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(
"
MovedMoved
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;