Merge lp:~ubuntu-branches/ubuntu/natty/mod-wsgi/natty-201104071155 into lp:ubuntu/natty/mod-wsgi

Proposed by James Westby
Status: Rejected
Rejected by: Colin Watson
Proposed branch: lp:~ubuntu-branches/ubuntu/natty/mod-wsgi/natty-201104071155
Merge into: lp:ubuntu/natty/mod-wsgi
Diff against target: 15059 lines (+15019/-0) (has conflicts)
7 files modified
.pc/.quilt_patches (+1/-0)
.pc/.quilt_series (+1/-0)
.pc/.version (+1/-0)
.pc/applied-patches (+1/-0)
.pc/fix-python-3.2-lp-749720.patch/mod_wsgi.c (+14969/-0)
debian/patches/fix-python-3.2-lp-749720.patch (+45/-0)
debian/patches/series (+1/-0)
Conflict adding file .pc.  Moved existing file to .pc.moved.
Conflict adding file debian/patches.  Moved existing file to debian/patches.moved.
To merge this branch: bzr merge lp:~ubuntu-branches/ubuntu/natty/mod-wsgi/natty-201104071155
Reviewer Review Type Date Requested Status
Dave Walker (community) Disapprove
Daniel Holbach (community) Needs Fixing
Ubuntu branches Pending
Review via email: mp+56743@code.launchpad.net

Description of the change

The package history in the archive and the history in the bzr branch differ. As the archive is authoritative the history of lp:ubuntu/natty/mod-wsgi now reflects that and the old bzr branch has been pushed to lp:~ubuntu-branches/ubuntu/natty/mod-wsgi/natty-201104071155. A merge should be performed if necessary.

To post a comment you must log in.
Revision history for this message
Daniel Holbach (dholbach) wrote :

There's conflicts. This can't be merged as is.

Revision history for this message
Daniel Holbach (dholbach) :
review: Needs Fixing
Revision history for this message
Dave Walker (davewalker) wrote :

This is fallout from UDD vs Developer push to lp:ubuntu/* branch. Merge proposal contains same delta.

review: Disapprove

Unmerged revisions

16. By James Page

* Updates to support python >= 3.2 (LP: #749720):
  - Picked patch to support python >= 3.2 from upstream trunk.
  - Fixed libapache2-mod-wsgi-py3.postinst to correctly detect
    default python 3 version.
* Converted to source/format "3.0 (quilt)" and updated debhelper
  version (>= 7.0.15).

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory '.pc'
2=== renamed directory '.pc' => '.pc.moved'
3=== added file '.pc/.quilt_patches'
4--- .pc/.quilt_patches 1970-01-01 00:00:00 +0000
5+++ .pc/.quilt_patches 2011-04-07 12:02:22 +0000
6@@ -0,0 +1,1 @@
7+debian/patches
8
9=== added file '.pc/.quilt_series'
10--- .pc/.quilt_series 1970-01-01 00:00:00 +0000
11+++ .pc/.quilt_series 2011-04-07 12:02:22 +0000
12@@ -0,0 +1,1 @@
13+series
14
15=== added file '.pc/.version'
16--- .pc/.version 1970-01-01 00:00:00 +0000
17+++ .pc/.version 2011-04-07 12:02:22 +0000
18@@ -0,0 +1,1 @@
19+2
20
21=== added file '.pc/applied-patches'
22--- .pc/applied-patches 1970-01-01 00:00:00 +0000
23+++ .pc/applied-patches 2011-04-07 12:02:22 +0000
24@@ -0,0 +1,1 @@
25+fix-python-3.2-lp-749720.patch
26
27=== added directory '.pc/fix-python-3.2-lp-749720.patch'
28=== added file '.pc/fix-python-3.2-lp-749720.patch/.timestamp'
29=== added file '.pc/fix-python-3.2-lp-749720.patch/mod_wsgi.c'
30--- .pc/fix-python-3.2-lp-749720.patch/mod_wsgi.c 1970-01-01 00:00:00 +0000
31+++ .pc/fix-python-3.2-lp-749720.patch/mod_wsgi.c 2011-04-07 12:02:22 +0000
32@@ -0,0 +1,14969 @@
33+/* vim: set sw=4 expandtab : */
34+
35+/*
36+ * Copyright 2007-2010 GRAHAM DUMPLETON
37+ *
38+ * Licensed under the Apache License, Version 2.0 (the "License");
39+ * you may not use this file except in compliance with the License.
40+ * You may obtain a copy of the License at
41+ *
42+ * http://www.apache.org/licenses/LICENSE-2.0
43+ *
44+ * Unless required by applicable law or agreed to in writing, software
45+ * distributed under the License is distributed on an "AS IS" BASIS,
46+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
47+ * See the License for the specific language governing permissions and
48+ * limitations under the License.
49+ */
50+
51+/*
52+ * Enabled access to Apache private API and data structures. Need to do
53+ * this to access the following:
54+ *
55+ * In Apache 1.3 it is not possible to access ap_check_cmd_context()
56+ * where as this was made public in Apache 2.0.
57+ *
58+ * In Apache 2.X need access to ap_create_request_config().
59+ *
60+ * In Apache 2.X need access to core_module and core_request_config.
61+ *
62+ */
63+
64+#define CORE_PRIVATE 1
65+
66+#include "httpd.h"
67+
68+#if !defined(HTTPD_ROOT)
69+#error Sorry, Apache developer package does not appear to be installed.
70+#endif
71+
72+#if !defined(AP_SERVER_MAJORVERSION_NUMBER)
73+#if AP_MODULE_MAGIC_AT_LEAST(20010224,0)
74+#define AP_SERVER_MAJORVERSION_NUMBER 2
75+#define AP_SERVER_MINORVERSION_NUMBER 0
76+#define AP_SERVER_PATCHLEVEL_NUMBER 0
77+#else
78+#define AP_SERVER_MAJORVERSION_NUMBER 1
79+#define AP_SERVER_MINORVERSION_NUMBER 3
80+#define AP_SERVER_PATCHLEVEL_NUMBER 0
81+#endif
82+#endif
83+
84+#if !defined(AP_SERVER_BASEVERSION)
85+#define AP_SERVER_BASEVERSION SERVER_BASEVERSION
86+#endif
87+
88+#if AP_SERVER_MAJORVERSION_NUMBER < 2
89+typedef int apr_status_t;
90+#define APR_SUCCESS 0
91+typedef pool apr_pool_t;
92+typedef unsigned int apr_port_t;
93+#include "ap_ctype.h"
94+#include "ap_alloc.h"
95+#define apr_isspace ap_isspace
96+#define apr_table_make ap_make_table
97+#define apr_table_get ap_table_get
98+#define apr_table_set ap_table_set
99+#define apr_table_setn ap_table_setn
100+#define apr_table_add ap_table_add
101+#define apr_table_elts ap_table_elts
102+#define apr_array_make ap_make_array
103+#define apr_array_push ap_push_array
104+#define apr_array_cat ap_array_cat
105+#define apr_array_append ap_append_arrays
106+typedef array_header apr_array_header_t;
107+typedef table apr_table_t;
108+typedef table_entry apr_table_entry_t;
109+typedef int apr_size_t;
110+typedef unsigned long apr_off_t;
111+#define apr_psprintf ap_psprintf
112+#define apr_pstrndup ap_pstrndup
113+#define apr_pstrdup ap_pstrdup
114+#define apr_pstrcat ap_pstrcat
115+#define apr_pcalloc ap_pcalloc
116+#define apr_palloc ap_palloc
117+#define apr_isalnum isalnum
118+#define apr_toupper toupper
119+typedef time_t apr_time_t;
120+#include "http_config.h"
121+typedef int apr_lockmech_e;
122+#else
123+#include "apr_lib.h"
124+#include "ap_mpm.h"
125+#include "ap_compat.h"
126+#include "apr_tables.h"
127+#include "apr_strings.h"
128+#include "http_config.h"
129+#include "ap_listen.h"
130+#include "apr_version.h"
131+#endif
132+
133+#include "ap_config.h"
134+#include "http_core.h"
135+#include "http_log.h"
136+#include "http_main.h"
137+#include "http_protocol.h"
138+#include "http_request.h"
139+#include "util_script.h"
140+#include "util_md5.h"
141+
142+#ifndef APR_FPROT_GWRITE
143+#define APR_FPROT_GWRITE APR_GWRITE
144+#endif
145+#ifndef APR_FPROT_WWRITE
146+#define APR_FPROT_WWRITE APR_WWRITE
147+#endif
148+
149+#if !AP_MODULE_MAGIC_AT_LEAST(20050127,0)
150+/* Debian backported ap_regex_t to Apache 2.0 and
151+ * thus made official version checking break. */
152+#ifndef AP_REG_EXTENDED
153+typedef regex_t ap_regex_t;
154+typedef regmatch_t ap_regmatch_t;
155+#define AP_REG_EXTENDED REG_EXTENDED
156+#endif
157+#endif
158+
159+#if !AP_MODULE_MAGIC_AT_LEAST(20081201,0)
160+#define ap_unixd_config unixd_config
161+#endif
162+
163+#ifndef WIN32
164+#include <pwd.h>
165+#endif
166+
167+#include "Python.h"
168+
169+#if !defined(PY_VERSION_HEX)
170+#error Sorry, Python developer package does not appear to be installed.
171+#endif
172+
173+#if PY_VERSION_HEX <= 0x02030000
174+#error Sorry, mod_wsgi requires at least Python 2.3.0 for Python 2.X.
175+#endif
176+
177+#if PY_VERSION_HEX >= 0x03000000 && PY_VERSION_HEX < 0x03010000
178+#error Sorry, mod_wsgi requires at least Python 3.1.0 for Python 3.X.
179+#endif
180+
181+#if !defined(WITH_THREAD)
182+#error Sorry, mod_wsgi requires that Python supporting thread.
183+#endif
184+
185+#include "compile.h"
186+#include "node.h"
187+#include "osdefs.h"
188+
189+#ifndef PyVarObject_HEAD_INIT
190+#define PyVarObject_HEAD_INIT(type, size) \
191+ PyObject_HEAD_INIT(type) size,
192+#endif
193+
194+#if PY_MAJOR_VERSION >= 3
195+#define PyStringObject PyBytesObject
196+#define PyString_Check PyBytes_Check
197+#define PyString_Size PyBytes_Size
198+#define PyString_AsString PyBytes_AsString
199+#define PyString_FromString PyBytes_FromString
200+#define PyString_FromStringAndSize PyBytes_FromStringAndSize
201+#define PyString_AS_STRING PyBytes_AS_STRING
202+#define PyString_GET_SIZE PyBytes_GET_SIZE
203+#define _PyString_Resize _PyBytes_Resize
204+#endif
205+
206+#ifndef WIN32
207+#if AP_SERVER_MAJORVERSION_NUMBER >= 2
208+#if APR_HAS_OTHER_CHILD && APR_HAS_THREADS && APR_HAS_FORK
209+#define MOD_WSGI_WITH_DAEMONS 1
210+#endif
211+#endif
212+#endif
213+
214+#if AP_SERVER_MAJORVERSION_NUMBER >= 2
215+#define MOD_WSGI_WITH_BUCKETS 1
216+#define MOD_WSGI_WITH_AAA_HANDLERS 1
217+#endif
218+
219+#if defined(MOD_WSGI_WITH_AAA_HANDLERS)
220+static PyTypeObject Auth_Type;
221+#if AP_SERVER_MAJORVERSION_NUMBER >= 2
222+#if AP_SERVER_MINORVERSION_NUMBER >= 2
223+#define MOD_WSGI_WITH_AUTHN_PROVIDER 1
224+#endif
225+#endif
226+#if AP_MODULE_MAGIC_AT_LEAST(20060110,0)
227+#define MOD_WSGI_WITH_AUTHZ_PROVIDER 1
228+#endif
229+#endif
230+
231+#if defined(MOD_WSGI_WITH_AUTHN_PROVIDER)
232+#include "mod_auth.h"
233+#include "ap_provider.h"
234+#ifndef AUTHN_PROVIDER_VERSION
235+#define AUTHN_PROVIDER_VERSION "0"
236+#endif
237+#endif
238+
239+#if defined(MOD_WSGI_WITH_DAEMONS)
240+
241+#if !AP_MODULE_MAGIC_AT_LEAST(20051115,0)
242+static void ap_close_listeners(void)
243+{
244+ ap_listen_rec *lr;
245+
246+ for (lr = ap_listeners; lr; lr = lr->next) {
247+ apr_socket_close(lr->sd);
248+ lr->active = 0;
249+ }
250+}
251+#endif
252+
253+#if (APR_MAJOR_VERSION == 0) && \
254+ (APR_MINOR_VERSION == 9) && \
255+ (APR_PATCH_VERSION < 5)
256+static apr_status_t apr_unix_file_cleanup(void *thefile)
257+{
258+ apr_file_t *file = thefile;
259+
260+ return apr_file_close(file);
261+}
262+
263+static apr_status_t apr_os_pipe_put_ex(apr_file_t **file,
264+ apr_os_file_t *thefile,
265+ int register_cleanup,
266+ apr_pool_t *pool)
267+{
268+ apr_status_t rv;
269+
270+ rv = apr_os_pipe_put(file, thefile, pool);
271+
272+ if (register_cleanup) {
273+ apr_pool_cleanup_register(pool, (void *)(*file),
274+ apr_unix_file_cleanup,
275+ apr_pool_cleanup_null);
276+ }
277+
278+ return rv;
279+}
280+#endif
281+
282+#endif
283+
284+#if AP_SERVER_MAJORVERSION_NUMBER < 2
285+
286+static char *apr_off_t_toa(apr_pool_t *p, apr_off_t n)
287+{
288+ const int BUFFER_SIZE = sizeof(apr_off_t) * 3 + 2;
289+ char *buf = apr_palloc(p, BUFFER_SIZE);
290+ char *start = buf + BUFFER_SIZE - 1;
291+ int negative;
292+ if (n < 0) {
293+ negative = 1;
294+ n = -n;
295+ }
296+ else {
297+ negative = 0;
298+ }
299+ *start = 0;
300+ do {
301+ *--start = '0' + (char)(n % 10);
302+ n /= 10;
303+ } while (n);
304+ if (negative) {
305+ *--start = '-';
306+ }
307+ return start;
308+}
309+
310+#endif
311+
312+#if defined(WIN32) && defined(APR_HAS_UNICODE_FS)
313+typedef apr_uint16_t apr_wchar_t;
314+
315+APR_DECLARE(apr_status_t) apr_conv_utf8_to_ucs2(const char *in,
316+ apr_size_t *inbytes,
317+ apr_wchar_t *out,
318+ apr_size_t *outwords);
319+
320+static apr_status_t wsgi_utf8_to_unicode_path(apr_wchar_t* retstr,
321+ apr_size_t retlen,
322+ const char* srcstr)
323+{
324+ /* TODO: The computations could preconvert the string to determine
325+ * the true size of the retstr, but that's a memory over speed
326+ * tradeoff that isn't appropriate this early in development.
327+ *
328+ * Allocate the maximum string length based on leading 4
329+ * characters of \\?\ (allowing nearly unlimited path lengths)
330+ * plus the trailing null, then transform /'s into \\'s since
331+ * the \\?\ form doesn't allow '/' path seperators.
332+ *
333+ * Note that the \\?\ form only works for local drive paths, and
334+ * \\?\UNC\ is needed UNC paths.
335+ */
336+ apr_size_t srcremains = strlen(srcstr) + 1;
337+ apr_wchar_t *t = retstr;
338+ apr_status_t rv;
339+
340+ /* This is correct, we don't twist the filename if it is will
341+ * definately be shorter than 248 characters. It merits some
342+ * performance testing to see if this has any effect, but there
343+ * seem to be applications that get confused by the resulting
344+ * Unicode \\?\ style file names, especially if they use argv[0]
345+ * or call the Win32 API functions such as GetModuleName, etc.
346+ * Not every application is prepared to handle such names.
347+ *
348+ * Note also this is shorter than MAX_PATH, as directory paths
349+ * are actually limited to 248 characters.
350+ *
351+ * Note that a utf-8 name can never result in more wide chars
352+ * than the original number of utf-8 narrow chars.
353+ */
354+ if (srcremains > 248) {
355+ if (srcstr[1] == ':' && (srcstr[2] == '/' || srcstr[2] == '\\')) {
356+ wcscpy (retstr, L"\\\\?\\");
357+ retlen -= 4;
358+ t += 4;
359+ }
360+ else if ((srcstr[0] == '/' || srcstr[0] == '\\')
361+ && (srcstr[1] == '/' || srcstr[1] == '\\')
362+ && (srcstr[2] != '?')) {
363+ /* Skip the slashes */
364+ srcstr += 2;
365+ srcremains -= 2;
366+ wcscpy (retstr, L"\\\\?\\UNC\\");
367+ retlen -= 8;
368+ t += 8;
369+ }
370+ }
371+
372+ if (rv = apr_conv_utf8_to_ucs2(srcstr, &srcremains, t, &retlen)) {
373+ return (rv == APR_INCOMPLETE) ? APR_EINVAL : rv;
374+ }
375+ if (srcremains) {
376+ return APR_ENAMETOOLONG;
377+ }
378+ for (; *t; ++t)
379+ if (*t == L'/')
380+ *t = L'\\';
381+ return APR_SUCCESS;
382+}
383+#endif
384+
385+/* Compatibility macros for log level and status. */
386+
387+#if AP_SERVER_MAJORVERSION_NUMBER < 2
388+#define WSGI_LOG_LEVEL(l) l
389+#define WSGI_LOG_LEVEL_AND_STATUS(l, e) l | (!e ? APLOG_NOERRNO : 0)
390+#else
391+#define WSGI_LOG_LEVEL(l) l, 0
392+#define WSGI_LOG_LEVEL_AND_STATUS(l, e) l, e
393+#endif
394+
395+#define WSGI_LOG_EMERG(e) WSGI_LOG_LEVEL_AND_STATUS(APLOG_EMERG, e)
396+#define WSGI_LOG_ALERT(e) WSGI_LOG_LEVEL_AND_STATUS(APLOG_ALERT, e)
397+#define WSGI_LOG_CRIT(e) WSGI_LOG_LEVEL_AND_STATUS(APLOG_CRIT, e)
398+#define WSGI_LOG_ERR(e) WSGI_LOG_LEVEL_AND_STATUS(APLOG_ERR, e)
399+#define WSGI_LOG_WARNING(e) WSGI_LOG_LEVEL_AND_STATUS(APLOG_WARNING, e)
400+#define WSGI_LOG_NOTICE(e) WSGI_LOG_LEVEL_AND_STATUS(APLOG_NOTICE, e)
401+#define WSGI_LOG_INFO(e) WSGI_LOG_LEVEL_AND_STATUS(APLOG_INFO, e)
402+#define WSGI_LOG_DEBUG(e) WSGI_LOG_LEVEL_AND_STATUS(APLOG_DEBUG, e)
403+
404+/* Version and module information. */
405+
406+#define MOD_WSGI_MAJORVERSION_NUMBER 3
407+#define MOD_WSGI_MINORVERSION_NUMBER 3
408+#define MOD_WSGI_VERSION_STRING "3.3"
409+
410+#if AP_SERVER_MAJORVERSION_NUMBER < 2
411+module MODULE_VAR_EXPORT wsgi_module;
412+#else
413+module AP_MODULE_DECLARE_DATA wsgi_module;
414+#endif
415+
416+/* Constants. */
417+
418+#define WSGI_RELOAD_MODULE 0
419+#define WSGI_RELOAD_PROCESS 1
420+
421+/* Base server object. */
422+
423+static server_rec *wsgi_server = NULL;
424+
425+/* Process information. */
426+
427+static pid_t wsgi_parent_pid = 0;
428+static int wsgi_multiprocess = 1;
429+static int wsgi_multithread = 1;
430+
431+/* Daemon information. */
432+
433+static const char *wsgi_daemon_group = "";
434+
435+static apr_array_header_t *wsgi_daemon_list = NULL;
436+
437+static apr_pool_t *wsgi_parent_pool = NULL;
438+static apr_pool_t *wsgi_daemon_pool = NULL;
439+
440+static int volatile wsgi_daemon_shutdown = 0;
441+
442+#if defined(MOD_WSGI_WITH_DAEMONS)
443+static apr_interval_time_t wsgi_deadlock_timeout = 0;
444+static apr_interval_time_t wsgi_inactivity_timeout = 0;
445+static apr_time_t volatile wsgi_deadlock_shutdown_time = 0;
446+static apr_time_t volatile wsgi_inactivity_shutdown_time = 0;
447+static apr_thread_mutex_t* wsgi_shutdown_lock = NULL;
448+#endif
449+
450+/* Script information. */
451+
452+static apr_array_header_t *wsgi_import_list = NULL;
453+
454+/* Configuration objects. */
455+
456+typedef struct {
457+ const char *location;
458+ const char *application;
459+ ap_regex_t *regexp;
460+ const char *process_group;
461+ const char *application_group;
462+ const char *callable_object;
463+ int pass_authorization;
464+} WSGIAliasEntry;
465+
466+typedef struct {
467+ const char *handler_script;
468+ const char *process_group;
469+ const char *application_group;
470+ const char *callable_object;
471+ const char *pass_authorization;
472+} WSGIScriptFile;
473+
474+typedef struct {
475+ apr_pool_t *pool;
476+
477+ apr_array_header_t *alias_list;
478+
479+ const char *socket_prefix;
480+ apr_lockmech_e lock_mechanism;
481+
482+ int verbose_debugging;
483+
484+ apr_array_header_t *python_warnings;
485+
486+ int python_optimize;
487+ int py3k_warning_flag;
488+
489+ const char *python_home;
490+ const char *python_path;
491+ const char *python_eggs;
492+
493+ int restrict_embedded;
494+ int restrict_stdin;
495+ int restrict_stdout;
496+ int restrict_signal;
497+
498+ int case_sensitivity;
499+
500+ apr_table_t *restrict_process;
501+
502+ const char *process_group;
503+ const char *application_group;
504+ const char *callable_object;
505+
506+ WSGIScriptFile *dispatch_script;
507+
508+ int pass_apache_request;
509+ int pass_authorization;
510+ int script_reloading;
511+ int error_override;
512+ int chunked_request;
513+
514+#if AP_SERVER_MAJORVERSION_NUMBER >= 2
515+ apr_hash_t *handler_scripts;
516+#endif
517+} WSGIServerConfig;
518+
519+static WSGIServerConfig *wsgi_server_config = NULL;
520+
521+static WSGIScriptFile *newWSGIScriptFile(apr_pool_t *p)
522+{
523+ WSGIScriptFile *object = NULL;
524+
525+ object = (WSGIScriptFile *)apr_pcalloc(p, sizeof(WSGIScriptFile));
526+
527+ object->handler_script = NULL;
528+ object->application_group = NULL;
529+ object->process_group = NULL;
530+
531+ return object;
532+}
533+
534+static WSGIServerConfig *newWSGIServerConfig(apr_pool_t *p)
535+{
536+ WSGIServerConfig *object = NULL;
537+
538+ object = (WSGIServerConfig *)apr_pcalloc(p, sizeof(WSGIServerConfig));
539+
540+ object->pool = p;
541+
542+ object->alias_list = NULL;
543+
544+ object->socket_prefix = NULL;
545+
546+#if defined(MOD_WSGI_WITH_DAEMONS)
547+ object->socket_prefix = DEFAULT_REL_RUNTIMEDIR "/wsgi";
548+ object->socket_prefix = ap_server_root_relative(p, object->socket_prefix);
549+#endif
550+
551+ object->verbose_debugging = 0;
552+
553+ object->python_warnings = NULL;
554+
555+ object->py3k_warning_flag = -1;
556+ object->python_optimize = -1;
557+
558+ object->python_home = NULL;
559+ object->python_path = NULL;
560+ object->python_eggs = NULL;
561+
562+ object->restrict_embedded = -1;
563+ object->restrict_stdin = -1;
564+ object->restrict_stdout = -1;
565+ object->restrict_signal = -1;
566+
567+#if defined(WIN32) || defined(DARWIN)
568+ object->case_sensitivity = 0;
569+#else
570+ object->case_sensitivity = 1;
571+#endif
572+
573+ object->restrict_process = NULL;
574+
575+ object->process_group = NULL;
576+ object->application_group = NULL;
577+ object->callable_object = NULL;
578+
579+ object->dispatch_script = NULL;
580+
581+ object->pass_apache_request = -1;
582+ object->pass_authorization = -1;
583+ object->script_reloading = -1;
584+ object->error_override = -1;
585+ object->chunked_request = -1;
586+
587+ return object;
588+}
589+
590+static void *wsgi_create_server_config(apr_pool_t *p, server_rec *s)
591+{
592+ WSGIServerConfig *config = NULL;
593+
594+ config = newWSGIServerConfig(p);
595+
596+ return config;
597+}
598+
599+static void *wsgi_merge_server_config(apr_pool_t *p, void *base_conf,
600+ void *new_conf)
601+{
602+ WSGIServerConfig *config = NULL;
603+ WSGIServerConfig *parent = NULL;
604+ WSGIServerConfig *child = NULL;
605+
606+ config = newWSGIServerConfig(p);
607+
608+ parent = (WSGIServerConfig *)base_conf;
609+ child = (WSGIServerConfig *)new_conf;
610+
611+ if (child->alias_list && parent->alias_list) {
612+ config->alias_list = apr_array_append(p, child->alias_list,
613+ parent->alias_list);
614+ }
615+ else if (child->alias_list) {
616+ config->alias_list = apr_array_make(p, 20, sizeof(WSGIAliasEntry));
617+ apr_array_cat(config->alias_list, child->alias_list);
618+ }
619+ else if (parent->alias_list) {
620+ config->alias_list = apr_array_make(p, 20, sizeof(WSGIAliasEntry));
621+ apr_array_cat(config->alias_list, parent->alias_list);
622+ }
623+
624+ if (child->restrict_process)
625+ config->restrict_process = child->restrict_process;
626+ else
627+ config->restrict_process = parent->restrict_process;
628+
629+ if (child->process_group)
630+ config->process_group = child->process_group;
631+ else
632+ config->process_group = parent->process_group;
633+
634+ if (child->application_group)
635+ config->application_group = child->application_group;
636+ else
637+ config->application_group = parent->application_group;
638+
639+ if (child->callable_object)
640+ config->callable_object = child->callable_object;
641+ else
642+ config->callable_object = parent->callable_object;
643+
644+ if (child->dispatch_script)
645+ config->dispatch_script = child->dispatch_script;
646+ else
647+ config->dispatch_script = parent->dispatch_script;
648+
649+ if (child->pass_apache_request != -1)
650+ config->pass_apache_request = child->pass_apache_request;
651+ else
652+ config->pass_apache_request = parent->pass_apache_request;
653+
654+ if (child->pass_authorization != -1)
655+ config->pass_authorization = child->pass_authorization;
656+ else
657+ config->pass_authorization = parent->pass_authorization;
658+
659+ if (child->script_reloading != -1)
660+ config->script_reloading = child->script_reloading;
661+ else
662+ config->script_reloading = parent->script_reloading;
663+
664+ if (child->error_override != -1)
665+ config->error_override = child->error_override;
666+ else
667+ config->error_override = parent->error_override;
668+
669+ if (child->chunked_request != -1)
670+ config->chunked_request = child->chunked_request;
671+ else
672+ config->chunked_request = parent->chunked_request;
673+
674+#if AP_SERVER_MAJORVERSION_NUMBER >= 2
675+ if (!child->handler_scripts)
676+ config->handler_scripts = parent->handler_scripts;
677+ else if (!parent->handler_scripts)
678+ config->handler_scripts = child->handler_scripts;
679+ else {
680+ config->handler_scripts = apr_hash_overlay(p, child->handler_scripts,
681+ parent->handler_scripts);
682+ }
683+#endif
684+
685+ return config;
686+}
687+
688+typedef struct {
689+ apr_pool_t *pool;
690+
691+ apr_table_t *restrict_process;
692+
693+ const char *process_group;
694+ const char *application_group;
695+ const char *callable_object;
696+
697+ WSGIScriptFile *dispatch_script;
698+
699+ int pass_apache_request;
700+ int pass_authorization;
701+ int script_reloading;
702+ int error_override;
703+ int chunked_request;
704+
705+ WSGIScriptFile *access_script;
706+ WSGIScriptFile *auth_user_script;
707+ WSGIScriptFile *auth_group_script;
708+ int user_authoritative;
709+ int group_authoritative;
710+
711+#if AP_SERVER_MAJORVERSION_NUMBER >= 2
712+ apr_hash_t *handler_scripts;
713+#endif
714+} WSGIDirectoryConfig;
715+
716+static WSGIDirectoryConfig *newWSGIDirectoryConfig(apr_pool_t *p)
717+{
718+ WSGIDirectoryConfig *object = NULL;
719+
720+ object = (WSGIDirectoryConfig *)apr_pcalloc(p, sizeof(WSGIDirectoryConfig));
721+
722+ object->pool = p;
723+
724+ object->process_group = NULL;
725+ object->application_group = NULL;
726+ object->callable_object = NULL;
727+
728+ object->dispatch_script = NULL;
729+
730+ object->pass_apache_request = -1;
731+ object->pass_authorization = -1;
732+ object->script_reloading = -1;
733+ object->error_override = -1;
734+ object->chunked_request = -1;
735+
736+ object->access_script = NULL;
737+ object->auth_user_script = NULL;
738+ object->auth_group_script = NULL;
739+ object->user_authoritative = -1;
740+ object->group_authoritative = -1;
741+
742+ return object;
743+}
744+
745+static void *wsgi_create_dir_config(apr_pool_t *p, char *dir)
746+{
747+ WSGIDirectoryConfig *config = NULL;
748+
749+ config = newWSGIDirectoryConfig(p);
750+
751+ return config;
752+}
753+
754+static void *wsgi_merge_dir_config(apr_pool_t *p, void *base_conf,
755+ void *new_conf)
756+{
757+ WSGIDirectoryConfig *config = NULL;
758+ WSGIDirectoryConfig *parent = NULL;
759+ WSGIDirectoryConfig *child = NULL;
760+
761+ config = newWSGIDirectoryConfig(p);
762+
763+ parent = (WSGIDirectoryConfig *)base_conf;
764+ child = (WSGIDirectoryConfig *)new_conf;
765+
766+ if (child->restrict_process)
767+ config->restrict_process = child->restrict_process;
768+ else
769+ config->restrict_process = parent->restrict_process;
770+
771+ if (child->process_group)
772+ config->process_group = child->process_group;
773+ else
774+ config->process_group = parent->process_group;
775+
776+ if (child->application_group)
777+ config->application_group = child->application_group;
778+ else
779+ config->application_group = parent->application_group;
780+
781+ if (child->callable_object)
782+ config->callable_object = child->callable_object;
783+ else
784+ config->callable_object = parent->callable_object;
785+
786+ if (child->dispatch_script)
787+ config->dispatch_script = child->dispatch_script;
788+ else
789+ config->dispatch_script = parent->dispatch_script;
790+
791+ if (child->pass_apache_request != -1)
792+ config->pass_apache_request = child->pass_apache_request;
793+ else
794+ config->pass_apache_request = parent->pass_apache_request;
795+
796+ if (child->pass_authorization != -1)
797+ config->pass_authorization = child->pass_authorization;
798+ else
799+ config->pass_authorization = parent->pass_authorization;
800+
801+ if (child->script_reloading != -1)
802+ config->script_reloading = child->script_reloading;
803+ else
804+ config->script_reloading = parent->script_reloading;
805+
806+ if (child->error_override != -1)
807+ config->error_override = child->error_override;
808+ else
809+ config->error_override = parent->error_override;
810+
811+ if (child->chunked_request != -1)
812+ config->chunked_request = child->chunked_request;
813+ else
814+ config->chunked_request = parent->chunked_request;
815+
816+ if (child->access_script)
817+ config->access_script = child->access_script;
818+ else
819+ config->access_script = parent->access_script;
820+
821+ if (child->auth_user_script)
822+ config->auth_user_script = child->auth_user_script;
823+ else
824+ config->auth_user_script = parent->auth_user_script;
825+
826+ if (child->auth_group_script)
827+ config->auth_group_script = child->auth_group_script;
828+ else
829+ config->auth_group_script = parent->auth_group_script;
830+
831+ if (child->user_authoritative != -1)
832+ config->user_authoritative = child->user_authoritative;
833+ else
834+ config->user_authoritative = parent->user_authoritative;
835+
836+ if (child->group_authoritative != -1)
837+ config->group_authoritative = child->group_authoritative;
838+ else
839+ config->group_authoritative = parent->group_authoritative;
840+
841+#if AP_SERVER_MAJORVERSION_NUMBER >= 2
842+ if (!child->handler_scripts)
843+ config->handler_scripts = parent->handler_scripts;
844+ else if (!parent->handler_scripts)
845+ config->handler_scripts = child->handler_scripts;
846+ else {
847+ config->handler_scripts = apr_hash_overlay(p, child->handler_scripts,
848+ parent->handler_scripts);
849+ }
850+#endif
851+
852+ return config;
853+}
854+
855+typedef struct {
856+ apr_pool_t *pool;
857+
858+ apr_table_t *restrict_process;
859+
860+ const char *process_group;
861+ const char *application_group;
862+ const char *callable_object;
863+
864+ WSGIScriptFile *dispatch_script;
865+
866+ int pass_apache_request;
867+ int pass_authorization;
868+ int script_reloading;
869+ int error_override;
870+ int chunked_request;
871+
872+ WSGIScriptFile *access_script;
873+ WSGIScriptFile *auth_user_script;
874+ WSGIScriptFile *auth_group_script;
875+ int user_authoritative;
876+ int group_authoritative;
877+
878+#if AP_SERVER_MAJORVERSION_NUMBER >= 2
879+ apr_hash_t *handler_scripts;
880+#endif
881+ const char *handler_script;
882+} WSGIRequestConfig;
883+
884+static int wsgi_find_path_info(const char *uri, const char *path_info)
885+{
886+ int lu = strlen(uri);
887+ int lp = strlen(path_info);
888+
889+ while (lu-- && lp-- && uri[lu] == path_info[lp]) {
890+ if (path_info[lp] == '/') {
891+ while (lu && uri[lu-1] == '/') lu--;
892+ }
893+ }
894+
895+ if (lu == -1) {
896+ lu = 0;
897+ }
898+
899+ while (uri[lu] != '\0' && uri[lu] != '/') {
900+ lu++;
901+ }
902+ return lu;
903+}
904+
905+static const char *wsgi_script_name(request_rec *r)
906+{
907+ char *script_name = NULL;
908+ int path_info_start = 0;
909+
910+ if (!r->path_info || !*r->path_info) {
911+ script_name = apr_pstrdup(r->pool, r->uri);
912+ }
913+ else {
914+ path_info_start = wsgi_find_path_info(r->uri, r->path_info);
915+
916+ script_name = apr_pstrndup(r->pool, r->uri, path_info_start);
917+ }
918+
919+ if (*script_name) {
920+ while (*script_name && (*(script_name+1) == '/'))
921+ script_name++;
922+ script_name = apr_pstrdup(r->pool, script_name);
923+ ap_no2slash((char*)script_name);
924+ }
925+
926+ ap_str_tolower(script_name);
927+
928+ return script_name;
929+}
930+
931+static const char *wsgi_process_group(request_rec *r, const char *s)
932+{
933+ const char *name = NULL;
934+ const char *value = NULL;
935+
936+ if (!s)
937+ return "";
938+
939+ if (*s != '%')
940+ return s;
941+
942+ name = s + 1;
943+
944+ if (*name) {
945+ if (!strcmp(name, "{GLOBAL}"))
946+ return "";
947+
948+ if (strstr(name, "{ENV:") == name) {
949+ int len = 0;
950+
951+ name = name + 5;
952+ len = strlen(name);
953+
954+ if (len && name[len-1] == '}') {
955+ name = apr_pstrndup(r->pool, name, len-1);
956+
957+ value = apr_table_get(r->notes, name);
958+
959+ if (!value)
960+ value = apr_table_get(r->subprocess_env, name);
961+
962+ if (!value)
963+ value = getenv(name);
964+
965+ if (value) {
966+ if (*value == '%' && strstr(value, "%{ENV:") != value)
967+ return wsgi_process_group(r, value);
968+
969+ return value;
970+ }
971+ }
972+ }
973+ }
974+
975+ return s;
976+}
977+
978+static const char *wsgi_server_group(request_rec *r, const char *s)
979+{
980+ const char *name = NULL;
981+ const char *value = NULL;
982+
983+ const char *h = NULL;
984+ apr_port_t p = 0;
985+
986+ if (!s)
987+ return "";
988+
989+ if (*s != '%')
990+ return s;
991+
992+ name = s + 1;
993+
994+ if (*name) {
995+ if (!strcmp(name, "{SERVER}")) {
996+ h = r->server->server_hostname;
997+ p = ap_get_server_port(r);
998+
999+ if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
1000+ return apr_psprintf(r->pool, "%s:%u", h, p);
1001+ else
1002+ return h;
1003+ }
1004+
1005+ if (!strcmp(name, "{GLOBAL}"))
1006+ return "";
1007+ }
1008+
1009+ return s;
1010+}
1011+
1012+static const char *wsgi_application_group(request_rec *r, const char *s)
1013+{
1014+ const char *name = NULL;
1015+ const char *value = NULL;
1016+
1017+ const char *h = NULL;
1018+ apr_port_t p = 0;
1019+ const char *n = NULL;
1020+
1021+ if (!s) {
1022+ h = r->server->server_hostname;
1023+ p = ap_get_server_port(r);
1024+ n = wsgi_script_name(r);
1025+
1026+ if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
1027+ return apr_psprintf(r->pool, "%s:%u|%s", h, p, n);
1028+ else
1029+ return apr_psprintf(r->pool, "%s|%s", h, n);
1030+ }
1031+
1032+ if (*s != '%')
1033+ return s;
1034+
1035+ name = s + 1;
1036+
1037+ if (*name) {
1038+ if (!strcmp(name, "{RESOURCE}")) {
1039+ h = r->server->server_hostname;
1040+ p = ap_get_server_port(r);
1041+ n = wsgi_script_name(r);
1042+
1043+ if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
1044+ return apr_psprintf(r->pool, "%s:%u|%s", h, p, n);
1045+ else
1046+ return apr_psprintf(r->pool, "%s|%s", h, n);
1047+ }
1048+
1049+ if (!strcmp(name, "{SERVER}")) {
1050+ h = r->server->server_hostname;
1051+ p = ap_get_server_port(r);
1052+
1053+ if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
1054+ return apr_psprintf(r->pool, "%s:%u", h, p);
1055+ else
1056+ return h;
1057+ }
1058+
1059+ if (!strcmp(name, "{GLOBAL}"))
1060+ return "";
1061+
1062+ if (strstr(name, "{ENV:") == name) {
1063+ int len = 0;
1064+
1065+ name = name + 5;
1066+ len = strlen(name);
1067+
1068+ if (len && name[len-1] == '}') {
1069+ name = apr_pstrndup(r->pool, name, len-1);
1070+
1071+ value = apr_table_get(r->notes, name);
1072+
1073+ if (!value)
1074+ value = apr_table_get(r->subprocess_env, name);
1075+
1076+ if (!value)
1077+ value = getenv(name);
1078+
1079+ if (value) {
1080+ if (*value == '%' && strstr(value, "%{ENV:") != value)
1081+ return wsgi_application_group(r, value);
1082+
1083+ return value;
1084+ }
1085+ }
1086+ }
1087+ }
1088+
1089+ return s;
1090+}
1091+
1092+static const char *wsgi_callable_object(request_rec *r, const char *s)
1093+{
1094+ const char *name = NULL;
1095+ const char *value = NULL;
1096+
1097+ if (!s)
1098+ return "application";
1099+
1100+ if (*s != '%')
1101+ return s;
1102+
1103+ name = s + 1;
1104+
1105+ if (!*name)
1106+ return "application";
1107+
1108+ if (strstr(name, "{ENV:") == name) {
1109+ int len = 0;
1110+
1111+ name = name + 5;
1112+ len = strlen(name);
1113+
1114+ if (len && name[len-1] == '}') {
1115+ name = apr_pstrndup(r->pool, name, len-1);
1116+
1117+ value = apr_table_get(r->notes, name);
1118+
1119+ if (!value)
1120+ value = apr_table_get(r->subprocess_env, name);
1121+
1122+ if (!value)
1123+ value = getenv(name);
1124+
1125+ if (value)
1126+ return value;
1127+ }
1128+ }
1129+
1130+ return "application";
1131+}
1132+
1133+static WSGIRequestConfig *wsgi_create_req_config(apr_pool_t *p, request_rec *r)
1134+{
1135+ WSGIRequestConfig *config = NULL;
1136+ WSGIServerConfig *sconfig = NULL;
1137+ WSGIDirectoryConfig *dconfig = NULL;
1138+
1139+ config = (WSGIRequestConfig *)apr_pcalloc(p, sizeof(WSGIRequestConfig));
1140+
1141+ dconfig = ap_get_module_config(r->per_dir_config, &wsgi_module);
1142+ sconfig = ap_get_module_config(r->server->module_config, &wsgi_module);
1143+
1144+ config->pool = p;
1145+
1146+ config->restrict_process = dconfig->restrict_process;
1147+
1148+ if (!config->restrict_process)
1149+ config->restrict_process = sconfig->restrict_process;
1150+
1151+ config->process_group = dconfig->process_group;
1152+
1153+ if (!config->process_group)
1154+ config->process_group = sconfig->process_group;
1155+
1156+ config->process_group = wsgi_process_group(r, config->process_group);
1157+
1158+ config->application_group = dconfig->application_group;
1159+
1160+ if (!config->application_group)
1161+ config->application_group = sconfig->application_group;
1162+
1163+ config->application_group = wsgi_application_group(r,
1164+ config->application_group);
1165+
1166+ config->callable_object = dconfig->callable_object;
1167+
1168+ if (!config->callable_object)
1169+ config->callable_object = sconfig->callable_object;
1170+
1171+ config->callable_object = wsgi_callable_object(r, config->callable_object);
1172+
1173+ config->dispatch_script = dconfig->dispatch_script;
1174+
1175+ if (!config->dispatch_script)
1176+ config->dispatch_script = sconfig->dispatch_script;
1177+
1178+ config->pass_apache_request = dconfig->pass_apache_request;
1179+
1180+ if (config->pass_apache_request < 0) {
1181+ config->pass_apache_request = sconfig->pass_apache_request;
1182+ if (config->pass_apache_request < 0)
1183+ config->pass_apache_request = 0;
1184+ }
1185+
1186+ config->pass_authorization = dconfig->pass_authorization;
1187+
1188+ if (config->pass_authorization < 0) {
1189+ config->pass_authorization = sconfig->pass_authorization;
1190+ if (config->pass_authorization < 0)
1191+ config->pass_authorization = 0;
1192+ }
1193+
1194+ config->script_reloading = dconfig->script_reloading;
1195+
1196+ if (config->script_reloading < 0) {
1197+ config->script_reloading = sconfig->script_reloading;
1198+ if (config->script_reloading < 0)
1199+ config->script_reloading = 1;
1200+ }
1201+
1202+ config->error_override = dconfig->error_override;
1203+
1204+ if (config->error_override < 0) {
1205+ config->error_override = sconfig->error_override;
1206+ if (config->error_override < 0)
1207+ config->error_override = 0;
1208+ }
1209+
1210+ config->chunked_request = dconfig->chunked_request;
1211+
1212+ if (config->chunked_request < 0) {
1213+ config->chunked_request = sconfig->chunked_request;
1214+ if (config->chunked_request < 0)
1215+ config->chunked_request = 0;
1216+ }
1217+
1218+ config->access_script = dconfig->access_script;
1219+
1220+ config->auth_user_script = dconfig->auth_user_script;
1221+
1222+ config->auth_group_script = dconfig->auth_group_script;
1223+
1224+ config->user_authoritative = dconfig->user_authoritative;
1225+
1226+ if (config->user_authoritative == -1)
1227+ config->user_authoritative = 1;
1228+
1229+ config->group_authoritative = dconfig->group_authoritative;
1230+
1231+ if (config->group_authoritative == -1)
1232+ config->group_authoritative = 1;
1233+
1234+#if AP_SERVER_MAJORVERSION_NUMBER >= 2
1235+ if (!dconfig->handler_scripts)
1236+ config->handler_scripts = sconfig->handler_scripts;
1237+ else if (!sconfig->handler_scripts)
1238+ config->handler_scripts = dconfig->handler_scripts;
1239+ else {
1240+ config->handler_scripts = apr_hash_overlay(p, dconfig->handler_scripts,
1241+ sconfig->handler_scripts);
1242+ }
1243+#endif
1244+
1245+ config->handler_script = "";
1246+
1247+ return config;
1248+}
1249+
1250+/*
1251+ * Apache 2.X and UNIX specific definitions related to
1252+ * distinct daemon processes.
1253+ */
1254+
1255+#if defined(MOD_WSGI_WITH_DAEMONS)
1256+
1257+#include "unixd.h"
1258+#include "scoreboard.h"
1259+#include "mpm_common.h"
1260+#include "apr_proc_mutex.h"
1261+#include "apr_thread_cond.h"
1262+#include "apr_atomic.h"
1263+#include "http_connection.h"
1264+#include "apr_buckets.h"
1265+#include "apr_poll.h"
1266+#include "apr_signal.h"
1267+#include "http_vhost.h"
1268+
1269+#if APR_MAJOR_VERSION < 1
1270+#define apr_atomic_cas32 apr_atomic_cas
1271+#endif
1272+
1273+#if APR_HAVE_SYS_SOCKET_H
1274+#include <sys/socket.h>
1275+#endif
1276+#if APR_HAVE_UNISTD_H
1277+#include <unistd.h>
1278+#endif
1279+#if APR_HAVE_SYS_TYPES_H
1280+#include <sys/types.h>
1281+#endif
1282+#ifdef HAVE_SYS_SEM_H
1283+#include <sys/sem.h>
1284+#endif
1285+
1286+#include <sys/un.h>
1287+
1288+#ifndef WSGI_LISTEN_BACKLOG
1289+#define WSGI_LISTEN_BACKLOG 100
1290+#endif
1291+
1292+#ifndef WSGI_CONNECT_ATTEMPTS
1293+#define WSGI_CONNECT_ATTEMPTS 15
1294+#endif
1295+
1296+#define WSGI_STACK_HEAD 0xffff
1297+#define WSGI_STACK_LAST 0xffff
1298+#define WSGI_STACK_TERMINATED 0x10000
1299+#define WSGI_STACK_NO_LISTENER 0x20000
1300+
1301+typedef struct {
1302+ server_rec *server;
1303+ long random;
1304+ int id;
1305+ const char *name;
1306+ const char *user;
1307+ uid_t uid;
1308+ const char *group;
1309+ gid_t gid;
1310+ int processes;
1311+ int multiprocess;
1312+ int threads;
1313+ int umask;
1314+ const char *root;
1315+ const char *home;
1316+ const char *python_path;
1317+ const char *python_eggs;
1318+ int stack_size;
1319+ int maximum_requests;
1320+ int shutdown_timeout;
1321+ apr_time_t deadlock_timeout;
1322+ apr_time_t inactivity_timeout;
1323+ const char *display_name;
1324+ int send_buffer_size;
1325+ int recv_buffer_size;
1326+ const char *script_user;
1327+ const char *script_group;
1328+ int cpu_time_limit;
1329+ int cpu_priority;
1330+ const char *socket;
1331+ int listener_fd;
1332+ const char* mutex_path;
1333+ apr_proc_mutex_t* mutex;
1334+} WSGIProcessGroup;
1335+
1336+typedef struct {
1337+ WSGIProcessGroup *group;
1338+ int instance;
1339+ apr_proc_t process;
1340+ apr_socket_t *listener;
1341+} WSGIDaemonProcess;
1342+
1343+typedef struct {
1344+ int id;
1345+ WSGIDaemonProcess *process;
1346+ apr_thread_t *thread;
1347+ int running;
1348+ int next;
1349+ int wakeup;
1350+ apr_thread_cond_t *condition;
1351+ apr_thread_mutex_t *mutex;
1352+} WSGIDaemonThread;
1353+
1354+typedef struct {
1355+ apr_uint32_t state;
1356+} WSGIThreadStack;
1357+
1358+typedef struct {
1359+ const char *name;
1360+ const char *socket;
1361+ int fd;
1362+} WSGIDaemonSocket;
1363+
1364+static int wsgi_daemon_count = 0;
1365+static apr_hash_t *wsgi_daemon_index = NULL;
1366+static apr_hash_t *wsgi_daemon_listeners = NULL;
1367+
1368+static WSGIDaemonProcess *wsgi_daemon_process = NULL;
1369+
1370+static int volatile wsgi_request_count = 0;
1371+
1372+static WSGIDaemonThread *wsgi_worker_threads = NULL;
1373+
1374+static WSGIThreadStack *wsgi_worker_stack = NULL;
1375+
1376+#endif
1377+
1378+/* Class objects used by response handler. */
1379+
1380+static PyTypeObject Dispatch_Type;
1381+
1382+typedef struct {
1383+ PyObject_HEAD
1384+ const char *target;
1385+ request_rec *r;
1386+ int level;
1387+ char *s;
1388+ int l;
1389+ int expired;
1390+#if PY_MAJOR_VERSION < 3
1391+ int softspace;
1392+#endif
1393+} LogObject;
1394+
1395+static PyTypeObject Log_Type;
1396+
1397+static PyObject *newLogObject(request_rec *r, int level, const char *target)
1398+{
1399+ LogObject *self;
1400+
1401+#if PY_MAJOR_VERSION >= 3
1402+ PyObject *module = NULL;
1403+ PyObject *dict = NULL;
1404+ PyObject *object = NULL;
1405+ PyObject *args = NULL;
1406+ PyObject *result = NULL;
1407+
1408+ module = PyImport_ImportModule("io");
1409+
1410+ if (!module)
1411+ return NULL;
1412+
1413+ dict = PyModule_GetDict(module);
1414+ object = PyDict_GetItemString(dict, "TextIOWrapper");
1415+
1416+ if (!object) {
1417+ PyErr_SetString(PyExc_NameError,
1418+ "name 'TextIOWrapper' is not defined");
1419+ return NULL;
1420+ }
1421+#endif
1422+
1423+ self = PyObject_New(LogObject, &Log_Type);
1424+ if (self == NULL)
1425+ return NULL;
1426+
1427+ self->target = target;
1428+ self->r = r;
1429+ self->level = APLOG_NOERRNO|level;
1430+ self->s = NULL;
1431+ self->l = 0;
1432+ self->expired = 0;
1433+#if PY_MAJOR_VERSION < 3
1434+ self->softspace = 0;
1435+#endif
1436+
1437+#if PY_MAJOR_VERSION >= 3
1438+ Py_INCREF(object);
1439+ args = Py_BuildValue("(OssOO)", self, "utf-8", "replace",
1440+ Py_None, Py_True);
1441+ Py_DECREF(self);
1442+ result = PyEval_CallObject(object, args);
1443+ Py_DECREF(args);
1444+ Py_DECREF(object);
1445+
1446+ return result;
1447+#else
1448+ return (PyObject *)self;
1449+#endif
1450+}
1451+
1452+#if 0
1453+static void Log_file(LogObject *self, const char *s, int l)
1454+{
1455+ /*
1456+ * XXX This function is not currently being used.
1457+ * The intention was that it be called instead of
1458+ * Log_call() when 'target' is non zero. This would
1459+ * be the case for 'stdout' and 'stderr'. Doing
1460+ * this bypasses normally Apache logging mechanisms
1461+ * though. May reawaken this code in mod_wsgi 4.0
1462+ * by way of a mechanism to divert logging from a
1463+ * daemon process to specfic log file or pipe using
1464+ * an option to WSGIDaemonProcess.
1465+ */
1466+
1467+ char errstr[MAX_STRING_LEN];
1468+
1469+ int plen = 0;
1470+ int slen = 0;
1471+
1472+#if AP_SERVER_MAJORVERSION_NUMBER < 2
1473+ FILE *logf;
1474+#else
1475+ apr_file_t *logf = NULL;
1476+#endif
1477+
1478+ if (self->r)
1479+ logf = self->r->server->error_log;
1480+ else
1481+ logf = wsgi_server->error_log;
1482+
1483+#if AP_SERVER_MAJORVERSION_NUMBER < 2
1484+ plen = ap_snprintf(errstr, sizeof(errstr), "[%s] ", ap_get_time());
1485+#else
1486+ errstr[0] = '[';
1487+ ap_recent_ctime(errstr + 1, apr_time_now());
1488+ errstr[1 + APR_CTIME_LEN - 1] = ']';
1489+ errstr[1 + APR_CTIME_LEN ] = ' ';
1490+ plen = 1 + APR_CTIME_LEN + 1;
1491+#endif
1492+
1493+ if (self->target) {
1494+ int len;
1495+
1496+ errstr[plen++] = '[';
1497+
1498+ len = strlen(self->target);
1499+ memcpy(errstr+plen, self->target, len);
1500+
1501+ plen += len;
1502+
1503+ errstr[plen++] = ']';
1504+ errstr[plen++] = ' ';
1505+ }
1506+
1507+ slen = MAX_STRING_LEN - plen - 1;
1508+
1509+ Py_BEGIN_ALLOW_THREADS
1510+
1511+ /*
1512+ * We actually break long lines up into segments
1513+ * of around 8192 characters, with the date/time
1514+ * and target information prefixing each line.
1515+ * This is just to avoid having to allocate more
1516+ * memory just to format the line with prefix.
1517+ * We want to avoid writing the prefix separately
1518+ * so at least try and write line in one atomic
1519+ * operation.
1520+ */
1521+
1522+ while (1) {
1523+ if (l > slen) {
1524+ memcpy(errstr+plen, s, slen);
1525+ errstr[plen+slen] = '\n';
1526+#if AP_SERVER_MAJORVERSION_NUMBER < 2
1527+ fwrite(errstr, plen+slen+1, 1, logf);
1528+ fflush(logf);
1529+#else
1530+ apr_file_write_full(logf, errstr, plen+slen+1, NULL);
1531+ apr_file_flush(logf);
1532+#endif
1533+ s += slen;
1534+ l -= slen;
1535+ }
1536+ else {
1537+ memcpy(errstr+plen, s, l);
1538+ errstr[plen+l] = '\n';
1539+#if AP_SERVER_MAJORVERSION_NUMBER < 2
1540+ fwrite(errstr, plen+l+1, 1, logf);
1541+ fflush(logf);
1542+#else
1543+ apr_file_write_full(logf, errstr, plen+l+1, NULL);
1544+ apr_file_flush(logf);
1545+#endif
1546+ break;
1547+ }
1548+ }
1549+
1550+ Py_END_ALLOW_THREADS
1551+}
1552+#endif
1553+
1554+static void Log_call(LogObject *self, const char *s, int l)
1555+{
1556+ /*
1557+ * The length of the string to be logged is ignored
1558+ * for now. We just pass the whole string to the
1559+ * Apache error log functions. It will actually
1560+ * truncate it at some value less than 8192
1561+ * characters depending on the length of the prefix
1562+ * to go at the front. If there are embedded NULLs
1563+ * then truncation will occur at that point. That
1564+ * truncation occurs like this is also what happens
1565+ * if using FASTCGI solutions for Apache, so not
1566+ * doing anything different here.
1567+ */
1568+
1569+ if (self->r) {
1570+ Py_BEGIN_ALLOW_THREADS
1571+ ap_log_rerror(APLOG_MARK, WSGI_LOG_LEVEL(self->level),
1572+ self->r, "%s", s);
1573+ Py_END_ALLOW_THREADS
1574+ }
1575+ else {
1576+ Py_BEGIN_ALLOW_THREADS
1577+ ap_log_error(APLOG_MARK, WSGI_LOG_LEVEL(self->level),
1578+ wsgi_server, "%s", s);
1579+ Py_END_ALLOW_THREADS
1580+ }
1581+}
1582+
1583+static void Log_dealloc(LogObject *self)
1584+{
1585+ if (self->s) {
1586+ if (!self->expired)
1587+ Log_call(self, self->s, self->l);
1588+
1589+ free(self->s);
1590+ }
1591+
1592+ PyObject_Del(self);
1593+}
1594+
1595+static PyObject *Log_flush(LogObject *self, PyObject *args)
1596+{
1597+ if (self->expired) {
1598+ PyErr_SetString(PyExc_RuntimeError, "log object has expired");
1599+ return NULL;
1600+ }
1601+
1602+ if (!PyArg_ParseTuple(args, ":flush"))
1603+ return NULL;
1604+
1605+ if (self->s) {
1606+ Log_call(self, self->s, self->l);
1607+
1608+ free(self->s);
1609+ self->s = NULL;
1610+ self->l = 0;
1611+ }
1612+
1613+ Py_INCREF(Py_None);
1614+ return Py_None;
1615+}
1616+
1617+static PyObject *Log_close(LogObject *self, PyObject *args)
1618+{
1619+ PyObject *result = NULL;
1620+
1621+ if (!PyArg_ParseTuple(args, ":close"))
1622+ return NULL;
1623+
1624+ if (!self->expired)
1625+ result = Log_flush(self, args);
1626+
1627+ Py_XDECREF(result);
1628+
1629+ self->r = NULL;
1630+ self->expired = 1;
1631+
1632+ Py_INCREF(Py_None);
1633+ return Py_None;
1634+}
1635+
1636+static PyObject *Log_isatty(LogObject *self, PyObject *args)
1637+{
1638+ PyObject *result = NULL;
1639+
1640+ if (!PyArg_ParseTuple(args, ":isatty"))
1641+ return NULL;
1642+
1643+ Py_INCREF(Py_False);
1644+ return Py_False;
1645+}
1646+
1647+static void Log_queue(LogObject *self, const char *msg, int len)
1648+{
1649+ const char *p = NULL;
1650+ const char *q = NULL;
1651+ const char *e = NULL;
1652+
1653+ p = msg;
1654+ e = p + len;
1655+
1656+ /*
1657+ * Break string on newline. This is on assumption
1658+ * that primarily textual information being logged.
1659+ */
1660+
1661+ q = p;
1662+ while (q != e) {
1663+ if (*q == '\n')
1664+ break;
1665+ q++;
1666+ }
1667+
1668+ while (q != e) {
1669+ /* Output each complete line. */
1670+
1671+ if (self->s) {
1672+ /* Need to join with buffered value. */
1673+
1674+ int m = 0;
1675+ int n = 0;
1676+ char *s = NULL;
1677+
1678+ m = self->l;
1679+ n = m+q-p+1;
1680+
1681+ s = (char *)malloc(n);
1682+ memcpy(s, self->s, m);
1683+ memcpy(s+m, p, q-p);
1684+ s[n-1] = '\0';
1685+
1686+ free(self->s);
1687+ self->s = NULL;
1688+ self->l = 0;
1689+
1690+ Log_call(self, s, n-1);
1691+
1692+ free(s);
1693+ }
1694+ else {
1695+ int n = 0;
1696+ char *s = NULL;
1697+
1698+ n = q-p+1;
1699+
1700+ s = (char *)malloc(n);
1701+ memcpy(s, p, q-p);
1702+ s[n-1] = '\0';
1703+
1704+ Log_call(self, s, n-1);
1705+
1706+ free(s);
1707+ }
1708+
1709+ p = q+1;
1710+
1711+ /* Break string on newline. */
1712+
1713+ q = p;
1714+ while (q != e) {
1715+ if (*q == '\n')
1716+ break;
1717+ q++;
1718+ }
1719+ }
1720+
1721+ if (p != e) {
1722+ /* Save away incomplete line. */
1723+
1724+ if (self->s) {
1725+ /* Need to join with buffered value. */
1726+
1727+ int m = 0;
1728+ int n = 0;
1729+
1730+ m = self->l;
1731+ n = m+e-p+1;
1732+
1733+ self->s = (char *)realloc(self->s, n);
1734+ memcpy(self->s+m, p, e-p);
1735+ self->s[n-1] = '\0';
1736+ self->l = n-1;
1737+ }
1738+ else {
1739+ int n = 0;
1740+
1741+ n = e-p+1;
1742+
1743+ self->s = (char *)malloc(n);
1744+ memcpy(self->s, p, n-1);
1745+ self->s[n-1] = '\0';
1746+ self->l = n-1;
1747+ }
1748+ }
1749+}
1750+
1751+static PyObject *Log_write(LogObject *self, PyObject *args)
1752+{
1753+ const char *msg = NULL;
1754+ int len = -1;
1755+
1756+ if (self->expired) {
1757+ PyErr_SetString(PyExc_RuntimeError, "log object has expired");
1758+ return NULL;
1759+ }
1760+
1761+ if (!PyArg_ParseTuple(args, "s#:write", &msg, &len))
1762+ return NULL;
1763+
1764+ Log_queue(self, msg, len);
1765+
1766+ Py_INCREF(Py_None);
1767+ return Py_None;
1768+}
1769+
1770+static PyObject *Log_writelines(LogObject *self, PyObject *args)
1771+{
1772+ PyObject *sequence = NULL;
1773+ PyObject *iterator = NULL;
1774+ PyObject *item = NULL;
1775+ const char *msg = NULL;
1776+
1777+ if (self->expired) {
1778+ PyErr_SetString(PyExc_RuntimeError, "log object has expired");
1779+ return NULL;
1780+ }
1781+
1782+ if (!PyArg_ParseTuple(args, "O:writelines", &sequence))
1783+ return NULL;
1784+
1785+ iterator = PyObject_GetIter(sequence);
1786+
1787+ if (iterator == NULL) {
1788+ PyErr_SetString(PyExc_TypeError,
1789+ "argument must be sequence of strings");
1790+
1791+ return NULL;
1792+ }
1793+
1794+ while ((item = PyIter_Next(iterator))) {
1795+ PyObject *result = NULL;
1796+
1797+ result = Log_write(self, item);
1798+
1799+ if (!result) {
1800+ Py_DECREF(iterator);
1801+
1802+ PyErr_SetString(PyExc_TypeError,
1803+ "argument must be sequence of strings");
1804+
1805+ return NULL;
1806+ }
1807+ }
1808+
1809+ Py_DECREF(iterator);
1810+
1811+ Py_INCREF(Py_None);
1812+ return Py_None;
1813+}
1814+
1815+#if PY_MAJOR_VERSION >= 3
1816+static PyObject *Log_readable(LogObject *self, PyObject *args)
1817+{
1818+ if (!PyArg_ParseTuple(args, ":readable"))
1819+ return NULL;
1820+
1821+ Py_INCREF(Py_False);
1822+ return Py_False;
1823+}
1824+
1825+static PyObject *Log_seekable(LogObject *self, PyObject *args)
1826+{
1827+ if (!PyArg_ParseTuple(args, ":seekable"))
1828+ return NULL;
1829+
1830+ Py_INCREF(Py_False);
1831+ return Py_False;
1832+}
1833+
1834+static PyObject *Log_writable(LogObject *self, PyObject *args)
1835+{
1836+ if (!PyArg_ParseTuple(args, ":writable"))
1837+ return NULL;
1838+
1839+ Py_INCREF(Py_True);
1840+ return Py_True;
1841+}
1842+#endif
1843+
1844+static PyObject *Log_closed(LogObject *self, void *closure)
1845+{
1846+ Py_INCREF(Py_False);
1847+ return Py_False;
1848+}
1849+
1850+#if PY_MAJOR_VERSION < 3
1851+static PyObject *Log_get_softspace(LogObject *self, void *closure)
1852+{
1853+ return PyInt_FromLong(self->softspace);
1854+}
1855+
1856+static int Log_set_softspace(LogObject *self, PyObject *value)
1857+{
1858+ int new;
1859+
1860+ if (value == NULL) {
1861+ PyErr_SetString(PyExc_TypeError, "can't delete softspace attribute");
1862+ return -1;
1863+ }
1864+
1865+ new = PyInt_AsLong(value);
1866+ if (new == -1 && PyErr_Occurred())
1867+ return -1;
1868+
1869+ self->softspace = new;
1870+
1871+ return 0;
1872+}
1873+
1874+#else
1875+
1876+static PyObject *Log_get_encoding(LogObject *self, void *closure)
1877+{
1878+ return PyUnicode_FromString("utf-8");
1879+}
1880+
1881+static PyObject *Log_get_errors(LogObject *self, void *closure)
1882+{
1883+ return PyUnicode_FromString("replace");
1884+}
1885+#endif
1886+
1887+static PyMethodDef Log_methods[] = {
1888+ { "flush", (PyCFunction)Log_flush, METH_VARARGS, 0 },
1889+ { "close", (PyCFunction)Log_close, METH_VARARGS, 0 },
1890+ { "isatty", (PyCFunction)Log_isatty, METH_VARARGS, 0 },
1891+ { "write", (PyCFunction)Log_write, METH_VARARGS, 0 },
1892+ { "writelines", (PyCFunction)Log_writelines, METH_VARARGS, 0 },
1893+#if PY_MAJOR_VERSION >= 3
1894+ { "readable", (PyCFunction)Log_readable, METH_VARARGS, 0 },
1895+ { "seekable", (PyCFunction)Log_seekable, METH_VARARGS, 0 },
1896+ { "writable", (PyCFunction)Log_writable, METH_VARARGS, 0 },
1897+#endif
1898+ { NULL, NULL}
1899+};
1900+
1901+static PyGetSetDef Log_getset[] = {
1902+ { "closed", (getter)Log_closed, NULL, 0 },
1903+#if PY_MAJOR_VERSION < 3
1904+ { "softspace", (getter)Log_get_softspace, (setter)Log_set_softspace, 0 },
1905+#else
1906+ { "encoding", (getter)Log_get_encoding, NULL, 0 },
1907+ { "errors", (getter)Log_get_errors, NULL, 0 },
1908+#endif
1909+ { NULL },
1910+};
1911+
1912+static PyTypeObject Log_Type = {
1913+ PyVarObject_HEAD_INIT(NULL, 0)
1914+ "mod_wsgi.Log", /*tp_name*/
1915+ sizeof(LogObject), /*tp_basicsize*/
1916+ 0, /*tp_itemsize*/
1917+ /* methods */
1918+ (destructor)Log_dealloc, /*tp_dealloc*/
1919+ 0, /*tp_print*/
1920+ 0, /*tp_getattr*/
1921+ 0, /*tp_setattr*/
1922+ 0, /*tp_compare*/
1923+ 0, /*tp_repr*/
1924+ 0, /*tp_as_number*/
1925+ 0, /*tp_as_sequence*/
1926+ 0, /*tp_as_mapping*/
1927+ 0, /*tp_hash*/
1928+ 0, /*tp_call*/
1929+ 0, /*tp_str*/
1930+ 0, /*tp_getattro*/
1931+ 0, /*tp_setattro*/
1932+ 0, /*tp_as_buffer*/
1933+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
1934+ 0, /*tp_doc*/
1935+ 0, /*tp_traverse*/
1936+ 0, /*tp_clear*/
1937+ 0, /*tp_richcompare*/
1938+ 0, /*tp_weaklistoffset*/
1939+ 0, /*tp_iter*/
1940+ 0, /*tp_iternext*/
1941+ Log_methods, /*tp_methods*/
1942+ 0, /*tp_members*/
1943+ Log_getset, /*tp_getset*/
1944+ 0, /*tp_base*/
1945+ 0, /*tp_dict*/
1946+ 0, /*tp_descr_get*/
1947+ 0, /*tp_descr_set*/
1948+ 0, /*tp_dictoffset*/
1949+ 0, /*tp_init*/
1950+ 0, /*tp_alloc*/
1951+ 0, /*tp_new*/
1952+ 0, /*tp_free*/
1953+ 0, /*tp_is_gc*/
1954+};
1955+
1956+static void wsgi_log_python_error(request_rec *r, PyObject *log,
1957+ const char *filename)
1958+{
1959+ PyObject *m = NULL;
1960+ PyObject *result = NULL;
1961+
1962+ PyObject *type = NULL;
1963+ PyObject *value = NULL;
1964+ PyObject *traceback = NULL;
1965+
1966+ PyObject *xlog = NULL;
1967+
1968+ if (!PyErr_Occurred())
1969+ return;
1970+
1971+ if (!log) {
1972+ PyErr_Fetch(&type, &value, &traceback);
1973+
1974+ xlog = newLogObject(r, APLOG_ERR, NULL);
1975+
1976+ log = xlog;
1977+
1978+ PyErr_Restore(type, value, traceback);
1979+
1980+ type = NULL;
1981+ value = NULL;
1982+ traceback = NULL;
1983+ }
1984+
1985+ if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
1986+ Py_BEGIN_ALLOW_THREADS
1987+ if (r) {
1988+ ap_log_rerror(APLOG_MARK, WSGI_LOG_ERR(0), r,
1989+ "mod_wsgi (pid=%d): SystemExit exception raised by "
1990+ "WSGI script '%s' ignored.", getpid(), filename);
1991+ }
1992+ else {
1993+ ap_log_error(APLOG_MARK, WSGI_LOG_ERR(0), wsgi_server,
1994+ "mod_wsgi (pid=%d): SystemExit exception raised by "
1995+ "WSGI script '%s' ignored.", getpid(), filename);
1996+ }
1997+ Py_END_ALLOW_THREADS
1998+ }
1999+ else {
2000+ Py_BEGIN_ALLOW_THREADS
2001+ if (r) {
2002+ ap_log_rerror(APLOG_MARK, WSGI_LOG_ERR(0), r,
2003+ "mod_wsgi (pid=%d): Exception occurred processing "
2004+ "WSGI script '%s'.", getpid(), filename);
2005+ }
2006+ else {
2007+ ap_log_error(APLOG_MARK, WSGI_LOG_ERR(0), wsgi_server,
2008+ "mod_wsgi (pid=%d): Exception occurred processing "
2009+ "WSGI script '%s'.", getpid(), filename);
2010+ }
2011+ Py_END_ALLOW_THREADS
2012+ }
2013+
2014+ PyErr_Fetch(&type, &value, &traceback);
2015+ PyErr_NormalizeException(&type, &value, &traceback);
2016+
2017+ if (!value) {
2018+ value = Py_None;
2019+ Py_INCREF(value);
2020+ }
2021+
2022+ if (!traceback) {
2023+ traceback = Py_None;
2024+ Py_INCREF(traceback);
2025+ }
2026+
2027+ m = PyImport_ImportModule("traceback");
2028+
2029+ if (m) {
2030+ PyObject *d = NULL;
2031+ PyObject *o = NULL;
2032+ d = PyModule_GetDict(m);
2033+ o = PyDict_GetItemString(d, "print_exception");
2034+ if (o) {
2035+ PyObject *args = NULL;
2036+ Py_INCREF(o);
2037+ args = Py_BuildValue("(OOOOO)", type, value, traceback,
2038+ Py_None, log);
2039+ result = PyEval_CallObject(o, args);
2040+ Py_DECREF(args);
2041+ Py_DECREF(o);
2042+ }
2043+ }
2044+
2045+ if (!result) {
2046+ /*
2047+ * If can't output exception and traceback then
2048+ * use PyErr_Print to dump out details of the
2049+ * exception. For SystemExit though if we do
2050+ * that the process will actually be terminated
2051+ * so can only clear the exception information
2052+ * and keep going.
2053+ */
2054+
2055+ PyErr_Restore(type, value, traceback);
2056+
2057+ if (!PyErr_ExceptionMatches(PyExc_SystemExit)) {
2058+ PyErr_Print();
2059+ PyErr_Clear();
2060+ }
2061+ else {
2062+ PyErr_Clear();
2063+ }
2064+ }
2065+ else {
2066+ Py_XDECREF(type);
2067+ Py_XDECREF(value);
2068+ Py_XDECREF(traceback);
2069+ }
2070+
2071+ Py_XDECREF(result);
2072+
2073+ Py_XDECREF(m);
2074+
2075+ Py_XDECREF(xlog);
2076+}
2077+
2078+typedef struct {
2079+ PyObject_HEAD
2080+ request_rec *r;
2081+ int init;
2082+ int done;
2083+ char *buffer;
2084+ apr_size_t size;
2085+ apr_size_t offset;
2086+ apr_size_t length;
2087+} InputObject;
2088+
2089+static PyTypeObject Input_Type;
2090+
2091+static InputObject *newInputObject(request_rec *r)
2092+{
2093+ InputObject *self;
2094+
2095+ self = PyObject_New(InputObject, &Input_Type);
2096+ if (self == NULL)
2097+ return NULL;
2098+
2099+ self->r = r;
2100+ self->init = 0;
2101+ self->done = 0;
2102+
2103+ self->buffer = NULL;
2104+ self->size = 0;
2105+ self->offset = 0;
2106+ self->length = 0;
2107+
2108+ return self;
2109+}
2110+
2111+static void Input_dealloc(InputObject *self)
2112+{
2113+ if (self->buffer)
2114+ free(self->buffer);
2115+
2116+ PyObject_Del(self);
2117+}
2118+
2119+static PyObject *Input_close(InputObject *self, PyObject *args)
2120+{
2121+ if (!self->r) {
2122+ PyErr_SetString(PyExc_RuntimeError, "request object has expired");
2123+ return NULL;
2124+ }
2125+
2126+ if (!PyArg_ParseTuple(args, ":close"))
2127+ return NULL;
2128+
2129+ Py_INCREF(Py_None);
2130+ return Py_None;
2131+}
2132+
2133+static PyObject *Input_read(InputObject *self, PyObject *args)
2134+{
2135+ long size = -1;
2136+
2137+ PyObject *result = NULL;
2138+ char *buffer = NULL;
2139+ apr_size_t length = 0;
2140+ int init = 0;
2141+
2142+ apr_size_t n;
2143+
2144+ if (!self->r) {
2145+ PyErr_SetString(PyExc_RuntimeError, "request object has expired");
2146+ return NULL;
2147+ }
2148+
2149+ if (!PyArg_ParseTuple(args, "|l:read", &size))
2150+ return NULL;
2151+
2152+#if defined(MOD_WSGI_WITH_DAEMONS)
2153+ if (wsgi_inactivity_timeout) {
2154+ apr_thread_mutex_lock(wsgi_shutdown_lock);
2155+ wsgi_inactivity_shutdown_time = apr_time_now();
2156+ wsgi_inactivity_shutdown_time += wsgi_inactivity_timeout;
2157+ apr_thread_mutex_unlock(wsgi_shutdown_lock);
2158+ }
2159+#endif
2160+
2161+ init = self->init;
2162+
2163+ if (!self->init) {
2164+ if (!ap_should_client_block(self->r))
2165+ self->done = 1;
2166+
2167+ self->init = 1;
2168+ }
2169+
2170+ /* No point continuing if no more data to be consumed. */
2171+
2172+ if (self->done && self->length == 0)
2173+ return PyString_FromString("");
2174+
2175+ /*
2176+ * If requested size is zero bytes, then still need to pass
2177+ * this through to Apache input filters so that any
2178+ * 100-continue response is triggered. Only do this if very
2179+ * first attempt to read data. Note that this will cause an
2180+ * assertion failure in HTTP_IN input filter when Apache
2181+ * maintainer mode is enabled. It is arguable that the
2182+ * assertion check, which prohibits a zero length read,
2183+ * shouldn't exist, as why should a zero length read be not
2184+ * allowed if input filter processing still works when it
2185+ * does occur.
2186+ */
2187+
2188+ if (size == 0) {
2189+ if (!init) {
2190+ char dummy[1];
2191+
2192+ Py_BEGIN_ALLOW_THREADS
2193+ n = ap_get_client_block(self->r, dummy, 0);
2194+ Py_END_ALLOW_THREADS
2195+
2196+ if (n == -1) {
2197+ PyErr_SetString(PyExc_IOError, "request data read error");
2198+ return NULL;
2199+ }
2200+ }
2201+
2202+ return PyString_FromString("");
2203+ }
2204+
2205+ /*
2206+ * First deal with case where size has been specified. After
2207+ * that deal with case where expected that all remaining
2208+ * data is to be read in and returned as one string.
2209+ */
2210+
2211+ if (size > 0) {
2212+ /* Allocate string of the exact size required. */
2213+
2214+ result = PyString_FromStringAndSize(NULL, size);
2215+
2216+ if (!result)
2217+ return NULL;
2218+
2219+ buffer = PyString_AS_STRING((PyStringObject *)result);
2220+
2221+ /* Copy any residual data from use of readline(). */
2222+
2223+ if (self->buffer && self->length) {
2224+ if (size >= self->length) {
2225+ length = self->length;
2226+ memcpy(buffer, self->buffer + self->offset, length);
2227+ self->offset = 0;
2228+ self->length = 0;
2229+ }
2230+ else {
2231+ length = size;
2232+ memcpy(buffer, self->buffer + self->offset, length);
2233+ self->offset += length;
2234+ self->length -= length;
2235+ }
2236+ }
2237+
2238+ /* If all data residual buffer consumed then free it. */
2239+
2240+ if (!self->length) {
2241+ free(self->buffer);
2242+ self->buffer = NULL;
2243+ }
2244+
2245+ /* Read in remaining data required to achieve size. */
2246+
2247+ if (length < size) {
2248+ while (length != size) {
2249+ Py_BEGIN_ALLOW_THREADS
2250+ n = ap_get_client_block(self->r, buffer + length,
2251+ size - length);
2252+ Py_END_ALLOW_THREADS
2253+
2254+ if (n == -1) {
2255+ PyErr_SetString(PyExc_IOError, "request data read error");
2256+ Py_DECREF(result);
2257+ return NULL;
2258+ }
2259+ else if (n == 0) {
2260+ /* Have exhausted all the available input data. */
2261+
2262+ self->done = 1;
2263+ break;
2264+ }
2265+
2266+ length += n;
2267+ }
2268+
2269+ /*
2270+ * Resize the final string. If the size reduction is
2271+ * by more than 25% of the string size, then Python
2272+ * will allocate a new block of memory and copy the
2273+ * data into it.
2274+ */
2275+
2276+ if (length != size) {
2277+ if (_PyString_Resize(&result, length))
2278+ return NULL;
2279+ }
2280+ }
2281+ }
2282+ else {
2283+ /*
2284+ * Here we are going to try and read in all the
2285+ * remaining data. First we have to allocate a suitably
2286+ * large string, but we can't fully trust the amount
2287+ * that the request structure says is remaining based on
2288+ * the original content length though, as an input
2289+ * filter can insert/remove data from the input stream
2290+ * thereby invalidating the original content length.
2291+ * What we do is allow for an extra 25% above what we
2292+ * have already buffered and what the request structure
2293+ * says is remaining. A value of 25% has been chosen so
2294+ * as to match best how Python handles resizing of
2295+ * strings. Note that even though we do this and allow
2296+ * all available content, strictly speaking the WSGI
2297+ * specification says we should only read up until content
2298+ * length. This though is because the WSGI specification
2299+ * is deficient in dealing with the concept of mutating
2300+ * input filters. Since read() with no argument is also
2301+ * not allowed by WSGI specification implement it in the
2302+ * way which is most logical and ensure that input data
2303+ * is not truncated.
2304+ */
2305+
2306+ size = self->length;
2307+
2308+ if (!self->r->read_chunked && self->r->remaining > 0)
2309+ size += self->r->remaining;
2310+
2311+ size = size + (size >> 2);
2312+
2313+ if (size < 256)
2314+ size = self->r->read_chunked ? 8192 : 256;
2315+
2316+ /* Allocate string of the estimated size. */
2317+
2318+ result = PyString_FromStringAndSize(NULL, size);
2319+
2320+ if (!result)
2321+ return NULL;
2322+
2323+ buffer = PyString_AS_STRING((PyStringObject *)result);
2324+
2325+ /*
2326+ * Copy any residual data from use of readline(). The
2327+ * residual should always be less in size than the
2328+ * string we have allocated to hold it, so can consume
2329+ * all of it.
2330+ */
2331+
2332+ if (self->buffer && self->length) {
2333+ length = self->length;
2334+ memcpy(buffer, self->buffer + self->offset, length);
2335+ self->offset = 0;
2336+ self->length = 0;
2337+
2338+ free(self->buffer);
2339+ self->buffer = NULL;
2340+ }
2341+
2342+ /* Now make first attempt at reading remaining data. */
2343+
2344+ Py_BEGIN_ALLOW_THREADS
2345+ n = ap_get_client_block(self->r, buffer + length, size - length);
2346+ Py_END_ALLOW_THREADS
2347+
2348+ if (n == -1) {
2349+ PyErr_SetString(PyExc_IOError, "request data read error");
2350+ Py_DECREF(result);
2351+ return NULL;
2352+ }
2353+ else if (n == 0) {
2354+ /* Have exhausted all the available input data. */
2355+
2356+ self->done = 1;
2357+ }
2358+
2359+ length += n;
2360+
2361+ /*
2362+ * Don't just assume that all data has been read if
2363+ * amount read was less than that requested. Still must
2364+ * perform a read which returns that no more data found.
2365+ */
2366+
2367+ while (!self->done) {
2368+ if (length == size) {
2369+ /* Increase the size of the string by 25%. */
2370+
2371+ size = size + (size >> 2);
2372+
2373+ if (_PyString_Resize(&result, size))
2374+ return NULL;
2375+
2376+ buffer = PyString_AS_STRING((PyStringObject *)result);
2377+ }
2378+
2379+ /* Now make succesive attempt at reading data. */
2380+
2381+ Py_BEGIN_ALLOW_THREADS
2382+ n = ap_get_client_block(self->r, buffer + length, size - length);
2383+ Py_END_ALLOW_THREADS
2384+
2385+ if (n == -1) {
2386+ PyErr_SetString(PyExc_IOError, "request data read error");
2387+ Py_DECREF(result);
2388+ return NULL;
2389+ }
2390+ else if (n == 0) {
2391+ /* Have exhausted all the available input data. */
2392+
2393+ self->done = 1;
2394+ }
2395+
2396+ length += n;
2397+ }
2398+
2399+ /*
2400+ * Resize the final string. If the size reduction is by
2401+ * more than 25% of the string size, then Python will
2402+ * allocate a new block of memory and copy the data into
2403+ * it.
2404+ */
2405+
2406+ if (length != size) {
2407+ if (_PyString_Resize(&result, length))
2408+ return NULL;
2409+ }
2410+ }
2411+
2412+ return result;
2413+}
2414+
2415+static PyObject *Input_readline(InputObject *self, PyObject *args)
2416+{
2417+ long size = -1;
2418+
2419+ PyObject *result = NULL;
2420+ char *buffer = NULL;
2421+ apr_size_t length = 0;
2422+
2423+ apr_size_t n;
2424+
2425+ if (!self->r) {
2426+ PyErr_SetString(PyExc_RuntimeError, "request object has expired");
2427+ return NULL;
2428+ }
2429+
2430+ if (!PyArg_ParseTuple(args, "|l:readline", &size))
2431+ return NULL;
2432+
2433+ if (!self->init) {
2434+ if (!ap_should_client_block(self->r))
2435+ self->done = 1;
2436+
2437+ self->init = 1;
2438+ }
2439+
2440+ /*
2441+ * No point continuing if requested size is zero or if no
2442+ * more data to read and no buffered data.
2443+ */
2444+
2445+ if ((self->done && self->length == 0) || size == 0)
2446+ return PyString_FromString("");
2447+
2448+ /*
2449+ * First deal with case where size has been specified. After
2450+ * that deal with case where expected that a complete line
2451+ * is returned regardless of the size.
2452+ */
2453+
2454+ if (size > 0) {
2455+ /* Allocate string of the exact size required. */
2456+
2457+ result = PyString_FromStringAndSize(NULL, size);
2458+
2459+ if (!result)
2460+ return NULL;
2461+
2462+ buffer = PyString_AS_STRING((PyStringObject *)result);
2463+
2464+ /* Copy any residual data from use of readline(). */
2465+
2466+ if (self->buffer && self->length) {
2467+ char *p = NULL;
2468+ const char *q = NULL;
2469+
2470+ p = buffer;
2471+ q = self->buffer + self->offset;
2472+
2473+ while (self->length && length < size) {
2474+ self->offset++;
2475+ self->length--;
2476+ length++;
2477+ if ((*p++ = *q++) == '\n')
2478+ break;
2479+ }
2480+
2481+ /* If all data in residual buffer consumed then free it. */
2482+
2483+ if (!self->length) {
2484+ free(self->buffer);
2485+ self->buffer = NULL;
2486+ }
2487+ }
2488+
2489+ /*
2490+ * Read in remaining data required to achieve size. Note
2491+ * that can't just return whatever the first read might
2492+ * have returned if no EOL encountered as must return
2493+ * exactly the required size if no EOL unless that would
2494+ * have exhausted all input.
2495+ */
2496+
2497+ while ((!length || buffer[length-1] != '\n') &&
2498+ !self->done && length < size) {
2499+
2500+ char *p = NULL;
2501+ char *q = NULL;
2502+
2503+ Py_BEGIN_ALLOW_THREADS
2504+ n = ap_get_client_block(self->r, buffer + length, size - length);
2505+ Py_END_ALLOW_THREADS
2506+
2507+ if (n == -1) {
2508+ PyErr_SetString(PyExc_IOError, "request data read error");
2509+ Py_DECREF(result);
2510+ return NULL;
2511+ }
2512+ else if (n == 0) {
2513+ /* Have exhausted all the available input data. */
2514+
2515+ self->done = 1;
2516+ }
2517+ else {
2518+ /*
2519+ * Search for embedded EOL in what was read and if
2520+ * found copy any residual into a buffer for use
2521+ * next time the read functions are called.
2522+ */
2523+
2524+ p = buffer + length;
2525+ q = p + n;
2526+
2527+ while (p != q) {
2528+ length++;
2529+ if (*p++ == '\n')
2530+ break;
2531+ }
2532+
2533+ if (p != q) {
2534+ self->size = q - p;
2535+ self->buffer = (char *)malloc(self->size);
2536+ self->offset = 0;
2537+ self->length = self->size;
2538+
2539+ memcpy(self->buffer, p, self->size);
2540+ }
2541+ }
2542+ }
2543+
2544+ /*
2545+ * Resize the final string. If the size reduction is
2546+ * by more than 25% of the string size, then Python
2547+ * will allocate a new block of memory and copy the
2548+ * data into it.
2549+ */
2550+
2551+ if (length != size) {
2552+ if (_PyString_Resize(&result, length))
2553+ return NULL;
2554+ }
2555+ }
2556+ else {
2557+ /*
2558+ * Here we have to read in a line but where we have no
2559+ * idea how long it may be. What we can do first is if
2560+ * we have any residual data from a previous read
2561+ * operation, see if it contains an EOL. This means we
2562+ * have to do a search, but this is likely going to be
2563+ * better than having to resize and copy memory later on.
2564+ */
2565+
2566+ if (self->buffer && self->length) {
2567+ const char *p = NULL;
2568+ const char *q = NULL;
2569+
2570+ p = self->buffer + self->offset;
2571+ q = memchr(p, '\n', self->length);
2572+
2573+ if (q)
2574+ size = q - p;
2575+ }
2576+
2577+ /*
2578+ * If residual data buffer didn't contain an EOL, all we
2579+ * can do is allocate a reasonably sized string and if
2580+ * that isn't big enough keep increasing it in size. For
2581+ * this we will start out with a buffer 25% greater in
2582+ * size than what is stored in the residual data buffer
2583+ * or one the same size as Apache string size, whichever
2584+ * is greater.
2585+ */
2586+
2587+ if (self->buffer && size < 0) {
2588+ size = self->length;
2589+ size = size + (size >> 2);
2590+ }
2591+
2592+ if (size < HUGE_STRING_LEN)
2593+ size = HUGE_STRING_LEN;
2594+
2595+ /* Allocate string of the initial size. */
2596+
2597+ result = PyString_FromStringAndSize(NULL, size);
2598+
2599+ if (!result)
2600+ return NULL;
2601+
2602+ buffer = PyString_AS_STRING((PyStringObject *)result);
2603+
2604+ /* Copy any residual data from use of readline(). */
2605+
2606+ if (self->buffer && self->length) {
2607+ char *p = NULL;
2608+ const char *q = NULL;
2609+
2610+ p = buffer;
2611+ q = self->buffer + self->offset;
2612+
2613+ while (self->length && length < size) {
2614+ self->offset++;
2615+ self->length--;
2616+ length++;
2617+ if ((*p++ = *q++) == '\n')
2618+ break;
2619+ }
2620+
2621+ /* If all data in residual buffer consumed then free it. */
2622+
2623+ if (!self->length) {
2624+ free(self->buffer);
2625+ self->buffer = NULL;
2626+ }
2627+ }
2628+
2629+ /*
2630+ * Read in remaining data until find an EOL, or until all
2631+ * data has been consumed.
2632+ */
2633+
2634+ while ((!length || buffer[length-1] != '\n') && !self->done) {
2635+
2636+ char *p = NULL;
2637+ char *q = NULL;
2638+
2639+ Py_BEGIN_ALLOW_THREADS
2640+ n = ap_get_client_block(self->r, buffer + length, size - length);
2641+ Py_END_ALLOW_THREADS
2642+
2643+ if (n == -1) {
2644+ PyErr_SetString(PyExc_IOError, "request data read error");
2645+ Py_DECREF(result);
2646+ return NULL;
2647+ }
2648+ else if (n == 0) {
2649+ /* Have exhausted all the available input data. */
2650+
2651+ self->done = 1;
2652+ }
2653+ else {
2654+ /*
2655+ * Search for embedded EOL in what was read and if
2656+ * found copy any residual into a buffer for use
2657+ * next time the read functions are called.
2658+ */
2659+
2660+ p = buffer + length;
2661+ q = p + n;
2662+
2663+ while (p != q) {
2664+ length++;
2665+ if (*p++ == '\n')
2666+ break;
2667+ }
2668+
2669+ if (p != q) {
2670+ self->size = q - p;
2671+ self->buffer = (char *)malloc(self->size);
2672+ self->offset = 0;
2673+ self->length = self->size;
2674+
2675+ memcpy(self->buffer, p, self->size);
2676+ }
2677+
2678+ if (buffer[length-1] != '\n' && length == size) {
2679+ /* Increase size of string and keep going. */
2680+
2681+ size = size + (size >> 2);
2682+
2683+ if (_PyString_Resize(&result, size))
2684+ return NULL;
2685+
2686+ buffer = PyString_AS_STRING((PyStringObject *)result);
2687+ }
2688+ }
2689+ }
2690+
2691+ /*
2692+ * Resize the final string. If the size reduction is by
2693+ * more than 25% of the string size, then Python will
2694+ * allocate a new block of memory and copy the data into
2695+ * it.
2696+ */
2697+
2698+ if (length != size) {
2699+ if (_PyString_Resize(&result, length))
2700+ return NULL;
2701+ }
2702+ }
2703+
2704+ return result;
2705+}
2706+
2707+static PyObject *Input_readlines(InputObject *self, PyObject *args)
2708+{
2709+ long hint = 0;
2710+ long length = 0;
2711+
2712+ PyObject *result = NULL;
2713+ PyObject *line = NULL;
2714+ PyObject *rlargs = NULL;
2715+
2716+ if (!self->r) {
2717+ PyErr_SetString(PyExc_RuntimeError, "request object has expired");
2718+ return NULL;
2719+ }
2720+
2721+ if (!PyArg_ParseTuple(args, "|l:readlines", &hint))
2722+ return NULL;
2723+
2724+ result = PyList_New(0);
2725+ if (!result)
2726+ return NULL;
2727+
2728+ rlargs = PyTuple_New(0);
2729+ if (!rlargs) {
2730+ Py_DECREF(result);
2731+ return NULL;
2732+ }
2733+
2734+ while (1) {
2735+ int n;
2736+
2737+ if (!(line = Input_readline(self, rlargs))) {
2738+ Py_DECREF(result);
2739+ result = NULL;
2740+ break;
2741+ }
2742+
2743+ if ((n = PyString_Size(line)) == 0) {
2744+ Py_DECREF(line);
2745+ break;
2746+ }
2747+
2748+ if (PyList_Append(result, line) == -1) {
2749+ Py_DECREF(line);
2750+ Py_DECREF(result);
2751+ result = NULL;
2752+ break;
2753+ }
2754+
2755+ Py_DECREF(line);
2756+
2757+ length += n;
2758+ if (hint > 0 && length >= hint)
2759+ break;
2760+ }
2761+
2762+ Py_DECREF(rlargs);
2763+
2764+ return result;
2765+}
2766+
2767+static PyMethodDef Input_methods[] = {
2768+ { "close", (PyCFunction)Input_close, METH_VARARGS, 0 },
2769+ { "read", (PyCFunction)Input_read, METH_VARARGS, 0 },
2770+ { "readline", (PyCFunction)Input_readline, METH_VARARGS, 0 },
2771+ { "readlines", (PyCFunction)Input_readlines, METH_VARARGS, 0 },
2772+ { NULL, NULL}
2773+};
2774+
2775+static PyObject *Input_iter(InputObject *self)
2776+{
2777+ if (!self->r) {
2778+ PyErr_SetString(PyExc_RuntimeError, "request object has expired");
2779+ return NULL;
2780+ }
2781+
2782+ Py_INCREF(self);
2783+ return (PyObject *)self;
2784+}
2785+
2786+static PyObject *Input_iternext(InputObject *self)
2787+{
2788+ PyObject *line = NULL;
2789+ PyObject *rlargs = NULL;
2790+
2791+ if (!self->r) {
2792+ PyErr_SetString(PyExc_RuntimeError, "request object has expired");
2793+ return NULL;
2794+ }
2795+
2796+ rlargs = PyTuple_New(0);
2797+
2798+ if (!rlargs)
2799+ return NULL;
2800+
2801+ line = Input_readline(self, rlargs);
2802+
2803+ Py_DECREF(rlargs);
2804+
2805+ if (!line)
2806+ return NULL;
2807+
2808+ if (PyString_GET_SIZE(line) == 0) {
2809+ PyErr_SetObject(PyExc_StopIteration, Py_None);
2810+ Py_DECREF(line);
2811+ return NULL;
2812+ }
2813+
2814+ return line;
2815+}
2816+
2817+static PyTypeObject Input_Type = {
2818+ PyVarObject_HEAD_INIT(NULL, 0)
2819+ "mod_wsgi.Input", /*tp_name*/
2820+ sizeof(InputObject), /*tp_basicsize*/
2821+ 0, /*tp_itemsize*/
2822+ /* methods */
2823+ (destructor)Input_dealloc, /*tp_dealloc*/
2824+ 0, /*tp_print*/
2825+ 0, /*tp_getattr*/
2826+ 0, /*tp_setattr*/
2827+ 0, /*tp_compare*/
2828+ 0, /*tp_repr*/
2829+ 0, /*tp_as_number*/
2830+ 0, /*tp_as_sequence*/
2831+ 0, /*tp_as_mapping*/
2832+ 0, /*tp_hash*/
2833+ 0, /*tp_call*/
2834+ 0, /*tp_str*/
2835+ 0, /*tp_getattro*/
2836+ 0, /*tp_setattro*/
2837+ 0, /*tp_as_buffer*/
2838+#if defined(Py_TPFLAGS_HAVE_ITER)
2839+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, /*tp_flags*/
2840+#else
2841+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
2842+#endif
2843+ 0, /*tp_doc*/
2844+ 0, /*tp_traverse*/
2845+ 0, /*tp_clear*/
2846+ 0, /*tp_richcompare*/
2847+ 0, /*tp_weaklistoffset*/
2848+ (getiterfunc)Input_iter, /*tp_iter*/
2849+ (iternextfunc)Input_iternext, /*tp_iternext*/
2850+ Input_methods, /*tp_methods*/
2851+ 0, /*tp_members*/
2852+ 0, /*tp_getset*/
2853+ 0, /*tp_base*/
2854+ 0, /*tp_dict*/
2855+ 0, /*tp_descr_get*/
2856+ 0, /*tp_descr_set*/
2857+ 0, /*tp_dictoffset*/
2858+ 0, /*tp_init*/
2859+ 0, /*tp_alloc*/
2860+ 0, /*tp_new*/
2861+ 0, /*tp_free*/
2862+ 0, /*tp_is_gc*/
2863+};
2864+
2865+typedef struct {
2866+ PyObject_HEAD
2867+ int result;
2868+ request_rec *r;
2869+#if defined(MOD_WSGI_WITH_BUCKETS)
2870+ apr_bucket_brigade *bb;
2871+#endif
2872+ WSGIRequestConfig *config;
2873+ InputObject *input;
2874+ PyObject *log;
2875+ int status;
2876+ const char *status_line;
2877+ PyObject *headers;
2878+ PyObject *sequence;
2879+ int content_length_set;
2880+ apr_off_t content_length;
2881+ apr_off_t output_length;
2882+} AdapterObject;
2883+
2884+static PyTypeObject Adapter_Type;
2885+
2886+typedef struct {
2887+ PyObject_HEAD
2888+ AdapterObject *adapter;
2889+ PyObject *filelike;
2890+ apr_size_t blksize;
2891+} StreamObject;
2892+
2893+static PyTypeObject Stream_Type;
2894+
2895+static AdapterObject *newAdapterObject(request_rec *r)
2896+{
2897+ AdapterObject *self;
2898+
2899+ self = PyObject_New(AdapterObject, &Adapter_Type);
2900+ if (self == NULL)
2901+ return NULL;
2902+
2903+ self->result = HTTP_INTERNAL_SERVER_ERROR;
2904+
2905+ self->r = r;
2906+
2907+#if defined(MOD_WSGI_WITH_BUCKETS)
2908+ self->bb = NULL;
2909+#endif
2910+
2911+ self->config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
2912+ &wsgi_module);
2913+
2914+ self->status = HTTP_INTERNAL_SERVER_ERROR;
2915+ self->status_line = NULL;
2916+ self->headers = NULL;
2917+ self->sequence = NULL;
2918+
2919+ self->content_length_set = 0;
2920+ self->content_length = 0;
2921+ self->output_length = 0;
2922+
2923+ self->input = newInputObject(r);
2924+ self->log = newLogObject(r, APLOG_ERR, NULL);
2925+
2926+ return self;
2927+}
2928+
2929+static void Adapter_dealloc(AdapterObject *self)
2930+{
2931+ Py_XDECREF(self->headers);
2932+ Py_XDECREF(self->sequence);
2933+
2934+ Py_DECREF(self->input);
2935+ Py_DECREF(self->log);
2936+
2937+ PyObject_Del(self);
2938+}
2939+
2940+static PyObject *Adapter_start_response(AdapterObject *self, PyObject *args)
2941+{
2942+ const char *status = NULL;
2943+ PyObject *headers = NULL;
2944+ PyObject *exc_info = NULL;
2945+
2946+ PyObject *item = NULL;
2947+ PyObject *latin_item = NULL;
2948+
2949+ char* value = NULL;
2950+
2951+ if (!self->r) {
2952+ PyErr_SetString(PyExc_RuntimeError, "request object has expired");
2953+ return NULL;
2954+ }
2955+
2956+ if (!PyArg_ParseTuple(args, "OO|O:start_response",
2957+ &item, &headers, &exc_info)) {
2958+ return NULL;
2959+ }
2960+
2961+#if PY_MAJOR_VERSION >= 3
2962+ if (PyUnicode_Check(item)) {
2963+ latin_item = PyUnicode_AsLatin1String(item);
2964+ if (!latin_item) {
2965+ PyErr_Format(PyExc_TypeError, "expected byte string object for "
2966+ "status, value containing non 'latin-1' characters "
2967+ "found");
2968+ return NULL;
2969+ }
2970+
2971+ item = latin_item;
2972+ }
2973+#endif
2974+
2975+ if (!PyString_Check(item)) {
2976+ PyErr_Format(PyExc_TypeError, "expected byte string object for "
2977+ "status, value of type %.200s found",
2978+ item->ob_type->tp_name);
2979+ Py_XDECREF(latin_item);
2980+ return NULL;
2981+ }
2982+
2983+ status = PyString_AsString(item);
2984+
2985+ if (!PyList_Check(headers)) {
2986+ PyErr_SetString(PyExc_TypeError, "response headers must be a list");
2987+ Py_XDECREF(latin_item);
2988+ return NULL;
2989+ }
2990+
2991+ if (exc_info && exc_info != Py_None) {
2992+ if (self->status_line && !self->headers) {
2993+ PyObject *type = NULL;
2994+ PyObject *value = NULL;
2995+ PyObject *traceback = NULL;
2996+
2997+ if (!PyArg_ParseTuple(exc_info, "OOO", &type,
2998+ &value, &traceback)) {
2999+ Py_XDECREF(latin_item);
3000+ return NULL;
3001+ }
3002+
3003+ Py_INCREF(type);
3004+ Py_INCREF(value);
3005+ Py_INCREF(traceback);
3006+
3007+ PyErr_Restore(type, value, traceback);
3008+
3009+ Py_XDECREF(latin_item);
3010+
3011+ return NULL;
3012+ }
3013+ }
3014+ else if (self->status_line && !self->headers) {
3015+ PyErr_SetString(PyExc_RuntimeError, "headers have already been sent");
3016+ Py_XDECREF(latin_item);
3017+ return NULL;
3018+ }
3019+
3020+ self->status_line = apr_pstrdup(self->r->pool, status);
3021+
3022+ value = ap_getword(self->r->pool, &status, ' ');
3023+
3024+ errno = 0;
3025+ self->status = strtol(value, &value, 10);
3026+
3027+ if (*value || errno == ERANGE) {
3028+ PyErr_SetString(PyExc_TypeError, "status value is not an integer");
3029+ Py_XDECREF(latin_item);
3030+ return NULL;
3031+ }
3032+
3033+ if (!*status) {
3034+ PyErr_SetString(PyExc_ValueError, "status message was not supplied");
3035+ Py_XDECREF(latin_item);
3036+ return NULL;
3037+ }
3038+
3039+ Py_XDECREF(self->headers);
3040+
3041+ self->headers = headers;
3042+
3043+ Py_INCREF(self->headers);
3044+
3045+ Py_XDECREF(latin_item);
3046+
3047+ return PyObject_GetAttrString((PyObject *)self, "write");
3048+}
3049+
3050+static int Adapter_output(AdapterObject *self, const char *data, int length,
3051+ int exception_when_aborted)
3052+{
3053+ int i = 0;
3054+ int n = 0;
3055+ apr_status_t rv;
3056+ request_rec *r;
3057+
3058+#if defined(MOD_WSGI_WITH_DAEMONS)
3059+ if (wsgi_inactivity_timeout) {
3060+ apr_thread_mutex_lock(wsgi_shutdown_lock);
3061+ wsgi_inactivity_shutdown_time = apr_time_now();
3062+ wsgi_inactivity_shutdown_time += wsgi_inactivity_timeout;
3063+ apr_thread_mutex_unlock(wsgi_shutdown_lock);
3064+ }
3065+#endif
3066+
3067+ if (!self->status_line) {
3068+ PyErr_SetString(PyExc_RuntimeError, "response has not been started");
3069+ return 0;
3070+ }
3071+
3072+ r = self->r;
3073+
3074+ /* Have response headers yet been sent. */
3075+
3076+ if (self->headers) {
3077+ /*
3078+ * Apache prior to Apache 2.2.8 has a bug in it
3079+ * whereby it doesn't force '100 Continue'
3080+ * response before responding with headers if no
3081+ * read. So, force a zero length read before
3082+ * sending the headers if haven't yet attempted
3083+ * to read anything. This will ensure that if no
3084+ * request content has been read that any '100
3085+ * Continue' response will be flushed and sent
3086+ * back to the client if client was expecting
3087+ * one. Only want to do this for 2xx and 3xx
3088+ * status values. Note that even though Apple
3089+ * supplied version of Apache on MacOS X Leopard
3090+ * is newer than version 2.2.8, the header file
3091+ * has never been patched when they make updates
3092+ * and so anything compiled against it thinks it
3093+ * is older.
3094+ */
3095+
3096+#if (AP_SERVER_MAJORVERSION_NUMBER == 1) || \
3097+ (AP_SERVER_MAJORVERSION_NUMBER == 2 && \
3098+ AP_SERVER_MINORVERSION_NUMBER < 2) || \
3099+ (AP_SERVER_MAJORVERSION_NUMBER == 2 && \
3100+ AP_SERVER_MINORVERSION_NUMBER == 2 && \
3101+ AP_SERVER_PATCHLEVEL_NUMBER < 8)
3102+
3103+ if (!self->input->init) {
3104+ if (self->status >= 200 && self->status < 400) {
3105+ PyObject *args = NULL;
3106+ PyObject *result = NULL;
3107+ args = Py_BuildValue("(i)", 0);
3108+ result = Input_read(self->input, args);
3109+ if (PyErr_Occurred())
3110+ PyErr_Clear();
3111+ Py_DECREF(args);
3112+ Py_XDECREF(result);
3113+ }
3114+ }
3115+
3116+#endif
3117+
3118+ /* Now setup response headers in request object. */
3119+
3120+ r->status = self->status;
3121+ r->status_line = self->status_line;
3122+
3123+ for (i = 0; i < PyList_Size(self->headers); i++) {
3124+ PyObject *tuple = NULL;
3125+
3126+ PyObject *object1 = NULL;
3127+ PyObject *object2 = NULL;
3128+
3129+ char *name = NULL;
3130+ char *value = NULL;
3131+
3132+ tuple = PyList_GetItem(self->headers, i);
3133+
3134+ if (!PyTuple_Check(tuple)) {
3135+ PyErr_Format(PyExc_TypeError, "list of tuple values "
3136+ "expected, value of type %.200s found",
3137+ tuple->ob_type->tp_name);
3138+ return 0;
3139+ }
3140+
3141+ if (PyTuple_Size(tuple) != 2) {
3142+ PyErr_Format(PyExc_ValueError, "tuple of length 2 "
3143+ "expected, length is %d",
3144+ (int)PyTuple_Size(tuple));
3145+ return 0;
3146+ }
3147+
3148+ object1 = PyTuple_GetItem(tuple, 0);
3149+ object2 = PyTuple_GetItem(tuple, 1);
3150+
3151+ if (PyString_Check(object1)) {
3152+ name = PyString_AsString(object1);
3153+ }
3154+#if PY_MAJOR_VERSION >= 3
3155+ else if (PyUnicode_Check(object1)) {
3156+ PyObject *latin_object;
3157+ latin_object = PyUnicode_AsLatin1String(object1);
3158+ if (!latin_object) {
3159+ PyErr_Format(PyExc_TypeError, "header name "
3160+ "contained non 'latin-1' characters ");
3161+ return 0;
3162+ }
3163+
3164+ name = apr_pstrdup(r->pool, PyString_AsString(latin_object));
3165+ Py_DECREF(latin_object);
3166+ }
3167+#endif
3168+ else {
3169+ PyErr_Format(PyExc_TypeError, "expected byte string object "
3170+ "for header name, value of type %.200s "
3171+ "found", object1->ob_type->tp_name);
3172+ return 0;
3173+ }
3174+
3175+ if (PyString_Check(object2)) {
3176+ value = PyString_AsString(object2);
3177+ }
3178+#if PY_MAJOR_VERSION >= 3
3179+ else if (PyUnicode_Check(object2)) {
3180+ PyObject *latin_object;
3181+ latin_object = PyUnicode_AsLatin1String(object2);
3182+ if (!latin_object) {
3183+ PyErr_Format(PyExc_TypeError, "header value "
3184+ "contained non 'latin-1' characters ");
3185+ return 0;
3186+ }
3187+
3188+ value = apr_pstrdup(r->pool, PyString_AsString(latin_object));
3189+ Py_DECREF(latin_object);
3190+ }
3191+#endif
3192+ else {
3193+ PyErr_Format(PyExc_TypeError, "expected byte string object "
3194+ "for header value, value of type %.200s "
3195+ "found", object2->ob_type->tp_name);
3196+ return 0;
3197+ }
3198+
3199+ if (strchr(name, '\n') != 0 || strchr(value, '\n') != 0) {
3200+ PyErr_Format(PyExc_ValueError, "embedded newline in "
3201+ "response header with name '%s' and value '%s'",
3202+ name, value);
3203+ return 0;
3204+ }
3205+
3206+ if (!strcasecmp(name, "Content-Type")) {
3207+#if AP_SERVER_MAJORVERSION_NUMBER < 2
3208+ r->content_type = apr_pstrdup(r->pool, value);
3209+#else
3210+ /*
3211+ * In a daemon child process we cannot call the
3212+ * function ap_set_content_type() as want to
3213+ * avoid adding any output filters based on the
3214+ * type of file being served as this will be
3215+ * done in the main Apache child process which
3216+ * proxied the request to the daemon process.
3217+ */
3218+
3219+ if (*self->config->process_group)
3220+ r->content_type = apr_pstrdup(r->pool, value);
3221+ else
3222+ ap_set_content_type(r, value);
3223+#endif
3224+ }
3225+ else if (!strcasecmp(name, "Content-Length")) {
3226+ char *v = value;
3227+ long l = 0;
3228+
3229+ errno = 0;
3230+ l = strtol(v, &v, 10);
3231+ if (*v || errno == ERANGE || l < 0) {
3232+ PyErr_SetString(PyExc_ValueError,
3233+ "invalid content length");
3234+ return 0;
3235+ }
3236+
3237+ ap_set_content_length(r, l);
3238+
3239+ self->content_length_set = 1;
3240+ self->content_length = l;
3241+ }
3242+ else if (!strcasecmp(name, "WWW-Authenticate")) {
3243+ apr_table_add(r->err_headers_out, name, value);
3244+ }
3245+ else {
3246+ apr_table_add(r->headers_out, name, value);
3247+ }
3248+ }
3249+
3250+ /* Need to force output of headers when using Apache 1.3. */
3251+
3252+ Py_BEGIN_ALLOW_THREADS
3253+ ap_send_http_header(r);
3254+ Py_END_ALLOW_THREADS
3255+
3256+ /*
3257+ * Reset flag indicating whether '100 Continue' response
3258+ * expected. If we don't do this then if an attempt to read
3259+ * input for the first time is after headers have been
3260+ * sent, then Apache is wrongly generate the '100 Continue'
3261+ * response into the response content. Not sure if this is
3262+ * a bug in Apache, or that it truly believes that input
3263+ * will never be read after the response headers have been
3264+ * sent.
3265+ */
3266+
3267+ r->expecting_100 = 0;
3268+
3269+ /* No longer need headers now that they have been sent. */
3270+
3271+ Py_DECREF(self->headers);
3272+ self->headers = NULL;
3273+ }
3274+
3275+ /*
3276+ * If content length was specified, ensure that we don't
3277+ * actually output more data than was specified as being
3278+ * sent as otherwise technically in violation of HTTP RFC.
3279+ */
3280+
3281+ if (length) {
3282+ int output_length = length;
3283+
3284+ if (self->content_length_set) {
3285+ if (self->output_length < self->content_length) {
3286+ if (self->output_length + length > self->content_length) {
3287+ length = self->content_length - self->output_length;
3288+ }
3289+ }
3290+ else
3291+ length = 0;
3292+ }
3293+
3294+ self->output_length += output_length;
3295+ }
3296+
3297+ /* Now output any data. */
3298+
3299+ if (length) {
3300+#if defined(MOD_WSGI_WITH_BUCKETS)
3301+ apr_bucket *b;
3302+
3303+ /*
3304+ * When using Apache 2.X can use lower level
3305+ * bucket brigade APIs. This is preferred as
3306+ * ap_rwrite()/ap_rflush() will grow memory in
3307+ * the request pool on each call, which will
3308+ * result in an increase in memory use over time
3309+ * when streaming of data is being performed.
3310+ * The memory is still reclaimed, but only at
3311+ * the end of the request. Using bucket brigade
3312+ * API avoids this, and also avoids any copying
3313+ * of response data due to buffering performed
3314+ * by ap_rwrite().
3315+ */
3316+
3317+ if (r->connection->aborted) {
3318+ if (!exception_when_aborted) {
3319+ ap_log_rerror(APLOG_MARK, WSGI_LOG_DEBUG(0), self->r,
3320+ "mod_wsgi (pid=%d): Client closed connection.",
3321+ getpid());
3322+ }
3323+ else
3324+ PyErr_SetString(PyExc_IOError, "client connection closed");
3325+
3326+ return 0;
3327+ }
3328+
3329+ if (!self->bb) {
3330+ self->bb = apr_brigade_create(r->pool,
3331+ r->connection->bucket_alloc);
3332+ }
3333+
3334+ b = apr_bucket_transient_create(data, length,
3335+ r->connection->bucket_alloc);
3336+ APR_BRIGADE_INSERT_TAIL(self->bb, b);
3337+
3338+ b = apr_bucket_flush_create(r->connection->bucket_alloc);
3339+ APR_BRIGADE_INSERT_TAIL(self->bb, b);
3340+
3341+ Py_BEGIN_ALLOW_THREADS
3342+ rv = ap_pass_brigade(r->output_filters, self->bb);
3343+ Py_END_ALLOW_THREADS
3344+
3345+ if (rv != APR_SUCCESS) {
3346+ PyErr_SetString(PyExc_IOError, "failed to write data");
3347+ return 0;
3348+ }
3349+
3350+ Py_BEGIN_ALLOW_THREADS
3351+ apr_brigade_cleanup(self->bb);
3352+ Py_END_ALLOW_THREADS
3353+#else
3354+ /*
3355+ * In Apache 1.3, the bucket brigade system doesn't exist,
3356+ * so have no choice but to use ap_rwrite()/ap_rflush().
3357+ * It is not believed that Apache 1.3 suffers the memory
3358+ * accumulation problem when streaming lots of data.
3359+ */
3360+
3361+ Py_BEGIN_ALLOW_THREADS
3362+ n = ap_rwrite(data, length, r);
3363+ Py_END_ALLOW_THREADS
3364+
3365+ if (n == -1) {
3366+ PyErr_SetString(PyExc_IOError, "failed to write data");
3367+ return 0;
3368+ }
3369+
3370+ Py_BEGIN_ALLOW_THREADS
3371+ n = ap_rflush(r);
3372+ Py_END_ALLOW_THREADS
3373+
3374+ if (n == -1) {
3375+ PyErr_SetString(PyExc_IOError, "failed to flush data");
3376+ return 0;
3377+ }
3378+#endif
3379+ }
3380+
3381+ /*
3382+ * Check whether aborted connection was found when data
3383+ * being written, otherwise will not be flagged until next
3384+ * time that data is being written. Early detection is
3385+ * better as it may have been the last data block being
3386+ * written and application may think that data has all
3387+ * been written. In a streaming application, we also want
3388+ * to avoid any additional data processing to generate any
3389+ * successive data.
3390+ */
3391+
3392+ if (r->connection->aborted) {
3393+ if (!exception_when_aborted) {
3394+ ap_log_rerror(APLOG_MARK, WSGI_LOG_DEBUG(0), self->r,
3395+ "mod_wsgi (pid=%d): Client closed connection.",
3396+ getpid());
3397+ }
3398+ else
3399+ PyErr_SetString(PyExc_IOError, "client connection closed");
3400+
3401+ return 0;
3402+ }
3403+
3404+ return 1;
3405+}
3406+
3407+#if AP_SERVER_MAJORVERSION_NUMBER >= 2
3408+
3409+/* Split buckets at 1GB when sending large files. */
3410+
3411+#define MAX_BUCKET_SIZE (0x40000000)
3412+
3413+static int Adapter_output_file(AdapterObject *self, apr_file_t* tmpfile,
3414+ apr_off_t offset, apr_off_t len)
3415+{
3416+ request_rec *r;
3417+ apr_bucket *b;
3418+ apr_status_t rv;
3419+ apr_bucket_brigade *bb;
3420+
3421+ r = self->r;
3422+
3423+ if (r->connection->aborted) {
3424+ PyErr_SetString(PyExc_IOError, "client connection closed");
3425+ return 0;
3426+ }
3427+
3428+ if (len == 0)
3429+ return 1;
3430+
3431+ bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
3432+
3433+ if (sizeof(apr_off_t) == sizeof(apr_size_t) || len < MAX_BUCKET_SIZE) {
3434+ /* Can use a single bucket to send file. */
3435+
3436+ b = apr_bucket_file_create(tmpfile, offset, (apr_size_t)len, r->pool,
3437+ r->connection->bucket_alloc);
3438+ }
3439+ else {
3440+ /* Need to create multiple buckets to send file. */
3441+
3442+ b = apr_bucket_file_create(tmpfile, offset, MAX_BUCKET_SIZE, r->pool,
3443+ r->connection->bucket_alloc);
3444+
3445+ while (len > MAX_BUCKET_SIZE) {
3446+ apr_bucket *cb;
3447+ apr_bucket_copy(b, &cb);
3448+ APR_BRIGADE_INSERT_TAIL(bb, cb);
3449+ b->start += MAX_BUCKET_SIZE;
3450+ len -= MAX_BUCKET_SIZE;
3451+ }
3452+
3453+ /* Resize just the last bucket */
3454+
3455+ b->length = (apr_size_t)len;
3456+ }
3457+
3458+ APR_BRIGADE_INSERT_TAIL(bb, b);
3459+
3460+ b = apr_bucket_flush_create(r->connection->bucket_alloc);
3461+ APR_BRIGADE_INSERT_TAIL(bb, b);
3462+
3463+ b = apr_bucket_eos_create(r->connection->bucket_alloc);
3464+ APR_BRIGADE_INSERT_TAIL(bb, b);
3465+
3466+ Py_BEGIN_ALLOW_THREADS
3467+ rv = ap_pass_brigade(r->output_filters, bb);
3468+ Py_END_ALLOW_THREADS
3469+
3470+ if (rv != APR_SUCCESS) {
3471+ PyErr_SetString(PyExc_IOError, "failed to write data");
3472+ return 0;
3473+ }
3474+
3475+ Py_BEGIN_ALLOW_THREADS
3476+ apr_brigade_destroy(bb);
3477+ Py_END_ALLOW_THREADS
3478+
3479+ if (r->connection->aborted) {
3480+ PyErr_SetString(PyExc_IOError, "client connection closed");
3481+ return 0;
3482+ }
3483+
3484+ return 1;
3485+}
3486+
3487+#endif
3488+
3489+#if AP_SERVER_MAJORVERSION_NUMBER >= 2
3490+APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *));
3491+static APR_OPTIONAL_FN_TYPE(ssl_is_https) *wsgi_is_https = NULL;
3492+#endif
3493+
3494+static PyObject *Adapter_environ(AdapterObject *self)
3495+{
3496+ request_rec *r = NULL;
3497+
3498+ PyObject *vars = NULL;
3499+ PyObject *object = NULL;
3500+
3501+ const apr_array_header_t *head = NULL;
3502+ const apr_table_entry_t *elts = NULL;
3503+
3504+ int i = 0;
3505+
3506+ const char *scheme = NULL;
3507+
3508+ /* Create the WSGI environment dictionary. */
3509+
3510+ vars = PyDict_New();
3511+
3512+ /* Merge the CGI environment into the WSGI environment. */
3513+
3514+ r = self->r;
3515+
3516+ head = apr_table_elts(r->subprocess_env);
3517+ elts = (apr_table_entry_t *)head->elts;
3518+
3519+ for (i = 0; i < head->nelts; ++i) {
3520+ if (elts[i].key) {
3521+ if (elts[i].val) {
3522+#if PY_MAJOR_VERSION >= 3
3523+ if (!strcmp(elts[i].val, "DOCUMENT_ROOT")) {
3524+ object = PyUnicode_Decode(elts[i].val, strlen(elts[i].val),
3525+ Py_FileSystemDefaultEncoding,
3526+ "surrogateescape");
3527+ }
3528+ else if (!strcmp(elts[i].val, "SCRIPT_FILENAME")) {
3529+ object = PyUnicode_Decode(elts[i].val, strlen(elts[i].val),
3530+ Py_FileSystemDefaultEncoding,
3531+ "surrogateescape");
3532+ }
3533+ else {
3534+ object = PyUnicode_DecodeLatin1(elts[i].val,
3535+ strlen(elts[i].val), NULL);
3536+ }
3537+#else
3538+ object = PyString_FromString(elts[i].val);
3539+#endif
3540+ PyDict_SetItemString(vars, elts[i].key, object);
3541+ Py_DECREF(object);
3542+ }
3543+ else
3544+ PyDict_SetItemString(vars, elts[i].key, Py_None);
3545+ }
3546+ }
3547+
3548+ PyDict_DelItemString(vars, "PATH");
3549+
3550+ /* Now setup all the WSGI specific environment values. */
3551+
3552+ object = Py_BuildValue("(ii)", 1, 1);
3553+ PyDict_SetItemString(vars, "wsgi.version", object);
3554+ Py_DECREF(object);
3555+
3556+ object = PyBool_FromLong(wsgi_multithread);
3557+ PyDict_SetItemString(vars, "wsgi.multithread", object);
3558+ Py_DECREF(object);
3559+
3560+ object = PyBool_FromLong(wsgi_multiprocess);
3561+ PyDict_SetItemString(vars, "wsgi.multiprocess", object);
3562+ Py_DECREF(object);
3563+
3564+#if defined(MOD_WSGI_WITH_DAEMONS)
3565+ if (wsgi_daemon_process) {
3566+ if (wsgi_daemon_process->group->threads == 1 &&
3567+ wsgi_daemon_process->group->maximum_requests == 1) {
3568+ PyDict_SetItemString(vars, "wsgi.run_once", Py_True);
3569+ }
3570+ else
3571+ PyDict_SetItemString(vars, "wsgi.run_once", Py_False);
3572+ }
3573+ else
3574+ PyDict_SetItemString(vars, "wsgi.run_once", Py_False);
3575+#else
3576+ PyDict_SetItemString(vars, "wsgi.run_once", Py_False);
3577+#endif
3578+
3579+ scheme = apr_table_get(r->subprocess_env, "HTTPS");
3580+
3581+ if (scheme && (!strcasecmp(scheme, "On") || !strcmp(scheme, "1"))) {
3582+#if PY_MAJOR_VERSION >= 3
3583+ object = PyUnicode_FromString("https");
3584+#else
3585+ object = PyString_FromString("https");
3586+#endif
3587+ PyDict_SetItemString(vars, "wsgi.url_scheme", object);
3588+ Py_DECREF(object);
3589+ }
3590+ else {
3591+#if PY_MAJOR_VERSION >= 3
3592+ object = PyUnicode_FromString("http");
3593+#else
3594+ object = PyString_FromString("http");
3595+#endif
3596+ PyDict_SetItemString(vars, "wsgi.url_scheme", object);
3597+ Py_DECREF(object);
3598+ }
3599+
3600+ /*
3601+ * Setup log object for WSGI errors. Don't decrement
3602+ * reference to log object as keep reference to it.
3603+ */
3604+
3605+ object = (PyObject *)self->log;
3606+ PyDict_SetItemString(vars, "wsgi.errors", object);
3607+
3608+ /* Setup input object for request content. */
3609+
3610+ object = (PyObject *)self->input;
3611+ PyDict_SetItemString(vars, "wsgi.input", object);
3612+
3613+ /* Setup file wrapper object for efficient file responses. */
3614+
3615+ object = PyObject_GetAttrString((PyObject *)self, "file_wrapper");
3616+ PyDict_SetItemString(vars, "wsgi.file_wrapper", object);
3617+ Py_DECREF(object);
3618+
3619+ /* Add mod_wsgi version information. */
3620+
3621+ object = Py_BuildValue("(ii)", MOD_WSGI_MAJORVERSION_NUMBER,
3622+ MOD_WSGI_MINORVERSION_NUMBER);
3623+ PyDict_SetItemString(vars, "mod_wsgi.version", object);
3624+ Py_DECREF(object);
3625+
3626+ /*
3627+ * If Apache extensions are enabled and running in embedded
3628+ * mode add a CObject reference to the Apache request_rec
3629+ * structure instance.
3630+ */
3631+
3632+ if (!wsgi_daemon_pool && self->config->pass_apache_request) {
3633+ object = PyCObject_FromVoidPtr(self->r, 0);
3634+ PyDict_SetItemString(vars, "apache.request_rec", object);
3635+ Py_DECREF(object);
3636+ }
3637+
3638+ return vars;
3639+}
3640+
3641+static int Adapter_process_file_wrapper(AdapterObject *self)
3642+{
3643+ int done = 0;
3644+
3645+#ifndef WIN32
3646+#if AP_SERVER_MAJORVERSION_NUMBER >= 2
3647+
3648+ PyObject *filelike = NULL;
3649+ PyObject *method = NULL;
3650+ PyObject *object = NULL;
3651+
3652+ apr_status_t rv = 0;
3653+
3654+ apr_os_file_t fd = -1;
3655+ apr_file_t *tmpfile = NULL;
3656+ apr_finfo_t finfo;
3657+
3658+ apr_off_t fd_offset = 0;
3659+ apr_off_t fo_offset = 0;
3660+
3661+ apr_off_t length = 0;
3662+
3663+ /* Perform file wrapper optimisations where possible. */
3664+
3665+ if (self->sequence->ob_type != &Stream_Type)
3666+ return 0;
3667+
3668+ /*
3669+ * Only attempt to perform optimisations if the
3670+ * write() function returned by start_response()
3671+ * function has not been called with non zero length
3672+ * data. In other words if no prior response content
3673+ * generated. Technically it could be done, but want
3674+ * to have a consistent rule about how specifying a
3675+ * content length affects how much of a file is
3676+ * sent. Don't want to have to take into
3677+ * consideration whether write() function has been
3678+ * called or not as just complicates things.
3679+ */
3680+
3681+ if (self->output_length != 0)
3682+ return 0;
3683+
3684+ /*
3685+ * Work out if file wrapper is associated with a
3686+ * file like object, where that file object is
3687+ * associated with a regular file. If it does then
3688+ * we can optimise how the contents of the file are
3689+ * sent out. If no such associated file descriptor
3690+ * then it needs to be processed like any other
3691+ * iterable value.
3692+ */
3693+
3694+ filelike = ((StreamObject *)self->sequence)->filelike;
3695+
3696+ fd = PyObject_AsFileDescriptor(filelike);
3697+ if (fd == -1) {
3698+ PyErr_Clear();
3699+ return 0;
3700+ }
3701+
3702+ /*
3703+ * On some platforms, such as Linux, sendfile() system call
3704+ * will not work on UNIX sockets. Thus when using daemon mode
3705+ * cannot enable that feature.
3706+ */
3707+
3708+ if (!wsgi_daemon_pool)
3709+ apr_os_file_put(&tmpfile, &fd, APR_SENDFILE_ENABLED, self->r->pool);
3710+ else
3711+ apr_os_file_put(&tmpfile, &fd, 0, self->r->pool);
3712+
3713+ rv = apr_file_info_get(&finfo, APR_FINFO_SIZE|APR_FINFO_TYPE, tmpfile);
3714+ if (rv != APR_SUCCESS || finfo.filetype != APR_REG)
3715+ return 0;
3716+
3717+ /*
3718+ * Because Python file like objects potentially have
3719+ * their own buffering layering, or use an operating
3720+ * system FILE object which also has a buffering
3721+ * layer on top of a normal file descriptor, need to
3722+ * determine from the file like object its position
3723+ * within the file and use that as starting position.
3724+ * Note that it is assumed that user had flushed any
3725+ * modifications to the file as necessary. Also, we
3726+ * need to make sure we remember the original file
3727+ * descriptor position as will need to restore that
3728+ * position so it matches the upper buffering layers
3729+ * when done. This is done to avoid any potential
3730+ * problems if file like object does anything strange
3731+ * in its close() method which relies on file position
3732+ * being what it thought it should be.
3733+ */
3734+
3735+ rv = apr_file_seek(tmpfile, APR_CUR, &fd_offset);
3736+ if (rv != APR_SUCCESS)
3737+ return 0;
3738+
3739+ method = PyObject_GetAttrString(filelike, "tell");
3740+ if (!method)
3741+ return 0;
3742+
3743+ object = PyEval_CallObject(method, NULL);
3744+ Py_DECREF(method);
3745+
3746+ if (!object) {
3747+ PyErr_Clear();
3748+ return 0;
3749+ }
3750+
3751+ if (PyLong_Check(object)) {
3752+#if defined(HAVE_LONG_LONG)
3753+ fo_offset = PyLong_AsLongLong(object);
3754+#else
3755+ fo_offset = PyLong_AsLong(object);
3756+#endif
3757+ }
3758+#if PY_MAJOR_VERSION < 3
3759+ else if (PyInt_Check(object)) {
3760+ fo_offset = PyInt_AsLong(object);
3761+ }
3762+#endif
3763+ else {
3764+ Py_DECREF(object);
3765+ return 0;
3766+ }
3767+
3768+ if (PyErr_Occurred()){
3769+ Py_DECREF(object);
3770+ PyErr_Clear();
3771+ return 0;
3772+ }
3773+
3774+ Py_DECREF(object);
3775+
3776+ /*
3777+ * For a file wrapper object need to always ensure
3778+ * that response headers are parsed. This is done so
3779+ * that if the content length header has been
3780+ * defined we can get its value and use it to limit
3781+ * how much of a file is being sent. The WSGI 1.0
3782+ * specification says that we are meant to send all
3783+ * available bytes from the file, however this is
3784+ * questionable as sending more than content length
3785+ * would violate HTTP RFC. Note that this doesn't
3786+ * actually flush the headers out when using Apache
3787+ * 2.X. This is good, as we want to still be able to
3788+ * set the content length header if none set and file
3789+ * is seekable. If processing response headers fails,
3790+ * then need to return as if done, with error being
3791+ * logged later.
3792+ */
3793+
3794+ if (!Adapter_output(self, "", 0, 0))
3795+ return 1;
3796+
3797+ /*
3798+ * If content length wasn't defined then determine
3799+ * the amount of data which is available to send and
3800+ * set the content length response header. Either
3801+ * way, if can work out length then send data
3802+ * otherwise fall through and treat it as normal
3803+ * iterable.
3804+ */
3805+
3806+ if (!self->content_length_set) {
3807+ length = finfo.size - fo_offset;
3808+ self->output_length += length;
3809+
3810+ ap_set_content_length(self->r, length);
3811+
3812+ self->content_length_set = 1;
3813+ self->content_length = length;
3814+
3815+ if (Adapter_output_file(self, tmpfile, fo_offset, length))
3816+ self->result = OK;
3817+
3818+ done = 1;
3819+ }
3820+ else {
3821+ length = finfo.size - fo_offset;
3822+ self->output_length += length;
3823+
3824+ /* Use user specified content length instead. */
3825+
3826+ length = self->content_length;
3827+
3828+ if (Adapter_output_file(self, tmpfile, fo_offset, length))
3829+ self->result = OK;
3830+
3831+ done = 1;
3832+ }
3833+
3834+ /*
3835+ * Restore position of underlying file descriptor.
3836+ * If this fails, then not much we can do about it.
3837+ */
3838+
3839+ apr_file_seek(tmpfile, APR_SET, &fd_offset);
3840+
3841+#endif
3842+#endif
3843+
3844+ return done;
3845+}
3846+
3847+static int Adapter_run(AdapterObject *self, PyObject *object)
3848+{
3849+ PyObject *vars = NULL;
3850+ PyObject *start = NULL;
3851+ PyObject *args = NULL;
3852+ PyObject *iterator = NULL;
3853+ PyObject *close = NULL;
3854+
3855+ const char *msg = NULL;
3856+ int length = 0;
3857+
3858+#if defined(MOD_WSGI_WITH_DAEMONS)
3859+ if (wsgi_inactivity_timeout) {
3860+ apr_thread_mutex_lock(wsgi_shutdown_lock);
3861+ wsgi_inactivity_shutdown_time = apr_time_now();
3862+ wsgi_inactivity_shutdown_time += wsgi_inactivity_timeout;
3863+ apr_thread_mutex_unlock(wsgi_shutdown_lock);
3864+ }
3865+#endif
3866+
3867+ vars = Adapter_environ(self);
3868+
3869+ start = PyObject_GetAttrString((PyObject *)self, "start_response");
3870+
3871+ args = Py_BuildValue("(OO)", vars, start);
3872+
3873+ self->sequence = PyEval_CallObject(object, args);
3874+
3875+ if (self->sequence != NULL) {
3876+ if (!Adapter_process_file_wrapper(self)) {
3877+ int aborted = 0;
3878+
3879+ iterator = PyObject_GetIter(self->sequence);
3880+
3881+ if (iterator != NULL) {
3882+ PyObject *item = NULL;
3883+
3884+ while ((item = PyIter_Next(iterator))) {
3885+#if PY_MAJOR_VERSION >= 3
3886+ if (PyUnicode_Check(item)) {
3887+ PyObject *latin_item;
3888+ latin_item = PyUnicode_AsLatin1String(item);
3889+ if (!latin_item) {
3890+ PyErr_Format(PyExc_TypeError, "sequence of "
3891+ "byte string values expected, value "
3892+ "containing non 'latin-1' characters "
3893+ "found");
3894+ Py_DECREF(item);
3895+ break;
3896+ }
3897+
3898+ Py_DECREF(item);
3899+ item = latin_item;
3900+ }
3901+#endif
3902+
3903+ if (!PyString_Check(item)) {
3904+ PyErr_Format(PyExc_TypeError, "sequence of byte "
3905+ "string values expected, value of "
3906+ "type %.200s found",
3907+ item->ob_type->tp_name);
3908+ Py_DECREF(item);
3909+ break;
3910+ }
3911+
3912+ msg = PyString_AsString(item);
3913+ length = PyString_Size(item);
3914+
3915+ if (!msg) {
3916+ Py_DECREF(item);
3917+ break;
3918+ }
3919+
3920+ if (length && !Adapter_output(self, msg, length, 0)) {
3921+ if (!PyErr_Occurred())
3922+ aborted = 1;
3923+ Py_DECREF(item);
3924+ break;
3925+ }
3926+
3927+ Py_DECREF(item);
3928+ }
3929+ }
3930+
3931+ if (!PyErr_Occurred() && !aborted) {
3932+ if (Adapter_output(self, "", 0, 0))
3933+ self->result = OK;
3934+ }
3935+
3936+ Py_XDECREF(iterator);
3937+ }
3938+
3939+ if (PyErr_Occurred()) {
3940+ /*
3941+ * Response content has already been sent, so cannot
3942+ * return an internal server error as Apache will
3943+ * append its own error page. Thus need to return OK
3944+ * and just truncate the response.
3945+ */
3946+
3947+ if (self->status_line && !self->headers)
3948+ self->result = OK;
3949+
3950+ wsgi_log_python_error(self->r, self->log, self->r->filename);
3951+ }
3952+
3953+ if (PyObject_HasAttrString(self->sequence, "close")) {
3954+ PyObject *args = NULL;
3955+ PyObject *data = NULL;
3956+
3957+ close = PyObject_GetAttrString(self->sequence, "close");
3958+
3959+ args = Py_BuildValue("()");
3960+ data = PyEval_CallObject(close, args);
3961+
3962+ Py_DECREF(args);
3963+ Py_XDECREF(data);
3964+ Py_DECREF(close);
3965+ }
3966+
3967+ if (PyErr_Occurred())
3968+ wsgi_log_python_error(self->r, self->log, self->r->filename);
3969+
3970+ Py_DECREF(self->sequence);
3971+
3972+ self->sequence = NULL;
3973+ }
3974+
3975+ Py_DECREF(args);
3976+ Py_DECREF(start);
3977+ Py_DECREF(vars);
3978+
3979+ /*
3980+ * Log warning if more response content generated than was
3981+ * indicated, or less if there was no errors generated by
3982+ * the application.
3983+ */
3984+
3985+ if (self->content_length_set && ((!PyErr_Occurred() &&
3986+ self->output_length != self->content_length) ||
3987+ (self->output_length > self->content_length))) {
3988+ ap_log_rerror(APLOG_MARK, WSGI_LOG_DEBUG(0), self->r,
3989+ "mod_wsgi (pid=%d): Content length mismatch, "
3990+ "expected %s, response generated %s: %s", getpid(),
3991+ apr_off_t_toa(self->r->pool, self->content_length),
3992+ apr_off_t_toa(self->r->pool, self->output_length),
3993+ self->r->filename);
3994+ }
3995+
3996+ /* Log details of any final Python exceptions. */
3997+
3998+ if (PyErr_Occurred())
3999+ wsgi_log_python_error(self->r, self->log, self->r->filename);
4000+
4001+ /*
4002+ * If result indicates an internal server error, then
4003+ * replace the status line in the request object else
4004+ * that provided by the application will be what is used
4005+ * in any error page automatically generated by Apache.
4006+ */
4007+
4008+ if (self->result == HTTP_INTERNAL_SERVER_ERROR)
4009+ self->r->status_line = "500 Internal Server Error";
4010+
4011+ return self->result;
4012+}
4013+
4014+static PyObject *Adapter_write(AdapterObject *self, PyObject *args)
4015+{
4016+ PyObject *item = NULL;
4017+ const char *data = NULL;
4018+ int length = 0;
4019+
4020+ if (!self->r) {
4021+ PyErr_SetString(PyExc_RuntimeError, "request object has expired");
4022+ return NULL;
4023+ }
4024+
4025+ if (!PyArg_ParseTuple(args, "O:write", &item))
4026+ return NULL;
4027+
4028+#if PY_MAJOR_VERSION >= 3
4029+ if (PyUnicode_Check(item)) {
4030+ PyObject *latin_item;
4031+ latin_item = PyUnicode_AsLatin1String(item);
4032+ if (!latin_item) {
4033+ PyErr_Format(PyExc_TypeError, "byte string value expected, "
4034+ "value containing non 'latin-1' characters found");
4035+ Py_DECREF(item);
4036+ return NULL;
4037+ }
4038+
4039+ Py_DECREF(item);
4040+ item = latin_item;
4041+ }
4042+#endif
4043+
4044+ if (!PyString_Check(item)) {
4045+ PyErr_Format(PyExc_TypeError, "byte string value expected, value "
4046+ "of type %.200s found", item->ob_type->tp_name);
4047+ Py_DECREF(item);
4048+ return NULL;
4049+ }
4050+
4051+ data = PyString_AsString(item);
4052+ length = PyString_Size(item);
4053+
4054+ if (!Adapter_output(self, data, length, 1))
4055+ return NULL;
4056+
4057+ Py_INCREF(Py_None);
4058+ return Py_None;
4059+}
4060+
4061+static PyObject *newStreamObject(AdapterObject *adapter, PyObject *filelike,
4062+ apr_size_t blksize);
4063+
4064+static PyObject *Adapter_file_wrapper(AdapterObject *self, PyObject *args)
4065+{
4066+ PyObject *filelike = NULL;
4067+ apr_size_t blksize = HUGE_STRING_LEN;
4068+ PyObject *result = NULL;
4069+
4070+ if (!self->r) {
4071+ PyErr_SetString(PyExc_RuntimeError, "request object has expired");
4072+ return NULL;
4073+ }
4074+
4075+ if (!PyArg_ParseTuple(args, "O|l:file_wrapper", &filelike, &blksize))
4076+ return NULL;
4077+
4078+ return newStreamObject(self, filelike, blksize);
4079+}
4080+
4081+static PyMethodDef Adapter_methods[] = {
4082+ { "start_response", (PyCFunction)Adapter_start_response, METH_VARARGS, 0 },
4083+ { "write", (PyCFunction)Adapter_write, METH_VARARGS, 0 },
4084+ { "file_wrapper", (PyCFunction)Adapter_file_wrapper, METH_VARARGS, 0 },
4085+ { NULL, NULL}
4086+};
4087+
4088+static PyTypeObject Adapter_Type = {
4089+ PyVarObject_HEAD_INIT(NULL, 0)
4090+ "mod_wsgi.Adapter", /*tp_name*/
4091+ sizeof(AdapterObject), /*tp_basicsize*/
4092+ 0, /*tp_itemsize*/
4093+ /* methods */
4094+ (destructor)Adapter_dealloc, /*tp_dealloc*/
4095+ 0, /*tp_print*/
4096+ 0, /*tp_getattr*/
4097+ 0, /*tp_setattr*/
4098+ 0, /*tp_compare*/
4099+ 0, /*tp_repr*/
4100+ 0, /*tp_as_number*/
4101+ 0, /*tp_as_sequence*/
4102+ 0, /*tp_as_mapping*/
4103+ 0, /*tp_hash*/
4104+ 0, /*tp_call*/
4105+ 0, /*tp_str*/
4106+ 0, /*tp_getattro*/
4107+ 0, /*tp_setattro*/
4108+ 0, /*tp_as_buffer*/
4109+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
4110+ 0, /*tp_doc*/
4111+ 0, /*tp_traverse*/
4112+ 0, /*tp_clear*/
4113+ 0, /*tp_richcompare*/
4114+ 0, /*tp_weaklistoffset*/
4115+ 0, /*tp_iter*/
4116+ 0, /*tp_iternext*/
4117+ Adapter_methods, /*tp_methods*/
4118+ 0, /*tp_members*/
4119+ 0, /*tp_getset*/
4120+ 0, /*tp_base*/
4121+ 0, /*tp_dict*/
4122+ 0, /*tp_descr_get*/
4123+ 0, /*tp_descr_set*/
4124+ 0, /*tp_dictoffset*/
4125+ 0, /*tp_init*/
4126+ 0, /*tp_alloc*/
4127+ 0, /*tp_new*/
4128+ 0, /*tp_free*/
4129+ 0, /*tp_is_gc*/
4130+};
4131+
4132+static PyObject *newStreamObject(AdapterObject *adapter, PyObject *filelike,
4133+ apr_size_t blksize)
4134+{
4135+ StreamObject *self;
4136+
4137+ self = PyObject_New(StreamObject, &Stream_Type);
4138+ if (self == NULL)
4139+ return NULL;
4140+
4141+ self->adapter = adapter;
4142+ self->filelike = filelike;
4143+ self->blksize = blksize;
4144+
4145+ Py_INCREF(self->adapter);
4146+ Py_INCREF(self->filelike);
4147+
4148+ return (PyObject *)self;
4149+}
4150+
4151+static void Stream_dealloc(StreamObject *self)
4152+{
4153+ Py_DECREF(self->filelike);
4154+ Py_DECREF(self->adapter);
4155+
4156+ PyObject_Del(self);
4157+}
4158+
4159+static PyObject *Stream_iter(StreamObject *self)
4160+{
4161+ if (!self->adapter->r) {
4162+ PyErr_SetString(PyExc_RuntimeError, "request object has expired");
4163+ return NULL;
4164+ }
4165+
4166+ Py_INCREF(self);
4167+ return (PyObject *)self;
4168+}
4169+
4170+static PyObject *Stream_iternext(StreamObject *self)
4171+{
4172+ PyObject *method = NULL;
4173+ PyObject *args = NULL;
4174+ PyObject *result = NULL;
4175+
4176+ if (!self->adapter->r) {
4177+ PyErr_SetString(PyExc_RuntimeError, "request object has expired");
4178+ return NULL;
4179+ }
4180+
4181+ method = PyObject_GetAttrString(self->filelike, "read");
4182+
4183+ if (!method) {
4184+ PyErr_SetString(PyExc_KeyError,
4185+ "file like object has no read() method");
4186+ return 0;
4187+ }
4188+
4189+ args = Py_BuildValue("(l)", self->blksize);
4190+ result = PyEval_CallObject(method, args);
4191+
4192+ Py_DECREF(method);
4193+ Py_DECREF(args);
4194+
4195+ if (!result)
4196+ return 0;
4197+
4198+ if (PyString_Check(result)) {
4199+ if (PyString_Size(result) == 0) {
4200+ PyErr_SetObject(PyExc_StopIteration, Py_None);
4201+ Py_DECREF(result);
4202+ return 0;
4203+ }
4204+
4205+ return result;
4206+ }
4207+
4208+#if PY_MAJOR_VERSION >= 3
4209+ if (PyUnicode_Check(result)) {
4210+ if (PyUnicode_GetSize(result) == 0) {
4211+ PyErr_SetObject(PyExc_StopIteration, Py_None);
4212+ Py_DECREF(result);
4213+ return 0;
4214+ }
4215+
4216+ return result;
4217+ }
4218+#endif
4219+
4220+ Py_DECREF(result);
4221+
4222+ PyErr_SetString(PyExc_TypeError,
4223+ "file like object yielded non string type");
4224+
4225+ return 0;
4226+}
4227+
4228+static PyObject *Stream_close(StreamObject *self, PyObject *args)
4229+{
4230+ PyObject *method = NULL;
4231+ PyObject *result = NULL;
4232+
4233+ method = PyObject_GetAttrString(self->filelike, "close");
4234+
4235+ if (method) {
4236+ result = PyEval_CallObject(method, (PyObject *)NULL);
4237+ if (!result)
4238+ PyErr_Clear();
4239+ Py_DECREF(method);
4240+ }
4241+
4242+ Py_XDECREF(result);
4243+
4244+ Py_INCREF(Py_None);
4245+ return Py_None;
4246+}
4247+
4248+static PyMethodDef Stream_methods[] = {
4249+ { "close", (PyCFunction)Stream_close, METH_VARARGS, 0 },
4250+ { NULL, NULL}
4251+};
4252+
4253+static PyTypeObject Stream_Type = {
4254+ PyVarObject_HEAD_INIT(NULL, 0)
4255+ "mod_wsgi.Stream", /*tp_name*/
4256+ sizeof(StreamObject), /*tp_basicsize*/
4257+ 0, /*tp_itemsize*/
4258+ /* methods */
4259+ (destructor)Stream_dealloc, /*tp_dealloc*/
4260+ 0, /*tp_print*/
4261+ 0, /*tp_getattr*/
4262+ 0, /*tp_setattr*/
4263+ 0, /*tp_compare*/
4264+ 0, /*tp_repr*/
4265+ 0, /*tp_as_number*/
4266+ 0, /*tp_as_sequence*/
4267+ 0, /*tp_as_mapping*/
4268+ 0, /*tp_hash*/
4269+ 0, /*tp_call*/
4270+ 0, /*tp_str*/
4271+ 0, /*tp_getattro*/
4272+ 0, /*tp_setattro*/
4273+ 0, /*tp_as_buffer*/
4274+#if defined(Py_TPFLAGS_HAVE_ITER)
4275+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, /*tp_flags*/
4276+#else
4277+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
4278+#endif
4279+ 0, /*tp_doc*/
4280+ 0, /*tp_traverse*/
4281+ 0, /*tp_clear*/
4282+ 0, /*tp_richcompare*/
4283+ 0, /*tp_weaklistoffset*/
4284+ (getiterfunc)Stream_iter, /*tp_iter*/
4285+ (iternextfunc)Stream_iternext, /*tp_iternext*/
4286+ Stream_methods, /*tp_methods*/
4287+ 0, /*tp_members*/
4288+ 0, /*tp_getset*/
4289+ 0, /*tp_base*/
4290+ 0, /*tp_dict*/
4291+ 0, /*tp_descr_get*/
4292+ 0, /*tp_descr_set*/
4293+ 0, /*tp_dictoffset*/
4294+ 0, /*tp_init*/
4295+ 0, /*tp_alloc*/
4296+ 0, /*tp_new*/
4297+ 0, /*tp_free*/
4298+ 0, /*tp_is_gc*/
4299+};
4300+
4301+/* Restricted object to stop access to STDIN/STDOUT. */
4302+
4303+typedef struct {
4304+ PyObject_HEAD
4305+ const char *s;
4306+} RestrictedObject;
4307+
4308+static PyTypeObject Restricted_Type;
4309+
4310+static RestrictedObject *newRestrictedObject(const char *s)
4311+{
4312+ RestrictedObject *self;
4313+
4314+ self = PyObject_New(RestrictedObject, &Restricted_Type);
4315+ if (self == NULL)
4316+ return NULL;
4317+
4318+ self->s = s;
4319+
4320+ return self;
4321+}
4322+
4323+static void Restricted_dealloc(RestrictedObject *self)
4324+{
4325+ PyObject_Del(self);
4326+}
4327+
4328+static PyObject *Restricted_getattr(RestrictedObject *self, char *name)
4329+{
4330+ PyErr_Format(PyExc_IOError, "%s access restricted by mod_wsgi", self->s);
4331+
4332+ return NULL;
4333+}
4334+
4335+static PyTypeObject Restricted_Type = {
4336+ PyVarObject_HEAD_INIT(NULL, 0)
4337+ "mod_wsgi.Restricted", /*tp_name*/
4338+ sizeof(RestrictedObject), /*tp_basicsize*/
4339+ 0, /*tp_itemsize*/
4340+ /* methods */
4341+ (destructor)Restricted_dealloc, /*tp_dealloc*/
4342+ 0, /*tp_print*/
4343+ (getattrfunc)Restricted_getattr, /*tp_getattr*/
4344+ 0, /*tp_setattr*/
4345+ 0, /*tp_compare*/
4346+ 0, /*tp_repr*/
4347+ 0, /*tp_as_number*/
4348+ 0, /*tp_as_sequence*/
4349+ 0, /*tp_as_mapping*/
4350+ 0, /*tp_hash*/
4351+ 0, /*tp_call*/
4352+ 0, /*tp_str*/
4353+ 0, /*tp_getattro*/
4354+ 0, /*tp_setattro*/
4355+ 0, /*tp_as_buffer*/
4356+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
4357+ 0, /*tp_doc*/
4358+ 0, /*tp_traverse*/
4359+ 0, /*tp_clear*/
4360+ 0, /*tp_richcompare*/
4361+ 0, /*tp_weaklistoffset*/
4362+ 0, /*tp_iter*/
4363+ 0, /*tp_iternext*/
4364+ 0, /*tp_methods*/
4365+ 0, /*tp_members*/
4366+ 0, /*tp_getset*/
4367+ 0, /*tp_base*/
4368+ 0, /*tp_dict*/
4369+ 0, /*tp_descr_get*/
4370+ 0, /*tp_descr_set*/
4371+ 0, /*tp_dictoffset*/
4372+ 0, /*tp_init*/
4373+ 0, /*tp_alloc*/
4374+ 0, /*tp_new*/
4375+ 0, /*tp_free*/
4376+ 0, /*tp_is_gc*/
4377+};
4378+
4379+/* Function to restrict access to use of signal(). */
4380+
4381+static PyObject *wsgi_signal_intercept(PyObject *self, PyObject *args)
4382+{
4383+ PyObject *h = NULL;
4384+ int n = 0;
4385+
4386+ PyObject *m = NULL;
4387+
4388+ if (!PyArg_ParseTuple(args, "iO:signal", &n, &h))
4389+ return NULL;
4390+
4391+ Py_BEGIN_ALLOW_THREADS
4392+ ap_log_error(APLOG_MARK, WSGI_LOG_WARNING(0), wsgi_server,
4393+ "mod_wsgi (pid=%d): Callback registration for "
4394+ "signal %d ignored.", getpid(), n);
4395+ Py_END_ALLOW_THREADS
4396+
4397+ m = PyImport_ImportModule("traceback");
4398+
4399+ if (m) {
4400+ PyObject *d = NULL;
4401+ PyObject *o = NULL;
4402+ d = PyModule_GetDict(m);
4403+ o = PyDict_GetItemString(d, "print_stack");
4404+ if (o) {
4405+ PyObject *log = NULL;
4406+ PyObject *args = NULL;
4407+ PyObject *result = NULL;
4408+ Py_INCREF(o);
4409+ log = newLogObject(NULL, APLOG_WARNING, NULL);
4410+ args = Py_BuildValue("(OOO)", Py_None, Py_None, log);
4411+ result = PyEval_CallObject(o, args);
4412+ Py_XDECREF(result);
4413+ Py_DECREF(args);
4414+ Py_DECREF(log);
4415+ Py_DECREF(o);
4416+ }
4417+ }
4418+
4419+ Py_XDECREF(m);
4420+
4421+ Py_INCREF(h);
4422+
4423+ return h;
4424+}
4425+
4426+static PyMethodDef wsgi_signal_method[] = {
4427+ { "signal", (PyCFunction)wsgi_signal_intercept, METH_VARARGS, 0 },
4428+ { NULL, NULL }
4429+};
4430+
4431+/* Wrapper around Python interpreter instances. */
4432+
4433+static const char *wsgi_python_path = NULL;
4434+static const char *wsgi_python_eggs = NULL;
4435+
4436+#if APR_HAS_THREADS
4437+static int wsgi_thread_count = 0;
4438+static apr_threadkey_t *wsgi_thread_key;
4439+#endif
4440+
4441+typedef struct {
4442+ PyObject_HEAD
4443+ char *name;
4444+ PyInterpreterState *interp;
4445+ int owner;
4446+#if APR_HAS_THREADS
4447+ apr_hash_t *tstate_table;
4448+#else
4449+ PyThreadState *tstate;
4450+#endif
4451+} InterpreterObject;
4452+
4453+static PyTypeObject Interpreter_Type;
4454+
4455+static InterpreterObject *newInterpreterObject(const char *name)
4456+{
4457+ PyInterpreterState *interp = NULL;
4458+ InterpreterObject *self = NULL;
4459+ PyThreadState *tstate = NULL;
4460+ PyThreadState *save_tstate = NULL;
4461+ PyObject *module = NULL;
4462+ PyObject *object = NULL;
4463+ PyObject *item = NULL;
4464+
4465+ /* Create handle for interpreter and local data. */
4466+
4467+ self = PyObject_New(InterpreterObject, &Interpreter_Type);
4468+ if (self == NULL)
4469+ return NULL;
4470+
4471+ /*
4472+ * If interpreter not named, then we want to bind
4473+ * to the first Python interpreter instance created.
4474+ * Give this interpreter an empty string as name.
4475+ */
4476+
4477+ if (!name) {
4478+ interp = PyInterpreterState_Head();
4479+ while (interp->next)
4480+ interp = interp->next;
4481+
4482+ name = "";
4483+ }
4484+
4485+ /* Save away the interpreter name. */
4486+
4487+ self->name = strdup(name);
4488+
4489+ if (interp) {
4490+ /*
4491+ * Interpreter provided to us so will not be
4492+ * responsible for deleting it later. This will
4493+ * be the case for the main Python interpreter.
4494+ */
4495+
4496+ ap_log_error(APLOG_MARK, WSGI_LOG_INFO(0), wsgi_server,
4497+ "mod_wsgi (pid=%d): Attach interpreter '%s'.",
4498+ getpid(), name);
4499+
4500+ self->interp = interp;
4501+ self->owner = 0;
4502+ }
4503+ else {
4504+ /*
4505+ * Remember active thread state so can restore
4506+ * it. This is actually the thread state
4507+ * associated with simplified GIL state API.
4508+ */
4509+
4510+ save_tstate = PyThreadState_Swap(NULL);
4511+
4512+ /*
4513+ * Create the interpreter. If creation of the
4514+ * interpreter fails it will restore the
4515+ * existing active thread state for us so don't
4516+ * need to worry about it in that case.
4517+ */
4518+
4519+ tstate = Py_NewInterpreter();
4520+
4521+ if (!tstate) {
4522+ PyErr_SetString(PyExc_RuntimeError, "Py_NewInterpreter() failed");
4523+
4524+ Py_DECREF(self);
4525+
4526+ return NULL;
4527+ }
4528+
4529+ Py_BEGIN_ALLOW_THREADS
4530+ ap_log_error(APLOG_MARK, WSGI_LOG_INFO(0), wsgi_server,
4531+ "mod_wsgi (pid=%d): Create interpreter '%s'.",
4532+ getpid(), name);
4533+ Py_END_ALLOW_THREADS
4534+
4535+ self->interp = tstate->interp;
4536+ self->owner = 1;
4537+ }
4538+
4539+ /*
4540+ * Install restricted objects for STDIN and STDOUT,
4541+ * or log object for STDOUT as appropriate. Don't do
4542+ * this if not running on Win32 and we believe we
4543+ * are running in single process mode, otherwise
4544+ * it prevents use of interactive debuggers such as
4545+ * the 'pdb' module.
4546+ */
4547+
4548+ object = newLogObject(NULL, APLOG_ERR, "stderr");
4549+ PySys_SetObject("stderr", object);
4550+ Py_DECREF(object);
4551+
4552+#ifndef WIN32
4553+ if (wsgi_parent_pid != getpid()) {
4554+#endif
4555+ if (wsgi_server_config->restrict_stdout == 1) {
4556+ object = (PyObject *)newRestrictedObject("sys.stdout");
4557+ PySys_SetObject("stdout", object);
4558+ Py_DECREF(object);
4559+ }
4560+ else {
4561+ object = newLogObject(NULL, APLOG_ERR, "stdout");
4562+ PySys_SetObject("stdout", object);
4563+ Py_DECREF(object);
4564+ }
4565+
4566+ if (wsgi_server_config->restrict_stdin == 1) {
4567+ object = (PyObject *)newRestrictedObject("sys.stdin");
4568+ PySys_SetObject("stdin", object);
4569+ Py_DECREF(object);
4570+ }
4571+#ifndef WIN32
4572+ }
4573+#endif
4574+
4575+ /*
4576+ * Set sys.argv to one element list to fake out
4577+ * modules that look there for Python command
4578+ * line arguments as appropriate.
4579+ */
4580+
4581+ object = PyList_New(0);
4582+#if PY_MAJOR_VERSION >= 3
4583+ item = PyUnicode_FromString("mod_wsgi");
4584+#else
4585+ item = PyString_FromString("mod_wsgi");
4586+#endif
4587+ PyList_Append(object, item);
4588+ PySys_SetObject("argv", object);
4589+ Py_DECREF(item);
4590+ Py_DECREF(object);
4591+
4592+ /*
4593+ * Install intercept for signal handler registration
4594+ * if appropriate.
4595+ */
4596+
4597+ if (wsgi_server_config->restrict_signal != 0) {
4598+ module = PyImport_ImportModule("signal");
4599+ PyModule_AddObject(module, "signal", PyCFunction_New(
4600+ &wsgi_signal_method[0], NULL));
4601+ Py_DECREF(module);
4602+ }
4603+
4604+ /*
4605+ * If running in daemon process, override as appropriate
4606+ * the USER, USERNAME or LOGNAME environment variables
4607+ * so that they match the user that the process is running
4608+ * as. Need to do this else we inherit the value from the
4609+ * Apache parent process which is likely wrong as will be
4610+ * root or the user than ran sudo when Apache started.
4611+ * Can't update these for normal Apache child processes
4612+ * as that would change the expected environment of other
4613+ * Apache modules.
4614+ */
4615+
4616+#ifndef WIN32
4617+ if (wsgi_daemon_pool) {
4618+ module = PyImport_ImportModule("os");
4619+
4620+ if (module) {
4621+ PyObject *dict = NULL;
4622+ PyObject *key = NULL;
4623+ PyObject *value = NULL;
4624+
4625+ dict = PyModule_GetDict(module);
4626+ object = PyDict_GetItemString(dict, "environ");
4627+
4628+ if (object) {
4629+ struct passwd *pwent;
4630+
4631+ pwent = getpwuid(geteuid());
4632+
4633+ if (getenv("USER")) {
4634+#if PY_MAJOR_VERSION >= 3
4635+ key = PyUnicode_FromString("USER");
4636+ value = PyUnicode_Decode(pwent->pw_name,
4637+ strlen(pwent->pw_name),
4638+ Py_FileSystemDefaultEncoding,
4639+ "surrogateescape");
4640+#else
4641+ key = PyString_FromString("USER");
4642+ value = PyString_FromString(pwent->pw_name);
4643+#endif
4644+
4645+ PyObject_SetItem(object, key, value);
4646+
4647+ Py_DECREF(key);
4648+ Py_DECREF(value);
4649+ }
4650+
4651+ if (getenv("USERNAME")) {
4652+#if PY_MAJOR_VERSION >= 3
4653+ key = PyUnicode_FromString("USERNAME");
4654+ value = PyUnicode_Decode(pwent->pw_name,
4655+ strlen(pwent->pw_name),
4656+ Py_FileSystemDefaultEncoding,
4657+ "surrogateescape");
4658+#else
4659+ key = PyString_FromString("USERNAME");
4660+ value = PyString_FromString(pwent->pw_name);
4661+#endif
4662+
4663+ PyObject_SetItem(object, key, value);
4664+
4665+ Py_DECREF(key);
4666+ Py_DECREF(value);
4667+ }
4668+
4669+ if (getenv("LOGNAME")) {
4670+#if PY_MAJOR_VERSION >= 3
4671+ key = PyUnicode_FromString("LOGNAME");
4672+ value = PyUnicode_Decode(pwent->pw_name,
4673+ strlen(pwent->pw_name),
4674+ Py_FileSystemDefaultEncoding,
4675+ "surrogateescape");
4676+#else
4677+ key = PyString_FromString("LOGNAME");
4678+ value = PyString_FromString(pwent->pw_name);
4679+#endif
4680+
4681+ PyObject_SetItem(object, key, value);
4682+
4683+ Py_DECREF(key);
4684+ Py_DECREF(value);
4685+ }
4686+ }
4687+
4688+ Py_DECREF(module);
4689+ }
4690+ }
4691+#endif
4692+
4693+ /*
4694+ * If running in daemon process, override HOME environment
4695+ * variable so that is matches the home directory of the
4696+ * user that the process is running as. Need to do this as
4697+ * Apache will inherit HOME from root user or user that ran
4698+ * sudo and started Apache and this would be wrong. Can't
4699+ * update HOME for normal Apache child processes as that
4700+ * would change the expected environment of other Apache
4701+ * modules.
4702+ */
4703+
4704+#ifndef WIN32
4705+ if (wsgi_daemon_pool) {
4706+ module = PyImport_ImportModule("os");
4707+
4708+ if (module) {
4709+ PyObject *dict = NULL;
4710+ PyObject *key = NULL;
4711+ PyObject *value = NULL;
4712+
4713+ dict = PyModule_GetDict(module);
4714+ object = PyDict_GetItemString(dict, "environ");
4715+
4716+ if (object) {
4717+ struct passwd *pwent;
4718+
4719+ pwent = getpwuid(geteuid());
4720+#if PY_MAJOR_VERSION >= 3
4721+ key = PyUnicode_FromString("HOME");
4722+ value = PyUnicode_Decode(pwent->pw_dir, strlen(pwent->pw_dir),
4723+ Py_FileSystemDefaultEncoding,
4724+ "surrogateescape");
4725+#else
4726+ key = PyString_FromString("HOME");
4727+ value = PyString_FromString(pwent->pw_dir);
4728+#endif
4729+
4730+ PyObject_SetItem(object, key, value);
4731+
4732+ Py_DECREF(key);
4733+ Py_DECREF(value);
4734+ }
4735+
4736+ Py_DECREF(module);
4737+ }
4738+ }
4739+#endif
4740+
4741+ /*
4742+ * Explicitly override the PYTHON_EGG_CACHE variable if it
4743+ * was defined by Apache configuration. For embedded processes
4744+ * this would have been done by using WSGIPythonEggs directive.
4745+ * For daemon processes the 'python-eggs' option to the
4746+ * WSGIDaemonProcess directive would have needed to be used.
4747+ */
4748+
4749+ if (!wsgi_daemon_pool)
4750+ wsgi_python_eggs = wsgi_server_config->python_eggs;
4751+
4752+ if (wsgi_python_eggs) {
4753+ module = PyImport_ImportModule("os");
4754+
4755+ if (module) {
4756+ PyObject *dict = NULL;
4757+ PyObject *key = NULL;
4758+ PyObject *value = NULL;
4759+
4760+ dict = PyModule_GetDict(module);
4761+ object = PyDict_GetItemString(dict, "environ");
4762+
4763+ if (object) {
4764+#if PY_MAJOR_VERSION >= 3
4765+ key = PyUnicode_FromString("PYTHON_EGG_CACHE");
4766+ value = PyUnicode_Decode(wsgi_python_eggs,
4767+ strlen(wsgi_python_eggs),
4768+ Py_FileSystemDefaultEncoding,
4769+ "surrogateescape");
4770+#else
4771+ key = PyString_FromString("PYTHON_EGG_CACHE");
4772+ value = PyString_FromString(wsgi_python_eggs);
4773+#endif
4774+
4775+ PyObject_SetItem(object, key, value);
4776+
4777+ Py_DECREF(key);
4778+ Py_DECREF(value);
4779+ }
4780+
4781+ Py_DECREF(module);
4782+ }
4783+ }
4784+
4785+ /*
4786+ * Install user defined Python module search path. This is
4787+ * added using site.addsitedir() so that any Python .pth
4788+ * files are opened and additional directories so defined
4789+ * are added to default Python search path as well. This
4790+ * allows virtual Python environments to work. Note that
4791+ * site.addsitedir() adds new directories at the end of
4792+ * sys.path when they really need to be added in order at
4793+ * the start. We therefore need to do a fiddle and shift
4794+ * any newly added directories to the start of sys.path.
4795+ */
4796+
4797+ if (!wsgi_daemon_pool)
4798+ wsgi_python_path = wsgi_server_config->python_path;
4799+
4800+ if (wsgi_python_path) {
4801+ PyObject *path = NULL;
4802+
4803+ module = PyImport_ImportModule("site");
4804+ path = PySys_GetObject("path");
4805+
4806+ if (module && path) {
4807+ PyObject *dict = NULL;
4808+
4809+ PyObject *old = NULL;
4810+ PyObject *new = NULL;
4811+ PyObject *tmp = NULL;
4812+
4813+ PyObject *item = NULL;
4814+
4815+ int i = 0;
4816+
4817+ old = PyList_New(0);
4818+ new = PyList_New(0);
4819+ tmp = PyList_New(0);
4820+
4821+ for (i=0; i<PyList_Size(path); i++)
4822+ PyList_Append(old, PyList_GetItem(path, i));
4823+
4824+ dict = PyModule_GetDict(module);
4825+ object = PyDict_GetItemString(dict, "addsitedir");
4826+
4827+ if (object) {
4828+ const char *start;
4829+ const char *end;
4830+ const char *value;
4831+
4832+ PyObject *item;
4833+ PyObject *args;
4834+
4835+ PyObject *result = NULL;
4836+
4837+ Py_INCREF(object);
4838+
4839+ start = wsgi_python_path;
4840+ end = strchr(start, DELIM);
4841+
4842+ if (end) {
4843+#if PY_MAJOR_VERSION >= 3
4844+ item = PyUnicode_Decode(start, end-start,
4845+ Py_FileSystemDefaultEncoding,
4846+ "surrogateescape");
4847+#else
4848+ item = PyString_FromStringAndSize(start, end-start);
4849+#endif
4850+ start = end+1;
4851+
4852+ value = PyString_AsString(item);
4853+
4854+ Py_BEGIN_ALLOW_THREADS
4855+ ap_log_error(APLOG_MARK, WSGI_LOG_INFO(0), wsgi_server,
4856+ "mod_wsgi (pid=%d): Adding '%s' to "
4857+ "path.", getpid(), value);
4858+ Py_END_ALLOW_THREADS
4859+
4860+ args = Py_BuildValue("(O)", item);
4861+ result = PyEval_CallObject(object, args);
4862+
4863+ if (!result) {
4864+ Py_BEGIN_ALLOW_THREADS
4865+ ap_log_error(APLOG_MARK, WSGI_LOG_ERR(0), wsgi_server,
4866+ "mod_wsgi (pid=%d): Call to "
4867+ "'site.addsitedir()' failed for '%s', "
4868+ "stopping.", getpid(), value);
4869+ Py_END_ALLOW_THREADS
4870+ }
4871+
4872+ Py_XDECREF(result);
4873+ Py_DECREF(item);
4874+ Py_DECREF(args);
4875+
4876+ end = strchr(start, DELIM);
4877+
4878+ while (result && end) {
4879+#if PY_MAJOR_VERSION >= 3
4880+ item = PyUnicode_Decode(start, end-start,
4881+ Py_FileSystemDefaultEncoding,
4882+ "surrogateescape");
4883+#else
4884+ item = PyString_FromStringAndSize(start, end-start);
4885+#endif
4886+ start = end+1;
4887+
4888+ value = PyString_AsString(item);
4889+
4890+ Py_BEGIN_ALLOW_THREADS
4891+ ap_log_error(APLOG_MARK, WSGI_LOG_INFO(0), wsgi_server,
4892+ "mod_wsgi (pid=%d): Adding '%s' to "
4893+ "path.", getpid(), value);
4894+ Py_END_ALLOW_THREADS
4895+
4896+ args = Py_BuildValue("(O)", item);
4897+ result = PyEval_CallObject(object, args);
4898+
4899+ if (!result) {
4900+ Py_BEGIN_ALLOW_THREADS
4901+ ap_log_error(APLOG_MARK, WSGI_LOG_ERR(0),
4902+ wsgi_server, "mod_wsgi (pid=%d): "
4903+ "Call to 'site.addsitedir()' failed "
4904+ "for '%s', stopping.",
4905+ getpid(), value);
4906+ Py_END_ALLOW_THREADS
4907+ }
4908+
4909+ Py_XDECREF(result);
4910+ Py_DECREF(item);
4911+ Py_DECREF(args);
4912+
4913+ end = strchr(start, DELIM);
4914+ }
4915+ }
4916+
4917+ Py_BEGIN_ALLOW_THREADS
4918+ ap_log_error(APLOG_MARK, WSGI_LOG_INFO(0), wsgi_server,
4919+ "mod_wsgi (pid=%d): Adding '%s' to "
4920+ "path.", getpid(), start);
4921+ Py_END_ALLOW_THREADS
4922+
4923+ args = Py_BuildValue("(s)", start);
4924+ result = PyEval_CallObject(object, args);
4925+
4926+ if (!result) {
4927+ Py_BEGIN_ALLOW_THREADS
4928+ ap_log_error(APLOG_MARK, WSGI_LOG_ERR(0), wsgi_server,
4929+ "mod_wsgi (pid=%d): Call to "
4930+ "'site.addsitedir()' failed for '%s'.",
4931+ getpid(), start);
4932+ Py_END_ALLOW_THREADS
4933+ }
4934+
4935+ Py_XDECREF(result);
4936+ Py_DECREF(args);
4937+
4938+ Py_DECREF(object);
4939+ }
4940+ else {
4941+ Py_BEGIN_ALLOW_THREADS
4942+ ap_log_error(APLOG_MARK, WSGI_LOG_ERR(0), wsgi_server,
4943+ "mod_wsgi (pid=%d): Unable to locate "
4944+ "'site.addsitedir()'.", getpid());
4945+ Py_END_ALLOW_THREADS
4946+ }
4947+
4948+ for (i=0; i<PyList_Size(path); i++)
4949+ PyList_Append(tmp, PyList_GetItem(path, i));
4950+
4951+ for (i=0; i<PyList_Size(tmp); i++) {
4952+ item = PyList_GetItem(tmp, i);
4953+ if (!PySequence_Contains(old, item)) {
4954+ int index = PySequence_Index(path, item);
4955+ PyList_Append(new, item);
4956+ if (index != -1)
4957+ PySequence_DelItem(path, index);
4958+ }
4959+ }
4960+
4961+ PyList_SetSlice(path, 0, 0, new);
4962+
4963+ Py_DECREF(old);
4964+ Py_DECREF(new);
4965+ Py_DECREF(tmp);
4966+ }
4967+ else {
4968+ if (!module) {
4969+ Py_BEGIN_ALLOW_THREADS
4970+ ap_log_error(APLOG_MARK, WSGI_LOG_ERR(0), wsgi_server,
4971+ "mod_wsgi (pid=%d): Unable to import 'site' "
4972+ "module.", getpid());
4973+ Py_END_ALLOW_THREADS
4974+ }
4975+
4976+ if (!path) {
4977+ Py_BEGIN_ALLOW_THREADS
4978+ ap_log_error(APLOG_MARK, WSGI_LOG_ERR(0), wsgi_server,
4979+ "mod_wsgi (pid=%d): Lookup for 'sys.path' "
4980+ "failed.", getpid());
4981+ Py_END_ALLOW_THREADS
4982+ }
4983+ }
4984+
4985+ Py_XDECREF(module);
4986+ }
4987+
4988+ /*
4989+ * Create 'mod_wsgi' Python module. We first try and import an
4990+ * external Python module of the same name. The intent is
4991+ * that this external module would provide optional features
4992+ * implementable using pure Python code. Don't want to
4993+ * include them in the main Apache mod_wsgi package as that
4994+ * complicates that package and also wouldn't allow them to
4995+ * be released to a separate schedule. It is easier for
4996+ * people to replace Python modules package with a new
4997+ * version than it is to replace Apache module package.
4998+ */
4999+
5000+ module = PyImport_ImportModule("mod_wsgi");
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: