From 4ee267ca7e7c475381cee226ead9b75b54762ea2 Mon Sep 17 00:00:00 2001 From: hunshcn Date: Tue, 2 Jan 2024 13:45:40 +0800 Subject: [PATCH] fix: add backgroundRead for plain http inbound (#952) https://github.com/golang/go/blob/go1.21.5/src/net/http/server.go#L682 --- listener/http/proxy.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/listener/http/proxy.go b/listener/http/proxy.go index 45f53d6be..4822eabce 100644 --- a/listener/http/proxy.go +++ b/listener/http/proxy.go @@ -1,10 +1,14 @@ package http import ( + "context" "fmt" + "io" "net" "net/http" "strings" + "sync" + _ "unsafe" "github.com/metacubex/mihomo/adapter/inbound" "github.com/metacubex/mihomo/common/lru" @@ -14,9 +18,18 @@ import ( "github.com/metacubex/mihomo/log" ) +//go:linkname registerOnHitEOF net/http.registerOnHitEOF +func registerOnHitEOF(rc io.ReadCloser, fn func()) + +//go:linkname requestBodyRemains net/http.requestBodyRemains +func requestBodyRemains(rc io.ReadCloser) bool + func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], additions ...inbound.Addition) { client := newClient(c, tunnel, additions...) defer client.CloseIdleConnections() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + peekMutex := sync.Mutex{} conn := N.NewBufferedConn(c) @@ -24,7 +37,9 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], trusted := cache == nil // disable authenticate if lru is nil for keepAlive { + peekMutex.Lock() request, err := ReadRequest(conn.Reader()) + peekMutex.Unlock() if err != nil { break } @@ -72,6 +87,23 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], if request.URL.Scheme == "" || request.URL.Host == "" { resp = responseWith(request, http.StatusBadRequest) } else { + request = request.WithContext(ctx) + + startBackgroundRead := func() { + go func() { + peekMutex.Lock() + defer peekMutex.Unlock() + _, err := conn.Peek(1) + if err != nil { + cancel() + } + }() + } + if requestBodyRemains(request.Body) { + registerOnHitEOF(request.Body, startBackgroundRead) + } else { + startBackgroundRead() + } resp, err = client.Do(request) if err != nil { resp = responseWith(request, http.StatusBadGateway)