API refinement, cleanups, bugfixes.

Proper per-request (re-)connecting if redirected with a 3xx code.
This commit is contained in:
fgenesis 2012-03-31 00:33:11 +02:00
parent 56b5e2d85c
commit c9cd4ad790
2 changed files with 55 additions and 34 deletions

View file

@ -238,7 +238,11 @@ bool TcpSocket::open(const char *host /* = NULL */, unsigned int port /* = 0 */)
{
if(isOpen())
{
return (!host || host == _host) && (!port || port == _lastport); // ok if same settings, fail if other settings
if( (host && host != _host) || (port && port != _lastport) )
close();
// ... and continue connecting to new host/port
else
return true; // still connected, to same host and port.
}
sockaddr_in addr;
@ -289,7 +293,7 @@ bool TcpSocket::SendBytes(const char *str, unsigned int len)
{
if(!SOCKETVALID(_s))
return false;
traceprint("SEND: '%s'\n", str);
//traceprint("SEND: '%s'\n", str);
return ::send(_s, str, len, 0) >= 0;
// TODO: check _GetError()
}
@ -379,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)
_followRedir(true), _alwaysHandle(false)
{
}
@ -400,45 +404,42 @@ bool HttpSocket::_OnUpdate()
// initiate transfer if queue is not empty, but the socket somehow forgot to proceed
if(_requestQ.size() && !_inProgress)
{
if(!(isOpen() || open())) // if the socket is closed, try to re-open
return false;
_DequeueMore();
}
return true;
}
bool HttpSocket::Download(const std::string& url, void *user /* = NULL */)
{
std::string host, file;
int port;
SplitURI(url, host, file, port);
if(port < 0)
port = 80;
if(!open(host.c_str(), port))
return false;
return SendGet(file.c_str(), user);
Request req;
req.user = user;
SplitURI(url, req.host, req.resource, req.port);
if(req.port < 0)
req.port = 80;
return SendGet(req, false);
}
bool HttpSocket::SendGet(const std::string what, void *user /* = NULL */)
{
Request req(what, user);
Request req(what, _host, _lastport, user);
return SendGet(req, false);
}
bool HttpSocket::QueueGet(const std::string what, void *user /* = NULL */)
{
Request req(what, user);
Request req(what, _host, _lastport, user);
return SendGet(req, true);
}
bool HttpSocket::SendGet(Request& req, bool enqueue)
{
if(req.host.empty() || !req.port)
return false;
std::stringstream r;
const char *crlf = "\r\n";
r << "GET " << req.resource << " HTTP/1.1" << crlf;
r << "Host: " << _host << crlf;
r << "Host: " << req.host << crlf;
if(_keep_alive)
{
r << "Connection: Keep-Alive" << crlf;
@ -469,8 +470,11 @@ bool HttpSocket::_EnqueueOrSend(const Request& req, bool forceQueue /* = false *
return true;
}
// ok, we can send directly
_inProgress = true;
return SendBytes(req.header.c_str(), req.header.length());
_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());
return _inProgress;
}
// called whenever a request is finished completely and the socket checks for more things to send
@ -479,23 +483,28 @@ void HttpSocket::_DequeueMore(void)
if(_inProgress)
_FinishRequest();
// _inProgress is known to be false here
if(_requestQ.size()) // still have other requests queued?
{
_curRequest = _requestQ.front();
if(SendBytes(_curRequest.header.c_str(), _curRequest.header.size())) // could we send?
{
_inProgress = true;
if(_EnqueueOrSend(_requestQ.front(), false)) // could we send?
_requestQ.pop(); // if so, we are done with this request
}
}
// otherwise, we are done for now. socket is kept alive for future sends. Nothing to do.
}
bool HttpSocket::_OpenRequest(const Request& req)
{
if(!open(req.host.c_str(), req.port))
return false;
_inProgress = true;
_curRequest = req;
return true;
}
void HttpSocket::_FinishRequest(void)
{
_OnRequestDone(); // notify about finished request
_inProgress = false;
_hdrs.clear();
}
void HttpSocket::_ProcessChunk(void)
@ -513,7 +522,7 @@ void HttpSocket::_ProcessChunk(void)
{
if(_remaining <= _recvSize) // it contains the rest of the chunk, including CRLF
{
_OnRecv(_readptr, _remaining - 2); // implicitly skip CRLF
_OnRecvInternal(_readptr, _remaining - 2); // implicitly skip CRLF
_readptr += _remaining;
_recvSize -= _remaining;
_remaining = 0; // done with this one.
@ -522,7 +531,7 @@ void HttpSocket::_ProcessChunk(void)
}
else // buffer did not yet arrive completely
{
_OnRecv(_readptr, _recvSize);
_OnRecvInternal(_readptr, _recvSize);
_remaining -= _recvSize;
_recvSize = 0; // done with the whole buffer, but not with the chunk
return; // nothing else to do here
@ -701,7 +710,7 @@ void HttpSocket::_OnData(void)
else if(_remaining && _recvSize) // something remaining? if so, we got a header earlier, but not all data
{
_remaining -= _recvSize;
_OnRecv(_readptr, _recvSize);
_OnRecvInternal(_readptr, _recvSize);
if(int(_remaining) < 0)
{
@ -722,6 +731,12 @@ void HttpSocket::_OnData(void)
// otherwise, the server sent just the header, with the data following in the next packet
}
void HttpSocket::_OnRecvInternal(char *buf, unsigned int size)
{
if(_status == 200 || _alwaysHandle)
_OnRecv(buf, size);
}
#endif
// ===========================

View file

@ -93,12 +93,14 @@ enum HttpCode
struct Request
{
Request() : user(NULL) {}
Request(const std::string& r, void *u = NULL)
: resource(r), user(u) {}
Request() : port(80), user(NULL) {}
Request(const std::string& h, const std::string& res, int p = 80, void *u = NULL)
: host(h), resource(res), port(80), user(u) {}
std::string host;
std::string header; // set by socket
std::string resource;
int port;
void *user;
};
@ -113,6 +115,7 @@ public:
void SetUserAgent(const std::string &s) { _user_agent = s; }
void SetAcceptEncoding(const std::string& s) { _accept_encoding = s; }
void SetFollowRedirect(bool follow) { _followRedir = follow; }
void SetAlwaysHandle(bool h) { _alwaysHandle = h; }
bool Download(const std::string& url, void *user = NULL);
bool SendGet(Request& what, bool enqueue);
@ -142,10 +145,12 @@ protected:
void _ProcessChunk();
bool _EnqueueOrSend(const Request& req, bool forceQueue = false);
void _DequeueMore();
bool _OpenRequest(const Request& req);
void _ParseHeader();
void _ParseHeaderFields(const char *s, size_t size);
bool _HandleStatus(); // Returns whether the processed request was successful, or not
void _FinishRequest();
void _OnRecvInternal(char *buf, unsigned int size);
std::string _user_agent;
std::string _accept_encoding; // Default empty.
@ -166,6 +171,7 @@ protected:
bool _chunkedTransfer;
bool _mustClose; // keep-alive specified, or not
bool _followRedir; // Default true. Follow 3xx redirects if this is set.
bool _alwaysHandle; // Also deliver to _OnRecv() if a non-success code was received.
};
} // end namespace minihttp