From 47a1a477f360d019027cd958701e4dfc896aeb3c Mon Sep 17 00:00:00 2001 From: GoEdgeLab Date: Sun, 27 Sep 2020 10:02:54 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8F=8D=E5=90=91=E4=BB=A3=E7=90=86=E6=94=AF?= =?UTF-8?q?=E6=8C=81RequestPath=E3=80=81RequestURI=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/nodes/http_request.go | 13 +++- internal/nodes/http_request_reverse_proxy.go | 66 +++++++++++++++++++- internal/nodes/http_request_websocket.go | 10 +-- internal/utils/path.go | 24 +++++++ internal/utils/path_test.go | 23 +++++++ 5 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 internal/utils/path.go create mode 100644 internal/utils/path_test.go diff --git a/internal/nodes/http_request.go b/internal/nodes/http_request.go index 36669aa..acca511 100644 --- a/internal/nodes/http_request.go +++ b/internal/nodes/http_request.go @@ -727,7 +727,6 @@ func (this *HTTPRequest) setForwardHeaders(header http.Header) { } // 处理自定义Request Header -// TODO 处理一些被Golang转换了的Header,比如Websocket func (this *HTTPRequest) processRequestHeaders(reqHeader http.Header) { if this.web.RequestHeaderPolicy != nil && this.web.RequestHeaderPolicy.IsOn { // 删除某些Header @@ -768,6 +767,18 @@ func (this *HTTPRequest) processRequestHeaders(reqHeader http.Header) { } } +// 处理一些被Golang转换了的Header +// TODO 可以自定义要转换的Header +func (this *HTTPRequest) fixRequestHeader(header http.Header) { + for k, v := range header { + if strings.Contains(k, "-Websocket-") { + header.Del(k) + k = strings.ReplaceAll(k, "-Websocket-", "-WebSocket-") + header[k] = v + } + } +} + // 处理自定义Response Header func (this *HTTPRequest) processResponseHeaders(statusCode int) { responseHeader := this.writer.Header() diff --git a/internal/nodes/http_request_reverse_proxy.go b/internal/nodes/http_request_reverse_proxy.go index 34f49f8..ff30b41 100644 --- a/internal/nodes/http_request_reverse_proxy.go +++ b/internal/nodes/http_request_reverse_proxy.go @@ -1,7 +1,72 @@ package nodes +import ( + "github.com/TeaOSLab/EdgeNode/internal/utils" + "strings" +) + // 处理反向代理 func (this *HTTPRequest) doReverseProxy() { + if this.reverseProxy == nil { + return + } + + // StripPrefix + if len(this.reverseProxy.StripPrefix) > 0 { + stripPrefix := this.reverseProxy.StripPrefix + if stripPrefix[0] != '/' { + stripPrefix = "/" + stripPrefix + } + this.uri = strings.TrimPrefix(this.uri, stripPrefix) + if len(this.uri) == 0 || this.uri[0] != '/' { + this.uri = "/" + this.uri + } + } + + // RequestURI + if len(this.reverseProxy.RequestURI) > 0 { + if this.reverseProxy.RequestURIHasVariables() { + this.uri = this.Format(this.reverseProxy.RequestURI) + } else { + this.uri = this.reverseProxy.RequestURI + } + if len(this.uri) == 0 || this.uri[0] != '/' { + this.uri = "/" + this.uri + } + + // 处理RequestURI中的问号 + questionMark := strings.LastIndex(this.uri, "?") + if questionMark > 0 { + path := this.uri[:questionMark] + if strings.Contains(path, "?") { + this.uri = path + "&" + this.uri[questionMark+1:] + } + } + + // 去除多个/ + this.uri = utils.CleanPath(this.uri) + } + + // 重组请求URL + questionMark := strings.Index(this.uri, "?") + if questionMark > -1 { + this.RawReq.URL.Path = this.uri[:questionMark] + this.RawReq.URL.RawQuery = this.uri[questionMark+1:] + } else { + this.RawReq.URL.Path = this.uri + this.RawReq.URL.RawQuery = "" + } + + // RequestHost + if len(this.reverseProxy.RequestHost) > 0 { + if this.reverseProxy.RequestHostHasVariables() { + this.RawReq.Host = this.Format(this.reverseProxy.RequestHost) + } else { + this.RawReq.Host = this.reverseProxy.RequestHost + } + this.RawReq.URL.Host = this.RawReq.Host + } + // 判断是否为Websocket请求 if this.RawReq.Header.Get("Upgrade") == "websocket" { this.doWebsocket() @@ -11,4 +76,3 @@ func (this *HTTPRequest) doReverseProxy() { // 普通HTTP请求 // TODO } - diff --git a/internal/nodes/http_request_websocket.go b/internal/nodes/http_request_websocket.go index e1d4b4c..b217d44 100644 --- a/internal/nodes/http_request_websocket.go +++ b/internal/nodes/http_request_websocket.go @@ -16,6 +16,8 @@ func (this *HTTPRequest) doWebsocket() { return } + // TODO 实现handshakeTimeout + // 校验来源 requestOrigin := this.RawReq.Header.Get("Origin") if len(requestOrigin) > 0 { @@ -38,7 +40,9 @@ func (this *HTTPRequest) doWebsocket() { return } + // 处理Header this.processRequestHeaders(this.RawReq.Header) + this.fixRequestHeader(this.RawReq.Header) // 处理 Websocket -> WebSocket // 设置指定的来源域 if !this.web.Websocket.RequestSameOrigin && len(this.web.Websocket.RequestOrigin) > 0 { @@ -49,11 +53,7 @@ func (this *HTTPRequest) doWebsocket() { this.RawReq.Header.Set("Origin", newRequestOrigin) } - // TODO 修改RequestURI - // TODO 实现handshakeTimeout - // TODO 修改 Websocket- 为 WebSocket- - - // TODO 增加N次错误重试 + // TODO 增加N次错误重试,重试的时候需要尝试不同的源站 originConn, err := OriginConnect(origin) if err != nil { logs.Error(err) diff --git a/internal/utils/path.go b/internal/utils/path.go new file mode 100644 index 0000000..fc77315 --- /dev/null +++ b/internal/utils/path.go @@ -0,0 +1,24 @@ +package utils + +// 清理Path中的多余的字符 +func CleanPath(path string) string { + l := len(path) + if l == 0 { + return "/" + } + result := []byte{'/'} + isSlash := true + for i := 0; i < l; i++ { + if path[i] == '\\' || path[i] == '/' { + if !isSlash { + isSlash = true + result = append(result, '/') + } + } else { + isSlash = false + result = append(result, path[i]) + } + } + return string(result) +} + diff --git a/internal/utils/path_test.go b/internal/utils/path_test.go new file mode 100644 index 0000000..6b6b8a5 --- /dev/null +++ b/internal/utils/path_test.go @@ -0,0 +1,23 @@ +package utils + +import ( + "github.com/iwind/TeaGo/assert" + "testing" +) + +func TestCleanPath(t *testing.T) { + a := assert.NewAssertion(t) + + a.IsTrue(CleanPath("") == "/") + a.IsTrue(CleanPath("/hello/world") == "/hello/world") + a.IsTrue(CleanPath("\\hello\\world") == "/hello/world") + a.IsTrue(CleanPath("/\\hello\\//world") == "/hello/world") + a.IsTrue(CleanPath("hello/world") == "/hello/world") + a.IsTrue(CleanPath("/hello////world") == "/hello/world") +} + +func BenchmarkCleanPath(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = CleanPath("/hello///world/very/long/very//long") + } +}