diff --git a/modules/juce_core/native/java/JuceAppActivity.java b/modules/juce_core/native/java/JuceAppActivity.java index 061c979972..26b3d2ba08 100644 --- a/modules/juce_core/native/java/JuceAppActivity.java +++ b/modules/juce_core/native/java/JuceAppActivity.java @@ -899,13 +899,74 @@ public class JuceAppActivity extends Activity //============================================================================== public static class HTTPStream { - public HTTPStream (HttpURLConnection connection_, - int[] statusCode_, - StringBuffer responseHeaders_) + public HTTPStream (String address, boolean isPostToUse, byte[] postDataToUse, + String headersToUse, int timeOutMsToUse, + int[] statusCodeToUse, StringBuffer responseHeadersToUse, + int numRedirectsToFollowToUse, String httpRequestCmdToUse) throws IOException { - connection = connection_; - statusCode = statusCode_; - responseHeaders = responseHeaders_; + isPost = isPostToUse; + postData = postDataToUse; + headers = headersToUse; + timeOutMs = timeOutMsToUse; + statusCode = statusCodeToUse; + responseHeaders = responseHeadersToUse; + numRedirectsToFollow = numRedirectsToFollowToUse; + httpRequestCmd = httpRequestCmdToUse; + + connection = createConnection (address, isPost, postData, headers, timeOutMs, httpRequestCmd); + } + + private final HttpURLConnection createConnection (String address, boolean isPost, byte[] postData, + String headers, int timeOutMs, String httpRequestCmdToUse) throws IOException + { + HttpURLConnection newConnection = (HttpURLConnection) (new URL(address).openConnection()); + + try + { + newConnection.setInstanceFollowRedirects (false); + newConnection.setConnectTimeout (timeOutMs); + newConnection.setReadTimeout (timeOutMs); + + // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. + // So convert headers string to an array, with an element for each line + String headerLines[] = headers.split("\\n"); + + // Set request headers + for (int i = 0; i < headerLines.length; ++i) + { + int pos = headerLines[i].indexOf (":"); + + if (pos > 0 && pos < headerLines[i].length()) + { + String field = headerLines[i].substring (0, pos); + String value = headerLines[i].substring (pos + 1); + + if (value.length() > 0) + newConnection.setRequestProperty (field, value); + } + } + + newConnection.setRequestMethod (httpRequestCmd); + + if (isPost) + { + newConnection.setDoOutput (true); + + if (postData != null) + { + OutputStream out = newConnection.getOutputStream(); + out.write(postData); + out.flush(); + } + } + + return newConnection; + } + catch (Throwable e) + { + newConnection.disconnect(); + throw new IOException ("Connection error"); + } } private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException @@ -928,19 +989,12 @@ public class JuceAppActivity extends Activity try { - if (connection.getConnectTimeout() > 0) - return streamFuture.get (connection.getConnectTimeout(), TimeUnit.MILLISECONDS); - else - return streamFuture.get(); + return streamFuture.get(); } catch (InterruptedException e) { return null; } - catch (TimeoutException e) - { - return null; - } catch (CancellationException e) { return null; @@ -948,6 +1002,89 @@ public class JuceAppActivity extends Activity } public final boolean connect() + { + boolean result = false; + int numFollowedRedirects = 0; + + while (true) + { + result = doConnect(); + + if (! result) + return false; + + if (++numFollowedRedirects > numRedirectsToFollow) + break; + + int status = statusCode[0]; + + if (status == 301 || status == 302 || status == 303 || status == 307) + { + // Assumes only one occurrence of "Location" + int pos1 = responseHeaders.indexOf ("Location:") + 10; + int pos2 = responseHeaders.indexOf ("\n", pos1); + + if (pos2 > pos1) + { + String currentLocation = connection.getURL().toString(); + String newLocation = responseHeaders.substring (pos1, pos2); + + try + { + // Handle newLocation whether it's absolute or relative + URL baseUrl = new URL (currentLocation); + URL newUrl = new URL (baseUrl, newLocation); + String transformedNewLocation = newUrl.toString(); + + if (transformedNewLocation != currentLocation) + { + // Clear responseHeaders before next iteration + responseHeaders.delete (0, responseHeaders.length()); + + synchronized (createStreamLock) + { + if (hasBeenCancelled.get()) + return false; + + connection.disconnect(); + + try + { + connection = createConnection (transformedNewLocation, isPost, + postData, headers, timeOutMs, + httpRequestCmd); + } + catch (Throwable e) + { + return false; + } + } + } + else + { + break; + } + } + catch (Throwable e) + { + return false; + } + } + else + { + break; + } + } + else + { + break; + } + } + + return result; + } + + private final boolean doConnect() { synchronized (createStreamLock) { @@ -1094,9 +1231,15 @@ public class JuceAppActivity extends Activity public final boolean isExhausted() { return false; } public final boolean setPosition (long newPos) { return false; } + private boolean isPost; + private byte[] postData; + private String headers; + private int timeOutMs; + String httpRequestCmd; private HttpURLConnection connection; private int[] statusCode; private StringBuffer responseHeaders; + private int numRedirectsToFollow; private InputStream inputStream; private long position; private final ReentrantLock createStreamLock = new ReentrantLock(); @@ -1118,89 +1261,15 @@ public class JuceAppActivity extends Activity else if (timeOutMs == 0) timeOutMs = 30000; - // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. - // So convert headers string to an array, with an element for each line - String headerLines[] = headers.split("\\n"); - for (;;) { try { - HttpURLConnection connection = (HttpURLConnection) (new URL(address).openConnection()); + HTTPStream httpStream = new HTTPStream (address, isPost, postData, headers, + timeOutMs, statusCode, responseHeaders, + numRedirectsToFollow, httpRequestCmd); - if (connection != null) - { - try - { - connection.setInstanceFollowRedirects (false); - connection.setConnectTimeout (timeOutMs); - connection.setReadTimeout (timeOutMs); - - // Set request headers - for (int i = 0; i < headerLines.length; ++i) - { - int pos = headerLines[i].indexOf (":"); - - if (pos > 0 && pos < headerLines[i].length()) - { - String field = headerLines[i].substring (0, pos); - String value = headerLines[i].substring (pos + 1); - - if (value.length() > 0) - connection.setRequestProperty (field, value); - } - } - - connection.setRequestMethod (httpRequestCmd); - if (isPost) - { - connection.setDoOutput (true); - - if (postData != null) - { - OutputStream out = connection.getOutputStream(); - out.write(postData); - out.flush(); - } - } - - HTTPStream httpStream = new HTTPStream (connection, statusCode, responseHeaders); - - // Process redirect & continue as necessary - int status = statusCode[0]; - - if (--numRedirectsToFollow >= 0 - && (status == 301 || status == 302 || status == 303 || status == 307)) - { - // Assumes only one occurrence of "Location" - int pos1 = responseHeaders.indexOf ("Location:") + 10; - int pos2 = responseHeaders.indexOf ("\n", pos1); - - if (pos2 > pos1) - { - String newLocation = responseHeaders.substring(pos1, pos2); - // Handle newLocation whether it's absolute or relative - URL baseUrl = new URL (address); - URL newUrl = new URL (baseUrl, newLocation); - String transformedNewLocation = newUrl.toString(); - - if (transformedNewLocation != address) - { - address = transformedNewLocation; - // Clear responseHeaders before next iteration - responseHeaders.delete (0, responseHeaders.length()); - continue; - } - } - } - - return httpStream; - } - catch (Throwable e) - { - connection.disconnect(); - } - } + return httpStream; } catch (Throwable e) {} diff --git a/modules/juce_core/native/juce_android_Network.cpp b/modules/juce_core/native/juce_android_Network.cpp index 55e33f8b2f..4189766e4d 100644 --- a/modules/juce_core/native/juce_android_Network.cpp +++ b/modules/juce_core/native/juce_android_Network.cpp @@ -132,8 +132,8 @@ public: javaString (httpRequest).get())); } - if (stream != 0) - stream.callBooleanMethod (HTTPStream.connect); + if (stream != 0 && ! stream.callBooleanMethod (HTTPStream.connect)) + stream.clear(); jint* const statusCodeElements = env->GetIntArrayElements (statusCodeArray, 0); statusCode = statusCodeElements[0];