From a5e06c666694a17e101faf9bfe20c14e8ff0aa27 Mon Sep 17 00:00:00 2001 From: "rui.zheng" Date: Thu, 29 Sep 2016 20:46:49 +0800 Subject: [PATCH] fix http2 --- conn.go | 90 ++++++++++++++++++++++++++++++------------------------ forward.go | 2 +- http.go | 31 +++++++++++-------- main.go | 3 +- socks.go | 2 +- ss.go | 2 +- util.go | 11 ++----- 7 files changed, 76 insertions(+), 65 deletions(-) diff --git a/conn.go b/conn.go index a9e9be0..883146e 100644 --- a/conn.go +++ b/conn.go @@ -52,13 +52,10 @@ func listenAndServe(arg Args) error { case "wss": // websocket security connection return NewWs(arg).listenAndServeTLS() case "tls": // tls connection - if arg.Protocol == "http2" { // only support http2 over TLS - return listenAndServeHttp2(arg, http.HandlerFunc(handlerHttp2Request)) - } ln, err = tls.Listen("tcp", arg.Addr, &tls.Config{Certificates: []tls.Certificate{arg.Cert}}) case "http2": // http2 connetction - return listenAndServeHttp2(arg, http.HandlerFunc(handleHttp2Transport)) + return listenAndServeHttp2(arg, http.HandlerFunc(handlerHttp2Request)) case "tcp": // Local TCP port forwarding return listenAndServeTcpForward(arg) case "udp": // Local UDP port forwarding @@ -378,45 +375,15 @@ func (r *reqReader) Read(p []byte) (n int, err error) { return } -func Connect(addr string) (conn net.Conn, err error) { - if len(forwardArgs) > 0 { - last := forwardArgs[len(forwardArgs)-1] - if http2Client != nil && last.Protocol == "http2" { - return connectHttp2(http2Client, addr) - } - } - return connectWithChain(addr, forwardArgs...) -} - -func connectHttp2(client *http.Client, host string) (net.Conn, error) { - pr, pw := io.Pipe() - u := url.URL{Scheme: "https", Host: host} - req, err := http.NewRequest(http.MethodConnect, u.String(), ioutil.NopCloser(pr)) - if err != nil { - return nil, err - } - req.ContentLength = -1 - if glog.V(LDEBUG) { - dump, _ := httputil.DumpRequest(req, false) - glog.Infoln(string(dump)) - } - resp, err := http2Client.Do(req) - if err != nil { - return nil, err - } - if resp.StatusCode != http.StatusOK { - resp.Body.Close() - return nil, errors.New(resp.Status) - } - conn := &Http2ClientConn{r: resp.Body, w: pw} - conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", host) - return conn, nil -} - -func connectWithChain(addr string, chain ...Args) (conn net.Conn, err error) { +func connect(addr string, prot string, chain []Args) (conn net.Conn, err error) { if !strings.Contains(addr, ":") { addr += ":80" } + + if enabled, h2host := http2Enabled(); enabled { + return connectHttp2(http2Client, h2host, addr, prot) + } + if len(chain) == 0 { return net.DialTimeout("tcp", addr, time.Second*90) } @@ -433,6 +400,49 @@ func connectWithChain(addr string, chain ...Args) (conn net.Conn, err error) { return conn, nil } +func http2Enabled() (enabled bool, host string) { + length := len(forwardArgs) + if http2Client == nil || length == 0 || forwardArgs[length-1].Transport != "http2" { + return + } + return true, forwardArgs[length-1].Addr +} + +func connectHttp2(client *http.Client, host, target string, prot string) (net.Conn, error) { + pr, pw := io.Pipe() + req := http.Request{ + Method: http.MethodConnect, + URL: &url.URL{Scheme: "https", Host: host}, + Header: make(http.Header), + Proto: "HTTP/2.0", + ProtoMajor: 2, + ProtoMinor: 0, + Body: ioutil.NopCloser(pr), + Host: host, + ContentLength: -1, + } + req.Header.Set("gost-target-addr", target) + if prot != "" { + req.Header.Set("gost-prot", prot) + } + + if glog.V(LDEBUG) { + dump, _ := httputil.DumpRequest(&req, false) + glog.Infoln(string(dump)) + } + resp, err := client.Do(&req) + if err != nil { + return nil, err + } + if resp.StatusCode != http.StatusOK { + resp.Body.Close() + return nil, errors.New(resp.Status) + } + conn := &Http2ClientConn{r: resp.Body, w: pw} + conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", target) + return conn, nil +} + // establish connection throughout the forward chain func forwardChain(chain ...Args) (conn net.Conn, end Args, err error) { defer func() { diff --git a/forward.go b/forward.go index 863795c..679c1ca 100644 --- a/forward.go +++ b/forward.go @@ -13,7 +13,7 @@ func handleTcpForward(conn net.Conn, raddr net.Addr) { defer conn.Close() glog.V(LINFO).Infof("[tcp] %s - %s", conn.RemoteAddr(), raddr) - c, err := Connect(raddr.String()) + c, err := connect(raddr.String(), "") if err != nil { glog.V(LWARNING).Infof("[tcp] %s -> %s : %s", conn.RemoteAddr(), raddr, err) return diff --git a/http.go b/http.go index 14fa328..9bb1abb 100644 --- a/http.go +++ b/http.go @@ -19,7 +19,7 @@ var ( ) func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) { - glog.V(LINFO).Infof("[http] %s - %s", conn.RemoteAddr(), req.Host) + glog.V(LINFO).Infof("[http] %s %s - %s %s", req.Method, conn.RemoteAddr(), req.Host, req.Proto) if glog.V(LDEBUG) { dump, _ := httputil.DumpRequest(req, false) @@ -55,7 +55,8 @@ func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) { return } } - c, err := Connect(req.Host) + + c, err := connect(req.Host, "http") if err != nil { glog.V(LWARNING).Infof("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err) @@ -160,7 +161,6 @@ func (c *Http2ClientConn) SetWriteDeadline(t time.Time) error { // init http2 client with target http2 proxy server addr, and forward chain chain func initHttp2Client(host string, chain ...Args) { - glog.V(LINFO).Infoln("init http2 client") tr := http2.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, @@ -178,15 +178,20 @@ func initHttp2Client(host string, chain ...Args) { } func handlerHttp2Request(w http.ResponseWriter, req *http.Request) { - glog.V(LINFO).Infof("[http2] %s - %s", req.RemoteAddr, req.Host) + target := req.Header.Get("gost-target-addr") + if target == "" { + target = req.Host + } + + glog.V(LINFO).Infof("[http2] %s %s - %s %s", req.Method, req.RemoteAddr, target, req.Proto) if glog.V(LDEBUG) { dump, _ := httputil.DumpRequest(req, false) glog.Infoln(string(dump)) } - c, err := Connect(req.Host) + c, err := connect(target, req.Header.Get("gost-prot")) if err != nil { - glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, req.Host, err) + glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err) w.Header().Set("Proxy-Agent", "gost/"+Version) w.WriteHeader(http.StatusServiceUnavailable) if fw, ok := w.(http.Flusher); ok { @@ -196,7 +201,7 @@ func handlerHttp2Request(w http.ResponseWriter, req *http.Request) { } defer c.Close() - glog.V(LINFO).Infof("[http2] %s <-> %s", req.RemoteAddr, req.Host) + glog.V(LINFO).Infof("[http2] %s <-> %s", req.RemoteAddr, target) errc := make(chan error, 2) if req.Method == http.MethodConnect { @@ -211,7 +216,7 @@ func handlerHttp2Request(w http.ResponseWriter, req *http.Request) { // we take over the underly connection conn, _, err := hj.Hijack() if err != nil { - glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, req.Host, err) + glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err) return } defer conn.Close() @@ -230,7 +235,7 @@ func handlerHttp2Request(w http.ResponseWriter, req *http.Request) { } else { req.Header.Set("Connection", "Keep-Alive") if err = req.Write(c); err != nil { - glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, req.Host, err) + glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, target, err) return } @@ -250,15 +255,16 @@ func handlerHttp2Request(w http.ResponseWriter, req *http.Request) { if fw, ok := w.(http.Flusher); ok { fw.Flush() } - if _, err := io.Copy(flushWriter{w}, resp.Body); err != nil { - glog.V(LWARNING).Infof("[http2] %s <- %s : %s", req.RemoteAddr, req.Host, err) + glog.V(LWARNING).Infof("[http2] %s <- %s : %s", req.RemoteAddr, target, err) } } - glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, req.Host) + glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, target) } +//func processSocks5OverHttp2() + func handleHttp2Transport(w http.ResponseWriter, req *http.Request) { glog.V(LINFO).Infof("[http2] %s - %s", req.RemoteAddr, req.Host) if glog.V(LDEBUG) { @@ -296,7 +302,6 @@ func (fw flushWriter) Write(p []byte) (n int, err error) { n, err = fw.w.Write(p) if err != nil { glog.V(LWARNING).Infoln("flush writer:", err) - return } if f, ok := fw.w.(http.Flusher); ok { f.Flush() diff --git a/main.go b/main.go index 2b768c6..0834db5 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,7 @@ const ( LWARNING LINFO LDEBUG + LVDEBUG // verbose debug ) const ( @@ -37,7 +38,7 @@ func init() { flag.BoolVar(&pv, "V", false, "print version") flag.Parse() - if glog.V(LDEBUG) { + if glog.V(LVDEBUG) { http2.VerboseLogs = true } } diff --git a/socks.go b/socks.go index 3dab9e4..1551f01 100644 --- a/socks.go +++ b/socks.go @@ -170,7 +170,7 @@ func handleSocks5Request(req *gosocks5.Request, conn net.Conn) { case gosocks5.CmdConnect: glog.V(LINFO).Infof("[socks5-connect] %s - %s", conn.RemoteAddr(), req.Addr) - tconn, err := Connect(req.Addr.String()) + tconn, err := connect(req.Addr.String(), "socks5") if err != nil { glog.V(LWARNING).Infof("[socks5-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil) diff --git a/ss.go b/ss.go index be22f7a..0182303 100644 --- a/ss.go +++ b/ss.go @@ -31,7 +31,7 @@ func handleShadow(conn net.Conn, arg Args) { } glog.V(LINFO).Infof("[ss] %s -> %s", conn.RemoteAddr(), addr.String()) - sconn, err := Connect(addr.String()) + sconn, err := connect(addr.String(), "ss") if err != nil { glog.V(LWARNING).Infof("[ss] %s -> %s : %s", conn.RemoteAddr(), addr.String(), err) return diff --git a/util.go b/util.go index 343f768..023ca37 100644 --- a/util.go +++ b/util.go @@ -79,7 +79,7 @@ func parseArgs(ss []string) (args []Args) { arg.Protocol = "http" arg.Transport = "tls" case "http2": - arg.Protocol = "" + arg.Protocol = "http2" case "tcp", "udp": // started from v2.1, tcp and udp are for local port forwarding arg.Remote = strings.Trim(u.EscapedPath(), "/") case "rtcp", "rudp": // started from v2.1, rtcp and rudp are for remote port forwarding @@ -89,9 +89,7 @@ func parseArgs(ss []string) (args []Args) { } switch arg.Protocol { - case "http", "socks", "socks5", "ss": - case "http2": - arg.Transport = "tls" // standard http2 proxy, only support http2 over tls + case "http", "http2", "socks", "socks5", "ss": default: arg.Protocol = "" } @@ -112,14 +110,11 @@ func processForwardChain(chain ...Args) { // http2 restrict: only last proxy can enable http2 for i, _ := range c { - if c[i].Protocol == "http2" { - c[i].Protocol = "http" - } if c[i].Transport == "http2" { c[i].Transport = "" } } - if last.Protocol == "http2" || last.Transport == "http2" { + if last.Transport == "http2" { initHttp2Client(last.Addr, c...) } }