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 "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){