Context root / script_name in Ring

249 views
Skip to first unread message

Matjaz Gregoric

unread,
Oct 15, 2010, 3:57:08 PM10/15/10
to Ring
Hello,

I have been playing with Clojure and Ring for a while now and have
been quite impressed. Ring feels simple and intuitive, and I believe
Clojure and web development make for a very good match.

There is something about Ring that I find quite problematic, though,
which is that Ring has no notion of context root. By context root I
mean something like the SCRIPT_NAME variable as specified by Python's
WSGI and Ruby's Rack.
Ring merges WSGI's and Rack's SCRIPT_NAME and PATH_INFO variables into
the :uri entry of the request map.

I find this quite problematic since it makes it impossible to deploy
Ring applications under a non-root context (which is quite common when
deploying war files to java application servers) without modifying the
application or writing some custom middleware.

Perhaps even more importantly, the absence of context root information
makes it hard to compose arbitrary Ring applications, which due to the
fact that Ring applications are just functions, could otherwise be
strikingly simple.

Let's say I wrote a Ring web app and I want to add a blog engine to
it. I don't want to write my own engine but instead want to use an
existing open source Ring based blog engine. I could write a
middleware and route all requests to "/blog/*" to the blog engine
handler and route everything else to my main handler.

I have a problem however, since the blog engine expects to live at the
root url, not under /blog. I could rewrite the :uri variable in my
handler, but then the links that the blog engine constructs wouldn't
point to the right urls.

In my opinion a Ring application should work out of the box no matter
what context root it is deployed under.
I believe Ring specification should be changed to replace the :uri
variable with equivalents of Rack/WSGI's SCRIPT_NAME and PATH_INFO.

Does this make sense or am I missing something?

James Reeves

unread,
Oct 15, 2010, 5:37:24 PM10/15/10
to ring-c...@googlegroups.com
On 15 October 2010 20:57, Matjaz Gregoric <gre...@gmail.com> wrote:
> In my opinion a Ring application should work out of the box no matter
> what context root it is deployed under.
> I believe Ring specification should be changed to replace the :uri
> variable with equivalents of Rack/WSGI's SCRIPT_NAME and PATH_INFO.

I don't think that we should replace the :uri key.

First, whilst it might make sense to have :path and :context keys in
addition to the :uri key, I don't think they should *replace* the :uri
key. Why remove potentially useful information?

Second, the Ring request map is a representation of a HTTP request,
and the URI is an intrinsic part of every HTTP request. I think we
should keep the basic request/response maps as much of a one-to-one
mapping to the HTTP protocol as possible.

Third, what you describe could be packaged up in a middleware function:

(defn wrap-context [handler context]
(fn [{uri :uri :as request}]
(handler
(if (.startsWith uri context)
(assoc request
:context context
:path (subs uri 0 (count context)))
request))))

One could construct similar middleware to pull the context from the
servlet info, or a configuration file, rather than have it statically
specified.

- James

Matjaz Gregoric

unread,
Oct 16, 2010, 2:34:51 AM10/16/10
to ring-c...@googlegroups.com

Thank you for your response, James.

I don't think that we should replace the :uri key.

First, whilst it might make sense to have :path and :context keys in
addition to the :uri key, I don't think they should *replace* the :uri
key. Why remove potentially useful information?

:uri should always equal :context + :path, so it is redundant information.

Second, the Ring request map is a representation of a HTTP request,
and the URI is an intrinsic part of every HTTP request. I think we
should keep the basic request/response maps as much of a one-to-one
mapping to the HTTP protocol as possible.

I agree there certainly is a beauty to this, but on the other hand, I strongly believe that applications should work the same (without modification) regardless of what context they are deployed under. And indeed, a Ruby Rack application will work out of the box regardless whether you deploy it at the root, under /blog/* or under /path/to/my/app/*.

The :uri entry can always be reconstructed from :context and :path and Ring handlers should almost never care about :uri. In fact, if they care about the :uri, that is a sign that they are too tied to the context.

I am not saying the :uri key has to go, but the actual :uri shouldn't matter 99% of the time and having both a :path and :uri key in the request map might potentially lead to confusion.
 
Third, what you describe could be packaged up in a middleware function:

 (defn wrap-context [handler context]
   (fn [{uri :uri :as request}]
     (handler
       (if (.startsWith uri context)
         (assoc request
           :context context
           :path (subs uri 0 (count context)))
         request))))

One could construct similar middleware to pull the context from the
servlet info, or a configuration file, rather than have it statically
specified.

I think that it should be *enforced* by the Ring spec that adapters provide context information. The servlet adapter would pull the context out of the servlet, while other adapters would have different means of determining the context. Again, that's how WSGI and Rack do it.

The :context and :path entries should also be enforced by the Ring spec and it should be emphasized that handlers should base their actions on the :path key instead of the :uri key.
When constructing links, applications should take the :context entry into account.

Only then can applications truly be mountable under arbitrary context without having to change/add/modify middleware functions.


Matjaz

James Reeves

unread,
Oct 16, 2010, 6:52:52 PM10/16/10
to ring-c...@googlegroups.com
On 16 October 2010 07:34, Matjaz Gregoric <gre...@gmail.com> wrote:
> :uri should always equal :context + :path, so it is redundant information.

Perhaps, but so is :content-type, :content-length and
:character-encoding. Removing the :uri key also breaks backward
compatibility.

> I am not saying the :uri key has to go, but the actual :uri shouldn't matter
> 99% of the time and having both a :path and :uri key in the request map
> might potentially lead to confusion.

Other HTTP interfaces provide both (such as the Java servlet
specification) and I don't think that potential confusion is
sufficient grounds for breaking backward compatibility.

However, I'm beginning to warm to the idea of having standard keys for
keeping track of context and path, in addition to the :uri key.

Allowing handlers to have a context is useful, for frameworks like
Compojure which can nest routes, and for supporting legacy systems
like Java servlets.

Currently this is usually done by using middleware to remove the
context from the :uri key:

 (defn wrap-context [handler context]
   (fn [{uri :uri :as request}]

     (if (.startsWith uri context)
       (handler (assoc request :uri (subs uri 0 (count context))))))))

So if the context is "/foo", then the URI "/foo/bar" becomes "/bar".

However, this has two problems:

1. We're removing information from the request, or at least moving it
to a non-standard key (e.g. :full-uri).
2. The :uri key no longer refers to the request URI, so it's somewhat misnamed.
3. There's no standard key for referring back to the context.

I'm tempted to suggest introducing :context and :path keys. Perhaps
:path should be :path-info, as in Java and Ruby, but the "info" part
seems superfluous to me.

The :path key would be mandatory in the updated SPEC (for Ring version
0.4.0?). The :context key would be optional. If the handler has no
context (e.g. if you pass it directly into the Jetty adapter), then
the :path key would be equal to the :uri key.

In other words:

(= (:uri request)
(str (:context request) (:path request)))

Would always be true for the new version of the SPEC. To support
previous versions, one could fall back to the :uri if the :path is not
found:

(= (:uri request)
(str (:context request) (:path request (:uri request))))

This would be useful for the following reasons:

1. Web frameworks could start to use :path instead of :uri, providing
a standard way of interpreting context.
2. The :context key could be added to URLs in links, so a handler
proxied into a Java servlet would have correct links, even if the
servlet path is changed.
3. Web frameworks could check for :path first, and fall-back to :uri
if it is not found, allowing previous versions of Ring to be
supported.

I'd be interested in criticisms of this idea. I'm by no means sold on it...

- James

Matjaz Gregoric

unread,
Oct 17, 2010, 4:08:26 AM10/17/10
to ring-c...@googlegroups.com
Perhaps, but so is :content-type, :content-length and
:character-encoding. Removing the :uri key also breaks backward
compatibility.

Other HTTP interfaces provide both (such as the Java servlet
specification) and I don't think that potential confusion is
sufficient grounds for breaking backward compatibility.

These are all very good points, I now agree that the :uri entry should stay.

I'd be interested in criticisms of this idea. I'm by no means sold on it...

I believe what you propose would satisfy everything that I asked for in my original post. IMHO the Ring ecosystem would benefit from this change.

Having standard entries for context and path would allow for standard middleware to nest/map handlers and a standard way of constructing context-independent URLs in links.

Regards,
Matjaz

Saul Hazledine

unread,
Oct 17, 2010, 4:13:38 AM10/17/10
to Ring
On Oct 17, 12:52 am, James Reeves <jree...@weavejester.com> wrote:
> Allowing handlers to have a context is useful, for frameworks like
> Compojure which can nest routes, and for supporting legacy systems
> like Java servlets.
>
> The :path key would be mandatory in the updated SPEC (for Ring version
> 0.4.0?). The :context key would be optional. If the handler has no
> context (e.g. if you pass it directly into the Jetty adapter), then
> the :path key would be equal to the :uri key.
>
> In other words:
>
>   (= (:uri request)
>      (str (:context request) (:path request)))
>

This sounds good to me and simplifies routing with servlets.

One possible addition would be to complete the circle and help with
the generation of absolute URLs. Applications can route on :path but
need to generate urls with :context. Currently, I do this with my
own, substandard, middleware. However, if there was a way to do this
provided with Ring (or perhaps Compojure), it would make things easier
for newcomers.

Saul

Constantine Vetoshev

unread,
Oct 17, 2010, 4:57:44 PM10/17/10
to ring-c...@googlegroups.com
On Sat, Oct 16, 2010 at 3:52 PM, James Reeves <jre...@weavejester.com> wrote:
> I'm tempted to suggest introducing :context and :path keys. Perhaps
> :path should be :path-info, as in Java and Ruby, but the "info" part
> seems superfluous to me.
>
> The :path key would be mandatory in the updated SPEC (for Ring version
> 0.4.0?). The :context key would be optional. If the handler has no
> context (e.g. if you pass it directly into the Jetty adapter), then
> the :path key would be equal to the :uri key.
>
> In other words:
>
>  (= (:uri request)
>     (str (:context request) (:path request)))

I really, really like this. It would make combining Ring handlers and
regular Java servlets cleaner, since the Ring code could know both the
full :uri and the relative context-free :path.

One small comment: the spec above suggests that either :context needs
to have a trailing slash, or :path needs a leading slash. I'm
concerned that some code will require slash checking routines, as (=
"/myapp" "myapp") is false, but may be true as far as URL checking
goes.

Wilson MacGyver

unread,
Oct 17, 2010, 5:26:19 PM10/17/10
to ring-c...@googlegroups.com
this would be great. thus far all my clojure compojure/ring usage are
on traditional
tomcat war deployment. So this would be very helpful.


On Sat, Oct 16, 2010 at 6:52 PM, James Reeves <jre...@weavejester.com> wrote:
>  (= (:uri request)
>     (str (:context request) (:path request)))
>
> Would always be true for the new version of the SPEC. To support
> previous versions, one could fall back to the :uri if the :path is not
> found:
>
>  (= (:uri request)
>     (str (:context request) (:path request (:uri request))))


--
Omnem crede diem tibi diluxisse supremum.

James Reeves

unread,
Oct 17, 2010, 5:29:10 PM10/17/10
to ring-c...@googlegroups.com
On 17 October 2010 21:57, Constantine Vetoshev <gepa...@gmail.com> wrote:
> One small comment: the spec above suggests that either :context needs
> to have a trailing slash, or :path needs a leading slash. I'm
> concerned that some code will require slash checking routines, as (=
> "/myapp" "myapp") is false, but may be true as far as URL checking
> goes.

I suggest that :path follows the same rules as :uri, in that it must
always begin with a "/". This would be necessary if :path were to be
used as a substitute for :uri.

And since :path must begin with a "/", :context should not end with a "/".

- James

Brenton

unread,
Oct 17, 2010, 7:33:16 PM10/17/10
to Ring
Great idea. I vote for :path instead of :path-info. I tried to deal
with contexts in Sandbar but would love to delete that code in favor
of a standard way of dealing with contexts in Ring.

Brenton

James Reeves

unread,
Oct 17, 2010, 8:45:24 PM10/17/10
to ring-c...@googlegroups.com
On 18 October 2010 00:33, Brenton <bash...@gmail.com> wrote:
> Great idea. I vote for :path instead of :path-info.

:path does seem more logical, but :path-info is the name used by Java,
Ruby, .NET, Python and PHP. So I'm thinking that :path-info actually
might be the better choice, as it's the name used everywhere else.

Conversely, the :context key has no universal name.

- James

Shantanu Kumar

unread,
Oct 24, 2010, 4:32:30 AM10/24/10
to Ring
+1 to the whole idea of splitting up :uri into :context and :path[-
info].

Regards,
Shantanu

On Oct 18, 5:45 am, James Reeves <jree...@weavejester.com> wrote:

Mark McGranaghan

unread,
Oct 25, 2010, 2:53:51 AM10/25/10
to Ring
I have some thoughts about the specific solutions proposed for the
context/path-info problem, but I'd like to step back for a second and
look carefully at the problem itself.

The main motivators that I have heard so far for context/path-info
support are:

1) To help support nested routes and mounted applications within
applications.
2) To help users deploy applications in contexts.
3) Comparable specifications use it.

If you have had problems with 1 and 2, would you be willing to post
code examples and provide other specific details illustrating your
issue? Those details would enable us to build complete before/after
pictures of your use case both with and without various possible
changes to Ring. It's only with that sort of data that we can decide
if any of those changes would be a net benefit.

I'd be particularly interested in hearing about:
* The mechanics of how you feed :path-info and :context values into
uri generation for responses
* How you are able to / not able to obtain web context at boot / start
time
* Where your workarounds in current Ring apps fell over
* If / how you see nested routes as being different from mounted apps

I emphasize the importance here of example code in motivating the
discussion. I haven't had these use cases recently in any of my Ring
apps, but I started to sketch out some code to get a feel for the
problem:
http://gist.github.com/644502

Let me know if that's relevant to the issues you're facing as well.

- Mark

Philipp Meier

unread,
Oct 25, 2010, 4:34:42 AM10/25/10
to ring-c...@googlegroups.com
Am 25.10.10 08:53, schrieb Mark McGranaghan:

>
> I'd be particularly interested in hearing about:
> * The mechanics of how you feed :path-info and :context values into
> uri generation for responses
> * How you are able to / not able to obtain web context at boot / start
> time
> * Where your workarounds in current Ring apps fell over
> * If / how you see nested routes as being different from mounted apps
>
> I emphasize the importance here of example code in motivating the
> discussion. I haven't had these use cases recently in any of my Ring
> apps, but I started to sketch out some code to get a feel for the
> problem:
> http://gist.github.com/644502
>
> Let me know if that's relevant to the issues you're facing as well.

A feature that is missing is a way to build an URL reference for a
specific handler. Speaking in terms of your example, imaginge to link
from a blog post to a forum thread.

For simple handlers and routing this can be addressed by looking the
path for the forum handler in a routing map. However I suppose things
get complicated when handlers can be mounted as you sketched it.

I will try if I can work out a way to combine "in-context" with the
lookup of a handlers path.

-billy.

James Reeves

unread,
Oct 25, 2010, 4:27:54 PM10/25/10
to ring-c...@googlegroups.com
On 25 October 2010 07:53, Mark McGranaghan <mmcg...@gmail.com> wrote:
> I'd be particularly interested in hearing about:
> * The mechanics of how you feed :path-info and :context values into
> uri generation for responses

I suspect we'd need separate middleware to wrap the handler in a binding:

(declare *context*)

(defn wrap-context-binding [handler]
(fn [request]
(binding [*context* (request :context)]
(handler request))))

> * How you are able to / not able to obtain web context at boot / start
> time

To an extent, everything could be done using middleware. For example:

(defn wrap-servlet-path-info [handler]
(fn [request]
(let [path-info (.getPathInfo (:servlet-request handler))]
(handler (assoc request :path-info path-info)))))

It may be that if we were to go down this route, the :path-info and
:context keys would become de-facto standards.

However, I don't really like the idea of basing a web framework on
de-facto standards. It feels too much like circumventing the SPEC, and
risks fragmentation.

> * Where your workarounds in current Ring apps fell over

Compojure currently matches paths on the :uri, so routes with some
form of context have to rewrite the :uri key.

I've been thinking about providing middleware to do that, but it's
doesn't strike me as very neat.

> * If / how you see nested routes as being different from mounted apps

I don't think there is a difference. Whether an handler is embedded in
another handler, or embedded in a servlet, the general idea is the
same.

- James

Saul Hazledine

unread,
Oct 26, 2010, 2:27:36 AM10/26/10
to Ring
On Oct 25, 8:53 am, Mark McGranaghan <mmcgr...@gmail.com> wrote:
> I have some thoughts about the specific solutions proposed for the
> context/path-info problem, but I'd like to step back for a second and
> look carefully at the problem itself.
>
> The main motivators that I have heard so far for context/path-info
> support are:
>
> 1) To help support nested routes and mounted applications within
> applications.
> 2) To help users deploy applications in contexts.
>
> If you have had problems with 1 and 2, would you be willing to post
> code examples and provide other specific details illustrating your
> issue?
>

This is some example code that uses Compojure and gets the context
from the servlet request:

http://github.com/alienscience/compojure-war-example/blob/master/src/routes.clj


> I'd be particularly interested in hearing about:
> * How you are able to / not able to obtain web context at boot / start
> time

If it is possible to get the context at start time that would make
things a lot easier and cleaner. I don't know how though.

>
> I emphasize the importance here of example code in motivating the
> discussion. I haven't had these use cases recently in any of my Ring
> apps, but I started to sketch out some code to get a feel for the
> problem:http://gist.github.com/644502
>

The example code you gave assumes that the clojure application knows
which context it is deployed under. The problem I have is that the
application context is set by the servlet container e.g a standalone
Jetty sets the context based on the name of the war file.

Saul

Matjaz Gregoric

unread,
Oct 26, 2010, 4:03:11 AM10/26/10
to ring-c...@googlegroups.com
On Mon, Oct 25, 2010 at 8:53 AM, Mark McGranaghan <mmcg...@gmail.com> wrote:
* The mechanics of how you feed :path-info and :context values into
uri generation for responses

Assuming that you only need to build the path part of the URL:

(defn path-to [path req]
  (str (:context req) path)))

(path-to "/gallery" req) ; -> "/my/context/gallery"

If you need full URLs (ie. "http://example.com/my/context/gallery"), you could pull the :scheme, :server-name and :server-port from the request.
The important thing is, that the context needs to be prepended to the path.

 
* Where your workarounds in current Ring apps fell over

The main problem is that since there is no standard set of keys to represent context and path, each developer/framework has to invent his own way of dealing with nesting.

For instance, I'd like to be able to compose a compojure application, a moustache application and a couple of pure ring handlers. Since ring spec says nothing about context this currently isn't possible without specifically crafting each application to be mountable and using custom middleware to wire them up.
 

* If / how you see nested routes as being different from mounted apps

Ideally there should be no difference.

With nested routes I usually think about handlers that I coded myself/have control over, and with mounted apps I think about full applications that I pulled from github or somewhere.

Since ring apps are "just functions" it shouldn't matter whether one is trying to mount his own handler or an arbitrary app. The proposed changes to the spec would help to minimize the difference.

 
I emphasize the importance here of example code in motivating the
discussion. I haven't had these use cases recently in any of my Ring
apps, but I started to sketch out some code to get a feel for the
problem:
http://gist.github.com/644502

The general idea is good, but the in-context function should be modified to allow for arbitrary level of nesting (the blog-app or forum-app could itself be composed of multiple nested handlers).

In the code sample you assume that blog-app and forum-app will know how to use :context and :path-info information, which may not be true unless :context and :path-info become part of the ring spec.

Even though this could be implemented purely as a middleware, I don't think that's a good idea.
That would mean that every handler in an application that decides to use the middeware to become "mountable", would have to act based on the :path-info key, while handlers in applications that don't use the middleware would have to act based on the :uri key.
If one would want to write a "universal" handler which would work in both situations, the handler would first have to check for the :path-info key and, if not present, fall back to the :uri, which is not very elegant.

If, on the other hand, :context and :path-info were enforced by the spec, there would be no such difference and every app/handler would be mountable by default.

IMHO the ability to compose and nest arbitrary handlers is important and seems natural in a functional language such as Clojure where composing functions is a common task.


Matjaz

Shantanu Kumar

unread,
Oct 26, 2010, 7:18:00 AM10/26/10
to Ring
Matjaz explains it quite nicely already. I would urge everybody to
consider :context as a more general abstraction than the Servlet
context-path.

For example, in a Servlet container a CRM application may be deployed
under the context path "/crmapp" (so, :context is "/crmapp"):
/crmapp/
/crmapp/register
/crmapp/login
/products/crmapp/choose-vendor/all-cat

Whereas in a non-servlet server, it may be deployed under "/products/
crmapp" (so, :context is "/products/crmapp")
/products/crmapp/
/products/crmapp/register
/products/crmapp/login
/products/crmapp/choose-vendor/all-cat

When handling nested routes such as "/products/crmapp/choose-vendor/
all-categories", :context maybe be "/products/crmapp/choose-vendor"
and the corresponding :path-info may be "/all-categories". Fictitious
example:

;; handler (see 'run-server' below)
;;
(handler ""
;; :context = /products/crmapp
(route "" (redirect-to "/")) ; goes to "/products/crmapp/"
(route "/" some-action)
(route "/register" register-fn)
(route "/login" login-fn)
(route "/choose-vendor" (handler-fn
(handler "/choose-vendor"
;; :context = /products/crmapp/choose-
vendor
(route "/" choose-vendor-fn)
(route "/all-cat" choose-allcat-fn))))
;; :context = /products/crmapp
(route "/something-else" foo-fn))


(run-server handler {:context "/products/crmapp"})

So, essentially I am trying to suggest that :context and :path-info
may be allowed to vary inside the handler for different use cases
(e.g. different routes).


Regards,
Shantanu


On Oct 26, 1:03 pm, Matjaz Gregoric <gre...@gmail.com> wrote:

Shantanu Kumar

unread,
Oct 26, 2010, 7:21:32 AM10/26/10
to Ring


On Oct 26, 4:18 pm, Shantanu Kumar <kumar.shant...@gmail.com> wrote:
> Matjaz explains it quite nicely already. I would urge everybody to
> consider :context as a more general abstraction than the Servlet
> context-path.
>
> For example, in a Servlet container a CRM application may be deployed
> under the context path "/crmapp" (so, :context is "/crmapp"):
> /crmapp/
> /crmapp/register
> /crmapp/login
> /products/crmapp/choose-vendor/all-cat

This line is a typo. I meant this:

James Reeves

unread,
Nov 18, 2010, 5:16:40 PM11/18/10
to ring-c...@googlegroups.com
Mark, have you thought any more about this?

I was making some changes to Clout and Compojure, and having a
:path-info key would be rather useful. I'm planning on changing Clout
so it only matches routes against request maps, and I'm planning on
improving Compojure's ability to embed one route inside another.

I could match against the :uri key, and alter it for embedded routes,
but it seems a rather nasty solution. The :path-info and :context keys
would be very nice to have.

- James

On 25 October 2010 07:53, Mark McGranaghan <mmcg...@gmail.com> wrote:

Mark McGranaghan

unread,
Nov 28, 2010, 5:12:21 PM11/28/10
to ring-c...@googlegroups.com
Hi James,

Thanks for the poke.

I have continued to think about this, and am still not convinced that
it should be in Ring core / the Ring spec.

My main hesitation is that the context (and therefore the path-info as
it relates to the uri) may need to be known outside of the
request-response cycle, or within the request-response cycle but not
particularly within the request map. As an example of the first point,
some of my Clojure apps generate and cache HTML asynchronously, and
this generation requires knowing the context. As an example of the
second, generating self-referencing urls requires some notion of
context, but it is more convenient to access through a vanilla var
than to extract it on every request from the request map. So I agree
that apps may sometimes need to know their context, but I'm not sure
(one way or the the other) that :context-info as the canonical source
is the best approach.

I think that the best iterative step forward would be for users who
want :path-info and :context keys to start using them and treating
them as a de-facto standard, in the same way that we use :params. This
will give us a chance to see how they are actually used in real code
and give us more data on which to make a decision to move the keys
into the Ring spec. I know you said specifically that you don't like
basing web application libraries on de-facto standards, but I think
it's worth doing for at least some amount of time so that we can see
what it would look like in actual application code.

How does that sound?

- Mark

James Reeves

unread,
Nov 28, 2010, 5:48:10 PM11/28/10
to ring-c...@googlegroups.com
Hi Mark,

I think I agree that the context is often most usefully kept in a var.
If the plans for supporting cross-thread bindings in Clojure bear
fruit, a bound var would be even more appropriate.

The path-info, however, is perhaps more closely tied to the request
map. My main interest in path-info is to use it for routing purposes
in Clout and Compojure, which currently use the :uri key.

I am a little wary about relying on the presence of a :path-info key
when not part of the spec. However, I can always fall back on the :uri
key:

(:path-info request (:uri request))

Which makes this less of a problem, as the :uri key is always
guaranteed to exist.

So I think that's probably okay. I just have a couple of questions:

1. Would you be okay with having wrap-context middleware in ring-core
that would add a :context and :path-info key to the request map?

2. Would you be okay with having the ring.util.servlet code (and by
extension, ring.adapter.jetty) adding a :context and :path-info key
automatically to the request map?

- James

Mark McGranaghan

unread,
Nov 28, 2010, 6:16:18 PM11/28/10
to ring-c...@googlegroups.com
On Sun, Nov 28, 2010 at 2:48 PM, James Reeves <jre...@weavejester.com> wrote:
> Hi Mark,
>
> I think I agree that the context is often most usefully kept in a var.
> If the plans for supporting cross-thread bindings in Clojure bear
> fruit, a bound var would be even more appropriate.
>
> The path-info, however, is perhaps more closely tied to the request
> map. My main interest in path-info is to use it for routing purposes
> in Clout and Compojure, which currently use the :uri key.
>
> I am a little wary about relying on the presence of a :path-info key
> when not part of the spec. However, I can always fall back on the :uri
> key:
>
>  (:path-info request (:uri request))
>
> Which makes this less of a problem, as the :uri key is always
> guaranteed to exist.
>
> So I think that's probably okay. I just have a couple of questions:
>
> 1. Would you be okay with having wrap-context middleware in ring-core
> that would add a :context and :path-info key to the request map?

Yes. I think having a canonical implementation of ":context and
:path-info via middleware" would be good. Were you thinking that this
middleware would also bind a context var? Perhaps this could be an
addition later. Also, as I explain below, I think that this middleware
should take an explicit context argument.

> 2. Would you be okay with having the ring.util.servlet code (and by
> extension, ring.adapter.jetty) adding a :context and :path-info key
> automatically to the request map?

I'd rather not do this just yet - I don't want to make speculative
changes to the API of such a core component, especially if users may
immediately start relying on it. I think a good compromise is to
require explicitly specifying the context to wrap-context. I'm also
hesitant on this because it suggests a mindset that I really want to
avoid, which is that the adapter/servlet and then :context is the
canonical source of the app's context. The :context key is a mechanism
for propagating the context throughout the request/response processing
call graph in a uniform way. The context should be known and set by
the app deployer, and that info given explicitly to any runtime
components that require it (perhaps through wrap-context, perhaps
through a var, etc.). It is in my view an error to only be able to
discover the context of your app by interrogating the adapter on which
it is running. As a practical matter, you'll probably went to set the
context explicitly anyways in your request/response tests. This is why
I rejected another idea that I had here, which was to have
wrap-context try to pull :context out of :servlet

- Mark

James Reeves

unread,
Nov 28, 2010, 6:47:03 PM11/28/10
to ring-c...@googlegroups.com
On 28 November 2010 23:16, Mark McGranaghan <mmcg...@gmail.com> wrote:
> The :context key is a mechanism for propagating the context
> throughout the request/response processing call graph in a uniform
> way. The context should be known and set by the app deployer,
> and that info given explicitly to any runtime components that require
> it (perhaps through wrap-context, perhaps through a var, etc.).

Okay, that seems reasonable. The context is unique to a particular
handler, rather than the request map.

However, the path-info *is* specific to the request map. I guess one
could always derive manually it by subtracting the context from the
URI, but it seems a fairly useful and appropriate thing to add to the
request map.

Perhaps the wrap-context middleware should *only* set the :path-info
key. After all, if the context is fixed, then one can refer to it via
a var or some other static reference.

(def my-context "/foo")

(def app (wrap-context handler my-context))

In this case, the context is static, and therefore is stored in a var.
The wrap-context middleware just adds a :path-info key to the request.

That said, if there is also a :context key, then generic middleware
can access the context, which may be useful for redirections and so
forth that require a URI.

Another option is to have a (path-info request) function, which
derives the path info dynamically from the request. However, in such a
case, how would the context be stored?

I guess that's the main question: where should the context be stored,
if not in the request map? Ideally, the context needs to be:

1. Accessible by generic middleware that might need it.
2. Specific to a handler, but not necessarily the entire program.

In fact, I'm not even sure about number 2, because one could
potentially have the same handler function running under different
contexts.

- James

Matjaz Gregoric

unread,
Nov 29, 2010, 4:15:02 PM11/29/10
to ring-c...@googlegroups.com
I'm also hesitant on this because it suggests a mindset that I really want to
avoid, which is that the adapter/servlet and then :context is the
canonical source of the app's context. The :context key is a mechanism
for propagating the context throughout the request/response processing
call graph in a uniform way. The context should be known and set by
the app deployer, and that info given explicitly to any runtime
components that require it (perhaps through wrap-context, perhaps
through a var, etc.). It is in my view an error to only be able to
discover the context of your app by interrogating the adapter on which
it is running.

I agree that there is much more to the context than merely the context of the adapter.

The :context and :path-info keys might get modified by middleware during the course of the request/response cycle, for instance when using a router to route requests to multiple nested handlers or when mounting one compojure application inside another.
The adapter context is only the first step in this cycle.

It is understandable that you wouldn't want to let adapters inject :context and :path-info keys into the request while these keys are not yet part of the spec, but if they do make it into the spec, I'd say adapters should be responsible for setting the original values of :context and :path-info.

This is probably the behavior one would expect and if for some reason one wants to change the context to some other value, he can always do so using a custom middleware.

- Matjaz

James Reeves

unread,
Nov 29, 2010, 4:23:01 PM11/29/10
to ring-c...@googlegroups.com
On 29 November 2010 21:15, Matjaz Gregoric <gre...@gmail.com> wrote:
> The :context and :path-info keys might get modified by middleware during the
> course of the request/response cycle, for instance when using a router to
> route requests to multiple nested handlers or when mounting one compojure
> application inside another.

That's a good point. In such a case, the context would need to be held
in a binding, or added to the request map.

Having the context in the request map has a certain symmetry, as the
path-info is also contained in the request.

- James

Matjaz Gregoric

unread,
Nov 29, 2010, 5:51:55 PM11/29/10
to ring-c...@googlegroups.com
That said, if there is also a :context key, then generic middleware
can access the context, which may be useful for redirections and so
forth that require a URI.

I believe this is important. If *generic* middleware doesn't have access to the context, we didn't really do much.

While the context could be made available through a dynamically bound var in the ring namespace, that just feels strange. I believe the :context entry in the request map is a better solution and feels more "ringish", if I may say so.


I guess that's the main question: where should the context be stored,
if not in the request map? Ideally, the context needs to be:

1. Accessible by generic middleware that might need it.
2. Specific to a handler, but not necessarily the entire program.

In fact, I'm not even sure about number 2, because one could
potentially have the same handler function running under different
contexts.

I want to add to this that the context could even be completely dynamic.

In my days of working with Zope (a Python web framework/app server) I found one thing which I think is quite convenient - the VirtualHostMonster (http://www.zope.org/Documentation/Books/ZopeBook/2_6Edition/VirtualHosting.stx).

It is basically a way of addressing your zope application (which is usually deployed inside a zope server under a non-root context) through a specially crafted url to make the application generate links as though the requests were coming in from a different host, port and context root.

Let's say the application is available on http://localhost:8080/myapp. Now I want to make it available as http://www.mydomain.com. Thanks to the VirtualHostMonster, I can simply write a rewrite rule inside my frontend web server (apache, nginx...) to rewrite all requests coming in to http://www.mydomain.com to:

http://localhost:8080/VirtualHostBase/http/www.mydomain.com/80/myapp/VirtualHostRoot

Zope will recognize the special tokens in the incoming url and either way I access the application (through localhost or mydomain), zope will generate the links correctly, once at /myapp, once at the root context.

We found this quite useful since we could start working on an application on localhost and then, when the application was ready to go to production, set up the domain and add a VirtualHostMonster rewrite rule to apache. The application didn't have to be modified in any way and the links were generated correctly no matter how the application was accessed.

Now to (finally) get to the point: Something like zope's VirtualHostMonster could easily be implemented as a ring middleware which would modify the :context and :path-info values according to the tokens in the incoming uri.
Context would be completely dynamic in such case.

If the context in ring was required to be set explicitly by the developer, these types of middleware wouldn't be possible.

- Matjaz

PS: I am by no means a fan of zope, but I actually find this feature quite convenient.

James Reeves

unread,
Nov 29, 2010, 6:05:07 PM11/29/10
to ring-c...@googlegroups.com
On 28 November 2010 23:16, Mark McGranaghan <mmcg...@gmail.com> wrote:
> Yes. I think having a canonical implementation of ":context and
> :path-info via middleware" would be good.

I tried implementing this, but wound up with the problem of what to do
if the context doesn't match.

For instance: (def app (wrap-context handler "/foo"))

In this case:

(app {:uri "/foo/bar"})

Is the same as:

(hander {:path-info "/bar", :context "/foo", :uri "/foo/bar})

But what should happen if the URI doesn't match the context, e.g.

(app {:uri "/other/bar"})

In Compojure, routes that don't match return nil, so we could do the
same in this case.

However, this would seem out of place in Ring. As far as I'm aware,
all other Ring middleware returns a valid response map, so this
doesn't seem the right choice.

I can think of a few other ways to do this, but I'm tempted to
implement this first in Compojure, in order to test it out.

- James

Saul Hazledine

unread,
Nov 30, 2010, 4:58:34 AM11/30/10
to Ring
On Nov 29, 11:51 pm, Matjaz Gregoric <gre...@gmail.com> wrote:
> Let's say the application is available onhttp://localhost:8080/myapp. Now I
> want to make it available ashttp://www.mydomain.com. Thanks to the
> VirtualHostMonster, I can simply write a rewrite rule inside my frontend web
> server (apache, nginx...) to rewrite all requests coming in tohttp://www.mydomain.comto:
>
> http://localhost:8080/VirtualHostBase/http/www.mydomain.com/80/myapp/...
>

I suggested a similar solution to a problem on the Compojure mailing
list a few months ago. Unfortunately, the person who had to handle
different contexts wasn't using a frontend web server (such as nginx)
on their intranet and had many webapplications deployed which would
make rewriting rules difficult to maintain.

I've used rewriting rules in the past but can see its not the ideal
solution when dealing with different contexts.

Saul
Reply all
Reply to author
Forward
0 new messages