From 30415c6a7310209f012a602cf507f1550600c1a6 Mon Sep 17 00:00:00 2001 From: Max Neunhoeffer Date: Mon, 6 Jan 2014 11:40:49 +0100 Subject: [PATCH] Cleanup conversion C++ <-> Javascript of HttpRequest/HttpResponse. --- arangod/Actions/actions.h | 16 +- arangod/V8Server/v8-actions.cpp | 518 +++++++++------------- js/server/modules/org/arangodb/actions.js | 18 - 3 files changed, 202 insertions(+), 350 deletions(-) diff --git a/arangod/Actions/actions.h b/arangod/Actions/actions.h index 520f793f2b..65fd971cd4 100644 --- a/arangod/Actions/actions.h +++ b/arangod/Actions/actions.h @@ -52,19 +52,6 @@ namespace triagens { /// @{ //////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -/// @brief parameter type -//////////////////////////////////////////////////////////////////////////////// - -typedef enum { - TRI_ACT_STRING, - TRI_ACT_NUMBER, - TRI_ACT_COLLECTION, - TRI_ACT_COLLECTION_NAME, - TRI_ACT_COLLECTION_ID -} -TRI_action_parameter_type_e; - //////////////////////////////////////////////////////////////////////////////// /// @brief action descriptor //////////////////////////////////////////////////////////////////////////////// @@ -72,7 +59,7 @@ TRI_action_parameter_type_e; class TRI_action_t { public: TRI_action_t (std::set const& context) - : _type(), _url(), _isPrefix(false), _urlParts(0), _parameters(), _contexts(context) { + : _type(), _url(), _isPrefix(false), _urlParts(0), _contexts(context) { } virtual ~TRI_action_t () {} @@ -84,7 +71,6 @@ class TRI_action_t { bool _isPrefix; size_t _urlParts; - std::map _parameters; std::set _contexts; }; diff --git a/arangod/V8Server/v8-actions.cpp b/arangod/V8Server/v8-actions.cpp index 279d0627c6..71999a7295 100644 --- a/arangod/V8Server/v8-actions.cpp +++ b/arangod/V8Server/v8-actions.cpp @@ -203,69 +203,6 @@ class v8_action_t : public TRI_action_t { /// @{ //////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -/// @brief parses the action options "parameters" field of type string -//////////////////////////////////////////////////////////////////////////////// - -static void ParseActionOptionsParameter (TRI_v8_global_t* v8g, - TRI_action_t* action, - string const& key, - string const& parameter) { - TRI_action_parameter_type_e p; - - if (parameter == "collection") { - p = TRI_ACT_COLLECTION; - } - else if (parameter == "collection-name") { - p = TRI_ACT_COLLECTION_NAME; - } - else if (parameter == "collection-identifier") { - p = TRI_ACT_COLLECTION_ID; - } - else if (parameter == "number") { - p = TRI_ACT_NUMBER; - } - else if (parameter == "string") { - p = TRI_ACT_STRING; - } - else { - LOG_ERROR("unknown parameter type '%s', falling back to string", parameter.c_str()); - p = TRI_ACT_STRING; - } - - action->_parameters[key] = p; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief parses the action options "parameters" field -//////////////////////////////////////////////////////////////////////////////// - -static void ParseActionOptionsParameter (TRI_v8_global_t* v8g, - TRI_action_t* action, - string const& key, - v8::Handle parameter) { - if (parameter->IsString() || parameter->IsStringObject()) { - ParseActionOptionsParameter(v8g, action, key, TRI_ObjectToString(parameter)); - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief parses the action options "parameters" field -//////////////////////////////////////////////////////////////////////////////// - -static void ParseActionOptionsParameters (TRI_v8_global_t* v8g, - TRI_action_t* action, - v8::Handle parameters) { - v8::Handle keys = parameters->GetOwnPropertyNames(); - uint32_t len = keys->Length(); - - for (uint32_t i = 0; i < len; ++i) { - v8::Handle key = keys->Get(i); - - ParseActionOptionsParameter(v8g, action, TRI_ObjectToString(key), parameters->Get(key)); - } -} - //////////////////////////////////////////////////////////////////////////////// /// @brief parses the action options //////////////////////////////////////////////////////////////////////////////// @@ -274,15 +211,6 @@ static void ParseActionOptions (TRI_v8_global_t* v8g, TRI_action_t* action, v8::Handle options) { - // check "parameters" field - if (options->Has(v8g->ParametersKey)) { - v8::Handle parameters = options->Get(v8g->ParametersKey); - - if (parameters->IsObject()) { - ParseActionOptionsParameters(v8g, action, parameters->ToObject()); - } - } - // check the "prefix" field if (options->Has(v8g->PrefixKey)) { action->_isPrefix = TRI_ObjectToBoolean(options->Get(v8g->PrefixKey)); @@ -296,7 +224,7 @@ static void ParseActionOptions (TRI_v8_global_t* v8g, /// @brief add cookie //////////////////////////////////////////////////////////////////////////////// -static void AddCookie (TRI_v8_global_t* v8g, +static void AddCookie (TRI_v8_global_t const* v8g, HttpResponse* response, v8::Handle data) { @@ -349,21 +277,12 @@ static void AddCookie (TRI_v8_global_t* v8g, } //////////////////////////////////////////////////////////////////////////////// -/// @brief executes an action +/// @brief convert a C++ HttpRequest to a V8 request object //////////////////////////////////////////////////////////////////////////////// -static HttpResponse* ExecuteActionVocbase (TRI_vocbase_t* vocbase, - v8::Isolate* isolate, - TRI_action_t const* action, - v8::Handle callback, - HttpRequest* request) { - TRI_v8_global_t* v8g; - +static v8::Handle RequestCppToV8 ( TRI_v8_global_t const* v8g, + HttpRequest* request) { v8::HandleScope scope; - v8::TryCatch tryCatch; - - v8g = (TRI_v8_global_t*) isolate->GetData(); - // setup the request v8::Handle req = v8::Object::New(); @@ -440,25 +359,6 @@ static HttpResponse* ExecuteActionVocbase (TRI_vocbase_t* vocbase, req->Set(v8g->PrefixKey, v8::String::New(path.c_str(), path.size())); - // copy suffix - v8::Handle suffixArray = v8::Array::New(); - vector const& suffix = request->suffix(); - - uint32_t index = 0; - char const* sep = ""; - - for (size_t s = action->_urlParts; s < suffix.size(); ++s) { - suffixArray->Set(index++, v8::String::New(suffix[s].c_str(), suffix[s].size())); - - path += sep + suffix[s]; - sep = "/"; - } - - req->Set(v8g->SuffixKey, suffixArray); - - // copy full path - req->Set(v8g->PathKey, v8::String::New(path.c_str(), path.size())); - // copy header fields v8::Handle headerFields = v8::Object::New(); @@ -466,8 +366,10 @@ static HttpResponse* ExecuteActionVocbase (TRI_vocbase_t* vocbase, map::const_iterator iter = headers.begin(); for (; iter != headers.end(); ++iter) { - headerFields->Set(v8::String::New(iter->first.c_str(), iter->first.size()), - v8::String::New(iter->second.c_str(), iter->second.size())); + headerFields->Set(v8::String::New(iter->first.c_str(), + iter->first.size()), + v8::String::New(iter->second.c_str(), + iter->second.size())); } req->Set(v8g->HeadersKey, headerFields); @@ -476,17 +378,20 @@ static HttpResponse* ExecuteActionVocbase (TRI_vocbase_t* vocbase, switch (request->requestType()) { case HttpRequest::HTTP_REQUEST_POST: req->Set(v8g->RequestTypeKey, v8g->PostConstant); - req->Set(v8g->RequestBodyKey, v8::String::New(request->body(), request->bodySize())); + req->Set(v8g->RequestBodyKey, v8::String::New(request->body(), + request->bodySize())); break; case HttpRequest::HTTP_REQUEST_PUT: req->Set(v8g->RequestTypeKey, v8g->PutConstant); - req->Set(v8g->RequestBodyKey, v8::String::New(request->body(), request->bodySize())); + req->Set(v8g->RequestBodyKey, v8::String::New(request->body(), + request->bodySize())); break; case HttpRequest::HTTP_REQUEST_PATCH: req->Set(v8g->RequestTypeKey, v8g->PatchConstant); - req->Set(v8g->RequestBodyKey, v8::String::New(request->body(), request->bodySize())); + req->Set(v8g->RequestBodyKey, v8::String::New(request->body(), + request->bodySize())); break; case HttpRequest::HTTP_REQUEST_OPTIONS: @@ -511,101 +416,20 @@ static HttpResponse* ExecuteActionVocbase (TRI_vocbase_t* vocbase, v8::Handle valuesObject = v8::Object::New(); map values = request->values(); - for (map::iterator i = values.begin(); i != values.end(); ++i) { + for (map::iterator i = values.begin(); + i != values.end(); ++i) { string const& k = i->first; string const& v = i->second; - map::const_iterator p = action->_parameters.find(k); - - if (p == action->_parameters.end()) { - valuesObject->Set(v8::String::New(k.c_str(), k.size()), - v8::String::New(v.c_str(), v.size())); - } - else { - TRI_action_parameter_type_e const& ap = p->second; - - switch (ap) { - case TRI_ACT_COLLECTION: { - if (! v.empty()) { - char ch = v[0]; - TRI_vocbase_col_t const* collection = 0; - - if ('0' < ch && ch <= '9') { - collection = TRI_LookupCollectionByIdVocBase(vocbase, TRI_UInt64String(v.c_str())); - } - else { - collection = TRI_LookupCollectionByNameVocBase(vocbase, v.c_str()); - } - - if (collection != 0) { - v8::Handle c = TRI_WrapCollection(collection); - - if (c.IsEmpty()) { - // TODO: raise exception here - } - else { - valuesObject->Set(v8::String::New(k.c_str(), k.size()), c); - } - } - } - - break; - } - - case TRI_ACT_COLLECTION_NAME: { - TRI_vocbase_col_t const* collection = TRI_LookupCollectionByNameVocBase(vocbase, v.c_str()); - - if (collection != 0) { - v8::Handle c = TRI_WrapCollection(collection); - - if (c.IsEmpty()) { - // TODO: raise exception here - } - else { - valuesObject->Set(v8::String::New(k.c_str(), k.size()), c); - } - } - - break; - } - - case TRI_ACT_COLLECTION_ID: { - TRI_vocbase_col_t const* collection = TRI_LookupCollectionByIdVocBase( - vocbase, - TRI_UInt64String(v.c_str())); - - if (collection != 0) { - v8::Handle c = TRI_WrapCollection(collection); - - if (c.IsEmpty()) { - // TODO: raise exception here - } - else { - valuesObject->Set(v8::String::New(k.c_str(), k.size()), c); - } - } - - break; - } - - case TRI_ACT_NUMBER: - valuesObject->Set(v8::String::New(k.c_str(), k.size()), - v8::Number::New(TRI_DoubleString(v.c_str()))); - break; - - case TRI_ACT_STRING: { - valuesObject->Set(v8::String::New(k.c_str(), k.size()), - v8::String::New(v.c_str())); - break; - } - } - } + valuesObject->Set(v8::String::New(k.c_str(), k.size()), + v8::String::New(v.c_str(), v.size())); } // copy request array parameter (a[]=1&a[]=2&...) map* > arrayValues = request->arrayValues(); - for (map* >::iterator i = arrayValues.begin(); i != arrayValues.end(); ++i) { + for (map* >::iterator i = arrayValues.begin(); + i != arrayValues.end(); ++i) { string const& k = i->first; vector* v = i->second; @@ -627,17 +451,193 @@ static HttpResponse* ExecuteActionVocbase (TRI_vocbase_t* vocbase, iter = cookies.begin(); for (; iter != cookies.end(); ++iter) { - cookiesObject->Set(v8::String::New(iter->first.c_str(), iter->first.size()), - v8::String::New(iter->second.c_str(), iter->second.size())); + cookiesObject->Set(v8::String::New(iter->first.c_str(), + iter->first.size()), + v8::String::New(iter->second.c_str(), + iter->second.size())); } req->Set(v8g->CookiesKey, cookiesObject); - // determine API compatibility version int32_t compatibility = request->compatibility(); req->Set(v8g->CompatibilityKey, v8::Integer::New(compatibility)); - + + return scope.Close(req); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief convert a C++ HttpRequest to a V8 request object +//////////////////////////////////////////////////////////////////////////////// + +static HttpResponse* ResponseV8ToCpp( TRI_v8_global_t const* v8g, + v8::Handle res) { + HttpResponse::HttpResponseCode code = HttpResponse::OK; + + if (res->Has(v8g->ResponseCodeKey)) { + // Windows has issues with converting from a double to an enumeration type + code = (HttpResponse::HttpResponseCode) + ((int) (TRI_ObjectToDouble(res->Get(v8g->ResponseCodeKey)))); + } + + HttpResponse* response = new HttpResponse(code); + + if (res->Has(v8g->ContentTypeKey)) { + response->setContentType( + TRI_ObjectToString(res->Get(v8g->ContentTypeKey))); + } + + // ......................................................................... + // body + // ......................................................................... + + if (res->Has(v8g->BodyKey)) { + // check if we should apply result transformations + // transformations turn the result from one type into another + // a Javascript action can request transformations by + // putting a list of transformations into the res.transformations + // array, e.g. res.transformations = [ "base64encode" ] + v8::Handle val = res->Get(v8g->TransformationsKey); + + if (val->IsArray()) { + string out(TRI_ObjectToString(res->Get(v8g->BodyKey))); + v8::Handle transformations = val.As(); + + for (uint32_t i = 0; i < transformations->Length(); i++) { + v8::Handle transformator + = transformations->Get(v8::Integer::New(i)); + string name = TRI_ObjectToString(transformator); + + // check available transformations + if (name == "base64encode") { + // base64-encode the result + out = StringUtils::encodeBase64(out); + // set the correct content-encoding header + response->setHeader("content-encoding", "base64"); + } + else if (name == "base64decode") { + // base64-decode the result + out = StringUtils::decodeBase64(out); + // set the correct content-encoding header + response->setHeader("content-encoding", "binary"); + } + } + + response->body().appendText(out); + } + else { + response->body().appendText(TRI_ObjectToString(res->Get(v8g->BodyKey))); + } + } + + // ......................................................................... + // body from file + // ......................................................................... + + else if (res->Has(v8g->BodyFromFileKey)) { + TRI_Utf8ValueNFC filename(TRI_UNKNOWN_MEM_ZONE, + res->Get(v8g->BodyFromFileKey)); + size_t length; + char* content = TRI_SlurpFile(TRI_UNKNOWN_MEM_ZONE, *filename, &length); + + if (content != 0) { + response->body().appendText(content, length); + TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, content); + } + else { + string msg = string("cannot read file '") + *filename + "': " + + TRI_last_error(); + + response->body().appendText(msg.c_str(), msg.size()); + response->setResponseCode(HttpResponse::SERVER_ERROR); + } + } + + // ......................................................................... + // headers + // ......................................................................... + + if (res->Has(v8g->HeadersKey)) { + v8::Handle val = res->Get(v8g->HeadersKey); + v8::Handle v8Headers = val.As(); + + if (v8Headers->IsObject()) { + v8::Handle props = v8Headers->GetPropertyNames(); + + for (uint32_t i = 0; i < props->Length(); i++) { + v8::Handle key = props->Get(v8::Integer::New(i)); + response->setHeader(TRI_ObjectToString(key), + TRI_ObjectToString(v8Headers->Get(key))); + } + } + } + + // ......................................................................... + // cookies + // ......................................................................... + + if (res->Has(v8g->CookiesKey)) { + v8::Handle val = res->Get(v8g->CookiesKey); + v8::Handle v8Cookies = val.As(); + + if (v8Cookies->IsArray()) { + v8::Handle v8Array = v8Cookies.As(); + + for (uint32_t i = 0; i < v8Array->Length(); i++) { + v8::Handle v8Cookie = v8Array->Get(i); + if (v8Cookie->IsObject()) { + AddCookie(v8g, response, v8Cookie.As()); + } + } + } + else if (v8Cookies->IsObject()) { + // one cookie + AddCookie(v8g, response, v8Cookies); + } + } + + return response; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes an action +//////////////////////////////////////////////////////////////////////////////// + +static HttpResponse* ExecuteActionVocbase (TRI_vocbase_t* vocbase, + v8::Isolate* isolate, + TRI_action_t const* action, + v8::Handle callback, + HttpRequest* request) { + TRI_v8_global_t const* v8g; + + v8::HandleScope scope; + v8::TryCatch tryCatch; + + v8g = (TRI_v8_global_t const*) isolate->GetData(); + + v8::Handle req = RequestCppToV8(v8g, request); + + // copy suffix, which comes from the action: + string path = request->prefix(); + v8::Handle suffixArray = v8::Array::New(); + vector const& suffix = request->suffix(); + + uint32_t index = 0; + char const* sep = ""; + + for (size_t s = action->_urlParts; s < suffix.size(); ++s) { + suffixArray->Set(index++, v8::String::New(suffix[s].c_str(), + suffix[s].size())); + + path += sep + suffix[s]; + sep = "/"; + } + + req->Set(v8g->SuffixKey, suffixArray); + + // copy full path + req->Set(v8g->PathKey, v8::String::New(path.c_str(), path.size())); + // execute the callback v8::Handle res = v8::Object::New(); v8::Handle args[2] = { req, res }; @@ -654,124 +654,8 @@ static HttpResponse* ExecuteActionVocbase (TRI_vocbase_t* vocbase, } else { - HttpResponse::HttpResponseCode code = HttpResponse::OK; + HttpResponse* response = ResponseV8ToCpp( v8g, res ); - if (res->Has(v8g->ResponseCodeKey)) { - // Windows has issues with converting from a double to an enumeration type - code = (HttpResponse::HttpResponseCode) ((int) (TRI_ObjectToDouble(res->Get(v8g->ResponseCodeKey)))); - } - - HttpResponse* response = new HttpResponse(code); - - if (res->Has(v8g->ContentTypeKey)) { - response->setContentType(TRI_ObjectToString(res->Get(v8g->ContentTypeKey))); - } - - // ............................................................................. - // body - // ............................................................................. - - if (res->Has(v8g->BodyKey)) { - // check if we should apply result transformations - // transformations turn the result from one type into another - // a Javascript action can request transformations by - // putting a list of transformations into the res.transformations - // array, e.g. res.transformations = [ "base64encode" ] - v8::Handle val = res->Get(v8g->TransformationsKey); - - if (val->IsArray()) { - string out(TRI_ObjectToString(res->Get(v8g->BodyKey))); - v8::Handle transformations = val.As(); - - for (uint32_t i = 0; i < transformations->Length(); i++) { - v8::Handle transformator = transformations->Get(v8::Integer::New(i)); - string name = TRI_ObjectToString(transformator); - - // check available transformations - if (name == "base64encode") { - // base64-encode the result - out = StringUtils::encodeBase64(out); - // set the correct content-encoding header - response->setHeader("content-encoding", "base64"); - } - else if (name == "base64decode") { - // base64-decode the result - out = StringUtils::decodeBase64(out); - // set the correct content-encoding header - response->setHeader("content-encoding", "binary"); - } - } - - response->body().appendText(out); - } - else { - response->body().appendText(TRI_ObjectToString(res->Get(v8g->BodyKey))); - } - } - - // ............................................................................. - // body from file - // ............................................................................. - - else if (res->Has(v8g->BodyFromFileKey)) { - TRI_Utf8ValueNFC filename(TRI_UNKNOWN_MEM_ZONE, res->Get(v8g->BodyFromFileKey)); - size_t length; - char* content = TRI_SlurpFile(TRI_UNKNOWN_MEM_ZONE, *filename, &length); - - if (content != 0) { - response->body().appendText(content, length); - TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, content); - } - else { - string msg = string("cannot read file '") + *filename + "': " + TRI_last_error(); - - response->body().appendText(msg.c_str(), msg.size()); - response->setResponseCode(HttpResponse::SERVER_ERROR); - } - } - - // ............................................................................. - // headers - // ............................................................................. - - if (res->Has(v8g->HeadersKey)) { - v8::Handle val = res->Get(v8g->HeadersKey); - v8::Handle v8Headers = val.As(); - - if (v8Headers->IsObject()) { - v8::Handle props = v8Headers->GetPropertyNames(); - - for (uint32_t i = 0; i < props->Length(); i++) { - v8::Handle key = props->Get(v8::Integer::New(i)); - response->setHeader(TRI_ObjectToString(key), TRI_ObjectToString(v8Headers->Get(key))); - } - } - } - - // ............................................................................. - // cookies - // ............................................................................. - - if (res->Has(v8g->CookiesKey)) { - v8::Handle val = res->Get(v8g->CookiesKey); - v8::Handle v8Cookies = val.As(); - - if (v8Cookies->IsArray()) { - v8::Handle v8Array = v8Cookies.As(); - - for (uint32_t i = 0; i < v8Array->Length(); i++) { - v8::Handle v8Cookie = v8Array->Get(i); - if (v8Cookie->IsObject()) { - AddCookie(v8g, response, v8Cookie.As()); - } - } - } - else if (v8Cookies->IsObject()) { - // one cookie - AddCookie(v8g, response, v8Cookies); - } - } - return response; } } diff --git a/js/server/modules/org/arangodb/actions.js b/js/server/modules/org/arangodb/actions.js index d26bbfa63f..7e60335a47 100644 --- a/js/server/modules/org/arangodb/actions.js +++ b/js/server/modules/org/arangodb/actions.js @@ -932,17 +932,6 @@ function flattenRouting (routes, path, urlParameters, depth, prefix) { /// /// You can use the functions @FN{ResultOk} and @FN{ResultError} to easily /// generate a response. -/// -/// @FA{options.parameters} -/// -/// Normally the parameters are passed to the callback as strings. You can -/// use the @FA{options}, to force a converstion of the parameter to -/// -/// - @c "collection" -/// - @c "collection-identifier" -/// - @c "collection-name" -/// - @c "number" -/// - @c "string" //////////////////////////////////////////////////////////////////////////////// function defineHttp (options) { @@ -951,7 +940,6 @@ function defineHttp (options) { var url = options.url; var context = options.context; var callback = options.callback; - var parameters = options.parameters; var prefix = true; if (typeof context === "undefined") { @@ -968,7 +956,6 @@ function defineHttp (options) { } var parameter = { - parameters : parameters, prefix : prefix }; @@ -1994,11 +1981,6 @@ exports.redirectRequest = redirectRequest; exports.pathHandler = pathHandler; // some useful constants -exports.COLLECTION = "collection"; -exports.COLLECTION_IDENTIFIER = "collection-identifier"; -exports.COLLECTION_NAME = "collection-name"; -exports.NUMBER = "number"; - exports.DELETE = "DELETE"; exports.GET = "GET"; exports.HEAD = "HEAD";