vaadin / framework

Vaadin 6, 7, 8 is a Java framework for modern Java web applications.
http://vaadin.com/
Other
1.78k stars 729 forks source link

Push fallback transport does not work when using Tomcat AJP #4349

Closed vaadin-bot closed 11 years ago

vaadin-bot commented 11 years ago

Originally by @Legioth


It appears that using UI.access() in a push application causes the UI to hang with the red spinner.

Reloading the browser brings it back, but then the same problem occurs again.

I'm attaching a test case that reproduces the problem.

Problem occurs in both Firefox 22.0 and Chrome 29.0.1547.57.

Edit: As discussed bellow, the problem only occurs when using Apache httpd as a front-end proxy using the AJP protocol. Configuring Vaadin to use the streaming transport for push instead of even trying to use websockets makes the problem go away.

vaadin-bot commented 11 years ago

Originally by archie172


Attachment added: demo3.war (8472.4 KiB) WAR file exhibiting the problem

vaadin-bot commented 11 years ago

Originally by @Legioth


Please verify that you are actually using Vaadin 7.1.3 that was released earlier this week.

It seems like the "random" data that your test application outputs could trigger #12404 that has been fixed in 7.1.3.

vaadin-bot commented 11 years ago

Originally by archie172


Now would I lie to you?? :)

Yes I am using Vaadin 7.1.3.

The WAR file I uploaded contains the JARs if you want to verify them:

SHA1(WEB-INF/lib/vaadin-7.1.3-atmosphere-compat-jbossweb.jar)= 69b2216c4109a9af5fbb3e0b84e06bd5fbfe556d
SHA1(WEB-INF/lib/vaadin-7.1.3-atmosphere-compat-tomcat.jar)= 7a02baf4bea91bdfc89b32b652cd78d3ad5c3b0b
SHA1(WEB-INF/lib/vaadin-7.1.3-atmosphere-compat-tomcat7.jar)= bce30367c46a7e20ac145f382608b9b22002e491
SHA1(WEB-INF/lib/vaadin-7.1.3-atmosphere-runtime.jar)= 45bd9d187b32896f2de26020927894588b726202
SHA1(WEB-INF/lib/vaadin-7.1.3-push.jar)= 29fd528c26ee2833ce6e491817196c9030f1005f
SHA1(WEB-INF/lib/vaadin-7.1.3-server.jar)= 3084e09daced0aa71652c222e90e6941557b2e5a
SHA1(WEB-INF/lib/vaadin-7.1.3-shared.jar)= e01be2377034ab6e1370fcb05892d52dd47e2af0
SHA1(WEB-INF/lib/vaadin-7.1.3-themes.jar)= 456ce210546658bf077e842d59ef014fd33f5a62
SHA1(WEB-INF/lib/vaadin-7.1.3-vaadin-slf4j.jar)= 2268b4bb8226e01963357bc9a02a5d56c9992b8a
SHA1(WEB-INF/lib/vaadin-7.1.3-widgets.jar)= 8cf2a0c1e4bdcff413e07c86c876f3f565013305
vaadin-bot commented 11 years ago

Originally by archie172


FYI, I am using Tomcat 7.0.42.

vaadin-bot commented 11 years ago

Originally by @Legioth


Replying to archie172:

Now would I lie to you?? :)

Yes I am using Vaadin 7.1.3.

The WAR file I uploaded contains the JARs if you want to verify them:

Sorry about that! I was apparently not fully awake when initially testing your WAR as I didn't realize that the used Vaadin version is of course there inside the file.

Your WAR does however work perfectly in my Tomcat 7.0.42 using Chrome 29 or Firefox 23. Since the symptoms you describe still match perfectly with a bug that was fixed in 7.1.3, I would guess that it's the browser that is lying to you by using a cached version of vaadinPush.js from Vaadin 7.1.2. #7868 suggests doing something to avoid that kind of problem.

So, could you please verify that you don't have an old version of vaadinPush.js cached in your browser?

vaadin-bot commented 11 years ago

Originally by archie172


I did the following...

First I looked in the Firefox cache using about:cache and verified that vaadinPush.js was the correct version:

$ openssl sha1 '/Users/archie/Library/Caches/Firefox/Profiles/te7l3sa4.default/Cache/B/6A/A53A7d01'
SHA1(/Users/archie/Library/Caches/Firefox/Profiles/te7l3sa4.default/Cache/B/6A/A53A7d01)= c4ee64df581df5738fdefdc5c69f56a102b93b93

Then I cleared my Firefox cache and reloaded the test WAR file.

Then I pressed the button on the screen and I got a permanent red spinner.

I guess I forgot to mention that you need to press the button to get the bug to occur (thought it would be obvious as it says "Press Me" :) Did you actually press the button?

FYI I'm using Mac OS X 10.8.4 on a Mac Book Pro. The Tomcat and Firefox are both running on this machine with Apache as proxy.

Aha, here is something to go on...

My tests have been via the URL http://localhost/demo3, which goes through the Apache reverse proxy.

When I connect directly to Tomcat via http://localhost:8080/demo3, the problem goes away.

So this must be some incompatibility with Vaadin Push and Apache reverse proxy.

My Apache reverse proxy configuration simple:

<LocationMatch "/demo3/">
    ProxyPass         ajp://127.0.0.1:8009/demo3/
    ProxyPassReverse  ajp://127.0.0.1:8009/demo3/
</LocationMatch>

Aha.. I just changed this to:

<LocationMatch "/demo3/">
    ProxyPass         http://127.0.0.1:8080/demo3/
    ProxyPassReverse  http://127.0.0.1:8080/demo3/
</LocationMatch>

and it started to work. This is a relief because in production we actually use http: instead of ajp:.

So it appears that the real problem is that Vaadin push is incompatible somehow with the Apache reverse proxy when using the AJP connector to Tomcat.

vaadin-bot commented 11 years ago

Originally by @Legioth


At least websockets do not work when using ajp: http://stackoverflow.com/questions/10536718/tomcat-7-websockects-through-ajp-not-working-any-suggestions. Atmosphere does generally try to fall back to http streaming if a websocket connection can't be established, but there are some special cases that can't easily be detected when proxies interrupt the connection attempt in strange ways.

You could maybe try using @Push(transport = Transport.STREAMING) on your UI to make it always use streaming instead of even attempting to open a websocket connection that will still not succeed because of the proxy.

vaadin-bot commented 11 years ago

Originally by archie172


Indeed, the problem does go away when using @Push(transport = Transport.STREAMING).

vaadin-bot commented 11 years ago

Originally by @Legioth


Excellent! With that information, I believe that I have a quite good understanding of the problem.

vaadin-bot commented 11 years ago

Originally by @wolfie


I'm reproducing this with Tomcat 7.0 and Apache httpd 2.2.22.

Simply opening an application that tries to use websockets will introduce a loop where, according to the ?debug-window, a websocket connection is established. 15 seconds later, the connection is dropped but re-established 0.5s later, only to be dropped 15s later - and this goes on forever.

In contrast to archie172, however, if I change AJP to HTTP in the Apache proxy configurations, my Vaadin application fails immediately with a "No active request" error message in the debug console and a "session expired" notification in the application.

Investigating.

vaadin-bot commented 11 years ago

Originally by @wolfie


Correction to previous: I am now able to reproduce the behavior as described originally. My proxy didn't have equivalent GET parameters, which apparently broke something. (i.e. http://localhost/ajp/foo/ was redirected to http://localhost:8080/foo/)

Now that I have equivalent paths for both servers, everything seems to work as expected:

vaadin-bot commented 11 years ago

Originally by @wolfie


This issue seems to be out of our hands - we can't fix this within Vaadin. This is misbehavior by either Apache httpd or Tomcat's AJP handling. Our educated guess is in the mod_proxy module and its AJP protocol implementation. Neither Apache httpd as a server nor AJP as a protocol support websockets.

Here's what's going on:

  1. Browser sends Apache a protocol upgrade request over HTTP
  2. Apache sees a HTTP request, accepts the request and does one of two probable things with it (we're not entirely certain, although it's irrelevant in practice for this bug's purpose)
    • it either sends the request as-is to Tomcat over AJP, where Tomcat fails to deliver on the websocket upgrade
    • or sending the message fails here, since AJP doesn't support websockets
  3. The browser sees an open request to the apache server, which never fails, since the client is trying to upgrade the connection, but never gets a proper response from Apache.
  4. Atmosphere sees that the connection has timed out after 15 seconds, and tries to re-establish the connection. Repeating forever.

Neither the Vaadin client nor the Vaadin server see anything being wrong. Everything appears to be working correctly.

What should happen is that Apache replies with a 501 Not Implemented error message, since the connection chain doesn't support websockets. However, when Apache and Tomcat talk with HTTP with eachother, Apache knows to drop the upgrade request, since it doesn't support websockets - this leads to the browser seeing the error report, and correctly being able to fall back to streaming.

-*What to do in this case:** If the entire chain doesn't support websockets (as it doesn't - we have two broken links here), the proxy software (Apache httpd) should be configured to detect the "Upgrade: websocket" header in the HTTP request, and reply with an error status (for example 501 Not Implemented). For some reason, this doesn't currently happen.

vaadin-bot commented 11 years ago

Originally by archie172


Thanks for the investigation. From packet traces, it looks like Tomcat is doing the right thing but Apache just doesn't know how to handle the upgrade.

FYI, here is an (invalid) bug I filed against Tomcat with some feedback from a Tomcat developer.

https://issues.apache.org/bugzilla/show_bug.cgi?id=55546