*Skylark: fixed cookies in redirect

git-svn-id: svn://ultimatepp.org/upp/trunk@5190 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
cxl 2012-07-14 18:14:25 +00:00
parent f18d29f38f
commit 21305f6f70
5 changed files with 995 additions and 998 deletions

View file

@ -1,402 +1,401 @@
#include "Skylark.h"
#define LLOG(x) // DLOG(x)
namespace Upp {
Value Raw(const String& s)
{
RawHtmlText r;
r.text = s;
return RawToValue(r);
}
VectorMap<String, Value (*)(const Vector<Value>&, const Renderer *)>& Compiler::functions()
{
static VectorMap<String, Value (*)(const Vector<Value>&, const Renderer *)> x;
return x;
}
void Compiler::Register(const String& id, Value (*fn)(const Vector<Value>&, const Renderer *))
{
functions().GetAdd(id) = fn;
}
int Compiler::ForVar(String id, int i)
{
if(i + 1 < var.GetCount() && forvar[i])
return i + 1;
p.ThrowError(id + " is not 'for' iterator");
return 0;
}
int CountLinkArgs(const Vector<String>& part)
{
int args = 0;
for(int i = 0; i < part.GetCount(); i++) {
int p = (byte)*part[i];
if(p >= 0 && p < 30)
args = max(args, p + 1);
}
return args;
}
One<Exe> Compiler::Prim()
{
One<Exe> result;
DDUMP(p.GetPtr());
if(p.Char('!'))
result = Create<ExeNot>(Prim());
else
if(p.Char('-'))
result = Create<ExeNeg>(Prim());
else
if(p.Char('+'))
result = Prim();
else
if(p.IsId() || p.IsChar('.') || p.IsChar('@')) {
String id;
if(p.Char('.'))
id = ".";
else
if(p.Char('@'))
id = "@";
id << p.ReadId();
int n = var.Find(id);
if(p.Char('(')) {
Value (*f)(const Vector<Value>&, const Renderer *) = functions().Get(id, NULL);
if(!f) {
Vector<String> *part = GetUrlViewLinkParts(id);
if(!part)
p.ThrowError("function nor link not found '" + id + "'");
ExeLink& ln = result.Create<ExeLink>();
ln.part = part;
if(!p.Char(')')) {
do
ln.arg.AddPick(Exp());
while(p.Char(','));
p.PassChar(')');
}
if(CountLinkArgs(*part) > ln.arg.GetCount())
p.ThrowError("invalid number of link arguments '" + id + "'");
}
else {
ExeFn& fn = result.Create<ExeFn>();
fn.fn = f;
if(!p.Char(')')) {
do
fn.arg.AddPick(Exp());
while(p.Char(','));
p.PassChar(')');
}
while(p.Char('.')) {
One<Exe> r;
ExeField& f = r.Create<ExeField>();
f.value = result;
f.id = p.ReadId();
result = r;
}
}
return result;
}
if(n < 0) {
Vector<String> *part = GetUrlViewLinkParts(id);
ExeConst& c = result.Create<ExeConst>();
if(!part) {
return result;
}
String l = "\"/";
for(int i = 0; i < (*part).GetCount(); i++) {
if(i)
l << '/';
l << UrlEncode((*part)[i]);
}
l << '\"';
c.value = Raw(l);
}
else
if(p.Char('.')) {
if(p.Id("_first"))
result.Create<ExeFirst>().var_index = ForVar(id, n);
else
if(p.Id("_last"))
result.Create<ExeLast>().var_index = ForVar(id, n);
else
if(p.Id("_index"))
result.Create<ExeIndex>().var_index = ForVar(id, n);
else
if(p.Id("_key"))
result.Create<ExeKey>().var_index = ForVar(id, n);
else {
result.Create<ExeVar>().var_index = n;
do {
One<Exe> r;
ExeField& f = r.Create<ExeField>();
f.value = result;
f.id = p.ReadId();
result = r;
}
while(p.Char('.'));
}
}
else
result.Create<ExeVar>().var_index = n;
}
else
if(p.Char('{')) {
ExeMap& m = result.Create<ExeMap>();
do {
m.key.AddPick(Exp());
p.PassChar(':');
m.value.AddPick(Exp());
}
while(p.Char(','));
p.PassChar('}');
}
else
if(p.Char('[')) {
ExeArray& m = result.Create<ExeArray>();
do {
m.item.AddPick(Exp());
}
while(p.Char(','));
p.PassChar(']');
}
else
if(p.Char('(')) {
result = Exp();
p.PassChar(')');
}
else {
ExeConst& c = result.Create<ExeConst>();
if(p.Char2('0', 'x') || p.Char2('0', 'X'))
c.value = (int)p.ReadNumber(16);
else
if(p.Char('0'))
c.value = int(p.IsNumber() ? p.ReadNumber(8) : 0);
else
c.value = p.IsString() ? Value(p.ReadString()) : Value(p.ReadDouble());
}
return result;
}
One<Exe> Compiler::Mul()
{
One<Exe> result = Prim();
for(;;)
if(p.Char('*'))
result = Create<ExeMul>(result, Prim());
else
if(p.Char('/'))
result = Create<ExeDiv>(result, Prim());
else
if(p.Char('%'))
result = Create<ExeMod>(result, Prim());
else
return result;
}
One<Exe> Compiler::Add()
{
One<Exe> result = Mul();
for(;;)
if(p.Char('+'))
result = Create<ExeAdd>(result, Mul());
else
if(p.Char('-'))
result = Create<ExeSub>(result, Mul());
else
return result;
}
One<Exe> Compiler::Shift()
{
One<Exe> result = Add();
for(;;)
if(p.Char3('>', '>', '>'))
result = Create<ExeSrl>(result, Add());
else
if(p.Char2('>', '>'))
result = Create<ExeSra>(result, Add());
else
if(p.Char2('<', '<'))
result = Create<ExeSll>(result, Add());
else
return result;
}
One<Exe> Compiler::Rel()
{
One<Exe> result = Shift();
for(;;)
if(p.Char2('<', '='))
result = Create<ExeLte>(result, Shift());
else
if(p.Char2('>', '='))
result = Create<ExeLte>(Shift(), result);
else
if(p.Char('<'))
result = Create<ExeLt>(result, Shift());
else
if(p.Char('>'))
result = Create<ExeLt>(Shift(), result);
else
return result;
}
One<Exe> Compiler::Eq()
{
One<Exe> result = Rel();
for(;;)
if(p.Char2('=', '='))
result = Create<ExeEq>(result, Rel());
else
if(p.Char2('!', '='))
result = Create<ExeNeq>(Rel(), result);
else
return result;
}
One<Exe> Compiler::And()
{
One<Exe> result = Eq();
while(!p.IsChar2('&', '&') && p.Char('&'))
result = Create<ExeAnd>(result, Eq());
return result;
}
One<Exe> Compiler::Xor()
{
One<Exe> result = And();
while(p.Char('^'))
result = Create<ExeXor>(result, And());
return result;
}
One<Exe> Compiler::Or()
{
One<Exe> result = Xor();
while(!p.IsChar2('|', '|') && p.Char('|'))
result = Create<ExeOr>(result, Xor());
return result;
}
One<Exe> Compiler::LogAnd()
{
One<Exe> result = Or();
while(p.Char2('&', '&'))
result = Create<ExeAnl>(result, Or());
return result;
}
One<Exe> Compiler::LogOr()
{
One<Exe> result = LogAnd();
while(p.Char2('|', '|'))
result = Create<ExeOrl>(result, LogAnd());
return result;
}
One<Exe> Compiler::Conditional()
{
One<Exe> result = LogOr();
if(p.Char('?')) {
One<Exe> r;
ExeCond& c = r.Create<ExeCond>();
c.cond = result;
c.ontrue = LogOr();
p.PassChar(':');
c.onfalse = LogOr();
result = r;
}
return result;
}
One<Exe> Compiler::Exp()
{
return Conditional();
}
void Compiler::ExeBlock::AddText(const char *b, const char *s)
{
if(s > b) {
RawHtmlText t;
t.text = String(b, s);
item.Add().Create<ExeConst>().value = RawToValue(t);
}
}
One<Exe> Compiler::Block()
{
One<Exe> result;
ExeBlock& blk = result.Create<ExeBlock>();
const char *s = p.GetSpacePtr();
const char *b = s;
int line = 1;
while(*s) {
if(*s == '$') {
if(s[1] == '$')
s += 2;
else {
blk.AddText(b, s);
p.Set(s + 1, NULL, line);
if(p.Id("if")) {
ExeCond& c = blk.item.Add().Create<ExeCond>();
p.PassChar('(');
c.cond = Exp();
p.PassChar(')');
c.ontrue = Block();
if(p.Id("else"))
c.onfalse = Block();
if(!p.Char('/'))
p.PassId("endif");
}
else
if(p.Id("for")) {
ExeFor& c = blk.item.Add().Create<ExeFor>();
p.PassChar('(');
int q = var.GetCount();
var.Add(p.ReadId());
var.Add(Null); // LoopInfo placeholder
forvar.Add(true);
forvar.Add(true);
p.PassId("in");
c.value = Exp();
p.PassChar(')');
c.body = Block();
var.Trim(q);
forvar.SetCount(q);
if(p.Id("else"))
c.onempty = Block();
if(!p.Char('/'))
p.PassId("endfor");
}
else
if(p.IsId("else") || p.IsId("endif") || p.IsId("endfor") || p.IsChar('/'))
return result;
else
blk.item.AddPick(Prim());
b = s = p.GetSpacePtr();
line = p.GetLine();
}
}
else
if(*s++ == '\n')
line++;
}
blk.AddText(b, s);
p.Set(s, NULL, line);
return result;
}
One<Exe> Compile(const char *code, const Index<String>& vars)
{
Compiler c(code, vars);
One<Exe> exe = c.Block();
LLOG("Before optimization node count: " << c.GetNodeCount(exe));
c.Optimize(exe);
LLOG("After optimization node count: " << c.GetNodeCount(exe));
return exe;
}
#include "Skylark.h"
#define LLOG(x) // DLOG(x)
namespace Upp {
Value Raw(const String& s)
{
RawHtmlText r;
r.text = s;
return RawToValue(r);
}
VectorMap<String, Value (*)(const Vector<Value>&, const Renderer *)>& Compiler::functions()
{
static VectorMap<String, Value (*)(const Vector<Value>&, const Renderer *)> x;
return x;
}
void Compiler::Register(const String& id, Value (*fn)(const Vector<Value>&, const Renderer *))
{
functions().GetAdd(id) = fn;
}
int Compiler::ForVar(String id, int i)
{
if(i + 1 < var.GetCount() && forvar[i])
return i + 1;
p.ThrowError(id + " is not 'for' iterator");
return 0;
}
int CountLinkArgs(const Vector<String>& part)
{
int args = 0;
for(int i = 0; i < part.GetCount(); i++) {
int p = (byte)*part[i];
if(p >= 0 && p < 30)
args = max(args, p + 1);
}
return args;
}
One<Exe> Compiler::Prim()
{
One<Exe> result;
if(p.Char('!'))
result = Create<ExeNot>(Prim());
else
if(p.Char('-'))
result = Create<ExeNeg>(Prim());
else
if(p.Char('+'))
result = Prim();
else
if(p.IsId() || p.IsChar('.') || p.IsChar('@')) {
String id;
if(p.Char('.'))
id = ".";
else
if(p.Char('@'))
id = "@";
id << p.ReadId();
int n = var.Find(id);
if(p.Char('(')) {
Value (*f)(const Vector<Value>&, const Renderer *) = functions().Get(id, NULL);
if(!f) {
Vector<String> *part = GetUrlViewLinkParts(id);
if(!part)
p.ThrowError("function nor link not found '" + id + "'");
ExeLink& ln = result.Create<ExeLink>();
ln.part = part;
if(!p.Char(')')) {
do
ln.arg.AddPick(Exp());
while(p.Char(','));
p.PassChar(')');
}
if(CountLinkArgs(*part) > ln.arg.GetCount())
p.ThrowError("invalid number of link arguments '" + id + "'");
}
else {
ExeFn& fn = result.Create<ExeFn>();
fn.fn = f;
if(!p.Char(')')) {
do
fn.arg.AddPick(Exp());
while(p.Char(','));
p.PassChar(')');
}
while(p.Char('.')) {
One<Exe> r;
ExeField& f = r.Create<ExeField>();
f.value = result;
f.id = p.ReadId();
result = r;
}
}
return result;
}
if(n < 0) {
Vector<String> *part = GetUrlViewLinkParts(id);
ExeConst& c = result.Create<ExeConst>();
if(!part) {
return result;
}
String l = "\"/";
for(int i = 0; i < (*part).GetCount(); i++) {
if(i)
l << '/';
l << UrlEncode((*part)[i]);
}
l << '\"';
c.value = Raw(l);
}
else
if(p.Char('.')) {
if(p.Id("_first"))
result.Create<ExeFirst>().var_index = ForVar(id, n);
else
if(p.Id("_last"))
result.Create<ExeLast>().var_index = ForVar(id, n);
else
if(p.Id("_index"))
result.Create<ExeIndex>().var_index = ForVar(id, n);
else
if(p.Id("_key"))
result.Create<ExeKey>().var_index = ForVar(id, n);
else {
result.Create<ExeVar>().var_index = n;
do {
One<Exe> r;
ExeField& f = r.Create<ExeField>();
f.value = result;
f.id = p.ReadId();
result = r;
}
while(p.Char('.'));
}
}
else
result.Create<ExeVar>().var_index = n;
}
else
if(p.Char('{')) {
ExeMap& m = result.Create<ExeMap>();
do {
m.key.AddPick(Exp());
p.PassChar(':');
m.value.AddPick(Exp());
}
while(p.Char(','));
p.PassChar('}');
}
else
if(p.Char('[')) {
ExeArray& m = result.Create<ExeArray>();
do {
m.item.AddPick(Exp());
}
while(p.Char(','));
p.PassChar(']');
}
else
if(p.Char('(')) {
result = Exp();
p.PassChar(')');
}
else {
ExeConst& c = result.Create<ExeConst>();
if(p.Char2('0', 'x') || p.Char2('0', 'X'))
c.value = (int)p.ReadNumber(16);
else
if(p.Char('0'))
c.value = int(p.IsNumber() ? p.ReadNumber(8) : 0);
else
c.value = p.IsString() ? Value(p.ReadString()) : Value(p.ReadDouble());
}
return result;
}
One<Exe> Compiler::Mul()
{
One<Exe> result = Prim();
for(;;)
if(p.Char('*'))
result = Create<ExeMul>(result, Prim());
else
if(p.Char('/'))
result = Create<ExeDiv>(result, Prim());
else
if(p.Char('%'))
result = Create<ExeMod>(result, Prim());
else
return result;
}
One<Exe> Compiler::Add()
{
One<Exe> result = Mul();
for(;;)
if(p.Char('+'))
result = Create<ExeAdd>(result, Mul());
else
if(p.Char('-'))
result = Create<ExeSub>(result, Mul());
else
return result;
}
One<Exe> Compiler::Shift()
{
One<Exe> result = Add();
for(;;)
if(p.Char3('>', '>', '>'))
result = Create<ExeSrl>(result, Add());
else
if(p.Char2('>', '>'))
result = Create<ExeSra>(result, Add());
else
if(p.Char2('<', '<'))
result = Create<ExeSll>(result, Add());
else
return result;
}
One<Exe> Compiler::Rel()
{
One<Exe> result = Shift();
for(;;)
if(p.Char2('<', '='))
result = Create<ExeLte>(result, Shift());
else
if(p.Char2('>', '='))
result = Create<ExeLte>(Shift(), result);
else
if(p.Char('<'))
result = Create<ExeLt>(result, Shift());
else
if(p.Char('>'))
result = Create<ExeLt>(Shift(), result);
else
return result;
}
One<Exe> Compiler::Eq()
{
One<Exe> result = Rel();
for(;;)
if(p.Char2('=', '='))
result = Create<ExeEq>(result, Rel());
else
if(p.Char2('!', '='))
result = Create<ExeNeq>(Rel(), result);
else
return result;
}
One<Exe> Compiler::And()
{
One<Exe> result = Eq();
while(!p.IsChar2('&', '&') && p.Char('&'))
result = Create<ExeAnd>(result, Eq());
return result;
}
One<Exe> Compiler::Xor()
{
One<Exe> result = And();
while(p.Char('^'))
result = Create<ExeXor>(result, And());
return result;
}
One<Exe> Compiler::Or()
{
One<Exe> result = Xor();
while(!p.IsChar2('|', '|') && p.Char('|'))
result = Create<ExeOr>(result, Xor());
return result;
}
One<Exe> Compiler::LogAnd()
{
One<Exe> result = Or();
while(p.Char2('&', '&'))
result = Create<ExeAnl>(result, Or());
return result;
}
One<Exe> Compiler::LogOr()
{
One<Exe> result = LogAnd();
while(p.Char2('|', '|'))
result = Create<ExeOrl>(result, LogAnd());
return result;
}
One<Exe> Compiler::Conditional()
{
One<Exe> result = LogOr();
if(p.Char('?')) {
One<Exe> r;
ExeCond& c = r.Create<ExeCond>();
c.cond = result;
c.ontrue = LogOr();
p.PassChar(':');
c.onfalse = LogOr();
result = r;
}
return result;
}
One<Exe> Compiler::Exp()
{
return Conditional();
}
void Compiler::ExeBlock::AddText(const char *b, const char *s)
{
if(s > b) {
RawHtmlText t;
t.text = String(b, s);
item.Add().Create<ExeConst>().value = RawToValue(t);
}
}
One<Exe> Compiler::Block()
{
One<Exe> result;
ExeBlock& blk = result.Create<ExeBlock>();
const char *s = p.GetSpacePtr();
const char *b = s;
int line = 1;
while(*s) {
if(*s == '$') {
if(s[1] == '$')
s += 2;
else {
blk.AddText(b, s);
p.Set(s + 1, NULL, line);
if(p.Id("if")) {
ExeCond& c = blk.item.Add().Create<ExeCond>();
p.PassChar('(');
c.cond = Exp();
p.PassChar(')');
c.ontrue = Block();
if(p.Id("else"))
c.onfalse = Block();
if(!p.Char('/'))
p.PassId("endif");
}
else
if(p.Id("for")) {
ExeFor& c = blk.item.Add().Create<ExeFor>();
p.PassChar('(');
int q = var.GetCount();
var.Add(p.ReadId());
var.Add(Null); // LoopInfo placeholder
forvar.Add(true);
forvar.Add(true);
p.PassId("in");
c.value = Exp();
p.PassChar(')');
c.body = Block();
var.Trim(q);
forvar.SetCount(q);
if(p.Id("else"))
c.onempty = Block();
if(!p.Char('/'))
p.PassId("endfor");
}
else
if(p.IsId("else") || p.IsId("endif") || p.IsId("endfor") || p.IsChar('/'))
return result;
else
blk.item.AddPick(Prim());
b = s = p.GetSpacePtr();
line = p.GetLine();
}
}
else
if(*s++ == '\n')
line++;
}
blk.AddText(b, s);
p.Set(s, NULL, line);
return result;
}
One<Exe> Compile(const char *code, const Index<String>& vars)
{
Compiler c(code, vars);
One<Exe> exe = c.Block();
LLOG("Before optimization node count: " << c.GetNodeCount(exe));
c.Optimize(exe);
LLOG("After optimization node count: " << c.GetNodeCount(exe));
return exe;
}
};

View file

@ -380,6 +380,8 @@ void Http::Dispatch(TcpSocket& socket)
SKYLARKLOG("Redirect to: " << redirect);
r << "HTTP/1.1 " << code << " Found\r\n";
r << "Location: " << redirect << "\r\n";
for(int i = 0; i < cookies.GetCount(); i++)
r << cookies[i];
}
else {
r <<
@ -390,8 +392,6 @@ void Http::Dispatch(TcpSocket& socket)
"Connection: close\r\n"
"Cache-Control: no-cache\r\n"
"Content-Type: " << content_type << "\r\n";
for(int i = 0; i < cookies.GetCount(); i++)
r << cookies[i];
r << "\r\n";
}
socket.PutAll(r);

View file

@ -1,350 +1,350 @@
#include "Skylark.h"
#define LLOG(x) LOG(x)
#define LTIMING(x) RTIMING(x)
namespace Upp {
Http::Http(SkylarkApp& app)
: app(app)
{
code = 200;
content_type = "text/html; charset=UTF-8";
session_dirty = false;
lang = LNG_ENGLISH;
}
void Http::ParseRequest(const char *p)
{
while(*p) {
const char *last = p;
while(*p && *p != '=' && *p != '&')
p++;
String key = UrlDecode(last, p);
if(*p == '=')
p++;
last = p;
while(*p && *p != '&')
p++;
if(*key != '.' && *key != '@')
var.GetAdd(key) = UrlDecode(last, p);
if(*p)
p++;
}
}
String HttpResponse(int code, const char *phrase, const String& data, const char *content_type,
const char *cookies)
{
String r;
r <<
"HTTP/1.0 " << code << ' ' << phrase << "\r\n"
"Date: " << WwwFormat(GetUtcTime()) << "\r\n"
"Server: U++\r\n"
"Content-Length: " << data.GetCount() << "\r\n"
"Connection: close\r\n"
<< cookies;
if(content_type)
r << "Content-Type: " << content_type << "\r\n";
r << "\r\n" << data;
return r;
}
Http& Http::SetRawCookie(const char *id, const String& value, Time expires,
const char *path, const char *domain, bool secure,
bool httponly)
{
if(*id == '.')
id++;
var.GetAdd('@' + id) = value;
String& c = cookies.GetAdd(id);
c.Clear();
c << "Set-Cookie:" << ' ' << id << '=' << value;
if(!IsNull(expires))
c << "; " << WwwFormat(expires);
c << "; Path=" << (path && *path ? path : "/");
if(domain && *domain)
c << "; Domain=" << domain;
if(secure)
c << "; Secure";
if(httponly)
c << "; HttpOnly";
c << "\r\n";
return *this;
}
int Http::Int(const char *id) const
{
Value v = operator[](id);
if(v.Is<int>())
return v;
if(IsString(v))
return ScanInt((String)v);
if(IsNull(v))
return Null;
if(IsNumber(v)) {
double d = v;
if(d > INT_MIN && d <= INT_MAX)
return (int)d;
}
return Null;
}
int Http::Int(int i) const
{
return ScanInt(operator[](i));
}
String HttpAsString(const Value& v)
{
if(v.Is<RawHtmlText>())
return v.To<RawHtmlText>().text;
return AsString(v);
}
Http& Http::Content(const char *s, const Value& data)
{
content_type = s;
response = HttpAsString(data);
return *this;
}
Http& Http::operator<<(const Value& s)
{
response << HttpAsString(s);
return *this;
}
Http& Http::SetCookie(const char *id, const String& value, Time expires,
const char *path, const char *domain, bool secure, bool httponly)
{
return SetRawCookie(id, UrlEncode(value), expires, path, domain, secure);
}
void Http::ReadMultiPart(const String& buffer)
{
const char *p = buffer;
while(p[0] != '-' || p[1] != '-') {
while(*p != '\n')
if(*p++ == 0)
return; // end of file, boundary not found
p++;
}
String delimiter;
{ // read multipart delimiter
const char *b = (p += 2);
while(*p && *p++ != '\n')
;
const char *e = p;
while(e > b && (byte)e[-1] <= ' ')
e--;
delimiter = String(b, e);
}
int delta = 4 + delimiter.GetLength();
const char *e = buffer.End();
if(e - p < delta)
return;
e -= delta;
while(p < e) { // read individual parts
String filename, content_type, name;
while(!MemICmp(p, "content-", 8)) { // parse content specifiers
p += 8;
if(!MemICmp(p, "disposition:", 12)) {
p += 12;
while(*p && *p != '\n')
if((byte)*p <= ' ')
p++;
else { // fetch key-value pair
const char *kp = p;
while(*p && *p != '\n' && *p != '=' && *p != ';')
p++;
const char *ke = p;
String value;
if(*p == '=') {
const char *b = ++p;
if(*p == '\"') { // quoted value
b++;
while(*++p && *p != '\n' && *p != '\"')
;
value = String(b, p);
if(*p == '\"')
p++;
}
else {
while(*p && *p != '\n' && *p != ';')
p++;
value = String(b, p);
}
}
if(ke - kp == 4 && !MemICmp(kp, "name", 4))
name = value;
else if(ke - kp == 8 && !MemICmp(kp, "filename", 8))
filename = value;
if(*p == ';')
p++;
}
}
else if(!MemICmp(p, "type:", 5)) {
p += 5;
while(*p && *p != '\n' && (byte)*p <= ' ')
p++;
const char *b = p;
while(*p && *p != '\n')
p++;
const char *e = p;
while(e > b && (byte)e[-1] <= ' ')
e--;
content_type = String(b, e);
}
;
while(*p && *p++ != '\n')
;
}
if(*p++ != '\r' || *p++ != '\n')
return;
const char *b = p;
while(p < e) {
p = (const char *)memchr(p, '\r', e - p);
if(!p)
return;
if(p[0] == '\r' && p[1] == '\n' && p[2] == '-' && p[3] == '-'
&& !memcmp(p + 4, delimiter, delimiter.GetLength()))
break;
p++;
}
if(!name.IsEmpty() && *name != '.' && *name != '@') { // add variables
if(!filename.IsEmpty())
var.GetAdd(name + ".filename") = filename;
if(!content_type.IsEmpty())
var.GetAdd(name + ".content_type") = content_type;
var.Add(name, String(b, p));
}
p += delta;
while(*p && *p++ != '\n')
;
}
}
static const char hex_digits[] = "0123456789ABCDEF";
void UrlEncode(StringBuffer& out, const String& s)
{
static bool ok[256];
ONCELOCK {
for(int ch = 0; ch < 256; ch++)
ok[ch] = IsAlNum(ch) || ch == ',' || ch == '.' || ch == '-' || ch == '_';
}
const char *p = s, *e = s.End();
for(; p < e; p++)
{
const char *b = p;
while(p < e && ok[(byte)*p])
p++;
if(p > b)
out.Cat(b, int(p - b));
if(p >= e)
break;
if(*p == ' ')
out << '+';
else
out << '%' << hex_digits[(*p >> 4) & 15] << hex_digits[*p & 15];
}
}
void MakeLink(StringBuffer& out, const Vector<String>& part, const Vector<Value>& arg)
{
LTIMING("MakeLink");
out.Cat("/");
for(int i = 0; i < part.GetCount(); i++) {
const String& p = part[i];
if(i)
out << '/';
int q = (byte)*p;
if(q < 32) {
if(q >= 0 && q < arg.GetCount())
UrlEncode(out, AsString(arg[q]));
}
else
UrlEncode(out, p);
}
bool get = false;
for(int i = 0; i < arg.GetCount(); i++)
if(IsValueMap(arg[i])) {
if(get)
out << '&';
else
out << '?';
get = true;
ValueMap m = arg[i];
for(int i = 0; i < m.GetCount(); i++) {
if(i)
out << '&';
UrlEncode(out, AsString(m.GetKeys()[i]));
out << '=';
UrlEncode(out, AsString(m.GetValues()[i]));
}
}
}
Http& Http::RenderResult(const char *template_name)
{
LTIMING("Render");
response << UPP::Render(GetTemplate(template_name), this, var.GetValues());
return *this;
}
Http& Http::Redirect(void (*view)(Http&), const Vector<Value>& arg)
{
Redirect(MakeLink(view, arg));
return *this;
}
Http& Http::Redirect(void (*view)(Http&))
{
Vector<Value> arg;
Redirect(view, arg);
return *this;
}
Http& Http::Redirect(void (*view)(Http&), const Value& v1)
{
Vector<Value> arg;
arg.Add(v1);
Redirect(view, arg);
return *this;
}
Http& Http::Redirect(void (*view)(Http&), const Value& v1, const Value& v2)
{
Vector<Value> arg;
arg.Add(v1);
arg.Add(v2);
Redirect(view, arg);
return *this;
}
Http& Http::Ux(const char *id, const String& text)
{
if(response.GetCount())
response << '\1';
response << id << ':' << text;
return *this;
}
Http& Http::UxRender(const char *id, const char *template_name)
{
return Ux(id, RenderString(template_name));
}
Http& Http::UxSet(const char *id, const String& value)
{
return Ux(String(">") + id, value);
}
Http& Http::UxRun(const String& js_code)
{
return Ux("!", js_code);
}
#include "Skylark.h"
#define LLOG(x) // DLOG(x)
#define LTIMING(x) // RTIMING(x)
namespace Upp {
Http::Http(SkylarkApp& app)
: app(app)
{
code = 200;
content_type = "text/html; charset=UTF-8";
session_dirty = false;
lang = LNG_ENGLISH;
}
void Http::ParseRequest(const char *p)
{
while(*p) {
const char *last = p;
while(*p && *p != '=' && *p != '&')
p++;
String key = UrlDecode(last, p);
if(*p == '=')
p++;
last = p;
while(*p && *p != '&')
p++;
if(*key != '.' && *key != '@')
var.GetAdd(key) = UrlDecode(last, p);
if(*p)
p++;
}
}
String HttpResponse(int code, const char *phrase, const String& data, const char *content_type,
const char *cookies)
{
String r;
r <<
"HTTP/1.0 " << code << ' ' << phrase << "\r\n"
"Date: " << WwwFormat(GetUtcTime()) << "\r\n"
"Server: U++\r\n"
"Content-Length: " << data.GetCount() << "\r\n"
"Connection: close\r\n"
<< cookies;
if(content_type)
r << "Content-Type: " << content_type << "\r\n";
r << "\r\n" << data;
return r;
}
Http& Http::SetRawCookie(const char *id, const String& value, Time expires,
const char *path, const char *domain, bool secure,
bool httponly)
{
if(*id == '.')
id++;
var.GetAdd('@' + id) = value;
String& c = cookies.GetAdd(id);
c.Clear();
c << "Set-Cookie:" << ' ' << id << '=' << value;
if(!IsNull(expires))
c << "; " << WwwFormat(expires);
c << "; Path=" << (path && *path ? path : "/");
if(domain && *domain)
c << "; Domain=" << domain;
if(secure)
c << "; Secure";
if(httponly)
c << "; HttpOnly";
c << "\r\n";
return *this;
}
int Http::Int(const char *id) const
{
Value v = operator[](id);
if(v.Is<int>())
return v;
if(IsString(v))
return ScanInt((String)v);
if(IsNull(v))
return Null;
if(IsNumber(v)) {
double d = v;
if(d > INT_MIN && d <= INT_MAX)
return (int)d;
}
return Null;
}
int Http::Int(int i) const
{
return ScanInt(operator[](i));
}
String HttpAsString(const Value& v)
{
if(v.Is<RawHtmlText>())
return v.To<RawHtmlText>().text;
return AsString(v);
}
Http& Http::Content(const char *s, const Value& data)
{
content_type = s;
response = HttpAsString(data);
return *this;
}
Http& Http::operator<<(const Value& s)
{
response << HttpAsString(s);
return *this;
}
Http& Http::SetCookie(const char *id, const String& value, Time expires,
const char *path, const char *domain, bool secure, bool httponly)
{
return SetRawCookie(id, UrlEncode(value), expires, path, domain, secure);
}
void Http::ReadMultiPart(const String& buffer)
{
const char *p = buffer;
while(p[0] != '-' || p[1] != '-') {
while(*p != '\n')
if(*p++ == 0)
return; // end of file, boundary not found
p++;
}
String delimiter;
{ // read multipart delimiter
const char *b = (p += 2);
while(*p && *p++ != '\n')
;
const char *e = p;
while(e > b && (byte)e[-1] <= ' ')
e--;
delimiter = String(b, e);
}
int delta = 4 + delimiter.GetLength();
const char *e = buffer.End();
if(e - p < delta)
return;
e -= delta;
while(p < e) { // read individual parts
String filename, content_type, name;
while(!MemICmp(p, "content-", 8)) { // parse content specifiers
p += 8;
if(!MemICmp(p, "disposition:", 12)) {
p += 12;
while(*p && *p != '\n')
if((byte)*p <= ' ')
p++;
else { // fetch key-value pair
const char *kp = p;
while(*p && *p != '\n' && *p != '=' && *p != ';')
p++;
const char *ke = p;
String value;
if(*p == '=') {
const char *b = ++p;
if(*p == '\"') { // quoted value
b++;
while(*++p && *p != '\n' && *p != '\"')
;
value = String(b, p);
if(*p == '\"')
p++;
}
else {
while(*p && *p != '\n' && *p != ';')
p++;
value = String(b, p);
}
}
if(ke - kp == 4 && !MemICmp(kp, "name", 4))
name = value;
else if(ke - kp == 8 && !MemICmp(kp, "filename", 8))
filename = value;
if(*p == ';')
p++;
}
}
else if(!MemICmp(p, "type:", 5)) {
p += 5;
while(*p && *p != '\n' && (byte)*p <= ' ')
p++;
const char *b = p;
while(*p && *p != '\n')
p++;
const char *e = p;
while(e > b && (byte)e[-1] <= ' ')
e--;
content_type = String(b, e);
}
;
while(*p && *p++ != '\n')
;
}
if(*p++ != '\r' || *p++ != '\n')
return;
const char *b = p;
while(p < e) {
p = (const char *)memchr(p, '\r', e - p);
if(!p)
return;
if(p[0] == '\r' && p[1] == '\n' && p[2] == '-' && p[3] == '-'
&& !memcmp(p + 4, delimiter, delimiter.GetLength()))
break;
p++;
}
if(!name.IsEmpty() && *name != '.' && *name != '@') { // add variables
if(!filename.IsEmpty())
var.GetAdd(name + ".filename") = filename;
if(!content_type.IsEmpty())
var.GetAdd(name + ".content_type") = content_type;
var.Add(name, String(b, p));
}
p += delta;
while(*p && *p++ != '\n')
;
}
}
static const char hex_digits[] = "0123456789ABCDEF";
void UrlEncode(StringBuffer& out, const String& s)
{
static bool ok[256];
ONCELOCK {
for(int ch = 0; ch < 256; ch++)
ok[ch] = IsAlNum(ch) || ch == ',' || ch == '.' || ch == '-' || ch == '_';
}
const char *p = s, *e = s.End();
for(; p < e; p++)
{
const char *b = p;
while(p < e && ok[(byte)*p])
p++;
if(p > b)
out.Cat(b, int(p - b));
if(p >= e)
break;
if(*p == ' ')
out << '+';
else
out << '%' << hex_digits[(*p >> 4) & 15] << hex_digits[*p & 15];
}
}
void MakeLink(StringBuffer& out, const Vector<String>& part, const Vector<Value>& arg)
{
LTIMING("MakeLink");
out.Cat("/");
for(int i = 0; i < part.GetCount(); i++) {
const String& p = part[i];
if(i)
out << '/';
int q = (byte)*p;
if(q < 32) {
if(q >= 0 && q < arg.GetCount())
UrlEncode(out, AsString(arg[q]));
}
else
UrlEncode(out, p);
}
bool get = false;
for(int i = 0; i < arg.GetCount(); i++)
if(IsValueMap(arg[i])) {
if(get)
out << '&';
else
out << '?';
get = true;
ValueMap m = arg[i];
for(int i = 0; i < m.GetCount(); i++) {
if(i)
out << '&';
UrlEncode(out, AsString(m.GetKeys()[i]));
out << '=';
UrlEncode(out, AsString(m.GetValues()[i]));
}
}
}
Http& Http::RenderResult(const char *template_name)
{
LTIMING("Render");
response << UPP::Render(GetTemplate(template_name), this, var.GetValues());
return *this;
}
Http& Http::Redirect(void (*view)(Http&), const Vector<Value>& arg)
{
Redirect(MakeLink(view, arg));
return *this;
}
Http& Http::Redirect(void (*view)(Http&))
{
Vector<Value> arg;
Redirect(view, arg);
return *this;
}
Http& Http::Redirect(void (*view)(Http&), const Value& v1)
{
Vector<Value> arg;
arg.Add(v1);
Redirect(view, arg);
return *this;
}
Http& Http::Redirect(void (*view)(Http&), const Value& v1, const Value& v2)
{
Vector<Value> arg;
arg.Add(v1);
arg.Add(v2);
Redirect(view, arg);
return *this;
}
Http& Http::Ux(const char *id, const String& text)
{
if(response.GetCount())
response << '\1';
response << id << ':' << text;
return *this;
}
Http& Http::UxRender(const char *id, const char *template_name)
{
return Ux(id, RenderString(template_name));
}
Http& Http::UxSet(const char *id, const String& value)
{
return Ux(String(">") + id, value);
}
Http& Http::UxRun(const String& js_code)
{
return Ux("!", js_code);
}
};

View file

@ -1,82 +1,81 @@
#include "Skylark.h"
#define LLOG(x) // DLOG(x)
#define LTIMING(x) // RTIMING(x)
namespace Upp {
Renderer& Renderer::operator()(const ValueMap& map)
{
ValueArray v = map.GetValues();
const Index<Value>& k = map.GetKeys();
for(int i = 0; i < map.GetCount(); i++)
var.Add(k[i], v[i]);
return *this;
}
Renderer& Renderer::Link(const char *id, void (*view)(Http&), const Vector<Value>& arg)
{
var.Add(id, Raw('\"' + MakeLink(view, arg) + '\"'));
return *this;
}
Renderer& Renderer::operator()(const char *id, void (*view)(Http&))
{
return Link(id, view, Vector<Value>());
}
Renderer& Renderer::operator()(const char *id, void (*view)(Http&), const Value& arg1)
{
return Link(id, view, Vector<Value>() << arg1);
}
Renderer& Renderer::operator()(const char *id, void (*view)(Http&), const Value& arg1, const Value& arg2)
{
return Link(id, view, Vector<Value>() << arg1 << arg2);
}
StaticMutex template_cache_lock;
ArrayMap<String, One<Exe> > template_cache;
const One<Exe>& Renderer::GetTemplate(const char *template_name)
{
DDUMPM(var);
LTIMING("GetTemplate");
StringBuffer s;
{
LTIMING("MakeSignature");
for(int i = 0; i < var.GetCount(); i++)
s << var.GetKey(i) << ';';
s << ':' << template_name;
}
if(!SkylarkApp::Config().use_caching) // Templates get overwritten is not cached, MT hazard
s << ';' << Thread::GetCurrentId();
String sgn = s;
LLOG("Trying to retrieve " << sgn << " from cache");
Mutex::Lock __(template_cache_lock);
int q = template_cache.Find(sgn);
if(q >= 0 && SkylarkApp::Config().use_caching)
return template_cache[q];
LLOG("About to compile: " << sgn);
LTIMING("Compile");
One<Exe>& exe = q >= 0 ? template_cache[q] : template_cache.Add(sgn);
exe = Compile(GetPreprocessedTemplate(template_name, lang), var.GetIndex());
return exe;
}
String Renderer::RenderString(const String& template_name)
{
return UPP::Render(GetTemplate(template_name), this, var.GetValues());
}
Renderer& Renderer::Render(const char *id, const String& template_name)
{
var.Add(id, Render(template_name));
return *this;
}
Renderer::~Renderer()
{
}
#include "Skylark.h"
#define LLOG(x) // DLOG(x)
#define LTIMING(x) // RTIMING(x)
namespace Upp {
Renderer& Renderer::operator()(const ValueMap& map)
{
ValueArray v = map.GetValues();
const Index<Value>& k = map.GetKeys();
for(int i = 0; i < map.GetCount(); i++)
var.Add(k[i], v[i]);
return *this;
}
Renderer& Renderer::Link(const char *id, void (*view)(Http&), const Vector<Value>& arg)
{
var.Add(id, Raw('\"' + MakeLink(view, arg) + '\"'));
return *this;
}
Renderer& Renderer::operator()(const char *id, void (*view)(Http&))
{
return Link(id, view, Vector<Value>());
}
Renderer& Renderer::operator()(const char *id, void (*view)(Http&), const Value& arg1)
{
return Link(id, view, Vector<Value>() << arg1);
}
Renderer& Renderer::operator()(const char *id, void (*view)(Http&), const Value& arg1, const Value& arg2)
{
return Link(id, view, Vector<Value>() << arg1 << arg2);
}
StaticMutex template_cache_lock;
ArrayMap<String, One<Exe> > template_cache;
const One<Exe>& Renderer::GetTemplate(const char *template_name)
{
LTIMING("GetTemplate");
StringBuffer s;
{
LTIMING("MakeSignature");
for(int i = 0; i < var.GetCount(); i++)
s << var.GetKey(i) << ';';
s << ':' << template_name;
}
if(!SkylarkApp::Config().use_caching) // Templates get overwritten is not cached, MT hazard
s << ';' << Thread::GetCurrentId();
String sgn = s;
LLOG("Trying to retrieve " << sgn << " from cache");
Mutex::Lock __(template_cache_lock);
int q = template_cache.Find(sgn);
if(q >= 0 && SkylarkApp::Config().use_caching)
return template_cache[q];
LLOG("About to compile: " << sgn);
LTIMING("Compile");
One<Exe>& exe = q >= 0 ? template_cache[q] : template_cache.Add(sgn);
exe = Compile(GetPreprocessedTemplate(template_name, lang), var.GetIndex());
return exe;
}
String Renderer::RenderString(const String& template_name)
{
return UPP::Render(GetTemplate(template_name), this, var.GetValues());
}
Renderer& Renderer::Render(const char *id, const String& template_name)
{
var.Add(id, Render(template_name));
return *this;
}
Renderer::~Renderer()
{
}
};

View file

@ -1,166 +1,165 @@
#include "Skylark.h"
#define LLOG(x) //DLOG(x)
#define LDUMPC(x) //DDUMPC(x)
#define LDUMPM(x) //DDUMPM(x)
#define LLOGHEX(x) //DLOGHEX(x)
namespace Upp {
SessionConfig::SessionConfig()
{
cookie = "__skylark_session_cookie__";
dir = ConfigFile("session");
format = SESSION_FORMAT_BINARY;
id_column = "ID";
data_column = "DATA";
lastwrite_column = "LASTWRITE";
expire = 3600 * 24 * 365; // one year to expire the session
}
String Http::SessionFile(const String& sid)
{
ONCELOCK
RealizeDirectory(app.session.dir);
return AppendFileName(app.session.dir, sid);
}
void Http::LoadSession()
{
const SessionConfig& cfg = app.session;
session_var.Clear();
session_id = (*this)['@' + cfg.cookie];
if(IsNull(session_id))
return;
String data;
if(cfg.table.IsNull())
data = LoadFile(SessionFile(session_id));
else
data = SQLR % Select(cfg.data_column).From(cfg.table)
.Where(cfg.id_column == session_id);
LLOGHEX(data);
switch(cfg.format) {
case SESSION_FORMAT_JSON:
LoadFromJson(session_var, data);
break;
case SESSION_FORMAT_XML:
LoadFromXML(session_var, data);
break;
case SESSION_FORMAT_BINARY:
LoadFromString(session_var, data);
break;
}
SKYLARKLOG("Loaded session: " << session_id);
LDUMPM(session_var);
for(int i = 0; i < session_var.GetCount(); i++)
var.GetAdd('.' + session_var.GetKey(i)) = session_var[i];
}
thread__ int s_exp;
static int s_last_expiration_check;
StaticMutex expire_mutex;
void Http::SaveSession()
{
const SessionConfig& cfg = app.session;
SetCookie(cfg.cookie, session_id);
if(IsNull(session_id))
return;
String data;
switch(cfg.format) {
case SESSION_FORMAT_JSON:
data = StoreAsJson(session_var);
break;
case SESSION_FORMAT_XML:
data = StoreAsXML(session_var, "session");
break;
case SESSION_FORMAT_BINARY:
data = StoreAsString(session_var);
break;
}
if(cfg.table.IsNull())
SaveFile(SessionFile(session_id), data);
else {
SqlVal d = SqlBinary(data);
Time tm = GetSysTime();
SQL * Update(cfg.table)
(cfg.data_column, d)
(cfg.lastwrite_column, tm)
.Where(cfg.id_column == session_id);
if(SQL.GetRowsProcessed() == 0)
SQL * Insert(cfg.table)
(cfg.id_column, session_id)
(cfg.data_column, d)
(cfg.lastwrite_column, tm);
}
SKYLARKLOG("Stored session: " << session_id);
LDUMPM(session_var);
if((s_exp++ % 1000) == 0) {
bool expire_sessions = false;
{
Mutex::Lock __(expire_mutex);
if((dword)msecs(s_last_expiration_check) > 1000 * 60 * 10) {
expire_sessions = true;
s_last_expiration_check = msecs();
}
}
if(expire_sessions) {
Time tm = GetSysTime() - cfg.expire;
SKYLARKLOG("Expiring sessions older than " << tm);
if(cfg.table.IsNull()) {
FindFile ff(AppendFileName(cfg.dir, "*.*"));
Vector<String> todelete;
while(ff) {
if(ff.GetLastWriteTime() < tm)
todelete.Add(ff.GetPath());
ff.Next();
}
for(int i = 0; i < todelete.GetCount(); i++)
FileDelete(todelete[i]);
}
else
SQL * Delete(cfg.table).Where(cfg.lastwrite_column < tm);
}
}
}
Http& Http::ClearSession()
{
session_var.Clear();
session_id.Clear();
return *this;
}
Http& Http::SessionSet(const char *id, const Value& value)
{
LLOG("SessionSet " << id << " = " << value);
if(*id == '.')
id++;
if(IsNull(session_id))
NewSessionId();
session_var.GetAdd(id) = value;
var.GetAdd('.' + id) = value;
session_dirty = true;
return *this;
}
Http& Http::NewSessionId()
{
session_id = AsString(Uuid::Create());
session_dirty = true;
return *this;
}
Http& Http::SetLanguage(int lang_)
{
lang = lang_;
Upp::SetLanguage(lang_);
SessionSet("__lang__", lang);
SessionSet("language", ToLower(LNGAsText(lang)));
return *this;
}
#include "Skylark.h"
#define LLOG(x) //DLOG(x)
#define LDUMPC(x) //DDUMPC(x)
#define LDUMPM(x) //DDUMPM(x)
#define LLOGHEX(x) //DLOGHEX(x)
namespace Upp {
SessionConfig::SessionConfig()
{
cookie = "__skylark_session_cookie__";
dir = ConfigFile("session");
format = SESSION_FORMAT_BINARY;
id_column = "ID";
data_column = "DATA";
lastwrite_column = "LASTWRITE";
expire = 3600 * 24 * 365; // one year to expire the session
}
String Http::SessionFile(const String& sid)
{
ONCELOCK
RealizeDirectory(app.session.dir);
return AppendFileName(app.session.dir, sid);
}
void Http::LoadSession()
{
const SessionConfig& cfg = app.session;
session_var.Clear();
session_id = (*this)['@' + cfg.cookie];
if(IsNull(session_id))
return;
String data;
if(cfg.table.IsNull())
data = LoadFile(SessionFile(session_id));
else
data = SQLR % Select(cfg.data_column).From(cfg.table)
.Where(cfg.id_column == session_id);
switch(cfg.format) {
case SESSION_FORMAT_JSON:
LoadFromJson(session_var, data);
break;
case SESSION_FORMAT_XML:
LoadFromXML(session_var, data);
break;
case SESSION_FORMAT_BINARY:
LoadFromString(session_var, data);
break;
}
SKYLARKLOG("Loaded session: " << session_id);
LDUMPM(session_var);
for(int i = 0; i < session_var.GetCount(); i++)
var.GetAdd('.' + session_var.GetKey(i)) = session_var[i];
}
thread__ int s_exp;
static int s_last_expiration_check;
StaticMutex expire_mutex;
void Http::SaveSession()
{
const SessionConfig& cfg = app.session;
SetCookie(cfg.cookie, session_id);
if(IsNull(session_id))
return;
String data;
switch(cfg.format) {
case SESSION_FORMAT_JSON:
data = StoreAsJson(session_var);
break;
case SESSION_FORMAT_XML:
data = StoreAsXML(session_var, "session");
break;
case SESSION_FORMAT_BINARY:
data = StoreAsString(session_var);
break;
}
if(cfg.table.IsNull())
SaveFile(SessionFile(session_id), data);
else {
SqlVal d = SqlBinary(data);
Time tm = GetSysTime();
SQL * Update(cfg.table)
(cfg.data_column, d)
(cfg.lastwrite_column, tm)
.Where(cfg.id_column == session_id);
if(SQL.GetRowsProcessed() == 0)
SQL * Insert(cfg.table)
(cfg.id_column, session_id)
(cfg.data_column, d)
(cfg.lastwrite_column, tm);
}
SKYLARKLOG("Stored session: " << session_id);
LDUMPM(session_var);
if((s_exp++ % 1000) == 0) {
bool expire_sessions = false;
{
Mutex::Lock __(expire_mutex);
if((dword)msecs(s_last_expiration_check) > 1000 * 60 * 10) {
expire_sessions = true;
s_last_expiration_check = msecs();
}
}
if(expire_sessions) {
Time tm = GetSysTime() - cfg.expire;
SKYLARKLOG("Expiring sessions older than " << tm);
if(cfg.table.IsNull()) {
FindFile ff(AppendFileName(cfg.dir, "*.*"));
Vector<String> todelete;
while(ff) {
if(ff.GetLastWriteTime() < tm)
todelete.Add(ff.GetPath());
ff.Next();
}
for(int i = 0; i < todelete.GetCount(); i++)
FileDelete(todelete[i]);
}
else
SQL * Delete(cfg.table).Where(cfg.lastwrite_column < tm);
}
}
}
Http& Http::ClearSession()
{
session_var.Clear();
session_id.Clear();
return *this;
}
Http& Http::SessionSet(const char *id, const Value& value)
{
LLOG("SessionSet " << id << " = " << value);
if(*id == '.')
id++;
if(IsNull(session_id))
NewSessionId();
session_var.GetAdd(id) = value;
var.GetAdd('.' + id) = value;
session_dirty = true;
return *this;
}
Http& Http::NewSessionId()
{
session_id = AsString(Uuid::Create());
session_dirty = true;
return *this;
}
Http& Http::SetLanguage(int lang_)
{
lang = lang_;
Upp::SetLanguage(lang_);
SessionSet("__lang__", lang);
SessionSet("language", ToLower(LNGAsText(lang)));
return *this;
}
};