Skip to content

Commit

Permalink
do not recommend multiple dispatch cycles in the cookbook
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Apr 29, 2014
1 parent cf50ccb commit 42bd0a8
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 55 deletions.
56 changes: 28 additions & 28 deletions lib/Mojolicious/Guides/Cookbook.pod
Expand Up @@ -668,42 +668,42 @@ which can be combined to solve some of hardest problems in web development.
use Mojolicious::Lite;
use Scalar::Util 'weaken';

# Emit "request" event early for requests that get upgraded to multipart
# Intercept multipart uploads and log each chunk received
hook after_build_tx => sub {
my $tx = shift;

# Subscribe to "upgrade" event to indentify multipart uploads
weaken $tx;
$tx->req->content->on(upgrade => sub { $tx->emit('request') });
$tx->req->content->on(upgrade => sub {
my ($single, $multi) = @_;
return unless $tx->req->url->path->contains('/upload');

# Subscribe to "part" event to find the right one
return $tx->req->content->on(part => sub {
my ($multi, $single) = @_;

# Subscribe to "body" event of part to make sure we have all headers
$single->on(body => sub {
my $single = shift;

# Make sure we have the right part and replace "read" event
return unless $single->headers->content_disposition =~ /example/;
$single->unsubscribe('read')->on(read => sub {
my ($single, $bytes) = @_;

# Log size of every chunk we receive
app->log->debug(length($bytes) . ' bytes uploaded.');
});
});
});
});
};

# Upload form in DATA section
get '/' => 'index';

# Streaming multipart upload (invoked twice, due to early "request" event)
post '/upload' => sub {
my $self = shift;

# First invocation, subscribe to "part" event to find the right one
return $self->req->content->on(part => sub {
my ($multi, $single) = @_;

# Subscribe to "body" event of part to make sure we have all headers
$single->on(body => sub {
my $single = shift;

# Make sure we have the right part and replace "read" event
return unless $single->headers->content_disposition =~ /example/;
$single->unsubscribe('read')->on(read => sub {
my ($single, $bytes) = @_;

# Log size of every chunk we receive
$self->app->log->debug(length($bytes) . ' bytes uploaded.');
});
});
}) unless $self->req->is_finished;

# Second invocation, render response
$self->render(text => 'Upload was successful.');
};
# Streaming multipart upload
post '/upload' => {text => 'Upload was successful.'};

app->start;
__DATA__
Expand Down
55 changes: 28 additions & 27 deletions t/mojolicious/upload_stream_lite_app.t
Expand Up @@ -10,64 +10,65 @@ use Mojolicious::Lite;
use Scalar::Util 'weaken';
use Test::Mojo;

# Emit "request" event early for multipart requests under "/upload"
# Stream multipart uploads into cache
my %cache;
hook after_build_tx => sub {
my $tx = shift;

weaken $tx;
$tx->req->content->on(
upgrade => sub {
$tx->emit('request') if $tx->req->url->path->contains('/upload');
}
);
};
my ($single, $multi) = @_;

my %cache;
post '/upload/:id' => sub {
my $self = shift;
return unless $tx->req->url->path->contains('/upload');

# First invocation, prepare streaming
my $id = $self->param('id');
$self->req->content->on(
part => sub {
my ($multi, $single) = @_;
$single->on(
body => sub {
my $single = shift;
return unless $single->headers->content_disposition =~ /my_file/;
$single->unsubscribe('read');
$single->on(read => sub { $cache{$id} .= pop });
my $id = $tx->req->url->query->param('id');
$multi->on(
part => sub {
my ($multi, $single) = @_;
$single->on(
body => sub {
my $single = shift;
return unless $single->headers->content_disposition =~ /my_file/;
$single->unsubscribe('read');
$single->on(read => sub { $cache{$id} .= pop });
}
);
}
);
}
);
return unless $self->req->is_finished;
};

# Second invocation, render response
post '/upload' => sub {
my $self = shift;
my $id = $self->param('id');
$self->render(data => $cache{$id});
};

get '/download/:id' => sub {
get '/download' => sub {
my $self = shift;
$self->render(data => $cache{$self->param('id')});
};

my $t = Test::Mojo->new;

# Small upload
$t->post_ok('/upload/23' => form => {my_file => {content => 'whatever'}})
$t->post_ok('/upload?id=23' => form => {my_file => {content => 'whatever'}})
->status_is(200)->content_is('whatever');

# Small download
$t->get_ok('/download/23')->status_is(200)->content_is('whatever');
$t->get_ok('/download?id=23')->status_is(200)->content_is('whatever');

# Big upload
$t->post_ok('/upload/24' => form => {my_file => {content => '1234' x 131072}})
$t->post_ok(
'/upload?id=24' => form => {my_file => {content => '1234' x 131072}})
->status_is(200)->content_is('1234' x 131072);

# Big download
$t->get_ok('/download/24')->status_is(200)->content_is('1234' x 131072);
$t->get_ok('/download?id=24')->status_is(200)->content_is('1234' x 131072);

# Small download again
$t->get_ok('/download/23')->status_is(200)->content_is('whatever');
$t->get_ok('/download?id=23')->status_is(200)->content_is('whatever');

done_testing();

0 comments on commit 42bd0a8

Please sign in to comment.