package tiny_libs
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
From-scratch libraries for teaching: graphics, audio, compression, crypto, networking and more
Install
dune-project
Dependency
Authors
Maintainers
Sources
0.3.6.tar.gz
md5=7c636383d146d30ac6f2fa234a6253c8
sha512=c79f3823c5f8f57e5038eb640d487c61168b84aa07c61999d6622ef9fd0c890e2b03b4c6a7cdbbe9352a49e25dda00ac7bb14693cee8e3d7beeed251351a2af0
doc/src/tiny_libs.networking_protocols/Url.ml.html
Source file Url.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176(* Claude Code * * Copyright (C) 2026 Yoann Padioleau * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * (LGPL) as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. *) (* See Url.mli *) type t = { scheme : string option; authority : authority option; path : string; query : string option; fragment : string option; } (*****************************************************************************) (* Parsing: appendix B's regexp, by hand *) (*****************************************************************************) (* the index of the first character of [s] at or after [from] that is * in [stops], or the length of [s] *) let index_of_any (s : string) (from : int) (stops : string) : int = let rec go i = if i >= String.length s || String.contains stops s.[i] then i else go (i + 1) in go from let starts_with ~prefix s = String.starts_with ~prefix s let rest (s : string) (from : int) : string = String.sub s from (String.length s - from) (* userinfo@host:port, section 3.2; the host an IP literal in brackets * ("[::1]") may hold ':'s, so the port is after the ']' *) let (s : string) : (authority, string) result = let userinfo, hostport = match String.index_opt s '@' with | Some i -> (Some (String.sub s 0 i), rest s (i + 1)) | None -> (None, s) in let colon = if starts_with ~prefix:"[" hostport then match String.index_opt hostport ']' with | Some j when j + 1 < String.length hostport && hostport.[j + 1] = ':' -> Some (j + 1) | _ -> None else String.rindex_opt hostport ':' in match colon with | None -> Ok { userinfo; host = String.lowercase_ascii hostport; port = None } | Some i -> ( let host = String.lowercase_ascii (String.sub hostport 0 i) in match rest hostport (i + 1) with | "" -> Ok { userinfo; host; port = None } (* "http://a:/", allowed, the default port *) | digits when String.for_all (fun c -> c >= '0' && c <= '9') digits && String.length digits <= 5 -> Ok { userinfo; host; port = Some (int_of_string digits) } | bad -> Error (Printf.sprintf "Url.parse: bad port %S" bad)) let parse (s : string) : (t, string) result = (* {|^(([^:/?#]+):)?|} *) let scheme, i = let j = index_of_any s 0 ":/?#" in if j > 0 && j < String.length s && s.[j] = ':' then (Some (String.lowercase_ascii (String.sub s 0 j)), j + 1) else (None, 0) in (* {|(//([^/?#]*))?|} *) let , i = if starts_with ~prefix:"//" (rest s i) then let j = index_of_any s (i + 2) "/?#" in (Some (String.sub s (i + 2) (j - i - 2)), j) else (None, i) in (* {|([^?#]*)|} *) let j = index_of_any s i "?#" in let path = String.sub s i (j - i) in (* {|(\?([^#]*))?|} *) let query, i = if j < String.length s && s.[j] = '?' then let k = index_of_any s (j + 1) "#" in (Some (String.sub s (j + 1) (k - j - 1)), k) else (None, j) in (* {|(#(.*))?|} *) let fragment = if i < String.length s then Some (rest s (i + 1)) else None in match authority with | None -> Ok { scheme; authority = None; path; query; fragment } | Some a -> ( match parse_authority a with | Ok a -> Ok { scheme; authority = Some a; path; query; fragment } | Error e -> Error e) (*****************************************************************************) (* Recomposition, section 5.3 *) (*****************************************************************************) let (a : authority) : string = (match a.userinfo with Some u -> u ^ "@" | None -> "") ^ a.host ^ match a.port with Some p -> ":" ^ string_of_int p | None -> "" let to_string (u : t) : string = let b = Buffer.create 64 in Option.iter (fun s -> Buffer.add_string b (s ^ ":")) u.scheme; Option.iter (fun a -> Buffer.add_string b ("//" ^ authority_to_string a)) u.authority; Buffer.add_string b u.path; Option.iter (fun q -> Buffer.add_string b ("?" ^ q)) u.query; Option.iter (fun f -> Buffer.add_string b ("#" ^ f)) u.fragment; Buffer.contents b (*****************************************************************************) (* Resolving a relative reference, section 5.2 *) (*****************************************************************************) (* the output's last segment and the '/' before it removed: "/a/b" -> "/a" *) let drop_last_segment (out : string) : string = match String.rindex_opt out '/' with Some i -> String.sub out 0 i | None -> "" (* section 5.2.4, its steps A to E: the input consumed from the left, * a segment at a time, into the output *) let remove_dot_segments (path : string) : string = let rec go input out = if input = "" then out (* A: a leading "../" or "./" dropped *) else if starts_with ~prefix:"../" input then go (rest input 3) out else if starts_with ~prefix:"./" input then go (rest input 2) out (* B: "/./" or a final "/." becomes "/" *) else if starts_with ~prefix:"/./" input then go (rest input 2) out else if input = "/." then go "/" out (* C: "/../" or a final "/.." becomes "/", and the output's last * segment goes *) else if starts_with ~prefix:"/../" input then go (rest input 3) (drop_last_segment out) else if input = "/.." then go "/" (drop_last_segment out) (* D: a lone "." or ".." dropped *) else if input = "." || input = ".." then out (* E: the first segment, with its leading '/' if any, moved to the * output *) else let j = index_of_any input (if input.[0] = '/' then 1 else 0) "/" in go (rest input j) (out ^ String.sub input 0 j) in go path "" (* section 5.2.3: the base's path up to its last '/', then the * reference's (and "/" for a base with an authority and no path) *) let merge (base : t) (ref_path : string) : string = if base.authority <> None && base.path = "" then "/" ^ ref_path else match String.rindex_opt base.path '/' with | Some i -> String.sub base.path 0 (i + 1) ^ ref_path | None -> ref_path (* section 5.2.2, its pseudo-code transcribed branch by branch *) let resolve (base : t) (r : t) : t = if r.scheme <> None then { r with path = remove_dot_segments r.path } else if r.authority <> None then { r with scheme = base.scheme; path = remove_dot_segments r.path } else let path, query = if r.path = "" then (base.path, if r.query <> None then r.query else base.query) else if starts_with ~prefix:"/" r.path then (remove_dot_segments r.path, r.query) else (remove_dot_segments (merge base r.path), r.query) in { scheme = base.scheme; authority = base.authority; path; query; fragment = r.fragment } (*****************************************************************************) (* For HTTP *) (*****************************************************************************) let port (u : t) : int option = match u.authority with | Some { port = Some p; _ } -> Some p | _ -> ( match u.scheme with Some "http" -> Some 80 | Some "https" -> Some 443 | _ -> None) let request_target (u : t) : string = (if u.path = "" then "/" else u.path) ^ match u.query with Some q -> "?" ^ q | None -> ""
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>