xref: /PHP-5.5/sapi/tux/php_tux.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 #include "php.h"
20 #include "SAPI.h"
21 #include "php_main.h"
22 #include "php_variables.h"
23 
24 #include "ext/standard/php_smart_str.h"
25 
26 #include "tuxmodule.h"
27 
28 #include <sys/uio.h>
29 
30 #if 0
31 #include <pthread.h>
32 #endif
33 
34 void tux_closed_conn(int fd);
35 
36 enum {
37 	PHP_TUX_BACKGROUND_CONN = 1
38 };
39 
40 typedef struct {
41 	user_req_t *req;
42 	void (*on_close)(int);
43 	int tux_action;
44 	struct iovec *header_vec;
45 	int number_vec;
46 } php_tux_globals;
47 
48 static php_tux_globals tux_globals;
49 
50 #define TG(v) (tux_globals.v)
51 
sapi_tux_ub_write(const char * str,uint str_length TSRMLS_DC)52 static int sapi_tux_ub_write(const char *str, uint str_length TSRMLS_DC)
53 {
54 	int n;
55 	int m;
56 	const char *estr;
57 
58 	/* combine headers and body */
59 	if (TG(number_vec)) {
60 		struct iovec *vec = TG(header_vec);
61 
62 		n = TG(number_vec);
63 		vec[n].iov_base = (void *) str;
64 		vec[n++].iov_len = str_length;
65 
66 		/* XXX: this might need more complete error handling */
67 		if ((m = writev(TG(req)->sock, vec, n)) == -1 && errno == EPIPE)
68 			php_handle_aborted_connection();
69 
70 		if (m > 0)
71 			TG(req)->bytes_sent += str_length;
72 
73 		TG(number_vec) = 0;
74 		return str_length;
75 	}
76 
77 	estr = str + str_length;
78 
79 	while (str < estr) {
80 		n = send(TG(req)->sock, str, estr - str, 0);
81 
82 		if (n == -1 && errno == EPIPE)
83 			php_handle_aborted_connection();
84 		if (n == -1 && errno == EAGAIN)
85 			continue;
86 		if (n <= 0)
87 			return n;
88 
89 		str += n;
90 	}
91 
92 	n = str_length - (estr - str);
93 
94 	TG(req)->bytes_sent += n;
95 
96 	return n;
97 }
98 
sapi_tux_send_headers(sapi_headers_struct * sapi_headers)99 static int sapi_tux_send_headers(sapi_headers_struct *sapi_headers)
100 {
101 	char buf[1024];
102 	struct iovec *vec;
103 	int n;
104 	int max_headers;
105 	zend_llist_position pos;
106 	sapi_header_struct *h;
107 	size_t len;
108 	char *status_line;
109 	int locate_cl;
110 	TSRMLS_FETCH();
111 
112 	max_headers = 30;
113 	n = 1;
114 
115 	vec = malloc(sizeof(struct iovec) * max_headers);
116 	status_line = malloc(30);
117 
118 	/* safe sprintf use */
119 	len = slprintf(status_line, 30, "HTTP/1.1 %d NA\r\n", SG(sapi_headers).http_response_code);
120 
121 	vec[0].iov_base = status_line;
122 	vec[0].iov_len = len;
123 
124 	TG(req)->http_status = SG(sapi_headers).http_response_code;
125 
126 	if (TG(tux_action) == TUX_ACTION_FINISH_CLOSE_REQ && TG(req)->http_version == HTTP_1_1)
127 		locate_cl = 1;
128 	else
129 		locate_cl = 0;
130 
131 	h = zend_llist_get_first_ex(&sapi_headers->headers, &pos);
132 	while (h) {
133 		if (locate_cl
134 				&& strncasecmp(h->header, "Content-length:", sizeof("Content-length:")-1) == 0) {
135 			TG(tux_action) = TUX_ACTION_FINISH_REQ;
136 			locate_cl = 0;
137 		}
138 
139 		vec[n].iov_base = h->header;
140 		vec[n++].iov_len = h->header_len;
141 		if (n >= max_headers - 3) {
142 			max_headers *= 2;
143 			vec = realloc(vec, sizeof(struct iovec) * max_headers);
144 		}
145 		vec[n].iov_base = "\r\n";
146 		vec[n++].iov_len = 2;
147 
148 		h = zend_llist_get_next_ex(&sapi_headers->headers, &pos);
149 	}
150 
151 	vec[n].iov_base = "\r\n";
152 	vec[n++].iov_len = 2;
153 
154 	TG(number_vec) = n;
155 	TG(header_vec) = vec;
156 
157 
158 	return SAPI_HEADER_SENT_SUCCESSFULLY;
159 }
160 
sapi_tux_read_post(char * buffer,uint count_bytes)161 static int sapi_tux_read_post(char *buffer, uint count_bytes)
162 {
163 #if 0
164 	int amount = 0;
165 	TSRMLS_FETCH();
166 
167 	TG(req)->objectlen = count_bytes;
168 	TG(req)->object_addr = buffer;
169 	if (tux(TUX_ACTION_READ_POST_DATA, TG(req)))
170 		return 0;
171 
172 	TG(read_post_data) = 1;
173 
174 	return TG(req)->objectlen;
175 #else
176 	return 0;
177 #endif
178 }
179 
sapi_tux_read_cookies(void)180 static char *sapi_tux_read_cookies(void)
181 {
182 	TSRMLS_FETCH();
183 
184 	return TG(req)->cookies;
185 }
186 
187 #define BUF_SIZE 512
188 #define ADD_STRING(name)										\
189 	php_register_variable(name, buf, track_vars_array TSRMLS_CC)
190 
sapi_tux_register_variables(zval * track_vars_array TSRMLS_DC)191 static void sapi_tux_register_variables(zval *track_vars_array TSRMLS_DC)
192 {
193 	char buf[BUF_SIZE + 1];
194 	char *p;
195 	sapi_header_line ctr = {0};
196 
197 	ctr.line = buf;
198 	ctr.line_len = slprintf(buf, sizeof(buf), "Server: %s", TUXAPI_version);
199 	sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC);
200 
201 	php_register_variable("PHP_SELF", SG(request_info).request_uri, track_vars_array TSRMLS_CC);
202 	php_register_variable("SERVER_SOFTWARE", TUXAPI_version, track_vars_array TSRMLS_CC);
203 	php_register_variable("GATEWAY_INTERFACE", "CGI/1.1", track_vars_array TSRMLS_CC);
204 	php_register_variable("REQUEST_METHOD", (char *) SG(request_info).request_method, track_vars_array TSRMLS_CC);
205 	php_register_variable("DOCUMENT_ROOT", TUXAPI_docroot, track_vars_array TSRMLS_CC);
206 	php_register_variable("SERVER_NAME", TUXAPI_servername, track_vars_array TSRMLS_CC);
207 	php_register_variable("REQUEST_URI", SG(request_info).request_uri, track_vars_array TSRMLS_CC);
208 	php_register_variable("PATH_TRANSLATED", SG(request_info).path_translated, track_vars_array TSRMLS_CC);
209 
210 	p = inet_ntoa(TG(req)->client_host);
211 	/* string representation of IPs are never larger than 512 bytes */
212 	if (p) {
213 		memcpy(buf, p, strlen(p) + 1);
214 		ADD_STRING("REMOTE_ADDR");
215 		ADD_STRING("REMOTE_HOST");
216 	}
217 
218 	snprintf(buf, sizeof(buf), "%d", CGI_SERVER_PORT(TG(req)));
219 	ADD_STRING("SERVER_PORT");
220 
221 #if 0
222 	snprintf(buf, BUF_SIZE, "/%s", TG(hc)->pathinfo);
223 	ADD_STRING("PATH_INFO");
224 
225 	snprintf(buf, BUF_SIZE, "/%s", TG(hc)->origfilename);
226 	ADD_STRING("SCRIPT_NAME");
227 #endif
228 
229 #define CONDADD(name, field) 							\
230 	if (TG(req)->field[0]) {								\
231 		php_register_variable(#name, TG(req)->field, track_vars_array TSRMLS_CC); \
232 	}
233 
234 	CONDADD(HTTP_REFERER, referer);
235 	CONDADD(HTTP_USER_AGENT, user_agent);
236 	CONDADD(HTTP_ACCEPT, accept);
237 	CONDADD(HTTP_ACCEPT_ENCODING, accept_encoding);
238 	CONDADD(HTTP_ACCEPT_LANGUAGE, accept_language);
239 	CONDADD(HTTP_COOKIE, cookies);
240 	CONDADD(CONTENT_TYPE, content_type);
241 
242 #if 0
243 	if (TG(hc)->contentlength != -1) {
244 		snprintf(buf, sizeof(buf), "%ld", (long) TG(hc)->contentlength);
245 		ADD_STRING("CONTENT_LENGTH");
246 	}
247 #endif
248 
249 #if 0
250 	if (TG(hc)->authorization[0])
251 		php_register_variable("AUTH_TYPE", "Basic", track_vars_array TSRMLS_CC);
252 #endif
253 }
254 
255 
php_tux_startup(sapi_module_struct * sapi_module)256 static int php_tux_startup(sapi_module_struct *sapi_module)
257 {
258 	if (php_module_startup(sapi_module, NULL, 0)==FAILURE) {
259 		return FAILURE;
260 	} else {
261 		return SUCCESS;
262 	}
263 }
264 
265 static sapi_module_struct tux_sapi_module = {
266 	"tux",
267 	"tux",
268 
269 	php_tux_startup,
270 	php_module_shutdown_wrapper,
271 
272 	NULL,									/* activate */
273 	NULL,									/* deactivate */
274 
275 	sapi_tux_ub_write,
276 	NULL,
277 	NULL,									/* get uid */
278 	NULL,									/* getenv */
279 
280 	php_error,
281 
282 	NULL,
283 	sapi_tux_send_headers,
284 	NULL,
285 	sapi_tux_read_post,
286 	sapi_tux_read_cookies,
287 
288 	sapi_tux_register_variables,
289 	NULL,									/* Log message */
290 	NULL,									/* Get request time */
291 	NULL,									/* Child terminate */
292 
293 	STANDARD_SAPI_MODULE_PROPERTIES
294 };
295 
tux_module_main(TSRMLS_D)296 static void tux_module_main(TSRMLS_D)
297 {
298 	zend_file_handle file_handle;
299 
300 	file_handle.type = ZEND_HANDLE_FILENAME;
301 	file_handle.filename = SG(request_info).path_translated;
302 	file_handle.free_filename = 0;
303 	file_handle.opened_path = NULL;
304 
305 	if (php_request_startup(TSRMLS_C) == FAILURE) {
306 		return;
307 	}
308 
309 	php_execute_script(&file_handle TSRMLS_CC);
310 	php_request_shutdown(NULL);
311 }
312 
tux_request_ctor(TSRMLS_D)313 static void tux_request_ctor(TSRMLS_D)
314 {
315 	char buf[1024];
316 	int offset;
317 	size_t filename_len;
318 	size_t cwd_len;
319 	smart_str s = {0};
320 	char *p;
321 
322 	TG(number_vec) = 0;
323 	TG(header_vec) = NULL;
324 	SG(request_info).query_string = strdup(TG(req)->query);
325 
326 	smart_str_appends_ex(&s, "/", 1);
327 	smart_str_appends_ex(&s, TG(req)->query, 1);
328 	smart_str_0(&s);
329 	p = strchr(s.c, '&');
330 	if (p)
331 		*p = '\0';
332 	SG(request_info).path_translated = s.c;
333 
334 	s.c = NULL;
335 	smart_str_appendc_ex(&s, '/', 1);
336 	smart_str_appends_ex(&s, TG(req)->objectname, 1);
337 	smart_str_0(&s);
338 	SG(request_info).request_uri = s.c;
339 	SG(request_info).request_method = CGI_REQUEST_METHOD(TG(req));
340 	if(TG(req)->http_version == HTTP_1_1) SG(request_info).proto_num = 1001;
341 	else SG(request_info).proto_num = 1000;
342 	SG(sapi_headers).http_response_code = 200;
343 	SG(request_info).content_type = TG(req)->content_type;
344 	SG(request_info).content_length = 0; /* TG(req)->contentlength; */
345 
346 #if 0
347 	php_handle_auth_data(TG(hc)->authorization TSRMLS_CC);
348 #endif
349 }
350 
tux_request_dtor(TSRMLS_D)351 static void tux_request_dtor(TSRMLS_D)
352 {
353 	if (TG(header_vec)) {
354 		/* free status_line */
355 		free(TG(header_vec)[0].iov_base);
356 		free(TG(header_vec));
357 	}
358 	if (SG(request_info).query_string)
359 		free(SG(request_info).query_string);
360 	free(SG(request_info).request_uri);
361 	free(SG(request_info).path_translated);
362 }
363 
364 #if 0
365 static void *separate_thread(void *bla)
366 {
367 	int fd;
368 	int i = 0;
369 
370 	fd = (int) bla;
371 
372 	while (i++ < 5) {
373 		send(fd, "test<br />\n", 9, 0);
374 		sleep(1);
375 	}
376 
377 	tux(TUX_ACTION_CONTINUE_REQ, (user_req_t *) fd);
378 	/* We HAVE to trigger some event on the fd. Otherwise
379 	   fast_thread won't wake up, so that the eventloop
380 	   won't be entered -> TUX hangs */
381 	shutdown(fd, 2);
382 	pthread_exit(NULL);
383 }
384 #endif
385 
TUXAPI_handle_events(user_req_t * req)386 int TUXAPI_handle_events(user_req_t *req)
387 {
388 	TSRMLS_FETCH();
389 
390 	if (req->event == PHP_TUX_BACKGROUND_CONN) {
391 		tux_closed_conn(req->sock);
392 		return tux(TUX_ACTION_FINISH_CLOSE_REQ, req);
393 	}
394 
395 	TG(req) = req;
396 	TG(tux_action) = TUX_ACTION_FINISH_CLOSE_REQ;
397 
398 	tux_request_ctor(TSRMLS_C);
399 
400 	tux_module_main(TSRMLS_C);
401 
402 	tux_request_dtor(TSRMLS_C);
403 
404 	return tux(TG(tux_action), req);
405 }
406 
tux_register_on_close(void (* arg)(int))407 void tux_register_on_close(void (*arg)(int))
408 {
409 	TG(on_close) = arg;
410 }
411 
tux_closed_conn(int fd)412 void tux_closed_conn(int fd)
413 {
414 	TSRMLS_FETCH();
415 
416 	if (TG(on_close)) TG(on_close)(fd);
417 }
418 
tux_get_fd(void)419 int tux_get_fd(void)
420 {
421 	TSRMLS_FETCH();
422 
423 	return TG(req)->sock;
424 }
425 
tux_set_dont_close(void)426 void tux_set_dont_close(void)
427 {
428 	TSRMLS_FETCH();
429 
430 	TG(req)->event = PHP_TUX_BACKGROUND_CONN;
431 	tux(TUX_ACTION_POSTPONE_REQ, TG(req));
432 	TG(tux_action) = TUX_ACTION_EVENTLOOP;
433 }
434 
TUXAPI_init(void)435 void TUXAPI_init(void)
436 {
437 	sapi_startup(&tux_sapi_module);
438 	tux_sapi_module.startup(&tux_sapi_module);
439 	SG(server_context) = (void *) 1;
440 }
441 
doesnotmatter_fini(void)442 void doesnotmatter_fini(void)
443 {
444 	if (SG(server_context) != NULL) {
445 		tux_sapi_module.shutdown(&tux_sapi_module);
446 		sapi_shutdown();
447 	}
448 }
449 
450 /*
451  * Local variables:
452  * tab-width: 4
453  * c-basic-offset: 4
454  * End:
455  * vim600: sw=4 ts=4 fdm=marker
456  * vim<600: sw=4 ts=4
457  */
458