from static method

Creates an instance of SSHHttpClientResponse that contains the response sent by the HTTP server over socket.

Implementation

static Future<SSHHttpClientResponse> from(SSHSocket socket) async {
  int? statusCode;
  String? reasonPhrase;
  final body = StringBuffer();
  final headers = <String, List<String>>{};

  var inHeader = false;
  var inBody = false;
  var contentLength = 0;
  var contentRead = 0;

  void processLine(String line, int bytesRead, LineDecoder decoder) {
    if (inBody) {
      body.write(line);
      contentRead += bytesRead;
    } else if (inHeader) {
      if (line.trim().isEmpty) {
        inBody = true;
        if (contentLength > 0) {
          decoder.expectedByteCount = contentLength;
        }
        return;
      }
      final separator = line.indexOf(':');
      final name = line.substring(0, separator).toLowerCase().trim();
      final value = line.substring(separator + 1).trim();
      if (name == SSHHttpHeaders.transferEncodingHeader &&
          value.toLowerCase() != 'identity') {
        throw UnsupportedError('only identity transfer encoding is accepted');
      }
      if (name == SSHHttpHeaders.contentLengthHeader) {
        contentLength = int.parse(value);
      }
      if (!headers.containsKey(name)) {
        headers[name] = [];
      }
      headers[name]!.add(value);
    } else if (line.startsWith('HTTP/1.1') || line.startsWith('HTTP/1.0')) {
      statusCode = int.parse(
        line.substring('HTTP/1.x '.length, 'HTTP/1.x xxx'.length),
      );
      reasonPhrase = line.substring('HTTP/1.x xxx '.length);
      inHeader = true;
    } else {
      throw UnsupportedError('unsupported http response format');
    }
  }

  final lineDecoder = LineDecoder.withCallback(processLine);

  await for (final chunk in socket.stream) {
    if (!inHeader ||
        !inBody ||
        ((contentRead + lineDecoder.bufferedBytes) < contentLength)) {
      lineDecoder.add(chunk);
      continue;
    }
    break;
  }

  try {
    lineDecoder.close();
  } finally {
    socket.close();
  }

  // try {
  //   while (!inHeader ||
  //       !inBody ||
  //       ((contentRead + lineDecoder.bufferedBytes) < contentLength)) {
  //     final bytes = socket.readSync(1024);

  //     if (bytes == null || bytes.isEmpty) {
  //       break;
  //     }
  //     lineDecoder.add(bytes);
  //   }
  // } finally {
  //   try {
  //     lineDecoder.close();
  //   } finally {
  //     socket.closeSync();
  //   }
  // }

  return SSHHttpClientResponse._(
    headers,
    reasonPhrase: reasonPhrase,
    statusCode: statusCode,
    body: body.toString(),
  );
}