Implemented custom response headers for WebServerRequest.

This commit is contained in:
Relintai 2023-07-08 21:11:56 +02:00
parent 3244eab5c5
commit b68bbd3bfb
4 changed files with 159 additions and 34 deletions

View File

@ -139,12 +139,19 @@ void HTTPServerConnection::update() {
void HTTPServerConnection::send_redirect(Ref<WebServerRequest> request, const String &location, const HTTPServerEnums::HTTPStatusCode status_code) {
//String s = "HTTP/1.1 " + itos(static_cast<int>(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<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";
}
}
for (int i = 0; i < request->response_get_cookie_count(); ++i) {
@ -159,6 +166,10 @@ void HTTPServerConnection::send_redirect(Ref<WebServerRequest> request, const St
}
}
for (HashMap<StringName, String>::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<WebServerRequest> request, const St
void HTTPServerConnection::send(Ref<WebServerRequest> 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<StringName, String> 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<WebServerRequest> request) {
}
}
for (HashMap<StringName, String>::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<WebServerRequest> request) {
peer->put_data((const uint8_t *)cs.get_data(), cs.size() - 1);
}
void HTTPServerConnection::send_file(Ref<WebServerRequest> request, const String &p_file_path) {
HashMap<StringName, String> 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<WebServerRequest> request, const String
}
}
for (HashMap<StringName, String>::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<WebServerRequest> 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<WebServerCookie> cookie = request->response_get_cookie(i);
@ -270,10 +307,10 @@ void HTTPServerConnection::send_file(Ref<WebServerRequest> 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<StringName, String>::Element *E = custom_headers.front(); E; E = E->next) {
s += String(E->key()) + ": " + E->value() + "\r\n";
}
s += "\r\n";
#if CONNECTION_RESPOSE_DEBUG

View File

@ -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.
</description>
</method>
<method name="custom_response_header_get">
<return type="String" />
<argument index="0" name="key" type="StringName" />
<description>
Returns a custom HTTP response header or an empty String if it's not set.
</description>
</method>
<method name="custom_response_header_has">
<return type="bool" />
<argument index="0" name="key" type="StringName" />
<description>
Returns whether a custom HTTP response header is set.
</description>
</method>
<method name="custom_response_header_set">
<return type="void" />
<argument index="0" name="key" type="StringName" />
<argument index="1" name="value" type="String" />
<description>
Set a custom HTTP response header that will be sent to the client.
</description>
</method>
<method name="custom_response_headers_get">
<return type="Dictionary" />
<description>
Returns all the custom response headers that were previously set.
</description>
</method>
<method name="get_cookie">
<return type="String" />
<argument index="0" name="key" type="String" />
@ -394,6 +422,10 @@
<member name="connection_closed" type="bool" setter="set_connection_closed" getter="get_connection_closed" default="false">
The server might set this to true if the connection got closed while handling the request. It's not yet used.
</member>
<member name="content_type" type="String" setter="set_content_type" getter="get_content_type" default="&quot;&quot;">
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].
</member>
<member name="footer" type="String" setter="set_footer" getter="get_footer" default="&quot;&quot;">
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.
</member>

View File

@ -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<StringName, String> WebServerRequest::custom_response_headers_get() {
return _custom_response_headers;
}
Dictionary WebServerRequest::custom_response_headers_get_bind() {
Dictionary ret;
for (HashMap<StringName, String>::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);

View File

@ -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<StringName, String> 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<Ref<WebServerCookie>> _cookies;
HashMap<StringName, String> _custom_response_headers;
Ref<WebPermission> _active_permission;
int _permissions;