MCPcopy Create free account
hub / github.com/clockworklabs/SpacetimeDB / http_request

Method http_request

crates/core/src/host/instance_env.rs:855–1008  ·  view source on GitHub ↗

Perform an HTTP request. Exposed to modules via the `ProcedureContext`. It's very important that the error returned from this function not contain any potentially sensitive data from `request`, such as the query parameters or header values. This way, it's safe to log the errors (either for us to do so, or for module code to do so), and less dangerous to send them to the calling client of a proced

(
        &mut self,
        request: st_http::Request,
        body: bytes::Bytes,
    )

Source from the content-addressed store, hash-verified

853 /// This way, it's safe to log the errors (either for us to do so, or for module code to do so),
854 /// and less dangerous to send them to the calling client of a procedure.
855 pub fn http_request(
856 &mut self,
857 request: st_http::Request,
858 body: bytes::Bytes,
859 ) -> Result<impl Future<Output = Result<(st_http::Response, bytes::Bytes), NodesError>> + use<>, NodesError> {
860 if self.in_tx() {
861 // If we're holding a transaction open, refuse to perform this blocking operation.
862 return Err(NodesError::WouldBlockTransaction(super::AbiCall::ProcedureHttpRequest));
863 }
864
865 // Record in metrics that we're starting an HTTP request.
866 DB_METRICS
867 .procedure_num_http_requests
868 .with_label_values(self.database_identity())
869 .inc();
870 DB_METRICS
871 .procedure_http_request_size_bytes
872 .with_label_values(self.database_identity())
873 .inc_by((request.size_in_bytes() + body.len()) as _);
874 // Make a guard for the `in_progress` metric that will be decremented on exit.
875 let _in_progress_metric = DB_METRICS
876 .procedure_num_in_progress_http_requests
877 .with_label_values(self.database_identity())
878 .inc_scope();
879
880 /// Strip the query part out of the URL in `err`, as query parameters may be sensitive
881 /// and we'd like it to be safe to directly log errors from this method.
882 fn strip_query_params_from_reqwest_error(mut err: reqwest::Error) -> reqwest::Error {
883 if let Some(url) = err.url_mut() {
884 // `set_query` of `None` clears the query part.
885 url.set_query(None);
886 }
887 err
888 }
889
890 fn http_error<E: std::error::Error>(err: E) -> NodesError {
891 // Include the full error chain, not just the top-level message.
892 // `reqwest::Error` wraps underlying causes (DNS failure, connection refused,
893 // timeout, TLS errors, etc.) which are essential for debugging.
894 use std::fmt::Write;
895 let mut message = err.to_string();
896 let mut source = err.source();
897 while let Some(cause) = source {
898 write!(message, ": {cause}").unwrap();
899 source = cause.source();
900 }
901 NodesError::HttpError(message)
902 }
903
904 // Then convert the request into an `http::Request`, a semi-standard "lingua franca" type in the Rust ecosystem,
905 // and map its body into a type `reqwest` will like.
906 //
907 // See comments on and in `convert_http_request` for justification that there's no sensitive info in this error.
908 let (request, timeout) = convert_http_request(request).map_err(http_error)?;
909
910 let request = http::Request::from_parts(request, body);
911
912 let mut reqwest: reqwest::Request = request

Callers 2

procedure_http_requestFunction · 0.80

Calls 15

convert_http_requestFunction · 0.85
is_blocked_ip_literalFunction · 0.85
HttpErrorClass · 0.85
customFunction · 0.85
convert_http_responseFunction · 0.85
in_txMethod · 0.80
size_in_bytesMethod · 0.80
inc_scopeMethod · 0.80
try_intoMethod · 0.80
unwrap_orMethod · 0.80
into_partsMethod · 0.80
to_bytesMethod · 0.80

Tested by

no test coverage detected