-
Notifications
You must be signed in to change notification settings - Fork 60
/
HttpProxyClient.php
149 lines (134 loc) · 4.54 KB
/
HttpProxyClient.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
<?php
/*
* This file is part of the FOSHttpCache package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\HttpCache\ProxyClient;
use Http\Discovery\MessageFactoryDiscovery;
use Http\Message\RequestFactory;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Base class for HTTP based caching proxy client.
*
* @author David de Boer <david@driebit.nl>
*/
abstract class HttpProxyClient implements ProxyClient
{
/**
* Dispatcher for invalidation HTTP requests.
*
* @var HttpDispatcher
*/
private $httpDispatcher;
/**
* @var RequestFactory
*/
private $requestFactory;
/**
* The options configured in the constructor argument or default values.
*
* @var array The resolved options
*/
protected $options;
/**
* Constructor.
*
* The base class has no options.
*
* @param Dispatcher $httpDispatcher Helper to send HTTP requests to caching proxy
* @param array $options Options for this client
* @param RequestFactory|null $messageFactory Factory for PSR-7 messages. If none supplied,
* a default one is created
*/
public function __construct(
Dispatcher $httpDispatcher,
array $options = [],
?RequestFactory $messageFactory = null
) {
$this->httpDispatcher = $httpDispatcher;
$this->options = $this->configureOptions()->resolve($options);
$this->requestFactory = $messageFactory ?: MessageFactoryDiscovery::find();
}
public function flush()
{
return $this->httpDispatcher->flush();
}
/**
* Get options resolver with default settings.
*
* @return OptionsResolver
*/
protected function configureOptions()
{
return new OptionsResolver();
}
/**
* Create a request and queue it with the HTTP dispatcher.
*
* @param string $method
* @param string|UriInterface $url
* @param bool $validateHost see Dispatcher::invalidate
* @param resource|string|StreamInterface|null $body
*/
protected function queueRequest($method, $url, array $headers, $validateHost = true, $body = null)
{
$this->httpDispatcher->invalidate(
$this->requestFactory->createRequest($method, $url, $headers, $body),
$validateHost
);
}
/**
* Make sure that the tags are valid.
*
* Reusable function for proxy clients.
* Escapes `,` and `\n` (newline) characters.
*
* Note: This is not a safe escaping function, it can lead to collisions,
* e.g. between "foo,bar" and "foo_bar". But from the nature of the data,
* such collisions are unlikely, and from the function of cache tagging,
* collisions would in the worst case lead to unintended invalidations,
* which is not a bug.
*
* @param array $tags The tags to escape
*
* @return array Sane tags
*/
protected function escapeTags(array $tags)
{
array_walk($tags, function (&$tag) {
// WARNING: changing the list of characters that are escaped is a BC break for existing installations,
// as existing tags on the cache would not be invalidated anymore if they contain a character that is
// newly escaped
$tag = str_replace([',', "\n"], '_', $tag);
});
return $tags;
}
/**
* Calculate how many tags fit into the header.
*
* This assumes that the tags are separated by one character.
*
* @param string[] $escapedTags
* @param string $glue The concatenation string to use
*
* @return int Number of tags per tag invalidation request
*/
protected function determineTagsPerHeader($escapedTags, $glue)
{
if (mb_strlen(implode($glue, $escapedTags)) < $this->options['header_length']) {
return count($escapedTags);
}
/*
* estimate the amount of tags to invalidate by dividing the max
* header length by the largest tag (minus the glue length)
*/
$tagsize = max(array_map('mb_strlen', $escapedTags));
return floor($this->options['header_length'] / ($tagsize + strlen($glue))) ?: 1;
}
}