diff --git a/game/sdl/ios/iphonehttprequest.h b/game/sdl/ios/iphonehttprequest.h index 818e8d3..d915781 100644 --- a/game/sdl/ios/iphonehttprequest.h +++ b/game/sdl/ios/iphonehttprequest.h @@ -9,22 +9,6 @@ #include "base/thread.h" #include "base/bytebuffer.h" -namespace wi { -class IPhoneHttpRequest; -} - -@interface ConnectionDelegate : NSObject { - NSURLConnection *conn_; - wi::IPhoneHttpRequest *req_; -} -- (void)submit; -- (void)cancel; -- (void)connection:(NSURLConnection *)conn - didFailWithError:(NSError *)error; -- (void)connection:(NSURLConnection *)conn didReceiveData:(NSData *)data; -- (void)connectionDidFinishLoading:(NSURLConnection *)conn; -@end - namespace wi { class IPhoneHttpRequest : public HttpRequest, base::MessageHandler { @@ -32,8 +16,7 @@ public: IPhoneHttpRequest(HttpResponseHandler *phandler); ~IPhoneHttpRequest(); - void Submit(); - void Release(); + void Dispose(); NSURLRequest *CreateNSURLRequest(); void OnReceivedResponse(NSHTTPURLResponse *resp); void OnReceivedData(NSData *data); @@ -44,7 +27,6 @@ private: virtual void OnMessage(base::Message *pmsg); HttpResponseHandler *handler_; - ConnectionDelegate *delegate_; }; struct ReceivedResponseParams : base::MessageData { diff --git a/game/sdl/ios/iphonehttprequest.mm b/game/sdl/ios/iphonehttprequest.mm index 87ecb32..0a2dbc1 100644 --- a/game/sdl/ios/iphonehttprequest.mm +++ b/game/sdl/ios/iphonehttprequest.mm @@ -5,126 +5,69 @@ // Requests come in from the game thread; NS* api calls occur on main // thread. -@implementation ConnectionDelegate - -- (id)initWithRequest:(wi::IPhoneHttpRequest *)req { - self = [super init]; - if (self != nil) { - req_ = req; - conn_ = nil; - } - return self; -} - - -- (void)submit { - NSURLRequest *req = req_->CreateNSURLRequest(); - conn_ = [NSURLConnection - connectionWithRequest:req - delegate:self]; -} - -- (void)cancel { - [conn_ cancel]; - conn_ = nil; -} - -- (void)connection:(NSURLConnection *)conn - didReceiveResponse:(NSURLResponse *)resp { - req_->OnReceivedResponse((NSHTTPURLResponse *)resp); -} - -- (void)connection:(NSURLConnection *)conn didReceiveData:(NSData *)data { - req_->OnReceivedData(data); -} - -- (void)connectionDidFinishLoading:(NSURLConnection *)conn { - req_->OnFinishedLoading(); -} - -- (void)connection:(NSURLConnection *)conn - didFailWithError:(NSError *)error { - NSLog(@"error: %@", error); - req_->OnError(error); -} -@end // C++ implementation of HttpRequest interface for iPhone namespace wi { -IPhoneHttpRequest::IPhoneHttpRequest(HttpResponseHandler *handler) : - handler_(handler), delegate_(nil) { +IPhoneHttpRequest::IPhoneHttpRequest(HttpResponseHandler *handler) : handler_(handler) { } IPhoneHttpRequest::~IPhoneHttpRequest() { } -void IPhoneHttpRequest::Submit() { - delegate_ = [[ConnectionDelegate alloc] initWithRequest:this]; - [delegate_ performSelectorOnMainThread:@selector(submit) - withObject:nil waitUntilDone: NO]; -} - -void IPhoneHttpRequest::Release() { - // This can cause a deadlock when exiting because of how the main thread - // is synchronizing with the game thread to exit before it does - - /*if (!IPhone::IsExiting()) { - [delegate_ performSelectorOnMainThread:@selector(cancel) - withObject:nil waitUntilDone: YES]; - [delegate_ release]; - }*/ - delegate_ = nil; +void IPhoneHttpRequest::Dispose() { + // Called on game thread thread_.Clear(this); - Dispose(); + MessageHandler::Dispose(); } NSURLRequest *IPhoneHttpRequest::CreateNSURLRequest() { - @autoreleasepool { - NSMutableURLRequest *req = [[NSMutableURLRequest alloc] init]; + // Called on main thread + // NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSMutableURLRequest *req = [[NSMutableURLRequest alloc] init]; - // Set the url - NSString *s = [NSString stringWithCString:url_.c_str() - encoding:[NSString defaultCStringEncoding]]; - [req setURL:[NSURL URLWithString:s]]; + // Set the url + NSString *s = [NSString stringWithCString:url_.c_str() + encoding:[NSString defaultCStringEncoding]]; + [req setURL:[NSURL URLWithString:s]]; - // Set the method - s = [NSString stringWithCString:method_.c_str() - encoding:[NSString defaultCStringEncoding]]; - [req setHTTPMethod:s]; + // Set the method + s = [NSString stringWithCString:method_.c_str() + encoding:[NSString defaultCStringEncoding]]; + [req setHTTPMethod:s]; - // Set the body - if (pbb_ != NULL) { - int cb; - void *pv = pbb_->Strip(&cb); - NSData *data = [NSData dataWithBytesNoCopy:(void *)pv length:cb]; - [req setHTTPBody:data]; - } - - // Set timeout - [req setTimeoutInterval:timeout_]; - - // Set cache policy - [req setCachePolicy:NSURLRequestReloadIgnoringCacheData]; - - // Set headers - Enum enm; - char szKey[128]; - while (headers_.EnumKeys(&enm, szKey, sizeof(szKey))) { - char szValue[256]; - if (headers_.GetValue(szKey, szValue, sizeof(szValue))) { - NSString *key = [NSString stringWithCString:szKey - encoding:[NSString defaultCStringEncoding]]; - NSString *value = [NSString stringWithCString:szValue - encoding:[NSString defaultCStringEncoding]]; - [req setValue:value forHTTPHeaderField:key]; - } - } - - // Done - return req; + // Set the body + if (pbb_ != NULL) { + int cb; + void *pv = pbb_->Strip(&cb); + NSData *data = [NSData dataWithBytesNoCopy:(void *)pv length:cb]; + [req setHTTPBody:data]; } + + // Set timeout + [req setTimeoutInterval:timeout_]; + + // Set cache policy + [req setCachePolicy:NSURLRequestReloadIgnoringCacheData]; + + // Set headers + Enum enm; + char szKey[128]; + while (headers_.EnumKeys(&enm, szKey, sizeof(szKey))) { + char szValue[256]; + if (headers_.GetValue(szKey, szValue, sizeof(szValue))) { + NSString *key = [NSString stringWithCString:szKey + encoding:[NSString defaultCStringEncoding]]; + NSString *value = [NSString stringWithCString:szValue + encoding:[NSString defaultCStringEncoding]]; + [req setValue:value forHTTPHeaderField:key]; + } + } + + // Done + // [pool release]; + return req; } void IPhoneHttpRequest::OnMessage(base::Message *pmsg) { @@ -172,7 +115,7 @@ void IPhoneHttpRequest::OnReceivedResponse(NSHTTPURLResponse *resp) { [k cStringUsingEncoding:[NSString defaultCStringEncoding]], [v cStringUsingEncoding:[NSString defaultCStringEncoding]]); } - pparams->code = [resp statusCode]; + pparams->code = (int)[resp statusCode]; // Post this to the game thread thread_.Post(kidmReceivedResponse, this, pparams); @@ -181,7 +124,7 @@ void IPhoneHttpRequest::OnReceivedResponse(NSHTTPURLResponse *resp) { void IPhoneHttpRequest::OnReceivedData(NSData *data) { // Called on main thread. Populate ReceivedDataParams ReceivedDataParams *pparams = new ReceivedDataParams; - pparams->bb.WriteBytes((const byte *)[data bytes], [data length]); + pparams->bb.WriteBytes((const byte *)[data bytes], (int)[data length]); // Post this to the game thread thread_.Post(kidmReceivedData, this, pparams); diff --git a/game/sdl/ios/iphonehttpservice.h b/game/sdl/ios/iphonehttpservice.h index 6e6f735..7cefa37 100644 --- a/game/sdl/ios/iphonehttpservice.h +++ b/game/sdl/ios/iphonehttpservice.h @@ -1,17 +1,32 @@ #ifndef __IPHONEHTTPSERVICE_H__ #define __IPHONEHTTPSERVICE_H__ +#include "game/sdl/ios/iphonehttprequest.h" #include "inc/basictypes.h" #include "game/httpservice.h" +#include + +typedef std::map TaskMap; + +@interface SessionDelegate : NSObject { + NSURLSession *session_; + TaskMap taskmap_; +} +@end namespace wi { class IPhoneHttpService : public HttpService { public: + IPhoneHttpService(); + // HttpService methods virtual HttpRequest *NewRequest(HttpResponseHandler *phandler); virtual void SubmitRequest(HttpRequest *preq); virtual void ReleaseRequest(HttpRequest *preq); + +private: + SessionDelegate *delegate_; }; } // namespace wi diff --git a/game/sdl/ios/iphonehttpservice.mm b/game/sdl/ios/iphonehttpservice.mm index 7d177d3..f539079 100644 --- a/game/sdl/ios/iphonehttpservice.mm +++ b/game/sdl/ios/iphonehttpservice.mm @@ -1,23 +1,147 @@ +#import +#import + #include "game/sdl/ios/iphonehttpservice.h" #include "game/sdl/ios/iphonehttprequest.h" +#include "game/sdl/sysmessages.h" +#include "game/sdl/hosthelpers.h" // HttpService calls come in on the game thread. In order to use // iPhone NS* Http apis, requests execute on the main thread. +@interface IPhoneHttpRequestWrapper : NSObject { + wi::IPhoneHttpRequest *req_; +} +@end + +@implementation IPhoneHttpRequestWrapper +- (id)initWithRequest:(wi::IPhoneHttpRequest *)req { + self = [super init]; + if (self != nil) { + req_ = req; + } + return self; +} + +- (wi::IPhoneHttpRequest *)request { + return req_; +} +@end + +@implementation SessionDelegate +- (id)init { + self = [super init]; + if (self != nil) { + session_ = nil; + } + return self; +} + +- (void)dealloc { + // [session_ release]; + // [super dealloc]; +} + +- (void)cancel:(IPhoneHttpRequestWrapper *)wrapper { + wi::IPhoneHttpRequest *req = [wrapper request]; + NSURLSessionDataTask *task = nil; + TaskMap::iterator it = taskmap_.begin(); + for (; it != taskmap_.end(); it++) { + if (it->second == req) { + task = it->first; + taskmap_.erase(it); + break; + } + } + if (task != nil) { + [task cancel]; + // [task release]; + } + // [wrapper release]; +} + +- (void)submit:(IPhoneHttpRequestWrapper *)wrapper { + if (session_ == nil) { + session_ = [NSURLSession + sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] + delegate:self + delegateQueue:[NSOperationQueue mainQueue]]; + } + wi::IPhoneHttpRequest *req = [wrapper request]; + NSURLRequest *url_req = req->CreateNSURLRequest(); + NSURLSessionDataTask *task = [session_ dataTaskWithRequest:url_req]; + // [url_req release]; + // [task retain]; + taskmap_.insert(TaskMap::value_type(task, req)); + [task resume]; + // [wrapper release]; +} + +- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)task + didReceiveResponse:(NSURLResponse *)resp + completionHandler:(void (^)(NSURLSessionResponseDisposition disp))handler { + TaskMap::iterator it = taskmap_.find(task); + if (it != taskmap_.end()) { + it->second->OnReceivedResponse((NSHTTPURLResponse *)resp); + } + handler(NSURLSessionResponseAllow); +} + +- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)task + didReceiveData:(NSData *)data { + TaskMap::iterator it = taskmap_.find(task); + if (it != taskmap_.end()) { + it->second->OnReceivedData(data); + } +} + +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task + didCompleteWithError:(NSError *)error { + TaskMap::iterator it = taskmap_.find((NSURLSessionDataTask *)task); + if (it != taskmap_.end()) { + if (error == nil) { + it->second->OnFinishedLoading(); + } else { + NSLog(@"error: %@", error); + it->second->OnError(error); + } + } +} +@end + namespace wi { +IPhoneHttpService::IPhoneHttpService() { + delegate_ = NULL; +} + HttpRequest *IPhoneHttpService::NewRequest(HttpResponseHandler *phandler) { return new IPhoneHttpRequest(phandler); } void IPhoneHttpService::SubmitRequest(HttpRequest *preq) { - IPhoneHttpRequest *preqT = (IPhoneHttpRequest *)preq; - preqT->Submit(); + if (delegate_ == NULL) { + delegate_ = [[SessionDelegate alloc] init]; + } + IPhoneHttpRequestWrapper *wrapper = [[IPhoneHttpRequestWrapper alloc] + initWithRequest:(IPhoneHttpRequest *)preq]; + SessionDelegate *delegate = (SessionDelegate *)delegate_; + [delegate performSelectorOnMainThread:@selector(submit:) + withObject:wrapper waitUntilDone: NO]; } void IPhoneHttpService::ReleaseRequest(HttpRequest *preq) { - IPhoneHttpRequest *preqT = (IPhoneHttpRequest *)preq; - preqT->Release(); + // This can cause a deadlock when exiting because of how the main thread + // is synchronizing with the game thread to exit before it does + IPhoneHttpRequest *req = (IPhoneHttpRequest *)preq; + if (!HostHelpers::IsExiting()) { + IPhoneHttpRequestWrapper *wrapper = [[IPhoneHttpRequestWrapper alloc] + initWithRequest:req]; + SessionDelegate *delegate = (SessionDelegate *)delegate_; + [delegate performSelectorOnMainThread:@selector(cancel:) + withObject:wrapper waitUntilDone: YES]; + } + req->Dispose(); } } // namespace wi