mirror of https://gitee.com/bigwinds/arangodb
Merge branch '1.1' of https://github.com/triAGENS/ArangoDB into 1.1
This commit is contained in:
commit
0cf91cab07
51
CHANGELOG
51
CHANGELOG
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue