英文:
How to get an HTTP Proxy response Header in Golang built-in HTTP client?
问题
我正在使用默认的"net/http" Golang库通过HTTP代理进行简单的HTTP GET请求,并希望读取第一个代理回复的内容(对于使用CONNECT方法的HTTP客户端请求)。在纯文本中,它看起来像这样:
HTTP/1.1 200 OK
Request-Uid: <some id>
<another header>: <another value>
Golang代码:
...
proxyUrlParsed, errUrl := url.Parse(proxyUrl)
tr := &http.Transport{
Proxy: http.ProxyURL(proxyUrlParsed),
}
client := &http.Client{
Transport: tr,
}
request, errReq := http.NewRequest("GET", targetUrl, nil)
response, errDo := client.Do(request)
// Response contains HTTP headers from the reply from the target resource but not the intermediate proxy.
我部分解决了这个问题,使用了DialContext,但我需要实现一些我发现不太方便且成本较高的协议部分以供以后支持。所以有没有一种简单而巧妙的方法来解决这个问题?
英文:
I'm doing a simple HTTP GET request with the default "net/http" Golang lib via HTTP proxy and want to read the content of the first proxy reply(for the HTTP client req with the CONNECT method). In the plain text, it looks like
HTTP/1.1 200 OK
Request-Uid: <some id>
<another header>: <another value>
Golang code:
...
proxyUrlParsed, errUrl := url.Parse(proxyUrl)
tr := &http.Transport{
Proxy: http.ProxyURL(proxyUrlParsed),
}
client := &http.Client{
Transport: tr,
}
request, errReq := http.NewRequest("GET", targetUrl, nil)
response, errDo := client.Do(request)
// Response contains HTTP headers from the reply from the target resource but not the intermediate proxy.
I partially solved it with DialContext, but I were needed to impl some parts of the protocol that I found not so handy and costly for later support. So is there an easy and clever way to do it?
答案1
得分: 1
隧道代理
以使用curl客户端为例。在请求https时,使用CONNECT
方法连接到隧道连接。获取的流内容是TLS加密内容,代理无法解密。
如果有TLS证书,可以尝试解析响应流。
当wireshark
捕获https请求时,需要在浏览器中配置一个参数。证书将保存在指定的文件中。
http_proxy=127.0.0.1:8021 https_proxy=127.0.0.1:8021 curl -v https://qq.com
http_proxy=127.0.0.1:8021 https_proxy=127.0.0.1:8021 curl -v https://qq.com
package main
import (
"fmt"
"io"
"log"
"net"
"net/http"
"net/http/httputil"
"os"
)
func main() {
proxy := func(w http.ResponseWriter, req *http.Request) {
log.Println("proxy", req.Method, req.RequestURI)
if req.URL.Host != "" {
if req.Method == http.MethodConnect {
// 隧道
conn, err := net.Dial("tcp", req.URL.Host)
if err != nil {
w.WriteHeader(502)
fmt.Fprint(w, err)
return
}
client, _, err := w.(http.Hijacker).Hijack()
if err != nil {
w.WriteHeader(502)
fmt.Fprint(w, err)
conn.Close()
return
}
client.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
hr, hw := io.Pipe()
go func() {
io.Copy(os.Stdout, hr)
hr.Close()
}()
go func() {
// 将响应打印到标准输出
io.Copy(io.MultiWriter(client, hw), conn)
client.Close()
conn.Close()
hw.Close()
}()
go func() {
io.Copy(conn, client)
client.Close()
conn.Close()
}()
return
}
httputil.NewSingleHostReverseProxy(req.URL).ServeHTTP(w, req)
}
}
http.ListenAndServe(":8021", http.HandlerFunc(proxy))
}
反向代理
使用/net/http/httputil.ReverseProxy
代理请求,设置ModifyResponse
字段作为响应钩子。
package main
import (
"log"
"net/http"
"net/http/httputil"
"net/url"
)
func main() {
proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: "http", Host: "127.0.0.1:8020"})
proxy.ModifyResponse = func(w *http.Response) error {
w.Header.Add("Author", "eudore")
log.Println(w.Request.Method, w.Request.RequestURI, w.Status)
return nil
}
http.ListenAndServe(":8021", proxy)
}
curl请求:
[root@node1 ~]# curl -I 127.0.0.1:8020?222
HTTP/1.1 401 Unauthorized
Www-Authenticate: Basic
Date: Thu, 17 Nov 2022 01:34:06 GMT
[root@node1 ~]# curl -I 127.0.0.1:8021?222
HTTP/1.1 401 Unauthorized
Author: eudore
Date: Thu, 17 Nov 2022 01:34:07 GMT
Www-Authenticate: Basic
英文:
tunnel proxy
Take the use of the curl client as an example. When requesting https, use the CONNECT
method to connect to the tunnel connection. The obtained stream content is TLS encrypted content, which cannot be decrypted by the proxy.
If have a tls certificate, can try to parse the response stream.
When wireshark
captures https requests, a parameter needs to be configured in the browser. The certificate is saved in the specified file
http_proxy=127.0.0.1:8021 https_proxy=127.0.0.1:8021 curl -v https://qq.com
http_proxy=127.0.0.1:8021 https_proxy=127.0.0.1:8021 curl -v https://qq.com
package main
import (
"fmt"
"io"
"log"
"net"
"net/http"
"net/http/httputil"
"os"
)
func main() {
proxy := func(w http.ResponseWriter, req *http.Request) {
log.Println("proxy", req.Method, req.RequestURI)
if req.URL.Host != "" {
if req.Method == http.MethodConnect {
// tunnel
conn, err := net.Dial("tcp", req.URL.Host)
if err != nil {
w.WriteHeader(502)
fmt.Fprint(w, err)
return
}
client, _, err := w.(http.Hijacker).Hijack()
if err != nil {
w.WriteHeader(502)
fmt.Fprint(w, err)
conn.Close()
return
}
client.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
hr, hw := io.Pipe()
go func(){
io.Copy(os.Stdout, hr)
hr.Close()
}()
go func() {
// print response to stdout
io.Copy(io.MultiWriter(client, hw), conn)
client.Close()
conn.Close()
hw.Close()
}()
go func() {
io.Copy(conn, client)
client.Close()
conn.Close()
}()
return
}
httputil.NewSingleHostReverseProxy(req.URL).ServeHTTP(w, req)
}
}
http.ListenAndServe(":8021", http.HandlerFunc(proxy))
}
reverse proxy
use /net/http/httputil.ReverseProxy
proxy a request ,set ModifyResponse
field is response hook.
package main
import (
"log"
"net/http"
"net/http/httputil"
"net/url"
)
func main() {
proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: "http", Host: "127.0.0.1:8020"})
proxy.ModifyResponse = func(w *http.Response) error {
w.Header.Add("Author", "eudore")
log.Println(w.Request.Method, w.Request.RequestURI, w.Status)
return nil
}
http.ListenAndServe(":8021", proxy)
}
curl request:
[root@node1 ~]# curl -I 127.0.0.1:8020?222
HTTP/1.1 401 Unauthorized
Www-Authenticate: Basic
Date: Thu, 17 Nov 2022 01:34:06 GMT
[root@node1 ~]# curl -I 127.0.0.1:8021?222
HTTP/1.1 401 Unauthorized
Author: eudore
Date: Thu, 17 Nov 2022 01:34:07 GMT
Www-Authenticate: Basic
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论