123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- <?php
- /**
- * Copyright (C) 2014-2020 Textalk/Abicart and contributors.
- *
- * This file is part of Websocket PHP and is free software under the ISC License.
- * License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING
- */
- namespace WebSocket;
- class Server extends Base
- {
- // Default options
- protected static $default_options = [
- 'timeout' => null,
- 'fragment_size' => 4096,
- 'port' => 8000,
- 'logger' => null,
- ];
- protected $addr;
- protected $port;
- protected $listening;
- protected $request;
- protected $request_path;
- /**
- * @param array $options
- * Associative array containing:
- * - timeout: Set the socket timeout in seconds.
- * - fragment_size: Set framgemnt size. Default: 4096
- * - port: Chose port for listening. Default 8000.
- */
- public function __construct(array $options = array())
- {
- $this->options = array_merge(self::$default_options, $options);
- $this->port = $this->options['port'];
- $this->setLogger($this->options['logger']);
- do {
- $this->listening = @stream_socket_server("tcp://0.0.0.0:$this->port", $errno, $errstr);
- } while ($this->listening === false && $this->port++ < 10000);
- if (!$this->listening) {
- $error = "Could not open listening socket: {$errstr} ({$errno})";
- $this->logger->error($error);
- throw new ConnectionException($error, $errno);
- }
- $this->logger->info("Server listening to port {$this->port}");
- }
- public function __destruct()
- {
- if ($this->isConnected()) {
- fclose($this->socket);
- }
- $this->socket = null;
- }
- public function getPort()
- {
- return $this->port;
- }
- public function getPath()
- {
- return $this->request_path;
- }
- public function getRequest()
- {
- return $this->request;
- }
- public function getHeader($header)
- {
- foreach ($this->request as $row) {
- if (stripos($row, $header) !== false) {
- list($headername, $headervalue) = explode(":", $row);
- return trim($headervalue);
- }
- }
- return null;
- }
- public function accept()
- {
- $this->socket = null;
- return (bool)$this->listening;
- }
- protected function connect()
- {
- if (empty($this->options['timeout'])) {
- $this->socket = @stream_socket_accept($this->listening);
- if (!$this->socket) {
- $error = 'Server failed to connect.';
- $this->logger->error($error);
- throw new ConnectionException($error);
- }
- } else {
- $this->socket = @stream_socket_accept($this->listening, $this->options['timeout']);
- if (!$this->socket) {
- $error = 'Server failed to connect.';
- $this->logger->error($error);
- throw new ConnectionException($error);
- }
- stream_set_timeout($this->socket, $this->options['timeout']);
- }
- $this->performHandshake();
- $this->logger->info("Server connected to port {$this->port}");
- }
- protected function performHandshake()
- {
- $request = '';
- do {
- $buffer = stream_get_line($this->socket, 1024, "\r\n");
- $request .= $buffer . "\n";
- $metadata = stream_get_meta_data($this->socket);
- } while (!feof($this->socket) && $metadata['unread_bytes'] > 0);
- if (!preg_match('/GET (.*) HTTP\//mUi', $request, $matches)) {
- $error = "No GET in request: {$request}";
- $this->logger->error($error);
- throw new ConnectionException($error);
- }
- $get_uri = trim($matches[1]);
- $uri_parts = parse_url($get_uri);
- $this->request = explode("\n", $request);
- $this->request_path = $uri_parts['path'];
- /// @todo Get query and fragment as well.
- if (!preg_match('#Sec-WebSocket-Key:\s(.*)$#mUi', $request, $matches)) {
- $error = "Client had no Key in upgrade request: {$request}";
- $this->logger->error($error);
- throw new ConnectionException($error);
- }
- $key = trim($matches[1]);
- /// @todo Validate key length and base 64...
- $response_key = base64_encode(pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
- $header = "HTTP/1.1 101 Switching Protocols\r\n"
- . "Upgrade: websocket\r\n"
- . "Connection: Upgrade\r\n"
- . "Sec-WebSocket-Accept: $response_key\r\n"
- . "\r\n";
- $this->write($header);
- }
- }
|