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 "simple_web_server_request.h"
#include "web_server_simple.h" #include "web_server_simple.h"
void HTTPServerSimple::stop() { void HTTPServerConnection::update() {
server->stop(); ERR_FAIL_COND(closed());
_clear_client();
}
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) { if (OS::get_singleton()->get_ticks_usec() - time > 1000000) {
_clear_client(); close();
return; return;
} }
if (tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED) { if (tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
@ -90,8 +51,8 @@ void HTTPServerSimple::poll() {
ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create());
peer = ssl; peer = ssl;
ssl->set_blocking_handshake_enabled(false); ssl->set_blocking_handshake_enabled(false);
if (ssl->accept_stream(tcp, key, cert) != OK) { if (ssl->accept_stream(tcp, key, _http_server->cert) != OK) {
_clear_client(); close();
return; return;
} }
} }
@ -101,7 +62,7 @@ void HTTPServerSimple::poll() {
return; return;
} }
if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) { if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) {
_clear_client(); close();
return; return;
} }
} }
@ -111,7 +72,7 @@ void HTTPServerSimple::poll() {
if (err != OK) { if (err != OK) {
// Got an error // Got an error
_clear_client(); close();
return; return;
} }
@ -130,18 +91,19 @@ void HTTPServerSimple::poll() {
if (_http_parser->get_request_count() > 0) { if (_http_parser->get_request_count() > 0) {
Ref<SimpleWebServerRequest> request = _http_parser->get_next_request(); Ref<SimpleWebServerRequest> request = _http_parser->get_next_request();
request->_server = this; request->_server = _http_server;
request->_connection = Ref<HTTPServerConnection>(this);
request->setup_url_stack(); request->setup_url_stack();
_web_server->server_handle_request(request); _web_server->server_handle_request(request);
if (_http_parser->get_request_count() == 0 && _http_parser->is_finished()) { 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 " + itos(static_cast<int>(status_code)) + " Found\r\n";
String s = "HTTP/1.1 " + HTTPServerEnums::get_status_code_header_string(status_code) + "\r\n"; String s = "HTTP/1.1 " + HTTPServerEnums::get_status_code_header_string(status_code) + "\r\n";
s += "Location: " + location + "\r\n"; s += "Location: " + location + "\r\n";
@ -163,7 +125,7 @@ void HTTPServerSimple::send_redirect(Ref<WebServerRequest> request, const String
CharString cs = s.utf8(); CharString cs = s.utf8();
peer->put_data((const uint8_t *)cs.get_data(), cs.size() - 1); 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 body = request->get_compiled_body();
String s = "HTTP/1.1 " + HTTPServerEnums::get_status_code_header_string(request->get_status_code()) + "\r\n"; 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(); CharString cs = s.utf8();
peer->put_data((const uint8_t *)cs.get_data(), cs.size() - 1); 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)) { if (!FileAccess::exists(p_file_path)) {
String s = "HTTP/1.1 404 Not Found\r\n"; String s = "HTTP/1.1 404 Not Found\r\n";
s += "Connection: Close\r\n"; s += "Connection: Close\r\n";
@ -215,8 +177,8 @@ void HTTPServerSimple::send_file(Ref<WebServerRequest> request, const String &p_
String ctype; String ctype;
String req_ext = p_file_path.get_extension(); String req_ext = p_file_path.get_extension();
if (!mimes.has(req_ext)) { if (!_http_server->mimes.has(req_ext)) {
ctype = mimes[req_ext]; ctype = _http_server->mimes[req_ext];
} else { } else {
ctype = "text/plain"; ctype = "text/plain";
} }
@ -268,6 +230,98 @@ void HTTPServerSimple::send_file(Ref<WebServerRequest> request, const String &p_
memdelete(f); 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() { HTTPServerSimple::HTTPServerSimple() {
_web_server = nullptr; _web_server = nullptr;
@ -280,19 +334,18 @@ HTTPServerSimple::HTTPServerSimple() {
mimes["wasm"] = "application/wasm"; mimes["wasm"] = "application/wasm";
server.instance(); server.instance();
_http_parser.instance();
stop(); stop();
} }
HTTPServerSimple::~HTTPServerSimple() { HTTPServerSimple::~HTTPServerSimple() {
} }
void HTTPServerSimple::_clear_client() { void HTTPServerSimple::_clear_clients() {
peer = Ref<StreamPeer>(); for (int i = 0; i < _connections.size(); ++i) {
ssl = Ref<StreamPeerSSL>(); _connections.write[i]->close();
tcp = Ref<StreamPeerTCP>(); }
memset(req_buf, 0, sizeof(req_buf));
time = 0; _connections.clear();
} }
void HTTPServerSimple::_set_internal_certs(Ref<Crypto> p_crypto) { 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/stream_peer_ssl.h"
#include "core/io/tcp_server.h" #include "core/io/tcp_server.h"
#include "core/io/zip_io.h" #include "core/io/zip_io.h"
#include "core/vector.h"
#include "core/project_settings.h" #include "core/project_settings.h"
@ -42,8 +43,44 @@
class HTTPParser; class HTTPParser;
class WebServerSimple; class WebServerSimple;
class WebServerRequest; 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 { class HTTPServerSimple : public Reference {
GDCLASS(HTTPServerSimple, Reference);
public: public:
void stop(); void stop();
@ -51,29 +88,25 @@ public:
bool is_listening() const; bool is_listening() const;
void poll(); 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();
~HTTPServerSimple(); ~HTTPServerSimple();
WebServerSimple *_web_server; WebServerSimple *_web_server;
Map<String, String> mimes;
Ref<X509Certificate> cert;
private: private:
Ref<TCP_Server> server; 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); 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) { void SimpleWebServerRequest::send_redirect(const String &location, const HTTPServerEnums::HTTPStatusCode status_code) {
ERR_FAIL_COND(!_server); 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() { void SimpleWebServerRequest::send() {
ERR_FAIL_COND(!_server); ERR_FAIL_COND(!_server);
_server->send(Ref<WebServerRequest>(this)); _connection->send(Ref<WebServerRequest>(this));
// if (connection_closed) { // if (connection_closed) {
// SimpleWebServerRequestPool::return_request(this); // SimpleWebServerRequestPool::return_request(this);
@ -92,9 +92,9 @@ void SimpleWebServerRequest::send() {
} }
void SimpleWebServerRequest::send_file(const String &p_file_path) { 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); // SimpleWebServerRequestPool::return_request(this);
} }

View File

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