2
* Copyright 2004 Apache Software Foundation
4
* Licensed under the Apache License, Version 2.0 (the "License"); you
5
* may not use this file except in compliance with the License. You
6
* may obtain a copy of the License at
8
* http://www.apache.org/licenses/LICENSE-2.0
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13
* implied. See the License for the specific language governing
14
* permissions and limitations under the License.
16
* Originally developed by Gregory Trubetskoy.
21
* $Id: requestobject.c,v 1.57 2004/02/16 19:47:27 grisha Exp $
25
#include "mod_python.h"
28
** MpRequest_FromRequest
30
* This routine creates a Python requestobject given an Apache
31
* request_rec pointer.
35
PyObject * MpRequest_FromRequest(request_rec *req)
37
requestobject *result;
39
result = PyObject_New(requestobject, &MpRequest_Type);
41
return PyErr_NoMemory();
43
result->dict = PyDict_New();
45
return PyErr_NoMemory();
46
result->request_rec = req;
47
result->connection = NULL;
48
result->server = NULL;
52
result->headers_in = MpTable_FromTable(req->headers_in);
53
result->headers_out = MpTable_FromTable(req->headers_out);
54
result->err_headers_out = MpTable_FromTable(req->err_headers_out);
55
result->subprocess_env = MpTable_FromTable(req->subprocess_env);
56
result->notes = MpTable_FromTable(req->notes);
58
result->extension = NULL;
59
result->interpreter = NULL;
60
result->content_type_set = 0;
63
result->rbuff_pos = 0;
64
result->rbuff_len = 0;
66
return (PyObject *) result;
72
** request.add_common_vars(reqeust self)
74
* Interface to ap_add_common_vars. Adds a bunch of CGI
75
* environment variables.
79
static PyObject * req_add_common_vars(requestobject *self)
82
ap_add_common_vars(self->request_rec);
92
* utility func - makes sure a phase is valid
95
static int valid_phase(const char *p)
97
if ((strcmp(p, "PythonHandler") != 0) &&
98
(strcmp(p, "PythonAuthenHandler") != 0) &&
99
(strcmp(p, "PythonPostReadRequestHandler") != 0) &&
100
(strcmp(p, "PythonTransHandler") != 0) &&
101
(strcmp(p, "PythonHeaderParserHandler") != 0) &&
102
(strcmp(p, "PythonAccessHandler") != 0) &&
103
(strcmp(p, "PythonAuthzHandler") != 0) &&
104
(strcmp(p, "PythonTypeHandler") != 0) &&
105
(strcmp(p, "PythonFixupHandler") != 0) &&
106
(strcmp(p, "PythonLogHandler") != 0) &&
107
(strcmp(p, "PythonInitHandler") != 0))
114
** request.add_handler(request self, string phase, string handler)
116
* Allows to add another handler to the handler list.
119
static PyObject *req_add_handler(requestobject *self, PyObject *args)
123
const char *dir = NULL;
124
const char *currphase;
126
if (! PyArg_ParseTuple(args, "ss|s", &phase, &handler, &dir))
129
if (! valid_phase(phase)) {
130
PyErr_SetString(PyExc_IndexError,
131
apr_psprintf(self->request_rec->pool,
132
"Invalid phase: %s", phase));
136
/* which phase are we processing? */
137
currphase = PyString_AsString(self->phase);
139
/* are we in same phase as what's being added? */
140
if (strcmp(currphase, phase) == 0) {
142
/* then just append to hlist */
143
hlist_append(self->request_rec->pool, self->hlo->head,
147
/* this is a phase that we're not in */
149
py_req_config *req_config;
152
/* get request config */
153
req_config = (py_req_config *)
154
ap_get_module_config(self->request_rec->request_config,
157
hle = apr_hash_get(req_config->dynhls, phase, APR_HASH_KEY_STRING);
160
hle = hlist_new(self->request_rec->pool, handler, dir, 0);
161
apr_hash_set(req_config->dynhls, phase, APR_HASH_KEY_STRING, hle);
164
hlist_append(self->request_rec->pool, hle, handler, dir, 0);
173
** request.allow_methods(request self, list methods, reset=0)
175
* a wrapper around ap_allow_methods. (used for the "allow:" header
176
* to be passed to client when needed.)
179
static PyObject *req_allow_methods(requestobject *self, PyObject *args)
186
if (! PyArg_ParseTuple(args, "O|i", &methods, &reset))
189
if (! PySequence_Check(methods)){
190
PyErr_SetString(PyExc_TypeError,
191
"First argument must be a sequence");
195
len = PySequence_Length(methods);
201
method = PySequence_GetItem(methods, 0);
202
if (! PyString_Check(method)) {
203
PyErr_SetString(PyExc_TypeError,
204
"Methods must be strings");
208
ap_allow_methods(self->request_rec, (reset == REPLACE_ALLOW),
209
PyString_AS_STRING(method), NULL);
211
for (i = 1; i < len; i++) {
212
method = PySequence_GetItem(methods, i);
213
if (! PyString_Check(method)) {
214
PyErr_SetString(PyExc_TypeError,
215
"Methods must be strings");
219
ap_allow_methods(self->request_rec, MERGE_ALLOW,
220
PyString_AS_STRING(method), NULL);
229
** request.document_root(self)
231
* ap_docuement_root wrapper
234
static PyObject *req_document_root(requestobject *self)
237
return PyString_FromString(ap_document_root(self->request_rec));
242
** request.get_basic_auth_pw(request self)
244
* get basic authentication password,
245
* similar to ap_get_basic_auth_pw
248
static PyObject * req_get_basic_auth_pw(requestobject *self, PyObject *args)
253
req = self->request_rec;
255
if (! ap_get_basic_auth_pw(req, &pw))
256
return PyString_FromString(pw);
264
** request.get_addhandler_exts(request self)
266
* Returns file extentions that were given as argument to AddHandler mod_mime
267
* directive, if any, if at all. This is useful for the Publisher, which can
268
* chop off file extentions for modules based on this info.
270
* XXX Due to the way this is implemented, it is best stay undocumented.
273
static PyObject * req_get_addhandler_exts(requestobject *self, PyObject *args)
276
char *exts = get_addhandler_extensions(self->request_rec);
279
return PyString_FromString(exts);
281
return PyString_FromString("");
285
** request.get_config(request self)
287
* Returns the config directives set through Python* apache directives.
288
* except for Python*Handler and PythonOption (which you get via get_options).
291
static PyObject * req_get_config(requestobject *self)
294
(py_config *) ap_get_module_config(self->request_rec->per_dir_config,
296
return MpTable_FromTable(conf->directives);
300
** request.get_remodte_host(request self, [int type])
302
* An interface to the ap_get_remote_host function.
305
static PyObject * req_get_remote_host(requestobject *self, PyObject *args)
308
int type = REMOTE_NAME;
309
PyObject *str_is_ip = Py_None;
313
if (! PyArg_ParseTuple(args, "|iO", &type, &str_is_ip))
316
if (str_is_ip != Py_None) {
317
host = ap_get_remote_host(self->request_rec->connection,
318
self->request_rec->per_dir_config, type, &_str_is_ip);
321
host = ap_get_remote_host(self->request_rec->connection,
322
self->request_rec->per_dir_config, type, NULL);
330
if (str_is_ip != Py_None) {
331
return Py_BuildValue("(s,i)", host, _str_is_ip);
334
return PyString_FromString(host);
340
** request.get_options(request self)
344
static PyObject * req_get_options(requestobject *self, PyObject *args)
347
(py_config *) ap_get_module_config(self->request_rec->per_dir_config,
349
return MpTable_FromTable(conf->options);
353
** request.internal_redirect(request self, string newuri)
357
static PyObject * req_internal_redirect(requestobject *self, PyObject *args)
361
if (! PyArg_ParseTuple(args, "z", &new_uri))
362
return NULL; /* error */
364
Py_BEGIN_ALLOW_THREADS
365
ap_internal_redirect(new_uri, self->request_rec);
373
** request.log_error(req self, string message, int level)
375
* calls ap_log_rerror
378
static PyObject * req_log_error(requestobject *self, PyObject *args)
381
char *message = NULL;
383
if (! PyArg_ParseTuple(args, "z|i", &message, &level))
384
return NULL; /* error */
389
level = APLOG_NOERRNO|APLOG_ERR;
391
ap_log_rerror(APLOG_MARK, level, 0, self->request_rec, "%s", message);
399
** request.read(request self, int bytes)
401
* Reads stuff like POST requests from the client
402
* (based on the old net_read)
405
static PyObject * req_read(requestobject *self, PyObject *args)
407
int rc, bytes_read, chunk_len;
413
if (! PyArg_ParseTuple(args, "|l", &len))
417
return PyString_FromString("");
420
/* is this the first read? */
421
if (! self->request_rec->read_length) {
423
/* then do some initial setting up */
424
rc = ap_setup_client_block(self->request_rec, REQUEST_CHUNKED_ERROR);
426
PyObject *val = PyInt_FromLong(rc);
429
PyErr_SetObject(get_ServerReturn(), val);
434
if (! ap_should_client_block(self->request_rec)) {
435
/* client has nothing to send */
436
return PyString_FromString("");
441
/* XXX ok to use request_rec->remaining? */
442
len = self->request_rec->remaining +
443
(self->rbuff_len - self->rbuff_pos);
445
result = PyString_FromStringAndSize(NULL, len);
447
/* possibly no more memory */
451
buffer = PyString_AS_STRING((PyStringObject *) result);
453
/* if anything left in the readline buffer */
454
while ((self->rbuff_pos < self->rbuff_len) && (copied < len))
455
buffer[copied++] = self->rbuff[self->rbuff_pos++];
457
/* Free rbuff if we're done with it */
458
if (self->rbuff_pos >= self->rbuff_len && self->rbuff != NULL) {
464
return result; /* we're done! */
467
Py_BEGIN_ALLOW_THREADS
468
chunk_len = ap_get_client_block(self->request_rec, buffer, len);
470
bytes_read = chunk_len;
472
/* if this is a "short read", try reading more */
473
while ((bytes_read < len) && (chunk_len != 0)) {
474
Py_BEGIN_ALLOW_THREADS
475
chunk_len = ap_get_client_block(self->request_rec,
476
buffer+bytes_read, len-bytes_read);
478
if (chunk_len == -1) {
479
PyErr_SetObject(PyExc_IOError,
480
PyString_FromString("Client read error (Timeout?)"));
484
bytes_read += chunk_len;
487
/* resize if necessary */
488
if (bytes_read < len)
489
if(_PyString_Resize(&result, bytes_read))
496
** request.readline(request self, int maxbytes)
498
* Reads stuff like POST requests from the client
499
* (based on the old net_read) until EOL
502
static PyObject * req_readline(requestobject *self, PyObject *args)
505
int rc, chunk_len, bytes_read;
511
if (! PyArg_ParseTuple(args, "|l", &len))
515
return PyString_FromString("");
518
/* is this the first read? */
519
if (! self->request_rec->read_length) {
521
/* then do some initial setting up */
522
rc = ap_setup_client_block(self->request_rec, REQUEST_CHUNKED_ERROR);
525
PyObject *val = PyInt_FromLong(rc);
528
PyErr_SetObject(get_ServerReturn(), val);
533
if (! ap_should_client_block(self->request_rec)) {
534
/* client has nothing to send */
535
return PyString_FromString("");
540
len = self->request_rec->remaining +
541
(self->rbuff_len - self->rbuff_pos);
543
/* create the result buffer */
544
result = PyString_FromStringAndSize(NULL, len);
546
/* possibly no more memory */
550
buffer = PyString_AS_STRING((PyStringObject *) result);
552
/* is there anything left in the rbuff from previous reads? */
553
if (self->rbuff_pos < self->rbuff_len) {
555
/* if yes, process that first */
556
while (self->rbuff_pos < self->rbuff_len) {
558
buffer[copied++] = self->rbuff[self->rbuff_pos];
559
if ((self->rbuff[self->rbuff_pos++] == '\n') ||
562
/* our work is done */
564
/* resize if necessary */
566
if(_PyString_Resize(&result, copied))
574
/* Free old rbuff as the old contents have been copied over and
575
we are about to allocate a new rbuff. Perhaps this could be reused
577
if (self->rbuff_pos >= self->rbuff_len && self->rbuff != NULL)
583
/* if got this far, the buffer should be empty, we need to read more */
585
/* create a read buffer */
586
self->rbuff_len = len > HUGE_STRING_LEN ? len : HUGE_STRING_LEN;
587
self->rbuff_pos = self->rbuff_len;
588
self->rbuff = malloc(self->rbuff_len);
590
return PyErr_NoMemory();
593
Py_BEGIN_ALLOW_THREADS
594
chunk_len = ap_get_client_block(self->request_rec, self->rbuff,
596
Py_END_ALLOW_THREADS;
597
bytes_read = chunk_len;
599
/* if this is a "short read", try reading more */
600
while ((chunk_len != 0 ) && (bytes_read + copied < len)) {
602
Py_BEGIN_ALLOW_THREADS
603
chunk_len = ap_get_client_block(self->request_rec,
604
self->rbuff + bytes_read,
605
self->rbuff_len - bytes_read);
608
if (chunk_len == -1) {
610
/* Free rbuff since returning NULL here should end the request */
614
PyErr_SetObject(PyExc_IOError,
615
PyString_FromString("Client read error (Timeout?)"));
619
bytes_read += chunk_len;
621
self->rbuff_len = bytes_read;
624
/* now copy the remaining bytes */
625
while (self->rbuff_pos < self->rbuff_len) {
627
buffer[copied++] = self->rbuff[self->rbuff_pos];
628
if ((self->rbuff[self->rbuff_pos++] == '\n') ||
630
/* our work is done */
634
/* Free rbuff if we're done with it */
635
if (self->rbuff_pos >= self->rbuff_len && self->rbuff != NULL)
641
/* resize if necessary */
643
if(_PyString_Resize(&result, copied))
650
** request.readlines([long maxsize])
652
* just like file.readlines()
655
static PyObject *req_readlines(requestobject *self, PyObject *args)
658
PyObject *result = PyList_New(0);
659
PyObject *line, *rlargs;
663
if (! PyArg_ParseTuple(args, "|l", &sizehint))
667
return PyErr_NoMemory();
669
rlargs = PyTuple_New(0);
671
return PyErr_NoMemory();
673
line = req_readline(self, rlargs);
674
while (line && !(strcmp(PyString_AsString(line), "") == 0)) {
675
PyList_Append(result, line);
676
size += PyString_Size(line);
677
if ((sizehint != -1) && (size >= size))
679
line = req_readline(self, args);
689
** request.register_cleanup(handler, data)
691
* registers a cleanup at request pool destruction time.
692
* optional data argument will be passed to the cleanup function.
695
static PyObject *req_register_cleanup(requestobject *self, PyObject *args)
698
PyObject *handler = NULL;
699
PyObject *data = NULL;
701
if (! PyArg_ParseTuple(args, "O|O", &handler, &data))
702
return NULL; /* bad args */
704
ci = (cleanup_info *)malloc(sizeof(cleanup_info));
705
ci->request_rec = self->request_rec;
706
ci->server_rec = self->request_rec->server;
707
if (PyCallable_Check(handler)) {
709
ci->handler = handler;
710
ci->interpreter = self->interpreter;
721
PyErr_SetString(PyExc_ValueError,
722
"first argument must be a callable object");
727
apr_pool_cleanup_register(self->request_rec->pool, ci, python_cleanup,
728
apr_pool_cleanup_null);
736
** request.requires(self)
738
* Interface to ap_requires()
741
static PyObject * req_requires(requestobject *self)
744
/* This function returns everything specified after the "requires"
745
as is, without any attempts to parse/organize because
746
"requires" args only need to be grokable by mod_auth if it is
747
authoritative. When AuthAuthoritative is off, anything can
748
follow requires, e.g. "requires role terminator".
751
const apr_array_header_t *reqs_arr = ap_requires(self->request_rec);
758
return Py_BuildValue("()");
761
result = PyTuple_New(reqs_arr->nelts);
763
reqs = (require_line *) reqs_arr->elts;
765
for (i = 0; i < reqs_arr->nelts; ++i) {
766
if (reqs[i].method_mask & (AP_METHOD_BIT << self->request_rec->method_number)) {
767
PyTuple_SetItem(result, ti++,
768
PyString_FromString(reqs[i].requirement));
772
_PyTuple_Resize(&result, ti);
778
** request.send_http_header(request self)
780
* this is a noop, just so we don't break old scripts
783
static PyObject * req_send_http_header(requestobject *self)
790
** request.set_content_length(request self, long content_length)
792
* write output to the client
795
static PyObject * req_set_content_length(requestobject *self, PyObject *args)
799
if (! PyArg_ParseTuple(args, "l", &len))
800
return NULL; /* bad args */
802
ap_set_content_length(self->request_rec, len);
809
** request.write(request self, string what, flush=1)
811
* write output to the client
814
static PyObject * req_write(requestobject *self, PyObject *args)
821
if (! PyArg_ParseTuple(args, "s#|i", &buff, &len, &flush))
822
return NULL; /* bad args */
826
Py_BEGIN_ALLOW_THREADS
827
rc = ap_rwrite(buff, len, self->request_rec);
828
if (flush && (rc != -1))
829
rc = ap_rflush(self->request_rec);
832
PyErr_SetString(PyExc_IOError, "Write failed, client closed connection.");
843
** request.flush(request self)
845
* flush output buffer
848
static PyObject * req_flush(requestobject *self)
852
Py_BEGIN_ALLOW_THREADS
853
rc = ap_rflush(self->request_rec);
856
PyErr_SetString(PyExc_IOError, "Flush failed, client closed connection.");
869
static PyObject * req_sendfile(requestobject *self, PyObject *args)
873
apr_size_t offset=0, len=-1, nbytes;
875
PyObject * py_result = NULL;
878
if (! PyArg_ParseTuple(args, "s|ll", &fname, &offset, &len))
879
return NULL; /* bad args */
881
Py_BEGIN_ALLOW_THREADS
882
status=apr_stat(&finfo, fname,
883
APR_READ, self->request_rec->pool);
885
if (status != APR_SUCCESS) {
886
PyErr_SetString(PyExc_IOError, "Could not stat file for reading");
890
Py_BEGIN_ALLOW_THREADS
891
status=apr_file_open(&fd, fname,
892
APR_READ, finfo.protection,
893
self->request_rec->pool);
895
if (status != APR_SUCCESS) {
896
PyErr_SetString(PyExc_IOError, "Could not open file for reading");
900
if (len==-1) len=finfo.size;
902
Py_BEGIN_ALLOW_THREADS
903
status = ap_send_fd(fd, self->request_rec, offset,
907
if (status != APR_SUCCESS) {
908
PyErr_SetString(PyExc_IOError, "Write failed, client closed connection.");
912
py_result = PyLong_FromLong (nbytes);
913
Py_INCREF(py_result);
917
static PyMethodDef request_methods[] = {
918
{"add_common_vars", (PyCFunction) req_add_common_vars, METH_NOARGS},
919
{"add_handler", (PyCFunction) req_add_handler, METH_VARARGS},
920
{"allow_methods", (PyCFunction) req_allow_methods, METH_VARARGS},
921
{"document_root", (PyCFunction) req_document_root, METH_NOARGS},
922
{"flush", (PyCFunction) req_flush, METH_NOARGS},
923
{"get_basic_auth_pw", (PyCFunction) req_get_basic_auth_pw, METH_NOARGS},
924
{"get_addhandler_exts", (PyCFunction) req_get_addhandler_exts, METH_NOARGS},
925
{"get_config", (PyCFunction) req_get_config, METH_NOARGS},
926
{"get_remote_host", (PyCFunction) req_get_remote_host, METH_VARARGS},
927
{"get_options", (PyCFunction) req_get_options, METH_NOARGS},
928
{"internal_redirect", (PyCFunction) req_internal_redirect, METH_VARARGS},
929
{"log_error", (PyCFunction) req_log_error, METH_VARARGS},
930
{"read", (PyCFunction) req_read, METH_VARARGS},
931
{"readline", (PyCFunction) req_readline, METH_VARARGS},
932
{"readlines", (PyCFunction) req_readlines, METH_VARARGS},
933
{"register_cleanup", (PyCFunction) req_register_cleanup, METH_VARARGS},
934
{"requires", (PyCFunction) req_requires, METH_NOARGS},
935
{"send_http_header", (PyCFunction) req_send_http_header, METH_NOARGS},
936
{"sendfile", (PyCFunction) req_sendfile, METH_VARARGS},
937
{"set_content_length", (PyCFunction) req_set_content_length, METH_VARARGS},
938
{"write", (PyCFunction) req_write, METH_VARARGS},
939
{ NULL, NULL } /* sentinel */
944
These are offsets into the Apache request_rec structure.
945
They are accessed via getset functions. Note that the types
946
specified here are irrelevant if a function other than
947
getreq_recmbr() is used. E.g. bytes_sent is a long long,
948
and is retrieved via getreq_recmbr_off() which ignores what's
952
#define OFF(x) offsetof(request_rec, x)
954
static struct PyMemberDef request_rec_mbrs[] = {
955
{"the_request", T_STRING, OFF(the_request)},
956
{"assbackwards", T_INT, OFF(assbackwards)},
957
{"proxyreq", T_INT, OFF(proxyreq)},
958
{"header_only", T_INT, OFF(header_only)},
959
{"protocol", T_STRING, OFF(protocol)},
960
{"proto_num", T_INT, OFF(proto_num)},
961
{"hostname", T_STRING, OFF(hostname)},
962
{"request_time", T_LONG, OFF(request_time)},
963
{"status_line", T_STRING, OFF(status_line)},
964
{"status", T_INT, OFF(status)},
965
{"method", T_STRING, OFF(method)},
966
{"method_number", T_INT, OFF(method_number)},
967
{"allowed", T_LONG, OFF(allowed)},
968
{"allowed_xmethods", T_OBJECT, OFF(allowed_xmethods)},
969
{"allowed_methods", T_OBJECT, OFF(allowed_methods)},
970
{"sent_bodyct", T_LONG, OFF(sent_bodyct)},
971
{"bytes_sent", T_LONG, OFF(bytes_sent)},
972
{"mtime", T_LONG, OFF(mtime)},
973
{"chunked", T_INT, OFF(chunked)},
974
{"range", T_STRING, OFF(range)},
975
{"clength", T_LONG, OFF(clength)},
976
{"remaining", T_LONG, OFF(remaining)},
977
{"read_length", T_LONG, OFF(read_length)},
978
{"read_body", T_INT, OFF(read_body)},
979
{"read_chunked", T_INT, OFF(read_chunked)},
980
{"expecting_100", T_INT, OFF(expecting_100)},
981
{"content_type", T_STRING, OFF(content_type)},
982
{"handler", T_STRING, OFF(handler)},
983
{"content_encoding", T_STRING, OFF(content_encoding)},
984
{"content_languages", T_OBJECT, OFF(content_languages)},
985
{"vlist_validator", T_STRING, OFF(vlist_validator)},
986
{"user", T_STRING, OFF(user)},
987
{"ap_auth_type", T_STRING, OFF(ap_auth_type)},
988
{"no_cache", T_INT, OFF(no_cache)},
989
{"no_local_copy", T_INT, OFF(no_local_copy)},
990
{"unparsed_uri", T_STRING, OFF(unparsed_uri)},
991
{"uri", T_STRING, OFF(uri)},
992
{"filename", T_STRING, OFF(filename)},
993
{"canonical_filename", T_STRING, OFF(canonical_filename)},
994
{"path_info", T_STRING, OFF(path_info)},
995
{"args", T_STRING, OFF(args)},
996
{"finfo", T_OBJECT, OFF(finfo)},
997
{"parsed_uri", T_OBJECT, OFF(parsed_uri)},
998
{"used_path_info", T_INT, OFF(used_path_info)},
999
{"eos_sent", T_INT, OFF(eos_sent)},
1000
{NULL} /* Sentinel */
1006
* Retrieves request_rec structure members
1009
static PyObject *getreq_recmbr(requestobject *self, void *name)
1012
* apparently at least ap_internal_fast_redirect blatently
1013
* substitute request members, and so we always have to make
1014
* sure that various apr_tables referenced haven't been
1015
* replaced in between handlers and we're left with a stale.
1018
if (strcmp(name, "headers_in") == 0) {
1019
if (((tableobject*)self->headers_in)->table != self->request_rec->headers_in)
1020
((tableobject*)self->headers_in)->table = self->request_rec->headers_in;
1021
Py_INCREF(self->headers_in);
1022
return self->headers_in;
1024
else if (strcmp(name, "headers_out") == 0) {
1025
if (((tableobject*)self->headers_out)->table != self->request_rec->headers_out)
1026
((tableobject*)self->headers_out)->table = self->request_rec->headers_out;
1027
Py_INCREF(self->headers_out);
1028
return self->headers_out;
1030
else if (strcmp(name, "err_headers_out") == 0) {
1031
if (((tableobject*)self->err_headers_out)->table != self->request_rec->err_headers_out)
1032
((tableobject*)self->err_headers_out)->table = self->request_rec->err_headers_out;
1033
Py_INCREF(self->err_headers_out);
1034
return self->err_headers_out;
1036
else if (strcmp(name, "subprocess_env") == 0) {
1037
if (((tableobject*)self->subprocess_env)->table != self->request_rec->subprocess_env)
1038
((tableobject*)self->subprocess_env)->table = self->request_rec->subprocess_env;
1039
Py_INCREF(self->subprocess_env);
1040
return self->subprocess_env;
1042
else if (strcmp(name, "notes") == 0) {
1043
if (((tableobject*)self->notes)->table != self->request_rec->notes)
1044
((tableobject*)self->notes)->table = self->request_rec->notes;
1045
Py_INCREF(self->notes);
1049
return PyMember_GetOne((char*)self->request_rec,
1050
find_memberdef(request_rec_mbrs, name));
1056
* Sets request_rec structure members
1059
static int setreq_recmbr(requestobject *self, PyObject *val, void *name)
1062
if (strcmp(name, "content_type") == 0) {
1063
if (! PyString_Check(val)) {
1064
PyErr_SetString(PyExc_TypeError, "content_type must be a string");
1067
ap_set_content_type(self->request_rec,
1068
apr_pstrdup(self->request_rec->pool,
1069
PyString_AsString(val)));
1070
self->content_type_set = 1;
1073
else if (strcmp(name, "user") == 0) {
1074
if (! PyString_Check(val)) {
1075
PyErr_SetString(PyExc_TypeError, "user must be a string");
1078
self->request_rec->user =
1079
apr_pstrdup(self->request_rec->pool, PyString_AsString(val));
1082
else if (strcmp(name, "filename") == 0) {
1083
if (! PyString_Check(val)) {
1084
PyErr_SetString(PyExc_TypeError, "filename must be a string");
1087
self->request_rec->filename =
1088
apr_pstrdup(self->request_rec->pool, PyString_AsString(val));
1092
return PyMember_SetOne((char*)self->request_rec,
1093
find_memberdef(request_rec_mbrs, (char*)name),
1098
** getreq_recmbr_time
1100
* Retrieves apr_time_t request_rec members
1103
static PyObject *getreq_recmbr_time(requestobject *self, void *name)
1105
PyMemberDef *md = find_memberdef(request_rec_mbrs, name);
1106
char *addr = (char *)self->request_rec + md->offset;
1107
apr_time_t time = *(apr_time_t*)addr;
1108
return PyFloat_FromDouble(time*0.000001);
1112
** getreq_recmbr_off
1114
* Retrieves apr_off_t request_rec members
1117
static PyObject *getreq_recmbr_off(requestobject *self, void *name)
1119
PyMemberDef *md = find_memberdef(request_rec_mbrs, name);
1120
char *addr = (char *)self->request_rec + md->offset;
1121
if (sizeof(apr_off_t) == sizeof(LONG_LONG)) {
1122
LONG_LONG l = *(LONG_LONG*)addr;
1123
return PyLong_FromLongLong(l);
1126
/* assume it's long */
1127
long l = *(long*)addr;
1128
return PyLong_FromLong(l);
1135
* For array headers that will get converted to tuple
1138
static PyObject *getreq_rec_ah(requestobject *self, void *name)
1140
const PyMemberDef *md = find_memberdef(request_rec_mbrs, name);
1141
apr_array_header_t *ah =
1142
(apr_array_header_t *)((char *)self->request_rec + md->offset);
1144
return tuple_from_array_header(ah);
1150
* For method lists that will get converted to tuple
1153
static PyObject *getreq_rec_ml(requestobject *self, void *name)
1155
const PyMemberDef *md = find_memberdef(request_rec_mbrs, (char*)name);
1156
ap_method_list_t *ml =
1157
(ap_method_list_t *)((char *)self->request_rec + md->offset);
1159
return tuple_from_method_list(ml);
1165
* For file info that will get converted to tuple
1168
static PyObject *getreq_rec_fi(requestobject *self, void *name)
1170
const PyMemberDef *md = find_memberdef(request_rec_mbrs, (char*)name);
1172
(apr_finfo_t *)((char *)self->request_rec + md->offset);
1174
return tuple_from_finfo(fi);
1180
* For parsed uri that will get converted to tuple
1183
static PyObject *getreq_rec_uri(requestobject *self, void *name)
1185
const PyMemberDef *md = find_memberdef(request_rec_mbrs, (char*)name);
1186
apr_uri_t *uri = (apr_uri_t *)((char *)self->request_rec + md->offset);
1188
return tuple_from_apr_uri(uri);
1194
* A getter func that creates an object as needed.
1197
static PyObject *getmakeobj(requestobject* self, void *objname)
1199
char *name = (char *)objname;
1200
PyObject *result = NULL;
1202
if (strcmp(name, "connection") == 0) {
1203
if (!self->connection && self->request_rec->connection)
1204
self->connection = MpConn_FromConn(self->request_rec->connection);
1205
result = self->connection;
1207
else if (strcmp(name, "server") == 0) {
1208
if (!self->server && self->request_rec->server)
1209
self->server = MpServer_FromServer(self->request_rec->server);
1210
result = self->server;
1212
else if (strcmp(name, "next") == 0) {
1213
if (!self->next && self->request_rec->next)
1214
self->next = MpRequest_FromRequest(self->request_rec->next);
1215
result = self->next;
1217
else if (strcmp(name, "prev") == 0) {
1218
if (!self->prev && self->request_rec->prev)
1219
self->prev = MpRequest_FromRequest(self->request_rec->prev);
1220
result = self->prev;
1222
else if (strcmp(name, "main") == 0) {
1223
if (!self->main && self->request_rec->main)
1224
self->main = MpRequest_FromRequest(self->request_rec->main);
1225
result = self->main;
1236
static PyGetSetDef request_getsets[] = {
1237
{"connection", (getter)getmakeobj, NULL, "Connection object", "connection"},
1238
{"server", (getter)getmakeobj, NULL, "Server object", "server"},
1239
{"next", (getter)getmakeobj, NULL, "If redirected, pointer to the to request", "next"},
1240
{"prev", (getter)getmakeobj, NULL, "If redirected, pointer to the from request", "prev"},
1241
{"main", (getter)getmakeobj, NULL, "If subrequest, pointer to the main request", "main"},
1242
{"the_request", (getter)getreq_recmbr, NULL, "First line of request", "the_request"},
1243
{"assbackwards", (getter)getreq_recmbr, (setter)setreq_recmbr, "HTTP/0.9 \"simple\" request", "assbackwards"},
1244
{"proxyreq", (getter)getreq_recmbr, NULL, "A proxy request: one of apache.PROXYREQ_* values", "proxyreq"},
1245
{"header_only", (getter)getreq_recmbr, NULL, "HEAD request, as oppsed to GET", "header_only"},
1246
{"protocol", (getter)getreq_recmbr, NULL, "Protocol as given to us, or HTTP/0.9", "protocol"},
1247
{"proto_num", (getter)getreq_recmbr, NULL, "Protocol version. 1.1 = 1001", "proto_num"},
1248
{"hostname", (getter)getreq_recmbr, NULL, "Host, as set by full URI or Host:", "hostname"},
1249
{"request_time", (getter)getreq_recmbr_time, NULL, "When request started", "request_time"},
1250
{"status_line", (getter)getreq_recmbr, NULL, "Status line, if set by script", "status_line"},
1251
{"status", (getter)getreq_recmbr, (setter)setreq_recmbr, "Status", "status"},
1252
{"method", (getter)getreq_recmbr, NULL, "Request method", "method"},
1253
{"method_number", (getter)getreq_recmbr, NULL, "Request method number, one of apache.M_*", "method_number"},
1254
{"allowed", (getter)getreq_recmbr, NULL, "Status", "allowed"},
1255
{"allowed_xmethods", (getter)getreq_rec_ah, NULL, "Allowed extension methods", "allowed_xmethods"},
1256
{"allowed_methods", (getter)getreq_rec_ml, NULL, "Allowed methods", "allowed_methods"},
1257
{"sent_bodyct", (getter)getreq_recmbr_off, NULL, "Byte count in stream for body", "sent_bodyct"},
1258
{"bytes_sent", (getter)getreq_recmbr_off, NULL, "Bytes sent", "bytes_sent"},
1259
{"mtime", (getter)getreq_recmbr_time, NULL, "Time resource was last modified", "mtime"},
1260
{"chunked", (getter)getreq_recmbr, NULL, "Sending chunked transfer-coding", "chunked"},
1261
{"boundary", (getter)getreq_recmbr, NULL, "Multipart/byteranges boundary", "boundary"},
1262
{"range", (getter)getreq_recmbr, NULL, "The Range: header", "range"},
1263
{"clength", (getter)getreq_recmbr_off, NULL, "The \"real\" contenct length", "clength"},
1264
{"remaining", (getter)getreq_recmbr_off, NULL, "Bytes left to read", "remaining"},
1265
{"read_length", (getter)getreq_recmbr_off, NULL, "Bytes that have been read", "read_length"},
1266
{"read_body", (getter)getreq_recmbr, NULL, "How the request body should be read", "read_body"},
1267
{"read_chunked", (getter)getreq_recmbr, NULL, "Reading chunked transfer-coding", "read_chunked"},
1268
{"expecting_100", (getter)getreq_recmbr, NULL, "Is client waitin for a 100 response?", "expecting_100"},
1269
{"content_type", (getter)getreq_recmbr, (setter)setreq_recmbr, "Content type", "content_type"},
1270
{"handler", (getter)getreq_recmbr, NULL, "The handler string", "handler"},
1271
{"content_encoding", (getter)getreq_recmbr, NULL, "How to encode the data", "content_encoding"},
1272
{"content_languages", (getter)getreq_rec_ah, NULL, "Content languages", "content_languages"},
1273
{"vlist_validator", (getter)getreq_recmbr, NULL, "Variant list validator (if negotiated)", "vlist_validator"},
1274
{"user", (getter)getreq_recmbr, (setter)setreq_recmbr, "If authentication check was made, the user name", "user"},
1275
{"ap_auth_type", (getter)getreq_recmbr, NULL, "If authentication check was made, auth type", "ap_auth_type"},
1276
{"no_cache", (getter)getreq_recmbr, NULL, "This response in non-cacheable", "no_cache"},
1277
{"no_local_copy", (getter)getreq_recmbr, NULL, "There is no local copy of the response", "no_local_copy"},
1278
{"unparsed_uri", (getter)getreq_recmbr, NULL, "The URI without any parsing performed", "unparsed_uri"},
1279
{"uri", (getter)getreq_recmbr, NULL, "The path portion of URI", "uri"},
1280
{"filename", (getter)getreq_recmbr, (setter)setreq_recmbr, "The file name on disk that this request corresponds to", "filename"},
1281
{"canonical_filename", (getter)getreq_recmbr, NULL, "The true filename (req.filename is canonicalized if they dont match)", "canonical_filename"},
1282
{"path_info", (getter)getreq_recmbr, NULL, "Path_info, if any", "path_info"},
1283
{"args", (getter)getreq_recmbr, NULL, "QUERY_ARGS, if any", "args"},
1284
{"finfo", (getter)getreq_rec_fi, NULL, "File information", "finfo"},
1285
{"parsed_uri", (getter)getreq_rec_uri, NULL, "Components of URI", "parsed_uri"},
1286
{"used_path_info", (getter)getreq_recmbr, NULL, "Flag to accept or reject path_info on current request", "used_path_info"},
1287
{"headers_in", (getter)getreq_recmbr, NULL, "Incoming headers", "headers_in"},
1288
{"headers_out", (getter)getreq_recmbr, NULL, "Outgoing headers", "headers_out"},
1289
{"err_headers_out", (getter)getreq_recmbr, NULL, "Outgoing headers for errors", "err_headers_out"},
1290
{"subprocess_env", (getter)getreq_recmbr, NULL, "Subprocess environment", "subprocess_env"},
1291
{"notes", (getter)getreq_recmbr, NULL, "Notes", "notes"},
1292
/* XXX per_dir_config */
1293
/* XXX request_config */
1295
/* XXX filters and eos */
1296
{"eos_sent", (getter)getreq_recmbr, NULL, "EOS bucket sent", "eos_sent"},
1297
{NULL} /* Sentinel */
1301
#define OFF(x) offsetof(requestobject, x)
1303
static struct PyMemberDef request_members[] = {
1304
{"_content_type_set", T_INT, OFF(content_type_set), RO},
1305
{"phase", T_OBJECT, OFF(phase), RO},
1306
{"extension", T_STRING, OFF(extension), RO},
1307
{"interpreter", T_STRING, OFF(interpreter), RO},
1308
{"hlist", T_OBJECT, OFF(hlo), RO},
1309
{NULL} /* Sentinel */
1318
static void request_dealloc(requestobject *self)
1320
Py_XDECREF(self->dict);
1321
Py_XDECREF(self->connection);
1322
Py_XDECREF(self->server);
1323
Py_XDECREF(self->next);
1324
Py_XDECREF(self->prev);
1325
Py_XDECREF(self->main);
1326
Py_XDECREF(self->headers_in);
1327
Py_XDECREF(self->headers_out);
1328
Py_XDECREF(self->err_headers_out);
1329
Py_XDECREF(self->subprocess_env);
1330
Py_XDECREF(self->notes);
1331
Py_XDECREF(self->phase);
1332
Py_XDECREF(self->hlo);
1337
static char request_doc[] =
1338
"Apache request_rec structure\n";
1340
PyTypeObject MpRequest_Type = {
1341
PyObject_HEAD_INIT(NULL)
1344
sizeof(requestobject),
1346
(destructor) request_dealloc, /*tp_dealloc*/
1353
0, /*tp_as_sequence*/
1354
0, /*tp_as_mapping*/
1358
PyObject_GenericGetAttr, /* tp_getattro */
1359
PyObject_GenericSetAttr, /* tp_setattro */
1360
0, /* tp_as_buffer */
1361
Py_TPFLAGS_DEFAULT |
1362
Py_TPFLAGS_BASETYPE, /* tp_flags */
1363
request_doc, /* tp_doc */
1364
0, /* tp_traverse */
1366
0, /* tp_richcompare */
1367
0, /* tp_weaklistoffset */
1369
0, /* tp_iternext */
1370
request_methods, /* tp_methods */
1371
request_members, /* tp_members */
1372
request_getsets, /* tp_getset */
1375
0, /* tp_descr_get */
1376
0, /* tp_descr_set */
1377
offsetof(requestobject, dict), /* tp_dictoffset */
1381
(destructor)request_dealloc, /* tp_free */