message recv bugfix
This commit is contained in:
parent
8809203953
commit
bc4663e3a7
|
@ -36,9 +36,10 @@ class fuckwebsockets{
|
||||||
"max_tcp_sndtime" => 30, // in seconds
|
"max_tcp_sndtime" => 30, // in seconds
|
||||||
"tcp_write_size" => 4096, // in bytes
|
"tcp_write_size" => 4096, // in bytes
|
||||||
"tcp_keepalive" => true,
|
"tcp_keepalive" => true,
|
||||||
"ws_ping_interval" => 0, // 0 for disabled, int for ping interval
|
"ws_ping_interval" => 0, // int for ping interval, 0 for disabled, in seconds
|
||||||
"max_ws_rcv_size" => 4096, // in bytes
|
"max_ws_rcv_size" => 4096, // in bytes
|
||||||
"http_headers" => [] // Eg ["Host" => "localhost"]
|
"http_headers" => [], // Eg ["Host" => "localhost"]
|
||||||
|
"debug_log" => true
|
||||||
],
|
],
|
||||||
$options
|
$options
|
||||||
);
|
);
|
||||||
|
@ -348,6 +349,7 @@ class fuckwebsockets{
|
||||||
|
|
||||||
// handle incoming messages
|
// handle incoming messages
|
||||||
$buffer = null;
|
$buffer = null;
|
||||||
|
$bufferlength = 0;
|
||||||
|
|
||||||
// remove socket timeout
|
// remove socket timeout
|
||||||
socket_set_option(
|
socket_set_option(
|
||||||
|
@ -365,57 +367,47 @@ class fuckwebsockets{
|
||||||
// decode message
|
// decode message
|
||||||
$this->socket_recv($this->client, $header, 2, MSG_WAITALL);
|
$this->socket_recv($this->client, $header, 2, MSG_WAITALL);
|
||||||
|
|
||||||
|
$finalmessage = (ord($header[0]) & 0x80) === 128;
|
||||||
$opcode = ord($header[0]) & 0x0F;
|
$opcode = ord($header[0]) & 0x0F;
|
||||||
$finalmessage = ord($header[0]) & 0x80;
|
$masked = (ord($header[1]) & 0x80) === 128;
|
||||||
$masked = ord($header[1]) & 0x80;
|
|
||||||
$messagelength = ord($header[1]) & 0x7F;
|
$messagelength = ord($header[1]) & 0x7F;
|
||||||
|
|
||||||
// disconnect if frame isn't masked
|
switch($messagelength){
|
||||||
if(!$masked){
|
|
||||||
|
|
||||||
$this->ws_disconnect($this->client, self::close_bad_data, "Received unmasked frame");
|
case 126:
|
||||||
}
|
$this->socket_recv($this->client, $messagelength, 2, MSG_WAITALL);
|
||||||
|
$messagelength = unpack("n", $messagelength)[1];
|
||||||
|
break;
|
||||||
|
|
||||||
// get extension length
|
case 127:
|
||||||
$extensionlength = 0;
|
$this->socket_recv($this->client, $messagelength, 8, MSG_WAITALL);
|
||||||
|
$messagelength = unpack("J", $messagelength)[1];
|
||||||
if($messagelength >= 0x7E){
|
break;
|
||||||
|
|
||||||
$extensionlength = 2;
|
|
||||||
|
|
||||||
if($messagelength == 0x7F){
|
|
||||||
|
|
||||||
$extensionlength = 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
$messagelength = 0;
|
|
||||||
|
|
||||||
$this->socket_recv($this->client, $header, $extensionlength, MSG_WAITALL);
|
|
||||||
|
|
||||||
for($i=0; $i<$extensionlength; $i++){
|
|
||||||
|
|
||||||
$messagelength += ord($header[$i]) << ($extensionlength - $i - 1) * 8;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if($messagelength > $this->options["max_ws_rcv_size"]){
|
if($messagelength > $this->options["max_ws_rcv_size"]){
|
||||||
|
|
||||||
$this->ws_disconnect($this->client, self::close_too_big, "Frame exceeded " . $this->options["max_ws_rcv_size"] . "b limit");
|
$this->ws_disconnect($this->client, self::close_too_big, "Frame exceeds " . $this->options["max_ws_rcv_size"] . "b limit");
|
||||||
}
|
}
|
||||||
|
|
||||||
// get mask
|
if($masked){
|
||||||
$this->socket_recv($this->client, $mask, 4, MSG_WAITALL);
|
|
||||||
|
|
||||||
// extract data from frame
|
// get mask and message payload
|
||||||
|
$this->socket_recv($this->client, $frame_data, $messagelength + 4, MSG_WAITALL);
|
||||||
|
|
||||||
|
for($i=4; $i<$messagelength; $i++){
|
||||||
|
|
||||||
|
$frame_data[$i] = $frame_data[$i] ^ $frame_data[$i % 4];
|
||||||
|
}
|
||||||
|
|
||||||
|
$frame_data = substr($frame_data, 4);
|
||||||
|
}else{
|
||||||
|
|
||||||
|
// get message payload
|
||||||
$this->socket_recv($this->client, $frame_data, $messagelength, MSG_WAITALL);
|
$this->socket_recv($this->client, $frame_data, $messagelength, MSG_WAITALL);
|
||||||
|
|
||||||
// unmask data
|
|
||||||
for($i=0; $i<strlen($frame_data); $i++){
|
|
||||||
|
|
||||||
$frame_data[$i] = $frame_data[$i] ^ $mask[$i % 4];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle data
|
// call user functions
|
||||||
switch($opcode){
|
switch($opcode){
|
||||||
|
|
||||||
case self::op_text:
|
case self::op_text:
|
||||||
|
@ -423,14 +415,8 @@ class fuckwebsockets{
|
||||||
if($finalmessage === false){
|
if($finalmessage === false){
|
||||||
// partial message, come back later
|
// partial message, come back later
|
||||||
|
|
||||||
if(strlen($buffer) > 0){
|
|
||||||
|
|
||||||
// message is partial but client is
|
|
||||||
// sending more op_text frames? makes no sense!
|
|
||||||
$this->ws_disconnect($this->client, self::close_protocol, "Did not send continue frame for partial message");
|
|
||||||
}
|
|
||||||
|
|
||||||
$buffer = $frame_data;
|
$buffer = $frame_data;
|
||||||
|
$bufferlength = $messagelength;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,27 +427,30 @@ class fuckwebsockets{
|
||||||
|
|
||||||
case self::op_continue:
|
case self::op_continue:
|
||||||
$buffer .= $frame_data;
|
$buffer .= $frame_data;
|
||||||
|
$bufferlength += $messagelength;
|
||||||
|
|
||||||
if(strlen($buffer) > $this->options["max_ws_rcv_size"]){
|
if($bufferlength > $this->options["max_ws_rcv_size"]){
|
||||||
|
|
||||||
$this->ws_disconnect($this->client, self::close_too_big, "Message exceeded " . $this->options["max_ws_rcv_size"] . " bytes limit");
|
$this->ws_disconnect($this->client, self::close_too_big, "Message buffer exceeds " . $this->options["max_ws_rcv_size"] . "b limit");
|
||||||
}
|
}
|
||||||
|
|
||||||
if($finalmessage === true){
|
if($finalmessage){
|
||||||
|
|
||||||
// we received the entire thing, trigger event
|
// we received the entire thing, trigger event
|
||||||
$this->log($this->client, "Text frame (buffered): {$buffer}");
|
$this->log($this->client, "Text frame (buffer): {$buffer}");
|
||||||
$buffer = null;
|
$buffer = null;
|
||||||
|
$bufferlength = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
case self::op_ping:
|
case self::op_ping:
|
||||||
if(strlen($frame_data) > 128){
|
if($messagelength > 126){
|
||||||
|
|
||||||
$this->ws_disconnect($this->client, self::close_too_big, "Ping frame exceeded 128 bytes");
|
$this->ws_disconnect($this->client, self::close_too_big, "Ping frame exceeded 128 bytes");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->log($this->client, "Received ping: " . $frame_data);
|
||||||
$this->ws_send($this->client, self::op_pong, $frame_data);
|
$this->ws_send($this->client, self::op_pong, $frame_data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -499,12 +488,18 @@ class fuckwebsockets{
|
||||||
exit(0);
|
exit(0);
|
||||||
}elseif($state === false){
|
}elseif($state === false){
|
||||||
|
|
||||||
|
$error = socket_last_error();
|
||||||
|
if($error === 0){
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if($client->request->handshake === false){
|
if($client->request->handshake === false){
|
||||||
|
|
||||||
$this->http_disconnect($client, 408, "Read timeout");
|
$this->http_disconnect($client, 408, socket_strerror($error));
|
||||||
}else{
|
}else{
|
||||||
|
|
||||||
$this->ws_disconnect($client, self::close_no_status, "Read timeout");
|
$this->ws_disconnect($client, self::close_no_status, socket_strerror($error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -590,8 +585,11 @@ class fuckwebsockets{
|
||||||
|
|
||||||
public function log(object $client, string $message){
|
public function log(object $client, string $message){
|
||||||
|
|
||||||
|
if($this->options["debug_log"]){
|
||||||
|
|
||||||
echo $client->ip . ":" . $client->port . "] " . $message . "\n";
|
echo $client->ip . ":" . $client->port . "] " . $message . "\n";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function http_disconnect(object $client, int $errcode, string $message){
|
public function http_disconnect(object $client, int $errcode, string $message){
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue