34 define('HTTPD_ICON_SCRIPT', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGSSURBVCjPVVFNSwJhEF78Ad79Cf6PvXQRsotUlzKICosuRYmR2RJR0KE6lBFFZVEbpFBSqKu2rum6llFS9HHI4iUhT153n6ZtIWMOM+/MM88z7wwH7s9Ub16SJcnbmrNcxVm2q7Z8/QPvEOtntpj92NkCqITLepEpjix7xQtiLOoQ2b6+E7YAN/5nfOEJ2WbKqOIOJ4bYVMEQx4LfBBQDsvFMhUcCVU1/CxVXmDBGA5ZETrhDCQVcYAPbyEJBhvrnBVPiSpNr6cYDNCQwo4zzU/ySckkgDYuNuVpI42T9k4gLKGMPs/xPzzovQiY2hQYe0jlJfyNNhTqiWDYBq/wBMcSRpnyPzu1oS7WtxjVBSthU1vgVksiQ3Dn6Gp5ah2YOKQo5GiuHPA6xT1EKpxQNCNYejgIR457KKio0S56YckjSa9jo//3mrj+BV0QQagqGTOo+Y7gZIf1puP3WHoLhEb2PjTlCTCWGXtbp8DCX3hZuOdaIc9A+aQvWk4ihq95p67a7nP+u+Ws+r0dql9z/zv0NCYhdCPKZ7oYAAAAASUVORK5CYII='); |
34 define('HTTPD_ICON_SCRIPT', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGSSURBVCjPVVFNSwJhEF78Ad79Cf6PvXQRsotUlzKICosuRYmR2RJR0KE6lBFFZVEbpFBSqKu2rum6llFS9HHI4iUhT153n6ZtIWMOM+/MM88z7wwH7s9Ub16SJcnbmrNcxVm2q7Z8/QPvEOtntpj92NkCqITLepEpjix7xQtiLOoQ2b6+E7YAN/5nfOEJ2WbKqOIOJ4bYVMEQx4LfBBQDsvFMhUcCVU1/CxVXmDBGA5ZETrhDCQVcYAPbyEJBhvrnBVPiSpNr6cYDNCQwo4zzU/ySckkgDYuNuVpI42T9k4gLKGMPs/xPzzovQiY2hQYe0jlJfyNNhTqiWDYBq/wBMcSRpnyPzu1oS7WtxjVBSthU1vgVksiQ3Dn6Gp5ah2YOKQo5GiuHPA6xT1EKpxQNCNYejgIR457KKio0S56YckjSa9jo//3mrj+BV0QQagqGTOo+Y7gZIf1puP3WHoLhEb2PjTlCTCWGXtbp8DCX3hZuOdaIc9A+aQvWk4ihq95p67a7nP+u+Ws+r0dql9z/zv0NCYhdCPKZ7oYAAAAASUVORK5CYII='); |
35 define('HTTPD_ICON_FOLDER', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGrSURBVDjLxZO7ihRBFIa/6u0ZW7GHBUV0UQQTZzd3QdhMQxOfwMRXEANBMNQX0MzAzFAwEzHwARbNFDdwEd31Mj3X7a6uOr9BtzNjYjKBJ6nicP7v3KqcJFaxhBVtZUAK8OHlld2st7Xl3DJPVONP+zEUV4HqL5UDYHr5xvuQAjgl/Qs7TzvOOVAjxjlC+ePSwe6DfbVegLVuT4r14eTr6zvA8xSAoBLzx6pvj4l+DZIezuVkG9fY2H7YRQIMZIBwycmzH1/s3F8AapfIPNF3kQk7+kw9PWBy+IZOdg5Ug3mkAATy/t0usovzGeCUWTjCz0B+Sj0ekfdvkZ3abBv+U4GaCtJ1iEm6ANQJ6fEzrG/engcKw/wXQvEKxSEKQxRGKE7Izt+DSiwBJMUSm71rguMYhQKrBygOIRStf4TiFFRBvbRGKiQLWP29yRSHKBTtfdBmHs0BUpgvtgF4yRFR+NUKi0XZcYjCeCG2smkzLAHkbRBmP0/Uk26O5YnUActBp1GsAI+S5nRJJJal5K1aAMrq0d6Tm9uI6zjyf75dAe6tx/SsWeD//o2/Ab6IH3/h25pOAAAAAElFTkSuQmCC'); |
35 define('HTTPD_ICON_FOLDER', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGrSURBVDjLxZO7ihRBFIa/6u0ZW7GHBUV0UQQTZzd3QdhMQxOfwMRXEANBMNQX0MzAzFAwEzHwARbNFDdwEd31Mj3X7a6uOr9BtzNjYjKBJ6nicP7v3KqcJFaxhBVtZUAK8OHlld2st7Xl3DJPVONP+zEUV4HqL5UDYHr5xvuQAjgl/Qs7TzvOOVAjxjlC+ePSwe6DfbVegLVuT4r14eTr6zvA8xSAoBLzx6pvj4l+DZIezuVkG9fY2H7YRQIMZIBwycmzH1/s3F8AapfIPNF3kQk7+kw9PWBy+IZOdg5Ug3mkAATy/t0usovzGeCUWTjCz0B+Sj0ekfdvkZ3abBv+U4GaCtJ1iEm6ANQJ6fEzrG/engcKw/wXQvEKxSEKQxRGKE7Izt+DSiwBJMUSm71rguMYhQKrBygOIRStf4TiFFRBvbRGKiQLWP29yRSHKBTtfdBmHs0BUpgvtgF4yRFR+NUKi0XZcYjCeCG2smkzLAHkbRBmP0/Uk26O5YnUActBp1GsAI+S5nRJJJal5K1aAMrq0d6Tm9uI6zjyf75dAe6tx/SsWeD//o2/Ab6IH3/h25pOAAAAAElFTkSuQmCC'); |
36 define('HTTPD_ICON_FILE', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAINSURBVBgZBcG/r55zGAfg6/4+z3va01NHlYgzEfE7MdCIGISFgS4Gk8ViYyM2Mdlsko4GSf8Do0FLRCIkghhYJA3aVBtEz3nP89wf11VJvPDepdd390+8Nso5nESBQoq0pfvXm9fzWf19453LF85vASqJlz748vInb517dIw6EyYBIIG49u+xi9/c9MdvR//99MPPZ7+4cP4IZhhTPbwzT2d+vGoaVRRp1rRliVvHq+cfvM3TD82+7mun0o/ceO7NT+/4/KOXjwZU1ekk0840bAZzMQ2mooqh0A72d5x/6sB9D5zYnff3PoYBoWBgFKPKqDKqjCpjKr//dcu9p489dra88cydps30KswACfNEKanSaxhlntjJ8Mv12Paie+vZ+0+oeSwwQ0Iw1xAR1CiFNJkGO4wu3ZMY1AAzBI0qSgmCNJsJUEOtJSMaCTBDLyQ0CknAGOgyTyFFiLI2awMzdEcSQgSAAKVUmAeNkxvWJWCGtVlDmgYQ0GFtgg4pNtOwbBcwQy/Rife/2yrRRVI0qYCEBly8Z+P4qMEMy7JaVw72N568e+iwhrXoECQkfH91kY7jwwXMsBx1L93ZruqrK6uuiAIdSnTIKKPLPFcvay8ww/Hh+ufeznTXu49v95IMoQG3784gYXdTqvRmqn/Wpa/ADFX58MW3L71SVU9ETgEIQQQIOOzub+fhIvwPRDgeVjWDahIAAAAASUVORK5CYII='); |
36 define('HTTPD_ICON_FILE', 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAINSURBVBgZBcG/r55zGAfg6/4+z3va01NHlYgzEfE7MdCIGISFgS4Gk8ViYyM2Mdlsko4GSf8Do0FLRCIkghhYJA3aVBtEz3nP89wf11VJvPDepdd390+8Nso5nESBQoq0pfvXm9fzWf19453LF85vASqJlz748vInb517dIw6EyYBIIG49u+xi9/c9MdvR//99MPPZ7+4cP4IZhhTPbwzT2d+vGoaVRRp1rRliVvHq+cfvM3TD82+7mun0o/ceO7NT+/4/KOXjwZU1ekk0840bAZzMQ2mooqh0A72d5x/6sB9D5zYnff3PoYBoWBgFKPKqDKqjCpjKr//dcu9p489dra88cydps30KswACfNEKanSaxhlntjJ8Mv12Paie+vZ+0+oeSwwQ0Iw1xAR1CiFNJkGO4wu3ZMY1AAzBI0qSgmCNJsJUEOtJSMaCTBDLyQ0CknAGOgyTyFFiLI2awMzdEcSQgSAAKVUmAeNkxvWJWCGtVlDmgYQ0GFtgg4pNtOwbBcwQy/Rife/2yrRRVI0qYCEBly8Z+P4qMEMy7JaVw72N568e+iwhrXoECQkfH91kY7jwwXMsBx1L93ZruqrK6uuiAIdSnTIKKPLPFcvay8ww/Hh+ufeznTXu49v95IMoQG3784gYXdTqvRmqn/Wpa/ADFX58MW3L71SVU9ETgEIQQQIOOzub+fhIvwPRDgeVjWDahIAAAAASUVORK5CYII='); |
37 |
37 |
38 /** |
38 /** |
|
39 * Abstraction layer to use |
|
40 */ |
|
41 |
|
42 define('HTTPD_SOCKET_LAYER', 'Stream'); |
|
43 |
|
44 /** |
39 * Simple but full-featured embedded web server written in PHP. |
45 * Simple but full-featured embedded web server written in PHP. |
40 * @package Amarok |
46 * @package Amarok |
41 * @subpackage WebControl |
47 * @subpackage WebControl |
42 * @author Dan Fuhry |
48 * @author Dan Fuhry |
43 * @license GNU General Public License <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html> |
49 * @license GNU General Public License <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html> |
287 { |
296 { |
288 if ( !defined('HTTPD_WS_CHILD') && $this->socket_initted ) |
297 if ( !defined('HTTPD_WS_CHILD') && $this->socket_initted ) |
289 { |
298 { |
290 if ( function_exists('status') ) |
299 if ( function_exists('status') ) |
291 status('WebServer: destroying socket'); |
300 status('WebServer: destroying socket'); |
292 // http://us3.php.net/manual/en/function.socket-bind.php |
301 $this->server->destroy(); |
293 if ( !@socket_set_option($this->sock, SOL_SOCKET, SO_REUSEADDR, 1) ) |
302 |
294 { |
303 // tell all children to shut down |
295 echo socket_strerror(socket_last_error($sock)) . "\n"; |
304 if ( $this->allow_fork ) |
296 } |
305 { |
297 @socket_shutdown($this->sock, 2); |
306 if ( function_exists('status') ) |
298 @socket_close($this->sock); |
307 status('WebServer: asking all children to exit'); |
|
308 $this->send_ipc_event("die _"); |
|
309 } |
|
310 |
|
311 // that last operation should have been asynchronous, so shut everything down now |
|
312 @socket_shutdown($this->parent_sock); |
|
313 @socket_close($this->parent_sock); |
|
314 } |
|
315 else if ( defined('HTTPD_WS_CHILD') ) |
|
316 { |
|
317 @socket_shutdown($this->child_sock); |
|
318 @socket_close($this->child_sock); |
299 } |
319 } |
300 } |
320 } |
301 |
321 |
302 /** |
322 /** |
303 * Main server loop |
323 * Main server loop |
304 */ |
324 */ |
305 |
325 |
306 function serve() |
326 function serve() |
307 { |
327 { |
|
328 // If we're allowed to use multithreading, set up to handle SIGUSR2 which waits on the child |
|
329 if ( function_exists('pcntl_signal') && $this->allow_fork ) |
|
330 { |
|
331 // required for signal handling to work |
|
332 declare(ticks=1); |
|
333 |
|
334 // trap SIGTERM |
|
335 pcntl_signal(SIGUSR2, array(&$this, '_ipc_event')); |
|
336 |
|
337 if ( !($sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP)) ) |
|
338 { |
|
339 throw new Exception("Could not set up private IPC socket. Reason: " . socket_strerror(socket_last_error())); |
|
340 } |
|
341 |
|
342 $this->parent_sock =& $sockets[0]; |
|
343 $this->child_sock =& $sockets[1]; |
|
344 } |
|
345 |
308 while ( true ) |
346 while ( true ) |
309 { |
347 { |
310 // if this is a child process, we're finished - close up shop |
348 // if this is a child process, we're finished - close up shop |
311 if ( defined('HTTPD_WS_CHILD') && !$this->in_keepalive ) |
349 if ( defined('HTTPD_WS_CHILD') && !$this->in_keepalive ) |
312 { |
350 { |
313 if ( function_exists('status') ) |
351 if ( function_exists('status') ) |
314 status('Exiting child process'); |
352 status('Exiting child process'); |
315 @socket_shutdown($remote); |
353 |
316 @socket_close($remote); |
354 $remote->destroy(); |
|
355 |
|
356 // let the parent know that we're out of here |
|
357 $this->send_ipc_event("exit " . getmypid()); |
|
358 |
|
359 // bye |
317 exit(0); |
360 exit(0); |
318 } |
361 } |
319 |
362 |
320 // wait for connection... |
363 // wait for connection... |
321 // trick from http://us.php.net/manual/en/function.socket-accept.php |
364 // trick from http://us.php.net/manual/en/function.socket-accept.php |
322 if ( !defined('HTTPD_WS_CHILD') ) |
365 if ( !defined('HTTPD_WS_CHILD') ) |
323 { |
366 { |
324 $remote = false; |
367 $remote = $this->server->accept(); |
325 $timeout = 5; |
368 } |
326 switch(@socket_select($r = array($this->sock), $w = array($this->sock), $e = array($this->sock), $timeout)) { |
369 |
327 case 2: |
|
328 break; |
|
329 case 1: |
|
330 $remote = @socket_accept($this->sock); |
|
331 break; |
|
332 case 0: |
|
333 break; |
|
334 } |
|
335 } |
|
336 |
|
337 if ( !$remote ) |
370 if ( !$remote ) |
338 { |
371 { |
339 $this->in_keepalive = false; |
372 $this->in_keepalive = false; |
340 continue; |
373 continue; |
341 } |
374 } |
349 // do nothing; continue responding to request in single-threaded mode |
382 // do nothing; continue responding to request in single-threaded mode |
350 } |
383 } |
351 else if ( $pid ) |
384 else if ( $pid ) |
352 { |
385 { |
353 // we are the parent, continue listening |
386 // we are the parent, continue listening |
354 socket_close($remote); |
387 $remote->soft_shutdown(); |
355 $this->child_list[] = $pid; |
388 $this->child_list[] = $pid; |
356 continue; |
389 continue; |
357 } |
390 } |
358 else |
391 else |
359 { |
392 { |
360 // this is the child |
393 // this is the child |
361 define('HTTPD_WS_CHILD', 1); |
394 define('HTTPD_WS_CHILD', 1); |
362 @socket_set_option($this->sock, SOL_SOCKET, SO_REUSEADDR, 1); |
395 |
363 socket_close($this->sock); |
396 // setup to handle signals |
|
397 if ( function_exists('pcntl_signal') ) |
|
398 { |
|
399 // required for signal handling to work |
|
400 declare(ticks=1); |
|
401 |
|
402 // trap SIGTERM |
|
403 pcntl_signal(SIGUSR2, array(&$this, '_ipc_event')); |
|
404 } |
364 } |
405 } |
365 } |
406 } |
366 |
407 |
367 $this->in_keepalive = false; |
408 $this->in_keepalive = false; |
368 $this->headers_sent = false; |
409 $this->headers_sent = false; |
369 $this->in_scriptlet = false; |
410 $this->in_scriptlet = false; |
370 |
411 |
371 // read request |
412 // |
|
413 // READ THE REQUEST |
|
414 // |
|
415 |
|
416 // this is a complicated situation because we need to keep enough ticks going to properly handle |
|
417 // signals, meaning we can't use stream_set_timeout() and instead need to rely on our own timing |
|
418 // logic. setting the timeout to a short period, say 200,000 usec, we can minimize CPU usage and |
|
419 // have a good response time. |
|
420 |
|
421 $remote->set_timeout(0, 200000); |
|
422 $start_time = microtime(true); |
|
423 $client_headers = ''; |
|
424 $last_line = ''; |
|
425 while ( true ) |
|
426 { |
|
427 if ( $start_time + HTTPD_KEEP_ALIVE_TIMEOUT < microtime(true) || $remote->is_eof() ) |
|
428 { |
|
429 // request expired -- end the process here |
|
430 if ( !defined('HTTPD_WS_CHILD') ) |
|
431 $remote->destroy(); |
|
432 |
|
433 continue 2; |
|
434 } |
|
435 $line = strval($remote->read_normal()); |
|
436 $line = str_replace("\r", "", $line); |
|
437 |
|
438 // raw layer wants to send 2 empty lines through, stream layer wants to send one. |
|
439 $last_line_check = ( HTTPD_SOCKET_LAYER != 'Raw' || ( HTTPD_SOCKET_LAYER == 'Raw' && $last_line == "\n" ) ); |
|
440 if ( $line == "\n" && $last_line_check ) |
|
441 // we have two newlines in a row, break out since we have a full request |
|
442 break; |
|
443 |
|
444 $client_headers .= $line; |
|
445 $last_line = $line; |
|
446 } |
|
447 |
|
448 /* |
372 $last_line = ''; |
449 $last_line = ''; |
373 $client_headers = ''; |
450 $client_headers = ''; |
374 if ( defined('HTTPD_WS_CHILD') ) |
451 if ( defined('HTTPD_WS_CHILD') ) |
375 { |
452 { |
376 @socket_set_timeout($remote, HTTPD_KEEP_ALIVE_TIMEOUT); |
453 $remote->set_timeout(1); |
377 } |
454 } |
378 if ( $line = @socket_read($remote, 1024, PHP_NORMAL_READ) ) |
455 if ( $line = $remote->read_normal() ) |
379 { |
456 { |
380 do |
457 do |
381 { |
458 { |
382 $line = str_replace("\r", "", $line); |
459 $line = str_replace("\r", "", $line); |
383 if ( empty($line) ) |
460 if ( empty($line) ) |
384 continue; |
461 continue; |
385 if ( $line == "\n" && $last_line == "\n" ) |
462 $last_line_check = ( HTTPD_SOCKET_LAYER != 'Raw' || ( HTTPD_SOCKET_LAYER == 'Raw' && $last_line == "\n" ) ); |
|
463 if ( $line == "\n" && $last_line_check ) |
386 break; |
464 break; |
387 $client_headers .= $line; |
465 $client_headers .= $line; |
388 $last_line = $line; |
466 $last_line = $line; |
389 } |
467 $line = $remote->read_normal() |
390 while ( $line = @socket_read($remote, 1024, PHP_NORMAL_READ) ); |
468 } |
|
469 while ( true ); |
391 } |
470 } |
392 else |
471 else |
393 { |
472 { |
394 if ( defined('HTTPD_WS_CHILD') ) |
473 if ( defined('HTTPD_WS_CHILD') ) |
395 { |
474 { |
396 $md = @socket_get_status($remote); |
475 if ( $remote->timed_out() ) |
397 if ( @$md['timed_out'] ) |
|
398 { |
476 { |
399 status('[debug] keep-alive connection timed out'); |
477 status('[debug] keep-alive connection timed out'); |
400 continue; // will jump back to the start of the loop and kill the child process |
478 continue; // will jump back to the start of the loop and kill the child process |
401 } |
479 } |
402 } |
480 } |
403 } |
481 } |
|
482 */ |
404 |
483 |
405 // parse request |
484 // parse request |
406 $client_headers = trim($client_headers); |
485 $client_headers = trim($client_headers); |
407 |
486 |
408 if ( isset($last_finish_time) && empty($client_headers) && defined('HTTPD_WS_CHILD') && $last_finish_time + HTTPD_KEEP_ALIVE_TIMEOUT < microtime(true) ) |
487 if ( isset($last_finish_time) && empty($client_headers) && defined('HTTPD_WS_CHILD') && $last_finish_time + HTTPD_KEEP_ALIVE_TIMEOUT < microtime(true) ) |
741 $headers = str_replace("\r\n", "\n", $headers); |
815 $headers = str_replace("\r\n", "\n", $headers); |
742 $headers = str_replace("\n", "\r\n", $headers); |
816 $headers = str_replace("\n", "\r\n", $headers); |
743 $headers = preg_replace("#[\r\n]+$#", '', $headers); |
817 $headers = preg_replace("#[\r\n]+$#", '', $headers); |
744 $connection = ( $this->in_keepalive ) ? 'keep-alive' : 'close'; |
818 $connection = ( $this->in_keepalive ) ? 'keep-alive' : 'close'; |
745 |
819 |
746 @socket_write($socket, "HTTP/1.1 $http_code $reason_code\r\n"); |
820 $socket->write("HTTP/1.1 $http_code $reason_code\r\n"); |
747 @socket_write($socket, "Server: $this->server_string"); |
821 $socket->write("Server: $this->server_string"); |
748 @socket_write($socket, "Connection: $connection\r\n"); |
822 $socket->write("Connection: $connection\r\n"); |
749 @socket_write($socket, "Content-Type: $contenttype\r\n"); |
823 $socket->write("Content-Type: $contenttype\r\n"); |
750 if ( !empty($headers) ) |
824 if ( !empty($headers) ) |
751 { |
825 { |
752 @socket_write($socket, "$headers\r\n"); |
826 $socket->write("$headers\r\n"); |
753 } |
827 } |
754 @socket_write($socket, "\r\n"); |
828 $socket->write("\r\n"); |
755 } |
829 } |
756 |
830 |
757 /** |
831 /** |
758 * Sends a normal response |
832 * Sends a normal response |
759 * @param resource Socket connection to client |
833 * @param resource Socket connection to client |
1526 } |
1600 } |
1527 } |
1601 } |
1528 return $array; |
1602 return $array; |
1529 } |
1603 } |
1530 |
1604 |
|
1605 /** |
|
1606 * Handle an IPC event. Called only upon SIGUSR2. |
|
1607 */ |
|
1608 |
|
1609 function _ipc_event() |
|
1610 { |
|
1611 $pid = getmypid() . ':' . $this->parent_pid; |
|
1612 |
|
1613 // decide which socket to use |
|
1614 if ( defined('HTTPD_WS_CHILD') ) |
|
1615 $sock =& $this->parent_sock; |
|
1616 else |
|
1617 $sock =& $this->child_sock; |
|
1618 |
|
1619 // try to read the event |
|
1620 // this sometimes gets hung up because socket_set_timeout() doesn't seem to work on its own set of |
|
1621 // functions (it only works on PHP's normal streams) |
|
1622 if ( $line = @fgets($sock, 1024) ) |
|
1623 { |
|
1624 $line = trim($line); |
|
1625 list($action, $param) = explode(' ', $line); |
|
1626 switch($action) |
|
1627 { |
|
1628 case 'exit': |
|
1629 // this is to prevent zombie children |
|
1630 pcntl_waitpid(intval($param), $status); |
|
1631 // we know this child is dead now, remove them from the list |
|
1632 foreach ( $this->child_list as $i => $pid ) |
|
1633 { |
|
1634 if ( $pid === intval($param) ) |
|
1635 { |
|
1636 unset($this->child_list[$i]); |
|
1637 $this->child_list = array_values($this->child_list); |
|
1638 break; |
|
1639 } |
|
1640 } |
|
1641 break; |
|
1642 case 'die': |
|
1643 // only do this if this is a child (both security and design) |
|
1644 if ( defined('HTTPD_WS_CHILD') ) |
|
1645 { |
|
1646 if ( function_exists('status') ) |
|
1647 { |
|
1648 status('Received shutdown request, complying'); |
|
1649 } |
|
1650 $this->send_ipc_event("exit " . getmypid()); |
|
1651 exit(0); |
|
1652 } |
|
1653 break; |
|
1654 default: |
|
1655 break; |
|
1656 } |
|
1657 } |
|
1658 } |
|
1659 |
|
1660 /** |
|
1661 * Send an IPC event. |
|
1662 * @param string Data to write to the socket, newline will be added automatically |
|
1663 */ |
|
1664 |
|
1665 function send_ipc_event($data) |
|
1666 { |
|
1667 if ( defined('HTTPD_WS_CHILD') ) |
|
1668 $sock =& $this->parent_sock; |
|
1669 else |
|
1670 $sock =& $this->child_sock; |
|
1671 |
|
1672 $data = rtrim($data, "\r\n") . "\n"; |
|
1673 @fwrite($sock, $data); |
|
1674 |
|
1675 // if we're a child, signal the parent |
|
1676 if ( defined('HTTPD_WS_CHILD') ) |
|
1677 { |
|
1678 posix_kill($this->parent_pid, SIGUSR2); |
|
1679 } |
|
1680 // if we're the parent, signal all children |
|
1681 else |
|
1682 { |
|
1683 foreach ( $this->child_list as $pid ) |
|
1684 { |
|
1685 posix_kill($pid, SIGUSR2); |
|
1686 } |
|
1687 } |
|
1688 } |
|
1689 |
|
1690 } |
|
1691 |
|
1692 /** |
|
1693 * Socket abstraction layer - low-level socket functions (socket_*) |
|
1694 */ |
|
1695 |
|
1696 class Socket_Raw |
|
1697 { |
|
1698 var $sock; |
|
1699 var $socket_initted = false; |
|
1700 |
|
1701 function tcp_listen($address, $port) |
|
1702 { |
|
1703 $this->sock = @socket_create(AF_INET, SOCK_STREAM, getprotobyname('tcp')); |
|
1704 if ( !$this->sock ) |
|
1705 throw new Exception('Could not create socket'); |
|
1706 $result = @socket_bind($this->sock, $address, $port); |
|
1707 if ( !$result ) |
|
1708 throw new Exception("Could not bind to $address:$port"); |
|
1709 $this->socket_initted = true; |
|
1710 $result = @socket_listen($this->sock, SOMAXCONN); |
|
1711 if ( !$result ) |
|
1712 throw new Exception("Could not listen for connections $address:$port"); |
|
1713 |
|
1714 $this->socket_initted = true; |
|
1715 } |
|
1716 |
|
1717 function destroy() |
|
1718 { |
|
1719 if ( $this->socket_initted ) |
|
1720 { |
|
1721 // http://us3.php.net/manual/en/function.socket-bind.php |
|
1722 if ( !@socket_set_option($this->sock, SOL_SOCKET, SO_REUSEADDR, 1) ) |
|
1723 { |
|
1724 echo socket_strerror(socket_last_error($this->sock)) . "\n"; |
|
1725 } |
|
1726 @socket_shutdown($this->sock, 2); |
|
1727 @socket_close($this->sock); |
|
1728 } |
|
1729 } |
|
1730 |
|
1731 function accept() |
|
1732 { |
|
1733 $remote = false; |
|
1734 $timeout = 5; |
|
1735 switch(@socket_select($r = array($this->sock), $w = array($this->sock), $e = array($this->sock), $timeout)) { |
|
1736 case 2: |
|
1737 return false; |
|
1738 case 1: |
|
1739 $remote = @socket_accept($this->sock); |
|
1740 $return = new Socket_Raw(); |
|
1741 $return->sock = $remote; |
|
1742 $return->socket_initted = true; |
|
1743 return $return; |
|
1744 break; |
|
1745 case 0: |
|
1746 return false; |
|
1747 } |
|
1748 } |
|
1749 |
|
1750 /** |
|
1751 * Closes the socket but doesn't destroy it. |
|
1752 */ |
|
1753 |
|
1754 function soft_shutdown() |
|
1755 { |
|
1756 @socket_set_option($this->sock, SOL_SOCKET, SO_REUSEADDR, 1); |
|
1757 socket_close($this->sock); |
|
1758 } |
|
1759 |
|
1760 function set_timeout($timeout, $usec = false) |
|
1761 { |
|
1762 // doesn't work in this. |
|
1763 } |
|
1764 |
|
1765 function read_normal($length = 1024) |
|
1766 { |
|
1767 return @socket_read($this->sock, $length, PHP_NORMAL_READ); |
|
1768 } |
|
1769 |
|
1770 function read_binary($length = 1024) |
|
1771 { |
|
1772 return @socket_read($this->sock, $length, PHP_BINARY_READ); |
|
1773 } |
|
1774 |
|
1775 function timed_out() |
|
1776 { |
|
1777 $md = @socket_get_status($this->sock); |
|
1778 return ( isset($md['timed_out']) ) ? $md['timed_out'] : false; |
|
1779 } |
|
1780 |
|
1781 function get_peer_info(&$addr, &$port) |
|
1782 { |
|
1783 socket_getpeername($this->sock, $addr, $port); |
|
1784 } |
|
1785 |
|
1786 function write($data) |
|
1787 { |
|
1788 return @socket_write($this->sock, $data); |
|
1789 } |
|
1790 |
|
1791 function is_eof() |
|
1792 { |
|
1793 // feof() not supported |
|
1794 return false; |
|
1795 } |
|
1796 } |
|
1797 |
|
1798 /** |
|
1799 * Socket abstraction layer - PHP stream support |
|
1800 */ |
|
1801 |
|
1802 class Socket_Stream |
|
1803 { |
|
1804 var $sock; |
|
1805 var $socket_initted = false; |
|
1806 |
|
1807 function tcp_listen($address, $port) |
|
1808 { |
|
1809 $this->sock = @stream_socket_server("tcp://$address:$port", $errno, $errstr); |
|
1810 if ( !$this->sock ) |
|
1811 throw new Exception("Could not create the socket: error $errno: $errstr"); |
|
1812 } |
|
1813 |
|
1814 function destroy() |
|
1815 { |
|
1816 if ( $this->socket_initted ) |
|
1817 { |
|
1818 // PHP >= 5.2.1 |
|
1819 if ( function_exists('stream_socket_shutdown') ) |
|
1820 { |
|
1821 @stream_socket_shutdown($this->sock, STREAM_SHUT_RDWR); |
|
1822 } |
|
1823 fclose($this->sock); |
|
1824 } |
|
1825 } |
|
1826 |
|
1827 function accept() |
|
1828 { |
|
1829 // the goal of a custom accept() with *_select() is to tick every 5 seconds to allow signals. |
|
1830 stream_set_blocking($this->sock, 1); |
|
1831 $timeout = 5; |
|
1832 $selection = @stream_select($r = array($this->sock), $w = array($this->sock), $e = array($this->sock), $timeout); |
|
1833 if ( !$selection ) |
|
1834 { |
|
1835 return false; |
|
1836 } |
|
1837 $remote = stream_socket_accept($this->sock); |
|
1838 $return = new Socket_Stream(); |
|
1839 $return->sock = $remote; |
|
1840 $return->socket_initted = true; |
|
1841 return $return; |
|
1842 } |
|
1843 |
|
1844 function soft_shutdown() |
|
1845 { |
|
1846 fclose($this->sock); |
|
1847 } |
|
1848 |
|
1849 function set_timeout($timeout, $usec = false) |
|
1850 { |
|
1851 return ( $usec ) ? @stream_set_timeout($this->sock, 0, $usec) : @stream_set_timeout($this->sock, $timeout); |
|
1852 } |
|
1853 |
|
1854 function read_normal($length = 1024) |
|
1855 { |
|
1856 return @fgets($this->sock, $length); |
|
1857 } |
|
1858 |
|
1859 function read_binary($length = 1024) |
|
1860 { |
|
1861 return @fread($this->sock, $length); |
|
1862 } |
|
1863 |
|
1864 function timed_out() |
|
1865 { |
|
1866 $md = @stream_get_meta_data($this->sock); |
|
1867 return ( isset($md['timed_out']) ) ? $md['timed_out'] : false; |
|
1868 } |
|
1869 |
|
1870 function get_peer_info(&$addr, &$port) |
|
1871 { |
|
1872 $peer = stream_socket_get_name($this->sock, true); |
|
1873 list($addr, $port) = explode(':', $peer); |
|
1874 } |
|
1875 |
|
1876 function write($data) |
|
1877 { |
|
1878 return @fwrite($this->sock, $data); |
|
1879 } |
|
1880 |
|
1881 function is_eof() |
|
1882 { |
|
1883 return feof($this->sock); |
|
1884 } |
1531 } |
1885 } |
1532 |
1886 |
1533 /** |
1887 /** |
1534 * Exception class that allows breaking directly out of a scriptlet. |
1888 * Exception class that allows breaking directly out of a scriptlet. |
1535 */ |
1889 */ |