Skip to main content

borderless_runtime/
http.rs

1use bytes::Bytes;
2use http::request::Parts;
3use http::{header::CONTENT_TYPE, HeaderValue, StatusCode};
4use mime::{APPLICATION_JSON, TEXT_PLAIN_UTF_8};
5use serde::de::DeserializeOwned;
6use serde::Serialize;
7pub use tower::Service;
8
9#[cfg(feature = "contracts")]
10pub mod contract;
11
12#[cfg(feature = "agents")]
13pub mod agent;
14
15#[cfg(feature = "contracts")]
16pub mod ledger;
17
18pub type Request<T = Bytes> = http::Request<T>;
19pub type Response<T = Bytes> = http::Response<T>;
20
21pub fn reject_404() -> Response {
22    let mut resp = Response::new(Bytes::new());
23    *resp.status_mut() = StatusCode::NOT_FOUND;
24    resp
25}
26
27pub fn method_not_allowed() -> Response {
28    let mut resp = Response::new(Bytes::new());
29    *resp.status_mut() = StatusCode::METHOD_NOT_ALLOWED;
30    resp
31}
32
33pub fn unsupported_media_type() -> Response {
34    let mut resp = Response::new(Bytes::new());
35    *resp.status_mut() = StatusCode::UNSUPPORTED_MEDIA_TYPE;
36    resp
37}
38
39#[derive(Serialize)]
40struct ErrBody {
41    status: String,
42    error: String,
43}
44
45pub fn bad_request(err: String) -> Response {
46    err_response(StatusCode::BAD_REQUEST, err)
47}
48
49pub fn err_response(status: StatusCode, err_msg: String) -> Response {
50    let body = ErrBody {
51        status: status.to_string(),
52        error: err_msg,
53    };
54    let mut resp = json_response(&body);
55    *resp.status_mut() = status;
56    resp
57}
58
59pub fn json_response<S: Serialize>(value: &S) -> Response<Bytes> {
60    let bytes = serde_json::to_vec(value).unwrap_or_default();
61    json_body(bytes)
62}
63
64pub fn json_body(bytes: Vec<u8>) -> Response<Bytes> {
65    let mut resp = Response::new(bytes.into());
66    resp.headers_mut().insert(
67        CONTENT_TYPE,
68        HeaderValue::from_static(APPLICATION_JSON.as_ref()),
69    );
70    resp
71}
72
73pub(crate) fn json_response_nested<S: Serialize + DeserializeOwned>(
74    value: S,
75    truncated_path: &str,
76) -> Response<Bytes> {
77    use borderless::__private::storage_traits::ToPayload;
78    match value.to_payload(truncated_path) {
79        Ok(Some(s)) => json_body(s.into_bytes()),
80        Ok(None) => json_response(&None::<()>),
81        Err(e) => into_server_error(e),
82    }
83}
84
85pub fn check_json_content(parts: &Parts) -> bool {
86    if let Some(content_type) = parts.headers.get(CONTENT_TYPE) {
87        content_type.as_bytes() == APPLICATION_JSON.as_ref().as_bytes()
88    } else {
89        false
90    }
91}
92
93/// Converts any error-type into a http-response with status-code 500
94///
95/// The response has content-type set to `text/plain; charset=utf-8`
96/// and the body contains the error message as string.
97pub fn into_server_error<E: ToString>(error: E) -> Response {
98    let mut resp = Response::new(error.to_string().into_bytes().into());
99    resp.headers_mut().append(
100        CONTENT_TYPE,
101        HeaderValue::from_static(TEXT_PLAIN_UTF_8.as_ref()),
102    );
103    *resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
104    resp
105}