trying to port web app starter from PHP

This commit is contained in:
udo
2026-04-19 09:38:23 +00:00
parent 46d98a092f
commit be514d63d6
546 changed files with 76910 additions and 2807 deletions
+126 -74
View File
@@ -38,6 +38,7 @@
#include <stdexcept>
#include <errno.h> // E*
#include <fcntl.h>
#include <unistd.h> // read, write, close, unlink
#include <arpa/inet.h> // hton*
#include <netinet/in.h> // sockaddr_in, INADDR_*
@@ -74,6 +75,16 @@ is_valid_close_code(u16 status_code)
return(false);
}
static void
set_socket_nonblocking(int socket_handle)
{
int flags = fcntl(socket_handle, F_GETFL, 0);
if(flags == -1)
throw std::runtime_error("fcntl(F_GETFL) failed");
if(fcntl(socket_handle, F_SETFL, flags | O_NONBLOCK) == -1)
throw std::runtime_error("fcntl(F_SETFL) failed");
}
void
FastCGIServer::shutdown()
{
@@ -145,6 +156,7 @@ FastCGIServer::listen(unsigned tcp_port)
if (::listen(server_socket, 100))
throw std::runtime_error("listen() failed");
set_socket_nonblocking(server_socket);
server_sockets.push_back(server_socket);
} catch (...) {
close(server_socket);
@@ -185,6 +197,7 @@ FastCGIServer::listen(const std::string& local_path)
if (::listen(server_socket, 100))
throw std::runtime_error("listen() failed");
set_socket_nonblocking(server_socket);
server_sockets.push_back(server_socket);
listen_unlink.push_back(local_path);
@@ -208,8 +221,14 @@ FastCGIServer::send_output_buffer(Connection& con)
int write_result = write(con.client_socket,
con.output_buffer.data(),
con.output_buffer.size());
if (write_result == -1)
if(write_result == -1)
{
if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
return(0);
if(errno == ECONNRESET || errno == EPIPE)
return(-1);
throw std::runtime_error("write() failed");
}
con.output_buffer.erase(0, write_result);
return write_result;
}
@@ -255,6 +274,8 @@ FastCGIServer::dispatch_websocket_message(Connection& connection, RequestID requ
it->second->resources.is_websocket = true;
it->second->resources.websocket_connection_id = connection.websocket_connection_id;
it->second->resources.websocket_scope = connection.websocket_scope;
it->second->resources.websocket_connection_state = &connection.websocket_state;
it->second->connection.set_reference(&connection.websocket_state);
it->second->resources.websocket_opcode = opcode;
it->second->resources.websocket_is_binary = (opcode == 0x2);
it->second->resources.websocket_is_text = (opcode == 0x1);
@@ -282,125 +303,152 @@ FastCGIServer::process(int timeout_ms)
for(auto con : client_sockets)
{
FD_SET(con.first, &fs_read);
if (!con.second->output_buffer.empty() || con.second->close_socket)
if(!con.second->output_buffer.empty() || con.second->close_socket)
FD_SET(con.first, &fs_write);
nfd = std::max(nfd, con.first);
}
int select_result = select(nfd + 1, &fs_read, &fs_write, NULL,
timeout_ms < 0 ? NULL : &tv);
if (select_result == -1)
if (errno == EINTR)
return;
else
int select_result = select(
nfd + 1,
&fs_read,
&fs_write,
NULL,
timeout_ms < 0 ? NULL : &tv
);
if(select_result == -1)
{
if(errno == EINTR)
return;
throw std::runtime_error("select() failed");
}
for(auto socket_handle : server_sockets)
if (FD_ISSET(socket_handle, &fs_read))
{
int client_socket = accept(socket_handle, NULL, NULL);
if (client_socket == -1)
throw std::runtime_error("accept() failed");
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(!FD_ISSET(socket_handle, &fs_read))
continue;
for(;;)
{
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;
int client_socket = accept(socket_handle, NULL, NULL);
if(client_socket == -1)
{
if(errno == EAGAIN || errno == EWOULDBLOCK)
break;
if(errno == EINTR)
continue;
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')
{
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 = client_sockets.begin();
for(std::map<int, Connection*>::iterator it = client_sockets.begin();
it != client_sockets.end();)
{
int read_socket = it->first;
Connection* connection = it->second;
if (FD_ISSET(read_socket, &fs_read))
if(FD_ISSET(read_socket, &fs_read))
{
int read_result = read(read_socket, buffer, sizeof(buffer));
if (read_result == -1)
if (errno == ECONNRESET)
if(read_result == -1)
{
if(errno == ECONNRESET)
goto close_socket;
else
if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
throw std::runtime_error("read() on socket failed");
if (read_result == 0)
}
else if(read_result == 0)
{
if(connection->type == 'H' && connection->is_websocket)
{
if(it->second->type == 'H' && it->second->is_websocket)
{
it->second->close_socket = true;
}
else 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;
}
connection->close_socket = true;
}
else if(connection->type == 'H' && 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())
connection->input_buffer = "";
else
{
it->second->close_socket = true;
connection->close_socket = true;
}
else
{
connection->close_socket = true;
}
}
else
{
it->second->input_buffer.append(buffer, read_result);
if(it->second->type == 'H')
connection->input_buffer.append(buffer, read_result);
if(connection->type == 'H')
{
if(connection->is_websocket)
{
if(it->second->is_websocket)
{
process_websocket_input(*it->second);
}
else
{
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 = "";
}
process_websocket_input(*connection);
}
else
{
read_fgci(*it->second);
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 = "";
}
}
else
{
read_fgci(*connection);
}
}
}
if (!it->second->output_buffer.empty() &&
FD_ISSET(read_socket, &fs_write))
if(!connection->output_buffer.empty() && FD_ISSET(read_socket, &fs_write))
{
if(it->second->type == 'F')
write_fgci(*it->second);
send_output_buffer(*it->second);
if(connection->type == 'F')
write_fgci(*connection);
if(send_output_buffer(*connection) == -1)
goto close_socket;
}
if (it->second->close_socket && it->second->output_buffer.empty())
if(connection->close_socket && connection->output_buffer.empty())
{
close_socket:
printf("Closing socket %i\n", it->first);
int close_result = close(it->first);
if (close_result == -1 && errno != ECONNRESET)
if(close_result == -1 && errno != ECONNRESET)
throw std::runtime_error("close() failed");
Connection* connection = it->second;
Connection* doomed_connection = it->second;
client_sockets.erase(it++);
delete connection;
delete doomed_connection;
if(calls_until_termination != -1 && client_sockets.size() == 0)
{
calls_until_termination -= 1;
if(calls_until_termination <= 0)
exit(0);
}
} else
}
else
{
++it;
}
}
}
@@ -528,6 +576,8 @@ FastCGIServer::process_http_request(FastCGIRequest& request, String& data)
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"
@@ -682,14 +732,15 @@ FastCGIServer::process_websocket_input(Connection& connection)
}
bool
FastCGIServer::websocket_send_to(String connection_id, String message)
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);
connection->output_buffer += ws_encode_frame(message, opcode);
return(true);
}
}
@@ -697,9 +748,10 @@ FastCGIServer::websocket_send_to(String connection_id, String message)
}
u64
FastCGIServer::websocket_broadcast(String scope, String message)
FastCGIServer::websocket_broadcast(String scope, String message, bool binary)
{
u64 sent = 0;
u8 opcode = binary ? 0x2 : 0x1;
for(auto& item : client_sockets)
{
Connection* connection = item.second;
@@ -707,7 +759,7 @@ FastCGIServer::websocket_broadcast(String scope, String message)
continue;
if(scope != "" && connection->websocket_scope != scope)
continue;
connection->output_buffer += ws_encode_frame(message);
connection->output_buffer += ws_encode_frame(message, opcode);
sent += 1;
}
return(sent);
+3 -2
View File
@@ -72,6 +72,7 @@ public:
bool is_websocket = false;
String websocket_connection_id;
String websocket_scope;
DTree websocket_state;
String websocket_fragment_buffer;
u8 websocket_fragment_opcode = 0;
char type = 'F'; // F = FastCGI, H = HttpServer
@@ -91,8 +92,8 @@ public:
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);
bool websocket_send_to(String connection_id, String message);
u64 websocket_broadcast(String scope, String message);
bool websocket_send_to(String connection_id, String message, bool binary = false);
u64 websocket_broadcast(String scope, String message, bool binary = false);
StringList websocket_connection_ids(String scope = "");
bool websocket_close(String connection_id, u16 status_code = 1000, String reason = "");
static void request_write_fgci(Connection&, RequestID, FastCGIRequest&);