mirror of
				https://github.com/TeaOSLab/EdgeNode.git
				synced 2025-11-04 07:40:56 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			2079 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			2079 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package nodes
 | 
						||
 | 
						||
import (
 | 
						||
	"bytes"
 | 
						||
	"context"
 | 
						||
	"errors"
 | 
						||
	"fmt"
 | 
						||
	"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
 | 
						||
	iplib "github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
 | 
						||
	"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
 | 
						||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
 | 
						||
	teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
 | 
						||
	"github.com/TeaOSLab/EdgeNode/internal/metrics"
 | 
						||
	"github.com/TeaOSLab/EdgeNode/internal/stats"
 | 
						||
	"github.com/TeaOSLab/EdgeNode/internal/utils"
 | 
						||
	"github.com/TeaOSLab/EdgeNode/internal/utils/bytepool"
 | 
						||
	"github.com/iwind/TeaGo/lists"
 | 
						||
	"github.com/iwind/TeaGo/maps"
 | 
						||
	"github.com/iwind/TeaGo/types"
 | 
						||
	"io"
 | 
						||
	"net"
 | 
						||
	"net/http"
 | 
						||
	"net/url"
 | 
						||
	"os"
 | 
						||
	"path/filepath"
 | 
						||
	"strconv"
 | 
						||
	"strings"
 | 
						||
	"time"
 | 
						||
)
 | 
						||
 | 
						||
// 环境变量
 | 
						||
var HOSTNAME, _ = os.Hostname()
 | 
						||
 | 
						||
// errors
 | 
						||
var errWritingToClient = errors.New("writing to client error")
 | 
						||
 | 
						||
// HTTPRequest HTTP请求
 | 
						||
type HTTPRequest struct {
 | 
						||
	requestId string
 | 
						||
 | 
						||
	// 外部参数
 | 
						||
	RawReq        *http.Request
 | 
						||
	RawWriter     http.ResponseWriter
 | 
						||
	ReqServer     *serverconfigs.ServerConfig
 | 
						||
	ReqHost       string // 请求的Host
 | 
						||
	ServerName    string // 实际匹配到的Host
 | 
						||
	ServerAddr    string // 实际启动的服务器监听地址
 | 
						||
	IsHTTP        bool
 | 
						||
	IsHTTPS       bool
 | 
						||
	IsHTTP3       bool
 | 
						||
	isHealthCheck bool
 | 
						||
 | 
						||
	// 共享参数
 | 
						||
	// DO NOT change the variable after created
 | 
						||
	nodeConfig *nodeconfigs.NodeConfig
 | 
						||
 | 
						||
	// ln request
 | 
						||
	isLnRequest  bool
 | 
						||
	lnRemoteAddr string
 | 
						||
 | 
						||
	// 内部参数
 | 
						||
	isSubRequest         bool
 | 
						||
	writer               *HTTPWriter
 | 
						||
	web                  *serverconfigs.HTTPWebConfig      // Web配置,重要提示:由于引用了别的共享的配置,所以操作中只能读取不要修改
 | 
						||
	reverseProxyRef      *serverconfigs.ReverseProxyRef    // 反向代理引用
 | 
						||
	reverseProxy         *serverconfigs.ReverseProxyConfig // 反向代理配置,重要提示:由于引用了别的共享的配置,所以操作中只能读取不要修改
 | 
						||
	rawURI               string                            // 原始的URI
 | 
						||
	uri                  string                            // 经过rewrite等运算之后的URI
 | 
						||
	varMapping           map[string]string                 // 变量集合
 | 
						||
	requestFromTime      time.Time                         // 请求开始时间
 | 
						||
	requestCost          float64                           // 请求耗时
 | 
						||
	filePath             string                            // 请求的文件名,仅在读取Root目录下的内容时不为空
 | 
						||
	origin               *serverconfigs.OriginConfig       // 源站
 | 
						||
	originAddr           string                            // 源站实际地址
 | 
						||
	originStatus         int32                             // 源站响应代码
 | 
						||
	errors               []string                          // 错误信息
 | 
						||
	rewriteRule          *serverconfigs.HTTPRewriteRule    // 匹配到的重写规则
 | 
						||
	rewriteReplace       string                            // 重写规则的目标
 | 
						||
	rewriteIsExternalURL bool                              // 重写目标是否为外部URL
 | 
						||
	remoteAddr           string                            // 计算后的RemoteAddr
 | 
						||
 | 
						||
	cacheRef         *serverconfigs.HTTPCacheRef // 缓存设置
 | 
						||
	cacheKey         string                      // 缓存使用的Key
 | 
						||
	isCached         bool                        // 是否已经被缓存
 | 
						||
	cacheCanTryStale bool                        // 是否可以尝试使用Stale缓存
 | 
						||
 | 
						||
	isAttack        bool   // 是否是攻击请求
 | 
						||
	requestBodyData []byte // 读取的Body内容
 | 
						||
 | 
						||
	isWebsocketResponse bool // 是否为Websocket响应(非请求)
 | 
						||
 | 
						||
	// WAF相关
 | 
						||
	firewallPolicyId    int64
 | 
						||
	firewallRuleGroupId int64
 | 
						||
	firewallRuleSetId   int64
 | 
						||
	firewallRuleId      int64
 | 
						||
	firewallActions     []string
 | 
						||
	wafHasRequestBody   bool
 | 
						||
 | 
						||
	tags []string
 | 
						||
 | 
						||
	logAttrs map[string]string
 | 
						||
 | 
						||
	disableLog bool // 是否在当前请求中关闭Log
 | 
						||
	forceLog   bool // 是否强制记录日志
 | 
						||
 | 
						||
	disableMetrics bool // 不记录统计指标
 | 
						||
 | 
						||
	isHijacked bool
 | 
						||
 | 
						||
	// script相关操作
 | 
						||
	isDone bool
 | 
						||
}
 | 
						||
 | 
						||
// 初始化
 | 
						||
func (this *HTTPRequest) init() {
 | 
						||
	this.writer = NewHTTPWriter(this, this.RawWriter)
 | 
						||
	this.web = &serverconfigs.HTTPWebConfig{
 | 
						||
		IsOn: true,
 | 
						||
	}
 | 
						||
 | 
						||
	// this.uri = this.RawReq.URL.RequestURI()
 | 
						||
	// 之所以不使用RequestURI(),是不想让URL中的Path被Encode
 | 
						||
	var urlPath = this.RawReq.URL.Path
 | 
						||
	if this.ReqServer.Web != nil && this.ReqServer.Web.MergeSlashes {
 | 
						||
		urlPath = utils.CleanPath(urlPath)
 | 
						||
		this.web.MergeSlashes = true
 | 
						||
	}
 | 
						||
	if len(this.RawReq.URL.RawQuery) > 0 {
 | 
						||
		this.uri = urlPath + "?" + this.RawReq.URL.RawQuery
 | 
						||
	} else {
 | 
						||
		this.uri = urlPath
 | 
						||
	}
 | 
						||
 | 
						||
	this.rawURI = this.uri
 | 
						||
	this.varMapping = map[string]string{
 | 
						||
		// 缓存相关初始化
 | 
						||
		"cache.status":      "BYPASS",
 | 
						||
		"cache.age":         "0",
 | 
						||
		"cache.key":         "",
 | 
						||
		"cache.policy.name": "",
 | 
						||
		"cache.policy.id":   "0",
 | 
						||
		"cache.policy.type": "",
 | 
						||
	}
 | 
						||
	this.logAttrs = map[string]string{}
 | 
						||
	this.requestFromTime = time.Now()
 | 
						||
	this.requestId = httpRequestNextId()
 | 
						||
}
 | 
						||
 | 
						||
// Do 执行请求
 | 
						||
func (this *HTTPRequest) Do() {
 | 
						||
	// 初始化
 | 
						||
	this.init()
 | 
						||
 | 
						||
	// 当前服务的反向代理配置
 | 
						||
	if this.ReqServer.ReverseProxyRef != nil && this.ReqServer.ReverseProxy != nil {
 | 
						||
		this.reverseProxyRef = this.ReqServer.ReverseProxyRef
 | 
						||
		this.reverseProxy = this.ReqServer.ReverseProxy
 | 
						||
	}
 | 
						||
 | 
						||
	// Web配置
 | 
						||
	err := this.configureWeb(this.ReqServer.Web, true, 0)
 | 
						||
	if err != nil {
 | 
						||
		this.write50x(err, http.StatusInternalServerError, "Failed to configure the server", "配置服务失败", false)
 | 
						||
		this.doEnd()
 | 
						||
		return
 | 
						||
	}
 | 
						||
 | 
						||
	// 是否为低级别节点
 | 
						||
	this.isLnRequest = this.checkLnRequest()
 | 
						||
 | 
						||
	// 回调事件
 | 
						||
	this.onInit()
 | 
						||
	if this.writer.isFinished {
 | 
						||
		this.doEnd()
 | 
						||
		return
 | 
						||
	}
 | 
						||
 | 
						||
	// 处理健康检查
 | 
						||
	var healthCheckKey = this.RawReq.Header.Get(serverconfigs.HealthCheckHeaderName)
 | 
						||
	if len(healthCheckKey) > 0 {
 | 
						||
		if this.doHealthCheck(healthCheckKey, &this.isHealthCheck) {
 | 
						||
			this.doEnd()
 | 
						||
			return
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	if !this.isLnRequest {
 | 
						||
		// 特殊URL处理
 | 
						||
		if len(this.rawURI) > 1 && this.rawURI[1] == '.' {
 | 
						||
			// ACME
 | 
						||
			// TODO 需要配置是否启用ACME检测
 | 
						||
			if strings.HasPrefix(this.rawURI, "/.well-known/acme-challenge/") {
 | 
						||
				if this.doACME() {
 | 
						||
					this.doEnd()
 | 
						||
					return
 | 
						||
				}
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// 套餐
 | 
						||
		if this.ReqServer.UserPlan != nil {
 | 
						||
			if this.doPlanBefore() {
 | 
						||
				this.doEnd()
 | 
						||
				return
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// 流量限制
 | 
						||
		if this.ReqServer.TrafficLimitStatus != nil && this.ReqServer.TrafficLimitStatus.IsValid() {
 | 
						||
			if this.doTrafficLimit(this.ReqServer.TrafficLimitStatus) {
 | 
						||
				this.doEnd()
 | 
						||
				return
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// UAM
 | 
						||
		var uamIsCalled = false
 | 
						||
		if !this.isHealthCheck {
 | 
						||
			if this.web.UAM == nil && this.ReqServer.UAM != nil && this.ReqServer.UAM.IsOn {
 | 
						||
				this.web.UAM = this.ReqServer.UAM
 | 
						||
			}
 | 
						||
 | 
						||
			if this.web.UAM != nil && this.web.UAM.IsOn && this.isUAMRequest() {
 | 
						||
				uamIsCalled = true
 | 
						||
				if this.doUAM() {
 | 
						||
					this.doEnd()
 | 
						||
					return
 | 
						||
				}
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// WAF
 | 
						||
		if this.web.FirewallRef != nil && this.web.FirewallRef.IsOn {
 | 
						||
			if this.doWAFRequest() {
 | 
						||
				this.doEnd()
 | 
						||
				return
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// UAM
 | 
						||
		if !this.isHealthCheck && !uamIsCalled {
 | 
						||
			if this.web.UAM != nil && this.web.UAM.IsOn {
 | 
						||
				if this.doUAM() {
 | 
						||
					this.doEnd()
 | 
						||
					return
 | 
						||
				}
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// CC
 | 
						||
		if !this.isHealthCheck {
 | 
						||
			if this.web.CC != nil {
 | 
						||
				if this.web.CC.IsOn {
 | 
						||
					if this.doCC() {
 | 
						||
						this.doEnd()
 | 
						||
						return
 | 
						||
					}
 | 
						||
				}
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// 防盗链
 | 
						||
		if !this.isSubRequest && this.web.Referers != nil && this.web.Referers.IsOn {
 | 
						||
			if this.doCheckReferers() {
 | 
						||
				this.doEnd()
 | 
						||
				return
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// UA名单
 | 
						||
		if !this.isSubRequest && this.web.UserAgent != nil && this.web.UserAgent.IsOn {
 | 
						||
			if this.doCheckUserAgent() {
 | 
						||
				this.doEnd()
 | 
						||
				return
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// 访问控制
 | 
						||
		if !this.isSubRequest && this.web.Auth != nil && this.web.Auth.IsOn {
 | 
						||
			if this.doAuth() {
 | 
						||
				this.doEnd()
 | 
						||
				return
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// 自动跳转到HTTPS
 | 
						||
		if this.IsHTTP && this.web.RedirectToHttps != nil && this.web.RedirectToHttps.IsOn {
 | 
						||
			if this.doRedirectToHTTPS(this.web.RedirectToHttps) {
 | 
						||
				this.doEnd()
 | 
						||
				return
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// Compression
 | 
						||
		if this.web.Compression != nil && this.web.Compression.IsOn && this.web.Compression.Level > 0 {
 | 
						||
			this.writer.SetCompression(this.web.Compression)
 | 
						||
		}
 | 
						||
 | 
						||
		// HLS
 | 
						||
		if this.web.HLS != nil &&
 | 
						||
			this.web.HLS.Encrypting != nil &&
 | 
						||
			this.web.HLS.Encrypting.IsOn {
 | 
						||
			if this.processHLSBefore() {
 | 
						||
				this.doEnd()
 | 
						||
				return
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// 开始调用
 | 
						||
	this.doBegin()
 | 
						||
 | 
						||
	// 关闭写入
 | 
						||
	this.writer.Close()
 | 
						||
 | 
						||
	// 结束调用
 | 
						||
	this.doEnd()
 | 
						||
}
 | 
						||
 | 
						||
// 开始调用
 | 
						||
func (this *HTTPRequest) doBegin() {
 | 
						||
	// 是否找不到域名匹配
 | 
						||
	if this.ReqServer.Id == 0 {
 | 
						||
		this.doMismatch()
 | 
						||
		return
 | 
						||
	}
 | 
						||
 | 
						||
	if !this.isLnRequest {
 | 
						||
		// 处理request limit
 | 
						||
		if this.web.RequestLimit != nil &&
 | 
						||
			this.web.RequestLimit.IsOn {
 | 
						||
			if this.doRequestLimit() {
 | 
						||
				return
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// 处理requestBody
 | 
						||
		if this.RawReq.ContentLength > 0 &&
 | 
						||
			this.web.AccessLogRef != nil &&
 | 
						||
			this.web.AccessLogRef.IsOn &&
 | 
						||
			this.web.AccessLogRef.ContainsField(serverconfigs.HTTPAccessLogFieldRequestBody) {
 | 
						||
			var err error
 | 
						||
			this.requestBodyData, err = io.ReadAll(io.LimitReader(this.RawReq.Body, AccessLogMaxRequestBodySize))
 | 
						||
			if err != nil {
 | 
						||
				this.write50x(err, http.StatusBadGateway, "Failed to read request body for access log", "为访问日志读取请求Body失败", false)
 | 
						||
				return
 | 
						||
			}
 | 
						||
			this.RawReq.Body = io.NopCloser(io.MultiReader(bytes.NewBuffer(this.requestBodyData), this.RawReq.Body))
 | 
						||
		}
 | 
						||
 | 
						||
		// 跳转
 | 
						||
		if len(this.web.HostRedirects) > 0 {
 | 
						||
			if this.doHostRedirect() {
 | 
						||
				return
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// 临时关闭页面
 | 
						||
		if this.web.Shutdown != nil && this.web.Shutdown.IsOn {
 | 
						||
			this.doShutdown()
 | 
						||
			return
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// 缓存
 | 
						||
	if this.web.Cache != nil && this.web.Cache.IsOn {
 | 
						||
		if this.doCacheRead(false) {
 | 
						||
			return
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	if !this.isLnRequest {
 | 
						||
		// 重写规则
 | 
						||
		if this.rewriteRule != nil {
 | 
						||
			if this.doRewrite() {
 | 
						||
				return
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// Fastcgi
 | 
						||
		if this.web.FastcgiRef != nil && this.web.FastcgiRef.IsOn && len(this.web.FastcgiList) > 0 {
 | 
						||
			if this.doFastcgi() {
 | 
						||
				return
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// root
 | 
						||
		if this.web.Root != nil && this.web.Root.IsOn {
 | 
						||
			// 如果处理成功,则终止请求的处理
 | 
						||
			if this.doRoot() {
 | 
						||
				return
 | 
						||
			}
 | 
						||
 | 
						||
			// 如果明确设置了终止,则也会自动终止
 | 
						||
			if this.web.Root.IsBreak {
 | 
						||
				return
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// Reverse Proxy
 | 
						||
	if this.reverseProxyRef != nil && this.reverseProxyRef.IsOn && this.reverseProxy != nil && this.reverseProxy.IsOn {
 | 
						||
		_ = this.doReverseProxy(true)
 | 
						||
		return
 | 
						||
	}
 | 
						||
 | 
						||
	// 返回404页面
 | 
						||
	this.write404()
 | 
						||
}
 | 
						||
 | 
						||
// 结束调用
 | 
						||
func (this *HTTPRequest) doEnd() {
 | 
						||
	// 记录日志
 | 
						||
	this.log()
 | 
						||
 | 
						||
	// 流量统计
 | 
						||
	// TODO 增加是否开启开关
 | 
						||
	if this.ReqServer != nil && this.ReqServer.Id > 0 && !this.isHealthCheck /** 健康检查时不统计 **/ {
 | 
						||
		var totalBytes int64 = 0
 | 
						||
 | 
						||
		var requestConn = this.RawReq.Context().Value(HTTPConnContextKey)
 | 
						||
		if requestConn != nil {
 | 
						||
			requestClientConn, ok := requestConn.(ClientConnInterface)
 | 
						||
			if ok {
 | 
						||
				// 这里读取的其实是上一个请求消耗的流量,不是当前请求消耗的流量,只不过单个请求的流量统计不需要特别精确,整体趋于一致即可
 | 
						||
				totalBytes = requestClientConn.LastRequestBytes()
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		if totalBytes == 0 {
 | 
						||
			totalBytes = this.writer.SentBodyBytes() + this.writer.SentHeaderBytes()
 | 
						||
		}
 | 
						||
 | 
						||
		var countCached int64 = 0
 | 
						||
		var cachedBytes int64 = 0
 | 
						||
 | 
						||
		var countAttacks int64 = 0
 | 
						||
		var attackBytes int64 = 0
 | 
						||
 | 
						||
		var countWebsocketConnections int64 = 0
 | 
						||
 | 
						||
		if this.isCached {
 | 
						||
			countCached = 1
 | 
						||
			cachedBytes = totalBytes
 | 
						||
		}
 | 
						||
		if this.isAttack {
 | 
						||
			countAttacks = 1
 | 
						||
			attackBytes = this.CalculateSize()
 | 
						||
			if attackBytes < totalBytes {
 | 
						||
				attackBytes = totalBytes
 | 
						||
			}
 | 
						||
		}
 | 
						||
		if this.isWebsocketResponse {
 | 
						||
			countWebsocketConnections = 1
 | 
						||
		}
 | 
						||
 | 
						||
		stats.SharedTrafficStatManager.Add(this.ReqServer.UserId, this.ReqServer.Id, this.ReqHost, totalBytes, cachedBytes, 1, countCached, countAttacks, attackBytes, countWebsocketConnections, this.ReqServer.ShouldCheckTrafficLimit(), this.ReqServer.PlanId())
 | 
						||
 | 
						||
		// unique IP
 | 
						||
		stats.SharedDAUManager.AddIP(this.ReqServer.Id, this.requestRemoteAddr(true))
 | 
						||
 | 
						||
		// 指标
 | 
						||
		if !this.disableMetrics && metrics.SharedManager.HasHTTPMetrics() {
 | 
						||
			this.doMetricsResponse()
 | 
						||
		}
 | 
						||
 | 
						||
		// 统计
 | 
						||
		if this.web.StatRef != nil && this.web.StatRef.IsOn {
 | 
						||
			// 放到最后执行
 | 
						||
			this.doStat()
 | 
						||
		}
 | 
						||
	}
 | 
						||
}
 | 
						||
 | 
						||
// RawURI 原始的请求URI
 | 
						||
func (this *HTTPRequest) RawURI() string {
 | 
						||
	return this.rawURI
 | 
						||
}
 | 
						||
 | 
						||
// 配置
 | 
						||
func (this *HTTPRequest) configureWeb(web *serverconfigs.HTTPWebConfig, isTop bool, redirects int) error {
 | 
						||
	if web == nil || !web.IsOn {
 | 
						||
		return nil
 | 
						||
	}
 | 
						||
 | 
						||
	// 防止跳转次数过多
 | 
						||
	if redirects > 8 {
 | 
						||
		return errors.New("too many redirects")
 | 
						||
	}
 | 
						||
	redirects++
 | 
						||
 | 
						||
	// uri
 | 
						||
	rawPath := ""
 | 
						||
	rawQuery := ""
 | 
						||
	qIndex := strings.Index(this.uri, "?") // question mark index
 | 
						||
	if qIndex > -1 {
 | 
						||
		rawPath = this.uri[:qIndex]
 | 
						||
		rawQuery = this.uri[qIndex+1:]
 | 
						||
	} else {
 | 
						||
		rawPath = this.uri
 | 
						||
	}
 | 
						||
 | 
						||
	// redirect
 | 
						||
	if web.RedirectToHttps != nil && (web.RedirectToHttps.IsPrior || isTop) {
 | 
						||
		this.web.RedirectToHttps = web.RedirectToHttps
 | 
						||
	}
 | 
						||
 | 
						||
	// pages
 | 
						||
	this.web.EnableGlobalPages = web.EnableGlobalPages
 | 
						||
 | 
						||
	if len(web.Pages) > 0 {
 | 
						||
		this.web.Pages = web.Pages
 | 
						||
	}
 | 
						||
 | 
						||
	// shutdown
 | 
						||
	if web.Shutdown != nil && (web.Shutdown.IsPrior || isTop) {
 | 
						||
		this.web.Shutdown = web.Shutdown
 | 
						||
	}
 | 
						||
 | 
						||
	// headers
 | 
						||
	if web.RequestHeaderPolicyRef != nil && (web.RequestHeaderPolicyRef.IsPrior || isTop) && web.RequestHeaderPolicy != nil {
 | 
						||
		// TODO 现在是只能选一个有效的设置,未来可以选择是否合并多级别的设置
 | 
						||
		this.web.RequestHeaderPolicy = web.RequestHeaderPolicy
 | 
						||
	}
 | 
						||
	if web.ResponseHeaderPolicyRef != nil && (web.ResponseHeaderPolicyRef.IsPrior || isTop) && web.ResponseHeaderPolicy != nil {
 | 
						||
		// TODO 现在是只能选一个有效的设置,未来可以选择是否合并多级别的设置
 | 
						||
		this.web.ResponseHeaderPolicy = web.ResponseHeaderPolicy
 | 
						||
	}
 | 
						||
 | 
						||
	// root
 | 
						||
	if web.Root != nil && (web.Root.IsPrior || isTop) {
 | 
						||
		this.web.Root = web.Root
 | 
						||
	}
 | 
						||
 | 
						||
	// remote addr
 | 
						||
	if web.RemoteAddr != nil && (web.RemoteAddr.IsPrior || isTop) && web.RemoteAddr.IsOn {
 | 
						||
		this.web.RemoteAddr = web.RemoteAddr
 | 
						||
 | 
						||
		// check if from proxy
 | 
						||
		if len(this.web.RemoteAddr.Value) > 0 && this.web.RemoteAddr.Value != "${rawRemoteAddr}" {
 | 
						||
			var requestConn = this.RawReq.Context().Value(HTTPConnContextKey)
 | 
						||
			if requestConn != nil {
 | 
						||
				requestClientConn, ok := requestConn.(ClientConnInterface)
 | 
						||
				if ok {
 | 
						||
					requestClientConn.SetIsPersistent(true)
 | 
						||
				}
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// charset
 | 
						||
	if web.Charset != nil && (web.Charset.IsPrior || isTop) {
 | 
						||
		this.web.Charset = web.Charset
 | 
						||
	}
 | 
						||
 | 
						||
	// websocket
 | 
						||
	if web.WebsocketRef != nil && (web.WebsocketRef.IsPrior || isTop) {
 | 
						||
		this.web.WebsocketRef = web.WebsocketRef
 | 
						||
		this.web.Websocket = web.Websocket
 | 
						||
	}
 | 
						||
 | 
						||
	// compression
 | 
						||
	if web.Compression != nil && (web.Compression.IsPrior || isTop) {
 | 
						||
		this.web.Compression = web.Compression
 | 
						||
	}
 | 
						||
 | 
						||
	// optimizer
 | 
						||
	if web.Optimization != nil && (web.Optimization.IsPrior || (isTop && web.Optimization.IsOn())) {
 | 
						||
		this.web.Optimization = web.Optimization
 | 
						||
	}
 | 
						||
 | 
						||
	// webp
 | 
						||
	if web.WebP != nil && (web.WebP.IsPrior || isTop) {
 | 
						||
		this.web.WebP = web.WebP
 | 
						||
	}
 | 
						||
 | 
						||
	// cache
 | 
						||
	if web.Cache != nil && (web.Cache.IsPrior || isTop) {
 | 
						||
		this.web.Cache = web.Cache
 | 
						||
	}
 | 
						||
 | 
						||
	// waf
 | 
						||
	if web.FirewallRef != nil && (web.FirewallRef.IsPrior || isTop) {
 | 
						||
		this.web.FirewallRef = web.FirewallRef
 | 
						||
		if web.FirewallPolicy != nil {
 | 
						||
			this.web.FirewallPolicy = web.FirewallPolicy
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// access log
 | 
						||
	if web.AccessLogRef != nil && (web.AccessLogRef.IsPrior || isTop) {
 | 
						||
		this.web.AccessLogRef = web.AccessLogRef
 | 
						||
	}
 | 
						||
 | 
						||
	// host redirects
 | 
						||
	if len(web.HostRedirects) > 0 {
 | 
						||
		this.web.HostRedirects = web.HostRedirects
 | 
						||
	}
 | 
						||
 | 
						||
	// stat
 | 
						||
	if web.StatRef != nil && (web.StatRef.IsPrior || isTop) {
 | 
						||
		this.web.StatRef = web.StatRef
 | 
						||
	}
 | 
						||
 | 
						||
	// fastcgi
 | 
						||
	if web.FastcgiRef != nil && (web.FastcgiRef.IsPrior || isTop) {
 | 
						||
		this.web.FastcgiRef = web.FastcgiRef
 | 
						||
		this.web.FastcgiList = web.FastcgiList
 | 
						||
	}
 | 
						||
 | 
						||
	// auth
 | 
						||
	if web.Auth != nil && (web.Auth.IsPrior || isTop) {
 | 
						||
		this.web.Auth = web.Auth
 | 
						||
	}
 | 
						||
 | 
						||
	// referers
 | 
						||
	if web.Referers != nil && (web.Referers.IsPrior || isTop) {
 | 
						||
		this.web.Referers = web.Referers
 | 
						||
	}
 | 
						||
 | 
						||
	// user agent
 | 
						||
	if web.UserAgent != nil && (web.UserAgent.IsPrior || isTop) {
 | 
						||
		this.web.UserAgent = web.UserAgent
 | 
						||
	}
 | 
						||
 | 
						||
	// request limit
 | 
						||
	if web.RequestLimit != nil && (web.RequestLimit.IsPrior || isTop) {
 | 
						||
		this.web.RequestLimit = web.RequestLimit
 | 
						||
	}
 | 
						||
 | 
						||
	// request scripts
 | 
						||
	if web.RequestScripts != nil {
 | 
						||
		if this.web.RequestScripts == nil {
 | 
						||
			this.web.RequestScripts = &serverconfigs.HTTPRequestScriptsConfig{
 | 
						||
				InitGroup:    web.RequestScripts.InitGroup,
 | 
						||
				RequestGroup: web.RequestScripts.RequestGroup,
 | 
						||
			} // 不要直接赋值,需要复制,防止在运行时被修改
 | 
						||
		} else {
 | 
						||
			if web.RequestScripts.InitGroup != nil && (web.RequestScripts.InitGroup.IsPrior || isTop) {
 | 
						||
				if this.web.RequestScripts == nil {
 | 
						||
					this.web.RequestScripts = &serverconfigs.HTTPRequestScriptsConfig{}
 | 
						||
				}
 | 
						||
				this.web.RequestScripts.InitGroup = web.RequestScripts.InitGroup
 | 
						||
			}
 | 
						||
			if web.RequestScripts.RequestGroup != nil && (web.RequestScripts.RequestGroup.IsPrior || isTop) {
 | 
						||
				if this.web.RequestScripts == nil {
 | 
						||
					this.web.RequestScripts = &serverconfigs.HTTPRequestScriptsConfig{}
 | 
						||
				}
 | 
						||
				this.web.RequestScripts.RequestGroup = web.RequestScripts.RequestGroup
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// UAM
 | 
						||
	if web.UAM != nil && (web.UAM.IsPrior || isTop) {
 | 
						||
		this.web.UAM = web.UAM
 | 
						||
	}
 | 
						||
 | 
						||
	// CC
 | 
						||
	if web.CC != nil && (web.CC.IsPrior || isTop) {
 | 
						||
		this.web.CC = web.CC
 | 
						||
	}
 | 
						||
 | 
						||
	// HLS
 | 
						||
	if web.HLS != nil && (web.HLS.IsPrior || isTop) {
 | 
						||
		this.web.HLS = web.HLS
 | 
						||
	}
 | 
						||
 | 
						||
	// 重写规则
 | 
						||
	if len(web.RewriteRefs) > 0 {
 | 
						||
		for index, ref := range web.RewriteRefs {
 | 
						||
			if !ref.IsOn {
 | 
						||
				continue
 | 
						||
			}
 | 
						||
			rewriteRule := web.RewriteRules[index]
 | 
						||
			if !rewriteRule.IsOn {
 | 
						||
				continue
 | 
						||
			}
 | 
						||
			if replace, varMapping, isMatched := rewriteRule.MatchRequest(rawPath, this.Format); isMatched {
 | 
						||
				this.addVarMapping(varMapping)
 | 
						||
				this.rewriteRule = rewriteRule
 | 
						||
 | 
						||
				if rewriteRule.WithQuery {
 | 
						||
					queryIndex := strings.Index(replace, "?")
 | 
						||
					if queryIndex > -1 {
 | 
						||
						if len(rawQuery) > 0 {
 | 
						||
							replace = replace[:queryIndex] + "?" + rawQuery + "&" + replace[queryIndex+1:]
 | 
						||
						}
 | 
						||
					} else {
 | 
						||
						if len(rawQuery) > 0 {
 | 
						||
							replace += "?" + rawQuery
 | 
						||
						}
 | 
						||
					}
 | 
						||
				}
 | 
						||
 | 
						||
				this.rewriteReplace = replace
 | 
						||
 | 
						||
				// 如果是外部URL直接返回
 | 
						||
				if rewriteRule.IsExternalURL(replace) {
 | 
						||
					this.rewriteIsExternalURL = true
 | 
						||
					return nil
 | 
						||
				}
 | 
						||
 | 
						||
				// 如果是内部URL继续解析
 | 
						||
				if replace == this.uri {
 | 
						||
					// URL不变,则停止解析,防止无限循环跳转
 | 
						||
					return nil
 | 
						||
				}
 | 
						||
				this.uri = replace
 | 
						||
 | 
						||
				// 终止解析的几个个条件:
 | 
						||
				//    isBreak = true
 | 
						||
				//    mode = redirect
 | 
						||
				//    replace = external url
 | 
						||
				//    replace = uri
 | 
						||
				if rewriteRule.IsBreak || rewriteRule.Mode == serverconfigs.HTTPRewriteModeRedirect {
 | 
						||
					return nil
 | 
						||
				}
 | 
						||
 | 
						||
				return this.configureWeb(web, isTop, redirects+1)
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// locations
 | 
						||
	if len(web.LocationRefs) > 0 {
 | 
						||
		var resultLocation *serverconfigs.HTTPLocationConfig
 | 
						||
		for index, ref := range web.LocationRefs {
 | 
						||
			if !ref.IsOn {
 | 
						||
				continue
 | 
						||
			}
 | 
						||
			location := web.Locations[index]
 | 
						||
			if !location.IsOn {
 | 
						||
				continue
 | 
						||
			}
 | 
						||
			if varMapping, isMatched := location.Match(rawPath, this.Format); isMatched {
 | 
						||
				// 检查专属域名
 | 
						||
				if len(location.Domains) > 0 && !configutils.MatchDomains(location.Domains, this.ReqHost) {
 | 
						||
					continue
 | 
						||
				}
 | 
						||
 | 
						||
				if len(varMapping) > 0 {
 | 
						||
					this.addVarMapping(varMapping)
 | 
						||
				}
 | 
						||
				resultLocation = location
 | 
						||
 | 
						||
				if location.IsBreak {
 | 
						||
					break
 | 
						||
				}
 | 
						||
			}
 | 
						||
		}
 | 
						||
		if resultLocation != nil {
 | 
						||
			// reset rewrite rule
 | 
						||
			this.rewriteRule = nil
 | 
						||
 | 
						||
			// Reverse Proxy
 | 
						||
			if resultLocation.ReverseProxyRef != nil && resultLocation.ReverseProxyRef.IsPrior {
 | 
						||
				this.reverseProxyRef = resultLocation.ReverseProxyRef
 | 
						||
				this.reverseProxy = resultLocation.ReverseProxy
 | 
						||
			}
 | 
						||
 | 
						||
			// Web
 | 
						||
			if resultLocation.Web != nil {
 | 
						||
				err := this.configureWeb(resultLocation.Web, false, redirects+1)
 | 
						||
				if err != nil {
 | 
						||
					return err
 | 
						||
				}
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	return nil
 | 
						||
}
 | 
						||
 | 
						||
// Format 利用请求参数格式化字符串
 | 
						||
func (this *HTTPRequest) Format(source string) string {
 | 
						||
	if len(source) == 0 {
 | 
						||
		return ""
 | 
						||
	}
 | 
						||
 | 
						||
	var hasVarMapping = len(this.varMapping) > 0
 | 
						||
 | 
						||
	return configutils.ParseVariables(source, func(varName string) string {
 | 
						||
		// 自定义变量
 | 
						||
		if hasVarMapping {
 | 
						||
			value, found := this.varMapping[varName]
 | 
						||
			if found {
 | 
						||
				return value
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// 请求变量
 | 
						||
		switch varName {
 | 
						||
		case "edgeVersion":
 | 
						||
			return teaconst.Version
 | 
						||
		case "remoteAddr":
 | 
						||
			return this.requestRemoteAddr(true)
 | 
						||
		case "remoteAddrValue":
 | 
						||
			return this.requestRemoteAddr(false)
 | 
						||
		case "rawRemoteAddr":
 | 
						||
			var addr = this.RawReq.RemoteAddr
 | 
						||
			host, _, err := net.SplitHostPort(addr)
 | 
						||
			if err == nil {
 | 
						||
				addr = host
 | 
						||
			}
 | 
						||
			return addr
 | 
						||
		case "remotePort":
 | 
						||
			return strconv.Itoa(this.requestRemotePort())
 | 
						||
		case "remoteUser":
 | 
						||
			return this.requestRemoteUser()
 | 
						||
		case "requestId":
 | 
						||
			return this.requestId
 | 
						||
		case "requestURI", "requestUri":
 | 
						||
			return this.rawURI
 | 
						||
		case "requestURL":
 | 
						||
			var scheme = "http"
 | 
						||
			if this.IsHTTPS {
 | 
						||
				scheme = "https"
 | 
						||
			}
 | 
						||
			return scheme + "://" + this.ReqHost + this.rawURI
 | 
						||
		case "requestPath":
 | 
						||
			return this.Path()
 | 
						||
		case "requestPathExtension":
 | 
						||
			return filepath.Ext(this.Path())
 | 
						||
		case "requestPathLowerExtension":
 | 
						||
			return strings.ToLower(filepath.Ext(this.Path()))
 | 
						||
		case "requestLength":
 | 
						||
			return strconv.FormatInt(this.requestLength(), 10)
 | 
						||
		case "requestTime":
 | 
						||
			return fmt.Sprintf("%.6f", this.requestCost)
 | 
						||
		case "requestMethod":
 | 
						||
			if len(this.RawReq.Method) == 0 {
 | 
						||
				return http.MethodGet
 | 
						||
			}
 | 
						||
			return this.RawReq.Method
 | 
						||
		case "requestFilename":
 | 
						||
			filename := this.requestFilename()
 | 
						||
			if len(filename) > 0 {
 | 
						||
				return filename
 | 
						||
			}
 | 
						||
 | 
						||
			if this.web.Root != nil && this.web.Root.IsOn {
 | 
						||
				return filepath.Clean(this.web.Root.Dir + this.Path())
 | 
						||
			}
 | 
						||
 | 
						||
			return ""
 | 
						||
		case "scheme":
 | 
						||
			if this.IsHTTP {
 | 
						||
				return "http"
 | 
						||
			} else {
 | 
						||
				return "https"
 | 
						||
			}
 | 
						||
		case "serverProtocol", "proto":
 | 
						||
			return this.RawReq.Proto
 | 
						||
		case "bytesSent":
 | 
						||
			return strconv.FormatInt(this.writer.SentBodyBytes(), 10) // TODO 加上Header长度
 | 
						||
		case "bodyBytesSent":
 | 
						||
			return strconv.FormatInt(this.writer.SentBodyBytes(), 10)
 | 
						||
		case "status":
 | 
						||
			return strconv.Itoa(this.writer.StatusCode())
 | 
						||
		case "statusMessage":
 | 
						||
			return http.StatusText(this.writer.StatusCode())
 | 
						||
		case "timeISO8601":
 | 
						||
			return this.requestFromTime.Format("2006-01-02T15:04:05.000Z07:00")
 | 
						||
		case "timeLocal":
 | 
						||
			return this.requestFromTime.Format("2/Jan/2006:15:04:05 -0700")
 | 
						||
		case "msec":
 | 
						||
			return fmt.Sprintf("%.6f", float64(this.requestFromTime.Unix())+float64(this.requestFromTime.Nanosecond())/1000000000)
 | 
						||
		case "timestamp":
 | 
						||
			return strconv.FormatInt(this.requestFromTime.Unix(), 10)
 | 
						||
		case "host":
 | 
						||
			return this.ReqHost
 | 
						||
		case "cname":
 | 
						||
			return this.ReqServer.CNameDomain
 | 
						||
		case "referer":
 | 
						||
			return this.RawReq.Referer()
 | 
						||
		case "referer.host":
 | 
						||
			u, err := url.Parse(this.RawReq.Referer())
 | 
						||
			if err == nil {
 | 
						||
				return u.Host
 | 
						||
			}
 | 
						||
			return ""
 | 
						||
		case "userAgent":
 | 
						||
			return this.RawReq.UserAgent()
 | 
						||
		case "contentType":
 | 
						||
			return this.requestContentType()
 | 
						||
		case "request":
 | 
						||
			return this.requestString()
 | 
						||
		case "cookies":
 | 
						||
			return this.requestCookiesString()
 | 
						||
		case "isArgs":
 | 
						||
			if strings.Contains(this.uri, "?") {
 | 
						||
				return "?"
 | 
						||
			}
 | 
						||
			return ""
 | 
						||
		case "args", "queryString":
 | 
						||
			return this.requestQueryString()
 | 
						||
		case "headers":
 | 
						||
			return this.requestHeadersString()
 | 
						||
		case "serverName":
 | 
						||
			return this.ServerName
 | 
						||
		case "serverAddr":
 | 
						||
			var nodeConfig = this.nodeConfig
 | 
						||
			if nodeConfig != nil && nodeConfig.GlobalServerConfig != nil && nodeConfig.GlobalServerConfig.HTTPAll.EnableServerAddrVariable {
 | 
						||
				if len(this.requestRemoteAddrs()) > 1 {
 | 
						||
					return "" // hidden for security
 | 
						||
				}
 | 
						||
				var requestConn = this.RawReq.Context().Value(HTTPConnContextKey)
 | 
						||
				if requestConn != nil {
 | 
						||
					conn, ok := requestConn.(net.Conn)
 | 
						||
					if ok {
 | 
						||
						host, _, _ := net.SplitHostPort(conn.LocalAddr().String())
 | 
						||
						if len(host) > 0 {
 | 
						||
							return host
 | 
						||
						}
 | 
						||
					}
 | 
						||
				}
 | 
						||
			}
 | 
						||
			return ""
 | 
						||
		case "serverPort":
 | 
						||
			return strconv.Itoa(this.requestServerPort())
 | 
						||
		case "hostname":
 | 
						||
			return HOSTNAME
 | 
						||
		case "documentRoot":
 | 
						||
			if this.web.Root != nil {
 | 
						||
				return this.web.Root.Dir
 | 
						||
			}
 | 
						||
			return ""
 | 
						||
		}
 | 
						||
 | 
						||
		dotIndex := strings.Index(varName, ".")
 | 
						||
		if dotIndex < 0 {
 | 
						||
			return "${" + varName + "}"
 | 
						||
		}
 | 
						||
		prefix := varName[:dotIndex]
 | 
						||
		suffix := varName[dotIndex+1:]
 | 
						||
 | 
						||
		// cookie.
 | 
						||
		if prefix == "cookie" {
 | 
						||
			return this.requestCookie(suffix)
 | 
						||
		}
 | 
						||
 | 
						||
		// arg.
 | 
						||
		if prefix == "arg" {
 | 
						||
			return this.requestQueryParam(suffix)
 | 
						||
		}
 | 
						||
 | 
						||
		// header.
 | 
						||
		if prefix == "header" || prefix == "http" {
 | 
						||
			return this.requestHeader(suffix)
 | 
						||
		}
 | 
						||
 | 
						||
		// response.
 | 
						||
		if prefix == "response" {
 | 
						||
			switch suffix {
 | 
						||
			case "contentType":
 | 
						||
				return this.writer.Header().Get("Content-Type")
 | 
						||
			}
 | 
						||
 | 
						||
			// response.xxx.xxx
 | 
						||
			dotIndex = strings.Index(suffix, ".")
 | 
						||
			if dotIndex < 0 {
 | 
						||
				return "${" + varName + "}"
 | 
						||
			}
 | 
						||
			switch suffix[:dotIndex] {
 | 
						||
			case "header":
 | 
						||
				var headers = this.writer.Header()
 | 
						||
				var headerKey = suffix[dotIndex+1:]
 | 
						||
				v, found := headers[headerKey]
 | 
						||
				if found {
 | 
						||
					if len(v) == 0 {
 | 
						||
						return ""
 | 
						||
					}
 | 
						||
					return v[0]
 | 
						||
				}
 | 
						||
				var canonicalHeaderKey = http.CanonicalHeaderKey(headerKey)
 | 
						||
				if canonicalHeaderKey != headerKey {
 | 
						||
					v = headers[canonicalHeaderKey]
 | 
						||
					if len(v) > 0 {
 | 
						||
						return v[0]
 | 
						||
					}
 | 
						||
				}
 | 
						||
				return ""
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// origin.
 | 
						||
		if prefix == "origin" {
 | 
						||
			if this.origin != nil {
 | 
						||
				switch suffix {
 | 
						||
				case "address", "addr":
 | 
						||
					return this.originAddr
 | 
						||
				case "host":
 | 
						||
					addr := this.originAddr
 | 
						||
					index := strings.Index(addr, ":")
 | 
						||
					if index > -1 {
 | 
						||
						return addr[:index]
 | 
						||
					} else {
 | 
						||
						return ""
 | 
						||
					}
 | 
						||
				case "id":
 | 
						||
					return strconv.FormatInt(this.origin.Id, 10)
 | 
						||
				case "scheme", "protocol":
 | 
						||
					return this.origin.Addr.Protocol.String()
 | 
						||
				case "code":
 | 
						||
					return this.origin.Code
 | 
						||
				}
 | 
						||
			}
 | 
						||
			return ""
 | 
						||
		}
 | 
						||
 | 
						||
		// node
 | 
						||
		if prefix == "node" {
 | 
						||
			switch suffix {
 | 
						||
			case "id":
 | 
						||
				return strconv.FormatInt(this.nodeConfig.Id, 10)
 | 
						||
			case "name":
 | 
						||
				return this.nodeConfig.Name
 | 
						||
			case "role":
 | 
						||
				return teaconst.Role
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// host
 | 
						||
		if prefix == "host" {
 | 
						||
			pieces := strings.Split(this.ReqHost, ".")
 | 
						||
			switch suffix {
 | 
						||
			case "first":
 | 
						||
				if len(pieces) > 0 {
 | 
						||
					return pieces[0]
 | 
						||
				}
 | 
						||
				return ""
 | 
						||
			case "last":
 | 
						||
				if len(pieces) > 0 {
 | 
						||
					return pieces[len(pieces)-1]
 | 
						||
				}
 | 
						||
				return ""
 | 
						||
			case "0":
 | 
						||
				if len(pieces) > 0 {
 | 
						||
					return pieces[0]
 | 
						||
				}
 | 
						||
				return ""
 | 
						||
			case "1":
 | 
						||
				if len(pieces) > 1 {
 | 
						||
					return pieces[1]
 | 
						||
				}
 | 
						||
				return ""
 | 
						||
			case "2":
 | 
						||
				if len(pieces) > 2 {
 | 
						||
					return pieces[2]
 | 
						||
				}
 | 
						||
				return ""
 | 
						||
			case "3":
 | 
						||
				if len(pieces) > 3 {
 | 
						||
					return pieces[3]
 | 
						||
				}
 | 
						||
				return ""
 | 
						||
			case "4":
 | 
						||
				if len(pieces) > 4 {
 | 
						||
					return pieces[4]
 | 
						||
				}
 | 
						||
				return ""
 | 
						||
			case "-1":
 | 
						||
				if len(pieces) > 0 {
 | 
						||
					return pieces[len(pieces)-1]
 | 
						||
				}
 | 
						||
				return ""
 | 
						||
			case "-2":
 | 
						||
				if len(pieces) > 1 {
 | 
						||
					return pieces[len(pieces)-2]
 | 
						||
				}
 | 
						||
				return ""
 | 
						||
			case "-3":
 | 
						||
				if len(pieces) > 2 {
 | 
						||
					return pieces[len(pieces)-3]
 | 
						||
				}
 | 
						||
				return ""
 | 
						||
			case "-4":
 | 
						||
				if len(pieces) > 3 {
 | 
						||
					return pieces[len(pieces)-4]
 | 
						||
				}
 | 
						||
				return ""
 | 
						||
			case "-5":
 | 
						||
				if len(pieces) > 4 {
 | 
						||
					return pieces[len(pieces)-5]
 | 
						||
				}
 | 
						||
				return ""
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// geo
 | 
						||
		if prefix == "geo" {
 | 
						||
			var result = iplib.LookupIP(this.requestRemoteAddr(true))
 | 
						||
 | 
						||
			switch suffix {
 | 
						||
			case "country.name":
 | 
						||
				if result != nil && result.IsOk() {
 | 
						||
					return result.CountryName()
 | 
						||
				}
 | 
						||
				return ""
 | 
						||
			case "country.id":
 | 
						||
				if result != nil && result.IsOk() {
 | 
						||
					return types.String(result.CountryId())
 | 
						||
				}
 | 
						||
				return "0"
 | 
						||
			case "province.name":
 | 
						||
				if result != nil && result.IsOk() {
 | 
						||
					return result.ProvinceName()
 | 
						||
				}
 | 
						||
				return ""
 | 
						||
			case "province.id":
 | 
						||
				if result != nil && result.IsOk() {
 | 
						||
					return types.String(result.ProvinceId())
 | 
						||
				}
 | 
						||
				return "0"
 | 
						||
			case "city.name":
 | 
						||
				if result != nil && result.IsOk() {
 | 
						||
					return result.CityName()
 | 
						||
				}
 | 
						||
				return ""
 | 
						||
			case "city.id":
 | 
						||
				if result != nil && result.IsOk() {
 | 
						||
					return types.String(result.CityId())
 | 
						||
				}
 | 
						||
				return "0"
 | 
						||
			case "town.name":
 | 
						||
				if result != nil && result.IsOk() {
 | 
						||
					return result.TownName()
 | 
						||
				}
 | 
						||
				return ""
 | 
						||
			case "town.id":
 | 
						||
				if result != nil && result.IsOk() {
 | 
						||
					return types.String(result.TownId())
 | 
						||
				}
 | 
						||
				return "0"
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// ips
 | 
						||
		if prefix == "isp" {
 | 
						||
			var result = iplib.LookupIP(this.requestRemoteAddr(true))
 | 
						||
 | 
						||
			switch suffix {
 | 
						||
			case "name":
 | 
						||
				if result != nil && result.IsOk() {
 | 
						||
					return result.ProviderName()
 | 
						||
				}
 | 
						||
			case "id":
 | 
						||
				if result != nil && result.IsOk() {
 | 
						||
					return types.String(result.ProviderId())
 | 
						||
				}
 | 
						||
				return "0"
 | 
						||
			}
 | 
						||
			return ""
 | 
						||
		}
 | 
						||
 | 
						||
		// browser
 | 
						||
		if prefix == "browser" {
 | 
						||
			var result = stats.SharedUserAgentParser.Parse(this.RawReq.UserAgent())
 | 
						||
			switch suffix {
 | 
						||
			case "os.name":
 | 
						||
				return result.OS.Name
 | 
						||
			case "os.version":
 | 
						||
				return result.OS.Version
 | 
						||
			case "name":
 | 
						||
				return result.BrowserName
 | 
						||
			case "version":
 | 
						||
				return result.BrowserVersion
 | 
						||
			case "isMobile":
 | 
						||
				if result.IsMobile {
 | 
						||
					return "1"
 | 
						||
				} else {
 | 
						||
					return "0"
 | 
						||
				}
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// product
 | 
						||
		if prefix == "product" {
 | 
						||
			switch suffix {
 | 
						||
			case "name":
 | 
						||
				if this.nodeConfig.ProductConfig != nil && len(this.nodeConfig.ProductConfig.Name) > 0 {
 | 
						||
					return this.nodeConfig.ProductConfig.Name
 | 
						||
				}
 | 
						||
				return teaconst.GlobalProductName
 | 
						||
			case "version":
 | 
						||
				if this.nodeConfig.ProductConfig != nil && len(this.nodeConfig.ProductConfig.Version) > 0 {
 | 
						||
					return this.nodeConfig.ProductConfig.Version
 | 
						||
				}
 | 
						||
				return teaconst.Version
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		return "${" + varName + "}"
 | 
						||
	})
 | 
						||
}
 | 
						||
 | 
						||
// 添加变量定义
 | 
						||
func (this *HTTPRequest) addVarMapping(varMapping map[string]string) {
 | 
						||
	for k, v := range varMapping {
 | 
						||
		this.varMapping[k] = v
 | 
						||
	}
 | 
						||
}
 | 
						||
 | 
						||
// 获取请求的客户端地址
 | 
						||
func (this *HTTPRequest) requestRemoteAddr(supportVar bool) string {
 | 
						||
	if len(this.lnRemoteAddr) > 0 {
 | 
						||
		return this.lnRemoteAddr
 | 
						||
	}
 | 
						||
 | 
						||
	if supportVar && len(this.remoteAddr) > 0 {
 | 
						||
		return this.remoteAddr
 | 
						||
	}
 | 
						||
 | 
						||
	if supportVar &&
 | 
						||
		this.web.RemoteAddr != nil &&
 | 
						||
		this.web.RemoteAddr.IsOn &&
 | 
						||
		!this.web.RemoteAddr.IsEmpty() {
 | 
						||
		if this.web.RemoteAddr.HasValues() { // multiple values
 | 
						||
			for _, value := range this.web.RemoteAddr.Values() {
 | 
						||
				var remoteAddr = this.Format(value)
 | 
						||
				if len(remoteAddr) > 0 && net.ParseIP(remoteAddr) != nil {
 | 
						||
					this.remoteAddr = remoteAddr
 | 
						||
					return remoteAddr
 | 
						||
				}
 | 
						||
			}
 | 
						||
		} else { // single value
 | 
						||
			var remoteAddr = this.Format(this.web.RemoteAddr.Value)
 | 
						||
			if len(remoteAddr) > 0 && net.ParseIP(remoteAddr) != nil {
 | 
						||
				this.remoteAddr = remoteAddr
 | 
						||
				return remoteAddr
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// 如果是从Header中读取,则直接返回原始IP
 | 
						||
		if this.web.RemoteAddr.Type == serverconfigs.HTTPRemoteAddrTypeRequestHeader {
 | 
						||
			var remoteAddr = this.RawReq.RemoteAddr
 | 
						||
			host, _, err := net.SplitHostPort(remoteAddr)
 | 
						||
			if err == nil {
 | 
						||
				this.remoteAddr = host
 | 
						||
				return host
 | 
						||
			} else {
 | 
						||
				return remoteAddr
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// X-Forwarded-For
 | 
						||
	var forwardedFor = this.RawReq.Header.Get("X-Forwarded-For")
 | 
						||
	if len(forwardedFor) > 0 {
 | 
						||
		commaIndex := strings.Index(forwardedFor, ",")
 | 
						||
		if commaIndex > 0 {
 | 
						||
			forwardedFor = forwardedFor[:commaIndex]
 | 
						||
		}
 | 
						||
		if net.ParseIP(forwardedFor) != nil {
 | 
						||
			if supportVar {
 | 
						||
				this.remoteAddr = forwardedFor
 | 
						||
			}
 | 
						||
			return forwardedFor
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// Real-IP
 | 
						||
	{
 | 
						||
		realIP, ok := this.RawReq.Header["X-Real-IP"]
 | 
						||
		if ok && len(realIP) > 0 {
 | 
						||
			if net.ParseIP(realIP[0]) != nil {
 | 
						||
				if supportVar {
 | 
						||
					this.remoteAddr = realIP[0]
 | 
						||
				}
 | 
						||
				return realIP[0]
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// Real-Ip
 | 
						||
	{
 | 
						||
		realIP, ok := this.RawReq.Header["X-Real-Ip"]
 | 
						||
		if ok && len(realIP) > 0 {
 | 
						||
			if net.ParseIP(realIP[0]) != nil {
 | 
						||
				if supportVar {
 | 
						||
					this.remoteAddr = realIP[0]
 | 
						||
				}
 | 
						||
				return realIP[0]
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// Remote-Addr
 | 
						||
	var remoteAddr = this.RawReq.RemoteAddr
 | 
						||
	host, _, err := net.SplitHostPort(remoteAddr)
 | 
						||
	if err == nil {
 | 
						||
		if supportVar {
 | 
						||
			this.remoteAddr = host
 | 
						||
		}
 | 
						||
		return host
 | 
						||
	} else {
 | 
						||
		return remoteAddr
 | 
						||
	}
 | 
						||
}
 | 
						||
 | 
						||
// 获取请求的客户端地址列表
 | 
						||
func (this *HTTPRequest) requestRemoteAddrs() (result []string) {
 | 
						||
	result = append(result, this.requestRemoteAddr(true))
 | 
						||
 | 
						||
	// X-Forwarded-For
 | 
						||
	var forwardedFor = this.RawReq.Header.Get("X-Forwarded-For")
 | 
						||
	if len(forwardedFor) > 0 {
 | 
						||
		commaIndex := strings.Index(forwardedFor, ",")
 | 
						||
		if commaIndex > 0 && !lists.ContainsString(result, forwardedFor[:commaIndex]) {
 | 
						||
			result = append(result, forwardedFor[:commaIndex])
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// Real-IP
 | 
						||
	{
 | 
						||
		realIP, ok := this.RawReq.Header["X-Real-IP"]
 | 
						||
		if ok && len(realIP) > 0 && !lists.ContainsString(result, realIP[0]) {
 | 
						||
			result = append(result, realIP[0])
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// Real-Ip
 | 
						||
	{
 | 
						||
		realIP, ok := this.RawReq.Header["X-Real-Ip"]
 | 
						||
		if ok && len(realIP) > 0 && !lists.ContainsString(result, realIP[0]) {
 | 
						||
			result = append(result, realIP[0])
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// Remote-Addr
 | 
						||
	{
 | 
						||
		var remoteAddr = this.RawReq.RemoteAddr
 | 
						||
		host, _, err := net.SplitHostPort(remoteAddr)
 | 
						||
		if err == nil {
 | 
						||
			if !lists.ContainsString(result, host) {
 | 
						||
				result = append(result, host)
 | 
						||
			}
 | 
						||
		} else {
 | 
						||
			result = append(result, remoteAddr)
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	return
 | 
						||
}
 | 
						||
 | 
						||
// 请求内容长度
 | 
						||
func (this *HTTPRequest) requestLength() int64 {
 | 
						||
	return this.RawReq.ContentLength
 | 
						||
}
 | 
						||
 | 
						||
// 请求用户
 | 
						||
func (this *HTTPRequest) requestRemoteUser() string {
 | 
						||
	username, _, ok := this.RawReq.BasicAuth()
 | 
						||
	if !ok {
 | 
						||
		return ""
 | 
						||
	}
 | 
						||
	return username
 | 
						||
}
 | 
						||
 | 
						||
// Path 请求的URL中路径部分
 | 
						||
func (this *HTTPRequest) Path() string {
 | 
						||
	uri, err := url.ParseRequestURI(this.uri)
 | 
						||
	if err != nil {
 | 
						||
		return ""
 | 
						||
	}
 | 
						||
	return uri.Path
 | 
						||
}
 | 
						||
 | 
						||
// 客户端端口
 | 
						||
func (this *HTTPRequest) requestRemotePort() int {
 | 
						||
	_, port, err := net.SplitHostPort(this.RawReq.RemoteAddr)
 | 
						||
	if err == nil {
 | 
						||
		return types.Int(port)
 | 
						||
	}
 | 
						||
	return 0
 | 
						||
}
 | 
						||
 | 
						||
// 获取的URI中的参数部分
 | 
						||
func (this *HTTPRequest) requestQueryString() string {
 | 
						||
	uri, err := url.ParseRequestURI(this.uri)
 | 
						||
	if err != nil {
 | 
						||
		return ""
 | 
						||
	}
 | 
						||
	return uri.RawQuery
 | 
						||
}
 | 
						||
 | 
						||
// 构造类似于"GET / HTTP/1.1"之类的请求字符串
 | 
						||
func (this *HTTPRequest) requestString() string {
 | 
						||
	return this.RawReq.Method + " " + this.rawURI + " " + this.RawReq.Proto
 | 
						||
}
 | 
						||
 | 
						||
// 构造请求字符串
 | 
						||
func (this *HTTPRequest) requestCookiesString() string {
 | 
						||
	var cookies = []string{}
 | 
						||
	for _, cookie := range this.RawReq.Cookies() {
 | 
						||
		cookies = append(cookies, url.QueryEscape(cookie.Name)+"="+url.QueryEscape(cookie.Value))
 | 
						||
	}
 | 
						||
	return strings.Join(cookies, "&")
 | 
						||
}
 | 
						||
 | 
						||
// 查询单个Cookie值
 | 
						||
func (this *HTTPRequest) requestCookie(name string) string {
 | 
						||
	cookie, err := this.RawReq.Cookie(name)
 | 
						||
	if err != nil {
 | 
						||
		return ""
 | 
						||
	}
 | 
						||
	return cookie.Value
 | 
						||
}
 | 
						||
 | 
						||
// 查询请求参数值
 | 
						||
func (this *HTTPRequest) requestQueryParam(name string) string {
 | 
						||
	uri, err := url.ParseRequestURI(this.rawURI)
 | 
						||
	if err != nil {
 | 
						||
		return ""
 | 
						||
	}
 | 
						||
 | 
						||
	v, found := uri.Query()[name]
 | 
						||
	if !found {
 | 
						||
		return ""
 | 
						||
	}
 | 
						||
	return strings.Join(v, "&")
 | 
						||
}
 | 
						||
 | 
						||
// 查询单个请求Header值
 | 
						||
func (this *HTTPRequest) requestHeader(key string) string {
 | 
						||
	v, found := this.RawReq.Header[key]
 | 
						||
	if !found {
 | 
						||
		// 转换为canonical header再尝试
 | 
						||
		var canonicalHeaderKey = http.CanonicalHeaderKey(key)
 | 
						||
		if canonicalHeaderKey != key {
 | 
						||
			return strings.Join(this.RawReq.Header[canonicalHeaderKey], ";")
 | 
						||
		}
 | 
						||
 | 
						||
		return ""
 | 
						||
	}
 | 
						||
	return strings.Join(v, ";")
 | 
						||
}
 | 
						||
 | 
						||
// 以字符串的形式返回所有请求Header
 | 
						||
func (this *HTTPRequest) requestHeadersString() string {
 | 
						||
	var headers = []string{}
 | 
						||
	for k, v := range this.RawReq.Header {
 | 
						||
		for _, subV := range v {
 | 
						||
			headers = append(headers, k+": "+subV)
 | 
						||
		}
 | 
						||
	}
 | 
						||
	return strings.Join(headers, ";")
 | 
						||
}
 | 
						||
 | 
						||
// 获取请求Content-Type值
 | 
						||
func (this *HTTPRequest) requestContentType() string {
 | 
						||
	return this.RawReq.Header.Get("Content-Type")
 | 
						||
}
 | 
						||
 | 
						||
// 获取请求的文件名,仅在请求是读取本地文件时不为空
 | 
						||
func (this *HTTPRequest) requestFilename() string {
 | 
						||
	return this.filePath
 | 
						||
}
 | 
						||
 | 
						||
// 请求的scheme
 | 
						||
func (this *HTTPRequest) requestScheme() string {
 | 
						||
	if this.IsHTTPS {
 | 
						||
		return "https"
 | 
						||
	}
 | 
						||
	return "http"
 | 
						||
}
 | 
						||
 | 
						||
// 请求的服务器地址中的端口
 | 
						||
func (this *HTTPRequest) requestServerPort() int {
 | 
						||
	if len(this.ServerAddr) > 0 {
 | 
						||
		_, port, err := net.SplitHostPort(this.ServerAddr)
 | 
						||
		if err == nil && len(port) > 0 {
 | 
						||
			return types.Int(port)
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	var host = this.RawReq.Host
 | 
						||
	if len(host) > 0 {
 | 
						||
		_, port, err := net.SplitHostPort(host)
 | 
						||
		if err == nil && len(port) > 0 {
 | 
						||
			return types.Int(port)
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	if this.IsHTTP {
 | 
						||
		return 80
 | 
						||
	}
 | 
						||
	return 443
 | 
						||
}
 | 
						||
 | 
						||
func (this *HTTPRequest) Id() string {
 | 
						||
	return this.requestId
 | 
						||
}
 | 
						||
 | 
						||
func (this *HTTPRequest) Server() maps.Map {
 | 
						||
	return maps.Map{"id": this.ReqServer.Id}
 | 
						||
}
 | 
						||
 | 
						||
func (this *HTTPRequest) Node() maps.Map {
 | 
						||
	return maps.Map{"id": teaconst.NodeId}
 | 
						||
}
 | 
						||
 | 
						||
// URL 获取完整的URL
 | 
						||
func (this *HTTPRequest) URL() string {
 | 
						||
	return this.requestScheme() + "://" + this.ReqHost + this.uri
 | 
						||
}
 | 
						||
 | 
						||
// Host 获取Host
 | 
						||
func (this *HTTPRequest) Host() string {
 | 
						||
	return this.ReqHost
 | 
						||
}
 | 
						||
 | 
						||
func (this *HTTPRequest) Proto() string {
 | 
						||
	return this.RawReq.Proto
 | 
						||
}
 | 
						||
 | 
						||
func (this *HTTPRequest) ProtoMajor() int {
 | 
						||
	return this.RawReq.ProtoMajor
 | 
						||
}
 | 
						||
 | 
						||
func (this *HTTPRequest) ProtoMinor() int {
 | 
						||
	return this.RawReq.ProtoMinor
 | 
						||
}
 | 
						||
 | 
						||
func (this *HTTPRequest) RemoteAddr() string {
 | 
						||
	return this.requestRemoteAddr(true)
 | 
						||
}
 | 
						||
 | 
						||
func (this *HTTPRequest) RawRemoteAddr() string {
 | 
						||
	var addr = this.RawReq.RemoteAddr
 | 
						||
	host, _, err := net.SplitHostPort(addr)
 | 
						||
	if err == nil {
 | 
						||
		addr = host
 | 
						||
	}
 | 
						||
	return addr
 | 
						||
}
 | 
						||
 | 
						||
func (this *HTTPRequest) RemotePort() int {
 | 
						||
	addr := this.RawReq.RemoteAddr
 | 
						||
	_, port, err := net.SplitHostPort(addr)
 | 
						||
	if err != nil {
 | 
						||
		return 0
 | 
						||
	}
 | 
						||
	return types.Int(port)
 | 
						||
}
 | 
						||
 | 
						||
func (this *HTTPRequest) SetAttr(name string, value string) {
 | 
						||
	this.logAttrs[name] = value
 | 
						||
}
 | 
						||
 | 
						||
func (this *HTTPRequest) SetVar(name string, value string) {
 | 
						||
	this.varMapping[name] = value
 | 
						||
}
 | 
						||
 | 
						||
// ContentLength 请求内容长度
 | 
						||
func (this *HTTPRequest) ContentLength() int64 {
 | 
						||
	return this.RawReq.ContentLength
 | 
						||
}
 | 
						||
 | 
						||
// CalculateSize 计算当前请求的尺寸(预估)
 | 
						||
func (this *HTTPRequest) CalculateSize() (size int64) {
 | 
						||
	// Get /xxx HTTP/1.1
 | 
						||
	size += int64(len(this.RawReq.Method)) + 1
 | 
						||
	size += int64(len(this.RawReq.URL.String())) + 1
 | 
						||
	size += int64(len(this.RawReq.Proto)) + 1
 | 
						||
	for k, v := range this.RawReq.Header {
 | 
						||
		for _, v1 := range v {
 | 
						||
			size += int64(len(k) + 2 /** : **/ + len(v1) + 1)
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	size += 1 /** \r\n **/
 | 
						||
 | 
						||
	if this.RawReq.ContentLength > 0 {
 | 
						||
		size += this.RawReq.ContentLength
 | 
						||
	} else if len(this.requestBodyData) > 0 {
 | 
						||
		size += int64(len(this.requestBodyData))
 | 
						||
	}
 | 
						||
	return size
 | 
						||
}
 | 
						||
 | 
						||
// Method 请求方法
 | 
						||
func (this *HTTPRequest) Method() string {
 | 
						||
	return this.RawReq.Method
 | 
						||
}
 | 
						||
 | 
						||
// TransferEncoding 获取传输编码
 | 
						||
func (this *HTTPRequest) TransferEncoding() string {
 | 
						||
	if len(this.RawReq.TransferEncoding) > 0 {
 | 
						||
		return this.RawReq.TransferEncoding[0]
 | 
						||
	}
 | 
						||
	return ""
 | 
						||
}
 | 
						||
 | 
						||
// Cookie 获取Cookie
 | 
						||
func (this *HTTPRequest) Cookie(name string) string {
 | 
						||
	c, err := this.RawReq.Cookie(name)
 | 
						||
	if err != nil {
 | 
						||
		return ""
 | 
						||
	}
 | 
						||
	return c.Value
 | 
						||
}
 | 
						||
 | 
						||
// DeleteHeader 删除请求Header
 | 
						||
func (this *HTTPRequest) DeleteHeader(name string) {
 | 
						||
	this.RawReq.Header.Del(name)
 | 
						||
}
 | 
						||
 | 
						||
// SetHeader 设置请求Header
 | 
						||
func (this *HTTPRequest) SetHeader(name string, values []string) {
 | 
						||
	this.RawReq.Header[name] = values
 | 
						||
}
 | 
						||
 | 
						||
// Header 读取Header
 | 
						||
func (this *HTTPRequest) Header() http.Header {
 | 
						||
	return this.RawReq.Header
 | 
						||
}
 | 
						||
 | 
						||
// URI 获取当前请求的URI
 | 
						||
func (this *HTTPRequest) URI() string {
 | 
						||
	return this.uri
 | 
						||
}
 | 
						||
 | 
						||
// SetURI 设置当前请求的URI
 | 
						||
func (this *HTTPRequest) SetURI(uri string) {
 | 
						||
	this.uri = uri
 | 
						||
}
 | 
						||
 | 
						||
// Done 设置已完成
 | 
						||
func (this *HTTPRequest) Done() {
 | 
						||
	this.isDone = true
 | 
						||
}
 | 
						||
 | 
						||
// Close 关闭连接
 | 
						||
func (this *HTTPRequest) Close() {
 | 
						||
	this.Done()
 | 
						||
 | 
						||
	var requestConn = this.RawReq.Context().Value(HTTPConnContextKey)
 | 
						||
	if requestConn == nil {
 | 
						||
		return
 | 
						||
	}
 | 
						||
 | 
						||
	lingerConn, ok := requestConn.(LingerConn)
 | 
						||
	if ok {
 | 
						||
		_ = lingerConn.SetLinger(0)
 | 
						||
	}
 | 
						||
 | 
						||
	conn, ok := requestConn.(net.Conn)
 | 
						||
	if ok {
 | 
						||
		_ = conn.Close()
 | 
						||
		return
 | 
						||
	}
 | 
						||
}
 | 
						||
 | 
						||
// Allow 放行
 | 
						||
func (this *HTTPRequest) Allow() {
 | 
						||
	this.web.FirewallRef = nil
 | 
						||
}
 | 
						||
 | 
						||
// 设置代理相关头部信息
 | 
						||
// 参考:https://tools.ietf.org/html/rfc7239
 | 
						||
func (this *HTTPRequest) setForwardHeaders(header http.Header) {
 | 
						||
	// TODO 做成可选项
 | 
						||
	if this.RawReq.Header.Get("Connection") == "close" {
 | 
						||
		this.RawReq.Header.Set("Connection", "keep-alive")
 | 
						||
	}
 | 
						||
 | 
						||
	var rawRemoteAddr = this.RawReq.RemoteAddr
 | 
						||
	host, _, err := net.SplitHostPort(rawRemoteAddr)
 | 
						||
	if err == nil {
 | 
						||
		rawRemoteAddr = host
 | 
						||
	}
 | 
						||
 | 
						||
	// x-real-ip
 | 
						||
	if this.reverseProxy != nil && this.reverseProxy.ShouldAddXRealIPHeader() {
 | 
						||
		_, ok1 := header["X-Real-IP"]
 | 
						||
		_, ok2 := header["X-Real-Ip"]
 | 
						||
		if !ok1 && !ok2 {
 | 
						||
			header["X-Real-IP"] = []string{this.requestRemoteAddr(true)}
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// X-Forwarded-For
 | 
						||
	if this.reverseProxy != nil && this.reverseProxy.ShouldAddXForwardedForHeader() {
 | 
						||
		forwardedFor, ok := header["X-Forwarded-For"]
 | 
						||
		if ok && len(forwardedFor) > 0 { // already exists
 | 
						||
			_, hasForwardHeader := this.RawReq.Header["X-Forwarded-For"]
 | 
						||
			if hasForwardHeader {
 | 
						||
				// 限制转发的XFF中地址数量
 | 
						||
				if this.nodeConfig != nil && this.nodeConfig.GlobalServerConfig != nil && this.nodeConfig.GlobalServerConfig.HTTPAll.XFFMaxAddresses > 0 {
 | 
						||
					var maxForwardedAddresses = this.nodeConfig.GlobalServerConfig.HTTPAll.XFFMaxAddresses
 | 
						||
					if maxForwardedAddresses == 1 {
 | 
						||
						forwardedFor = nil
 | 
						||
					} else {
 | 
						||
						var forwardedAddresses []string
 | 
						||
						for _, forwardedHeader := range forwardedFor {
 | 
						||
							if len(forwardedHeader) > 0 {
 | 
						||
								forwardedAddresses = append(forwardedAddresses, strings.Split(forwardedHeader, ", ")...)
 | 
						||
							}
 | 
						||
						}
 | 
						||
						if len(forwardedAddresses) >= maxForwardedAddresses {
 | 
						||
							forwardedFor = []string{strings.Join(forwardedAddresses[:maxForwardedAddresses-1], ", ")}
 | 
						||
						}
 | 
						||
					}
 | 
						||
				}
 | 
						||
 | 
						||
				if len(forwardedFor) > 0 {
 | 
						||
					header["X-Forwarded-For"] = []string{strings.Join(forwardedFor, ", ") + ", " + rawRemoteAddr}
 | 
						||
				} else {
 | 
						||
					header["X-Forwarded-For"] = []string{rawRemoteAddr}
 | 
						||
				}
 | 
						||
			}
 | 
						||
		} else {
 | 
						||
			var clientRemoteAddr = this.requestRemoteAddr(true)
 | 
						||
			if len(clientRemoteAddr) > 0 && clientRemoteAddr != rawRemoteAddr {
 | 
						||
				header["X-Forwarded-For"] = []string{clientRemoteAddr + ", " + rawRemoteAddr}
 | 
						||
			} else {
 | 
						||
				header["X-Forwarded-For"] = []string{rawRemoteAddr}
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// Forwarded
 | 
						||
	/**{
 | 
						||
		forwarded, ok := header["Forwarded"]
 | 
						||
		if ok {
 | 
						||
			header["Forwarded"] = []string{strings.Join(forwarded, ", ") + ", by=" + this.serverAddr + "; for=" + remoteAddr + "; host=" + this.ReqHost + "; proto=" + this.rawScheme}
 | 
						||
		} else {
 | 
						||
			header["Forwarded"] = []string{"by=" + this.serverAddr + "; for=" + remoteAddr + "; host=" + this.ReqHost + "; proto=" + this.rawScheme}
 | 
						||
		}
 | 
						||
	}**/
 | 
						||
 | 
						||
	// others
 | 
						||
	if this.reverseProxy != nil && this.reverseProxy.ShouldAddXForwardedByHeader() {
 | 
						||
		this.RawReq.Header.Set("X-Forwarded-By", this.ServerAddr)
 | 
						||
	}
 | 
						||
 | 
						||
	if this.reverseProxy != nil && this.reverseProxy.ShouldAddXForwardedHostHeader() {
 | 
						||
		if _, ok := header["X-Forwarded-Host"]; !ok {
 | 
						||
			this.RawReq.Header.Set("X-Forwarded-Host", this.ReqHost)
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	if this.reverseProxy != nil && this.reverseProxy.ShouldAddXForwardedProtoHeader() {
 | 
						||
		if _, ok := header["X-Forwarded-Proto"]; !ok {
 | 
						||
			this.RawReq.Header.Set("X-Forwarded-Proto", this.requestScheme())
 | 
						||
		}
 | 
						||
	}
 | 
						||
}
 | 
						||
 | 
						||
// 处理自定义Request Header
 | 
						||
func (this *HTTPRequest) processRequestHeaders(reqHeader http.Header) {
 | 
						||
	this.fixRequestHeader(reqHeader)
 | 
						||
 | 
						||
	if this.web.RequestHeaderPolicy != nil && this.web.RequestHeaderPolicy.IsOn {
 | 
						||
		// 删除某些Header
 | 
						||
		for name := range reqHeader {
 | 
						||
			if this.web.RequestHeaderPolicy.ContainsDeletedHeader(name) {
 | 
						||
				reqHeader.Del(name)
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// Set
 | 
						||
		for _, header := range this.web.RequestHeaderPolicy.SetHeaders {
 | 
						||
			if !header.IsOn {
 | 
						||
				continue
 | 
						||
			}
 | 
						||
 | 
						||
			// 是否已删除
 | 
						||
			if this.web.RequestHeaderPolicy.ContainsDeletedHeader(header.Name) {
 | 
						||
				continue
 | 
						||
			}
 | 
						||
 | 
						||
			// 请求方法
 | 
						||
			if len(header.Methods) > 0 && !lists.ContainsString(header.Methods, this.RawReq.Method) {
 | 
						||
				continue
 | 
						||
			}
 | 
						||
 | 
						||
			// 域名
 | 
						||
			if len(header.Domains) > 0 && !configutils.MatchDomains(header.Domains, this.ReqHost) {
 | 
						||
				continue
 | 
						||
			}
 | 
						||
 | 
						||
			var headerValue = header.Value
 | 
						||
			if header.ShouldReplace {
 | 
						||
				if len(headerValue) == 0 {
 | 
						||
					headerValue = reqHeader.Get(header.Name) // 原有值
 | 
						||
				} else if header.HasVariables() {
 | 
						||
					headerValue = this.Format(header.Value)
 | 
						||
				}
 | 
						||
 | 
						||
				for _, v := range header.ReplaceValues {
 | 
						||
					headerValue = v.Replace(headerValue)
 | 
						||
				}
 | 
						||
			} else if header.HasVariables() {
 | 
						||
				headerValue = this.Format(header.Value)
 | 
						||
			}
 | 
						||
 | 
						||
			// 支持修改Host
 | 
						||
			if header.Name == "Host" && len(header.Value) > 0 {
 | 
						||
				this.RawReq.Host = headerValue
 | 
						||
			} else {
 | 
						||
				if header.ShouldAppend {
 | 
						||
					reqHeader[header.Name] = append(reqHeader[header.Name], headerValue)
 | 
						||
				} else {
 | 
						||
					reqHeader[header.Name] = []string{headerValue}
 | 
						||
				}
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// 非标Header
 | 
						||
		if len(this.web.RequestHeaderPolicy.NonStandardHeaders) > 0 {
 | 
						||
			for _, name := range this.web.RequestHeaderPolicy.NonStandardHeaders {
 | 
						||
				var canonicalKey = http.CanonicalHeaderKey(name)
 | 
						||
				if canonicalKey != name {
 | 
						||
					v, ok := reqHeader[canonicalKey]
 | 
						||
					if ok {
 | 
						||
						delete(reqHeader, canonicalKey)
 | 
						||
						reqHeader[name] = v
 | 
						||
					}
 | 
						||
				}
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
}
 | 
						||
 | 
						||
// 处理一些被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
 | 
						||
		} else if strings.HasPrefix(k, "Sec-Ch") {
 | 
						||
			header.Del(k)
 | 
						||
			k = strings.ReplaceAll(k, "Sec-Ch-Ua", "Sec-CH-UA")
 | 
						||
			header[k] = v
 | 
						||
		} else {
 | 
						||
			switch k {
 | 
						||
			case "Www-Authenticate":
 | 
						||
				header.Del(k)
 | 
						||
				header["WWW-Authenticate"] = v
 | 
						||
			case "A-Im":
 | 
						||
				header.Del(k)
 | 
						||
				header["A-IM"] = v
 | 
						||
			case "Content-Md5":
 | 
						||
				header.Del(k)
 | 
						||
				header["Content-MD5"] = v
 | 
						||
			case "Sec-Gpc":
 | 
						||
				header.Del(k)
 | 
						||
				header["Content-GPC"] = v
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
}
 | 
						||
 | 
						||
// ProcessResponseHeaders 处理自定义Response Header
 | 
						||
func (this *HTTPRequest) ProcessResponseHeaders(responseHeader http.Header, statusCode int) {
 | 
						||
	// Server Name
 | 
						||
	if this.nodeConfig != nil && this.nodeConfig.GlobalServerConfig != nil && len(this.nodeConfig.GlobalServerConfig.HTTPAll.ServerName) > 0 {
 | 
						||
		responseHeader.Set("Server", this.nodeConfig.GlobalServerConfig.HTTPAll.ServerName)
 | 
						||
	}
 | 
						||
 | 
						||
	// 删除/添加/替换Header
 | 
						||
	// TODO 实现AddTrailers
 | 
						||
	if this.web.ResponseHeaderPolicy != nil && this.web.ResponseHeaderPolicy.IsOn {
 | 
						||
		// 删除某些Header
 | 
						||
		for name := range responseHeader {
 | 
						||
			if this.web.ResponseHeaderPolicy.ContainsDeletedHeader(name) {
 | 
						||
				responseHeader.Del(name)
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// Set
 | 
						||
		for _, header := range this.web.ResponseHeaderPolicy.SetHeaders {
 | 
						||
			if !header.IsOn {
 | 
						||
				continue
 | 
						||
			}
 | 
						||
 | 
						||
			// 是否已删除
 | 
						||
			if this.web.ResponseHeaderPolicy.ContainsDeletedHeader(header.Name) {
 | 
						||
				continue
 | 
						||
			}
 | 
						||
 | 
						||
			// 状态码
 | 
						||
			if header.Status != nil && !header.Status.Match(statusCode) {
 | 
						||
				continue
 | 
						||
			}
 | 
						||
 | 
						||
			// 请求方法
 | 
						||
			if len(header.Methods) > 0 && !lists.ContainsString(header.Methods, this.RawReq.Method) {
 | 
						||
				continue
 | 
						||
			}
 | 
						||
 | 
						||
			// 域名
 | 
						||
			if len(header.Domains) > 0 && !configutils.MatchDomains(header.Domains, this.ReqHost) {
 | 
						||
				continue
 | 
						||
			}
 | 
						||
 | 
						||
			// 是否为跳转
 | 
						||
			if header.DisableRedirect && httpStatusIsRedirect(statusCode) {
 | 
						||
				continue
 | 
						||
			}
 | 
						||
 | 
						||
			var headerValue = header.Value
 | 
						||
			if header.ShouldReplace {
 | 
						||
				if len(headerValue) == 0 {
 | 
						||
					headerValue = responseHeader.Get(header.Name) // 原有值
 | 
						||
				} else if header.HasVariables() {
 | 
						||
					headerValue = this.Format(header.Value)
 | 
						||
				}
 | 
						||
 | 
						||
				for _, v := range header.ReplaceValues {
 | 
						||
					headerValue = v.Replace(headerValue)
 | 
						||
				}
 | 
						||
			} else if header.HasVariables() {
 | 
						||
				headerValue = this.Format(header.Value)
 | 
						||
			}
 | 
						||
 | 
						||
			if header.ShouldAppend {
 | 
						||
				responseHeader[header.Name] = append(responseHeader[header.Name], headerValue)
 | 
						||
			} else {
 | 
						||
				responseHeader[header.Name] = []string{headerValue}
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// 非标Header
 | 
						||
		if len(this.web.ResponseHeaderPolicy.NonStandardHeaders) > 0 {
 | 
						||
			for _, name := range this.web.ResponseHeaderPolicy.NonStandardHeaders {
 | 
						||
				var canonicalKey = http.CanonicalHeaderKey(name)
 | 
						||
				if canonicalKey != name {
 | 
						||
					v, ok := responseHeader[canonicalKey]
 | 
						||
					if ok {
 | 
						||
						delete(responseHeader, canonicalKey)
 | 
						||
						responseHeader[name] = v
 | 
						||
					}
 | 
						||
				}
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// CORS
 | 
						||
		if this.web.ResponseHeaderPolicy.CORS != nil && this.web.ResponseHeaderPolicy.CORS.IsOn && (!this.web.ResponseHeaderPolicy.CORS.OptionsMethodOnly || this.RawReq.Method == http.MethodOptions) {
 | 
						||
			var corsConfig = this.web.ResponseHeaderPolicy.CORS
 | 
						||
 | 
						||
			// Allow-Origin
 | 
						||
			if len(corsConfig.AllowOrigin) == 0 {
 | 
						||
				var origin = this.RawReq.Header.Get("Origin")
 | 
						||
				if len(origin) > 0 {
 | 
						||
					responseHeader.Set("Access-Control-Allow-Origin", origin)
 | 
						||
				}
 | 
						||
			} else {
 | 
						||
				responseHeader.Set("Access-Control-Allow-Origin", corsConfig.AllowOrigin)
 | 
						||
			}
 | 
						||
 | 
						||
			// Allow-Methods
 | 
						||
			if len(corsConfig.AllowMethods) == 0 {
 | 
						||
				responseHeader.Set("Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, HEAD, OPTIONS, PATCH")
 | 
						||
			} else {
 | 
						||
				responseHeader.Set("Access-Control-Allow-Methods", strings.Join(corsConfig.AllowMethods, ", "))
 | 
						||
			}
 | 
						||
 | 
						||
			// Max-Age
 | 
						||
			if corsConfig.MaxAge > 0 {
 | 
						||
				responseHeader.Set("Access-Control-Max-Age", types.String(corsConfig.MaxAge))
 | 
						||
			}
 | 
						||
 | 
						||
			// Expose-Headers
 | 
						||
			if len(corsConfig.ExposeHeaders) > 0 {
 | 
						||
				responseHeader.Set("Access-Control-Expose-Headers", strings.Join(corsConfig.ExposeHeaders, ", "))
 | 
						||
			}
 | 
						||
 | 
						||
			// Request-Method
 | 
						||
			if len(corsConfig.RequestMethod) > 0 {
 | 
						||
				responseHeader.Set("Access-Control-Request-Method", strings.ToUpper(corsConfig.RequestMethod))
 | 
						||
			}
 | 
						||
 | 
						||
			// Allow-Credentials
 | 
						||
			responseHeader.Set("Access-Control-Allow-Credentials", "true")
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// HSTS
 | 
						||
	if this.IsHTTPS &&
 | 
						||
		this.ReqServer.HTTPS != nil &&
 | 
						||
		this.ReqServer.HTTPS.SSLPolicy != nil &&
 | 
						||
		this.ReqServer.HTTPS.SSLPolicy.IsOn &&
 | 
						||
		this.ReqServer.HTTPS.SSLPolicy.HSTS != nil &&
 | 
						||
		this.ReqServer.HTTPS.SSLPolicy.HSTS.IsOn &&
 | 
						||
		this.ReqServer.HTTPS.SSLPolicy.HSTS.Match(this.ReqHost) {
 | 
						||
		responseHeader.Set(this.ReqServer.HTTPS.SSLPolicy.HSTS.HeaderKey(), this.ReqServer.HTTPS.SSLPolicy.HSTS.HeaderValue())
 | 
						||
	}
 | 
						||
 | 
						||
	// HTTP/3
 | 
						||
	if this.IsHTTPS && !this.IsHTTP3 && this.ReqServer.SupportsHTTP3() {
 | 
						||
		this.processHTTP3Headers(responseHeader)
 | 
						||
	}
 | 
						||
}
 | 
						||
 | 
						||
// 添加错误信息
 | 
						||
func (this *HTTPRequest) addError(err error) {
 | 
						||
	if err == nil {
 | 
						||
		return
 | 
						||
	}
 | 
						||
	this.errors = append(this.errors, err.Error())
 | 
						||
}
 | 
						||
 | 
						||
// 计算合适的buffer size
 | 
						||
func (this *HTTPRequest) bytePool(contentLength int64) *bytepool.Pool {
 | 
						||
	if contentLength < 0 {
 | 
						||
		return bytepool.Pool16k
 | 
						||
	}
 | 
						||
	if contentLength < 8192 { // < 8K
 | 
						||
		return bytepool.Pool1k
 | 
						||
	}
 | 
						||
	if contentLength < 32768 { // < 32K
 | 
						||
		return bytepool.Pool16k
 | 
						||
	}
 | 
						||
	return bytepool.Pool32k
 | 
						||
}
 | 
						||
 | 
						||
// 检查是否可以忽略错误
 | 
						||
func (this *HTTPRequest) canIgnore(err error) bool {
 | 
						||
	if err == nil {
 | 
						||
		return true
 | 
						||
	}
 | 
						||
 | 
						||
	// 已读到头
 | 
						||
	if err == io.EOF || errors.Is(err, io.ErrUnexpectedEOF) {
 | 
						||
		return true
 | 
						||
	}
 | 
						||
 | 
						||
	// 网络错误
 | 
						||
	var opErr *net.OpError
 | 
						||
	ok := errors.As(err, &opErr)
 | 
						||
	if ok {
 | 
						||
		return true
 | 
						||
	}
 | 
						||
 | 
						||
	// 客户端主动取消
 | 
						||
	if errors.Is(err, errWritingToClient) ||
 | 
						||
		errors.Is(err, context.Canceled) ||
 | 
						||
		errors.Is(err, io.ErrShortWrite) ||
 | 
						||
		strings.Contains(err.Error(), "write: connection") ||
 | 
						||
		strings.Contains(err.Error(), "write: broken pipe") ||
 | 
						||
		strings.Contains(err.Error(), "write tcp") {
 | 
						||
		return true
 | 
						||
	}
 | 
						||
 | 
						||
	// HTTP/2流错误
 | 
						||
	if err.Error() == "http2: stream closed" || strings.Contains(err.Error(), "stream error") || err.Error() == "client disconnected" { // errStreamClosed, errClientDisconnected
 | 
						||
		return true
 | 
						||
	}
 | 
						||
 | 
						||
	// HTTP内部错误
 | 
						||
	if strings.HasPrefix(err.Error(), "http:") || strings.HasPrefix(err.Error(), "http2:") {
 | 
						||
		return true
 | 
						||
	}
 | 
						||
 | 
						||
	return false
 | 
						||
}
 | 
						||
 | 
						||
// 检查连接是否已关闭
 | 
						||
func (this *HTTPRequest) isConnClosed() bool {
 | 
						||
	var requestConn = this.RawReq.Context().Value(HTTPConnContextKey)
 | 
						||
	if requestConn == nil {
 | 
						||
		return true
 | 
						||
	}
 | 
						||
 | 
						||
	conn, ok := requestConn.(net.Conn)
 | 
						||
	if ok {
 | 
						||
		return isClientConnClosed(conn)
 | 
						||
	}
 | 
						||
 | 
						||
	return true
 | 
						||
}
 |