users@grizzly.java.net

CometWriter assumes Transfer-Encoding: chunked

From: Richard Zschech <richard_at_zschech.net>
Date: Thu, 28 Jan 2010 11:25:48 +1030

Hi Alexey,

 

Below is my code. It writes "BEFORE FLUSH" to the response then generates 10
notifications, one every three seconds. For each notification it
registerAsyncWrite then uses the CometWriter to write 1024 bytes of data.

 

Below the code is the first TCP capture with the request and chunked
response. You can see that it writes the "BEFORE FLUSH" chunk fine then the
after writing the first data chunk the CometWriter sends a zero sized chunk.
This signals to the browser that the response is complete. I then send
another chunk of data down the response and everything looks fine from my
codes point of view, but the browser ignores it as the response is complete.

 

The second TCP capture shows the request and non-chunked response. You can
see that it writes the "BEFORE FLUSH" without the chunk length 'd' fine then
the CometWriter sends a '400' chunk length and the data followed by a zero
sized chunk. This means the browser thinks the chunk lengths are a part of
the data.

 

From Richard.

 

Servlet code:

 

 

import java.io.IOException;

 

import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

import com.sun.grizzly.comet.CometContext;

import com.sun.grizzly.comet.CometEngine;

import com.sun.grizzly.comet.CometEvent;

import com.sun.grizzly.comet.CometHandler;

import com.sun.grizzly.comet.CometWriter;

 

public class GrizzlyServlet extends HttpServlet {

        

        private String topic;

        private CometEngine cometEngine;

        private CometContext cometContext;

        

        @Override

        public void init(ServletConfig config) throws ServletException {

               topic = config.getServletContext().getContextPath();

               cometEngine = CometEngine.getEngine();

               cometContext = cometEngine.register(topic);

               cometContext.setExpirationDelay(-1);

        }

        

        @Override

        public void destroy() {

               cometEngine.unregister(topic);

        }

        

        protected void doGet(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {

               response.getWriter().append("BEFORE FLUSH\n");

               response.getWriter().flush();

               final CometHandlerImpl handler = new CometHandlerImpl();

               handler.attach(response);

               cometContext.addCometHandler(handler);

               new Thread() {

                       @Override

                       public void run() {

                               try {

                                      for (int i = 0; i < 10; i++) {

                                              Thread.sleep(1);

 
System.out.println("Notifying");

                                              cometContext.notify(null,
handler);

                                              Thread.sleep(3000);

                                      }

                               }

                               catch (InterruptedException e) {

                                      e.printStackTrace();

                               }

                               catch (IOException e) {

                                      e.printStackTrace();

                               }

                       }

               }.start();

        }

        

        private class CometHandlerImpl implements
CometHandler<HttpServletResponse> {

               private HttpServletResponse response;

               

               volatile int index = 0;

               int size = 1 << 10;

               byte[] bytes = new byte[size];

               

               public CometHandlerImpl() {

                       for (int i = 0; i < size - 1; i++) {

                               bytes[i] = (byte) (i % 26 + 'A');

                       }

                       bytes[size - 1] = (byte)'\n';

               }

               

               public void attach(HttpServletResponse response) {

                       this.response = response;

               }

               

               public void onInitialize(CometEvent event) throws IOException
{

                       System.out.println("onInitialize");

               }

               

               public void onInterrupt(CometEvent event) throws IOException
{

                       System.out.println("onInterrupt");

               }

               

               public void onTerminate(CometEvent event) throws IOException
{

                       System.out.println("onTerminate");

               }

               

               public void onEvent(CometEvent event) throws IOException {

                       System.out.println("onEvent " + event.getType() + " "
+ event.attachment());

                       if (event.getType() == CometEvent.NOTIFY) {

                               System.out.println("Register " + index);

                               index = 0;

                               cometContext.registerAsyncWrite(this);

                       }

                       else if (event.getType() == CometEvent.WRITE) {

                               CometWriter writer = (CometWriter)
event.attachment();

                               

                               int write;

                               while ((write = writer.write(bytes, index,
bytes.length - index)) > 0) {

                                      System.out.println("Written " +
write);

                                      index += write;

                                      if (index == bytes.length) {

                                              return;

                                      }

                               }

                               System.out.println("Register " + index);

                               cometContext.registerAsyncWrite(this);

                       }

               }

        }

}

 

 

TCP capture with chunked response:

 

 

GET /grizzly HTTP/1.1

Host: 10.66.5.53:8080

User-Agent: Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.8.1.11)
Gecko/20080118 Firefox/2.0.0.11

Accept:
text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=
0.8,image/png,*/*;q=0.5

Accept-Language: en-us,en;q=0.5

Accept-Encoding: gzip,deflate

Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7

Keep-Alive: 300

Connection: keep-alive

HTTP/1.1 200 OK

X-Powered-By: Servlet/3.0

Server: GlassFish v3

Transfer-Encoding: chunked

Date: Thu, 28 Jan 2010 00:37:19 GMT

d

BEFORE FLUSH

 

400

ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWX
YZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUV
WXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRST
UVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQR
STUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP
QRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMN
OPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKL
MNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJ
KLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGH
IJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEF
GHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCD
EFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZAB
CDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHI

0

400

ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWX
YZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUV
WXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRST
UVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQR
STUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP
QRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMN
OPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKL
MNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJ
KLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGH
IJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEF
GHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCD
EFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZAB
CDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHI

0

 

400

......

 

 

TCP capture with non-chunked response:

 

 

GET /grizzly HTTP/1.1

Host: 10.66.5.53:8080

HTTP/1.1 200 OK

X-Powered-By: Servlet/3.0

Server: GlassFish v3

Date: Thu, 28 Jan 2010 00:49:32 GMT

Connection: close

 

BEFORE FLUSH

400

ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWX
YZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUV
WXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRST
UVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQR
STUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP
QRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMN
OPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKL
MNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJ
KLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGH
IJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEF
GHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCD
EFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZAB
CDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHI

0

 

400

ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWX
YZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUV
WXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRST
UVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQR
STUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP
QRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMN
OPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKL
MNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJ
KLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGH
IJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEF
GHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCD
EFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZAB
CDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHI

0

 

400

......

 

 

 

 

 

Hi Richard,

 

can you pls. attach the code you're using to send Comet response?

 

Thank you.

 

WBR,

Alexey.

 

On Jan 22, 2010, at 2:49 , Richard Zschech wrote:

 

> Hi Alexey,

>

> If GlassFish decides to use "Connection: close" to signal the end of

> the HTTP response with the socket disconnecting rather than with

> chunks the CometWriter should not ouput the chunk length information.

>

> The example I have GlassFish responds with:

>

> HTTP/1.1 200 OK

> X-Powered-By: Servlet/3.0

> Server: GlassFish v3

> Cache-Control: no-cache

> Content-Type: text/plain;charset=UTF-8

> Date: Fri, 22 Jan 2010 01:33:39 GMT

> Connection: close

>

> 5

> ABCDE

> 0

>

> where I've written "ABCDE" to the CometWriter, 5 and 0 are the chunk

> lengths, but because the Transfer-Encoding is not chunked then the 5

> and 0 are interpreted as a part of the response data.

>

> It should be:

>

> HTTP/1.1 200 OK

> X-Powered-By: Servlet/3.0

> Server: GlassFish v3

> Cache-Control: no-cache

> Content-Type: text/plain;charset=UTF-8

> Date: Fri, 22 Jan 2010 01:33:39 GMT

> Connection: close

>

> ABCDE

>

>

>

> Another problem I'm having is the CometWriter assumes that you are

> only going to write one chunk to the HTTP response. It writes the 0

> length chunk to the response to terminate it. Can this be configured?

>

> From Richard.

>

>

> > Hi Richard,

> >

> > > CometWriter assumes that the HTTP response is using "Transfer-

> > > Encoding: chunked".

> > >

> > > In some cases Glassfish decides to respond with "Connection:

> close"

> > > where the user agent detects the end of the HTTP response from the

> > > connection closing rather than using HTTP keep alive and

> chunking .

> > Can you pls. elaborate? Not sure I understand why "Connection:

> close"

> > causes issue?

> >

> > Thank you.

> >

> > WBR,

> > Alexey.

>