Support redirect to relative Uri, as specified by rfc3986#4.2.
BUG=https://code.google.com/p/dart/issues/detail?id=10210
R=sgjesse@google.com
Review URL: https://codereview.chromium.org//14988003
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@22413 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart
index e3e4945..8ea7174 100644
--- a/sdk/lib/io/http_impl.dart
+++ b/sdk/lib/io/http_impl.dart
@@ -1412,6 +1412,29 @@
Future<HttpClientRequest> _openUrlFromRequest(String method,
Uri uri,
_HttpClientRequest previous) {
+ // If the new URI is relative (to either '/' or some sub-path),
+ // construct a full URI from the previous one.
+ // See http://tools.ietf.org/html/rfc3986#section-4.2
+ replaceComponents({scheme, domain, port, path}) {
+ uri = new Uri.fromComponents(
+ scheme: scheme != null ? scheme : uri.scheme,
+ domain: domain != null ? domain : uri.domain,
+ port: port != null ? port : uri.port,
+ path: path != null ? path : uri.path,
+ query: uri.query,
+ fragment: uri.fragment);
+ }
+ if (uri.domain == '') {
+ replaceComponents(domain: previous.uri.domain, port: previous.uri.port);
+ }
+ if (uri.scheme == '') {
+ replaceComponents(scheme: previous.uri.scheme);
+ }
+ if (!uri.path.startsWith('/') && previous.uri.path.startsWith('/')) {
+ var absolute = new Path.raw(previous.uri.path).directoryPath;
+ absolute = absolute.join(new Path.raw(uri.path));
+ replaceComponents(path: absolute.canonicalize().toString());
+ }
return openUrl(method, uri).then((_HttpClientRequest request) {
// Only follow redirects if initial request did.
request.followRedirects = previous.followRedirects;
diff --git a/tests/standalone/io/http_redirect_test.dart b/tests/standalone/io/http_redirect_test.dart
index 363f8d0..b3caef5 100644
--- a/tests/standalone/io/http_redirect_test.dart
+++ b/tests/standalone/io/http_redirect_test.dart
@@ -57,6 +57,69 @@
}
);
+ // Setup redirects with relative url.
+ addRequestHandler(
+ "/redirectUrl",
+ (HttpRequest request, HttpResponse response) {
+ response.headers.set(HttpHeaders.LOCATION, "/some/relativeUrl");
+ response.statusCode = HttpStatus.MOVED_PERMANENTLY;
+ response.close();
+ }
+ );
+
+ addRequestHandler(
+ "/some/redirectUrl",
+ (HttpRequest request, HttpResponse response) {
+ response.headers.set(HttpHeaders.LOCATION, "relativeUrl");
+ response.statusCode = HttpStatus.MOVED_PERMANENTLY;
+ response.close();
+ }
+ );
+
+ addRequestHandler(
+ "/some/relativeUrl",
+ (HttpRequest request, HttpResponse response) {
+ response.close();
+ }
+ );
+
+ addRequestHandler(
+ "/redirectUrl2",
+ (HttpRequest request, HttpResponse response) {
+ response.headers.set(HttpHeaders.LOCATION, "location");
+ response.statusCode = HttpStatus.MOVED_PERMANENTLY;
+ response.close();
+ }
+ );
+
+ addRequestHandler(
+ "/redirectUrl3",
+ (HttpRequest request, HttpResponse response) {
+ response.headers.set(HttpHeaders.LOCATION, "./location");
+ response.statusCode = HttpStatus.MOVED_PERMANENTLY;
+ response.close();
+ }
+ );
+
+ addRequestHandler(
+ "/redirectUrl4",
+ (HttpRequest request, HttpResponse response) {
+ response.headers.set(HttpHeaders.LOCATION, "./a/b/../../location");
+ response.statusCode = HttpStatus.MOVED_PERMANENTLY;
+ response.close();
+ }
+ );
+
+ addRequestHandler(
+ "/redirectUrl5",
+ (HttpRequest request, HttpResponse response) {
+ response.headers.set(HttpHeaders.LOCATION,
+ "//127.0.0.1:${server.port}/location");
+ response.statusCode = HttpStatus.MOVED_PERMANENTLY;
+ response.close();
+ }
+ );
+
// Setup redirect chain.
int n = 1;
addRedirectHandler(n++, HttpStatus.MOVED_PERMANENTLY);
@@ -370,6 +433,34 @@
});
}
+void testRedirectRelativeUrl() {
+ testPath(String path) {
+ setupServer().then((server) {
+ HttpClient client = new HttpClient();
+
+ print(path);
+ client.getUrl(Uri.parse("http://127.0.0.1:${server.port}$path"))
+ .then((request) => request.close())
+ .then((response) {
+ response.listen(
+ (_) {},
+ onDone: () {
+ Expect.equals(HttpStatus.OK, response.statusCode);
+ Expect.equals(1, response.redirects.length);
+ server.close();
+ client.close();
+ });
+ });
+ });
+ }
+ testPath("/redirectUrl");
+ testPath("/some/redirectUrl");
+ testPath("/redirectUrl2");
+ testPath("/redirectUrl3");
+ testPath("/redirectUrl4");
+ testPath("/redirectUrl5");
+}
+
main() {
testManualRedirect();
testManualRedirectWithHeaders();
@@ -380,4 +471,5 @@
testAutoRedirectLimit();
testRedirectLoop();
testRedirectClosingConnection();
+ testRedirectRelativeUrl();
}