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 */ };
|