Hoa central
Client.php
Go to the documentation of this file.
1 <?php
2 
37 namespace Hoa\Websocket;
38 
39 use Hoa\Core;
40 use Hoa\Http;
41 use Hoa\Socket;
42 
51 class Client extends Connection
52 {
58  protected $_endPoint = null;
59 
65  protected $_host = null;
66 
72  protected $_response = null;
73 
74 
75 
85  public function __construct(
86  Socket\Client $client,
87  $endPoint = '/',
88  Http\Response $response = null
89  ) {
90  parent::__construct($client);
91  $this->setEndPoint($endPoint);
92 
93  if (null === $response) {
94  $response = new Http\Response(false);
95  }
96 
97  $this->setResponse($response);
98 
99  return;
100  }
101 
107  public function connect()
108  {
109  return $this->doHandshake();
110  }
111 
117  public function run()
118  {
119  $this->connect();
120 
121  return parent::run();
122  }
123 
129  public function receive()
130  {
131  $connection = $this->getConnection();
132  $node = $connection->getCurrentNode();
133 
134  do {
135  $this->_run($node);
136  } while (
137  false === $connection->isDisconnected() &&
138  true !== $node->isMessageComplete()
139  );
140  }
141 
149  protected function doHandshake()
150  {
151  static $_tail = ['A', 'Q', 'g', 'w'];
152 
153  $connection = $this->getConnection();
154  $connection->connect();
155  $connection->setStreamBlocking(true);
156 
157  $key =
158  substr(base64_encode(Core::uuid()), 0, 21) .
159  $_tail[mt_rand(0, 3)] . '==';
160 
161  $expected = base64_encode(sha1($key . Protocol\Rfc6455::GUID, true));
162 
163  if (null === $host = $this->getHost()) {
164  throw new Exception(
165  'Host name is null. Please, use the %s::setHost() method.',
166  0,
167  __CLASS__
168  );
169  }
170 
171  $connection->writeAll(
172  $request =
173  'GET ' . $this->getEndPoint() . ' HTTP/1.1' . CRLF .
174  'Host: ' . $host . CRLF .
175  'User-Agent: Hoa' . CRLF .
176  'Upgrade: WebSocket' . CRLF .
177  'Connection: Upgrade' . CRLF .
178  'Pragma: no-cache' . CRLF .
179  'Cache-Control: no-cache' . CRLF .
180  'Sec-WebSocket-Key: ' . $key . CRLF .
181  'Sec-WebSocket-Version: 13' . CRLF . CRLF
182  );
183 
184  $buffer = $connection->read(2048);
185  $response = $this->getResponse();
186  $response->parse($buffer);
187 
188  if ($response::STATUS_SWITCHING_PROTOCOLS !== $response['status'] ||
189  'websocket' !== strtolower($response['upgrade']) ||
190  'upgrade' !== strtolower($response['connection']) ||
191  $expected !== $response['sec-websocket-accept']) {
192  throw new Exception\BadProtocol(
193  'Handshake has failed, the server did not return a valid ' .
194  'response.' . "\n\n" .
195  'Client:' . "\n" . ' %s' . "\n" .
196  'Server:' . "\n" . ' %s',
197  0,
198  [
199  str_replace("\n", "\n" . ' ', $request),
200  str_replace("\n", "\n" . ' ', $buffer)
201  ]
202  );
203  }
204 
205  $currentNode = $connection->getCurrentNode();
206  $currentNode->setHandshake(SUCCEED);
207  $currentNode->setProtocolImplementation(new Protocol\Rfc6455($connection));
208 
209  $this->_on->fire(
210  'open',
211  new Core\Event\Bucket()
212  );
213 
214  return;
215  }
216 
225  public function close($code = self::CLOSE_NORMAL, $reason = null)
226  {
227  $connection = $this->getConnection();
228  $protocol = $connection->getCurrentNode()->getProtocolImplementation();
229 
230  if (null !== $protocol) {
231  $protocol->close($code, $reason, true);
232  }
233 
234  $connection->mute();
235  $connection->setStreamTimeout(0, 2 * 15000); // 2 * MLS (on FreeBSD)
236  $connection->read(1);
237 
238  return $connection->disconnect();
239  }
240 
247  protected function setEndPoint($endPoint)
248  {
249  $old = $this->_endPoint;
250  $this->_endPoint = $endPoint;
251 
252  return $old;
253  }
254 
260  public function getEndPoint()
261  {
262  return $this->_endPoint;
263  }
264 
271  public function setResponse(Http\Response $response)
272  {
273  $old = $this->_response;
274  $this->_response = $response;
275 
276  return $old;
277  }
278 
284  public function getResponse()
285  {
286  return $this->_response;
287  }
288 
295  public function setHost($host)
296  {
297  $old = $this->_host;
298  $this->_host = $host;
299 
300  return $old;
301  }
302 
308  public function getHost()
309  {
310  return
311  null !== $this->_host
312  ? $this->_host
313  : (isset($_SERVER['HTTP_HOST'])
314  ? $_SERVER['HTTP_HOST']
315  : null);
316  }
317 }
close($code=self::CLOSE_NORMAL, $reason=null)
Definition: Client.php:225
setEndPoint($endPoint)
Definition: Client.php:247
_run(Socket\Node $node)
Definition: Connection.php:236
static uuid()
Definition: Core.php:444
setResponse(Http\Response $response)
Definition: Client.php:271
__construct(Socket\Client $client, $endPoint= '/', Http\Response $response=null)
Definition: Client.php:85