2020-09-26 19:54:26 +08:00
|
|
|
|
package nodes
|
|
|
|
|
|
|
2020-09-27 10:02:54 +08:00
|
|
|
|
import (
|
2020-09-27 15:26:06 +08:00
|
|
|
|
"context"
|
|
|
|
|
|
"errors"
|
2020-11-30 22:27:50 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
2020-09-27 15:26:06 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
2020-12-17 17:36:10 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
2020-09-27 10:02:54 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
2023-09-17 18:17:34 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fnv"
|
2024-03-12 16:24:23 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/utils/minifiers"
|
2023-08-26 15:46:45 +08:00
|
|
|
|
"github.com/iwind/TeaGo/lists"
|
2022-06-29 21:58:41 +08:00
|
|
|
|
"github.com/iwind/TeaGo/types"
|
2020-09-27 15:26:06 +08:00
|
|
|
|
"io"
|
2021-09-01 08:48:03 +08:00
|
|
|
|
"net/http"
|
2020-09-27 15:26:06 +08:00
|
|
|
|
"net/url"
|
|
|
|
|
|
"strconv"
|
2020-09-27 10:02:54 +08:00
|
|
|
|
"strings"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2020-09-26 19:54:26 +08:00
|
|
|
|
// 处理反向代理
|
|
|
|
|
|
func (this *HTTPRequest) doReverseProxy() {
|
2020-09-27 10:02:54 +08:00
|
|
|
|
if this.reverseProxy == nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-08-03 19:33:50 +08:00
|
|
|
|
var retries = 3
|
|
|
|
|
|
|
|
|
|
|
|
var failedOriginIds []int64
|
2022-08-03 20:30:59 +08:00
|
|
|
|
var failedLnNodeIds []int64
|
2023-11-15 19:06:15 +08:00
|
|
|
|
var failStatusCode int
|
2022-08-03 19:33:50 +08:00
|
|
|
|
|
|
|
|
|
|
for i := 0; i < retries; i++ {
|
2023-11-15 19:06:15 +08:00
|
|
|
|
originId, lnNodeId, shouldRetry := this.doOriginRequest(failedOriginIds, failedLnNodeIds, i == 0, i == retries-1, &failStatusCode)
|
2022-08-03 19:33:50 +08:00
|
|
|
|
if !shouldRetry {
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
if originId > 0 {
|
|
|
|
|
|
failedOriginIds = append(failedOriginIds, originId)
|
|
|
|
|
|
}
|
2022-08-03 20:30:59 +08:00
|
|
|
|
if lnNodeId > 0 {
|
|
|
|
|
|
failedLnNodeIds = append(failedLnNodeIds, lnNodeId)
|
|
|
|
|
|
}
|
2022-08-03 19:33:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 请求源站
|
2023-11-15 19:06:15 +08:00
|
|
|
|
func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeIds []int64, isFirstTry bool, isLastRetry bool, failStatusCode *int) (originId int64, lnNodeId int64, shouldRetry bool) {
|
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()
|
2022-08-03 19:33:50 +08:00
|
|
|
|
var oldURI = this.uri
|
2020-11-30 22:27:50 +08:00
|
|
|
|
|
|
|
|
|
|
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()
|
2021-09-20 09:55:42 +08:00
|
|
|
|
requestCall.Request = this.RawReq
|
2021-09-20 09:59:24 +08:00
|
|
|
|
requestCall.Formatter = this.Format
|
2022-01-01 20:15:39 +08:00
|
|
|
|
requestCall.Domain = this.ReqHost
|
2022-04-04 12:06:53 +08:00
|
|
|
|
|
|
|
|
|
|
var origin *serverconfigs.OriginConfig
|
|
|
|
|
|
|
|
|
|
|
|
// 二级节点
|
2022-08-03 20:30:59 +08:00
|
|
|
|
var hasMultipleLnNodes = false
|
2023-06-05 17:06:03 +08:00
|
|
|
|
if this.cacheRef != nil || (this.nodeConfig != nil && this.nodeConfig.GlobalServerConfig != nil && this.nodeConfig.GlobalServerConfig.HTTPAll.ForceLnRequest) {
|
2023-09-17 18:17:34 +08:00
|
|
|
|
origin, lnNodeId, hasMultipleLnNodes = this.getLnOrigin(failedLnNodeIds, fnv.HashString(this.URL()))
|
2022-04-04 12:06:53 +08:00
|
|
|
|
if origin != nil {
|
|
|
|
|
|
// 强制变更原来访问的域名
|
|
|
|
|
|
requestHost = this.ReqHost
|
|
|
|
|
|
}
|
2022-08-07 16:37:29 +08:00
|
|
|
|
|
2023-06-05 17:06:03 +08:00
|
|
|
|
if this.cacheRef != nil {
|
|
|
|
|
|
// 回源Header中去除If-None-Match和If-Modified-Since
|
|
|
|
|
|
if !this.cacheRef.EnableIfNoneMatch {
|
|
|
|
|
|
this.DeleteHeader("If-None-Match")
|
|
|
|
|
|
}
|
|
|
|
|
|
if !this.cacheRef.EnableIfModifiedSince {
|
|
|
|
|
|
this.DeleteHeader("If-Modified-Since")
|
|
|
|
|
|
}
|
2022-08-07 16:37:29 +08:00
|
|
|
|
}
|
2022-04-04 12:06:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 自定义源站
|
2020-09-27 15:26:06 +08:00
|
|
|
|
if origin == nil {
|
2022-08-03 19:33:50 +08:00
|
|
|
|
if !isFirstTry {
|
|
|
|
|
|
origin = this.reverseProxy.AnyOrigin(requestCall, failedOriginIds)
|
|
|
|
|
|
}
|
|
|
|
|
|
if origin == nil {
|
|
|
|
|
|
origin = this.reverseProxy.NextOrigin(requestCall)
|
2023-11-15 19:06:15 +08:00
|
|
|
|
if origin != nil && origin.Id > 0 && (*failStatusCode >= 403 && *failStatusCode <= 404) && lists.ContainsInt64(failedOriginIds, origin.Id) {
|
2024-01-28 09:27:50 +08:00
|
|
|
|
shouldRetry = false
|
|
|
|
|
|
isLastRetry = true
|
2023-11-15 19:06:15 +08:00
|
|
|
|
}
|
2022-08-03 19:33:50 +08:00
|
|
|
|
}
|
2022-04-04 12:06:53 +08:00
|
|
|
|
requestCall.CallResponseCallbacks(this.writer)
|
|
|
|
|
|
if origin == nil {
|
|
|
|
|
|
err := errors.New(this.URL() + ": no available origin sites for reverse proxy")
|
|
|
|
|
|
remotelogs.ServerError(this.ReqServer.Id, "HTTP_REQUEST_REVERSE_PROXY", err.Error(), "", nil)
|
2022-07-30 10:48:41 +08:00
|
|
|
|
this.write50x(err, http.StatusBadGateway, "No origin site yet", "尚未配置源站", true)
|
2022-04-04 12:06:53 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2022-08-03 19:33:50 +08:00
|
|
|
|
originId = origin.Id
|
2022-04-04 12:06:53 +08:00
|
|
|
|
|
|
|
|
|
|
if len(origin.StripPrefix) > 0 {
|
|
|
|
|
|
stripPrefix = origin.StripPrefix
|
|
|
|
|
|
}
|
|
|
|
|
|
if len(origin.RequestURI) > 0 {
|
|
|
|
|
|
requestURI = origin.RequestURI
|
|
|
|
|
|
requestURIHasVariables = origin.RequestURIHasVariables()
|
|
|
|
|
|
}
|
2020-09-27 15:26:06 +08:00
|
|
|
|
}
|
2022-04-04 12:06:53 +08:00
|
|
|
|
|
2020-09-27 15:26:06 +08:00
|
|
|
|
this.origin = origin // 设置全局变量是为了日志等处理
|
2022-04-04 12:06:53 +08:00
|
|
|
|
|
2020-09-27 15:26:06 +08:00
|
|
|
|
if len(origin.RequestHost) > 0 {
|
|
|
|
|
|
requestHost = origin.RequestHost
|
|
|
|
|
|
requestHostHasVariables = origin.RequestHostHasVariables()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-06-07 17:27:55 +08:00
|
|
|
|
// 处理OSS
|
|
|
|
|
|
var isHTTPOrigin = origin.OSS == nil
|
|
|
|
|
|
|
2020-09-27 15:26:06 +08:00
|
|
|
|
// 处理Scheme
|
2023-06-07 17:27:55 +08:00
|
|
|
|
if isHTTPOrigin && origin.Addr == nil {
|
2022-07-15 11:15:55 +08:00
|
|
|
|
err := errors.New(this.URL() + ": Origin '" + strconv.FormatInt(origin.Id, 10) + "' does not has a address")
|
2022-09-20 14:58:55 +08:00
|
|
|
|
remotelogs.ErrorServer("HTTP_REQUEST_REVERSE_PROXY", err.Error())
|
2022-07-30 10:48:41 +08:00
|
|
|
|
this.write50x(err, http.StatusBadGateway, "Origin site did not has a valid address", "源站尚未配置地址", true)
|
2020-09-27 15:26:06 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2023-06-07 17:27:55 +08:00
|
|
|
|
|
|
|
|
|
|
if isHTTPOrigin {
|
|
|
|
|
|
this.RawReq.URL.Scheme = origin.Addr.Protocol.Primary().Scheme()
|
|
|
|
|
|
}
|
2020-09-27 15:26:06 +08:00
|
|
|
|
|
2020-09-27 10:02:54 +08:00
|
|
|
|
// StripPrefix
|
2020-09-27 15:26:06 +08:00
|
|
|
|
if len(stripPrefix) > 0 {
|
2020-09-27 10:02:54 +08:00
|
|
|
|
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)
|
2020-09-27 10:02:54 +08:00
|
|
|
|
} else {
|
2020-09-27 15:26:06 +08:00
|
|
|
|
this.uri = requestURI
|
2020-09-27 10:02:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
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, "?")
|
2020-09-27 10:02:54 +08:00
|
|
|
|
if questionMark > 0 {
|
2022-03-17 17:03:52 +08:00
|
|
|
|
var path = this.uri[:questionMark]
|
2020-09-27 10:02:54 +08:00
|
|
|
|
if strings.Contains(path, "?") {
|
|
|
|
|
|
this.uri = path + "&" + this.uri[questionMark+1:]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 去除多个/
|
|
|
|
|
|
this.uri = utils.CleanPath(this.uri)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-06-07 17:27:55 +08:00
|
|
|
|
var originAddr = ""
|
|
|
|
|
|
if isHTTPOrigin {
|
|
|
|
|
|
// 获取源站地址
|
|
|
|
|
|
originAddr = origin.Addr.PickAddress()
|
|
|
|
|
|
if origin.Addr.HostHasVariables() {
|
|
|
|
|
|
originAddr = this.Format(originAddr)
|
2022-06-29 21:58:41 +08:00
|
|
|
|
}
|
2020-11-30 22:27:50 +08:00
|
|
|
|
|
2023-06-07 17:27:55 +08:00
|
|
|
|
// 端口跟随
|
|
|
|
|
|
if origin.FollowPort {
|
|
|
|
|
|
var originHostIndex = strings.Index(originAddr, ":")
|
|
|
|
|
|
if originHostIndex < 0 {
|
|
|
|
|
|
var originErr = errors.New(this.URL() + ": Invalid origin address '" + originAddr + "', lacking port")
|
|
|
|
|
|
remotelogs.ErrorServer("HTTP_REQUEST_REVERSE_PROXY", originErr.Error())
|
|
|
|
|
|
this.write50x(originErr, http.StatusBadGateway, "No port in origin site address", "源站地址中没有配置端口", true)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
originAddr = originAddr[:originHostIndex+1] + types.String(this.requestServerPort())
|
2020-09-27 15:26:06 +08:00
|
|
|
|
}
|
2023-06-07 17:27:55 +08:00
|
|
|
|
this.originAddr = originAddr
|
2022-06-30 12:12:05 +08:00
|
|
|
|
|
2023-06-07 17:27:55 +08:00
|
|
|
|
// RequestHost
|
|
|
|
|
|
if len(requestHost) > 0 {
|
|
|
|
|
|
if requestHostHasVariables {
|
|
|
|
|
|
this.RawReq.Host = this.Format(requestHost)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.RawReq.Host = requestHost
|
|
|
|
|
|
}
|
2022-06-30 12:12:05 +08:00
|
|
|
|
|
2023-06-07 17:27:55 +08:00
|
|
|
|
// 是否移除端口
|
|
|
|
|
|
if this.reverseProxy.RequestHostExcludingPort {
|
|
|
|
|
|
this.RawReq.Host = utils.ParseAddrHost(this.RawReq.Host)
|
|
|
|
|
|
}
|
2021-12-05 09:30:45 +08:00
|
|
|
|
|
2023-06-07 17:27:55 +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")
|
|
|
|
|
|
}
|
2022-06-30 12:12:05 +08:00
|
|
|
|
|
2023-06-07 17:27:55 +08:00
|
|
|
|
this.RawReq.Host = hostname
|
2022-06-30 12:12:05 +08:00
|
|
|
|
|
2023-06-07 17:27:55 +08:00
|
|
|
|
// 是否移除端口
|
|
|
|
|
|
if this.reverseProxy.RequestHostExcludingPort {
|
|
|
|
|
|
this.RawReq.Host = utils.ParseAddrHost(this.RawReq.Host)
|
|
|
|
|
|
}
|
2022-06-30 12:12:05 +08:00
|
|
|
|
|
2023-06-07 17:27:55 +08:00
|
|
|
|
this.RawReq.URL.Host = this.RawReq.Host
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.RawReq.URL.Host = this.ReqHost
|
|
|
|
|
|
|
|
|
|
|
|
// 是否移除端口
|
|
|
|
|
|
if this.reverseProxy.RequestHostExcludingPort {
|
|
|
|
|
|
this.RawReq.Host = utils.ParseAddrHost(this.RawReq.Host)
|
|
|
|
|
|
this.RawReq.URL.Host = utils.ParseAddrHost(this.RawReq.URL.Host)
|
|
|
|
|
|
}
|
2022-06-30 12:12:05 +08:00
|
|
|
|
}
|
2020-09-27 15:26:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-09-27 10:02:54 +08:00
|
|
|
|
// 重组请求URL
|
2022-03-17 17:03:52 +08:00
|
|
|
|
var questionMark = strings.Index(this.uri, "?")
|
2020-09-27 10:02:54 +08:00
|
|
|
|
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 10:02:54 +08:00
|
|
|
|
|
2020-09-27 15:26:06 +08:00
|
|
|
|
// 处理Header
|
|
|
|
|
|
this.setForwardHeaders(this.RawReq.Header)
|
|
|
|
|
|
this.processRequestHeaders(this.RawReq.Header)
|
2020-09-27 10:02:54 +08:00
|
|
|
|
|
2021-12-30 11:19:11 +08:00
|
|
|
|
// 调用回调
|
|
|
|
|
|
this.onRequest()
|
|
|
|
|
|
if this.writer.isFinished {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-09-26 19:54:26 +08:00
|
|
|
|
// 判断是否为Websocket请求
|
2023-06-07 17:27:55 +08:00
|
|
|
|
if isHTTPOrigin && this.RawReq.Header.Get("Upgrade") == "websocket" {
|
2022-09-16 09:37:49 +08:00
|
|
|
|
shouldRetry = this.doWebsocket(requestHost, isLastRetry)
|
2020-09-26 19:54:26 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-06-07 17:27:55 +08:00
|
|
|
|
var resp *http.Response
|
2023-08-08 18:14:48 +08:00
|
|
|
|
var respBodyIsClosed bool
|
2023-06-07 17:27:55 +08:00
|
|
|
|
var requestErr error
|
2023-06-08 17:47:04 +08:00
|
|
|
|
var requestErrCode string
|
2023-06-07 17:27:55 +08:00
|
|
|
|
if isHTTPOrigin { // 普通HTTP(S)源站
|
2023-06-07 20:17:07 +08:00
|
|
|
|
// 修复空User-Agent问题
|
|
|
|
|
|
_, existsUserAgent := this.RawReq.Header["User-Agent"]
|
|
|
|
|
|
if !existsUserAgent {
|
|
|
|
|
|
this.RawReq.Header["User-Agent"] = []string{""}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-06-07 17:27:55 +08:00
|
|
|
|
// 获取请求客户端
|
2024-04-17 20:38:00 +08:00
|
|
|
|
client, err := SharedHTTPClientPool.Client(this, origin, originAddr, this.reverseProxy.ProxyProtocol, this.reverseProxy.FollowRedirects)
|
2023-06-07 17:27:55 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
remotelogs.ErrorServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Create client failed: "+err.Error())
|
|
|
|
|
|
this.write50x(err, http.StatusBadGateway, "Failed to create origin site client", "构造源站客户端失败", true)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-06-18 18:05:28 +08:00
|
|
|
|
// 尝试自动纠正源站地址中的scheme
|
|
|
|
|
|
if this.RawReq.URL.Scheme == "http" && strings.HasSuffix(originAddr, ":443") {
|
|
|
|
|
|
this.RawReq.URL.Scheme = "https"
|
|
|
|
|
|
} else if this.RawReq.URL.Scheme == "https" && strings.HasSuffix(originAddr, ":80") {
|
|
|
|
|
|
this.RawReq.URL.Scheme = "http"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-06-07 17:27:55 +08:00
|
|
|
|
// 开始请求
|
|
|
|
|
|
resp, requestErr = client.Do(this.RawReq)
|
|
|
|
|
|
} else if origin.OSS != nil { // OSS源站
|
2023-06-07 19:30:51 +08:00
|
|
|
|
var goNext bool
|
2023-07-02 11:10:04 +08:00
|
|
|
|
resp, goNext, requestErrCode, _, requestErr = this.doOSSOrigin(origin)
|
2023-06-08 17:47:04 +08:00
|
|
|
|
if requestErr == nil {
|
|
|
|
|
|
if resp == nil || !goNext {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2023-06-07 17:27:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2023-06-08 17:47:04 +08:00
|
|
|
|
this.writeCode(http.StatusBadGateway, "The type of origin site has not been supported", "设置的源站类型尚未支持")
|
2020-09-27 15:26:06 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2023-08-08 18:14:48 +08:00
|
|
|
|
|
|
|
|
|
|
if resp != nil && resp.Body != nil {
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
if !respBodyIsClosed {
|
|
|
|
|
|
_ = resp.Body.Close()
|
|
|
|
|
|
}
|
|
|
|
|
|
}()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-06-07 17:27:55 +08:00
|
|
|
|
if requestErr != nil {
|
2020-09-27 15:26:06 +08:00
|
|
|
|
// 客户端取消请求,则不提示
|
2023-08-20 11:45:39 +08:00
|
|
|
|
var httpErr *url.Error
|
|
|
|
|
|
ok := errors.As(requestErr, &httpErr)
|
2021-09-01 08:48:03 +08:00
|
|
|
|
if !ok {
|
2023-06-07 19:28:16 +08:00
|
|
|
|
if isHTTPOrigin {
|
|
|
|
|
|
SharedOriginStateManager.Fail(origin, requestHost, this.reverseProxy, func() {
|
|
|
|
|
|
this.reverseProxy.ResetScheduling()
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2023-06-08 17:47:04 +08:00
|
|
|
|
|
|
|
|
|
|
if len(requestErrCode) > 0 {
|
|
|
|
|
|
this.write50x(requestErr, http.StatusBadGateway, "Failed to read origin site (error code: "+requestErrCode+")", "源站读取失败(错误代号:"+requestErrCode+")", true)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.write50x(requestErr, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true)
|
|
|
|
|
|
}
|
2023-06-07 17:27:55 +08:00
|
|
|
|
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.RawReq.URL.String()+": Request origin server failed: "+requestErr.Error())
|
2023-08-20 11:45:39 +08:00
|
|
|
|
} else if !errors.Is(httpErr, context.Canceled) {
|
2023-06-07 19:28:16 +08:00
|
|
|
|
if isHTTPOrigin {
|
|
|
|
|
|
SharedOriginStateManager.Fail(origin, requestHost, this.reverseProxy, func() {
|
|
|
|
|
|
this.reverseProxy.ResetScheduling()
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2022-08-03 19:33:50 +08:00
|
|
|
|
|
|
|
|
|
|
// 是否需要重试
|
2022-08-03 20:30:59 +08:00
|
|
|
|
if (originId > 0 || (lnNodeId > 0 && hasMultipleLnNodes)) && !isLastRetry {
|
2022-08-03 19:33:50 +08:00
|
|
|
|
shouldRetry = true
|
|
|
|
|
|
this.uri = oldURI // 恢复备份
|
|
|
|
|
|
|
2023-10-08 19:05:09 +08:00
|
|
|
|
if httpErr.Err != io.EOF && !errors.Is(httpErr.Err, http.ErrBodyReadAfterClose) {
|
2023-06-07 17:27:55 +08:00
|
|
|
|
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Request origin server failed: "+requestErr.Error())
|
2022-08-03 19:33:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-09-01 08:48:03 +08:00
|
|
|
|
if httpErr.Timeout() {
|
2023-06-07 17:27:55 +08:00
|
|
|
|
this.write50x(requestErr, http.StatusGatewayTimeout, "Read origin site timeout", "源站读取超时", true)
|
2021-09-01 08:48:03 +08:00
|
|
|
|
} else if httpErr.Temporary() {
|
2023-06-07 17:27:55 +08:00
|
|
|
|
this.write50x(requestErr, http.StatusServiceUnavailable, "Origin site unavailable now", "源站当前不可用", true)
|
2021-09-01 08:48:03 +08:00
|
|
|
|
} else {
|
2023-06-07 17:27:55 +08:00
|
|
|
|
this.write50x(requestErr, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true)
|
2021-09-01 08:48:03 +08:00
|
|
|
|
}
|
2023-10-08 19:05:09 +08:00
|
|
|
|
if httpErr.Err != io.EOF && !errors.Is(httpErr.Err, http.ErrBodyReadAfterClose) {
|
2023-06-07 17:27:55 +08:00
|
|
|
|
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Request origin server failed: "+requestErr.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 {
|
2023-08-20 11:45:39 +08:00
|
|
|
|
if errors.Is(httpErr, context.Canceled) {
|
2021-12-01 20:55:19 +08:00
|
|
|
|
// 如果是服务器端主动关闭,则无需提示
|
|
|
|
|
|
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 {
|
2023-06-07 17:27:55 +08:00
|
|
|
|
this.write50x(requestErr, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true)
|
2020-09-27 15:26:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2022-09-16 10:07:40 +08:00
|
|
|
|
|
2024-01-17 15:17:19 +08:00
|
|
|
|
if resp == nil {
|
|
|
|
|
|
this.write50x(requestErr, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// fix Content-Type
|
|
|
|
|
|
if resp.Header["Content-Type"] == nil {
|
|
|
|
|
|
resp.Header["Content-Type"] = []string{}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-15 19:06:15 +08:00
|
|
|
|
// 40x && 50x
|
|
|
|
|
|
*failStatusCode = resp.StatusCode
|
2024-01-17 15:17:19 +08:00
|
|
|
|
if ((resp.StatusCode >= 500 && resp.StatusCode < 510 && this.reverseProxy.Retry50X) ||
|
|
|
|
|
|
(resp.StatusCode >= 403 && resp.StatusCode <= 404 && this.reverseProxy.Retry40X)) &&
|
2023-08-20 15:50:31 +08:00
|
|
|
|
(originId > 0 || (lnNodeId > 0 && hasMultipleLnNodes)) &&
|
|
|
|
|
|
!isLastRetry {
|
|
|
|
|
|
if resp.Body != nil {
|
|
|
|
|
|
_ = resp.Body.Close()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
shouldRetry = true
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-08-26 15:46:45 +08:00
|
|
|
|
// 尝试从缓存中恢复
|
2024-01-17 15:17:19 +08:00
|
|
|
|
if resp.StatusCode >= 500 && // support 50X only
|
2023-08-26 15:46:45 +08:00
|
|
|
|
resp.StatusCode < 510 &&
|
|
|
|
|
|
this.cacheCanTryStale &&
|
|
|
|
|
|
this.web.Cache.Stale != nil &&
|
|
|
|
|
|
this.web.Cache.Stale.IsOn &&
|
|
|
|
|
|
(len(this.web.Cache.Stale.Status) == 0 || lists.ContainsInt(this.web.Cache.Stale.Status, resp.StatusCode)) {
|
|
|
|
|
|
var ok = this.doCacheRead(true)
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-09-16 10:07:40 +08:00
|
|
|
|
// 记录相关数据
|
|
|
|
|
|
this.originStatus = int32(resp.StatusCode)
|
|
|
|
|
|
|
|
|
|
|
|
// 恢复源站状态
|
2023-06-07 19:28:16 +08:00
|
|
|
|
if !origin.IsOk && isHTTPOrigin {
|
2021-08-01 21:56:02 +08:00
|
|
|
|
SharedOriginStateManager.Success(origin, func() {
|
|
|
|
|
|
this.reverseProxy.ResetScheduling()
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2020-09-27 15:26:06 +08:00
|
|
|
|
|
|
|
|
|
|
// WAF对出站进行检查
|
2020-12-17 17:36:10 +08:00
|
|
|
|
if this.web.FirewallRef != nil && this.web.FirewallRef.IsOn {
|
2020-10-08 15:06:42 +08:00
|
|
|
|
if this.doWAFResponse(resp) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-09-27 15:26:06 +08:00
|
|
|
|
|
|
|
|
|
|
// 特殊页面
|
2023-07-31 09:55:13 +08:00
|
|
|
|
if this.doPage(resp.StatusCode) {
|
2020-10-08 15:06:42 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2020-09-27 15:26:06 +08:00
|
|
|
|
|
2023-07-11 19:52:57 +08:00
|
|
|
|
// Page optimization
|
|
|
|
|
|
if this.web.Optimization != nil && resp.Body != nil && this.cacheRef != nil /** must under cache **/ {
|
2024-03-12 16:24:23 +08:00
|
|
|
|
err := minifiers.MinifyResponse(this.web.Optimization, this.URL(), resp)
|
2023-07-11 19:52:57 +08:00
|
|
|
|
if err != nil {
|
2024-01-14 20:34:55 +08:00
|
|
|
|
this.write50x(err, http.StatusBadGateway, "Page Optimization: fail to read content from origin", "内容优化:从源站读取内容失败", false)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// HLS
|
|
|
|
|
|
if this.web.HLS != nil &&
|
|
|
|
|
|
this.web.HLS.Encrypting != nil &&
|
|
|
|
|
|
this.web.HLS.Encrypting.IsOn &&
|
|
|
|
|
|
resp.StatusCode == http.StatusOK {
|
|
|
|
|
|
m3u8Err := this.processM3u8Response(resp)
|
|
|
|
|
|
if m3u8Err != nil {
|
|
|
|
|
|
this.write50x(m3u8Err, http.StatusBadGateway, "m3u8 encrypt: fail to read content from origin", "m3u8加密:从源站读取内容失败", false)
|
2023-07-11 19:52:57 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-09-27 15:26:06 +08:00
|
|
|
|
// 设置Charset
|
2024-01-13 16:29:32 +08:00
|
|
|
|
// TODO 这里应该可以设置文本类型的列表
|
2020-09-27 15:26:06 +08:00
|
|
|
|
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]
|
2024-01-13 16:29:32 +08:00
|
|
|
|
if this.web.Charset.Force {
|
|
|
|
|
|
var semiIndex = strings.Index(contentType, ";")
|
|
|
|
|
|
if semiIndex > 0 {
|
|
|
|
|
|
contentType = contentType[:semiIndex]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-09-27 15:26:06 +08:00
|
|
|
|
if _, found := textMimeMap[contentType]; found {
|
2024-01-13 16:29:32 +08:00
|
|
|
|
var newCharset = this.web.Charset.Charset
|
|
|
|
|
|
if this.web.Charset.IsUpper {
|
|
|
|
|
|
newCharset = strings.ToUpper(newCharset)
|
|
|
|
|
|
}
|
|
|
|
|
|
resp.Header["Content-Type"][0] = contentType + "; charset=" + newCharset
|
2020-09-27 15:26:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-09 20:37:05 +08:00
|
|
|
|
// 替换Location中的源站地址
|
|
|
|
|
|
var locationHeader = resp.Header.Get("Location")
|
|
|
|
|
|
if len(locationHeader) > 0 {
|
2022-05-19 20:16:40 +08:00
|
|
|
|
// 空Location处理
|
|
|
|
|
|
if locationHeader == emptyHTTPLocation {
|
|
|
|
|
|
resp.Header.Del("Location")
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 自动修正Location中的源站地址
|
|
|
|
|
|
locationURL, err := url.Parse(locationHeader)
|
|
|
|
|
|
if err == nil && locationURL.Host != this.ReqHost && (locationURL.Host == originAddr || strings.HasPrefix(originAddr, locationURL.Host+":")) {
|
|
|
|
|
|
locationURL.Host = this.ReqHost
|
2022-06-07 11:25:09 +08:00
|
|
|
|
|
|
|
|
|
|
var oldScheme = locationURL.Scheme
|
|
|
|
|
|
|
|
|
|
|
|
// 尝试和当前Scheme一致
|
2022-05-19 20:16:40 +08:00
|
|
|
|
if this.IsHTTP {
|
|
|
|
|
|
locationURL.Scheme = "http"
|
|
|
|
|
|
} else if this.IsHTTPS {
|
|
|
|
|
|
locationURL.Scheme = "https"
|
|
|
|
|
|
}
|
2022-04-09 20:37:05 +08:00
|
|
|
|
|
2022-06-07 11:25:09 +08:00
|
|
|
|
// 如果和当前URL一样,则可能是http -> https,防止无限循环
|
|
|
|
|
|
if locationURL.String() == this.URL() {
|
|
|
|
|
|
locationURL.Scheme = oldScheme
|
|
|
|
|
|
resp.Header.Set("Location", locationURL.String())
|
|
|
|
|
|
} else {
|
|
|
|
|
|
resp.Header.Set("Location", locationURL.String())
|
|
|
|
|
|
}
|
2022-05-19 20:16:40 +08:00
|
|
|
|
}
|
2022-04-09 20:37:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-09-27 15:26:06 +08:00
|
|
|
|
// 响应Header
|
|
|
|
|
|
this.writer.AddHeaders(resp.Header)
|
2023-06-11 10:46:20 +08:00
|
|
|
|
this.ProcessResponseHeaders(this.writer.Header(), resp.StatusCode)
|
2020-09-27 15:26:06 +08:00
|
|
|
|
|
|
|
|
|
|
// 是否需要刷新
|
2023-07-03 16:23:54 +08:00
|
|
|
|
var shouldAutoFlush = this.reverseProxy.AutoFlush || (resp.Header != nil && strings.Contains(resp.Header.Get("Content-Type"), "stream"))
|
|
|
|
|
|
|
|
|
|
|
|
// 设置当前连接为Persistence
|
|
|
|
|
|
if shouldAutoFlush && this.nodeConfig != nil && this.nodeConfig.HasConnTimeoutSettings() {
|
|
|
|
|
|
var requestConn = this.RawReq.Context().Value(HTTPConnContextKey)
|
|
|
|
|
|
if requestConn == nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
requestClientConn, ok := requestConn.(ClientConnInterface)
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
requestClientConn.SetIsPersistent(true)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-09-27 15:26:06 +08:00
|
|
|
|
|
|
|
|
|
|
// 准备
|
2022-02-15 14:55:49 +08:00
|
|
|
|
var delayHeaders = this.writer.Prepare(resp, resp.ContentLength, resp.StatusCode, true)
|
2020-09-27 15:26:06 +08:00
|
|
|
|
|
|
|
|
|
|
// 设置响应代码
|
2021-10-13 11:11:57 +08:00
|
|
|
|
if !delayHeaders {
|
|
|
|
|
|
this.writer.WriteHeader(resp.StatusCode)
|
|
|
|
|
|
}
|
2020-09-27 15:26:06 +08:00
|
|
|
|
|
2022-01-03 18:10:02 +08:00
|
|
|
|
// 是否有内容
|
|
|
|
|
|
if resp.ContentLength == 0 && len(resp.TransferEncoding) == 0 {
|
2022-06-09 20:26:36 +08:00
|
|
|
|
// 即使内容为0,也需要读取一次,以便于触发相关事件
|
|
|
|
|
|
var buf = utils.BytePool4k.Get()
|
2024-04-15 09:26:00 +08:00
|
|
|
|
_, _ = io.CopyBuffer(this.writer, resp.Body, buf.Bytes)
|
2022-06-09 20:26:36 +08:00
|
|
|
|
utils.BytePool4k.Put(buf)
|
2022-01-03 18:10:02 +08:00
|
|
|
|
_ = resp.Body.Close()
|
2023-08-08 18:14:48 +08:00
|
|
|
|
respBodyIsClosed = true
|
2022-06-09 20:26:36 +08:00
|
|
|
|
|
2022-01-03 18:10:02 +08:00
|
|
|
|
this.writer.SetOk()
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-09-27 15:26:06 +08:00
|
|
|
|
// 输出到客户端
|
2022-03-04 11:51:59 +08:00
|
|
|
|
var pool = this.bytePool(resp.ContentLength)
|
|
|
|
|
|
var buf = pool.Get()
|
2023-06-07 17:27:55 +08:00
|
|
|
|
var err error
|
2020-09-27 18:41:56 +08:00
|
|
|
|
if shouldAutoFlush {
|
2020-09-27 15:26:06 +08:00
|
|
|
|
for {
|
2024-04-15 09:26:00 +08:00
|
|
|
|
n, readErr := resp.Body.Read(buf.Bytes)
|
2020-09-27 15:26:06 +08:00
|
|
|
|
if n > 0 {
|
2024-04-15 09:26:00 +08:00
|
|
|
|
_, err = this.writer.Write(buf.Bytes[:n])
|
2020-09-27 15:26:06 +08:00
|
|
|
|
this.writer.Flush()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if readErr != nil {
|
|
|
|
|
|
err = readErr
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2023-07-31 15:49:04 +08:00
|
|
|
|
if this.cacheRef != nil &&
|
|
|
|
|
|
this.cacheRef.EnableReadingOriginAsync &&
|
|
|
|
|
|
resp.ContentLength > 0 &&
|
|
|
|
|
|
resp.ContentLength < (128<<20) { // TODO configure max content-length in cache policy OR CacheRef
|
|
|
|
|
|
var requestIsCanceled = false
|
|
|
|
|
|
for {
|
2024-04-15 09:26:00 +08:00
|
|
|
|
n, readErr := resp.Body.Read(buf.Bytes)
|
2023-07-31 15:49:04 +08:00
|
|
|
|
|
|
|
|
|
|
if n > 0 && !requestIsCanceled {
|
2024-04-15 09:26:00 +08:00
|
|
|
|
_, err = this.writer.Write(buf.Bytes[:n])
|
2023-07-31 15:49:04 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
requestIsCanceled = true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if readErr != nil {
|
|
|
|
|
|
err = readErr
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2024-04-15 09:26:00 +08:00
|
|
|
|
_, err = io.CopyBuffer(this.writer, resp.Body, buf.Bytes)
|
2023-07-31 15:49:04 +08:00
|
|
|
|
}
|
2020-09-27 15:26:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
pool.Put(buf)
|
|
|
|
|
|
|
2022-03-17 17:03:52 +08:00
|
|
|
|
var closeErr = resp.Body.Close()
|
2023-08-08 18:14:48 +08:00
|
|
|
|
respBodyIsClosed = true
|
2021-06-06 23:42:11 +08:00
|
|
|
|
if closeErr != nil {
|
2021-11-13 21:30:24 +08:00
|
|
|
|
if !this.canIgnore(closeErr) {
|
2022-09-20 14:58:55 +08:00
|
|
|
|
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Closing error: "+closeErr.Error())
|
2021-05-23 15:50:21 +08:00
|
|
|
|
}
|
2020-09-27 15:26:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-09-27 18:41:56 +08:00
|
|
|
|
if err != nil && err != io.EOF {
|
2021-05-23 16:16:56 +08:00
|
|
|
|
if !this.canIgnore(err) {
|
2022-09-20 14:58:55 +08:00
|
|
|
|
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Writing error: "+err.Error())
|
2021-05-23 15:50:21 +08:00
|
|
|
|
this.addError(err)
|
|
|
|
|
|
}
|
2020-09-27 15:26:06 +08:00
|
|
|
|
}
|
2021-06-06 23:42:11 +08:00
|
|
|
|
|
|
|
|
|
|
// 是否成功结束
|
2021-12-05 16:55:33 +08:00
|
|
|
|
if (err == nil || err == io.EOF) && (closeErr == nil || closeErr == io.EOF) {
|
2021-06-06 23:42:11 +08:00
|
|
|
|
this.writer.SetOk()
|
|
|
|
|
|
}
|
2022-08-03 19:33:50 +08:00
|
|
|
|
|
|
|
|
|
|
return
|
2020-09-26 19:54:26 +08:00
|
|
|
|
}
|