[dancer-users] after hook breaks send_file()

Warren Young warren at etr-usa.com
Mon Mar 24 13:26:01 GMT 2014

Part of the web app I'm porting to Dancer builds dynamic PDF files for 
the client when they hit certain URLs.  I'm generating these files in 
the route handler, then calling Dancer::send_file() at the end of the 
route handler.

All of this is working fine, except that I was getting a 500 error on 
the send_file call:

[8802] error @0.035479> [hit #16]request to GET /pdf/foo.pdf crashed: 
Too late to set another cookie, headers already built at 
/usr/lib/perl5/site_perl/5.8.8/Dancer/Response.pm line 166. in 
/usr/lib/perl5/site_perl/5.8.8/Dancer/Handler.pm l. 98

After some debugging, I found that all it takes to cause this is to add 
this to a freshly-generated lib/myapp.pm:

	hook after => sub {
	    my $sid = session->id;

That's it, merely *accessing* the session object after send_file() is 
called causes the error.[*]

I was able to fix this by putting this at the top of the after hook:

	return if request->path =~ m{\.pdf$};

I'm not sure if this creates a backdoor to my app's dynamic PDFs or not, 
bypassing session management.  My initial thought is that it doesn't, 
but data security can be tricky, and requires more thought than I've 
been able to give this so far.

Bottom line, is there any good reason why my after hook should not be 
able to a) check the Dancer session object; and b) redirect the user to 
another page, even though we've already scheduled a file download?

[*] My actual "after" hook code is more involved, checking whether our 
back-end server marked this session ID as invalid.  If it is, I redirect 
the user to '/' to make them log back in again.  I do this in the 
"after" hook because it isn't until after I try talking to the back-end 
server that I learn whether it still believes we're logged in.  Putting 
this in the "after" hook satisfies the DRY principle, since a worse 
alternative is to wrap all back-end server calls with "are we still 
alive?" checks.  Another poor alternative is to "ping" the back-end 
server in the "before" hook, but that's wasteful and creates a race 

More information about the dancer-users mailing list