Skip to content

Commit

Permalink
- Now WebServerSimple can also use temporary files for uploads.
Browse files Browse the repository at this point in the history
- Added move_file() and is_file_moved()  helper methods to WebServerRequest.
- get_file_length() in WebServerRequest now returns uint64_t.
- Removed parse_files() from WebServerRequest. It's doesn't work well with how the http protocol works.
  • Loading branch information
Relintai committed Mar 9, 2024
1 parent 432478c commit f86c644
Show file tree
Hide file tree
Showing 14 changed files with 436 additions and 62 deletions.
138 changes: 121 additions & 17 deletions modules/http_server_simple/http_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
#include "./http_parser/http_parser.h"
#include "./multipart_parser_c/multipart_parser.h"
#include "core/log/logger.h"
#include "core/os/dir_access.h"
#include "core/os/os.h"

#include "modules/web/http/web_server_request.h"

Expand Down Expand Up @@ -97,7 +99,13 @@ int HTTPParser::read_from_buffer(const char *p_buffer, const int p_data_length)
HTTPParser::HTTPParser() {
// Should always get set from the outside, if it remains 0 it's a bug.
max_request_size = 0;
request_max_file_upload_size = 0;
upload_file_store_type = WebServerSimple::FILE_UPLOAD_STORE_TYPE_MEMORY;

_upload_file_access = NULL;

_current_request_size = 0;
_current_upload_files_size = 0;

_is_ready = false;
_content_type = REQUEST_CONTENT_URLENCODED;
Expand Down Expand Up @@ -140,6 +148,20 @@ HTTPParser::~HTTPParser() {
memdelete(parser);
memdelete(settings);
parser = nullptr;

if (_upload_file_access) {
String path = _upload_file_access->get_path();
_upload_file_access->close();
memdelete(_upload_file_access);
_upload_file_access = NULL;

DirAccess *d = DirAccess::create_for_path(path.get_base_dir());

if (d) {
d->remove(path);
memdelete(d);
}
}
}

void HTTPParser::_bind_methods() {
Expand Down Expand Up @@ -169,6 +191,7 @@ void HTTPParser::_process_multipart_header_value(const String &val) {

if (_multipart_form_name.length() >= 2 && _multipart_form_name.begins_with("\"") && _multipart_form_name.ends_with("\"")) {
_multipart_form_name.remove(0);
//TODO check if this should be _multipart_form_name.remove(_multipart_form_name.length() - 1);
_multipart_form_name.remove(_multipart_form_name.size() - 1);
}
} else if (kk == "filename") {
Expand All @@ -178,8 +201,55 @@ void HTTPParser::_process_multipart_header_value(const String &val) {

if (_multipart_form_name.length() >= 2 && _multipart_form_name.begins_with("\"") && _multipart_form_name.ends_with("\"")) {
_multipart_form_name.remove(0);
//TODO check if this should be _multipart_form_name.remove(_multipart_form_name.length() - 1);
_multipart_form_name.remove(_multipart_form_name.size() - 1);
}

if (upload_file_store_type == WebServerSimple::FILE_UPLOAD_STORE_TYPE_TEMP_FILES) {
if (_upload_file_access) {
ERR_PRINT("BUG! if (_upload_file_access) is true!");
_upload_file_access->close();
memdelete(_upload_file_access);
_upload_file_access = NULL;
}

DirAccess *da = DirAccess::create_for_path(upload_temp_file_store_path);

if (!da) {
// NO fallback!
ERR_PRINT("upload_file_store_type == FILE_UPLOAD_STORE_TYPE_TEMP_FILES, but temp file path cannot be opened! Sending Error!");
_error = true;
return;
}

// Just use OS::Time for now. These names are internal, and if the file is not copied out it will get deleted automatically.
// If filename exists just add values
String fbase_name = upload_temp_file_store_path + itos(OS::get_singleton()->get_unix_time()) + "_" + itos(OS::get_singleton()->get_ticks_usec());

String fname = fbase_name;
int fcounter = 0;

while (da->file_exists(fname) && fcounter < 100) {
fname = fbase_name + "_" + itos(fcounter);
++fcounter;
}

memdelete(da);

Error err;
_upload_file_access = FileAccess::open(fname, FileAccess::WRITE, &err);

if (err != OK) {
ERR_PRINT(vformat("upload_file_store_type == FILE_UPLOAD_STORE_TYPE_TEMP_FILES, but temp file cannot be opened! Sending Error! Error: %d, FileName: %s", itos(err), fname));
_error = true;

if (_upload_file_access) {
memdelete(_upload_file_access);
}
}

_upload_file_full_path = fname;
}
}
}

Expand Down Expand Up @@ -576,12 +646,38 @@ int HTTPParser::on_multipart_part_data_cb(const char *at, size_t length) {
ERR_PRINT("on_multipart_part_data_cb");
#endif

int l = static_cast<int>(length);
int mfdofs = _multipart_form_data.size();
_multipart_form_data.resize(mfdofs + length);
char *w = _multipart_form_data.ptrw();
for (int i = 0; i < l; ++i) {
w[mfdofs++] = at[i];
if (_upload_file_access) {
_upload_file_access->store_buffer((const uint8_t *)at, (uint64_t)length);

_current_upload_files_size += length;

if (_current_upload_files_size >= request_max_file_upload_size) {
_error = true;

#if PROTOCOL_ERROR_LOGGING_ENABLED
PLOG_ERR("_current_upload_files_size >= request_max_file_upload_size");
#endif

_upload_file_access->close();
memdelete(_upload_file_access);
_upload_file_access = NULL;

DirAccess *d = DirAccess::create_for_path(_upload_file_full_path.get_base_dir());

if (d) {
d->remove(_upload_file_full_path);
memdelete(d);
}
}

} else {
int l = static_cast<int>(length);
int mfdofs = _multipart_form_data.size();
_multipart_form_data.resize(mfdofs + length);
char *w = _multipart_form_data.ptrw();
for (int i = 0; i < l; ++i) {
w[mfdofs++] = at[i];
}
}

return 0;
Expand All @@ -606,19 +702,27 @@ int HTTPParser::on_multipart_part_data_end_cb() {
#endif

if (_multipart_form_is_file) {
if (_multipart_form_data.size() > 0) {
PoolByteArray file_data;
int len = _multipart_form_data.size();
file_data.resize(len);
PoolByteArray::Write w = file_data.write();
const char *r = _multipart_form_data.ptr();
for (int i = 0; i < len; i++) {
w[i] = r[i];
}
if (_upload_file_access) {
_upload_file_access->close();
memdelete(_upload_file_access);
_upload_file_access = NULL;

_request->add_file_temp_file(_multipart_form_name, _multipart_form_filename, _upload_file_full_path);
} else {
if (_multipart_form_data.size() > 0) {
PoolByteArray file_data;
int len = _multipart_form_data.size();
file_data.resize(len);
PoolByteArray::Write w = file_data.write();
const char *r = _multipart_form_data.ptr();
for (int i = 0; i < len; i++) {
w[i] = r[i];
}

w.release();
w.release();

_request->add_file(_multipart_form_name, _multipart_form_filename, file_data);
_request->add_file_memory(_multipart_form_name, _multipart_form_filename, file_data);
}
}
} else {
String s = String::utf8(_multipart_form_data.ptr(), _multipart_form_data.size());
Expand Down
11 changes: 11 additions & 0 deletions modules/http_server_simple/http_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@

#include "core/containers/vector.h"
#include "core/string/ustring.h"
#include "core/os/file_access.h"

#include "core/object/reference.h"

#include "web_server_simple.h"

class SimpleWebServerRequest;
struct http_parser;
struct http_parser_settings;
Expand All @@ -54,6 +57,10 @@ class HTTPParser : public Reference {
};

uint64_t max_request_size;
uint64_t request_max_file_upload_size;

WebServerSimple::FileUploadStoreType upload_file_store_type;
String upload_temp_file_store_path;

Ref<SimpleWebServerRequest> get_next_request();
int get_request_count() const;
Expand All @@ -76,6 +83,10 @@ class HTTPParser : public Reference {
String _partial_data;

uint64_t _current_request_size;
uint64_t _current_upload_files_size;

String _upload_file_full_path;
FileAccess *_upload_file_access;

Ref<SimpleWebServerRequest> _request;
Vector<Ref<SimpleWebServerRequest>> _requests;
Expand Down
8 changes: 6 additions & 2 deletions modules/http_server_simple/http_server_simple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,6 @@ HTTPServerConnection::HTTPServerConnection() {
_timeout_usec = 20 * 1000 * 1000;

_http_parser.instance();
_http_parser->max_request_size = max_request_size;
time = 0;

memset(req_buf, 0, sizeof(req_buf));
Expand Down Expand Up @@ -681,8 +680,10 @@ void HTTPServerSimple::poll() {
connection->_web_server = _web_server;
connection->_http_server = this;

connection->max_request_size = max_request_size;
connection->_http_parser->max_request_size = max_request_size;
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;

connection->use_ssl = use_ssl;
connection->key = key;
Expand Down Expand Up @@ -842,6 +843,9 @@ HTTPServerSimple::HTTPServerSimple() {

server.instance();
stop();

max_request_size = 0;
request_max_file_upload_size = 0;
}

HTTPServerSimple::~HTTPServerSimple() {
Expand Down
8 changes: 6 additions & 2 deletions modules/http_server_simple/http_server_simple.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@

#include "modules/web/http/http_server_enums.h"

#include "web_server_simple.h"

class HTTPParser;
class WebServerSimple;
class WebServerRequest;
Expand Down Expand Up @@ -78,8 +80,6 @@ class HTTPServerConnection : public Reference {
WebServerSimple *_web_server;
HTTPServerSimple *_http_server;

uint64_t max_request_size;

bool use_ssl = false;
Ref<CryptoKey> key;
Ref<X509Certificate> _certificate;
Expand Down Expand Up @@ -126,6 +126,10 @@ class HTTPServerSimple : public Reference {
WebServerSimple *_web_server;

uint64_t max_request_size;
uint64_t request_max_file_upload_size;

WebServerSimple::FileUploadStoreType upload_file_store_type;
String upload_temp_file_store_path;

RBMap<StringName, String> mimes;

Expand Down
Loading

0 comments on commit f86c644

Please sign in to comment.