You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tomcat.apache.org by Michael Greco <mi...@gmail.com> on 2015/10/04 20:03:57 UTC

Rewriting entire request /body in Servlet Filter not working as expected

First time post here.

Using :
Tomcat 8.0.26
JDK1.8.0 update 51
Apache MyFaces 2.2.8.
Maven build of webapp war file
Chrome  45.0.2454.101 m 64 bit
Windows 7 64 bit

Trying to rewrite the entire request body in a filter using a http request
wrapper.  Not entirely sure if this is the right approach or even possible
but researching similar examples it seems this approach should work.
Created a simple test case with a one page test app, expecting the
index.xhtml page's text value of  "Welcome" to be replaced with the text
"NEW TEXT HERE" found in the filter code, but I only get "Welcome" back
when the page is rendered in the browser.

Below is what I have in the index page, the filter and the web.xml and the
pom.xml.  The reason I can see is that the getInputStream() or getReader()
calls are never called by the FacesServlet in the extended
HttpServletRequestWrapper.  I did override the getParameter function to put
some simple debug output in there to ensure the wrapped request was
actually used, so I know it's definitely getting into this class but not
calling the methods that would return it the new body content.

I even tried to re-read the entire request in the getParametersMap() from
examples I found on the web, but this didn't seen to do the job either.  It
seems that the request body is somehow cached somewhere / somehow and not
changable?

Thanks in advance for any insight on this.

index page code (index.xhtml) :
---------------------------------------------------------------------------------
<!DOCTYPE html>
<html lang="en"
  xmlns="http://www.w3.org/1999/xhtml"
  xmlns:jsf="http://xmlns.jcp.org/jsf"
  xmlns:h="http://xmlns.jcp.org/jsf/html">
<head jsf:id="head">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Test Webapp</title>
</head>
<body jsf:id="body">
    <div><h1>Welcome</h1></div>
</body>
</html>

Filter code :
--------------------------------------------------------------------------
package com.testwebapp.reqrewrite.filter;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

public class RewriteBodyTestFilter implements Filter {

public void init(FilterConfig filterConfig) throws ServletException {
return;
}

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
String newReqBody = new String("<div><h1>NEW TEXT HERE</h1></div>");
HttpServletRequest req = (HttpServletRequest)request;
RewriteBodyRequestWrapper rewriteBodyRequestWrapper = new
RewriteBodyRequestWrapper(req, newReqBody);
chain.doFilter(rewriteBodyRequestWrapper, response);
}

public void destroy() {
return;
}
class RewriteBodyRequestWrapper extends HttpServletRequestWrapper {

private byte[] buffer;

public RewriteBodyRequestWrapper(HttpServletRequest req, String reqBody)
throws IOException {
super(req);
System.out.println("entering RewriteBodyRequestWrapper() constructor");
this.buffer = reqBody.getBytes();
}

@Override
public ServletInputStream getInputStream() {
System.out.println("entering RewriteBodyRequestWrapper().getInputStream()");
try {
ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
BufferedServletInputStream bsis = new BufferedServletInputStream(bais);
return bsis;
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
}
}

@Override
public BufferedReader getReader() {
System.out.println("entering RewriteBodyRequestWrapper().getReader()");
InputStreamReader isr = new InputStreamReader(getInputStream());
return new BufferedReader(isr);
}

}
class BufferedServletInputStream extends ServletInputStream  {

ByteArrayInputStream bais;

public BufferedServletInputStream(ByteArrayInputStream bais) {
this.bais = bais;
}

@Override
public int available() {
return bais.available();
}

@Override
public boolean isFinished() {
return bais.available() == 0;
}

@Override
public boolean isReady() {
return true;
}

@Override
public void setReadListener(ReadListener readListener) {
return;
}

@Override
public int read() {
return bais.read();
}

@Override
public int read(byte[] buf) {
return this.read(buf, 0, buf.length);
}

@Override
public int read(byte[] buf, int off, int len) {
return bais.read(buf, off, len);
}

}
}

web.xml code:
--------------------------------------------------------------------------------------------------
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
metadata-complete="true" version="3.0">

<display-name>Test Webapp Req Rewrite</display-name>

<!-- Change to "Production" when you are ready to deploy -->
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>

<!-- JSF mapping -->
<servlet>
<servlet-name>FacesServlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<!-- Map these files with JSF -->
<servlet-mapping>
<servlet-name>FacesServlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>

<!-- Session timeout -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>

<!-- Welcome page -->
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>

<!-- Filters -->
<filter>
<filter-name>RewriteBodyTestFilter</filter-name>
<filter-class>com.testwebapp.reqrewrite.filter.RewriteBodyTestFilter
</filter-class>
</filter>

<!-- Filter mappings / chain ordering -->
<filter-mapping>
<filter-name>RewriteBodyTestFilter</filter-name>
<servlet-name>FacesServlet</servlet-name>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>

</web-app>

pom.xml :
-------------------------------------------------------------------------------------
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.testwebapp.reqrewrite</groupId>
  <artifactId>reqrewrite</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>reqrewrite Maven Webapp</name>
  <url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.myfaces.core</groupId>
<artifactId>myfaces-api</artifactId>
<version>2.2.8</version>
</dependency>
<dependency>
<groupId>org.apache.myfaces.core</groupId>
<artifactId>myfaces-impl</artifactId>
<version>2.2.8</version>
</dependency>
</dependencies>
  <build>
    <finalName>reqrewrite</finalName>
  </build>
</project>

Re: Rewriting entire request /body in Servlet Filter not working as expected

Posted by Michael Greco <mi...@gmail.com>.
On Mon, Oct 5, 2015 at 3:30 AM, Mark Thomas <ma...@apache.org> wrote:

> On 04/10/2015 19:03, Michael Greco wrote:
> > First time post here.
> >
> > Using :
> > Tomcat 8.0.26
> > JDK1.8.0 update 51
> > Apache MyFaces 2.2.8.
> > Maven build of webapp war file
> > Chrome  45.0.2454.101 m 64 bit
> > Windows 7 64 bit
> >
> > Trying to rewrite the entire request body in a filter using a http
> request
> > wrapper.  Not entirely sure if this is the right approach or even
> possible
> > but researching similar examples it seems this approach should work.
> > Created a simple test case with a one page test app, expecting the
> > index.xhtml page's text value of  "Welcome" to be replaced with the text
> > "NEW TEXT HERE" found in the filter code, but I only get "Welcome" back
> > when the page is rendered in the browser.
> >
> > Below is what I have in the index page, the filter and the web.xml and
> the
> > pom.xml.  The reason I can see is that the getInputStream() or
> getReader()
> > calls are never called by the FacesServlet in the extended
> > HttpServletRequestWrapper.  I did override the getParameter function to
> put
> > some simple debug output in there to ensure the wrapped request was
> > actually used, so I know it's definitely getting into this class but not
> > calling the methods that would return it the new body content.
> >
> > I even tried to re-read the entire request in the getParametersMap() from
> > examples I found on the web, but this didn't seen to do the job either.
> It
> > seems that the request body is somehow cached somewhere / somehow and not
> > changable?
> >
> > Thanks in advance for any insight on this.
>
> First of all welcome files should be exactly that. Files. They should
> not include paths. There is no guarantee that 'faces/index.html' is
> going to work as you think it will and some containers may reject the
> configuration as invalid. That Tomcat lets you get away with this is
> fortunate and - now that I know about it - likely to change in future
> releases.
>
> You seem to be confusing reading the static resource from the file
> system and reading the request body. The request body is the data sent
> by the client with the HTTP request. The static resource (index.xhtml)
> is read from the file system.
>
> I assume that FacesServlet has its own static resource handling and that
> a request for /faces/index.xhtml is expected to return the content
> listed for index.html below.
>
> The FacesServlet will open an input stream but not to the request body.
> It will open one for the static resource on the file system. It will
> then copy those bytes to the response output stream. At no point will it
> attempt to read the request body (unless it is a POST request with
> parameters).
>
> If you want to modify the response body then you need to wrap the
> response and provide Writer and OutputStream implementations that make
> the modifications you require. The simplest way is to buffer the entire
> response body and then edit it but that can get expensive in terms of
> memory. It will be more efficient to use fixed buffer but then more
> complex to ensure your desired edits take place.
>
> Mark
>
> >
> > index page code (index.xhtml) :
> >
> ---------------------------------------------------------------------------------
> > <!DOCTYPE html>
> > <html lang="en"
> >   xmlns="http://www.w3.org/1999/xhtml"
> >   xmlns:jsf="http://xmlns.jcp.org/jsf"
> >   xmlns:h="http://xmlns.jcp.org/jsf/html">
> > <head jsf:id="head">
> > <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
> > <title>Test Webapp</title>
> > </head>
> > <body jsf:id="body">
> >     <div><h1>Welcome</h1></div>
> > </body>
> > </html>
> >
> > Filter code :
> >
> --------------------------------------------------------------------------
> > package com.testwebapp.reqrewrite.filter;
> >
> > import java.io.BufferedReader;
> > import java.io.ByteArrayInputStream;
> > import java.io.IOException;
> > import java.io.InputStreamReader;
> >
> > import javax.servlet.Filter;
> > import javax.servlet.FilterChain;
> > import javax.servlet.FilterConfig;
> > import javax.servlet.ReadListener;
> > import javax.servlet.ServletException;
> > import javax.servlet.ServletInputStream;
> > import javax.servlet.ServletRequest;
> > import javax.servlet.ServletResponse;
> > import javax.servlet.http.HttpServletRequest;
> > import javax.servlet.http.HttpServletRequestWrapper;
> >
> > public class RewriteBodyTestFilter implements Filter {
> >
> > public void init(FilterConfig filterConfig) throws ServletException {
> > return;
> > }
> >
> > public void doFilter(ServletRequest request, ServletResponse response,
> > FilterChain chain)
> > throws IOException, ServletException {
> > String newReqBody = new String("<div><h1>NEW TEXT HERE</h1></div>");
> > HttpServletRequest req = (HttpServletRequest)request;
> > RewriteBodyRequestWrapper rewriteBodyRequestWrapper = new
> > RewriteBodyRequestWrapper(req, newReqBody);
> > chain.doFilter(rewriteBodyRequestWrapper, response);
> > }
> >
> > public void destroy() {
> > return;
> > }
> > class RewriteBodyRequestWrapper extends HttpServletRequestWrapper {
> >
> > private byte[] buffer;
> >
> > public RewriteBodyRequestWrapper(HttpServletRequest req, String reqBody)
> > throws IOException {
> > super(req);
> > System.out.println("entering RewriteBodyRequestWrapper() constructor");
> > this.buffer = reqBody.getBytes();
> > }
> >
> > @Override
> > public ServletInputStream getInputStream() {
> > System.out.println("entering
> RewriteBodyRequestWrapper().getInputStream()");
> > try {
> > ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
> > BufferedServletInputStream bsis = new BufferedServletInputStream(bais);
> > return bsis;
> > } catch (Exception ex) {
> > ex.printStackTrace();
> > throw new RuntimeException(ex);
> > }
> > }
> >
> > @Override
> > public BufferedReader getReader() {
> > System.out.println("entering RewriteBodyRequestWrapper().getReader()");
> > InputStreamReader isr = new InputStreamReader(getInputStream());
> > return new BufferedReader(isr);
> > }
> >
> > }
> > class BufferedServletInputStream extends ServletInputStream  {
> >
> > ByteArrayInputStream bais;
> >
> > public BufferedServletInputStream(ByteArrayInputStream bais) {
> > this.bais = bais;
> > }
> >
> > @Override
> > public int available() {
> > return bais.available();
> > }
> >
> > @Override
> > public boolean isFinished() {
> > return bais.available() == 0;
> > }
> >
> > @Override
> > public boolean isReady() {
> > return true;
> > }
> >
> > @Override
> > public void setReadListener(ReadListener readListener) {
> > return;
> > }
> >
> > @Override
> > public int read() {
> > return bais.read();
> > }
> >
> > @Override
> > public int read(byte[] buf) {
> > return this.read(buf, 0, buf.length);
> > }
> >
> > @Override
> > public int read(byte[] buf, int off, int len) {
> > return bais.read(buf, off, len);
> > }
> >
> > }
> > }
> >
> > web.xml code:
> >
> --------------------------------------------------------------------------------------------------
> > <?xml version="1.0" encoding="ISO-8859-1"?>
> > <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="
> > http://www.w3.org/2001/XMLSchema-instance"
> > xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
> > http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
> > metadata-complete="true" version="3.0">
> >
> > <display-name>Test Webapp Req Rewrite</display-name>
> >
> > <!-- Change to "Production" when you are ready to deploy -->
> > <context-param>
> > <param-name>javax.faces.PROJECT_STAGE</param-name>
> > <param-value>Development</param-value>
> > </context-param>
> >
> > <!-- JSF mapping -->
> > <servlet>
> > <servlet-name>FacesServlet</servlet-name>
> > <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
> > <load-on-startup>1</load-on-startup>
> > </servlet>
> >
> > <!-- Map these files with JSF -->
> > <servlet-mapping>
> > <servlet-name>FacesServlet</servlet-name>
> > <url-pattern>/faces/*</url-pattern>
> > </servlet-mapping>
> >
> > <!-- Session timeout -->
> > <session-config>
> > <session-timeout>30</session-timeout>
> > </session-config>
> >
> > <!-- Welcome page -->
> > <welcome-file-list>
> > <welcome-file>faces/index.xhtml</welcome-file>
> > </welcome-file-list>
> >
> > <!-- Filters -->
> > <filter>
> > <filter-name>RewriteBodyTestFilter</filter-name>
> > <filter-class>com.testwebapp.reqrewrite.filter.RewriteBodyTestFilter
> > </filter-class>
> > </filter>
> >
> > <!-- Filter mappings / chain ordering -->
> > <filter-mapping>
> > <filter-name>RewriteBodyTestFilter</filter-name>
> > <servlet-name>FacesServlet</servlet-name>
> > <dispatcher>REQUEST</dispatcher>
> > <dispatcher>FORWARD</dispatcher>
> > </filter-mapping>
> >
> > </web-app>
> >
> > pom.xml :
> >
> -------------------------------------------------------------------------------------
> > <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="
> > http://www.w3.org/2001/XMLSchema-instance"
> >   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
> > http://maven.apache.org/maven-v4_0_0.xsd">
> >   <modelVersion>4.0.0</modelVersion>
> >   <groupId>com.testwebapp.reqrewrite</groupId>
> >   <artifactId>reqrewrite</artifactId>
> >   <packaging>war</packaging>
> >   <version>1.0-SNAPSHOT</version>
> >   <name>reqrewrite Maven Webapp</name>
> >   <url>http://maven.apache.org</url>
> > <dependencies>
> > <dependency>
> > <groupId>javax.servlet</groupId>
> > <artifactId>javax.servlet-api</artifactId>
> > <version>3.1.0</version>
> > <scope>provided</scope>
> > </dependency>
> > <dependency>
> > <groupId>org.apache.myfaces.core</groupId>
> > <artifactId>myfaces-api</artifactId>
> > <version>2.2.8</version>
> > </dependency>
> > <dependency>
> > <groupId>org.apache.myfaces.core</groupId>
> > <artifactId>myfaces-impl</artifactId>
> > <version>2.2.8</version>
> > </dependency>
> > </dependencies>
> >   <build>
> >     <finalName>reqrewrite</finalName>
> >   </build>
> > </project>
> >
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: users-help@tomcat.apache.org
>
>
Chris, Mark,

Thanks for your time and responses.  I'll try to respond to both of your
comments with this single bottom post.

I created this one page webapp to simply test the ability to modify the
request object's content.  In this example I only have one page and wanted
it to go through the FacesServlet therefore "faking" its url pattern in the
web.xml's welcome file element triggers the FacesServlet execution path and
therefore my filter as well.  Maybe I should have created a more "static"
index.html page and then placed a meta refresh tag in the page to redirect
to a separate faces/index.xhtml page instead but this is just an experiment
with trying to modify or change with the request content.

One of the use cases described in the Java EE 6 documentation is to do just
that (modify the request data), so I was hoping I could but it doesn't seem
to be possible in this particular chain setup.  (See
http://docs.oracle.com/javaee/6/tutorial/doc/bnagb.html in case I'm
confused).  I think this referenced tutorial translates to modifying just
the request headers, attributes and parameters only if you don't have
control over what the Servlet is going to do with the inputStream after
your filter(s) executes.  So in this simplified test webapp the request
inputStream definitely contains the index.xhtml content when i'm in my
filter, a simple request wrapper to buffer and display it shows this (I
tried that already), so there's no doubt in my mind it's coming in and
available to at least read in the filter via the stream.  I could probably
just as well have used my own Servlet in this example (I can try this and
post it) but if I'm in complete control at the Servlet level I could just
pull in the content there and rewrite the response as Mark mentioned.
Unfortunately beyond this simple test case I would be using JSF so I wanted
to be sure I can change the content in components I can control (the
filter, not the Servlet).

Outside this test case, the "real" use case/experiment was where I was
trying to weave together several technologies (without using Spring) in
this order via the web.xml filter chain:

URLRewriteFilter (http://tuckey.org/urlrewrite/) ==> Custom Thymeleaf
Filter (http://www.thymeleaf.org/) ==> JSF Servlet (MyFaces or any other
implementation, didn't matter).

So the URLRewriteFilter would forward a page request outside the WEB-INF
folder inward to a page within WEB-INF.  Then the Thymeleaf Filter would
read the request inputStream through it's templating engine and produce the
appropriate HTML5 page without the Thymeleaf tags (at this point the bigger
test case was working) and then finally the FacesServlet would process the
remaining jsf: tags before returning the response (failed here too, the
response was basically the original request input).  So unfortunately I ran
into this same issue, where the FacesServlet does take in my wrapped
request object but since the request inputStream is either not executed by
the FacesServlet logic and it simply goes the filesystem to get the input
stream as Mark mentioned... or... there is an "indirect" call to the
wrapper superclass' getInputStream or getReader through for example
getParameterMap() on the superclass in which case my custom wrapper
subclass overrides to getInputStream() or getReader() never have a chance
to execute.

Thanks for the "input stream" everyone, but it appears this test case is a
fail for me.  But I did learn a lot so definitely not all bad.  Again,
thanks for your time and I hope to try to post more stuff here.  Great
resources!

Re: Rewriting entire request /body in Servlet Filter not working as expected

Posted by Mark Thomas <ma...@apache.org>.
On 04/10/2015 19:03, Michael Greco wrote:
> First time post here.
> 
> Using :
> Tomcat 8.0.26
> JDK1.8.0 update 51
> Apache MyFaces 2.2.8.
> Maven build of webapp war file
> Chrome  45.0.2454.101 m 64 bit
> Windows 7 64 bit
> 
> Trying to rewrite the entire request body in a filter using a http request
> wrapper.  Not entirely sure if this is the right approach or even possible
> but researching similar examples it seems this approach should work.
> Created a simple test case with a one page test app, expecting the
> index.xhtml page's text value of  "Welcome" to be replaced with the text
> "NEW TEXT HERE" found in the filter code, but I only get "Welcome" back
> when the page is rendered in the browser.
> 
> Below is what I have in the index page, the filter and the web.xml and the
> pom.xml.  The reason I can see is that the getInputStream() or getReader()
> calls are never called by the FacesServlet in the extended
> HttpServletRequestWrapper.  I did override the getParameter function to put
> some simple debug output in there to ensure the wrapped request was
> actually used, so I know it's definitely getting into this class but not
> calling the methods that would return it the new body content.
> 
> I even tried to re-read the entire request in the getParametersMap() from
> examples I found on the web, but this didn't seen to do the job either.  It
> seems that the request body is somehow cached somewhere / somehow and not
> changable?
> 
> Thanks in advance for any insight on this.

First of all welcome files should be exactly that. Files. They should
not include paths. There is no guarantee that 'faces/index.html' is
going to work as you think it will and some containers may reject the
configuration as invalid. That Tomcat lets you get away with this is
fortunate and - now that I know about it - likely to change in future
releases.

You seem to be confusing reading the static resource from the file
system and reading the request body. The request body is the data sent
by the client with the HTTP request. The static resource (index.xhtml)
is read from the file system.

I assume that FacesServlet has its own static resource handling and that
a request for /faces/index.xhtml is expected to return the content
listed for index.html below.

The FacesServlet will open an input stream but not to the request body.
It will open one for the static resource on the file system. It will
then copy those bytes to the response output stream. At no point will it
attempt to read the request body (unless it is a POST request with
parameters).

If you want to modify the response body then you need to wrap the
response and provide Writer and OutputStream implementations that make
the modifications you require. The simplest way is to buffer the entire
response body and then edit it but that can get expensive in terms of
memory. It will be more efficient to use fixed buffer but then more
complex to ensure your desired edits take place.

Mark

> 
> index page code (index.xhtml) :
> ---------------------------------------------------------------------------------
> <!DOCTYPE html>
> <html lang="en"
>   xmlns="http://www.w3.org/1999/xhtml"
>   xmlns:jsf="http://xmlns.jcp.org/jsf"
>   xmlns:h="http://xmlns.jcp.org/jsf/html">
> <head jsf:id="head">
> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
> <title>Test Webapp</title>
> </head>
> <body jsf:id="body">
>     <div><h1>Welcome</h1></div>
> </body>
> </html>
> 
> Filter code :
> --------------------------------------------------------------------------
> package com.testwebapp.reqrewrite.filter;
> 
> import java.io.BufferedReader;
> import java.io.ByteArrayInputStream;
> import java.io.IOException;
> import java.io.InputStreamReader;
> 
> import javax.servlet.Filter;
> import javax.servlet.FilterChain;
> import javax.servlet.FilterConfig;
> import javax.servlet.ReadListener;
> import javax.servlet.ServletException;
> import javax.servlet.ServletInputStream;
> import javax.servlet.ServletRequest;
> import javax.servlet.ServletResponse;
> import javax.servlet.http.HttpServletRequest;
> import javax.servlet.http.HttpServletRequestWrapper;
> 
> public class RewriteBodyTestFilter implements Filter {
> 
> public void init(FilterConfig filterConfig) throws ServletException {
> return;
> }
> 
> public void doFilter(ServletRequest request, ServletResponse response,
> FilterChain chain)
> throws IOException, ServletException {
> String newReqBody = new String("<div><h1>NEW TEXT HERE</h1></div>");
> HttpServletRequest req = (HttpServletRequest)request;
> RewriteBodyRequestWrapper rewriteBodyRequestWrapper = new
> RewriteBodyRequestWrapper(req, newReqBody);
> chain.doFilter(rewriteBodyRequestWrapper, response);
> }
> 
> public void destroy() {
> return;
> }
> class RewriteBodyRequestWrapper extends HttpServletRequestWrapper {
> 
> private byte[] buffer;
> 
> public RewriteBodyRequestWrapper(HttpServletRequest req, String reqBody)
> throws IOException {
> super(req);
> System.out.println("entering RewriteBodyRequestWrapper() constructor");
> this.buffer = reqBody.getBytes();
> }
> 
> @Override
> public ServletInputStream getInputStream() {
> System.out.println("entering RewriteBodyRequestWrapper().getInputStream()");
> try {
> ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
> BufferedServletInputStream bsis = new BufferedServletInputStream(bais);
> return bsis;
> } catch (Exception ex) {
> ex.printStackTrace();
> throw new RuntimeException(ex);
> }
> }
> 
> @Override
> public BufferedReader getReader() {
> System.out.println("entering RewriteBodyRequestWrapper().getReader()");
> InputStreamReader isr = new InputStreamReader(getInputStream());
> return new BufferedReader(isr);
> }
> 
> }
> class BufferedServletInputStream extends ServletInputStream  {
> 
> ByteArrayInputStream bais;
> 
> public BufferedServletInputStream(ByteArrayInputStream bais) {
> this.bais = bais;
> }
> 
> @Override
> public int available() {
> return bais.available();
> }
> 
> @Override
> public boolean isFinished() {
> return bais.available() == 0;
> }
> 
> @Override
> public boolean isReady() {
> return true;
> }
> 
> @Override
> public void setReadListener(ReadListener readListener) {
> return;
> }
> 
> @Override
> public int read() {
> return bais.read();
> }
> 
> @Override
> public int read(byte[] buf) {
> return this.read(buf, 0, buf.length);
> }
> 
> @Override
> public int read(byte[] buf, int off, int len) {
> return bais.read(buf, off, len);
> }
> 
> }
> }
> 
> web.xml code:
> --------------------------------------------------------------------------------------------------
> <?xml version="1.0" encoding="ISO-8859-1"?>
> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="
> http://www.w3.org/2001/XMLSchema-instance"
> xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
> http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
> metadata-complete="true" version="3.0">
> 
> <display-name>Test Webapp Req Rewrite</display-name>
> 
> <!-- Change to "Production" when you are ready to deploy -->
> <context-param>
> <param-name>javax.faces.PROJECT_STAGE</param-name>
> <param-value>Development</param-value>
> </context-param>
> 
> <!-- JSF mapping -->
> <servlet>
> <servlet-name>FacesServlet</servlet-name>
> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
> <load-on-startup>1</load-on-startup>
> </servlet>
> 
> <!-- Map these files with JSF -->
> <servlet-mapping>
> <servlet-name>FacesServlet</servlet-name>
> <url-pattern>/faces/*</url-pattern>
> </servlet-mapping>
> 
> <!-- Session timeout -->
> <session-config>
> <session-timeout>30</session-timeout>
> </session-config>
> 
> <!-- Welcome page -->
> <welcome-file-list>
> <welcome-file>faces/index.xhtml</welcome-file>
> </welcome-file-list>
> 
> <!-- Filters -->
> <filter>
> <filter-name>RewriteBodyTestFilter</filter-name>
> <filter-class>com.testwebapp.reqrewrite.filter.RewriteBodyTestFilter
> </filter-class>
> </filter>
> 
> <!-- Filter mappings / chain ordering -->
> <filter-mapping>
> <filter-name>RewriteBodyTestFilter</filter-name>
> <servlet-name>FacesServlet</servlet-name>
> <dispatcher>REQUEST</dispatcher>
> <dispatcher>FORWARD</dispatcher>
> </filter-mapping>
> 
> </web-app>
> 
> pom.xml :
> -------------------------------------------------------------------------------------
> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="
> http://www.w3.org/2001/XMLSchema-instance"
>   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
> http://maven.apache.org/maven-v4_0_0.xsd">
>   <modelVersion>4.0.0</modelVersion>
>   <groupId>com.testwebapp.reqrewrite</groupId>
>   <artifactId>reqrewrite</artifactId>
>   <packaging>war</packaging>
>   <version>1.0-SNAPSHOT</version>
>   <name>reqrewrite Maven Webapp</name>
>   <url>http://maven.apache.org</url>
> <dependencies>
> <dependency>
> <groupId>javax.servlet</groupId>
> <artifactId>javax.servlet-api</artifactId>
> <version>3.1.0</version>
> <scope>provided</scope>
> </dependency>
> <dependency>
> <groupId>org.apache.myfaces.core</groupId>
> <artifactId>myfaces-api</artifactId>
> <version>2.2.8</version>
> </dependency>
> <dependency>
> <groupId>org.apache.myfaces.core</groupId>
> <artifactId>myfaces-impl</artifactId>
> <version>2.2.8</version>
> </dependency>
> </dependencies>
>   <build>
>     <finalName>reqrewrite</finalName>
>   </build>
> </project>
> 


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Re: Rewriting entire request /body in Servlet Filter not working as expected

Posted by Michael Greco <mi...@gmail.com>.
On Sun, Oct 4, 2015 at 7:25 PM, Michael Greco <mi...@gmail.com>
wrote:

> On Sun, Oct 4, 2015 at 2:03 PM, Michael Greco <mi...@gmail.com>
> wrote:
>
>> First time post here.
>>
>> Using :
>> Tomcat 8.0.26
>> JDK1.8.0 update 51
>> Apache MyFaces 2.2.8.
>> Maven build of webapp war file
>> Chrome  45.0.2454.101 m 64 bit
>> Windows 7 64 bit
>>
>> Trying to rewrite the entire request body in a filter using a http
>> request wrapper.  Not entirely sure if this is the right approach or even
>> possible but researching similar examples it seems this approach should
>> work.  Created a simple test case with a one page test app, expecting the
>> index.xhtml page's text value of  "Welcome" to be replaced with the text
>> "NEW TEXT HERE" found in the filter code, but I only get "Welcome" back
>> when the page is rendered in the browser.
>>
>> Below is what I have in the index page, the filter and the web.xml and
>> the pom.xml.  The reason I can see is that the getInputStream() or
>> getReader() calls are never called by the FacesServlet in the extended
>> HttpServletRequestWrapper.  I did override the getParameter function to put
>> some simple debug output in there to ensure the wrapped request was
>> actually used, so I know it's definitely getting into this class but not
>> calling the methods that would return it the new body content.
>>
>> I even tried to re-read the entire request in the getParametersMap() from
>> examples I found on the web, but this didn't seen to do the job either.  It
>> seems that the request body is somehow cached somewhere / somehow and not
>> changable?
>>
>> Thanks in advance for any insight on this.
>>
>> index page code (index.xhtml) :
>>
>> ---------------------------------------------------------------------------------
>> <!DOCTYPE html>
>> <html lang="en"
>>   xmlns="http://www.w3.org/1999/xhtml"
>>   xmlns:jsf="http://xmlns.jcp.org/jsf"
>>   xmlns:h="http://xmlns.jcp.org/jsf/html">
>> <head jsf:id="head">
>> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
>> <title>Test Webapp</title>
>> </head>
>> <body jsf:id="body">
>>     <div><h1>Welcome</h1></div>
>> </body>
>> </html>
>>
>> Filter code :
>> --------------------------------------------------------------------------
>> package com.testwebapp.reqrewrite.filter;
>>
>> import java.io.BufferedReader;
>> import java.io.ByteArrayInputStream;
>> import java.io.IOException;
>> import java.io.InputStreamReader;
>>
>> import javax.servlet.Filter;
>> import javax.servlet.FilterChain;
>> import javax.servlet.FilterConfig;
>> import javax.servlet.ReadListener;
>> import javax.servlet.ServletException;
>> import javax.servlet.ServletInputStream;
>> import javax.servlet.ServletRequest;
>> import javax.servlet.ServletResponse;
>> import javax.servlet.http.HttpServletRequest;
>> import javax.servlet.http.HttpServletRequestWrapper;
>>
>> public class RewriteBodyTestFilter implements Filter {
>>
>> public void init(FilterConfig filterConfig) throws ServletException {
>> return;
>> }
>>
>> public void doFilter(ServletRequest request, ServletResponse response,
>> FilterChain chain)
>> throws IOException, ServletException {
>> String newReqBody = new String("<div><h1>NEW TEXT HERE</h1></div>");
>> HttpServletRequest req = (HttpServletRequest)request;
>> RewriteBodyRequestWrapper rewriteBodyRequestWrapper = new
>> RewriteBodyRequestWrapper(req, newReqBody);
>> chain.doFilter(rewriteBodyRequestWrapper, response);
>> }
>>
>> public void destroy() {
>> return;
>> }
>> class RewriteBodyRequestWrapper extends HttpServletRequestWrapper {
>>
>> private byte[] buffer;
>>
>> public RewriteBodyRequestWrapper(HttpServletRequest req, String reqBody)
>> throws IOException {
>> super(req);
>> System.out.println("entering RewriteBodyRequestWrapper() constructor");
>> this.buffer = reqBody.getBytes();
>> }
>>
>> @Override
>> public ServletInputStream getInputStream() {
>> System.out.println("entering
>> RewriteBodyRequestWrapper().getInputStream()");
>> try {
>> ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
>> BufferedServletInputStream bsis = new BufferedServletInputStream(bais);
>> return bsis;
>> } catch (Exception ex) {
>> ex.printStackTrace();
>> throw new RuntimeException(ex);
>> }
>> }
>>
>> @Override
>> public BufferedReader getReader() {
>> System.out.println("entering RewriteBodyRequestWrapper().getReader()");
>> InputStreamReader isr = new InputStreamReader(getInputStream());
>> return new BufferedReader(isr);
>> }
>>
>> }
>> class BufferedServletInputStream extends ServletInputStream  {
>>
>> ByteArrayInputStream bais;
>>
>> public BufferedServletInputStream(ByteArrayInputStream bais) {
>> this.bais = bais;
>> }
>>
>> @Override
>> public int available() {
>> return bais.available();
>> }
>>
>> @Override
>> public boolean isFinished() {
>> return bais.available() == 0;
>> }
>>
>> @Override
>> public boolean isReady() {
>> return true;
>> }
>>
>> @Override
>> public void setReadListener(ReadListener readListener) {
>> return;
>> }
>>
>> @Override
>> public int read() {
>> return bais.read();
>> }
>>
>> @Override
>> public int read(byte[] buf) {
>> return this.read(buf, 0, buf.length);
>> }
>>
>> @Override
>> public int read(byte[] buf, int off, int len) {
>> return bais.read(buf, off, len);
>> }
>>
>> }
>> }
>>
>> web.xml code:
>>
>> --------------------------------------------------------------------------------------------------
>> <?xml version="1.0" encoding="ISO-8859-1"?>
>> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="
>> http://www.w3.org/2001/XMLSchema-instance"
>> xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
>> http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
>> metadata-complete="true" version="3.0">
>>
>> <display-name>Test Webapp Req Rewrite</display-name>
>>
>> <!-- Change to "Production" when you are ready to deploy -->
>> <context-param>
>> <param-name>javax.faces.PROJECT_STAGE</param-name>
>> <param-value>Development</param-value>
>> </context-param>
>>
>> <!-- JSF mapping -->
>> <servlet>
>> <servlet-name>FacesServlet</servlet-name>
>> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
>> <load-on-startup>1</load-on-startup>
>> </servlet>
>>
>> <!-- Map these files with JSF -->
>> <servlet-mapping>
>> <servlet-name>FacesServlet</servlet-name>
>> <url-pattern>/faces/*</url-pattern>
>> </servlet-mapping>
>>
>> <!-- Session timeout -->
>> <session-config>
>> <session-timeout>30</session-timeout>
>> </session-config>
>>
>> <!-- Welcome page -->
>> <welcome-file-list>
>> <welcome-file>faces/index.xhtml</welcome-file>
>> </welcome-file-list>
>>
>> <!-- Filters -->
>> <filter>
>> <filter-name>RewriteBodyTestFilter</filter-name>
>> <filter-class>com.testwebapp.reqrewrite.filter.RewriteBodyTestFilter
>> </filter-class>
>> </filter>
>>
>> <!-- Filter mappings / chain ordering -->
>> <filter-mapping>
>> <filter-name>RewriteBodyTestFilter</filter-name>
>> <servlet-name>FacesServlet</servlet-name>
>> <dispatcher>REQUEST</dispatcher>
>> <dispatcher>FORWARD</dispatcher>
>> </filter-mapping>
>>
>> </web-app>
>>
>> pom.xml :
>>
>> -------------------------------------------------------------------------------------
>> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="
>> http://www.w3.org/2001/XMLSchema-instance"
>>   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
>> http://maven.apache.org/maven-v4_0_0.xsd">
>>   <modelVersion>4.0.0</modelVersion>
>>   <groupId>com.testwebapp.reqrewrite</groupId>
>>   <artifactId>reqrewrite</artifactId>
>>   <packaging>war</packaging>
>>   <version>1.0-SNAPSHOT</version>
>>   <name>reqrewrite Maven Webapp</name>
>>   <url>http://maven.apache.org</url>
>> <dependencies>
>> <dependency>
>> <groupId>javax.servlet</groupId>
>> <artifactId>javax.servlet-api</artifactId>
>> <version>3.1.0</version>
>> <scope>provided</scope>
>> </dependency>
>> <dependency>
>> <groupId>org.apache.myfaces.core</groupId>
>> <artifactId>myfaces-api</artifactId>
>> <version>2.2.8</version>
>> </dependency>
>> <dependency>
>> <groupId>org.apache.myfaces.core</groupId>
>> <artifactId>myfaces-impl</artifactId>
>> <version>2.2.8</version>
>> </dependency>
>> </dependencies>
>>   <build>
>>     <finalName>reqrewrite</finalName>
>>   </build>
>> </project>
>>
>>
>> I think I might know what is going on now.  A specific
> HttpServletRequestWrapper superclass method is being invoked first
> (something other than getInputStream() and getReader()) and my overridden
> methods getInputStream() and getReader() of my subclass are not being
> invoked because of this, which leaves me to having to figure out this first
> method invoked in the FacesServlet execution path that later invokes
> getInputStream() and getReader() and then override this as well?  I really
> think it would be a bad idea for me to try to implement/override more of
> the container's request object methods beyond what I've done already.  Is
> there another design pattern here that I can utilize to get around this
> issue?  I would assume this issue probably varies from container to
> container depending on how the server implements the seeded request classes.
> (Sorry for the Top Post, and not sure if this next identical response will
> be Bottom Post until I try, using Gmail web client)
>
> Thanks
> Mike
>

Looking into the Tomcat 8.0.26 Javadocs and using a bit of reflection I see
I'm dealing with the following classes.  I was going to try a hack and use
reflection to reset the input stream, but then I thought otherwise cause
I'm sure that's probably just plain wrong.  I'm stumped on this one, it
seems folks have modified the "body" but I'm thinking they could have only
done this on the response back up the chain.  There appears to be no way to
do this, to modify the request body, from what I can see, going down the
chain to the servlet.

// org.apache.catalina.connector.RequestFacade
// org.apache.catalina.connector.Request
// org.apache.catalina.connector.CoyoteInputStream
// org.apache.catalina.connector.InputBuffer
// org.apache.coyote.Request (accessed by attribute
org.apache.tomcat.request by trusted apps)

Re: Rewriting entire request /body in Servlet Filter not working as expected

Posted by Michael Greco <mi...@gmail.com>.
On Sun, Oct 4, 2015 at 2:03 PM, Michael Greco <mi...@gmail.com>
wrote:

> First time post here.
>
> Using :
> Tomcat 8.0.26
> JDK1.8.0 update 51
> Apache MyFaces 2.2.8.
> Maven build of webapp war file
> Chrome  45.0.2454.101 m 64 bit
> Windows 7 64 bit
>
> Trying to rewrite the entire request body in a filter using a http request
> wrapper.  Not entirely sure if this is the right approach or even possible
> but researching similar examples it seems this approach should work.
> Created a simple test case with a one page test app, expecting the
> index.xhtml page's text value of  "Welcome" to be replaced with the text
> "NEW TEXT HERE" found in the filter code, but I only get "Welcome" back
> when the page is rendered in the browser.
>
> Below is what I have in the index page, the filter and the web.xml and the
> pom.xml.  The reason I can see is that the getInputStream() or getReader()
> calls are never called by the FacesServlet in the extended
> HttpServletRequestWrapper.  I did override the getParameter function to put
> some simple debug output in there to ensure the wrapped request was
> actually used, so I know it's definitely getting into this class but not
> calling the methods that would return it the new body content.
>
> I even tried to re-read the entire request in the getParametersMap() from
> examples I found on the web, but this didn't seen to do the job either.  It
> seems that the request body is somehow cached somewhere / somehow and not
> changable?
>
> Thanks in advance for any insight on this.
>
> index page code (index.xhtml) :
>
> ---------------------------------------------------------------------------------
> <!DOCTYPE html>
> <html lang="en"
>   xmlns="http://www.w3.org/1999/xhtml"
>   xmlns:jsf="http://xmlns.jcp.org/jsf"
>   xmlns:h="http://xmlns.jcp.org/jsf/html">
> <head jsf:id="head">
> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
> <title>Test Webapp</title>
> </head>
> <body jsf:id="body">
>     <div><h1>Welcome</h1></div>
> </body>
> </html>
>
> Filter code :
> --------------------------------------------------------------------------
> package com.testwebapp.reqrewrite.filter;
>
> import java.io.BufferedReader;
> import java.io.ByteArrayInputStream;
> import java.io.IOException;
> import java.io.InputStreamReader;
>
> import javax.servlet.Filter;
> import javax.servlet.FilterChain;
> import javax.servlet.FilterConfig;
> import javax.servlet.ReadListener;
> import javax.servlet.ServletException;
> import javax.servlet.ServletInputStream;
> import javax.servlet.ServletRequest;
> import javax.servlet.ServletResponse;
> import javax.servlet.http.HttpServletRequest;
> import javax.servlet.http.HttpServletRequestWrapper;
>
> public class RewriteBodyTestFilter implements Filter {
>
> public void init(FilterConfig filterConfig) throws ServletException {
> return;
> }
>
> public void doFilter(ServletRequest request, ServletResponse response,
> FilterChain chain)
> throws IOException, ServletException {
> String newReqBody = new String("<div><h1>NEW TEXT HERE</h1></div>");
> HttpServletRequest req = (HttpServletRequest)request;
> RewriteBodyRequestWrapper rewriteBodyRequestWrapper = new
> RewriteBodyRequestWrapper(req, newReqBody);
> chain.doFilter(rewriteBodyRequestWrapper, response);
> }
>
> public void destroy() {
> return;
> }
> class RewriteBodyRequestWrapper extends HttpServletRequestWrapper {
>
> private byte[] buffer;
>
> public RewriteBodyRequestWrapper(HttpServletRequest req, String reqBody)
> throws IOException {
> super(req);
> System.out.println("entering RewriteBodyRequestWrapper() constructor");
> this.buffer = reqBody.getBytes();
> }
>
> @Override
> public ServletInputStream getInputStream() {
> System.out.println("entering
> RewriteBodyRequestWrapper().getInputStream()");
> try {
> ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
> BufferedServletInputStream bsis = new BufferedServletInputStream(bais);
> return bsis;
> } catch (Exception ex) {
> ex.printStackTrace();
> throw new RuntimeException(ex);
> }
> }
>
> @Override
> public BufferedReader getReader() {
> System.out.println("entering RewriteBodyRequestWrapper().getReader()");
> InputStreamReader isr = new InputStreamReader(getInputStream());
> return new BufferedReader(isr);
> }
>
> }
> class BufferedServletInputStream extends ServletInputStream  {
>
> ByteArrayInputStream bais;
>
> public BufferedServletInputStream(ByteArrayInputStream bais) {
> this.bais = bais;
> }
>
> @Override
> public int available() {
> return bais.available();
> }
>
> @Override
> public boolean isFinished() {
> return bais.available() == 0;
> }
>
> @Override
> public boolean isReady() {
> return true;
> }
>
> @Override
> public void setReadListener(ReadListener readListener) {
> return;
> }
>
> @Override
> public int read() {
> return bais.read();
> }
>
> @Override
> public int read(byte[] buf) {
> return this.read(buf, 0, buf.length);
> }
>
> @Override
> public int read(byte[] buf, int off, int len) {
> return bais.read(buf, off, len);
> }
>
> }
> }
>
> web.xml code:
>
> --------------------------------------------------------------------------------------------------
> <?xml version="1.0" encoding="ISO-8859-1"?>
> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="
> http://www.w3.org/2001/XMLSchema-instance"
> xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
> http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
> metadata-complete="true" version="3.0">
>
> <display-name>Test Webapp Req Rewrite</display-name>
>
> <!-- Change to "Production" when you are ready to deploy -->
> <context-param>
> <param-name>javax.faces.PROJECT_STAGE</param-name>
> <param-value>Development</param-value>
> </context-param>
>
> <!-- JSF mapping -->
> <servlet>
> <servlet-name>FacesServlet</servlet-name>
> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
> <load-on-startup>1</load-on-startup>
> </servlet>
>
> <!-- Map these files with JSF -->
> <servlet-mapping>
> <servlet-name>FacesServlet</servlet-name>
> <url-pattern>/faces/*</url-pattern>
> </servlet-mapping>
>
> <!-- Session timeout -->
> <session-config>
> <session-timeout>30</session-timeout>
> </session-config>
>
> <!-- Welcome page -->
> <welcome-file-list>
> <welcome-file>faces/index.xhtml</welcome-file>
> </welcome-file-list>
>
> <!-- Filters -->
> <filter>
> <filter-name>RewriteBodyTestFilter</filter-name>
> <filter-class>com.testwebapp.reqrewrite.filter.RewriteBodyTestFilter
> </filter-class>
> </filter>
>
> <!-- Filter mappings / chain ordering -->
> <filter-mapping>
> <filter-name>RewriteBodyTestFilter</filter-name>
> <servlet-name>FacesServlet</servlet-name>
> <dispatcher>REQUEST</dispatcher>
> <dispatcher>FORWARD</dispatcher>
> </filter-mapping>
>
> </web-app>
>
> pom.xml :
>
> -------------------------------------------------------------------------------------
> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="
> http://www.w3.org/2001/XMLSchema-instance"
>   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
> http://maven.apache.org/maven-v4_0_0.xsd">
>   <modelVersion>4.0.0</modelVersion>
>   <groupId>com.testwebapp.reqrewrite</groupId>
>   <artifactId>reqrewrite</artifactId>
>   <packaging>war</packaging>
>   <version>1.0-SNAPSHOT</version>
>   <name>reqrewrite Maven Webapp</name>
>   <url>http://maven.apache.org</url>
> <dependencies>
> <dependency>
> <groupId>javax.servlet</groupId>
> <artifactId>javax.servlet-api</artifactId>
> <version>3.1.0</version>
> <scope>provided</scope>
> </dependency>
> <dependency>
> <groupId>org.apache.myfaces.core</groupId>
> <artifactId>myfaces-api</artifactId>
> <version>2.2.8</version>
> </dependency>
> <dependency>
> <groupId>org.apache.myfaces.core</groupId>
> <artifactId>myfaces-impl</artifactId>
> <version>2.2.8</version>
> </dependency>
> </dependencies>
>   <build>
>     <finalName>reqrewrite</finalName>
>   </build>
> </project>
>
>
> I think I might know what is going on now.  A specific
HttpServletRequestWrapper superclass method is being invoked first
(something other than getInputStream() and getReader()) and my overridden
methods getInputStream() and getReader() of my subclass are not being
invoked because of this, which leaves me to having to figure out this first
method invoked in the FacesServlet execution path that later invokes
getInputStream() and getReader() and then override this as well?  I really
think it would be a bad idea for me to try to implement/override more of
the container's request object methods beyond what I've done already.  Is
there another design pattern here that I can utilize to get around this
issue?  I would assume this issue probably varies from container to
container depending on how the server implements the seeded request classes.
(Sorry for the Top Post, and not sure if this next identical response will
be Bottom Post until I try, using Gmail web client)

Thanks
Mike

Re: Rewriting entire request /body in Servlet Filter not working as expected

Posted by Michael Greco <mi...@gmail.com>.
I think I might know what is going on now.  A specific
HttpServletRequestWrapper superclass method is being invoked first
(something other than getInputStream() and getReader()) and my overridden
methods getInputStream() and getReader() of my subclass are not being
invoked because of this, which leaves me to having to figure out this first
method invoked in the FacesServlet execution path that later invokes
getInputStream() and getReader() and then override this as well?  I really
think it would be a bad idea for me to try to implement/override more of
the container's request object methods beyond what I've done already.  Is
there another design pattern here that I can utilize to get around this
issue?  I would assume this issue probably varies from container to
container depending on how the server implements the seeded request classes.

On Sun, Oct 4, 2015 at 2:03 PM, Michael Greco <mi...@gmail.com>
wrote:

> First time post here.
>
> Using :
> Tomcat 8.0.26
> JDK1.8.0 update 51
> Apache MyFaces 2.2.8.
> Maven build of webapp war file
> Chrome  45.0.2454.101 m 64 bit
> Windows 7 64 bit
>
> Trying to rewrite the entire request body in a filter using a http request
> wrapper.  Not entirely sure if this is the right approach or even possible
> but researching similar examples it seems this approach should work.
> Created a simple test case with a one page test app, expecting the
> index.xhtml page's text value of  "Welcome" to be replaced with the text
> "NEW TEXT HERE" found in the filter code, but I only get "Welcome" back
> when the page is rendered in the browser.
>
> Below is what I have in the index page, the filter and the web.xml and the
> pom.xml.  The reason I can see is that the getInputStream() or getReader()
> calls are never called by the FacesServlet in the extended
> HttpServletRequestWrapper.  I did override the getParameter function to put
> some simple debug output in there to ensure the wrapped request was
> actually used, so I know it's definitely getting into this class but not
> calling the methods that would return it the new body content.
>
> I even tried to re-read the entire request in the getParametersMap() from
> examples I found on the web, but this didn't seen to do the job either.  It
> seems that the request body is somehow cached somewhere / somehow and not
> changable?
>
> Thanks in advance for any insight on this.
>
> index page code (index.xhtml) :
>
> ---------------------------------------------------------------------------------
> <!DOCTYPE html>
> <html lang="en"
>   xmlns="http://www.w3.org/1999/xhtml"
>   xmlns:jsf="http://xmlns.jcp.org/jsf"
>   xmlns:h="http://xmlns.jcp.org/jsf/html">
> <head jsf:id="head">
> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
> <title>Test Webapp</title>
> </head>
> <body jsf:id="body">
>     <div><h1>Welcome</h1></div>
> </body>
> </html>
>
> Filter code :
> --------------------------------------------------------------------------
> package com.testwebapp.reqrewrite.filter;
>
> import java.io.BufferedReader;
> import java.io.ByteArrayInputStream;
> import java.io.IOException;
> import java.io.InputStreamReader;
>
> import javax.servlet.Filter;
> import javax.servlet.FilterChain;
> import javax.servlet.FilterConfig;
> import javax.servlet.ReadListener;
> import javax.servlet.ServletException;
> import javax.servlet.ServletInputStream;
> import javax.servlet.ServletRequest;
> import javax.servlet.ServletResponse;
> import javax.servlet.http.HttpServletRequest;
> import javax.servlet.http.HttpServletRequestWrapper;
>
> public class RewriteBodyTestFilter implements Filter {
>
> public void init(FilterConfig filterConfig) throws ServletException {
> return;
> }
>
> public void doFilter(ServletRequest request, ServletResponse response,
> FilterChain chain)
> throws IOException, ServletException {
> String newReqBody = new String("<div><h1>NEW TEXT HERE</h1></div>");
> HttpServletRequest req = (HttpServletRequest)request;
> RewriteBodyRequestWrapper rewriteBodyRequestWrapper = new
> RewriteBodyRequestWrapper(req, newReqBody);
> chain.doFilter(rewriteBodyRequestWrapper, response);
> }
>
> public void destroy() {
> return;
> }
> class RewriteBodyRequestWrapper extends HttpServletRequestWrapper {
>
> private byte[] buffer;
>
> public RewriteBodyRequestWrapper(HttpServletRequest req, String reqBody)
> throws IOException {
> super(req);
> System.out.println("entering RewriteBodyRequestWrapper() constructor");
> this.buffer = reqBody.getBytes();
> }
>
> @Override
> public ServletInputStream getInputStream() {
> System.out.println("entering
> RewriteBodyRequestWrapper().getInputStream()");
> try {
> ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
> BufferedServletInputStream bsis = new BufferedServletInputStream(bais);
> return bsis;
> } catch (Exception ex) {
> ex.printStackTrace();
> throw new RuntimeException(ex);
> }
> }
>
> @Override
> public BufferedReader getReader() {
> System.out.println("entering RewriteBodyRequestWrapper().getReader()");
> InputStreamReader isr = new InputStreamReader(getInputStream());
> return new BufferedReader(isr);
> }
>
> }
> class BufferedServletInputStream extends ServletInputStream  {
>
> ByteArrayInputStream bais;
>
> public BufferedServletInputStream(ByteArrayInputStream bais) {
> this.bais = bais;
> }
>
> @Override
> public int available() {
> return bais.available();
> }
>
> @Override
> public boolean isFinished() {
> return bais.available() == 0;
> }
>
> @Override
> public boolean isReady() {
> return true;
> }
>
> @Override
> public void setReadListener(ReadListener readListener) {
> return;
> }
>
> @Override
> public int read() {
> return bais.read();
> }
>
> @Override
> public int read(byte[] buf) {
> return this.read(buf, 0, buf.length);
> }
>
> @Override
> public int read(byte[] buf, int off, int len) {
> return bais.read(buf, off, len);
> }
>
> }
> }
>
> web.xml code:
>
> --------------------------------------------------------------------------------------------------
> <?xml version="1.0" encoding="ISO-8859-1"?>
> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="
> http://www.w3.org/2001/XMLSchema-instance"
> xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
> http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
> metadata-complete="true" version="3.0">
>
> <display-name>Test Webapp Req Rewrite</display-name>
>
> <!-- Change to "Production" when you are ready to deploy -->
> <context-param>
> <param-name>javax.faces.PROJECT_STAGE</param-name>
> <param-value>Development</param-value>
> </context-param>
>
> <!-- JSF mapping -->
> <servlet>
> <servlet-name>FacesServlet</servlet-name>
> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
> <load-on-startup>1</load-on-startup>
> </servlet>
>
> <!-- Map these files with JSF -->
> <servlet-mapping>
> <servlet-name>FacesServlet</servlet-name>
> <url-pattern>/faces/*</url-pattern>
> </servlet-mapping>
>
> <!-- Session timeout -->
> <session-config>
> <session-timeout>30</session-timeout>
> </session-config>
>
> <!-- Welcome page -->
> <welcome-file-list>
> <welcome-file>faces/index.xhtml</welcome-file>
> </welcome-file-list>
>
> <!-- Filters -->
> <filter>
> <filter-name>RewriteBodyTestFilter</filter-name>
> <filter-class>com.testwebapp.reqrewrite.filter.RewriteBodyTestFilter
> </filter-class>
> </filter>
>
> <!-- Filter mappings / chain ordering -->
> <filter-mapping>
> <filter-name>RewriteBodyTestFilter</filter-name>
> <servlet-name>FacesServlet</servlet-name>
> <dispatcher>REQUEST</dispatcher>
> <dispatcher>FORWARD</dispatcher>
> </filter-mapping>
>
> </web-app>
>
> pom.xml :
>
> -------------------------------------------------------------------------------------
> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="
> http://www.w3.org/2001/XMLSchema-instance"
>   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
> http://maven.apache.org/maven-v4_0_0.xsd">
>   <modelVersion>4.0.0</modelVersion>
>   <groupId>com.testwebapp.reqrewrite</groupId>
>   <artifactId>reqrewrite</artifactId>
>   <packaging>war</packaging>
>   <version>1.0-SNAPSHOT</version>
>   <name>reqrewrite Maven Webapp</name>
>   <url>http://maven.apache.org</url>
> <dependencies>
> <dependency>
> <groupId>javax.servlet</groupId>
> <artifactId>javax.servlet-api</artifactId>
> <version>3.1.0</version>
> <scope>provided</scope>
> </dependency>
> <dependency>
> <groupId>org.apache.myfaces.core</groupId>
> <artifactId>myfaces-api</artifactId>
> <version>2.2.8</version>
> </dependency>
> <dependency>
> <groupId>org.apache.myfaces.core</groupId>
> <artifactId>myfaces-impl</artifactId>
> <version>2.2.8</version>
> </dependency>
> </dependencies>
>   <build>
>     <finalName>reqrewrite</finalName>
>   </build>
> </project>
>
>
>

Re: Rewriting entire request /body in Servlet Filter not working as expected

Posted by Christopher Schultz <ch...@christopherschultz.net>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Michael,

On 10/4/15 2:03 PM, Michael Greco wrote:
> First time post here.

Welcome!

> Using : Tomcat 8.0.26 JDK1.8.0 update 51 Apache MyFaces 2.2.8. 
> Maven build of webapp war file Chrome  45.0.2454.101 m 64 bit 
> Windows 7 64 bit

Thanks.

> Trying to rewrite the entire request body in a filter using a http
> request wrapper.  Not entirely sure if this is the right approach
> or even possible but researching similar examples it seems this
> approach should work. Created a simple test case with a one page
> test app, expecting the index.xhtml page's text value of  "Welcome"
> to be replaced with the text "NEW TEXT HERE" found in the filter
> code, but I only get "Welcome" back when the page is rendered in
> the browser.

I'm curious about your use-case. Why would you want to re-write the
user's request?

> I even tried to re-read the entire request in the
> getParametersMap() from examples I found on the web, but this
> didn't seen to do the job either.  It seems that the request body
> is somehow cached somewhere / somehow and not changable?

Once Tomcat has consumed the request body -- like for a POST where
some component has called any "getParameter" method on the request --
then Tomcat will return the previously-parsed data. If you don't
re-write the request before then, you'll have no effect, unless the
servlet itself reads the request... which will probably work.

> index page code (index.xhtml) : 
> ----------------------------------------------------------------------
- -----------
>
> 
<!DOCTYPE html>
> <html lang="en" xmlns="http://www.w3.org/1999/xhtml" 
> xmlns:jsf="http://xmlns.jcp.org/jsf" 
> xmlns:h="http://xmlns.jcp.org/jsf/html"> <head jsf:id="head"> <meta
> http-equiv="Content-Type" content="text/html; charset=UTF-8"/> 
> <title>Test Webapp</title> </head> <body jsf:id="body"> 
> <div><h1>Welcome</h1></div> </body> </html>

I'm pretty sure the above is going to display "Welcome" no matter what
your Filter does (as long as it doesn't crash or return without
calling down the chain). What were you expecting?

> public void doFilter(ServletRequest request, ServletResponse
> response, FilterChain chain) throws IOException, ServletException
> { String newReqBody = new String("<div><h1>NEW TEXT
> HERE</h1></div>");
> 
> HttpServletRequest req = (HttpServletRequest)request; 
> RewriteBodyRequestWrapper rewriteBodyRequestWrapper = new 
> RewriteBodyRequestWrapper(req, newReqBody);
> 
> chain.doFilter(rewriteBodyRequestWrapper, response); }

[snip]

> class RewriteBodyRequestWrapper extends HttpServletRequestWrapper
> {
> 
> private byte[] buffer;
> 
> public RewriteBodyRequestWrapper(HttpServletRequest req, String
> reqBody) throws IOException { super(req); 
> System.out.println("entering RewriteBodyRequestWrapper()
> constructor"); this.buffer = reqBody.getBytes(); }

You might want to make sure the encoding is right on that getBytes()
call. It's got nothing to do with whatever problem you're having, but
good to think about for the future.

> @Override public ServletInputStream getInputStream() { 
> System.out.println("entering
> RewriteBodyRequestWrapper().getInputStream()"); try { 
> ByteArrayInputStream bais = new ByteArrayInputStream(buffer); 
> BufferedServletInputStream bsis = new
> BufferedServletInputStream(bais); return bsis; } catch (Exception
> ex) { ex.printStackTrace(); throw new RuntimeException(ex); } }
> 
> @Override public BufferedReader getReader() { 
> System.out.println("entering
> RewriteBodyRequestWrapper().getReader()"); InputStreamReader isr =
> new InputStreamReader(getInputStream()); return new
> BufferedReader(isr); }

I think that's about the best you can do. Just make sure that the
Filter is the first possible Filter to run (i.e. is mapped topmost in
web.xml).

> web.xml code: 
> ----------------------------------------------------------------------
- ----------------------------
>
> 
<?xml version="1.0" encoding="ISO-8859-1"?>
> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi=" 
> http://www.w3.org/2001/XMLSchema-instance" 
> xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
> http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
> metadata-complete="true" version="3.0">
> 
> <display-name>Test Webapp Req Rewrite</display-name>
> 
> <!-- Change to "Production" when you are ready to deploy --> 
> <context-param> <param-name>javax.faces.PROJECT_STAGE</param-name> 
> <param-value>Development</param-value> </context-param>
> 
> <!-- JSF mapping --> <servlet> 
> <servlet-name>FacesServlet</servlet-name> 
> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> 
> <load-on-startup>1</load-on-startup> </servlet>
> 
> <!-- Map these files with JSF --> <servlet-mapping> 
> <servlet-name>FacesServlet</servlet-name> 
> <url-pattern>/faces/*</url-pattern> </servlet-mapping>
> 
> <!-- Session timeout --> <session-config> 
> <session-timeout>30</session-timeout> </session-config>
> 
> <!-- Welcome page --> <welcome-file-list> 
> <welcome-file>faces/index.xhtml</welcome-file> 
> </welcome-file-list>
> 
> <!-- Filters --> <filter> 
> <filter-name>RewriteBodyTestFilter</filter-name> 
> <filter-class>com.testwebapp.reqrewrite.filter.RewriteBodyTestFilter
>
> 
</filter-class>
> </filter>
> 
> <!-- Filter mappings / chain ordering --> <filter-mapping> 
> <filter-name>RewriteBodyTestFilter</filter-name> 
> <servlet-name>FacesServlet</servlet-name> 
> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> 
> </filter-mapping>
> 
> </web-app>

So... what were you expecting here and what did you get?

- -chris
-----BEGIN PGP SIGNATURE-----
Comment: GPGTools - http://gpgtools.org

iQIcBAEBCAAGBQJWEfKXAAoJEBzwKT+lPKRYzXQP/0ol5Yuf13WdaPdaqsILcGd5
iP9TLGWhMe/neCosMuPYQ/BLxXym/wMcCuBxxD8U4FO1aX/JRfWB8zIe9JmSnECH
wKutjkKlwOrQS2AtO1EFryYO1yYEO3W29JcK7AJwTPt1y6dQ+daoEK6VVbuRJe3a
cJTe0ZoH220fkgyn6th/IzwrMjbO0WoattcJ9N4pTjoImou9Rj4O84jtHV6PX81w
IcpKfRhuVNDd3aUl551L5tUaPP3UsQAAhmwtDlx2ujme/ThTiURuzg3ATw4buAy1
ugG9rJrfc8dMiP6zfD2gYz1CQVXMa7q1mEfuuCJEXvYf/sW/h6t0mqi5SA7fHr2d
2r3o9AiSdhvMF6JZYQChxLdJvazOA4+25v+JhFR9PsxeBcVKoJsTeIYKmvDm7BGA
8Vlvig2z6O3T0fV1pmdNlLYgJowej0B/8UZY1sVet29etCEoqQagzkaaovn+nqK0
OojNEYwms3tBZNTO2BGFrpQPwpbg6LKoaZJdEDtDthbUu6S2JYNWZE5z936ANmDf
cwvQ7zpNQ/kv0+ySzcErLZF0iJRjdd2AlP2CszygvzaQAky3bQTV7S7TCdfjT1Pa
wsFyHx7ruuGofhlnVlG8FSBgs39fyhSEjIViufPzpxWWTbLftjW5Esevyndk/LyA
Wr8OKqnHT9m7b9lx9An9
=AFHk
-----END PGP SIGNATURE-----

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org