tags:

views:

124

answers:

2

I'm writing a file upload handler Catalyst. I'm trying to restrict the maximum file size. To do this I've made a Plugin (based on the answer here). Here is the code where I check for the file size:

before 'prepare_body' => sub {
    my $c = shift;

    my $req = $c->request;
    my $length = $req->headers->{"content-length"};
    if ($length > 10000)
    {
        $c->stash->{errors} = "File upload error";
        # how do I abort the upload?
    }
};

This correctly detects files that are too big, but I can't for the life of me figure out how to abort the upload. Ideally, it should also reach the controller/action. Can anyone give me a pointer? Thanks a lot.

+1  A: 

I think this needs to occur earlier in the chain. If you have the headers, then the packet is already created.

Perhaps you could try: $c->detach(); or possibly loop through the $c->stack array and remove actions that might have been added, related to your upload.

vol7ron
No, `before 'prepare_body'` is A) early enough to stop reading the request body, and B) long before the dispatcher runs, so `$c->detach` and `$c->stack` both make no sense in this context.
hobbs
A: 

Very simply, you probably shouldn't. Anything you do from plugin code to abort the handling is going to knock out the ability of user code to deal with the situation in a nice way (for example by giving a validation error or a nice error page, instead of a Catalyst exception page).

However, all is not lost. Why not try something like this?

around 'prepare_body' => sub {
  my ($orig, $self) = (shift, shift);
  my ($c) = @_;

  my $max_length = ref($c)->config->{'Plugin::WhateverMyNameIs'}->{max_request_size};
  $max_length = 1_000_000 unless defined $max_length; # default
  my $length = $c->engine->read_length;
  if ($length <= $max_length) { # ok, go ahead
    $self->$orig(@_);
  } else {
    $c->stash->{request_body_aborted} = 1;
  }
};

This will stop the read if your request is over-size, but it will let dispatch proceed as normal -- which means you will want to write some code in your action, or in a begin action, or in a chain root, that checks for $c->stash->{request_body_aborted} and does something appropriate -- whether that's setting a form validation error, or calling $c->error("Request too large"); $c->detach or whatever. It's also configurable, as any plugin should be.

hobbs