Share
Embed
HttpFoundation – An excursion in a Symfony2 component
In this presentation I talk about one of the most basic components of the Symfony2 framework, HttpFoundation, and how you can use it.
Magnus Nordlander
February 21, 2013
Transcript
35 slides
-
Who am I • Magnus Nordlander • Developer at E-butik.se
• ~6 years of Symfony experience • SensioLabs Certified Symfony Developer -
HTTP • Created by Tim Berners-Lee back in ’91 •
Current version is HTTP 1.1, standardized in ’99 in IETF RFC 2616 • Single best advice: You work with HTTP every day. Read RFC 2616. -
An HTTP request/response GET /foo/baz HTTP/1.1 Host: www.example.com HTTP/1.1 200
OK Date: Mon, 18 Feb 2013 20:38:34 GMT Content-Length: 12 Content-Type: text/plain Connection: close Hello world! -
Another request/response POST /?foo=quux HTTP/1.1 Host: www.example.com Content-Type: application/x-www-form- urlencoded
Content-Length: 9 bar=xyzzy HTTP/1.1 200 OK Content-Length: 21 Content-Type: text/plain Connection: close Foo: quux, Bar: xyzzy -
Another request/response POST /?foo=quux HTTP/1.1 Host: www.example.com Content-Type: application/x-www-form- urlencoded
Content-Length: 9 bar=xyzzy HTTP/1.1 200 OK Content-Length: 21 Content-Type: text/plain Connection: close Foo: quux, Bar: xyzzy Symfony\Component\HttpFoundation\Request Symfony\Component\HttpFoundation\Response -
As code… <?php $foo = $_GET[‘foo’]; $bar = $_POST[‘bar’]; header(‘Content-Type:
text/plain’); echo sprintf(“Foo: %s, Bar: %s”, $foo, $bar); <?php use Symfony\Component\HttpFoundation \Request; use Symfony\Component\HttpFoundation \Response; $request = Request::createFromGlobals(); $foo = $request->query->get(‘foo’); $bar = $request->request->get(‘bar’); $response = new Response( 200, sprintf(“Foo: %s, Bar: %s”, $foo, $bar), [‘Content-Type’ => ‘text/plain’]); $response->send(); -
Let’s look at the code <?php use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals(); $foo = $request->query->get(‘foo’); $bar = $request->request->get(‘foo’); $response = new Response(200, sprintf(“Foo: %s, Bar: %s”, $foo, $bar)); $response->send(); -
Let’s look at the code <?php use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals(); $foo = $request->query->get(‘foo’); $bar = $request->request->get(‘foo’); $response = new Response(200, sprintf(“Foo: %s, Bar: %s”, $foo, $bar)); $response->send(); Encapsulating the superglobals: great for testing, reduces hidden dependencies -
Let’s look at the code <?php use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals(); $foo = $request->query->get(‘foo’); $bar = $request->request->get(‘foo’); $response = new Response(200, sprintf(“Foo: %s, Bar: %s”, $foo, $bar)); $response->send(); Because these come from an object, easily fakable in tests. Additional features like default values, fetching by path. -
Let’s look at the code <?php use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals(); $foo = $request->query->get(‘foo’); $bar = $request->request->get(‘foo’); $response = new Response(200, sprintf(“Foo: %s, Bar: %s”, $foo, $bar)); $response->send(); Entire response encapsulated: Great for functional tests. No output (nor headers) until send. Processable by listener, middleware etc. -
Edge case handling <?php // Let’s get the client IP
address $ip = $_SERVER[‘REMOTE_ADDR’]; // Right? -
Edge case handling • One word: Proxy • Requests through
proxies originate through the proxy IP • Most send the X-Forwarded-For header, with the client IP • And yes, you want proxy support (for performance reasons) -
Edge case handling <?php // Let’s get the client IP
address if (isset($_SERVER[‘X_FORWARDED_FOR’])) { // This should handle those pesky proxies $ip = $_SERVER[‘X_FORWARDED_FOR’]; } else { $ip = $_SERVER[‘REMOTE_ADDR’]; } -
Edge case handling • Now we have a security flaw
• Any client can send the X-Forwarded-For header, spoofing its IP • Only trusted proxies should be allowed to do that -
Edge case handling <?php // Let’s get the client IP
address $originatingIp = $_SERVER[‘REMOTE_ADDR’]; $clientIp = isset($_SERVER[‘X_FORWARDED_FOR’]) ? $_SERVER[‘X_FORWARDED_FOR’] : null; $trustedProxies = [‘10.0.0.1’, ‘10.0.0.2’]; if (in_array($originatingIp, $trustedProxies) && $clientIp) { return $clientIp; } else { return $originatingIp; } -
Edge case handling • That’s not quite it though... •
You can have multiple upstream proxies, and they send a comma separated list of IP addresses. • Also, not all proxies use X-Forwarded-For, because that’s never been standardized • Are you getting a headache yet? Let’s look at how to do it with HttpFoundation instead. -
Edge case handling <?php // Let’s get the client IP
address use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; $request = Request::createFromGlobals(); Request::setTrustedProxies([‘10.0.0.1’, ‘10.0.0.2’]); // Other header name? // Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, ‘Forwarded-For’); $ip = $request->getClientIp(); -
Edge case handling • X-Forwarded-Host, X-Forwarded-Proto, X-Forwarded-Port • X-Http-Method-Override •
Accept headers with quality • Non-standard request content types -
The Request class • Can be created from Superglobals, or
manually • HTTP based, not CGI based • HTTP based methods and properties -
The Request class • Important ParameterBags • query • request
• headers • attributes -
The Request class • Assorted methods • getMethod • getContent($asResource
= false) • getRequestFormat -
The Response class • Also HTTP based • Common HTTP
Header “clusters” have specific methods, e.g. caching • public function __construct($content = '', $status = 200, $headers = []) -
Parameter bags • Like PHP arrays, but better • Defaults
handling • Deep get syntax • Specialized subclasses like HeaderBag and ServerBag -
What else is there? • HTTP caching support (expiration and
validation) • Sessions • Storage abstraction • Flash parameters • Cookies -
What else is there? • Uploaded file handling • Streamed
responses • Accept header parsing -
HttpKernelInterface • Housed in the HttpKernel component • One of
the most important interfaces in the Symfony2 ecosystem • public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true); • Returns Response -
HttpKernelInterface • Foundation for the Request/Response paradigm of Symfony2 •
Because of this interface, all web stacks implementing HttpKernelInterface is inherently compatible! -
Learn more! • Symfony.com • Middlewares with HttpKernel: https://igor.io/2013/02/02/http-kernel- middlewares.html
• Read RFC2616: http://www.w3.org/Protocols/rfc2616/rfc2616.html