|
@@ -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
|
|
|
+}
|