1 /* ------------------------------------------------------------------------- */
2
3 /*
4 * Copyright 2007-2019 GRAHAM DUMPLETON
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 /* ------------------------------------------------------------------------- */
20
21 #include "wsgi_apache.h"
22 #include "wsgi_python.h"
23
24 #ifdef HAVE_SYS_PRCTL_H
25 #include <sys/prctl.h>
26 #endif
27
28 #ifndef WIN32
29 #include <pwd.h>
30 #endif
31
32 static PyTypeObject Auth_Type;
33 #if AP_SERVER_MINORVERSION_NUMBER >= 2
34 #define MOD_WSGI_WITH_AUTHN_PROVIDER 1
35 #endif
36 #if AP_MODULE_MAGIC_AT_LEAST(20060110,0)
37 #define MOD_WSGI_WITH_AUTHZ_PROVIDER 1
38 #if AP_MODULE_MAGIC_AT_LEAST(20100919,0)
39 #define MOD_WSGI_WITH_AUTHZ_PROVIDER_PARSED 1
40 #endif
41 #endif
42
43 #if defined(MOD_WSGI_WITH_AUTHN_PROVIDER)
44 #include "mod_auth.h"
45 #include "ap_provider.h"
46 #ifndef AUTHN_PROVIDER_VERSION
47 #define AUTHN_PROVIDER_VERSION "0"
48 #endif
49 #endif
50
51 /* Local project header files. */
52
53 #include "wsgi_version.h"
54 #include "wsgi_convert.h"
55 #include "wsgi_validate.h"
56 #include "wsgi_interp.h"
57 #include "wsgi_server.h"
58 #include "wsgi_logger.h"
59 #include "wsgi_restrict.h"
60 #include "wsgi_stream.h"
61 #include "wsgi_metrics.h"
62 #include "wsgi_daemon.h"
63 #include "wsgi_buckets.h"
64 #include "wsgi_thread.h"
65
66 /* Module information. */
67
68 module AP_MODULE_DECLARE_DATA wsgi_module;
69
70 /* Process information. */
71
72 static int wsgi_multiprocess = 1;
73 static int wsgi_multithread = 1;
74
75 /* Daemon information. */
76
77 static apr_array_header_t *wsgi_daemon_list = NULL;
78
79 static apr_pool_t *wsgi_parent_pool = NULL;
80
81 int volatile wsgi_daemon_shutdown = 0;
82 static int volatile wsgi_daemon_graceful = 0;
83 static int wsgi_dump_stack_traces = 0;
84 static char *wsgi_shutdown_reason = "";
85
86 #if defined(MOD_WSGI_WITH_DAEMONS)
87 static apr_interval_time_t wsgi_startup_timeout = 0;
88 static apr_interval_time_t wsgi_deadlock_timeout = 0;
89 static apr_interval_time_t wsgi_idle_timeout = 0;
90 static apr_interval_time_t wsgi_request_timeout = 0;
91 static apr_interval_time_t wsgi_graceful_timeout = 0;
92 static apr_interval_time_t wsgi_eviction_timeout = 0;
93 static apr_interval_time_t wsgi_restart_interval = 0;
94 static apr_time_t volatile wsgi_startup_shutdown_time = 0;
95 static apr_time_t volatile wsgi_deadlock_shutdown_time = 0;
96 static apr_time_t volatile wsgi_idle_shutdown_time = 0;
97 static apr_time_t volatile wsgi_graceful_shutdown_time = 0;
98 static apr_time_t volatile wsgi_restart_shutdown_time = 0;
99 #endif
100
101 /* Script information. */
102
103 static apr_array_header_t *wsgi_import_list = NULL;
104
wsgi_create_server_config(apr_pool_t * p,server_rec * s)105 static void *wsgi_create_server_config(apr_pool_t *p, server_rec *s)
106 {
107 WSGIServerConfig *config = NULL;
108
109 config = newWSGIServerConfig(p);
110
111 return config;
112 }
113
wsgi_merge_server_config(apr_pool_t * p,void * base_conf,void * new_conf)114 static void *wsgi_merge_server_config(apr_pool_t *p, void *base_conf,
115 void *new_conf)
116 {
117 WSGIServerConfig *config = NULL;
118 WSGIServerConfig *parent = NULL;
119 WSGIServerConfig *child = NULL;
120
121 config = newWSGIServerConfig(p);
122
123 parent = (WSGIServerConfig *)base_conf;
124 child = (WSGIServerConfig *)new_conf;
125
126 if (child->alias_list && parent->alias_list) {
127 config->alias_list = apr_array_append(p, child->alias_list,
128 parent->alias_list);
129 }
130 else if (child->alias_list) {
131 config->alias_list = apr_array_make(p, 20, sizeof(WSGIAliasEntry));
132 apr_array_cat(config->alias_list, child->alias_list);
133 }
134 else if (parent->alias_list) {
135 config->alias_list = apr_array_make(p, 20, sizeof(WSGIAliasEntry));
136 apr_array_cat(config->alias_list, parent->alias_list);
137 }
138
139 if (child->restrict_process)
140 config->restrict_process = child->restrict_process;
141 else
142 config->restrict_process = parent->restrict_process;
143
144 if (child->process_group)
145 config->process_group = child->process_group;
146 else
147 config->process_group = parent->process_group;
148
149 if (child->application_group)
150 config->application_group = child->application_group;
151 else
152 config->application_group = parent->application_group;
153
154 if (child->callable_object)
155 config->callable_object = child->callable_object;
156 else
157 config->callable_object = parent->callable_object;
158
159 if (child->dispatch_script)
160 config->dispatch_script = child->dispatch_script;
161 else
162 config->dispatch_script = parent->dispatch_script;
163
164 if (child->pass_apache_request != -1)
165 config->pass_apache_request = child->pass_apache_request;
166 else
167 config->pass_apache_request = parent->pass_apache_request;
168
169 if (child->pass_authorization != -1)
170 config->pass_authorization = child->pass_authorization;
171 else
172 config->pass_authorization = parent->pass_authorization;
173
174 if (child->script_reloading != -1)
175 config->script_reloading = child->script_reloading;
176 else
177 config->script_reloading = parent->script_reloading;
178
179 if (child->error_override != -1)
180 config->error_override = child->error_override;
181 else
182 config->error_override = parent->error_override;
183
184 if (child->chunked_request != -1)
185 config->chunked_request = child->chunked_request;
186 else
187 config->chunked_request = parent->chunked_request;
188
189 if (child->map_head_to_get != -1)
190 config->map_head_to_get = child->map_head_to_get;
191 else
192 config->map_head_to_get = parent->map_head_to_get;
193
194 if (child->ignore_activity != -1)
195 config->ignore_activity = child->ignore_activity;
196 else
197 config->ignore_activity = parent->ignore_activity;
198
199 if (child->trusted_proxy_headers)
200 config->trusted_proxy_headers = child->trusted_proxy_headers;
201 else
202 config->trusted_proxy_headers = parent->trusted_proxy_headers;
203
204 if (child->trusted_proxies)
205 config->trusted_proxies = child->trusted_proxies;
206 else
207 config->trusted_proxies = parent->trusted_proxies;
208
209 if (child->enable_sendfile != -1)
210 config->enable_sendfile = child->enable_sendfile;
211 else
212 config->enable_sendfile = parent->enable_sendfile;
213
214 if (!child->handler_scripts)
215 config->handler_scripts = parent->handler_scripts;
216 else if (!parent->handler_scripts)
217 config->handler_scripts = child->handler_scripts;
218 else {
219 config->handler_scripts = apr_hash_overlay(p, child->handler_scripts,
220 parent->handler_scripts);
221 }
222
223 return config;
224 }
225
226 typedef struct {
227 apr_pool_t *pool;
228
229 apr_table_t *restrict_process;
230
231 const char *process_group;
232 const char *application_group;
233 const char *callable_object;
234
235 WSGIScriptFile *dispatch_script;
236
237 int pass_apache_request;
238 int pass_authorization;
239 int script_reloading;
240 int error_override;
241 int chunked_request;
242 int map_head_to_get;
243 int ignore_activity;
244
245 apr_array_header_t *trusted_proxy_headers;
246 apr_array_header_t *trusted_proxies;
247
248 int enable_sendfile;
249
250 WSGIScriptFile *access_script;
251 WSGIScriptFile *auth_user_script;
252 WSGIScriptFile *auth_group_script;
253 int user_authoritative;
254 int group_authoritative;
255
256 apr_hash_t *handler_scripts;
257 } WSGIDirectoryConfig;
258
newWSGIDirectoryConfig(apr_pool_t * p)259 static WSGIDirectoryConfig *newWSGIDirectoryConfig(apr_pool_t *p)
260 {
261 WSGIDirectoryConfig *object = NULL;
262
263 object = (WSGIDirectoryConfig *)apr_pcalloc(p, sizeof(WSGIDirectoryConfig));
264
265 object->pool = p;
266
267 object->process_group = NULL;
268 object->application_group = NULL;
269 object->callable_object = NULL;
270
271 object->dispatch_script = NULL;
272
273 object->pass_apache_request = -1;
274 object->pass_authorization = -1;
275 object->script_reloading = -1;
276 object->error_override = -1;
277 object->chunked_request = -1;
278 object->map_head_to_get = -1;
279 object->ignore_activity = -1;
280
281 object->trusted_proxy_headers = NULL;
282 object->trusted_proxies = NULL;
283
284 object->enable_sendfile = -1;
285
286 object->access_script = NULL;
287 object->auth_user_script = NULL;
288 object->auth_group_script = NULL;
289 object->user_authoritative = -1;
290 object->group_authoritative = -1;
291
292 return object;
293 }
294
wsgi_create_dir_config(apr_pool_t * p,char * dir)295 static void *wsgi_create_dir_config(apr_pool_t *p, char *dir)
296 {
297 WSGIDirectoryConfig *config = NULL;
298
299 config = newWSGIDirectoryConfig(p);
300
301 return config;
302 }
303
wsgi_merge_dir_config(apr_pool_t * p,void * base_conf,void * new_conf)304 static void *wsgi_merge_dir_config(apr_pool_t *p, void *base_conf,
305 void *new_conf)
306 {
307 WSGIDirectoryConfig *config = NULL;
308 WSGIDirectoryConfig *parent = NULL;
309 WSGIDirectoryConfig *child = NULL;
310
311 config = newWSGIDirectoryConfig(p);
312
313 parent = (WSGIDirectoryConfig *)base_conf;
314 child = (WSGIDirectoryConfig *)new_conf;
315
316 if (child->restrict_process)
317 config->restrict_process = child->restrict_process;
318 else
319 config->restrict_process = parent->restrict_process;
320
321 if (child->process_group)
322 config->process_group = child->process_group;
323 else
324 config->process_group = parent->process_group;
325
326 if (child->application_group)
327 config->application_group = child->application_group;
328 else
329 config->application_group = parent->application_group;
330
331 if (child->callable_object)
332 config->callable_object = child->callable_object;
333 else
334 config->callable_object = parent->callable_object;
335
336 if (child->dispatch_script)
337 config->dispatch_script = child->dispatch_script;
338 else
339 config->dispatch_script = parent->dispatch_script;
340
341 if (child->pass_apache_request != -1)
342 config->pass_apache_request = child->pass_apache_request;
343 else
344 config->pass_apache_request = parent->pass_apache_request;
345
346 if (child->pass_authorization != -1)
347 config->pass_authorization = child->pass_authorization;
348 else
349 config->pass_authorization = parent->pass_authorization;
350
351 if (child->script_reloading != -1)
352 config->script_reloading = child->script_reloading;
353 else
354 config->script_reloading = parent->script_reloading;
355
356 if (child->error_override != -1)
357 config->error_override = child->error_override;
358 else
359 config->error_override = parent->error_override;
360
361 if (child->chunked_request != -1)
362 config->chunked_request = child->chunked_request;
363 else
364 config->chunked_request = parent->chunked_request;
365
366 if (child->map_head_to_get != -1)
367 config->map_head_to_get = child->map_head_to_get;
368 else
369 config->map_head_to_get = parent->map_head_to_get;
370
371 if (child->ignore_activity != -1)
372 config->ignore_activity = child->ignore_activity;
373 else
374 config->ignore_activity = parent->ignore_activity;
375
376 if (child->trusted_proxy_headers)
377 config->trusted_proxy_headers = child->trusted_proxy_headers;
378 else
379 config->trusted_proxy_headers = parent->trusted_proxy_headers;
380
381 if (child->trusted_proxies)
382 config->trusted_proxies = child->trusted_proxies;
383 else
384 config->trusted_proxies = parent->trusted_proxies;
385
386 if (child->enable_sendfile != -1)
387 config->enable_sendfile = child->enable_sendfile;
388 else
389 config->enable_sendfile = parent->enable_sendfile;
390
391 if (child->access_script)
392 config->access_script = child->access_script;
393 else
394 config->access_script = parent->access_script;
395
396 if (child->auth_user_script)
397 config->auth_user_script = child->auth_user_script;
398 else
399 config->auth_user_script = parent->auth_user_script;
400
401 if (child->auth_group_script)
402 config->auth_group_script = child->auth_group_script;
403 else
404 config->auth_group_script = parent->auth_group_script;
405
406 if (child->user_authoritative != -1)
407 config->user_authoritative = child->user_authoritative;
408 else
409 config->user_authoritative = parent->user_authoritative;
410
411 if (child->group_authoritative != -1)
412 config->group_authoritative = child->group_authoritative;
413 else
414 config->group_authoritative = parent->group_authoritative;
415
416 if (!child->handler_scripts)
417 config->handler_scripts = parent->handler_scripts;
418 else if (!parent->handler_scripts)
419 config->handler_scripts = child->handler_scripts;
420 else {
421 config->handler_scripts = apr_hash_overlay(p, child->handler_scripts,
422 parent->handler_scripts);
423 }
424
425 return config;
426 }
427
428 typedef struct {
429 apr_pool_t *pool;
430
431 apr_table_t *restrict_process;
432
433 const char *process_group;
434 const char *application_group;
435 const char *callable_object;
436
437 WSGIScriptFile *dispatch_script;
438
439 int pass_apache_request;
440 int pass_authorization;
441 int script_reloading;
442 int error_override;
443 int chunked_request;
444 int map_head_to_get;
445 int ignore_activity;
446
447 apr_array_header_t *trusted_proxy_headers;
448 apr_array_header_t *trusted_proxies;
449
450 int enable_sendfile;
451
452 WSGIScriptFile *access_script;
453 WSGIScriptFile *auth_user_script;
454 WSGIScriptFile *auth_group_script;
455 int user_authoritative;
456 int group_authoritative;
457
458 apr_hash_t *handler_scripts;
459 const char *handler_script;
460
461 int daemon_connects;
462 int daemon_restarts;
463
464 apr_time_t request_start;
465 apr_time_t queue_start;
466 apr_time_t daemon_start;
467 } WSGIRequestConfig;
468
wsgi_find_path_info(const char * uri,const char * path_info)469 static long wsgi_find_path_info(const char *uri, const char *path_info)
470 {
471 long lu = strlen(uri);
472 long lp = strlen(path_info);
473
474 while (lu-- && lp-- && uri[lu] == path_info[lp]) {
475 if (path_info[lp] == '/') {
476 while (lu && uri[lu-1] == '/') lu--;
477 }
478 }
479
480 if (lu == -1) {
481 lu = 0;
482 }
483
484 while (uri[lu] != '\0' && uri[lu] != '/') {
485 lu++;
486 }
487 return lu;
488 }
489
wsgi_script_name(request_rec * r)490 static const char *wsgi_script_name(request_rec *r)
491 {
492 char *script_name = NULL;
493 long path_info_start = 0;
494
495 if (!r->path_info || !*r->path_info) {
496 script_name = apr_pstrdup(r->pool, r->uri);
497 }
498 else {
499 path_info_start = wsgi_find_path_info(r->uri, r->path_info);
500
501 script_name = apr_pstrndup(r->pool, r->uri, path_info_start);
502 }
503
504 if (*script_name) {
505 while (*script_name && (*(script_name+1) == '/'))
506 script_name++;
507 script_name = apr_pstrdup(r->pool, script_name);
508 ap_no2slash((char*)script_name);
509 }
510
511 ap_str_tolower(script_name);
512
513 return script_name;
514 }
515
wsgi_process_group(request_rec * r,const char * s)516 static const char *wsgi_process_group(request_rec *r, const char *s)
517 {
518 const char *name = NULL;
519 const char *value = NULL;
520
521 const char *h = NULL;
522 apr_port_t p = 0;
523 const char *n = NULL;
524
525 if (!s)
526 return "";
527
528 if (*s != '%')
529 return s;
530
531 name = s + 1;
532
533 if (*name) {
534 if (!strcmp(name, "{GLOBAL}"))
535 return "";
536
537 if (!strcmp(name, "{RESOURCE}")) {
538 h = r->server->server_hostname;
539 p = ap_get_server_port(r);
540 n = wsgi_script_name(r);
541
542 if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
543 return apr_psprintf(r->pool, "%s:%u|%s", h, p, n);
544 else
545 return apr_psprintf(r->pool, "%s|%s", h, n);
546 }
547
548 if (!strcmp(name, "{SERVER}")) {
549 h = r->server->server_hostname;
550 p = ap_get_server_port(r);
551
552 if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
553 return apr_psprintf(r->pool, "%s:%u", h, p);
554 else
555 return h;
556 }
557
558 if (!strcmp(name, "{HOST}")) {
559 h = r->hostname;
560 p = ap_get_server_port(r);
561
562 /*
563 * The Host header could be empty or absent for HTTP/1.0
564 * or older. In that case fallback to ServerName.
565 */
566
567 if (h == NULL || *h == 0)
568 h = r->server->server_hostname;
569
570 if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
571 return apr_psprintf(r->pool, "%s:%u", h, p);
572 else
573 return h;
574 }
575
576 if (strstr(name, "{ENV:") == name) {
577 long len = 0;
578
579 name = name + 5;
580 len = strlen(name);
581
582 if (len && name[len-1] == '}') {
583 name = apr_pstrndup(r->pool, name, len-1);
584
585 value = apr_table_get(r->notes, name);
586
587 if (!value)
588 value = apr_table_get(r->subprocess_env, name);
589
590 if (!value)
591 value = getenv(name);
592
593 if (value) {
594 if (*value == '%' && strstr(value, "%{ENV:") != value)
595 return wsgi_process_group(r, value);
596
597 return value;
598 }
599 }
600 }
601 }
602
603 return s;
604 }
605
wsgi_server_group(request_rec * r,const char * s)606 static const char *wsgi_server_group(request_rec *r, const char *s)
607 {
608 const char *name = NULL;
609
610 const char *h = NULL;
611 apr_port_t p = 0;
612
613 if (!s)
614 return "";
615
616 if (*s != '%')
617 return s;
618
619 name = s + 1;
620
621 if (*name) {
622 if (!strcmp(name, "{GLOBAL}"))
623 return "";
624
625 if (!strcmp(name, "{SERVER}")) {
626 h = r->server->server_hostname;
627 p = ap_get_server_port(r);
628
629 if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
630 return apr_psprintf(r->pool, "%s:%u", h, p);
631 else
632 return h;
633 }
634
635 if (!strcmp(name, "{HOST}")) {
636 h = r->hostname;
637 p = ap_get_server_port(r);
638
639 /*
640 * The Host header could be empty or absent for HTTP/1.0
641 * or older. In that case fallback to ServerName.
642 */
643
644 if (h == NULL || *h == 0)
645 h = r->server->server_hostname;
646
647 if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
648 return apr_psprintf(r->pool, "%s:%u", h, p);
649 else
650 return h;
651 }
652 }
653
654 return s;
655 }
656
wsgi_application_group(request_rec * r,const char * s)657 static const char *wsgi_application_group(request_rec *r, const char *s)
658 {
659 const char *name = NULL;
660 const char *value = NULL;
661
662 const char *h = NULL;
663 apr_port_t p = 0;
664 const char *n = NULL;
665
666 if (!s) {
667 h = r->server->server_hostname;
668 p = ap_get_server_port(r);
669 n = wsgi_script_name(r);
670
671 if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
672 return apr_psprintf(r->pool, "%s:%u|%s", h, p, n);
673 else
674 return apr_psprintf(r->pool, "%s|%s", h, n);
675 }
676
677 if (*s != '%')
678 return s;
679
680 name = s + 1;
681
682 if (*name) {
683 if (!strcmp(name, "{GLOBAL}"))
684 return "";
685
686 if (!strcmp(name, "{RESOURCE}")) {
687 h = r->server->server_hostname;
688 p = ap_get_server_port(r);
689 n = wsgi_script_name(r);
690
691 if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
692 return apr_psprintf(r->pool, "%s:%u|%s", h, p, n);
693 else
694 return apr_psprintf(r->pool, "%s|%s", h, n);
695 }
696
697 if (!strcmp(name, "{SERVER}")) {
698 h = r->server->server_hostname;
699 p = ap_get_server_port(r);
700
701 if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
702 return apr_psprintf(r->pool, "%s:%u", h, p);
703 else
704 return h;
705 }
706
707 if (!strcmp(name, "{HOST}")) {
708 h = r->hostname;
709 p = ap_get_server_port(r);
710
711 /*
712 * The Host header could be empty or absent for HTTP/1.0
713 * or older. In that case fallback to ServerName.
714 */
715
716 if (h == NULL || *h == 0)
717 h = r->server->server_hostname;
718
719 if (p != DEFAULT_HTTP_PORT && p != DEFAULT_HTTPS_PORT)
720 return apr_psprintf(r->pool, "%s:%u", h, p);
721 else
722 return h;
723 }
724
725 if (strstr(name, "{ENV:") == name) {
726 long len = 0;
727
728 name = name + 5;
729 len = strlen(name);
730
731 if (len && name[len-1] == '}') {
732 name = apr_pstrndup(r->pool, name, len-1);
733
734 value = apr_table_get(r->notes, name);
735
736 if (!value)
737 value = apr_table_get(r->subprocess_env, name);
738
739 if (!value)
740 value = getenv(name);
741
742 if (value) {
743 if (*value == '%' && strstr(value, "%{ENV:") != value)
744 return wsgi_application_group(r, value);
745
746 return value;
747 }
748 }
749 }
750 }
751
752 return s;
753 }
754
wsgi_callable_object(request_rec * r,const char * s)755 static const char *wsgi_callable_object(request_rec *r, const char *s)
756 {
757 const char *name = NULL;
758 const char *value = NULL;
759
760 if (!s)
761 return "application";
762
763 if (*s != '%')
764 return s;
765
766 name = s + 1;
767
768 if (!*name)
769 return "application";
770
771 if (strstr(name, "{ENV:") == name) {
772 long len = 0;
773
774 name = name + 5;
775 len = strlen(name);
776
777 if (len && name[len-1] == '}') {
778 name = apr_pstrndup(r->pool, name, len-1);
779
780 value = apr_table_get(r->notes, name);
781
782 if (!value)
783 value = apr_table_get(r->subprocess_env, name);
784
785 if (!value)
786 value = getenv(name);
787
788 if (value)
789 return value;
790 }
791 }
792
793 return "application";
794 }
795
wsgi_create_req_config(apr_pool_t * p,request_rec * r)796 static WSGIRequestConfig *wsgi_create_req_config(apr_pool_t *p, request_rec *r)
797 {
798 WSGIRequestConfig *config = NULL;
799 WSGIServerConfig *sconfig = NULL;
800 WSGIDirectoryConfig *dconfig = NULL;
801
802 config = (WSGIRequestConfig *)apr_pcalloc(p, sizeof(WSGIRequestConfig));
803
804 dconfig = ap_get_module_config(r->per_dir_config, &wsgi_module);
805 sconfig = ap_get_module_config(r->server->module_config, &wsgi_module);
806
807 config->pool = p;
808
809 config->restrict_process = dconfig->restrict_process;
810
811 if (!config->restrict_process)
812 config->restrict_process = sconfig->restrict_process;
813
814 config->process_group = dconfig->process_group;
815
816 if (!config->process_group)
817 config->process_group = sconfig->process_group;
818
819 config->process_group = wsgi_process_group(r, config->process_group);
820
821 config->application_group = dconfig->application_group;
822
823 if (!config->application_group)
824 config->application_group = sconfig->application_group;
825
826 config->application_group = wsgi_application_group(r,
827 config->application_group);
828
829 config->callable_object = dconfig->callable_object;
830
831 if (!config->callable_object)
832 config->callable_object = sconfig->callable_object;
833
834 config->callable_object = wsgi_callable_object(r, config->callable_object);
835
836 config->dispatch_script = dconfig->dispatch_script;
837
838 if (!config->dispatch_script)
839 config->dispatch_script = sconfig->dispatch_script;
840
841 config->pass_apache_request = dconfig->pass_apache_request;
842
843 if (config->pass_apache_request < 0) {
844 config->pass_apache_request = sconfig->pass_apache_request;
845 if (config->pass_apache_request < 0)
846 config->pass_apache_request = 0;
847 }
848
849 config->pass_authorization = dconfig->pass_authorization;
850
851 if (config->pass_authorization < 0) {
852 config->pass_authorization = sconfig->pass_authorization;
853 if (config->pass_authorization < 0)
854 config->pass_authorization = 0;
855 }
856
857 config->script_reloading = dconfig->script_reloading;
858
859 if (config->script_reloading < 0) {
860 config->script_reloading = sconfig->script_reloading;
861 if (config->script_reloading < 0)
862 config->script_reloading = 1;
863 }
864
865 config->error_override = dconfig->error_override;
866
867 if (config->error_override < 0) {
868 config->error_override = sconfig->error_override;
869 if (config->error_override < 0)
870 config->error_override = 0;
871 }
872
873 config->chunked_request = dconfig->chunked_request;
874
875 if (config->chunked_request < 0) {
876 config->chunked_request = sconfig->chunked_request;
877 if (config->chunked_request < 0)
878 config->chunked_request = 0;
879 }
880
881 config->map_head_to_get = dconfig->map_head_to_get;
882
883 if (config->map_head_to_get < 0) {
884 config->map_head_to_get = sconfig->map_head_to_get;
885 if (config->map_head_to_get < 0)
886 config->map_head_to_get = 2;
887 }
888
889 config->ignore_activity = dconfig->ignore_activity;
890
891 if (config->ignore_activity < 0) {
892 config->ignore_activity = sconfig->ignore_activity;
893 if (config->ignore_activity < 0)
894 config->ignore_activity = 0;
895 }
896
897 config->trusted_proxy_headers = dconfig->trusted_proxy_headers;
898
899 if (!config->trusted_proxy_headers)
900 config->trusted_proxy_headers = sconfig->trusted_proxy_headers;
901
902 config->trusted_proxies = dconfig->trusted_proxies;
903
904 if (!config->trusted_proxies)
905 config->trusted_proxies = sconfig->trusted_proxies;
906
907 config->enable_sendfile = dconfig->enable_sendfile;
908
909 if (config->enable_sendfile < 0) {
910 config->enable_sendfile = sconfig->enable_sendfile;
911 if (config->enable_sendfile < 0)
912 config->enable_sendfile = 0;
913 }
914
915 config->access_script = dconfig->access_script;
916
917 config->auth_user_script = dconfig->auth_user_script;
918
919 config->auth_group_script = dconfig->auth_group_script;
920
921 config->user_authoritative = dconfig->user_authoritative;
922
923 if (config->user_authoritative == -1)
924 config->user_authoritative = 1;
925
926 config->group_authoritative = dconfig->group_authoritative;
927
928 if (config->group_authoritative == -1)
929 config->group_authoritative = 1;
930
931 if (!dconfig->handler_scripts)
932 config->handler_scripts = sconfig->handler_scripts;
933 else if (!sconfig->handler_scripts)
934 config->handler_scripts = dconfig->handler_scripts;
935 else {
936 config->handler_scripts = apr_hash_overlay(p, dconfig->handler_scripts,
937 sconfig->handler_scripts);
938 }
939
940 config->handler_script = "";
941
942 config->daemon_connects = 0;
943 config->daemon_restarts = 0;
944
945 config->request_start = 0;
946 config->queue_start = 0;
947 config->daemon_start = 0;
948
949 return config;
950 }
951
952 /* Error reporting. */
953
wsgi_log_script_error(request_rec * r,const char * e,const char * n)954 static void wsgi_log_script_error(request_rec *r, const char *e, const char *n)
955 {
956 char *message = NULL;
957
958 if (!n)
959 n = r->filename;
960
961 message = apr_psprintf(r->pool, "%s: %s", e, n);
962
963 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "%s", message);
964 }
965
966 /* Class objects used by response handler. */
967
968 static PyTypeObject Dispatch_Type;
969
970 typedef struct {
971 PyObject_HEAD
972 request_rec *r;
973 int init;
974 int done;
975 char *buffer;
976 apr_off_t size;
977 apr_off_t offset;
978 apr_off_t length;
979 apr_bucket_brigade *bb;
980 int seen_eos;
981 int seen_error;
982 apr_off_t bytes;
983 apr_off_t reads;
984 apr_time_t time;
985 int ignore_activity;
986 } InputObject;
987
988 static PyTypeObject Input_Type;
989
newInputObject(request_rec * r,int ignore_activity)990 static InputObject *newInputObject(request_rec *r, int ignore_activity)
991 {
992 InputObject *self;
993
994 self = PyObject_New(InputObject, &Input_Type);
995 if (self == NULL)
996 return NULL;
997
998 self->r = r;
999 self->init = 0;
1000 self->done = 0;
1001
1002 self->buffer = NULL;
1003 self->size = 0;
1004 self->offset = 0;
1005 self->length = 0;
1006
1007 self->bb = NULL;
1008
1009 self->seen_eos = 0;
1010 self->seen_error = 0;
1011
1012 self->bytes = 0;
1013 self->reads = 0;
1014 self->time = 0;
1015
1016 self->ignore_activity = ignore_activity;
1017
1018 return self;
1019 }
1020
Input_dealloc(InputObject * self)1021 static void Input_dealloc(InputObject *self)
1022 {
1023 if (self->buffer)
1024 free(self->buffer);
1025
1026 PyObject_Del(self);
1027 }
1028
Input_finish(InputObject * self)1029 static void Input_finish(InputObject *self)
1030 {
1031 if (self->bb) {
1032 Py_BEGIN_ALLOW_THREADS
1033 apr_brigade_destroy(self->bb);
1034 Py_END_ALLOW_THREADS
1035
1036 self->bb = NULL;
1037 }
1038
1039 self->r = NULL;
1040 }
1041
Input_close(InputObject * self,PyObject * args)1042 static PyObject *Input_close(InputObject *self, PyObject *args)
1043 {
1044 if (!self->r) {
1045 PyErr_SetString(PyExc_RuntimeError, "request object has expired");
1046 return NULL;
1047 }
1048
1049 Py_INCREF(Py_None);
1050 return Py_None;
1051 }
1052
wsgi_strtoff(apr_off_t * offset,const char * nptr,char ** endptr,int base)1053 static apr_status_t wsgi_strtoff(apr_off_t *offset, const char *nptr,
1054 char **endptr, int base)
1055 {
1056 errno = 0;
1057 if (sizeof(apr_off_t) == 4) {
1058 *offset = strtol(nptr, endptr, base);
1059 }
1060 else {
1061 *offset = apr_strtoi64(nptr, endptr, base);
1062 }
1063 return APR_FROM_OS_ERROR(errno);
1064 }
1065
Input_read_from_input(InputObject * self,char * buffer,apr_size_t bufsiz)1066 static apr_int64_t Input_read_from_input(InputObject *self, char *buffer,
1067 apr_size_t bufsiz)
1068 {
1069 request_rec *r = self->r;
1070 apr_bucket_brigade *bb = self->bb;
1071
1072 apr_status_t rv;
1073
1074 apr_status_t error_status = 0;
1075 const char *error_message = NULL;
1076
1077 apr_time_t start = 0;
1078 apr_time_t finish = 0;
1079
1080 /* If have already seen end of input, return an empty string. */
1081
1082 if (self->seen_eos)
1083 return 0;
1084
1085 /* If have already encountered an error, then raise a new error. */
1086
1087 if (self->seen_error) {
1088 PyErr_SetString(PyExc_IOError, "Apache/mod_wsgi request data read "
1089 "error: Input is already in error state.");
1090
1091 return -1;
1092 }
1093
1094 /*
1095 * When reaading the request content we will be saying that we
1096 * should block if there is no input data available at that
1097 * point but not all data has been exhausted. We therefore need
1098 * to ensure that we do not cause Python as a whole to block by
1099 * releasing the GIL, but also must remember to reacquire the GIL
1100 * when we exit.
1101 */
1102
1103 Py_BEGIN_ALLOW_THREADS
1104
1105 start = apr_time_now();
1106
1107 self->reads += 1;
1108
1109 /*
1110 * Create the bucket brigade the first time it is required and
1111 * save it against the input object. We need to make sure we
1112 * perform a cleanup, but not destroy, the bucket brigade each
1113 * time we exit this function.
1114 */
1115
1116 if (!bb) {
1117 bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
1118
1119 if (bb == NULL) {
1120 r->connection->keepalive = AP_CONN_CLOSE;
1121 error_message = "Unable to create bucket brigade";
1122 goto finally;
1123 }
1124
1125 self->bb = bb;
1126 }
1127
1128 /* Force the required amount of input to be read. */
1129
1130 rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
1131 APR_BLOCK_READ, bufsiz);
1132
1133 if (rv != APR_SUCCESS) {
1134 /*
1135 * If we actually fail here, we want to just return and
1136 * stop trying to read data from the client. The HTTP_IN
1137 * input filter is a bit of a pain here as it can return
1138 * EAGAIN in various strange situations where it isn't
1139 * believed that it means to retry, but that it is still
1140 * a permanent failure. This can include timeouts and
1141 * errors in chunked encoding format. To avoid a message
1142 * of 'Resource temporarily unavailable' which could be
1143 * confusing, replace it with a generic message that the
1144 * connection was terminated.
1145 */
1146
1147 r->connection->keepalive = AP_CONN_CLOSE;
1148
1149 if (APR_STATUS_IS_EAGAIN(rv))
1150 error_message = "Connection was terminated";
1151 else
1152 error_status = rv;
1153
1154 goto finally;
1155 }
1156
1157 /*
1158 * If this fails, it means that a filter is written incorrectly and
1159 * that it needs to learn how to properly handle APR_BLOCK_READ
1160 * requests by returning data when requested.
1161 */
1162
1163 AP_DEBUG_ASSERT(!APR_BRIGADE_EMPTY(bb));
1164
1165 /*
1166 * Check to see if EOS terminates the brigade. If so, we remember
1167 * this to avoid any attempts to read more data in future calls.
1168 */
1169
1170 if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb)))
1171 self->seen_eos = 1;
1172
1173 /* Now extract the actual data from the bucket brigade. */
1174
1175 rv = apr_brigade_flatten(bb, buffer, &bufsiz);
1176
1177 if (rv != APR_SUCCESS) {
1178 error_status = rv;
1179 goto finally;
1180 }
1181
1182 finally:
1183 /*
1184 * We must always cleanup up, not destroy, the brigade after
1185 * each call.
1186 */
1187
1188 if (bb)
1189 apr_brigade_cleanup(bb);
1190
1191 finish = apr_time_now();
1192
1193 if (finish > start)
1194 self->time += (finish - start);
1195
1196 /* Make sure we reacquire the GIL when all done. */
1197
1198 Py_END_ALLOW_THREADS
1199
1200 /*
1201 * Set any Python exception when an error has occurred and
1202 * remember there was an error so can flag on subsequent
1203 * reads that already in an error state.
1204 */
1205
1206 if (error_status) {
1207 char status_buffer[512];
1208
1209 error_message = apr_psprintf(r->pool, "Apache/mod_wsgi request "
1210 "data read error: %s.", apr_strerror(error_status,
1211 status_buffer, sizeof(status_buffer)-1));
1212
1213 PyErr_SetString(PyExc_IOError, error_message);
1214
1215 self->seen_error = 1;
1216
1217 return -1;
1218 }
1219 else if (error_message) {
1220 error_message = apr_psprintf(r->pool, "Apache/mod_wsgi request "
1221 "data read error: %s.", error_message);
1222
1223 PyErr_SetString(PyExc_IOError, error_message);
1224
1225 self->seen_error = 1;
1226
1227 return -1;
1228 }
1229
1230 /*
1231 * Finally return the amount of data that was read. This will be
1232 * zero if all data has been consumed.
1233 */
1234
1235 return bufsiz;
1236 }
1237
Input_read(InputObject * self,PyObject * args)1238 static PyObject *Input_read(InputObject *self, PyObject *args)
1239 {
1240 #if defined(HAVE_LONG_LONG)
1241 PY_LONG_LONG size = -1;
1242 #else
1243 long size = -1;
1244 #endif
1245
1246 PyObject *result = NULL;
1247 char *buffer = NULL;
1248 apr_off_t length = 0;
1249 int init = 0;
1250
1251 apr_int64_t n;
1252
1253 if (!self->r) {
1254 PyErr_SetString(PyExc_RuntimeError, "request object has expired");
1255 return NULL;
1256 }
1257
1258 #if defined(HAVE_LONG_LONG)
1259 if (!PyArg_ParseTuple(args, "|L:read", &size))
1260 return NULL;
1261 #else
1262 if (!PyArg_ParseTuple(args, "|l:read", &size))
1263 return NULL;
1264 #endif
1265
1266 #if defined(MOD_WSGI_WITH_DAEMONS)
1267 if (wsgi_idle_timeout && !self->ignore_activity) {
1268 apr_thread_mutex_lock(wsgi_monitor_lock);
1269
1270 if (wsgi_idle_timeout) {
1271 wsgi_idle_shutdown_time = apr_time_now();
1272 wsgi_idle_shutdown_time += wsgi_idle_timeout;
1273 }
1274
1275 apr_thread_mutex_unlock(wsgi_monitor_lock);
1276 }
1277 #endif
1278
1279 if (self->seen_error) {
1280 PyErr_SetString(PyExc_IOError, "Apache/mod_wsgi request data read "
1281 "error: Input is already in error state.");
1282
1283 return NULL;
1284 }
1285
1286 init = self->init;
1287
1288 if (!self->init)
1289 self->init = 1;
1290
1291 /* No point continuing if no more data to be consumed. */
1292
1293 if (self->done && self->length == 0)
1294 return PyString_FromString("");
1295
1296 /*
1297 * If requested size is zero bytes, then still need to pass
1298 * this through to Apache input filters so that any
1299 * 100-continue response is triggered. Only do this if very
1300 * first attempt to read data. Note that this will cause an
1301 * assertion failure in HTTP_IN input filter when Apache
1302 * maintainer mode is enabled. It is arguable that the
1303 * assertion check, which prohibits a zero length read,
1304 * shouldn't exist, as why should a zero length read be not
1305 * allowed if input filter processing still works when it
1306 * does occur.
1307 */
1308
1309 if (size == 0) {
1310 if (!init) {
1311 char dummy[1];
1312
1313 n = Input_read_from_input(self, dummy, 0);
1314
1315 if (n == -1)
1316 return NULL;
1317 }
1318
1319 return PyString_FromString("");
1320 }
1321
1322 /*
1323 * First deal with case where size has been specified. After
1324 * that deal with case where expected that all remaining
1325 * data is to be read in and returned as one string.
1326 */
1327
1328 if (size > 0) {
1329 /* Allocate string of the exact size required. */
1330
1331 result = PyString_FromStringAndSize(NULL, size);
1332
1333 if (!result)
1334 return NULL;
1335
1336 buffer = PyString_AS_STRING((PyStringObject *)result);
1337
1338 /* Copy any residual data from use of readline(). */
1339
1340 if (self->buffer && self->length) {
1341 if (size >= self->length) {
1342 length = self->length;
1343 memcpy(buffer, self->buffer + self->offset, length);
1344 self->offset = 0;
1345 self->length = 0;
1346 }
1347 else {
1348 length = size;
1349 memcpy(buffer, self->buffer + self->offset, length);
1350 self->offset += length;
1351 self->length -= length;
1352 }
1353 }
1354
1355 /* If all data residual buffer consumed then free it. */
1356
1357 if (!self->length) {
1358 free(self->buffer);
1359 self->buffer = NULL;
1360 }
1361
1362 /* Read in remaining data required to achieve size. */
1363
1364 if (length < size) {
1365 while (length != size) {
1366 n = Input_read_from_input(self, buffer+length, size-length);
1367
1368 if (n == -1) {
1369 Py_DECREF(result);
1370 return NULL;
1371 }
1372 else if (n == 0) {
1373 /* Have exhausted all the available input data. */
1374
1375 self->done = 1;
1376 break;
1377 }
1378
1379 length += n;
1380 }
1381
1382 /*
1383 * Resize the final string. If the size reduction is
1384 * by more than 25% of the string size, then Python
1385 * will allocate a new block of memory and copy the
1386 * data into it.
1387 */
1388
1389 if (length != size) {
1390 if (_PyString_Resize(&result, length))
1391 return NULL;
1392 }
1393 }
1394 }
1395 else {
1396 /*
1397 * Here we are going to try and read in all the
1398 * remaining data. First we have to allocate a suitably
1399 * large string, but we can't fully trust the amount
1400 * that the request structure says is remaining based on
1401 * the original content length though, as an input
1402 * filter can insert/remove data from the input stream
1403 * thereby invalidating the original content length.
1404 * What we do is allow for an extra 25% above what we
1405 * have already buffered and what the request structure
1406 * says is remaining. A value of 25% has been chosen so
1407 * as to match best how Python handles resizing of
1408 * strings. Note that even though we do this and allow
1409 * all available content, strictly speaking the WSGI
1410 * specification says we should only read up until content
1411 * length. This though is because the WSGI specification
1412 * is deficient in dealing with the concept of mutating
1413 * input filters. Since read() with no argument is also
1414 * not allowed by WSGI specification implement it in the
1415 * way which is most logical and ensure that input data
1416 * is not truncated.
1417 */
1418
1419 if (self->buffer) {
1420 size = self->length;
1421 size = size + (size >> 2);
1422
1423 if (size < HUGE_STRING_LEN)
1424 size = HUGE_STRING_LEN;
1425 }
1426 else
1427 size = HUGE_STRING_LEN;
1428
1429 /* Allocate string of the estimated size. */
1430
1431 result = PyString_FromStringAndSize(NULL, size);
1432
1433 if (!result)
1434 return NULL;
1435
1436 buffer = PyString_AS_STRING((PyStringObject *)result);
1437
1438 /*
1439 * Copy any residual data from use of readline(). The
1440 * residual should always be less in size than the
1441 * string we have allocated to hold it, so can consume
1442 * all of it.
1443 */
1444
1445 if (self->buffer && self->length) {
1446 length = self->length;
1447 memcpy(buffer, self->buffer + self->offset, length);
1448 self->offset = 0;
1449 self->length = 0;
1450
1451 free(self->buffer);
1452 self->buffer = NULL;
1453 }
1454
1455 /* Now make first attempt at reading remaining data. */
1456
1457 n = Input_read_from_input(self, buffer+length, size-length);
1458
1459 if (n == -1) {
1460 Py_DECREF(result);
1461 return NULL;
1462 }
1463 else if (n == 0) {
1464 /* Have exhausted all the available input data. */
1465
1466 self->done = 1;
1467 }
1468
1469 length += n;
1470
1471 /*
1472 * Don't just assume that all data has been read if
1473 * amount read was less than that requested. Still must
1474 * perform a read which returns that no more data found.
1475 */
1476
1477 while (!self->done) {
1478 if (length == size) {
1479 /* Increase the size of the string by 25%. */
1480
1481 size = size + (size >> 2);
1482
1483 if (_PyString_Resize(&result, size))
1484 return NULL;
1485
1486 buffer = PyString_AS_STRING((PyStringObject *)result);
1487 }
1488
1489 /* Now make succesive attempt at reading data. */
1490
1491 n = Input_read_from_input(self, buffer+length, size-length);
1492
1493 if (n == -1) {
1494 Py_DECREF(result);
1495 return NULL;
1496 }
1497 else if (n == 0) {
1498 /* Have exhausted all the available input data. */
1499
1500 self->done = 1;
1501 }
1502
1503 length += n;
1504 }
1505
1506 /*
1507 * Resize the final string. If the size reduction is by
1508 * more than 25% of the string size, then Python will
1509 * allocate a new block of memory and copy the data into
1510 * it.
1511 */
1512
1513 if (length != size) {
1514 if (_PyString_Resize(&result, length))
1515 return NULL;
1516 }
1517 }
1518
1519 self->bytes += length;
1520
1521 return result;
1522 }
1523
Input_readline(InputObject * self,PyObject * args)1524 static PyObject *Input_readline(InputObject *self, PyObject *args)
1525 {
1526 #if defined(HAVE_LONG_LONG)
1527 PY_LONG_LONG size = -1;
1528 #else
1529 long size = -1;
1530 #endif
1531
1532 PyObject *result = NULL;
1533 char *buffer = NULL;
1534 apr_off_t length = 0;
1535
1536 apr_int64_t n;
1537
1538 if (!self->r) {
1539 PyErr_SetString(PyExc_RuntimeError, "request object has expired");
1540 return NULL;
1541 }
1542
1543 #if defined(HAVE_LONG_LONG)
1544 if (!PyArg_ParseTuple(args, "|L:readline", &size))
1545 return NULL;
1546 #else
1547 if (!PyArg_ParseTuple(args, "|l:readline", &size))
1548 return NULL;
1549 #endif
1550
1551 if (self->seen_error) {
1552 PyErr_SetString(PyExc_IOError, "Apache/mod_wsgi request data read "
1553 "error: Input is already in error state.");
1554
1555 return NULL;
1556 }
1557
1558 if (!self->init)
1559 self->init = 1;
1560
1561 /*
1562 * No point continuing if requested size is zero or if no
1563 * more data to read and no buffered data.
1564 */
1565
1566 if ((self->done && self->length == 0) || size == 0)
1567 return PyString_FromString("");
1568
1569 /*
1570 * First deal with case where size has been specified. After
1571 * that deal with case where expected that a complete line
1572 * is returned regardless of the size.
1573 */
1574
1575 if (size > 0) {
1576 /* Allocate string of the exact size required. */
1577
1578 result = PyString_FromStringAndSize(NULL, size);
1579
1580 if (!result)
1581 return NULL;
1582
1583 buffer = PyString_AS_STRING((PyStringObject *)result);
1584
1585 /* Copy any residual data from use of readline(). */
1586
1587 if (self->buffer && self->length) {
1588 char *p = NULL;
1589 const char *q = NULL;
1590
1591 p = buffer;
1592 q = self->buffer + self->offset;
1593
1594 while (self->length && length < size) {
1595 self->offset++;
1596 self->length--;
1597 length++;
1598 if ((*p++ = *q++) == '\n')
1599 break;
1600 }
1601
1602 /* If all data in residual buffer consumed then free it. */
1603
1604 if (!self->length) {
1605 free(self->buffer);
1606 self->buffer = NULL;
1607 }
1608 }
1609
1610 /*
1611 * Read in remaining data required to achieve size. Note
1612 * that can't just return whatever the first read might
1613 * have returned if no EOL encountered as must return
1614 * exactly the required size if no EOL unless that would
1615 * have exhausted all input.
1616 */
1617
1618 while ((!length || buffer[length-1] != '\n') &&
1619 !self->done && length < size) {
1620
1621 char *p = NULL;
1622 char *q = NULL;
1623
1624 n = Input_read_from_input(self, buffer+length, size-length);
1625
1626 if (n == -1) {
1627 Py_DECREF(result);
1628 return NULL;
1629 }
1630 else if (n == 0) {
1631 /* Have exhausted all the available input data. */
1632
1633 self->done = 1;
1634 }
1635 else {
1636 /*
1637 * Search for embedded EOL in what was read and if
1638 * found copy any residual into a buffer for use
1639 * next time the read functions are called.
1640 */
1641
1642 p = buffer + length;
1643 q = p + n;
1644
1645 while (p != q) {
1646 length++;
1647 if (*p++ == '\n')
1648 break;
1649 }
1650
1651 if (p != q) {
1652 self->size = q - p;
1653 self->buffer = (char *)malloc(self->size);
1654 self->offset = 0;
1655 self->length = self->size;
1656
1657 memcpy(self->buffer, p, self->size);
1658 }
1659 }
1660 }
1661
1662 /*
1663 * Resize the final string. If the size reduction is
1664 * by more than 25% of the string size, then Python
1665 * will allocate a new block of memory and copy the
1666 * data into it.
1667 */
1668
1669 if (length != size) {
1670 if (_PyString_Resize(&result, length))
1671 return NULL;
1672 }
1673 }
1674 else {
1675 /*
1676 * Here we have to read in a line but where we have no
1677 * idea how long it may be. What we can do first is if
1678 * we have any residual data from a previous read
1679 * operation, see if it contains an EOL. This means we
1680 * have to do a search, but this is likely going to be
1681 * better than having to resize and copy memory later on.
1682 */
1683
1684 if (self->buffer && self->length) {
1685 const char *p = NULL;
1686 const char *q = NULL;
1687
1688 p = self->buffer + self->offset;
1689 q = memchr(p, '\n', self->length);
1690
1691 if (q)
1692 size = q - p;
1693 }
1694
1695 /*
1696 * If residual data buffer didn't contain an EOL, all we
1697 * can do is allocate a reasonably sized string and if
1698 * that isn't big enough keep increasing it in size. For
1699 * this we will start out with a buffer 25% greater in
1700 * size than what is stored in the residual data buffer
1701 * or one the same size as Apache string size, whichever
1702 * is greater.
1703 */
1704
1705 if (self->buffer && size < 0) {
1706 size = self->length;
1707 size = size + (size >> 2);
1708 }
1709
1710 if (size < HUGE_STRING_LEN)
1711 size = HUGE_STRING_LEN;
1712
1713 /* Allocate string of the initial size. */
1714
1715 result = PyString_FromStringAndSize(NULL, size);
1716
1717 if (!result)
1718 return NULL;
1719
1720 buffer = PyString_AS_STRING((PyStringObject *)result);
1721
1722 /* Copy any residual data from use of readline(). */
1723
1724 if (self->buffer && self->length) {
1725 char *p = NULL;
1726 const char *q = NULL;
1727
1728 p = buffer;
1729 q = self->buffer + self->offset;
1730
1731 while (self->length && length < size) {
1732 self->offset++;
1733 self->length--;
1734 length++;
1735 if ((*p++ = *q++) == '\n')
1736 break;
1737 }
1738
1739 /* If all data in residual buffer consumed then free it. */
1740
1741 if (!self->length) {
1742 free(self->buffer);
1743 self->buffer = NULL;
1744 }
1745 }
1746
1747 /*
1748 * Read in remaining data until find an EOL, or until all
1749 * data has been consumed.
1750 */
1751
1752 while ((!length || buffer[length-1] != '\n') && !self->done) {
1753
1754 char *p = NULL;
1755 char *q = NULL;
1756
1757 n = Input_read_from_input(self, buffer+length, size-length);
1758
1759 if (n == -1) {
1760 Py_DECREF(result);
1761 return NULL;
1762 }
1763 else if (n == 0) {
1764 /* Have exhausted all the available input data. */
1765
1766 self->done = 1;
1767 }
1768 else {
1769 /*
1770 * Search for embedded EOL in what was read and if
1771 * found copy any residual into a buffer for use
1772 * next time the read functions are called.
1773 */
1774
1775 p = buffer + length;
1776 q = p + n;
1777
1778 while (p != q) {
1779 length++;
1780 if (*p++ == '\n')
1781 break;
1782 }
1783
1784 if (p != q) {
1785 self->size = q - p;
1786 self->buffer = (char *)malloc(self->size);
1787 self->offset = 0;
1788 self->length = self->size;
1789
1790 memcpy(self->buffer, p, self->size);
1791 }
1792
1793 if (buffer[length-1] != '\n' && length == size) {
1794 /* Increase size of string and keep going. */
1795
1796 size = size + (size >> 2);
1797
1798 if (_PyString_Resize(&result, size))
1799 return NULL;
1800
1801 buffer = PyString_AS_STRING((PyStringObject *)result);
1802 }
1803 }
1804 }
1805
1806 /*
1807 * Resize the final string. If the size reduction is by
1808 * more than 25% of the string size, then Python will
1809 * allocate a new block of memory and copy the data into
1810 * it.
1811 */
1812
1813 if (length != size) {
1814 if (_PyString_Resize(&result, length))
1815 return NULL;
1816 }
1817 }
1818
1819 self->bytes += length;
1820
1821 return result;
1822 }
1823
Input_readlines(InputObject * self,PyObject * args)1824 static PyObject *Input_readlines(InputObject *self, PyObject *args)
1825 {
1826 long hint = 0;
1827 long length = 0;
1828
1829 PyObject *result = NULL;
1830 PyObject *line = NULL;
1831 PyObject *rlargs = NULL;
1832
1833 if (!self->r) {
1834 PyErr_SetString(PyExc_RuntimeError, "request object has expired");
1835 return NULL;
1836 }
1837
1838 if (!PyArg_ParseTuple(args, "|l:readlines", &hint))
1839 return NULL;
1840
1841 result = PyList_New(0);
1842 if (!result)
1843 return NULL;
1844
1845 rlargs = PyTuple_New(0);
1846 if (!rlargs) {
1847 Py_DECREF(result);
1848 return NULL;
1849 }
1850
1851 while (1) {
1852 long n;
1853
1854 if (!(line = Input_readline(self, rlargs))) {
1855 Py_DECREF(result);
1856 result = NULL;
1857 break;
1858 }
1859
1860 if ((n = PyString_Size(line)) == 0) {
1861 Py_DECREF(line);
1862 break;
1863 }
1864
1865 if (PyList_Append(result, line) == -1) {
1866 Py_DECREF(line);
1867 Py_DECREF(result);
1868 result = NULL;
1869 break;
1870 }
1871
1872 Py_DECREF(line);
1873
1874 length += n;
1875 if (hint > 0 && length >= hint)
1876 break;
1877 }
1878
1879 Py_DECREF(rlargs);
1880
1881 return result;
1882 }
1883
1884 static PyMethodDef Input_methods[] = {
1885 { "close", (PyCFunction)Input_close, METH_NOARGS, 0 },
1886 { "read", (PyCFunction)Input_read, METH_VARARGS, 0 },
1887 { "readline", (PyCFunction)Input_readline, METH_VARARGS, 0 },
1888 { "readlines", (PyCFunction)Input_readlines, METH_VARARGS, 0 },
1889 { NULL, NULL}
1890 };
1891
Input_iter(InputObject * self)1892 static PyObject *Input_iter(InputObject *self)
1893 {
1894 if (!self->r) {
1895 PyErr_SetString(PyExc_RuntimeError, "request object has expired");
1896 return NULL;
1897 }
1898
1899 Py_INCREF(self);
1900 return (PyObject *)self;
1901 }
1902
Input_iternext(InputObject * self)1903 static PyObject *Input_iternext(InputObject *self)
1904 {
1905 PyObject *line = NULL;
1906 PyObject *rlargs = NULL;
1907
1908 if (!self->r) {
1909 PyErr_SetString(PyExc_RuntimeError, "request object has expired");
1910 return NULL;
1911 }
1912
1913 rlargs = PyTuple_New(0);
1914
1915 if (!rlargs)
1916 return NULL;
1917
1918 line = Input_readline(self, rlargs);
1919
1920 Py_DECREF(rlargs);
1921
1922 if (!line)
1923 return NULL;
1924
1925 if (PyString_GET_SIZE(line) == 0) {
1926 PyErr_SetObject(PyExc_StopIteration, Py_None);
1927 Py_DECREF(line);
1928 return NULL;
1929 }
1930
1931 return line;
1932 }
1933
1934 static PyTypeObject Input_Type = {
1935 PyVarObject_HEAD_INIT(NULL, 0)
1936 "mod_wsgi.Input", /*tp_name*/
1937 sizeof(InputObject), /*tp_basicsize*/
1938 0, /*tp_itemsize*/
1939 /* methods */
1940 (destructor)Input_dealloc, /*tp_dealloc*/
1941 0, /*tp_print*/
1942 0, /*tp_getattr*/
1943 0, /*tp_setattr*/
1944 0, /*tp_compare*/
1945 0, /*tp_repr*/
1946 0, /*tp_as_number*/
1947 0, /*tp_as_sequence*/
1948 0, /*tp_as_mapping*/
1949 0, /*tp_hash*/
1950 0, /*tp_call*/
1951 0, /*tp_str*/
1952 0, /*tp_getattro*/
1953 0, /*tp_setattro*/
1954 0, /*tp_as_buffer*/
1955 #if defined(Py_TPFLAGS_HAVE_ITER)
1956 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, /*tp_flags*/
1957 #else
1958 Py_TPFLAGS_DEFAULT, /*tp_flags*/
1959 #endif
1960 0, /*tp_doc*/
1961 0, /*tp_traverse*/
1962 0, /*tp_clear*/
1963 0, /*tp_richcompare*/
1964 0, /*tp_weaklistoffset*/
1965 (getiterfunc)Input_iter, /*tp_iter*/
1966 (iternextfunc)Input_iternext, /*tp_iternext*/
1967 Input_methods, /*tp_methods*/
1968 0, /*tp_members*/
1969 0, /*tp_getset*/
1970 0, /*tp_base*/
1971 0, /*tp_dict*/
1972 0, /*tp_descr_get*/
1973 0, /*tp_descr_set*/
1974 0, /*tp_dictoffset*/
1975 0, /*tp_init*/
1976 0, /*tp_alloc*/
1977 0, /*tp_new*/
1978 0, /*tp_free*/
1979 0, /*tp_is_gc*/
1980 };
1981
1982 typedef struct {
1983 PyObject_HEAD
1984 int result;
1985 request_rec *r;
1986 apr_bucket_brigade *bb;
1987 WSGIRequestConfig *config;
1988 InputObject *input;
1989 PyObject *log_buffer;
1990 PyObject *log;
1991 int status;
1992 const char *status_line;
1993 PyObject *headers;
1994 PyObject *sequence;
1995 int content_length_set;
1996 apr_off_t content_length;
1997 apr_off_t output_length;
1998 apr_off_t output_writes;
1999 apr_time_t output_time;
2000 apr_time_t start_time;
2001 } AdapterObject;
2002
2003 static PyTypeObject Adapter_Type;
2004
newAdapterObject(request_rec * r)2005 static AdapterObject *newAdapterObject(request_rec *r)
2006 {
2007 AdapterObject *self;
2008
2009 self = PyObject_New(AdapterObject, &Adapter_Type);
2010 if (self == NULL)
2011 return NULL;
2012
2013 self->result = HTTP_INTERNAL_SERVER_ERROR;
2014
2015 self->r = r;
2016
2017 self->bb = NULL;
2018
2019 self->config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
2020 &wsgi_module);
2021
2022 self->status = HTTP_INTERNAL_SERVER_ERROR;
2023 self->status_line = NULL;
2024 self->headers = NULL;
2025 self->sequence = NULL;
2026
2027 self->content_length_set = 0;
2028 self->content_length = 0;
2029 self->output_length = 0;
2030 self->output_writes = 0;
2031
2032 self->output_time = 0;
2033
2034 self->input = newInputObject(r, self->config->ignore_activity);
2035
2036 self->log_buffer = newLogBufferObject(r, APLOG_ERR, "<wsgi.errors>", 0);
2037 self->log = newLogWrapperObject(self->log_buffer);
2038
2039 return self;
2040 }
2041
Adapter_dealloc(AdapterObject * self)2042 static void Adapter_dealloc(AdapterObject *self)
2043 {
2044 Py_XDECREF(self->headers);
2045 Py_XDECREF(self->sequence);
2046
2047 Py_DECREF(self->input);
2048
2049 Py_DECREF(self->log_buffer);
2050 Py_DECREF(self->log);
2051
2052 PyObject_Del(self);
2053 }
2054
Adapter_start_response(AdapterObject * self,PyObject * args)2055 static PyObject *Adapter_start_response(AdapterObject *self, PyObject *args)
2056 {
2057 PyObject *result = NULL;
2058
2059 PyObject *status_line = NULL;
2060 PyObject *headers = NULL;
2061 PyObject *exc_info = Py_None;
2062
2063 PyObject *status_line_as_bytes = NULL;
2064 PyObject *headers_as_bytes = NULL;
2065
2066 if (!self->r) {
2067 PyErr_SetString(PyExc_RuntimeError, "request object has expired");
2068 return NULL;
2069 }
2070
2071 if (!PyArg_ParseTuple(args, "OO!|O:start_response",
2072 &status_line, &PyList_Type, &headers, &exc_info)) {
2073 return NULL;
2074 }
2075
2076 if (exc_info != Py_None && !PyTuple_Check(exc_info)) {
2077 PyErr_SetString(PyExc_RuntimeError, "exception info must be a tuple");
2078 return NULL;
2079 }
2080
2081 if (exc_info != Py_None) {
2082 if (self->status_line && !self->headers) {
2083 PyObject *type = NULL;
2084 PyObject *value = NULL;
2085 PyObject *traceback = NULL;
2086
2087 if (!PyArg_ParseTuple(exc_info, "OOO", &type,
2088 &value, &traceback)) {
2089 return NULL;
2090 }
2091
2092 Py_INCREF(type);
2093 Py_INCREF(value);
2094 Py_INCREF(traceback);
2095
2096 PyErr_Restore(type, value, traceback);
2097
2098 return NULL;
2099 }
2100 }
2101 else if (self->status_line && !self->headers) {
2102 PyErr_SetString(PyExc_RuntimeError, "headers have already been sent");
2103 return NULL;
2104 }
2105
2106 /* Publish event for the start of the response. */
2107
2108 if (wsgi_event_subscribers()) {
2109 WSGIThreadInfo *thread_info;
2110
2111 PyObject *event = NULL;
2112 PyObject *value = NULL;
2113
2114 thread_info = wsgi_thread_info(0, 0);
2115
2116 event = PyDict_New();
2117
2118 #if AP_MODULE_MAGIC_AT_LEAST(20100923,2)
2119 if (self->r->log_id) {
2120 #if PY_MAJOR_VERSION >= 3
2121 value = PyUnicode_DecodeLatin1(self->r->log_id,
2122 strlen(self->r->log_id), NULL);
2123 #else
2124 value = PyString_FromString(self->r->log_id);
2125 #endif
2126 PyDict_SetItemString(event, "request_id", value);
2127 Py_DECREF(value);
2128 }
2129 #endif
2130
2131 PyDict_SetItemString(event, "response_status", status_line);
2132 PyDict_SetItemString(event, "response_headers", headers);
2133 PyDict_SetItemString(event, "exception_info", exc_info);
2134
2135 PyDict_SetItemString(event, "request_data", thread_info->request_data);
2136
2137 wsgi_publish_event("response_started", event);
2138
2139 Py_DECREF(event);
2140 }
2141
2142 status_line_as_bytes = wsgi_convert_status_line_to_bytes(status_line);
2143
2144 if (!status_line_as_bytes)
2145 goto finally;
2146
2147 headers_as_bytes = wsgi_convert_headers_to_bytes(headers);
2148
2149 if (!headers_as_bytes)
2150 goto finally;
2151
2152 self->status_line = apr_pstrdup(self->r->pool, PyString_AsString(
2153 status_line_as_bytes));
2154 self->status = (int)strtol(self->status_line, NULL, 10);
2155
2156 Py_XDECREF(self->headers);
2157 self->headers = headers_as_bytes;
2158 Py_INCREF(headers_as_bytes);
2159
2160 result = PyObject_GetAttrString((PyObject *)self, "write");
2161
2162 finally:
2163 Py_XDECREF(status_line_as_bytes);
2164 Py_XDECREF(headers_as_bytes);
2165
2166 return result;
2167 }
2168
Adapter_output(AdapterObject * self,const char * data,apr_off_t length,PyObject * string_object,int exception_when_aborted)2169 static int Adapter_output(AdapterObject *self, const char *data,
2170 apr_off_t length, PyObject *string_object,
2171 int exception_when_aborted)
2172 {
2173 int i = 0;
2174 apr_status_t rv;
2175 request_rec *r;
2176
2177 apr_time_t output_start = 0;
2178 apr_time_t output_finish = 0;
2179
2180 #if defined(MOD_WSGI_WITH_DAEMONS)
2181 if (wsgi_idle_timeout && !self->config->ignore_activity) {
2182 apr_thread_mutex_lock(wsgi_monitor_lock);
2183
2184 if (wsgi_idle_timeout) {
2185 wsgi_idle_shutdown_time = apr_time_now();
2186 wsgi_idle_shutdown_time += wsgi_idle_timeout;
2187 }
2188
2189 apr_thread_mutex_unlock(wsgi_monitor_lock);
2190 }
2191 #endif
2192
2193 if (!self->status_line) {
2194 PyErr_SetString(PyExc_RuntimeError, "response has not been started");
2195 return 0;
2196 }
2197
2198 r = self->r;
2199
2200 /* Remember we started sending this block of output. */
2201
2202 output_start = apr_time_now();
2203
2204 /* Count how many separate blocks have been output. */
2205
2206 if (string_object)
2207 self->output_writes++;
2208
2209 /* Have response headers yet been sent. */
2210
2211 if (self->headers) {
2212 /*
2213 * Apache prior to Apache 2.2.8 has a bug in it
2214 * whereby it doesn't force '100 Continue'
2215 * response before responding with headers if no
2216 * read. So, force a zero length read before
2217 * sending the headers if haven't yet attempted
2218 * to read anything. This will ensure that if no
2219 * request content has been read that any '100
2220 * Continue' response will be flushed and sent
2221 * back to the client if client was expecting
2222 * one. Only want to do this for 2xx and 3xx
2223 * status values. Note that even though Apple
2224 * supplied version of Apache on MacOS X Leopard
2225 * is newer than version 2.2.8, the header file
2226 * has never been patched when they make updates
2227 * and so anything compiled against it thinks it
2228 * is older.
2229 */
2230
2231 #if (AP_SERVER_MAJORVERSION_NUMBER == 2 && \
2232 AP_SERVER_MINORVERSION_NUMBER < 2) || \
2233 (AP_SERVER_MAJORVERSION_NUMBER == 2 && \
2234 AP_SERVER_MINORVERSION_NUMBER == 2 && \
2235 AP_SERVER_PATCHLEVEL_NUMBER < 8)
2236
2237 if (!self->input->init) {
2238 if (self->status >= 200 && self->status < 400) {
2239 PyObject *args = NULL;
2240 PyObject *result = NULL;
2241 args = Py_BuildValue("(i)", 0);
2242 result = Input_read(self->input, args);
2243 if (PyErr_Occurred())
2244 PyErr_Clear();
2245 Py_DECREF(args);
2246 Py_XDECREF(result);
2247 }
2248 }
2249
2250 #endif
2251
2252 /*
2253 * Now setup the response headers in request object. We
2254 * have already converted any native strings in the
2255 * headers to byte strings and validated the format of
2256 * the header names and values so can skip all the error
2257 * checking.
2258 */
2259
2260 r->status = self->status;
2261 r->status_line = self->status_line;
2262
2263 for (i = 0; i < PyList_Size(self->headers); i++) {
2264 PyObject *tuple = NULL;
2265
2266 PyObject *object1 = NULL;
2267 PyObject *object2 = NULL;
2268
2269 char *name = NULL;
2270 char *value = NULL;
2271
2272 tuple = PyList_GetItem(self->headers, i);
2273
2274 object1 = PyTuple_GetItem(tuple, 0);
2275 object2 = PyTuple_GetItem(tuple, 1);
2276
2277 name = PyBytes_AsString(object1);
2278 value = PyBytes_AsString(object2);
2279
2280 if (!strcasecmp(name, "Content-Type")) {
2281 /*
2282 * In a daemon child process we cannot call the
2283 * function ap_set_content_type() as want to
2284 * avoid adding any output filters based on the
2285 * type of file being served as this will be
2286 * done in the main Apache child process which
2287 * proxied the request to the daemon process.
2288 */
2289
2290 if (*self->config->process_group)
2291 r->content_type = apr_pstrdup(r->pool, value);
2292 else
2293 ap_set_content_type(r, apr_pstrdup(r->pool, value));
2294 }
2295 else if (!strcasecmp(name, "Content-Length")) {
2296 char *endstr;
2297 apr_off_t length;
2298
2299 if (wsgi_strtoff(&length, value, &endstr, 10)
2300 || *endstr || length < 0) {
2301
2302 PyErr_SetString(PyExc_ValueError,
2303 "invalid content length");
2304
2305 output_finish = apr_time_now();
2306
2307 if (output_finish > output_start)
2308 self->output_time += (output_finish - output_start);
2309
2310 return 0;
2311 }
2312
2313 ap_set_content_length(r, length);
2314
2315 self->content_length_set = 1;
2316 self->content_length = length;
2317 }
2318 else if (!strcasecmp(name, "WWW-Authenticate")) {
2319 apr_table_add(r->err_headers_out, name, value);
2320 }
2321 else {
2322 apr_table_add(r->headers_out, name, value);
2323 }
2324 }
2325
2326 /*
2327 * Reset flag indicating whether '100 Continue' response
2328 * expected. If we don't do this then if an attempt to read
2329 * input for the first time is after headers have been
2330 * sent, then Apache is wrongly generate the '100 Continue'
2331 * response into the response content. Not sure if this is
2332 * a bug in Apache, or that it truly believes that input
2333 * will never be read after the response headers have been
2334 * sent.
2335 */
2336
2337 r->expecting_100 = 0;
2338
2339 /* No longer need headers now that they have been sent. */
2340
2341 Py_DECREF(self->headers);
2342 self->headers = NULL;
2343 }
2344
2345 /*
2346 * If content length was specified, ensure that we don't
2347 * actually output more data than was specified as being
2348 * sent as otherwise technically in violation of HTTP RFC.
2349 */
2350
2351 if (length) {
2352 apr_off_t output_length = length;
2353
2354 if (self->content_length_set) {
2355 if (self->output_length < self->content_length) {
2356 if (self->output_length + length > self->content_length) {
2357 length = self->content_length - self->output_length;
2358 }
2359 }
2360 else
2361 length = 0;
2362 }
2363
2364 self->output_length += output_length;
2365 }
2366
2367 /* Now output any data. */
2368
2369 if (length) {
2370 apr_bucket *b;
2371
2372 /*
2373 * When using Apache 2.X can use lower level
2374 * bucket brigade APIs. This is preferred as
2375 * ap_rwrite()/ap_rflush() will grow memory in
2376 * the request pool on each call, which will
2377 * result in an increase in memory use over time
2378 * when streaming of data is being performed.
2379 * The memory is still reclaimed, but only at
2380 * the end of the request. Using bucket brigade
2381 * API avoids this, and also avoids any copying
2382 * of response data due to buffering performed
2383 * by ap_rwrite().
2384 */
2385
2386 if (r->connection->aborted) {
2387 if (!exception_when_aborted) {
2388 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, self->r,
2389 "mod_wsgi (pid=%d): Client closed connection.",
2390 getpid());
2391 }
2392 else
2393 PyErr_SetString(PyExc_IOError, "Apache/mod_wsgi client "
2394 "connection closed.");
2395
2396 output_finish = apr_time_now();
2397
2398 if (output_finish > output_start)
2399 self->output_time += (output_finish - output_start);
2400
2401 return 0;
2402 }
2403
2404 if (!self->bb) {
2405 self->bb = apr_brigade_create(r->pool,
2406 r->connection->bucket_alloc);
2407 }
2408
2409 #if 0
2410 if (string_object) {
2411 b = wsgi_apr_bucket_python_create(data, length,
2412 self->config->application_group, string_object,
2413 r->connection->bucket_alloc);
2414 }
2415 else {
2416 #endif
2417 b = apr_bucket_transient_create(data, (apr_size_t)length,
2418 r->connection->bucket_alloc);
2419 #if 0
2420 }
2421 #endif
2422
2423 APR_BRIGADE_INSERT_TAIL(self->bb, b);
2424
2425 b = apr_bucket_flush_create(r->connection->bucket_alloc);
2426 APR_BRIGADE_INSERT_TAIL(self->bb, b);
2427
2428 Py_BEGIN_ALLOW_THREADS
2429 rv = ap_pass_brigade(r->output_filters, self->bb);
2430 Py_END_ALLOW_THREADS
2431
2432 if (rv != APR_SUCCESS) {
2433 char status_buffer[512];
2434 const char *error_message;
2435
2436 if (!exception_when_aborted) {
2437 error_message = apr_psprintf(r->pool, "Failed to write "
2438 "response data: %s", apr_strerror(rv, status_buffer,
2439 sizeof(status_buffer)-1));
2440
2441 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, self->r,
2442 "mod_wsgi (pid=%d): %s.", getpid(),
2443 error_message);
2444 }
2445 else {
2446 error_message = apr_psprintf(r->pool, "Apache/mod_wsgi "
2447 "failed to write response data: %s",
2448 apr_strerror(rv, status_buffer,
2449 sizeof(status_buffer)-1));
2450
2451 PyErr_SetString(PyExc_IOError, error_message);
2452 }
2453
2454 output_finish = apr_time_now();
2455
2456 if (output_finish > output_start)
2457 self->output_time += (output_finish - output_start);
2458
2459 return 0;
2460 }
2461
2462 Py_BEGIN_ALLOW_THREADS
2463 apr_brigade_cleanup(self->bb);
2464 Py_END_ALLOW_THREADS
2465 }
2466
2467 /* Add how much time we spent send this block of output. */
2468
2469 output_finish = apr_time_now();
2470
2471 if (output_finish > output_start)
2472 self->output_time += (output_finish - output_start);
2473
2474 /*
2475 * Check whether aborted connection was found when data
2476 * being written, otherwise will not be flagged until next
2477 * time that data is being written. Early detection is
2478 * better as it may have been the last data block being
2479 * written and application may think that data has all
2480 * been written. In a streaming application, we also want
2481 * to avoid any additional data processing to generate any
2482 * successive data.
2483 */
2484
2485 if (r->connection->aborted) {
2486 if (!exception_when_aborted) {
2487 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, self->r,
2488 "mod_wsgi (pid=%d): Client closed connection.",
2489 getpid());
2490 }
2491 else
2492 PyErr_SetString(PyExc_IOError, "Apache/mod_wsgi client "
2493 "connection closed.");
2494
2495 return 0;
2496 }
2497
2498 return 1;
2499 }
2500
2501 /* Split buckets at 1GB when sending large files. */
2502
2503 #define MAX_BUCKET_SIZE (0x40000000)
2504
Adapter_output_file(AdapterObject * self,apr_file_t * tmpfile,apr_off_t offset,apr_off_t len)2505 static int Adapter_output_file(AdapterObject *self, apr_file_t* tmpfile,
2506 apr_off_t offset, apr_off_t len)
2507 {
2508 request_rec *r;
2509 apr_bucket *b;
2510 apr_status_t rv;
2511 apr_bucket_brigade *bb;
2512
2513 apr_file_t* dupfile = NULL;
2514
2515 r = self->r;
2516
2517 if (r->connection->aborted) {
2518 PyErr_SetString(PyExc_IOError, "Apache/mod_wsgi client "
2519 "connection closed.");
2520 return 0;
2521 }
2522
2523 if (len == 0)
2524 return 1;
2525
2526 bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
2527
2528 apr_file_dup(&dupfile, tmpfile, r->pool);
2529
2530 if (sizeof(apr_off_t) == sizeof(apr_size_t) || len < MAX_BUCKET_SIZE) {
2531 /* Can use a single bucket to send file. */
2532
2533 #if 0
2534 b = apr_bucket_file_create(tmpfile, offset, (apr_size_t)len, r->pool,
2535 r->connection->bucket_alloc);
2536 #endif
2537 b = apr_bucket_file_create(dupfile, offset, (apr_size_t)len, r->pool,
2538 r->connection->bucket_alloc);
2539 }
2540 else {
2541 /* Need to create multiple buckets to send file. */
2542
2543 #if 0
2544 b = apr_bucket_file_create(tmpfile, offset, MAX_BUCKET_SIZE, r->pool,
2545 r->connection->bucket_alloc);
2546 #endif
2547 b = apr_bucket_file_create(dupfile, offset, MAX_BUCKET_SIZE, r->pool,
2548 r->connection->bucket_alloc);
2549
2550 while (len > MAX_BUCKET_SIZE) {
2551 apr_bucket *cb;
2552 apr_bucket_copy(b, &cb);
2553 APR_BRIGADE_INSERT_TAIL(bb, cb);
2554 b->start += MAX_BUCKET_SIZE;
2555 len -= MAX_BUCKET_SIZE;
2556 }
2557
2558 /* Resize just the last bucket */
2559
2560 b->length = (apr_size_t)len;
2561 }
2562
2563 APR_BRIGADE_INSERT_TAIL(bb, b);
2564
2565 b = apr_bucket_flush_create(r->connection->bucket_alloc);
2566 APR_BRIGADE_INSERT_TAIL(bb, b);
2567
2568 b = apr_bucket_eos_create(r->connection->bucket_alloc);
2569 APR_BRIGADE_INSERT_TAIL(bb, b);
2570
2571 Py_BEGIN_ALLOW_THREADS
2572 rv = ap_pass_brigade(r->output_filters, bb);
2573 Py_END_ALLOW_THREADS
2574
2575 if (rv != APR_SUCCESS) {
2576 char status_buffer[512];
2577 const char *error_message;
2578
2579 error_message = apr_psprintf(r->pool, "Apache/mod_wsgi failed "
2580 "to write response data: %s.", apr_strerror(rv,
2581 status_buffer, sizeof(status_buffer)-1));
2582
2583 PyErr_SetString(PyExc_IOError, error_message);
2584 return 0;
2585 }
2586
2587 Py_BEGIN_ALLOW_THREADS
2588 apr_brigade_destroy(bb);
2589 Py_END_ALLOW_THREADS
2590
2591 if (r->connection->aborted) {
2592 PyErr_SetString(PyExc_IOError, "Apache/mod_wsgi client connection "
2593 "closed.");
2594 return 0;
2595 }
2596
2597 return 1;
2598 }
2599
2600 static APR_OPTIONAL_FN_TYPE(ssl_is_https) *wsgi_is_https = NULL;
2601
Adapter_environ(AdapterObject * self)2602 static PyObject *Adapter_environ(AdapterObject *self)
2603 {
2604 request_rec *r = NULL;
2605
2606 PyObject *vars = NULL;
2607 PyObject *object = NULL;
2608
2609 const apr_array_header_t *head = NULL;
2610 const apr_table_entry_t *elts = NULL;
2611
2612 int i = 0;
2613
2614 const char *scheme = NULL;
2615
2616 /* Create the WSGI environment dictionary. */
2617
2618 vars = PyDict_New();
2619
2620 /* Merge the CGI environment into the WSGI environment. */
2621
2622 r = self->r;
2623
2624 head = apr_table_elts(r->subprocess_env);
2625 elts = (apr_table_entry_t *)head->elts;
2626
2627 for (i = 0; i < head->nelts; ++i) {
2628 if (elts[i].key) {
2629 if (elts[i].val) {
2630 #if PY_MAJOR_VERSION >= 3
2631 if (!strcmp(elts[i].val, "DOCUMENT_ROOT")) {
2632 object = PyUnicode_Decode(elts[i].val, strlen(elts[i].val),
2633 Py_FileSystemDefaultEncoding,
2634 "surrogateescape");
2635 }
2636 else if (!strcmp(elts[i].val, "SCRIPT_FILENAME")) {
2637 object = PyUnicode_Decode(elts[i].val, strlen(elts[i].val),
2638 Py_FileSystemDefaultEncoding,
2639 "surrogateescape");
2640 }
2641 else {
2642 object = PyUnicode_DecodeLatin1(elts[i].val,
2643 strlen(elts[i].val), NULL);
2644 }
2645 #else
2646 object = PyString_FromString(elts[i].val);
2647 #endif
2648 PyDict_SetItemString(vars, elts[i].key, object);
2649 Py_DECREF(object);
2650 }
2651 else
2652 PyDict_SetItemString(vars, elts[i].key, Py_None);
2653 }
2654 }
2655
2656 PyDict_DelItemString(vars, "PATH");
2657
2658 /* Now setup all the WSGI specific environment values. */
2659
2660 object = Py_BuildValue("(ii)", 1, 0);
2661 PyDict_SetItemString(vars, "wsgi.version", object);
2662 Py_DECREF(object);
2663
2664 object = PyBool_FromLong(wsgi_multithread);
2665 PyDict_SetItemString(vars, "wsgi.multithread", object);
2666 Py_DECREF(object);
2667
2668 object = PyBool_FromLong(wsgi_multiprocess);
2669 PyDict_SetItemString(vars, "wsgi.multiprocess", object);
2670 Py_DECREF(object);
2671
2672 #if defined(MOD_WSGI_WITH_DAEMONS)
2673 if (wsgi_daemon_process) {
2674 if (wsgi_daemon_process->group->threads == 1 &&
2675 wsgi_daemon_process->group->maximum_requests == 1) {
2676 PyDict_SetItemString(vars, "wsgi.run_once", Py_True);
2677 }
2678 else
2679 PyDict_SetItemString(vars, "wsgi.run_once", Py_False);
2680 }
2681 else
2682 PyDict_SetItemString(vars, "wsgi.run_once", Py_False);
2683 #else
2684 PyDict_SetItemString(vars, "wsgi.run_once", Py_False);
2685 #endif
2686
2687 scheme = apr_table_get(r->subprocess_env, "HTTPS");
2688
2689 if (scheme && (!strcasecmp(scheme, "On") || !strcmp(scheme, "1"))) {
2690 #if PY_MAJOR_VERSION >= 3
2691 object = PyUnicode_FromString("https");
2692 #else
2693 object = PyString_FromString("https");
2694 #endif
2695 PyDict_SetItemString(vars, "wsgi.url_scheme", object);
2696 Py_DECREF(object);
2697 }
2698 else {
2699 #if PY_MAJOR_VERSION >= 3
2700 object = PyUnicode_FromString("http");
2701 #else
2702 object = PyString_FromString("http");
2703 #endif
2704 PyDict_SetItemString(vars, "wsgi.url_scheme", object);
2705 Py_DECREF(object);
2706 }
2707
2708 /*
2709 * We remove the HTTPS variable because WSGI compliant
2710 * applications shouldn't rely on it. Instead they should
2711 * use wsgi.url_scheme. We do this even if SetEnv was
2712 * used to set HTTPS from Apache configuration. That is
2713 * we convert it into the correct variable and remove the
2714 * original.
2715 */
2716
2717 if (scheme)
2718 PyDict_DelItemString(vars, "HTTPS");
2719
2720 /*
2721 * Setup log object for WSGI errors. Don't decrement
2722 * reference to log object as keep reference to it.
2723 */
2724
2725 object = (PyObject *)self->log;
2726 PyDict_SetItemString(vars, "wsgi.errors", object);
2727
2728 /* Setup input object for request content. */
2729
2730 object = (PyObject *)self->input;
2731 PyDict_SetItemString(vars, "wsgi.input", object);
2732
2733 PyDict_SetItemString(vars, "wsgi.input_terminated", Py_True);
2734
2735 /* Setup file wrapper object for efficient file responses. */
2736
2737 PyDict_SetItemString(vars, "wsgi.file_wrapper", (PyObject *)&Stream_Type);
2738
2739 /* Add Apache and mod_wsgi version information. */
2740
2741 object = Py_BuildValue("(iii)", AP_SERVER_MAJORVERSION_NUMBER,
2742 AP_SERVER_MINORVERSION_NUMBER,
2743 AP_SERVER_PATCHLEVEL_NUMBER);
2744 PyDict_SetItemString(vars, "apache.version", object);
2745 Py_DECREF(object);
2746
2747 object = Py_BuildValue("(iii)", MOD_WSGI_MAJORVERSION_NUMBER,
2748 MOD_WSGI_MINORVERSION_NUMBER,
2749 MOD_WSGI_MICROVERSION_NUMBER);
2750 PyDict_SetItemString(vars, "mod_wsgi.version", object);
2751 Py_DECREF(object);
2752
2753 /*
2754 * If Apache extensions are enabled and running in embedded
2755 * mode add a CObject reference to the Apache request_rec
2756 * structure instance.
2757 */
2758
2759 if (!wsgi_daemon_pool && self->config->pass_apache_request) {
2760 #if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 2) || \
2761 (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 7)
2762 object = PyCapsule_New(self->r, 0, 0);
2763 #else
2764 object = PyCObject_FromVoidPtr(self->r, 0);
2765 #endif
2766 PyDict_SetItemString(vars, "apache.request_rec", object);
2767 Py_DECREF(object);
2768 }
2769
2770 /*
2771 * Extensions for accessing SSL certificate information from
2772 * mod_ssl when in use.
2773 */
2774
2775 #if 0
2776 if (!wsgi_daemon_pool) {
2777 object = PyObject_GetAttrString((PyObject *)self, "ssl_is_https");
2778 PyDict_SetItemString(vars, "mod_ssl.is_https", object);
2779 Py_DECREF(object);
2780
2781 object = PyObject_GetAttrString((PyObject *)self, "ssl_var_lookup");
2782 PyDict_SetItemString(vars, "mod_ssl.var_lookup", object);
2783 Py_DECREF(object);
2784 }
2785 #endif
2786
2787 return vars;
2788 }
2789
Adapter_process_file_wrapper(AdapterObject * self)2790 static int Adapter_process_file_wrapper(AdapterObject *self)
2791 {
2792 int done = 0;
2793
2794 #ifndef WIN32
2795 PyObject *filelike = NULL;
2796 PyObject *method = NULL;
2797 PyObject *object = NULL;
2798
2799 apr_status_t rv = 0;
2800
2801 apr_os_file_t fd = -1;
2802 apr_file_t *tmpfile = NULL;
2803 apr_finfo_t finfo;
2804
2805 apr_off_t fd_offset = 0;
2806 apr_off_t fo_offset = 0;
2807
2808 apr_off_t length = 0;
2809
2810 /* Perform file wrapper optimisations where possible. */
2811
2812 if (!PyObject_IsInstance(self->sequence, (PyObject *)&Stream_Type))
2813 return 0;
2814
2815 /*
2816 * Only attempt to perform optimisations if the
2817 * write() function returned by start_response()
2818 * function has not been called with non zero length
2819 * data. In other words if no prior response content
2820 * generated. Technically it could be done, but want
2821 * to have a consistent rule about how specifying a
2822 * content length affects how much of a file is
2823 * sent. Don't want to have to take into
2824 * consideration whether write() function has been
2825 * called or not as just complicates things.
2826 */
2827
2828 if (self->output_length != 0)
2829 return 0;
2830
2831 /*
2832 * Work out if file wrapper is associated with a
2833 * file like object, where that file object is
2834 * associated with a regular file. If it does then
2835 * we can optimise how the contents of the file are
2836 * sent out. If no such associated file descriptor
2837 * then it needs to be processed like any other
2838 * iterable value.
2839 */
2840
2841
2842 filelike = PyObject_GetAttrString((PyObject *)self->sequence, "filelike");
2843
2844 if (!filelike) {
2845 PyErr_SetString(PyExc_KeyError,
2846 "file wrapper no filelike attribute");
2847 return 0;
2848 }
2849
2850 fd = PyObject_AsFileDescriptor(filelike);
2851 if (fd == -1) {
2852 PyErr_Clear();
2853 Py_DECREF(filelike);
2854 return 0;
2855 }
2856
2857 Py_DECREF(filelike);
2858
2859 /*
2860 * On some platforms, such as Linux, sendfile() system call
2861 * will not work on UNIX sockets. Thus when using daemon mode
2862 * cannot enable that feature.
2863 */
2864
2865 if (self->config->enable_sendfile)
2866 apr_os_file_put(&tmpfile, &fd, APR_SENDFILE_ENABLED, self->r->pool);
2867 else
2868 apr_os_file_put(&tmpfile, &fd, 0, self->r->pool);
2869
2870 rv = apr_file_info_get(&finfo, APR_FINFO_SIZE|APR_FINFO_TYPE, tmpfile);
2871 if (rv != APR_SUCCESS || finfo.filetype != APR_REG)
2872 return 0;
2873
2874 /*
2875 * Because Python file like objects potentially have
2876 * their own buffering layering, or use an operating
2877 * system FILE object which also has a buffering
2878 * layer on top of a normal file descriptor, need to
2879 * determine from the file like object its position
2880 * within the file and use that as starting position.
2881 * Note that it is assumed that user had flushed any
2882 * modifications to the file as necessary. Also, we
2883 * need to make sure we remember the original file
2884 * descriptor position as will need to restore that
2885 * position so it matches the upper buffering layers
2886 * when done. This is done to avoid any potential
2887 * problems if file like object does anything strange
2888 * in its close() method which relies on file position
2889 * being what it thought it should be.
2890 */
2891
2892 rv = apr_file_seek(tmpfile, APR_CUR, &fd_offset);
2893 if (rv != APR_SUCCESS)
2894 return 0;
2895
2896 method = PyObject_GetAttrString(filelike, "tell");
2897 if (!method)
2898 return 0;
2899
2900 object = PyEval_CallObject(method, NULL);
2901 Py_DECREF(method);
2902
2903 if (!object) {
2904 PyErr_Clear();
2905 return 0;
2906 }
2907
2908 if (PyLong_Check(object)) {
2909 #if defined(HAVE_LONG_LONG)
2910 fo_offset = PyLong_AsLongLong(object);
2911 #else
2912 fo_offset = PyLong_AsLong(object);
2913 #endif
2914 }
2915 #if PY_MAJOR_VERSION < 3
2916 else if (PyInt_Check(object)) {
2917 fo_offset = PyInt_AsLong(object);
2918 }
2919 #endif
2920 else {
2921 Py_DECREF(object);
2922 return 0;
2923 }
2924
2925 if (PyErr_Occurred()){
2926 Py_DECREF(object);
2927 PyErr_Clear();
2928 return 0;
2929 }
2930
2931 Py_DECREF(object);
2932
2933 /*
2934 * For a file wrapper object need to always ensure
2935 * that response headers are parsed. This is done so
2936 * that if the content length header has been
2937 * defined we can get its value and use it to limit
2938 * how much of a file is being sent. The WSGI 1.0
2939 * specification says that we are meant to send all
2940 * available bytes from the file, however this is
2941 * questionable as sending more than content length
2942 * would violate HTTP RFC. Note that this doesn't
2943 * actually flush the headers out when using Apache
2944 * 2.X. This is good, as we want to still be able to
2945 * set the content length header if none set and file
2946 * is seekable. If processing response headers fails,
2947 * then need to return as if done, with error being
2948 * logged later.
2949 */
2950
2951 if (!Adapter_output(self, "", 0, NULL, 0))
2952 return 1;
2953
2954 /*
2955 * If content length wasn't defined then determine
2956 * the amount of data which is available to send and
2957 * set the content length response header. Either
2958 * way, if can work out length then send data
2959 * otherwise fall through and treat it as normal
2960 * iterable.
2961 */
2962
2963 if (!self->content_length_set) {
2964 length = finfo.size - fo_offset;
2965 self->output_length += length;
2966
2967 ap_set_content_length(self->r, length);
2968
2969 self->content_length_set = 1;
2970 self->content_length = length;
2971
2972 if (Adapter_output_file(self, tmpfile, fo_offset, length))
2973 self->result = OK;
2974
2975 done = 1;
2976 }
2977 else {
2978 length = finfo.size - fo_offset;
2979 self->output_length += length;
2980
2981 /* Use user specified content length instead. */
2982
2983 length = self->content_length;
2984
2985 if (Adapter_output_file(self, tmpfile, fo_offset, length))
2986 self->result = OK;
2987
2988 done = 1;
2989 }
2990
2991 /*
2992 * Restore position of underlying file descriptor.
2993 * If this fails, then not much we can do about it.
2994 */
2995
2996 apr_file_seek(tmpfile, APR_SET, &fd_offset);
2997
2998 #endif
2999
3000 return done;
3001 }
3002
Adapter_run(AdapterObject * self,PyObject * object)3003 static int Adapter_run(AdapterObject *self, PyObject *object)
3004 {
3005 PyObject *vars = NULL;
3006 PyObject *start = NULL;
3007 PyObject *args = NULL;
3008 PyObject *iterator = NULL;
3009 PyObject *close = NULL;
3010
3011 PyObject *nrwrapper = NULL;
3012 PyObject *evwrapper = NULL;
3013
3014 PyObject *value = NULL;
3015 PyObject *event = NULL;
3016
3017 const char *msg = NULL;
3018 apr_off_t length = 0;
3019
3020 WSGIThreadInfo *thread_handle = NULL;
3021
3022 apr_time_t finish_time;
3023
3024 WSGIThreadCPUUsage start_usage;
3025 WSGIThreadCPUUsage end_usage;
3026
3027 int aborted = 0;
3028
3029 #if defined(MOD_WSGI_WITH_DAEMONS)
3030 if (wsgi_idle_timeout && !self->config->ignore_activity) {
3031 apr_thread_mutex_lock(wsgi_monitor_lock);
3032
3033 if (wsgi_idle_timeout) {
3034 wsgi_idle_shutdown_time = apr_time_now();
3035 wsgi_idle_shutdown_time += wsgi_idle_timeout;
3036 }
3037
3038 apr_thread_mutex_unlock(wsgi_monitor_lock);
3039 }
3040 #endif
3041
3042 if (wsgi_newrelic_config_file) {
3043 PyObject *module = NULL;
3044
3045 module = PyImport_ImportModule("newrelic.agent");
3046
3047 if (module) {
3048 PyObject *dict;
3049 PyObject *factory;
3050
3051 dict = PyModule_GetDict(module);
3052 factory = PyDict_GetItemString(dict, "WSGIApplicationWrapper");
3053
3054 if (factory) {
3055 Py_INCREF(factory);
3056
3057 nrwrapper = PyObject_CallFunctionObjArgs(
3058 factory, object, Py_None, NULL);
3059
3060 if (!nrwrapper) {
3061 wsgi_log_python_error(self->r, self->log,
3062 self->r->filename, 0);
3063 PyErr_Clear();
3064 }
3065
3066 Py_DECREF(factory);
3067 }
3068
3069 Py_DECREF(module);
3070 }
3071 }
3072
3073 if (nrwrapper)
3074 object = nrwrapper;
3075
3076 self->start_time = apr_time_now();
3077
3078 apr_table_setn(self->r->subprocess_env, "mod_wsgi.script_start",
3079 apr_psprintf(self->r->pool, "%" APR_TIME_T_FMT,
3080 self->start_time));
3081
3082 vars = Adapter_environ(self);
3083
3084 value = wsgi_PyInt_FromLongLong(wsgi_total_requests);
3085 PyDict_SetItemString(vars, "mod_wsgi.total_requests", value);
3086 Py_DECREF(value);
3087
3088 thread_handle = wsgi_thread_info(1, 1);
3089
3090 value = wsgi_PyInt_FromLong(thread_handle->thread_id);
3091 PyDict_SetItemString(vars, "mod_wsgi.thread_id", value);
3092 Py_DECREF(value);
3093
3094 value = wsgi_PyInt_FromLongLong(thread_handle->request_count);
3095 PyDict_SetItemString(vars, "mod_wsgi.thread_requests", value);
3096 Py_DECREF(value);
3097
3098 /* Publish event for the start of the request. */
3099
3100 start_usage.user_time = 0.0;
3101 start_usage.system_time = 0.0;
3102
3103 if (wsgi_event_subscribers()) {
3104 wsgi_thread_cpu_usage(&start_usage);
3105
3106 event = PyDict_New();
3107
3108 #if AP_MODULE_MAGIC_AT_LEAST(20100923,2)
3109 if (self->r->log_id) {
3110 #if PY_MAJOR_VERSION >= 3
3111 value = PyUnicode_DecodeLatin1(self->r->log_id,
3112 strlen(self->r->log_id), NULL);
3113 #else
3114 value = PyString_FromString(self->r->log_id);
3115 #endif
3116 PyDict_SetItemString(event, "request_id", value);
3117 Py_DECREF(value);
3118 }
3119 #endif
3120
3121 value = wsgi_PyInt_FromLong(thread_handle->thread_id);
3122 PyDict_SetItemString(event, "thread_id", value);
3123 Py_DECREF(value);
3124
3125 value = wsgi_PyInt_FromLong(self->config->daemon_connects);
3126 PyDict_SetItemString(event, "daemon_connects", value);
3127 Py_DECREF(value);
3128
3129 value = wsgi_PyInt_FromLong(self->config->daemon_restarts);
3130 PyDict_SetItemString(event, "daemon_restarts", value);
3131 Py_DECREF(value);
3132
3133 value = PyFloat_FromDouble(apr_time_sec(
3134 (double)self->config->request_start));
3135 PyDict_SetItemString(event, "request_start", value);
3136 Py_DECREF(value);
3137
3138 value = PyFloat_FromDouble(apr_time_sec(
3139 (double)self->config->queue_start));
3140 PyDict_SetItemString(event, "queue_start", value);
3141 Py_DECREF(value);
3142
3143 value = PyFloat_FromDouble(apr_time_sec(
3144 (double)self->config->daemon_start));
3145 PyDict_SetItemString(event, "daemon_start", value);
3146 Py_DECREF(value);
3147
3148 PyDict_SetItemString(event, "application_object", object);
3149
3150 PyDict_SetItemString(event, "request_environ", vars);
3151
3152 value = PyFloat_FromDouble(apr_time_sec((double)self->start_time));
3153 PyDict_SetItemString(event, "application_start", value);
3154 Py_DECREF(value);
3155
3156 PyDict_SetItemString(event, "request_data", thread_handle->request_data);
3157
3158 wsgi_publish_event("request_started", event);
3159
3160 evwrapper = PyDict_GetItemString(event, "application_object");
3161
3162 if (evwrapper) {
3163 if (evwrapper != object) {
3164 Py_INCREF(evwrapper);
3165 object = evwrapper;
3166 }
3167 else
3168 evwrapper = NULL;
3169 }
3170
3171 Py_DECREF(event);
3172 }
3173
3174 /* Pass the request through to the WSGI application. */
3175
3176 thread_handle->request_count++;
3177
3178 start = PyObject_GetAttrString((PyObject *)self, "start_response");
3179
3180 args = Py_BuildValue("(OO)", vars, start);
3181
3182 self->sequence = PyEval_CallObject(object, args);
3183
3184 if (self->sequence != NULL) {
3185 if (!Adapter_process_file_wrapper(self)) {
3186 iterator = PyObject_GetIter(self->sequence);
3187
3188 if (iterator != NULL) {
3189 PyObject *item = NULL;
3190
3191 while ((item = PyIter_Next(iterator))) {
3192 if (!PyString_Check(item)) {
3193 PyErr_Format(PyExc_TypeError, "sequence of byte "
3194 "string values expected, value of "
3195 "type %.200s found",
3196 item->ob_type->tp_name);
3197 Py_DECREF(item);
3198 break;
3199 }
3200
3201 msg = PyString_AsString(item);
3202 length = PyString_Size(item);
3203
3204 if (!msg) {
3205 Py_DECREF(item);
3206 break;
3207 }
3208
3209 if (length && !Adapter_output(self, msg, length,
3210 item, 0)) {
3211 if (!PyErr_Occurred())
3212 aborted = 1;
3213 Py_DECREF(item);
3214 break;
3215 }
3216
3217 Py_DECREF(item);
3218 }
3219 }
3220
3221 if (!PyErr_Occurred()) {
3222 if (!aborted) {
3223 /*
3224 * In the case where the response was empty we
3225 * need to ensure we explicitly flush out the
3226 * headers. This is done by calling the output
3227 * routine but with an empty string as content.
3228 * This could be gated on whether any content
3229 * had already been sent, but easier to just call
3230 * it all the time.
3231 */
3232
3233 if (Adapter_output(self, "", 0, NULL, 0))
3234 self->result = OK;
3235 }
3236 else {
3237 /*
3238 * If the client connection was already marked
3239 * as aborted, then it indicates the client has
3240 * closed the connection. In this case mark the
3241 * final result as okay rather than an error so
3242 * that the access log still records the original
3243 * HTTP response code for the request rather than
3244 * overriding it. If don't do this then access
3245 * log will show 500 when the WSGI application
3246 * itself had run fine.
3247 */
3248
3249 self->result = OK;
3250 }
3251 }
3252
3253 Py_XDECREF(iterator);
3254 }
3255
3256 /*
3257 * Log warning if more response content generated than was
3258 * indicated, or less, if there was no errors generated by
3259 * the application and connection wasn't aborted.
3260 */
3261
3262 if (self->content_length_set && ((!PyErr_Occurred() && !aborted &&
3263 self->output_length != self->content_length) ||
3264 (self->output_length > self->content_length))) {
3265 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, self->r,
3266 "mod_wsgi (pid=%d): Content length mismatch, "
3267 "expected %s, response generated %s: %s", getpid(),
3268 apr_off_t_toa(self->r->pool, self->content_length),
3269 apr_off_t_toa(self->r->pool, self->output_length),
3270 self->r->filename);
3271 }
3272
3273 if (PyErr_Occurred()) {
3274 /*
3275 * Response content has already been sent, so cannot
3276 * return an internal server error as Apache will
3277 * append its own error page. Thus need to return OK
3278 * and just truncate the response.
3279 */
3280
3281 if (self->status_line && !self->headers)
3282 self->result = OK;
3283
3284 wsgi_log_python_error(self->r, self->log, self->r->filename, 1);
3285
3286 /*
3287 * If response content is being chunked and an error
3288 * occurred, we need to prevent the sending of the EOS
3289 * bucket so a client is able to detect that the the
3290 * response was incomplete.
3291 */
3292
3293 if (self->r->chunked)
3294 self->r->eos_sent = 1;
3295 }
3296
3297 if (PyObject_HasAttrString(self->sequence, "close")) {
3298 PyObject *args = NULL;
3299 PyObject *data = NULL;
3300
3301 close = PyObject_GetAttrString(self->sequence, "close");
3302
3303 args = Py_BuildValue("()");
3304 data = PyEval_CallObject(close, args);
3305
3306 Py_DECREF(args);
3307 Py_XDECREF(data);
3308 Py_DECREF(close);
3309 }
3310
3311 if (PyErr_Occurred())
3312 wsgi_log_python_error(self->r, self->log, self->r->filename, 1);
3313 }
3314 else
3315 wsgi_log_python_error(self->r, self->log, self->r->filename, 1);
3316
3317 /* Publish event for the end of the request. */
3318
3319 if (wsgi_event_subscribers()) {
3320 double application_time = 0.0;
3321 double output_time = 0.0;
3322
3323 event = PyDict_New();
3324
3325 #if AP_MODULE_MAGIC_AT_LEAST(20100923,2)
3326 if (self->r->log_id) {
3327 #if PY_MAJOR_VERSION >= 3
3328 value = PyUnicode_DecodeLatin1(self->r->log_id,
3329 strlen(self->r->log_id), NULL);
3330 #else
3331 value = PyString_FromString(self->r->log_id);
3332 #endif
3333 PyDict_SetItemString(event, "request_id", value);
3334 Py_DECREF(value);
3335 }
3336 #endif
3337
3338 value = wsgi_PyInt_FromLongLong(self->input->reads);
3339 PyDict_SetItemString(event, "input_reads", value);
3340 Py_DECREF(value);
3341
3342 value = wsgi_PyInt_FromLongLong(self->input->bytes);
3343 PyDict_SetItemString(event, "input_length", value);
3344 Py_DECREF(value);
3345
3346 value = PyFloat_FromDouble(apr_time_sec((double)self->input->time));
3347 PyDict_SetItemString(event, "input_time", value);
3348 Py_DECREF(value);
3349
3350 value = wsgi_PyInt_FromLongLong(self->output_length);
3351 PyDict_SetItemString(event, "output_length", value);
3352 Py_DECREF(value);
3353
3354 value = wsgi_PyInt_FromLongLong(self->output_writes);
3355 PyDict_SetItemString(event, "output_writes", value);
3356 Py_DECREF(value);
3357
3358 output_time = apr_time_sec((double)self->output_time);
3359
3360 if (output_time < 0.0)
3361 output_time = 0.0;
3362
3363 finish_time = apr_time_now();
3364
3365 application_time = apr_time_sec((double)finish_time-self->start_time);
3366
3367 if (application_time < 0.0)
3368 application_time = 0.0;
3369
3370 if (start_usage.user_time != 0.0) {
3371 if (wsgi_thread_cpu_usage(&end_usage)) {
3372 double user_seconds;
3373 double system_seconds;
3374 double total_seconds;
3375
3376 user_seconds = end_usage.user_time;
3377 user_seconds -= start_usage.user_time;
3378
3379 if (user_seconds < 0.0)
3380 user_seconds = 0.0;
3381
3382 system_seconds = end_usage.system_time;
3383 system_seconds -= start_usage.system_time;
3384
3385 if (system_seconds < 0.0)
3386 system_seconds = 0.0;
3387
3388 total_seconds = user_seconds + system_seconds;
3389
3390 if (total_seconds && total_seconds > application_time) {
3391 user_seconds = (user_seconds/total_seconds)*application_time;
3392 system_seconds = application_time - user_seconds;
3393 }
3394
3395 value = PyFloat_FromDouble(user_seconds);
3396 PyDict_SetItemString(event, "cpu_user_time", value);
3397 Py_DECREF(value);
3398
3399 value = PyFloat_FromDouble(system_seconds);
3400 PyDict_SetItemString(event, "cpu_system_time", value);
3401 Py_DECREF(value);
3402 }
3403 }
3404
3405 value = PyFloat_FromDouble(output_time);
3406 PyDict_SetItemString(event, "output_time", value);
3407 Py_DECREF(value);
3408
3409 value = PyFloat_FromDouble(apr_time_sec((double)finish_time));
3410 PyDict_SetItemString(event, "application_finish", value);
3411 Py_DECREF(value);
3412
3413 value = PyFloat_FromDouble(application_time);
3414 PyDict_SetItemString(event, "application_time", value);
3415 Py_DECREF(value);
3416
3417 PyDict_SetItemString(event, "request_data", thread_handle->request_data);
3418
3419 wsgi_publish_event("request_finished", event);
3420
3421 Py_DECREF(event);
3422 }
3423
3424 /*
3425 * If result indicates an internal server error, then
3426 * replace the status line in the request object else
3427 * that provided by the application will be what is used
3428 * in any error page automatically generated by Apache.
3429 */
3430
3431 if (self->result == HTTP_INTERNAL_SERVER_ERROR)
3432 self->r->status_line = "500 Internal Server Error";
3433
3434 Py_DECREF(args);
3435 Py_DECREF(start);
3436 Py_DECREF(vars);
3437
3438 Py_XDECREF(nrwrapper);
3439 Py_XDECREF(evwrapper);
3440
3441 Py_XDECREF(self->sequence);
3442 self->sequence = NULL;
3443
3444 return self->result;
3445 }
3446
Adapter_write(AdapterObject * self,PyObject * args)3447 static PyObject *Adapter_write(AdapterObject *self, PyObject *args)
3448 {
3449 PyObject *item = NULL;
3450 const char *data = NULL;
3451 long length = 0;
3452
3453 if (!self->r) {
3454 PyErr_SetString(PyExc_RuntimeError, "request object has expired");
3455 return NULL;
3456 }
3457
3458 if (!PyArg_ParseTuple(args, "O:write", &item))
3459 return NULL;
3460
3461 if (!PyString_Check(item)) {
3462 PyErr_Format(PyExc_TypeError, "byte string value expected, value "
3463 "of type %.200s found", item->ob_type->tp_name);
3464 return NULL;
3465 }
3466
3467 data = PyString_AsString(item);
3468 length = PyString_Size(item);
3469
3470 if (!Adapter_output(self, data, length, item, 1)) {
3471 return NULL;
3472 }
3473
3474 Py_INCREF(Py_None);
3475 return Py_None;
3476 }
3477
Adapter_ssl_is_https(AdapterObject * self,PyObject * args)3478 static PyObject *Adapter_ssl_is_https(AdapterObject *self, PyObject *args)
3479 {
3480 APR_OPTIONAL_FN_TYPE(ssl_is_https) *ssl_is_https = 0;
3481
3482 if (!self->r) {
3483 PyErr_SetString(PyExc_RuntimeError, "request object has expired");
3484 return NULL;
3485 }
3486
3487 if (!PyArg_ParseTuple(args, ":ssl_is_https"))
3488 return NULL;
3489
3490 ssl_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
3491
3492 if (ssl_is_https == 0)
3493 return Py_BuildValue("i", 0);
3494
3495 return Py_BuildValue("i", ssl_is_https(self->r->connection));
3496 }
3497
Adapter_ssl_var_lookup(AdapterObject * self,PyObject * args)3498 static PyObject *Adapter_ssl_var_lookup(AdapterObject *self, PyObject *args)
3499 {
3500 APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *ssl_var_lookup = 0;
3501
3502 PyObject *item = NULL;
3503 PyObject *latin_item = NULL;
3504
3505 char *name = 0;
3506 char *value = 0;
3507
3508 if (!self->r) {
3509 PyErr_SetString(PyExc_RuntimeError, "request object has expired");
3510 return NULL;
3511 }
3512
3513 if (!PyArg_ParseTuple(args, "O:ssl_var_lookup", &item))
3514 return NULL;
3515
3516 #if PY_MAJOR_VERSION >= 3
3517 if (PyUnicode_Check(item)) {
3518 latin_item = PyUnicode_AsLatin1String(item);
3519 if (!latin_item) {
3520 PyErr_Format(PyExc_TypeError, "byte string value expected, "
3521 "value containing non 'latin-1' characters found");
3522
3523 return NULL;
3524 }
3525
3526 item = latin_item;
3527 }
3528 #endif
3529
3530 if (!PyString_Check(item)) {
3531 PyErr_Format(PyExc_TypeError, "byte string value expected, value "
3532 "of type %.200s found", item->ob_type->tp_name);
3533
3534 Py_XDECREF(latin_item);
3535
3536 return NULL;
3537 }
3538
3539 name = PyString_AsString(item);
3540
3541 ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
3542
3543 if (ssl_var_lookup == 0)
3544 {
3545 Py_XDECREF(latin_item);
3546
3547 Py_INCREF(Py_None);
3548
3549 return Py_None;
3550 }
3551
3552 value = ssl_var_lookup(self->r->pool, self->r->server,
3553 self->r->connection, self->r, name);
3554
3555 Py_XDECREF(latin_item);
3556
3557 if (!value) {
3558 Py_INCREF(Py_None);
3559
3560 return Py_None;
3561 }
3562
3563 #if PY_MAJOR_VERSION >= 3
3564 return PyUnicode_DecodeLatin1(value, strlen(value), NULL);
3565 #else
3566 return PyString_FromString(value);
3567 #endif
3568 }
3569
3570 static PyMethodDef Adapter_methods[] = {
3571 { "start_response", (PyCFunction)Adapter_start_response, METH_VARARGS, 0 },
3572 { "write", (PyCFunction)Adapter_write, METH_VARARGS, 0 },
3573 { "ssl_is_https", (PyCFunction)Adapter_ssl_is_https, METH_VARARGS, 0 },
3574 { "ssl_var_lookup", (PyCFunction)Adapter_ssl_var_lookup, METH_VARARGS, 0 },
3575 { NULL, NULL}
3576 };
3577
3578 static PyTypeObject Adapter_Type = {
3579 PyVarObject_HEAD_INIT(NULL, 0)
3580 "mod_wsgi.Adapter", /*tp_name*/
3581 sizeof(AdapterObject), /*tp_basicsize*/
3582 0, /*tp_itemsize*/
3583 /* methods */
3584 (destructor)Adapter_dealloc, /*tp_dealloc*/
3585 0, /*tp_print*/
3586 0, /*tp_getattr*/
3587 0, /*tp_setattr*/
3588 0, /*tp_compare*/
3589 0, /*tp_repr*/
3590 0, /*tp_as_number*/
3591 0, /*tp_as_sequence*/
3592 0, /*tp_as_mapping*/
3593 0, /*tp_hash*/
3594 0, /*tp_call*/
3595 0, /*tp_str*/
3596 0, /*tp_getattro*/
3597 0, /*tp_setattro*/
3598 0, /*tp_as_buffer*/
3599 Py_TPFLAGS_DEFAULT, /*tp_flags*/
3600 0, /*tp_doc*/
3601 0, /*tp_traverse*/
3602 0, /*tp_clear*/
3603 0, /*tp_richcompare*/
3604 0, /*tp_weaklistoffset*/
3605 0, /*tp_iter*/
3606 0, /*tp_iternext*/
3607 Adapter_methods, /*tp_methods*/
3608 0, /*tp_members*/
3609 0, /*tp_getset*/
3610 0, /*tp_base*/
3611 0, /*tp_dict*/
3612 0, /*tp_descr_get*/
3613 0, /*tp_descr_set*/
3614 0, /*tp_dictoffset*/
3615 0, /*tp_init*/
3616 0, /*tp_alloc*/
3617 0, /*tp_new*/
3618 0, /*tp_free*/
3619 0, /*tp_is_gc*/
3620 };
3621
3622 /*
3623 * Code for importing a module from source by absolute path.
3624 */
3625
wsgi_load_source(apr_pool_t * pool,request_rec * r,const char * name,int exists,const char * filename,const char * process_group,const char * application_group,int ignore_system_exit)3626 static PyObject *wsgi_load_source(apr_pool_t *pool, request_rec *r,
3627 const char *name, int exists,
3628 const char* filename,
3629 const char *process_group,
3630 const char *application_group,
3631 int ignore_system_exit)
3632 {
3633 FILE *fp = NULL;
3634 PyObject *m = NULL;
3635 PyObject *co = NULL;
3636 struct _node *n = NULL;
3637
3638 #if defined(WIN32) && defined(APR_HAS_UNICODE_FS)
3639 apr_wchar_t wfilename[APR_PATH_MAX];
3640 #endif
3641
3642 if (exists) {
3643 Py_BEGIN_ALLOW_THREADS
3644 if (r) {
3645 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
3646 "mod_wsgi (pid=%d, process='%s', application='%s'): "
3647 "Reloading WSGI script '%s'.", getpid(),
3648 process_group, application_group, filename);
3649 }
3650 else {
3651 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
3652 "mod_wsgi (pid=%d, process='%s', application='%s'): "
3653 "Reloading WSGI script '%s'.", getpid(),
3654 process_group, application_group, filename);
3655 }
3656 Py_END_ALLOW_THREADS
3657 }
3658 else {
3659 Py_BEGIN_ALLOW_THREADS
3660 if (r) {
3661 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
3662 "mod_wsgi (pid=%d, process='%s', application='%s'): "
3663 "Loading Python script file '%s'.", getpid(),
3664 process_group, application_group, filename);
3665 }
3666 else {
3667 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
3668 "mod_wsgi (pid=%d, process='%s', application='%s'): "
3669 "Loading Python script file '%s'.", getpid(),
3670 process_group, application_group, filename);
3671 }
3672 Py_END_ALLOW_THREADS
3673 }
3674
3675 #if defined(WIN32) && defined(APR_HAS_UNICODE_FS)
3676 if (wsgi_utf8_to_unicode_path(wfilename, sizeof(wfilename) /
3677 sizeof(apr_wchar_t), filename)) {
3678
3679 Py_BEGIN_ALLOW_THREADS
3680 if (r) {
3681 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
3682 "mod_wsgi (pid=%d, process='%s', "
3683 "application='%s'): Failed to convert '%s' "
3684 "to UCS2 filename.", getpid(),
3685 process_group, application_group, filename);
3686 }
3687 else {
3688 ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
3689 "mod_wsgi (pid=%d, process='%s', "
3690 "application='%s'): Failed to convert '%s' "
3691 "to UCS2 filename.", getpid(),
3692 process_group, application_group, filename);
3693 }
3694 Py_END_ALLOW_THREADS
3695 return NULL;
3696 }
3697
3698 fp = _wfopen(wfilename, L"r");
3699 #else
3700 fp = fopen(filename, "r");
3701 #endif
3702
3703 if (!fp) {
3704 Py_BEGIN_ALLOW_THREADS
3705 if (r) {
3706 ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r,
3707 "mod_wsgi (pid=%d, process='%s', application='%s'): "
3708 "Call to fopen() failed for '%s'.", getpid(),
3709 process_group, application_group, filename);
3710 }
3711 else {
3712 ap_log_error(APLOG_MARK, APLOG_ERR, errno, wsgi_server,
3713 "mod_wsgi (pid=%d, process='%s', application='%s'): "
3714 "Call to fopen() failed for '%s'.", getpid(),
3715 process_group, application_group, filename);
3716 }
3717 Py_END_ALLOW_THREADS
3718 return NULL;
3719 }
3720
3721 n = PyParser_SimpleParseFile(fp, filename, Py_file_input);
3722
3723 fclose(fp);
3724
3725 if (!n) {
3726 Py_BEGIN_ALLOW_THREADS
3727 if (r) {
3728 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
3729 "mod_wsgi (pid=%d, process='%s', application='%s'): "
3730 "Failed to parse Python script file '%s'.", getpid(),
3731 process_group, application_group, filename);
3732 }
3733 else {
3734 ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
3735 "mod_wsgi (pid=%d, process='%s', application='%s'): "
3736 "Failed to parse Python script file '%s'.", getpid(),
3737 process_group, application_group, filename);
3738 }
3739 Py_END_ALLOW_THREADS
3740
3741 wsgi_log_python_error(r, NULL, filename, 0);
3742
3743 return NULL;
3744 }
3745
3746 co = (PyObject *)PyNode_Compile(n, filename);
3747 PyNode_Free(n);
3748
3749 if (co)
3750 m = PyImport_ExecCodeModuleEx((char *)name, co, (char *)filename);
3751
3752 Py_XDECREF(co);
3753
3754 if (m) {
3755 PyObject *object = NULL;
3756
3757 if (!r || strcmp(r->filename, filename)) {
3758 apr_finfo_t finfo;
3759 if (apr_stat(&finfo, filename, APR_FINFO_NORM,
3760 pool) != APR_SUCCESS) {
3761 object = PyLong_FromLongLong(0);
3762 }
3763 else {
3764 object = PyLong_FromLongLong(finfo.mtime);
3765 }
3766 }
3767 else {
3768 object = PyLong_FromLongLong(r->finfo.mtime);
3769 }
3770 PyModule_AddObject(m, "__mtime__", object);
3771 }
3772 else {
3773 if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
3774 if (!ignore_system_exit) {
3775 Py_BEGIN_ALLOW_THREADS
3776 if (r) {
3777 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
3778 "mod_wsgi (pid=%d): SystemExit exception "
3779 "raised when doing exec of Python script "
3780 "file '%s'.", getpid(), filename);
3781 }
3782 else {
3783 ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
3784 "mod_wsgi (pid=%d): SystemExit exception "
3785 "raised when doing exec of Python script "
3786 "file '%s'.", getpid(), filename);
3787 }
3788 Py_END_ALLOW_THREADS
3789 }
3790 }
3791 else {
3792 Py_BEGIN_ALLOW_THREADS
3793 if (r) {
3794 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
3795 "mod_wsgi (pid=%d): Failed to exec Python script "
3796 "file '%s'.", getpid(), filename);
3797 }
3798 else {
3799 ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
3800 "mod_wsgi (pid=%d): Failed to exec Python script "
3801 "file '%s'.", getpid(), filename);
3802 }
3803 Py_END_ALLOW_THREADS
3804
3805 wsgi_log_python_error(r, NULL, filename, 0);
3806 }
3807 }
3808
3809 return m;
3810 }
3811
wsgi_reload_required(apr_pool_t * pool,request_rec * r,const char * filename,PyObject * module,const char * resource)3812 static int wsgi_reload_required(apr_pool_t *pool, request_rec *r,
3813 const char *filename, PyObject *module,
3814 const char *resource)
3815 {
3816 PyObject *dict = NULL;
3817 PyObject *object = NULL;
3818 apr_time_t mtime = 0;
3819
3820 dict = PyModule_GetDict(module);
3821 object = PyDict_GetItemString(dict, "__mtime__");
3822
3823 if (object) {
3824 mtime = PyLong_AsLongLong(object);
3825
3826 if (!r || strcmp(r->filename, filename)) {
3827 apr_finfo_t finfo;
3828 if (apr_stat(&finfo, filename, APR_FINFO_NORM,
3829 pool) != APR_SUCCESS) {
3830 return 1;
3831 }
3832 else if (mtime != finfo.mtime) {
3833 return 1;
3834 }
3835 }
3836 else {
3837 if (mtime != r->finfo.mtime)
3838 return 1;
3839 }
3840 }
3841 else
3842 return 1;
3843
3844 if (resource) {
3845 PyObject *dict = NULL;
3846 PyObject *object = NULL;
3847
3848 dict = PyModule_GetDict(module);
3849 object = PyDict_GetItemString(dict, "reload_required");
3850
3851 if (object) {
3852 PyObject *args = NULL;
3853 PyObject *result = NULL;
3854
3855 Py_INCREF(object);
3856 args = Py_BuildValue("(s)", resource);
3857 result = PyEval_CallObject(object, args);
3858 Py_DECREF(args);
3859 Py_DECREF(object);
3860
3861 if (result && PyObject_IsTrue(result)) {
3862 Py_DECREF(result);
3863
3864 return 1;
3865 }
3866
3867 if (PyErr_Occurred())
3868 wsgi_log_python_error(r, NULL, filename, 0);
3869
3870 Py_XDECREF(result);
3871 }
3872 }
3873
3874 return 0;
3875 }
3876
wsgi_module_name(apr_pool_t * pool,const char * filename)3877 static char *wsgi_module_name(apr_pool_t *pool, const char *filename)
3878 {
3879 char *hash = NULL;
3880 char *file = NULL;
3881
3882 /*
3883 * Calculate a name for the module using the MD5 of its full
3884 * pathname. This is so that different code files with the
3885 * same basename are still considered unique. Note that where
3886 * we believe a case insensitive file system is being used,
3887 * we always change the file name to lower case so that use
3888 * of different case in name doesn't result in duplicate
3889 * modules being loaded for the same file.
3890 */
3891
3892 file = (char *)filename;
3893
3894 if (wsgi_server_config->case_sensitivity) {
3895 file = apr_pstrdup(pool, file);
3896 ap_str_tolower(file);
3897 }
3898
3899 hash = ap_md5(pool, (const unsigned char *)file);
3900 return apr_pstrcat(pool, "_mod_wsgi_", hash, NULL);
3901 }
3902
3903 #if APR_HAS_THREADS
3904 static apr_thread_mutex_t* wsgi_module_lock = NULL;
3905 #endif
3906
wsgi_execute_script(request_rec * r)3907 static int wsgi_execute_script(request_rec *r)
3908 {
3909 WSGIRequestConfig *config = NULL;
3910
3911 InterpreterObject *interp = NULL;
3912 PyObject *modules = NULL;
3913 PyObject *module = NULL;
3914 const char *script = NULL;
3915 const char *name = NULL;
3916 int exists = 0;
3917
3918 int status;
3919
3920 WSGIThreadInfo *thread_info = NULL;
3921
3922 /* Grab request configuration. */
3923
3924 config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
3925 &wsgi_module);
3926
3927 /*
3928 * Acquire the desired python interpreter. Once this is done
3929 * it is safe to start manipulating python objects.
3930 */
3931
3932 interp = wsgi_acquire_interpreter(config->application_group);
3933
3934 if (!interp) {
3935 ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
3936 "mod_wsgi (pid=%d): Cannot acquire interpreter '%s'.",
3937 getpid(), config->application_group);
3938
3939 return HTTP_INTERNAL_SERVER_ERROR;
3940 }
3941
3942 /* Setup startup timeout if first request and specified. */
3943
3944 #if defined(MOD_WSGI_WITH_DAEMONS)
3945 if (wsgi_daemon_process) {
3946 if (wsgi_startup_shutdown_time == 0) {
3947 if (wsgi_startup_timeout > 0) {
3948 apr_thread_mutex_lock(wsgi_monitor_lock);
3949 wsgi_startup_shutdown_time = apr_time_now();
3950 wsgi_startup_shutdown_time += wsgi_startup_timeout;
3951 apr_thread_mutex_unlock(wsgi_monitor_lock);
3952 }
3953 }
3954 }
3955 #endif
3956
3957 /*
3958 * Use a lock around the check to see if the module is
3959 * already loaded and the import of the module to prevent
3960 * two request handlers trying to import the module at the
3961 * same time.
3962 */
3963
3964 #if APR_HAS_THREADS
3965 Py_BEGIN_ALLOW_THREADS
3966 apr_thread_mutex_lock(wsgi_module_lock);
3967 Py_END_ALLOW_THREADS
3968 #endif
3969
3970 /* Calculate the Python module name to be used for script. */
3971
3972 if (config->handler_script && *config->handler_script) {
3973 script = config->handler_script;
3974
3975 #if 0
3976 /*
3977 * Check for whether a module reference is provided
3978 * as opposed to a filesystem path.
3979 */
3980
3981 if (strlen(script) > 2 && script[0] == '(' &&
3982 script[strlen(script)-1] == ')') {
3983 name = apr_pstrndup(r->pool, script+1, strlen(script)-2);
3984
3985 module = PyImport_ImportModule(name);
3986
3987 if (!module) {
3988 Py_BEGIN_ALLOW_THREADS
3989 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
3990 "mod_wsgi (pid=%d): Failed to import handler "
3991 "via Python module reference %s.", getpid(),
3992 script);
3993 Py_END_ALLOW_THREADS
3994
3995 wsgi_log_python_error(r, NULL, r->filename, 0);
3996 }
3997 }
3998 #endif
3999 }
4000 else
4001 script = r->filename;
4002
4003 if (!module) {
4004 name = wsgi_module_name(r->pool, script);
4005
4006 modules = PyImport_GetModuleDict();
4007 module = PyDict_GetItemString(modules, name);
4008
4009 Py_XINCREF(module);
4010
4011 if (module)
4012 exists = 1;
4013
4014 /*
4015 * If script reloading is enabled and the module for it has
4016 * previously been loaded, see if it has been modified since
4017 * the last time it was accessed. For a handler script will
4018 * also see if it contains a custom function for determining
4019 * if a reload should be performed.
4020 */
4021
4022 if (module && config->script_reloading) {
4023 if (wsgi_reload_required(r->pool, r, script, module, r->filename)) {
4024 /*
4025 * Script file has changed. Discard reference to
4026 * loaded module and work out what action we are
4027 * supposed to take. Choices are process reloading
4028 * and module reloading. Process reloading cannot be
4029 * performed unless a daemon process is being used.
4030 */
4031
4032 Py_DECREF(module);
4033 module = NULL;
4034
4035 #if defined(MOD_WSGI_WITH_DAEMONS)
4036 if (*config->process_group) {
4037 /*
4038 * Need to restart the daemon process. We bail
4039 * out on the request process here, sending back
4040 * a special response header indicating that
4041 * process is being restarted and that remote
4042 * end should abandon connection and attempt to
4043 * reconnect again. We also need to signal this
4044 * process so it will actually shutdown. The
4045 * process supervisor code will ensure that it
4046 * is restarted.
4047 */
4048
4049 Py_BEGIN_ALLOW_THREADS
4050 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
4051 "mod_wsgi (pid=%d): Force restart of "
4052 "process '%s'.", getpid(),
4053 config->process_group);
4054 Py_END_ALLOW_THREADS
4055
4056 #if APR_HAS_THREADS
4057 apr_thread_mutex_unlock(wsgi_module_lock);
4058 #endif
4059
4060 wsgi_release_interpreter(interp);
4061
4062 r->status = HTTP_INTERNAL_SERVER_ERROR;
4063 r->status_line = "200 Rejected";
4064
4065 wsgi_daemon_shutdown++;
4066 kill(getpid(), SIGINT);
4067
4068 return OK;
4069 }
4070 else {
4071 /*
4072 * Need to reload just the script module. Remove
4073 * the module from the modules dictionary before
4074 * reloading it again. If code is executing
4075 * within the module at the time, the callers
4076 * reference count on the module should ensure
4077 * it isn't actually destroyed until it is
4078 * finished.
4079 */
4080
4081 PyDict_DelItemString(modules, name);
4082 }
4083 #else
4084 /*
4085 * Need to reload just the script module. Remove
4086 * the module from the modules dictionary before
4087 * reloading it again. If code is executing
4088 * within the module at the time, the callers
4089 * reference count on the module should ensure
4090 * it isn't actually destroyed until it is
4091 * finished.
4092 */
4093
4094 PyDict_DelItemString(modules, name);
4095 #endif
4096 }
4097 }
4098 }
4099
4100 /*
4101 * When process reloading is in use need to indicate
4102 * that request content should now be sent through.
4103 * This is done by writing a special response header
4104 * directly out onto the appropriate network output
4105 * filter. The special response is picked up by
4106 * remote end and data will then be sent.
4107 */
4108
4109 #if defined(MOD_WSGI_WITH_DAEMONS)
4110 if (*config->process_group) {
4111 ap_filter_t *filters;
4112 apr_bucket_brigade *bb;
4113 apr_bucket *b;
4114
4115 const char *data = "Status: 200 Continue\r\n\r\n";
4116 long length = strlen(data);
4117
4118 filters = r->output_filters;
4119 while (filters && filters->frec->ftype != AP_FTYPE_NETWORK) {
4120 filters = filters->next;
4121 }
4122
4123 bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
4124
4125 b = apr_bucket_transient_create(data, length,
4126 r->connection->bucket_alloc);
4127 APR_BRIGADE_INSERT_TAIL(bb, b);
4128
4129 b = apr_bucket_flush_create(r->connection->bucket_alloc);
4130 APR_BRIGADE_INSERT_TAIL(bb, b);
4131
4132 /*
4133 * This should always work, so ignore any errors
4134 * from passing the brigade to the network
4135 * output filter. If there are are problems they
4136 * will be picked up further down in processing
4137 * anyway.
4138 */
4139
4140 ap_pass_brigade(filters, bb);
4141 }
4142 #endif
4143
4144 /* Setup metrics for start of request. */
4145
4146 thread_info = wsgi_start_request(r);
4147
4148 /* Load module if not already loaded. */
4149
4150 if (!module) {
4151 module = wsgi_load_source(r->pool, r, name, exists, script,
4152 config->process_group,
4153 config->application_group, 0);
4154 }
4155
4156 /* Safe now to release the module lock. */
4157
4158 #if APR_HAS_THREADS
4159 apr_thread_mutex_unlock(wsgi_module_lock);
4160 #endif
4161
4162 /*
4163 * Clear startup timeout and prevent from running again if the
4164 * module was successfully loaded.
4165 */
4166
4167 #if defined(MOD_WSGI_WITH_DAEMONS)
4168 if (module && wsgi_startup_shutdown_time > 0) {
4169 wsgi_startup_shutdown_time = -1;
4170 }
4171 #endif
4172
4173 /* Assume an internal server error unless everything okay. */
4174
4175 status = HTTP_INTERNAL_SERVER_ERROR;
4176
4177 /* Determine if script exists and execute it. */
4178
4179 if (module) {
4180 PyObject *module_dict = NULL;
4181 PyObject *object = NULL;
4182
4183 module_dict = PyModule_GetDict(module);
4184 object = PyDict_GetItemString(module_dict, config->callable_object);
4185
4186 if (object) {
4187 AdapterObject *adapter = NULL;
4188 adapter = newAdapterObject(r);
4189
4190 if (adapter) {
4191 PyObject *method = NULL;
4192 PyObject *args = NULL;
4193
4194 Py_INCREF(adapter->log_buffer);
4195 thread_info->log_buffer = adapter->log_buffer;
4196
4197 Py_INCREF(object);
4198 status = Adapter_run(adapter, object);
4199 Py_DECREF(object);
4200
4201 /*
4202 * Wipe out references to Apache request object
4203 * held by Python objects, so can detect when an
4204 * application holds on to the transient Python
4205 * objects beyond the life of the request and
4206 * thus raise an exception if they are used.
4207 */
4208
4209 adapter->r = NULL;
4210
4211 Input_finish(adapter->input);
4212
4213 /* Close the log object so data is flushed. */
4214
4215 method = PyObject_GetAttrString(adapter->log, "close");
4216
4217 if (!method) {
4218 PyErr_Format(PyExc_AttributeError,
4219 "'%s' object has no attribute 'close'",
4220 adapter->log->ob_type->tp_name);
4221 }
4222 else {
4223 args = PyTuple_New(0);
4224 object = PyEval_CallObject(method, args);
4225 Py_DECREF(args);
4226 }
4227
4228 Py_XDECREF(object);
4229 Py_XDECREF(method);
4230
4231 Py_CLEAR(thread_info->log_buffer);
4232
4233 adapter->bb = NULL;
4234 }
4235
4236 Py_XDECREF((PyObject *)adapter);
4237 }
4238 else {
4239 Py_BEGIN_ALLOW_THREADS
4240 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
4241 "mod_wsgi (pid=%d): Target WSGI script '%s' does "
4242 "not contain WSGI application '%s'.",
4243 getpid(), script, config->callable_object);
4244 Py_END_ALLOW_THREADS
4245
4246 status = HTTP_NOT_FOUND;
4247 }
4248 }
4249
4250 /* Log any details of exceptions if execution failed. */
4251
4252 if (PyErr_Occurred())
4253 wsgi_log_python_error(r, NULL, r->filename, 0);
4254
4255 Py_XDECREF(module);
4256
4257 /* Finalise any metrics at end of the request. */
4258
4259 wsgi_end_request();
4260
4261 /* Cleanup and release interpreter, */
4262
4263 wsgi_release_interpreter(interp);
4264
4265 return status;
4266 }
4267
4268 /*
4269 * Apache child process initialisation and cleanup. Initialise
4270 * global table containing Python interpreter instances and
4271 * cache reference to main interpreter. Also register cleanup
4272 * function to delete interpreter on process shutdown.
4273 */
4274
wsgi_python_child_cleanup(void * data)4275 static apr_status_t wsgi_python_child_cleanup(void *data)
4276 {
4277 PyObject *interp = NULL;
4278
4279 /*
4280 * If not a daemon process need to publish that process
4281 * is shutting down here. For daemon we did it earlier
4282 * before trying to wait on request threads.
4283 */
4284
4285 #if defined(MOD_WSGI_WITH_DAEMONS)
4286 if (!wsgi_daemon_process)
4287 wsgi_publish_process_stopping(wsgi_shutdown_reason);
4288 #else
4289 wsgi_publish_process_stopping(wsgi_shutdown_reason);
4290 #endif
4291
4292 /* In a multithreaded MPM must protect table. */
4293
4294 #if APR_HAS_THREADS
4295 apr_thread_mutex_lock(wsgi_interp_lock);
4296 #endif
4297
4298 /*
4299 * We should be executing in the main thread again at this
4300 * point but without the GIL, so simply restore the original
4301 * thread state for that thread that we remembered when we
4302 * initialised the interpreter.
4303 */
4304
4305 PyEval_AcquireThread(wsgi_main_tstate);
4306
4307 /*
4308 * Extract a handle to the main Python interpreter from
4309 * interpreters dictionary as want to process that one last.
4310 */
4311
4312 interp = PyDict_GetItemString(wsgi_interpreters, "");
4313 Py_INCREF(interp);
4314
4315 /*
4316 * Remove all items from interpreters dictionary. This will
4317 * have side affect of calling any exit functions and
4318 * destroying interpreters we own.
4319 */
4320
4321 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
4322 "mod_wsgi (pid=%d): Destroying interpreters.", getpid());
4323
4324 PyDict_Clear(wsgi_interpreters);
4325
4326 #if APR_HAS_THREADS
4327 apr_thread_mutex_unlock(wsgi_interp_lock);
4328 #endif
4329
4330 /*
4331 * Now we decrement reference on handle for main Python
4332 * interpreter. This only causes exit functions to be called
4333 * and doesn't result in interpreter being destroyed as we
4334 * we didn't previously mark ourselves as the owner of the
4335 * interpreter. Note that when Python as a whole is later
4336 * being destroyed it will also call exit functions, but by
4337 * then the exit function registrations have been removed
4338 * and so they will not actually be run a second time.
4339 */
4340
4341 Py_DECREF(interp);
4342
4343 /*
4344 * The code which performs actual shutdown of the main
4345 * interpreter expects to be called without the GIL, so
4346 * we release it here again.
4347 */
4348
4349 PyEval_ReleaseThread(wsgi_main_tstate);
4350
4351 /*
4352 * Destroy Python itself including the main interpreter.
4353 * If mod_python is being loaded it is left to mod_python to
4354 * destroy Python, although it currently doesn't do so.
4355 */
4356
4357 if (wsgi_python_initialized)
4358 wsgi_python_term();
4359
4360 return APR_SUCCESS;
4361 }
4362
wsgi_python_child_init(apr_pool_t * p)4363 static void wsgi_python_child_init(apr_pool_t *p)
4364 {
4365 PyGILState_STATE state;
4366 PyObject *object = NULL;
4367
4368 int ignore_system_exit = 0;
4369
4370 /* Working with Python, so must acquire GIL. */
4371
4372 state = PyGILState_Ensure();
4373
4374 /*
4375 * Trigger any special Python stuff required after a fork.
4376 * Only do this though if we were responsible for the
4377 * initialisation of the Python interpreter in the first
4378 * place to avoid it being done multiple times. Also only
4379 * do it if Python was initialised in parent process.
4380 */
4381
4382 #ifdef HAVE_FORK
4383 if (wsgi_python_initialized && !wsgi_python_after_fork) {
4384 #if PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 7)
4385 PyOS_AfterFork_Child();
4386 #else
4387 PyOS_AfterFork();
4388 #endif
4389 }
4390 #endif
4391
4392 /* Finalise any Python objects required by child process. */
4393
4394 PyType_Ready(&Log_Type);
4395 PyType_Ready(&Stream_Type);
4396 PyType_Ready(&Input_Type);
4397 PyType_Ready(&Adapter_Type);
4398 PyType_Ready(&Restricted_Type);
4399 PyType_Ready(&Interpreter_Type);
4400 PyType_Ready(&Dispatch_Type);
4401 PyType_Ready(&Auth_Type);
4402
4403 PyType_Ready(&SignalIntercept_Type);
4404
4405 #if PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 4)
4406 PyType_Ready(&ShutdownInterpreter_Type);
4407 #endif
4408
4409 /* Initialise Python interpreter instance table and lock. */
4410
4411 wsgi_interpreters = PyDict_New();
4412
4413 #if APR_HAS_THREADS
4414 apr_thread_mutex_create(&wsgi_interp_lock, APR_THREAD_MUTEX_UNNESTED, p);
4415 apr_thread_mutex_create(&wsgi_module_lock, APR_THREAD_MUTEX_UNNESTED, p);
4416 apr_thread_mutex_create(&wsgi_shutdown_lock, APR_THREAD_MUTEX_UNNESTED, p);
4417 #endif
4418
4419 /*
4420 * Create an interpreters index using Apache data structure so
4421 * can iterate over interpreter names without needing Python GIL.
4422 */
4423
4424 wsgi_interpreters_index = apr_hash_make(p);
4425
4426 /*
4427 * Initialise the key for data related to a thread and force
4428 * creation of thread info.
4429 */
4430
4431 apr_threadkey_private_create(&wsgi_thread_key, NULL, p);
4432
4433 wsgi_thread_info(1, 0);
4434
4435 /*
4436 * Cache a reference to the first Python interpreter
4437 * instance. This interpreter is special as some third party
4438 * Python modules will only work when used from within this
4439 * interpreter. This is generally when they use the Python
4440 * simplified GIL API or otherwise don't use threading API
4441 * properly. An empty string for name is used to identify
4442 * the first Python interpreter instance.
4443 */
4444
4445 object = (PyObject *)newInterpreterObject(NULL);
4446 PyDict_SetItemString(wsgi_interpreters, "", object);
4447 Py_DECREF(object);
4448
4449 apr_hash_set(wsgi_interpreters_index, "", APR_HASH_KEY_STRING, "");
4450
4451 /* Restore the prior thread state and release the GIL. */
4452
4453 PyGILState_Release(state);
4454
4455 /* Register cleanups to performed on process shutdown. */
4456
4457 apr_pool_cleanup_register(p, NULL, wsgi_python_child_cleanup,
4458 apr_pool_cleanup_null);
4459
4460 /* Loop through import scripts for this process and load them. */
4461
4462 #if defined(MOD_WSGI_WITH_DAEMONS)
4463 if (wsgi_daemon_process && wsgi_daemon_process->group->threads == 0)
4464 ignore_system_exit = 1;
4465 #endif
4466
4467 if (wsgi_import_list) {
4468 apr_array_header_t *scripts = NULL;
4469
4470 WSGIScriptFile *entries;
4471 WSGIScriptFile *entry;
4472
4473 int i;
4474
4475 scripts = wsgi_import_list;
4476 entries = (WSGIScriptFile *)scripts->elts;
4477
4478 for (i = 0; i < scripts->nelts; ++i) {
4479 entry = &entries[i];
4480
4481 /*
4482 * Stop loading scripts if this is a daemon process and
4483 * we have already been flagged to be shutdown.
4484 */
4485
4486 if (wsgi_daemon_shutdown)
4487 break;
4488
4489 if (!strcmp(wsgi_daemon_group, entry->process_group)) {
4490 InterpreterObject *interp = NULL;
4491 PyObject *modules = NULL;
4492 PyObject *module = NULL;
4493 char *name = NULL;
4494 int exists = 0;
4495
4496 interp = wsgi_acquire_interpreter(entry->application_group);
4497
4498 if (!interp) {
4499 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, wsgi_server,
4500 "mod_wsgi (pid=%d): Cannot acquire "
4501 "interpreter '%s'.", getpid(),
4502 entry->application_group);
4503 }
4504
4505 /* Calculate the Python module name to be used for script. */
4506
4507 name = wsgi_module_name(p, entry->handler_script);
4508
4509 /*
4510 * Use a lock around the check to see if the module is
4511 * already loaded and the import of the module. Strictly
4512 * speaking shouldn't be required at this point.
4513 */
4514
4515 #if APR_HAS_THREADS
4516 Py_BEGIN_ALLOW_THREADS
4517 apr_thread_mutex_lock(wsgi_module_lock);
4518 Py_END_ALLOW_THREADS
4519 #endif
4520
4521 modules = PyImport_GetModuleDict();
4522 module = PyDict_GetItemString(modules, name);
4523
4524 Py_XINCREF(module);
4525
4526 if (module)
4527 exists = 1;
4528
4529 /*
4530 * If script reloading is enabled and the module for it has
4531 * previously been loaded, see if it has been modified since
4532 * the last time it was accessed.
4533 */
4534
4535 if (module && wsgi_server_config->script_reloading) {
4536 if (wsgi_reload_required(p, NULL, entry->handler_script,
4537 module, NULL)) {
4538 /*
4539 * Script file has changed. Only support module
4540 * reloading for dispatch scripts. Remove the
4541 * module from the modules dictionary before
4542 * reloading it again. If code is executing within
4543 * the module at the time, the callers reference
4544 * count on the module should ensure it isn't
4545 * actually destroyed until it is finished.
4546 */
4547
4548 Py_DECREF(module);
4549 module = NULL;
4550
4551 PyDict_DelItemString(modules, name);
4552 }
4553 }
4554
4555 if (!module) {
4556 module = wsgi_load_source(p, NULL, name, exists,
4557 entry->handler_script,
4558 entry->process_group,
4559 entry->application_group,
4560 ignore_system_exit);
4561
4562 if (PyErr_Occurred())
4563 PyErr_Clear();
4564 }
4565
4566 /* Safe now to release the module lock. */
4567
4568 #if APR_HAS_THREADS
4569 apr_thread_mutex_unlock(wsgi_module_lock);
4570 #endif
4571
4572 /* Cleanup and release interpreter, */
4573
4574 Py_XDECREF(module);
4575
4576 wsgi_release_interpreter(interp);
4577 }
4578 }
4579 }
4580 }
4581
4582 /* The processors for directives. */
4583
wsgi_parse_option(apr_pool_t * p,const char ** line,const char ** name,const char ** value)4584 static int wsgi_parse_option(apr_pool_t *p, const char **line,
4585 const char **name, const char **value)
4586 {
4587 const char *str = *line, *strend;
4588
4589 while (*str && apr_isspace(*str))
4590 ++str;
4591
4592 if (!*str || *str == '=') {
4593 *line = str;
4594 return !APR_SUCCESS;
4595 }
4596
4597 /* Option must be of form name=value. Extract the name. */
4598
4599 strend = str;
4600 while (*strend && *strend != '=' && !apr_isspace(*strend))
4601 ++strend;
4602
4603 if (*strend != '=') {
4604 *line = str;
4605 return !APR_SUCCESS;
4606 }
4607
4608 *name = apr_pstrndup(p, str, strend-str);
4609
4610 *line = strend+1;
4611
4612 /* Now extract the value. Note that value can be quoted. */
4613
4614 *value = ap_getword_conf(p, line);
4615
4616 return APR_SUCCESS;
4617 }
4618
wsgi_add_script_alias(cmd_parms * cmd,void * mconfig,const char * args)4619 static const char *wsgi_add_script_alias(cmd_parms *cmd, void *mconfig,
4620 const char *args)
4621 {
4622 const char *l = NULL;
4623 const char *a = NULL;
4624
4625 WSGIServerConfig *sconfig = NULL;
4626 WSGIAliasEntry *entry = NULL;
4627
4628 const char *option = NULL;
4629 const char *value = NULL;
4630
4631 #if defined(MOD_WSGI_WITH_DAEMONS)
4632 const char *process_group = NULL;
4633 #else
4634 const char *process_group = "";
4635 #endif
4636
4637 const char *application_group = NULL;
4638 const char *callable_object = NULL;
4639
4640 int pass_authorization = -1;
4641
4642 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
4643
4644 if (!sconfig->alias_list) {
4645 sconfig->alias_list = apr_array_make(sconfig->pool, 20,
4646 sizeof(WSGIAliasEntry));
4647 }
4648
4649 l = ap_getword_conf(cmd->pool, &args);
4650
4651 if (*l == '\0' || *args == 0) {
4652 return apr_pstrcat(cmd->pool, cmd->cmd->name,
4653 " requires at least two arguments",
4654 cmd->cmd->errmsg ? ", " : NULL,
4655 cmd->cmd->errmsg, NULL);
4656 }
4657
4658 a = ap_getword_conf(cmd->pool, &args);
4659
4660 if (*a == '\0') {
4661 return apr_pstrcat(cmd->pool, cmd->cmd->name,
4662 " requires at least two arguments",
4663 cmd->cmd->errmsg ? ", " : NULL,
4664 cmd->cmd->errmsg, NULL);
4665 }
4666
4667 while (*args) {
4668 if (wsgi_parse_option(cmd->pool, &args, &option,
4669 &value) != APR_SUCCESS) {
4670 return "Invalid option to WSGI script alias definition.";
4671 }
4672
4673 if (!strcmp(option, "application-group")) {
4674 if (!*value)
4675 return "Invalid name for WSGI application group.";
4676
4677 if (!strcmp(value, "%{GLOBAL}"))
4678 value = "";
4679
4680 application_group = value;
4681 }
4682 #if defined(MOD_WSGI_WITH_DAEMONS)
4683 else if (!strcmp(option, "process-group")) {
4684 if (!*value)
4685 return "Invalid name for WSGI process group.";
4686
4687 if (!strcmp(value, "%{GLOBAL}"))
4688 value = "";
4689
4690 process_group = value;
4691 }
4692 #endif
4693 else if (!strcmp(option, "callable-object")) {
4694 if (!*value)
4695 return "Invalid name for WSGI callable object.";
4696
4697 callable_object = value;
4698 }
4699 else if (!strcmp(option, "pass-authorization")) {
4700 if (!*value)
4701 return "Invalid value for authorization flag.";
4702
4703 if (strcasecmp(value, "Off") == 0)
4704 pass_authorization = 0;
4705 else if (strcasecmp(value, "On") == 0)
4706 pass_authorization = 1;
4707 else
4708 return "Invalid value for authorization flag.";
4709 }
4710 else
4711 return "Invalid option to WSGI script alias definition.";
4712 }
4713
4714 entry = (WSGIAliasEntry *)apr_array_push(sconfig->alias_list);
4715
4716 if (cmd->info) {
4717 entry->regexp = ap_pregcomp(cmd->pool, l, AP_REG_EXTENDED);
4718 if (!entry->regexp)
4719 return "Regular expression could not be compiled.";
4720 }
4721
4722 entry->location = l;
4723 entry->application = a;
4724
4725 entry->process_group = process_group;
4726 entry->application_group = application_group;
4727 entry->callable_object = callable_object;
4728 entry->pass_authorization = pass_authorization;
4729
4730 /*
4731 * Only add to import list if both process group and application
4732 * group are specified, that they don't include substitution values,
4733 * and in the case of WSGIScriptAliasMatch, that the WSGI script
4734 * target path doesn't include substitutions from URL pattern.
4735 */
4736
4737 if (process_group && application_group &&
4738 !strstr(process_group, "%{") &&
4739 !strstr(application_group, "%{") &&
4740 (!cmd->info || !strstr(a, "$"))) {
4741
4742 WSGIScriptFile *object = NULL;
4743
4744 if (!wsgi_import_list) {
4745 wsgi_import_list = apr_array_make(sconfig->pool, 20,
4746 sizeof(WSGIScriptFile));
4747 }
4748
4749 object = (WSGIScriptFile *)apr_array_push(wsgi_import_list);
4750
4751 object->handler_script = a;
4752 object->process_group = process_group;
4753 object->application_group = application_group;
4754
4755 #if defined(MOD_WSGI_WITH_DAEMONS)
4756 if (*object->process_group &&
4757 strcmp(object->process_group, "%{RESOURCE}") != 0 &&
4758 strcmp(object->process_group, "%{SERVER}") != 0 &&
4759 strcmp(object->process_group, "%{HOST}") != 0) {
4760
4761 WSGIProcessGroup *group = NULL;
4762 WSGIProcessGroup *entries = NULL;
4763 WSGIProcessGroup *entry = NULL;
4764 int i;
4765
4766 if (!wsgi_daemon_list)
4767 return "WSGI process group not yet configured.";
4768
4769 entries = (WSGIProcessGroup *)wsgi_daemon_list->elts;
4770
4771 for (i = 0; i < wsgi_daemon_list->nelts; ++i) {
4772 entry = &entries[i];
4773
4774 if (!strcmp(entry->name, object->process_group)) {
4775 group = entry;
4776 break;
4777 }
4778 }
4779
4780 if (!group)
4781 return "WSGI process group not yet configured.";
4782
4783 if (cmd->server->server_hostname &&
4784 group->server->server_hostname &&
4785 strcmp(cmd->server->server_hostname,
4786 group->server->server_hostname) &&
4787 group->server->is_virtual) {
4788
4789 return "WSGI process group not accessible.";
4790 }
4791
4792 if (!cmd->server->server_hostname &&
4793 group->server->server_hostname &&
4794 group->server->is_virtual) {
4795
4796 return "WSGI process group not matchable.";
4797 }
4798
4799 if (cmd->server->server_hostname &&
4800 !group->server->server_hostname &&
4801 group->server->is_virtual) {
4802
4803 return "WSGI process group not matchable.";
4804 }
4805 }
4806 #endif
4807 }
4808
4809 return NULL;
4810 }
4811
wsgi_set_verbose_debugging(cmd_parms * cmd,void * mconfig,const char * f)4812 static const char *wsgi_set_verbose_debugging(cmd_parms *cmd, void *mconfig,
4813 const char *f)
4814 {
4815 const char *error = NULL;
4816 WSGIServerConfig *sconfig = NULL;
4817
4818 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4819 if (error != NULL)
4820 return error;
4821
4822 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
4823
4824 if (strcasecmp(f, "Off") == 0)
4825 sconfig->verbose_debugging = 0;
4826 else if (strcasecmp(f, "On") == 0)
4827 sconfig->verbose_debugging = 1;
4828 else
4829 return "WSGIVerboseDebugging must be one of: Off | On";
4830
4831 return NULL;
4832 }
4833
wsgi_set_lazy_initialization(cmd_parms * cmd,void * mconfig,const char * f)4834 static const char *wsgi_set_lazy_initialization(cmd_parms *cmd, void *mconfig,
4835 const char *f)
4836 {
4837 const char *error = NULL;
4838
4839 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4840 if (error != NULL)
4841 return error;
4842
4843 if (strcasecmp(f, "Off") == 0)
4844 wsgi_python_after_fork = 0;
4845 else if (strcasecmp(f, "On") == 0)
4846 wsgi_python_after_fork = 1;
4847 else
4848 return "WSGILazyInitialization must be one of: Off | On";
4849
4850 return NULL;
4851 }
4852
wsgi_add_python_warnings(cmd_parms * cmd,void * mconfig,const char * f)4853 static const char *wsgi_add_python_warnings(cmd_parms *cmd, void *mconfig,
4854 const char *f)
4855 {
4856 const char *error = NULL;
4857 WSGIServerConfig *sconfig = NULL;
4858
4859 char **entry = NULL;
4860
4861 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4862 if (error != NULL)
4863 return error;
4864
4865 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
4866
4867 if (!sconfig->python_warnings) {
4868 sconfig->python_warnings = apr_array_make(sconfig->pool, 5,
4869 sizeof(char*));
4870 }
4871
4872 entry = (char **)apr_array_push(sconfig->python_warnings);
4873 *entry = apr_pstrdup(sconfig->pool, f);
4874
4875 return NULL;
4876 }
4877
4878 #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 6
wsgi_set_py3k_warning_flag(cmd_parms * cmd,void * mconfig,const char * f)4879 static const char *wsgi_set_py3k_warning_flag(cmd_parms *cmd, void *mconfig,
4880 const char *f)
4881 {
4882 const char *error = NULL;
4883 WSGIServerConfig *sconfig = NULL;
4884
4885 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4886 if (error != NULL)
4887 return error;
4888
4889 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
4890
4891 if (strcasecmp(f, "Off") == 0)
4892 sconfig->py3k_warning_flag = 0;
4893 else if (strcasecmp(f, "On") == 0)
4894 sconfig->py3k_warning_flag = 1;
4895 else
4896 return "WSGIPy3kWarningFlag must be one of: Off | On";
4897
4898 return NULL;
4899 }
4900 #endif
4901
4902 #if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 3) || \
4903 (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 6)
wsgi_set_dont_write_bytecode(cmd_parms * cmd,void * mconfig,const char * f)4904 static const char *wsgi_set_dont_write_bytecode(cmd_parms *cmd, void *mconfig,
4905 const char *f)
4906 {
4907 const char *error = NULL;
4908 WSGIServerConfig *sconfig = NULL;
4909
4910 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4911 if (error != NULL)
4912 return error;
4913
4914 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
4915
4916 if (strcasecmp(f, "Off") == 0)
4917 sconfig->dont_write_bytecode = 0;
4918 else if (strcasecmp(f, "On") == 0)
4919 sconfig->dont_write_bytecode = 1;
4920 else
4921 return "WSGIDontWriteBytecode must be one of: Off | On";
4922
4923 return NULL;
4924 }
4925 #endif
4926
wsgi_set_python_optimize(cmd_parms * cmd,void * mconfig,const char * f)4927 static const char *wsgi_set_python_optimize(cmd_parms *cmd, void *mconfig,
4928 const char *f)
4929 {
4930 const char *error = NULL;
4931 WSGIServerConfig *sconfig = NULL;
4932
4933 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4934 if (error != NULL)
4935 return error;
4936
4937 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
4938 sconfig->python_optimize = atoi(f);
4939
4940 return NULL;
4941 }
4942
wsgi_set_python_home(cmd_parms * cmd,void * mconfig,const char * f)4943 static const char *wsgi_set_python_home(cmd_parms *cmd, void *mconfig,
4944 const char *f)
4945 {
4946 const char *error = NULL;
4947 WSGIServerConfig *sconfig = NULL;
4948
4949 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4950 if (error != NULL)
4951 return error;
4952
4953 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
4954 sconfig->python_home = f;
4955
4956 return NULL;
4957 }
4958
wsgi_set_python_path(cmd_parms * cmd,void * mconfig,const char * f)4959 static const char *wsgi_set_python_path(cmd_parms *cmd, void *mconfig,
4960 const char *f)
4961 {
4962 const char *error = NULL;
4963 WSGIServerConfig *sconfig = NULL;
4964
4965 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4966 if (error != NULL)
4967 return error;
4968
4969 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
4970 sconfig->python_path = f;
4971
4972 return NULL;
4973 }
4974
wsgi_set_python_eggs(cmd_parms * cmd,void * mconfig,const char * f)4975 static const char *wsgi_set_python_eggs(cmd_parms *cmd, void *mconfig,
4976 const char *f)
4977 {
4978 const char *error = NULL;
4979 WSGIServerConfig *sconfig = NULL;
4980
4981 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4982 if (error != NULL)
4983 return error;
4984
4985 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
4986 sconfig->python_eggs = f;
4987
4988 return NULL;
4989 }
4990
wsgi_set_python_hash_seed(cmd_parms * cmd,void * mconfig,const char * f)4991 static const char *wsgi_set_python_hash_seed(cmd_parms *cmd, void *mconfig,
4992 const char *f)
4993 {
4994 const char *error = NULL;
4995 WSGIServerConfig *sconfig = NULL;
4996
4997 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
4998 if (error != NULL)
4999 return error;
5000
5001 /*
5002 * Must check this here because if we don't and is wrong, then
5003 * Python interpreter will check later and may kill the process.
5004 */
5005
5006 if (f && *f != '\0' && strcmp(f, "random") != 0) {
5007 const char *endptr = f;
5008 unsigned long seed;
5009
5010 seed = PyOS_strtoul((char *)f, (char **)&endptr, 10);
5011
5012 if (*endptr != '\0' || seed > 4294967295UL
5013 || (errno == ERANGE && seed == ULONG_MAX))
5014 {
5015 return "WSGIPythonHashSeed must be \"random\" or an integer "
5016 "in range [0; 4294967295]";
5017 }
5018 }
5019
5020 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
5021 sconfig->python_hash_seed = f;
5022
5023 return NULL;
5024 }
5025
wsgi_set_restrict_embedded(cmd_parms * cmd,void * mconfig,const char * f)5026 static const char *wsgi_set_restrict_embedded(cmd_parms *cmd, void *mconfig,
5027 const char *f)
5028 {
5029 const char *error = NULL;
5030 WSGIServerConfig *sconfig = NULL;
5031
5032 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
5033 if (error != NULL)
5034 return error;
5035
5036 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
5037
5038 if (strcasecmp(f, "Off") == 0)
5039 sconfig->restrict_embedded = 0;
5040 else if (strcasecmp(f, "On") == 0)
5041 sconfig->restrict_embedded = 1;
5042 else
5043 return "WSGIRestrictEmbedded must be one of: Off | On";
5044
5045 if (sconfig->restrict_embedded) {
5046 if (wsgi_python_required == -1)
5047 wsgi_python_required = 0;
5048 }
5049
5050 return NULL;
5051 }
5052
wsgi_set_restrict_stdin(cmd_parms * cmd,void * mconfig,const char * f)5053 static const char *wsgi_set_restrict_stdin(cmd_parms *cmd, void *mconfig,
5054 const char *f)
5055 {
5056 const char *error = NULL;
5057 WSGIServerConfig *sconfig = NULL;
5058
5059 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
5060 if (error != NULL)
5061 return error;
5062
5063 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
5064
5065 if (strcasecmp(f, "Off") == 0)
5066 sconfig->restrict_stdin = 0;
5067 else if (strcasecmp(f, "On") == 0)
5068 sconfig->restrict_stdin = 1;
5069 else
5070 return "WSGIRestrictStdin must be one of: Off | On";
5071
5072 return NULL;
5073 }
5074
wsgi_set_restrict_stdout(cmd_parms * cmd,void * mconfig,const char * f)5075 static const char *wsgi_set_restrict_stdout(cmd_parms *cmd, void *mconfig,
5076 const char *f)
5077 {
5078 const char *error = NULL;
5079 WSGIServerConfig *sconfig = NULL;
5080
5081 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
5082 if (error != NULL)
5083 return error;
5084
5085 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
5086
5087 if (strcasecmp(f, "Off") == 0)
5088 sconfig->restrict_stdout = 0;
5089 else if (strcasecmp(f, "On") == 0)
5090 sconfig->restrict_stdout = 1;
5091 else
5092 return "WSGIRestrictStdout must be one of: Off | On";
5093
5094 return NULL;
5095 }
5096
wsgi_set_restrict_signal(cmd_parms * cmd,void * mconfig,const char * f)5097 static const char *wsgi_set_restrict_signal(cmd_parms *cmd, void *mconfig,
5098 const char *f)
5099 {
5100 const char *error = NULL;
5101 WSGIServerConfig *sconfig = NULL;
5102
5103 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
5104 if (error != NULL)
5105 return error;
5106
5107 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
5108
5109 if (strcasecmp(f, "Off") == 0)
5110 sconfig->restrict_signal = 0;
5111 else if (strcasecmp(f, "On") == 0)
5112 sconfig->restrict_signal = 1;
5113 else
5114 return "WSGIRestrictSignal must be one of: Off | On";
5115
5116 return NULL;
5117 }
5118
wsgi_set_case_sensitivity(cmd_parms * cmd,void * mconfig,const char * f)5119 static const char *wsgi_set_case_sensitivity(cmd_parms *cmd, void *mconfig,
5120 const char *f)
5121 {
5122 const char *error = NULL;
5123 WSGIServerConfig *sconfig = NULL;
5124
5125 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
5126 if (error != NULL)
5127 return error;
5128
5129 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
5130
5131 if (strcasecmp(f, "Off") == 0)
5132 sconfig->case_sensitivity = 0;
5133 else if (strcasecmp(f, "On") == 0)
5134 sconfig->case_sensitivity = 1;
5135 else
5136 return "WSGICaseSensitivity must be one of: Off | On";
5137
5138 return NULL;
5139 }
5140
wsgi_set_restrict_process(cmd_parms * cmd,void * mconfig,const char * args)5141 static const char *wsgi_set_restrict_process(cmd_parms *cmd, void *mconfig,
5142 const char *args)
5143 {
5144 apr_table_t *index = apr_table_make(cmd->pool, 5);
5145
5146 if (cmd->path) {
5147 WSGIDirectoryConfig *dconfig = NULL;
5148 dconfig = (WSGIDirectoryConfig *)mconfig;
5149
5150 dconfig->restrict_process = index;
5151 }
5152 else {
5153 WSGIServerConfig *sconfig = NULL;
5154 sconfig = ap_get_module_config(cmd->server->module_config,
5155 &wsgi_module);
5156
5157 sconfig->restrict_process = index;
5158 }
5159
5160 while (*args) {
5161 const char *option;
5162
5163 option = ap_getword_conf(cmd->pool, &args);
5164
5165 if (!strcmp(option, "%{GLOBAL}"))
5166 option = "";
5167
5168 apr_table_setn(index, option, option);
5169 }
5170
5171 return NULL;
5172 }
5173
wsgi_set_process_group(cmd_parms * cmd,void * mconfig,const char * n)5174 static const char *wsgi_set_process_group(cmd_parms *cmd, void *mconfig,
5175 const char *n)
5176 {
5177 if (cmd->path) {
5178 WSGIDirectoryConfig *dconfig = NULL;
5179 dconfig = (WSGIDirectoryConfig *)mconfig;
5180 dconfig->process_group = n;
5181 }
5182 else {
5183 WSGIServerConfig *sconfig = NULL;
5184 sconfig = ap_get_module_config(cmd->server->module_config,
5185 &wsgi_module);
5186 sconfig->process_group = n;
5187 }
5188
5189 return NULL;
5190 }
5191
wsgi_set_application_group(cmd_parms * cmd,void * mconfig,const char * n)5192 static const char *wsgi_set_application_group(cmd_parms *cmd, void *mconfig,
5193 const char *n)
5194 {
5195 if (cmd->path) {
5196 WSGIDirectoryConfig *dconfig = NULL;
5197 dconfig = (WSGIDirectoryConfig *)mconfig;
5198 dconfig->application_group = n;
5199 }
5200 else {
5201 WSGIServerConfig *sconfig = NULL;
5202 sconfig = ap_get_module_config(cmd->server->module_config,
5203 &wsgi_module);
5204 sconfig->application_group = n;
5205 }
5206
5207 return NULL;
5208 }
5209
wsgi_set_callable_object(cmd_parms * cmd,void * mconfig,const char * n)5210 static const char *wsgi_set_callable_object(cmd_parms *cmd, void *mconfig,
5211 const char *n)
5212 {
5213 if (cmd->path) {
5214 WSGIDirectoryConfig *dconfig = NULL;
5215 dconfig = (WSGIDirectoryConfig *)mconfig;
5216 dconfig->callable_object = n;
5217 }
5218 else {
5219 WSGIServerConfig *sconfig = NULL;
5220 sconfig = ap_get_module_config(cmd->server->module_config,
5221 &wsgi_module);
5222 sconfig->callable_object = n;
5223 }
5224
5225 return NULL;
5226 }
5227
wsgi_add_import_script(cmd_parms * cmd,void * mconfig,const char * args)5228 static const char *wsgi_add_import_script(cmd_parms *cmd, void *mconfig,
5229 const char *args)
5230 {
5231 WSGIScriptFile *object = NULL;
5232
5233 const char *option = NULL;
5234 const char *value = NULL;
5235
5236 if (!wsgi_import_list) {
5237 wsgi_import_list = apr_array_make(cmd->pool, 20,
5238 sizeof(WSGIScriptFile));
5239 }
5240
5241 object = (WSGIScriptFile *)apr_array_push(wsgi_import_list);
5242
5243 object->handler_script = ap_getword_conf(cmd->pool, &args);
5244 object->process_group = NULL;
5245 object->application_group = NULL;
5246
5247 if (!object->handler_script || !*object->handler_script)
5248 return "Location of import script not supplied.";
5249
5250 while (*args) {
5251 if (wsgi_parse_option(cmd->pool, &args, &option,
5252 &value) != APR_SUCCESS) {
5253 return "Invalid option to WSGI import script definition.";
5254 }
5255
5256 if (!strcmp(option, "application-group")) {
5257 if (!*value)
5258 return "Invalid name for WSGI application group.";
5259
5260 object->application_group = value;
5261 }
5262 #if defined(MOD_WSGI_WITH_DAEMONS)
5263 else if (!strcmp(option, "process-group")) {
5264 if (!*value)
5265 return "Invalid name for WSGI process group.";
5266
5267 object->process_group = value;
5268 }
5269 #endif
5270 else
5271 return "Invalid option to WSGI import script definition.";
5272 }
5273
5274 if (!object->application_group)
5275 return "Name of WSGI application group required.";
5276
5277 if (!strcmp(object->application_group, "%{GLOBAL}"))
5278 object->application_group = "";
5279
5280 #if defined(MOD_WSGI_WITH_DAEMONS)
5281 if (!object->process_group)
5282 return "Name of WSGI process group required.";
5283
5284 if (!strcmp(object->process_group, "%{GLOBAL}"))
5285 object->process_group = "";
5286
5287 if (*object->process_group) {
5288 WSGIProcessGroup *group = NULL;
5289 WSGIProcessGroup *entries = NULL;
5290 WSGIProcessGroup *entry = NULL;
5291 int i;
5292
5293 if (!wsgi_daemon_list)
5294 return "WSGI process group not yet configured.";
5295
5296 entries = (WSGIProcessGroup *)wsgi_daemon_list->elts;
5297
5298 for (i = 0; i < wsgi_daemon_list->nelts; ++i) {
5299 entry = &entries[i];
5300
5301 if (!strcmp(entry->name, object->process_group)) {
5302 group = entry;
5303 break;
5304 }
5305 }
5306
5307 if (!group)
5308 return "WSGI process group not yet configured.";
5309
5310 if (cmd->server->server_hostname &&
5311 group->server->server_hostname &&
5312 strcmp(cmd->server->server_hostname,
5313 group->server->server_hostname) &&
5314 group->server->is_virtual) {
5315
5316 return "WSGI process group not accessible.";
5317 }
5318
5319 if (!cmd->server->server_hostname &&
5320 group->server->server_hostname &&
5321 group->server->is_virtual) {
5322
5323 return "WSGI process group not matchable.";
5324 }
5325
5326 if (cmd->server->server_hostname &&
5327 !group->server->server_hostname &&
5328 group->server->is_virtual) {
5329
5330 return "WSGI process group not matchable.";
5331 }
5332 }
5333 #else
5334 object->process_group = "";
5335 #endif
5336
5337 if (!*object->process_group)
5338 wsgi_python_required = 1;
5339
5340 return NULL;
5341 }
5342
wsgi_set_dispatch_script(cmd_parms * cmd,void * mconfig,const char * args)5343 static const char *wsgi_set_dispatch_script(cmd_parms *cmd, void *mconfig,
5344 const char *args)
5345 {
5346 WSGIScriptFile *object = NULL;
5347
5348 const char *option = NULL;
5349 const char *value = NULL;
5350
5351 object = newWSGIScriptFile(cmd->pool);
5352
5353 object->handler_script = ap_getword_conf(cmd->pool, &args);
5354
5355 if (!object->handler_script || !*object->handler_script)
5356 return "Location of dispatch script not supplied.";
5357
5358 while (*args) {
5359 if (wsgi_parse_option(cmd->pool, &args, &option,
5360 &value) != APR_SUCCESS) {
5361 return "Invalid option to WSGI dispatch script definition.";
5362 }
5363
5364 if (!strcmp(option, "application-group")) {
5365 if (!*value)
5366 return "Invalid name for WSGI application group.";
5367
5368 object->application_group = value;
5369 }
5370 else
5371 return "Invalid option to WSGI dispatch script definition.";
5372 }
5373
5374 if (cmd->path) {
5375 WSGIDirectoryConfig *dconfig = NULL;
5376 dconfig = (WSGIDirectoryConfig *)mconfig;
5377 dconfig->dispatch_script = object;
5378 }
5379 else {
5380 WSGIServerConfig *sconfig = NULL;
5381 sconfig = ap_get_module_config(cmd->server->module_config,
5382 &wsgi_module);
5383 sconfig->dispatch_script = object;
5384 }
5385
5386 wsgi_python_required = 1;
5387
5388 return NULL;
5389 }
5390
wsgi_set_pass_apache_request(cmd_parms * cmd,void * mconfig,const char * f)5391 static const char *wsgi_set_pass_apache_request(cmd_parms *cmd, void *mconfig,
5392 const char *f)
5393 {
5394 if (cmd->path) {
5395 WSGIDirectoryConfig *dconfig = NULL;
5396 dconfig = (WSGIDirectoryConfig *)mconfig;
5397
5398 if (strcasecmp(f, "Off") == 0)
5399 dconfig->pass_apache_request = 0;
5400 else if (strcasecmp(f, "On") == 0)
5401 dconfig->pass_apache_request = 1;
5402 else
5403 return "WSGIPassApacheRequest must be one of: Off | On";
5404 }
5405 else {
5406 WSGIServerConfig *sconfig = NULL;
5407 sconfig = ap_get_module_config(cmd->server->module_config,
5408 &wsgi_module);
5409
5410 if (strcasecmp(f, "Off") == 0)
5411 sconfig->pass_apache_request = 0;
5412 else if (strcasecmp(f, "On") == 0)
5413 sconfig->pass_apache_request = 1;
5414 else
5415 return "WSGIPassApacheRequest must be one of: Off | On";
5416 }
5417
5418 return NULL;
5419 }
5420
wsgi_set_pass_authorization(cmd_parms * cmd,void * mconfig,const char * f)5421 static const char *wsgi_set_pass_authorization(cmd_parms *cmd, void *mconfig,
5422 const char *f)
5423 {
5424 if (cmd->path) {
5425 WSGIDirectoryConfig *dconfig = NULL;
5426 dconfig = (WSGIDirectoryConfig *)mconfig;
5427
5428 if (strcasecmp(f, "Off") == 0)
5429 dconfig->pass_authorization = 0;
5430 else if (strcasecmp(f, "On") == 0)
5431 dconfig->pass_authorization = 1;
5432 else
5433 return "WSGIPassAuthorization must be one of: Off | On";
5434 }
5435 else {
5436 WSGIServerConfig *sconfig = NULL;
5437 sconfig = ap_get_module_config(cmd->server->module_config,
5438 &wsgi_module);
5439
5440 if (strcasecmp(f, "Off") == 0)
5441 sconfig->pass_authorization = 0;
5442 else if (strcasecmp(f, "On") == 0)
5443 sconfig->pass_authorization = 1;
5444 else
5445 return "WSGIPassAuthorization must be one of: Off | On";
5446 }
5447
5448 return NULL;
5449 }
5450
wsgi_set_script_reloading(cmd_parms * cmd,void * mconfig,const char * f)5451 static const char *wsgi_set_script_reloading(cmd_parms *cmd, void *mconfig,
5452 const char *f)
5453 {
5454 if (cmd->path) {
5455 WSGIDirectoryConfig *dconfig = NULL;
5456 dconfig = (WSGIDirectoryConfig *)mconfig;
5457
5458 if (strcasecmp(f, "Off") == 0)
5459 dconfig->script_reloading = 0;
5460 else if (strcasecmp(f, "On") == 0)
5461 dconfig->script_reloading = 1;
5462 else
5463 return "WSGIScriptReloading must be one of: Off | On";
5464 }
5465 else {
5466 WSGIServerConfig *sconfig = NULL;
5467 sconfig = ap_get_module_config(cmd->server->module_config,
5468 &wsgi_module);
5469
5470 if (strcasecmp(f, "Off") == 0)
5471 sconfig->script_reloading = 0;
5472 else if (strcasecmp(f, "On") == 0)
5473 sconfig->script_reloading = 1;
5474 else
5475 return "WSGIScriptReloading must be one of: Off | On";
5476 }
5477
5478 return NULL;
5479 }
5480
wsgi_set_error_override(cmd_parms * cmd,void * mconfig,const char * f)5481 static const char *wsgi_set_error_override(cmd_parms *cmd, void *mconfig,
5482 const char *f)
5483 {
5484 if (cmd->path) {
5485 WSGIDirectoryConfig *dconfig = NULL;
5486 dconfig = (WSGIDirectoryConfig *)mconfig;
5487
5488 if (strcasecmp(f, "Off") == 0)
5489 dconfig->error_override = 0;
5490 else if (strcasecmp(f, "On") == 0)
5491 dconfig->error_override = 1;
5492 else
5493 return "WSGIErrorOverride must be one of: Off | On";
5494 }
5495 else {
5496 WSGIServerConfig *sconfig = NULL;
5497 sconfig = ap_get_module_config(cmd->server->module_config,
5498 &wsgi_module);
5499
5500 if (strcasecmp(f, "Off") == 0)
5501 sconfig->error_override = 0;
5502 else if (strcasecmp(f, "On") == 0)
5503 sconfig->error_override = 1;
5504 else
5505 return "WSGIErrorOverride must be one of: Off | On";
5506 }
5507
5508 return NULL;
5509 }
5510
wsgi_set_chunked_request(cmd_parms * cmd,void * mconfig,const char * f)5511 static const char *wsgi_set_chunked_request(cmd_parms *cmd, void *mconfig,
5512 const char *f)
5513 {
5514 if (cmd->path) {
5515 WSGIDirectoryConfig *dconfig = NULL;
5516 dconfig = (WSGIDirectoryConfig *)mconfig;
5517
5518 if (strcasecmp(f, "Off") == 0)
5519 dconfig->chunked_request = 0;
5520 else if (strcasecmp(f, "On") == 0)
5521 dconfig->chunked_request = 1;
5522 else
5523 return "WSGIChunkedRequest must be one of: Off | On";
5524 }
5525 else {
5526 WSGIServerConfig *sconfig = NULL;
5527 sconfig = ap_get_module_config(cmd->server->module_config,
5528 &wsgi_module);
5529
5530 if (strcasecmp(f, "Off") == 0)
5531 sconfig->chunked_request = 0;
5532 else if (strcasecmp(f, "On") == 0)
5533 sconfig->chunked_request = 1;
5534 else
5535 return "WSGIChunkedRequest must be one of: Off | On";
5536 }
5537
5538 return NULL;
5539 }
5540
wsgi_set_map_head_to_get(cmd_parms * cmd,void * mconfig,const char * f)5541 static const char *wsgi_set_map_head_to_get(cmd_parms *cmd, void *mconfig,
5542 const char *f)
5543 {
5544 if (cmd->path) {
5545 WSGIDirectoryConfig *dconfig = NULL;
5546 dconfig = (WSGIDirectoryConfig *)mconfig;
5547
5548 if (strcasecmp(f, "Off") == 0)
5549 dconfig->map_head_to_get = 0;
5550 else if (strcasecmp(f, "On") == 0)
5551 dconfig->map_head_to_get = 1;
5552 else if (strcasecmp(f, "Auto") == 0)
5553 dconfig->map_head_to_get = 2;
5554 else
5555 return "WSGIMapHEADToGET must be one of: Off | On | Auto";
5556 }
5557 else {
5558 WSGIServerConfig *sconfig = NULL;
5559 sconfig = ap_get_module_config(cmd->server->module_config,
5560 &wsgi_module);
5561
5562 if (strcasecmp(f, "Off") == 0)
5563 sconfig->map_head_to_get = 0;
5564 else if (strcasecmp(f, "On") == 0)
5565 sconfig->map_head_to_get = 1;
5566 else if (strcasecmp(f, "Auto") == 0)
5567 sconfig->map_head_to_get = 2;
5568 else
5569 return "WSGIMapHEADToGET must be one of: Off | On | Auto";
5570 }
5571
5572 return NULL;
5573 }
5574
wsgi_set_ignore_activity(cmd_parms * cmd,void * mconfig,const char * f)5575 static const char *wsgi_set_ignore_activity(cmd_parms *cmd, void *mconfig,
5576 const char *f)
5577 {
5578 if (cmd->path) {
5579 WSGIDirectoryConfig *dconfig = NULL;
5580 dconfig = (WSGIDirectoryConfig *)mconfig;
5581
5582 if (strcasecmp(f, "Off") == 0)
5583 dconfig->ignore_activity = 0;
5584 else if (strcasecmp(f, "On") == 0)
5585 dconfig->ignore_activity = 1;
5586 else
5587 return "WSGIIgnoreActivity must be one of: Off | On";
5588 }
5589 else {
5590 WSGIServerConfig *sconfig = NULL;
5591 sconfig = ap_get_module_config(cmd->server->module_config,
5592 &wsgi_module);
5593
5594 if (strcasecmp(f, "Off") == 0)
5595 sconfig->ignore_activity = 0;
5596 else if (strcasecmp(f, "On") == 0)
5597 sconfig->ignore_activity = 1;
5598 else
5599 return "WSGIIgnoreActivity must be one of: Off | On";
5600 }
5601
5602 return NULL;
5603 }
5604
5605 static char *wsgi_http2env(apr_pool_t *a, const char *w);
5606
wsgi_set_trusted_proxy_headers(cmd_parms * cmd,void * mconfig,const char * args)5607 static const char *wsgi_set_trusted_proxy_headers(cmd_parms *cmd,
5608 void *mconfig,
5609 const char *args)
5610 {
5611 apr_array_header_t *headers = NULL;
5612
5613 if (cmd->path) {
5614 WSGIDirectoryConfig *dconfig = NULL;
5615 dconfig = (WSGIDirectoryConfig *)mconfig;
5616
5617 if (!dconfig->trusted_proxy_headers) {
5618 headers = apr_array_make(cmd->pool, 3, sizeof(char*));
5619 dconfig->trusted_proxy_headers = headers;
5620 }
5621 else
5622 headers = dconfig->trusted_proxy_headers;
5623 }
5624 else {
5625 WSGIServerConfig *sconfig = NULL;
5626 sconfig = ap_get_module_config(cmd->server->module_config,
5627 &wsgi_module);
5628
5629 if (!sconfig->trusted_proxy_headers) {
5630 headers = apr_array_make(cmd->pool, 3, sizeof(char*));
5631 sconfig->trusted_proxy_headers = headers;
5632 }
5633 else
5634 headers = sconfig->trusted_proxy_headers;
5635 }
5636
5637 while (*args) {
5638 const char **entry = NULL;
5639
5640 entry = (const char **)apr_array_push(headers);
5641 *entry = wsgi_http2env(cmd->pool, ap_getword_conf(cmd->pool, &args));
5642 }
5643
5644 return NULL;
5645 }
5646
wsgi_looks_like_ip(const char * ip)5647 static int wsgi_looks_like_ip(const char *ip) {
5648 static const char ipv4_set[] = "0123456789./";
5649 static const char ipv6_set[] = "0123456789abcdef:/";
5650
5651 const char *ptr;
5652
5653 /* Zero length value is not valid. */
5654
5655 if (!*ip)
5656 return 0;
5657
5658 /* Determine if this could be a IPv6 or IPv4 address. */
5659
5660 ptr = ip;
5661
5662 if (strchr(ip, ':')) {
5663 while(*ptr && strchr(ipv6_set, *ptr) != NULL)
5664 ++ptr;
5665 }
5666 else {
5667 while(*ptr && strchr(ipv4_set, *ptr) != NULL)
5668 ++ptr;
5669 }
5670
5671 return (*ptr == '\0');
5672 }
5673
wsgi_set_trusted_proxies(cmd_parms * cmd,void * mconfig,const char * args)5674 static const char *wsgi_set_trusted_proxies(cmd_parms *cmd,
5675 void *mconfig, const char *args)
5676 {
5677 apr_array_header_t *proxy_ips = NULL;
5678
5679 if (cmd->path) {
5680 WSGIDirectoryConfig *dconfig = NULL;
5681 dconfig = (WSGIDirectoryConfig *)mconfig;
5682
5683 if (!dconfig->trusted_proxies) {
5684 proxy_ips = apr_array_make(cmd->pool, 3, sizeof(char*));
5685 dconfig->trusted_proxies = proxy_ips;
5686 }
5687 else
5688 proxy_ips = dconfig->trusted_proxies;
5689 }
5690 else {
5691 WSGIServerConfig *sconfig = NULL;
5692 sconfig = ap_get_module_config(cmd->server->module_config,
5693 &wsgi_module);
5694
5695 if (!sconfig->trusted_proxies) {
5696 proxy_ips = apr_array_make(cmd->pool, 3, sizeof(char*));
5697 sconfig->trusted_proxies = proxy_ips;
5698 }
5699 else
5700 proxy_ips = sconfig->trusted_proxies;
5701 }
5702
5703 while (*args) {
5704 const char *proxy_ip;
5705
5706 proxy_ip = ap_getword_conf(cmd->pool, &args);
5707
5708 if (wsgi_looks_like_ip(proxy_ip)) {
5709 char *ip;
5710 char *mask;
5711 apr_ipsubnet_t **sub;
5712 apr_status_t rv;
5713
5714 ip = apr_pstrdup(cmd->temp_pool, proxy_ip);
5715
5716 if ((mask = ap_strchr(ip, '/')))
5717 *mask++ = '\0';
5718
5719 sub = (apr_ipsubnet_t **)apr_array_push(proxy_ips);
5720
5721 rv = apr_ipsubnet_create(sub, ip, mask, cmd->pool);
5722
5723 if (rv != APR_SUCCESS) {
5724 char msgbuf[128];
5725 apr_strerror(rv, msgbuf, sizeof(msgbuf));
5726
5727 return apr_pstrcat(cmd->pool, "Unable to parse trusted "
5728 "proxy IP address/subnet of \"", proxy_ip,
5729 "\". ", msgbuf, NULL);
5730 }
5731 }
5732 else {
5733 return apr_pstrcat(cmd->pool, "Unable to parse trusted proxy "
5734 "IP address/subnet of \"", proxy_ip, "\".",
5735 NULL);
5736 }
5737 }
5738
5739 return NULL;
5740 }
5741
wsgi_set_enable_sendfile(cmd_parms * cmd,void * mconfig,const char * f)5742 static const char *wsgi_set_enable_sendfile(cmd_parms *cmd, void *mconfig,
5743 const char *f)
5744 {
5745 if (cmd->path) {
5746 WSGIDirectoryConfig *dconfig = NULL;
5747 dconfig = (WSGIDirectoryConfig *)mconfig;
5748
5749 if (strcasecmp(f, "Off") == 0)
5750 dconfig->enable_sendfile = 0;
5751 else if (strcasecmp(f, "On") == 0)
5752 dconfig->enable_sendfile = 1;
5753 else
5754 return "WSGIEnableSendfile must be one of: Off | On";
5755 }
5756 else {
5757 WSGIServerConfig *sconfig = NULL;
5758 sconfig = ap_get_module_config(cmd->server->module_config,
5759 &wsgi_module);
5760
5761 if (strcasecmp(f, "Off") == 0)
5762 sconfig->enable_sendfile = 0;
5763 else if (strcasecmp(f, "On") == 0)
5764 sconfig->enable_sendfile = 1;
5765 else
5766 return "WSGIEnableSendfile must be one of: Off | On";
5767 }
5768
5769 return NULL;
5770 }
5771
wsgi_set_access_script(cmd_parms * cmd,void * mconfig,const char * args)5772 static const char *wsgi_set_access_script(cmd_parms *cmd, void *mconfig,
5773 const char *args)
5774 {
5775 WSGIDirectoryConfig *dconfig = NULL;
5776 WSGIScriptFile *object = NULL;
5777
5778 const char *option = NULL;
5779 const char *value = NULL;
5780
5781 object = newWSGIScriptFile(cmd->pool);
5782
5783 object->handler_script = ap_getword_conf(cmd->pool, &args);
5784
5785 if (!object->handler_script || !*object->handler_script)
5786 return "Location of access script not supplied.";
5787
5788 while (*args) {
5789 if (wsgi_parse_option(cmd->pool, &args, &option,
5790 &value) != APR_SUCCESS) {
5791 return "Invalid option to WSGI access script definition.";
5792 }
5793
5794 if (!strcmp(option, "application-group")) {
5795 if (!*value)
5796 return "Invalid name for WSGI application group.";
5797
5798 object->application_group = value;
5799 }
5800 else
5801 return "Invalid option to WSGI access script definition.";
5802 }
5803
5804 dconfig = (WSGIDirectoryConfig *)mconfig;
5805 dconfig->access_script = object;
5806
5807 wsgi_python_required = 1;
5808
5809 return NULL;
5810 }
5811
wsgi_set_auth_user_script(cmd_parms * cmd,void * mconfig,const char * args)5812 static const char *wsgi_set_auth_user_script(cmd_parms *cmd, void *mconfig,
5813 const char *args)
5814 {
5815 WSGIDirectoryConfig *dconfig = NULL;
5816 WSGIScriptFile *object = NULL;
5817
5818 const char *option = NULL;
5819 const char *value = NULL;
5820
5821 object = newWSGIScriptFile(cmd->pool);
5822
5823 object->handler_script = ap_getword_conf(cmd->pool, &args);
5824
5825 if (!object->handler_script || !*object->handler_script)
5826 return "Location of auth user script not supplied.";
5827
5828 while (*args) {
5829 if (wsgi_parse_option(cmd->pool, &args, &option,
5830 &value) != APR_SUCCESS) {
5831 return "Invalid option to WSGI auth user script definition.";
5832 }
5833
5834 if (!strcmp(option, "application-group")) {
5835 if (!*value)
5836 return "Invalid name for WSGI application group.";
5837
5838 object->application_group = value;
5839 }
5840 else
5841 return "Invalid option to WSGI auth user script definition.";
5842 }
5843
5844 dconfig = (WSGIDirectoryConfig *)mconfig;
5845 dconfig->auth_user_script = object;
5846
5847 wsgi_python_required = 1;
5848
5849 return NULL;
5850 }
5851
wsgi_set_auth_group_script(cmd_parms * cmd,void * mconfig,const char * args)5852 static const char *wsgi_set_auth_group_script(cmd_parms *cmd, void *mconfig,
5853 const char *args)
5854 {
5855 WSGIDirectoryConfig *dconfig = NULL;
5856 WSGIScriptFile *object = NULL;
5857
5858 const char *option = NULL;
5859 const char *value = NULL;
5860
5861 object = newWSGIScriptFile(cmd->pool);
5862
5863 object->handler_script = ap_getword_conf(cmd->pool, &args);
5864
5865 if (!object->handler_script || !*object->handler_script)
5866 return "Location of auth group script not supplied.";
5867
5868 while (*args) {
5869 if (wsgi_parse_option(cmd->pool, &args, &option,
5870 &value) != APR_SUCCESS) {
5871 return "Invalid option to WSGI auth group script definition.";
5872 }
5873
5874 if (!strcmp(option, "application-group")) {
5875 if (!*value)
5876 return "Invalid name for WSGI application group.";
5877
5878 object->application_group = value;
5879 }
5880 else
5881 return "Invalid option to WSGI auth group script definition.";
5882 }
5883
5884 dconfig = (WSGIDirectoryConfig *)mconfig;
5885 dconfig->auth_group_script = object;
5886
5887 wsgi_python_required = 1;
5888
5889 return NULL;
5890 }
5891
5892 #if !defined(MOD_WSGI_WITH_AUTHN_PROVIDER)
wsgi_set_user_authoritative(cmd_parms * cmd,void * mconfig,const char * f)5893 static const char *wsgi_set_user_authoritative(cmd_parms *cmd, void *mconfig,
5894 const char *f)
5895 {
5896 WSGIDirectoryConfig *dconfig = NULL;
5897 dconfig = (WSGIDirectoryConfig *)mconfig;
5898
5899 if (strcasecmp(f, "Off") == 0)
5900 dconfig->user_authoritative = 0;
5901 else if (strcasecmp(f, "On") == 0)
5902 dconfig->user_authoritative = 1;
5903 else
5904 return "WSGIUserAuthoritative must be one of: Off | On";
5905
5906 return NULL;
5907 }
5908 #endif
5909
wsgi_set_group_authoritative(cmd_parms * cmd,void * mconfig,const char * f)5910 static const char *wsgi_set_group_authoritative(cmd_parms *cmd, void *mconfig,
5911 const char *f)
5912 {
5913 WSGIDirectoryConfig *dconfig = NULL;
5914 dconfig = (WSGIDirectoryConfig *)mconfig;
5915
5916 if (strcasecmp(f, "Off") == 0)
5917 dconfig->group_authoritative = 0;
5918 else if (strcasecmp(f, "On") == 0)
5919 dconfig->group_authoritative = 1;
5920 else
5921 return "WSGIGroupAuthoritative must be one of: Off | On";
5922
5923 return NULL;
5924 }
5925
wsgi_add_handler_script(cmd_parms * cmd,void * mconfig,const char * args)5926 static const char *wsgi_add_handler_script(cmd_parms *cmd, void *mconfig,
5927 const char *args)
5928 {
5929 WSGIScriptFile *object = NULL;
5930
5931 const char *name = NULL;
5932 const char *option = NULL;
5933 const char *value = NULL;
5934
5935 name = ap_getword_conf(cmd->pool, &args);
5936
5937 if (!name || !*name)
5938 return "Name for handler script not supplied.";
5939
5940 object = newWSGIScriptFile(cmd->pool);
5941
5942 object->handler_script = ap_getword_conf(cmd->pool, &args);
5943
5944 if (!object->handler_script || !*object->handler_script)
5945 return "Location of handler script not supplied.";
5946
5947 while (*args) {
5948 if (wsgi_parse_option(cmd->pool, &args, &option,
5949 &value) != APR_SUCCESS) {
5950 return "Invalid option to WSGI handler script definition.";
5951 }
5952
5953 if (!strcmp(option, "process-group")) {
5954 if (!*value)
5955 return "Invalid name for WSGI process group.";
5956
5957 object->process_group = value;
5958 }
5959 else if (!strcmp(option, "application-group")) {
5960 if (!*value)
5961 return "Invalid name for WSGI application group.";
5962
5963 object->application_group = value;
5964 }
5965 else if (!strcmp(option, "pass-authorization")) {
5966 if (!*value)
5967 return "Invalid value for authorization flag.";
5968
5969 if (strcasecmp(value, "Off") == 0)
5970 object->pass_authorization = "0";
5971 else if (strcasecmp(value, "On") == 0)
5972 object->pass_authorization = "1";
5973 else
5974 return "Invalid value for authorization flag.";
5975 }
5976 else
5977 return "Invalid option to WSGI handler script definition.";
5978 }
5979
5980 if (cmd->path) {
5981 WSGIDirectoryConfig *dconfig = NULL;
5982 dconfig = (WSGIDirectoryConfig *)mconfig;
5983
5984 if (!dconfig->handler_scripts)
5985 dconfig->handler_scripts = apr_hash_make(cmd->pool);
5986
5987 apr_hash_set(dconfig->handler_scripts, name, APR_HASH_KEY_STRING,
5988 object);
5989 }
5990 else {
5991 WSGIServerConfig *sconfig = NULL;
5992 sconfig = ap_get_module_config(cmd->server->module_config,
5993 &wsgi_module);
5994
5995 if (!sconfig->handler_scripts)
5996 sconfig->handler_scripts = apr_hash_make(cmd->pool);
5997
5998 apr_hash_set(sconfig->handler_scripts, name, APR_HASH_KEY_STRING,
5999 object);
6000 }
6001
6002 return NULL;
6003 }
6004
wsgi_set_server_metrics(cmd_parms * cmd,void * mconfig,const char * f)6005 static const char *wsgi_set_server_metrics(cmd_parms *cmd, void *mconfig,
6006 const char *f)
6007 {
6008 const char *error = NULL;
6009 WSGIServerConfig *sconfig = NULL;
6010
6011 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
6012 if (error != NULL)
6013 return error;
6014
6015 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
6016
6017 if (strcasecmp(f, "Off") == 0)
6018 sconfig->server_metrics = 0;
6019 else if (strcasecmp(f, "On") == 0)
6020 sconfig->server_metrics = 1;
6021 else
6022 return "WSGIServerMetrics must be one of: Off | On";
6023
6024 return NULL;
6025 }
6026
wsgi_set_newrelic_config_file(cmd_parms * cmd,void * mconfig,const char * f)6027 static const char *wsgi_set_newrelic_config_file(
6028 cmd_parms *cmd, void *mconfig, const char *f)
6029 {
6030 const char *error = NULL;
6031 WSGIServerConfig *sconfig = NULL;
6032
6033 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
6034 if (error != NULL)
6035 return error;
6036
6037 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
6038 sconfig->newrelic_config_file = f;
6039
6040 return NULL;
6041 }
6042
wsgi_set_newrelic_environment(cmd_parms * cmd,void * mconfig,const char * f)6043 static const char *wsgi_set_newrelic_environment(
6044 cmd_parms *cmd, void *mconfig, const char *f)
6045 {
6046 const char *error = NULL;
6047 WSGIServerConfig *sconfig = NULL;
6048
6049 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
6050 if (error != NULL)
6051 return error;
6052
6053 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
6054 sconfig->newrelic_environment = f;
6055
6056 return NULL;
6057 }
6058
6059 /* Handler for the translate name phase. */
6060
wsgi_alias_matches(const char * uri,const char * alias_fakename)6061 static long wsgi_alias_matches(const char *uri, const char *alias_fakename)
6062 {
6063 /* Code for this function from Apache mod_alias module. */
6064
6065 const char *aliasp = alias_fakename, *urip = uri;
6066
6067 while (*aliasp) {
6068 if (*aliasp == '/') {
6069 /* any number of '/' in the alias matches any number in
6070 * the supplied URI, but there must be at least one...
6071 */
6072 if (*urip != '/')
6073 return 0;
6074
6075 do {
6076 ++aliasp;
6077 } while (*aliasp == '/');
6078 do {
6079 ++urip;
6080 } while (*urip == '/');
6081 }
6082 else {
6083 /* Other characters are compared literally */
6084 if (*urip++ != *aliasp++)
6085 return 0;
6086 }
6087 }
6088
6089 /* Check last alias path component matched all the way */
6090
6091 if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
6092 return 0;
6093
6094 /* Return number of characters from URI which matched (may be
6095 * greater than length of alias, since we may have matched
6096 * doubled slashes)
6097 */
6098
6099 return urip - uri;
6100 }
6101
wsgi_hook_intercept(request_rec * r)6102 static int wsgi_hook_intercept(request_rec *r)
6103 {
6104 WSGIServerConfig *config = NULL;
6105
6106 apr_array_header_t *aliases = NULL;
6107
6108 WSGIAliasEntry *entries = NULL;
6109 WSGIAliasEntry *entry = NULL;
6110
6111 ap_regmatch_t matches[AP_MAX_REG_MATCH];
6112
6113 const char *location = NULL;
6114 const char *application = NULL;
6115
6116 int i = 0;
6117
6118 config = ap_get_module_config(r->server->module_config, &wsgi_module);
6119
6120 if (!config->alias_list)
6121 return DECLINED;
6122
6123 if (r->uri[0] != '/' && r->uri[0])
6124 return DECLINED;
6125
6126 aliases = config->alias_list;
6127 entries = (WSGIAliasEntry *)aliases->elts;
6128
6129 for (i = 0; i < aliases->nelts; ++i) {
6130 long l = 0;
6131
6132 entry = &entries[i];
6133
6134 if (entry->regexp) {
6135 if (!ap_regexec(entry->regexp, r->uri, AP_MAX_REG_MATCH,
6136 matches, 0)) {
6137 if (entry->application) {
6138 l = matches[0].rm_eo;
6139
6140 location = apr_pstrndup(r->pool, r->uri, l);
6141 application = ap_pregsub(r->pool, entry->application,
6142 r->uri, AP_MAX_REG_MATCH,
6143 matches);
6144 }
6145 }
6146 }
6147 else if (entry->location) {
6148 l = wsgi_alias_matches(r->uri, entry->location);
6149
6150 location = entry->location;
6151 application = entry->application;
6152 }
6153
6154 if (l > 0) {
6155 if (!strcmp(location, "/")) {
6156 r->filename = apr_pstrcat(r->pool, application,
6157 r->uri, NULL);
6158 }
6159 else {
6160 r->filename = apr_pstrcat(r->pool, application,
6161 r->uri + l, NULL);
6162 }
6163
6164 r->handler = "wsgi-script";
6165 apr_table_setn(r->notes, "alias-forced-type", r->handler);
6166
6167 if (entry->process_group) {
6168 apr_table_setn(r->notes, "mod_wsgi.process_group",
6169 entry->process_group);
6170 }
6171 if (entry->application_group) {
6172 apr_table_setn(r->notes, "mod_wsgi.application_group",
6173 entry->application_group);
6174 }
6175 if (entry->callable_object) {
6176 apr_table_setn(r->notes, "mod_wsgi.callable_object",
6177 entry->callable_object);
6178 }
6179
6180 if (entry->pass_authorization == 0)
6181 apr_table_setn(r->notes, "mod_wsgi.pass_authorization", "0");
6182 else if (entry->pass_authorization == 1)
6183 apr_table_setn(r->notes, "mod_wsgi.pass_authorization", "1");
6184
6185 return OK;
6186 }
6187 }
6188
6189 return DECLINED;
6190 }
6191
6192 /* Handler for the response handler phase. */
6193
6194 static void wsgi_drop_invalid_headers(request_rec *r);
6195 static void wsgi_process_proxy_headers(request_rec *r);
6196
wsgi_build_environment(request_rec * r)6197 static void wsgi_build_environment(request_rec *r)
6198 {
6199 WSGIRequestConfig *config = NULL;
6200
6201 const char *value = NULL;
6202 const char *script_name = NULL;
6203 const char *path_info = NULL;
6204
6205 conn_rec *c = r->connection;
6206
6207 /* Grab request configuration. */
6208
6209 config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
6210 &wsgi_module);
6211
6212 /*
6213 * Remove any invalid headers which use invalid characters.
6214 * This is necessary to ensure that someone doesn't try and
6215 * take advantage of header spoofing. This can come about
6216 * where characters other than alphanumerics or '-' are used
6217 * as the conversion of non alphanumerics to '_' means one
6218 * can get collisions. This is technically only an issue
6219 * with Apache 2.2 as Apache 2.4 addresses the problem and
6220 * drops them anyway. Still go through and drop them even
6221 * for Apache 2.4 as not sure which version of Apache 2.4
6222 * introduces the change.
6223 */
6224
6225 wsgi_drop_invalid_headers(r);
6226
6227 /* Populate environment with standard CGI variables. */
6228
6229 ap_add_cgi_vars(r);
6230 ap_add_common_vars(r);
6231
6232 /*
6233 * Mutate a HEAD request into a GET request. This is
6234 * required because WSGI specification doesn't lay out
6235 * clearly how WSGI applications should treat a HEAD
6236 * request. Generally authors of WSGI applications or
6237 * frameworks take it that they do not need to return any
6238 * content, but this screws up any Apache output filters
6239 * which need to see all the response content in order to
6240 * correctly set up response headers for a HEAD request such
6241 * that they are the same as a GET request. Thus change a
6242 * HEAD request into a GET request to ensure that request
6243 * content is generated. If using Apache 2.X we can skip
6244 * doing this if we know there is no output filter that
6245 * might change the content and/or headers.
6246 *
6247 * The default behaviour here of changing it if an output
6248 * filter is detected can be overridden using the directive
6249 * WSGIMapHEADToGet. The default value is 'Auto'. If set to
6250 * 'On' then it remapped regardless of whether an output
6251 * filter is present. If 'Off' then it will be left alone
6252 * and the original value used.
6253 */
6254
6255 if (config->map_head_to_get == 2) {
6256 if (r->method_number == M_GET && r->header_only &&
6257 r->output_filters->frec->ftype < AP_FTYPE_PROTOCOL)
6258 apr_table_setn(r->subprocess_env, "REQUEST_METHOD", "GET");
6259 }
6260 else if (config->map_head_to_get == 1) {
6261 if (r->method_number == M_GET)
6262 apr_table_setn(r->subprocess_env, "REQUEST_METHOD", "GET");
6263 }
6264
6265 /*
6266 * If enabled, pass along authorisation headers which Apache
6267 * leaves out of CGI environment. WSGI still needs to see
6268 * these if it needs to implement any of the standard
6269 * authentication schemes such as Basic and Digest. We do
6270 * not pass these through by default though as it can result
6271 * in passwords being leaked though to a WSGI application
6272 * when it shouldn't. This would be a problem where there is
6273 * some sort of site wide authorisation scheme in place
6274 * which has got nothing to do with specific applications.
6275 */
6276
6277 if (config->pass_authorization) {
6278 value = apr_table_get(r->headers_in, "Authorization");
6279 if (value)
6280 apr_table_setn(r->subprocess_env, "HTTP_AUTHORIZATION", value);
6281 }
6282
6283 /* If PATH_INFO not set, set it to an empty string. */
6284
6285 value = apr_table_get(r->subprocess_env, "PATH_INFO");
6286 if (!value)
6287 apr_table_setn(r->subprocess_env, "PATH_INFO", "");
6288
6289 /*
6290 * Multiple slashes are not always collapsed into a single
6291 * slash in SCRIPT_NAME and PATH_INFO with Apache 1.3 and
6292 * Apache 2.X behaving a bit differently. Because some WSGI
6293 * applications don't deal with multiple slashes properly we
6294 * collapse any duplicate slashes to a single slash so
6295 * Apache behaviour is consistent across all versions. We
6296 * don't care that PATH_TRANSLATED can on Apache 1.3 still
6297 * contain multiple slashes as that should not be getting
6298 * used from a WSGI application anyway.
6299 */
6300
6301 script_name = apr_table_get(r->subprocess_env, "SCRIPT_NAME");
6302
6303 if (*script_name == '/') {
6304 while (*script_name && (*(script_name+1) == '/'))
6305 script_name++;
6306 script_name = apr_pstrdup(r->pool, script_name);
6307 ap_no2slash((char*)script_name);
6308 apr_table_setn(r->subprocess_env, "SCRIPT_NAME", script_name);
6309 }
6310
6311 path_info = apr_table_get(r->subprocess_env, "PATH_INFO");
6312
6313 if (*path_info == '/') {
6314 while (*path_info && (*(path_info+1) == '/'))
6315 path_info++;
6316 path_info = apr_pstrdup(r->pool, path_info);
6317 ap_no2slash((char*)path_info);
6318 apr_table_setn(r->subprocess_env, "PATH_INFO", path_info);
6319 }
6320
6321 /*
6322 * Save away the SCRIPT_NAME and PATH_INFO values at this point
6323 * so we have a way of determining if they are rewritten somehow.
6324 * This can be important when dealing with rewrite rules and
6325 * a trusted header was being handled for SCRIPT_NAME.
6326 */
6327
6328 apr_table_setn(r->subprocess_env, "mod_wsgi.script_name", script_name);
6329 apr_table_setn(r->subprocess_env, "mod_wsgi.path_info", path_info);
6330
6331 /*
6332 * Perform fixups on environment based on trusted proxy headers
6333 * sent through from a front end proxy.
6334 */
6335
6336 wsgi_process_proxy_headers(r);
6337
6338 /*
6339 * Determine whether connection uses HTTPS protocol. This has
6340 * to be done after and fixups due to trusted proxy headers.
6341 */
6342
6343 if (!wsgi_is_https)
6344 wsgi_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
6345
6346 if (wsgi_is_https && wsgi_is_https(r->connection))
6347 apr_table_set(r->subprocess_env, "HTTPS", "1");
6348
6349 /*
6350 * Set values specific to mod_wsgi configuration. These control
6351 * aspects of how a request is managed but don't strictly need
6352 * to be passed through to the application itself. It is though
6353 * easier to set them here as then they are carried across to
6354 * the daemon process as part of the environment where they can
6355 * be extracted and used.
6356 */
6357
6358 apr_table_setn(r->subprocess_env, "mod_wsgi.process_group",
6359 config->process_group);
6360 apr_table_setn(r->subprocess_env, "mod_wsgi.application_group",
6361 config->application_group);
6362 apr_table_setn(r->subprocess_env, "mod_wsgi.callable_object",
6363 config->callable_object);
6364
6365 apr_table_setn(r->subprocess_env, "mod_wsgi.request_handler", r->handler);
6366 apr_table_setn(r->subprocess_env, "mod_wsgi.handler_script",
6367 config->handler_script);
6368
6369 apr_table_setn(r->subprocess_env, "mod_wsgi.script_reloading",
6370 apr_psprintf(r->pool, "%d", config->script_reloading));
6371
6372 #if defined(MOD_WSGI_WITH_DAEMONS)
6373 apr_table_setn(r->subprocess_env, "mod_wsgi.listener_host",
6374 c->local_addr->hostname ? c->local_addr->hostname : "");
6375 apr_table_setn(r->subprocess_env, "mod_wsgi.listener_port",
6376 apr_psprintf(r->pool, "%d", c->local_addr->port));
6377 #endif
6378
6379 apr_table_setn(r->subprocess_env, "mod_wsgi.enable_sendfile",
6380 apr_psprintf(r->pool, "%d", config->enable_sendfile));
6381 apr_table_setn(r->subprocess_env, "mod_wsgi.ignore_activity",
6382 apr_psprintf(r->pool, "%d", config->ignore_activity));
6383
6384 apr_table_setn(r->subprocess_env, "mod_wsgi.request_start",
6385 apr_psprintf(r->pool, "%" APR_TIME_T_FMT, r->request_time));
6386
6387 #if AP_MODULE_MAGIC_AT_LEAST(20100923,2)
6388 if (!r->log_id) {
6389 const char **id;
6390
6391 /* Need to cast const away. */
6392
6393 id = &((request_rec *)r)->log_id;
6394
6395 ap_run_generate_log_id(c, r, id);
6396 }
6397
6398 if (r->log_id)
6399 apr_table_setn(r->subprocess_env, "mod_wsgi.request_id", r->log_id);
6400 if (r->connection->log_id)
6401 apr_table_setn(r->subprocess_env, "mod_wsgi.connection_id",
6402 r->connection->log_id);
6403 #endif
6404 }
6405
6406 typedef struct {
6407 PyObject_HEAD
6408 request_rec *r;
6409 WSGIRequestConfig *config;
6410 PyObject *log;
6411 } DispatchObject;
6412
newDispatchObject(request_rec * r,WSGIRequestConfig * config)6413 static DispatchObject *newDispatchObject(request_rec *r,
6414 WSGIRequestConfig *config)
6415 {
6416 DispatchObject *self;
6417
6418 self = PyObject_New(DispatchObject, &Dispatch_Type);
6419 if (self == NULL)
6420 return NULL;
6421
6422 self->config = config;
6423
6424 self->r = r;
6425
6426 self->log = newLogObject(r, APLOG_ERR, NULL, 0);
6427
6428 return self;
6429 }
6430
Dispatch_dealloc(DispatchObject * self)6431 static void Dispatch_dealloc(DispatchObject *self)
6432 {
6433 Py_DECREF(self->log);
6434
6435 PyObject_Del(self);
6436 }
6437
Dispatch_environ(DispatchObject * self,const char * group)6438 static PyObject *Dispatch_environ(DispatchObject *self, const char *group)
6439 {
6440 request_rec *r = NULL;
6441
6442 PyObject *vars = NULL;
6443 PyObject *object = NULL;
6444
6445 const apr_array_header_t *head = NULL;
6446 const apr_table_entry_t *elts = NULL;
6447
6448 int i = 0;
6449
6450 /* Create the WSGI environment dictionary. */
6451
6452 vars = PyDict_New();
6453
6454 /* Merge the CGI environment into the WSGI environment. */
6455
6456 r = self->r;
6457
6458 head = apr_table_elts(r->subprocess_env);
6459 elts = (apr_table_entry_t *)head->elts;
6460
6461 for (i = 0; i < head->nelts; ++i) {
6462 if (elts[i].key) {
6463 if (elts[i].val) {
6464 #if PY_MAJOR_VERSION >= 3
6465 object = PyUnicode_DecodeLatin1(elts[i].val,
6466 strlen(elts[i].val), NULL);
6467 #else
6468 object = PyString_FromString(elts[i].val);
6469 #endif
6470 PyDict_SetItemString(vars, elts[i].key, object);
6471 Py_DECREF(object);
6472 }
6473 else
6474 PyDict_SetItemString(vars, elts[i].key, Py_None);
6475 }
6476 }
6477
6478 /*
6479 * Need to override process and application group as they
6480 * are set to the default target, where as we need to set
6481 * them to context dispatch script is run in. Also need
6482 * to remove callable object reference.
6483 */
6484
6485 #if PY_MAJOR_VERSION >= 3
6486 object = PyUnicode_FromString("");
6487 #else
6488 object = PyString_FromString("");
6489 #endif
6490 PyDict_SetItemString(vars, "mod_wsgi.process_group", object);
6491 Py_DECREF(object);
6492
6493 #if PY_MAJOR_VERSION >= 3
6494 object = PyUnicode_DecodeLatin1(group, strlen(group), NULL);
6495 #else
6496 object = PyString_FromString(group);
6497 #endif
6498 PyDict_SetItemString(vars, "mod_wsgi.application_group", object);
6499 Py_DECREF(object);
6500
6501 PyDict_DelItemString(vars, "mod_wsgi.callable_object");
6502
6503 /*
6504 * Setup log object for WSGI errors. Don't decrement
6505 * reference to log object as keep reference to it.
6506 */
6507
6508 object = (PyObject *)self->log;
6509 PyDict_SetItemString(vars, "wsgi.errors", object);
6510
6511 /*
6512 * If Apache extensions are enabled add a CObject reference
6513 * to the Apache request_rec structure instance.
6514 */
6515
6516 if (!wsgi_daemon_pool && self->config->pass_apache_request) {
6517 #if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 2) || \
6518 (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 7)
6519 object = PyCapsule_New(self->r, 0, 0);
6520 #else
6521 object = PyCObject_FromVoidPtr(self->r, 0);
6522 #endif
6523 PyDict_SetItemString(vars, "apache.request_rec", object);
6524 Py_DECREF(object);
6525 }
6526
6527 /*
6528 * Extensions for accessing SSL certificate information from
6529 * mod_ssl when in use.
6530 */
6531
6532 #if 0
6533 object = PyObject_GetAttrString((PyObject *)self, "ssl_is_https");
6534 PyDict_SetItemString(vars, "mod_ssl.is_https", object);
6535 Py_DECREF(object);
6536
6537 object = PyObject_GetAttrString((PyObject *)self, "ssl_var_lookup");
6538 PyDict_SetItemString(vars, "mod_ssl.var_lookup", object);
6539 Py_DECREF(object);
6540 #endif
6541
6542 return vars;
6543 }
6544
Dispatch_ssl_is_https(DispatchObject * self,PyObject * args)6545 static PyObject *Dispatch_ssl_is_https(DispatchObject *self, PyObject *args)
6546 {
6547 APR_OPTIONAL_FN_TYPE(ssl_is_https) *ssl_is_https = 0;
6548
6549 if (!self->r) {
6550 PyErr_SetString(PyExc_RuntimeError, "request object has expired");
6551 return NULL;
6552 }
6553
6554 if (!PyArg_ParseTuple(args, ":ssl_is_https"))
6555 return NULL;
6556
6557 ssl_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
6558
6559 if (ssl_is_https == 0)
6560 return Py_BuildValue("i", 0);
6561
6562 return Py_BuildValue("i", ssl_is_https(self->r->connection));
6563 }
6564
Dispatch_ssl_var_lookup(DispatchObject * self,PyObject * args)6565 static PyObject *Dispatch_ssl_var_lookup(DispatchObject *self, PyObject *args)
6566 {
6567 APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *ssl_var_lookup = 0;
6568
6569 PyObject *item = NULL;
6570
6571 char *name = 0;
6572 char *value = 0;
6573
6574 if (!self->r) {
6575 PyErr_SetString(PyExc_RuntimeError, "request object has expired");
6576 return NULL;
6577 }
6578
6579 if (!PyArg_ParseTuple(args, "O:ssl_var_lookup", &item))
6580 return NULL;
6581
6582 #if PY_MAJOR_VERSION >= 3
6583 if (PyUnicode_Check(item)) {
6584 PyObject *latin_item;
6585 latin_item = PyUnicode_AsLatin1String(item);
6586 if (!latin_item) {
6587 PyErr_Format(PyExc_TypeError, "byte string value expected, "
6588 "value containing non 'latin-1' characters found");
6589 Py_DECREF(item);
6590 return NULL;
6591 }
6592
6593 Py_DECREF(item);
6594 item = latin_item;
6595 }
6596 #endif
6597
6598 if (!PyString_Check(item)) {
6599 PyErr_Format(PyExc_TypeError, "byte string value expected, value "
6600 "of type %.200s found", item->ob_type->tp_name);
6601 Py_DECREF(item);
6602 return NULL;
6603 }
6604
6605 name = PyString_AsString(item);
6606
6607 ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
6608
6609 if (ssl_var_lookup == 0)
6610 {
6611 Py_INCREF(Py_None);
6612
6613 return Py_None;
6614 }
6615
6616 value = ssl_var_lookup(self->r->pool, self->r->server,
6617 self->r->connection, self->r, name);
6618
6619 if (!value) {
6620 Py_INCREF(Py_None);
6621
6622 return Py_None;
6623 }
6624
6625 #if PY_MAJOR_VERSION >= 3
6626 return PyUnicode_DecodeLatin1(value, strlen(value), NULL);
6627 #else
6628 return PyString_FromString(value);
6629 #endif
6630 }
6631
6632 static PyMethodDef Dispatch_methods[] = {
6633 { "ssl_is_https", (PyCFunction)Dispatch_ssl_is_https, METH_VARARGS, 0 },
6634 { "ssl_var_lookup", (PyCFunction)Dispatch_ssl_var_lookup, METH_VARARGS, 0 },
6635 { NULL, NULL}
6636 };
6637
6638 static PyTypeObject Dispatch_Type = {
6639 PyVarObject_HEAD_INIT(NULL, 0)
6640 "mod_wsgi.Dispatch", /*tp_name*/
6641 sizeof(DispatchObject), /*tp_basicsize*/
6642 0, /*tp_itemsize*/
6643 /* methods */
6644 (destructor)Dispatch_dealloc, /*tp_dealloc*/
6645 0, /*tp_print*/
6646 0, /*tp_getattr*/
6647 0, /*tp_setattr*/
6648 0, /*tp_compare*/
6649 0, /*tp_repr*/
6650 0, /*tp_as_number*/
6651 0, /*tp_as_sequence*/
6652 0, /*tp_as_mapping*/
6653 0, /*tp_hash*/
6654 0, /*tp_call*/
6655 0, /*tp_str*/
6656 0, /*tp_getattro*/
6657 0, /*tp_setattro*/
6658 0, /*tp_as_buffer*/
6659 Py_TPFLAGS_DEFAULT, /*tp_flags*/
6660 0, /*tp_doc*/
6661 0, /*tp_traverse*/
6662 0, /*tp_clear*/
6663 0, /*tp_richcompare*/
6664 0, /*tp_weaklistoffset*/
6665 0, /*tp_iter*/
6666 0, /*tp_iternext*/
6667 Dispatch_methods, /*tp_methods*/
6668 0, /*tp_members*/
6669 0, /*tp_getset*/
6670 0, /*tp_base*/
6671 0, /*tp_dict*/
6672 0, /*tp_descr_get*/
6673 0, /*tp_descr_set*/
6674 0, /*tp_dictoffset*/
6675 0, /*tp_init*/
6676 0, /*tp_alloc*/
6677 0, /*tp_new*/
6678 0, /*tp_free*/
6679 0, /*tp_is_gc*/
6680 };
6681
wsgi_execute_dispatch(request_rec * r)6682 static int wsgi_execute_dispatch(request_rec *r)
6683 {
6684 WSGIRequestConfig *config;
6685
6686 InterpreterObject *interp = NULL;
6687 PyObject *modules = NULL;
6688 PyObject *module = NULL;
6689 char *name = NULL;
6690 int exists = 0;
6691
6692 const char *script = NULL;
6693 const char *group = NULL;
6694
6695 int status;
6696
6697 /* Grab request configuration. */
6698
6699 config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
6700 &wsgi_module);
6701
6702 if (!config->dispatch_script) {
6703 ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
6704 "mod_wsgi (pid=%d): Location of WSGI dispatch "
6705 "script not provided.", getpid());
6706
6707 return HTTP_INTERNAL_SERVER_ERROR;
6708 }
6709
6710 /*
6711 * Acquire the desired python interpreter. Once this is done
6712 * it is safe to start manipulating python objects.
6713 */
6714
6715 script = config->dispatch_script->handler_script;
6716 group = wsgi_server_group(r, config->dispatch_script->application_group);
6717
6718 interp = wsgi_acquire_interpreter(group);
6719
6720 if (!interp) {
6721 ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
6722 "mod_wsgi (pid=%d): Cannot acquire interpreter '%s'.",
6723 getpid(), group);
6724
6725 return HTTP_INTERNAL_SERVER_ERROR;
6726 }
6727
6728 /* Calculate the Python module name to be used for script. */
6729
6730 name = wsgi_module_name(r->pool, script);
6731
6732 /*
6733 * Use a lock around the check to see if the module is
6734 * already loaded and the import of the module to prevent
6735 * two request handlers trying to import the module at the
6736 * same time.
6737 */
6738
6739 #if APR_HAS_THREADS
6740 Py_BEGIN_ALLOW_THREADS
6741 apr_thread_mutex_lock(wsgi_module_lock);
6742 Py_END_ALLOW_THREADS
6743 #endif
6744
6745 modules = PyImport_GetModuleDict();
6746 module = PyDict_GetItemString(modules, name);
6747
6748 Py_XINCREF(module);
6749
6750 if (module)
6751 exists = 1;
6752
6753 /*
6754 * If script reloading is enabled and the module for it has
6755 * previously been loaded, see if it has been modified since
6756 * the last time it was accessed.
6757 */
6758
6759 if (module && config->script_reloading) {
6760 if (wsgi_reload_required(r->pool, r, script, module, NULL)) {
6761 /*
6762 * Script file has changed. Only support module
6763 * reloading for dispatch scripts. Remove the
6764 * module from the modules dictionary before
6765 * reloading it again. If code is executing within
6766 * the module at the time, the callers reference
6767 * count on the module should ensure it isn't
6768 * actually destroyed until it is finished.
6769 */
6770
6771 Py_DECREF(module);
6772 module = NULL;
6773
6774 PyDict_DelItemString(modules, name);
6775 }
6776 }
6777
6778 if (!module) {
6779 module = wsgi_load_source(r->pool, r, name, exists, script,
6780 "", group, 0);
6781 }
6782
6783 /* Safe now to release the module lock. */
6784
6785 #if APR_HAS_THREADS
6786 apr_thread_mutex_unlock(wsgi_module_lock);
6787 #endif
6788
6789 /* Log any details of exceptions if import failed. */
6790
6791 if (PyErr_Occurred())
6792 wsgi_log_python_error(r, NULL, script, 0);
6793
6794 /* Assume everything will be okay for now. */
6795
6796 status = OK;
6797
6798 /* Determine if script exists and execute it. */
6799
6800 if (module) {
6801 PyObject *module_dict = NULL;
6802 PyObject *object = NULL;
6803 DispatchObject *adapter = NULL;
6804
6805 module_dict = PyModule_GetDict(module);
6806
6807 adapter = newDispatchObject(r, config);
6808
6809 if (adapter) {
6810 PyObject *vars = NULL;
6811 PyObject *args = NULL;
6812 PyObject *method = NULL;
6813
6814 vars = Dispatch_environ(adapter, group);
6815
6816 /* First check process_group(). */
6817
6818 #if defined(MOD_WSGI_WITH_DAEMONS)
6819 object = PyDict_GetItemString(module_dict, "process_group");
6820
6821 if (object) {
6822 PyObject *result = NULL;
6823
6824 if (adapter) {
6825 Py_INCREF(object);
6826 args = Py_BuildValue("(O)", vars);
6827 result = PyEval_CallObject(object, args);
6828 Py_DECREF(args);
6829 Py_DECREF(object);
6830
6831 if (result) {
6832 if (result != Py_None) {
6833 if (PyString_Check(result)) {
6834 const char *s;
6835
6836 s = PyString_AsString(result);
6837 s = apr_pstrdup(r->pool, s);
6838 s = wsgi_process_group(r, s);
6839 config->process_group = s;
6840
6841 apr_table_setn(r->subprocess_env,
6842 "mod_wsgi.process_group",
6843 config->process_group);
6844 }
6845 #if PY_MAJOR_VERSION >= 3
6846 else if (PyUnicode_Check(result)) {
6847 PyObject *latin_item;
6848 latin_item = PyUnicode_AsLatin1String(result);
6849 if (!latin_item) {
6850 PyErr_SetString(PyExc_TypeError,
6851 "Process group must be "
6852 "a byte string, value "
6853 "containing non 'latin-1' "
6854 "characters found");
6855
6856 status = HTTP_INTERNAL_SERVER_ERROR;
6857 }
6858 else {
6859 const char *s;
6860
6861 Py_DECREF(result);
6862 result = latin_item;
6863
6864 s = PyString_AsString(result);
6865 s = apr_pstrdup(r->pool, s);
6866 s = wsgi_process_group(r, s);
6867 config->process_group = s;
6868
6869 apr_table_setn(r->subprocess_env,
6870 "mod_wsgi.process_group",
6871 config->process_group);
6872 }
6873 }
6874 #endif
6875 else {
6876 PyErr_SetString(PyExc_TypeError, "Process "
6877 "group must be a byte string");
6878
6879 status = HTTP_INTERNAL_SERVER_ERROR;
6880 }
6881 }
6882
6883 Py_DECREF(result);
6884 }
6885 else
6886 status = HTTP_INTERNAL_SERVER_ERROR;
6887
6888 /* Log any details of exceptions if execution failed. */
6889
6890 if (PyErr_Occurred())
6891 wsgi_log_python_error(r, NULL, script, 0);
6892 }
6893
6894 object = NULL;
6895 }
6896 #endif
6897
6898 /* Now check application_group(). */
6899
6900 if (status == OK)
6901 object = PyDict_GetItemString(module_dict, "application_group");
6902
6903 if (object) {
6904 PyObject *result = NULL;
6905
6906 if (adapter) {
6907 Py_INCREF(object);
6908 args = Py_BuildValue("(O)", vars);
6909 result = PyEval_CallObject(object, args);
6910 Py_DECREF(args);
6911 Py_DECREF(object);
6912
6913 if (result) {
6914 if (result != Py_None) {
6915 if (PyString_Check(result)) {
6916 const char *s;
6917
6918 s = PyString_AsString(result);
6919 s = apr_pstrdup(r->pool, s);
6920 s = wsgi_application_group(r, s);
6921 config->application_group = s;
6922
6923 apr_table_setn(r->subprocess_env,
6924 "mod_wsgi.application_group",
6925 config->application_group);
6926 }
6927 #if PY_MAJOR_VERSION >= 3
6928 else if (PyUnicode_Check(result)) {
6929 PyObject *latin_item;
6930 latin_item = PyUnicode_AsLatin1String(result);
6931 if (!latin_item) {
6932 PyErr_SetString(PyExc_TypeError,
6933 "Application group must "
6934 "be a byte string, value "
6935 "containing non 'latin-1' "
6936 "characters found");
6937
6938 status = HTTP_INTERNAL_SERVER_ERROR;
6939 }
6940 else {
6941 const char *s;
6942
6943 Py_DECREF(result);
6944 result = latin_item;
6945
6946 s = PyString_AsString(result);
6947 s = apr_pstrdup(r->pool, s);
6948 s = wsgi_application_group(r, s);
6949 config->application_group = s;
6950
6951 apr_table_setn(r->subprocess_env,
6952 "mod_wsgi.application_group",
6953 config->application_group);
6954 }
6955 }
6956 #endif
6957 else {
6958 PyErr_SetString(PyExc_TypeError, "Application "
6959 "group must be a string "
6960 "object");
6961
6962 status = HTTP_INTERNAL_SERVER_ERROR;
6963 }
6964 }
6965
6966 Py_DECREF(result);
6967 }
6968 else
6969 status = HTTP_INTERNAL_SERVER_ERROR;
6970
6971 /* Log any details of exceptions if execution failed. */
6972
6973 if (PyErr_Occurred())
6974 wsgi_log_python_error(r, NULL, script, 0);
6975 }
6976
6977 object = NULL;
6978 }
6979
6980 /* Now check callable_object(). */
6981
6982 if (status == OK)
6983 object = PyDict_GetItemString(module_dict, "callable_object");
6984
6985 if (object) {
6986 PyObject *result = NULL;
6987
6988 if (adapter) {
6989 Py_INCREF(object);
6990 args = Py_BuildValue("(O)", vars);
6991 result = PyEval_CallObject(object, args);
6992 Py_DECREF(args);
6993 Py_DECREF(object);
6994
6995 if (result) {
6996 if (result != Py_None) {
6997 if (PyString_Check(result)) {
6998 const char *s;
6999
7000 s = PyString_AsString(result);
7001 s = apr_pstrdup(r->pool, s);
7002 s = wsgi_callable_object(r, s);
7003 config->callable_object = s;
7004
7005 apr_table_setn(r->subprocess_env,
7006 "mod_wsgi.callable_object",
7007 config->callable_object);
7008 }
7009 #if PY_MAJOR_VERSION >= 3
7010 else if (PyUnicode_Check(result)) {
7011 PyObject *latin_item;
7012 latin_item = PyUnicode_AsLatin1String(result);
7013 if (!latin_item) {
7014 PyErr_SetString(PyExc_TypeError,
7015 "Callable object must "
7016 "be a byte string, value "
7017 "containing non 'latin-1' "
7018 "characters found");
7019
7020 status = HTTP_INTERNAL_SERVER_ERROR;
7021 }
7022 else {
7023 const char *s;
7024
7025 Py_DECREF(result);
7026 result = latin_item;
7027
7028 s = PyString_AsString(result);
7029 s = apr_pstrdup(r->pool, s);
7030 s = wsgi_callable_object(r, s);
7031 config->callable_object = s;
7032
7033 apr_table_setn(r->subprocess_env,
7034 "mod_wsgi.callable_object",
7035 config->callable_object);
7036 }
7037 }
7038 #endif
7039 else {
7040 PyErr_SetString(PyExc_TypeError, "Callable "
7041 "object must be a string "
7042 "object");
7043
7044 status = HTTP_INTERNAL_SERVER_ERROR;
7045 }
7046 }
7047
7048 Py_DECREF(result);
7049 }
7050 else
7051 status = HTTP_INTERNAL_SERVER_ERROR;
7052
7053 /* Log any details of exceptions if execution failed. */
7054
7055 if (PyErr_Occurred())
7056 wsgi_log_python_error(r, NULL, script, 0);
7057 }
7058
7059 object = NULL;
7060 }
7061
7062 /*
7063 * Wipe out references to Apache request object
7064 * held by Python objects, so can detect when an
7065 * application holds on to the transient Python
7066 * objects beyond the life of the request and
7067 * thus raise an exception if they are used.
7068 */
7069
7070 adapter->r = NULL;
7071
7072 /* Close the log object so data is flushed. */
7073
7074 method = PyObject_GetAttrString(adapter->log, "close");
7075
7076 if (!method) {
7077 PyErr_Format(PyExc_AttributeError,
7078 "'%s' object has no attribute 'close'",
7079 adapter->log->ob_type->tp_name);
7080 }
7081 else {
7082 args = PyTuple_New(0);
7083 object = PyEval_CallObject(method, args);
7084 Py_DECREF(args);
7085 }
7086
7087 Py_XDECREF(object);
7088 Py_XDECREF(method);
7089
7090 /* No longer need adapter object. */
7091
7092 Py_DECREF((PyObject *)adapter);
7093
7094 /* Log any details of exceptions if execution failed. */
7095
7096 if (PyErr_Occurred())
7097 wsgi_log_python_error(r, NULL, script, 0);
7098
7099 Py_DECREF(vars);
7100 }
7101 }
7102
7103 /* Cleanup and release interpreter, */
7104
7105 Py_XDECREF(module);
7106
7107 wsgi_release_interpreter(interp);
7108
7109 return status;
7110 }
7111
wsgi_is_script_aliased(request_rec * r)7112 static int wsgi_is_script_aliased(request_rec *r)
7113 {
7114 const char *t = NULL;
7115
7116 t = apr_table_get(r->notes, "alias-forced-type");
7117 return t && (!strcasecmp(t, "wsgi-script"));
7118 }
7119
7120 #if defined(MOD_WSGI_WITH_DAEMONS)
7121 static int wsgi_execute_remote(request_rec *r);
7122 #endif
7123
wsgi_hook_handler(request_rec * r)7124 static int wsgi_hook_handler(request_rec *r)
7125 {
7126 int status;
7127 apr_off_t limit = 0;
7128
7129 WSGIRequestConfig *config = NULL;
7130
7131 const char *value = NULL;
7132
7133 const char *tenc = NULL;
7134 const char *lenp = NULL;
7135
7136 /* Filter out the obvious case of no handler defined. */
7137
7138 if (!r->handler)
7139 return DECLINED;
7140
7141 /*
7142 * Construct request configuration and cache it in the
7143 * request object against this module so can access it later
7144 * from handler code.
7145 */
7146
7147 config = wsgi_create_req_config(r->pool, r);
7148
7149 ap_set_module_config(r->request_config, &wsgi_module, config);
7150
7151 /*
7152 * Only process requests for this module. First check for
7153 * where target is the actual WSGI script. Then need to
7154 * check for the case where handler name mapped to a handler
7155 * script definition.
7156 */
7157
7158 if (!strcmp(r->handler, "wsgi-script") ||
7159 !strcmp(r->handler, "application/x-httpd-wsgi")) {
7160
7161 /*
7162 * Ensure that have adequate privileges to run the WSGI
7163 * script. Require ExecCGI to be specified in Options for
7164 * this. In doing this, using the wider interpretation that
7165 * ExecCGI refers to any executable like script even though
7166 * not a separate process execution.
7167 */
7168
7169 if (!(ap_allow_options(r) & OPT_EXECCGI) &&
7170 !wsgi_is_script_aliased(r)) {
7171 wsgi_log_script_error(r, "Options ExecCGI is off in this "
7172 "directory", r->filename);
7173 return HTTP_FORBIDDEN;
7174 }
7175
7176 /* Ensure target script exists and is a file. */
7177
7178 if (r->finfo.filetype == 0) {
7179 wsgi_log_script_error(r, "Target WSGI script not found or unable "
7180 "to stat", r->filename);
7181 return HTTP_NOT_FOUND;
7182 }
7183
7184 if (r->finfo.filetype == APR_DIR) {
7185 wsgi_log_script_error(r, "Attempt to invoke directory as WSGI "
7186 "application", r->filename);
7187 return HTTP_FORBIDDEN;
7188 }
7189
7190 if (wsgi_is_script_aliased(r)) {
7191 /*
7192 * Allow any configuration supplied through request notes to
7193 * override respective values. Request notes are used when
7194 * configuration supplied with WSGIScriptAlias directives.
7195 */
7196
7197 if ((value = apr_table_get(r->notes, "mod_wsgi.process_group")))
7198 config->process_group = wsgi_process_group(r, value);
7199 if ((value = apr_table_get(r->notes, "mod_wsgi.application_group")))
7200 config->application_group = wsgi_application_group(r, value);
7201 if ((value = apr_table_get(r->notes, "mod_wsgi.callable_object")))
7202 config->callable_object = value;
7203
7204 if ((value = apr_table_get(r->notes,
7205 "mod_wsgi.pass_authorization"))) {
7206 if (!strcmp(value, "1"))
7207 config->pass_authorization = 1;
7208 else
7209 config->pass_authorization = 0;
7210 }
7211 }
7212 }
7213 #if 0
7214 else if (strstr(r->handler, "wsgi-handler=") == r->handler) {
7215 config->handler_script = apr_pstrcat(r->pool, r->handler+13, NULL);
7216 config->callable_object = "handle_request";
7217 }
7218 #endif
7219 else if (config->handler_scripts) {
7220 WSGIScriptFile *entry;
7221
7222 entry = (WSGIScriptFile *)apr_hash_get(config->handler_scripts,
7223 r->handler,
7224 APR_HASH_KEY_STRING);
7225
7226 if (entry) {
7227 config->handler_script = entry->handler_script;
7228 config->callable_object = "handle_request";
7229
7230 if ((value = entry->process_group))
7231 config->process_group = wsgi_process_group(r, value);
7232 if ((value = entry->application_group))
7233 config->application_group = wsgi_application_group(r, value);
7234
7235 if ((value = entry->pass_authorization)) {
7236 if (!strcmp(value, "1"))
7237 config->pass_authorization = 1;
7238 else
7239 config->pass_authorization = 0;
7240 }
7241 }
7242 else
7243 return DECLINED;
7244 }
7245 else
7246 return DECLINED;
7247
7248 /*
7249 * Honour AcceptPathInfo directive. Default behaviour is
7250 * accept additional path information.
7251 */
7252
7253 #if AP_MODULE_MAGIC_AT_LEAST(20011212,0)
7254 if ((r->used_path_info == AP_REQ_REJECT_PATH_INFO) &&
7255 r->path_info && *r->path_info) {
7256 wsgi_log_script_error(r, "AcceptPathInfo off disallows user's path",
7257 r->filename);
7258 return HTTP_NOT_FOUND;
7259 }
7260 #endif
7261
7262 /*
7263 * Setup policy to apply if request contains a body. Note that the
7264 * WSGI specification doesn't strictly allow for chunked request
7265 * content as CONTENT_LENGTH is required when reading input and
7266 * an application isn't meant to read more than what is defined by
7267 * CONTENT_LENGTH. We still optionally allow chunked request content.
7268 * For an application to use the content, it has to ignore the WSGI
7269 * specification and use read() with no arguments to read all
7270 * available input, or call read() with specific block size until
7271 * read() returns an empty string.
7272 */
7273
7274 tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
7275
7276 if (tenc) {
7277 /* Only chunked transfer encoding is supported. */
7278
7279 if (strcasecmp(tenc, "chunked")) {
7280 wsgi_log_script_error(r, apr_psprintf(r->pool,
7281 "Unexpected value for Transfer-Encoding of '%s' "
7282 "supplied. Only 'chunked' supported.", tenc),
7283 r->filename);
7284 return HTTP_NOT_IMPLEMENTED;
7285 }
7286
7287 /* Only allow chunked requests when explicitly enabled. */
7288
7289 if (!config->chunked_request) {
7290 wsgi_log_script_error(r, "Received request requiring chunked "
7291 "transfer encoding, but optional support for chunked "
7292 "transfer encoding has not been enabled.", r->filename);
7293 return HTTP_LENGTH_REQUIRED;
7294 }
7295
7296 /*
7297 * When chunked transfer encoding is specified, there should
7298 * not be any content length specified.
7299 */
7300
7301 if (lenp) {
7302 wsgi_log_script_error(r, "Unexpected Content-Length header "
7303 "supplied where Transfer-Encoding was specified "
7304 "as 'chunked'.", r->filename);
7305 return HTTP_BAD_REQUEST;
7306 }
7307 }
7308
7309 /*
7310 * Check to see if the request content is too large if the
7311 * Content-Length header is defined then end the request here. We do
7312 * this as otherwise it will not be done until first time input data
7313 * is read in by the application. Problem is that underlying HTTP
7314 * output filter will also generate a 413 response and the error
7315 * raised from the application will be appended to that. The call to
7316 * ap_discard_request_body() is hopefully enough to trigger sending
7317 * of the 413 response by the HTTP filter.
7318 */
7319
7320 lenp = apr_table_get(r->headers_in, "Content-Length");
7321
7322 if (lenp) {
7323 char *endstr;
7324 apr_off_t length;
7325
7326 if (wsgi_strtoff(&length, lenp, &endstr, 10)
7327 || *endstr || length < 0) {
7328
7329 wsgi_log_script_error(r, apr_psprintf(r->pool,
7330 "Invalid Content-Length header value of '%s' was "
7331 "supplied.", lenp), r->filename);
7332
7333 return HTTP_BAD_REQUEST;
7334 }
7335
7336 limit = ap_get_limit_req_body(r);
7337
7338 if (limit && limit < length) {
7339 ap_discard_request_body(r);
7340 return OK;
7341 }
7342 }
7343
7344 /* Build the sub process environment. */
7345
7346 config->request_start = r->request_time;
7347
7348 wsgi_build_environment(r);
7349
7350 /*
7351 * If a dispatch script has been provided, as appropriate
7352 * allow it to override any of the configuration related
7353 * to what context the script will be executed in and what
7354 * the target callable object for the application is.
7355 */
7356
7357 if (config->dispatch_script) {
7358 status = wsgi_execute_dispatch(r);
7359
7360 if (status != OK)
7361 return status;
7362 }
7363
7364 /*
7365 * Execute the target WSGI application script or proxy
7366 * request to one of the daemon processes as appropriate.
7367 */
7368
7369 #if defined(MOD_WSGI_WITH_DAEMONS)
7370 status = wsgi_execute_remote(r);
7371
7372 if (status != DECLINED)
7373 return status;
7374 #endif
7375
7376 #if defined(MOD_WSGI_DISABLE_EMBEDDED)
7377 wsgi_log_script_error(r, "Embedded mode of mod_wsgi disabled at compile "
7378 "time", r->filename);
7379 return HTTP_INTERNAL_SERVER_ERROR;
7380 #endif
7381
7382 if (wsgi_server_config->restrict_embedded == 1) {
7383 wsgi_log_script_error(r, "Embedded mode of mod_wsgi disabled by "
7384 "runtime configuration", r->filename);
7385 return HTTP_INTERNAL_SERVER_ERROR;
7386 }
7387
7388 return wsgi_execute_script(r);
7389 }
7390
7391 /*
7392 * Apache 2.X and UNIX specific code for creation and management
7393 * of distinct daemon processes.
7394 */
7395
7396 #if defined(MOD_WSGI_WITH_DAEMONS)
7397
wsgi_add_daemon_process(cmd_parms * cmd,void * mconfig,const char * args)7398 static const char *wsgi_add_daemon_process(cmd_parms *cmd, void *mconfig,
7399 const char *args)
7400 {
7401 const char *name = NULL;
7402 const char *user = NULL;
7403 const char *group = NULL;
7404
7405 int processes = 1;
7406 int multiprocess = 0;
7407 int threads = 15;
7408 long umask = -1;
7409
7410 const char *root = NULL;
7411 const char *home = NULL;
7412
7413 const char *lang = NULL;
7414 const char *locale = NULL;
7415
7416 const char *python_home = NULL;
7417 const char *python_path = NULL;
7418 const char *python_eggs = NULL;
7419
7420 int stack_size = 0;
7421 int maximum_requests = 0;
7422 int startup_timeout = 0;
7423 int shutdown_timeout = 5;
7424 int deadlock_timeout = 300;
7425 int inactivity_timeout = 0;
7426 int request_timeout = 0;
7427 int graceful_timeout = 0;
7428 int eviction_timeout = 0;
7429 int restart_interval = 0;
7430 int connect_timeout = 15;
7431 int socket_timeout = 0;
7432 int queue_timeout = 0;
7433
7434 const char *socket_user = NULL;
7435
7436 int listen_backlog = WSGI_LISTEN_BACKLOG;
7437
7438 const char *display_name = NULL;
7439
7440 int send_buffer_size = 0;
7441 int recv_buffer_size = 0;
7442 int header_buffer_size = 0;
7443 int response_buffer_size = 0;
7444
7445 int response_socket_timeout = 0;
7446
7447 const char *script_user = NULL;
7448 const char *script_group = NULL;
7449
7450 int cpu_time_limit = 0;
7451 int cpu_priority = 0;
7452
7453 apr_int64_t memory_limit = 0;
7454 apr_int64_t virtual_memory_limit = 0;
7455
7456 uid_t uid;
7457 uid_t gid;
7458
7459 const char *groups_list = NULL;
7460 int groups_count = 0;
7461 gid_t *groups = NULL;
7462
7463 int server_metrics = 0;
7464
7465 const char *newrelic_config_file = NULL;
7466 const char *newrelic_environment = NULL;
7467
7468 const char *option = NULL;
7469 const char *value = NULL;
7470
7471 WSGIProcessGroup *entries = NULL;
7472 WSGIProcessGroup *entry = NULL;
7473
7474 int i;
7475
7476 /*
7477 * Set the defaults for user/group from values
7478 * defined for the User/Group directives in main
7479 * Apache configuration.
7480 */
7481
7482 uid = ap_unixd_config.user_id;
7483 user = ap_unixd_config.user_name;
7484
7485 gid = ap_unixd_config.group_id;
7486
7487 /* Now parse options for directive. */
7488
7489 name = ap_getword_conf(cmd->pool, &args);
7490
7491 if (!name || !*name)
7492 return "Name of WSGI daemon process not supplied.";
7493
7494 while (*args) {
7495 if (wsgi_parse_option(cmd->pool, &args, &option,
7496 &value) != APR_SUCCESS) {
7497 return "Invalid option to WSGI daemon process definition.";
7498 }
7499
7500 if (!strcmp(option, "user")) {
7501 if (!*value)
7502 return "Invalid user for WSGI daemon process.";
7503
7504 user = value;
7505 uid = ap_uname2id(user);
7506 if (uid == 0)
7507 return "WSGI process blocked from running as root.";
7508
7509 if (*user == '#') {
7510 struct passwd *entry = NULL;
7511
7512 if ((entry = getpwuid(uid)) == NULL)
7513 return "Couldn't determine user name from uid.";
7514
7515 user = entry->pw_name;
7516 }
7517 }
7518 else if (!strcmp(option, "group")) {
7519 if (!*value)
7520 return "Invalid group for WSGI daemon process.";
7521
7522 group = value;
7523 gid = ap_gname2id(group);
7524 }
7525 else if (!strcmp(option, "supplementary-groups")) {
7526 groups_list = value;
7527 }
7528 else if (!strcmp(option, "processes")) {
7529 if (!*value)
7530 return "Invalid process count for WSGI daemon process.";
7531
7532 processes = atoi(value);
7533 if (processes < 1)
7534 return "Invalid process count for WSGI daemon process.";
7535
7536 multiprocess = 1;
7537 }
7538 else if (!strcmp(option, "threads")) {
7539 if (!*value)
7540 return "Invalid thread count for WSGI daemon process.";
7541
7542 threads = atoi(value);
7543 if (threads < 0 || threads >= WSGI_STACK_LAST-1)
7544 return "Invalid thread count for WSGI daemon process.";
7545 }
7546 else if (!strcmp(option, "umask")) {
7547 if (!*value)
7548 return "Invalid umask for WSGI daemon process.";
7549
7550 errno = 0;
7551 umask = strtol(value, (char **)&value, 8);
7552
7553 if (*value || errno == ERANGE || umask < 0)
7554 return "Invalid umask for WSGI daemon process.";
7555 }
7556 else if (!strcmp(option, "chroot")) {
7557 if (geteuid())
7558 return "Cannot chroot WSGI daemon process when not root.";
7559
7560 if (*value != '/')
7561 return "Invalid chroot directory for WSGI daemon process.";
7562
7563 root = value;
7564 }
7565 else if (!strcmp(option, "home")) {
7566 if (*value != '/')
7567 return "Invalid home directory for WSGI daemon process.";
7568
7569 home = value;
7570 }
7571 else if (!strcmp(option, "lang")) {
7572 lang = value;
7573 }
7574 else if (!strcmp(option, "locale")) {
7575 locale = value;
7576 }
7577 else if (!strcmp(option, "python-home")) {
7578 python_home = value;
7579 }
7580 else if (!strcmp(option, "python-path")) {
7581 python_path = value;
7582 }
7583 else if (!strcmp(option, "python-eggs")) {
7584 python_eggs = value;
7585 }
7586 #if (APR_MAJOR_VERSION >= 1)
7587 else if (!strcmp(option, "stack-size")) {
7588 if (!*value)
7589 return "Invalid stack size for WSGI daemon process.";
7590
7591 stack_size = atoi(value);
7592 if (stack_size <= 0)
7593 return "Invalid stack size for WSGI daemon process.";
7594 }
7595 #endif
7596 else if (!strcmp(option, "maximum-requests")) {
7597 if (!*value)
7598 return "Invalid request count for WSGI daemon process.";
7599
7600 maximum_requests = atoi(value);
7601 if (maximum_requests < 0)
7602 return "Invalid request count for WSGI daemon process.";
7603 }
7604 else if (!strcmp(option, "startup-timeout")) {
7605 if (!*value)
7606 return "Invalid startup timeout for WSGI daemon process.";
7607
7608 startup_timeout = atoi(value);
7609 if (startup_timeout < 0)
7610 return "Invalid startup timeout for WSGI daemon process.";
7611 }
7612 else if (!strcmp(option, "shutdown-timeout")) {
7613 if (!*value)
7614 return "Invalid shutdown timeout for WSGI daemon process.";
7615
7616 shutdown_timeout = atoi(value);
7617 if (shutdown_timeout < 0)
7618 return "Invalid shutdown timeout for WSGI daemon process.";
7619 }
7620 else if (!strcmp(option, "deadlock-timeout")) {
7621 if (!*value)
7622 return "Invalid deadlock timeout for WSGI daemon process.";
7623
7624 deadlock_timeout = atoi(value);
7625 if (deadlock_timeout < 0)
7626 return "Invalid deadlock timeout for WSGI daemon process.";
7627 }
7628 else if (!strcmp(option, "inactivity-timeout")) {
7629 if (!*value)
7630 return "Invalid inactivity timeout for WSGI daemon process.";
7631
7632 inactivity_timeout = atoi(value);
7633 if (inactivity_timeout < 0)
7634 return "Invalid inactivity timeout for WSGI daemon process.";
7635 }
7636 else if (!strcmp(option, "request-timeout")) {
7637 if (!*value)
7638 return "Invalid request timeout for WSGI daemon process.";
7639
7640 request_timeout = atoi(value);
7641 if (request_timeout < 0)
7642 return "Invalid request timeout for WSGI daemon process.";
7643 }
7644 else if (!strcmp(option, "graceful-timeout")) {
7645 if (!*value)
7646 return "Invalid graceful timeout for WSGI daemon process.";
7647
7648 graceful_timeout = atoi(value);
7649 if (graceful_timeout < 0)
7650 return "Invalid graceful timeout for WSGI daemon process.";
7651 }
7652 else if (!strcmp(option, "eviction-timeout")) {
7653 if (!*value)
7654 return "Invalid eviction timeout for WSGI daemon process.";
7655
7656 eviction_timeout = atoi(value);
7657 if (eviction_timeout < 0)
7658 return "Invalid eviction timeout for WSGI daemon process.";
7659 }
7660 else if (!strcmp(option, "restart-interval")) {
7661 if (!*value)
7662 return "Invalid restart interval for WSGI daemon process.";
7663
7664 restart_interval = atoi(value);
7665 if (restart_interval < 0)
7666 return "Invalid restart interval for WSGI daemon process.";
7667 }
7668 else if (!strcmp(option, "connect-timeout")) {
7669 if (!*value)
7670 return "Invalid connect timeout for WSGI daemon process.";
7671
7672 connect_timeout = atoi(value);
7673 if (connect_timeout < 0)
7674 return "Invalid connect timeout for WSGI daemon process.";
7675 }
7676 else if (!strcmp(option, "socket-timeout")) {
7677 if (!*value)
7678 return "Invalid socket timeout for WSGI daemon process.";
7679
7680 socket_timeout = atoi(value);
7681 if (socket_timeout < 0)
7682 return "Invalid socket timeout for WSGI daemon process.";
7683 }
7684 else if (!strcmp(option, "queue-timeout")) {
7685 if (!*value)
7686 return "Invalid queue timeout for WSGI daemon process.";
7687
7688 queue_timeout = atoi(value);
7689 if (queue_timeout < 0)
7690 return "Invalid queue timeout for WSGI daemon process.";
7691 }
7692 else if (!strcmp(option, "listen-backlog")) {
7693 if (!*value)
7694 return "Invalid listen backlog for WSGI daemon process.";
7695
7696 listen_backlog = atoi(value);
7697 if (listen_backlog < 0)
7698 return "Invalid listen backlog for WSGI daemon process.";
7699 }
7700 else if (!strcmp(option, "display-name")) {
7701 display_name = value;
7702 }
7703 else if (!strcmp(option, "send-buffer-size")) {
7704 if (!*value)
7705 return "Invalid send buffer size for WSGI daemon process.";
7706
7707 send_buffer_size = atoi(value);
7708 if (send_buffer_size < 512 && send_buffer_size != 0) {
7709 return "Send buffer size must be >= 512 bytes, "
7710 "or 0 for system default.";
7711 }
7712 }
7713 else if (!strcmp(option, "receive-buffer-size")) {
7714 if (!*value)
7715 return "Invalid receive buffer size for WSGI daemon process.";
7716
7717 recv_buffer_size = atoi(value);
7718 if (recv_buffer_size < 512 && recv_buffer_size != 0) {
7719 return "Receive buffer size must be >= 512 bytes, "
7720 "or 0 for system default.";
7721 }
7722 }
7723 else if (!strcmp(option, "header-buffer-size")) {
7724 if (!*value)
7725 return "Invalid header buffer size for WSGI daemon process.";
7726
7727 header_buffer_size = atoi(value);
7728 if (header_buffer_size < 8192 && header_buffer_size != 0) {
7729 return "Header buffer size must be >= 8192 bytes, "
7730 "or 0 for default.";
7731 }
7732 }
7733 else if (!strcmp(option, "response-buffer-size")) {
7734 if (!*value)
7735 return "Invalid response buffer size for WSGI daemon process.";
7736
7737 response_buffer_size = atoi(value);
7738 if (response_buffer_size < 65536 && response_buffer_size != 0) {
7739 return "Response buffer size must be >= 65536 bytes, "
7740 "or 0 for default.";
7741 }
7742 }
7743 else if (!strcmp(option, "response-socket-timeout")) {
7744 if (!*value)
7745 return "Invalid response socket timeout for WSGI daemon process.";
7746
7747 response_socket_timeout = atoi(value);
7748 if (response_socket_timeout < 0)
7749 return "Invalid response socket timeout for WSGI daemon process.";
7750 }
7751 else if (!strcmp(option, "socket-user")) {
7752 uid_t socket_uid;
7753
7754 if (!*value)
7755 return "Invalid socket user for WSGI daemon process.";
7756
7757 socket_uid = ap_uname2id(value);
7758
7759 if (*value == '#') {
7760 struct passwd *entry = NULL;
7761
7762 if ((entry = getpwuid(socket_uid)) == NULL)
7763 return "Couldn't determine user name from socket user.";
7764
7765 value = entry->pw_name;
7766 }
7767
7768 socket_user = value;
7769 }
7770 else if (!strcmp(option, "script-user")) {
7771 uid_t script_uid;
7772
7773 if (!*value)
7774 return "Invalid script user for WSGI daemon process.";
7775
7776 script_uid = ap_uname2id(value);
7777
7778 if (*value == '#') {
7779 struct passwd *entry = NULL;
7780
7781 if ((entry = getpwuid(script_uid)) == NULL)
7782 return "Couldn't determine uid from script user.";
7783
7784 value = entry->pw_name;
7785 }
7786
7787 script_user = value;
7788 }
7789 else if (!strcmp(option, "script-group")) {
7790 gid_t script_gid;
7791
7792 if (!*value)
7793 return "Invalid script group for WSGI daemon process.";
7794
7795 script_gid = ap_gname2id(value);
7796
7797 if (*value == '#') {
7798 struct group *entry = NULL;
7799
7800 if ((entry = getgrgid(script_gid)) == NULL)
7801 return "Couldn't determine gid from script group.";
7802
7803 value = entry->gr_name;
7804 }
7805
7806 script_group = value;
7807 }
7808 else if (!strcmp(option, "cpu-time-limit")) {
7809 if (!*value)
7810 return "Invalid CPU time limit for WSGI daemon process.";
7811
7812 cpu_time_limit = atoi(value);
7813 if (cpu_time_limit < 0)
7814 return "Invalid CPU time limit for WSGI daemon process.";
7815 }
7816 else if (!strcmp(option, "cpu-priority")) {
7817 if (!*value)
7818 return "Invalid CPU priority for WSGI daemon process.";
7819
7820 cpu_priority = atoi(value);
7821 }
7822 else if (!strcmp(option, "memory-limit")) {
7823 if (!*value)
7824 return "Invalid memory limit for WSGI daemon process.";
7825
7826 memory_limit = apr_atoi64(value);
7827 if (memory_limit < 0)
7828 return "Invalid memory limit for WSGI daemon process.";
7829 }
7830 else if (!strcmp(option, "virtual-memory-limit")) {
7831 if (!*value)
7832 return "Invalid virtual memory limit for WSGI daemon process.";
7833
7834 virtual_memory_limit = apr_atoi64(value);
7835 if (virtual_memory_limit < 0)
7836 return "Invalid virtual memory limit for WSGI daemon process.";
7837 }
7838 else if (!strcmp(option, "server-metrics")) {
7839 if (!*value)
7840 return "Invalid server metrics flag for WSGI daemon process.";
7841
7842 if (strcasecmp(value, "Off") == 0)
7843 server_metrics = 0;
7844 else if (strcasecmp(value, "On") == 0)
7845 server_metrics = 1;
7846 else
7847 return "Invalid server metrics flag for WSGI daemon process.";
7848 }
7849 else if (!strcmp(option, "newrelic-config-file")) {
7850 newrelic_config_file = value;
7851 }
7852 else if (!strcmp(option, "newrelic-environment")) {
7853 newrelic_environment = value;
7854 }
7855 else
7856 return "Invalid option to WSGI daemon process definition.";
7857 }
7858
7859 if (script_user && script_group)
7860 return "Only one of script-user and script-group allowed.";
7861
7862 if (groups_list) {
7863 const char *group_name = NULL;
7864 long groups_maximum = NGROUPS_MAX;
7865 const char *items = NULL;
7866
7867 #ifdef _SC_NGROUPS_MAX
7868 groups_maximum = sysconf(_SC_NGROUPS_MAX);
7869 if (groups_maximum < 0)
7870 groups_maximum = NGROUPS_MAX;
7871 #endif
7872 groups = (gid_t *)apr_pcalloc(cmd->pool,
7873 groups_maximum*sizeof(groups[0]));
7874
7875 groups[groups_count++] = gid;
7876
7877 items = groups_list;
7878 group_name = ap_getword(cmd->pool, &items, ',');
7879
7880 while (group_name && *group_name) {
7881 if (groups_count >= groups_maximum)
7882 return "Too many supplementary groups WSGI daemon process";
7883
7884 groups[groups_count++] = ap_gname2id(group_name);
7885 group_name = ap_getword(cmd->pool, &items, ',');
7886 }
7887 }
7888
7889 if (!wsgi_daemon_list) {
7890 wsgi_daemon_list = apr_array_make(cmd->pool, 20,
7891 sizeof(WSGIProcessGroup));
7892 }
7893
7894 entries = (WSGIProcessGroup *)wsgi_daemon_list->elts;
7895
7896 for (i = 0; i < wsgi_daemon_list->nelts; ++i) {
7897 entry = &entries[i];
7898
7899 if (!strcmp(entry->name, name))
7900 return "Name duplicates previous WSGI daemon definition.";
7901 }
7902
7903 wsgi_daemon_count++;
7904
7905 entry = (WSGIProcessGroup *)apr_array_push(wsgi_daemon_list);
7906
7907 entry->server = cmd->server;
7908
7909 entry->random = random();
7910 entry->id = wsgi_daemon_count;
7911
7912 entry->name = apr_pstrdup(cmd->pool, name);
7913 entry->user = apr_pstrdup(cmd->pool, user);
7914 entry->group = apr_pstrdup(cmd->pool, group);
7915
7916 entry->uid = uid;
7917 entry->gid = gid;
7918
7919 entry->groups_list = groups_list;
7920 entry->groups_count = groups_count;
7921 entry->groups = groups;
7922
7923 entry->processes = processes;
7924 entry->multiprocess = multiprocess;
7925 entry->threads = threads;
7926
7927 entry->umask = umask;
7928 entry->root = root;
7929 entry->home = home;
7930
7931 entry->lang = lang;
7932 entry->locale = locale;
7933
7934 entry->python_home = python_home;
7935 entry->python_path = python_path;
7936 entry->python_eggs = python_eggs;
7937
7938 entry->stack_size = stack_size;
7939 entry->maximum_requests = maximum_requests;
7940 entry->shutdown_timeout = shutdown_timeout;
7941 entry->startup_timeout = apr_time_from_sec(startup_timeout);
7942 entry->deadlock_timeout = apr_time_from_sec(deadlock_timeout);
7943 entry->inactivity_timeout = apr_time_from_sec(inactivity_timeout);
7944 entry->request_timeout = apr_time_from_sec(request_timeout);
7945 entry->graceful_timeout = apr_time_from_sec(graceful_timeout);
7946 entry->eviction_timeout = apr_time_from_sec(eviction_timeout);
7947 entry->restart_interval = apr_time_from_sec(restart_interval);
7948 entry->connect_timeout = apr_time_from_sec(connect_timeout);
7949 entry->socket_timeout = apr_time_from_sec(socket_timeout);
7950 entry->queue_timeout = apr_time_from_sec(queue_timeout);
7951
7952 entry->socket_user = apr_pstrdup(cmd->pool, socket_user);
7953
7954 entry->listen_backlog = listen_backlog;
7955
7956 entry->display_name = display_name;
7957
7958 entry->send_buffer_size = send_buffer_size;
7959 entry->recv_buffer_size = recv_buffer_size;
7960 entry->header_buffer_size = header_buffer_size;
7961 entry->response_buffer_size = response_buffer_size;
7962
7963 if (response_socket_timeout == 0)
7964 response_socket_timeout = socket_timeout;
7965
7966 entry->response_socket_timeout = apr_time_from_sec(response_socket_timeout);
7967
7968 entry->script_user = script_user;
7969 entry->script_group = script_group;
7970
7971 entry->cpu_time_limit = cpu_time_limit;
7972 entry->cpu_priority = cpu_priority;
7973
7974 entry->memory_limit = memory_limit;
7975 entry->virtual_memory_limit = virtual_memory_limit;
7976
7977 entry->server_metrics = server_metrics;
7978
7979 entry->newrelic_config_file = newrelic_config_file;
7980 entry->newrelic_environment = newrelic_environment;
7981
7982 entry->listener_fd = -1;
7983
7984 return NULL;
7985 }
7986
wsgi_set_socket_prefix(cmd_parms * cmd,void * mconfig,const char * arg)7987 static const char *wsgi_set_socket_prefix(cmd_parms *cmd, void *mconfig,
7988 const char *arg)
7989 {
7990 const char *error = NULL;
7991 WSGIServerConfig *sconfig = NULL;
7992
7993 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
7994 if (error != NULL)
7995 return error;
7996
7997 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
7998
7999 sconfig->socket_prefix = ap_server_root_relative(cmd->pool, arg);
8000
8001 if (!sconfig->socket_prefix) {
8002 return apr_pstrcat(cmd->pool, "Invalid WSGISocketPrefix '",
8003 arg, "'.", NULL);
8004 }
8005
8006 return NULL;
8007 }
8008
wsgi_set_socket_rotation(cmd_parms * cmd,void * mconfig,const char * f)8009 static const char *wsgi_set_socket_rotation(cmd_parms *cmd, void *mconfig,
8010 const char *f)
8011 {
8012 const char *error = NULL;
8013 WSGIServerConfig *sconfig = NULL;
8014
8015 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
8016 if (error != NULL)
8017 return error;
8018
8019 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
8020
8021 if (strcasecmp(f, "Off") == 0)
8022 sconfig->socket_rotation = 0;
8023 else if (strcasecmp(f, "On") == 0)
8024 sconfig->socket_rotation = 1;
8025 else
8026 return "WSGISocketRotation must be one of: Off | On";
8027
8028 return NULL;
8029 }
8030
8031 static const char wsgi_valid_accept_mutex_string[] =
8032 "Valid accept mutex mechanisms for this platform are: default"
8033 #if APR_HAS_FLOCK_SERIALIZE
8034 ", flock"
8035 #endif
8036 #if APR_HAS_FCNTL_SERIALIZE
8037 ", fcntl"
8038 #endif
8039 #if APR_HAS_SYSVSEM_SERIALIZE
8040 ", sysvsem"
8041 #endif
8042 #if APR_HAS_POSIXSEM_SERIALIZE
8043 ", posixsem"
8044 #endif
8045 #if APR_HAS_PROC_PTHREAD_SERIALIZE
8046 ", pthread"
8047 #endif
8048 ".";
8049
wsgi_set_accept_mutex(cmd_parms * cmd,void * mconfig,const char * arg)8050 static const char *wsgi_set_accept_mutex(cmd_parms *cmd, void *mconfig,
8051 const char *arg)
8052 {
8053 const char *error = NULL;
8054 WSGIServerConfig *sconfig = NULL;
8055
8056 error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
8057 if (error != NULL)
8058 return error;
8059
8060 sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
8061
8062 #if !defined(AP_ACCEPT_MUTEX_TYPE)
8063 sconfig->lock_mechanism = ap_accept_lock_mech;
8064 #else
8065 sconfig->lock_mechanism = APR_LOCK_DEFAULT;
8066 #endif
8067
8068 if (!strcasecmp(arg, "default")) {
8069 sconfig->lock_mechanism = APR_LOCK_DEFAULT;
8070 }
8071 #if APR_HAS_FLOCK_SERIALIZE
8072 else if (!strcasecmp(arg, "flock")) {
8073 sconfig->lock_mechanism = APR_LOCK_FLOCK;
8074 }
8075 #endif
8076 #if APR_HAS_FCNTL_SERIALIZE
8077 else if (!strcasecmp(arg, "fcntl")) {
8078 sconfig->lock_mechanism = APR_LOCK_FCNTL;
8079 }
8080 #endif
8081 #if APR_HAS_SYSVSEM_SERIALIZE
8082 else if (!strcasecmp(arg, "sysvsem")) {
8083 sconfig->lock_mechanism = APR_LOCK_SYSVSEM;
8084 }
8085 #endif
8086 #if APR_HAS_POSIXSEM_SERIALIZE
8087 else if (!strcasecmp(arg, "posixsem")) {
8088 sconfig->lock_mechanism = APR_LOCK_POSIXSEM;
8089 }
8090 #endif
8091 #if APR_HAS_PROC_PTHREAD_SERIALIZE
8092 else if (!strcasecmp(arg, "pthread")) {
8093 sconfig->lock_mechanism = APR_LOCK_PROC_PTHREAD;
8094 }
8095 #endif
8096 else {
8097 return apr_pstrcat(cmd->pool, "Accept mutex lock mechanism '", arg,
8098 "' is invalid. ", wsgi_valid_accept_mutex_string,
8099 NULL);
8100 }
8101
8102 return NULL;
8103 }
8104
8105 static apr_file_t *wsgi_signal_pipe_in = NULL;
8106 static apr_file_t *wsgi_signal_pipe_out = NULL;
8107
wsgi_signal_handler(int signum)8108 static void wsgi_signal_handler(int signum)
8109 {
8110 apr_size_t nbytes = 1;
8111
8112 if (wsgi_daemon_pid != 0 && wsgi_daemon_pid != getpid())
8113 exit(-1);
8114
8115 if (signum == AP_SIG_GRACEFUL) {
8116 apr_file_write(wsgi_signal_pipe_out, "G", &nbytes);
8117 apr_file_flush(wsgi_signal_pipe_out);
8118 }
8119 else if (signum == SIGXCPU) {
8120 if (!wsgi_graceful_timeout)
8121 wsgi_daemon_shutdown++;
8122
8123 apr_file_write(wsgi_signal_pipe_out, "C", &nbytes);
8124 apr_file_flush(wsgi_signal_pipe_out);
8125 }
8126 else {
8127 wsgi_daemon_shutdown++;
8128
8129 apr_file_write(wsgi_signal_pipe_out, "S", &nbytes);
8130 apr_file_flush(wsgi_signal_pipe_out);
8131 }
8132 }
8133
wsgi_exit_daemon_process(int status)8134 static void wsgi_exit_daemon_process(int status)
8135 {
8136 if (wsgi_server && wsgi_daemon_group) {
8137 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
8138 "mod_wsgi (pid=%d): Exiting process '%s'.", getpid(),
8139 wsgi_daemon_group);
8140 }
8141
8142 exit(status);
8143 }
8144
8145 static int wsgi_start_process(apr_pool_t *p, WSGIDaemonProcess *daemon);
8146
wsgi_manage_process(int reason,void * data,apr_wait_t status)8147 static void wsgi_manage_process(int reason, void *data, apr_wait_t status)
8148 {
8149 WSGIDaemonProcess *daemon = data;
8150
8151 switch (reason) {
8152
8153 /* Child daemon process has died. */
8154
8155 case APR_OC_REASON_DEATH: {
8156 int mpm_state;
8157 int stopping;
8158
8159 /*
8160 * Determine if Apache is being shutdown or not and
8161 * if it is not being shutdown, we will need to
8162 * restart the child daemon process that has died.
8163 * If MPM doesn't support query assume that child
8164 * daemon process shouldn't be restarted. Both
8165 * prefork and worker MPMs support this query so
8166 * should always be okay.
8167 */
8168
8169 stopping = 1;
8170
8171 if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state) == APR_SUCCESS
8172 && mpm_state != AP_MPMQ_STOPPING) {
8173 stopping = 0;
8174 }
8175
8176 if (!stopping) {
8177 ap_log_error(APLOG_MARK, APLOG_INFO, 0,
8178 wsgi_server, "mod_wsgi (pid=%d): "
8179 "Process '%s' has died, deregister and "
8180 "restart it.", daemon->process.pid,
8181 daemon->group->name);
8182
8183 if (WIFEXITED(status)) {
8184 ap_log_error(APLOG_MARK, APLOG_INFO, 0,
8185 wsgi_server, "mod_wsgi (pid=%d): "
8186 "Process '%s' terminated normally, exit code %d",
8187 daemon->process.pid, daemon->group->name,
8188 WEXITSTATUS(status));
8189 }
8190 else if (WIFSIGNALED(status)) {
8191 ap_log_error(APLOG_MARK, APLOG_INFO, 0,
8192 wsgi_server, "mod_wsgi (pid=%d): "
8193 "Process '%s' terminated by signal %d",
8194 daemon->process.pid, daemon->group->name,
8195 WTERMSIG(status));
8196 }
8197 }
8198 else {
8199 ap_log_error(APLOG_MARK, APLOG_INFO, 0,
8200 wsgi_server, "mod_wsgi (pid=%d): "
8201 "Process '%s' has died but server is "
8202 "being stopped, deregister it.",
8203 daemon->process.pid, daemon->group->name);
8204 }
8205
8206 /* Deregister existing process so we stop watching it. */
8207
8208 apr_proc_other_child_unregister(daemon);
8209
8210 /* Now restart process if not shutting down. */
8211
8212 if (!stopping)
8213 wsgi_start_process(wsgi_parent_pool, daemon);
8214
8215 break;
8216 }
8217
8218 /* Apache is being restarted or shutdown. */
8219
8220 case APR_OC_REASON_RESTART: {
8221
8222 ap_log_error(APLOG_MARK, APLOG_INFO, 0,
8223 wsgi_server, "mod_wsgi (pid=%d): "
8224 "Process '%s' to be deregistered, as server is "
8225 "restarting or being shutdown.",
8226 daemon->process.pid, daemon->group->name);
8227
8228 /* Deregister existing process so we stop watching it. */
8229
8230 apr_proc_other_child_unregister(daemon);
8231
8232 break;
8233 }
8234
8235 /* Child daemon process vanished. */
8236
8237 case APR_OC_REASON_LOST: {
8238
8239 ap_log_error(APLOG_MARK, APLOG_INFO, 0,
8240 wsgi_server, "mod_wsgi (pid=%d): "
8241 "Process '%s' appears to have been lost, "
8242 "deregister and restart it.",
8243 daemon->process.pid, daemon->group->name);
8244
8245 /* Deregister existing process so we stop watching it. */
8246
8247 apr_proc_other_child_unregister(daemon);
8248
8249 /* Restart the child daemon process that has died. */
8250
8251 wsgi_start_process(wsgi_parent_pool, daemon);
8252
8253 break;
8254 }
8255
8256 /* Call to unregister the process. */
8257
8258 case APR_OC_REASON_UNREGISTER: {
8259
8260 /* Nothing to do at present. */
8261
8262 ap_log_error(APLOG_MARK, APLOG_INFO, 0,
8263 wsgi_server, "mod_wsgi (pid=%d): "
8264 "Process '%s' has been deregistered and will "
8265 "no longer be monitored.", daemon->process.pid,
8266 daemon->group->name);
8267
8268 break;
8269 }
8270
8271 default: {
8272 ap_log_error(APLOG_MARK, APLOG_INFO, 0,
8273 wsgi_server, "mod_wsgi (pid=%d): "
8274 "Process '%s' targeted by unexpected event %d.",
8275 daemon->process.pid, daemon->group->name, reason);
8276 }
8277 }
8278 }
8279
wsgi_setup_daemon_name(WSGIDaemonProcess * daemon,apr_pool_t * p)8280 static void wsgi_setup_daemon_name(WSGIDaemonProcess *daemon, apr_pool_t *p)
8281 {
8282 const char *display_name = NULL;
8283
8284 #if !(defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__))
8285 long slen = 0;
8286 long dlen = 0;
8287
8288 char *argv0 = NULL;
8289 #endif
8290
8291 display_name = daemon->group->display_name;
8292
8293 if (!display_name)
8294 return;
8295
8296 if (!strcmp(display_name, "%{GROUP}")) {
8297 display_name = apr_pstrcat(p, "(wsgi:", daemon->group->name,
8298 ")", NULL);
8299 }
8300
8301 /*
8302 * Only argv[0] is guaranteed to be the real things as MPM
8303 * modules may make modifications to subsequent arguments.
8304 * Thus can only replace the argv[0] value. Because length
8305 * is restricted, need to truncate display name if too long.
8306 */
8307
8308 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
8309 setproctitle("%s", display_name);
8310 #else
8311 argv0 = (char*)wsgi_server->process->argv[0];
8312
8313 dlen = strlen(argv0);
8314 slen = strlen(display_name);
8315
8316 memset(argv0, ' ', dlen);
8317
8318 if (slen < dlen)
8319 memcpy(argv0, display_name, slen);
8320 else
8321 memcpy(argv0, display_name, dlen);
8322 #endif
8323 }
8324
wsgi_setup_access(WSGIDaemonProcess * daemon)8325 static int wsgi_setup_access(WSGIDaemonProcess *daemon)
8326 {
8327 /* Change to chroot environment. */
8328
8329 if (daemon->group->root) {
8330 if (chroot(daemon->group->root) == -1) {
8331 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8332 "mod_wsgi (pid=%d): Unable to change root "
8333 "directory to '%s'.", getpid(), daemon->group->root);
8334
8335 return -1;
8336 }
8337 }
8338
8339 /* We don't need to switch user/group if not root. */
8340
8341 if (geteuid() == 0) {
8342 /* Setup the daemon process real and effective group. */
8343
8344 if (setgid(daemon->group->gid) == -1) {
8345 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8346 "mod_wsgi (pid=%d): Unable to set group id "
8347 "to gid=%u.", getpid(),
8348 (unsigned)daemon->group->gid);
8349
8350 return -1;
8351 }
8352 else {
8353 if (daemon->group->groups) {
8354 if (setgroups(daemon->group->groups_count,
8355 daemon->group->groups) == -1) {
8356 ap_log_error(APLOG_MARK, APLOG_ALERT, errno,
8357 wsgi_server, "mod_wsgi (pid=%d): Unable "
8358 "to set supplementary groups for uname=%s "
8359 "of '%s'.", getpid(), daemon->group->user,
8360 daemon->group->groups_list);
8361
8362 return -1;
8363 }
8364 }
8365 else if (initgroups(daemon->group->user,
8366 daemon->group->gid) == -1) {
8367 ap_log_error(APLOG_MARK, APLOG_ALERT, errno,
8368 wsgi_server, "mod_wsgi (pid=%d): Unable "
8369 "to set groups for uname=%s and gid=%u.",
8370 getpid(), daemon->group->user,
8371 (unsigned)daemon->group->gid);
8372
8373 return -1;
8374 }
8375 }
8376
8377 /* Setup the daemon process real and effective user. */
8378
8379 if (setuid(daemon->group->uid) == -1) {
8380 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8381 "mod_wsgi (pid=%d): Unable to change to uid=%ld.",
8382 getpid(), (long)daemon->group->uid);
8383
8384 /*
8385 * On true UNIX systems this should always succeed at
8386 * this point. With certain Linux kernel versions though
8387 * we can get back EAGAIN where the target user had
8388 * reached their process limit. In that case will be left
8389 * running as wrong user. Just exit on all failures to be
8390 * safe. Don't die immediately to avoid a fork bomb.
8391 *
8392 * We could just return -1 here and let the caller do the
8393 * sleep() and exit() but this failure is critical enough
8394 * that we still do it here so it is obvious that the issue
8395 * is being addressed.
8396 */
8397
8398 ap_log_error(APLOG_MARK, APLOG_ALERT, 0, wsgi_server,
8399 "mod_wsgi (pid=%d): Failure to configure the "
8400 "daemon process correctly and process left in "
8401 "unspecified state. Restarting daemon process "
8402 "after delay.", getpid());
8403
8404 sleep(20);
8405
8406 wsgi_exit_daemon_process(-1);
8407
8408 return -1;
8409 }
8410 }
8411
8412 /*
8413 * Setup the working directory for the process. It is either set to
8414 * what the 'home' option explicitly provides, or the home home
8415 * directory of the user, where it has been set to be different to
8416 * the user that Apache's own processes run as.
8417 */
8418
8419 if (daemon->group->home) {
8420 if (chdir(daemon->group->home) == -1) {
8421 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8422 "mod_wsgi (pid=%d): Unable to change working "
8423 "directory to '%s'.", getpid(), daemon->group->home);
8424
8425 return -1;
8426 }
8427 }
8428 else if (geteuid() != ap_unixd_config.user_id) {
8429 struct passwd *pwent;
8430
8431 pwent = getpwuid(geteuid());
8432
8433 if (pwent) {
8434 if (chdir(pwent->pw_dir) == -1) {
8435 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8436 "mod_wsgi (pid=%d): Unable to change working "
8437 "directory to home directory '%s' for uid=%ld.",
8438 getpid(), pwent->pw_dir, (long)geteuid());
8439
8440 return -1;
8441 }
8442 }
8443 else {
8444 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8445 "mod_wsgi (pid=%d): Unable to determine home "
8446 "directory for uid=%ld.", getpid(), (long)geteuid());
8447
8448 return -1;
8449 }
8450 }
8451
8452 /* Setup the umask for the effective user. */
8453
8454 if (daemon->group->umask != -1)
8455 umask(daemon->group->umask);
8456
8457 /*
8458 * Linux prevents generation of core dumps after setuid()
8459 * has been used. Attempt to reenable ability to dump core
8460 * so that the CoreDumpDirectory directive still works.
8461 */
8462
8463 #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
8464 /* This applies to Linux 2.4 and later. */
8465
8466 if (ap_coredumpdir_configured) {
8467 if (prctl(PR_SET_DUMPABLE, 1)) {
8468 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8469 "mod_wsgi (pid=%d): Set dumpable failed. This child "
8470 "will not coredump after software errors.", getpid());
8471 }
8472 }
8473 #endif
8474
8475 return 0;
8476 }
8477
wsgi_setup_socket(WSGIProcessGroup * process)8478 static int wsgi_setup_socket(WSGIProcessGroup *process)
8479 {
8480 int sockfd = -1;
8481 struct sockaddr_un addr;
8482 mode_t omask;
8483 int rc;
8484
8485 int sendsz = process->send_buffer_size;
8486 int recvsz = process->recv_buffer_size;
8487
8488 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
8489 "mod_wsgi (pid=%d): Socket for '%s' is '%s'.",
8490 getpid(), process->name, process->socket_path);
8491
8492 if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
8493 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8494 "mod_wsgi (pid=%d): Couldn't create unix domain "
8495 "socket.", getpid());
8496 return -1;
8497 }
8498
8499 #ifdef SO_SNDBUF
8500 if (sendsz) {
8501 if (setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF,
8502 (void *)&sendsz, sizeof(sendsz)) == -1) {
8503 ap_log_error(APLOG_MARK, APLOG_WARNING, errno, wsgi_server,
8504 "mod_wsgi (pid=%d): Failed to set send buffer "
8505 "size on daemon process socket.", getpid());
8506 }
8507 }
8508 #endif
8509 #ifdef SO_RCVBUF
8510 if (recvsz) {
8511 if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
8512 (void *)&recvsz, sizeof(recvsz)) == -1) {
8513 ap_log_error(APLOG_MARK, APLOG_WARNING, errno, wsgi_server,
8514 "mod_wsgi (pid=%d): Failed to set receive buffer "
8515 "size on daemon process socket.", getpid());
8516 }
8517 }
8518 #endif
8519
8520 if (strlen(process->socket_path) > sizeof(addr.sun_path)) {
8521 ap_log_error(APLOG_MARK, APLOG_ALERT, 0, wsgi_server,
8522 "mod_wsgi (pid=%d): Length of path for daemon process "
8523 "socket exceeds maxmimum allowed value and will be "
8524 "truncated, resulting in likely failure to bind the "
8525 "socket, or other later related failure.", getpid());
8526 }
8527
8528 memset(&addr, 0, sizeof(addr));
8529 addr.sun_family = AF_UNIX;
8530 apr_cpystrn(addr.sun_path, process->socket_path, sizeof(addr.sun_path));
8531
8532 omask = umask(0077);
8533 rc = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
8534
8535 if (rc < 0 && errno == EADDRINUSE) {
8536 ap_log_error(APLOG_MARK, APLOG_WARNING, errno, wsgi_server,
8537 "mod_wsgi (pid=%d): Removing stale unix domain "
8538 "socket '%s'.", getpid(), process->socket_path);
8539
8540 unlink(process->socket_path);
8541
8542 rc = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
8543 }
8544
8545 umask(omask);
8546
8547 if (rc < 0) {
8548 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8549 "mod_wsgi (pid=%d): Couldn't bind unix domain "
8550 "socket '%s'.", getpid(), process->socket_path);
8551
8552 close(sockfd);
8553
8554 return -1;
8555 }
8556
8557 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
8558 "mod_wsgi (pid=%d): Listen backlog for socket '%s' is '%d'.",
8559 getpid(), process->socket_path, process->listen_backlog);
8560
8561 if (listen(sockfd, process->listen_backlog) < 0) {
8562 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8563 "mod_wsgi (pid=%d): Couldn't listen on unix domain "
8564 "socket.", getpid());
8565
8566 close(sockfd);
8567
8568 return -1;
8569 }
8570
8571 /*
8572 * Set the ownership of the UNIX listener socket. This would
8573 * normally be the Apache user that the Apache server child
8574 * processes run as, as they are the only processes that
8575 * would connect to the sockets. In the case of ITK MPM,
8576 * having them owned by Apache user is useless as at the
8577 * time the request is to be proxied, the Apache server
8578 * child process will have uid corresponding to the user
8579 * whose request they are handling. For ITK, thus set the
8580 * ownership to be the same as the daemon processes. This is
8581 * still restrictive, in that can only connect to daemon
8582 * process group running under same user, but most of the
8583 * time that is what you would want anyway when using ITK
8584 * MPM.
8585 */
8586
8587 if (!geteuid()) {
8588 #if defined(MPM_ITK) || defined(ITK_MPM)
8589 uid_t socket_uid = process->uid;
8590 #else
8591 uid_t socket_uid = ap_unixd_config.user_id;
8592 #endif
8593
8594 if (process->socket_user)
8595 socket_uid = ap_uname2id(process->socket_user);
8596
8597 if (chown(process->socket_path, socket_uid, -1) < 0) {
8598 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
8599 "mod_wsgi (pid=%d): Couldn't change owner of unix "
8600 "domain socket '%s' to uid=%ld.", getpid(),
8601 process->socket_path, (long)socket_uid);
8602
8603 close(sockfd);
8604
8605 return -1;
8606 }
8607 }
8608
8609 return sockfd;
8610 }
8611
8612 static int wsgi_hook_daemon_handler(conn_rec *c);
8613
wsgi_process_socket(apr_pool_t * p,apr_socket_t * sock,apr_bucket_alloc_t * bucket_alloc,WSGIDaemonProcess * daemon)8614 static void wsgi_process_socket(apr_pool_t *p, apr_socket_t *sock,
8615 apr_bucket_alloc_t *bucket_alloc,
8616 WSGIDaemonProcess *daemon)
8617 {
8618 apr_status_t rv;
8619
8620 conn_rec *c;
8621 ap_sb_handle_t *sbh;
8622 core_net_rec *net;
8623
8624 /*
8625 * This duplicates Apache connection setup. This is done
8626 * here rather than letting Apache do it so that avoid the
8627 * possibility that any Apache modules, such as mod_ssl
8628 * will add their own input/output filters to the chain.
8629 */
8630
8631 #if AP_MODULE_MAGIC_AT_LEAST(20110619,0)
8632 /* For 2.4 a NULL sbh pointer should work. */
8633 sbh = NULL;
8634 #else
8635 /* For 2.2 a dummy sbh pointer is needed. */
8636 ap_create_sb_handle(&sbh, p, -1, 0);
8637 #endif
8638
8639 c = (conn_rec *)apr_pcalloc(p, sizeof(conn_rec));
8640
8641 c->sbh = sbh;
8642
8643 c->conn_config = ap_create_conn_config(p);
8644 c->notes = apr_table_make(p, 5);
8645 c->pool = p;
8646
8647 if ((rv = apr_socket_addr_get(&c->local_addr, APR_LOCAL, sock))
8648 != APR_SUCCESS) {
8649 ap_log_error(APLOG_MARK, APLOG_INFO, rv, wsgi_server,
8650 "mod_wsgi (pid=%d): Failed call "
8651 "apr_socket_addr_get(APR_LOCAL).", getpid());
8652 apr_socket_close(sock);
8653 return;
8654 }
8655 apr_sockaddr_ip_get(&c->local_ip, c->local_addr);
8656
8657 #if AP_MODULE_MAGIC_AT_LEAST(20111130,0)
8658 if ((rv = apr_socket_addr_get(&c->client_addr, APR_REMOTE, sock))
8659 != APR_SUCCESS) {
8660 ap_log_error(APLOG_MARK, APLOG_INFO, rv, wsgi_server,
8661 "mod_wsgi (pid=%d): Failed call "
8662 "apr_socket_addr_get(APR_REMOTE).", getpid());
8663 apr_socket_close(sock);
8664 return;
8665 }
8666 c->client_ip = "unknown";
8667 #else
8668 if ((rv = apr_socket_addr_get(&c->remote_addr, APR_REMOTE, sock))
8669 != APR_SUCCESS) {
8670 ap_log_error(APLOG_MARK, APLOG_INFO, rv, wsgi_server,
8671 "mod_wsgi (pid=%d): Failed call "
8672 "apr_socket_addr_get(APR_REMOTE).", getpid());
8673 apr_socket_close(sock);
8674 return;
8675 }
8676 c->remote_ip = "unknown";
8677 #endif
8678
8679 c->base_server = daemon->group->server;
8680
8681 c->bucket_alloc = bucket_alloc;
8682 c->id = 1;
8683
8684 net = apr_palloc(c->pool, sizeof(core_net_rec));
8685
8686 if (daemon->group->socket_timeout)
8687 rv = apr_socket_timeout_set(sock, daemon->group->socket_timeout);
8688 else
8689 rv = apr_socket_timeout_set(sock, c->base_server->timeout);
8690
8691 if (rv != APR_SUCCESS) {
8692 ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, wsgi_server,
8693 "mod_wsgi (pid=%d): Failed call "
8694 "apr_socket_timeout_set().", getpid());
8695 }
8696
8697 net->c = c;
8698 net->in_ctx = NULL;
8699 net->out_ctx = NULL;
8700 net->client_socket = sock;
8701
8702 ap_set_module_config(net->c->conn_config, &core_module, sock);
8703 ap_add_input_filter_handle(ap_core_input_filter_handle,
8704 net, NULL, net->c);
8705 ap_add_output_filter_handle(ap_core_output_filter_handle,
8706 net, NULL, net->c);
8707
8708 wsgi_hook_daemon_handler(c);
8709
8710 ap_lingering_close(c);
8711 }
8712
wsgi_worker_acquire(int id)8713 static apr_status_t wsgi_worker_acquire(int id)
8714 {
8715 WSGIThreadStack *stack = wsgi_worker_stack;
8716 WSGIDaemonThread *thread = &wsgi_worker_threads[id];
8717
8718 while (1) {
8719 apr_uint32_t state = stack->state;
8720 if (state & (WSGI_STACK_TERMINATED | WSGI_STACK_NO_LISTENER)) {
8721 if (state & WSGI_STACK_TERMINATED) {
8722 return APR_EINVAL;
8723 }
8724 if (apr_atomic_cas32(&(stack->state), WSGI_STACK_LAST, state) !=
8725 state) {
8726 continue;
8727 }
8728 else {
8729 return APR_SUCCESS;
8730 }
8731 }
8732 thread->next = state;
8733 if (apr_atomic_cas32(&(stack->state), (unsigned)id, state) != state) {
8734 continue;
8735 }
8736 else {
8737 apr_status_t rv;
8738
8739 if (thread->wakeup) {
8740 thread->wakeup = 0;
8741
8742 return APR_SUCCESS;
8743 }
8744
8745 rv = apr_thread_cond_wait(thread->condition, thread->mutex);
8746
8747 while (rv == APR_SUCCESS && !thread->wakeup)
8748 rv = apr_thread_cond_wait(thread->condition, thread->mutex);
8749
8750 if (rv != APR_SUCCESS) {
8751 ap_log_error(APLOG_MARK, APLOG_CRIT, rv,
8752 wsgi_server, "mod_wsgi (pid=%d): "
8753 "Wait on thread %d wakeup condition variable "
8754 "failed.", getpid(), id);
8755 }
8756
8757 thread->wakeup = 0;
8758
8759 return rv;
8760 }
8761 }
8762 }
8763
wsgi_worker_release(void)8764 static apr_status_t wsgi_worker_release(void)
8765 {
8766 WSGIThreadStack *stack = wsgi_worker_stack;
8767
8768 while (1) {
8769 apr_uint32_t state = stack->state;
8770 unsigned int first = state & WSGI_STACK_HEAD;
8771 if (first == WSGI_STACK_LAST) {
8772 if (apr_atomic_cas32(&(stack->state),
8773 state | WSGI_STACK_NO_LISTENER,
8774 state) != state) {
8775 continue;
8776 }
8777 else {
8778 return APR_SUCCESS;
8779 }
8780 }
8781 else {
8782 WSGIDaemonThread *thread = &wsgi_worker_threads[first];
8783 if (apr_atomic_cas32(&(stack->state),
8784 (state ^ first) | thread->next,
8785 state) != state) {
8786 continue;
8787 }
8788 else {
8789 /*
8790 * Flag that thread should be woken up and then
8791 * signal it via the condition variable.
8792 */
8793
8794 apr_status_t rv;
8795 if ((rv = apr_thread_mutex_lock(thread->mutex)) !=
8796 APR_SUCCESS) {
8797 return rv;
8798 }
8799
8800 thread->wakeup = 1;
8801
8802 if ((rv = apr_thread_mutex_unlock(thread->mutex)) !=
8803 APR_SUCCESS) {
8804 return rv;
8805 }
8806
8807 return apr_thread_cond_signal(thread->condition);
8808 }
8809 }
8810 }
8811 }
8812
wsgi_worker_shutdown(void)8813 static apr_status_t wsgi_worker_shutdown(void)
8814 {
8815 int i;
8816 apr_status_t rv;
8817 WSGIThreadStack *stack = wsgi_worker_stack;
8818
8819 while (1) {
8820 apr_uint32_t state = stack->state;
8821 if (apr_atomic_cas32(&(stack->state), state | WSGI_STACK_TERMINATED,
8822 state) == state) {
8823 break;
8824 }
8825 }
8826 for (i = 0; i < wsgi_daemon_process->group->threads; i++) {
8827 if ((rv = wsgi_worker_release()) != APR_SUCCESS) {
8828 return rv;
8829 }
8830 }
8831 return APR_SUCCESS;
8832 }
8833
wsgi_daemon_worker(apr_pool_t * p,WSGIDaemonThread * thread)8834 static void wsgi_daemon_worker(apr_pool_t *p, WSGIDaemonThread *thread)
8835 {
8836 apr_status_t status;
8837 apr_socket_t *socket;
8838
8839 apr_pool_t *ptrans;
8840
8841 apr_pollset_t *pollset;
8842 apr_pollfd_t pfd = { 0 };
8843 apr_int32_t numdesc;
8844 const apr_pollfd_t *pdesc;
8845
8846 apr_bucket_alloc_t *bucket_alloc;
8847
8848 WSGIDaemonProcess *daemon = thread->process;
8849 WSGIProcessGroup *group = daemon->group;
8850
8851 /* Loop until signal received to shutdown daemon process. */
8852
8853 while (!wsgi_daemon_shutdown) {
8854 apr_status_t rv;
8855
8856 /*
8857 * Only allow one thread in this process to attempt to
8858 * acquire the global process lock as the global process
8859 * lock will actually allow all threads in this process
8860 * through once one in this process acquires lock. Only
8861 * allowing one means better chance of another process
8862 * subsequently getting it thereby distributing requests
8863 * across processes better and reducing chance of Python
8864 * GIL contention.
8865 */
8866
8867 wsgi_worker_acquire(thread->id);
8868
8869 if (wsgi_daemon_shutdown)
8870 break;
8871
8872 if (group->mutex) {
8873 /*
8874 * Grab the accept mutex across all daemon processes
8875 * in this process group.
8876 */
8877
8878 rv = apr_proc_mutex_lock(group->mutex);
8879
8880 if (rv != APR_SUCCESS) {
8881 #if 0
8882 #if defined(EIDRM)
8883 /*
8884 * When using multiple threads locking the
8885 * process accept mutex fails with an EIDRM when
8886 * process being shutdown but signal check
8887 * hasn't triggered quick enough to set shutdown
8888 * flag. This causes lots of error messages to
8889 * be logged which make it look like something
8890 * nasty has happened even when it hasn't. For
8891 * now assume that if multiple threads and EIDRM
8892 * occurs that it is okay and the process is
8893 * being shutdown. The condition should by
8894 * rights only occur when the Apache parent
8895 * process is being shutdown or has died for
8896 * some reason so daemon process would logically
8897 * therefore also be in process of being
8898 * shutdown or killed.
8899 */
8900 if (!strcmp(apr_proc_mutex_name(group->mutex), "sysvsem")) {
8901 if (errno == EIDRM && group->threads > 1)
8902 wsgi_daemon_shutdown = 1;
8903 }
8904 #endif
8905 #endif
8906
8907 if (!wsgi_daemon_shutdown) {
8908 ap_log_error(APLOG_MARK, APLOG_CRIT, rv,
8909 wsgi_server, "mod_wsgi (pid=%d): "
8910 "Couldn't acquire accept mutex '%s'. "
8911 "Shutting down daemon process.",
8912 getpid(), group->socket_path);
8913
8914 wsgi_daemon_shutdown++;
8915 kill(getpid(), SIGTERM);
8916 sleep(5);
8917 }
8918
8919 break;
8920 }
8921
8922 /*
8923 * Daemon process being shutdown so don't accept the
8924 * connection after all.
8925 */
8926
8927 if (wsgi_daemon_shutdown) {
8928 apr_proc_mutex_unlock(group->mutex);
8929
8930 wsgi_worker_release();
8931
8932 break;
8933 }
8934 }
8935
8936 apr_pool_create(&ptrans, p);
8937
8938 /*
8939 * Accept socket connection from the child process. We
8940 * test the socket for whether it is ready before actually
8941 * performing the accept() so that can know for sure that
8942 * we will be processing a request and flag thread as
8943 * running. Only bother to do join with thread which is
8944 * actually running when process is being shutdown.
8945 */
8946
8947 apr_pollset_create(&pollset, 1, ptrans, 0);
8948
8949 memset(&pfd, '\0', sizeof(pfd));
8950 pfd.desc_type = APR_POLL_SOCKET;
8951 pfd.desc.s = daemon->listener;
8952 pfd.reqevents = APR_POLLIN;
8953 pfd.client_data = daemon;
8954
8955 apr_pollset_add(pollset, &pfd);
8956
8957 rv = apr_pollset_poll(pollset, -1, &numdesc, &pdesc);
8958
8959 if (rv != APR_SUCCESS && !APR_STATUS_IS_EINTR(rv)) {
8960 ap_log_error(APLOG_MARK, APLOG_CRIT, rv,
8961 wsgi_server, "mod_wsgi (pid=%d): "
8962 "Unable to poll daemon socket for '%s'. "
8963 "Shutting down daemon process.",
8964 getpid(), group->socket_path);
8965
8966 wsgi_daemon_shutdown++;
8967 kill(getpid(), SIGTERM);
8968 sleep(5);
8969
8970 break;
8971 }
8972
8973 if (wsgi_daemon_shutdown) {
8974 if (group->mutex)
8975 apr_proc_mutex_unlock(group->mutex);
8976
8977 wsgi_worker_release();
8978
8979 apr_pool_destroy(ptrans);
8980
8981 break;
8982 }
8983
8984 if (rv != APR_SUCCESS && APR_STATUS_IS_EINTR(rv)) {
8985 if (group->mutex)
8986 apr_proc_mutex_unlock(group->mutex);
8987
8988 wsgi_worker_release();
8989
8990 apr_pool_destroy(ptrans);
8991
8992 continue;
8993 }
8994
8995 thread->running = 1;
8996
8997 status = apr_socket_accept(&socket, daemon->listener, ptrans);
8998
8999 if (group->mutex) {
9000 apr_status_t rv;
9001 rv = apr_proc_mutex_unlock(group->mutex);
9002
9003 if (rv != APR_SUCCESS) {
9004 if (!wsgi_daemon_shutdown) {
9005 wsgi_worker_release();
9006
9007 ap_log_error(APLOG_MARK, APLOG_CRIT, rv,
9008 wsgi_server, "mod_wsgi (pid=%d): "
9009 "Couldn't release accept mutex '%s'.",
9010 getpid(), group->socket_path);
9011
9012 apr_pool_destroy(ptrans);
9013 thread->running = 0;
9014
9015 break;
9016 }
9017 }
9018 }
9019
9020 wsgi_worker_release();
9021
9022 if (status != APR_SUCCESS && APR_STATUS_IS_EINTR(status)) {
9023 apr_pool_destroy(ptrans);
9024 thread->running = 0;
9025
9026 continue;
9027 }
9028
9029 /* Process the request proxied from the child process. */
9030
9031 apr_thread_mutex_lock(wsgi_monitor_lock);
9032 thread->request = apr_time_now();
9033 apr_thread_mutex_unlock(wsgi_monitor_lock);
9034
9035 bucket_alloc = apr_bucket_alloc_create(ptrans);
9036 wsgi_process_socket(ptrans, socket, bucket_alloc, daemon);
9037
9038 apr_thread_mutex_lock(wsgi_monitor_lock);
9039 thread->request = 0;
9040 apr_thread_mutex_unlock(wsgi_monitor_lock);
9041
9042 /* Cleanup ready for next request. */
9043
9044 apr_pool_destroy(ptrans);
9045
9046 thread->running = 0;
9047
9048 /* Check to see if maximum number of requests reached. */
9049
9050 if (daemon->group->maximum_requests) {
9051 if (--wsgi_request_count <= 0) {
9052 if (wsgi_graceful_timeout && wsgi_active_requests) {
9053 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9054 "mod_wsgi (pid=%d): Maximum requests "
9055 "reached, attempt a graceful shutdown "
9056 "'%s'.", getpid(), daemon->group->name);
9057
9058 apr_thread_mutex_lock(wsgi_monitor_lock);
9059 wsgi_graceful_shutdown_time = apr_time_now();
9060 wsgi_graceful_shutdown_time += wsgi_graceful_timeout;
9061 apr_thread_mutex_unlock(wsgi_monitor_lock);
9062 }
9063 else {
9064 if (!wsgi_daemon_shutdown) {
9065 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9066 "mod_wsgi (pid=%d): Maximum requests "
9067 "reached, triggering immediate shutdown "
9068 "'%s'.", getpid(), daemon->group->name);
9069 }
9070
9071 wsgi_daemon_shutdown++;
9072 kill(getpid(), SIGINT);
9073 }
9074 }
9075 }
9076 else if (wsgi_daemon_graceful && !wsgi_daemon_shutdown) {
9077 if (wsgi_active_requests == 0) {
9078 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9079 "mod_wsgi (pid=%d): Requests have completed, "
9080 "triggering immediate shutdown '%s'.",
9081 getpid(), daemon->group->name);
9082
9083 wsgi_daemon_shutdown++;
9084 kill(getpid(), SIGINT);
9085 }
9086 }
9087 }
9088
9089 if (wsgi_server_config->verbose_debugging) {
9090 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9091 "mod_wsgi (pid=%d): Exiting thread %d in daemon "
9092 "process '%s'.", getpid(), thread->id,
9093 thread->process->group->name);
9094 }
9095 else {
9096 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9097 "mod_wsgi (pid=%d): Exiting thread %d in daemon "
9098 "process '%s'.", getpid(), thread->id,
9099 thread->process->group->name);
9100 }
9101 }
9102
wsgi_daemon_thread(apr_thread_t * thd,void * data)9103 static void *wsgi_daemon_thread(apr_thread_t *thd, void *data)
9104 {
9105 WSGIDaemonThread *thread = data;
9106 apr_pool_t *p = apr_thread_pool_get(thd);
9107
9108 if (wsgi_server_config->verbose_debugging) {
9109 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9110 "mod_wsgi (pid=%d): Started thread %d in daemon "
9111 "process '%s'.", getpid(), thread->id,
9112 thread->process->group->name);
9113 }
9114 else {
9115 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9116 "mod_wsgi (pid=%d): Started thread %d in daemon "
9117 "process '%s'.", getpid(), thread->id,
9118 thread->process->group->name);
9119 }
9120
9121 apr_thread_mutex_lock(thread->mutex);
9122
9123 wsgi_daemon_worker(p, thread);
9124
9125 apr_thread_exit(thd, APR_SUCCESS);
9126
9127 return NULL;
9128 }
9129
wsgi_reaper_thread(apr_thread_t * thd,void * data)9130 static void *wsgi_reaper_thread(apr_thread_t *thd, void *data)
9131 {
9132 WSGIDaemonProcess *daemon = data;
9133
9134 sleep(daemon->group->shutdown_timeout);
9135
9136 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9137 "mod_wsgi (pid=%d): Aborting process '%s'.",
9138 getpid(), daemon->group->name);
9139
9140 wsgi_exit_daemon_process(-1);
9141
9142 return NULL;
9143 }
9144
wsgi_deadlock_thread(apr_thread_t * thd,void * data)9145 static void *wsgi_deadlock_thread(apr_thread_t *thd, void *data)
9146 {
9147 WSGIDaemonProcess *daemon = data;
9148
9149 PyGILState_STATE gilstate;
9150
9151 if (wsgi_server_config->verbose_debugging) {
9152 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9153 "mod_wsgi (pid=%d): Enable deadlock thread in "
9154 "process '%s'.", getpid(), daemon->group->name);
9155 }
9156
9157 apr_thread_mutex_lock(wsgi_monitor_lock);
9158 wsgi_deadlock_shutdown_time = apr_time_now();
9159 wsgi_deadlock_shutdown_time += wsgi_deadlock_timeout;
9160 apr_thread_mutex_unlock(wsgi_monitor_lock);
9161
9162 while (1) {
9163 apr_sleep(apr_time_from_sec(1));
9164
9165 apr_thread_mutex_lock(wsgi_shutdown_lock);
9166
9167 if (!wsgi_daemon_shutdown) {
9168 gilstate = PyGILState_Ensure();
9169 PyGILState_Release(gilstate);
9170 }
9171
9172 apr_thread_mutex_unlock(wsgi_shutdown_lock);
9173
9174 apr_thread_mutex_lock(wsgi_monitor_lock);
9175 wsgi_deadlock_shutdown_time = apr_time_now();
9176 wsgi_deadlock_shutdown_time += wsgi_deadlock_timeout;
9177 apr_thread_mutex_unlock(wsgi_monitor_lock);
9178 }
9179
9180 return NULL;
9181 }
9182
wsgi_monitor_thread(apr_thread_t * thd,void * data)9183 static void *wsgi_monitor_thread(apr_thread_t *thd, void *data)
9184 {
9185 WSGIDaemonProcess *daemon = data;
9186 WSGIProcessGroup *group = daemon->group;
9187
9188 int restart = 0;
9189
9190 if (wsgi_server_config->verbose_debugging) {
9191 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9192 "mod_wsgi (pid=%d): Enable monitor thread in "
9193 "process '%s'.", getpid(), group->name);
9194
9195 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9196 "mod_wsgi (pid=%d): Startup timeout is %d.",
9197 getpid(), (int)(apr_time_sec(wsgi_startup_timeout)));
9198 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9199 "mod_wsgi (pid=%d): Deadlock timeout is %d.",
9200 getpid(), (int)(apr_time_sec(wsgi_deadlock_timeout)));
9201 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9202 "mod_wsgi (pid=%d): Idle inactivity timeout is %d.",
9203 getpid(), (int)(apr_time_sec(wsgi_idle_timeout)));
9204 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9205 "mod_wsgi (pid=%d): Request time limit is %d.",
9206 getpid(), (int)(apr_time_sec(wsgi_request_timeout)));
9207 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9208 "mod_wsgi (pid=%d): Graceful timeout is %d.",
9209 getpid(), (int)(apr_time_sec(wsgi_graceful_timeout)));
9210 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9211 "mod_wsgi (pid=%d): Eviction timeout is %d.",
9212 getpid(), (int)(apr_time_sec(wsgi_eviction_timeout)));
9213 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9214 "mod_wsgi (pid=%d): Restart interval is %d.",
9215 getpid(), (int)(apr_time_sec(wsgi_restart_interval)));
9216 }
9217
9218 /*
9219 * If a restart interval was specified then set up the time for
9220 * when the restart should occur.
9221 */
9222
9223 if (wsgi_restart_interval) {
9224 wsgi_restart_shutdown_time = apr_time_now();
9225 wsgi_restart_shutdown_time += wsgi_restart_interval;
9226 }
9227
9228 while (1) {
9229 apr_time_t now;
9230
9231 apr_time_t startup_time;
9232 apr_time_t deadlock_time;
9233 apr_time_t idle_time;
9234 apr_time_t graceful_time;
9235 apr_time_t restart_time;
9236
9237 apr_time_t request_time = 0;
9238
9239 apr_interval_time_t period = 0;
9240
9241 int i = 0;
9242
9243 now = apr_time_now();
9244
9245 apr_thread_mutex_lock(wsgi_monitor_lock);
9246
9247 startup_time = wsgi_startup_shutdown_time;
9248 deadlock_time = wsgi_deadlock_shutdown_time;
9249 idle_time = wsgi_idle_shutdown_time;
9250 graceful_time = wsgi_graceful_shutdown_time;
9251 restart_time = wsgi_restart_shutdown_time;
9252
9253 if (wsgi_request_timeout && wsgi_worker_threads) {
9254 for (i = 0; i<wsgi_daemon_process->group->threads; i++) {
9255 if (wsgi_worker_threads[i].request)
9256 request_time += (now - wsgi_worker_threads[i].request);
9257 }
9258 }
9259
9260 request_time /= wsgi_daemon_process->group->threads;
9261
9262 apr_thread_mutex_unlock(wsgi_monitor_lock);
9263
9264 if (!restart && wsgi_request_timeout) {
9265 if (request_time > wsgi_request_timeout) {
9266 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9267 "mod_wsgi (pid=%d): Daemon process request "
9268 "time limit exceeded, stopping process "
9269 "'%s'.", getpid(), group->name);
9270
9271 wsgi_shutdown_reason = "request_timeout";
9272
9273 wsgi_dump_stack_traces = 1;
9274
9275 restart = 1;
9276 }
9277 }
9278
9279 if (!restart && wsgi_startup_timeout) {
9280 if (startup_time > 0) {
9281 if (startup_time <= now) {
9282 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9283 "mod_wsgi (pid=%d): Application startup "
9284 "timer expired, stopping process '%s'.",
9285 getpid(), group->name);
9286
9287 wsgi_shutdown_reason = "startup_timeout";
9288
9289 restart = 1;
9290 }
9291 else {
9292 period = startup_time - now;
9293 }
9294 }
9295 }
9296
9297 if (!restart && wsgi_restart_interval) {
9298 if (restart_time > 0) {
9299 if (restart_time <= now) {
9300 if (!wsgi_daemon_graceful) {
9301 if (wsgi_active_requests) {
9302 wsgi_daemon_graceful++;
9303
9304 apr_thread_mutex_lock(wsgi_monitor_lock);
9305 wsgi_graceful_shutdown_time = apr_time_now();
9306 wsgi_graceful_shutdown_time += wsgi_graceful_timeout;
9307 apr_thread_mutex_unlock(wsgi_monitor_lock);
9308
9309 ap_log_error(APLOG_MARK, APLOG_INFO, 0,
9310 wsgi_server, "mod_wsgi (pid=%d): "
9311 "Application restart timer expired, "
9312 "waiting for requests to complete "
9313 "'%s'.", getpid(),
9314 daemon->group->name);
9315 }
9316 else {
9317 ap_log_error(APLOG_MARK, APLOG_INFO, 0,
9318 wsgi_server, "mod_wsgi (pid=%d): "
9319 "Application restart timer expired, "
9320 "stopping process '%s'.", getpid(),
9321 daemon->group->name);
9322
9323 wsgi_shutdown_reason = "restart_interval";
9324
9325 restart = 1;
9326 }
9327 }
9328 }
9329 else {
9330 period = restart_time - now;
9331 }
9332 }
9333 }
9334
9335 if (!restart && wsgi_deadlock_timeout) {
9336 if (deadlock_time) {
9337 if (deadlock_time <= now) {
9338 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9339 "mod_wsgi (pid=%d): Daemon process deadlock "
9340 "timer expired, stopping process '%s'.",
9341 getpid(), group->name);
9342
9343 restart = 1;
9344 }
9345 else {
9346 if (!period || ((deadlock_time - now) < period))
9347 period = deadlock_time - now;
9348 }
9349 }
9350 else {
9351 if (!period || (wsgi_deadlock_timeout < period))
9352 period = wsgi_deadlock_timeout;
9353 }
9354 }
9355
9356 if (!restart && wsgi_idle_timeout) {
9357 if (idle_time) {
9358 if (idle_time <= now) {
9359 if (wsgi_active_requests == 0) {
9360 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9361 "mod_wsgi (pid=%d): Daemon process "
9362 "idle inactivity timer expired, "
9363 "stopping process '%s'.", getpid(),
9364 group->name);
9365
9366 wsgi_shutdown_reason = "inactivity_timeout";
9367
9368 restart = 1;
9369 }
9370 else {
9371 /* Ignore for now as still have requests. */
9372
9373 if (!period || (wsgi_idle_timeout < period))
9374 period = wsgi_idle_timeout;
9375 }
9376 }
9377 else {
9378 if (!period || ((idle_time - now) < period))
9379 period = idle_time - now;
9380 }
9381 }
9382 else {
9383 if (!period || (wsgi_idle_timeout < period))
9384 period = wsgi_idle_timeout;
9385 }
9386 }
9387
9388 if (!restart && wsgi_graceful_timeout) {
9389 if (graceful_time) {
9390 if (graceful_time <= now) {
9391 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9392 "mod_wsgi (pid=%d): Daemon process "
9393 "graceful timer expired '%s'.", getpid(),
9394 group->name);
9395
9396 restart = 1;
9397 }
9398 else {
9399 if (!period || ((graceful_time - now) < period))
9400 period = graceful_time - now;
9401 else if (wsgi_graceful_timeout < period)
9402 period = wsgi_graceful_timeout;
9403 }
9404 }
9405 else {
9406 if (!period || (wsgi_graceful_timeout < period))
9407 period = wsgi_graceful_timeout;
9408 }
9409 }
9410
9411 if (!restart && wsgi_eviction_timeout) {
9412 if (graceful_time) {
9413 if (graceful_time <= now) {
9414 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9415 "mod_wsgi (pid=%d): Daemon process "
9416 "graceful timer expired '%s'.", getpid(),
9417 group->name);
9418
9419 restart = 1;
9420 }
9421 else {
9422 if (!period || ((graceful_time - now) < period))
9423 period = graceful_time - now;
9424 else if (wsgi_eviction_timeout < period)
9425 period = wsgi_eviction_timeout;
9426 }
9427 }
9428 else {
9429 if (!period || (wsgi_eviction_timeout < period))
9430 period = wsgi_eviction_timeout;
9431 }
9432 }
9433
9434 if (restart) {
9435 wsgi_daemon_shutdown++;
9436 kill(getpid(), SIGINT);
9437 }
9438
9439 if (restart || wsgi_request_timeout || period <= 0)
9440 period = apr_time_from_sec(1);
9441
9442 apr_sleep(period);
9443 }
9444
9445 return NULL;
9446 }
9447
9448 #if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 5)
wsgi_log_stack_traces(void)9449 static void wsgi_log_stack_traces(void)
9450 {
9451 PyGILState_STATE state;
9452
9453 PyObject *threads = NULL;
9454
9455 /*
9456 * This should only be called on shutdown so don't try and log
9457 * any errors, just dump them straight out.
9458 */
9459
9460 state = PyGILState_Ensure();
9461
9462 threads = _PyThread_CurrentFrames();
9463
9464 if (threads && PyDict_Size(threads) != 0) {
9465 PyObject *seq = NULL;
9466
9467 seq = PyObject_GetIter(threads);
9468
9469 if (seq) {
9470 PyObject *id = NULL;
9471 PyObject *frame = NULL;
9472
9473 Py_ssize_t i = 0;
9474
9475 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9476 "mod_wsgi (pid=%d): Dumping stack trace for "
9477 "active Python threads.", getpid());
9478
9479 while (PyDict_Next(threads, &i, &id, &frame)) {
9480 apr_int64_t thread_id = 0;
9481
9482 PyFrameObject *current = NULL;
9483
9484 thread_id = PyLong_AsLong(id);
9485
9486 current = (PyFrameObject *)frame;
9487
9488 while (current) {
9489 int lineno;
9490
9491 const char *filename = NULL;
9492 const char *name = NULL;
9493
9494 if (current->f_trace) {
9495 lineno = current->f_lineno;
9496 }
9497 else {
9498 lineno = PyCode_Addr2Line(current->f_code,
9499 current->f_lasti);
9500 }
9501
9502 #if PY_MAJOR_VERSION >= 3
9503 filename = PyUnicode_AsUTF8(current->f_code->co_filename);
9504 name = PyUnicode_AsUTF8(current->f_code->co_name);
9505 #else
9506 filename = PyString_AsString(current->f_code->co_filename);
9507 name = PyString_AsString(current->f_code->co_name);
9508 #endif
9509
9510 if (current == (PyFrameObject *)frame) {
9511 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9512 "mod_wsgi (pid=%d): Thread %" APR_INT64_T_FMT
9513 " executing file \"%s\", line %d, in %s",
9514 getpid(), thread_id, filename, lineno, name);
9515 }
9516 else {
9517 if (current->f_back) {
9518 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9519 "mod_wsgi (pid=%d): called from file "
9520 "\"%s\", line %d, in %s,", getpid(),
9521 filename, lineno, name);
9522 }
9523 else {
9524 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9525 "mod_wsgi (pid=%d): called from file "
9526 "\"%s\", line %d, in %s.", getpid(),
9527 filename, lineno, name);
9528 }
9529 }
9530
9531 current = current->f_back;
9532 }
9533 }
9534 }
9535 else {
9536 ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
9537 "mod_wsgi (pid=%d): Failed to iterate over "
9538 "current frames for active threads.", getpid());
9539
9540 PyErr_Print();
9541 PyErr_Clear();
9542 }
9543 }
9544 else {
9545 ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
9546 "mod_wsgi (pid=%d): Failed to get current frames "
9547 "for active threads.", getpid());
9548
9549 PyErr_Print();
9550 PyErr_Clear();
9551 }
9552
9553 Py_XDECREF(threads);
9554
9555 PyGILState_Release(state);
9556 }
9557 #endif
9558
wsgi_daemon_main(apr_pool_t * p,WSGIDaemonProcess * daemon)9559 static void wsgi_daemon_main(apr_pool_t *p, WSGIDaemonProcess *daemon)
9560 {
9561 apr_threadattr_t *thread_attr;
9562 apr_thread_t *reaper = NULL;
9563
9564 int i;
9565 apr_status_t rv;
9566 apr_status_t thread_rv;
9567
9568 apr_pollfd_t poll_fd;
9569 apr_int32_t poll_count = 0;
9570
9571 /*
9572 * Setup poll object for listening for shutdown notice from
9573 * signal handler.
9574 */
9575
9576 poll_fd.desc_type = APR_POLL_FILE;
9577 poll_fd.reqevents = APR_POLLIN;
9578 poll_fd.desc.f = wsgi_signal_pipe_in;
9579
9580 /* Initialise maximum request count for daemon. */
9581
9582 if (daemon->group->maximum_requests)
9583 wsgi_request_count = daemon->group->maximum_requests;
9584
9585 /* Ensure that threads are joinable. */
9586
9587 apr_threadattr_create(&thread_attr, p);
9588 apr_threadattr_detach_set(thread_attr, 0);
9589
9590 #if (APR_MAJOR_VERSION >= 1)
9591 if (daemon->group->stack_size) {
9592 apr_threadattr_stacksize_set(thread_attr, daemon->group->stack_size);
9593 }
9594 #endif
9595
9596 /* Start monitoring thread if required. */
9597
9598 wsgi_startup_timeout = daemon->group->startup_timeout;
9599 wsgi_deadlock_timeout = daemon->group->deadlock_timeout;
9600 wsgi_idle_timeout = daemon->group->inactivity_timeout;
9601 wsgi_request_timeout = daemon->group->request_timeout;
9602 wsgi_graceful_timeout = daemon->group->graceful_timeout;
9603 wsgi_eviction_timeout = daemon->group->eviction_timeout;
9604 wsgi_restart_interval = daemon->group->restart_interval;
9605
9606 if (wsgi_deadlock_timeout || wsgi_idle_timeout) {
9607 rv = apr_thread_create(&reaper, thread_attr, wsgi_monitor_thread,
9608 daemon, p);
9609
9610 if (rv != APR_SUCCESS) {
9611 ap_log_error(APLOG_MARK, APLOG_ALERT, rv, wsgi_server,
9612 "mod_wsgi (pid=%d): Couldn't create monitor "
9613 "thread in daemon process '%s'.", getpid(),
9614 daemon->group->name);
9615 }
9616 }
9617
9618 if (wsgi_deadlock_timeout) {
9619 if (rv != APR_SUCCESS) {
9620 ap_log_error(APLOG_MARK, APLOG_ALERT, rv, wsgi_server,
9621 "mod_wsgi (pid=%d): Couldn't create deadlock "
9622 "thread in daemon process '%s'.", getpid(),
9623 daemon->group->name);
9624 }
9625
9626 rv = apr_thread_create(&reaper, thread_attr, wsgi_deadlock_thread,
9627 daemon, p);
9628 }
9629
9630 /* Initialise worker stack. */
9631
9632 wsgi_worker_stack = (WSGIThreadStack *)apr_palloc(p,
9633 sizeof(WSGIThreadStack));
9634 wsgi_worker_stack->state = WSGI_STACK_NO_LISTENER | WSGI_STACK_LAST;
9635
9636 /* Start the required number of threads. */
9637
9638 wsgi_worker_threads = (WSGIDaemonThread *)apr_pcalloc(p,
9639 daemon->group->threads * sizeof(WSGIDaemonThread));
9640
9641 if (wsgi_server_config->verbose_debugging) {
9642 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9643 "mod_wsgi (pid=%d): Starting %d threads in daemon "
9644 "process '%s'.", getpid(), daemon->group->threads,
9645 daemon->group->name);
9646 }
9647
9648 for (i=0; i<daemon->group->threads; i++) {
9649 WSGIDaemonThread *thread = &wsgi_worker_threads[i];
9650
9651 if (wsgi_server_config->verbose_debugging) {
9652 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
9653 "mod_wsgi (pid=%d): Starting thread %d in daemon "
9654 "process '%s'.", getpid(), i+1, daemon->group->name);
9655 }
9656
9657 /* Create the mutex and condition variable for this thread. */
9658
9659 rv = apr_thread_cond_create(&thread->condition, p);
9660
9661 if (rv != APR_SUCCESS) {
9662 ap_log_error(APLOG_MARK, APLOG_ALERT, rv, wsgi_server,
9663 "mod_wsgi (pid=%d): Couldn't create worker "
9664 "thread %d state condition variable in daemon "
9665 "process '%s'.", getpid(), i, daemon->group->name);
9666
9667 /*
9668 * Try to force an exit of the process if fail
9669 * to create the worker threads.
9670 */
9671
9672 kill(getpid(), SIGTERM);
9673 sleep(5);
9674 }
9675
9676 rv = apr_thread_mutex_create(&thread->mutex,
9677 APR_THREAD_MUTEX_DEFAULT, p);
9678
9679 if (rv != APR_SUCCESS) {
9680 ap_log_error(APLOG_MARK, APLOG_ALERT, rv, wsgi_server,
9681 "mod_wsgi (pid=%d): Couldn't create worker "
9682 "thread %d state mutex variable in daemon "
9683 "process '%s'.", getpid(), i, daemon->group->name);
9684
9685 /*
9686 * Try to force an exit of the process if fail
9687 * to create the worker threads.
9688 */
9689
9690 kill(getpid(), SIGTERM);
9691 sleep(5);
9692 }
9693
9694 /* Now create the actual thread. */
9695
9696 thread->id = i;
9697 thread->process = daemon;
9698 thread->running = 0;
9699 thread->request = 0;
9700
9701 rv = apr_thread_create(&thread->thread, thread_attr,
9702 wsgi_daemon_thread, thread, p);
9703
9704 if (rv != APR_SUCCESS) {
9705 ap_log_error(APLOG_MARK, APLOG_ALERT, rv, wsgi_server,
9706 "mod_wsgi (pid=%d): Couldn't create worker "
9707 "thread %d in daemon process '%s'.", getpid(),
9708 i, daemon->group->name);
9709
9710 /*
9711 * Try to force an exit of the process if fail
9712 * to create the worker threads.
9713 */
9714
9715 kill(getpid(), SIGTERM);
9716 sleep(5);
9717 }
9718 }
9719
9720 /* Block until we get a process shutdown signal. */
9721
9722 while (1) {
9723 char buf[1];
9724 apr_size_t nbytes = 1;
9725
9726 rv = apr_poll(&poll_fd, 1, &poll_count, -1);
9727 if (APR_STATUS_IS_EINTR(rv))
9728 continue;
9729
9730 rv = apr_file_read(wsgi_signal_pipe_in, buf, &nbytes);
9731
9732 if (rv != APR_SUCCESS || nbytes != 1) {
9733 ap_log_error(APLOG_MARK, APLOG_ALERT, 0, wsgi_server,
9734 "mod_wsgi (pid=%d): Failed read on signal pipe '%s'.",
9735 getpid(), daemon->group->name);
9736
9737 break;
9738 }
9739
9740 if (buf[0] == 'C') {
9741 if (!wsgi_daemon_graceful) {
9742 wsgi_shutdown_reason = "cpu_time_limit";
9743
9744 if (wsgi_active_requests) {
9745 wsgi_daemon_graceful++;
9746
9747 apr_thread_mutex_lock(wsgi_monitor_lock);
9748 wsgi_graceful_shutdown_time = apr_time_now();
9749 wsgi_graceful_shutdown_time += wsgi_graceful_timeout;
9750 apr_thread_mutex_unlock(wsgi_monitor_lock);
9751
9752 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9753 "mod_wsgi (pid=%d): Exceeded CPU time "
9754 "limit, waiting for requests to complete "
9755 "'%s'.", getpid(), daemon->group->name);
9756 }
9757 else {
9758 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9759 "mod_wsgi (pid=%d): Exceeded CPU time "
9760 "limit, triggering immediate shutdown "
9761 "'%s'.", getpid(), daemon->group->name);
9762
9763 wsgi_daemon_shutdown++;
9764 kill(getpid(), SIGINT);
9765 }
9766 }
9767 }
9768 else if (buf[0] == 'G') {
9769 if (!wsgi_daemon_graceful) {
9770 wsgi_shutdown_reason = "graceful_signal";
9771
9772 if (wsgi_active_requests) {
9773 wsgi_daemon_graceful++;
9774
9775 apr_thread_mutex_lock(wsgi_monitor_lock);
9776 wsgi_graceful_shutdown_time = apr_time_now();
9777 if (wsgi_eviction_timeout)
9778 wsgi_graceful_shutdown_time += wsgi_eviction_timeout;
9779 else
9780 wsgi_graceful_shutdown_time += wsgi_graceful_timeout;
9781 apr_thread_mutex_unlock(wsgi_monitor_lock);
9782
9783 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9784 "mod_wsgi (pid=%d): Process eviction "
9785 "requested, waiting for requests to complete "
9786 "'%s'.", getpid(), daemon->group->name);
9787 }
9788 else {
9789 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9790 "mod_wsgi (pid=%d): Process eviction "
9791 "requested, triggering immediate shutdown "
9792 "'%s'.", getpid(), daemon->group->name);
9793
9794 wsgi_daemon_shutdown++;
9795 kill(getpid(), SIGINT);
9796 }
9797 }
9798 }
9799 else
9800 break;
9801 }
9802
9803 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9804 "mod_wsgi (pid=%d): Shutdown requested '%s'.",
9805 getpid(), daemon->group->name);
9806
9807 /*
9808 * Create a reaper thread to abort process if graceful
9809 * shutdown takes too long. Not recommended to disable
9810 * this unless external process is controlling shutdown.
9811 */
9812
9813 if (daemon->group->shutdown_timeout) {
9814 rv = apr_thread_create(&reaper, thread_attr, wsgi_reaper_thread,
9815 daemon, p);
9816
9817 if (rv != APR_SUCCESS) {
9818 ap_log_error(APLOG_MARK, APLOG_ALERT, rv, wsgi_server,
9819 "mod_wsgi (pid=%d): Couldn't create reaper "
9820 "thread in daemon process '%s'.", getpid(),
9821 daemon->group->name);
9822 }
9823 }
9824
9825 /*
9826 * If shutting down process due to reaching request time
9827 * limit, then try and dump out stack traces of any threads
9828 * which are running as a debugging aid.
9829 */
9830
9831 wsgi_publish_process_stopping(wsgi_shutdown_reason);
9832
9833 #if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 5)
9834 if (wsgi_dump_stack_traces)
9835 wsgi_log_stack_traces();
9836 #endif
9837
9838 /*
9839 * Attempt a graceful shutdown by waiting for any
9840 * threads which were processing a request at the time
9841 * of shutdown. In some respects this is a bit pointless
9842 * as even though we allow the requests to be completed,
9843 * the Apache child process which proxied the request
9844 * through to this daemon process could get killed off
9845 * before the daemon process and so the response gets
9846 * cut off or lost.
9847 */
9848
9849 wsgi_worker_shutdown();
9850
9851 for (i=0; i<daemon->group->threads; i++) {
9852 if (wsgi_worker_threads[i].thread && wsgi_worker_threads[i].running) {
9853 rv = apr_thread_join(&thread_rv, wsgi_worker_threads[i].thread);
9854 if (rv != APR_SUCCESS) {
9855 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, wsgi_server,
9856 "mod_wsgi (pid=%d): Couldn't join with "
9857 "worker thread %d in daemon process '%s'.",
9858 getpid(), i, daemon->group->name);
9859 }
9860 }
9861 }
9862 }
9863
wsgi_cleanup_process(void * data)9864 static apr_status_t wsgi_cleanup_process(void *data)
9865 {
9866 WSGIProcessGroup *group = (WSGIProcessGroup *)data;
9867
9868 /* Only do cleanup if in Apache parent process. */
9869
9870 if (wsgi_parent_pid != getpid())
9871 return APR_SUCCESS;
9872
9873 if (group->listener_fd != -1) {
9874 if (close(group->listener_fd) < 0) {
9875 ap_log_error(APLOG_MARK, APLOG_ERR, errno,
9876 wsgi_server, "mod_wsgi (pid=%d): "
9877 "Couldn't close unix domain socket '%s'.",
9878 getpid(), group->socket_path);
9879 }
9880
9881 if (unlink(group->socket_path) < 0 && errno != ENOENT) {
9882 ap_log_error(APLOG_MARK, APLOG_ERR, errno,
9883 wsgi_server, "mod_wsgi (pid=%d): "
9884 "Couldn't unlink unix domain socket '%s'.",
9885 getpid(), group->socket_path);
9886 }
9887 }
9888
9889 return APR_SUCCESS;
9890 }
9891
wsgi_start_process(apr_pool_t * p,WSGIDaemonProcess * daemon)9892 static int wsgi_start_process(apr_pool_t *p, WSGIDaemonProcess *daemon)
9893 {
9894 apr_status_t status;
9895
9896 ap_listen_rec *lr;
9897
9898 WSGIProcessGroup *entries = NULL;
9899 WSGIProcessGroup *entry = NULL;
9900 int i = 0;
9901
9902 if ((status = apr_proc_fork(&daemon->process, p)) < 0) {
9903 ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
9904 "mod_wsgi: Couldn't spawn process '%s'.",
9905 daemon->group->name);
9906 return DECLINED;
9907 }
9908 else if (status == APR_INCHILD) {
9909 if (!geteuid()) {
9910 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9911 "mod_wsgi (pid=%d): Starting process '%s' with "
9912 "uid=%ld, gid=%u and threads=%d.", getpid(),
9913 daemon->group->name, (long)daemon->group->uid,
9914 (unsigned)daemon->group->gid, daemon->group->threads);
9915 }
9916 else {
9917 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
9918 "mod_wsgi (pid=%d): Starting process '%s' with "
9919 "threads=%d.", getpid(), daemon->group->name,
9920 daemon->group->threads);
9921 }
9922
9923 #ifdef HAVE_BINDPROCESSOR
9924 /*
9925 * By default, AIX binds to a single processor. This
9926 * bit unbinds children which will then bind to another
9927 * CPU.
9928 */
9929
9930 status = bindprocessor(BINDPROCESS, (int)getpid(),
9931 PROCESSOR_CLASS_ANY);
9932 if (status != OK) {
9933 ap_log_error(APLOG_MARK, APLOG_ERR, errno, wsgi_server,
9934 "mod_wsgi (pid=%d): Failed to unbind processor.",
9935 getpid());
9936 }
9937 #endif
9938
9939 /* Setup daemon process name displayed by 'ps'. */
9940
9941 wsgi_setup_daemon_name(daemon, p);
9942
9943 /* Adjust CPU priority if overridden. */
9944
9945 if (daemon->group->cpu_priority != 0) {
9946 if (setpriority(PRIO_PROCESS, 0,
9947 daemon->group->cpu_priority) == -1) {
9948 ap_log_error(APLOG_MARK, APLOG_ERR, errno, wsgi_server,
9949 "mod_wsgi (pid=%d): Couldn't set CPU priority "
9950 "in daemon process '%d'.", getpid(),
9951 daemon->group->cpu_priority);
9952 }
9953 }
9954
9955 /* Setup daemon process user/group/umask etc. */
9956
9957 if (wsgi_setup_access(daemon) == -1) {
9958 /*
9959 * If we get any failure from setting up the appropriate
9960 * permissions or working directory for the daemon process
9961 * then we exit the process. Don't die immediately to avoid
9962 * a fork bomb.
9963 */
9964
9965 ap_log_error(APLOG_MARK, APLOG_ALERT, 0, wsgi_server,
9966 "mod_wsgi (pid=%d): Failure to configure the "
9967 "daemon process correctly and process left in "
9968 "unspecified state. Restarting daemon process "
9969 "after delay.", getpid());
9970
9971 sleep(20);
9972
9973 wsgi_exit_daemon_process(-1);
9974 }
9975
9976 /* Reinitialise accept mutex in daemon process. */
9977
9978 if (daemon->group->mutex) {
9979 status = apr_proc_mutex_child_init(&daemon->group->mutex,
9980 daemon->group->mutex_path, p);
9981
9982 if (status != APR_SUCCESS) {
9983 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, wsgi_server,
9984 "mod_wsgi (pid=%d): Couldn't intialise accept "
9985 "mutex in daemon process '%s'.",
9986 getpid(), daemon->group->mutex_path);
9987
9988 /* Don't die immediately to avoid a fork bomb. */
9989
9990 sleep(20);
9991
9992 wsgi_exit_daemon_process(-1);
9993 }
9994 }
9995
9996 /*
9997 * Create a lookup table of listener socket address
9998 * details so can use it later in daemon when trying
9999 * to map request to correct virtual host server.
10000 */
10001
10002 wsgi_daemon_listeners = apr_hash_make(p);
10003
10004 for (lr = ap_listeners; lr; lr = lr->next) {
10005 char *key;
10006 char *host;
10007 apr_port_t port;
10008
10009 host = lr->bind_addr->hostname;
10010 port = lr->bind_addr->port;
10011
10012 if (!host)
10013 host = "";
10014
10015 key = apr_psprintf(p, "%s|%d", host, port);
10016
10017 apr_hash_set(wsgi_daemon_listeners, key, APR_HASH_KEY_STRING,
10018 lr->bind_addr);
10019 }
10020
10021 /*
10022 * Close child copy of the listening sockets for the
10023 * Apache parent process so we don't interfere with
10024 * the parent process.
10025 */
10026
10027 ap_close_listeners();
10028
10029 /*
10030 * Cleanup the Apache scoreboard to ensure that any
10031 * shared memory segments or memory mapped files not
10032 * available to code in daemon processes.
10033 */
10034
10035 /*
10036 * XXX If this is closed, under Apache 2.4 then daemon
10037 * mode processes will crash. Not much choice but to
10038 * leave it open. Daemon mode really needs to be
10039 * rewritten not to use normal Apache request object and
10040 * output bucket chain to avoid potential for problems.
10041 */
10042
10043 #if 0
10044 ap_cleanup_scoreboard(0);
10045 #endif
10046
10047 /*
10048 * Wipe out random value used in magic token so that not
10049 * possible for user code running in daemon process to
10050 * discover this value for other daemon process groups.
10051 * In other words, wipe out all but our own.
10052 */
10053
10054 entries = (WSGIProcessGroup *)wsgi_daemon_list->elts;
10055
10056 for (i = 0; i < wsgi_daemon_list->nelts; ++i) {
10057 entry = &entries[i];
10058
10059 if (entry != daemon->group)
10060 entry->random = 0;
10061 }
10062
10063 /*
10064 * Close listener socket for daemon processes for other
10065 * daemon process groups. In other words, close all but
10066 * our own.
10067 */
10068
10069 entries = (WSGIProcessGroup *)wsgi_daemon_list->elts;
10070
10071 for (i = 0; i < wsgi_daemon_list->nelts; ++i) {
10072 entry = &entries[i];
10073
10074 if (entry != daemon->group && entry->listener_fd != -1) {
10075 close(entry->listener_fd);
10076 entry->listener_fd = -1;
10077 }
10078 }
10079
10080 /*
10081 * Register signal handler to receive shutdown signal
10082 * from Apache parent process. We need to first create
10083 * pipe by which signal handler can notify the main
10084 * thread that signal has arrived indicating that
10085 * process needs to shutdown.
10086 */
10087
10088 status = apr_file_pipe_create(&wsgi_signal_pipe_in,
10089 &wsgi_signal_pipe_out, p);
10090
10091 if (status != APR_SUCCESS) {
10092 ap_log_error(APLOG_MARK, APLOG_EMERG, status, wsgi_server,
10093 "mod_wsgi (pid=%d): Couldn't initialise signal "
10094 "pipe in daemon process '%s'.", getpid(),
10095 daemon->group->name);
10096
10097 /* Don't die immediately to avoid a fork bomb. */
10098
10099 sleep(20);
10100
10101 wsgi_exit_daemon_process(-1);
10102 }
10103
10104 wsgi_daemon_shutdown = 0;
10105
10106 wsgi_daemon_pid = getpid();
10107
10108 apr_signal(SIGINT, wsgi_signal_handler);
10109 apr_signal(SIGTERM, wsgi_signal_handler);
10110
10111 apr_signal(AP_SIG_GRACEFUL, wsgi_signal_handler);
10112
10113 #ifdef SIGXCPU
10114 apr_signal(SIGXCPU, wsgi_signal_handler);
10115 #endif
10116
10117 /* Set limits on amount of CPU time that can be used. */
10118
10119 if (daemon->group->cpu_time_limit > 0) {
10120 struct rlimit limit;
10121 int result = -1;
10122
10123 limit.rlim_cur = daemon->group->cpu_time_limit;
10124
10125 limit.rlim_max = daemon->group->cpu_time_limit + 1;
10126 limit.rlim_max += daemon->group->shutdown_timeout;
10127
10128 #if defined(RLIMIT_CPU)
10129 result = setrlimit(RLIMIT_CPU, &limit);
10130 #endif
10131
10132 if (result == -1) {
10133 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, wsgi_server,
10134 "mod_wsgi (pid=%d): Couldn't set CPU time "
10135 "limit of %d seconds for process '%s'.", getpid(),
10136 daemon->group->cpu_time_limit,
10137 daemon->group->name);
10138 }
10139 }
10140
10141 /*
10142 * Set limits on amount of date segment memory that can
10143 * be used. Although this is done, some platforms
10144 * doesn't actually support it.
10145 */
10146
10147 if (daemon->group->memory_limit > 0) {
10148 struct rlimit limit;
10149 int result = -1;
10150
10151 limit.rlim_cur = daemon->group->memory_limit;
10152
10153 limit.rlim_max = daemon->group->memory_limit;
10154
10155 #if defined(RLIMIT_DATA)
10156 result = setrlimit(RLIMIT_DATA, &limit);
10157 #endif
10158
10159 if (result == -1) {
10160 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, wsgi_server,
10161 "mod_wsgi (pid=%d): Couldn't set memory "
10162 "limit of %ld for process '%s'.", getpid(),
10163 (long)daemon->group->memory_limit,
10164 daemon->group->name);
10165 }
10166 }
10167
10168 /*
10169 * Set limits on amount of virtual memory that can be used.
10170 * Although this is done, some platforms doesn't actually
10171 * support it.
10172 */
10173
10174 if (daemon->group->virtual_memory_limit > 0) {
10175 struct rlimit limit;
10176 int result = -1;
10177
10178 limit.rlim_cur = daemon->group->virtual_memory_limit;
10179
10180 limit.rlim_max = daemon->group->virtual_memory_limit;
10181
10182 #if defined(RLIMIT_AS)
10183 result = setrlimit(RLIMIT_AS, &limit);
10184 #elif defined(RLIMIT_VMEM)
10185 result = setrlimit(RLIMIT_VMEM, &limit);
10186 #endif
10187
10188 if (result == -1) {
10189 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, wsgi_server,
10190 "mod_wsgi (pid=%d): Couldn't set virtual memory "
10191 "limit of %ld for process '%s'.", getpid(),
10192 (long)daemon->group->virtual_memory_limit,
10193 daemon->group->name);
10194 }
10195 }
10196
10197 /*
10198 * Flag whether multiple daemon processes or denoted
10199 * that requests could be spread across multiple daemon
10200 * process groups.
10201 */
10202
10203 wsgi_multiprocess = daemon->group->multiprocess;
10204 wsgi_multithread = daemon->group->threads != 1;
10205
10206 /*
10207 * Create a pool for the child daemon process so
10208 * we can trigger various events off it at shutdown.
10209 */
10210
10211 apr_pool_create(&wsgi_daemon_pool, p);
10212
10213 /*
10214 * Retain a reference to daemon process details. Do
10215 * this here as when doing lazy initialisation of
10216 * the interpreter we want to know if in a daemon
10217 * process so can pick any daemon process specific
10218 * home directory for Python installation.
10219 */
10220
10221 wsgi_daemon_group = daemon->group->name;
10222 wsgi_daemon_process = daemon;
10223
10224 /* Set lang/locale if specified for daemon process. */
10225
10226 if (daemon->group->lang) {
10227 char *envvar;
10228
10229 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
10230 "mod_wsgi (pid=%d): Setting lang to %s for "
10231 "daemon process group %s.", getpid(),
10232 daemon->group->lang, daemon->group->name);
10233
10234 envvar = apr_pstrcat(p, "LANG=", daemon->group->lang, NULL);
10235 putenv(envvar);
10236 }
10237
10238 if (daemon->group->locale) {
10239 char *envvar;
10240 char *result;
10241
10242 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
10243 "mod_wsgi (pid=%d): Setting locale to %s for "
10244 "daemon process group %s.", getpid(),
10245 daemon->group->locale, daemon->group->name);
10246
10247 envvar = apr_pstrcat(p, "LC_ALL=", daemon->group->locale, NULL);
10248 putenv(envvar);
10249
10250 result = setlocale(LC_ALL, daemon->group->locale);
10251
10252 if (!result) {
10253 ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
10254 "mod_wsgi (pid=%d): Unsupported locale setting "
10255 "%s specified for daemon process group %s. "
10256 "Consider using 'C.UTF-8' as fallback setting.",
10257 getpid(), daemon->group->locale,
10258 daemon->group->name);
10259 }
10260 }
10261
10262 /* Create lock for request monitoring. */
10263
10264 apr_thread_mutex_create(&wsgi_monitor_lock,
10265 APR_THREAD_MUTEX_UNNESTED, p);
10266
10267 /*
10268 * Initialise Python if required to be done in the child
10269 * process. Note that it will not be initialised if
10270 * mod_python loaded and it has already been done.
10271 */
10272
10273 if (wsgi_python_after_fork)
10274 wsgi_python_init(p);
10275
10276 #if PY_MAJOR_VERSION < 3
10277 /*
10278 * If mod_python is also being loaded and thus it was
10279 * responsible for initialising Python it can leave in
10280 * place an active thread state. Under normal conditions
10281 * this would be eliminated in Apache child process by
10282 * the time that mod_wsgi got to do its own child
10283 * initialisation but in daemon process we skip the
10284 * mod_python child initialisation so the active thread
10285 * state still exists. Thus need to do a bit of a fiddle
10286 * to ensure there is no active thread state. Don't need
10287 * to worry about this with Python 3.X as mod_python
10288 * only supports Python 2.X.
10289 */
10290
10291 if (!wsgi_python_initialized) {
10292 PyGILState_STATE state;
10293
10294 PyEval_AcquireLock();
10295
10296 state = PyGILState_Ensure();
10297 PyGILState_Release(state);
10298
10299 if (state == PyGILState_LOCKED)
10300 PyThreadState_Swap(NULL);
10301
10302 PyEval_ReleaseLock();
10303 }
10304 #endif
10305
10306 /*
10307 * If the daemon is associated with a virtual host then
10308 * we can close all other error logs so long as they
10309 * aren't the same one as being used for the virtual
10310 * host. If the virtual host error log is different to
10311 * the main server error log, then also tie stderr to
10312 * that log file instead. This way any debugging sent
10313 * direct to stderr from C code also goes to the virtual
10314 * host error log. We close the error logs that aren't
10315 * required as that eliminates possibility that user
10316 * code executing in daemon process could maliciously
10317 * dump messages into error log for a different virtual
10318 * host, as well as stop them being reopened with mode
10319 * that would allow seeking back to start of file and
10320 * read any information in them.
10321 */
10322
10323 if (daemon->group->server->is_virtual) {
10324 server_rec *server = NULL;
10325 apr_file_t *errfile = NULL;
10326
10327 /*
10328 * Iterate over all servers and close any error
10329 * logs different to that for virtual host. Note that
10330 * if errors are being redirected to syslog, then
10331 * the server error log reference will actually be
10332 * a null pointer, so need to ensure that check for
10333 * that and don't attempt to close it in that case.
10334 */
10335
10336 server = wsgi_server;
10337
10338 while (server != NULL) {
10339 if (server->error_log &&
10340 server->error_log != daemon->group->server->error_log) {
10341 apr_file_close(server->error_log);
10342 }
10343
10344 server = server->next;
10345 }
10346
10347 /*
10348 * Reassociate stderr output with error log from the
10349 * virtual host the daemon is associated with. Close
10350 * the virtual host error log and point it at stderr
10351 * log instead. Do the latter so don't get two
10352 * references to same open file. Just in case
10353 * anything still accesses error log of main server,
10354 * map main server error log to that of the virtual
10355 * host. Note that cant do this if errors are being
10356 * redirected to syslog, as indicated by virtual
10357 * host error log being a null pointer. In that case
10358 * just leave everything as it was. Also can't remap
10359 * the error log for main server if it was being
10360 * redirected to syslog but virtual host wasn't.
10361 */
10362
10363 if (daemon->group->server->error_log &&
10364 daemon->group->server->error_log != wsgi_server->error_log) {
10365
10366 apr_file_t *oldfile = NULL;
10367
10368 apr_file_open_stderr(&errfile, wsgi_server->process->pool);
10369 apr_file_dup2(errfile, daemon->group->server->error_log,
10370 wsgi_server->process->pool);
10371
10372 oldfile = daemon->group->server->error_log;
10373
10374 server = wsgi_server;
10375
10376 while (server != NULL) {
10377 if (server->error_log == oldfile)
10378 server->error_log = errfile;
10379 server = server->next;
10380 }
10381
10382 apr_file_close(oldfile);
10383
10384 if (wsgi_server->error_log)
10385 wsgi_server->error_log = errfile;
10386 }
10387 }
10388
10389 /*
10390 * Update reference to server object in case daemon
10391 * process is actually associated with a virtual host.
10392 * This way all logging actually goes into the virtual
10393 * hosts log file.
10394 */
10395
10396 if (daemon->group->server) {
10397 if (wsgi_server_config->verbose_debugging) {
10398 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
10399 "mod_wsgi (pid=%d): Process '%s' logging to "
10400 "'%s'.", getpid(), daemon->group->name,
10401 daemon->group->server->server_hostname);
10402 }
10403
10404 wsgi_server = daemon->group->server;
10405 }
10406 else {
10407 if (wsgi_server_config->verbose_debugging) {
10408 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
10409 "mod_wsgi (pid=%d): Process '%s' forced to log "
10410 "to '%s'.", getpid(), daemon->group->name,
10411 wsgi_server->server_hostname);
10412 }
10413 }
10414
10415 /* Time daemon process started waiting for requests. */
10416
10417 wsgi_restart_time = apr_time_now();
10418
10419 /*
10420 * Setup Python in the child daemon process. Note that
10421 * we ensure that we are now marked as the original
10422 * initialiser of the Python interpreter even though
10423 * mod_python might have done it, as we will be the one
10424 * to cleanup the child daemon process and not
10425 * mod_python. We also need to perform the special
10426 * Python setup which has to be done after a fork.
10427 */
10428
10429 wsgi_python_initialized = 1;
10430
10431 wsgi_python_path = daemon->group->python_path;
10432 wsgi_python_eggs = daemon->group->python_eggs;
10433
10434 wsgi_newrelic_config_file = daemon->group->newrelic_config_file;
10435 wsgi_newrelic_environment = daemon->group->newrelic_environment;
10436
10437 wsgi_python_child_init(wsgi_daemon_pool);
10438
10439 /*
10440 * Create socket wrapper for listener file descriptor
10441 * and mutex for controlling which thread gets to
10442 * perform the accept() when a connection is ready.
10443 */
10444
10445 apr_os_sock_put(&daemon->listener, &daemon->group->listener_fd, p);
10446
10447 /*
10448 * Run the main routine for the daemon process if there
10449 * is a non zero number of threads. When number of threads
10450 * is zero we actually go on and shutdown immediately.
10451 */
10452
10453 if (daemon->group->threads != 0)
10454 wsgi_daemon_main(p, daemon);
10455
10456 /*
10457 * Destroy the pool for the daemon process. This will
10458 * have the side affect of also destroying Python.
10459 */
10460
10461 ap_log_error(APLOG_MARK, APLOG_INFO, 0, wsgi_server,
10462 "mod_wsgi (pid=%d): Stopping process '%s'.", getpid(),
10463 daemon->group->name);
10464
10465 apr_pool_destroy(wsgi_daemon_pool);
10466
10467 /* Exit the daemon process when being shutdown. */
10468
10469 wsgi_exit_daemon_process(0);
10470 }
10471
10472 #ifdef HAVE_FORK
10473 if (wsgi_python_initialized) {
10474 #if PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 7)
10475 PyOS_AfterFork_Parent();
10476 #endif
10477 }
10478 #endif
10479
10480 apr_pool_note_subprocess(p, &daemon->process, APR_KILL_AFTER_TIMEOUT);
10481 apr_proc_other_child_register(&daemon->process, wsgi_manage_process,
10482 daemon, NULL, p);
10483
10484 return OK;
10485 }
10486
wsgi_start_daemons(apr_pool_t * p)10487 static int wsgi_start_daemons(apr_pool_t *p)
10488 {
10489 WSGIProcessGroup *entries = NULL;
10490 WSGIProcessGroup *entry = NULL;
10491 WSGIDaemonProcess *process = NULL;
10492
10493 int mpm_generation = 0;
10494
10495 int i, j;
10496
10497 /* Do we need to create any daemon processes. */
10498
10499 if (!wsgi_daemon_list)
10500 return OK;
10501
10502 /* What server generation is this. */
10503
10504 #if defined(AP_MPMQ_GENERATION)
10505 ap_mpm_query(AP_MPMQ_GENERATION, &mpm_generation);
10506 #else
10507 mpm_generation = ap_my_generation;
10508 #endif
10509
10510 /*
10511 * Cache references to root server and pool as will need
10512 * to access these when restarting daemon process when
10513 * they die.
10514 */
10515
10516 wsgi_parent_pool = p;
10517
10518 /*
10519 * Startup in turn the required number of daemon processes
10520 * for each of the named process groups.
10521 */
10522
10523 wsgi_daemon_index = apr_hash_make(p);
10524
10525 entries = (WSGIProcessGroup *)wsgi_daemon_list->elts;
10526
10527 for (i = 0; i < wsgi_daemon_list->nelts; ++i) {
10528 int status;
10529
10530 entry = &entries[i];
10531
10532 /*
10533 * Check for whether the daemon process user and
10534 * group are the default Apache values. If they are
10535 * then reset them to the current values configured for
10536 * Apache. This is to work around where the User/Group
10537 * directives had not been set before the WSGIDaemonProcess
10538 * directive was used in configuration file. In this case,
10539 * where no 'user' and 'group' options were provided,
10540 * the default values would have been used, but these
10541 * were later overridden thus why we need to update it.
10542 */
10543
10544 if (entry->uid == ap_uname2id(DEFAULT_USER)) {
10545 entry->uid = ap_unixd_config.user_id;
10546 entry->user = ap_unixd_config.user_name;
10547
10548 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
10549 "mod_wsgi (pid=%d): Reset default user for "
10550 "daemon process group '%s' to uid=%ld.",
10551 getpid(), entry->name, (long)entry->uid);
10552 }
10553
10554 if (entry->gid == ap_gname2id(DEFAULT_GROUP)) {
10555 entry->gid = ap_unixd_config.group_id;
10556
10557 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
10558 "mod_wsgi (pid=%d): Reset default group for "
10559 "daemon process group '%s' to gid=%ld.",
10560 getpid(), entry->name, (long)entry->gid);
10561 }
10562
10563 /*
10564 * Calculate path for socket to accept requests on and
10565 * create the socket.
10566 */
10567
10568 entry->socket_rotation = wsgi_server_config->socket_rotation;
10569
10570 if (entry->socket_rotation) {
10571 entry->socket_path = apr_psprintf(p, "%s.%d.%d.%d.sock",
10572 wsgi_server_config->socket_prefix,
10573 getpid(), mpm_generation, entry->id);
10574 }
10575 else {
10576 entry->socket_path = apr_psprintf(p, "%s.%d.u%d.%d.sock",
10577 wsgi_server_config->socket_prefix,
10578 getpid(), entry->uid, entry->id);
10579 }
10580
10581 apr_hash_set(wsgi_daemon_index, entry->name, APR_HASH_KEY_STRING,
10582 entry);
10583
10584 entry->listener_fd = wsgi_setup_socket(entry);
10585
10586 if (entry->listener_fd == -1)
10587 return DECLINED;
10588
10589 /*
10590 * Register cleanup so that listener socket is cleaned
10591 * up properly on a restart and on shutdown.
10592 */
10593
10594 apr_pool_cleanup_register(p, entry, wsgi_cleanup_process,
10595 apr_pool_cleanup_null);
10596
10597 /*
10598 * If there is more than one daemon process in the group
10599 * then need to create an accept mutex for the daemon
10600 * processes to use so they don't interfere with each
10601 * other.
10602 */
10603
10604 if (entry->processes > 1) {
10605 entry->mutex_path = apr_psprintf(p, "%s.%d.%d.%d.lock",
10606 wsgi_server_config->socket_prefix,
10607 getpid(), mpm_generation,
10608 entry->id);
10609
10610 status = apr_proc_mutex_create(&entry->mutex, entry->mutex_path,
10611 wsgi_server_config->lock_mechanism,
10612 p);
10613
10614 if (status != APR_SUCCESS) {
10615 ap_log_error(APLOG_MARK, APLOG_CRIT, errno, wsgi_server,
10616 "mod_wsgi (pid=%d): Couldn't create accept "
10617 "lock '%s' (%d).", getpid(), entry->mutex_path,
10618 wsgi_server_config->lock_mechanism);
10619 return DECLINED;
10620 }
10621
10622 /*
10623 * Depending on the locking mechanism being used
10624 * need to change the permissions of the lock. Can't
10625 * use unixd_set_proc_mutex_perms() as it uses the
10626 * default Apache child process uid/gid where the
10627 * daemon process uid/gid can be different.
10628 */
10629
10630 if (!geteuid()) {
10631 #if APR_HAS_SYSVSEM_SERIALIZE
10632 if (!strcmp(apr_proc_mutex_name(entry->mutex), "sysvsem")) {
10633 apr_os_proc_mutex_t ospmutex;
10634 #if !APR_HAVE_UNION_SEMUN
10635 union semun {
10636 long val;
10637 struct semid_ds *buf;
10638 unsigned short *array;
10639 };
10640 #endif
10641 union semun ick;
10642 struct semid_ds buf;
10643
10644 apr_os_proc_mutex_get(&ospmutex, entry->mutex);
10645 buf.sem_perm.uid = entry->uid;
10646 buf.sem_perm.gid = entry->gid;
10647 buf.sem_perm.mode = 0600;
10648 ick.buf = &buf;
10649 if (semctl(ospmutex.crossproc, 0, IPC_SET, ick) < 0) {
10650 ap_log_error(APLOG_MARK, APLOG_CRIT, errno,
10651 wsgi_server, "mod_wsgi (pid=%d): "
10652 "Couldn't set permissions on accept "
10653 "mutex '%s' (sysvsem).", getpid(),
10654 entry->mutex_path);
10655 return DECLINED;
10656 }
10657 }
10658 #endif
10659 #if APR_HAS_FLOCK_SERIALIZE
10660 if (!strcmp(apr_proc_mutex_name(entry->mutex), "flock")) {
10661 if (chown(entry->mutex_path, entry->uid, -1) < 0) {
10662 ap_log_error(APLOG_MARK, APLOG_CRIT, errno,
10663 wsgi_server, "mod_wsgi (pid=%d): "
10664 "Couldn't set permissions on accept "
10665 "mutex '%s' (flock).", getpid(),
10666 entry->mutex_path);
10667 return DECLINED;
10668 }
10669 }
10670 #endif
10671 }
10672 }
10673
10674 /* Create the actual required daemon processes. */
10675
10676 for (j = 1; j <= entry->processes; j++) {
10677 process = (WSGIDaemonProcess *)apr_pcalloc(p, sizeof(
10678 WSGIDaemonProcess));
10679
10680 process->group = entry;
10681 process->instance = j;
10682
10683 status = wsgi_start_process(p, process);
10684
10685 if (status != OK)
10686 return status;
10687 }
10688 }
10689
10690 return OK;
10691 }
10692
10693 static apr_pool_t *wsgi_pconf_pool = NULL;
10694
wsgi_deferred_start_daemons(apr_pool_t * p,ap_scoreboard_e sb_type)10695 static int wsgi_deferred_start_daemons(apr_pool_t *p, ap_scoreboard_e sb_type)
10696 {
10697 return wsgi_start_daemons(wsgi_pconf_pool);
10698 }
10699
wsgi_socket_connect_un(apr_socket_t * sock,struct sockaddr_un * sa)10700 static apr_status_t wsgi_socket_connect_un(apr_socket_t *sock,
10701 struct sockaddr_un *sa)
10702 {
10703 apr_status_t rv;
10704 apr_os_sock_t rawsock;
10705 apr_interval_time_t t;
10706
10707 rv = apr_os_sock_get(&rawsock, sock);
10708 if (rv != APR_SUCCESS) {
10709 return rv;
10710 }
10711
10712 rv = apr_socket_timeout_get(sock, &t);
10713 if (rv != APR_SUCCESS) {
10714 return rv;
10715 }
10716
10717 do {
10718 rv = connect(rawsock, (struct sockaddr*)sa,
10719 APR_OFFSETOF(struct sockaddr_un, sun_path)
10720 + strlen(sa->sun_path) + 1);
10721 } while (rv == -1 && errno == EINTR);
10722
10723 if ((rv == -1) && (errno == EINPROGRESS || errno == EALREADY)
10724 && (t > 0)) {
10725 #if APR_MAJOR_VERSION < 2
10726 rv = apr_wait_for_io_or_timeout(NULL, sock, 0);
10727 #else
10728 rv = apr_socket_wait(sock, APR_WAIT_WRITE);
10729 #endif
10730
10731 if (rv != APR_SUCCESS) {
10732 return rv;
10733 }
10734 }
10735
10736 if (rv == -1 && errno != EISCONN) {
10737 return errno;
10738 }
10739
10740 return APR_SUCCESS;
10741 }
10742
wsgi_connect_daemon(request_rec * r,WSGIDaemonSocket * daemon)10743 static int wsgi_connect_daemon(request_rec *r, WSGIDaemonSocket *daemon)
10744 {
10745 WSGIRequestConfig *config = NULL;
10746
10747 apr_status_t rv;
10748
10749 struct sockaddr_un addr;
10750
10751 int retries = 0;
10752 apr_interval_time_t timer = 0;
10753 apr_interval_time_t total_time = 0;
10754
10755 apr_time_t start_time = 0;
10756
10757 /* Grab request configuration. */
10758
10759 config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
10760 &wsgi_module);
10761
10762 memset(&addr, 0, sizeof(addr));
10763 addr.sun_family = AF_UNIX;
10764 apr_cpystrn(addr.sun_path, daemon->socket_path, sizeof(addr.sun_path));
10765
10766 start_time = apr_time_now();
10767
10768 while (1) {
10769 retries++;
10770
10771 config->daemon_connects++;
10772
10773 rv = apr_socket_create(&daemon->socket, AF_UNIX, SOCK_STREAM,
10774 0, r->pool);
10775
10776 if (rv != APR_SUCCESS) {
10777 ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r,
10778 "mod_wsgi (pid=%d): Unable to create socket to "
10779 "connect to WSGI daemon process.", getpid());
10780
10781 return HTTP_INTERNAL_SERVER_ERROR;
10782 }
10783
10784 /*
10785 * Apply timeout before issuing the socket connection in
10786 * case this hangs for some reason. Would have to be an extreme
10787 * event for a UNIX socket connect to hang, but have had some
10788 * unexplained situations which look exactly like that.
10789 */
10790
10791 if (daemon->socket_timeout)
10792 apr_socket_timeout_set(daemon->socket, daemon->socket_timeout);
10793 else
10794 apr_socket_timeout_set(daemon->socket, r->server->timeout);
10795
10796 rv = wsgi_socket_connect_un(daemon->socket, &addr);
10797
10798 if (rv != APR_SUCCESS) {
10799 /*
10800 * We need to check for both connection refused and
10801 * connection unavailable as Linux systems when
10802 * connecting to a UNIX listener socket in non
10803 * blocking mode, where the listener backlog is full
10804 * will return the error EAGAIN rather than returning
10805 * ECONNREFUSED as is supposedly dictated by POSIX.
10806 */
10807
10808 if (APR_STATUS_IS_ECONNREFUSED(rv) || APR_STATUS_IS_EAGAIN(rv)) {
10809 if ((apr_time_now()-start_time) < daemon->connect_timeout) {
10810 if (wsgi_server_config->verbose_debugging) {
10811 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r,
10812 "mod_wsgi (pid=%d): Connection attempt "
10813 "#%d to WSGI daemon process '%s' on "
10814 "'%s' failed, sleeping before retrying "
10815 "again.", getpid(), retries,
10816 daemon->name, daemon->socket_path);
10817 }
10818
10819 apr_socket_close(daemon->socket);
10820
10821 /*
10822 * Progressively increase time we wait between
10823 * connection attempts. Start at 0.125 second, but
10824 * back off to 1 second interval after 2 seconds.
10825 */
10826
10827 if (total_time < apr_time_make(2, 0))
10828 timer = apr_time_make(0, 125000);
10829 else
10830 timer = apr_time_make(1, 0);
10831
10832 apr_sleep(timer);
10833
10834 total_time += timer;
10835 }
10836 else {
10837 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
10838 "mod_wsgi (pid=%d): Unable to connect to "
10839 "WSGI daemon process '%s' on '%s' after "
10840 "multiple attempts as listener backlog "
10841 "limit was exceeded or the socket does "
10842 "not exist.", getpid(), daemon->name,
10843 daemon->socket_path);
10844
10845 apr_socket_close(daemon->socket);
10846
10847 return HTTP_SERVICE_UNAVAILABLE;
10848 }
10849 }
10850 else {
10851 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
10852 "mod_wsgi (pid=%d): Unable to connect to "
10853 "WSGI daemon process '%s' on '%s' as user "
10854 "with uid=%ld.", getpid(), daemon->name,
10855 daemon->socket_path, (long)geteuid());
10856
10857 apr_socket_close(daemon->socket);
10858
10859 return HTTP_SERVICE_UNAVAILABLE;
10860 }
10861 }
10862 else
10863 break;
10864 }
10865
10866 return OK;
10867 }
10868
wsgi_socket_send(apr_socket_t * sock,const char * buf,size_t buf_size)10869 static apr_status_t wsgi_socket_send(apr_socket_t *sock, const char *buf,
10870 size_t buf_size)
10871 {
10872 apr_status_t rv;
10873 apr_size_t len;
10874
10875 while (buf_size > 0)
10876 {
10877 len = buf_size;
10878
10879 rv = apr_socket_send(sock, buf, &len);
10880
10881 if (rv != APR_SUCCESS)
10882 return rv;
10883
10884 buf += len;
10885 buf_size -= len;
10886 }
10887
10888 return APR_SUCCESS;
10889 }
10890
wsgi_socket_sendv_limit(apr_socket_t * sock,struct iovec * vec,size_t nvec)10891 static apr_status_t wsgi_socket_sendv_limit(apr_socket_t *sock,
10892 struct iovec *vec, size_t nvec)
10893 {
10894 apr_status_t rv;
10895 apr_size_t written = 0;
10896 apr_size_t to_write = 0;
10897 size_t i, offset;
10898
10899 /* Calculate how much has to be sent. */
10900
10901 for (i = 0; i < nvec; i++) {
10902 to_write += vec[i].iov_len;
10903 }
10904
10905 /* Loop until all data has been sent. */
10906
10907 offset = 0;
10908
10909 while (to_write) {
10910 apr_size_t n = 0;
10911
10912 rv = apr_socket_sendv(sock, vec+offset, nvec-offset, &n);
10913
10914 if (rv != APR_SUCCESS)
10915 return rv;
10916
10917 if (n > 0) {
10918 /* Bail out of all data has been sent. */
10919
10920 written += n;
10921
10922 if (written >= to_write)
10923 break;
10924
10925 /*
10926 * Not all data was sent, so ween need to try
10927 * again with the remainder of the data. We
10928 * first need to work out where to start from.
10929 */
10930
10931 for (i = offset; i < nvec; ) {
10932 if (n >= vec[i].iov_len) {
10933 offset++;
10934 n -= vec[i++].iov_len;
10935 } else {
10936 vec[i].iov_len -= n;
10937 vec[i].iov_base = (char *) vec[i].iov_base + n;
10938 break;
10939 }
10940 }
10941 }
10942 }
10943
10944 return APR_SUCCESS;
10945 }
10946
wsgi_socket_sendv(apr_socket_t * sock,struct iovec * vec,size_t nvec)10947 static apr_status_t wsgi_socket_sendv(apr_socket_t *sock, struct iovec *vec,
10948 size_t nvec)
10949 {
10950 #if defined(_SC_IOV_MAX)
10951 static size_t iov_max = 0;
10952
10953 if (iov_max == 0)
10954 iov_max = sysconf(_SC_IOV_MAX);
10955 #else
10956 static size_t iov_max = APR_MAX_IOVEC_SIZE;
10957 #endif
10958
10959 if (nvec > iov_max) {
10960 int offset = 0;
10961
10962 while (nvec != 0) {
10963 apr_status_t rv;
10964
10965 rv = wsgi_socket_sendv_limit(sock, &vec[offset],
10966 (nvec < iov_max ? nvec : (int)iov_max));
10967
10968 if (rv != APR_SUCCESS)
10969 return rv;
10970
10971 if (nvec > iov_max) {
10972 nvec -= iov_max;
10973 offset += iov_max;
10974 } else {
10975 nvec = 0;
10976 }
10977 }
10978
10979 return APR_SUCCESS;
10980 }
10981 else
10982 return wsgi_socket_sendv_limit(sock, vec, nvec);
10983 }
10984
wsgi_send_request(request_rec * r,WSGIRequestConfig * config,WSGIDaemonSocket * daemon)10985 static apr_status_t wsgi_send_request(request_rec *r,
10986 WSGIRequestConfig *config,
10987 WSGIDaemonSocket *daemon)
10988 {
10989 int rv;
10990
10991 const apr_array_header_t *env_arr;
10992 const apr_table_entry_t *elts;
10993 int i;
10994
10995 struct iovec *vec;
10996 struct iovec *vec_start;
10997 struct iovec *vec_next;
10998
10999 apr_size_t total = 0;
11000 apr_size_t count = 0;
11001
11002 apr_table_setn(r->subprocess_env, "mod_wsgi.daemon_connects",
11003 apr_psprintf(r->pool, "%d", config->daemon_connects));
11004 apr_table_setn(r->subprocess_env, "mod_wsgi.daemon_restarts",
11005 apr_psprintf(r->pool, "%d", config->daemon_restarts));
11006
11007 /* Send subprocess environment from request object. */
11008
11009 env_arr = apr_table_elts(r->subprocess_env);
11010 elts = (const apr_table_entry_t *)env_arr->elts;
11011
11012 /*
11013 * Sending total amount of data, followed by count of separate
11014 * strings and then each null terminated string. The total is
11015 * inclusive of the bytes used for the count of the strings.
11016 */
11017
11018 vec = (struct iovec *)apr_palloc(r->pool, (2+(2*env_arr->nelts))*
11019 sizeof(struct iovec));
11020
11021 vec_start = &vec[2];
11022 vec_next = vec_start;
11023
11024 for (i=0; i<env_arr->nelts; ++i) {
11025 if (!elts[i].key)
11026 continue;
11027
11028 vec_next->iov_base = (void*)elts[i].key;
11029 vec_next->iov_len = strlen(elts[i].key) + 1;
11030
11031 total += vec_next->iov_len;
11032
11033 vec_next++;
11034
11035 if (elts[i].val) {
11036 vec_next->iov_base = (void*)elts[i].val;
11037 vec_next->iov_len = strlen(elts[i].val) + 1;
11038 }
11039 else
11040 {
11041 vec_next->iov_base = (void*)"";
11042 vec_next->iov_len = 1;
11043 }
11044
11045 total += vec_next->iov_len;
11046
11047 vec_next++;
11048 }
11049
11050 count = vec_next - vec_start;
11051
11052 vec[1].iov_base = (void*)&count;
11053 vec[1].iov_len = sizeof(count);
11054
11055 total += vec[1].iov_len;
11056
11057 vec[0].iov_base = (void*)&total;
11058 vec[0].iov_len = sizeof(total);
11059
11060 rv = wsgi_socket_sendv(daemon->socket, vec, (int)(vec_next-vec));
11061
11062 if (rv != APR_SUCCESS)
11063 return rv;
11064
11065 return APR_SUCCESS;
11066 }
11067
wsgi_discard_output(apr_bucket_brigade * bb)11068 static void wsgi_discard_output(apr_bucket_brigade *bb)
11069 {
11070 apr_bucket *e;
11071 const char *buf;
11072 apr_size_t len;
11073 apr_status_t rv;
11074
11075 for (e = APR_BRIGADE_FIRST(bb);
11076 e != APR_BRIGADE_SENTINEL(bb);
11077 e = APR_BUCKET_NEXT(e))
11078 {
11079 if (APR_BUCKET_IS_EOS(e)) {
11080 break;
11081 }
11082 rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ);
11083 if (rv != APR_SUCCESS) {
11084 break;
11085 }
11086 }
11087 }
11088
wsgi_copy_header(void * v,const char * key,const char * val)11089 static int wsgi_copy_header(void *v, const char *key, const char *val)
11090 {
11091 apr_table_addn(v, key, val);
11092 return 1;
11093 }
11094
11095 #define HTTP_UNSET (-HTTP_OK)
11096
wsgi_scan_headers(request_rec * r,char * buffer,int buflen,int (* getsfunc)(char *,int,void *),void * getsfunc_data)11097 static int wsgi_scan_headers(request_rec *r, char *buffer, int buflen,
11098 int (*getsfunc) (char *, int, void *),
11099 void *getsfunc_data)
11100 {
11101 char x[32768];
11102 char *w, *l;
11103 size_t p;
11104
11105 int cgi_status = HTTP_UNSET;
11106
11107 apr_table_t *merge;
11108 apr_table_t *cookie_table;
11109 apr_table_t *authen_table;
11110
11111 WSGIRequestConfig *config = NULL;
11112
11113 config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
11114 &wsgi_module);
11115
11116 /*
11117 * Default to internal fixed size buffer for reading headers if one
11118 * is not supplied explicitly with the call.
11119 */
11120
11121 if (buffer)
11122 *buffer = '\0';
11123
11124 w = buffer ? buffer : x;
11125 buflen = buffer ? buflen : sizeof(x);
11126
11127 /* Temporary place to hold headers as we read them. */
11128
11129 merge = apr_table_make(r->pool, 10);
11130
11131 /*
11132 * The HTTP specification says that it is legal to merge duplicate
11133 * headers into one. Some browsers don't like certain headers being
11134 * merged however. These headers are Set-Cookie and WWW-Authenticate.
11135 * We will therefore keep these separate and merge them back in
11136 * independently at the end. Before we start though, we need to make
11137 * sure we save away any instances of these headers which may already
11138 * be listed in the request structure for some reason.
11139 */
11140
11141 cookie_table = apr_table_make(r->pool, 2);
11142 apr_table_do(wsgi_copy_header, cookie_table, r->headers_out,
11143 "Set-Cookie", NULL);
11144
11145 authen_table = apr_table_make(r->pool, 2);
11146 apr_table_do(wsgi_copy_header, authen_table, r->err_headers_out,
11147 "WWW-Authenticate", NULL);
11148
11149 while (1) {
11150 int rv = (*getsfunc) (w, buflen - 1, getsfunc_data);
11151
11152 if (rv == 0) {
11153 wsgi_log_script_error(r, apr_psprintf(r->pool, "Truncated or "
11154 "oversized response headers received from "
11155 "daemon process '%s'",
11156 config->process_group), r->filename);
11157
11158 r->status_line = NULL;
11159
11160 return HTTP_INTERNAL_SERVER_ERROR;
11161 }
11162 else if (rv == -1) {
11163 wsgi_log_script_error(r, apr_psprintf(r->pool, "Timeout when "
11164 "reading response headers from daemon "
11165 "process '%s'", config->process_group),
11166 r->filename);
11167
11168 r->status_line = NULL;
11169
11170 return HTTP_GATEWAY_TIME_OUT;
11171 }
11172
11173 /*
11174 * Delete any trailing (CR?)LF. Indeed, the host's '\n':
11175 * '\012' for UNIX; '\015' for MacOS; '\025' for OS/390.
11176 */
11177
11178 p = strlen(w);
11179
11180 if (p > 0 && w[p - 1] == '\n') {
11181 if (p > 1 && w[p - 2] == CR) {
11182 w[p - 2] = '\0';
11183 }
11184 else {
11185 w[p - 1] = '\0';
11186 }
11187 }
11188
11189 /*
11190 * If we've finished reading the headers, check to make sure
11191 * any HTTP/1.1 conditions are met. If so, we're done; normal
11192 * processing will handle the script's output. If not, just
11193 * return the error.
11194 */
11195
11196 if (w[0] == '\0') {
11197 int cond_status = OK;
11198
11199 /*
11200 * This fails because it gets confused when a CGI Status
11201 * header overrides ap_meets_conditions.
11202 *
11203 * We can fix that by dropping ap_meets_conditions when
11204 * Status has been set. Since this is the only place
11205 * cgi_status gets used, let's test it explicitly.
11206 *
11207 * The alternative would be to ignore CGI Status when
11208 * ap_meets_conditions returns anything interesting. That
11209 * would be safer wrt HTTP, but would break CGI.
11210 */
11211
11212 if ((cgi_status == HTTP_UNSET) && (r->method_number == M_GET)) {
11213 cond_status = ap_meets_conditions(r);
11214 }
11215
11216 /*
11217 * Merge the headers received back into the request
11218 * structure. There should only be one per header with
11219 * values combined for these.
11220 */
11221
11222 apr_table_overlap(r->headers_out, merge,
11223 APR_OVERLAP_TABLES_MERGE);
11224
11225 /*
11226 * Now add in the special headers which we can't merge
11227 * because it gives certain browsers problems.
11228 */
11229
11230 if (!apr_is_empty_table(cookie_table)) {
11231 apr_table_unset(r->headers_out, "Set-Cookie");
11232 r->headers_out = apr_table_overlay(r->pool,
11233 r->headers_out, cookie_table);
11234 }
11235
11236 if (!apr_is_empty_table(authen_table)) {
11237 apr_table_unset(r->err_headers_out, "WWW-Authenticate");
11238 r->err_headers_out = apr_table_overlay(r->pool,
11239 r->err_headers_out, authen_table);
11240 }
11241
11242 return cond_status;
11243 }
11244
11245 /* If we see a bogus header don't ignore it. Shout and scream. */
11246
11247 if (!(l = strchr(w, ':'))) {
11248 char malformed[32];
11249
11250 strncpy(malformed, w, sizeof(malformed)-1);
11251 malformed[sizeof(malformed)-1] = '\0';
11252
11253 if (!buffer) {
11254 /* Soak up all the script output. */
11255
11256 while ((*getsfunc)(w, buflen - 1, getsfunc_data) > 0) {
11257 continue;
11258 }
11259 }
11260
11261 wsgi_log_script_error(r, apr_psprintf(r->pool, "Malformed "
11262 "header '%s' found when reading script "
11263 "headers from daemon process '%s'",
11264 malformed, config->process_group),
11265 r->filename);
11266
11267 r->status_line = NULL;
11268
11269 return HTTP_INTERNAL_SERVER_ERROR;
11270 }
11271
11272 /* Strip leading white space from header value. */
11273
11274 *l++ = '\0';
11275 while (*l && apr_isspace(*l)) {
11276 ++l;
11277 }
11278
11279 if (!strcasecmp(w, "Content-type")) {
11280 char *tmp;
11281
11282 /* Nuke trailing whitespace. */
11283
11284 char *endp = l + strlen(l) - 1;
11285 while (endp > l && apr_isspace(*endp)) {
11286 *endp-- = '\0';
11287 }
11288
11289 tmp = apr_pstrdup(r->pool, l);
11290 ap_content_type_tolower(tmp);
11291 ap_set_content_type(r, tmp);
11292 }
11293 else if (!strcasecmp(w, "Status")) {
11294 /*
11295 * If the script returned a specific status, that's what
11296 * we'll use, otherwise we assume 200 OK.
11297 */
11298
11299 r->status = cgi_status = atoi(l);
11300 r->status_line = apr_pstrdup(r->pool, l);
11301 }
11302 else if (!strcasecmp(w, "Location")) {
11303 apr_table_set(r->headers_out, w, l);
11304 }
11305 else if (!strcasecmp(w, "Content-Length")) {
11306 apr_table_set(r->headers_out, w, l);
11307 }
11308 else if (!strcasecmp(w, "Content-Range")) {
11309 apr_table_set(r->headers_out, w, l);
11310 }
11311 else if (!strcasecmp(w, "Transfer-Encoding")) {
11312 apr_table_set(r->headers_out, w, l);
11313 }
11314 else if (!strcasecmp(w, "Last-Modified")) {
11315 /*
11316 * If the script gave us a Last-Modified header, we can't just
11317 * pass it on blindly because of restrictions on future values.
11318 */
11319
11320 ap_update_mtime(r, apr_date_parse_http(l));
11321 ap_set_last_modified(r);
11322 }
11323 else if (!strcasecmp(w, "Set-Cookie")) {
11324 apr_table_add(cookie_table, w, l);
11325 }
11326 else if (!strcasecmp(w, "WWW-Authenticate")) {
11327 apr_table_add(authen_table, w, l);
11328 }
11329 else {
11330 apr_table_add(merge, w, l);
11331 }
11332 }
11333
11334 return OK;
11335 }
11336
wsgi_getsfunc_brigade(char * buf,int len,void * arg)11337 static int wsgi_getsfunc_brigade(char *buf, int len, void *arg)
11338 {
11339 apr_bucket_brigade *bb = (apr_bucket_brigade *)arg;
11340 const char *dst_end = buf + len - 1;
11341 char *dst = buf;
11342 apr_bucket *e = APR_BRIGADE_FIRST(bb);
11343 apr_status_t rv;
11344 int done = 0;
11345
11346 while ((dst < dst_end) && !done && e != APR_BRIGADE_SENTINEL(bb)
11347 && !APR_BUCKET_IS_EOS(e)) {
11348 const char *bucket_data;
11349 apr_size_t bucket_data_len;
11350 const char *src;
11351 const char *src_end;
11352 apr_bucket * next;
11353
11354 rv = apr_bucket_read(e, &bucket_data, &bucket_data_len,
11355 APR_BLOCK_READ);
11356 if (rv != APR_SUCCESS || (bucket_data_len == 0)) {
11357 *dst = '\0';
11358 return APR_STATUS_IS_TIMEUP(rv) ? -1 : 0;
11359 }
11360 src = bucket_data;
11361 src_end = bucket_data + bucket_data_len;
11362 while ((src < src_end) && (dst < dst_end) && !done) {
11363 if (*src == '\n') {
11364 done = 1;
11365 }
11366 else if (*src != '\r') {
11367 *dst++ = *src;
11368 }
11369 src++;
11370 }
11371
11372 if (src < src_end) {
11373 apr_bucket_split(e, src - bucket_data);
11374 }
11375 next = APR_BUCKET_NEXT(e);
11376 APR_BUCKET_REMOVE(e);
11377 apr_bucket_destroy(e);
11378 e = next;
11379 }
11380 *dst = '\0';
11381 return done;
11382 }
11383
wsgi_scan_headers_brigade(request_rec * r,apr_bucket_brigade * bb,char * buffer,int buflen)11384 static int wsgi_scan_headers_brigade(request_rec *r,
11385 apr_bucket_brigade *bb,
11386 char *buffer, int buflen)
11387 {
11388 return wsgi_scan_headers(r, buffer, buflen, wsgi_getsfunc_brigade, bb);
11389 }
11390
wsgi_transfer_response(request_rec * r,apr_bucket_brigade * bb,apr_size_t buffer_size,apr_time_t timeout)11391 static int wsgi_transfer_response(request_rec *r, apr_bucket_brigade *bb,
11392 apr_size_t buffer_size, apr_time_t timeout)
11393 {
11394 apr_bucket *e;
11395 apr_read_type_e mode = APR_NONBLOCK_READ;
11396
11397 apr_bucket_brigade *tmpbb;
11398
11399 const char *data = NULL;
11400 apr_size_t length = 0;
11401
11402 apr_size_t bytes_transfered = 0;
11403
11404 int bucket_count = 0;
11405
11406 apr_status_t rv;
11407
11408 #if AP_MODULE_MAGIC_AT_LEAST(20110605, 2)
11409 apr_socket_t *sock;
11410 apr_interval_time_t existing_timeout = 0;
11411 #endif
11412
11413 if (buffer_size == 0)
11414 buffer_size = 65536;
11415
11416 /*
11417 * Override the socket timeout for writing back data to the
11418 * client. If that wasn't defined this will be the same as
11419 * the timeout for the socket used in communicating with the
11420 * daemon, or left as the overall server timeout if that
11421 * isn't specified. Just to be safe we remember the existing
11422 * timeout and restore it at the end of a successful request
11423 * in case the same connection if kept alive and used for a
11424 * subsequent request with a different handler.
11425 */
11426
11427 #if AP_MODULE_MAGIC_AT_LEAST(20110605, 2)
11428 sock = ap_get_conn_socket(r->connection);
11429
11430 rv = apr_socket_timeout_get(sock, &existing_timeout);
11431
11432 if (rv != APR_SUCCESS) {
11433 existing_timeout = 0;
11434 }
11435 else {
11436 if (timeout)
11437 apr_socket_timeout_set(sock, timeout);
11438 }
11439 #endif
11440
11441 /*
11442 * Transfer any response content. We want to avoid the
11443 * problem where the core output filter has no flow control
11444 * to deal with slow HTTP clients and can actually buffer up
11445 * excessive amounts of response content in memory. A fix
11446 * for this was only introduced in Apache 2.3.3, with
11447 * possible further tweaks in Apache 2.4.1. To avoid issue of
11448 * what version it was implemented in, just employ a
11449 * strategy of forcing a flush every time we pass through
11450 * more than a certain amount of data.
11451 */
11452
11453 tmpbb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
11454
11455 while ((e = APR_BRIGADE_FIRST(bb)) != APR_BRIGADE_SENTINEL(bb)) {
11456 /* If we have reached end of stream, we need to pass it on */
11457
11458 if (APR_BUCKET_IS_EOS(e)) {
11459 /*
11460 * Probably do not need to force a flush as EOS should
11461 * do that, but do it just in case when we potentially
11462 * have pending data to be written out.
11463 */
11464
11465 if (bytes_transfered != 0) {
11466 APR_BRIGADE_INSERT_TAIL(tmpbb, apr_bucket_flush_create(
11467 r->connection->bucket_alloc));
11468 }
11469
11470 APR_BRIGADE_INSERT_TAIL(tmpbb, apr_bucket_eos_create(
11471 r->connection->bucket_alloc));
11472
11473 rv = ap_pass_brigade(r->output_filters, tmpbb);
11474
11475 apr_brigade_cleanup(tmpbb);
11476
11477 if (rv != APR_SUCCESS) {
11478 apr_brigade_destroy(bb);
11479
11480 /*
11481 * Don't flag error if client connection was aborted
11482 * so that access log still records the original HTTP
11483 * response code returned by the WSGI application.
11484 */
11485
11486 if (r->connection->aborted)
11487 return OK;
11488
11489 return HTTP_INTERNAL_SERVER_ERROR;
11490 }
11491
11492 break;
11493 }
11494
11495 /*
11496 * Force the reading in of next block of data to be
11497 * transfered if necessary. If the bucket is a heap
11498 * bucket, then it will be whatever data is in it. If it
11499 * is a socket bucket, this will result in the bucket
11500 * being converted to a heap bucket with some amount of
11501 * data and the socket bucket added back in after it. Any
11502 * non data buckets should be skipped and discarded. The
11503 * result should always be that the first bucket is a
11504 * heap bucket.
11505 */
11506
11507 rv = apr_bucket_read(e, &data, &length, mode);
11508
11509 /*
11510 * If we would have blocked if not in non blocking mode
11511 * we send a flush bucket to ensure that all buffered
11512 * data is sent out before we block waiting for more.
11513 */
11514
11515 if (rv == APR_EAGAIN && mode == APR_NONBLOCK_READ) {
11516 APR_BRIGADE_INSERT_TAIL(tmpbb, apr_bucket_flush_create(
11517 r->connection->bucket_alloc));
11518
11519 rv = ap_pass_brigade(r->output_filters, tmpbb);
11520
11521 apr_brigade_cleanup(tmpbb);
11522
11523 if (rv == APR_TIMEUP) {
11524 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
11525 "mod_wsgi (pid=%d): Failed to proxy response "
11526 "to client.", getpid());
11527 }
11528
11529 if (rv != APR_SUCCESS) {
11530 apr_brigade_destroy(bb);
11531
11532 /*
11533 * Don't flag error if client connection was aborted
11534 * so that access log still records the original HTTP
11535 * response code returned by the WSGI application.
11536 */
11537
11538 if (r->connection->aborted)
11539 return OK;
11540
11541 return HTTP_INTERNAL_SERVER_ERROR;
11542 }
11543
11544 bytes_transfered = 0;
11545
11546 bucket_count = 0;
11547
11548 /*
11549 * Retry read from daemon using a blocking read. We do
11550 * not delete the bucket as we want to operate on the
11551 * same one as we would have blocked.
11552 */
11553
11554 mode = APR_BLOCK_READ;
11555
11556 continue;
11557
11558 } else if (rv != APR_SUCCESS) {
11559 apr_brigade_destroy(bb);
11560
11561 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
11562 "mod_wsgi (pid=%d): Failed to proxy response "
11563 "from daemon.", getpid());
11564
11565 /*
11566 * Don't flag error if couldn't read from daemon
11567 * so that access log still records the original HTTP
11568 * response code returned by the WSGI application.
11569 */
11570
11571 return OK;
11572 }
11573
11574 /*
11575 * We had some data to transfer. Next time round we need to
11576 * always be try a non-blocking read first.
11577 */
11578
11579 mode = APR_NONBLOCK_READ;
11580
11581 /*
11582 * Now we don't actually work with the data which was
11583 * read direct and instead simply remove what should be a
11584 * heap bucket from the start of the bucket brigade and
11585 * then place in a new bucket brigade to be pushed out to
11586 * the client. By passing down the bucket, it avoids the
11587 * need to create a transient bucket holding a reference
11588 * to the data from the first bucket.
11589 */
11590
11591 APR_BUCKET_REMOVE(e);
11592 APR_BRIGADE_INSERT_TAIL(tmpbb, e);
11593
11594 /*
11595 * If we have reached the buffer size threshold, we want
11596 * to flush the data so that we aren't buffering too much
11597 * in memory and blowing out memory size. We also have a
11598 * check on the number of buckets we have accumulated as
11599 * a large number of buckets with very small amounts of
11600 * data will also accumulate a lot of memory. Apache's
11601 * own flow control doesn't cope with such a situation.
11602 * Right now hard wire the max number of buckets at 16
11603 * which equates to worst case number of separate data
11604 * blocks can be written by a writev() call on systems
11605 * such as Solaris.
11606 */
11607
11608 bytes_transfered += length;
11609
11610 bucket_count += 1;
11611
11612 if (bytes_transfered > buffer_size || bucket_count >= 16) {
11613 APR_BRIGADE_INSERT_TAIL(tmpbb, apr_bucket_flush_create(
11614 r->connection->bucket_alloc));
11615
11616 bytes_transfered = 0;
11617
11618 bucket_count = 0;
11619
11620 /*
11621 * Since we flushed the data out to the client, it is
11622 * okay to go back and do a blocking read the next time.
11623 */
11624
11625 mode = APR_BLOCK_READ;
11626 }
11627
11628 /* Pass the heap bucket and any flush bucket on. */
11629
11630 rv = ap_pass_brigade(r->output_filters, tmpbb);
11631
11632 apr_brigade_cleanup(tmpbb);
11633
11634 if (rv == APR_TIMEUP) {
11635 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
11636 "mod_wsgi (pid=%d): Failed to proxy response "
11637 "to client.", getpid());
11638 }
11639
11640 if (rv != APR_SUCCESS) {
11641 apr_brigade_destroy(bb);
11642
11643 /*
11644 * Don't flag error if client connection was aborted
11645 * so that access log still records the original HTTP
11646 * response code returned by the WSGI application.
11647 */
11648
11649 if (r->connection->aborted)
11650 return OK;
11651
11652 return HTTP_INTERNAL_SERVER_ERROR;
11653 }
11654 }
11655
11656 #if AP_MODULE_MAGIC_AT_LEAST(20110605, 2)
11657 if (existing_timeout)
11658 apr_socket_timeout_set(sock, existing_timeout);
11659 #endif
11660
11661 apr_brigade_destroy(bb);
11662
11663 return OK;
11664 }
11665
11666 #define ASCII_CRLF "\015\012"
11667 #define ASCII_ZERO "\060"
11668
wsgi_execute_remote(request_rec * r)11669 static int wsgi_execute_remote(request_rec *r)
11670 {
11671 WSGIRequestConfig *config = NULL;
11672 WSGIDaemonSocket *daemon = NULL;
11673 WSGIProcessGroup *group = NULL;
11674
11675 char *key = NULL;
11676 const char *hash = NULL;
11677
11678 int status;
11679 apr_status_t rv;
11680
11681 int seen_eos;
11682 int child_stopped_reading;
11683 apr_bucket_brigade *bbout;
11684 apr_bucket_brigade *bbin;
11685 apr_bucket *b;
11686
11687 const char *location = NULL;
11688
11689 char *header_buffer = NULL;
11690 int header_buflen = 0;
11691
11692 /* Grab request configuration. */
11693
11694 config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
11695 &wsgi_module);
11696
11697 /*
11698 * Only allow the process group to match against a restricted
11699 * set of processes if such a restricted set has been defined.
11700 */
11701
11702 if (config->restrict_process) {
11703 if (!apr_table_get(config->restrict_process,
11704 config->process_group)) {
11705 wsgi_log_script_error(r, apr_psprintf(r->pool, "Daemon "
11706 "process called '%s' cannot be "
11707 "accessed by this WSGI application "
11708 "as not a member of allowed groups",
11709 config->process_group), r->filename);
11710
11711 return HTTP_INTERNAL_SERVER_ERROR;
11712 }
11713 }
11714
11715 /*
11716 * Do not process request as remote if actually targeted at
11717 * the main Apache processes.
11718 */
11719
11720 if (!*config->process_group)
11721 return DECLINED;
11722
11723 /* Grab details of matching process group. */
11724
11725 if (!wsgi_daemon_index) {
11726 wsgi_log_script_error(r, apr_psprintf(r->pool, "No WSGI daemon "
11727 "process called '%s' has been configured",
11728 config->process_group), r->filename);
11729
11730 return HTTP_INTERNAL_SERVER_ERROR;
11731 }
11732
11733 group = (WSGIProcessGroup *)apr_hash_get(wsgi_daemon_index,
11734 config->process_group,
11735 APR_HASH_KEY_STRING);
11736
11737 if (!group) {
11738 wsgi_log_script_error(r, apr_psprintf(r->pool, "No WSGI daemon "
11739 "process called '%s' has been configured",
11740 config->process_group), r->filename);
11741
11742 return HTTP_INTERNAL_SERVER_ERROR;
11743 }
11744
11745 /*
11746 * Only allow the process group to match against a daemon
11747 * process defined within a virtual host with the same
11748 * server name or a daemon process defined at global server
11749 * scope.
11750 */
11751
11752 if (group->server != r->server && group->server != wsgi_server) {
11753 if (strcmp(group->server->server_hostname,
11754 r->server->server_hostname) != 0) {
11755 wsgi_log_script_error(r, apr_psprintf(r->pool, "Daemon "
11756 "process called '%s' cannot be "
11757 "accessed by this WSGI application",
11758 config->process_group), r->filename);
11759
11760 return HTTP_INTERNAL_SERVER_ERROR;
11761 }
11762 }
11763
11764 /*
11765 * Check restrictions related to the group of the WSGI
11766 * script file and who has write access to the directory it
11767 * is contained in. If not satisfied forbid access.
11768 */
11769
11770 if (group->script_group) {
11771 apr_uid_t gid;
11772 struct group *grent = NULL;
11773 const char *grname = NULL;
11774 apr_finfo_t finfo;
11775 const char *path = NULL;
11776
11777 if (!(r->finfo.valid & APR_FINFO_GROUP)) {
11778 wsgi_log_script_error(r, apr_psprintf(r->pool, "Group "
11779 "information not available for WSGI "
11780 "script file"), r->filename);
11781 return HTTP_FORBIDDEN;
11782 }
11783
11784 gid = r->finfo.group;
11785
11786 if ((grent = getgrgid(gid)) == NULL) {
11787 wsgi_log_script_error(r, apr_psprintf(r->pool, "Couldn't "
11788 "determine group of WSGI script file, "
11789 "gid=%ld", (long)gid), r->filename);
11790 return HTTP_FORBIDDEN;
11791 }
11792
11793 grname = grent->gr_name;
11794
11795 if (strcmp(group->script_group, grname)) {
11796 wsgi_log_script_error(r, apr_psprintf(r->pool, "Group of WSGI "
11797 "script file does not match required group "
11798 "for daemon process, group=%s", grname),
11799 r->filename);
11800 return HTTP_FORBIDDEN;
11801 }
11802
11803 if (!(r->finfo.valid & APR_FINFO_WPROT)) {
11804 wsgi_log_script_error(r, apr_psprintf(r->pool, "World "
11805 "permissions not available for WSGI "
11806 "script file"), r->filename);
11807 return HTTP_FORBIDDEN;
11808 }
11809
11810 if (r->finfo.protection & APR_FPROT_WWRITE) {
11811 wsgi_log_script_error(r, apr_psprintf(r->pool, "WSGI script "
11812 "file is writable to world"), r->filename);
11813 return HTTP_FORBIDDEN;
11814 }
11815
11816 path = ap_make_dirstr_parent(r->pool, r->filename);
11817
11818 if (apr_stat(&finfo, path, APR_FINFO_NORM, r->pool) != APR_SUCCESS) {
11819 wsgi_log_script_error(r, apr_psprintf(r->pool, "Unable to stat "
11820 "parent directory of WSGI script"), path);
11821 return HTTP_FORBIDDEN;
11822 }
11823
11824 gid = finfo.group;
11825
11826 if ((grent = getgrgid(gid)) == NULL) {
11827 wsgi_log_script_error(r, apr_psprintf(r->pool, "Couldn't "
11828 "determine group of parent directory of "
11829 "WSGI script file, gid=%ld", (long)gid),
11830 r->filename);
11831 return HTTP_FORBIDDEN;
11832 }
11833
11834 grname = grent->gr_name;
11835
11836 if (strcmp(group->script_group, grname)) {
11837 wsgi_log_script_error(r, apr_psprintf(r->pool, "Group of parent "
11838 "directory of WSGI script file does not "
11839 "match required group for daemon process, "
11840 "group=%s", grname), r->filename);
11841 return HTTP_FORBIDDEN;
11842 }
11843
11844 if (finfo.protection & APR_FPROT_WWRITE) {
11845 wsgi_log_script_error(r, apr_psprintf(r->pool, "Parent directory "
11846 "of WSGI script file is writable to world"),
11847 r->filename);
11848 return HTTP_FORBIDDEN;
11849 }
11850 }
11851
11852 /*
11853 * Check restrictions related to who can be the owner of
11854 * the WSGI script file and who has write access to the
11855 * directory it is contained in. If not satisfied forbid
11856 * access.
11857 */
11858
11859 if (group->script_user) {
11860 apr_uid_t uid;
11861 struct passwd *pwent = NULL;
11862 const char *pwname = NULL;
11863 apr_finfo_t finfo;
11864 const char *path = NULL;
11865
11866 if (!(r->finfo.valid & APR_FINFO_USER)) {
11867 wsgi_log_script_error(r, apr_psprintf(r->pool, "User "
11868 "information not available for WSGI "
11869 "script file"), r->filename);
11870 return HTTP_FORBIDDEN;
11871 }
11872
11873 uid = r->finfo.user;
11874
11875 if ((pwent = getpwuid(uid)) == NULL) {
11876 wsgi_log_script_error(r, apr_psprintf(r->pool, "Couldn't "
11877 "determine owner of WSGI script file, "
11878 "uid=%ld", (long)uid), r->filename);
11879 return HTTP_FORBIDDEN;
11880 }
11881
11882 pwname = pwent->pw_name;
11883
11884 if (strcmp(group->script_user, pwname)) {
11885 wsgi_log_script_error(r, apr_psprintf(r->pool, "Owner of WSGI "
11886 "script file does not match required user "
11887 "for daemon process, user=%s", pwname),
11888 r->filename);
11889 return HTTP_FORBIDDEN;
11890 }
11891
11892 if (!(r->finfo.valid & APR_FINFO_GPROT)) {
11893 wsgi_log_script_error(r, apr_psprintf(r->pool, "Group "
11894 "permissions not available for WSGI "
11895 "script file"), r->filename);
11896 return HTTP_FORBIDDEN;
11897 }
11898
11899 if (r->finfo.protection & APR_FPROT_GWRITE) {
11900 wsgi_log_script_error(r, apr_psprintf(r->pool, "WSGI script "
11901 "file is writable to group"), r->filename);
11902 return HTTP_FORBIDDEN;
11903 }
11904
11905 if (!(r->finfo.valid & APR_FINFO_WPROT)) {
11906 wsgi_log_script_error(r, apr_psprintf(r->pool, "World "
11907 "permissions not available for WSGI "
11908 "script file"), r->filename);
11909 return HTTP_FORBIDDEN;
11910 }
11911
11912 if (r->finfo.protection & APR_FPROT_WWRITE) {
11913 wsgi_log_script_error(r, apr_psprintf(r->pool, "WSGI script "
11914 "file is writable to world"), r->filename);
11915 return HTTP_FORBIDDEN;
11916 }
11917
11918 path = ap_make_dirstr_parent(r->pool, r->filename);
11919
11920 if (apr_stat(&finfo, path, APR_FINFO_NORM, r->pool) != APR_SUCCESS) {
11921 wsgi_log_script_error(r, apr_psprintf(r->pool, "Unable to stat "
11922 "parent directory of WSGI script"), path);
11923 return HTTP_FORBIDDEN;
11924 }
11925
11926 uid = finfo.user;
11927
11928 if ((pwent = getpwuid(uid)) == NULL) {
11929 wsgi_log_script_error(r, apr_psprintf(r->pool, "Couldn't "
11930 "determine owner of parent directory of "
11931 "WSGI script file, uid=%ld", (long)uid),
11932 r->filename);
11933 return HTTP_FORBIDDEN;
11934 }
11935
11936 pwname = pwent->pw_name;
11937
11938 if (strcmp(group->script_user, pwname)) {
11939 wsgi_log_script_error(r, apr_psprintf(r->pool, "Owner of parent "
11940 "directory of WSGI script file does not "
11941 "match required user for daemon process, "
11942 "user=%s", pwname), r->filename);
11943 return HTTP_FORBIDDEN;
11944 }
11945
11946 if (finfo.protection & APR_FPROT_WWRITE) {
11947 wsgi_log_script_error(r, apr_psprintf(r->pool, "Parent directory "
11948 "of WSGI script file is writable to world"),
11949 r->filename);
11950 return HTTP_FORBIDDEN;
11951 }
11952
11953 if (finfo.protection & APR_FPROT_GWRITE) {
11954 wsgi_log_script_error(r, apr_psprintf(r->pool, "Parent directory "
11955 "of WSGI script file is writable to group"),
11956 r->filename);
11957 return HTTP_FORBIDDEN;
11958 }
11959 }
11960
11961 /*
11962 * Add magic marker into request environment so that daemon
11963 * process can verify that request is from a sender that can
11964 * be trusted. Wipe out original key to make it a bit harder
11965 * for rogue code in Apache child processes to trawl through
11966 * memory looking for unhashed string.
11967 */
11968
11969 key = apr_psprintf(r->pool, "%ld|%s|%s|%s", group->random,
11970 group->socket_path, r->filename,
11971 config->handler_script);
11972 hash = ap_md5(r->pool, (const unsigned char *)key);
11973 memset(key, '\0', strlen(key));
11974
11975 apr_table_setn(r->subprocess_env, "mod_wsgi.magic", hash);
11976
11977 /* Create connection to the daemon process. */
11978
11979 apr_table_setn(r->subprocess_env, "mod_wsgi.queue_start",
11980 apr_psprintf(r->pool, "%" APR_TIME_T_FMT, apr_time_now()));
11981
11982 daemon = (WSGIDaemonSocket *)apr_pcalloc(r->pool,
11983 sizeof(WSGIDaemonSocket));
11984
11985 daemon->name = config->process_group;
11986 daemon->socket_path = group->socket_path;
11987 daemon->connect_timeout = group->connect_timeout;
11988 daemon->socket_timeout = group->socket_timeout;
11989
11990 if ((status = wsgi_connect_daemon(r, daemon)) != OK)
11991 return status;
11992
11993 /* Send request details and subprocess environment. */
11994
11995 if (wsgi_server_config->verbose_debugging) {
11996 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
11997 "mod_wsgi (pid=%d): Request server was "
11998 "'%s|%d'.", getpid(), r->server->server_hostname,
11999 r->server->port);
12000 }
12001
12002 if ((rv = wsgi_send_request(r, config, daemon)) != APR_SUCCESS) {
12003 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
12004 "mod_wsgi (pid=%d): Unable to send request details "
12005 "to WSGI daemon process '%s' on '%s'.", getpid(),
12006 daemon->name, daemon->socket_path);
12007
12008 return HTTP_INTERNAL_SERVER_ERROR;
12009 }
12010
12011 /* Setup bucket brigade for reading response from daemon. */
12012
12013 bbin = apr_brigade_create(r->pool, r->connection->bucket_alloc);
12014 b = apr_bucket_socket_create(daemon->socket, r->connection->bucket_alloc);
12015 APR_BRIGADE_INSERT_TAIL(bbin, b);
12016 b = apr_bucket_eos_create(r->connection->bucket_alloc);
12017 APR_BRIGADE_INSERT_TAIL(bbin, b);
12018
12019 /* Create alternate buffer for reading in response header values. */
12020
12021 if (group->header_buffer_size != 0) {
12022 header_buflen = group->header_buffer_size;
12023 header_buffer = apr_pcalloc(r->pool, header_buflen);
12024 }
12025
12026 /*
12027 * If process reload mechanism enabled, then we need to look
12028 * for marker indicating it is okay to transfer content, or
12029 * whether process is being restarted and that we should
12030 * therefore create a connection to daemon process again.
12031 */
12032
12033 if (*config->process_group) {
12034 int retries = 0;
12035 int maximum = (2*group->processes)+1;
12036
12037 /*
12038 * While special header indicates a restart is being
12039 * done, then keep trying to reconnect. Cap the number
12040 * of retries to at most about 2 times the number of
12041 * daemon processes in the process group. If still being
12042 * told things are being restarted, then we will error
12043 * indicating service is unavailable.
12044 */
12045
12046 while (retries < maximum) {
12047 /* Scan the CGI script like headers from daemon. */
12048
12049 status = wsgi_scan_headers_brigade(r, bbin, header_buffer,
12050 header_buflen);
12051
12052 if (status != OK)
12053 return status;
12054
12055 /*
12056 * Status must be 200 for our special headers. Ideally
12057 * we would use 0 as did in the past but Apache 2.4
12058 * complains if use 0 as not a valid status value.
12059 */
12060
12061 if (r->status != 200) {
12062 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
12063 "mod_wsgi (pid=%d): Unexpected status from "
12064 "WSGI daemon process '%d'.", getpid(),
12065 r->status);
12066
12067 r->status_line = NULL;
12068
12069 return HTTP_INTERNAL_SERVER_ERROR;
12070 }
12071
12072 if (!strcmp(r->status_line, "200 Continue")) {
12073 r->status_line = NULL;
12074
12075 break;
12076 }
12077
12078 if (!strcmp(r->status_line, "200 Timeout")) {
12079 r->status_line = NULL;
12080
12081 return HTTP_GATEWAY_TIME_OUT;
12082 }
12083
12084 if (strcmp(r->status_line, "200 Rejected")) {
12085 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
12086 "mod_wsgi (pid=%d): Unexpected status from "
12087 "WSGI daemon process '%d'.", getpid(), r->status);
12088
12089 r->status_line = NULL;
12090
12091 return HTTP_INTERNAL_SERVER_ERROR;
12092 }
12093
12094 r->status_line = NULL;
12095
12096 /* Need to close previous socket connection first. */
12097
12098 apr_socket_close(daemon->socket);
12099
12100 /* Has maximum number of attempts been reached. */
12101
12102 if (retries == maximum) {
12103 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
12104 "mod_wsgi (pid=%d): Maximum number of WSGI "
12105 "daemon process restart connects reached '%d'.",
12106 getpid(), maximum);
12107 return HTTP_SERVICE_UNAVAILABLE;
12108 }
12109
12110 retries++;
12111
12112 config->daemon_restarts++;
12113
12114 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
12115 "mod_wsgi (pid=%d): Connect after WSGI daemon "
12116 "process restart, attempt #%d.", getpid(),
12117 retries);
12118
12119 /* Connect and setup connection just like before. */
12120
12121 if ((status = wsgi_connect_daemon(r, daemon)) != OK)
12122 return status;
12123
12124 if ((rv = wsgi_send_request(r, config, daemon)) != APR_SUCCESS) {
12125 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
12126 "mod_wsgi (pid=%d): Unable to send request "
12127 "details to WSGI daemon process '%s' on '%s'.",
12128 getpid(), daemon->name, daemon->socket_path);
12129
12130 return HTTP_INTERNAL_SERVER_ERROR;
12131 }
12132
12133 apr_brigade_destroy(bbin);
12134
12135 bbin = apr_brigade_create(r->pool, r->connection->bucket_alloc);
12136 b = apr_bucket_socket_create(daemon->socket,
12137 r->connection->bucket_alloc);
12138 APR_BRIGADE_INSERT_TAIL(bbin, b);
12139 b = apr_bucket_eos_create(r->connection->bucket_alloc);
12140 APR_BRIGADE_INSERT_TAIL(bbin, b);
12141 }
12142 }
12143
12144
12145 /*
12146 * Need to reset request status value to HTTP_OK else it
12147 * screws up HTTP input filter when processing a POST
12148 * request with 100-continue requirement.
12149 */
12150
12151 r->status = HTTP_OK;
12152
12153 /*
12154 * Transfer any request content which was provided. Note that we
12155 * actually frame each data block sent with same format as is used
12156 * for chunked transfer encoding. This will be decoded in the
12157 * daemon process. This is done so that the EOS can be properly
12158 * identified by the daemon process in the absence of a value for
12159 * CONTENT_LENGTH that can be relied on. The CONTENT_LENGTH is
12160 * dodgy when have mutating input filters and none will be present
12161 * at all if chunked request content was used.
12162 */
12163
12164 seen_eos = 0;
12165 child_stopped_reading = 0;
12166
12167 bbout = apr_brigade_create(r->pool, r->connection->bucket_alloc);
12168
12169 do {
12170 apr_bucket *bucket;
12171
12172 rv = ap_get_brigade(r->input_filters, bbout, AP_MODE_READBYTES,
12173 APR_BLOCK_READ, HUGE_STRING_LEN);
12174
12175 if (rv != APR_SUCCESS) {
12176 char status_buffer[512];
12177 const char *error_message;
12178
12179 error_message = apr_psprintf(r->pool, "Request data read "
12180 "error when proxying data to daemon process: %s",
12181 apr_strerror(rv, status_buffer, sizeof(
12182 status_buffer)-1));
12183
12184 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
12185 "mod_wsgi (pid=%d): %s.", getpid(), error_message);
12186
12187 if (APR_STATUS_IS_TIMEUP(rv))
12188 return HTTP_REQUEST_TIME_OUT;
12189
12190 return HTTP_INTERNAL_SERVER_ERROR;
12191 }
12192
12193 for (bucket = APR_BRIGADE_FIRST(bbout);
12194 bucket != APR_BRIGADE_SENTINEL(bbout);
12195 bucket = APR_BUCKET_NEXT(bucket))
12196 {
12197 const char *data;
12198 apr_size_t len;
12199
12200 char chunk_hdr[20];
12201 apr_size_t hdr_len;
12202
12203 struct iovec vec[3];
12204
12205 if (APR_BUCKET_IS_EOS(bucket)) {
12206 /* Send closing frame for chunked content. */
12207
12208 rv = wsgi_socket_send(daemon->socket,
12209 ASCII_ZERO ASCII_CRLF ASCII_CRLF, 5);
12210
12211 if (rv != APR_SUCCESS) {
12212 char status_buffer[512];
12213 const char *error_message;
12214
12215 error_message = apr_psprintf(r->pool, "Request data write "
12216 "error when proxying data to daemon process: %s",
12217 apr_strerror(rv, status_buffer, sizeof(
12218 status_buffer)-1));
12219
12220 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
12221 "mod_wsgi (pid=%d): %s.", getpid(),
12222 error_message);
12223 }
12224
12225 seen_eos = 1;
12226 break;
12227 }
12228
12229 /* We can't do much with this. */
12230
12231 if (APR_BUCKET_IS_FLUSH(bucket)) {
12232 continue;
12233 }
12234
12235 /* If the child stopped, we still must read to EOS. */
12236
12237 if (child_stopped_reading) {
12238 continue;
12239 }
12240
12241 /* Read block. */
12242
12243 rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
12244
12245 if (rv != APR_SUCCESS) {
12246 char status_buffer[512];
12247 const char *error_message;
12248
12249 error_message = apr_psprintf(r->pool, "Request data read "
12250 "error when proxying data to daemon process: %s",
12251 apr_strerror(rv, status_buffer, sizeof(
12252 status_buffer)-1));
12253
12254 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
12255 "mod_wsgi (pid=%d): %s.", getpid(),
12256 error_message);
12257
12258 break;
12259 }
12260
12261 /*
12262 * Keep writing data to the child until done or too
12263 * much time elapses with no progress or an error
12264 * occurs. Frame the data being sent with format used
12265 * for chunked transfer encoding.
12266 */
12267
12268 hdr_len = apr_snprintf(chunk_hdr, sizeof(chunk_hdr),
12269 "%" APR_UINT64_T_HEX_FMT ASCII_CRLF, (apr_uint64_t)len);
12270
12271 vec[0].iov_base = (void *)chunk_hdr;
12272 vec[0].iov_len = hdr_len;
12273 vec[1].iov_base = (void *)data;
12274 vec[1].iov_len = len;
12275 vec[2].iov_base = (void *)ASCII_CRLF;
12276 vec[2].iov_len = 2;
12277
12278 rv = wsgi_socket_sendv(daemon->socket, vec, 3);
12279
12280 if (rv != APR_SUCCESS) {
12281 char status_buffer[512];
12282 const char *error_message;
12283
12284 error_message = apr_psprintf(r->pool, "Request data write "
12285 "error when proxying data to daemon process: %s",
12286 apr_strerror(rv, status_buffer, sizeof(
12287 status_buffer)-1));
12288
12289 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
12290 "mod_wsgi (pid=%d): %s.", getpid(),
12291 error_message);
12292
12293 /* Daemon stopped reading, discard remainder. */
12294
12295 child_stopped_reading = 1;
12296 }
12297 }
12298 apr_brigade_cleanup(bbout);
12299 }
12300 while (!seen_eos);
12301
12302 /*
12303 * Close socket for writing so that daemon detects end of
12304 * request content.
12305 */
12306
12307 apr_socket_shutdown(daemon->socket, APR_SHUTDOWN_WRITE);
12308
12309 /* Scan the CGI script like headers from daemon. */
12310
12311 status = wsgi_scan_headers_brigade(r, bbin, header_buffer,
12312 header_buflen);
12313
12314 if (status != OK)
12315 return status;
12316
12317 /*
12318 * Look for the special case of status being 200 but the
12319 * status line indicating an error and translate it into a
12320 * 500 error so that error document processing will occur
12321 * for those cases where WSGI application wouldn't have
12322 * supplied their own error document. We used to use 0
12323 * here for status but Apache 2.4 prohibits it now.
12324 */
12325
12326 if (r->status == 200 && !strcmp(r->status_line, "200 Error")) {
12327 r->status_line = NULL;
12328
12329 return HTTP_INTERNAL_SERVER_ERROR;
12330 }
12331
12332 /*
12333 * Look for 'Location' header and if an internal
12334 * redirect, execute the redirect. This behaviour is
12335 * consistent with how mod_cgi and mod_cgid work and
12336 * what is permitted by the CGI specification.
12337 */
12338
12339 location = apr_table_get(r->headers_out, "Location");
12340
12341 if (location && location[0] == '/' && r->status == 200) {
12342 /*
12343 * Discard all response content returned from
12344 * the daemon process.
12345 */
12346
12347 wsgi_discard_output(bbin);
12348 apr_brigade_destroy(bbin);
12349
12350 /*
12351 * The internal redirect needs to be a GET no
12352 * matter what the original method was.
12353 */
12354
12355 r->method = apr_pstrdup(r->pool, "GET");
12356 r->method_number = M_GET;
12357
12358 /*
12359 * We already read the message body (if any), so
12360 * don't allow the redirected request to think
12361 * it has one. Not sure if we need to worry
12362 * about removing 'Transfer-Encoding' header.
12363 */
12364
12365 apr_table_unset(r->headers_in, "Content-Length");
12366
12367 ap_internal_redirect_handler(location, r);
12368
12369 return OK;
12370 }
12371
12372 /*
12373 * Allow the web server to override any error
12374 * page produced by the WSGI application.
12375 */
12376
12377 if (config->error_override && ap_is_HTTP_ERROR(r->status)) {
12378 status = r->status;
12379
12380 r->status = HTTP_OK;
12381 r->status_line = NULL;
12382
12383 /*
12384 * Discard all response content returned from
12385 * the daemon process if any expected.
12386 */
12387
12388 if (!r->header_only && /* not HEAD request */
12389 (status != HTTP_NO_CONTENT) && /* not 204 */
12390 (status != HTTP_NOT_MODIFIED)) { /* not 304 */
12391 wsgi_discard_output(bbin);
12392 apr_brigade_destroy(bbin);
12393 }
12394
12395 return status;
12396 }
12397
12398 /* Transfer any response content. */
12399
12400 return wsgi_transfer_response(r, bbin, group->response_buffer_size,
12401 group->response_socket_timeout);
12402 }
12403
wsgi_socket_read(apr_socket_t * sock,void * vbuf,apr_size_t size)12404 static apr_status_t wsgi_socket_read(apr_socket_t *sock, void *vbuf,
12405 apr_size_t size)
12406 {
12407 char *buf = vbuf;
12408 apr_status_t rv;
12409 apr_size_t count = 0;
12410 apr_size_t len = 0;
12411
12412 do {
12413 len = size - count;
12414 if ((rv = apr_socket_recv(sock, buf + count, &len)) != APR_SUCCESS)
12415 return rv;
12416 count += len;
12417 } while (count < size);
12418
12419 return APR_SUCCESS;
12420 }
12421
wsgi_read_strings(apr_socket_t * sock,char *** s,apr_pool_t * p)12422 static apr_status_t wsgi_read_strings(apr_socket_t *sock, char ***s,
12423 apr_pool_t *p)
12424 {
12425 apr_status_t rv;
12426
12427 apr_size_t total;
12428
12429 apr_size_t n;
12430 apr_size_t i;
12431 apr_size_t l;
12432
12433 char *buffer;
12434 char *offset;
12435
12436 if ((rv = wsgi_socket_read(sock, &total, sizeof(total))) != APR_SUCCESS)
12437 return rv;
12438
12439 buffer = apr_palloc(p, total);
12440 offset = buffer;
12441
12442 if ((rv = wsgi_socket_read(sock, buffer, total)) != APR_SUCCESS)
12443 return rv;
12444
12445 memcpy(&n, offset, sizeof(n));
12446 offset += sizeof(n);
12447
12448 *s = apr_pcalloc(p, (n+1)*sizeof(**s));
12449
12450 for (i = 0; i < n; i++) {
12451 l = strlen(offset) + 1;
12452 (*s)[i] = offset;
12453 offset += l;
12454 }
12455
12456 return APR_SUCCESS;
12457 }
12458
wsgi_read_request(apr_socket_t * sock,request_rec * r)12459 static apr_status_t wsgi_read_request(apr_socket_t *sock, request_rec *r)
12460 {
12461 int rv;
12462
12463 char **vars;
12464
12465 /* Read subprocess environment from request object. */
12466
12467 rv = wsgi_read_strings(sock, &vars, r->pool);
12468
12469 if (rv != APR_SUCCESS)
12470 return rv;
12471
12472 while (*vars) {
12473 char *key = *vars++;
12474
12475 apr_table_setn(r->subprocess_env, key, *vars++);
12476 }
12477
12478 return APR_SUCCESS;
12479 }
12480
12481 static ap_filter_rec_t *wsgi_header_filter_handle;
12482
wsgi_header_filter(ap_filter_t * f,apr_bucket_brigade * b)12483 static apr_status_t wsgi_header_filter(ap_filter_t *f, apr_bucket_brigade *b)
12484 {
12485 request_rec *r = f->r;
12486
12487 struct iovec vec1[4];
12488 apr_bucket_brigade *b2;
12489 char crlf[] = CRLF;
12490 apr_size_t buflen;
12491
12492 const apr_array_header_t *elts;
12493 const apr_table_entry_t *t_elt;
12494 const apr_table_entry_t *t_end;
12495 struct iovec *vec2;
12496 struct iovec *vec2_next;
12497
12498 /* Output status line. */
12499
12500 vec1[0].iov_base = (void *)"Status:";
12501 vec1[0].iov_len = strlen("Status:");
12502 vec1[1].iov_base = (void *)" ";
12503 vec1[1].iov_len = sizeof(" ") - 1;
12504 vec1[2].iov_base = (void *)(r->status_line);
12505 vec1[2].iov_len = strlen(r->status_line);
12506 vec1[3].iov_base = (void *)CRLF;
12507 vec1[3].iov_len = sizeof(CRLF) - 1;
12508
12509 b2 = apr_brigade_create(r->pool, r->connection->bucket_alloc);
12510 apr_brigade_writev(b2, NULL, NULL, vec1, 4);
12511
12512 /* Merge response header tables together. */
12513
12514 if (!apr_is_empty_table(r->err_headers_out)) {
12515 r->headers_out = apr_table_overlay(r->pool, r->err_headers_out,
12516 r->headers_out);
12517 }
12518
12519 /* Override the content type for response. */
12520
12521 if (r->content_type)
12522 apr_table_setn(r->headers_out, "Content-Type", r->content_type);
12523
12524 /* Formt the response headers for output. */
12525
12526 elts = apr_table_elts(r->headers_out);
12527 if (elts->nelts != 0) {
12528 t_elt = (const apr_table_entry_t *)(elts->elts);
12529 t_end = t_elt + elts->nelts;
12530 vec2 = (struct iovec *)apr_palloc(r->pool, 4 * elts->nelts *
12531 sizeof(struct iovec));
12532 vec2_next = vec2;
12533
12534 do {
12535 vec2_next->iov_base = (void*)(t_elt->key);
12536 vec2_next->iov_len = strlen(t_elt->key);
12537 vec2_next++;
12538 vec2_next->iov_base = ": ";
12539 vec2_next->iov_len = sizeof(": ") - 1;
12540 vec2_next++;
12541 vec2_next->iov_base = (void*)(t_elt->val);
12542 vec2_next->iov_len = strlen(t_elt->val);
12543 vec2_next++;
12544 vec2_next->iov_base = CRLF;
12545 vec2_next->iov_len = sizeof(CRLF) - 1;
12546 vec2_next++;
12547 t_elt++;
12548 } while (t_elt < t_end);
12549
12550 apr_brigade_writev(b2, NULL, NULL, vec2, vec2_next - vec2);
12551 }
12552
12553 /* Format terminating blank line for response headers. */
12554
12555 buflen = strlen(crlf);
12556 apr_brigade_write(b2, NULL, NULL, crlf, buflen);
12557
12558 /* Output the response headers. */
12559
12560 ap_pass_brigade(f->next, b2);
12561
12562 /* Remove ourselves from filter chain so we aren't called again. */
12563
12564 ap_remove_output_filter(f);
12565
12566 /* Output the partial response content. */
12567
12568 return ap_pass_brigade(f->next, b);
12569 }
12570
12571 typedef struct cve_2013_5704_fields cve_2013_5704_fields;
12572 typedef struct cve_2013_5704_apache22 cve_2013_5704_apache22;
12573 typedef struct cve_2013_5704_apache24 cve_2013_5704_apache24;
12574
12575 struct cve_2013_5704_fields {
12576 apr_table_t *trailers_in;
12577 apr_table_t *trailers_out;
12578 };
12579
12580 struct cve_2013_5704_apache22 {
12581 struct ap_filter_t *proto_input_filters;
12582 int eos_sent;
12583 cve_2013_5704_fields fields;
12584 };
12585
12586 struct cve_2013_5704_apache24 {
12587 apr_sockaddr_t *useragent_addr;
12588 char *useragent_ip;
12589 cve_2013_5704_fields fields;
12590 };
12591
wsgi_hook_daemon_handler(conn_rec * c)12592 static int wsgi_hook_daemon_handler(conn_rec *c)
12593 {
12594 apr_socket_t *csd;
12595 request_rec *r;
12596 apr_pool_t *p;
12597 apr_status_t rv;
12598
12599 char *key;
12600 apr_sockaddr_t *addr;
12601
12602 const char *filename;
12603 const char *script;
12604 const char *magic;
12605 const char *hash;
12606
12607 WSGIRequestConfig *config;
12608
12609 apr_bucket *e;
12610 apr_bucket_brigade *bb;
12611
12612 core_request_config *req_cfg;
12613
12614 ap_filter_t *current = NULL;
12615 ap_filter_t *next = NULL;
12616
12617 const char *item;
12618
12619 int queue_timeout_occurred = 0;
12620
12621 #if ! (AP_MODULE_MAGIC_AT_LEAST(20120211, 37) || \
12622 (AP_SERVER_MAJORVERSION_NUMBER == 2 && \
12623 AP_SERVER_MINORVERSION_NUMBER <= 2 && \
12624 AP_MODULE_MAGIC_AT_LEAST(20051115, 36)))
12625 apr_size_t size = 0;
12626 #endif
12627
12628 /* Don't do anything if not in daemon process. */
12629
12630 if (!wsgi_daemon_pool)
12631 return DECLINED;
12632
12633 /*
12634 * Remove all input/output filters except the core filters.
12635 * This will ensure that any SSL filters we don't want are
12636 * removed. This is a bit of a hack. Only other option is to
12637 * duplicate the code for core input/output filters so can
12638 * avoid full Apache connection processing, which is what is
12639 * installed the SSL filters and possibly other filters for
12640 * logging etc.
12641 */
12642
12643 current = c->input_filters;
12644 next = current->next;
12645
12646 while (current) {
12647 if (current->frec == ap_core_input_filter_handle) {
12648 current = next;
12649 if (!current)
12650 break;
12651 next = current->next;
12652 continue;
12653 }
12654
12655 ap_remove_input_filter(current);
12656
12657 current = next;
12658 if (current)
12659 next = current->next;
12660 }
12661
12662 current = c->output_filters;
12663 next = current->next;
12664
12665 while (current) {
12666 if (current->frec == ap_core_output_filter_handle) {
12667 current = next;
12668 if (!current)
12669 break;
12670 next = current->next;
12671 continue;
12672 }
12673
12674 ap_remove_output_filter(current);
12675
12676 current = next;
12677 if (current)
12678 next = current->next;
12679 }
12680
12681 /*
12682 * Create and populate our own request object. We allocate more
12683 * memory than we require here for the request_rec in order to
12684 * implement an opimistic hack for the case where mod_wsgi is built
12685 * against an Apache version prior to CVE-2013-6704 being applied to
12686 * it. If that Apache is upgraded but mod_wsgi not recompiled then
12687 * it will crash in daemon mode. We therefore use the extra space to
12688 * set the structure members which are added by CVE-2013-6704 to try
12689 * and avoid that situation. Note that this is distinct from the
12690 * hack down below to deal with where mod_wsgi was compiled against
12691 * an Apache version which had CVE-2013-6704 backported.
12692 */
12693
12694 apr_pool_create(&p, c->pool);
12695
12696 r = apr_pcalloc(p, sizeof(request_rec)+sizeof(cve_2013_5704_fields));
12697
12698 r->pool = p;
12699 r->connection = c;
12700 r->server = c->base_server;
12701
12702 r->user = NULL;
12703 r->ap_auth_type = NULL;
12704
12705 r->allowed_methods = ap_make_method_list(p, 2);
12706
12707 r->headers_in = apr_table_make(r->pool, 25);
12708 r->subprocess_env = apr_table_make(r->pool, 25);
12709 r->headers_out = apr_table_make(r->pool, 12);
12710 r->err_headers_out = apr_table_make(r->pool, 5);
12711 r->notes = apr_table_make(r->pool, 5);
12712
12713 r->request_config = ap_create_request_config(r->pool);
12714
12715 r->proto_output_filters = c->output_filters;
12716 r->output_filters = r->proto_output_filters;
12717 r->proto_input_filters = c->input_filters;
12718 r->input_filters = r->proto_input_filters;
12719
12720 #if AP_MODULE_MAGIC_AT_LEAST(20120211, 37) || \
12721 (AP_SERVER_MAJORVERSION_NUMBER == 2 && \
12722 AP_SERVER_MINORVERSION_NUMBER <= 2 && \
12723 AP_MODULE_MAGIC_AT_LEAST(20051115, 36))
12724
12725 /*
12726 * New request_rec fields were added to Apache because of changes
12727 * related to CVE-2013-5704. The change means that mod_wsgi version
12728 * 4.4.0-4.4.5 will crash if run on the Apache versions with the
12729 * addition fields if mod_wsgi daemon mode is used. If we are using
12730 * Apache 2.2.29 or 2.4.11, we set the fields direct against the
12731 * new structure members.
12732 */
12733
12734 r->trailers_in = apr_table_make(r->pool, 5);
12735 r->trailers_out = apr_table_make(r->pool, 5);
12736 #else
12737 /*
12738 * We use a huge hack here to try and identify when CVE-2013-5704
12739 * has been back ported to older Apache version. This is necessary
12740 * as when backported the Apache module magic number will not be
12741 * updated and it isn't possible to determine from that at compile
12742 * time if the new structure members exist and so that they should
12743 * be set. We therefore try and work out whether the extra structure
12744 * members exist through looking at the size of request_rec and
12745 * whether memory has been allocated above what is known to be the
12746 * last member in the structure before the new members were added.
12747 */
12748
12749 #if AP_SERVER_MINORVERSION_NUMBER <= 2
12750 size = offsetof(request_rec, eos_sent);
12751 size += sizeof(r->eos_sent);
12752 #else
12753 size = offsetof(request_rec, useragent_ip);
12754 size += sizeof(r->useragent_ip);
12755 #endif
12756
12757 /*
12758 * Check whether request_rec is at least as large as minimal size
12759 * plus the size of the extra fields. If it is, then we need to
12760 * set the additional fields.
12761 */
12762
12763 if (sizeof(request_rec) >= size + sizeof(cve_2013_5704_fields)) {
12764 #if AP_SERVER_MINORVERSION_NUMBER <= 2
12765 cve_2013_5704_apache22 *rext;
12766 rext = (cve_2013_5704_apache22 *)&r->proto_input_filters;
12767 #else
12768 cve_2013_5704_apache24 *rext;
12769 rext = (cve_2013_5704_apache24 *)&r->useragent_addr;
12770 #endif
12771
12772 rext->fields.trailers_in = apr_table_make(r->pool, 5);
12773 rext->fields.trailers_out = apr_table_make(r->pool, 5);
12774 }
12775 else {
12776 /*
12777 * Finally, to allow forward portability of a compiled mod_wsgi
12778 * binary from an Apache version without the CVE-2013-5704
12779 * change to one where it is, without needing to recompile
12780 * mod_wsgi, we set fields in the extra memory we added before
12781 * the actual request_rec.
12782 */
12783
12784 cve_2013_5704_fields *rext;
12785 rext = (cve_2013_5704_fields *)(r+1);
12786
12787 rext->trailers_in = apr_table_make(r->pool, 5);
12788 rext->trailers_out = apr_table_make(r->pool, 5);
12789 }
12790 #endif
12791
12792 r->per_dir_config = r->server->lookup_defaults;
12793
12794 r->sent_bodyct = 0;
12795
12796 r->read_length = 0;
12797 r->read_body = REQUEST_NO_BODY;
12798
12799 r->status = HTTP_OK;
12800 r->status_line = NULL;
12801 r->the_request = NULL;
12802
12803 r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
12804
12805 /*
12806 * Install our own output filter for writing back headers in
12807 * CGI script style.
12808 */
12809
12810 ap_add_output_filter_handle(wsgi_header_filter_handle,
12811 NULL, r, r->connection);
12812
12813 /* Create and install the WSGI request config. */
12814
12815 config = (WSGIRequestConfig *)apr_pcalloc(r->pool,
12816 sizeof(WSGIRequestConfig));
12817 ap_set_module_config(r->request_config, &wsgi_module, (void *)config);
12818
12819 /* Grab the socket from the connection core config. */
12820
12821 csd = ap_get_module_config(c->conn_config, &core_module);
12822
12823 /*
12824 * Fake up parts of the internal per request core
12825 * configuration. If we don't do this then when Apache is
12826 * compiled with the symbol AP_DEBUG, internal checks made
12827 * by Apache will result in process crashing.
12828 */
12829
12830 req_cfg = apr_pcalloc(r->pool, sizeof(core_request_config));
12831
12832 req_cfg->bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
12833
12834 ap_set_module_config(r->request_config, &core_module, req_cfg);
12835
12836 /* Read in the request details and setup request object. */
12837
12838 if ((rv = wsgi_read_request(csd, r)) != APR_SUCCESS) {
12839 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, wsgi_server,
12840 "mod_wsgi (pid=%d): Unable to read WSGI request.",
12841 getpid());
12842
12843 apr_pool_destroy(p);
12844
12845 return HTTP_INTERNAL_SERVER_ERROR;
12846 }
12847
12848 /* Check magic marker used to validate origin of request. */
12849
12850 filename = apr_table_get(r->subprocess_env, "SCRIPT_FILENAME");
12851 script = apr_table_get(r->subprocess_env, "mod_wsgi.handler_script");
12852
12853 magic = apr_table_get(r->subprocess_env, "mod_wsgi.magic");
12854
12855 if (!magic) {
12856 ap_log_error(APLOG_MARK, APLOG_ALERT, rv, wsgi_server,
12857 "mod_wsgi (pid=%d): Request origin could not be "
12858 "validated.", getpid());
12859
12860 apr_pool_destroy(p);
12861
12862 return HTTP_INTERNAL_SERVER_ERROR;
12863 }
12864
12865 key = apr_psprintf(r->pool, "%ld|%s|%s|%s",
12866 wsgi_daemon_process->group->random,
12867 wsgi_daemon_process->group->socket_path,
12868 filename, script);
12869 hash = ap_md5(r->pool, (const unsigned char *)key);
12870 memset(key, '\0', strlen(key));
12871
12872 if (strcmp(magic, hash) != 0) {
12873 ap_log_error(APLOG_MARK, APLOG_ALERT, rv, wsgi_server,
12874 "mod_wsgi (pid=%d): Request origin could not be "
12875 "validated.", getpid());
12876
12877 apr_pool_destroy(p);
12878
12879 return HTTP_INTERNAL_SERVER_ERROR;
12880 }
12881
12882 apr_table_unset(r->subprocess_env, "mod_wsgi.magic");
12883
12884 /*
12885 * If we are executing in a chroot environment, we need to
12886 * adjust SCRIPT_FILENAME to remove leading portion of path
12887 * that corresponds to the location of the chroot directory.
12888 * Also need to adjust DOCUMENT_ROOT as well, although in
12889 * that case if it doesn't actually fall within the choot
12890 * directory, we just delete it outright as would be incorrect
12891 * if that directory lay outside of the chroot directory.
12892 */
12893
12894 if (wsgi_daemon_process->group->root) {
12895 const char *root;
12896 const char *path;
12897
12898 root = wsgi_daemon_process->group->root;
12899
12900 path = filename;
12901
12902 if (strstr(path, root) == path && path[strlen(root)] == '/') {
12903 path += strlen(root);
12904
12905 apr_table_set(r->subprocess_env, "SCRIPT_FILENAME", path);
12906
12907 filename = path;
12908 }
12909 else {
12910 ap_log_error(APLOG_MARK, APLOG_CRIT, rv, wsgi_server,
12911 "mod_wsgi (pid=%d): WSGI script '%s' not located "
12912 "within chroot directory '%s'.", getpid(), path, root);
12913
12914 return HTTP_INTERNAL_SERVER_ERROR;
12915 }
12916
12917 path = (char *)apr_table_get(r->subprocess_env, "DOCUMENT_ROOT");
12918
12919 if (strstr(path, root) == path) {
12920 path += strlen(root);
12921
12922 apr_table_set(r->subprocess_env, "DOCUMENT_ROOT", path);
12923 }
12924 else {
12925 apr_table_unset(r->subprocess_env, "DOCUMENT_ROOT");
12926 }
12927 }
12928
12929 r->filename = (char *)filename;
12930
12931 /* Recalculate WSGI script or handler script modification time. */
12932
12933 if (script && *script) {
12934 if ((rv = apr_stat(&r->finfo, script, APR_FINFO_NORM,
12935 r->pool)) != APR_SUCCESS) {
12936 /*
12937 * Don't fail at this point. Allow the lack of file to
12938 * be detected later when trying to load the script file.
12939 */
12940
12941 ap_log_error(APLOG_MARK, APLOG_WARNING, rv, wsgi_server,
12942 "mod_wsgi (pid=%d): Unable to stat target handler "
12943 "script '%s'.", getpid(), script);
12944
12945 r->finfo.mtime = 0;
12946 }
12947 }
12948 else {
12949 if ((rv = apr_stat(&r->finfo, filename, APR_FINFO_NORM,
12950 r->pool)) != APR_SUCCESS) {
12951 /*
12952 * Don't fail at this point. Allow the lack of file to
12953 * be detected later when trying to load the script file.
12954 */
12955
12956 ap_log_error(APLOG_MARK, APLOG_WARNING, rv, wsgi_server,
12957 "mod_wsgi (pid=%d): Unable to stat target WSGI "
12958 "script '%s'.", getpid(), filename);
12959
12960 r->finfo.mtime = 0;
12961 }
12962 }
12963
12964 /*
12965 * Trigger mapping of host information to server configuration
12966 * so that when logging errors they go to the correct error log
12967 * file for the host.
12968 */
12969
12970 #if AP_MODULE_MAGIC_AT_LEAST(20111130,0)
12971 r->connection->client_ip = (char *)apr_table_get(r->subprocess_env,
12972 "REMOTE_ADDR");
12973 r->connection->client_addr->port = atoi(apr_table_get(r->subprocess_env,
12974 "REMOTE_PORT"));
12975 #else
12976 r->connection->remote_ip = (char *)apr_table_get(r->subprocess_env,
12977 "REMOTE_ADDR");
12978 r->connection->remote_addr->port = atoi(apr_table_get(r->subprocess_env,
12979 "REMOTE_PORT"));
12980 #endif
12981
12982 #if AP_MODULE_MAGIC_AT_LEAST(20111130,0)
12983 r->useragent_addr = c->client_addr;
12984 r->useragent_ip = c->client_ip;
12985 #endif
12986
12987 key = apr_psprintf(p, "%s|%s",
12988 apr_table_get(r->subprocess_env,
12989 "mod_wsgi.listener_host"),
12990 apr_table_get(r->subprocess_env,
12991 "mod_wsgi.listener_port"));
12992
12993 if (wsgi_server_config->verbose_debugging) {
12994 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
12995 "mod_wsgi (pid=%d): Server listener address '%s'.",
12996 getpid(), key);
12997 }
12998
12999 addr = (apr_sockaddr_t *)apr_hash_get(wsgi_daemon_listeners,
13000 key, APR_HASH_KEY_STRING);
13001
13002 if (wsgi_server_config->verbose_debugging) {
13003 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
13004 "mod_wsgi (pid=%d): Server listener address '%s' was"
13005 "%s found.", getpid(), key, addr ? "" : " not");
13006 }
13007
13008 if (addr) {
13009 c->local_addr = addr;
13010 }
13011
13012 ap_update_vhost_given_ip(r->connection);
13013
13014 if (wsgi_server_config->verbose_debugging) {
13015 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
13016 "mod_wsgi (pid=%d): Connection server matched was "
13017 "'%s|%d'.", getpid(), c->base_server->server_hostname,
13018 c->base_server->port);
13019 }
13020
13021 r->server = c->base_server;
13022
13023 if (apr_table_get(r->subprocess_env, "HTTP_HOST")) {
13024 apr_table_setn(r->headers_in, "Host",
13025 apr_table_get(r->subprocess_env, "HTTP_HOST"));
13026 }
13027
13028 ap_update_vhost_from_headers(r);
13029
13030 if (wsgi_server_config->verbose_debugging) {
13031 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, wsgi_server,
13032 "mod_wsgi (pid=%d): Request server matched was '%s|%d'.",
13033 getpid(), r->server->server_hostname, r->server->port);
13034 }
13035
13036 /*
13037 * Set content length of any request content and add the
13038 * standard HTTP input filter so that standard input routines
13039 * for request content will work.
13040 */
13041
13042 item = apr_table_get(r->subprocess_env, "CONTENT_LENGTH");
13043
13044 if (item)
13045 apr_table_setn(r->headers_in, "Content-Length", item);
13046
13047 /* Set details of WSGI specific request config. */
13048
13049 config->process_group = apr_table_get(r->subprocess_env,
13050 "mod_wsgi.process_group");
13051 config->application_group = apr_table_get(r->subprocess_env,
13052 "mod_wsgi.application_group");
13053 config->callable_object = apr_table_get(r->subprocess_env,
13054 "mod_wsgi.callable_object");
13055
13056 config->handler_script = apr_table_get(r->subprocess_env,
13057 "mod_wsgi.handler_script");
13058
13059 config->script_reloading = atoi(apr_table_get(r->subprocess_env,
13060 "mod_wsgi.script_reloading"));
13061
13062 item = apr_table_get(r->subprocess_env, "mod_wsgi.enable_sendfile");
13063
13064 if (item && !strcasecmp(item, "1"))
13065 config->enable_sendfile = 1;
13066 else
13067 config->enable_sendfile = 0;
13068
13069 item = apr_table_get(r->subprocess_env, "mod_wsgi.ignore_activity");
13070
13071 if (item && !strcasecmp(item, "1"))
13072 config->ignore_activity = 1;
13073 else
13074 config->ignore_activity = 0;
13075
13076 config->daemon_connects = atoi(apr_table_get(r->subprocess_env,
13077 "mod_wsgi.daemon_connects"));
13078 config->daemon_restarts = atoi(apr_table_get(r->subprocess_env,
13079 "mod_wsgi.daemon_restarts"));
13080
13081 item = apr_table_get(r->subprocess_env, "mod_wsgi.request_start");
13082
13083 if (item) {
13084 errno = 0;
13085 config->request_start = apr_strtoi64(item, (char **)&item, 10);
13086
13087 if (!*item && errno != ERANGE)
13088 r->request_time = config->request_start;
13089 else
13090 config->request_start = 0.0;
13091 }
13092
13093 item = apr_table_get(r->subprocess_env, "mod_wsgi.queue_start");
13094
13095 if (item) {
13096 errno = 0;
13097 config->queue_start = apr_strtoi64(item, (char **)&item, 10);
13098
13099 if (!(!*item && errno != ERANGE))
13100 config->queue_start = 0.0;
13101 }
13102
13103 config->daemon_start = apr_time_now();
13104
13105 apr_table_setn(r->subprocess_env, "mod_wsgi.daemon_start",
13106 apr_psprintf(r->pool, "%" APR_TIME_T_FMT,
13107 config->daemon_start));
13108
13109 #if AP_MODULE_MAGIC_AT_LEAST(20100923,2)
13110 item = apr_table_get(r->subprocess_env, "mod_wsgi.request_id");
13111
13112 if (item)
13113 r->log_id = item;
13114
13115 item = apr_table_get(r->subprocess_env, "mod_wsgi.connection_id");
13116
13117 if (item)
13118 r->connection->log_id = item;
13119 #endif
13120
13121 /*
13122 * Install the standard HTTP input filter and set header for
13123 * chunked transfer encoding to force it to dechunk the input.
13124 * This is necessary as we chunk the data that is proxied to
13125 * the daemon processes so that we can determining whether we
13126 * actually receive all input or it was truncated.
13127 *
13128 * Note that the subprocess_env table that gets passed to the
13129 * WSGI environ dictionary has already been populated, so the
13130 * Transfer-Encoding header will not be passed in the WSGI
13131 * environ dictionary as a result of this.
13132 */
13133
13134 apr_table_setn(r->headers_in, "Transfer-Encoding", "chunked");
13135
13136 ap_add_input_filter("HTTP_IN", NULL, r, r->connection);
13137
13138 /* Check for queue timeout. */
13139
13140 r->status = HTTP_OK;
13141
13142 if (wsgi_daemon_process->group->queue_timeout) {
13143 if (config->request_start) {
13144 apr_time_t queue_time = 0;
13145
13146 queue_time = config->daemon_start - config->request_start;
13147
13148 if (queue_time > wsgi_daemon_process->group->queue_timeout) {
13149 queue_timeout_occurred = 1;
13150
13151 r->status = HTTP_INTERNAL_SERVER_ERROR;
13152 r->status_line = "200 Timeout";
13153
13154 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
13155 "mod_wsgi (pid=%d): Queue timeout expired "
13156 "for WSGI daemon process '%s'.", getpid(),
13157 wsgi_daemon_process->group->name);
13158 }
13159 }
13160 }
13161
13162 /*
13163 * Execute the actual target WSGI application. In
13164 * normal cases OK should always be returned. If
13165 * however an error occurs in importing or executing
13166 * the script or the Python code raises an exception
13167 * which is not caught and handled, then an internal
13168 * server error can be returned. As we don't want to
13169 * be triggering any error document handlers in the
13170 * daemon process we use a fake status line with 0
13171 * as the status value. This will be picked up in
13172 * the Apache child process which will translate it
13173 * back to a 500 error so that normal error document
13174 * processing occurs.
13175 */
13176
13177 if (!queue_timeout_occurred) {
13178 if (wsgi_execute_script(r) != OK) {
13179 r->status = HTTP_INTERNAL_SERVER_ERROR;
13180 r->status_line = "200 Error";
13181 }
13182 }
13183
13184 /*
13185 * Ensure that request is finalised and any response
13186 * is flushed out. This will as a side effect read
13187 * any input data which wasn't consumed, thus
13188 * ensuring that the Apache child process isn't hung
13189 * waiting to send the request content and can
13190 * therefore process the response correctly.
13191 */
13192
13193 ap_finalize_request_protocol(r);
13194
13195 bb = apr_brigade_create(r->pool, c->bucket_alloc);
13196 e = apr_bucket_flush_create(c->bucket_alloc);
13197 APR_BRIGADE_INSERT_HEAD(bb, e);
13198 ap_pass_brigade(r->connection->output_filters, bb);
13199
13200 apr_pool_destroy(p);
13201
13202 return OK;
13203 }
13204
13205 #endif
13206
13207 /*
13208 * Apache 2.X module initialisation functions.
13209 */
13210
wsgi_hook_init(apr_pool_t * pconf,apr_pool_t * ptemp,apr_pool_t * plog,server_rec * s)13211 static int wsgi_hook_init(apr_pool_t *pconf, apr_pool_t *ptemp,
13212 apr_pool_t *plog, server_rec *s)
13213 {
13214 void *data = NULL;
13215 const char *userdata_key;
13216 char package[128];
13217 char interpreter[256];
13218
13219 int status = OK;
13220
13221 /*
13222 * No longer support using mod_python at the same time as
13223 * mod_wsgi as becoming too painful to hack around
13224 * mod_python's broken usage of threading APIs when align
13225 * code to the stricter API requirements of Python 3.2.
13226 */
13227
13228 userdata_key = "python_init";
13229
13230 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
13231 if (data) {
13232 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL,
13233 "mod_wsgi (pid=%d): The mod_python module can "
13234 "not be used in conjunction with mod_wsgi 4.0+. "
13235 "Remove the mod_python module from the Apache "
13236 "configuration.", getpid());
13237
13238 return HTTP_INTERNAL_SERVER_ERROR;
13239 }
13240
13241 /*
13242 * Init function gets called twice during startup, we only
13243 * need to actually do anything on the second time it is
13244 * called. This avoids unecessarily initialising and then
13245 * destroying Python for no reason. We also though have to
13246 * deal with a special case when a graceful restart is done.
13247 * For that we are only called once, which is generally okay
13248 * as the 'wsgi_init' key will be set from initial start up
13249 * of the server. The exception to this is where the module
13250 * is only loaded into Apache when the server is already
13251 * running. In this case we have to detect that it is not
13252 * the initial startup, but a subsequent restart. We can do
13253 * this by looking at whether the scoreboard has been
13254 * initialised yet. That is probably enough, but to be safe,
13255 * also check what generation it is.
13256 */
13257
13258 userdata_key = "wsgi_init";
13259
13260 apr_pool_userdata_get(&data, userdata_key, s->process->pool);
13261
13262 if (!data) {
13263 apr_pool_userdata_set((const void *)1, userdata_key,
13264 apr_pool_cleanup_null, s->process->pool);
13265
13266 /*
13267 * Check for the special case of a graceful restart and
13268 * the module being loaded for the first time. In this
13269 * case we still go onto perform initialisation as the
13270 * initialisation routine for the module will not be
13271 * called a second time.
13272 */
13273
13274 if (!ap_scoreboard_image ||
13275 ap_get_scoreboard_global()->running_generation == 0) {
13276
13277 return OK;
13278 }
13279 }
13280
13281 /* Setup module version information. */
13282
13283 sprintf(package, "mod_wsgi/%s", MOD_WSGI_VERSION_STRING);
13284
13285 ap_add_version_component(pconf, package);
13286
13287 /* Record Python version string with Apache. */
13288
13289 sprintf(interpreter, "Python/%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION);
13290 ap_add_version_component(pconf, interpreter);
13291
13292 /* Retain reference to base server. */
13293
13294 wsgi_server = s;
13295
13296 /* Retain record of parent process ID. */
13297
13298 wsgi_parent_pid = getpid();
13299
13300 /* Determine whether multiprocess and/or multithread. */
13301
13302 ap_mpm_query(AP_MPMQ_IS_THREADED, &wsgi_multithread);
13303 wsgi_multithread = (wsgi_multithread != AP_MPMQ_NOT_SUPPORTED);
13304
13305 ap_mpm_query(AP_MPMQ_IS_FORKED, &wsgi_multiprocess);
13306 if (wsgi_multiprocess != AP_MPMQ_NOT_SUPPORTED) {
13307 ap_mpm_query(AP_MPMQ_MAX_DAEMONS, &wsgi_multiprocess);
13308 wsgi_multiprocess = (wsgi_multiprocess != 1);
13309 }
13310
13311 /* Retain reference to main server config. */
13312
13313 wsgi_server_config = ap_get_module_config(s->module_config, &wsgi_module);
13314
13315 /*
13316 * Check that the version of Python found at
13317 * runtime is what was used at compilation.
13318 *
13319 * XXX Can't do this as will cause Anaconda
13320 * Python to fail as not safe to call the
13321 * Py_GetVersion() function before one calls
13322 * the Py_Initialize() function when using
13323 * Anaconda Python.
13324 */
13325
13326 #if 0
13327 wsgi_python_version();
13328 #endif
13329
13330 /*
13331 * Initialise Python if required to be done in
13332 * the parent process. Note that it will not be
13333 * initialised if mod_python loaded and it has
13334 * already been done.
13335 */
13336
13337 if (wsgi_python_required == -1)
13338 wsgi_python_required = 1;
13339
13340 if (!wsgi_python_after_fork)
13341 wsgi_python_init(pconf);
13342
13343 /*
13344 * Startup separate named daemon processes. This is
13345 * a bit tricky as we only want to do this after the
13346 * scoreboard has been created. On the initial server
13347 * startup though, this hook function is called prior
13348 * to the MPM being run, which means the scoreboard
13349 * hasn't been created yet. In that case we need to
13350 * defer process creation until after that, which we
13351 * can only do by hooking into the pre_mpm hook after
13352 * scoreboard creation has been done. On a server
13353 * restart, the scoreboard will be preserved, so we
13354 * can do it here, which is just as well as the pre_mpm
13355 * hook isn't run on a restart.
13356 */
13357
13358 #if defined(MOD_WSGI_WITH_DAEMONS)
13359 if (!ap_scoreboard_image) {
13360 /*
13361 * Need to remember the pool we were given here as
13362 * the pre_mpm hook functions get given a different
13363 * pool which isn't the one we want and if we use
13364 * that then Apache will crash when it is being
13365 * shutdown. So our pre_mpm hook will use the pool
13366 * we have remembered here.
13367 */
13368
13369 wsgi_pconf_pool = pconf;
13370
13371 ap_hook_pre_mpm(wsgi_deferred_start_daemons, NULL, NULL,
13372 APR_HOOK_REALLY_LAST);
13373 }
13374 else
13375 status = wsgi_start_daemons(pconf);
13376 #endif
13377
13378 return status;
13379 }
13380
wsgi_hook_child_init(apr_pool_t * p,server_rec * s)13381 static void wsgi_hook_child_init(apr_pool_t *p, server_rec *s)
13382 {
13383 #if defined(MOD_WSGI_WITH_DAEMONS)
13384 WSGIProcessGroup *entries = NULL;
13385 WSGIProcessGroup *entry = NULL;
13386
13387 int i;
13388
13389 /* Close listener sockets for daemon processes. */
13390
13391 if (wsgi_daemon_list) {
13392 entries = (WSGIProcessGroup *)wsgi_daemon_list->elts;
13393
13394 for (i = 0; i < wsgi_daemon_list->nelts; ++i) {
13395 entry = &entries[i];
13396
13397 if (entry->listener_fd != -1) {
13398 close(entry->listener_fd);
13399 entry->listener_fd = -1;
13400 }
13401 }
13402 }
13403 #endif
13404
13405 /* Remember worker process ID. */
13406
13407 wsgi_worker_pid = getpid();
13408
13409 /* Time child process started waiting for requests. */
13410
13411 wsgi_restart_time = apr_time_now();
13412
13413 /* Create lock for request monitoring. */
13414
13415 apr_thread_mutex_create(&wsgi_monitor_lock,
13416 APR_THREAD_MUTEX_UNNESTED, p);
13417
13418 if (wsgi_python_required) {
13419 /*
13420 * Initialise Python if required to be done in
13421 * the child process. Note that it will not be
13422 * initialised if mod_python loaded and it has
13423 * already been done.
13424 */
13425
13426 if (wsgi_python_after_fork)
13427 wsgi_python_init(p);
13428
13429 /*
13430 * Now perform additional initialisation steps
13431 * always done in child process.
13432 */
13433
13434 wsgi_python_child_init(p);
13435 }
13436 }
13437
13438 #include "apr_lib.h"
13439
wsgi_original_uri(request_rec * r)13440 static char *wsgi_original_uri(request_rec *r)
13441 {
13442 char *first, *last;
13443
13444 if (r->the_request == NULL) {
13445 return (char *) apr_pcalloc(r->pool, 1);
13446 }
13447
13448 first = r->the_request; /* use the request-line */
13449
13450 while (*first && !apr_isspace(*first)) {
13451 ++first; /* skip over the method */
13452 }
13453 while (apr_isspace(*first)) {
13454 ++first; /* and the space(s) */
13455 }
13456
13457 last = first;
13458 while (*last && !apr_isspace(*last)) {
13459 ++last; /* end at next whitespace */
13460 }
13461
13462 return apr_pstrmemdup(r->pool, first, last - first);
13463 }
13464
wsgi_http_invalid_header(const char * w)13465 static int wsgi_http_invalid_header(const char *w)
13466 {
13467 char c;
13468
13469 while ((c = *w++) != 0) {
13470 if (!apr_isalnum(c) && c != '-')
13471 return 1;
13472 }
13473
13474 return 0;
13475 }
13476
wsgi_drop_invalid_headers(request_rec * r)13477 static void wsgi_drop_invalid_headers(request_rec *r)
13478 {
13479 /*
13480 * Apache 2.2 when converting headers for CGI variables, doesn't
13481 * ignore headers with invalid names. That is, any which use any
13482 * characters besides alphanumerics and the '-' character. This
13483 * opens us up to header spoofing whereby something can inject
13484 * multiple headers which differ by using non alphanumeric
13485 * characters in the same position, which would then encode to same
13486 * value. Since not easy to cleanup after the fact, as a workaround,
13487 * is easier to simply remove the invalid headers. This will make
13488 * things end up being the same as Apache 2.4. Doing this could
13489 * annoy some users of Apache 2.2 who were using invalid headers,
13490 * but things will break for them under Apache 2.4 anyway.
13491 */
13492
13493 apr_array_header_t *to_delete = NULL;
13494
13495 const apr_array_header_t *hdrs_arr;
13496 const apr_table_entry_t *hdrs;
13497
13498 int i;
13499
13500 hdrs_arr = apr_table_elts(r->headers_in);
13501 hdrs = (const apr_table_entry_t *) hdrs_arr->elts;
13502
13503 for (i = 0; i < hdrs_arr->nelts; ++i) {
13504 if (!hdrs[i].key) {
13505 continue;
13506 }
13507
13508 if (wsgi_http_invalid_header(hdrs[i].key)) {
13509 char **new;
13510
13511 if (!to_delete)
13512 to_delete = apr_array_make(r->pool, 1, sizeof(char *));
13513
13514 new = (char **)apr_array_push(to_delete);
13515 *new = hdrs[i].key;
13516 }
13517 }
13518
13519 if (to_delete) {
13520 char *key;
13521
13522 for (i = 0; i < to_delete->nelts; i++) {
13523 key = ((char **)to_delete->elts)[i];
13524
13525 apr_table_unset(r->headers_in, key);
13526 }
13527 }
13528 }
13529
13530 static const char *wsgi_proxy_client_headers[] = {
13531 "HTTP_X_FORWARDED_FOR",
13532 "HTTP_X_CLIENT_IP",
13533 "HTTP_X_REAL_IP",
13534 NULL,
13535 };
13536
13537 static const char *wsgi_proxy_scheme_headers[] = {
13538 "HTTP_X_FORWARDED_HTTPS",
13539 "HTTP_X_FORWARDED_PROTO",
13540 "HTTP_X_FORWARDED_SCHEME",
13541 "HTTP_X_FORWARDED_SSL",
13542 "HTTP_X_HTTPS",
13543 "HTTP_X_SCHEME",
13544 NULL,
13545 };
13546
13547 static const char *wsgi_proxy_host_headers[] = {
13548 "HTTP_X_FORWARDED_HOST",
13549 "HTTP_X_HOST",
13550 NULL,
13551 };
13552
13553 static const char *wsgi_proxy_script_name_headers[] = {
13554 "HTTP_X_SCRIPT_NAME",
13555 "HTTP_X_FORWARDED_SCRIPT_NAME",
13556 NULL,
13557 };
13558
wsgi_ip_is_in_array(apr_sockaddr_t * client_ip,apr_array_header_t * proxy_ips)13559 static int wsgi_ip_is_in_array(apr_sockaddr_t *client_ip,
13560 apr_array_header_t *proxy_ips) {
13561 int i;
13562 apr_ipsubnet_t **subs = (apr_ipsubnet_t **)proxy_ips->elts;
13563
13564 for (i = 0; i < proxy_ips->nelts; i++) {
13565 if (apr_ipsubnet_test(subs[i], client_ip)) {
13566 return 1;
13567 }
13568 }
13569
13570 return 0;
13571 }
13572
wsgi_process_forwarded_for(request_rec * r,WSGIRequestConfig * config,const char * value)13573 static void wsgi_process_forwarded_for(request_rec *r,
13574 WSGIRequestConfig *config,
13575 const char *value
13576 )
13577 {
13578 if (config->trusted_proxies) {
13579 /*
13580 * A potentially comma separated list where client we are
13581 * interested in will be that immediately before the last
13582 * trusted proxy working from the end forwards. If there
13583 * are no trusted proxies then we use the last.
13584 */
13585
13586 apr_array_header_t *arr;
13587
13588 arr = apr_array_make(r->pool, 3, sizeof(char *));
13589
13590 while (*value != '\0') {
13591 /* Skip leading whitespace for item. */
13592
13593 while (*value != '\0' && apr_isspace(*value))
13594 value++;
13595
13596 if (*value != '\0') {
13597 const char *end = NULL;
13598 const char *next = NULL;
13599
13600 char **entry = NULL;
13601
13602 end = value;
13603
13604 while (*end != '\0' && *end != ',')
13605 end++;
13606
13607 if (*end == '\0')
13608 next = end;
13609 else if (*end == ',')
13610 next = end+1;
13611
13612 /* Need deal with trailing whitespace. */
13613
13614 while (end != value) {
13615 if (!apr_isspace(*(end-1)))
13616 break;
13617
13618 end--;
13619 }
13620
13621 entry = (char **)apr_array_push(arr);
13622 *entry = apr_pstrndup(r->pool, value, (end-value));
13623
13624 value = next;
13625 }
13626 }
13627
13628 if (arr->nelts != 0) {
13629 /* HTTP_X_FORDWARDED_FOR wasn't just an empty string. */
13630
13631 char **items;
13632 int first = -1;
13633 int i;
13634
13635 items = (char **)arr->elts;
13636
13637 /*
13638 * Work out the position of the IP closest to the start
13639 * that we actually trusted.
13640 */
13641
13642 for (i=arr->nelts; i>0; ) {
13643 apr_sockaddr_t *sa;
13644 apr_status_t rv;
13645
13646 i--;
13647
13648 rv = apr_sockaddr_info_get(&sa, items[i], APR_UNSPEC,
13649 0, 0, r->pool);
13650
13651 if (rv == APR_SUCCESS) {
13652 if (!wsgi_ip_is_in_array(sa, config->trusted_proxies))
13653 break;
13654
13655 first = i;
13656 }
13657 else {
13658 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, r,
13659 "mod_wsgi (pid=%d): Forwarded IP of \"%s\" is "
13660 "not a valid IP address.", getpid(), items[i]);
13661 break;
13662 }
13663 }
13664
13665 if (first >= 0) {
13666 /*
13667 * We found at least one trusted IP. We use the
13668 * IP that may have appeared before that as
13669 * REMOTE_ADDR. We rewrite HTTP_X_FORWARDED_FOR
13670 * to record only from REMOTE_ADDR onwards.
13671 */
13672
13673 char *list;
13674
13675 i = first-1;
13676 if (i<0)
13677 i = 0;
13678
13679 apr_table_setn(r->subprocess_env, "REMOTE_ADDR", items[i]);
13680
13681 list = items[i];
13682
13683 i++;
13684
13685 while (arr->nelts != i) {
13686 list = apr_pstrcat(r->pool, list, ", ", items[i], NULL);
13687 i++;
13688 }
13689
13690 apr_table_setn(r->subprocess_env, "HTTP_X_FORWARDED_FOR",
13691 list);
13692 }
13693 else {
13694 /*
13695 * No trusted IP. Use the last for REMOTE_ADDR.
13696 * We rewrite HTTP_X_FORWARDED_FOR to record only
13697 * the last.
13698 */
13699
13700 apr_table_setn(r->subprocess_env, "REMOTE_ADDR",
13701 items[arr->nelts-1]);
13702 apr_table_setn(r->subprocess_env, "HTTP_X_FORWARDED_FOR",
13703 items[arr->nelts-1]);
13704 }
13705 }
13706 }
13707 else {
13708 /*
13709 * We do not need to validate the proxies. We will have a
13710 * potentially comma separated list where the client we
13711 * are interested in will be listed first.
13712 */
13713
13714 const char *end = NULL;
13715
13716 /* Skip leading whitespace for item. */
13717
13718 while (*value != '\0' && apr_isspace(*value))
13719 value++;
13720
13721 if (*value != '\0') {
13722 end = value;
13723
13724 while (*end != '\0' && *end != ',')
13725 end++;
13726
13727 /* Need deal with trailing whitespace. */
13728
13729 while (end != value) {
13730 if (!apr_isspace(*(end-1)))
13731 break;
13732
13733 end--;
13734 }
13735
13736 /* Override REMOTE_ADDR. Leave HTTP_X_FORWARDED_FOR. */
13737
13738 apr_table_setn(r->subprocess_env, "REMOTE_ADDR",
13739 apr_pstrndup(r->pool, value, (end-value)));
13740 }
13741 }
13742 }
13743
wsgi_process_proxy_headers(request_rec * r)13744 static void wsgi_process_proxy_headers(request_rec *r)
13745 {
13746 WSGIRequestConfig *config = NULL;
13747
13748 apr_array_header_t *trusted_proxy_headers = NULL;
13749
13750 int match_client_header = 0;
13751 int match_host_header = 0;
13752 int match_script_name_header = 0;
13753 int match_scheme_header = 0;
13754
13755 const char *trusted_client_header = NULL;
13756 const char *trusted_host_header = NULL;
13757 const char *trusted_script_name_header = NULL;
13758 const char *trusted_scheme_header = NULL;
13759
13760 int i = 0;
13761
13762 int trusted_proxy = 1;
13763
13764 const char *client_ip = NULL;
13765
13766 apr_status_t rv;
13767
13768 config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
13769 &wsgi_module);
13770
13771 trusted_proxy_headers = config->trusted_proxy_headers;
13772
13773 /* Nothing to do if no trusted headers have been specified. */
13774
13775 if (!trusted_proxy_headers)
13776 return;
13777
13778 /*
13779 * Check for any special processing required for each trusted
13780 * header which has been specified. We should only do this if
13781 * there was no list of trusted proxies, or if the client IP
13782 * was that of a trusted proxy.
13783 */
13784
13785 if (config->trusted_proxies) {
13786 client_ip = apr_table_get(r->subprocess_env, "REMOTE_ADDR");
13787
13788 if (client_ip) {
13789 apr_sockaddr_t *sa;
13790
13791 rv = apr_sockaddr_info_get(&sa, client_ip, APR_UNSPEC,
13792 0, 0, r->pool);
13793
13794 if (rv == APR_SUCCESS) {
13795 if (!wsgi_ip_is_in_array(sa, config->trusted_proxies))
13796 trusted_proxy = 0;
13797 }
13798 else {
13799 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
13800 "mod_wsgi (pid=%d): REMOTE_ADDR of \"%s\" is "
13801 "not a valid IP address.", getpid(), client_ip);
13802
13803 trusted_proxy = 0;
13804 }
13805 }
13806 else
13807 trusted_proxy = 0;
13808 }
13809
13810 if (trusted_proxy) {
13811 for (i=0; i<trusted_proxy_headers->nelts; i++) {
13812 const char *name;
13813 const char *value;
13814
13815 name = ((const char**)trusted_proxy_headers->elts)[i];
13816 value = apr_table_get(r->subprocess_env, name);
13817
13818 if (!strcmp(name, "HTTP_X_FORWARDED_FOR")) {
13819 match_client_header = 1;
13820
13821 if (value) {
13822 wsgi_process_forwarded_for(r, config, value);
13823
13824 trusted_client_header = name;
13825 }
13826 }
13827 else if (!strcmp(name, "HTTP_X_CLIENT_IP") ||
13828 !strcmp(name, "HTTP_X_REAL_IP")) {
13829
13830 match_client_header = 1;
13831
13832 if (value) {
13833 /* Use the value as is. */
13834
13835 apr_table_setn(r->subprocess_env, "REMOTE_ADDR", value);
13836
13837 trusted_client_header = name;
13838 }
13839 }
13840 else if (!strcmp(name, "HTTP_X_FORWARDED_HOST") ||
13841 !strcmp(name, "HTTP_X_HOST")) {
13842
13843 match_host_header = 1;
13844
13845 if (value) {
13846 /* Use the value as is. May include a port. */
13847
13848 trusted_host_header = name;
13849
13850 apr_table_setn(r->subprocess_env, "HTTP_HOST", value);
13851 }
13852 }
13853 else if (!strcmp(name, "HTTP_X_FORWARDED_SERVER")) {
13854 if (value) {
13855 /* Use the value as is. */
13856
13857 apr_table_setn(r->subprocess_env, "SERVER_NAME", value);
13858 }
13859 }
13860 else if (!strcmp(name, "HTTP_X_FORWARDED_PORT")) {
13861 if (value) {
13862 /* Use the value as is. */
13863
13864 apr_table_setn(r->subprocess_env, "SERVER_PORT", value);
13865 }
13866 }
13867 else if (!strcmp(name, "HTTP_X_SCRIPT_NAME") ||
13868 !strcmp(name, "HTTP_X_FORWARDED_SCRIPT_NAME")) {
13869
13870 match_script_name_header = 1;
13871
13872 if (value) {
13873 /*
13874 * Use the value as is. We want to remember what the
13875 * original value for SCRIPT_NAME was though.
13876 */
13877
13878 apr_table_setn(r->subprocess_env, "mod_wsgi.mount_point",
13879 value);
13880
13881 trusted_script_name_header = name;
13882
13883 apr_table_setn(r->subprocess_env, "SCRIPT_NAME", value);
13884 }
13885 }
13886 else if (!strcmp(name, "HTTP_X_FORWARDED_PROTO") ||
13887 !strcmp(name, "HTTP_X_FORWARDED_SCHEME") ||
13888 !strcmp(name, "HTTP_X_SCHEME")) {
13889
13890 match_scheme_header = 1;
13891
13892 if (value) {
13893 trusted_scheme_header = name;
13894
13895 /* Value can be either 'http' or 'https'. */
13896
13897 if (!strcasecmp(value, "https"))
13898 apr_table_setn(r->subprocess_env, "HTTPS", "1");
13899 else if (!strcasecmp(value, "http"))
13900 apr_table_unset(r->subprocess_env, "HTTPS");
13901 }
13902 }
13903 else if (!strcmp(name, "HTTP_X_FORWARDED_HTTPS") ||
13904 !strcmp(name, "HTTP_X_FORWARDED_SSL") ||
13905 !strcmp(name, "HTTP_X_HTTPS")) {
13906
13907 match_scheme_header = 1;
13908
13909 if (value) {
13910 trusted_scheme_header = name;
13911
13912 /*
13913 * Value can be a boolean like flag such as 'On',
13914 * 'Off', 'true', 'false', '1' or '0'.
13915 */
13916
13917 if (!strcasecmp(value, "On") ||
13918 !strcasecmp(value, "true") ||
13919 !strcasecmp(value, "1")) {
13920
13921 apr_table_setn(r->subprocess_env, "HTTPS", "1");
13922 }
13923 else if (!strcasecmp(value, "Off") ||
13924 !strcasecmp(value, "false") ||
13925 !strcasecmp(value, "0")) {
13926
13927 apr_table_unset(r->subprocess_env, "HTTPS");
13928 }
13929 }
13930 }
13931 }
13932 }
13933 else {
13934 /*
13935 * If it isn't a trusted proxy, we still need to knock
13936 * out any headers for categories we were interested in.
13937 */
13938
13939 for (i=0; i<trusted_proxy_headers->nelts; i++) {
13940 const char *name;
13941
13942 name = ((const char**)trusted_proxy_headers->elts)[i];
13943
13944 if (!strcmp(name, "HTTP_X_FORWARDED_FOR") ||
13945 !strcmp(name, "HTTP_X_REAL_IP")) {
13946
13947 match_client_header = 1;
13948 }
13949 else if (!strcmp(name, "HTTP_X_FORWARDED_HOST") ||
13950 !strcmp(name, "HTTP_X_HOST")) {
13951
13952 match_host_header = 1;
13953 }
13954 else if (!strcmp(name, "HTTP_X_SCRIPT_NAME") ||
13955 !strcmp(name, "HTTP_X_FORWARDED_SCRIPT_NAME")) {
13956
13957 match_script_name_header = 1;
13958 }
13959 else if (!strcmp(name, "HTTP_X_FORWARDED_PROTO") ||
13960 !strcmp(name, "HTTP_X_FORWARDED_SCHEME") ||
13961 !strcmp(name, "HTTP_X_SCHEME") ||
13962 !strcmp(name, "HTTP_X_FORWARDED_HTTPS") ||
13963 !strcmp(name, "HTTP_X_FORWARDED_SSL") ||
13964 !strcmp(name, "HTTP_X_HTTPS")) {
13965
13966 match_scheme_header = 1;
13967 }
13968 }
13969 }
13970
13971 /*
13972 * Remove all client IP headers from request environment which
13973 * weren't matched as being trusted.
13974 */
13975
13976 if (match_client_header) {
13977 const char *name = NULL;
13978
13979 for (i=0; (name=wsgi_proxy_client_headers[i]); i++) {
13980 if (!trusted_client_header || strcmp(name, trusted_client_header)) {
13981 apr_table_unset(r->subprocess_env, name);
13982 }
13983 }
13984 }
13985
13986 /*
13987 * Remove all proxy scheme headers from request environment
13988 * which weren't matched as being trusted.
13989 */
13990
13991 if (match_scheme_header) {
13992 const char *name = NULL;
13993
13994 for (i=0; (name=wsgi_proxy_scheme_headers[i]); i++) {
13995 if (!trusted_scheme_header || strcmp(name, trusted_scheme_header)) {
13996 apr_table_unset(r->subprocess_env, name);
13997 }
13998 }
13999 }
14000
14001 /*
14002 * Remove all proxy host from request environment which weren't
14003 * matched as being trusted.
14004 */
14005
14006 if (match_host_header) {
14007 const char *name = NULL;
14008
14009 for (i=0; (name=wsgi_proxy_host_headers[i]); i++) {
14010 if (!trusted_host_header || strcmp(name, trusted_host_header))
14011 apr_table_unset(r->subprocess_env, name);
14012 }
14013 }
14014
14015 /*
14016 * Remove all proxy script name headers from request environment
14017 * which weren't matched as being trusted.
14018 */
14019
14020 if (match_script_name_header) {
14021 const char *name = NULL;
14022
14023 for (i=0; (name=wsgi_proxy_script_name_headers[i]); i++) {
14024 if (!trusted_script_name_header ||
14025 strcmp(name, trusted_script_name_header)) {
14026 apr_table_unset(r->subprocess_env, name);
14027 }
14028 }
14029 }
14030 }
14031
wsgi_http2env(apr_pool_t * a,const char * w)14032 static char *wsgi_http2env(apr_pool_t *a, const char *w)
14033 {
14034 char *res = (char *)apr_palloc(a, sizeof("HTTP_") + strlen(w));
14035 char *cp = res;
14036 char c;
14037
14038 *cp++ = 'H';
14039 *cp++ = 'T';
14040 *cp++ = 'T';
14041 *cp++ = 'P';
14042 *cp++ = '_';
14043
14044 while ((c = *w++) != 0) {
14045 if (apr_isalnum(c)) {
14046 *cp++ = apr_toupper(c);
14047 }
14048 else if (c == '-') {
14049 *cp++ = '_';
14050 }
14051 else
14052 return NULL;
14053 }
14054 *cp = 0;
14055
14056 return res;
14057 }
14058
14059 typedef struct {
14060 PyObject_HEAD
14061 request_rec *r;
14062 WSGIRequestConfig *config;
14063 PyObject *log;
14064 } AuthObject;
14065
newAuthObject(request_rec * r,WSGIRequestConfig * config)14066 static AuthObject *newAuthObject(request_rec *r, WSGIRequestConfig *config)
14067 {
14068 AuthObject *self;
14069
14070 self = PyObject_New(AuthObject, &Auth_Type);
14071 if (self == NULL)
14072 return NULL;
14073
14074 self->config = config;
14075
14076 self->r = r;
14077
14078 self->log = newLogObject(r, APLOG_ERR, NULL, 0);
14079
14080 return self;
14081 }
14082
Auth_dealloc(AuthObject * self)14083 static void Auth_dealloc(AuthObject *self)
14084 {
14085 Py_DECREF(self->log);
14086
14087 PyObject_Del(self);
14088 }
14089
Auth_environ(AuthObject * self,const char * group)14090 static PyObject *Auth_environ(AuthObject *self, const char *group)
14091 {
14092 PyObject *vars;
14093 PyObject *object;
14094
14095 request_rec *r = self->r;
14096 server_rec *s = r->server;
14097 conn_rec *c = r->connection;
14098 apr_port_t rport;
14099
14100 const apr_array_header_t *hdrs_arr;
14101 const apr_table_entry_t *hdrs;
14102
14103 const char *value = NULL;
14104
14105 int i;
14106
14107 vars = PyDict_New();
14108
14109 hdrs_arr = apr_table_elts(r->headers_in);
14110 hdrs = (const apr_table_entry_t *) hdrs_arr->elts;
14111
14112 for (i = 0; i < hdrs_arr->nelts; ++i) {
14113 if (!hdrs[i].key) {
14114 continue;
14115 }
14116
14117 if (!strcasecmp(hdrs[i].key, "Content-type")) {
14118 #if PY_MAJOR_VERSION >= 3
14119 object = PyUnicode_DecodeLatin1(hdrs[i].val,
14120 strlen(hdrs[i].val), NULL);
14121 #else
14122 object = PyString_FromString(hdrs[i].val);
14123 #endif
14124 PyDict_SetItemString(vars, "CONTENT_TYPE", object);
14125 Py_DECREF(object);
14126 }
14127 else if (!strcasecmp(hdrs[i].key, "Content-length")) {
14128 #if PY_MAJOR_VERSION >= 3
14129 object = PyUnicode_DecodeLatin1(hdrs[i].val,
14130 strlen(hdrs[i].val), NULL);
14131 #else
14132 object = PyString_FromString(hdrs[i].val);
14133 #endif
14134 PyDict_SetItemString(vars, "CONTENT_LENGTH", object);
14135 Py_DECREF(object);
14136 }
14137 else if (!strcasecmp(hdrs[i].key, "Authorization")
14138 || !strcasecmp(hdrs[i].key, "Proxy-Authorization")) {
14139 continue;
14140 }
14141 else {
14142 if (hdrs[i].val) {
14143 char *header = wsgi_http2env(r->pool, hdrs[i].key);
14144
14145 if (header) {
14146 #if PY_MAJOR_VERSION >= 3
14147 object = PyUnicode_DecodeLatin1(hdrs[i].val,
14148 strlen(hdrs[i].val), NULL);
14149 #else
14150 object = PyString_FromString(hdrs[i].val);
14151 #endif
14152
14153 PyDict_SetItemString(vars, header, object);
14154
14155 Py_DECREF(object);
14156 }
14157 }
14158 }
14159 }
14160
14161 value = ap_psignature("", r);
14162 #if PY_MAJOR_VERSION >= 3
14163 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14164 #else
14165 object = PyString_FromString(value);
14166 #endif
14167 PyDict_SetItemString(vars, "SERVER_SIGNATURE", object);
14168 Py_DECREF(object);
14169
14170 #if AP_MODULE_MAGIC_AT_LEAST(20060905,0)
14171 value = ap_get_server_banner();
14172 #else
14173 value = ap_get_server_version();
14174 #endif
14175 #if PY_MAJOR_VERSION >= 3
14176 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14177 #else
14178 object = PyString_FromString(value);
14179 #endif
14180 PyDict_SetItemString(vars, "SERVER_SOFTWARE", object);
14181 Py_DECREF(object);
14182
14183 value = ap_escape_html(r->pool, ap_get_server_name(r));
14184 #if PY_MAJOR_VERSION >= 3
14185 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14186 #else
14187 object = PyString_FromString(value);
14188 #endif
14189 PyDict_SetItemString(vars, "SERVER_NAME", object);
14190 Py_DECREF(object);
14191
14192 if (r->connection->local_ip) {
14193 value = r->connection->local_ip;
14194 #if PY_MAJOR_VERSION >= 3
14195 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14196 #else
14197 object = PyString_FromString(value);
14198 #endif
14199 PyDict_SetItemString(vars, "SERVER_ADDR", object);
14200 Py_DECREF(object);
14201 }
14202
14203 value = apr_psprintf(r->pool, "%u", ap_get_server_port(r));
14204 #if PY_MAJOR_VERSION >= 3
14205 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14206 #else
14207 object = PyString_FromString(value);
14208 #endif
14209 PyDict_SetItemString(vars, "SERVER_PORT", object);
14210 Py_DECREF(object);
14211
14212 value = ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST, NULL);
14213 if (value) {
14214 #if PY_MAJOR_VERSION >= 3
14215 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14216 #else
14217 object = PyString_FromString(value);
14218 #endif
14219 PyDict_SetItemString(vars, "REMOTE_HOST", object);
14220 Py_DECREF(object);
14221 }
14222
14223 #if AP_MODULE_MAGIC_AT_LEAST(20111130,0)
14224 if (r->useragent_ip) {
14225 value = r->useragent_ip;
14226 #if PY_MAJOR_VERSION >= 3
14227 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14228 #else
14229 object = PyString_FromString(value);
14230 #endif
14231 PyDict_SetItemString(vars, "REMOTE_ADDR", object);
14232 Py_DECREF(object);
14233 }
14234 #else
14235 if (c->remote_ip) {
14236 value = c->remote_ip;
14237 #if PY_MAJOR_VERSION >= 3
14238 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14239 #else
14240 object = PyString_FromString(value);
14241 #endif
14242 PyDict_SetItemString(vars, "REMOTE_ADDR", object);
14243 Py_DECREF(object);
14244 }
14245 #endif
14246
14247 #if PY_MAJOR_VERSION >= 3
14248 value = ap_document_root(r);
14249 object = PyUnicode_Decode(value, strlen(value),
14250 Py_FileSystemDefaultEncoding,
14251 "surrogateescape");
14252 #else
14253 object = PyString_FromString(ap_document_root(r));
14254 #endif
14255 PyDict_SetItemString(vars, "DOCUMENT_ROOT", object);
14256 Py_DECREF(object);
14257
14258 if (s->server_admin) {
14259 value = s->server_admin;
14260 #if PY_MAJOR_VERSION >= 3
14261 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14262 #else
14263 object = PyString_FromString(value);
14264 #endif
14265 PyDict_SetItemString(vars, "SERVER_ADMIN", object);
14266 Py_DECREF(object);
14267 }
14268
14269 #if AP_MODULE_MAGIC_AT_LEAST(20111130,0)
14270 rport = c->client_addr->port;
14271 value = apr_itoa(r->pool, rport);
14272 #if PY_MAJOR_VERSION >= 3
14273 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14274 #else
14275 object = PyString_FromString(value);
14276 #endif
14277 PyDict_SetItemString(vars, "REMOTE_PORT", object);
14278 Py_DECREF(object);
14279 #else
14280 rport = c->remote_addr->port;
14281 value = apr_itoa(r->pool, rport);
14282 #if PY_MAJOR_VERSION >= 3
14283 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14284 #else
14285 object = PyString_FromString(value);
14286 #endif
14287 PyDict_SetItemString(vars, "REMOTE_PORT", object);
14288 Py_DECREF(object);
14289 #endif
14290
14291 value = r->protocol;
14292 #if PY_MAJOR_VERSION >= 3
14293 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14294 #else
14295 object = PyString_FromString(value);
14296 #endif
14297 PyDict_SetItemString(vars, "SERVER_PROTOCOL", object);
14298 Py_DECREF(object);
14299
14300 value = r->method;
14301 #if PY_MAJOR_VERSION >= 3
14302 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14303 #else
14304 object = PyString_FromString(value);
14305 #endif
14306 PyDict_SetItemString(vars, "REQUEST_METHOD", object);
14307 Py_DECREF(object);
14308
14309 value = r->args ? r->args : "";
14310 #if PY_MAJOR_VERSION >= 3
14311 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14312 #else
14313 object = PyString_FromString(value);
14314 #endif
14315 PyDict_SetItemString(vars, "QUERY_STRING", object);
14316 Py_DECREF(object);
14317
14318 value = wsgi_original_uri(r);
14319 #if PY_MAJOR_VERSION >= 3
14320 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14321 #else
14322 object = PyString_FromString(value);
14323 #endif
14324 PyDict_SetItemString(vars, "REQUEST_URI", object);
14325 Py_DECREF(object);
14326
14327 /*
14328 * XXX Apparently webdav does actually do modifications to
14329 * the uri and path_info attributes of request and they
14330 * could be used as part of authorisation.
14331 */
14332
14333 if (!strcmp(r->protocol, "INCLUDED")) {
14334 value = r->uri;
14335 #if PY_MAJOR_VERSION >= 3
14336 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14337 #else
14338 object = PyString_FromString(value);
14339 #endif
14340 PyDict_SetItemString(vars, "SCRIPT_NAME", object);
14341 Py_DECREF(object);
14342
14343 value = r->path_info ? r->path_info : "";
14344 #if PY_MAJOR_VERSION >= 3
14345 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14346 #else
14347 object = PyString_FromString(value);
14348 #endif
14349 PyDict_SetItemString(vars, "PATH_INFO", object);
14350 Py_DECREF(object);
14351 }
14352 else if (!r->path_info || !*r->path_info) {
14353 value = r->uri;
14354 #if PY_MAJOR_VERSION >= 3
14355 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14356 #else
14357 object = PyString_FromString(value);
14358 #endif
14359 PyDict_SetItemString(vars, "SCRIPT_NAME", object);
14360 Py_DECREF(object);
14361
14362 value = "";
14363 #if PY_MAJOR_VERSION >= 3
14364 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14365 #else
14366 object = PyString_FromString(value);
14367 #endif
14368 PyDict_SetItemString(vars, "PATH_INFO", object);
14369 Py_DECREF(object);
14370 }
14371 else {
14372 int path_info_start = ap_find_path_info(r->uri, r->path_info);
14373 value = apr_pstrndup(r->pool, r->uri, path_info_start);
14374 #if PY_MAJOR_VERSION >= 3
14375 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14376 #else
14377 object = PyString_FromString(value);
14378 #endif
14379 PyDict_SetItemString(vars, "SCRIPT_NAME", object);
14380 Py_DECREF(object);
14381
14382 value = r->path_info ? r->path_info : "";
14383 #if PY_MAJOR_VERSION >= 3
14384 object = PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14385 #else
14386 object = PyString_FromString(value);
14387 #endif
14388 PyDict_SetItemString(vars, "PATH_INFO", object);
14389 Py_DECREF(object);
14390 }
14391
14392 object = Py_BuildValue("(iii)", AP_SERVER_MAJORVERSION_NUMBER,
14393 AP_SERVER_MINORVERSION_NUMBER,
14394 AP_SERVER_PATCHLEVEL_NUMBER);
14395 PyDict_SetItemString(vars, "apache.version", object);
14396 Py_DECREF(object);
14397
14398 object = Py_BuildValue("(iii)", MOD_WSGI_MAJORVERSION_NUMBER,
14399 MOD_WSGI_MINORVERSION_NUMBER,
14400 MOD_WSGI_MICROVERSION_NUMBER);
14401 PyDict_SetItemString(vars, "mod_wsgi.version", object);
14402 Py_DECREF(object);
14403
14404 #if PY_MAJOR_VERSION >= 3
14405 object = PyUnicode_FromString("");
14406 #else
14407 object = PyString_FromString("");
14408 #endif
14409 PyDict_SetItemString(vars, "mod_wsgi.process_group", object);
14410 Py_DECREF(object);
14411
14412 #if PY_MAJOR_VERSION >= 3
14413 object = PyUnicode_DecodeLatin1(group, strlen(group), NULL);
14414 #else
14415 object = PyString_FromString(group);
14416 #endif
14417 PyDict_SetItemString(vars, "mod_wsgi.application_group", object);
14418 Py_DECREF(object);
14419
14420 object = PyLong_FromLong(self->config->script_reloading);
14421 PyDict_SetItemString(vars, "mod_wsgi.script_reloading", object);
14422 Py_DECREF(object);
14423
14424 /*
14425 * Setup log object for WSGI errors. Don't decrement
14426 * reference to log object as keep reference to it.
14427 */
14428
14429 object = (PyObject *)self->log;
14430 PyDict_SetItemString(vars, "wsgi.errors", object);
14431
14432 /*
14433 * If Apache extensions are enabled add a CObject reference
14434 * to the Apache request_rec structure instance.
14435 */
14436
14437 if (!wsgi_daemon_pool && self->config->pass_apache_request) {
14438 #if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 2) || \
14439 (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 7)
14440 object = PyCapsule_New(self->r, 0, 0);
14441 #else
14442 object = PyCObject_FromVoidPtr(self->r, 0);
14443 #endif
14444 PyDict_SetItemString(vars, "apache.request_rec", object);
14445 Py_DECREF(object);
14446 }
14447
14448 /*
14449 * Extensions for accessing SSL certificate information from
14450 * mod_ssl when in use.
14451 */
14452
14453 object = PyObject_GetAttrString((PyObject *)self, "ssl_is_https");
14454 PyDict_SetItemString(vars, "mod_ssl.is_https", object);
14455 Py_DECREF(object);
14456
14457 object = PyObject_GetAttrString((PyObject *)self, "ssl_var_lookup");
14458 PyDict_SetItemString(vars, "mod_ssl.var_lookup", object);
14459 Py_DECREF(object);
14460
14461 return vars;
14462 }
14463
Auth_ssl_is_https(AuthObject * self,PyObject * args)14464 static PyObject *Auth_ssl_is_https(AuthObject *self, PyObject *args)
14465 {
14466 APR_OPTIONAL_FN_TYPE(ssl_is_https) *ssl_is_https = 0;
14467
14468 if (!self->r) {
14469 PyErr_SetString(PyExc_RuntimeError, "request object has expired");
14470 return NULL;
14471 }
14472
14473 if (!PyArg_ParseTuple(args, ":ssl_is_https"))
14474 return NULL;
14475
14476 ssl_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
14477
14478 if (ssl_is_https == 0)
14479 return Py_BuildValue("i", 0);
14480
14481 return Py_BuildValue("i", ssl_is_https(self->r->connection));
14482 }
14483
Auth_ssl_var_lookup(AuthObject * self,PyObject * args)14484 static PyObject *Auth_ssl_var_lookup(AuthObject *self, PyObject *args)
14485 {
14486 APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *ssl_var_lookup = 0;
14487
14488 PyObject *item = NULL;
14489 PyObject *latin_item = NULL;
14490
14491 char *name = 0;
14492 char *value = 0;
14493
14494 if (!self->r) {
14495 PyErr_SetString(PyExc_RuntimeError, "request object has expired");
14496 return NULL;
14497 }
14498
14499 if (!PyArg_ParseTuple(args, "O:ssl_var_lookup", &item))
14500 return NULL;
14501
14502 #if PY_MAJOR_VERSION >= 3
14503 if (PyUnicode_Check(item)) {
14504 latin_item = PyUnicode_AsLatin1String(item);
14505 if (!latin_item) {
14506 PyErr_Format(PyExc_TypeError, "byte string value expected, "
14507 "value containing non 'latin-1' characters found");
14508
14509 return NULL;
14510 }
14511
14512 item = latin_item;
14513 }
14514 #endif
14515
14516 if (!PyString_Check(item)) {
14517 PyErr_Format(PyExc_TypeError, "byte string value expected, value "
14518 "of type %.200s found", item->ob_type->tp_name);
14519
14520 Py_XDECREF(latin_item);
14521
14522 return NULL;
14523 }
14524
14525 name = PyString_AsString(item);
14526
14527 ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
14528
14529 if (ssl_var_lookup == 0)
14530 {
14531 Py_XDECREF(latin_item);
14532
14533 Py_INCREF(Py_None);
14534
14535 return Py_None;
14536 }
14537
14538 value = ssl_var_lookup(self->r->pool, self->r->server,
14539 self->r->connection, self->r, name);
14540
14541 Py_XDECREF(latin_item);
14542
14543 if (!value) {
14544 Py_INCREF(Py_None);
14545
14546 return Py_None;
14547 }
14548
14549 #if PY_MAJOR_VERSION >= 3
14550 return PyUnicode_DecodeLatin1(value, strlen(value), NULL);
14551 #else
14552 return PyString_FromString(value);
14553 #endif
14554 }
14555
14556 static PyMethodDef Auth_methods[] = {
14557 { "ssl_is_https", (PyCFunction)Auth_ssl_is_https, METH_VARARGS, 0 },
14558 { "ssl_var_lookup", (PyCFunction)Auth_ssl_var_lookup, METH_VARARGS, 0 },
14559 { NULL, NULL}
14560 };
14561
14562 static PyTypeObject Auth_Type = {
14563 PyVarObject_HEAD_INIT(NULL, 0)
14564 "mod_wsgi.Auth", /*tp_name*/
14565 sizeof(AuthObject), /*tp_basicsize*/
14566 0, /*tp_itemsize*/
14567 /* methods */
14568 (destructor)Auth_dealloc, /*tp_dealloc*/
14569 0, /*tp_print*/
14570 0, /*tp_getattr*/
14571 0, /*tp_setattr*/
14572 0, /*tp_compare*/
14573 0, /*tp_repr*/
14574 0, /*tp_as_number*/
14575 0, /*tp_as_sequence*/
14576 0, /*tp_as_mapping*/
14577 0, /*tp_hash*/
14578 0, /*tp_call*/
14579 0, /*tp_str*/
14580 0, /*tp_getattro*/
14581 0, /*tp_setattro*/
14582 0, /*tp_as_buffer*/
14583 Py_TPFLAGS_DEFAULT, /*tp_flags*/
14584 0, /*tp_doc*/
14585 0, /*tp_traverse*/
14586 0, /*tp_clear*/
14587 0, /*tp_richcompare*/
14588 0, /*tp_weaklistoffset*/
14589 0, /*tp_iter*/
14590 0, /*tp_iternext*/
14591 Auth_methods, /*tp_methods*/
14592 0, /*tp_members*/
14593 0, /*tp_getset*/
14594 0, /*tp_base*/
14595 0, /*tp_dict*/
14596 0, /*tp_descr_get*/
14597 0, /*tp_descr_set*/
14598 0, /*tp_dictoffset*/
14599 0, /*tp_init*/
14600 0, /*tp_alloc*/
14601 0, /*tp_new*/
14602 0, /*tp_free*/
14603 0, /*tp_is_gc*/
14604 };
14605
14606 #if defined(MOD_WSGI_WITH_AUTHN_PROVIDER)
wsgi_check_password(request_rec * r,const char * user,const char * password)14607 static authn_status wsgi_check_password(request_rec *r, const char *user,
14608 const char *password)
14609 {
14610 WSGIRequestConfig *config;
14611
14612 InterpreterObject *interp = NULL;
14613 PyObject *modules = NULL;
14614 PyObject *module = NULL;
14615 char *name = NULL;
14616 int exists = 0;
14617
14618 const char *script;
14619 const char *group;
14620
14621 authn_status status;
14622
14623 config = wsgi_create_req_config(r->pool, r);
14624
14625 if (!config->auth_user_script) {
14626 ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
14627 "mod_wsgi (pid=%d): Location of WSGI user "
14628 "authentication script not provided.", getpid());
14629
14630 return AUTH_GENERAL_ERROR;
14631 }
14632
14633 /*
14634 * Acquire the desired python interpreter. Once this is done
14635 * it is safe to start manipulating python objects.
14636 */
14637
14638 script = config->auth_user_script->handler_script;
14639 group = wsgi_server_group(r, config->auth_user_script->application_group);
14640
14641 interp = wsgi_acquire_interpreter(group);
14642
14643 if (!interp) {
14644 ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
14645 "mod_wsgi (pid=%d): Cannot acquire interpreter '%s'.",
14646 getpid(), group);
14647
14648 return AUTH_GENERAL_ERROR;
14649 }
14650
14651 /* Calculate the Python module name to be used for script. */
14652
14653 name = wsgi_module_name(r->pool, script);
14654
14655 /*
14656 * Use a lock around the check to see if the module is
14657 * already loaded and the import of the module to prevent
14658 * two request handlers trying to import the module at the
14659 * same time.
14660 */
14661
14662 #if APR_HAS_THREADS
14663 Py_BEGIN_ALLOW_THREADS
14664 apr_thread_mutex_lock(wsgi_module_lock);
14665 Py_END_ALLOW_THREADS
14666 #endif
14667
14668 modules = PyImport_GetModuleDict();
14669 module = PyDict_GetItemString(modules, name);
14670
14671 Py_XINCREF(module);
14672
14673 if (module)
14674 exists = 1;
14675
14676 /*
14677 * If script reloading is enabled and the module for it has
14678 * previously been loaded, see if it has been modified since
14679 * the last time it was accessed.
14680 */
14681
14682 if (module && config->script_reloading) {
14683 if (wsgi_reload_required(r->pool, r, script, module, NULL)) {
14684 /*
14685 * Script file has changed. Only support module
14686 * reloading for authentication scripts. Remove the
14687 * module from the modules dictionary before
14688 * reloading it again. If code is executing within
14689 * the module at the time, the callers reference
14690 * count on the module should ensure it isn't
14691 * actually destroyed until it is finished.
14692 */
14693
14694 Py_DECREF(module);
14695 module = NULL;
14696
14697 PyDict_DelItemString(modules, name);
14698 }
14699 }
14700
14701 if (!module) {
14702 module = wsgi_load_source(r->pool, r, name, exists, script,
14703 "", group, 0);
14704 }
14705
14706 /* Safe now to release the module lock. */
14707
14708 #if APR_HAS_THREADS
14709 apr_thread_mutex_unlock(wsgi_module_lock);
14710 #endif
14711
14712 /* Log any details of exceptions if import failed. */
14713
14714 if (PyErr_Occurred())
14715 wsgi_log_python_error(r, NULL, script, 0);
14716
14717 /* Assume an internal server error unless everything okay. */
14718
14719 status = AUTH_GENERAL_ERROR;
14720
14721 /* Determine if script exists and execute it. */
14722
14723 if (module) {
14724 PyObject *module_dict = NULL;
14725 PyObject *object = NULL;
14726
14727 module_dict = PyModule_GetDict(module);
14728 object = PyDict_GetItemString(module_dict, "check_password");
14729
14730 if (object) {
14731 PyObject *vars = NULL;
14732 PyObject *args = NULL;
14733 PyObject *result = NULL;
14734 PyObject *method = NULL;
14735
14736 AuthObject *adapter = NULL;
14737
14738 adapter = newAuthObject(r, config);
14739
14740 if (adapter) {
14741 vars = Auth_environ(adapter, group);
14742
14743 Py_INCREF(object);
14744 args = Py_BuildValue("(Oss)", vars, user, password);
14745 result = PyEval_CallObject(object, args);
14746 Py_DECREF(args);
14747 Py_DECREF(object);
14748 Py_DECREF(vars);
14749
14750 if (result) {
14751 if (result == Py_None) {
14752 status = AUTH_USER_NOT_FOUND;
14753 }
14754 else if (result == Py_True) {
14755 status = AUTH_GRANTED;
14756 }
14757 else if (result == Py_False) {
14758 status = AUTH_DENIED;
14759 }
14760 #if PY_MAJOR_VERSION >= 3
14761 else if (PyUnicode_Check(result)) {
14762 PyObject *str = NULL;
14763
14764 str = PyUnicode_AsUTF8String(result);
14765
14766 if (str) {
14767 adapter->r->user = apr_pstrdup(adapter->r->pool,
14768 PyString_AsString(str));
14769
14770 status = AUTH_GRANTED;
14771 }
14772 }
14773 #else
14774 else if (PyString_Check(result)) {
14775 adapter->r->user = apr_pstrdup(adapter->r->pool,
14776 PyString_AsString(result));
14777
14778 status = AUTH_GRANTED;
14779 }
14780 #endif
14781 else {
14782 PyErr_SetString(PyExc_TypeError, "Basic auth "
14783 "provider must return True, False "
14784 "None or user name as string");
14785 }
14786
14787 Py_DECREF(result);
14788 }
14789
14790 /*
14791 * Wipe out references to Apache request object
14792 * held by Python objects, so can detect when an
14793 * application holds on to the transient Python
14794 * objects beyond the life of the request and
14795 * thus raise an exception if they are used.
14796 */
14797
14798 adapter->r = NULL;
14799
14800 /* Log any details of exceptions if execution failed. */
14801
14802 if (PyErr_Occurred())
14803 wsgi_log_python_error(r, NULL, script, 0);
14804
14805 /* Close the log object so data is flushed. */
14806
14807 method = PyObject_GetAttrString(adapter->log, "close");
14808
14809 if (!method) {
14810 PyErr_Format(PyExc_AttributeError,
14811 "'%s' object has no attribute 'close'",
14812 adapter->log->ob_type->tp_name);
14813 }
14814 else {
14815 args = PyTuple_New(0);
14816 result = PyEval_CallObject(method, args);
14817 Py_XDECREF(result);
14818 Py_DECREF(args);
14819 }
14820
14821 /* Log any details of exceptions if execution failed. */
14822
14823 if (PyErr_Occurred())
14824 wsgi_log_python_error(r, NULL, script, 0);
14825
14826 Py_XDECREF(method);
14827
14828 /* No longer need adapter object. */
14829
14830 Py_DECREF((PyObject *)adapter);
14831 }
14832 }
14833 else {
14834 Py_BEGIN_ALLOW_THREADS
14835 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
14836 "mod_wsgi (pid=%d): Target WSGI user "
14837 "authentication script '%s' does not provide "
14838 "'Basic' auth provider.", getpid(), script);
14839 Py_END_ALLOW_THREADS
14840 }
14841 }
14842
14843 /* Cleanup and release interpreter, */
14844
14845 Py_XDECREF(module);
14846
14847 wsgi_release_interpreter(interp);
14848
14849 return status;
14850 }
14851
wsgi_get_realm_hash(request_rec * r,const char * user,const char * realm,char ** rethash)14852 static authn_status wsgi_get_realm_hash(request_rec *r, const char *user,
14853 const char *realm, char **rethash)
14854 {
14855 WSGIRequestConfig *config;
14856
14857 InterpreterObject *interp = NULL;
14858 PyObject *modules = NULL;
14859 PyObject *module = NULL;
14860 char *name = NULL;
14861 int exists = 0;
14862
14863 const char *script;
14864 const char *group;
14865
14866 authn_status status;
14867
14868 config = wsgi_create_req_config(r->pool, r);
14869
14870 if (!config->auth_user_script) {
14871 ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
14872 "mod_wsgi (pid=%d): Location of WSGI user "
14873 "authentication script not provided.", getpid());
14874
14875 return AUTH_GENERAL_ERROR;
14876 }
14877
14878 /*
14879 * Acquire the desired python interpreter. Once this is done
14880 * it is safe to start manipulating python objects.
14881 */
14882
14883 script = config->auth_user_script->handler_script;
14884 group = wsgi_server_group(r, config->auth_user_script->application_group);
14885
14886 interp = wsgi_acquire_interpreter(group);
14887
14888 if (!interp) {
14889 ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
14890 "mod_wsgi (pid=%d): Cannot acquire interpreter '%s'.",
14891 getpid(), group);
14892
14893 return AUTH_GENERAL_ERROR;
14894 }
14895
14896 /* Calculate the Python module name to be used for script. */
14897
14898 name = wsgi_module_name(r->pool, script);
14899
14900 /*
14901 * Use a lock around the check to see if the module is
14902 * already loaded and the import of the module to prevent
14903 * two request handlers trying to import the module at the
14904 * same time.
14905 */
14906
14907 #if APR_HAS_THREADS
14908 Py_BEGIN_ALLOW_THREADS
14909 apr_thread_mutex_lock(wsgi_module_lock);
14910 Py_END_ALLOW_THREADS
14911 #endif
14912
14913 modules = PyImport_GetModuleDict();
14914 module = PyDict_GetItemString(modules, name);
14915
14916 Py_XINCREF(module);
14917
14918 if (module)
14919 exists = 1;
14920
14921 /*
14922 * If script reloading is enabled and the module for it has
14923 * previously been loaded, see if it has been modified since
14924 * the last time it was accessed.
14925 */
14926
14927 if (module && config->script_reloading) {
14928 if (wsgi_reload_required(r->pool, r, script, module, NULL)) {
14929 /*
14930 * Script file has changed. Only support module
14931 * reloading for authentication scripts. Remove the
14932 * module from the modules dictionary before
14933 * reloading it again. If code is executing within
14934 * the module at the time, the callers reference
14935 * count on the module should ensure it isn't
14936 * actually destroyed until it is finished.
14937 */
14938
14939 Py_DECREF(module);
14940 module = NULL;
14941
14942 PyDict_DelItemString(modules, name);
14943 }
14944 }
14945
14946 if (!module) {
14947 module = wsgi_load_source(r->pool, r, name, exists, script,
14948 "", group, 0);
14949 }
14950
14951 /* Safe now to release the module lock. */
14952
14953 #if APR_HAS_THREADS
14954 apr_thread_mutex_unlock(wsgi_module_lock);
14955 #endif
14956
14957 /* Log any details of exceptions if import failed. */
14958
14959 if (PyErr_Occurred())
14960 wsgi_log_python_error(r, NULL, script, 0);
14961
14962 /* Assume an internal server error unless everything okay. */
14963
14964 status = AUTH_GENERAL_ERROR;
14965
14966 /* Determine if script exists and execute it. */
14967
14968 if (module) {
14969 PyObject *module_dict = NULL;
14970 PyObject *object = NULL;
14971
14972 module_dict = PyModule_GetDict(module);
14973 object = PyDict_GetItemString(module_dict, "get_realm_hash");
14974
14975 if (object) {
14976 PyObject *vars = NULL;
14977 PyObject *args = NULL;
14978 PyObject *result = NULL;
14979 PyObject *method = NULL;
14980
14981 AuthObject *adapter = NULL;
14982
14983 adapter = newAuthObject(r, config);
14984
14985 if (adapter) {
14986 vars = Auth_environ(adapter, group);
14987
14988 Py_INCREF(object);
14989 args = Py_BuildValue("(Oss)", vars, user, realm);
14990 result = PyEval_CallObject(object, args);
14991 Py_DECREF(args);
14992 Py_DECREF(object);
14993 Py_DECREF(vars);
14994
14995 if (result) {
14996 if (result == Py_None) {
14997 status = AUTH_USER_NOT_FOUND;
14998 }
14999 else if (PyString_Check(result)) {
15000 *rethash = PyString_AsString(result);
15001 *rethash = apr_pstrdup(r->pool, *rethash);
15002
15003 status = AUTH_USER_FOUND;
15004 }
15005 #if PY_MAJOR_VERSION >= 3
15006 else if (PyUnicode_Check(result)) {
15007 PyObject *latin_item;
15008 latin_item = PyUnicode_AsLatin1String(result);
15009 if (!latin_item) {
15010 PyErr_SetString(PyExc_TypeError, "Digest auth "
15011 "provider must return None "
15012 "or string object, value "
15013 "containing non 'latin-1' "
15014 "characters found");
15015 }
15016 else {
15017 Py_DECREF(result);
15018 result = latin_item;
15019
15020 *rethash = PyString_AsString(result);
15021 *rethash = apr_pstrdup(r->pool, *rethash);
15022
15023 status = AUTH_USER_FOUND;
15024 }
15025 }
15026 #endif
15027 else {
15028 PyErr_SetString(PyExc_TypeError, "Digest auth "
15029 "provider must return None "
15030 "or string object");
15031 }
15032
15033 Py_DECREF(result);
15034 }
15035
15036 /*
15037 * Wipe out references to Apache request object
15038 * held by Python objects, so can detect when an
15039 * application holds on to the transient Python
15040 * objects beyond the life of the request and
15041 * thus raise an exception if they are used.
15042 */
15043
15044 adapter->r = NULL;
15045
15046 /* Log any details of exceptions if execution failed. */
15047
15048 if (PyErr_Occurred())
15049 wsgi_log_python_error(r, NULL, script, 0);
15050
15051 /* Close the log object so data is flushed. */
15052
15053 method = PyObject_GetAttrString(adapter->log, "close");
15054
15055 if (!method) {
15056 PyErr_Format(PyExc_AttributeError,
15057 "'%s' object has no attribute 'close'",
15058 adapter->log->ob_type->tp_name);
15059 }
15060 else {
15061 args = PyTuple_New(0);
15062 result = PyEval_CallObject(method, args);
15063 Py_XDECREF(result);
15064 Py_DECREF(args);
15065 }
15066
15067 /* Log any details of exceptions if execution failed. */
15068
15069 if (PyErr_Occurred())
15070 wsgi_log_python_error(r, NULL, script, 0);
15071
15072 Py_XDECREF(method);
15073
15074 /* No longer need adapter object. */
15075
15076 Py_DECREF((PyObject *)adapter);
15077 }
15078 }
15079 else {
15080 Py_BEGIN_ALLOW_THREADS
15081 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
15082 "mod_wsgi (pid=%d): Target WSGI user "
15083 "authentication script '%s' does not provide "
15084 "'Digest' auth provider.", getpid(), script);
15085 Py_END_ALLOW_THREADS
15086 }
15087 }
15088
15089 /* Cleanup and release interpreter, */
15090
15091 Py_XDECREF(module);
15092
15093 wsgi_release_interpreter(interp);
15094
15095 return status;
15096 }
15097
15098 static const authn_provider wsgi_authn_provider =
15099 {
15100 &wsgi_check_password,
15101 &wsgi_get_realm_hash
15102 };
15103 #endif
15104
wsgi_groups_for_user(request_rec * r,WSGIRequestConfig * config,apr_table_t ** grpstatus)15105 static int wsgi_groups_for_user(request_rec *r, WSGIRequestConfig *config,
15106 apr_table_t **grpstatus)
15107 {
15108 apr_table_t *grps = apr_table_make(r->pool, 15);
15109
15110 InterpreterObject *interp = NULL;
15111 PyObject *modules = NULL;
15112 PyObject *module = NULL;
15113 char *name = NULL;
15114 int exists = 0;
15115
15116 const char *script;
15117 const char *group;
15118
15119 int status = HTTP_INTERNAL_SERVER_ERROR;
15120
15121 if (!config->auth_group_script) {
15122 ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
15123 "mod_wsgi (pid=%d): Location of WSGI group "
15124 "authentication script not provided.", getpid());
15125
15126 return HTTP_INTERNAL_SERVER_ERROR;
15127 }
15128
15129 /*
15130 * Acquire the desired python interpreter. Once this is done
15131 * it is safe to start manipulating python objects.
15132 */
15133
15134 script = config->auth_group_script->handler_script;
15135 group = wsgi_server_group(r, config->auth_group_script->application_group);
15136
15137 interp = wsgi_acquire_interpreter(group);
15138
15139 if (!interp) {
15140 ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
15141 "mod_wsgi (pid=%d): Cannot acquire interpreter '%s'.",
15142 getpid(), group);
15143
15144 return HTTP_INTERNAL_SERVER_ERROR;
15145 }
15146
15147 /* Calculate the Python module name to be used for script. */
15148
15149 name = wsgi_module_name(r->pool, script);
15150
15151 /*
15152 * Use a lock around the check to see if the module is
15153 * already loaded and the import of the module to prevent
15154 * two request handlers trying to import the module at the
15155 * same time.
15156 */
15157
15158 #if APR_HAS_THREADS
15159 Py_BEGIN_ALLOW_THREADS
15160 apr_thread_mutex_lock(wsgi_module_lock);
15161 Py_END_ALLOW_THREADS
15162 #endif
15163
15164 modules = PyImport_GetModuleDict();
15165 module = PyDict_GetItemString(modules, name);
15166
15167 Py_XINCREF(module);
15168
15169 if (module)
15170 exists = 1;
15171
15172 /*
15173 * If script reloading is enabled and the module for it has
15174 * previously been loaded, see if it has been modified since
15175 * the last time it was accessed.
15176 */
15177
15178 if (module && config->script_reloading) {
15179 if (wsgi_reload_required(r->pool, r, script, module, NULL)) {
15180 /*
15181 * Script file has changed. Only support module
15182 * reloading for authentication scripts. Remove the
15183 * module from the modules dictionary before
15184 * reloading it again. If code is executing within
15185 * the module at the time, the callers reference
15186 * count on the module should ensure it isn't
15187 * actually destroyed until it is finished.
15188 */
15189
15190 Py_DECREF(module);
15191 module = NULL;
15192
15193 PyDict_DelItemString(modules, name);
15194 }
15195 }
15196
15197 if (!module) {
15198 module = wsgi_load_source(r->pool, r, name, exists, script,
15199 "", group, 0);
15200 }
15201
15202 /* Safe now to release the module lock. */
15203
15204 #if APR_HAS_THREADS
15205 apr_thread_mutex_unlock(wsgi_module_lock);
15206 #endif
15207
15208 /* Log any details of exceptions if import failed. */
15209
15210 if (PyErr_Occurred())
15211 wsgi_log_python_error(r, NULL, script, 0);
15212
15213 /* Assume an internal server error unless everything okay. */
15214
15215 status = HTTP_INTERNAL_SERVER_ERROR;
15216
15217 /* Determine if script exists and execute it. */
15218
15219 if (module) {
15220 PyObject *module_dict = NULL;
15221 PyObject *object = NULL;
15222
15223 module_dict = PyModule_GetDict(module);
15224 object = PyDict_GetItemString(module_dict, "groups_for_user");
15225
15226 if (object) {
15227 PyObject *vars = NULL;
15228 PyObject *args = NULL;
15229 PyObject *result = NULL;
15230 PyObject *method = NULL;
15231
15232 AuthObject *adapter = NULL;
15233
15234 adapter = newAuthObject(r, config);
15235
15236 if (adapter) {
15237 vars = Auth_environ(adapter, group);
15238
15239 Py_INCREF(object);
15240 args = Py_BuildValue("(Os)", vars, r->user);
15241 result = PyEval_CallObject(object, args);
15242 Py_DECREF(args);
15243 Py_DECREF(object);
15244 Py_DECREF(vars);
15245
15246 if (result) {
15247 PyObject *iterator;
15248
15249 iterator = PyObject_GetIter(result);
15250
15251 if (iterator) {
15252 PyObject *item;
15253 const char *name;
15254
15255 status = OK;
15256
15257 while ((item = PyIter_Next(iterator))) {
15258 #if PY_MAJOR_VERSION >= 3
15259 if (PyUnicode_Check(item)) {
15260 PyObject *latin_item;
15261 latin_item = PyUnicode_AsLatin1String(item);
15262 if (!latin_item) {
15263 Py_BEGIN_ALLOW_THREADS
15264 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0,
15265 r, "mod_wsgi (pid=%d): "
15266 "Groups for user returned "
15267 "from '%s' must be an "
15268 "iterable sequence of "
15269 "byte strings, value "
15270 "containing non 'latin-1' "
15271 "characters found",
15272 getpid(), script);
15273 Py_END_ALLOW_THREADS
15274
15275 Py_DECREF(item);
15276
15277 status = HTTP_INTERNAL_SERVER_ERROR;
15278
15279 break;
15280 }
15281 else {
15282 Py_DECREF(item);
15283 item = latin_item;
15284 }
15285 }
15286 #endif
15287
15288 if (!PyString_Check(item)) {
15289 Py_BEGIN_ALLOW_THREADS
15290 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
15291 "mod_wsgi (pid=%d): Groups for "
15292 "user returned from '%s' must "
15293 "be an iterable sequence of "
15294 "byte strings.", getpid(),
15295 script);
15296 Py_END_ALLOW_THREADS
15297
15298 Py_DECREF(item);
15299
15300 status = HTTP_INTERNAL_SERVER_ERROR;
15301
15302 break;
15303 }
15304
15305 name = PyString_AsString(item);
15306
15307 apr_table_setn(grps, apr_pstrdup(r->pool, name),
15308 "1");
15309
15310 Py_DECREF(item);
15311 }
15312
15313 Py_DECREF(iterator);
15314 }
15315 else {
15316 Py_BEGIN_ALLOW_THREADS
15317 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
15318 "mod_wsgi (pid=%d): Groups for user "
15319 "returned from '%s' must be an iterable "
15320 "sequence of byte strings.", getpid(),
15321 script);
15322 Py_END_ALLOW_THREADS
15323 }
15324
15325 Py_DECREF(result);
15326 }
15327
15328 /*
15329 * Wipe out references to Apache request object
15330 * held by Python objects, so can detect when an
15331 * application holds on to the transient Python
15332 * objects beyond the life of the request and
15333 * thus raise an exception if they are used.
15334 */
15335
15336 adapter->r = NULL;
15337
15338 /* Log any details of exceptions if execution failed. */
15339
15340 if (PyErr_Occurred())
15341 wsgi_log_python_error(r, NULL, script, 0);
15342
15343 /* Close the log object so data is flushed. */
15344
15345 method = PyObject_GetAttrString(adapter->log, "close");
15346
15347 if (!method) {
15348 PyErr_Format(PyExc_AttributeError,
15349 "'%s' object has no attribute 'close'",
15350 adapter->log->ob_type->tp_name);
15351 }
15352 else {
15353 args = PyTuple_New(0);
15354 result = PyEval_CallObject(method, args);
15355 Py_XDECREF(result);
15356 Py_DECREF(args);
15357 }
15358
15359 /* Log any details of exceptions if execution failed. */
15360
15361 if (PyErr_Occurred())
15362 wsgi_log_python_error(r, NULL, script, 0);
15363
15364 Py_XDECREF(method);
15365
15366 /* No longer need adapter object. */
15367
15368 Py_DECREF((PyObject *)adapter);
15369 }
15370 }
15371 else {
15372 Py_BEGIN_ALLOW_THREADS
15373 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
15374 "mod_wsgi (pid=%d): Target WSGI group "
15375 "authentication script '%s' does not provide "
15376 "group provider.", getpid(), script);
15377 Py_END_ALLOW_THREADS
15378 }
15379 }
15380
15381 /* Cleanup and release interpreter, */
15382
15383 Py_XDECREF(module);
15384
15385 wsgi_release_interpreter(interp);
15386
15387 if (status == OK)
15388 *grpstatus = grps;
15389
15390 return status;
15391 }
15392
wsgi_allow_access(request_rec * r,WSGIRequestConfig * config,const char * host)15393 static int wsgi_allow_access(request_rec *r, WSGIRequestConfig *config,
15394 const char *host)
15395 {
15396 InterpreterObject *interp = NULL;
15397 PyObject *modules = NULL;
15398 PyObject *module = NULL;
15399 char *name = NULL;
15400 int exists = 0;
15401
15402 const char *script;
15403 const char *group;
15404
15405 int allow = 0;
15406
15407 if (!config->access_script) {
15408 ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
15409 "mod_wsgi (pid=%d): Location of WSGI host "
15410 "access script not provided.", getpid());
15411
15412 return 0;
15413 }
15414
15415 /*
15416 * Acquire the desired python interpreter. Once this is done
15417 * it is safe to start manipulating python objects.
15418 */
15419
15420 script = config->access_script->handler_script;
15421 group = wsgi_server_group(r, config->access_script->application_group);
15422
15423 interp = wsgi_acquire_interpreter(group);
15424
15425 if (!interp) {
15426 ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
15427 "mod_wsgi (pid=%d): Cannot acquire interpreter '%s'.",
15428 getpid(), group);
15429
15430 return 0;
15431 }
15432
15433 /* Calculate the Python module name to be used for script. */
15434
15435 name = wsgi_module_name(r->pool, script);
15436
15437 /*
15438 * Use a lock around the check to see if the module is
15439 * already loaded and the import of the module to prevent
15440 * two request handlers trying to import the module at the
15441 * same time.
15442 */
15443
15444 #if APR_HAS_THREADS
15445 Py_BEGIN_ALLOW_THREADS
15446 apr_thread_mutex_lock(wsgi_module_lock);
15447 Py_END_ALLOW_THREADS
15448 #endif
15449
15450 modules = PyImport_GetModuleDict();
15451 module = PyDict_GetItemString(modules, name);
15452
15453 Py_XINCREF(module);
15454
15455 if (module)
15456 exists = 1;
15457
15458 /*
15459 * If script reloading is enabled and the module for it has
15460 * previously been loaded, see if it has been modified since
15461 * the last time it was accessed.
15462 */
15463
15464 if (module && config->script_reloading) {
15465 if (wsgi_reload_required(r->pool, r, script, module, NULL)) {
15466 /*
15467 * Script file has changed. Only support module
15468 * reloading for authentication scripts. Remove the
15469 * module from the modules dictionary before
15470 * reloading it again. If code is executing within
15471 * the module at the time, the callers reference
15472 * count on the module should ensure it isn't
15473 * actually destroyed until it is finished.
15474 */
15475
15476 Py_DECREF(module);
15477 module = NULL;
15478
15479 PyDict_DelItemString(modules, name);
15480 }
15481 }
15482
15483 if (!module) {
15484 module = wsgi_load_source(r->pool, r, name, exists, script,
15485 "", group, 0);
15486 }
15487
15488 /* Safe now to release the module lock. */
15489
15490 #if APR_HAS_THREADS
15491 apr_thread_mutex_unlock(wsgi_module_lock);
15492 #endif
15493
15494 /* Log any details of exceptions if import failed. */
15495
15496 if (PyErr_Occurred())
15497 wsgi_log_python_error(r, NULL, script, 0);
15498
15499 /* Assume not allowed unless everything okay. */
15500
15501 allow = 0;
15502
15503 /* Determine if script exists and execute it. */
15504
15505 if (module) {
15506 PyObject *module_dict = NULL;
15507 PyObject *object = NULL;
15508
15509 module_dict = PyModule_GetDict(module);
15510 object = PyDict_GetItemString(module_dict, "allow_access");
15511
15512 if (object) {
15513 PyObject *vars = NULL;
15514 PyObject *args = NULL;
15515 PyObject *result = NULL;
15516 PyObject *method = NULL;
15517
15518 AuthObject *adapter = NULL;
15519
15520 adapter = newAuthObject(r, config);
15521
15522 if (adapter) {
15523 vars = Auth_environ(adapter, group);
15524
15525 Py_INCREF(object);
15526 args = Py_BuildValue("(Oz)", vars, host);
15527 result = PyEval_CallObject(object, args);
15528 Py_DECREF(args);
15529 Py_DECREF(object);
15530 Py_DECREF(vars);
15531
15532 if (result) {
15533 if (result == Py_None) {
15534 allow = -1;
15535 }
15536 else if (PyBool_Check(result)) {
15537 if (result == Py_True)
15538 allow = 1;
15539 }
15540 else {
15541 Py_BEGIN_ALLOW_THREADS
15542 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
15543 "mod_wsgi (pid=%d): Indicator of "
15544 "host accessibility returned from '%s' "
15545 "must a boolean or None.", getpid(),
15546 script);
15547 Py_END_ALLOW_THREADS
15548 }
15549
15550 Py_DECREF(result);
15551 }
15552
15553 /*
15554 * Wipe out references to Apache request object
15555 * held by Python objects, so can detect when an
15556 * application holds on to the transient Python
15557 * objects beyond the life of the request and
15558 * thus raise an exception if they are used.
15559 */
15560
15561 adapter->r = NULL;
15562
15563 /* Log any details of exceptions if execution failed. */
15564
15565 if (PyErr_Occurred())
15566 wsgi_log_python_error(r, NULL, script, 0);
15567
15568 /* Close the log object so data is flushed. */
15569
15570 method = PyObject_GetAttrString(adapter->log, "close");
15571
15572 if (!method) {
15573 PyErr_Format(PyExc_AttributeError,
15574 "'%s' object has no attribute 'close'",
15575 adapter->log->ob_type->tp_name);
15576 }
15577 else {
15578 args = PyTuple_New(0);
15579 result = PyEval_CallObject(method, args);
15580 Py_XDECREF(result);
15581 Py_DECREF(args);
15582 }
15583
15584 /* Log any details of exceptions if execution failed. */
15585
15586 if (PyErr_Occurred())
15587 wsgi_log_python_error(r, NULL, script, 0);
15588
15589 Py_XDECREF(method);
15590
15591 /* No longer need adapter object. */
15592
15593 Py_DECREF((PyObject *)adapter);
15594 }
15595 }
15596 else {
15597 Py_BEGIN_ALLOW_THREADS
15598 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
15599 "mod_wsgi (pid=%d): Target WSGI host "
15600 "access script '%s' does not provide "
15601 "host validator.", getpid(), script);
15602 Py_END_ALLOW_THREADS
15603 }
15604 }
15605
15606 /* Cleanup and release interpreter, */
15607
15608 Py_XDECREF(module);
15609
15610 wsgi_release_interpreter(interp);
15611
15612 return allow;
15613 }
15614
wsgi_hook_access_checker(request_rec * r)15615 static int wsgi_hook_access_checker(request_rec *r)
15616 {
15617 WSGIRequestConfig *config;
15618
15619 int allow = 0;
15620 const char *host = NULL;
15621
15622 config = wsgi_create_req_config(r->pool, r);
15623
15624 if (!config->access_script)
15625 return DECLINED;
15626
15627 host = ap_get_remote_host(r->connection, r->per_dir_config,
15628 REMOTE_HOST, NULL);
15629
15630 #if AP_MODULE_MAGIC_AT_LEAST(20111130,0)
15631 if (!host)
15632 host = r->useragent_ip;
15633 #else
15634 if (!host)
15635 host = r->connection->remote_ip;
15636 #endif
15637
15638 allow = wsgi_allow_access(r, config, host);
15639
15640 if (allow < 0)
15641 return DECLINED;
15642 else if (allow)
15643 return OK;
15644
15645 if (ap_satisfies(r) != SATISFY_ANY || !ap_some_auth_required(r)) {
15646 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "mod_wsgi (pid=%d): "
15647 "Client denied by server configuration: '%s'.",
15648 getpid(), r->filename);
15649 }
15650
15651 return HTTP_FORBIDDEN;
15652 }
15653
15654 #if !defined(MOD_WSGI_WITH_AUTHN_PROVIDER)
wsgi_hook_check_user_id(request_rec * r)15655 static int wsgi_hook_check_user_id(request_rec *r)
15656 {
15657 WSGIRequestConfig *config;
15658
15659 int status = -1;
15660
15661 const char *password;
15662
15663 InterpreterObject *interp = NULL;
15664 PyObject *modules = NULL;
15665 PyObject *module = NULL;
15666 char *name = NULL;
15667 int exists = 0;
15668
15669 const char *script;
15670 const char *group;
15671
15672 if ((status = ap_get_basic_auth_pw(r, &password)))
15673 return status;
15674
15675 config = wsgi_create_req_config(r->pool, r);
15676
15677 if (!config->auth_user_script)
15678 return DECLINED;
15679
15680 /*
15681 * Acquire the desired python interpreter. Once this is done
15682 * it is safe to start manipulating python objects.
15683 */
15684
15685 script = config->auth_user_script->handler_script;
15686 group = wsgi_server_group(r, config->auth_user_script->application_group);
15687
15688 interp = wsgi_acquire_interpreter(group);
15689
15690 if (!interp) {
15691 ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r,
15692 "mod_wsgi (pid=%d): Cannot acquire interpreter '%s'.",
15693 getpid(), group);
15694
15695 return HTTP_INTERNAL_SERVER_ERROR;
15696 }
15697
15698 /* Calculate the Python module name to be used for script. */
15699
15700 name = wsgi_module_name(r->pool, script);
15701
15702 /*
15703 * Use a lock around the check to see if the module is
15704 * already loaded and the import of the module to prevent
15705 * two request handlers trying to import the module at the
15706 * same time.
15707 */
15708
15709 #if APR_HAS_THREADS
15710 Py_BEGIN_ALLOW_THREADS
15711 apr_thread_mutex_lock(wsgi_module_lock);
15712 Py_END_ALLOW_THREADS
15713 #endif
15714
15715 modules = PyImport_GetModuleDict();
15716 module = PyDict_GetItemString(modules, name);
15717
15718 Py_XINCREF(module);
15719
15720 if (module)
15721 exists = 1;
15722
15723 /*
15724 * If script reloading is enabled and the module for it has
15725 * previously been loaded, see if it has been modified since
15726 * the last time it was accessed.
15727 */
15728
15729 if (module && config->script_reloading) {
15730 if (wsgi_reload_required(r->pool, r, script, module, NULL)) {
15731 /*
15732 * Script file has changed. Only support module
15733 * reloading for authentication scripts. Remove the
15734 * module from the modules dictionary before
15735 * reloading it again. If code is executing within
15736 * the module at the time, the callers reference
15737 * count on the module should ensure it isn't
15738 * actually destroyed until it is finished.
15739 */
15740
15741 Py_DECREF(module);
15742 module = NULL;
15743
15744 PyDict_DelItemString(modules, name);
15745 }
15746 }
15747
15748 if (!module) {
15749 module = wsgi_load_source(r->pool, r, name, exists, script,
15750 "", group, 0);
15751 }
15752
15753 /* Safe now to release the module lock. */
15754
15755 #if APR_HAS_THREADS
15756 apr_thread_mutex_unlock(wsgi_module_lock);
15757 #endif
15758
15759 /* Log any details of exceptions if import failed. */
15760
15761 if (PyErr_Occurred())
15762 wsgi_log_python_error(r, NULL, script, 0);
15763
15764 /* Assume an internal server error unless everything okay. */
15765
15766 status = HTTP_INTERNAL_SERVER_ERROR;
15767
15768 /* Determine if script exists and execute it. */
15769
15770 if (module) {
15771 PyObject *module_dict = NULL;
15772 PyObject *object = NULL;
15773
15774 module_dict = PyModule_GetDict(module);
15775 object = PyDict_GetItemString(module_dict, "check_password");
15776
15777 if (object) {
15778 PyObject *vars = NULL;
15779 PyObject *args = NULL;
15780 PyObject *result = NULL;
15781 PyObject *method = NULL;
15782
15783 AuthObject *adapter = NULL;
15784
15785 adapter = newAuthObject(r, config);
15786
15787 if (adapter) {
15788 vars = Auth_environ(adapter, group);
15789
15790 Py_INCREF(object);
15791 args = Py_BuildValue("(Oss)", vars, r->user, password);
15792 result = PyEval_CallObject(object, args);
15793 Py_DECREF(args);
15794 Py_DECREF(object);
15795 Py_DECREF(vars);
15796
15797 if (result) {
15798 if (result == Py_None) {
15799 if (config->user_authoritative) {
15800 ap_note_basic_auth_failure(r);
15801 status = HTTP_UNAUTHORIZED;
15802
15803 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
15804 "mod_wsgi (pid=%d): User '%s' not "
15805 "found in executing authentication "
15806 "script '%s', for uri '%s'.",
15807 getpid(), r->user, script, r->uri);
15808 }
15809 else
15810 status = DECLINED;
15811 }
15812 else if (result == Py_True) {
15813 status = OK;
15814 }
15815 else if (result == Py_False) {
15816 ap_note_basic_auth_failure(r);
15817 status = HTTP_UNAUTHORIZED;
15818
15819 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
15820 "mod_wsgi (pid=%d): Password mismatch "
15821 "for user '%s' in executing "
15822 "authentication script '%s', for uri "
15823 "'%s'.", getpid(), r->user, script,
15824 r->uri);
15825 }
15826 else {
15827 PyErr_SetString(PyExc_TypeError, "Basic auth "
15828 "provider must return True, False "
15829 "or None");
15830 }
15831
15832 Py_DECREF(result);
15833 }
15834
15835 /*
15836 * Wipe out references to Apache request object
15837 * held by Python objects, so can detect when an
15838 * application holds on to the transient Python
15839 * objects beyond the life of the request and
15840 * thus raise an exception if they are used.
15841 */
15842
15843 adapter->r = NULL;
15844
15845 /* Log any details of exceptions if execution failed. */
15846
15847 if (PyErr_Occurred())
15848 wsgi_log_python_error(r, NULL, script, 0);
15849
15850 /* Close the log object so data is flushed. */
15851
15852 method = PyObject_GetAttrString(adapter->log, "close");
15853
15854 if (!method) {
15855 PyErr_Format(PyExc_AttributeError,
15856 "'%s' object has no attribute 'close'",
15857 adapter->log->ob_type->tp_name);
15858 }
15859 else {
15860 args = PyTuple_New(0);
15861 result = PyEval_CallObject(method, args);
15862 Py_XDECREF(result);
15863 Py_DECREF(args);
15864 }
15865
15866 /* Log any details of exceptions if execution failed. */
15867
15868 if (PyErr_Occurred())
15869 wsgi_log_python_error(r, NULL, script, 0);
15870
15871 Py_XDECREF(method);
15872
15873 /* No longer need adapter object. */
15874
15875 Py_DECREF((PyObject *)adapter);
15876 }
15877 }
15878 else {
15879 Py_BEGIN_ALLOW_THREADS
15880 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
15881 "mod_wsgi (pid=%d): Target WSGI user "
15882 "authentication script '%s' does not provide "
15883 "'Basic' auth provider.", getpid(), script);
15884 Py_END_ALLOW_THREADS
15885 }
15886 }
15887
15888 /* Cleanup and release interpreter, */
15889
15890 Py_XDECREF(module);
15891
15892 wsgi_release_interpreter(interp);
15893
15894 return status;
15895 }
15896 #endif
15897
15898 #if defined(MOD_WSGI_WITH_AUTHZ_PROVIDER)
15899
15900 #if MOD_WSGI_WITH_AUTHZ_PROVIDER_PARSED
wsgi_check_authorization(request_rec * r,const char * require_args,const void * parsed_require_line)15901 static authz_status wsgi_check_authorization(request_rec *r,
15902 const char *require_args,
15903 const void *parsed_require_line)
15904 #else
15905 static authz_status wsgi_check_authorization(request_rec *r,
15906 const char *require_args)
15907 #endif
15908 {
15909 WSGIRequestConfig *config;
15910
15911 apr_table_t *grpstatus = NULL;
15912 const char *t, *w;
15913 int status;
15914
15915 #if AP_MODULE_MAGIC_AT_LEAST(20100714,0)
15916 if (!r->user)
15917 return AUTHZ_DENIED_NO_USER;
15918 #endif
15919
15920 config = wsgi_create_req_config(r->pool, r);
15921
15922 if (!config->auth_group_script) {
15923 ap_log_error(APLOG_MARK, APLOG_ERR, 0, wsgi_server,
15924 "mod_wsgi (pid=%d): Location of WSGI group "
15925 "authorization script not provided.", getpid());
15926
15927 return AUTHZ_DENIED;
15928 }
15929
15930 status = wsgi_groups_for_user(r, config, &grpstatus);
15931
15932 if (status != OK)
15933 return AUTHZ_DENIED;
15934
15935 if (apr_table_elts(grpstatus)->nelts == 0) {
15936 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "mod_wsgi (pid=%d): "
15937 "Authorization of user '%s' to access '%s' failed. "
15938 "User is not a member of any groups.", getpid(),
15939 r->user, r->uri);
15940 return AUTHZ_DENIED;
15941 }
15942
15943 t = require_args;
15944 while ((w = ap_getword_conf(r->pool, &t)) && w[0]) {
15945 if (apr_table_get(grpstatus, w)) {
15946 return AUTHZ_GRANTED;
15947 }
15948 }
15949
15950 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "mod_wsgi (pid=%d): "
15951 "Authorization of user '%s' to access '%s' failed. "
15952 "User is not a member of designated groups.", getpid(),
15953 r->user, r->uri);
15954
15955 return AUTHZ_DENIED;
15956 }
15957
15958 static const authz_provider wsgi_authz_provider =
15959 {
15960 &wsgi_check_authorization,
15961 #if MOD_WSGI_WITH_AUTHZ_PROVIDER_PARSED
15962 NULL,
15963 #endif
15964 };
15965
15966 #else
15967
wsgi_hook_auth_checker(request_rec * r)15968 static int wsgi_hook_auth_checker(request_rec *r)
15969 {
15970 WSGIRequestConfig *config;
15971
15972 int m = r->method_number;
15973 const apr_array_header_t *reqs_arr;
15974 require_line *reqs;
15975 int required_group = 0;
15976 register int x;
15977 const char *t, *w;
15978 apr_table_t *grpstatus = NULL;
15979 char *reason = NULL;
15980
15981 config = wsgi_create_req_config(r->pool, r);
15982
15983 if (!config->auth_group_script)
15984 return DECLINED;
15985
15986 reqs_arr = ap_requires(r);
15987
15988 if (!reqs_arr)
15989 return DECLINED;
15990
15991 reqs = (require_line *)reqs_arr->elts;
15992
15993 for (x = 0; x < reqs_arr->nelts; x++) {
15994
15995 if (!(reqs[x].method_mask & (AP_METHOD_BIT << m))) {
15996 continue;
15997 }
15998
15999 t = reqs[x].requirement;
16000 w = ap_getword_white(r->pool, &t);
16001
16002 #if AP_MODULE_MAGIC_AT_LEAST(20100714,0)
16003 if (!strcasecmp(w, "wsgi-group")) {
16004 #else
16005 if (!strcasecmp(w, "group") || !strcasecmp(w, "wsgi-group")) {
16006 #endif
16007 required_group = 1;
16008
16009 if (!grpstatus) {
16010 int status;
16011
16012 status = wsgi_groups_for_user(r, config, &grpstatus);
16013
16014 if (status != OK)
16015 return status;
16016
16017 if (apr_table_elts(grpstatus)->nelts == 0) {
16018 reason = "User is not a member of any groups";
16019 break;
16020 }
16021 }
16022
16023 while (t[0]) {
16024 w = ap_getword_conf(r->pool, &t);
16025 if (apr_table_get(grpstatus, w)) {
16026 return OK;
16027 }
16028 }
16029 }
16030 }
16031
16032 if (!required_group || !config->group_authoritative)
16033 return DECLINED;
16034
16035 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "mod_wsgi (pid=%d): "
16036 "Authorization of user '%s' to access '%s' failed. %s.",
16037 getpid(), r->user, r->uri, reason ? reason : "User is not "
16038 "a member of designated groups");
16039
16040 ap_note_auth_failure(r);
16041
16042 return HTTP_UNAUTHORIZED;
16043 }
16044
16045 #endif
16046
16047 APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *wsgi_logio_add_bytes_out;
16048
16049 static void ap_logio_add_bytes_out(conn_rec *c, apr_off_t bytes)
16050 {
16051 if (!wsgi_daemon_pool && wsgi_logio_add_bytes_out)
16052 wsgi_logio_add_bytes_out(c, bytes);
16053 }
16054
16055 static int wsgi_hook_logio(apr_pool_t *pconf, apr_pool_t *ptemp,
16056 apr_pool_t *plog, server_rec *s)
16057 {
16058 /*
16059 * This horrible fiddle is to insert a proxy function before
16060 * the normal ap_logio_add_bytes_out() function so that the
16061 * call to it can be disabled when mod_wsgi running in daemon
16062 * mode. If this is not done, then daemon process will crash
16063 * when mod_logio has been loaded.
16064 */
16065
16066 wsgi_logio_add_bytes_out = APR_RETRIEVE_OPTIONAL_FN(ap_logio_add_bytes_out);
16067
16068 APR_REGISTER_OPTIONAL_FN(ap_logio_add_bytes_out);
16069
16070 return OK;
16071 }
16072
16073 static void wsgi_register_hooks(apr_pool_t *p)
16074 {
16075 static const char * const p1[] = { "mod_alias.c", NULL };
16076 static const char * const n1[]= { "mod_userdir.c",
16077 "mod_vhost_alias.c", NULL };
16078
16079 static const char * const n2[] = { "core.c", NULL };
16080
16081 #if !defined(MOD_WSGI_WITH_AUTHN_PROVIDER)
16082 static const char * const p3[] = { "mod_auth.c", NULL };
16083 #endif
16084 #if !defined(MOD_WSGI_WITH_AUTHZ_PROVIDER)
16085 static const char * const n4[] = { "mod_authz_user.c", NULL };
16086 #endif
16087 static const char * const n5[] = { "mod_authz_host.c", NULL };
16088
16089 static const char * const p6[] = { "mod_python.c", NULL };
16090
16091 static const char * const p7[] = { "mod_ssl.c", NULL };
16092
16093 ap_hook_post_config(wsgi_hook_init, p6, NULL, APR_HOOK_MIDDLE);
16094 ap_hook_child_init(wsgi_hook_child_init, p6, NULL, APR_HOOK_MIDDLE);
16095
16096 ap_hook_translate_name(wsgi_hook_intercept, p1, n1, APR_HOOK_MIDDLE);
16097 ap_hook_handler(wsgi_hook_handler, NULL, NULL, APR_HOOK_MIDDLE);
16098
16099 #if defined(MOD_WSGI_WITH_DAEMONS)
16100 ap_hook_post_config(wsgi_hook_logio, NULL, n2, APR_HOOK_REALLY_FIRST);
16101
16102 wsgi_header_filter_handle =
16103 ap_register_output_filter("WSGI_HEADER", wsgi_header_filter,
16104 NULL, AP_FTYPE_PROTOCOL);
16105 #endif
16106
16107 #if !defined(MOD_WSGI_WITH_AUTHN_PROVIDER)
16108 ap_hook_check_user_id(wsgi_hook_check_user_id, p3, NULL, APR_HOOK_MIDDLE);
16109 #else
16110 ap_register_provider(p, AUTHN_PROVIDER_GROUP, "wsgi",
16111 AUTHN_PROVIDER_VERSION, &wsgi_authn_provider);
16112 #endif
16113 #if !defined(MOD_WSGI_WITH_AUTHZ_PROVIDER)
16114 ap_hook_auth_checker(wsgi_hook_auth_checker, NULL, n4, APR_HOOK_MIDDLE);
16115 #else
16116 ap_register_provider(p, AUTHZ_PROVIDER_GROUP, "wsgi-group",
16117 AUTHZ_PROVIDER_VERSION, &wsgi_authz_provider);
16118 #endif
16119 ap_hook_access_checker(wsgi_hook_access_checker, p7, n5, APR_HOOK_MIDDLE);
16120 }
16121
16122 static const command_rec wsgi_commands[] =
16123 {
16124 AP_INIT_RAW_ARGS("WSGIScriptAlias", wsgi_add_script_alias,
16125 NULL, RSRC_CONF, "Map location to target WSGI script file."),
16126 AP_INIT_RAW_ARGS("WSGIScriptAliasMatch", wsgi_add_script_alias,
16127 "*", RSRC_CONF, "Map location pattern to target WSGI script file."),
16128
16129 #if defined(MOD_WSGI_WITH_DAEMONS)
16130 AP_INIT_RAW_ARGS("WSGIDaemonProcess", wsgi_add_daemon_process,
16131 NULL, RSRC_CONF, "Specify details of daemon processes to start."),
16132 AP_INIT_TAKE1("WSGISocketPrefix", wsgi_set_socket_prefix,
16133 NULL, RSRC_CONF, "Path prefix for the daemon process sockets."),
16134 AP_INIT_TAKE1("WSGISocketRotation", wsgi_set_socket_rotation,
16135 NULL, RSRC_CONF, "Enable/Disable rotation of daemon process sockets."),
16136 AP_INIT_TAKE1("WSGIAcceptMutex", wsgi_set_accept_mutex,
16137 NULL, RSRC_CONF, "Set accept mutex type for daemon processes."),
16138
16139 AP_INIT_TAKE1("WSGILazyInitialization", wsgi_set_lazy_initialization,
16140 NULL, RSRC_CONF, "Enable/Disable lazy Python initialization."),
16141 #endif
16142
16143 AP_INIT_TAKE1("WSGIVerboseDebugging", wsgi_set_verbose_debugging,
16144 NULL, RSRC_CONF, "Enable/Disable verbose debugging messages."),
16145
16146 #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 6
16147 AP_INIT_TAKE1("WSGIPy3kWarningFlag", wsgi_set_py3k_warning_flag,
16148 NULL, RSRC_CONF, "Enable/Disable Python 3.0 warnings."),
16149 #endif
16150
16151 #if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 3) || \
16152 (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 6)
16153 AP_INIT_TAKE1("WSGIDontWriteBytecode", wsgi_set_dont_write_bytecode,
16154 NULL, RSRC_CONF, "Enable/Disable writing of byte code."),
16155 #endif
16156
16157 AP_INIT_TAKE1("WSGIPythonWarnings", wsgi_add_python_warnings,
16158 NULL, RSRC_CONF, "Control Python warning messages."),
16159 AP_INIT_TAKE1("WSGIPythonOptimize", wsgi_set_python_optimize,
16160 NULL, RSRC_CONF, "Set level of Python compiler optimisations."),
16161 AP_INIT_TAKE1("WSGIPythonHome", wsgi_set_python_home,
16162 NULL, RSRC_CONF, "Python prefix/exec_prefix absolute path names."),
16163 AP_INIT_TAKE1("WSGIPythonPath", wsgi_set_python_path,
16164 NULL, RSRC_CONF, "Python module search path."),
16165 AP_INIT_TAKE1("WSGIPythonEggs", wsgi_set_python_eggs,
16166 NULL, RSRC_CONF, "Python eggs cache directory."),
16167 AP_INIT_TAKE1("WSGIPythonHashSeed", wsgi_set_python_hash_seed,
16168 NULL, RSRC_CONF, "Python hash seed."),
16169
16170 #if defined(MOD_WSGI_WITH_DAEMONS)
16171 AP_INIT_TAKE1("WSGIRestrictEmbedded", wsgi_set_restrict_embedded,
16172 NULL, RSRC_CONF, "Enable/Disable use of embedded mode."),
16173 #endif
16174 AP_INIT_TAKE1("WSGIRestrictStdin", wsgi_set_restrict_stdin,
16175 NULL, RSRC_CONF, "Enable/Disable restrictions on use of STDIN."),
16176 AP_INIT_TAKE1("WSGIRestrictStdout", wsgi_set_restrict_stdout,
16177 NULL, RSRC_CONF, "Enable/Disable restrictions on use of STDOUT."),
16178 AP_INIT_TAKE1("WSGIRestrictSignal", wsgi_set_restrict_signal,
16179 NULL, RSRC_CONF, "Enable/Disable restrictions on use of signal()."),
16180
16181 AP_INIT_TAKE1("WSGICaseSensitivity", wsgi_set_case_sensitivity,
16182 NULL, RSRC_CONF, "Define whether file system is case sensitive."),
16183
16184 #if defined(MOD_WSGI_WITH_DAEMONS)
16185 AP_INIT_RAW_ARGS("WSGIRestrictProcess", wsgi_set_restrict_process,
16186 NULL, ACCESS_CONF|RSRC_CONF, "Limit selectable WSGI process groups."),
16187 AP_INIT_TAKE1("WSGIProcessGroup", wsgi_set_process_group,
16188 NULL, ACCESS_CONF|RSRC_CONF, "Name of the WSGI process group."),
16189 #endif
16190
16191 AP_INIT_TAKE1("WSGIApplicationGroup", wsgi_set_application_group,
16192 NULL, ACCESS_CONF|RSRC_CONF, "Application interpreter group."),
16193 AP_INIT_TAKE1("WSGICallableObject", wsgi_set_callable_object,
16194 NULL, OR_FILEINFO, "Name of entry point in WSGI script file."),
16195
16196 AP_INIT_RAW_ARGS("WSGIImportScript", wsgi_add_import_script,
16197 NULL, RSRC_CONF, "Location of WSGI import script."),
16198 AP_INIT_RAW_ARGS("WSGIDispatchScript", wsgi_set_dispatch_script,
16199 NULL, ACCESS_CONF|RSRC_CONF, "Location of WSGI dispatch script."),
16200
16201 AP_INIT_TAKE1("WSGIPassApacheRequest", wsgi_set_pass_apache_request,
16202 NULL, ACCESS_CONF|RSRC_CONF, "Enable/Disable Apache request object."),
16203 AP_INIT_TAKE1("WSGIPassAuthorization", wsgi_set_pass_authorization,
16204 NULL, OR_FILEINFO, "Enable/Disable WSGI authorization."),
16205 AP_INIT_TAKE1("WSGIScriptReloading", wsgi_set_script_reloading,
16206 NULL, OR_FILEINFO, "Enable/Disable script reloading mechanism."),
16207 AP_INIT_TAKE1("WSGIErrorOverride", wsgi_set_error_override,
16208 NULL, OR_FILEINFO, "Enable/Disable overriding of error pages."),
16209 AP_INIT_TAKE1("WSGIChunkedRequest", wsgi_set_chunked_request,
16210 NULL, OR_FILEINFO, "Enable/Disable support for chunked requests."),
16211 AP_INIT_TAKE1("WSGIMapHEADToGET", wsgi_set_map_head_to_get,
16212 NULL, OR_FILEINFO, "Enable/Disable mapping of HEAD to GET."),
16213 AP_INIT_TAKE1("WSGIIgnoreActivity", wsgi_set_ignore_activity,
16214 NULL, OR_FILEINFO, "Enable/Disable reset of inactvity timeout."),
16215
16216 AP_INIT_RAW_ARGS("WSGITrustedProxyHeaders", wsgi_set_trusted_proxy_headers,
16217 NULL, OR_FILEINFO, "Specify a list of trusted proxy headers."),
16218 AP_INIT_RAW_ARGS("WSGITrustedProxies", wsgi_set_trusted_proxies,
16219 NULL, OR_FILEINFO, "Specify a list of trusted proxies."),
16220
16221 #ifndef WIN32
16222 AP_INIT_TAKE1("WSGIEnableSendfile", wsgi_set_enable_sendfile,
16223 NULL, OR_FILEINFO, "Enable/Disable support for kernel sendfile."),
16224 #endif
16225
16226 AP_INIT_RAW_ARGS("WSGIAccessScript", wsgi_set_access_script,
16227 NULL, OR_AUTHCFG, "Location of WSGI host access script file."),
16228 AP_INIT_RAW_ARGS("WSGIAuthUserScript", wsgi_set_auth_user_script,
16229 NULL, OR_AUTHCFG, "Location of WSGI user auth script file."),
16230 AP_INIT_RAW_ARGS("WSGIAuthGroupScript", wsgi_set_auth_group_script,
16231 NULL, OR_AUTHCFG, "Location of WSGI group auth script file."),
16232 #if !defined(MOD_WSGI_WITH_AUTHN_PROVIDER)
16233 AP_INIT_TAKE1("WSGIUserAuthoritative", wsgi_set_user_authoritative,
16234 NULL, OR_AUTHCFG, "Enable/Disable as being authoritative on users."),
16235 #endif
16236 AP_INIT_TAKE1("WSGIGroupAuthoritative", wsgi_set_group_authoritative,
16237 NULL, OR_AUTHCFG, "Enable/Disable as being authoritative on groups."),
16238
16239 AP_INIT_RAW_ARGS("WSGIHandlerScript", wsgi_add_handler_script,
16240 NULL, ACCESS_CONF|RSRC_CONF, "Location of WSGI handler script file."),
16241
16242 AP_INIT_TAKE1("WSGIServerMetrics", wsgi_set_server_metrics,
16243 NULL, RSRC_CONF, "Enabled/Disable access to server metrics."),
16244
16245 AP_INIT_TAKE1("WSGINewRelicConfigFile", wsgi_set_newrelic_config_file,
16246 NULL, RSRC_CONF, "New Relic monitoring agent configuration file."),
16247 AP_INIT_TAKE1("WSGINewRelicEnvironment", wsgi_set_newrelic_environment,
16248 NULL, RSRC_CONF, "New Relic monitoring agent environment."),
16249
16250 { NULL }
16251 };
16252
16253 /* Dispatch list for API hooks */
16254
16255 module AP_MODULE_DECLARE_DATA wsgi_module = {
16256 STANDARD20_MODULE_STUFF,
16257 wsgi_create_dir_config, /* create per-dir config structures */
16258 wsgi_merge_dir_config, /* merge per-dir config structures */
16259 wsgi_create_server_config, /* create per-server config structures */
16260 wsgi_merge_server_config, /* merge per-server config structures */
16261 wsgi_commands, /* table of config file commands */
16262 wsgi_register_hooks /* register hooks */
16263 };
16264
16265 /* ------------------------------------------------------------------------- */
16266
16267 #if defined(_WIN32)
16268 #if PY_MAJOR_VERSION < 3
16269 PyMODINIT_FUNC initmod_wsgi(void)
16270 {
16271 }
16272 #else
16273 PyMODINIT_FUNC PyInit_mod_wsgi(void)
16274 {
16275 return NULL;
16276 }
16277 #endif
16278 #endif
16279
16280 /* ------------------------------------------------------------------------- */
16281
16282 /* vi: set sw=4 expandtab : */
16283