How to stop spring-boot embedded-undertow from adding trailing slash - java

When i call the context root of my spring-boot application "localhost:8080/api/players", which is mapped in a RestController method by the annotation #GetMapping(path= {"/",""}), undertow alway redirect (httpstatus: 307 Temporary redirect) to "localhost:8080/api/players/" adding trailing slash at the end.
My application context-root is indeed /api/players as defined in spring-boot application.properties file (server.servlet.context-path=/api/players)
I've tried with embedded-tomcat and it works correctly by setting the property server.tomcat.redirect-context-root=false
There is a way to configure undertow to act like tomcat?

Peering into the sourcecode for undertow a bit, it looks like the relevant code is here in ServletInitialHandler.java, which will issue a 307 redirect status code in the case that the request is an upgrade request... unless the request is an HTTP 2 upgrade request. This doesn't seem to be configurable by the server, although there is some attempt to avoid the redirect based on what the client does.
It's probably worthwhile to look at your HTTP requests, understand better if your HTTP client is actually making an upgrade request, and then consider either accepting this fact, or changing the client to make a different kind of request (possibly by making it send the HTTP2 upgrade request header).

Related

Limiting the maximum request size in Tomcat

Working on a REST service based on Spring Boot with an embedded Tomcat I would like to limit the size of the request body for any HTTP method (POST, PUT, GET, ...).
I know of the maxPostSize property of a connector. But this property seems to limit only POST requests. Another possibility IMHO is to implement javax.servlet.Filter and to check the value of the Content-Length header.
Is there another way to achieve this that I am not aware of?
You may set the multipart.maxRequestSize property in application.properties. The default is 10Mb.

How to make Spring Security application to run behind a proxy?

We have an application build on Java 1.6 with Spring 3.0.3 that use Spring Security 3.0.5 and implements REST API using Spring Web with RestEasy 2.1.0.
I need to place this application (server) behind a proxy that would translate HTTPS request traffic from a REST API Client application into HTTP traffic. This change creates a “cross domain” scenario for login request : Client sends HTTPS request for login, and Server answers with redirect URL of HTTP. Currently it responses with:
”http://192.168.0.10:8090/index.html;jsessionid=64FD79...86D”,
what I need here is:
”/index.html;jsessionid=64FD79...86D”
We came with solution to make server to respond with “relative” URL instead “absolute” URL. So I tried to implement something similar with described situation here:
thread on forum.spring.io
I have set the RedirectStrategy bean with contextRelative="true" and override the redirectStrategy setter from AbstractAuthenticationTargetUrlRequestHandler within my LoginSuccessHandler extended class and I see that redirectStrategy property for HttpServletResponse object is set to true as expected. Still it not resolving the issue.
Also when changing redirectURLCC property of HttpServletResponse object using encodeRedirectURL("otherLogin") is sets something like
”http://192.168.0.10:8090/otherLogin”
and its not what I need. I need to remove whole protocol+ipaddress part of the URL. The URL property of response object is not accessible for change as it is wrapped by Filter and FilterChain interfaces implementation.
Please suggest any ideas. I suppose this kind of things should be resolved in web.xml or auth-AplicationContext.xml files not in code.
Best Regards.
Spring Security uses the following logic when sending a redirect:
public void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException {
String redirectUrl = calculateRedirectUrl(request.getContextPath(), url);
redirectUrl = response.encodeRedirectURL(redirectUrl);
if (logger.isDebugEnabled()) {
logger.debug("Redirecting to '" + redirectUrl + "'");
}
response.sendRedirect(redirectUrl);
}
The sendRedirect method is required to behave in the following way:
This method can accept relative URLs; the servlet container must
convert the relative URL to an absolute URL before sending the
response to the client.
That means you will by deafult always get an absolute URL, no matter what's the configuration or context setting.
You have multiple options:
configure your container or application server to be aware of the public URL (for example by using AJP instead of HTTP reverse proxy, or passing HTTP headers with the public URL which is supported by some application servers), e.g. documentation for Tomcat
configure your HTTP reverse proxy to perform correct rewriting, e.g. see ProxyPassReverse in Apache mod_proxy documentation
implement a custom org.springframework.security.web.RedirectStrategy where you will manually set the Location response header and HTTP 302 status code, this should allow you to send context relative redirect as you want
Not entirely relative, but probably you might want to have a look into a 4th option using the org.springframework.web.servlet.view.UrlBasedViewResolver to rewrite the redirected URL with your external hostname as described in this answer: Override the default redirect URL in a SpringMVC application.
That should be achievable on Apache with:
ProxyPreserveHost Off
ProxyPass / http://192.168.0.10:8090
ProxyPassReverse / http://192.168.0.10:8090
and adding one more proxy reverse on the proxing host with the port.
E.g supposing your server name is proxy.example.com the fourth line would be:
ProxyPassReverse / http://proxy.exemple.com:8090
Look at this answer: Sending redirect in Tomcat web application behind a Apache 2 proxy (mod_proxy)

Response already committed when using Spring 3.x on Websphere 8.5

When adding / updating a response header after the request has been processed I get the following warning:
com.ibm.ws.webcontainer.srt.SRTServletResponse setHeader SRVE8094W: WARNING: Cannot set header. Response already committed.
I am trying to manipulate the header by calling httpServletResponse.setHeader() or httpServletResponse.addHeader() from within a servlet filter after filterChain.doFilter() or by postHandle code spring interceptor.
I have encountered this problem while running my webapp using spring 3.x on websphere 8.5.
The problem is not reproduced on tomcat 7.0 therefore my guess is that it is Websphere related.
The problem is not reproduced in a pure servlet app therefore my guess is that it is Spring related.
I am familiar with
ShallowEtagHeaderFilter does not work under WAS8 app server
and with
Cannot set header in JSP. Response already committed
but I am looking for a "cleaner" solution.
Can anybody help me on this?
Websphere strictly follows the J2EE standard, hence whatever works in Tomcat need not necessarily work in Websphere. In my project, I got a lot of exception/errors which are particular for Websphere, but works as a charm in Tomcat. I suggest you to set the header in the controller. One other alternate is not to flush the output stream in the controller (if you have done it explicitly).

How do i deal with // URI prefix in tomcat?

i am running a tomcat server which has filters (and a servlet) mapped to /xxx/*
I have client that sends //xxx/* at the HTTP header as the uri. as a result, the servlet and the filters are not getting called.
I have tried putting a filter at /* that catches the request, wraps it with a requestWrapper and override the getURI() and getServletPath() methods (they just return the URI with a single / to whoever calls)
That doesn't seem to work, so i am assuming that once tomcat receives a request it decides which servlet/filters should be evaluated against this uri BEFORE even sending it to the first filter.
Is there a way to solve this? can i make tomcat reevaluate after every filter maybe? is there another way?
thanks in advance
If the URLs in the request header are not conforming to the spec, Tomcat is doing the right thing by dropping them.
If this is the problem, you need to fix the client so that it puts proper absolute URLs into the HTTP requests.
"Hacking" Tomcat to make it accept rubbish requests is a bad idea. It will limit your options for upgrading platforms, and/or deploying in different network environments.

Tomcat is not adding trailing slash to web app's context

I'd like to have Tomcat automatically add a trailing slash to my app's context if the url is entered without it.
When I test with Jetty, it automatically adds the trailing slash to my app's context, but Tomcat doesn't do this.
I'm uncertain what the context will be named once deployed, as I'm handing the WAR off to someone else, so any resource references in HTML is all relative. Is there any way to have Tomcat automatically redirect to the same context with a trailing slash added?
Currently Using Tomcat 7 with Spring 3.
It's an old post, but as of Tomcat 7.0.67, you need to add the following attribute to your context.xml file:
<Context mapperContextRootRedirectEnabled="true">...</Context>
As per the 7.0.67 changelog:
Move the functionality that provides redirects for context roots and directories where a trailing / is added from the Mapper to the DefaultServlet. This enables such requests to be processed by any configured Valves and Filters before the redirect is made. This behaviour is configurable via the mapperContextRootRedirectEnabled and mapperDirectoryRedirectEnabled attributes of the Context which may be used to restore the previous behaviour.
And in the Tomcat context documentation:
mapperContextRootRedirectEnabled: If enabled, requests for a web application context root will be redirected (adding a trailing slash) if necessary by the Mapper rather than the default Servlet. This is more efficient but has the side effect of confirming that the context path exists. If not specified, the default value of false is used.
It seems that your application's web.xml has a mapping to "/*". A servlet-mapping to "/*" causes tomcat to pass the request as-is to the web application (i.e. does not redirect).
To properly redirect, you must change the "/*" mapping to just "/", the latter means the default servlet.
Tomcat adds a trailing slash automatically. Just test it with the example application supplied with Tomcat..
If - due to some special configuration - it does not, I'd write a Filter that examines the query string and redirects as needed by the application. Many times this is needed anyways (doing http->https redirections, etc.)
Have you tried playing with URL Rewrite on Tomcat?
This might help: http://code.google.com/p/urlrewritefilter/
If that does not help, take a look at this: URL rewrite in tomcat web.xml
Pat's excellent answer helped me dig up a few more details on this. It seems this is related to some quirks in some versions of Tomcat (Tomcat 7 at 7.0.67+, and Tomcat 8 between 8.29 and 8.37) having to do with session cookies and URL redirection.
The bottom line seems to be that if the java server creates path-specific session cookies with a slash at the end (like "/app_name/"), then the server must also do an automatic initial redirect (/app_name --> /app_name/) ... otherwise, the session cookie will not get sent with the request, and it will never look to the server like you have a valid session. The may cause a redirect loop from the app to the authentication.
There are configurations in Tomcat that control both behaviors, but as far as I can tell, they were essentially out-of-sync in these versions, such that one might get the cookie with the trailing slash, without getting the redirect. There are several related issues/changes in the Tomcat changelog:
https://tomcat.apache.org/tomcat-8.0-doc/changelog.html
As Pat has already noted, this is resolved by adding this attribute to your app's Context element:
<Context mapperContextRootRedirectEnabled="true">

Categories