From 31e904abbb1cf2aabb263d56a6e0fb368df101da Mon Sep 17 00:00:00 2001 From: fgenesis Date: Sat, 31 Mar 2012 02:40:30 +0200 Subject: [PATCH] More fixes, added sample code. --- CMakeLists.txt | 30 ++++++++++++++++++++++ main.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ minihttp.cpp | 67 ++++++++++++++++++++++++++++++++------------------ minihttp.h | 37 ++++++++++++++++++++-------- 4 files changed, 165 insertions(+), 34 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..67393c1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 2.6) +project(minihttp) + + +if(MSVC) + if(NOT CMAKE_C_STANDARD_LIBRARIES MATCHES ".*lws2_32.lib.*") + set(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES} ws2_32.lib" CACHE STRING "Default C libs" FORCE) + endif() + if(NOT CMAKE_CXX_STANDARD_LIBRARIES MATCHES ".*lws2_32.lib.*") + set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} ws2_32.lib" CACHE STRING "Default C++ libs" FORCE) + endif() +else() + if(WIN32) + if(NOT CMAKE_C_STANDARD_LIBRARIES MATCHES ".*-lws2_32.*") + set(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES} -lws2_32" CACHE STRING "Default C libs" FORCE) + endif() + if(NOT CMAKE_CXX_STANDARD_LIBRARIES MATCHES ".*-lws2_32.*") + set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} -lws2_32" CACHE STRING "Default C++ libs" FORCE) + endif() + endif() + + # non-msvc needs build type - if no build type was provided, set a default one + if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build (Debug, Release, RelWithDebInfo, MinSizeRel)" FORCE) + endif() +endif() + + + +add_executable(htt minihttp.cpp minihttp.h main.cpp) diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..42da1cd --- /dev/null +++ b/main.cpp @@ -0,0 +1,65 @@ +// minihttp main.cpp - Sample code how to download a file. +// Released under the WTFPL (See minihttp.h) + +#include +#include + +#include "minihttp.h" + +// Overloaded socket class that handles incoming data. +// This one just prints to standard output. +class HttpDumpSocket : public minihttp::HttpSocket +{ +public: + virtual ~HttpDumpSocket() {} + +protected: + virtual void _OnClose() + { + puts("_OnClose()"); + minihttp::HttpSocket::_OnClose(); + } + virtual void _OnOpen() + { + puts("_OnOpen()"); + minihttp::HttpSocket::_OnOpen(); + } + + virtual void _OnRequestDone() + { + printf("_OnRequestDone(): %s\n", GetCurrentRequest().resource.c_str()); + } + + virtual void _OnRecv(char *buf, unsigned int size) + { + if(!size) + return; + printf("===START==[Status:%d, Size:%d]======\n", GetStatusCode(), size); + fwrite(buf, 1, size, stdout); + puts("\n===END===================="); + } +}; + +int main(int argc, char *argv[]) +{ + minihttp::InitNetwork(); + HttpDumpSocket *ht = new HttpDumpSocket; + + ht->SetKeepAlive(3); + + ht->SetBufsizeIn(64 * 1024); + + ht->Download("http://www.ietf.org/rfc/rfc2616.txt"); + ht->Download("http://example.com"); // Queue another one + + minihttp::SocketSet ss; + + ss.add(ht, true); // Delete socket if closed and no task left. + + // This is non-blocking and could be done in background or by another thread. + // Hogs quite some CPU doing it this way, though. + while(ss.size()) + ss.update(); + + return 0; +} diff --git a/minihttp.cpp b/minihttp.cpp index 5981a23..5d0aac1 100644 --- a/minihttp.cpp +++ b/minihttp.cpp @@ -230,7 +230,7 @@ void TcpSocket::SetBufsizeIn(unsigned int s) if(s != _inbufSize) _inbuf = (char*)realloc(_inbuf, s); _inbufSize = s; - _writeSize = s - 1; // FIXME: why? + _writeSize = s - 1; _readptr = _writeptr = _inbuf; } @@ -364,7 +364,7 @@ bool TcpSocket::update(void) #endif return false; } - traceprint("SOCKET UPDATE: %s\n", _GetErrorStr(e).c_str()); + traceprint("SOCKET UPDATE ERROR: (%d): %s\n", e, _GetErrorStr(e).c_str()); } return true; } @@ -383,7 +383,7 @@ static void strToLower(std::string& s) HttpSocket::HttpSocket() : TcpSocket(), _keep_alive(0), _remaining(0), _chunkedTransfer(false), _mustClose(true), _inProgress(false), -_followRedir(true), _alwaysHandle(false) +_followRedir(true), _alwaysHandle(false), _status(0) { } @@ -402,8 +402,11 @@ bool HttpSocket::_OnUpdate() if(!TcpSocket::_OnUpdate()) return false; + if(_inProgress && !_chunkedTransfer && !_remaining && _status) + _FinishRequest(); + // initiate transfer if queue is not empty, but the socket somehow forgot to proceed - if(_requestQ.size() && !_inProgress) + if(_requestQ.size() && !_remaining && !_chunkedTransfer && !_inProgress) _DequeueMore(); return true; @@ -470,7 +473,6 @@ bool HttpSocket::_EnqueueOrSend(const Request& req, bool forceQueue /* = false * return true; } // ok, we can send directly - _inProgress = false; // assume fail until we really got some bytes out if(!_OpenRequest(req)) return false; _inProgress = SendBytes(req.header.c_str(), req.header.length()); @@ -480,8 +482,7 @@ bool HttpSocket::_EnqueueOrSend(const Request& req, bool forceQueue /* = false * // called whenever a request is finished completely and the socket checks for more things to send void HttpSocket::_DequeueMore(void) { - if(_inProgress) - _FinishRequest(); + _FinishRequest(); // In case this was not done yet. // _inProgress is known to be false here if(_requestQ.size()) // still have other requests queued? @@ -493,18 +494,27 @@ void HttpSocket::_DequeueMore(void) bool HttpSocket::_OpenRequest(const Request& req) { + if(_inProgress) + { + traceprint("HttpSocket::_OpenRequest(): _inProgress == true, should not be called."); + return false; + } if(!open(req.host.c_str(), req.port)) return false; _inProgress = true; _curRequest = req; + _status = 0; return true; } void HttpSocket::_FinishRequest(void) { - _OnRequestDone(); // notify about finished request - _inProgress = false; - _hdrs.clear(); + if(_inProgress) + { + _OnRequestDone(); // notify about finished request + _inProgress = false; + _hdrs.clear(); + } } void HttpSocket::_ProcessChunk(void) @@ -608,9 +618,9 @@ void HttpSocket::_ParseHeaderFields(const char *s, size_t size) } } -const char *HttpSocket::Hdr(const char *h) +const char *HttpSocket::Hdr(const char *h) const { - std::map::iterator it = _hdrs.find(h); + std::map::const_iterator it = _hdrs.find(h); return it == _hdrs.end() ? NULL : it->second.c_str(); } @@ -629,7 +639,7 @@ bool HttpSocket::_HandleStatus() const char *conn = Hdr("connection"); // if its not keep-alive, server will close it, so we can too _mustClose = !conn || STRNICMP(conn, "keep-alive", 10); - if(!(_chunkedTransfer || _contentLen)) + if(!(_chunkedTransfer || _contentLen) && _status == 200) traceprint("_ParseHeader: Not chunked transfer and content-length==0, this will go fail"); switch(_status) @@ -644,7 +654,10 @@ bool HttpSocket::_HandleStatus() case 308: if(_followRedir) if(const char *loc = Hdr("location")) - Download(loc, GetCurrentRequest()->user); + { + traceprint("Following HTTP redirect to: %s\n", loc); + Download(loc, _curRequest.user); + } return false; default: @@ -655,8 +668,6 @@ bool HttpSocket::_HandleStatus() void HttpSocket::_ParseHeader(void) { - // if we are here, we expect a header - // TODO: this can be more optimized _tmpHdr += _inbuf; const char *hptr = _tmpHdr.c_str(); @@ -673,6 +684,8 @@ void HttpSocket::_ParseHeader(void) return; } + //traceprint(hptr); + hptr = strchr(hptr + 5, ' '); // skip "HTTP/", already known if(!hptr) return; // WTF? @@ -751,21 +764,25 @@ SocketSet::~SocketSet() void SocketSet::deleteAll(void) { - for(std::set::iterator it = _store.begin(); it != _store.end(); ++it) - delete *it; + for(Store::iterator it = _store.begin(); it != _store.end(); ++it) + delete it->first; _store.clear(); } bool SocketSet::update(void) { bool interesting = false; - std::set::iterator it = _store.begin(); + Store::iterator it = _store.begin(); for( ; it != _store.end(); ) { - interesting = (*it)->update() || interesting; - // HACK: TEMP - if(!(*it)->isOpen()) + TcpSocket *sock = it->first; + SocketSetData& sdata = it->second; + interesting = sock->update() || interesting; + if(sdata.deleteWhenDone && !sock->isOpen() && !sock->HasPendingTask()) + { + delete sock; _store.erase(it++); + } else ++it; } @@ -777,10 +794,12 @@ void SocketSet::remove(TcpSocket *s) _store.erase(s); } -void SocketSet::add(TcpSocket *s) +void SocketSet::add(TcpSocket *s, bool deleteWhenDone /* = true */) { s->SetNonBlocking(true); - _store.insert(s); + SocketSetData sdata; + sdata.deleteWhenDone = deleteWhenDone; + _store[s] = sdata; } #endif diff --git a/minihttp.h b/minihttp.h index 5435f63..175eb3c 100644 --- a/minihttp.h +++ b/minihttp.h @@ -32,6 +32,8 @@ public: TcpSocket(); virtual ~TcpSocket(); + virtual bool HasPendingTask() const { return false; } + bool open(const char *addr = NULL, unsigned int port = 0); void close(); bool update(); // returns true if something interesting happened (incoming data, closed connection, etc) @@ -111,6 +113,11 @@ public: HttpSocket(); virtual ~HttpSocket(); + virtual bool HasPendingTask() const + { + return ExpectMoreData() || _requestQ.size(); + } + void SetKeepAlive(unsigned int secs) { _keep_alive = secs; } void SetUserAgent(const std::string &s) { _user_agent = s; } void SetAcceptEncoding(const std::string& s) { _accept_encoding = s; } @@ -122,15 +129,15 @@ public: bool SendGet(const std::string what, void *user = NULL); bool QueueGet(const std::string what, void *user = NULL); - unsigned int GetRemaining() { return _remaining; } + unsigned int GetRemaining() const { return _remaining; } - unsigned int GetStatusCode() { return _status; } - unsigned int GetContentLen() { return _contentLen; } - bool ChunkedTransfer() { return _chunkedTransfer; } - bool ExpectMoreData() { return _remaining || _chunkedTransfer; } + unsigned int GetStatusCode() const { return _status; } + unsigned int GetContentLen() const { return _contentLen; } + bool ChunkedTransfer() const { return _chunkedTransfer; } + bool ExpectMoreData() const { return _remaining || _chunkedTransfer; } - Request *GetCurrentRequest() { return _inProgress ? &_curRequest : NULL; } - const char *Hdr(const char *h); + const Request &GetCurrentRequest() const { return _curRequest; } + const char *Hdr(const char *h) const; protected: @@ -182,7 +189,7 @@ protected: #ifdef MINIHTTP_SUPPORT_SOCKET_SET -#include +#include namespace minihttp { @@ -193,12 +200,22 @@ public: virtual ~SocketSet(); void deleteAll(); bool update(); - void add(TcpSocket *s); + void add(TcpSocket *s, bool deleteWhenDone = true); + bool has(TcpSocket *s); void remove(TcpSocket *s); inline size_t size() { return _store.size(); } protected: - std::set _store; + + struct SocketSetData + { + bool deleteWhenDone; + // To be extended + }; + + typedef std::map Store; + + Store _store; }; #endif