Details
-
Type:
Bug
-
Status:
Resolved
-
Priority:
Major
-
Resolution: Fixed
-
Affects Version/s: None
-
Fix Version/s: 6.1.20
-
Component/s: Security and SSL
-
Labels:None
-
Environment:Jetty 6.1.18 and jetty-7.
jdk1.5 and jdk1.6.
-
Number of attachments :
Description
Pasted from the Jetty lists:
I've been looking a problem in our app associated with chunking requests
from an HTTPS client. When client uses HTTPS and chunking, Jetty gets
into a state where it just sits waiting for more data from input stream
on server side after reading the first chunk. It eventually causes the thread to timeout (30s?). However,
the same thing works when the client is dong HTTP.
I've recreated the problem with the test program below which exhibits the same behavior.
When the following program runs with variable https = true (meaning use
HTTPS from client), Test.doPost() will do one read of 122 bytes from the input stream,
but on its second read Jetty goes into a wait state seamingly waiting
for more data.
Stack:
Thread [1658468894@qtp0-4] (Suspended)
Object.wait(long) line: not available [native method] [local variables
unavailable]
SslHttpChannelEndPoint(SelectChannelEndPoint).blockReadable(long) line:
243
HttpParser$Input.blockForContent() line: 1147
HttpParser$Input.read(byte[], int, int) line: 1103
HttpParser$Input(InputStream).read(byte[]) line: 85
ChunkingTest$Test.doPost(HttpServletRequest, HttpServletResponse) line:
70
ChunkingTest$Test(HttpServlet).service(HttpServletRequest,
HttpServletResponse) line: 760
ChunkingTest$Test(HttpServlet).service(ServletRequest, ServletResponse)
line: 853
ServletHolder.handle(ServletRequest, ServletResponse) line: 502
ServletHandler.handle(String, HttpServletRequest, HttpServletResponse,
int) line: 363
Server(HandlerWrapper).handle(String, HttpServletRequest,
HttpServletResponse, int) line: 152
Server.handle(HttpConnection) line: 324
HttpConnection.handleRequest() line: 534
HttpConnection$RequestHandler.content(Buffer) line: 879
HttpParser.parseNext() line: 828
HttpParser.parseAvailable() line: 207
HttpConnection.handle() line: 403
SslHttpChannelEndPoint(SelectChannelEndPoint).run() line: 409
QueuedThreadPool$PoolThread.run() line: 522
If the program is run with https = false (meaning use HTTP from client),
the Test servlet doPost() successfully reads all chunked data and
then returns.
I'm at a loss as to why the use of chunking works with a client that is HTTP,
but doesn't when the client is doing HTTPS. Wireshark shows that
everything looks ok with the request under HTTP.
I've tried this program with Jetty 6.1.14 and 6.1.18, with the latest
JDK 1.5 and 1.6 versions and it behaves the same under all versions.
I've also tried changing the use of HttpsURLConnection with Apache
HttpClient, but it has the same problem too.
Perhaps I'm just missing something unobvious? Anybody have any ideas?
// Test program
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.mortbay.jetty.security.SslSelectChannelConnector;
import org.mortbay.jetty.servlet.ServletHandler;
import org.mortbay.thread.QueuedThreadPool;
public class ChunkingTest {
// With true, Test.doPost() blocks waiting for
// With false, all chunks are read by Test.doPost().
static boolean https = false;
private static final int BODY_SIZE = 300;
public static final void main(String[] args) throws Exception {
startServer();
URL url = new URL((https ? "https" : "http") + "://localhost:8080/test");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
if (conn instanceof HttpsURLConnection) {
((HttpsURLConnection) conn)
.setHostnameVerifier(new HostnameVerifier() {
public boolean verify(String urlHostName,
SSLSession session) {
return true;
}
});
}
conn.setConnectTimeout(10000);
conn.setReadTimeout(100000);
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "text/plain"); //$NON-NLS-1$
conn.setChunkedStreamingMode(128);
conn.connect();
byte[] b = new byte[BODY_SIZE];
for (int i = 0; i < BODY_SIZE; i++) {
b[i] = 'x';
}
OutputStream os = conn.getOutputStream();
os.write(b);
os.flush();
int rc = conn.getResponseCode();
int len = 0;
InputStream is = conn.getInputStream();
while ((len = is.read(b)) > -1)
public static class Test extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setBufferSize(128);
byte[] b = new byte[BODY_SIZE];
int len = 0;
InputStream is = req.getInputStream();
// !!!! UNDER HTTPS, FIRST CHUNK IS READ HERE BUT THEN SERVER IS WAITING FOR
// !!!! MORE DATA
while ((len = is.read(b)) > -1) {
System.out.println("servlet read chunk: " + len);
}
OutputStream os = resp.getOutputStream();
for (int i = 0; i < BODY_SIZE; i++)
}
private static void startServer() throws NoSuchAlgorithmException,
KeyManagementException, Exception {
Server server = new Server();
QueuedThreadPool btp = new QueuedThreadPool();
server.setThreadPool(btp);
if (https) {
TrustManager[] tm =
;
SSLContext context = SSLContext.getInstance("SSL"); //$NON-NLS-1$
context.init(null, tm, null);
HttpsURLConnection.setDefaultSSLSocketFactory(context
.getSocketFactory());
SslSelectChannelConnector httpsConnector = new SslSelectChannelConnector();
httpsConnector.setPort(8080);
httpsConnector.setName("JettySSL");
httpsConnector.setKeystore("keystore");
httpsConnector.setPassword("password");
httpsConnector.setKeyPassword("password");
httpsConnector.setAcceptors(25);
httpsConnector.setAcceptQueueSize(2000);
httpsConnector.setAcceptorPriorityOffset(0);
httpsConnector.setMaxIdleTime(300000);
server.addConnector(httpsConnector);
} else {
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(8080);
connector.setName("Jetty");
connector.setAcceptors(25);
connector.setAcceptQueueSize(2000);
connector.setMaxIdleTime(300000);
connector.setLowResourcesConnections(100);
server.addConnector(connector);
}
ServletHandler handler = new ServletHandler();
handler.addServletWithMapping(Test.class.getName(), "/test");
server.addHandler(handler);
server.start();
}
private static class CredulousTM implements TrustManager, X509TrustManager {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
return;
}
public void checkServerTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException
Issue Links
- duplicates
-
JETTY-1036
Chunked transfer encoding and SSL
-
The problem was that some bytes had been read but not processed by the SSL engine.
Adding the lines to blockforcontent
if (_endp.isBufferingInput() && _parser.parseNext()>0)
continue;
fixes the issue.
committed and will be in the next release.
cheers