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

Conrad Steenberg conrad at hep.caltech.edu
Mon May 19 14:08:43 EST 2003


Hi

Here is a patch to mod_python version 2.7.x that adds the method
write_file to the request object.

This method writes the named file to the client using the Apache
ap_send_fd_length() call. It works for both normal (http) and SSL/TLS
(https) connections, with the former using the sendfile() system call
where available. This significantly improves performance and reduces CPU
usage relative to reading data into a buffer and writing it to the
client using the req.write() method.

Usage e.g.:

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


This is the companion patch to the 3.0.x version (also attached).

Please consider applying the patch(es) to the main tree.

Regards

Conrad

-- 
Conrad Steenberg <conrad at hep.caltech.edu>
-------------- next part --------------
--- src/requestobject.c.orig	Mon May 19 11:38:46 2003
+++ src/requestobject.c	Mon May 19 11:51:03 2003
@@ -781,6 +781,52 @@
 
 }
 
+static PyObject * req_write_file(requestobject *self, PyObject *args)
+{
+    int rc;
+    char *fname;
+    FILE *fd;
+    size_t offset=0, len=-1, nbytes;
+    struct stat status;
+    PyObject * py_result = NULL;
+    
+    
+    if (! PyArg_ParseTuple(args, "s|ll", &fname, &offset, &len))
+        return NULL;  /* bad args */
+
+    rc=stat(fname,&status);
+    if (rc !=0) {
+        PyErr_SetString(PyExc_IOError, "Could not stat file for reading");
+        return NULL;
+    }
+    
+    fd=fopen(fname,"r");
+    if (!fd) {
+        PyErr_SetString(PyExc_IOError, "Could not open file for reading");
+        return NULL;
+    }                         
+
+    if (len==-1) len=status.st_size;
+    if (offset!=0){
+      rc=fseek(fd,offset,SEEK_SET);
+      if (rc!=0){
+        PyErr_SetString(PyExc_IOError, "Could not seek to specified offset");
+        fclose(fd);
+        return NULL;      
+      }
+    }
+        
+    Py_BEGIN_ALLOW_THREADS                         
+    nbytes = ap_send_fd_length(fd, self->request_rec, len);
+    
+    Py_END_ALLOW_THREADS
+    
+    py_result = PyLong_FromLong (nbytes);
+    Py_INCREF(py_result);
+    return py_result;
+
+}
+
 static PyMethodDef requestobjectmethods[] = {
     {"add_common_vars",      (PyCFunction) req_add_common_vars,      METH_VARARGS},
     {"add_handler",          (PyCFunction) req_add_handler,          METH_VARARGS},
@@ -797,6 +843,7 @@
     {"register_cleanup",     (PyCFunction) req_register_cleanup,     METH_VARARGS},
     {"send_http_header",     (PyCFunction) req_send_http_header,     METH_VARARGS},
     {"write",                (PyCFunction) req_write,                METH_VARARGS},
+    {"write_file",           (PyCFunction) req_write_file,           METH_VARARGS},
     { NULL, NULL } /* sentinel */
 };
 
-------------- 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