httpRequest function
Future<HttpResponse>
httpRequest(
- Uri uri,
- String method,
- SocketInterface socket, {
- Map<
String, String> requestHeaders, - Uint8List body,
- StringCallback debugPrint,
- bool persistentConnection = true,
Makes HTTP request over SocketInterface, e.g. SSHTunneledSocketImpl.
Implementation
Future<HttpResponse> httpRequest(Uri uri, String method, SocketInterface socket,
{Map<String, String> requestHeaders,
Uint8List body,
StringCallback debugPrint,
bool persistentConnection = true}) async {
/// Initialize connection state.
String headerText;
List<String> statusLine;
Map<String, String> headers;
int contentLength = 0, contentRead = 0;
QueueBuffer buffer = QueueBuffer(Uint8List(0));
Completer<String> readHeadersCompleter = Completer<String>();
StreamController<List<int>> contentController = StreamController<List<int>>();
if (!socket.connected && !socket.connecting) {
socket = await connectUri(uri, socket);
}
socket.handleDone((String reason) {
if (debugPrint != null) {
debugPrint('SSHTunneledBaseClient.socket.handleDone');
}
socket.close();
contentController.close();
if (headerText == null) readHeadersCompleter.complete('done');
});
socket.handleError((error) {
if (debugPrint != null) {
debugPrint('SSHTunneledBaseClient.socket.handleError');
}
socket.close();
contentController.close();
if (headerText == null) readHeadersCompleter.complete('$error');
});
socket.listen((Uint8List m) {
if (debugPrint != null) {
debugPrint('SSHTunneledBaseClient.socket.listen: read ${m.length} bytes');
}
if (headerText == null) {
buffer.add(m);
int headersEnd = searchUint8List(
buffer.data, Uint8List.fromList('\r\n\r\n'.codeUnits));
/// Parse HTTP headers.
if (headersEnd != -1) {
headerText = utf8.decode(viewUint8List(buffer.data, 0, headersEnd));
buffer.flush(headersEnd + 4);
var lines = LineSplitter.split(headerText);
statusLine = lines.first.split(' ');
headers = Map<String, String>.fromIterable(lines.skip(1),
key: (h) => h.substring(0, h.indexOf(': ')),
value: (h) => h.substring(h.indexOf(': ') + 2).trim());
headers.forEach((key, value) {
if (key.toLowerCase() == 'content-length') {
contentLength = int.parse(value);
}
});
readHeadersCompleter.complete(null);
/// If there's no content then we're already done.
if (contentLength == 0) {
if (debugPrint != null) {
debugPrint(
'SSHTunneledBaseClient.socket.listen: Content-Length: 0, remaining=${buffer.data.length}');
}
contentController.close();
if (!persistentConnection) {
socket.close();
}
return;
}
/// Handle any remaining data in the read buffer.
if (buffer.data.isEmpty) return;
m = buffer.data;
}
}
/// Add content to the stream until completed.
contentController.add(m);
contentRead += m.length;
if (contentRead >= contentLength) {
if (debugPrint != null) {
debugPrint(
'SSHTunneledBaseClient.socket.listen: done $contentRead / $contentLength');
}
contentController.close();
if (!persistentConnection || contentRead > contentLength) {
socket.close();
}
}
});
requestHeaders['Host'] = '${uri.host}';
if (method == 'POST') {
requestHeaders['Content-Length'] = '${body.length}';
}
socket.send('${method} /${uri.path} HTTP/1.1\r\n' +
requestHeaders.entries
.map((header) => '${header.key}: ${header.value}')
.join('\r\n') +
'\r\n\r\n');
if (method == 'POST') socket.sendRaw(body);
String readHeadersError = await readHeadersCompleter.future;
if (readHeadersError != null) throw FormatException(readHeadersError);
return HttpResponse(int.parse(statusLine[1]),
reason: statusLine.sublist(2).join(' '),
headers: headers,
contentLength: contentLength,
contentStream: contentController.stream);
}