https://issues.apache.org/bugzilla/show_bug.cgi?id=50044
Summary: NIO HTTP connector aborts GET requests incorrectly. Product: Tomcat 6 Version: 6.0.29 Platform: PC OS/Version: Windows NT Status: NEW Severity: normal Priority: P2 Component: Connectors AssignedTo: dev@tomcat.apache.org ReportedBy: gober...@msn.com CC: gober...@msn.com I use NIO HTTP Tomcat connector org.apache.coyote.Http11NioProtocol to implement Comet streaming to Android phones. The application opens 'primary' persistent Comet connection to receive data from the server. Server sends data to the client periodically. Client can terminate this GET by sending "disconnect" GET command to the server. The "disconnect" command completes server response and cleans up client connection state. Each client has a unique client Id to identify it. So the "connect" GET looks like: /WebContentGateway/Controller?clientId=notimportant&command=connect And disconenct looks like: /WebContentGateway/Controller?clientId=notimportant&command=disconnect The problem I uncovered is that "disconnect" GET somehow gets truncated and never invoked on the server side (looks like it is "lost"). I traced it with Wireshark and Tomcat does receive GET request, but just closes connection without invoking servlet/ sending GET response (the “disconnect” GET socket connection is obviously different from the “primary” connection). I have both client and servlet code and can provide any support to reproduce the problem. This problem did not exist with build 6.0.16, but can be replicated with the latest Tomcat 6 and 7. It is very important for me to resolve it - NIO HTTP Tomcat does not look solid it this point. Client code just send “connect” command followed by “disconnect”. There should not be any threading contention because I serialize sending commands in one thread. It hangs on readThread.join(); because "primary" thread does not terminate. Also, if I throttle the for loop by putting sleep, the problem does not occur, so it is some sort of a racing condition in Tomcat. CLIENT CODE: public class Test extends AndroidTestCase { private static final String CLIENT_ID_NOTIMPORTANT = "clientId=notimportant"; private static String url = "http://172.16.16.33/WebContentGateway/Controller?"; private static InputStream cometConnectionInputStream; private static Thread readThread; public void testConnectDisconnect() { for (int i = 0; i < 100; i++) { try { URL cometUrl = new URL(url + CLIENT_ID_NOTIMPORTANT + "&command=connect"); HttpURLConnection cometConnection = (HttpURLConnection) cometUrl.openConnection(); cometConnection.setRequestMethod("GET"); cometConnection.setDoInput(true); cometConnection.setUseCaches(false); cometConnection.setConnectTimeout(10000); cometConnection.setReadTimeout(0); cometConnectionInputStream = cometConnection.getInputStream(); final CountDownLatch readLatch = new CountDownLatch(1); readThread = new Thread(new Runnable() { public void run() { System.out.println("Started Connect GET thread."); readLatch.countDown(); byte[] readConnectionReadBuffer = new byte[10 * 1024]; try { while ((cometConnectionInputStream.read(readConnectionReadBuffer, 0, readConnectionReadBuffer.length)) >= 0) { } } catch (Throwable e) { } System.out.println("Connect GET finished."); } }); readThread.start(); readLatch.await(); URL disconnectUrl = new URL(url + CLIENT_ID_NOTIMPORTANT + "&command=disconnect"); System.out.println("Sending disconnect"); HttpURLConnection disconnectConnection = (HttpURLConnection) disconnectUrl.openConnection(); disconnectConnection.setRequestMethod("GET"); disconnectConnection.setDoInput(true); disconnectConnection.setUseCaches(false); disconnectConnection.setConnectTimeout(10000); disconnectConnection.setReadTimeout(0); InputStream disconnectConnectionInputStream = disconnectConnection.getInputStream(); byte[] writeConnectionReadBuffer = new byte[10 * 1024]; while ((disconnectConnectionInputStream.read(writeConnectionReadBuffer, 0, writeConnectionReadBuffer.length)) >= 0) { } disconnectConnectionInputStream.close(); readThread.join(); } catch (Exception e) { e.printStackTrace(); } } } } SERVER CODE: public class WebFrameworkServlet extends HttpServlet implements CometProcessor { private Map<String, CometClientInfo> cometClientsInfoMap = new ConcurrentHashMap<String, CometClientInfo>(); private Map<HttpServletResponse, String> cometResponsesMap = new ConcurrentHashMap<HttpServletResponse, String>(); public void event(CometEvent event) throws IOException, ServletException { HttpServletRequest request = event.getHttpServletRequest(); HttpServletResponse response = event.getHttpServletResponse(); if (event.getEventType() == CometEvent.EventType.BEGIN) { if (request.getParameter(ParamNames.IN_PARAM_COMMAND).equals(ParamNames.COMMAND_NAME_CONNECT)) { String clientId = request.getParameter(ParamNames.COMMAND_LOGIN_CLIENT_ID); response.setHeader("pragma", "no-cache,no-store"); response.setHeader("cache-control", "no-cache,no-store,max-age=0,max-stale=0"); event.setTimeout(Integer.MAX_VALUE); Client client = new Client(clientId, null, null); CometClientInfo cometClientInfo = new CometClientInfo(client, response, 0, event); cometClientsInfoMap.put(client.getClientId(), cometClientInfo); cometResponsesMap.put(response, client.getClientId()); PrintWriter out = response.getWriter(); out.print("{\"statusCode\" : 0}"); out.flush(); } if (request.getParameter(ParamNames.IN_PARAM_COMMAND).equals(ParamNames.COMMAND_NAME_DISCONNECT)) { String clientId = request.getParameter(ParamNames.COMMAND_LOGIN_CLIENT_ID); CometClientInfo existingClientInfo = cometClientsInfoMap.remove(clientId); response.setContentType("text/json"); PrintWriter out = response.getWriter(); out.print("{\"statusCode\" : 0}"); out.close(); if (existingClientInfo != null) { cometResponsesMap.remove(existingClientInfo.getResponse()); existingClientInfo.getWriter().close(); existingClientInfo.getEvent().close(); } } } else if (event.getEventType() == CometEvent.EventType.ERROR) { event.close(); String clientId = cometResponsesMap.remove(response); if (clientId != null) { CometClientInfo cometClientInfo = cometClientsInfoMap.remove(clientId); if (cometClientInfo != null) { cometClientInfo.getClient().setDisconnectTime(System.currentTimeMillis()); } } } else if (event.getEventType() == CometEvent.EventType.END) { event.close(); String clientId = cometResponsesMap.remove(response); if (clientId != null) { CometClientInfo cometClientInfo = cometClientsInfoMap.remove(clientId); if (cometClientInfo != null) { cometClientInfo.getClient().setDisconnectTime(System.currentTimeMillis()); } } } } } -- Configure bugmail: https://issues.apache.org/bugzilla/userprefs.cgi?tab=email ------- You are receiving this mail because: ------- You are the assignee for the bug. --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org