GitHub PullRequestReviewCommentEvent at 2014-05-17 11:51:08
Repository: FOSHttpCache
Actor: stof
Payload: {"comment":{"url":"https://api.github.com/repos/FriendsOfSymfony/FOSHttpCache/pulls/comments/12768123","id":12768123,"diff_hunk":"@@ -1,93 +1,147 @@\n-# User Context Caching\n+Cache on User Context\n+=====================\n \n-The rendering of a view in your application may be different\n-if the user is authenticated, by his rights or by other information.\n+In applications that differentiate between user types, served content can be\n+different per user type. For instance, on one and the same URL a guest sees a\n+‘Log in’ message; an editor sees an ‘Edit’ button and the admin a link that\n+says\n \n-* [Overview](#overview)\n-* [HashGenerator](#hashgenerator)\n-* [Implementation](#implementation)\n-* [Varnish setup](#varnishsetup)\n-\n-## Overview\n-\n-This functionality can be implemented with the following workflow :\n-\n-1. A client send a request to `/foo.php` url\n-2. The HttpProxy receive the request, extract the session id (a unique identifier for each user), and send an HEAD request on a specific url (like `/head.php`)\n-    * If not cached, the backend response contains a X-FOSHttpCache-Hash header, where the value is different for each category of user : user context\n-\n-      If you want to cache this call you must vary the response on the unique identifier (here we use the X-FOSHttpCache-SessionId header)\n-  * If cached, the response is already available in the HttpProxy\n-3. The real request is then made to the `/foo.php` url\n-\n-  This request can be cached with a Vary on the X-FOSHttpCache-Hash header. So all the clients which have the same hash will have the same response and only the first request will make a call to the backend, all others can be returned by the HttpProxy.\n-\n-## HashGenerator\n-\n-FOSHttpCache provide tool to generate an hash depending on the user context.\n+The FOSHttpCache library includes a solution to vary cached responses based on\n+user context (whether the user is authenticated, his access rights, or other\n+information).\n \n-Your application must make sure to provide a [ContextProviderInterface](../src/UserContext/ContextProviderInterface.php)\n-for each context that influences the rendered output. The providers update the [UserContext](../src/UserContext/UserContext.php)\n-with parameters having an impact on the view.\n+* [Overview](#overview)\n+* [Proxy Client Configuration](#proxy-client-configuration)\n+* [Calculating the User Context Hash](#calculating-the-user-context-hash)\n+* [Context Providers](#context-providers)\n+* [Returning the User Context Hash](#returning-the-user-context-hash)\n+\n+Overview\n+--------\n+\n+Caching on user context works as follows:\n+\n+1. A client requests `/foo.php` (the *original request*).\n+2. The [caching proxy](invalidation-introduction.md#http-caching-terminology)\n+   receives the request. It reads the session id, which is unique or each\n+   user. The caching proxy sends a HEAD request (the *hash request*) with that\n+   session id in custom header (e.g., `X-FOSHttpCache-SessionId`) to a specific\n+   URL, e.g., `/auth.php`.\n+3. The [application](invalidation-introduction.md#http-caching-terminology)\n+   receives the hash request. The application knows the client’s user context\n+   (roles, permissions, etc.) and generates a hash based on that information.\n+   The application then returns a response containing that hash in a custom\n+   header (`X-FOSHttpCache-Hash`).\n+4. The caching proxy receives the hash response, copies the hash header to the\n+   client’s original request for `/foo.php` and restarts that request.\n+5. The caching proxy is configured to vary on `X-FOSHttpCache-Hash`, so returns\n+   the appropriate representation of `/foo.php` to the client.\n+\n+Proxy Client Configuration\n+--------------------------\n+\n+Currently, user context caching is only supported by Varnish. See the\n+[Varnish Configuration Chapter](varnish-configuration.md#user-context) on how\n+to prepare Varnish for user context caching.\n+\n+Calculating the User Context Hash\n+---------------------------------\n+\n+The user context hash calculation (step 3 above) is managed by the\n+[HashGenerator](../src/UserContext/HashGenerator.php). Because the calculation\n+itself will be different per application, you need to implement at least one\n+ContextProvider and register that with the HashGenerator.\n \n-Then, you need to register this provider in the [HashGenerator](../src/UserContext/HashGenerator.php)\n-with the `registerProvider` method.\n+```php\n+use namespace FOS\\HttpCache\\UserContext\\HashGenerator;\n \n-Once all provider are registered you can call the `generateHash` method to get the Hash for the current user context.\n+$hashGenerator = new HashGenerator();\n+$hashGenerator->registerProvider(IsAuthenticatedProvider());\n+$hashGenerator->registerProvider(RoleProvider());\n+```\n \n-## Implementation\n+Once all providers are registered you can call `generateHash()` to get the hash\n+for the current user context.\n \n-FOSHttpCache does not implement transformation of the Response part. However, if you use\n- Symfony2, an implementation is available in the [FOSHttpCacheBundle](https://github.com/FriendsOfSymfony/FOSHttpCacheBundle).\n+Context Providers\n+-----------------\n \n-Here is a simple implementation example :\n+Each provider is passed the [UserContext](../src/UserContext/UserContext.php)\n+and updates that with parameters which influence the varied response.\n \n-AuthenticationService : \n+A provider that looks at whether the user is authenticated could look like this:\n \n ```php\n-<?php\n+use namespace FOS\\HttpCache\\UserContext\\ContextProviderInterface;\n+use namespace FOS\\HttpCache\\UserContext\\UserContext;\n \n-class AuthenticationService implements ContextProviderInterface\n+class IsAuthenticatedProvider implements ContextProviderInterface\n {\n-    ...\n+    protected $user;\n \n-    public function isAuthenticated()\n+    public function __construct(User $user)\n     {\n-        return $this->authenticated;\n+        $this->user = $user;\n     }\n \n-    public function updateUserContext(UserContext $context)\n+    public function updateUserContext(UserContext $userContext)\n     {\n-        $context->addParameter('authenticated', $this->isAuthenticated());\n+        $context->addParameter('authenticated', $this->user->isAuthenticated());\n     }\n }\n-\n ```\n \n-The head.php file, called by the first request. We say that this hash is valid for one hour, a good practice is to set the same ttl as your session one.\n+Returning the User Context Hash\n+-------------------------------\n+\n+It is up to you to return the user context hash in response to the hash request\n+(`/auth.php` in step 3 above):\n \n ```php\n-<?php\n-// /head.php file\n-$authenticationService = new AuthenticationService();\n+$hash = $hashGenerator->generateHash();\n \n-$hashGenerator = new HashGenerator();\n-$hashGenerator->registerProvider($authenticationService);\n+if ('HEAD' == strtoupper($_SERVER['REQUEST_METHOD'])) {\n+    header(sprintf('X-FOSHttpCache-Hash: %s', $hash));\n+    exit;\n+}\n+\n+// 404 Method not allowed in case of non-HEAD requests","path":"doc/user-context.md","position":158,"original_position":158,"commit_id":"a9a0690cc0a9996b89467ceefe61011403ac83fb","original_commit_id":"a9a0690cc0a9996b89467ceefe61011403ac83fb","user":{"login":"stof","id":439401,"avatar_url":"https://avatars.githubusercontent.com/u/439401?","gravatar_id":"7894bbdf1c05b18a1444ad8c76c9d583","url":"https://api.github.com/users/stof","html_url":"https://github.com/stof","followers_url":"https://api.github.com/users/stof/followers","following_url":"https://api.github.com/users/stof/following{/other_user}","gists_url":"https://api.github.com/users/stof/gists{/gist_id}","starred_url":"https://api.github.com/users/stof/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stof/subscriptions","organizations_url":"https://api.github.com/users/stof/orgs","repos_url":"https://api.github.com/users/stof/repos","events_url":"https://api.github.com/users/stof/events{/privacy}","received_events_url":"https://api.github.com/users/stof/received_events","type":"User","site_admin":false},"body":"405, not 404","created_at":"2014-05-17T11:51:08Z","updated_at":"2014-05-17T11:51:08Z","html_url":"https://github.com/FriendsOfSymfony/FOSHttpCache/pull/66#discussion_r12768123","pull_request_url":"https://api.github.com/repos/FriendsOfSymfony/FOSHttpCache/pulls/66","_links":{"self":{"href":"https://api.github.com/repos/FriendsOfSymfony/FOSHttpCache/pulls/comments/12768123"},"html":{"href":"https://github.com/FriendsOfSymfony/FOSHttpCache/pull/66#discussion_r12768123"},"pull_request":{"href":"https://api.github.com/repos/FriendsOfSymfony/FOSHttpCache/pulls/66"}}}}