Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-httpclient

Named HTTP clients for Pudu: a client factory, delegating handlers, pooled keep-alive connections, handler lifetimes, logging, and resilience

0.1.1Apache-2.01

InstallClose

Headers.pudu

Pudu100 lines3.6 KB

GitHub ↗
1/** @Domain.Headers.Module — header lists compared without regard to case */2module PuduLangHttpClient.Domain.Headers34import Std.Http.Safe as Safe5import Std.Result as Result67/// The headers that describe a body rather than the message carrying it.8const CONTENT_HEADERS: Set[Str] = #{9  "allow",10  "content-disposition",11  "content-encoding",12  "content-language",13  "content-length",14  "content-location",15  "content-md5",16  "content-range",17  "content-type",18  "expires",19  "last-modified"20}2122/// The value written in place of a redacted header value.23export const REDACTED: Str = "*"2425/// The first value given for a header.26export fn get(headers: &Array[(Str, Str)], name: Str) -> Option[Str] {27  let wanted = name.toLower()28  for pair in headers {29    if pair[0].toLower() == wanted { return Some(pair[1]) }30  }31  None32}3334/// Every value given for a header, in order.35export fn values(headers: &Array[(Str, Str)], name: Str) -> Array[Str] {36  let wanted = name.toLower()37  headers.filter(|pair: (Str, Str)| pair[0].toLower() == wanted).map(|pair: (Str, Str)| pair[1])38}3940/// Whether a header is present.41export fn has(headers: &Array[(Str, Str)], name: Str) -> Bool {42  let wanted = name.toLower()43  for pair in headers {44    if pair[0].toLower() == wanted { return true }45  }46  false47}4849/// The headers with one value added after any the name already has.50export fn add(headers: &Array[(Str, Str)], name: Str, value: Str) -> Array[(Str, Str)] { headers.push((name, value)) }5152/// The headers with every value of the name replaced by one.53export fn set(headers: &Array[(Str, Str)], name: Str, value: Str) -> Array[(Str, Str)] { remove(headers, name).push((name, value)) }5455/// The headers without any value of the name.56export fn remove(headers: &Array[(Str, Str)], name: Str) -> Array[(Str, Str)] {57  let wanted = name.toLower()58  headers.filter(|pair: (Str, Str)| pair[0].toLower() != wanted)59}6061/// The headers without any value of the names.62export fn removeAll(headers: &Array[(Str, Str)], names: &Set[Str]) -> Array[(Str, Str)] {63  headers.filter(|pair: (Str, Str)| !(pair[0].toLower() in *names))64}6566/// The defaults whose names the message does not already carry, followed by the message's own.67export fn merged(defaults: &Array[(Str, Str)], own: &Array[(Str, Str)]) -> Array[(Str, Str)] {68  defaults.filter(|pair: (Str, Str)| !has(own, pair[0])).concat(*own)69}7071/// The headers with the value of every name `redact` holds for replaced by `REDACTED`.72export fn redacted(headers: &Array[(Str, Str)], redact: fn(Str) -> Bool) -> Array[(Str, Str)] {73  headers.map(|pair: (Str, Str)| if redact(pair[0].toLower()) { (pair[0], REDACTED) } else { pair })74}7576/// Whether a header describes a body rather than the message carrying it.77export fn isContentHeader(name: Str) -> Bool { name.toLower() in CONTENT_HEADERS }7879/// The headers split into the message's own and the body's.80export fn splitContent(headers: &Array[(Str, Str)]) -> (Array[(Str, Str)], Array[(Str, Str)]) {81  (headers.filter(|pair: (Str, Str)| !isContentHeader(pair[0])), headers.filter(|pair: (Str, Str)| isContentHeader(pair[0])))82}8384/// The first header whose name or value cannot be written without breaking the message.85export fn unwritable(headers: &Array[(Str, Str)]) -> Option[Str] {86  for pair in headers {87    if pair[0].isEmpty() || pair[0].contains(":") || Result.isErr(&Safe.headerWritable(pair[0], pair[1])) { return Some(pair[0]) }88  }89  None90}9192/// Whether a comma-separated header value lists a token, compared without regard to case.93export fn lists(value: Str, token: Str) -> Bool {94  let wanted = token.toLower()95  for piece in value.split(",") {96    if piece.trim().toLower() == wanted { return true }97  }98  false99}100