What happens when an exception is thrown from a filter after the servlet completes? - http

I am working on a service hosting several REST API using Java Servlet.
The Servlet uses annotations provided by javax.ws.rs to define the API's URL, HTTP method, etc. So one of them may look like as follows:
#Path("root")
#Service
public class MyClass {
#Path("path")
#Get
#Produces("application/json; charset=UTF-8")
public ResponseObject myAction() { ... }
}
I also have a filter that due to some bug, could throw an exception after executing the servlet. So it behaves as follows:
public class MyFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
chain.doFilter(request, response);
throws new Exception();
}
}
The web.xml contains following definition: (not complete, just those I think might be relevant)
<servlet>
<servlet-name>jersey-servlet</servlet-name>
<servlet-class>
com.sun.jersey.spi.spring.container.servlet.SpringServlet
</servlet-class>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>myorg.MyFilter</filter-class>
</filter>
When I call this API using different approaches, I get different results.
Using Jersey client and get as string
WebResource resource = client.resource("https://my-service/root/path");
ClientResponse response = resource.get(ClientResponse.class);
String s = response.getEntity(String.class);
This will give me below error:
Caused by: java.io.IOException: Premature EOF
at sun.net.www.http.ChunkedInputStream.readAheadBlocking(ChunkedInputStream.java:565)
at sun.net.www.http.ChunkedInputStream.readAhead(ChunkedInputStream.java:609)
at sun.net.www.http.ChunkedInputStream.read(ChunkedInputStream.java:696)
at java.io.FilterInputStream.read(FilterInputStream.java:133)
at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(HttpURLConnection.java:3393)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.Reader.read(Reader.java:140)
at com.sun.jersey.core.util.ReaderWriter.readFromAsString(ReaderWriter.java:171)
at com.sun.jersey.core.util.ReaderWriter.readFromAsString(ReaderWriter.java:157)
at com.sun.jersey.core.provider.AbstractMessageReaderWriterProvider.readFromAsString(AbstractMessageReaderWriterProvider.java:114)
at com.sun.jersey.core.impl.provider.entity.StringProvider.readFrom(StringProvider.java:73)
at com.sun.jersey.core.impl.provider.entity.StringProvider.readFrom(StringProvider.java:58)
at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:634)
Using Jersey client but de-serializing the JSON response into an object
WebResource resource = client.resource("https://my-service/root/path");
ClientResponse response = resource.get(ClientResponse.class);
ResponseObject result = response.getEntity(ResponseObject.class);
This will return successfully and the resulting ResponseObject contains correct content.
Call this API using HTTP tool Postman. This will also return status 200 and correct response body.
Call the API using .Net core library RestSharp
var restClient = new RestClient("https://my-service/");
var request = new RestRequest("/root/path", method);
var response = restClient.Execute(request);
This will fail with following error:
The server returned an invalid or unrecognized response.
at System.Net.Http.HttpConnection.FillAsync()
at System.Net.Http.HttpConnection.ChunkedEncodingReadStream.ReadAsyncCore(Memory`1 buffer, CancellationToken cancellationToken)
at System.Net.Http.HttpConnection.HttpContentReadStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at RestSharp.Extensions.MiscExtensions.ReadAsBytes(Stream input)
at RestSharp.Http.ProcessResponseStream(Stream webResponseStream, HttpResponse response)
at RestSharp.Http.ExtractResponseData(HttpResponse response, HttpWebResponse webResponse)
at RestSharp.Http.GetResponse(HttpWebRequest request)
My question is:
What happened to my HTTP response in this case and why different clients behaved differently ? I guess when MyClass.myAction completed, the serialized return object ResponseObject was already sent to the client. However, when the control was given back to MyFilter, an error was thrown. This must have had some impact on the HTTP response, but what ? And why would this by observed in some cases by some client but ignored by others ?
It's quite easy for me to fix the bug itself - to not throw exception in the filter. But I want to understand more about what happened underneath and appreciate any help.

Related

No 'Access-Control-Allow-Origin' header is present on the requested resource | Firebase Cloud Firestore - Java - Ionic

I'm developing an application and I'm using Ionic (Angular) for client side, java (JAX-RS) as backend and Firebase Cloud Firestore for persistence. Maybe this stack is weird but I'm new in create real applications and Firebase caughy my attention.
I want to save data in Firebase Cloud Firestore using Ionic with an REST API, I can do this with tools like postman, but i can't with Ionic, I'm getting an error No 'Access-Control-Allow-Origin' header is present on the requested resource. You will say to me that configure my server to allow requests but I do this already, I test my REST API without including the part of saving data in Firebase Cloud Firestore, and this work perfectly, I don't get that error. The problem only spends when I call a Firebase method.
Server Configuration
I add the following Java Class
package api;
public class RestCorsFilter implements Filter {
#Override
public void destroy() {
//enter code here
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse resp = (HttpServletResponse) response;
resp.addHeader("Access-Control-Allow-Origin", "*");
resp.addHeader("Access-Control-Allow-Headers", "*");
resp.addHeader("Access-Control-Allow-Methods", "*");
chain.doFilter(request, resp);
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
}
And then, I add this fragment in web.xml
<filter>
<filter-name>RestCorsFilter</filter-name>
<filter-class>api.RestCorsFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>RestCorsFilter</filter-name>
<url-pattern>/api/*</url-pattern>
</filter-mapping>
My application path:
#ApplicationPath("/api")
I was facing this error because every exception caused in the java application was showed in browser's console as a CORS error. The solution was look at the application log to catch and deal with the exception generated by the application.

ContentCachingResponseWrapper : How to get application response object (not httpResponse) using ContentCachingResponseWrapper

Using Interceptors to validate the requests in Spring Web.
I've extended HandlerInterceptorAdapter to implement postHandle method.
I want to check the value inside application response object and accordingly do some action.
I tried IOUtils to get the app response object but getting a "" string.
public class XYZInterceptor extends HandlerInterceptorAdapter {
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
// need to retrieve application response object
return;
}
}
After going through many docs, and hands on I figured out that the input stream / output steam can be accessed only once. The response object would already have written output stream somewhere before it reaches postHandler. So output stream is empty in response object of postHandle.
If you wish to access response object in postHandle is to setAttribute for request object with actual response object.

Spring 4 upgrade broke error page filter chain

Scenario:
We have an interceptor that looks for bogus attributes in URLs and throws a NoSuchRequestHandlingMethodException if it finds one. We then display a custom 404 page.
All pages go through the same filter chain to set up the local request state, log some information, and then display the requested page. In Spring 4, it stopped going through the filter chain for the 404 page in this case. It still goes through it if you go to a completely bogus page, and the 404 works, but when we throw the NoSuchRequestHandlingMethodException, the filters don't happen.
Spring 3:
1. Runs the filter chain for the main request
2. We throw NoSuchRequestHandlingMethodException
3. Filter chain finishes
4. New filter chain starts
5. We log the error page metrics
6. We display a nice 404 page to the customer
Spring 4:
1. Runs the filter chain for the main request
2. We throw NoSuchRequestHandlingMethodException
3. Filter chain finishes
4. We try to log the error page metrics, but NPE since a second filter chain never started
5. We display a terrible blank page to the customer
Filter code in web.xml:
<!-- The filter that captures the HttpServletRequest and HttpServletResponse-->
<filter>
<filter-name>ServletObjectFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>xxxxxxx.servletObjectFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>ServletObjectFilter</filter-name>
<servlet-name>springmvc</servlet-name>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
...
<error-page>
<error-code>404</error-code>
<location>/errors/404</location>
</error-page>
Filter code:
public void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain chain )
throws ServletException, IOException {
try {
getServletContainer().setServletObjects( request, response );
chain.doFilter( request, response );
} finally {
getServletContainer().removeAll();
}
ServletContainer:
static final ThreadLocal< HttpServletRequest > REQUESTS = new ThreadLocal< HttpServletRequest >();
static final ThreadLocal< HttpServletResponse > RESPONSES = new ThreadLocal< HttpServletResponse >();
public void setServletObjects( HttpServletRequest request, HttpServletResponse response ) {
REQUESTS.set( request );
RESPONSES.set( response );
}
public void removeAll() {
REQUESTS.remove();
RESPONSES.remove();
}
Code that then fails:
public class RequestResponseAwareBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization( Object bean, String beanName ) {
...
if ( bean instanceof RequestAware ) {
HttpServletRequest request = getServletContainer().getRequest();
if ( request == null ) {
throw new IllegalStateException( "The request object is NULL" );
}
RequestAware requestAware = (RequestAware) bean;
requestAware.setRequest( request );
}
}
I "solved" the problem by splitting up my error page #Controller into two, one where they're the targets of internal redirects and don't get the filter chain, and one where they are directly loaded, and do get the filter chain. I then added the redirect #Controller to the interceptor blacklist, so it doesn't require any logic or data from the filters. It solved this specific problem, but I'm worried that something else in my codebase also relies on this behavior.

How can I redirect but keep the URL the same

I am doing the following, but the url in the address bar changes. from /test to localhost:8080...
Is it possible to keep the url the same in the address bar?
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>xxx.xxxx.Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>test</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
Servlet.java
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String path = request.getRequestURI();
response.sendRedirect("http://localhost:8080"+path);
}
You can use forward instead of redirect. I wrote a method that gets a servlet's name and dispatch it:
protected void gotoServlet(HttpServletRequest req, HttpServletResponse resp,String servletName) throws ServletException, IOException {
RequestDispatcher dispatcher = this.getServletContext().getNamedDispatcher(servletName);
dispatcher.forward(req,resp);
}
You have to first understand that your Servlets (HttpServlet) and the Servlet container they're running in are implementing an HTTP stack. HTTP is a request-response protocol.
The client sends an HTTP request and the server (your Servlet container) replies with an HTTP response. In this case,
response.sendRedirect("http://localhost:8080"+path);
it is responding with a 302, indicating redirection. How your client handles this is up to them. Typically, a browser client will send a new HTTP GET request to the redirection target. This will force a page refresh/renew.
If that's not the behavior you want, you need to change your client behavior. For example, you can put part of your client logic within an iframe. You'd then have the redirect only refresh the iframe.

How to get Request object in CustomPhaseListener?

Am hitting my Servlet from a link. Some Cookies would have been already set in Client. When my Servlet is hit, I want to retrieve these Cookies.
For eg., am hitting the link like http:/myDomain/myServlet/ServletReceiver
In web.xml, I have below code
<servlet>
<display-name>ServletReceiver</display-name>
<servlet-name>ServletReceiver</servlet-name>
<servlet-class>(location of my ServletReceiver)</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletReceiver</servlet-name>
<url-pattern>/ServletReceiver</url-pattern>
</servlet-mapping>
And my ServletReceiver code is below
public class ServletReceiver extends HttpServlet{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie[] cookies = request.getCookies();
// Do some checks here based on cookies obtained and redirect to corresponding page
RequestDispatcher dispatcher=request.getRequestDispatcher("/pages/index.jsf");
dispatcher.forward(request, response);
}
}
My requirement is, when I retrieve some data from Cookies, I want to set it into bean. And since am creating an instance of the bean in CustomPhaselistener (and not in ServletReceiver), if I get the request object through which I can get cookie values then I can set that in my bean in PhaseListener.
My bean is request scoped.
So, is there a way to get request object in CustomPhaseListener?
Also, am retrieving Cookies in doGetmethod. Is that suggested?
Am using JSF 1.2

Resources