Newer
Older
Import / projects / Gameloft / glwebtools / source / glwebtools / glwebtools_urlconnection_curl.cpp
#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