Why env.object_id is different in each middleware?

18 views
Skip to first unread message

Iñaki Baz Castillo

unread,
Dec 9, 2009, 5:59:08 PM12/9/09
to rack-...@googlegroups.com
Hi, I've realized that env.object_id is different when inspecting it in each
middleware or final application in a Rack builder. How is possible?

In my case I use this simple builder:

---------------------------------------
::Rack::Builder.new do

use ::Clogger, ...

map "/", &::MyApp.handle_request

end


class MyApp
def self.handle_request
proc do run Proc.new { |env|
::MyApp::Request.handle(env)
} end
end
end
---------------------------------------

If I check env.object_id into Clogger call method, and also into
MyApp.ahndle_request, they have different values.

Since env is a hash I cannot understand why its object_id changes. Any
explanatuion for it?
This explains that when I change env[XXX] into my final appplication
MyApp.handle_request the change doesn't exist after calling
resp = @app.call(env)
in the first middleware.

Thanks a lot.
--
Iñaki Baz Castillo <i...@aliax.net>

Iñaki Baz Castillo

unread,
Dec 9, 2009, 6:15:17 PM12/9/09
to rack-...@googlegroups.com
El Miércoles, 9 de Diciembre de 2009, Iñaki Baz Castillo escribió:
> Since env is a hash I cannot understand why its object_id changes. Any
> explanation for it?
> This explains that when I change env[XXX] into my final appplication
> MyApp.handle_request the change doesn't exist after calling
> resp = @app.call(env)
> in the first middleware.

If I add a env["LALA"] in the first middleware then it's visible for following
middlewares.
However if I add env["LOLO"] in the second middleware this is not visible for
first middleware after calling "@app.call(env)".

Is it the expected behavior?

Thanks.

Tim Carey-Smith

unread,
Jan 3, 2010, 7:50:22 PM1/3/10
to rack-...@googlegroups.com

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

Iñaki Baz Castillo

unread,
Jan 3, 2010, 7:58:18 PM1/3/10
to rack-...@googlegroups.com
El Lunes, 4 de Enero de 2010, Tim Carey-Smith escribió:

> > 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 :)

Iñaki Baz Castillo

unread,
Jan 4, 2010, 2:44:15 PM1/4/10
to rack-...@googlegroups.com
El Lunes, 4 de Enero de 2010, Tim Carey-Smith escribió:
> 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?

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.

Ryan Tomayko

unread,
Jan 5, 2010, 3:57:58 PM1/5/10
to rack-...@googlegroups.com

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

Iñaki Baz Castillo

unread,
Jan 5, 2010, 6:47:41 PM1/5/10
to rack-...@googlegroups.com
El Martes, 5 de Enero de 2010, Ryan Tomayko escribió:
> 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?

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?

Reply all
Reply to author
Forward
0 new messages