diff options
author | J. Shuster <joshuagregoryshuster@gmail.com> | 2017-09-08 11:58:42 +0300 |
---|---|---|
committer | Nick Thomas <nick@gitlab.com> | 2017-09-08 11:58:42 +0300 |
commit | 0a144bc6055b41fac726fdc6eeaa7150f622bd20 (patch) | |
tree | 9ed2a4dbecbd8146245ae8fa609cd9f54d038bf6 /internal/httperrors | |
parent | 0173d4e6b6b17443155d121a9098d0e742b9c4e3 (diff) |
Add an artifacts proxy to GitLab Pages
Diffstat (limited to 'internal/httperrors')
-rw-r--r-- | internal/httperrors/httperrors.go | 162 | ||||
-rw-r--r-- | internal/httperrors/httperrors_test.go | 105 |
2 files changed, 267 insertions, 0 deletions
diff --git a/internal/httperrors/httperrors.go b/internal/httperrors/httperrors.go new file mode 100644 index 00000000..82d43fc5 --- /dev/null +++ b/internal/httperrors/httperrors.go @@ -0,0 +1,162 @@ +package httperrors + +import ( + "fmt" + "net/http" +) + +type content struct { + status int + title string + statusString string + header string + subHeader string +} + +var ( + content404 = content{ + http.StatusNotFound, + "The page you're looking for could not be found (404)", + "404", + "The page you're looking for could not be found.", + `<p>The resource that you are attempting to access does not exist or you don't have the necessary permissions to view it.</p> + <p>Make sure the address is correct and that the page hasn't moved.</p> + <p>Please contact your GitLab administrator if you think this is a mistake.</p>`, + } + content500 = content{ + http.StatusInternalServerError, + "Something went wrong (500)", + "500", + "Whoops, something went wrong on our end.", + `<p>Try refreshing the page, or going back and attempting the action again.</p> + <p>Please contact your GitLab administrator if this problem persists.</p>`, + } + + content502 = content{ + http.StatusBadGateway, + "Something went wrong (502)", + "502", + "Whoops, something went wrong on our end.", + `<p>Try refreshing the page, or going back and attempting the action again.</p> + <p>Please contact your GitLab administrator if this problem persists.</p>`, + } +) + +const predefinedErrorPage = ` +<!DOCTYPE html> +<html> +<head> + <meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport"> + <title>%v</title> + <style> + body { + color: #666; + text-align: center; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + margin: auto; + font-size: 14px; + } + + h1 { + font-size: 56px; + line-height: 100px; + font-weight: 400; + color: #456; + } + + h2 { + font-size: 24px; + color: #666; + line-height: 1.5em; + } + + h3 { + color: #456; + font-size: 20px; + font-weight: 400; + line-height: 28px; + } + + hr { + max-width: 800px; + margin: 18px auto; + border: 0; + border-top: 1px solid #EEE; + border-bottom: 1px solid white; + } + + img { + max-width: 40vw; + display: block; + margin: 40px auto; + } + + a { + line-height: 100px; + font-weight: 400; + color: #4A8BEE; + font-size: 18px; + text-decoration: none; + } + + .container { + margin: auto 20px; + } + + .go-back { + display: none; + } + + </style> +</head> + +<body> + <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEwIiBoZWlnaHQ9IjIxMCIgdmlld0JveD0iMCAwIDIxMCAyMTAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTVsMzguNjQtMTE4LjkyMWgtNzcuMjhsMzguNjQgMTE4LjkyMXoiIGZpbGw9IiNlMjQzMjkiLz4KICA8cGF0aCBkPSJNMTA1LjA2MTQgMjAzLjY1NDhsLTM4LjY0LTExOC45MjFoLTU0LjE1M2w5Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTIuMjY4NSA4NC43MzQxbC0xMS43NDIgMzYuMTM5Yy0xLjA3MSAzLjI5Ni4xMDIgNi45MDcgMi45MDYgOC45NDRsMTAxLjYyOSA3My44MzgtOTIuNzkzLTExOC45MjF6IiBmaWxsPSIjZmNhMzI2Ii8+CiAgPHBhdGggZD0iTTEyLjI2ODUgODQuNzM0Mmg1NC4xNTNsLTIzLjI3My03MS42MjVjLTEuMTk3LTMuNjg2LTYuNDExLTMuNjg1LTcuNjA4IDBsLTIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+CiAgPHBhdGggZD0iTTEwNS4wNjE0IDIwMy42NTQ4bDM4LjY0LTExOC45MjFoNTQuMTUzbC05Mi43OTMgMTE4LjkyMXoiIGZpbGw9IiNmYzZkMjYiLz4KICA8cGF0aCBkPSJNMTk3Ljg1NDQgODQuNzM0MWwxMS43NDIgMzYuMTM5YzEuMDcxIDMuMjk2LS4xMDIgNi45MDctMi45MDYgOC45NDRsLTEwMS42MjkgNzMuODM4IDkyLjc5My0xMTguOTIxeiIgZmlsbD0iI2ZjYTMyNiIvPgogIDxwYXRoIGQ9Ik0xOTcuODU0NCA4NC43MzQyaC01NC4xNTNsMjMuMjczLTcxLjYyNWMxLjE5Ny0zLjY4NiA2LjQxMS0zLjY4NSA3LjYwOCAwbDIzLjI3MiA3MS42MjV6IiBmaWxsPSIjZTI0MzI5Ii8+Cjwvc3ZnPgo=" + alt="GitLab Logo" /> + <h1> + %v + </h1> + <div class="container"> + <h3>%v</h3> + <hr /> + %v + <a href="javascript:history.back()" class="js-go-back go-back">Go back</a> + </div> + <script> + (function () { + var goBack = document.querySelector('.js-go-back'); + + if (history.length > 1) { + goBack.style.display = 'inline'; + } + })(); + </script> +</body> +</html> +` + +func generateErrorHTML(c content) string { + return fmt.Sprintf(predefinedErrorPage, c.title, c.statusString, c.header, c.subHeader) +} + +func serveErrorPage(w http.ResponseWriter, c content) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.Header().Set("X-Content-Type-Options", "nosniff") + w.WriteHeader(c.status) + fmt.Fprintln(w, generateErrorHTML(c)) +} + +// Serve404 returns a 404 error response / HTML page to the http.ResponseWriter +func Serve404(w http.ResponseWriter) { + serveErrorPage(w, content404) +} + +// Serve500 returns a 500 error response / HTML page to the http.ResponseWriter +func Serve500(w http.ResponseWriter) { + serveErrorPage(w, content500) +} + +// Serve502 returns a 502 error response / HTML page to the http.ResponseWriter +func Serve502(w http.ResponseWriter) { + serveErrorPage(w, content502) +} diff --git a/internal/httperrors/httperrors_test.go b/internal/httperrors/httperrors_test.go new file mode 100644 index 00000000..1a79d850 --- /dev/null +++ b/internal/httperrors/httperrors_test.go @@ -0,0 +1,105 @@ +package httperrors + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +// creates a new implementation of http.ResponseWriter that allows the +// casting of values in order to aid testing efforts. +type testResponseWriter struct { + status int + content string + http.ResponseWriter +} + +func newTestResponseWriter(w http.ResponseWriter) *testResponseWriter { + return &testResponseWriter{0, "", w} +} + +func (w *testResponseWriter) Status() int { + return w.status +} + +func (w *testResponseWriter) Content() string { + return w.content +} + +func (w *testResponseWriter) Header() http.Header { + return w.ResponseWriter.Header() +} + +func (w *testResponseWriter) Write(data []byte) (int, error) { + w.content = string(data) + return w.ResponseWriter.Write(data) +} + +func (w *testResponseWriter) WriteHeader(statusCode int) { + w.status = statusCode + w.ResponseWriter.WriteHeader(statusCode) +} + +var ( + testingContent = content{ + http.StatusNotFound, + "Title", + "533", + "Header test", + "subheader text", + } +) + +func TestGenerateemailHTML(t *testing.T) { + actual := generateErrorHTML(testingContent) + assert.Contains(t, actual, testingContent.title) + assert.Contains(t, actual, testingContent.statusString) + assert.Contains(t, actual, testingContent.header) + assert.Contains(t, actual, testingContent.subHeader) +} + +func TestServeErrorPage(t *testing.T) { + w := newTestResponseWriter(httptest.NewRecorder()) + serveErrorPage(w, testingContent) + assert.Equal(t, w.Header().Get("Content-Type"), "text/html; charset=utf-8") + assert.Equal(t, w.Header().Get("X-Content-Type-Options"), "nosniff") + assert.Equal(t, w.Status(), testingContent.status) +} + +func TestServe404(t *testing.T) { + w := newTestResponseWriter(httptest.NewRecorder()) + Serve404(w) + assert.Equal(t, w.Header().Get("Content-Type"), "text/html; charset=utf-8") + assert.Equal(t, w.Header().Get("X-Content-Type-Options"), "nosniff") + assert.Equal(t, w.Status(), content404.status) + assert.Contains(t, w.Content(), content404.title) + assert.Contains(t, w.Content(), content404.statusString) + assert.Contains(t, w.Content(), content404.header) + assert.Contains(t, w.Content(), content404.subHeader) +} + +func TestServe500(t *testing.T) { + w := newTestResponseWriter(httptest.NewRecorder()) + Serve500(w) + assert.Equal(t, w.Header().Get("Content-Type"), "text/html; charset=utf-8") + assert.Equal(t, w.Header().Get("X-Content-Type-Options"), "nosniff") + assert.Equal(t, w.Status(), content500.status) + assert.Contains(t, w.Content(), content500.title) + assert.Contains(t, w.Content(), content500.statusString) + assert.Contains(t, w.Content(), content500.header) + assert.Contains(t, w.Content(), content500.subHeader) +} + +func TestServe502(t *testing.T) { + w := newTestResponseWriter(httptest.NewRecorder()) + Serve502(w) + assert.Equal(t, w.Header().Get("Content-Type"), "text/html; charset=utf-8") + assert.Equal(t, w.Header().Get("X-Content-Type-Options"), "nosniff") + assert.Equal(t, w.Status(), content502.status) + assert.Contains(t, w.Content(), content502.title) + assert.Contains(t, w.Content(), content502.statusString) + assert.Contains(t, w.Content(), content502.header) + assert.Contains(t, w.Content(), content502.subHeader) +} |