如何使用 GO 实现一个简单的 HTTP(S) PROXY
source link: https://www.v2ex.com/t/825720
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
想实现一个简单的 proxy,仅作为玩具使用, 当然其实现成的 lib 有很多, 但是目标很明确,学习一下基本原理
- 是否有其他的代码参考
有的, 有一个 python 版本的, 在 github 上发现的 python proxy code
- 是否有 GO 代码
package main
import (
"errors"
"fmt"
"github.com/valyala/fasthttp"
"io"
"log"
"net"
"sync"
)
func main() {
if err := fasthttp.ListenAndServe(":1234", requestHandler); err != nil {
log.Fatalf("Error in ListenAndServe: %s", err)
}
}
func processSocket(conn1, conn2 net.Conn, wg *sync.WaitGroup, s string) {
defer func() {
fmt.Println("END", s)
wg.Done()
}()
fmt.Println(s)
var buf []byte
buf = make([]byte, 4096)
i, err := conn1.Read(buf)
buf = buf[:i]
if err != nil {
return
}
for {
fmt.Println(s, string(buf))
conn2.Write(buf)
buf = buf[:]
buf = make([]byte, 2<<10<<10) // 10m
i, err := conn1.Read(buf)
buf = buf[:i]
if err != nil {
if len(buf) > 0 {
conn2.Write(buf)
}
fmt.Println(s, err)
if errors.Is(err, io.EOF) {
break
}
}
}
}
func haddleHTTPS(ctx *fasthttp.RequestCtx) {
h := string(ctx.Request.Host()) // host:port eg. www.baidu.com:443
curConn := ctx.Conn()
curConn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 6\r\n\r\nfoo"))
fmt.Println("haddle:", h)
remotConn, err := net.Dial("tcp", h)
if err != nil {
}
var wg sync.WaitGroup
wg.Add(2)
go processSocket(curConn, remotConn, &wg, "from local to remote")
go processSocket(remotConn, curConn, &wg, "from remote to local")
wg.Wait()
}
func haddleHTTP(ctx *fasthttp.RequestCtx) {
req := fasthttp.AcquireRequest()
req.SetRequestURIBytes(ctx.Request.RequestURI())
//req.Header.SetMethodBytes(ctx.Method())
req.Header = ctx.Request.Header
req.SetBody(ctx.Request.Body())
client := &fasthttp.Client{}
resp := fasthttp.AcquireResponse()
client.Do(req, resp)
body := resp.Body()
fmt.Println(body)
ctx.Write(body)
}
func requestHandler(ctx *fasthttp.RequestCtx) {
method := string(ctx.Method())
if method == "CONNECT" {
// https
haddleHTTPS(ctx)
return
}
// http
haddleHTTP(ctx)
return
}
- 代码是否可以正常工作
不行, http 是没有问题的, https 存在问题
- 问题日志 /描述
➜ ~ curl https://www.baidu.com -vvv
* Uses proxy env variable https_proxy == 'http://127.0.0.1:1234'
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 1234 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to www.baidu.com:443
> CONNECT www.baidu.com:443 HTTP/1.1
> Host: www.baidu.com:443
> User-Agent: curl/7.64.1
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 OK
< Content-Length: 6
* Ignoring Content-Length in CONNECT 200 response
<
* Proxy replied 200 to CONNECT request
* CONNECT phase completed!
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* CONNECT phase completed!
* CONNECT phase completed!
* error:1400410B:SSL routines:CONNECT_CR_SRVR_HELLO:wrong version number
* Closing connection 0
curl: (35) error:1400410B:SSL routines:CONNECT_CR_SRVR_HELLO:wrong version number
- 是否自行 debug
有的, 发现 local to server 的时候, read 出现了 error, err: ECONNRESET (54) 然后 接下来就 EOF 了, 所以就退出了, 但是拿到这个 error 的时候, curl 就已经结束了, 所以拿到 EOF 也是正常行为, 主要在于不知道为啥会 curl 会断掉
- 想知道为什么 curl 会断掉
- 有没有什么解决办法[在上述代码中修改]
- 或者有其他代码编写方式
第 1 条附言 · 1 天前
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK