http2 support with http1.x compatible
This commit is contained in:
parent
8502dd2905
commit
d3e52bb457
10
conn.go
10
conn.go
@ -89,9 +89,13 @@ func listenAndServe(arg Args) error {
|
|||||||
|
|
||||||
func listenAndServeHttp2(arg Args) error {
|
func listenAndServeHttp2(arg Args) error {
|
||||||
srv := http.Server{
|
srv := http.Server{
|
||||||
Addr: arg.Addr,
|
Addr: arg.Addr,
|
||||||
Handler: http.HandlerFunc(handlerHttp2Request),
|
Handler: http.HandlerFunc(handlerHttp2Request),
|
||||||
TLSConfig: &tls.Config{Certificates: []tls.Certificate{arg.Cert}},
|
TLSConfig: &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{arg.Cert},
|
||||||
|
//MinVersion: tls.VersionTLS12,
|
||||||
|
//PreferServerCipherSuites: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
http2.ConfigureServer(&srv, nil)
|
http2.ConfigureServer(&srv, nil)
|
||||||
return srv.ListenAndServeTLS("", "")
|
return srv.ListenAndServeTLS("", "")
|
||||||
|
90
http2.go
90
http2.go
@ -1,8 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
@ -27,6 +29,8 @@ func handlerHttp2Request(w http.ResponseWriter, req *http.Request) {
|
|||||||
var c net.Conn
|
var c net.Conn
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
fw := flushWriter{w}
|
||||||
|
|
||||||
c, err = Connect(req.Host)
|
c, err = Connect(req.Host)
|
||||||
if err != nil {
|
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, req.Host, err)
|
||||||
@ -34,40 +38,84 @@ func handlerHttp2Request(w http.ResponseWriter, req *http.Request) {
|
|||||||
"Proxy-Agent: gost/" + Version + "\r\n\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))
|
glog.V(LDEBUG).Infof("[http2] %s <- %s\n%s", req.RemoteAddr, req.Host, string(b))
|
||||||
//w.WriteHeader(http.StatusServiceUnavailable)
|
//w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
w.Write(b)
|
fw.Write(b)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
|
rChan := make(chan error, 1)
|
||||||
|
wChan := make(chan error, 1)
|
||||||
|
|
||||||
if req.Method == http.MethodConnect {
|
if req.Method == http.MethodConnect {
|
||||||
b := []byte("HTTP/2.0 200 Connection established\r\n" +
|
w.Header().Set("Proxy-Agent", "gost/"+Version)
|
||||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
w.WriteHeader(http.StatusOK)
|
||||||
glog.V(LDEBUG).Infof("[http2] %s <- %s\n%s", req.RemoteAddr, req.Host, string(b))
|
|
||||||
w.Write(b)
|
if fw, ok := w.(http.Flusher); ok {
|
||||||
if f, ok := w.(http.Flusher); ok {
|
fw.Flush()
|
||||||
f.Flush()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// compatible with HTTP 1.x
|
||||||
|
if hj, ok := w.(http.Hijacker); ok && req.ProtoMajor == 1 {
|
||||||
|
conn, _, err := hj.Hijack()
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infoln(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
go Pipe(conn, c, rChan)
|
||||||
|
go Pipe(c, conn, wChan)
|
||||||
|
} else {
|
||||||
|
go Pipe(req.Body, c, rChan)
|
||||||
|
go Pipe(c, fw, wChan)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-rChan:
|
||||||
|
glog.V(LWARNING).Infoln("r exit", err)
|
||||||
|
case err := <-wChan:
|
||||||
|
glog.V(LWARNING).Infoln("w exit", err)
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
req.Header.Set("Connection", "Keep-Alive")
|
req.Header.Set("Connection", "Keep-Alive")
|
||||||
if err = req.Write(c); err != nil {
|
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, req.Host, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(c), req)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(LWARNING).Infoln(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
for k, v := range resp.Header {
|
||||||
|
for _, vv := range v {
|
||||||
|
w.Header().Add(k, vv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(resp.StatusCode)
|
||||||
|
if _, err := io.Copy(fw, resp.Body); err != nil {
|
||||||
|
glog.V(LWARNING).Infoln(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
glog.V(LINFO).Infof("[http2] %s <-> %s", req.RemoteAddr, req.Host)
|
//glog.V(LINFO).Infof("[http2] %s <-> %s", req.RemoteAddr, req.Host)
|
||||||
|
|
||||||
rChan := make(chan error, 1)
|
//glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, req.Host)
|
||||||
wChan := make(chan error, 1)
|
}
|
||||||
go Pipe(c, w, wChan)
|
|
||||||
go Pipe(req.Body, c, rChan)
|
type flushWriter struct {
|
||||||
|
w io.Writer
|
||||||
select {
|
}
|
||||||
case err = <-wChan:
|
|
||||||
glog.V(LWARNING).Infoln("w exit", err)
|
func (fw flushWriter) Write(p []byte) (n int, err error) {
|
||||||
case err = <-rChan:
|
n, err = fw.w.Write(p)
|
||||||
glog.V(LWARNING).Infoln("r exit", err)
|
if f, ok := fw.w.(http.Flusher); ok {
|
||||||
}
|
f.Flush()
|
||||||
|
}
|
||||||
glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, req.Host)
|
return
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user