1
0
Fork 0
arangodb/arangosh/Shell/V8ClientConnection.cpp

1712 lines
57 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Achim Brandt
////////////////////////////////////////////////////////////////////////////////
#include "V8ClientConnection.h"
#include <iostream>
#include <v8.h>
#include "Basics/FileUtils.h"
#include "Basics/StringUtils.h"
#include "Basics/VelocyPackHelper.h"
#include "Import/ImportHelper.h"
#include "Rest/HttpResponse.h"
#include "Rest/Version.h"
#include "Shell/ClientFeature.h"
#include "Shell/ConsoleFeature.h"
#include "SimpleHttpClient/GeneralClientConnection.h"
#include "SimpleHttpClient/SimpleHttpClient.h"
#include "SimpleHttpClient/SimpleHttpResult.h"
#include "V8/v8-conv.h"
#include "V8/v8-json.h"
#include "V8/v8-utils.h"
using namespace arangodb;
using namespace arangodb::application_features;
using namespace arangodb::basics;
using namespace arangodb::httpclient;
using namespace arangodb::import;
V8ClientConnection::V8ClientConnection(
std::unique_ptr<GeneralClientConnection>& connection,
std::string const& database, std::string const& username,
std::string const& password, double requestTimeout)
: _requestTimeout(requestTimeout),
_client(nullptr),
_lastHttpReturnCode(0),
_lastErrorMessage(""),
_httpResult(nullptr),
_version("arango"),
_mode("unknown mode") {
init(connection, username, password, database);
}
V8ClientConnection::~V8ClientConnection() {}
void V8ClientConnection::init(
std::unique_ptr<GeneralClientConnection>& connection, std::string const& username,
std::string const& password, std::string const& databaseName) {
_username = username;
_password = password;
_databaseName = databaseName;
_client.reset(new SimpleHttpClient(connection, _requestTimeout, false));
_client->setLocationRewriter(this, &rewriteLocation);
_client->setUserNamePassword("/", _username, _password);
// connect to server and get version number
std::unordered_map<std::string, std::string> headerFields;
std::unique_ptr<SimpleHttpResult> result(
_client->request(rest::RequestType::GET,
"/_api/version?details=true", nullptr, 0, headerFields));
if (result.get() == nullptr || !result->isComplete()) {
// save error message
_lastErrorMessage = _client->getErrorMessage();
_lastHttpReturnCode = 500;
} else {
_lastHttpReturnCode = result->getHttpReturnCode();
if (result->getHttpReturnCode() == static_cast<int>(rest::ResponseCode::OK)) {
try {
std::shared_ptr<VPackBuilder> parsedBody = result->getBodyVelocyPack();
VPackSlice const body = parsedBody->slice();
std::string const server =
VelocyPackHelper::getStringValue(body, "server", "");
// "server" value is a string and content is "arango"
if (server == "arango") {
// look up "version" value
_version = VelocyPackHelper::getStringValue(body, "version", "");
VPackSlice const details = body.get("details");
if (details.isObject()) {
VPackSlice const mode = details.get("mode");
if (mode.isString()) {
_mode = mode.copyString();
}
}
std::string const versionString =
VelocyPackHelper::getStringValue(body, "version", "");
std::pair<int, int> version = rest::Version::parseVersionString(versionString);
if (version.first < 3) {
// major version of server is too low
_client->disconnect();
_lastErrorMessage = "Server version number ('" + versionString + "') is too low. Expecting 3.0 or higher";
return;
}
}
} catch (...) {
// Ignore all parse errors
}
} else {
// initial request for /_api/version returned some non-HTTP 200 response.
// now set up an error message
_lastErrorMessage = _client->getErrorMessage();
if (result->getHttpReturnCode() > 0) {
_lastErrorMessage = StringUtils::itoa(result->getHttpReturnCode()) +
": " + result->getHttpReturnMessage();
}
}
}
}
std::string V8ClientConnection::rewriteLocation(void* data,
std::string const& location) {
V8ClientConnection* c = static_cast<V8ClientConnection*>(data);
TRI_ASSERT(c != nullptr);
if (c->_databaseName.empty()) {
// no database name provided
return location;
}
if (location[0] == '/') {
if (location.size() >= 5 && location[1] == '_' && location[2] == 'd' &&
location[3] == 'b' && location[4] == '/') {
// location already contains /_db/
return location;
}
return "/_db/" + c->_databaseName + location;
}
return "/_db/" + c->_databaseName + "/" + location;
}
void V8ClientConnection::setInterrupted(bool value) {
_client->setInterrupted(value);
}
bool V8ClientConnection::isConnected() { return _client->isConnected(); }
std::string V8ClientConnection::endpointSpecification() const {
return _client->getEndpointSpecification();
}
void V8ClientConnection::reconnect(ClientFeature* client) {
try {
std::unique_ptr<GeneralClientConnection> connection =
client->createConnection(client->endpoint());
init(connection, client->username(), client->password(), client->databaseName());
} catch (...) {
std::string errorMessage = "error in '" + client->endpoint() + "'";
throw errorMessage;
}
if (isConnected() &&
_lastHttpReturnCode == static_cast<int>(rest::ResponseCode::OK)) {
LOG_TOPIC(INFO, arangodb::Logger::FIXME) << "Connected to ArangoDB "
<< "'" << endpointSpecification() << "', "
<< "version " << _version << " [" << _mode << "], "
<< "database '" << _databaseName << "', "
<< "username: '" << _username << "'";
} else {
LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "Could not connect to endpoint '" << client->endpoint()
<< "', username: '" << client->username() << "'";
std::string errorMsg = "could not connect";
if (!_lastErrorMessage.empty()) {
errorMsg = _lastErrorMessage;
}
throw errorMsg;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief enum for wrapped V8 objects
////////////////////////////////////////////////////////////////////////////////
enum WRAP_CLASS_TYPES { WRAP_TYPE_CONNECTION = 1 };
////////////////////////////////////////////////////////////////////////////////
/// @brief map of connection objects
////////////////////////////////////////////////////////////////////////////////
static std::unordered_map<void*, v8::Persistent<v8::External>> Connections;
////////////////////////////////////////////////////////////////////////////////
/// @brief object template for the initial connection
////////////////////////////////////////////////////////////////////////////////
static v8::Persistent<v8::ObjectTemplate> ConnectionTempl;
////////////////////////////////////////////////////////////////////////////////
/// @brief copies v8::Object to std::unordered_map<std::string, std::string>
////////////////////////////////////////////////////////////////////////////////
static void ObjectToMap(v8::Isolate* isolate,
std::unordered_map<std::string, std::string>& myMap,
v8::Handle<v8::Value> val) {
v8::Local<v8::Object> v8Headers = val.As<v8::Object>();
if (v8Headers->IsObject()) {
v8::Local<v8::Array> const props = v8Headers->GetPropertyNames();
for (uint32_t i = 0; i < props->Length(); i++) {
v8::Local<v8::Value> key = props->Get(i);
myMap.emplace(TRI_ObjectToString(isolate, key),
TRI_ObjectToString(isolate, v8Headers->Get(key)));
}
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief weak reference callback for queries (call the destructor here)
////////////////////////////////////////////////////////////////////////////////
static void DestroyV8ClientConnection(V8ClientConnection* v8connection) {
TRI_ASSERT(v8connection != nullptr);
auto it = Connections.find(v8connection);
if (it != Connections.end()) {
(*it).second.Reset();
Connections.erase(it);
}
delete v8connection;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns a new client connection instance
////////////////////////////////////////////////////////////////////////////////
static V8ClientConnection* CreateV8ClientConnection(
std::unique_ptr<GeneralClientConnection>& connection,
ClientFeature* client) {
return new V8ClientConnection(connection, client->databaseName(),
client->username(), client->password(),
client->requestTimeout());
}
////////////////////////////////////////////////////////////////////////////////
/// @brief weak reference callback for queries (call the destructor here)
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_DestructorCallback(
const v8::WeakCallbackInfo<v8::Persistent<v8::External>>&
data) {
auto persistent = data.GetParameter();
auto myConnection =
v8::Local<v8::External>::New(data.GetIsolate(), *persistent);
auto v8connection = static_cast<V8ClientConnection*>(myConnection->Value());
DestroyV8ClientConnection(v8connection);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief wrap V8ClientConnection in a v8::Object
////////////////////////////////////////////////////////////////////////////////
static v8::Handle<v8::Value> WrapV8ClientConnection(
v8::Isolate* isolate, V8ClientConnection* v8connection) {
v8::EscapableHandleScope scope(isolate);
auto localConnectionTempl =
v8::Local<v8::ObjectTemplate>::New(isolate, ConnectionTempl);
v8::Local<v8::Object> result = localConnectionTempl->NewInstance();
auto myConnection = v8::External::New(isolate, v8connection);
result->SetInternalField(SLOT_CLASS_TYPE,
v8::Integer::New(isolate, WRAP_TYPE_CONNECTION));
result->SetInternalField(SLOT_CLASS, myConnection);
Connections[v8connection].Reset(isolate, myConnection);
Connections[v8connection].SetWeak(&Connections[v8connection],
ClientConnection_DestructorCallback,
v8::WeakCallbackType::kFinalizer);
return scope.Escape<v8::Value>(result);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection constructor
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_ConstructorCallback(
v8::FunctionCallbackInfo<v8::Value> const& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::External> wrap = v8::Local<v8::External>::Cast(args.Data());
ClientFeature* client = static_cast<ClientFeature*>(wrap->Value());
std::unique_ptr<GeneralClientConnection> connection;
try {
if (args.Length() > 0 && args[0]->IsString()) {
std::string definition = TRI_ObjectToString(isolate, args[0]);
connection = client->createConnection(definition);
} else {
connection = client->createConnection();
}
} catch (...) {
TRI_V8_THROW_EXCEPTION_PARAMETER("cannot connect to client");
}
std::unique_ptr<V8ClientConnection> v8connection(
CreateV8ClientConnection(connection, client));
if (v8connection->isConnected() &&
v8connection->lastHttpReturnCode() ==
(int)rest::ResponseCode::OK) {
LOG_TOPIC(INFO, arangodb::Logger::FIXME) << "Connected to ArangoDB "
<< "'" << v8connection->endpointSpecification() << "', "
<< "version " << v8connection->version() << " ["
<< v8connection->mode() << "], "
<< "database '" << v8connection->databaseName() << "', "
<< "username: '" << v8connection->username() << "'";
} else {
std::string errorMessage =
"Could not connect. Error message: " + v8connection->lastErrorMessage();
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_SIMPLE_CLIENT_COULD_NOT_CONNECT,
errorMessage.c_str());
}
TRI_V8_RETURN(WrapV8ClientConnection(isolate, v8connection.release()));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "reconnect"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_reconnect(
v8::FunctionCallbackInfo<v8::Value> const& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
v8::Local<v8::External> wrap = v8::Local<v8::External>::Cast(args.Data());
ClientFeature* client = static_cast<ClientFeature*>(wrap->Value());
if (v8connection == nullptr || client == nullptr) {
TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted");
}
if (args.Length() < 2) {
TRI_V8_THROW_EXCEPTION_USAGE(
"reconnect(<endpoint>, <database>, [, <username>, <password>])");
}
std::string const endpoint = TRI_ObjectToString(isolate, args[0]);
std::string databaseName = TRI_ObjectToString(isolate, args[1]);
std::string username;
if (args.Length() < 3) {
username = client->username();
} else {
username = TRI_ObjectToString(isolate, args[2]);
}
std::string password;
if (args.Length() < 4) {
ConsoleFeature* console =
ApplicationServer::getFeature<ConsoleFeature>("Console");
if (console->isEnabled()) {
password = console->readPassword("Please specify a password: ");
} else {
std::cout << "Please specify a password: " << std::flush;
password = ConsoleFeature::readPassword();
std::cout << std::endl << std::flush;
}
} else {
password = TRI_ObjectToString(isolate, args[3]);
}
client->setEndpoint(endpoint);
client->setDatabaseName(databaseName);
client->setUsername(username);
client->setPassword(password);
try {
v8connection->reconnect(client);
} catch (std::string const& errorMessage) {
TRI_V8_THROW_EXCEPTION_PARAMETER(errorMessage.c_str());
} catch (...) {
std::string errorMessage = "error in '" + endpoint + "'";
TRI_V8_THROW_EXCEPTION_PARAMETER(errorMessage.c_str());
}
TRI_V8_RETURN_TRUE();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "GET" helper
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpGetAny(
v8::FunctionCallbackInfo<v8::Value> const& args, bool raw) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
// get the connection
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
if (v8connection == nullptr) {
TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted");
}
// check params
if (args.Length() < 1 || args.Length() > 2 || !args[0]->IsString()) {
TRI_V8_THROW_EXCEPTION_USAGE("get(<url>[, <headers>])");
}
TRI_Utf8ValueNFC url(TRI_UNKNOWN_MEM_ZONE, args[0]);
// check header fields
std::unordered_map<std::string, std::string> headerFields;
if (args.Length() > 1) {
ObjectToMap(isolate, headerFields, args[1]);
}
TRI_V8_RETURN(v8connection->getData(isolate, *url, headerFields, raw));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "GET"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpGet(
v8::FunctionCallbackInfo<v8::Value> const& args) {
ClientConnection_httpGetAny(args, false);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "GET_RAW"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpGetRaw(
v8::FunctionCallbackInfo<v8::Value> const& args) {
ClientConnection_httpGetAny(args, true);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "HEAD" helper
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpHeadAny(
v8::FunctionCallbackInfo<v8::Value> const& args, bool raw) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
// get the connection
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
if (v8connection == nullptr) {
TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted");
}
// check params
if (args.Length() < 1 || args.Length() > 2 || !args[0]->IsString()) {
TRI_V8_THROW_EXCEPTION_USAGE("head(<url>[, <headers>])");
}
TRI_Utf8ValueNFC url(TRI_UNKNOWN_MEM_ZONE, args[0]);
// check header fields
std::unordered_map<std::string, std::string> headerFields;
if (args.Length() > 1) {
ObjectToMap(isolate, headerFields, args[1]);
}
TRI_V8_RETURN(v8connection->headData(isolate, *url, headerFields, raw));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "HEAD"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpHead(
v8::FunctionCallbackInfo<v8::Value> const& args) {
ClientConnection_httpHeadAny(args, false);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "HEAD_RAW"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpHeadRaw(
v8::FunctionCallbackInfo<v8::Value> const& args) {
ClientConnection_httpHeadAny(args, true);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "DELETE" helper
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpDeleteAny(
v8::FunctionCallbackInfo<v8::Value> const& args, bool raw) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
// get the connection
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
if (v8connection == nullptr) {
TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted");
}
// check params
if (args.Length() < 1 || args.Length() > 3 || !args[0]->IsString()) {
TRI_V8_THROW_EXCEPTION_USAGE("delete(<url>[, <headers>[, <body>]])");
}
TRI_Utf8ValueNFC url(TRI_UNKNOWN_MEM_ZONE, args[0]);
// check header fields
std::unordered_map<std::string, std::string> headerFields;
if (args.Length() > 1) {
ObjectToMap(isolate, headerFields, args[1]);
}
if (args.Length() > 2) {
TRI_Utf8ValueNFC bodyUtf(TRI_UNKNOWN_MEM_ZONE, args[2]);
std::string body = *bodyUtf;
TRI_V8_RETURN(v8connection->deleteData(isolate, *url, headerFields, raw, body));
}
TRI_V8_RETURN(v8connection->deleteData(isolate, *url, headerFields, raw, std::string()));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "DELETE"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpDelete(
v8::FunctionCallbackInfo<v8::Value> const& args) {
ClientConnection_httpDeleteAny(args, false);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "DELETE_RAW"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpDeleteRaw(
v8::FunctionCallbackInfo<v8::Value> const& args) {
ClientConnection_httpDeleteAny(args, true);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "OPTIONS" helper
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpOptionsAny(
v8::FunctionCallbackInfo<v8::Value> const& args, bool raw) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
// get the connection
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
if (v8connection == nullptr) {
TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted");
}
// check params
if (args.Length() < 2 || args.Length() > 3 || !args[0]->IsString() ||
!args[1]->IsString()) {
TRI_V8_THROW_EXCEPTION_USAGE("options(<url>, <body>[, <headers>])");
}
TRI_Utf8ValueNFC url(TRI_UNKNOWN_MEM_ZONE, args[0]);
v8::String::Utf8Value body(args[1]);
// check header fields
std::unordered_map<std::string, std::string> headerFields;
if (args.Length() > 2) {
ObjectToMap(isolate, headerFields, args[2]);
}
TRI_V8_RETURN(
v8connection->optionsData(isolate, *url, *body, headerFields, raw));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "OPTIONS"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpOptions(
v8::FunctionCallbackInfo<v8::Value> const& args) {
ClientConnection_httpOptionsAny(args, false);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "OPTIONS_RAW"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpOptionsRaw(
v8::FunctionCallbackInfo<v8::Value> const& args) {
ClientConnection_httpOptionsAny(args, true);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "POST" helper
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpPostAny(
v8::FunctionCallbackInfo<v8::Value> const& args, bool raw) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
// get the connection
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
if (v8connection == nullptr) {
TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted");
}
// check params
if (args.Length() < 2 || args.Length() > 3 || !args[0]->IsString() ||
!args[1]->IsString()) {
TRI_V8_THROW_EXCEPTION_USAGE("post(<url>, <body>[, <headers>])");
}
TRI_Utf8ValueNFC url(TRI_UNKNOWN_MEM_ZONE, args[0]);
v8::String::Utf8Value body(args[1]);
// check header fields
std::unordered_map<std::string, std::string> headerFields;
if (args.Length() > 2) {
ObjectToMap(isolate, headerFields, args[2]);
}
TRI_V8_RETURN(
v8connection->postData(isolate, *url, *body, headerFields, raw));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "POST"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpPost(
v8::FunctionCallbackInfo<v8::Value> const& args) {
ClientConnection_httpPostAny(args, false);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "POST_RAW"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpPostRaw(
v8::FunctionCallbackInfo<v8::Value> const& args) {
ClientConnection_httpPostAny(args, true);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "PUT" helper
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpPutAny(
v8::FunctionCallbackInfo<v8::Value> const& args, bool raw) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
// get the connection
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
if (v8connection == nullptr) {
TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted");
}
// check params
if (args.Length() < 2 || args.Length() > 3 || !args[0]->IsString() ||
!args[1]->IsString()) {
TRI_V8_THROW_EXCEPTION_USAGE("put(<url>, <body>[, <headers>])");
}
TRI_Utf8ValueNFC url(TRI_UNKNOWN_MEM_ZONE, args[0]);
v8::String::Utf8Value body(args[1]);
// check header fields
std::unordered_map<std::string, std::string> headerFields;
if (args.Length() > 2) {
ObjectToMap(isolate, headerFields, args[2]);
}
TRI_V8_RETURN(v8connection->putData(isolate, *url, *body, headerFields, raw));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "PUT"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpPut(
v8::FunctionCallbackInfo<v8::Value> const& args) {
ClientConnection_httpPutAny(args, false);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "PUT_RAW"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpPutRaw(
v8::FunctionCallbackInfo<v8::Value> const& args) {
ClientConnection_httpPutAny(args, true);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "PATCH" helper
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpPatchAny(
v8::FunctionCallbackInfo<v8::Value> const& args, bool raw) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
// get the connection
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
if (v8connection == nullptr) {
TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted");
}
// check params
if (args.Length() < 2 || args.Length() > 3 || !args[0]->IsString() ||
!args[1]->IsString()) {
TRI_V8_THROW_EXCEPTION_USAGE("patch(<url>, <body>[, <headers>])");
}
TRI_Utf8ValueNFC url(TRI_UNKNOWN_MEM_ZONE, args[0]);
v8::String::Utf8Value body(args[1]);
// check header fields
std::unordered_map<std::string, std::string> headerFields;
if (args.Length() > 2) {
ObjectToMap(isolate, headerFields, args[2]);
}
TRI_V8_RETURN(
v8connection->patchData(isolate, *url, *body, headerFields, raw));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "PATCH"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpPatch(
v8::FunctionCallbackInfo<v8::Value> const& args) {
ClientConnection_httpPatchAny(args, false);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "PATCH_RAW"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpPatchRaw(
v8::FunctionCallbackInfo<v8::Value> const& args) {
ClientConnection_httpPatchAny(args, true);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection send file helper
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_httpSendFile(
v8::FunctionCallbackInfo<v8::Value> const& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
// get the connection
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
if (v8connection == nullptr) {
TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted");
}
// check params
if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsString()) {
TRI_V8_THROW_EXCEPTION_USAGE("sendFile(<url>, <file>)");
}
TRI_Utf8ValueNFC url(TRI_UNKNOWN_MEM_ZONE, args[0]);
std::string const infile = TRI_ObjectToString(isolate, args[1]);
if (!FileUtils::exists(infile)) {
TRI_V8_THROW_EXCEPTION(TRI_ERROR_FILE_NOT_FOUND);
}
std::string body;
try {
body = FileUtils::slurp(infile);
} catch (...) {
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_errno(), "could not read file");
}
v8::TryCatch tryCatch;
// check header fields
std::unordered_map<std::string, std::string> headerFields;
v8::Local<v8::Value> result =
v8connection->postData(isolate, *url, body, headerFields);
if (tryCatch.HasCaught()) {
isolate->ThrowException(tryCatch.Exception());
return;
}
TRI_V8_RETURN(result);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "getEndpoint"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_getEndpoint(
v8::FunctionCallbackInfo<v8::Value> const& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
// get the connection
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
v8::Local<v8::External> wrap = v8::Local<v8::External>::Cast(args.Data());
ClientFeature* client = static_cast<ClientFeature*>(wrap->Value());
if (v8connection == nullptr || client == nullptr) {
TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted");
}
// check params
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("getEndpoint()");
}
TRI_V8_RETURN_STD_STRING(client->endpoint());
}
////////////////////////////////////////////////////////////////////////////////
/// @brief imports a CSV file
////////////////////////////////////////////////////////////////////////////////
static uint64_t DefaultChunkSize = 1024 * 1024 * 4;
static void ClientConnection_importCsv(
v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
if (args.Length() < 2) {
TRI_V8_THROW_EXCEPTION_USAGE(
"importCsvFile(<filename>, <collection>[, <options>])");
}
// extract the filename
v8::String::Utf8Value filename(args[0]);
if (*filename == nullptr) {
TRI_V8_THROW_TYPE_ERROR("<filename> must be a UTF-8 filename");
}
v8::String::Utf8Value collection(args[1]);
if (*collection == nullptr) {
TRI_V8_THROW_TYPE_ERROR("<collection> must be a UTF-8 filename");
}
// extract the options
v8::Handle<v8::String> separatorKey = TRI_V8_ASCII_STRING("separator");
v8::Handle<v8::String> quoteKey = TRI_V8_ASCII_STRING("quote");
std::string separator = ",";
std::string quote = "\"";
if (3 <= args.Length()) {
v8::Handle<v8::Object> options = args[2]->ToObject();
// separator
if (options->Has(separatorKey)) {
separator = TRI_ObjectToString(isolate, options->Get(separatorKey));
if (separator.length() < 1) {
TRI_V8_THROW_EXCEPTION_PARAMETER(
"<options>.separator must be at least one character");
}
}
// quote
if (options->Has(quoteKey)) {
quote = TRI_ObjectToString(isolate, options->Get(quoteKey));
if (quote.length() > 1) {
TRI_V8_THROW_EXCEPTION_PARAMETER(
"<options>.quote must be at most one character");
}
}
}
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
v8::Local<v8::External> wrap = v8::Local<v8::External>::Cast(args.Data());
ClientFeature* client = static_cast<ClientFeature*>(wrap->Value());
std::unique_ptr<SimpleHttpClient> httpClient =
client->createHttpClient(v8connection->endpointSpecification());
ImportHelper ih(httpClient.get(), DefaultChunkSize);
ih.setQuote(quote);
ih.setSeparator(separator.c_str());
std::string fileName = TRI_ObjectToString(isolate, args[0]);
std::string collectionName = TRI_ObjectToString(isolate, args[1]);
if (ih.importDelimited(collectionName, fileName, ImportHelper::CSV)) {
v8::Handle<v8::Object> result = v8::Object::New(isolate);
result->Set(TRI_V8_ASCII_STRING("lines"),
v8::Integer::New(isolate, (int32_t)ih.getReadLines()));
result->Set(TRI_V8_ASCII_STRING("created"),
v8::Integer::New(isolate, (int32_t)ih.getNumberCreated()));
result->Set(TRI_V8_ASCII_STRING("errors"),
v8::Integer::New(isolate, (int32_t)ih.getNumberErrors()));
result->Set(TRI_V8_ASCII_STRING("updated"),
v8::Integer::New(isolate, (int32_t)ih.getNumberUpdated()));
result->Set(TRI_V8_ASCII_STRING("ignored"),
v8::Integer::New(isolate, (int32_t)ih.getNumberIgnored()));
TRI_V8_RETURN(result);
}
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FAILED,
ih.getErrorMessage().c_str());
TRI_V8_TRY_CATCH_END
}
////////////////////////////////////////////////////////////////////////////////
/// @brief imports a JSON file
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_importJson(
v8::FunctionCallbackInfo<v8::Value> const& args) {
TRI_V8_TRY_CATCH_BEGIN(isolate);
v8::HandleScope scope(isolate);
if (args.Length() < 2) {
TRI_V8_THROW_EXCEPTION_USAGE("importJsonFile(<filename>, <collection>)");
}
// extract the filename
v8::String::Utf8Value filename(args[0]);
if (*filename == nullptr) {
TRI_V8_THROW_TYPE_ERROR("<filename> must be a UTF-8 filename");
}
v8::String::Utf8Value collection(args[1]);
if (*collection == nullptr) {
TRI_V8_THROW_TYPE_ERROR("<collection> must be a UTF-8 filename");
}
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
v8::Local<v8::External> wrap = v8::Local<v8::External>::Cast(args.Data());
ClientFeature* client = static_cast<ClientFeature*>(wrap->Value());
std::unique_ptr<SimpleHttpClient> httpClient =
client->createHttpClient(v8connection->endpointSpecification());
ImportHelper ih(httpClient.get(), DefaultChunkSize);
std::string fileName = TRI_ObjectToString(isolate, args[0]);
std::string collectionName = TRI_ObjectToString(isolate, args[1]);
if (ih.importJson(collectionName, fileName, false)) {
v8::Handle<v8::Object> result = v8::Object::New(isolate);
result->Set(TRI_V8_ASCII_STRING("lines"),
v8::Integer::New(isolate, (int32_t)ih.getReadLines()));
result->Set(TRI_V8_ASCII_STRING("created"),
v8::Integer::New(isolate, (int32_t)ih.getNumberCreated()));
result->Set(TRI_V8_ASCII_STRING("errors"),
v8::Integer::New(isolate, (int32_t)ih.getNumberErrors()));
result->Set(TRI_V8_ASCII_STRING("updated"),
v8::Integer::New(isolate, (int32_t)ih.getNumberUpdated()));
result->Set(TRI_V8_ASCII_STRING("ignored"),
v8::Integer::New(isolate, (int32_t)ih.getNumberIgnored()));
TRI_V8_RETURN(result);
}
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_FAILED,
ih.getErrorMessage().c_str());
TRI_V8_TRY_CATCH_END
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "lastError"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_lastHttpReturnCode(
v8::FunctionCallbackInfo<v8::Value> const& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
// get the connection
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
if (v8connection == nullptr) {
TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted");
}
// check params
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("lastHttpReturnCode()");
}
TRI_V8_RETURN(v8::Integer::New(isolate, v8connection->lastHttpReturnCode()));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "lastErrorMessage"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_lastErrorMessage(
v8::FunctionCallbackInfo<v8::Value> const& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
// get the connection
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
if (v8connection == nullptr) {
TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted");
}
// check params
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("lastErrorMessage()");
}
TRI_V8_RETURN_STD_STRING(v8connection->lastErrorMessage());
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "isConnected"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_isConnected(
v8::FunctionCallbackInfo<v8::Value> const& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
// get the connection
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
if (v8connection == nullptr) {
TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted");
}
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("isConnected()");
}
if (v8connection->isConnected()) {
TRI_V8_RETURN_TRUE();
}
TRI_V8_RETURN_FALSE();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "isConnected"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_toString(
v8::FunctionCallbackInfo<v8::Value> const& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
// get the connection
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
if (v8connection == nullptr) {
TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted");
}
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("toString()");
}
std::string result =
"[object ArangoConnection:" + v8connection->endpointSpecification();
if (v8connection->isConnected()) {
result += "," + v8connection->version() + ",connected]";
} else {
result += ",unconnected]";
}
TRI_V8_RETURN_STD_STRING(result);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "getVersion"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_getVersion(
v8::FunctionCallbackInfo<v8::Value> const& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
// get the connection
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
if (v8connection == nullptr) {
TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted");
}
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("getVersion()");
}
TRI_V8_RETURN_STD_STRING(v8connection->version());
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "getMode"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_getMode(
v8::FunctionCallbackInfo<v8::Value> const& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
// get the connection
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
if (v8connection == nullptr) {
TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted");
}
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("getMode()");
}
TRI_V8_RETURN_STD_STRING(v8connection->mode());
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "getDatabaseName"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_getDatabaseName(
v8::FunctionCallbackInfo<v8::Value> const& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
// get the connection
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
if (v8connection == nullptr) {
TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted");
}
if (args.Length() != 0) {
TRI_V8_THROW_EXCEPTION_USAGE("getDatabaseName()");
}
TRI_V8_RETURN_STD_STRING(v8connection->databaseName());
}
////////////////////////////////////////////////////////////////////////////////
/// @brief ClientConnection method "setDatabaseName"
////////////////////////////////////////////////////////////////////////////////
static void ClientConnection_setDatabaseName(
v8::FunctionCallbackInfo<v8::Value> const& args) {
v8::Isolate* isolate = args.GetIsolate();
v8::HandleScope scope(isolate);
// get the connection
V8ClientConnection* v8connection =
TRI_UnwrapClass<V8ClientConnection>(args.Holder(), WRAP_TYPE_CONNECTION);
v8::Local<v8::External> wrap = v8::Local<v8::External>::Cast(args.Data());
ClientFeature* client = static_cast<ClientFeature*>(wrap->Value());
if (v8connection == nullptr || client == nullptr) {
TRI_V8_THROW_EXCEPTION_INTERNAL("connection class corrupted");
}
if (args.Length() != 1 || !args[0]->IsString()) {
TRI_V8_THROW_EXCEPTION_USAGE("setDatabaseName(<name>)");
}
std::string const dbName = TRI_ObjectToString(isolate, args[0]);
v8connection->setDatabaseName(dbName);
client->setDatabaseName(dbName);
TRI_V8_RETURN_TRUE();
}
v8::Handle<v8::Value> V8ClientConnection::getData(
v8::Isolate* isolate, std::string const& location,
std::unordered_map<std::string, std::string> const& headerFields, bool raw) {
if (raw) {
return requestDataRaw(isolate, rest::RequestType::GET, location,
"", headerFields);
}
return requestData(isolate, rest::RequestType::GET, location, "",
headerFields);
}
v8::Handle<v8::Value> V8ClientConnection::headData(
v8::Isolate* isolate, std::string const& location,
std::unordered_map<std::string, std::string> const& headerFields, bool raw) {
if (raw) {
return requestDataRaw(isolate, rest::RequestType::HEAD, location,
"", headerFields);
}
return requestData(isolate, rest::RequestType::HEAD, location, "",
headerFields);
}
v8::Handle<v8::Value> V8ClientConnection::deleteData(
v8::Isolate* isolate, std::string const& location,
std::unordered_map<std::string, std::string> const& headerFields, bool raw,
std::string const& body) {
if (raw) {
return requestDataRaw(isolate, rest::RequestType::DELETE_REQ, location,
body, headerFields);
}
return requestData(isolate, rest::RequestType::DELETE_REQ, location, body,
headerFields);
}
v8::Handle<v8::Value> V8ClientConnection::optionsData(
v8::Isolate* isolate, std::string const& location, std::string const& body,
std::unordered_map<std::string, std::string> const& headerFields, bool raw) {
if (raw) {
return requestDataRaw(isolate, rest::RequestType::OPTIONS,
location, body, headerFields);
}
return requestData(isolate, rest::RequestType::OPTIONS, location,
body, headerFields);
}
v8::Handle<v8::Value> V8ClientConnection::postData(
v8::Isolate* isolate, std::string const& location, std::string const& body,
std::unordered_map<std::string, std::string> const& headerFields, bool raw) {
if (raw) {
return requestDataRaw(isolate, rest::RequestType::POST, location,
body, headerFields);
}
return requestData(isolate, rest::RequestType::POST, location, body,
headerFields);
}
v8::Handle<v8::Value> V8ClientConnection::putData(
v8::Isolate* isolate, std::string const& location, std::string const& body,
std::unordered_map<std::string, std::string> const& headerFields, bool raw) {
if (raw) {
return requestDataRaw(isolate, rest::RequestType::PUT, location,
body, headerFields);
}
return requestData(isolate, rest::RequestType::PUT, location, body,
headerFields);
}
v8::Handle<v8::Value> V8ClientConnection::patchData(
v8::Isolate* isolate, std::string const& location, std::string const& body,
std::unordered_map<std::string, std::string> const& headerFields, bool raw) {
if (raw) {
return requestDataRaw(isolate, rest::RequestType::PATCH, location,
body, headerFields);
}
return requestData(isolate, rest::RequestType::PATCH, location,
body, headerFields);
}
v8::Handle<v8::Value> V8ClientConnection::requestData(
v8::Isolate* isolate, rest::RequestType method,
std::string const& location, std::string const& body,
std::unordered_map<std::string, std::string> const& headerFields) {
_lastErrorMessage = "";
_lastHttpReturnCode = 0;
if (body.empty()) {
_httpResult.reset(
_client->request(method, location, nullptr, 0, headerFields));
} else {
_httpResult.reset(_client->request(method, location, body.c_str(),
body.length(), headerFields));
}
return handleResult(isolate);
}
v8::Handle<v8::Value> V8ClientConnection::requestDataRaw(
v8::Isolate* isolate, rest::RequestType method,
std::string const& location, std::string const& body,
std::unordered_map<std::string, std::string> const& headerFields) {
_lastErrorMessage = "";
_lastHttpReturnCode = 0;
if (body.empty()) {
_httpResult.reset(
_client->request(method, location, nullptr, 0, headerFields));
} else {
_httpResult.reset(_client->request(method, location, body.c_str(),
body.size(), headerFields));
}
if (_httpResult == nullptr) {
// create a fake response to prevent crashes when accessing the response
_httpResult.reset(new SimpleHttpResult());
_httpResult->setHttpReturnCode(500);
_httpResult->setResultType(SimpleHttpResult::COULD_NOT_CONNECT);
}
v8::Handle<v8::Object> result = v8::Object::New(isolate);
TRI_ASSERT(_httpResult != nullptr);
if (!_httpResult->isComplete()) {
// not complete
_lastErrorMessage = _client->getErrorMessage();
if (_lastErrorMessage.empty()) {
_lastErrorMessage = "Unknown error";
}
_lastHttpReturnCode = static_cast<int>(rest::ResponseCode::SERVER_ERROR);
result->ForceSet(
TRI_V8_ASCII_STRING("code"),
v8::Integer::New(isolate,
static_cast<int>(rest::ResponseCode::SERVER_ERROR)));
int errorNumber = 0;
switch (_httpResult->getResultType()) {
case (SimpleHttpResult::COULD_NOT_CONNECT):
errorNumber = TRI_SIMPLE_CLIENT_COULD_NOT_CONNECT;
break;
case (SimpleHttpResult::READ_ERROR):
errorNumber = TRI_SIMPLE_CLIENT_COULD_NOT_READ;
break;
case (SimpleHttpResult::WRITE_ERROR):
errorNumber = TRI_SIMPLE_CLIENT_COULD_NOT_WRITE;
break;
default:
errorNumber = TRI_SIMPLE_CLIENT_UNKNOWN_ERROR;
break;
}
result->ForceSet(TRI_V8_ASCII_STRING("error"),
v8::Boolean::New(isolate, true));
result->ForceSet(TRI_V8_ASCII_STRING("errorNum"),
v8::Integer::New(isolate, errorNumber));
result->ForceSet(TRI_V8_ASCII_STRING("errorMessage"),
TRI_V8_STD_STRING(_lastErrorMessage));
return result;
}
// complete
_lastHttpReturnCode = _httpResult->getHttpReturnCode();
// create raw response
result->ForceSet(TRI_V8_ASCII_STRING("code"),
v8::Integer::New(isolate, _lastHttpReturnCode));
if (_lastHttpReturnCode >= 400) {
std::string returnMessage(_httpResult->getHttpReturnMessage());
result->ForceSet(TRI_V8_ASCII_STRING("error"),
v8::Boolean::New(isolate, true));
result->ForceSet(TRI_V8_ASCII_STRING("errorNum"),
v8::Integer::New(isolate, _lastHttpReturnCode));
result->ForceSet(TRI_V8_ASCII_STRING("errorMessage"),
TRI_V8_STD_STRING(returnMessage));
} else {
result->ForceSet(TRI_V8_ASCII_STRING("error"),
v8::Boolean::New(isolate, false));
}
// got a body, copy it into the result
StringBuffer& sb = _httpResult->getBody();
if (sb.length() > 0) {
v8::Handle<v8::String> b = TRI_V8_STD_STRING(sb);
result->ForceSet(TRI_V8_ASCII_STRING("body"), b);
}
// copy all headers
v8::Handle<v8::Object> headers = v8::Object::New(isolate);
auto const& hf = _httpResult->getHeaderFields();
for (auto const& it : hf) {
v8::Handle<v8::String> key = TRI_V8_STD_STRING(it.first);
v8::Handle<v8::String> val = TRI_V8_STD_STRING(it.second);
headers->ForceSet(key, val);
}
result->ForceSet(TRI_V8_ASCII_STRING("headers"), headers);
// and returns
return result;
}
v8::Handle<v8::Value> V8ClientConnection::handleResult(v8::Isolate* isolate) {
if (_httpResult.get() == nullptr) {
return v8::Undefined(isolate);
}
// not complete
if (!_httpResult->isComplete()) {
_lastErrorMessage = _client->getErrorMessage();
if (_lastErrorMessage.empty()) {
_lastErrorMessage = "Unknown error";
}
_lastHttpReturnCode = static_cast<int>(rest::ResponseCode::SERVER_ERROR);
v8::Local<v8::Object> result = v8::Object::New(isolate);
result->ForceSet(TRI_V8_ASCII_STRING("error"),
v8::Boolean::New(isolate, true));
result->ForceSet(
TRI_V8_ASCII_STRING("code"),
v8::Integer::New(isolate,
static_cast<int>(rest::ResponseCode::SERVER_ERROR)));
int errorNumber = 0;
switch (_httpResult->getResultType()) {
case (SimpleHttpResult::COULD_NOT_CONNECT):
errorNumber = TRI_SIMPLE_CLIENT_COULD_NOT_CONNECT;
break;
case (SimpleHttpResult::READ_ERROR):
errorNumber = TRI_SIMPLE_CLIENT_COULD_NOT_READ;
break;
case (SimpleHttpResult::WRITE_ERROR):
errorNumber = TRI_SIMPLE_CLIENT_COULD_NOT_WRITE;
break;
default:
errorNumber = TRI_SIMPLE_CLIENT_UNKNOWN_ERROR;
break;
}
result->ForceSet(TRI_V8_ASCII_STRING("errorNum"),
v8::Integer::New(isolate, errorNumber));
result->ForceSet(TRI_V8_ASCII_STRING("errorMessage"),
TRI_V8_STD_STRING(_lastErrorMessage));
return result;
}
// complete
_lastHttpReturnCode = _httpResult->getHttpReturnCode();
// got a body
StringBuffer& sb = _httpResult->getBody();
if (sb.length() > 0) {
isolate->GetCurrentContext()->Global();
if (_httpResult->isJson()) {
// TODO: check if we can use the VPack parser here...
return TRI_FromJsonString(isolate, sb.c_str(), nullptr);
}
// return body as string
return TRI_V8_STD_STRING(sb);
}
// no body
v8::Local<v8::Object> result = v8::Object::New(isolate);
result->ForceSet(TRI_V8_ASCII_STRING("code"),
v8::Integer::New(isolate, _lastHttpReturnCode));
if (_lastHttpReturnCode >= 400) {
std::string returnMessage(_httpResult->getHttpReturnMessage());
result->ForceSet(TRI_V8_ASCII_STRING("error"),
v8::Boolean::New(isolate, true));
result->ForceSet(TRI_V8_ASCII_STRING("errorNum"),
v8::Integer::New(isolate, _lastHttpReturnCode));
result->ForceSet(TRI_V8_ASCII_STRING("errorMessage"),
TRI_V8_STD_STRING(returnMessage));
} else {
result->ForceSet(TRI_V8_ASCII_STRING("error"),
v8::Boolean::New(isolate, false));
}
return result;
}
void V8ClientConnection::initServer(v8::Isolate* isolate,
v8::Handle<v8::Context> context,
ClientFeature* client) {
v8::Local<v8::Value> v8client = v8::External::New(isolate, client);
v8::Local<v8::FunctionTemplate> connection_templ =
v8::FunctionTemplate::New(isolate);
connection_templ->SetClassName(TRI_V8_ASCII_STRING("ArangoConnection"));
v8::Local<v8::ObjectTemplate> connection_proto =
connection_templ->PrototypeTemplate();
connection_proto->Set(
isolate, "DELETE",
v8::FunctionTemplate::New(isolate, ClientConnection_httpDelete));
connection_proto->Set(
isolate, "DELETE_RAW",
v8::FunctionTemplate::New(isolate, ClientConnection_httpDeleteRaw));
connection_proto->Set(isolate, "GET", v8::FunctionTemplate::New(
isolate, ClientConnection_httpGet));
connection_proto->Set(
isolate, "GET_RAW",
v8::FunctionTemplate::New(isolate, ClientConnection_httpGetRaw));
connection_proto->Set(
isolate, "HEAD",
v8::FunctionTemplate::New(isolate, ClientConnection_httpHead));
connection_proto->Set(
isolate, "HEAD_RAW",
v8::FunctionTemplate::New(isolate, ClientConnection_httpHeadRaw));
connection_proto->Set(
isolate, "OPTIONS",
v8::FunctionTemplate::New(isolate, ClientConnection_httpOptions));
connection_proto->Set(
isolate, "OPTIONS_RAW",
v8::FunctionTemplate::New(isolate, ClientConnection_httpOptionsRaw));
connection_proto->Set(
isolate, "PATCH",
v8::FunctionTemplate::New(isolate, ClientConnection_httpPatch));
connection_proto->Set(
isolate, "PATCH_RAW",
v8::FunctionTemplate::New(isolate, ClientConnection_httpPatchRaw));
connection_proto->Set(
isolate, "POST",
v8::FunctionTemplate::New(isolate, ClientConnection_httpPost));
connection_proto->Set(
isolate, "POST_RAW",
v8::FunctionTemplate::New(isolate, ClientConnection_httpPostRaw));
connection_proto->Set(isolate, "PUT", v8::FunctionTemplate::New(
isolate, ClientConnection_httpPut));
connection_proto->Set(
isolate, "PUT_RAW",
v8::FunctionTemplate::New(isolate, ClientConnection_httpPutRaw));
connection_proto->Set(
isolate, "SEND_FILE",
v8::FunctionTemplate::New(isolate, ClientConnection_httpSendFile));
connection_proto->Set(isolate, "getEndpoint",
v8::FunctionTemplate::New(
isolate, ClientConnection_getEndpoint, v8client));
connection_proto->Set(
isolate, "lastHttpReturnCode",
v8::FunctionTemplate::New(isolate, ClientConnection_lastHttpReturnCode));
connection_proto->Set(
isolate, "lastErrorMessage",
v8::FunctionTemplate::New(isolate, ClientConnection_lastErrorMessage));
connection_proto->Set(
isolate, "isConnected",
v8::FunctionTemplate::New(isolate, ClientConnection_isConnected));
connection_proto->Set(
isolate, "reconnect",
v8::FunctionTemplate::New(isolate, ClientConnection_reconnect, v8client));
connection_proto->Set(
isolate, "toString",
v8::FunctionTemplate::New(isolate, ClientConnection_toString));
connection_proto->Set(
isolate, "getVersion",
v8::FunctionTemplate::New(isolate, ClientConnection_getVersion));
connection_proto->Set(
isolate, "getMode",
v8::FunctionTemplate::New(isolate, ClientConnection_getMode));
connection_proto->Set(
isolate, "getDatabaseName",
v8::FunctionTemplate::New(isolate, ClientConnection_getDatabaseName));
connection_proto->Set(
isolate, "setDatabaseName",
v8::FunctionTemplate::New(isolate, ClientConnection_setDatabaseName,
v8client));
connection_proto->Set(
isolate, "importCsv",
v8::FunctionTemplate::New(isolate, ClientConnection_importCsv, v8client));
connection_proto->Set(isolate, "importJson",
v8::FunctionTemplate::New(
isolate, ClientConnection_importJson, v8client));
connection_proto->SetCallAsFunctionHandler(
ClientConnection_ConstructorCallback, v8client);
v8::Local<v8::ObjectTemplate> connection_inst =
connection_templ->InstanceTemplate();
connection_inst->SetInternalFieldCount(2);
TRI_AddGlobalVariableVocbase(isolate, context,
TRI_V8_ASCII_STRING("ArangoConnection"),
connection_proto->NewInstance());
ConnectionTempl.Reset(isolate, connection_inst);
// add the client connection to the context:
TRI_AddGlobalVariableVocbase(isolate, context,
TRI_V8_ASCII_STRING("SYS_ARANGO"),
WrapV8ClientConnection(isolate, this));
}