More fixes, added sample code.

This commit is contained in:
fgenesis 2012-03-31 02:40:30 +02:00
parent c9cd4ad790
commit 31e904abbb
4 changed files with 165 additions and 34 deletions

30
CMakeLists.txt Normal file
View file

@ -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)

65
main.cpp Normal file
View file

@ -0,0 +1,65 @@
// minihttp main.cpp - Sample code how to download a file.
// Released under the WTFPL (See minihttp.h)
#include <stdio.h>
#include <stdlib.h>
#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;
}

View file

@ -230,7 +230,7 @@ void TcpSocket::SetBufsizeIn(unsigned int s)
if(s != _inbufSize) if(s != _inbufSize)
_inbuf = (char*)realloc(_inbuf, s); _inbuf = (char*)realloc(_inbuf, s);
_inbufSize = s; _inbufSize = s;
_writeSize = s - 1; // FIXME: why? _writeSize = s - 1;
_readptr = _writeptr = _inbuf; _readptr = _writeptr = _inbuf;
} }
@ -364,7 +364,7 @@ bool TcpSocket::update(void)
#endif #endif
return false; 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; return true;
} }
@ -383,7 +383,7 @@ static void strToLower(std::string& s)
HttpSocket::HttpSocket() HttpSocket::HttpSocket()
: TcpSocket(), : TcpSocket(),
_keep_alive(0), _remaining(0), _chunkedTransfer(false), _mustClose(true), _inProgress(false), _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()) if(!TcpSocket::_OnUpdate())
return false; return false;
if(_inProgress && !_chunkedTransfer && !_remaining && _status)
_FinishRequest();
// initiate transfer if queue is not empty, but the socket somehow forgot to proceed // 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(); _DequeueMore();
return true; return true;
@ -470,7 +473,6 @@ bool HttpSocket::_EnqueueOrSend(const Request& req, bool forceQueue /* = false *
return true; return true;
} }
// ok, we can send directly // ok, we can send directly
_inProgress = false; // assume fail until we really got some bytes out
if(!_OpenRequest(req)) if(!_OpenRequest(req))
return false; return false;
_inProgress = SendBytes(req.header.c_str(), req.header.length()); _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 // called whenever a request is finished completely and the socket checks for more things to send
void HttpSocket::_DequeueMore(void) void HttpSocket::_DequeueMore(void)
{ {
if(_inProgress) _FinishRequest(); // In case this was not done yet.
_FinishRequest();
// _inProgress is known to be false here // _inProgress is known to be false here
if(_requestQ.size()) // still have other requests queued? if(_requestQ.size()) // still have other requests queued?
@ -493,18 +494,27 @@ void HttpSocket::_DequeueMore(void)
bool HttpSocket::_OpenRequest(const Request& req) 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)) if(!open(req.host.c_str(), req.port))
return false; return false;
_inProgress = true; _inProgress = true;
_curRequest = req; _curRequest = req;
_status = 0;
return true; return true;
} }
void HttpSocket::_FinishRequest(void) void HttpSocket::_FinishRequest(void)
{ {
_OnRequestDone(); // notify about finished request if(_inProgress)
_inProgress = false; {
_hdrs.clear(); _OnRequestDone(); // notify about finished request
_inProgress = false;
_hdrs.clear();
}
} }
void HttpSocket::_ProcessChunk(void) 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<std::string, std::string>::iterator it = _hdrs.find(h); std::map<std::string, std::string>::const_iterator it = _hdrs.find(h);
return it == _hdrs.end() ? NULL : it->second.c_str(); 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 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); _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"); traceprint("_ParseHeader: Not chunked transfer and content-length==0, this will go fail");
switch(_status) switch(_status)
@ -644,7 +654,10 @@ bool HttpSocket::_HandleStatus()
case 308: case 308:
if(_followRedir) if(_followRedir)
if(const char *loc = Hdr("location")) if(const char *loc = Hdr("location"))
Download(loc, GetCurrentRequest()->user); {
traceprint("Following HTTP redirect to: %s\n", loc);
Download(loc, _curRequest.user);
}
return false; return false;
default: default:
@ -655,8 +668,6 @@ bool HttpSocket::_HandleStatus()
void HttpSocket::_ParseHeader(void) void HttpSocket::_ParseHeader(void)
{ {
// if we are here, we expect a header
// TODO: this can be more optimized
_tmpHdr += _inbuf; _tmpHdr += _inbuf;
const char *hptr = _tmpHdr.c_str(); const char *hptr = _tmpHdr.c_str();
@ -673,6 +684,8 @@ void HttpSocket::_ParseHeader(void)
return; return;
} }
//traceprint(hptr);
hptr = strchr(hptr + 5, ' '); // skip "HTTP/", already known hptr = strchr(hptr + 5, ' '); // skip "HTTP/", already known
if(!hptr) if(!hptr)
return; // WTF? return; // WTF?
@ -751,21 +764,25 @@ SocketSet::~SocketSet()
void SocketSet::deleteAll(void) void SocketSet::deleteAll(void)
{ {
for(std::set<TcpSocket*>::iterator it = _store.begin(); it != _store.end(); ++it) for(Store::iterator it = _store.begin(); it != _store.end(); ++it)
delete *it; delete it->first;
_store.clear(); _store.clear();
} }
bool SocketSet::update(void) bool SocketSet::update(void)
{ {
bool interesting = false; bool interesting = false;
std::set<TcpSocket*>::iterator it = _store.begin(); Store::iterator it = _store.begin();
for( ; it != _store.end(); ) for( ; it != _store.end(); )
{ {
interesting = (*it)->update() || interesting; TcpSocket *sock = it->first;
// HACK: TEMP SocketSetData& sdata = it->second;
if(!(*it)->isOpen()) interesting = sock->update() || interesting;
if(sdata.deleteWhenDone && !sock->isOpen() && !sock->HasPendingTask())
{
delete sock;
_store.erase(it++); _store.erase(it++);
}
else else
++it; ++it;
} }
@ -777,10 +794,12 @@ void SocketSet::remove(TcpSocket *s)
_store.erase(s); _store.erase(s);
} }
void SocketSet::add(TcpSocket *s) void SocketSet::add(TcpSocket *s, bool deleteWhenDone /* = true */)
{ {
s->SetNonBlocking(true); s->SetNonBlocking(true);
_store.insert(s); SocketSetData sdata;
sdata.deleteWhenDone = deleteWhenDone;
_store[s] = sdata;
} }
#endif #endif

View file

@ -32,6 +32,8 @@ public:
TcpSocket(); TcpSocket();
virtual ~TcpSocket(); virtual ~TcpSocket();
virtual bool HasPendingTask() const { return false; }
bool open(const char *addr = NULL, unsigned int port = 0); bool open(const char *addr = NULL, unsigned int port = 0);
void close(); void close();
bool update(); // returns true if something interesting happened (incoming data, closed connection, etc) bool update(); // returns true if something interesting happened (incoming data, closed connection, etc)
@ -111,6 +113,11 @@ public:
HttpSocket(); HttpSocket();
virtual ~HttpSocket(); virtual ~HttpSocket();
virtual bool HasPendingTask() const
{
return ExpectMoreData() || _requestQ.size();
}
void SetKeepAlive(unsigned int secs) { _keep_alive = secs; } void SetKeepAlive(unsigned int secs) { _keep_alive = secs; }
void SetUserAgent(const std::string &s) { _user_agent = s; } void SetUserAgent(const std::string &s) { _user_agent = s; }
void SetAcceptEncoding(const std::string& s) { _accept_encoding = 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 SendGet(const std::string what, void *user = NULL);
bool QueueGet(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 GetStatusCode() const { return _status; }
unsigned int GetContentLen() { return _contentLen; } unsigned int GetContentLen() const { return _contentLen; }
bool ChunkedTransfer() { return _chunkedTransfer; } bool ChunkedTransfer() const { return _chunkedTransfer; }
bool ExpectMoreData() { return _remaining || _chunkedTransfer; } bool ExpectMoreData() const { return _remaining || _chunkedTransfer; }
Request *GetCurrentRequest() { return _inProgress ? &_curRequest : NULL; } const Request &GetCurrentRequest() const { return _curRequest; }
const char *Hdr(const char *h); const char *Hdr(const char *h) const;
protected: protected:
@ -182,7 +189,7 @@ protected:
#ifdef MINIHTTP_SUPPORT_SOCKET_SET #ifdef MINIHTTP_SUPPORT_SOCKET_SET
#include <set> #include <map>
namespace minihttp namespace minihttp
{ {
@ -193,12 +200,22 @@ public:
virtual ~SocketSet(); virtual ~SocketSet();
void deleteAll(); void deleteAll();
bool update(); bool update();
void add(TcpSocket *s); void add(TcpSocket *s, bool deleteWhenDone = true);
bool has(TcpSocket *s);
void remove(TcpSocket *s); void remove(TcpSocket *s);
inline size_t size() { return _store.size(); } inline size_t size() { return _store.size(); }
protected: protected:
std::set<TcpSocket*> _store;
struct SocketSetData
{
bool deleteWhenDone;
// To be extended
};
typedef std::map<TcpSocket*, SocketSetData> Store;
Store _store;
}; };
#endif #endif