diff --git a/internal/nodes/http_request.go b/internal/nodes/http_request.go index f4bff67..834e068 100644 --- a/internal/nodes/http_request.go +++ b/internal/nodes/http_request.go @@ -151,7 +151,7 @@ func (this *HTTPRequest) Do() { // Web配置 err := this.configureWeb(this.ReqServer.Web, true, 0) if err != nil { - this.write50x(err, http.StatusInternalServerError, false) + this.write50x(err, http.StatusInternalServerError, "Failed to configure the server", "配置服务失败", false) this.doEnd() return } @@ -286,7 +286,7 @@ func (this *HTTPRequest) doBegin() { var err error this.requestBodyData, err = ioutil.ReadAll(io.LimitReader(this.RawReq.Body, AccessLogMaxRequestBodySize)) if err != nil { - this.write50x(err, http.StatusBadGateway, false) + this.write50x(err, http.StatusBadGateway, "Failed to read request body for access log", "为访问日志读取请求Body失败", false) return } this.RawReq.Body = ioutil.NopCloser(io.MultiReader(bytes.NewBuffer(this.requestBodyData), this.RawReq.Body)) diff --git a/internal/nodes/http_request_auth.go b/internal/nodes/http_request_auth.go index 66505cd..d4a31f5 100644 --- a/internal/nodes/http_request_auth.go +++ b/internal/nodes/http_request_auth.go @@ -33,7 +33,7 @@ func (this *HTTPRequest) doAuth() (shouldStop bool) { return writer.StatusCode(), nil }, this.Format) if err != nil { - this.write50x(err, http.StatusInternalServerError, false) + this.write50x(err, http.StatusInternalServerError, "Failed to execute the AuthPolicy", "认证策略执行失败", false) return } if b { diff --git a/internal/nodes/http_request_error.go b/internal/nodes/http_request_error.go index ed7460a..847460b 100644 --- a/internal/nodes/http_request_error.go +++ b/internal/nodes/http_request_error.go @@ -1,32 +1,59 @@ package nodes import ( + "github.com/TeaOSLab/EdgeCommon/pkg/configutils" "github.com/iwind/TeaGo/lists" "github.com/iwind/TeaGo/types" "net/http" + "strings" ) +const httpStatusPageTemplate = ` + +
+${message}
+ +Request ID: ${requestId}. + + +` + func (this *HTTPRequest) write404() { - if this.doPage(http.StatusNotFound) { + this.writeCode(http.StatusNotFound) +} + +func (this *HTTPRequest) writeCode(statusCode int) { + if this.doPage(statusCode) { return } - this.processResponseHeaders(http.StatusNotFound) - this.writer.WriteHeader(http.StatusNotFound) - _, _ = this.writer.Write([]byte("404 page not found: '" + this.URL() + "'" + " (Request Id: " + this.requestId + ")")) + var pageContent = configutils.ParseVariables(httpStatusPageTemplate, func(varName string) (value string) { + switch varName { + case "status": + return types.String(statusCode) + case "statusMessage": + return http.StatusText(statusCode) + case "requestId": + return this.requestId + case "message": + return "" // 空 + } + return "${" + varName + "}" + }) + + this.processResponseHeaders(statusCode) + this.writer.WriteHeader(statusCode) + + _, _ = this.writer.Write([]byte(pageContent)) } -func (this *HTTPRequest) writeCode(code int) { - if this.doPage(code) { - return - } - - this.processResponseHeaders(code) - this.writer.WriteHeader(code) - _, _ = this.writer.Write([]byte(types.String(code) + " " + http.StatusText(code) + ": '" + this.URL() + "'" + " (Request Id: " + this.requestId + ")")) -} - -func (this *HTTPRequest) write50x(err error, statusCode int, canTryStale bool) { +func (this *HTTPRequest) write50x(err error, statusCode int, enMessage string, zhMessage string, canTryStale bool) { if err != nil { this.addError(err) } @@ -37,7 +64,7 @@ func (this *HTTPRequest) write50x(err error, statusCode int, canTryStale bool) { this.web.Cache.Stale != nil && this.web.Cache.Stale.IsOn && (len(this.web.Cache.Stale.Status) == 0 || lists.ContainsInt(this.web.Cache.Stale.Status, statusCode)) { - ok := this.doCacheRead(true) + var ok = this.doCacheRead(true) if ok { return } @@ -47,7 +74,34 @@ func (this *HTTPRequest) write50x(err error, statusCode int, canTryStale bool) { if this.doPage(statusCode) { return } + + // 内置HTML模板 + var pageContent = configutils.ParseVariables(httpStatusPageTemplate, func(varName string) (value string) { + switch varName { + case "status": + return types.String(statusCode) + case "statusMessage": + return http.StatusText(statusCode) + case "requestId": + return this.requestId + case "message": + var acceptLanguages = this.RawReq.Header.Get("Accept-Language") + if len(acceptLanguages) > 0 { + var index = strings.Index(acceptLanguages, ",") + if index > 0 { + var firstLanguage = acceptLanguages[:index] + if firstLanguage == "zh-CN" { + return "网站出了一点小问题,原因:" + zhMessage + "。" + } + } + } + return "The site is unavailable now, cause: " + enMessage + "." + } + return "${" + varName + "}" + }) + this.processResponseHeaders(statusCode) this.writer.WriteHeader(statusCode) - _, _ = this.writer.Write([]byte(types.String(statusCode) + " " + http.StatusText(statusCode) + " (Request Id: " + this.requestId + ")")) + + _, _ = this.writer.Write([]byte(pageContent)) } diff --git a/internal/nodes/http_request_fastcgi.go b/internal/nodes/http_request_fastcgi.go index dd22f79..c85f4eb 100644 --- a/internal/nodes/http_request_fastcgi.go +++ b/internal/nodes/http_request_fastcgi.go @@ -81,7 +81,7 @@ func (this *HTTPRequest) doFastcgi() (shouldStop bool) { client, err := fcgi.SharedPool(fastcgi.Network(), fastcgi.RealAddress(), uint(poolSize)).Client() if err != nil { - this.write50x(err, http.StatusInternalServerError, false) + this.write50x(err, http.StatusInternalServerError, "Failed to create Fastcgi pool", "Fastcgi池生成失败", false) return } @@ -159,13 +159,13 @@ func (this *HTTPRequest) doFastcgi() (shouldStop bool) { resp, stderr, err := client.Call(fcgiReq) if err != nil { - this.write50x(err, http.StatusInternalServerError, false) + this.write50x(err, http.StatusInternalServerError, "Failed to read Fastcgi", "读取Fastcgi失败", false) return } if len(stderr) > 0 { err := errors.New("Fastcgi Error: " + strings.TrimSpace(string(stderr)) + " script: " + maps.NewMap(params).GetString("SCRIPT_FILENAME")) - this.write50x(err, http.StatusInternalServerError, false) + this.write50x(err, http.StatusInternalServerError, "Failed to read Fastcgi", "读取Fastcgi失败", false) return } diff --git a/internal/nodes/http_request_reverse_proxy.go b/internal/nodes/http_request_reverse_proxy.go index 75b2f3f..ef207e9 100644 --- a/internal/nodes/http_request_reverse_proxy.go +++ b/internal/nodes/http_request_reverse_proxy.go @@ -56,7 +56,7 @@ func (this *HTTPRequest) doReverseProxy() { 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) - this.write50x(err, http.StatusBadGateway, true) + this.write50x(err, http.StatusBadGateway, "No origin site yet", "尚未配置源站", true) return } @@ -80,7 +80,7 @@ func (this *HTTPRequest) doReverseProxy() { if origin.Addr == nil { err := errors.New(this.URL() + ": Origin '" + strconv.FormatInt(origin.Id, 10) + "' does not has a address") remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", err.Error()) - this.write50x(err, http.StatusBadGateway, true) + this.write50x(err, http.StatusBadGateway, "Origin site did not has a valid address", "源站尚未配置地址", true) return } this.RawReq.URL.Scheme = origin.Addr.Protocol.Primary().Scheme() @@ -132,7 +132,7 @@ func (this *HTTPRequest) doReverseProxy() { if originHostIndex < 0 { var originErr = errors.New(this.URL() + ": Invalid origin address '" + originAddr + "', lacking port") remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", originErr.Error()) - this.write50x(originErr, http.StatusBadGateway, true) + this.write50x(originErr, http.StatusBadGateway, "No port in origin site address", "源站地址中没有配置端口", true) return } originAddr = originAddr[:originHostIndex+1] + types.String(this.requestServerPort()) @@ -211,7 +211,7 @@ func (this *HTTPRequest) doReverseProxy() { client, err := SharedHTTPClientPool.Client(this, origin, originAddr, this.reverseProxy.ProxyProtocol, this.reverseProxy.FollowRedirects) if err != nil { remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Create client failed: "+err.Error()) - this.write50x(err, http.StatusBadGateway, true) + this.write50x(err, http.StatusBadGateway, "Failed to create origin site client", "构造源站客户端失败", true) return } @@ -230,18 +230,18 @@ func (this *HTTPRequest) doReverseProxy() { SharedOriginStateManager.Fail(origin, requestHost, this.reverseProxy, func() { this.reverseProxy.ResetScheduling() }) - this.write50x(err, http.StatusBadGateway, true) + this.write50x(err, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true) remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.RawReq.URL.String()+": Request origin server failed: "+err.Error()) } else if httpErr.Err != context.Canceled { SharedOriginStateManager.Fail(origin, requestHost, this.reverseProxy, func() { this.reverseProxy.ResetScheduling() }) if httpErr.Timeout() { - this.write50x(err, http.StatusGatewayTimeout, true) + this.write50x(err, http.StatusGatewayTimeout, "Read origin site timeout", "源站读取超时", true) } else if httpErr.Temporary() { - this.write50x(err, http.StatusServiceUnavailable, true) + this.write50x(err, http.StatusServiceUnavailable, "Origin site unavailable now", "源站当前不可用", true) } else { - this.write50x(err, http.StatusBadGateway, true) + this.write50x(err, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true) } if httpErr.Err != io.EOF { remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Request origin server failed: "+err.Error()) @@ -264,7 +264,7 @@ func (this *HTTPRequest) doReverseProxy() { } if !isClientError { - this.write50x(err, http.StatusBadGateway, true) + this.write50x(err, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true) } } if resp != nil && resp.Body != nil { diff --git a/internal/nodes/http_request_root.go b/internal/nodes/http_request_root.go index b1d58ee..66f5eb6 100644 --- a/internal/nodes/http_request_root.go +++ b/internal/nodes/http_request_root.go @@ -114,7 +114,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) { } return } else { - this.write50x(err, http.StatusInternalServerError, true) + this.write50x(err, http.StatusInternalServerError, "Failed to stat the file", "查看文件统计信息失败", true) if !this.canIgnore(err) { logs.Error(err) } @@ -145,7 +145,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) { } return } else { - this.write50x(err, http.StatusInternalServerError, true) + this.write50x(err, http.StatusInternalServerError, "Failed to stat the file", "查看文件统计信息失败", true) if !this.canIgnore(err) { logs.Error(err) } @@ -285,7 +285,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) { fileReader, err := os.OpenFile(filePath, os.O_RDONLY, 0444) if err != nil { - this.write50x(err, http.StatusInternalServerError, true) + this.write50x(err, http.StatusInternalServerError, "Failed to open the file", "试图打开文件失败", true) return true } diff --git a/internal/nodes/http_request_url.go b/internal/nodes/http_request_url.go index f4a3996..bb43a20 100644 --- a/internal/nodes/http_request_url.go +++ b/internal/nodes/http_request_url.go @@ -35,7 +35,7 @@ func (this *HTTPRequest) doURL(method string, url string, host string, statusCod resp, err := client.Do(req) if err != nil { remotelogs.Error("HTTP_REQUEST_URL", req.URL.String()+": "+err.Error()) - this.write50x(err, http.StatusInternalServerError, false) + this.write50x(err, http.StatusInternalServerError, "Failed to read url", "读取URL失败", false) return } defer func() { diff --git a/internal/nodes/http_request_websocket.go b/internal/nodes/http_request_websocket.go index a1f6e19..12da4b4 100644 --- a/internal/nodes/http_request_websocket.go +++ b/internal/nodes/http_request_websocket.go @@ -43,7 +43,7 @@ func (this *HTTPRequest) doWebsocket(requestHost string) { // TODO 增加N次错误重试,重试的时候需要尝试不同的源站 originConn, _, err := OriginConnect(this.origin, this.requestServerPort(), this.RawReq.RemoteAddr, requestHost) if err != nil { - this.write50x(err, http.StatusBadGateway, false) + this.write50x(err, http.StatusBadGateway, "Failed to connect origin site", "源站连接失败", false) // 增加失败次数 SharedOriginStateManager.Fail(this.origin, requestHost, this.reverseProxy, func() { @@ -65,13 +65,13 @@ func (this *HTTPRequest) doWebsocket(requestHost string) { err = this.RawReq.Write(originConn) if err != nil { - this.write50x(err, http.StatusBadGateway, false) + this.write50x(err, http.StatusBadGateway, "Failed to write request to origin site", "源站请求初始化失败", false) return } clientConn, _, err := this.writer.Hijack() if err != nil || clientConn == nil { - this.write50x(err, http.StatusInternalServerError, false) + this.write50x(err, http.StatusInternalServerError, "Failed to get origin site connection", "获取源站连接失败", false) return } defer func() {