Hi there,
I've hit this before. This is because you are using Rack::URLMap (via
Builder#map).
The inner app is called with a new env hash.
> diff --git a/lib/rack/urlmap.rb b/lib/rack/urlmap.rb
> index b699d35..3374535 100644
> --- a/lib/rack/urlmap.rb
> +++ b/lib/rack/urlmap.rb
> @@ -45,7 +45,7 @@ module Rack
> next unless rest.empty? || rest[0] == ?/
>
> return app.call(
> - env.merge(
> + env.merge!(
> 'SCRIPT_NAME' => (script_name + location),
> 'PATH_INFO' => rest))
> }
Is this patch useful?
Is it useful to assume that a request will only have a single env hash?
Will it make Rack::Cascade and friends behave incorrectly?
Should URLMap revert the change in an ensure to allow subsequent
requests to function?
Hope this explains the behaviour,
Tim
> > diff --git a/lib/rack/urlmap.rb b/lib/rack/urlmap.rb
> > index b699d35..3374535 100644
> > --- a/lib/rack/urlmap.rb
> > +++ b/lib/rack/urlmap.rb
> > @@ -45,7 +45,7 @@ module Rack
> > next unless rest.empty? || rest[0] == ?/
> >
> > return app.call(
> > - env.merge(
> > + env.merge!(
> > 'SCRIPT_NAME' => (script_name + location),
> > 'PATH_INFO' => rest))
> > }
>
> Is this patch useful?
> Is it useful to assume that a request will only have a single env hash?
> Will it make Rack::Cascade and friends behave incorrectly?
>
> Should URLMap revert the change in an ensure to allow subsequent
> requests to function?
>
> Hope this explains the behaviour,
Yes it does :)
A workaround is creating an initial middleware (like rack-config), adding some
empty entries to env hash and latter, in other middlewares or final
application, using env["NEW_ENTRY"].replace.
Of course this is a hack as it's only valid if the new entry is a String or an
object supporting "replace".
class Init
def initialize(app)
@app = app
end
def call(env)
# Create env["REMOTE_USER"] so it can be latter replaced
# (String#replaced) by the main application and Clogger will log it.
env["REMOTE_USER"] = ""
@app.call(env)
end
end
And in config.ru:
---------------------------------
use Init
use ::Clogger, xxxxx
run MyApp.new
---------------------------------
Then in case env["REMOTE_USER"] is set into MyApp it should be set as follows:
class MyApp
def call(env)
...getting username string...
env["REMOTE_USER"].replace(username)
...
end
end
Would be any performance penalty in using env.merge! like you suggest rather
than env.merge?
Regards.
This is something I've always thought should be included in the rack
spec. Should a rack component, A, that calls another component, B, be
able to assume that env modifications made by B (or downstream) will
be visible in the env passed by A when B returns? As of right now,
there's a number of core and contrib middleware that assume: no. The
URLMap example above is a good one. A lot of middleware pass a copy of
the env downstream instead of modifying and relaying the env provided.
I see this come up fairly often but I'm not sure there's ever been a
good use case for it. It's usually a sign that you're using the env
where you should be using the response tuple. e.g., instead of putting
something in the env, put it in the response headers or convey it via
status code.
Or maybe this really is something the env could be useful for and I've
just never run into a good case. Can anyone provide real examples of
where it would be necessary?
Thanks,
Ryan
My real case:
I want to use Clogger to log the authenticated user ($env["REMOTE_USER"]).
Clogger expects that it runs after an authentication middleware which adds
such env entry. However in my case I need to decide the "remote_user" in the
final Rack application so I need that changes done in env by the last Rack
application are visible for previous middlewares (Clogger).
Perhaps env shouldn't be used for this purpose, but what else? adding such
info in the response headers seem a hack to me, what about if I want to pass
an object or a hash rather than a single string?