mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2025-04-06 20:11:49 +02:00
Implement handling multipart forms for the WebServer. It will need some changes to work with binary files though.
This commit is contained in:
parent
2648034425
commit
25dda93577
@ -167,6 +167,12 @@ void WebServerRequest::parse_files() {
|
|||||||
int WebServerRequest::get_file_count() const {
|
int WebServerRequest::get_file_count() const {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
String WebServerRequest::get_file_file_name(const int index) const {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
String WebServerRequest::get_file_key(const int index) const {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
int WebServerRequest::get_file_length(const int index) const {
|
int WebServerRequest::get_file_length(const int index) const {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -501,6 +507,8 @@ void WebServerRequest::_bind_methods() {
|
|||||||
//virtual const uint8_t *get_file_data(const int index) const;
|
//virtual const uint8_t *get_file_data(const int index) const;
|
||||||
ClassDB::bind_method(D_METHOD("parse_files"), &WebServerRequest::parse_files);
|
ClassDB::bind_method(D_METHOD("parse_files"), &WebServerRequest::parse_files);
|
||||||
ClassDB::bind_method(D_METHOD("get_file_count"), &WebServerRequest::get_file_count);
|
ClassDB::bind_method(D_METHOD("get_file_count"), &WebServerRequest::get_file_count);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_file_file_name", "index"), &WebServerRequest::get_file_file_name);
|
||||||
|
ClassDB::bind_method(D_METHOD("get_file_key", "index"), &WebServerRequest::get_file_key);
|
||||||
ClassDB::bind_method(D_METHOD("get_file_length", "index"), &WebServerRequest::get_file_length);
|
ClassDB::bind_method(D_METHOD("get_file_length", "index"), &WebServerRequest::get_file_length);
|
||||||
ClassDB::bind_method(D_METHOD("get_file_data_str"), &WebServerRequest::get_file_data_str);
|
ClassDB::bind_method(D_METHOD("get_file_data_str"), &WebServerRequest::get_file_data_str);
|
||||||
|
|
||||||
|
@ -67,6 +67,8 @@ public:
|
|||||||
|
|
||||||
virtual void parse_files();
|
virtual void parse_files();
|
||||||
virtual int get_file_count() const;
|
virtual int get_file_count() const;
|
||||||
|
virtual String get_file_file_name(const int index) const;
|
||||||
|
virtual String get_file_key(const int index) const;
|
||||||
virtual int get_file_length(const int index) const;
|
virtual int get_file_length(const int index) const;
|
||||||
virtual const uint8_t *get_file_data(const int index) const;
|
virtual const uint8_t *get_file_data(const int index) const;
|
||||||
virtual String get_file_data_str(const int index) const;
|
virtual String get_file_data_str(const int index) const;
|
||||||
|
@ -45,6 +45,9 @@ int HTTPParser::read_from_buffer(const char *p_buffer, const int p_data_length)
|
|||||||
HTTPParser::HTTPParser() {
|
HTTPParser::HTTPParser() {
|
||||||
_is_ready = false;
|
_is_ready = false;
|
||||||
_content_type = REQUEST_CONTENT_URLENCODED;
|
_content_type = REQUEST_CONTENT_URLENCODED;
|
||||||
|
_in_multipart_boundary = false;
|
||||||
|
_in_boundary_header = false;
|
||||||
|
_multipart_form_is_file = false;
|
||||||
|
|
||||||
settings = memnew(http_parser_settings);
|
settings = memnew(http_parser_settings);
|
||||||
|
|
||||||
@ -88,6 +91,152 @@ String HTTPParser::chr_len_to_str(const char *at, size_t length) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HTTPParser::HTTPParser::process_multipart_data() {
|
||||||
|
int iter = 0;
|
||||||
|
//process one element per loop
|
||||||
|
while (true) {
|
||||||
|
//first boundary -> ignore, with everything before it
|
||||||
|
if (!_in_multipart_boundary) {
|
||||||
|
int boundary_index = _partial_data.find(_multipart_boundary);
|
||||||
|
|
||||||
|
if (boundary_index == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boundary_index += _multipart_boundary.size();
|
||||||
|
|
||||||
|
_partial_data = _partial_data.substr(boundary_index);
|
||||||
|
_in_multipart_boundary = true;
|
||||||
|
_in_boundary_header = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//find the first \n\n -> process boundary_header
|
||||||
|
//cut it out from the string.
|
||||||
|
if (_in_boundary_header) {
|
||||||
|
int header_end_index = _partial_data.find("\r\n\r\n");
|
||||||
|
|
||||||
|
if (header_end_index != -1) {
|
||||||
|
String header = _partial_data.substr_index(0, header_end_index);
|
||||||
|
_partial_data = _partial_data.substr(header_end_index + 4);
|
||||||
|
|
||||||
|
header = header.strip_edges();
|
||||||
|
|
||||||
|
//ERR_PRINT("HEADER");
|
||||||
|
//ERR_PRINT(header);
|
||||||
|
|
||||||
|
_process_multipart_header(header);
|
||||||
|
_in_boundary_header = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Boundary header has not yet fully arrived, return
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Multipart body
|
||||||
|
int boundary_index = _partial_data.find(_multipart_boundary);
|
||||||
|
|
||||||
|
if (boundary_index == -1) {
|
||||||
|
//TODO
|
||||||
|
//if file-> append everything to the HTTPTempFile, except the last boundary.size() - 1 characters from the string.
|
||||||
|
//should probably only happen after a while to save on memory use like maybe a meg or two (should be configurable)
|
||||||
|
// Should probably also be configurable whether it happens or not at all
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//ERR_PRINT("BODY");
|
||||||
|
_multipart_form_data = _partial_data.substr_index(0, boundary_index - 4); //to strip the 2 \r\n from before the boundary
|
||||||
|
|
||||||
|
//ERR_PRINT(data);
|
||||||
|
|
||||||
|
if (_multipart_form_is_file) {
|
||||||
|
if (_multipart_form_data == "") {
|
||||||
|
_in_boundary_header = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_request->add_file(_multipart_form_name, _multipart_form_filename, _multipart_form_data);
|
||||||
|
} else {
|
||||||
|
_request->add_parameter(_multipart_form_name, _multipart_form_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
boundary_index += _multipart_boundary.size();
|
||||||
|
_partial_data = _partial_data.substr(boundary_index);
|
||||||
|
|
||||||
|
if (_partial_data.begins_with("--")) {
|
||||||
|
//done
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_in_boundary_header = true;
|
||||||
|
|
||||||
|
//Safety for now
|
||||||
|
++iter;
|
||||||
|
ERR_FAIL_COND(iter == 10000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HTTPParser::_process_multipart_header(const String &header) {
|
||||||
|
_multipart_form_name = "";
|
||||||
|
_multipart_form_filename = "";
|
||||||
|
_multipart_form_content_type = "";
|
||||||
|
_multipart_form_data = "";
|
||||||
|
_multipart_form_is_file = false;
|
||||||
|
|
||||||
|
int nlc = header.get_slice_count("\r\n");
|
||||||
|
|
||||||
|
for (int i = 0; i < nlc; ++i) {
|
||||||
|
String l = header.get_slice("\r\n", i);
|
||||||
|
|
||||||
|
int sc = l.get_slice_count(":");
|
||||||
|
|
||||||
|
if (sc != 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = l.get_slicec(':', 0);
|
||||||
|
String val = l.get_slicec(':', 1);
|
||||||
|
|
||||||
|
if (key == "Content-Disposition") {
|
||||||
|
int c = val.get_slice_count(";");
|
||||||
|
|
||||||
|
for (int j = 0; j < c; ++j) {
|
||||||
|
String vs = val.get_slicec(';', j).strip_edges();
|
||||||
|
|
||||||
|
if (vs.get_slice_count("=") != 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String kk = vs.get_slicec('=', 0);
|
||||||
|
|
||||||
|
if (kk == "name") {
|
||||||
|
_multipart_form_name = vs.get_slicec('=', 1);
|
||||||
|
|
||||||
|
if (_multipart_form_name.length() >= 2 && _multipart_form_name.begins_with("\"") && _multipart_form_name.ends_with("\"")) {
|
||||||
|
_multipart_form_name.remove(0);
|
||||||
|
_multipart_form_name.remove(_multipart_form_name.size() - 1);
|
||||||
|
}
|
||||||
|
} else if (kk == "filename") {
|
||||||
|
_multipart_form_filename = vs.get_slicec('=', 1);
|
||||||
|
_multipart_form_is_file = true;
|
||||||
|
|
||||||
|
if (_multipart_form_name.length() >= 2 && _multipart_form_name.begins_with("\"") && _multipart_form_name.ends_with("\"")) {
|
||||||
|
_multipart_form_name.remove(0);
|
||||||
|
_multipart_form_name.remove(_multipart_form_name.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (key == "Content-Type") {
|
||||||
|
_multipart_form_content_type = val;
|
||||||
|
} else {
|
||||||
|
//Shouldn't happen, should probably close connection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#define MESSAGE_DEBUG 0
|
#define MESSAGE_DEBUG 0
|
||||||
|
|
||||||
int HTTPParser::on_message_begin() {
|
int HTTPParser::on_message_begin() {
|
||||||
@ -98,6 +247,10 @@ int HTTPParser::on_message_begin() {
|
|||||||
_in_header = true;
|
_in_header = true;
|
||||||
_content_type = REQUEST_CONTENT_URLENCODED;
|
_content_type = REQUEST_CONTENT_URLENCODED;
|
||||||
_multipart_boundary = "";
|
_multipart_boundary = "";
|
||||||
|
_in_multipart_boundary = false;
|
||||||
|
_in_multipart_boundary = false;
|
||||||
|
_in_boundary_header = false;
|
||||||
|
_multipart_form_is_file = false;
|
||||||
|
|
||||||
_request.instance();
|
_request.instance();
|
||||||
|
|
||||||
@ -178,6 +331,11 @@ int HTTPParser::on_header_value(const char *at, size_t length) {
|
|||||||
_multipart_boundary = s.substr(bs);
|
_multipart_boundary = s.substr(bs);
|
||||||
_multipart_boundary = _multipart_boundary.strip_edges();
|
_multipart_boundary = _multipart_boundary.strip_edges();
|
||||||
|
|
||||||
|
//TODO can be inside quoted
|
||||||
|
//Append -- if it doesn't have it already
|
||||||
|
//It shouldn't be longer that 70 chars
|
||||||
|
//The CRLF preceeding could also be appended for simpler logic
|
||||||
|
|
||||||
if (_multipart_boundary == "") {
|
if (_multipart_boundary == "") {
|
||||||
//Error! TODO set an error variable and close the connection
|
//Error! TODO set an error variable and close the connection
|
||||||
}
|
}
|
||||||
@ -188,6 +346,8 @@ int HTTPParser::on_header_value(const char *at, size_t length) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO close connection on chunked connection (for now)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int HTTPParser::on_headers_complete() {
|
int HTTPParser::on_headers_complete() {
|
||||||
@ -213,22 +373,15 @@ int HTTPParser::on_body(const char *at, size_t length) {
|
|||||||
ERR_PRINT("on_body " + s);
|
ERR_PRINT("on_body " + s);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (_content_type == REQUEST_CONTENT_MULTIPART_FORM_DATA) {
|
_partial_data += s;
|
||||||
//first boundary -> ignore, with everythong before it
|
|
||||||
//find the first \n\n -> process boundary_header
|
|
||||||
//cut it out from the string.
|
|
||||||
//if file-> create HTTPTempFile class -> try to find boundary, if cant be found append everything to the HTTPTempFile, except the last boundary.size() - 1 characters from the string.
|
|
||||||
//else try to find boundary, if cant be found just append everythong to _partial data and return -> if can be found handle it as normal form param
|
|
||||||
|
|
||||||
//try parse
|
if (_content_type == REQUEST_CONTENT_MULTIPART_FORM_DATA) {
|
||||||
} else if (_content_type == REQUEST_CONTENT_URLENCODED) {
|
process_multipart_data();
|
||||||
_partial_data += s;
|
|
||||||
} else {
|
|
||||||
//ignore. Maybe close connection?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int HTTPParser::on_message_complete() {
|
int HTTPParser::on_message_complete() {
|
||||||
ERR_FAIL_COND_V(!_request.is_valid(), 0);
|
ERR_FAIL_COND_V(!_request.is_valid(), 0);
|
||||||
|
|
||||||
@ -236,13 +389,13 @@ int HTTPParser::on_message_complete() {
|
|||||||
ERR_PRINT("msg_copmlete");
|
ERR_PRINT("msg_copmlete");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (_content_type == REQUEST_CONTENT_MULTIPART_FORM_DATA) {
|
//if (_content_type == REQUEST_CONTENT_MULTIPART_FORM_DATA) {
|
||||||
//the parser seems to cut out the last boundary, so finish parsing the last element, and send _partial_data to a file if a file is being uploaded
|
// process_multipart_data();
|
||||||
} else if (_content_type == REQUEST_CONTENT_URLENCODED) {
|
//} else
|
||||||
|
|
||||||
|
if (_content_type == REQUEST_CONTENT_URLENCODED) {
|
||||||
//Parse the content into the request
|
//Parse the content into the request
|
||||||
//Also add content body
|
//Also add content body
|
||||||
} else {
|
|
||||||
//Add content body to the request?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_requests.push_back(_request);
|
_requests.push_back(_request);
|
||||||
|
@ -47,6 +47,9 @@ protected:
|
|||||||
private:
|
private:
|
||||||
String chr_len_to_str(const char *at, size_t length);
|
String chr_len_to_str(const char *at, size_t length);
|
||||||
|
|
||||||
|
void process_multipart_data();
|
||||||
|
void _process_multipart_header(const String &header);
|
||||||
|
|
||||||
int on_message_begin();
|
int on_message_begin();
|
||||||
int on_url(const char *at, size_t length);
|
int on_url(const char *at, size_t length);
|
||||||
int on_status(const char *at, size_t length);
|
int on_status(const char *at, size_t length);
|
||||||
@ -76,6 +79,14 @@ private:
|
|||||||
String _multipart_boundary;
|
String _multipart_boundary;
|
||||||
bool _in_header;
|
bool _in_header;
|
||||||
String _queued_header_field;
|
String _queued_header_field;
|
||||||
|
bool _in_multipart_boundary;
|
||||||
|
bool _in_boundary_header;
|
||||||
|
|
||||||
|
String _multipart_form_name;
|
||||||
|
String _multipart_form_filename;
|
||||||
|
String _multipart_form_content_type;
|
||||||
|
String _multipart_form_data;
|
||||||
|
bool _multipart_form_is_file;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -29,16 +29,33 @@ HTTPServerEnums::HTTPMethod SimpleWebServerRequest::get_method() const {
|
|||||||
void SimpleWebServerRequest::parse_files() {
|
void SimpleWebServerRequest::parse_files() {
|
||||||
}
|
}
|
||||||
int SimpleWebServerRequest::get_file_count() const {
|
int SimpleWebServerRequest::get_file_count() const {
|
||||||
return 0;
|
return _files.size();
|
||||||
|
}
|
||||||
|
String SimpleWebServerRequest::get_file_file_name(const int index) const {
|
||||||
|
ERR_FAIL_INDEX_V(index, _files.size(), "");
|
||||||
|
|
||||||
|
return _files[index].file_name;
|
||||||
|
}
|
||||||
|
String SimpleWebServerRequest::get_file_key(const int index) const {
|
||||||
|
ERR_FAIL_INDEX_V(index, _files.size(), "");
|
||||||
|
|
||||||
|
return _files[index].file_name;
|
||||||
}
|
}
|
||||||
int SimpleWebServerRequest::get_file_length(const int index) const {
|
int SimpleWebServerRequest::get_file_length(const int index) const {
|
||||||
return 0;
|
ERR_FAIL_INDEX_V(index, _files.size(), 0);
|
||||||
|
|
||||||
|
return _files[index].data.length();
|
||||||
}
|
}
|
||||||
const uint8_t *SimpleWebServerRequest::get_file_data(const int index) const {
|
const uint8_t *SimpleWebServerRequest::get_file_data(const int index) const {
|
||||||
|
ERR_FAIL_INDEX_V(index, _files.size(), nullptr);
|
||||||
|
|
||||||
|
//return _files[index].data.ptr();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
String SimpleWebServerRequest::get_file_data_str(const int index) const {
|
String SimpleWebServerRequest::get_file_data_str(const int index) const {
|
||||||
return "";
|
ERR_FAIL_INDEX_V(index, _files.size(), "");
|
||||||
|
|
||||||
|
return _files[index].data;
|
||||||
}
|
}
|
||||||
|
|
||||||
String SimpleWebServerRequest::get_parameter(const String &key) const {
|
String SimpleWebServerRequest::get_parameter(const String &key) const {
|
||||||
@ -96,6 +113,15 @@ void SimpleWebServerRequest::set_host(const String &value) {
|
|||||||
_host = value;
|
_host = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SimpleWebServerRequest::add_file(const String &key, const String &file_name, const String &data) {
|
||||||
|
FileEntry e;
|
||||||
|
e.key = key;
|
||||||
|
e.file_name = file_name;
|
||||||
|
e.data = data;
|
||||||
|
|
||||||
|
_files.push_back(e);
|
||||||
|
}
|
||||||
|
|
||||||
SimpleWebServerRequest::SimpleWebServerRequest() {
|
SimpleWebServerRequest::SimpleWebServerRequest() {
|
||||||
_server = nullptr;
|
_server = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,8 @@ public:
|
|||||||
|
|
||||||
virtual void parse_files();
|
virtual void parse_files();
|
||||||
virtual int get_file_count() const;
|
virtual int get_file_count() const;
|
||||||
|
virtual String get_file_file_name(const int index) const;
|
||||||
|
virtual String get_file_key(const int index) const;
|
||||||
virtual int get_file_length(const int index) const;
|
virtual int get_file_length(const int index) const;
|
||||||
virtual const uint8_t *get_file_data(const int index) const;
|
virtual const uint8_t *get_file_data(const int index) const;
|
||||||
virtual String get_file_data_str(const int index) const;
|
virtual String get_file_data_str(const int index) const;
|
||||||
@ -45,6 +47,8 @@ public:
|
|||||||
void set_parser_path(const String &value);
|
void set_parser_path(const String &value);
|
||||||
void set_host(const String &value);
|
void set_host(const String &value);
|
||||||
|
|
||||||
|
void add_file(const String &key, const String &file_name, const String &data);
|
||||||
|
|
||||||
//virtual String get_path_full() const;
|
//virtual String get_path_full() const;
|
||||||
|
|
||||||
SimpleWebServerRequest();
|
SimpleWebServerRequest();
|
||||||
@ -58,6 +62,14 @@ protected:
|
|||||||
HashMap<String, String> _parameters;
|
HashMap<String, String> _parameters;
|
||||||
String _parser_path;
|
String _parser_path;
|
||||||
String _host;
|
String _host;
|
||||||
|
|
||||||
|
struct FileEntry {
|
||||||
|
String file_name;
|
||||||
|
String data;
|
||||||
|
String key; //form name key
|
||||||
|
};
|
||||||
|
|
||||||
|
Vector<FileEntry> _files;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user