1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
package badgateway
import (
"bytes"
"fmt"
"html/template"
"io/ioutil"
"net/http"
"strings"
"time"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/log"
)
// Error is a custom error for pretty Sentry 'issues'
type sentryError struct{ error }
type roundTripper struct {
next http.RoundTripper
developmentMode bool
}
// NewRoundTripper creates a RoundTripper with a provided underlying next transport
func NewRoundTripper(developmentMode bool, next http.RoundTripper) http.RoundTripper {
return &roundTripper{next: next, developmentMode: developmentMode}
}
func (t *roundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
start := time.Now()
res, err := t.next.RoundTrip(r)
if err == nil {
return res, err
}
// httputil.ReverseProxy translates all errors from this
// RoundTrip function into 500 errors. But the most likely error
// is that the Rails app is not responding, in which case users
// and administrators expect to see a 502 error. To show 502s
// instead of 500s we catch the RoundTrip error here and inject a
// 502 response.
fields := log.Fields{"duration_ms": int64(time.Since(start).Seconds() * 1000)}
log.WithRequest(r).WithFields(fields).WithError(&sentryError{fmt.Errorf("badgateway: failed to receive response: %v", err)}).Error()
injectedResponse := &http.Response{
StatusCode: http.StatusBadGateway,
Status: http.StatusText(http.StatusBadGateway),
Request: r,
ProtoMajor: r.ProtoMajor,
ProtoMinor: r.ProtoMinor,
Proto: r.Proto,
Header: make(http.Header),
Trailer: make(http.Header),
}
message := "GitLab is not responding"
contentType := "text/plain"
if t.developmentMode {
message, contentType = developmentModeResponse(err)
}
injectedResponse.Body = ioutil.NopCloser(strings.NewReader(message))
injectedResponse.Header.Set("Content-Type", contentType)
return injectedResponse, nil
}
func developmentModeResponse(err error) (body string, contentType string) {
data := TemplateData{
Time: time.Now().Format("15:04:05"),
Error: err.Error(),
ReloadSeconds: 5,
}
buf := &bytes.Buffer{}
if err := developmentErrorTemplate.Execute(buf, data); err != nil {
return data.Error, "text/plain"
}
return buf.String(), "text/html"
}
type TemplateData struct {
Time string
Error string
ReloadSeconds int
}
var developmentErrorTemplate = template.Must(template.New("error502").Parse(`
<html>
<head>
<title>502: GitLab is not responding</title>
<script>
window.setTimeout(function() { location.reload() }, {{.ReloadSeconds}} * 1000)
</script>
</head>
<body>
<h1>502</h1>
<p>GitLab is not responding. The error was:</p>
<pre>{{.Error}}</pre>
<p>If you just started GDK it can take 60-300 seconds before GitLab has finished booting. This page will automatically reload every {{.ReloadSeconds}} seconds.</p>
<footer>Generated by gitlab-workhorse running in development mode at {{.Time}}.</footer>
</body>
</html>
`))
|