Refactor FastCGI server and compiler, enhance HTTP header parsing, and add WebSocket support
- Refactored FastCGIServer class to improve socket handling and added shutdown functionality. - Updated compiler functions to streamline HTML and text literal processing. - Enhanced split_kv function to support uppercase keys and added split_http_headers for better HTTP header parsing. - Introduced new session management functions to validate and handle session IDs. - Added WebSocket frame parsing and handling in the URI module. - Created systemd service scripts for easier deployment and management of the UCE FastCGI runtime. - Added new test cases for header handling, time parsing, and WebSocket functionality.
This commit is contained in:
parent
984453f336
commit
cd8f07aaa7
18
README.md
18
README.md
@ -8,6 +8,24 @@ This is in the early stages of development. Don't use this for anything importan
|
||||
|
||||
The aim of this project is to make a PHP-like runtime that enables server-side "scripting" using C/C++. At the core is a multi-worker FastCGI server that can be talked to from Nginx or similar front-end servers. UCE has a shared-nothing isolated architecture to serve page requests. To minimize the potential for memory leaks, UCE uses a per-request memory arena. UCE also provides a PHP-like API to ease web development. Advanced features such as an integrated WebSockets broker are planned. UCE aims to use minimal dependencies (at the moment, the only dependency is the Clang compiler). UCE pages are automatically recompiled and dynamically reloaded as necessary.
|
||||
|
||||
## Service Setup
|
||||
|
||||
The repository now includes a systemd unit template and a management script for the FastCGI runtime:
|
||||
|
||||
- `scripts/systemd/uce.service`
|
||||
- `scripts/systemd/manage-uce-service.sh`
|
||||
|
||||
Typical usage on the deployment host:
|
||||
|
||||
```bash
|
||||
scripts/systemd/manage-uce-service.sh setup
|
||||
scripts/systemd/manage-uce-service.sh status
|
||||
scripts/systemd/manage-uce-service.sh restart
|
||||
scripts/systemd/manage-uce-service.sh logs 200
|
||||
```
|
||||
|
||||
The service runs the runtime from the repository root so `COMPILER_SYS_PATH` resolves correctly and nginx can forward `.uce` requests to the Unix socket defined in `/etc/uce/settings.cfg`.
|
||||
|
||||
# API
|
||||
|
||||
Memcache Functions
|
||||
|
||||
@ -2,9 +2,9 @@ Output / Invocation Functions
|
||||
|
||||
call_file
|
||||
load
|
||||
ob_clear
|
||||
ob_close
|
||||
ob_get
|
||||
ob_get_clear
|
||||
ob_get_close
|
||||
ob_start
|
||||
print
|
||||
render_file
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
:sig
|
||||
void ob_clear()
|
||||
|
||||
:params
|
||||
(none)
|
||||
|
||||
:desc
|
||||
Discard the current output buffer.
|
||||
|
||||
:see
|
||||
>ob
|
||||
11
doc/pages/ob_close.txt
Normal file
11
doc/pages/ob_close.txt
Normal file
@ -0,0 +1,11 @@
|
||||
:sig
|
||||
void ob_close()
|
||||
|
||||
:params
|
||||
(none)
|
||||
|
||||
:desc
|
||||
Discard the current output buffer. If are more output buffers on the stack, switch to the next one.
|
||||
|
||||
:see
|
||||
>ob
|
||||
@ -1,11 +1,12 @@
|
||||
:sig
|
||||
String ob_get_clear()
|
||||
String ob_get_close()
|
||||
|
||||
:params
|
||||
return value : content of the current output buffer
|
||||
|
||||
:desc
|
||||
Returns the contents of the current output buffer and then discards the buffer.
|
||||
If are more output buffers on the stack, switch to the next one.
|
||||
|
||||
:see
|
||||
>ob
|
||||
@ -5,7 +5,7 @@ void ob_start()
|
||||
(none)
|
||||
|
||||
:desc
|
||||
Starts a new output buffer. All subsequent output will be directed into this buffer.
|
||||
Starts a new output buffer. All subsequent output will be directed into this buffer. Every call to ob_start() starts a new buffer and puts that buffer on the output buffer stack. ob_close() and ob_get_close() destroy buffers and remove them from the stack.
|
||||
|
||||
:see
|
||||
>ob
|
||||
|
||||
@ -4,8 +4,8 @@ TMP_UPLOAD_PATH=/tmp/uce/uploads
|
||||
SESSION_PATH=/tmp/uce/sessions
|
||||
|
||||
# LISTEN ON SOCKETS
|
||||
SOCKET_PATH=/run/uce.sock
|
||||
LISTEN_PORT=9993
|
||||
FCGI_SOCKET_PATH=/run/uce.sock
|
||||
FCGI_PORT=9993
|
||||
|
||||
# PRECOMPILE .uce FILES
|
||||
PRECOMPILE_FILES_IN=
|
||||
|
||||
@ -3,7 +3,7 @@ cd "$(dirname "$0")"
|
||||
cd ..
|
||||
|
||||
BUILDMODE=${2:-"debug"}
|
||||
OPT_FLAG="O3"
|
||||
OPT_FLAG="O0"
|
||||
GF="uce_fastcgi"
|
||||
|
||||
mkdir bin > /dev/null 2>&1
|
||||
|
||||
50
scripts/systemd/manage-uce-service.sh
Executable file
50
scripts/systemd/manage-uce-service.sh
Executable file
@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ ${EUID:-0} -ne 0 ]]; then
|
||||
echo "This script must run as root." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
UNIT_NAME="uce.service"
|
||||
UNIT_SOURCE="$SCRIPT_DIR/uce.service"
|
||||
UNIT_DEST="/etc/systemd/system/$UNIT_NAME"
|
||||
CONFIG_SOURCE="$REPO_ROOT/etc/uce/settings.cfg"
|
||||
CONFIG_DEST="/etc/uce/settings.cfg"
|
||||
|
||||
action="${1:-setup}"
|
||||
|
||||
install_unit() {
|
||||
install -D -m 0644 "$UNIT_SOURCE" "$UNIT_DEST"
|
||||
if [[ ! -f "$CONFIG_DEST" ]]; then
|
||||
install -D -m 0644 "$CONFIG_SOURCE" "$CONFIG_DEST"
|
||||
fi
|
||||
systemctl daemon-reload
|
||||
}
|
||||
|
||||
case "$action" in
|
||||
install)
|
||||
install_unit
|
||||
;;
|
||||
setup)
|
||||
install_unit
|
||||
systemctl enable --now "$UNIT_NAME"
|
||||
;;
|
||||
enable)
|
||||
install_unit
|
||||
systemctl enable "$UNIT_NAME"
|
||||
;;
|
||||
start|stop|restart|status)
|
||||
systemctl "$action" "$UNIT_NAME"
|
||||
;;
|
||||
logs)
|
||||
lines="${2:-100}"
|
||||
journalctl -u "$UNIT_NAME" -n "$lines" --no-pager
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 [install|setup|enable|start|stop|restart|status|logs [lines]]" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
22
scripts/systemd/uce.service
Normal file
22
scripts/systemd/uce.service
Normal file
@ -0,0 +1,22 @@
|
||||
[Unit]
|
||||
Description=UCE FastCGI Runtime
|
||||
After=network-online.target mariadb.service memcached.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/Code/uce.openfu.com/uce
|
||||
ExecStartPre=/usr/bin/mkdir -p /tmp/uce/work /tmp/uce/uploads /tmp/uce/sessions
|
||||
ExecStartPre=/usr/bin/rm -f /run/uce.sock
|
||||
ExecStartPre=/usr/bin/bash /Code/uce.openfu.com/uce/scripts/build_linux.sh
|
||||
ExecStart=/Code/uce.openfu.com/uce/bin/uce_fastcgi.linux.bin
|
||||
ExecStopPost=/usr/bin/rm -f /run/uce.sock
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
TimeoutStopSec=15
|
||||
KillMode=mixed
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@ -47,138 +47,150 @@
|
||||
|
||||
#include "../fastcgi_devkit/fastcgi.h"
|
||||
|
||||
|
||||
FastCGIServer::RequestInfo::RequestInfo() :
|
||||
params_closed(false),
|
||||
in_closed(false),
|
||||
output_closed(false)
|
||||
void
|
||||
FastCGIServer::shutdown()
|
||||
{
|
||||
|
||||
}
|
||||
if(getpid() != parent_pid) // if we're a child process, we must not close the handles
|
||||
return;
|
||||
|
||||
for (std::vector<int>::iterator it = server_sockets.begin();
|
||||
it != server_sockets.end(); ++it)
|
||||
{
|
||||
printf("Closing server socket %i\n", *it);
|
||||
close(*it);
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
for (std::vector<std::string>::iterator it = listen_unlink.begin();
|
||||
it != listen_unlink.end(); ++it)
|
||||
unlink(it->c_str());
|
||||
|
||||
FastCGIServer::Connection::Connection() :
|
||||
close_responsibility(false),
|
||||
close_socket(false)
|
||||
{
|
||||
for (std::map<int, Connection*>::iterator it = client_sockets.begin();
|
||||
it != client_sockets.end(); ++it)
|
||||
{
|
||||
close(it->first);
|
||||
for (RequestList::iterator req_it = it->second->requests.begin();
|
||||
req_it != it->second->requests.end(); ++req_it)
|
||||
delete req_it->second;
|
||||
delete it->second;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FastCGIServer::FastCGIServer()
|
||||
{
|
||||
sleep(1);
|
||||
server_sockets.clear();
|
||||
|
||||
}
|
||||
|
||||
FastCGIServer::~FastCGIServer()
|
||||
{
|
||||
|
||||
if(my_pid != parent_pid) // if we're a child process, we must not close the handles
|
||||
return;
|
||||
|
||||
for (std::vector<int>::iterator it = listen_sockets.begin();
|
||||
it != listen_sockets.end(); ++it)
|
||||
close(*it);
|
||||
|
||||
for (std::vector<std::string>::iterator it = listen_unlink.begin();
|
||||
it != listen_unlink.end(); ++it)
|
||||
unlink(it->c_str());
|
||||
|
||||
for (std::map<int, Connection*>::iterator it = read_sockets.begin();
|
||||
it != read_sockets.end(); ++it) {
|
||||
close(it->first);
|
||||
for (RequestList::iterator req_it = it->second->requests.begin();
|
||||
req_it != it->second->requests.end(); ++req_it)
|
||||
delete req_it->second;
|
||||
delete it->second;
|
||||
}
|
||||
|
||||
shutdown();
|
||||
}
|
||||
|
||||
int
|
||||
FastCGIServer::listen_http(unsigned tcp_port)
|
||||
{
|
||||
int server_socket = listen(tcp_port);
|
||||
server_socket_types[server_socket] = 'H';
|
||||
return server_socket;
|
||||
}
|
||||
|
||||
void
|
||||
int
|
||||
FastCGIServer::listen(unsigned tcp_port)
|
||||
{
|
||||
int listen_socket = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (listen_socket == -1)
|
||||
throw std::runtime_error("socket() failed");
|
||||
int server_socket = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (server_socket == -1)
|
||||
throw std::runtime_error("socket() failed");
|
||||
|
||||
try {
|
||||
struct sockaddr_in sa;
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(tcp_port);
|
||||
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
if (bind(listen_socket, (struct sockaddr*)&sa, sizeof(sa)) == -1)
|
||||
throw std::runtime_error("bind() failed");
|
||||
|
||||
if (::listen(listen_socket, 100))
|
||||
throw std::runtime_error("listen() failed");
|
||||
int iSetOption = 1;
|
||||
setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&iSetOption,
|
||||
sizeof(iSetOption));
|
||||
|
||||
listen_sockets.push_back(listen_socket);
|
||||
struct sockaddr_in sa;
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(tcp_port);
|
||||
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
if (bind(server_socket, (struct sockaddr*)&sa, sizeof(sa)) == -1)
|
||||
throw std::runtime_error("bind() failed");
|
||||
|
||||
if (::listen(server_socket, 100))
|
||||
throw std::runtime_error("listen() failed");
|
||||
|
||||
server_sockets.push_back(server_socket);
|
||||
} catch (...) {
|
||||
close(listen_socket);
|
||||
throw;
|
||||
close(server_socket);
|
||||
throw;
|
||||
}
|
||||
|
||||
server_socket_types[server_socket] = 'F';
|
||||
printf("(P) listening to #%i port %i\n", server_socket, tcp_port);
|
||||
return server_socket;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
int
|
||||
FastCGIServer::listen(const std::string& local_path)
|
||||
{
|
||||
int listen_socket = socket(PF_UNIX, SOCK_STREAM, 0);
|
||||
if (listen_socket == -1)
|
||||
int server_socket = socket(PF_UNIX, SOCK_STREAM, 0);
|
||||
if (server_socket == -1)
|
||||
throw std::runtime_error("socket() failed");
|
||||
|
||||
try {
|
||||
struct sockaddr_un sa;
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sun_family = AF_LOCAL;
|
||||
struct sockaddr_un sa;
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sun_family = AF_LOCAL;
|
||||
|
||||
std::string::size_type size = local_path.size();
|
||||
if (size >= sizeof(sa.sun_path))
|
||||
throw std::runtime_error("path too long");
|
||||
if (local_path.find_first_of('\0') != std::string::npos)
|
||||
throw std::runtime_error("null character in path");
|
||||
std::string::size_type size = local_path.size();
|
||||
if (size >= sizeof(sa.sun_path))
|
||||
throw std::runtime_error("path too long");
|
||||
if (local_path.find_first_of('\0') != std::string::npos)
|
||||
throw std::runtime_error("null character in path");
|
||||
|
||||
std::memcpy(sa.sun_path, local_path.data(), size);
|
||||
std::memcpy(sa.sun_path, local_path.data(), size);
|
||||
|
||||
unlink(local_path.c_str());
|
||||
try {
|
||||
if (bind(listen_socket, (struct sockaddr*)&sa,
|
||||
sizeof(sa) - (sizeof(sa.sun_path) - size - 1)) == -1)
|
||||
throw std::runtime_error("bind() failed");
|
||||
unlink(local_path.c_str());
|
||||
try {
|
||||
if (bind(server_socket, (struct sockaddr*)&sa,
|
||||
sizeof(sa) - (sizeof(sa.sun_path) - size - 1)) == -1)
|
||||
throw std::runtime_error("bind() failed");
|
||||
|
||||
if (::listen(listen_socket, 100))
|
||||
throw std::runtime_error("listen() failed");
|
||||
if (::listen(server_socket, 100))
|
||||
throw std::runtime_error("listen() failed");
|
||||
|
||||
listen_sockets.push_back(listen_socket);
|
||||
listen_unlink.push_back(local_path);
|
||||
server_sockets.push_back(server_socket);
|
||||
listen_unlink.push_back(local_path);
|
||||
|
||||
printf("(P) listening to #%i socket %s\n", server_socket, local_path.c_str());
|
||||
|
||||
} catch (...) {
|
||||
unlink(local_path.c_str());
|
||||
throw;
|
||||
}
|
||||
|
||||
} catch (...) {
|
||||
unlink(local_path.c_str());
|
||||
throw;
|
||||
}
|
||||
|
||||
} catch (...) {
|
||||
close(listen_socket);
|
||||
throw;
|
||||
close(server_socket);
|
||||
throw;
|
||||
}
|
||||
return server_socket;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FastCGIServer::abandon_files()
|
||||
int
|
||||
FastCGIServer::send_output_buffer(Connection& con)
|
||||
{
|
||||
listen_unlink.clear();
|
||||
int write_result = write(con.client_socket,
|
||||
con.output_buffer.data(),
|
||||
con.output_buffer.size());
|
||||
if (write_result == -1)
|
||||
throw std::runtime_error("write() failed");
|
||||
con.output_buffer.erase(0, write_result);
|
||||
return write_result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FastCGIServer::process(int timeout_ms)
|
||||
{
|
||||
char buffer[4096];
|
||||
char buffer[64*1024];
|
||||
fd_set fs_read;
|
||||
fd_set fs_write;
|
||||
int nfd = 0;
|
||||
@ -187,16 +199,16 @@ FastCGIServer::process(int timeout_ms)
|
||||
FD_ZERO(&fs_read);
|
||||
FD_ZERO(&fs_write);
|
||||
|
||||
for(auto socket_handle : listen_sockets)
|
||||
for(auto socket_handle : server_sockets)
|
||||
{
|
||||
FD_SET(socket_handle, &fs_read);
|
||||
nfd = std::max(nfd, socket_handle);
|
||||
}
|
||||
|
||||
for(auto con : read_sockets)
|
||||
for(auto con : client_sockets)
|
||||
{
|
||||
FD_SET(con.first, &fs_read);
|
||||
if (!con.second->output_buffer.empty())
|
||||
if (!con.second->output_buffer.empty() || con.second->close_socket)
|
||||
FD_SET(con.first, &fs_write);
|
||||
nfd = std::max(nfd, con.first);
|
||||
}
|
||||
@ -209,18 +221,29 @@ FastCGIServer::process(int timeout_ms)
|
||||
else
|
||||
throw std::runtime_error("select() failed");
|
||||
|
||||
for(auto socket_handle : listen_sockets)
|
||||
for(auto socket_handle : server_sockets)
|
||||
if (FD_ISSET(socket_handle, &fs_read))
|
||||
{
|
||||
int posix_con = accept(socket_handle, NULL, NULL);
|
||||
if (posix_con == -1)
|
||||
int client_socket = accept(socket_handle, NULL, NULL);
|
||||
if (client_socket == -1)
|
||||
throw std::runtime_error("accept() failed");
|
||||
read_sockets[posix_con] = new Connection();
|
||||
read_sockets[posix_con]->posix_con = posix_con;
|
||||
printf("Opening socket %i\n", client_socket);
|
||||
client_sockets[client_socket] = new Connection();
|
||||
client_sockets[client_socket]->client_socket = client_socket;
|
||||
client_sockets[client_socket]->server_socket = socket_handle;
|
||||
client_sockets[client_socket]->type = server_socket_types[socket_handle];
|
||||
if(client_sockets[client_socket]->type == 'H')
|
||||
{
|
||||
FastCGIRequest* new_request = new FastCGIRequest();
|
||||
new_request->resources.client_socket = client_socket;
|
||||
new_request->resources.server_socket = socket_handle;
|
||||
new_request->stats.time_init = microtime();
|
||||
client_sockets[client_socket]->requests[client_socket] = new_request;
|
||||
}
|
||||
}
|
||||
|
||||
for (std::map<int, Connection*>::iterator it = read_sockets.begin();
|
||||
it != read_sockets.end();)
|
||||
for (std::map<int, Connection*>::iterator it = client_sockets.begin();
|
||||
it != client_sockets.end();)
|
||||
{
|
||||
int read_socket = it->first;
|
||||
|
||||
@ -229,39 +252,62 @@ FastCGIServer::process(int timeout_ms)
|
||||
int read_result = read(read_socket, buffer, sizeof(buffer));
|
||||
if (read_result == -1)
|
||||
if (errno == ECONNRESET)
|
||||
goto close_socket;
|
||||
goto close_socket;
|
||||
else
|
||||
throw std::runtime_error("read() on socket failed");
|
||||
if (read_result == 0)
|
||||
it->second->close_socket = true;
|
||||
else {
|
||||
throw std::runtime_error("read() on socket failed");
|
||||
if (read_result == 0)
|
||||
{
|
||||
if(it->second->type == 'H' && it->second->input_buffer != "")
|
||||
{
|
||||
process_http_request(
|
||||
*client_sockets[it->second->client_socket]->requests[it->second->client_socket],
|
||||
it->second->input_buffer);
|
||||
if(it->second->close_socket || !it->second->output_buffer.empty())
|
||||
it->second->input_buffer = "";
|
||||
else
|
||||
it->second->close_socket = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
it->second->close_socket = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
it->second->input_buffer.append(buffer, read_result);
|
||||
process_connection_read(*it->second);
|
||||
if(it->second->type == 'H')
|
||||
{
|
||||
process_http_request(
|
||||
*client_sockets[it->second->client_socket]->requests[it->second->client_socket],
|
||||
it->second->input_buffer);
|
||||
if(it->second->close_socket || !it->second->output_buffer.empty())
|
||||
it->second->input_buffer = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
read_fgci(*it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!it->second->output_buffer.empty() &&
|
||||
FD_ISSET(read_socket, &fs_write))
|
||||
{
|
||||
process_connection_write(*it->second);
|
||||
int write_result = write(read_socket,
|
||||
it->second->output_buffer.data(),
|
||||
it->second->output_buffer.size());
|
||||
if (write_result == -1)
|
||||
throw std::runtime_error("write() failed");
|
||||
it->second->output_buffer.erase(0, write_result);
|
||||
write_fgci(*it->second);
|
||||
send_output_buffer(*it->second);
|
||||
}
|
||||
|
||||
if (it->second->close_socket && it->second->output_buffer.empty())
|
||||
{
|
||||
close_socket:
|
||||
printf("Closing socket %i\n", it->first);
|
||||
int close_result = close(it->first);
|
||||
if (close_result == -1 && errno != ECONNRESET)
|
||||
throw std::runtime_error("close() failed");
|
||||
Connection* connection = it->second;
|
||||
read_sockets.erase(it++);
|
||||
client_sockets.erase(it++);
|
||||
delete connection;
|
||||
if(calls_until_termination != -1 && read_sockets.size() == 0)
|
||||
if(calls_until_termination != -1 && client_sockets.size() == 0)
|
||||
{
|
||||
calls_until_termination -= 1;
|
||||
if(calls_until_termination <= 0)
|
||||
@ -272,6 +318,54 @@ FastCGIServer::process(int timeout_ms)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FastCGIServer::process_http_request(FastCGIRequest& request, String& data)
|
||||
{
|
||||
auto header_end = data.find("\r\n\r\n");
|
||||
if(header_end == String::npos)
|
||||
return;
|
||||
|
||||
if(request.params.size() == 0)
|
||||
{
|
||||
request.params = split_http_headers(data.substr(0, header_end));
|
||||
request.flags.params_closed = true;
|
||||
if(request.params["HTTP_SCRIPT_FILENAME"] != "")
|
||||
request.params["SCRIPT_FILENAME"] = request.params["HTTP_SCRIPT_FILENAME"];
|
||||
else if(request.params["SCRIPT_FILENAME"] == "" && request.params["DOCUMENT_URI"] != "")
|
||||
{
|
||||
String document_root = first(request.params["DOCUMENT_ROOT"], get_cwd());
|
||||
if(document_root.length() > 1 && document_root[document_root.length()-1] == '/')
|
||||
document_root.resize(document_root.length()-1);
|
||||
request.params["DOCUMENT_ROOT"] = document_root;
|
||||
request.params["SCRIPT_FILENAME"] = document_root + request.params["DOCUMENT_URI"];
|
||||
}
|
||||
}
|
||||
|
||||
request.in = data.substr(header_end + 4);
|
||||
u64 content_length = int_val(first(request.params["CONTENT_LENGTH"], "0"));
|
||||
if(request.in.length() < content_length)
|
||||
return;
|
||||
request.flags.input_closed = true;
|
||||
|
||||
if(request.params["HTTP_UPGRADE"] == "websocket")
|
||||
{
|
||||
printf("(i) got upgrade request %s\n", var_dump(request.params).c_str());
|
||||
client_sockets[request.resources.client_socket]->close_socket = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
on_complete(request);
|
||||
|
||||
assemble_output_buffer(
|
||||
request,
|
||||
client_sockets[request.resources.client_socket]);
|
||||
|
||||
printf("data written: %i bytes\n",
|
||||
client_sockets[request.resources.client_socket]->output_buffer.size());
|
||||
|
||||
client_sockets[request.resources.client_socket]->close_socket = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FastCGIServer::process_forever()
|
||||
@ -280,18 +374,8 @@ FastCGIServer::process_forever()
|
||||
process();
|
||||
}
|
||||
|
||||
int
|
||||
FastCGIServer::call_completion_handler(FastCGIRequest& request)
|
||||
{
|
||||
//printf("- request complete\n");
|
||||
////switch_to_arena(request.mem);
|
||||
auto result = on_complete(request);
|
||||
////switch_to_system_alloc();
|
||||
return(result);
|
||||
}
|
||||
|
||||
void
|
||||
FastCGIServer::process_connection_read(Connection& connection)
|
||||
FastCGIServer::read_fgci(Connection& connection)
|
||||
{
|
||||
std::string::size_type n = 0;
|
||||
while (connection.input_buffer.size() - n >= FCGI_HEADER_LEN) {
|
||||
@ -316,7 +400,7 @@ FastCGIServer::process_connection_read(Connection& connection)
|
||||
{
|
||||
case FCGI_GET_VALUES:
|
||||
{
|
||||
Pairs pairs = parse_pairs(content, content_length);
|
||||
Pairs pairs = parse_pairs_fcgi(content, content_length);
|
||||
|
||||
std::string::size_type base = connection.output_buffer.size();
|
||||
connection.output_buffer.push_back(FCGI_VERSION_1);
|
||||
@ -325,13 +409,13 @@ FastCGIServer::process_connection_read(Connection& connection)
|
||||
|
||||
for (Pairs::iterator it = pairs.begin(); it != pairs.end(); ++it)
|
||||
if (it->first == FCGI_MAX_CONNS)
|
||||
write_pair(connection.output_buffer,
|
||||
write_pair_fcgi(connection.output_buffer,
|
||||
it->first, std::string("100"));
|
||||
else if (it->first == FCGI_MAX_REQS)
|
||||
write_pair(connection.output_buffer,
|
||||
write_pair_fcgi(connection.output_buffer,
|
||||
it->first, std::string("1000"));
|
||||
else if (it->first == FCGI_MPXS_CONNS)
|
||||
write_pair(connection.output_buffer,
|
||||
write_pair_fcgi(connection.output_buffer,
|
||||
it->first, std::string("1"));
|
||||
|
||||
std::string::size_type len = connection.output_buffer.size() - base;
|
||||
@ -361,7 +445,7 @@ FastCGIServer::process_connection_read(Connection& connection)
|
||||
connection.output_buffer.append(
|
||||
reinterpret_cast<const char*>(&unknown), sizeof(unknown));
|
||||
if (connection.close_responsibility)
|
||||
connection.close_socket = true;
|
||||
connection.close_socket = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -382,12 +466,10 @@ FastCGIServer::process_connection_read(Connection& connection)
|
||||
printf("(!) %i requests in flight at the same time!\n", connection.requests.size());
|
||||
}
|
||||
|
||||
//switch_to_arena(request_arena);
|
||||
RequestInfo* new_request = new RequestInfo();
|
||||
new_request->resources.fcgi_socket = connection.posix_con;
|
||||
//new_request->mem = request_arena;
|
||||
FastCGIRequest* new_request = new FastCGIRequest();
|
||||
new_request->resources.client_socket = connection.client_socket;
|
||||
new_request->resources.server_socket = connection.server_socket;
|
||||
new_request->stats.time_init = microtime();
|
||||
//switch_to_system_alloc();
|
||||
connection.requests[request_id] = new_request;
|
||||
|
||||
break;
|
||||
@ -420,25 +502,26 @@ FastCGIServer::process_connection_read(Connection& connection)
|
||||
if (it == connection.requests.end())
|
||||
break;
|
||||
|
||||
RequestInfo& request = *it->second;
|
||||
FastCGIRequest& request = *it->second;
|
||||
//switch_to_arena(it->second->mem);
|
||||
if (!request.params_closed)
|
||||
if (!request.flags.params_closed)
|
||||
if (content_length != 0)
|
||||
request.params_buffer.append(content, content_length);
|
||||
request.resources.params_buffer.append(content, content_length);
|
||||
else {
|
||||
request.params = parse_pairs(request.params_buffer.data(),
|
||||
request.params_buffer.size());
|
||||
request.params_buffer.clear();
|
||||
request.params_closed = true;
|
||||
|
||||
request.status = on_request(request);
|
||||
if (request.status == 0 && !request.in.empty())
|
||||
{
|
||||
request.status = on_data(request);
|
||||
if (request.status == 0 && request.in_closed)
|
||||
request.status = call_completion_handler(request);
|
||||
}
|
||||
process_write_request(connection, request_id, request);
|
||||
request.params = parse_pairs_fcgi(
|
||||
request.resources.params_buffer.data(),
|
||||
request.resources.params_buffer.size());
|
||||
request.resources.params_buffer.clear();
|
||||
request.flags.params_closed = true;
|
||||
//std::cout << "Params " << var_dump(request.params) << "\n";
|
||||
request.flags.status = on_request(request);
|
||||
if (request.flags.status == 0 && !request.in.empty())
|
||||
{
|
||||
request.flags.status = on_data(request);
|
||||
if (request.flags.status == 0 && request.flags.input_closed)
|
||||
request.flags.status = on_complete(request);
|
||||
}
|
||||
request_write_fgci(connection, request_id, request);
|
||||
}
|
||||
//switch_to_system_alloc();
|
||||
break;
|
||||
@ -449,22 +532,22 @@ FastCGIServer::process_connection_read(Connection& connection)
|
||||
if (it == connection.requests.end())
|
||||
break;
|
||||
|
||||
RequestInfo& request = *it->second;
|
||||
FastCGIRequest& request = *it->second;
|
||||
//switch_to_arena(it->second->mem);
|
||||
if (!request.in_closed)
|
||||
if (!request.flags.input_closed)
|
||||
if (content_length != 0) {
|
||||
request.in.append(content, content_length);
|
||||
if (request.params_closed && request.status == 0)
|
||||
{
|
||||
request.status = on_data(request);
|
||||
process_write_request(connection, request_id, request);
|
||||
}
|
||||
request.in.append(content, content_length);
|
||||
if (request.flags.params_closed && request.flags.status == 0)
|
||||
{
|
||||
request.flags.status = on_data(request);
|
||||
request_write_fgci(connection, request_id, request);
|
||||
}
|
||||
} else {
|
||||
request.in_closed = true;
|
||||
if (request.params_closed && request.status == 0) {
|
||||
request.status = call_completion_handler(request);
|
||||
process_write_request(connection, request_id, request);
|
||||
}
|
||||
request.flags.input_closed = true;
|
||||
if (request.flags.params_closed && request.flags.status == 0) {
|
||||
request.flags.status = on_complete(request);
|
||||
request_write_fgci(connection, request_id, request);
|
||||
}
|
||||
}
|
||||
//switch_to_system_alloc();
|
||||
break;
|
||||
@ -490,56 +573,70 @@ FastCGIServer::process_connection_read(Connection& connection)
|
||||
connection.input_buffer.erase(0, n);
|
||||
}
|
||||
|
||||
void
|
||||
FastCGIServer::assemble_output_buffer(FastCGIRequest& request, Connection* connection)
|
||||
{
|
||||
request.out =
|
||||
request.response_code+"\r\n"+
|
||||
var_dump(request.header, "", "\r\n") +
|
||||
var_dump(request.set_cookies, "", "\r\n") +
|
||||
"\r\n";
|
||||
|
||||
for(auto obs : request.ob_stack)
|
||||
{
|
||||
request.out += obs->str();
|
||||
delete obs;
|
||||
}
|
||||
request.ob_stack.clear();
|
||||
request.flags.output_closed = true;
|
||||
request.stats.time_end = microtime();
|
||||
if(request.flags.log_request)
|
||||
printf("(r) pid:%i\t%s\t%0.6fs\tfps:%0.0f\tout:%0.1fkB\tmem:%0.0f/%0.0fkB\n",
|
||||
my_pid,
|
||||
request.params["REQUEST_URI"].c_str(),
|
||||
request.stats.time_end - request.stats.time_start,
|
||||
1.0 / (request.stats.time_end - request.stats.time_start),
|
||||
(f32)(request.out.length()/1024),
|
||||
(f32)(request.stats.mem_high/1024),
|
||||
(f32)(request.stats.mem_alloc/1024)
|
||||
);
|
||||
|
||||
if(connection)
|
||||
{
|
||||
connection->output_buffer.clear();
|
||||
connection->output_buffer.append(request.out);
|
||||
request.out.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
FastCGIServer::process_write_request(Connection& connection, RequestID id,
|
||||
RequestInfo& request)
|
||||
FastCGIServer::request_write_fgci(Connection& connection, RequestID id,
|
||||
FastCGIRequest& request)
|
||||
{
|
||||
if (!request.out.empty())
|
||||
{
|
||||
write_data(connection.output_buffer, id, request.out, FCGI_STDOUT);
|
||||
write_data_fcgi(connection.output_buffer, id, request.out, FCGI_STDOUT);
|
||||
//switch_to_arena(request.mem);
|
||||
request.out.clear();
|
||||
//switch_to_system_alloc();
|
||||
}
|
||||
if (!request.err.empty())
|
||||
{
|
||||
write_data(connection.output_buffer, id, request.err, FCGI_STDERR);
|
||||
write_data_fcgi(connection.output_buffer, id, request.err, FCGI_STDERR);
|
||||
//switch_to_arena(request.mem);
|
||||
request.err.clear();
|
||||
//switch_to_system_alloc();
|
||||
}
|
||||
if ((request.in_closed || request.status != 0) &&
|
||||
!request.output_closed)
|
||||
if ((request.flags.input_closed || request.flags.status != 0) &&
|
||||
!request.flags.output_closed)
|
||||
{
|
||||
//switch_to_arena(request.mem);
|
||||
request.out =
|
||||
var_dump(request.header, "", "\r\n") +
|
||||
var_dump(request.set_cookies, "", "\r\n") +
|
||||
"\r\n";
|
||||
|
||||
for(auto obs : request.ob_stack)
|
||||
{
|
||||
request.out += obs->str();
|
||||
delete obs;
|
||||
}
|
||||
request.ob_stack.clear();
|
||||
assemble_output_buffer(request);
|
||||
|
||||
//switch_to_system_alloc();
|
||||
write_data(connection.output_buffer, id, request.out, FCGI_STDOUT);
|
||||
write_data(connection.output_buffer, id, request.err, FCGI_STDERR);
|
||||
|
||||
request.stats.time_end = microtime();
|
||||
if(request.flags.log_request)
|
||||
printf("(r) pid:%i\t%s\t%0.6fs\tfps:%0.0f\tout:%0.1fkB\tmem:%0.0f/%0.0fkB\n",
|
||||
my_pid,
|
||||
request.params["REQUEST_URI"].c_str(),
|
||||
request.stats.time_end - request.stats.time_start,
|
||||
1.0 / (request.stats.time_end - request.stats.time_start),
|
||||
(f32)(request.out.length()/1024),
|
||||
(f32)(request.stats.mem_high/1024),
|
||||
(f32)(request.stats.mem_alloc/1024)
|
||||
);
|
||||
write_data_fcgi(connection.output_buffer, id, request.out, FCGI_STDOUT);
|
||||
write_data_fcgi(connection.output_buffer, id, request.err, FCGI_STDERR);
|
||||
|
||||
FCGI_EndRequestRecord complete;
|
||||
bzero(&complete, sizeof(complete));
|
||||
@ -548,17 +645,17 @@ FastCGIServer::process_write_request(Connection& connection, RequestID id,
|
||||
complete.header.requestIdB1 = (id >> 8) & 0xff;
|
||||
complete.header.requestIdB0 = id & 0xff;
|
||||
complete.header.contentLengthB0 = sizeof(complete.body);
|
||||
complete.body.appStatusB3 = (request.status >> 24) & 0xff;
|
||||
complete.body.appStatusB2 = (request.status >> 16) & 0xff;
|
||||
complete.body.appStatusB1 = (request.status >> 8) & 0xff;
|
||||
complete.body.appStatusB0 = request.status & 0xff;
|
||||
complete.body.appStatusB3 = (request.flags.status >> 24) & 0xff;
|
||||
complete.body.appStatusB2 = (request.flags.status >> 16) & 0xff;
|
||||
complete.body.appStatusB1 = (request.flags.status >> 8) & 0xff;
|
||||
complete.body.appStatusB0 = request.flags.status & 0xff;
|
||||
complete.body.protocolStatus = FCGI_REQUEST_COMPLETE;
|
||||
connection.output_buffer.append(
|
||||
reinterpret_cast<const char*>(&complete), sizeof(complete));
|
||||
if (connection.close_responsibility)
|
||||
connection.close_socket = true;
|
||||
|
||||
request.output_closed = true;
|
||||
|
||||
//printf("- output done\n");
|
||||
}
|
||||
//switch_to_system_alloc();
|
||||
@ -566,16 +663,16 @@ FastCGIServer::process_write_request(Connection& connection, RequestID id,
|
||||
|
||||
|
||||
void
|
||||
FastCGIServer::process_connection_write(Connection& connection)
|
||||
FastCGIServer::write_fgci(Connection& connection)
|
||||
{
|
||||
for (RequestList::iterator it = connection.requests.begin();
|
||||
it != connection.requests.end();)
|
||||
{
|
||||
process_write_request(connection, it->first, *it->second);
|
||||
if (it->second->params_closed && it->second->in_closed)
|
||||
request_write_fgci(connection, it->first, *it->second);
|
||||
if (it->second->flags.params_closed && it->second->flags.input_closed)
|
||||
{
|
||||
//switch_to_arena(it->second->mem);
|
||||
//printf("- process_connection_write close\n");
|
||||
//printf("- write_fgci close\n");
|
||||
delete it->second;
|
||||
//switch_to_system_alloc();
|
||||
connection.requests.erase(it++);
|
||||
@ -587,7 +684,7 @@ FastCGIServer::process_connection_write(Connection& connection)
|
||||
|
||||
|
||||
FastCGIServer::Pairs
|
||||
FastCGIServer::parse_pairs(const char* data, std::string::size_type n)
|
||||
FastCGIServer::parse_pairs_fcgi(const char* data, std::string::size_type n)
|
||||
{
|
||||
Pairs pairs;
|
||||
|
||||
@ -633,7 +730,7 @@ FastCGIServer::parse_pairs(const char* data, std::string::size_type n)
|
||||
|
||||
|
||||
void
|
||||
FastCGIServer::write_pair(std::string& buffer,
|
||||
FastCGIServer::write_pair_fcgi(std::string& buffer,
|
||||
const std::string& key, const std::string& value)
|
||||
{
|
||||
if (key.size() > 0x7f) {
|
||||
@ -658,7 +755,7 @@ FastCGIServer::write_pair(std::string& buffer,
|
||||
|
||||
|
||||
void
|
||||
FastCGIServer::write_data(std::string& buffer, RequestID id,
|
||||
FastCGIServer::write_data_fcgi(std::string& buffer, RequestID id,
|
||||
const std::string& input, unsigned char type)
|
||||
{
|
||||
FCGI_Header header;
|
||||
|
||||
@ -41,65 +41,55 @@
|
||||
|
||||
class FastCGIServer {
|
||||
public:
|
||||
FastCGIServer();
|
||||
~FastCGIServer();
|
||||
void shutdown();
|
||||
|
||||
// called when the parameters and standard input have been receieved
|
||||
std::function<int(FastCGIRequest&)> on_request = 0;
|
||||
std::function<int(FastCGIRequest&)> on_data = 0;
|
||||
std::function<int(FastCGIRequest&)> on_complete = 0;
|
||||
|
||||
void listen(unsigned tcp_port);
|
||||
void listen(const std::string& local_path);
|
||||
void abandon_files();
|
||||
int listen(unsigned tcp_port);
|
||||
int listen_http(unsigned tcp_port);
|
||||
int listen(const std::string& local_path);
|
||||
|
||||
void process(int timeout_ms = -1); // timeout_ms<0 blocks forever
|
||||
void process_forever();
|
||||
int calls_until_termination = 8; // set this to -1 to never terminate
|
||||
|
||||
int call_completion_handler(FastCGIRequest& request);
|
||||
|
||||
protected:
|
||||
struct RequestInfo : FastCGIRequest {
|
||||
RequestInfo();
|
||||
|
||||
std::string params_buffer;
|
||||
bool params_closed;
|
||||
bool in_closed;
|
||||
int status;
|
||||
bool output_closed;
|
||||
|
||||
friend class FastCGIServer;
|
||||
};
|
||||
|
||||
typedef unsigned RequestID;
|
||||
typedef std::map<RequestID, RequestInfo*> RequestList;
|
||||
struct Connection {
|
||||
Connection();
|
||||
typedef std::map<RequestID, FastCGIRequest*> RequestList;
|
||||
|
||||
struct Connection {
|
||||
RequestList requests;
|
||||
u64 posix_con = 0;
|
||||
u64 client_socket = 0;
|
||||
u64 server_socket = 0;
|
||||
std::string input_buffer;
|
||||
std::string output_buffer;
|
||||
bool close_responsibility;
|
||||
bool close_socket;
|
||||
bool close_responsibility = false;
|
||||
bool close_socket = false;
|
||||
char type = 'F'; // F = FastCGI, H = HttpServer
|
||||
};
|
||||
|
||||
typedef StringMap Pairs;
|
||||
|
||||
std::vector<int> listen_sockets;
|
||||
std::vector<int> server_sockets;
|
||||
std::vector<std::string> listen_unlink;
|
||||
|
||||
std::map<int, Connection*> read_sockets;
|
||||
std::map<int, char> server_socket_types;
|
||||
std::map<int, Connection*> client_sockets;
|
||||
|
||||
void process_connection_read(Connection&);
|
||||
static void process_write_request(Connection&, RequestID, RequestInfo&);
|
||||
static void process_connection_write(Connection&);
|
||||
static Pairs parse_pairs(const char*, std::string::size_type);
|
||||
static void write_pair(std::string& buffer,
|
||||
void read_fgci(Connection&);
|
||||
static void request_write_fgci(Connection&, RequestID, FastCGIRequest&);
|
||||
static void write_fgci(Connection&);
|
||||
static Pairs parse_pairs_fcgi(const char*, std::string::size_type);
|
||||
static void write_pair_fcgi(std::string& buffer,
|
||||
const std::string& key, const std::string&);
|
||||
static void write_data(std::string& buffer, RequestID id,
|
||||
static void write_data_fcgi(std::string& buffer, RequestID id,
|
||||
const std::string& input, unsigned char type);
|
||||
static void assemble_output_buffer(FastCGIRequest& request, Connection* connection = 0);
|
||||
int send_output_buffer(Connection& con);
|
||||
void process_http_request(FastCGIRequest& request, String& data);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#include "compiler.h"
|
||||
#include <sys/file.h>
|
||||
|
||||
String process_html_literal(Request* context, SharedUnit* su, String content)
|
||||
String process_text_literal(Request* context, SharedUnit* su, String content)
|
||||
{
|
||||
String pc;
|
||||
String HT_START = "print(R\"(";
|
||||
@ -89,7 +89,12 @@ String process_html_literal(Request* context, SharedUnit* su, String content)
|
||||
|
||||
String preprocess_shared_unit_char_wise(Request* context, SharedUnit* su, String content)
|
||||
{
|
||||
String pc = "#include \"" + su->src_file_name + ".setup.h" + "\"\n#line 1\n";
|
||||
String pc =
|
||||
("#include \"")+context->server->config["COMPILER_SYS_PATH"] +"/src/lib/uce_lib.h\" \n"+
|
||||
file_get_contents(
|
||||
context->server->config["COMPILER_SYS_PATH"] + "/" + context->server->config["SETUP_TEMPLATE"]
|
||||
)+
|
||||
"#line 1\n";
|
||||
String token = "";
|
||||
String html_buffer = "";
|
||||
u8 mode = 0;
|
||||
@ -100,29 +105,13 @@ String preprocess_shared_unit_char_wise(Request* context, SharedUnit* su, String
|
||||
{
|
||||
char c = content[i];
|
||||
current_line.append(1, c);
|
||||
if(mode == 2)
|
||||
if(mode == 1)
|
||||
{
|
||||
auto end_pos = content.find(String("</")+token+">", i);
|
||||
if(end_pos != String::npos)
|
||||
if(c == '<' && (content[i+1] == '/') && (content[i+2] == '>'))
|
||||
{
|
||||
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);
|
||||
i += 2;
|
||||
pc.append(process_text_literal(context, su, html_buffer));
|
||||
mode = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -130,13 +119,12 @@ String preprocess_shared_unit_char_wise(Request* context, SharedUnit* su, String
|
||||
html_buffer.append(1, c);
|
||||
}
|
||||
}
|
||||
else if(!inside_quote && c == '<' && (content[i+1] == '>'/* || isalpha(content[i+1])*/))
|
||||
else if(!inside_quote && c == '<' && (content[i+1] == '>'))
|
||||
{
|
||||
mode = 1;
|
||||
token = "";
|
||||
html_buffer = "";
|
||||
if(content[i+1] != '>')
|
||||
html_buffer.append(1, c);
|
||||
i += 1;
|
||||
}
|
||||
else if(c == '\"')
|
||||
{
|
||||
@ -206,7 +194,7 @@ void setup_unit_paths(Request* context, SharedUnit* su, String file_name)
|
||||
|
||||
su->so_name = su->bin_path + "/" + su->bin_file_name;
|
||||
su->api_file_name = su->bin_path + "/" + su->src_file_name + ".exports.txt";
|
||||
su->setup_file_name = su->bin_path + "/" + su->src_file_name + ".setup.h";
|
||||
//su->setup_file_name = su->bin_path + "/" + su->src_file_name + ".setup.h";
|
||||
}
|
||||
|
||||
void load_shared_unit(Request* context, SharedUnit* su, String file_name)
|
||||
@ -245,7 +233,7 @@ void load_shared_unit(Request* context, SharedUnit* su, String file_name)
|
||||
}
|
||||
}
|
||||
|
||||
String compile_setup_file(Request* context, SharedUnit* su)
|
||||
/*String compile_setup_file(Request* context, SharedUnit* su)
|
||||
{
|
||||
String result =
|
||||
String("#ifndef UCE_LIB_INCLUDED\n") +
|
||||
@ -254,17 +242,13 @@ String compile_setup_file(Request* context, SharedUnit* su)
|
||||
file_get_contents(
|
||||
context->server->config["COMPILER_SYS_PATH"] + "/" + context->server->config["SETUP_TEMPLATE"]) +
|
||||
"#endif \n";
|
||||
StringList declarations;
|
||||
StringList load_units;
|
||||
|
||||
result = replace(result, "/*load_declarations*/", join(declarations, "\n"));
|
||||
result = replace(result, "/*load_units*/", join(load_units, "\n"));
|
||||
return(result);
|
||||
}
|
||||
}*/
|
||||
|
||||
void compile_shared_unit(Request* context, SharedUnit* su, String file_name)
|
||||
{
|
||||
//setup_unit_paths(context, su, file_name);
|
||||
f64 comp_start = microtime();
|
||||
|
||||
if(!file_exists(su->file_name))
|
||||
{
|
||||
@ -274,7 +258,7 @@ void compile_shared_unit(Request* context, SharedUnit* su, String file_name)
|
||||
|
||||
shell_exec("mkdir -p " + shell_escape(su->pre_path));
|
||||
file_put_contents(su->pre_path + "/" + su->pre_file_name, preprocess_shared_unit(context, su));
|
||||
file_put_contents(su->setup_file_name, compile_setup_file(context, su));
|
||||
//file_put_contents(su->setup_file_name, compile_setup_file(context, su));
|
||||
file_put_contents(su->api_file_name, join(su->api_declarations, "\n"));
|
||||
|
||||
if(!su->opt_so_optional)
|
||||
@ -293,7 +277,9 @@ void compile_shared_unit(Request* context, SharedUnit* su, String file_name)
|
||||
else
|
||||
{
|
||||
load_shared_unit(context, su, file_name);
|
||||
//printf("(i) compiled unit %s\n", file_name.c_str());
|
||||
printf("(i) compiled unit %s in %f s\n",
|
||||
(su->pre_path + "/" + su->pre_file_name).c_str(),
|
||||
microtime() - comp_start);
|
||||
}
|
||||
}
|
||||
|
||||
@ -390,6 +376,7 @@ SharedUnit* compiler_load_shared_unit(Request* context, String file_name, String
|
||||
|
||||
void compiler_invoke(Request* context, String file_name, DTree& call_param)
|
||||
{
|
||||
printf("(i) compiler_invoke file %s\n", file_name.c_str());
|
||||
auto su = compiler_load_shared_unit(context, file_name, "", false);
|
||||
if(su)
|
||||
{
|
||||
|
||||
0
src/lib/compiler2.cpp
Normal file
0
src/lib/compiler2.cpp
Normal file
@ -6,7 +6,7 @@ String var_dump(StringMap map, String prefix, String postfix)
|
||||
|
||||
for (auto it = map.begin(); it != map.end(); ++it)
|
||||
{
|
||||
result.append(prefix + it->first + ": " + it->second + postfix);
|
||||
result.append(prefix + to_upper(it->first) + ": " + it->second + postfix);
|
||||
}
|
||||
|
||||
return(result);
|
||||
@ -42,25 +42,15 @@ u8 hex_to_u8(String src)
|
||||
|
||||
String to_lower(String s)
|
||||
{
|
||||
String result = "";
|
||||
for(auto c : s)
|
||||
{
|
||||
if(c >= 'A' && c <= 'Z')
|
||||
c = tolower(c);
|
||||
result.append(1, c);
|
||||
}
|
||||
String result = s;
|
||||
std::transform(result.begin(), result.end(),result.begin(), ::tolower);
|
||||
return(result);
|
||||
}
|
||||
|
||||
String to_upper(String s)
|
||||
{
|
||||
String result = "";
|
||||
for(auto c : s)
|
||||
{
|
||||
if(c >= 'A' && c <= 'Z')
|
||||
c = toupper(c);
|
||||
result.append(1, c);
|
||||
}
|
||||
String result = s;
|
||||
std::transform(result.begin(), result.end(),result.begin(), ::toupper);
|
||||
return(result);
|
||||
}
|
||||
|
||||
@ -140,7 +130,7 @@ StringList split(String str, String delim)
|
||||
return(result);
|
||||
}
|
||||
|
||||
StringMap split_kv(String s, char separator, bool trim_whitespace)
|
||||
StringMap split_kv(String s, char separator, bool trim_whitespace, bool uppercase_keys)
|
||||
{
|
||||
StringMap result;
|
||||
String k;
|
||||
@ -157,7 +147,7 @@ StringMap split_kv(String s, char separator, bool trim_whitespace)
|
||||
if(c == separator)
|
||||
mode = 1;
|
||||
else
|
||||
k.append(1, c);
|
||||
k.append(1, uppercase_keys ? toupper(c) : c);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -175,6 +165,68 @@ StringMap split_kv(String s, char separator, bool trim_whitespace)
|
||||
return(result);
|
||||
}
|
||||
|
||||
StringMap split_http_headers(String s)
|
||||
{
|
||||
StringMap result;
|
||||
String k;
|
||||
String v;
|
||||
String query_string;
|
||||
String base_uri;
|
||||
for(auto s : split(s, "\n"))
|
||||
{
|
||||
u8 mode = 0;
|
||||
k = "";
|
||||
v = "";
|
||||
for(auto c : s)
|
||||
{
|
||||
if(mode == 0)
|
||||
{
|
||||
if(c == ':')
|
||||
mode = 1;
|
||||
else
|
||||
k.append(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
v.append(1, c);
|
||||
}
|
||||
}
|
||||
if(k != "")
|
||||
{
|
||||
k = trim(k);
|
||||
v = trim(v);
|
||||
if(v == "")
|
||||
{
|
||||
if(result["REQUEST_METHOD"] == "")
|
||||
{
|
||||
result["REQUEST_METHOD"] = nibble(k, " ");
|
||||
result["REQUEST_URI"] = nibble(k, " ");
|
||||
String query_string = result["REQUEST_URI"];
|
||||
String base_uri = nibble(query_string, "?");
|
||||
result["SERVER_PROTOCOL"] = k;
|
||||
result["SCRIPT_NAME"] = base_uri;
|
||||
result["DOCUMENT_URI"] = base_uri;
|
||||
result["QUERY_STRING"] = query_string;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(k != "")
|
||||
result["_"] = k;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
String header_key = to_upper(k);
|
||||
std::replace(header_key.begin(), header_key.end(), '-', '_');
|
||||
result["HTTP_"+header_key] = v;
|
||||
if(header_key == "CONTENT_TYPE" || header_key == "CONTENT_LENGTH")
|
||||
result[header_key] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
String join(StringList l, String delim)
|
||||
{
|
||||
String result;
|
||||
|
||||
@ -11,7 +11,8 @@ String trim(String raw);
|
||||
StringList split_space(String str);
|
||||
StringList split(String str, String delim);
|
||||
StringList split_utf8(String s, bool compound_characters = false);
|
||||
StringMap split_kv(String s, char separator = '=', bool trim_whitespace = true);
|
||||
StringMap split_kv(String s, char separator = '=', bool trim_whitespace = true, bool uppercase_keys = false);
|
||||
StringMap split_http_headers(String s);
|
||||
String join(StringList l, String delim = "\n");
|
||||
String nibble(String& haystack, String delim);
|
||||
void json_consume_space(String s, u32& i);
|
||||
@ -84,9 +85,10 @@ String var_dump(StringMap map, String prefix = "", String postfix = "\n");
|
||||
String var_dump(StringList slist, String prefix = "", String postfix = "\n");
|
||||
|
||||
void ob_start();
|
||||
void ob_clear();
|
||||
String ob_get_clear();
|
||||
void ob_close();
|
||||
String ob_get();
|
||||
String ob_get_close();
|
||||
|
||||
String safe_name(String raw);
|
||||
|
||||
#define is_bit_set(var,pos) ((var) & (1<<(pos)))
|
||||
|
||||
@ -135,14 +135,28 @@ String file_get_contents(String file_name)
|
||||
|
||||
bool file_put_contents(String file_name, String content)
|
||||
{
|
||||
s32 fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC);
|
||||
s32 fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if(fd == -1)
|
||||
{
|
||||
printf("(!) Could not write %s\n", file_name.c_str());
|
||||
return(false);
|
||||
}
|
||||
flock(fd, LOCK_EX);
|
||||
write(fd, content.data(), content.length());
|
||||
const char* data = content.data();
|
||||
size_t remaining = content.length();
|
||||
while(remaining > 0)
|
||||
{
|
||||
auto bytes_written = write(fd, data, remaining);
|
||||
if(bytes_written < 0)
|
||||
{
|
||||
flock(fd, LOCK_UN);
|
||||
close(fd);
|
||||
printf("(!) Could not fully write %s\n", file_name.c_str());
|
||||
return(false);
|
||||
}
|
||||
data += bytes_written;
|
||||
remaining -= bytes_written;
|
||||
}
|
||||
flock(fd, LOCK_UN);
|
||||
close(fd);
|
||||
return(true);
|
||||
@ -242,7 +256,7 @@ String gmdate(String format, u64 timestamp)
|
||||
|
||||
u64 parse_time(String time_String)
|
||||
{
|
||||
return(int_val(trim(shell_exec("date -u -d '"+time_String+"' +'%s'"))));
|
||||
return(int_val(trim(shell_exec("date -u -d "+shell_escape(time_String)+" +'%s'"))));
|
||||
}
|
||||
|
||||
u64 socket_connect(String host, short port)
|
||||
@ -475,8 +489,8 @@ pid_t task(String key, std::function<void()> exec_after_spawn, u64 timeout)
|
||||
my_pid = getpid();
|
||||
file_put_contents(status_file_name, std::to_string(my_pid));
|
||||
|
||||
close(context->resources.fcgi_socket);
|
||||
context->resources.fcgi_socket = 0;
|
||||
close(context->resources.client_socket);
|
||||
context->resources.client_socket = 0;
|
||||
//printf("(C) child procress started, PID:%i\n", my_pid);
|
||||
//prctl(PR_SET_PDEATHSIG, SIGHUP);
|
||||
exec_after_spawn();
|
||||
@ -534,13 +548,13 @@ StringMap make_server_settings()
|
||||
cfg["SETUP_TEMPLATE"] = "scripts/setup.h.template";
|
||||
cfg["LIT_ESC"] = "3d5b5_1";
|
||||
cfg["CONTENT_TYPE"] = "text/html; charset=utf-8";
|
||||
cfg["SOCKET_PATH"] = "/run/uce.sock";
|
||||
cfg["FCGI_SOCKET_PATH"] = "/run/uce.sock";
|
||||
cfg["TMP_UPLOAD_PATH"] = "/tmp/uce/uploads";
|
||||
cfg["SESSION_PATH"] = "/tmp/uce/sessions";
|
||||
cfg["COMPILER_SYS_PATH"] = ".";
|
||||
cfg["PRECOMPILE_FILES_IN"] = ".";
|
||||
|
||||
cfg["LISTEN_PORT"] = std::to_string(9993);
|
||||
cfg["HTTP_PORT"] = std::to_string(8080);
|
||||
cfg["SESSION_TIME"] = std::to_string(60*60*24*30);
|
||||
cfg["WORKER_COUNT"] = std::to_string(4);
|
||||
cfg["MAX_MEMORY"] = std::to_string(1024*1024*16);
|
||||
@ -550,5 +564,15 @@ StringMap make_server_settings()
|
||||
cfg[it.first] = it.second;
|
||||
}
|
||||
|
||||
if(cfg["FCGI_SOCKET_PATH"] == "" && cfg["SOCKET_PATH"] != "")
|
||||
cfg["FCGI_SOCKET_PATH"] = cfg["SOCKET_PATH"];
|
||||
if(cfg["SOCKET_PATH"] == "" && cfg["FCGI_SOCKET_PATH"] != "")
|
||||
cfg["SOCKET_PATH"] = cfg["FCGI_SOCKET_PATH"];
|
||||
|
||||
if(cfg["FCGI_PORT"] == "" && cfg["LISTEN_PORT"] != "")
|
||||
cfg["FCGI_PORT"] = cfg["LISTEN_PORT"];
|
||||
if(cfg["LISTEN_PORT"] == "" && cfg["FCGI_PORT"] != "")
|
||||
cfg["LISTEN_PORT"] = cfg["FCGI_PORT"];
|
||||
|
||||
return(cfg);
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ String nibble(String div, String& haystack)
|
||||
|
||||
void Request::ob_start()
|
||||
{
|
||||
ob_stack.push_back(new std::ostringstream());
|
||||
ob_stack.push_back(new ByteStream());
|
||||
ob = ob_stack.back();
|
||||
}
|
||||
|
||||
|
||||
@ -47,6 +47,7 @@ String operator+(String lhs, f32 rhs) {
|
||||
|
||||
typedef std::map<String, String> StringMap;
|
||||
typedef std::vector<String> StringList;
|
||||
typedef std::ostringstream ByteStream;
|
||||
|
||||
struct Request;
|
||||
struct DTree;
|
||||
@ -129,22 +130,27 @@ struct Request {
|
||||
String session_name = "";
|
||||
std::vector<UploadedFile> uploaded_files;
|
||||
|
||||
String response_code = "HTTP/1.1 200 OK";
|
||||
StringMap header;
|
||||
StringList set_cookies;
|
||||
|
||||
u64 random_seed;
|
||||
u64 random_index;
|
||||
|
||||
std::vector<ByteStream*> ob_stack;
|
||||
ByteStream* ob;
|
||||
|
||||
String in;
|
||||
std::vector<std::ostringstream*> ob_stack;
|
||||
std::ostringstream* ob;
|
||||
String out;
|
||||
String err;
|
||||
|
||||
bool is_finished = false;
|
||||
|
||||
struct Flags {
|
||||
bool log_request = true;
|
||||
bool is_finished = false;
|
||||
int status;
|
||||
bool output_closed = false;
|
||||
bool params_closed = false;
|
||||
bool input_closed = false;
|
||||
} flags;
|
||||
|
||||
struct Stats {
|
||||
@ -152,20 +158,20 @@ struct Request {
|
||||
f64 time_init;
|
||||
f64 time_start;
|
||||
f64 time_end;
|
||||
u64 mem_high;
|
||||
u64 mem_alloc;
|
||||
u64 mem_high = 0;
|
||||
u64 mem_alloc = 0;
|
||||
u32 invoke_count = 0;
|
||||
} stats;
|
||||
|
||||
// OS-related resources
|
||||
struct Resources {
|
||||
std::vector<u64> sockets;
|
||||
std::vector<void*> mysql_connections;
|
||||
u64 fcgi_socket = 0;
|
||||
u64 client_socket = 0;
|
||||
u64 server_socket = 0;
|
||||
std::string params_buffer;
|
||||
} resources;
|
||||
|
||||
//void invoke(String file_name);
|
||||
//void invoke(String file_name, DTree& call_param);
|
||||
|
||||
void ob_start();
|
||||
|
||||
~Request();
|
||||
@ -184,10 +190,16 @@ void print(Ts... args)
|
||||
((*context->ob << args), ...);
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
void out(Ts... args)
|
||||
{
|
||||
((*context->ob << args), ...);
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
String concat(Ts... args)
|
||||
{
|
||||
std::stringstream out;
|
||||
ByteStream out;
|
||||
((out << args), ...);
|
||||
return(out.str());
|
||||
}
|
||||
|
||||
162
src/lib/uri.cpp
162
src/lib/uri.cpp
@ -101,42 +101,117 @@ String encode_query(StringMap map)
|
||||
return(result);
|
||||
}
|
||||
|
||||
String trim_wrapping_quotes(String raw)
|
||||
{
|
||||
if(raw.length() >= 2 && raw[0] == '"' && raw[raw.length()-1] == '"')
|
||||
return(raw.substr(1, raw.length()-2));
|
||||
return(raw);
|
||||
}
|
||||
|
||||
void parse_multipart_content_disposition(
|
||||
String raw,
|
||||
String& disposition_type,
|
||||
String& field_name,
|
||||
String& file_name)
|
||||
{
|
||||
auto parts = split(raw, ";");
|
||||
if(parts.size() == 0)
|
||||
return;
|
||||
|
||||
disposition_type = to_lower(trim(parts[0]));
|
||||
for(u32 i = 1; i < parts.size(); i++)
|
||||
{
|
||||
String part = trim(parts[i]);
|
||||
String key = to_lower(trim(nibble(part, "=")));
|
||||
String value = trim_wrapping_quotes(trim(part));
|
||||
if(key == "name")
|
||||
field_name = value;
|
||||
else if(key == "filename")
|
||||
file_name = value;
|
||||
}
|
||||
}
|
||||
|
||||
String make_upload_tmp_name()
|
||||
{
|
||||
String upload_path = context->server->config["TMP_UPLOAD_PATH"];
|
||||
if(upload_path.length() > 0 && upload_path[upload_path.length()-1] != '/')
|
||||
upload_path.append(1, '/');
|
||||
return(upload_path + make_session_id());
|
||||
}
|
||||
|
||||
StringMap parse_multipart(String q, String boundary, std::vector<UploadedFile>& uploaded_files)
|
||||
{
|
||||
StringMap result;
|
||||
|
||||
auto i = boundary.length();
|
||||
if(boundary == "")
|
||||
return(result);
|
||||
|
||||
u64 i = 0;
|
||||
while(i < q.length())
|
||||
{
|
||||
auto end_pos = q.find(boundary, i);
|
||||
if(end_pos != String::npos)
|
||||
auto start_pos = q.find(boundary, i);
|
||||
if(start_pos == String::npos)
|
||||
break;
|
||||
|
||||
start_pos += boundary.length();
|
||||
if(q.substr(start_pos, 2) == "--")
|
||||
break;
|
||||
if(q.substr(start_pos, 2) == "\r\n")
|
||||
start_pos += 2;
|
||||
|
||||
auto header_end = q.find("\r\n\r\n", start_pos);
|
||||
if(header_end == String::npos)
|
||||
break;
|
||||
|
||||
String header_block = q.substr(start_pos, header_end - start_pos);
|
||||
auto end_pos = q.find(boundary, header_end + 4);
|
||||
if(end_pos == String::npos)
|
||||
break;
|
||||
|
||||
String body = q.substr(header_end + 4, end_pos - (header_end + 4));
|
||||
if(body.length() >= 2 && body.substr(body.length()-2) == "\r\n")
|
||||
body.resize(body.length()-2);
|
||||
|
||||
String disposition_type;
|
||||
String field_name;
|
||||
String file_name;
|
||||
for(auto header_line : split(header_block, "\r\n"))
|
||||
{
|
||||
String field = q.substr(i, end_pos - i);
|
||||
nibble(":", field);
|
||||
String ftype = trim(nibble(";", field));
|
||||
if(ftype == "form-data")
|
||||
String header_name = to_lower(trim(nibble(header_line, ":")));
|
||||
String header_value = trim(header_line);
|
||||
if(header_name == "content-disposition")
|
||||
{
|
||||
nibble("=\"", field);
|
||||
String field_name = nibble("\"", field);
|
||||
result[field_name] = field.substr(4, field.length()-6);
|
||||
parse_multipart_content_disposition(
|
||||
header_value,
|
||||
disposition_type,
|
||||
field_name,
|
||||
file_name
|
||||
);
|
||||
}
|
||||
else if(ftype == "attachment")
|
||||
}
|
||||
|
||||
if(field_name != "")
|
||||
{
|
||||
bool is_uploaded_file =
|
||||
(disposition_type == "form-data" || disposition_type == "attachment") &&
|
||||
file_name != "";
|
||||
|
||||
if(is_uploaded_file)
|
||||
{
|
||||
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);
|
||||
f.tmp_name = make_upload_tmp_name();
|
||||
f.file_name = file_name;
|
||||
f.size = body.length();
|
||||
file_put_contents(f.tmp_name, body);
|
||||
uploaded_files.push_back(f);
|
||||
result[field_name] = file_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
result[field_name] = body;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we're done
|
||||
end_pos = q.length();
|
||||
}
|
||||
|
||||
i = end_pos + boundary.length();
|
||||
}
|
||||
|
||||
@ -287,23 +362,56 @@ String make_session_id()
|
||||
return(to_hex(rand())+to_hex(rand())+to_hex(rand())+to_hex(rand()));
|
||||
}
|
||||
|
||||
bool is_valid_session_id(String session_id)
|
||||
{
|
||||
if(session_id.length() < 16 || session_id.length() > 128)
|
||||
return(false);
|
||||
for(auto c : session_id)
|
||||
{
|
||||
if(!isxdigit(c))
|
||||
return(false);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
String session_file_path(String session_id)
|
||||
{
|
||||
if(!is_valid_session_id(session_id))
|
||||
return("");
|
||||
return(context->server->config["SESSION_PATH"] + "/" + session_id);
|
||||
}
|
||||
|
||||
StringMap load_session_data(String session_id)
|
||||
{
|
||||
return(parse_query(file_get_contents(context->server->config["SESSION_PATH"] + "/" + session_id)));
|
||||
String session_path = session_file_path(session_id);
|
||||
if(session_path == "")
|
||||
return(StringMap());
|
||||
return(parse_query(file_get_contents(session_path)));
|
||||
}
|
||||
|
||||
void save_session_data(String session_id, StringMap data)
|
||||
{
|
||||
file_put_contents(context->server->config["SESSION_PATH"] + "/" + session_id, encode_query(data));
|
||||
String session_path = session_file_path(session_id);
|
||||
if(session_path == "")
|
||||
{
|
||||
printf("(!) Refusing to save invalid session id\n");
|
||||
return;
|
||||
}
|
||||
file_put_contents(session_path, encode_query(data));
|
||||
}
|
||||
|
||||
String session_start(String session_name)
|
||||
{
|
||||
if(context->cookies[session_name].length() == 0)
|
||||
String session_id = context->cookies[session_name];
|
||||
if(!is_valid_session_id(session_id))
|
||||
session_id = "";
|
||||
|
||||
if(session_id.length() == 0)
|
||||
{
|
||||
set_cookie(session_name, make_session_id(), time() + int_val(context->server->config["SESSION_TIME"]));
|
||||
session_id = make_session_id();
|
||||
set_cookie(session_name, session_id, time() + int_val(context->server->config["SESSION_TIME"]));
|
||||
}
|
||||
context->session_id = context->cookies[session_name];
|
||||
context->session_id = session_id;
|
||||
context->session_name = session_name;
|
||||
context->session = load_session_data(context->session_id);
|
||||
return(context->session_id);
|
||||
|
||||
@ -17,3 +17,69 @@ StringMap load_session_data(String session_id);
|
||||
void save_session_data(String session_id, StringMap data);
|
||||
String session_start(String session_name = "uce-session");
|
||||
void session_destroy(String session_name = "uce-session");
|
||||
|
||||
struct WSFrame {
|
||||
|
||||
u8 opcode;
|
||||
u8 is_final_fragment;
|
||||
u8 mask_bit;
|
||||
u64 payload_length;
|
||||
u64 offset;
|
||||
u8* payload;
|
||||
|
||||
WSFrame(String& bufstr)
|
||||
{
|
||||
const char* buffer = bufstr.c_str();
|
||||
u64 buffer_size = bufstr.size();
|
||||
opcode = buffer[0] & 0x0F;
|
||||
is_final_fragment = buffer[0] & 0x80;
|
||||
mask_bit = buffer[1] & 0x80;
|
||||
payload_length = buffer[1] & 0x7F;
|
||||
|
||||
offset = 2;
|
||||
if (payload_length == 126) {
|
||||
payload_length = ((u64) buffer[2] << 8) | buffer[3];
|
||||
offset = 4;
|
||||
} else if (payload_length == 127) {
|
||||
payload_length = ((u64) buffer[2] << 56) |
|
||||
((u64) buffer[3] << 48) |
|
||||
((u64) buffer[4] << 40) |
|
||||
((u64) buffer[5] << 32) |
|
||||
((u64) buffer[6] << 24) |
|
||||
((u64) buffer[7] << 16) |
|
||||
((u64) buffer[8] << 8) |
|
||||
buffer[9];
|
||||
offset = 10;
|
||||
}
|
||||
|
||||
uint8_t* maskingKey = nullptr;
|
||||
if (mask_bit) {
|
||||
maskingKey = reinterpret_cast<uint8_t*>(const_cast<char*>(buffer + offset));
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
// Parse the frame payload
|
||||
payload = reinterpret_cast<uint8_t*>(const_cast<char*>(buffer + offset));
|
||||
if (mask_bit) {
|
||||
for (int i = 0; i < payload_length; i++) {
|
||||
payload[i] = payload[i] ^ maskingKey[i % 4];
|
||||
}
|
||||
}
|
||||
|
||||
// Print the parsed frame
|
||||
std::cout << "Parsed WebSocket frame:" << std::endl;
|
||||
std::cout << " Opcode: " << (int) opcode << std::endl;
|
||||
std::cout << " Is final fragment: " << (is_final_fragment ? "true" : "false") << std::endl;
|
||||
std::cout << " Payload length: " << payload_length << std::endl;
|
||||
if (mask_bit) {
|
||||
std::cout << " Masking key: ";
|
||||
for (int i = 0; i < 4; i++) {
|
||||
std::cout << std::hex << (int) maskingKey[i] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
std::cout << " Payload: " << std::endl << std::string((const char*) payload, payload_length) << std::endl;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@ -70,8 +70,8 @@ int handle_complete(FastCGIRequest& request) {
|
||||
}
|
||||
}
|
||||
|
||||
// printf("(i) request ready\n");
|
||||
render_file(request.params["SCRIPT_FILENAME"]);
|
||||
DTree call_param;
|
||||
compiler_invoke(&request, request.params["SCRIPT_FILENAME"], call_param);
|
||||
|
||||
for( auto &f : request.uploaded_files)
|
||||
{
|
||||
@ -86,6 +86,20 @@ int handle_complete(FastCGIRequest& request) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
volatile bool termination_signal_received = false;
|
||||
|
||||
void on_terminate(int sig)
|
||||
{
|
||||
if(termination_signal_received)
|
||||
return;
|
||||
termination_signal_received = true;
|
||||
if(getpid() != parent_pid)
|
||||
exit(1);
|
||||
printf("Terminating... PID %i:%i\n", getpid(), parent_pid);
|
||||
server.shutdown();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void listen_for_connections()
|
||||
{
|
||||
if(precompile_jobs.size() > 0)
|
||||
@ -119,25 +133,27 @@ void init_base_process()
|
||||
|
||||
server_state.config["COMPILE_SCRIPT"] =
|
||||
server_state.config["COMPILER_SYS_PATH"] + "/" + server_state.config["COMPILE_SCRIPT"];
|
||||
if(server_state.config["LISTEN_PORT"] != "")
|
||||
server.listen(int_val(server_state.config["LISTEN_PORT"]));
|
||||
|
||||
if(server_state.config["FCGI_PORT"] != "")
|
||||
server.listen(int_val(server_state.config["FCGI_PORT"]));
|
||||
|
||||
printf("%s\n", var_dump(server_state.config).c_str());
|
||||
|
||||
if(server_state.config["SOCKET_PATH"] != "")
|
||||
if(server_state.config["FCGI_SOCKET_PATH"] != "")
|
||||
{
|
||||
server.listen(server_state.config["SOCKET_PATH"]);
|
||||
chmod(server_state.config["SOCKET_PATH"].c_str(), S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||
server.listen(server_state.config["FCGI_SOCKET_PATH"]);
|
||||
chmod(server_state.config["FCGI_SOCKET_PATH"].c_str(), S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||
}
|
||||
|
||||
//dirname(server_state.config.COMPILER_SYS_PATH);
|
||||
//basename(server_state.config.COMPILER_SYS_PATH);
|
||||
if(server_state.config["HTTP_PORT"] != "")
|
||||
server.listen_http(int_val(server_state.config["HTTP_PORT"]));
|
||||
|
||||
mkdir(server_state.config["BIN_DIRECTORY"]);
|
||||
mkdir(server_state.config["TMP_UPLOAD_PATH"]);
|
||||
mkdir(server_state.config["SESSION_PATH"]);
|
||||
|
||||
signal(SIGCHLD, on_child_exit);
|
||||
signal(SIGINT, on_terminate);
|
||||
srand(time());
|
||||
}
|
||||
|
||||
@ -187,7 +203,8 @@ int main(int argc, char** argv)
|
||||
}
|
||||
}
|
||||
}
|
||||
spawn_subprocess(listen_for_connections);
|
||||
if(!termination_signal_received)
|
||||
spawn_subprocess(listen_for_connections);
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
25
test/header.uce
Normal file
25
test/header.uce
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
|
||||
RENDER()
|
||||
{
|
||||
DTree t;
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
Header
|
||||
</h1>
|
||||
|
||||
Response Headers
|
||||
<pre style="white-space: pre-wrap"><?
|
||||
|
||||
print(var_dump(context->header));
|
||||
|
||||
?></pre>
|
||||
|
||||
Params
|
||||
<pre><?= var_dump(context->params) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
@ -33,6 +33,9 @@ RENDER()
|
||||
<li><a href="task_repeat.uce">Task repeat</a></li>
|
||||
<li><a href="utf8.uce">UTF-8</a></li>
|
||||
<li><a href="call_file.uce">call_file()</a></li>
|
||||
<li><a href="parse_time.uce">parse_time()</a></li>
|
||||
<li><a href="header.uce">header</a></li>
|
||||
<li><a href="websockets.ws.uce">WebSockets</a></li>
|
||||
</ul>
|
||||
<pre><?
|
||||
print("Worker PID: ", my_pid, "\n");
|
||||
|
||||
30
test/parse_time.uce
Normal file
30
test/parse_time.uce
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
RENDER()
|
||||
{
|
||||
|
||||
String raw = first(context->post["raw"], "today");
|
||||
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
parse_time
|
||||
</h1>
|
||||
<form action="?" method="post">
|
||||
<div>
|
||||
<label>Time string</label>
|
||||
<input type="text" name="raw" value="<?= raw ?>"/>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="Parse"/>
|
||||
</div>
|
||||
</form>
|
||||
<pre><?
|
||||
|
||||
print(date("%Y-%m-%d %H:%M", parse_time(raw)));
|
||||
|
||||
?></pre>
|
||||
<pre><?= var_dump(context->params) ?></pre>
|
||||
</>
|
||||
|
||||
}
|
||||
42
test/websockets.ws.uce
Normal file
42
test/websockets.ws.uce
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
RENDER()
|
||||
{
|
||||
String raw = first(context->post["raw"], "today");
|
||||
String server_url = context->params["HTTP_HOST"]+context->params["DOCUMENT_URI"];
|
||||
<>
|
||||
<link rel="stylesheet" href='style.css'></link>
|
||||
<h1>
|
||||
<a href="index.uce">UCE Test</a>:
|
||||
WebSocket Test
|
||||
</h1>
|
||||
<form action="?" method="post">
|
||||
<div>
|
||||
<label></label>
|
||||
<input type="text" name="raw" value="<?= raw ?>"/>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="Parse"/>
|
||||
</div>
|
||||
</form>
|
||||
<pre id="log"></pre>
|
||||
<script>
|
||||
const ws = new WebSocket('wss://<?= server_url ?>');
|
||||
const log_element = document.getElementById('log');
|
||||
log_element.append('Connecting to wss://<?= server_url ?>...' + "\n");
|
||||
ws.addEventListener('open', (event) => {
|
||||
log_element.append('Connection open' + "\n");
|
||||
ws.send('Hello, server!');
|
||||
});
|
||||
ws.addEventListener('message', (event) => {
|
||||
log_element.append('Message: ' + JSON.stringify(event.data) + "\n");
|
||||
});
|
||||
ws.addEventListener('close', (event) => {
|
||||
log_element.append('Connection closed' + "\n");
|
||||
});
|
||||
ws.addEventListener('error', (event) => {
|
||||
log_element.append('Error: ' + JSON.stringify(event) + "\n");
|
||||
});
|
||||
</script>
|
||||
<pre><?= var_dump(context->params) ?></pre>
|
||||
</>
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user