1
0
Fork 0

added cookie support

This commit is contained in:
a-brandt 2013-04-10 16:23:42 +02:00
parent 71a093e3d0
commit 2d8c547fc8
6 changed files with 477 additions and 0 deletions

View File

@ -270,6 +270,60 @@ static void ParseActionOptions (TRI_v8_global_t* v8g,
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief add cookie
////////////////////////////////////////////////////////////////////////////////
static void addCookie (TRI_v8_global_t* v8g, HttpResponse* response, v8::Handle<v8::Object> data) {
string name;
string value;
int lifeTimeSeconds = 0;
string path = "/";
string domain = "";
bool secure = false;
bool httpOnly = false;
if (data->Has(v8g->CookieName)) {
v8::Handle<v8::Value> v = data->Get(v8g->CookieName);
name = TRI_ObjectToString(v);
}
else {
// something is wrong here
return;
}
if (data->Has(v8g->CookieValue)) {
v8::Handle<v8::Value> v = data->Get(v8g->CookieValue);
value = TRI_ObjectToString(v);
}
else {
// something is wrong here
return;
}
if (data->Has(v8g->CookieLiveTime)) {
v8::Handle<v8::Value> v = data->Get(v8g->CookieLiveTime);
lifeTimeSeconds = TRI_ObjectToInt64(v);
}
if (data->Has(v8g->CookiePath)) {
v8::Handle<v8::Value> v = data->Get(v8g->CookiePath);
path = TRI_ObjectToString(v);
}
if (data->Has(v8g->CookieDomain)) {
v8::Handle<v8::Value> v = data->Get(v8g->CookieDomain);
domain = TRI_ObjectToString(v);
}
if (data->Has(v8g->CookieSecure)) {
v8::Handle<v8::Value> v = data->Get(v8g->CookieSecure);
secure = TRI_ObjectToBoolean(v);
}
if (data->Has(v8g->CookieHttpOnly)) {
v8::Handle<v8::Value> v = data->Get(v8g->CookieHttpOnly);
httpOnly = TRI_ObjectToBoolean(v);
}
response->setCookie(name, value, lifeTimeSeconds, path, domain, secure, httpOnly);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief executes an action
////////////////////////////////////////////////////////////////////////////////
@ -311,6 +365,10 @@ static HttpResponse* ExecuteActionVocbase (TRI_vocbase_t* vocbase,
// "user-agent" : "Mozilla/5.0"
// },
//
// "cookies" : {
// "ARANGODB_SESSION_ID" : "0cwuzusd23nw3qiwui84uwqwqw23e"
// },
//
// "requestType" : "GET",
// "requestBody" : "... only for PUT and POST ...",
// "user" : "authenticatedUser"
@ -489,6 +547,18 @@ static HttpResponse* ExecuteActionVocbase (TRI_vocbase_t* vocbase,
req->Set(v8g->ParametersKey, valuesObject);
// copy cookies
v8::Handle<v8::Object> cookiesObject = v8::Object::New();
map<string, string> const& cookies = request->cookieValues();
iter = cookies.begin();
for (; iter != cookies.end(); ++iter) {
cookiesObject->Set(v8::String::New(iter->first.c_str()), v8::String::New(iter->second.c_str()));
}
req->Set(v8g->CookiesKey, cookiesObject);
// execute the callback
v8::Handle<v8::Object> res = v8::Object::New();
v8::Handle<v8::Value> args[2] = { req, res };
@ -599,6 +669,30 @@ static HttpResponse* ExecuteActionVocbase (TRI_vocbase_t* vocbase,
}
}
// .............................................................................
// cookies
// .............................................................................
if (res->Has(v8g->CookiesKey)) {
v8::Handle<v8::Value> val = res->Get(v8g->CookiesKey);
v8::Handle<v8::Object> v8Cookies = val.As<v8::Object>();
if (v8Cookies->IsArray()) {
v8::Handle<v8::Array> v8Array = v8Cookies.As<v8::Array>();
for (uint32_t i = 0; i < v8Array->Length(); i++) {
v8::Handle<v8::Value> v8Cookie = v8Array->Get(i);
if (v8Cookie->IsObject()) {
addCookie(v8g, response, v8Cookie.As<v8::Object>());
}
}
}
else if (v8Cookies->IsObject()) {
// one cookie
addCookie(v8g, response, v8Cookies);
}
}
return response;
}
}
@ -774,6 +868,14 @@ void TRI_InitV8Actions (v8::Handle<v8::Context> context, ApplicationV8* applicat
v8g->BodyFromFileKey = v8::Persistent<v8::String>::New(TRI_V8_SYMBOL("bodyFromFile"));
v8g->ContentTypeKey = v8::Persistent<v8::String>::New(TRI_V8_SYMBOL("contentType"));
v8g->HeadersKey = v8::Persistent<v8::String>::New(TRI_V8_SYMBOL("headers"));
v8g->CookiesKey = v8::Persistent<v8::String>::New(TRI_V8_SYMBOL("cookies"));
v8g->CookieName = v8::Persistent<v8::String>::New(TRI_V8_SYMBOL("name"));
v8g->CookieValue = v8::Persistent<v8::String>::New(TRI_V8_SYMBOL("value"));
v8g->CookieLiveTime = v8::Persistent<v8::String>::New(TRI_V8_SYMBOL("liveTime"));
v8g->CookiePath = v8::Persistent<v8::String>::New(TRI_V8_SYMBOL("path"));
v8g->CookieDomain = v8::Persistent<v8::String>::New(TRI_V8_SYMBOL("domain"));
v8g->CookieSecure = v8::Persistent<v8::String>::New(TRI_V8_SYMBOL("secure"));
v8g->CookieHttpOnly = v8::Persistent<v8::String>::New(TRI_V8_SYMBOL("httpOnly"));
v8g->ParametersKey = v8::Persistent<v8::String>::New(TRI_V8_SYMBOL("parameters"));
v8g->PathKey = v8::Persistent<v8::String>::New(TRI_V8_SYMBOL("path"));
v8g->PrefixKey = v8::Persistent<v8::String>::New(TRI_V8_SYMBOL("prefix"));

View File

@ -1937,6 +1937,48 @@ function pathHandler (req, res, options, next) {
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief add a cookie
////////////////////////////////////////////////////////////////////////////////
function addCookie (res, name, value, liveTime, path, domain, secure, httpOnly) {
'use strict';
if (name === undefined) {
return;
}
if (value === undefined) {
return;
}
var cookie = {
'name' : name,
'value' : value
};
if (liveTime !== undefined && liveTime !== null) {
cookie.liveTime = parseInt(liveTime);
}
if (path !== undefined && path !== null) {
cookie.path = path;
}
if (domain !== undefined && domain !== null) {
cookie.path = domain;
}
if (secure !== undefined && secure !== null) {
cookie.secure = (secure) ? true : false;
}
if (httpOnly !== undefined && httpOnly !== null) {
cookie.httpOnly = (httpOnly) ? true : false;
}
if (res.cookies === undefined || res.cookies === null) {
res.cookies = [];
}
res.cookies.push(cookie);
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
@ -1959,6 +2001,7 @@ exports.reloadRouting = reloadRouting;
exports.firstRouting = firstRouting;
exports.nextRouting = nextRouting;
exports.routingCache = function() { return RoutingCache; };
exports.addCookie = addCookie;
// standard HTTP responses
exports.badParameter = badParameter;

View File

@ -66,6 +66,7 @@ HttpRequest::HttpRequest (char const* header, size_t length)
_headers(5),
_values(10),
_arrayValues(10),
_cookies(1),
_contentLength(0),
_body(0),
_bodySize(0),
@ -95,6 +96,7 @@ HttpRequest::HttpRequest ()
_headers(1),
_values(1),
_arrayValues(1),
_cookies(1),
_contentLength(0),
_body(0),
_bodySize(0),
@ -252,6 +254,30 @@ void HttpRequest::write (TRI_string_buffer_t* buffer) const {
TRI_AppendStringStringBuffer(buffer, value);
TRI_AppendString2StringBuffer(buffer, "\r\n", 2);
}
first = true;
for (_cookies.range(begin, end); begin < end; ++begin) {
char const* key = begin->_key;
if (key == 0) {
continue;
}
if (first) {
first = false;
TRI_AppendString2StringBuffer(buffer, "Cookie: ", 8);
}
else {
TRI_AppendString2StringBuffer(buffer, "; ", 2);
}
const size_t keyLength = strlen(key);
TRI_AppendString2StringBuffer(buffer, key, keyLength);
TRI_AppendString2StringBuffer(buffer, "=", 2);
char const* value = begin->_value;
TRI_AppendUrlEncodedStringStringBuffer(buffer, value);
}
TRI_AppendString2StringBuffer(buffer, "content-length: ", 16);
TRI_AppendInt64StringBuffer(buffer, _contentLength);
@ -441,6 +467,61 @@ map<string, vector<char const*>* > HttpRequest::arrayValues () const {
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
char const* HttpRequest::cookieValue (char const* key) const {
Dictionary<char const*>::KeyValue const* kv = _cookies.lookup(key);
if (kv == 0) {
return EMPTY_STR;
}
else {
return kv->_value;
}
}
////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
char const* HttpRequest::cookieValue (char const* key, bool& found) const {
Dictionary<char const*>::KeyValue const* kv = _cookies.lookup(key);
if (kv == 0) {
found = false;
return EMPTY_STR;
}
else {
found = true;
return kv->_value;
}
}
////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
map<string, string> HttpRequest::cookieValues () const {
basics::Dictionary<char const*>::KeyValue const* begin;
basics::Dictionary<char const*>::KeyValue const* end;
map<string, string> result;
for (_cookies.range(begin, end); begin < end; ++begin) {
char const* key = begin->_key;
if (key == 0) {
continue;
}
result[key] = begin->_value;
}
return result;
}
////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
char const* HttpRequest::body () const {
return _body == 0 ? EMPTY_STR : _body;
}
@ -480,9 +561,13 @@ void HttpRequest::setHeader (char const* key, size_t keyLength, char const* valu
_contentLength = TRI_Int64String(value);
}
else if (keyLength == 6 && memcmp(key, "cookie", keyLength) == 0) { // 6 = strlen("cookie")
parseCookies(value);
}
else {
_headers.insert(key, keyLength, value);
}
}
////////////////////////////////////////////////////////////////////////////////
@ -1206,6 +1291,128 @@ void HttpRequest::setArrayValue (char* key, size_t length, char const* value) {
v->push_back(value);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief set cookie
////////////////////////////////////////////////////////////////////////////////
void HttpRequest::setCookie (char* key, size_t length, char const* value) {
_cookies.insert(key, length, value);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief parse value of a cookie header field
////////////////////////////////////////////////////////////////////////////////
void HttpRequest::parseCookies (const char* buffer) {
char* keyBegin = 0;
char* key = 0;
char* valueBegin = 0;
char* value = 0;
enum { KEY, VALUE } phase = KEY;
enum { NORMAL, HEX1, HEX2 } reader = NORMAL;
int hex = 0;
char const AMB = ';';
char const EQUAL = '=';
char const PERCENT = '%';
char const SPACE = ' ';
char* buffer2 = (char*) buffer;
char* end = buffer2 + strlen(buffer);
for (keyBegin = key = buffer2; buffer2 < end; buffer2++) {
char next = *buffer2;
if (phase == KEY && next == EQUAL) {
phase = VALUE;
valueBegin = value = buffer2 + 1;
continue;
}
else if (next == AMB) {
phase = KEY;
*key = '\0';
// check for missing value phase
if (valueBegin == 0) {
valueBegin = value = key;
}
else {
*value = '\0';
}
setCookie(keyBegin, key - keyBegin, valueBegin);
//keyBegin = key = buffer2 + 1;
while ( *(keyBegin = key = buffer2 + 1) == SPACE && buffer2 < end) {
buffer2++;
}
valueBegin = value = 0;
continue;
}
else if (next == PERCENT) {
reader = HEX1;
continue;
}
else if (reader == HEX1) {
int h1 = StringUtils::hex2int(next, -1);
if (h1 == -1) {
reader = NORMAL;
--buffer2;
continue;
}
hex = h1 * 16;
reader = HEX2;
continue;
}
else if (reader == HEX2) {
int h1 = StringUtils::hex2int(next, -1);
if (h1 == -1) {
--buffer2;
}
else {
hex += h1;
}
reader = NORMAL;
next = static_cast<char>(hex);
}
if (phase == KEY) {
*key++ = next;
}
else {
*value++ = next;
}
}
if (keyBegin != key) {
*key = '\0';
// check for missing value phase
if (valueBegin == 0) {
valueBegin = value = key;
}
else {
*value = '\0';
}
setCookie(keyBegin, key - keyBegin, valueBegin);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////

View File

@ -405,6 +405,29 @@ namespace triagens {
map<string, vector<char const*>* > arrayValues () const;
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the value of a cookie
///
/// Returns the value of a cookie.
////////////////////////////////////////////////////////////////////////////////
char const* cookieValue (char const* key) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the value of a cookie
///
/// Returns the value of a cookie. found is true if the client specified the key.
////////////////////////////////////////////////////////////////////////////////
char const* cookieValue (char const* key, bool& found) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief returns all cookies
///
/// Returns all key/value pairs of the request.
////////////////////////////////////////////////////////////////////////////////
map<string, string > cookieValues () const;
////////////////////////////////////////////////////////////////////////////////
/// @}
@ -536,6 +559,18 @@ namespace triagens {
void setArrayValue (char* key, size_t length, char const* value);
////////////////////////////////////////////////////////////////////////////////
/// @brief set cookie
////////////////////////////////////////////////////////////////////////////////
void setCookie (char* key, size_t length, char const* value);
////////////////////////////////////////////////////////////////////////////////
/// @brief parse value of a cookie header field
////////////////////////////////////////////////////////////////////////////////
void parseCookies (const char* buffer);
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
@ -575,6 +610,12 @@ namespace triagens {
basics::Dictionary< vector<char const*>* > _arrayValues;
////////////////////////////////////////////////////////////////////////////////
/// @brief cookies
////////////////////////////////////////////////////////////////////////////////
basics::Dictionary<char const*> _cookies;
////////////////////////////////////////////////////////////////////////////////
/// @brief content length
////////////////////////////////////////////////////////////////////////////////

View File

@ -495,6 +495,66 @@ void HttpResponse::setHeaders (string const& headers, bool includeLine0) {
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief swaps data
////////////////////////////////////////////////////////////////////////////////
void HttpResponse::setCookie (string const& name, string const& value,
int lifeTimeSeconds, string const& path, string const& domain,
bool secure, bool httpOnly) {
triagens::basics::StringBuffer* buffer = new triagens::basics::StringBuffer(TRI_UNKNOWN_MEM_ZONE);
string tmp = StringUtils::trim(name);
buffer->appendText(tmp.c_str(), tmp.length());
buffer->appendChar('=');
tmp = StringUtils::urlEncode(value);
buffer->appendText(tmp.c_str(), tmp.length());
if (lifeTimeSeconds > 0) {
time_t rawtime;
struct tm * timeinfo;
char buffer2 [80];
time(&rawtime);
rawtime += lifeTimeSeconds;
if (rawtime > 0) {
timeinfo = gmtime(&rawtime);
strftime(buffer2, 80, "%a, %d-%b-%Y %H:%M:%S %Z", timeinfo);
buffer->appendText("; expires=");
buffer->appendText(buffer2);
}
}
if (path != "") {
buffer->appendText("; path=");
buffer->appendText(path);
}
if (domain != "") {
buffer->appendText("; domain=");
buffer->appendText(domain);
}
if (secure) {
buffer->appendText("; secure");
}
if (httpOnly) {
buffer->appendText("; HttpOnly");
}
char const* l = StringUtils::duplicate(buffer->c_str());
buffer->clear();
free(buffer);
_cookies.push_back(l);
_freeables.push_back(l);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief swaps data
////////////////////////////////////////////////////////////////////////////////
@ -560,6 +620,13 @@ void HttpResponse::writeHeader (StringBuffer* output) {
output->appendText("\r\n", 2);
}
for (vector<char const*>::iterator iter = _cookies.begin();
iter != _cookies.end(); ++iter) {
output->appendText("Set-Cookie: ", 12);
output->appendText(*iter);
output->appendText("\r\n", 2);
}
if (seenTransferEncoding && transferEncoding == "chunked") {
output->appendText("transfer-encoding: chunked\r\n\r\n", 30);
}

View File

@ -310,6 +310,16 @@ namespace triagens {
void setHeaders (string const& headers, bool includeLine0);
////////////////////////////////////////////////////////////////////////////////
/// @brief sets a cookie
///
/// The name is automatically trimmed.
////////////////////////////////////////////////////////////////////////////////
void setCookie (string const& name, string const& value,
int lifeTimeSeconds, string const& path, string const& domain,
bool secure, bool httpOnly);
////////////////////////////////////////////////////////////////////////////////
/// @brief swaps data
////////////////////////////////////////////////////////////////////////////////
@ -378,6 +388,13 @@ namespace triagens {
basics::Dictionary<char const*> _headers;
////////////////////////////////////////////////////////////////////////////////
/// @brief cookies
////////////////////////////////////////////////////////////////////////////////
vector<char const*> _cookies;
////////////////////////////////////////////////////////////////////////////////
/// @brief body
////////////////////////////////////////////////////////////////////////////////