@@ -29,6 +29,11 @@ pub const DEFAULT_GET_HEADER_TIMEOUT_MILLIS: u64 = 1000;
29
29
/// Default user agent for HTTP requests.
30
30
pub const DEFAULT_USER_AGENT : & str = lighthouse_version:: VERSION ;
31
31
32
+ /// The value we set on the `ACCEPT` http header to indicate a preference for ssz response.
33
+ pub const PREFERENCE_ACCEPT_VALUE : & str = "application/octet-stream;q=1.0,application/json;q=0.9" ;
34
+ /// Only accept json responses.
35
+ pub const JSON_ACCEPT_VALUE : & str = "application/json" ;
36
+
32
37
#[ derive( Clone ) ]
33
38
pub struct Timeouts {
34
39
get_header : Duration ,
@@ -57,14 +62,20 @@ pub struct BuilderHttpClient {
57
62
server : SensitiveUrl ,
58
63
timeouts : Timeouts ,
59
64
user_agent : String ,
60
- ssz_enabled : Arc < AtomicBool > ,
65
+ /// Only use json for all requests/responses types.
66
+ disable_ssz : bool ,
67
+ /// Indicates that the `get_header` response had content-type ssz
68
+ /// so we can set content-type header to ssz to make the `submit_blinded_blocks`
69
+ /// request.
70
+ ssz_available : Arc < AtomicBool > ,
61
71
}
62
72
63
73
impl BuilderHttpClient {
64
74
pub fn new (
65
75
server : SensitiveUrl ,
66
76
user_agent : Option < String > ,
67
77
builder_header_timeout : Option < Duration > ,
78
+ disable_ssz : bool ,
68
79
) -> Result < Self , Error > {
69
80
let user_agent = user_agent. unwrap_or ( DEFAULT_USER_AGENT . to_string ( ) ) ;
70
81
let client = reqwest:: Client :: builder ( ) . user_agent ( & user_agent) . build ( ) ?;
@@ -73,7 +84,8 @@ impl BuilderHttpClient {
73
84
server,
74
85
timeouts : Timeouts :: new ( builder_header_timeout) ,
75
86
user_agent,
76
- ssz_enabled : Arc :: new ( false . into ( ) ) ,
87
+ disable_ssz,
88
+ ssz_available : Arc :: new ( false . into ( ) ) ,
77
89
} )
78
90
}
79
91
@@ -124,15 +136,15 @@ impl BuilderHttpClient {
124
136
125
137
let Ok ( Some ( fork_name) ) = self . fork_name_from_header ( & headers) else {
126
138
// if no fork version specified, attempt to fallback to JSON
127
- self . ssz_enabled . store ( false , Ordering :: SeqCst ) ;
139
+ self . ssz_available . store ( false , Ordering :: SeqCst ) ;
128
140
return serde_json:: from_slice ( & response_bytes) . map_err ( Error :: InvalidJson ) ;
129
141
} ;
130
142
131
143
let content_type = self . content_type_from_header ( & headers) ;
132
144
133
145
match content_type {
134
146
ContentType :: Ssz => {
135
- self . ssz_enabled . store ( true , Ordering :: SeqCst ) ;
147
+ self . ssz_available . store ( true , Ordering :: SeqCst ) ;
136
148
T :: from_ssz_bytes_by_fork ( & response_bytes, fork_name)
137
149
. map ( |data| ForkVersionedResponse {
138
150
version : Some ( fork_name) ,
@@ -142,15 +154,17 @@ impl BuilderHttpClient {
142
154
. map_err ( Error :: InvalidSsz )
143
155
}
144
156
ContentType :: Json => {
145
- self . ssz_enabled . store ( false , Ordering :: SeqCst ) ;
157
+ self . ssz_available . store ( false , Ordering :: SeqCst ) ;
146
158
serde_json:: from_slice ( & response_bytes) . map_err ( Error :: InvalidJson )
147
159
}
148
160
}
149
161
}
150
162
151
163
/// Return `true` if the most recently received response from the builder had SSZ Content-Type.
152
- pub fn is_ssz_enabled ( & self ) -> bool {
153
- self . ssz_enabled . load ( Ordering :: SeqCst )
164
+ /// Return `false` otherwise.
165
+ /// Also returns `false` if we have explicitly disabled ssz.
166
+ pub fn is_ssz_available ( & self ) -> bool {
167
+ !self . disable_ssz && self . ssz_available . load ( Ordering :: SeqCst )
154
168
}
155
169
156
170
async fn get_with_timeout < T : DeserializeOwned , U : IntoUrl > (
@@ -213,19 +227,14 @@ impl BuilderHttpClient {
213
227
& self ,
214
228
url : U ,
215
229
ssz_body : Vec < u8 > ,
216
- mut headers : HeaderMap ,
230
+ headers : HeaderMap ,
217
231
timeout : Option < Duration > ,
218
232
) -> Result < Response , Error > {
219
233
let mut builder = self . client . post ( url) ;
220
234
if let Some ( timeout) = timeout {
221
235
builder = builder. timeout ( timeout) ;
222
236
}
223
237
224
- headers. insert (
225
- CONTENT_TYPE_HEADER ,
226
- HeaderValue :: from_static ( SSZ_CONTENT_TYPE_HEADER ) ,
227
- ) ;
228
-
229
238
let response = builder
230
239
. headers ( headers)
231
240
. body ( ssz_body)
@@ -292,9 +301,21 @@ impl BuilderHttpClient {
292
301
. push ( "blinded_blocks" ) ;
293
302
294
303
let mut headers = HeaderMap :: new ( ) ;
295
- if let Ok ( value) = HeaderValue :: from_str ( & blinded_block. fork_name_unchecked ( ) . to_string ( ) ) {
296
- headers. insert ( CONSENSUS_VERSION_HEADER , value) ;
297
- }
304
+ headers. insert (
305
+ CONSENSUS_VERSION_HEADER ,
306
+ HeaderValue :: from_str ( & blinded_block. fork_name_unchecked ( ) . to_string ( ) )
307
+ . map_err ( |e| Error :: InvalidHeaders ( format ! ( "{}" , e) ) ) ?,
308
+ ) ;
309
+ headers. insert (
310
+ CONTENT_TYPE_HEADER ,
311
+ HeaderValue :: from_str ( SSZ_CONTENT_TYPE_HEADER )
312
+ . map_err ( |e| Error :: InvalidHeaders ( format ! ( "{}" , e) ) ) ?,
313
+ ) ;
314
+ headers. insert (
315
+ ACCEPT ,
316
+ HeaderValue :: from_str ( PREFERENCE_ACCEPT_VALUE )
317
+ . map_err ( |e| Error :: InvalidHeaders ( format ! ( "{}" , e) ) ) ?,
318
+ ) ;
298
319
299
320
let result = self
300
321
. post_ssz_with_raw_response (
@@ -326,9 +347,21 @@ impl BuilderHttpClient {
326
347
. push ( "blinded_blocks" ) ;
327
348
328
349
let mut headers = HeaderMap :: new ( ) ;
329
- if let Ok ( value) = HeaderValue :: from_str ( & blinded_block. fork_name_unchecked ( ) . to_string ( ) ) {
330
- headers. insert ( CONSENSUS_VERSION_HEADER , value) ;
331
- }
350
+ headers. insert (
351
+ CONSENSUS_VERSION_HEADER ,
352
+ HeaderValue :: from_str ( & blinded_block. fork_name_unchecked ( ) . to_string ( ) )
353
+ . map_err ( |e| Error :: InvalidHeaders ( format ! ( "{}" , e) ) ) ?,
354
+ ) ;
355
+ headers. insert (
356
+ CONTENT_TYPE_HEADER ,
357
+ HeaderValue :: from_str ( JSON_CONTENT_TYPE_HEADER )
358
+ . map_err ( |e| Error :: InvalidHeaders ( format ! ( "{}" , e) ) ) ?,
359
+ ) ;
360
+ headers. insert (
361
+ ACCEPT ,
362
+ HeaderValue :: from_str ( JSON_ACCEPT_VALUE )
363
+ . map_err ( |e| Error :: InvalidHeaders ( format ! ( "{}" , e) ) ) ?,
364
+ ) ;
332
365
333
366
Ok ( self
334
367
. post_with_raw_response (
@@ -362,12 +395,20 @@ impl BuilderHttpClient {
362
395
. push ( pubkey. as_hex_string ( ) . as_str ( ) ) ;
363
396
364
397
let mut headers = HeaderMap :: new ( ) ;
365
- if let Ok ( ssz_content_type_header) = HeaderValue :: from_str ( & format ! (
366
- "{}; q=1.0,{}; q=0.9" ,
367
- SSZ_CONTENT_TYPE_HEADER , JSON_CONTENT_TYPE_HEADER
368
- ) ) {
369
- headers. insert ( ACCEPT , ssz_content_type_header) ;
370
- } ;
398
+ if self . disable_ssz {
399
+ headers. insert (
400
+ ACCEPT ,
401
+ HeaderValue :: from_str ( JSON_CONTENT_TYPE_HEADER )
402
+ . map_err ( |e| Error :: InvalidHeaders ( format ! ( "{}" , e) ) ) ?,
403
+ ) ;
404
+ } else {
405
+ // Indicate preference for ssz response in the accept header
406
+ headers. insert (
407
+ ACCEPT ,
408
+ HeaderValue :: from_str ( PREFERENCE_ACCEPT_VALUE )
409
+ . map_err ( |e| Error :: InvalidHeaders ( format ! ( "{}" , e) ) ) ?,
410
+ ) ;
411
+ }
371
412
372
413
let resp = self
373
414
. get_with_header ( path, self . timeouts . get_header , headers)
@@ -395,3 +436,18 @@ impl BuilderHttpClient {
395
436
. await
396
437
}
397
438
}
439
+
440
+ #[ cfg( test) ]
441
+ mod tests {
442
+ use super :: * ;
443
+
444
+ #[ test]
445
+ fn test_headers_no_panic ( ) {
446
+ for fork in ForkName :: list_all ( ) {
447
+ assert ! ( HeaderValue :: from_str( & fork. to_string( ) ) . is_ok( ) ) ;
448
+ }
449
+ assert ! ( HeaderValue :: from_str( PREFERENCE_ACCEPT_VALUE ) . is_ok( ) ) ;
450
+ assert ! ( HeaderValue :: from_str( JSON_ACCEPT_VALUE ) . is_ok( ) ) ;
451
+ assert ! ( HeaderValue :: from_str( JSON_CONTENT_TYPE_HEADER ) . is_ok( ) ) ;
452
+ }
453
+ }
0 commit comments