diff --git a/CMakeLists.txt b/CMakeLists.txt index bcb8609e7e..c72826dc03 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,10 @@ if (POLICY CMP0017) cmake_policy(SET CMP0017 NEW) endif () +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release CACHE string "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) +endif () + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "deployment target for MacOSX") if (WIN32) diff --git a/arangod/RestHandler/RestBaseHandler.cpp b/arangod/RestHandler/RestBaseHandler.cpp index ba01f347c0..83e8ecf988 100644 --- a/arangod/RestHandler/RestBaseHandler.cpp +++ b/arangod/RestHandler/RestBaseHandler.cpp @@ -23,19 +23,17 @@ #include "RestBaseHandler.h" -#include "Basics/StaticStrings.h" -#include "Basics/StringUtils.h" -#include "Basics/VelocyPackDumper.h" -#include "Basics/VPackStringBufferAdapter.h" -#include "Rest/HttpRequest.h" -#include "Rest/HttpResponse.h" -#include "Utils/TransactionContext.h" - #include #include #include #include +#include "Basics/StaticStrings.h" +#include "Basics/StringUtils.h" +#include "Rest/HttpRequest.h" +#include "Rest/HttpResponse.h" +#include "Utils/TransactionContext.h" + using namespace arangodb; using namespace arangodb::basics; using namespace arangodb::rest; @@ -52,7 +50,6 @@ void RestBaseHandler::handleError(Exception const& ex) { void RestBaseHandler::generateResult(GeneralResponse::ResponseCode code, VPackSlice const& slice) { - createResponse(code); writeResult(slice, VPackOptions::Defaults); } @@ -61,9 +58,9 @@ void RestBaseHandler::generateResult(GeneralResponse::ResponseCode code, /// @brief generates a result from VelocyPack //////////////////////////////////////////////////////////////////////////////// -void RestBaseHandler::generateResult(GeneralResponse::ResponseCode code, - VPackSlice const& slice, - std::shared_ptr context) { +void RestBaseHandler::generateResult( + GeneralResponse::ResponseCode code, VPackSlice const& slice, + std::shared_ptr context) { createResponse(code); writeResult(slice, *(context->getVPackOptions())); } @@ -116,7 +113,8 @@ void RestBaseHandler::generateError(GeneralResponse::ResponseCode code, //////////////////////////////////////////////////////////////////////////////// void RestBaseHandler::generateOOMError() { - generateError(GeneralResponse::ResponseCode::SERVER_ERROR, TRI_ERROR_OUT_OF_MEMORY); + generateError(GeneralResponse::ResponseCode::SERVER_ERROR, + TRI_ERROR_OUT_OF_MEMORY); } //////////////////////////////////////////////////////////////////////////////// @@ -124,43 +122,23 @@ void RestBaseHandler::generateOOMError() { //////////////////////////////////////////////////////////////////////////////// void RestBaseHandler::generateCanceled() { - return generateError(GeneralResponse::ResponseCode::GONE, TRI_ERROR_REQUEST_CANCELED); -} - -////////////////////////////////////////////////////////////////////////////// -/// @brief checks if velocypack is expected as answer -////////////////////////////////////////////////////////////////////////////// - -bool RestBaseHandler::returnVelocypack() const { - std::string const& result = _request->header(StaticStrings::Accept); - return (std::string::npos != result.find(StaticStrings::MimeTypeVPack)); + return generateError(GeneralResponse::ResponseCode::GONE, + TRI_ERROR_REQUEST_CANCELED); } ////////////////////////////////////////////////////////////////////////////// /// @brief writes volocypack or json to response ////////////////////////////////////////////////////////////////////////////// -void RestBaseHandler::writeResult(arangodb::velocypack::Slice const& slice, +void RestBaseHandler::writeResult(arangodb::velocypack::Slice const& slice, VPackOptions const& options) { - if (returnVelocypack()) { - _response->setContentType(HttpResponse::CONTENT_TYPE_VPACK); - _response->body().appendText(slice.startAs(), static_cast(slice.byteSize())); - return; - } - - // JSON - - _response->setContentType(HttpResponse::CONTENT_TYPE_JSON); try { - arangodb::basics::VelocyPackDumper dumper(&(_response->body()), &options); - dumper.dumpValue(slice); - //VPackStringBufferAdapter buffer(_response->body().stringBuffer()); - //VPackDumper dumper(&buffer, &options); - //dumper.dump(slice); + _response->fillBody(_request, slice, true, options); } catch (std::exception const& ex) { - generateError(GeneralResponse::ResponseCode::SERVER_ERROR, TRI_ERROR_INTERNAL, ex.what()); + generateError(GeneralResponse::ResponseCode::SERVER_ERROR, + TRI_ERROR_INTERNAL, ex.what()); } catch (...) { - generateError(GeneralResponse::ResponseCode::SERVER_ERROR, TRI_ERROR_INTERNAL, - "cannot generate output"); + generateError(GeneralResponse::ResponseCode::SERVER_ERROR, + TRI_ERROR_INTERNAL, "cannot generate output"); } } diff --git a/arangod/RestHandler/RestBaseHandler.h b/arangod/RestHandler/RestBaseHandler.h index a252228b4c..37e68fd4b4 100644 --- a/arangod/RestHandler/RestBaseHandler.h +++ b/arangod/RestHandler/RestBaseHandler.h @@ -24,8 +24,8 @@ #ifndef ARANGOD_REST_HANDLER_REST_BASE_HANDLER_H #define ARANGOD_REST_HANDLER_REST_BASE_HANDLER_H 1 -#include "Basics/Common.h" #include "HttpServer/HttpHandler.h" + #include "Rest/HttpResponse.h" namespace arangodb { @@ -36,71 +36,36 @@ struct Options; class Slice; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief default handler for error handling and json in-/output -//////////////////////////////////////////////////////////////////////////////// - class RestBaseHandler : public rest::HttpHandler { public: explicit RestBaseHandler(HttpRequest* request); void handleError(basics::Exception const&) override; - ////////////////////////////////////////////////////////////////////////////// - /// @brief generates a result from VelocyPack - ////////////////////////////////////////////////////////////////////////////// - + public: + // generates a result from VelocyPack void generateResult(GeneralResponse::ResponseCode, arangodb::velocypack::Slice const& slice); - ////////////////////////////////////////////////////////////////////////////// - /// @brief generates a result from VelocyPack - ////////////////////////////////////////////////////////////////////////////// - + // generates a result from VelocyPack void generateResult(GeneralResponse::ResponseCode, arangodb::velocypack::Slice const& slice, std::shared_ptr context); - ////////////////////////////////////////////////////////////////////////////// - /// @brief generates an error - ////////////////////////////////////////////////////////////////////////////// - + // generates an error void generateError(GeneralResponse::ResponseCode, int); - ////////////////////////////////////////////////////////////////////////////// - /// @brief generates an error - ////////////////////////////////////////////////////////////////////////////// - + // generates an error void generateError(GeneralResponse::ResponseCode, int, std::string const&); - ////////////////////////////////////////////////////////////////////////////// - /// @brief generates an out of memory error - ////////////////////////////////////////////////////////////////////////////// - + // generates an out of memory error void generateOOMError(); - ////////////////////////////////////////////////////////////////////////////// - /// @brief generates a canceled message - ////////////////////////////////////////////////////////////////////////////// - + // generates a canceled message void generateCanceled(); - ////////////////////////////////////////////////////////////////////////////// - /// @brief dumps the response as JSON into the response string buffer - ////////////////////////////////////////////////////////////////////////////// - protected: - - ////////////////////////////////////////////////////////////////////////////// - /// @brief checks if velocypack is expected as answer - ////////////////////////////////////////////////////////////////////////////// - - bool returnVelocypack() const; - - ////////////////////////////////////////////////////////////////////////////// - /// @brief write result back to client - ////////////////////////////////////////////////////////////////////////////// - + // write result back to client void writeResult(arangodb::velocypack::Slice const& slice, arangodb::velocypack::Options const& options); diff --git a/arangod/RestHandler/RestVocbaseBaseHandler.cpp b/arangod/RestHandler/RestVocbaseBaseHandler.cpp index 8078c299fa..f91c915639 100644 --- a/arangod/RestHandler/RestVocbaseBaseHandler.cpp +++ b/arangod/RestHandler/RestVocbaseBaseHandler.cpp @@ -175,7 +175,8 @@ void RestVocbaseBaseHandler::generateSaved( VPackBuilder errorBuilder; errorBuilder.openObject(); for (auto const& it : result.countErrorCodes) { - errorBuilder.add(basics::StringUtils::itoa(it.first), VPackValue(it.second)); + errorBuilder.add(basics::StringUtils::itoa(it.first), + VPackValue(it.second)); } errorBuilder.close(); _response->setHeaderNC(StaticStrings::ErrorCodes, errorBuilder.toJson()); @@ -204,21 +205,21 @@ void RestVocbaseBaseHandler::generateDeleted( //////////////////////////////////////////////////////////////////////////////// void RestVocbaseBaseHandler::generate20x( - arangodb::OperationResult const& result, - std::string const& collectionName, - TRI_col_type_e type, - VPackOptions const* options) { - + arangodb::OperationResult const& result, std::string const& collectionName, + TRI_col_type_e type, VPackOptions const* options) { VPackSlice slice = result.slice(); TRI_ASSERT(slice.isObject() || slice.isArray()); if (slice.isObject()) { - _response->setHeaderNC(StaticStrings::Etag, "\"" + slice.get(StaticStrings::RevString).copyString() + "\""); + _response->setHeaderNC( + StaticStrings::Etag, + "\"" + slice.get(StaticStrings::RevString).copyString() + "\""); // pre 1.4 location headers withdrawn for >= 3.0 std::string escapedHandle(assembleDocumentId( - collectionName, slice.get(StaticStrings::KeyString).copyString(), true)); + collectionName, slice.get(StaticStrings::KeyString).copyString(), + true)); _response->setHeaderNC(StaticStrings::Location, - std::string("/_db/" + _request->databaseName() + - DOCUMENT_PATH + "/" + escapedHandle)); + std::string("/_db/" + _request->databaseName() + + DOCUMENT_PATH + "/" + escapedHandle)); } writeResult(slice, *options); @@ -248,26 +249,28 @@ void RestVocbaseBaseHandler::generateForbidden() { void RestVocbaseBaseHandler::generatePreconditionFailed( VPackSlice const& slice) { - createResponse(GeneralResponse::ResponseCode::PRECONDITION_FAILED); if (slice.isObject()) { // single document case - std::string const rev = VelocyPackHelper::getStringValue(slice, StaticStrings::KeyString, ""); + std::string const rev = + VelocyPackHelper::getStringValue(slice, StaticStrings::KeyString, ""); _response->setHeaderNC(StaticStrings::Etag, "\"" + rev + "\""); } VPackBuilder builder; { VPackObjectBuilder guard(&builder); builder.add("error", VPackValue(true)); - builder.add( - "code", - VPackValue(static_cast(GeneralResponse::ResponseCode::PRECONDITION_FAILED))); + builder.add("code", + VPackValue(static_cast( + GeneralResponse::ResponseCode::PRECONDITION_FAILED))); builder.add("errorNum", VPackValue(TRI_ERROR_ARANGO_CONFLICT)); builder.add("errorMessage", VPackValue("precondition failed")); if (slice.isObject()) { builder.add(StaticStrings::IdString, slice.get(StaticStrings::IdString)); - builder.add(StaticStrings::KeyString, slice.get(StaticStrings::KeyString)); - builder.add(StaticStrings::RevString, slice.get(StaticStrings::RevString)); + builder.add(StaticStrings::KeyString, + slice.get(StaticStrings::KeyString)); + builder.add(StaticStrings::RevString, + slice.get(StaticStrings::RevString)); } else { builder.add("result", slice); } @@ -282,11 +285,12 @@ void RestVocbaseBaseHandler::generatePreconditionFailed( //////////////////////////////////////////////////////////////////////////////// void RestVocbaseBaseHandler::generatePreconditionFailed( - std::string const& collectionName, std::string const& key, TRI_voc_rid_t rev) { - + std::string const& collectionName, std::string const& key, + TRI_voc_rid_t rev) { VPackBuilder builder; builder.openObject(); - builder.add(StaticStrings::IdString, VPackValue(assembleDocumentId(collectionName, key, false))); + builder.add(StaticStrings::IdString, + VPackValue(assembleDocumentId(collectionName, key, false))); builder.add(StaticStrings::KeyString, VPackValue(std::to_string(rev))); builder.add(StaticStrings::RevString, VPackValue(key)); builder.close(); @@ -300,7 +304,8 @@ void RestVocbaseBaseHandler::generatePreconditionFailed( void RestVocbaseBaseHandler::generateNotModified(TRI_voc_rid_t rid) { createResponse(GeneralResponse::ResponseCode::NOT_MODIFIED); - _response->setHeaderNC(StaticStrings::Etag, "\"" + StringUtils::itoa(rid) + "\""); + _response->setHeaderNC(StaticStrings::Etag, + "\"" + StringUtils::itoa(rid) + "\""); } //////////////////////////////////////////////////////////////////////////////// @@ -314,43 +319,23 @@ void RestVocbaseBaseHandler::generateDocument(VPackSlice const& input, std::string rev; if (document.isObject()) { - rev = VelocyPackHelper::getStringValue(document, StaticStrings::RevString, ""); + rev = VelocyPackHelper::getStringValue(document, StaticStrings::RevString, + ""); } // and generate a response createResponse(GeneralResponse::ResponseCode::OK); + + // set ETAG header if (!rev.empty()) { _response->setHeaderNC(StaticStrings::Etag, "\"" + rev + "\""); } - if (generateBody) { - writeResult(document, *options); - return; - } - - // no body, i.e. a HEAD response - if (returnVelocypack()) { - _response->setContentType(HttpResponse::CONTENT_TYPE_VPACK); - _response->headResponse(static_cast(document.byteSize())); - } else { - _response->setContentType(HttpResponse::CONTENT_TYPE_JSON); - - // TODO can we optimize this? - // Just dump some where else to find real length - StringBuffer tmp(TRI_UNKNOWN_MEM_ZONE, false); - // convert object to string - VPackStringBufferAdapter buffer(tmp.stringBuffer()); - - //usual dumping - but not to the response body - VPackDumper dumper(&buffer, options); - try { - dumper.dump(document); - } catch (...) { - generateError(GeneralResponse::ResponseCode::SERVER_ERROR, TRI_ERROR_INTERNAL, - "cannot generate output"); - } - // set the length of what would have been written - _response->headResponse(tmp.length()); + try { + _response->fillBody(_request, document, generateBody, *options); + } catch (...) { + generateError(GeneralResponse::ResponseCode::SERVER_ERROR, + TRI_ERROR_INTERNAL, "cannot generate output"); } } @@ -391,11 +376,13 @@ void RestVocbaseBaseHandler::generateTransactionError( return; case TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD: - generateError(GeneralResponse::ResponseCode::BAD, res, "invalid document handle"); + generateError(GeneralResponse::ResponseCode::BAD, res, + "invalid document handle"); return; case TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE: - generateError(GeneralResponse::ResponseCode::BAD, res, "invalid edge attribute"); + generateError(GeneralResponse::ResponseCode::BAD, res, + "invalid edge attribute"); return; case TRI_ERROR_ARANGO_OUT_OF_KEYS: @@ -417,8 +404,8 @@ void RestVocbaseBaseHandler::generateTransactionError( return; case TRI_ERROR_ARANGO_CONFLICT: - generatePreconditionFailed(collectionName, - key.empty() ? "unknown" : key, rev); + generatePreconditionFailed(collectionName, key.empty() ? "unknown" : key, + rev); return; case TRI_ERROR_CLUSTER_SHARD_GONE: @@ -485,19 +472,23 @@ void RestVocbaseBaseHandler::generateTransactionError( return; case TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD: - generateError(GeneralResponse::ResponseCode::BAD, result.code, "invalid document key"); + generateError(GeneralResponse::ResponseCode::BAD, result.code, + "invalid document key"); return; case TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD: - generateError(GeneralResponse::ResponseCode::BAD, result.code, "invalid document handle"); + generateError(GeneralResponse::ResponseCode::BAD, result.code, + "invalid document handle"); return; case TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE: - generateError(GeneralResponse::ResponseCode::BAD, result.code, "invalid edge attribute"); + generateError(GeneralResponse::ResponseCode::BAD, result.code, + "invalid edge attribute"); return; case TRI_ERROR_ARANGO_OUT_OF_KEYS: - generateError(GeneralResponse::ResponseCode::SERVER_ERROR, result.code, "out of keys"); + generateError(GeneralResponse::ResponseCode::SERVER_ERROR, result.code, + "out of keys"); return; case TRI_ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED: @@ -535,7 +526,8 @@ void RestVocbaseBaseHandler::generateTransactionError( } case TRI_ERROR_CLUSTER_UNSUPPORTED: { - generateError(GeneralResponse::ResponseCode::NOT_IMPLEMENTED, result.code); + generateError(GeneralResponse::ResponseCode::NOT_IMPLEMENTED, + result.code); return; } @@ -560,8 +552,6 @@ void RestVocbaseBaseHandler::generateTransactionError( } } - - //////////////////////////////////////////////////////////////////////////////// /// @brief extracts the revision //////////////////////////////////////////////////////////////////////////////// @@ -648,12 +638,13 @@ bool RestVocbaseBaseHandler::extractBooleanParameter(char const* name, std::shared_ptr RestVocbaseBaseHandler::parseVelocyPackBody( VPackOptions const* options, bool& success) { bool found; - std::string const& contentType = _request->header(StaticStrings::ContentTypeHeader, found); + std::string const& contentType = + _request->header(StaticStrings::ContentTypeHeader, found); try { success = true; - if (found && contentType.size() == StaticStrings::MimeTypeVPack.size() && + if (found && contentType.size() == StaticStrings::MimeTypeVPack.size() && contentType == StaticStrings::MimeTypeVPack) { VPackSlice slice{_request->body().c_str()}; auto builder = std::make_shared(options); @@ -685,7 +676,8 @@ void RestVocbaseBaseHandler::prepareExecute() { std::string const& shardId = _request->header("x-arango-nolock", found); if (found) { - _nolockHeaderSet = new std::unordered_set{ std::string(shardId) }; + _nolockHeaderSet = + new std::unordered_set{std::string(shardId)}; arangodb::Transaction::_makeNolockHeaders = _nolockHeaderSet; } } diff --git a/lib/Basics/VPackStringBufferAdapter.h b/lib/Basics/VPackStringBufferAdapter.h index 2ff772102d..a33db9dc8f 100644 --- a/lib/Basics/VPackStringBufferAdapter.h +++ b/lib/Basics/VPackStringBufferAdapter.h @@ -21,6 +21,9 @@ /// @author Michael Hackstein //////////////////////////////////////////////////////////////////////////////// +#ifndef ARANGODB_BASICS_VPACKSTRING_H +#define ARANGODB_BASICS_VPACKSTRING_H + #include "Basics/StringBuffer.h" #include "Basics/Exceptions.h" @@ -74,3 +77,5 @@ class VPackStringBufferAdapter final : public VPackSink { }; } } + +#endif diff --git a/lib/Rest/GeneralRequest.cpp b/lib/Rest/GeneralRequest.cpp index 2619accdc8..06e91bb8ae 100644 --- a/lib/Rest/GeneralRequest.cpp +++ b/lib/Rest/GeneralRequest.cpp @@ -258,3 +258,7 @@ void GeneralRequest::setArrayValue(char* key, size_t length, char const* value) _arrayValues[std::string(key, length)].emplace_back(value); } +bool GeneralRequest::velocyPackResponse () const { + std::string const& result = header(StaticStrings::Accept); + return (std::string::npos != result.find(StaticStrings::MimeTypeVPack)); +} diff --git a/lib/Rest/GeneralRequest.h b/lib/Rest/GeneralRequest.h index b4334621bc..fc3273edd9 100644 --- a/lib/Rest/GeneralRequest.h +++ b/lib/Rest/GeneralRequest.h @@ -158,6 +158,8 @@ class GeneralRequest { } void setArrayValue(std::string const& key, std::string const& value); + bool velocyPackResponse () const; + protected: void setValue(char const* key, char const* value); void setArrayValue(char* key, size_t length, char const* value); diff --git a/lib/Rest/GeneralResponse.h b/lib/Rest/GeneralResponse.h index 61e31a700f..76679e569d 100644 --- a/lib/Rest/GeneralResponse.h +++ b/lib/Rest/GeneralResponse.h @@ -30,6 +30,13 @@ #include "Basics/StringUtils.h" namespace arangodb { +namespace velocypack { +struct Options; +class Slice; +} + +class GeneralRequest; + class GeneralResponse { GeneralResponse() = delete; GeneralResponse(GeneralResponse const&) = delete; @@ -110,23 +117,32 @@ class GeneralResponse { _responseCode = responseCode; } - std::unordered_map headers() const { return _headers; } + std::unordered_map headers() const { + return _headers; + } - /// @brief adds a header. the header field name will be lower-cased + // adds a header. the header field name will be lower-cased void setHeader(std::string const& key, std::string const& value) { _headers[basics::StringUtils::tolower(key)] = value; } - /// @brief adds a header. the header field name must be lower-cased + // adds a header. the header field name must be lower-cased void setHeaderNC(std::string const& key, std::string const& value) { _headers[key] = value; } - - /// @brief adds a header. the header field name must be lower-cased + + // adds a header. the header field name must be lower-cased void setHeaderNC(std::string const& key, std::string&& value) { _headers[key] = std::move(value); } + public: + // generates the response body, sets the content type; this might + // throw an error + virtual void fillBody(GeneralRequest const*, + arangodb::velocypack::Slice const&, bool generateBody, + arangodb::velocypack::Options const&) = 0; + protected: ResponseCode _responseCode; std::unordered_map _headers; diff --git a/lib/Rest/HttpRequest.cpp b/lib/Rest/HttpRequest.cpp index 9aa0b1b70c..4d2b16d1bd 100644 --- a/lib/Rest/HttpRequest.cpp +++ b/lib/Rest/HttpRequest.cpp @@ -38,7 +38,7 @@ using namespace arangodb; using namespace arangodb::basics; - + HttpRequest::HttpRequest(ConnectionInfo const& connectionInfo, char const* header, size_t length, bool allowMethodOverride) @@ -46,7 +46,6 @@ HttpRequest::HttpRequest(ConnectionInfo const& connectionInfo, _contentLength(0), _header(nullptr), _allowMethodOverride(allowMethodOverride) { - if (0 < length) { _header = new char[length + 1]; memcpy(_header, header, length); @@ -55,9 +54,7 @@ HttpRequest::HttpRequest(ConnectionInfo const& connectionInfo, } } -HttpRequest::~HttpRequest() { - delete[] _header; -} +HttpRequest::~HttpRequest() { delete[] _header; } void HttpRequest::parseHeader(size_t length) { char* start = _header; @@ -363,7 +360,8 @@ void HttpRequest::parseHeader(size_t length) { } if (keyBegin < keyEnd) { - setHeader(keyBegin, keyEnd - keyBegin, valueBegin, valueEnd - valueBegin); + setHeader(keyBegin, keyEnd - keyBegin, valueBegin, + valueEnd - valueBegin); } } @@ -508,8 +506,8 @@ void HttpRequest::setHeader(char const* key, size_t keyLength, _contentLength = StringUtils::int64(value, valueLength); // do not store this header return; - } - + } + if (keyLength == 6 && memcmp(key, "cookie", keyLength) == 0) { // 6 = strlen("cookie") parseCookies(value, valueLength); @@ -522,10 +520,9 @@ void HttpRequest::setHeader(char const* key, size_t keyLength, // override HTTP method? if ((keyLength == 13 && memcmp(key, "x-http-method", keyLength) == 0) || - (keyLength == 17 && - memcmp(key, "x-method-override", keyLength) == 0) || + (keyLength == 17 && memcmp(key, "x-method-override", keyLength) == 0) || (keyLength == 22 && - memcmp(key, "x-http-method-override", keyLength) == 0)) { + memcmp(key, "x-http-method-override", keyLength) == 0)) { std::string overriddenType(value, valueLength); StringUtils::tolowerInPlace(&overriddenType); @@ -657,21 +654,20 @@ std::string const& HttpRequest::cookieValue(std::string const& key) const { return it->second; } -std::string const& HttpRequest::cookieValue(std::string const& key, bool& found) const { +std::string const& HttpRequest::cookieValue(std::string const& key, + bool& found) const { auto it = _cookies.find(key); if (it == _cookies.end()) { found = false; - return StaticStrings::Empty; + return StaticStrings::Empty; } found = true; return it->second; } -std::string const& HttpRequest::body() const { - return _body; -} +std::string const& HttpRequest::body() const { return _body; } void HttpRequest::setBody(char const* body, size_t length) { _body = std::string(body, length); @@ -683,4 +679,3 @@ std::shared_ptr HttpRequest::toVelocyPack( parser.parse(body()); return parser.steal(); } - diff --git a/lib/Rest/HttpResponse.cpp b/lib/Rest/HttpResponse.cpp index 6aac79110e..56d9bb3878 100644 --- a/lib/Rest/HttpResponse.cpp +++ b/lib/Rest/HttpResponse.cpp @@ -24,10 +24,18 @@ #include "HttpResponse.h" +#include +#include +#include +#include + #include "Basics/Exceptions.h" #include "Basics/StringBuffer.h" #include "Basics/StringUtils.h" +#include "Basics/VPackStringBufferAdapter.h" +#include "Basics/VelocyPackDumper.h" #include "Basics/tri-strings.h" +#include "Rest/GeneralRequest.h" using namespace arangodb; using namespace arangodb::basics; @@ -41,10 +49,9 @@ HttpResponse::HttpResponse(ResponseCode code) _isHeadResponse(false), _body(TRI_UNKNOWN_MEM_ZONE, false), _bodySize(0) { - if (_body.c_str() == nullptr) { // no buffer could be reserved. out of memory! - THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); + THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); } } @@ -121,7 +128,7 @@ void HttpResponse::writeHeader(StringBuffer* output) { output->appendText(TRI_CHAR_LENGTH_PAIR("HTTP/1.1 ")); output->appendText(responseString(_responseCode)); output->appendText("\r\n", 2); - + bool seenServerHeader = false; bool seenConnectionHeader = false; bool seenTransferEncodingHeader = false; @@ -145,20 +152,21 @@ void HttpResponse::writeHeader(StringBuffer* output) { continue; } - if (keyLength == 6 && key[0] == 's' && + if (keyLength == 6 && key[0] == 's' && memcmp(key.c_str(), "server", keyLength) == 0) { // this ensures we don't print two "Server" headers - seenServerHeader = true; + 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) { + } 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; + seenConnectionHeader = true; // go on and use the user-defined "Connection" header value } // reserve enough space for header name + ": " + value + "\r\n" - if (output->reserve(keyLength + 2 + it.second.size() + 2) != TRI_ERROR_NO_ERROR) { + if (output->reserve(keyLength + 2 + it.second.size() + 2) != + TRI_ERROR_NO_ERROR) { THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); } @@ -190,7 +198,7 @@ void HttpResponse::writeHeader(StringBuffer* output) { output->appendTextUnsafe(it.second); output->appendTextUnsafe("\r\n", 2); } - + // add "Server" response header if (!seenServerHeader && !HIDE_PRODUCT_HEADER) { output->appendText("Server: ArangoDB\r\n"); @@ -214,23 +222,29 @@ void HttpResponse::writeHeader(StringBuffer* output) { // add "Content-Type" header switch (_contentType) { case CONTENT_TYPE_JSON: - output->appendText(TRI_CHAR_LENGTH_PAIR("Content-Type: application/json; charset=utf-8\r\n")); + 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")); + 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")); + 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")); + 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")); + 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 + // the header should have been in _headers already, and should have been + // handled above } } @@ -273,3 +287,41 @@ void HttpResponse::writeHeader(StringBuffer* output) { // end of header, body to follow } +void HttpResponse::fillBody(GeneralRequest const* request, + arangodb::velocypack::Slice const& slice, + bool generateBody, VPackOptions const& options) { + // VELOCYPACK + if (request->velocyPackResponse()) { + setContentType(HttpResponse::CONTENT_TYPE_VPACK); + size_t length = static_cast(slice.byteSize()); + + if (generateBody) { + _body.appendText(slice.startAs(), length); + } else { + headResponse(length); + } + } + + // JSON + else { + setContentType(HttpResponse::CONTENT_TYPE_JSON); + + if (generateBody) { + arangodb::basics::VelocyPackDumper dumper(&_body, &options); + dumper.dumpValue(slice); + } else { + // TODO can we optimize this? + // Just dump some where else to find real length + StringBuffer tmp(TRI_UNKNOWN_MEM_ZONE, false); + + // convert object to string + VPackStringBufferAdapter buffer(tmp.stringBuffer()); + + // usual dumping - but not to the response body + VPackDumper dumper(&buffer, &options); + dumper.dump(slice); + + headResponse(tmp.length()); + } + } +} diff --git a/lib/Rest/HttpResponse.h b/lib/Rest/HttpResponse.h index 04e702a0a0..e9d8cafd31 100644 --- a/lib/Rest/HttpResponse.h +++ b/lib/Rest/HttpResponse.h @@ -45,14 +45,14 @@ class HttpResponse : public GeneralResponse { 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 + 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: @@ -76,7 +76,7 @@ class HttpResponse : public GeneralResponse { /// @brief set type of connection void setConnectionType(ConnectionType type) { _connectionType = type; } - + /// @brief set content-type void setContentType(ContentType type) { _contentType = type; } @@ -86,15 +86,21 @@ class HttpResponse : public GeneralResponse { _headers[arangodb::StaticStrings::ContentTypeHeader] = contentType; _contentType = CONTENT_TYPE_CUSTOM; } - + void setContentType(std::string&& contentType) { - _headers[arangodb::StaticStrings::ContentTypeHeader] = std::move(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*); + public: + void fillBody(GeneralRequest const*, arangodb::velocypack::Slice const&, + bool generateBody, + arangodb::velocypack::Options const&) override final; + private: // the body must already be set. deflate is then run on the existing body int deflate(size_t = 16384);