## 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 response’s 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