#include <cassert>
#include <list>
#include <iostream>
+#include <errno.h>
#include <boost/foreach.hpp>
#include <boost/algorithm/string/case_conv.hpp>
extern const int DEFAULT_HTTP_PORT = 80;
const char* CONTENT_TYPE_URL_ENCODED = "application/x-www-form-urlencoded";
-const int MAX_INFLIGHT_REQUESTS = 32;
+const unsigned int MAX_INFLIGHT_REQUESTS = 32;
const int ZLIB_DECOMPRESS_BUFFER_SIZE = 32 * 1024;
const int ZLIB_INFLATE_WINDOW_BITS = -MAX_WBITS;
const int GZIP_HEADER_ID1 = 31;
const int GZIP_HEADER_ID2 = 139;
const int GZIP_HEADER_METHOD_DEFLATE = 8;
-const int GZIP_HEADER_SIZE = 10;
+const unsigned int GZIP_HEADER_SIZE = 10;
const int GZIP_HEADER_FEXTRA = 1 << 2;
const int GZIP_HEADER_FNAME = 1 << 3;
const int GZIP_HEADER_COMMENT = 1 << 4;
// socket-level errors
virtual void handleError(int error)
- {
+ {
+ if (error == ENOENT) {
+ // name lookup failure
+ // we won't have an active request yet, so the logic below won't
+ // fire to actually call setFailure. Let's fail all of the requests
+ BOOST_FOREACH(Request_ptr req, sentRequests) {
+ req->setFailure(error, "hostname lookup failure");
+ }
+
+ BOOST_FOREACH(Request_ptr req, queuedRequests) {
+ req->setFailure(error, "hostname lookup failure");
+ }
+
+ // name lookup failure, abandon all requests on this connection
+ sentRequests.clear();
+ queuedRequests.clear();
+ }
+
NetChat::handleError(error);
- if (activeRequest) {
+ if (activeRequest) {
SG_LOG(SG_IO, SG_INFO, "HTTP socket error");
activeRequest->setFailure(error, "socket error");
activeRequest = NULL;
{
assert(!sentRequests.empty());
- activeRequest = sentRequests.front();
+ activeRequest = sentRequests.front();
activeRequest->responseStart(buffer);
state = STATE_GETTING_HEADERS;
buffer.clear();
Request_ptr r = queuedRequests.front();
requestBodyBytesToSend = r->requestBodyLength();
-
+
stringstream headerData;
string path = r->path();
+ assert(!path.empty());
string query = r->query();
string bodyData;
path = r->scheme() + "://" + r->host() + r->path();
}
- if (r->method() == "POST") {
+ if (r->requestBodyType() == CONTENT_TYPE_URL_ENCODED) {
headerData << r->method() << " " << path << " HTTP/1.1\r\n";
bodyData = query.substr(1); // URL-encode, drop the leading '?'
headerData << "Content-Type:" << CONTENT_TYPE_URL_ENCODED << "\r\n";
zlib.next_out = zlibOutputBuffer;
zlib.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
- if (contentGZip) {
+ if (contentGZip && !handleGZipHeader()) {
+ return;
+ }
+
+ int writtenSize = 0;
+ do {
+ int result = inflate(&zlib, Z_NO_FLUSH);
+ if (result == Z_OK || result == Z_STREAM_END) {
+ // nothing to do
+ } else {
+ SG_LOG(SG_IO, SG_WARN, "got Zlib error:" << result);
+ return;
+ }
+
+ writtenSize = ZLIB_DECOMPRESS_BUFFER_SIZE - zlib.avail_out;
+ if (result == Z_STREAM_END) {
+ break;
+ }
+ } while ((writtenSize == 0) && (zlib.avail_in > 0));
+
+ if (writtenSize > 0) {
+ activeRequest->processBodyBytes((const char*) zlibOutputBuffer, writtenSize);
+ }
+ }
+
+ bool handleGZipHeader()
+ {
// we clear this down to contentDeflate once the GZip header has been seen
- if (reqSize < GZIP_HEADER_SIZE) {
- return; // need more header bytes
+ if (zlib.avail_in < GZIP_HEADER_SIZE) {
+ return false; // need more header bytes
}
if ((zlibInflateBuffer[0] != GZIP_HEADER_ID1) ||
(zlibInflateBuffer[1] != GZIP_HEADER_ID2) ||
(zlibInflateBuffer[2] != GZIP_HEADER_METHOD_DEFLATE))
{
- return; // invalid GZip header
+ return false; // invalid GZip header
}
char flags = zlibInflateBuffer[3];
int gzipHeaderSize = GZIP_HEADER_SIZE;
if (flags & GZIP_HEADER_FEXTRA) {
gzipHeaderSize += 2;
- if (reqSize < gzipHeaderSize) {
- return; // need more header bytes
+ if (zlib.avail_in < gzipHeaderSize) {
+ return false; // need more header bytes
}
unsigned short extraHeaderBytes = *(reinterpret_cast<unsigned short*>(zlibInflateBuffer + GZIP_HEADER_FEXTRA));
}
gzipHeaderSize += extraHeaderBytes;
- if (reqSize < gzipHeaderSize) {
- return; // need more header bytes
+ if (zlib.avail_in < gzipHeaderSize) {
+ return false; // need more header bytes
}
}
if (flags & GZIP_HEADER_FNAME) {
gzipHeaderSize++;
- while (gzipHeaderSize <= reqSize) {
+ while (gzipHeaderSize <= zlib.avail_in) {
if (zlibInflateBuffer[gzipHeaderSize-1] == 0) {
break; // found terminating NULL character
}
if (flags & GZIP_HEADER_COMMENT) {
gzipHeaderSize++;
- while (gzipHeaderSize <= reqSize) {
+ while (gzipHeaderSize <= zlib.avail_in) {
if (zlibInflateBuffer[gzipHeaderSize-1] == 0) {
break; // found terminating NULL character
}
gzipHeaderSize += 2;
}
- if (reqSize < gzipHeaderSize) {
- return; // need more header bytes
+ if (zlib.avail_in < gzipHeaderSize) {
+ return false; // need more header bytes
}
zlib.next_in += gzipHeaderSize;
- zlib.avail_in = reqSize - gzipHeaderSize;
+ zlib.avail_in -= gzipHeaderSize;
// now we've processed the GZip header, can decode as deflate
contentGZip = false;
contentDeflate = true;
- }
-
- int writtenSize = 0;
- do {
- int result = inflate(&zlib, Z_NO_FLUSH);
- if (result == Z_OK || result == Z_STREAM_END) {
-
- } else {
- SG_LOG(SG_IO, SG_WARN, "got Zlib error:" << result);
- return;
- }
-
- writtenSize = ZLIB_DECOMPRESS_BUFFER_SIZE - zlib.avail_out;
- } while ((writtenSize == 0) && (zlib.avail_in > 0));
-
- if (writtenSize > 0) {
- activeRequest->processBodyBytes((const char*) zlibOutputBuffer, writtenSize);
- }
+ return true;
}
virtual void foundTerminator(void)
case STATE_GETTING_CHUNKED_BYTES:
setTerminator("\r\n");
state = STATE_GETTING_CHUNKED;
+ buffer.clear();
break;
private:
bool connectToHost()
{
- SG_LOG(SG_IO, SG_INFO, "HTTP connecting to " << host << ":" << port);
+ SG_LOG(SG_IO, SG_DEBUG, "HTTP connecting to " << host << ":" << port);
if (!open()) {
SG_LOG(SG_ALL, SG_WARN, "HTTP::Connection: connectToHost: open() failed");
// blank line after chunk data
return;
}
-
+
int chunkSize = 0;
int semiPos = buffer.find(';');
if (semiPos >= 0) {
_connections.erase(del);
} else {
if (it->second->shouldStartNext()) {
- SG_LOG(SG_IO, SG_INFO, "should start next, hmm");
it->second->tryStartNextRequest();
}