This repository has been archived by the owner on Jul 9, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathServer.php
130 lines (106 loc) · 3.29 KB
/
Server.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
<?php
/*
* This file is part of the overtrue/websocket-client.
*
* (c) overtrue <anzhengchao@gmail.com>
*
* This source file is subject to the MIT license that is bundled.
*/
namespace Overtrue\WebSocket;
use Overtrue\WebSocket\Exceptions\BadRequestException;
use Overtrue\WebSocket\Exceptions\ConnectionException;
/**
* Class Server.
*/
class Server extends WebSocket
{
/**
* @var resource
*/
protected $listening;
/**
* @var array
*/
protected $request;
/**
* Server constructor.
*
* @param array $options
*
* @throws \Overtrue\WebSocket\Exceptions\ConnectionException
*/
public function __construct(array $options = [])
{
$this->options = \array_merge($this->options, $options);
$port = $this->options['port'] ?? 8000;
do {
$this->listening = @stream_socket_server("tcp://0.0.0.0:$port", $errno, $message);
} while (false === $this->listening && $this->port++ < 10000);
if (!$this->listening) {
throw new ConnectionException('No valid port to listen.');
}
}
/**
* @param string $header
*
* @return string|null
*/
public function getHeader(string $header)
{
foreach ($this->request as $row) {
if (false !== stripos($row, $header)) {
list($name, $value) = explode(':', $row);
return trim($value);
}
}
return null;
}
/**
* @return bool|resource
*
* @throws \Overtrue\WebSocket\Exceptions\BadRequestException
*/
public function accept()
{
if (empty($this->options['timeout'])) {
$this->socket = stream_socket_accept($this->listening);
} else {
$this->socket = stream_socket_accept($this->listening, $this->options['timeout']);
stream_set_timeout($this->socket, $this->options['timeout']);
}
$this->performHandshake();
stream_set_blocking($this->socket, false);
return $this->socket;
}
/**
* @throws \Overtrue\WebSocket\Exceptions\BadRequestException
* @throws \Overtrue\WebSocket\Exceptions\ConnectionException
*/
protected function performHandshake()
{
$body = '';
do {
$buffer = stream_get_line($this->socket, 1024, "\r\n");
$body .= $buffer."\n";
$metadata = stream_get_meta_data($this->socket);
} while (!feof($this->socket) && $metadata['unread_bytes'] > 0);
if (!preg_match('/GET (.*) HTTP\//mUi', $body, $matches)) {
throw new BadRequestException('Invalid Request headers.');
}
$this->request = explode("\n", $body);
if (!preg_match('#Sec-WebSocket-Key:\s(.*)$#mUi', $body, $matches)) {
throw new BadRequestException('No key found in upgrade request');
}
$key = trim($matches[1]);
// @todo Validate key length and base 64...
$responseKey = base64_encode(pack('H*', sha1($key.self::KEY_SALT)));
$headers = [
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
"Sec-WebSocket-Accept: $responseKey",
"\r\n",
];
$this->write(\join("\r\n", $headers));
}
}