type page = Request.req -> (string -> unit) -> unit
type map = ((unit -> page) * page ref) StringMap.t
let chunked_response f o =
output_string o "Transfer-encoding: chunked\r\n\r\n";
flush o;
let ch = Chunked.channel o Server.bufsize in
f (Chunked.puts ch);
Chunked.finish ch;
(* footers go here *)
output_string o "\r\n"
let buffered_response f o =
let buf = Buffer.create Server.bufsize in
f (Buffer.add_string buf);
Printf.fprintf o "Content-length: %d\r\n" (Buffer.length buf);
(* headers go here *)
output_string o "\r\n";
Buffer.output_buffer o buf
let choose_response req =
if Request.version req < 1.1 then buffered_response
else chunked_response
let headers o req st =
Printf.fprintf o
"HTTP/1.1 %d %s\r\n\
Server: MetaOCaml/%s\r\n\
Connection: %s\r\n\
Date: %s\r\n\
Content-type: text/html\r\n"
(Status.code st) (Status.text st)
Sys.ocaml_version
(Request.keep_alive req)
(TimeStamp.now())
let mutex = Mutex.create()
(* NEED A WAY FOR script function to send additional headers *)
(* would be nice to support HEAD *)
let run map req o =
(match Request.meth req with
Request.GET ->
let _, code = StringMap.find (Request.uri req) map in
let fn puts =
try
(*Mutex.lock mutex;
let code = .!code in
Mutex.unlock mutex;*)
!code req puts
with e ->
Printf.kprintf puts
"Unhandled exception: %s" (Printexc.to_string e) in
headers o req Status.Ok;
choose_response req fn o;
Status.Ok
| _ -> raise Server.Not_implemented)
(* This one is for already-compiled code (not using META features) *)
let runc map req o =
(match Request.meth req with
Request.GET ->
let handler = StringMap.find (Request.uri req) map in
let fn puts =
try handler req puts
with e ->
Printf.kprintf puts
"Unhandled exception: %s" (Printexc.to_string e) in
headers o req Status.Ok;
choose_response req fn o;
Status.Ok
| _ -> raise Server.Not_implemented)
(* Here is a separate handler that redirects if adding a slash
to the uri would cause a match in the code_map, i.e.,
"/dir" --> "/dir/"
*)
let redirect map req o =
(match Request.meth req with
Request.GET ->
let uri' = Request.uri req ^ "/" in
let _ = StringMap.find uri' map in
let url = "http://" in
let url = url^Unix.gethostname() in
let url =
match Unix.getsockname (Unix.descr_of_out_channel o) with
Unix.ADDR_INET(_,p) -> Printf.sprintf "%s:%d" url p
| _ -> url (* bogus anyway *) in
let url = url^uri' in
let page puts =
Printf.kprintf puts "Go here.\n" url in
headers o req Status.Moved_permanently;
Printf.fprintf o "Location: %s\r\n" url;
choose_response req page o;
Status.Moved_permanently
| _ -> raise Server.Not_implemented)
(* This one only applies if the uri ends with "!". It will
force regeneration of the page. *)
let regenerate map req o =
(match Request.meth req with
Request.GET | Request.POST ->
let uri = Request.uri req in
let n = String.length uri - 1 in
if String.get uri n <> '!'
then raise Not_found;
let uri' = String.sub uri 0 n in
let (mk, cache) = StringMap.find uri' map in
cache := mk();
let url = "http://" in
let url = url^Unix.gethostname() in
let url =
match Unix.getsockname (Unix.descr_of_out_channel o) with
Unix.ADDR_INET(_,p) -> Printf.sprintf "%s:%d" url p
| _ -> url (* bogus anyway *) in
let url = url^uri' in
let page puts =
Printf.kprintf puts "Go here.\n" url in
headers o req Status.Moved_permanently;
Printf.fprintf o "Pragma: no-cache\r\nLocation: %s\r\n" url;
choose_response req page o;
Status.Moved_permanently
| _ -> raise Server.Not_implemented)