[jetty-user] Re: SPDY failures

Howard Lewis Ship <[email protected]> Thu, 7 Jun 2012 10:32:55 -0700
Newsgroups gmane.comp.java.jetty.support
Message-ID <CACd-vsNPygEbn9EC2YtuEROwdBOosoGeR=Rgj5HU7umc_rVndg@mail.gmail.com>
On Wed, Jun 6, 2012 at 4:20 PM, Howard Lewis Ship <[email protected]> wrote:
> I'm attempting to set up a Tapestry 5 application on Jetty
> 8.1.4.v20120524, Mac OS X Lion, JDK 1.7, Chrome.
>
> Most features work fine: HTTPs converts into SPDY (I'm using the SPDY
> Indicator plugin for Chrome, and it turns green).
>
> My page's HTML markup comes down fine, as does most of the related
> images and stylesheets.
>
> I'm having one problem though ... some of my larger JavaScript files
> are coming through truncated.  In production mode, Tapestry aggregates
> many small JavaScript files into a single virtual file.  This appears
> to be failing, and I'm seeing exceptions on my server side, and
> truncated JavaScript on the client side.
>
> Here's some details:
>
> EXCEPTION STACK:
>
>  org.apache.tapestry5.ioc.internal.OperationException
>  Protocol violation: cannot send a DATA frame on a closed stream
>              trace: Streaming asset stack en/core.js
>
>  java.lang.IllegalStateException
>  Protocol violation: cannot send a DATA frame on a closed stream
>  Stack trace:
>  - org.eclipse.jetty.spdy.StandardStream.data(StandardStream.java:393)
>  - org.eclipse.jetty.spdy.StandardStream.data(StandardStream.java:378)
>  - org.eclipse.jetty.spdy.http.ServerHTTPSPDYAsyncConnection$HTTPSPDYGenerator.flush(ServerHTTPSPDYAsyncConnection.java:695)
>  - org.eclipse.jetty.server.HttpOutput.flush(HttpOutput.java:94)
>  - org.eclipse.jetty.server.AbstractHttpConnection$Output.flush(AbstractHttpConnection.java:1022)
>  - org.eclipse.jetty.server.HttpOutput.write(HttpOutput.java:173)
>  - org.eclipse.jetty.server.HttpOutput.write(HttpOutput.java:101)
>  - org.apache.tapestry5.internal.services.assets.BytestreamCache.writeTo(BytestreamCache.java:46)
>  - org.apache.tapestry5.internal.services.assets.StreamableResourceImpl.streamTo(StreamableResourceImpl.java:73)
>  - org.apache.tapestry5.internal.services.ResourceStreamerImpl.streamResource(ResourceStreamerImpl.java:145)
>  - $ResourceStreamer_12952a34919a54bc.streamResource(Unknown Source)
>  - org.apache.tapestry5.internal.services.assets.StackAssetRequestHandler$1.perform(StackAssetRequestHandler.java:105)
>  - org.apache.tapestry5.internal.TapestryInternalUtils$5.run(TapestryInternalUtils.java:582)
>  - org.apache.tapestry5.ioc.internal.OperationTrackerImpl$1.invoke(OperationTrackerImpl.java:51)
>  - org.apache.tapestry5.ioc.internal.OperationTrackerImpl$1.invoke(OperationTrackerImpl.java:48)
>  - org.apache.tapestry5.ioc.internal.OperationTrackerImpl.invoke(OperationTrackerImpl.java:74)
>  - org.apache.tapestry5.ioc.internal.OperationTrackerImpl.run(OperationTrackerImpl.java:47)
>  - org.apache.tapestry5.ioc.internal.PerThreadOperationTracker.run(PerThreadOperationTracker.java:76)
>  - org.apache.tapestry5.ioc.internal.RegistryImpl.run(RegistryImpl.java:1116)
>  - org.apache.tapestry5.internal.TapestryInternalUtils.performIO(TapestryInternalUtils.java:576)
>  - org.apache.tapestry5.internal.services.assets.StackAssetRequestHandler.handleAssetRequest(StackAssetRequestHandler.java:96)
>  - org.apache.tapestry5.internal.services.AssetDispatcher.dispatch(AssetDispatcher.java:114)
>  - $Dispatcher_12952a34919a54b4.dispatch(Unknown Source)
>  - $Dispatcher_12952a34919a54b9.dispatch(Unknown Source)
>  - $Dispatcher_12952a34919a54b2.dispatch(Unknown Source)
>  - org.apache.tapestry5.services.TapestryModule$RequestHandlerTerminator.service(TapestryModule.java:302)
>  - org.apache.tapestry5.internal.services.RequestErrorFilter.service(RequestErrorFilter.java:26)
>  - $RequestHandler_12952a34919a54b3.service(Unknown Source)
>  - org.apache.tapestry5.services.TapestryModule$3.service(TapestryModule.java:902)
>  - $RequestHandler_12952a34919a54b3.service(Unknown Source)
>  - org.apache.tapestry5.services.TapestryModule$2.service(TapestryModule.java:892)
>  - $RequestHandler_12952a34919a54b3.service(Unknown Source)
>  - org.apache.tapestry5.internal.services.StaticFilesFilter.service(StaticFilesFilter.java:90)
>  - $RequestHandler_12952a34919a54b3.service(Unknown Source)
>  - $RequestHandler_12952a34919a54a7.service(Unknown Source)
>  - org.apache.tapestry5.services.TapestryModule$HttpServletRequestHandlerTerminator.service(TapestryModule.java:253)
>  - com.fivoosh.services.ajax.AjaxModule$1.service(AjaxModule.java:36)
>  - $HttpServletRequestHandler_12952a34919a54a9.service(Unknown Source)
>  - com.fivoosh.services.AppModule$12.service(AppModule.java:541)
>  - $HttpServletRequestHandler_12952a34919a54a9.service(Unknown Source)
>  - org.apache.tapestry5.internal.gzip.GZipFilter.service(GZipFilter.java:53)
>  - $HttpServletRequestHandler_12952a34919a54a9.service(Unknown Source)
>  - org.apache.tapestry5.internal.services.IgnoredPathsFilter.service(IgnoredPathsFilter.java:62)
>  - $HttpServletRequestFilter_12952a34919a54a2.service(Unknown Source)
>  - $HttpServletRequestHandler_12952a34919a54a9.service(Unknown Source)
>  - org.apache.tapestry5.services.TapestryModule$1.service(TapestryModule.java:852)
>  - $HttpServletRequestHandler_12952a34919a54a9.service(Unknown Source)
>  - $HttpServletRequestHandler_12952a34919a549f.service(Unknown Source)
>  - org.apache.tapestry5.TapestryFilter.doFilter(TapestryFilter.java:171)
>  - org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1338)
>  - com.fivoosh.NakedDomainRedirectorFilter.doFilter(NakedDomainRedirectorFilter.java:33)
>  - org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1338)
>  - org.eclipse.jetty.servlets.QoSFilter.doFilter(QoSFilter.java:205)
>  - org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1338)
>  - org.eclipse.jetty.servlets.DoSFilter.doFilterChain(DoSFilter.java:479)
>  - org.eclipse.jetty.servlets.DoSFilter.doFilter(DoSFilter.java:327)
>  - org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1338)
>  - org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:484)
>  - org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:119)
>  - org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:524)
>  - org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231)
>  - org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1065)
>  - org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:413)
>  - org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:192)
>  - org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:999)
>  - org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
>  - org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:250)
>  - org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:149)
>  - org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:111)
>  - org.eclipse.jetty.server.Server.handle(Server.java:350)
>  - org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:454)
>  - org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:890)
>  - org.eclipse.jetty.spdy.http.ServerHTTPSPDYAsyncConnection.handle(ServerHTTPSPDYAsyncConnection.java:215)
>  - org.eclipse.jetty.spdy.http.ServerHTTPSPDYAsyncConnection.performEndRequest(ServerHTTPSPDYAsyncConnection.java:371)
>  - org.eclipse.jetty.spdy.http.ServerHTTPSPDYAsyncConnection.access$500(ServerHTTPSPDYAsyncConnection.java:61)
>  - org.eclipse.jetty.spdy.http.ServerHTTPSPDYAsyncConnection$2.run(ServerHTTPSPDYAsyncConnection.java:301)
>  - org.eclipse.jetty.spdy.http.ServerHTTPSPDYAsyncConnection$1.run(ServerHTTPSPDYAsyncConnection.java:133)
>  - org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:603)
>  - org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:538)
>  - java.lang.Thread.run(Thread.java:722)
>
> REQUEST:
>
> Basic Information:
>        contextPath:
>              flags: secure
>             method: GET
>               path: /assets/20120528/stack/en/core.js
>             locale: en_US
>         serverName: localhost
>
> Headers:
>             Accept: */*
>     Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
>    Accept-Encoding: gzip,deflate,sdch
>    Accept-Language: en-US,en;q=0.8
>      Cache-Control: max-age=0
>             Cookie: tapx-timezone=America/Boise;
> JSESSIONID=ipnrrl4j225s103kx5r1mdqh9
>               Host: localhost
>  If-Modified-Since: Wed, 06 Jun 2012 22:48:48 GMT
>            Referer: https://localhost/login
>         User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4)
> AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.53 Safari/536.5
>             scheme: https
>
>
> I'm not sure where the flush() is coming from; here's the code for
> BytestreamCache:
>
> public class BytestreamCache
> {
>    private final byte[] streamData;
>
>    public BytestreamCache(byte[] streamData)
>    {
>        this.streamData = streamData;
>    }
>
>    public BytestreamCache(ByteArrayOutputStream os)
>    {
>        this(os.toByteArray());
>    }
>
>    public void writeTo(OutputStream os) throws IOException
>    {
>        os.write(streamData, 0, streamData.length); // line 46
>    }
>
>    public int size()
>    {
>        return streamData.length;
>    }
>
>    public InputStream openStream()
>    {
>        return new ByteArrayInputStream(streamData);
>    }
> }
>
>
> Basically, the BytestreamCache captures the output of a
> ByteArrayOutputStream (which itself contains the fully stream of
> aggregated JavaScript, in UTF-8), and then can stream it to an
> arbitrary OutputStream as needed.
>
> It is likely that the BytestreamCache in this case has had its content
> GZip compressed (Tapestry handles GZip compression itself, and caches
> the result for later use).  As a side note, I'm trying to figure out
> how to identify SPDY requests from the HttpServletRequest API, as to
> my understanding, SPDY handles compression itself.
>
> In any case, I turned off GZip compression and didn't see a difference
> here: I appeared to get the same exception on the same files,
> regardless of whether GZip compression had occurred or not.
>
>
> Tapestry will have set some response headers before opening the output
> stream: it sets headers for Last-Modified, Expires, and
> Content-Encoding (if the stream is compressed).
>
>
> So, I'm at a loss.  Much of the application appears to work correctly
> with the SPDY support, but this is a critical area.
>
> In terms of closing the stream ... when Tapestry renders a page, it
> uses creates its own PrintWriter around the OutputStream from
> HttpServletResponse.getOutputStream() ... after it finishes rendering,
> it will close() that PrintWriter (which should propogate down to the
> OutputStream).  Should Tapestry be invoking flush(), not close()?
> This occurs elsewhere as well ... that Tapestry will close() the
> stream it obtains.
>
> Thanks in advance for any help or insight.  This is awfully bleeding edge stuff!
>

I've done a little more research.

Turning off GZIp compression and JavaScript aggregation means lots
more requests, for smaller files.

What I'm seeing is that some subset of the individual files requested
are truncated.

Using FireFox instead of Chrome, with SPDY not enabled, the
application runs perfectly.


> --
> Howard M. Lewis Ship
>
> Creator of Apache Tapestry
>
> The source for Tapestry training, mentoring and support. Contact me to
> learn how I can get you up and productive in Tapestry fast!
>
> (971) 678-5210
> http://howardlewisship.com

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email