当前位置:首页 > 文章列表 > Golang > Go问答 > 注入代理授权并将请求传输到远程代理的端口转发器

注入代理授权并将请求传输到远程代理的端口转发器

来源:stackoverflow 2024-04-14 10:24:36 0浏览 收藏

大家好,今天本人给大家带来文章《注入代理授权并将请求传输到远程代理的端口转发器》,文中内容主要涉及到,如果你对Golang方面的知识点感兴趣,那就请各位朋友继续看下去吧~希望能真正帮到你们,谢谢!

问题内容

我实际上正在使用 go 构建一个基于 selenium 的自动化工具,称为 igopher,并且我想实现本机代理支持。

但是,我遇到了身份验证方面的问题...

我无法将代理凭据发送到 chrome,如果没有它们,它会通过一个警报框要求进行身份验证,而我几乎无法通过 selenium 进行交互(我什至不确定在无头模式下是否可行)。

所以我想到了一个由我的程序本地托管的中间代理系统,它将添加代理授权标头并将请求传输到远程代理:

我发现这个 nodejs 程序非常有用,我想在 go 中重现它。

主要部分如下:

function createportforwarder(local_host, local_port, remote_host, remote_port, buf_proxy_basic_auth, is_remote_https, ignore_https_cert) {
  net.createserver({allowhalfopen: true}, function (socket) {
    var realcon = (is_remote_https ? tls : net).connect({
      port: remote_port, host: remote_host, allowhalfopen: true,
      rejectunauthorized: !ignore_https_cert /*not used when is_remote_https false*/
    });
    realcon.on('data', function (buf) {
      socket.write(buf);
      realcon.__havegotdata = true;
    }).on('end', function () {
      socket.end();
      if (!realcon.__havegotdata && !realcon.__haveshownerror) {
        console.error('[localproxy(:' + local_port + ')][connection to ' + remote_host + ':' + remote_port + '] error: ended by remote peer');
        realcon.__haveshownerror = true;
      }
    }).on('close', function () {
      socket.end();
      if (!realcon.__havegotdata && !realcon.__haveshownerror) {
        console.error('[localproxy(:' + local_port + ')][connection to ' + remote_host + ':' + remote_port + '] error: reset by remote peer');
        realcon.__haveshownerror = true;
      }
    }).on('error', function (err) {
      console.error('[localproxy(:' + local_port + ')][connection to ' + remote_host + ':' + remote_port + '] ' + err);
      realcon.__haveshownerror = true;
    });

    var parser = new httpparser(httpparser.request);
    parser[httpparser.konheaderscomplete] = function (versionmajor, versionminor, headers, method,
                                                      url, statuscode, statusmessage, upgrade,
                                                      shouldkeepalive) {
      parser.__is_headers_complete = true;
      parser.__upgrade = upgrade;
      parser.__method = method;
    };

    var state = state_none;
    socket.on('data', function (buf) {
      if (!parser) {
        realcon.write(buf);
        return
      }

      var buf_ary = [], unsavedstart = 0, buf_len = buf.length;

      for (var i = 0; i < buf_len; i++) {
        //find first lf
        if (state === state_none) {
          if (buf[i] === lf) {
            state = state_found_lf;
          }
          continue;
        }

        //find second cr lf or lf
        if (buf[i] === lf) {
          parser.__is_headers_complete = false;
          parser.execute(buf.slice(unsavedstart, i + 1));

          if (parser.__is_headers_complete) {
            buf_ary.push(buf.slice(unsavedstart, buf[i - 1] === cr ? i - 1 : i));
            //insert auth header
            buf_ary.push(buf_proxy_basic_auth);
            buf_ary.push(state === state_found_lf_cr ? buf_cr_lf_cr_lf : buf_lf_lf);

            // stop intercepting packets if encountered tls and websocket handshake
            if (parser.__method === 5 /*connect*/ || parser.__upgrade) {
              parser.close();
              parser = null;

              buf_ary.push(buf.slice(i + 1));
              realcon.write(buffer.concat(buf_ary));

              state = state_none;
              return;
            }

            unsavedstart = i + 1;
            state = state_none;
          }
          else {
            state = state_found_lf;
          }
        }
        else if (buf[i] === cr && state === state_found_lf) {
          state = state_found_lf_cr;
        } else {
          state = state_none;
        }
      }

      if (unsavedstart < buf_len) {
        buf = buf.slice(unsavedstart, buf_len);
        parser.execute(buf);
        buf_ary.push(buf);
      }

      realcon.write(buffer.concat(buf_ary));

    }).on('end', cleanup).on('close', cleanup).on('error', function (err) {
      if (!socket.__cleanup) {
        console.error('[localproxy(:' + local_port + ')][incoming connection] ' + err);
      }
    });

    function cleanup() {
      socket.__cleanup = true;
      if (parser) {
        parser.close();
        parser = null;
      }
      realcon.end();
    }
  }).on('error', function (err) {
    console.error('[localproxy(:' + local_port + ')] ' + err);
    process.exit(1);
  }).listen(local_port, local_host === '*' ? undefined : local_host, function () {
    console.log('[localproxy(:' + local_port + ')] ok: forward http://' + local_host + ':' + local_port + ' to http' + (is_remote_https ? 's' : '') + '://' + remote_host + ':' + remote_port);
  });
}

可以在 go 中重现吗?我找到了 tcp 转发器的要点,但我不知道是否可以用它编辑请求标头...

如果这是不可能的(这会让我感到惊讶),我仍然可以在我自己的程序中使用这个节点程序,但我真的更愿意避免使节点成为依赖项。

此外,将其本地包含在我的程序中将使与它的交互变得更容易,特别是能够停止或重新启动该服务器。

因此,如果有人有想法、建议或资源,会对我有很大帮助!我被这个问题困扰了很长时间......

提前致谢!

编辑:

我已经尝试使用 reverseproxy 但没有成功,修改后它无法将请求发送到远程代理,因为它是 connect 代理并且 url 的格式为 //ip:port例如://google.com:443

这是我的代码:

func printresponse(r *http.response) error {
    logrus.infof("response: %+v\n", r)
    return nil
}

// launchforwardingproxy launch forward server used to inject proxy authentication header
// into outgoing requests
func launchforwardingproxy(localport uint16, remoteproxy proxyconfig) error {
    localserverhost = fmt.sprintf("localhost:%d", localport)
    remoteserverhost = fmt.sprintf(
        "http://%s:%d",
        remoteproxy.ip,
        remoteproxy.port,
    )
    remoteserverauth = fmt.sprintf(
        "%s:%s",
        remoteproxy.username,
        remoteproxy.password,
    )

    remote, err := url.parse(remoteserverhost)
    if err != nil {
        panic(err)
    }

    proxy := httputil.newsinglehostreverseproxy(remote)
    d := func(req *http.request) {
        logrus.infof("pre-edited request: %+v\n", req)

        req.host = remoteserverhost

        // inject proxy authentication headers to outgoing request into new header
        basicauth := "basic " + base64.stdencoding.encodetostring([]byte(remoteserverauth))
        req.header.set("proxy-authorization", basicauth)

        logrus.infof("edited request: %+v\n", req)
        logrus.infof("scheme: %s, host: %s, port: %s\n", req.url.scheme, req.url.host, req.url.port())
    }
    proxy.director = d
    proxy.modifyresponse = printresponse
    http.listenandserve(localserverhost, proxy)

    return nil
}

curl https://google.com --proxy http://127.0.0.1:8880 输出:

INFO[0013] Pre-Edited request: &{Method:CONNECT URL://google.com:443 Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[Proxy-Connection:[Keep-Alive] User-Agent:[curl/7.68.0]] Body: GetBody: ContentLength:0 TransferEncoding:[] Close:false Host:google.com:443 Form:map[] PostForm:map[] MultipartForm: Trailer:map[] RemoteAddr:127.0.0.1:54126 RequestURI:google.com:443 TLS: Cancel: Response: ctx:0xc000674280}  function=func1 line=59

INFO[0013] Edited Request: &{Method:CONNECT URL://google.com:443 Proto:HTTP/1.1 ProtoMajor:1 ProtoMinor:1 Header:map[Proxy-Authorization:[Basic auth] Proxy-Connection:[Keep-Alive] User-Agent:[curl/7.68.0]] Body: GetBody: ContentLength:0 TransferEncoding:[] Close:false Host:http://51.178.xx.xx:3128 Form:map[] PostForm:map[] MultipartForm: Trailer:map[] RemoteAddr:127.0.0.1:54126 RequestURI:google.com:443 TLS: Cancel: Response: ctx:0xc000674280}  function=func1 line=67

INFO[0013] Scheme: , Host: google.com:443, Port: 443     function=func1 line=68

2021/03/16 21:53:51 http: proxy error: unsupported protocol scheme ""

解决方案


看看这段代码,它演示了您之前提出的方案

package main

import (
    "encoding/base64"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "net/http/httputil"
    "net/url"
    "time"

    auth "github.com/abbot/go-http-auth"
)

func Secret(user, realm string) string {
    if user == "john" {
        // password is "hello"
        return "$1$dlPL2MqE$oQmn16q49SqdmhenQuNgs1"
    }
    return ""
}

func serveWithAuth() {
    authenticator := auth.NewBasicAuthenticator("localhost", Secret)
    http.HandleFunc("/", authenticator.Wrap(func(w http.ResponseWriter, r *auth.AuthenticatedRequest) {
        fmt.Fprintf(w, "

Hello, %s!

", r.Username) })) http.ListenAndServe(":8080", nil) } func serveNoAuth(backURL string) { rpURL, err := url.Parse(backURL) if err != nil { log.Fatal(err) } p := NewSingleHostReverseProxy(rpURL) srv := &http.Server{Handler: p, Addr: ":9090"} srv.ListenAndServe() } func NewSingleHostReverseProxy(target *url.URL) *httputil.ReverseProxy { rp := httputil.NewSingleHostReverseProxy(target) director := rp.Director rp.Director = func(req *http.Request) { director(req) if target.User != nil { b := base64.StdEncoding.EncodeToString([]byte(target.User.String())) req.Header.Set("Authorization", fmt.Sprintf("Basic %v", string(b))) } } return rp } func main() { go serveWithAuth() go serveNoAuth("http://john:hello@localhost:8080/") <-time.After(time.Second) resp, err := http.Get("http://localhost:9090/") if err != nil { log.Fatal(err) } b, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Printf("%s", b) }

好了,本文到此结束,带大家了解了《注入代理授权并将请求传输到远程代理的端口转发器》,希望本文对你有所帮助!关注golang学习网公众号,给大家分享更多Golang知识!

版本声明
本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
Win11怎么不显示聚焦图片? 通过注册表设置是否显示聚焦图片的方法Win11怎么不显示聚焦图片? 通过注册表设置是否显示聚焦图片的方法
上一篇
Win11怎么不显示聚焦图片? 通过注册表设置是否显示聚焦图片的方法
人工智能与云:虚拟世界中的绝配!
下一篇
人工智能与云:虚拟世界中的绝配!
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    542次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    508次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    497次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    484次学习
查看更多
AI推荐
  • 笔灵AI生成答辩PPT:高效制作学术与职场PPT的利器
    笔灵AI生成答辩PPT
    探索笔灵AI生成答辩PPT的强大功能,快速制作高质量答辩PPT。精准内容提取、多样模板匹配、数据可视化、配套自述稿生成,让您的学术和职场展示更加专业与高效。
    24次使用
  • 知网AIGC检测服务系统:精准识别学术文本中的AI生成内容
    知网AIGC检测服务系统
    知网AIGC检测服务系统,专注于检测学术文本中的疑似AI生成内容。依托知网海量高质量文献资源,结合先进的“知识增强AIGC检测技术”,系统能够从语言模式和语义逻辑两方面精准识别AI生成内容,适用于学术研究、教育和企业领域,确保文本的真实性和原创性。
    38次使用
  • AIGC检测服务:AIbiye助力确保论文原创性
    AIGC检测-Aibiye
    AIbiye官网推出的AIGC检测服务,专注于检测ChatGPT、Gemini、Claude等AIGC工具生成的文本,帮助用户确保论文的原创性和学术规范。支持txt和doc(x)格式,检测范围为论文正文,提供高准确性和便捷的用户体验。
    38次使用
  • 易笔AI论文平台:快速生成高质量学术论文的利器
    易笔AI论文
    易笔AI论文平台提供自动写作、格式校对、查重检测等功能,支持多种学术领域的论文生成。价格优惠,界面友好,操作简便,适用于学术研究者、学生及论文辅导机构。
    50次使用
  • 笔启AI论文写作平台:多类型论文生成与多语言支持
    笔启AI论文写作平台
    笔启AI论文写作平台提供多类型论文生成服务,支持多语言写作,满足学术研究者、学生和职场人士的需求。平台采用AI 4.0版本,确保论文质量和原创性,并提供查重保障和隐私保护。
    41次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码