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