Export to GitHub

google-web-toolkit - issue #4817

Serializazion policy error when using via Apache proxy


Posted on Apr 4, 2010 by Massive Elephant

Found in GWT Release: 2.0.3

Encountered on OS / Browser: Kubuntu, FF3

Shortest code snippet which demonstrates issue:

I developed a GWT application that use several remote service. I tested it locally and in a remote application server (Jetty) and it run perfect.

The issue come out when I tried to use this application, using an Apache 2.0 web server as proxy server.

In my application server the application is deployed as: http://www.myserver.com:8080/dogproject and the proxy is setted as: http://www.myserver.com

So, what I want to do is to redirect "/" path to http://www.myserver.com:8080/dogproject

I added these lines to apache configuration files:

<VirtualHost *:80> ServerAdmin my.personal@email.com ServerName www.myserver.com

    ProxyRequests Off
    ProxyVia Off
    ProxyPreserveHost On

    &lt;Proxy *&gt;
      AddDefaultCharset off
      Order deny,allow
      Allow from all
    &lt;/Proxy&gt;

    &lt;Location /&gt;
           Order allow,deny
           Allow from all
           ProxyPass http://myserver:8080/dogproject/
           ProxyPassReverse http://rubi:8080/dogproject/
           SetEnv proxy-nokeepalive 1
    &lt;/Location&gt;

</VirtualHost>

When I try to open the application everything work ok: images are loaded ok, css too, tue only problem is when I try to use a remote services.

Client side I get the message: 500 Internal server error Server side I get the message:

2010-04-05 01:03:40.271:INFO:/dogproject:greetServlet: ERROR: The serialization policy file '/76E1D650870848211ECADADCA565F703.gwt.rpc' was not found; did you forget to include it in this deployment? 2010-04-05 01:03:40.272:WARN:/dogproject:Exception while dispatching incoming RPC call java.lang.NullPointerException at org.mortbay.log.StdErrLog.format(StdErrLog.java:212) at org.mortbay.log.StdErrLog.warn(StdErrLog.java:130) at org.mortbay.jetty.handler.ContextHandler$SContext.log(ContextHandler.java:1423) at javax.servlet.GenericServlet.log(GenericServlet.java:277) at com.google.gwt.user.server.rpc.RemoteServiceServlet.getSerializationPolicy(RemoteServiceServlet.java:144) at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.prepareToRead(ServerSerializationStreamReader.java:445) at com.google.gwt.user.server.rpc.RPC.decodeRequest(RPC.java:236) at com.google.gwt.user.server.rpc.RemoteServiceServlet.processCall(RemoteServiceServlet.java:186) at com.google.gwt.user.server.rpc.RemoteServiceServlet.processPost(RemoteServiceServlet.java:224) at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:62) at javax.servlet.http.HttpServlet.service(HttpServlet.java:710) at javax.servlet.http.HttpServlet.service(HttpServlet.java:803) at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511) at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:390) at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216) at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182) at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765) at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418) at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230) at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114) at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) at org.mortbay.jetty.Server.handle(Server.java:326) at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542) at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:938) at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:755) at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218) at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404) at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409) at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

The problem, I think, is that GWT is not able to find the serialization policy file. This file is present in my war/dogproject directory, but GWT search it in war/ directory (because of Apache proxy rule).

Checking the GWT source code seem to be the problem (if it is a problem) is in file:

com.google.gwt.user.server.rpc.RemoteServiceServlet in method:

static SerializationPolicy loadSerializationPolicy(HttpServlet servlet, HttpServletRequest request, String moduleBaseURL, String strongName)

because it use

> // The request can tell you the path of the web app relative to the > // container root. > String contextPath = request.getContextPath();

this code to get the path of the serialization policy. In my case the request.getContextPath is / and he go to search on the wrong path.

The right thig, I think, it to search on serverlet instances and to don't trust on request instance.

To help you work I developed an little application that can be used to test the issue. You have to deploy it on an application server, and try to use it.

Workaround if you have one:

To move the war/dogproject/** to war/

Attachments

Comment #1

Posted on Apr 9, 2010 by Grumpy Hippo

Sync from internal issue tracker.

Comment #2

Posted on May 1, 2010 by Massive Elephant

Thank you to reply to my bug report. If it is not a bug, there is a way to set my custom "serialization policy location"?

http://stackoverflow.com/questions/1517290/problem-with-gwt-behind-a-reverse-proxy-either-nginx-or-apache/2750719#2750719

Thank you

Comment #3

Posted on May 1, 2010 by Massive Elephant

Here there is the solution I found. I extended RemoteServiceServlet to take Serialization Policy file based on Context instead than in Web URL.

Then, in my service, I extended this custom class instead of RemoteServiceServlet.

In this way, GWT application can run in Apache Proxy environment without any problem.

Thank you Michele Renda

Attachments

Comment #4

Posted on Sep 22, 2010 by Quick Cat

Michele,

Thank you for the example servlet to handle this problem. However when I tried to use your approach it worked in the reverse proxy environment but not in my dev mode eclipse environment. After stepping through the code it became clear why. I decided to post my experience here for others to hopefully benefit.

I took an approach that would let me seamlessly move between my dev and prod environments.

As you did I overwrote RemoteServiceServlet but I only replaced following...

    @Override
protected SerializationPolicy doGetSerializationPolicy(
        HttpServletRequest request, String moduleBaseURL, String strongName) {
    //get the base url from the header instead of the body this way 
    //apache reverse proxy with rewrite on the header can work
    String moduleBaseURLHdr = request.getHeader("X-GWT-Module-Base");

    if(moduleBaseURLHdr != null){
        moduleBaseURL = moduleBaseURLHdr;
    }

    return super.doGetSerializationPolicy(request, moduleBaseURL, strongName);
}

In my apache config I added...

ProxyPass /app/ ajp://localhost:8009/App-0.0.1-SNAPSHOT/

RequestHeader edit X-GWT-Module-Base ^(.*)/app/(.*)$ $1/App-0.0.1-SNAPSHOT/$2

This approach works in all scenarios and delegates the url "mucking" to apache's proxy settings which is the approach I've always taken.

Comments on this approach are appreciated.

Comment #5

Posted on Nov 11, 2011 by Helpful Wombat

kc,

Well done, and thanks for posting this. I was also experiencing this issue, and your fix worked great.

Comment #6

Posted on Jun 23, 2013 by Helpful Panda

Hi, I have applied your suggestion. Now I don't have error "500 call failed on server" but there is another big problem. In my login service (that extends MyRemoteServiceServlet) I set session data, i.e. user, configuration, etc. When in another service class (that extends MyRemoteServiceServlet) I try to access this session data, they are null. It seem s that session is not managed

Comment #7

Posted on Sep 24, 2013 by Happy Cat

To fix the santure... problem, please follow this question: http://stackoverflow.com/questions/9486498/how-to-properly-set-jsessionid-cookie-path-behind-reverse-proxy

The trick is to use the ProxyPassReverseCookiePath and ProxyPassReverseCookieDomain properties.

Comment #8

Posted on Sep 17, 2014 by Massive Kangaroo

How about this code change? https://gist.github.com/BinaryMuse/476175 It won't break anything.

Status: AsDesigned