compatible with HTTP/1.0

This commit is contained in:
ginuerzh 2024-08-06 18:31:29 +08:00
parent 1a776dc759
commit 5e8a8a4b4d

View File

@ -24,6 +24,7 @@ import (
md "github.com/go-gost/core/metadata"
"github.com/go-gost/core/observer/stats"
ctxvalue "github.com/go-gost/x/ctx"
xio "github.com/go-gost/x/internal/io"
netpkg "github.com/go-gost/x/internal/net"
stats_util "github.com/go-gost/x/internal/util/stats"
traffic_wrapper "github.com/go-gost/x/limiter/traffic/wrapper"
@ -236,7 +237,7 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
}
if req.Method != http.MethodConnect {
return h.handleProxy(rw, cc, req, log)
return h.handleProxy(xio.NewReadWriteCloser(rw, rw, conn), cc, req, log)
}
resp.StatusCode = http.StatusOK
@ -261,25 +262,78 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
return nil
}
func (h *httpHandler) handleProxy(rw, cc io.ReadWriter, req *http.Request, log logger.Logger) (err error) {
func (h *httpHandler) handleProxy(rw io.ReadWriteCloser, cc io.ReadWriter, req *http.Request, log logger.Logger) (err error) {
roundTrip := func(req *http.Request) error {
if req == nil {
return nil
}
resp := &http.Response{
ProtoMajor: req.ProtoMajor,
ProtoMinor: req.ProtoMinor,
Header: http.Header{},
StatusCode: http.StatusServiceUnavailable,
}
// HTTP/1.0
if req.ProtoMajor == 1 && req.ProtoMinor == 0 {
if strings.ToLower(req.Header.Get("Connection")) == "keep-alive" {
req.Header.Del("Connection")
} else {
req.Header.Set("Connection", "close")
}
}
req.Header.Del("Proxy-Connection")
if err = req.Write(cc); err != nil {
log.Error(err)
resp.Write(rw)
return err
}
ch := make(chan error, 1)
go func() {
ch <- netpkg.CopyBuffer(rw, cc, 32*1024)
res, err := http.ReadResponse(bufio.NewReader(cc), req)
if err != nil {
h.options.Logger.Errorf("read response: %v", err)
resp.Write(rw)
return
}
if log.IsLevelEnabled(logger.TraceLevel) {
dump, _ := httputil.DumpResponse(res, false)
log.Trace(string(dump))
}
if res.Close {
defer rw.Close()
}
// HTTP/1.0
if req.ProtoMajor == 1 && req.ProtoMinor == 0 {
if !res.Close {
res.Header.Set("Connection", "keep-alive")
}
res.ProtoMajor = req.ProtoMajor
res.ProtoMinor = req.ProtoMinor
}
if err = res.Write(rw); err != nil {
rw.Close()
log.Errorf("write response: %v", err)
}
}()
return nil
}
if err = roundTrip(req); err != nil {
return err
}
for {
err := func() error {
req, err := http.ReadRequest(bufio.NewReader(rw))
if err != nil {
if err == io.EOF {
if errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed) {
return nil
}
return err
@ -290,21 +344,10 @@ func (h *httpHandler) handleProxy(rw, cc io.ReadWriter, req *http.Request, log l
log.Trace(string(dump))
}
req.Header.Del("Proxy-Connection")
if err = req.Write(cc); err != nil {
if err = roundTrip(req); err != nil {
return err
}
return nil
}()
ch <- err
if err != nil {
break
}
}
return <-ch
}
func (h *httpHandler) decodeServerName(s string) (string, error) {