blob: 9c70f4dcec8f2683dc3a21f606f8a1edfea4951c (
plain)
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
|
<?php
namespace Guzzle\Plugin\Md5;
use Guzzle\Common\Event;
use Guzzle\Common\Exception\UnexpectedValueException;
use Guzzle\Http\Message\Response;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Ensures that an the MD5 hash of an entity body matches the Content-MD5
* header (if set) of an HTTP response. An exception is thrown if the
* calculated MD5 does not match the expected MD5.
*/
class Md5ValidatorPlugin implements EventSubscriberInterface
{
/** @var int Maximum Content-Length in bytes to validate */
protected $contentLengthCutoff;
/** @var bool Whether or not to compare when a Content-Encoding is present */
protected $contentEncoded;
/**
* @param bool $contentEncoded Calculating the MD5 hash of an entity body where a Content-Encoding was
* applied is a more expensive comparison because the entity body will need to
* be compressed in order to get the correct hash. Set to FALSE to not
* validate the MD5 hash of an entity body with an applied Content-Encoding.
* @param bool|int $contentLengthCutoff Maximum Content-Length (bytes) in which a MD5 hash will be validated. Any
* response with a Content-Length greater than this value will not be validated
* because it will be deemed too memory intensive.
*/
public function __construct($contentEncoded = true, $contentLengthCutoff = false)
{
$this->contentLengthCutoff = $contentLengthCutoff;
$this->contentEncoded = $contentEncoded;
}
public static function getSubscribedEvents()
{
return array('request.complete' => array('onRequestComplete', 255));
}
/**
* {@inheritdoc}
* @throws UnexpectedValueException
*/
public function onRequestComplete(Event $event)
{
$response = $event['response'];
if (!$contentMd5 = $response->getContentMd5()) {
return;
}
$contentEncoding = $response->getContentEncoding();
if ($contentEncoding && !$this->contentEncoded) {
return false;
}
// Make sure that the size of the request is under the cutoff size
if ($this->contentLengthCutoff) {
$size = $response->getContentLength() ?: $response->getBody()->getSize();
if (!$size || $size > $this->contentLengthCutoff) {
return;
}
}
if (!$contentEncoding) {
$hash = $response->getBody()->getContentMd5();
} elseif ($contentEncoding == 'gzip') {
$response->getBody()->compress('zlib.deflate');
$hash = $response->getBody()->getContentMd5();
$response->getBody()->uncompress();
} elseif ($contentEncoding == 'compress') {
$response->getBody()->compress('bzip2.compress');
$hash = $response->getBody()->getContentMd5();
$response->getBody()->uncompress();
} else {
return;
}
if ($contentMd5 !== $hash) {
throw new UnexpectedValueException(
"The response entity body may have been modified over the wire. The Content-MD5 "
. "received ({$contentMd5}) did not match the calculated MD5 hash ({$hash})."
);
}
}
}
|