Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Tiny_httpd_util.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
(* test utils *) (*$inject let pp_res f = function Ok x -> f x | Error e -> e let pp_res_query = (Q.Print.(pp_res (list (pair string string)))) let err_map f = function Ok x-> Ok (f x) | Error e -> Error e let sort_l l = List.sort compare l let eq_sorted a b = (=) (err_map sort_l a)(err_map sort_l b) let is_ascii_char c = Char.code c < 128 *) let percent_encode ?(skip=fun _->false) s = let buf = Buffer.create (String.length s) in String.iter (function | c when skip c -> Buffer.add_char buf c | (' ' | '!' | '"' | '#' | '$' | '%' | '&' | '\'' | '(' | ')' | '*' | '+' | ',' | '/' | ':' | ';' | '=' | '?' | '@' | '[' | ']' | '~') as c -> Printf.bprintf buf "%%%X" (Char.code c) | c when Char.code c > 127 -> Printf.bprintf buf "%%%X" (Char.code c) | c -> Buffer.add_char buf c) s; Buffer.contents buf (*$= & ~printer:(fun s->s) "hello%20world" (percent_encode "hello world") "%23%25^%24%40^%40" (percent_encode "#%^$@^@") "a%20ohm%2B5235%25%26%40%23%20---%20_" (percent_encode "a ohm+5235%&@# --- _") *) (*$= & ~printer:Q.(Print.(option string)) (Some "?") (percent_decode @@ percent_encode "?") *) let hex_int (s:string) : int = Scanf.sscanf s "%x" (fun x->x) let percent_decode (s:string) : _ option = let buf = Buffer.create (String.length s) in let i = ref 0 in try while !i < String.length s do match String.get s !i with | '%' -> if !i+2 < String.length s then ( begin match hex_int @@ String.sub s (!i+1) 2 with | n -> Buffer.add_char buf (Char.chr n) | exception _ -> raise Exit end; i := !i + 3; ) else ( raise Exit (* truncated *) ) | '+' -> Buffer.add_char buf ' '; incr i (* for query strings *) | c -> Buffer.add_char buf c; incr i done; Some (Buffer.contents buf) with Exit -> None (*$QR & ~count:1_000 ~long_factor:20 Q.string (fun s -> String.iter (fun c -> Q.assume @@ is_ascii_char c) s; match percent_decode (percent_encode s) with | Some s' -> s=s' | None -> Q.Test.fail_report "invalid percent encoding") *) exception Invalid_query let find_q_index_ s = String.index s '?' let get_non_query_path s = match find_q_index_ s with | i -> String.sub s 0 i | exception Not_found -> s let get_query s : string = match find_q_index_ s with | i -> String.sub s (i+1) (String.length s-i-1) | exception Not_found -> "" let split_query s = get_non_query_path s, get_query s let split_on_slash s : _ list = let l = ref [] in let i = ref 0 in let n = String.length s in while !i < n do match String.index_from s !i '/' with | exception Not_found -> if !i < n then ( (* last component *) l := String.sub s !i (n - !i) :: !l; ); i := n (* done *) | j -> if j > !i then ( l := String.sub s !i (j - !i) :: !l; ); i := j+1; done; List.rev !l (*$= & ~printer:Q.Print.(list string) ["a"; "b"] (split_on_slash "/a/b") ["coucou"; "lol"] (split_on_slash "/coucou/lol") ["a"; "b"; "c"] (split_on_slash "/a/b//c/") ["a"; "b"] (split_on_slash "//a/b/") ["a"] (split_on_slash "/a//") [] (split_on_slash "/") [] (split_on_slash "//") *) let parse_query s : (_ list, string) result= let pairs = ref [] in let is_sep_ = function '&' | ';' -> true | _ -> false in let i = ref 0 in let j = ref 0 in try let percent_decode s = match percent_decode s with Some x -> x | None -> raise Invalid_query in let parse_pair () = let eq = String.index_from s !i '=' in let k = percent_decode @@ String.sub s !i (eq- !i) in let v = percent_decode @@ String.sub s (eq+1) (!j-eq-1) in pairs := (k,v) :: !pairs; in while !i < String.length s do while !j < String.length s && not (is_sep_ (String.get s !j)) do incr j done; if !j < String.length s then ( assert (is_sep_ (String.get s !j)); parse_pair(); i := !j+1; j := !i; ) else ( parse_pair(); i := String.length s; (* done *) ) done; Ok !pairs with | Invalid_argument _ | Not_found | Failure _ -> Error (Printf.sprintf "error in parse_query for %S: i=%d,j=%d" s !i !j) | Invalid_query -> Error ("invalid query string: " ^ s) (*$= & ~printer:pp_res_query ~cmp:eq_sorted (Ok ["a", "b"; "c", "d"]) (parse_query "a=b&c=d") *) (*$QR & ~long_factor:20 ~count:1_000 Q.(small_list (pair string string)) (fun l -> List.iter (fun (a,b) -> Q.assume (a<>"" && b<>"" ); String.iter (fun c -> Q.assume @@ is_ascii_char c) a; String.iter (fun c -> Q.assume @@ is_ascii_char c) b; ) l; let s = String.concat "&" (List.map (fun (x,y) -> percent_encode x ^"="^percent_encode y) l) in eq_sorted (Ok l) (parse_query s)) *)