Add archive helpers and harden task runtime

This commit is contained in:
udo
2026-05-21 00:00:23 +00:00
parent 9f7625c7fd
commit 02e153a6a7
71 changed files with 12817 additions and 305 deletions
+348 -162
View File
@@ -44,10 +44,42 @@
#include <netinet/in.h> // sockaddr_in, INADDR_*
#include <sys/select.h> // select, fd_set, FD_*, timeval
#include <sys/socket.h> // socket, bind, accept, listen, sockaddr, AF_*, SOCK_*
#include <sys/stat.h> // mkdir
#include <sys/un.h> // sockaddr_un
#include "../fastcgi_devkit/fastcgi.h"
namespace {
struct TransportLimits {
u64 max_client_connections = 256;
u64 max_http_header_bytes = 16 * 1024;
u64 max_http_body_bytes = 1024 * 1024;
u64 max_websocket_frame_bytes = 1024 * 1024;
u64 max_websocket_message_bytes = 1024 * 1024;
u64 max_websocket_output_bytes = 4 * 1024 * 1024;
f64 http_request_timeout_seconds = 15.0;
f64 connection_idle_timeout_seconds = 120.0;
u64 max_http_buffer_bytes() const
{
return(max_http_header_bytes + max_http_body_bytes);
}
u64 max_websocket_buffer_bytes() const
{
return(max_websocket_message_bytes + 16 * 1024);
}
};
const TransportLimits& transport_limits()
{
static const TransportLimits limits;
return(limits);
}
}
static String
make_http_text_response(String status_line, String body, String extra_headers = "")
{
@@ -75,6 +107,27 @@ is_valid_close_code(u16 status_code)
return(false);
}
static void
ensure_parent_directories(const std::string& path)
{
std::string::size_type slash = path.rfind('/');
if(slash == std::string::npos || slash == 0)
return;
std::string current;
std::string directory = path.substr(0, slash);
for(std::string::size_type i = 1; i <= directory.length(); ++i)
{
if(i != directory.length() && directory[i] != '/')
continue;
current = directory.substr(0, i);
if(current == "")
continue;
if(::mkdir(current.c_str(), 0775) == -1 && errno != EEXIST)
throw std::runtime_error("mkdir() failed for Unix socket directory");
}
}
static void
set_socket_nonblocking(int socket_handle)
{
@@ -132,6 +185,15 @@ FastCGIServer::listen_http(unsigned tcp_port)
return server_socket;
}
int
FastCGIServer::listen_cli(const std::string& local_path)
{
int server_socket = listen(local_path);
server_socket_types[server_socket] = 'C';
printf("(P) CLI command socket ready at %s\n", local_path.c_str());
return server_socket;
}
int
FastCGIServer::listen(unsigned tcp_port)
{
@@ -188,6 +250,7 @@ FastCGIServer::listen(const std::string& local_path)
std::memcpy(sa.sun_path, local_path.data(), size);
ensure_parent_directories(local_path);
file_unlink(local_path);
try {
if (bind(server_socket, (struct sockaddr*)&sa,
@@ -250,9 +313,95 @@ FastCGIServer::close_http_listeners()
}
}
bool
FastCGIServer::is_http_like_type(char type)
{
return(type == 'H' || type == 'C');
}
FastCGIServer::Connection*
FastCGIServer::open_client_connection(int server_socket, int client_socket)
{
set_socket_nonblocking(client_socket);
printf("Opening socket %i\n", client_socket);
Connection* connection = new Connection();
connection->client_socket = client_socket;
connection->server_socket = server_socket;
connection->type = server_socket_types[server_socket];
connection->opened_at = time_precise();
connection->last_activity_at = connection->opened_at;
client_sockets[client_socket] = connection;
if(is_http_like_type(connection->type))
{
FastCGIRequest* new_request = new FastCGIRequest();
new_request->resources.client_socket = client_socket;
new_request->resources.server_socket = server_socket;
new_request->stats.time_init = connection->opened_at;
connection->requests[client_socket] = new_request;
}
return(connection);
}
bool
FastCGIServer::reject_http_connection(Connection& connection, String status_line, String body, String extra_headers)
{
connection.output_buffer += make_http_text_response(status_line, body, extra_headers);
connection.close_socket = true;
return(false);
}
void
FastCGIServer::enforce_connection_timeouts(Connection& connection)
{
if(connection.close_socket)
return;
const TransportLimits& limits = transport_limits();
f64 now = time_precise();
if(is_http_like_type(connection.type) && !connection.is_websocket && !connection.requests.empty())
{
FastCGIRequest* pending_request = connection.requests.begin()->second;
if(!pending_request->flags.input_closed && now - connection.opened_at > limits.http_request_timeout_seconds)
{
reject_http_connection(connection, "HTTP/1.1 408 Request Timeout", "request timed out\n");
return;
}
}
if(now - connection.last_activity_at > limits.connection_idle_timeout_seconds)
connection.close_socket = true;
}
bool
FastCGIServer::queue_websocket_frame(Connection& connection, String frame)
{
if(connection.output_buffer.length() + frame.length() > transport_limits().max_websocket_output_bytes)
{
fail_websocket_connection(connection, 1013, "websocket output queue is full");
return(false);
}
connection.output_buffer += frame;
return(true);
}
bool
FastCGIServer::queue_websocket_payload(Connection& connection, String message, bool binary)
{
if(message.length() > transport_limits().max_websocket_message_bytes)
return(false);
return(queue_websocket_frame(connection, ws_encode_frame(message, binary ? 0x2 : 0x1)));
}
void
FastCGIServer::close_websocket_connection(Connection& connection, u16 status_code, String reason)
{
if(!is_valid_close_code(status_code))
status_code = 1002;
if(reason.length() > 123)
reason = reason.substr(0, 123);
if(!ws_is_valid_utf8(reason))
reason = "";
if(!connection.close_socket)
connection.output_buffer += ws_close_frame(status_code, reason);
connection.close_socket = true;
@@ -261,6 +410,12 @@ FastCGIServer::close_websocket_connection(Connection& connection, u16 status_cod
void
FastCGIServer::fail_websocket_connection(Connection& connection, u16 status_code, String reason)
{
// Drop stale application frames on failure so slow peers cannot keep a large
// queued buffer alive. Preserve an unsent 101 response, though: if the client
// pipelined a bad WebSocket frame immediately after the HTTP upgrade, the
// close frame must still follow the accepted upgrade response.
if(!str_starts_with(connection.output_buffer, "HTTP/1.1 101 Switching Protocols\r\n"))
connection.output_buffer = "";
close_websocket_connection(connection, status_code, reason);
}
@@ -339,20 +494,14 @@ FastCGIServer::process(int timeout_ms)
throw std::runtime_error("accept() failed");
}
set_socket_nonblocking(client_socket);
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')
if(client_sockets.size() >= transport_limits().max_client_connections)
{
FastCGIRequest* new_request = new FastCGIRequest();
new_request->resources.client_socket = client_socket;
new_request->resources.server_socket = socket_handle;
new_request->stats.time_init = time_precise();
client_sockets[client_socket]->requests[client_socket] = new_request;
printf("(!) rejecting socket %i: too many clients\n", client_socket);
close(client_socket);
continue;
}
open_client_connection(socket_handle, client_socket);
}
}
@@ -361,6 +510,7 @@ FastCGIServer::process(int timeout_ms)
{
int read_socket = it->first;
Connection* connection = it->second;
enforce_connection_timeouts(*connection);
if(FD_ISSET(read_socket, &fs_read))
{
@@ -378,15 +528,12 @@ FastCGIServer::process(int timeout_ms)
{
connection->close_socket = true;
}
else if(connection->type == 'H' && connection->input_buffer != "")
else if(is_http_like_type(connection->type) && connection->input_buffer != "")
{
process_http_request(
*client_sockets[connection->client_socket]->requests[connection->client_socket],
connection->input_buffer
);
if(connection->close_socket || !connection->output_buffer.empty())
process_http_like_connection_input(*connection);
if(!connection->is_websocket && (connection->close_socket || !connection->output_buffer.empty()))
connection->input_buffer = "";
else
else if(!connection->is_websocket)
connection->close_socket = true;
}
else
@@ -396,22 +543,16 @@ FastCGIServer::process(int timeout_ms)
}
else
{
connection->last_activity_at = time_precise();
connection->input_buffer.append(buffer, read_result);
if(connection->type == 'H')
if(is_http_like_type(connection->type))
{
if(connection->is_websocket)
{
process_websocket_input(*connection);
}
if(!connection->is_websocket && connection->input_buffer.length() > transport_limits().max_http_buffer_bytes())
reject_http_connection(*connection, "HTTP/1.1 413 Payload Too Large", "request is too large\n");
else
{
process_http_request(
*client_sockets[connection->client_socket]->requests[connection->client_socket],
connection->input_buffer
);
if(connection->close_socket || !connection->output_buffer.empty())
connection->input_buffer = "";
}
process_http_like_connection_input(*connection);
if(!connection->is_websocket && (connection->close_socket || !connection->output_buffer.empty()))
connection->input_buffer = "";
}
else
{
@@ -420,6 +561,9 @@ FastCGIServer::process(int timeout_ms)
}
}
if(connection->is_websocket && connection->output_buffer.length() > transport_limits().max_websocket_output_bytes)
fail_websocket_connection(*connection, 1013, "websocket output queue is full");
if(!connection->output_buffer.empty() && FD_ISSET(read_socket, &fs_write))
{
if(connection->type == 'F')
@@ -428,6 +572,9 @@ FastCGIServer::process(int timeout_ms)
goto close_socket;
}
if(connection->is_websocket && connection->output_buffer.empty() && !connection->input_buffer.empty() && !connection->close_socket)
process_websocket_input(*connection);
if(connection->close_socket && connection->output_buffer.empty())
{
close_socket:
@@ -452,141 +599,160 @@ FastCGIServer::process(int timeout_ms)
}
}
void
FastCGIServer::process_http_request(FastCGIRequest& request, String& data)
bool
FastCGIServer::parse_http_message(FastCGIRequest& request, String& data)
{
Connection* connection = client_sockets[request.resources.client_socket];
const TransportLimits& limits = transport_limits();
auto header_end = data.find("\r\n\r\n");
if(header_end == String::npos)
return;
{
if(data.length() > limits.max_http_header_bytes)
reject_http_connection(*connection, "HTTP/1.1 431 Request Header Fields Too Large", "request headers are too large\n");
return(false);
}
if(header_end > limits.max_http_header_bytes)
return(reject_http_connection(*connection, "HTTP/1.1 431 Request Header Fields Too Large", "request headers are too large\n"));
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"], cwd_get());
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"];
}
}
u64 content_length = int_val(first(request.params["CONTENT_LENGTH"], "0"));
if(content_length > limits.max_http_body_bytes)
return(reject_http_connection(*connection, "HTTP/1.1 413 Payload Too Large", "request body is too large\n"));
u64 request_size = header_end + 4 + content_length;
if(request_size > limits.max_http_buffer_bytes())
return(reject_http_connection(*connection, "HTTP/1.1 413 Payload Too Large", "request is too large\n"));
if(data.length() < request_size)
return;
return(false);
request.in = data.substr(header_end + 4, content_length);
request.flags.input_closed = true;
data.erase(0, request_size);
return(true);
}
void
FastCGIServer::process_http_like_connection_input(Connection& connection)
{
if(connection.type == 'H' && connection.is_websocket)
{
process_websocket_input(connection);
return;
}
FastCGIRequest& request = *connection.requests[connection.client_socket];
if(connection.type == 'C')
process_cli_request(request, connection.input_buffer);
else
process_http_request(request, connection.input_buffer);
}
void
FastCGIServer::process_cli_request(FastCGIRequest& request, String& data)
{
if(!parse_http_message(request, data))
return;
Connection* connection = client_sockets[request.resources.client_socket];
if(!on_cli_complete)
{
connection->output_buffer += make_http_text_response(
"HTTP/1.1 500 Internal Server Error",
"UCE CLI dispatcher is not configured\n"
);
}
else
{
on_cli_complete(request);
assemble_output_buffer(request, connection);
}
connection->close_socket = true;
}
bool
FastCGIServer::validate_websocket_upgrade(FastCGIRequest& request, Connection& connection)
{
String request_method = trim(request.params["REQUEST_METHOD"]);
String request_protocol = trim(request.params["SERVER_PROTOCOL"]);
String websocket_connection = to_lower(request.params["HTTP_CONNECTION"]);
String websocket_host = trim(request.params["HTTP_HOST"]);
String websocket_key = trim(request.params["HTTP_SEC_WEBSOCKET_KEY"]);
String websocket_version = trim(request.params["HTTP_SEC_WEBSOCKET_VERSION"]);
if(request_method != "GET")
return(reject_http_connection(connection, "HTTP/1.1 405 Method Not Allowed", "websocket upgrades require GET"));
if(request_protocol != "HTTP/1.1")
return(reject_http_connection(connection, "HTTP/1.1 400 Bad Request", "websocket upgrades require HTTP/1.1"));
if(websocket_host == "")
return(reject_http_connection(connection, "HTTP/1.1 400 Bad Request", "missing Host header"));
if(websocket_connection.find("upgrade") == String::npos)
return(reject_http_connection(connection, "HTTP/1.1 400 Bad Request", "missing Connection: Upgrade header"));
if(websocket_key == "")
return(reject_http_connection(connection, "HTTP/1.1 400 Bad Request", "missing Sec-WebSocket-Key header"));
if(!ws_is_valid_client_key(websocket_key))
return(reject_http_connection(connection, "HTTP/1.1 400 Bad Request", "invalid Sec-WebSocket-Key header"));
if(websocket_version == "")
return(reject_http_connection(connection, "HTTP/1.1 400 Bad Request", "missing Sec-WebSocket-Version header"));
if(websocket_version != "13")
return(reject_http_connection(connection, "HTTP/1.1 426 Upgrade Required", "unsupported websocket version", "Sec-WebSocket-Version: 13\r\n"));
return(true);
}
void
FastCGIServer::begin_websocket_upgrade(FastCGIRequest& request, Connection& connection, String& data)
{
connection.is_websocket = true;
connection.websocket_connection_id = std::to_string(getpid()) + ":" + std::to_string(connection.client_socket);
connection.websocket_scope = first(
request.params["SCRIPT_FILENAME"],
request.params["DOCUMENT_URI"],
request.params["REQUEST_URI"]
);
request.resources.is_websocket = true;
request.resources.websocket_connection_id = connection.websocket_connection_id;
request.resources.websocket_scope = connection.websocket_scope;
request.resources.websocket_connection_state = &connection.websocket_state;
request.connection.set_reference(&connection.websocket_state);
connection.output_buffer +=
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: " + ws_make_accept_key(trim(request.params["HTTP_SEC_WEBSOCKET_KEY"])) + "\r\n\r\n";
if(!data.empty())
process_websocket_input(connection);
}
void
FastCGIServer::process_http_request(FastCGIRequest& request, String& data)
{
if(!parse_http_message(request, data))
return;
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"], cwd_get());
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"];
}
if(to_lower(request.params["HTTP_UPGRADE"]) == "websocket")
{
Connection* connection = client_sockets[request.resources.client_socket];
String request_method = trim(request.params["REQUEST_METHOD"]);
String request_protocol = trim(request.params["SERVER_PROTOCOL"]);
String websocket_connection = to_lower(request.params["HTTP_CONNECTION"]);
String websocket_host = trim(request.params["HTTP_HOST"]);
String websocket_key = trim(request.params["HTTP_SEC_WEBSOCKET_KEY"]);
String websocket_version = trim(request.params["HTTP_SEC_WEBSOCKET_VERSION"]);
if(request_method != "GET")
{
connection->output_buffer += make_http_text_response(
"HTTP/1.1 405 Method Not Allowed",
"websocket upgrades require GET"
);
connection->close_socket = true;
return;
}
if(request_protocol != "HTTP/1.1")
{
connection->output_buffer += make_http_text_response(
"HTTP/1.1 400 Bad Request",
"websocket upgrades require HTTP/1.1"
);
connection->close_socket = true;
return;
}
if(websocket_host == "")
{
connection->output_buffer += make_http_text_response(
"HTTP/1.1 400 Bad Request",
"missing Host header"
);
connection->close_socket = true;
return;
}
if(websocket_connection.find("upgrade") == String::npos)
{
connection->output_buffer += make_http_text_response(
"HTTP/1.1 400 Bad Request",
"missing Connection: Upgrade header"
);
connection->close_socket = true;
return;
}
if(websocket_key == "")
{
connection->output_buffer += make_http_text_response(
"HTTP/1.1 400 Bad Request",
"missing Sec-WebSocket-Key header"
);
connection->close_socket = true;
return;
}
if(!ws_is_valid_client_key(websocket_key))
{
connection->output_buffer += make_http_text_response(
"HTTP/1.1 400 Bad Request",
"invalid Sec-WebSocket-Key header"
);
connection->close_socket = true;
return;
}
if(websocket_version == "")
{
connection->output_buffer += make_http_text_response(
"HTTP/1.1 400 Bad Request",
"missing Sec-WebSocket-Version header"
);
connection->close_socket = true;
return;
}
if(websocket_version != "" && websocket_version != "13")
{
connection->output_buffer += make_http_text_response(
"HTTP/1.1 426 Upgrade Required",
"unsupported websocket version",
"Sec-WebSocket-Version: 13\r\n"
);
connection->close_socket = true;
return;
}
connection->is_websocket = true;
connection->websocket_connection_id = std::to_string(getpid()) + ":" + std::to_string(connection->client_socket);
connection->websocket_scope = first(
request.params["SCRIPT_FILENAME"],
request.params["DOCUMENT_URI"],
request.params["REQUEST_URI"]
);
request.resources.is_websocket = true;
request.resources.websocket_connection_id = connection->websocket_connection_id;
request.resources.websocket_scope = connection->websocket_scope;
request.resources.websocket_connection_state = &connection->websocket_state;
request.connection.set_reference(&connection->websocket_state);
connection->output_buffer +=
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: " + ws_make_accept_key(websocket_key) + "\r\n\r\n";
if(!data.empty())
process_websocket_input(*connection);
if(validate_websocket_upgrade(request, *connection))
begin_websocket_upgrade(request, *connection, data);
}
else
{
@@ -606,6 +772,13 @@ FastCGIServer::process_http_request(FastCGIRequest& request, String& data)
void
FastCGIServer::process_websocket_input(Connection& connection)
{
const TransportLimits& limits = transport_limits();
if(connection.input_buffer.length() > limits.max_websocket_buffer_bytes())
{
fail_websocket_connection(connection, 1009, "websocket input buffer is too large");
return;
}
while(!connection.input_buffer.empty())
{
WSFrame frame;
@@ -613,10 +786,13 @@ FastCGIServer::process_websocket_input(Connection& connection)
if(!frame.parse(connection.input_buffer, error))
{
if(error != "")
{
connection.output_buffer += ws_close_frame(1002, error);
connection.close_socket = true;
}
fail_websocket_connection(connection, 1002, error);
return;
}
if(frame.payload_length > limits.max_websocket_frame_bytes)
{
fail_websocket_connection(connection, 1009, "websocket frame is too large");
return;
}
@@ -645,6 +821,11 @@ FastCGIServer::process_websocket_input(Connection& connection)
return;
}
if(connection.websocket_fragment_buffer.length() + frame.payload.length() > limits.max_websocket_message_bytes)
{
fail_websocket_connection(connection, 1009, "websocket message is too large");
return;
}
connection.websocket_fragment_buffer += frame.payload;
if(!frame.is_final_fragment)
break;
@@ -672,6 +853,12 @@ FastCGIServer::process_websocket_input(Connection& connection)
return;
}
if(frame.payload.length() > limits.max_websocket_message_bytes)
{
fail_websocket_connection(connection, 1009, "websocket message is too large");
return;
}
if(frame.is_final_fragment)
{
if(frame.opcode == 0x1 && !ws_is_valid_utf8(frame.payload))
@@ -720,7 +907,8 @@ FastCGIServer::process_websocket_input(Connection& connection)
return;
}
case 0x9:
connection.output_buffer += ws_encode_frame(frame.payload, 0xA);
if(!queue_websocket_frame(connection, ws_encode_frame(frame.payload, 0xA)))
return;
break;
case 0xA:
break;
@@ -734,15 +922,11 @@ FastCGIServer::process_websocket_input(Connection& connection)
bool
FastCGIServer::websocket_send_to(String connection_id, String message, bool binary)
{
u8 opcode = binary ? 0x2 : 0x1;
for(auto& item : client_sockets)
{
Connection* connection = item.second;
if(connection->is_websocket && connection->websocket_connection_id == connection_id)
{
connection->output_buffer += ws_encode_frame(message, opcode);
return(true);
}
return(queue_websocket_payload(*connection, message, binary));
}
return(false);
}
@@ -751,7 +935,10 @@ u64
FastCGIServer::websocket_broadcast(String scope, String message, bool binary)
{
u64 sent = 0;
u8 opcode = binary ? 0x2 : 0x1;
if(message.length() > transport_limits().max_websocket_message_bytes)
return(sent);
String frame = ws_encode_frame(message, binary ? 0x2 : 0x1);
for(auto& item : client_sockets)
{
Connection* connection = item.second;
@@ -759,8 +946,8 @@ FastCGIServer::websocket_broadcast(String scope, String message, bool binary)
continue;
if(scope != "" && connection->websocket_scope != scope)
continue;
connection->output_buffer += ws_encode_frame(message, opcode);
sent += 1;
if(queue_websocket_frame(*connection, frame))
sent += 1;
}
return(sent);
}
@@ -789,8 +976,7 @@ FastCGIServer::websocket_close(String connection_id, u16 status_code, String rea
Connection* connection = item.second;
if(connection->is_websocket && connection->websocket_connection_id == connection_id)
{
connection->output_buffer += ws_close_frame(status_code, reason);
connection->close_socket = true;
close_websocket_connection(*connection, status_code, reason);
return(true);
}
}
+16 -1
View File
@@ -48,10 +48,12 @@ public:
std::function<int(FastCGIRequest&)> on_request = 0;
std::function<int(FastCGIRequest&)> on_data = 0;
std::function<int(FastCGIRequest&)> on_complete = 0;
std::function<int(FastCGIRequest&)> on_cli_complete = 0;
std::function<int(FastCGIRequest&, const String&, u8)> on_websocket_message = 0;
int listen(unsigned tcp_port);
int listen_http(unsigned tcp_port);
int listen_cli(const std::string& local_path);
int listen(const std::string& local_path);
void process(int timeout_ms = -1); // timeout_ms<0 blocks forever
@@ -75,7 +77,9 @@ public:
DTree websocket_state;
String websocket_fragment_buffer;
u8 websocket_fragment_opcode = 0;
char type = 'F'; // F = FastCGI, H = HttpServer
f64 opened_at = 0;
f64 last_activity_at = 0;
char type = 'F'; // F = FastCGI, H = HttpServer/WebSocket, C = CLI-over-HTTP Unix socket
};
typedef StringMap Pairs;
@@ -88,7 +92,15 @@ public:
void close_http_listeners();
void read_fgci(Connection&);
static bool is_http_like_type(char type);
Connection* open_client_connection(int server_socket, int client_socket);
void enforce_connection_timeouts(Connection& connection);
bool reject_http_connection(Connection& connection, String status_line, String body, String extra_headers = "");
bool queue_websocket_frame(Connection& connection, String frame);
bool queue_websocket_payload(Connection& connection, String message, bool binary = false);
void process_websocket_input(Connection&);
bool validate_websocket_upgrade(FastCGIRequest& request, Connection& connection);
void begin_websocket_upgrade(FastCGIRequest& request, Connection& connection, String& data);
void fail_websocket_connection(Connection& connection, u16 status_code, String reason = "");
void close_websocket_connection(Connection& connection, u16 status_code = 1000, String reason = "");
void dispatch_websocket_message(Connection& connection, RequestID request_id, String payload, u8 opcode);
@@ -105,7 +117,10 @@ public:
const std::string& input, unsigned char type);
static void assemble_output_buffer(FastCGIRequest& request, Connection* connection = 0);
int send_output_buffer(Connection& con);
bool parse_http_message(FastCGIRequest& request, String& data);
void process_http_request(FastCGIRequest& request, String& data);
void process_cli_request(FastCGIRequest& request, String& data);
void process_http_like_connection_input(Connection& connection);
};