[mod_python] [Patch] req.write_file for 3.0.x

Conrad Steenberg conrad at hep.caltech.edu
Tue May 6 01:36:26 EST 2003


Hi

Attached is a patch (against 3.0.1 but probably works for all 3.0.x) for
src/requestobject.c to allow mod_python programs to send files
efficiently using the available Apache machinery for both unencrypted
and SSL/TLS connections.

What does it do?
----------------

Implements a file_write() method for the Request object. It does NOT
write headers, only the file.

E.g.

def handler(req):
  req.content_type='text/html'
  req.send_http_header()
  sent=0
  while 
  req.write_file("/var/www/html/index.html",0,-1)
  return apache.OK

The first argument is the filename, the second is the offset to start
reading in the file, the third argument is the number of bytes to write.
The last two arguments are optional.

The return value is the number of bytes written, which may be less than
the total number of bytes in the file. See the manpage of sendfile for
more details.

The patch is NOT OS- or architecture dependent, it merely lets Apache
call its output handler, in the same way that req.write() does for
string data.

If there is interest, the patch can developed further to also set the
content-type, send headers and iterate until the whole file is sent.

Cheers

Conrad
-- 
Conrad Steenberg <conrad at hep.caltech.edu>
-------------- next part --------------
--- requestobject.c.orig	2003-05-05 12:13:07.000000000 -0700
+++ requestobject.c	2003-05-05 13:34:50.000000000 -0700
@@ -808,6 +808,54 @@
 
 }
 
+static PyObject * req_write_file(requestobject *self, PyObject *args)
+{
+    int rc;
+    char *fname;
+    apr_file_t *fd;
+    apr_size_t offset=0, len=-1, nbytes;
+    apr_status_t status;
+    PyObject * py_result = NULL;
+    apr_finfo_t finfo;
+    
+    
+    if (! PyArg_ParseTuple(args, "s|ll", &fname, &offset, &len))
+        return NULL;  /* bad args */
+
+    status=apr_stat(&finfo, fname,
+                    APR_READ, self->request_rec->pool);
+    if (status != APR_SUCCESS) {
+        PyErr_SetString(PyExc_IOError, "Could not stat file for reading");
+        return NULL;
+    }
+    
+    status=apr_file_open(&fd, fname,
+                         APR_READ, finfo.protection,
+                         self->request_rec->pool);
+    if (status != APR_SUCCESS) {
+        PyErr_SetString(PyExc_IOError, "Could not open file for reading");
+        return NULL;
+    }                         
+
+    if (len==-1) len=finfo.size;
+        
+    Py_BEGIN_ALLOW_THREADS                         
+    status = ap_send_fd(fd, self->request_rec, offset, 
+             len, &nbytes);
+    
+    Py_END_ALLOW_THREADS
+    
+    if (status != APR_SUCCESS) {
+        PyErr_SetString(PyExc_IOError, "Write failed, client closed connection.");
+        return NULL;
+    }
+
+    py_result = PyLong_FromLong (nbytes);
+    Py_INCREF(py_result);
+    return py_result;
+
+}
+
 static PyMethodDef request_methods[] = {
     {"add_common_vars",       (PyCFunction) req_add_common_vars,       METH_NOARGS},
     {"add_handler",           (PyCFunction) req_add_handler,           METH_VARARGS},
@@ -827,6 +875,7 @@
     {"send_http_header",      (PyCFunction) req_send_http_header,      METH_NOARGS},
     {"set_content_length",    (PyCFunction) req_set_content_length,    METH_VARARGS},
     {"write",                 (PyCFunction) req_write,                 METH_VARARGS},
+    {"write_file",            (PyCFunction) req_write_file,            METH_VARARGS},
     { NULL, NULL } /* sentinel */
 };
 


More information about the Mod_python mailing list