Merge branch 'master' into websocket-servlet-refactor

This commit is contained in:
Joakim Erdfelt 2011-12-14 11:15:20 -07:00
commit c8e43ede1c
16 changed files with 1325 additions and 279 deletions

View File

@ -1,4 +1,5 @@
package org.eclipse.jetty.http.spi; package org.eclipse.jetty.http.spi;
//======================================================================== //========================================================================
//Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd. //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. //You may elect to redistribute this code under either of these licenses.
//======================================================================== //========================================================================
import com.sun.net.httpserver.Authenticator; import java.io.IOException;
import com.sun.net.httpserver.Authenticator.Result; import java.io.PrintWriter;
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 javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; 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}. * Jetty handler that bridges requests to {@link HttpHandler}.
@ -36,7 +40,6 @@ public class HttpSpiContextHandler extends ContextHandler
private HttpHandler _httpHandler; private HttpHandler _httpHandler;
public HttpSpiContextHandler(HttpContext httpContext, HttpHandler httpHandler) public HttpSpiContextHandler(HttpContext httpContext, HttpHandler httpHandler)
{ {
this._httpContext = httpContext; this._httpContext = httpContext;
@ -46,9 +49,20 @@ public class HttpSpiContextHandler extends ContextHandler
@Override @Override
public void doScope(String target, Request baseRequest, HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException 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 // TODO: add filters processing
@ -56,11 +70,15 @@ public class HttpSpiContextHandler extends ContextHandler
{ {
Authenticator auth = _httpContext.getAuthenticator(); Authenticator auth = _httpContext.getAuthenticator();
if (auth != null) if (auth != null)
handleAuthentication(resp, jettyHttpExchange, auth); {
handleAuthentication(resp,jettyHttpExchange,auth);
}
else else
{
_httpHandler.handle(jettyHttpExchange); _httpHandler.handle(jettyHttpExchange);
}
} }
catch(Exception ex) catch (Exception ex)
{ {
PrintWriter writer = new PrintWriter(jettyHttpExchange.getResponseBody()); PrintWriter writer = new PrintWriter(jettyHttpExchange.getResponseBody());
@ -84,10 +102,9 @@ public class HttpSpiContextHandler extends ContextHandler
} }
private void handleAuthentication(HttpServletResponse resp, HttpExchange httpExchange, Authenticator auth) throws IOException
private void handleAuthentication(HttpServletResponse resp, JettyHttpExchange jettyHttpExchange, Authenticator auth) throws IOException
{ {
Result result = auth.authenticate(jettyHttpExchange); Result result = auth.authenticate(httpExchange);
if (result instanceof Authenticator.Failure) if (result instanceof Authenticator.Failure)
{ {
int rc = ((Authenticator.Failure)result).getResponseCode(); int rc = ((Authenticator.Failure)result).getResponseCode();
@ -101,8 +118,8 @@ public class HttpSpiContextHandler extends ContextHandler
else if (result instanceof Authenticator.Success) else if (result instanceof Authenticator.Success)
{ {
HttpPrincipal principal = ((Authenticator.Success)result).getPrincipal(); HttpPrincipal principal = ((Authenticator.Success)result).getPrincipal();
jettyHttpExchange.setPrincipal(principal); ((JettyExchange)httpExchange).setPrincipal(principal);
_httpHandler.handle(jettyHttpExchange); _httpHandler.handle(httpExchange);
} }
} }

View File

@ -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);
}

View File

@ -1,27 +1,23 @@
package org.eclipse.jetty.http.spi; // ========================================================================
// 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;
//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.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.URI; 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.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -31,197 +27,226 @@ import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpPrincipal; import com.sun.net.httpserver.HttpPrincipal;
/* ------------------------------------------------------------ */
/** /**
* Jetty implementation of {@link com.sun.net.httpserver.HttpExchange}
*/ */
public class JettyHttpExchange extends HttpExchange public class JettyHttpExchange extends HttpExchange implements JettyExchange
{ {
private JettyHttpExchangeDelegate _delegate;
private HttpContext _httpContext; public JettyHttpExchange(HttpContext jaxWsContext, HttpServletRequest req, HttpServletResponse resp)
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; super();
this._req = req; _delegate = new JettyHttpExchangeDelegate(jaxWsContext,req,resp);
this._resp = resp;
try
{
this._is = req.getInputStream();
this._os = resp.getOutputStream();
}
catch (IOException ex)
{
throw new RuntimeException(ex);
}
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#hashCode()
*/
@Override
public int hashCode()
{
return _delegate.hashCode();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getRequestHeaders()
*/
@Override @Override
public Headers getRequestHeaders() public Headers getRequestHeaders()
{ {
Headers headers = new Headers(); return _delegate.getRequestHeaders();
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;
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getResponseHeaders()
*/
@Override @Override
public Headers getResponseHeaders() public Headers getResponseHeaders()
{ {
return _responseHeaders; return _delegate.getResponseHeaders();
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getRequestURI()
*/
@Override @Override
public URI getRequestURI() public URI getRequestURI()
{ {
try return _delegate.getRequestURI();
{
String uriAsString = _req.getRequestURI();
if (_req.getQueryString() != null)
uriAsString += "?" + _req.getQueryString();
return new URI(uriAsString);
}
catch (URISyntaxException ex)
{
throw new RuntimeException(ex);
}
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getRequestMethod()
*/
@Override @Override
public String getRequestMethod() public String getRequestMethod()
{ {
return _req.getMethod(); return _delegate.getRequestMethod();
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getHttpContext()
*/
@Override @Override
public HttpContext getHttpContext() public HttpContext getHttpContext()
{ {
return _httpContext; return _delegate.getHttpContext();
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#close()
*/
@Override @Override
public void close() public void close()
{ {
try _delegate.close();
{
_resp.getOutputStream().close();
}
catch (IOException ex)
{
throw new RuntimeException(ex);
}
} }
/* ------------------------------------------------------------ */
/**
* @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 @Override
public InputStream getRequestBody() public InputStream getRequestBody()
{ {
return _is; return _delegate.getRequestBody();
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getResponseBody()
*/
@Override @Override
public OutputStream getResponseBody() public OutputStream getResponseBody()
{ {
return _os; return _delegate.getResponseBody();
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#sendResponseHeaders(int, long)
*/
@Override @Override
public void sendResponseHeaders(int rCode, long responseLength) public void sendResponseHeaders(int rCode, long responseLength) throws IOException
throws IOException
{ {
this._responseCode = rCode; _delegate.sendResponseHeaders(rCode,responseLength);
for (Map.Entry<String, List<String>> stringListEntry : _responseHeaders.entrySet())
{
String name = stringListEntry.getKey();
List<String> values = stringListEntry.getValue();
for (String value : values)
{
_resp.setHeader(name, value);
}
}
if (responseLength > 0)
_resp.setHeader("content-length", "" + responseLength);
_resp.setStatus(rCode);
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getRemoteAddress()
*/
@Override @Override
public InetSocketAddress getRemoteAddress() public InetSocketAddress getRemoteAddress()
{ {
return new InetSocketAddress(_req.getRemoteAddr(), _req.getRemotePort()); return _delegate.getRemoteAddress();
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getResponseCode()
*/
@Override @Override
public int getResponseCode() public int getResponseCode()
{ {
return _responseCode; return _delegate.getResponseCode();
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getLocalAddress()
*/
@Override @Override
public InetSocketAddress getLocalAddress() public InetSocketAddress getLocalAddress()
{ {
return new InetSocketAddress(_req.getLocalAddr(), _req.getLocalPort()); return _delegate.getLocalAddress();
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getProtocol()
*/
@Override @Override
public String getProtocol() public String getProtocol()
{ {
return _req.getProtocol(); return _delegate.getProtocol();
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getAttribute(java.lang.String)
*/
@Override @Override
public Object getAttribute(String name) public Object getAttribute(String name)
{ {
return _req.getAttribute(name); return _delegate.getAttribute(name);
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#setAttribute(java.lang.String, java.lang.Object)
*/
@Override @Override
public void setAttribute(String name, Object value) public void setAttribute(String name, Object value)
{ {
_req.setAttribute(name, value); _delegate.setAttribute(name,value);
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#setStreams(java.io.InputStream, java.io.OutputStream)
*/
@Override @Override
public void setStreams(InputStream i, OutputStream o) public void setStreams(InputStream i, OutputStream o)
{ {
_is = i; _delegate.setStreams(i,o);
_os = o;
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getPrincipal()
*/
@Override @Override
public HttpPrincipal getPrincipal() public HttpPrincipal getPrincipal()
{ {
return _httpPrincipal; return _delegate.getPrincipal();
} }
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#setPrincipal(com.sun.net.httpserver.HttpPrincipal)
*/
public void setPrincipal(HttpPrincipal principal) public void setPrincipal(HttpPrincipal principal)
{ {
this._httpPrincipal = principal; _delegate.setPrincipal(principal);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#toString()
*/
@Override
public String toString()
{
return _delegate.toString();
} }
} }

View File

@ -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<String, List<String>> stringListEntry : _responseHeaders.entrySet())
{
String name = stringListEntry.getKey();
List<String> 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;
}
}

View File

@ -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;
}
}

View File

@ -280,7 +280,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
public void checkIdleTimestamp(long now) public void checkIdleTimestamp(long now)
{ {
long idleTimestamp=_idleTimestamp; long idleTimestamp=_idleTimestamp;
if (!getChannel().isOpen() || idleTimestamp!=0 && _maxIdleTime>0 && now>(idleTimestamp+_maxIdleTime)) if (idleTimestamp!=0 && _maxIdleTime>0 && now>(idleTimestamp+_maxIdleTime))
{ {
onIdleExpired(); onIdleExpired();
_idleTimestamp=now; _idleTimestamp=now;

View File

@ -16,7 +16,6 @@ package org.eclipse.jetty.io.nio;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus; import javax.net.ssl.SSLEngineResult.HandshakeStatus;
@ -381,9 +380,10 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
} }
// pass on ishut/oshut state // pass on ishut/oshut state
if (!_inbound.hasContent() && _endp.isInputShutdown()) if (_endp.isOpen() && _endp.isInputShutdown() && !_inbound.hasContent())
_engine.closeInbound(); _engine.closeInbound();
if (!_outbound.hasContent() && _engine.isOutboundDone())
if (_endp.isOpen() && _engine.isOutboundDone() && !_outbound.hasContent())
_endp.shutdownOutput(); _endp.shutdownOutput();
some_progress|=progress; some_progress|=progress;
@ -570,11 +570,7 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
public String toString() public String toString()
{ {
Buffer i=_inbound; return String.format("%s | %s", super.toString(), _sslEndPoint);
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());
} }
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */

View File

@ -14,7 +14,9 @@
package org.eclipse.jetty.servlets; package org.eclipse.jetty.servlets;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Queue; import java.util.Queue;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -32,8 +34,10 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener; import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionEvent;
import org.eclipse.jetty.continuation.Continuation; import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationListener; import org.eclipse.jetty.continuation.ContinuationListener;
@ -908,12 +912,13 @@ public class DoSFilter implements Filter
* A RateTracker is associated with a connection, and stores request rate * A RateTracker is associated with a connection, and stores request rate
* data. * data.
*/ */
class RateTracker extends Timeout.Task implements HttpSessionBindingListener class RateTracker extends Timeout.Task implements HttpSessionBindingListener, HttpSessionActivationListener
{ {
protected final String _id; transient protected final String _id;
protected final int _type; transient protected final int _type;
protected final long[] _timestamps; transient protected final long[] _timestamps;
protected int _next; transient protected int _next;
public RateTracker(String id, int type,int maxRequestsPerSecond) public RateTracker(String id, int type,int maxRequestsPerSecond)
{ {
@ -954,24 +959,48 @@ public class DoSFilter implements Filter
public void valueBound(HttpSessionBindingEvent event) public void valueBound(HttpSessionBindingEvent event)
{ {
if (LOG.isDebugEnabled())
LOG.debug("Value bound:"+_id);
} }
public void valueUnbound(HttpSessionBindingEvent event) 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() public void expired()
{ {
long now = _trackerTimeoutQ.getNow(); if (_rateTrackers != null && _trackerTimeoutQ != null)
int latestIndex = _next == 0 ? 3 : (_next - 1 ) % _timestamps.length; {
long last=_timestamps[latestIndex]; long now = _trackerTimeoutQ.getNow();
boolean hasRecentRequest = last != 0 && (now-last)<1000L; int latestIndex = _next == 0 ? 3 : (_next - 1 ) % _timestamps.length;
long last=_timestamps[latestIndex];
boolean hasRecentRequest = last != 0 && (now-last)<1000L;
if (hasRecentRequest) if (hasRecentRequest)
reschedule(); reschedule();
else else
_rateTrackers.remove(_id); _rateTrackers.remove(_id);
}
} }
@Override @Override
@ -979,6 +1008,8 @@ public class DoSFilter implements Filter
{ {
return "RateTracker/"+_id+"/"+_type; return "RateTracker/"+_id+"/"+_type;
} }
} }
class FixedRateTracker extends RateTracker class FixedRateTracker extends RateTracker

View File

@ -46,6 +46,12 @@
<artifactId>jetty-test-helper</artifactId> <artifactId>jetty-test-helper</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -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();
}
}

View File

@ -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 <a
* href="https://jira.codehaus.org/browse/JETTY-1463">JETTY-1463</a>
*/
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);
}
}

View File

@ -2,6 +2,7 @@ package org.eclipse.jetty.websocket;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -121,4 +122,63 @@ public class WebSocketOverSSLTest
Assert.assertTrue(serverLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(serverLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(clientLatch.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);
}
} }

View File

@ -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<String> messages;
public CaptureSocket()
{
messages = new ArrayList<String>();
}
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;
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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<CaptureSocket> captures = new ArrayList<CaptureSocket>();;
@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;
}
}