
Cookies.pudu
Pudu84 lines3.3 KB
1/** @HttpClient.Cookies.Module — a jar of cookies shared by the requests of one transport */2module PuduLangHttpClient.Cookies34import Std.Json as Json5import Std.Time as Time6import PuduLangHttpClient.Content as Content7import PuduLangHttpClient.Domain.Cookie as Cookie8import PuduLangHttpClient.Domain.Uri as Uri9import PuduLangHttpClient as HttpClient10import PuduLangHttpClient.Utils.Shared as Shared1112/** @HttpClient.Cookies.Jar — the cookies stored so far, shared between threads */13export type Jar = { held: Shared.Shared[Array[Cookie.Stored]] }141516export fn jar() -> Jar { Jar{held: Shared.shared([])} }171819export fn store(target: &Jar, uri: Str, values: &Array[Str]) -> () {20 if let Some(endpoint) = Uri.endpointOf(uri) {21 let now = wallClock()22 let path = Uri.split(uri).path23 for value in values {24 if let Some(cookie) = Cookie.parse(value, endpoint.host, path, now) {25 Shared.update(&target.held, |held: Array[Cookie.Stored]| Cookie.store(&held, &cookie, now))26 }27 }28 }29}303132export fn add(target: &Jar, uri: Str, name: Str, value: Str) -> () { store(target, uri, &[name + "=" + value]) }333435export fn cookiesFor(source: &Jar, uri: Str) -> Array[Cookie.Stored] {36 match Uri.endpointOf(uri) {37 case Some(endpoint) => {38 let now = wallClock()39 let path = Uri.split(uri).path40 Shared.current(&source.held).filter(|cookie: Cookie.Stored| Cookie.applies(&cookie, endpoint.host, path, endpoint.secure, now))41 }42 case None => []43 }44}454647export fn header(source: &Jar, uri: Str) -> Option[Str] {48 let applying = cookiesFor(source, uri)49 if applying.isEmpty() { None } else { Some(Cookie.render(&applying)) }50}515253export fn cookies(source: &Jar) -> Array[Cookie.Stored] {54 let now = wallClock()55 Shared.current(&source.held).filter(|cookie: Cookie.Stored| !Cookie.expired(&cookie, now))56}575859export fn clear(target: &Jar) -> () { Shared.update(&target.held, |_held: Array[Cookie.Stored]| []) }606162export fn clearSession(target: &Jar) -> () {63 Shared.update(&target.held, |held: Array[Cookie.Stored]| held.filter(|cookie: Cookie.Stored| Cookie.isPersistent(&cookie)))64}656667export fn save(source: &Jar) -> Str {68 Json.encode(&cookies(source).filter(|cookie: Cookie.Stored| Cookie.isPersistent(&cookie)).encode())69}70717273export fn restore(target: &Jar, saved: Str) -> HttpClient.Outcome[()] {74 let restored: Array[Cookie.Stored] = Content.readAs(&Content.plain(saved)) ?75 let now = wallClock()76 for cookie in restored {77 Shared.update(&target.held, |held: Array[Cookie.Stored]| Cookie.store(&held, &cookie, now))78 }79 Ok(())80}818283fn wallClock() -> Int { Time.toMillis(&Time.currentInstant()) }84