Browse Source

http_proxy: fix error using encryption or compression

fatedier 7 years ago
parent
commit
2a044c9d6d
1 changed files with 62 additions and 6 deletions
  1. 62 6
      models/plugin/http_proxy.go

+ 62 - 6
models/plugin/http_proxy.go

@@ -15,6 +15,7 @@
 package plugin
 
 import (
+	"bufio"
 	"encoding/base64"
 	"fmt"
 	"io"
@@ -106,14 +107,26 @@ func (hp *HttpProxy) Name() string {
 }
 
 func (hp *HttpProxy) Handle(conn io.ReadWriteCloser) {
-	var wrapConn net.Conn
-	if realConn, ok := conn.(net.Conn); ok {
+	var wrapConn frpNet.Conn
+	if realConn, ok := conn.(frpNet.Conn); ok {
 		wrapConn = realConn
 	} else {
 		wrapConn = frpNet.WrapReadWriteCloserToConn(conn)
 	}
 
-	hp.l.PutConn(wrapConn)
+	sc, rd := frpNet.NewShareConn(wrapConn)
+	request, err := http.ReadRequest(bufio.NewReader(rd))
+	if err != nil {
+		wrapConn.Close()
+		return
+	}
+
+	if request.Method == http.MethodConnect {
+		hp.handleConnectReq(request, frpIo.WrapReadWriteCloser(rd, wrapConn, nil))
+		return
+	}
+
+	hp.l.PutConn(sc)
 	return
 }
 
@@ -124,13 +137,15 @@ func (hp *HttpProxy) Close() error {
 }
 
 func (hp *HttpProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
-	if ok := hp.Auth(rw, req); !ok {
+	if ok := hp.Auth(req); !ok {
 		rw.Header().Set("Proxy-Authenticate", "Basic")
 		rw.WriteHeader(http.StatusProxyAuthRequired)
 		return
 	}
 
-	if req.Method == "CONNECT" {
+	if req.Method == http.MethodConnect {
+		// deprecated
+		// Connect request is handled in Handle function.
 		hp.ConnectHandler(rw, req)
 	} else {
 		hp.HttpHandler(rw, req)
@@ -156,6 +171,9 @@ func (hp *HttpProxy) HttpHandler(rw http.ResponseWriter, req *http.Request) {
 	}
 }
 
+// deprecated
+// Hijack needs to SetReadDeadline on the Conn of the request, but if we use stream compression here,
+// we may always get i/o timeout error.
 func (hp *HttpProxy) ConnectHandler(rw http.ResponseWriter, req *http.Request) {
 	hj, ok := rw.(http.Hijacker)
 	if !ok {
@@ -180,7 +198,7 @@ func (hp *HttpProxy) ConnectHandler(rw http.ResponseWriter, req *http.Request) {
 	go frpIo.Join(remote, client)
 }
 
-func (hp *HttpProxy) Auth(rw http.ResponseWriter, req *http.Request) bool {
+func (hp *HttpProxy) Auth(req *http.Request) bool {
 	if hp.AuthUser == "" && hp.AuthPasswd == "" {
 		return true
 	}
@@ -206,6 +224,30 @@ func (hp *HttpProxy) Auth(rw http.ResponseWriter, req *http.Request) bool {
 	return true
 }
 
+func (hp *HttpProxy) handleConnectReq(req *http.Request, rwc io.ReadWriteCloser) {
+	defer rwc.Close()
+	if ok := hp.Auth(req); !ok {
+		res := getBadResponse()
+		res.Write(rwc)
+		return
+	}
+
+	remote, err := net.Dial("tcp", req.URL.Host)
+	if err != nil {
+		res := &http.Response{
+			StatusCode: 400,
+			Proto:      "HTTP/1.1",
+			ProtoMajor: 1,
+			ProtoMinor: 1,
+		}
+		res.Write(rwc)
+		return
+	}
+	rwc.Write([]byte("HTTP/1.1 200 OK\r\n\r\n"))
+
+	frpIo.Join(remote, rwc)
+}
+
 func copyHeaders(dst, src http.Header) {
 	for key, values := range src {
 		for _, value := range values {
@@ -225,3 +267,17 @@ func removeProxyHeaders(req *http.Request) {
 	req.Header.Del("Transfer-Encoding")
 	req.Header.Del("Upgrade")
 }
+
+func getBadResponse() *http.Response {
+	header := make(map[string][]string)
+	header["Proxy-Authenticate"] = []string{"Basic"}
+	res := &http.Response{
+		Status:     "407 Not authorized",
+		StatusCode: 407,
+		Proto:      "HTTP/1.1",
+		ProtoMajor: 1,
+		ProtoMinor: 1,
+		Header:     header,
+	}
+	return res
+}