Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Render the styled error page for every miss and failure

e84a66f5b53c269d8b0c207d0335b537e1646cb3 · Keenan Nemetz · 15d ago · parent f6f4b1db

Render the styled error page for every miss and failure

Handler-level 404s now use the same view as the catch-all, including unknown
detail resources and download paths. Internal 500s render the shared error
layout and log the detail instead of exposing it. A panic before the first
byte is recovered into that page; a partial response is left to net/http.

Also mark label/assignee triage and inline review comments done in the UI plan,
where the slice order was stale.

server/web/auth.go +45−1

@@ -95,6 +95,34 @@ func (w *Web) principal(r *http.Request) *principal {
95 95 return p
96 96 }
97 97
98 // statusWriter records whether the response has started. A recover can still
99 // answer with the styled 500 page only while the header is unsent; once bytes
100 // are on the wire the connection is the only thing left to close.
101 type statusWriter struct {
102 http.ResponseWriter
103 wroteHeader bool
104 }
105
106 func (s *statusWriter) WriteHeader(code int) {
107 s.wroteHeader = true
108 s.ResponseWriter.WriteHeader(code)
109 }
110
111 func (s *statusWriter) Write(b []byte) (int, error) {
112 s.wroteHeader = true
113 return s.ResponseWriter.Write(b)
114 }
115
116 // Flush keeps streaming handlers (git's side-band progress) working through
117 // the wrapper.
118 func (s *statusWriter) Flush() {
119 if f, ok := s.ResponseWriter.(http.Flusher); ok {
120 f.Flush()
121 }
122 }
123
124 func (s *statusWriter) Unwrap() http.ResponseWriter { return s.ResponseWriter }
125
98 126 // Middleware resolves the session cookie once per request and sets the browser
99 127 // security headers every response carries. An invalid or expired cookie is
100 128 // cleared rather than left to be retried on every page.
@@ -123,7 +151,23 @@ func (w *Web) Middleware(next http.Handler) http.Handler {
123 151 }
124 152 }
125 153 }
126 next.ServeHTTP(rw, r)
154 sw := &statusWriter{ResponseWriter: rw}
155 defer func() {
156 v := recover()
157 if v == nil {
158 return
159 }
160 if w.Logger != nil {
161 w.Logger("panic %s %s: %v", r.Method, r.URL.Path, v)
162 }
163 if sw.wroteHeader {
164 // The reply is already partial; let net/http close the connection
165 // rather than append a page to an archive or a git stream.
166 panic(v)
167 }
168 w.serverError(sw, r, "")
169 }()
170 next.ServeHTTP(sw, r)
127 171 })
128 172 }
129 173