[mod_python] apache API and mod_python <-> protocol module integration

Gregory (Grisha) Trubetskoy grisha at modpython.org
Wed Jul 16 20:47:22 EST 2003


Well, after some looking, apparently the APR does work this way, it's just
that the way the connection handler was coded probably because I didn't
understand APR well enough at the time I made it loop until specified
length of bytes was read (I started out with replicating req.read(), which
knows how much data there is to read).

I attached a patch to the connobject.c that changes its behaviour so that
read(len) will read at most len bytes, and will only block if no data is
available. And len is an optional argument.

Here is a connection handler code that does the exact same thing you had
below. (note that print writes to stdout, which is visible if you run
httpd with -X (stay in foreground) option:

from mod_python import apache

def connectionhandler(conn):

    print "Connect from '%s'" % `conn.remote_addr`

    while 1:
        data = conn.read()
        if not data: break
        print " Received: %s" % `data`

    print "Connection closed from '%s'" % `conn.remote_addr`

    return apache.OK

And here's the result:

$ ./bin/httpd -X
Connect from '('0.0.0.0', 3513)'
 Received: 'Hello\r\n'
 Received: 'World\r\n'
Connection closed from '('0.0.0.0', 3513)'

If I do 'for i in "Hello World"' thing, I get this:

Connect from '('0.0.0.0', 3524)'
 Received: 'Hello World'
Connection closed from '('0.0.0.0', 3524)'

Grisha

On Wed, 16 Jul 2003, Dustin Mitchell wrote:

> On Wed, Jul 16, 2003 at 01:59:58PM -0400, Gregory (Grisha) Trubetskoy wrote:
> > This is not a problem of mod_python or Apache, this is the way sockets
> > work. If your protocol is such that you know ahead of time how much to
> > read (e.g. HTTP), then you can request this much data in one read.
> > Otherwise, you must read in smallest unit sizes of your protocol, which
> > (I'm guessing) is 1 byte in case of Z39.50.
>
> This is not strictly true, as long as you're OK with possibly getting a
> bit of *extra* data beyond a particular PDU (a little bit of buffering
> can take care of that).  Apache has to do this to read the HTTP header,
> which is not of a fixed length.
>
> The socket blocking read interface is structured such that it will block
> when *no* data is available, and will return *up to* the quantity
> specified in the recv() call if it is available.  However, if some
> smaller amount of data is available, that data will be returned
> immediately.
>
> To demonstrate, I hacked this together:
>
> from socket import *
> s = socket(AF_INET, SOCK_STREAM)
> s.bind(("", 9900))
> s.listen(5)
> while 1:
>   ss, sa = s.accept()
>   print "Connect from '%s'" % (sa,)
>   while 1:
>     data = ss.recv(100)
>     if not data: break
>     print " Received: %s" % `data`
>   print "Connection closed from '%s'" % (sa,)
>
> Then connected with telnet and typed "Hello" and "World" on separate
> lines.  Telnet, by default, buffers lines of input until a newline, but
> you will note that in the two returns from ss.recv(), neither contained
> 100 bytes of data:
>
> Connect from '('127.0.0.1', 56260)'
>  Received: 'Hello\r\n'
>  Received: 'World\r\n'
> Connection closed from '('127.0.0.1', 56260)'
>
> Further, a loop such as:
>   for i in 'hello world': s.send(i)
> will produce output like this:
>
> Connect from '('127.0.0.1', 56277)'
>  Received: 'h'
>  Received: 'ello world'
> Connection closed from '('127.0.0.1', 56277)'
>
> Either the client or the server's socket implementation is buffering the
> characters because they're appearing so quickly, which cuts down on the
> number of recv() calls which must be made.
>
> I can't speak for the Apache sockets API, or the Python interface
> thereto, but sockets themselves *do* support this interface (they'd be
> pretty useless if they didn't!).  I highly recommend W. Richard
> Stevens' TCP/IP Illustrated for an in-depth and very readable
> discussion of this and many other crucial implementation details of
> TCP/IP.
>
> Dustin
>
> --
>
>   Dustin Mitchell
>   dustin at ywlcs.org/djmitche at alumni.uchicago.edu
>   http://people.cs.uchicago.edu/~dustin/
> _______________________________________________
> Mod_python mailing list
> Mod_python at modpython.org
> http://mailman.modpython.org/mailman/listinfo/mod_python
>
-------------- next part --------------
Index: connobject.c
===================================================================
RCS file: /home/cvs/httpd-python/src/connobject.c,v
retrieving revision 1.14
diff -u -r1.14 connobject.c
--- connobject.c	23 Jan 2003 22:34:18 -0000	1.14
+++ connobject.c	17 Jul 2003 00:35:28 -0000
@@ -113,8 +113,10 @@
 
     bb = apr_brigade_create(c->pool, c->bucket_alloc);
 
+    bufsize = len == 0 ? HUGE_STRING_LEN : len;
+
     Py_BEGIN_ALLOW_THREADS;
-    rc = ap_get_brigade(c->input_filters, bb, mode, APR_BLOCK_READ, len);
+    rc = ap_get_brigade(c->input_filters, bb, mode, APR_BLOCK_READ, bufsize);
     Py_END_ALLOW_THREADS;
 
     if (! APR_STATUS_IS_SUCCESS(rc)) {
@@ -135,7 +137,6 @@
         return Py_None;
     }
 
-    bufsize = len == 0 ? HUGE_STRING_LEN : len;
     result = PyString_FromStringAndSize(NULL, bufsize);
 
     /* possibly no more memory */
@@ -180,7 +181,7 @@
 	}
 
 
-        if (mode == AP_MODE_GETLINE) {
+        if (mode == AP_MODE_GETLINE || len == 0) {
             apr_bucket_delete(b);
             break;
         }
@@ -208,11 +209,8 @@
 
     long len = 0;
 
-    if (! PyArg_ParseTuple(args, "l|i", &len)) 
+    if (! PyArg_ParseTuple(args, "|l", &len)) 
         return NULL;
-
-    if (len == 0)
-        return PyString_FromString("");
 
     if (len == -1)
         return _conn_read(self->conn, AP_MODE_EXHAUSTIVE, 0);


More information about the Mod_python mailing list