handlePostMessage method

Future<void> handlePostMessage(
  1. HttpRequest request, {
  2. dynamic parsedBody,
})

Handles incoming HTTP POST requests containing client messages.

Parses the request body as JSON, validates it, and invokes the onmessage callback with the parsed message.

Implementation

Future<void> handlePostMessage(
  HttpRequest request, {
  dynamic parsedBody,
}) async {
  final response = request.response;

  if (_closeController.isClosed) {
    response.statusCode = HttpStatus.serviceUnavailable;
    response.write("SSE connection not established or closed.");
    await response.close();
    onerror?.call(
      StateError("Received POST message but SSE connection is not active."),
    );
    return;
  }

  if (request.method != 'POST') {
    response.statusCode = HttpStatus.methodNotAllowed;
    response.headers.set(HttpHeaders.allowHeader, 'POST');
    response.write("Method Not Allowed. Use POST.");
    await response.close();
    return;
  }

  ContentType? contentType;
  try {
    contentType = request.headers.contentType ?? ContentType.json;
  } catch (e) {
    response.statusCode = HttpStatus.badRequest;
    response.write("Invalid Content-Type header: $e");
    await response.close();
    onerror?.call(
      ArgumentError("Invalid Content-Type header in POST request."),
    );
    return;
  }

  if (contentType.mimeType != 'application/json') {
    response.statusCode = HttpStatus.unsupportedMediaType;
    response.write(
      "Unsupported Content-Type: ${request.headers.contentType?.mimeType}. Expected 'application/json'.",
    );
    await response.close();
    onerror?.call(
      ArgumentError(
        "Unsupported Content-Type in POST request: ${request.headers.contentType?.mimeType}",
      ),
    );
    return;
  }

  dynamic messageJson;
  try {
    if (parsedBody != null) {
      messageJson = parsedBody;
    } else {
      final bodyBytes =
          await request.fold<BytesBuilder>(BytesBuilder(), (builder, chunk) {
        builder.add(chunk);
        if (builder.length > _maximumMessageSize) {
          throw HttpException(
            "Message size exceeds limit of $_maximumMessageSize bytes.",
          );
        }
        return builder;
      }).then((builder) => builder.toBytes());

      final encoding =
          Encoding.getByName(contentType.parameters['charset']) ?? utf8;
      final bodyString = encoding.decode(bodyBytes);
      messageJson = jsonDecode(bodyString);
    }

    if (messageJson is! Map<String, dynamic>) {
      throw FormatException(
        "Invalid JSON message format: Expected a JSON object.",
      );
    }

    await handleMessage(messageJson);

    response.statusCode = HttpStatus.accepted;
    response.write("Accepted");
    await response.close();
  } catch (error) {
    onerror?.call(
      error is Error
          ? error
          : StateError("Error handling POST message: $error"),
    );
    response.statusCode = HttpStatus.internalServerError;
    response.write("Error processing message: $error");
    await response.close();
  }
}