2022-06-27 16:38:35 +02:00
/*************************************************************************/
2023-12-18 00:02:58 +01:00
/* http_server_simple.cpp */
2022-06-27 16:38:35 +02:00
/*************************************************************************/
2023-12-18 00:02:58 +01:00
/* This file is part of: */
/* PANDEMONIUM ENGINE */
/* https://github.com/Relintai/pandemonium_engine */
2022-06-27 16:38:35 +02:00
/*************************************************************************/
2023-12-18 00:02:58 +01:00
/* Copyright (c) 2022-present Péter Magyar. */
2022-06-27 16:38:35 +02:00
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2023-12-18 00:02:58 +01:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
2022-06-27 16:38:35 +02:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2022-06-30 16:28:23 +02:00
# include "http_server_simple.h"
2022-06-27 16:38:35 +02:00
2023-02-19 11:53:14 +01:00
# include "core/os/dir_access.h"
2022-06-30 23:18:45 +02:00
# include "http_parser.h"
2023-02-19 10:34:39 +01:00
# include "modules/web/http/web_server_cookie.h"
2022-06-30 23:12:32 +02:00
# include "simple_web_server_request.h"
2022-07-01 17:40:47 +02:00
# include "web_server_simple.h"
2022-06-30 23:12:32 +02:00
2023-02-19 15:11:29 +01:00
# define CONNECTION_OPEN_CLOSE_DEBUG 0
# define CONNECTION_RESPOSE_DEBUG 0
2022-07-24 02:40:39 +02:00
void HTTPServerConnection : : update ( ) {
2023-08-28 12:54:30 +02:00
if ( closed ( ) ) {
return ;
}
2022-06-27 16:38:35 +02:00
2023-08-28 17:16:19 +02:00
if ( OS : : get_singleton ( ) - > get_ticks_usec ( ) - time > _timeout_usec ) {
2022-07-24 02:40:39 +02:00
close ( ) ;
2022-06-30 16:28:23 +02:00
return ;
2022-06-27 16:38:35 +02:00
}
2022-07-24 15:10:42 +02:00
2022-06-30 16:28:23 +02:00
if ( tcp - > get_status ( ) ! = StreamPeerTCP : : STATUS_CONNECTED ) {
return ;
2022-06-27 16:38:35 +02:00
}
2022-06-30 16:28:23 +02:00
if ( use_ssl ) {
if ( ssl . is_null ( ) ) {
ssl = Ref < StreamPeerSSL > ( StreamPeerSSL : : create ( ) ) ;
ssl - > set_blocking_handshake_enabled ( false ) ;
2024-02-16 23:06:39 +01:00
peer = ssl ;
2023-02-19 16:04:08 +01:00
Ref < CryptoKey > key = Ref < CryptoKey > ( CryptoKey : : create ( ) ) ;
Error err = key - > load ( _http_server - > _ssl_key_file ) ;
if ( err ! = OK ) {
close ( ) ;
ERR_FAIL_COND ( err ! = OK ) ;
}
2024-02-16 23:06:39 +01:00
_certificate = Ref < X509Certificate > ( X509Certificate : : create ( ) ) ;
err = _certificate - > load ( _http_server - > _ssl_cert_file ) ;
2023-02-19 16:04:08 +01:00
if ( err ! = OK ) {
close ( ) ;
ERR_FAIL_COND ( err ! = OK ) ;
}
2024-02-16 23:06:39 +01:00
if ( ssl - > accept_stream ( tcp , key , _certificate ) ! = OK ) {
2022-07-24 02:40:39 +02:00
close ( ) ;
2022-06-30 16:28:23 +02:00
return ;
2022-06-27 16:38:35 +02:00
}
}
2023-02-19 15:11:29 +01:00
2022-06-30 16:28:23 +02:00
ssl - > poll ( ) ;
2023-02-19 15:11:29 +01:00
2022-06-30 16:28:23 +02:00
if ( ssl - > get_status ( ) = = StreamPeerSSL : : STATUS_HANDSHAKING ) {
// Still handshaking, keep waiting.
return ;
2022-06-27 16:38:35 +02:00
}
2023-02-19 15:11:29 +01:00
2022-06-30 16:28:23 +02:00
if ( ssl - > get_status ( ) ! = StreamPeerSSL : : STATUS_CONNECTED ) {
2022-07-24 02:40:39 +02:00
close ( ) ;
2022-06-30 16:28:23 +02:00
return ;
2022-06-27 16:38:35 +02:00
}
}
2023-08-28 12:54:30 +02:00
if ( _current_request . is_valid ( ) ) {
2023-09-22 16:21:37 +02:00
update_send_file ( _current_request ) ;
2023-08-28 12:54:30 +02:00
if ( closed ( ) ) {
//some error happened
return ;
}
if ( ! _current_request - > sent ( ) ) {
// we will get back to this
return ;
}
_current_request . unref ( ) ;
}
2022-06-30 23:12:32 +02:00
int read = 0 ;
Error err = peer - > get_partial_data ( req_buf , 4096 , read ) ;
if ( err ! = OK ) {
// Got an error
2022-07-24 02:40:39 +02:00
close ( ) ;
2022-06-30 23:12:32 +02:00
return ;
}
2022-07-01 17:40:47 +02:00
if ( read > 0 ) {
int buffer_start_index = 0 ;
while ( true ) {
char * rb = reinterpret_cast < char * > ( & req_buf [ buffer_start_index ] ) ;
buffer_start_index + = _http_parser - > read_from_buffer ( rb , read ) ;
2022-06-30 23:12:32 +02:00
2022-07-01 17:40:47 +02:00
if ( buffer_start_index > = read ) {
break ;
}
2023-02-19 10:34:39 +01:00
// Stop processing if a protocol error happened
if ( _http_parser - > has_error ( ) ) {
break ;
}
2022-07-01 17:40:47 +02:00
}
2023-08-28 17:16:19 +02:00
// We had activity, reset timeout timer
time = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
2022-07-01 17:40:47 +02:00
}
2022-06-30 23:12:32 +02:00
2022-07-01 17:40:47 +02:00
if ( _http_parser - > get_request_count ( ) > 0 ) {
2023-08-28 12:54:30 +02:00
_current_request = _http_parser - > get_next_request ( ) ;
2022-06-30 23:12:32 +02:00
2023-08-28 12:54:30 +02:00
_current_request - > _server = _http_server ;
_current_request - > _connection = Ref < HTTPServerConnection > ( this ) ;
_current_request - > setup_url_stack ( ) ;
2022-06-27 16:38:35 +02:00
2023-08-28 12:54:30 +02:00
_web_server - > server_handle_request ( _current_request ) ;
if ( closed ( ) ) {
//some error happened
return ;
}
if ( ! _current_request - > sent ( ) ) {
// we will get back to this
2023-08-28 17:16:19 +02:00
time = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
2023-08-28 12:54:30 +02:00
return ;
}
_current_request . unref ( ) ;
2022-07-01 18:25:45 +02:00
if ( _http_parser - > get_request_count ( ) = = 0 & & _http_parser - > is_finished ( ) ) {
2022-07-24 02:40:39 +02:00
close ( ) ;
2022-07-01 18:25:45 +02:00
}
}
2023-02-19 10:34:39 +01:00
if ( _http_parser - > has_error ( ) ) {
close ( ) ;
}
2022-07-01 18:25:45 +02:00
}
2022-07-24 02:40:39 +02:00
void HTTPServerConnection : : send_redirect ( Ref < WebServerRequest > request , const String & location , const HTTPServerEnums : : HTTPStatusCode status_code ) {
2024-02-16 23:06:39 +01:00
if ( closed ( ) ) {
return ;
}
2022-07-01 18:25:45 +02:00
//String s = "HTTP/1.1 " + itos(static_cast<int>(status_code)) + " Found\r\n";
2022-07-21 12:26:39 +02:00
String s = " HTTP/1.1 " + HTTPServerEnums : : get_status_code_header_string ( status_code ) + " \r \n " ;
2023-02-19 15:11:29 +01:00
2023-07-08 21:11:56 +02:00
HashMap < StringName , String > custom_headers = request - > custom_response_headers_get ( ) ;
if ( ! custom_headers . has ( " Location " ) ) {
s + = " Location: " + location + " \r \n " ;
}
if ( ! custom_headers . has ( " Connection " ) ) {
if ( has_more_messages ( ) ) {
s + = " Connection: keep-alive \r \n " ;
} else {
s + = " Connection: close \r \n " ;
}
2023-02-19 15:11:29 +01:00
}
2022-07-21 14:05:55 +02:00
for ( int i = 0 ; i < request - > response_get_cookie_count ( ) ; + + i ) {
Ref < WebServerCookie > cookie = request - > response_get_cookie ( i ) ;
ERR_CONTINUE ( ! cookie . is_valid ( ) ) ;
String cookie_str = cookie - > get_response_header_string ( ) ;
if ( cookie_str ! = " " ) {
s + = cookie_str ;
}
}
2023-07-08 21:11:56 +02:00
for ( HashMap < StringName , String > : : Element * E = custom_headers . front ( ) ; E ; E = E - > next ) {
s + = String ( E - > key ( ) ) + " : " + E - > value ( ) + " \r \n " ;
}
2022-07-01 18:25:45 +02:00
s + = " \r \n " ;
2023-02-19 15:11:29 +01:00
# if CONNECTION_RESPOSE_DEBUG
ERR_PRINT ( s ) ;
# endif
2022-07-01 18:25:45 +02:00
CharString cs = s . utf8 ( ) ;
peer - > put_data ( ( const uint8_t * ) cs . get_data ( ) , cs . size ( ) - 1 ) ;
}
2022-07-24 15:10:42 +02:00
2022-07-24 02:40:39 +02:00
void HTTPServerConnection : : send ( Ref < WebServerRequest > request ) {
2024-02-16 23:06:39 +01:00
if ( closed ( ) ) {
return ;
}
2022-07-21 12:26:39 +02:00
String body = request - > get_compiled_body ( ) ;
2023-07-08 21:11:56 +02:00
HashMap < StringName , String > custom_headers = request - > custom_response_headers_get ( ) ;
2022-07-21 12:26:39 +02:00
String s = " HTTP/1.1 " + HTTPServerEnums : : get_status_code_header_string ( request - > get_status_code ( ) ) + " \r \n " ;
2023-02-19 15:11:29 +01:00
2023-07-08 21:11:56 +02:00
if ( ! custom_headers . has ( " Content-Length " ) ) {
s + = " Content-Length: " + itos ( body . utf8_byte_length ( ) ) + " \r \n " ;
}
if ( ! custom_headers . has ( " Content-type " ) ) {
s + = " Content-type: text/html \r \n " ;
}
if ( ! custom_headers . has ( " Connection " ) ) {
if ( has_more_messages ( ) ) {
s + = " Connection: keep-alive \r \n " ;
} else {
s + = " Connection: close \r \n " ;
}
2023-02-19 15:11:29 +01:00
}
2022-07-21 14:05:55 +02:00
for ( int i = 0 ; i < request - > response_get_cookie_count ( ) ; + + i ) {
Ref < WebServerCookie > cookie = request - > response_get_cookie ( i ) ;
ERR_CONTINUE ( ! cookie . is_valid ( ) ) ;
String cookie_str = cookie - > get_response_header_string ( ) ;
if ( cookie_str ! = " " ) {
s + = cookie_str ;
}
}
2023-07-08 21:11:56 +02:00
for ( HashMap < StringName , String > : : Element * E = custom_headers . front ( ) ; E ; E = E - > next ) {
s + = String ( E - > key ( ) ) + " : " + E - > value ( ) + " \r \n " ;
}
2022-07-01 18:25:45 +02:00
s + = " \r \n " ;
s + = body ;
2023-02-19 15:11:29 +01:00
# if CONNECTION_RESPOSE_DEBUG
ERR_PRINT ( s ) ;
# endif
2022-07-01 18:25:45 +02:00
CharString cs = s . utf8 ( ) ;
peer - > put_data ( ( const uint8_t * ) cs . get_data ( ) , cs . size ( ) - 1 ) ;
}
2022-07-24 02:40:39 +02:00
void HTTPServerConnection : : send_file ( Ref < WebServerRequest > request , const String & p_file_path ) {
2024-02-16 23:06:39 +01:00
if ( closed ( ) ) {
return ;
}
2023-07-08 21:11:56 +02:00
HashMap < StringName , String > custom_headers = request - > custom_response_headers_get ( ) ;
2022-07-01 18:25:45 +02:00
if ( ! FileAccess : : exists ( p_file_path ) ) {
2023-08-28 12:54:30 +02:00
request - > send_error ( HTTPServerEnums : : HTTP_STATUS_CODE_404_NOT_FOUND ) ;
return ;
}
2022-07-21 14:05:55 +02:00
2023-08-28 12:54:30 +02:00
Ref < SimpleWebServerRequest > r = request ;
2023-07-08 21:11:56 +02:00
2023-08-28 12:54:30 +02:00
r - > _sending_file_fa = FileAccess : : open ( p_file_path , FileAccess : : READ ) ;
2023-02-19 15:11:29 +01:00
2023-08-28 12:54:30 +02:00
if ( ! r - > _sending_file_fa ) {
request - > send_error ( HTTPServerEnums : : HTTP_STATUS_CODE_404_NOT_FOUND ) ;
2022-07-01 18:25:45 +02:00
return ;
}
2023-12-22 15:37:04 +01:00
_file_start = 0 ;
_file_length = r - > _sending_file_fa - > get_len ( ) ;
_file_end = _file_length ;
uint64_t content_length = _file_length ;
String result_content_range_header ;
String range_header = request - > get_header_parameter ( " range " ) ;
if ( ! range_header . empty ( ) ) {
//Range: <unit>=<range-start>-
//Range: <unit>=<range-start>-<range-end>
//Range: <unit>=<range-start>-<range-end>, <range-start>-<range-end>
//Range: <unit>=<range-start>-<range-end>, <range-start>-<range-end>, <range-start>-<range-end>
//Range: <unit>=-<suffix-length>
//Range: bytes=200-999, 2000-2499, 9500-
//range_header -> "bytes=200-999, 2000-2499, 9500-"
//Currently only bytes units are registered which are offsets (zero-indexed & inclusive). If the requested data has a content coding applied, each byte range represents the encoded sequence of bytes, not the bytes that would be obtained after decoding.
if ( ! range_header . begins_with ( " bytes= " ) ) {
//If the ranges are invalid, the server returns the 416 Range Not Satisfiable error.
close_file ( request ) ;
request - > send_error ( HTTPServerEnums : : HTTP_STATUS_CODE_416_REQUESTED_RANGE_NOT_SATISFIABLE ) ;
return ;
}
//range_noprefix -> "200-999, 2000-2499, 9500-"
String range_noprefix = range_header . substr_index ( 5 , range_header . size ( ) - 1 ) ;
Vector < String > ranges = range_header . split ( " , " ) ;
if ( ranges . size ( ) = = 0 ) {
//If the ranges are invalid, the server returns the 416 Range Not Satisfiable error.
close_file ( request ) ;
request - > send_error ( HTTPServerEnums : : HTTP_STATUS_CODE_416_REQUESTED_RANGE_NOT_SATISFIABLE ) ;
return ;
}
// only handle the first one. Handling more creates extreme amounts of unnecessary complexity.
//range -> "200-999"
String range = ranges [ 0 ] . strip_edges ( ) ;
Vector < String > range_values = range . split ( " - " ) ;
if ( range_values . size ( ) ! = 2 ) {
//If the ranges are invalid, the server returns the 416 Range Not Satisfiable error.
close_file ( request ) ;
request - > send_error ( HTTPServerEnums : : HTTP_STATUS_CODE_416_REQUESTED_RANGE_NOT_SATISFIABLE ) ;
return ;
}
//9500-
bool error = false ;
uint64_t start_value = 0 ;
if ( ! range_values [ 0 ] . empty ( ) & & ! range_values [ 0 ] . is_valid_unsigned_integer ( ) ) {
start_value = range_values [ 0 ] . to_int64 ( ) ;
} else {
error = true ;
}
if ( ! error & & start_value > = _file_length ) {
error = true ;
}
if ( ! error ) {
_file_start = start_value ;
r - > _sending_file_fa - > seek ( _file_start ) ;
2023-12-22 13:02:17 +01:00
2023-12-22 15:37:04 +01:00
if ( ! range_values [ 1 ] . empty ( ) ) {
//200-999
uint64_t end_value = 0 ;
if ( ! range_values [ 1 ] . is_valid_unsigned_integer ( ) ) {
end_value = range_values [ 1 ] . to_int64 ( ) ;
} else {
error = true ;
}
if ( ! error & & end_value > _file_length ) {
error = true ;
}
if ( ! error ) {
_file_end = end_value ;
}
}
}
if ( error ) {
//If the ranges are invalid, the server returns the 416 Range Not Satisfiable error.
close_file ( request ) ;
request - > send_error ( HTTPServerEnums : : HTTP_STATUS_CODE_416_REQUESTED_RANGE_NOT_SATISFIABLE ) ;
return ;
}
if ( request - > get_status_code ( ) = = 200 ) {
request - > set_status_code ( HTTPServerEnums : : HTTP_STATUS_CODE_206_PARTIAL_CONTENT ) ;
}
content_length = _file_end - _file_start ;
//Content-Range: <unit> <range-start>-<range-end>/<size> (<size> = The total length of the document (or '*' if unknown).)
result_content_range_header = " Content-Range: bytes " + itos ( _file_start ) + " - " + itos ( _file_end ) + " / " + itos ( _file_length ) + + " \r \n " ;
}
2023-12-22 13:02:17 +01:00
2023-08-28 09:15:08 +02:00
String s = " HTTP/1.1 " + HTTPServerEnums : : get_status_code_header_string ( request - > get_status_code ( ) ) + " \r \n " ;
2023-02-19 15:11:29 +01:00
2023-12-22 15:37:04 +01:00
if ( ! result_content_range_header . empty ( ) ) {
s + = result_content_range_header ;
}
2023-07-08 21:11:56 +02:00
if ( ! custom_headers . has ( " Connection " ) ) {
if ( has_more_messages ( ) ) {
s + = " Connection: keep-alive \r \n " ;
} else {
s + = " Connection: close \r \n " ;
}
2023-02-19 15:11:29 +01:00
}
2023-07-08 21:11:56 +02:00
if ( ! custom_headers . has ( " Content-Type " ) ) {
String ctype ;
2023-08-28 13:38:44 +02:00
StringName req_ext = p_file_path . get_extension ( ) . to_lower ( ) ;
2023-07-08 21:11:56 +02:00
if ( _http_server - > mimes . has ( req_ext ) ) {
ctype = _http_server - > mimes [ req_ext ] ;
} else {
2023-08-28 12:56:18 +02:00
ctype = " application/octet-stream " ;
2023-07-08 21:11:56 +02:00
}
s + = " Content-Type: " + ctype + " \r \n " ;
}
2022-07-21 14:05:55 +02:00
2023-12-22 15:37:04 +01:00
s + = " Content-Length: " + itos ( content_length ) + " \r \n " ;
2023-08-28 12:54:30 +02:00
2022-07-21 14:05:55 +02:00
for ( int i = 0 ; i < request - > response_get_cookie_count ( ) ; + + i ) {
Ref < WebServerCookie > cookie = request - > response_get_cookie ( i ) ;
ERR_CONTINUE ( ! cookie . is_valid ( ) ) ;
String cookie_str = cookie - > get_response_header_string ( ) ;
if ( cookie_str ! = " " ) {
s + = cookie_str ;
}
}
2023-07-08 21:11:56 +02:00
for ( HashMap < StringName , String > : : Element * E = custom_headers . front ( ) ; E ; E = E - > next ) {
s + = String ( E - > key ( ) ) + " : " + E - > value ( ) + " \r \n " ;
}
2022-07-01 18:25:45 +02:00
s + = " \r \n " ;
2023-02-19 15:11:29 +01:00
# if CONNECTION_RESPOSE_DEBUG
ERR_PRINT ( s ) ;
# endif
2022-07-01 18:25:45 +02:00
CharString cs = s . utf8 ( ) ;
Error err = peer - > put_data ( ( const uint8_t * ) cs . get_data ( ) , cs . size ( ) - 1 ) ;
if ( err ! = OK ) {
2023-08-28 12:54:30 +02:00
close ( ) ;
return ;
2022-07-01 18:25:45 +02:00
}
2023-08-28 17:16:19 +02:00
_file_buffer_start = 0 ;
_file_buffer_end = 0 ;
2023-08-28 12:54:30 +02:00
2023-09-22 16:21:37 +02:00
update_send_file ( r ) ;
2023-08-28 12:54:30 +02:00
}
2023-09-22 16:21:37 +02:00
void HTTPServerConnection : : update_send_file ( Ref < SimpleWebServerRequest > request ) {
2024-02-16 23:06:39 +01:00
if ( closed ( ) ) {
return ;
}
2023-08-28 17:16:19 +02:00
int loop_count = 0 ;
2022-07-01 18:25:45 +02:00
while ( true ) {
2023-08-28 12:54:30 +02:00
//read into buffer
2023-08-28 17:16:19 +02:00
if ( _file_buffer_start = = _file_buffer_end ) {
_file_buffer_start = 0 ;
2023-08-28 12:54:30 +02:00
2023-12-22 15:37:04 +01:00
uint64_t remaining = _file_end - request - > _sending_file_fa - > get_position ( ) ;
uint64_t read_length = MIN ( remaining , 4096 ) ;
_file_buffer_end = request - > _sending_file_fa - > get_buffer ( _file_send_buffer , read_length ) ;
2023-08-28 12:54:30 +02:00
2023-08-28 17:16:19 +02:00
if ( _file_buffer_end = = 0 ) {
2023-08-28 12:54:30 +02:00
//finished
break ;
}
2022-07-01 18:25:45 +02:00
}
2023-08-28 12:54:30 +02:00
int read = 0 ;
2023-08-28 17:16:19 +02:00
Error err = peer - > put_partial_data ( & _file_send_buffer [ _file_buffer_start ] , _file_buffer_end - _file_buffer_start , read ) ;
2023-08-28 12:54:30 +02:00
2023-08-28 17:16:19 +02:00
_file_buffer_start + = read ;
if ( read > 0 ) {
time = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
}
2023-08-28 12:54:30 +02:00
if ( err = = ERR_BUSY ) {
// we can get ERR_BUSY is the socket is full -> we need to wait
return ;
}
2022-07-01 18:25:45 +02:00
if ( err ! = OK ) {
2023-12-22 15:37:04 +01:00
close_file ( request ) ;
2023-08-28 12:54:30 +02:00
close ( ) ;
2023-08-28 17:16:19 +02:00
_file_buffer_start = 0 ;
_file_buffer_end = 0 ;
return ;
}
loop_count + = 1 ;
if ( loop_count > = _file_buffer_send_max_consecutive_loops ) {
// Work on other clients aswell.
2023-08-28 12:54:30 +02:00
return ;
2022-07-01 18:25:45 +02:00
}
2022-06-27 16:38:35 +02:00
}
2022-07-01 18:25:45 +02:00
2023-12-22 15:37:04 +01:00
close_file ( request ) ;
2023-08-28 12:54:30 +02:00
2023-08-28 17:16:19 +02:00
_file_buffer_start = 0 ;
_file_buffer_end = 0 ;
2022-06-27 16:38:35 +02:00
}
2022-07-24 02:40:39 +02:00
void HTTPServerConnection : : close ( ) {
2023-02-19 15:11:29 +01:00
# if CONNECTION_OPEN_CLOSE_DEBUG
ERR_PRINT ( " CONN CLOSE " ) ;
# endif
2024-02-16 23:06:39 +01:00
if ( ssl . is_valid ( ) ) {
ssl - > disconnect_from_stream ( ) ;
}
if ( tcp . is_valid ( ) ) {
tcp - > disconnect_from_host ( ) ;
}
2022-07-24 02:40:39 +02:00
peer . unref ( ) ;
2024-02-16 23:06:39 +01:00
ssl . unref ( ) ;
tcp . unref ( ) ;
2022-07-24 02:40:39 +02:00
_closed = true ;
}
bool HTTPServerConnection : : closed ( ) {
return _closed ;
}
2023-02-19 15:11:29 +01:00
bool HTTPServerConnection : : has_more_messages ( ) {
if ( _closed ) {
return false ;
}
if ( _http_parser - > has_error ( ) ) {
return false ;
}
if ( _http_parser - > get_request_count ( ) = = 0 & & _http_parser - > is_finished ( ) ) {
return false ;
}
return true ;
}
2023-12-22 15:37:04 +01:00
void HTTPServerConnection : : close_file ( Ref < SimpleWebServerRequest > request ) {
if ( request . is_valid ( ) & & request - > _sending_file_fa ) {
memdelete ( request - > _sending_file_fa ) ;
request - > _sending_file_fa = NULL ;
}
}
2022-07-24 02:40:39 +02:00
HTTPServerConnection : : HTTPServerConnection ( ) {
_web_server = nullptr ;
_http_server = nullptr ;
2023-08-28 17:16:19 +02:00
// This parameter will likely needs some tweaks
_file_buffer_send_max_consecutive_loops = 5 ;
// 20 sec
_timeout_usec = 20 * 1000 * 1000 ;
2022-07-24 02:40:39 +02:00
_http_parser . instance ( ) ;
time = 0 ;
memset ( req_buf , 0 , sizeof ( req_buf ) ) ;
_closed = false ;
2023-08-28 12:54:30 +02:00
2023-08-28 17:16:19 +02:00
_file_buffer_start = 0 ;
_file_buffer_end = 0 ;
2023-12-22 15:37:04 +01:00
_file_start = 0 ;
_file_end = 0 ;
_file_length = 0 ;
2022-07-24 02:40:39 +02:00
}
HTTPServerConnection : : ~ HTTPServerConnection ( ) {
}
void HTTPServerSimple : : stop ( ) {
server - > stop ( ) ;
2022-07-24 13:19:21 +02:00
2022-07-24 02:40:39 +02:00
_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 ;
2023-02-19 16:04:08 +01:00
_ssl_key_file = p_ssl_key ;
_ssl_cert_file = p_ssl_cert ;
2022-07-24 13:19:21 +02:00
2024-03-10 07:01:05 +01:00
if ( upload_file_store_type = = WebServerSimple : : FILE_UPLOAD_STORE_TYPE_TEMP_FILES ) {
DirAccess * dir = DirAccess : : create_for_path ( upload_temp_file_store_path ) ;
ERR_FAIL_COND_V ( ! dir , ERR_FILE_CANT_WRITE ) ;
Error err = OK ;
if ( ! dir - > dir_exists ( upload_temp_file_store_path ) ) {
err = dir - > make_dir_recursive ( upload_temp_file_store_path ) ;
}
memdelete ( dir ) ;
ERR_FAIL_COND_V_MSG ( err ! = OK , ERR_FILE_CANT_WRITE , vformat ( " Cannot create temporary files directory for uploads! Error: %d " , err ) ) ;
}
2022-07-24 02:40:39 +02:00
if ( use_ssl ) {
Ref < Crypto > crypto = Crypto : : create ( ) ;
if ( crypto . is_null ( ) ) {
return ERR_UNAVAILABLE ;
}
2022-07-24 13:19:21 +02:00
2022-07-24 02:40:39 +02:00
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 ) ;
}
}
2022-07-24 13:19:21 +02:00
Error err = server - > listen ( p_port , p_address ) ;
if ( err ! = OK ) {
return err ;
}
if ( _use_worker_threads ) {
for ( int i = 0 ; i < _thread_count ; + + i ) {
ServerWorkerThread * t = memnew ( ServerWorkerThread ) ;
t - > running = true ;
t - > server . reference_ptr ( this ) ;
t - > semaphore = memnew ( Semaphore ) ;
t - > thread = memnew ( Thread ( ) ) ;
t - > thread - > start ( HTTPServerSimple : : _worker_thread_func , t ) ;
_threads . push_back ( t ) ;
}
}
return OK ;
2022-07-24 02:40:39 +02:00
}
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 ( ) ) {
2022-07-24 15:10:42 +02:00
_connections_lock . write_lock ( ) ;
Ref < StreamPeerTCP > tcp = server - > take_connection ( ) ;
ERR_CONTINUE ( ! tcp . is_valid ( ) ) ;
2023-02-19 15:11:29 +01:00
# if CONNECTION_OPEN_CLOSE_DEBUG
ERR_PRINT ( " NEW CONN " ) ;
# endif
2022-07-24 02:40:39 +02:00
Ref < HTTPServerConnection > connection ;
connection . instance ( ) ;
connection - > _web_server = _web_server ;
connection - > _http_server = this ;
2023-03-18 11:52:25 +01:00
connection - > _http_parser - > max_request_size = max_request_size ;
2024-03-09 23:39:33 +01:00
connection - > _http_parser - > request_max_file_upload_size = request_max_file_upload_size ;
connection - > _http_parser - > upload_file_store_type = upload_file_store_type ;
connection - > _http_parser - > upload_temp_file_store_path = upload_temp_file_store_path ;
2023-03-18 11:52:25 +01:00
2022-07-24 02:40:39 +02:00
connection - > use_ssl = use_ssl ;
connection - > key = key ;
2022-07-24 15:10:42 +02:00
connection - > tcp = tcp ;
2022-07-24 02:40:39 +02:00
connection - > peer = connection - > tcp ;
connection - > time = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
_connections . push_back ( connection ) ;
2022-07-24 11:53:16 +02:00
_connections_lock . write_unlock ( ) ;
2022-07-24 02:40:39 +02:00
}
2022-07-24 13:19:21 +02:00
if ( ! _use_worker_threads ) {
_connections_lock . write_lock ( ) ;
2022-07-24 11:53:16 +02:00
2022-07-24 13:19:21 +02:00
List < Ref < HTTPServerConnection > > : : Element * e = _connections . front ( ) ;
2022-07-24 11:53:16 +02:00
2022-07-24 13:19:21 +02:00
while ( e ) {
Ref < HTTPServerConnection > c = e - > get ( ) ;
2022-07-24 11:53:16 +02:00
2022-07-24 13:19:21 +02:00
if ( c - > closed ( ) ) {
List < Ref < HTTPServerConnection > > : : Element * etmp = e - > next ( ) ;
_connections . erase ( e ) ;
e = etmp ;
continue ;
}
2022-07-24 11:53:16 +02:00
2022-07-24 13:19:21 +02:00
c - > update ( ) ;
2022-07-24 11:53:16 +02:00
2022-07-24 13:19:21 +02:00
if ( c - > closed ( ) ) {
List < Ref < HTTPServerConnection > > : : Element * etmp = e - > next ( ) ;
_connections . erase ( e ) ;
e = etmp ;
continue ;
}
2022-07-24 02:40:39 +02:00
2022-07-24 13:19:21 +02:00
e = e - > next ( ) ;
2022-07-24 02:40:39 +02:00
}
2022-07-24 13:19:21 +02:00
_connections_lock . write_unlock ( ) ;
} else {
if ( _connections . size ( ) > 0 ) {
_wake_workers ( ) ;
2022-07-24 11:53:16 +02:00
}
2022-07-24 02:40:39 +02:00
}
}
2023-12-22 12:38:38 +01:00
Dictionary HTTPServerSimple : : unregister_connection_for_request ( const Ref < WebServerRequest > & request ) {
Ref < SimpleWebServerRequest > srequest = request ;
Dictionary d ;
d [ " result " ] = ERR_DOES_NOT_EXIST ;
if ( ! srequest . is_valid ( ) ) {
return d ;
}
bool found = false ;
_connections_lock . write_lock ( ) ;
List < Ref < HTTPServerConnection > > : : Element * e = _connections . front ( ) ;
while ( e ) {
Ref < HTTPServerConnection > c = e - > get ( ) ;
if ( c - > _current_request = = srequest ) {
d [ " result " ] = OK ;
d [ " use_ssl " ] = c - > use_ssl ;
d [ " key " ] = c - > key ;
d [ " tcp " ] = c - > tcp ;
d [ " ssl " ] = c - > ssl ;
d [ " peer " ] = c - > peer ;
c - > _closed = true ;
found = true ;
_connections . erase ( e ) ;
break ;
}
e = e - > next ( ) ;
}
if ( ! found ) {
for ( int i = 0 ; i < _threads . size ( ) ; + + i ) {
ServerWorkerThread * t = _threads [ i ] ;
Ref < HTTPServerConnection > & c = t - > current_connection ;
if ( ! c . is_valid ( ) ) {
continue ;
}
if ( c - > _current_request = = srequest ) {
d [ " result " ] = OK ;
d [ " use_ssl " ] = c - > use_ssl ;
d [ " key " ] = c - > key ;
d [ " tcp " ] = c - > tcp ;
d [ " ssl " ] = c - > ssl ;
d [ " peer " ] = c - > peer ;
//So the thread will not put it back to the connections array
c - > _closed = true ;
break ;
}
}
}
_connections_lock . write_unlock ( ) ;
return d ;
}
2022-06-30 16:28:23 +02:00
HTTPServerSimple : : HTTPServerSimple ( ) {
2022-07-01 17:40:47 +02:00
_web_server = nullptr ;
2022-06-30 16:28:23 +02:00
mimes [ " html " ] = " text/html " ;
mimes [ " js " ] = " application/javascript " ;
mimes [ " json " ] = " application/json " ;
mimes [ " pck " ] = " application/octet-stream " ;
mimes [ " png " ] = " image/png " ;
mimes [ " svg " ] = " image/svg " ;
2022-08-22 14:37:40 +02:00
mimes [ " jpg " ] = " image/jpeg " ;
mimes [ " jpeg " ] = " image/jpeg " ;
2022-06-30 16:28:23 +02:00
mimes [ " wasm " ] = " application/wasm " ;
2022-08-22 14:37:40 +02:00
mimes [ " css " ] = " text/css " ;
2023-03-22 14:00:18 +01:00
mimes [ " pdf " ] = " application/pdf " ;
mimes [ " mp4 " ] = " video/mp4 " ;
mimes [ " zip " ] = " application/zip " ;
mimes [ " rar " ] = " application/vnd.rar " ;
mimes [ " txt " ] = " text/plain " ;
2023-08-28 13:14:50 +02:00
mimes [ " exe " ] = " application/vnd.microsoft.portable-executable " ;
mimes [ " vorbis " ] = " audio/vorbis " ;
mimes [ " otf " ] = " font/otf " ;
mimes [ " sfnt " ] = " font/sfnt " ;
mimes [ " ttf " ] = " font/ttf " ;
mimes [ " ogg " ] = " video/ogg " ;
mimes [ " ogv " ] = " video/ogg " ;
mimes [ " mpv " ] = " video/MPV " ;
mimes [ " quicktime " ] = " video/quicktime " ;
mimes [ " gzip " ] = " application/gzip " ;
mimes [ " pdf " ] = " application/pdf " ;
mimes [ " rtf " ] = " application/rtf " ;
mimes [ " zip " ] = " application/zip " ;
mimes [ " opus " ] = " audio/opus " ;
mimes [ " bmp " ] = " image/bmp " ;
mimes [ " csv " ] = " text/csv " ;
2023-08-28 18:14:14 +02:00
mimes [ " md " ] = " text/markdown " ;
2022-07-01 17:40:47 +02:00
2022-06-30 16:28:23 +02:00
server . instance ( ) ;
stop ( ) ;
2024-03-09 23:39:33 +01:00
max_request_size = 0 ;
request_max_file_upload_size = 0 ;
2022-06-27 16:38:35 +02:00
}
2022-06-30 23:18:45 +02:00
HTTPServerSimple : : ~ HTTPServerSimple ( ) {
}
2022-07-24 02:40:39 +02:00
void HTTPServerSimple : : _clear_clients ( ) {
2022-07-24 11:53:16 +02:00
//stop worker threads first!
2022-07-24 13:19:21 +02:00
_stop_workers ( ) ;
2022-07-24 11:53:16 +02:00
_connections_lock . write_lock ( ) ;
for ( List < Ref < HTTPServerConnection > > : : Element * e = _connections . front ( ) ; e ; e = e - > next ( ) ) {
e - > get ( ) - > close ( ) ;
2022-07-24 02:40:39 +02:00
}
_connections . clear ( ) ;
2022-07-24 11:53:16 +02:00
_connections_lock . write_unlock ( ) ;
2022-06-27 16:38:35 +02:00
}
2022-07-24 13:19:21 +02:00
void HTTPServerSimple : : _stop_workers ( ) {
for ( int i = 0 ; i < _threads . size ( ) ; + + i ) {
_threads . write [ i ] - > running = false ;
_threads . write [ i ] - > semaphore - > post ( ) ;
}
for ( int i = 0 ; i < _threads . size ( ) ; + + i ) {
_threads . write [ i ] - > thread - > wait_to_finish ( ) ;
memdelete ( _threads . write [ i ] - > thread ) ;
memdelete ( _threads . write [ i ] - > semaphore ) ;
}
_threads . clear ( ) ;
}
2022-06-30 16:28:23 +02:00
void HTTPServerSimple : : _set_internal_certs ( Ref < Crypto > p_crypto ) {
2023-02-19 11:42:37 +01:00
const String cache_path = " user://cache/web/ " ;
2023-02-19 11:53:14 +01:00
DirAccess * dir = DirAccess : : create ( DirAccess : : ACCESS_USERDATA ) ;
ERR_FAIL_COND ( ! dir ) ;
dir - > make_dir_recursive ( cache_path ) ;
memdelete ( dir ) ;
2023-02-19 11:42:37 +01:00
const String key_path = cache_path . plus_file ( " http_server_simple_cert.key " ) ;
const String crt_path = cache_path . plus_file ( " http_server_simple_cert.crt " ) ;
2022-06-30 16:28:23 +02:00
bool regen = ! FileAccess : : exists ( key_path ) | | ! FileAccess : : exists ( crt_path ) ;
2022-07-24 11:53:16 +02:00
2022-06-30 16:28:23 +02:00
if ( ! regen ) {
key = Ref < CryptoKey > ( CryptoKey : : create ( ) ) ;
cert = Ref < X509Certificate > ( X509Certificate : : create ( ) ) ;
if ( key - > load ( key_path ) ! = OK | | cert - > load ( crt_path ) ! = OK ) {
regen = true ;
2022-06-27 16:38:35 +02:00
}
}
2022-07-24 11:53:16 +02:00
2022-06-30 16:28:23 +02:00
if ( regen ) {
key = p_crypto - > generate_rsa ( 2048 ) ;
key - > save ( key_path ) ;
cert = p_crypto - > generate_self_signed_certificate ( key , " CN=pandemonium-debug.local,O=A Game Dev,C=XXA " , " 20140101000000 " , " 20340101000000 " ) ;
cert - > save ( crt_path ) ;
2022-06-27 16:38:35 +02:00
}
2023-02-19 16:04:08 +01:00
_ssl_key_file = key_path ;
_ssl_cert_file = crt_path ;
2022-06-27 16:38:35 +02:00
}
2022-07-24 13:19:21 +02:00
void HTTPServerSimple : : _wake_workers ( ) {
for ( int i = 0 ; i < _threads . size ( ) ; + + i ) {
if ( _connections . size ( ) = = 0 ) {
return ;
}
ServerWorkerThread * t = _threads [ i ] ;
if ( ! t - > working ) {
t - > semaphore - > post ( ) ;
}
}
}
void HTTPServerSimple : : _worker_thread_func ( void * data ) {
ServerWorkerThread * context = reinterpret_cast < ServerWorkerThread * > ( data ) ;
Ref < HTTPServerSimple > server = context - > server ;
context - > working = true ;
while ( context - > running ) {
//THis will only work well in a worker thread
while ( ! server - > _connections . empty ( ) ) {
server - > _connections_lock . write_lock ( ) ;
List < Ref < HTTPServerConnection > > : : Element * e = server - > _connections . front ( ) ;
if ( ! e ) {
server - > _connections_lock . write_unlock ( ) ;
break ;
}
Ref < HTTPServerConnection > c = e - > get ( ) ;
2023-12-22 12:38:38 +01:00
context - > current_connection = c ;
2022-07-24 15:10:42 +02:00
2022-07-24 13:19:21 +02:00
server - > _connections . pop_front ( ) ;
server - > _connections_lock . write_unlock ( ) ;
if ( c - > closed ( ) ) {
continue ;
}
c - > update ( ) ;
if ( c - > closed ( ) ) {
continue ;
}
server - > _connections_lock . write_lock ( ) ;
server - > _connections . push_back ( c ) ;
2023-12-22 12:38:38 +01:00
context - > current_connection . unref ( ) ;
2022-07-24 13:19:21 +02:00
server - > _connections_lock . write_unlock ( ) ;
}
context - > working = false ;
context - > semaphore - > wait ( ) ;
context - > working = true ;
}
}