handleRequest method

Future<bool> handleRequest(
  1. RequestContext req,
  2. ResponseContext res
)

Handles an incoming HTTP request.

Implementation

Future<bool> handleRequest(RequestContext req, ResponseContext res) async {
  if (req is HttpRequestContext && res is HttpResponseContext) {
    if (!WebSocketTransformer.isUpgradeRequest(req.rawRequest!)) {
      throw AngelHttpException.badRequest();
    }
    res.detach();
    var ws = await WebSocketTransformer.upgrade(req.rawRequest!);
    var channel = IOWebSocketChannel(ws);
    var socket = WebSocketContext(channel, req, res);
    scheduleMicrotask(() => handleClient(socket));
    return false;
  } else if (req is Http2RequestContext && res is Http2ResponseContext) {
    var connection =
        req.headers?['connection']?.map((s) => s.toLowerCase().trim());
    var upgrade = req.headers?.value('upgrade')?.toLowerCase();
    var version = req.headers?.value('sec-websocket-version');
    var key = req.headers?.value('sec-websocket-key');
    var protocol = req.headers?.value('sec-websocket-protocol');

    if (connection == null) {
      throw AngelHttpException.badRequest(
          message: 'Missing `connection` header.');
    } else if (!connection.contains('upgrade')) {
      throw AngelHttpException.badRequest(
          message: 'Missing "upgrade" in `connection` header.');
    } else if (upgrade != 'websocket') {
      throw AngelHttpException.badRequest(
          message: 'The `upgrade` header must equal "websocket".');
    } else if (version != '13') {
      throw AngelHttpException.badRequest(
          message: 'The `sec-websocket-version` header must equal "13".');
    } else if (key == null) {
      throw AngelHttpException.badRequest(
          message: 'Missing `sec-websocket-key` header.');
    } else if (protocol != null &&
        allowedProtocols.isNotEmpty &&
        !allowedProtocols.contains(protocol)) {
      throw AngelHttpException.badRequest(
          message: 'Disallowed `sec-websocket-protocol` header "$protocol".');
    } else {
      var stream = res.detach();
      var ctrl = StreamChannelController<List<int>>();

      ctrl.local.stream.listen((buf) {
        stream.sendData(buf);
      }, onDone: () {
        stream.outgoingMessages.close();
      });

      if (req.hasParsedBody) {
        await ctrl.local.sink.close();
      } else {
        await req.body.pipe(ctrl.local.sink);
      }

      var sink = utf8.encoder.startChunkedConversion(ctrl.foreign.sink);
      sink.add('HTTP/1.1 101 Switching Protocols\r\n'
          'Upgrade: websocket\r\n'
          'Connection: Upgrade\r\n'
          'Sec-WebSocket-Accept: ${WebSocketChannel.signKey(key)}\r\n');
      if (protocol != null) sink.add('Sec-WebSocket-Protocol: $protocol\r\n');
      sink.add('\r\n');

      var ws = WebSocketChannel(ctrl.foreign);
      var socket = WebSocketContext(ws, req, res);
      scheduleMicrotask(() => handleClient(socket));
      return false;
    }
  } else {
    throw ArgumentError(
        'Not an HTTP/1.1 or HTTP/2 RequestContext+ResponseContext pair: $req, $res');
  }
}