diff --git a/uppsrc/Core/src.tpp/Callbacks$en-us.tpp b/uppsrc/Core/src.tpp/Callbacks$en-us.tpp index ed36f1983..0f56e503b 100644 --- a/uppsrc/Core/src.tpp/Callbacks$en-us.tpp +++ b/uppsrc/Core/src.tpp/Callbacks$en-us.tpp @@ -535,4 +535,4 @@ output value.&] [s7; [%-*C@3 value]-|Value.&] [s7; [*/ Return value]-|Callback.&] [s3; &] -[s0; ] \ No newline at end of file +[s0; ]] \ No newline at end of file diff --git a/uppsrc/CppBase/Parser.cpp b/uppsrc/CppBase/Parser.cpp index 63e64ee07..7cc27e301 100644 --- a/uppsrc/CppBase/Parser.cpp +++ b/uppsrc/CppBase/Parser.cpp @@ -669,7 +669,7 @@ Array Parser::Declaration0(bool l0, bool more) } Qualifier(); if(l0) - if(lex == tk_SKYLARK && lex[1] == '(') { + if(lex == tk_SKYLARK && lex[1] == '(' && lex.IsId(2)) { ++lex; ++lex; Decl& a = r.Add(); @@ -684,8 +684,9 @@ Array Parser::Declaration0(bool l0, bool more) lex.GetText(); Key(')'); return r; - } else - if((lex == tk_RPC_METHOD || lex == tk_RPC_GMETHOD) && lex[1] == '(') { + } + else + if((lex == tk_RPC_METHOD || lex == tk_RPC_GMETHOD) && lex[1] == '(' && lex.IsId(2)) { ++lex; ++lex; Decl& a = r.Add(); diff --git a/uppsrc/Skylark/Dispatch.cpp b/uppsrc/Skylark/Dispatch.cpp index 667f53322..16b763197 100644 --- a/uppsrc/Skylark/Dispatch.cpp +++ b/uppsrc/Skylark/Dispatch.cpp @@ -10,17 +10,17 @@ namespace Upp { enum { DISPATCH_VARARGS = -1 }; -struct DispatchNode : Moveable { +struct DispatchNode : Moveable { // Single node in url hierarchy tree VectorMap subnode; - void (*view)(Http&); - int argpos; - int method; - bool post_raw; - String id; + Callback1 handler; // Associated handler, if the dispatch ends at this node + int argpos; + int method; // Type of method - GET or POST + bool post_raw; // true for POST_RAW + String id; // Handler ID enum { GET, POST }; - DispatchNode() { view = NULL; argpos = Null; method = GET; post_raw = false; } + DispatchNode() { argpos = Null; method = GET; post_raw = false; } }; static Vector& sDispatchMap() @@ -35,9 +35,9 @@ static VectorMap >& sLinkMap() return x; } -static Index& sViewIndex() -{ - static Index x; +static Index& sHandlerIndex() +{ // map of handler functions for Redirect + static Index x; // use uintptr_t because it has defined hashing return x; } @@ -63,7 +63,7 @@ Vector *GetUrlViewLinkParts(const String& id) String MakeLink(void (*view)(Http&), const Vector& arg) { - int q = sViewIndex().Find((uintptr_t)view); + int q = sHandlerIndex().Find((uintptr_t)view); if(q < 0) throw Exc("Invalid view"); if(q < 0) @@ -73,11 +73,13 @@ String MakeLink(void (*view)(Http&), const Vector& arg) return out; } -void RegisterView0(void (*view)(Http&), const char *id, String path, bool primary) +void RegisterView0(void (*fn)(Http&), Callback1 cb, const char *id, String path, bool primary) { LLOG("RegisterView " << path); + ASSERT_(sLinkMap().Find(id) < 0, "duplicate handler id " + String(id)); Vector& linkpart = sLinkMap().GetAdd(id); - sViewIndex().FindAdd((uintptr_t)view); + ASSERT_(!fn || sHandlerIndex().Find((uintptr_t)fn) < 0, "duplicate view function registration " + String(id)); + sHandlerIndex().FindAdd((uintptr_t)fn); Vector& DispatchMap = sDispatchMap(); int method = DispatchNode::GET; bool post_raw = false; @@ -127,41 +129,50 @@ void RegisterView0(void (*view)(Http&), const char *id, String path, bool primar } } } - ASSERT_(!DispatchMap[q].view, "duplicate view " + String(path)); - DispatchMap[q].view = view; + ASSERT_(!DispatchMap[q].handler, "duplicate view " + String(path)); + DispatchMap[q].handler = fn ? callback(fn) : cb; DispatchMap[q].method = method; DispatchMap[q].id = id; DispatchMap[q].post_raw = post_raw; // DumpDispatchMap(); } -struct ViewData { - void (*view)(Http&); - String id; - String path; +struct HandlerData { // temporary storage of handlers until FinalizeViews call + void (*fn)(Http&); + Callback1 cb; + String id; + String path; }; -static Array& sViewData() +static Array& sHandlerData() { - static Array x; + static Array x; return x; } -void RegisterHandler(void (*view)(Http&), const char *id, const char *path) +void RegisterHandler(void (*fn)(Http&), const char *id, const char *path) { - Array& v = sViewData(); - ViewData& w = v.Add(); - w.view = view; + Array& v = sHandlerData(); + HandlerData& w = v.Add(); + w.fn = fn; + w.id = id; + w.path = path; +} + +void RegisterHandler(Callback1 cb, const char *id, const char *path) +{ + Array& v = sHandlerData(); + HandlerData& w = v.Add(); + w.cb = cb; w.id = id; w.path = path; } void SkylarkApp::FinalizeViews() -{ - Array& w = sViewData(); +{// the reason for HandlerData/FinalizeViews is to have a chance to ReplaceVars + Array& w = sHandlerData(); 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)); + const HandlerData& v = w[i]; String p = v.path; if(*p == '/') p = p.Mid(1); @@ -169,44 +180,44 @@ void SkylarkApp::FinalizeViews() p = root + '/' + p; Vector h = Split(ReplaceVars(p, view_var, '$'), ';'); for(int i = 0; i < h.GetCount(); i++) - RegisterView0(v.view, v.id, h[i], i == 0); + RegisterView0(v.fn, v.cb, v.id, h[i], i == 0); } w.Clear(); } -struct BestDispatch { - void (*view)(Http&); - int matched_parts; - int matched_params; - Vector& arg; - String id; - bool post_raw; +struct BestDispatch { // Information about the best dispatch node for given path + Callback1 handler; + int matched_parts; // number of matched path elements + int matched_params; // number of '*' arguments extracted + Vector& arg; // arguments extracted + String id; // id of handler + bool post_raw; // handler is POST_RAW type - BestDispatch(Vector& arg) : arg(arg) { matched_parts = -1; matched_params = 0; view = NULL; post_raw = false; } + BestDispatch(Vector& arg) : arg(arg) { matched_parts = -1; matched_params = 0; post_raw = false; } }; void GetBestDispatch(int method, const Vector& part, int ii, const DispatchNode& n, Vector& arg, BestDispatch& bd, int matched_parts, int matched_params) -{ +{// find the best DispatchNode for given path, best is the one with most path elements matched, if equal, more '*' used (not '**') Vector& DispatchMap = sDispatchMap(); - if(ii >= part.GetCount()) { - if(n.view && n.method == method && + if(ii >= part.GetCount()) { // we have reached the end of path + if(n.handler && n.method == method && (matched_parts > bd.matched_parts || - matched_parts == bd.matched_parts && matched_params > bd.matched_params)) { + matched_parts == bd.matched_parts && matched_params > bd.matched_params)) { // node has handler LLOG("Matched " << n.id << " parts " << matched_parts << " params " << matched_params); bd.arg <<= arg; - bd.view = n.view; + bd.handler = n.handler; bd.matched_parts = matched_parts; bd.id = n.id; bd.post_raw = n.post_raw; } - if(!bd.view) { + if(!bd.handler) { // node does not have handler, try '**' subnode 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; + if(an.argpos == DISPATCH_VARARGS && an.handler && an.method == method) { + bd.handler = an.handler; bd.id = n.id; bd.arg.Clear(); break; @@ -217,7 +228,7 @@ void GetBestDispatch(int method, return; } int qq = n.subnode.Get(part[ii], -1); - if(qq >= 0) + if(qq >= 0) // path element matched, try subnodes first GetBestDispatch(method, part, ii + 1, DispatchMap[qq], arg, bd, matched_parts + 1, matched_params); int q = n.subnode.Find(String()); while(q >= 0) { @@ -227,12 +238,12 @@ void GetBestDispatch(int method, int apos = an.argpos; LLOG(" *" << qq << " apos: " << apos); if(apos == DISPATCH_VARARGS) { - if(an.view && an.method == method && + if(an.handler && an.method == method && (matched_parts > bd.matched_parts || matched_parts == bd.matched_parts && matched_params > bd.matched_params)) { LLOG("Matched VARARGS " << an.id << " parts " << matched_parts << " params " << matched_params); bd.arg <<= arg; bd.arg.Append(part, ii, part.GetCount() - ii); - bd.view = an.view; + bd.handler = an.handler; bd.matched_parts = matched_parts; bd.id = an.id; } @@ -315,7 +326,7 @@ void Http::Dispatch(TcpSocket& socket) GetBestDispatch(post ? DispatchNode::POST : DispatchNode::GET, part, 0, DispatchMap[0], a, bd, 0, 0); LDUMPC(arg); response.Clear(); - if(bd.view) { + if(bd.handler) { try { if(SQL.IsOpen()) SQL.Begin(); @@ -332,7 +343,7 @@ void Http::Dispatch(TcpSocket& socket) var.GetAdd(".language") = ToLower(LNGAsText(lang)); handlerid = bd.id; LDUMP(viewid); - (*bd.view)(*this); + bd.handler(*this); if(session_dirty) SaveSession(); if(SQL.IsOpen()) @@ -390,12 +401,12 @@ void Http::Dispatch(TcpSocket& socket) } else { r << - "HTTP/1.1 " << code << ' ' << code_text << "\r\n" + "HTTP/1.1 " << code << ' ' << code_text << "\r\n" "Date: " << WwwFormat(GetUtcTime()) << "\r\n" "Content-Length: " << response.GetCount() << "\r\n" "Content-Type: " << content_type << "\r\n"; - for(int i = 0; i < headers.GetCount(); i++) - r << headers.GetKey(i) << ": " << headers[i] << "\r\n"; + for(int i = 0; i < headers.GetCount(); i++) + r << headers.GetKey(i) << ": " << headers[i] << "\r\n"; for(int i = 0; i < cookies.GetCount(); i++) r << cookies[i]; r << "\r\n"; diff --git a/uppsrc/Skylark/Http.h b/uppsrc/Skylark/Http.h index 30773b23d..c7665d361 100644 --- a/uppsrc/Skylark/Http.h +++ b/uppsrc/Skylark/Http.h @@ -1,151 +1,152 @@ -void MakeLink(StringBuffer& out, const Vector& part, const Vector& arg); - -class Renderer { -protected: - VectorMap var; - int lang; - - Renderer& Link(const char *id, void (*view)(Http&), const Vector& arg); - const One& 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 (*handler)(Http&)); - Renderer& operator()(const char *id, void (*handler)(Http&), const Value& arg1); - Renderer& operator()(const char *id, void (*handler)(Http&), const Value& arg1, const Value& arg2); - - Renderer& operator()(const Sql& sql); - Renderer& operator()(Fields rec); - Renderer& operator()(const SqlSelect& row_sel); - Renderer& operator()(const char *id, const SqlSelect& sel); - - Renderer& SetLanguage(int lang); - - 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 handlerid; - - Vector arg; - String session_id; - VectorMap session_var; - bool session_dirty; - - String redirect; - int code; - String code_text; - String response; - String content_type; - String request_content_type; - - VectorMap cookies; - VectorMap headers; - - 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 (*handler)(Http&)) { Renderer::operator()(id, handler); return *this; } - Http& operator()(const char *id, void (*handler)(Http&), const Value& arg1) { Renderer::operator()(id, handler, arg1); return *this; } - Http& operator()(const char *id, void (*handler)(Http&), const Value& arg1, const Value& arg2) { Renderer::operator()(id, handler, arg1, arg2); return *this; } - - Http& operator()(const Sql& sql) { Renderer::operator()(sql); return *this; } - Http& operator()(Fields rec) { Renderer::operator()(rec); return *this; } - Http& operator()(const SqlSelect& row_sel) { Renderer::operator()(row_sel); return *this; } - Http& operator()(const char *id, const SqlSelect& sel) { Renderer::operator()(id, sel); 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); } - - String GetHeader(const char *s) const { return hdr[s]; } - int GetLength() const { return atoi(GetHeader("content-length")); } - - String GetHandlerId() const { return handlerid; } - - Value operator[](const char *id) const { return Renderer::operator[](id); } - int Int(const char *id) const; - - String operator[](int i) const { return i >= 0 && i < arg.GetCount() ? arg[i] : String(); } - 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& SetHeader(const char *header, const char *data); - Http& ClearHeader(const char *header); - - Http& ClearSession(); - Http& SessionSet(const char *id, const Value& value); - Http& NewSessionId(); - - Http& NewIdentity() { SessionSet("__identity__", Null); return *this; } - - 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 (*handler)(Http&), const Vector& arg); - Http& Redirect(void (*handler)(Http&)); - Http& Redirect(void (*handler)(Http&), const Value& v1); - Http& Redirect(void (*handler)(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& UxSet(const char *id, const String& value); - Http& UxRun(const String& js_code); - - String GetResponse() const { return response; } - - void Dispatch(TcpSocket& socket); - - const SkylarkApp& App() const { return app; } - - Http(SkylarkApp& app); -}; - -void RegisterHandler(void (*handler)(Http& http), const char *id, const char *path); - -#define SKYLARK(name, path) void name(Http& http); INITBLOCK { UPP::RegisterHandler(name, #name, path); } void name(Http& http) - -Vector *GetUrlViewLinkParts(const String& id); - -String MakeLink(void (*handler)(Http&), const Vector& arg); - -enum { - SESSION_FORMAT_BINARY, SESSION_FORMAT_JSON, SESSION_FORMAT_XML -}; +void MakeLink(StringBuffer& out, const Vector& part, const Vector& arg); + +class Renderer { +protected: + VectorMap var; + int lang; + + Renderer& Link(const char *id, void (*view)(Http&), const Vector& arg); + const One& 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 (*handler)(Http&)); + Renderer& operator()(const char *id, void (*handler)(Http&), const Value& arg1); + Renderer& operator()(const char *id, void (*handler)(Http&), const Value& arg1, const Value& arg2); + + Renderer& operator()(const Sql& sql); + Renderer& operator()(Fields rec); + Renderer& operator()(const SqlSelect& row_sel); + Renderer& operator()(const char *id, const SqlSelect& sel); + + Renderer& SetLanguage(int lang); + + 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 handlerid; + + Vector arg; + String session_id; + VectorMap session_var; + bool session_dirty; + + String redirect; + int code; + String code_text; + String response; + String content_type; + String request_content_type; + + VectorMap cookies; + VectorMap headers; + + 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 (*handler)(Http&)) { Renderer::operator()(id, handler); return *this; } + Http& operator()(const char *id, void (*handler)(Http&), const Value& arg1) { Renderer::operator()(id, handler, arg1); return *this; } + Http& operator()(const char *id, void (*handler)(Http&), const Value& arg1, const Value& arg2) { Renderer::operator()(id, handler, arg1, arg2); return *this; } + + Http& operator()(const Sql& sql) { Renderer::operator()(sql); return *this; } + Http& operator()(Fields rec) { Renderer::operator()(rec); return *this; } + Http& operator()(const SqlSelect& row_sel) { Renderer::operator()(row_sel); return *this; } + Http& operator()(const char *id, const SqlSelect& sel) { Renderer::operator()(id, sel); 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); } + + String GetHeader(const char *s) const { return hdr[s]; } + int GetLength() const { return atoi(GetHeader("content-length")); } + + String GetHandlerId() const { return handlerid; } + + Value operator[](const char *id) const { return Renderer::operator[](id); } + int Int(const char *id) const; + + String operator[](int i) const { return i >= 0 && i < arg.GetCount() ? arg[i] : String(); } + 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& SetHeader(const char *header, const char *data); + Http& ClearHeader(const char *header); + + Http& ClearSession(); + Http& SessionSet(const char *id, const Value& value); + Http& NewSessionId(); + + Http& NewIdentity() { SessionSet("__identity__", Null); return *this; } + + 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 (*handler)(Http&), const Vector& arg); + Http& Redirect(void (*handler)(Http&)); + Http& Redirect(void (*handler)(Http&), const Value& v1); + Http& Redirect(void (*handler)(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& UxSet(const char *id, const String& value); + Http& UxRun(const String& js_code); + + String GetResponse() const { return response; } + + void Dispatch(TcpSocket& socket); + + const SkylarkApp& App() const { return app; } + + Http(SkylarkApp& app); +}; + +void RegisterHandler(void (*handler)(Http& http), const char *id, const char *path); +void RegisterHandler(Callback1 handler, const char *id, const char *path); + +#define SKYLARK(name, path) void name(Http& http); INITBLOCK { UPP::RegisterHandler(name, #name, path); } void name(Http& http) + +Vector *GetUrlViewLinkParts(const String& id); + +String MakeLink(void (*handler)(Http&), const Vector& arg); + +enum { + SESSION_FORMAT_BINARY, SESSION_FORMAT_JSON, SESSION_FORMAT_XML +};