Skip to content

Commit 9353382

Browse files
committed
Call route: when caller sends BSATN, respond in kind.
1 parent c7f8160 commit 9353382

1 file changed

Lines changed: 55 additions & 22 deletions

File tree

crates/client-api/src/routes/database.rs

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ pub async fn call<S: ControlStateDelegate + NodeDelegate>(
142142
) -> axum::response::Result<impl IntoResponse> {
143143
let caller_identity = auth.claims.identity;
144144

145-
let args = parse_call_args(content_type, body)?;
145+
let (args, want_bsatn) = parse_call_args(content_type, body)?;
146146

147147
// HTTP callers always need a connection ID to provide to connect/disconnect,
148148
// so generate one.
@@ -195,8 +195,14 @@ pub async fn call<S: ControlStateDelegate + NodeDelegate>(
195195

196196
match result {
197197
Ok(CallResult::Reducer(result)) => {
198-
let (status, body) =
199-
reducer_outcome_response(&module, &owner_identity, &reducer, result.outcome, reducer_return_value)?;
198+
let (status, body) = reducer_outcome_response(
199+
&module,
200+
&owner_identity,
201+
&reducer,
202+
result.outcome,
203+
reducer_return_value,
204+
want_bsatn,
205+
)?;
200206
Ok((
201207
status,
202208
TypedHeader(SpacetimeEnergyUsed(result.energy_used)),
@@ -209,7 +215,7 @@ pub async fn call<S: ControlStateDelegate + NodeDelegate>(
209215
// Procedures don't assign a special meaning to error returns, unlike reducers,
210216
// as there's no transaction for them to automatically abort.
211217
// Instead, we just pass on their return value with the OK status so long as we successfully invoked the procedure.
212-
let (status, body) = procedure_outcome_response(result.return_val);
218+
let (status, body) = procedure_outcome_response(result.return_val, want_bsatn);
213219
Ok((
214220
status,
215221
TypedHeader(SpacetimeExecutionDurationMicros(result.execution_duration)),
@@ -225,13 +231,15 @@ pub async fn call<S: ControlStateDelegate + NodeDelegate>(
225231
///
226232
/// - `application/json` → [`FunctionArgs::Json`] (UTF-8 required).
227233
/// - `application/octet-stream` → [`FunctionArgs::Bsatn`] (raw BSATN bytes).
228-
fn parse_call_args(content_type: headers::ContentType, body: Bytes) -> axum::response::Result<FunctionArgs> {
234+
///
235+
/// Also returns `want_bsatn`, a bool, which is true if and only if the response should be BSATN-encoded.
236+
fn parse_call_args(content_type: headers::ContentType, body: Bytes) -> axum::response::Result<(FunctionArgs, bool)> {
229237
if content_type == headers::ContentType::json() {
230238
let s = bytestring::ByteString::try_from(body)
231239
.map_err(|_| (StatusCode::BAD_REQUEST, "request body is not valid UTF-8").into_response())?;
232-
Ok(FunctionArgs::Json(s))
240+
Ok((FunctionArgs::Json(s), false))
233241
} else if content_type == headers::ContentType::octet_stream() {
234-
Ok(FunctionArgs::Bsatn(body))
242+
Ok((FunctionArgs::Bsatn(body), true))
235243
} else {
236244
Err((
237245
StatusCode::BAD_REQUEST,
@@ -241,12 +249,18 @@ fn parse_call_args(content_type: headers::ContentType, body: Bytes) -> axum::res
241249
}
242250
}
243251

252+
/// Encode a reducer return value as an HTTP response.
253+
///
254+
/// If the outcome is an error, return a raw string with `application/text`. Ignore `want_bsatn` in this case.
255+
/// If the outcome is successful, and `want_bsatn`, send BSATN with `application/octet-stream`.
256+
/// If the outcome is successful, and not `want_bsatn`, send JSON with `application/json`.
244257
fn reducer_outcome_response(
245258
module: &ModuleHost,
246259
owner_identity: &Identity,
247260
reducer: &str,
248261
outcome: ReducerOutcome,
249262
reducer_return_value: Option<Bytes>,
263+
want_bsatn: bool,
250264
) -> axum::response::Result<(StatusCode, axum::response::Response)> {
251265
match outcome {
252266
ReducerOutcome::Committed => {
@@ -258,20 +272,29 @@ fn reducer_outcome_response(
258272
};
259273

260274
if let Some(bytes) = reducer_return_value.filter(|value| !value.is_empty()) {
261-
let seed = sats::WithTypespace::new(module.info.module_def.typespace(), &return_value);
262-
let mut reader = &bytes[..];
263-
let value: AlgebraicValue = seed.deserialize(bsatn::Deserializer::new(&mut reader)).map_err(|err| {
264-
(
265-
StatusCode::INTERNAL_SERVER_ERROR,
266-
format!("Failed to decode reducer return value: {err}"),
267-
)
268-
})?;
269-
Ok((
270-
StatusCode::OK,
271-
axum::Json(sats::serde::SerdeWrapper(value)).into_response(),
272-
))
275+
if want_bsatn {
276+
Ok((StatusCode::OK, bytes.into_response()))
277+
} else {
278+
let seed = sats::WithTypespace::new(module.info.module_def.typespace(), &return_value);
279+
let mut reader = &bytes[..];
280+
let value: AlgebraicValue =
281+
seed.deserialize(bsatn::Deserializer::new(&mut reader)).map_err(|err| {
282+
(
283+
StatusCode::INTERNAL_SERVER_ERROR,
284+
format!("Failed to decode reducer return value: {err}"),
285+
)
286+
})?;
287+
Ok((
288+
StatusCode::OK,
289+
axum::Json(sats::serde::SerdeWrapper(value)).into_response(),
290+
))
291+
}
273292
} else {
274-
Ok((StatusCode::OK, "".into_response()))
293+
if want_bsatn {
294+
Ok((StatusCode::OK, Vec::<u8>::new().into_response()))
295+
} else {
296+
Ok((StatusCode::OK, "".into_response()))
297+
}
275298
}
276299
}
277300
ReducerOutcome::Failed(errmsg) => {
@@ -354,10 +377,20 @@ pub enum DBCallErr {
354377
InstanceNotScheduled,
355378
}
356379

357-
fn procedure_outcome_response(return_val: AlgebraicValue) -> (StatusCode, axum::response::Response) {
380+
/// Encode a procedure's result as an HTTP response.
381+
///
382+
/// If `want_bsatn`, send BSATN bytes as `application/octet-stream`.
383+
/// If not `want_bsatn`, send JSON as `application/json`.
384+
fn procedure_outcome_response(return_val: AlgebraicValue, want_bsatn: bool) -> (StatusCode, axum::response::Response) {
358385
(
359386
StatusCode::OK,
360-
axum::Json(sats::serde::SerdeWrapper(return_val)).into_response(),
387+
if want_bsatn {
388+
bsatn::to_vec(&return_val)
389+
.expect("BSATN serializing an AlgebraicValue should be infallible")
390+
.into_response()
391+
} else {
392+
axum::Json(sats::serde::SerdeWrapper(return_val)).into_response()
393+
},
361394
)
362395
}
363396

0 commit comments

Comments
 (0)