@@ -84,11 +84,12 @@ var entrypoints = []entrypointName{addChainName, addPreChainName, getRootsName}
84
84
// pathHandlers maps from a path to the relevant AppHandler instance.
85
85
type pathHandlers map [string ]appHandler
86
86
87
- // appHandler holds a logInfo and a handler function that uses it, and is
88
- // an implementation of the http.Handler interface.
87
+ // appHandler connects an HTTP static-ct-api endpoint with log storage.
88
+ // It is an implementation of the http.Handler interface.
89
89
type appHandler struct {
90
- info * logInfo
91
- handler func (context.Context , * logInfo , http.ResponseWriter , * http.Request ) (int , error )
90
+ log * log
91
+ opts * HandlerOptions
92
+ handler func (context.Context , * HandlerOptions , * log , http.ResponseWriter , * http.Request ) (int , error )
92
93
name entrypointName
93
94
method string // http.MethodGet or http.MethodPost
94
95
}
@@ -97,64 +98,58 @@ type appHandler struct {
97
98
// does additional common error and stats processing.
98
99
func (a appHandler ) ServeHTTP (w http.ResponseWriter , r * http.Request ) {
99
100
var statusCode int
100
- label0 := a .info . log .origin
101
+ label0 := a .log .origin
101
102
label1 := string (a .name )
102
103
reqsCounter .Inc (label0 , label1 )
103
- startTime := a .info . iOpts .TimeSource .Now ()
104
- logCtx := a .info . iOpts .RequestLog .start (r .Context ())
105
- a .info . iOpts . RequestLog .origin (logCtx , a . info .log .origin )
104
+ startTime := a .opts .TimeSource .Now ()
105
+ logCtx := a .opts .RequestLog .start (r .Context ())
106
+ a .opts . RequestLog .origin (logCtx , a .log .origin )
106
107
defer func () {
107
- latency := a .info . iOpts .TimeSource .Now ().Sub (startTime ).Seconds ()
108
+ latency := a .opts .TimeSource .Now ().Sub (startTime ).Seconds ()
108
109
rspLatency .Observe (latency , label0 , label1 , strconv .Itoa (statusCode ))
109
110
}()
110
- klog .V (2 ).Infof ("%s: request %v %q => %s" , a .info . log .origin , r .Method , r .URL , a .name )
111
+ klog .V (2 ).Infof ("%s: request %v %q => %s" , a .log .origin , r .Method , r .URL , a .name )
111
112
// TODO(phboneff): add a.Method directly on the handler path and remove this test.
112
113
if r .Method != a .method {
113
- klog .Warningf ("%s: %s wrong HTTP method: %v" , a .info . log .origin , a .name , r .Method )
114
- a .info .sendHTTPError (w , http .StatusMethodNotAllowed , fmt .Errorf ("method not allowed: %s" , r .Method ))
115
- a .info . iOpts .RequestLog .status (logCtx , http .StatusMethodNotAllowed )
114
+ klog .Warningf ("%s: %s wrong HTTP method: %v" , a .log .origin , a .name , r .Method )
115
+ a .opts .sendHTTPError (w , http .StatusMethodNotAllowed , fmt .Errorf ("method not allowed: %s" , r .Method ))
116
+ a .opts .RequestLog .status (logCtx , http .StatusMethodNotAllowed )
116
117
return
117
118
}
118
119
119
120
// For GET requests all params come as form encoded so we might as well parse them now.
120
121
// POSTs will decode the raw request body as JSON later.
121
122
if r .Method == http .MethodGet {
122
123
if err := r .ParseForm (); err != nil {
123
- a .info .sendHTTPError (w , http .StatusBadRequest , fmt .Errorf ("failed to parse form data: %s" , err ))
124
- a .info . iOpts .RequestLog .status (logCtx , http .StatusBadRequest )
124
+ a .opts .sendHTTPError (w , http .StatusBadRequest , fmt .Errorf ("failed to parse form data: %s" , err ))
125
+ a .opts .RequestLog .status (logCtx , http .StatusBadRequest )
125
126
return
126
127
}
127
128
}
128
129
129
130
// impose a deadline on this onward request.
130
- ctx , cancel := context .WithDeadline (logCtx , deadlineTime (a .info ))
131
+ ctx , cancel := context .WithDeadline (logCtx , deadlineTime (a .opts ))
131
132
defer cancel ()
132
133
133
134
var err error
134
- statusCode , err = a .handler (ctx , a .info , w , r )
135
- a .info . iOpts .RequestLog .status (ctx , statusCode )
136
- klog .V (2 ).Infof ("%s: %s <= st=%d" , a .info . log .origin , a .name , statusCode )
135
+ statusCode , err = a .handler (ctx , a .opts , a . log , w , r )
136
+ a .opts .RequestLog .status (ctx , statusCode )
137
+ klog .V (2 ).Infof ("%s: %s <= st=%d" , a .log .origin , a .name , statusCode )
137
138
rspsCounter .Inc (label0 , label1 , strconv .Itoa (statusCode ))
138
139
if err != nil {
139
- klog .Warningf ("%s: %s handler error: %v" , a .info . log .origin , a .name , err )
140
- a .info .sendHTTPError (w , statusCode , err )
140
+ klog .Warningf ("%s: %s handler error: %v" , a .log .origin , a .name , err )
141
+ a .opts .sendHTTPError (w , statusCode , err )
141
142
return
142
143
}
143
144
144
145
// Additional check, for consistency the handler must return an error for non-200 st
145
146
if statusCode != http .StatusOK {
146
- klog .Warningf ("%s: %s handler non 200 without error: %d %v" , a .info . log .origin , a .name , statusCode , err )
147
- a .info .sendHTTPError (w , http .StatusInternalServerError , fmt .Errorf ("http handler misbehaved, st: %d" , statusCode ))
147
+ klog .Warningf ("%s: %s handler non 200 without error: %d %v" , a .log .origin , a .name , statusCode , err )
148
+ a .opts .sendHTTPError (w , http .StatusInternalServerError , fmt .Errorf ("http handler misbehaved, st: %d" , statusCode ))
148
149
return
149
150
}
150
151
}
151
152
152
- // logInfo holds information for a specific log instance.
153
- type logInfo struct {
154
- log * log
155
- iOpts * HandlerOptions
156
- }
157
-
158
153
// HandlerOptions describes log handlers options.
159
154
type HandlerOptions struct {
160
155
// Deadline is a timeout for HTTP requests.
@@ -171,36 +166,25 @@ type HandlerOptions struct {
171
166
}
172
167
173
168
func NewPathHandlers (opts * HandlerOptions , log * log ) pathHandlers {
174
- li := & logInfo {
175
- log : log ,
176
- iOpts : opts ,
177
- }
178
-
179
169
once .Do (func () { setupMetrics (opts .MetricFactory ) })
180
170
knownLogs .Set (1.0 , log .origin )
181
171
182
- return li .handlers (log .origin )
183
- }
184
-
185
- // handlers returns a map from URL paths (with the given prefix) and AppHandler instances
186
- // to handle those entrypoints.
187
- func (li * logInfo ) handlers (prefix string ) pathHandlers {
188
- prefix = strings .TrimRight (prefix , "/" )
172
+ prefix := strings .TrimRight (log .origin , "/" )
189
173
190
- // Bind the logInfo instance to give an AppHandler instance for each endpoint .
174
+ // Bind each endpoint to an appHandler instance.
191
175
ph := pathHandlers {
192
- prefix + ct .AddChainPath : appHandler {info : li , handler : addChain , name : addChainName , method : http .MethodPost },
193
- prefix + ct .AddPreChainPath : appHandler {info : li , handler : addPreChain , name : addPreChainName , method : http .MethodPost },
194
- prefix + ct .GetRootsPath : appHandler {info : li , handler : getRoots , name : getRootsName , method : http .MethodGet },
176
+ prefix + ct .AddChainPath : appHandler {opts : opts , log : log , handler : addChain , name : addChainName , method : http .MethodPost },
177
+ prefix + ct .AddPreChainPath : appHandler {opts : opts , log : log , handler : addPreChain , name : addPreChainName , method : http .MethodPost },
178
+ prefix + ct .GetRootsPath : appHandler {opts : opts , log : log , handler : getRoots , name : getRootsName , method : http .MethodGet },
195
179
}
196
180
197
181
return ph
198
182
}
199
183
200
184
// sendHTTPError generates a custom error page to give more information on why something didn't work
201
- func (li * logInfo ) sendHTTPError (w http.ResponseWriter , statusCode int , err error ) {
185
+ func (opts * HandlerOptions ) sendHTTPError (w http.ResponseWriter , statusCode int , err error ) {
202
186
errorBody := http .StatusText (statusCode )
203
- if ! li . iOpts .MaskInternalErrors || statusCode != http .StatusInternalServerError {
187
+ if ! opts .MaskInternalErrors || statusCode != http .StatusInternalServerError {
204
188
errorBody += fmt .Sprintf ("\n %v" , err )
205
189
}
206
190
http .Error (w , errorBody , statusCode )
@@ -231,7 +215,7 @@ func parseBodyAsJSONChain(r *http.Request) (ct.AddChainRequest, error) {
231
215
232
216
// addChainInternal is called by add-chain and add-pre-chain as the logic involved in
233
217
// processing these requests is almost identical
234
- func addChainInternal (ctx context.Context , li * logInfo , w http.ResponseWriter , r * http.Request , isPrecert bool ) (int , error ) {
218
+ func addChainInternal (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request , isPrecert bool ) (int , error ) {
235
219
var method entrypointName
236
220
if isPrecert {
237
221
method = addPreChainName
@@ -242,45 +226,45 @@ func addChainInternal(ctx context.Context, li *logInfo, w http.ResponseWriter, r
242
226
// Check the contents of the request and convert to slice of certificates.
243
227
addChainReq , err := parseBodyAsJSONChain (r )
244
228
if err != nil {
245
- return http .StatusBadRequest , fmt .Errorf ("%s: failed to parse add-chain body: %s" , li . log .origin , err )
229
+ return http .StatusBadRequest , fmt .Errorf ("%s: failed to parse add-chain body: %s" , log .origin , err )
246
230
}
247
231
// Log the DERs now because they might not parse as valid X.509.
248
232
for _ , der := range addChainReq .Chain {
249
- li . iOpts .RequestLog .addDERToChain (ctx , der )
233
+ opts .RequestLog .addDERToChain (ctx , der )
250
234
}
251
- chain , err := verifyAddChain (li , addChainReq , isPrecert )
235
+ chain , err := verifyAddChain (log , addChainReq , isPrecert )
252
236
if err != nil {
253
237
return http .StatusBadRequest , fmt .Errorf ("failed to verify add-chain contents: %s" , err )
254
238
}
255
239
for _ , cert := range chain {
256
- li . iOpts .RequestLog .addCertToChain (ctx , cert )
240
+ opts .RequestLog .addCertToChain (ctx , cert )
257
241
}
258
242
// Get the current time in the form used throughout RFC6962, namely milliseconds since Unix
259
243
// epoch, and use this throughout.
260
- timeMillis := uint64 (li . iOpts .TimeSource .Now ().UnixNano () / nanosPerMilli )
244
+ timeMillis := uint64 (opts .TimeSource .Now ().UnixNano () / nanosPerMilli )
261
245
262
246
entry , err := entryFromChain (chain , isPrecert , timeMillis )
263
247
if err != nil {
264
248
return http .StatusBadRequest , fmt .Errorf ("failed to build MerkleTreeLeaf: %s" , err )
265
249
}
266
250
267
- klog .V (2 ).Infof ("%s: %s => storage.GetCertIndex" , li . log .origin , method )
268
- sctDedupInfo , isDup , err := li . log .storage .GetCertDedupInfo (ctx , chain [0 ])
251
+ klog .V (2 ).Infof ("%s: %s => storage.GetCertIndex" , log .origin , method )
252
+ sctDedupInfo , isDup , err := log .storage .GetCertDedupInfo (ctx , chain [0 ])
269
253
idx := sctDedupInfo .Idx
270
254
if err != nil {
271
255
return http .StatusInternalServerError , fmt .Errorf ("couldn't deduplicate the request: %s" , err )
272
256
}
273
257
274
258
if isDup {
275
- klog .V (3 ).Infof ("%s: %s - found duplicate entry at index %d" , li . log .origin , method , idx )
259
+ klog .V (3 ).Infof ("%s: %s - found duplicate entry at index %d" , log .origin , method , idx )
276
260
entry .Timestamp = sctDedupInfo .Timestamp
277
261
} else {
278
- if err := li . log .storage .AddIssuerChain (ctx , chain [1 :]); err != nil {
262
+ if err := log .storage .AddIssuerChain (ctx , chain [1 :]); err != nil {
279
263
return http .StatusInternalServerError , fmt .Errorf ("failed to store issuer chain: %s" , err )
280
264
}
281
265
282
- klog .V (2 ).Infof ("%s: %s => storage.Add" , li . log .origin , method )
283
- idx , err = li . log .storage .Add (ctx , entry )()
266
+ klog .V (2 ).Infof ("%s: %s => storage.Add" , log .origin , method )
267
+ idx , err = log .storage .Add (ctx , entry )()
284
268
if err != nil {
285
269
if errors .Is (err , tessera .ErrPushback ) {
286
270
w .Header ().Add ("Retry-After" , "1" )
@@ -291,8 +275,8 @@ func addChainInternal(ctx context.Context, li *logInfo, w http.ResponseWriter, r
291
275
// We store the index for this certificate in the deduplication storage immediately.
292
276
// It might be stored again later, if a local deduplication storage is synced, potentially
293
277
// with a smaller value.
294
- klog .V (2 ).Infof ("%s: %s => storage.AddCertIndex" , li . log .origin , method )
295
- err = li . log .storage .AddCertDedupInfo (ctx , chain [0 ], dedup.SCTDedupInfo {Idx : idx , Timestamp : entry .Timestamp })
278
+ klog .V (2 ).Infof ("%s: %s => storage.AddCertIndex" , log .origin , method )
279
+ err = log .storage .AddCertDedupInfo (ctx , chain [0 ], dedup.SCTDedupInfo {Idx : idx , Timestamp : entry .Timestamp })
296
280
// TODO: block log writes if deduplication breaks
297
281
if err != nil {
298
282
klog .Warningf ("AddCertIndex(): failed to store certificate index: %v" , err )
@@ -311,7 +295,7 @@ func addChainInternal(ctx context.Context, li *logInfo, w http.ResponseWriter, r
311
295
// As the Log server has definitely got the Merkle tree leaf, we can
312
296
// generate an SCT and respond with it.
313
297
// TODO(phboneff): this should work, but double check
314
- sct , err := li . log .signSCT (& loggedLeaf )
298
+ sct , err := log .signSCT (& loggedLeaf )
315
299
if err != nil {
316
300
return http .StatusInternalServerError , fmt .Errorf ("failed to generate SCT: %s" , err )
317
301
}
@@ -320,32 +304,32 @@ func addChainInternal(ctx context.Context, li *logInfo, w http.ResponseWriter, r
320
304
return http .StatusInternalServerError , fmt .Errorf ("failed to marshall SCT: %s" , err )
321
305
}
322
306
// We could possibly fail to issue the SCT after this but it's v. unlikely.
323
- li . iOpts .RequestLog .issueSCT (ctx , sctBytes )
307
+ opts .RequestLog .issueSCT (ctx , sctBytes )
324
308
err = marshalAndWriteAddChainResponse (sct , w )
325
309
if err != nil {
326
310
// reason is logged and http status is already set
327
311
return http .StatusInternalServerError , fmt .Errorf ("failed to write response: %s" , err )
328
312
}
329
- klog .V (3 ).Infof ("%s: %s <= SCT" , li . log .origin , method )
313
+ klog .V (3 ).Infof ("%s: %s <= SCT" , log .origin , method )
330
314
if sct .Timestamp == timeMillis {
331
- lastSCTTimestamp .Set (float64 (sct .Timestamp ), li . log .origin )
315
+ lastSCTTimestamp .Set (float64 (sct .Timestamp ), log .origin )
332
316
}
333
317
334
318
return http .StatusOK , nil
335
319
}
336
320
337
- func addChain (ctx context.Context , li * logInfo , w http.ResponseWriter , r * http.Request ) (int , error ) {
338
- return addChainInternal (ctx , li , w , r , false )
321
+ func addChain (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request ) (int , error ) {
322
+ return addChainInternal (ctx , opts , log , w , r , false )
339
323
}
340
324
341
- func addPreChain (ctx context.Context , li * logInfo , w http.ResponseWriter , r * http.Request ) (int , error ) {
342
- return addChainInternal (ctx , li , w , r , true )
325
+ func addPreChain (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request ) (int , error ) {
326
+ return addChainInternal (ctx , opts , log , w , r , true )
343
327
}
344
328
345
- func getRoots (_ context.Context , li * logInfo , w http.ResponseWriter , _ * http.Request ) (int , error ) {
329
+ func getRoots (_ context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , _ * http.Request ) (int , error ) {
346
330
// Pull out the raw certificates from the parsed versions
347
- rawCerts := make ([][]byte , 0 , len (li . log .chainValidationOpts .trustedRoots .RawCertificates ()))
348
- for _ , cert := range li . log .chainValidationOpts .trustedRoots .RawCertificates () {
331
+ rawCerts := make ([][]byte , 0 , len (log .chainValidationOpts .trustedRoots .RawCertificates ()))
332
+ for _ , cert := range log .chainValidationOpts .trustedRoots .RawCertificates () {
349
333
rawCerts = append (rawCerts , cert .Raw )
350
334
}
351
335
@@ -354,23 +338,23 @@ func getRoots(_ context.Context, li *logInfo, w http.ResponseWriter, _ *http.Req
354
338
enc := json .NewEncoder (w )
355
339
err := enc .Encode (jsonMap )
356
340
if err != nil {
357
- klog .Warningf ("%s: get_roots failed: %v" , li . log .origin , err )
341
+ klog .Warningf ("%s: get_roots failed: %v" , log .origin , err )
358
342
return http .StatusInternalServerError , fmt .Errorf ("get-roots failed with: %s" , err )
359
343
}
360
344
361
345
return http .StatusOK , nil
362
346
}
363
347
364
348
// deadlineTime calculates the future time a request should expire based on our config.
365
- func deadlineTime (li * logInfo ) time.Time {
366
- return li . iOpts . TimeSource .Now ().Add (li . iOpts .Deadline )
349
+ func deadlineTime (opts * HandlerOptions ) time.Time {
350
+ return opts . TimeSource .Now ().Add (opts .Deadline )
367
351
}
368
352
369
353
// verifyAddChain is used by add-chain and add-pre-chain. It does the checks that the supplied
370
354
// cert is of the correct type and chains to a trusted root.
371
- func verifyAddChain (li * logInfo , req ct.AddChainRequest , expectingPrecert bool ) ([]* x509.Certificate , error ) {
355
+ func verifyAddChain (log * log , req ct.AddChainRequest , expectingPrecert bool ) ([]* x509.Certificate , error ) {
372
356
// We already checked that the chain is not empty so can move on to verification
373
- validPath , err := validateChain (req .Chain , li . log .chainValidationOpts )
357
+ validPath , err := validateChain (req .Chain , log .chainValidationOpts )
374
358
if err != nil {
375
359
// We rejected it because the cert failed checks or we could not find a path to a root etc.
376
360
// Lots of possible causes for errors
@@ -385,9 +369,9 @@ func verifyAddChain(li *logInfo, req ct.AddChainRequest, expectingPrecert bool)
385
369
// The type of the leaf must match the one the handler expects
386
370
if isPrecert != expectingPrecert {
387
371
if expectingPrecert {
388
- klog .Warningf ("%s: Cert (or precert with invalid CT ext) submitted as precert chain: %q" , li . log .origin , req .Chain )
372
+ klog .Warningf ("%s: Cert (or precert with invalid CT ext) submitted as precert chain: %q" , log .origin , req .Chain )
389
373
} else {
390
- klog .Warningf ("%s: Precert (or cert with invalid CT ext) submitted as cert chain: %q" , li . log .origin , req .Chain )
374
+ klog .Warningf ("%s: Precert (or cert with invalid CT ext) submitted as cert chain: %q" , log .origin , req .Chain )
391
375
}
392
376
return nil , fmt .Errorf ("cert / precert mismatch: %T" , expectingPrecert )
393
377
}
0 commit comments