From 9a24c06f96fddc20dd99b59a5e66641f20f61e55 Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Thu, 9 Jan 2020 11:11:51 +0800 Subject: [PATCH] add path support for h2/h2c --- cmd/gost/route.go | 9 ++++----- http2.go | 36 ++++++++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/cmd/gost/route.go b/cmd/gost/route.go index 2d089b7..eb4f5d5 100644 --- a/cmd/gost/route.go +++ b/cmd/gost/route.go @@ -163,10 +163,9 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) { case "http2": tr = gost.HTTP2Transporter(tlsCfg) case "h2": - tr = gost.H2Transporter(tlsCfg) + tr = gost.H2Transporter(tlsCfg, node.Get("path")) case "h2c": - tr = gost.H2CTransporter() - + tr = gost.H2CTransporter(node.Get("path")) case "obfs4": tr = gost.Obfs4Transporter() case "ohttp": @@ -348,9 +347,9 @@ func (r *route) GenRouters() ([]router, error) { case "http2": ln, err = gost.HTTP2Listener(node.Addr, tlsCfg) case "h2": - ln, err = gost.H2Listener(node.Addr, tlsCfg) + ln, err = gost.H2Listener(node.Addr, tlsCfg, node.Get("path")) case "h2c": - ln, err = gost.H2CListener(node.Addr) + ln, err = gost.H2CListener(node.Addr, node.Get("path")) case "tcp": // Directly use SSH port forwarding if the last chain node is forward+ssh if chain.LastNode().Protocol == "forward" && chain.LastNode().Transport == "ssh" { diff --git a/http2.go b/http2.go index af9b2e8..49174dd 100644 --- a/http2.go +++ b/http2.go @@ -180,27 +180,31 @@ func (tr *http2Transporter) Multiplex() bool { return true } +// TODO: clean closed clients type h2Transporter struct { clients map[string]*http.Client clientMutex sync.Mutex tlsConfig *tls.Config + path string } // H2Transporter creates a Transporter that is used by HTTP2 h2 tunnel client. -func H2Transporter(config *tls.Config) Transporter { +func H2Transporter(config *tls.Config, path string) Transporter { if config == nil { config = &tls.Config{InsecureSkipVerify: true} } return &h2Transporter{ clients: make(map[string]*http.Client), tlsConfig: config, + path: path, } } // H2CTransporter creates a Transporter that is used by HTTP2 h2c tunnel client. -func H2CTransporter() Transporter { +func H2CTransporter(path string) Transporter { return &h2Transporter{ clients: make(map[string]*http.Client), + path: path, } } @@ -251,6 +255,11 @@ func (tr *h2Transporter) Dial(addr string, options ...DialOption) (net.Conn, err Host: addr, ContentLength: -1, } + if tr.path != "" { + req.Method = http.MethodGet + req.URL.Path = tr.path + } + if Debug { dump, _ := httputil.DumpRequest(req, false) log.Log("[http2]", string(dump)) @@ -650,12 +659,13 @@ type h2Listener struct { net.Listener server *http2.Server tlsConfig *tls.Config + path string connChan chan net.Conn errChan chan error } // H2Listener creates a Listener for HTTP2 h2 tunnel server. -func H2Listener(addr string, config *tls.Config) (Listener, error) { +func H2Listener(addr string, config *tls.Config, path string) (Listener, error) { ln, err := net.Listen("tcp", addr) if err != nil { return nil, err @@ -672,6 +682,7 @@ func H2Listener(addr string, config *tls.Config) (Listener, error) { IdleTimeout: 5 * time.Minute, }, tlsConfig: config, + path: path, connChan: make(chan net.Conn, 1024), errChan: make(chan error, 1), } @@ -681,7 +692,7 @@ func H2Listener(addr string, config *tls.Config) (Listener, error) { } // H2CListener creates a Listener for HTTP2 h2c tunnel server. -func H2CListener(addr string) (Listener, error) { +func H2CListener(addr string, path string) (Listener, error) { ln, err := net.Listen("tcp", addr) if err != nil { return nil, err @@ -691,6 +702,7 @@ func H2CListener(addr string) (Listener, error) { server: &http2.Server{ // MaxConcurrentStreams: 1000, }, + path: path, connChan: make(chan net.Conn, 1024), errChan: make(chan error, 1), } @@ -733,7 +745,8 @@ func (l *h2Listener) handleLoop(conn net.Conn) { } func (l *h2Listener) handleFunc(w http.ResponseWriter, r *http.Request) { - log.Logf("[http2] %s %s - %s %s", r.Method, r.RemoteAddr, r.Host, r.Proto) + log.Logf("[http2] %s -> %s %s %s %s", + r.RemoteAddr, r.Host, r.Method, r.RequestURI, r.Proto) if Debug { dump, _ := httputil.DumpRequest(r, false) log.Log("[http2]", string(dump)) @@ -741,7 +754,8 @@ func (l *h2Listener) handleFunc(w http.ResponseWriter, r *http.Request) { w.Header().Set("Proxy-Agent", "gost/"+Version) conn, err := l.upgrade(w, r) if err != nil { - log.Logf("[http2] %s %s - %s %s", r.Method, r.RemoteAddr, r.Host, r.Proto) + log.Logf("[http2] %s - %s %s %s %s: %s", + r.RemoteAddr, r.Host, r.Method, r.RequestURI, r.Proto, err) return } select { @@ -755,10 +769,16 @@ func (l *h2Listener) handleFunc(w http.ResponseWriter, r *http.Request) { } func (l *h2Listener) upgrade(w http.ResponseWriter, r *http.Request) (*http2Conn, error) { - if r.Method != http.MethodConnect { + if l.path == "" && r.Method != http.MethodConnect { w.WriteHeader(http.StatusMethodNotAllowed) - return nil, errors.New("Method not allowed") + return nil, errors.New("method not allowed") } + + if l.path != "" && r.RequestURI != l.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