diff --git a/bin/uce_fastcgi.debug.linux.bin b/bin/uce_fastcgi.debug.linux.bin index 11b57ac..5264f78 100755 Binary files a/bin/uce_fastcgi.debug.linux.bin and b/bin/uce_fastcgi.debug.linux.bin differ diff --git a/build_linux.sh b/build_linux.sh index e693f95..a27f576 100755 --- a/build_linux.sh +++ b/build_linux.sh @@ -22,7 +22,8 @@ LIBS="-ldl -lm -lpthread " SRCFLAGS="-D EXEC_NAME=\"$GF\" -D PLATFORM_NAME=\"linux\"" echo "Compliling executable..." -time -p $COMPILER src/linux_fastcgi.cpp $SRCFLAGS $FLAGS $LIBS -o bin/$GF.$BUILDMODE.linux.bin +#time -p +$COMPILER src/linux_fastcgi.cpp $SRCFLAGS $FLAGS $LIBS -o bin/$GF.$BUILDMODE.linux.bin 2>&1 if [ $? -eq 0 ] then diff --git a/src/fastcgi/src/fcgicc.h b/src/fastcgi/src/fcgicc.h index ce0558f..31892a0 100644 --- a/src/fastcgi/src/fcgicc.h +++ b/src/fastcgi/src/fcgicc.h @@ -97,7 +97,7 @@ protected: bool close_socket; }; - typedef std::map Pairs; + typedef StringMap Pairs; std::vector listen_sockets; std::vector listen_unlink; diff --git a/src/lib/compiler.h b/src/lib/compiler.h index 8caf124..43b8496 100644 --- a/src/lib/compiler.h +++ b/src/lib/compiler.h @@ -1,6 +1,6 @@ #include -#define RENDER() extern "C" void render() +#define RENDER() extern "C" void render() typedef void (*call_handler)(); typedef void (*request_handler)(Request* request); @@ -30,12 +30,12 @@ string process_html_literal(string content, string file_name) string pc; string HT_START = "context->print(R\"("; string HT_END = ")\");"; - + u8 mode = 0; bool inside_quote = false; string code_buffer = ""; bool is_field = false; - + for(u32 i = 0; i < content.length(); i++) { char c = content[i]; @@ -53,10 +53,10 @@ string process_html_literal(string content, string file_name) if(is_field) { pc.append( - HT_END + + HT_END + "echo(html_escape( " + code_buffer + - " )); " + + " )); " + HT_START ); } @@ -67,7 +67,7 @@ string process_html_literal(string content, string file_name) } else { - code_buffer.append(1, c); + code_buffer.append(1, c); } } else if(!inside_quote && c == '<' && content[i+1] == '?') @@ -85,11 +85,11 @@ string process_html_literal(string content, string file_name) } mode = 1; } - else if(c == '\"') + /*else if(c == '\"') { inside_quote = !inside_quote; pc.append(1, c); - } + }*/ else { pc.append(1, c); @@ -115,7 +115,7 @@ string preprocess(string content, string file_name) if(end_pos != string::npos) { u32 len = token.length() + 3 + end_pos - i; - html_buffer.append(content.substr(i, len)); + html_buffer.append(content.substr(i, len - (token.length() == 0 ? 3 : 0))); i += len - 1; pc.append(process_html_literal(html_buffer, file_name)); } @@ -128,17 +128,24 @@ string preprocess(string content, string file_name) 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); + html_buffer.append(1, c); + } } - else if(!inside_quote && c == '<' && isalpha(content[i+1])) + else if(!inside_quote && c == '<' && (content[i+1] == '>' || isalpha(content[i+1]))) { mode = 1; token = ""; html_buffer = ""; - html_buffer.append(1, c); + if(content[i+1] != '>') + html_buffer.append(1, c); } else if(c == '\"') { @@ -203,7 +210,7 @@ SharedUnit* compile_shared_unit(string file_name) printf("(i) compiled unit %s\n", file_name.c_str()); - load_shared_unit(su); + load_shared_unit(su); return(su); } @@ -241,7 +248,7 @@ void invoke(Request* req, string file_name) else { su->on_setup(req); - su->on_render(); + su->on_render(); } } } @@ -249,4 +256,4 @@ void invoke(Request* req, string file_name) void invoke(string file_name) { return(invoke(context, file_name)); -} \ No newline at end of file +} diff --git a/src/lib/datastructures.h b/src/lib/datastructures.h index 474d2f0..d64ab47 100644 --- a/src/lib/datastructures.h +++ b/src/lib/datastructures.h @@ -71,7 +71,7 @@ string implode(StringList l, string delim = "\n") string html_escape(string s) { string result; - + for(u32 i = 0; i < s.length(); i++) { char c = s[i]; @@ -111,4 +111,21 @@ string html_escape(f64 a) u64 int_val(string s, u32 base = 10) { return(strtoll(s.c_str(), 0, base)); -} \ No newline at end of file +} + +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); + } +} diff --git a/src/lib/settings.h b/src/lib/settings.h index 8288ef8..b36f532 100644 --- a/src/lib/settings.h +++ b/src/lib/settings.h @@ -5,6 +5,7 @@ struct Settings { string LIT_ESC = "3d5b5_1"; string CONTENT_TYPE = "text/html; charset=utf-8"; string SOCKET_PATH = "/run/uce.sock"; + string TMP_UPLOAD = "/tmp"; u32 LISTEN_PORT = 9993; -} Config; \ No newline at end of file +} Config; diff --git a/src/lib/sys.h b/src/lib/sys.h index 5a4903e..39eeb00 100644 --- a/src/lib/sys.h +++ b/src/lib/sys.h @@ -110,3 +110,8 @@ time_t file_mtime(string file_name) return(info.st_mtime); } } + +void unlink(string file_name) +{ + remove(file_name.c_str()); +} diff --git a/src/lib/types.h b/src/lib/types.h index bf851ae..15e378b 100644 --- a/src/lib/types.h +++ b/src/lib/types.h @@ -2,6 +2,7 @@ #include #include #include +//#include #include #include #include @@ -32,5 +33,11 @@ typedef long long s64; typedef std::map StringMap; typedef std::vector StringList; +struct UploadedFile { + string file_name; + string tmp_name; + u32 size; +}; + #include "dtree.h" diff --git a/src/lib/uce_gen.h b/src/lib/uce_gen.h index 23940cf..b338c4a 100644 --- a/src/lib/uce_gen.h +++ b/src/lib/uce_gen.h @@ -6,14 +6,18 @@ #include "uri.h" struct FastCGIRequest { + StringMap params; + StringMap get; + StringMap post; + std::vector uploaded_files; + StringMap header; - URI uri; - + std::string in; std::string out; std::string err; - + f64 time_init; f64 time_start; f64 time_end; diff --git a/src/lib/uri.h b/src/lib/uri.h index fcae7eb..8e78e45 100644 --- a/src/lib/uri.h +++ b/src/lib/uri.h @@ -137,6 +137,7 @@ struct URI { result[URI::decode(key)] = URI::decode(value); key = ""; value = ""; + is_key = true; } else if(is_key) { @@ -153,6 +154,48 @@ struct URI { return(result); } + static StringMap parse_multipart(string q, string boundary, std::vector& 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 = Config.TMP_UPLOAD + 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); + } + static string decode(string q) { string result; diff --git a/src/linux_fastcgi.cpp b/src/linux_fastcgi.cpp index 5cae677..2ebd64f 100644 --- a/src/linux_fastcgi.cpp +++ b/src/linux_fastcgi.cpp @@ -11,9 +11,6 @@ int handle_request(FastCGIRequest& request) { // server receives all parameters. There may be more data coming on the // standard input stream. request.time_init = microtime(); - request.out.reserve(128 * 1024); - request.in.reserve(128 * 1024); - request.err.reserve(128 * 1024); if (request.params.count("REQUEST_URI")) return 0; // OK, continue processing else @@ -42,20 +39,39 @@ int handle_complete(FastCGIRequest& request) { // event occurs when the parameters and standard input streams are // both closed, and thus the request is complete. request.time_start = microtime(); + request.header["Content-Type"] = Config.CONTENT_TYPE; - request.uri = URI::parse(request.params["REQUEST_URI"]); - request.uri.parts["scheme"] = request.params["REQUEST_SCHEME"]; - request.uri.parts["method"] = request.params["REQUEST_METHOD"]; - request.uri.parts["host"] = request.params["HTTP_HOST"]; + request.get = URI::parse_query(request.params["QUERY_STRING"]); + + string ct_info = request.params["CONTENT_TYPE"]; + string ct_type = nibble(";", ct_info); + + if(request.params["REQUEST_METHOD"] == "POST") + { + if(ct_type == "multipart/form-data") + { + nibble("boundary=", ct_info); + request.post = URI::parse_multipart(request.in, string("--")+ct_info, request.uploaded_files); + } + else + { + request.post = URI::parse_query(request.in); + } + } invoke(&request, request.params["SCRIPT_FILENAME"]); string headers = var_dump(request.header, "", "\r\n") + "\r\n"; request.out = headers + request.out; - + + for( auto &f : request.uploaded_files) + { + unlink(f.tmp_name); + } + request.time_end = microtime(); - printf("(r) %s\t%0.6fs\t%0.1fkB\n", - request.params["REQUEST_URI"].c_str(), + printf("(r) %s\t%0.6fs\t%0.1fkB\n", + request.params["REQUEST_URI"].c_str(), request.time_end - request.time_start, (f32)(request.out.length()/1024) ); diff --git a/test/.fuse_hidden0000015700000003 b/test/.fuse_hidden0000015700000003 new file mode 100644 index 0000000..e69de29 diff --git a/test/.fuse_hidden0000015c00000005 b/test/.fuse_hidden0000015c00000005 new file mode 100644 index 0000000..e69de29 diff --git a/test/.fuse_hidden0000018600000004 b/test/.fuse_hidden0000018600000004 new file mode 100644 index 0000000..e69de29 diff --git a/test/.fuse_hidden000001f600000006 b/test/.fuse_hidden000001f600000006 new file mode 100644 index 0000000..e69de29 diff --git a/test/hello.uce b/test/hello.uce index 846d6a5..b927a87 100644 --- a/test/hello.uce +++ b/test/hello.uce @@ -1,6 +1,6 @@ -RENDER() +void show_stuff() { //context->header["Content-Type"] = "text/plain"; //echo(to_string(time())+"\n"); @@ -8,7 +8,7 @@ RENDER() Mwahahaaha
hello world: params["HTTP_HOST"] ?>
-
+		
 Display the date using any or all of the following elements:
 
 %a: abbreviated day name (i.e. mon, tue, wed)
@@ -174,3 +174,17 @@ Lifewire is part of the Dotdash publishing family.
 
 }
 
+RENDER()
+{
+
+	
+		
+		

+ UCE Test: + Index +

+ +
params) ?>
+ + +} diff --git a/test/index.uce b/test/index.uce new file mode 100644 index 0000000..a7ae0db --- /dev/null +++ b/test/index.uce @@ -0,0 +1,20 @@ + + +RENDER() +{ + + + +

+ UCE Test: + Index +

+ +
params) ?>
+ + +} diff --git a/test/post-multipart.uce b/test/post-multipart.uce new file mode 100644 index 0000000..7a87a8d --- /dev/null +++ b/test/post-multipart.uce @@ -0,0 +1,47 @@ + +void show_form() +{ + +
+
+ + "/> +
+
+ + +
+
+ +
+
+ +} + +RENDER() +{ + + <> + +

+ UCE Test: + Multipart-Encoded Form POST +

+ + + +
+ +
+ + +
post) ?>
+ + +
in ?>
+ + +
params) ?>
+ + +} diff --git a/test/post.uce b/test/post.uce new file mode 100644 index 0000000..d8df0a2 --- /dev/null +++ b/test/post.uce @@ -0,0 +1,43 @@ + +void show_form() +{ + +
+
+ + "/> +
+
+ + +
+
+ +
+
+ +} + +RENDER() +{ + + + +

+ UCE Test: + Form POST +

+ + + + +
post) ?>
+ + +
in ?>
+ + +
params) ?>
+ + +} diff --git a/test/style.css b/test/style.css new file mode 100644 index 0000000..461e468 --- /dev/null +++ b/test/style.css @@ -0,0 +1,71 @@ +* { + font-family: inherit; + font-size: inherit; + box-sizing: inherit; + color: inherit; + line-height: inherit; +} + +a { + color: yellow; +} + +h1 { + font-size: 200%; + padding-top: 8px; + padding-bottom: 8px; +} + +body { + max-width: 1024px; + margin-left: auto; + margin-right: auto; + font-family: Tahoma, Helvetica, Arial; + font-size: 1.2em; + box-sizing: border-box; + background: #24f; + color: white; + line-height: 150%; +} + +form > div, label { + display: block; + padding-top: 8px; + padding-bottom: 8px; +} + +input, textarea { + background: rgba(0,0,0,0.2); + border: 2px solid rgba(255,255,255,0.2); +} + +input[type=submit], button { + padding: 8px; + cursor: pointer; +} + +input[type=submit]:hover, button:hover { + color: yellow; + background: rgba(0,0,0,0.5); +} + +input[type=text], textarea { + padding: 8px; + width: 100%; +} + +input[type=text]:hover, textarea:hover { + background: rgba(0,0,0,0.25); +} + +textarea { + height: 20%; +} + +pre { + height: 20%; + overflow: auto; + padding. 8px; + border: 2px solid rgba(0,0,0,0.2); + background: rgba(100,100,100,0.15); +} diff --git a/todo.txt b/todo.txt index fc9a0bd..15c9ab8 100644 --- a/todo.txt +++ b/todo.txt @@ -15,13 +15,15 @@ Library Bugs ================================= - shell_escape() -- Include Path +- include path +- current request working directory +- preprocessor + - nested tags Framework ================================= - DTree -- POST - File Upload - Proxy mode - WebSockets