Refactored the SimpleHTTPServer to be able to multi thread it a bit later.

This commit is contained in:
Relintai 2022-07-24 02:40:39 +02:00
parent 7b7b27297f
commit 492f29163c
4 changed files with 167 additions and 79 deletions

View File

@ -35,50 +35,11 @@
#include "simple_web_server_request.h"
#include "web_server_simple.h"
void HTTPServerSimple::stop() {
server->stop();
_clear_client();
}
void HTTPServerConnection::update() {
ERR_FAIL_COND(closed());
Error HTTPServerSimple::listen(int p_port, IP_Address p_address, bool p_use_ssl, String p_ssl_key, String p_ssl_cert) {
use_ssl = p_use_ssl;
if (use_ssl) {
Ref<Crypto> crypto = Crypto::create();
if (crypto.is_null()) {
return ERR_UNAVAILABLE;
}
if (!p_ssl_key.empty() && !p_ssl_cert.empty()) {
key = Ref<CryptoKey>(CryptoKey::create());
Error err = key->load(p_ssl_key);
ERR_FAIL_COND_V(err != OK, err);
cert = Ref<X509Certificate>(X509Certificate::create());
err = cert->load(p_ssl_cert);
ERR_FAIL_COND_V(err != OK, err);
} else {
_set_internal_certs(crypto);
}
}
return server->listen(p_port, p_address);
}
bool HTTPServerSimple::is_listening() const {
return server->is_listening();
}
void HTTPServerSimple::poll() {
if (!server->is_listening()) {
return;
}
if (tcp.is_null()) {
if (!server->is_connection_available()) {
return;
}
tcp = server->take_connection();
peer = tcp;
time = OS::get_singleton()->get_ticks_usec();
}
if (OS::get_singleton()->get_ticks_usec() - time > 1000000) {
_clear_client();
close();
return;
}
if (tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
@ -90,8 +51,8 @@ void HTTPServerSimple::poll() {
ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create());
peer = ssl;
ssl->set_blocking_handshake_enabled(false);
if (ssl->accept_stream(tcp, key, cert) != OK) {
_clear_client();
if (ssl->accept_stream(tcp, key, _http_server->cert) != OK) {
close();
return;
}
}
@ -101,7 +62,7 @@ void HTTPServerSimple::poll() {
return;
}
if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) {
_clear_client();
close();
return;
}
}
@ -111,7 +72,7 @@ void HTTPServerSimple::poll() {
if (err != OK) {
// Got an error
_clear_client();
close();
return;
}
@ -130,18 +91,19 @@ void HTTPServerSimple::poll() {
if (_http_parser->get_request_count() > 0) {
Ref<SimpleWebServerRequest> request = _http_parser->get_next_request();
request->_server = this;
request->_server = _http_server;
request->_connection = Ref<HTTPServerConnection>(this);
request->setup_url_stack();
_web_server->server_handle_request(request);
if (_http_parser->get_request_count() == 0 && _http_parser->is_finished()) {
_clear_client();
close();
}
}
}
void HTTPServerSimple::send_redirect(Ref<WebServerRequest> request, const String &location, const HTTPServerEnums::HTTPStatusCode status_code) {
void HTTPServerConnection::send_redirect(Ref<WebServerRequest> request, const String &location, const HTTPServerEnums::HTTPStatusCode status_code) {
//String s = "HTTP/1.1 " + itos(static_cast<int>(status_code)) + " Found\r\n";
String s = "HTTP/1.1 " + HTTPServerEnums::get_status_code_header_string(status_code) + "\r\n";
s += "Location: " + location + "\r\n";
@ -163,7 +125,7 @@ void HTTPServerSimple::send_redirect(Ref<WebServerRequest> request, const String
CharString cs = s.utf8();
peer->put_data((const uint8_t *)cs.get_data(), cs.size() - 1);
}
void HTTPServerSimple::send(Ref<WebServerRequest> request) {
void HTTPServerConnection::send(Ref<WebServerRequest> request) {
String body = request->get_compiled_body();
String s = "HTTP/1.1 " + HTTPServerEnums::get_status_code_header_string(request->get_status_code()) + "\r\n";
@ -189,7 +151,7 @@ void HTTPServerSimple::send(Ref<WebServerRequest> request) {
CharString cs = s.utf8();
peer->put_data((const uint8_t *)cs.get_data(), cs.size() - 1);
}
void HTTPServerSimple::send_file(Ref<WebServerRequest> request, const String &p_file_path) {
void HTTPServerConnection::send_file(Ref<WebServerRequest> request, const String &p_file_path) {
if (!FileAccess::exists(p_file_path)) {
String s = "HTTP/1.1 404 Not Found\r\n";
s += "Connection: Close\r\n";
@ -215,8 +177,8 @@ void HTTPServerSimple::send_file(Ref<WebServerRequest> request, const String &p_
String ctype;
String req_ext = p_file_path.get_extension();
if (!mimes.has(req_ext)) {
ctype = mimes[req_ext];
if (!_http_server->mimes.has(req_ext)) {
ctype = _http_server->mimes[req_ext];
} else {
ctype = "text/plain";
}
@ -268,6 +230,98 @@ void HTTPServerSimple::send_file(Ref<WebServerRequest> request, const String &p_
memdelete(f);
}
void HTTPServerConnection::close() {
tcp.unref();
ssl.unref();
peer.unref();
_closed = true;
}
bool HTTPServerConnection::closed() {
return _closed;
}
HTTPServerConnection::HTTPServerConnection() {
_web_server = nullptr;
_http_server = nullptr;
_http_parser.instance();
time = 0;
memset(req_buf, 0, sizeof(req_buf));
_closed = false;
}
HTTPServerConnection::~HTTPServerConnection() {
}
void HTTPServerSimple::stop() {
server->stop();
_clear_clients();
}
Error HTTPServerSimple::listen(int p_port, IP_Address p_address, bool p_use_ssl, String p_ssl_key, String p_ssl_cert) {
use_ssl = p_use_ssl;
if (use_ssl) {
Ref<Crypto> crypto = Crypto::create();
if (crypto.is_null()) {
return ERR_UNAVAILABLE;
}
if (!p_ssl_key.empty() && !p_ssl_cert.empty()) {
key = Ref<CryptoKey>(CryptoKey::create());
Error err = key->load(p_ssl_key);
ERR_FAIL_COND_V(err != OK, err);
cert = Ref<X509Certificate>(X509Certificate::create());
err = cert->load(p_ssl_cert);
ERR_FAIL_COND_V(err != OK, err);
} else {
_set_internal_certs(crypto);
}
}
return server->listen(p_port, p_address);
}
bool HTTPServerSimple::is_listening() const {
return server->is_listening();
}
void HTTPServerSimple::poll() {
if (!server->is_listening()) {
return;
}
//todo add connection limit
while (server->is_connection_available()) {
Ref<HTTPServerConnection> connection;
connection.instance();
connection->_web_server = _web_server;
connection->_http_server = this;
connection->use_ssl = use_ssl;
connection->key = key;
connection->tcp = server->take_connection();
connection->peer = connection->tcp;
connection->time = OS::get_singleton()->get_ticks_usec();
_connections.push_back(connection);
}
//TODO This should be done by worker threads (with a proper lock free queue)
for (int i = 0; i < _connections.size(); ++i) {
Ref<HTTPServerConnection> c = _connections[i];
if (c->closed()) {
_connections.remove(i);
--i;
continue;
}
c->update();
}
}
HTTPServerSimple::HTTPServerSimple() {
_web_server = nullptr;
@ -280,19 +334,18 @@ HTTPServerSimple::HTTPServerSimple() {
mimes["wasm"] = "application/wasm";
server.instance();
_http_parser.instance();
stop();
}
HTTPServerSimple::~HTTPServerSimple() {
}
void HTTPServerSimple::_clear_client() {
peer = Ref<StreamPeer>();
ssl = Ref<StreamPeerSSL>();
tcp = Ref<StreamPeerTCP>();
memset(req_buf, 0, sizeof(req_buf));
time = 0;
void HTTPServerSimple::_clear_clients() {
for (int i = 0; i < _connections.size(); ++i) {
_connections.write[i]->close();
}
_connections.clear();
}
void HTTPServerSimple::_set_internal_certs(Ref<Crypto> p_crypto) {

View File

@ -34,6 +34,7 @@
#include "core/io/stream_peer_ssl.h"
#include "core/io/tcp_server.h"
#include "core/io/zip_io.h"
#include "core/vector.h"
#include "core/project_settings.h"
@ -42,8 +43,44 @@
class HTTPParser;
class WebServerSimple;
class WebServerRequest;
class HTTPServerSimple;
class HTTPServerConnection : public Reference {
GDCLASS(HTTPServerConnection, Reference);
public:
void update();
void send_redirect(Ref<WebServerRequest> request, const String &location, const HTTPServerEnums::HTTPStatusCode status_code);
void send(Ref<WebServerRequest> request);
void send_file(Ref<WebServerRequest> request, const String &p_file_path);
void close();
bool closed();
HTTPServerConnection();
~HTTPServerConnection();
WebServerSimple *_web_server;
HTTPServerSimple *_http_server;
bool use_ssl = false;
Ref<CryptoKey> key;
Ref<StreamPeerTCP> tcp;
Ref<StreamPeerSSL> ssl;
Ref<StreamPeer> peer;
Ref<HTTPParser> _http_parser;
uint64_t time = 0;
uint8_t req_buf[4096];
bool _closed;
};
class HTTPServerSimple : public Reference {
GDCLASS(HTTPServerSimple, Reference);
public:
void stop();
@ -51,29 +88,25 @@ public:
bool is_listening() const;
void poll();
void send_redirect(Ref<WebServerRequest> request, const String &location, const HTTPServerEnums::HTTPStatusCode status_code);
void send(Ref<WebServerRequest> request);
void send_file(Ref<WebServerRequest> request, const String &p_file_path);
HTTPServerSimple();
~HTTPServerSimple();
WebServerSimple *_web_server;
Map<String, String> mimes;
Ref<X509Certificate> cert;
private:
Ref<TCP_Server> server;
Map<String, String> mimes;
Ref<StreamPeerTCP> tcp;
Ref<StreamPeerSSL> ssl;
Ref<StreamPeer> peer;
Ref<CryptoKey> key;
Ref<X509Certificate> cert;
Ref<HTTPParser> _http_parser;
bool use_ssl = false;
uint64_t time = 0;
uint8_t req_buf[4096];
void _clear_client();
Ref<CryptoKey> key;
bool use_ssl = false;
//TODO add a lock free queue
Vector<Ref<HTTPServerConnection>> _connections;
void _clear_clients();
void _set_internal_certs(Ref<Crypto> p_crypto);
};

View File

@ -75,13 +75,13 @@ String SimpleWebServerRequest::get_parameter(const String &key) const {
void SimpleWebServerRequest::send_redirect(const String &location, const HTTPServerEnums::HTTPStatusCode status_code) {
ERR_FAIL_COND(!_server);
_server->send_redirect(Ref<WebServerRequest>(this), location, status_code);
_connection->send_redirect(Ref<WebServerRequest>(this), location, status_code);
}
void SimpleWebServerRequest::send() {
ERR_FAIL_COND(!_server);
_server->send(Ref<WebServerRequest>(this));
_connection->send(Ref<WebServerRequest>(this));
// if (connection_closed) {
// SimpleWebServerRequestPool::return_request(this);
@ -92,9 +92,9 @@ void SimpleWebServerRequest::send() {
}
void SimpleWebServerRequest::send_file(const String &p_file_path) {
ERR_FAIL_COND(!_server);
ERR_FAIL_COND(!_connection.is_valid());
_server->send_file(Ref<WebServerRequest>(this), p_file_path);
_connection->send_file(Ref<WebServerRequest>(this), p_file_path);
// SimpleWebServerRequestPool::return_request(this);
}

View File

@ -16,6 +16,7 @@ class HTTPSession;
class WebPermission;
class WebNode;
class HTTPServerSimple;
class HTTPServerConnection;
class SimpleWebServerRequest : public WebServerRequest {
GDCLASS(SimpleWebServerRequest, WebServerRequest);
@ -57,6 +58,7 @@ public:
~SimpleWebServerRequest();
HTTPServerSimple *_server;
Ref<HTTPServerConnection> _connection;
protected:
static void _bind_methods();