blob: 92b1b20cf340184438a94e84a77472ea48c0b953 [file] [log] [blame] [edit]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'package:async/async.dart';
import 'base_client.dart';
import 'exception.dart';
import 'request.dart';
import 'response.dart';
/// A `dart:io`-based HTTP client.
///
/// This is the default client when running on the command line.
///
/// You can control the underlying `dart:io` [HttpRequest] by adding values to
/// [Request.context]:
///
/// * `"http.io.follow_redirects"` is a boolean. If it's `true` (the default)
/// then the request will automatically follow HTTP redirects. If it's
/// `false`, the client will need to handle redirects manually. See also
/// [HttpClientRequest.followRedirects].
///
/// * `"http.io.max_redirects"` is an integer that specifies the maximum number
/// of redirects that will be followed if `follow_redirects` is `true`. If the
/// site redirects more than this, [send] will throw a [ClientException]. It
/// defaults to `5`. See also [HttpClientRequest.maxRedirects].
///
/// * `"http.io.persistent_connection"` is a boolean. If it's `true` (the
/// default) the client will request that the TCP connection be kept alive
/// after the request completes. See also
/// [HttpClientRequest.persistentConnection].
class IOClient extends BaseClient {
/// The underlying `dart:io` HTTP client.
HttpClient _inner;
/// Creates a new HTTP client.
IOClient([HttpClient inner]) : _inner = inner ?? new HttpClient();
Future<Response> send(Request request) async {
try {
var ioRequest = await _inner.openUrl(request.method, request.url);
var context = request.context;
ioRequest
..followRedirects = context['http.io.follow_redirects'] ?? true
..maxRedirects = context['http.io.max_redirects'] ?? 5
..persistentConnection =
context['http.io.persistent_connection'] ?? true;
request.headers.forEach((name, value) {
ioRequest.headers.set(name, value);
});
request.read().pipe(DelegatingStreamConsumer.typed<List<int>>(ioRequest));
var response = await ioRequest.done;
var headers = <String, String>{};
response.headers.forEach((key, values) {
headers[key] = values.join(',');
});
return new Response(_responseUrl(request, response), response.statusCode,
reasonPhrase: response.reasonPhrase,
body: DelegatingStream.typed<List<int>>(response).handleError(
(error) => throw new ClientException(error.message, error.uri),
test: (error) => error is HttpException),
headers: headers);
} on HttpException catch (error) {
throw new ClientException(error.message, error.uri);
} on SocketException catch (error) {
throw new ClientException(error.message, request.url);
}
}
void close() {
if (_inner != null) _inner.close(force: true);
_inner = null;
}
/// Determines the finalUrl retrieved by evaluating any redirects received in
/// the [response] based on the initial [request].
Uri _responseUrl(Request request, HttpClientResponse response) {
var finalUrl = request.url;
for (var redirect in response.redirects) {
var location = redirect.location;
// Redirects can either be absolute or relative
finalUrl = location.isAbsolute ? location : finalUrl.resolveUri(location);
}
return finalUrl;
}
}