utexas_pjm Posted May 12, 2007 Share Posted May 12, 2007 So over the past few days I've been writing an HTTP1.1 webserver in PHP. I have implemented the responses for GET and POST requests on static files (inlcuding most MIME types). I'm serving a demo site with my new, as of yet, unnamed PHP server (any suggestions?) here: http://www.patrickmizer.com:9191/. I have the same demo site served by Apache2 here: http://www.patrickmizer.com/apaBench/www/. Was wondering if you guys wouldn't mind hitting the site a few times. This is my first round of testing so I expect the server to go down occasionally, I'm just trying to identify bottlenecks etc. Thanks for the help, Patrick P.S. If anyone is interested in seeing the source just shoot me an email and I'll be happy to share. Link to comment https://forums.phpfreaks.com/topic/51087-php-webserver/ Share on other sites More sharing options...
redbullmarky Posted May 12, 2007 Share Posted May 12, 2007 looking good so far! i'll have a good dig though, this is definitely an interesting one. i wondered how long before you did this one anyway! ps i've sent you an email as i'd be pretty keen to see how it works Link to comment https://forums.phpfreaks.com/topic/51087-php-webserver/#findComment-251473 Share on other sites More sharing options...
utexas_pjm Posted May 12, 2007 Author Share Posted May 12, 2007 looking good so far! i'll have a good dig though, this is definitely an interesting one. i wondered how long before you did this one anyway! ps i've sent you an email as i'd be pretty keen to see how it works Thanks for having a look. The source should be in your email. Patrick Link to comment https://forums.phpfreaks.com/topic/51087-php-webserver/#findComment-251483 Share on other sites More sharing options...
utexas_pjm Posted May 13, 2007 Author Share Posted May 13, 2007 Someone crashed the server with this request: TRACK /TRACK_test HTTP/1.0 Accept: */* User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.1.4322) Host: www.patrickmizer.com:9191 Connection: Close Pragma: no-cache Thanks. If the Request type (TRACK) was undefined it was throwing a fatal exception instead of sending an "unimplemented" response. This issue has been fixed. Patrick Link to comment https://forums.phpfreaks.com/topic/51087-php-webserver/#findComment-251730 Share on other sites More sharing options...
Daniel0 Posted May 13, 2007 Share Posted May 13, 2007 Seems to work ok. It appears to be serving the web page just as fast as when served by Apache. Have you tested when under load? Link to comment https://forums.phpfreaks.com/topic/51087-php-webserver/#findComment-251885 Share on other sites More sharing options...
utexas_pjm Posted May 13, 2007 Author Share Posted May 13, 2007 Not yet, that's the next step, The performance under load will lag because Apache is multi threaded. I'm in the process of rigging a pseudo thread pool in my webserver using pcntl_form. Thanks for having a look. Patrick Link to comment https://forums.phpfreaks.com/topic/51087-php-webserver/#findComment-252084 Share on other sites More sharing options...
utexas_pjm Posted May 13, 2007 Author Share Posted May 13, 2007 Not yet, that's the next step, The performance under load will lag because Apache is multi threaded. I'm in the process of rigging a pseudo thread pool in my webserver using pcntl_form. Thanks for having a look. Patrick The "makes sense" translation of the above is: Not yet, that's the next step. The performance under load will lag behind Apache because PHP does not natively support threads. I'm in the process of rigging a pseudo thread pool in my webserver using pcntl_fork. Thanks for having a look. Patrick (sigh) Link to comment https://forums.phpfreaks.com/topic/51087-php-webserver/#findComment-252327 Share on other sites More sharing options...
utexas_pjm Posted May 14, 2007 Author Share Posted May 14, 2007 I managed to get my webserver to fork processes last night. So now each HTTP request is handled by its own subprocess. In theory this should speed the server up... we'll see. Patrick In case anyone is interested I've attached the code below: <?php /** * Http.class.php * * This class listens on the defined port for incoming * HTTP requests. Once a request is received this class * will attempt to spawn a ServerThread which parses the * request and sends a response. * */ class Http { /** * Socket which listens for requests. */ private $serverSocket; /** * Mimetype registry instance. */ private $mimeRegistry; /** * Number of spawned child processes. */ private static $childProcesses; /** * Whether or not to attempt process forking. */ private $canFork; /** * IPC Message Queue. */ private $messageQueue; /** * Constructor. */ public function __construct($address, $port) { $this->mimeRegistry = new MimeRegistry(); $this->serverSocket = HttpSocket::createNewSocket(); $this->serverSocket->bindSocket($address, $port); $this->canFork = (HTTP_CONFIG_FORK_PROCESSES && function_exists('pcntl_fork')); /** * If we can fork then we need to init a few more things. */ if($this->canFork){ $this->messageQueue = msg_get_queue(@ftok(HTTP_CONFIG_MSG_QUEUE, 'R'), 0666 | IPC_CREAT); self::$childProcesses = 0; pcntl_signal(SIGCHLD, array("Http", "sigHandler")); } } /** * Listen for incoming sockets. */ public function listen() { Logger::log('Http Daemon initialized and listening on port ' . HTTP_CONFIG_PORT, HTTP_LOG_DEBUG); while(1){ $clientSocket = HttpSocket::createSocketFromResource( $this->serverSocket->acceptConnection()); if($clientSocket){ if($this->canFork){ $this->forkServerThread($clientSocket); }else{ $this->runServerThread($clientSocket); } } } } /** * Handle sigs from processes. * * @param sig number. */ public static function sigHandler($signo) { switch ($signo) { case SIGCHLD: Logger::log('Child process ' . self::$childProcesses .' killed.', HTTP_LOG_DEBUG); -- self::$childProcesses; break; } } /** * Attempt to fork the process and run a server thread. * * @param client socket */ private function forkServerThread($clientSocket) { /** * If we are at our child process threshold, wait one * second and try again. */ while ( self::$childProcesses >= HTTP_MAX_CHILD_PROCESSES ){ Logger::log('Maximum child processes threshold met.', HTTP_LOG_DEBUG); sleep(1); } ++ self::$childProcesses; $pid = @pcntl_fork(); if($pid == -1) { /** * If we get a -1 we were unable to fork process. * So we'll just handle the request in serial. */ Logger::log('Unable to fork.', HTTP_LOG_DEBUG); $this->runServerThread($clientSocket); }else if($pid){ /** * I am the parent process. */ Logger::log('Parent process PID = ' . $pid, HTTP_LOG_DEBUG); /** * Possibly look for zombies. */ if(rand(1, 100) <= HTTP_CONF_KILL_ZOMBIE_PERC){ /** * I need to look in the IPC message queue for any zombie * children which must be killed off. */ $currentqueue = msg_stat_queue($this->messageQueue); $n = $currentqueue['msg_qnum']; if($n > 0){ $this->cleanUpZombies($n); } } }else { /** * I am a childprocess. */ Logger::log('Child process ' . self::$childProcesses . ' spawned.', HTTP_LOG_DEBUG); /** * First I will process the request. */ $this->runServerThread($clientSocket); /** * Then, I will tell my parent that I am done. */ $error = ''; if (!msg_send($this->messageQueue, 1, posix_getpid(), true, true, $error)) { throw new Exception('Error sending message via msg_send(): ' . $error); } /** * Finally I become a zombie and wait for my parent to clean me up. */ Logger::log('Zombie.', HTTP_LOG_DEBUG); exit(); } } /** * Instantiate and run a server thread. * * @param client socket */ private function runServerThread($clientSocket) { $serverThread = new ServerThread($clientSocket, $this->mimeRegistry); $serverThread->run(); } private function cleanUpZombies($num) { Logger::log('There are '.$num.' zombies to clean up.', HTTP_LOG_DEBUG); for ($i = 0; $i < $num; $i++) { /** * Pop the kid's PID from the IPC message queue */ $msgType = ''; $msg = ''; $error = ''; if (!msg_receive ($this->messageQueue, 1, $msgType, 16384, $msg, true, 0, $error)) { throw new Exception('Error getting message via msg_receive(): ' . $error); }else { /** * Terminate child. */ $tmpstat = ''; pcntl_waitpid($msg, $tmpstat, 0); } } } } Link to comment https://forums.phpfreaks.com/topic/51087-php-webserver/#findComment-252757 Share on other sites More sharing options...
448191 Posted May 17, 2007 Share Posted May 17, 2007 Very impressive. You using the HTTP extension? Link to comment https://forums.phpfreaks.com/topic/51087-php-webserver/#findComment-255125 Share on other sites More sharing options...
448191 Posted May 17, 2007 Share Posted May 17, 2007 Hey, question: for my own framework's Response class I want to add a basic check if a header is a valid HTTP1.1 header... Shy of making an array of all valid headers in the RFC, any way to do that that you can think of? Link to comment https://forums.phpfreaks.com/topic/51087-php-webserver/#findComment-255128 Share on other sites More sharing options...
utexas_pjm Posted May 17, 2007 Author Share Posted May 17, 2007 Very impressive. You using the HTTP extension? Thanks. No -- I'm trying not to use any 3rd party extension such that it can be run "out of the box" with the noyl requirement being PHP5 (and *nix) plat form if you want process forking. Hey, question: for my own framework's Response class I want to add a basic check if a header is a valid HTTP1.1 header. My server currently only implements a relatively small subset of the headers defined in HTTP/1.1. The headers which are supported are validated via an associative array. Best, Patrick P.S. I just finished my CGI module and got PHP running I'll post a demo this evening... Link to comment https://forums.phpfreaks.com/topic/51087-php-webserver/#findComment-255319 Share on other sites More sharing options...
utexas_pjm Posted May 17, 2007 Author Share Posted May 17, 2007 Couldn't wait... Wordpress running on my PHP webserver: http://www.patrickmizer.com:9191/wordpress/ Patrick Link to comment https://forums.phpfreaks.com/topic/51087-php-webserver/#findComment-255337 Share on other sites More sharing options...
Daniel0 Posted May 17, 2007 Share Posted May 17, 2007 Does it support POST requests? It reset the connection when trying to comment on wordpress. Still pretty cool though. Link to comment https://forums.phpfreaks.com/topic/51087-php-webserver/#findComment-255462 Share on other sites More sharing options...
utexas_pjm Posted May 17, 2007 Author Share Posted May 17, 2007 Yeah.... it does. I think that something is different about the php-cgi binary on my server at home (it's a 64 bit arch). On my two other systems the POST works fine. I'm going to have a look when I get home this evening. Thanks for testing. Patrick Link to comment https://forums.phpfreaks.com/topic/51087-php-webserver/#findComment-255463 Share on other sites More sharing options...
obsidian Posted May 17, 2007 Share Posted May 17, 2007 Very impressive. I know this is the PHP Beta test, but I figure I'd mention that the javascript function on the contact page is throwing an error. I'm very impressed with how quickly things are being processed. Link to comment https://forums.phpfreaks.com/topic/51087-php-webserver/#findComment-255468 Share on other sites More sharing options...
utexas_pjm Posted May 19, 2007 Author Share Posted May 19, 2007 So... I've been trying to figure out was going on with the POST. It seems that when the server is accessed locally i.e., http://localhost:9191/test.php the post data is parsed from HTTP request header and passed on to the CGI binary without incident. However when it's accessed via http://www.patrickmizer.com:9191/test.php request headers get truncated about 80% of the time, like this: Actual HTTP Request POST /test.php HTTP/1.1 Host: www.patrickmizer.com:9191 User-Agent: Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.1.1) Gecko/20061208 Firefox/2.0.0.1 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive Referer: http://www.patrickmizer.com:9191/test.php Content-Type: application/x-www-form-urlencoded Content-Length: 28 height=0&width=0&submitted=1 Truncated HTTP Request: POST /test.php HTTP/1.1 Host: www.patrickmizer.com:9191 User-Agent: Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.8.1.1) Gecko/20061208 Firefox/2.0.0.1 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive Referer: http://www.patrickmizer.com:9191/test.php I have no idea why the request is being truncated when accessing the server from its external IP address. The only thing I can think of is that maybe the socket is timing out or something during the read... but it's weird that it always cuts off at Referer. I was hoping myabe you guys could bang away at this URL: http://www.patrickmizer.com:9191/test.php and test out the POST form, maybe a pattern will emerge. Thanks, Patrick Link to comment https://forums.phpfreaks.com/topic/51087-php-webserver/#findComment-257071 Share on other sites More sharing options...
utexas_pjm Posted May 19, 2007 Author Share Posted May 19, 2007 I will be putting the complete, commented (I aplogize again to those whom I have alreay sent undocumented code to) source up soon as I'm just about stumped on this one. Link to comment https://forums.phpfreaks.com/topic/51087-php-webserver/#findComment-257170 Share on other sites More sharing options...
Recommended Posts