carlos / todomvc-bakeoff Public

Clone
git clone https://amadan.net/carlos/todomvc-bakeoff

Plain git — no account needed to clone.

Download

Download this file

1(* application/x-www-form-urlencoded bodies and Cookie headers. *)
2
3let hex c =
4 match c with
5 | '0' .. '9' -> Some (Char.code c - 48)
6 | 'a' .. 'f' -> Some (Char.code c - 87)
7 | 'A' .. 'F' -> Some (Char.code c - 55)
8 | _ -> None
9
10(* Decodes '+' as a space and %XX as a byte. A malformed escape is kept
11 as it is, the way browsers treat it. *)
12let decode s =
13 let n = String.length s in
14 let b = Buffer.create n in
15 let rec go i =
16 if i < n then
17 match s.[i] with
18 | '+' -> Buffer.add_char b ' '; go (i + 1)
19 | '%' when i + 2 < n -> (
20 match (hex s.[i + 1], hex s.[i + 2]) with
21 | Some h, Some l -> Buffer.add_char b (Char.chr ((h lsl 4) lor l)); go (i + 3)
22 | _ -> Buffer.add_char b '%'; go (i + 1))
23 | c -> Buffer.add_char b c; go (i + 1)
24 in
25 go 0;
26 Buffer.contents b
27
28let parse body =
29 String.split_on_char '&' body
30 |> List.filter_map (fun pair ->
31 if pair = "" then None
32 else
33 match String.index_opt pair '=' with
34 | Some i ->
35 Some (decode (String.sub pair 0 i),
36 decode (String.sub pair (i + 1) (String.length pair - i - 1)))
37 | None -> Some (decode pair, ""))
38
39(* The value of the first cookie called [name] in a Cookie header. *)
40let cookie header name =
41 String.split_on_char ';' header
42 |> List.find_map (fun part ->
43 match String.index_opt part '=' with
44 | Some i when String.trim (String.sub part 0 i) = name ->
45 Some (String.trim (String.sub part (i + 1) (String.length part - i - 1)))
46 | _ -> None)
47
48(* Unicode White_Space, plus U+FEFF (byte order mark), the set JavaScript's
49 String.prototype.trim removes. *)
50let is_space u =
51 match Uchar.to_int u with
52 | 0x09 | 0x0A | 0x0B | 0x0C | 0x0D | 0x20 | 0x85 | 0xA0 | 0x1680 | 0x2028 | 0x2029 | 0x202F
53 | 0x205F | 0x3000 | 0xFEFF ->
54 true
55 | n -> n >= 0x2000 && n <= 0x200A
56
57(* Removes leading and trailing whitespace by that definition, so a title
58 of only non-breaking spaces trims to nothing. [s] is valid UTF-8. *)
59let trim s =
60 let n = String.length s in
61 let rec go i first last =
62 if i >= n then (first, last)
63 else
64 let d = String.get_utf_8_uchar s i in
65 let next = i + Uchar.utf_decode_length d in
66 if is_space (Uchar.utf_decode_uchar d) then go next first last
67 else go next (if first < 0 then i else first) next
68 in
69 match go 0 (-1) 0 with
70 | -1, _ -> ""
71 | first, last -> String.sub s first (last - first)
72