@@ -31,6 +31,7 @@ import (
31
31
"github.com/transparency-dev/tesseract/internal/types/tls"
32
32
"github.com/transparency-dev/tesseract/internal/x509util"
33
33
tessera "github.com/transparency-dev/trillian-tessera"
34
+ "go.opentelemetry.io/otel/attribute"
34
35
"go.opentelemetry.io/otel/metric"
35
36
"k8s.io/klog/v2"
36
37
)
@@ -108,25 +109,25 @@ type pathHandlers map[string]appHandler
108
109
type appHandler struct {
109
110
log * log
110
111
opts * HandlerOptions
111
- handler func (context.Context , * HandlerOptions , * log , http.ResponseWriter , * http.Request ) (int , error )
112
+ handler func (context.Context , * HandlerOptions , * log , http.ResponseWriter , * http.Request ) (int , []attribute. KeyValue , error )
112
113
name entrypointName
113
114
method string // http.MethodGet or http.MethodPost
114
115
}
115
116
116
117
// ServeHTTP for an AppHandler invokes the underlying handler function but
117
118
// does additional common error and stats processing.
118
119
func (a appHandler ) ServeHTTP (w http.ResponseWriter , r * http.Request ) {
119
- var statusCode int
120
-
121
120
originAttr := originKey .String (a .log .origin )
122
121
operationAttr := operationKey .String (a .name )
123
- reqCounter .Add (r .Context (), 1 , metric .WithAttributes (originAttr , operationAttr ))
122
+ attrs := []attribute.KeyValue {originAttr , operationAttr }
123
+
124
+ reqCounter .Add (r .Context (), 1 , metric .WithAttributes (attrs ... ))
124
125
startTime := time .Now ()
125
126
logCtx := a .opts .RequestLog .start (r .Context ())
126
127
a .opts .RequestLog .origin (logCtx , a .log .origin )
127
128
defer func () {
128
129
latency := time .Since (startTime ).Seconds ()
129
- reqDuration .Record (r .Context (), latency , metric .WithAttributes (originAttr , operationAttr , codeKey . Int ( statusCode ) ))
130
+ reqDuration .Record (r .Context (), latency , metric .WithAttributes (attrs ... ))
130
131
}()
131
132
132
133
klog .V (2 ).Infof ("%s: request %v %q => %s" , a .log .origin , r .Method , r .URL , a .name )
@@ -153,11 +154,13 @@ func (a appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
153
154
ctx , cancel := context .WithTimeout (logCtx , a .opts .Deadline )
154
155
defer cancel ()
155
156
156
- var err error
157
- statusCode , err = a .handler (ctx , a .opts , a .log , w , r )
157
+ statusCode , hattrs , err := a .handler (ctx , a .opts , a .log , w , r )
158
+ for _ , attr := range hattrs {
159
+ attrs = append (attrs , attr )
160
+ }
158
161
a .opts .RequestLog .status (ctx , statusCode )
159
162
klog .V (2 ).Infof ("%s: %s <= st=%d" , a .log .origin , a .name , statusCode )
160
- rspCounter .Add (r .Context (), 1 , metric .WithAttributes (originAttr , operationAttr , codeKey . Int ( statusCode ) ))
163
+ rspCounter .Add (r .Context (), 1 , metric .WithAttributes (attrs ... ))
161
164
if err != nil {
162
165
klog .Warningf ("%s: %s handler error: %v" , a .log .origin , a .name , err )
163
166
a .opts .sendHTTPError (w , statusCode , err )
@@ -240,7 +243,7 @@ func parseBodyAsJSONChain(r *http.Request) (rfc6962.AddChainRequest, error) {
240
243
241
244
// addChainInternal is called by add-chain and add-pre-chain as the logic involved in
242
245
// processing these requests is almost identical
243
- func addChainInternal (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request , isPrecert bool ) (int , error ) {
246
+ func addChainInternal (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request , isPrecert bool ) (int , []attribute. KeyValue , error ) {
244
247
var method entrypointName
245
248
if isPrecert {
246
249
method = addPreChainName
@@ -251,15 +254,15 @@ func addChainInternal(ctx context.Context, opts *HandlerOptions, log *log, w htt
251
254
// Check the contents of the request and convert to slice of certificates.
252
255
addChainReq , err := parseBodyAsJSONChain (r )
253
256
if err != nil {
254
- return http .StatusBadRequest , fmt .Errorf ("%s: failed to parse add-chain body: %s" , log .origin , err )
257
+ return http .StatusBadRequest , nil , fmt .Errorf ("%s: failed to parse add-chain body: %s" , log .origin , err )
255
258
}
256
259
// Log the DERs now because they might not parse as valid X.509.
257
260
for _ , der := range addChainReq .Chain {
258
261
opts .RequestLog .addDERToChain (ctx , der )
259
262
}
260
263
chain , err := log .chainValidator .Validate (addChainReq , isPrecert )
261
264
if err != nil {
262
- return http .StatusBadRequest , fmt .Errorf ("failed to verify add-chain contents: %s" , err )
265
+ return http .StatusBadRequest , nil , fmt .Errorf ("failed to verify add-chain contents: %s" , err )
263
266
}
264
267
for _ , cert := range chain {
265
268
opts .RequestLog .addCertToChain (ctx , cert )
@@ -271,74 +274,79 @@ func addChainInternal(ctx context.Context, opts *HandlerOptions, log *log, w htt
271
274
272
275
entry , err := x509util .EntryFromChain (chain , isPrecert , timeMillis )
273
276
if err != nil {
274
- return http .StatusBadRequest , fmt .Errorf ("failed to build MerkleTreeLeaf: %s" , err )
277
+ return http .StatusBadRequest , nil , fmt .Errorf ("failed to build MerkleTreeLeaf: %s" , err )
275
278
}
276
279
277
280
if err := log .storage .AddIssuerChain (ctx , chain [1 :]); err != nil {
278
- return http .StatusInternalServerError , fmt .Errorf ("failed to store issuer chain: %s" , err )
281
+ return http .StatusInternalServerError , nil , fmt .Errorf ("failed to store issuer chain: %s" , err )
279
282
}
280
283
281
284
klog .V (2 ).Infof ("%s: %s => storage.Add" , log .origin , method )
282
285
index , dedupedTimeMillis , err := log .storage .Add (ctx , entry )
283
286
if err != nil {
284
287
if errors .Is (err , tessera .ErrPushback ) {
285
288
w .Header ().Add ("Retry-After" , "1" )
286
- return http .StatusServiceUnavailable , fmt .Errorf ("received pushback from Tessera sequencer: %v" , err )
289
+ return http .StatusServiceUnavailable , nil , fmt .Errorf ("received pushback from Tessera sequencer: %v" , err )
287
290
}
288
- return http .StatusInternalServerError , fmt .Errorf ("couldn't store the leaf: %v" , err )
291
+ return http .StatusInternalServerError , nil , fmt .Errorf ("couldn't store the leaf: %v" , err )
292
+ }
293
+ dedupedAttribute := dedupedKey .Bool (false )
294
+ isDup := dedupedTimeMillis != timeMillis
295
+ if isDup {
296
+ dedupedAttribute = dedupedKey .Bool (true )
289
297
}
290
298
entry .Timestamp = dedupedTimeMillis
291
299
292
300
// Always use the returned leaf as the basis for an SCT.
293
301
var loggedLeaf rfc6962.MerkleTreeLeaf
294
302
leafValue := entry .MerkleTreeLeaf (index )
295
303
if rest , err := tls .Unmarshal (leafValue , & loggedLeaf ); err != nil {
296
- return http .StatusInternalServerError , fmt .Errorf ("failed to reconstruct MerkleTreeLeaf: %s" , err )
304
+ return http .StatusInternalServerError , nil , fmt .Errorf ("failed to reconstruct MerkleTreeLeaf: %s" , err )
297
305
} else if len (rest ) > 0 {
298
- return http .StatusInternalServerError , fmt .Errorf ("extra data (%d bytes) on reconstructing MerkleTreeLeaf" , len (rest ))
306
+ return http .StatusInternalServerError , nil , fmt .Errorf ("extra data (%d bytes) on reconstructing MerkleTreeLeaf" , len (rest ))
299
307
}
300
308
301
309
// As the Log server has definitely got the Merkle tree leaf, we can
302
310
// generate an SCT and respond with it.
303
311
sct , err := log .signSCT (& loggedLeaf )
304
312
if err != nil {
305
- return http .StatusInternalServerError , fmt .Errorf ("failed to generate SCT: %s" , err )
313
+ return http .StatusInternalServerError , nil , fmt .Errorf ("failed to generate SCT: %s" , err )
306
314
}
307
315
sctBytes , err := tls .Marshal (* sct )
308
316
if err != nil {
309
- return http .StatusInternalServerError , fmt .Errorf ("failed to marshall SCT: %s" , err )
317
+ return http .StatusInternalServerError , nil , fmt .Errorf ("failed to marshall SCT: %s" , err )
310
318
}
311
319
// We could possibly fail to issue the SCT after this but it's v. unlikely.
312
320
opts .RequestLog .issueSCT (ctx , sctBytes )
313
321
err = marshalAndWriteAddChainResponse (sct , w )
314
322
if err != nil {
315
323
// reason is logged and http status is already set
316
- return http .StatusInternalServerError , fmt .Errorf ("failed to write response: %s" , err )
324
+ return http .StatusInternalServerError , nil , fmt .Errorf ("failed to write response: %s" , err )
317
325
}
318
326
klog .V (3 ).Infof ("%s: %s <= SCT" , log .origin , method )
319
- if sct . Timestamp == timeMillis {
327
+ if ! isDup {
320
328
lastSCTTimestamp .Record (ctx , otel .Clamp64 (sct .Timestamp ), metric .WithAttributes (originKey .String (log .origin )))
321
329
lastSCTIndex .Record (ctx , otel .Clamp64 (index ), metric .WithAttributes (originKey .String (log .origin )))
322
330
}
323
331
324
- return http .StatusOK , nil
332
+ return http .StatusOK , []attribute. KeyValue { dedupedAttribute }, nil
325
333
}
326
334
327
- func addChain (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request ) (int , error ) {
335
+ func addChain (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request ) (int , []attribute. KeyValue , error ) {
328
336
ctx , span := tracer .Start (ctx , "tesseract.addChain" )
329
337
defer span .End ()
330
338
331
339
return addChainInternal (ctx , opts , log , w , r , false )
332
340
}
333
341
334
- func addPreChain (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request ) (int , error ) {
342
+ func addPreChain (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , r * http.Request ) (int , []attribute. KeyValue , error ) {
335
343
ctx , span := tracer .Start (ctx , "tesseract.addPreChain" )
336
344
defer span .End ()
337
345
338
346
return addChainInternal (ctx , opts , log , w , r , true )
339
347
}
340
348
341
- func getRoots (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , _ * http.Request ) (int , error ) {
349
+ func getRoots (ctx context.Context , opts * HandlerOptions , log * log , w http.ResponseWriter , _ * http.Request ) (int , []attribute. KeyValue , error ) {
342
350
_ , span := tracer .Start (ctx , "tesseract.getRoots" )
343
351
defer span .End ()
344
352
@@ -355,10 +363,10 @@ func getRoots(ctx context.Context, opts *HandlerOptions, log *log, w http.Respon
355
363
err := enc .Encode (jsonMap )
356
364
if err != nil {
357
365
klog .Warningf ("%s: get_roots failed: %v" , log .origin , err )
358
- return http .StatusInternalServerError , fmt .Errorf ("get-roots failed with: %s" , err )
366
+ return http .StatusInternalServerError , nil , fmt .Errorf ("get-roots failed with: %s" , err )
359
367
}
360
368
361
- return http .StatusOK , nil
369
+ return http .StatusOK , nil , nil
362
370
}
363
371
364
372
// marshalAndWriteAddChainResponse is used by add-chain and add-pre-chain to create and write
0 commit comments