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)
_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<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();
}
@ -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<TcpSocket*>::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<TcpSocket*>::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

View file

@ -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 <set>
#include <map>
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<TcpSocket*> _store;
struct SocketSetData
{
bool deleteWhenDone;
// To be extended
};
typedef std::map<TcpSocket*, SocketSetData> Store;
Store _store;
};
#endif