| @@ -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> | |||||