using Invercargill; using Invercargill.DataStructures; using InvercargillJson; using Inversion; using Astralis; namespace Statum { /** * `POST /_statum/slots` — refreshes snapshots whose `transmit_after` window * has elapsed, and is also the target of a `transmit(retry)` directive. * * The client POSTs a JSON array of Statum frames it holds. For each frame * this endpoint verifies the signature: on success it * {@link StateService.restore_slot}s the slot into the cache, re-signs it * (issuing a fresh `as_at`/`transmit_after`), and returns a `set` directive * so the client refreshes its local snapshot; on failure it returns an * `invalidate` for that frame's signature so the client drops the slot whose * frame the server could not trust. */ public class SlotPostEndpoint : Object, Endpoint { private StateService state_service = inject(); private Cryptography.SigningProvider signing_provider = inject(); public async HttpResult handle_request(HttpContext http_context, RouteContext route_context) throws GLib.Error { var body = yield http_context.request.request_body.read_all(); var body_str = body.to_raw_string(); var directives = new Series(); var invalid_signatures = new Series(); if (body_str.strip().length > 0) { var body_element = new JsonElement.from_string(body_str); var array = body_element.as(); foreach (var item in array) { var frame = Model.FrameDto.get_mapper().materialise(item.as()); try { signing_provider.verify_frame(frame); } catch (GLib.Error e) { // Signature did not verify: tell the client to drop the // slot holding this frame, identified by signature. if (frame.signature != null && frame.signature.length > 0) { invalid_signatures.add(frame.signature); } continue; } var content_element = new JsonElement.from_string(frame.content); var content_object = content_element.as(); var snapshot = Model.SnapshotDto.get_mapper().materialise(content_object); state_service.restore_slot(frame); var refreshed = state_service.sign_slot(snapshot.slot.key); if (refreshed != null) { directives.add(new Model.SetDirectiveDto.with_frame((!)refreshed)); } } } return StatumDirectives.to_result(directives, invalid_signatures); } } }