Completed
Push — master ( 7113c4...8875d3 )
by Yassine
14s
created

UrlDetectionHandler::isBehindSsl()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 0
dl 0
loc 14
ccs 8
cts 8
cp 1
crap 3
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Copyright 2017 Facebook, Inc.
4
 *
5
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
6
 * use, copy, modify, and distribute this software in source code or binary
7
 * form for use in connection with the web services and APIs provided by
8
 * Facebook.
9
 *
10
 * As with any software that integrates with the Facebook platform, your use
11
 * of this software is subject to the Facebook Developer Principles and
12
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
13
 * shall be included in all copies or substantial portions of the software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
 * DEALINGS IN THE SOFTWARE.
22
 */
23
namespace Facebook\Url;
24
25
/**
26
 * @package Facebook
27
 */
28
class UrlDetectionHandler implements UrlDetectionInterface
29
{
30
    /**
31
     * {@inheritdoc}
32
     */
33 7
    public function getCurrentUrl()
34
    {
35 7
        return $this->getHttpScheme() . '://' . $this->getHostName() . $this->getServerVar('REQUEST_URI');
36
    }
37
38
    /**
39
     * Get the currently active URL scheme.
40
     *
41
     * @return string
42
     */
43 7
    protected function getHttpScheme()
44
    {
45 7
        return $this->isBehindSsl() ? 'https' : 'http';
46
    }
47
48
    /**
49
     * Tries to detect if the server is running behind an SSL.
50
     *
51
     * @return bool
52
     */
53 7
    protected function isBehindSsl()
54
    {
55
        // Check for proxy first
56 7
        $protocol = $this->getHeader('X_FORWARDED_PROTO');
57 7
        if ($protocol) {
58 3
            return $this->protocolWithActiveSsl($protocol);
59
        }
60
61 4
        $protocol = $this->getServerVar('HTTPS');
62 4
        if ($protocol) {
63 1
            return $this->protocolWithActiveSsl($protocol);
64
        }
65
66 3
        return (string)$this->getServerVar('SERVER_PORT') === '443';
67
    }
68
69
    /**
70
     * Detects an active SSL protocol value.
71
     *
72
     * @param string $protocol
73
     *
74
     * @return bool
75
     */
76 4
    protected function protocolWithActiveSsl($protocol)
77
    {
78 4
        $protocol = strtolower((string)$protocol);
79
80 4
        return in_array($protocol, ['on', '1', 'https', 'ssl'], true);
81
    }
82
83
    /**
84
     * Tries to detect the host name of the server.
85
     *
86
     * Some elements adapted from
87
     *
88
     * @see https://github.com/symfony/HttpFoundation/blob/master/Request.php
89
     *
90
     * @return string
91
     */
92 7
    protected function getHostName()
93
    {
94
        // Check for proxy first
95 7
        $header = $this->getHeader('X_FORWARDED_HOST');
96 7
        if ($header && $this->isValidForwardedHost($header)) {
97
            $elements = explode(',', $header);
98
            $host = $elements[count($elements) - 1];
99 7
        } elseif (!$host = $this->getHeader('HOST')) {
100
            if (!$host = $this->getServerVar('SERVER_NAME')) {
101
                $host = $this->getServerVar('SERVER_ADDR');
102
            }
103
        }
104
105
        // trim and remove port number from host
106
        // host is lowercase as per RFC 952/2181
107 7
        $host = strtolower(preg_replace('/:\d+$/', '', trim($host)));
108
109
        // Port number
110 7
        $scheme = $this->getHttpScheme();
111 7
        $port = $this->getCurrentPort();
112 7
        $appendPort = ':' . $port;
113
114
        // Don't append port number if a normal port.
115 7
        if (($scheme == 'http' && $port == '80') || ($scheme == 'https' && $port == '443')) {
116 4
            $appendPort = '';
117
        }
118
119 7
        return $host . $appendPort;
120
    }
121
122 7
    protected function getCurrentPort()
123
    {
124
        // Check for proxy first
125 7
        $port = $this->getHeader('X_FORWARDED_PORT');
126 7
        if ($port) {
127 3
            return (string)$port;
128
        }
129
130 4
        $protocol = (string)$this->getHeader('X_FORWARDED_PROTO');
131 4
        if ($protocol === 'https') {
132
            return '443';
133
        }
134
135 4
        return (string)$this->getServerVar('SERVER_PORT');
136
    }
137
138
    /**
139
     * Returns the a value from the $_SERVER super global.
140
     *
141
     * @param string $key
142
     *
143
     * @return string
144
     */
145 7
    protected function getServerVar($key)
146
    {
147 7
        return isset($_SERVER[$key]) ? $_SERVER[$key] : '';
148
    }
149
150
    /**
151
     * Gets a value from the HTTP request headers.
152
     *
153
     * @param string $key
154
     *
155
     * @return string
156
     */
157 7
    protected function getHeader($key)
158
    {
159 7
        return $this->getServerVar('HTTP_' . $key);
160
    }
161
162
    /**
163
     * Checks if the value in X_FORWARDED_HOST is a valid hostname
164
     * Could prevent unintended redirections.
165
     *
166
     * @param string $header
167
     *
168
     * @return bool
169
     */
170
    protected function isValidForwardedHost($header)
171
    {
172
        $elements = explode(',', $header);
173
        $host = $elements[count($elements) - 1];
174
        
175
        return preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $host) //valid chars check
176
            && 0 < strlen($host) && strlen($host) < 254 //overall length check
177
            && preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $host); //length of each label
178
    }
179
}
180