@@ -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`.
244257fn 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