diff --git a/arangod/Aql/RestAqlHandler.cpp b/arangod/Aql/RestAqlHandler.cpp index 2e4a3235bf..2ec47d7297 100644 --- a/arangod/Aql/RestAqlHandler.cpp +++ b/arangod/Aql/RestAqlHandler.cpp @@ -339,7 +339,7 @@ void RestAqlHandler::createQueryFromString() { } createResponse(GeneralResponse::ResponseCode::ACCEPTED); - _response->setContentType(StaticStrings::MimeTypeJson); + _response->setContentType(HttpResponse::CONTENT_TYPE_JSON); arangodb::basics::Json answerBody(arangodb::basics::Json::Object, 2); answerBody("queryId", arangodb::basics::Json(arangodb::basics::StringUtils::itoa(_qId)))( @@ -550,7 +550,7 @@ void RestAqlHandler::getInfoQuery(std::string const& operation, _queryRegistry->close(_vocbase, _qId); createResponse(GeneralResponse::ResponseCode::OK); - _response->setContentType(StaticStrings::MimeTypeJson); + _response->setContentType(HttpResponse::CONTENT_TYPE_JSON); answerBody("error", arangodb::basics::Json(false)); _response->body().appendText(answerBody.toString()); } @@ -903,7 +903,7 @@ void RestAqlHandler::sendResponse( arangodb::TransactionContext* transactionContext) { // TODO: use RestBaseHandler createResponse(code); - _response->setContentType(StaticStrings::MimeTypeJson); + _response->setContentType(HttpResponse::CONTENT_TYPE_JSON); arangodb::basics::VPackStringBufferAdapter buffer( _response->body().stringBuffer()); VPackDumper dumper(&buffer, transactionContext->getVPackOptions()); diff --git a/arangod/HttpServer/HttpCommTask.cpp b/arangod/HttpServer/HttpCommTask.cpp index 6f92f9df19..f99a473eec 100644 --- a/arangod/HttpServer/HttpCommTask.cpp +++ b/arangod/HttpServer/HttpCommTask.cpp @@ -500,7 +500,7 @@ bool HttpCommTask::processRead() { // not found else if (authResult == GeneralResponse::ResponseCode::NOT_FOUND) { HttpResponse response(authResult); - response.setContentType(StaticStrings::MimeTypeJson); + response.setContentType(HttpResponse::CONTENT_TYPE_JSON); response.body() .appendText(TRI_CHAR_LENGTH_PAIR("{\"error\":true,\"errorMessage\":\"")) @@ -518,7 +518,7 @@ bool HttpCommTask::processRead() { // forbidden else if (authResult == GeneralResponse::ResponseCode::FORBIDDEN) { HttpResponse response(authResult); - response.setContentType(StaticStrings::MimeTypeJson); + response.setContentType(HttpResponse::CONTENT_TYPE_JSON); response.body() .appendText(TRI_CHAR_LENGTH_PAIR( @@ -620,9 +620,7 @@ void HttpCommTask::addResponse(HttpResponse* response) { // set "connection" header // keep-alive is the default - response->setHeaderNC( - StaticStrings::Connection, - (_closeRequested ? StaticStrings::Close : StaticStrings::KeepAlive)); + response->setConnectionType(_closeRequested ? HttpResponse::CONNECTION_CLOSE : HttpResponse::CONNECTION_KEEP_ALIVE); size_t const responseBodyLength = response->bodySize(); @@ -632,7 +630,7 @@ void HttpCommTask::addResponse(HttpResponse* response) { response->headResponse(responseBodyLength); } // else { - // // to enable automatic deflating of responses, active this. + // // to enable automatic deflating of responses, activate this. // // deflate takes a lot of CPU time so it should only be enabled for // // dedicated purposes and not generally // if (responseBodyLength > 16384 && _acceptDeflate) { diff --git a/arangod/HttpServer/PathHandler.cpp b/arangod/HttpServer/PathHandler.cpp index 2fb300c1eb..be97cb88e6 100644 --- a/arangod/HttpServer/PathHandler.cpp +++ b/arangod/HttpServer/PathHandler.cpp @@ -79,9 +79,8 @@ HttpHandler::status_t PathHandler::execute() { createResponse(GeneralResponse::ResponseCode::MOVED_PERMANENTLY); - static std::string const location = "location"; - _response->setHeaderNC(location, url); - _response->setContentType("text/html"); + _response->setHeaderNC(StaticStrings::Location, url); + _response->setContentType(HttpResponse::CONTENT_TYPE_HTML); _response->body().appendText( "Moved

Moved

This " diff --git a/arangod/RestHandler/RestBaseHandler.cpp b/arangod/RestHandler/RestBaseHandler.cpp index 936d1a65a0..ab44096667 100644 --- a/arangod/RestHandler/RestBaseHandler.cpp +++ b/arangod/RestHandler/RestBaseHandler.cpp @@ -161,10 +161,10 @@ bool RestBaseHandler::returnVelocypack() const { void RestBaseHandler::writeResult(arangodb::velocypack::Slice const& slice, VPackOptions const& options) { if (returnVelocypack()) { - _response->setContentType(StaticStrings::MimeTypeVPack); + _response->setContentType(HttpResponse::CONTENT_TYPE_VPACK); _response->body().appendText(slice.startAs(), slice.byteSize()); } else { - _response->setContentType(StaticStrings::MimeTypeJson); + _response->setContentType(HttpResponse::CONTENT_TYPE_JSON); dumpResponse(slice, &options); } } diff --git a/arangod/RestHandler/RestBatchHandler.cpp b/arangod/RestHandler/RestBatchHandler.cpp index 1439af23b8..dd15409a50 100644 --- a/arangod/RestHandler/RestBatchHandler.cpp +++ b/arangod/RestHandler/RestBatchHandler.cpp @@ -213,11 +213,10 @@ HttpHandler::status_t RestBatchHandler::execute() { _response->body().appendText(TRI_CHAR_LENGTH_PAIR("\r\n\r\n")); // remove some headers we don't need - static std::string const connection = "connection"; static std::string const server = "server"; static std::string const empty = ""; - partResponse->setHeaderNC(connection, empty); + partResponse->setConnectionType(HttpResponse::CONNECTION_NONE); partResponse->setHeaderNC(server, empty); // append the part response header diff --git a/arangod/RestHandler/RestCursorHandler.cpp b/arangod/RestHandler/RestCursorHandler.cpp index 9037cc5a9c..0064143763 100644 --- a/arangod/RestHandler/RestCursorHandler.cpp +++ b/arangod/RestHandler/RestCursorHandler.cpp @@ -138,7 +138,7 @@ void RestCursorHandler::processQuery(VPackSlice const& slice) { { createResponse(GeneralResponse::ResponseCode::CREATED); - _response->setContentType(StaticStrings::MimeTypeJson); + _response->setContentType(HttpResponse::CONTENT_TYPE_JSON); std::shared_ptr extra = buildExtra(queryResult); VPackSlice opts = options->slice(); @@ -454,7 +454,7 @@ void RestCursorHandler::modifyCursor() { try { createResponse(GeneralResponse::ResponseCode::OK); - _response->setContentType(StaticStrings::MimeTypeJson); + _response->setContentType(HttpResponse::CONTENT_TYPE_JSON); _response->body().appendChar('{'); cursor->dump(_response->body()); diff --git a/arangod/RestHandler/RestExportHandler.cpp b/arangod/RestHandler/RestExportHandler.cpp index dd1cef4293..b2e31fcec8 100644 --- a/arangod/RestHandler/RestExportHandler.cpp +++ b/arangod/RestHandler/RestExportHandler.cpp @@ -262,7 +262,7 @@ void RestExportHandler::createCursor() { options, "count", false); createResponse(GeneralResponse::ResponseCode::CREATED); - _response->setContentType(StaticStrings::MimeTypeJson); + _response->setContentType(HttpResponse::CONTENT_TYPE_JSON); auto cursors = static_cast(_vocbase->_cursorRepository); @@ -330,7 +330,7 @@ void RestExportHandler::modifyCursor() { try { createResponse(GeneralResponse::ResponseCode::OK); - _response->setContentType(StaticStrings::MimeTypeJson); + _response->setContentType(HttpResponse::CONTENT_TYPE_JSON); _response->body().appendChar('{'); cursor->dump(_response->body()); @@ -381,7 +381,7 @@ void RestExportHandler::deleteCursor() { // TODO: use RestBaseHandler createResponse(GeneralResponse::ResponseCode::ACCEPTED); - _response->setContentType(StaticStrings::MimeTypeJson); + _response->setContentType(HttpResponse::CONTENT_TYPE_JSON); VPackBuilder result; result.openObject(); result.add("id", VPackValue(id)); diff --git a/arangod/RestHandler/RestImportHandler.cpp b/arangod/RestHandler/RestImportHandler.cpp index ee5f94f3de..b1c92a6d98 100644 --- a/arangod/RestHandler/RestImportHandler.cpp +++ b/arangod/RestHandler/RestImportHandler.cpp @@ -779,7 +779,7 @@ void RestImportHandler::generateDocumentsCreated( RestImportResult const& result) { // TODO: is it necessary to create a response object here already createResponse(GeneralResponse::ResponseCode::CREATED); - _response->setContentType(StaticStrings::MimeTypeJson); + _response->setContentType(HttpResponse::CONTENT_TYPE_JSON); try { VPackBuilder json; diff --git a/arangod/RestHandler/RestPleaseUpgradeHandler.cpp b/arangod/RestHandler/RestPleaseUpgradeHandler.cpp index 815a3524f2..46be6fe5b6 100644 --- a/arangod/RestHandler/RestPleaseUpgradeHandler.cpp +++ b/arangod/RestHandler/RestPleaseUpgradeHandler.cpp @@ -34,7 +34,7 @@ bool RestPleaseUpgradeHandler::isDirect() const { return true; } HttpHandler::status_t RestPleaseUpgradeHandler::execute() { createResponse(GeneralResponse::ResponseCode::OK); - _response->setContentType("text/plain; charset=utf-8"); + _response->setContentType(HttpResponse::CONTENT_TYPE_TEXT); auto& buffer = _response->body(); buffer.appendText("Database: "); diff --git a/arangod/RestHandler/RestReplicationHandler.cpp b/arangod/RestHandler/RestReplicationHandler.cpp index 0bac643f33..7f768fe4b9 100644 --- a/arangod/RestHandler/RestReplicationHandler.cpp +++ b/arangod/RestHandler/RestReplicationHandler.cpp @@ -943,7 +943,7 @@ void RestReplicationHandler::handleCommandLoggerFollow() { createResponse(GeneralResponse::ResponseCode::OK); } - _response->setContentType("application/x-arango-dump; charset=utf-8"); + _response->setContentType(HttpResponse::CONTENT_TYPE_DUMP); // set headers _response->setHeaderNC(TRI_REPLICATION_HEADER_CHECKMORE, @@ -1037,7 +1037,7 @@ void RestReplicationHandler::handleCommandDetermineOpenTransactions() { createResponse(GeneralResponse::ResponseCode::OK); } - _response->setContentType("application/x-arango-dump; charset=utf-8"); + _response->setContentType(HttpResponse::CONTENT_TYPE_DUMP); _response->setHeaderNC(TRI_REPLICATION_HEADER_FROMPRESENT, dump._fromTickIncluded ? "true" : "false"); @@ -2928,7 +2928,7 @@ void RestReplicationHandler::handleCommandDump() { createResponse(GeneralResponse::ResponseCode::OK); } - _response->setContentType("application/x-arango-dump; charset=utf-8"); + _response->setContentType(HttpResponse::CONTENT_TYPE_DUMP); // set headers _response->setHeaderNC( diff --git a/arangod/RestHandler/RestSimpleHandler.cpp b/arangod/RestHandler/RestSimpleHandler.cpp index 6beb1bccbc..09faf53797 100644 --- a/arangod/RestHandler/RestSimpleHandler.cpp +++ b/arangod/RestHandler/RestSimpleHandler.cpp @@ -350,7 +350,7 @@ void RestSimpleHandler::lookupByKeys(VPackSlice const& slice) { { VPackObjectBuilder guard(&result); createResponse(GeneralResponse::ResponseCode::OK); - _response->setContentType(StaticStrings::MimeTypeJson); + _response->setContentType(HttpResponse::CONTENT_TYPE_JSON); if (qResult.isArray()) { diff --git a/arangod/RestHandler/RestUploadHandler.cpp b/arangod/RestHandler/RestUploadHandler.cpp index 038fa7d1d1..f4d3b6aa6d 100644 --- a/arangod/RestHandler/RestUploadHandler.cpp +++ b/arangod/RestHandler/RestUploadHandler.cpp @@ -105,7 +105,7 @@ HttpHandler::status_t RestUploadHandler::execute() { // create the response createResponse(GeneralResponse::ResponseCode::CREATED); - _response->setContentType(StaticStrings::MimeTypeJson); + _response->setContentType(HttpResponse::CONTENT_TYPE_JSON); // TODO: use RestBaseHandler VPackBuilder b; diff --git a/arangod/RestHandler/RestVocbaseBaseHandler.cpp b/arangod/RestHandler/RestVocbaseBaseHandler.cpp index d80fff7eb3..f860fc3717 100644 --- a/arangod/RestHandler/RestVocbaseBaseHandler.cpp +++ b/arangod/RestHandler/RestVocbaseBaseHandler.cpp @@ -368,11 +368,11 @@ void RestVocbaseBaseHandler::generateDocument(VPackSlice const& input, if (generateBody) { writeResult(document, *options); } else { - if (returnVelocypack()){ - _response->setContentType(StaticStrings::MimeTypeVPack); + if (returnVelocypack()) { + _response->setContentType(HttpResponse::CONTENT_TYPE_VPACK); _response->headResponse(document.byteSize()); } else { - _response->setContentType(StaticStrings::MimeTypeJson); + _response->setContentType(HttpResponse::CONTENT_TYPE_JSON); // TODO can we optimize this? // Just dump some where else to find real length diff --git a/arangod/Utils/WorkMonitorArangod.cpp b/arangod/Utils/WorkMonitorArangod.cpp index 2a58d0f4a0..ab53ebbdfd 100644 --- a/arangod/Utils/WorkMonitorArangod.cpp +++ b/arangod/Utils/WorkMonitorArangod.cpp @@ -165,7 +165,7 @@ void WorkMonitor::vpackHandler(VPackBuilder* b, WorkDescription* desc) { void WorkMonitor::sendWorkOverview(uint64_t taskId, std::string const& data) { auto response = std::make_unique(GeneralResponse::ResponseCode::OK); - response->setContentType(StaticStrings::MimeTypeJson); + response->setContentType(HttpResponse::CONTENT_TYPE_JSON); TRI_AppendString2StringBuffer(response->body().stringBuffer(), data.c_str(), data.length()); diff --git a/lib/Basics/StringBuffer.h b/lib/Basics/StringBuffer.h index 3ce5aa19d8..bff566c2f7 100644 --- a/lib/Basics/StringBuffer.h +++ b/lib/Basics/StringBuffer.h @@ -204,6 +204,9 @@ int TRI_AppendStringStringBuffer(TRI_string_buffer_t* self, char const* str); int TRI_AppendString2StringBuffer(TRI_string_buffer_t* self, char const* str, size_t len); +void TRI_AppendString2UnsafeStringBuffer(TRI_string_buffer_t* self, char const* str, + size_t len); + //////////////////////////////////////////////////////////////////////////////// /// @brief appends characters but does not check buffer bounds //////////////////////////////////////////////////////////////////////////////// @@ -224,6 +227,11 @@ static inline void TRI_AppendStringUnsafeStringBuffer(TRI_string_buffer_t* self, self->_current += len; } +static inline void TRI_AppendStringUnsafeStringBuffer(TRI_string_buffer_t* self, std::string const& str) { + memcpy(self->_current, str.c_str(), str.size()); + self->_current += str.size(); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief appends characters but json-encode the null-terminated string //////////////////////////////////////////////////////////////////////////////// @@ -778,6 +786,10 @@ class StringBuffer { return *this; } + void appendCharUnsafe(char chr) { + TRI_AppendCharUnsafeStringBuffer(&_buffer, chr); + } + ////////////////////////////////////////////////////////////////////////////// /// @brief appends as json-encoded ////////////////////////////////////////////////////////////////////////////// @@ -804,6 +816,10 @@ class StringBuffer { TRI_AppendString2StringBuffer(&_buffer, str, len); return *this; } + + void appendTextUnsafe(char const* str, size_t len) { + TRI_AppendStringUnsafeStringBuffer(&_buffer, str, len); + } ////////////////////////////////////////////////////////////////////////////// /// @brief appends characters @@ -822,6 +838,10 @@ class StringBuffer { TRI_AppendString2StringBuffer(&_buffer, str.c_str(), str.length()); return *this; } + + void appendTextUnsafe(std::string const& str) { + TRI_AppendStringUnsafeStringBuffer(&_buffer, str.c_str(), str.length()); + } ////////////////////////////////////////////////////////////////////////////// /// @brief appends a string buffer diff --git a/lib/Rest/GeneralResponse.h b/lib/Rest/GeneralResponse.h index 58bcf0df3c..b4e8cec23b 100644 --- a/lib/Rest/GeneralResponse.h +++ b/lib/Rest/GeneralResponse.h @@ -109,10 +109,6 @@ class GeneralResponse { _responseCode = responseCode; } - void setContentType(std::string const& contentType) { - _headers[arangodb::StaticStrings::ContentTypeHeader] = contentType; - } - // 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. @@ -126,10 +122,6 @@ class GeneralResponse { void setHeaderNC(std::string const& key, std::string const& value); void setHeaderNC(std::string const& key, std::string&& value); - private: - // checks for special headers - virtual void checkHeader(std::string const& key, std::string const& value) {} - protected: ResponseCode _responseCode; std::unordered_map _headers; diff --git a/lib/Rest/HttpResponse.cpp b/lib/Rest/HttpResponse.cpp index a9f33e00ff..9769be8d2f 100644 --- a/lib/Rest/HttpResponse.cpp +++ b/lib/Rest/HttpResponse.cpp @@ -24,6 +24,7 @@ #include "HttpResponse.h" +#include "Basics/Exceptions.h" #include "Basics/StringBuffer.h" #include "Basics/StringUtils.h" #include "Basics/tri-strings.h" @@ -36,16 +37,17 @@ bool HttpResponse::HIDE_PRODUCT_HEADER = false; HttpResponse::HttpResponse(ResponseCode code) : GeneralResponse(code), + _connectionType(CONNECTION_KEEP_ALIVE), + _contentType(CONTENT_TYPE_TEXT), _isHeadResponse(false), - _isChunked(false), + _isChunked(false), // TODO: remove _body(TRI_UNKNOWN_MEM_ZONE, false), _bodySize(0) { - if (!HIDE_PRODUCT_HEADER) { - _headers[StaticStrings::Server] = "ArangoDB"; - } - _headers[StaticStrings::Connection] = StaticStrings::KeepAlive; - _headers[StaticStrings::ContentTypeHeader] = StaticStrings::MimeTypeText; + if (_body.c_str() == nullptr) { + // no buffer could be reserved. out of memory! + THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); + } } void HttpResponse::setCookie(std::string const& name, std::string const& value, @@ -118,13 +120,13 @@ size_t HttpResponse::bodySize() const { } void HttpResponse::writeHeader(StringBuffer* output) { - bool const capitalizeHeaders = true; - output->appendText(TRI_CHAR_LENGTH_PAIR("HTTP/1.1 ")); output->appendText(responseString(_responseCode)); - output->appendText(TRI_CHAR_LENGTH_PAIR("\r\n")); - - bool seenTransferEncoding = false; + output->appendText("\r\n", 2); + + bool seenServerHeader = false; + bool seenConnectionHeader = false; + bool seenTransferEncodingHeader = false; std::string transferEncoding; for (auto const& it : _headers) { @@ -140,80 +142,116 @@ void HttpResponse::writeHeader(StringBuffer* output) { // save transfer encoding if (keyLength == 17 && key[0] == 't' && memcmp(key.c_str(), "transfer-encoding", keyLength) == 0) { - seenTransferEncoding = true; + seenTransferEncodingHeader = true; transferEncoding = it.second; continue; } - if (capitalizeHeaders) { - char const* p = key.c_str(); - char const* end = p + keyLength; - int capState = 1; - - while (p < end) { - if (capState == 1) { - // upper case - output->appendChar(::toupper(*p)); - capState = 0; - } else if (capState == 0) { - // normal case - output->appendChar(::tolower(*p)); - if (*p == '-') { - capState = 1; - } else if (*p == ':') { - capState = 2; - } - } else { - // output as is - output->appendChar(*p); - } - ++p; - } - } else { - output->appendText(key); + if (keyLength == 6 && key[0] == 's' && + memcmp(key.c_str(), "server", keyLength) == 0) { + // this ensures we don't print two "Server" headers + seenServerHeader = true; + // go on and use the user-defined "Server" header value + } else if (keyLength == 10 && key[0] == 'c' && + memcmp(key.c_str(), "connection", keyLength) == 0) { + // this ensures we don't print two "Connection" headers + seenConnectionHeader = true; + // go on and use the user-defined "Connection" header value } - output->appendText(TRI_CHAR_LENGTH_PAIR(": ")); - output->appendText(it.second); - output->appendText(TRI_CHAR_LENGTH_PAIR("\r\n")); + // reserve enough space for header name + ": " + value + "\r\n" + if (output->reserve(keyLength + 2 + it.second.size() + 2) != TRI_ERROR_NO_ERROR) { + THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); + } + + char const* p = key.c_str(); + char const* end = p + keyLength; + int capState = 1; + + while (p < end) { + if (capState == 1) { + // upper case + output->appendCharUnsafe(::toupper(*p)); + capState = 0; + } else if (capState == 0) { + // normal case + output->appendCharUnsafe(::tolower(*p)); + if (*p == '-') { + capState = 1; + } else if (*p == ':') { + capState = 2; + } + } else { + // output as is + output->appendCharUnsafe(*p); + } + ++p; + } + + output->appendTextUnsafe(": ", 2); + output->appendTextUnsafe(it.second); + output->appendTextUnsafe("\r\n", 2); + } + + // add "Server" response header + if (!seenServerHeader && !HIDE_PRODUCT_HEADER) { + output->appendText("Server: ArangoDB\r\n"); + } + + // add "Connection" response header + if (!seenConnectionHeader) { + switch (_connectionType) { + case CONNECTION_KEEP_ALIVE: + output->appendText(TRI_CHAR_LENGTH_PAIR("Connection: Keep-Alive\r\n")); + break; + case CONNECTION_CLOSE: + output->appendText(TRI_CHAR_LENGTH_PAIR("Connection: Close\r\n")); + break; + case CONNECTION_NONE: + output->appendText(TRI_CHAR_LENGTH_PAIR("Connection: \r\n")); + break; + } + } + + switch (_contentType) { + case CONTENT_TYPE_JSON: + output->appendText(TRI_CHAR_LENGTH_PAIR("Content-Type: application/json; charset=utf-8\r\n")); + break; + case CONTENT_TYPE_VPACK: + output->appendText(TRI_CHAR_LENGTH_PAIR("Content-Type: application/x-velocypack\r\n")); + break; + case CONTENT_TYPE_TEXT: + output->appendText(TRI_CHAR_LENGTH_PAIR("Content-Type: text/plain; charset=utf-8\r\n")); + break; + case CONTENT_TYPE_HTML: + output->appendText(TRI_CHAR_LENGTH_PAIR("Content-Type: text/html; charset=utf-8\r\n")); + break; + case CONTENT_TYPE_DUMP: + output->appendText(TRI_CHAR_LENGTH_PAIR("Content-Type: application/x-arango-dump; charset=utf-8\r\n")); + break; + case CONTENT_TYPE_CUSTOM: { + // intentionally don't print anything here + // the header should have been in _headers already, and should have been handled above + } } for (auto const& it : _cookies) { - if (capitalizeHeaders) { - output->appendText(TRI_CHAR_LENGTH_PAIR("Set-Cookie: ")); - } else { - output->appendText(TRI_CHAR_LENGTH_PAIR("set-cookie: ")); - } - + output->appendText(TRI_CHAR_LENGTH_PAIR("Set-Cookie: ")); output->appendText(it); - output->appendText(TRI_CHAR_LENGTH_PAIR("\r\n")); + output->appendText("\r\n", 2); } - if (seenTransferEncoding && transferEncoding == "chunked") { - if (capitalizeHeaders) { - output->appendText( - TRI_CHAR_LENGTH_PAIR("Transfer-Encoding: chunked\r\n\r\n")); - } else { - output->appendText( - TRI_CHAR_LENGTH_PAIR("transfer-encoding: chunked\r\n\r\n")); - } + if (seenTransferEncodingHeader && transferEncoding == "chunked") { + output->appendText( + TRI_CHAR_LENGTH_PAIR("Transfer-Encoding: chunked\r\n\r\n")); } else { - if (seenTransferEncoding) { - if (capitalizeHeaders) { - output->appendText(TRI_CHAR_LENGTH_PAIR("Transfer-Encoding: ")); - } else { - output->appendText(TRI_CHAR_LENGTH_PAIR("transfer-encoding: ")); - } - + if (seenTransferEncodingHeader) { + output->appendText(TRI_CHAR_LENGTH_PAIR("Transfer-Encoding: ")); output->appendText(transferEncoding); - output->appendText(TRI_CHAR_LENGTH_PAIR("\r\n")); + output->appendText("\r\n", 2); } - if (capitalizeHeaders) { - output->appendText(TRI_CHAR_LENGTH_PAIR("Content-Length: ")); - } else { - output->appendText(TRI_CHAR_LENGTH_PAIR("content-length: ")); - } + output->appendText(TRI_CHAR_LENGTH_PAIR("Content-Length: ")); if (_isHeadResponse) { // From http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13 @@ -231,18 +269,8 @@ void HttpResponse::writeHeader(StringBuffer* output) { output->appendInteger(_body.length()); } - output->appendText(TRI_CHAR_LENGTH_PAIR("\r\n\r\n")); + output->appendText("\r\n\r\n", 4); } // end of header, body to follow } -void HttpResponse::checkHeader(std::string const& key, - std::string const& value) { - if (key[0] == 't' && key == "transfer-encoding") { - if (TRI_CaseEqualString(value.c_str(), "chunked")) { - _isChunked = true; - } else { - _isChunked = false; - } - } -} diff --git a/lib/Rest/HttpResponse.h b/lib/Rest/HttpResponse.h index 7ac284dd31..7ad75e1054 100644 --- a/lib/Rest/HttpResponse.h +++ b/lib/Rest/HttpResponse.h @@ -42,6 +42,21 @@ class HttpResponse : public GeneralResponse { bool isHeadResponse() const { return _isHeadResponse; } bool isChunked() const { return _isChunked; } + enum ConnectionType { + CONNECTION_NONE, + CONNECTION_KEEP_ALIVE, + CONNECTION_CLOSE + }; + + enum ContentType { + CONTENT_TYPE_CUSTOM, // use Content-Type from _headers + CONTENT_TYPE_JSON, // application/json + CONTENT_TYPE_VPACK, // application/x-velocypack + CONTENT_TYPE_TEXT, // text/plain + CONTENT_TYPE_HTML, // text/html + CONTENT_TYPE_DUMP // application/x-arango-dump + }; + public: using GeneralResponse::setHeader; @@ -61,6 +76,24 @@ class HttpResponse : public GeneralResponse { basics::StringBuffer& body() { return _body; } size_t bodySize() const; + /// @brief set type of connection + void setConnectionType(ConnectionType type) { _connectionType = type; } + + /// @brief set content-type + void setContentType(ContentType type) { _contentType = type; } + + /// @brief set content-type from a string. this should only be used in + /// cases when the content-type is user-defined + void setContentType(std::string const& contentType) { + _headers[arangodb::StaticStrings::ContentTypeHeader] = contentType; + _contentType = CONTENT_TYPE_CUSTOM; + } + + void setContentType(std::string&& contentType) { + _headers[arangodb::StaticStrings::ContentTypeHeader] = std::move(contentType); + _contentType = CONTENT_TYPE_CUSTOM; + } + // you should call writeHeader only after the body has been created void writeHeader(basics::StringBuffer*); @@ -68,12 +101,11 @@ class HttpResponse : public GeneralResponse { // the body must already be set. deflate is then run on the existing body int deflate(size_t = 16384); - // checks for special headers - void checkHeader(std::string const& key, std::string const& value) override final; - private: + ConnectionType _connectionType; + ContentType _contentType; bool _isHeadResponse; - bool _isChunked; + bool _isChunked; // TODO: remove std::vector _cookies; basics::StringBuffer _body; size_t _bodySize;