make WWW_Find work again, now that its ML modules reside within a theory context (cf. bf5b45870110) -- patch by Rafal Kolanski;
1 (* Title: Tools/WWW_Find/scgi_server.ML
2 Author: Timothy Bourke, NICTA
7 signature SCGI_SERVER =
9 val max_threads : int Unsynchronized.ref
10 type handler = ScgiReq.t * Word8Vector.vector * (string -> unit) -> unit
11 val register : (string * Mime.t option * handler) -> unit
12 val server : string -> int -> unit
13 val server' : int -> string -> int -> unit (* keeps trying for port *)
14 val simple_handler : (string Symtab.table -> (string -> unit) -> unit) -> handler
15 val raw_post_handler : (string -> string) -> handler
18 structure ScgiServer : SCGI_SERVER =
20 val max_threads = Unsynchronized.ref 5;
22 type handler = ScgiReq.t * Word8Vector.vector * (string -> unit) -> unit;
25 val servers = Unsynchronized.ref (Symtab.empty : (Mime.t option * handler) Symtab.table);
27 fun register (name, mime, f) =
28 Unsynchronized.change servers (Symtab.update_new (name, (mime, f)));
29 fun lookup name = Symtab.lookup (!servers) name;
31 fun dump_handlers () = (
32 tracing(" with handlers:");
33 app (fn (x, _) => tracing (" - " ^ x)) (Symtab.dest (!servers)))
36 fun server server_prefix port =
38 val passive_sock = Socket_Util.init_server_socket (SOME "localhost") port;
40 val thread_wait = ConditionVar.conditionVar ();
41 val thread_wait_mutex = Mutex.mutex ();
44 val threads = Unsynchronized.ref ([] : Thread.thread list);
45 fun purge () = Unsynchronized.change threads (filter Thread.isActive);
47 fun add_thread th = Unsynchronized.change threads (cons th);
49 fun launch_thread threadf =
51 if length (!threads) < (!max_threads) then ()
52 else (tracing ("Waiting for a free thread...");
53 ConditionVar.wait (thread_wait, thread_wait_mutex));
55 (Thread.fork (* FIXME avoid low-level Poly/ML thread primitives *)
56 (fn () => exception_trace threadf,
57 [Thread.EnableBroadcastInterrupt true,
59 Thread.InterruptAsynchOnce])))
64 val (sock, _)= Socket.accept passive_sock;
66 val (sin, sout) = Socket_Util.make_streams sock;
68 fun send msg = BinIO.output (sout, Byte.stringToBytes msg);
69 fun send_log msg = (tracing msg; send msg);
71 fun get_content (st, 0) = Word8Vector.fromList []
72 | get_content x = BinIO.inputN x;
76 val (req as ScgiReq.Req {path_info, request_method, ...},
79 handle ScgiReq.InvalidReq s =>
81 (HttpUtil.reply_header (HttpStatus.bad_request, NONE, []));
82 raise Fail ("Invalid request: " ^ s));
83 val () = tracing ("request: " ^ path_info);
85 (case lookup (unprefix server_prefix path_info) of
86 NONE => send (HttpUtil.reply_header (HttpStatus.not_found, NONE, []))
87 | SOME (NONE, f) => f (req, get_content content_is, send)
89 (send (HttpUtil.reply_header (HttpStatus.ok, t, []));
90 if request_method = ScgiReq.Head then ()
91 else f (req, get_content content_is, send)))
94 fun thread_req () = (* FIXME avoid handle e *)
95 (do_req () handle e => (warning (exnMessage e));
96 BinIO.closeOut sout handle e => warning (exnMessage e);
97 BinIO.closeIn sin handle e => warning (exnMessage e);
98 Socket.close sock handle e => warning (exnMessage e);
99 tracing ("request done.");
100 ConditionVar.signal thread_wait);
102 launch_thread thread_req;
106 tracing ("SCGI server started on port " ^ string_of_int port ^ ".");
109 Socket.close passive_sock
115 fun server' 0 server_prefix port = (warning "Giving up."; exit 1)
116 | server' countdown server_prefix port =
117 server server_prefix port
118 handle OS.SysErr ("bind failed", _) =>
119 (warning ("Could not acquire port "
120 ^ string_of_int port ^ ". Trying again in "
121 ^ string_of_int delay ^ " seconds...");
122 OS.Process.sleep (Time.fromSeconds delay);
123 server' (countdown - 1) server_prefix port);
126 fun simple_handler h (ScgiReq.Req {request_method, query_string, ...}, content, send) =
127 h (case request_method of
128 ScgiReq.Get => query_string
131 |> Byte.bytesToString
132 |> HttpUtil.parse_query_string
133 | ScgiReq.Head => raise Fail "Cannot handle Head requests.")
136 fun raw_post_handler h (ScgiReq.Req {request_method=ScgiReq.Post, ...}, content, send) =
137 send (h (Byte.bytesToString content))
138 | raw_post_handler _ _ = raise Fail "Can only handle POST request.";