rewrote the HTTP encoding, and missing functions

This commit is contained in:
Simon Cruanes 2013-10-18 23:29:47 +02:00
parent 7313a258b4
commit a860e67443
4 changed files with 92 additions and 70 deletions

View file

@ -4,8 +4,13 @@
#directory "_build/tests/";; #directory "_build/tests/";;
#load "containers.cma";; #load "containers.cma";;
#require "threads";; #require "threads";;
#load "thread_containers.cma";; #load "containers_thread.cma";;
open Containers;;
open Sequence.Infix;; open Sequence.Infix;;
#install_printer Bencode.pretty;; #install_printer Bencode.pretty;;
#require "CamlGI";;
#load "containers_cgi.cma";;
let pp_html fmt h = Format.pp_print_string fmt (ToWeb.HTML.render h);;
#install_printer pp_html;;
(* vim:syntax=ocaml: (* vim:syntax=ocaml:
*) *)

View file

@ -49,7 +49,7 @@ debug="true"
profile="false" profile="false"
native_dynlink="true" native_dynlink="true"
ocamlbuildflags="" ocamlbuildflags=""
cgi="false" cgi="true"
lwt="false" lwt="false"
thread="true" thread="true"
bench="false" bench="false"
@ -58,6 +58,7 @@ tests="false"
ocaml_version_ge_4_00_1="4.00.1" ocaml_version_ge_4_00_1="4.00.1"
pkg_unix="/usr/lib/ocaml" pkg_unix="/usr/lib/ocaml"
pkg_threads="/usr/lib/ocaml" pkg_threads="/usr/lib/ocaml"
pkg_camlgi="/home/simon/.opam/system/lib/CamlGI"
ocamldoc="/usr/bin/ocamldoc" ocamldoc="/usr/bin/ocamldoc"
pkg_bench="/home/simon/.opam/system/lib/bench" pkg_bench="/home/simon/.opam/system/lib/bench"
pkg_ounit="/home/simon/.opam/system/lib/oUnit" pkg_ounit="/home/simon/.opam/system/lib/oUnit"

139
toWeb.ml
View file

@ -44,6 +44,7 @@ module HTML = struct
| H of int * t | H of int * t
| Link of link | Link of link
| Tag of string * t | Tag of string * t
| TagWith of string * (string * string) list * t
and url = { and url = {
url_alt : string option; url_alt : string option;
url_url : string; url_url : string;
@ -62,16 +63,19 @@ module HTML = struct
let bprintf format = let bprintf format =
let buffer = Buffer.create 64 in let buffer = Buffer.create 64 in
let r = ref (str "") in
Printf.kbprintf Printf.kbprintf
(fun fmt -> str (Buffer.contents buffer)) (fun x -> r := str (Buffer.contents buffer))
buffer buffer
format format;
!r
let bprintf format = let sprintf format =
let buffer = Buffer.create 64 in let r = ref (str "") in
Printf.ksprintf Printf.ksprintf
str (fun s -> r := str s)
format format;
!r
let list l = List l let list l = List l
@ -97,6 +101,22 @@ module HTML = struct
let h3 x = H (3, x) let h3 x = H (3, x)
let h n x = H (n, x) let h n x = H (n, x)
let p x = Tag ("p", x)
let div ?id ?class_ x =
match id, class_ with
| None, None -> Tag ("div", x)
| Some i, None -> TagWith ("div", ["id", i], x)
| None, Some c -> TagWith ("div", ["class", c], x)
| Some i, Some c -> TagWith ("div", ["id", i; "class", c], x)
let span ?id ?class_ x =
match id, class_ with
| None, None -> Tag ("span", x)
| Some i, None -> TagWith ("span", ["id", i], x)
| None, Some c -> TagWith ("span", ["class", c], x)
| Some i, Some c -> TagWith ("span", ["id", i; "class", c], x)
let link ~rel ~url = Link { let link ~rel ~url = Link {
link_rel = rel; link_rel = rel;
@ -109,69 +129,39 @@ module HTML = struct
let html x = Tag ("html", x) let html x = Tag ("html", x)
(* XXX UGLY: I copied code from CamlGI because it doesn't provide a way let _to_hex n = match n with
to encode strings with HTML conventions. Bad bad bad. *) | _ when n >= 0 && n < 10 -> Char.chr (Char.code '0' + n)
module Encode = struct | 10 -> 'A'
(* Use a table lookup for speed. *) | 11 -> 'B'
let char_of_hex = | 12 -> 'C'
let hex = [| '0'; '1'; '2'; '3'; '4'; '5'; '6'; '7'; '8'; '9'; | 13 -> 'D'
'A'; 'B'; 'C'; 'D'; 'E'; 'F' |] in | 14 -> 'E'
fun i -> Array.unsafe_get hex i | 15 -> 'F'
| _ -> failwith "not an hexadecimal digit"
let _encode_char buf c =
Buffer.add_string buf "&#x";
let h, l = Char.code c / 16, Char.code c mod 16 in
Buffer.add_char buf (_to_hex h);
Buffer.add_char buf (_to_hex l)
let encode_wrt is_special s0 = let encode str =
let len = String.length s0 in let b = Buffer.create (String.length str + 10) in
let encoded_length = ref len in for i = 0 to String.length str - 1 do
for i = 0 to len - 1 do match str.[i] with
if is_special(String.unsafe_get s0 i) then | ';' | '/' | '?' | ':' | '@' | '&' | '=' | '+' | '$' | ',' | '<'
encoded_length := !encoded_length + 2 | '>' | '#' | '%' | '"' | '{' | '}' | '|' | '\\' | '^' | '[' | ']'
done; | '`' -> _encode_char b str.[i]
let s = String.create !encoded_length in | c when Char.code c < 32 -> _encode_char b str.[i]
let rec do_enc i0 i = (* copy the encoded string in s *) | c when Char.code c > 127 -> _encode_char b str.[i]
if i0 < len then begin | _ -> Buffer.add_char b str.[i]
let s0i0 = String.unsafe_get s0 i0 in done;
if is_special s0i0 then begin Buffer.contents b
let c = Char.code s0i0 in
let i1 = succ i in
let i2 = succ i1 in
String.unsafe_set s i '%';
String.unsafe_set s i1 (char_of_hex (c lsr 4));
String.unsafe_set s i2 (char_of_hex (c land 0x0F));
do_enc (succ i0) (succ i2)
end
else if s0i0 = ' ' then begin
String.unsafe_set s i '+';
do_enc (succ i0) (succ i)
end
else begin
String.unsafe_set s i s0i0;
do_enc (succ i0) (succ i)
end
end in
do_enc 0 0;
s
(* Unreserved characters consist of all alphanumeric chars and the
following limited set of punctuation marks and symbols: '-' | '_' |
'.' | '!' | '~' | '*' | '\'' | '(' | ')'. According to RFC 2396,
they should not be escaped unless the context requires it. *)
let special_rfc2396 = function
| ';' | '/' | '?' | ':' | '@' | '&' | '=' | '+' | '$' | ',' (* Reserved *)
| '\000' .. '\031' | '\127' .. '\255' (* Control chars and non-ASCII *)
| '<' | '>' | '#' | '%' | '"' (* delimiters *)
| '{' | '}' | '|' | '\\' | '^' | '[' | ']' | '`' (* unwise *)
-> true
| _ -> false
(* ' ' must also be encoded but its encoding '+' takes a single char. *)
let encode = encode_wrt special_rfc2396
end
(* real rendering is always into a buffer (for now) *) (* real rendering is always into a buffer (for now) *)
let rec to_buf buf x = let rec to_buf buf x =
match x with match x with
| Str s -> Buffer.add_string buf (Encode.encode s) | Str s -> Buffer.add_string buf (encode s)
| List l -> | List l ->
Buffer.add_string buf "<ul>"; Buffer.add_string buf "<ul>";
List.iter List.iter
@ -182,17 +172,26 @@ module HTML = struct
begin match url.url_alt with begin match url.url_alt with
| None -> | None ->
Printf.bprintf buf "<a href=\"%s\">%s</a>" url.url_url Printf.bprintf buf "<a href=\"%s\">%s</a>" url.url_url
(Encode.encode url.url_descr) (encode url.url_descr)
| Some alt -> | Some alt ->
Printf.bprintf buf "<a href=\"%s\" alt=\"%s\">%s</a>" Printf.bprintf buf "<a href=\"%s\" alt=\"%s\">%s</a>"
url.url_url (Encode.encode alt) (Encode.encode url.url_descr) url.url_url (encode alt) (encode url.url_descr)
end end
| Img i -> failwith "img: not implemented" | Img i -> failwith "img: not implemented"
| Concat l -> List.iter (to_buf buf) l | Concat l ->
List.iteri
(fun i y ->
if i > 0 then Buffer.add_char buf ' ';
to_buf buf y)
l
| H (n, y) -> | H (n, y) ->
Printf.bprintf buf "<h%i> %a </h%i>" n to_buf y n Printf.bprintf buf "<h%i> %a </h%i>" n to_buf y n
| Link _ -> failwith "link: not implemented" | Link _ -> failwith "link: not implemented"
| Tag (str, y) -> Printf.bprintf buf "<%s> %a </%s>" str to_buf y str | Tag (str, y) -> Printf.bprintf buf "<%s> %a </%s>" str to_buf y str
| TagWith (str, attrs, y) ->
Printf.bprintf buf "<%s " str;
List.iter (fun (name,attr) -> Printf.bprintf buf "%s=\"%s\"" name attr) attrs;
Printf.bprintf buf "> %a </%s>" to_buf y str
let render x = let render x =
let buf = Buffer.create 256 in let buf = Buffer.create 256 in
@ -339,3 +338,11 @@ module Router = struct
match st with match st with
| State.Wrap st -> State.handle_request st req | State.Wrap st -> State.handle_request st req
end end
(** {2 Main Interface} *)
let serve_state ?sockaddr st =
CamlGI.Cgi.register_script ?sockaddr (State.handle_request st)
let serve_router ?sockaddr router =
CamlGI.Cgi.register_script ?sockaddr (Router.handle_request router)

View file

@ -41,10 +41,10 @@ module HTML : sig
val str : string -> t val str : string -> t
(** Simple string *) (** Simple string *)
val bprintf : ('a, Buffer.t, unit, string) format4 -> t val bprintf : ('a, Buffer.t, unit, unit) format4 -> t
(** Use a buffer printer to render a string. Shortcut for {!str} *) (** Use a buffer printer to render a string. Shortcut for {!str} *)
val sprintf : ('a, unit, string) format -> t val sprintf : ('a, unit, string, unit) format4 -> t
(** Use a string printer to render into a string. Shortcut for {!str} *) (** Use a string printer to render into a string. Shortcut for {!str} *)
val list : t list -> t val list : t list -> t
@ -71,6 +71,15 @@ module HTML : sig
val h : int -> t -> t val h : int -> t -> t
(** Title of level parametrized by the integer *) (** Title of level parametrized by the integer *)
val p : t -> t
(** Paragraph *)
val div : ?id:string -> ?class_:string -> t -> t
(** Div tag, to specify a block *)
val span : ?id:string -> ?class_:string -> t -> t
(** Non semantic tag, mostly useful for CSS *)
val link : rel:string -> url:string -> t val link : rel:string -> url:string -> t
(** Link (for head) *) (** Link (for head) *)