| @@ -35,6 +35,7 @@ void MACAddress::findAllAddresses (Array<MACAddress>& result) | |||||
| for (const ifaddrs* cursor = addrs; cursor != nullptr; cursor = cursor->ifa_next) | for (const ifaddrs* cursor = addrs; cursor != nullptr; cursor = cursor->ifa_next) | ||||
| { | { | ||||
| sockaddr_storage* sto = (sockaddr_storage*) cursor->ifa_addr; | sockaddr_storage* sto = (sockaddr_storage*) cursor->ifa_addr; | ||||
| if (sto->ss_family == AF_LINK) | if (sto->ss_family == AF_LINK) | ||||
| { | { | ||||
| const sockaddr_dl* const sadd = (const sockaddr_dl*) cursor->ifa_addr; | const sockaddr_dl* const sadd = (const sockaddr_dl*) cursor->ifa_addr; | ||||
| @@ -45,7 +46,7 @@ void MACAddress::findAllAddresses (Array<MACAddress>& result) | |||||
| if (sadd->sdl_type == IFT_ETHER) | if (sadd->sdl_type == IFT_ETHER) | ||||
| { | { | ||||
| MACAddress ma (MACAddress (((const uint8*) sadd->sdl_data) + sadd->sdl_nlen)); | |||||
| MACAddress ma (((const uint8*) sadd->sdl_data) + sadd->sdl_nlen); | |||||
| if (! ma.isNull()) | if (! ma.isNull()) | ||||
| result.addIfNotAlreadyThere (ma); | result.addIfNotAlreadyThere (ma); | ||||
| @@ -109,7 +110,7 @@ bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailA | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| class URLConnectionState : public Thread | |||||
| class URLConnectionState : private Thread | |||||
| { | { | ||||
| public: | public: | ||||
| URLConnectionState (NSURLRequest* req, const int maxRedirects) | URLConnectionState (NSURLRequest* req, const int maxRedirects) | ||||
| @@ -117,7 +118,8 @@ public: | |||||
| contentLength (-1), | contentLength (-1), | ||||
| delegate (nil), | delegate (nil), | ||||
| request ([req retain]), | request ([req retain]), | ||||
| connection (nil), | |||||
| session (nil), | |||||
| task (nil), | |||||
| data ([[NSMutableData data] retain]), | data ([[NSMutableData data] retain]), | ||||
| headers (nil), | headers (nil), | ||||
| statusCode (0), | statusCode (0), | ||||
| @@ -136,10 +138,10 @@ public: | |||||
| ~URLConnectionState() | ~URLConnectionState() | ||||
| { | { | ||||
| stop(); | stop(); | ||||
| [connection release]; | |||||
| [data release]; | [data release]; | ||||
| [request release]; | [request release]; | ||||
| [headers release]; | [headers release]; | ||||
| [session release]; | |||||
| [delegate release]; | [delegate release]; | ||||
| } | } | ||||
| @@ -150,18 +152,20 @@ public: | |||||
| while (isThreadRunning() && ! initialised) | while (isThreadRunning() && ! initialised) | ||||
| { | { | ||||
| if (callback != nullptr) | if (callback != nullptr) | ||||
| callback (context, latestTotalBytes, (int) [[request HTTPBody] length]); | |||||
| callback (context, (int) latestTotalBytes, (int) [[request HTTPBody] length]); | |||||
| Thread::sleep (1); | Thread::sleep (1); | ||||
| } | } | ||||
| return connection != nil && ! hasFailed; | |||||
| return true; | |||||
| } | } | ||||
| void stop() | void stop() | ||||
| { | { | ||||
| [connection cancel]; | |||||
| [task cancel]; | |||||
| stopThread (10000); | stopThread (10000); | ||||
| [task release]; | |||||
| task = nil; | |||||
| } | } | ||||
| int read (char* dest, int numBytes) | int read (char* dest, int numBytes) | ||||
| @@ -194,7 +198,7 @@ public: | |||||
| return numDone; | return numDone; | ||||
| } | } | ||||
| void didReceiveResponse (NSURLResponse* response) | |||||
| void didReceiveResponse (NSURLResponse* response, id completionHandler) | |||||
| { | { | ||||
| { | { | ||||
| const ScopedLock sl (dataLock); | const ScopedLock sl (dataLock); | ||||
| @@ -214,22 +218,17 @@ public: | |||||
| } | } | ||||
| initialised = true; | initialised = true; | ||||
| } | |||||
| NSURLRequest* willSendRequest (NSURLRequest* newRequest, NSURLResponse* redirectResponse) | |||||
| { | |||||
| if (redirectResponse != nullptr) | |||||
| if (completionHandler != nil) | |||||
| { | { | ||||
| if (numRedirects >= numRedirectsToFollow) | |||||
| return nil; // Cancel redirect and allow connection to continue | |||||
| ++numRedirects; | |||||
| // Need to wrangle this parameter back into an obj-C block, | |||||
| // and call it to allow the transfer to continue.. | |||||
| void (^callbackBlock)(NSURLSessionResponseDisposition) = completionHandler; | |||||
| callbackBlock (NSURLSessionResponseAllow); | |||||
| } | } | ||||
| return newRequest; | |||||
| } | } | ||||
| void didFailWithError (NSError* error) | |||||
| void didBecomeInvalidWithError (NSError* error) | |||||
| { | { | ||||
| DBG (nsStringToJuce ([error description])); ignoreUnused (error); | DBG (nsStringToJuce ([error description])); ignoreUnused (error); | ||||
| hasFailed = true; | hasFailed = true; | ||||
| @@ -244,60 +243,67 @@ public: | |||||
| initialised = true; | initialised = true; | ||||
| } | } | ||||
| void didSendBodyData (NSInteger totalBytesWritten, NSInteger /*totalBytesExpected*/) | |||||
| void didSendBodyData (int64_t totalBytesWritten) | |||||
| { | { | ||||
| latestTotalBytes = static_cast<int> (totalBytesWritten); | latestTotalBytes = static_cast<int> (totalBytesWritten); | ||||
| } | } | ||||
| void finishedLoading() | |||||
| { | |||||
| hasFinished = true; | |||||
| initialised = true; | |||||
| signalThreadShouldExit(); | |||||
| } | |||||
| void run() override | void run() override | ||||
| { | { | ||||
| connection = [[NSURLConnection alloc] initWithRequest: request | |||||
| delegate: delegate]; | |||||
| jassert (task == nil && session == nil); | |||||
| session = [[NSURLSession sessionWithConfiguration: [NSURLSessionConfiguration defaultSessionConfiguration] | |||||
| delegate: delegate | |||||
| delegateQueue: [NSOperationQueue currentQueue]] retain]; | |||||
| task = [session dataTaskWithRequest: request]; | |||||
| if (task == nil) | |||||
| return; | |||||
| [task retain]; | |||||
| [task resume]; | |||||
| while (! threadShouldExit()) | while (! threadShouldExit()) | ||||
| { | { | ||||
| JUCE_AUTORELEASEPOOL | |||||
| { | |||||
| [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; | |||||
| } | |||||
| wait (5); | |||||
| if (task.state != NSURLSessionTaskStateRunning) | |||||
| break; | |||||
| } | } | ||||
| hasFinished = true; | |||||
| initialised = true; | |||||
| } | } | ||||
| int64 contentLength; | int64 contentLength; | ||||
| CriticalSection dataLock; | CriticalSection dataLock; | ||||
| NSObject* delegate; | |||||
| id delegate; | |||||
| NSURLRequest* request; | NSURLRequest* request; | ||||
| NSURLConnection* connection; | |||||
| NSURLSession* session; | |||||
| NSURLSessionTask* task; | |||||
| NSMutableData* data; | NSMutableData* data; | ||||
| NSDictionary* headers; | NSDictionary* headers; | ||||
| int statusCode; | int statusCode; | ||||
| bool initialised, hasFailed, hasFinished; | bool initialised, hasFailed, hasFinished; | ||||
| const int numRedirectsToFollow; | const int numRedirectsToFollow; | ||||
| int numRedirects; | int numRedirects; | ||||
| int latestTotalBytes; | |||||
| int64 latestTotalBytes; | |||||
| private: | private: | ||||
| //============================================================================== | //============================================================================== | ||||
| struct DelegateClass : public ObjCClass<NSObject> | struct DelegateClass : public ObjCClass<NSObject> | ||||
| { | { | ||||
| DelegateClass() : ObjCClass<NSObject> ("JUCEAppDelegate_") | |||||
| DelegateClass() : ObjCClass<NSObject> ("JUCE_URLDelegate_") | |||||
| { | { | ||||
| addIvar<URLConnectionState*> ("state"); | addIvar<URLConnectionState*> ("state"); | ||||
| addMethod (@selector (connection:didReceiveResponse:), didReceiveResponse, "v@:@@"); | |||||
| addMethod (@selector (connection:didFailWithError:), didFailWithError, "v@:@@"); | |||||
| addMethod (@selector (connection:didReceiveData:), didReceiveData, "v@:@@"); | |||||
| addMethod (@selector (connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:), | |||||
| connectionDidSendBodyData, "v@:@iii"); | |||||
| addMethod (@selector (connectionDidFinishLoading:), connectionDidFinishLoading, "v@:@"); | |||||
| addMethod (@selector (connection:willSendRequest:redirectResponse:), willSendRequest, "@@:@@@"); | |||||
| addMethod (@selector (URLSession:dataTask:didReceiveResponse:completionHandler:), | |||||
| didReceiveResponse, "v@:@@@@"); | |||||
| addMethod (@selector (URLSession:didBecomeInvalidWithError:), didBecomeInvalidWithError, "v@:@@"); | |||||
| addMethod (@selector (URLSession:dataTask:didReceiveData:), didReceiveData, "v@:@@@"); | |||||
| addMethod (@selector (URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:), | |||||
| didSendBodyData, "v@:@@qqq"); | |||||
| registerClass(); | registerClass(); | ||||
| } | } | ||||
| @@ -305,34 +311,24 @@ private: | |||||
| static URLConnectionState* getState (id self) { return getIvar<URLConnectionState*> (self, "state"); } | static URLConnectionState* getState (id self) { return getIvar<URLConnectionState*> (self, "state"); } | ||||
| private: | private: | ||||
| static void didReceiveResponse (id self, SEL, NSURLConnection*, NSURLResponse* response) | |||||
| static void didReceiveResponse (id self, SEL, NSURLSession*, NSURLSessionDataTask*, NSURLResponse* response, id completionHandler) | |||||
| { | { | ||||
| getState (self)->didReceiveResponse (response); | |||||
| getState (self)->didReceiveResponse (response, completionHandler); | |||||
| } | } | ||||
| static void didFailWithError (id self, SEL, NSURLConnection*, NSError* error) | |||||
| static void didBecomeInvalidWithError (id self, SEL, NSURLSession*, NSError* error) | |||||
| { | { | ||||
| getState (self)->didFailWithError (error); | |||||
| getState (self)->didBecomeInvalidWithError (error); | |||||
| } | } | ||||
| static void didReceiveData (id self, SEL, NSURLConnection*, NSData* newData) | |||||
| static void didReceiveData (id self, SEL, NSURLSession*, NSURLSessionDataTask*, NSData* newData) | |||||
| { | { | ||||
| getState (self)->didReceiveData (newData); | getState (self)->didReceiveData (newData); | ||||
| } | } | ||||
| static NSURLRequest* willSendRequest (id self, SEL, NSURLConnection*, NSURLRequest* request, NSURLResponse* response) | |||||
| static void didSendBodyData (id self, SEL, NSURLSession*, NSURLSessionTask*, int64_t, int64_t totalBytesWritten, int64_t) | |||||
| { | { | ||||
| return getState (self)->willSendRequest (request, response); | |||||
| } | |||||
| static void connectionDidSendBodyData (id self, SEL, NSURLConnection*, NSInteger, NSInteger totalBytesWritten, NSInteger totalBytesExpected) | |||||
| { | |||||
| getState (self)->didSendBodyData (totalBytesWritten, totalBytesExpected); | |||||
| } | |||||
| static void connectionDidFinishLoading (id self, SEL, NSURLConnection*) | |||||
| { | |||||
| getState (self)->finishedLoading(); | |||||
| getState (self)->didSendBodyData (totalBytesWritten); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -433,14 +429,11 @@ private: | |||||
| { | { | ||||
| jassert (connection == nullptr); | jassert (connection == nullptr); | ||||
| NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: juceStringToNS (address)] | |||||
| cachePolicy: NSURLRequestReloadIgnoringLocalCacheData | |||||
| timeoutInterval: timeOutMs <= 0 ? 60.0 : (timeOutMs / 1000.0)]; | |||||
| if (req != nil) | |||||
| if (NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: juceStringToNS (address)] | |||||
| cachePolicy: NSURLRequestReloadIgnoringLocalCacheData | |||||
| timeoutInterval: timeOutMs <= 0 ? 60.0 : (timeOutMs / 1000.0)]) | |||||
| { | { | ||||
| [req setHTTPMethod: [NSString stringWithUTF8String: httpRequestCmd.toRawUTF8()]]; | [req setHTTPMethod: [NSString stringWithUTF8String: httpRequestCmd.toRawUTF8()]]; | ||||
| //[req setCachePolicy: NSURLRequestReloadIgnoringLocalAndRemoteCacheData]; | |||||
| StringArray headerLines; | StringArray headerLines; | ||||
| headerLines.addLines (headers); | headerLines.addLines (headers); | ||||
| @@ -448,8 +441,8 @@ private: | |||||
| for (int i = 0; i < headerLines.size(); ++i) | for (int i = 0; i < headerLines.size(); ++i) | ||||
| { | { | ||||
| const String key (headerLines[i].upToFirstOccurrenceOf (":", false, false).trim()); | |||||
| const String value (headerLines[i].fromFirstOccurrenceOf (":", false, false).trim()); | |||||
| String key = headerLines[i].upToFirstOccurrenceOf (":", false, false).trim(); | |||||
| String value = headerLines[i].fromFirstOccurrenceOf (":", false, false).trim(); | |||||
| if (key.isNotEmpty() && value.isNotEmpty()) | if (key.isNotEmpty() && value.isNotEmpty()) | ||||
| [req addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)]; | [req addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)]; | ||||