@@ -27,6 +27,9 @@ Fixed bugs: | |||||
central repository didn't contain any source files. | central repository didn't contain any source files. | ||||
Bugzilla Report 65110 | Bugzilla Report 65110 | ||||
* The <http> condition didn't follow redirects from http to https. | |||||
Bugzilla Report 65105 | |||||
Other changes: | Other changes: | ||||
-------------- | -------------- | ||||
@@ -533,6 +533,18 @@ public class Get extends Task { | |||||
extends org.apache.tools.ant.util.Base64Converter { | extends org.apache.tools.ant.util.Base64Converter { | ||||
} | } | ||||
/** | |||||
* Does the response code represent a redirection? | |||||
* | |||||
* @since 1.10.10 | |||||
*/ | |||||
public static boolean isMoved(final int responseCode) { | |||||
return responseCode == HttpURLConnection.HTTP_MOVED_PERM | |||||
|| responseCode == HttpURLConnection.HTTP_MOVED_TEMP | |||||
|| responseCode == HttpURLConnection.HTTP_SEE_OTHER | |||||
|| responseCode == HTTP_MOVED_TEMP; | |||||
} | |||||
/** | /** | ||||
* Interface implemented for reporting | * Interface implemented for reporting | ||||
* progress of downloading. | * progress of downloading. | ||||
@@ -815,13 +827,6 @@ public class Get extends Task { | |||||
return connection; | return connection; | ||||
} | } | ||||
private boolean isMoved(final int responseCode) { | |||||
return responseCode == HttpURLConnection.HTTP_MOVED_PERM | |||||
|| responseCode == HttpURLConnection.HTTP_MOVED_TEMP | |||||
|| responseCode == HttpURLConnection.HTTP_SEE_OTHER | |||||
|| responseCode == HTTP_MOVED_TEMP; | |||||
} | |||||
private boolean downloadFile() throws IOException { | private boolean downloadFile() throws IOException { | ||||
for (int i = 0; i < numberRetries; i++) { | for (int i = 0; i < numberRetries; i++) { | ||||
// this three attempt trick is to get round quirks in different | // this three attempt trick is to get round quirks in different | ||||
@@ -30,6 +30,7 @@ import java.util.Locale; | |||||
import org.apache.tools.ant.BuildException; | import org.apache.tools.ant.BuildException; | ||||
import org.apache.tools.ant.Project; | import org.apache.tools.ant.Project; | ||||
import org.apache.tools.ant.ProjectComponent; | import org.apache.tools.ant.ProjectComponent; | ||||
import org.apache.tools.ant.taskdefs.Get; | |||||
/** | /** | ||||
* Condition to wait for a HTTP request to succeed. Its attribute(s) are: | * Condition to wait for a HTTP request to succeed. Its attribute(s) are: | ||||
@@ -42,6 +43,8 @@ import org.apache.tools.ant.ProjectComponent; | |||||
public class Http extends ProjectComponent implements Condition { | public class Http extends ProjectComponent implements Condition { | ||||
private static final int ERROR_BEGINS = 400; | private static final int ERROR_BEGINS = 400; | ||||
private static final String DEFAULT_REQUEST_METHOD = "GET"; | private static final String DEFAULT_REQUEST_METHOD = "GET"; | ||||
private static final String HTTP = "http"; | |||||
private static final String HTTPS = "https"; | |||||
private String spec = null; | private String spec = null; | ||||
private String requestMethod = DEFAULT_REQUEST_METHOD; | private String requestMethod = DEFAULT_REQUEST_METHOD; | ||||
@@ -124,11 +127,7 @@ public class Http extends ProjectComponent implements Condition { | |||||
try { | try { | ||||
URLConnection conn = url.openConnection(); | URLConnection conn = url.openConnection(); | ||||
if (conn instanceof HttpURLConnection) { | if (conn instanceof HttpURLConnection) { | ||||
HttpURLConnection http = (HttpURLConnection) conn; | |||||
http.setRequestMethod(requestMethod); | |||||
http.setInstanceFollowRedirects(followRedirects); | |||||
http.setReadTimeout(readTimeout); | |||||
int code = http.getResponseCode(); | |||||
int code = request((HttpURLConnection) conn, url); | |||||
log("Result code for " + spec + " was " + code, | log("Result code for " + spec + " was " + code, | ||||
Project.MSG_VERBOSE); | Project.MSG_VERBOSE); | ||||
return code > 0 && code < errorsBeginAt; | return code > 0 && code < errorsBeginAt; | ||||
@@ -144,4 +143,39 @@ public class Http extends ProjectComponent implements Condition { | |||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
private int request(final HttpURLConnection http, final URL url) throws IOException { | |||||
http.setRequestMethod(requestMethod); | |||||
http.setInstanceFollowRedirects(followRedirects); | |||||
http.setReadTimeout(readTimeout); | |||||
final int firstStatusCode = http.getResponseCode(); | |||||
if (Get.isMoved(firstStatusCode)) { | |||||
final String newLocation = http.getHeaderField("Location"); | |||||
final URL newURL = new URL(newLocation); | |||||
if (redirectionAllowed(url, newURL)) { | |||||
final URLConnection newConn = newURL.openConnection(); | |||||
if (newConn instanceof HttpURLConnection) { | |||||
log("Following redirect from " + url + " to " + newURL); | |||||
return request((HttpURLConnection) newConn, newURL); | |||||
} | |||||
} | |||||
} | |||||
return firstStatusCode; | |||||
} | |||||
private boolean redirectionAllowed(final URL from, final URL to) { | |||||
if (from.equals(to)) { | |||||
// most simple case of an infinite redirect loop | |||||
return false; | |||||
} | |||||
if (!(from.getProtocol().equals(to.getProtocol()) | |||||
|| (HTTP.equals(from.getProtocol()) | |||||
&& HTTPS.equals(to.getProtocol())))) { | |||||
log("Redirection detected from " | |||||
+ from.getProtocol() + " to " + to.getProtocol() | |||||
+ ". Protocol switch unsafe, not allowed."); | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
} | } |
@@ -0,0 +1,71 @@ | |||||
<?xml version="1.0"?> | |||||
<!-- | |||||
Licensed to the Apache Software Foundation (ASF) under one or more | |||||
contributor license agreements. See the NOTICE file distributed with | |||||
this work for additional information regarding copyright ownership. | |||||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
(the "License"); you may not use this file except in compliance with | |||||
the License. You may obtain a copy of the License at | |||||
https://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
--> | |||||
<project name="http-test" default="antunit" xmlns:au="antlib:org.apache.ant.antunit"> | |||||
<import file="../../antunit-base.xml" /> | |||||
<property name="location" value="https://ant.apache.org/webtest/gettest" /> | |||||
<property name="unsecurelocation" value="http://ant.apache.org/webtest/gettest/http-to-https.txt" /> | |||||
<target name="testSeeOtherRedirect"> | |||||
<sleep milliseconds="250"/> | |||||
<au:assertTrue> | |||||
<http url="${location}/other.txt"/> | |||||
</au:assertTrue> | |||||
<au:assertLogContains level="verbose" | |||||
text="Result code for ${location}/other.txt was 200" /> | |||||
</target> | |||||
<target name="testPermanentRedirect"> | |||||
<sleep milliseconds="250"/> | |||||
<au:assertTrue> | |||||
<http url="${location}/permanent.txt"/> | |||||
</au:assertTrue> | |||||
<au:assertLogContains level="verbose" | |||||
text="Result code for ${location}/permanent.txt was 200" /> | |||||
</target> | |||||
<target name="testTemporaryRedirect"> | |||||
<sleep milliseconds="250"/> | |||||
<au:assertTrue> | |||||
<http url="${location}/temp.txt"/> | |||||
</au:assertTrue> | |||||
<au:assertLogContains level="verbose" | |||||
text="Result code for ${location}/temp.txt was 200" /> | |||||
</target> | |||||
<target name="testStatusCode307Redirect"> | |||||
<sleep milliseconds="250"/> | |||||
<au:assertTrue> | |||||
<http url="${location}/307.txt"/> | |||||
</au:assertTrue> | |||||
<au:assertLogContains level="verbose" | |||||
text="Result code for ${location}/307.txt was 200" /> | |||||
</target> | |||||
<target name="testHttpToHttpsRedirect" description="Tests that a resource that's redirected | |||||
from HTTP to HTTPS works without an error. See bugzilla-65105 for details"> | |||||
<sleep milliseconds="250"/> | |||||
<au:assertTrue> | |||||
<http url="${unsecurelocation}"/> | |||||
</au:assertTrue> | |||||
<au:assertLogContains level="verbose" | |||||
text="Result code for ${unsecurelocation} was 200" /> | |||||
</target> | |||||
</project> |