Skip to content

Commit

Permalink
added simple JSON serialization support and json event to Mojo::Trans…
Browse files Browse the repository at this point in the history
…action::WebSocket
  • Loading branch information
kraih committed May 8, 2013
1 parent 62494ad commit 0cdd4c4
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 39 deletions.
4 changes: 3 additions & 1 deletion Changes
@@ -1,6 +1,8 @@

4.0 2013-05-08
4.0 2013-05-09
- Code name "Top Hat", this is a major release.
- Added simple JSON serialization support to Mojo::Transaction::WebSocket.
- Added json event to Mojo::Transaction::WebSocket.
- Added is_empty method to Mojo::Transaction::HTTP.
- Added close_gracefully method to Mojo::IOLoop::Stream.
- Removed Mojolicious::Plugin::PoweredBy and
Expand Down
8 changes: 3 additions & 5 deletions examples/websocket.pl
@@ -1,16 +1,14 @@
use FindBin;
use lib "$FindBin::Bin/../lib";
use Mojolicious::Lite;
use Mojo::JSON 'j';

websocket '/test' => sub {
my $self = shift;
$self->on(
text => sub {
my ($self, $data) = @_;
my $hash = j($data);
json => sub {
my ($self, $hash) = @_;
$hash->{test} = "$hash->{test}";
$self->send({text => j($hash)});
$self->send({json => $hash});
}
);
};
Expand Down
36 changes: 30 additions & 6 deletions lib/Mojo/Transaction/WebSocket.pm
Expand Up @@ -2,6 +2,7 @@ package Mojo::Transaction::WebSocket;
use Mojo::Base 'Mojo::Transaction';

use Config;
use Mojo::JSON;
use Mojo::Transaction::HTTP;
use Mojo::Util qw(b64_encode decode encode sha1_bytes xor_encode);

Expand Down Expand Up @@ -200,12 +201,17 @@ sub resume {
sub send {
my ($self, $frame, $cb) = @_;

# Binary or raw text
$frame
= exists $frame->{text}
? [1, 0, 0, 0, TEXT, $frame->{text}]
: [1, 0, 0, 0, BINARY, $frame->{binary}]
if ref $frame eq 'HASH';
if (ref $frame eq 'HASH') {

# JSON
$frame->{text} = Mojo::JSON->new->encode($frame->{json}) if $frame->{json};

# Binary or raw text
$frame
= exists $frame->{text}
? [1, 0, 0, 0, TEXT, $frame->{text}]
: [1, 0, 0, 0, BINARY, $frame->{binary}];
}

# Text or object (forcing stringification)
$frame = [1, 0, 0, 0, TEXT, encode('UTF-8', "$frame")]
Expand Down Expand Up @@ -288,6 +294,8 @@ sub _message {

# Whole message
my $msg = delete $self->{message};
$self->emit(json => Mojo::JSON->new->decode($msg))
if $self->has_subscribers('json');
if (delete $self->{op} == TEXT) {
$self->emit(text => $msg);
$msg = decode 'UTF-8', $msg;
Expand Down Expand Up @@ -386,6 +394,21 @@ Emitted when a WebSocket frame has been received.
say "Payload: $frame->[5]";
});
=head2 json
$ws->on(json => sub {
my ($ws, $json) = @_;
...
});
Emitted when a complete WebSocket message has been received and there are
subscribers, messages will be automatically JSON decoded.
$ws->on(json => sub {
my ($ws, $hash) = @_;
say "Message: $hash->{msg}";
});
=head2 message
$ws->on(message => sub {
Expand Down Expand Up @@ -592,6 +615,7 @@ Resume C<handshake> transaction.
$ws = $ws->send({binary => $bytes});
$ws = $ws->send({text => $bytes});
$ws = $ws->send({json => {test => [1, 2, 3]}});
$ws = $ws->send([$fin, $rsv1, $rsv2, $rsv3, $op, $bytes]);
$ws = $ws->send(Mojo::ByteStream->new($chars));
$ws = $ws->send($chars);
Expand Down
10 changes: 4 additions & 6 deletions lib/Mojo/UserAgent.pm
Expand Up @@ -541,18 +541,16 @@ Mojo::UserAgent - Non-blocking I/O HTTP and WebSocket user agent
}
$delay->wait unless Mojo::IOLoop->is_running;
# Non-blocking WebSocket connection sending and receiving JSON text messages
use Mojo::JSON 'j';
# Non-blocking WebSocket connection sending and receiving JSON messages
$ua->websocket('ws://localhost:3000/echo.json' => sub {
my ($ua, $tx) = @_;
say 'WebSocket handshake failed!' and return unless $tx->is_websocket;
$tx->on(text => sub {
my ($tx, $bytes) = @_;
my $hash = j($bytes);
$tx->on(json => sub {
my ($tx, $hash) = @_;
say "WebSocket message via JSON: $hash->{msg}";
$tx->finish;
});
$tx->send({text => j({msg => 'Hello World!'})});
$tx->send({json => {msg => 'Hello World!'});
});
Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
Expand Down
21 changes: 9 additions & 12 deletions lib/Mojolicious/Controller.pm
Expand Up @@ -602,20 +602,17 @@ L<Mojo::Transaction::WebSocket> object.
$c->app->log->debug("Message: $msg");
});
# Receive JSON object via WebSocket "Text" message
use Mojo::JSON 'j';
$c->on(text => sub {
my ($c, $bytes) = @_;
my $test = j($bytes)->{test};
$c->app->log->debug("Test: $test");
# Receive JSON object via WebSocket message
$c->on(json => sub {
my ($c, $hash) = @_;
$c->app->log->debug("Test: $hash->{test}");
});
# Receive JSON object via WebSocket "Binary" message
use Mojo::JSON 'j';
# Receive WebSocket "Binary" message
$c->on(binary => sub {
my ($c, $bytes) = @_;
my $test = j($bytes)->{test};
$c->app->log->debug("Test: $test");
my $len = length $bytes;
$c->app->log->debug("Received $len bytes.");
});
=head2 param
Expand Down Expand Up @@ -838,6 +835,7 @@ is set to the value C<XMLHttpRequest>.
$c = $c->send({binary => $bytes});
$c = $c->send({text => $bytes});
$c = $c->send({json => {test => [1, 2, 3]}});
$c = $c->send([$fin, $rsv1, $rsv2, $rsv3, $op, $bytes]);
$c = $c->send(Mojo::ByteStream->new($chars));
$c = $c->send($chars);
Expand All @@ -850,8 +848,7 @@ will be invoked once all data has been written.
$c->send('I ♥ Mojolicious!');
# Send JSON object as "Text" message
use Mojo::JSON 'j';
$c->send({text => j({test => 'I ♥ Mojolicious!'})});
$c->send({json => {test => 'I ♥ Mojolicious!'});
# Send JSON object as "Binary" message
use Mojo::JSON 'j';
Expand Down
7 changes: 7 additions & 0 deletions lib/Mojolicious/Guides/Cookbook.pod
Expand Up @@ -531,6 +531,13 @@ L<Test::Mojo> API to be used.
->message_is('echo: Hello Mojo!')
->finish_ok;

# Test JSON web service
$t->websocket_ok('/echo.json')
->send_ok({json => {test => [1, 2, 3]}})
->message_ok
->json_message_is('/test', [1, 2, 3])
->finish_ok;

done_testing();

=head2 EventSource web service
Expand Down
4 changes: 2 additions & 2 deletions lib/Test/Mojo.pm
Expand Up @@ -825,16 +825,16 @@ Reset user agent session.
$t = $t->send_ok({binary => $bytes});
$t = $t->send_ok({text => $bytes});
$t = $t->send_ok({json => {test => [1, 2, 3]}});
$t = $t->send_ok([$fin, $rsv1, $rsv2, $rsv3, $op, $payload]);
$t = $t->send_ok($chars);
$t = $t->send_ok($chars, 'sent successfully');
Send message or frame via WebSocket.
# Send JSON object as "Text" message
use Mojo::JSON 'j';
$t->websocket_ok('/echo.json')
->send_ok({text => j({test => 'I ♥ Mojolicious!'})})
->send_ok({json => {test => 'I ♥ Mojolicious!'})
->message_ok
->json_message_is('/test' => 'I ♥ Mojolicious!')
->finish_ok;
Expand Down
12 changes: 5 additions & 7 deletions t/mojolicious/websocket_lite_app.t
Expand Up @@ -27,13 +27,12 @@ get '/echo' => {text => 'plain echo!'};

websocket '/json' => sub {
my $self = shift;
$self->on(binary => sub { shift->send({binary => j([@{j(shift)}, 4])}) });
$self->on(
text => sub {
json => sub {
my ($self, $json) = @_;
my $hash = j($json);
$hash->{test} += 1;
$self->send({text => j($hash)});
return $self->send({json => [@$json, 4]}) if ref $json eq 'ARRAY';
$json->{test} += 1;
$self->send({json => $json});
}
);
};
Expand Down Expand Up @@ -148,8 +147,7 @@ $t->websocket_ok('/echo')->send_ok([0, 0, 0, 0, 2, 'c' x 100000])
$t->get_ok('/echo')->status_is(200)->content_is('plain echo!');

# JSON roundtrips
$t->websocket_ok('/json')
->send_ok({text => j({test => 23, snowman => ''})})
$t->websocket_ok('/json')->send_ok({json => {test => 23, snowman => ''}})
->message_ok->json_message_is('' => {test => 24, snowman => ''})
->json_message_has('/test')->json_message_hasnt('/test/2')
->send_ok({binary => j([1, 2, 3])})
Expand Down

0 comments on commit 0cdd4c4

Please sign in to comment.