1
0
Fork 0

added fillBody

This commit is contained in:
Frank Celler 2016-05-13 15:14:44 +02:00
parent 2d9f384043
commit 9dbba248de
10 changed files with 212 additions and 197 deletions

View File

@ -23,19 +23,17 @@
#include "RestBaseHandler.h" #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 <velocypack/Builder.h> #include <velocypack/Builder.h>
#include <velocypack/Dumper.h> #include <velocypack/Dumper.h>
#include <velocypack/Options.h> #include <velocypack/Options.h>
#include <velocypack/velocypack-aliases.h> #include <velocypack/velocypack-aliases.h>
#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;
using namespace arangodb::basics; using namespace arangodb::basics;
using namespace arangodb::rest; using namespace arangodb::rest;
@ -52,7 +50,6 @@ void RestBaseHandler::handleError(Exception const& ex) {
void RestBaseHandler::generateResult(GeneralResponse::ResponseCode code, void RestBaseHandler::generateResult(GeneralResponse::ResponseCode code,
VPackSlice const& slice) { VPackSlice const& slice) {
createResponse(code); createResponse(code);
writeResult(slice, VPackOptions::Defaults); writeResult(slice, VPackOptions::Defaults);
} }
@ -61,9 +58,9 @@ void RestBaseHandler::generateResult(GeneralResponse::ResponseCode code,
/// @brief generates a result from VelocyPack /// @brief generates a result from VelocyPack
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void RestBaseHandler::generateResult(GeneralResponse::ResponseCode code, void RestBaseHandler::generateResult(
VPackSlice const& slice, GeneralResponse::ResponseCode code, VPackSlice const& slice,
std::shared_ptr<TransactionContext> context) { std::shared_ptr<TransactionContext> context) {
createResponse(code); createResponse(code);
writeResult(slice, *(context->getVPackOptions())); writeResult(slice, *(context->getVPackOptions()));
} }
@ -116,7 +113,8 @@ void RestBaseHandler::generateError(GeneralResponse::ResponseCode code,
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void RestBaseHandler::generateOOMError() { 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() { void RestBaseHandler::generateCanceled() {
return generateError(GeneralResponse::ResponseCode::GONE, TRI_ERROR_REQUEST_CANCELED); 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));
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief writes volocypack or json to response /// @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) { VPackOptions const& options) {
if (returnVelocypack()) {
_response->setContentType(HttpResponse::CONTENT_TYPE_VPACK);
_response->body().appendText(slice.startAs<const char>(), static_cast<size_t>(slice.byteSize()));
return;
}
// JSON
_response->setContentType(HttpResponse::CONTENT_TYPE_JSON);
try { try {
arangodb::basics::VelocyPackDumper dumper(&(_response->body()), &options); _response->fillBody(_request, slice, true, options);
dumper.dumpValue(slice);
//VPackStringBufferAdapter buffer(_response->body().stringBuffer());
//VPackDumper dumper(&buffer, &options);
//dumper.dump(slice);
} catch (std::exception const& ex) { } 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 (...) { } catch (...) {
generateError(GeneralResponse::ResponseCode::SERVER_ERROR, TRI_ERROR_INTERNAL, generateError(GeneralResponse::ResponseCode::SERVER_ERROR,
"cannot generate output"); TRI_ERROR_INTERNAL, "cannot generate output");
} }
} }

View File

@ -24,8 +24,8 @@
#ifndef ARANGOD_REST_HANDLER_REST_BASE_HANDLER_H #ifndef ARANGOD_REST_HANDLER_REST_BASE_HANDLER_H
#define ARANGOD_REST_HANDLER_REST_BASE_HANDLER_H 1 #define ARANGOD_REST_HANDLER_REST_BASE_HANDLER_H 1
#include "Basics/Common.h"
#include "HttpServer/HttpHandler.h" #include "HttpServer/HttpHandler.h"
#include "Rest/HttpResponse.h" #include "Rest/HttpResponse.h"
namespace arangodb { namespace arangodb {
@ -36,71 +36,36 @@ struct Options;
class Slice; class Slice;
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief default handler for error handling and json in-/output
////////////////////////////////////////////////////////////////////////////////
class RestBaseHandler : public rest::HttpHandler { class RestBaseHandler : public rest::HttpHandler {
public: public:
explicit RestBaseHandler(HttpRequest* request); explicit RestBaseHandler(HttpRequest* request);
void handleError(basics::Exception const&) override; void handleError(basics::Exception const&) override;
////////////////////////////////////////////////////////////////////////////// public:
/// @brief generates a result from VelocyPack // generates a result from VelocyPack
//////////////////////////////////////////////////////////////////////////////
void generateResult(GeneralResponse::ResponseCode, void generateResult(GeneralResponse::ResponseCode,
arangodb::velocypack::Slice const& slice); arangodb::velocypack::Slice const& slice);
////////////////////////////////////////////////////////////////////////////// // generates a result from VelocyPack
/// @brief generates a result from VelocyPack
//////////////////////////////////////////////////////////////////////////////
void generateResult(GeneralResponse::ResponseCode, void generateResult(GeneralResponse::ResponseCode,
arangodb::velocypack::Slice const& slice, arangodb::velocypack::Slice const& slice,
std::shared_ptr<arangodb::TransactionContext> context); std::shared_ptr<arangodb::TransactionContext> context);
////////////////////////////////////////////////////////////////////////////// // generates an error
/// @brief generates an error
//////////////////////////////////////////////////////////////////////////////
void generateError(GeneralResponse::ResponseCode, int); void generateError(GeneralResponse::ResponseCode, int);
////////////////////////////////////////////////////////////////////////////// // generates an error
/// @brief generates an error
//////////////////////////////////////////////////////////////////////////////
void generateError(GeneralResponse::ResponseCode, int, std::string const&); void generateError(GeneralResponse::ResponseCode, int, std::string const&);
////////////////////////////////////////////////////////////////////////////// // generates an out of memory error
/// @brief generates an out of memory error
//////////////////////////////////////////////////////////////////////////////
void generateOOMError(); void generateOOMError();
////////////////////////////////////////////////////////////////////////////// // generates a canceled message
/// @brief generates a canceled message
//////////////////////////////////////////////////////////////////////////////
void generateCanceled(); void generateCanceled();
//////////////////////////////////////////////////////////////////////////////
/// @brief dumps the response as JSON into the response string buffer
//////////////////////////////////////////////////////////////////////////////
protected: protected:
// write result back to client
//////////////////////////////////////////////////////////////////////////////
/// @brief checks if velocypack is expected as answer
//////////////////////////////////////////////////////////////////////////////
bool returnVelocypack() const;
//////////////////////////////////////////////////////////////////////////////
/// @brief write result back to client
//////////////////////////////////////////////////////////////////////////////
void writeResult(arangodb::velocypack::Slice const& slice, void writeResult(arangodb::velocypack::Slice const& slice,
arangodb::velocypack::Options const& options); arangodb::velocypack::Options const& options);

View File

@ -175,7 +175,8 @@ void RestVocbaseBaseHandler::generateSaved(
VPackBuilder errorBuilder; VPackBuilder errorBuilder;
errorBuilder.openObject(); errorBuilder.openObject();
for (auto const& it : result.countErrorCodes) { 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(); errorBuilder.close();
_response->setHeaderNC(StaticStrings::ErrorCodes, errorBuilder.toJson()); _response->setHeaderNC(StaticStrings::ErrorCodes, errorBuilder.toJson());
@ -204,21 +205,21 @@ void RestVocbaseBaseHandler::generateDeleted(
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void RestVocbaseBaseHandler::generate20x( void RestVocbaseBaseHandler::generate20x(
arangodb::OperationResult const& result, arangodb::OperationResult const& result, std::string const& collectionName,
std::string const& collectionName, TRI_col_type_e type, VPackOptions const* options) {
TRI_col_type_e type,
VPackOptions const* options) {
VPackSlice slice = result.slice(); VPackSlice slice = result.slice();
TRI_ASSERT(slice.isObject() || slice.isArray()); TRI_ASSERT(slice.isObject() || slice.isArray());
if (slice.isObject()) { 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 // pre 1.4 location headers withdrawn for >= 3.0
std::string escapedHandle(assembleDocumentId( std::string escapedHandle(assembleDocumentId(
collectionName, slice.get(StaticStrings::KeyString).copyString(), true)); collectionName, slice.get(StaticStrings::KeyString).copyString(),
true));
_response->setHeaderNC(StaticStrings::Location, _response->setHeaderNC(StaticStrings::Location,
std::string("/_db/" + _request->databaseName() + std::string("/_db/" + _request->databaseName() +
DOCUMENT_PATH + "/" + escapedHandle)); DOCUMENT_PATH + "/" + escapedHandle));
} }
writeResult(slice, *options); writeResult(slice, *options);
@ -248,26 +249,28 @@ void RestVocbaseBaseHandler::generateForbidden() {
void RestVocbaseBaseHandler::generatePreconditionFailed( void RestVocbaseBaseHandler::generatePreconditionFailed(
VPackSlice const& slice) { VPackSlice const& slice) {
createResponse(GeneralResponse::ResponseCode::PRECONDITION_FAILED); createResponse(GeneralResponse::ResponseCode::PRECONDITION_FAILED);
if (slice.isObject()) { // single document case 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 + "\""); _response->setHeaderNC(StaticStrings::Etag, "\"" + rev + "\"");
} }
VPackBuilder builder; VPackBuilder builder;
{ {
VPackObjectBuilder guard(&builder); VPackObjectBuilder guard(&builder);
builder.add("error", VPackValue(true)); builder.add("error", VPackValue(true));
builder.add( builder.add("code",
"code", VPackValue(static_cast<int32_t>(
VPackValue(static_cast<int32_t>(GeneralResponse::ResponseCode::PRECONDITION_FAILED))); GeneralResponse::ResponseCode::PRECONDITION_FAILED)));
builder.add("errorNum", VPackValue(TRI_ERROR_ARANGO_CONFLICT)); builder.add("errorNum", VPackValue(TRI_ERROR_ARANGO_CONFLICT));
builder.add("errorMessage", VPackValue("precondition failed")); builder.add("errorMessage", VPackValue("precondition failed"));
if (slice.isObject()) { if (slice.isObject()) {
builder.add(StaticStrings::IdString, slice.get(StaticStrings::IdString)); builder.add(StaticStrings::IdString, slice.get(StaticStrings::IdString));
builder.add(StaticStrings::KeyString, slice.get(StaticStrings::KeyString)); builder.add(StaticStrings::KeyString,
builder.add(StaticStrings::RevString, slice.get(StaticStrings::RevString)); slice.get(StaticStrings::KeyString));
builder.add(StaticStrings::RevString,
slice.get(StaticStrings::RevString));
} else { } else {
builder.add("result", slice); builder.add("result", slice);
} }
@ -282,11 +285,12 @@ void RestVocbaseBaseHandler::generatePreconditionFailed(
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
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; VPackBuilder builder;
builder.openObject(); 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::KeyString, VPackValue(std::to_string(rev)));
builder.add(StaticStrings::RevString, VPackValue(key)); builder.add(StaticStrings::RevString, VPackValue(key));
builder.close(); builder.close();
@ -300,7 +304,8 @@ void RestVocbaseBaseHandler::generatePreconditionFailed(
void RestVocbaseBaseHandler::generateNotModified(TRI_voc_rid_t rid) { void RestVocbaseBaseHandler::generateNotModified(TRI_voc_rid_t rid) {
createResponse(GeneralResponse::ResponseCode::NOT_MODIFIED); 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; std::string rev;
if (document.isObject()) { if (document.isObject()) {
rev = VelocyPackHelper::getStringValue(document, StaticStrings::RevString, ""); rev = VelocyPackHelper::getStringValue(document, StaticStrings::RevString,
"");
} }
// and generate a response // and generate a response
createResponse(GeneralResponse::ResponseCode::OK); createResponse(GeneralResponse::ResponseCode::OK);
// set ETAG header
if (!rev.empty()) { if (!rev.empty()) {
_response->setHeaderNC(StaticStrings::Etag, "\"" + rev + "\""); _response->setHeaderNC(StaticStrings::Etag, "\"" + rev + "\"");
} }
if (generateBody) { try {
writeResult(document, *options); _response->fillBody(_request, document, generateBody, *options);
return; } catch (...) {
} generateError(GeneralResponse::ResponseCode::SERVER_ERROR,
TRI_ERROR_INTERNAL, "cannot generate output");
// no body, i.e. a HEAD response
if (returnVelocypack()) {
_response->setContentType(HttpResponse::CONTENT_TYPE_VPACK);
_response->headResponse(static_cast<size_t>(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());
} }
} }
@ -391,11 +376,13 @@ void RestVocbaseBaseHandler::generateTransactionError(
return; return;
case TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD: case TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD:
generateError(GeneralResponse::ResponseCode::BAD, res, "invalid document handle"); generateError(GeneralResponse::ResponseCode::BAD, res,
"invalid document handle");
return; return;
case TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE: case TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE:
generateError(GeneralResponse::ResponseCode::BAD, res, "invalid edge attribute"); generateError(GeneralResponse::ResponseCode::BAD, res,
"invalid edge attribute");
return; return;
case TRI_ERROR_ARANGO_OUT_OF_KEYS: case TRI_ERROR_ARANGO_OUT_OF_KEYS:
@ -417,8 +404,8 @@ void RestVocbaseBaseHandler::generateTransactionError(
return; return;
case TRI_ERROR_ARANGO_CONFLICT: case TRI_ERROR_ARANGO_CONFLICT:
generatePreconditionFailed(collectionName, generatePreconditionFailed(collectionName, key.empty() ? "unknown" : key,
key.empty() ? "unknown" : key, rev); rev);
return; return;
case TRI_ERROR_CLUSTER_SHARD_GONE: case TRI_ERROR_CLUSTER_SHARD_GONE:
@ -485,19 +472,23 @@ void RestVocbaseBaseHandler::generateTransactionError(
return; return;
case TRI_ERROR_ARANGO_DOCUMENT_KEY_BAD: 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; return;
case TRI_ERROR_ARANGO_DOCUMENT_HANDLE_BAD: 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; return;
case TRI_ERROR_ARANGO_INVALID_EDGE_ATTRIBUTE: 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; return;
case TRI_ERROR_ARANGO_OUT_OF_KEYS: 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; return;
case TRI_ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED: case TRI_ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED:
@ -535,7 +526,8 @@ void RestVocbaseBaseHandler::generateTransactionError(
} }
case TRI_ERROR_CLUSTER_UNSUPPORTED: { case TRI_ERROR_CLUSTER_UNSUPPORTED: {
generateError(GeneralResponse::ResponseCode::NOT_IMPLEMENTED, result.code); generateError(GeneralResponse::ResponseCode::NOT_IMPLEMENTED,
result.code);
return; return;
} }
@ -560,8 +552,6 @@ void RestVocbaseBaseHandler::generateTransactionError(
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief extracts the revision /// @brief extracts the revision
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -648,12 +638,13 @@ bool RestVocbaseBaseHandler::extractBooleanParameter(char const* name,
std::shared_ptr<VPackBuilder> RestVocbaseBaseHandler::parseVelocyPackBody( std::shared_ptr<VPackBuilder> RestVocbaseBaseHandler::parseVelocyPackBody(
VPackOptions const* options, bool& success) { VPackOptions const* options, bool& success) {
bool found; bool found;
std::string const& contentType = _request->header(StaticStrings::ContentTypeHeader, found); std::string const& contentType =
_request->header(StaticStrings::ContentTypeHeader, found);
try { try {
success = true; success = true;
if (found && contentType.size() == StaticStrings::MimeTypeVPack.size() && if (found && contentType.size() == StaticStrings::MimeTypeVPack.size() &&
contentType == StaticStrings::MimeTypeVPack) { contentType == StaticStrings::MimeTypeVPack) {
VPackSlice slice{_request->body().c_str()}; VPackSlice slice{_request->body().c_str()};
auto builder = std::make_shared<VPackBuilder>(options); auto builder = std::make_shared<VPackBuilder>(options);
@ -685,7 +676,8 @@ void RestVocbaseBaseHandler::prepareExecute() {
std::string const& shardId = _request->header("x-arango-nolock", found); std::string const& shardId = _request->header("x-arango-nolock", found);
if (found) { if (found) {
_nolockHeaderSet = new std::unordered_set<std::string>{ std::string(shardId) }; _nolockHeaderSet =
new std::unordered_set<std::string>{std::string(shardId)};
arangodb::Transaction::_makeNolockHeaders = _nolockHeaderSet; arangodb::Transaction::_makeNolockHeaders = _nolockHeaderSet;
} }
} }

View File

@ -21,6 +21,9 @@
/// @author Michael Hackstein /// @author Michael Hackstein
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGODB_BASICS_VPACKSTRING_H
#define ARANGODB_BASICS_VPACKSTRING_H
#include "Basics/StringBuffer.h" #include "Basics/StringBuffer.h"
#include "Basics/Exceptions.h" #include "Basics/Exceptions.h"
@ -74,3 +77,5 @@ class VPackStringBufferAdapter final : public VPackSink {
}; };
} }
} }
#endif

View File

@ -258,3 +258,7 @@ void GeneralRequest::setArrayValue(char* key, size_t length, char const* value)
_arrayValues[std::string(key, length)].emplace_back(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));
}

View File

@ -158,6 +158,8 @@ class GeneralRequest {
} }
void setArrayValue(std::string const& key, std::string const& value); void setArrayValue(std::string const& key, std::string const& value);
bool velocyPackResponse () const;
protected: protected:
void setValue(char const* key, char const* value); void setValue(char const* key, char const* value);
void setArrayValue(char* key, size_t length, char const* value); void setArrayValue(char* key, size_t length, char const* value);

View File

@ -30,6 +30,13 @@
#include "Basics/StringUtils.h" #include "Basics/StringUtils.h"
namespace arangodb { namespace arangodb {
namespace velocypack {
struct Options;
class Slice;
}
class GeneralRequest;
class GeneralResponse { class GeneralResponse {
GeneralResponse() = delete; GeneralResponse() = delete;
GeneralResponse(GeneralResponse const&) = delete; GeneralResponse(GeneralResponse const&) = delete;
@ -110,23 +117,32 @@ class GeneralResponse {
_responseCode = responseCode; _responseCode = responseCode;
} }
std::unordered_map<std::string, std::string> headers() const { return _headers; } std::unordered_map<std::string, std::string> 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) { void setHeader(std::string const& key, std::string const& value) {
_headers[basics::StringUtils::tolower(key)] = 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) { void setHeaderNC(std::string const& key, std::string const& value) {
_headers[key] = 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) { void setHeaderNC(std::string const& key, std::string&& value) {
_headers[key] = std::move(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: protected:
ResponseCode _responseCode; ResponseCode _responseCode;
std::unordered_map<std::string, std::string> _headers; std::unordered_map<std::string, std::string> _headers;

View File

@ -38,7 +38,7 @@
using namespace arangodb; using namespace arangodb;
using namespace arangodb::basics; using namespace arangodb::basics;
HttpRequest::HttpRequest(ConnectionInfo const& connectionInfo, HttpRequest::HttpRequest(ConnectionInfo const& connectionInfo,
char const* header, size_t length, char const* header, size_t length,
bool allowMethodOverride) bool allowMethodOverride)
@ -46,7 +46,6 @@ HttpRequest::HttpRequest(ConnectionInfo const& connectionInfo,
_contentLength(0), _contentLength(0),
_header(nullptr), _header(nullptr),
_allowMethodOverride(allowMethodOverride) { _allowMethodOverride(allowMethodOverride) {
if (0 < length) { if (0 < length) {
_header = new char[length + 1]; _header = new char[length + 1];
memcpy(_header, header, length); memcpy(_header, header, length);
@ -55,9 +54,7 @@ HttpRequest::HttpRequest(ConnectionInfo const& connectionInfo,
} }
} }
HttpRequest::~HttpRequest() { HttpRequest::~HttpRequest() { delete[] _header; }
delete[] _header;
}
void HttpRequest::parseHeader(size_t length) { void HttpRequest::parseHeader(size_t length) {
char* start = _header; char* start = _header;
@ -363,7 +360,8 @@ void HttpRequest::parseHeader(size_t length) {
} }
if (keyBegin < keyEnd) { 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); _contentLength = StringUtils::int64(value, valueLength);
// do not store this header // do not store this header
return; return;
} }
if (keyLength == 6 && if (keyLength == 6 &&
memcmp(key, "cookie", keyLength) == 0) { // 6 = strlen("cookie") memcmp(key, "cookie", keyLength) == 0) { // 6 = strlen("cookie")
parseCookies(value, valueLength); parseCookies(value, valueLength);
@ -522,10 +520,9 @@ void HttpRequest::setHeader(char const* key, size_t keyLength,
// override HTTP method? // override HTTP method?
if ((keyLength == 13 && memcmp(key, "x-http-method", keyLength) == 0) || if ((keyLength == 13 && memcmp(key, "x-http-method", keyLength) == 0) ||
(keyLength == 17 && (keyLength == 17 && memcmp(key, "x-method-override", keyLength) == 0) ||
memcmp(key, "x-method-override", keyLength) == 0) ||
(keyLength == 22 && (keyLength == 22 &&
memcmp(key, "x-http-method-override", keyLength) == 0)) { memcmp(key, "x-http-method-override", keyLength) == 0)) {
std::string overriddenType(value, valueLength); std::string overriddenType(value, valueLength);
StringUtils::tolowerInPlace(&overriddenType); StringUtils::tolowerInPlace(&overriddenType);
@ -657,21 +654,20 @@ std::string const& HttpRequest::cookieValue(std::string const& key) const {
return it->second; 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); auto it = _cookies.find(key);
if (it == _cookies.end()) { if (it == _cookies.end()) {
found = false; found = false;
return StaticStrings::Empty; return StaticStrings::Empty;
} }
found = true; found = true;
return it->second; return it->second;
} }
std::string const& HttpRequest::body() const { std::string const& HttpRequest::body() const { return _body; }
return _body;
}
void HttpRequest::setBody(char const* body, size_t length) { void HttpRequest::setBody(char const* body, size_t length) {
_body = std::string(body, length); _body = std::string(body, length);
@ -683,4 +679,3 @@ std::shared_ptr<VPackBuilder> HttpRequest::toVelocyPack(
parser.parse(body()); parser.parse(body());
return parser.steal(); return parser.steal();
} }

View File

@ -24,10 +24,18 @@
#include "HttpResponse.h" #include "HttpResponse.h"
#include <velocypack/Builder.h>
#include <velocypack/Dumper.h>
#include <velocypack/Options.h>
#include <velocypack/velocypack-aliases.h>
#include "Basics/Exceptions.h" #include "Basics/Exceptions.h"
#include "Basics/StringBuffer.h" #include "Basics/StringBuffer.h"
#include "Basics/StringUtils.h" #include "Basics/StringUtils.h"
#include "Basics/VPackStringBufferAdapter.h"
#include "Basics/VelocyPackDumper.h"
#include "Basics/tri-strings.h" #include "Basics/tri-strings.h"
#include "Rest/GeneralRequest.h"
using namespace arangodb; using namespace arangodb;
using namespace arangodb::basics; using namespace arangodb::basics;
@ -41,10 +49,9 @@ HttpResponse::HttpResponse(ResponseCode code)
_isHeadResponse(false), _isHeadResponse(false),
_body(TRI_UNKNOWN_MEM_ZONE, false), _body(TRI_UNKNOWN_MEM_ZONE, false),
_bodySize(0) { _bodySize(0) {
if (_body.c_str() == nullptr) { if (_body.c_str() == nullptr) {
// no buffer could be reserved. out of memory! // 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(TRI_CHAR_LENGTH_PAIR("HTTP/1.1 "));
output->appendText(responseString(_responseCode)); output->appendText(responseString(_responseCode));
output->appendText("\r\n", 2); output->appendText("\r\n", 2);
bool seenServerHeader = false; bool seenServerHeader = false;
bool seenConnectionHeader = false; bool seenConnectionHeader = false;
bool seenTransferEncodingHeader = false; bool seenTransferEncodingHeader = false;
@ -145,20 +152,21 @@ void HttpResponse::writeHeader(StringBuffer* output) {
continue; continue;
} }
if (keyLength == 6 && key[0] == 's' && if (keyLength == 6 && key[0] == 's' &&
memcmp(key.c_str(), "server", keyLength) == 0) { memcmp(key.c_str(), "server", keyLength) == 0) {
// this ensures we don't print two "Server" headers // this ensures we don't print two "Server" headers
seenServerHeader = true; seenServerHeader = true;
// go on and use the user-defined "Server" header value // go on and use the user-defined "Server" header value
} else if (keyLength == 10 && key[0] == 'c' && } else if (keyLength == 10 && key[0] == 'c' &&
memcmp(key.c_str(), "connection", keyLength) == 0) { memcmp(key.c_str(), "connection", keyLength) == 0) {
// this ensures we don't print two "Connection" headers // this ensures we don't print two "Connection" headers
seenConnectionHeader = true; seenConnectionHeader = true;
// go on and use the user-defined "Connection" header value // go on and use the user-defined "Connection" header value
} }
// reserve enough space for header name + ": " + value + "\r\n" // 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); THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
} }
@ -190,7 +198,7 @@ void HttpResponse::writeHeader(StringBuffer* output) {
output->appendTextUnsafe(it.second); output->appendTextUnsafe(it.second);
output->appendTextUnsafe("\r\n", 2); output->appendTextUnsafe("\r\n", 2);
} }
// add "Server" response header // add "Server" response header
if (!seenServerHeader && !HIDE_PRODUCT_HEADER) { if (!seenServerHeader && !HIDE_PRODUCT_HEADER) {
output->appendText("Server: ArangoDB\r\n"); output->appendText("Server: ArangoDB\r\n");
@ -214,23 +222,29 @@ void HttpResponse::writeHeader(StringBuffer* output) {
// add "Content-Type" header // add "Content-Type" header
switch (_contentType) { switch (_contentType) {
case CONTENT_TYPE_JSON: 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; break;
case CONTENT_TYPE_VPACK: 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; break;
case CONTENT_TYPE_TEXT: 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; break;
case CONTENT_TYPE_HTML: 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; break;
case CONTENT_TYPE_DUMP: 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; break;
case CONTENT_TYPE_CUSTOM: { case CONTENT_TYPE_CUSTOM: {
// intentionally don't print anything here // 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 // 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<size_t>(slice.byteSize());
if (generateBody) {
_body.appendText(slice.startAs<const char>(), 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());
}
}
}

View File

@ -45,14 +45,14 @@ class HttpResponse : public GeneralResponse {
CONNECTION_KEEP_ALIVE, CONNECTION_KEEP_ALIVE,
CONNECTION_CLOSE CONNECTION_CLOSE
}; };
enum ContentType { enum ContentType {
CONTENT_TYPE_CUSTOM, // use Content-Type from _headers CONTENT_TYPE_CUSTOM, // use Content-Type from _headers
CONTENT_TYPE_JSON, // application/json CONTENT_TYPE_JSON, // application/json
CONTENT_TYPE_VPACK, // application/x-velocypack CONTENT_TYPE_VPACK, // application/x-velocypack
CONTENT_TYPE_TEXT, // text/plain CONTENT_TYPE_TEXT, // text/plain
CONTENT_TYPE_HTML, // text/html CONTENT_TYPE_HTML, // text/html
CONTENT_TYPE_DUMP // application/x-arango-dump CONTENT_TYPE_DUMP // application/x-arango-dump
}; };
public: public:
@ -76,7 +76,7 @@ class HttpResponse : public GeneralResponse {
/// @brief set type of connection /// @brief set type of connection
void setConnectionType(ConnectionType type) { _connectionType = type; } void setConnectionType(ConnectionType type) { _connectionType = type; }
/// @brief set content-type /// @brief set content-type
void setContentType(ContentType type) { _contentType = type; } void setContentType(ContentType type) { _contentType = type; }
@ -86,15 +86,21 @@ class HttpResponse : public GeneralResponse {
_headers[arangodb::StaticStrings::ContentTypeHeader] = contentType; _headers[arangodb::StaticStrings::ContentTypeHeader] = contentType;
_contentType = CONTENT_TYPE_CUSTOM; _contentType = CONTENT_TYPE_CUSTOM;
} }
void setContentType(std::string&& contentType) { void setContentType(std::string&& contentType) {
_headers[arangodb::StaticStrings::ContentTypeHeader] = std::move(contentType); _headers[arangodb::StaticStrings::ContentTypeHeader] =
std::move(contentType);
_contentType = CONTENT_TYPE_CUSTOM; _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*);
public:
void fillBody(GeneralRequest const*, arangodb::velocypack::Slice const&,
bool generateBody,
arangodb::velocypack::Options const&) override final;
private: private:
// 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);