
Redirect.pudu
Pudu39 lines1.7 KB
1/** @Domain.Redirect.Module — which redirects are followed, and how the request changes */2module PuduLangHttpClient.Domain.Redirect34import Std.Http as Http5import PuduLangHttpClient.Domain.Uri as Uri67/** @Domain.Redirect.Hop — the request a followed redirect sends next */8export type Hop = { method: Http.Method, uri: Str, keepsBody: Bool, keepsCredentials: Bool }91011const REDIRECTS: Set[Int] = #{300, 301, 302, 303, 307, 308}121314const TO_GET_AFTER_POST: Set[Int] = #{300, 301, 302}151617export fn isRedirect(code: Int) -> Bool { code in REDIRECTS }1819202122232425export fn next(method: &Http.Method, code: Int, from: Str, location: Str) -> Option[Hop] {26 if !isRedirect(code) || location.trim().isEmpty() { return None }27 let destination = Uri.withoutFragment(Uri.resolve(from, Uri.escape(location.trim())))28 let target = Uri.endpointOf(destination) ?29 let origin = Uri.endpointOf(from) ?30 if origin.secure && !target.secure { return None }31 let changed = (code == 303 && *method != Http.Head) || (*method == Http.Post && code in TO_GET_AFTER_POST)32 Some(Hop {33 method: if changed { Http.Get } else { *method },34 uri: destination,35 keepsBody: !changed,36 keepsCredentials: Uri.origin(&origin) == Uri.origin(&target)37 })38}39