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

Webserver (froscon)

Webserver (froscon)

Build your own Webserver with PHP.

Avatar for Igor Wiedler

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();