diff --git a/modules/http_server_simple/http_server_simple.cpp b/modules/http_server_simple/http_server_simple.cpp index 47e06bb5f..4768d24e4 100644 --- a/modules/http_server_simple/http_server_simple.cpp +++ b/modules/http_server_simple/http_server_simple.cpp @@ -139,12 +139,19 @@ void HTTPServerConnection::update() { void HTTPServerConnection::send_redirect(Ref request, const String &location, const HTTPServerEnums::HTTPStatusCode status_code) { //String s = "HTTP/1.1 " + itos(static_cast(status_code)) + " Found\r\n"; String s = "HTTP/1.1 " + HTTPServerEnums::get_status_code_header_string(status_code) + "\r\n"; - s += "Location: " + location + "\r\n"; - if (has_more_messages()) { - s += "Connection: keep-alive\r\n"; - } else { - s += "Connection: close\r\n"; + HashMap 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"; + } } for (int i = 0; i < request->response_get_cookie_count(); ++i) { @@ -159,6 +166,10 @@ void HTTPServerConnection::send_redirect(Ref request, const St } } + for (HashMap::Element *E = custom_headers.front(); E; E = E->next) { + s += String(E->key()) + ": " + E->value() + "\r\n"; + } + s += "\r\n"; #if CONNECTION_RESPOSE_DEBUG @@ -172,14 +183,24 @@ void HTTPServerConnection::send_redirect(Ref request, const St void HTTPServerConnection::send(Ref request) { String body = request->get_compiled_body(); - String s = "HTTP/1.1 " + HTTPServerEnums::get_status_code_header_string(request->get_status_code()) + "\r\n"; - s += "Content-Length: " + itos(body.utf8_byte_length()) + "\r\n"; - s += "Content-type: text/html\r\n"; + HashMap custom_headers = request->custom_response_headers_get(); - if (has_more_messages()) { - s += "Connection: keep-alive\r\n"; - } else { - s += "Connection: close\r\n"; + String s = "HTTP/1.1 " + HTTPServerEnums::get_status_code_header_string(request->get_status_code()) + "\r\n"; + + 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"; + } } for (int i = 0; i < request->response_get_cookie_count(); ++i) { @@ -194,6 +215,10 @@ void HTTPServerConnection::send(Ref request) { } } + for (HashMap::Element *E = custom_headers.front(); E; E = E->next) { + s += String(E->key()) + ": " + E->value() + "\r\n"; + } + s += "\r\n"; s += body; @@ -205,13 +230,17 @@ void HTTPServerConnection::send(Ref request) { peer->put_data((const uint8_t *)cs.get_data(), cs.size() - 1); } void HTTPServerConnection::send_file(Ref request, const String &p_file_path) { + HashMap custom_headers = request->custom_response_headers_get(); + if (!FileAccess::exists(p_file_path)) { String s = "HTTP/1.1 404 Not Found\r\n"; - if (has_more_messages()) { - s += "Connection: keep-alive\r\n"; - } else { - s += "Connection: close\r\n"; + if (!custom_headers.has("Connection")) { + if (has_more_messages()) { + s += "Connection: keep-alive\r\n"; + } else { + s += "Connection: close\r\n"; + } } for (int i = 0; i < request->response_get_cookie_count(); ++i) { @@ -226,6 +255,10 @@ void HTTPServerConnection::send_file(Ref request, const String } } + for (HashMap::Element *E = custom_headers.front(); E; E = E->next) { + s += String(E->key()) + ": " + E->value() + "\r\n"; + } + s += "\r\n"; #if CONNECTION_RESPOSE_DEBUG @@ -237,26 +270,30 @@ void HTTPServerConnection::send_file(Ref request, const String return; } - String ctype; - String req_ext = p_file_path.get_extension(); - - if (_http_server->mimes.has(req_ext)) { - ctype = _http_server->mimes[req_ext]; - } else { - ctype = "text/plain"; - } - FileAccess *f = FileAccess::open(p_file_path, FileAccess::READ); ERR_FAIL_COND(!f); String s = "HTTP/1.1 200 OK\r\n"; - if (has_more_messages()) { - s += "Connection: keep-alive\r\n"; - } else { - s += "Connection: close\r\n"; + if (!custom_headers.has("Connection")) { + if (has_more_messages()) { + s += "Connection: keep-alive\r\n"; + } else { + s += "Connection: close\r\n"; + } } - s += "Content-Type: " + ctype + "\r\n"; + if (!custom_headers.has("Content-Type")) { + String ctype; + String req_ext = p_file_path.get_extension(); + + if (_http_server->mimes.has(req_ext)) { + ctype = _http_server->mimes[req_ext]; + } else { + ctype = "text/plain"; + } + + s += "Content-Type: " + ctype + "\r\n"; + } for (int i = 0; i < request->response_get_cookie_count(); ++i) { Ref cookie = request->response_get_cookie(i); @@ -270,10 +307,10 @@ void HTTPServerConnection::send_file(Ref request, const String } } - s += "Access-Control-Allow-Origin: *\r\n"; - s += "Cross-Origin-Opener-Policy: same-origin\r\n"; - s += "Cross-Origin-Embedder-Policy: require-corp\r\n"; - s += "Cache-Control: no-store, max-age=0\r\n"; + for (HashMap::Element *E = custom_headers.front(); E; E = E->next) { + s += String(E->key()) + ": " + E->value() + "\r\n"; + } + s += "\r\n"; #if CONNECTION_RESPOSE_DEBUG diff --git a/modules/web/doc_classes/WebServerRequest.xml b/modules/web/doc_classes/WebServerRequest.xml index 1ac66bf8e..ae604e25a 100644 --- a/modules/web/doc_classes/WebServerRequest.xml +++ b/modules/web/doc_classes/WebServerRequest.xml @@ -48,6 +48,34 @@ Takes the head, body and footer properties, and merges them into the [code]compiled_body[/code] property. It adds an html5 type declaration, then the opening [code]html[/code] tag, then the contents of the [code]head[/code] property to the [code]head[/code] section of the response, and then the contents of the [code]body[/code] then footer property into the [code]body[/code] section of the response, then it closes the main [code]html[/code] tag. + + + + + Returns a custom HTTP response header or an empty String if it's not set. + + + + + + + Returns whether a custom HTTP response header is set. + + + + + + + + Set a custom HTTP response header that will be sent to the client. + + + + + + Returns all the custom response headers that were previously set. + + @@ -394,6 +422,10 @@ The server might set this to true if the connection got closed while handling the request. It's not yet used. + + A shorthand property to set the Content-Type HTTP header. + Equivalent to [code]custom_response_header_set("Content-Type", content_type)[/code] and [code]custom_response_header_get("Content-Type")[/code]. + When you call [code]compile_body()[/code] or [code]compile_and_send_body()[/code], the contents of this property will end up in the bottom of the [code]body[/code] portion of the resulting HTML. diff --git a/modules/web/http/web_server_request.cpp b/modules/web/http/web_server_request.cpp index edcd18b2c..6b1a93bb0 100644 --- a/modules/web/http/web_server_request.cpp +++ b/modules/web/http/web_server_request.cpp @@ -176,6 +176,41 @@ void WebServerRequest::response_remove_cookie_simple(const String &key) { _cookies.push_back(cookie); } +void WebServerRequest::custom_response_header_set(const StringName &key, const String &value) { + _custom_response_headers[key] = value; +} +String WebServerRequest::custom_response_header_get(const StringName &key) { + String *e = _custom_response_headers.getptr(key); + + if (!e) { + return ""; + } + + return *e; +} +bool WebServerRequest::custom_response_header_has(const StringName &key) { + return _custom_response_headers.has(key); +} +HashMap WebServerRequest::custom_response_headers_get() { + return _custom_response_headers; +} +Dictionary WebServerRequest::custom_response_headers_get_bind() { + Dictionary ret; + + for (HashMap::Element *E = _custom_response_headers.front(); E; E = E->next) { + ret[E->key()] = E->value(); + } + + return ret; +} + +String WebServerRequest::get_content_type() { + return custom_response_header_get("Content-Type"); +} +void WebServerRequest::set_content_type(const String &content_type) { + custom_response_header_set("Content-Type", content_type); +} + HTTPServerEnums::HTTPMethod WebServerRequest::get_method() const { return HTTPServerEnums::HTTP_METHOD_GET; } @@ -550,6 +585,15 @@ void WebServerRequest::_bind_methods() { ClassDB::bind_method(D_METHOD("response_remove_cookie", "index"), &WebServerRequest::response_remove_cookie); ClassDB::bind_method(D_METHOD("response_remove_cookie_simple", "key"), &WebServerRequest::response_remove_cookie_simple); + ClassDB::bind_method(D_METHOD("custom_response_header_set", "key", "value"), &WebServerRequest::custom_response_header_set); + ClassDB::bind_method(D_METHOD("custom_response_header_get", "key"), &WebServerRequest::custom_response_header_get); + ClassDB::bind_method(D_METHOD("custom_response_header_has", "key"), &WebServerRequest::custom_response_header_has); + ClassDB::bind_method(D_METHOD("custom_response_headers_get"), &WebServerRequest::custom_response_headers_get_bind); + + ClassDB::bind_method(D_METHOD("get_content_type"), &WebServerRequest::get_content_type); + ClassDB::bind_method(D_METHOD("set_content_type", "content_type"), &WebServerRequest::set_content_type); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "content_type"), "set_content_type", "get_content_type"); + ClassDB::bind_method(D_METHOD("get_method"), &WebServerRequest::get_method); ClassDB::bind_method(D_METHOD("parse_files"), &WebServerRequest::parse_files); diff --git a/modules/web/http/web_server_request.h b/modules/web/http/web_server_request.h index 5fcc6e152..7c6261600 100644 --- a/modules/web/http/web_server_request.h +++ b/modules/web/http/web_server_request.h @@ -4,6 +4,7 @@ #include "core/variant/dictionary.h" #include "core/string/ustring.h" #include "core/containers/vector.h" +#include "core/containers/hash_map.h" #include "core/object/object.h" #include "core/object/reference.h" @@ -67,6 +68,15 @@ public: void response_remove_cookie(const int index); void response_remove_cookie_simple(const String &key); + void custom_response_header_set(const StringName &key, const String& value); + String custom_response_header_get(const StringName &key); + bool custom_response_header_has(const StringName &key); + HashMap custom_response_headers_get(); + Dictionary custom_response_headers_get_bind(); + + String get_content_type(); + void set_content_type(const String &content_type); + virtual HTTPServerEnums::HTTPMethod get_method() const; virtual void parse_files(); @@ -143,6 +153,8 @@ protected: int _path_stack_pointer; Vector> _cookies; + HashMap _custom_response_headers; + Ref _active_permission; int _permissions;