Plack::Request base URL changes after callback?

18 views
Skip to first unread message

Eric Wong

unread,
May 16, 2016, 3:37:52 AM5/16/16
to psgi-...@googlegroups.com
Hi,

Attached is a standalone script which seems to show
Plack::Request::base changing the URL after the response
callback is called.

I'm not sure if this is intended behavior or if I'm doing
something wrong somewhere (very likely) or even a bug.

I can work around it by capturing the URL I want before
making the response callback.

Attachment is also at https://80x24.org/plack-request-base-cb.t
in case mail filters eat it.


Anyways, other than this minor confusion, PSGI/Plack has been a
pleasure to work with (coming from many years of Ruby/Rack,
CGI.pm/mod_perl/CGI::Application). Thanks!
plack-request-base-cb.t

Aristotle Pagaltzis

unread,
May 16, 2016, 5:25:47 PM5/16/16
to psgi-...@googlegroups.com
Hi Eric,

* Eric Wong <e...@80x24.org> [2016-05-16 11:22]:
> I'm not sure if this is intended behavior or if I'm doing something
> wrong somewhere (very likely) or even a bug.

in Plack::App::URLMap (which is what implements `mount '/a' => $app`)
you will find this:

my $orig_path_info = $env->{PATH_INFO};
my $orig_script_name = $env->{SCRIPT_NAME};

$env->{PATH_INFO} = $path;
$env->{SCRIPT_NAME} = $script_name . $location;
return $self->response_cb($app->($env), sub {
$env->{PATH_INFO} = $orig_path_info;
$env->{SCRIPT_NAME} = $orig_script_name;
});

I guess the idea here is to make the modified environment visible to the
mounted app(s) but hide it from middleware layers outside the mapping.
I’m puzzled as to why it was not simply written as this:

local $env->{PATH_INFO} = $path;
local $env->{SCRIPT_NAME} = $script_name . $location;
return $app->($env);

This would make your test pass, insofar as the environment would not
change after calling the writer callback. But note that this means that
the environment would change even *before* you call the writer callback:
namely as soon as your app returns its streaming response callback. When
the streaming response callback eventually gets called by the server, it
is in a completely different call stack and the effect of that `local`
has long been unwound.

So the fix would still be to capture the base URL – only you would have
to do that in your *app*, *outside* your streaming response callback.

Which is a habit I suggest you get into in general.

PSGI does not discuss how to make temporary modifications to the %env
hash in middlewares, so you will find that middlewares use a surprising
variety of ways of doing that, all with slightly different semantics,
mostly similar but sometimes wildly different and inconvenient (such as
passing down a modified copy of the entire %env hash [^1]).

The major semantic differences are when and how the changes are unwound,
which makes it tricky to write code that looks at the environment during
a streaming response – adding or removing a middleware may change how it
behaves. The request call flow (that the app sub participates in) takes
place before unwinding, so there are no surprises at that time.

So the less you need to look at the environment during the response; the
more of that you can take care of on the request side of the fence – the
more robust your code will be against using different middlewares.

[^1]: I’m looking at you, Catalyst::Middleware::Stash.

Regards,
--
Aristotle Pagaltzis // <http://plasmasturm.org/>
Reply all
Reply to author
Forward
0 new messages