///|
/// Split a raw HTTP request-target into its path and query string.
///
/// Routes match against the path; the query is retained separately on the
/// `HttpRequest`. A `#fragment` is never used for routing, so it is stripped
/// from the query rather than exposed.
fn split_request_target(target : String) -> (String, String) {
let target = match target.find("#") {
Some(fragment) => target[:fragment].to_owned()
None => target
}
match target.find("?") {
Some(query) => (target[:query].to_owned(), target[query + 1:].to_owned())
None => (target, "")
}
}
///|
/// Whether a request carries a body: POST/PUT/PATCH always may, and any
/// method with an explicit (non-empty) `content-length` or a
/// `transfer-encoding` (i.e. chunked) frame is treated as having a body.
/// The same rule is used by every backend so bodies are read consistently.
fn request_has_body(
http_method : String,
headers : Map[@http.CaseInsensitiveString, StringView],
) -> Bool {
match http_method {
"POST" | "PUT" | "PATCH" => true
_ =>
headers.get("transfer-encoding") is Some(_) ||
headers
.get("content-length")
.map(value => value.to_owned().trim() != "0")
.unwrap_or(false)
}
}
///|
pub async fn dispatch_http(
mocket : App,
http_method : String,
url : String,
headers : Map[@http.CaseInsensitiveString, StringView],
body : Bytes,
) -> HttpResponse {
let (path, query) = split_request_target(url)
let (params, handler) = match mocket.find_route(http_method, path) {
Some((h, p)) => (p, h)
_ => ({}, handle_not_found())
}
let event = {
req: {
http_method,
url: path,
query,
headers,
reader: @io.MemoryReader(writer => writer.write(body)),
},
res: HttpResponse(OK),
params,
}
let responder = mocket.execute_middlewares(event, handler) catch {
err => mocket.handle_request_error(event, err)
}
responder.options(event.res)
event.res.body = Some(responder)
event.res
}