diff --git a/conn.go b/conn.go index c49f5be..7454af6 100644 --- a/conn.go +++ b/conn.go @@ -51,11 +51,13 @@ func listenAndServe(arg Args) error { case "wss": // websocket security connection return NewWs(arg).listenAndServeTLS() case "tls": // tls connection - if arg.Protocol == "http2" || arg.Protocol == "h2" { // only support http2 over TLS - return listenAndServeHttp2(arg) + 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)) case "tcp": // Local TCP port forwarding return listenAndServeTcpForward(arg) case "udp": // Local UDP port forwarding @@ -87,14 +89,12 @@ func listenAndServe(arg Args) error { } } -func listenAndServeHttp2(arg Args) error { +func listenAndServeHttp2(arg Args, handler http.Handler) error { srv := http.Server{ Addr: arg.Addr, - Handler: http.HandlerFunc(handlerHttp2Request), + Handler: handler, TLSConfig: &tls.Config{ Certificates: []tls.Certificate{arg.Cert}, - //MinVersion: tls.VersionTLS12, - //PreferServerCipherSuites: true, }, } http2.ConfigureServer(&srv, nil) diff --git a/http.go b/http.go index 2f9651c..1f5151e 100644 --- a/http.go +++ b/http.go @@ -13,12 +13,8 @@ func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) { glog.V(LINFO).Infof("[http] %s - %s", conn.RemoteAddr(), req.Host) if glog.V(LDEBUG) { - dump, err := httputil.DumpRequest(req, false) - if err != nil { - glog.Infoln(err) - } else { - glog.Infoln(string(dump)) - } + dump, _ := httputil.DumpRequest(req, false) + glog.Infoln(string(dump)) } var username, password string diff --git a/http2.go b/http2.go index 994063c..142dd2c 100644 --- a/http2.go +++ b/http2.go @@ -5,78 +5,67 @@ import ( "github.com/golang/glog" "golang.org/x/net/http2" "io" - "net" + //"net" "net/http" "net/http/httputil" ) func init() { - http2.VerboseLogs = true + if glog.V(LDEBUG) { + http2.VerboseLogs = true + } } func handlerHttp2Request(w http.ResponseWriter, req *http.Request) { glog.V(LINFO).Infof("[http2] %s - %s", req.RemoteAddr, req.Host) - if glog.V(LDEBUG) { - dump, err := httputil.DumpRequest(req, false) - if err != nil { - glog.Infoln(err) - } else { - glog.Infoln(string(dump)) - } + dump, _ := httputil.DumpRequest(req, false) + glog.Infoln(string(dump)) } - var c net.Conn - var err error - - fw := flushWriter{w} - - c, err = Connect(req.Host) + c, err := Connect(req.Host) if err != nil { glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, req.Host, err) - b := []byte("HTTP/1.1 503 Service unavailable\r\n" + - "Proxy-Agent: gost/" + Version + "\r\n\r\n") - glog.V(LDEBUG).Infof("[http2] %s <- %s\n%s", req.RemoteAddr, req.Host, string(b)) - //w.WriteHeader(http.StatusServiceUnavailable) - fw.Write(b) + w.Header().Set("Proxy-Agent", "gost/"+Version) + w.WriteHeader(http.StatusServiceUnavailable) + if fw, ok := w.(http.Flusher); ok { + fw.Flush() + } return } defer c.Close() - rChan := make(chan error, 1) - wChan := make(chan error, 1) + glog.V(LINFO).Infof("[http2] %s <-> %s", req.RemoteAddr, req.Host) + errc := make(chan error, 2) if req.Method == http.MethodConnect { w.Header().Set("Proxy-Agent", "gost/"+Version) w.WriteHeader(http.StatusOK) - if fw, ok := w.(http.Flusher); ok { fw.Flush() } // compatible with HTTP 1.x if hj, ok := w.(http.Hijacker); ok && req.ProtoMajor == 1 { + // we take over the underly connection conn, _, err := hj.Hijack() if err != nil { - glog.V(LWARNING).Infoln(err) + glog.V(LWARNING).Infof("[http2] %s -> %s : %s", req.RemoteAddr, req.Host, err) return } defer conn.Close() - go Pipe(conn, c, rChan) - go Pipe(c, conn, wChan) + go Pipe(conn, c, errc) + go Pipe(c, conn, errc) } else { - go Pipe(req.Body, c, rChan) - go Pipe(c, fw, wChan) + go Pipe(req.Body, c, errc) + go Pipe(c, flushWriter{w}, errc) } select { - case err := <-rChan: - glog.V(LWARNING).Infoln("r exit", err) - case err := <-wChan: - glog.V(LWARNING).Infoln("w exit", err) + case <-errc: + // glog.V(LWARNING).Infoln("exit", err) } - } else { req.Header.Set("Connection", "Keep-Alive") if err = req.Write(c); err != nil { @@ -96,16 +85,22 @@ func handlerHttp2Request(w http.ResponseWriter, req *http.Request) { w.Header().Add(k, vv) } } - w.WriteHeader(resp.StatusCode) - if _, err := io.Copy(fw, resp.Body); err != nil { - glog.V(LWARNING).Infoln(err) + + 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(LINFO).Infof("[http2] %s <-> %s", req.RemoteAddr, req.Host) + glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, req.Host) +} - //glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, req.Host) +func handleHttp2Transport(w http.ResponseWriter, req *http.Request) { + glog.V(LINFO).Infof("[http2] %s - %s", req.RemoteAddr, req.Host) + if glog.V(LDEBUG) { + dump, _ := httputil.DumpRequest(req, false) + glog.Infoln(string(dump)) + } } type flushWriter struct { diff --git a/util.go b/util.go index 4773fba..c9448d1 100644 --- a/util.go +++ b/util.go @@ -74,7 +74,7 @@ func parseArgs(ss []string) (args []Args) { } switch arg.Protocol { - case "http", "http2", "h2", "socks", "socks5", "ss": + case "http", "http2", "socks", "socks5", "ss": case "https": arg.Protocol = "http" arg.Transport = "tls" @@ -87,6 +87,8 @@ func parseArgs(ss []string) (args []Args) { case "https": arg.Protocol = "http" arg.Transport = "tls" + case "http2": + arg.Protocol = "" 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