diff --git a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/HttpSpiContextHandler.java b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/HttpSpiContextHandler.java
index 9f6198e8598..62c30cac169 100644
--- a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/HttpSpiContextHandler.java
+++ b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/HttpSpiContextHandler.java
@@ -1,4 +1,5 @@
package org.eclipse.jetty.http.spi;
+
//========================================================================
//Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
@@ -12,19 +13,22 @@ package org.eclipse.jetty.http.spi;
//You may elect to redistribute this code under either of these licenses.
//========================================================================
-import com.sun.net.httpserver.Authenticator;
-import com.sun.net.httpserver.Authenticator.Result;
-import com.sun.net.httpserver.HttpContext;
-import com.sun.net.httpserver.HttpHandler;
-import com.sun.net.httpserver.HttpPrincipal;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.ContextHandler;
+import java.io.IOException;
+import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.io.PrintWriter;
+
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.ContextHandler;
+
+import com.sun.net.httpserver.Authenticator;
+import com.sun.net.httpserver.Authenticator.Result;
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpPrincipal;
/**
* Jetty handler that bridges requests to {@link HttpHandler}.
@@ -36,7 +40,6 @@ public class HttpSpiContextHandler extends ContextHandler
private HttpHandler _httpHandler;
-
public HttpSpiContextHandler(HttpContext httpContext, HttpHandler httpHandler)
{
this._httpContext = httpContext;
@@ -46,9 +49,20 @@ public class HttpSpiContextHandler extends ContextHandler
@Override
public void doScope(String target, Request baseRequest, HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException
{
- if (!target.startsWith(getContextPath())) return;
+ if (!target.startsWith(getContextPath()))
+ {
+ return;
+ }
- JettyHttpExchange jettyHttpExchange = new JettyHttpExchange(_httpContext, req, resp);
+ HttpExchange jettyHttpExchange;
+ if (baseRequest.isSecure())
+ {
+ jettyHttpExchange = new JettyHttpsExchange(_httpContext,req,resp);
+ }
+ else
+ {
+ jettyHttpExchange = new JettyHttpExchange(_httpContext,req,resp);
+ }
// TODO: add filters processing
@@ -56,38 +70,41 @@ public class HttpSpiContextHandler extends ContextHandler
{
Authenticator auth = _httpContext.getAuthenticator();
if (auth != null)
- handleAuthentication(resp, jettyHttpExchange, auth);
+ {
+ handleAuthentication(resp,jettyHttpExchange,auth);
+ }
else
+ {
_httpHandler.handle(jettyHttpExchange);
+ }
}
- catch(Exception ex)
+ catch (Exception ex)
{
PrintWriter writer = new PrintWriter(jettyHttpExchange.getResponseBody());
-
+
resp.setStatus(500);
writer.println("
HTTP ERROR: 500
");
writer.println("INTERNAL_SERVER_ERROR
");
writer.println("RequestURI=" + req.getRequestURI() + "
");
-
+
writer.println("");
ex.printStackTrace(writer);
writer.println("
");
-
+
writer.println("Powered by jetty://
");
-
+
writer.close();
}
finally
{
baseRequest.setHandled(true);
}
-
+
}
-
- private void handleAuthentication(HttpServletResponse resp, JettyHttpExchange jettyHttpExchange, Authenticator auth) throws IOException
+ private void handleAuthentication(HttpServletResponse resp, HttpExchange httpExchange, Authenticator auth) throws IOException
{
- Result result = auth.authenticate(jettyHttpExchange);
+ Result result = auth.authenticate(httpExchange);
if (result instanceof Authenticator.Failure)
{
int rc = ((Authenticator.Failure)result).getResponseCode();
@@ -101,8 +118,8 @@ public class HttpSpiContextHandler extends ContextHandler
else if (result instanceof Authenticator.Success)
{
HttpPrincipal principal = ((Authenticator.Success)result).getPrincipal();
- jettyHttpExchange.setPrincipal(principal);
- _httpHandler.handle(jettyHttpExchange);
+ ((JettyExchange)httpExchange).setPrincipal(principal);
+ _httpHandler.handle(httpExchange);
}
}
diff --git a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyExchange.java b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyExchange.java
new file mode 100644
index 00000000000..f448c118185
--- /dev/null
+++ b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyExchange.java
@@ -0,0 +1,28 @@
+// ========================================================================
+// Copyright (c) 2009-2009 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.http.spi;
+
+import com.sun.net.httpserver.HttpPrincipal;
+
+/* ------------------------------------------------------------ */
+/**
+ */
+public interface JettyExchange
+{
+
+ HttpPrincipal getPrincipal();
+
+ void setPrincipal(HttpPrincipal principal);
+
+}
\ No newline at end of file
diff --git a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchange.java b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchange.java
index 9ce8e959d83..41809d036e3 100644
--- a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchange.java
+++ b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchange.java
@@ -1,227 +1,252 @@
-package org.eclipse.jetty.http.spi;
-
-//========================================================================
-//Copyright (c) 2004-2009 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.
-//========================================================================
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetSocketAddress;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Map;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import com.sun.net.httpserver.Headers;
-import com.sun.net.httpserver.HttpContext;
-import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpPrincipal;
-
-/**
- * Jetty implementation of {@link com.sun.net.httpserver.HttpExchange}
- */
-public class JettyHttpExchange extends HttpExchange
-{
-
- private HttpContext _httpContext;
-
- private HttpServletRequest _req;
-
- private HttpServletResponse _resp;
-
- private Headers _responseHeaders = new Headers();
-
- private int _responseCode = 0;
-
- private InputStream _is;
-
- private OutputStream _os;
-
- private HttpPrincipal _httpPrincipal;
-
-
- public JettyHttpExchange(HttpContext jaxWsContext , HttpServletRequest req,
- HttpServletResponse resp)
- {
- this._httpContext = jaxWsContext;
- this._req = req;
- this._resp = resp;
- try
- {
- this._is = req.getInputStream();
- this._os = resp.getOutputStream();
- }
- catch (IOException ex)
- {
- throw new RuntimeException(ex);
- }
- }
-
- @Override
- public Headers getRequestHeaders()
- {
- Headers headers = new Headers();
- Enumeration> en = _req.getHeaderNames();
- while (en.hasMoreElements())
- {
- String name = (String) en.nextElement();
- Enumeration> en2 = _req.getHeaders(name);
- while (en2.hasMoreElements())
- {
- String value = (String) en2.nextElement();
- headers.add(name, value);
- }
- }
- return headers;
- }
-
- @Override
- public Headers getResponseHeaders()
- {
- return _responseHeaders;
- }
-
- @Override
- public URI getRequestURI()
- {
- try
- {
- String uriAsString = _req.getRequestURI();
- if (_req.getQueryString() != null)
- uriAsString += "?" + _req.getQueryString();
-
- return new URI(uriAsString);
- }
- catch (URISyntaxException ex)
- {
- throw new RuntimeException(ex);
- }
- }
-
- @Override
- public String getRequestMethod()
- {
- return _req.getMethod();
- }
-
- @Override
- public HttpContext getHttpContext()
- {
- return _httpContext;
- }
-
- @Override
- public void close()
- {
- try
- {
- _resp.getOutputStream().close();
- }
- catch (IOException ex)
- {
- throw new RuntimeException(ex);
- }
- }
-
- @Override
- public InputStream getRequestBody()
- {
- return _is;
- }
-
- @Override
- public OutputStream getResponseBody()
- {
- return _os;
- }
-
- @Override
- public void sendResponseHeaders(int rCode, long responseLength)
- throws IOException
- {
- this._responseCode = rCode;
-
- for (Map.Entry> stringListEntry : _responseHeaders.entrySet())
- {
- String name = stringListEntry.getKey();
- List values = stringListEntry.getValue();
-
- for (String value : values)
- {
- _resp.setHeader(name, value);
- }
- }
- if (responseLength > 0)
- _resp.setHeader("content-length", "" + responseLength);
- _resp.setStatus(rCode);
- }
-
- @Override
- public InetSocketAddress getRemoteAddress()
- {
- return new InetSocketAddress(_req.getRemoteAddr(), _req.getRemotePort());
- }
-
- @Override
- public int getResponseCode()
- {
- return _responseCode;
- }
-
- @Override
- public InetSocketAddress getLocalAddress()
- {
- return new InetSocketAddress(_req.getLocalAddr(), _req.getLocalPort());
- }
-
- @Override
- public String getProtocol()
- {
- return _req.getProtocol();
- }
-
- @Override
- public Object getAttribute(String name)
- {
- return _req.getAttribute(name);
- }
-
- @Override
- public void setAttribute(String name, Object value)
- {
- _req.setAttribute(name, value);
- }
-
- @Override
- public void setStreams(InputStream i, OutputStream o)
- {
- _is = i;
- _os = o;
- }
-
- @Override
- public HttpPrincipal getPrincipal()
- {
- return _httpPrincipal;
- }
-
- public void setPrincipal(HttpPrincipal principal)
- {
- this._httpPrincipal = principal;
- }
-
-}
+// ========================================================================
+// Copyright (c) 2009-2009 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.http.spi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.URI;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.sun.net.httpserver.Headers;
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpPrincipal;
+
+/* ------------------------------------------------------------ */
+/**
+ */
+public class JettyHttpExchange extends HttpExchange implements JettyExchange
+{
+ private JettyHttpExchangeDelegate _delegate;
+
+ public JettyHttpExchange(HttpContext jaxWsContext, HttpServletRequest req, HttpServletResponse resp)
+ {
+ super();
+ _delegate = new JettyHttpExchangeDelegate(jaxWsContext,req,resp);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#hashCode()
+ */
+ @Override
+ public int hashCode()
+ {
+ return _delegate.hashCode();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#getRequestHeaders()
+ */
+ @Override
+ public Headers getRequestHeaders()
+ {
+ return _delegate.getRequestHeaders();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#getResponseHeaders()
+ */
+ @Override
+ public Headers getResponseHeaders()
+ {
+ return _delegate.getResponseHeaders();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#getRequestURI()
+ */
+ @Override
+ public URI getRequestURI()
+ {
+ return _delegate.getRequestURI();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#getRequestMethod()
+ */
+ @Override
+ public String getRequestMethod()
+ {
+ return _delegate.getRequestMethod();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#getHttpContext()
+ */
+ @Override
+ public HttpContext getHttpContext()
+ {
+ return _delegate.getHttpContext();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#close()
+ */
+ @Override
+ public void close()
+ {
+ _delegate.close();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ return _delegate.equals(obj);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#getRequestBody()
+ */
+ @Override
+ public InputStream getRequestBody()
+ {
+ return _delegate.getRequestBody();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#getResponseBody()
+ */
+ @Override
+ public OutputStream getResponseBody()
+ {
+ return _delegate.getResponseBody();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#sendResponseHeaders(int, long)
+ */
+ @Override
+ public void sendResponseHeaders(int rCode, long responseLength) throws IOException
+ {
+ _delegate.sendResponseHeaders(rCode,responseLength);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#getRemoteAddress()
+ */
+ @Override
+ public InetSocketAddress getRemoteAddress()
+ {
+ return _delegate.getRemoteAddress();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#getResponseCode()
+ */
+ @Override
+ public int getResponseCode()
+ {
+ return _delegate.getResponseCode();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#getLocalAddress()
+ */
+ @Override
+ public InetSocketAddress getLocalAddress()
+ {
+ return _delegate.getLocalAddress();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#getProtocol()
+ */
+ @Override
+ public String getProtocol()
+ {
+ return _delegate.getProtocol();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#getAttribute(java.lang.String)
+ */
+ @Override
+ public Object getAttribute(String name)
+ {
+ return _delegate.getAttribute(name);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#setAttribute(java.lang.String, java.lang.Object)
+ */
+ @Override
+ public void setAttribute(String name, Object value)
+ {
+ _delegate.setAttribute(name,value);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#setStreams(java.io.InputStream, java.io.OutputStream)
+ */
+ @Override
+ public void setStreams(InputStream i, OutputStream o)
+ {
+ _delegate.setStreams(i,o);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#getPrincipal()
+ */
+ @Override
+ public HttpPrincipal getPrincipal()
+ {
+ return _delegate.getPrincipal();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#setPrincipal(com.sun.net.httpserver.HttpPrincipal)
+ */
+ public void setPrincipal(HttpPrincipal principal)
+ {
+ _delegate.setPrincipal(principal);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.http.spi.JettyExchange#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return _delegate.toString();
+ }
+
+}
diff --git a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchangeDelegate.java b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchangeDelegate.java
new file mode 100644
index 00000000000..b772afbe598
--- /dev/null
+++ b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpExchangeDelegate.java
@@ -0,0 +1,228 @@
+package org.eclipse.jetty.http.spi;
+
+//========================================================================
+//Copyright (c) 2004-2009 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.
+//========================================================================
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.sun.net.httpserver.Headers;
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpPrincipal;
+
+/**
+ * Jetty implementation of {@link com.sun.net.httpserver.HttpExchange}
+ */
+public class JettyHttpExchangeDelegate extends HttpExchange
+{
+
+ private HttpContext _httpContext;
+
+ private HttpServletRequest _req;
+
+ private HttpServletResponse _resp;
+
+ private Headers _responseHeaders = new Headers();
+
+ private int _responseCode = 0;
+
+ private InputStream _is;
+
+ private OutputStream _os;
+
+ private HttpPrincipal _httpPrincipal;
+
+ JettyHttpExchangeDelegate(HttpContext jaxWsContext, HttpServletRequest req, HttpServletResponse resp)
+ {
+ this._httpContext = jaxWsContext;
+ this._req = req;
+ this._resp = resp;
+ try
+ {
+ this._is = req.getInputStream();
+ this._os = resp.getOutputStream();
+ }
+ catch (IOException ex)
+ {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ @Override
+ public Headers getRequestHeaders()
+ {
+ Headers headers = new Headers();
+ Enumeration> en = _req.getHeaderNames();
+ while (en.hasMoreElements())
+ {
+ String name = (String)en.nextElement();
+ Enumeration> en2 = _req.getHeaders(name);
+ while (en2.hasMoreElements())
+ {
+ String value = (String)en2.nextElement();
+ headers.add(name,value);
+ }
+ }
+ return headers;
+ }
+
+ @Override
+ public Headers getResponseHeaders()
+ {
+ return _responseHeaders;
+ }
+
+ @Override
+ public URI getRequestURI()
+ {
+ try
+ {
+ String uriAsString = _req.getRequestURI();
+ if (_req.getQueryString() != null)
+ {
+ uriAsString += "?" + _req.getQueryString();
+ }
+
+ return new URI(uriAsString);
+ }
+ catch (URISyntaxException ex)
+ {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ @Override
+ public String getRequestMethod()
+ {
+ return _req.getMethod();
+ }
+
+ @Override
+ public HttpContext getHttpContext()
+ {
+ return _httpContext;
+ }
+
+ @Override
+ public void close()
+ {
+ try
+ {
+ _resp.getOutputStream().close();
+ }
+ catch (IOException ex)
+ {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ @Override
+ public InputStream getRequestBody()
+ {
+ return _is;
+ }
+
+ @Override
+ public OutputStream getResponseBody()
+ {
+ return _os;
+ }
+
+ @Override
+ public void sendResponseHeaders(int rCode, long responseLength) throws IOException
+ {
+ this._responseCode = rCode;
+
+ for (Map.Entry> stringListEntry : _responseHeaders.entrySet())
+ {
+ String name = stringListEntry.getKey();
+ List values = stringListEntry.getValue();
+
+ for (String value : values)
+ {
+ _resp.setHeader(name,value);
+ }
+ }
+ if (responseLength > 0)
+ {
+ _resp.setHeader("content-length","" + responseLength);
+ }
+ _resp.setStatus(rCode);
+ }
+
+ @Override
+ public InetSocketAddress getRemoteAddress()
+ {
+ return new InetSocketAddress(_req.getRemoteAddr(),_req.getRemotePort());
+ }
+
+ @Override
+ public int getResponseCode()
+ {
+ return _responseCode;
+ }
+
+ @Override
+ public InetSocketAddress getLocalAddress()
+ {
+ return new InetSocketAddress(_req.getLocalAddr(),_req.getLocalPort());
+ }
+
+ @Override
+ public String getProtocol()
+ {
+ return _req.getProtocol();
+ }
+
+ @Override
+ public Object getAttribute(String name)
+ {
+ return _req.getAttribute(name);
+ }
+
+ @Override
+ public void setAttribute(String name, Object value)
+ {
+ _req.setAttribute(name,value);
+ }
+
+ @Override
+ public void setStreams(InputStream i, OutputStream o)
+ {
+ _is = i;
+ _os = o;
+ }
+
+ @Override
+ public HttpPrincipal getPrincipal()
+ {
+ return _httpPrincipal;
+ }
+
+ public void setPrincipal(HttpPrincipal principal)
+ {
+ this._httpPrincipal = principal;
+ }
+
+}
diff --git a/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpsExchange.java b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpsExchange.java
new file mode 100644
index 00000000000..f4af2b99ce9
--- /dev/null
+++ b/jetty-http-spi/src/main/java/org/eclipse/jetty/http/spi/JettyHttpsExchange.java
@@ -0,0 +1,179 @@
+// ========================================================================
+// Copyright (c) 2009-2009 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.http.spi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.URI;
+
+import javax.net.ssl.SSLSession;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.sun.net.httpserver.Headers;
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpPrincipal;
+import com.sun.net.httpserver.HttpsExchange;
+
+/* ------------------------------------------------------------ */
+/**
+ */
+public class JettyHttpsExchange extends HttpsExchange implements JettyExchange
+{
+ private JettyHttpExchangeDelegate _delegate;
+
+ public JettyHttpsExchange(HttpContext jaxWsContext, HttpServletRequest req, HttpServletResponse resp)
+ {
+ super();
+ _delegate = new JettyHttpExchangeDelegate(jaxWsContext,req,resp);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return _delegate.hashCode();
+ }
+
+ @Override
+ public Headers getRequestHeaders()
+ {
+ return _delegate.getRequestHeaders();
+ }
+
+ @Override
+ public Headers getResponseHeaders()
+ {
+ return _delegate.getResponseHeaders();
+ }
+
+ @Override
+ public URI getRequestURI()
+ {
+ return _delegate.getRequestURI();
+ }
+
+ @Override
+ public String getRequestMethod()
+ {
+ return _delegate.getRequestMethod();
+ }
+
+ @Override
+ public HttpContext getHttpContext()
+ {
+ return _delegate.getHttpContext();
+ }
+
+ @Override
+ public void close()
+ {
+ _delegate.close();
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ return _delegate.equals(obj);
+ }
+
+ @Override
+ public InputStream getRequestBody()
+ {
+ return _delegate.getRequestBody();
+ }
+
+ @Override
+ public OutputStream getResponseBody()
+ {
+ return _delegate.getResponseBody();
+ }
+
+ @Override
+ public void sendResponseHeaders(int rCode, long responseLength) throws IOException
+ {
+ _delegate.sendResponseHeaders(rCode,responseLength);
+ }
+
+ @Override
+ public InetSocketAddress getRemoteAddress()
+ {
+ return _delegate.getRemoteAddress();
+ }
+
+ @Override
+ public int getResponseCode()
+ {
+ return _delegate.getResponseCode();
+ }
+
+ @Override
+ public InetSocketAddress getLocalAddress()
+ {
+ return _delegate.getLocalAddress();
+ }
+
+ @Override
+ public String getProtocol()
+ {
+ return _delegate.getProtocol();
+ }
+
+ @Override
+ public Object getAttribute(String name)
+ {
+ return _delegate.getAttribute(name);
+ }
+
+ @Override
+ public void setAttribute(String name, Object value)
+ {
+ _delegate.setAttribute(name,value);
+ }
+
+ @Override
+ public void setStreams(InputStream i, OutputStream o)
+ {
+ _delegate.setStreams(i,o);
+ }
+
+ @Override
+ public HttpPrincipal getPrincipal()
+ {
+ return _delegate.getPrincipal();
+ }
+
+ public void setPrincipal(HttpPrincipal principal)
+ {
+ _delegate.setPrincipal(principal);
+ }
+
+ @Override
+ public String toString()
+ {
+ return _delegate.toString();
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see com.sun.net.httpserver.HttpsExchange#getSSLSession()
+ */
+ @Override
+ public SSLSession getSSLSession()
+ {
+ return null;
+ }
+
+}
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java
index cf69878278a..cc698d5e4e7 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectChannelEndPoint.java
@@ -262,25 +262,25 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
{
_idleTimestamp=check?System.currentTimeMillis():0;
}
-
+
/* ------------------------------------------------------------ */
public boolean isCheckForIdle()
{
return _idleTimestamp!=0;
}
-
+
/* ------------------------------------------------------------ */
protected void notIdle()
{
if (_idleTimestamp!=0)
_idleTimestamp=System.currentTimeMillis();
}
-
+
/* ------------------------------------------------------------ */
public void checkIdleTimestamp(long now)
{
long idleTimestamp=_idleTimestamp;
- if (!getChannel().isOpen() || idleTimestamp!=0 && _maxIdleTime>0 && now>(idleTimestamp+_maxIdleTime))
+ if (idleTimestamp!=0 && _maxIdleTime>0 && now>(idleTimestamp+_maxIdleTime))
{
onIdleExpired();
_idleTimestamp=now;
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslConnection.java
index 9bda79b9191..82d6fbeceb7 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslConnection.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslConnection.java
@@ -16,7 +16,6 @@ package org.eclipse.jetty.io.nio;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
-
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
@@ -381,9 +380,10 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
}
// pass on ishut/oshut state
- if (!_inbound.hasContent() && _endp.isInputShutdown())
+ if (_endp.isOpen() && _endp.isInputShutdown() && !_inbound.hasContent())
_engine.closeInbound();
- if (!_outbound.hasContent() && _engine.isOutboundDone())
+
+ if (_endp.isOpen() && _engine.isOutboundDone() && !_outbound.hasContent())
_endp.shutdownOutput();
some_progress|=progress;
@@ -570,11 +570,7 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
public String toString()
{
- Buffer i=_inbound;
- Buffer o=_outbound;
- Buffer u=_unwrapBuf;
-
- return super.toString()+"|"+_engine.getHandshakeStatus()+" i/u/o="+(i==null?0:i.length())+"/"+(u==null?0:u.length())+"/"+(o==null?0:o.length());
+ return String.format("%s | %s", super.toString(), _sslEndPoint);
}
/* ------------------------------------------------------------ */
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java
index 69bd7d89265..6c12e2e75b3 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/DoSFilter.java
@@ -14,7 +14,9 @@
package org.eclipse.jetty.servlets;
import java.io.IOException;
+import java.io.Serializable;
import java.util.HashSet;
+import java.util.Map;
import java.util.Queue;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
@@ -32,8 +34,10 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
+import javax.servlet.http.HttpSessionEvent;
import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationListener;
@@ -908,13 +912,14 @@ public class DoSFilter implements Filter
* A RateTracker is associated with a connection, and stores request rate
* data.
*/
- class RateTracker extends Timeout.Task implements HttpSessionBindingListener
+ class RateTracker extends Timeout.Task implements HttpSessionBindingListener, HttpSessionActivationListener
{
- protected final String _id;
- protected final int _type;
- protected final long[] _timestamps;
- protected int _next;
-
+ transient protected final String _id;
+ transient protected final int _type;
+ transient protected final long[] _timestamps;
+ transient protected int _next;
+
+
public RateTracker(String id, int type,int maxRequestsPerSecond)
{
_id = id;
@@ -953,25 +958,49 @@ public class DoSFilter implements Filter
public void valueBound(HttpSessionBindingEvent event)
- {
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Value bound:"+_id);
}
public void valueUnbound(HttpSessionBindingEvent event)
{
- _rateTrackers.remove(_id);
+ //take the tracker out of the list of trackers
+ if (_rateTrackers != null)
+ _rateTrackers.remove(_id);
+ if (LOG.isDebugEnabled()) LOG.debug("Tracker removed: "+_id);
}
+ public void sessionWillPassivate(HttpSessionEvent se)
+ {
+ //take the tracker of the list of trackers (if its still there)
+ //and ensure that we take ourselves out of the session so we are not saved
+ if (_rateTrackers != null)
+ _rateTrackers.remove(_id);
+ se.getSession().removeAttribute(__TRACKER);
+ if (LOG.isDebugEnabled()) LOG.debug("Value removed: "+_id);
+ }
+
+ public void sessionDidActivate(HttpSessionEvent se)
+ {
+ LOG.warn("Unexpected session activation");
+ }
+
+
public void expired()
{
- long now = _trackerTimeoutQ.getNow();
- int latestIndex = _next == 0 ? 3 : (_next - 1 ) % _timestamps.length;
- long last=_timestamps[latestIndex];
- boolean hasRecentRequest = last != 0 && (now-last)<1000L;
+ if (_rateTrackers != null && _trackerTimeoutQ != null)
+ {
+ long now = _trackerTimeoutQ.getNow();
+ int latestIndex = _next == 0 ? 3 : (_next - 1 ) % _timestamps.length;
+ long last=_timestamps[latestIndex];
+ boolean hasRecentRequest = last != 0 && (now-last)<1000L;
- if (hasRecentRequest)
- reschedule();
- else
- _rateTrackers.remove(_id);
+ if (hasRecentRequest)
+ reschedule();
+ else
+ _rateTrackers.remove(_id);
+ }
}
@Override
@@ -979,6 +1008,8 @@ public class DoSFilter implements Filter
{
return "RateTracker/"+_id+"/"+_type;
}
+
+
}
class FixedRateTracker extends RateTracker
diff --git a/jetty-websocket/pom.xml b/jetty-websocket/pom.xml
index 1bc5214b513..cfb4b91e581 100644
--- a/jetty-websocket/pom.xml
+++ b/jetty-websocket/pom.xml
@@ -46,6 +46,12 @@
jetty-test-helper
test
+
+ org.eclipse.jetty
+ jetty-servlet
+ ${project.version}
+ test
+
diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/SafariWebsocketDraft0Test.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/SafariWebsocketDraft0Test.java
new file mode 100644
index 00000000000..3d7f2864143
--- /dev/null
+++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/SafariWebsocketDraft0Test.java
@@ -0,0 +1,110 @@
+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.util.log.StdErrLog;
+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.Ignore;
+import org.junit.Test;
+
+public class SafariWebsocketDraft0Test
+{
+ 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.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();
+ context.addServlet(new ServletHolder(servlet),"/");
+
+ // 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
+ @Ignore
+ public void testSendTextMessages() throws Exception
+ {
+ SafariD00 safari = new SafariD00(serverUri);
+
+ try
+ {
+ safari.connect();
+ safari.issueHandshake();
+
+ // Send 5 short messages, using technique seen in Safari.
+ safari.sendMessage("aa-0"); // single msg
+ safari.sendMessage("aa-1", "aa-2", "aa-3", "aa-4");
+
+ // Servlet should show only 1 connection.
+ Assert.assertThat("Servlet.captureSockets.size",servlet.captures.size(),is(1));
+
+ CaptureSocket socket = servlet.captures.get(0);
+ Assert.assertThat("CaptureSocket",socket,notNullValue());
+ Assert.assertThat("CaptureSocket.isConnected", socket.isConnected(), is(true));
+
+ // Give servlet 500 millisecond to process messages
+ threadSleep(1,TimeUnit.SECONDS);
+ // Should have captured 5 messages.
+ Assert.assertThat("CaptureSocket.messages.size",socket.messages.size(),is(5));
+ }
+ 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/WebSocketCommTest.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketCommTest.java
new file mode 100644
index 00000000000..c2017f7c0a9
--- /dev/null
+++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketCommTest.java
@@ -0,0 +1,112 @@
+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.util.log.StdErrLog;
+import org.eclipse.jetty.websocket.helper.CaptureSocket;
+import org.eclipse.jetty.websocket.helper.MessageSender;
+import org.eclipse.jetty.websocket.helper.WebSocketCaptureServlet;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * WebSocketCommTest - to test reported undelivered messages in bug JETTY-1463
+ */
+public class WebSocketCommTest
+{
+ 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.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();
+ context.addServlet(new ServletHolder(servlet),"/");
+
+ // 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 testSendTextMessages() throws Exception
+ {
+ WebSocketClientFactory clientFactory = new WebSocketClientFactory();
+ clientFactory.start();
+
+ WebSocketClient wsc = clientFactory.newWebSocketClient();
+ MessageSender sender = new MessageSender();
+ wsc.open(serverUri,sender);
+
+ try
+ {
+ sender.awaitConnect();
+
+ // Send 5 short messages
+ for (int i = 0; i < 5; i++)
+ {
+ System.out.printf("Sending msg-%d%n",i);
+ sender.sendMessage("msg-%d",i);
+ }
+
+ // Servlet should show only 1 connection.
+ Assert.assertThat("Servlet.captureSockets.size",servlet.captures.size(),is(1));
+
+ CaptureSocket socket = servlet.captures.get(0);
+ Assert.assertThat("CaptureSocket",socket,notNullValue());
+ Assert.assertThat("CaptureSocket.isConnected", socket.isConnected(), is(true));
+
+ // Give servlet 500 millisecond to process messages
+ threadSleep(500,TimeUnit.MILLISECONDS);
+ // Should have captured 5 messages.
+ Assert.assertThat("CaptureSocket.messages.size",socket.messages.size(),is(5));
+ }
+ finally
+ {
+ System.out.println("Closing client socket");
+ sender.close();
+ }
+ }
+
+ public static void threadSleep(int dur, TimeUnit unit) throws InterruptedException
+ {
+ long ms = TimeUnit.MILLISECONDS.convert(dur,unit);
+ Thread.sleep(ms);
+ }
+}
diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketOverSSLTest.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketOverSSLTest.java
index b78baf44de9..a9e07a0b60d 100644
--- a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketOverSSLTest.java
+++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketOverSSLTest.java
@@ -2,6 +2,7 @@ package org.eclipse.jetty.websocket;
import java.io.IOException;
import java.net.URI;
+import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
@@ -121,4 +122,63 @@ public class WebSocketOverSSLTest
Assert.assertTrue(serverLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
}
+
+ @Test
+ public void testManyMessages() throws Exception
+ {
+ startServer(new WebSocket.OnTextMessage()
+ {
+ private Connection connection;
+
+ public void onOpen(Connection connection)
+ {
+ this.connection = connection;
+ }
+
+ public void onMessage(String data)
+ {
+ try
+ {
+ connection.sendMessage(data);
+ }
+ catch (IOException x)
+ {
+ x.printStackTrace();
+ }
+ }
+
+ public void onClose(int closeCode, String message)
+ {
+ }
+ });
+ int count = 1000;
+ final CountDownLatch clientLatch = new CountDownLatch(count);
+ startClient(new WebSocket.OnTextMessage()
+ {
+ public void onOpen(Connection connection)
+ {
+ }
+
+ public void onMessage(String data)
+ {
+ clientLatch.countDown();
+ }
+
+ public void onClose(int closeCode, String message)
+ {
+ }
+ });
+
+ char[] chars = new char[256];
+ Arrays.fill(chars, 'x');
+ String message = new String(chars);
+ for (int i = 0; i < count; ++i)
+ _connection.sendMessage(message);
+
+ Assert.assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
+
+ // While messages may have all arrived, the SSL close alert
+ // may be in the way so give some time for it to be processed.
+ TimeUnit.SECONDS.sleep(1);
+ }
}
diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/CaptureSocket.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/CaptureSocket.java
new file mode 100644
index 00000000000..45842596af3
--- /dev/null
+++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/CaptureSocket.java
@@ -0,0 +1,42 @@
+package org.eclipse.jetty.websocket.helper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.websocket.WebSocket;
+
+public class CaptureSocket implements WebSocket, WebSocket.OnTextMessage
+{
+ private Connection conn;
+ public List messages;
+
+ public CaptureSocket()
+ {
+ messages = new ArrayList();
+ }
+
+ public boolean isConnected()
+ {
+ if (conn == null)
+ {
+ return false;
+ }
+ return conn.isOpen();
+ }
+
+ public void onMessage(String data)
+ {
+ System.out.printf("Received Message \"%s\" [size %d]%n", data, data.length());
+ messages.add(data);
+ }
+
+ public void onOpen(Connection connection)
+ {
+ this.conn = connection;
+ }
+
+ public void onClose(int closeCode, String message)
+ {
+ this.conn = null;
+ }
+}
\ No newline at end of file
diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/MessageSender.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/MessageSender.java
new file mode 100644
index 00000000000..1a039ceb192
--- /dev/null
+++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/MessageSender.java
@@ -0,0 +1,52 @@
+package org.eclipse.jetty.websocket.helper;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.websocket.WebSocket;
+
+public class MessageSender implements WebSocket
+{
+ private Connection conn;
+ private CountDownLatch connectLatch = new CountDownLatch(1);
+
+ public void onOpen(Connection connection)
+ {
+ this.conn = connection;
+ connectLatch.countDown();
+ }
+
+ public void onClose(int closeCode, String message)
+ {
+ this.conn = null;
+ }
+
+ public boolean isConnected()
+ {
+ if (this.conn == null)
+ {
+ return false;
+ }
+ return this.conn.isOpen();
+ }
+
+ public void sendMessage(String format, Object... args) throws IOException
+ {
+ this.conn.sendMessage(String.format(format,args));
+ }
+
+ public void awaitConnect() throws InterruptedException
+ {
+ connectLatch.await(1,TimeUnit.SECONDS);
+ }
+
+ public void close()
+ {
+ if (this.conn == null)
+ {
+ return;
+ }
+ this.conn.close();
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 00000000000..a1370f092a0
--- /dev/null
+++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/SafariD00.java
@@ -0,0 +1,129 @@
+package org.eclipse.jetty.websocket.helper;
+
+import static org.hamcrest.Matchers.*;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.URI;
+
+import org.eclipse.jetty.io.ByteArrayBuffer;
+import org.eclipse.jetty.util.TypeUtil;
+import org.junit.Assert;
+
+public class SafariD00
+{
+ private URI uri;
+ private SocketAddress endpoint;
+ private Socket socket;
+ private OutputStream out;
+ private InputStream in;
+
+ public SafariD00(URI uri)
+ {
+ this.uri = uri;
+ this.endpoint = new InetSocketAddress(uri.getHost(),uri.getPort());
+ }
+
+ /**
+ * Open the Socket to the destination endpoint and
+ *
+ * @return the open java Socket.
+ * @throws IOException
+ */
+ public Socket connect() throws IOException
+ {
+ socket = new Socket();
+ socket.connect(endpoint,1000);
+
+ out = socket.getOutputStream();
+ in = socket.getInputStream();
+
+ return socket;
+ }
+
+ /**
+ * Issue an Http websocket (Draft-0) upgrade request using the Safari particulars.
+ *
+ * @throws UnsupportedEncodingException
+ */
+ public void issueHandshake() throws IOException
+ {
+ StringBuilder req = new StringBuilder();
+ req.append("GET ").append(uri.getPath()).append(" HTTP/1.1\r\n");
+ req.append("Upgrade: WebSocket\r\n");
+ req.append("Connection: Upgrade\r\n");
+ req.append("Host: ").append(uri.getHost()).append(":").append(uri.getPort()).append("\r\n");
+ req.append("Origin: http://www.google.com/\r\n");
+ req.append("Sec-WebSocket-Key1: 15{ft :6@87 0 M 5 c901\r\n");
+ req.append("Sec-WebSocket-Key2: 3? C;7~0 8 \" 3 2105 6 `_ {\r\n");
+ req.append("\r\n");
+
+ System.out.printf("--- Request ---%n%s",req);
+
+ byte reqBytes[] = req.toString().getBytes("UTF-8");
+ byte hixieBytes[] = TypeUtil.fromHexString("e739617916c9daf3");
+ byte buf[] = new byte[reqBytes.length + hixieBytes.length];
+ System.arraycopy(reqBytes,0,buf,0,reqBytes.length);
+ System.arraycopy(hixieBytes,0,buf,reqBytes.length,hixieBytes.length);
+
+ // Send HTTP GET Request (with hixie bytes)
+ out.write(buf,0,buf.length);
+ out.flush();
+
+ // Read HTTP 101 Upgrade / Handshake Response
+ InputStreamReader reader = new InputStreamReader(in);
+ BufferedReader br = new BufferedReader(reader);
+
+ boolean foundEnd = false;
+ String line;
+ while (!foundEnd)
+ {
+ line = br.readLine();
+ System.out.printf("RESP: %s%n",line);
+ if (line.length() == 0)
+ {
+ foundEnd = true;
+ }
+ }
+
+ // Read expected handshake hixie bytes
+ byte hixieHandshakeExpected[] = TypeUtil.fromHexString("c7438d956cf611a6af70603e6fa54809");
+ byte hixieHandshake[] = new byte[hixieHandshakeExpected.length];
+
+ int readLen = in.read(hixieHandshake,0,hixieHandshake.length);
+ Assert.assertThat("Read hixie handshake bytes",readLen,is(hixieHandshake.length));
+ }
+
+ public void sendMessage(String... msgs) throws IOException
+ {
+ int len = 0;
+ for (String msg : msgs)
+ {
+ len += (msg.length() + 2);
+ }
+
+ ByteArrayBuffer buf = new ByteArrayBuffer(len);
+
+ for (String msg : msgs)
+ {
+ buf.put((byte)0x00);
+ buf.put(msg.getBytes("UTF-8"));
+ buf.put((byte)0xFF);
+ }
+
+ out.write(buf.array());
+ out.flush();
+ }
+
+ public void disconnect() throws IOException
+ {
+ socket.close();
+ }
+}
diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/WebSocketCaptureServlet.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/WebSocketCaptureServlet.java
new file mode 100644
index 00000000000..706c4bd7eb4
--- /dev/null
+++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/helper/WebSocketCaptureServlet.java
@@ -0,0 +1,31 @@
+package org.eclipse.jetty.websocket.helper;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.websocket.WebSocket;
+import org.eclipse.jetty.websocket.WebSocketServlet;
+
+@SuppressWarnings("serial")
+public class WebSocketCaptureServlet extends WebSocketServlet
+{
+ public List captures = new ArrayList();;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ {
+ resp.sendError(404);
+ }
+
+ public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
+ {
+ CaptureSocket capture = new CaptureSocket();
+ captures.add(capture);
+ return capture;
+ }
+}
\ No newline at end of file