gcl-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

HTTP servers, was Re: [Gcl-devel] Re: General GCL quesions


From: Jeff Dalton
Subject: HTTP servers, was Re: [Gcl-devel] Re: General GCL quesions
Date: Wed, 14 Apr 2004 17:08:33 +0100 (BST)
User-agent: IMP/PHP IMAP webmail program 2.2.8

Quoting Chris Hall <address@hidden>:

> > Back in the 90s, I wrote an HTTP server using the C interface
> > for sockets and select.  It's part of O-Plan (an AI planner),
> > used to provide a web-browser interface.  I don't think it's
> > too hard to do, but it would depend on how sophisticated a
> > server you needed.

> I'm curious - Camm mentioned that he had something similar, I think.

> In your approach, which is 'closer' to the web, Lisp or C?  I.e.,
> which actually _listens_ to the web?

The socket-level stuff (bind, listen, etc) is done in C,
called from Lisp.  I used the C interface to define functions
such as listen-socket.

I also used the C interface to define a Lisp select that calls
the C one, for some things about forking, pids, etc, and for
some stream-related things.

The requests are read and processed in Lisp, but some of the handlers
fork a separate Unix process that does the work.  For example,
if all I have to do is send the contents of a file, I fork
a /bin/cat to do it, because /bin/cat does fancy things to copy
efficiently.

> I would think that parsing an HTTP request would be much simpler in
> Lisp, but then the C is needed to effectively handle more than one
> open port at a time, correct?

My server listens on only one port, but it could handle multiple
input sources by using select.  Listening to one port seemed
typical to me, e.g. the usual HTTP server listens on port 80.

It uses select in any case, because the server wants to have a
timeout on the wait for input.

> Perhaps C listens/manages the ports, accepts the complete HTTP
> request and hands it off (without looking at it) to Lisp for
> parsing/processing?

For accept, I have:

(defun socket-connection-stream (s)
  (let ((fd (accept-socket-connection s)))
    (make-two-way-stream
      (fork:fdopen fd :input)
      (fork:fdopen fd :output)))

"fork" is a package that defined things such as fork, execlp,
pipe, dup, and some stream-related things.  fork:fdopen opens
a Unix file descriptor as a CL stream.

accept-socket-connection is defined like this:

(Clines

%   static int accept_socket_connection(int s)
%   {
%       int fd;
%       fd = accept(s, 0, 0);
%       if (fd < 0)
%           unix_error("Accept_socket_connection failed.");
%       else
%           return(fd);
%   }

)

(defentry accept-socket-connection (int) (int accept_socket_connection))

I don't do a lot of processing at the C level.  Instead, I define
simple functions like that and do the rest in Lisp.

> I wrote a socket server in Java a while back (using code from a book
> as a starting point), and I remember that it listened on one port to
> accept session-start requests, generated a new local socket on a
> separate port, and all session traffic for a particular client went
> over that port.

That is very like what I do, except that sessions are started
using CGI.

There's a web page containing a form.  The user fills in some
things and clicks on a button.  That's handled by an ordinary
HTTP server (Apache) listening on port 80.  O-Plan is then
run as a CGI program.  In some cases, it does some planning,
produces some outputs, and exits.  In others, it forks another
O-Plan that then sets itself up as an HTTP server listening on
a free port.  The other branch of the fork sends back a
redirection response that sends the user's browser to the
HTTP server.

(Using free ports is a bit of a problem these days, because
some people have firewalls that don't let them connect to random
ports on other machines, and so they can't connect to that formerly
free port.  Also the systems people at my end don't like to open
up a wide range of ports.)

I could do without the Apache / CGI part, but then I'd have
to have the O-Plan HTTP server sitting there all the time waiting
for users to come along; and since I didn't need to do that,
I didn't.

> Now that I think about it, and the reason I bring it up, is that I
> have no idea how a web server (or smtp or ssh) handles multiple
> connections using only one open port on the firewall.

It could keep the socket fds returned by accept open.
There would then be one per user, so long as the user ends
kept them open as well.

> Wait, lemme
> guess - the server uses the IP address of the incoming connection,
> probably keeping a list of pending requests (HTTP) or open sessions
> (ssh/smtp)?

There might be more than one connection from the same IP address.

Cookies and session ids start to enter the picture.

> I'm also curious about what 'lispy' APIs 'try' to look like, though I
> think I'm gradually starting to get a feel for it.  Does anyone know
> of a style guide, or if there exists a such a thing?

That's a good question.  For sockets and the like, I start by
essentially duplicating the C API at the Lisp level.

Here are some advantages: (1) You get to do most of the work in Lisp.
(2) It will be easier for socketry experts to understand the Lisp code,
because what they know is the C-level API.  (3) It's easy to construct
experiments in Lisp without having to recompile anything.

-- Jeff




reply via email to

[Prev in Thread] Current Thread [Next in Thread]