mirror of https://gitee.com/bigwinds/arangodb
handle "Connection" and "Content-Type" headers separately
This commit is contained in:
parent
fb8c90b28c
commit
f90a9fce18
|
@ -339,7 +339,7 @@ void RestAqlHandler::createQueryFromString() {
|
||||||
}
|
}
|
||||||
|
|
||||||
createResponse(GeneralResponse::ResponseCode::ACCEPTED);
|
createResponse(GeneralResponse::ResponseCode::ACCEPTED);
|
||||||
_response->setContentType(StaticStrings::MimeTypeJson);
|
_response->setContentType(HttpResponse::CONTENT_TYPE_JSON);
|
||||||
arangodb::basics::Json answerBody(arangodb::basics::Json::Object, 2);
|
arangodb::basics::Json answerBody(arangodb::basics::Json::Object, 2);
|
||||||
answerBody("queryId",
|
answerBody("queryId",
|
||||||
arangodb::basics::Json(arangodb::basics::StringUtils::itoa(_qId)))(
|
arangodb::basics::Json(arangodb::basics::StringUtils::itoa(_qId)))(
|
||||||
|
@ -550,7 +550,7 @@ void RestAqlHandler::getInfoQuery(std::string const& operation,
|
||||||
_queryRegistry->close(_vocbase, _qId);
|
_queryRegistry->close(_vocbase, _qId);
|
||||||
|
|
||||||
createResponse(GeneralResponse::ResponseCode::OK);
|
createResponse(GeneralResponse::ResponseCode::OK);
|
||||||
_response->setContentType(StaticStrings::MimeTypeJson);
|
_response->setContentType(HttpResponse::CONTENT_TYPE_JSON);
|
||||||
answerBody("error", arangodb::basics::Json(false));
|
answerBody("error", arangodb::basics::Json(false));
|
||||||
_response->body().appendText(answerBody.toString());
|
_response->body().appendText(answerBody.toString());
|
||||||
}
|
}
|
||||||
|
@ -903,7 +903,7 @@ void RestAqlHandler::sendResponse(
|
||||||
arangodb::TransactionContext* transactionContext) {
|
arangodb::TransactionContext* transactionContext) {
|
||||||
// TODO: use RestBaseHandler
|
// TODO: use RestBaseHandler
|
||||||
createResponse(code);
|
createResponse(code);
|
||||||
_response->setContentType(StaticStrings::MimeTypeJson);
|
_response->setContentType(HttpResponse::CONTENT_TYPE_JSON);
|
||||||
arangodb::basics::VPackStringBufferAdapter buffer(
|
arangodb::basics::VPackStringBufferAdapter buffer(
|
||||||
_response->body().stringBuffer());
|
_response->body().stringBuffer());
|
||||||
VPackDumper dumper(&buffer, transactionContext->getVPackOptions());
|
VPackDumper dumper(&buffer, transactionContext->getVPackOptions());
|
||||||
|
|
|
@ -500,7 +500,7 @@ bool HttpCommTask::processRead() {
|
||||||
// not found
|
// not found
|
||||||
else if (authResult == GeneralResponse::ResponseCode::NOT_FOUND) {
|
else if (authResult == GeneralResponse::ResponseCode::NOT_FOUND) {
|
||||||
HttpResponse response(authResult);
|
HttpResponse response(authResult);
|
||||||
response.setContentType(StaticStrings::MimeTypeJson);
|
response.setContentType(HttpResponse::CONTENT_TYPE_JSON);
|
||||||
|
|
||||||
response.body()
|
response.body()
|
||||||
.appendText(TRI_CHAR_LENGTH_PAIR("{\"error\":true,\"errorMessage\":\""))
|
.appendText(TRI_CHAR_LENGTH_PAIR("{\"error\":true,\"errorMessage\":\""))
|
||||||
|
@ -518,7 +518,7 @@ bool HttpCommTask::processRead() {
|
||||||
// forbidden
|
// forbidden
|
||||||
else if (authResult == GeneralResponse::ResponseCode::FORBIDDEN) {
|
else if (authResult == GeneralResponse::ResponseCode::FORBIDDEN) {
|
||||||
HttpResponse response(authResult);
|
HttpResponse response(authResult);
|
||||||
response.setContentType(StaticStrings::MimeTypeJson);
|
response.setContentType(HttpResponse::CONTENT_TYPE_JSON);
|
||||||
|
|
||||||
response.body()
|
response.body()
|
||||||
.appendText(TRI_CHAR_LENGTH_PAIR(
|
.appendText(TRI_CHAR_LENGTH_PAIR(
|
||||||
|
@ -620,9 +620,7 @@ void HttpCommTask::addResponse(HttpResponse* response) {
|
||||||
|
|
||||||
// set "connection" header
|
// set "connection" header
|
||||||
// keep-alive is the default
|
// keep-alive is the default
|
||||||
response->setHeaderNC(
|
response->setConnectionType(_closeRequested ? HttpResponse::CONNECTION_CLOSE : HttpResponse::CONNECTION_KEEP_ALIVE);
|
||||||
StaticStrings::Connection,
|
|
||||||
(_closeRequested ? StaticStrings::Close : StaticStrings::KeepAlive));
|
|
||||||
|
|
||||||
size_t const responseBodyLength = response->bodySize();
|
size_t const responseBodyLength = response->bodySize();
|
||||||
|
|
||||||
|
@ -632,7 +630,7 @@ void HttpCommTask::addResponse(HttpResponse* response) {
|
||||||
response->headResponse(responseBodyLength);
|
response->headResponse(responseBodyLength);
|
||||||
}
|
}
|
||||||
// else {
|
// 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
|
// // deflate takes a lot of CPU time so it should only be enabled for
|
||||||
// // dedicated purposes and not generally
|
// // dedicated purposes and not generally
|
||||||
// if (responseBodyLength > 16384 && _acceptDeflate) {
|
// if (responseBodyLength > 16384 && _acceptDeflate) {
|
||||||
|
|
|
@ -79,9 +79,8 @@ HttpHandler::status_t PathHandler::execute() {
|
||||||
|
|
||||||
createResponse(GeneralResponse::ResponseCode::MOVED_PERMANENTLY);
|
createResponse(GeneralResponse::ResponseCode::MOVED_PERMANENTLY);
|
||||||
|
|
||||||
static std::string const location = "location";
|
_response->setHeaderNC(StaticStrings::Location, url);
|
||||||
_response->setHeaderNC(location, url);
|
_response->setContentType(HttpResponse::CONTENT_TYPE_HTML);
|
||||||
_response->setContentType("text/html");
|
|
||||||
|
|
||||||
_response->body().appendText(
|
_response->body().appendText(
|
||||||
"<html><head><title>Moved</title></head><body><h1>Moved</h1><p>This "
|
"<html><head><title>Moved</title></head><body><h1>Moved</h1><p>This "
|
||||||
|
|
|
@ -161,10 +161,10 @@ bool RestBaseHandler::returnVelocypack() const {
|
||||||
void RestBaseHandler::writeResult(arangodb::velocypack::Slice const& slice,
|
void RestBaseHandler::writeResult(arangodb::velocypack::Slice const& slice,
|
||||||
VPackOptions const& options) {
|
VPackOptions const& options) {
|
||||||
if (returnVelocypack()) {
|
if (returnVelocypack()) {
|
||||||
_response->setContentType(StaticStrings::MimeTypeVPack);
|
_response->setContentType(HttpResponse::CONTENT_TYPE_VPACK);
|
||||||
_response->body().appendText(slice.startAs<const char>(), slice.byteSize());
|
_response->body().appendText(slice.startAs<const char>(), slice.byteSize());
|
||||||
} else {
|
} else {
|
||||||
_response->setContentType(StaticStrings::MimeTypeJson);
|
_response->setContentType(HttpResponse::CONTENT_TYPE_JSON);
|
||||||
dumpResponse(slice, &options);
|
dumpResponse(slice, &options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,11 +213,10 @@ HttpHandler::status_t RestBatchHandler::execute() {
|
||||||
_response->body().appendText(TRI_CHAR_LENGTH_PAIR("\r\n\r\n"));
|
_response->body().appendText(TRI_CHAR_LENGTH_PAIR("\r\n\r\n"));
|
||||||
|
|
||||||
// remove some headers we don't need
|
// remove some headers we don't need
|
||||||
static std::string const connection = "connection";
|
|
||||||
static std::string const server = "server";
|
static std::string const server = "server";
|
||||||
static std::string const empty = "";
|
static std::string const empty = "";
|
||||||
|
|
||||||
partResponse->setHeaderNC(connection, empty);
|
partResponse->setConnectionType(HttpResponse::CONNECTION_NONE);
|
||||||
partResponse->setHeaderNC(server, empty);
|
partResponse->setHeaderNC(server, empty);
|
||||||
|
|
||||||
// append the part response header
|
// append the part response header
|
||||||
|
|
|
@ -138,7 +138,7 @@ void RestCursorHandler::processQuery(VPackSlice const& slice) {
|
||||||
|
|
||||||
{
|
{
|
||||||
createResponse(GeneralResponse::ResponseCode::CREATED);
|
createResponse(GeneralResponse::ResponseCode::CREATED);
|
||||||
_response->setContentType(StaticStrings::MimeTypeJson);
|
_response->setContentType(HttpResponse::CONTENT_TYPE_JSON);
|
||||||
|
|
||||||
std::shared_ptr<VPackBuilder> extra = buildExtra(queryResult);
|
std::shared_ptr<VPackBuilder> extra = buildExtra(queryResult);
|
||||||
VPackSlice opts = options->slice();
|
VPackSlice opts = options->slice();
|
||||||
|
@ -454,7 +454,7 @@ void RestCursorHandler::modifyCursor() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
createResponse(GeneralResponse::ResponseCode::OK);
|
createResponse(GeneralResponse::ResponseCode::OK);
|
||||||
_response->setContentType(StaticStrings::MimeTypeJson);
|
_response->setContentType(HttpResponse::CONTENT_TYPE_JSON);
|
||||||
|
|
||||||
_response->body().appendChar('{');
|
_response->body().appendChar('{');
|
||||||
cursor->dump(_response->body());
|
cursor->dump(_response->body());
|
||||||
|
|
|
@ -262,7 +262,7 @@ void RestExportHandler::createCursor() {
|
||||||
options, "count", false);
|
options, "count", false);
|
||||||
|
|
||||||
createResponse(GeneralResponse::ResponseCode::CREATED);
|
createResponse(GeneralResponse::ResponseCode::CREATED);
|
||||||
_response->setContentType(StaticStrings::MimeTypeJson);
|
_response->setContentType(HttpResponse::CONTENT_TYPE_JSON);
|
||||||
|
|
||||||
auto cursors =
|
auto cursors =
|
||||||
static_cast<arangodb::CursorRepository*>(_vocbase->_cursorRepository);
|
static_cast<arangodb::CursorRepository*>(_vocbase->_cursorRepository);
|
||||||
|
@ -330,7 +330,7 @@ void RestExportHandler::modifyCursor() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
createResponse(GeneralResponse::ResponseCode::OK);
|
createResponse(GeneralResponse::ResponseCode::OK);
|
||||||
_response->setContentType(StaticStrings::MimeTypeJson);
|
_response->setContentType(HttpResponse::CONTENT_TYPE_JSON);
|
||||||
|
|
||||||
_response->body().appendChar('{');
|
_response->body().appendChar('{');
|
||||||
cursor->dump(_response->body());
|
cursor->dump(_response->body());
|
||||||
|
@ -381,7 +381,7 @@ void RestExportHandler::deleteCursor() {
|
||||||
|
|
||||||
// TODO: use RestBaseHandler
|
// TODO: use RestBaseHandler
|
||||||
createResponse(GeneralResponse::ResponseCode::ACCEPTED);
|
createResponse(GeneralResponse::ResponseCode::ACCEPTED);
|
||||||
_response->setContentType(StaticStrings::MimeTypeJson);
|
_response->setContentType(HttpResponse::CONTENT_TYPE_JSON);
|
||||||
VPackBuilder result;
|
VPackBuilder result;
|
||||||
result.openObject();
|
result.openObject();
|
||||||
result.add("id", VPackValue(id));
|
result.add("id", VPackValue(id));
|
||||||
|
|
|
@ -779,7 +779,7 @@ void RestImportHandler::generateDocumentsCreated(
|
||||||
RestImportResult const& result) {
|
RestImportResult const& result) {
|
||||||
// TODO: is it necessary to create a response object here already
|
// TODO: is it necessary to create a response object here already
|
||||||
createResponse(GeneralResponse::ResponseCode::CREATED);
|
createResponse(GeneralResponse::ResponseCode::CREATED);
|
||||||
_response->setContentType(StaticStrings::MimeTypeJson);
|
_response->setContentType(HttpResponse::CONTENT_TYPE_JSON);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
VPackBuilder json;
|
VPackBuilder json;
|
||||||
|
|
|
@ -34,7 +34,7 @@ bool RestPleaseUpgradeHandler::isDirect() const { return true; }
|
||||||
|
|
||||||
HttpHandler::status_t RestPleaseUpgradeHandler::execute() {
|
HttpHandler::status_t RestPleaseUpgradeHandler::execute() {
|
||||||
createResponse(GeneralResponse::ResponseCode::OK);
|
createResponse(GeneralResponse::ResponseCode::OK);
|
||||||
_response->setContentType("text/plain; charset=utf-8");
|
_response->setContentType(HttpResponse::CONTENT_TYPE_TEXT);
|
||||||
|
|
||||||
auto& buffer = _response->body();
|
auto& buffer = _response->body();
|
||||||
buffer.appendText("Database: ");
|
buffer.appendText("Database: ");
|
||||||
|
|
|
@ -943,7 +943,7 @@ void RestReplicationHandler::handleCommandLoggerFollow() {
|
||||||
createResponse(GeneralResponse::ResponseCode::OK);
|
createResponse(GeneralResponse::ResponseCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
_response->setContentType("application/x-arango-dump; charset=utf-8");
|
_response->setContentType(HttpResponse::CONTENT_TYPE_DUMP);
|
||||||
|
|
||||||
// set headers
|
// set headers
|
||||||
_response->setHeaderNC(TRI_REPLICATION_HEADER_CHECKMORE,
|
_response->setHeaderNC(TRI_REPLICATION_HEADER_CHECKMORE,
|
||||||
|
@ -1037,7 +1037,7 @@ void RestReplicationHandler::handleCommandDetermineOpenTransactions() {
|
||||||
createResponse(GeneralResponse::ResponseCode::OK);
|
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,
|
_response->setHeaderNC(TRI_REPLICATION_HEADER_FROMPRESENT,
|
||||||
dump._fromTickIncluded ? "true" : "false");
|
dump._fromTickIncluded ? "true" : "false");
|
||||||
|
@ -2928,7 +2928,7 @@ void RestReplicationHandler::handleCommandDump() {
|
||||||
createResponse(GeneralResponse::ResponseCode::OK);
|
createResponse(GeneralResponse::ResponseCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
_response->setContentType("application/x-arango-dump; charset=utf-8");
|
_response->setContentType(HttpResponse::CONTENT_TYPE_DUMP);
|
||||||
|
|
||||||
// set headers
|
// set headers
|
||||||
_response->setHeaderNC(
|
_response->setHeaderNC(
|
||||||
|
|
|
@ -350,7 +350,7 @@ void RestSimpleHandler::lookupByKeys(VPackSlice const& slice) {
|
||||||
{
|
{
|
||||||
VPackObjectBuilder guard(&result);
|
VPackObjectBuilder guard(&result);
|
||||||
createResponse(GeneralResponse::ResponseCode::OK);
|
createResponse(GeneralResponse::ResponseCode::OK);
|
||||||
_response->setContentType(StaticStrings::MimeTypeJson);
|
_response->setContentType(HttpResponse::CONTENT_TYPE_JSON);
|
||||||
|
|
||||||
if (qResult.isArray()) {
|
if (qResult.isArray()) {
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ HttpHandler::status_t RestUploadHandler::execute() {
|
||||||
|
|
||||||
// create the response
|
// create the response
|
||||||
createResponse(GeneralResponse::ResponseCode::CREATED);
|
createResponse(GeneralResponse::ResponseCode::CREATED);
|
||||||
_response->setContentType(StaticStrings::MimeTypeJson);
|
_response->setContentType(HttpResponse::CONTENT_TYPE_JSON);
|
||||||
|
|
||||||
// TODO: use RestBaseHandler
|
// TODO: use RestBaseHandler
|
||||||
VPackBuilder b;
|
VPackBuilder b;
|
||||||
|
|
|
@ -368,11 +368,11 @@ void RestVocbaseBaseHandler::generateDocument(VPackSlice const& input,
|
||||||
if (generateBody) {
|
if (generateBody) {
|
||||||
writeResult(document, *options);
|
writeResult(document, *options);
|
||||||
} else {
|
} else {
|
||||||
if (returnVelocypack()){
|
if (returnVelocypack()) {
|
||||||
_response->setContentType(StaticStrings::MimeTypeVPack);
|
_response->setContentType(HttpResponse::CONTENT_TYPE_VPACK);
|
||||||
_response->headResponse(document.byteSize());
|
_response->headResponse(document.byteSize());
|
||||||
} else {
|
} else {
|
||||||
_response->setContentType(StaticStrings::MimeTypeJson);
|
_response->setContentType(HttpResponse::CONTENT_TYPE_JSON);
|
||||||
|
|
||||||
// TODO can we optimize this?
|
// TODO can we optimize this?
|
||||||
// Just dump some where else to find real length
|
// Just dump some where else to find real length
|
||||||
|
|
|
@ -165,7 +165,7 @@ void WorkMonitor::vpackHandler(VPackBuilder* b, WorkDescription* desc) {
|
||||||
void WorkMonitor::sendWorkOverview(uint64_t taskId, std::string const& data) {
|
void WorkMonitor::sendWorkOverview(uint64_t taskId, std::string const& data) {
|
||||||
auto response = std::make_unique<HttpResponse>(GeneralResponse::ResponseCode::OK);
|
auto response = std::make_unique<HttpResponse>(GeneralResponse::ResponseCode::OK);
|
||||||
|
|
||||||
response->setContentType(StaticStrings::MimeTypeJson);
|
response->setContentType(HttpResponse::CONTENT_TYPE_JSON);
|
||||||
TRI_AppendString2StringBuffer(response->body().stringBuffer(), data.c_str(),
|
TRI_AppendString2StringBuffer(response->body().stringBuffer(), data.c_str(),
|
||||||
data.length());
|
data.length());
|
||||||
|
|
||||||
|
|
|
@ -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,
|
int TRI_AppendString2StringBuffer(TRI_string_buffer_t* self, char const* str,
|
||||||
size_t len);
|
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
|
/// @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;
|
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
|
/// @brief appends characters but json-encode the null-terminated string
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -778,6 +786,10 @@ class StringBuffer {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void appendCharUnsafe(char chr) {
|
||||||
|
TRI_AppendCharUnsafeStringBuffer(&_buffer, chr);
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief appends as json-encoded
|
/// @brief appends as json-encoded
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -804,6 +816,10 @@ class StringBuffer {
|
||||||
TRI_AppendString2StringBuffer(&_buffer, str, len);
|
TRI_AppendString2StringBuffer(&_buffer, str, len);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void appendTextUnsafe(char const* str, size_t len) {
|
||||||
|
TRI_AppendStringUnsafeStringBuffer(&_buffer, str, len);
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief appends characters
|
/// @brief appends characters
|
||||||
|
@ -822,6 +838,10 @@ class StringBuffer {
|
||||||
TRI_AppendString2StringBuffer(&_buffer, str.c_str(), str.length());
|
TRI_AppendString2StringBuffer(&_buffer, str.c_str(), str.length());
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void appendTextUnsafe(std::string const& str) {
|
||||||
|
TRI_AppendStringUnsafeStringBuffer(&_buffer, str.c_str(), str.length());
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief appends a string buffer
|
/// @brief appends a string buffer
|
||||||
|
|
|
@ -109,10 +109,6 @@ class GeneralResponse {
|
||||||
_responseCode = responseCode;
|
_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
|
// 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
|
// with the given name was specified by the client, the empty string is
|
||||||
// returned.
|
// 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 const& value);
|
||||||
void setHeaderNC(std::string const& key, std::string&& 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:
|
protected:
|
||||||
ResponseCode _responseCode;
|
ResponseCode _responseCode;
|
||||||
std::unordered_map<std::string, std::string> _headers;
|
std::unordered_map<std::string, std::string> _headers;
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
#include "HttpResponse.h"
|
#include "HttpResponse.h"
|
||||||
|
|
||||||
|
#include "Basics/Exceptions.h"
|
||||||
#include "Basics/StringBuffer.h"
|
#include "Basics/StringBuffer.h"
|
||||||
#include "Basics/StringUtils.h"
|
#include "Basics/StringUtils.h"
|
||||||
#include "Basics/tri-strings.h"
|
#include "Basics/tri-strings.h"
|
||||||
|
@ -36,16 +37,17 @@ bool HttpResponse::HIDE_PRODUCT_HEADER = false;
|
||||||
|
|
||||||
HttpResponse::HttpResponse(ResponseCode code)
|
HttpResponse::HttpResponse(ResponseCode code)
|
||||||
: GeneralResponse(code),
|
: GeneralResponse(code),
|
||||||
|
_connectionType(CONNECTION_KEEP_ALIVE),
|
||||||
|
_contentType(CONTENT_TYPE_TEXT),
|
||||||
_isHeadResponse(false),
|
_isHeadResponse(false),
|
||||||
_isChunked(false),
|
_isChunked(false), // TODO: remove
|
||||||
_body(TRI_UNKNOWN_MEM_ZONE, false),
|
_body(TRI_UNKNOWN_MEM_ZONE, false),
|
||||||
_bodySize(0) {
|
_bodySize(0) {
|
||||||
if (!HIDE_PRODUCT_HEADER) {
|
|
||||||
_headers[StaticStrings::Server] = "ArangoDB";
|
|
||||||
}
|
|
||||||
|
|
||||||
_headers[StaticStrings::Connection] = StaticStrings::KeepAlive;
|
if (_body.c_str() == nullptr) {
|
||||||
_headers[StaticStrings::ContentTypeHeader] = StaticStrings::MimeTypeText;
|
// 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,
|
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) {
|
void HttpResponse::writeHeader(StringBuffer* output) {
|
||||||
bool const capitalizeHeaders = true;
|
|
||||||
|
|
||||||
output->appendText(TRI_CHAR_LENGTH_PAIR("HTTP/1.1 "));
|
output->appendText(TRI_CHAR_LENGTH_PAIR("HTTP/1.1 "));
|
||||||
output->appendText(responseString(_responseCode));
|
output->appendText(responseString(_responseCode));
|
||||||
output->appendText(TRI_CHAR_LENGTH_PAIR("\r\n"));
|
output->appendText("\r\n", 2);
|
||||||
|
|
||||||
bool seenTransferEncoding = false;
|
bool seenServerHeader = false;
|
||||||
|
bool seenConnectionHeader = false;
|
||||||
|
bool seenTransferEncodingHeader = false;
|
||||||
std::string transferEncoding;
|
std::string transferEncoding;
|
||||||
|
|
||||||
for (auto const& it : _headers) {
|
for (auto const& it : _headers) {
|
||||||
|
@ -140,80 +142,116 @@ void HttpResponse::writeHeader(StringBuffer* output) {
|
||||||
// save transfer encoding
|
// save transfer encoding
|
||||||
if (keyLength == 17 && key[0] == 't' &&
|
if (keyLength == 17 && key[0] == 't' &&
|
||||||
memcmp(key.c_str(), "transfer-encoding", keyLength) == 0) {
|
memcmp(key.c_str(), "transfer-encoding", keyLength) == 0) {
|
||||||
seenTransferEncoding = true;
|
seenTransferEncodingHeader = true;
|
||||||
transferEncoding = it.second;
|
transferEncoding = it.second;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (capitalizeHeaders) {
|
if (keyLength == 6 && key[0] == 's' &&
|
||||||
char const* p = key.c_str();
|
memcmp(key.c_str(), "server", keyLength) == 0) {
|
||||||
char const* end = p + keyLength;
|
// this ensures we don't print two "Server" headers
|
||||||
int capState = 1;
|
seenServerHeader = true;
|
||||||
|
// go on and use the user-defined "Server" header value
|
||||||
while (p < end) {
|
} else if (keyLength == 10 && key[0] == 'c' &&
|
||||||
if (capState == 1) {
|
memcmp(key.c_str(), "connection", keyLength) == 0) {
|
||||||
// upper case
|
// this ensures we don't print two "Connection" headers
|
||||||
output->appendChar(::toupper(*p));
|
seenConnectionHeader = true;
|
||||||
capState = 0;
|
// go on and use the user-defined "Connection" header value
|
||||||
} 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
output->appendText(TRI_CHAR_LENGTH_PAIR(": "));
|
// reserve enough space for header name + ": " + value + "\r\n"
|
||||||
output->appendText(it.second);
|
if (output->reserve(keyLength + 2 + it.second.size() + 2) != TRI_ERROR_NO_ERROR) {
|
||||||
output->appendText(TRI_CHAR_LENGTH_PAIR("\r\n"));
|
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) {
|
for (auto const& it : _cookies) {
|
||||||
if (capitalizeHeaders) {
|
output->appendText(TRI_CHAR_LENGTH_PAIR("Set-Cookie: "));
|
||||||
output->appendText(TRI_CHAR_LENGTH_PAIR("Set-Cookie: "));
|
|
||||||
} else {
|
|
||||||
output->appendText(TRI_CHAR_LENGTH_PAIR("set-cookie: "));
|
|
||||||
}
|
|
||||||
|
|
||||||
output->appendText(it);
|
output->appendText(it);
|
||||||
output->appendText(TRI_CHAR_LENGTH_PAIR("\r\n"));
|
output->appendText("\r\n", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seenTransferEncoding && transferEncoding == "chunked") {
|
if (seenTransferEncodingHeader && transferEncoding == "chunked") {
|
||||||
if (capitalizeHeaders) {
|
output->appendText(
|
||||||
output->appendText(
|
TRI_CHAR_LENGTH_PAIR("Transfer-Encoding: chunked\r\n\r\n"));
|
||||||
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"));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (seenTransferEncoding) {
|
if (seenTransferEncodingHeader) {
|
||||||
if (capitalizeHeaders) {
|
output->appendText(TRI_CHAR_LENGTH_PAIR("Transfer-Encoding: "));
|
||||||
output->appendText(TRI_CHAR_LENGTH_PAIR("Transfer-Encoding: "));
|
|
||||||
} else {
|
|
||||||
output->appendText(TRI_CHAR_LENGTH_PAIR("transfer-encoding: "));
|
|
||||||
}
|
|
||||||
|
|
||||||
output->appendText(transferEncoding);
|
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: "));
|
||||||
output->appendText(TRI_CHAR_LENGTH_PAIR("Content-Length: "));
|
|
||||||
} else {
|
|
||||||
output->appendText(TRI_CHAR_LENGTH_PAIR("content-length: "));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_isHeadResponse) {
|
if (_isHeadResponse) {
|
||||||
// From http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13
|
// 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->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
|
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -42,6 +42,21 @@ class HttpResponse : public GeneralResponse {
|
||||||
bool isHeadResponse() const { return _isHeadResponse; }
|
bool isHeadResponse() const { return _isHeadResponse; }
|
||||||
bool isChunked() const { return _isChunked; }
|
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:
|
public:
|
||||||
using GeneralResponse::setHeader;
|
using GeneralResponse::setHeader;
|
||||||
|
|
||||||
|
@ -61,6 +76,24 @@ class HttpResponse : public GeneralResponse {
|
||||||
basics::StringBuffer& body() { return _body; }
|
basics::StringBuffer& body() { return _body; }
|
||||||
size_t bodySize() const;
|
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
|
// you should call writeHeader only after the body has been created
|
||||||
void writeHeader(basics::StringBuffer*);
|
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
|
// the body must already be set. deflate is then run on the existing body
|
||||||
int deflate(size_t = 16384);
|
int deflate(size_t = 16384);
|
||||||
|
|
||||||
// checks for special headers
|
|
||||||
void checkHeader(std::string const& key, std::string const& value) override final;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
ConnectionType _connectionType;
|
||||||
|
ContentType _contentType;
|
||||||
bool _isHeadResponse;
|
bool _isHeadResponse;
|
||||||
bool _isChunked;
|
bool _isChunked; // TODO: remove
|
||||||
std::vector<std::string> _cookies;
|
std::vector<std::string> _cookies;
|
||||||
basics::StringBuffer _body;
|
basics::StringBuffer _body;
|
||||||
size_t _bodySize;
|
size_t _bodySize;
|
||||||
|
|
Loading…
Reference in New Issue