diff --git a/game/sdl/android/jni/curl b/game/sdl/android/jni/curl new file mode 120000 index 0000000..9c44381 --- /dev/null +++ b/game/sdl/android/jni/curl @@ -0,0 +1 @@ +../../curl \ No newline at end of file diff --git a/game/sdl/sdlhttprequest.cpp b/game/sdl/sdlhttprequest.cpp index 94132d2..8404d7c 100644 --- a/game/sdl/sdlhttprequest.cpp +++ b/game/sdl/sdlhttprequest.cpp @@ -2,178 +2,199 @@ #include "game/sdl/hosthelpers.h" #include "base/thread.h" -// C++ implementation of HttpRequest interface for SDL +#include +#include +// Curl implementation of HttpRequest for SDL namespace wi { -#if 0 // TODO(darrinm) -SdlHttpRequest::SdlHttpRequest(HttpResponseHandler *handler) : - handler_(handler), delegate_(nil) { -#else -SdlHttpRequest::SdlHttpRequest(HttpResponseHandler *handler) { - LOG() << "SdlHttpRequest constructor not implemented yet"; -#endif +size_t SdlHttpRequest::WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) +{ + LOG() << "WriteMemoryCallback"; + + size_t realsize = size * nmemb; + struct MemoryStruct *mem = (struct MemoryStruct *)userp; + + if (mem->what == CURLOPT_WRITEDATA) + { + LOG() << "Data size: " << realsize; + ReceivedDataParams *pparams = new ReceivedDataParams; + pparams->bb.WriteBytes((const byte*)contents,realsize); + thread_.Post(kidmReceivedData, this, pparams); + } + else if (mem->what == CURLOPT_HEADERDATA) + { + LOG() << "Header"; + ReceivedResponseParams *pparams = new ReceivedResponseParams; + pparams->code = 200; + thread_.Post(kidmReceivedResponse, this, pparams); + } + + mem->memory = (char*)realloc(mem->memory, mem->size + realsize + 1); + if(mem->memory == NULL) { + /* out of memory! */ + LOG() << "not enough memory (realloc returned NULL)"; + return 0; + } + + memcpy(&(mem->memory[mem->size]), contents, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + + return realsize; +} + +SdlHttpRequest::SdlHttpRequest(HttpResponseHandler *handler) : handler_(handler) { + LOG() << "SdlHttpRequest::SdlHttpRequest"; } SdlHttpRequest::~SdlHttpRequest() { } +void *SdlHttpRequest::doAccess(void *arg) +{ + + CURL *curl_handle; + CURLcode res; + /* init the curl session */ + curl_handle = curl_easy_init(); + + struct MemoryStruct chunk; + struct MemoryStruct chunkHeader; + + chunk.cls = this; + chunk.memory = (char*)malloc(1); /* will be grown as needed by the realloc above */ + chunk.size = 0; /* no data at this point */ + chunk.what = CURLOPT_WRITEDATA; + + chunkHeader.cls = this; + chunkHeader.memory = (char*)malloc(1); /* will be grown as needed by the realloc above */ + chunkHeader.what = CURLOPT_HEADERDATA; + chunkHeader.size = 0; /* no data at this point */ + + curl_global_init(CURL_GLOBAL_ALL); + + /* specify URL to get */ + curl_easy_setopt(curl_handle, CURLOPT_URL, url_.c_str()); + + /* send all data to this function */ + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, SdlHttpRequest::WriteMemoryCallback_helper); + + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, (void *)&chunkHeader); + + curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT,10); + + /* some servers don't like requests that are made without a user-agent + field, so we provide one */ + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L); + + res = curl_easy_perform(curl_handle); + + long http_code = 0; + + /* check for errors */ + if(res != CURLE_OK) { + LOG() << "curl_easy_perform() failed: " << curl_easy_strerror(res); + ErrorParams *pparams = new ErrorParams; + strncpyz(pparams->szError, curl_easy_strerror(res), sizeof(pparams->szError)); + + // Called on main thread. Post this to game thread. + thread_.Post(kidmError, this, pparams); + } + else { + curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_code); + + LOG() << "Resp code = " << http_code; + + //base::ByteBuffer bb; + //bb.WriteBytes((const byte*)chunkHeader.memory,chunkHeader.size); + + //ReceivedResponseParams *pparams = new ReceivedResponseParams; + //pparams->code = http_code; + //thread_.Post(kidmReceivedResponse, this, pparams); + + if (http_code == 200) + { + LOG() << "Data length: " << chunk.size; + //ReceivedDataParams *pparams = new ReceivedDataParams; + //pparams->bb.WriteBytes((const byte*)chunk.memory,chunk.size); + //thread_.Post(kidmReceivedData, this, pparams); + } + + LOG() << "%lu bytes retrieved" << (long)chunk.size; + LOG() << "got: " << chunk.memory; + } + + /* cleanup curl stuff */ + curl_easy_cleanup(curl_handle); + + if(chunk.memory) + free(chunk.memory); + + if(chunkHeader.memory) + free(chunkHeader.memory); + + thread_.Post(kidmFinishedLoading, this); + + return 0; +} + void SdlHttpRequest::Submit() { -#if 0 - delegate_ = [[ConnectionDelegate alloc] initWithRequest:this]; - [delegate_ performSelectorOnMainThread:@selector(submit) - withObject:nil waitUntilDone: NO]; -#else - LOG() << "SdlHttpRequest::Submit not implemented yet"; -#endif + LOG() << "SdlHttpRequest::SdlHttpRequest, URL = " << url_.c_str(); + + pthread_t pth; // this is our thread identifier + pthread_create(&pth,NULL, SdlHttpRequest::doAccess_helper,this); + } void SdlHttpRequest::Release() { -#if 0 - // 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 (!Sdl::IsExiting()) { - [delegate_ performSelectorOnMainThread:@selector(cancel) - withObject:nil waitUntilDone: YES]; - [delegate_ release]; - } - delegate_ = nil; - thread_.Clear(this); - Dispose(); -#else - LOG() << "SdlHttpRequest::Release not implemented yet"; -#endif + LOG() << "SdlHttpRequest::Release"; + // TODO: need to abort thread.. + thread_.Clear(this); } -#if 0 // TODO(darrinm) -NSURLRequest *SdlHttpRequest::CreateNSURLRequest() { - 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 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 - [pool release]; -} -#endif - void SdlHttpRequest::OnMessage(base::Message *pmsg) { -#if 0 // TODO(darrinm) - switch (pmsg->id) { - case kidmReceivedResponse: - { - ReceivedResponseParams *pparams = - (ReceivedResponseParams *)pmsg->data; - handler_->OnReceivedResponse(this, pparams->code, - &pparams->headers); - delete pparams; - } + LOG() << "SdlHttpRequest::OnMessage"; + + switch (pmsg->id) { + case kidmReceivedResponse: + { + ReceivedResponseParams *pparams = + (ReceivedResponseParams *)pmsg->data; + handler_->OnReceivedResponse(this, pparams->code, + &pparams->headers); + delete pparams; break; + } - case kidmReceivedData: - { - ReceivedDataParams *pparams = - (ReceivedDataParams *)pmsg->data; - handler_->OnReceivedData(this, &pparams->bb); - delete pparams; - } + case kidmReceivedData: + { + ReceivedDataParams *pparams = + (ReceivedDataParams *)pmsg->data; + handler_->OnReceivedData(this, &pparams->bb); + delete pparams; break; + } - case kidmFinishedLoading: - handler_->OnFinishedLoading(this); + case kidmFinishedLoading: + handler_->OnFinishedLoading(this); + break; + + case kidmError: + { + ErrorParams *pparams = (ErrorParams *)pmsg->data; + handler_->OnError(this, pparams->szError); + delete pparams; break; + } - case kidmError: - { - ErrorParams *pparams = (ErrorParams *)pmsg->data; - handler_->OnError(this, pparams->szError); - delete pparams; - } - break; - } -#endif + } // switch } -#if 0 // TODO(darrinm) -void SdlHttpRequest::OnReceivedResponse(NSHTTPURLResponse *resp) { - // Called on main thread. Populate ReceivedResponseParams - ReceivedResponseParams *pparams = new ReceivedResponseParams; - NSDictionary *dict = [resp allHeaderFields]; - for (NSString *k in dict) { - NSString *v = [dict objectForKey:k]; - pparams->headers.SetValue( - [k cStringUsingEncoding:[NSString defaultCStringEncoding]], - [v cStringUsingEncoding:[NSString defaultCStringEncoding]]); - } - pparams->code = [resp statusCode]; - - // Post this to the game thread - thread_.Post(kidmReceivedResponse, this, pparams); -} - -void SdlHttpRequest::OnReceivedData(NSData *data) { - // Called on main thread. Populate ReceivedDataParams - ReceivedDataParams *pparams = new ReceivedDataParams; - pparams->bb.WriteBytes((const byte *)[data bytes], [data length]); - - // Post this to the game thread - thread_.Post(kidmReceivedData, this, pparams); -} - -void SdlHttpRequest::OnFinishedLoading() { - // Called on main thread. Post this to game thread. - thread_.Post(kidmFinishedLoading, this); -} - -void SdlHttpRequest::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]]; - 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); -} -#endif - } // namespace wi diff --git a/game/sdl/sdlhttprequest.h b/game/sdl/sdlhttprequest.h index 45bdede..fda26ec 100644 --- a/game/sdl/sdlhttprequest.h +++ b/game/sdl/sdlhttprequest.h @@ -8,40 +8,53 @@ namespace wi { +class SdlHttpRequest; + +struct MemoryStruct { + SdlHttpRequest *cls; + int what; + char *memory; + size_t size; +}; + + class SdlHttpRequest : public HttpRequest, base::MessageHandler { public: - SdlHttpRequest(HttpResponseHandler *phandler); - ~SdlHttpRequest(); + SdlHttpRequest(HttpResponseHandler *phandler); + ~SdlHttpRequest(); - void Submit(); - void Release(); -// TODO(darrinm): -// NSURLRequest *CreateNSURLRequest(); -// void OnReceivedResponse(NSHTTPURLResponse *resp); -// void OnReceivedData(NSData *data); - void OnFinishedLoading(); -// TODO(darrinm): -// void OnError(NSError *error); + void Submit(); + void Release(); + void OnFinishedLoading(); private: - virtual void OnMessage(base::Message *pmsg); - HttpResponseHandler *handler_; -// TODO(darrinm): -// ConnectionDelegate *delegate_; + + void *doAccess(void *arg); + virtual void OnMessage(base::Message *pmsg); + size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp); + + static void *doAccess_helper(void *context) { + return ((SdlHttpRequest *)context)->doAccess(0); + } + + static size_t WriteMemoryCallback_helper(void *contents, size_t size, size_t nmemb, void *userp) + { + return (((MemoryStruct *)userp)->cls)->WriteMemoryCallback(contents,size,nmemb,userp); + } }; struct ReceivedResponseParams : base::MessageData { - int code; - Map headers; + int code; + Map headers; }; struct ReceivedDataParams : base::MessageData { - base::ByteBuffer bb; + base::ByteBuffer bb; }; struct ErrorParams : base::MessageData { - char szError[80]; + char szError[80]; }; } // namespace wi