Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion crates/common/src/config/mux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,6 @@ impl MuxKeysLoader {
"Mux keys URL {url} is insecure; consider using HTTPS if possible instead"
);
}
let url = url.as_str();
let client = reqwest::ClientBuilder::new().timeout(http_timeout).build()?;
let response = client.get(url).send().await?;
let pubkey_bytes = safe_read_http_response(response, MUXER_HTTP_MAX_LENGTH).await?;
Expand Down
3 changes: 3 additions & 0 deletions crates/common/src/pbs/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ pub enum PbsError {
#[error("json decode error: {err:?}, raw: {raw}")]
JsonDecode { err: serde_json::Error, raw: String },

#[error("ssz decode error: {err:?}, fork: {fork}")]
SSZDecode { err: String, fork: ForkName },

#[error("{0}")]
ReadResponse(#[from] ResponseReadError),

Expand Down
30 changes: 24 additions & 6 deletions crates/common/src/wire.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use lh_types::{BeaconBlock, ForkName};
use mediatype::{MediaType, ReadParams};
use reqwest::{
Response,
header::{ACCEPT, CONTENT_TYPE, HeaderMap},
header::{ACCEPT, CONTENT_TYPE, HeaderMap, ToStrError},
};
use thiserror::Error;

Expand All @@ -37,6 +37,18 @@ pub enum ResponseReadError {
NonSuccess { status_code: u16, error_msg: String, request_url: String },
}

#[derive(Debug, Error)]
pub enum AcceptedEncodingsError {
#[error("invalid header string: {0}")]
InvalidString(#[from] ToStrError),

#[error("invalid accept header: {error_msg}")]
InvalidEncoding { error_msg: String },

#[error("unsupported accept type")]
UnsupportedAcceptType,
}

#[cfg(feature = "testing-flags")]
thread_local! {
static IGNORE_CONTENT_LENGTH: Cell<bool> = const { Cell::new(false) };
Expand Down Expand Up @@ -108,7 +120,7 @@ pub async fn read_chunked_body_with_max(
/// Reads an HTTP response body with a size limit, erroring on non-success
/// status or read failure.
pub async fn safe_read_http_response(
response: reqwest::Response,
response: Response,
max_size: usize,
) -> Result<Vec<u8>, ResponseReadError> {
let status_code = response.status();
Expand Down Expand Up @@ -198,16 +210,22 @@ impl IntoIterator for AcceptedEncodings {
/// The returned order honors the RFC 9110 §12.5.1 precedence rules already
/// applied by `headers_accept::Accept::media_types()` (specificity, then
/// q-value, then original order).
pub fn get_accept_types(req_headers: &HeaderMap) -> eyre::Result<AcceptedEncodings> {
pub fn get_accept_types(
req_headers: &HeaderMap,
) -> Result<AcceptedEncodings, AcceptedEncodingsError> {
// Only two supported media types, so the ordered set is at most two
// entries: primary + optional fallback.
let mut primary: Option<EncodingType> = None;
let mut fallback: Option<EncodingType> = None;
let mut saw_any = false;
let mut had_supported = false;
for header in req_headers.get_all(ACCEPT).iter() {
let accept = Accept::from_str(header.to_str()?)
.map_err(|e| eyre::eyre!("invalid accept header: {e}"))?;
let accept_str = header.to_str().map_err(AcceptedEncodingsError::InvalidString)?;
let accept =
Accept::from_str(accept_str).map_err(|e| AcceptedEncodingsError::InvalidEncoding {
error_msg: (format!("invalid accept header: {e}")).to_string(),
})?;

for mt in accept.media_types() {
saw_any = true;

Expand Down Expand Up @@ -243,7 +261,7 @@ pub fn get_accept_types(req_headers: &HeaderMap) -> eyre::Result<AcceptedEncodin
}

if saw_any && !had_supported {
eyre::bail!("unsupported accept type");
return Err(AcceptedEncodingsError::UnsupportedAcceptType)
}

// No accept header (or only q=0 rejections): fall back to the request
Expand Down
13 changes: 10 additions & 3 deletions crates/pbs/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use axum::{http::StatusCode, response::IntoResponse};
use cb_common::wire::BodyDeserializeError;
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
};
use cb_common::wire::{AcceptedEncodingsError, BodyDeserializeError};
use thiserror::Error;

#[derive(Debug, Error)]
Expand All @@ -13,6 +16,8 @@ pub enum PbsClientError {
Internal,
#[error("failed to deserialize body: {0}")]
DecodeError(#[from] BodyDeserializeError),
#[error("invalid accept types: {0}")]
HeaderError(#[from] AcceptedEncodingsError),
}

impl PbsClientError {
Expand All @@ -22,17 +27,19 @@ impl PbsClientError {
PbsClientError::NoPayload => StatusCode::BAD_GATEWAY,
PbsClientError::Internal => StatusCode::INTERNAL_SERVER_ERROR,
PbsClientError::DecodeError(_) => StatusCode::BAD_REQUEST,
PbsClientError::HeaderError(_) => StatusCode::BAD_REQUEST,
}
}
}

impl IntoResponse for PbsClientError {
fn into_response(self) -> axum::response::Response {
fn into_response(self) -> Response {
let msg = match &self {
PbsClientError::NoResponse => "no response from relays".to_string(),
PbsClientError::NoPayload => "no payload from relays".to_string(),
PbsClientError::Internal => "internal server error".to_string(),
PbsClientError::DecodeError(e) => format!("error decoding request: {e}"),
PbsClientError::HeaderError(e) => format!("header error: {e}"),
};

(self.status_code(), msg).into_response()
Expand Down
Loading
Loading