Optimize header reads (avoid extra allocs)

This commit is contained in:
Fabio Alessandrelli 2019-07-03 12:05:15 +02:00
parent 9233edd344
commit 3380dc9638
4 changed files with 63 additions and 46 deletions

View File

@ -47,11 +47,16 @@ void WSLClient::_do_handshake() {
_requested += sent; _requested += sent;
} else { } else {
uint8_t byte = 0;
int read = 0; int read = 0;
while (true) { while (true) {
Error err = _connection->get_partial_data(&byte, 1, read); if (_resp_pos >= WSL_MAX_HEADER_SIZE) {
// Header is too big
disconnect_from_host();
_on_error();
ERR_EXPLAIN("Response headers too big");
ERR_FAIL();
}
Error err = _connection->get_partial_data(&_resp_buf[_resp_pos], 1, read);
if (err == ERR_FILE_EOF) { if (err == ERR_FILE_EOF) {
// We got a disconnect. // We got a disconnect.
disconnect_from_host(); disconnect_from_host();
@ -66,16 +71,11 @@ void WSLClient::_do_handshake() {
// Busy, wait next poll. // Busy, wait next poll.
break; break;
} }
// TODO lots of allocs. Use a buffer. // Check "\r\n\r\n" header terminator
_response += byte; char *r = (char *)_resp_buf;
if (_response.size() > WSL_MAX_HEADER_SIZE) { int l = _resp_pos;
// Header is too big if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') {
disconnect_from_host(); r[l - 3] = '\0';
_on_error();
ERR_EXPLAIN("Response headers too big");
ERR_FAIL();
}
if (_response.ends_with("\r\n\r\n")) {
String protocol; String protocol;
// Response is over, verify headers and create peer. // Response is over, verify headers and create peer.
if (!_verify_headers(protocol)) { if (!_verify_headers(protocol)) {
@ -93,12 +93,14 @@ void WSLClient::_do_handshake() {
_peer->make_context(data, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size); _peer->make_context(data, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size);
_on_connect(protocol); _on_connect(protocol);
} }
_resp_pos += 1;
} }
} }
} }
bool WSLClient::_verify_headers(String &r_protocol) { bool WSLClient::_verify_headers(String &r_protocol) {
Vector<String> psa = _response.trim_suffix("\r\n\r\n").split("\r\n"); String s = (char *)_resp_buf;
Vector<String> psa = s.split("\r\n");
int len = psa.size(); int len = psa.size();
if (len < 4) { if (len < 4) {
ERR_EXPLAIN("Not enough response headers."); ERR_EXPLAIN("Not enough response headers.");
@ -305,12 +307,17 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) {
_peer->close(p_code, p_reason); _peer->close(p_code, p_reason);
_connection = Ref<StreamPeer>(NULL); _connection = Ref<StreamPeer>(NULL);
_tcp = Ref<StreamPeerTCP>(memnew(StreamPeerTCP)); _tcp = Ref<StreamPeerTCP>(memnew(StreamPeerTCP));
_request = "";
_response = "";
_key = ""; _key = "";
_host = ""; _host = "";
_protocols.resize(0);
_use_ssl = false; _use_ssl = false;
_request = "";
_requested = 0; _requested = 0;
memset(_resp_buf, 0, sizeof(_resp_buf));
_resp_pos = 0;
} }
IP_Address WSLClient::get_connected_host() const { IP_Address WSLClient::get_connected_host() const {
@ -325,7 +332,7 @@ uint16_t WSLClient::get_connected_port() const {
Error WSLClient::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) { Error WSLClient::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) {
ERR_EXPLAIN("Buffers sizes can only be set before listening or connecting"); ERR_EXPLAIN("Buffers sizes can only be set before listening or connecting");
ERR_FAIL_COND_V(_ctx != NULL, FAILED); ERR_FAIL_COND_V(_connection.is_valid(), FAILED);
_in_buf_size = nearest_shift(p_in_buffer - 1) + 10; _in_buf_size = nearest_shift(p_in_buffer - 1) + 10;
_in_pkt_size = nearest_shift(p_in_packets - 1); _in_pkt_size = nearest_shift(p_in_packets - 1);
@ -340,10 +347,9 @@ WSLClient::WSLClient() {
_out_buf_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_BUF) - 1) + 10; _out_buf_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_BUF) - 1) + 10;
_out_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_PKT) - 1); _out_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_PKT) - 1);
_ctx = NULL;
_peer.instance(); _peer.instance();
_tcp.instance(); _tcp.instance();
_requested = 0; disconnect_from_host();
} }
WSLClient::~WSLClient() { WSLClient::~WSLClient() {

View File

@ -49,17 +49,22 @@ private:
int _in_pkt_size; int _in_pkt_size;
int _out_buf_size; int _out_buf_size;
int _out_pkt_size; int _out_pkt_size;
wslay_event_context_ptr _ctx;
Ref<WSLPeer> _peer; Ref<WSLPeer> _peer;
// XXX we could use HTTPClient with some hacking instead...
Ref<StreamPeerTCP> _tcp; Ref<StreamPeerTCP> _tcp;
Ref<StreamPeer> _connection;
CharString _request; CharString _request;
int _requested;
uint8_t _resp_buf[WSL_MAX_HEADER_SIZE];
int _resp_pos;
String _response; String _response;
String _key; String _key;
String _host; String _host;
PoolVector<String> _protocols; PoolVector<String> _protocols;
Ref<StreamPeer> _connection;
int _requested;
bool _use_ssl; bool _use_ssl;
void _do_handshake(); void _do_handshake();

View File

@ -34,8 +34,16 @@
#include "core/os/os.h" #include "core/os/os.h"
#include "core/project_settings.h" #include "core/project_settings.h"
WSLServer::PendingPeer::PendingPeer() {
time = 0;
has_request = false;
response_sent = 0;
req_pos = 0;
memset(req_buf, 0, sizeof(req_buf));
}
bool WSLServer::PendingPeer::_parse_request(String &r_key) { bool WSLServer::PendingPeer::_parse_request(String &r_key) {
Vector<String> psa = request.trim_suffix("\r\n\r\n").split("\r\n"); Vector<String> psa = String((char *)req_buf).split("\r\n");
int len = psa.size(); int len = psa.size();
if (len < 4) { if (len < 4) {
ERR_EXPLAIN("Not enough response headers."); ERR_EXPLAIN("Not enough response headers.");
@ -87,34 +95,35 @@ Error WSLServer::PendingPeer::do_handshake() {
if (OS::get_singleton()->get_ticks_msec() - time > WSL_SERVER_TIMEOUT) if (OS::get_singleton()->get_ticks_msec() - time > WSL_SERVER_TIMEOUT)
return ERR_TIMEOUT; return ERR_TIMEOUT;
if (!has_request) { if (!has_request) {
uint8_t byte = 0;
int read = 0; int read = 0;
while (true) { while (true) {
Error err = connection->get_partial_data(&byte, 1, read); if (req_pos >= WSL_MAX_HEADER_SIZE) {
// Header is too big
ERR_EXPLAIN("Response headers too big");
ERR_FAIL_V(ERR_OUT_OF_MEMORY);
}
Error err = connection->get_partial_data(&req_buf[req_pos], 1, read);
if (err != OK) // Got an error if (err != OK) // Got an error
return FAILED; return FAILED;
else if (read != 1) // Busy, wait next poll else if (read != 1) // Busy, wait next poll
return ERR_BUSY; return ERR_BUSY;
request += byte; char *r = (char *)req_buf;
int l = req_pos;
if (request.size() > WSL_MAX_HEADER_SIZE) { if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') {
ERR_EXPLAIN("Response headers too big"); r[l - 3] = '\0';
ERR_FAIL_V(ERR_OUT_OF_MEMORY);
}
if (request.ends_with("\r\n\r\n")) {
if (!_parse_request(key)) { if (!_parse_request(key)) {
return FAILED; return FAILED;
} }
String r = "HTTP/1.1 101 Switching Protocols\r\n"; String s = "HTTP/1.1 101 Switching Protocols\r\n";
r += "Upgrade: websocket\r\n"; s += "Upgrade: websocket\r\n";
r += "Connection: Upgrade\r\n"; s += "Connection: Upgrade\r\n";
r += "Sec-WebSocket-Accept: " + WSLPeer::compute_key_response(key) + "\r\n"; s += "Sec-WebSocket-Accept: " + WSLPeer::compute_key_response(key) + "\r\n";
r += "\r\n"; s += "\r\n";
response = r.utf8(); response = s.utf8();
has_request = true; has_request = true;
WARN_PRINTS("Parsed, " + key);
break; break;
} }
req_pos += 1;
} }
} }
if (has_request && response_sent < response.size() - 1) { if (has_request && response_sent < response.size() - 1) {

View File

@ -55,17 +55,14 @@ private:
Ref<StreamPeer> connection; Ref<StreamPeer> connection;
int time; int time;
String request; uint8_t req_buf[WSL_MAX_HEADER_SIZE];
int req_pos;
String key; String key;
bool has_request; bool has_request;
CharString response; CharString response;
int response_sent; int response_sent;
PendingPeer() { PendingPeer();
time = 0;
has_request = false;
response_sent = 0;
}
Error do_handshake(); Error do_handshake();
}; };