| @@ -666,38 +666,103 @@ public final class JuceDemo extends Activity | |||
| public static final HTTPStream createHTTPStream (String address, | |||
| boolean isPost, byte[] postData, String headers, | |||
| int timeOutMs, int[] statusCode, | |||
| StringBuffer responseHeaders) | |||
| StringBuffer responseHeaders, | |||
| int numRedirectsToFollow) | |||
| { | |||
| try | |||
| // timeout parameter of zero for HttpUrlConnection is a blocking connect (negative value for juce::URL) | |||
| if (timeOutMs < 0) | |||
| timeOutMs = 0; | |||
| 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 (;;) | |||
| { | |||
| HttpURLConnection connection = (HttpURLConnection) (new URL(address) | |||
| .openConnection()); | |||
| if (connection != null) | |||
| try | |||
| { | |||
| try | |||
| HttpURLConnection connection = (HttpURLConnection) (new URL(address).openConnection()); | |||
| if (connection != null) | |||
| { | |||
| if (isPost) | |||
| try | |||
| { | |||
| connection.setRequestMethod("POST"); | |||
| connection.setConnectTimeout(timeOutMs); | |||
| connection.setDoOutput(true); | |||
| connection.setChunkedStreamingMode(0); | |||
| OutputStream out = connection.getOutputStream(); | |||
| out.write(postData); | |||
| out.flush(); | |||
| } | |||
| connection.setInstanceFollowRedirects (false); | |||
| connection.setConnectTimeout (timeOutMs); | |||
| connection.setReadTimeout (timeOutMs); | |||
| return new HTTPStream (connection, statusCode, responseHeaders); | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| connection.disconnect(); | |||
| // 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); | |||
| } | |||
| } | |||
| if (isPost) | |||
| { | |||
| connection.setRequestMethod ("POST"); | |||
| 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(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| catch (Throwable e) {} | |||
| catch (Throwable e) {} | |||
| return null; | |||
| return null; | |||
| } | |||
| } | |||
| public final void launchURL (String url) | |||
| @@ -53,6 +53,10 @@ extern bool juceDemoRepaintDebuggingActive; | |||
| //#define JUCE_WASAPI | |||
| #endif | |||
| #ifndef JUCE_WASAPI_EXCLUSIVE | |||
| //#define JUCE_WASAPI_EXCLUSIVE | |||
| #endif | |||
| #ifndef JUCE_DIRECTSOUND | |||
| //#define JUCE_DIRECTSOUND | |||
| #endif | |||
| @@ -666,38 +666,103 @@ public final class JuceAppActivity extends Activity | |||
| public static final HTTPStream createHTTPStream (String address, | |||
| boolean isPost, byte[] postData, String headers, | |||
| int timeOutMs, int[] statusCode, | |||
| StringBuffer responseHeaders) | |||
| StringBuffer responseHeaders, | |||
| int numRedirectsToFollow) | |||
| { | |||
| try | |||
| // timeout parameter of zero for HttpUrlConnection is a blocking connect (negative value for juce::URL) | |||
| if (timeOutMs < 0) | |||
| timeOutMs = 0; | |||
| 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 (;;) | |||
| { | |||
| HttpURLConnection connection = (HttpURLConnection) (new URL(address) | |||
| .openConnection()); | |||
| if (connection != null) | |||
| try | |||
| { | |||
| try | |||
| HttpURLConnection connection = (HttpURLConnection) (new URL(address).openConnection()); | |||
| if (connection != null) | |||
| { | |||
| if (isPost) | |||
| try | |||
| { | |||
| connection.setRequestMethod("POST"); | |||
| connection.setConnectTimeout(timeOutMs); | |||
| connection.setDoOutput(true); | |||
| connection.setChunkedStreamingMode(0); | |||
| OutputStream out = connection.getOutputStream(); | |||
| out.write(postData); | |||
| out.flush(); | |||
| } | |||
| connection.setInstanceFollowRedirects (false); | |||
| connection.setConnectTimeout (timeOutMs); | |||
| connection.setReadTimeout (timeOutMs); | |||
| return new HTTPStream (connection, statusCode, responseHeaders); | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| connection.disconnect(); | |||
| // 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); | |||
| } | |||
| } | |||
| if (isPost) | |||
| { | |||
| connection.setRequestMethod ("POST"); | |||
| 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(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| catch (Throwable e) {} | |||
| catch (Throwable e) {} | |||
| return null; | |||
| return null; | |||
| } | |||
| } | |||
| public final void launchURL (String url) | |||
| @@ -378,7 +378,7 @@ struct AndroidThreadScope | |||
| METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \ | |||
| METHOD (excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \ | |||
| METHOD (renderGlyph, "renderGlyph", "(CLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \ | |||
| STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \ | |||
| STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;I)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \ | |||
| METHOD (launchURL, "launchURL", "(Ljava/lang/String;)V") \ | |||
| METHOD (showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ | |||
| METHOD (showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ | |||
| @@ -70,7 +70,7 @@ class WebInputStream : public InputStream | |||
| public: | |||
| WebInputStream (String address, bool isPost, const MemoryBlock& postData, | |||
| URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||
| const String& headers, int timeOutMs, StringPairArray* responseHeaders) | |||
| const String& headers, int timeOutMs, StringPairArray* responseHeaders, const int numRedirectsToFollow) | |||
| : statusCode (0) | |||
| { | |||
| if (! address.contains ("://")) | |||
| @@ -103,7 +103,8 @@ public: | |||
| javaString (headers).get(), | |||
| (jint) timeOutMs, | |||
| statusCodeArray, | |||
| responseHeaderBuffer.get())); | |||
| responseHeaderBuffer.get(), | |||
| (jint) numRedirectsToFollow)); | |||
| jint* const statusCodeElements = env->GetIntArrayElements (statusCodeArray, 0); | |||
| statusCode = statusCodeElements[0]; | |||
| @@ -74,12 +74,13 @@ class WebInputStream : public InputStream | |||
| public: | |||
| WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, | |||
| URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||
| const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) | |||
| const String& headers_, int timeOutMs_, StringPairArray* responseHeaders, | |||
| const int maxRedirects) | |||
| : statusCode (0), socketHandle (-1), levelsOfRedirection (0), | |||
| address (address_), headers (headers_), postData (postData_), position (0), | |||
| finished (false), isPost (isPost_), timeOutMs (timeOutMs_) | |||
| finished (false), isPost (isPost_), timeOutMs (timeOutMs_), numRedirectsToFollow (maxRedirects) | |||
| { | |||
| statusCode = createConnection (progressCallback, progressCallbackContext); | |||
| statusCode = createConnection (progressCallback, progressCallbackContext, numRedirectsToFollow); | |||
| if (responseHeaders != nullptr && ! isError()) | |||
| { | |||
| @@ -147,7 +148,7 @@ public: | |||
| { | |||
| closeSocket(); | |||
| position = 0; | |||
| statusCode = createConnection (0, 0); | |||
| statusCode = createConnection (0, 0, numRedirectsToFollow); | |||
| } | |||
| skipNextBytes (wantedPos - position); | |||
| @@ -168,24 +169,27 @@ private: | |||
| bool finished; | |||
| const bool isPost; | |||
| const int timeOutMs; | |||
| const int numRedirectsToFollow; | |||
| void closeSocket() | |||
| void closeSocket (bool resetLevelsOfRedirection = true) | |||
| { | |||
| if (socketHandle >= 0) | |||
| close (socketHandle); | |||
| socketHandle = -1; | |||
| levelsOfRedirection = 0; | |||
| if (resetLevelsOfRedirection) | |||
| levelsOfRedirection = 0; | |||
| } | |||
| int createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) | |||
| int createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||
| const int numRedirectsToFollow) | |||
| { | |||
| closeSocket(); | |||
| closeSocket (false); | |||
| uint32 timeOutTime = Time::getMillisecondCounter(); | |||
| if (timeOutMs == 0) | |||
| timeOutTime += 60000; | |||
| timeOutTime += 30000; | |||
| else if (timeOutMs < 0) | |||
| timeOutTime = 0xffffffff; | |||
| else | |||
| @@ -273,28 +277,28 @@ private: | |||
| const int status = responseHeader.fromFirstOccurrenceOf (" ", false, false) | |||
| .substring (0, 3).getIntValue(); | |||
| //int contentLength = findHeaderItem (lines, "Content-Length:").getIntValue(); | |||
| //bool isChunked = findHeaderItem (lines, "Transfer-Encoding:").equalsIgnoreCase ("chunked"); | |||
| String location (findHeaderItem (headerLines, "Location:")); | |||
| if (status >= 300 && status < 400 | |||
| if (++levelsOfRedirection <= numRedirectsToFollow | |||
| && status >= 300 && status < 400 | |||
| && location.isNotEmpty() && location != address) | |||
| { | |||
| if (! location.startsWithIgnoreCase ("http://")) | |||
| location = "http://" + location; | |||
| if (++levelsOfRedirection <= 3) | |||
| if (! (location.startsWithIgnoreCase ("http://") | |||
| || location.startsWithIgnoreCase ("https://") | |||
| || location.startsWithIgnoreCase ("ftp://"))) | |||
| { | |||
| address = location; | |||
| return createConnection (progressCallback, progressCallbackContext); | |||
| // The following is a bit dodgy. Ideally, we should do a proper transform of the relative URI to a target URI | |||
| if (location.startsWithChar ('/')) | |||
| location = URL (address).withNewSubPath (location).toString (true); | |||
| else | |||
| location = address + "/" + location; | |||
| } | |||
| address = location; | |||
| return createConnection (progressCallback, progressCallbackContext, numRedirectsToFollow); | |||
| } | |||
| else | |||
| { | |||
| levelsOfRedirection = 0; | |||
| return status; | |||
| } | |||
| return status; | |||
| } | |||
| closeSocket(); | |||
| @@ -363,10 +367,14 @@ private: | |||
| writeValueIfNotPresent (header, userHeaders, "Connection:", "close"); | |||
| if (isPost) | |||
| { | |||
| writeValueIfNotPresent (header, userHeaders, "Content-Length:", String ((int) postData.getSize())); | |||
| header << "\r\n" << userHeaders | |||
| << "\r\n" << postData; | |||
| header << userHeaders << "\r\n" << postData; | |||
| } | |||
| else | |||
| { | |||
| header << "\r\n" << userHeaders << "\r\n"; | |||
| } | |||
| return header.getMemoryBlock(); | |||
| } | |||
| @@ -115,7 +115,7 @@ bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailA | |||
| class URLConnectionState : public Thread | |||
| { | |||
| public: | |||
| URLConnectionState (NSURLRequest* req) | |||
| URLConnectionState (NSURLRequest* req, const int maxRedirects) | |||
| : Thread ("http connection"), | |||
| contentLength (-1), | |||
| delegate (nil), | |||
| @@ -126,7 +126,9 @@ public: | |||
| statusCode (0), | |||
| initialised (false), | |||
| hasFailed (false), | |||
| hasFinished (false) | |||
| hasFinished (false), | |||
| numRedirectsToFollow (maxRedirects), | |||
| numRedirects (0) | |||
| { | |||
| static DelegateClass cls; | |||
| delegate = [cls.createInstance() init]; | |||
| @@ -215,6 +217,19 @@ public: | |||
| } | |||
| } | |||
| NSURLRequest* willSendRequest (NSURLRequest* newRequest, NSURLResponse* redirectResponse) | |||
| { | |||
| if (redirectResponse != nullptr) | |||
| { | |||
| if (numRedirects >= numRedirectsToFollow) | |||
| return nil; // Cancel redirect and allow connection to continue | |||
| ++numRedirects; | |||
| } | |||
| return newRequest; | |||
| } | |||
| void didFailWithError (NSError* error) | |||
| { | |||
| DBG (nsStringToJuce ([error description])); (void) error; | |||
| @@ -263,6 +278,8 @@ public: | |||
| NSDictionary* headers; | |||
| int statusCode; | |||
| bool initialised, hasFailed, hasFinished; | |||
| const int numRedirectsToFollow; | |||
| int numRedirects; | |||
| private: | |||
| //============================================================================== | |||
| @@ -278,7 +295,7 @@ private: | |||
| addMethod (@selector (connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:), | |||
| connectionDidSendBodyData, "v@:@iii"); | |||
| addMethod (@selector (connectionDidFinishLoading:), connectionDidFinishLoading, "v@:@"); | |||
| addMethod (@selector (connection:willSendRequest:redirectResponse:), willSendRequest, "@@:@@"); | |||
| addMethod (@selector (connection:willSendRequest:redirectResponse:), willSendRequest, "@@:@@@"); | |||
| registerClass(); | |||
| } | |||
| @@ -302,9 +319,9 @@ private: | |||
| getState (self)->didReceiveData (newData); | |||
| } | |||
| static NSURLRequest* willSendRequest (id, SEL, NSURLConnection*, NSURLRequest* request, NSURLResponse*) | |||
| static NSURLRequest* willSendRequest (id self, SEL, NSURLConnection*, NSURLRequest* request, NSURLResponse* response) | |||
| { | |||
| return request; | |||
| return getState (self)->willSendRequest (request, response); | |||
| } | |||
| static void connectionDidSendBodyData (id self, SEL, NSURLConnection*, NSInteger, NSInteger totalBytesWritten, NSInteger totalBytesExpected) | |||
| @@ -328,23 +345,27 @@ class WebInputStream : public InputStream | |||
| public: | |||
| WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, | |||
| URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||
| const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) | |||
| const String& headers_, int timeOutMs_, StringPairArray* responseHeaders, | |||
| const int numRedirectsToFollow_) | |||
| : statusCode (0), address (address_), headers (headers_), postData (postData_), position (0), | |||
| finished (false), isPost (isPost_), timeOutMs (timeOutMs_) | |||
| finished (false), isPost (isPost_), timeOutMs (timeOutMs_), numRedirectsToFollow (numRedirectsToFollow_) | |||
| { | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| createConnection (progressCallback, progressCallbackContext); | |||
| if (responseHeaders != nullptr && connection != nullptr && connection->headers != nil) | |||
| if (connection != nullptr && connection->headers != nil) | |||
| { | |||
| statusCode = connection->statusCode; | |||
| NSEnumerator* enumerator = [connection->headers keyEnumerator]; | |||
| if (responseHeaders != nullptr) | |||
| { | |||
| NSEnumerator* enumerator = [connection->headers keyEnumerator]; | |||
| while (NSString* key = [enumerator nextObject]) | |||
| responseHeaders->set (nsStringToJuce (key), | |||
| nsStringToJuce ((NSString*) [connection->headers objectForKey: key])); | |||
| while (NSString* key = [enumerator nextObject]) | |||
| responseHeaders->set (nsStringToJuce (key), | |||
| nsStringToJuce ((NSString*) [connection->headers objectForKey: key])); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -403,6 +424,7 @@ private: | |||
| bool finished; | |||
| const bool isPost; | |||
| const int timeOutMs; | |||
| const int numRedirectsToFollow; | |||
| void createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) | |||
| { | |||
| @@ -434,7 +456,7 @@ private: | |||
| [req setHTTPBody: [NSData dataWithBytes: postData.getData() | |||
| length: postData.getSize()]]; | |||
| connection = new URLConnectionState (req); | |||
| connection = new URLConnectionState (req, numRedirectsToFollow); | |||
| if (! connection->start (progressCallback, progressCallbackContext)) | |||
| connection = nullptr; | |||
| @@ -40,12 +40,12 @@ class WebInputStream : public InputStream | |||
| public: | |||
| WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, | |||
| URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, | |||
| const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) | |||
| const String& headers_, int timeOutMs_, StringPairArray* responseHeaders, int numRedirectsToFollow) | |||
| : statusCode (0), connection (0), request (0), | |||
| address (address_), headers (headers_), postData (postData_), position (0), | |||
| finished (false), isPost (isPost_), timeOutMs (timeOutMs_) | |||
| { | |||
| for (int maxRedirects = 10; --maxRedirects >= 0;) | |||
| while (numRedirectsToFollow-- >= 0) | |||
| { | |||
| createConnection (progressCallback, progressCallbackContext); | |||
| @@ -88,9 +88,22 @@ public: | |||
| { | |||
| statusCode = (int) status; | |||
| if (status == 301 || status == 302 || status == 303 || status == 307) | |||
| if (numRedirectsToFollow >= 0 | |||
| && (statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 307)) | |||
| { | |||
| const String newLocation (headers["Location"]); | |||
| String newLocation (headers["Location"]); | |||
| // Check whether location is a relative URI - this is an incomplete test for relative path, | |||
| // but we'll use it for now (valid protocols for this implementation are http, https & ftp) | |||
| if (! (newLocation.startsWithIgnoreCase ("http://") | |||
| || newLocation.startsWithIgnoreCase ("https://") | |||
| || newLocation.startsWithIgnoreCase ("ftp://"))) | |||
| { | |||
| if (newLocation.startsWithChar ('/')) | |||
| newLocation = URL (address).withNewSubPath (newLocation).toString (true); | |||
| else | |||
| newLocation = address + "/" + newLocation; | |||
| } | |||
| if (newLocation.isNotEmpty() && newLocation != address) | |||
| { | |||
| @@ -334,7 +334,8 @@ InputStream* URL::createInputStream (const bool usePostCommand, | |||
| String headers, | |||
| const int timeOutMs, | |||
| StringPairArray* const responseHeaders, | |||
| int* statusCode) const | |||
| int* statusCode, | |||
| const int numRedirectsToFollow) const | |||
| { | |||
| MemoryBlock headersAndPostData; | |||
| @@ -350,7 +351,8 @@ InputStream* URL::createInputStream (const bool usePostCommand, | |||
| ScopedPointer<WebInputStream> wi (new WebInputStream (toString (! usePostCommand), | |||
| usePostCommand, headersAndPostData, | |||
| progressCallback, progressCallbackContext, | |||
| headers, timeOutMs, responseHeaders)); | |||
| headers, timeOutMs, responseHeaders, | |||
| numRedirectsToFollow)); | |||
| if (statusCode != nullptr) | |||
| *statusCode = wi->statusCode; | |||
| @@ -266,6 +266,8 @@ public: | |||
| in the response will be stored in this array | |||
| @param statusCode if this is non-null, it will get set to the http status code, if one | |||
| is known, or 0 if a code isn't available | |||
| @param numRedirectsToFollow specifies the number of redirects that will be followed before | |||
| returning a response (ignored for Android which follows up to 5 redirects) | |||
| @returns an input stream that the caller must delete, or a null pointer if there was an | |||
| error trying to open it. | |||
| */ | |||
| @@ -275,7 +277,8 @@ public: | |||
| String extraHeaders = String(), | |||
| int connectionTimeOutMs = 0, | |||
| StringPairArray* responseHeaders = nullptr, | |||
| int* statusCode = nullptr) const; | |||
| int* statusCode = nullptr, | |||
| int numRedirectsToFollow = 5) const; | |||
| //============================================================================== | |||