send method
Implementation
@override
Future<AckResponse> send(AckRequest request) async {
final message = http.Request(request.method, request.url)
..headers.addAll(request.headers);
if (request.body != null) message.body = request.body!;
/*
* NO try/catch, and that is deliberate rather than an omission.
*
* Anything thrown here -- a timeout, a reset, DNS, a closed client -- is
* the UNKNOWN case, and `AckBizApi` already turns any throw into
* `AckUnreachable`, whose whole meaning is "this may have happened;
* reconcile, do not resend". Catching and rewrapping would bury the real
* cause one layer deeper in a log for no gain.
*/
final streamed = await _client.send(message).timeout(_patienceFor(request));
final response = await http.Response.fromStream(streamed);
/*
* EVERY status code returns, 500 included. NOTHING is thrown for one.
*
* This is the line an adopter gets wrong. A hand-written sender reaches
* for `if (response.statusCode >= 400) throw ...` because it reads like
* error handling -- and it collapses "the server refused" into "we do not
* know", which for a call that creates an order are opposite instructions.
* Refused means show the message; unknown means reconcile with the same
* idempotency key. Collapsing them charges someone twice.
*
* What a status means belongs to `AckApiException`, which already knows
* which ones are worth retrying.
*/
return AckResponse(
statusCode: response.statusCode,
body: response.body,
/*
* LOWER-CASED HERE, not assumed.
*
* `AckResponse` documents lower-case keys and `AckBizApi` looks the
* renewed-token header up by one. dart:io does lower-case a real
* response, so this looked safe -- but nothing enforced it, and a
* capitalised key would make the lookup miss SILENTLY. The device would
* then keep a token until it expired and re-register, which is the one
* call that can touch a seat.
*
* A contract stated in a doc comment and relied on by a lookup should be
* guaranteed by the code that produces it.
*/
headers: {
for (final entry in response.headers.entries)
entry.key.toLowerCase(): entry.value,
},
);
}