xref: /PHP-5.5/sapi/thttpd/thttpd.c (revision 73c1be26)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2015 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Sascha Schumann <sascha@schumann.cx>                         |
16    +----------------------------------------------------------------------+
17 */
18 
19 /* $Id$ */
20 
21 #include "php.h"
22 #include "SAPI.h"
23 #include "php_main.h"
24 #include "php_thttpd.h"
25 #include "php_variables.h"
26 #include "version.h"
27 #include "php_ini.h"
28 #include "zend_highlight.h"
29 
30 #include "ext/standard/php_smart_str.h"
31 
32 #include <sys/time.h>
33 #include <sys/types.h>
34 #include <sys/uio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 
38 #ifdef HAVE_GETNAMEINFO
39 #include <sys/socket.h>
40 #include <netdb.h>
41 #endif
42 
43 typedef struct {
44 	httpd_conn *hc;
45 	void (*on_close)(int);
46 
47 	size_t unconsumed_length;
48 	smart_str sbuf;
49 	int seen_cl;
50 	int seen_cn;
51 } php_thttpd_globals;
52 
53 #define PHP_SYS_CALL(x) do { x } while (n == -1 && errno == EINTR)
54 
55 #ifdef PREMIUM_THTTPD
56 # define do_keep_alive persistent
57 #endif
58 
59 #ifdef ZTS
60 static int thttpd_globals_id;
61 #define TG(v) TSRMG(thttpd_globals_id, php_thttpd_globals *, v)
62 #else
63 static php_thttpd_globals thttpd_globals;
64 #define TG(v) (thttpd_globals.v)
65 #endif
66 
sapi_thttpd_ub_write(const char * str,uint str_length TSRMLS_DC)67 static int sapi_thttpd_ub_write(const char *str, uint str_length TSRMLS_DC)
68 {
69 	int n;
70 	uint sent = 0;
71 
72 	if (TG(sbuf).c != 0) {
73 		smart_str_appendl_ex(&TG(sbuf), str, str_length, 1);
74 		return str_length;
75 	}
76 
77 	while (str_length > 0) {
78 		PHP_SYS_CALL(n = send(TG(hc)->conn_fd, str, str_length, 0););
79 
80 		if (n == -1) {
81 			if (errno == EAGAIN) {
82 				smart_str_appendl_ex(&TG(sbuf), str, str_length, 1);
83 
84 				return sent + str_length;
85 			} else
86 				php_handle_aborted_connection();
87 		}
88 
89 		TG(hc)->bytes_sent += n;
90 		str += n;
91 		sent += n;
92 		str_length -= n;
93 	}
94 
95 	return sent;
96 }
97 
98 #define COMBINE_HEADERS 64
99 
100 #if defined(IOV_MAX)
101 # if IOV_MAX - 64 <= 0
102 #  define SERIALIZE_HEADERS
103 # endif
104 #endif
105 
do_writev(struct iovec * vec,int nvec,int len TSRMLS_DC)106 static int do_writev(struct iovec *vec, int nvec, int len TSRMLS_DC)
107 {
108 	int n;
109 
110 	assert(nvec <= IOV_MAX);
111 
112 	if (TG(sbuf).c == 0) {
113 		PHP_SYS_CALL(n = writev(TG(hc)->conn_fd, vec, nvec););
114 
115 		if (n == -1) {
116 			if (errno == EAGAIN) {
117 				n = 0;
118 			} else {
119 				php_handle_aborted_connection();
120 			}
121 		}
122 
123 
124 		TG(hc)->bytes_sent += n;
125 	} else {
126 		n = 0;
127 	}
128 
129 	if (n < len) {
130 		int i;
131 
132 		/* merge all unwritten data into sbuf */
133 		for (i = 0; i < nvec; vec++, i++) {
134 			/* has this vector been written completely? */
135 			if (n >= vec->iov_len) {
136 				/* yes, proceed */
137 				n -= vec->iov_len;
138 				continue;
139 			}
140 
141 			if (n > 0) {
142 				/* adjust vector */
143 				vec->iov_base = (char *) vec->iov_base + n;
144 				vec->iov_len -= n;
145 				n = 0;
146 			}
147 
148 			smart_str_appendl_ex(&TG(sbuf), vec->iov_base, vec->iov_len, 1);
149 		}
150 	}
151 
152 	return 0;
153 }
154 
155 #ifdef SERIALIZE_HEADERS
156 # define ADD_VEC(str,l) smart_str_appendl(&vec_str, (str), (l))
157 # define VEC_BASE() smart_str vec_str = {0}
158 # define VEC_FREE() smart_str_free(&vec_str)
159 #else
160 # define ADD_VEC(str,l) vec[n].iov_base=str;len += (vec[n].iov_len=l); n++
161 # define VEC_BASE() struct iovec vec[COMBINE_HEADERS]
162 # define VEC_FREE() do {} while (0)
163 #endif
164 
165 #define ADD_VEC_S(str) ADD_VEC((str), sizeof(str)-1)
166 
167 #define CL_TOKEN "Content-length: "
168 #define CN_TOKEN "Connection: "
169 #define KA_DO "Connection: keep-alive\r\n"
170 #define KA_NO "Connection: close\r\n"
171 #define DEF_CT "Content-Type: text/html\r\n"
172 
sapi_thttpd_send_headers(sapi_headers_struct * sapi_headers TSRMLS_DC)173 static int sapi_thttpd_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
174 {
175 	char buf[1024], *p;
176 	VEC_BASE();
177 	int n = 0;
178 	zend_llist_position pos;
179 	sapi_header_struct *h;
180 	size_t len = 0;
181 
182 	if (!SG(sapi_headers).http_status_line) {
183 		ADD_VEC_S("HTTP/1.1 ");
184 		p = smart_str_print_long(buf+sizeof(buf)-1,
185 				SG(sapi_headers).http_response_code);
186 		ADD_VEC(p, strlen(p));
187 		ADD_VEC_S(" HTTP\r\n");
188 	} else {
189 		ADD_VEC(SG(sapi_headers).http_status_line,
190 				strlen(SG(sapi_headers).http_status_line));
191 		ADD_VEC("\r\n", 2);
192 	}
193 	TG(hc)->status = SG(sapi_headers).http_response_code;
194 
195 	if (SG(sapi_headers).send_default_content_type) {
196 		ADD_VEC(DEF_CT, strlen(DEF_CT));
197 	}
198 
199 	h = zend_llist_get_first_ex(&sapi_headers->headers, &pos);
200 	while (h) {
201 
202 		switch (h->header[0]) {
203 			case 'c': case 'C':
204 				if (!TG(seen_cl) && strncasecmp(h->header, CL_TOKEN, sizeof(CL_TOKEN)-1) == 0) {
205 					TG(seen_cl) = 1;
206 				} else if (!TG(seen_cn) && strncasecmp(h->header, CN_TOKEN, sizeof(CN_TOKEN)-1) == 0) {
207 					TG(seen_cn) = 1;
208 				}
209 		}
210 
211 		ADD_VEC(h->header, h->header_len);
212 #ifndef SERIALIZE_HEADERS
213 		if (n >= COMBINE_HEADERS - 1) {
214 			len = do_writev(vec, n, len TSRMLS_CC);
215 			n = 0;
216 		}
217 #endif
218 		ADD_VEC("\r\n", 2);
219 
220 		h = zend_llist_get_next_ex(&sapi_headers->headers, &pos);
221 	}
222 
223 	if (TG(seen_cl) && !TG(seen_cn) && TG(hc)->do_keep_alive) {
224 		ADD_VEC(KA_DO, sizeof(KA_DO)-1);
225 	} else {
226 		TG(hc)->do_keep_alive = 0;
227 		ADD_VEC(KA_NO, sizeof(KA_NO)-1);
228 	}
229 
230 	ADD_VEC("\r\n", 2);
231 
232 #ifdef SERIALIZE_HEADERS
233 	sapi_thttpd_ub_write(vec_str.c, vec_str.len TSRMLS_CC);
234 #else
235 	do_writev(vec, n, len TSRMLS_CC);
236 #endif
237 
238 	VEC_FREE();
239 
240 	return SAPI_HEADER_SENT_SUCCESSFULLY;
241 }
242 
243 /* to understand this, read cgi_interpose_input() in libhttpd.c */
244 #define SIZEOF_UNCONSUMED_BYTES() (TG(hc)->read_idx - TG(hc)->checked_idx)
245 #define CONSUME_BYTES(n) do { TG(hc)->checked_idx += (n); } while (0)
246 
247 
sapi_thttpd_read_post(char * buffer,uint count_bytes TSRMLS_DC)248 static int sapi_thttpd_read_post(char *buffer, uint count_bytes TSRMLS_DC)
249 {
250 	size_t read_bytes = 0;
251 
252 	if (TG(unconsumed_length) > 0) {
253 		read_bytes = MIN(TG(unconsumed_length), count_bytes);
254 		memcpy(buffer, TG(hc)->read_buf + TG(hc)->checked_idx, read_bytes);
255 		TG(unconsumed_length) -= read_bytes;
256 		CONSUME_BYTES(read_bytes);
257 	}
258 
259 	return read_bytes;
260 }
261 
sapi_thttpd_read_cookies(TSRMLS_D)262 static char *sapi_thttpd_read_cookies(TSRMLS_D)
263 {
264 	return TG(hc)->cookie;
265 }
266 
267 #define BUF_SIZE 512
268 #define ADD_STRING_EX(name,buf)									\
269 	php_register_variable(name, buf, track_vars_array TSRMLS_CC)
270 #define ADD_STRING(name) ADD_STRING_EX((name), buf)
271 
sapi_thttpd_register_variables(zval * track_vars_array TSRMLS_DC)272 static void sapi_thttpd_register_variables(zval *track_vars_array TSRMLS_DC)
273 {
274 	char buf[BUF_SIZE + 1];
275 	char *p;
276 
277 	php_register_variable("PHP_SELF", SG(request_info).request_uri, track_vars_array TSRMLS_CC);
278 	php_register_variable("SERVER_SOFTWARE", SERVER_SOFTWARE, track_vars_array TSRMLS_CC);
279 	php_register_variable("GATEWAY_INTERFACE", "CGI/1.1", track_vars_array TSRMLS_CC);
280 	php_register_variable("REQUEST_METHOD", (char *) SG(request_info).request_method, track_vars_array TSRMLS_CC);
281 	php_register_variable("REQUEST_URI", SG(request_info).request_uri, track_vars_array TSRMLS_CC);
282 	php_register_variable("PATH_TRANSLATED", SG(request_info).path_translated, track_vars_array TSRMLS_CC);
283 
284 	if (TG(hc)->one_one) {
285 		php_register_variable("SERVER_PROTOCOL", "HTTP/1.1", track_vars_array TSRMLS_CC);
286 	} else {
287 		php_register_variable("SERVER_PROTOCOL", "HTTP/1.0", track_vars_array TSRMLS_CC);
288 	}
289 
290 	p = httpd_ntoa(&TG(hc)->client_addr);
291 
292 	ADD_STRING_EX("REMOTE_ADDR", p);
293 	ADD_STRING_EX("REMOTE_HOST", p);
294 
295 	ADD_STRING_EX("SERVER_PORT",
296 			smart_str_print_long(buf + sizeof(buf) - 1,
297 				TG(hc)->hs->port));
298 
299 	buf[0] = '/';
300 	memcpy(buf + 1, TG(hc)->pathinfo, strlen(TG(hc)->pathinfo) + 1);
301 	ADD_STRING("PATH_INFO");
302 
303 	buf[0] = '/';
304 	memcpy(buf + 1, TG(hc)->origfilename, strlen(TG(hc)->origfilename) + 1);
305 	ADD_STRING("SCRIPT_NAME");
306 
307 #define CONDADD(name, field) 							\
308 	if (TG(hc)->field[0]) {								\
309 		php_register_variable(#name, TG(hc)->field, track_vars_array TSRMLS_CC); \
310 	}
311 
312 	CONDADD(QUERY_STRING, query);
313 	CONDADD(HTTP_HOST, hdrhost);
314 	CONDADD(HTTP_REFERER, referer);
315 	CONDADD(HTTP_USER_AGENT, useragent);
316 	CONDADD(HTTP_ACCEPT, accept);
317 	CONDADD(HTTP_ACCEPT_LANGUAGE, acceptl);
318 	CONDADD(HTTP_ACCEPT_ENCODING, accepte);
319 	CONDADD(HTTP_COOKIE, cookie);
320 	CONDADD(CONTENT_TYPE, contenttype);
321 	CONDADD(REMOTE_USER, remoteuser);
322 	CONDADD(SERVER_PROTOCOL, protocol);
323 
324 	if (TG(hc)->contentlength != -1) {
325 		ADD_STRING_EX("CONTENT_LENGTH",
326 				smart_str_print_long(buf + sizeof(buf) - 1,
327 					TG(hc)->contentlength));
328 	}
329 
330 	if (TG(hc)->authorization[0])
331 		php_register_variable("AUTH_TYPE", "Basic", track_vars_array TSRMLS_CC);
332 }
333 
PHP_MINIT_FUNCTION(thttpd)334 static PHP_MINIT_FUNCTION(thttpd)
335 {
336 	return SUCCESS;
337 }
338 
339 static zend_module_entry php_thttpd_module = {
340 	STANDARD_MODULE_HEADER,
341 	"thttpd",
342 	NULL,
343 	PHP_MINIT(thttpd),
344 	NULL,
345 	NULL,
346 	NULL,
347 	NULL, /* info */
348 	NULL,
349 	STANDARD_MODULE_PROPERTIES
350 };
351 
php_thttpd_startup(sapi_module_struct * sapi_module)352 static int php_thttpd_startup(sapi_module_struct *sapi_module)
353 {
354 #if PHP_API_VERSION >= 20020918
355 	if (php_module_startup(sapi_module, &php_thttpd_module, 1) == FAILURE) {
356 #else
357 	if (php_module_startup(sapi_module) == FAILURE
358 			|| zend_startup_module(&php_thttpd_module) == FAILURE) {
359 #endif
360 		return FAILURE;
361 	}
362 	return SUCCESS;
363 }
364 
365 static int sapi_thttpd_get_fd(int *nfd TSRMLS_DC)
366 {
367 	if (nfd) *nfd = TG(hc)->conn_fd;
368 	return SUCCESS;
369 }
370 
371 static sapi_module_struct thttpd_sapi_module = {
372 	"thttpd",
373 	"thttpd",
374 
375 	php_thttpd_startup,
376 	php_module_shutdown_wrapper,
377 
378 	NULL,									/* activate */
379 	NULL,									/* deactivate */
380 
381 	sapi_thttpd_ub_write,
382 	NULL,
383 	NULL,									/* get uid */
384 	NULL,									/* getenv */
385 
386 	php_error,
387 
388 	NULL,
389 	sapi_thttpd_send_headers,
390 	NULL,
391 	sapi_thttpd_read_post,
392 	sapi_thttpd_read_cookies,
393 
394 	sapi_thttpd_register_variables,
395 	NULL,									/* Log message */
396 	NULL,									/* Get request time */
397 	NULL,									/* Child terminate */
398 
399 	NULL,									/* php.ini path override */
400 	NULL,									/* Block interruptions */
401 	NULL,									/* Unblock interruptions */
402 
403 	NULL,
404 	NULL,
405 	NULL,
406 	0,
407 	sapi_thttpd_get_fd
408 };
409 
410 static void thttpd_module_main(int show_source TSRMLS_DC)
411 {
412 	zend_file_handle file_handle;
413 
414 	if (php_request_startup(TSRMLS_C) == FAILURE) {
415 		return;
416 	}
417 
418 	if (show_source) {
419 		zend_syntax_highlighter_ini syntax_highlighter_ini;
420 
421 		php_get_highlight_struct(&syntax_highlighter_ini);
422 		highlight_file(SG(request_info).path_translated, &syntax_highlighter_ini TSRMLS_CC);
423 	} else {
424 		file_handle.type = ZEND_HANDLE_FILENAME;
425 		file_handle.filename = SG(request_info).path_translated;
426 		file_handle.free_filename = 0;
427 		file_handle.opened_path = NULL;
428 
429 		php_execute_script(&file_handle TSRMLS_CC);
430 	}
431 
432 	php_request_shutdown(NULL);
433 }
434 
435 static void thttpd_request_ctor(TSRMLS_D)
436 {
437 	smart_str s = {0};
438 
439 	TG(seen_cl) = 0;
440 	TG(seen_cn) = 0;
441 	TG(sbuf).c = 0;
442 	SG(request_info).query_string = TG(hc)->query?strdup(TG(hc)->query):NULL;
443 
444 	smart_str_appends_ex(&s, TG(hc)->hs->cwd, 1);
445 	smart_str_appends_ex(&s, TG(hc)->expnfilename, 1);
446 	smart_str_0(&s);
447 	SG(request_info).path_translated = s.c;
448 
449 	s.c = NULL;
450 	smart_str_appendc_ex(&s, '/', 1);
451 	smart_str_appends_ex(&s, TG(hc)->origfilename, 1);
452 	smart_str_0(&s);
453 	SG(request_info).request_uri = s.c;
454 	SG(request_info).request_method = httpd_method_str(TG(hc)->method);
455 	if (TG(hc)->one_one) SG(request_info).proto_num = 1001;
456 	else SG(request_info).proto_num = 1000;
457 	SG(sapi_headers).http_response_code = 200;
458 	if (TG(hc)->contenttype)
459 		SG(request_info).content_type = strdup(TG(hc)->contenttype);
460 	SG(request_info).content_length = TG(hc)->contentlength == -1 ? 0
461 		: TG(hc)->contentlength;
462 
463 	TG(unconsumed_length) = SG(request_info).content_length;
464 
465 	php_handle_auth_data(TG(hc)->authorization TSRMLS_CC);
466 }
467 
468 static void thttpd_request_dtor(TSRMLS_D)
469 {
470 	smart_str_free_ex(&TG(sbuf), 1);
471 	if (SG(request_info).query_string)
472 		free(SG(request_info).query_string);
473 	free(SG(request_info).request_uri);
474 	free(SG(request_info).path_translated);
475 	if (SG(request_info).content_type)
476 		free(SG(request_info).content_type);
477 }
478 
479 #ifdef ZTS
480 
481 #ifdef TSRM_ST
482 #define thread_create_simple_detached(n) st_thread_create(n, NULL, 0, 0)
483 #define thread_usleep(n) st_usleep(n)
484 #define thread_exit() st_thread_exit(NULL)
485 /* No preemption, simple operations are safe */
486 #define thread_atomic_inc(n) (++n)
487 #define thread_atomic_dec(n) (--n)
488 #else
489 #error No thread primitives available
490 #endif
491 
492 /* We might want to replace this with a STAILQ */
493 typedef struct qreq {
494 	httpd_conn *hc;
495 	struct qreq *next;
496 } qreq_t;
497 
498 static MUTEX_T qr_lock;
499 static qreq_t *queued_requests;
500 static qreq_t *last_qr;
501 static int nr_free_threads;
502 static int nr_threads;
503 static int max_threads = 50;
504 
505 #define HANDLE_STRINGS() { \
506 	HANDLE_STR(encodedurl); \
507 	HANDLE_STR(decodedurl); \
508 	HANDLE_STR(origfilename); \
509 	HANDLE_STR(expnfilename); \
510 	HANDLE_STR(pathinfo); \
511 	HANDLE_STR(query); \
512 	HANDLE_STR(referer); \
513 	HANDLE_STR(useragent); \
514 	HANDLE_STR(accept); \
515 	HANDLE_STR(accepte); \
516 	HANDLE_STR(acceptl); \
517 	HANDLE_STR(cookie); \
518 	HANDLE_STR(contenttype); \
519 	HANDLE_STR(authorization); \
520 	HANDLE_STR(remoteuser); \
521 	}
522 
523 static httpd_conn *duplicate_conn(httpd_conn *hc, httpd_conn *nhc)
524 {
525 	memcpy(nhc, hc, sizeof(*nhc));
526 
527 #define HANDLE_STR(m) nhc->m = nhc->m ? strdup(nhc->m) : NULL
528 	HANDLE_STRINGS();
529 #undef HANDLE_STR
530 
531 	return nhc;
532 }
533 
534 static void destroy_conn(httpd_conn *hc)
535 {
536 #define HANDLE_STR(m) if (hc->m) free(hc->m)
537 	HANDLE_STRINGS();
538 #undef HANDLE_STR
539 }
540 
541 static httpd_conn *dequeue_request(void)
542 {
543 	httpd_conn *ret = NULL;
544 	qreq_t *m;
545 
546 	tsrm_mutex_lock(qr_lock);
547 	if (queued_requests) {
548 		m = queued_requests;
549 		ret = m->hc;
550 		if (!(queued_requests = m->next))
551 			last_qr = NULL;
552 		free(m);
553 	}
554 	tsrm_mutex_unlock(qr_lock);
555 
556 	return ret;
557 }
558 
559 static void *worker_thread(void *);
560 
561 static void queue_request(httpd_conn *hc)
562 {
563 	qreq_t *m;
564 	httpd_conn *nhc;
565 
566 	/* Mark as long-running request */
567 	hc->file_address = (char *) 1;
568 
569 	/*
570      * We cannot synchronously revoke accesses to hc in the worker
571 	 * thread, so we need to pass a copy of hc to the worker thread.
572 	 */
573 	nhc = malloc(sizeof *nhc);
574 	duplicate_conn(hc, nhc);
575 
576 	/* Allocate request queue container */
577 	m = malloc(sizeof *m);
578 	m->hc = nhc;
579 	m->next = NULL;
580 
581 	tsrm_mutex_lock(qr_lock);
582 	/* Create new threads when reaching a certain threshold */
583 	if (nr_threads < max_threads && nr_free_threads < 2) {
584 		nr_threads++; /* protected by qr_lock */
585 
586 		thread_atomic_inc(nr_free_threads);
587 		thread_create_simple_detached(worker_thread);
588 	}
589 	/* Insert container into request queue */
590 	if (queued_requests)
591 		last_qr->next = m;
592 	else
593 		queued_requests = m;
594 	last_qr = m;
595 	tsrm_mutex_unlock(qr_lock);
596 }
597 
598 static off_t thttpd_real_php_request(httpd_conn *hc, int TSRMLS_DC);
599 
600 static void *worker_thread(void *dummy)
601 {
602 	int do_work = 50;
603 	httpd_conn *hc;
604 
605 	while (do_work) {
606 		hc = dequeue_request();
607 
608 		if (!hc) {
609 /*			do_work--; */
610 			thread_usleep(500000);
611 			continue;
612 		}
613 /*		do_work = 50; */
614 
615 		thread_atomic_dec(nr_free_threads);
616 
617 		thttpd_real_php_request(hc, 0 TSRMLS_CC);
618 		shutdown(hc->conn_fd, 0);
619 		destroy_conn(hc);
620 		free(hc);
621 
622 		thread_atomic_inc(nr_free_threads);
623 	}
624 	thread_atomic_dec(nr_free_threads);
625 	thread_atomic_dec(nr_threads);
626 	thread_exit();
627 }
628 
629 static void remove_dead_conn(int fd)
630 {
631 	qreq_t *m, *prev = NULL;
632 
633 	tsrm_mutex_lock(qr_lock);
634 	m = queued_requests;
635 	while (m) {
636 		if (m->hc->conn_fd == fd) {
637 			if (prev)
638 				if (!(prev->next = m->next))
639 					last_qr = prev;
640 			else
641 				if (!(queued_requests = m->next))
642 					last_qr = NULL;
643 			destroy_conn(m->hc);
644 			free(m->hc);
645 			free(m);
646 			break;
647 		}
648 		prev = m;
649 		m = m->next;
650 	}
651 	tsrm_mutex_unlock(qr_lock);
652 }
653 
654 #endif
655 
656 static off_t thttpd_real_php_request(httpd_conn *hc, int show_source TSRMLS_DC)
657 {
658 	TG(hc) = hc;
659 	hc->bytes_sent = 0;
660 
661 	if (hc->contentlength != -1) {
662 		hc->should_linger = 1;
663 		hc->do_keep_alive = 0;
664 	}
665 
666 	if (hc->contentlength != -1
667 			&& SIZEOF_UNCONSUMED_BYTES() < hc->contentlength) {
668 		hc->read_body_into_mem = 1;
669 		return 0;
670 	}
671 
672 	thttpd_request_ctor(TSRMLS_C);
673 
674 	thttpd_module_main(show_source TSRMLS_CC);
675 
676 	/* disable kl, if no content-length was seen or Connection: was set */
677 	if (TG(seen_cl) == 0 || TG(seen_cn) == 1) {
678 		TG(hc)->do_keep_alive = 0;
679 	}
680 
681 	if (TG(sbuf).c != 0) {
682 		if (TG(hc)->response)
683 			free(TG(hc)->response);
684 
685 		TG(hc)->response = TG(sbuf).c;
686 		TG(hc)->responselen = TG(sbuf).len;
687 		TG(hc)->maxresponse = TG(sbuf).a;
688 
689 		TG(sbuf).c = 0;
690 		TG(sbuf).len = 0;
691 		TG(sbuf).a = 0;
692 	}
693 
694 	thttpd_request_dtor(TSRMLS_C);
695 
696 	return 0;
697 }
698 
699 off_t thttpd_php_request(httpd_conn *hc, int show_source)
700 {
701 #ifdef ZTS
702 	queue_request(hc);
703 #else
704 	TSRMLS_FETCH();
705 	return thttpd_real_php_request(hc, show_source TSRMLS_CC);
706 #endif
707 }
708 
709 void thttpd_register_on_close(void (*arg)(int))
710 {
711 	TSRMLS_FETCH();
712 	TG(on_close) = arg;
713 }
714 
715 void thttpd_closed_conn(int fd)
716 {
717 	TSRMLS_FETCH();
718 	if (TG(on_close)) TG(on_close)(fd);
719 }
720 
721 int thttpd_get_fd(void)
722 {
723 	TSRMLS_FETCH();
724 	return TG(hc)->conn_fd;
725 }
726 
727 void thttpd_set_dont_close(void)
728 {
729 	TSRMLS_FETCH();
730 #ifndef PREMIUM_THTTPD
731 	TG(hc)->file_address = (char *) 1;
732 #endif
733 }
734 
735 
736 void thttpd_php_init(void)
737 {
738 	char *ini;
739 
740 #ifdef ZTS
741 	tsrm_startup(1, 1, 0, NULL);
742 	ts_allocate_id(&thttpd_globals_id, sizeof(php_thttpd_globals), NULL, NULL);
743 	qr_lock = tsrm_mutex_alloc();
744 	thttpd_register_on_close(remove_dead_conn);
745 #endif
746 
747 	if ((ini = getenv("PHP_INI_PATH"))) {
748 		thttpd_sapi_module.php_ini_path_override = ini;
749 	}
750 
751 	sapi_startup(&thttpd_sapi_module);
752 	thttpd_sapi_module.startup(&thttpd_sapi_module);
753 
754 	{
755 		TSRMLS_FETCH();
756 
757 		SG(server_context) = (void *) 1;
758 	}
759 }
760 
761 void thttpd_php_shutdown(void)
762 {
763 	TSRMLS_FETCH();
764 
765 	if (SG(server_context) != NULL) {
766 		thttpd_sapi_module.shutdown(&thttpd_sapi_module);
767 		sapi_shutdown();
768 #ifdef ZTS
769 		tsrm_shutdown();
770 #endif
771 	}
772 }
773