Skip to content

Commit c800921

Browse files
committed
Rewrite get_header relay communication to support SSZ and JSON
content negotiation. Includes: - SSZ and JSON encoding/decoding for relay get_header responses - Content-Type and Eth-Consensus-Version header parsing - Accept header forwarding to relays with q-value ordering - MIME parameter tolerance on relay response Content-Type - Fork-aware SSZ bid value extraction for all supported forks - Mock relay and validator SSZ support in test infrastructure - get_header integration tests for both encodings - Dynamic port allocation in tests via get_free_listener
1 parent 3e62c4c commit c800921

15 files changed

Lines changed: 1270 additions & 302 deletions

File tree

‎Cargo.lock‎

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎crates/common/src/pbs/error.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ pub enum PbsError {
1515
#[error("json decode error: {err:?}, raw: {raw}")]
1616
JsonDecode { err: serde_json::Error, raw: String },
1717

18+
#[error("ssz decode error: {err:?}, fork: {fork}")]
19+
SSZDecode { err: String, fork: ForkName },
20+
1821
#[error("{0}")]
1922
ReadResponse(#[from] ResponseReadError),
2023

‎crates/common/src/wire.rs‎

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use lh_types::{BeaconBlock, ForkName};
1010
use mediatype::{MediaType, ReadParams};
1111
use reqwest::{
1212
Response,
13-
header::{ACCEPT, CONTENT_TYPE, HeaderMap},
13+
header::{ACCEPT, CONTENT_TYPE, HeaderMap, ToStrError},
1414
};
1515
use thiserror::Error;
1616

@@ -37,6 +37,18 @@ pub enum ResponseReadError {
3737
NonSuccess { status_code: u16, error_msg: String, request_url: String },
3838
}
3939

40+
#[derive(Debug, Error)]
41+
pub enum AcceptedEncodingsError {
42+
#[error("invalid header string: {0}")]
43+
InvalidString(#[from] ToStrError),
44+
45+
#[error("invalid accept header: {error_msg}")]
46+
InvalidEncoding { error_msg: String },
47+
48+
#[error("unsupported accept type")]
49+
UnsupportedAcceptType,
50+
}
51+
4052
#[cfg(feature = "testing-flags")]
4153
thread_local! {
4254
static IGNORE_CONTENT_LENGTH: Cell<bool> = const { Cell::new(false) };
@@ -108,7 +120,7 @@ pub async fn read_chunked_body_with_max(
108120
/// Reads an HTTP response body with a size limit, erroring on non-success
109121
/// status or read failure.
110122
pub async fn safe_read_http_response(
111-
response: reqwest::Response,
123+
response: Response,
112124
max_size: usize,
113125
) -> Result<Vec<u8>, ResponseReadError> {
114126
let status_code = response.status();
@@ -198,16 +210,24 @@ impl IntoIterator for AcceptedEncodings {
198210
/// The returned order honors the RFC 9110 §12.5.1 precedence rules already
199211
/// applied by `headers_accept::Accept::media_types()` (specificity, then
200212
/// q-value, then original order).
201-
pub fn get_accept_types(req_headers: &HeaderMap) -> eyre::Result<AcceptedEncodings> {
213+
// pub fn get_accept_types(req_headers: &HeaderMap) ->
214+
// eyre::Result<AcceptedEncodings> {
215+
pub fn get_accept_types(
216+
req_headers: &HeaderMap,
217+
) -> Result<AcceptedEncodings, AcceptedEncodingsError> {
202218
// Only two supported media types, so the ordered set is at most two
203219
// entries: primary + optional fallback.
204220
let mut primary: Option<EncodingType> = None;
205221
let mut fallback: Option<EncodingType> = None;
206222
let mut saw_any = false;
207223
let mut had_supported = false;
208224
for header in req_headers.get_all(ACCEPT).iter() {
209-
let accept = Accept::from_str(header.to_str()?)
210-
.map_err(|e| eyre::eyre!("invalid accept header: {e}"))?;
225+
let accept_str = header.to_str().map_err(AcceptedEncodingsError::InvalidString)?;
226+
let accept =
227+
Accept::from_str(accept_str).map_err(|e| AcceptedEncodingsError::InvalidEncoding {
228+
error_msg: (format!("invalid accept header: {e}")).to_string(),
229+
})?;
230+
211231
for mt in accept.media_types() {
212232
saw_any = true;
213233

@@ -243,7 +263,7 @@ pub fn get_accept_types(req_headers: &HeaderMap) -> eyre::Result<AcceptedEncodin
243263
}
244264

245265
if saw_any && !had_supported {
246-
eyre::bail!("unsupported accept type");
266+
return Err(AcceptedEncodingsError::UnsupportedAcceptType)
247267
}
248268

249269
// No accept header (or only q=0 rejections): fall back to the request

‎crates/pbs/src/error.rs‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
use axum::{http::StatusCode, response::IntoResponse};
2-
use cb_common::wire::BodyDeserializeError;
1+
use axum::{
2+
http::StatusCode,
3+
response::{IntoResponse, Response},
4+
};
5+
use cb_common::wire::{AcceptedEncodingsError, BodyDeserializeError};
36
use thiserror::Error;
47

58
#[derive(Debug, Error)]
@@ -13,6 +16,8 @@ pub enum PbsClientError {
1316
Internal,
1417
#[error("failed to deserialize body: {0}")]
1518
DecodeError(#[from] BodyDeserializeError),
19+
#[error("invalid accept types: {0}")]
20+
HeaderError(#[from] AcceptedEncodingsError),
1621
}
1722

1823
impl PbsClientError {
@@ -22,17 +27,19 @@ impl PbsClientError {
2227
PbsClientError::NoPayload => StatusCode::BAD_GATEWAY,
2328
PbsClientError::Internal => StatusCode::INTERNAL_SERVER_ERROR,
2429
PbsClientError::DecodeError(_) => StatusCode::BAD_REQUEST,
30+
PbsClientError::HeaderError(_) => StatusCode::BAD_REQUEST,
2531
}
2632
}
2733
}
2834

2935
impl IntoResponse for PbsClientError {
30-
fn into_response(self) -> axum::response::Response {
36+
fn into_response(self) -> Response {
3137
let msg = match &self {
3238
PbsClientError::NoResponse => "no response from relays".to_string(),
3339
PbsClientError::NoPayload => "no payload from relays".to_string(),
3440
PbsClientError::Internal => "internal server error".to_string(),
3541
PbsClientError::DecodeError(e) => format!("error decoding request: {e}"),
42+
PbsClientError::HeaderError(e) => format!("header error: {e}"),
3643
};
3744

3845
(self.status_code(), msg).into_response()

0 commit comments

Comments
 (0)