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