#include <glwebtools/internal/glwebtools_default_config.h>
#if GLWEBTOOLS_USE_CURL
#include <glwebtools/internal/glwebtools_urlconnection.h>
#include <glwebtools/internal/glwebtools_urlrequest.h>
#include <glwebtools/internal/glwebtools_urlresponse.h>
#include <glwebtools/internal/glwebtools_macro.h>
#include <glwebtools/os/glwebtools_thread.h>
#include <glwebtools/os/glwebtools_memorybarrier.h>
#include <glwebtools/os/glwebtools_taskgroup.h>
#include <glwebtools/os/glwebtools_iostream.h>
#include <glwebtools/glwebtools_error.h>
#include <glwebtools/sse/glwebtools_serversideeventstreamparser.h>
#if GLWEBTOOLS_ENABLE_DEBUGGER_MODULE
#include <glwebtools/debugger/glwebtools_debugger_module.h>
#endif // GLWEBTOOLS_ENABLE_DEBUGGER_MODULE
#if (GLWEBTOOLS_ENABLE_DEBUGGER_MODULE|| GLWEBTOOLS_ENABLE_DUMP_REQUEST)
#include <glwebtools/serialization/glwebtools_jsonwriter.h>
#include <glwebtools/os/glwebtools_time.h>
#endif // (GLWEBTOOLS_ENABLE_DEBUGGER_MODULE|| GLWEBTOOLS_ENABLE_DUMP_REQUEST)
#include <glwebtools/os/glwebtools_systemclock.h>
#include <sstream>
#include <curl/curl.h>
#include <list>
#include <string>
#include <cstring>
//! \cond internal
namespace
{
struct HTTPHeaderField
{
static bool IsValidChar(char nChar)
{
//http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
return (nChar > 31 && nChar != 127 /* && nChar < 256*/);
}
static bool IsNotValidChar(char nChar)
{
return !IsValidChar(nChar);
}
static bool IsSpaceOrColumnOrNotValidChar(char nChar)
{
return nChar == ' ' || nChar == ':' || nChar == ';' || nChar == '\\' || !IsValidChar(nChar);
}
static bool IsNotSpaceAndNotColumnAndValidChar(char nChar)
{
return !IsSpaceOrColumnOrNotValidChar(nChar);
}
};
}
//! \endcond
namespace glwebtools
{
//! \cond internal
namespace
{
struct ErrorHandler
{
static glwebtools::Error Translate(CURLcode error)
{
switch (error)
{
case CURLE_OK:
return glwebtools::E_SUCCESS;
case CURLE_UNSUPPORTED_PROTOCOL:
return glwebtools::E_UNSUPPORTED_PROTOCOL;
case CURLE_URL_MALFORMAT:
return glwebtools::E_URL_MALFORMAT;
case CURLE_TOO_MANY_REDIRECTS:
return glwebtools::E_TOO_MANY_REDIRECTS;
case CURLE_COULDNT_RESOLVE_HOST:
return glwebtools::E_COULDNT_RESOLVE_HOST;
case CURLE_COULDNT_CONNECT:
return glwebtools::E_COULDNT_CONNECT;
case CURLE_OPERATION_TIMEDOUT:
return glwebtools::E_TIMEOUT;
case CURLE_SSL_CONNECT_ERROR:
return glwebtools::E_SSL_CONNECT_ERROR;
case CURLE_ABORTED_BY_CALLBACK:
return glwebtools::E_CONNECTION_CANCELLED;
};
return glwebtools::E_UNKNOWN;
}
};
}
//! \endcond
typedef std::list<glwebtools::String, glwebtools::SAllocator< glwebtools::String > > StringList;
UrlConnection::CreationSettings::CreationSettings()
:m_maxConnections(GLWEBTOOLS_URL_CONNECTION_MAX_CONNECTIONS)
,m_follow302(GLWEBTOOLS_URL_CONNECTION_FOLLOW_302)
,m_connectTimeout(GLWEBTOOLS_URL_CONNECTION_CONNECT_TIMEOUT)
,m_timeout(GLWEBTOOLS_URL_CONNECTION_TIMEOUT)
,m_lowSpeed(0)
,m_lowSpeedTimeout(0)
,m_taskGroupId(GLWEBTOOLS_DEFAULT_TASKGROUP_ID)
,m_threadPriority(GLWEBTOOLS_URL_CONNECTION_THREAD_PRIORITY)
,m_taskStartTimeout(GLWEBTOOLS_URL_CONNECTION_TASK_TIMEOUT)
,m_enableAcceptEncoding(GLWEBTOOLS_URL_CONNECTION_ENABLE_ACCEPT_ENCODING)
,m_acceptEncoding(GLWEBTOOLS_URL_CONNECTION_ACCEPT_ENCODING)
,m_userAgent("")
,m_supportServerSideEvent(false)
,m_peerCAFile("")
,m_useDataPacketQueue(false)
,m_dumpRequestForDebug(GLWEBTOOLS_URL_CONNECTION_DUMP_REQUEST)
{
#ifdef GLWEBTOOLS_URL_CONNECTION_LOW_SPEED
m_lowSpeed = GLWEBTOOLS_URL_CONNECTION_LOW_SPEED;
#endif
#ifdef GLWEBTOOLS_URL_CONNECTION_LOW_SPEED_TIMEOUT
m_lowSpeedTimeout = GLWEBTOOLS_URL_CONNECTION_LOW_SPEED_TIMEOUT;
#endif
}
UrlConnection::CreationSettings::CreationSettings(unsigned int maxConnections, bool failOnReponse400andHigher, bool follow302, unsigned int connectTimeout, glwebtools::Thread::Priority /*threadPriority*/, bool enableAcceptEncoding, const char* acceptEncoding)
:m_maxConnections(maxConnections)
,m_follow302(follow302)
,m_connectTimeout(connectTimeout)
,m_timeout(GLWEBTOOLS_URL_CONNECTION_TIMEOUT)
,m_lowSpeed(0)
,m_lowSpeedTimeout(0)
,m_taskGroupId(GLWEBTOOLS_DEFAULT_TASKGROUP_ID)
,m_threadPriority(GLWEBTOOLS_URL_CONNECTION_THREAD_PRIORITY)
,m_taskStartTimeout(GLWEBTOOLS_URL_CONNECTION_TASK_TIMEOUT)
,m_enableAcceptEncoding(enableAcceptEncoding)
,m_acceptEncoding(acceptEncoding ? acceptEncoding : "")
,m_userAgent("")
,m_supportServerSideEvent(false)
,m_peerCAFile("")
,m_useDataPacketQueue(false)
,m_dumpRequestForDebug(GLWEBTOOLS_URL_CONNECTION_DUMP_REQUEST)
{
#ifdef GLWEBTOOLS_URL_CONNECTION_LOW_SPEED
m_lowSpeed = GLWEBTOOLS_URL_CONNECTION_LOW_SPEED;
#endif
#ifdef GLWEBTOOLS_URL_CONNECTION_LOW_SPEED_TIMEOUT
m_lowSpeedTimeout = GLWEBTOOLS_URL_CONNECTION_LOW_SPEED_TIMEOUT;
#endif
}
UrlConnection::CreationSettings::CreationSettings(unsigned int maxConnections, bool failOnReponse400andHigher, bool follow302, unsigned int connectTimeout, const std::string& taskGroupId, glwebtools::Thread::Priority /*threadPriority*/, bool enableAcceptEncoding, const char* acceptEncoding)
:m_maxConnections(maxConnections)
,m_follow302(follow302)
,m_connectTimeout(connectTimeout)
,m_timeout(GLWEBTOOLS_URL_CONNECTION_TIMEOUT)
,m_lowSpeed(0)
,m_lowSpeedTimeout(0)
,m_taskGroupId(taskGroupId)
,m_threadPriority(GLWEBTOOLS_URL_CONNECTION_THREAD_PRIORITY)
,m_taskStartTimeout(GLWEBTOOLS_URL_CONNECTION_TASK_TIMEOUT)
,m_enableAcceptEncoding(enableAcceptEncoding)
,m_acceptEncoding(acceptEncoding ? acceptEncoding : "")
,m_userAgent("")
,m_supportServerSideEvent(false)
,m_peerCAFile("")
,m_useDataPacketQueue(false)
,m_dumpRequestForDebug(GLWEBTOOLS_URL_CONNECTION_DUMP_REQUEST)
{
#ifdef GLWEBTOOLS_URL_CONNECTION_LOW_SPEED
m_lowSpeed = GLWEBTOOLS_URL_CONNECTION_LOW_SPEED;
#endif
#ifdef GLWEBTOOLS_URL_CONNECTION_LOW_SPEED_TIMEOUT
m_lowSpeedTimeout = GLWEBTOOLS_URL_CONNECTION_LOW_SPEED_TIMEOUT;
#endif
}
struct UrlConnection_CurlCB
{
UrlConnection_CurlCB()
:m_responseData(0)
,m_outFile(0)
,m_needCancel(false)
,m_bytesCount(0)
,m_timeoutInMilliSeconds(GLWEBTOOLS_URL_CONNECTION_TIMEOUT*1000)
,m_lastChangedTime(SystemClock::GetMilliseconds())
,m_useQueue(false)
{
}
virtual ~UrlConnection_CurlCB()
{
Reset();
}
static int HeaderWriteCB( void *ptr, size_t size, size_t nmemb, void *userdata)
{
if(userdata)
{
UrlConnection_CurlCB* curlCB = static_cast<UrlConnection_CurlCB*>(userdata);
return curlCB->HeaderWrite(ptr, size*nmemb);
}
return 0;
}
static int DataWriteCB( char *ptr, size_t size, size_t nmemb, void *userdata)
{
if(userdata)
{
UrlConnection_CurlCB* curlCB = static_cast<UrlConnection_CurlCB*>(userdata);
return curlCB->DataWrite(ptr, size*nmemb);
}
return 0;
}
static int ProgressCB(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow)
{
if(userdata)
{
UrlConnection_CurlCB* curlCB = static_cast<UrlConnection_CurlCB*>(userdata);
return curlCB->Progress(dltotal, dlnow, ultotal, ulnow);
}
return 0;
}
virtual int HeaderWrite(void *ptr, size_t size)
{
m_lastChangedTime = SystemClock::GetMilliseconds();
if(ptr && size > 0 && !m_needCancel)
{
char* str_start = static_cast<char*>(ptr);
size_t str_size = size;
// strip invalid characters at head
while (str_size > 0 && (!HTTPHeaderField::IsValidChar(*str_start) || *str_start == ' '))
{
++str_start;
--str_size;
}
// strip invalid characters at tail
while (str_size > 0 && (!HTTPHeaderField::IsValidChar(*(str_start+str_size-1)) || *(str_start+str_size-1) == ' '))
{
--str_size;
}
glwebtools::String str(str_start, str_size);
if(std::strncmp(str.c_str(), "HTTP", 4) == 0)
{
#ifdef GLWEBTOOLS_DEBUG
GLWEBTOOLS_LOG_VERBOSE("[%x] Clearing headers", this);
#endif
m_headerLines.clear(); //redirect, flush previous header
}
#ifdef GLWEBTOOLS_DEBUG
GLWEBTOOLS_LOG_VERBOSE("[%x] Header field => %s", this, str.c_str());
#endif
m_headerLines.push_back(str);
return static_cast<int>(size);
}
return 0;
}
virtual int DataWrite(void *ptr, size_t size)
{
m_lastChangedTime = SystemClock::GetMilliseconds();
if(m_needCancel)
return 0;
if(m_outFile)
{
size_t wsize = m_outFile->Write(ptr, size);
m_bytesCount += wsize;
if(wsize != size) //write operation didn't work as intended, cancel transfer
return 0;
return static_cast<int>(wsize);
}
else if(m_useQueue)
{
MutableData* data = GLWEBTOOLS_NEW MutableData();
if(data)
{
bool result = data->AppendData(ptr, size);
if(result)
{
m_bytesCount += size;
glwebtools::LockScope _ls(m_queueMutex);
m_packetQueue.push_back(data);
return static_cast<int>(size);
}
else
{
GLWEBTOOLS_LOG_MINOR_ERROR("Could not allocate %d bytes for %s", size, __FUNCTION__);
return 0;
}
}
else
{
GLWEBTOOLS_LOG_MINOR_ERROR("Could not create data packet for %s", __FUNCTION__);
return 0;
}
}
else if(m_responseData)
{
bool result = m_responseData->AppendData(ptr, size);
if(result)
{
m_bytesCount += size;
return static_cast<int>(size);
}
}
return 0;
}
int Progress(double dltotal, double dlnow, double ultotal, double ulnow)
{
if(m_needCancel)
return -1;
if ((m_timeoutInMilliSeconds > 0) && ((SystemClock::GetMilliseconds() - m_lastChangedTime) > (m_timeoutInMilliSeconds)))
{
return -1;
}
return 0;
}
virtual void Reset()
{
CloseFile();
m_headerLines.clear();
m_needCancel = false;
m_responseData = 0;
m_bytesCount = 0;
m_timeoutInMilliSeconds = GLWEBTOOLS_URL_CONNECTION_TIMEOUT * 1000;
m_lastChangedTime = SystemClock::GetMilliseconds();
{
glwebtools::LockScope _ls(m_queueMutex);
while(!m_packetQueue.empty())
{
glwebtools::MutableData* data = m_packetQueue.front();
GLWEBTOOLS_DELETE(data);
m_packetQueue.pop_front();
}
}
}
void SetResponseData(MutableData* responseData)
{
Reset();
m_responseData = responseData;
}
void SetTimeout(unsigned int timeout)
{
m_timeoutInMilliSeconds = timeout * 1000;
}
bool OpenFile(const std::string& filename)
{
if(filename.empty())
return false;
Reset();
m_outFile = GLWEBTOOLS_NEW IOStream();
if(!m_outFile)
{
GLWEBTOOLS_LOG_MINOR_ERROR("Could not allocate file %s for UrlRequest, request cancelled", filename.c_str());
return false;
}
if(!m_outFile->Open(filename.c_str(), glwebtools::IOStream::M_WRITE | glwebtools::IOStream::M_USER_SPACE))
{
GLWEBTOOLS_LOG_MINOR_ERROR("Could not open file %s for UrlRequest, request cancelled", filename.c_str());
GLWEBTOOLS_DELETE(m_outFile);
m_outFile = 0;
return false;
}
return true;
}
void CloseFile()
{
if (m_outFile)
{
m_outFile->Close();
GLWEBTOOLS_DELETE(m_outFile);
m_outFile = 0;
}
}
bool IsFileOpen() const
{
return m_outFile != 0;
}
size_t GetBytesCount() const
{
return m_bytesCount;
}
bool IsDataPacketQueueEmpty()
{
glwebtools::LockScope _ls(m_queueMutex);
return m_packetQueue.empty();
}
bool PopDataPacket(glwebtools::MutableData& outData)
{
glwebtools::LockScope _ls(m_queueMutex);
if(m_packetQueue.empty())
return false;
glwebtools::MutableData* data = m_packetQueue.front();
m_packetQueue.pop_front();
if(data)
{
outData.Swap(*data);
GLWEBTOOLS_DELETE(data);
return true;
}
return false;
}
bool m_needCancel;
StringList m_headerLines;
bool m_useQueue;
protected:
size_t m_bytesCount;
IOStream* m_outFile;
MutableData* m_responseData;
unsigned int m_timeoutInMilliSeconds;
u64 m_lastChangedTime;
glwebtools::Mutex m_queueMutex;
std::list<glwebtools::MutableData*, glwebtools::SAllocator<glwebtools::MutableData*> > m_packetQueue;
};
struct ServerSideEventListener_CurlCB : UrlConnection_CurlCB
{
ServerSideEventListener_CurlCB()
{
}
virtual int HeaderWrite(void *ptr, size_t size)
{
if(ptr && size > 0 && !m_needCancel)
{
char* str_start = static_cast<char*>(ptr);
size_t str_size = size;
// strip invalid characters at head
while (str_size > 0 && (!HTTPHeaderField::IsValidChar(*str_start) || *str_start == ' '))
{
++str_start;
--str_size;
}
// strip invalid characters at tail
while (str_size > 0 && (!HTTPHeaderField::IsValidChar(*(str_start+str_size-1)) || *(str_start+str_size-1) == ' '))
{
--str_size;
}
glwebtools::String str(str_start, str_size);
if(std::strncmp(str.c_str(), "HTTP", 4) == 0)
{
#ifdef GLWEBTOOLS_DEBUG
GLWEBTOOLS_LOG_VERBOSE("[%x] Clearing headers", this);
#endif
m_headerLines.clear(); //redirect, flush previous header
m_content_type.clear();
m_transfer_encoding.clear();
}
#ifdef GLWEBTOOLS_DEBUG
GLWEBTOOLS_LOG_VERBOSE("[%x] Header field => %s", this, str.c_str());
#endif
m_headerLines.push_back(str);
std::string name;
std::string value;
parse(str, name, value);
if (name == "Content-Type")
m_content_type = value;
if (name == "Transfer-Encoding")
m_transfer_encoding = value;
return static_cast<int>(size);
}
return 0;
}
virtual int DataWrite(void *ptr, size_t size)
{
if(m_needCancel)
return 0;
if(m_outFile)
{
size_t wsize = m_outFile->Write(ptr, size);
m_bytesCount += wsize;
if(wsize != size) //write operation didn't work as intended, cancel transfer
return 0;
return static_cast<int>(wsize);
}
else if(m_responseData)
{
if (m_content_type == "text/event-stream")
{
std::string data((char*)ptr, size);
if (GLWEBTOOLS_FAIL(m_serverSideEventparser.PushStream(data)))
{
GLWEBTOOLS_LOG_MAJOR_ERROR("[%x] Cannot push the stream of Server Side Events in the parser.", this);
return 0;
}
}
else
{
if (m_transfer_encoding == "chunked")
{
GLWEBTOOLS_LOG_MINOR_ERROR("[%x] Server Side Event cannot provide a chunked response.", this);
return 0;
}
bool result = m_responseData->AppendData(ptr, size);
if(result == false)
{
GLWEBTOOLS_LOG_MAJOR_ERROR("[%x] Cannot append data in the response.", this);
return 0;
}
}
m_bytesCount += size;
return static_cast<int>(size);
}
return 0;
}
virtual void Reset()
{
UrlConnection_CurlCB::Reset();
m_content_type.clear();
m_transfer_encoding.clear();
m_serverSideEventparser.Clear();
}
static void parse(const std::string& s, std::string& name, std::string& value)
{
// find end of name
std::string::const_iterator name_end = std::find_if(s.begin(), s.end(), HTTPHeaderField::IsSpaceOrColumnOrNotValidChar);
if (name_end == s.end())
return;
size_t pos = name_end - s.begin();
name = s.substr(0, pos);
// check ':' exists
pos = s.find(':', pos);
if (pos == std::string::npos)
return;
// find start of value
std::string::const_iterator value_begin = std::find_if(s.begin() + pos, s.end(), HTTPHeaderField::IsNotSpaceAndNotColumnAndValidChar);
if (value_begin == s.end())
return;
// find end of value
std::string::const_iterator value_end = std::find_if(value_begin, s.end(), HTTPHeaderField::IsSpaceOrColumnOrNotValidChar);
value = s.substr(value_begin - s.begin(), value_end - value_begin);
}
std::string m_content_type;
std::string m_transfer_encoding;
ServerSideEventStreamParser m_serverSideEventparser;
};
struct UrlConnection_Curl_ImplData
{
UrlConnection_Curl_ImplData()
{
m_curlHandle = curl_easy_init();
m_curlCB = 0;
m_taskId = 0;
}
virtual void Initialize()
{
m_curlCB = GLWEBTOOLS_NEW UrlConnection_CurlCB();
}
bool IsInitialized() const
{
return m_curlCB != 0;
}
virtual ~UrlConnection_Curl_ImplData()
{
if(m_curlHandle)
{
curl_easy_cleanup(m_curlHandle);
m_curlHandle = 0;
}
if(m_curlCB)
{
GLWEBTOOLS_DELETE(m_curlCB);
m_curlCB = 0;
}
}
virtual ServerSideEventStreamParser* GetServerSideEventParser()
{
return 0;
}
CURL* m_curlHandle;
#ifdef GLWEBTOOLS_DEBUG
char m_curlErrorBuffer[CURL_ERROR_SIZE];
#endif
UrlConnection_CurlCB* m_curlCB;
glwebtools::Task::Id m_taskId;// shouls be moved to UrlConnectionCore
};
struct ServerSideEventListener_Curl_ImplData : UrlConnection_Curl_ImplData
{
virtual void Initialize()
{
m_curlCB = GLWEBTOOLS_NEW ServerSideEventListener_CurlCB();
}
virtual ServerSideEventStreamParser* GetServerSideEventParser()
{
if (m_curlCB == 0)
return 0;
return &(static_cast<ServerSideEventListener_CurlCB*>(m_curlCB)->m_serverSideEventparser);
}
};
UrlConnectionCore::~UrlConnectionCore()
{
DestroyImplData();
if(m_urlResponse)
{
GLWEBTOOLS_DELETE(m_urlResponse);
m_urlResponse = 0;
}
}
bool UrlConnectionCore::CanDelete() const
{
LockScope _ls(m_mutex);
return (m_refCount < 1) && (m_state != UrlConnection::S_RUNNING) && (m_state != UrlConnection::S_WAITING_TO_RUN);
}
unsigned int UrlConnectionCore::Grab()
{
LockScope _ls(m_mutex);
return Managed::Grab();
}
unsigned int UrlConnectionCore::Drop()
{
LockScope _ls(m_mutex);
return Managed::Drop();
}
UrlConnection::State UrlConnectionCore::GetState() const
{
return m_state;
}
glwebtools::Error UrlConnectionCore::AttachRequest(UrlRequestCore* anUrlRequest)
{
LockScope _ls(m_mutex);
// Check Preconditions
if (m_state == UrlConnection::S_WAITING_TO_RUN || m_state == UrlConnection::S_RUNNING)
return glwebtools::E_INVALID_OPERATION;
if(!m_urlRequest)
{
anUrlRequest->Grab();
m_urlRequest = anUrlRequest;
if(m_urlResponse)
{
GLWEBTOOLS_DELETE(m_urlResponse);
m_urlResponse = 0;
}
m_state = UrlConnection::S_WAITING_TO_RUN;
return glwebtools::E_SUCCESS;
}
return glwebtools::E_INVALID_PARAMETER;
}
const UrlResponseCore* UrlConnectionCore::GetUrlResponse() const
{
LockScope _ls(m_mutex);
if(m_state != UrlConnection::S_RUNNING)
return m_urlResponse;
return 0;
}
glwebtools::Error UrlConnectionCore::StartRequest()
{
LockScope _ls(m_mutex);
if (!m_implData)
{
GLWEBTOOLS_RETURN_ERROR_ON_FAIL(CreateImplData());
}
UrlConnection_Curl_ImplData* curlImplData = static_cast<UrlConnection_Curl_ImplData*>(m_implData);
glwebtools::Error internError = glwebtools::E_SUCCESS;
if(m_state == UrlConnection::S_WAITING_TO_RUN && m_urlRequest)
{
if(!curlImplData->IsInitialized())
{
curlImplData->Initialize();
}
else
{
curlImplData->m_curlCB->Reset();
}
if(!m_urlResponse)
{
m_urlResponse = GLWEBTOOLS_NEW UrlResponseCore();
}
else //reset and reuse
{
m_urlResponse->m_data->Resize(0);
m_urlResponse->m_headerFields.clear();
}
if (m_urlRequest->m_creationSettings.m_outFilename.empty()==false)
{
curlImplData->m_curlCB->OpenFile(m_urlRequest->m_creationSettings.m_outFilename);
}
curlImplData->m_curlCB->m_useQueue = m_creationSettings.m_useDataPacketQueue;
m_urlRequest->AddHeaders("User-Agent", m_creationSettings.m_userAgent.c_str());
bool setupResult = m_urlRequest->SetupHandler(curlImplData->m_curlHandle);
if(curlImplData->m_curlCB && m_urlResponse && setupResult && (curlImplData->m_curlCB->IsFileOpen() || m_urlRequest->m_creationSettings.m_outFilename.empty()))
{
if (curlImplData->m_curlCB->IsFileOpen()==false && !m_creationSettings.m_useDataPacketQueue)
{
curlImplData->m_curlCB->SetResponseData(m_urlResponse->m_data);
}
curlImplData->m_curlCB->SetTimeout(m_creationSettings.m_timeout);
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_PROGRESSFUNCTION, UrlConnection_CurlCB::ProgressCB);
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_PROGRESSDATA, curlImplData->m_curlCB);
#ifdef GLWEBTOOLS_DEBUG
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_ERRORBUFFER, curlImplData->m_curlErrorBuffer);
#endif
if (!m_creationSettings.m_noProxyOption.empty())
{
/* Pass a pointer to a zero terminated string. The string consists of a
comma separated list of host names that do not require a proxy to get
reached, even if one is specified. The only wildcard available is a
single * character, which matches all hosts, and effectively disables
the proxy. Each name in this list is matched as either a domain which
contains the hostname, or the hostname itself. For example, example.com
would match example.com, example.com:80, and www.example.com, but not
www.notanexample.com or example.com.othertld. */
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_NOPROXY, m_creationSettings.m_noProxyOption.c_str());
}
if(m_creationSettings.m_follow302)
{
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_FOLLOWLOCATION, true);
}
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_WRITEFUNCTION, UrlConnection_CurlCB::DataWriteCB);
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_WRITEDATA, curlImplData->m_curlCB);
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_HEADERFUNCTION, UrlConnection_CurlCB::HeaderWriteCB);
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_WRITEHEADER, curlImplData->m_curlCB);
if(!m_creationSettings.m_peerCAFile.empty())
{
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_SSL_VERIFYPEER, 1L); //Enable ssl peer verification
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_CAINFO, m_creationSettings.m_peerCAFile.c_str());
}
else
{
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_SSL_VERIFYPEER, 0L); //Disable ssl peer verification - Assume peer trustable
}
if(m_creationSettings.m_enableAcceptEncoding)
{
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_ACCEPT_ENCODING, m_creationSettings.m_acceptEncoding.c_str());
}
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_MAXCONNECTS, m_creationSettings.m_maxConnections);
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_CONNECTTIMEOUT, m_creationSettings.m_connectTimeout);
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_LOW_SPEED_LIMIT, SupportServerSideEvent() ? 0 : m_creationSettings.m_lowSpeed);
curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_LOW_SPEED_TIME, SupportServerSideEvent() ? 0 : m_creationSettings.m_lowSpeedTimeout);
// enable this line to debug with fiddler or webscarab
//curl_easy_setopt(curlImplData->m_curlHandle, CURLOPT_PROXY, "127.0.0.1:8008");
m_urlRequest->SetState(UrlRequest::S_RUNNING);
m_state = UrlConnection::S_RUNNING;
glwebtools::MemBar();
Job job;
job.m_userCaller = this;
job.m_userUpdateFunc = UrlConnectionCore::RunRequestStatic;
curlImplData->m_taskId = 0;
m_accumulator_ms = 0;
return m_taskGroup->PushTask(&curlImplData->m_taskId, job, m_urlRequest->m_creationSettings.m_priority);
}
if(!curlImplData->m_curlCB || !m_urlResponse)
{
internError = glwebtools::E_MEMORY_ERROR;
}
else if(!setupResult)
{
internError = glwebtools::E_INVALID_DATA; //setup result should be a glwebtools::Error ...
}
}
//Failed
if(m_urlRequest)
{
m_urlRequest->SetState(UrlRequest::S_ERROR);
m_urlRequest->Drop();
m_urlRequest = 0;
}
glwebtools::MemBar();
m_state = UrlConnection::S_ERROR;
if (curlImplData->m_taskId != 0)
{
m_taskGroup->RemoveTask(curlImplData->m_taskId);
curlImplData->m_taskId = 0;
m_accumulator_ms = 0;
}
if(m_state != UrlConnection::S_WAITING_TO_RUN)
{
return glwebtools::E_INVALID_OPERATION;
}
else if(!m_urlRequest)//should never get here since state cannot be S_WAITING_TO_RUN if m_urlRequest is not set
{
return glwebtools::E_INVALID_HANDLE;
}
else if(GLWEBTOOLS_FAIL(internError))
{
return internError;
}
return glwebtools::E_UNKNOWN;
}
glwebtools::Error UrlConnectionCore::CancelRequest()
{
LockScope _ls(m_mutex);
if(m_state == UrlConnection::S_RUNNING && m_implData)
{
UrlConnection_Curl_ImplData* curlImplData = static_cast<UrlConnection_Curl_ImplData*>(m_implData);
glwebtools::Error result = m_taskGroup->RemoveTask(curlImplData->m_taskId);
// Connection was still in queue
if(GLWEBTOOLS_SUCCESS(result))
{
// At this point the request cannot be running
// Set the task id to 0 only if the task is actually removed
// so that if the task is removed twice, do no set the state in error
curlImplData->m_taskId = 0;
m_accumulator_ms = 0;
GLWEBTOOLS_DELETE(m_urlResponse);
m_urlResponse = 0; //Destroy response on connection error
m_urlRequest->SetState(UrlRequest::S_ERROR);
m_urlRequest->Drop();
m_urlRequest = 0;
SetLastError(glwebtools::E_CONNECTION_CANCELLED);
m_state = UrlConnection::S_ERROR;
return glwebtools::E_SUCCESS;
}
else
{
// Here the request is currently running
// and cannot be deleted because there is a lock on CanDelete
if(curlImplData->m_curlCB)
{
curlImplData->m_curlCB->m_needCancel = true;
return glwebtools::E_SUCCESS;
}
else
{
return glwebtools::E_INVALID_DATA;
}
}
}
return glwebtools::E_INVALID_OPERATION;
}
void UrlConnectionCore::RunRequestStatic(void* caller, void* param)
{
if(caller)
{
UrlConnectionCore* urlConnection = static_cast<UrlConnectionCore*>(caller);
urlConnection->RunRequest();
}
}
//private:
UrlConnectionCore::UrlConnectionCore(TaskGroup& taskGroup)
:m_token(0), m_error(glwebtools::E_SUCCESS), m_taskGroup(&taskGroup)
{
m_urlRequest = 0;
m_urlResponse = 0;
m_state = UrlConnection::S_IDLE;
m_implData = 0;
m_accumulator_ms = 0;
m_creationSettings = UrlConnection::CreationSettings();
}
UrlConnectionCore::UrlConnectionCore(const UrlConnection::CreationSettings& cs, TaskGroup& taskGroup)
:m_token(0), m_error(glwebtools::E_SUCCESS), m_taskGroup(&taskGroup)
{
m_urlRequest = 0;
m_urlResponse = 0;
m_state = UrlConnection::S_IDLE;
m_implData = 0;
m_accumulator_ms = 0;
m_creationSettings = cs;
}
unsigned int UrlConnectionCore::GetToken()
{
return m_token;
}
void UrlConnectionCore::SetToken(unsigned int token)
{
m_token = token;
}
glwebtools::Error UrlConnectionCore::RunRequest()
{
glwebtools::Error error = glwebtools::E_SUCCESS;
glwebtools::UrlConnection::State nextState;
if(m_implData)
{
UrlConnection_Curl_ImplData* curlImplData = static_cast<UrlConnection_Curl_ImplData*>(m_implData);
#ifdef GLWEBTOOLS_DEBUG
// Must be fully initialized before running the request in a thread
GLWEBTOOLS_ASSERT(m_state == UrlConnection::S_RUNNING);
#endif
CURLcode result = curl_easy_perform(curlImplData->m_curlHandle);
if(curlImplData->m_curlCB)
{
curlImplData->m_curlCB->CloseFile();
}
long responseCode = 0;
#if ((GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER) || GLWEBTOOLS_ENABLE_DUMP_REQUEST)
glwebtools::JsonWriter m_debugPacketWriter;
bool m_debuggerTrackingEnabled = false;
bool dumpRequestEnabled = false;
#if GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER
m_debuggerTrackingEnabled = glwebtools::GlfDebuggerModule::GetInstance().IsTracking();
#endif
#if GLWEBTOOLS_ENABLE_DUMP_REQUEST
dumpRequestEnabled = m_creationSettings.m_dumpRequestForDebug;
#endif
#endif // ((GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER) || GLWEBTOOLS_ENABLE_DUMP_REQUEST)
#if (GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER)
static int sDebugPacketId = 0;
if(m_debuggerTrackingEnabled)
{
std::string sessionName = glwebtools::GlfDebuggerModule::GetInstance().GetSessionName();
m_debugPacketWriter << glwebtools::ByName("session", sessionName);
std::stringstream ss;
ss << sessionName;
ss << "_";
ss << ++sDebugPacketId;
m_debugPacketWriter << glwebtools::ByName("id", ss.str());
}
#endif // (GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER)
#if ((GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER) || GLWEBTOOLS_ENABLE_DUMP_REQUEST)
if(m_debuggerTrackingEnabled || dumpRequestEnabled)
{
std::stringstream ss2;
time_t curtime = glwebtools::Time::GetCurrentTimestamp();
ss2 << curtime;
std::string timestr = ss2.str();
m_debugPacketWriter << glwebtools::ByName("timestamp", timestr.c_str());
m_debugPacketWriter << glwebtools::ByName("glwebtools_instance", m_instanceName);
}
#endif // ((GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER) || GLWEBTOOLS_ENABLE_DUMP_REQUEST)
#if ((GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER) || GLWEBTOOLS_ENABLE_DUMP_REQUEST)
//if(sCurlEventType.IsEnabled())
if(m_debuggerTrackingEnabled || dumpRequestEnabled)
{
glwebtools::JsonWriter m_debugPacketRequestWriter;
m_debugPacketRequestWriter = m_debugPacketWriter["request"];
// Request
const char* curlcstringvalue = 0;
curl_easy_getinfo(curlImplData->m_curlHandle, CURLINFO_EFFECTIVE_URL, &curlcstringvalue);
switch(m_urlRequest->m_method)
{
case glwebtools::UrlRequest::M_GET:
{
m_debugPacketRequestWriter << glwebtools::ByName("method", "GET");
break;
}
case glwebtools::UrlRequest::M_POST:
{
m_debugPacketRequestWriter << glwebtools::ByName("method", "POST");
break;
}
case glwebtools::UrlRequest::M_HEAD:
{
m_debugPacketRequestWriter << glwebtools::ByName("method", "HEAD");
break;
}
case glwebtools::UrlRequest::M_DELETE:
{
m_debugPacketRequestWriter << glwebtools::ByName("method", "DELETE");
break;
}
case glwebtools::UrlRequest::M_PUT:
{
m_debugPacketRequestWriter << glwebtools::ByName("method", "PUT");
break;
}
default:
{
m_debugPacketRequestWriter << glwebtools::ByName("method", "OTHER");
break;
}
}
if(curlcstringvalue)
{
if(strncmp(curlcstringvalue, "https", 5) == 0)
{
m_debugPacketRequestWriter << glwebtools::ByName("protocol", "HTTPS");
const char* path = strchr(curlcstringvalue + 8, '/');
if(path)
{
m_debugPacketRequestWriter << glwebtools::ByName("path", path);
std::string hostport(curlcstringvalue + 8, path - (curlcstringvalue + 8));
m_debugPacketRequestWriter << glwebtools::ByName("authority_port", hostport);
}
const char* port = strchr(curlcstringvalue + 8, ':');
if(port && port < path)
{
std::string portstr(port + 1, path - (port + 1));
int iport = atoi(portstr.c_str());
m_debugPacketRequestWriter << glwebtools::ByName("port", iport);
std::string host(curlcstringvalue + 8, port - (curlcstringvalue + 8));
m_debugPacketRequestWriter << glwebtools::ByName("authority", host);
}
else
{
std::string hostport(curlcstringvalue + 8, path - (curlcstringvalue + 8));
m_debugPacketRequestWriter << glwebtools::ByName("authority", hostport);
}
}
else if(strncmp(curlcstringvalue, "http", 4) == 0)
{
m_debugPacketRequestWriter << glwebtools::ByName("protocol", "HTTP");
const char* path = strchr(curlcstringvalue + 7, '/');
if(path)
{
m_debugPacketRequestWriter << glwebtools::ByName("path", path);
std::string hostport(curlcstringvalue + 7, path - (curlcstringvalue + 7));
m_debugPacketRequestWriter << glwebtools::ByName("authority_port", hostport);
}
const char* port = strchr(curlcstringvalue + 7, ':');
if(port && port < path)
{
std::string portstr(port + 1, path - (port + 1));
int iport = atoi(portstr.c_str());
m_debugPacketRequestWriter << glwebtools::ByName("port", iport);
std::string host(curlcstringvalue + 7, port - (curlcstringvalue + 7));
m_debugPacketRequestWriter << glwebtools::ByName("authority", host);
}
else
{
std::string hostport(curlcstringvalue + 7, path - (curlcstringvalue + 7));
m_debugPacketRequestWriter << glwebtools::ByName("authority", hostport);
}
}
else
{
m_debugPacketRequestWriter << glwebtools::ByName("protocol", "UNKNOWN");
}
m_debugPacketRequestWriter << glwebtools::ByName("effective_url", curlcstringvalue);
}
#if (GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER)
if(glwebtools::GlfDebuggerModule::GetInstance().IsTrackingData())
{
#endif // (GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER)
m_debugPacketRequestWriter << glwebtools::ByName("data", m_urlRequest->m_data);
#if (GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER)
}
#endif // (GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER)
// Timing
glwebtools::JsonWriter m_debugPacketTimingWriter = m_debugPacketWriter["timing"];
m_debugPacketTimingWriter << glwebtools::ByName("unit", "s");
double curldoublevalue = 0.0;
curl_easy_getinfo(curlImplData->m_curlHandle, CURLINFO_NAMELOOKUP_TIME, &curldoublevalue);
m_debugPacketTimingWriter << glwebtools::ByName("namelookup", curldoublevalue);
curldoublevalue = 0.0;
curl_easy_getinfo(curlImplData->m_curlHandle, CURLINFO_CONNECT_TIME, &curldoublevalue);
m_debugPacketTimingWriter << glwebtools::ByName("connect", curldoublevalue);
curldoublevalue = 0.0;
curl_easy_getinfo(curlImplData->m_curlHandle, CURLINFO_APPCONNECT_TIME, &curldoublevalue);
m_debugPacketTimingWriter << glwebtools::ByName("app_connect", curldoublevalue);
curldoublevalue = 0.0;
curl_easy_getinfo(curlImplData->m_curlHandle, CURLINFO_PRETRANSFER_TIME, &curldoublevalue);
m_debugPacketTimingWriter << glwebtools::ByName("pre_transfer", curldoublevalue);
double pretime = curldoublevalue;
curldoublevalue = 0.0;
curl_easy_getinfo(curlImplData->m_curlHandle, CURLINFO_STARTTRANSFER_TIME, &curldoublevalue);
m_debugPacketTimingWriter << glwebtools::ByName("start_transfer", curldoublevalue);
m_debugPacketTimingWriter << glwebtools::ByName("wait_transfer", curldoublevalue - pretime);
curldoublevalue = 0.0;
curl_easy_getinfo(curlImplData->m_curlHandle, CURLINFO_TOTAL_TIME, &curldoublevalue);
m_debugPacketTimingWriter << glwebtools::ByName("total", curldoublevalue);
curldoublevalue = 0.0;
curl_easy_getinfo(curlImplData->m_curlHandle, CURLINFO_REDIRECT_TIME, &curldoublevalue);
m_debugPacketTimingWriter << glwebtools::ByName("redirect", curldoublevalue);
}
#endif //((GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER) || GLWEBTOOLS_ENABLE_DUMP_REQUEST)
if(result == CURLE_OK)
{
if(responseCode == 0)
{
curl_easy_getinfo(curlImplData->m_curlHandle, CURLINFO_RESPONSE_CODE, &responseCode);
m_urlResponse->SetResponseCode(responseCode);
}
//TODO parse header list from cb struct
if(curlImplData->m_curlCB->m_headerLines.size() > 0)
{
StringList::iterator iter = curlImplData->m_curlCB->m_headerLines.begin();
StringList::iterator end = curlImplData->m_curlCB->m_headerLines.end();
#if ((GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER) || GLWEBTOOLS_ENABLE_DUMP_REQUEST)
glwebtools::JsonWriter m_debugPacketResponseHeaderWriter;
m_debugPacketResponseHeaderWriter = m_debugPacketWriter["response"]["headers"];
#endif // ((GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER) || GLWEBTOOLS_ENABLE_DUMP_REQUEST)
for(;iter != end; ++iter)
{
const char* line = iter->c_str();
const char* sep = std::strchr(line, ':');
if(sep)
{
while(sep[-1] == ' ' || !HTTPHeaderField::IsValidChar(sep[-1]))
{
--sep;
}
glwebtools::String key(line, sep-line);
sep = strchr(line, ':');
while((sep[0] == ':' || sep[0] == ' ' || !HTTPHeaderField::IsValidChar(sep[0])) && (sep < (line + iter->size())))
{
++sep;
}
if(sep < (line + iter->size()))
{
GLWEBTOOLS_LOG_VERBOSE("Adding Header %s = %s", key.c_str(), sep);
m_urlResponse->AddHeaderField(key.c_str(), sep);
#if ((GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER) || GLWEBTOOLS_ENABLE_DUMP_REQUEST)
if(m_debuggerTrackingEnabled || dumpRequestEnabled)
{
m_debugPacketResponseHeaderWriter << glwebtools::ByName(key.c_str(), sep);
}
#endif
}
}
}
}
#if ((GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER) || GLWEBTOOLS_ENABLE_DUMP_REQUEST)
//if(sCurlEventType.IsEnabled())
if(m_debuggerTrackingEnabled || dumpRequestEnabled)
{
glwebtools::JsonWriter m_debugPacketResponseWriter;
m_debugPacketResponseWriter = m_debugPacketWriter["response"];
// Response
m_debugPacketResponseWriter << glwebtools::ByName("code", (int)responseCode);
const char* ct = m_urlResponse->GetHeaderField("Content-type");
if(ct)
{
if(responseCode >= 400)
{
// if error response code and data is text or json, consider data an error message
if(strncmp(ct, "text", 4) == 0 || strncmp(ct, "application/json", 16) == 0)
{
std::string str;
m_urlResponse->GetDataAsString(str);
m_debugPacketResponseWriter << glwebtools::ByName("error_msg", str);
}
}
else
{
#if (GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER)
if(glwebtools::GlfDebuggerModule::GetInstance().IsTrackingData())
{
#endif // (GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER)
if(strncmp(ct, "text", 4) == 0 || strncmp(ct, "application/json", 16) == 0)
{
std::string str;
m_urlResponse->GetDataAsString(str);
m_debugPacketResponseWriter << glwebtools::ByName("data", str);
m_debugPacketResponseWriter << glwebtools::ByName("data_size", (int)str.size());
}
else
{
const void* data;
size_t datasize;
m_urlResponse->GetData(data, datasize);
m_debugPacketResponseWriter << glwebtools::ByName("data", "Data omitted due to content-type");
m_debugPacketResponseWriter << glwebtools::ByName("data_size", (int)datasize);
}
#if (GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER)
}
#endif // (GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER)
}
}
m_debugPacketWriter.ToString(m_outputDebugString);
#if (GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER)
glwebtools::GlfDebuggerModule::GetInstance().SendConnectionData(m_outputDebugString);
#endif // (GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER)
//glf::debugger::Profiler::Event evtcurl("Curl http request result", m_outputDebugString.c_str());
//glf::debugger::Profiler::GetInstance()->AddTimeEvent(evtcurl, sCurlEventType);
#ifdef GLWEBTOOLS_DEBUG
if(responseCode<400L)
{
GLWEBTOOLS_LOG_VERBOSE("%s", m_outputDebugString.c_str());
}
else
{
GLWEBTOOLS_LOG_MINOR_ERROR("%s", m_outputDebugString.c_str());
}
#endif
}
#endif // ((GLWEBTOOLS_ENABLE_DEBUGGER_MODULE && GLF_ENABLE_DEBUGGER) || GLWEBTOOLS_ENABLE_DUMP_REQUEST)
if(responseCode >= 400L)
{
m_urlRequest->SetState(UrlRequest::S_ERROR);
}
else
{
m_urlRequest->SetState(UrlRequest::S_IDLE);
}
#ifdef GLWEBTOOLS_DEBUG
if(responseCode<400L)
{
GLWEBTOOLS_LOG_INFO("Request ended with response code : [%d]", responseCode);
}
else
{
GLWEBTOOLS_LOG_MINOR_ERROR("Request ended with response code : [%d]", responseCode);
}
#endif
m_urlRequest->Drop();
m_urlRequest = 0;
nextState = UrlConnection::S_IDLE;
error = glwebtools::E_SUCCESS;
}
else
{
m_debugPacketWriter.ToString(m_outputDebugString);
#ifdef GLWEBTOOLS_DEBUG
GLWEBTOOLS_LOG_MINOR_ERROR("%s", m_outputDebugString.c_str());
#endif
GLWEBTOOLS_DELETE(m_urlResponse);
m_urlResponse = 0; //Destroy response on connection error
m_urlRequest->SetState(UrlRequest::S_ERROR);
m_urlRequest->Drop();
m_urlRequest = 0;
#ifdef GLWEBTOOLS_DEBUG
GLWEBTOOLS_LOG_MINOR_ERROR("Request ended with error : [%d] %s", result, curl_easy_strerror(result));
#endif
nextState = UrlConnection::S_ERROR;
error = glwebtools::ErrorHandler::Translate(result);
}
}
else
{
error = glwebtools::E_INVALID_OPERATION;
}
this->SetLastError(error);
glwebtools::MemBar();
m_state = nextState;
// At this point this instance can be deleted by glwebtools
return error;
}
void UrlConnectionCore::SetLastError(glwebtools::Error error)
{
m_error = error;
}
glwebtools::Error UrlConnectionCore::GetLastError() const
{
return m_error;
}
glwebtools::Error UrlConnectionCore::InvalidateRequest()
{
return glwebtools::E_SUCCESS;
}
glwebtools::Error UrlConnectionCore::InvalidateConnection()
{
return glwebtools::E_SUCCESS;
}
glwebtools::Error UrlConnectionCore::SetIntanceName(const std::string& instanceName)
{
m_instanceName = instanceName;
return glwebtools::E_SUCCESS;
}
size_t UrlConnectionCore::GetDownloadedBytes() const
{
if(m_implData)
{
UrlConnection_Curl_ImplData* curlImplData = static_cast<UrlConnection_Curl_ImplData*>(m_implData);
if(curlImplData->m_curlCB)
{
return curlImplData->m_curlCB->GetBytesCount();
}
}
return 0;
}
glwebtools::Error UrlConnectionCore::CreateImplData()
{
if (m_implData)
return glwebtools::E_INVALID_OPERATION;
UrlConnection_Curl_ImplData* curlImplData = 0;
if (SupportServerSideEvent())
{
curlImplData = GLWEBTOOLS_NEW ServerSideEventListener_Curl_ImplData();
}
else
{
curlImplData = GLWEBTOOLS_NEW UrlConnection_Curl_ImplData();
}
if (curlImplData == 0)
return glwebtools::E_MEMORY_ERROR;
m_implData = static_cast<void*>(curlImplData);
return glwebtools::E_SUCCESS;
}
glwebtools::Error UrlConnectionCore::DestroyImplData()
{
if (m_implData)
{
UrlConnection_Curl_ImplData* curlImplData = static_cast<UrlConnection_Curl_ImplData*>(m_implData);
m_implData = 0;
GLWEBTOOLS_DELETE(curlImplData);
}
return glwebtools::E_SUCCESS;
}
bool UrlConnectionCore::SupportServerSideEvent() const
{
return m_creationSettings.m_supportServerSideEvent;
}
bool UrlConnectionCore::HasServerSideEvent() const
{
if (SupportServerSideEvent() == false)
return false;
if (!m_implData)
return false;
ServerSideEventListener_Curl_ImplData* curlImplData = static_cast<ServerSideEventListener_Curl_ImplData*>(m_implData);
ServerSideEventStreamParser* parser = curlImplData->GetServerSideEventParser();
if (parser == 0)
return false;
return parser->HasEvent();
}
glwebtools::Error UrlConnectionCore::PopServerSideEvent(ServerSideEvent& serverSideEvent)
{
if (HasServerSideEvent() == false)
return glwebtools::E_INVALID_OPERATION;
ServerSideEventListener_Curl_ImplData* curlImplData = static_cast<ServerSideEventListener_Curl_ImplData*>(m_implData);
ServerSideEventStreamParser* parser = curlImplData->GetServerSideEventParser();
return parser->PopEvent(serverSideEvent);
}
bool UrlConnectionCore::SupportDataPacketQueue() const
{
return m_creationSettings.m_useDataPacketQueue;
}
bool UrlConnectionCore::HasDataPacket() const
{
if (SupportDataPacketQueue() == false)
return false;
if (!m_implData)
return false;
UrlConnection_Curl_ImplData* curlImplData = static_cast<UrlConnection_Curl_ImplData*>(m_implData);
if(curlImplData->m_curlCB)
{
return !curlImplData->m_curlCB->IsDataPacketQueueEmpty();
}
return false;
}
glwebtools::Error UrlConnectionCore::PopDataPacket(MutableData& outData)
{
if (SupportDataPacketQueue() == false)
return false;
if (!m_implData)
return false;
UrlConnection_Curl_ImplData* curlImplData = static_cast<UrlConnection_Curl_ImplData*>(m_implData);
if(curlImplData->m_curlCB)
{
return curlImplData->m_curlCB->PopDataPacket(outData);
}
return false;
}
glwebtools::Error UrlConnectionCore::GetCreationSettings(UrlConnection::CreationSettings& cs) const
{
cs = m_creationSettings;
return glwebtools::E_SUCCESS;
}
glwebtools::Error UrlConnectionCore::Update(u64 delta_time_ms)
{
LockScope _ls(m_mutex);
if(m_state == UrlConnection::S_RUNNING && m_implData)
{
UrlConnection_Curl_ImplData* curlImplData = static_cast<UrlConnection_Curl_ImplData*>(m_implData);
if (m_taskGroup->IsScheduled(curlImplData->m_taskId) && m_taskGroup->SomeThreadStarted() == false)
{
if (m_accumulator_ms > 0)
{
// accumulate time only if the task is queued and no thread is started
m_accumulator_ms += delta_time_ms;
if (m_accumulator_ms > m_creationSettings.m_taskStartTimeout)
{
// Here the task cannot be popped by glwebtools, because this function is called internally from glwebtools::Update
// So this cannot fail
glwebtools::Error result = m_taskGroup->RemoveTask(curlImplData->m_taskId);
// Connection was still in queue
if(GLWEBTOOLS_SUCCESS(result))
{
curlImplData->m_taskId = 0;
m_accumulator_ms = 0;
GLWEBTOOLS_DELETE(m_urlResponse);
m_urlResponse = 0; //Destroy response on connection error
m_urlRequest->SetState(UrlRequest::S_ERROR);
m_urlRequest->Drop();
m_urlRequest = 0;
SetLastError(glwebtools::E_CONNECTION_NOTHREAD);
m_state = UrlConnection::S_ERROR;
return glwebtools::E_SUCCESS;
}
}
}
else
{
// accumulate time only if the task is queued and no thread is started
m_accumulator_ms += delta_time_ms;
}
}
}
return glwebtools::E_SUCCESS;
}
const std::string& UrlConnectionCore::GetOutputDebugString() const
{
return m_outputDebugString;
}
} //namespace glwebtools
#endif//GLWEBTOOLS_USE_CURL