|
|
|
@ -1,8 +1,10 @@ |
|
|
|
package main |
|
|
|
|
|
|
|
import ( |
|
|
|
"bufio" |
|
|
|
"github.com/golang/glog" |
|
|
|
"golang.org/x/net/http2" |
|
|
|
"io" |
|
|
|
"net" |
|
|
|
"net/http" |
|
|
|
"net/http/httputil" |
|
|
|
@ -27,6 +29,8 @@ func handlerHttp2Request(w http.ResponseWriter, req *http.Request) { |
|
|
|
var c net.Conn |
|
|
|
var err error |
|
|
|
|
|
|
|
fw := flushWriter{w} |
|
|
|
|
|
|
|
c, err = Connect(req.Host) |
|
|
|
if err != nil { |
|
|
|
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") |
|
|
|
glog.V(LDEBUG).Infof("[http2] %s <- %s\n%s", req.RemoteAddr, req.Host, string(b)) |
|
|
|
//w.WriteHeader(http.StatusServiceUnavailable)
|
|
|
|
w.Write(b) |
|
|
|
fw.Write(b) |
|
|
|
return |
|
|
|
} |
|
|
|
defer c.Close() |
|
|
|
|
|
|
|
rChan := make(chan error, 1) |
|
|
|
wChan := make(chan error, 1) |
|
|
|
|
|
|
|
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() |
|
|
|
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 { |
|
|
|
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 { |
|
|
|
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) |
|
|
|
resp, err := http.ReadResponse(bufio.NewReader(c), req) |
|
|
|
if err != nil { |
|
|
|
glog.V(LWARNING).Infoln(err) |
|
|
|
return |
|
|
|
} |
|
|
|
defer resp.Body.Close() |
|
|
|
|
|
|
|
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) |
|
|
|
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)
|
|
|
|
|
|
|
|
//glog.V(LINFO).Infof("[http2] %s >-< %s", req.RemoteAddr, req.Host)
|
|
|
|
} |
|
|
|
|
|
|
|
type flushWriter struct { |
|
|
|
w io.Writer |
|
|
|
} |
|
|
|
|
|
|
|
func (fw flushWriter) Write(p []byte) (n int, err error) { |
|
|
|
n, err = fw.w.Write(p) |
|
|
|
if f, ok := fw.w.(http.Flusher); ok { |
|
|
|
f.Flush() |
|
|
|
} |
|
|
|
return |
|
|
|
} |
|
|
|
|