///|
/// An outgoing HTTP response containing the status code, headers, cookies, and body.
pub(all) struct HttpResponse {
mut status_code : StatusCode
headers : Map[String, String]
cookies : Map[String, CookieItem]
mut raw_body : Bytes
fn new(
status_code : StatusCode,
headers? : Map[String, String],
cookies? : Map[String, CookieItem],
raw_body? : Bytes,
) -> HttpResponse
}
///|
/// Deserializes the response body into a value of type `T` via the `BodyReader` trait.
pub fn[T : BodyReader] HttpResponse::read_body(self : HttpResponse) -> T raise {
T::from_request(
HttpRequest(Other(""), "", self.headers, raw_body=self.raw_body),
)
}
///|
/// Creates a new `HttpResponse` with the given status code and optional headers, cookies, and body.
pub fn HttpResponse::new(
status_code : StatusCode,
headers? : Map[String, String],
cookies? : Map[String, CookieItem],
raw_body? : Bytes,
) -> HttpResponse {
{
status_code,
headers: headers.unwrap_or({}),
cookies: cookies.unwrap_or({}),
raw_body: raw_body.unwrap_or(""),
}
}
///|
/// Sets the response body from any `Responder` and returns the response for chaining.
///
/// Applies the responder's `options()` (e.g. sets Content-Type) and uses the
/// `output_bytes()` fast path when available to avoid an intermediate buffer copy.
pub fn HttpResponse::body(
self : HttpResponse,
body : &Responder,
) -> HttpResponse {
body.options(self)
match body.output_bytes() {
Some(bytes) => self.raw_body = bytes
None => {
let buf = @buffer.new()
body.output(buf)
self.raw_body = buf.to_bytes()
}
}
self
}
///|
/// Sets the response body to the JSON representation of the given value.
/// Also sets `Content-Type: application/json; charset=utf-8`.
pub fn HttpResponse::json(self : HttpResponse, obj : &ToJson) -> HttpResponse {
self.body(obj.to_json())
}
///|
/// Serializes a typed value as JSON, sets `Content-Type`, and returns the response.
///
/// Uses case-insensitive header matching: any existing `content-type` /
/// `CONTENT-TYPE` header is replaced, not duplicated.
pub fn[T : ToJson] HttpResponse::json_value(
self : HttpResponse,
value : T,
) -> HttpResponse {
@mhttp.set_header_case_insensitive(
self.headers,
"Content-Type",
"application/json; charset=utf-8",
)
let json_str = value.to_json().stringify()
self.raw_body = @utf8.encode(json_str)
self
}
///|
/// Sets a response header and returns self for fluent chaining.
///
/// Uses case-insensitive matching: setting `Content-Type` will replace any
/// existing `content-type` or `CONTENT-TYPE` header.
pub fn HttpResponse::header(
self : HttpResponse,
name : String,
value : String,
) -> HttpResponse {
@mhttp.set_header_case_insensitive(self.headers, name, value)
self
}
///|
/// Converts this response into a `Responder` trait object for use as a handler return value.
pub fn HttpResponse::to_responder(self : HttpResponse) -> &Responder {
self
}
///|
test "read_response_body" {
let res = HttpResponse(OK, raw_body=b"{\"Hello\":\"Response\"}")
let text : String = res.read_body()
let json : Json = res.read_body()
inspect(
text,
content=(
#|{"Hello":"Response"}
),
)
json_inspect(json, content={ "Hello": "Response" })
}