Issues (21)

src/Infrastructure/Header/NativeHeaderProvider.php (1 issue)

Severity
1
<?php declare(strict_types=1);
2
3
/**
4
 * @license  http://opensource.org/licenses/mit-license.php MIT
5
 * @link     https://github.com/nicoSWD
6
 * @author   Nicolas Oelgart <[email protected]>
7
 */
8
namespace nicoSWD\SecHeaderCheck\Infrastructure\Header;
9
10
use nicoSWD\SecHeaderCheck\Domain\Header\AbstractHeaderProvider;
11
use nicoSWD\SecHeaderCheck\Domain\Header\Exception\ConnectionTimeoutException;
12
use nicoSWD\SecHeaderCheck\Domain\Header\Exception\MaxHeaderSizeExceededException;
13
use nicoSWD\SecHeaderCheck\Domain\URL\URL;
14
15
final class NativeHeaderProvider extends AbstractHeaderProvider
16
{
17
    private const ONE_KB = 1024;
18
    private const MAX_HEADER_SIZE = self::ONE_KB * 8;
19
20
    public function getRawHeaders(URL $url): string
21
    {
22
        $fp = $this->connect($url);
23
        $this->sendRequest($url, $fp);
24
25
        $headers = '';
26
        $bytesRead = 0;
27
28
        while (!feof($fp)) {
29
            $line = $this->readLine($fp);
30
31
            if ($line === false || trim($line) === '') {
32
                break;
33
            }
34
35
            $bytesRead += strlen($line);
36
37
            if ($this->hasExceededMaxSize($bytesRead)) {
38
                throw new MaxHeaderSizeExceededException();
39
            }
40
41
            $headers .= $line;
42
        }
43
44
        fclose($fp);
45
46
        return $headers;
47
    }
48
49
    private function readLine($fp)
50
    {
51
        return fgets($fp, self::ONE_KB * 2);
52
    }
53
54
    private function hasExceededMaxSize(int $bytesRead): bool
55
    {
56
        return $bytesRead > self::MAX_HEADER_SIZE;
57
    }
58
59
    private function sendRequest(URL $url, $fp): void
60
    {
61
        $request = "HEAD {$url->path()}{$url->query()} HTTP/1.1\r\n";
62
        $request .= "Host: {$url->host()}\r\n";
63
        $request .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n";
64
        $request .= "User-Agent: Security Headers Scanner/1.0 (https://github.com/nicoSWD)\r\n";
65
        $request .= "Connection: Close\r\n\r\n";
66
67
        fwrite($fp, $request);
68
    }
69
70
    private function connect(URL $url)
71
    {
72
        if ($url->isHttps()) {
73
            $scheme = 'ssl://';
74
        } else {
75
            $scheme = '';
76
        }
77
78
        $fp = @fsockopen($scheme . $url->host(), $url->port(), $errNo, $errStr, $this->connectionTimeout);
79
80
        if (!$fp) {
0 ignored issues
show
$fp is of type false|resource, thus it always evaluated to false.
Loading history...
81
            throw new ConnectionTimeoutException();
82
        }
83
84
        return $fp;
85
    }
86
}
87