Changeset View
Changeset View
Standalone View
Standalone View
externals/JAXL/http/http_server.php
- This file was added.
| <?php | |||||
| /** | |||||
| * Jaxl (Jabber XMPP Library) | |||||
| * | |||||
| * Copyright (c) 2009-2012, Abhinav Singh <me@abhinavsingh.com>. | |||||
| * All rights reserved. | |||||
| * | |||||
| * Redistribution and use in source and binary forms, with or without | |||||
| * modification, are permitted provided that the following conditions | |||||
| * are met: | |||||
| * | |||||
| * * Redistributions of source code must retain the above copyright | |||||
| * notice, this list of conditions and the following disclaimer. | |||||
| * | |||||
| * * Redistributions in binary form must reproduce the above copyright | |||||
| * notice, this list of conditions and the following disclaimer in | |||||
| * the documentation and/or other materials provided with the | |||||
| * distribution. | |||||
| * | |||||
| * * Neither the name of Abhinav Singh nor the names of his | |||||
| * contributors may be used to endorse or promote products derived | |||||
| * from this software without specific prior written permission. | |||||
| * | |||||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||||
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |||||
| * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |||||
| * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |||||
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |||||
| * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||||
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |||||
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRIC | |||||
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |||||
| * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |||||
| * POSSIBILITY OF SUCH DAMAGE. | |||||
| * | |||||
| */ | |||||
| require_once JAXL_CWD.'/core/jaxl_logger.php'; | |||||
| require_once JAXL_CWD.'/http/http_dispatcher.php'; | |||||
| require_once JAXL_CWD.'/http/http_request.php'; | |||||
| // carriage return and line feed | |||||
| define('HTTP_CRLF', "\r\n"); | |||||
| // 1xx informational | |||||
| define('HTTP_100', "Continue"); | |||||
| define('HTTP_101', "Switching Protocols"); | |||||
| // 2xx success | |||||
| define('HTTP_200', "OK"); | |||||
| // 3xx redirection | |||||
| define('HTTP_301', 'Moved Permanently'); | |||||
| define('HTTP_304', 'Not Modified'); | |||||
| // 4xx client error | |||||
| define('HTTP_400', 'Bad Request'); | |||||
| define('HTTP_403', 'Forbidden'); | |||||
| define('HTTP_404', 'Not Found'); | |||||
| define('HTTP_405', 'Method Not Allowed'); | |||||
| define('HTTP_499', 'Client Closed Request'); // Nginx | |||||
| // 5xx server error | |||||
| define('HTTP_500', 'Internal Server Error'); | |||||
| define('HTTP_503', 'Service Unavailable'); | |||||
| class HTTPServer { | |||||
| private $server = null; | |||||
| public $cb = null; | |||||
| private $dispatcher = null; | |||||
| private $requests = array(); | |||||
| public function __construct($port=9699, $address="127.0.0.1") { | |||||
| $path = 'tcp://'.$address.':'.$port; | |||||
| $this->server = new JAXLSocketServer( | |||||
| $path, | |||||
| array(&$this, 'on_accept'), | |||||
| array(&$this, 'on_request') | |||||
| ); | |||||
| $this->dispatcher = new HTTPDispatcher(); | |||||
| } | |||||
| public function __destruct() { | |||||
| $this->server = null; | |||||
| } | |||||
| public function dispatch($rules) { | |||||
| foreach($rules as $rule) { | |||||
| $this->dispatcher->add_rule($rule); | |||||
| } | |||||
| } | |||||
| public function start($cb=null) { | |||||
| $this->cb = $cb; | |||||
| JAXLLoop::run(); | |||||
| } | |||||
| public function on_accept($sock, $addr) { | |||||
| _debug("on_accept for client#$sock, addr:$addr"); | |||||
| // initialize new request obj | |||||
| $request = new HTTPRequest($sock, $addr); | |||||
| // setup sock cb | |||||
| $request->set_sock_cb( | |||||
| array(&$this->server, 'send'), | |||||
| array(&$this->server, 'read'), | |||||
| array(&$this->server, 'close') | |||||
| ); | |||||
| // cache request object | |||||
| $this->requests[$sock] = &$request; | |||||
| // reactive client for further read | |||||
| $this->server->read($sock); | |||||
| } | |||||
| public function on_request($sock, $raw) { | |||||
| _debug("on_request for client#$sock"); | |||||
| $request = $this->requests[$sock]; | |||||
| // 'wait_for_body' state is reached when ever | |||||
| // application calls recv_body() method | |||||
| // on received $request object | |||||
| if($request->state() == 'wait_for_body') { | |||||
| $request->body($raw); | |||||
| } | |||||
| else { | |||||
| // break on crlf | |||||
| $lines = explode(HTTP_CRLF, $raw); | |||||
| // parse request line | |||||
| if($request->state() == 'wait_for_request_line') { | |||||
| list($method, $resource, $version) = explode(" ", $lines[0]); | |||||
| $request->line($method, $resource, $version); | |||||
| unset($lines[0]); | |||||
| _info($request->ip." ".$request->method." ".$request->resource." ".$request->version); | |||||
| } | |||||
| // parse headers | |||||
| foreach($lines as $line) { | |||||
| $line_parts = explode(":", $line); | |||||
| if(sizeof($line_parts) > 1) { | |||||
| if(strlen($line_parts[0]) > 0) { | |||||
| $k = $line_parts[0]; | |||||
| unset($line_parts[0]); | |||||
| $v = implode(":", $line_parts); | |||||
| $request->set_header($k, $v); | |||||
| } | |||||
| } | |||||
| else if(strlen(trim($line_parts[0])) == 0) { | |||||
| $request->empty_line(); | |||||
| } | |||||
| // if exploded line array size is 1 | |||||
| // and thr is something in $line_parts[0] | |||||
| // must be request body | |||||
| else { | |||||
| $request->body($line); | |||||
| } | |||||
| } | |||||
| } | |||||
| // if request has reached 'headers_received' state? | |||||
| if($request->state() == 'headers_received') { | |||||
| // dispatch to any matching rule found | |||||
| _debug("delegating to dispatcher for further routing"); | |||||
| $dispatched = $this->dispatcher->dispatch($request); | |||||
| // if no dispatch rule matched call generic callback | |||||
| if(!$dispatched && $this->cb) { | |||||
| _debug("no dispatch rule matched, sending to generic callback"); | |||||
| call_user_func($this->cb, $request); | |||||
| } | |||||
| // else if not dispatched and not generic callbacked | |||||
| // send 404 not_found | |||||
| else if(!$dispatched) { | |||||
| // TODO: send 404 if no callback is registered for this request | |||||
| _debug("dropping request since no matching dispatch rule or generic callback was specified"); | |||||
| $request->not_found('404 Not Found'); | |||||
| } | |||||
| } | |||||
| // if state is not 'headers_received' | |||||
| // reactivate client socket for read event | |||||
| else { | |||||
| $this->server->read($sock); | |||||
| } | |||||
| } | |||||
| } | |||||
| ?> | |||||