注入代理授权并将请求传输到远程代理的端口转发器
来源:stackoverflow
时间:2024-04-14 10:24:36 347浏览 收藏
大家好,今天本人给大家带来文章《注入代理授权并将请求传输到远程代理的端口转发器》,文中内容主要涉及到,如果你对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:<nil> GetBody:<nil> ContentLength:0 TransferEncoding:[] Close:false Host:google.com:443 Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr:127.0.0.1:54126 RequestURI:google.com:443 TLS:<nil> Cancel:<nil> Response:<nil> 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:<nil> GetBody:<nil> ContentLength:0 TransferEncoding:[] Close:false Host:http://51.178.xx.xx:3128 Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr:127.0.0.1:54126 RequestURI:google.com:443 TLS:<nil> Cancel:<nil> Response:<nil> 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, "<html><body><h1>Hello, %s!</h1></body></html>", 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知识!
-
502 收藏
-
502 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
139 收藏
-
204 收藏
-
325 收藏
-
478 收藏
-
486 收藏
-
439 收藏
-
357 收藏
-
352 收藏
-
101 收藏
-
440 收藏
-
212 收藏
-
143 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习