Golang中如何让http.Server处理来自连接的请求

Golang中如何让http.Server处理来自连接的请求 我当前的目标:编写一个能够解密HTTPS连接的代理服务器。

问题如下:在http.Handler中劫持连接并完成TLS握手后,我获得了一个包含HTTP流量的解密连接,我希望使用http.Server来处理这些流量,这样我就不需要自己编写HTTP请求处理循环。

如何以最正确且无需额外内存分配的方式实现这一点?

在fasthttp库中,有一个很好的fasthttp.ServeConn方法,我能在net.http中实现类似的功能吗?

4 回复

我也不理解在你的示例中发生了什么,所以我们两个都有同样的困惑。当你处于处理函数中时,TLS握手和HTTP请求已经完成,你看到的请求是明文的。此时通过劫持来启动另一个握手对我来说没有意义。这不是代理服务器的做法。

// 代码示例保留原样
func main() {
    fmt.Println("hello world")
}

更多关于Golang中如何让http.Server处理来自连接的请求的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


听起来你正在构建一个HTTPS代理。net/http/httputil.ReverseProxy类型很适合这个场景,它允许你在传输过程中检查和修改请求和响应。相比于使用劫持等技术,这个方案是否更适合你的需求?

另外,如果你想通过常规的http.Handler来处理传入请求,为什么不直接使用标准的监听http.Server呢?

func main() {
    fmt.Println("hello world")
}

我不太理解在中间人攻击的情况下,httputil.ReverseProxy 如何帮助我。

通过一个示例来说明(代码注释中的问题):

	http.HandleFunc("/hijack", func(w http.ResponseWriter, r *http.Request) {
		hj, ok := w.(http.Hijacker)
		if !ok {
			http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError)
			return
		}
		conn, bufrw, err := hj.Hijack()
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		// 不要忘记关闭连接:
		defer conn.Close()

		tls_conn := tls.Server(conn, tlsConfig)
		if err := tls_conn.Handshake(); err != nil {
			return nil, fmt.Errorf("Handshake error: %s", err)
		}
		
		// 待办:如何使用 http.Server 处理来自 tls_conn 的 http 流?
	})

在Go标准库的net/http包中,虽然没有像fasthttp那样直接的ServeConn方法,但你可以通过劫持连接并使用http.Server的底层处理机制来实现类似功能。以下是实现方案:

核心解决方案

package main

import (
    "bufio"
    "context"
    "crypto/tls"
    "fmt"
    "net"
    "net/http"
    "time"
)

type connWrapper struct {
    net.Conn
}

func (cw *connWrapper) Close() error {
    return cw.Conn.Close()
}

// 处理解密后的TLS连接
func handleDecryptedConnection(conn net.Conn, server *http.Server) error {
    // 创建连接包装器
    wrappedConn := &connWrapper{Conn: conn}
    
    // 使用Server的底层连接处理机制
    return server.Serve(net.Listener(&singleConnListener{conn: wrappedConn}))
}

// 单连接监听器实现
type singleConnListener struct {
    conn net.Conn
    done bool
}

func (l *singleConnListener) Accept() (net.Conn, error) {
    if l.done {
        return nil, fmt.Errorf("listener closed")
    }
    l.done = true
    return l.conn, nil
}

func (l *singleConnListener) Close() error {
    return l.conn.Close()
}

func (l *singleConnListener) Addr() net.Addr {
    return l.conn.LocalAddr()
}

// 代理服务器处理函数
func proxyHandler(server *http.Server) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        // 劫持连接
        hijacker, ok := w.(http.Hijacker)
        if !ok {
            http.Error(w, "Hijacking not supported", http.StatusInternalServerError)
            return
        }
        
        clientConn, _, err := hijacker.Hijack()
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        
        // 发送连接建立响应
        fmt.Fprintf(clientConn, "HTTP/1.1 200 Connection established\r\n\r\n")
        
        // 执行TLS握手(这里需要你实际的TLS配置)
        tlsConfig := &tls.Config{
            // 你的TLS配置
        }
        
        tlsConn := tls.Server(clientConn, tlsConfig)
        if err := tlsConn.Handshake(); err != nil {
            tlsConn.Close()
            return
        }
        
        // 处理解密后的连接
        go handleDecryptedConnection(tlsConn, server)
    }
}

完整示例

func main() {
    // 创建HTTP服务器
    server := &http.Server{
        Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            fmt.Fprintf(w, "处理请求: %s %s\n", r.Method, r.URL.Path)
        }),
        ReadTimeout:  10 * time.Second,
        WriteTimeout: 10 * time.Second,
    }
    
    // 代理服务器
    proxyServer := &http.Server{
        Addr: ":8080",
        Handler: proxyHandler(server),
    }
    
    fmt.Println("代理服务器运行在 :8080")
    if err := proxyServer.ListenAndServe(); err != nil {
        fmt.Printf("服务器错误: %v\n", err)
    }
}

更高效的实现方式

如果你需要更低的延迟和内存分配,可以直接使用bufio.Reader和HTTP请求解析:

func handleHTTPConnection(conn net.Conn, handler http.Handler) {
    defer conn.Close()
    
    reader := bufio.NewReader(conn)
    writer := bufio.NewWriter(conn)
    
    for {
        // 读取HTTP请求
        req, err := http.ReadRequest(reader)
        if err != nil {
            break
        }
        
        // 创建响应写入器
        rw := &responseWriter{
            conn:   conn,
            writer: writer,
            header: make(http.Header),
        }
        
        // 处理请求
        handler.ServeHTTP(rw, req)
        
        // 刷新缓冲区
        if err := writer.Flush(); err != nil {
            break
        }
        
        // 如果不是keep-alive,则退出循环
        if !isKeepAlive(req) {
            break
        }
    }
}

// 自定义响应写入器
type responseWriter struct {
    conn   net.Conn
    writer *bufio.Writer
    header http.Header
    status int
}

func (rw *responseWriter) Header() http.Header {
    return rw.header
}

func (rw *responseWriter) Write(data []byte) (int, error) {
    return rw.writer.Write(data)
}

func (rw *responseWriter) WriteHeader(statusCode int) {
    rw.status = statusCode
}

func isKeepAlive(req *http.Request) bool {
    return req.Close == false
}

关键要点

  1. 使用singleConnListener:通过实现net.Listener接口来包装单个连接,让http.Server能够处理该连接。

  2. 避免额外内存分配:重用连接和缓冲区,减少GC压力。

  3. 正确处理连接生命周期:确保连接在适当的时候关闭,避免资源泄漏。

  4. 性能优化:对于高并发场景,可以考虑使用连接池和对象重用模式。

这种方法让你能够利用标准库的HTTP处理能力,同时保持对底层连接的控制,实现高效的代理服务器功能。

回到顶部