Summary: | CometProcessor does not flush and close HTTP/1.0 requests | ||
---|---|---|---|
Product: | Tomcat 7 | Reporter: | Frank Schroeder <frank.schroeder> |
Component: | Catalina | Assignee: | Tomcat Developers Mailing List <dev> |
Status: | RESOLVED DUPLICATE | ||
Severity: | normal | ||
Priority: | P2 | ||
Version: | 7.0.8 | ||
Target Milestone: | --- | ||
Hardware: | PC | ||
OS: | All |
Description
Frank Schroeder
2011-02-09 14:16:29 UTC
I've found the problem. The following sequence works with HTTP/1.1 but not with HTTP/1.0. I guess I've made a classical optimization mistake. This also explains why the content length and encoding headers were never set. String data = "..."; PrintWriter writer = response.getWriter(); response.setStatus(200); response.setCharacterEncoding("UTF-8"); response.setContentLength(data.length()); writer.write(data); writer.flush(); event.close(); Getting the Writer *after* setting the response headers makes it all work String data = "..."; response.setStatus(200); response.setCharacterEncoding("UTF-8"); response.setContentLength(data.length()); PrintWriter writer = response.getWriter(); writer.write(data); writer.flush(); event.close(); > String data = "...";
> response.setCharacterEncoding("UTF-8");
> response.setContentLength(data.length());
> PrintWriter writer = response.getWriter();
The above code is wrong, because String.length() is measured in chars, but content-length header is measured in bytes. UTF-8 uses more than 1 byte for characters > 127.
Tomcat could be a little smarter here. We currently ignore a call to setContentLength() after a call to getWriter(). However, we only have to ignore the content length once bytes have actually been written to the response and a method to determine that is available. If I were to patch trunk, could you build 7.0.x from source and give it a try? Sure, I can try that. While you're at it you can also check the encoding as it wasn't set as well. The content type was OK. The reason I had this code was because of this: PrintWriter writer = response.getWriter(); if (jsonp) { String data = ... build jsonp here ... response.setStatus(200); response.setContentType("text/javascript"); response.setCharacterEncoding("UTF-8"); response.setContentLength(data.length()); writer.write(data); } else { String data = ... build json here ... response.setStatus(200); response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); response.setContentLength(data.length()); writer.write(data); } writer.flush(); event.close(); Regarding the content length. So I guess that should be then like this? response.setContentLength(data.getBytes("UTF-8").length); As a side node and for completeness: nginx still hung but turning off proxy buffering with proxy_buffering off; fixed that as well. You can't change the encoding once the writer has been obtained since the encoding is used to create the writer. (In reply to comment #3) > Tomcat could be a little smarter here. For reference: I filed bug 50748 to deal with setContentLength() improvements. (In reply to comment #4) > Regarding the content length. So I guess that should be then like this? > response.setContentLength(data.getBytes("UTF-8").length); > If you already called getBytes() then use byte[] array that it returns and pass to an OutputStream. You won't need a Writer. There is no need to do the double work. This issue is part useful discussion, part bug. The bug part has moved to 50748 and the discussion should move to the users mailing list. *** This bug has been marked as a duplicate of bug 50748 *** Just for the sake of completeness: I've changed the code to String data = "..."; byte[] buf = data.getBytes(charset); response.setContentLength(buf.length); response.getOutputStream().write(buf); response.getOutputStream().flush(); cometEvent.close(); |