mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-09-10 03:54:54 -06:00
uppsrc: Skylark moved from sandbox
git-svn-id: svn://ultimatepp.org/upp/trunk@5125 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
00baf70dbc
commit
8e070bb698
19 changed files with 3072 additions and 0 deletions
288
uppsrc/Skylark/App.cpp
Normal file
288
uppsrc/Skylark/App.cpp
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
#include "Skylark.h"
|
||||
#ifdef PLATFORM_POSIX
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#ifdef PLATFORM_WIN32
|
||||
#include <wincon.h>
|
||||
#endif
|
||||
|
||||
#define CONSOLE(x) Cout() << x << '\n'
|
||||
|
||||
#ifdef PLATFORM_WIN32
|
||||
BOOL WINAPI SkylarkApp::CtrlCHandlerRoutine(__in DWORD dwCtrlType)
|
||||
{
|
||||
LOG("Ctrl+C handler");
|
||||
TheApp().quit = true;
|
||||
Cout() << "Ctrl + C\n";
|
||||
TcpSocket h;
|
||||
h.Connect("127.0.0.1", TheApp().port);
|
||||
h.Put("quit");
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
void SkylarkApp::WorkThread()
|
||||
{
|
||||
RunThread();
|
||||
}
|
||||
|
||||
void SkylarkApp::ThreadRun()
|
||||
{
|
||||
WorkThread();
|
||||
}
|
||||
|
||||
void SkylarkApp::RunThread()
|
||||
{
|
||||
SQL.ClearError();
|
||||
SQLR.ClearError();
|
||||
SQL.GetSession().ThrowOnError();
|
||||
SQLR.GetSession().ThrowOnError();
|
||||
|
||||
for(;;) {
|
||||
TcpSocket request;
|
||||
accept_mutex.Enter();
|
||||
if(quit) {
|
||||
accept_mutex.Leave();
|
||||
break;
|
||||
}
|
||||
bool b = request.Timeout(2000).Accept(server);
|
||||
accept_mutex.Leave();
|
||||
if(quit)
|
||||
break;
|
||||
if(b) {
|
||||
CONSOLE("Accepted " << Thread::GetCurrentId());
|
||||
#ifdef PLATFORM_POSIX
|
||||
if(prefork)
|
||||
alarm(timeout);
|
||||
#endif
|
||||
Http http(*this);
|
||||
http.Dispatch(request);
|
||||
#ifdef PLATFORM_POSIX
|
||||
if(prefork)
|
||||
alarm(0);
|
||||
#endif
|
||||
CONSOLE("Finished " << Thread::GetCurrentId());
|
||||
}
|
||||
else
|
||||
CONSOLE("Waiting " << Thread::GetCurrentId());
|
||||
}
|
||||
}
|
||||
|
||||
void SkylarkApp::Main()
|
||||
{
|
||||
Buffer<Thread> uwt(threads);
|
||||
for(int i = 0; i < threads; i++)
|
||||
Thread::Start(THISBACK(ThreadRun));
|
||||
|
||||
while(Thread::GetCount()) {
|
||||
if(getpid() == main_pid && (msecs() % 1000) == 0)
|
||||
ThreadRun();
|
||||
Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
void SkylarkApp::Broadcast(int signal)
|
||||
{
|
||||
#ifdef PLATFORM_POSIX
|
||||
if(getpid() == main_pid)
|
||||
for(int i = 0; i < child_pid.GetCount(); i++)
|
||||
kill(child_pid[i], signal);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SkylarkApp::Signal(int signal)
|
||||
{
|
||||
#ifdef PLATFORM_POSIX
|
||||
switch(signal) {
|
||||
case SIGTERM:
|
||||
case SIGHUP:
|
||||
quit = true;
|
||||
Broadcast(signal);
|
||||
break;
|
||||
case SIGINT:
|
||||
Broadcast(signal);
|
||||
exit(0);
|
||||
break;
|
||||
case SIGALRM:
|
||||
if(getpid() != TheApp().main_pid) {
|
||||
// "Timeout - session stoped"
|
||||
exit(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SkylarkApp::SignalHandler(int signal)
|
||||
{
|
||||
TheApp().Signal(signal);
|
||||
}
|
||||
|
||||
void DisableHUP()
|
||||
{
|
||||
#ifdef PLATFORM_POSIX
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGHUP);
|
||||
sigprocmask(SIG_BLOCK, &mask, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EnableHUP()
|
||||
{
|
||||
#ifdef PLATFORM_POSIX
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGHUP);
|
||||
sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SkylarkApp::Run()
|
||||
{
|
||||
// DisableHUP();
|
||||
SqlSession::PerThread();
|
||||
SqlId::UseQuotes();
|
||||
FinalizeViews();
|
||||
|
||||
#ifdef PLATFORM_WIN32
|
||||
SetConsoleCtrlHandler(CtrlCHandlerRoutine, true);
|
||||
#endif
|
||||
|
||||
main_pid = getpid();
|
||||
quit = false;
|
||||
|
||||
#if defined(PLATFORM_POSIX) && defined(_DEBUG)
|
||||
// Avoid the need to close running server in debug mode...
|
||||
String pidf = ConfigFile("debug_pid");
|
||||
int prev_pid = atoi(LoadFile(pidf));
|
||||
if(prev_pid) {
|
||||
kill(prev_pid, SIGTERM);
|
||||
int status = 0;
|
||||
waitpid(prev_pid, &status, 0);
|
||||
}
|
||||
for(int i = 0; i < 100; i++) {
|
||||
Cout() << i;
|
||||
if(server.Listen(port, 5))
|
||||
goto listening;
|
||||
Sleep(10);
|
||||
}
|
||||
LOG("Cannot open server socket!");
|
||||
Cout() << "Cannot open server socket!\n";
|
||||
return;
|
||||
listening:;
|
||||
#else
|
||||
if(!server.Listen(port, 5)) {
|
||||
LOG("Cannot open server socket!");
|
||||
Cout() << "Cannot open server socket!\n";
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PLATFORM_POSIX
|
||||
#ifdef _DEBUG
|
||||
SaveFile(pidf, AsString(main_pid));
|
||||
#endif
|
||||
if(prefork) {
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = SignalHandler;
|
||||
sigaction(SIGTERM, &sa, NULL);
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
sigaction(SIGHUP, &sa, NULL);
|
||||
sigaction(SIGALRM, &sa, NULL);
|
||||
// EnableHUP();
|
||||
while(!quit) {
|
||||
while(child_pid.GetCount() < prefork && !quit) {
|
||||
pid_t p = fork();
|
||||
if(p == 0) {
|
||||
Main();
|
||||
return;
|
||||
}
|
||||
else
|
||||
if(p > 0)
|
||||
child_pid.Add(p);
|
||||
else {
|
||||
// "cant create new process"
|
||||
Broadcast(SIGINT);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
int status = 0;
|
||||
pid_t p = wait(&status);
|
||||
if(p > 0) {
|
||||
int q = FindIndex(child_pid, p);
|
||||
if(q >= 0)
|
||||
child_pid.Remove(q);
|
||||
}
|
||||
}
|
||||
|
||||
Broadcast(SIGTERM);
|
||||
int status = 0;
|
||||
for(int i = 0; i < child_pid.GetCount(); i++)
|
||||
waitpid(child_pid[i], &status, 0);
|
||||
// "server stopped";
|
||||
}
|
||||
else
|
||||
#endif
|
||||
Main();
|
||||
|
||||
#if defined(_DEBUG) && defined(POSIX)
|
||||
FileDelete(pidf);
|
||||
#endif
|
||||
|
||||
CONSOLE("ExitSkylark");
|
||||
}
|
||||
|
||||
|
||||
void SkylarkApp::SqlError(Http& http)
|
||||
{
|
||||
}
|
||||
|
||||
void SkylarkApp::InternalError(Http& http)
|
||||
{
|
||||
}
|
||||
|
||||
void SkylarkApp::NotFound(Http& http)
|
||||
{
|
||||
}
|
||||
|
||||
void SkylarkApp::Unauthorized(Http& http)
|
||||
{
|
||||
}
|
||||
|
||||
SkylarkApp *SkylarkApp::app;
|
||||
|
||||
SkylarkApp& SkylarkApp::TheApp()
|
||||
{
|
||||
ASSERT(app);
|
||||
return *app;
|
||||
}
|
||||
|
||||
const SkylarkConfig& SkylarkApp::Config()
|
||||
{
|
||||
ASSERT(app);
|
||||
return *app;
|
||||
}
|
||||
|
||||
SkylarkApp::SkylarkApp()
|
||||
{
|
||||
ASSERT(!app);
|
||||
app = this;
|
||||
threads = 3 * CPU_Cores() + 1;
|
||||
port = 8001;
|
||||
use_caching = true;
|
||||
#ifdef _DEBUG
|
||||
prefork = 0;
|
||||
timeout = 0;
|
||||
#else
|
||||
prefork = 1;
|
||||
timeout = 300;
|
||||
#endif
|
||||
}
|
||||
|
||||
SkylarkApp::~SkylarkApp()
|
||||
{
|
||||
app = NULL;
|
||||
}
|
||||
14
uppsrc/Skylark/Base.witz
Normal file
14
uppsrc/Skylark/Base.witz
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="cs" lang="cs">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>#TITLE</title>
|
||||
<script type="text/javascript" src="static/Skylark/skylark.js"></script>
|
||||
#HEAD
|
||||
</head>
|
||||
<body id="body_id">
|
||||
#BODY
|
||||
</body>
|
||||
</html>
|
||||
|
||||
#define TITLE Skylark application
|
||||
398
uppsrc/Skylark/Compile.cpp
Normal file
398
uppsrc/Skylark/Compile.cpp
Normal file
|
|
@ -0,0 +1,398 @@
|
|||
#include "Skylark.h"
|
||||
|
||||
#define LLOG(x) // DLOG(x)
|
||||
|
||||
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()) {
|
||||
String 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);
|
||||
try {
|
||||
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;
|
||||
}
|
||||
catch(CParser::Error e) {
|
||||
One<Exe> result;
|
||||
result.Create<Compiler::ExeConst>().value = e;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
374
uppsrc/Skylark/Dispatch.cpp
Normal file
374
uppsrc/Skylark/Dispatch.cpp
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
#include "Skylark.h"
|
||||
|
||||
#define LLOG(x) DLOG(x)
|
||||
#define LDUMP(x) DDUMP(x)
|
||||
#define LTIMING(x) RTIMING(x)
|
||||
|
||||
enum { DISPATCH_VARARGS = -1 };
|
||||
|
||||
struct DispatchNode : Moveable<DispatchNode> {
|
||||
VectorMap<String, int> subnode;
|
||||
void (*view)(Http&);
|
||||
int argpos;
|
||||
int method;
|
||||
bool post_raw;
|
||||
String id;
|
||||
|
||||
enum { GET, POST };
|
||||
|
||||
DispatchNode() { view = NULL; argpos = Null; method = GET; post_raw = false; }
|
||||
};
|
||||
|
||||
static Vector<DispatchNode>& sDispatchMap()
|
||||
{
|
||||
static Vector<DispatchNode> x;
|
||||
return x;
|
||||
}
|
||||
|
||||
static VectorMap<String, Vector<String> >& sLinkMap()
|
||||
{
|
||||
static VectorMap<String, Vector<String> > x;
|
||||
return x;
|
||||
}
|
||||
|
||||
static Index<uintptr_t>& sViewIndex()
|
||||
{
|
||||
static Index<uintptr_t> x;
|
||||
return x;
|
||||
}
|
||||
|
||||
void DumpDispatchMap()
|
||||
{
|
||||
Vector<DispatchNode>& DispatchMap = sDispatchMap();
|
||||
for(int i = 0; i < DispatchMap.GetCount(); i++) {
|
||||
LLOG("-------------");
|
||||
String sub;
|
||||
for(int j = 0; j < DispatchMap[i].subnode.GetCount(); j++)
|
||||
sub << DispatchMap[i].subnode.GetKey(j) << "->" << DispatchMap[i].subnode[j] << ", ";
|
||||
LLOG(i << " " << (bool)DispatchMap[i].view << ": " << sub);
|
||||
}
|
||||
}
|
||||
|
||||
Vector<String> *GetUrlViewLinkParts(const String& id)
|
||||
{
|
||||
int q = sLinkMap().Find(id);
|
||||
if(q < 0)
|
||||
return NULL;
|
||||
return &sLinkMap()[q];
|
||||
}
|
||||
|
||||
String MakeLink(void (*view)(Http&), const Vector<Value>& arg)
|
||||
{
|
||||
int q = sViewIndex().Find((uintptr_t)view);
|
||||
if(q < 0)
|
||||
throw Exc("Invalid view");
|
||||
if(q < 0)
|
||||
return String();
|
||||
StringBuffer out;
|
||||
MakeLink(out, sLinkMap()[q], arg);
|
||||
return out;
|
||||
}
|
||||
|
||||
void RegisterView0(void (*view)(Http&), const char *id, String path, bool primary)
|
||||
{
|
||||
LLOG("RegisterView " << path);
|
||||
Vector<String>& linkpart = sLinkMap().GetAdd(id);
|
||||
sViewIndex().FindAdd((uintptr_t)view);
|
||||
Vector<DispatchNode>& DispatchMap = sDispatchMap();
|
||||
int method = DispatchNode::GET;
|
||||
bool post_raw = false;
|
||||
int q = path.Find(':');
|
||||
if(q >= 0) {
|
||||
if(path.Mid(q + 1) == "POST")
|
||||
method = DispatchNode::POST;
|
||||
if(path.Mid(q + 1) == "POST_RAW") {
|
||||
method = DispatchNode::POST;
|
||||
post_raw = true;
|
||||
}
|
||||
path = path.Mid(0, q);
|
||||
}
|
||||
Vector<String> h = Split(path, '/');
|
||||
if(DispatchMap.GetCount() == 0)
|
||||
DispatchMap.Add();
|
||||
q = 0;
|
||||
int linkargpos = 0;
|
||||
for(int i = 0; i < h.GetCount(); i++) {
|
||||
String s = h[i];
|
||||
LLOG(" Node " << h[i]);
|
||||
DispatchNode& n = DispatchMap[q];
|
||||
if(*s == '*') {
|
||||
int argpos = Null;
|
||||
if(IsDigit(s[1]))
|
||||
linkargpos = argpos = minmax(atoi(~s + 1), 0, 30);
|
||||
else
|
||||
if(s[1] == '*')
|
||||
argpos = DISPATCH_VARARGS;
|
||||
q = DispatchMap.GetCount();
|
||||
LLOG(" Adding arg " << argpos << ": " << q);
|
||||
n.subnode.Add(Null, q);
|
||||
DispatchMap.Add();
|
||||
DispatchMap[q].argpos = argpos;
|
||||
if(primary)
|
||||
linkpart.Add(String(linkargpos++, 1));
|
||||
}
|
||||
else {
|
||||
if(primary)
|
||||
linkpart.Add(s);
|
||||
q = n.subnode.Get(s, -1);
|
||||
if(q < 0) {
|
||||
q = DispatchMap.GetCount();
|
||||
LLOG(" Adding " << s << ": " << q);
|
||||
n.subnode.Add(s, q);
|
||||
DispatchMap.Add();
|
||||
}
|
||||
}
|
||||
}
|
||||
ASSERT_(!DispatchMap[q].view, "duplicate view " + String(path));
|
||||
DispatchMap[q].view = view;
|
||||
DispatchMap[q].method = method;
|
||||
DispatchMap[q].id = id;
|
||||
DispatchMap[q].post_raw = post_raw;
|
||||
// DumpDispatchMap();
|
||||
}
|
||||
|
||||
struct ViewData {
|
||||
void (*view)(Http&);
|
||||
String id;
|
||||
String path;
|
||||
};
|
||||
|
||||
static Array<ViewData>& sViewData()
|
||||
{
|
||||
static Array<ViewData> x;
|
||||
return x;
|
||||
}
|
||||
|
||||
void RegisterView(void (*view)(Http&), const char *id, const char *path)
|
||||
{
|
||||
Array<ViewData>& v = sViewData();
|
||||
ViewData& w = v.Add();
|
||||
w.view = view;
|
||||
w.id = id;
|
||||
w.path = path;
|
||||
}
|
||||
|
||||
void SkylarkApp::FinalizeViews()
|
||||
{
|
||||
Array<ViewData>& w = sViewData();
|
||||
for(int i = 0; i < w.GetCount(); i++) {
|
||||
const ViewData& v = w[i];
|
||||
ASSERT_(sViewIndex().Find((uintptr_t)v.view) < 0, "duplicate view function registration " + String(v.id));
|
||||
Vector<String> h = Split(ReplaceVars(root + '/' + v.path, view_var, '$'), ';');
|
||||
for(int i = 0; i < h.GetCount(); i++)
|
||||
RegisterView0(v.view, v.id, h[i], i == 0);
|
||||
}
|
||||
w.Clear();
|
||||
}
|
||||
|
||||
struct BestDispatch {
|
||||
void (*view)(Http&);
|
||||
int matched_parts;
|
||||
int matched_params;
|
||||
Vector<String>& arg;
|
||||
String id;
|
||||
bool post_raw;
|
||||
|
||||
BestDispatch(Vector<String>& arg) : arg(arg) { matched_parts = -1; matched_params = 0; view = NULL; post_raw = false; }
|
||||
};
|
||||
|
||||
void GetBestDispatch(int method,
|
||||
const Vector<String>& part, int ii, const DispatchNode& n, Vector<String>& arg,
|
||||
BestDispatch& bd, int matched_parts, int matched_params)
|
||||
{
|
||||
Vector<DispatchNode>& DispatchMap = sDispatchMap();
|
||||
if(ii >= part.GetCount()) {
|
||||
if(n.view && n.method == method &&
|
||||
(matched_parts > bd.matched_parts ||
|
||||
matched_parts == bd.matched_parts && matched_params > bd.matched_params)) {
|
||||
bd.arg <<= arg;
|
||||
bd.view = n.view;
|
||||
bd.matched_parts = matched_parts;
|
||||
bd.id = n.id;
|
||||
bd.post_raw = n.post_raw;
|
||||
}
|
||||
int q = n.subnode.Find(String());
|
||||
while(q >= 0) {
|
||||
const DispatchNode& an = DispatchMap[n.subnode[q]];
|
||||
if(an.argpos == DISPATCH_VARARGS && an.view && an.method == method) {
|
||||
bd.view = an.view;
|
||||
bd.arg.Clear();
|
||||
break;
|
||||
}
|
||||
q = n.subnode.FindNext(q);
|
||||
}
|
||||
return;
|
||||
}
|
||||
int qq = n.subnode.Get(part[ii], -1);
|
||||
if(qq >= 0)
|
||||
GetBestDispatch(method, part, ii + 1, DispatchMap[qq], arg, bd, matched_parts + 1, matched_params);
|
||||
int q = n.subnode.Find(String());
|
||||
while(q >= 0) {
|
||||
int qq = n.subnode[q];
|
||||
int ac = arg.GetCount();
|
||||
const DispatchNode& an = DispatchMap[qq];
|
||||
int apos = an.argpos;
|
||||
LLOG(" *" << qq << " apos: " << apos);
|
||||
if(apos == DISPATCH_VARARGS) {
|
||||
if(an.view && an.method == method &&
|
||||
(matched_parts > bd.matched_parts || matched_parts == bd.matched_parts && matched_params > bd.matched_params)) {
|
||||
bd.arg <<= arg;
|
||||
bd.arg.Append(part, ii, part.GetCount() - ii);
|
||||
bd.view = an.view;
|
||||
bd.matched_parts = matched_parts;
|
||||
bd.id = an.id;
|
||||
}
|
||||
}
|
||||
else {
|
||||
String pv;
|
||||
if(IsNull(apos))
|
||||
arg.Add(part[ii]);
|
||||
else {
|
||||
String& at = arg.At(apos);
|
||||
pv = at;
|
||||
at = part[ii];
|
||||
}
|
||||
GetBestDispatch(method, part, ii + 1, an, arg, bd, matched_parts, matched_params + 1);
|
||||
if(!IsNull(apos))
|
||||
arg[apos] = pv;
|
||||
}
|
||||
arg.SetCount(ac);
|
||||
q = n.subnode.FindNext(q);
|
||||
}
|
||||
}
|
||||
|
||||
void Http::Dispatch(TcpSocket& socket)
|
||||
{
|
||||
Vector<DispatchNode>& DispatchMap = sDispatchMap();
|
||||
if(hdr.Read(socket)) {
|
||||
int len = GetLength();
|
||||
content = socket.GetAll(len);
|
||||
LLOG("--------------------------------------------");
|
||||
LLOG(hdr.GetMethod() << " " << hdr.GetURI() << "\n");
|
||||
LDUMP(content);
|
||||
Cout() << hdr.GetMethod() << " " << hdr.GetURI() << "\n";
|
||||
String r;
|
||||
var.Clear();
|
||||
arg.Clear();
|
||||
LTIMING("Request processing");
|
||||
request_content_type = GetHeader("content-type");
|
||||
String rc = ToLower(request_content_type);
|
||||
bool post = hdr.GetMethod() == "POST";
|
||||
if(post)
|
||||
if(rc.StartsWith("application/x-www-form-urlencoded"))
|
||||
ParseRequest(content);
|
||||
else
|
||||
if(rc.StartsWith("multipart/"))
|
||||
ReadMultiPart(content);
|
||||
String uri = hdr.GetURI();
|
||||
int q = uri.Find('?');
|
||||
if(q >= 0) {
|
||||
if(!post)
|
||||
ParseRequest(~uri + q + 1);
|
||||
uri.Trim(q);
|
||||
}
|
||||
for(int i = hdr.fields.Find("cookie"); i >= 0; i = hdr.fields.FindNext(i)) {
|
||||
const String& h = hdr.fields[i];
|
||||
int q = 0;
|
||||
for(;;) {
|
||||
int qq = h.Find('=', q);
|
||||
if(qq < 0)
|
||||
break;
|
||||
String id = ToLower(TrimBoth(h.Mid(q, qq - q)));
|
||||
qq++;
|
||||
DUMP(id);
|
||||
q = h.Find(';', qq);
|
||||
if(q < 0) {
|
||||
var.Add(id, UrlDecode(h.Mid(qq)));
|
||||
break;
|
||||
}
|
||||
var.Add(id, UrlDecode(h.Mid(qq, q - qq)));
|
||||
q++;
|
||||
}
|
||||
}
|
||||
var.GetAdd("__identity__"); // To make StdLib.icpp GetIndentity work without changing preset stack positions
|
||||
DUMPM(var);
|
||||
Vector<String> part = Split(uri, '/');
|
||||
for(int i = 0; i < part.GetCount(); i++)
|
||||
part[i] = UrlDecode(part[i]);
|
||||
DUMPC(part);
|
||||
Vector<String> a;
|
||||
BestDispatch bd(arg);
|
||||
if(DispatchMap.GetCount())
|
||||
GetBestDispatch(post ? DispatchNode::POST : DispatchNode::GET, part, 0, DispatchMap[0], a, bd, 0, 0);
|
||||
DUMPC(arg);
|
||||
response.Clear();
|
||||
if(bd.view) {
|
||||
try {
|
||||
SQL.Begin();
|
||||
LoadSession();
|
||||
session_dirty = false;
|
||||
if(post && !bd.post_raw) {
|
||||
String id = Nvl((*this)["__post_identity__"], (*this)["__js_identity__"]);
|
||||
if(id != (*this)["__identity__"])
|
||||
throw AuthExc("identity error");
|
||||
}
|
||||
lang = Nvl(Int("__lang__"), LNG_ENGLISH);
|
||||
SetLanguage(lang);
|
||||
viewid = bd.id;
|
||||
(*bd.view)(*this);
|
||||
if(session_dirty)
|
||||
SaveSession();
|
||||
SQL.Commit();
|
||||
}
|
||||
catch(SqlExc e) {
|
||||
SQL.Rollback();
|
||||
response << "Internal server error<br>"
|
||||
<< "SQL ERROR: " << e;
|
||||
code = 500;
|
||||
code_text = "Internal server error";
|
||||
app.SqlError(*this);
|
||||
}
|
||||
catch(AuthExc e) {
|
||||
SQL.Rollback();
|
||||
response << e;
|
||||
code = 403;
|
||||
code_text = "Unauthorized";
|
||||
app.Unauthorized(*this);
|
||||
}
|
||||
catch(Exc e) {
|
||||
SQL.Rollback();
|
||||
response << "Internal server error<br>"
|
||||
<< e;
|
||||
code = 500;
|
||||
code_text = "Internal server error";
|
||||
app.InternalError(*this);
|
||||
}
|
||||
}
|
||||
else {
|
||||
response << "Page not found";
|
||||
code = 404;
|
||||
code_text = "Not found";
|
||||
app.NotFound(*this);
|
||||
}
|
||||
r.Clear();
|
||||
if(redirect.GetCount()) {
|
||||
r << "HTTP/1.1 " << code << " Found\r\n";
|
||||
r << "Location: " << redirect << "\r\n";
|
||||
}
|
||||
else {
|
||||
r <<
|
||||
"HTTP/1.0 " << code << ' ' << code_text << "\r\n"
|
||||
"Date: " << WwwFormat(GetUtcTime()) << "\r\n"
|
||||
"Server: U++\r\n"
|
||||
"Content-Length: " << response.GetCount() << "\r\n"
|
||||
"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);
|
||||
socket.PutAll(response);
|
||||
}
|
||||
}
|
||||
396
uppsrc/Skylark/Exe.cpp
Normal file
396
uppsrc/Skylark/Exe.cpp
Normal file
|
|
@ -0,0 +1,396 @@
|
|||
#include "Skylark.h"
|
||||
|
||||
#define LTIMING(x) // RTIMING(x)
|
||||
#define LLOG(x) DLOG(x)
|
||||
|
||||
force_inline
|
||||
bool Compiler::IsTrue(const Value& v)
|
||||
{
|
||||
return !(IsNull(v) || IsNumber(v) && (int)v == 0 || IsValueArray(v) && v.GetCount() == 0);
|
||||
}
|
||||
|
||||
String TypeMismatch(const char *s)
|
||||
{
|
||||
return ErrorValue("<* type mismatch for '" + String(s) + "' *>");
|
||||
}
|
||||
|
||||
Value Compiler::ExeVar::Eval(ExeContext& x) const
|
||||
{
|
||||
LLOG("Retrieving var no " << var_index << " = " << x.stack[var_index]);
|
||||
return x.stack[var_index];
|
||||
}
|
||||
|
||||
Value Compiler::ExeConst::Eval(ExeContext& x) const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
Value Compiler::ExeArray::Eval(ExeContext& x) const
|
||||
{
|
||||
ValueArray va;
|
||||
for(int i = 0; i < item.GetCount(); i++)
|
||||
va.Add(item[i]->Eval(x));
|
||||
return va;
|
||||
}
|
||||
|
||||
Value Compiler::ExeMap::Eval(ExeContext& x) const
|
||||
{
|
||||
ValueMap m;
|
||||
for(int i = 0; i < key.GetCount(); i++)
|
||||
m.Add(key[i]->Eval(x), value[i]->Eval(x));
|
||||
return m;
|
||||
}
|
||||
|
||||
Value Compiler::ExeNot::Eval(ExeContext& x) const
|
||||
{
|
||||
return !IsTrue(a->Eval(x));
|
||||
}
|
||||
|
||||
Value Compiler::ExeNeg::Eval(ExeContext& x) const
|
||||
{
|
||||
Value v = a->Eval(x);
|
||||
if(IsNumber(v))
|
||||
return -(double)v;
|
||||
return TypeMismatch("unary -");
|
||||
}
|
||||
|
||||
Value Compiler::ExeMul::Eval(ExeContext& x) const
|
||||
{
|
||||
Value v1 = a->Eval(x);
|
||||
Value v2 = b->Eval(x);
|
||||
if(IsNumber(v1) && IsNumber(v2))
|
||||
return (double)v1 * (double)v2;
|
||||
return TypeMismatch("*");
|
||||
}
|
||||
|
||||
Value Compiler::ExeDiv::Eval(ExeContext& x) const
|
||||
{
|
||||
Value v1 = a->Eval(x);
|
||||
Value v2 = b->Eval(x);
|
||||
if(IsNumber(v1) && IsNumber(v2))
|
||||
return (double)v1 / (double)v2;
|
||||
return TypeMismatch("/");
|
||||
}
|
||||
|
||||
Value Compiler::ExeMod::Eval(ExeContext& x) const
|
||||
{
|
||||
Value v1 = a->Eval(x);
|
||||
Value v2 = b->Eval(x);
|
||||
if(IsNumber(v1) && IsNumber(v2)) {
|
||||
int m = v2;
|
||||
if(m == 0)
|
||||
return Null;
|
||||
return (int)v1 % m;
|
||||
}
|
||||
return TypeMismatch("%");
|
||||
}
|
||||
|
||||
Value Compiler::ExeAdd::Eval(ExeContext& x) const
|
||||
{
|
||||
Value v1 = a->Eval(x);
|
||||
Value v2 = b->Eval(x);
|
||||
if(IsString(v1) && IsString(v2))
|
||||
return (String)v1 + (String)v2;
|
||||
if(IsNumber(v1) && IsNumber(v2))
|
||||
return (double)v1 + (double)v2;
|
||||
if(v1.Is<RawHtmlText>() || v2.Is<RawHtmlText>()) {
|
||||
RawHtmlText h;
|
||||
h.text = AsString(v1) + AsString(v2);
|
||||
return RawToValue(h);
|
||||
}
|
||||
return AsString(v1) + AsString(v2);
|
||||
}
|
||||
|
||||
Value Compiler::ExeSub::Eval(ExeContext& x) const
|
||||
{
|
||||
Value v1 = a->Eval(x);
|
||||
Value v2 = b->Eval(x);
|
||||
if(IsNumber(v1) && IsNumber(v2))
|
||||
return (double)v1 - (double)v2;
|
||||
return TypeMismatch("-");
|
||||
}
|
||||
|
||||
Value Compiler::ExeSll::Eval(ExeContext& x) const
|
||||
{
|
||||
Value v1 = a->Eval(x);
|
||||
Value v2 = b->Eval(x);
|
||||
if(IsNumber(v1) && IsNumber(v2))
|
||||
return (int)v1 << min(32, (int)v2);
|
||||
return TypeMismatch("<<");
|
||||
}
|
||||
|
||||
Value Compiler::ExeSrl::Eval(ExeContext& x) const
|
||||
{
|
||||
Value v1 = a->Eval(x);
|
||||
Value v2 = b->Eval(x);
|
||||
if(IsNumber(v1) && IsNumber(v2))
|
||||
return int((unsigned)(int)v1 >> min(32, (int)v2));
|
||||
return TypeMismatch(">>>");
|
||||
}
|
||||
|
||||
Value Compiler::ExeSra::Eval(ExeContext& x) const
|
||||
{
|
||||
Value v1 = a->Eval(x);
|
||||
Value v2 = b->Eval(x);
|
||||
if(IsNumber(v1) && IsNumber(v2))
|
||||
return (int)v1 >> min(32, (int)v2);
|
||||
return TypeMismatch(">>");
|
||||
}
|
||||
|
||||
Value Compiler::ExeLt::Eval(ExeContext& x) const
|
||||
{
|
||||
Value v1 = a->Eval(x);
|
||||
Value v2 = b->Eval(x);
|
||||
if(IsString(v1) && IsString(v2))
|
||||
return (String)v1 < (String)v2;
|
||||
if(IsNumber(v1) && IsNumber(v2))
|
||||
return (double)v1 < (double)v2;
|
||||
return AsString(v1) < AsString(v2);
|
||||
}
|
||||
|
||||
Value Compiler::ExeLte::Eval(ExeContext& x) const
|
||||
{
|
||||
Value v1 = a->Eval(x);
|
||||
Value v2 = b->Eval(x);
|
||||
if(IsString(v1) && IsString(v2))
|
||||
return (String)v1 <= (String)v2;
|
||||
if(IsNumber(v1) && IsNumber(v2))
|
||||
return (double)v1 <= (double)v2;
|
||||
return AsString(v1) <= AsString(v2);
|
||||
}
|
||||
|
||||
Value Compiler::ExeEq::Eval(ExeContext& x) const
|
||||
{
|
||||
Value v1 = a->Eval(x);
|
||||
Value v2 = b->Eval(x);
|
||||
if(IsString(v1) && IsString(v2))
|
||||
return (String)v1 == (String)v2;
|
||||
if(IsNumber(v1) && IsNumber(v2))
|
||||
return (double)v1 == (double)v2;
|
||||
return AsString(v1) == AsString(v2);
|
||||
}
|
||||
|
||||
Value Compiler::ExeNeq::Eval(ExeContext& x) const
|
||||
{
|
||||
Value v1 = a->Eval(x);
|
||||
Value v2 = b->Eval(x);
|
||||
if(IsString(v1) && IsString(v2))
|
||||
return (String)v1 != (String)v2;
|
||||
if(IsNumber(v1) && IsNumber(v2))
|
||||
return (double)v1 != (double)v2;
|
||||
return AsString(v1) != AsString(v2);
|
||||
}
|
||||
|
||||
Value Compiler::ExeAnd::Eval(ExeContext& x) const
|
||||
{
|
||||
Value v1 = a->Eval(x);
|
||||
Value v2 = b->Eval(x);
|
||||
if(IsNumber(v1) && IsNumber(v2))
|
||||
return (int)v1 & (int)v2;
|
||||
return TypeMismatch("&");
|
||||
}
|
||||
|
||||
Value Compiler::ExeXor::Eval(ExeContext& x) const
|
||||
{
|
||||
Value v1 = a->Eval(x);
|
||||
Value v2 = b->Eval(x);
|
||||
if(IsNumber(v1) && IsNumber(v2))
|
||||
return (int)v1 ^ (int)v2;
|
||||
return TypeMismatch("^");
|
||||
}
|
||||
|
||||
Value Compiler::ExeOr::Eval(ExeContext& x) const
|
||||
{
|
||||
Value v1 = a->Eval(x);
|
||||
Value v2 = b->Eval(x);
|
||||
if(IsNumber(v1) && IsNumber(v2))
|
||||
return (int)v1 | (int)v2;
|
||||
return TypeMismatch("|");
|
||||
}
|
||||
|
||||
Value Compiler::ExeAnl::Eval(ExeContext& x) const
|
||||
{
|
||||
Value v1 = a->Eval(x);
|
||||
Value v2 = b->Eval(x);
|
||||
return IsTrue(v1) && IsTrue(v2);
|
||||
}
|
||||
|
||||
Value Compiler::ExeOrl::Eval(ExeContext& x) const
|
||||
{
|
||||
Value v1 = a->Eval(x);
|
||||
Value v2 = b->Eval(x);
|
||||
return IsTrue(v1) || IsTrue(v2);
|
||||
}
|
||||
|
||||
Value Compiler::ExeCond::Eval(ExeContext& x) const
|
||||
{
|
||||
if(cond->Eval(x))
|
||||
return ontrue->Eval(x);
|
||||
else
|
||||
if(onfalse)
|
||||
return onfalse->Eval(x);
|
||||
return Value();
|
||||
}
|
||||
|
||||
Value Compiler::ExeField::Eval(ExeContext& x) const
|
||||
{
|
||||
return value->Eval(x)[id];
|
||||
}
|
||||
|
||||
Value Compiler::ExeVarField::Eval(ExeContext& x) const
|
||||
{
|
||||
return x.stack[var_index][id];
|
||||
}
|
||||
|
||||
Value Compiler::ExeFn::Eval(ExeContext& x) const
|
||||
{
|
||||
Vector<Value> v;
|
||||
v.SetCount(arg.GetCount());
|
||||
for(int i = 0; i < arg.GetCount(); i++)
|
||||
v[i] = arg[i]->Eval(x);
|
||||
return (*fn)(v, x.renderer);
|
||||
}
|
||||
|
||||
Value Compiler::ExeLink::Eval(ExeContext& x) const
|
||||
{
|
||||
LTIMING("ExeLink");
|
||||
Vector<Value> v;
|
||||
v.SetCount(arg.GetCount());
|
||||
for(int i = 0; i < arg.GetCount(); i++) {
|
||||
LTIMING("arg eval");
|
||||
v[i] = arg[i]->Eval(x);
|
||||
}
|
||||
StringBuffer r;
|
||||
r << "\"";
|
||||
MakeLink(r, *part, v);
|
||||
r << "\"";
|
||||
return Raw(r);
|
||||
}
|
||||
|
||||
Value Compiler::ExeLinkVarField1::Eval(ExeContext& x) const
|
||||
{
|
||||
LTIMING("ExeLinkVarField");
|
||||
Vector<Value> v;
|
||||
v.Add(x.stack[var_index][id]);
|
||||
StringBuffer r;
|
||||
r << "\"";
|
||||
MakeLink(r, *part, v);
|
||||
r << "\"";
|
||||
return Raw(r);
|
||||
}
|
||||
|
||||
Value Compiler::ExeFirst::Eval(ExeContext& x) const
|
||||
{
|
||||
const LoopInfo& f = ValueTo<LoopInfo>(x.stack[var_index]);
|
||||
return f.first;
|
||||
}
|
||||
|
||||
Value Compiler::ExeLast::Eval(ExeContext& x) const
|
||||
{
|
||||
const LoopInfo& f = ValueTo<LoopInfo>(x.stack[var_index]);
|
||||
return f.last;
|
||||
}
|
||||
|
||||
Value Compiler::ExeIndex::Eval(ExeContext& x) const
|
||||
{
|
||||
const LoopInfo& f = ValueTo<LoopInfo>(x.stack[var_index]);
|
||||
return f.index;
|
||||
}
|
||||
|
||||
Value Compiler::ExeKey::Eval(ExeContext& x) const
|
||||
{
|
||||
const LoopInfo& f = ValueTo<LoopInfo>(x.stack[var_index]);
|
||||
return f.key;
|
||||
}
|
||||
|
||||
force_inline
|
||||
static void sCatAsString(StringBuffer& out, const Value& v)
|
||||
{
|
||||
LTIMING("sCatAsString");
|
||||
if(IsNull(v))
|
||||
return;
|
||||
if(v.Is<RawHtmlText>()) {
|
||||
LTIMING("Cat RawHtml");
|
||||
out.Cat(ValueTo<RawHtmlText>(v).text);
|
||||
}
|
||||
else {
|
||||
const char *s;
|
||||
String h;
|
||||
if(v.Is<String>())
|
||||
s = ValueTo<String>(v);
|
||||
else {
|
||||
LTIMING("AsString");
|
||||
h = AsString(v);
|
||||
s = h;
|
||||
}
|
||||
LTIMING("Escape html");
|
||||
while(*s) {
|
||||
if(*s == 31)
|
||||
out.Cat(" ");
|
||||
else
|
||||
if(*s == '<')
|
||||
out.Cat("<");
|
||||
else
|
||||
if(*s == '>')
|
||||
out.Cat(">");
|
||||
else
|
||||
if(*s == '&')
|
||||
out.Cat("&");
|
||||
else
|
||||
if(*s == '\"')
|
||||
out.Cat(""");
|
||||
else
|
||||
if((byte)*s < ' ')
|
||||
out.Cat(NFormat("&#%d;", (byte)*s));
|
||||
else
|
||||
out.Cat(*s);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Value Compiler::ExeFor::Eval(ExeContext& x) const
|
||||
{
|
||||
LTIMING("ExeFor");
|
||||
Value array = value->Eval(x);
|
||||
LLOG("ExeFor array: " << array);
|
||||
if(array.GetCount() == 0 && onempty)
|
||||
return onempty->Eval(x);
|
||||
ValueMap m;
|
||||
bool map = array.Is<ValueMap>();
|
||||
if(map)
|
||||
m = array;
|
||||
int q = x.stack.GetCount();
|
||||
x.stack.Add();
|
||||
x.stack.Add();
|
||||
for(int i = 0; i < array.GetCount(); i++) {
|
||||
x.stack[q] = array[i];
|
||||
LoopInfo f;
|
||||
f.first = i == 0;
|
||||
f.last = i == array.GetCount() - 1;
|
||||
f.index = i;
|
||||
f.key = map ? m.GetKeys()[i] : (Value)i;
|
||||
x.stack[q + 1] = RawToValue(f);
|
||||
sCatAsString(x.out, body->Eval(x));
|
||||
}
|
||||
x.stack.SetCount(q);
|
||||
return Value();
|
||||
}
|
||||
|
||||
Value Compiler::ExeBlock::Eval(ExeContext& x) const
|
||||
{
|
||||
int q = x.stack.GetCount();
|
||||
for(int i = 0; i < item.GetCount(); i++)
|
||||
sCatAsString(x.out, item[i]->Eval(x));
|
||||
x.stack.SetCount(q);
|
||||
return Value();
|
||||
}
|
||||
|
||||
String Render(const One<Exe>& exe, Renderer *r, Vector<Value>& var)
|
||||
{
|
||||
LTIMING("Render0");
|
||||
ExeContext x(var, r);
|
||||
Value v = exe->Eval(x);
|
||||
x.out.Cat(AsString(v));
|
||||
return x.out;
|
||||
}
|
||||
340
uppsrc/Skylark/Http.cpp
Normal file
340
uppsrc/Skylark/Http.cpp
Normal file
|
|
@ -0,0 +1,340 @@
|
|||
#include "Skylark.h"
|
||||
|
||||
#define LLOG(x) LOG(x)
|
||||
#define LTIMING(x) RTIMING(x)
|
||||
|
||||
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++;
|
||||
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)
|
||||
{
|
||||
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()) { // 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 << ::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)
|
||||
{
|
||||
Ux(id, RenderString(template_name));
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::UxSetValue(const char *id, const String& value)
|
||||
{
|
||||
Ux(String(">") + id, value);
|
||||
return *this;
|
||||
}
|
||||
142
uppsrc/Skylark/Http.h
Normal file
142
uppsrc/Skylark/Http.h
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
void MakeLink(StringBuffer& out, const Vector<String>& part, const Vector<Value>& arg);
|
||||
|
||||
class Renderer {
|
||||
protected:
|
||||
VectorMap<String, Value> var;
|
||||
int lang;
|
||||
|
||||
Renderer& Link(const char *id, void (*view)(Http&), const Vector<Value>& arg);
|
||||
const One<Exe>& GetTemplate(const char *template_name);
|
||||
|
||||
public:
|
||||
Renderer& operator()(const char *id, const Value& v) { var.Add(id, v); return *this; }
|
||||
Renderer& operator()(const ValueMap& map);
|
||||
Renderer& operator()(const char *id, void (*view)(Http&));
|
||||
Renderer& operator()(const char *id, void (*view)(Http&), const Value& arg1);
|
||||
Renderer& operator()(const char *id, void (*view)(Http&), const Value& arg1, const Value& arg2);
|
||||
|
||||
Renderer& operator()(const Sql& sql);
|
||||
Renderer& operator()(Fields rec);
|
||||
SqlUpdate Update(SqlId table);
|
||||
SqlInsert Insert(SqlId table);
|
||||
|
||||
Value operator[](const char *id) const { return var.Get(id, Null); }
|
||||
|
||||
String RenderString(const String& template_name);
|
||||
Value Render(const String& template_name) { return Raw(RenderString(template_name)); }
|
||||
|
||||
Renderer& Render(const char *id, const String& template_name);
|
||||
|
||||
Renderer() { lang = LNG_ENGLISH; }
|
||||
virtual ~Renderer();
|
||||
};
|
||||
|
||||
class Http : public Renderer {
|
||||
SkylarkApp& app;
|
||||
HttpHeader hdr;
|
||||
|
||||
String content;
|
||||
String viewid;
|
||||
|
||||
Vector<String> arg;
|
||||
String session_id;
|
||||
VectorMap<String, Value> session_var;
|
||||
bool session_dirty;
|
||||
|
||||
String redirect;
|
||||
int code;
|
||||
String code_text;
|
||||
String response;
|
||||
String content_type;
|
||||
String request_content_type;
|
||||
|
||||
VectorMap<String, String> cookies;
|
||||
|
||||
void ParseRequest(const char *s);
|
||||
void ReadMultiPart(const String& content);
|
||||
|
||||
String SessionFile(const String& sid);
|
||||
|
||||
void LoadSession();
|
||||
void SaveSession();
|
||||
|
||||
public:
|
||||
Http& operator()(const char *id, const Value& v) { var.Add(id, v); return *this; }
|
||||
Http& operator()(const ValueMap& map) { Renderer::operator()(map); return *this; }
|
||||
Http& operator()(const char *id, void (*view)(Http&)) { Renderer::operator()(id, view); return *this; }
|
||||
Http& operator()(const char *id, void (*view)(Http&), const Value& arg1) { Renderer::operator()(id, view, arg1); return *this; }
|
||||
Http& operator()(const char *id, void (*view)(Http&), const Value& arg1, const Value& arg2) { Renderer::operator()(id, view, arg1, arg2); return *this; }
|
||||
|
||||
Http& operator()(const Sql& sql) { Renderer::operator()(sql); return *this; }
|
||||
Http& operator()(Fields rec) { Renderer::operator()(rec); return *this; }
|
||||
Http& Render(const char *id, const String& template_name) { Renderer::Render(id, template_name); return *this; }
|
||||
Value Render(const String& template_name) { return Renderer::Render(template_name); }
|
||||
|
||||
void Dispatch(TcpSocket& socket);
|
||||
|
||||
String GetHeader(const char *s) const { return hdr[s]; }
|
||||
int GetLength() const { return atoi(GetHeader("content-length")); }
|
||||
|
||||
String GetViewId() const { return viewid; }
|
||||
|
||||
Value operator[](const char *id) const { return Renderer::operator[](id); }
|
||||
String operator[](int i) const { return i >= 0 && i < arg.GetCount() ? arg[i] : String(); }
|
||||
|
||||
int Int(const char *id) const;
|
||||
int Int(int i) const;
|
||||
|
||||
int GetParamCount() const { return arg.GetCount(); }
|
||||
|
||||
Http& ContentType(const char *s) { content_type = s; return *this; }
|
||||
|
||||
Http& Content(const char *s, const Value& data);
|
||||
Http& operator<<(const Value& s);
|
||||
|
||||
Http& SetRawCookie(const char *id, const String& value,
|
||||
Time expires = Null, const char *path = NULL,
|
||||
const char *domain = NULL, bool secure = false, bool httponly = false);
|
||||
Http& SetCookie(const char *id, const String& value,
|
||||
Time expires = Null, const char *path = NULL,
|
||||
const char *domain = NULL, bool secure = false, bool httponly = false);
|
||||
|
||||
Http& ClearSession();
|
||||
Http& SessionSet(const char *id, const Value& value);
|
||||
|
||||
Http& NewIdentity() { SessionSet("__identity__", Null); return *this; }
|
||||
Http& NewSessionId();
|
||||
Http& SetLanguage(int lang);
|
||||
|
||||
Http& Response(int code_, const String& ctext) { code = code_; code_text = ctext; return *this; }
|
||||
|
||||
Http& RenderResult(const char *template_name);
|
||||
Http& Redirect(const char *url, int code_ = 302) { code = code_; redirect = url; return *this; }
|
||||
Http& Redirect(void (*view)(Http&), const Vector<Value>& arg);
|
||||
Http& Redirect(void (*view)(Http&));
|
||||
Http& Redirect(void (*view)(Http&), const Value& v1);
|
||||
Http& Redirect(void (*view)(Http&), const Value& v1, const Value& v2);
|
||||
|
||||
Http& Ux(const char *id, const String& text);
|
||||
Http& UxRender(const char *id, const char *template_name);
|
||||
Http& UxSetValue(const char *id, const String& value);
|
||||
|
||||
String GetResponse() const { return response; }
|
||||
|
||||
const SkylarkApp& App() const { return app; }
|
||||
|
||||
Http(SkylarkApp& app);
|
||||
};
|
||||
|
||||
String HttpResponse(int code, const char *phrase, const String& data, const char *content_type = NULL);
|
||||
|
||||
void RegisterView(void (*view)(Http&), const char *id, const char *path);
|
||||
|
||||
#define URL_VIEW(name, path) void name(Http& http); INITBLOCK { RegisterView(name, #name, path); } void name(Http& http)
|
||||
#define SKYLARK(name, path) void name(Http& http); INITBLOCK { RegisterView(name, #name, path); } void name(Http& http)
|
||||
|
||||
Vector<String> *GetUrlViewLinkParts(const String& id);
|
||||
|
||||
String MakeLink(void (*view)(Http&), const Vector<Value>& arg);
|
||||
|
||||
enum {
|
||||
SESSION_FORMAT_BINARY, SESSION_FORMAT_JSON, SESSION_FORMAT_XML
|
||||
};
|
||||
156
uppsrc/Skylark/Optimize.cpp
Normal file
156
uppsrc/Skylark/Optimize.cpp
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
#include "Skylark.h"
|
||||
|
||||
#define LLOG(x) // DLOG(x)
|
||||
|
||||
void Compiler::Iterate(Vector< One<Exe> >& a, Callback1< One<Exe>& > op)
|
||||
{
|
||||
for(int i = 0; i < a.GetCount(); i++)
|
||||
op(a[i]);
|
||||
}
|
||||
|
||||
void Compiler::OptimizeConst(One<Exe>& exe)
|
||||
{
|
||||
One<Exe> oxe;
|
||||
Vector<Value> stack;
|
||||
ExeContext x(stack);
|
||||
oxe.Create<ExeConst>().value = exe->Eval(x);
|
||||
LLOG("OPTIMIZED constant: " << exe->Eval(stack, out));
|
||||
exe = oxe;
|
||||
optimized = true;
|
||||
}
|
||||
|
||||
void Compiler::Iterate(One<Exe>& exe, Callback1< One<Exe>& > op)
|
||||
{
|
||||
if(ExeBlock *e = dynamic_cast<ExeBlock *>(~exe)) {
|
||||
Iterate(e->item, op);
|
||||
return;
|
||||
}
|
||||
if(ExeFor *e = dynamic_cast<ExeFor *>(~exe)) {
|
||||
op(e->value);
|
||||
op(e->body);
|
||||
op(e->onempty);
|
||||
return;
|
||||
}
|
||||
if(ExeLink *e = dynamic_cast<ExeLink *>(~exe)) {
|
||||
Iterate(e->arg, op);
|
||||
return;
|
||||
}
|
||||
if(ExeFn *e = dynamic_cast<ExeFn *>(~exe)) {
|
||||
Iterate(e->arg, op);
|
||||
return;
|
||||
}
|
||||
if(ExeField *e = dynamic_cast<ExeField *>(~exe)) {
|
||||
op(e->value);
|
||||
return;
|
||||
}
|
||||
if(ExeCond *e = dynamic_cast<ExeCond *>(~exe)) {
|
||||
op(e->cond);
|
||||
op(e->ontrue);
|
||||
op(e->onfalse);
|
||||
return;
|
||||
}
|
||||
if(ExeMap *e = dynamic_cast<ExeMap *>(~exe)) {
|
||||
Iterate(e->key, op);
|
||||
Iterate(e->value, op);
|
||||
return;
|
||||
}
|
||||
if(ExeArray *e = dynamic_cast<ExeArray *>(~exe)) {
|
||||
Iterate(e->item, op);
|
||||
return;
|
||||
}
|
||||
if(Exe2 *e = dynamic_cast<Exe2 *>(~exe)) {
|
||||
op(e->a);
|
||||
op(e->b);
|
||||
return;
|
||||
}
|
||||
if(Exe1 *e = dynamic_cast<Exe1 *>(~exe)) {
|
||||
op(e->a);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Compiler::CountNodes(One<Exe>& exe)
|
||||
{
|
||||
count_node++;
|
||||
Iterate(exe, THISBACK(CountNodes));
|
||||
}
|
||||
|
||||
int Compiler::GetNodeCount(One<Exe>& exe)
|
||||
{
|
||||
count_node = 0;
|
||||
CountNodes(exe);
|
||||
return count_node;
|
||||
}
|
||||
|
||||
void Compiler::Optimize(One<Exe>& exe)
|
||||
{
|
||||
if(!exe)
|
||||
return;
|
||||
bool optimized2 = false;
|
||||
#ifdef _DEBUG0
|
||||
String name = "Optimizing " + String(typeid(*~exe).name());
|
||||
LLOGBLOCK(name);
|
||||
#endif
|
||||
do {
|
||||
optimized = false;
|
||||
Iterate(exe, THISBACK(Optimize));
|
||||
if(ExeField *e = dynamic_cast<ExeField *>(~exe)) {
|
||||
if(ExeVar *e1 = dynamic_cast<ExeVar *>(~e->value)) {
|
||||
One<Exe> oxe;
|
||||
ExeVarField& vf = oxe.Create<ExeVarField>();
|
||||
vf.id = e->id;
|
||||
vf.var_index = e1->var_index;
|
||||
exe = oxe;
|
||||
optimized = true;
|
||||
LLOG("OPTIMIZED ExeVarField");
|
||||
}
|
||||
}
|
||||
if(ExeLink *e = dynamic_cast<ExeLink *>(~exe)) {
|
||||
ExeVarField *e1;
|
||||
if(e->arg.GetCount() == 1 && (e1 = dynamic_cast<ExeVarField *>(~e->arg[0]))) {
|
||||
One<Exe> oxe;
|
||||
ExeLinkVarField1& o = oxe.Create<ExeLinkVarField1>();
|
||||
o.id = e1->id;
|
||||
o.var_index = e1->var_index;
|
||||
o.part = e->part;
|
||||
exe = oxe;
|
||||
optimized = true;
|
||||
LLOG("OPTIMIZED ExeLinkVarField1");
|
||||
}
|
||||
}
|
||||
if(ExeBlock *e = dynamic_cast<ExeBlock *>(~exe)) {
|
||||
Vector< One<Exe> >& m = e->item;
|
||||
int i = 0;
|
||||
while(i < m.GetCount() - 1) {
|
||||
ExeConst *e1 = dynamic_cast<ExeConst *>(~m[i]);
|
||||
ExeConst *e2 = dynamic_cast<ExeConst *>(~m[i + 1]);
|
||||
if(e1 && e2 && e1->value.Is<RawHtmlText>() && e2->value.Is<RawHtmlText>()) {
|
||||
RawHtmlText t;
|
||||
t.text = ValueTo<RawHtmlText>(e1->value).text + ValueTo<RawHtmlText>(e2->value).text;
|
||||
One<Exe> oxe;
|
||||
oxe.Create<ExeConst>().value = RawToValue(t);
|
||||
m[i] = oxe;
|
||||
m.Remove(i + 1);
|
||||
optimized = true;
|
||||
LLOG("OPTIMIZED ExeBlock constant folding");
|
||||
}
|
||||
else
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if(Exe2 *e = dynamic_cast<Exe2 *>(~exe)) {
|
||||
if(dynamic_cast<ExeConst *>(~e->a) && dynamic_cast<ExeConst *>(~e->b))
|
||||
OptimizeConst(exe);
|
||||
}
|
||||
else
|
||||
if(Exe1 *e = dynamic_cast<Exe1 *>(~exe)) {
|
||||
if(dynamic_cast<ExeConst *>(~e->a))
|
||||
OptimizeConst(exe);
|
||||
}
|
||||
optimized2 = optimized2 || optimized;
|
||||
LLOG("---------");
|
||||
}
|
||||
while(optimized);
|
||||
|
||||
optimized = optimized2;
|
||||
}
|
||||
126
uppsrc/Skylark/Preprocess.cpp
Normal file
126
uppsrc/Skylark/Preprocess.cpp
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
#include "Skylark.h"
|
||||
|
||||
String GetFileOnPath1(const char *file, const char *path)
|
||||
{
|
||||
String r = GetFileOnPath(file, path);
|
||||
return r;
|
||||
}
|
||||
|
||||
String LoadTemplate(const char *file, const String& search_path, int lang)
|
||||
{
|
||||
String f = file;
|
||||
String path = GetFileOnPath1(f + '.' + ToLower(LNGAsText(lang)) + ".witz", search_path);
|
||||
if(IsNull(path)) {
|
||||
path = GetFileOnPath1(f + ".witz", search_path);
|
||||
if(IsNull(path)) {
|
||||
path = GetFileOnPath1(f, search_path);
|
||||
if(IsNull(path))
|
||||
return Null;
|
||||
}
|
||||
}
|
||||
FileIn in(path);
|
||||
String r;
|
||||
while(in && !in.IsEof()) {
|
||||
String line = in.GetLine();
|
||||
CParser p(line);
|
||||
if(p.Char('#') && p.Id("include")) {
|
||||
String file = p.GetPtr();
|
||||
int q = file.Find(' ');
|
||||
if(q < 0)
|
||||
q = file.Find('\t');
|
||||
if(q >= 0)
|
||||
file = file.Mid(0, q);
|
||||
r << LoadTemplate(file, GetFileFolder(path) + ';' + search_path, lang);
|
||||
}
|
||||
else
|
||||
r << line;
|
||||
r << "\r\n";
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
VectorMap<String, String> GetTemplateDefs(const char *file, int lang)
|
||||
{
|
||||
String s = LoadTemplate(file, SkylarkApp::Config().path, lang);
|
||||
VectorMap<String, String> def;
|
||||
int ti = def.FindAdd("MAIN");
|
||||
StringStream ss(s);
|
||||
while(!ss.IsEof()) {
|
||||
String line = ss.GetLine();
|
||||
CParser p(line);
|
||||
if(p.Char('#') && p.Id("define")) {
|
||||
String id = "MAIN";
|
||||
if(p.IsId())
|
||||
id = p.ReadId();
|
||||
ti = def.FindAdd(id);
|
||||
def[ti].Clear();
|
||||
def[ti] << p.GetPtr();
|
||||
}
|
||||
else
|
||||
def[ti] << line << "\r\n";
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
int CharFilterIsCrLf(int c)
|
||||
{
|
||||
return c == '\r' || c == '\n' ? c : 0;
|
||||
}
|
||||
|
||||
String ReplaceVars(const String& src, const VectorMap<String, String>& def, int chr)
|
||||
{
|
||||
Index<String> expanded;
|
||||
String r = src;
|
||||
bool again;
|
||||
do {
|
||||
again = false;
|
||||
String rr;
|
||||
const char *s = ~r;
|
||||
for(;;) {
|
||||
const char *q = strchr(s, chr);
|
||||
if(q) {
|
||||
rr.Cat(s, q);
|
||||
CParser p(q + 1);
|
||||
if(p.Char(chr) || !p.IsId())
|
||||
rr << (char)20;
|
||||
else {
|
||||
String id = p.ReadId();
|
||||
if(expanded.Find(id) >= 0)
|
||||
rr.Cat(q, p.GetSpacePtr());
|
||||
else {
|
||||
rr.Cat(def.Get(id, Null));
|
||||
expanded.Add(id);
|
||||
again = true;
|
||||
}
|
||||
}
|
||||
s = p.GetSpacePtr();
|
||||
}
|
||||
else {
|
||||
rr.Cat(s, r.End());
|
||||
break;
|
||||
}
|
||||
}
|
||||
r = rr;
|
||||
}
|
||||
while(again);
|
||||
return r;
|
||||
}
|
||||
|
||||
int CharFilter20toHash(int c)
|
||||
{
|
||||
return c == 20 ? '#' : c;
|
||||
}
|
||||
|
||||
String GetPreprocessedTemplate(const String& name, int lang)
|
||||
{
|
||||
String id = "MAIN";
|
||||
String file = name;
|
||||
int q = file.Find(':');
|
||||
if(q >= 0) {
|
||||
id = file.Mid(q + 1);
|
||||
file = file.Mid(0, q);
|
||||
}
|
||||
VectorMap<String, String> def = GetTemplateDefs(file, lang);
|
||||
String r = Filter(ReplaceVars(def.Get(id, Null), def, '#'), CharFilter20toHash);
|
||||
return Join(Split(r, CharFilterIsCrLf), "\r\n");
|
||||
}
|
||||
76
uppsrc/Skylark/Renderer.cpp
Normal file
76
uppsrc/Skylark/Renderer.cpp
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#include "Skylark.h"
|
||||
|
||||
#define LLOG(x) DLOG(x)
|
||||
#define LTIMING(x) // RTIMING(x)
|
||||
|
||||
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;
|
||||
}
|
||||
String sgn = s;
|
||||
LLOG("Trying to retrieve " << sgn << " from cache");
|
||||
DDUMPM(var);
|
||||
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 ::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()
|
||||
{
|
||||
}
|
||||
152
uppsrc/Skylark/Session.cpp
Normal file
152
uppsrc/Skylark/Session.cpp
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
#include "Skylark.h"
|
||||
|
||||
#define LLOG(x) //DLOG(x)
|
||||
#define LDUMPC(x) //DDUMPC(x)
|
||||
#define LDUMPM(x) //DDUMPM(x)
|
||||
#define LLOGHEX(x) //DLOGHEX(x)
|
||||
|
||||
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;
|
||||
}
|
||||
LLOG("Loaded session: " << session_id);
|
||||
LDUMPM(session_var);
|
||||
for(int i = 0; i < session_var.GetCount(); i++)
|
||||
var.Add(session_var.GetKey(i), session_var[i]);
|
||||
}
|
||||
|
||||
thread__ int s_exp;
|
||||
|
||||
void Http::SaveSession()
|
||||
{
|
||||
DDUMPM(session_var);
|
||||
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);
|
||||
}
|
||||
LLOG("Stored session: " << session_id);
|
||||
LDUMPM(session_var);
|
||||
|
||||
if((s_exp++ % 1000) == 0) {
|
||||
Time tm = GetSysTime() - cfg.expire;
|
||||
LLOG("Expiring sessions older than " << tm);
|
||||
if(cfg.table.IsNull()) {
|
||||
FindFile ff(AppendFileName(cfg.dir, "*.*"));
|
||||
Vector<String> todelete;
|
||||
while(ff) {
|
||||
DDUMP(ff.GetPath());
|
||||
DDUMP(Time(ff.GetLastWriteTime()));
|
||||
if(ff.GetLastWriteTime() < tm)
|
||||
todelete.Add(ff.GetPath());
|
||||
ff.Next();
|
||||
}
|
||||
DDUMPC(todelete);
|
||||
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)
|
||||
{
|
||||
DLOG("SessionSet " << id << " = " << value);
|
||||
if(IsNull(session_id))
|
||||
NewSessionId();
|
||||
session_var.GetAdd(id) = value;
|
||||
var.GetAdd(id) = value;
|
||||
DDUMPM(var);
|
||||
session_dirty = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::NewSessionId()
|
||||
{
|
||||
session_id = AsString(Uuid::Create());
|
||||
session_dirty = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::SetLanguage(int lang_)
|
||||
{
|
||||
DDUMP(lang_);
|
||||
lang = lang_;
|
||||
Upp::SetLanguage(lang_);
|
||||
SessionSet("__lang__", lang);
|
||||
SessionSet("language", ToLower(LNGAsText(lang)));
|
||||
return *this;
|
||||
}
|
||||
90
uppsrc/Skylark/Skylark.h
Normal file
90
uppsrc/Skylark/Skylark.h
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
#ifndef _Wpp_Wpp_h
|
||||
#define _Wpp_Wpp_h
|
||||
|
||||
#include <Draw/Draw.h>
|
||||
#include <plugin/png/png.h>
|
||||
#include <plugin/jpg/jpg.h>
|
||||
#include <Sql/Sql.h>
|
||||
|
||||
using namespace Upp;
|
||||
|
||||
class Renderer;
|
||||
class Http;
|
||||
|
||||
struct SessionConfig {
|
||||
String cookie;
|
||||
String dir;
|
||||
int format;
|
||||
SqlId table, id_column, data_column, lastwrite_column;
|
||||
int expire;
|
||||
|
||||
SessionConfig();
|
||||
};
|
||||
|
||||
struct AuthExc : Exc {
|
||||
AuthExc(const String& s) : Exc(s) {}
|
||||
};
|
||||
|
||||
struct SkylarkConfig {
|
||||
String root;
|
||||
VectorMap<String, String> view_var;
|
||||
String path;
|
||||
SessionConfig session;
|
||||
int threads;
|
||||
int port;
|
||||
int prefork;
|
||||
int timeout;
|
||||
bool use_caching;
|
||||
};
|
||||
|
||||
class SkylarkApp : protected SkylarkConfig {
|
||||
TcpSocket server;
|
||||
Mutex accept_mutex;
|
||||
int main_pid;
|
||||
Vector<int> child_pid;
|
||||
bool quit;
|
||||
|
||||
|
||||
void ThreadRun();
|
||||
void Broadcast(int signal);
|
||||
void Signal(int signal);
|
||||
static void SignalHandler(int signal);
|
||||
void Main();
|
||||
|
||||
void FinalizeViews();
|
||||
|
||||
static SkylarkApp *app;
|
||||
|
||||
#ifdef PLATFORM_WIN32
|
||||
static BOOL WINAPI CtrlCHandlerRoutine(__in DWORD dwCtrlType);
|
||||
#endif
|
||||
|
||||
typedef SkylarkApp CLASSNAME;
|
||||
|
||||
friend class Http;
|
||||
|
||||
public:
|
||||
virtual void SqlError(Http& http);
|
||||
virtual void InternalError(Http& http);
|
||||
virtual void NotFound(Http& http);
|
||||
virtual void Unauthorized(Http& http);
|
||||
|
||||
virtual void WorkThread() = 0;
|
||||
|
||||
void RunThread();
|
||||
|
||||
void Run();
|
||||
|
||||
static SkylarkApp& TheApp();
|
||||
static const SkylarkConfig& Config();
|
||||
|
||||
SkylarkApp();
|
||||
virtual ~SkylarkApp();
|
||||
};
|
||||
|
||||
void SetStaticPath(const String& path);
|
||||
|
||||
#include "Witz.h"
|
||||
#include "Http.h"
|
||||
|
||||
#endif
|
||||
45
uppsrc/Skylark/Skylark.upp
Normal file
45
uppsrc/Skylark/Skylark.upp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
description "\377B128,0,0";
|
||||
|
||||
uses
|
||||
Draw,
|
||||
plugin\png,
|
||||
plugin\jpg,
|
||||
Sql;
|
||||
|
||||
file
|
||||
Skylark.h,
|
||||
App.cpp,
|
||||
Witz.h,
|
||||
Preprocess.cpp,
|
||||
Compile.cpp,
|
||||
Optimize.cpp,
|
||||
Exe.cpp,
|
||||
Http.h,
|
||||
Renderer.cpp,
|
||||
Sql.cpp,
|
||||
Http.cpp,
|
||||
Session.cpp,
|
||||
Dispatch.cpp,
|
||||
StdLib.icpp,
|
||||
Util readonly separator,
|
||||
Static.icpp,
|
||||
Client readonly separator,
|
||||
Base.witz,
|
||||
skylark.js;
|
||||
|
||||
custom() ".css",
|
||||
"cp $(PATH) $(EXEDIR)/static/$(PACKAGE)/$(RELPATH)",
|
||||
"$(EXEDIR)/static/$(PACKAGE)/$(RELPATH)";
|
||||
|
||||
custom() ".html",
|
||||
"cp $(PATH) $(EXEDIR)/static/$(PACKAGE)/$(RELPATH)",
|
||||
"$(EXEDIR)/static/$(PACKAGE)/$(RELPATH)";
|
||||
|
||||
custom() ".js",
|
||||
"cp $(PATH) $(EXEDIR)/static/$(PACKAGE)/$(RELPATH)",
|
||||
"$(EXEDIR)/static/$(PACKAGE)/$(RELPATH)";
|
||||
|
||||
custom() ".whtml",
|
||||
"cp $(PATH) $(EXEDIR)/template/$(PACKAGE)/$(RELPATH)",
|
||||
"$(EXEDIR)/template/$(PACKAGE)/$(RELPATH)";
|
||||
|
||||
56
uppsrc/Skylark/Sql.cpp
Normal file
56
uppsrc/Skylark/Sql.cpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#include "Skylark.h"
|
||||
|
||||
struct sFieldsToRenderer : public FieldOperator {
|
||||
Renderer& http;
|
||||
|
||||
void Field(const char *name, Ref f) {
|
||||
http(name, f);
|
||||
}
|
||||
|
||||
sFieldsToRenderer(Renderer& http) : http(http) {}
|
||||
};
|
||||
|
||||
Renderer& Renderer::operator()(Fields rec)
|
||||
{
|
||||
sFieldsToRenderer x(*this);
|
||||
rec(x);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Renderer& Renderer::operator()(const Sql& sql)
|
||||
{
|
||||
int n = sql.GetColumns();
|
||||
for(int i = 0; i < n; i++)
|
||||
(*this)(sql.GetColumnInfo(i).name, sql[i]);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SqlUpdate Renderer::Update(SqlId table)
|
||||
{
|
||||
Vector<String> col = GetSchColumns(~table);
|
||||
SqlUpdate u(table);
|
||||
for(int i = 0; i < col.GetCount(); i++) {
|
||||
String c = col[i];
|
||||
int q = var.Find(c);
|
||||
if(q < 0)
|
||||
q = var.Find(ToLower(c));
|
||||
if(q >= 0)
|
||||
u(c, var[q]);
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
SqlInsert Renderer::Insert(SqlId table)
|
||||
{
|
||||
Vector<String> col = GetSchColumns(~table);
|
||||
SqlInsert y(table);
|
||||
for(int i = 0; i < col.GetCount(); i++) {
|
||||
String c = col[i];
|
||||
int q = var.Find(c);
|
||||
if(q < 0)
|
||||
q = var.Find(ToLower(c));
|
||||
if(q >= 0)
|
||||
y(c, var[q]);
|
||||
}
|
||||
return y;
|
||||
}
|
||||
46
uppsrc/Skylark/Static.icpp
Normal file
46
uppsrc/Skylark/Static.icpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#include "Skylark.h"
|
||||
|
||||
// add static page caching?
|
||||
|
||||
SKYLARK(ServeStaticPage, "static/**")
|
||||
{
|
||||
String file;
|
||||
for(int i = 0; i < http.GetParamCount(); i++) {
|
||||
if(i)
|
||||
file << '/';
|
||||
file << http[i];
|
||||
}
|
||||
DDUMP(file);
|
||||
DDUMP(SkylarkApp::Config().path);
|
||||
String path = GetFileOnPath(file, SkylarkApp::Config().path, false);
|
||||
if(path.GetCount()) {
|
||||
String ext = ToLower(GetFileExt(file));
|
||||
DDUMP(ext);
|
||||
String type = "text";
|
||||
if(ext == ".css")
|
||||
type = "text/css";
|
||||
else
|
||||
if(ext == ".js")
|
||||
type = "text/javascript";
|
||||
else
|
||||
if(ext == ".png" || ext == ".jpg" || ext == ".gif")
|
||||
type = "image/" + ext.Mid(1);
|
||||
http.Content(type, LoadFile(path));
|
||||
}
|
||||
}
|
||||
|
||||
SKYLARK(ServeIml, "iml/*")
|
||||
{
|
||||
String name = http[0];
|
||||
int q = name.Find('.');
|
||||
String ext;
|
||||
if(q >= 0) {
|
||||
ext = name.Mid(q + 1);
|
||||
name = name.Mid(0, q);
|
||||
}
|
||||
Image m = GetImlImage(name);
|
||||
if(ext == "jpg" || ext == "JPG" || ext == "jpeg" || ext == "JPEG")
|
||||
http.Content("image/jpeg", JPGEncoder().SaveString(m));
|
||||
else
|
||||
http.Content("image/png", PNGEncoder().SaveString(m));
|
||||
}
|
||||
47
uppsrc/Skylark/StdLib.icpp
Normal file
47
uppsrc/Skylark/StdLib.icpp
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#include "Skylark.h"
|
||||
|
||||
Value Cycle(const Vector<Value>& arg, const Renderer *)
|
||||
{
|
||||
if(arg.GetCount() < 3 && !IsNumber(arg[0]))
|
||||
return String();
|
||||
return arg[1 + int(arg[0]) % (arg.GetCount() - 1)];
|
||||
}
|
||||
|
||||
Value RawFn(const Vector<Value>& arg, const Renderer *)
|
||||
{
|
||||
RawHtmlText r;
|
||||
for(int i = 0; i < arg.GetCount(); i++)
|
||||
r.text.Cat(AsString(arg[i]));
|
||||
return RawToValue(r);
|
||||
}
|
||||
|
||||
String GetIdentity(const Renderer *r)
|
||||
{
|
||||
// This ugly hack expects that __identity__ is always present in r->var
|
||||
Http *http = const_cast<Http *>(dynamic_cast<const Http *>(r));
|
||||
if(!http)
|
||||
throw Exc("invalid POST identity call");
|
||||
String s = (*http)["__identity__"];
|
||||
if(s.GetCount())
|
||||
return s;
|
||||
s = AsString(Uuid::Create());
|
||||
http->SessionSet("__identity__", s);
|
||||
return s;
|
||||
}
|
||||
|
||||
Value PostIdentity(const Vector<Value>&, const Renderer *r)
|
||||
{
|
||||
return Raw("<input type=\"hidden\" name=\"__post_identity__\" value=\"" + GetIdentity(r) + "\">");
|
||||
}
|
||||
|
||||
Value JsIdentity(const Vector<Value>&, const Renderer *r)
|
||||
{
|
||||
return Raw("<script type=\"text/javascript\">var __js_identity__ = \"" + GetIdentity(r) + "\"</script>");
|
||||
}
|
||||
|
||||
INITBLOCK {
|
||||
Compiler::Register("cycle", Cycle);
|
||||
Compiler::Register("raw", RawFn);
|
||||
Compiler::Register("post_identity", PostIdentity);
|
||||
Compiler::Register("js_identity", JsIdentity);
|
||||
};
|
||||
228
uppsrc/Skylark/Witz.h
Normal file
228
uppsrc/Skylark/Witz.h
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
struct ExeContext {
|
||||
const Renderer *renderer;
|
||||
Vector<Value>& stack;
|
||||
StringBuffer out;
|
||||
|
||||
ExeContext(Vector<Value>& stack, const Renderer *r = NULL) : renderer(r), stack(stack) {}
|
||||
};
|
||||
|
||||
struct Exe {
|
||||
virtual Value Eval(ExeContext& x) const { return Value(); }
|
||||
virtual ~Exe() {}
|
||||
};
|
||||
|
||||
struct RawHtmlText {
|
||||
String text;
|
||||
};
|
||||
|
||||
Value Raw(const String& s);
|
||||
|
||||
struct Compiler {
|
||||
static VectorMap<String, Value (*)(const Vector<Value>&, const Renderer *)>& functions();
|
||||
|
||||
static bool IsTrue(const Value& v);
|
||||
|
||||
struct Exe1 : Exe {
|
||||
One<Exe> a;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
static One<Exe> Create(One<Exe> a) {
|
||||
One<Exe> rr;
|
||||
T& m = rr.Create<T>();
|
||||
m.a = a;
|
||||
return rr;
|
||||
}
|
||||
|
||||
struct Exe2 : Exe {
|
||||
One<Exe> a;
|
||||
One<Exe> b;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
static One<Exe> Create(One<Exe> a, One<Exe> b) {
|
||||
One<Exe> rr;
|
||||
T& m = rr.Create<T>();
|
||||
m.a = a;
|
||||
m.b = b;
|
||||
return rr;
|
||||
}
|
||||
|
||||
struct ExeVar : Exe {
|
||||
int var_index;
|
||||
|
||||
virtual Value Eval(ExeContext& x) const;
|
||||
};
|
||||
|
||||
struct ExeConst : Exe {
|
||||
Value value;
|
||||
|
||||
virtual Value Eval(ExeContext& x) const;
|
||||
};
|
||||
|
||||
struct ExeArray : Exe {
|
||||
Vector< One<Exe> > item;
|
||||
|
||||
virtual Value Eval(ExeContext& x) const;
|
||||
};
|
||||
|
||||
struct ExeMap : Exe {
|
||||
Vector< One<Exe> > key;
|
||||
Vector< One<Exe> > value;
|
||||
|
||||
virtual Value Eval(ExeContext& x) const;
|
||||
};
|
||||
|
||||
struct ExeNot : Exe1 { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeNeg : Exe1 { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeMul : Exe2 { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeDiv : Exe2 { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeMod : Exe2 { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeAdd : Exe2 { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeSub : Exe2 { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeSll : Exe2 { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeSra : Exe2 { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeSrl : Exe2 { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeLt : Exe2 { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeLte : Exe2 { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeEq : Exe2 { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeNeq : Exe2 { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeAnd : Exe2 { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeXor : Exe2 { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeOr : Exe2 { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeAnl : Exe2 { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeOrl : Exe2 { virtual Value Eval(ExeContext& x) const; };
|
||||
|
||||
struct ExeCond : Exe {
|
||||
One<Exe> cond;
|
||||
One<Exe> ontrue;
|
||||
One<Exe> onfalse;
|
||||
|
||||
virtual Value Eval(ExeContext& x) const;
|
||||
};
|
||||
|
||||
struct ExeField : Exe {
|
||||
One<Exe> value;
|
||||
String id;
|
||||
|
||||
virtual Value Eval(ExeContext& x) const;
|
||||
};
|
||||
|
||||
struct ExeVarField : Exe {
|
||||
int var_index;
|
||||
String id;
|
||||
|
||||
virtual Value Eval(ExeContext& x) const;
|
||||
};
|
||||
|
||||
struct ExeFn : Exe {
|
||||
Value (*fn)(const Vector<Value>&, const Renderer *);
|
||||
|
||||
Vector< One<Exe> > arg;
|
||||
|
||||
virtual Value Eval(ExeContext& x) const;
|
||||
};
|
||||
|
||||
struct ExeLink : Exe {
|
||||
const Vector<String> *part;
|
||||
|
||||
Vector< One<Exe> > arg;
|
||||
|
||||
virtual Value Eval(ExeContext& x) const;
|
||||
};
|
||||
|
||||
struct ExeLinkVarField1 : Exe {
|
||||
const Vector<String> *part;
|
||||
|
||||
int var_index;
|
||||
String id;
|
||||
|
||||
virtual Value Eval(ExeContext& x) const;
|
||||
};
|
||||
|
||||
struct LoopInfo {
|
||||
bool first;
|
||||
bool last;
|
||||
int index;
|
||||
Value key;
|
||||
};
|
||||
|
||||
struct ExeFirst : ExeVar { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeLast : ExeVar { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeIndex : ExeVar { virtual Value Eval(ExeContext& x) const; };
|
||||
struct ExeKey : ExeVar { virtual Value Eval(ExeContext& x) const; };
|
||||
|
||||
struct ExeFor : Exe {
|
||||
One<Exe> value;
|
||||
One<Exe> body;
|
||||
One<Exe> onempty;
|
||||
|
||||
virtual Value Eval(ExeContext& x) const;
|
||||
};
|
||||
|
||||
struct ExeBlock : Exe {
|
||||
Vector< One<Exe> > item;
|
||||
|
||||
void AddText(const char *b, const char *s);
|
||||
|
||||
virtual Value Eval(ExeContext& x) const;
|
||||
};
|
||||
|
||||
struct CompiledTemplate {
|
||||
String path;
|
||||
Index<String> var;
|
||||
|
||||
Exe program;
|
||||
|
||||
Vector<Value> data;
|
||||
Value Eval();
|
||||
};
|
||||
|
||||
CParser p;
|
||||
Index<String> var;
|
||||
Vector<bool> forvar;
|
||||
bool optimized;
|
||||
int count_node;
|
||||
|
||||
int ForVar(String id, int i);
|
||||
|
||||
One<Exe> Prim();
|
||||
One<Exe> Mul();
|
||||
One<Exe> Add();
|
||||
One<Exe> Shift();
|
||||
One<Exe> Rel();
|
||||
One<Exe> Eq();
|
||||
One<Exe> And();
|
||||
One<Exe> Xor();
|
||||
One<Exe> Or();
|
||||
One<Exe> LogAnd();
|
||||
One<Exe> LogOr();
|
||||
One<Exe> Conditional();
|
||||
|
||||
One<Exe> Exp();
|
||||
|
||||
One<Exe> Block();
|
||||
|
||||
typedef Compiler CLASSNAME;
|
||||
|
||||
void Iterate(Vector< One<Exe> >& a, Callback1< One<Exe>& > op);
|
||||
void Iterate(One<Exe>& exe, Callback1< One<Exe>& > op);
|
||||
void OptimizeConst(One<Exe>& exe);
|
||||
void Optimize(One<Exe>& exe);
|
||||
|
||||
void CountNodes(One<Exe>& exe);
|
||||
|
||||
int GetNodeCount(One<Exe>& exe);
|
||||
|
||||
static void Register(const String& id, Value (*fn)(const Vector<Value>&, const Renderer *));
|
||||
|
||||
Compiler(const char *code, const Index<String>& var) : p(code), var(var, 1) { forvar.SetCount(var.GetCount(), false); }
|
||||
};
|
||||
|
||||
One<Exe> Compile(const char *code, const Index<String>& vars);
|
||||
|
||||
String Render(const One<Exe>& exe, Renderer *r, Vector<Value>& var);
|
||||
|
||||
String GetPreprocessedTemplate(const String& name, int lang);
|
||||
|
||||
String ReplaceVars(const String& src, const VectorMap<String, String>& def, int chr);
|
||||
13
uppsrc/Skylark/init
Normal file
13
uppsrc/Skylark/init
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef _Skylark_icpp_init_stub
|
||||
#define _Skylark_icpp_init_stub
|
||||
#include "Draw/init"
|
||||
#include "plugin\png/init"
|
||||
#include "plugin\jpg/init"
|
||||
#include "Sql/init"
|
||||
#define BLITZ_INDEX__ F5727c7d5aaf9771a467d28f05356763c
|
||||
#include "StdLib.icpp"
|
||||
#undef BLITZ_INDEX__
|
||||
#define BLITZ_INDEX__ F8bbcf8bffcf1dc379d0adde61a45f9fe
|
||||
#include "Static.icpp"
|
||||
#undef BLITZ_INDEX__
|
||||
#endif
|
||||
85
uppsrc/Skylark/skylark.js
Normal file
85
uppsrc/Skylark/skylark.js
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
function Log(x)
|
||||
{
|
||||
// document.writeln(x + "<br>");
|
||||
}
|
||||
|
||||
function ProcessAjaxResult(ss)
|
||||
{
|
||||
for(i = 0; i < ss.length; i++) {
|
||||
var pos = ss[i].indexOf(':');
|
||||
if(pos >= 0) {
|
||||
var id = ss[i].slice(0, pos);
|
||||
var text = ss[i].slice(pos + 1);
|
||||
if(id.length > 1 && id.charAt(0) == '>')
|
||||
document.getElementById(id.slice(1)).value = text;
|
||||
else
|
||||
document.getElementById(id).innerHTML = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function AjaxRequest()
|
||||
{
|
||||
if(window.XMLHttpRequest)
|
||||
return new XMLHttpRequest();
|
||||
else
|
||||
return new ActiveXObject("Microsoft.XMLHTTP");
|
||||
}
|
||||
|
||||
function IsNull(x)
|
||||
{
|
||||
return x == null || x.length == 0;
|
||||
}
|
||||
|
||||
function ScanForValues(x, result)
|
||||
{
|
||||
if(x) {
|
||||
if(x.nodeType == 1 &&
|
||||
(x.nodeName == "INPUT" || x.nodeName == "SELECT" || x.nodeName == "TEXTAREA")) {
|
||||
if(result.val.length)
|
||||
result.val += "&";
|
||||
Log(x.nodeName);
|
||||
Log("Name: " + x.name);
|
||||
Log("Id: " + x.id);
|
||||
Log(x.name == undefined);
|
||||
Log(x.name == null);
|
||||
var id = IsNull(x.name) ? x.id : x.name;
|
||||
Log("ID: " + id);
|
||||
if(!IsNull(id))
|
||||
result.val += id + '=' + encodeURIComponent(x.value);
|
||||
}
|
||||
for(var i = 0; i < x.childNodes.length; i++)
|
||||
ScanForValues(x.childNodes[i], result);
|
||||
}
|
||||
}
|
||||
|
||||
function UxGet(request)
|
||||
{
|
||||
var xmlhttp = AjaxRequest();
|
||||
xmlhttp.onreadystatechange = function() {
|
||||
if(xmlhttp.readyState == 4 && xmlhttp.status == 200)
|
||||
ProcessAjaxResult(xmlhttp.responseText.split('\1'));
|
||||
}
|
||||
xmlhttp.open("GET", request, true);
|
||||
xmlhttp.send();
|
||||
}
|
||||
|
||||
function UxPost(request)
|
||||
{
|
||||
var xmlhttp = AjaxRequest();
|
||||
xmlhttp.onreadystatechange = function() {
|
||||
if(xmlhttp.readyState == 4 && xmlhttp.status == 200)
|
||||
ProcessAjaxResult(xmlhttp.responseText.split('\1'));
|
||||
}
|
||||
var parameters = { val: "" };
|
||||
for(var i = 1; i < arguments.length; i++)
|
||||
ScanForValues(document.getElementById(arguments[i]), parameters);
|
||||
if(__js_identity__ != undefined) {
|
||||
if(parameters.val.length)
|
||||
parameters.val += "&";
|
||||
parameters.val += "__js_identity__=" + __js_identity__;
|
||||
}
|
||||
xmlhttp.open("POST", request, true);
|
||||
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
|
||||
xmlhttp.send(parameters.val);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue