...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package restapi
19
20 import (
21 "crypto/tls"
22 "net/http"
23 "strconv"
24 "strings"
25 "time"
26
27 "github.com/go-chi/chi/middleware"
28 "github.com/go-openapi/errors"
29 "github.com/go-openapi/runtime"
30 "github.com/mitchellh/mapstructure"
31 "github.com/rs/cors"
32 "github.com/urfave/negroni"
33
34 pkgapi "github.com/sigstore/timestamp-authority/pkg/api"
35 "github.com/sigstore/timestamp-authority/pkg/generated/restapi/operations"
36 "github.com/sigstore/timestamp-authority/pkg/generated/restapi/operations/timestamp"
37 "github.com/sigstore/timestamp-authority/pkg/internal/cmdparams"
38 "github.com/sigstore/timestamp-authority/pkg/log"
39 )
40
41
42
43 func configureFlags(_ *operations.TimestampServerAPI) {
44
45 }
46
47 func configureAPI(api *operations.TimestampServerAPI) http.Handler {
48
49 api.ServeError = logAndServeError
50
51
52
53
54
55
56 api.Logger = log.Logger.Infof
57
58
59
60
61
62 api.JSONConsumer = runtime.JSONConsumer()
63 api.ApplicationPemCertificateChainProducer = runtime.TextProducer()
64 api.ApplicationTimestampQueryConsumer = runtime.ByteStreamConsumer()
65 api.ApplicationTimestampReplyProducer = runtime.ByteStreamProducer()
66
67 api.TimestampGetTimestampResponseHandler = timestamp.GetTimestampResponseHandlerFunc(pkgapi.TimestampResponseHandler)
68 api.TimestampGetTimestampCertChainHandler = timestamp.GetTimestampCertChainHandlerFunc(pkgapi.GetTimestampCertChainHandler)
69
70 api.PreServerShutdown = func() {}
71
72 api.ServerShutdown = func() {}
73
74 api.AddMiddlewareFor("POST", "/api/v1/timestamp", middleware.NoCache)
75 api.AddMiddlewareFor("GET", "/api/v1/timestamp/certchain", cacheForDay)
76
77 return setupGlobalMiddleware(api.Serve(setupMiddlewares))
78 }
79
80
81 func configureTLS(_ *tls.Config) {
82
83 }
84
85
86
87
88
89 func configureServer(s *http.Server, scheme, addr string) {
90 }
91
92
93
94 func setupMiddlewares(handler http.Handler) http.Handler {
95 return handler
96 }
97
98
99 type logAdapter struct {
100 }
101
102 func (l *logAdapter) Print(v ...interface{}) {
103 log.Logger.Info(v...)
104 }
105
106 const pingPath = "/ping"
107
108
109
110 func httpPingOnly() func(http.Handler) http.Handler {
111 f := func(h http.Handler) http.Handler {
112 fn := func(w http.ResponseWriter, r *http.Request) {
113 if r.URL.Scheme != "https" && !strings.EqualFold(r.URL.Path, pingPath) {
114 w.Header().Set("Content-Type", "text/plain")
115 w.WriteHeader(http.StatusNotFound)
116 w.Write([]byte("http server supports only the " + pingPath + " entrypoint"))
117 return
118 }
119 h.ServeHTTP(w, r)
120 }
121 return http.HandlerFunc(fn)
122 }
123 return f
124 }
125
126
127
128 func setupGlobalMiddleware(handler http.Handler) http.Handler {
129 middleware.DefaultLogger = middleware.RequestLogger(
130 &middleware.DefaultLogFormatter{Logger: &logAdapter{}})
131 returnHandler := middleware.Logger(handler)
132 returnHandler = middleware.Recoverer(returnHandler)
133 returnHandler = middleware.Heartbeat(pingPath)(returnHandler)
134 if cmdparams.IsHTTPPingOnly {
135 returnHandler = httpPingOnly()(returnHandler)
136 }
137
138 handleCORS := cors.Default().Handler
139 returnHandler = handleCORS(returnHandler)
140
141 returnHandler = wrapMetrics(returnHandler)
142
143 return middleware.RequestID(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
144 ctx := r.Context()
145 r = r.WithContext(log.WithRequestID(ctx, middleware.GetReqID(ctx)))
146 defer func() {
147 _ = log.RequestIDLogger(r).Sync()
148 }()
149
150 returnHandler.ServeHTTP(w, r)
151 }))
152 }
153
154 func wrapMetrics(handler http.Handler) http.Handler {
155 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
156 start := time.Now()
157 ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
158 defer func() {
159
160 pkgapi.MetricLatency.With(map[string]string{
161 "path": r.URL.Path,
162 "code": strconv.Itoa(ww.Status()),
163 }).Observe(float64(time.Since(start)))
164
165 pkgapi.MetricLatencySummary.With(map[string]string{
166 "path": r.URL.Path,
167 "code": strconv.Itoa(ww.Status()),
168 }).Observe(float64(time.Since(start)))
169
170 pkgapi.MetricRequestLatency.With(map[string]string{
171 "path": r.URL.Path,
172 "method": r.Method,
173 }).Observe(float64(time.Since(start)))
174
175 pkgapi.MetricRequestCount.With(map[string]string{
176 "path": r.URL.Path,
177 "method": r.Method,
178 "code": strconv.Itoa(ww.Status()),
179 }).Inc()
180 }()
181
182 handler.ServeHTTP(ww, r)
183
184 })
185 }
186
187 func cacheForDay(handler http.Handler) http.Handler {
188 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
189 ww := negroni.NewResponseWriter(w)
190 ww.Before(func(w negroni.ResponseWriter) {
191 if w.Status() >= 200 && w.Status() <= 299 {
192 w.Header().Set("Cache-Control", "max-age=86400, immutable")
193 }
194 })
195 handler.ServeHTTP(ww, r)
196 })
197 }
198
199 func logAndServeError(w http.ResponseWriter, r *http.Request, err error) {
200 if apiErr, ok := err.(errors.Error); ok && apiErr.Code() == http.StatusNotFound {
201 log.RequestIDLogger(r).Warn(err)
202 } else {
203 log.RequestIDLogger(r).Error(err)
204 }
205 requestFields := map[string]interface{}{}
206 if err := mapstructure.Decode(r, &requestFields); err == nil {
207 log.RequestIDLogger(r).Debug(requestFields)
208 }
209 errors.ServeError(w, r, err)
210 }
211
View as plain text