Filip
Peter Warren wrote:
Just to make sure that I wasn't crazy, I did some more tests, including using your cometgui.jar. My results still show both read errors and END events. Is it possible that it's a platform issue? I'm running Windows XP Pro SP2. As I mentioned before I'm using the tomcat 6.0.x trunk as of 21-01-2008. Also, I re-started tomcat in between all tests to make sure there weren't stray bytes hanging around somewhere. My server code is the same as shown previously in this thread. Peter Here are my results: 1) Running LastChunkTest (code below) with last chunk of 0crlfcrlf, server shows: event: BEGIN, subtype: null event: READ, subtype: null Read 10 bytes: comet test for session: 627378C9DEE2817A93EBAC190DE47599 read error 2) Running LastChunkTest with last chunk of 0crlfcrlf and a 50 ms sleep before sending last chunk, server shows: event: BEGIN, subtype: null event: READ, subtype: null Read 10 bytes: comet test for session: A00EFFC9A9FE8CBF1833D204EF00667C event: END, subtype: null 3) Running LastChunkTest with a last chunk of 0crlf, server shows: event: BEGIN, subtype: null event: READ, subtype: null Read 10 bytes: comet test for session: DD41C198E3D5CD6E7D73FC0D7B37E86F 4) Running cometgui.jar with 0crlfcrlf: client sends (without the dashes): --- GET /CometTest HTTP/1.1 User-Agent: Filips Awesome Client/1.0 Host: 127.0.0.1:8080 Transfer-Encoding: chunked 10 test data test 1 0 --- server shows: event: BEGIN, subtype: null event: READ, subtype: null Read 16 bytes: test data test 1 for session: 4023C27063C5674C1F37D315C3AF0AFC read error 5) Running cometgui.jar with 0crlf: client sends (without the dashes): --- GET /CometTest HTTP/1.1 User-Agent: Filips Awesome Client/1.0 Host: 127.0.0.1:8080 Transfer-Encoding: chunked 10 test data test 1 0 --- server shows: event: BEGIN, subtype: null event: READ, subtype: null Read 16 bytes: test data test 1 for session: BC5EA8DA373D1E6726020F44F71165C7 side note: Not sure if this is important, but when doing the following steps in cometgui for the client data shown above, I get the exception below: connect submit disconnect Jan 22, 2008 10:42:49 AM org.apache.catalina.core.StandardWrapperValve event SEVERE: Servlet.service() for servlet CometTest threw exception java.io.EOFException: Unexpected EOF read on the socket at org.apache.coyote.http11.InternalNioInputBuffer.readSocket(InternalNi oInputBuffer.java:589) at org.apache.coyote.http11.InternalNioInputBuffer.fill(InternalNioInput Buffer.java:868) at org.apache.coyote.http11.InternalNioInputBuffer$SocketInputBuffer.doR ead(InternalNioInputBuffer.java:892) at org.apache.coyote.http11.filters.ChunkedInputFilter.readBytes(Chunked InputFilter.java:243) at org.apache.coyote.http11.filters.ChunkedInputFilter.parseCRLF(Chunked InputFilter.java:326) at org.apache.coyote.http11.filters.ChunkedInputFilter.parseEndChunk(Chu nkedInputFilter.java:356) at org.apache.coyote.http11.filters.ChunkedInputFilter.doRead(ChunkedInp utFilter.java:136) at org.apache.coyote.http11.InternalNioInputBuffer.doRead(InternalNioInp utBuffer.java:830) at org.apache.coyote.Request.doRead(Request.java:428) at org.apache.catalina.connector.InputBuffer.realReadBytes(InputBuffer.j ava:298) at org.apache.tomcat.util.buf.ByteChunk.substract(ByteChunk.java:405) at org.apache.catalina.connector.InputBuffer.read(InputBuffer.java:313) at org.apache.catalina.connector.CoyoteInputStream.read(CoyoteInputStrea m.java:162) at com.seekspeak.server.debug.CometTestServlet.event(CometTestServlet.ja va:31) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilterEvent (ApplicationFilterChain.java:470) at org.apache.catalina.core.ApplicationFilterChain.doFilterEvent(Applica tionFilterChain.java:363) at org.apache.catalina.core.StandardWrapperValve.event(StandardWrapperVa lve.java:422) at org.apache.catalina.core.StandardContextValve.event(StandardContextVa lve.java:252) at org.apache.catalina.core.StandardHostValve.event(StandardHostValve.ja va:179) at org.apache.catalina.valves.ValveBase.event(ValveBase.java:200) at org.apache.catalina.valves.ValveBase.event(ValveBase.java:200) at org.apache.catalina.core.StandardEngineValve.event(StandardEngineValv e.java:128) at org.apache.catalina.connector.CoyoteAdapter.event(CoyoteAdapter.java: 198) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.jav a:292) at org.apache.coyote.http11.Http11NioProcessor.process(Http11NioProcesso r.java:876) at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.pr ocess(Http11NioProtocol.java:719) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoin t.java:2080) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExec utor.java:885) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor .java:907) at java.lang.Thread.run(Thread.java:619) event: ERROR, subtype: null Code for LastChunkTest: ======================= package test; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.NoRouteToHostException; import java.net.Socket; import java.net.URL; public class LastChunkTest { private static final String ENCODING = "ISO-8859-1"; private static final String DELIMITER = "\r\n"; private URL url; private InputStream inputStream; private OutputStream outputStream; private Socket socket; public static void main(String[] args) throws Exception { LastChunkTest test = new LastChunkTest(); test.test(); } private void test() throws Exception { url = new URL("http://www.seekspeak.com/CometTest"); initConnection(); sendHeaders(); send("comet test"); // uncomment sleep to get END event instead of read error // try { // Thread.sleep(50); // } catch (InterruptedException ie) { // // do nothing // } sendLastChunk(); // block on read to keep app alive - server never sends response inputStream.read(); } private void initConnection() throws IOException { int port = url.getPort(); port = (port < 0) ? url.getDefaultPort() : port; try { socket = new Socket(url.getHost(), port); socket.setKeepAlive(true); inputStream = socket.getInputStream(); outputStream = socket.getOutputStream(); } catch (NoRouteToHostException nrthe) { System.out.println("host: " + url.getHost()); nrthe.printStackTrace(); } } private void sendHeaders() throws IOException { String path = url.getPath(); StringBuffer outputBuffer = new StringBuffer(); outputBuffer.append("POST " + path + " HTTP/1.1" + DELIMITER); outputBuffer.append("Host: " + url.getHost() + DELIMITER); outputBuffer.append("User-Agent: CometTest" + DELIMITER); outputBuffer.append("Connection: keep-alive" + DELIMITER); outputBuffer.append("Content-Type: text/plain" + DELIMITER); outputBuffer.append("Transfer-Encoding: chunked" + DELIMITER); outputBuffer.append(DELIMITER); byte[] outputBytes = outputBuffer.toString().getBytes(ENCODING); outputStream.write(outputBytes); outputStream.flush(); } private void send(String chunkData) throws IOException { byte[] chunkBytes = chunkData.getBytes(ENCODING); String hexChunkLength = Integer.toHexString(chunkBytes.length); StringBuffer outputBuffer = new StringBuffer(); outputBuffer.append(hexChunkLength); outputBuffer.append(DELIMITER); outputBuffer.append(chunkData); outputBuffer.append(DELIMITER); byte[] outputBytes = outputBuffer.toString().getBytes(ENCODING); outputStream.write(outputBytes); outputStream.flush(); } private void sendLastChunk() throws IOException { byte[] outputBytes = new String("0" + DELIMITER).getBytes(ENCODING); outputStream.write(outputBytes); outputStream.flush(); } } On Jan 22, 2008 9:07 AM, Filip Hanik - Dev Lists <[EMAIL PROTECTED]> wrote:I still don't get the END event, however, thanks for pointing it out, you did find a regression bug about the timeout http://svn.apache.org/viewvc?view=rev&revision=614249 I've added the patch above to the proposal list for 6.0.x to fix the timeout issue Filip Peter Warren wrote:as I mentioned, the "last chunk" doesn't generate an END event, I tried it locally. of course against 6.0.x trunk.I played around a bit because I was definitely getting an END event and found: Sending 0crlf does not generate and END event. However sending 0crlfcrlf, which is what HttpURLConnection does, does generate an END (or sometimes a read error - see below...) Looking at the http spec, it seems like 0crlfcrlf is actually the proper way to terminate the chunk body: Chunked-Body = *chunk last-chunk trailer CRLF last-chunk = 1*("0") [ chunk-extension ] CRLF Am I reading that correctly? Note about END and read error: When running both the client and the server locally (i.e. little latency), sending 0crlfcrlf would sometimes generate a read error (i.e. inputStream.isAvailable() > 0 would be true and then number of bytes read would be < 0) and sometimes an END event. I tried with both sockets and HttpURLConnection and saw similar behavior. However when using HttpURLConnection I could add a delay of 50 millis. and guarantee that I always got an end event (see code below). I am using the latest 6.0.x trunk updated locally today. Peter CLIENT CODE =========== URL url = new URL("http://www.seekspeak.com/CometTest"); HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); urlConn.setRequestMethod("POST"); urlConn.setChunkedStreamingMode(-1); // use default chunk length urlConn.setReadTimeout(0); urlConn.setDoInput(true); urlConn.setDoOutput(true); urlConn.connect(); PrintWriter out = new PrintWriter(urlConn.getOutputStream(), true); out.print("test"); out.flush(); try { // sleep to guarantee an END event - remove this sleep to get read error Thread.sleep(50); } catch (InterruptedException ie) { // do nothing } urlConn.getInputStream(); COMET SERVLET CODE: ============= public void event(CometEvent cometEvent) throws IOException, ServletException { System.out.println("event: " + cometEvent.getEventType() + ", subtype: " + cometEvent.getEventSubType()); if (cometEvent.getEventType() == CometEvent.EventType.ERROR) { cometEvent.close(); } else if (cometEvent.getEventType() == CometEvent.EventType.END) { cometEvent.close(); } else if (cometEvent.getEventType() == CometEvent.EventType.READ) { HttpServletRequest request = cometEvent.getHttpServletRequest(); InputStream inputStream = request.getInputStream(); byte[] buf = new byte[512]; do { int n = inputStream.read(buf); // can throw an IOException if (n > 0) { System.out.println("Read " + n + " bytes: " + new String(buf, 0, n) + " for session: " + request.getSession(true).getId()); } else if (n < 0) { System.out.println("read error"); return; } } while (inputStream.available() > 0); } } On Jan 21, 2008 11:53 AM, Filip Hanik - Dev Lists <[EMAIL PROTECTED]> wrote:answers inline Peter Warren wrote:First off, thanks for your responses. The contributors to this list are extremely responsive, patient, and helpful, and I really appreciate it! Hmm, in your test case did you set the HttpURLConnection to use chunked transfers (setChunkedStreamingMode(...))? I find if I use chunked transfers, the HttpURLConnection sends a "last chunk" message upon reading input from the server, which generates a comet END event on the server. If I don't use chunked transfers, no END event is generated because no "last chunk" is sent by the client.as I mentioned, the "last chunk" doesn't generate an END event, I tried it locally. of course against 6.0.x trunk.Which brings up an option I never considered: will a comet servlet function properly with non-chunked transfer (i.e. no transfer-coding header)? It seems to.yes, it can, just send a very large content-length headerLastly, I'm still a little confused about requiring the comet event to be closed on an END event. Doesn't this mean that tomcat comet can't handle pipelined requests? If a client sends 2 pipelined requests, it will send a "last chunk" to indicate the end of the first request. This "last chunk" will generate an END event on the server, which then requires the connection to be closed. After the comet event is closed the server cannot send a response to the client.pipelined requests are not defined by the HTTP spec for POST methods, only for GET assuming the pipelining you are talking about is true pipelining :) if you just mean, next request, then yes, tomcat handles that just fine, and that is why you have to call event.close()The END event docs indicate that pipelined request will generate an END event: ...End will also be called when data is available and the end of file is reached on the request input (this usually indicates the client has pipelined a request).depends on what you mean by pipeline, see aboveThanks, Peter On Jan 20, 2008 8:15 PM, Filip Hanik - Dev Lists <[EMAIL PROTECTED]> wrote:now I get it. I just ran through a test case, and an END event was not thrown just because there was an end chunk. the response is very much still open at that point Filip Peter Warren wrote:What java.net.HttpURLConnection has to do with Tomcat and comet is that HttpURLConnection is Java's implementation of an http client and will likely be used by people developing comet apps for Tomcat. In my case, I want to use it because I can't use raw sockets on my applet client due to permission problems when trying to use sockets behind a proxy. I understand that asynchronous writes are possible, but they're not when using HttpURLConnection because HttpURLConnection sends a "last chunk" message when it's done with its request. "Last chunk" generates a comet end event, which then requires that the connection to the client be closed. I guess I don't understand why tomcat needs to close the connection after an END event. It seems to me that the "last chunk" message from the client simply indicates that the client is done sending its request. Why does the server need to close the connection when the client finishes its request? Peter On Jan 19, 2008 6:01 PM, Filip Hanik - Dev Lists <[EMAIL PROTECTED]> wrote:I'm not sure what HttpURLConnection has to do with Tomcat or comet. and yes, asynchronous writes are possible, just not after the END or ERROR events have been issued Filip Peter Warren wrote:Does that mean that HttpURLConnection cannot be used for comet requests with asynchronous (i.e. delayed) responses? It would seem so to me since HttpURLConnection always sends an END message before reading from the server and since the server can no longer write to the client after closing the comet event. Am I missing something? Is there a way to write to the client after the comet event is closed? Would you consider it a bug that HttpURLConnection is implemented that way? Peter On Jan 18, 2008 9:21 PM, Filip Hanik - Dev Lists <[EMAIL PROTECTED]> wrote:during end and error, you MUST close the Comet event Filip Peter Warren wrote:What do I do to make the END event stop repeating? I don't want to close the CometEvent yet because the server is waiting for data to send to the client. If I don't close the comet event, the END event repeats incessantly. I'm using an unsigned applet as a comet client. To accommodate proxies, I've had to change my comet client to use HttpURLConnection instead of Sockets. (Accessing ProxySelector from an applet to create a socket with a proxy generates an AccessControlException.) HttpURLConnection unfortunately sends a 0crlf when its input stream is retrieved for reading. This generates a Comet END event. Short of closing the comet event, how can I make the server stop notifying me of END events? I can't close the comet event because I want to hold onto the comet output stream for use later to send data to the client. >From the comet docs: EventType.END: End may be called to end the processing of the request. Fields that have been initialized in the begin method should be reset. After this event has been processed, the request and response objects, as well as all their dependent objects will be recycled and used to process other requests. End will also be called when data is available and the end of file is reached on the request input (this usually indicates the client has pipelined a request). This seems to indicate that even if I could get the END event to go away quietly, the comet event's output stream might no longer be usable anyway. It seems to me I have 3 options: 1) figure out how to make the comet END event stop repeating and hope it's output stream still works 2) figure out how to keep HttpURLConnection from sending 0crlf (don't know if that can be done) 2) use sockets with ProxySelector (which requires signing my applet and getting users to grant it privileges) Thanks, Peter --------------------------------------------------------------------- To start a new topic, e-mail: users@tomcat.apache.org To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]--------------------------------------------------------------------- To start a new topic, e-mail: users@tomcat.apache.org To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]--------------------------------------------------------------------- To start a new topic, e-mail: users@tomcat.apache.org To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]--------------------------------------------------------------------- To start a new topic, e-mail: users@tomcat.apache.org To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]--------------------------------------------------------------------- To start a new topic, e-mail: users@tomcat.apache.org To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]--------------------------------------------------------------------- To start a new topic, e-mail: users@tomcat.apache.org To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]--------------------------------------------------------------------- To start a new topic, e-mail: users@tomcat.apache.org To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]--------------------------------------------------------------------- To start a new topic, e-mail: users@tomcat.apache.org To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]--------------------------------------------------------------------- To start a new topic, e-mail: users@tomcat.apache.org To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]--------------------------------------------------------------------- To start a new topic, e-mail: users@tomcat.apache.org To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]--------------------------------------------------------------------- To start a new topic, e-mail: users@tomcat.apache.org To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
--------------------------------------------------------------------- To start a new topic, e-mail: users@tomcat.apache.org To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]