From c282e69ffdc65e41353336025bfc3bc85430433a Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Mon, 14 Mar 2022 20:20:48 +0800 Subject: [PATCH] move http2 to github.com/go-gost/x --- cmd/gost/register.go | 114 +++++----- go.mod | 2 +- pkg/connector/http2/conn.go | 54 ----- pkg/connector/http2/connector.go | 121 ----------- pkg/connector/http2/metadata.go | 31 --- pkg/dialer/http2/dialer.go | 101 --------- pkg/dialer/http2/h2/conn.go | 54 ----- pkg/dialer/http2/h2/dialer.go | 165 -------------- pkg/dialer/http2/h2/metadata.go | 22 -- pkg/dialer/http2/metadata.go | 12 -- pkg/handler/http2/conn.go | 46 ---- pkg/handler/http2/handler.go | 343 ------------------------------ pkg/handler/http2/metadata.go | 47 ---- pkg/listener/http2/conn.go | 54 ----- pkg/listener/http2/h2/conn.go | 89 -------- pkg/listener/http2/h2/listener.go | 178 ---------------- pkg/listener/http2/h2/metadata.go | 29 --- pkg/listener/http2/listener.go | 120 ----------- pkg/listener/http2/metadata.go | 25 --- 19 files changed, 58 insertions(+), 1549 deletions(-) delete mode 100644 pkg/connector/http2/conn.go delete mode 100644 pkg/connector/http2/connector.go delete mode 100644 pkg/connector/http2/metadata.go delete mode 100644 pkg/dialer/http2/dialer.go delete mode 100644 pkg/dialer/http2/h2/conn.go delete mode 100644 pkg/dialer/http2/h2/dialer.go delete mode 100644 pkg/dialer/http2/h2/metadata.go delete mode 100644 pkg/dialer/http2/metadata.go delete mode 100644 pkg/handler/http2/conn.go delete mode 100644 pkg/handler/http2/handler.go delete mode 100644 pkg/handler/http2/metadata.go delete mode 100644 pkg/listener/http2/conn.go delete mode 100644 pkg/listener/http2/h2/conn.go delete mode 100644 pkg/listener/http2/h2/listener.go delete mode 100644 pkg/listener/http2/h2/metadata.go delete mode 100644 pkg/listener/http2/listener.go delete mode 100644 pkg/listener/http2/metadata.go diff --git a/cmd/gost/register.go b/cmd/gost/register.go index c73b0a9..bcb58c8 100644 --- a/cmd/gost/register.go +++ b/cmd/gost/register.go @@ -4,12 +4,9 @@ import ( // core components _ "github.com/go-gost/gost/v3/pkg/connector/forward" _ "github.com/go-gost/gost/v3/pkg/connector/http" - _ "github.com/go-gost/gost/v3/pkg/connector/http2" _ "github.com/go-gost/gost/v3/pkg/connector/socks/v4" _ "github.com/go-gost/gost/v3/pkg/connector/socks/v5" - _ "github.com/go-gost/gost/v3/pkg/dialer/http2" - _ "github.com/go-gost/gost/v3/pkg/dialer/http2/h2" _ "github.com/go-gost/gost/v3/pkg/dialer/tcp" _ "github.com/go-gost/gost/v3/pkg/dialer/tls" _ "github.com/go-gost/gost/v3/pkg/dialer/udp" @@ -18,69 +15,72 @@ import ( _ "github.com/go-gost/gost/v3/pkg/handler/forward/local" _ "github.com/go-gost/gost/v3/pkg/handler/forward/remote" _ "github.com/go-gost/gost/v3/pkg/handler/http" - _ "github.com/go-gost/gost/v3/pkg/handler/http2" _ "github.com/go-gost/gost/v3/pkg/handler/socks/v4" _ "github.com/go-gost/gost/v3/pkg/handler/socks/v5" - _ "github.com/go-gost/gost/v3/pkg/listener/http2" - _ "github.com/go-gost/gost/v3/pkg/listener/http2/h2" _ "github.com/go-gost/gost/v3/pkg/listener/rtcp" _ "github.com/go-gost/gost/v3/pkg/listener/rudp" _ "github.com/go-gost/gost/v3/pkg/listener/tcp" _ "github.com/go-gost/gost/v3/pkg/listener/tls" /* - // extended components - _ "github.com/go-gost/gost/pkg/connector/relay" - _ "github.com/go-gost/gost/pkg/connector/sni" - _ "github.com/go-gost/gost/pkg/connector/ss" - _ "github.com/go-gost/gost/pkg/connector/ss/udp" - _ "github.com/go-gost/gost/pkg/connector/sshd" + // extended components + _ "github.com/go-gost/gost/pkg/connector/relay" + _ "github.com/go-gost/gost/pkg/connector/sni" + _ "github.com/go-gost/gost/pkg/connector/ss" + _ "github.com/go-gost/gost/pkg/connector/ss/udp" + _ "github.com/go-gost/gost/pkg/connector/sshd" + _ "github.com/go-gost/gost/v3/pkg/connector/http2" - // Register dialers - _ "github.com/go-gost/gost/pkg/dialer/ftcp" - _ "github.com/go-gost/gost/pkg/dialer/grpc" - _ "github.com/go-gost/gost/pkg/dialer/http3" - _ "github.com/go-gost/gost/pkg/dialer/icmp" - _ "github.com/go-gost/gost/pkg/dialer/kcp" - _ "github.com/go-gost/gost/pkg/dialer/obfs/http" - _ "github.com/go-gost/gost/pkg/dialer/obfs/tls" - _ "github.com/go-gost/gost/pkg/dialer/pht" - _ "github.com/go-gost/gost/pkg/dialer/quic" - _ "github.com/go-gost/gost/pkg/dialer/ssh" - _ "github.com/go-gost/gost/pkg/dialer/sshd" - _ "github.com/go-gost/gost/pkg/dialer/tls/mux" - _ "github.com/go-gost/gost/pkg/dialer/ws" - _ "github.com/go-gost/gost/pkg/dialer/ws/mux" + // Register dialers + _ "github.com/go-gost/gost/pkg/dialer/ftcp" + _ "github.com/go-gost/gost/pkg/dialer/grpc" + _ "github.com/go-gost/gost/pkg/dialer/http3" + _ "github.com/go-gost/gost/pkg/dialer/icmp" + _ "github.com/go-gost/gost/pkg/dialer/kcp" + _ "github.com/go-gost/gost/pkg/dialer/obfs/http" + _ "github.com/go-gost/gost/pkg/dialer/obfs/tls" + _ "github.com/go-gost/gost/pkg/dialer/pht" + _ "github.com/go-gost/gost/pkg/dialer/quic" + _ "github.com/go-gost/gost/pkg/dialer/ssh" + _ "github.com/go-gost/gost/pkg/dialer/sshd" + _ "github.com/go-gost/gost/pkg/dialer/tls/mux" + _ "github.com/go-gost/gost/pkg/dialer/ws" + _ "github.com/go-gost/gost/pkg/dialer/ws/mux" + _ "github.com/go-gost/gost/v3/pkg/dialer/http2" + _ "github.com/go-gost/gost/v3/pkg/dialer/http2/h2" - // Register handlers - _ "github.com/go-gost/gost/pkg/handler/dns" - _ "github.com/go-gost/gost/pkg/handler/redirect" - _ "github.com/go-gost/gost/pkg/handler/relay" - _ "github.com/go-gost/gost/pkg/handler/sni" - _ "github.com/go-gost/gost/pkg/handler/ss" - _ "github.com/go-gost/gost/pkg/handler/ss/udp" - _ "github.com/go-gost/gost/pkg/handler/sshd" - _ "github.com/go-gost/gost/pkg/handler/tap" - _ "github.com/go-gost/gost/pkg/handler/tun" + // Register handlers + _ "github.com/go-gost/gost/pkg/handler/dns" + _ "github.com/go-gost/gost/pkg/handler/redirect" + _ "github.com/go-gost/gost/pkg/handler/relay" + _ "github.com/go-gost/gost/pkg/handler/sni" + _ "github.com/go-gost/gost/pkg/handler/ss" + _ "github.com/go-gost/gost/pkg/handler/ss/udp" + _ "github.com/go-gost/gost/pkg/handler/sshd" + _ "github.com/go-gost/gost/pkg/handler/tap" + _ "github.com/go-gost/gost/pkg/handler/tun" + _ "github.com/go-gost/gost/v3/pkg/handler/http2" - // Register listeners - _ "github.com/go-gost/gost/pkg/listener/dns" - _ "github.com/go-gost/gost/pkg/listener/ftcp" - _ "github.com/go-gost/gost/pkg/listener/grpc" - _ "github.com/go-gost/gost/pkg/listener/http3" - _ "github.com/go-gost/gost/pkg/listener/icmp" - _ "github.com/go-gost/gost/pkg/listener/kcp" - _ "github.com/go-gost/gost/pkg/listener/obfs/http" - _ "github.com/go-gost/gost/pkg/listener/obfs/tls" - _ "github.com/go-gost/gost/pkg/listener/pht" - _ "github.com/go-gost/gost/pkg/listener/quic" - _ "github.com/go-gost/gost/pkg/listener/redirect/udp" - _ "github.com/go-gost/gost/pkg/listener/ssh" - _ "github.com/go-gost/gost/pkg/listener/sshd" - _ "github.com/go-gost/gost/pkg/listener/tap" - _ "github.com/go-gost/gost/pkg/listener/tls/mux" - _ "github.com/go-gost/gost/pkg/listener/tun" - _ "github.com/go-gost/gost/pkg/listener/udp" - _ "github.com/go-gost/gost/pkg/listener/ws" - _ "github.com/go-gost/gost/pkg/listener/ws/mux" + // Register listeners + _ "github.com/go-gost/gost/pkg/listener/dns" + _ "github.com/go-gost/gost/pkg/listener/ftcp" + _ "github.com/go-gost/gost/pkg/listener/grpc" + _ "github.com/go-gost/gost/pkg/listener/http3" + _ "github.com/go-gost/gost/pkg/listener/icmp" + _ "github.com/go-gost/gost/pkg/listener/kcp" + _ "github.com/go-gost/gost/pkg/listener/obfs/http" + _ "github.com/go-gost/gost/pkg/listener/obfs/tls" + _ "github.com/go-gost/gost/pkg/listener/pht" + _ "github.com/go-gost/gost/pkg/listener/quic" + _ "github.com/go-gost/gost/pkg/listener/redirect/udp" + _ "github.com/go-gost/gost/pkg/listener/ssh" + _ "github.com/go-gost/gost/pkg/listener/sshd" + _ "github.com/go-gost/gost/pkg/listener/tap" + _ "github.com/go-gost/gost/pkg/listener/tls/mux" + _ "github.com/go-gost/gost/pkg/listener/tun" + _ "github.com/go-gost/gost/pkg/listener/udp" + _ "github.com/go-gost/gost/pkg/listener/ws" + _ "github.com/go-gost/gost/pkg/listener/ws/mux" + _ "github.com/go-gost/gost/v3/pkg/listener/http2" + _ "github.com/go-gost/gost/v3/pkg/listener/http2/h2" */) diff --git a/go.mod b/go.mod index 9b6ca3a..cfcb4a5 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,6 @@ require ( github.com/sirupsen/logrus v1.8.1 github.com/spf13/viper v1.9.0 github.com/xtaci/smux v1.5.16 - golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 gopkg.in/yaml.v2 v2.4.0 ) @@ -48,6 +47,7 @@ require ( github.com/subosito/gotenv v1.2.0 // indirect github.com/ugorji/go/codec v1.1.7 // indirect golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70 // indirect + golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect golang.org/x/text v0.3.7 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/ini.v1 v1.63.2 // indirect diff --git a/pkg/connector/http2/conn.go b/pkg/connector/http2/conn.go deleted file mode 100644 index 186e775..0000000 --- a/pkg/connector/http2/conn.go +++ /dev/null @@ -1,54 +0,0 @@ -package http2 - -import ( - "errors" - "io" - "net" - "time" -) - -// HTTP2 connection, wrapped up just like a net.Conn. -type http2Conn struct { - r io.Reader - w io.Writer - remoteAddr net.Addr - localAddr net.Addr -} - -func (c *http2Conn) Read(b []byte) (n int, err error) { - return c.r.Read(b) -} - -func (c *http2Conn) Write(b []byte) (n int, err error) { - return c.w.Write(b) -} - -func (c *http2Conn) Close() (err error) { - if r, ok := c.r.(io.Closer); ok { - err = r.Close() - } - if w, ok := c.w.(io.Closer); ok { - err = w.Close() - } - return -} - -func (c *http2Conn) LocalAddr() net.Addr { - return c.localAddr -} - -func (c *http2Conn) RemoteAddr() net.Addr { - return c.remoteAddr -} - -func (c *http2Conn) SetDeadline(t time.Time) error { - return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} -} - -func (c *http2Conn) SetReadDeadline(t time.Time) error { - return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} -} - -func (c *http2Conn) SetWriteDeadline(t time.Time) error { - return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} -} diff --git a/pkg/connector/http2/connector.go b/pkg/connector/http2/connector.go deleted file mode 100644 index 034df2f..0000000 --- a/pkg/connector/http2/connector.go +++ /dev/null @@ -1,121 +0,0 @@ -package http2 - -import ( - "context" - "encoding/base64" - "errors" - "fmt" - "io" - "net" - "net/http" - "net/http/httputil" - "net/url" - "time" - - "github.com/go-gost/gost/v3/pkg/connector" - http2_util "github.com/go-gost/gost/v3/pkg/internal/util/http2" - "github.com/go-gost/gost/v3/pkg/logger" - md "github.com/go-gost/gost/v3/pkg/metadata" - "github.com/go-gost/gost/v3/pkg/registry" -) - -func init() { - registry.ConnectorRegistry().Register("http2", NewConnector) -} - -type http2Connector struct { - md metadata - options connector.Options -} - -func NewConnector(opts ...connector.Option) connector.Connector { - options := connector.Options{} - for _, opt := range opts { - opt(&options) - } - - return &http2Connector{ - options: options, - } -} - -func (c *http2Connector) Init(md md.Metadata) (err error) { - return c.parseMetadata(md) -} - -func (c *http2Connector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) { - log := c.options.Logger.WithFields(map[string]any{ - "local": conn.LocalAddr().String(), - "remote": conn.RemoteAddr().String(), - "network": network, - "address": address, - }) - log.Infof("connect %s/%s", address, network) - - cc, ok := conn.(*http2_util.ClientConn) - if !ok { - err := errors.New("wrong connection type") - log.Error(err) - return nil, err - } - - pr, pw := io.Pipe() - req := &http.Request{ - Method: http.MethodConnect, - URL: &url.URL{Scheme: "https", Host: conn.RemoteAddr().String()}, - Host: address, - ProtoMajor: 2, - ProtoMinor: 0, - Header: make(http.Header), - Body: pr, - // ContentLength: -1, - } - if c.md.UserAgent != "" { - req.Header.Set("User-Agent", c.md.UserAgent) - } - - if user := c.options.Auth; user != nil { - u := user.Username() - p, _ := user.Password() - req.Header.Set("Proxy-Authorization", - "Basic "+base64.StdEncoding.EncodeToString([]byte(u+":"+p))) - } - - if log.IsLevelEnabled(logger.DebugLevel) { - dump, _ := httputil.DumpRequest(req, false) - log.Debug(string(dump)) - } - - if c.md.connectTimeout > 0 { - conn.SetDeadline(time.Now().Add(c.md.connectTimeout)) - defer conn.SetDeadline(time.Time{}) - } - - resp, err := cc.Client().Do(req.WithContext(ctx)) - if err != nil { - log.Error(err) - cc.Close() - return nil, err - } - - if log.IsLevelEnabled(logger.DebugLevel) { - dump, _ := httputil.DumpResponse(resp, false) - log.Debug(string(dump)) - } - if resp.StatusCode != http.StatusOK { - resp.Body.Close() - err = fmt.Errorf("%s", resp.Status) - log.Error(err) - return nil, err - } - - hc := &http2Conn{ - r: resp.Body, - w: pw, - localAddr: conn.RemoteAddr(), - } - - hc.remoteAddr, _ = net.ResolveTCPAddr(network, address) - - return hc, nil -} diff --git a/pkg/connector/http2/metadata.go b/pkg/connector/http2/metadata.go deleted file mode 100644 index 3937cb6..0000000 --- a/pkg/connector/http2/metadata.go +++ /dev/null @@ -1,31 +0,0 @@ -package http2 - -import ( - "time" - - mdata "github.com/go-gost/gost/v3/pkg/metadata" -) - -const ( - defaultUserAgent = "Chrome/78.0.3904.106" -) - -type metadata struct { - connectTimeout time.Duration - UserAgent string -} - -func (c *http2Connector) parseMetadata(md mdata.Metadata) (err error) { - const ( - connectTimeout = "timeout" - userAgent = "userAgent" - ) - - c.md.connectTimeout = mdata.GetDuration(md, connectTimeout) - c.md.UserAgent = mdata.GetString(md, userAgent) - if c.md.UserAgent == "" { - c.md.UserAgent = defaultUserAgent - } - - return -} diff --git a/pkg/dialer/http2/dialer.go b/pkg/dialer/http2/dialer.go deleted file mode 100644 index 6623549..0000000 --- a/pkg/dialer/http2/dialer.go +++ /dev/null @@ -1,101 +0,0 @@ -package http2 - -import ( - "context" - "net" - "net/http" - "sync" - "time" - - net_dialer "github.com/go-gost/gost/v3/pkg/common/net/dialer" - "github.com/go-gost/gost/v3/pkg/dialer" - http2_util "github.com/go-gost/gost/v3/pkg/internal/util/http2" - "github.com/go-gost/gost/v3/pkg/logger" - md "github.com/go-gost/gost/v3/pkg/metadata" - "github.com/go-gost/gost/v3/pkg/registry" -) - -func init() { - registry.DialerRegistry().Register("http2", NewDialer) -} - -type http2Dialer struct { - clients map[string]*http.Client - clientMutex sync.Mutex - logger logger.Logger - md metadata - options dialer.Options -} - -func NewDialer(opts ...dialer.Option) dialer.Dialer { - options := dialer.Options{} - for _, opt := range opts { - opt(&options) - } - - return &http2Dialer{ - clients: make(map[string]*http.Client), - logger: options.Logger, - options: options, - } -} - -func (d *http2Dialer) Init(md md.Metadata) (err error) { - if err = d.parseMetadata(md); err != nil { - return - } - - return nil -} - -// Multiplex implements dialer.Multiplexer interface. -func (d *http2Dialer) Multiplex() bool { - return true -} - -func (d *http2Dialer) Dial(ctx context.Context, address string, opts ...dialer.DialOption) (net.Conn, error) { - raddr, err := net.ResolveTCPAddr("tcp", address) - if err != nil { - d.logger.Error(err) - return nil, err - } - - d.clientMutex.Lock() - defer d.clientMutex.Unlock() - - client, ok := d.clients[address] - if !ok { - options := dialer.DialOptions{} - for _, opt := range opts { - opt(&options) - } - - client = &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: d.options.TLSConfig, - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - netd := options.NetDialer - if netd == nil { - netd = net_dialer.DefaultNetDialer - } - return netd.Dial(ctx, network, addr) - }, - ForceAttemptHTTP2: true, - MaxIdleConns: 100, - IdleConnTimeout: 90 * time.Second, - TLSHandshakeTimeout: 10 * time.Second, - ExpectContinueTimeout: 1 * time.Second, - }, - } - d.clients[address] = client - } - - return http2_util.NewClientConn( - &net.TCPAddr{}, raddr, - client, - func() { - d.clientMutex.Lock() - defer d.clientMutex.Unlock() - delete(d.clients, address) - }), nil -} diff --git a/pkg/dialer/http2/h2/conn.go b/pkg/dialer/http2/h2/conn.go deleted file mode 100644 index 6b786b4..0000000 --- a/pkg/dialer/http2/h2/conn.go +++ /dev/null @@ -1,54 +0,0 @@ -package h2 - -import ( - "errors" - "io" - "net" - "time" -) - -// HTTP2 connection, wrapped up just like a net.Conn. -type http2Conn struct { - r io.Reader - w io.Writer - remoteAddr net.Addr - localAddr net.Addr -} - -func (c *http2Conn) Read(b []byte) (n int, err error) { - return c.r.Read(b) -} - -func (c *http2Conn) Write(b []byte) (n int, err error) { - return c.w.Write(b) -} - -func (c *http2Conn) Close() (err error) { - if r, ok := c.r.(io.Closer); ok { - err = r.Close() - } - if w, ok := c.w.(io.Closer); ok { - err = w.Close() - } - return -} - -func (c *http2Conn) LocalAddr() net.Addr { - return c.localAddr -} - -func (c *http2Conn) RemoteAddr() net.Addr { - return c.remoteAddr -} - -func (c *http2Conn) SetDeadline(t time.Time) error { - return &net.OpError{Op: "set", Net: "h2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} -} - -func (c *http2Conn) SetReadDeadline(t time.Time) error { - return &net.OpError{Op: "set", Net: "h2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} -} - -func (c *http2Conn) SetWriteDeadline(t time.Time) error { - return &net.OpError{Op: "set", Net: "h2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} -} diff --git a/pkg/dialer/http2/h2/dialer.go b/pkg/dialer/http2/h2/dialer.go deleted file mode 100644 index d046dd6..0000000 --- a/pkg/dialer/http2/h2/dialer.go +++ /dev/null @@ -1,165 +0,0 @@ -package h2 - -import ( - "context" - "crypto/tls" - "errors" - "io" - "net" - "net/http" - "net/http/httputil" - "net/url" - "sync" - "time" - - "github.com/go-gost/gost/v3/pkg/dialer" - "github.com/go-gost/gost/v3/pkg/logger" - md "github.com/go-gost/gost/v3/pkg/metadata" - "github.com/go-gost/gost/v3/pkg/registry" - "golang.org/x/net/http2" -) - -func init() { - registry.DialerRegistry().Register("h2", NewTLSDialer) - registry.DialerRegistry().Register("h2c", NewDialer) -} - -type h2Dialer struct { - clients map[string]*http.Client - clientMutex sync.Mutex - h2c bool - logger logger.Logger - md metadata - options dialer.Options -} - -func NewDialer(opts ...dialer.Option) dialer.Dialer { - options := dialer.Options{} - for _, opt := range opts { - opt(&options) - } - - return &h2Dialer{ - h2c: true, - clients: make(map[string]*http.Client), - logger: options.Logger, - options: options, - } -} - -func NewTLSDialer(opts ...dialer.Option) dialer.Dialer { - options := dialer.Options{} - for _, opt := range opts { - opt(&options) - } - - return &h2Dialer{ - clients: make(map[string]*http.Client), - logger: options.Logger, - options: options, - } -} - -func (d *h2Dialer) Init(md md.Metadata) (err error) { - if err = d.parseMetadata(md); err != nil { - return - } - - return nil -} - -// Multiplex implements dialer.Multiplexer interface. -func (d *h2Dialer) Multiplex() bool { - return true -} - -func (d *h2Dialer) Dial(ctx context.Context, address string, opts ...dialer.DialOption) (net.Conn, error) { - raddr, err := net.ResolveTCPAddr("tcp", address) - if err != nil { - d.logger.Error(err) - return nil, err - } - - d.clientMutex.Lock() - - client, ok := d.clients[address] - if !ok { - options := &dialer.DialOptions{} - for _, opt := range opts { - opt(options) - } - - client = &http.Client{} - if d.h2c { - client.Transport = &http2.Transport{ - DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) { - return options.NetDialer.Dial(ctx, network, addr) - }, - } - } else { - client.Transport = &http.Transport{ - TLSClientConfig: d.options.TLSConfig, - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - return options.NetDialer.Dial(ctx, network, addr) - }, - ForceAttemptHTTP2: true, - MaxIdleConns: 100, - IdleConnTimeout: 90 * time.Second, - TLSHandshakeTimeout: 10 * time.Second, - ExpectContinueTimeout: 1 * time.Second, - } - } - - d.clients[address] = client - } - d.clientMutex.Unlock() - - host := d.md.host - if host == "" { - host = address - } - - pr, pw := io.Pipe() - req := &http.Request{ - Method: http.MethodConnect, - URL: &url.URL{Scheme: "https", Host: host}, - Header: make(http.Header), - ProtoMajor: 2, - ProtoMinor: 0, - Body: pr, - Host: host, - // ContentLength: -1, - } - if d.md.path != "" { - req.Method = http.MethodGet - req.URL.Path = d.md.path - } - - if d.logger.IsLevelEnabled(logger.DebugLevel) { - dump, _ := httputil.DumpRequest(req, false) - d.logger.Debug(string(dump)) - } - - resp, err := client.Do(req) - if err != nil { - return nil, err - } - - if d.logger.IsLevelEnabled(logger.DebugLevel) { - dump, _ := httputil.DumpResponse(resp, false) - d.logger.Debug(string(dump)) - } - - if resp.StatusCode != http.StatusOK { - resp.Body.Close() - return nil, errors.New(resp.Status) - } - - conn := &http2Conn{ - r: resp.Body, - w: pw, - remoteAddr: raddr, - localAddr: &net.TCPAddr{IP: net.IPv4zero, Port: 0}, - } - return conn, nil -} diff --git a/pkg/dialer/http2/h2/metadata.go b/pkg/dialer/http2/h2/metadata.go deleted file mode 100644 index c757797..0000000 --- a/pkg/dialer/http2/h2/metadata.go +++ /dev/null @@ -1,22 +0,0 @@ -package h2 - -import ( - mdata "github.com/go-gost/gost/v3/pkg/metadata" -) - -type metadata struct { - host string - path string -} - -func (d *h2Dialer) parseMetadata(md mdata.Metadata) (err error) { - const ( - host = "host" - path = "path" - ) - - d.md.host = mdata.GetString(md, host) - d.md.path = mdata.GetString(md, path) - - return -} diff --git a/pkg/dialer/http2/metadata.go b/pkg/dialer/http2/metadata.go deleted file mode 100644 index 675e175..0000000 --- a/pkg/dialer/http2/metadata.go +++ /dev/null @@ -1,12 +0,0 @@ -package http2 - -import ( - mdata "github.com/go-gost/gost/v3/pkg/metadata" -) - -type metadata struct { -} - -func (d *http2Dialer) parseMetadata(md mdata.Metadata) (err error) { - return -} diff --git a/pkg/handler/http2/conn.go b/pkg/handler/http2/conn.go deleted file mode 100644 index 5454565..0000000 --- a/pkg/handler/http2/conn.go +++ /dev/null @@ -1,46 +0,0 @@ -package http2 - -import ( - "errors" - "io" - "net/http" -) - -type readWriter struct { - r io.Reader - w io.Writer -} - -func (rw *readWriter) Read(p []byte) (n int, err error) { - return rw.r.Read(p) -} - -func (rw *readWriter) Write(p []byte) (n int, err error) { - return rw.w.Write(p) -} - -type flushWriter struct { - w io.Writer -} - -func (fw flushWriter) Write(p []byte) (n int, err error) { - defer func() { - if r := recover(); r != nil { - if s, ok := r.(string); ok { - err = errors.New(s) - return - } - err = r.(error) - } - }() - - n, err = fw.w.Write(p) - if err != nil { - // log.Log("flush writer:", err) - return - } - if f, ok := fw.w.(http.Flusher); ok { - f.Flush() - } - return -} diff --git a/pkg/handler/http2/handler.go b/pkg/handler/http2/handler.go deleted file mode 100644 index 1dd22f6..0000000 --- a/pkg/handler/http2/handler.go +++ /dev/null @@ -1,343 +0,0 @@ -package http2 - -import ( - "bufio" - "bytes" - "context" - "encoding/base64" - "encoding/binary" - "errors" - "hash/crc32" - "io" - "io/ioutil" - "net" - "net/http" - "net/http/httputil" - "os" - "strconv" - "strings" - "time" - - "github.com/go-gost/gost/v3/pkg/chain" - netpkg "github.com/go-gost/gost/v3/pkg/common/net" - "github.com/go-gost/gost/v3/pkg/handler" - http2_util "github.com/go-gost/gost/v3/pkg/internal/util/http2" - "github.com/go-gost/gost/v3/pkg/logger" - md "github.com/go-gost/gost/v3/pkg/metadata" - "github.com/go-gost/gost/v3/pkg/registry" -) - -func init() { - registry.HandlerRegistry().Register("http2", NewHandler) -} - -type http2Handler struct { - router *chain.Router - md metadata - options handler.Options -} - -func NewHandler(opts ...handler.Option) handler.Handler { - options := handler.Options{} - for _, opt := range opts { - opt(&options) - } - - return &http2Handler{ - options: options, - } -} - -func (h *http2Handler) Init(md md.Metadata) error { - if err := h.parseMetadata(md); err != nil { - return err - } - - h.router = h.options.Router - if h.router == nil { - h.router = (&chain.Router{}).WithLogger(h.options.Logger) - } - - return nil -} - -func (h *http2Handler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error { - defer conn.Close() - - start := time.Now() - log := h.options.Logger.WithFields(map[string]any{ - "remote": conn.RemoteAddr().String(), - "local": conn.LocalAddr().String(), - }) - log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr()) - defer func() { - log.WithFields(map[string]any{ - "duration": time.Since(start), - }).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr()) - }() - - cc, ok := conn.(*http2_util.ServerConn) - if !ok { - err := errors.New("wrong connection type") - log.Error(err) - return err - } - return h.roundTrip(ctx, cc.Writer(), cc.Request(), log) -} - -// NOTE: there is an issue (golang/go#43989) will cause the client hangs -// when server returns an non-200 status code, -// May be fixed in go1.18. -func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req *http.Request, log logger.Logger) error { - // Try to get the actual host. - // Compatible with GOST 2.x. - if v := req.Header.Get("Gost-Target"); v != "" { - if h, err := h.decodeServerName(v); err == nil { - req.Host = h - } - } - req.Header.Del("Gost-Target") - - if v := req.Header.Get("X-Gost-Target"); v != "" { - if h, err := h.decodeServerName(v); err == nil { - req.Host = h - } - } - req.Header.Del("X-Gost-Target") - - addr := req.Host - if _, port, _ := net.SplitHostPort(addr); port == "" { - addr = net.JoinHostPort(addr, "80") - } - - fields := map[string]any{ - "dst": addr, - } - if u, _, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization")); u != "" { - fields["user"] = u - } - log = log.WithFields(fields) - - if log.IsLevelEnabled(logger.DebugLevel) { - dump, _ := httputil.DumpRequest(req, false) - log.Debug(string(dump)) - } - log.Infof("%s >> %s", req.RemoteAddr, addr) - - for k := range h.md.header { - w.Header().Set(k, h.md.header.Get(k)) - } - - if h.options.Bypass != nil && h.options.Bypass.Contains(addr) { - w.WriteHeader(http.StatusForbidden) - log.Info("bypass: ", addr) - return nil - } - - resp := &http.Response{ - ProtoMajor: 2, - ProtoMinor: 0, - Header: http.Header{}, - Body: ioutil.NopCloser(bytes.NewReader([]byte{})), - } - - if !h.authenticate(w, req, resp, log) { - return nil - } - - // delete the proxy related headers. - req.Header.Del("Proxy-Authorization") - req.Header.Del("Proxy-Connection") - - cc, err := h.router.Dial(ctx, "tcp", addr) - if err != nil { - log.Error(err) - w.WriteHeader(http.StatusServiceUnavailable) - return err - } - defer cc.Close() - - if req.Method == http.MethodConnect { - w.WriteHeader(http.StatusOK) - if fw, ok := w.(http.Flusher); ok { - fw.Flush() - } - - // compatible with HTTP1.x - if hj, ok := w.(http.Hijacker); ok && req.ProtoMajor == 1 { - // we take over the underly connection - conn, _, err := hj.Hijack() - if err != nil { - log.Error(err) - w.WriteHeader(http.StatusInternalServerError) - return err - } - defer conn.Close() - - start := time.Now() - log.Infof("%s <-> %s", conn.RemoteAddr(), addr) - netpkg.Transport(conn, cc) - log.WithFields(map[string]any{ - "duration": time.Since(start), - }).Infof("%s >-< %s", conn.RemoteAddr(), addr) - - return nil - } - - start := time.Now() - log.Infof("%s <-> %s", req.RemoteAddr, addr) - netpkg.Transport(&readWriter{r: req.Body, w: flushWriter{w}}, cc) - log.WithFields(map[string]any{ - "duration": time.Since(start), - }).Infof("%s >-< %s", req.RemoteAddr, addr) - return nil - } - - // TODO: forward request - return nil -} - -func (h *http2Handler) decodeServerName(s string) (string, error) { - b, err := base64.RawURLEncoding.DecodeString(s) - if err != nil { - return "", err - } - if len(b) < 4 { - return "", errors.New("invalid name") - } - v, err := base64.RawURLEncoding.DecodeString(string(b[4:])) - if err != nil { - return "", err - } - if crc32.ChecksumIEEE(v) != binary.BigEndian.Uint32(b[:4]) { - return "", errors.New("invalid name") - } - return string(v), nil -} - -func (h *http2Handler) basicProxyAuth(proxyAuth string) (username, password string, ok bool) { - if proxyAuth == "" { - return - } - - if !strings.HasPrefix(proxyAuth, "Basic ") { - return - } - c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(proxyAuth, "Basic ")) - if err != nil { - return - } - cs := string(c) - s := strings.IndexByte(cs, ':') - if s < 0 { - return - } - - return cs[:s], cs[s+1:], true -} - -func (h *http2Handler) authenticate(w http.ResponseWriter, r *http.Request, resp *http.Response, log logger.Logger) (ok bool) { - u, p, _ := h.basicProxyAuth(r.Header.Get("Proxy-Authorization")) - if h.options.Auther == nil || h.options.Auther.Authenticate(u, p) { - return true - } - - pr := h.md.probeResistance - // probing resistance is enabled, and knocking host is mismatch. - if pr != nil && (pr.Knock == "" || !strings.EqualFold(r.URL.Hostname(), pr.Knock)) { - resp.StatusCode = http.StatusServiceUnavailable // default status code - switch pr.Type { - case "code": - resp.StatusCode, _ = strconv.Atoi(pr.Value) - case "web": - url := pr.Value - if !strings.HasPrefix(url, "http") { - url = "http://" + url - } - r, err := http.Get(url) - if err != nil { - log.Error(err) - break - } - resp = r - defer resp.Body.Close() - case "host": - cc, err := net.Dial("tcp", pr.Value) - if err != nil { - log.Error(err) - break - } - defer cc.Close() - - if err := h.forwardRequest(w, r, cc); err != nil { - log.Error(err) - } - return - case "file": - f, _ := os.Open(pr.Value) - if f != nil { - defer f.Close() - - resp.StatusCode = http.StatusOK - if finfo, _ := f.Stat(); finfo != nil { - resp.ContentLength = finfo.Size() - } - resp.Header.Set("Content-Type", "text/html") - resp.Body = f - } - } - } - - if resp.StatusCode == 0 { - resp.StatusCode = http.StatusProxyAuthRequired - resp.Header.Add("Proxy-Authenticate", "Basic realm=\"gost\"") - if strings.ToLower(r.Header.Get("Proxy-Connection")) == "keep-alive" { - // XXX libcurl will keep sending auth request in same conn - // which we don't supported yet. - resp.Header.Add("Connection", "close") - resp.Header.Add("Proxy-Connection", "close") - } - - log.Info("proxy authentication required") - } else { - resp.Header = http.Header{} - resp.Header.Set("Server", "nginx/1.20.1") - resp.Header.Set("Date", time.Now().Format(http.TimeFormat)) - if resp.StatusCode == http.StatusOK { - resp.Header.Set("Connection", "keep-alive") - } - } - - if log.IsLevelEnabled(logger.DebugLevel) { - dump, _ := httputil.DumpResponse(resp, false) - log.Debug(string(dump)) - } - - h.writeResponse(w, resp) - - return -} -func (h *http2Handler) forwardRequest(w http.ResponseWriter, r *http.Request, rw io.ReadWriter) (err error) { - if err = r.Write(rw); err != nil { - return - } - - resp, err := http.ReadResponse(bufio.NewReader(rw), r) - if err != nil { - return - } - defer resp.Body.Close() - - return h.writeResponse(w, resp) -} - -func (h *http2Handler) writeResponse(w http.ResponseWriter, resp *http.Response) error { - for k, v := range resp.Header { - for _, vv := range v { - w.Header().Add(k, vv) - } - } - w.WriteHeader(resp.StatusCode) - _, err := io.Copy(flushWriter{w}, resp.Body) - return err -} diff --git a/pkg/handler/http2/metadata.go b/pkg/handler/http2/metadata.go deleted file mode 100644 index 5ce2845..0000000 --- a/pkg/handler/http2/metadata.go +++ /dev/null @@ -1,47 +0,0 @@ -package http2 - -import ( - "net/http" - "strings" - - mdata "github.com/go-gost/gost/v3/pkg/metadata" -) - -type metadata struct { - probeResistance *probeResistance - header http.Header -} - -func (h *http2Handler) parseMetadata(md mdata.Metadata) error { - const ( - header = "header" - probeResistKey = "probeResistance" - knock = "knock" - ) - - if m := mdata.GetStringMapString(md, header); len(m) > 0 { - hd := http.Header{} - for k, v := range m { - hd.Add(k, v) - } - h.md.header = hd - } - - if v := mdata.GetString(md, probeResistKey); v != "" { - if ss := strings.SplitN(v, ":", 2); len(ss) == 2 { - h.md.probeResistance = &probeResistance{ - Type: ss[0], - Value: ss[1], - Knock: mdata.GetString(md, knock), - } - } - } - - return nil -} - -type probeResistance struct { - Type string - Value string - Knock string -} diff --git a/pkg/listener/http2/conn.go b/pkg/listener/http2/conn.go deleted file mode 100644 index c145860..0000000 --- a/pkg/listener/http2/conn.go +++ /dev/null @@ -1,54 +0,0 @@ -package http2 - -import ( - "errors" - "net" - "net/http" - "time" -) - -// a dummy HTTP2 server conn used by HTTP2 handler -type conn struct { - r *http.Request - w http.ResponseWriter - closed chan struct{} -} - -func (c *conn) Read(b []byte) (n int, err error) { - return 0, &net.OpError{Op: "read", Net: "http2", Source: nil, Addr: nil, Err: errors.New("read not supported")} -} - -func (c *conn) Write(b []byte) (n int, err error) { - return 0, &net.OpError{Op: "write", Net: "http2", Source: nil, Addr: nil, Err: errors.New("write not supported")} -} - -func (c *conn) Close() error { - select { - case <-c.closed: - default: - close(c.closed) - } - return nil -} - -func (c *conn) LocalAddr() net.Addr { - addr, _ := net.ResolveTCPAddr("tcp", c.r.Host) - return addr -} - -func (c *conn) RemoteAddr() net.Addr { - addr, _ := net.ResolveTCPAddr("tcp", c.r.RemoteAddr) - return addr -} - -func (c *conn) SetDeadline(t time.Time) error { - return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} -} - -func (c *conn) SetReadDeadline(t time.Time) error { - return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} -} - -func (c *conn) SetWriteDeadline(t time.Time) error { - return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} -} diff --git a/pkg/listener/http2/h2/conn.go b/pkg/listener/http2/h2/conn.go deleted file mode 100644 index 10f8d4c..0000000 --- a/pkg/listener/http2/h2/conn.go +++ /dev/null @@ -1,89 +0,0 @@ -package h2 - -import ( - "errors" - "io" - "net" - "net/http" - "time" -) - -// HTTP2 connection, wrapped up just like a net.Conn -type conn struct { - r io.Reader - w io.Writer - remoteAddr net.Addr - localAddr net.Addr - closed chan struct{} -} - -func (c *conn) Read(b []byte) (n int, err error) { - return c.r.Read(b) -} - -func (c *conn) Write(b []byte) (n int, err error) { - return c.w.Write(b) -} - -func (c *conn) Close() (err error) { - select { - case <-c.closed: - return - default: - close(c.closed) - } - if rc, ok := c.r.(io.Closer); ok { - err = rc.Close() - } - if w, ok := c.w.(io.Closer); ok { - err = w.Close() - } - return -} - -func (c *conn) LocalAddr() net.Addr { - return c.localAddr -} - -func (c *conn) RemoteAddr() net.Addr { - return c.remoteAddr -} - -func (c *conn) SetDeadline(t time.Time) error { - return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} -} - -func (c *conn) SetReadDeadline(t time.Time) error { - return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} -} - -func (c *conn) SetWriteDeadline(t time.Time) error { - return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} -} - -type flushWriter struct { - w io.Writer -} - -func (fw flushWriter) Write(p []byte) (n int, err error) { - defer func() { - if r := recover(); r != nil { - if s, ok := r.(string); ok { - err = errors.New(s) - // log.Log("[http2]", err) - return - } - err = r.(error) - } - }() - - n, err = fw.w.Write(p) - if err != nil { - // log.Log("flush writer:", err) - return - } - if f, ok := fw.w.(http.Flusher); ok { - f.Flush() - } - return -} diff --git a/pkg/listener/http2/h2/listener.go b/pkg/listener/http2/h2/listener.go deleted file mode 100644 index 92540c0..0000000 --- a/pkg/listener/http2/h2/listener.go +++ /dev/null @@ -1,178 +0,0 @@ -package h2 - -import ( - "crypto/tls" - "errors" - "net" - "net/http" - "net/http/httputil" - - "github.com/go-gost/gost/v3/pkg/common/admission" - "github.com/go-gost/gost/v3/pkg/common/metrics" - "github.com/go-gost/gost/v3/pkg/listener" - "github.com/go-gost/gost/v3/pkg/logger" - md "github.com/go-gost/gost/v3/pkg/metadata" - "github.com/go-gost/gost/v3/pkg/registry" - "golang.org/x/net/http2" - "golang.org/x/net/http2/h2c" -) - -func init() { - registry.ListenerRegistry().Register("h2c", NewListener) - registry.ListenerRegistry().Register("h2", NewTLSListener) -} - -type h2Listener struct { - server *http.Server - addr net.Addr - cqueue chan net.Conn - errChan chan error - logger logger.Logger - md metadata - h2c bool - options listener.Options -} - -func NewListener(opts ...listener.Option) listener.Listener { - options := listener.Options{} - for _, opt := range opts { - opt(&options) - } - return &h2Listener{ - h2c: true, - logger: options.Logger, - options: options, - } -} - -func NewTLSListener(opts ...listener.Option) listener.Listener { - options := listener.Options{} - for _, opt := range opts { - opt(&options) - } - return &h2Listener{ - logger: options.Logger, - options: options, - } -} - -func (l *h2Listener) Init(md md.Metadata) (err error) { - if err = l.parseMetadata(md); err != nil { - return - } - - l.server = &http.Server{ - Addr: l.options.Addr, - } - - ln, err := net.Listen("tcp", l.options.Addr) - if err != nil { - return err - } - l.addr = ln.Addr() - ln = metrics.WrapListener(l.options.Service, ln) - ln = admission.WrapListener(l.options.Admission, ln) - - if l.h2c { - l.server.Handler = h2c.NewHandler( - http.HandlerFunc(l.handleFunc), &http2.Server{}) - } else { - l.server.Handler = http.HandlerFunc(l.handleFunc) - l.server.TLSConfig = l.options.TLSConfig - if err := http2.ConfigureServer(l.server, nil); err != nil { - ln.Close() - return err - } - ln = tls.NewListener(ln, l.options.TLSConfig) - } - - l.cqueue = make(chan net.Conn, l.md.backlog) - l.errChan = make(chan error, 1) - - go func() { - if err := l.server.Serve(ln); err != nil { - l.logger.Error(err) - } - }() - - return -} - -func (l *h2Listener) Accept() (conn net.Conn, err error) { - var ok bool - select { - case conn = <-l.cqueue: - case err, ok = <-l.errChan: - if !ok { - err = listener.ErrClosed - } - } - return -} - -func (l *h2Listener) Addr() net.Addr { - return l.addr -} - -func (l *h2Listener) Close() (err error) { - select { - case <-l.errChan: - default: - err = l.server.Close() - l.errChan <- err - close(l.errChan) - } - return nil -} - -func (l *h2Listener) handleFunc(w http.ResponseWriter, r *http.Request) { - if l.logger.IsLevelEnabled(logger.DebugLevel) { - dump, _ := httputil.DumpRequest(r, false) - l.logger.Debug(string(dump)) - } - conn, err := l.upgrade(w, r) - if err != nil { - l.logger.Error(err) - return - } - select { - case l.cqueue <- conn: - default: - conn.Close() - l.logger.Warnf("connection queue is full, client %s discarded", r.RemoteAddr) - } - - <-conn.closed // NOTE: we need to wait for streaming end, or the connection will be closed -} - -func (l *h2Listener) upgrade(w http.ResponseWriter, r *http.Request) (*conn, error) { - if l.md.path == "" && r.Method != http.MethodConnect { - w.WriteHeader(http.StatusMethodNotAllowed) - return nil, errors.New("method not allowed") - } - - if l.md.path != "" && r.RequestURI != l.md.path { - w.WriteHeader(http.StatusBadRequest) - return nil, errors.New("bad request") - } - - w.WriteHeader(http.StatusOK) - if fw, ok := w.(http.Flusher); ok { - fw.Flush() // write header to client - } - - remoteAddr, _ := net.ResolveTCPAddr("tcp", r.RemoteAddr) - if remoteAddr == nil { - remoteAddr = &net.TCPAddr{ - IP: net.IPv4zero, - Port: 0, - } - } - return &conn{ - r: r.Body, - w: flushWriter{w}, - localAddr: l.addr, - remoteAddr: remoteAddr, - closed: make(chan struct{}), - }, nil -} diff --git a/pkg/listener/http2/h2/metadata.go b/pkg/listener/http2/h2/metadata.go deleted file mode 100644 index 3c8ced4..0000000 --- a/pkg/listener/http2/h2/metadata.go +++ /dev/null @@ -1,29 +0,0 @@ -package h2 - -import ( - mdata "github.com/go-gost/gost/v3/pkg/metadata" -) - -const ( - defaultBacklog = 128 -) - -type metadata struct { - path string - backlog int -} - -func (l *h2Listener) parseMetadata(md mdata.Metadata) (err error) { - const ( - path = "path" - backlog = "backlog" - ) - - l.md.backlog = mdata.GetInt(md, backlog) - if l.md.backlog <= 0 { - l.md.backlog = defaultBacklog - } - - l.md.path = mdata.GetString(md, path) - return -} diff --git a/pkg/listener/http2/listener.go b/pkg/listener/http2/listener.go deleted file mode 100644 index 7aa2895..0000000 --- a/pkg/listener/http2/listener.go +++ /dev/null @@ -1,120 +0,0 @@ -package http2 - -import ( - "crypto/tls" - "net" - "net/http" - - "github.com/go-gost/gost/v3/pkg/common/admission" - "github.com/go-gost/gost/v3/pkg/common/metrics" - http2_util "github.com/go-gost/gost/v3/pkg/internal/util/http2" - "github.com/go-gost/gost/v3/pkg/listener" - "github.com/go-gost/gost/v3/pkg/logger" - md "github.com/go-gost/gost/v3/pkg/metadata" - "github.com/go-gost/gost/v3/pkg/registry" - "golang.org/x/net/http2" -) - -func init() { - registry.ListenerRegistry().Register("http2", NewListener) -} - -type http2Listener struct { - server *http.Server - addr net.Addr - cqueue chan net.Conn - errChan chan error - logger logger.Logger - md metadata - options listener.Options -} - -func NewListener(opts ...listener.Option) listener.Listener { - options := listener.Options{} - for _, opt := range opts { - opt(&options) - } - return &http2Listener{ - logger: options.Logger, - options: options, - } -} - -func (l *http2Listener) Init(md md.Metadata) (err error) { - if err = l.parseMetadata(md); err != nil { - return - } - - l.server = &http.Server{ - Addr: l.options.Addr, - Handler: http.HandlerFunc(l.handleFunc), - TLSConfig: l.options.TLSConfig, - } - if err := http2.ConfigureServer(l.server, nil); err != nil { - return err - } - - ln, err := net.Listen("tcp", l.options.Addr) - if err != nil { - return err - } - l.addr = ln.Addr() - ln = metrics.WrapListener(l.options.Service, ln) - ln = admission.WrapListener(l.options.Admission, ln) - - ln = tls.NewListener( - ln, - l.options.TLSConfig, - ) - - l.cqueue = make(chan net.Conn, l.md.backlog) - l.errChan = make(chan error, 1) - - go func() { - if err := l.server.Serve(ln); err != nil { - l.logger.Error(err) - } - }() - - return -} - -func (l *http2Listener) Accept() (conn net.Conn, err error) { - var ok bool - select { - case conn = <-l.cqueue: - case err, ok = <-l.errChan: - if !ok { - err = listener.ErrClosed - } - } - return -} - -func (l *http2Listener) Addr() net.Addr { - return l.addr -} - -func (l *http2Listener) Close() (err error) { - select { - case <-l.errChan: - default: - err = l.server.Close() - l.errChan <- err - close(l.errChan) - } - return nil -} - -func (l *http2Listener) handleFunc(w http.ResponseWriter, r *http.Request) { - raddr, _ := net.ResolveTCPAddr("tcp", r.RemoteAddr) - conn := http2_util.NewServerConn(w, r, l.addr, raddr) - select { - case l.cqueue <- conn: - default: - l.logger.Warnf("connection queue is full, client %s discarded", r.RemoteAddr) - return - } - - <-conn.Done() -} diff --git a/pkg/listener/http2/metadata.go b/pkg/listener/http2/metadata.go deleted file mode 100644 index 97f2661..0000000 --- a/pkg/listener/http2/metadata.go +++ /dev/null @@ -1,25 +0,0 @@ -package http2 - -import ( - mdata "github.com/go-gost/gost/v3/pkg/metadata" -) - -const ( - defaultBacklog = 128 -) - -type metadata struct { - backlog int -} - -func (l *http2Listener) parseMetadata(md mdata.Metadata) (err error) { - const ( - backlog = "backlog" - ) - - l.md.backlog = mdata.GetInt(md, backlog) - if l.md.backlog <= 0 { - l.md.backlog = defaultBacklog - } - return -}