Files
EdgeNode/internal/nodes/http_request_reverse_proxy.go

328 lines
8.8 KiB
Go
Raw Normal View History

2020-09-26 19:54:26 +08:00
package nodes
import (
2020-09-27 15:26:06 +08:00
"context"
"errors"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
2020-09-27 15:26:06 +08:00
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
"github.com/TeaOSLab/EdgeNode/internal/utils"
2020-09-27 15:26:06 +08:00
"io"
"net/http"
2020-09-27 15:26:06 +08:00
"net/url"
"strconv"
"strings"
)
2020-09-26 19:54:26 +08:00
// 处理反向代理
func (this *HTTPRequest) doReverseProxy() {
if this.reverseProxy == nil {
return
}
2020-09-27 15:26:06 +08:00
// 对URL的处理
2022-03-17 17:03:52 +08:00
var stripPrefix = this.reverseProxy.StripPrefix
var requestURI = this.reverseProxy.RequestURI
var requestURIHasVariables = this.reverseProxy.RequestURIHasVariables()
var requestHost = ""
if this.reverseProxy.RequestHostType == serverconfigs.RequestHostTypeCustomized {
requestHost = this.reverseProxy.RequestHost
}
2022-03-17 17:03:52 +08:00
var requestHostHasVariables = this.reverseProxy.RequestHostHasVariables()
2020-09-27 15:26:06 +08:00
// 源站
2022-03-17 17:03:52 +08:00
var requestCall = shared.NewRequestCall()
requestCall.Request = this.RawReq
requestCall.Formatter = this.Format
2022-01-01 20:15:39 +08:00
requestCall.Domain = this.ReqHost
2022-03-17 17:03:52 +08:00
var origin = this.reverseProxy.NextOrigin(requestCall)
requestCall.CallResponseCallbacks(this.writer)
2020-09-27 15:26:06 +08:00
if origin == nil {
err := errors.New(this.URL() + ": no available origin sites for reverse proxy")
2022-01-01 20:15:39 +08:00
remotelogs.ServerError(this.ReqServer.Id, "HTTP_REQUEST_REVERSE_PROXY", err.Error(), "", nil)
2021-12-16 17:27:21 +08:00
this.write50x(err, http.StatusBadGateway, true)
2020-09-27 15:26:06 +08:00
return
}
this.origin = origin // 设置全局变量是为了日志等处理
if len(origin.StripPrefix) > 0 {
stripPrefix = origin.StripPrefix
}
if len(origin.RequestURI) > 0 {
requestURI = origin.RequestURI
requestURIHasVariables = origin.RequestURIHasVariables()
}
if len(origin.RequestHost) > 0 {
requestHost = origin.RequestHost
requestHostHasVariables = origin.RequestHostHasVariables()
}
// 处理Scheme
if origin.Addr == nil {
err := errors.New(this.URL() + ": origin '" + strconv.FormatInt(origin.Id, 10) + "' does not has a address")
remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", err.Error())
this.write50x(err, http.StatusBadGateway, true)
2020-09-27 15:26:06 +08:00
return
}
this.RawReq.URL.Scheme = origin.Addr.Protocol.Primary().Scheme()
// StripPrefix
2020-09-27 15:26:06 +08:00
if len(stripPrefix) > 0 {
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
2020-09-27 15:26:06 +08:00
if len(requestURI) > 0 {
if requestURIHasVariables {
this.uri = this.Format(requestURI)
} else {
2020-09-27 15:26:06 +08:00
this.uri = requestURI
}
if len(this.uri) == 0 || this.uri[0] != '/' {
this.uri = "/" + this.uri
}
// 处理RequestURI中的问号
2022-03-17 17:03:52 +08:00
var questionMark = strings.LastIndex(this.uri, "?")
if questionMark > 0 {
2022-03-17 17:03:52 +08:00
var path = this.uri[:questionMark]
if strings.Contains(path, "?") {
this.uri = path + "&" + this.uri[questionMark+1:]
}
}
// 去除多个/
this.uri = utils.CleanPath(this.uri)
}
// 获取源站地址
2022-03-17 17:03:52 +08:00
var originAddr = origin.Addr.PickAddress()
if origin.Addr.HostHasVariables() {
originAddr = this.Format(originAddr)
}
this.originAddr = originAddr
2020-09-27 15:26:06 +08:00
// RequestHost
if len(requestHost) > 0 {
if requestHostHasVariables {
this.RawReq.Host = this.Format(requestHost)
} else {
2022-03-17 17:03:52 +08:00
this.RawReq.Host = requestHost
2020-09-27 15:26:06 +08:00
}
this.RawReq.URL.Host = this.RawReq.Host
} else if this.reverseProxy.RequestHostType == serverconfigs.RequestHostTypeOrigin {
// 源站主机名
var hostname = originAddr
if origin.Addr.Protocol.IsHTTPFamily() {
hostname = strings.TrimSuffix(hostname, ":80")
} else if origin.Addr.Protocol.IsHTTPSFamily() {
hostname = strings.TrimSuffix(hostname, ":443")
}
this.RawReq.Host = hostname
this.RawReq.URL.Host = this.RawReq.Host
2020-09-27 15:26:06 +08:00
} else {
2022-01-01 20:15:39 +08:00
this.RawReq.URL.Host = this.ReqHost
2020-09-27 15:26:06 +08:00
}
// 重组请求URL
2022-03-17 17:03:52 +08:00
var 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 = ""
}
2020-09-27 15:26:06 +08:00
this.RawReq.RequestURI = ""
2020-09-27 15:26:06 +08:00
// 处理Header
this.setForwardHeaders(this.RawReq.Header)
this.processRequestHeaders(this.RawReq.Header)
// 调用回调
this.onRequest()
if this.writer.isFinished {
return
}
2020-09-26 19:54:26 +08:00
// 判断是否为Websocket请求
if this.RawReq.Header.Get("Upgrade") == "websocket" {
this.doWebsocket()
return
}
2020-09-27 15:26:06 +08:00
// 获取请求客户端
2022-03-14 15:07:18 +08:00
client, err := SharedHTTPClientPool.Client(this, origin, originAddr, this.reverseProxy.ProxyProtocol, this.reverseProxy.FollowRedirects)
2020-09-27 15:26:06 +08:00
if err != nil {
2022-03-30 17:31:47 +08:00
remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": "+err.Error())
2021-12-16 17:27:21 +08:00
this.write50x(err, http.StatusBadGateway, true)
2020-09-27 15:26:06 +08:00
return
}
// 在HTTP/2下需要防止因为requestBody而导致Content-Length为空的问题
if this.RawReq.ProtoMajor == 2 && this.RawReq.ContentLength == 0 {
_ = this.RawReq.Body.Close()
this.RawReq.Body = nil
}
2020-09-27 15:26:06 +08:00
// 开始请求
resp, err := client.Do(this.RawReq)
if err != nil {
// 客户端取消请求,则不提示
httpErr, ok := err.(*url.Error)
if !ok {
2021-08-01 21:56:02 +08:00
SharedOriginStateManager.Fail(origin, this.reverseProxy, func() {
this.reverseProxy.ResetScheduling()
})
2021-12-16 17:27:21 +08:00
this.write50x(err, http.StatusBadGateway, true)
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.RawReq.URL.String()+"': "+err.Error())
} else if httpErr.Err != context.Canceled {
SharedOriginStateManager.Fail(origin, this.reverseProxy, func() {
this.reverseProxy.ResetScheduling()
})
if httpErr.Timeout() {
2021-12-16 17:27:21 +08:00
this.write50x(err, http.StatusGatewayTimeout, true)
} else if httpErr.Temporary() {
2021-12-16 17:27:21 +08:00
this.write50x(err, http.StatusServiceUnavailable, true)
} else {
2021-12-16 17:27:21 +08:00
this.write50x(err, http.StatusBadGateway, true)
}
2022-01-09 15:32:02 +08:00
if httpErr.Err != io.EOF {
2022-03-30 17:31:47 +08:00
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": "+err.Error())
2022-01-09 15:32:02 +08:00
}
2020-09-27 15:26:06 +08:00
} else {
// 是否为客户端方面的错误
2022-03-17 17:03:52 +08:00
var isClientError = false
2020-09-27 15:26:06 +08:00
if ok {
if httpErr.Err == context.Canceled {
// 如果是服务器端主动关闭,则无需提示
if this.isConnClosed() {
this.disableLog = true
return
}
2020-09-27 15:26:06 +08:00
isClientError = true
this.addError(errors.New(httpErr.Op + " " + httpErr.URL + ": client closed the connection"))
this.writer.WriteHeader(499) // 仿照nginx
}
}
if !isClientError {
2021-12-16 17:27:21 +08:00
this.write50x(err, http.StatusBadGateway, true)
2020-09-27 15:26:06 +08:00
}
}
if resp != nil && resp.Body != nil {
_ = resp.Body.Close()
}
return
}
2021-08-01 21:56:02 +08:00
if !origin.IsOk {
SharedOriginStateManager.Success(origin, func() {
this.reverseProxy.ResetScheduling()
})
}
2020-09-27 15:26:06 +08:00
// WAF对出站进行检查
if this.web.FirewallRef != nil && this.web.FirewallRef.IsOn {
2020-10-08 15:06:42 +08:00
if this.doWAFResponse(resp) {
err = resp.Body.Close()
if err != nil {
2022-03-30 17:31:47 +08:00
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": "+err.Error())
2020-10-08 15:06:42 +08:00
}
return
}
}
2020-09-27 15:26:06 +08:00
// 特殊页面
2020-10-08 15:06:42 +08:00
if len(this.web.Pages) > 0 && this.doPage(resp.StatusCode) {
err = resp.Body.Close()
if err != nil {
2022-03-30 17:31:47 +08:00
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": "+err.Error())
2020-10-08 15:06:42 +08:00
}
return
}
2020-09-27 15:26:06 +08:00
// 设置Charset
// TODO 这里应该可以设置文本类型的列表,以及是否强制覆盖所有文本类型的字符集
if this.web.Charset != nil && this.web.Charset.IsOn && len(this.web.Charset.Charset) > 0 {
contentTypes, ok := resp.Header["Content-Type"]
if ok && len(contentTypes) > 0 {
2022-03-17 17:03:52 +08:00
var contentType = contentTypes[0]
2020-09-27 15:26:06 +08:00
if _, found := textMimeMap[contentType]; found {
resp.Header["Content-Type"][0] = contentType + "; charset=" + this.web.Charset.Charset
}
}
}
// 响应Header
this.writer.AddHeaders(resp.Header)
this.processResponseHeaders(resp.StatusCode)
// 是否需要刷新
var shouldAutoFlush = this.reverseProxy.AutoFlush || this.RawReq.Header.Get("Accept") == "text/event-stream"
2020-09-27 15:26:06 +08:00
// 准备
var delayHeaders = this.writer.Prepare(resp, resp.ContentLength, resp.StatusCode, true)
2020-09-27 15:26:06 +08:00
// 设置响应代码
if !delayHeaders {
this.writer.WriteHeader(resp.StatusCode)
}
2020-09-27 15:26:06 +08:00
// 是否有内容
if resp.ContentLength == 0 && len(resp.TransferEncoding) == 0 {
_ = resp.Body.Close()
this.writer.SetOk()
return
}
2020-09-27 15:26:06 +08:00
// 输出到客户端
var pool = this.bytePool(resp.ContentLength)
var buf = pool.Get()
if shouldAutoFlush {
2020-09-27 15:26:06 +08:00
for {
n, readErr := resp.Body.Read(buf)
if n > 0 {
_, err = this.writer.Write(buf[:n])
this.writer.Flush()
if err != nil {
break
}
}
if readErr != nil {
err = readErr
break
}
}
} else {
_, err = io.CopyBuffer(this.writer, resp.Body, buf)
}
pool.Put(buf)
2022-03-17 17:03:52 +08:00
var closeErr = resp.Body.Close()
if closeErr != nil {
if !this.canIgnore(closeErr) {
2022-03-30 17:31:47 +08:00
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": "+closeErr.Error())
2021-05-23 15:50:21 +08:00
}
2020-09-27 15:26:06 +08:00
}
if err != nil && err != io.EOF {
2021-05-23 16:16:56 +08:00
if !this.canIgnore(err) {
2022-03-30 17:31:47 +08:00
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": "+err.Error())
2021-05-23 15:50:21 +08:00
this.addError(err)
}
2020-09-27 15:26:06 +08:00
}
// 是否成功结束
if (err == nil || err == io.EOF) && (closeErr == nil || closeErr == io.EOF) {
this.writer.SetOk()
}
2020-09-26 19:54:26 +08:00
}