-
Notifications
You must be signed in to change notification settings - Fork 16
Optionally enable caching of any request method #24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
52b1777
e0c1c0e
0a3b7c8
8151701
baf4fbd
8d9d48c
5b21d4b
cfa6135
8d8f8e9
fbc1dc7
e94b74a
65d819d
10da16c
9703885
490da8b
727cbf0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,6 +45,7 @@ final class CachePlugin implements Plugin | |
* @var int $cache_lifetime (seconds) To support serving a previous stale response when the server answers 304 | ||
* we have to store the cache for a longer time than the server originally says it is valid for. | ||
* We store a cache item for $cache_lifetime + max age of the response. | ||
* @var array $methods case insensitive list of request methods which can be cached. | ||
* } | ||
*/ | ||
public function __construct(CacheItemPoolInterface $pool, StreamFactory $streamFactory, array $config = []) | ||
|
@@ -64,7 +65,7 @@ public function handleRequest(RequestInterface $request, callable $next, callabl | |
{ | ||
$method = strtoupper($request->getMethod()); | ||
// if the request not is cachable, move to $next | ||
if ($method !== 'GET' && $method !== 'HEAD') { | ||
if (!in_array($method, $this->config['methods'])) { | ||
return $next($request); | ||
} | ||
|
||
|
@@ -205,7 +206,6 @@ private function getCacheControlDirective(ResponseInterface $response, $name) | |
$headers = $response->getHeader('Cache-Control'); | ||
foreach ($headers as $header) { | ||
if (preg_match(sprintf('|%s=?([0-9]+)?|i', $name), $header, $matches)) { | ||
|
||
// return the value for $name if it exists | ||
if (isset($matches[1])) { | ||
return $matches[1]; | ||
|
@@ -225,7 +225,7 @@ private function getCacheControlDirective(ResponseInterface $response, $name) | |
*/ | ||
private function createCacheKey(RequestInterface $request) | ||
{ | ||
return hash($this->config['hash_algo'], $request->getMethod().' '.$request->getUri()); | ||
return hash($this->config['hash_algo'], $request->getMethod().' '.$request->getUri().$request->getBody()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if the body is super large? Do we care? Also, I suggest a space (or any other non-common delimiter) between uri and body. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Without space this is backwards compatible with previous versions if body is empty. Adding a space there will invalidate old caches, but it is of course ok with me if needed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great. I did not think about that. How about doing something like: $body = (string) $request->getBody()
if (!empty($body)) {
$body = ' ' . $body;
}
return hash($this->config['hash_algo'], $request->getMethod().' '.$request->getUri().$body); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think we do not need to worry about keeping caches valid over deployments. but nyholms idea is good. as GET usually has empty body, it happens to keep old caches. regarding big body: this method is only executed on methods that should be cached, right? so body will be usually empty, except for things like soap which hopefully won't be big file uploads. you would not want to cache a file upload, anyways. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. GET and HEAD has (EDIT: generally) no bodies. If you change the default behavior, then it is on you if you get performance issues. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can add it. Why is the space needed BTW? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. GET can have body, for example when talking to elasticsearch: https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-body.html There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To make a difference between these two requests POST /foo
Host: www.example.com
bar POST /foob
Host: www.example.com
ar There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel we could unconditionally add the space. It is only cache so it is not that big deal if old cache gets invalidated when plugin is updated. What do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, sorry for the late response. I would prefer not invalidating cache when we can.. Im using Google Translate paid services and I know I will have to pay 100USD if my cache gets invalidated. Technically, this is okey. But we could be more nice to our users. =) |
||
} | ||
|
||
/** | ||
|
@@ -273,12 +273,20 @@ private function configureOptions(OptionsResolver $resolver) | |
'default_ttl' => 0, | ||
'respect_cache_headers' => true, | ||
'hash_algo' => 'sha1', | ||
'methods' => ['GET', 'HEAD'], | ||
]); | ||
|
||
$resolver->setAllowedTypes('cache_lifetime', ['int', 'null']); | ||
$resolver->setAllowedTypes('default_ttl', ['int', 'null']); | ||
$resolver->setAllowedTypes('respect_cache_headers', 'bool'); | ||
$resolver->setAllowedTypes('methods', 'array'); | ||
$resolver->setAllowedValues('hash_algo', hash_algos()); | ||
$resolver->setAllowedValues('methods', function ($value) { | ||
/* Any VCHAR, except delimiters. RFC7230 sections 3.1.1 and 3.2.6 */ | ||
$matches = preg_grep('/[^[:alnum:]!#$%&\'*\/+\-.^_`|~]/', $value); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you please add a comment here with the RFC number that defines this regular expression? just for future reference and to make it clear we did not come up with a random expression here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i am horrible with regular expressions. is this forcing upper case? we should either force that or uppercase ourselves when processing the options. PSR-7 forces method to upper case. when you specify something lower case, it would never match. the doc should explain that if you specify methods, you also need to specify GET if you still want those cached. and that you should do specific clients with different setup if you do soap and other stuff, to be sure only the right requests get cached... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does not force uppercase. I have no strong feelings whether we should force uppercase or not. I think most of the time people probably use uppercase methods. The regexp basically says is request method has any other characters than these the method is invalid. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks really complicated. Why not use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unless I have misunderstood the specs request method can also contain lowercase letters. Valid request methods are described in RFC7230-3.1.1 which states it is a token. Token is defined in RFC7230-3.2.6 which says any visible There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, yes you are correct. (Reading the code and RFC more carefully...). I was wrong assuming that HTTP methods are generally uppercase but PSR-7 specifies that case should not change. I think we are correct by running $matches = preg_grep('/[^[A-Z0-9]!#$%&\'*\/+\-.^_`|~]/', $value); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. were do we strtoupper? i think we should leave the method unchanged, given how psr-7 is specified. as mentioned above, guzzle psr-7 request does not follow the SHOULD of the spec. |
||
|
||
return empty($matches); | ||
}); | ||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
its case sensitive, not insensitive
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes of course. Brain malfunction...