X Tutup
/* * Copyright (C) 2000, 2001, 2013 Gregory Trubetskoy * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); you * may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. * * Originally developed by Gregory Trubetskoy. * * * filterobject.c * * * See accompanying documentation and source code comments * for details. * */ #include "mod_python.h" /*** Some explanation of what is going on here: * * In Apache terminology, an "input" filter filters data flowing from * network to application (aka "up"), an "output" filter filters data * flowing from application to network (aka "down"). * * An output filter is invoked as a result of ap_pass_brigade() * call. It is given a populated brigade, which it then gives in the * same fashion to the next filter via ap_pass_brigade(). (The filter * may chose to create a new brigade and pass it instead). * * An input filter is invoked as a result of ap_get_brigade() call. It * is given an empty brigade, which it is expected to populate, which * may in turn require the filter to invoke the next filter in the * same fashion (via ap_get_brigade()). * * In mod_python Output filters: * * filter.read() - copies data from *given* bucket brigade (saved in * self->bb_in) into a Python string. * * filter.write() - copies data from a Python string into a *new* * bucket brigade (saved in self->bb_out). * * filter.close() - appends an EOS and passes the self->bb_out brigade * to the next filter via ap_pass_brigade() * * In mod_python Input filters: * * filter.read() - copies data from a *new* and *populated via * ap_get_brigade* (saved as self->bb_in) into a Python string. * * filter.write() - copies data from a Python string into a *given* * brigade (saved as self->bb_out). * * filter.close() - appends an EOS to *given* brigade. * */ /** ** MpFilter_FromFilter ** * This routine creates a Python filerobject. * */ PyObject *MpFilter_FromFilter(ap_filter_t *f, apr_bucket_brigade *bb, int is_input, ap_input_mode_t mode, apr_size_t readbytes, char * handler, char *dir) { filterobject *result; result = PyObject_New(filterobject, &MpFilter_Type); if (! result) return PyErr_NoMemory(); result->f = f; result->is_input = is_input; result->rc = APR_SUCCESS; if (is_input) { result->bb_in = NULL; result->bb_out = bb; result->mode = mode; result->readbytes = readbytes; } else { result->bb_in = bb; result->bb_out = NULL; result->mode = 0; result->readbytes = 0; } result->closed = 0; result->softspace = 0; result->handler = handler; result->dir = dir; result->request_obj = NULL; apr_pool_cleanup_register(f->r->pool, (PyObject *)result, python_decref, apr_pool_cleanup_null); return (PyObject *)result; } /** ** filter_pass_on ** * just passes everything on */ static PyObject *filter_pass_on(filterobject *self) { Py_BEGIN_ALLOW_THREADS; if (self->is_input) self->rc = ap_get_brigade(self->f->next, self->bb_out, self->mode, APR_BLOCK_READ, self->readbytes); else self->rc = ap_pass_brigade(self->f->next, self->bb_in); Py_END_ALLOW_THREADS; Py_INCREF(Py_None); return Py_None; } /** ** _filter_read ** * read from filter - works for both read() and readline() */ static PyObject *_filter_read(filterobject *self, PyObject *args, int readline) { apr_bucket *b; long bytes_read; PyObject *result; char *buffer; long bufsize; int newline = 0; long len = -1; conn_rec *c = self->request_obj->request_rec->connection; if (! PyArg_ParseTuple(args, "|l", &len)) return NULL; if (self->closed) { PyErr_SetString(PyExc_ValueError, "I/O operation on closed filter"); return NULL; } if (self->is_input) { /* does the output brigade exist? */ if (!self->bb_in) { self->bb_in = apr_brigade_create(self->f->r->pool, c->bucket_alloc); } Py_BEGIN_ALLOW_THREADS; self->rc = ap_get_brigade(self->f->next, self->bb_in, self->mode, APR_BLOCK_READ, self->readbytes); Py_END_ALLOW_THREADS; if (!APR_STATUS_IS_EAGAIN(self->rc) && !(self->rc == APR_SUCCESS)) { PyErr_SetString(PyExc_IOError, "Input filter read error"); return NULL; } } /* * loop through the brigade reading buckets into the string */ b = APR_BRIGADE_FIRST(self->bb_in); if (b == APR_BRIGADE_SENTINEL(self->bb_in)) return PyBytes_FromString(""); /* reached eos ? */ if (APR_BUCKET_IS_EOS(b)) { apr_bucket_delete(b); Py_INCREF(Py_None); return Py_None; } bufsize = len < 0 ? HUGE_STRING_LEN : len; result = PyBytes_FromStringAndSize(NULL, bufsize); /* possibly no more memory */ if (result == NULL) return PyErr_NoMemory(); buffer = PyBytes_AS_STRING((PyBytesObject *) result); bytes_read = 0; while ((bytes_read < len || len == -1) && !(APR_BUCKET_IS_EOS(b) || APR_BUCKET_IS_FLUSH(b) || b == APR_BRIGADE_SENTINEL(self->bb_in))) { const char *data; apr_size_t size; apr_bucket *old; int i; if (apr_bucket_read(b, &data, &size, APR_BLOCK_READ) != APR_SUCCESS) { PyErr_SetObject(PyExc_IOError, PyBytes_FromString("Filter read error")); return NULL; } if (bytes_read + size > bufsize) { apr_bucket_split(b, bufsize - bytes_read); size = bufsize - bytes_read; /* now the bucket is the exact size we need */ } if (readline) { /* scan for newline */ for (i=0; irequest_obj->request_rec->connection; if (self->closed) { PyErr_SetString(PyExc_ValueError, "I/O operation on closed filter"); return NULL; } if (! PyArg_ParseTuple(args, "s#", &buff, &len)) return NULL; /* bad args */ if (len) { /* does the output brigade exist? */ if (!self->bb_out) self->bb_out = apr_brigade_create(self->f->r->pool, c->bucket_alloc); /* it looks like there is no need to memcpy, an immortal bucket is fine, since Python won't free that memory before the write is over */ b = apr_bucket_immortal_create(buff, len, c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(self->bb_out, b); } Py_INCREF(Py_None); return Py_None; } /** ** filter.flush(filter self) ** * Flush output (i.e. pass brigade) */ static PyObject *filter_flush(filterobject *self, PyObject *args) { conn_rec *c = self->request_obj->request_rec->connection; /* does the output brigade exist? */ if (!self->bb_out) { self->bb_out = apr_brigade_create(self->f->r->pool, c->bucket_alloc); } APR_BRIGADE_INSERT_TAIL(self->bb_out, apr_bucket_flush_create(c->bucket_alloc)); if (!self->is_input) { Py_BEGIN_ALLOW_THREADS; self->rc = ap_pass_brigade(self->f->next, self->bb_out); apr_brigade_destroy(self->bb_out); Py_END_ALLOW_THREADS; if(self->rc != APR_SUCCESS) { PyErr_SetString(PyExc_IOError, "Flush failed."); return NULL; } } Py_INCREF(Py_None); return Py_None; } /** ** filter.close(filter self) ** * passes EOS */ static PyObject *filter_close(filterobject *self, PyObject *args) { conn_rec *c = self->request_obj->request_rec->connection; if (! self->closed) { /* does the output brigade exist? */ if (!self->bb_out) { self->bb_out = apr_brigade_create(self->f->r->pool, c->bucket_alloc); } APR_BRIGADE_INSERT_TAIL(self->bb_out, apr_bucket_eos_create(c->bucket_alloc)); if (! self->is_input) { Py_BEGIN_ALLOW_THREADS; self->rc = ap_pass_brigade(self->f->next, self->bb_out); apr_brigade_destroy(self->bb_out); Py_END_ALLOW_THREADS; self->bb_out = NULL; } self->closed = 1; } Py_INCREF(Py_None); return Py_None; } /** ** filter.disable(filter self) ** * Sets the transparent flag on causeing the filter_handler to * just pass the data through without envoking Python at all. * This is used during filter error output. */ static PyObject *filter_disable(filterobject *self, PyObject *args) { python_filter_ctx *ctx; ctx = (python_filter_ctx *) self->f->ctx; ctx->transparent = 1; Py_INCREF(Py_None); return Py_None; } static PyMethodDef filterobjectmethods[] = { {"pass_on", (PyCFunction) filter_pass_on, METH_NOARGS}, {"read", (PyCFunction) filter_read, METH_VARARGS}, {"readline", (PyCFunction) filter_readline, METH_VARARGS}, {"write", (PyCFunction) filter_write, METH_VARARGS}, {"flush", (PyCFunction) filter_flush, METH_VARARGS}, {"close", (PyCFunction) filter_close, METH_VARARGS}, {"disable", (PyCFunction) filter_disable, METH_VARARGS}, {NULL, NULL} }; #define OFF(x) offsetof(filterobject, x) static PyMemberDef filter_memberlist[] = { {"softspace", T_INT, OFF(softspace), }, {"closed", T_INT, OFF(closed), READONLY}, {"name", T_OBJECT, 0, READONLY}, {"req", T_OBJECT, OFF(request_obj), }, {"is_input", T_INT, OFF(is_input), READONLY}, {"handler", T_STRING, OFF(handler), READONLY}, {"dir", T_STRING, OFF(dir), READONLY}, {NULL} /* Sentinel */ }; /** ** filter_dealloc ** * */ static void filter_dealloc(filterobject *self) { Py_XDECREF(self->request_obj); PyObject_Del(self); } /** ** filter_getattr ** * Get filter object attributes * * */ static PyObject * filter_getattr(filterobject *self, char *name) { PyObject *res; PyMethodDef *ml = filterobjectmethods; for (; ml->ml_name != NULL; ml++) { if (name[0] == ml->ml_name[0] && strcmp(name+1, ml->ml_name+1) == 0) return PyCFunction_New(ml, (PyObject*)self); } PyErr_Clear(); if (strcmp(name, "name") == 0) { if (! self->f->frec->name) { Py_INCREF(Py_None); return Py_None; } else { return MpBytesOrUnicode_FromString(self->f->frec->name); } } else if (strcmp(name, "req") == 0) { if (! self->request_obj) { Py_INCREF(Py_None); return Py_None; } else { Py_INCREF(self->request_obj); return (PyObject *)self->request_obj; } } else { PyMemberDef *md = find_memberdef(filter_memberlist, name); if (!md) { PyErr_SetString(PyExc_AttributeError, name); return NULL; } return PyMember_GetOne((char*)self, md); } } /** ** filter_setattr ** * Set filter object attributes * */ static int filter_setattr(filterobject *self, char *name, PyObject *v) { if (v == NULL) { PyErr_SetString(PyExc_AttributeError, "can't delete filter attributes"); return -1; } PyMemberDef *md = find_memberdef(filter_memberlist, name); if (!md) { PyErr_SetString(PyExc_AttributeError, name); return -1; } return PyMember_SetOne((char*)self, md, v); } PyTypeObject MpFilter_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "mp_filter", /* tp_name */ sizeof(filterobject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor) filter_dealloc, /* tp_dealloc*/ 0, /* tp_print*/ (getattrfunc) filter_getattr, /* tp_getattr*/ (setattrfunc) filter_setattr, /* tp_setattr*/ 0, /* tp_compare*/ 0, /* tp_repr*/ 0, /* tp_as_number*/ 0, /* tp_as_sequence*/ 0, /* tp_as_mapping*/ 0, /* tp_hash*/ };
X Tutup