1
0
Fork 0

Bug fix/foxx vst (#10349)

This commit is contained in:
Wilfried Goesgens 2019-11-07 16:34:02 +01:00 committed by Jan
parent 972e0b2ce0
commit b6a400aec1
24 changed files with 866 additions and 1011 deletions

View File

@ -47,13 +47,6 @@ void RestAnalyzerHandler::createAnalyzer( // create
) {
TRI_ASSERT(_request); // ensured by execute()
if (_request->payload().isEmptyObject()) {
generateError(
arangodb::rest::ResponseCode::BAD, TRI_ERROR_HTTP_CORRUPTED_JSON
);
return;
}
bool success = false;
auto body = parseVPackBody(success);
@ -61,6 +54,13 @@ void RestAnalyzerHandler::createAnalyzer( // create
return; // parseVPackBody(...) calls generateError(...)
}
if (body.isEmptyObject()) {
generateError(
arangodb::rest::ResponseCode::BAD, TRI_ERROR_HTTP_CORRUPTED_JSON
);
return;
}
if (!body.isObject()) {
generateError(arangodb::Result(
TRI_ERROR_BAD_PARAMETER,

View File

@ -569,11 +569,6 @@ RestStatus RestCursorHandler::generateCursorResult(rest::ResponseCode code,
////////////////////////////////////////////////////////////////////////////////
RestStatus RestCursorHandler::createQueryCursor() {
if (_request->payload().isEmptyObject()) {
generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_CORRUPTED_JSON);
return RestStatus::DONE;
}
std::vector<std::string> const& suffixes = _request->suffixes();
if (!suffixes.empty()) {
@ -590,6 +585,11 @@ RestStatus RestCursorHandler::createQueryCursor() {
return RestStatus::DONE;
}
if (body.isEmptyObject()) {
generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_CORRUPTED_JSON);
return RestStatus::DONE;
}
// tell RestCursorHandler::finalizeExecute that the request
// could be parsed successfully and that it may look at it
_isValidForFinalize = true;

View File

@ -317,8 +317,6 @@ bool RestImportHandler::createFromJson(std::string const& type) {
// auto detect import type by peeking at first non-whitespace character
// json required here
TRI_ASSERT(_request->contentType() == ContentType::JSON);
VPackStringRef body = _request->rawPayload();
char const* ptr = body.data();
@ -372,10 +370,7 @@ bool RestImportHandler::createFromJson(std::string const& type) {
VPackBuilder tmpBuilder;
if (linewise) {
// json required here
TRI_ASSERT(_request->contentType() == ContentType::JSON);
if (linewise) {
// each line is a separate JSON document
VPackStringRef body = _request->rawPayload();
char const* ptr = body.data();

View File

@ -140,12 +140,6 @@ RestStatus RestViewHandler::execute() {
////////////////////////////////////////////////////////////////////////////////
void RestViewHandler::createView() {
if (_request->payload().isEmptyObject()) {
generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_CORRUPTED_JSON);
events::CreateView(_vocbase.name(), "", TRI_ERROR_HTTP_CORRUPTED_JSON);
return;
}
std::vector<std::string> const& suffixes = _request->suffixes();
if (!suffixes.empty()) {
@ -170,6 +164,13 @@ void RestViewHandler::createView() {
return;
}
if (body.isEmptyObject()) {
generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_CORRUPTED_JSON);
events::CreateView(_vocbase.name(), "", TRI_ERROR_HTTP_CORRUPTED_JSON);
return;
}
auto nameSlice = body.get(StaticStrings::DataSourceName);
auto typeSlice = body.get(StaticStrings::DataSourceType);

View File

@ -438,6 +438,30 @@ v8::Handle<v8::Object> TRI_RequestCppToV8(v8::Isolate* isolate,
TRI_GET_GLOBAL_STRING(RequestBodyKey);
auto setRequestBodyJsonOrVPack = [&]() {
if (rest::ContentType::UNSET == request->contentType()) {
bool digesteable = false;
try {
VPackOptions optionsWithUniquenessCheck = VPackOptions::Defaults;
optionsWithUniquenessCheck.checkAttributeUniqueness = true;
auto parsed = request->payload(&optionsWithUniquenessCheck);
if (parsed.isObject() || parsed.isArray()) {
digesteable = true;
}
} catch ( ... ) {}
// ok, no json/vpack after all ;-)
auto raw = request->rawPayload();
headers[StaticStrings::ContentLength] =
StringUtils::itoa(raw.size());
V8Buffer* buffer = V8Buffer::New(isolate, raw.data(), raw.size());
auto bufObj = v8::Local<v8::Object>::New(isolate, buffer->_handle);
TRI_GET_GLOBAL_STRING(RawRequestBodyKey);
req->Set(RawRequestBodyKey, bufObj);
req->Set(RequestBodyKey, TRI_V8_PAIR_STRING(isolate, raw.data(), raw.size()));
if (!digesteable) {
return;
}
}
if (rest::ContentType::JSON == request->contentType()) {
VPackStringRef body = request->rawPayload();
req->Set(RequestBodyKey, TRI_V8_PAIR_STRING(isolate, body.data(), body.size()));
@ -456,8 +480,6 @@ v8::Handle<v8::Object> TRI_RequestCppToV8(v8::Isolate* isolate,
req->Set(RequestBodyKey, TRI_V8_STD_STRING(isolate, jsonString));
headers[StaticStrings::ContentLength] = StringUtils::itoa(jsonString.size());
headers[StaticStrings::ContentTypeHeader] = StaticStrings::MimeTypeJson;
} else {
throw std::logic_error("unhandled request type");
}
};
@ -614,13 +636,14 @@ static void ResponseV8ToCpp(v8::Isolate* isolate, TRI_v8_global_t const* v8g,
response->setResponseCode(code);
// string should not be used
std::string contentType = "application/json";
std::string contentType = StaticStrings::MimeTypeJsonNoEncoding;
bool autoContent = true;
TRI_GET_GLOBAL_STRING(ContentTypeKey);
if (TRI_HasProperty(context, isolate, res, ContentTypeKey)) {
contentType = TRI_ObjectToString(isolate, res->Get(ContentTypeKey));
if (contentType.find("application/json") == std::string::npos) {
if ((contentType.find(StaticStrings::MimeTypeJsonNoEncoding) == std::string::npos) &&
(contentType.find(StaticStrings::MimeTypeVPack) == std::string::npos)) {
autoContent = false;
}
switch (response->transportType()) {
@ -633,7 +656,11 @@ static void ResponseV8ToCpp(v8::Isolate* isolate, TRI_v8_global_t const* v8g,
break;
case Endpoint::TransportType::VST:
response->setHeaderNC(arangodb::StaticStrings::ContentTypeHeader, contentType);
if (!autoContent) {
response->setContentType(contentType);
} else {
response->setHeaderNC(arangodb::StaticStrings::ContentTypeHeader, contentType);
}
break;
default:
@ -751,6 +778,7 @@ static void ResponseV8ToCpp(v8::Isolate* isolate, TRI_v8_global_t const* v8g,
out = TRI_ObjectToString(isolate, res->Get(BodyKey)); // should get moved
} else {
TRI_V8ToVPack(isolate, builder, v8Body, false);
response->setContentType(rest::ContentType::VPACK);
}
} else if (V8Buffer::hasInstance(isolate,
v8Body)) { // body form buffer - could
@ -771,6 +799,7 @@ static void ResponseV8ToCpp(v8::Isolate* isolate, TRI_v8_global_t const* v8g,
VPackParser parser(builder); // add json as vpack to the builder
parser.parse(out, false);
gotJson = true;
response->setContentType(rest::ContentType::VPACK);
} catch (...) { // do nothing
// json could not be converted
// there was no json - change content type?
@ -781,11 +810,12 @@ static void ResponseV8ToCpp(v8::Isolate* isolate, TRI_v8_global_t const* v8g,
}
if (!gotJson) {
builder.add(VPackValue(out)); // add output to the builder - when not added via parser
// don't go via the builder - when not added via parser
buffer.reset();
buffer.append(out);
}
}
response->setContentType(rest::ContentType::VPACK);
response->setPayload(std::move(buffer), true);
break;
}
@ -1162,14 +1192,8 @@ static void JS_RawRequestBody(v8::FunctionCallbackInfo<v8::Value> const& args) {
case Endpoint::TransportType::VST: {
if (request != nullptr) {
auto slice = request->payload();
V8Buffer* buffer = nullptr;
if (slice.isNone()) {
buffer = V8Buffer::New(isolate, "", 0);
} else {
std::string bodyStr = slice.toJson();
buffer = V8Buffer::New(isolate, bodyStr.c_str(), bodyStr.size());
}
auto raw = request->rawPayload();
V8Buffer* buffer = V8Buffer::New(isolate, raw.data(), raw.size());
TRI_V8_RETURN(buffer->_handle);
}
} break;

View File

@ -24,6 +24,7 @@
#include "V8ClientConnection.h"
#include <boost/algorithm/string.hpp>
#include <fuerte/connection.h>
#include <fuerte/jwt.h>
#include <fuerte/requests.h>
@ -1524,6 +1525,34 @@ again:
req->header.database = _databaseName;
req->header.parseArangoPath(location.toString());
for (auto& pair : headerFields) {
if (boost::iequals(StaticStrings::ContentTypeHeader, pair.first)) {
if (pair.second == StaticStrings::MimeTypeVPack) {
req->header.contentType(fuerte::ContentType::VPack);
} else if ((pair.second.length() >= StaticStrings::MimeTypeJsonNoEncoding.length()) &&
(memcmp(pair.second.c_str(),
StaticStrings::MimeTypeJsonNoEncoding.c_str(),
StaticStrings::MimeTypeJsonNoEncoding.length()) == 0)) {
// ignore encoding etc.
req->header.contentType(fuerte::ContentType::Json);
} else {
req->header.contentType(fuerte::ContentType::Custom);
}
} else if (boost::iequals(StaticStrings::Accept, pair.first)) {
if (pair.second == StaticStrings::MimeTypeVPack) {
req->header.acceptType(fuerte::ContentType::VPack);
} else if ((pair.second.length() >= StaticStrings::MimeTypeJsonNoEncoding.length()) &&
(memcmp(pair.second.c_str(),
StaticStrings::MimeTypeJsonNoEncoding.c_str(),
StaticStrings::MimeTypeJsonNoEncoding.length()) == 0)) {
// ignore encoding etc.
req->header.acceptType(fuerte::ContentType::Json);
} else {
req->header.acceptType(fuerte::ContentType::Custom);
}
req->header.acceptType(fuerte::ContentType::Custom);
}
req->header.addMeta(pair.first, pair.second);
}
if (isFile) {
@ -1623,6 +1652,11 @@ again:
req->header.database = _databaseName;
req->header.parseArangoPath(location.toString());
for (auto& pair : headerFields) {
if (boost::iequals(StaticStrings::ContentTypeHeader, pair.first)) {
req->header.contentType(fuerte::ContentType::Custom);
} else if (boost::iequals(StaticStrings::Accept, pair.first)) {
req->header.acceptType(fuerte::ContentType::Custom);
}
req->header.addMeta(pair.first, pair.second);
}
if (body->IsString() || body->IsStringObject()) { // assume JSON
@ -1815,8 +1849,15 @@ v8::Local<v8::Value> V8ClientConnection::handleResult(v8::Isolate* isolate,
}
return ret;
}
// return body as string
return TRI_V8_PAIR_STRING(isolate, str, sb.size());
if (res->isContentTypeHtml() || res->isContentTypeText()) {
// return body as string
return TRI_V8_PAIR_STRING(isolate, str, sb.size());
}
V8Buffer* buffer;
buffer = V8Buffer::New(isolate, reinterpret_cast<char const*>(sb.data()), sb.size());
auto bufObj = v8::Local<v8::Object>::New(isolate, buffer->_handle);
return bufObj;
}
// no body

View File

@ -1805,14 +1805,6 @@ function launchFinalize(options, instanceInfo, startTime) {
device = options.sniffDevice;
}
let pcapFile = fs.join(instanceInfo.rootDir, 'out.pcap');
let args = ['-ni', device, '-s0', '-w', pcapFile];
for (let port = 0; port < ports.length; port ++) {
if (port > 0) {
args.push('or');
}
args.push(ports[port]);
}
let prog = 'tcpdump';
if (platform.substr(0, 3) === 'win') {
prog = 'c:/Program Files/Wireshark/tshark.exe';
@ -1820,6 +1812,21 @@ function launchFinalize(options, instanceInfo, startTime) {
if (options.sniffProgram !== undefined) {
prog = options.sniffProgram;
}
let pcapFile = fs.join(instanceInfo.rootDir, 'out.pcap');
let args;
if (prog === 'ngrep') {
args = ['-l', '-Wbyline', '-d', device];
} else {
args = ['-ni', device, '-s0', '-w', pcapFile];
}
for (let port = 0; port < ports.length; port ++) {
if (port > 0) {
args.push('or');
}
args.push(ports[port]);
}
if (options.sniff === 'sudo') {
args.unshift(prog);
prog = 'sudo';

View File

@ -31,7 +31,6 @@ const typeIs = require('type-is').is;
const accepts = require('accepts');
const parseRange = require('range-parser');
const querystring = require('querystring');
const getRawBodyBuffer = require('internal').rawRequestBody;
const getTrustedProxies = require('internal').trustedProxies;
const crypto = require('@arangodb/crypto');
const Netmask = require('netmask').Netmask;
@ -74,7 +73,12 @@ module.exports =
this.path = this._url.pathname;
this.pathParams = {};
this.queryParams = querystring.parse(this._url.query);
this.body = getRawBodyBuffer(req);
if (req.hasOwnProperty('rawRequestBody')) {
this.body = req.rawRequestBody;
}
else {
this.body = req.requestBody;
}
this.rawBody = this.body;
this.trustProxy = (

View File

@ -217,6 +217,8 @@ std::string const StaticStrings::MimeTypeDump(
std::string const StaticStrings::MimeTypeHtml("text/html; charset=utf-8");
std::string const StaticStrings::MimeTypeJson(
"application/json; charset=utf-8");
std::string const StaticStrings::MimeTypeJsonNoEncoding(
"application/json");
std::string const StaticStrings::MimeTypeText("text/plain; charset=utf-8");
std::string const StaticStrings::MimeTypeVPack("application/x-velocypack");
std::string const StaticStrings::MultiPartContentType("multipart/form-data");

View File

@ -199,6 +199,7 @@ class StaticStrings {
static std::string const MimeTypeDump;
static std::string const MimeTypeHtml;
static std::string const MimeTypeJson;
static std::string const MimeTypeJsonNoEncoding;
static std::string const MimeTypeText;
static std::string const MimeTypeVPack;
static std::string const MultiPartContentType;

View File

@ -42,7 +42,7 @@ using namespace arangodb::basics;
static size_t const MinReserveValue = 32;
void VelocyPackDumper::handleUnsupportedType(VPackSlice const* /*slice*/) {
void VelocyPackDumper::handleUnsupportedType(VPackSlice const* slice) {
TRI_string_buffer_t* buffer = _buffer->stringBuffer();
if (options->unsupportedTypeBehavior == VPackOptions::NullifyUnsupportedType) {
@ -52,7 +52,10 @@ void VelocyPackDumper::handleUnsupportedType(VPackSlice const* /*slice*/) {
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
TRI_ASSERT(strlen("\"(non-representable type)\"") + 1 < MinReserveValue);
#endif
TRI_AppendStringUnsafeStringBuffer(buffer, "\"(non-representable type)\"");
TRI_AppendStringUnsafeStringBuffer(buffer, "\"(non-representable type ");
TRI_AppendStringUnsafeStringBuffer(buffer, slice->typeName());
TRI_AppendStringUnsafeStringBuffer(buffer, ")\"");
return;
}

View File

@ -48,7 +48,7 @@ HttpRequest::HttpRequest(ConnectionInfo const& connectionInfo, char const* heade
_vpackBuilder(nullptr),
_version(ProtocolVersion::UNKNOWN),
_allowMethodOverride(allowMethodOverride) {
_contentType = ContentType::JSON;
_contentType = ContentType::UNSET;
_contentTypeResponse = ContentType::JSON;
if (0 < length) {
auto buff = std::make_unique<char[]>(length + 1);
@ -545,9 +545,19 @@ void HttpRequest::setHeaderV2(std::string key, std::string value) {
if (key == StaticStrings::Accept && value == StaticStrings::MimeTypeVPack) {
_contentTypeResponse = ContentType::VPACK;
} else if (key == StaticStrings::ContentTypeHeader && value == StaticStrings::MimeTypeVPack) {
_contentType = ContentType::VPACK; // don't insert this header!!
return;
} else if ((_contentType == ContentType::UNSET) && (key == StaticStrings::ContentTypeHeader)) {
if (value == StaticStrings::MimeTypeVPack) {
_contentType = ContentType::VPACK; // don't insert this header!!
return;
}
else if ((value.length() >= StaticStrings::MimeTypeJsonNoEncoding.length()) &&
(memcmp(value.c_str(),
StaticStrings::MimeTypeJsonNoEncoding.c_str(),
StaticStrings::MimeTypeJsonNoEncoding.length()) == 0)) {
// ignore encoding etc.
_contentType = ContentType::JSON;
return;
}
} else if (key == StaticStrings::AcceptEncoding) {
// This can be much more elaborated as the can specify weights on encodings
// However, for now just toggle on deflate if deflate is requested
@ -720,13 +730,22 @@ void HttpRequest::setHeader(char const* key, size_t keyLength,
// This can be much more elaborated as the can specify weights on encodings
// However, for now just toggle on deflate if deflate is requested
_acceptEncoding = EncodingType::DEFLATE;
} else if (keyLength == StaticStrings::ContentTypeHeader.size() &&
valueLength == StaticStrings::MimeTypeVPack.size() &&
memcmp(key, StaticStrings::ContentTypeHeader.c_str(), keyLength) == 0 &&
memcmp(value, StaticStrings::MimeTypeVPack.c_str(), valueLength) == 0) {
_contentType = ContentType::VPACK;
// don't insert this header!!
return;
} else if ((_contentType == ContentType::UNSET) &&
(keyLength == StaticStrings::ContentTypeHeader.size()) &&
(memcmp(key, StaticStrings::ContentTypeHeader.c_str(), keyLength) == 0)) {
if (valueLength == StaticStrings::MimeTypeVPack.size() &&
memcmp(value, StaticStrings::MimeTypeVPack.c_str(), valueLength) == 0) {
_contentType = ContentType::VPACK;
// don't insert this header!!
return;
}
if (valueLength >= StaticStrings::MimeTypeJsonNoEncoding.size() &&
memcmp(value, StaticStrings::MimeTypeJsonNoEncoding.c_str(),
StaticStrings::MimeTypeJsonNoEncoding.length()) == 0) {
_contentType = ContentType::JSON;
// don't insert this header!!
return;
}
}
if (keyLength == 6 && memcmp(key, "cookie", keyLength) == 0) { // 6 = strlen("cookie")
@ -890,8 +909,7 @@ VPackStringRef HttpRequest::rawPayload() const {
VPackSlice HttpRequest::payload(VPackOptions const* options) {
TRI_ASSERT(options != nullptr);
if (_contentType == ContentType::JSON) {
if ((_contentType == ContentType::UNSET) || (_contentType == ContentType::JSON)) {
if (!_body.empty()) {
if (!_vpackBuilder) {
VPackParser parser(options);

View File

@ -87,7 +87,7 @@ class HttpRequest final : public GeneralRequest {
arangodb::velocypack::Buffer<uint8_t>& body() {
return _body;
}
/// @brief sets a key/value header
// this function is called by setHeaders and get offsets to
// the found key / value with respective lengths.

View File

@ -61,7 +61,7 @@ VstRequest::VstRequest(ConnectionInfo const& connectionInfo,
_payloadOffset(payloadOffset),
_messageId(messageId),
_validatedPayload(false) {
_contentType = ContentType::VPACK;
_contentType = ContentType::UNSET;
_contentTypeResponse = ContentType::VPACK;
parseHeaderInformation();
}
@ -86,7 +86,7 @@ VPackSlice VstRequest::payload(VPackOptions const* options) {
if (_vpackBuilder) {
return _vpackBuilder->slice();
}
} else if (_contentType == ContentType::VPACK) {
} else if ((_contentType == ContentType::UNSET) || (_contentType == ContentType::VPACK)) {
if (_buffer.size() > _payloadOffset) {
uint8_t const* ptr = _buffer.data() + _payloadOffset;
if (!_validatedPayload) {
@ -113,15 +113,25 @@ void VstRequest::setHeader(VPackSlice keySlice, VPackSlice valSlice) {
std::string value = valSlice.copyString();
std::string key = StringUtils::tolower(keySlice.copyString());
std::string val = StringUtils::tolower(value);
static const char* mime = "application/json";
if (key == StaticStrings::Accept && val.length() >= 16 &&
memcmp(val.data(), mime, 16) == 0) {
if (key == StaticStrings::Accept &&
val.length() == StaticStrings::MimeTypeJsonNoEncoding.length() &&
(val == StaticStrings::MimeTypeJsonNoEncoding)) {
_contentTypeResponse = ContentType::JSON;
return; // don't insert this header!!
} else if (key == StaticStrings::ContentTypeHeader && val.length() >= 16 &&
memcmp(val.data(), mime, 16) == 0) {
_contentType = ContentType::JSON;
return; // don't insert this header!!
} else if ((_contentType == ContentType::UNSET) &&
(key == StaticStrings::ContentTypeHeader)) {
if ((val.length() == StaticStrings::MimeTypeVPack.length()) &&
(val == StaticStrings::MimeTypeVPack)) {
_contentType = ContentType::VPACK;
return; // don't insert this header!!
}
if (val.length() >= StaticStrings::MimeTypeJsonNoEncoding.length() &&
memcmp(value.c_str(), StaticStrings::MimeTypeJsonNoEncoding.c_str(),
StaticStrings::MimeTypeJsonNoEncoding.length()) == 0) {
_contentType = ContentType::JSON;
// don't insert this header!!
return;
}
}
// must lower-case the header key

View File

@ -32,6 +32,7 @@
#include "Basics/Exceptions.h"
#include "Basics/StringBuffer.h"
#include "Basics/StringUtils.h"
#include "Basics/VelocyPackDumper.h"
#include "Basics/VelocyPackHelper.h"
#include "Basics/VPackStringBufferAdapter.h"
#include "Basics/tri-strings.h"
@ -56,6 +57,12 @@ void VstResponse::reset(ResponseCode code) {
void VstResponse::addPayload(VPackSlice const& slice,
VPackOptions const* options,
bool resolveExternals) {
if (_contentType == rest::ContentType::VPACK &&
_contentTypeRequested == rest::ContentType::JSON) {
// content type was set by a handler to VPACK but the client requested JSON
// as we have a slice at hand we are able to reply with JSON easily
_contentType = rest::ContentType::JSON;
}
if (!options) {
options = &VPackOptions::Options::Defaults;
}
@ -69,21 +76,50 @@ void VstResponse::addPayload(VPackSlice const& slice,
VPackBuilder builder(tmpBuffer, options);
VelocyPackHelper::sanitizeNonClientTypes(slice, VPackSlice::noneSlice(),
builder, options, true, true, true);
if (_payload.empty()) {
_payload = std::move(tmpBuffer);
if (_contentType == rest::ContentType::VPACK) {
if (_payload.empty()) {
_payload = std::move(tmpBuffer);
} else {
_payload.append(tmpBuffer.data(), tmpBuffer.size());
}
} else if (_contentType == rest::ContentType::JSON) {
VPackSlice finalSlice(tmpBuffer.data());
StringBuffer plainBuffer;
arangodb::basics::VelocyPackDumper dumper(&plainBuffer, options);
dumper.dumpValue(finalSlice);
_payload.reset();
_payload.append(plainBuffer.data(), plainBuffer.length());
} else {
_payload.append(tmpBuffer.data(), tmpBuffer.size());
_payload.reset();
_payload.append(slice.start(), slice.byteSize());
}
return;
}
}
// just copy
_payload.append(slice.startAs<char>(), slice.byteSize());
if (_contentType == rest::ContentType::VPACK) {
// just copy
_payload.append(slice.startAs<char>(), slice.byteSize());
} else if (_contentType == rest::ContentType::JSON) {
StringBuffer plainBuffer;
arangodb::basics::VelocyPackDumper dumper(&plainBuffer, options);
dumper.dumpValue(slice);
_payload.reset();
_payload.append(plainBuffer.data(), plainBuffer.length());
} else {
_payload.reset();
_payload.append(slice.start(), slice.byteSize());
}
}
void VstResponse::addPayload(VPackBuffer<uint8_t>&& buffer,
velocypack::Options const* options, bool resolveExternals) {
if (_contentType == rest::ContentType::VPACK &&
_contentTypeRequested == rest::ContentType::JSON) {
// content type was set by a handler to VPACK but the client wants JSON
// as we have a slice at had we are able to reply with JSON
_contentType = rest::ContentType::JSON;
}
if (!options) {
options = &VPackOptions::Options::Defaults;
}
@ -98,18 +134,42 @@ void VstResponse::addPayload(VPackBuffer<uint8_t>&& buffer,
VPackBuilder builder(tmpBuffer, options);
VelocyPackHelper::sanitizeNonClientTypes(input, VPackSlice::noneSlice(),
builder, options, true, true, true);
if (_payload.empty()) {
_payload = std::move(tmpBuffer);
if (_contentType == rest::ContentType::VPACK) {
if (_payload.empty()) {
_payload = std::move(tmpBuffer);
} else {
_payload.append(tmpBuffer.data(), tmpBuffer.size());
}
} else if (_contentType == rest::ContentType::JSON) {
VPackSlice finalSlice(tmpBuffer.data());
StringBuffer plainBuffer;
arangodb::basics::VelocyPackDumper dumper(&plainBuffer, options);
dumper.dumpValue(finalSlice);
_payload.reset();
_payload.append(plainBuffer.data(), plainBuffer.length());
} else {
_payload.append(tmpBuffer.data(), tmpBuffer.size());
_payload.reset();
_payload = buffer;
}
return; // done
return;
}
}
if (_payload.empty()) {
_payload = std::move(buffer);
if (_contentType == rest::ContentType::VPACK) {
if (_payload.empty()) {
_payload = std::move(buffer);
} else {
_payload.append(buffer.data(), buffer.size());
}
} else if (_contentType == rest::ContentType::JSON) {
VPackSlice finalSlice(buffer.data());
StringBuffer plainBuffer;
arangodb::basics::VelocyPackDumper dumper(&plainBuffer, options);
dumper.dumpValue(finalSlice);
_payload.reset();
_payload.append(plainBuffer.data(), plainBuffer.length());
} else {
_payload.append(buffer.data(), buffer.size());
_payload.reset();
_payload = buffer;
}
}

View File

@ -95,6 +95,7 @@ TRI_v8_global_t::TRI_v8_global_t(v8::Isolate* isolate, size_t id)
ProtocolKey(),
RawSuffixKey(),
RequestBodyKey(),
RawRequestBodyKey(),
RequestTypeKey(),
ResponseCodeKey(),
ReturnNewKey(),
@ -191,6 +192,7 @@ TRI_v8_global_t::TRI_v8_global_t(v8::Isolate* isolate, size_t id)
ProtocolKey.Reset(isolate, TRI_V8_ASCII_STRING(isolate, "protocol"));
RawSuffixKey.Reset(isolate, TRI_V8_ASCII_STRING(isolate, "rawSuffix"));
RequestBodyKey.Reset(isolate, TRI_V8_ASCII_STRING(isolate, "requestBody"));
RawRequestBodyKey.Reset(isolate, TRI_V8_ASCII_STRING(isolate, "rawRequestBody"));
RequestTypeKey.Reset(isolate, TRI_V8_ASCII_STRING(isolate, "requestType"));
ResponseCodeKey.Reset(isolate, TRI_V8_ASCII_STRING(isolate, "responseCode"));
ReturnNewKey.Reset(isolate, TRI_V8_ASCII_STRING(isolate, "returnNew"));

View File

@ -634,6 +634,9 @@ struct TRI_v8_global_t {
/// @brief "requestBody" key name
v8::Persistent<v8::String> RequestBodyKey;
/// @brief "rawRequestBody" key name
v8::Persistent<v8::String> RawRequestBodyKey;
/// @brief "requestType" key name
v8::Persistent<v8::String> RequestTypeKey;

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,13 @@
/* global arango, describe, beforeEach, afterEach, it*/
'use strict';
var expect = require('chai').expect;
var FoxxManager = require('org/arangodb/foxx/manager');
var fs = require('fs');
var internal = require('internal');
var basePath = fs.makeAbsolute(fs.join(internal.pathForTesting('common'), 'test-data', 'apps', 'headers'));
var origin = arango.getEndpoint().replace(/\+vpp/, '').replace(/^tcp:/, 'http:').replace(/^ssl:/, 'https:');
const expect = require('chai').expect;
const FoxxManager = require('org/arangodb/foxx/manager');
const fs = require('fs');
const internal = require('internal');
const arango = require('@arangodb').arango;
const basePath = fs.makeAbsolute(fs.join(internal.pathForTesting('common'), 'test-data', 'apps', 'headers'));
const origin = arango.getEndpoint().replace(/\+vpp/, '').replace(/^tcp:/, 'http:').replace(/^ssl:/, 'https:').replace(/^vst:/, 'http:');
describe('HTTP headers in Foxx services', function () {
describe('Check request-response', function () {

View File

@ -34,13 +34,13 @@ const fs = require('fs');
const db = require('internal').db;
const basePath = fs.makeAbsolute(fs.join(require('internal').pathForTesting('common'), 'test-data', 'apps'));
const arango = require('@arangodb').arango;
const originalEndpoint = arango.getEndpoint().replace(/localhost/, '127.0.0.1');
const origin = arango.getEndpoint().replace(/\+vpp/, '').replace(/^tcp:/, 'http:').replace(/^ssl:/, 'https:').replace(/^vst:/, 'http:');
const expect = require('chai').expect;
describe('Foxx Manager', function () {
describe('using different dbs', function () {
beforeEach(function () {
arango.reconnect(originalEndpoint, '_system', 'root', '');
arango.reconnect(origin, '_system', 'root', '');
try {
db._dropDatabase('tmpFMDB');
} catch (err) {
@ -56,23 +56,23 @@ describe('Foxx Manager', function () {
});
afterEach(function () {
arango.reconnect(originalEndpoint, '_system', 'root', '');
arango.reconnect(origin, '_system', 'root', '');
db._dropDatabase('tmpFMDB');
db._dropDatabase('tmpFMDB2');
});
it('should allow to install apps on same mount point', function () {
const download = require('internal').download;
arango.reconnect(originalEndpoint, 'tmpFMDB', 'root', '');
arango.reconnect(origin, 'tmpFMDB', 'root', '');
expect(function () {
FoxxManager.install(fs.join(basePath, 'itzpapalotl'), '/unittest');
}).not.to.throw();
arango.reconnect(originalEndpoint, 'tmpFMDB2', 'root', '');
arango.reconnect(origin, 'tmpFMDB2', 'root', '');
expect(function () {
FoxxManager.install(fs.join(basePath, 'minimal-working-manifest'), '/unittest');
}).not.to.throw();
db._useDatabase('_system');
const baseUrl = arango.getEndpoint().replace('tcp://', 'http://') + '/_db';
const baseUrl = origin + '/_db';
const available = download(baseUrl + '/tmpFMDB/unittest/random');
expect(available.code).to.equal(200);
const unavailable = download(baseUrl + '/tmpFMDB2/unittest/random');
@ -87,7 +87,7 @@ describe('Foxx Manager', function () {
const mount = '/unittest/upgrade';
const setupTeardownApp = fs.join(basePath, 'minimal-working-setup-teardown');
const setupApp = fs.join(basePath, 'minimal-working-setup');
const url = arango.getEndpoint().replace('tcp://', 'http://') + '/_db/_system' + mount + '/test';
const url = origin + '/_db/_system' + mount + '/test';
const brokenApp = fs.join(basePath, 'broken-controller-file');
beforeEach(function () {
@ -167,7 +167,7 @@ describe('Foxx Manager', function () {
const mount = '/unittest/replace';
const setupTeardownApp = fs.join(basePath, 'minimal-working-setup-teardown');
const setupApp = fs.join(basePath, 'minimal-working-setup');
const url = arango.getEndpoint().replace('tcp://', 'http://') + '/_db/_system' + mount + '/test';
const url = origin + '/_db/_system' + mount + '/test';
const brokenApp = fs.join(basePath, 'broken-controller-file');
beforeEach(function () {

View File

@ -11,6 +11,7 @@ const aql = arangodb.aql;
const basePath = fs.makeAbsolute(fs.join(require('internal').pathForTesting('common'), 'test-data', 'apps'));
const expect = require('chai').expect;
const download = require('internal').download;
const origin = arango.getEndpoint().replace(/\+vpp/, '').replace(/^tcp:/, 'http:').replace(/^ssl:/, 'https:').replace(/^vst:/, 'http:');
describe('Foxx Manager', function () {
describe('(CRUD operation) ', function () {
@ -73,7 +74,7 @@ describe('Foxx Manager', function () {
it('should be available', function () {
FoxxManager.install(setupTeardownApp, mount);
const url = `${arango.getEndpoint().replace('tcp://', 'http://')}${mount}/test`;
const url = `${origin}${mount}/test`;
const res = download(url);
expect(res.code).to.equal(200);
expect(res.body).to.equal('true');
@ -235,7 +236,7 @@ describe('Foxx Manager', function () {
FoxxManager.install(malformedSetupApp, mount);
} catch (e) {
}
const url = `${arango.getEndpoint().replace('tcp://', 'http://')}${mount}/test`;
const url = `${origin}${mount}/test`;
const res = download(url);
expect(res.code).to.equal(404);
});
@ -276,7 +277,7 @@ describe('Foxx Manager', function () {
FoxxManager.install(malformedSetupApp, mount);
} catch (e) {
}
const url = `${arango.getEndpoint().replace('tcp://', 'http://')}${mount}/test`;
const url = `${origin}${mount}/test`;
const res = download(url);
expect(res.code).to.equal(404);
});
@ -322,7 +323,7 @@ describe('Foxx Manager', function () {
FoxxManager.install(malformedSetupApp, mount);
} catch (e) {
}
const url = `${arango.getEndpoint().replace('tcp://', 'http://')}${mount}/test`;
const url = `${origin}${mount}/test`;
const res = download(url);
expect(res.code).to.equal(404);
});
@ -352,8 +353,7 @@ describe('Foxx Manager', function () {
it('should be available after install', function () {
FoxxManager.install(setupHealApp, mount);
let endpoint = arango.getEndpoint().replace('tcp://', 'http://');
const url = endpoint + mount;
const url = origin + mount;
const res = download(url);
expect(res.code).to.equal(200);
expect(res.body).to.equal('true');
@ -362,8 +362,7 @@ describe('Foxx Manager', function () {
it('should be available after replace', function () {
{ // set up some service
FoxxManager.install(setupMinimalApp, mount);
let endpoint = arango.getEndpoint().replace('tcp://', 'http://');
const url = endpoint + mount;
const url = origin + mount;
const res = download(url);
expect(res.code).to.equal(200);
expect(JSON.parse(res.body)).to.deep.equal({hello: 'world'});
@ -371,8 +370,7 @@ describe('Foxx Manager', function () {
{ // replace it and call heal() during setup
FoxxManager.replace(setupHealApp, mount);
let endpoint = arango.getEndpoint().replace('tcp://', 'http://');
const url = endpoint + mount;
const url = origin + mount;
const res = download(url);
expect(res.code).to.equal(200);
expect(res.body).to.equal('true');
@ -382,8 +380,7 @@ describe('Foxx Manager', function () {
it('should be available after upgrade', function () {
{ // set up some service
FoxxManager.install(setupMinimalApp, mount);
let endpoint = arango.getEndpoint().replace('tcp://', 'http://');
const url = endpoint + mount;
const url = origin + mount;
const res = download(url);
expect(res.code).to.equal(200);
expect(JSON.parse(res.body)).to.deep.equal({hello: 'world'});
@ -391,8 +388,7 @@ describe('Foxx Manager', function () {
{ // upgrade it and call heal() during setup
FoxxManager.upgrade(setupHealApp, mount);
let endpoint = arango.getEndpoint().replace('tcp://', 'http://');
const url = endpoint + mount;
const url = origin + mount;
const res = download(url);
expect(res.code).to.equal(200);
expect(res.body).to.equal('true');

View File

@ -7,7 +7,8 @@ const fs = require('fs');
const internal = require('internal');
const basePath = fs.makeAbsolute(fs.join(internal.pathForTesting('common'), 'test-data', 'apps'));
const arango = require('@arangodb').arango;
const baseUrl = arango.getEndpoint().replace('tcp://', 'http://') + '/_db/_system';
const origin = arango.getEndpoint().replace(/\+vpp/, '').replace(/^tcp:/, 'http:').replace(/^ssl:/, 'https:').replace(/^vst:/, 'http:');
const baseUrl = origin + '/_db/_system';
describe('Foxx service path handling', () => {
const mount = '/unittest/paths';

View File

@ -40,6 +40,7 @@ const request = require('@arangodb/request');
const arangodb = require('@arangodb');
const arango = require('@arangodb').arango;
const origin = arango.getEndpoint().replace(/\+vpp/, '').replace(/^tcp:/, 'http:').replace(/^ssl:/, 'https:').replace(/^vst:/, 'http:');
const aql = arangodb.aql;
const db = internal.db;
@ -53,7 +54,7 @@ describe('Foxx service', () => {
});
afterEach(() => {
waitForJob();
download(`${arango.getEndpoint().replace('tcp://', 'http://')}/${mount}`, '', {
download(`${origin}/${mount}`, '', {
method: 'delete'
});
if (db._collection('foxx_queue_test')) {
@ -65,7 +66,7 @@ describe('Foxx service', () => {
FOR queue IN _queues
RETURN queue
`).toArray();
const res = download(`${arango.getEndpoint().replace('tcp://', 'http://')}/${mount}`, '', {
const res = download(`${origin}/${mount}`, '', {
method: 'post'
});
expect(res.code).to.equal(204);
@ -80,12 +81,12 @@ describe('Foxx service', () => {
FOR queue IN _queues
RETURN queue
`).toArray();
let res = download(`${arango.getEndpoint().replace('tcp://', 'http://')}/${mount}`, '', {
let res = download(`${origin}/${mount}`, '', {
method: 'post'
});
expect(res.code).to.equal(204);
waitForJob();
res = download(`${arango.getEndpoint().replace('tcp://', 'http://')}/${mount}`, '', {
res = download(`${origin}/${mount}`, '', {
method: 'post'
});
expect(res.code).to.equal(204);
@ -96,7 +97,7 @@ describe('Foxx service', () => {
expect(queuesAfter.length - queuesBefore.length).to.equal(1);
});
it('should support jobs running in the queue', () => {
let res = download(`${arango.getEndpoint().replace('tcp://', 'http://')}/${mount}`, '', {
let res = download(`${origin}/${mount}`, '', {
method: 'post'
});
expect(res.code).to.equal(204);
@ -109,7 +110,7 @@ describe('Foxx service', () => {
expect(jobResult.length).to.equal(1);
});
it('should support repeating job running in the queue', () => {
let res = request.post(`${arango.getEndpoint().replace('tcp://', 'http://')}/${mount}`, {
let res = request.post(`${origin}/${mount}`, {
body: JSON.stringify({repeatTimes: 2})
});
expect(res.statusCode).to.equal(204);

View File

@ -240,14 +240,7 @@ function iResearchFeatureAqlTestSuite () {
// force server-side V8 garbage collection
if (db._connection !== undefined) { // client test
let url = require('internal').arango.getEndpoint().replace('tcp', 'http');
url += '/_admin/execute?returnAsJSON=true';
let options = require('@arangodb/process-utils').makeAuthorizationHeaders({
username: 'root',
password: ''
});
options.method = 'POST';
require('internal').download(url, 'require("internal").wait(0.1, true);', options);
arango.POST('/_admin/execute?returnAsJSON=true', 'require("internal").wait(0.1, true);');
} else {
require("internal").wait(0.1, true);
}