Merge branch 'jetty-9' of ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project into jetty-9

This commit is contained in:
Jesse McConnell 2012-08-21 15:51:17 -05:00
commit 71dd5322c7
37 changed files with 1221 additions and 576 deletions

View File

@ -236,10 +236,8 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
@Override
protected void doStop() throws Exception
{
_constraintMap.clear();
_constraintMappings.clear();
_roles.clear();
super.doStop();
_constraintMap.clear();
}
protected void processConstraintMapping(ConstraintMapping mapping)

View File

@ -43,6 +43,7 @@ import org.eclipse.jetty.server.Authentication.User;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.server.session.HashedSession;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
@ -139,6 +140,7 @@ public class FormAuthenticator extends LoginAuthenticator
}
/* ------------------------------------------------------------ */
@Override
public String getAuthMethod()
{
return Constraint.__FORM_AUTH;
@ -182,6 +184,7 @@ public class FormAuthenticator extends LoginAuthenticator
}
/* ------------------------------------------------------------ */
@Override
public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException
{
HttpServletRequest request = (HttpServletRequest)req;
@ -206,31 +209,36 @@ public class FormAuthenticator extends LoginAuthenticator
{
final String username = request.getParameter(__J_USERNAME);
final String password = request.getParameter(__J_PASSWORD);
UserIdentity user = _loginService.login(username,password);
LOG.debug("jsecuritycheck {} {}",username,user);
if (user!=null)
{
session=renewSession(request,response);
// Redirect to original request
String nuri;
FormAuthentication form_auth;
synchronized(session)
{
nuri = (String) session.getAttribute(__J_URI);
}
if (nuri == null || nuri.length() == 0)
{
nuri = request.getContextPath();
if (nuri.length() == 0)
nuri = URIUtil.SLASH;
if (nuri == null || nuri.length() == 0)
{
nuri = request.getContextPath();
if (nuri.length() == 0)
nuri = URIUtil.SLASH;
}
Authentication cached=new SessionAuthentication(getAuthMethod(),user,password);
session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached);
form_auth = new FormAuthentication(getAuthMethod(),user);
}
LOG.debug("authenticated {}->{}",form_auth,nuri);
response.setContentLength(0);
response.sendRedirect(response.encodeRedirectURL(nuri));
Authentication cached=new SessionAuthentication(getAuthMethod(),user,password);
session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached);
return new FormAuthentication(getAuthMethod(),user);
return form_auth;
}
// not authenticated
@ -238,11 +246,13 @@ public class FormAuthenticator extends LoginAuthenticator
LOG.debug("Form authentication FAILED for " + StringUtil.printable(username));
if (_formErrorPage == null)
{
LOG.debug("auth failed {}->403",username);
if (response != null)
response.sendError(HttpServletResponse.SC_FORBIDDEN);
}
else if (_dispatch)
{
LOG.debug("auth failed {}=={}",username,_formErrorPage);
RequestDispatcher dispatcher = request.getRequestDispatcher(_formErrorPage);
response.setHeader(HttpHeader.CACHE_CONTROL.asString(),HttpHeaderValue.NO_CACHE.asString());
response.setDateHeader(HttpHeader.EXPIRES.asString(),1);
@ -250,6 +260,7 @@ public class FormAuthenticator extends LoginAuthenticator
}
else
{
LOG.debug("auth failed {}->{}",username,_formErrorPage);
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(),_formErrorPage)));
}
@ -265,43 +276,51 @@ public class FormAuthenticator extends LoginAuthenticator
_loginService!=null &&
!_loginService.validate(((Authentication.User)authentication).getUserIdentity()))
{
LOG.debug("auth revoked {}",authentication);
session.removeAttribute(SessionAuthentication.__J_AUTHENTICATED);
}
else
{
String j_uri=(String)session.getAttribute(__J_URI);
if (j_uri!=null)
synchronized (session)
{
MultiMap j_post = (MultiMap)session.getAttribute(__J_POST);
if (j_post!=null)
String j_uri=(String)session.getAttribute(__J_URI);
if (j_uri!=null)
{
StringBuffer buf = request.getRequestURL();
if (request.getQueryString() != null)
buf.append("?").append(request.getQueryString());
if (j_uri.equals(buf.toString()))
LOG.debug("auth retry {}->{}",authentication,j_uri);
MultiMap<String> j_post = (MultiMap<String>)session.getAttribute(__J_POST);
if (j_post!=null)
{
// This is a retry of an original POST request
// so restore method and parameters
LOG.debug("auth rePOST {}->{}",authentication,j_uri);
StringBuffer buf = request.getRequestURL();
if (request.getQueryString() != null)
buf.append("?").append(request.getQueryString());
session.removeAttribute(__J_POST);
Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
base_request.setMethod(HttpMethod.POST,HttpMethod.POST.asString());
base_request.setParameters(j_post);
if (j_uri.equals(buf.toString()))
{
// This is a retry of an original POST request
// so restore method and parameters
session.removeAttribute(__J_POST);
Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
base_request.setMethod(HttpMethod.POST,HttpMethod.POST.asString());
base_request.setParameters(j_post);
}
}
else
session.removeAttribute(__J_URI);
}
else
session.removeAttribute(__J_URI);
}
LOG.debug("auth {}",authentication);
return authentication;
}
}
// if we can't send challenge
if (DeferredAuthentication.isDeferred(response))
{
LOG.debug("auth deferred {}",session.getId());
return Authentication.UNAUTHENTICATED;
}
// remember the current URI
synchronized (session)
@ -318,7 +337,7 @@ public class FormAuthenticator extends LoginAuthenticator
{
Request base_request = (req instanceof Request)?(Request)req:HttpChannel.getCurrentHttpChannel().getRequest();
base_request.extractParameters();
session.setAttribute(__J_POST, new MultiMap(base_request.getParameters()));
session.setAttribute(__J_POST, new MultiMap<String>(base_request.getParameters()));
}
}
}
@ -326,6 +345,7 @@ public class FormAuthenticator extends LoginAuthenticator
// send the the challenge
if (_dispatch)
{
LOG.debug("challenge {}=={}",session.getId(),_formLoginPage);
RequestDispatcher dispatcher = request.getRequestDispatcher(_formLoginPage);
response.setHeader(HttpHeader.CACHE_CONTROL.asString(),HttpHeaderValue.NO_CACHE.asString());
response.setDateHeader(HttpHeader.EXPIRES.asString(),1);
@ -333,11 +353,10 @@ public class FormAuthenticator extends LoginAuthenticator
}
else
{
LOG.debug("challenge {}->{}",session.getId(),_formLoginPage);
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(),_formLoginPage)));
}
return Authentication.SEND_CONTINUE;
}
catch (IOException | ServletException e)
{
@ -366,6 +385,7 @@ public class FormAuthenticator extends LoginAuthenticator
}
/* ------------------------------------------------------------ */
@Override
public boolean secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, User validatedUser) throws ServerAuthException
{
return true;

View File

@ -26,9 +26,15 @@ import org.eclipse.jetty.security.Authenticator;
import org.eclipse.jetty.security.IdentityService;
import org.eclipse.jetty.security.LoginService;
import org.eclipse.jetty.server.session.AbstractSessionManager;
import org.eclipse.jetty.server.session.HashSessionManager;
import org.eclipse.jetty.server.session.HashedSession;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public abstract class LoginAuthenticator implements Authenticator
{
{
private static final Logger LOG = Log.getLogger(LoginAuthenticator.class);
protected final DeferredAuthentication _deferred=new DeferredAuthentication(this);
protected LoginService _loginService;
protected IdentityService _identityService;
@ -67,14 +73,19 @@ public abstract class LoginAuthenticator implements Authenticator
protected HttpSession renewSession(HttpServletRequest request, HttpServletResponse response)
{
HttpSession httpSession = request.getSession(false);
//if we should renew sessions, and there is an existing session that may have been seen by non-authenticated users
//(indicated by SESSION_SECURED not being set on the session) then we should change id
if (_renewSession && httpSession!=null && httpSession.getAttribute(AbstractSessionManager.SESSION_KNOWN_ONLY_TO_AUTHENTICATED)!=Boolean.TRUE)
if (_renewSession && httpSession!=null)
{
synchronized (this)
synchronized (httpSession)
{
httpSession = AbstractSessionManager.renewSession(request, httpSession,true);
//if we should renew sessions, and there is an existing session that may have been seen by non-authenticated users
//(indicated by SESSION_SECURED not being set on the session) then we should change id
if (httpSession.getAttribute(AbstractSessionManager.SESSION_KNOWN_ONLY_TO_AUTHENTICATED)!=Boolean.TRUE)
{
HttpSession newSession = AbstractSessionManager.renewSession(request, httpSession,true);
LOG.debug("renew {}->{}",httpSession.getId(),newSession.getId());
httpSession=newSession;
}
}
}
return httpSession;

View File

@ -114,23 +114,27 @@ public class SessionAuthentication implements Authentication.User, Serializable,
@Override
public String toString()
{
return "Session"+super.toString();
return String.format("%s@%x{%s,%s}",this.getClass().getSimpleName(),hashCode(),_session==null?"-":_session.getId(),_userIdentity);
}
@Override
public void sessionWillPassivate(HttpSessionEvent se)
{
}
@Override
public void sessionDidActivate(HttpSessionEvent se)
{
if (_session==null)
_session=se.getSession();
}
@Override
public void valueBound(HttpSessionBindingEvent event)
{
}
@Override
public void valueUnbound(HttpSessionBindingEvent event)
{
doLogout();

View File

@ -34,6 +34,7 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.security.authentication.BasicAuthenticator;
import org.eclipse.jetty.security.authentication.FormAuthenticator;
import org.eclipse.jetty.security.authentication.LoginAuthenticator;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Request;
@ -44,6 +45,7 @@ import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.util.B64Code;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.security.Password;
import org.junit.After;
@ -777,9 +779,8 @@ public class ConstraintTest
_security.setHandler(check);
_security.setAuthenticator(new BasicAuthenticator());
_security.setStrict(false);
_server.start();
System.out.println(_server.dump());
_server.start();
String response;
response = _connector.getResponses("GET /ctx/noauth/info HTTP/1.0\r\n\r\n", 100000, TimeUnit.MILLISECONDS);
@ -792,40 +793,12 @@ public class ConstraintTest
_server.stop();
/*
* FIXME: this seems to indicate there is an issue with the way the server is stopping and starting now
*
* Note that ConstraintSecurityHandler loses all of its brains when the server starts and stops, but that
* change was made in 2/2011 and this wasn't exposed til now, which seems to indicate that previously
* when the server stopped that doStop() didn't make it down to the constraint handler...and now it does.
*
* also, seems to be an issue in local connector, I had to add a new one for it to be able to work here as well
* so issues in stop/start there as well
*/
_connector = new LocalConnector(_server);
_server.setConnectors(new Connector[]{_connector});
ContextHandler _context = new ContextHandler();
SessionHandler _session = new SessionHandler();
_context.setContextPath("/ctx");
_server.setHandler(_context);
_context.setHandler(_session);
_security = new ConstraintSecurityHandler();
_session.setHandler(_security);
RequestHandler _handler = new RequestHandler();
_security.setHandler(_handler);
RoleRefHandler roleref = new RoleRefHandler();
roleref.setHandler(_security.getHandler());
_security.setHandler(roleref);
roleref.setHandler(check);
_security.setConstraintMappings(getConstraintMappings(),getKnownRoles());
_server.start();
System.out.println(_server.dump());
_server.start();
response = _connector.getResponses("GET /ctx/auth/info HTTP/1.0\r\n" +
"Authorization: Basic " + B64Code.encode("user2:password") + "\r\n" +
@ -879,6 +852,7 @@ public class ConstraintTest
}
private class RequestHandler extends AbstractHandler
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException
{
baseRequest.setHandled(true);
@ -910,16 +884,19 @@ public class ConstraintTest
UserIdentity.Scope scope = new UserIdentity.Scope()
{
@Override
public String getContextPath()
{
return "/";
}
@Override
public String getName()
{
return "someServlet";
}
@Override
public Map<String, String> getRoleRefMap()
{
Map<String, String> map = new HashMap<>();
@ -943,6 +920,7 @@ public class ConstraintTest
private class RoleCheckHandler extends AbstractHandler
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException
{
((Request) request).setHandled(true);

View File

@ -0,0 +1,68 @@
//
// ========================================================================
// Copyright (c) 1995-2012 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.server;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import org.eclipse.jetty.util.ByteArrayOutputStream2;
/**
*
*/
public class EncodingHttpWriter extends HttpWriter
{
final Writer _converter;
/* ------------------------------------------------------------ */
public EncodingHttpWriter(HttpOutput out, String encoding)
{
super(out);
try
{
_converter = new OutputStreamWriter(_bytes, encoding);
}
catch (UnsupportedEncodingException e)
{
throw new RuntimeException(e);
}
}
/* ------------------------------------------------------------ */
@Override
public void write (char[] s,int offset, int length) throws IOException
{
if (length==0)
_out.checkAllWritten();
while (length > 0)
{
_bytes.reset();
int chars = length>MAX_OUTPUT_CHARS?MAX_OUTPUT_CHARS:length;
_converter.write(s, offset, chars);
_converter.flush();
_bytes.writeTo(_out);
length-=chars;
offset+=chars;
}
}
}

View File

@ -433,7 +433,23 @@ public abstract class HttpChannel
else
_uri.parse(uri);
_request.setUri(_uri);
_request.setPathInfo(_uri.getDecodedPath());
String path = null;
try
{
path = _uri.getDecodedPath();
}
catch (Exception e)
{
LOG.warn("Failed UTF-8 decode for request path, trying ISO-8859-1");
LOG.ignore(e);
path = _uri.getDecodedPath(StringUtil.__ISO_8859_1);
}
String info=URIUtil.canonicalPath(path);
if (info==null)
info="/";
_request.setPathInfo(info);
_version=version==null?HttpVersion.HTTP_0_9:version;
_request.setHttpVersion(_version);

View File

@ -40,13 +40,7 @@ public class HttpOutput extends ServletOutputStream
{
private final HttpChannel _channel;
private boolean _closed;
// These are held here for reuse by Writer
String _characterEncoding;
Writer _converter;
char[] _chars;
ByteArrayOutputStream2 _bytes;
long _written;
private long _written;
/* ------------------------------------------------------------ */
public HttpOutput(HttpChannel channel)

View File

@ -19,61 +19,27 @@
package org.eclipse.jetty.server;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import org.eclipse.jetty.util.ByteArrayOutputStream2;
import org.eclipse.jetty.util.StringUtil;
/** OutputWriter.
* A writer that can wrap a {@link HttpOutput} stream and provide
* character encodings.
*
* The UTF-8 encoding is done by this class and no additional
* buffers or Writers are used.
* The UTF-8 code was inspired by http://javolution.org
/**
*
*/
public class HttpWriter extends Writer
public abstract class HttpWriter extends Writer
{
public static final int MAX_OUTPUT_CHARS = 512;
private static final int WRITE_CONV = 0;
private static final int WRITE_ISO1 = 1;
private static final int WRITE_UTF8 = 2;
final HttpOutput _out;
int _writeMode;
int _surrogate;
final ByteArrayOutputStream2 _bytes;
final char[] _chars;
/* ------------------------------------------------------------ */
public HttpWriter(HttpOutput out, String encoding)
public HttpWriter(HttpOutput out)
{
_out=out;
_surrogate=0; // AS lastUTF16CodePoint
setCharacterEncoding(encoding);
}
/* ------------------------------------------------------------ */
public void setCharacterEncoding(String encoding)
{
if (encoding == null || StringUtil.__ISO_8859_1.equalsIgnoreCase(encoding))
{
_writeMode = WRITE_ISO1;
}
else if (StringUtil.__UTF8.equalsIgnoreCase(encoding))
{
_writeMode = WRITE_UTF8;
}
else
{
_writeMode = WRITE_CONV;
if (_out._characterEncoding == null || !_out._characterEncoding.equalsIgnoreCase(encoding))
_out._converter = null; // Set lazily in getConverter()
}
_out._characterEncoding = encoding;
if (_out._bytes==null)
_out._bytes = new ByteArrayOutputStream2(MAX_OUTPUT_CHARS);
_chars=new char[MAX_OUTPUT_CHARS];
_bytes = new ByteArrayOutputStream2(MAX_OUTPUT_CHARS);
}
/* ------------------------------------------------------------ */
@ -101,202 +67,14 @@ public class HttpWriter extends Writer
length -= MAX_OUTPUT_CHARS;
}
if (_out._chars==null)
{
_out._chars = new char[MAX_OUTPUT_CHARS];
}
char[] chars = _out._chars;
s.getChars(offset, offset + length, chars, 0);
write(chars, 0, length);
s.getChars(offset, offset + length, _chars, 0);
write(_chars, 0, length);
}
/* ------------------------------------------------------------ */
@Override
public void write (char[] s,int offset, int length) throws IOException
{
HttpOutput out = _out;
if (length==0)
out.checkAllWritten();
while (length > 0)
{
out._bytes.reset();
int chars = length>MAX_OUTPUT_CHARS?MAX_OUTPUT_CHARS:length;
switch (_writeMode)
{
case WRITE_CONV:
{
Writer converter=getConverter();
converter.write(s, offset, chars);
converter.flush();
}
break;
case WRITE_ISO1:
{
byte[] buffer=out._bytes.getBuf();
int bytes=out._bytes.getCount();
if (chars>buffer.length-bytes)
chars=buffer.length-bytes;
for (int i = 0; i < chars; i++)
{
int c = s[offset+i];
buffer[bytes++]=(byte)(c<256?c:'?'); // ISO-1 and UTF-8 match for 0 - 255
}
if (bytes>=0)
out._bytes.setCount(bytes);
break;
}
case WRITE_UTF8:
{
byte[] buffer=out._bytes.getBuf();
int bytes=out._bytes.getCount();
if (bytes+chars>buffer.length)
chars=buffer.length-bytes;
for (int i = 0; i < chars; i++)
{
int code = s[offset+i];
// Do we already have a surrogate?
if(_surrogate==0)
{
// No - is this char code a surrogate?
if(Character.isHighSurrogate((char)code))
{
_surrogate=code; // UCS-?
continue;
}
}
// else handle a low surrogate
else if(Character.isLowSurrogate((char)code))
{
code = Character.toCodePoint((char)_surrogate, (char)code); // UCS-4
}
// else UCS-2
else
{
code=_surrogate; // UCS-2
_surrogate=0; // USED
i--;
}
if ((code & 0xffffff80) == 0)
{
// 1b
if (bytes>=buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(code);
}
else
{
if((code&0xfffff800)==0)
{
// 2b
if (bytes+2>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xc0|(code>>6));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
}
else if((code&0xffff0000)==0)
{
// 3b
if (bytes+3>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xe0|(code>>12));
buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
}
else if((code&0xff200000)==0)
{
// 4b
if (bytes+4>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xf0|(code>>18));
buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
}
else if((code&0xf4000000)==0)
{
// 5b
if (bytes+5>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xf8|(code>>24));
buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
}
else if((code&0x80000000)==0)
{
// 6b
if (bytes+6>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xfc|(code>>30));
buffer[bytes++]=(byte)(0x80|((code>>24)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
}
else
{
buffer[bytes++]=(byte)('?');
}
_surrogate=0; // USED
if (bytes==buffer.length)
{
chars=i+1;
break;
}
}
}
out._bytes.setCount(bytes);
break;
}
default:
throw new IllegalStateException();
}
out._bytes.writeTo(out);
length-=chars;
offset+=chars;
}
{
throw new AbstractMethodError();
}
/* ------------------------------------------------------------ */
private Writer getConverter() throws IOException
{
if (_out._converter == null)
_out._converter = new OutputStreamWriter(_out._bytes, _out._characterEncoding);
return _out._converter;
}
}

View File

@ -0,0 +1,70 @@
//
// ========================================================================
// Copyright (c) 1995-2012 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.server;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import org.eclipse.jetty.util.ByteArrayOutputStream2;
import org.eclipse.jetty.util.StringUtil;
/**
*/
public class Iso88591HttpWriter extends HttpWriter
{
/* ------------------------------------------------------------ */
public Iso88591HttpWriter(HttpOutput out)
{
super(out);
}
/* ------------------------------------------------------------ */
@Override
public void write (char[] s,int offset, int length) throws IOException
{
HttpOutput out = _out;
if (length==0)
out.checkAllWritten();
while (length > 0)
{
_bytes.reset();
int chars = length>MAX_OUTPUT_CHARS?MAX_OUTPUT_CHARS:length;
byte[] buffer=_bytes.getBuf();
int bytes=_bytes.getCount();
if (chars>buffer.length-bytes)
chars=buffer.length-bytes;
for (int i = 0; i < chars; i++)
{
int c = s[offset+i];
buffer[bytes++]=(byte)(c<256?c:'?');
}
if (bytes>=0)
_bytes.setCount(bytes);
_bytes.writeTo(out);
length-=chars;
offset+=chars;
}
}
}

View File

@ -802,25 +802,19 @@ public class Response implements HttpServletResponse
setCharacterEncoding(encoding);
}
/* construct Writer using correct encoding */
// TODO switch on encoding here
_writer = new PrintWriter(new HttpWriter(_out,encoding))
if (encoding == null || StringUtil.__ISO_8859_1.equalsIgnoreCase(encoding))
{
public void close()
{
synchronized (lock)
{
try
{
out.close();
}
catch (IOException e)
{
setError();
}
}
}
};
_writer = new PrintWriter(new Iso88591HttpWriter(_out));
}
else if (StringUtil.__UTF8.equalsIgnoreCase(encoding))
{
_writer = new PrintWriter(new Utf8HttpWriter(_out));
}
else
{
_writer = new PrintWriter(new EncodingHttpWriter(_out,encoding));
}
}
_outputState=OutputState.WRITER;
return _writer;

View File

@ -0,0 +1,190 @@
//
// ========================================================================
// Copyright (c) 1995-2012 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.server;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import org.eclipse.jetty.util.ByteArrayOutputStream2;
import org.eclipse.jetty.util.StringUtil;
/** OutputWriter.
* A writer that can wrap a {@link HttpOutput} stream and provide
* character encodings.
*
* The UTF-8 encoding is done by this class and no additional
* buffers or Writers are used.
* The UTF-8 code was inspired by http://javolution.org
*/
public class Utf8HttpWriter extends HttpWriter
{
int _surrogate=0;
/* ------------------------------------------------------------ */
public Utf8HttpWriter(HttpOutput out)
{
super(out);
}
/* ------------------------------------------------------------ */
@Override
public void write (char[] s,int offset, int length) throws IOException
{
HttpOutput out = _out;
if (length==0)
out.checkAllWritten();
while (length > 0)
{
_bytes.reset();
int chars = length>MAX_OUTPUT_CHARS?MAX_OUTPUT_CHARS:length;
byte[] buffer=_bytes.getBuf();
int bytes=_bytes.getCount();
if (bytes+chars>buffer.length)
chars=buffer.length-bytes;
for (int i = 0; i < chars; i++)
{
int code = s[offset+i];
// Do we already have a surrogate?
if(_surrogate==0)
{
// No - is this char code a surrogate?
if(Character.isHighSurrogate((char)code))
{
_surrogate=code; // UCS-?
continue;
}
}
// else handle a low surrogate
else if(Character.isLowSurrogate((char)code))
{
code = Character.toCodePoint((char)_surrogate, (char)code); // UCS-4
}
// else UCS-2
else
{
code=_surrogate; // UCS-2
_surrogate=0; // USED
i--;
}
if ((code & 0xffffff80) == 0)
{
// 1b
if (bytes>=buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(code);
}
else
{
if((code&0xfffff800)==0)
{
// 2b
if (bytes+2>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xc0|(code>>6));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
}
else if((code&0xffff0000)==0)
{
// 3b
if (bytes+3>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xe0|(code>>12));
buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
}
else if((code&0xff200000)==0)
{
// 4b
if (bytes+4>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xf0|(code>>18));
buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
}
else if((code&0xf4000000)==0)
{
// 5b
if (bytes+5>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xf8|(code>>24));
buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
}
else if((code&0x80000000)==0)
{
// 6b
if (bytes+6>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xfc|(code>>30));
buffer[bytes++]=(byte)(0x80|((code>>24)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
}
else
{
buffer[bytes++]=(byte)('?');
}
_surrogate=0; // USED
if (bytes==buffer.length)
{
chars=i+1;
break;
}
}
}
_bytes.setCount(bytes);
_bytes.writeTo(out);
length-=chars;
offset+=chars;
}
}
}

View File

@ -164,13 +164,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
private Object _requestAttributeListeners;
private Map<String, Object> _managedAttributes;
private String[] _protectedTargets;
private boolean _shutdown = false;
private boolean _available = true;
private volatile int _availability; // 0=STOPPED, 1=AVAILABLE, 2=SHUTDOWN, 3=UNAVAILABLE
private final static int __STOPPED = 0, __AVAILABLE = 1, __SHUTDOWN = 2, __UNAVAILABLE = 3;
public enum Availability { AVAILABLE,SHUTDOWN,UNAVAILABLE};
private volatile Availability _availability;
/* ------------------------------------------------------------ */
/**
@ -629,12 +625,15 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
/**
* @return true if this context is accepting new requests
*/
@ManagedAttribute("false if this context is accepting new requests. true for graceful shutdown, which allows existing requests to complete")
@ManagedAttribute("true for graceful shutdown, which allows existing requests to complete")
public boolean isShutdown()
{
synchronized (this)
switch(_availability)
{
return !_shutdown;
case SHUTDOWN:
return true;
default:
return false;
}
}
@ -644,13 +643,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
* requests can complete, but no new requests are accepted.
*
*/
@Override
public void shutdown()
{
synchronized (this)
{
_shutdown = true;
_availability = isRunning() ? __SHUTDOWN : __STOPPED;
}
_availability = isRunning() ? Availability.SHUTDOWN : Availability.UNAVAILABLE;
}
/* ------------------------------------------------------------ */
@ -659,10 +655,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
*/
public boolean isAvailable()
{
synchronized (this)
{
return _available;
}
return _availability==Availability.AVAILABLE;
}
/* ------------------------------------------------------------ */
@ -673,8 +666,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
{
synchronized (this)
{
_available = available;
_availability = isRunning()?(_shutdown?__SHUTDOWN:_available?__AVAILABLE:__UNAVAILABLE):__STOPPED;
if (available && isRunning())
_availability = Availability.AVAILABLE;
else if (!available || !isRunning())
_availability = Availability.UNAVAILABLE;
}
}
@ -697,7 +692,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
@Override
protected void doStart() throws Exception
{
_availability = __STOPPED;
_availability = Availability.UNAVAILABLE;
if (_contextPath == null)
throw new IllegalStateException("Null contextPath");
@ -726,10 +721,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
// defers the calling of super.doStart()
startContext();
synchronized(this)
{
_availability = _shutdown?__SHUTDOWN:_available?__AVAILABLE:__UNAVAILABLE;
}
_availability = Availability.AVAILABLE;
}
finally
{
@ -806,7 +798,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
@Override
protected void doStop() throws Exception
{
_availability = __STOPPED;
_availability = Availability.UNAVAILABLE;
ClassLoader old_classloader = null;
Thread current_thread = null;
@ -867,10 +859,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
switch (_availability)
{
case __STOPPED:
case __SHUTDOWN:
return false;
case __UNAVAILABLE:
case SHUTDOWN:
case UNAVAILABLE:
baseRequest.setHandled(true);
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
return false;
@ -1517,8 +1507,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
b.append(s.charAt(0)).append('.');
}
}
b.append(getClass().getSimpleName());
b.append('{').append(getContextPath()).append(',').append(getBaseResource());
b.append(getClass().getSimpleName()).append('@').append(Integer.toString(hashCode(),16));
b.append('{').append(getContextPath()).append(',').append(getBaseResource()).append(',').append(_availability);
if (vhosts != null && vhosts.length > 0)
b.append(',').append(vhosts[0]);

View File

@ -64,6 +64,7 @@ public class HandlerWrapper extends AbstractHandlerContainer
/**
* @return Returns the handlers.
*/
@Override
public Handler[] getHandlers()
{
if (_handler==null)
@ -114,6 +115,7 @@ public class HandlerWrapper extends AbstractHandlerContainer
}
/* ------------------------------------------------------------ */
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
if (_handler!=null && isStarted())

View File

@ -59,6 +59,7 @@ public class HotSwapHandler extends AbstractHandlerContainer
/**
* @return Returns the handlers.
*/
@Override
public Handler[] getHandlers()
{
return new Handler[]
@ -122,6 +123,7 @@ public class HotSwapHandler extends AbstractHandlerContainer
/*
* @see org.eclipse.jetty.server.server.EventHandler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
if (_handler != null && isStarted())

View File

@ -114,7 +114,7 @@ public abstract class ScopedHandler extends HandlerWrapper
super.doStart();
_nextScope= (ScopedHandler)getChildHandlerByClass(ScopedHandler.class);
_nextScope= getChildHandlerByClass(ScopedHandler.class);
}
finally
@ -124,17 +124,19 @@ public abstract class ScopedHandler extends HandlerWrapper
}
}
/* ------------------------------------------------------------ */
/*
*/
@Override
public final void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
if (_outerScope==null)
doScope(target,baseRequest,request, response);
else
doHandle(target,baseRequest,request, response);
if (isStarted())
{
if (_outerScope==null)
doScope(target,baseRequest,request, response);
else
doHandle(target,baseRequest,request, response);
}
}
/* ------------------------------------------------------------ */

View File

@ -111,6 +111,7 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
}
/* ------------------------------------------------------------- */
@Override
public AbstractSession getSession()
{
return this;
@ -124,8 +125,15 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
return _accessed;
}
}
/* ------------------------------------------------------------- */
public Map<String,Object> getAttributeMap()
{
return _attributes;
}
/* ------------------------------------------------------------ */
@Override
public Object getAttribute(String name)
{
synchronized (this)
@ -147,6 +155,7 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
/* ------------------------------------------------------------ */
@SuppressWarnings({ "unchecked" })
@Override
public Enumeration<String> getAttributeNames()
{
synchronized (this)
@ -173,12 +182,14 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
}
/* ------------------------------------------------------------- */
@Override
public long getCreationTime() throws IllegalStateException
{
return _created;
}
/* ------------------------------------------------------------ */
@Override
public String getId() throws IllegalStateException
{
return _manager._nodeIdInSessionId?_nodeId:_clusterId;
@ -197,6 +208,7 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
}
/* ------------------------------------------------------------- */
@Override
public long getLastAccessedTime() throws IllegalStateException
{
checkValid();
@ -204,6 +216,7 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
}
/* ------------------------------------------------------------- */
@Override
public int getMaxInactiveInterval()
{
checkValid();
@ -214,6 +227,7 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
/*
* @see javax.servlet.http.HttpSession#getServletContext()
*/
@Override
public ServletContext getServletContext()
{
return _manager._context;
@ -221,6 +235,7 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
/* ------------------------------------------------------------- */
@Deprecated
@Override
public HttpSessionContext getSessionContext() throws IllegalStateException
{
checkValid();
@ -233,6 +248,7 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
* {@link #getAttribute}
*/
@Deprecated
@Override
public Object getValue(String name) throws IllegalStateException
{
return getAttribute(name);
@ -244,6 +260,7 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
* {@link #getAttributeNames}
*/
@Deprecated
@Override
public String[] getValueNames() throws IllegalStateException
{
synchronized(this)
@ -309,6 +326,7 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
}
/* ------------------------------------------------------------- */
@Override
public void invalidate() throws IllegalStateException
{
// remove session from context and invalidate other sessions with same ID.
@ -372,6 +390,7 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
}
/* ------------------------------------------------------------- */
@Override
public boolean isNew() throws IllegalStateException
{
checkValid();
@ -384,12 +403,14 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
* {@link #setAttribute}
*/
@Deprecated
@Override
public void putValue(java.lang.String name, java.lang.Object value) throws IllegalStateException
{
setAttribute(name,value);
}
/* ------------------------------------------------------------ */
@Override
public void removeAttribute(String name)
{
setAttribute(name,null);
@ -401,6 +422,7 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
* {@link #removeAttribute}
*/
@Deprecated
@Override
public void removeValue(java.lang.String name) throws IllegalStateException
{
removeAttribute(name);
@ -419,6 +441,7 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
}
/* ------------------------------------------------------------ */
@Override
public void setAttribute(String name, Object value)
{
Object old=null;
@ -447,6 +470,7 @@ public abstract class AbstractSession implements AbstractSessionManager.SessionI
}
/* ------------------------------------------------------------- */
@Override
public void setMaxInactiveInterval(int secs)
{
_maxIdleMs=(long)secs*1000L;

View File

@ -69,7 +69,7 @@ public abstract class AbstractHttpTest
server.stop();
}
protected TestHttpResponse executeRequest() throws URISyntaxException, IOException
protected SimpleHttpParser.TestHttpResponse executeRequest() throws URISyntaxException, IOException
{
Socket socket = new Socket("localhost", connector.getLocalPort());
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
@ -84,12 +84,12 @@ public abstract class AbstractHttpTest
return httpParser.readResponse(reader);
}
protected void assertResponseBody(TestHttpResponse response, String expectedResponseBody)
protected void assertResponseBody(SimpleHttpParser.TestHttpResponse response, String expectedResponseBody)
{
assertThat("response body is" + expectedResponseBody, response.getBody(), is(expectedResponseBody));
}
protected void assertHeader(TestHttpResponse response, String headerName, String expectedValue)
protected void assertHeader(SimpleHttpParser.TestHttpResponse response, String headerName, String expectedValue)
{
assertThat(headerName + "=" + expectedValue, response.getHeaders().get(headerName), is(expectedValue));
}

View File

@ -132,6 +132,7 @@ public class HttpConnectionTest
{
String response=connector.getResponses("GET http://localhost:80 HTTP/1.1\n"+
"Host: localhost:80\n"+
"Connection: close\n"+
"\n");
int offset=0;
@ -198,18 +199,18 @@ public class HttpConnectionTest
"Connection: close\n"+
"\015\012");
checkContains(response,0,"HTTP/1.1 400");
response=connector.getResponses("GET % HTTP/1.1\n"+
response=connector.getResponses("GET /foo/bar%c0%00 HTTP/1.1\n"+
"Host: localhost\n"+
"Connection: close\n"+
"\015\012");
checkContains(response,0,"HTTP/1.1 200"); //now fallback to iso-8859-1
response=connector.getResponses("GET /bad/utf8%c1 HTTP/1.1\n"+
"Host: localhost\n"+
"Connection: close\n"+
checkContains(response,0,"HTTP/1.1 200"); //now fallback to iso-8859-1
response=connector.getResponses("GET /bad/utf8%c1 HTTP/1.1\n"+
"Host: localhost\n"+
"Connection: close\n"+
"\015\012");
checkContains(response,0,"HTTP/1.1 200"); //now fallback to iso-8859-1
checkContains(response,0,"HTTP/1.1 200"); //now fallback to iso-8859-1
}
@Test

View File

@ -28,6 +28,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.util.SimpleHttpParser;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -65,7 +66,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new DoesNotSetHandledHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 404", response.getCode(), is("404"));
}
@ -76,7 +77,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new DoesNotSetHandledHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 500", response.getCode(), is("500"));
}
@ -119,7 +120,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new OnlySetHandledHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
assertHeader(response, "content-length", "0");
@ -131,7 +132,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new OnlySetHandledHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 500", response.getCode(), is("500"));
}
@ -176,7 +177,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new SetHandledWriteSomeDataHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
assertHeader(response, "content-length", "6");
@ -188,7 +189,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new SetHandledWriteSomeDataHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 500", response.getCode(), is("500"));
}
@ -240,7 +241,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new ExplicitFlushHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
@ -254,7 +255,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new ExplicitFlushHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
if ("HTTP/1.1".equals(httpVersion))
@ -310,7 +311,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new SetHandledAndFlushWithoutContentHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
if ("HTTP/1.1".equals(httpVersion))
@ -323,7 +324,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new SetHandledAndFlushWithoutContentHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
if ("HTTP/1.1".equals(httpVersion))
@ -377,7 +378,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new WriteFlushWriteMoreHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
if ("HTTP/1.1".equals(httpVersion))
@ -390,7 +391,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new WriteFlushWriteMoreHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
if ("HTTP/1.1".equals(httpVersion))
@ -447,7 +448,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new OverflowHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
assertResponseBody(response, "foobar");
@ -460,7 +461,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new OverflowHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
// response not committed when we throw, so 500 expected
assertThat("response code is 500", response.getCode(), is("500"));
@ -515,7 +516,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new SetContentLengthAndWriteThatAmountOfBytesHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
assertThat("response body is foo", response.getBody(), is("foo"));
@ -528,7 +529,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new SetContentLengthAndWriteThatAmountOfBytesHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
//TODO: should we expect 500 here?
assertThat("response code is 200", response.getCode(), is("200"));
@ -585,7 +586,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new SetContentLengthAndWriteMoreBytesHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
// jetty truncates the body when content-length is reached.! This is correct and desired behaviour?
@ -599,7 +600,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new SetContentLengthAndWriteMoreBytesHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
// TODO: we throw before response is committed. should we expect 500?
assertThat("response code is 200", response.getCode(), is("200"));
@ -656,7 +657,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new WriteAndSetContentLengthHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
//TODO: jetty ignores setContentLength and sends transfer-encoding header. Correct?
@ -668,7 +669,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new WriteAndSetContentLengthHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
}
@ -723,7 +724,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new WriteAndSetContentLengthTooSmallHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
assertResponseBody(response, "foobar");
@ -738,7 +739,7 @@ public class HttpManyWaysToAsyncCommitTest extends AbstractHttpTest
server.setHandler(new WriteAndSetContentLengthTooSmallHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat(response.getCode(), is("500"));
}

View File

@ -26,6 +26,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.util.SimpleHttpParser;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -57,7 +58,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new DoesNotSetHandledHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 404", response.getCode(), is("404"));
}
@ -68,7 +69,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new DoesNotSetHandledHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 500", response.getCode(), is("500"));
}
@ -94,7 +95,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new OnlySetHandledHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
assertHeader(response, "content-length", "0");
@ -106,7 +107,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new OnlySetHandledHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 500", response.getCode(), is("500"));
}
@ -133,7 +134,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new SetHandledWriteSomeDataHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
assertHeader(response, "content-length", "6");
@ -145,7 +146,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new SetHandledWriteSomeDataHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 500", response.getCode(), is("500"));
}
@ -172,7 +173,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new ExplicitFlushHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
@ -186,7 +187,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new ExplicitFlushHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
if ("HTTP/1.1".equals(httpVersion))
@ -216,7 +217,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new SetHandledAndFlushWithoutContentHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
if ("HTTP/1.1".equals(httpVersion))
@ -229,7 +230,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new SetHandledAndFlushWithoutContentHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
if ("HTTP/1.1".equals(httpVersion))
@ -258,7 +259,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new WriteFlushWriteMoreHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
if ("HTTP/1.1".equals(httpVersion))
@ -271,7 +272,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new WriteFlushWriteMoreHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
if ("HTTP/1.1".equals(httpVersion))
@ -302,7 +303,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new OverflowHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
assertResponseBody(response, "foobar");
@ -315,7 +316,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new OverflowHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
// response not committed when we throw, so 500 expected
assertThat("response code is 500", response.getCode(), is("500"));
@ -344,7 +345,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new SetContentLengthAndWriteThatAmountOfBytesHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
assertThat("response body is foo", response.getBody(), is("foo"));
@ -358,7 +359,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new SetContentLengthAndWriteThatAmountOfBytesHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
//TODO: should we expect 500 here?
assertThat("response code is 200", response.getCode(), is("200"));
@ -389,7 +390,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new SetContentLengthAndWriteMoreBytesHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
// jetty truncates the body when content-length is reached.! This is correct and desired behaviour?
@ -403,7 +404,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new SetContentLengthAndWriteMoreBytesHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
// TODO: we throw before response is committed. should we expect 500?
assertThat("response code is 200", response.getCode(), is("200"));
@ -434,7 +435,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new WriteAndSetContentLengthHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
//TODO: jetty ignores setContentLength and sends transfer-encoding header. Correct?
@ -446,7 +447,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new WriteAndSetContentLengthHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
}
@ -475,7 +476,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new WriteAndSetContentLengthTooSmallHandler(false));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat("response code is 200", response.getCode(), is("200"));
assertResponseBody(response, "foobar");
@ -490,7 +491,7 @@ public class HttpManyWaysToCommitTest extends AbstractHttpTest
server.setHandler(new WriteAndSetContentLengthTooSmallHandler(true));
server.start();
TestHttpResponse response = executeRequest();
SimpleHttpParser.TestHttpResponse response = executeRequest();
assertThat(response.getCode(), is("500"));
}

View File

@ -119,7 +119,7 @@ public class HttpWriterTest
@Test
public void testSimpleUTF8() throws Exception
{
HttpWriter _writer = new HttpWriter(_httpOut,StringUtil.__UTF8);
HttpWriter _writer = new Utf8HttpWriter(_httpOut);
_writer.write("Now is the time");
assertArrayEquals("Now is the time".getBytes(StringUtil.__UTF8),BufferUtil.toArray(_bytes));
}
@ -127,15 +127,23 @@ public class HttpWriterTest
@Test
public void testUTF8() throws Exception
{
HttpWriter _writer = new HttpWriter(_httpOut,StringUtil.__UTF8);
HttpWriter _writer = new Utf8HttpWriter(_httpOut);
_writer.write("How now \uFF22rown cow");
assertArrayEquals("How now \uFF22rown cow".getBytes(StringUtil.__UTF8),BufferUtil.toArray(_bytes));
}
@Test
public void testUTF16() throws Exception
{
HttpWriter _writer = new EncodingHttpWriter(_httpOut,StringUtil.__UTF16);
_writer.write("How now \uFF22rown cow");
assertArrayEquals("How now \uFF22rown cow".getBytes(StringUtil.__UTF16),BufferUtil.toArray(_bytes));
}
@Test
public void testNotCESU8() throws Exception
{
HttpWriter _writer = new HttpWriter(_httpOut,StringUtil.__UTF8);
HttpWriter _writer = new Utf8HttpWriter(_httpOut);
String data="xxx\uD801\uDC00xxx";
_writer.write(data);
assertEquals("787878F0909080787878",TypeUtil.toHexString(BufferUtil.toArray(_bytes)));
@ -151,7 +159,7 @@ public class HttpWriterTest
@Test
public void testMultiByteOverflowUTF8() throws Exception
{
HttpWriter _writer = new HttpWriter(_httpOut,StringUtil.__UTF8);
HttpWriter _writer = new Utf8HttpWriter(_httpOut);
final String singleByteStr = "a";
final String multiByteDuplicateStr = "\uFF22";
int remainSize = 1;
@ -178,7 +186,7 @@ public class HttpWriterTest
@Test
public void testISO8859() throws Exception
{
HttpWriter _writer = new HttpWriter(_httpOut,StringUtil.__ISO_8859_1);
HttpWriter _writer = new Iso88591HttpWriter(_httpOut);
_writer.write("How now \uFF22rown cow");
assertEquals("How now ?rown cow",new String(BufferUtil.toArray(_bytes),StringUtil.__ISO_8859_1));
}
@ -187,7 +195,7 @@ public class HttpWriterTest
@Test
public void testUTF16x2() throws Exception
{
HttpWriter _writer = new HttpWriter(_httpOut,StringUtil.__UTF8);
HttpWriter _writer = new Utf8HttpWriter(_httpOut);
String source = "\uD842\uDF9F";
@ -210,7 +218,7 @@ public class HttpWriterTest
@Test
public void testMultiByteOverflowUTF16x2() throws Exception
{
HttpWriter _writer = new HttpWriter(_httpOut,StringUtil.__UTF8);
HttpWriter _writer = new Utf8HttpWriter(_httpOut);
final String singleByteStr = "a";
int remainSize = 1;
@ -248,7 +256,7 @@ public class HttpWriterTest
@Test
public void testMultiByteOverflowUTF16x2_2() throws Exception
{
HttpWriter _writer = new HttpWriter(_httpOut,StringUtil.__UTF8);
HttpWriter _writer = new Utf8HttpWriter(_httpOut);
final String singleByteStr = "a";
int remainSize = 1;

View File

@ -58,6 +58,21 @@ public class LocalConnectorTest
assertThat(response,containsString("pathInfo=/R1"));
}
@Test
public void testStopStart() throws Exception
{
String response=_connector.getResponses("GET /R1 HTTP/1.0\r\n\r\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("pathInfo=/R1"));
_server.stop();
_server.start();
response=_connector.getResponses("GET /R2 HTTP/1.0\r\n\r\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("pathInfo=/R2"));
}
@Test
public void testTwoGETs() throws Exception
{

View File

@ -1,60 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2012 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.server;
import java.util.Map;
public class TestHttpResponse
{
private final String code;
private final Map<String, String> headers;
private final String body;
public TestHttpResponse(String code, Map<String, String> headers, String body)
{
this.code = code;
this.headers = headers;
this.body = body;
}
public String getCode()
{
return code;
}
public Map<String, String> getHeaders()
{
return headers;
}
public String getBody()
{
return body;
}
@Override
public String toString()
{
return "Response{" +
"code='" + code + '\'' +
", headers=" + headers +
", body='" + body + '\'' +
'}';
}
}

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.server.handler;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertThat;
import java.io.File;
import java.io.IOException;
@ -33,7 +34,8 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import junit.framework.Assert;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.LocalConnector;
@ -150,6 +152,60 @@ public class ContextHandlerTest
Assert.assertEquals(fooA._scontext,foobarA._scontext.getContext("/foo/other"));
}
@Test
public void testLifeCycle() throws Exception
{
Server server = new Server();
LocalConnector connector = new LocalConnector(server);
server.setConnectors(new Connector[] { connector });
ContextHandlerCollection contexts = new ContextHandlerCollection();
server.setHandler(contexts);
ContextHandler root = new ContextHandler(contexts,"/");
root.setHandler(new ContextPathHandler());
ContextHandler foo = new ContextHandler(contexts,"/foo");
foo.setHandler(new ContextPathHandler());
ContextHandler foobar = new ContextHandler(contexts,"/foo/bar");
foobar.setHandler(new ContextPathHandler());
// check that all contexts start normally
server.start();
assertThat(connector.getResponses("GET / HTTP/1.0\n\n"),Matchers.containsString("ctx=''"));
assertThat(connector.getResponses("GET /foo/xxx HTTP/1.0\n\n"),Matchers.containsString("ctx='/foo'"));
assertThat(connector.getResponses("GET /foo/bar/xxx HTTP/1.0\n\n"),Matchers.containsString("ctx='/foo/bar'"));
// If we stop foobar, then requests will be handled by foo
foobar.stop();
assertThat(connector.getResponses("GET / HTTP/1.0\n\n"),Matchers.containsString("ctx=''"));
assertThat(connector.getResponses("GET /foo/xxx HTTP/1.0\n\n"),Matchers.containsString("ctx='/foo'"));
assertThat(connector.getResponses("GET /foo/bar/xxx HTTP/1.0\n\n"),Matchers.containsString("ctx='/foo'"));
// If we shutdown foo then requests will be 503'd
foo.shutdown();
assertThat(connector.getResponses("GET / HTTP/1.0\n\n"),Matchers.containsString("ctx=''"));
assertThat(connector.getResponses("GET /foo/xxx HTTP/1.0\n\n"),Matchers.containsString("503"));
assertThat(connector.getResponses("GET /foo/bar/xxx HTTP/1.0\n\n"),Matchers.containsString("503"));
// If we stop foo then requests will be handled by root
foo.stop();
assertThat(connector.getResponses("GET / HTTP/1.0\n\n"),Matchers.containsString("ctx=''"));
assertThat(connector.getResponses("GET /foo/xxx HTTP/1.0\n\n"),Matchers.containsString("ctx=''"));
assertThat(connector.getResponses("GET /foo/bar/xxx HTTP/1.0\n\n"),Matchers.containsString("ctx=''"));
// If we start foo then foobar requests will be handled by foo
foo.start();
assertThat(connector.getResponses("GET / HTTP/1.0\n\n"),Matchers.containsString("ctx=''"));
assertThat(connector.getResponses("GET /foo/xxx HTTP/1.0\n\n"),Matchers.containsString("ctx='/foo'"));
assertThat(connector.getResponses("GET /foo/bar/xxx HTTP/1.0\n\n"),Matchers.containsString("ctx='/foo'"));
// If we start foobar then foobar requests will be handled by foobar
foobar.start();
assertThat(connector.getResponses("GET / HTTP/1.0\n\n"),Matchers.containsString("ctx=''"));
assertThat(connector.getResponses("GET /foo/xxx HTTP/1.0\n\n"),Matchers.containsString("ctx='/foo'"));
assertThat(connector.getResponses("GET /foo/bar/xxx HTTP/1.0\n\n"),Matchers.containsString("ctx='/foo/bar'"));
}
@Test
public void testContextVirtualGetContext() throws Exception
{
@ -393,6 +449,7 @@ public class ContextHandlerTest
return handled;
}
@Override
public void handle(String s, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
@ -404,38 +461,19 @@ public class ContextHandlerTest
handled = false;
}
}
private static final class WriterHandler extends AbstractHandler
private static final class ContextPathHandler extends AbstractHandler
{
volatile boolean error;
volatile Throwable throwable;
@Override
public void handle(String s, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
error = false;
throwable=null;
response.setStatus(200);
response.setContentType("text/plain; charset=utf-8");
response.setHeader("Connection","close");
PrintWriter writer = response.getWriter();
try
{
writer.write("Goodbye cruel world\n");
writer.close();
response.flushBuffer();
//writer.write("speaking from the dead");
writer.write("give the printwriter a chance"); //should create an error
if (writer.checkError())
writer.write("didn't take the chance, will throw now"); //write after an error
}
catch(Throwable th)
{
throwable=th;
}
error=writer.checkError();
writer.println("ctx='"+request.getContextPath()+"'");
}
}
}

View File

@ -27,8 +27,6 @@ import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jetty.server.TestHttpResponse;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@ -161,4 +159,43 @@ public class SimpleHttpParser
}
return body;
}
public static class TestHttpResponse
{
private final String code;
private final Map<String, String> headers;
private final String body;
public TestHttpResponse(String code, Map<String, String> headers, String body)
{
this.code = code;
this.headers = headers;
this.body = body;
}
public String getCode()
{
return code;
}
public Map<String, String> getHeaders()
{
return headers;
}
public String getBody()
{
return body;
}
@Override
public String toString()
{
return "Response{" +
"code='" + code + '\'' +
", headers=" + headers +
", body='" + body + '\'' +
'}';
}
}
}

View File

@ -93,17 +93,17 @@ public class StdErrLogTest
before.debug("testing {} {}","test","debug-before-false");
log.debug("testing {} {}","test","debug-deprecated-false");
after.debug("testing {} {}","test","debug-after-false");
output.assertContains("DBUG:xxx:tname: testing test debug");
output.assertContains("INFO:xxx:tname: testing test info");
output.assertContains("WARN:xxx:tname: testing test warn");
output.assertNotContains("YOU SHOULD NOT SEE THIS!");
output.assertContains("DBUG:x.before:tname:testing test debug-before");
output.assertNotContains("DBUG:xxx:tname: testing test debug-depdeprecated-false");
output.assertContains("DBUG:x.after:testing test debug-after");
output.assertNotContains("DBUG:x.before:tname:testing test debug-before-false");
output.assertNotContains("DBUG:xxx:tname:testing test debug-deprecated-false");
output.assertNotContains("DBUG:x.after:tname:testing test debug-after-false");
output.assertContains("DBUG:x.before:tname: testing test debug-before");
output.assertContains("DBUG:xxx:tname: testing test debug-deprecated");
output.assertContains("DBUG:x.after:tname: testing test debug-after");
output.assertNotContains("DBUG:x.before:tname: testing test debug-before-false");
output.assertNotContains("DBUG:xxx:tname: testing test debug-deprecated-false");
output.assertNotContains("DBUG:x.after:tname: testing test debug-after-false");
}
@Test

View File

@ -19,6 +19,10 @@
package org.eclipse.jetty.websocket.client;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@ -30,6 +34,7 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.websocket.api.Extension;
import org.eclipse.jetty.websocket.api.ExtensionRegistry;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.client.internal.ConnectionManager;
@ -37,6 +42,8 @@ import org.eclipse.jetty.websocket.client.internal.IWebSocketClient;
import org.eclipse.jetty.websocket.driver.EventMethodsCache;
import org.eclipse.jetty.websocket.driver.WebSocketEventDriver;
import org.eclipse.jetty.websocket.extensions.WebSocketExtensionRegistry;
import org.eclipse.jetty.websocket.io.WebSocketSession;
import org.eclipse.jetty.websocket.protocol.ExtensionConfig;
public class WebSocketClientFactory extends AggregateLifeCycle
{
@ -47,9 +54,10 @@ public class WebSocketClientFactory extends AggregateLifeCycle
private final ScheduledExecutorService scheduler;
private final EventMethodsCache methodsCache;
private final WebSocketPolicy policy;
private final ExtensionRegistry extensionRegistry;
private final WebSocketExtensionRegistry extensionRegistry;
private SocketAddress bindAddress;
private final Queue<WebSocketSession> sessions = new ConcurrentLinkedQueue<>();
private ConnectionManager connectionManager;
public WebSocketClientFactory()
@ -69,6 +77,7 @@ public class WebSocketClientFactory extends AggregateLifeCycle
public WebSocketClientFactory(Executor executor, ScheduledExecutorService scheduler, SslContextFactory sslContextFactory)
{
LOG.debug("new WebSocketClientFactory()");
if (executor == null)
{
throw new IllegalArgumentException("Executor is required");
@ -101,6 +110,20 @@ public class WebSocketClientFactory extends AggregateLifeCycle
this(new QueuedThreadPool(),Executors.newSingleThreadScheduledExecutor(),sslContextFactory);
}
@Override
protected void doStart() throws Exception
{
super.doStart();
LOG.debug("doStart()");
}
@Override
protected void doStop() throws Exception
{
super.doStop();
LOG.debug("doStop()");
}
/**
* The address to bind local physical (outgoing) TCP Sockets to.
*
@ -142,6 +165,26 @@ public class WebSocketClientFactory extends AggregateLifeCycle
return scheduler;
}
public List<Extension> initExtensions(List<ExtensionConfig> requested)
{
List<Extension> extensions = new ArrayList<Extension>();
for (ExtensionConfig cfg : requested)
{
Extension extension = extensionRegistry.newInstance(cfg);
if (extension == null)
{
continue;
}
LOG.debug("added {}",extension);
extensions.add(extension);
}
LOG.debug("extensions={}",extensions);
return extensions;
}
public WebSocketClient newWebSocketClient(Object websocketPojo)
{
LOG.debug("Creating new WebSocket for {}",websocketPojo);
@ -149,6 +192,33 @@ public class WebSocketClientFactory extends AggregateLifeCycle
return new IWebSocketClient(this,websocket);
}
public boolean sessionClosed(WebSocketSession session)
{
return isRunning() && sessions.remove(session);
}
public boolean sessionOpened(WebSocketSession session)
{
if (LOG.isDebugEnabled())
{
LOG.debug("Session Opened: {}",session);
}
// FIXME: what is going on?
// if (!isRunning())
// {
// LOG.debug("Factory.isRunning: {}",this.isRunning());
// LOG.debug("Factory.isStarted: {}",this.isStarted());
// LOG.debug("Factory.isStarting: {}",this.isStarting());
// LOG.debug("Factory.isStopped: {}",this.isStopped());
// LOG.debug("Factory.isStopping: {}",this.isStopping());
// LOG.warn("Factory is not running");
// return false;
// }
boolean ret = sessions.offer(session);
session.onConnect();
return ret;
}
/**
* @param bindAddress
* the address to bind the socket channel to

View File

@ -57,6 +57,8 @@ public class IWebSocketClient extends FutureCallback<UpgradeResponse> implements
public IWebSocketClient(WebSocketClientFactory factory, WebSocketEventDriver websocket)
{
this.factory = factory;
LOG.debug("factory.isRunning(): {}",factory.isRunning());
LOG.debug("factory.isStarted(): {}",factory.isStarted());
this.policy = factory.getPolicy();
this.websocket = websocket;
this.upgradeRequest = new ClientUpgradeRequest();
@ -210,9 +212,8 @@ public class IWebSocketClient extends FutureCallback<UpgradeResponse> implements
return websocketUri;
}
public void setUpgradeResponse(UpgradeResponse response)
public void setUpgradeResponse(ClientUpgradeResponse response)
{
// TODO Auto-generated method stub
this.upgradeResponse = response;
}
}

View File

@ -25,7 +25,6 @@ import java.util.regex.Pattern;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Utf8LineParser;
import org.eclipse.jetty.websocket.api.UpgradeException;
import org.eclipse.jetty.websocket.api.UpgradeResponse;
import org.eclipse.jetty.websocket.client.internal.ClientUpgradeResponse;
/**
@ -58,7 +57,7 @@ public class HttpResponseHeaderParser
return (state == State.END);
}
public UpgradeResponse parse(ByteBuffer buf) throws UpgradeException
public ClientUpgradeResponse parse(ByteBuffer buf) throws UpgradeException
{
while (!isDone() && (buf.remaining() > 0))
{

View File

@ -22,6 +22,7 @@ import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Executor;
@ -36,10 +37,17 @@ import org.eclipse.jetty.util.QuotedStringTokenizer;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.Extension;
import org.eclipse.jetty.websocket.api.UpgradeException;
import org.eclipse.jetty.websocket.api.UpgradeResponse;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.client.internal.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.internal.ClientUpgradeResponse;
import org.eclipse.jetty.websocket.client.internal.IWebSocketClient;
import org.eclipse.jetty.websocket.driver.WebSocketEventDriver;
import org.eclipse.jetty.websocket.io.IncomingFrames;
import org.eclipse.jetty.websocket.io.OutgoingFrames;
import org.eclipse.jetty.websocket.io.WebSocketSession;
import org.eclipse.jetty.websocket.protocol.AcceptHash;
import org.eclipse.jetty.websocket.protocol.ExtensionConfig;
@ -176,14 +184,14 @@ public class UpgradeConnection extends AbstractConnection
{
LOG.debug("Filled {} bytes - {}",filled,BufferUtil.toDetailString(buffer));
}
UpgradeResponse resp = parser.parse(buffer);
ClientUpgradeResponse resp = parser.parse(buffer);
if (resp != null)
{
// Got a response!
client.setUpgradeResponse(resp);
validateResponse(resp);
notifyConnect();
upgradeConnection();
upgradeConnection(resp);
return false; // do no more reading
}
}
@ -205,12 +213,72 @@ public class UpgradeConnection extends AbstractConnection
}
}
private void upgradeConnection()
private void upgradeConnection(ClientUpgradeResponse response)
{
EndPoint endp = getEndPoint();
Executor executor = getExecutor();
WebSocketClientConnection conn = new WebSocketClientConnection(endp,executor,client);
endp.setConnection(conn);
WebSocketClientConnection connection = new WebSocketClientConnection(endp,executor,client);
// Initialize / Negotiate Extensions
WebSocketEventDriver websocket = client.getWebSocket();
WebSocketPolicy policy = client.getPolicy();
String acceptedSubProtocol = response.getAcceptedSubProtocol();
WebSocketSession session = new WebSocketSession(websocket,connection,policy,acceptedSubProtocol);
connection.setSession(session);
List<Extension> extensions = client.getFactory().initExtensions(response.getExtensions());
// Start with default routing.
IncomingFrames incoming = session;
OutgoingFrames outgoing = connection;
// Connect extensions
if (extensions != null)
{
Iterator<Extension> extIter;
// Connect outgoings
extIter = extensions.iterator();
while (extIter.hasNext())
{
Extension ext = extIter.next();
ext.setNextOutgoingFrames(outgoing);
outgoing = ext;
// Handle RSV reservations
if (ext.useRsv1())
{
connection.getGenerator().setRsv1InUse(true);
connection.getParser().setRsv1InUse(true);
}
if (ext.useRsv2())
{
connection.getGenerator().setRsv2InUse(true);
connection.getParser().setRsv2InUse(true);
}
if (ext.useRsv3())
{
connection.getGenerator().setRsv3InUse(true);
connection.getParser().setRsv3InUse(true);
}
}
// Connect incomings
Collections.reverse(extensions);
extIter = extensions.iterator();
while (extIter.hasNext())
{
Extension ext = extIter.next();
ext.setNextIncomingFrames(incoming);
incoming = ext;
}
}
// configure session for outgoing flows
session.setOutgoing(outgoing);
// configure connection for incoming flows
connection.getParser().setIncomingFramesHandler(incoming);
// Now swap out the connection
endp.setConnection(connection);
}
private void validateResponse(UpgradeResponse response)

View File

@ -21,17 +21,25 @@ package org.eclipse.jetty.websocket.client.internal.io;
import java.util.concurrent.Executor;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.websocket.client.WebSocketClientFactory;
import org.eclipse.jetty.websocket.client.internal.IWebSocketClient;
import org.eclipse.jetty.websocket.io.AbstractWebSocketConnection;
/**
* Client side WebSocket physical connection.
*/
public class WebSocketClientConnection extends AbstractWebSocketConnection
{
private final WebSocketClientFactory factory;
private final IWebSocketClient client;
private boolean connected;
public WebSocketClientConnection(EndPoint endp, Executor executor, IWebSocketClient client)
{
super(endp,executor,client.getFactory().getScheduler(),client.getPolicy(),client.getFactory().getBufferPool());
this.client = client;
this.factory = client.getFactory();
this.connected = false;
}
public IWebSocketClient getClient()
@ -39,9 +47,21 @@ public class WebSocketClientConnection extends AbstractWebSocketConnection
return client;
}
@Override
public void onClose()
{
super.onClose();
factory.sessionClosed(getSession());
}
@Override
public void onOpen()
{
if (!connected)
{
factory.sessionOpened(getSession());
connected = true;
}
super.onOpen();
}
}

View File

@ -22,33 +22,39 @@ import static org.hamcrest.Matchers.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.api.WebSocketConnection;
import org.junit.Assert;
/**
* Testing Socket used on client side WebSocket testing.
*/
public class TrackingSocket extends WebSocketAdapter
{
public AtomicBoolean open = new AtomicBoolean(false);
public AtomicInteger close = new AtomicInteger(-1);
private static final Logger LOG = Log.getLogger(TrackingSocket.class);
public int closeCode = -1;
public StringBuilder closeMessage = new StringBuilder();
public CountDownLatch openLatch = new CountDownLatch(1);
public CountDownLatch closeLatch = new CountDownLatch(1);
public CountDownLatch dataLatch = new CountDownLatch(1);
public BlockingQueue<String> messageQueue = new BlockingArrayQueue<String>();
public void assertClose(int expectedStatusCode, String expectedReason)
public void assertClose(int expectedStatusCode, String expectedReason) throws InterruptedException
{
assertCloseCode(expectedStatusCode);
assertCloseReason(expectedReason);
}
public void assertCloseCode(int expectedCode)
public void assertCloseCode(int expectedCode) throws InterruptedException
{
Assert.assertThat("Close Code",close.get(),is(expectedCode));
Assert.assertThat("Was Closed",closeLatch.await(500,TimeUnit.MILLISECONDS),is(true));
Assert.assertThat("Close Code",closeCode,is(expectedCode));
}
private void assertCloseReason(String expectedReason)
@ -56,36 +62,37 @@ public class TrackingSocket extends WebSocketAdapter
Assert.assertThat("Close Reaosn",closeMessage.toString(),is(expectedReason));
}
public void assertIsOpen()
public void assertIsOpen() throws InterruptedException
{
assertWasOpened();
assertNotClosed();
}
public void assertMessage(String string)
public void assertMessage(String expected)
{
// TODO Auto-generated method stub
String actual = messageQueue.poll();
Assert.assertEquals("Message",expected,actual);
}
public void assertNotClosed()
{
Assert.assertThat("Close Code",close.get(),is(-1));
Assert.assertThat("Closed Latch",closeLatch.getCount(),greaterThanOrEqualTo(1L));
}
public void assertNotOpened()
{
Assert.assertThat("Opened State",open.get(),is(false));
Assert.assertThat("Open Latch",openLatch.getCount(),greaterThanOrEqualTo(1L));
}
public void assertWasOpened()
public void assertWasOpened() throws InterruptedException
{
Assert.assertThat("Opened State",open.get(),is(true));
Assert.assertThat("Was Opened",openLatch.await(500,TimeUnit.MILLISECONDS),is(true));
}
@Override
public void onWebSocketBinary(byte[] payload, int offset, int len)
{
LOG.debug("onWebSocketBinary()");
dataLatch.countDown();
}
@ -93,7 +100,7 @@ public class TrackingSocket extends WebSocketAdapter
public void onWebSocketClose(int statusCode, String reason)
{
super.onWebSocketClose(statusCode,reason);
close.set(statusCode);
closeCode = statusCode;
closeMessage.append(reason);
closeLatch.countDown();
}
@ -102,20 +109,19 @@ public class TrackingSocket extends WebSocketAdapter
public void onWebSocketConnect(WebSocketConnection connection)
{
super.onWebSocketConnect(connection);
open.set(true);
openLatch.countDown();
}
@Override
public void onWebSocketText(String message)
{
dataLatch.countDown();
LOG.debug("onWebSocketText({})",message);
messageQueue.add(message);
dataLatch.countDown();
}
public void waitForResponseMessage()
public void waitForMessage(TimeUnit timeoutUnit, int timeoutDuration) throws InterruptedException
{
// TODO Auto-generated method stub
Assert.assertThat("Message Received",dataLatch.await(timeoutDuration,timeoutUnit),is(true));
}
}

View File

@ -49,7 +49,7 @@ import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@Ignore("work in progress")
@Ignore("Work in Progress")
public class WebSocketClientTest
{
private BlockheadServer server;
@ -149,9 +149,9 @@ public class WebSocketClientTest
final ServerConnection srvSock = server.accept();
srvSock.upgrade();
UpgradeResponse resp = future.get(250,TimeUnit.MILLISECONDS);
UpgradeResponse resp = future.get(500,TimeUnit.MILLISECONDS);
Assert.assertThat("Response",resp,notNullValue());
Assert.assertEquals("Response.success",resp.isSuccess(),is(true));
Assert.assertThat("Response.success",resp.isSuccess(),is(true));
cliSock.assertWasOpened();
cliSock.assertNotClosed();
@ -159,9 +159,9 @@ public class WebSocketClientTest
Assert.assertThat("Factory.sockets.size",factory.getConnectionManager().getClients().size(),is(1));
cliSock.getConnection().write(null,new FutureCallback<Void>(),"Hello World!");
srvSock.echoMessage();
srvSock.echoMessage(1,TimeUnit.MILLISECONDS,500);
// wait for response from server
cliSock.waitForResponseMessage();
cliSock.waitForMessage(TimeUnit.MILLISECONDS,500);
cliSock.assertMessage("Hello World!");
}
@ -177,6 +177,11 @@ public class WebSocketClientTest
final ServerConnection srvSock = server.accept();
srvSock.upgrade();
// Validate connect
UpgradeResponse resp = future.get(500,TimeUnit.MILLISECONDS);
Assert.assertThat("Response",resp,notNullValue());
Assert.assertThat("Response.success",resp.isSuccess(),is(true));
// Have server send initial message
srvSock.write(WebSocketFrame.text("Hello World"));

View File

@ -31,11 +31,26 @@ import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.StandardByteBufferPool;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.io.IncomingFrames;
import org.eclipse.jetty.websocket.io.OutgoingFrames;
import org.eclipse.jetty.websocket.protocol.AcceptHash;
import org.eclipse.jetty.websocket.protocol.Generator;
import org.eclipse.jetty.websocket.protocol.OpCode;
import org.eclipse.jetty.websocket.protocol.Parser;
import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
import org.junit.Assert;
@ -46,15 +61,33 @@ import org.junit.Assert;
*/
public class BlockheadServer
{
public static class ServerConnection
public static class ServerConnection implements IncomingFrames, OutgoingFrames
{
private final Socket socket;
private final ByteBufferPool bufferPool;
private final WebSocketPolicy policy;
private final IncomingFramesCapture incomingFrames;
private final Parser parser;
private final Generator generator;
private final AtomicInteger parseCount;
/** Set to true to disable timeouts (for debugging reasons) */
private boolean debug = false;
private OutputStream out;
private InputStream in;
private IncomingFrames incoming = this;
private OutgoingFrames outgoing = this;
public ServerConnection(Socket socket)
{
this.socket = socket;
this.incomingFrames = new IncomingFramesCapture();
this.policy = WebSocketPolicy.newServerPolicy();
this.bufferPool = new StandardByteBufferPool(policy.getBufferSize());
this.parser = new Parser(policy);
this.parseCount = new AtomicInteger(0);
this.generator = new Generator(policy,bufferPool);
}
public void close() throws IOException
@ -62,10 +95,33 @@ public class BlockheadServer
this.socket.close();
}
public void echoMessage()
public void disconnect()
{
// TODO Auto-generated method stub
LOG.debug("disconnect");
IO.close(in);
IO.close(out);
if (socket != null)
{
try
{
socket.close();
}
catch (IOException ignore)
{
/* ignore */
}
}
}
public void echoMessage(int expectedFrames, TimeUnit timeoutUnit, int timeoutDuration) throws IOException, TimeoutException
{
LOG.debug("Echo Frames [expecting {}]",expectedFrames);
IncomingFramesCapture cap = readFrames(expectedFrames,timeoutUnit,timeoutDuration);
// now echo them back.
for (WebSocketFrame frame : cap.getFrames())
{
write(frame);
}
}
public void flush() throws IOException
@ -91,6 +147,95 @@ public class BlockheadServer
return out;
}
@Override
public void incoming(WebSocketException e)
{
// TODO Auto-generated method stub
}
@Override
public void incoming(WebSocketFrame frame)
{
// TODO Auto-generated method stub
}
@Override
public <C> void output(C context, Callback<C> callback, WebSocketFrame frame) throws IOException
{
ByteBuffer buf = generator.generate(frame);
if (LOG.isDebugEnabled())
{
LOG.debug("writing out: {}",BufferUtil.toDetailString(buf));
}
BufferUtil.writeTo(buf,out);
out.flush();
if (frame.getOpCode() == OpCode.CLOSE)
{
disconnect();
}
}
public int read(ByteBuffer buf) throws IOException
{
int len = 0;
while ((in.available() > 0) && (buf.remaining() > 0))
{
buf.put((byte)in.read());
len++;
}
return len;
}
public IncomingFramesCapture readFrames(int expectedCount, TimeUnit timeoutUnit, int timeoutDuration) throws IOException, TimeoutException
{
LOG.debug("Read: waiting for {} frame(s) from server",expectedCount);
int startCount = incomingFrames.size();
ByteBuffer buf = bufferPool.acquire(policy.getBufferSize(),false);
BufferUtil.clearToFill(buf);
try
{
long msDur = TimeUnit.MILLISECONDS.convert(timeoutDuration,timeoutUnit);
long now = System.currentTimeMillis();
long expireOn = now + msDur;
LOG.debug("Now: {} - expireOn: {} ({} ms)",now,expireOn,msDur);
int len = 0;
while (incomingFrames.size() < (startCount + expectedCount))
{
BufferUtil.clearToFill(buf);
len = read(buf);
if (len > 0)
{
LOG.debug("Read {} bytes",len);
BufferUtil.flipToFlush(buf,0);
parser.parse(buf);
}
try
{
TimeUnit.MILLISECONDS.sleep(20);
}
catch (InterruptedException gnore)
{
/* ignore */
}
if (!debug && (System.currentTimeMillis() > expireOn))
{
incomingFrames.dump();
throw new TimeoutException(String.format("Timeout reading all %d expected frames. (managed to only read %d frame(s))",expectedCount,
incomingFrames.size()));
}
}
}
finally
{
bufferPool.release(buf);
}
return incomingFrames;
}
public String readRequest() throws IOException
{
LOG.debug("Reading client request");
@ -138,14 +283,21 @@ public class BlockheadServer
}
}
// TODO: parse extensions
// TODO: setup extensions
StringBuilder resp = new StringBuilder();
resp.append("HTTP/1.1 101 Upgrade\r\n");
resp.append("Sec-WebSocket-Accept: ");
resp.append(AcceptHash.hashKey(key));
resp.append("\r\n");
resp.append(AcceptHash.hashKey(key)).append("\r\n");
// TODO: respond to used extensions
resp.append("\r\n");
write(resp.toString().getBytes());
// Configure Parser
parser.setIncomingFramesHandler(incomingFrames);
}
private void write(byte[] bytes) throws IOException
@ -163,10 +315,10 @@ public class BlockheadServer
getOutputStream().write(b);
}
public void write(WebSocketFrame frame)
public void write(WebSocketFrame frame) throws IOException
{
// TODO Auto-generated method stub
LOG.debug("write(Frame->{}) to {}",frame,outgoing);
outgoing.output(null,null,frame);
}
}

View File

@ -0,0 +1,140 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.client.blockhead;
import static org.hamcrest.Matchers.*;
import java.util.LinkedList;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.io.IncomingFrames;
import org.eclipse.jetty.websocket.protocol.OpCode;
import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
import org.junit.Assert;
public class IncomingFramesCapture implements IncomingFrames
{
private static final Logger LOG = Log.getLogger(IncomingFramesCapture.class);
private LinkedList<WebSocketFrame> frames = new LinkedList<>();
private LinkedList<WebSocketException> errors = new LinkedList<>();
public void assertErrorCount(int expectedCount)
{
Assert.assertThat("Captured error count",errors.size(),is(expectedCount));
}
public void assertFrameCount(int expectedCount)
{
Assert.assertThat("Captured frame count",frames.size(),is(expectedCount));
}
public void assertHasErrors(Class<? extends WebSocketException> errorType, int expectedCount)
{
Assert.assertThat(errorType.getSimpleName(),getErrorCount(errorType),is(expectedCount));
}
public void assertHasFrame(byte op)
{
Assert.assertThat(OpCode.name(op),getFrameCount(op),greaterThanOrEqualTo(1));
}
public void assertHasFrame(byte op, int expectedCount)
{
Assert.assertThat(OpCode.name(op),getFrameCount(op),is(expectedCount));
}
public void assertHasNoFrames()
{
Assert.assertThat("Has no frames",frames.size(),is(0));
}
public void assertNoErrors()
{
Assert.assertThat("Has no errors",errors.size(),is(0));
}
public void dump()
{
System.err.printf("Captured %d incoming frames%n",frames.size());
for (int i = 0; i < frames.size(); i++)
{
WebSocketFrame frame = frames.get(i);
System.err.printf("[%3d] %s%n",i,frame);
System.err.printf(" %s%n",BufferUtil.toDetailString(frame.getPayload()));
}
}
public int getErrorCount(Class<? extends WebSocketException> errorType)
{
int count = 0;
for (WebSocketException error : errors)
{
if (errorType.isInstance(error))
{
count++;
}
}
return count;
}
public LinkedList<WebSocketException> getErrors()
{
return errors;
}
public int getFrameCount(byte op)
{
int count = 0;
for (WebSocketFrame frame : frames)
{
if (frame.getOpCode() == op)
{
count++;
}
}
return count;
}
public LinkedList<WebSocketFrame> getFrames()
{
return frames;
}
@Override
public void incoming(WebSocketException e)
{
LOG.debug(e);
errors.add(e);
}
@Override
public void incoming(WebSocketFrame frame)
{
WebSocketFrame copy = new WebSocketFrame(frame);
frames.add(copy);
}
public int size()
{
return frames.size();
}
}

View File

@ -224,6 +224,7 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
@Override
public void onFillable()
{
LOG.debug("{} onFillable()",policy.getBehavior());
ByteBuffer buffer = bufferPool.acquire(policy.getBufferSize(),false);
BufferUtil.clear(buffer);
boolean readMore = false;
@ -251,6 +252,7 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
public void onOpen()
{
super.onOpen();
LOG.debug("fillInterested");
fillInterested();
}