multithreaded server

This commit is contained in:
Udo 2021-12-05 00:24:57 +00:00
parent 0262e33653
commit f77581114c
26 changed files with 2063 additions and 206 deletions

Binary file not shown.

View File

@ -293,7 +293,7 @@ FastCGIServer::process(int timeout_ms)
} }
if (it->second->close_socket && it->second->output_buffer.empty()) { if (it->second->close_socket && it->second->output_buffer.empty()) {
close_socket: close_socket:
int close_result = close(it->first); int close_result = close(it->first);
if (close_result == -1 && errno != ECONNRESET) if (close_result == -1 && errno != ECONNRESET)
throw std::runtime_error("close() failed"); throw std::runtime_error("close() failed");

316
src/lib/compiler.cpp Normal file
View File

@ -0,0 +1,316 @@
#include "compiler.h"
#include <sys/file.h>
String process_html_literal(Request* context, SharedUnit* su, String content)
{
String pc;
String HT_START = "context->print(R\"(";
String HT_END = ")\");";
u8 mode = 0;
char quote_char;
bool inside_quote = false;
String code_buffer = "";
bool is_field = false;
for(u32 i = 0; i < content.length(); i++)
{
char c = content[i];
switch(mode)
{
case(0):
if(c == '<' && content[i+1] == '?')
{
code_buffer = "";
if(content[i+2] == '=')
{
is_field = true;
i += 2;
}
else
{
is_field = false;
i += 1;
}
mode = 1; // code-parsing mode
}
else
{
pc.append(1, c);
}
break;
case(1):
if(inside_quote)
{
if(quote_char == c && content[i-1] != '\\')
inside_quote = false;
code_buffer.append(1, c);
}
else
{
if(c == '\"' || c == '\'')
{
inside_quote = true;
quote_char = c;
code_buffer.append(1, c);
}
else if(c == '?' && content[i+1] == '>')
{
mode = 0;
i += 1;
if(is_field)
{
pc.append(
HT_END +
"echo(html_escape( " +
code_buffer +
" )); " +
HT_START
);
}
else
{
pc.append(HT_END + code_buffer + HT_START);
}
}
else
{
code_buffer.append(1, c);
}
}
break;
}
}
return(HT_START + pc + HT_END);
}
String preprocess_shared_unit(Request* context, SharedUnit* su)
{
String content = file_get_contents(su->file_name);
printf("(c) compiling with root dir %s\n", context->server->config.COMPILER_SYS_PATH.c_str());
String pc = ("#include \"")+context->server->config.COMPILER_SYS_PATH +"/src/lib/uce_lib.h\" \n";
String token = "";
String html_buffer = "";
u8 mode = 0;
bool inside_quote = false;
for(u32 i = 0; i < content.length(); i++)
{
char c = content[i];
if(mode == 2)
{
auto end_pos = content.find(String("</")+token+">", i);
if(end_pos != String::npos)
{
u32 len = token.length() + 3 + end_pos - i;
html_buffer.append(content.substr(i, len - (token.length() == 0 ? 3 : 0)));
i += len - 1;
pc.append(process_html_literal(context, su, html_buffer));
}
else
{
printf("(!) unterminated HTML literal <%s> in %s", token.c_str(), su->file_name.c_str());
}
mode = 0;
}
else if(mode == 1)
{
if(isspace(c) || c == '>')
{
mode = 2;
if(token.length() > 0)
html_buffer.append(1, c);
}
else
{
token.append(1, c);
html_buffer.append(1, c);
}
}
else if(!inside_quote && c == '<' && (content[i+1] == '>'/* || isalpha(content[i+1])*/))
{
mode = 1;
token = "";
html_buffer = "";
if(content[i+1] != '>')
html_buffer.append(1, c);
}
else if(c == '\"')
{
inside_quote = !inside_quote;
pc.append(1, c);
}
else
{
pc.append(1, c);
}
}
return(pc);
}
void setup_unit_paths(Request* context, SharedUnit* su, String file_name)
{
su->file_name = file_name;
if(su->src_path.length() > 0) // we did this already
return;
su->src_path = dirname(file_name);
su->bin_path = context->server->config.BIN_DIRECTORY + su->src_path;
su->pre_path = context->server->config.BIN_DIRECTORY + su->src_path;
su->src_file_name = basename(file_name);
su->bin_file_name = su->src_file_name + ".so";
su->pre_file_name = su->src_file_name + ".cpp";
su->so_name = su->bin_path + "/" + su->bin_file_name;
}
void load_shared_unit(Request* context, SharedUnit* su, String file_name)
{
//setup_unit_paths(context, su, file_name);
su->on_render = 0;
su->on_setup = 0;
su->compiler_messages = "";
if(!file_exists(su->so_name))
{
printf("(i) unit file not found: %s\n", su->so_name.c_str());
su->compiler_messages = "unit file not found";
return;
}
su->so_handle = dlopen(su->so_name.c_str(), RTLD_NOW);
if(su->so_handle)
{
su->last_compiled = file_mtime(su->so_name);
char *error;
su->on_setup = (request_handler)dlsym(su->so_handle, "set_current_request");
su->on_render = (call_handler)dlsym(su->so_handle, "render");
if ((error = dlerror()) != NULL)
printf("Error - %s in %s\n", error, su->file_name.c_str());
else
printf("(i) loaded unit %s\n", su->file_name.c_str());
}
else
{
printf("Error loading unit %s, could not open %s\n", su->file_name.c_str(), su->so_name.c_str());
}
}
void compile_shared_unit(Request* context, SharedUnit* su, String file_name)
{
//setup_unit_paths(context, su, file_name);
if(!file_exists(su->file_name))
{
su->compiler_messages = "source file not found (" + su->file_name + ")";
return;
}
shell_exec("mkdir -p " + shell_escape(su->pre_path));
file_put_contents(su->pre_path + "/" + su->pre_file_name, preprocess_shared_unit(context, su));
printf("Config.COMPILE_SCRIPT %s\n", String(su->pre_path + "/" + su->pre_file_name).c_str());
su->compiler_messages = trim(shell_exec(context->server->config.COMPILE_SCRIPT+" "+
shell_escape(su->src_path)+" "+
shell_escape(su->bin_path)+" "+
shell_escape(su->file_name)+" "+
shell_escape(su->pre_file_name)+" "+
shell_escape(su->bin_file_name)
));
if(su->compiler_messages.length() > 0)
{
printf("%s \n", su->compiler_messages.c_str());
}
else
{
load_shared_unit(context, su, file_name);
printf("(i) compiled unit %s\n", file_name.c_str());
}
}
SharedUnit* get_shared_unit(Request* context, String file_name)
{
SharedUnit* su = context->server->units[file_name];
auto mod_time = file_mtime(file_name);
bool do_recompile = false;
if(su && (su->last_compiled < mod_time || mod_time == 0))
{
delete su;
su = 0;
do_recompile = true;
}
if(!su)
{
su = new SharedUnit();
setup_unit_paths(context, su, file_name);
int fdlock = open((su->so_name+".lock").c_str(), O_RDWR | O_CREAT, 0666 );
int fl_excl = flock(fdlock, LOCK_EX);
if(do_recompile)
{
compile_shared_unit(context, su, file_name);
}
else
{
load_shared_unit(context, su, file_name);
if(!su->so_handle)
compile_shared_unit(context, su, file_name);
}
flock(fdlock, LOCK_UN);
close(fdlock);
remove((su->so_name+".lock").c_str());
context->server->units[file_name] = su;
}
return(su);
}
void compiler_invoke(Request* context, String file_name, DTree& call_param)
{
if(file_name[0] != '/')
{
file_name = expand_path(file_name);
}
//printf("(i) invoke %s\n", file_name.c_str());
//printf("(i) invoke(%s)\n", file_name.c_str());
switch_to_system_alloc();
auto su = get_shared_unit(context, file_name);
switch_to_arena(context->request_arena);
if(!su)
{
printf("Error loading unit %s\n", file_name.c_str());
context->print("Error loading unit: "+file_name);
}
else if(!su->on_render)
{
context->header["Content-Type"] = "text/plain";
context->print("Compiler error: "+su->compiler_messages);
}
else
{
if(su->compiler_messages.length() > 0)
context->print(su->compiler_messages);
else
{
String prev_wd = get_cwd();
set_cwd(su->src_path);
su->on_setup(context);
su->on_render(call_param);
set_cwd(prev_wd);
}
}
}

View File

@ -1,9 +1,10 @@
#define RENDER() extern "C" void render(DTree& call) #define RENDER() extern "C" void render(DTree& call)
string process_html_literal(Request* context, SharedUnit* su, string content); String process_html_literal(Request* context, SharedUnit* su, String content);
string preprocess_shared_unit(Request* context, SharedUnit* su); String preprocess_shared_unit(Request* context, SharedUnit* su);
void setup_unit_paths(Request* context, SharedUnit* su, string file_name); void setup_unit_paths(Request* context, SharedUnit* su, String file_name);
void load_shared_unit(Request* context, SharedUnit* su, string file_name); void load_shared_unit(Request* context, SharedUnit* su, String file_name);
void compile_shared_unit(Request* context, SharedUnit* su, string file_name); void compile_shared_unit(Request* context, SharedUnit* su, String file_name);
SharedUnit* get_shared_unit(Request* context, string file_name); SharedUnit* get_shared_unit(Request* context, String file_name);
void compiler_invoke(Request* context, string file_name, DTree& call_param); void compiler_invoke(Request* context, String file_name, DTree& call_param);

263
src/lib/dtree.cpp Normal file
View File

@ -0,0 +1,263 @@
void DTree::each(std::function <void (DTree t, String key)> f)
{
switch(type)
{
case('M'):
for (auto it = _map.begin(); it != _map.end(); ++it)
{
f(it->second, it->first);
}
break;
default:
f(*this, "");
break;
}
}
bool DTree::is_array()
{
return(type == 'M');
}
String DTree::to_string()
{
switch(type)
{
case('S'):
return(_String);
break;
case('F'):
return(std::to_string(_float));
break;
case('B'):
return(_bool ? "(true)" : "(false)");
break;
case('M'):
return("");
break;
case('P'):
return(std::to_string((u64)_ptr));
break;
}
}
String DTree::to_json()
{
switch(type)
{
case('S'):
return(json_escape(_String));
break;
case('F'):
return(std::to_string(_float));
break;
case('B'):
return(_bool ? "true" : "false");
break;
case('M'):
return("\"(array)\"");
break;
case('P'):
return("\"(pointer)\"");
break;
}
}
String DTree::get_type_name()
{
switch(type)
{
case('S'):
return("String");
break;
case('F'):
return("f64");
break;
case('B'):
return("bool");
break;
case('M'):
return("array");
break;
case('P'):
return("pointer");
break;
}
}
void DTree::set_type(char t)
{
if(type != t)
{
type = t;
switch(type)
{
case('M'):
_map.clear();
_array_index = 0;
break;
}
}
}
void DTree::set(String s)
{
set_type('S');
_String = s;
}
void DTree::set(void* p)
{
set_type('P');
_ptr = p;
}
void DTree::set(f64 f)
{
set_type('F');
_float = f;
}
void DTree::set_bool(bool b)
{
set_type('B');
_bool = b;
}
void DTree::set(DTree source)
{
set_type(source.type);
switch(type)
{
case('S'):
_String = source._String;
break;
case('F'):
_float = source._float;
break;
case('B'):
_bool = source._bool;
break;
case('M'):
_map = source._map;
break;
case('P'):
_ptr = source._ptr;
break;
}
}
void DTree::set(StringMap source)
{
set_type('M');
for (auto it = source.begin(); it != source.end(); ++it)
{
_map[it->first] = it->second;
}
}
DTree* DTree::key(String s)
{
set_type('M');
return(&_map[s]);
}
DTree& DTree::operator [] (String s) {
set_type('M');
return(_map[s]);
}
void DTree::operator = (String v) { set(v); }
void DTree::operator = (f64 v) { set(v); }
void DTree::operator = (void* v) { set(v); }
void DTree::operator = (DTree v) { set(v); }
void DTree::operator = (StringMap v) { set(v); }
void DTree::push(DTree& child)
{
set_type('M');
_map[std::to_string(_array_index)] = child;
_array_index += 1;
}
DTree DTree::pop()
{
set_type('M');
auto last = _map.rbegin();
DTree result = last->second;
_map.erase(last->first);
return(result);
}
void DTree::remove(String s)
{
set_type('M');
_map.erase(s);
}
void DTree::clear()
{
set_type('M');
_map.clear();
}
String to_String(DTree t)
{
return(t.to_string());
}
String var_dump(DTree map, String prefix, String postfix)
{
String result = "";
if(!map.is_array())
return(map.to_string());
map.each([&] (DTree item, String key) {
result += prefix + key + ": " + item.to_string() + postfix;
if(item.is_array())
result += var_dump(item, prefix + "\t");
});
return(result);
}
String json_escape(String s)
{
//return(String("\"")+s+"\"");
String result;
u32 i = 0;
result.append(1, '"');
while(i < s.length())
{
char c = s[i];
switch(c)
{
case('\t'):
result.append("\\t");
break;
case('\n'):
result.append("\\n");
break;
case('"'):
result.append("\\\"");
break;
case('\r'):
result.append("\\r");
break;
case('\\'):
result.append("\\\\");
break;
case('\b'):
result.append("\\b");
break;
case('\f'):
result.append("\\f");
break;
default:
result.append(1, c);
break;
}
i += 1;
}
result.append(1, '"');
return(result);
}

View File

@ -1,31 +1,31 @@
string json_escape(string s); String json_escape(String s);
struct DTree { struct DTree {
char type = 'S'; char type = 'S';
string _string; String _String;
f64 _float; f64 _float;
s64 _array_index; s64 _array_index;
bool _bool; bool _bool;
void* _ptr; void* _ptr;
std::map<string, DTree> _map; std::map<String, DTree> _map;
void each(std::function <void (DTree t, string key)> f); void each(std::function <void (DTree t, String key)> f);
bool is_array(); bool is_array();
string to_string(); String to_string();
string to_json(); String to_json();
string get_type_name(); String get_type_name();
void set_type(char t); void set_type(char t);
void set(string s); void set(String s);
void set(void* p); void set(void* p);
void set(f64 f); void set(f64 f);
void set_bool(bool b); void set_bool(bool b);
void set(DTree source); void set(DTree source);
void set(StringMap source); void set(StringMap source);
DTree* key(string s); DTree* key(String s);
DTree& operator [] (string s); DTree& operator [] (String s);
void operator = (string v); void operator = (String v);
void operator = (f64 v); void operator = (f64 v);
void operator = (void* v); void operator = (void* v);
void operator = (DTree v); void operator = (DTree v);
@ -33,9 +33,9 @@ struct DTree {
void push(DTree& child); void push(DTree& child);
DTree pop(); DTree pop();
void remove(string s); void remove(String s);
void clear(); void clear();
}; };
string to_string(DTree t); String to_String(DTree t);
string var_dump(DTree map, string prefix = "", string postfix = "\n"); String var_dump(DTree map, String prefix = "", String postfix = "\n");

375
src/lib/functionlib.cpp Normal file
View File

@ -0,0 +1,375 @@
#include "functionlib.h"
String var_dump(StringMap map, String prefix, String postfix)
{
String result = "";
for (auto it = map.begin(); it != map.end(); ++it)
{
result.append(prefix + it->first + ": " + it->second + postfix);
}
return(result);
}
String var_dump(StringList slist, String prefix, String postfix)
{
String result = "";
for (auto& s : slist)
{
result.append(prefix + s + postfix);
}
return(result);
}
u8 char_to_u8(char input)
{
if(input >= '0' && input <= '9')
return input - '0';
if(input >= 'A' && input <= 'F')
return input - 'A' + 10;
if(input >= 'a' && input <= 'f')
return input - 'a' + 10;
return(0);
}
u8 hex_to_u8(String src)
{
return(char_to_u8(src[0])*16 + char_to_u8(src[1]));
}
String trim(String raw)
{
u32 len = raw.length();
u32 start_pos = 0;
u32 end_pos = len - 1;
if(len == 0 || (len == 1 && isspace(raw[0])))
return("");
while(start_pos < len && isspace(raw[start_pos]))
start_pos++;
while(end_pos >= 0 && isspace(raw[end_pos]))
end_pos--;
if(end_pos < start_pos)
return("");
return(raw.substr(start_pos, 1 + end_pos - start_pos));
}
StringList split(String str, String delim)
{
StringList result;
int start = 0;
int end = str.find(delim);
while (end != String::npos)
{
result.push_back(str.substr(start, end - start));
start = end + delim.size();
end = str.find(delim, start);
}
result.push_back(str.substr(start, end - start));
return(result);
}
String join(StringList l, String delim)
{
String result;
u32 i = 0;
for(auto& s : l)
{
if(i > 0)
result.append(delim);
result.append(s);
i += 1;
}
return(result);
}
StringList filter(StringList items, std::function<bool (String)> f)
{
StringList new_items;
for(auto item : items)
{
if(f(item))
new_items.push_back(item);
}
return(new_items);
}
String html_escape(String s)
{
String result;
for(u32 i = 0; i < s.length(); i++)
{
char c = s[i];
switch(c)
{
case('&'):
result.append("&amp;");
break;
case('<'):
result.append("&lt;");
break;
case('>'):
result.append("&gt;");
break;
case('"'):
result.append("&quot;");
break;
default:
result.append(1, c);
break;
}
}
return(result);
}
String html_escape(u64 a)
{
return(std::to_string(a));
}
String html_escape(f64 a)
{
return(std::to_string(a));
}
u64 int_val(String s, u32 base)
{
return(strtoll(s.c_str(), 0, base));
}
String nibble(String& haystack, String delim)
{
auto idx = haystack.find(delim);
if(idx == String::npos)
{
String result = haystack;
haystack = "";
return(result);
}
else
{
String result = haystack.substr(0, idx);
haystack = haystack.substr(idx+delim.length());
return(result);
}
}
String json_encode(DTree t)
{
String result = "";
if(t.is_array())
{
result += "{";
u32 count = 0;
t.each([&] (DTree item, String key) {
if(count > 0)
result += ", ";
count += 1;
result += json_escape(key) + ": " + json_encode(item);
});
result += "}";
}
else
{
result = t.to_json();
}
return(result);
}
// https://i.stack.imgur.com/SHLOB.gif
String json_decode_String(String s, u32& i, char termination_char)
{
String result;
//echo("json_decode_String " + s.substr(i) + "\n");
while(i < s.length())
{
char c = s[i];
if(c == termination_char)
{
i += 1;
//echo("json_decode_String = " + result + "\n");
return(result);
}
else if(c == '\\')
{
i += 1;
c = s[i];
switch(c)
{
case('t'):
result.append(1, '\t');
break;
case('n'):
result.append(1, '\n');
break;
case('r'):
result.append(1, '\r');
break;
case('\\'):
result.append(1, '\\');
break;
case('b'):
result.append(1, '\b');
break;
case('f'):
result.append(1, '\f');
break;
case('u'):
// todo decode
break;
default:
result.append(1, c);
break;
}
}
else
{
result.append(1, c);
}
i += 1;
}
return(result);
}
DTree json_decode_map(String s, u32& i);
void json_consume_space(String s, u32& i)
{
while(i < s.length() && isspace(s[i]))
i += 1;
}
String json_decode_keyword(String s, u32& i)
{
String result;
json_consume_space(s, i);
while(i < s.length())
{
char c = s[i];
if(isalnum(c))
{
result.append(1, c);
}
else
{
return(result);
}
i += 1;
}
return(result);
}
String json_decode_number(String s, u32& i)
{
String result;
json_consume_space(s, i);
while(i < s.length())
{
char c = s[i];
if(isdigit(c) || c == '.')
{
result.append(1, c);
}
else
{
return(result);
}
i += 1;
}
return(result);
}
DTree json_decode_value(String s, u32& i)
{
DTree result;
String value = "";
json_consume_space(s, i);
char c = s[i];
//echo("json_decode_value " + s.substr(i) + "\n");
if(c == '"' || c == '\'') // String value
{
result.type = 'S';
i += 1;
result._String = json_decode_String(s, i, s[i-1]);
return(result);
}
else if(isdigit(c))
{
result.type = 'S';
result._String = json_decode_number(s, i);
//result._float = stod(json_decode_number(s, i));
return(result);
}
else if(c == '{')
{
i += 1;
return(json_decode_map(s, i));
}
else
{
value = json_decode_keyword(s, i);
if(value == "true")
result.set_bool(true);
else if(value == "false")
result.set_bool(false);
else if(value == "null")
result.set("");
return(result);
}
return(result);
}
DTree json_decode_map(String s, u32& i)
{
DTree result;
result.type = 'M';
String key = "";
json_consume_space(s, i);
//echo("json_decode_map " + s.substr(i) + "\n");
while(i < s.length())
{
char c = s[i];
if(c == '}')
{
i += 1;
return(result);
}
else if(c == ',')
{
i += 1;
}
else if(c == '"' || c == '\'')
{
i += 1;
key = json_decode_String(s, i, s[i-1]);
json_consume_space(s, i);
if(s[i] != ':')
return(result); // malformed
i += 1;
DTree v = json_decode_value(s, i);
//result._map[key] = json_decode_value(s, i);
//echo("KV " + key + " = " + to_String(v) + "\n");
//printf("map add %s (%c) \n", key.c_str(), s[i]);
result._map[key] = v;
}
else
{
// malformed
return(result);
}
json_consume_space(s, i);
}
return(result);
}
DTree json_decode(String s)
{
u32 i = 0;
return(json_decode_value(s, i));
}

View File

@ -1,20 +1,26 @@
string var_dump(StringMap map, string prefix = "", string postfix = "\n");
string var_dump(StringList slist, string prefix = "", string postfix = "\n");
u8 char_to_u8(char input); u8 char_to_u8(char input);
u8 hex_to_u8(string src); u8 hex_to_u8(String src);
string trim(string raw); u64 int_val(String s, u32 base = 10);
StringList split(string str, string delim = "\n");
string join(StringList l, string delim = "\n"); String trim(String raw);
string html_escape(string s); StringList split(String str, String delim = "\n");
string html_escape(u64 a); String join(StringList l, String delim = "\n");
string html_escape(f64 a); String nibble(String& haystack, String delim);
u64 int_val(string s, u32 base = 10); void json_consume_space(String s, u32& i);
string nibble(string& haystack, string delim); StringList filter(StringList items, std::function<bool (String)> f);
string json_encode(DTree t);
string json_decode_string(string s, u32& i, char termination_char = '"'); String html_escape(String s);
void json_consume_space(string s, u32& i); String html_escape(u64 a);
string json_decode_keyword(string s, u32& i); String html_escape(f64 a);
string json_decode_number(string s, u32& i);
DTree json_decode_value(string s, u32& i); String json_encode(DTree t);
DTree json_decode_map(string s, u32& i); String json_decode_String(String s, u32& i, char termination_char = '"');
DTree json_decode(string s); String json_decode_keyword(String s, u32& i);
String json_decode_number(String s, u32& i);
DTree json_decode_value(String s, u32& i);
DTree json_decode_map(String s, u32& i);
DTree json_decode(String s);
String var_dump(StringMap map, String prefix = "", String postfix = "\n");
String var_dump(StringList slist, String prefix = "", String postfix = "\n");

View File

@ -3,7 +3,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "mysql-connector.h" #include "mysql-connector.h"
bool MySQL::connect(string host, string username, string password) bool MySQL::connect(String host, String username, String password)
{ {
connection = mysql_init(NULL); connection = mysql_init(NULL);
if (connection == NULL) if (connection == NULL)
@ -33,9 +33,9 @@ bool MySQL::connect(string host, string username, string password)
return(true); return(true);
} }
string MySQL::escape(string raw, char quote_char) String MySQL::escape(String raw, char quote_char)
{ {
string result; String result;
result.append(1, quote_char); result.append(1, quote_char);
for(u32 i = 0; i < raw.length(); i++) for(u32 i = 0; i < raw.length(); i++)
@ -97,7 +97,7 @@ DTree field_to_dtree_node(char* data_ptr, MySQLFieldInfo field_info, u32 len)
case(MYSQL_TYPE_NULL): case(MYSQL_TYPE_NULL):
break; break;
default: default:
string s; String s;
s.assign(data_ptr); s.assign(data_ptr);
result.set(s); result.set(s);
break; break;
@ -166,7 +166,7 @@ DTree MySQL::get_pending_result()
return(result_data); return(result_data);
} }
DTree MySQL::query(string q) DTree MySQL::query(String q)
{ {
_preload_next_error_code = mysql_query((MYSQL*)connection, q.c_str()); _preload_next_error_code = mysql_query((MYSQL*)connection, q.c_str());
DTree result; DTree result;
@ -175,21 +175,21 @@ DTree MySQL::query(string q)
return(result); return(result);
} }
DTree MySQL::query(string q, StringMap params) DTree MySQL::query(String q, StringMap params)
{ {
return(query( return(query(
parse_query_parameters(q, params).c_str() parse_query_parameters(q, params).c_str()
)); ));
} }
string MySQL::parse_query_parameters(string query, StringMap map) String MySQL::parse_query_parameters(String query, StringMap map)
{ {
string result; String result;
query.append(1, ' '); query.append(1, ' ');
u8 mode = 0; u8 mode = 0;
char quote; char quote;
string identifier; String identifier;
for(u32 i = 0; i < query.length(); i++) for(u32 i = 0; i < query.length(); i++)
{ {
char c = query[i]; char c = query[i];
@ -242,11 +242,11 @@ void MySQL::disconnect()
connection = NULL; connection = NULL;
} }
string MySQL::error() String MySQL::error()
{ {
if(_preload_next_error_code) if(_preload_next_error_code)
{ {
string p = "Unknown error"; String p = "Unknown error";
switch(_preload_next_error_code) switch(_preload_next_error_code)
{ {
case(CR_COMMANDS_OUT_OF_SYNC): case(CR_COMMANDS_OUT_OF_SYNC):
@ -272,7 +272,7 @@ string MySQL::error()
const char* res = mysql_error((MYSQL*)connection); const char* res = mysql_error((MYSQL*)connection);
if(res) if(res)
{ {
return(string(res)); return(String(res));
} }
else else
{ {

View File

@ -1,9 +1,9 @@
struct MySQLFieldInfo { struct MySQLFieldInfo {
string name; String name;
string table; String table;
string db; String db;
u64 length; u64 length;
string def; String def;
u64 max_length; u64 max_length;
u64 flags; u64 flags;
u32 type; u32 type;
@ -17,17 +17,17 @@ struct MySQL {
u32 field_count = 0; u32 field_count = 0;
u32 row_count = 0; u32 row_count = 0;
u64 insert_id = 0; u64 insert_id = 0;
string statement_info = ""; String statement_info = "";
std::vector<MySQLFieldInfo> field_info; std::vector<MySQLFieldInfo> field_info;
bool connect(string host = "localhost", string username = "root", string password = ""); bool connect(String host = "localhost", String username = "root", String password = "");
void disconnect(); void disconnect();
string error(); String error();
string escape(string raw, char quote_char = '\''); String escape(String raw, char quote_char = '\'');
string parse_query_parameters(string query, StringMap m); String parse_query_parameters(String query, StringMap m);
DTree query(string q); DTree query(String q);
DTree query(string q, StringMap params); DTree query(String q, StringMap params);
DTree get_pending_result(); DTree get_pending_result();
}; };

424
src/lib/sys.cpp Normal file
View File

@ -0,0 +1,424 @@
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <execinfo.h>
#include "sys.h"
String shell_exec(String cmd)
{
printf("(i) shell_exec(%s)\n", cmd.c_str());
String data;
FILE * stream;
const int max_buffer = 256;
char buffer[max_buffer];
cmd.append(" 2>&1");
stream = popen(cmd.c_str(), "r");
if (stream) {
while (!feof(stream))
if (fgets(buffer, max_buffer, stream) != NULL) data.append(buffer);
pclose(stream);
}
return data;
}
String shell_escape(String raw)
{
// FIXME
return("\"" + raw + "\"");
/*
` U+0060 (Grave Accent) Backtick Command substitution
~ U+007E Tilde Tilde expansion
! U+0021 Exclamation mark History expansion
# U+0023 Number sign Hash Comments
$ U+0024 Dollar sign Parameter expansion
& U+0026 Ampersand Background commands
* U+002A Asterisk Filename expansion and globbing
( U+0028 Left Parenthesis Subshells
) U+0029 Right Parenthesis Subshells
U+0009 Tab() Word splitting (whitespace)
{ U+007B Left Curly Bracket Left brace Brace expansion
[ U+005B Left Square Bracket Filename expansion and globbing
| U+007C Vertical Line Vertical bar Pipelines
\ U+005C Reverse Solidus Backslash Escape character
; U+003B Semicolon Separating commands
' U+0027 Apostrophe Single quote String quoting
" U+0022 Quotation Mark Double quote String quoting with interpolation
U+000A Line Feed Newline Line break
< U+003C Less than Input redirection
> U+003E Greater than Output redirection
? U+003F Question mark Filename expansion and globbing
U+0020 Space Word splitting1 (whitespace)
*/
}
String basename(String fn)
{
String result;
while(fn.length() > 0)
result = nibble("/", fn);
//printf("basename(%s) %s\n", fn.c_str(), result.c_str());
return(result);
}
String dirname(String fn)
{
String result;
auto seg = split(fn, "/");
seg.pop_back();
result = join(seg, "/");
//printf("dirname(%s) %s seg#%i\n", fn.c_str(), result.c_str(), seg.size());
return(result);
}
bool mkdir(String path)
{
shell_exec(String("mkdir -p ")+" "+shell_escape(path));
return(true);
}
bool file_exists(String path)
{
std::filesystem::path fp{ path };
return(std::filesystem::exists(fp));
}
String file_get_contents(String file_name)
{
try
{
std::ifstream ifs(file_name);
String content(
(std::istreambuf_iterator<char>(ifs) ),
(std::istreambuf_iterator<char>() ) );
return(content);
}
catch(std::exception e)
{
return("");
}
}
bool file_put_contents(String file_name, String content)
{
try
{
std::ofstream out(file_name);
out << content;
out.close();
return(true);
}
catch(std::exception e)
{
return(false);
}
}
String get_cwd()
{
return(std::filesystem::current_path());
}
void set_cwd(String path)
{
chdir(path.c_str());
}
time_t file_mtime(String file_name)
{
struct stat info;
if (stat(file_name.c_str(), &info) != 0)
{
return(0);
}
else
{
return(info.st_mtime);
}
}
void unlink(String file_name)
{
remove(file_name.c_str());
}
String expand_path(String path)
{
String result;
auto base_path = split(get_cwd(), "/");
auto rel_path = split(path, "/");
for(auto& s : rel_path)
{
if(s == "..")
{
base_path.pop_back();
}
else if(s == ".")
{
}
else
{
base_path.push_back(s);
}
}
return(join(base_path, "/"));
}
f64 microtime()
{
return ((f64)std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch()).count()) / 1000000;
}
u64 time()
{
return(std::time(0));
}
String date(String format, u64 timestamp)
{
String ts;
String fmt;
if(timestamp > 0)
ts = String("-d '@")+std::to_string(timestamp)+"'";
if(format != "")
fmt = String("+'"+format+"'");
return(trim(shell_exec("date "+ts+" "+fmt)));
}
String gmdate(String format, u64 timestamp)
{
String ts;
String fmt;
if(timestamp > 0)
ts = String("-d '@")+std::to_string(timestamp)+"'";
if(format == "RFC1123")
format = "%a, %d %b %Y %T GMT";
if(format != "")
fmt = String("+'"+format+"'");
return(trim(shell_exec("date -u "+ts+" "+fmt)));
}
u64 parse_time(String time_String)
{
return(int_val(trim(shell_exec("date -u -d '"+time_String+"' +'%s'"))));
}
u64 socket_connect(String host, short port)
{
/*String addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
String sockaddr *ai_addr;
char *ai_canonname;
String addrinfo *ai_next;
};*/
auto sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sockfd < 0)
{
echo("SOCKET ERROR (could not open socket)\n");
perror("SOCKET ERROR ");
return(0);
}
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(host.c_str());
if(connect(sockfd, (struct sockaddr*) &addr, sizeof(addr)) < 0)
{
echo("SOCKET ERROR (could not connect to address " + String(host) + ":" + std::to_string(port) + ")\n");
perror("SOCKET ERROR ");
return(0);
}
context->resources.sockets.push_back(sockfd);
return(sockfd);
}
void socket_close(u64 sockfd)
{
close(sockfd);
}
bool socket_write(u64 sockfd, String data)
{
return(write(sockfd, data.c_str(), data.length()) >= 0);
}
String socket_read(u64 sockfd, u32 max_length, u32 timeout)
{
struct timeval tv;
tv.tv_sec = timeout;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
char buf[max_length+1];
auto byte_count = recv(sockfd, buf, sizeof(buf), 0);
if(byte_count > 0)
{
buf[byte_count] = 0;
String result(buf, byte_count+1);
return(result);
}
return("");
}
String memcache_escape_key(String key)
{
String result;
for(auto c : key)
{
if(isspace(c))
c = '_';
result.append(1, c);
}
return(result);
}
StringList memcache_escape_keys(StringList keys)
{
StringList result;
for(auto s : keys)
{
result.push_back(memcache_escape_key(s));
}
return(result);
}
u64 memcache_connect(String host, short port)
{
return(socket_connect(host, port));
}
String memcache_command(u64 connection, String command)
{
socket_write(connection, command+"\r\n");
return(socket_read(connection)); // FIXME: do multi-chunk until END line is received!
}
bool memcache_set(u64 connection, String key, String value, u64 expires_in)
{
socket_write(connection,
// set KEY META_DATA EXPIRY_TIME LENGTH_IN_BYTES
String("set ") + memcache_escape_key(key) + " 0 " + std::to_string(expires_in) + " " + std::to_string(value.length()) + "\r\n" +
value + "\r\n");
return("STORED" == trim(socket_read(connection)));
}
bool memcache_delete(u64 connection, String key)
{
socket_write(connection,
// set KEY META_DATA EXPIRY_TIME LENGTH_IN_BYTES
String("delete ") + memcache_escape_key(key) + "\r\n"
);
return("DELETED" == trim(socket_read(connection)));
}
String memcache_get(u64 connection, String key, String default_value)
{
auto res = memcache_command(connection, String("get ")+memcache_escape_key(key));
String t = nibble(res, " ");
if(t == "VALUE")
{
String key = nibble(res, " ");
String meta = nibble(res, " ");
u32 length = stoi(nibble(res, "\r\n"));
return(res.substr(0, length));
}
return(default_value);
}
StringMap memcache_get_multiple(u64 connection, StringList keys)
{
StringMap result;
// to do: escape key String
auto res = memcache_command(connection, String("get ")+join(memcache_escape_keys(keys), " "));
while(res.length() > 0)
{
String t = nibble(res, " ");
if(t == "VALUE")
{
String key = nibble(res, " ");
String meta = nibble(res, " ");
u32 length = stoi(nibble(res, "\r\n"));
result[key] = res.substr(0, length);
res = res.substr(length+2);
}
}
return(result);
}
void on_segfault(int sig)
{
void *array[10];
size_t size;
// get void*'s for all entries on the stack
size = backtrace(array, 10);
// print out all the frames to stderr
fprintf(stderr, "SEG FAULT: %d:\n", sig);
backtrace_symbols_fd(array, size, STDERR_FILENO);
exit(1);
}
struct Worker {
pid_t pid;
};
std::map<pid_t, Worker> workers;
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/prctl.h>
void spawn_subprocess(std::function<void()> exec_after_spawn)
{
parent_pid = getpid();
pid_t p;
p = fork();
if(p == 0)
{
my_pid = getpid();
//printf("(C) child procress started, PID:%i\n", my_pid);
prctl(PR_SET_PDEATHSIG, SIGHUP);
exec_after_spawn();
}
else
{
Worker w;
w.pid = p;
workers[w.pid] = w;
printf("(P) child procress spawned: PID %i\n", p);
}
}
void on_child_exit(int sig)
{
pid_t pid;
int status;
if ((pid = waitpid(-1, &status, WNOHANG)) != -1)
{
if(workers.count(pid) > 0)
{
workers.erase(pid);
printf("(P) child terminated (PID:%i)\n", pid);
//spawn_subprocess();
}
}
}

View File

@ -1,36 +1,40 @@
#include <signal.h> #include <signal.h>
string shell_exec(string cmd); String shell_exec(String cmd);
string shell_escape(string raw); String shell_escape(String raw);
string basename(string fn); String basename(String fn);
string dirname(string fn); String dirname(String fn);
bool mkdir(string path); bool mkdir(String path);
bool file_exists(string path); bool file_exists(String path);
string file_get_contents(string file_name); String file_get_contents(String file_name);
bool file_put_contents(string file_name, string content); bool file_put_contents(String file_name, String content);
string get_cwd(); String get_cwd();
void set_cwd(string path); void set_cwd(String path);
time_t file_mtime(string file_name); time_t file_mtime(String file_name);
void unlink(string file_name); void unlink(String file_name);
string expand_path(string path); String expand_path(String path);
void on_segfault(int sig); void on_segfault(int sig);
f64 microtime(); f64 microtime();
u64 time(); u64 time();
string date(string format = "", u64 timestamp = 0); String date(String format = "", u64 timestamp = 0);
string gmdate(string format = "", u64 timestamp = 0); String gmdate(String format = "", u64 timestamp = 0);
u64 parse_time(string time_string); u64 parse_time(String time_String);
u64 socket_connect(string host, short port); u64 socket_connect(String host, short port);
void socket_close(u64 sockfd); void socket_close(u64 sockfd);
bool socket_write(u64 sockfd, string data); bool socket_write(u64 sockfd, String data);
string socket_read(u64 sockfd, u32 max_length = 1024*128, u32 timeout = 1); String socket_read(u64 sockfd, u32 max_length = 1024*128, u32 timeout = 1);
string memcache_escape_key(string key); String memcache_escape_key(String key);
StringList memcache_escape_keys(StringList keys); StringList memcache_escape_keys(StringList keys);
u64 memcache_connect(string host = "127.0.0.1", short port = 11211); u64 memcache_connect(String host = "127.0.0.1", short port = 11211);
string memcache_command(u64 connection, string command); String memcache_command(u64 connection, String command);
bool memcache_set(u64 connection, string key, string value, u64 expires_in = 60*60); bool memcache_set(u64 connection, String key, String value, u64 expires_in = 60*60);
bool memcache_delete(u64 connection, string key); bool memcache_delete(u64 connection, String key);
string memcache_get(u64 connection, string key, string default_value = ""); String memcache_get(u64 connection, String key, String default_value = "");
StringMap memcache_get_multiple(u64 connection, StringList keys); StringMap memcache_get_multiple(u64 connection, StringList keys);
pid_t parent_pid = 0;
pid_t my_pid = 0;

62
src/lib/types.cpp Normal file
View File

@ -0,0 +1,62 @@
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <filesystem>
#include <ctype.h>
#include <fstream>
#include <sys/stat.h>
#include <ctime>
#include <dlfcn.h>
#include <limits.h>
#include <algorithm>
#include <sys/stat.h>
#include <iostream>
#include "types.h"
SharedUnit::~SharedUnit()
{
if(so_handle)
{
dlclose(so_handle);
}
}
String nibble(String div, String& haystack)
{
auto pos = haystack.find(div);
if(pos == String::npos)
{
auto result = haystack;
haystack.clear();
return(result);
}
else
{
auto result = haystack.substr(0, pos);
haystack.erase(0, pos+div.length());
return(result);
}
}
void Request::invoke(String file_name)
{
DTree call_param;
compiler_invoke(this, file_name, call_param);
}
void Request::invoke(String file_name, DTree& call_param)
{
compiler_invoke(this, file_name, call_param);
}
void Request::print(String s)
{
out.append(s);
}
Request::~Request()
{
for(auto& sockfd : resources.sockets)
close(sockfd);
}

View File

@ -4,64 +4,35 @@
#include <vector> #include <vector>
#include <functional> #include <functional>
typedef std::string string;
typedef unsigned char u8; typedef unsigned char u8;
typedef signed char s8; typedef signed char s8;
typedef unsigned short u16; typedef unsigned short u16;
typedef signed short s16; typedef signed short s16;
typedef unsigned int u32; typedef unsigned int u32;
typedef signed int s32; typedef signed int s32;
typedef float f32; typedef float f32;
typedef double f64; typedef double f64;
typedef unsigned long long u64; typedef unsigned long long u64;
typedef long long s64; typedef long long s64;
//#define to_string(a) std::to_string(a) typedef std::string String;
#define echo(s) context->print(s)
typedef std::map<string, string> StringMap; #define DEBUG_MEMORY_OFF;
typedef std::list<string> StringList; #define GLOBAL_ARENA_ALLOCATOR;
struct Request;
struct DTree;
typedef void (*call_handler)(DTree& call_param);
typedef void (*request_handler)(Request* request);
string to_string(u64 v) { return(std::to_string(v)); }
string to_string(s64 v) { return(std::to_string(v)); }
string to_string(f64 v) { return(std::to_string(v)); }
struct ServerSettings {
string BIN_DIRECTORY = "work";
string COMPILE_SCRIPT = "scripts/compile";
string LIT_ESC = "3d5b5_1";
string CONTENT_TYPE = "text/html; charset=utf-8";
string SOCKET_PATH = "/run/uce.sock";
string TMP_UPLOAD_PATH = "/tmp/uce/uploads";
string SESSION_PATH = "/tmp/uce/sessions";
string COMPILER_SYS_PATH = ".";
u32 LISTEN_PORT = 9993;
u64 SESSION_TIME = 60*60*24*30;
};
struct MemoryArena { struct MemoryArena {
void* data; u8* data;
u64 size = 0; u64 size = 0;
u64 capacity = 0; u64 capacity = 0;
String name = "unnamed";
MemoryArena(u64 cap) MemoryArena(u64 cap, String _name = "unnamed")
{ {
name = _name;
capacity = cap; capacity = cap;
printf("(i) memory arena %p created with capacity of %i bytes\n", this, capacity); printf("(i) memory arena %s created with capacity of %llu bytes\n", name.c_str(), capacity);
data = malloc(cap); data = (u8*)malloc(cap);
} }
~MemoryArena() ~MemoryArena()
@ -71,56 +42,132 @@ struct MemoryArena {
void clear() void clear()
{ {
printf("(i) memory arena %p cleared after high mark of %i bytes\n", this, size); #ifdef DEBUG_MEMORY
printf("(i) memory arena %s cleared after high mark of %llu bytes\n", name.c_str(), size);
#endif
size = 0; size = 0;
} }
void* get(u64 size_needed) void* get(u64 size_needed)
{ {
if(size_needed + size >= capacity) u64 size_aligned = 8 + (8 * ((size_needed) / 8));
u8* result = data + size;
if(size_aligned + size >= capacity)
{ {
printf("(!) memory arena %p capacity (%i) exceeded by allocation of %i bytes\n", this, capacity, size_needed); printf("(!) memory arena '%s' capacity (%llu) exceeded %llu/%llu + %llu >= %llu\n",
name.c_str(), capacity, size_needed, size_aligned, size, capacity);
return(0); return(0);
} }
void* result = (char *)data + size; size += size_aligned;
size += size_needed; #ifdef DEBUG_MEMORY
printf("(i) memory arena %s [+%llu]:%p alloc %llu/%llu bytes\n", name.c_str(), size, result, size_needed, size_aligned);
#endif
return(result); return(result);
} }
}; };
MemoryArena* current_memory_arena = 0;
void switch_to_system_alloc()
{
current_memory_arena = 0;
}
void switch_to_arena(MemoryArena* a)
{
current_memory_arena = a;
}
#ifdef GLOBAL_ARENA_ALLOCATOR
void * operator new(decltype(sizeof(0)) n) noexcept(false)
{
if(current_memory_arena)
{
return(current_memory_arena->get(n));
}
else
{
return(malloc(n));
}
}
void operator delete(void * p) throw()
{
if(current_memory_arena)
{
}
else
{
free(p);
}
}
#endif
//#define to_String(a) std::to_String(a)
#define echo(s) context->print(s)
typedef std::map<String, String> StringMap;
typedef std::vector<String> StringList;
struct Request;
struct DTree;
typedef void (*call_handler)(DTree& call_param);
typedef void (*request_handler)(Request* request);
String to_string(s64 v) { return(std::to_string(v)); }
struct ServerSettings {
String BIN_DIRECTORY = "work";
String COMPILE_SCRIPT = "scripts/compile";
String LIT_ESC = "3d5b5_1";
String CONTENT_TYPE = "text/html; charset=utf-8";
String SOCKET_PATH = "/run/uce.sock";
String TMP_UPLOAD_PATH = "/tmp/uce/uploads";
String SESSION_PATH = "/tmp/uce/sessions";
String COMPILER_SYS_PATH = ".";
u32 LISTEN_PORT = 9993;
u64 SESSION_TIME = 60*60*24*30;
u32 WORKER_COUNT = 4;
u32 MAX_MEMORY = 1024*1024*16;
};
struct SharedUnit { struct SharedUnit {
string file_name; String file_name;
string so_name; String so_name;
string src_path; String src_path;
string bin_path; String bin_path;
string pre_path; String pre_path;
string src_file_name; String src_file_name;
string bin_file_name; String bin_file_name;
string pre_file_name; String pre_file_name;
void* so_handle; void* so_handle;
request_handler on_setup; request_handler on_setup;
call_handler on_render; call_handler on_render;
string compiler_messages; String compiler_messages;
time_t last_compiled; time_t last_compiled;
~SharedUnit(); ~SharedUnit();
}; };
struct UploadedFile { struct UploadedFile {
string file_name; String file_name;
string tmp_name; String tmp_name;
u32 size; u32 size;
}; };
struct ServerState { struct ServerState {
std::map<string, SharedUnit*> units; std::map<String, SharedUnit*> units;
ServerSettings config; ServerSettings config;
}; };
@ -132,13 +179,13 @@ struct URI {
}; };
string nibble(string div, string& haystack); String nibble(String div, String& haystack);
template <typename ITYPE> template <typename ITYPE>
string to_hex(ITYPE w, size_t hex_len = sizeof(ITYPE)<<1) String to_hex(ITYPE w, size_t hex_len = sizeof(ITYPE)<<1)
{ {
static const char* digits = "0123456789ABCDEF"; static const char* digits = "0123456789ABCDEF";
string rc(hex_len,'0'); String rc(hex_len,'0');
for (size_t i=0, j=(hex_len-1)*4 ; i<hex_len; ++i,j-=4) for (size_t i=0, j=(hex_len-1)*4 ; i<hex_len; ++i,j-=4)
rc[i] = digits[(w>>j) & 0x0f]; rc[i] = digits[(w>>j) & 0x0f];
return(rc); return(rc);
@ -146,7 +193,7 @@ string to_hex(ITYPE w, size_t hex_len = sizeof(ITYPE)<<1)
#include "dtree.h" #include "dtree.h"
void compiler_invoke(Request* context, string file_name, DTree& call_param); void compiler_invoke(Request* context, String file_name, DTree& call_param);
struct Request { struct Request {
@ -160,19 +207,18 @@ struct Request {
DTree var; DTree var;
string session_id = ""; String session_id = "";
string session_name = ""; String session_name = "";
std::vector<UploadedFile> uploaded_files; std::vector<UploadedFile> uploaded_files;
StringMap header; StringMap header;
StringList set_cookies; StringList set_cookies;
MemoryArena* out_arena; MemoryArena* request_arena;
MemoryArena* memory_arena;
std::string in; String in;
std::string out; String out;
std::string err; String err;
f64 time_init; f64 time_init;
f64 time_start; f64 time_start;
@ -183,9 +229,9 @@ struct Request {
std::vector<void*> mysql_connections; std::vector<void*> mysql_connections;
} resources; } resources;
void invoke(string file_name); void invoke(String file_name);
void invoke(string file_name, DTree& call_param); void invoke(String file_name, DTree& call_param);
void print(string s); void print(String s);
~Request(); ~Request();
}; };

9
src/lib/uce_lib.cpp Normal file
View File

@ -0,0 +1,9 @@
#include "types.cpp"
#include "dtree.cpp"
#include "functionlib.cpp"
#include "sys.cpp"
#include "uri.cpp"
#include "compiler.cpp"
#include "mysql-connector.cpp"

13
src/lib/uce_lib.h Normal file
View File

@ -0,0 +1,13 @@
#include "types.h"
#include "functionlib.h"
#include "sys.h"
#include "uri.h"
#include "compiler.h"
#include "mysql-connector.h"
extern "C" void set_current_request(Request* _request)
{
context = _request;
signal(SIGSEGV, on_segfault);
}

316
src/lib/uri.cpp Normal file
View File

@ -0,0 +1,316 @@
#include "uri.h"
String var_dump(URI uri, String prefix, String postfix)
{
return(
prefix + "URI Parts: " + postfix +
var_dump(uri.parts, prefix+" ", postfix)+
prefix + " Query: " + postfix +
var_dump(uri.query, prefix+" ", postfix)
);
}
String uri_decode(String q)
{
String result;
for(u32 i = 0; i < q.length(); i++)
{
char c = q[i];
if(c == '%' && q[i+1] != '%')
{
result.append(1, hex_to_u8(q.substr(i+1, 2)));
i += 2;
}
else
{
result.append(1, c);
}
}
return(result);
}
String uri_encode(String q)
{
String result;
for(u32 i = 0; i < q.length(); i++)
{
char c = q[i];
if(isalnum(c) || c == '~' || c == '.' || c == '_' || c == '-')
result.append(1, c);
else
{
result.append(1, '%');
result.append(to_hex(c));
}
}
return(result);
}
StringMap parse_query(String q)
{
StringMap result;
if(q.length() == 0)
return(result);
bool is_key = true;
String key = "";
String value = "";
for (char &c: q)
{
if(c == '=')
{
is_key = !is_key;
}
else if(c == '&')
{
result[uri_decode(key)] = uri_decode(value);
key = "";
value = "";
is_key = true;
}
else if(is_key)
{
key.append(1, c);
}
else
{
value.append(1, c);
}
}
result[uri_decode(key)] = uri_decode(value);
return(result);
}
String encode_query(StringMap map)
{
String result;
for (auto it = map.begin(); it != map.end(); ++it)
{
if(result.length() > 0)
result.append(1, '&');
result.append(uri_encode(it->first) + "=" + uri_encode(it->second));
}
return(result);
}
StringMap parse_multipart(String q, String boundary, std::vector<UploadedFile>& uploaded_files)
{
StringMap result;
auto i = boundary.length();
while(i < q.length())
{
auto end_pos = q.find(boundary, i);
if(end_pos != String::npos)
{
String field = q.substr(i, end_pos - i);
nibble(":", field);
String ftype = trim(nibble(";", field));
if(ftype == "form-data")
{
nibble("=\"", field);
String field_name = nibble("\"", field);
result[field_name] = field.substr(4, field.length()-6);
}
else if(ftype == "attachment")
{
nibble("=\"", field);
UploadedFile f;
f.tmp_name = context->server->config.TMP_UPLOAD_PATH + std::to_string(rand());
f.file_name = nibble("\"", field);
String bin = field.substr(4, field.length()-6);
f.size = bin.length();
file_put_contents(f.tmp_name, bin);
uploaded_files.push_back(f);
}
}
else
{
// we're done
end_pos = q.length();
}
i = end_pos + boundary.length();
}
return(result);
}
URI parse_uri(String uri_String)
{
URI result;
u8 state = 0;
String current = "";
char expect = 0;
result.parts["raw"] = uri_String;
String part_names[] = {
"scheme",
"host",
"port",
"path",
"query",
"fragment",
};
if(uri_String[0] == '/')
state = 3;
for (char &c: uri_String)
{
bool append_it = true;
if(expect && expect != c)
{
result.parts["error"] = String("\'") + c + String("\' expected");
result.parts["error_parsing"] = current;
result.parts["error_part"] = part_names[state];
return(result);
}
expect = 0;
switch(state)
{
case(0): // scheme
if(c == ':')
{
result.parts[part_names[state]] = current;
append_it = false;
current = "";
state = 1;
}
break;
case(1): // host name
if(c == '/')
{
if(current == "")
{
append_it = false;
break;
}
result.parts[part_names[state]] = current;
append_it = false;
current = "";
state = 3;
expect = '/';
}
else if(c == ':')
{
result.parts[part_names[state]] = current;
append_it = false;
current = "";
state = 2;
}
break;
case(2): // port
if(c == '/')
{
result.parts[part_names[state]] = current;
append_it = false;
current = "";
state = 3;
expect = '/';
}
break;
case(3): // path
if(c == '/' && current == "")
{
append_it = false;
break;
}
if(c == '?')
{
result.parts[part_names[state]] = current;
append_it = false;
current = "";
state = 4;
}
break;
case(4): // query
if(c == '#')
{
result.parts[part_names[state]] = current;
append_it = false;
current = "";
state = 5;
}
break;
}
if(append_it)
current.append(1, c);
}
result.parts[part_names[state]] = current;
result.query = parse_query(result.parts["query"]);
return(result);
}
void set_cookie(
String name, String value,
u64 expires, String path, String domain,
bool secure, bool http_only)
{
String cookie = "Set-Cookie: ";
cookie.append(uri_encode(name) + "=" + uri_encode(value));
if(expires > 0)
cookie.append(String("; Expires=") + gmdate("RFC1123", expires));
context->set_cookies.push_back(cookie);
context->cookies[name] = value;
}
StringMap parse_cookies(String cookie_String)
{
StringMap result;
while(cookie_String.length() > 0)
{
String key = trim(nibble("=", cookie_String));
String value = nibble(";", cookie_String);
result[key] = value;
}
return(result);
}
String make_session_id()
{
return(to_hex(rand())+to_hex(rand())+to_hex(rand())+to_hex(rand()));
}
StringMap load_session_data(String session_id)
{
return(parse_query(file_get_contents(context->server->config.SESSION_PATH + "/" + session_id)));
}
void save_session_data(String session_id, StringMap data)
{
file_put_contents(context->server->config.SESSION_PATH + "/" + session_id, encode_query(data));
}
String session_start(String session_name)
{
if(context->cookies[session_name].length() == 0)
{
set_cookie(session_name, make_session_id(), time() + context->server->config.SESSION_TIME);
}
context->session_id = context->cookies[session_name];
context->session_name = session_name;
context->session = load_session_data(context->session_id);
return(context->session_id);
}
void session_destroy(String session_name)
{
if(context->cookies[session_name].length() > 0)
{
set_cookie(session_name, "", time() - context->server->config.SESSION_TIME);
context->session.clear();
context->session_id = "";
}
}

View File

@ -1,19 +1,19 @@
string var_dump(URI uri, string prefix = "", string postfix = "\n"); String var_dump(URI uri, String prefix = "", String postfix = "\n");
string uri_decode(string q); String uri_decode(String q);
string uri_encode(string q); String uri_encode(String q);
StringMap parse_query(string q); StringMap parse_query(String q);
string encode_query(StringMap map); String encode_query(StringMap map);
StringMap parse_multipart(string q, string boundary, std::vector<UploadedFile>& uploaded_files); StringMap parse_multipart(String q, String boundary, std::vector<UploadedFile>& uploaded_files);
URI parse_uri(string uri_string); URI parse_uri(String uri_String);
void set_cookie( void set_cookie(
string name, string value = "", String name, String value = "",
u64 expires = 0, string path = "/", string domain = "", u64 expires = 0, String path = "/", String domain = "",
bool secure = false, bool http_only = true); bool secure = false, bool http_only = true);
StringMap parse_cookies(string cookie_string); StringMap parse_cookies(String cookie_String);
string make_session_id(); String make_session_id();
StringMap load_session_data(string session_id); StringMap load_session_data(String session_id);
void save_session_data(string session_id, StringMap data); void save_session_data(String session_id, StringMap data);
string session_start(string session_name = "uce-session"); String session_start(String session_name = "uce-session");
void session_destroy(string session_name = "uce-session"); void session_destroy(String session_name = "uce-session");

View File

@ -4,13 +4,14 @@
FastCGIServer server; FastCGIServer server;
ServerState server_state; ServerState server_state;
MemoryArena* request_arena;
int handle_request(FastCGIRequest& request) { int handle_request(FastCGIRequest& request) {
// This is always the first event to occur. It occurs when the // This is always the first event to occur. It occurs when the
// server receives all parameters. There may be more data coming on the // server receives all parameters. There may be more data coming on the
// standard input stream. // standard input stream.
request.time_init = microtime(); request.time_init = microtime();
if (request.params.count("REQUEST_URI")) if (request.params.count("REQUEST_URI"))
return 0; // OK, continue processing return 0; // OK, continue processing
else else
return 1; // stop processing and return error code return 1; // stop processing and return error code
@ -18,7 +19,7 @@ int handle_request(FastCGIRequest& request) {
int handle_data(FastCGIRequest& request) { int handle_data(FastCGIRequest& request) {
// This event occurs when data is received on the standard input stream. // This event occurs when data is received on the standard input stream.
// A simple string is used to hold the input stream, so it is the // A simple String is used to hold the input stream, so it is the
// responsibility of the application to remember which data it has // responsibility of the application to remember which data it has
// processed. The application may modify it; new data will be appended // processed. The application may modify it; new data will be appended
// to it by the server. The same goes for the output and error streams: // to it by the server. The same goes for the output and error streams:
@ -33,9 +34,6 @@ int handle_data(FastCGIRequest& request) {
return 0; // still OK return 0; // still OK
} }
MemoryArena output_buffer(1024*1024*16);
MemoryArena memory_buffer(1024*1024*16);
int handle_complete(FastCGIRequest& request) { int handle_complete(FastCGIRequest& request) {
// The event handler can also be a class member function. This // The event handler can also be a class member function. This
// event occurs when the parameters and standard input streams are // event occurs when the parameters and standard input streams are
@ -44,26 +42,22 @@ int handle_complete(FastCGIRequest& request) {
context = &request; context = &request;
request.server = &server_state; request.server = &server_state;
request.out_arena = &output_buffer;
request.out_arena->clear();
request.memory_arena = &memory_buffer;
request.memory_arena->clear();
request.time_start = microtime(); request.time_start = microtime();
request.header["Content-Type"] = context->server->config.CONTENT_TYPE; request.header["Content-Type"] = context->server->config.CONTENT_TYPE;
request.get = parse_query(request.params["QUERY_STRING"]); request.get = parse_query(request.params["QUERY_String"]);
if(request.params["HTTP_COOKIE"].length() > 0) if(request.params["HTTP_COOKIE"].length() > 0)
request.cookies = parse_cookies(request.params["HTTP_COOKIE"]); request.cookies = parse_cookies(request.params["HTTP_COOKIE"]);
string ct_info = request.params["CONTENT_TYPE"]; String ct_info = request.params["CONTENT_TYPE"];
string ct_type = nibble(";", ct_info); String ct_type = nibble(";", ct_info);
if(request.params["REQUEST_METHOD"] == "POST") if(request.params["REQUEST_METHOD"] == "POST")
{ {
if(ct_type == "multipart/form-data") if(ct_type == "multipart/form-data")
{ {
nibble("boundary=", ct_info); nibble("boundary=", ct_info);
request.post = parse_multipart(request.in, string("--")+ct_info, request.uploaded_files); request.post = parse_multipart(request.in, String("--")+ct_info, request.uploaded_files);
} }
else else
{ {
@ -72,9 +66,12 @@ int handle_complete(FastCGIRequest& request) {
} }
// printf("(i) request ready\n"); // printf("(i) request ready\n");
//current_memory_arena = request_arena;
//request_arena->clear();
request.invoke(request.params["SCRIPT_FILENAME"]); request.invoke(request.params["SCRIPT_FILENAME"]);
switch_to_system_alloc();
string headers = String headers =
var_dump(request.header, "", "\r\n") + var_dump(request.header, "", "\r\n") +
var_dump(request.set_cookies, "", "\r\n") + var_dump(request.set_cookies, "", "\r\n") +
"\r\n"; "\r\n";
@ -91,7 +88,8 @@ int handle_complete(FastCGIRequest& request) {
cleanup_mysql_connections(); cleanup_mysql_connections();
request.time_end = microtime(); request.time_end = microtime();
printf("(r) %s\t%0.6fs\t%0.1fkB\n", printf("(r) pid:%i\t%s\t%0.6fs\t%0.1fkB\n",
my_pid,
request.params["REQUEST_URI"].c_str(), request.params["REQUEST_URI"].c_str(),
request.time_end - request.time_start, request.time_end - request.time_start,
(f32)(request.out.length()/1024) (f32)(request.out.length()/1024)
@ -100,20 +98,30 @@ int handle_complete(FastCGIRequest& request) {
return 0; return 0;
} }
int main(int argc, char** argv) void listen_for_connections()
{ {
signal(SIGSEGV, on_segfault);
srand(time());
// Set up our request handlers
server.request_handler(&handle_request); server.request_handler(&handle_request);
server.data_handler(&handle_data); server.data_handler(&handle_data);
server.complete_handler(&handle_complete); server.complete_handler(&handle_complete);
request_arena = new MemoryArena(server_state.config.MAX_MEMORY, "request");
for(;;)
{
server.process();
}
}
int main(int argc, char** argv)
{
printf("(P) Starting parent server PID:%i\n", getpid());
signal(SIGSEGV, on_segfault);
signal(SIGCHLD, on_child_exit);
srand(time());
//if(server_state.config.COMPILER_SYS_PATH == "") //if(server_state.config.COMPILER_SYS_PATH == "")
server_state.config.COMPILER_SYS_PATH = get_cwd(); server_state.config.COMPILER_SYS_PATH = get_cwd();
printf("MySQL client version: %s\n", mysql_get_client_info()); // printf("MySQL client version: %s\n", mysql_get_client_info());
printf("Compiler base path: %s\n", server_state.config.COMPILER_SYS_PATH.c_str()); printf("Compiler base path: %s\n", server_state.config.COMPILER_SYS_PATH.c_str());
@ -143,7 +151,15 @@ int main(int argc, char** argv)
{ {
std::cout << e.what(); std::cout << e.what();
}*/ }*/
server.process_forever();
for(;;)
{
while(workers.size() < server_state.config.WORKER_COUNT)
{
spawn_subprocess(listen_for_connections);
}
sleep(1);
}
return 0; return 0;
} }

View File

@ -15,17 +15,17 @@ RENDER()
<pre><? <pre><?
t.set("string test"); t.set("String test");
echo(string("string test: ") + t.to_string()+"\n"); echo(String("String test: ") + t.to_string()+"\n");
t.set(true); t.set(true);
echo(string("bool test: ") + t.to_string()+"\n"); echo(String("bool test: ") + t.to_string()+"\n");
t.set(1234.5678); t.set(1234.5678);
echo(string("float test: ") + t.to_string()+"\n"); echo(String("float test: ") + t.to_string()+"\n");
t.set(&t); t.set(&t);
echo(string("pointer test: ") + t.to_string()+"\n"); echo(String("pointer test: ") + t.to_string()+"\n");
?></pre> ?></pre>

View File

@ -3,7 +3,6 @@
void show_stuff() void show_stuff()
{ {
//context->header["Content-Type"] = "text/plain"; //context->header["Content-Type"] = "text/plain";
//echo(to_string(time())+"\n");
<html> <html>
Mwahahaaha <?= time() ?> Mwahahaaha <?= time() ?>

View File

@ -24,7 +24,14 @@ RENDER()
<li><a href="memcached.uce">Memcached</a></li> <li><a href="memcached.uce">Memcached</a></li>
<li><a href="mysql.uce">MySQL Connector</a></li> <li><a href="mysql.uce">MySQL Connector</a></li>
</ul> </ul>
<pre><?
echo("Worker PID: "+std::to_string(my_pid)+"\n");
echo("Parent PID: "+std::to_string(parent_pid)+" \n");
?></pre>
<pre><?= (var_dump(p)) ?></pre> <pre><?= (var_dump(p)) ?></pre>
</> </>
String test;
test.assign(context->out);
} }

View File

@ -28,7 +28,7 @@ RENDER()
t["g"]["x"]["y4"] = "Ünicödä"; t["g"]["x"]["y4"] = "Ünicödä";
t["l"] = context->params; t["l"] = context->params;
string j; String j;
echo(j = json_encode(t)); echo(j = json_encode(t));
?></pre> ?></pre>

View File

@ -13,7 +13,7 @@ RENDER()
<label>MySQL Connection</label> <label>MySQL Connection</label>
<pre><? <pre><?
string query = "SELECT * FROM :table WHERE x = :val"; String query = "SELECT * FROM :table WHERE x = :val";
StringMap params; StringMap params;
params["table"] = "TableName"; params["table"] = "TableName";
params["val"] = "Dubious\\Value'Name\nwith;breaks"; params["val"] = "Dubious\\Value'Name\nwith;breaks";

View File

@ -12,7 +12,7 @@ RENDER()
</> </>
string action = context->get["action"]; String action = context->get["action"];
if(context->cookies["uce-session"].length() > 0) if(context->cookies["uce-session"].length() > 0)
session_start(); session_start();
if(action == "start") if(action == "start")

View File

@ -1,17 +1,17 @@
Library Library
================================= =================================
- mysql
- sha1 / md5 / meow - sha1 / md5 / meow
Bugs Bugs
================================= =================================
- shell_escape() - shell_escape()
- we crash on illegal paths
Framework Framework
================================= =================================
- Locks for file i/o
- Locks for compiler
- Check: File Upload - Check: File Upload
- Proxy mode - Proxy mode
- WebSockets - WebSockets