mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-05-06 23:59:43 -04:00
8dd18f078f
# Description of Changes Rust added procedure-scoped HTTP via the `procedure_http_request` ABI (see Rust PR #3684) to C# host bindings. What changed: 1) Fix for BSATN wire format * Updated C# BSATN wire types for HTTP (`BSATN.Runtime/HttpWireTypes.cs`) to match exactly the Rust stable types: * `spacetimedb_lib::http::Request` fields and order: `method`, `headers`, `timeout`, `uri`, `version` * `spacetimedb_lib::http::Response` fields and order: `headers`, `version`, `code` * Header pairs are (`name: string, value: bytes`); no additional metadata is encoded. * Added explicit “do not reorder/extend” note in the C# wire type file since the host BSATN-decodes these bytes directly and may trap on mismatch. 2) C# runtime HTTP client implementation * Implemented `ProcedureContext.Http` support (`Runtime/Http.cs`) that: * BSATN-serializes `HttpRequestWire` + sends body bytes via `procedure_http_request` * On success, BSATN-decodes `HttpResponseWire` and returns `(response, body)` as a typed `HttpResponse` * On `HTTP_ERROR`, decodes the error payload as a BSATN `string` * On `WOULD_BLOCK_TRANSACTION`, returns a stable error message (host rejects blocking HTTP while a mut tx is open) * Clamps timeouts to 500ms max (matching host behavior documented on the Rust side) 3) ABI wiring / import plumbing * Added the `procedure_http_request` import to the C# wasm shim (`Runtime/bindings.c`) under the `spacetime_10.3` import module. * Verified the generated C# P/Invoke signature matches the host ABI (request ptr/len, body ptr/len, out `[BytesSource; 2]`). 4) Procedure entrypoint contract (trap vs errno) * Updated/clarified the module procedure trampoline behavior (`Runtime/Internal/Module.call_procedure`): it must either return `Errno.OK` or trap (log + rethrow). This mirrors the host’s expectations and avoids “unexpected errno values from guest entrypoints” behavior. Behavioral notes vs Rust * Rust bindings may `expect(...)`/panic on “should never happen” serialization failures; C# runtime explicitly avoids throwing across the procedure boundary for the HTTP client path and instead returns `Result.Err` for unexpected failures. This prevents whole-module traps from user-space HTTP usage while still surfacing rich errors. * The host remains authoritative on timeout enforcement; C# now mirrors the effective host clamp (500ms) to keep behavior aligned. # API and ABI breaking changes No new host ABI introduced, and does not change the syscall signature. * This is not an ABI “break” in the host, but it is a guest-side wire compatibility correction: older C# guests built with the incorrect wire types could cause the host to trap during BSATN decode. After this PR, C# guests are compatible with the host’s expected layout. **Adds API**: `ProcedureContextBase.Http` is available for procedures No existing public types/method signatures are removed * A notable difference from Rust's panic behavior is that Rust code sometimes `expect(...)`s and may panic/trap on internal “should not happen” cases. On the other hand, C#'s `HttpClient.Send` explicitly converts unexpected failures into `Result.Err` to avoid trapping the module. This is a behavioral difference, but not an API break. # Expected complexity level and risk 2 - Mostly just creating equivalents to the Rust version. # Testing C# regression tests updated to cover: - [X] Successful request path (`ReadMySchemaViaHttp`) - [X] Invalid request path (`InvalidHttpRequest`) returning error without trapping Testing performed: - [X] Full regression suite run against local node (see chat log); no wasm traps, all suites reported `Success`. --------- Signed-off-by: Ryan <r.ekhoff@clockworklabs.io>
114 lines
3.0 KiB
C#
114 lines
3.0 KiB
C#
namespace SpacetimeDB;
|
|
|
|
using SpacetimeDB.Internal;
|
|
|
|
public abstract class StdbException : Exception
|
|
{
|
|
public abstract override string Message { get; }
|
|
}
|
|
|
|
public class NotInTransactionException : StdbException
|
|
{
|
|
public override string Message => "ABI call can only be made while in a transaction";
|
|
}
|
|
|
|
public class BsatnDecodeException : StdbException
|
|
{
|
|
public override string Message => "Couldn't decode the BSATN to the expected type";
|
|
}
|
|
|
|
public class NoSuchTableException : StdbException
|
|
{
|
|
public override string Message => "No such table";
|
|
}
|
|
|
|
public class NoSuchIndexException : StdbException
|
|
{
|
|
public override string Message => "No such index";
|
|
}
|
|
|
|
public class IndexNotUniqueException : StdbException
|
|
{
|
|
public override string Message => "The index was not unique";
|
|
}
|
|
|
|
public class NoSuchRowException : StdbException
|
|
{
|
|
public override string Message => "The row was not found, e.g., in an update call";
|
|
}
|
|
|
|
public class UniqueConstraintViolationException : StdbException
|
|
{
|
|
public override string Message => "Value with given unique identifier already exists";
|
|
}
|
|
|
|
public class ScheduleAtDelayTooLongException : StdbException
|
|
{
|
|
public override string Message => "Specified delay in scheduling row was too long";
|
|
}
|
|
|
|
public class BufferTooSmallException : StdbException
|
|
{
|
|
public override string Message => "The provided buffer is not large enough to store the data";
|
|
}
|
|
|
|
public class NoSuchIterException : StdbException
|
|
{
|
|
public override string Message => "The provided row iterator does not exist";
|
|
}
|
|
|
|
public class NoSuchLogStopwatch : StdbException
|
|
{
|
|
public override string Message => "The provided stopwatch does not exist";
|
|
}
|
|
|
|
public class NoSuchBytesException : StdbException
|
|
{
|
|
public override string Message => "The provided bytes source or sink does not exist";
|
|
}
|
|
|
|
public class NoSpaceException : StdbException
|
|
{
|
|
public override string Message => "The provided bytes sink has no more room left";
|
|
}
|
|
|
|
public class AutoIncOverflowException : StdbException
|
|
{
|
|
public override string Message => "The auto-increment sequence overflowed";
|
|
}
|
|
|
|
public class TransactionWouldBlockException : StdbException
|
|
{
|
|
public override string Message => "Attempted operation while another transaction is open";
|
|
}
|
|
|
|
public class TransactionNotAnonymousException : StdbException
|
|
{
|
|
public override string Message => "The transaction is not anonymous";
|
|
}
|
|
|
|
public class TransactionIsReadOnlyException : StdbException
|
|
{
|
|
public override string Message => "The transaction is read-only";
|
|
}
|
|
|
|
public class TransactionIsMutableException : StdbException
|
|
{
|
|
public override string Message =>
|
|
"ABI call can only be made while inside a read-only transaction";
|
|
}
|
|
|
|
public class HttpException : StdbException
|
|
{
|
|
public override string Message => "HTTP request failed";
|
|
}
|
|
|
|
public class UnknownException : StdbException
|
|
{
|
|
private readonly Errno code;
|
|
|
|
internal UnknownException(Errno code) => this.code = code;
|
|
|
|
public override string Message => $"SpacetimeDB error code {code}";
|
|
}
|