use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_util::FutureExt;
use http::StatusCode;
use http_body_util::BodyExt;
use jsonrpsee::server::{HttpBody, HttpRequest, HttpResponse};
use serde_json::Value;
use tower::{Layer, Service};
use tracing::debug;
#[derive(Clone, Debug)]
pub struct NotFound421Layer;
impl<S> Layer<S> for NotFound421Layer {
type Service = NotFound421<S>;
fn layer(&self, inner: S) -> Self::Service {
NotFound421 { inner }
}
}
#[derive(Clone, Debug)]
pub struct NotFound421<S> {
inner: S,
}
impl<S> Service<HttpRequest<HttpBody>> for NotFound421<S>
where
S: Service<HttpRequest<HttpBody>, Response = HttpResponse<HttpBody>> + Clone + Send + 'static,
S::Future: Send + 'static,
S::Error: Send + 'static,
{
type Response = HttpResponse<HttpBody>;
type Error = S::Error;
#[allow(clippy::type_complexity)]
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, req: HttpRequest<HttpBody>) -> Self::Future {
let fut = self.inner.call(req);
async move {
let res = fut.await?;
let (mut parts, body) = res.into_parts();
let bytes_vec: Vec<u8> = match body.collect().await {
Ok(b) => b.to_bytes().to_vec(),
Err(_) => {
return Ok(HttpResponse::from_parts(parts, HttpBody::empty()));
}
};
if is_result_null(&bytes_vec) {
parts.status = StatusCode::MISDIRECTED_REQUEST;
crate::metrics::rpc_misdirected();
}
Ok(HttpResponse::from_parts(parts, HttpBody::from(bytes_vec)))
}
.boxed()
}
}
fn is_result_null(bytes: &[u8]) -> bool {
let Ok(v) = serde_json::from_slice::<Value>(bytes) else {
debug!("non-JSON RPC response — leaving status untouched");
return false;
};
matches!(v.get("result"), Some(Value::Null))
}