1
0
Fork 0
This commit is contained in:
Heiko Kernbach 2012-07-20 13:44:44 +02:00
commit 0cf91cab07
24 changed files with 443 additions and 226 deletions

View File

@ -1,19 +1,58 @@
v1.1.x (XXXX-XX-XX)
-------------------
* removed startup options "--server.require-keep-alive" and
"--server.secure-require-keep-alive".
The server will now behave as follows which should be more conforming to the
HTTP standard:
* if a client sends a "Connection: close" header, the server will close the
connection
* if a client sends a "Connection: keep-alive" header, the server will not
close the connection
* if a client does not send any "Connection" header, the server will assume
"keep-alive" if the request was an HTTP/1.1 request, and "close" if the
request was an HTTP/1.0 request
* internal optimisations for HTTP request parsing and response header handling
v1.x.x (2012-XX-XX)
-------------------
* changed implementation of TRI_BlockCrc32 algorithm to use 8 bytes at a time
* fixed Unicode unescaping bugs for \f and surrogate pairs in BasicsC/strings.c
* fixed issue #122: arangod doesn't start if <log.file> cannot be created
* fixed issue #121: wrong collection size reported
* fixed issue #98: Unable to change journalSize
* fixed issue #88: fds not closed
* fixed escaping of document data in HTML admin front end
* added server startup option --server.disable-admin-interface to turn off the HTML admin interface
* added HTTP basic authentication, use "--server.http-auth yes" to enable
* honor server startup option --database.maximal-journal-size when creating new collections without specific journalsize setting. Previously, these collections were always created with journal file sizes of 32 MB and the --database.maximal-journal-size setting was ignored
* added server startup option --server.disable-admin-interface to turn off the
HTML admin interface
* honor server startup option --database.maximal-journal-size when creating new
collections without specific journalsize setting. Previously, these
collections were always created with journal file sizes of 32 MB and the
--database.maximal-journal-size setting was ignored
* added server startup option --database.wait-for-sync to control the default
behavior
* renamed "--unit-tests" to "--javascript.unit-tests"
v1.0.alpha3 (2012-06-30)
------------------------
* fixed issue #116: createCollection=create option doesn't work
* fixed issue #115: Compilation issue under OSX 10.7 Lion & 10.8 Mountain Lion (homebrew)
* fixed issue #115: Compilation issue under OSX 10.7 Lion & 10.8 Mountain Lion
(homebrew)
* fixed issue #114: image not found
@ -29,7 +68,8 @@ v1.0.alpha2 (2012-06-24)
* fixed issue #103: Should we cleanup the directory structure
* fixed issue #100: "count" attribute exists in cursor response with "count: false"
* fixed issue #100: "count" attribute exists in cursor response with "count:
false"
* fixed issue #84 explain command
@ -53,7 +93,8 @@ v1.0.alpha2 (2012-06-24)
* fixed several range-related assertion failures in the AQL query optimiser
* fixed AQL query optimisations for some edge cases (e.g. nested subqueries with invalid constant filter expressions)
* fixed AQL query optimisations for some edge cases (e.g. nested subqueries with
invalid constant filter expressions)
v1.0.alpha1 (2012-05-28)

View File

@ -991,7 +991,7 @@ void runTest (int mode)
list1 = GeoIndex_NearestCountPoints(gi,&gcp1,222);
gcmass(365,list1,222,26557002);
gcp1.latitude = 89,4;
gcp1.latitude = 89.0;
gcp1.longitude= 179.9;
list1 = GeoIndex_PointsWithinRadius(gi,&gcp1,930000.0);
gcmass(367,list1, 1857, 12304881);

View File

@ -621,9 +621,9 @@ void printBitarray(TRI_bitarray_t* ba) {
// ...........................................................................
printf("\n");
printf("THERE ARE %lu COLUMNS\n",ba->_numColumns);
printf("THE NUMBER OF ALLOCATED BLOCKS IN EACH COLUMN IS %lu\n",ba->_numBlocksInColumn);
printf("THE NUMBER OF THE LAST BLOCK USED IS %lu\n",ba->_lastBlockUsed);
printf("THERE ARE %lu COLUMNS\n", (unsigned long) ba->_numColumns);
printf("THE NUMBER OF ALLOCATED BLOCKS IN EACH COLUMN IS %lu\n", (unsigned long) ba->_numBlocksInColumn);
printf("THE NUMBER OF THE LAST BLOCK USED IS %lu\n", (unsigned long) ba->_lastBlockUsed);
printf("\n");
@ -634,7 +634,7 @@ void printBitarray(TRI_bitarray_t* ba) {
}
printf("==\n");
for (oo = 0; oo < BITARRAY_MASTER_TABLE_BLOCKSIZE; ++oo) {
printf("ROW %lu: ", ((bb * BITARRAY_MASTER_TABLE_BLOCKSIZE) + oo) );
printf("ROW %llu: ", (unsigned long long) ((bb * BITARRAY_MASTER_TABLE_BLOCKSIZE) + oo) );
for (j = 0; j < ba->_numColumns; ++j) {
BitColumn_t* column;
bit_column_int_t* bitInteger;

View File

@ -31,7 +31,7 @@
using namespace triagens::basics;
using namespace triagens::rest;
// -----------------------------------------------------------------------------
// --SECTION-- constructors and destructors
// -----------------------------------------------------------------------------
@ -47,11 +47,12 @@ using namespace triagens::rest;
BatchJob::BatchJob (HttpServer* server, HttpHandler* handler)
: GeneralServerJob<HttpServer, HttpHandler>(server, handler),
_doneAccomplisher(NOONE),
_handlers(),
_subjobs(),
_doneLock(),
_setupLock(),
_jobsDone(0),
_done(false),
_cleanup(false) {
}
@ -60,6 +61,7 @@ BatchJob::BatchJob (HttpServer* server, HttpHandler* handler)
////////////////////////////////////////////////////////////////////////////////
BatchJob::~BatchJob () {
MUTEX_LOCKER(_setupLock);
}
////////////////////////////////////////////////////////////////////////////////
@ -81,11 +83,12 @@ BatchJob::~BatchJob () {
void BatchJob::jobDone (BatchSubjob* subjob) {
_handler->addResponse(subjob->getHandler());
_doneLock.lock();
_doneLock.lock();
++_jobsDone;
if (_jobsDone >= _handlers.size()) {
// all sub-jobs are done
if (_cleanup) {
_doneLock.unlock();
@ -93,14 +96,15 @@ void BatchJob::jobDone (BatchSubjob* subjob) {
GeneralServerJob<HttpServer, HttpHandler>::cleanup();
}
else {
_done = true;
_doneAccomplisher = ASYNC;
_subjobs.clear();
_doneLock.unlock();
cleanup();
}
}
else {
// still something to do
_subjobs.erase(subjob);
_doneLock.unlock();
}
@ -129,18 +133,22 @@ Job::status_e BatchJob::work () {
if (_shutdown != 0) {
return Job::JOB_DONE;
}
// we must grab this lock so no one else can kill us while we're iterating
// over the sub handlers
MUTEX_LOCKER(_setupLock);
// handler::execute() is called to prepare the batch handler
// if it returns anything else but HANDLER_DONE, this is an
// indication of an error
if (_handler->execute() != Handler::HANDLER_DONE) {
// handler failed
_done = true;
_doneAccomplisher = DIRECT;
return Job::JOB_FAILED;
}
// handler did not fail
// setup did not fail
bool hasAsync = false;
_handlers = _handler->subhandlers();
@ -152,20 +160,29 @@ Job::status_e BatchJob::work () {
executeDirectHandler(handler);
}
else {
hasAsync = true;
if (!hasAsync) {
// we must do this ourselves. it is not safe to have the dispatcherThread
// call this method because the job might be deleted before that
_handler->setDispatcherThread(0);
hasAsync = true;
}
createSubjob(handler);
}
}
if (hasAsync) {
// we must do this ourselves. it is not safe to have the dispatcherthread
// call this method because the job might be deleted before that
setDispatcherThread(0);
return Job::JOB_DETACH;
if (!hasAsync) {
// only jobs executed directly, we're done and let the dispatcher kill us
return Job::JOB_DONE;
}
return Job::JOB_DONE;
MUTEX_LOCKER(_doneLock);
if (_doneAccomplisher == DIRECT) {
// all jobs already done. last job was finished by direct execution
return Job::JOB_DONE;
}
// someone else must kill this job
return Job::JOB_DETACH;
}
////////////////////////////////////////////////////////////////////////////////
@ -178,7 +195,7 @@ void BatchJob::cleanup () {
{
MUTEX_LOCKER(_doneLock);
if (_done) {
if (_doneAccomplisher != NOONE) {
done = true;
}
else {
@ -197,24 +214,26 @@ void BatchJob::cleanup () {
bool BatchJob::beginShutdown () {
LOGGER_TRACE << "shutdown job " << static_cast<Job*>(this);
_shutdown = 1;
bool cleanup;
{
MUTEX_LOCKER(_abandonLock);
MUTEX_LOCKER(_doneLock);
_shutdown = 1;
for (set<BatchSubjob*>::iterator i = _subjobs.begin(); i != _subjobs.end(); ++i) {
(*i)->abandon();
{
MUTEX_LOCKER(_abandonLock);
for (set<BatchSubjob*>::iterator i = _subjobs.begin(); i != _subjobs.end(); ++i) {
(*i)->abandon();
}
}
_doneAccomplisher = TASK;
cleanup = _cleanup;
}
MUTEX_LOCKER(_doneLock);
if (_cleanup) {
delete this;
}
else {
_done = true;
if (cleanup) {
GeneralServerJob<HttpServer, HttpHandler>::cleanup();
}
return true;
@ -275,12 +294,12 @@ void BatchJob::executeDirectHandler (HttpHandler* handler) {
if (status == Handler::HANDLER_DONE) {
_handler->addResponse(handler);
}
MUTEX_LOCKER(_doneLock);
++_jobsDone;
if (_jobsDone >= _handlers.size()) {
_done = true;
_doneAccomplisher = DIRECT;
}
}

View File

@ -60,6 +60,17 @@ namespace triagens {
BatchJob (BatchJob const&);
BatchJob& operator= (BatchJob const&);
////////////////////////////////////////////////////////////////////////////////
/// @brief who accomplished the batch job?
////////////////////////////////////////////////////////////////////////////////
enum Accomplisher {
NOONE = 0,
TASK = 1,
DIRECT = 2,
ASYNC = 3
};
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
@ -168,6 +179,12 @@ namespace triagens {
protected:
////////////////////////////////////////////////////////////////////////////////
/// @brief who finalised the batch job?
////////////////////////////////////////////////////////////////////////////////
Accomplisher _doneAccomplisher;
////////////////////////////////////////////////////////////////////////////////
/// @brief list of handlers
////////////////////////////////////////////////////////////////////////////////
@ -186,18 +203,18 @@ namespace triagens {
triagens::basics::Mutex _doneLock;
////////////////////////////////////////////////////////////////////////////////
/// @brief setup lock
////////////////////////////////////////////////////////////////////////////////
triagens::basics::Mutex _setupLock;
////////////////////////////////////////////////////////////////////////////////
/// @brief number of completed jobs
////////////////////////////////////////////////////////////////////////////////
size_t _jobsDone;
////////////////////////////////////////////////////////////////////////////////
/// @brief done flag
////////////////////////////////////////////////////////////////////////////////
bool _done;
////////////////////////////////////////////////////////////////////////////////
/// @brief cleanup seen
////////////////////////////////////////////////////////////////////////////////

View File

@ -74,12 +74,17 @@ BatchSubjob::~BatchSubjob () {
////////////////////////////////////////////////////////////////////////////////
void BatchSubjob::cleanup () {
bool abandon;
{
MUTEX_LOCKER(_abandonLock);
abandon = _abandon;
}
if (! _abandon) {
_parent->jobDone(this);
}
if (! abandon) {
// signal the parent (batch job) that a subjob is done
_parent->jobDone(this);
}
delete this;

View File

@ -64,6 +64,8 @@ RestBatchHandler::~RestBatchHandler () {
// delete protobuf message
delete _outputMessages;
}
destroyHandlers();
}
////////////////////////////////////////////////////////////////////////////////
@ -207,22 +209,46 @@ void RestBatchHandler::addResponse (HttpHandler* handler) {
void RestBatchHandler::assembleResponse () {
assert(_missingResponses == 0);
size_t messageSize = _outputMessages->ByteSize();
// allocate output buffer
char* output = (char*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(char) * messageSize, false);
if (output == NULL) {
generateError(HttpResponse::SERVER_ERROR,
TRI_ERROR_OUT_OF_MEMORY,
"out of memory");
return;
}
_response = new HttpResponse(HttpResponse::OK);
_response->setContentType(getContentType());
string data;
if (!_outputMessages->SerializeToString(&data)) {
// content of message is binary, cannot use null-terminated std::string
if (!_outputMessages->SerializeToArray(output, messageSize)) {
TRI_Free(TRI_UNKNOWN_MEM_ZONE, output);
delete _response;
generateError(HttpResponse::SERVER_ERROR,
TRI_ERROR_OUT_OF_MEMORY,
"out of memory");
return;
}
_response->body().appendText(data);
/*
for (char* x = output; x < output + messageSize; ++x) {
if (*x >= ' ' && *x <= 'z') {
printf("%c", *x);
}
else if (*x == '\n' || *x == '\0') {
printf("\n");
}
else {
printf(".");
}
}
*/
_response->body().appendText(output, messageSize);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, output);
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -239,6 +239,22 @@ namespace triagens {
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief looks up a key
////////////////////////////////////////////////////////////////////////////////
KeyValue const* lookup (char const* key, const size_t keyLength) const {
KeyValue l(key, keyLength);
KeyValue const& f = _array.findElement(l);
if (f._key != 0 && strcmp(f._key, key) == 0) {
return &f;
}
else {
return 0;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

View File

@ -55,6 +55,12 @@
#define TRI_TIMER_GET(name) (TRI_microtime() - TRI_TIMER_NAME(name))
////////////////////////////////////////////////////////////////////////////////
/// @brief dump a timer to stdout
////////////////////////////////////////////////////////////////////////////////
#define TRI_TIMER_DUMP(name) fprintf(stdout, "timer %s: %f\n", #name, TRI_TIMER_GET(name));
////////////////////////////////////////////////////////////////////////////////
/// @brief log a timer value to the log in debug mode
////////////////////////////////////////////////////////////////////////////////

View File

@ -295,7 +295,7 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
bool handleRequest (CT * task, GeneralHandler* handler) {
registerHandler(handler, task);
this->registerHandler(handler, task);
// execute handler and (possibly) requeue
while (true) {
@ -355,7 +355,7 @@ namespace triagens {
}
////////////////////////////////////////////////////////////////////////////////
/// @brief shut downs a handler for a task
/// @brief shuts down a handler for a task
////////////////////////////////////////////////////////////////////////////////
void shutdownHandlerByTask (Task* task) {

View File

@ -200,12 +200,15 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
void cleanup () {
bool abandon;
{
MUTEX_LOCKER(_abandonLock);
abandon = _abandon;
}
if (! _abandon) {
_server->jobDone(this);
}
if (! abandon) {
_server->jobDone(this);
}
delete this;

View File

@ -57,7 +57,6 @@ ApplicationHttpServer::ApplicationHttpServer (ApplicationScheduler* applicationS
_applicationScheduler(applicationScheduler),
_applicationDispatcher(applicationDispatcher),
_showPort(true),
_requireKeepAlive(false),
_httpServers(),
_httpPorts(),
_httpAddressPorts() {
@ -168,10 +167,6 @@ void ApplicationHttpServer::setupOptions (map<string, ProgramOptionsDescription>
("server.port", &_httpPorts, "listen port or address:port")
;
}
options[ApplicationServer::OPTIONS_SERVER + ":help-extended"]
("server.require-keep-alive", "close connection, if keep-alive is missing")
;
}
////////////////////////////////////////////////////////////////////////////////
@ -179,10 +174,6 @@ void ApplicationHttpServer::setupOptions (map<string, ProgramOptionsDescription>
////////////////////////////////////////////////////////////////////////////////
bool ApplicationHttpServer::parsePhase2 (ProgramOptions& options) {
if (options.has("server.require-keep-alive")) {
_requireKeepAlive= true;
}
for (vector<string>::const_iterator i = _httpPorts.begin(); i != _httpPorts.end(); ++i) {
addPort(*i);
}
@ -275,11 +266,6 @@ HttpServer* ApplicationHttpServer::buildHttpServer (HttpServer* httpServer,
httpServer = new HttpServer(scheduler, dispatcher);
}
// update close-without-keep-alive flag
if (_requireKeepAlive) {
httpServer->setCloseWithoutKeepAlive(true);
}
// keep a list of active server
_httpServers.push_back(httpServer);

View File

@ -232,12 +232,6 @@ namespace triagens {
bool _showPort;
////////////////////////////////////////////////////////////////////////////////
/// @brief is keep-alive required to keep the connection open
////////////////////////////////////////////////////////////////////////////////
bool _requireKeepAlive;
////////////////////////////////////////////////////////////////////////////////
/// @brief all constructed http servers
////////////////////////////////////////////////////////////////////////////////

View File

@ -235,18 +235,22 @@ bool HttpCommTask::processRead () {
_readBuffer->erase_front(_bodyPosition + _bodyLength);
_requestPending = true;
string connectionType = StringUtils::tolower(StringUtils::trim(_request->header("connection")));
string connectionType = StringUtils::tolower(_request->header("connection"));
if (connectionType == "close") {
// client has sent an explicit "Connection: Close" header. we should close the connection
LOGGER_DEBUG << "connection close requested by client";
_closeRequested = true;
}
else if (_server->getCloseWithoutKeepAlive() && connectionType != "keep-alive") {
else if (_request->isHttp10() && connectionType != "keep-alive") {
// HTTP 1.0 request, and no "Connection: Keep-Alive" header sent
// we should close the connection
LOGGER_DEBUG << "no keep-alive, connection close requested by client";
_closeRequested = true;
}
// we keep the connection open in all other cases (HTTP 1.1 or Keep-Alive header sent)
_readPosition = 0;
_bodyPosition = 0;
_bodyLength = 0;
@ -286,7 +290,15 @@ bool HttpCommTask::processRead () {
void HttpCommTask::addResponse (HttpResponse* response) {
StringBuffer * buffer;
if (_closeRequested) {
response->setHeader("connection", 10, "Close");
}
else {
// keep-alive is the default
response->setHeader("connection", 10, "Keep-Alive");
}
// save header
buffer = new StringBuffer(TRI_UNKNOWN_MEM_ZONE);
response->writeHeader(buffer);

View File

@ -47,8 +47,7 @@ using namespace triagens::rest;
////////////////////////////////////////////////////////////////////////////////
HttpServer::HttpServer (Scheduler* scheduler, Dispatcher* dispatcher)
: GeneralServerDispatcher<HttpServer, HttpHandlerFactory, HttpCommTask>(scheduler, dispatcher),
_closeWithoutKeepAlive(false) {
: GeneralServerDispatcher<HttpServer, HttpHandlerFactory, HttpCommTask>(scheduler, dispatcher) {
}
////////////////////////////////////////////////////////////////////////////////
@ -80,22 +79,6 @@ Dispatcher* HttpServer::getDispatcher () {
return _dispatcher;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief checks if to close connection if keep-alive is missing
////////////////////////////////////////////////////////////////////////////////
bool HttpServer::getCloseWithoutKeepAlive () const {
return _closeWithoutKeepAlive;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief close connection if keep-alive is missing
////////////////////////////////////////////////////////////////////////////////
void HttpServer::setCloseWithoutKeepAlive (bool value) {
_closeWithoutKeepAlive = value;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

View File

@ -39,7 +39,6 @@
namespace triagens {
namespace rest {
class HttpHandlerFactory;
class HttpListenTask;
// -----------------------------------------------------------------------------
// --SECTION-- class HttpServer
@ -105,38 +104,6 @@ namespace triagens {
Dispatcher* getDispatcher ();
////////////////////////////////////////////////////////////////////////////////
/// @brief checks if to close connection if keep-alive is missing
////////////////////////////////////////////////////////////////////////////////
bool getCloseWithoutKeepAlive () const;
////////////////////////////////////////////////////////////////////////////////
/// @brief close connection if keep-alive is missing
////////////////////////////////////////////////////////////////////////////////
void setCloseWithoutKeepAlive (bool value = true);
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- private variables
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup HttpServer
/// @{
////////////////////////////////////////////////////////////////////////////////
private:
////////////////////////////////////////////////////////////////////////////////
/// @brief close connection without explicit keep-alive
////////////////////////////////////////////////////////////////////////////////
bool _closeWithoutKeepAlive;
};
}
}

View File

@ -97,7 +97,6 @@ ApplicationHttpsServer::ApplicationHttpsServer (ApplicationScheduler* applicatio
_applicationScheduler(applicationScheduler),
_applicationDispatcher(applicationDispatcher),
_showPort(true),
_requireKeepAlive(false),
_sslProtocol(3),
_sslCacheMode(0),
_sslOptions(SSL_OP_TLS_ROLLBACK_BUG),
@ -197,7 +196,6 @@ void ApplicationHttpsServer::setupOptions (map<string, ProgramOptionsDescription
}
options[ApplicationServer::OPTIONS_SERVER + ":help-ssl"]
("server.secure-require-keep-alive", "close connection, if keep-alive is missing")
("server.keyfile", &_httpsKeyfile, "keyfile for SSL connections")
("server.cafile", &_cafile, "file containing the CA certificates of clients")
("server.ssl-protocol", &_sslProtocol, "1 = SSLv2, 2 = SSLv3, 3 = SSLv23, 4 = TLSv1")
@ -212,12 +210,6 @@ void ApplicationHttpsServer::setupOptions (map<string, ProgramOptionsDescription
bool ApplicationHttpsServer::parsePhase2 (ProgramOptions& options) {
// check keep alive
if (options.has("server.secure-require-keep-alive")) {
_requireKeepAlive= true;
}
// add ports
for (vector<string>::const_iterator i = _httpsPorts.begin(); i != _httpsPorts.end(); ++i) {
addPort(*i);
@ -320,11 +312,6 @@ HttpsServer* ApplicationHttpsServer::buildHttpsServer (vector<AddressPort> const
// create new server
HttpsServer* httpsServer = new HttpsServer(scheduler, dispatcher, _sslContext);
// update close-without-keep-alive flag
if (_requireKeepAlive) {
httpsServer->setCloseWithoutKeepAlive(true);
}
// keep a list of active server
_httpsServers.push_back(httpsServer);

View File

@ -230,12 +230,6 @@ namespace triagens {
bool _showPort;
////////////////////////////////////////////////////////////////////////////////
/// @brief is keep-alive required to keep the connection open
////////////////////////////////////////////////////////////////////////////////
bool _requireKeepAlive;
////////////////////////////////////////////////////////////////////////////////
/// @brief all constructed http servers
////////////////////////////////////////////////////////////////////////////////

View File

@ -57,7 +57,8 @@ HttpRequest::HttpRequest ()
: _connectionInfo(),
_type(HTTP_REQUEST_ILLEGAL),
_prefix(),
_suffix() {
_suffix(),
_version(HTTP_1_0) {
}
////////////////////////////////////////////////////////////////////////////////
@ -112,6 +113,22 @@ void HttpRequest::setRequestType (HttpRequestType newType) {
_type = newType;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns whether HTTP version is 1.0
////////////////////////////////////////////////////////////////////////////////
bool HttpRequest::isHttp10 () {
return _version == HTTP_1_0;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns whether HTTP version is 1.1
////////////////////////////////////////////////////////////////////////////////
bool HttpRequest::isHttp11 () {
return _version == HTTP_1_1;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

View File

@ -87,6 +87,15 @@ namespace triagens {
HTTP_REQUEST_ILLEGAL
};
////////////////////////////////////////////////////////////////////////////////
/// @brief http version
////////////////////////////////////////////////////////////////////////////////
enum HttpVersion {
HTTP_1_0,
HTTP_1_1
};
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
@ -156,6 +165,18 @@ namespace triagens {
void setRequestType (HttpRequestType newType);
////////////////////////////////////////////////////////////////////////////////
/// @brief return whether HTTP version is 1.0
////////////////////////////////////////////////////////////////////////////////
bool isHttp10 ();
////////////////////////////////////////////////////////////////////////////////
/// @brief return whether HTTP version is 1.1
////////////////////////////////////////////////////////////////////////////////
bool isHttp11 ();
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the full request path
/// The request path consists of the URL without the host and without any
@ -379,6 +400,12 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
vector<string> _suffix;
////////////////////////////////////////////////////////////////////////////////
/// @brief the HTTP version
////////////////////////////////////////////////////////////////////////////////
HttpVersion _version;
};
}
}

View File

@ -382,6 +382,40 @@ int HttpRequestPlain::setBody (char const* newBody, size_t length) {
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief determine the header type
////////////////////////////////////////////////////////////////////////////////
HttpRequest::HttpRequestType HttpRequestPlain::getRequestType (const char* ptr, const size_t length) {
switch (length) {
case 3:
if (ptr[0] == 'g' && ptr[1] == 'e' && ptr[2] == 't') {
return HTTP_REQUEST_GET;
}
if (ptr[0] == 'p' && ptr[1] == 'u' && ptr[2] == 't') {
return HTTP_REQUEST_PUT;
}
break;
case 4:
if (ptr[0] == 'p' && ptr[1] == 'o' && ptr[2] == 's' && ptr[3] == 't') {
return HTTP_REQUEST_POST;
}
if (ptr[0] == 'h' && ptr[1] == 'e' && ptr[2] == 'a' && ptr[3] == 'd') {
return HTTP_REQUEST_HEAD;
}
break;
case 6:
if (ptr[0] == 'd' && ptr[1] == 'e' && ptr[2] == 'l' && ptr[3] == 'e' && ptr[4] == 't' && ptr[5] == 'e') {
return HTTP_REQUEST_DELETE;
}
break;
}
return HTTP_REQUEST_ILLEGAL;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief parses the http header
////////////////////////////////////////////////////////////////////////////////
@ -427,6 +461,7 @@ void HttpRequestPlain::parseHeader (char* ptr, size_t length) {
// extract the value
keyEnd = e;
// trim value from the start
while (e < end && *e == ' ') {
++e;
}
@ -470,6 +505,32 @@ void HttpRequestPlain::parseHeader (char* ptr, size_t length) {
else {
valueEnd = e;
// HTTP protocol version is expected next
// trim value
while (e < end && *e == ' ') {
++e;
}
if (end - e > strlen("http/1.x")) {
if ((e[0] == 'h' || e[0] == 'H') &&
(e[1] == 't' || e[1] == 'T') &&
(e[2] == 't' || e[2] == 'T') &&
(e[3] == 'p' || e[3] == 'P') &&
e[4] == '/' &&
e[5] == '1' &&
e[6] == '.') {
if (e[7] == '1') {
_version = HTTP_1_1;
}
else {
_version = HTTP_1_0;
}
e += strlen("http/1.x");
}
}
// go on until eol
while (e < end && *e != '\n') {
++e;
}
@ -487,21 +548,7 @@ void HttpRequestPlain::parseHeader (char* ptr, size_t length) {
}
// check the key
if (strcmp(keyBegin, "post") == 0) {
_type = HTTP_REQUEST_POST;
}
else if (strcmp(keyBegin, "put") == 0) {
_type = HTTP_REQUEST_PUT;
}
else if (strcmp(keyBegin, "delete") == 0) {
_type = HTTP_REQUEST_DELETE;
}
else if (strcmp(keyBegin, "get") == 0) {
_type = HTTP_REQUEST_GET;
}
else if (strcmp(keyBegin, "head") == 0) {
_type = HTTP_REQUEST_HEAD;
}
_type = getRequestType(keyBegin, keyEnd - keyBegin);
// extract the path and decode the url and parameters
if (_type != HTTP_REQUEST_ILLEGAL) {
@ -577,21 +624,7 @@ void HttpRequestPlain::parseHeader (char* ptr, size_t length) {
}
// check the key
if (strcmp(keyBegin, "post") == 0) {
_type = HTTP_REQUEST_POST;
}
else if (strcmp(keyBegin, "put") == 0) {
_type = HTTP_REQUEST_PUT;
}
else if (strcmp(keyBegin, "delete") == 0) {
_type = HTTP_REQUEST_DELETE;
}
else if (strcmp(keyBegin, "get") == 0) {
_type = HTTP_REQUEST_GET;
}
else if (strcmp(keyBegin, "head") == 0) {
_type = HTTP_REQUEST_HEAD;
}
_type = getRequestType(keyBegin, keyEnd - keyBegin);
}
}

View File

@ -203,6 +203,12 @@ namespace triagens {
private:
////////////////////////////////////////////////////////////////////////////////
/// @brief determine the header type
////////////////////////////////////////////////////////////////////////////////
static HttpRequest::HttpRequestType getRequestType (const char*, const size_t);
////////////////////////////////////////////////////////////////////////////////
/// @brief parses the http header
////////////////////////////////////////////////////////////////////////////////

View File

@ -144,7 +144,7 @@ HttpResponse::HttpResponseCode HttpResponse::responseCode (const string& str) {
HttpResponse::HttpResponse ()
: _code(NOT_IMPLEMENTED),
_headers(5),
_headers(6),
_body(TRI_UNKNOWN_MEM_ZONE),
_isHeadResponse(false),
_bodySize(0),
@ -157,7 +157,7 @@ HttpResponse::HttpResponse ()
HttpResponse::HttpResponse (string const& header)
: _code(NOT_IMPLEMENTED),
_headers(5),
_headers(6),
_body(TRI_UNKNOWN_MEM_ZONE),
_isHeadResponse(false),
_bodySize(0),
@ -171,38 +171,15 @@ HttpResponse::HttpResponse (string const& header)
HttpResponse::HttpResponse (HttpResponseCode code)
: _code(code),
_headers(5),
_headers(6),
_body(TRI_UNKNOWN_MEM_ZONE),
_isHeadResponse(false),
_bodySize(0),
_freeables() {
char* headerBuffer = StringUtils::duplicate("server\ntriagens GmbH High-Performance HTTP Server\n"
"connection\nKeep-Alive\n"
"content-type\ntext/plain;charset=utf-8\n");
_freeables.push_back(headerBuffer);
bool key = true;
char* startKey = headerBuffer;
char* startValue = 0;
char* end = headerBuffer + strlen(headerBuffer);
for (char* ptr = headerBuffer; ptr < end; ++ptr) {
if (*ptr == '\n') {
*ptr = '\0';
if (key) {
startValue = ptr + 1;
key = false;
}
else {
_headers.insert(startKey, startValue);
startKey = ptr + 1;
startValue = 0;
key = true;
}
}
}
_headers.insert("server", 6, "triagens GmbH High-Performance HTTP Server");
_headers.insert("connection", 10, "Keep-Alive");
_headers.insert("content-type", 12, "text/plain; charset=utf-8");
}
////////////////////////////////////////////////////////////////////////////////
@ -245,7 +222,7 @@ size_t HttpResponse::contentLength () {
return _bodySize;
}
else {
Dictionary<char const*>::KeyValue const* kv = _headers.lookup("content-length");
Dictionary<char const*>::KeyValue const* kv = _headers.lookup("content-length", 14);
if (kv == 0) {
return 0;
@ -260,7 +237,7 @@ size_t HttpResponse::contentLength () {
////////////////////////////////////////////////////////////////////////////////
void HttpResponse::setContentType (string const& contentType) {
setHeader("content-type", contentType);
setHeader("content-type", 12, contentType);
}
////////////////////////////////////////////////////////////////////////////////
@ -283,6 +260,21 @@ string HttpResponse::header (string const& key) const {
/// @brief returns a header field
////////////////////////////////////////////////////////////////////////////////
string HttpResponse::header (const char* key, const size_t keyLength) const {
Dictionary<char const*>::KeyValue const* kv = _headers.lookup(key, keyLength);
if (kv == 0) {
return "";
}
else {
return kv->_value;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns a header field
////////////////////////////////////////////////////////////////////////////////
string HttpResponse::header (string const& key, bool& found) const {
string k = StringUtils::tolower(key);
Dictionary<char const*>::KeyValue const* kv = _headers.lookup(k.c_str());
@ -297,6 +289,23 @@ string HttpResponse::header (string const& key, bool& found) const {
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns a header field
////////////////////////////////////////////////////////////////////////////////
string HttpResponse::header (const char* key, const size_t keyLength, bool& found) const {
Dictionary<char const*>::KeyValue const* kv = _headers.lookup(key, keyLength);
if (kv == 0) {
found = false;
return "";
}
else {
found = true;
return kv->_value;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns all header fields
////////////////////////////////////////////////////////////////////////////////
@ -324,6 +333,36 @@ map<string, string> HttpResponse::headers () const {
/// @brief sets a header field
////////////////////////////////////////////////////////////////////////////////
void HttpResponse::setHeader (const char* key, const size_t keyLength, string const& value) {
if (value.empty()) {
_headers.erase(key);
}
else {
char const* v = StringUtils::duplicate(value);
_headers.insert(key, keyLength, v);
_freeables.push_back(v);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief sets a header field
////////////////////////////////////////////////////////////////////////////////
void HttpResponse::setHeader (const char* key, const size_t keyLength, const char* value) {
if (*value == '\0') {
_headers.erase(key);
}
else {
_headers.insert(key, keyLength, value);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief sets a header field
////////////////////////////////////////////////////////////////////////////////
void HttpResponse::setHeader (string const& key, string const& value) {
string lk = StringUtils::tolower(key);
@ -490,11 +529,11 @@ void HttpResponse::writeHeader (StringBuffer* output) {
}
// ignore content-length
if (strcmp(key, "content-length") == 0) {
if (*key == 'c' && (strcmp(key, "content-length") == 0)) {
continue;
}
if (strcmp(key, "transfer-encoding") == 0) {
if (*key == 't' && (strcmp(key, "transfer-encoding") == 0)) {
seenTransferEncoding = true;
transferEncoding = begin->_value;
continue;
@ -509,7 +548,7 @@ void HttpResponse::writeHeader (StringBuffer* output) {
}
if (seenTransferEncoding && transferEncoding == "chunked") {
output->appendText("transfer-encoding: chunked\r\n");
output->appendText("transfer-encoding: chunked\r\n\r\n");
}
else {
if (seenTransferEncoding) {
@ -527,10 +566,9 @@ void HttpResponse::writeHeader (StringBuffer* output) {
output->appendInteger(_body.length());
}
output->appendText("\r\n");
output->appendText("\r\n\r\n");
}
output->appendText("\r\n");
// end of header, body to follow
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -238,11 +238,32 @@ namespace triagens {
///
/// Returns the value of a header field with given name. If no header field
/// with the given name was specified by the client, the empty string is
/// returned. found is try if the client specified the header field.
/// returned.
/// The header field name must already be trimmed and lower-cased
////////////////////////////////////////////////////////////////////////////////
string header (const char*, const size_t) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief returns a header field
///
/// Returns the value of a header field with given name. If no header field
/// with the given name was specified by the client, the empty string is
/// returned. found is set if the client specified the header field.
////////////////////////////////////////////////////////////////////////////////
string header (string const& field, bool& found) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief returns a header field
///
/// Returns the value of a header field with given name. If no header field
/// with the given name was specified by the client, the empty string is
/// returned. found is set if the client specified the header field.
////////////////////////////////////////////////////////////////////////////////
string header (const char*, const size_t, bool& found) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief returns all header fields
///
@ -254,7 +275,26 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
/// @brief sets a header field
///
/// The key is automatically converted to lower case.
/// The key must be lowercased and trimmed already
/// The key string must remain valid until the response is destroyed
////////////////////////////////////////////////////////////////////////////////
void setHeader (const char*, const size_t, string const& value);
////////////////////////////////////////////////////////////////////////////////
/// @brief sets a header field
///
/// The key must be lowercased and trimmed already
/// The key string must remain valid until the response is destroyed
/// The value string must remain valid until the response is destroyed
////////////////////////////////////////////////////////////////////////////////
void setHeader (const char*, const size_t, const char*);
////////////////////////////////////////////////////////////////////////////////
/// @brief sets a header field
///
/// The key is automatically converted to lower case and trimmed.
////////////////////////////////////////////////////////////////////////////////
void setHeader (string const& key, string const& value);