Added support for AsyncRequests in cHttp.

Added cThreadLocal and tThreadLocalPtr ( needed for the async requests ).
This commit is contained in:
Martín Lucas Golini
2013-09-30 02:04:06 -03:00
parent aeb6dd4f07
commit cff7eb600f
23 changed files with 515 additions and 52 deletions

View File

@@ -6,8 +6,14 @@
#include <eepp/network/ctcpsocket.hpp>
#include <eepp/base/noncopyable.hpp>
#include <eepp/system/ctime.hpp>
#include <eepp/system/tthreadlocalptr.hpp>
#include <eepp/system/cthread.hpp>
#include <eepp/system/cmutex.hpp>
#include <eepp/system/clock.hpp>
#include <eepp/helper/PlusCallback/callback.hpp>
#include <map>
#include <string>
#include <list>
using namespace EE::System;
@@ -73,6 +79,9 @@ class EE_API cHttp : NonCopyable {
** The body is empty by default.
** @param body Content of the body */
void SetBody(const std::string& body);
/** @return The request Uri */
const std::string& GetUri() const;
private:
friend class cHttp;
@@ -212,6 +221,8 @@ class EE_API cHttp : NonCopyable {
** @param port Port to use for connection */
cHttp(const std::string& host, unsigned short port = 0);
~cHttp();
/** @brief Set the target host
** This function just stores the host address and port, it
** doesn't actually connect to it until you send a request.
@@ -224,7 +235,6 @@ class EE_API cHttp : NonCopyable {
** @param port Port to use for connection */
void SetHost(const std::string& host, unsigned short port = 0);
/** @brief Send a HTTP request and return the server's response.
** You must have a valid host before sending a request (see SetHost).
** Any missing mandatory header field in the request will be added
@@ -238,12 +248,46 @@ class EE_API cHttp : NonCopyable {
** @param timeout Maximum time to wait
** @return Server's response */
Response SendRequest(const Request& request, cTime timeout = cTime::Zero);
/** Definition of the async callback response */
typedef cb::Callback3<void, const cHttp&, cHttp::Request&, cHttp::Response&> AsyncResponseCallback;
/** @brief Sends the request and creates a new thread, when got the response informs the result to the callback.
** This function does not lock the caller thread.
** @see SendRequest */
void SendAsyncRequest( AsyncResponseCallback cb, const cHttp::Request& request, cTime timeout = cTime::Zero );
/** @return The host address */
const cIpAddress& GetHost() const;
/** @return The host name */
const std::string& GetHostName() const;
/** @return The host port */
const unsigned short& GetPort() const;
private:
// Member data
cTcpSocket mConnection; ///< Connection to the host
cIpAddress mHost; ///< Web host address
std::string mHostName; ///< Web host name
unsigned short mPort; ///< Port used for connection with host
class cAsyncRequest : public cThread {
public:
cAsyncRequest( cHttp * http, AsyncResponseCallback cb, cHttp::Request request, cTime timeout );
void Run();
protected:
friend class cHttp;
cHttp * mHttp;
AsyncResponseCallback mCb;
cHttp::Request mRequest;
cTime mTimeout;
bool mRunning;
};
friend class cAsyncRequest;
tThreadLocalPtr<cTcpSocket> mConnection; ///< Connection to the host
cIpAddress mHost; ///< Web host address
std::string mHostName; ///< Web host name
unsigned short mPort; ///< Port used for connection with host
std::list<cAsyncRequest*> mThreads;
cMutex mThreadsMutex;
void RemoveOldThreads();
};
}}

View File

@@ -22,5 +22,7 @@
#include <eepp/system/cresourceloader.hpp>
#include <eepp/system/tresourcemanager.hpp>
#include <eepp/system/cpackmanager.hpp>
#include <eepp/system/cthreadlocal.hpp>
#include <eepp/system/tthreadlocalptr.hpp>
#endif

View File

@@ -128,29 +128,29 @@ struct ThreadFunc
template <typename T>
struct ThreadFunctor : ThreadFunc
{
ThreadFunctor(T functor) : m_functor(functor) {}
virtual void Run() {m_functor();}
T m_functor;
ThreadFunctor(T functor) : mFunctor(functor) {}
virtual void Run() {mFunctor();}
T mFunctor;
};
// Specialization using a functor (including free functions) with one argument
template <typename F, typename A>
struct ThreadFunctorWithArg : ThreadFunc
{
ThreadFunctorWithArg(F function, A arg) : m_function(function), m_arg(arg) {}
virtual void Run() {m_function(m_arg);}
F m_function;
A m_arg;
ThreadFunctorWithArg(F function, A arg) : mFunction(function), mArg(arg) {}
virtual void Run() {mFunction(mArg);}
F mFunction;
A mArg;
};
// Specialization using a member function
template <typename C>
struct ThreadMemberFunc : ThreadFunc
{
ThreadMemberFunc(void(C::*function)(), C* object) : m_function(function), m_object(object) {}
virtual void Run() {(m_object->*m_function)();}
void(C::*m_function)();
C* m_object;
ThreadMemberFunc(void(C::*function)(), C* object) : mFunction(function), mObject(object) {}
virtual void Run() {(mObject->*mFunction)();}
void(C::*mFunction)();
C* mObject;
};
}

View File

@@ -0,0 +1,35 @@
#ifndef EE_SYSTEMCTHREADLOCAL_HPP
#define EE_SYSTEMCTHREADLOCAL_HPP
#include <eepp/base.hpp>
#include <eepp/base/noncopyable.hpp>
namespace EE { namespace System { namespace Private {
class cThreadLocalImpl;
}}}
namespace EE { namespace System {
/** @brief Defines variables with thread-local storage */
class EE_API cThreadLocal : NonCopyable {
public:
/** @brief Default constructor
** @param value Optional value to initalize the variable */
cThreadLocal(void* value = NULL);
~cThreadLocal();
/** @brief Set the thread-specific value of the variable
** @param value Value of the variable for the current thread */
void Value(void* value);
/** @brief Retrieve the thread-specific value of the variable
** @return Value of the variable for the current thread */
void* Value() const;
private :
Private::cThreadLocalImpl* mImpl; ///< Pointer to the OS specific implementation
};
}}
#endif

View File

@@ -0,0 +1,122 @@
#ifndef EE_SYSTEMCTHREADLOCALPTR_HPP
#define EE_SYSTEMCTHREADLOCALPTR_HPP
#include <eepp/system/cthreadlocal.hpp>
namespace EE { namespace System {
/** @brief Pointer to a thread-local variable */
template <typename T>
class tThreadLocalPtr : private cThreadLocal
{
public :
/** @brief Default constructor
** @param value Optional value to initalize the variable */
tThreadLocalPtr(T* value = NULL);
/** @brief Overload of unary operator *
** Like raw pointers, applying the * operator returns a
** reference to the pointed object.
** @return Reference to the pointed object */
T& operator *() const;
/** @brief Overload of operator ->
** Like raw pointers, applying the -> operator returns the
** pointed object.
** @return Pointed object */
T* operator ->() const;
/** @brief Cast operator to implicitely convert the pointer to its raw pointer type (T*)
** @return Pointer to the actual object */
operator T*() const;
/** @brief Assignment operator for a raw pointer parameter
** @param value Pointer to assign
** @return Reference to self */
tThreadLocalPtr<T>& operator =(T* value);
/** @brief Assignment operator for a tThreadLocalPtr parameter
** @param right tThreadLocalPtr to assign
** @return Reference to self */
tThreadLocalPtr<T>& operator =(const tThreadLocalPtr<T>& right);
};
template <typename T>
tThreadLocalPtr<T>::tThreadLocalPtr(T* value) :
cThreadLocal(value)
{
}
template <typename T>
T& tThreadLocalPtr<T>::operator *() const {
return *static_cast<T*>(Value());
}
template <typename T>
T* tThreadLocalPtr<T>::operator ->() const {
return static_cast<T*>(Value());
}
template <typename T>
tThreadLocalPtr<T>::operator T*() const {
return static_cast<T*>(Value());
}
template <typename T>
tThreadLocalPtr<T>& tThreadLocalPtr<T>::operator =(T* value) {
Value(value);
return *this;
}
template <typename T>
tThreadLocalPtr<T>& tThreadLocalPtr<T>::operator =(const tThreadLocalPtr<T>& right) {
Value(right.Value());
return *this;
}
}}
#endif
/**
@class tThreadLocalPtr
@ingroup System
tThreadLocalPtr is a type-safe wrapper for storing
pointers to thread-local variables. A thread-local
variable holds a different value for each different
thread, unlike normal variable that are shared.
Its usage is completely transparent, so that it is similar
to manipulating the raw pointer directly (like any smart pointer).
Usage example:
@code
MyClass object1;
MyClass object2;
tThreadLocalPtr<MyClass> objectPtr;
void thread1()
{
objectPtr = &object1; // doesn't impact thread2
...
}
void thread2()
{
objectPtr = &object2; // doesn't impact thread1
...
}
int main()
{
// Create and launch the two threads
cThread t1(&thread1);
cThread t2(&thread2);
t1.launch();
t2.launch();
return 0;
}
@endcode
*/

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 2.8.0, 2013-09-29T02:43:00. -->
<!-- Written by QtCreator 2.8.0, 2013-09-30T02:03:41. -->
<qtcreator>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
@@ -48,7 +48,7 @@
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{388e5431-b31b-42b3-b9ad-9002d279d75d}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">10</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">19</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">11</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">

View File

@@ -653,3 +653,10 @@
../../Makefile.base
../../Makefile
../../src/examples/http_request/http_request.cpp
../../include/eepp/system/cthreadlocal.hpp
../../src/eepp/system/cthreadlocal.cpp
../../src/eepp/system/platform/win/cthreadlocalimpl.hpp
../../src/eepp/system/platform/win/cthreadlocalimpl.cpp
../../src/eepp/system/platform/posix/cthreadlocalimpl.hpp
../../src/eepp/system/platform/posix/cthreadlocalimpl.cpp
../../include/eepp/system/tthreadlocalptr.hpp

View File

@@ -53,6 +53,10 @@ void cHttp::Request::SetBody(const std::string& body) {
mBody = body;
}
const std::string &cHttp::Request::GetUri() const {
return mUri;
}
std::string cHttp::Request::Prepare() const {
std::ostringstream out;
@@ -219,15 +223,36 @@ void cHttp::Response::Parse(const std::string& data) {
}
cHttp::cHttp() :
mConnection( NULL ),
mHost(),
mPort(0)
{
}
cHttp::cHttp(const std::string& host, unsigned short port) {
cHttp::cHttp(const std::string& host, unsigned short port) :
mConnection( NULL )
{
SetHost(host, port);
}
cHttp::~cHttp() {
std::list<cAsyncRequest*>::iterator itt;
// First we wait to finish any request pending
for ( itt = mThreads.begin(); itt != mThreads.end(); itt++ ) {
(*itt)->Wait();
}
for ( itt = mThreads.begin(); itt != mThreads.end(); itt++ ) {
eeDelete( *itt );
}
// Then we destroy the last open connection
cTcpSocket * tcp = mConnection;
eeSAFE_DELETE( tcp );
}
void cHttp::SetHost(const std::string& host, unsigned short port) {
// Check the protocol
if (toLower(host.substr(0, 7)) == "http://") {
@@ -253,6 +278,11 @@ void cHttp::SetHost(const std::string& host, unsigned short port) {
}
cHttp::Response cHttp::SendRequest(const cHttp::Request& request, cTime timeout) {
if ( NULL == mConnection ) {
cTcpSocket * Conn = eeNew( cTcpSocket, () );
mConnection = Conn;
}
// First make sure that the request is valid -- add missing mandatory fields
Request toSend(request);
@@ -286,19 +316,19 @@ cHttp::Response cHttp::SendRequest(const cHttp::Request& request, cTime timeout)
Response received;
// Connect the socket to the host
if (mConnection.Connect(mHost, mPort, timeout) == cSocket::Done) {
if (mConnection->Connect(mHost, mPort, timeout) == cSocket::Done) {
// Convert the request to string and send it through the connected socket
std::string requestStr = toSend.Prepare();
if (!requestStr.empty()) {
// Send it through the socket
if (mConnection.Send(requestStr.c_str(), requestStr.size()) == cSocket::Done) {
if (mConnection->Send(requestStr.c_str(), requestStr.size()) == cSocket::Done) {
// Wait for the server's response
std::string receivedStr;
std::size_t size = 0;
char buffer[1024];
while (mConnection.Receive(buffer, sizeof(buffer), size) == cSocket::Done) {
while (mConnection->Receive(buffer, sizeof(buffer), size) == cSocket::Done) {
receivedStr.append(buffer, buffer + size);
}
@@ -308,10 +338,80 @@ cHttp::Response cHttp::SendRequest(const cHttp::Request& request, cTime timeout)
}
// Close the connection
mConnection.Disconnect();
mConnection->Disconnect();
}
return received;
}
cHttp::cAsyncRequest::cAsyncRequest(cHttp *http, AsyncResponseCallback cb, cHttp::Request request, cTime timeout) :
mHttp( http ),
mCb( cb ),
mRequest( request ),
mTimeout( timeout ),
mRunning( true )
{
}
void cHttp::cAsyncRequest::Run() {
cHttp::Response response = mHttp->SendRequest( mRequest, mTimeout );
mCb( *mHttp, mRequest, response );
// The Async Request destroys the socket used to create the request
cTcpSocket * tcp = mHttp->mConnection;
eeSAFE_DELETE( tcp );
mHttp->mConnection = NULL;
mRunning = false;
}
void cHttp::RemoveOldThreads() {
std::list<cAsyncRequest*> remove;
std::list<cAsyncRequest*>::iterator it = mThreads.begin();
for ( ; it != mThreads.end(); it++ ) {
cAsyncRequest * ar = (*it);
if ( !ar->mRunning ) {
// We need to be sure, since the state is set in the thread, this will not block the thread anyway
ar->Wait();
eeDelete( ar );
remove.push_back( ar );
}
}
for ( it = remove.begin(); it != remove.end(); it++ ) {
mThreads.remove( (*it) );
}
}
void cHttp::SendAsyncRequest( AsyncResponseCallback cb, const cHttp::Request& request, cTime timeout ) {
cAsyncRequest * thread = eeNew( cAsyncRequest, ( this, cb, request, timeout ) );
thread->Launch();
// Clean old threads
cLock l( mThreadsMutex );
RemoveOldThreads();
mThreads.push_back( thread );
}
const cIpAddress &cHttp::GetHost() const {
return mHost;
}
const std::string &cHttp::GetHostName() const {
return mHostName;
}
const unsigned short& cHttp::GetPort() const {
return mPort;
}
}}

View File

@@ -0,0 +1,24 @@
#include <eepp/system/cthreadlocal.hpp>
#include <eepp/system/platform/platformimpl.hpp>
namespace EE { namespace System {
cThreadLocal::cThreadLocal(void* value) :
mImpl( eeNew( Private::cThreadLocalImpl, () ) )
{
Value( value );
}
cThreadLocal::~cThreadLocal() {
eeSAFE_DELETE( mImpl );
}
void cThreadLocal::Value(void* value) {
mImpl->Value(value);
}
void* cThreadLocal::Value() const {
return mImpl->Value();
}
}}

View File

@@ -8,13 +8,15 @@
#include <eepp/system/platform/posix/cmuteximpl.hpp>
#include <eepp/system/platform/posix/cclockimpl.hpp>
#include <eepp/system/platform/posix/cconditionimpl.hpp>
#include <eepp/system/platform/posix/cthreadlocalimpl.hpp>
#elif EE_PLATFORM == EE_PLATFORM_WIN
#include <eepp/system/platform/win/cthreadimpl.hpp>
#include <eepp/system/platform/win/cmuteximpl.hpp>
#include <eepp/system/platform/win/cclockimpl.hpp>
#include <eepp/system/platform/win/cconditionimpl.hpp>
#include <eepp/system/platform/win/cthreadlocalimpl.hpp>
#else
#error Threads, mutexes, conditions and timers not implemented for this platform.
#error Threads, mutexes, conditions, timers and thread local storage not implemented for this platform.
#endif
#endif

View File

@@ -0,0 +1,25 @@
#include <eepp/system/platform/posix/cthreadlocalimpl.hpp>
#if defined( EE_PLATFORM_POSIX )
namespace EE { namespace System { namespace Private {
cThreadLocalImpl::cThreadLocalImpl() {
pthread_key_create(&mKey, NULL);
}
cThreadLocalImpl::~cThreadLocalImpl() {
pthread_key_delete(mKey);
}
void cThreadLocalImpl::Value(void* value) {
pthread_setspecific(mKey, value);
}
void* cThreadLocalImpl::Value() const {
return pthread_getspecific(mKey);
}
}}}
#endif

View File

@@ -0,0 +1,31 @@
#ifndef EE_SYSTEMCTHREADLOCALIMPLPOSIX_HPP
#define EE_SYSTEMCTHREADLOCALIMPLPOSIX_HPP
#include <eepp/base.hpp>
#include <eepp/base/noncopyable.hpp>
#if defined( EE_PLATFORM_POSIX )
#include <pthread.h>
namespace EE { namespace System { namespace Private {
class cThreadLocalImpl : NonCopyable {
public:
cThreadLocalImpl();
~cThreadLocalImpl();
void Value(void* value);
void* Value() const;
private :
pthread_key_t mKey;
};
}}}
#endif
#endif

View File

@@ -0,0 +1,25 @@
#include <eepp/system/platform/win/cthreadlocalimpl.hpp>
#if EE_PLATFORM == EE_PLATFORM_WIN
namespace EE { namespace System { namespace Private {
cThreadLocalImpl::cThreadLocalImpl() {
mIndex = TlsAlloc();
}
cThreadLocalImpl::~cThreadLocalImpl() {
TlsFree(mIndex);
}
void cThreadLocalImpl::Value(void* value) {
TlsSetValue(mIndex, value);
}
void* cThreadLocalImpl::Value() const {
return TlsGetValue(mIndex);
}
}}}
#endif

View File

@@ -0,0 +1,30 @@
#ifndef EE_SYSTEMCTHREADLOCALIMPLWIN_HPP
#define EE_SYSTEMCTHREADLOCALIMPLWIN_HPP
#include <eepp/base.hpp>
#include <eepp/base/noncopyable.hpp>
#if EE_PLATFORM == EE_PLATFORM_WIN
#include <windows.h>
namespace EE { namespace System { namespace Private {
class cThreadLocalImpl : NonCopyable {
public:
cThreadLocalImpl();
~cThreadLocalImpl();
void Value(void* value);
void* Value() const;
private :
DWORD mIndex;
};
}}}
#endif
#endif

View File

@@ -42,7 +42,7 @@ EE_MAIN_FUNC int main (int argc, char * argv [])
cEngine::DestroySingleton();
// If was compiled in debug mode it will print the memory manager report
EE::MemoryManager::ShowResults();
MemoryManager::ShowResults();
return EXIT_SUCCESS;
}

View File

@@ -271,7 +271,7 @@ EE_MAIN_FUNC int main (int argc, char * argv [])
cEngine::DestroySingleton();
EE::MemoryManager::ShowResults();
MemoryManager::ShowResults();
return EXIT_SUCCESS;
}

View File

@@ -118,7 +118,7 @@ EE_MAIN_FUNC int main (int argc, char * argv [])
cEngine::DestroySingleton();
// If was compiled in debug mode it will print the memory manager report
EE::MemoryManager::ShowResults();
MemoryManager::ShowResults();
return EXIT_SUCCESS;
}

View File

@@ -1,28 +1,44 @@
#include <eepp/ee.hpp>
/// Entry point of application
EE_MAIN_FUNC int main (int argc, char * argv [])
{
// Create a new HTTP client
cHttp http;
void AsyncRequestCallback( const cHttp& http, cHttp::Request& request, cHttp::Response& response ) {
std::cout << "Got response from request: " << http.GetHostName() << request.GetUri() << std::endl;
// We'll work on http://www.wikipedia.org
http.SetHost("http://www.wikipedia.org");
// Prepare a request to get the '/' page
cHttp::Request request("/");
// Send the request
cHttp::Response response = http.SendRequest(request);
// Check the status code and display the result
cHttp::Response::Status status = response.GetStatus();
if ( status == cHttp::Response::Ok ) {
if ( response.GetStatus() == cHttp::Response::Ok ) {
std::cout << response.GetBody() << std::endl;
} else {
std::cout << "Error " << status << std::endl;
std::cout << "Error " << response.GetStatus() << std::endl;
}
}
EE_MAIN_FUNC int main (int argc, char * argv []) {
{
// Create a new HTTP client
cHttp http;
// We'll work on http://en.wikipedia.org
http.SetHost("http://en.wikipedia.org");
// Prepare a request to get the '/' page
cHttp::Request request("/wiki/Main_Page");
// Send the request
cHttp::Response response = http.SendRequest(request);
// Check the status code and display the result
cHttp::Response::Status status = response.GetStatus();
if ( status == cHttp::Response::Ok ) {
std::cout << response.GetBody() << std::endl;
} else {
std::cout << "Error " << status << std::endl;
}
cHttp::Request asyncRequest( "/wiki/" + Version::GetCodename() );
http.SendAsyncRequest( cb::Make3( AsyncRequestCallback ), asyncRequest, Seconds( 5 ) );
}
MemoryManager::ShowResults();
return EXIT_SUCCESS;
}

View File

@@ -642,7 +642,7 @@ EE_MAIN_FUNC int main (int argc, char * argv [])
cEngine::DestroySingleton();
EE::MemoryManager::ShowResults();
MemoryManager::ShowResults();
return EXIT_SUCCESS;
}

View File

@@ -68,7 +68,7 @@ EE_MAIN_FUNC int main (int argc, char * argv [])
std::cin.ignore(10000, '\n');
// If was compiled in debug mode it will print the memory manager report
EE::MemoryManager::ShowResults();
MemoryManager::ShowResults();
return EXIT_SUCCESS;
}

View File

@@ -151,7 +151,7 @@ EE_MAIN_FUNC int main (int argc, char * argv [])
cEngine::DestroySingleton();
// If was compiled in debug mode it will print the memory manager report
EE::MemoryManager::ShowResults();
MemoryManager::ShowResults();
return EXIT_SUCCESS;
}

View File

@@ -152,7 +152,7 @@ EE_MAIN_FUNC int main (int argc, char * argv [])
cEngine::DestroySingleton();
// If was compiled in debug mode it will print the memory manager report
EE::MemoryManager::ShowResults();
MemoryManager::ShowResults();
return EXIT_SUCCESS;
}

View File

@@ -1952,7 +1952,7 @@ EE_MAIN_FUNC int main (int argc, char * argv []) {
eeDelete( Test );
EE::MemoryManager::ShowResults();
MemoryManager::ShowResults();
return EXIT_SUCCESS;
}