参考资料: https://zh.mojotv.cn/tutorial/golang-interface-reader-writer
资料里面的代码在代理http协议时所有的请求行中资源路径都是绝对路径的问题, 部分服务器无法识别.
本文对此进行修复和其它的一些优化
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
"net/url"
"strconv"
"strings"
)
func main() {
proxyAddress := ":8989"
log.SetFlags(log.LstdFlags | log.Lshortfile)
log.Println("代理地址: " + proxyAddress)
listener, err := net.Listen("tcp", proxyAddress)
if err != nil {
log.Panic(err)
}
for {
client, err := listener.Accept()
if err != nil {
log.Panic(err)
}
go handleClientRequest(client)
}
}
func handleClientRequest(client net.Conn) {
if client == nil {
return
}
defer client.Close()
clientReader := bufio.NewReader(client)
method, requestAddress, protocol, headers, headerLines, err := decodeHeader(clientReader)
if err != nil {
log.Println(err)
return
}
nFirstLine := method + " " + requestAddress + " " + protocol
var serverAddress, oldHost string
if method == "CONNECT" {
serverAddress = requestAddress
} else {
hostPortURL, err := url.Parse(requestAddress)
if err != nil {
log.Println("url解析错误: " + nFirstLine)
log.Println(err)
return
}
oldHost = hostPortURL.Host
if !strings.Contains(oldHost, ":") {
serverAddress = oldHost + ":80"
} else {
serverAddress = oldHost
}
}
log.Println(nFirstLine + " " + serverAddress + "\n")
server, err := net.Dial("tcp", serverAddress)
if err != nil {
log.Println(err)
return
}
defer server.Close()
if method == "CONNECT" {
fmt.Fprint(client, "HTTP/1.1 200 Connection established\r\n\r\n")
go io.Copy(server, clientReader)
} else {
needDecodeHeader := false
go func() {
for {
if needDecodeHeader {
method, requestAddress, protocol, headers, headerLines, err = decodeHeader(clientReader)
if err != nil {
log.Println(err)
return
}
} else {
needDecodeHeader = true
}
requestPath := append(strings.Split(requestAddress, oldHost), "/")[1]
server.Write([]byte(method + " " + requestPath + " " + protocol + "\r\n"))
for _, line := range headerLines {
server.Write([]byte(line))
}
server.Write([]byte("\r\n"))
length64, err := strconv.ParseInt(headers["content-length"], 10, 64)
if err == nil {
if length64 == -1 {
io.Copy(server, clientReader)
return
}
limitedReader := io.LimitReader(clientReader, length64)
io.Copy(server, limitedReader)
limitedReader = io.LimitReader(clientReader, 2)
io.Copy(server, limitedReader)
}
}
}()
}
io.Copy(client, server)
}
func decodeHeader(render *bufio.Reader) (string, string, string, map[string]string, []string, error) {
var method, requestAddress, protocol string
var headers = map[string]string{}
var headerLines = []string{}
lineData, err := render.ReadBytes('\n')
if err != nil {
return method, requestAddress, protocol, headers, headerLines, err
}
line := string(lineData)
fmt.Sscanf(line, "%s%s%s", &method, &requestAddress, &protocol)
if line != method+" "+requestAddress+" "+protocol+"\r\n" {
log.Println("解析错误: " + line)
}
for {
lineData, err := render.ReadBytes('\n')
if err != nil {
return method, requestAddress, protocol, headers, headerLines, err
}
if len(lineData) == 2 {
break
}
line := string(lineData)
index := strings.Index(line, ":")
keyLower := strings.ToLower(strings.Trim(line[:index], "\r\n "))
value := line[index+1:]
if strings.HasPrefix(keyLower, "proxy-") {
log.Println(line)
}
headers[keyLower] = strings.Trim(value, "\r\n ")
if keyLower == "proxy-connection" {
headerLines = append(headerLines, "Connection:"+value)
} else {
headerLines = append(headerLines, line)
}
}
return method, requestAddress, protocol, headers, headerLines, err
}
优化前后请求体对比:
image.png
编译好的产物:
https://download.csdn.net/download/qq_37873556/88739142
和文档中的代码有稍微差异, 添加了超时机制等优化
提供如下系统产物
go env -w GOOS=linux GOARCH=amd64
go install
go env -w GOOS=darwin GOARCH=amd64
go install
go env -w GOOS=darwin GOARCH=arm64
go install
go env -w GOOS=linux GOARCH=arm GOARM=5
go install
go env -w GOOS=windows GOARCH=amd64
go install
网友评论