message recv bugfix

This commit is contained in:
lolcat 2024-01-20 13:49:23 -05:00
parent 8809203953
commit bc4663e3a7
1 changed files with 52 additions and 54 deletions

View File

@ -36,9 +36,10 @@ class fuckwebsockets{
"max_tcp_sndtime" => 30, // in seconds
"tcp_write_size" => 4096, // in bytes
"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
"http_headers" => [] // Eg ["Host" => "localhost"]
"http_headers" => [], // Eg ["Host" => "localhost"]
"debug_log" => true
],
$options
);
@ -348,6 +349,7 @@ class fuckwebsockets{
// handle incoming messages
$buffer = null;
$bufferlength = 0;
// remove socket timeout
socket_set_option(
@ -365,57 +367,47 @@ class fuckwebsockets{
// decode message
$this->socket_recv($this->client, $header, 2, MSG_WAITALL);
$finalmessage = (ord($header[0]) & 0x80) === 128;
$opcode = ord($header[0]) & 0x0F;
$finalmessage = ord($header[0]) & 0x80;
$masked = ord($header[1]) & 0x80;
$masked = (ord($header[1]) & 0x80) === 128;
$messagelength = ord($header[1]) & 0x7F;
// disconnect if frame isn't masked
if(!$masked){
switch($messagelength){
$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
$extensionlength = 0;
if($messagelength >= 0x7E){
$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;
}
case 127:
$this->socket_recv($this->client, $messagelength, 8, MSG_WAITALL);
$messagelength = unpack("J", $messagelength)[1];
break;
}
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
$this->socket_recv($this->client, $mask, 4, MSG_WAITALL);
if($masked){
// 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);
// 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){
case self::op_text:
@ -423,14 +415,8 @@ class fuckwebsockets{
if($finalmessage === false){
// 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;
$bufferlength = $messagelength;
break;
}
@ -441,27 +427,30 @@ class fuckwebsockets{
case self::op_continue:
$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
$this->log($this->client, "Text frame (buffered): {$buffer}");
$this->log($this->client, "Text frame (buffer): {$buffer}");
$buffer = null;
$bufferlength = 0;
}
break;
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->log($this->client, "Received ping: " . $frame_data);
$this->ws_send($this->client, self::op_pong, $frame_data);
break;
@ -499,12 +488,18 @@ class fuckwebsockets{
exit(0);
}elseif($state === false){
$error = socket_last_error();
if($error === 0){
return;
}
if($client->request->handshake === false){
$this->http_disconnect($client, 408, "Read timeout");
$this->http_disconnect($client, 408, socket_strerror($error));
}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){
if($this->options["debug_log"]){
echo $client->ip . ":" . $client->port . "] " . $message . "\n";
}
}
public function http_disconnect(object $client, int $errcode, string $message){