Improvements for SdlHttpRequest

- When “Downloaded X of Y bytes…” Y is no longer -1.
- HTTP posting now works (e.g. uploading sync errors).
This commit is contained in:
Nathan Fulton 2016-08-25 15:40:25 -04:00
parent 31adbf1da0
commit bdbe1647dd
2 changed files with 120 additions and 128 deletions

View File

@ -3,31 +3,60 @@
#include "base/thread.h"
#include <curl/curl.h>
#include <pthread.h>
#include <algorithm>
// Curl implementation of HttpRequest for SDL
namespace wi {
SdlHttpRequest::SdlHttpRequest(HttpResponseHandler *handler) : handler_(handler) {
}
SdlHttpRequest::~SdlHttpRequest() {
}
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);
pparams->bb.WriteBytes((const byte*)contents, (int)realsize);
thread_.Post(kidmReceivedData, this, pparams);
}
else if (mem->what == CURLOPT_HEADERDATA)
if (mem->what == CURLOPT_HEADERDATA)
{
LOG() << "Header";
ReceivedResponseParams *pparams = new ReceivedResponseParams;
pparams->code = 200;
thread_.Post(kidmReceivedResponse, this, pparams);
// Stuff for delimiting
size_t pos = 0;
std::string delimiter = ":";
std::string str((char *)contents);
std::string key;
std::string value;
// Split the HTTP header into a key and value
if ((pos = str.find(delimiter)) != std::string::npos) {
key = str.substr(0, pos);
str.erase(0, pos + delimiter.length());
}
value = str;
// Remove newlines and spaces that sneak in sometimes
value.erase(std::remove(value.begin(), value.end(), '\n'), value.end());
value.erase(std::remove(value.begin(), value.end(), '\r'), value.end());
value.erase(std::remove(value.begin(), value.end(), ' '), value.end());
// handler_ doesn't read the Content-Length header properly when
// other header(s) are posted. To be discovered why this happens.
// The game only uses Content-Length so only post it for now.
if (key == "Content-Length") {
pparams->headers.SetValue(key.c_str(), value.c_str());
thread_.Post(kidmReceivedResponse, this, pparams);
}
}
mem->memory = (char*)realloc(mem->memory, mem->size + realsize + 1);
@ -44,157 +73,119 @@ size_t SdlHttpRequest::WriteMemoryCallback(void *contents, size_t size, size_t n
return realsize;
}
SdlHttpRequest::SdlHttpRequest(HttpResponseHandler *handler) : handler_(handler) {
LOG() << "SdlHttpRequest::SdlHttpRequest";
}
void SdlHttpRequest::CreateCurlRequest() {
/* init the curl session */
curl_handle_ = curl_easy_init();
SdlHttpRequest::~SdlHttpRequest() {
}
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;
void *SdlHttpRequest::doAccess(void *arg)
{
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 *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);
curl_global_init(CURL_GLOBAL_ALL);
/* specify URL to get */
curl_easy_setopt(curl_handle, CURLOPT_URL, url_.c_str());
curl_easy_setopt(curl_handle_, CURLOPT_URL, url_.c_str());
/* specify body to post */
if (pbb_ != NULL) {
int cb;
void *pv = pbb_->Strip(&cb);
curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDS, pv);
}
/* send all data to this function */
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, SdlHttpRequest::WriteMemoryCallback_helper);
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);
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_HEADERDATA, (void *)&chunkHeader_);
curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT,10);
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_USERAGENT, "libcurl-agent/1.0");
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl_handle_, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl_handle_, CURLOPT_SSL_VERIFYHOST, 0L);
}
res = curl_easy_perform(curl_handle);
void SdlHttpRequest::SubmitCurlRequest(void *pv) {
SdlHttpRequest *req = (SdlHttpRequest *)pv;
long http_code = 0;
CURLcode res = curl_easy_perform(req->curl_handle_);
/* check for errors */
// 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;
strncpyz(pparams->szError, curl_easy_strerror(res), CURL_ERROR_SIZE);
req->thread().Post(kidmError, req, pparams);
} else {
req->thread().Post(kidmFinishedLoading, req);
}
}
void SdlHttpRequest::Submit() {
LOG() << "SdlHttpRequest::SdlHttpRequest, URL = " << url_.c_str();
pthread_t pth; // this is our thread identifier
pthread_create(&pth,NULL, SdlHttpRequest::doAccess_helper,this);
this->CreateCurlRequest();
// SdlHttpRequest::Submit() is called on the game thread
// so spin off a new thread to run the request on.
base::Thread *thread = new base::Thread();
thread->Start(SubmitCurlRequest, this);
}
void SdlHttpRequest::Release() {
LOG() << "SdlHttpRequest::Release";
// TODO: need to abort thread..
curl_easy_cleanup(curl_handle_);
if (chunk_.memory)
free(chunk_.memory);
if (chunkHeader_.memory)
free(chunkHeader_.memory);
thread_.Clear(this);
}
void SdlHttpRequest::OnMessage(base::Message *pmsg) {
LOG() << "SdlHttpRequest::OnMessage";
switch (pmsg->id) {
case kidmReceivedResponse:
{
ReceivedResponseParams *pparams =
(ReceivedResponseParams *)pmsg->data;
handler_->OnReceivedResponse(this, pparams->code,
&pparams->headers);
delete pparams;
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);
break;
case kidmError:
{
ErrorParams *pparams = (ErrorParams *)pmsg->data;
handler_->OnError(this, pparams->szError);
delete pparams;
case kidmFinishedLoading:
handler_->OnFinishedLoading(this);
break;
}
} // switch
case kidmError:
{
ErrorParams *pparams = (ErrorParams *)pmsg->data;
handler_->OnError(this, pparams->szError);
delete pparams;
}
break;
}
}
} // namespace wi

View File

@ -6,6 +6,8 @@
#include "base/thread.h"
#include "base/bytebuffer.h"
#include <curl/curl.h>
namespace wi {
class SdlHttpRequest;
@ -17,7 +19,6 @@ struct MemoryStruct {
size_t size;
};
class SdlHttpRequest : public HttpRequest, base::MessageHandler {
public:
SdlHttpRequest(HttpResponseHandler *phandler);
@ -29,15 +30,15 @@ public:
private:
HttpResponseHandler *handler_;
CURL *curl_handle_;
struct MemoryStruct chunk_;
struct MemoryStruct chunkHeader_;
void *doAccess(void *arg);
virtual void OnMessage(base::Message *pmsg);
static void SubmitCurlRequest(void *pv);
void CreateCurlRequest();
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);