From ea638658455c3a6a5d3246a2f3ed47a2890c6a53 Mon Sep 17 00:00:00 2001 From: Greg Kennedy Date: Sat, 25 May 2019 22:30:22 -0500 Subject: [PATCH 1/2] Do not decode bytes unless frame is_text. Correctly handle error code and message in close frames. --- lib/Protocol/WebSocket/Client.pm | 24 +++++++++++++++++------- lib/Protocol/WebSocket/Frame.pm | 6 +++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/Protocol/WebSocket/Client.pm b/lib/Protocol/WebSocket/Client.pm index 10732b2..210d849 100644 --- a/lib/Protocol/WebSocket/Client.pm +++ b/lib/Protocol/WebSocket/Client.pm @@ -8,6 +8,8 @@ use Protocol::WebSocket::URL; use Protocol::WebSocket::Handshake::Client; use Protocol::WebSocket::Frame; +use Encode (); + sub new { my $class = shift; $class = ref $class if ref $class; @@ -103,9 +105,15 @@ sub read { while (defined (my $bytes = $frame_buffer->next)) { if ($frame_buffer->is_close) { # Remote WebSocket close (TCP socket may stay open for a bit) - $self->disconnect if ($self->is_ready); - # TODO: see message in disconnect() about error code / reason - $self->{on_eof}->($self) if $self->{on_eof}; + # Decode the error code and message, if it exists + my $code = length $bytes > 1 ? unpack('n', substr($bytes, 0, 2)) : undef; + my $message = length $bytes > 3 ? Encode::decode('UTF-8', substr($bytes, 2)) : undef; + + # Spec says to send our own close frame (and echo the errno.) + $self->disconnect($code) if ($self->is_ready); + + # Call user callback + $self->{on_eof}->($self, $code, $message) if $self->{on_eof}; } elsif ($frame_buffer->is_pong) { # Server responded to our ping. $self->{on_pong}->($self, $bytes) if $self->{on_pong}; @@ -162,11 +170,13 @@ sub connect { # also sets state to -1 when called sub disconnect { my $self = shift; + my ($code, $message) = @_; + + my $buffer; + if (defined $code) { $buffer = pack 'n', $code } + if (defined $message) { $buffer .= Encode::encode('UTF-8', $message) } - # TODO: Spec states 'close' messages may contain a uint16 error code, and a utf-8 reason. - # Clients are supposed to echo back the error code when receiving close from server. - # For now, we just send an empty message. - $self->write( $self->_build_frame(type => 'close', masked => 1) ); + $self->write( $self->_build_frame(type => 'close', masked => 1, buffer => $buffer) ); $self->{state} = -1; diff --git a/lib/Protocol/WebSocket/Frame.pm b/lib/Protocol/WebSocket/Frame.pm index 9af60ae..33ad30f 100644 --- a/lib/Protocol/WebSocket/Frame.pm +++ b/lib/Protocol/WebSocket/Frame.pm @@ -84,7 +84,11 @@ sub next { my $bytes = $self->next_bytes; return unless defined $bytes; - return Encode::decode('UTF-8', $bytes); + if ($self->is_text) { + return Encode::decode('UTF-8', $bytes); + } else { + return $bytes; + } } sub fin { From 3bf65a13a1d037030fc441a69582a61644df0e99 Mon Sep 17 00:00:00 2001 From: Greg Kennedy Date: Mon, 27 May 2019 03:55:22 -0500 Subject: [PATCH 2/2] Add a fix for draft-ietf-hybi-00, which ONLY supports UTF-8 frames (no binary) --- lib/Protocol/WebSocket/Frame.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Protocol/WebSocket/Frame.pm b/lib/Protocol/WebSocket/Frame.pm index 33ad30f..7643caf 100644 --- a/lib/Protocol/WebSocket/Frame.pm +++ b/lib/Protocol/WebSocket/Frame.pm @@ -84,7 +84,7 @@ sub next { my $bytes = $self->next_bytes; return unless defined $bytes; - if ($self->is_text) { + if ($self->{version} eq 'draft-ietf-hybi-00' || $self->is_text) { return Encode::decode('UTF-8', $bytes); } else { return $bytes;