Skip to content

Commit 5078f67

Browse files
committed
if the client requests HTML, render an HTML 503 Service Unavailable page.
1 parent 7e69399 commit 5078f67

File tree

4 files changed

+75
-2
lines changed

4 files changed

+75
-2
lines changed

options/locale/locale_en-US.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ files = Files
117117

118118
error = Error
119119
error404 = The page you are trying to reach either <strong>does not exist</strong> or <strong>you are not authorized</strong> to view it.
120+
error503 = The server was unable to complete your request. Please try again later.
120121
go_back = Go Back
121122
invalid_data = Invalid data: %v
122123

routers/common/qos.go

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,18 @@ import (
1010
"strings"
1111

1212
user_model "code.gitea.io/gitea/models/user"
13+
"code.gitea.io/gitea/modules/log"
1314
"code.gitea.io/gitea/modules/setting"
15+
"code.gitea.io/gitea/modules/templates"
1416
"code.gitea.io/gitea/modules/web/middleware"
17+
giteacontext "code.gitea.io/gitea/services/context"
1518

1619
"github.com/bohde/codel"
1720
"github.com/go-chi/chi/v5"
1821
)
1922

23+
const tplStatus503 templates.TplName = "status/503"
24+
2025
type Priority int
2126

2227
func (p Priority) String() string {
@@ -70,8 +75,7 @@ func QoS() func(next http.Handler) http.Handler {
7075
// Check if the request can begin processing.
7176
err := c.Acquire(ctx, int(priority))
7277
if err != nil {
73-
// If it failed, the service is over capacity and should error
74-
http.Error(w, "Service Unavailable", http.StatusServiceUnavailable)
78+
renderServiceUnavailable(w, req)
7579
return
7680
}
7781

@@ -110,3 +114,31 @@ func requestPriority(ctx context.Context) Priority {
110114

111115
return DefaultPriority
112116
}
117+
118+
// renderServiceUnavailable will render an HTTP 503 Service
119+
// Unavailable page, providing HTML if the client accepts it.
120+
func renderServiceUnavailable(w http.ResponseWriter, req *http.Request) {
121+
acceptsHTML := false
122+
for _, part := range req.Header["Accept"] {
123+
if strings.Contains(part, "text/html") {
124+
acceptsHTML = true
125+
break
126+
}
127+
}
128+
129+
// If the client doesn't accept HTML, then render a plain text response
130+
if !acceptsHTML {
131+
http.Error(w, "503 Service Unavailable", http.StatusServiceUnavailable)
132+
return
133+
}
134+
135+
tmplCtx := giteacontext.TemplateContext{}
136+
tmplCtx["Locale"] = middleware.Locale(w, req)
137+
ctxData := middleware.GetContextData(req.Context())
138+
err := templates.HTMLRenderer().HTML(w, http.StatusServiceUnavailable, tplStatus503, ctxData, tmplCtx)
139+
if err != nil {
140+
log.Error("Error occurs again when rendering service unavailable page: %v", err)
141+
w.WriteHeader(http.StatusInternalServerError)
142+
_, _ = w.Write([]byte("Internal server error, please collect error logs and report to Gitea issue tracker"))
143+
}
144+
}

routers/common/qos_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package common
55

66
import (
7+
"net/http"
78
"testing"
89

910
user_model "code.gitea.io/gitea/models/user"
@@ -61,3 +62,30 @@ func TestRequestPriority(t *testing.T) {
6162
})
6263
}
6364
}
65+
66+
func TestRenderServiceUnavailable(t *testing.T) {
67+
t.Run("HTML", func(t *testing.T) {
68+
ctx, resp := contexttest.MockContext(t, "")
69+
ctx.Req.Header.Set("Accept", "text/html")
70+
71+
renderServiceUnavailable(resp, ctx.Req)
72+
assert.Equal(t, http.StatusServiceUnavailable, resp.Code)
73+
assert.Contains(t, resp.Header().Get("Content-Type"), "text/html")
74+
75+
body := resp.Body.String()
76+
assert.Contains(t, body, `lang="en-US"`)
77+
assert.Contains(t, body, "503 Service Unavailable")
78+
})
79+
80+
t.Run("plain", func(t *testing.T) {
81+
ctx, resp := contexttest.MockContext(t, "")
82+
ctx.Req.Header.Set("Accept", "text/plain")
83+
84+
renderServiceUnavailable(resp, ctx.Req)
85+
assert.Equal(t, http.StatusServiceUnavailable, resp.Code)
86+
assert.Contains(t, resp.Header().Get("Content-Type"), "text/plain")
87+
88+
body := resp.Body.String()
89+
assert.Contains(t, body, "503 Service Unavailable")
90+
})
91+
}

templates/status/503.tmpl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{{template "base/head" .}}
2+
<div role="main" aria-label="503 Service Unavailable" class="page-content">
3+
<div class="ui container">
4+
<div class="status-page-error">
5+
<div class="status-page-error-title">503 Service Unavailable</div>
6+
<div class="tw-text-center">
7+
<div class="tw-my-4">{{ctx.Locale.Tr "error503"}}</div>
8+
</div>
9+
</div>
10+
</div>
11+
</div>
12+
{{template "base/footer" .}}

0 commit comments

Comments
 (0)