diff --git a/commands/core/httpserver/httprequest.php b/commands/core/httpserver/httprequest.php
new file mode 100755
index 0000000..a9ab94d
--- /dev/null
+++ b/commands/core/httpserver/httprequest.php
@@ -0,0 +1,120 @@
+<?php
+
+class HTTPRequest
+{
+    public $method;             // HTTP method, e.g. "GET" or "POST"
+    public $request_uri;        // original requested URI, with query string
+    public $uri;                // path component of URI, without query string, after decoding %xx entities
+    public $http_version;       // version from the request line, e.g. "HTTP/1.1"
+    public $query_string;       // query string, like "a=b&c=d"
+    public $headers;            // associative array of HTTP headers    
+    public $content;            // content of POST request, if applicable    
+               
+    // internal fields to track the state of reading the HTTP request
+    private $cur_state = 0;
+    private $header_buf = '';
+    private $content_len = 0;
+
+    const READ_HEADERS = 0;
+    const READ_CONTENT = 1;
+    const READ_COMPLETE = 2;
+        
+    // fields used by HTTPServer to associate other data it tracks along with the request
+    public $socket;
+    public $response;
+    public $response_buf;
+    
+    function __construct($socket)
+    {
+        $this->socket = $socket;
+    }
+                            
+    /* 
+     * Reads a chunk of a HTTP request from a client socket.
+     */
+    function add_data($data)
+    {    
+        switch ($this->cur_state)
+        {
+            case static::READ_HEADERS:
+                $header_buf =& $this->header_buf;
+            
+                $header_buf .= $data;
+                       
+                $end_headers = strpos($header_buf, "\r\n\r\n", 4);
+                if ($end_headers === false)
+                {
+                    break;
+                }         
+
+                // parse HTTP request line    
+                $end_req = strpos($header_buf, "\r\n"); 
+                $req_line = substr($header_buf, 0, $end_req);
+                $req_arr = explode(' ', $req_line, 3);
+
+                $this->method = $req_arr[0];
+                $this->request_uri = $req_arr[1];
+                $this->http_version = $req_arr[2];    
+                
+                $parsed_uri = parse_url($this->request_uri);        
+                $this->uri = urldecode($parsed_uri['path']);
+                $this->query_string = @$parsed_uri['query'];              
+                
+                // parse HTTP headers
+                $start_headers = $end_req + 2;
+                        
+                $headers_str = substr($header_buf, $start_headers, $end_headers - $start_headers);
+                $this->headers = $headers = HTTPServer::parse_headers($headers_str);
+
+                $this->content_len = (int)@$headers['Content-Length'];
+                
+                $start_content = $end_headers + 4; // $end_headers is before last \r\n\r\n
+                
+                // add leftover to content
+                $this->content = substr($header_buf, $start_content);
+                $header_buf = '';                                
+                break;
+            case static::READ_CONTENT:
+                $this->content .= $data;
+                break;
+            case static::READ_COMPLETE:
+                break;
+        }    
+        
+        if (!$this->headers)
+        {
+            $this->cur_state = static::READ_HEADERS;
+        }
+        else if ($this->needs_content())
+        {
+            $this->cur_state = static::READ_CONTENT;
+        }
+        else
+        {
+            $this->cur_state = static::READ_COMPLETE;
+        }
+    }
+    
+    /*
+     * Returns true if a full HTTP request has been read by add_data().
+     */
+    function is_read_complete()
+    {
+        return $this->cur_state == static::READ_COMPLETE;
+    }
+    
+    function needs_content()
+    {
+        return $this->content_len - strlen($this->content) > 0;
+    }            
+    
+    /*
+     * Sets a HTTPResponse object associated with this request, and 
+     * prepares a buffer containing the remaining content. 
+     */ 
+    function set_response($response)
+    {
+        $this->response = $response;
+        $this->response_buf = $response->render(); 
+    }    
+}
\ No newline at end of file
diff --git a/commands/core/httpserver/httpresponse.php b/commands/core/httpserver/httpresponse.php
new file mode 100755
index 0000000..c2cfff5
--- /dev/null
+++ b/commands/core/httpserver/httpresponse.php
@@ -0,0 +1,105 @@
+<?php
+
+class HTTPResponse
+{
+    public $status;     // HTTP status code
+    public $content;    // response body        
+    public $headers;    // associative array of HTTP headers    
+    
+    function __construct($status = 200, $content = '', $headers = null)
+    {
+        $this->status = $status;
+        $this->content = $content;
+        $this->headers = $headers ?: array();
+    }        
+
+    function render()
+    {
+        $headers = $this->headers;
+        $status = $this->status;
+        $content = $this->content;
+
+        if (!isset($headers['Content-Length']))
+        {
+            $headers['Content-Length'] = strlen($content);
+        }        
+            
+        $status_msg = static::$messages[$status];
+
+        ob_start();
+        
+        echo "HTTP/1.1 $status $status_msg\r\n";
+        foreach ($headers as $name => $value)
+        {
+            echo "$name: $value\r\n";
+        }
+        echo "\r\n";
+        echo $content;
+        
+        return ob_get_clean();
+    }
+    
+    /* 
+     * HTTP status codes and messages originally from Kohana Request class
+     * (c) 2007-2010, Kohana Team, 
+     * released under BSD-style license in vendors/kohana_license.txt
+     */
+    public static $messages = array(
+        // Informational 1xx
+        100 => 'Continue',
+        101 => 'Switching Protocols',
+
+        // Success 2xx
+        200 => 'OK',
+        201 => 'Created',
+        202 => 'Accepted',
+        203 => 'Non-Authoritative Information',
+        204 => 'No Content',
+        205 => 'Reset Content',
+        206 => 'Partial Content',
+        207 => 'Multi-Status',
+
+        // Redirection 3xx
+        300 => 'Multiple Choices',
+        301 => 'Moved Permanently',
+        302 => 'Found', // 1.1
+        303 => 'See Other',
+        304 => 'Not Modified',
+        305 => 'Use Proxy',
+        // 306 is deprecated but reserved
+        307 => 'Temporary Redirect',
+
+        // Client Error 4xx
+        400 => 'Bad Request',
+        401 => 'Unauthorized',
+        402 => 'Payment Required',
+        403 => 'Forbidden',
+        404 => 'Not Found',
+        405 => 'Method Not Allowed',
+        406 => 'Not Acceptable',
+        407 => 'Proxy Authentication Required',
+        408 => 'Request Timeout',
+        409 => 'Conflict',
+        410 => 'Gone',
+        411 => 'Length Required',
+        412 => 'Precondition Failed',
+        413 => 'Request Entity Too Large',
+        414 => 'Request-URI Too Long',
+        415 => 'Unsupported Media Type',
+        416 => 'Requested Range Not Satisfiable',
+        417 => 'Expectation Failed',
+        422 => 'Unprocessable Entity',
+        423 => 'Locked',
+        424 => 'Failed Dependency',
+
+        // Server Error 5xx
+        500 => 'Internal Server Error',
+        501 => 'Not Implemented',
+        502 => 'Bad Gateway',
+        503 => 'Service Unavailable',
+        504 => 'Gateway Timeout',
+        505 => 'HTTP Version Not Supported',
+        507 => 'Insufficient Storage',
+        509 => 'Bandwidth Limit Exceeded'
+    );    
+}
\ No newline at end of file
diff --git a/commands/core/httpserver/httpserver.php b/commands/core/httpserver/httpserver.php
new file mode 100755
index 0000000..a7cfb3d
--- /dev/null
+++ b/commands/core/httpserver/httpserver.php
@@ -0,0 +1,399 @@
+<?php
+
+/*
+ * A simple standalone HTTP server for development that serves PHP scripts and static files.
+ *
+ * Each PHP request will be run in an isolated environment using PHP-CGI.
+ * (The 'php-cgi' binary must be installed on the local machine.)
+ *
+ * This allows running PHP scripts without needing to install a web server like Apache or Nginx.
+ * It also allows automated scripts (e.g. Selenium tests) to spawn a HTTP server with custom 
+ * environment variables (e.g. to override some application config settings).
+ *
+ * Requests are served by a single process, but non-blocking sockets are used to handle many 
+ * connections at once. Implements HTTP Keep-Alive for better performance. Works on Windows
+ * as well as POSIX systems.
+ *
+ * It is not very robust, may have security flaws, and shouldn't be used in production.
+ *
+ * Clients should subclass HTTPServer and override the route_request() method, at least.
+ * See examples/example_server.php. 
+ *
+ * HTTPServer only depends on code in this directory, and no other Envaya code,
+ * so it could easily be extracted and used in other PHP projects that need a standalone HTTP server.
+ *
+ * http://github.com/youngj/Envaya
+ * Copyright (c) 2010-2011 by Trust for Conservation Innovation
+ * Released under MIT license, see LICENSE.txt 
+ */
+ 
+require_once __DIR__."/httprequest.php";
+require_once __DIR__."/httpresponse.php";
+
+class HTTPServer
+{
+    /* 
+     * The following public properties can be passed as options to the constructor: 
+     */    
+    public $port = 80;                      // TCP port number to listen on    
+    public $cgi_env = array();              // associative array of additional environment variables to pass to php-cgi
+    public $server_id = 'WebServer/0.1';    // identifier string to use in 'Server' header of HTTP response
+    public $php_cgi = 'php-cgi';            // Path to php-cgi, if not in the PATH    
+    
+    /* 
+     * Internal map of active client socket resource IDs to HTTPRequest objects
+     */    
+    private $requests = array(/* socket_id => HTTPRequest */);    
+    
+    function __construct($options)
+    {
+        foreach ($options as $k => $v)
+        {
+            $this->$k = $v;
+        }
+    }
+    
+    /*  
+     * Subclasses should override to route the current request to either a static file or PHP script
+     * and return a HTTPResponse object. This function should call get_static_response() or
+     * get_php_response(), as applicable.
+     */
+    function route_request($request)
+    {
+        return new HTTPResponse(500, "WebServer::route_request not implemented");
+    }    
+    
+    /*
+     * Subclasses could override to disallow other characters in path names
+     */
+    function is_allowed_uri($uri)
+    {
+        return strpos($uri, '..') === false && !preg_match('#/\.#', $uri);
+    }    
+    
+    function bind_error()
+    {
+        error_log("Could not start a web server on port {$this->port}.");    
+    }
+    
+    function run_forever()
+    {    
+        // provide some required/useful environment variables even if 'E' is not in variables_order
+        $env_keys = array('HOME','OS','Path','PATHEXT','SystemRoot','TEMP','TMP');
+        foreach ($env_keys as $key)
+        {
+            $_ENV[$key] = getenv($key);
+        }
+    
+        set_time_limit(0);
+
+        $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
+        socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1);
+            
+        if (@socket_bind($sock, 0, $this->port) == false)
+        {
+            $this->bind_error();
+            return;
+        }
+
+        socket_listen($sock);
+
+        echo "Web server listening on 0.0.0.0:{$this->port} (see http://localhost:{$this->port}/)...\n";    
+
+        socket_set_nonblock($sock);        
+
+        $requests =& $this->requests;
+    
+        while (true)
+        {        
+            $read = array();
+            $write = array();
+            foreach ($requests as $id => $request)
+            {
+                if (!$request->is_read_complete())
+                {
+                    $read[] = $request->socket;
+                }
+                else
+                {
+                    $write[] = $request->socket;
+                }
+            }            
+            $read[] = $sock;            
+            
+            if (socket_select($read, $write, $except = null, null) < 1)
+                continue;
+                        
+            if (in_array($sock, $read))
+            {
+                $client = socket_accept($sock);
+                $requests[(int)$client] = new HTTPRequest($client);
+                
+                $key = array_search($sock, $read);
+                unset($read[$key]);
+            }
+            
+            foreach ($read as $client)
+            {
+                $this->read_socket($client);
+            }
+            
+            foreach ($write as $client)
+            {
+                $this->write_socket($client);
+            }
+        }        
+    }
+    
+    function write_socket($client)
+    {
+        $request = $this->requests[(int)$client];
+        $response_buf =& $request->response_buf;     
+        $len = @socket_write($client, $response_buf);
+        if ($len === null)
+        {
+            $this->end_request($request);
+        }
+        else if ($len < strlen($response_buf))
+        {
+            $response_buf = substr($response_buf, $len);
+        }
+        else
+        {
+            $response = $request->response;
+            $len = strlen($response->content);
+            $client_num = (int)$client;
+            echo "($client_num) {$request->method} {$request->request_uri} => {$response->status} {$len}\n";
+            
+            if (@$request->headers['Connection'] == 'close' || $request->http_version != 'HTTP/1.1')
+            {
+                $this->end_request($request);
+            }
+            else
+            {
+                $this->requests[(int)$client] = new HTTPRequest($client);
+            }
+        }                
+    }
+    
+    function read_socket($client)
+    {
+        $request = $this->requests[(int)$client];
+        $data = @socket_read($client, 8092, PHP_BINARY_READ);                                
+        if ($data === null || $data == '')
+        {
+            $this->end_request($request);
+        }
+        else
+        {
+            $request->add_data($data);
+            
+            if ($request->is_read_complete())
+            {
+                $response = $this->get_response($request);
+                $response->headers['Server'] = $this->server_id;
+                $request->set_response($response);
+            }    
+        }
+    }
+    
+    function end_request($request)
+    {
+        @socket_close($request->socket);
+        unset($this->requests[(int)$request->socket]);    
+    }        
+    
+    function get_response($request)
+    {
+        $uri = $request->uri;
+
+        // disallow suspicious paths
+        if (!$this->is_allowed_uri($uri) || $uri[0] != '/')
+        {
+            return new HTTPResponse(403, "Invalid URI $uri"); 
+        }
+        
+        return $this->route_request($request);        
+    }
+       
+    /*
+     * Returns a HTTPResponse object for the static file at $local_path.
+     */      
+    function get_static_response($request, $local_path)
+    {   
+        if (is_file($local_path))
+        {
+            return new HTTPResponse(200, 
+                file_get_contents($local_path),
+                array(
+                    'Content-Type' => static::get_mime_type($local_path),
+                    'Cache-Control' => "max-age=8640000"
+                )
+            );
+        }
+        else if (is_dir($local_path))
+        {
+            return new HTTPResponse(403, "Directory listing not allowed");
+        }
+        else
+        {
+            return new HTTPResponse(404, "File not found");
+        }    
+    }        
+            
+    /*
+     * Executes the PHP script in $script_filename using php-cgi, and returns 
+     * a HTTPResponse object. $cgi_env_override can be set to an associative array 
+     * to set or override any environment variables in the CGI process (e.g. PATH_INFO).
+     */
+    function get_php_response($request, $script_filename, $cgi_env_override = null)
+    {        
+        if (!is_file($script_filename))
+        {
+            return new HTTPResponse(404, "File not found");
+        }    
+        
+        $headers = $request->headers;
+        $content_length = @$headers['Content-Length'];        
+        
+        // see http://www.faqs.org/rfcs/rfc3875.html
+        $cgi_env = array(
+            'QUERY_STRING' => $request->query_string,
+            'REQUEST_METHOD' => $request->method,
+            'REQUEST_URI' => $request->request_uri,
+            'REDIRECT_STATUS' => 200,
+            'SCRIPT_FILENAME' => $script_filename,            
+            'SCRIPT_NAME' => pathinfo($script_filename, PATHINFO_BASENAME),
+            'SERVER_NAME' => @$headers['Host'],
+            'SERVER_PROTOCOL' => 'HTTP/1.1',
+            'SERVER_SOFTWARE' => $this->server_id,
+            'CONTENT_TYPE' => @$headers['Content-Type'],
+            'CONTENT_LENGTH' => $content_length,            
+        );        
+        
+        foreach ($headers as $name => $value)
+        {        
+            $name = str_replace('-','_', $name);
+            $name = strtoupper($name);
+            $cgi_env["HTTP_$name"] = $value;
+        }
+        
+        if ($cgi_env_override)
+        {
+            foreach ($cgi_env_override as $name => $value)
+            {
+                $cgi_env[$name] = $value;
+            }
+        }
+
+        if ($content_length)
+        {
+            $content_stream = tmpfile();
+            fwrite($content_stream, $request->content);
+            fseek($content_stream, 0);
+        }
+        else
+        {        
+            $content_stream = fopen("data://text/plain,", 'rb');
+        }
+        
+        $descriptorspec = array(
+           0 => $content_stream,
+           1 => array('pipe', 'w'),
+           2 => STDOUT, 
+        );
+        
+        $proc = proc_open($this->php_cgi, $descriptorspec, $pipes, 
+            __DIR__, 
+            array_merge($_ENV, $this->cgi_env, $cgi_env),
+            array(
+                'binary_pipes' => true,
+                'bypass_shell' => true
+            )
+        );                        
+        
+        if (!is_resource($proc))
+        {
+            return new HTTPResponse(500, "Internal Server Error: php-cgi was not found");
+        }
+                
+        ob_start();
+        fpassthru($pipes[1]);
+        $response_str = ob_get_clean();
+        
+        if (!$response_str)
+        {
+            return new HTTPResponse(500, "Internal Server Error: php-cgi did not return a response");
+        }        
+
+        $end_response_headers = strpos($response_str, "\r\n\r\n");
+        
+        $headers_str = substr($response_str, 0, $end_response_headers);
+
+        $headers = static::parse_headers($headers_str);        
+        
+        $response = new HTTPResponse();                        
+        
+        // CGI process sends HTTP status as regular header
+        if (isset($headers['Status']))
+        {
+            $response->status = (int) $headers['Status'];
+            unset($headers['Status']);
+        }
+        $response->headers = $headers;                        
+        $response->content = substr($response_str, $end_response_headers + 4);
+        
+        proc_close($proc);
+        
+        fclose($content_stream);
+                
+        return $response;
+    }         
+
+    static function parse_headers($headers_str)
+    {
+        $headers_arr = explode("\r\n", $headers_str);
+                
+        $headers = array();
+        foreach ($headers_arr as $header_str)
+        {
+            $header_arr = explode(": ", $header_str, 2);
+            $header_name = $header_arr[0];            
+            $headers[$header_name] = $header_arr[1];
+        }                
+        return $headers;
+    }                          
+        
+    static function get_mime_type($filename)
+    {
+        $pathinfo = pathinfo($filename);
+        $extension = strtolower($pathinfo['extension']);
+    
+        return @static::$mime_types[$extension];
+    }        
+    
+    /*
+     * List of mime types for common file extensions
+     * (c) Tyler Hall http://code.google.com/p/php-aws/
+     * released under MIT License
+     */
+	static $mime_types = array("323" => "text/h323", "acx" => "application/internet-property-stream", "ai" => "application/postscript", "aif" => "audio/x-aiff", "aifc" => "audio/x-aiff", "aiff" => "audio/x-aiff",
+        "asf" => "video/x-ms-asf", "asr" => "video/x-ms-asf", "asx" => "video/x-ms-asf", "au" => "audio/basic", "avi" => "video/quicktime", "axs" => "application/olescript", "bas" => "text/plain", "bcpio" => "application/x-bcpio", "bin" => "application/octet-stream", "bmp" => "image/bmp",
+        "c" => "text/plain", "cat" => "application/vnd.ms-pkiseccat", "cdf" => "application/x-cdf", "cer" => "application/x-x509-ca-cert", "class" => "application/octet-stream", "clp" => "application/x-msclip", "cmx" => "image/x-cmx", "cod" => "image/cis-cod", "cpio" => "application/x-cpio", "crd" => "application/x-mscardfile",
+        "crl" => "application/pkix-crl", "crt" => "application/x-x509-ca-cert", "csh" => "application/x-csh", "css" => "text/css", "dcr" => "application/x-director", "der" => "application/x-x509-ca-cert", "dir" => "application/x-director", "dll" => "application/x-msdownload", "dms" => "application/octet-stream", "doc" => "application/msword",
+        "dot" => "application/msword", "dvi" => "application/x-dvi", "dxr" => "application/x-director", "eps" => "application/postscript", "etx" => "text/x-setext", "evy" => "application/envoy", "exe" => "application/octet-stream", "fif" => "application/fractals", "flr" => "x-world/x-vrml", "gif" => "image/gif",
+        "gtar" => "application/x-gtar", "gz" => "application/x-gzip", "h" => "text/plain", "hdf" => "application/x-hdf", "hlp" => "application/winhlp", "hqx" => "application/mac-binhex40", "hta" => "application/hta", "htc" => "text/x-component", "htm" => "text/html", "html" => "text/html",
+        "htt" => "text/webviewhtml", "ico" => "image/x-icon", "ief" => "image/ief", "iii" => "application/x-iphone", "ins" => "application/x-internet-signup", "isp" => "application/x-internet-signup", "jfif" => "image/pipeg", "jpe" => "image/jpeg", "jpeg" => "image/jpeg", "jpg" => "image/jpeg",
+        "js" => "application/x-javascript", "latex" => "application/x-latex", "lha" => "application/octet-stream", "lsf" => "video/x-la-asf", "lsx" => "video/x-la-asf", "lzh" => "application/octet-stream", "m13" => "application/x-msmediaview", "m14" => "application/x-msmediaview", "m3u" => "audio/x-mpegurl", "man" => "application/x-troff-man",
+        "mdb" => "application/x-msaccess", "me" => "application/x-troff-me", "mht" => "message/rfc822", "mhtml" => "message/rfc822", "mid" => "audio/mid", "mny" => "application/x-msmoney", "mov" => "video/quicktime", "movie" => "video/x-sgi-movie", "mp2" => "video/mpeg", "mp3" => "audio/mpeg",
+        "mpa" => "video/mpeg", "mpe" => "video/mpeg", "mpeg" => "video/mpeg", "mpg" => "video/mpeg", "mpp" => "application/vnd.ms-project", "mpv2" => "video/mpeg", "ms" => "application/x-troff-ms", "mvb" => "application/x-msmediaview", "nws" => "message/rfc822", "oda" => "application/oda",
+        "p10" => "application/pkcs10", "p12" => "application/x-pkcs12", "p7b" => "application/x-pkcs7-certificates", "p7c" => "application/x-pkcs7-mime", "p7m" => "application/x-pkcs7-mime", "p7r" => "application/x-pkcs7-certreqresp", "p7s" => "application/x-pkcs7-signature", "pbm" => "image/x-portable-bitmap", "pdf" => "application/pdf", "pfx" => "application/x-pkcs12",
+        "pgm" => "image/x-portable-graymap", "pko" => "application/ynd.ms-pkipko", "pma" => "application/x-perfmon", "pmc" => "application/x-perfmon", "pml" => "application/x-perfmon", "pmr" => "application/x-perfmon", "pmw" => "application/x-perfmon", "png" => "image/png", "pnm" => "image/x-portable-anymap", "pot" => "application/vnd.ms-powerpoint", "ppm" => "image/x-portable-pixmap",
+        "pps" => "application/vnd.ms-powerpoint", "ppt" => "application/vnd.ms-powerpoint", "prf" => "application/pics-rules", "ps" => "application/postscript", "pub" => "application/x-mspublisher", "qt" => "video/quicktime", "ra" => "audio/x-pn-realaudio", "ram" => "audio/x-pn-realaudio", "ras" => "image/x-cmu-raster", "rgb" => "image/x-rgb",
+        "rmi" => "audio/mid", "roff" => "application/x-troff", "rtf" => "application/rtf", "rtx" => "text/richtext", "scd" => "application/x-msschedule", "sct" => "text/scriptlet", "setpay" => "application/set-payment-initiation", "setreg" => "application/set-registration-initiation", "sh" => "application/x-sh", "shar" => "application/x-shar",
+        "sit" => "application/x-stuffit", "snd" => "audio/basic", "spc" => "application/x-pkcs7-certificates", "spl" => "application/futuresplash", "src" => "application/x-wais-source", "sst" => "application/vnd.ms-pkicertstore", "stl" => "application/vnd.ms-pkistl", "stm" => "text/html", "svg" => "image/svg+xml", "sv4cpio" => "application/x-sv4cpio",
+        "sv4crc" => "application/x-sv4crc", "t" => "application/x-troff", "tar" => "application/x-tar", "tcl" => "application/x-tcl", "tex" => "application/x-tex", "texi" => "application/x-texinfo", "texinfo" => "application/x-texinfo", "tgz" => "application/x-compressed", "tif" => "image/tiff", "tiff" => "image/tiff",
+        "tr" => "application/x-troff", "trm" => "application/x-msterminal", "tsv" => "text/tab-separated-values", "txt" => "text/plain", "uls" => "text/iuls", "ustar" => "application/x-ustar", "vcf" => "text/x-vcard", "vrml" => "x-world/x-vrml", "wav" => "audio/x-wav", "wcm" => "application/vnd.ms-works",
+        "wdb" => "application/vnd.ms-works", "wks" => "application/vnd.ms-works", "wmf" => "application/x-msmetafile", "wps" => "application/vnd.ms-works", "wri" => "application/x-mswrite", "wrl" => "x-world/x-vrml", "wrz" => "x-world/x-vrml", "xaf" => "x-world/x-vrml", "xbm" => "image/x-xbitmap", "xla" => "application/vnd.ms-excel",
+        "xlc" => "application/vnd.ms-excel", "xlm" => "application/vnd.ms-excel", "xls" => "application/vnd.ms-excel", "xlt" => "application/vnd.ms-excel", "xlw" => "application/vnd.ms-excel", "xof" => "x-world/x-vrml", "xpm" => "image/x-xpixmap", "xwd" => "image/x-xwindowdump", "z" => "application/x-compress", "zip" => "application/zip");    
+}
diff --git a/commands/core/runserver.drush.inc b/commands/core/runserver.drush.inc
new file mode 100644
index 0000000..95b3bde
--- /dev/null
+++ b/commands/core/runserver.drush.inc
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * @file
+ *   Built in http server commands.
+ */
+
+require_once 'httpserver/httpserver.php';
+
+/**
+ * Implementation of hook_drush_help().
+ */
+function runserver_drush_help($section) {
+  switch ($section) {
+    case 'drush:runserver':
+      return dt("Runs a lightweight built in http server for development. Don't use this for production.");
+  }
+}
+
+/**
+ * Implementation of hook_drush_command().
+ */
+function runserver_drush_command() {
+  $items = array();
+
+  $items['runserver'] = array(
+    'description' => 'Runs a lightweight built in http server for development.',
+    'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_ROOT,
+    'arguments' => array(
+      'port' => 'Port number to bind to (default 8080)',
+    ),
+    'aliases' => array('rs'),
+  );
+  return $items;
+}
+
+/**
+ * Print out the specified shell aliases.
+ */
+function drush_core_runserver($port = '8080') {
+  $server = new DrupalServer(array('port' => $port));
+  $server->run_forever();
+}
+
+/**
+ * This class handles Drupal specific routing rules.
+ */
+class DrupalServer extends HTTPServer {
+  function route_request($request) {
+    $doc_root = DRUPAL_ROOT;
+    $path = $doc_root . $request->uri;
+    if (is_file(realpath($path))) {
+      return $this->get_static_response($request, $path);
+    }
+    $cgi_env = array(
+      'REMOTE_ADDR' => SERVER_ADDR,
+      'QUERY_STRING' => 'q=' . ltrim($uri, '/')
+    );
+    return $this->get_php_response($request, $doc_root . '/index.php', $cgi_env);
+  }        
+}
\ No newline at end of file
