Stripes

Resolving UrlBindings incorrectly: an unbound URL does not yield a 404 error

Details

  • Type: Bug Bug
  • Status: Open Open
  • Priority: Minor Minor
  • Resolution: Unresolved
  • Affects Version/s: Release 1.5.1
  • Fix Version/s: Release 1.6
  • Component/s: ActionBean Dispatching
  • Labels:
    None

Description

I have several ActionBean classes bound with these:
@UrlBinding("/")
@UrlBinding("/search/{text}")
@UrlBinding("/profile")
@UrlBinding("/admin")
@UrlBinding("/admin/{username}")

In addition to these, I have custom error pages defines for HTTP codes 403, 404 and 500.

When I navigate to /foobar I get the page for /, instead of the custom 404 error I expected.

What goes wrong here is that the closest match, @UrlBinding("/"), has no parameters. Therefore, it cannot match.
The bug probably is in UrlBindingFactory.getBindingPrototype(String path); it should check if a binding can match the URI. This probably only means an added check to see if the binding has a parameter that can be filled in.

  1. STS-688.patch
    12/Jun/09 2:25 PM
    2 kB
    Oscar Westra van Holthe - Kind

Activity

Hide
Oscar Westra van Holthe - Kind added a comment - 02/Jun/09 2:31 AM

A question that came to me this morning: if a URL binding has no parameters, should it be available for prefix matching at all?

If the answer is no, then my previous assessment is wrong.

Show
Oscar Westra van Holthe - Kind added a comment - 02/Jun/09 2:31 AM A question that came to me this morning: if a URL binding has no parameters, should it be available for prefix matching at all? If the answer is no, then my previous assessment is wrong.
Hide
Oscar Westra van Holthe - Kind added a comment - 12/Jun/09 2:25 PM

As an additional test, /profile/gg also yields the page mapped to /profile.
This is not intended (@UrlBinding("/profile") has no parameters and cannot match anything but /profile).

As a solution, I propose to ignore bindings without parameters, like so (a unified diff is attached as a patch):

$ diff -w UrlBindingFactory.java.orig UrlBindingFactory.java
464a465,471
> List<UrlBinding> parameters = binding.getParameters();
> if (binding != null && !binding.getParameters().isEmpty())
> { > // Ignore bindings without parameters: they are static, and hence already match. > // Also, as an example, a binding of @UrlBinding("/profile") should not match "/profile/gg". > // Fixes issue STS-688. > 474a482 > }

Show
Oscar Westra van Holthe - Kind added a comment - 12/Jun/09 2:25 PM As an additional test, /profile/gg also yields the page mapped to /profile. This is not intended (@UrlBinding("/profile") has no parameters and cannot match anything but /profile). As a solution, I propose to ignore bindings without parameters, like so (a unified diff is attached as a patch): $ diff -w UrlBindingFactory.java.orig UrlBindingFactory.java 464a465,471 > List<UrlBinding> parameters = binding.getParameters(); > if (binding != null && !binding.getParameters().isEmpty()) > { > // Ignore bindings without parameters: they are static, and hence already match. > // Also, as an example, a binding of @UrlBinding("/profile") should not match "/profile/gg". > // Fixes issue STS-688. > 474a482 > }
Hide
Oscar Westra van Holthe - Kind added a comment - 12/Jun/09 3:10 PM

My tests:

  • The patch works correctly against the code of both version 1.5.1 and a fresh subversion snapshot.
  • The paths mentioned in the issue, that resolved to a non-matching bindings now yield a 404 error.
  • Other paths, both with and without parameters, still resolve as they did.

I trust these tests completely test what I changed.

Show
Oscar Westra van Holthe - Kind added a comment - 12/Jun/09 3:10 PM My tests:
  • The patch works correctly against the code of both version 1.5.1 and a fresh subversion snapshot.
  • The paths mentioned in the issue, that resolved to a non-matching bindings now yield a 404 error.
  • Other paths, both with and without parameters, still resolve as they did.
I trust these tests completely test what I changed.
Hide
Ben Gunter added a comment - 15/Oct/09 2:36 PM

It was intentionally designed this way. Even basic servlet mappings behave this way. Whatever you bind the servlet to effectively acts as a prefix, and everything after that prefix is accessible by calling HttpServletRequest.getPathInfo(). There is also one little-known feature of Stripes that would be broken if "extra path info" were ignored. That is, the event name may be specified in the request path, as in /path/to/bean/eventName instead of using parameters. Because it would break backward compatibility and generally oppose how servlets have always worked, I don't think we can make this change.

Show
Ben Gunter added a comment - 15/Oct/09 2:36 PM It was intentionally designed this way. Even basic servlet mappings behave this way. Whatever you bind the servlet to effectively acts as a prefix, and everything after that prefix is accessible by calling HttpServletRequest.getPathInfo(). There is also one little-known feature of Stripes that would be broken if "extra path info" were ignored. That is, the event name may be specified in the request path, as in /path/to/bean/eventName instead of using parameters. Because it would break backward compatibility and generally oppose how servlets have always worked, I don't think we can make this change.
Hide
Oscar Westra van Holthe - Kind added a comment - 17/Oct/09 4:06 AM

I read the servlet specification (version 2.5) again to see if I missed something.

In my interpretation of section SRV.11.2, the URL pattern "/profile" does not match "/profile/extraPath", while "/profile/*" does. "/profile/" is used as an exact match, just like "/profile" is. This means that the Stripes feature to always use prefix mapping is not according to the servlet specification.

However, I also made an error, in that "/" actually matches anything that otherwise cannot match. So when using the DynamicMappingFilter in Stripes, this mapping prevents 404 errors.

Assuming that not following the servlet specification is not bad, this leaves the following options:

1. Leave the implementation as it is

When we leave the implementation as it is, always using prefix mapping, the javadoc for the UrlBinding must be updated. The reason is twofold, one being that it only mentions prefix mapping like this:
Clean URLs support both prefix mapping (/action/foo/{bar}), and extension mapping (/foo/{bar}.action).

In fact, the way I read the code is that /foo/{bar}.action is also used as a prefix! I doubt that this is intended.

Secondly, the event name is only documented to be handled like this:
The special parameter name $event may be used to embed the event name in a clean URL.
For example, given @UrlBinding("/foo/{$event}") the "bar" event could be invoked with the URL /foo/bar."

Note that no implementation of the ActionResolver interface mentions the prefix mapping either, and they would be a second place to look.

2. Make the URL bindings behave like documented

As documented now, URL bindings are an exact match. Not a match with extra path info, like when a servlet mapped to a URL pattern starting with a / and ending in /*. This means you'll need to use a parameter to get what otherwise would be extra path info, like this: @UrlBinding("/search/{text}"). The feature to specify the event name in the path must really be done like this: @UrlBinding("/profile/{$event}").

Obviously, adjusting the documentation is better for people who have become accustomed to the mismatch between documentation and implementation. I.e. backwards compatibility.

The current documentation however, is complex enough IMHO. Documenting the prefix mapping would further complicate things.

As a result, I would prefer an implementation that matches the current Javadoc.

Show
Oscar Westra van Holthe - Kind added a comment - 17/Oct/09 4:06 AM I read the servlet specification (version 2.5) again to see if I missed something. In my interpretation of section SRV.11.2, the URL pattern "/profile" does not match "/profile/extraPath", while "/profile/*" does. "/profile/" is used as an exact match, just like "/profile" is. This means that the Stripes feature to always use prefix mapping is not according to the servlet specification. However, I also made an error, in that "/" actually matches anything that otherwise cannot match. So when using the DynamicMappingFilter in Stripes, this mapping prevents 404 errors. Assuming that not following the servlet specification is not bad, this leaves the following options: 1. Leave the implementation as it is When we leave the implementation as it is, always using prefix mapping, the javadoc for the UrlBinding must be updated. The reason is twofold, one being that it only mentions prefix mapping like this: Clean URLs support both prefix mapping (/action/foo/{bar}), and extension mapping (/foo/{bar}.action). In fact, the way I read the code is that /foo/{bar}.action is also used as a prefix! I doubt that this is intended. Secondly, the event name is only documented to be handled like this: The special parameter name $event may be used to embed the event name in a clean URL. For example, given @UrlBinding("/foo/{$event}") the "bar" event could be invoked with the URL /foo/bar." Note that no implementation of the ActionResolver interface mentions the prefix mapping either, and they would be a second place to look. 2. Make the URL bindings behave like documented As documented now, URL bindings are an exact match. Not a match with extra path info, like when a servlet mapped to a URL pattern starting with a / and ending in /*. This means you'll need to use a parameter to get what otherwise would be extra path info, like this: @UrlBinding("/search/{text}"). The feature to specify the event name in the path must really be done like this: @UrlBinding("/profile/{$event}"). Obviously, adjusting the documentation is better for people who have become accustomed to the mismatch between documentation and implementation. I.e. backwards compatibility. The current documentation however, is complex enough IMHO. Documenting the prefix mapping would further complicate things. As a result, I would prefer an implementation that matches the current Javadoc.
Hide
Oscar Westra van Holthe - Kind added a comment - 29/Oct/09 4:27 PM

A new alternative:

Allowing the binding @UrlBinding("") as a special case would also solve this, without the need to change anything else.

Show
Oscar Westra van Holthe - Kind added a comment - 29/Oct/09 4:27 PM A new alternative: Allowing the binding @UrlBinding("") as a special case would also solve this, without the need to change anything else.
Hide
Anthony DePalma added a comment - 24/Aug/11 4:39 PM

So as it stands currently is there anything that can be done to allow custom 404 mappings to work with a home page bound like @UrlBinding("/")?

Show
Anthony DePalma added a comment - 24/Aug/11 4:39 PM So as it stands currently is there anything that can be done to allow custom 404 mappings to work with a home page bound like @UrlBinding("/")?
Hide
Ben Gunter added a comment - 22/Nov/11 12:37 PM

Oscar, you're right, of course, about servlet mappings not behaving as prefixes. I think now I'm willing to concede that @UrlBinding probably shouldn't either. I'll look at changing this behavior for 1.6.

Show
Ben Gunter added a comment - 22/Nov/11 12:37 PM Oscar, you're right, of course, about servlet mappings not behaving as prefixes. I think now I'm willing to concede that @UrlBinding probably shouldn't either. I'll look at changing this behavior for 1.6.

People

Vote (3)
Watch (3)

Dates

  • Created:
    01/Jun/09 8:11 AM
    Updated:
    22/Nov/11 12:38 PM