4
0
Fork 0
WolfBox/addons/godottpd/http_response.gd

175 lines
6.6 KiB
GDScript3
Raw Normal View History

2025-05-31 22:21:22 +01:00
## A response object useful to send out responses
class_name HttpResponse
extends RefCounted
## The client currently talking to the server
var client: StreamPeer
## The server identifier to use on responses [GodotTPD]
var server_identifier: String = "GodotTPD"
## A dictionary of headers
## [br] Headers can be set using the `set(name, value)` function
var headers: Dictionary = {}
## An array of cookies
## [br] Cookies can be set using the `cookie(name, value, options)` function
## [br] Cookies will be automatically sent via "Set-Cookie" headers to clients
var cookies: Array = []
## Origins allowed to call this resource
var access_control_origin = "*"
## Comma separed methods for the access control
var access_control_allowed_methods = "POST, GET, OPTIONS"
## Comma separed headers for the access control
var access_control_allowed_headers = "content-type"
## Send out a raw (Bytes) response to the client
## [br] Useful to send files faster or raw data which will be converted by the client
## [br][param status] - The HTTP Status code to send
## [br][param data] - The body data to send
## [br][param content_type] - The type of content to send.
func send_raw(status_code: int, data: PackedByteArray = PackedByteArray([]), content_type: String = "application/octet-stream") -> void:
client.put_data(("HTTP/1.1 %d %s\r\n" % [status_code, _match_status_code(status_code)]).to_ascii_buffer())
client.put_data(("Server: %s\r\n" % server_identifier).to_ascii_buffer())
for header in headers.keys():
client.put_data(("%s: %s\r\n" % [header, headers[header]]).to_ascii_buffer())
for cookie in cookies:
client.put_data(("Set-Cookie: %s\r\n" % cookie).to_ascii_buffer())
client.put_data(("Content-Length: %d\r\n" % data.size()).to_ascii_buffer())
client.put_data("Connection: close\r\n".to_ascii_buffer())
client.put_data(("Access-Control-Allow-Origin: %s\r\n" % access_control_origin).to_ascii_buffer())
client.put_data(("Access-Control-Allow-Methods: %s\r\n" % access_control_allowed_methods).to_ascii_buffer())
client.put_data(("Access-Control-Allow-Headers: %s\r\n" % access_control_allowed_headers).to_ascii_buffer())
client.put_data(("Content-Type: %s\r\n\r\n" % content_type).to_ascii_buffer())
client.put_data(data)
## Send out a response to the client
## [br]
## [br][param status_code] - The HTTP status code to send
## [br][param data] - The body to send
## [br][param content_type] - The type of the content to send
func send(status_code: int, data: String = "", content_type = "text/html") -> void:
send_raw(status_code, data.to_ascii_buffer(), content_type)
## Send out a JSON response to the client
## [br] This function will internally call the [method send]
## [br]
## [br][param status_code] - The HTTP status code to send
## [br][param data] - The body to send
func json(status_code: int, data) -> void:
send(status_code, JSON.stringify(data), "application/json")
## Sets the responses header "field" to "value"
## [br]
## [br][param field] - The name of the header. i.e. [code]Accept-Type[/code]
## [br][param value] - The value of this header. i.e. [code]application/json[/code]
func set(field: StringName, value: Variant) -> void:
headers[field] = value
## Sets cookie "name" to "value"
## [br]
## [br][param name] - The name of the cookie. i.e. [code]user-id[/code]
## [br][param value] - The value of this cookie. i.e. [code]abcdef[/code]
## [br][param options] - A Dictionary of [url=https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#attributes]cookie attributes[/url]
## for this specific cokkie in the [code]{ "secure" : "true"}[/code] format.
func cookie(name: String, value: String, options: Dictionary = {}) -> void:
var cookie: String = name+"="+value
if options.has("domain"): cookie+="; Domain="+options["domain"]
if options.has("max-age"): cookie+="; Max-Age="+options["max-age"]
if options.has("expires"): cookie+="; Expires="+options["expires"]
if options.has("path"): cookie+="; Path="+options["path"]
if options.has("secure"): cookie+="; Secure="+options["secure"]
if options.has("httpOnly"): cookie+="; HttpOnly="+options["httpOnly"]
if options.has("sameSite"):
match (options["sameSite"]):
true: cookie += "; SameSite=Strict"
"lax": cookie += "; SameSite=Lax"
"strict": cookie += "; SameSite=Strict"
"none": cookie += "; SameSite=None"
_: pass
cookies.append(cookie)
## Automatically matches a "status_code" to an RFC 7231 compliant "status_text"
## [br]
## [br][param code] - The HTTP Status code to be matched
## [br]Returns: the matched [code]status_text[/code]
func _match_status_code(code: int) -> String:
var text: String = "OK"
match(code):
# 1xx - Informational Responses
100: text="Continue"
101: text="Switching protocols"
102: text="Processing"
103: text="Early Hints"
# 2xx - Successful Responses
200: text="OK"
201: text="Created"
202: text="Accepted"
203: text="Non-Authoritative Information"
204: text="No Content"
205: text="Reset Content"
206: text="Partial Content"
207: text="Multi-Status"
208: text="Already Reported"
226: text="IM Used"
# 3xx - Redirection Messages
300: text="Multiple Choices"
301: text="Moved Permanently"
302: text="Found (Previously 'Moved Temporarily')"
303: text="See Other"
304: text="Not Modified"
305: text="Use Proxy"
306: text="Switch Proxy"
307: text="Temporary Redirect"
308: text="Permanent Redirect"
# 4xx - Client Error Responses
400: text="Bad Request"
401: text="Unauthorized"
402: text="Payment Required"
403: text="Forbidden"
404: text="Not Found"
405: text="Method Not Allowed"
406: text="Not Acceptable"
407: text="Proxy Authentication Required"
408: text="Request Timeout"
409: text="Conflict"
410: text="Gone"
411: text="Length Required"
412: text="Precondition Failed"
413: text="Payload Too Large"
414: text="URI Too Long"
415: text="Unsupported Media Type"
416: text="Range Not Satisfiable"
417: text="Expectation Failed"
418: text="I'm a Teapot"
421: text="Misdirected Request"
422: text="Unprocessable Entity"
423: text="Locked"
424: text="Failed Dependency"
425: text="Too Early"
426: text="Upgrade Required"
428: text="Precondition Required"
429: text="Too Many Requests"
431: text="Request Header Fields Too Large"
451: text="Unavailable For Legal Reasons"
# 5xx - Server Error Responses
500: text="Internal Server Error"
501: text="Not Implemented"
502: text="Bad Gateway"
503: text="Service Unavailable"
504: text="Gateway Timeout"
505: text="HTTP Version Not Supported"
506: text="Variant Also Negotiates"
507: text="Insufficient Storage"
508: text="Loop Detected"
510: text="Not Extended"
511: text="Network Authentication Required"
return text