parseResponse static method

bool parseResponse(
  1. String resp,
  2. String key
)

Implementation

static bool parseResponse(String resp, String key) {
  response += resp;
  final bodyOffset = response.indexOf('\n\n');
  // if we don't have a double newline yet we need to go back for more.
  if (bodyOffset < 0) {
    return true;
  }
  final lines = '${response.characters.getRange(0, bodyOffset)}'.split('\n');
  if (lines.isEmpty) {
    throw MqttNoConnectionException(
      'MqttServerWs2Connection::server returned invalid response',
    );
  }
  // split apart the status line
  final status = lines.first.split(' ');
  if (status.length < MqttServerWs2Connection.statusLines) {
    throw MqttNoConnectionException(
      'MqttServerWs2Connection::server returned malformed status line',
    );
  }
  // make a map of the headers
  final headers = <String, String>{};
  lines.removeAt(0);
  for (final l in lines) {
    final space = l.indexOf(' ');
    if (space < 0) {
      throw MqttNoConnectionException(
        'MqttServerWs2Connection::server returned malformed header line',
      );
    }
    headers['${l.characters.getRange(0, space - 1)}'.toLowerCase()] =
        '${l.characters.getRange(space + 1)}';
  }
  var body = '';
  // if we have a Content-Length key we can't stop till we read the body.
  if (headers.containsKey('content-length')) {
    final bodyLength = int.parse(headers['content-length']!);
    if (response.length <
        bodyOffset + bodyLength + MqttServerWs2Connection.bodyOffset) {
      return true;
    }
    body =
        '${response.characters.getRange(bodyOffset, bodyOffset + bodyLength + MqttServerWs2Connection.bodyOffset)}';
  }
  // if we make it to here we have read all we are going to read.
  // now lets see if we like what we found.
  if (status[1] != '101') {
    throw MqttNoConnectionException(
      'MqttServerWs2Connection::server refused to upgrade, response = '
      '${status[1]} - ${status[2]} - $body',
    );
  }

  if (!headers.containsKey('connection') ||
      headers['connection']!.toLowerCase() != 'upgrade') {
    throw MqttNoConnectionException(
      'MqttServerWs2Connection::server returned improper connection header line',
    );
  }
  if (!headers.containsKey('upgrade') ||
      headers['upgrade']!.toLowerCase() != 'websocket') {
    throw MqttNoConnectionException(
      'MqttServerWs2Connection::server returned improper upgrade header line',
    );
  }
  if (!headers.containsKey('sec-websocket-protocol')) {
    throw MqttNoConnectionException(
      'MqttServerWs2Connection::server failed to return protocol header',
    );
  }
  if (!headers.containsKey('sec-websocket-accept')) {
    throw MqttNoConnectionException(
      'MqttServerWs2Connection::server failed to return accept header',
    );
  }
  // We build up the accept in the same way the server should
  // then we check that the response is the same.

  // Do not change: https://tools.ietf.org/html/rfc6455#section-1.3
  const acceptSalt = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';

  final sha1Bytes = sha1.convert(utf8.encode(key + acceptSalt));
  final encodedSha1Bytes = base64.encode(sha1Bytes.bytes);
  if (encodedSha1Bytes != headers['sec-websocket-accept']) {
    throw MqttNoConnectionException(
      'MqttServerWs2Connection::handshake mismatch',
    );
  }
  return false;
}