Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(701)

Unified Diff: sdk/lib/io/http_impl.dart

Issue 11783034: Re-implement support for client redirects. (Closed) Base URL: https://dart.googlecode.com/svn/experimental/lib_v2_io/dart
Patch Set: Review fixes Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « sdk/lib/io/http.dart ('k') | sdk/lib/io/http_parser.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/io/http_impl.dart
diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart
index add73898c7b8b8869724672e32d27512cb859e94..1479a9354f9f3cb79e090a75e89bd0ecf332ccd2 100644
--- a/sdk/lib/io/http_impl.dart
+++ b/sdk/lib/io/http_impl.dart
@@ -11,8 +11,7 @@ class _HttpIncomingConnection extends StreamController<List<int>> {
final SignalCompleter _messageCompleter = new SignalCompleter();
// Common properties.
- _HttpHeaders headers;
- int contentLength;
+ final _HttpHeaders headers;
bool upgraded = false;
// ClientResponse properties.
@@ -23,7 +22,8 @@ class _HttpIncomingConnection extends StreamController<List<int>> {
String method;
Uri uri;
- _HttpIncomingConnection(void this._pause(),
+ _HttpIncomingConnection(_HttpHeaders this.headers,
+ void this._pause(),
void this._resume())
: super.singleSubscription() {
_pause();
@@ -39,7 +39,7 @@ class _HttpIncomingConnection extends StreamController<List<int>> {
if (isPaused) {
_pause();
} else {
- _reasume();
+ _resume();
}
}
@@ -116,12 +116,81 @@ class _HttpRequest extends _HttpIncoming implements HttpRequest {
class _HttpClientResponse extends _HttpIncoming implements HttpClientResponse {
- _HttpClientResponse(_HttpIncomingConnection _incomingConnection)
+ List<RedirectInfo> get redirects => _httpRequest._responseRedirects;
+
+ // The HttpClient this response belongs to.
+ final _HttpClient _httpClient;
+
+ // The HttpClientRequest of this response.
+ final _HttpClientRequest _httpRequest;
+
+ _HttpClientResponse(_HttpIncomingConnection _incomingConnection,
+ _HttpClientRequest this._httpRequest,
+ _HttpClient this._httpClient)
: super(_incomingConnection);
int get statusCode => _incomingConnection.statusCode;
String get reasonPhrase => _incomingConnection.reasonPhrase;
+ bool get isRedirect {
+ if (_httpRequest.method == "GET" || _httpRequest.method == "HEAD") {
+ return statusCode == HttpStatus.MOVED_PERMANENTLY ||
+ statusCode == HttpStatus.FOUND ||
+ statusCode == HttpStatus.SEE_OTHER ||
+ statusCode == HttpStatus.TEMPORARY_REDIRECT;
+ } else if (_httpRequest.method == "POST") {
+ return statusCode == HttpStatus.SEE_OTHER;
+ }
+ return false;
+ }
+
+ Future<HttpClientResponse> redirect([String method,
+ Uri url,
+ bool followLoops]) {
+ if (method == null) {
+ // Set method as defined by RFC 2616 section 10.3.4.
+ if (statusCode == HttpStatus.SEE_OTHER && _httpRequest.method == "POST") {
+ method = "GET";
+ } else {
+ method = _httpRequest.method;
+ }
+ }
+ if (url == null) {
+ String location = headers.value(HttpHeaders.LOCATION);
+ if (location == null) {
+ throw new StateError("Response has no Location header for redirect");
+ }
+ url = new Uri.fromString(location);
+ }
+ if (followLoops != true) {
+ for (var redirect in redirects) {
+ if (redirect.location == url) {
+ return new Future.immediateError(
+ new RedirectLoopException(redirects));
+ }
+ }
+ }
+ return _httpClient.openUrl(method, url)
+ .then((request) {
+ // Only follow redirects if initial request did.
+ request.followRedirects = _httpRequest.followRedirects;
+ // Allow the same number of redirects.
+ request.maxRedirects = _httpRequest.maxRedirects;
+ // Copy headers.
+ for (var header in _httpRequest.headers._headers.keys) {
+ if (header != HttpHeaders.HOST.toLowerCase()) {
+ request.headers.set(header, _httpRequest.headers[header]);
+ }
+ }
+ request.headers.contentLength = 0;
+ request._responseRedirects.addAll(this.redirects);
+ request._responseRedirects.add(new _RedirectInfo(statusCode,
+ method,
+ url));
+ return request.close();
+ });
+ }
+
StreamSubscription<List<int>> listen(void onData(List<int> event),
{void onError(AsyncError error),
void onDone(),
@@ -230,9 +299,28 @@ class _HttpResponse extends _HttpOutgoing<HttpResponse>
class _HttpClientRequest extends _HttpOutgoing<HttpClientRequest>
implements HttpClientRequest {
- _HttpClientRequest(Uri this.uri,
+ final String method;
+ final Uri uri;
+ final List<Cookie> cookies = new List<Cookie>();
+
+ // The HttpClient this request belongs to.
+ final _HttpClient _httpClient;
+
+ final Completer<HttpClientResponse> _responseCompleter
+ = new Completer<HttpClientResponse>();
+
+
+ // TODO(ajohnsen): Get default value from client?
+ bool _followRedirects = true;
+
+ int _maxRedirects = 5;
+
+ List<RedirectInfo> _responseRedirects = [];
+
+ _HttpClientRequest(_HttpOutgoingConnection outgoing,
+ Uri this.uri,
String this.method,
- _HttpOutgoingConnection outgoing)
+ _HttpClient this._httpClient)
: super("1.1", outgoing) {
// GET and HEAD have 'content-length: 0' by default.
if (method == "GET" || method == "HEAD") {
@@ -240,14 +328,52 @@ class _HttpClientRequest extends _HttpOutgoing<HttpClientRequest>
}
}
+ Future<HttpClientResponse> get response => _responseCompleter.future;
+
Future<HttpClientResponse> close() {
super.close();
return response;
}
+ int get maxRedirects => _maxRedirects;
+ void set maxRedirects(int maxRedirects) {
+ if (_headersWritten) throw new StateError("Request already sent");
+ _maxRedirects = maxRedirects;
+ }
+
+ bool get followRedirects => _followRedirects;
+ void set followRedirects(bool followRedirects) {
+ if (_headersWritten) throw new StateError("Request already sent");
+ _followRedirects = followRedirects;
+ }
+
void _onIncoming(_HttpIncomingConnection incoming) {
- // TODO(ajohnsen): Handle redirect and auth.
- _responseCompleter.complete(new _HttpClientResponse(incoming));
+ // TODO(ajohnsen): Handle auth.
+ var response = new _HttpClientResponse(incoming,
+ this,
+ _httpClient);
+
+ Future<HttpClientResponse> future;
+
+ if (followRedirects &&
+ response.isRedirect) {
+ if (response.redirects.length < maxRedirects) {
+ // Redirect
+ future = response.redirect();
+ } else {
+ // End with exception, too many redirects.
+ future = new Future.immediateError(
+ new RedirectLimitExceededException(response.redirects));
+ }
+ } else {
+ future = new Future<HttpClientResponse>.immediate(response);
+ }
+
+ future.then(
+ _responseCompleter.complete,
+ onError: (e) {
+ _responseCompleter.completeError(e.error, e.stackTrace);
+ });
}
void _onError(AsyncError error) {
@@ -298,14 +424,6 @@ class _HttpClientRequest extends _HttpOutgoing<HttpClientRequest>
headers._write(this);
writeCRLF();
}
-
- Future<HttpClientResponse> get response => _responseCompleter.future;
-
- final String method;
- final Uri uri;
- final List<Cookie> cookies = new List<Cookie>();
- final Completer<HttpClientResponse> _responseCompleter
- = new Completer<HttpClientResponse>();
}
@@ -460,7 +578,7 @@ class _HttpClient implements HttpClient {
String path) {
// TODO(sgjesse): The path set here can contain both query and
// fragment. They should be cracked and set correctly.
- return _open(method, new Uri.fromComponents(
+ return _openUrl(method, new Uri.fromComponents(
scheme: "http", domain: host, port: port, path: path));
}
@@ -475,7 +593,7 @@ class _HttpClient implements HttpClient {
}
Future<HttpClientRequest> getUrl(Uri url) {
- return _open("get", url);
+ return _openUrl("get", url);
}
Future<HttpClientRequest> post(String host,
@@ -485,7 +603,7 @@ class _HttpClient implements HttpClient {
}
Future<HttpClientRequest> postUrl(Uri url) {
- return _open("post", url);
+ return _openUrl("post", url);
}
void close() {
@@ -501,9 +619,9 @@ class _HttpClient implements HttpClient {
_activeConnections.clear();
}
- Future<HttpClientRequest> _open(String method,
- Uri uri,
- [_HttpClientConnection connection]) {
+ Future<HttpClientRequest> _openUrl(String method,
+ Uri uri,
+ [_HttpClientConnection connection]) {
if (method == null) {
throw new ArgumentError(method);
}
@@ -514,9 +632,9 @@ class _HttpClient implements HttpClient {
// TODO(ajohnsen): Proxy?
Future future;
+ int port = uri.port;
+ if (port == 0) port = HttpClient.DEFAULT_HTTP_PORT;
if (connection == null) {
- int port = uri.port;
- if (port == 0) port = HttpClient.DEFAULT_HTTP_PORT;
future = _getConnection(uri.domain, port);
} else {
future = new Future.immediate(connection);
@@ -530,8 +648,12 @@ class _HttpClient implements HttpClient {
// Create new internal outgoing connection.
var outgoing = new _HttpOutgoingConnection();
// Create new request object, wrapping the outgoing connection.
- var request = new _HttpClientRequest(
- uri, method.toUpperCase(), outgoing);
+ var request = new _HttpClientRequest(outgoing,
+ uri,
+ method.toUpperCase(),
+ this);
+ request.headers.host = uri.domain;
+ request.headers.port = port;
// Start sending the request (lazy, delayed until the user provides
// data).
connection.sendRequest(outgoing, onDone)
« no previous file with comments | « sdk/lib/io/http.dart ('k') | sdk/lib/io/http_parser.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698