/* * Copyright 2026 Flix Authors * * Use of this source code is governed by the Apache 2.0 license * that can be found in the LICENSE.md file. */////// Type of HTTP requests, and its functions.///pubmod Net.HttpRequest {use Net.Bodyuse Net.HttpLayeruse Net.Methoduse Net.HttpRequestuse Net.HttpRequest.HttpRequestuse Time.Duration////// Type of HTTP requests.////// An HTTP request has a method, a URL, multi-valued headers, and an optional body.///pubenumHttpRequest({ method = Method, url = String, headers = Map[String, List[String]], body = Option[Body], requestTimeout = Option[Duration], connectTimeout = Option[Duration], followRedirects = Bool })// --- Constructors ---////// Creates a GET request to the given `url`.///pubdefget(url: String): HttpRequest = HttpRequest({ method = Method.Get, url = url, headers = Map.empty(), body = None, requestTimeout = None, connectTimeout = None, followRedirects = true })////// Creates a HEAD request to the given `url`.///pubdefhead(url: String): HttpRequest = HttpRequest({ method = Method.Head, url = url, headers = Map.empty(), body = None, requestTimeout = None, connectTimeout = None, followRedirects = true })////// Creates an OPTIONS request to the given `url`.///pubdefoptions(url: String): HttpRequest = HttpRequest({ method = Method.Options, url = url, headers = Map.empty(), body = None, requestTimeout = None, connectTimeout = None, followRedirects = true })////// Creates a POST request to the given `url` with no body.////// Use `postWith` to set a body at construction, or `withBody`/`withBodyBytes` to add one later.///pubdefpost(url: String): HttpRequest = HttpRequest({ method = Method.Post, url = url, headers = Map.empty(), body = None, requestTimeout = None, connectTimeout = None, followRedirects = true })////// Creates a POST request to the given `url` with the given `body`.///pubdefpostWith(url: String, body: String): HttpRequest = HttpRequest({ method = Method.Post, url = url, headers = Map.empty(), body = Some(Body.Text(body)), requestTimeout = None, connectTimeout = None, followRedirects = true })////// Creates a PUT request to the given `url` with no body.////// Use `putWith` to set a body at construction, or `withBody`/`withBodyBytes` to add one later.///pubdefput(url: String): HttpRequest = HttpRequest({ method = Method.Put, url = url, headers = Map.empty(), body = None, requestTimeout = None, connectTimeout = None, followRedirects = true })////// Creates a PUT request to the given `url` with the given `body`.///pubdefputWith(url: String, body: String): HttpRequest = HttpRequest({ method = Method.Put, url = url, headers = Map.empty(), body = Some(Body.Text(body)), requestTimeout = None, connectTimeout = None, followRedirects = true })////// Creates a PATCH request to the given `url` with no body.////// Use `patchWith` to set a body at construction, or `withBody`/`withBodyBytes` to add one later.///pubdefpatch(url: String): HttpRequest = HttpRequest({ method = Method.Patch, url = url, headers = Map.empty(), body = None, requestTimeout = None, connectTimeout = None, followRedirects = true })////// Creates a PATCH request to the given `url` with the given `body`.///pubdefpatchWith(url: String, body: String): HttpRequest = HttpRequest({ method = Method.Patch, url = url, headers = Map.empty(), body = Some(Body.Text(body)), requestTimeout = None, connectTimeout = None, followRedirects = true })////// Creates a DELETE request to the given `url`.///pubdefdelete(url: String): HttpRequest = HttpRequest({ method = Method.Delete, url = url, headers = Map.empty(), body = None, requestTimeout = None, connectTimeout = None, followRedirects = true })// --- Getters ---////// Returns the HTTP method of the request `r`.///pubdefmethod(r: HttpRequest): Method = matchr {case HttpRequest(x) => x#method }////// Returns the URL of the request `r`.///pubdefurl(r: HttpRequest): String = matchr {case HttpRequest(x) => x#url }////// Returns the headers of the request `r`.///pubdefheaders(r: HttpRequest): Map[String, List[String]] = matchr {case HttpRequest(x) => x#headers }////// Returns the text body of the request `r`, or `None` if absent or binary.///pubdefbody(r: HttpRequest): Option[String] = matchr {case HttpRequest(x) => matchx#body {case Some(Body.Text(s)) => Some(s)case_ => None } }////// Returns the binary body of the request `r`, or `None` if absent or text.///pubdefbodyBytes(r: HttpRequest): Option[Vector[Int8]] = matchr {case HttpRequest(x) => matchx#body {case Some(Body.Bytes(v)) => Some(v)case_ => None } }////// Returns the request timeout of the request `r`.///pubdefrequestTimeout(r: HttpRequest): Option[Duration] = matchr {case HttpRequest(x) => x#requestTimeout }////// Returns the connect timeout of the request `r`.///pubdefconnectTimeout(r: HttpRequest): Option[Duration] = matchr {case HttpRequest(x) => x#connectTimeout }////// Returns `true` if the request `r` follows redirects.///pubdeffollowRedirects(r: HttpRequest): Bool = matchr {case HttpRequest(x) => x#followRedirects }// --- Modifiers: Headers ---////// Adds the header `name` with value `value` to the request `r`.////// If the header already exists, the value is appended to the list of values.///pubdefwithHeader(name: String, value: String, r: HttpRequest): HttpRequest =let HttpRequest(x) = r;letcurrent = x#headers;letvalues = matchMap.get(name, current) {case Some(vs) => List.append(vs, value :: Nil)case None => value :: Nil }; HttpRequest({headers = Map.insert(name, values, current) | x})////// Sets the header `name` to `value` on the request `r`, replacing any existing values.///pubdefsetHeader(name: String, value: String, r: HttpRequest): HttpRequest =let HttpRequest(x) = r; HttpRequest({headers = Map.insert(name, value :: Nil, x#headers) | x})////// Merges the given headers `h` into the request `r`.////// For headers that already exist, the new values are appended to the existing values.///pubdefwithHeaders(h: Map[String, List[String]], r: HttpRequest): HttpRequest =let HttpRequest(x) = r;letmerged = Map.foldLeftWithKey( (acc, name, newValues) -> {letexisting = Map.getWithDefault(name, Nil, acc);Map.insert(name, List.append(existing, newValues), acc) },x#headers,h ); HttpRequest({headers = merged | x})////// Sets the `Authorization` header to `Bearer <token>` on the request `r`.///pubdefwithBearerToken(token: String, r: HttpRequest): HttpRequest =setHeader("Authorization", "Bearer ${token}", r)////// Sets the `Content-Type` header to `contentType` on the request `r`.///pubdefwithContentType(contentType: String, r: HttpRequest): HttpRequest =setHeader("Content-Type", contentType, r)////// Sets the `Accept` header to `accept` on the request `r`.///pubdefwithAccept(accept: String, r: HttpRequest): HttpRequest =setHeader("Accept", accept, r)// --- Modifiers: Body ---////// Sets the body of the request `r` to the given text `body`.///pubdefwithBody(body: String, r: HttpRequest): HttpRequest =let HttpRequest(x) = r; HttpRequest({body = Some(Body.Text(body)) | x})////// Sets the body of the request `r` to the given binary `body`.///pubdefwithBodyBytes(body: Vector[Int8], r: HttpRequest): HttpRequest =let HttpRequest(x) = r; HttpRequest({body = Some(Body.Bytes(body)) | x})// --- Modifiers: Query Parameters ---////// Appends a query parameter `name=value` to the URL of the request `r`.////// The name and value are URL-encoded.///pubdefwithQueryParam(name: String, value: String, r: HttpRequest): HttpRequest =let HttpRequest(x) = r;letencodedName = HttpLayer.urlEncode(name);letencodedValue = HttpLayer.urlEncode(value);letseparator = if (String.contains(substr = "?", x#url)) "&"else"?";letnewUrl = "${x#url}${separator}${encodedName}=${encodedValue}"; HttpRequest({url = newUrl | x})////// Appends multiple query parameters to the URL of the request `r`.////// The names and values are URL-encoded.///pubdefwithQueryParams(params: Map[String, String], r: HttpRequest): HttpRequest =Map.foldLeftWithKey((acc, name, value) -> withQueryParam(name, value, acc), r, params)// --- Modifiers: Timeouts & Redirects ---////// Sets the request timeout (time until response headers are received).///pubdefwithTimeout(d: Duration, r: HttpRequest): HttpRequest =let HttpRequest(x) = r; HttpRequest({requestTimeout = Some(d) | x})////// Sets the connect timeout (time to establish the TCP connection).///pubdefwithConnectTimeout(d: Duration, r: HttpRequest): HttpRequest =let HttpRequest(x) = r; HttpRequest({connectTimeout = Some(d) | x})////// Sets whether the request `r` should follow HTTP redirects.///pubdefwithFollowRedirects(follow: Bool, r: HttpRequest): HttpRequest =let HttpRequest(x) = r; HttpRequest({followRedirects = follow | x})}