mirror of
https://github.com/spiffcode/hostile-takeover.git
synced 2026-03-27 15:29:39 -06:00
NSURLConnection -> NSURLSession for SDL-OSX
Code for this commit is based off of the network code in game/iphone
This commit is contained in:
parent
72566c33bd
commit
8ba3738d0f
@ -9,22 +9,6 @@
|
||||
#include "base/thread.h"
|
||||
#include "base/bytebuffer.h"
|
||||
|
||||
namespace wi {
|
||||
class MacHttpRequest;
|
||||
}
|
||||
|
||||
@interface ConnectionDelegate : NSObject {
|
||||
NSURLConnection *conn_;
|
||||
wi::MacHttpRequest *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 MacHttpRequest : public HttpRequest, base::MessageHandler {
|
||||
@ -32,8 +16,7 @@ public:
|
||||
MacHttpRequest(HttpResponseHandler *phandler);
|
||||
~MacHttpRequest();
|
||||
|
||||
void Submit();
|
||||
void Release();
|
||||
void Dispose();
|
||||
NSURLRequest *CreateNSURLRequest();
|
||||
void OnReceivedResponse(NSHTTPURLResponse *resp);
|
||||
void OnReceivedData(NSData *data);
|
||||
@ -41,8 +24,22 @@ public:
|
||||
void OnError(NSError *error);
|
||||
|
||||
private:
|
||||
virtual void OnMessage(base::Message *pmsg);
|
||||
|
||||
HttpResponseHandler *handler_;
|
||||
ConnectionDelegate *delegate_;
|
||||
};
|
||||
|
||||
struct ReceivedResponseParams : base::MessageData {
|
||||
int code;
|
||||
Map headers;
|
||||
};
|
||||
|
||||
struct ReceivedDataParams : base::MessageData {
|
||||
base::ByteBuffer bb;
|
||||
};
|
||||
|
||||
struct ErrorParams : base::MessageData {
|
||||
char szError[80];
|
||||
};
|
||||
|
||||
} // namespace wi
|
||||
|
||||
@ -1,148 +1,151 @@
|
||||
#include "game/sdl/mac/machttprequest.h"
|
||||
#include "game/sdl/sysmessages.h"
|
||||
#include "base/thread.h"
|
||||
|
||||
@implementation ConnectionDelegate
|
||||
// Requests come in from the game thread; NS* api calls occur on main
|
||||
// thread.
|
||||
|
||||
- (id)initWithRequest:(wi::MacHttpRequest *)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 Mac
|
||||
// C++ implementation of HttpRequest interface for iPhone
|
||||
|
||||
namespace wi {
|
||||
|
||||
MacHttpRequest::MacHttpRequest(HttpResponseHandler *handler) :
|
||||
handler_(handler), delegate_(nil) {
|
||||
MacHttpRequest::MacHttpRequest(HttpResponseHandler *handler) : handler_(handler) {
|
||||
}
|
||||
|
||||
MacHttpRequest::~MacHttpRequest() {
|
||||
}
|
||||
|
||||
void MacHttpRequest::Submit() {
|
||||
delegate_ = [[ConnectionDelegate alloc] initWithRequest:this];
|
||||
[delegate_ submit];
|
||||
}
|
||||
|
||||
void MacHttpRequest::Release() {
|
||||
[delegate_ cancel];
|
||||
delegate_ = nil;
|
||||
Dispose();
|
||||
void MacHttpRequest::Dispose() {
|
||||
// Called on game thread
|
||||
thread_.Clear(this);
|
||||
MessageHandler::Dispose();
|
||||
}
|
||||
|
||||
NSURLRequest *MacHttpRequest::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 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];
|
||||
}
|
||||
}
|
||||
|
||||
// Set timeout
|
||||
[req setTimeoutInterval:timeout_];
|
||||
// Done
|
||||
[pool release];
|
||||
return req;
|
||||
}
|
||||
|
||||
// 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];
|
||||
}
|
||||
void MacHttpRequest::OnMessage(base::Message *pmsg) {
|
||||
switch (pmsg->id) {
|
||||
case kidmReceivedResponse:
|
||||
{
|
||||
ReceivedResponseParams *pparams =
|
||||
(ReceivedResponseParams *)pmsg->data;
|
||||
handler_->OnReceivedResponse(this, pparams->code,
|
||||
&pparams->headers);
|
||||
delete pparams;
|
||||
}
|
||||
break;
|
||||
|
||||
// Done
|
||||
return req;
|
||||
case kidmReceivedData:
|
||||
{
|
||||
ReceivedDataParams *pparams =
|
||||
(ReceivedDataParams *)pmsg->data;
|
||||
handler_->OnReceivedData(this, &pparams->bb);
|
||||
delete pparams;
|
||||
}
|
||||
break;
|
||||
|
||||
case kidmFinishedLoading:
|
||||
handler_->OnFinishedLoading(this);
|
||||
break;
|
||||
|
||||
case kidmError:
|
||||
{
|
||||
ErrorParams *pparams = (ErrorParams *)pmsg->data;
|
||||
handler_->OnError(this, pparams->szError);
|
||||
delete pparams;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MacHttpRequest::OnReceivedResponse(NSHTTPURLResponse *resp) {
|
||||
Map headers;
|
||||
// Called on main thread. Populate ReceivedResponseParams
|
||||
ReceivedResponseParams *pparams = new ReceivedResponseParams;
|
||||
NSDictionary *dict = [resp allHeaderFields];
|
||||
for (NSString *k in dict) {
|
||||
NSString *v = [dict objectForKey:k];
|
||||
headers.SetValue(
|
||||
pparams->headers.SetValue(
|
||||
[k cStringUsingEncoding:[NSString defaultCStringEncoding]],
|
||||
[v cStringUsingEncoding:[NSString defaultCStringEncoding]]);
|
||||
}
|
||||
int code = [resp statusCode];
|
||||
handler_->OnReceivedResponse(this, code, &headers);
|
||||
pparams->code = (int)[resp statusCode];
|
||||
|
||||
// Post this to the game thread
|
||||
thread_.Post(kidmReceivedResponse, this, pparams);
|
||||
}
|
||||
|
||||
void MacHttpRequest::OnReceivedData(NSData *data) {
|
||||
base::ByteBuffer bb;
|
||||
bb.WriteBytes((const byte *)[data bytes], [data length]);
|
||||
handler_->OnReceivedData(this, &bb);
|
||||
// Called on main thread. Populate ReceivedDataParams
|
||||
ReceivedDataParams *pparams = new ReceivedDataParams;
|
||||
pparams->bb.WriteBytes((const byte *)[data bytes], (int)[data length]);
|
||||
|
||||
// Post this to the game thread
|
||||
thread_.Post(kidmReceivedData, this, pparams);
|
||||
}
|
||||
|
||||
void MacHttpRequest::OnFinishedLoading() {
|
||||
handler_->OnFinishedLoading(this);
|
||||
// Called on main thread. Post this to game thread.
|
||||
thread_.Post(kidmFinishedLoading, this);
|
||||
}
|
||||
|
||||
void MacHttpRequest::OnError(NSError *error) {
|
||||
// Called on main thread. Populate ErrorParams. Use
|
||||
// localizedDescription. Note there is also localizedFailureReason;
|
||||
// not sure which is better at the moment
|
||||
const char *psz = [[error localizedDescription] cStringUsingEncoding:
|
||||
[NSString defaultCStringEncoding]];
|
||||
char szError[80];
|
||||
strncpyz(szError, psz, sizeof(szError));
|
||||
handler_->OnError(this, szError);
|
||||
ErrorParams *pparams = new ErrorParams;
|
||||
strncpyz(pparams->szError, psz, sizeof(pparams->szError));
|
||||
|
||||
// Called on main thread. Post this to game thread.
|
||||
thread_.Post(kidmError, this, pparams);
|
||||
}
|
||||
|
||||
} // namespace wi
|
||||
|
||||
@ -8,10 +8,15 @@ namespace wi {
|
||||
|
||||
class MacHttpService : public HttpService {
|
||||
public:
|
||||
MacHttpService();
|
||||
|
||||
// HttpService methods
|
||||
virtual HttpRequest *NewRequest(HttpResponseHandler *phandler);
|
||||
virtual void SubmitRequest(HttpRequest *preq);
|
||||
virtual void ReleaseRequest(HttpRequest *preq);
|
||||
|
||||
private:
|
||||
void *delegate_;
|
||||
};
|
||||
|
||||
} // namespace wi
|
||||
|
||||
@ -1,20 +1,156 @@
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include "game/sdl/mac/machttpservice.h"
|
||||
#include "game/sdl/mac/machttprequest.h"
|
||||
#include "game/sdl/sysmessages.h"
|
||||
#include "game/sdl/hosthelpers.h"
|
||||
#include <map>
|
||||
|
||||
typedef std::map<NSURLSessionDataTask *, wi::MacHttpRequest *> TaskMap;
|
||||
|
||||
// HttpService calls come in on the game thread. In order to use
|
||||
// iPhone NS* Http apis, requests execute on the main thread.
|
||||
|
||||
@interface MacHttpRequestWrapper : NSObject {
|
||||
wi::MacHttpRequest *req_;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation MacHttpRequestWrapper
|
||||
- (id)initWithRequest:(wi::MacHttpRequest *)req {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
req_ = req;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (wi::MacHttpRequest *)request {
|
||||
return req_;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface SessionDelegate : NSObject<NSURLSessionDataDelegate> {
|
||||
NSURLSession *session_;
|
||||
TaskMap taskmap_;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation SessionDelegate
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
session_ = nil;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[session_ release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)cancel:(MacHttpRequestWrapper *)wrapper {
|
||||
wi::MacHttpRequest *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:(MacHttpRequestWrapper *)wrapper {
|
||||
if (session_ == nil) {
|
||||
session_ = [NSURLSession
|
||||
sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
|
||||
delegate:self
|
||||
delegateQueue:[NSOperationQueue mainQueue]];
|
||||
}
|
||||
wi::MacHttpRequest *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 {
|
||||
|
||||
MacHttpService::MacHttpService() {
|
||||
delegate_ = NULL;
|
||||
}
|
||||
|
||||
HttpRequest *MacHttpService::NewRequest(HttpResponseHandler *phandler) {
|
||||
return new MacHttpRequest(phandler);
|
||||
}
|
||||
|
||||
void MacHttpService::SubmitRequest(HttpRequest *preq) {
|
||||
MacHttpRequest *preqT = (MacHttpRequest *)preq;
|
||||
preqT->Submit();
|
||||
if (delegate_ == NULL) {
|
||||
delegate_ = [[SessionDelegate alloc] init];
|
||||
}
|
||||
MacHttpRequestWrapper *wrapper = [[MacHttpRequestWrapper alloc]
|
||||
initWithRequest:(MacHttpRequest *)preq];
|
||||
SessionDelegate *delegate = (SessionDelegate *)delegate_;
|
||||
[delegate performSelectorOnMainThread:@selector(submit:)
|
||||
withObject:wrapper waitUntilDone: NO];
|
||||
}
|
||||
|
||||
void MacHttpService::ReleaseRequest(HttpRequest *preq) {
|
||||
MacHttpRequest *preqT = (MacHttpRequest *)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
|
||||
MacHttpRequest *req = (MacHttpRequest *)preq;
|
||||
if (!HostHelpers::IsExiting()) {
|
||||
MacHttpRequestWrapper *wrapper = [[MacHttpRequestWrapper alloc]
|
||||
initWithRequest:req];
|
||||
SessionDelegate *delegate = (SessionDelegate *)delegate_;
|
||||
[delegate performSelectorOnMainThread:@selector(cancel:)
|
||||
withObject:wrapper waitUntilDone: YES];
|
||||
}
|
||||
req->Dispose();
|
||||
}
|
||||
|
||||
} // namespace wi
|
||||
|
||||
Loading…
Reference in New Issue
Block a user