Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Webserver (froscon)

Webserver (froscon)

Build your own Webserver with PHP.

Igor Wiedler

August 26, 2012
Tweet

More Decks by Igor Wiedler

Other Decks in Programming

Transcript

  1. • 1xx - Special protocol stuff • 2xx - All

    good • 3xx - Redirection • 4xx - You are stupid • 5xx - Oh crap! I’m a fucking moron. Status codes
  2. Status codes • 7xx - Developer Error • 710 -

    PHP • 712 - NoSQL • 720 - Unpossible • 724 - This line should be unreachable • 745 - I don’t always test my code, but when I do I do it in production • 759 - Unexpexted T_PAAMAYIM_NEKUDOTAYIM
  3. Redirection • 301 - Moved Permanently • 302 - Found

    (misused) • 303 - See Other • 305 - Use Proxy • 307 - Temporary Redirect • Location header
  4. Caching • Cache-Control: public/private, max-age • ETag / If-Match •

    Last-Modified / If-Modified-Since • 403 - Not Modified
  5. Basic Auth • 401 - Unauthorized • 403 - Forbidden

    • WWW-Authenticate: Basic realm=”foo” • Authorization: base64(username:password)
  6. Digest Auth • WWW-Authenticate: • Digest realm=”foo” • qop=”auth” •

    nonce=”123” • opaque=”456” • Authorization: • ha1 = md5( username:realm:pass ) • ha2 = md5( method:digestURI ) • value = md5(ha1:nonce:ha2)
  7. Privacy GET /index.html HTTP/1.1. Host: 127.0.0.1. Connection: keep-alive. User-Agent: Mozilla/5.0

    (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/ 21.0.1180.82 Safari/537.1. Accept: text/html,application/xhtml+xml,application/ xml;q=0.9,*/*;q=0.8. Referer: http://127.0.0.1/foo.html. Accept-Encoding: gzip,deflate,sdch. Accept-Language: en-US,en;q=0.8. Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3. .
  8. Salami Pizza +---+--------------+----------------+ | 7 | Application | HTTP, FTP,

    DNS | | 6 | Presentation | TLS | | 5 | Session | TCP | | 4 | Transport | TCP, UDP | | 3 | Network | IP | | 2 | Data Link | MAC, PPP | | 1 | Physical | | +---+--------------+----------------+
  9. PHP Socket API $address = '0.0.0.0'; $port = 5000; $sock

    = socket_create(AF_INET, SOCK_STREAM, 0); socket_bind($sock, $address, $port); socket_listen($sock); while (true) { $conn = socket_accept($sock); socket_write($conn, date(DATE_RFC822)."\n"); socket_close($conn); }
  10. $context = stream_context_create([ 'http' => [ 'method' => 'POST', 'header'

    => "Accept: application/json\r\n", 'content' => '{"secret":"data"}', ] ]); $contents = file_get_contents('https://igor.io', false, $context); var_dump($http_response_header);
  11. $sock = stream_socket_server('tcp://0.0.0.0:5000'); while (true) { $response = implode("\r\n", [

    'HTTP/1.1 200 OK', 'Content-Length: 2', '', 'Hi', ]); $conn = stream_socket_accept($sock, -1); fwrite($conn, $response); }
  12. $request = implode("\r\n", [ 'GET / HTTP/1.1', 'Host: igor.io', '',

    '', ]); $expected = [ 'method' => 'GET', 'path' => '/', 'protocol' => 'HTTP/1.1', 'headers' => ['Host' => 'igor.io'], 'body' => '', ];
  13. $request = implode("\r\n", [ 'GET / HTTP/1.1', 'Host: igor.io', '',

    '', ]); $expected = [ 'method' => 'GET', 'path' => '/', 'protocol' => 'HTTP/1.1', 'headers' => ['Host' => 'igor.io'], 'body' => '', ]; assert($expected === parseRequest($request));
  14. function parseRequest($request) { $lines = explode("\r\n", $request); $requestLine = array_shift($lines);

    list($method, $path, $protocol) = explode(' ', $requestLine); $headers = []; while ($header = array_shift($lines)) { list($name, $value) = explode(':', $header, 2); $headers[trim($name)] = trim($value); } $body = array_shift($lines); return compact('method', 'path', 'protocol', 'headers', 'body'); }
  15. $responseSpec = [200, ['Content-Length' => '2'], 'Hi']; $response = implode("\r\n",

    [ 'HTTP/1.1 200 OK', 'Content-Length: 2', '', 'Hi', ]); assert($response === buildResponse($responseSpec));
  16. $responseSpec = [200, ['Content-Length' => '2'], 'Hi']; $response = implode("\r\n",

    [ 'HTTP/1.1 200 OK', 'Content-Length: 2', '', 'Hi', ]); assert($response === buildResponse($responseSpec));
  17. function buildResponse(array $responseSpec) { list($status, $headers, $body) = $responseSpec; return

    buildResponseStatusLine($status). buildResponseHeaders($headers). "\r\n". $body; }
  18. function buildResponseStatusLine($status) { $statusMap = [ 200 => 'OK', 404

    => 'Not Found', ]; return "HTTP/1.1 $status {$statusMap[$status]}\r\n"; }
  19. function buildResponseHeaders(array $headers) { $flatHeaders = []; foreach ($headers as

    $name => $value) { $flatHeaders[] = "$name: $value"; } return implode("\r\n", $flatHeaders)."\r\n"; }
  20. $conn = stream_socket_accept($sock, -1); $request = fread($conn, 512); $parsedRequest =

    parseRequest($request); switch ($parsedRequest['path']) { case '/': $responseSpec = [200, ['Content-Length' => 3], "Hi\n"]; break; default: $responseSpec = [404, ['Content-Length' => 10], "Not found\n"]; } fwrite($conn, buildResponse($responseSpec));
  21. Problem? • Missing error handling • Assumption: client closes connection

    • Assumption: request can be read in one go • Assumption: request no larger than 512 bytes • Assumption: request/response body fits in memory • Blocking I/O
  22. $readable = $read ?: null; $writable = $write ?: null;

    $except = null; if (stream_select($readable, $writable, $except, 1)) { if ($readable) { foreach ($readable as $stream) { ... } } if ($writable) { foreach ($writable as $stream) { ... } } }
  23. $server = stream_socket_server('tcp://127.0.0.1:1337'); $read = [$server]; $write = []; ...

    // deep in the event loop foreach ($readable as $stream) { if ($server === $stream) { $conn = stream_socket_accept($server, 0); $read[] = $conn; } else { $data = fread($stream, 1024); echo $data; } }
  24. $loop = React\EventLoop\Factory::create(); $socket = new React\Socket\Server($loop); $http = new

    React\Http\Server($socket, $loop); $http->on('request', function ($req, $rep) { $rep->writeHead(); $rep->end("Hello World!\n"); }); $socket->listen(8080); $loop->run();