package http import ( "bufio" "context" "encoding/base64" "io" "net" "net/textproto" "net/url" "proxy-server/server/fwd/core" "strings" "github.com/pkg/errors" ) type Request struct { conn net.Conn reader *bufio.Reader method string uri string proto string headers *textproto.MIMEHeader auth *core.AuthContext dest *core.FwdAddr } func Process(ctx context.Context, conn net.Conn) (*core.Conn, error) { reader := bufio.NewReader(conn) textReader := textproto.NewReader(reader) // 首行 line, err := textReader.ReadLine() if err != nil { return nil, err } parts := strings.Fields(line) if len(parts) != 3 { return nil, errors.New("无效的 http 请求") } // 请求头 headers, err := textReader.ReadMIMEHeader() if err != nil { return nil, errors.Wrap(err, "解析请求头失败") } // 验证账号 authInfo := headers.Get("Proxy-Authorization") var auth *core.AuthContext if authInfo == "" { auth, err = core.CheckIp(conn) if err != nil { return nil, errors.Wrap(err, "验证账号失败") } } else { authParts := strings.Split(authInfo, " ") if len(authParts) != 2 { return nil, errors.New("无效的 Proxy-Authorization") } if authParts[0] != "Basic" { return nil, errors.New("不支持的认证方式") } authBytes, err := base64.URLEncoding.DecodeString(authParts[1]) if err != nil { return nil, errors.Wrap(err, "解码认证信息失败") } authPair := strings.Split(string(authBytes), ":") auth, err = core.CheckPass(conn, authPair[0], authPair[1]) } // 获取 Host host := headers.Get("Host") if host == "" { return nil, errors.New("无效的 Host") } if !strings.Contains(host, ":") { host += ":80" } addr, err := net.ResolveTCPAddr("tcp", host) if err != nil { return nil, errors.Wrap(err, "解析 Host 失败") } request := &Request{ conn: conn, reader: reader, method: parts[0], uri: parts[1], proto: parts[2], headers: &headers, dest: &core.FwdAddr{ IP: addr.IP, Port: addr.Port, Domain: host, }, auth: auth, } var user *core.Conn if parts[0] == "CONNECT" { user, err = processHttps(ctx, request) if err != nil { return nil, err } } else { user, err = processHttp(ctx, request) if err != nil { return nil, err } } return user, nil } func processHttps(ctx context.Context, req *Request) (*core.Conn, error) { // 响应 CONNECT _, err := req.conn.Write([]byte("HTTP/1.1 200 Connection Established\r\n\r\n")) if err != nil { return nil, errors.Wrap(err, "响应 CONNECT 失败") } return &core.Conn{ Conn: req.conn, Reader: req.reader, Tag: req.conn.RemoteAddr().String() + "_" + req.conn.LocalAddr().String(), Protocol: "http", Dest: req.dest, Auth: req.auth, }, nil } func processHttp(ctx context.Context, req *Request) (*core.Conn, error) { // 修改请求头 rawUrl, err := url.Parse(req.uri) if err != nil { return nil, errors.Wrap(err, "解析请求地址失败") } rawUrl.Scheme = "" rawUrl.Host = "" req.uri = rawUrl.String() req.headers.Del("Proxy-Authorization") // 构造请求 sb := strings.Builder{} sb.WriteString(req.method) sb.WriteString(" ") sb.WriteString(req.uri) sb.WriteString(" ") sb.WriteString(req.proto) sb.WriteString("\r\n") for k, v := range *req.headers { sb.WriteString(k) sb.WriteString(": ") sb.WriteString(strings.Join(v, ",")) sb.WriteString("\r\n") } sb.WriteString("\r\n") mReader := io.MultiReader(strings.NewReader(sb.String()), req.conn) newReader := bufio.NewReader(mReader) return &core.Conn{ Conn: req.conn, Reader: newReader, Tag: req.conn.RemoteAddr().String() + "_" + req.conn.LocalAddr().String(), Protocol: "http", Dest: req.dest, Auth: req.auth, }, nil }