Hi, Let me continue to develop some ideas about the socket-accept-handle triplet. - For [socket] (and [run]) of the AJP connector, I would add an optional parameter [?props] to be able to pass an optional property list -- the values set in this way supersede the values set by other optional arguments in order to be able to set defaults. To be more flexible, I would also replace [jvm_emu_main] by a function parsing the arguments (allowing to set more arguments if needed). That yields the following functions: val arg_parse : ?anon_fun:(string -> unit) -> ?usage_msg:string -> (Arg.key * Arg.spec * Arg.doc) list -> (string * string) list val props_of_file : string -> (string * string) list val run : ?props:(string * string) list ?config:config -> ?output_type:output_type -> ?arg_storage:(string -> Netmime.mime_header -> arg_storage) -> ?sockaddr:Unix.sockaddr -> (cgi -> unit) -> unit val socket : ?props:(string * string) list -> ?backlog:int -> ?reuseaddr:bool -> ?addr:Unix.inet_addr -> ?port:int -> Unix.file_descr The equivalent to [jvm_emu_main(fun props auth addr port -> server f auth addr port)] now reads: AJP.run ~props:(AJP.arg_parse []) f - The [handle_connection] will not be in a separate module but instead adapts itself to the version present in the protocol (convenient but also useful e.g. if the app machine must handle several web servers with different versions of the protocol). How will [handle_connection] reports that the app must shutdown or restart? So far, it was raising an exception. However, I believe it is better to "force" the user to handle them, so they should be return values instead: type connection_handler = Unix.file_descr -> Unix.file_descr -> (cgi -> unit) -> connection_status and connection_status = Ok | Shutdown | Restart (The [Ok] is in case the server shuts down the connection after a request which I think it is entitled to do.) In case of threads, that makes also easy to transmit the value to the "main thread" through a ['a Event.event] -- this will generally be preferred to pipes in a multi-threaded app. I guess. The [connection_handler] takes two file descriptors, one for input and the other one for output, for flexibility (e.g. buiding a chain,...). Generally, both will be the same socket. (Maybe the distinction is overkill.) The connection handler will execute the function [cgi -> unit] for each (well formed) request coming in and catch (and log) all exceptions (it will raise no exception itself). As you see, I do not require the user to create a cgi object -- it is done automatically as most of the time one will want to do so. I think it does not make much sense to execute the function [cgi -> unit] in a separate thread or process for AJP as all requests are presented sequentially. For FCGI with mutliplexed requests it does however, so some flexibility is required in that case (such flexibility is not provided by the current FCGI interface either). - The "accept" part is the more difficult as it is where the peculiarities of the concurrent model express themselves the more. On the other hand, it is fairly generic in the sense it depends very little on the protocol one must handle. In fact, I do not think it is possible to define a function that will handle all cases -- or its interface will be prohibitively complicated, thus defeat its purpose. Therefore, I believe the best is to handle common cases and leave it to the user to define its specialized cases (plus I guess functors can be defined on top of [socket] and [handle_connection] as accept does not depend on the protocol). The first case is a new process per connection. Since we need a pipe to communicate to the father the return value of the child, the signature is: val accept_fork : ?props:(string * string) list -> ?onrestart:(unit -> unit) -> ?onshutdown:(unit -> unit) -> ?allow_hosts:Unix.inet_addr list -> ?fork:((Unix.file_descr -> unit) -> int * Unix.file_descr) -> connection_handler -> Unix.inet_addr -> unit where the first [Unix.file_descr] is in the child to write the return value and the second one is in the father to read it. A typical fork function is let fork f = let (infd, outfd) = Unix.pipe () in match Unix.fork() with | 0 -> f outfd; exit 0 (* or the double fork trick *) | n -> n, infd As for threads, I guess the better is to use an event -- created by accept itself -- to transmit the return value, so the following interface should be enough: val accept_threads : ?props:(string * string) list -> ?onrestart:(unit -> unit) -> ?onshutdown:(unit -> unit) -> ?allow_hosts:Unix.inet_addr list -> ?thread:((unit -> unit) -> unit) -> connection_handler -> Unix.inet_addr -> unit (It should be possible to start a new thread everytime or to use a thread pool with this.) Maybe I forgot some important cases and the above design should be refined but at least it convey the idea. The interface modified with the previous ideas is attached. Regards, ChriS --- P.S. If we ever go in the direction I suggest, a possibility is to develop it in a netcgi directory. Not only that will make possible for the two versions to coexist but is more natural as the library name is netcgi (think of -I +netcgi).