Passed
Pull Request — master (#888)
by Tobias
01:50
created

UrlDetectionHandler::getHostName()   D

Complexity

Conditions 9
Paths 8

Size

Total Lines 28
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 14
nc 8
nop 0
dl 0
loc 28
rs 4.909
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
 */
24
namespace Facebook\Url;
25
26
/**
27
 *
28
 * @package Facebook
29
 */
30
class UrlDetectionHandler implements UrlDetectionInterface
31
{
32
    /**
33
     * @inheritdoc
34
     */
35
    public function getCurrentUrl()
36
    {
37
        return $this->getHttpScheme() . '://' . $this->getHostName() . $this->getServerVar('REQUEST_URI');
38
    }
39
40
    /**
41
     * Get the currently active URL scheme.
42
     *
43
     * @return string
44
     */
45
    protected function getHttpScheme()
46
    {
47
        return $this->isBehindSsl() ? 'https' : 'http';
48
    }
49
50
    /**
51
     * Tries to detect if the server is running behind an SSL.
52
     *
53
     * @return boolean
54
     */
55
    protected function isBehindSsl()
56
    {
57
        // Check for proxy first
58
        $protocol = $this->getHeader('X_FORWARDED_PROTO');
59
        if ($protocol) {
60
            return $this->protocolWithActiveSsl($protocol);
61
        }
62
63
        $protocol = $this->getServerVar('HTTPS');
64
        if ($protocol) {
65
            return $this->protocolWithActiveSsl($protocol);
66
        }
67
68
        return (string)$this->getServerVar('SERVER_PORT') === '443';
69
    }
70
71
    /**
72
     * Detects an active SSL protocol value.
73
     *
74
     * @param string $protocol
75
     *
76
     * @return boolean
77
     */
78
    protected function protocolWithActiveSsl($protocol)
79
    {
80
        $protocol = strtolower((string)$protocol);
81
82
        return in_array($protocol, ['on', '1', 'https', 'ssl'], true);
83
    }
84
85
    /**
86
     * Tries to detect the host name of the server.
87
     *
88
     * Some elements adapted from
89
     *
90
     * @see https://github.com/symfony/HttpFoundation/blob/master/Request.php
91
     *
92
     * @return string
93
     */
94
    protected function getHostName()
95
    {
96
        // Check for proxy first
97
        $header = $this->getHeader('X_FORWARDED_HOST');
98
        if ($header && $this->isValidForwardedHost($header)) {
99
            $elements = explode(',', $header);
100
            $host = $elements[count($elements) - 1];
101
        } elseif (!$host = $this->getHeader('HOST')) {
102
            if (!$host = $this->getServerVar('SERVER_NAME')) {
103
                $host = $this->getServerVar('SERVER_ADDR');
104
            }
105
        }
106
107
        // trim and remove port number from host
108
        // host is lowercase as per RFC 952/2181
109
        $host = strtolower(preg_replace('/:\d+$/', '', trim($host)));
110
111
        // Port number
112
        $scheme = $this->getHttpScheme();
113
        $port = $this->getCurrentPort();
114
        $appendPort = ':' . $port;
115
116
        // Don't append port number if a normal port.
117
        if (($scheme == 'http' && $port == '80') || ($scheme == 'https' && $port == '443')) {
118
            $appendPort = '';
119
        }
120
121
        return $host . $appendPort;
122
    }
123
124
    protected function getCurrentPort()
125
    {
126
        // Check for proxy first
127
        $port = $this->getHeader('X_FORWARDED_PORT');
128
        if ($port) {
129
            return (string)$port;
130
        }
131
132
        $protocol = (string)$this->getHeader('X_FORWARDED_PROTO');
133
        if ($protocol === 'https') {
134
            return '443';
135
        }
136
137
        return (string)$this->getServerVar('SERVER_PORT');
138
    }
139
140
    /**
141
     * Returns the a value from the $_SERVER super global.
142
     *
143
     * @param string $key
144
     *
145
     * @return string
146
     */
147
    protected function getServerVar($key)
148
    {
149
        return isset($_SERVER[$key]) ? $_SERVER[$key] : '';
150
    }
151
152
    /**
153
     * Gets a value from the HTTP request headers.
154
     *
155
     * @param string $key
156
     *
157
     * @return string
158
     */
159
    protected function getHeader($key)
160
    {
161
        return $this->getServerVar('HTTP_' . $key);
162
    }
163
164
    /**
165
     * Checks if the value in X_FORWARDED_HOST is a valid hostname
166
     * Could prevent unintended redirections
167
     *
168
     * @param string $header
169
     *
170
     * @return boolean
171
     */
172
    protected function isValidForwardedHost($header)
173
    {
174
        $elements = explode(',', $header);
175
        $host = $elements[count($elements) - 1];
176
        
177
        return preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $host) //valid chars check
178
            && 0 < strlen($host) && strlen($host) < 254 //overall length check
179
            && preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $host); //length of each label
180
    }
181
}
182