Skip to content

Commit

Permalink
allow finish and send methods to work like most users would expect
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Apr 8, 2015
1 parent 3bc22f3 commit 9411298
Show file tree
Hide file tree
Showing 8 changed files with 47 additions and 56 deletions.
6 changes: 5 additions & 1 deletion Changes
@@ -1,5 +1,9 @@

6.08 2015-04-08
6.08 2015-04-09
- Added is_established and server_open methods to
Mojo::Transaction::WebSocket.
- Improved finish and send methods in Mojolicious::Controller to
automatically establish the WebSocket connection if necessary.

6.07 2015-04-07
- Fixed Windows bug in "daemon.t".
Expand Down
1 change: 1 addition & 0 deletions lib/Mojo/Server/Daemon.pm
Expand Up @@ -123,6 +123,7 @@ sub _finish {
if ($ws->res->code == 101) {
weaken $self;
$ws->on(resume => sub { $self->_write($id) });
$ws->server_open;
}

# Failed upgrade
Expand Down
16 changes: 16 additions & 0 deletions lib/Mojo/Transaction/WebSocket.pm
Expand Up @@ -139,6 +139,8 @@ sub finish {
return $self;
}

sub is_established { !!shift->{open} }

sub is_websocket {1}

sub kept_alive { shift->handshake->kept_alive }
Expand Down Expand Up @@ -247,6 +249,8 @@ sub server_handshake {
_challenge($req_headers->sec_websocket_key));
}

sub server_open { shift->{open}++ }

sub server_read {
my ($self, $chunk) = @_;

Expand Down Expand Up @@ -576,6 +580,12 @@ Connection identifier.
Close WebSocket connection gracefully.
=head2 is_established
my $bool = $ws->is_established;
Check if WebSocket connection has been established yet.
=head2 is_websocket
my $true = $ws->is_websocket;
Expand Down Expand Up @@ -682,6 +692,12 @@ Transaction closed server-side, used to implement web servers.
Perform WebSocket handshake server-side, used to implement web servers.
=head2 server_open
$ws->server_open;
WebSocket connection established server-side, used to implement web servers.
=head2 server_read
$ws->server_read($data);
Expand Down
38 changes: 13 additions & 25 deletions lib/Mojolicious/Controller.pm
Expand Up @@ -107,7 +107,8 @@ sub finish {

# WebSocket
my $tx = $self->tx;
$tx->finish(@_) and return $self if $tx->is_websocket;
$tx->finish(@_) and return $tx->is_established ? $self : $self->rendered(101)
if $tx->is_websocket;

# Chunked stream
return @_ ? $self->write_chunk(@_)->write_chunk('') : $self->write_chunk('')
Expand Down Expand Up @@ -137,7 +138,7 @@ sub helpers { $_[0]->app->renderer->get_helper('')->($_[0]) }
sub on {
my ($self, $name, $cb) = @_;
my $tx = $self->tx;
$self->rendered(101) if $tx->is_websocket;
$self->rendered(101) if $tx->is_websocket && !$tx->is_established;
return $tx->on($name => sub { shift; $self->$cb(@_) });
}

Expand Down Expand Up @@ -251,7 +252,7 @@ sub send {
Carp::croak 'No WebSocket connection to send message to'
unless $tx->is_websocket;
$tx->send($msg, $cb ? sub { shift; $self->$cb(@_) } : ());
return $self;
return $tx->is_established ? $self : $self->rendered(101);
}

sub session {
Expand Down Expand Up @@ -507,10 +508,9 @@ sharing the same name as an array reference.
$c = $c->finish(1003 => 'Cannot accept data!');
$c = $c->finish('Bye!');
Close WebSocket connection or long poll stream gracefully.
# Accept WebSocket handshake and close WebSocket connection immediately
$c->rendered(101)->finish(1001);
Close WebSocket connection or long poll stream gracefully. This method will
automatically respond to WebSocket handshake requests with a C<101> response
status, to establish the WebSocket connection.
=head2 flash
Expand Down Expand Up @@ -543,7 +543,9 @@ L<Mojolicious::Plugin::DefaultHelpers> and L<Mojolicious::Plugin::TagHelpers>.
my $cb = $c->on(finish => sub {...});
Subscribe to events of L</"tx">, which is usually a L<Mojo::Transaction::HTTP>
or L<Mojo::Transaction::WebSocket> object.
or L<Mojo::Transaction::WebSocket> object. This method will automatically
respond to WebSocket handshake requests with a C<101> response status, to
establish the WebSocket connection.
# Do something after the transaction has been finished
$c->on(finish => sub {
Expand All @@ -570,16 +572,6 @@ or L<Mojo::Transaction::WebSocket> object.
$c->app->log->debug("Received $len bytes");
});
This method will automatically respond to WebSocket handshake requests with a
C<101> response status, to establish the WebSocket connection.
HTTP/1.1 101 Switching Protocols
Server: Mojolicious (Perl)
Date: Tue, 03 Feb 2015 17:08:24 GMT
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: SWsp5N2iNxPbHlcOTIw8ERvyVPY=
=head2 param
my $value = $c->param('foo');
Expand Down Expand Up @@ -723,9 +715,6 @@ using a C<200> response code.
$c->res->body('Hello World!');
$c->rendered(200);
# Accept WebSocket handshake without subscribing to an event
$c->rendered(101);
=head2 req
my $req = $c->req;
Expand Down Expand Up @@ -801,7 +790,9 @@ L<Mojolicious::Plugin::DefaultHelpers/"accepts">.
$c = $c->send($chars => sub {...});
Send message or frame non-blocking via WebSocket, the optional drain callback
will be invoked once all data has been written.
will be invoked once all data has been written. This method will automatically
respond to WebSocket handshake requests with a C<101> response status, to
establish the WebSocket connection.
# Send "Text" message
$c->send('I ♥ Mojolicious!');
Expand All @@ -822,9 +813,6 @@ will be invoked once all data has been written.
$c->send('Second message!');
});
# Accept WebSocket handshake, send a message and close connection immediately
$c->rendered(101)->send('I ♥ Mojolicious!')->finish;
For mostly idle WebSockets you might also want to increase the inactivity
timeout with L<Mojolicious::Plugin::DefaultHelpers/"inactivity_timeout">, which
usually defaults to C<15> seconds.
Expand Down
20 changes: 0 additions & 20 deletions lib/Mojolicious/Guides/Cookbook.pod
Expand Up @@ -623,26 +623,6 @@ L<Mojolicious::Controller/"send">.
</body>
</html>

The connection gets established when you respond to the WebSocket handshake
request, which is a normal HTTP C<GET> request, with a C<101> response status,
which happens automatically if you subscribe to an event with
L<Mojolicious::Controller/"on">.

GET /echo HTTP/1.1
Host: 127.0.0.1:3000
User-Agent: Mojolicious (Perl)
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: IDM3ODE4NDk2MjA1OTcxOQ==
Sec-WebSocket-Version: 13

HTTP/1.1 101 Switching Protocols
Server: Mojolicious (Perl)
Date: Tue, 03 Feb 2015 17:08:24 GMT
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: SWsp5N2iNxPbHlcOTIw8ERvyVPY=

The event L<Mojo::Transaction::WebSocket/"finish"> will be emitted right after
the WebSocket connection has been closed.

Expand Down
3 changes: 2 additions & 1 deletion lib/Mojolicious/Guides/Routing.pod
Expand Up @@ -638,7 +638,8 @@ additional information.

The connection gets established when you respond to the WebSocket handshake
request with a C<101> response status, which happens automatically if you
subscribe to an event with L<Mojolicious::Controller/"on">.
subscribe to an event with L<Mojolicious::Controller/"on"> or send a message
with L<Mojolicious::Controller/"send"> right away.

GET /echo HTTP/1.1
Host: mojolicio.us
Expand Down
12 changes: 6 additions & 6 deletions t/mojo/websocket.t
Expand Up @@ -47,11 +47,11 @@ get '/something/else' => sub {

websocket '/early_start' => sub {
my $c = shift;
$c->send('test1');
$c->send('test' . ($c->tx->is_established ? 1 : 0));
$c->on(
message => sub {
my ($c, $msg) = @_;
$c->send("${msg}test2");
$c->send("${msg}test" . ($c->tx->is_established ? 1 : 0));
$c->finish(1000 => 'I ♥ Mojolicious!');
}
);
Expand Down Expand Up @@ -160,7 +160,7 @@ is $code, 200, 'right status';
ok $body =~ /^(\d+)failed!$/ && $1 == 15, 'right content';

# Server directly sends a message
$result = undef;
$result = '';
my ($status, $msg);
$ua->websocket(
'/early_start' => sub {
Expand All @@ -175,16 +175,16 @@ $ua->websocket(
$tx->on(
message => sub {
my ($tx, $msg) = @_;
$result = $msg;
$tx->send('test3');
$result .= $msg;
$tx->send('test2');
}
);
}
);
Mojo::IOLoop->start;
is $status, 1000, 'right status';
is $msg, 'I ♥ Mojolicious!', 'right message';
is $result, 'test3test2', 'right result';
is $result, 'test0test2test1', 'right result';

# Connection denied
($stash, $code, $ws) = ();
Expand Down
7 changes: 4 additions & 3 deletions t/mojolicious/websocket_lite_app.t
Expand Up @@ -84,10 +84,11 @@ websocket '/once' => sub {
);
};

websocket '/close' => sub { shift->rendered(101)->finish(1001) };
websocket '/close' => sub { shift->finish(1001) };

websocket '/one_sided' =>
sub { shift->rendered(101)->send('I ♥ Mojolicious!')->finish };
websocket '/one_sided' => sub {
shift->send('I ♥ Mojolicious!' => sub { shift->finish });
};

under '/nested';

Expand Down

0 comments on commit 9411298

Please sign in to comment.