mirror of
https://github.com/Fishwaldo/minihttp.git
synced 2025-07-08 05:48:45 +00:00
More fixes, added sample code.
This commit is contained in:
parent
c9cd4ad790
commit
31e904abbb
4 changed files with 165 additions and 34 deletions
30
CMakeLists.txt
Normal file
30
CMakeLists.txt
Normal 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
65
main.cpp
Normal 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;
|
||||
}
|
67
minihttp.cpp
67
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<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
|
||||
|
|
37
minihttp.h
37
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 <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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue