1 | <?php |
||
2 | |||
3 | /** |
||
4 | * Represents a web server. It provides methods for testing that the server |
||
5 | * behaves as expected (e.g. requires authentication). |
||
6 | * |
||
7 | * @author Sam Stenvall <[email protected]> |
||
8 | * @copyright Copyright © Sam Stenvall 2014- |
||
9 | * @license https://www.gnu.org/licenses/gpl.html The GNU General Public License v3.0 |
||
10 | */ |
||
11 | class WebServer |
||
12 | { |
||
13 | |||
14 | /** |
||
15 | * @var string the server hostname |
||
16 | */ |
||
17 | private $_hostname; |
||
18 | |||
19 | /** |
||
20 | * @var int the server port |
||
21 | */ |
||
22 | private $_port; |
||
23 | |||
24 | /** |
||
25 | * @var \Zend\Http\Client() the HTTP client used |
||
26 | */ |
||
27 | private $_httpClient; |
||
28 | |||
29 | /** |
||
30 | * Class constructor. It instantiates the HTTP client. |
||
31 | * @param string $hostname the server hostname |
||
32 | * @param int $port the server port |
||
33 | */ |
||
34 | public function __construct($hostname, $port) |
||
35 | { |
||
36 | $this->_hostname = $hostname; |
||
37 | $this->_port = $port; |
||
38 | $this->_httpClient = new Zend\Http\Client(); |
||
39 | } |
||
40 | |||
41 | /** |
||
42 | * @return boolean whether the web server requires authentication |
||
43 | */ |
||
44 | public function requiresAuthentication() |
||
45 | { |
||
46 | return $this->getResponseStatusCode($this->createBasicRequest()) === 401; |
||
47 | } |
||
48 | |||
49 | /** |
||
50 | * @return string the authentication realm of the server, or an empty string |
||
51 | * if no authentication is requested |
||
52 | */ |
||
53 | public function getAuthenticationRealm() |
||
54 | { |
||
55 | $response = $this->silentDispatch($this->createBasicRequest()); |
||
56 | |||
57 | if ($response) |
||
0 ignored issues
–
show
introduced
by
![]() |
|||
58 | foreach ($response->getHeaders() as $header) |
||
59 | if ($header instanceof Zend\Http\Header\WWWAuthenticate) |
||
60 | return $this->parseRealm($header); |
||
61 | |||
62 | return ''; |
||
63 | } |
||
64 | |||
65 | /** |
||
66 | * Checks that the specified credentials are valid |
||
67 | * @param string $username the username |
||
68 | * @param string $password the password |
||
69 | * @return boolean whether authentication succeeded |
||
70 | */ |
||
71 | public function checkCredentials($username, $password) |
||
72 | { |
||
73 | $request = $this->createBasicRequest(); |
||
74 | $this->_httpClient->setAuth($username, $password); |
||
75 | |||
76 | $statusCode = $this->getResponseStatusCode($request); |
||
77 | $this->_httpClient->clearAuth(); |
||
78 | |||
79 | return $statusCode !== 401; |
||
80 | } |
||
81 | |||
82 | /** |
||
83 | * @return string the hostname:port combination |
||
84 | */ |
||
85 | public function getHostInfo() |
||
86 | { |
||
87 | return Backend::normalizeAddress($this->_hostname).':'.$this->_port; |
||
88 | } |
||
89 | |||
90 | /** |
||
91 | * @return \Zend\Http\Request a base request object |
||
92 | */ |
||
93 | private function createBasicRequest() |
||
94 | { |
||
95 | $request = new \Zend\Http\Request(); |
||
96 | return $request->setUri('http://'.$this->getHostInfo().'/'); |
||
97 | } |
||
98 | |||
99 | /** |
||
100 | * Dispatches the specified request and returns the response. If any |
||
101 | * exceptions occur, null is returned |
||
102 | * @param \Zend\Http\Request $request the request |
||
103 | * @return \Zend\Http\Response the response, or null if the request failed |
||
104 | */ |
||
105 | private function silentDispatch($request) |
||
106 | { |
||
107 | try |
||
108 | { |
||
109 | return $this->_httpClient->dispatch($request); |
||
110 | } |
||
111 | catch (Exception $e) |
||
112 | { |
||
113 | unset($e); |
||
114 | return null; |
||
115 | } |
||
116 | } |
||
117 | |||
118 | /** |
||
119 | * Dispatches the specified request and returns the response status code |
||
120 | * @param \Zend\Http\Request $request the request |
||
121 | * @return int|false the response code, or false if the request failed |
||
122 | */ |
||
123 | private function getResponseStatusCode($request) |
||
124 | { |
||
125 | $response = $this->silentDispatch($request); |
||
126 | |||
127 | if ($response) |
||
0 ignored issues
–
show
|
|||
128 | return $response->getStatusCode(); |
||
129 | else |
||
130 | return false; |
||
131 | } |
||
132 | |||
133 | /** |
||
134 | * Parses the actual realm from the value of the WWW-Authenticate header |
||
135 | * @param \Zend\Http\Header\WWWAuthenticate $authenticateHeader the header |
||
136 | * @return string the authentication realm (an empty string if it was |
||
137 | * malformed) |
||
138 | */ |
||
139 | private function parseRealm($authenticateHeader) |
||
140 | { |
||
141 | $parts = explode('=', $authenticateHeader->value); |
||
142 | |||
143 | // Sanity check, otherwise the rest of the method won't work |
||
144 | if (count($parts) !== 2) |
||
145 | return ''; |
||
146 | |||
147 | $realm = $parts[1]; |
||
148 | |||
149 | // Remove eventual citation marks |
||
150 | if (strpos($realm, '"') !== false) |
||
151 | return substr($realm, 1, strlen($realm) - 2); |
||
152 | else |
||
153 | return $realm; |
||
154 | } |
||
155 | |||
156 | } |
||
157 |