From 3d179bc418f31594edaadff2a8f57af0ea40c39a Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 21 Feb 2013 13:43:17 -0700 Subject: [PATCH 1/2] Making testcase JDK 1.5 compatible --- .../test/java/org/eclipse/jetty/servlets/ProxyServletTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ProxyServletTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ProxyServletTest.java index 74c762fe33a..e8e761fd1f8 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ProxyServletTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ProxyServletTest.java @@ -278,7 +278,7 @@ public class ProxyServletTest "2\r\n"+ "YZ\r\n"+ "0\r\n" - ).getBytes(StringUtil.__ISO_8859_1_CHARSET)); + ).getBytes(StringUtil.__ISO_8859_1)); String response=IO.toString(client.getInputStream()); From c7dd114cb62649d29b86424a2ce5182fddc209cf Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 21 Feb 2013 17:21:29 -0700 Subject: [PATCH 2/2] 401317 - Make Safari 5.x websocket support minVersion level error more clear + Making error message about minVersion configurable more clear on both the Logging on the server side, and the HTTP/1.1 400 error response line --- .../jetty/websocket/WebSocketFactory.java | 20 ++- .../websocket/WebSocketMinVersionTest.java | 119 ++++++++++++++++++ .../jetty/websocket/helper/SafariD00.java | 14 +++ 3 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketMinVersionTest.java diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketFactory.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketFactory.java index 1378956342f..7459887bb79 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketFactory.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketFactory.java @@ -230,6 +230,8 @@ public class WebSocketFactory extends AbstractLifeCycle // Old pre-RFC version specifications (header not present in RFC-6455) draft = request.getIntHeader("Sec-WebSocket-Draft"); } + // Remember requested version for possible error message later + int requestedVersion = draft; AbstractHttpConnection http = AbstractHttpConnection.getCurrentConnection(); if (http instanceof BlockingHttpConnection) throw new IllegalStateException("Websockets not supported on blocking connectors"); @@ -252,7 +254,7 @@ public class WebSocketFactory extends AbstractLifeCycle draft=Integer.MAX_VALUE; switch (draft) { - case -1: // unspecified draft/version + case -1: // unspecified draft/version (such as early OSX Safari 5.1 and iOS 5.x) case 0: // Old school draft/version { connection = new WebSocketServletConnectionD00(this, websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol); @@ -283,7 +285,6 @@ public class WebSocketFactory extends AbstractLifeCycle } default: { - LOG.warn("Unsupported Websocket version: " + draft); // Per RFC 6455 - 4.4 - Supporting Multiple Versions of WebSocket Protocol // Using the examples as outlined String versions="13"; @@ -295,7 +296,20 @@ public class WebSocketFactory extends AbstractLifeCycle versions+=", 0"; response.setHeader("Sec-WebSocket-Version", versions); - throw new HttpException(400, "Unsupported websocket version specification: " + draft); + + // Make error clear for developer / end-user + StringBuilder err = new StringBuilder(); + err.append("Unsupported websocket client version specification "); + if(requestedVersion >= 0) { + err.append("[").append(requestedVersion).append("]"); + } else { + err.append(""); + } + err.append(", configured minVersion [").append(_minVersion).append("]"); + err.append(", reported supported versions [").append(versions).append("]"); + LOG.warn(err.toString()); // Log it + // use spec language for unsupported versions + throw new HttpException(400, "Unsupported websocket version specification"); // Tell client } } diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketMinVersionTest.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketMinVersionTest.java new file mode 100644 index 00000000000..5fb4713c0f2 --- /dev/null +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketMinVersionTest.java @@ -0,0 +1,119 @@ +// +// ======================================================================== +// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.websocket; + +import static org.hamcrest.Matchers.*; + +import java.net.URI; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.websocket.helper.CaptureSocket; +import org.eclipse.jetty.websocket.helper.SafariD00; +import org.eclipse.jetty.websocket.helper.WebSocketCaptureServlet; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class WebSocketMinVersionTest +{ + private Server server; + private WebSocketCaptureServlet servlet; + private URI serverUri; + + @BeforeClass + public static void initLogging() + { + // Configure Logging + // System.setProperty("org.eclipse.jetty.util.log.class",StdErrLog.class.getName()); + System.setProperty("org.eclipse.jetty.websocket.helper.LEVEL","DEBUG"); + } + + @Before + public void startServer() throws Exception + { + // Configure Server + server = new Server(0); + + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + server.setHandler(context); + + // Serve capture servlet + servlet = new WebSocketCaptureServlet(); + ServletHolder holder = new ServletHolder(servlet); + holder.setInitParameter("minVersion","8"); + context.addServlet(holder,"/"); + + // Start Server + server.start(); + + Connector conn = server.getConnectors()[0]; + String host = conn.getHost(); + if (host == null) + { + host = "localhost"; + } + int port = conn.getLocalPort(); + serverUri = new URI(String.format("ws://%s:%d/",host,port)); + // System.out.printf("Server URI: %s%n",serverUri); + } + + @Test + public void testAttemptUpgrade() throws Exception + { + SafariD00 safari = new SafariD00(serverUri); + + try + { + safari.connect(); + safari.issueHandshake(); + Assert.fail("Expected upgrade failure"); + } + catch(IllegalStateException e) { + String respHeader = e.getMessage(); + Assert.assertThat("Response Header", respHeader, allOf( + containsString("HTTP/1.1 400 Unsupported"), + containsString("minVersion [8]"), + containsString("[13, 8]"))); + } + finally + { + // System.out.println("Closing client socket"); + safari.disconnect(); + } + } + + public static void threadSleep(int dur, TimeUnit unit) throws InterruptedException + { + long ms = TimeUnit.MILLISECONDS.convert(dur,unit); + Thread.sleep(ms); + } + + @After + public void stopServer() throws Exception + { + server.stop(); + } +} diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/SafariD00.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/SafariD00.java index d1a91e83ca4..ac3c4757d0b 100644 --- a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/SafariD00.java +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/SafariD00.java @@ -23,6 +23,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; +import java.net.ConnectException; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; @@ -35,6 +36,7 @@ import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.StdErrLog; import org.junit.Assert; +import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.is; public class SafariD00 @@ -109,12 +111,14 @@ public class SafariD00 // Read HTTP 101 Upgrade / Handshake Response InputStreamReader reader = new InputStreamReader(in); + StringBuilder respHeaders = new StringBuilder(); LOG.debug("Reading http headers"); int crlfs = 0; while (true) { int read = in.read(); + respHeaders.append((char)read); if (read == '\r' || read == '\n') ++crlfs; else @@ -122,6 +126,16 @@ public class SafariD00 if (crlfs == 4) break; } + + if(respHeaders.toString().startsWith("HTTP/1.1 101 ") == false) { + String respLine = respHeaders.toString(); + int idx = respLine.indexOf('\r'); + if(idx > 0) { + respLine = respLine.substring(0,idx); + } + LOG.debug("Response Headers: {}",respHeaders.toString()); + throw new IllegalStateException(respLine); + } // Read expected handshake hixie bytes byte hixieHandshakeExpected[] = TypeUtil.fromHexString("c7438d956cf611a6af70603e6fa54809");