Re: [Caml-list] Re: Common CGI interface
Gerd Stolpmann <[email protected]> Thu, 16 Jun 2005 14:05:03 +0200
| Newsgroups | gmane.comp.lang.ocaml.lib.net.devel |
|---|---|
| Message-ID | <[email protected]> |
Am Dienstag, den 14.06.2005, 21:39 +0200 schrieb Christophe TROESTLER: > On Tue, 14 Jun 2005, Gerd Stolpmann <[email protected]> wrote: > > > > As it seems, I have some time this week. Will look at it tomorrow. > > Thanks. You can read a complete commented signature at > http://ocaml-cgi.sourceforge.net/netcgi/Netcgi.html Ok, thanks. I also read your old messages again. When I had to summarize your proposal, I would do it the following way: 1) A refactorisation of the modules and classes, so that they are easier to understand, especially how they are related to each other. 2) New design of the connector API, or better, the first trial to design this API at all. 3) Some interesting details, such as the Random module. First of all, I must say that your proposal looks quite convincing, and much better than the current API. Nevertheless, I think we should discuss the various aspects in depth to see whether it is really worth changing the API. There is a very important downside of changes: they often break existing code, and this may drive users away from using Ocamlnet. 1) In your proposal, everything is contained in a top-level module Netcgi. In the current API, we have (in this order): Netcgi_env Netcgi_types Netcgi Netcgi_<connector> (a) When reading ocamldoc, the approach of having a single top-level module gives a faster survey what exists, and how the various pieces are connected. In the current API, this is more difficult. Just for naming the approaches, I call your approach "monolithic" and the current API "polylithic" However, there are also other aspects: (b) engineering. In the monolithic API, external people cannot add their own connectors to the module namespace. It is also difficult to have connectors that are optionally built. In the polylithic approach, this is no problem, just name the module accordingly. We actually have this problem when the HTTP connector will be added to Ocamlnet (it is already developed and checked in as part of a Ocamlnet branch, but not yet released). It is much larger than the other connectors and requires a number of additional modules as prerequisites. Because of this, we are going to make this connector optional. It also has a very different API, since it is essentially a complete web server in itself. When we adopt the monolithic API, at least this part cannot be incorporated. (c) Code size. When the executable is linked, there is a step called "garbage collection": unused code does not become part of the executable. O'Caml does garbage collection only for top-level modules, but not inside modules. This means that the monolithic approach will typically produce large executables than the polylithic approach where at least unused connectors do not go into the executable. This aspect has some importance for real-world programs, and I already got a number of complaints why programs are so large. As a compromise of the three mentioned aspects I can imagine the following: - Only combine Netcgi_env, Netcgi_types, and the non-CGI-specific part of Netcgi into a new top-level module Netcgi_api. The old modules would remain intact, so existing programs are not broken. Netcgi_api's task would be to establish the user's view on the core typing, whereas the old modules would focus on the implementor's view. - For every connector there is a new module Netcgi_<connector>_api in addition to the existing modules. Same split of views. 2) Now to the connectors. First of all, I like the direction of thinking. The connectors should be more uniform, and it should be easier to exchange one connector by a different one. (a) One of your ideas is to have a "run" interface for sequential connectors. At the first glance, this looks very convincing. However, I am not sure whether the users are put onto the right track. Both CGI and FCGI allow implicit parallelism driven by the web server. This isn't properly reflected by "run". The various "run" entries actually have very different semantics: CGI runs only once, but this may happen in parallel. FCGI runs several times, and in parallel. AJP runs several times, but there is only one instance. The common idea of "run" is that it is process-bound, and this is a quite low-level idea. I hope we can find something better (see below). (b) Regarding the socket interfaces: I don't understand how they work. Can you sketch some examples? (c) Unfortunately, the URL-binding stuff of AJP has been removed. The point is that AJP differs from CGI and FCGI here. In CGI, the running program is part of the file system backing the URL space: foo.cgi is found because there is a file with the same name. For AJP this is not true any more! In AJP, one binds a whole URL subtree to an AJP daemon (e.g. the /servlet subtree such that the daemon is invoked when /servlet/foo is requested as URL). It is now the task of the daemon to dispatch the services that can be selected by the URL part following the common prefix (e.g. invoke the "foo" handler for /servlet/foo). This style is generally also applicable to CGI if you configure a CGI as handler rather than the usual way. For the coming HTTP daemon, one needs URL dispatching, too (there is already an implementation - look at http://cvs.sourceforge.net/viewcvs.py/ocamlnet/ocamlnet/src/nethttpd/Attic/nethttpd_services.mli?rev=1.1.2.2&only_with_tag=ocamlnet-1-0-nethttpd&view=auto and http://cvs.sourceforge.net/viewcvs.py/ocamlnet/ocamlnet/examples/nethttpd/Attic/file_mt_reactor.ml?rev=1.1.2.2&only_with_tag=ocamlnet-1-0-nethttpd&view=auto) In general, I would more like a unification of the connectors based on their semantics (or better, semantic implications). In this approach one would set up a connector in two steps: First describe the aspects of the connector (run-time model, URL binding, actions), possibly several connector styles the program can support. Second call the connector entry function with the description. The function selects the right (or "best") connector style automatically. I think this can be made very smooth by choosing the right defaults (e.g. "any" as default run-time model; all input URLs are bound by default). For example: type runtime_model = [ `Any | `Sequential of ... (* would be interpreted strictly, e.g. in the CGI case the * activations would be serialized using mutexes *) | `Concurrent_processes of ... (* arguments describe that in detail: number of processes, * limit of the process lifetime etc. One can leave things * unspecified, especially when the web server controls them *) | `Concurrent_threads of ... (* Similar idea *) ] (* The arguments are objects, to allow subtyping *) type binding_list = ( url_filter * action ) list type binding = [ `All of action (* All URLs are handled by action *) | `Select of binding_list (* Select the first matching action *) ... (* maybe more styles *) ] val cgi_entry : ?runtime_models:runtime_model list -> binding -> unit val fcgi_entry : ?runtime_models:runtime_model list -> binding -> unit val ajp_entry : ?runtime_models:runtime_model list -> binding -> unit (* runtime_models defaults to [`Any] *) (* test_entry will be special because input does not come from * environment *) 3) Concerning the Random module, I think this is much better solved in Cryptokit. So better remove this module at all. There is a simplification for `Transactional. Well, transactions with file buffers are not very common, but sometimes one needs them. Cookie module: This must be improved. Other languages have much better cookie support. Especially one needs a cookie storage that is independent from the representation in the header. Shorter names for argument methods: Well, this is hard to change now. It would break all programs at a lot of locations. What is the idea of arg_true? This sounds very Perlish, and imports bad habits ("0" is false and e.g. "0E0" is true). Addition of "log": Good idea, and it already happened in CVS, but in a slightly different way. "log" is primarily part of the environment. It is not a channel, because we cannot guarantee this. It is simply a function like val log : string -> unit and means that a single message is logged (more following the syslog interface), together with an automatically generated timestamp. Maybe I overlooked other ideas in your proposal. Gerd -- ------------------------------------------------------------ Gerd Stolpmann * Viktoriastr. 45 * 64293 Darmstadt * Germany [email protected] http://www.gerd-stolpmann.de ------------------------------------------------------------ ------------------------------------------------------- SF.Net email is sponsored by: Discover Easy Linux Migration Strategies from IBM. Find simple to follow Roadmaps, straightforward articles, informative Webcasts and more! Get everything you need to get up to speed, fast. http://ads.osdn.com/?ad_id=7477&alloc_id=16492&op=click