diff --git a/conn.go b/conn.go index b318327..281792f 100644 --- a/conn.go +++ b/conn.go @@ -89,11 +89,12 @@ func listenAndServe(arg Args) error { func listenAndServeHttp2(arg Args) error { srv := http.Server{ - Addr: arg.Addr, - Handler: http.HandlerFunc(handlerHttp2Request), + Addr: arg.Addr, + Handler: http.HandlerFunc(handlerHttp2Request), + TLSConfig: &tls.Config{Certificates: []tls.Certificate{arg.Cert}}, } http2.ConfigureServer(&srv, nil) - return srv.ListenAndServeTLS(certFile, keyFile) + return srv.ListenAndServeTLS("", "") } func listenAndServeTcpForward(arg Args) error { diff --git a/http.go b/http.go index 3541cf5..2f9651c 100644 --- a/http.go +++ b/http.go @@ -88,7 +88,7 @@ func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) { } defer c.Close() - if req.Method == "CONNECT" { + if req.Method == http.MethodConnect { b := []byte("HTTP/1.1 200 Connection established\r\n" + "Proxy-Agent: gost/" + Version + "\r\n\r\n") glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b)) diff --git a/http2.go b/http2.go index 77b58e4..63cefff 100644 --- a/http2.go +++ b/http2.go @@ -2,15 +2,21 @@ package main import ( "github.com/golang/glog" + "golang.org/x/net/http2" + "net" "net/http" "net/http/httputil" ) -func handlerHttp2Request(w http.ResponseWriter, r *http.Request) { - glog.V(LINFO).Infof("[http2] %s - %s", r.RemoteAddr, r.Host) +func init() { + 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(r, false) + dump, err := httputil.DumpRequest(req, false) if err != nil { glog.Infoln(err) } else { @@ -18,4 +24,50 @@ func handlerHttp2Request(w http.ResponseWriter, r *http.Request) { } } + var c net.Conn + var err error + + 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) + w.Write(b) + return + } + defer c.Close() + + if req.Method == http.MethodConnect { + b := []byte("HTTP/2.0 200 Connection established\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.Write(b) + if f, ok := w.(http.Flusher); ok { + f.Flush() + } + } 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) + return + } + } + + glog.V(LINFO).Infof("[http2] %s <-> %s", req.RemoteAddr, req.Host) + + rChan := make(chan error, 1) + wChan := make(chan error, 1) + go Pipe(c, w, wChan) + go Pipe(req.Body, c, rChan) + + select { + case err = <-wChan: + glog.V(LWARNING).Infoln("w exit", err) + case err = <-rChan: + glog.V(LWARNING).Infoln("r exit", err) + } + + glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, req.Host) } diff --git a/main.go b/main.go index 5f6e990..70253b3 100644 --- a/main.go +++ b/main.go @@ -19,7 +19,7 @@ const ( ) const ( - Version = "2.1-rc1" + Version = "2.2-dev-http2" ) var ( diff --git a/util.go b/util.go index 27f3d9f..4773fba 100644 --- a/util.go +++ b/util.go @@ -74,7 +74,7 @@ func parseArgs(ss []string) (args []Args) { } switch arg.Protocol { - case "http", "socks", "socks5", "ss": + case "http", "http2", "h2", "socks", "socks5", "ss": case "https": arg.Protocol = "http" arg.Transport = "tls" diff --git a/utils/http2_client.go b/utils/http2_client.go new file mode 100644 index 0000000..cadf486 --- /dev/null +++ b/utils/http2_client.go @@ -0,0 +1,63 @@ +// +build http2client + +package main + +import ( + "crypto/tls" + "golang.org/x/net/http2" + "io" + "io/ioutil" + "log" + "net" + "net/http" + //"net/http/httputil" + "os" + "time" +) + +func init() { + log.SetFlags(log.LstdFlags | log.Lshortfile) +} + +func main() { + tr := http2.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) { + return tls.DialWithDialer(&net.Dialer{Timeout: 30 * time.Second}, "tcp", "localhost:8080", cfg) + }, + } + client := http.Client{Transport: &tr} + + pr, pw := io.Pipe() + + req, err := http.NewRequest("CONNECT", "https://www.baidu.com", ioutil.NopCloser(pr)) + req.ContentLength = -1 + if err != nil { + log.Fatal(err) + } + /* + req := &http.Request{ + Method: "CONNECT", + URL: &url.URL{Scheme: "https"}, + Host: "www.baidu.com:443", + Header: make(http.Header), + } + */ + + resp, err := client.Do(req) + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + + r, err := http.NewRequest("GET", "https://www.baidu.com", nil) + if err != nil { + log.Fatal(err) + } + r.Write(pw) + + n, err := io.Copy(os.Stdout, resp.Body) + log.Fatalf("copied %d, %v", n, err) +}