Merge branch 'jetty-9' of ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project into jetty-9
This commit is contained in:
commit
095a15f9e8
|
@ -0,0 +1,15 @@
|
|||
========================================================================
|
||||
Copyright (c) ${copyright-range} 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.
|
||||
========================================================================
|
|
@ -450,7 +450,7 @@ public class HttpFields implements Iterable<HttpFields.Field>
|
|||
}
|
||||
|
||||
return new Enumeration<String>()
|
||||
{
|
||||
{
|
||||
Field f = field;
|
||||
|
||||
@Override
|
||||
|
@ -467,7 +467,7 @@ public class HttpFields implements Iterable<HttpFields.Field>
|
|||
f = f._next;
|
||||
return n.getValue();
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------- */
|
||||
|
@ -1262,5 +1262,36 @@ public class HttpFields implements Iterable<HttpFields.Field>
|
|||
{
|
||||
return ("[" + getName() + "=" + _value + (_next == null ? "" : "->") + "]");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public boolean contains(String value)
|
||||
{
|
||||
if (_value==null)
|
||||
return false;
|
||||
|
||||
if (value.equalsIgnoreCase(_value))
|
||||
return true;
|
||||
|
||||
String[] split = _value.split("\\s*,\\s*");
|
||||
for (String s : split)
|
||||
{
|
||||
if (value.equalsIgnoreCase(s))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_next!=null)
|
||||
return _next.contains(value);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public boolean contains(HttpHeader header, String value)
|
||||
{
|
||||
Field field = getField(header);
|
||||
if (field==null)
|
||||
return false;
|
||||
return field.contains(value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,6 @@ public class HttpParser
|
|||
private HttpHeaderValue _value;
|
||||
private String _valueString;
|
||||
private int _responseStatus;
|
||||
private boolean _persistent;
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
private State _state=State.START;
|
||||
|
@ -186,12 +185,6 @@ public class HttpParser
|
|||
return _state == state;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public boolean isPersistent()
|
||||
{
|
||||
return _persistent;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
/* Quick lookahead for the start state looking for a request method or a HTTP version,
|
||||
* otherwise skip white space until something else to parse.
|
||||
|
@ -218,7 +211,6 @@ public class HttpParser
|
|||
if (_version!=null)
|
||||
{
|
||||
buffer.position(buffer.position()+_version.asString().length()+1);
|
||||
_persistent=_version.getVerion()>=HttpVersion.HTTP_1_1.getVerion();
|
||||
_state=State.SPACE1;
|
||||
return;
|
||||
}
|
||||
|
@ -309,7 +301,6 @@ public class HttpParser
|
|||
badMessage(buffer, "Unknown Version");
|
||||
return true;
|
||||
}
|
||||
_persistent=_version.getVerion()>=HttpVersion.HTTP_1_1.getVerion();
|
||||
_state=State.SPACE1;
|
||||
}
|
||||
else if (ch < HttpTokens.SPACE && ch>=0)
|
||||
|
@ -377,10 +368,9 @@ public class HttpParser
|
|||
_uri=_utf8.toString();
|
||||
_utf8.reset();
|
||||
return_from_parse|=_requestHandler.startRequest(_method,_methodString,_uri,null);
|
||||
_persistent=false;
|
||||
_state=State.END;
|
||||
BufferUtil.clear(buffer);
|
||||
return_from_parse|=_handler.headerComplete(false,_persistent);
|
||||
return_from_parse|=_handler.headerComplete();
|
||||
return_from_parse|=_handler.messageComplete(_contentPosition);
|
||||
}
|
||||
else
|
||||
|
@ -410,7 +400,6 @@ public class HttpParser
|
|||
_string.setLength(0);
|
||||
buffer.position(buffer.position()+_version.asString().length()-1);
|
||||
_eol=buffer.get();
|
||||
_persistent=_version.getVerion()>=HttpVersion.HTTP_1_1.getVerion();
|
||||
_state=State.HEADER;
|
||||
return_from_parse|=_requestHandler.startRequest(_method,_methodString, _uri, _version);
|
||||
}
|
||||
|
@ -429,10 +418,9 @@ public class HttpParser
|
|||
{
|
||||
// HTTP/0.9
|
||||
return_from_parse|=_requestHandler.startRequest(_method,_methodString, _uri, null);
|
||||
_persistent=false;
|
||||
_state=State.END;
|
||||
BufferUtil.clear(buffer);
|
||||
return_from_parse|=_handler.headerComplete(false,_persistent);
|
||||
return_from_parse|=_handler.headerComplete();
|
||||
return_from_parse|=_handler.messageComplete(_contentPosition);
|
||||
}
|
||||
}
|
||||
|
@ -450,7 +438,6 @@ public class HttpParser
|
|||
}
|
||||
|
||||
_eol=ch;
|
||||
_persistent=_version.getVerion()>=HttpVersion.HTTP_1_1.getVerion();
|
||||
_state=State.HEADER;
|
||||
return_from_parse|=_requestHandler.startRequest(_method,_methodString, _uri, _version);
|
||||
continue;
|
||||
|
@ -568,37 +555,6 @@ public class HttpParser
|
|||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CONNECTION:
|
||||
switch(_value==null?HttpHeaderValue.UNKNOWN:_value)
|
||||
{
|
||||
case CLOSE:
|
||||
_persistent=false;
|
||||
break;
|
||||
|
||||
case KEEP_ALIVE:
|
||||
_persistent=true;
|
||||
break;
|
||||
|
||||
default: // No match, may be multi valued
|
||||
{
|
||||
for (String v : _valueString.toString().split(","))
|
||||
{
|
||||
HttpHeaderValue val=HttpHeaderValue.CACHE.get(v.trim());
|
||||
switch(val==null?HttpHeaderValue.UNKNOWN:val)
|
||||
{
|
||||
case CLOSE:
|
||||
_persistent=false;
|
||||
break;
|
||||
|
||||
case KEEP_ALIVE:
|
||||
_persistent=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -634,24 +590,23 @@ public class HttpParser
|
|||
{
|
||||
case EOF_CONTENT:
|
||||
_state=State.EOF_CONTENT;
|
||||
_persistent=false;
|
||||
return_from_parse|=_handler.headerComplete(true,_persistent);
|
||||
return_from_parse|=_handler.headerComplete();
|
||||
break;
|
||||
|
||||
case CHUNKED_CONTENT:
|
||||
_state=State.CHUNKED_CONTENT;
|
||||
return_from_parse|=_handler.headerComplete(true,_persistent);
|
||||
return_from_parse|=_handler.headerComplete();
|
||||
break;
|
||||
|
||||
case NO_CONTENT:
|
||||
return_from_parse|=_handler.headerComplete(false,_persistent);
|
||||
return_from_parse|=_handler.headerComplete();
|
||||
_state=State.END;
|
||||
return_from_parse|=_handler.messageComplete(_contentPosition);
|
||||
break;
|
||||
|
||||
default:
|
||||
_state=State.CONTENT;
|
||||
return_from_parse|=_handler.headerComplete(true,_persistent);
|
||||
return_from_parse|=_handler.headerComplete();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1103,7 +1058,6 @@ public class HttpParser
|
|||
private void badMessage(ByteBuffer buffer, String reason)
|
||||
{
|
||||
BufferUtil.clear(buffer);
|
||||
_persistent=false;
|
||||
_state=State.END;
|
||||
_handler.badMessage(400, reason);
|
||||
}
|
||||
|
@ -1111,8 +1065,6 @@ public class HttpParser
|
|||
/* ------------------------------------------------------------------------------- */
|
||||
public void inputShutdown()
|
||||
{
|
||||
_persistent=false;
|
||||
|
||||
// was this unexpected?
|
||||
switch(_state)
|
||||
{
|
||||
|
@ -1139,15 +1091,18 @@ public class HttpParser
|
|||
{
|
||||
if (_state!=State.END && _state!=State.CLOSED)
|
||||
LOG.warn("Closing {}",this);
|
||||
_persistent=false;
|
||||
reset();
|
||||
_state=State.CLOSED;
|
||||
_endOfContent=EndOfContent.UNKNOWN_CONTENT;
|
||||
_contentPosition=0;
|
||||
_responseStatus=0;
|
||||
_contentChunk=null;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public void reset()
|
||||
{
|
||||
// reset state
|
||||
_state=_persistent?State.START:State.CLOSED;
|
||||
_state=State.START;
|
||||
_endOfContent=EndOfContent.UNKNOWN_CONTENT;
|
||||
_contentPosition=0;
|
||||
_responseStatus=0;
|
||||
|
@ -1159,19 +1114,16 @@ public class HttpParser
|
|||
{
|
||||
this._state=state;
|
||||
_endOfContent=EndOfContent.UNKNOWN_CONTENT;
|
||||
if (state==State.CLOSED)
|
||||
_persistent=false;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s{s=%s,c=%d,p=%b}",
|
||||
return String.format("%s{s=%s,c=%d}",
|
||||
getClass().getSimpleName(),
|
||||
_state,
|
||||
_contentLength,
|
||||
_persistent);
|
||||
_contentLength);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -1185,7 +1137,7 @@ public class HttpParser
|
|||
{
|
||||
public boolean content(ByteBuffer ref);
|
||||
|
||||
public boolean headerComplete(boolean hasBody,boolean persistent);
|
||||
public boolean headerComplete();
|
||||
|
||||
public boolean messageComplete(long contentLength);
|
||||
|
||||
|
|
|
@ -112,10 +112,9 @@ public class HttpTester
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean headerComplete(boolean hasBody, boolean persistent)
|
||||
public boolean headerComplete()
|
||||
{
|
||||
if (hasBody)
|
||||
_content=new ByteArrayOutputStream();
|
||||
_content=new ByteArrayOutputStream();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -587,6 +587,55 @@ public class HttpURI
|
|||
return new String(_raw,_path,length,_charset);
|
||||
return new String(bytes,0,n,_charset);
|
||||
}
|
||||
|
||||
|
||||
public String getDecodedPath(String encoding)
|
||||
{
|
||||
if (_path==_param)
|
||||
return null;
|
||||
|
||||
int length = _param-_path;
|
||||
byte[] bytes=null;
|
||||
int n=0;
|
||||
|
||||
for (int i=_path;i<_param;i++)
|
||||
{
|
||||
byte b = _raw[i];
|
||||
|
||||
if (b=='%')
|
||||
{
|
||||
if ((i+2)>=_param)
|
||||
throw new IllegalArgumentException("Bad % encoding: "+this);
|
||||
b=(byte)(0xff&TypeUtil.parseInt(_raw,i+1,2,16));
|
||||
i+=2;
|
||||
}
|
||||
else if (bytes==null)
|
||||
{
|
||||
n++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bytes==null)
|
||||
{
|
||||
bytes=new byte[length];
|
||||
System.arraycopy(_raw,_path,bytes,0,n);
|
||||
}
|
||||
|
||||
bytes[n++]=b;
|
||||
}
|
||||
|
||||
|
||||
if (bytes==null)
|
||||
return StringUtil.toString(_raw,_path,_param-_path,encoding);
|
||||
|
||||
return StringUtil.toString(bytes,0,n,encoding);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public String getPathAndParam()
|
||||
{
|
||||
|
|
|
@ -436,4 +436,25 @@ public class HttpFieldsTest
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContains() throws Exception
|
||||
{
|
||||
HttpFields header = new HttpFields();
|
||||
|
||||
header.add("0", "");
|
||||
header.add("1", ",");
|
||||
header.add("2", ",,");
|
||||
header.add("3", "abc");
|
||||
header.add("4", "def");
|
||||
header.add("5", "abc,def,hig");
|
||||
header.add("6", "abc");
|
||||
header.add("6", "def");
|
||||
header.add("6", "hig");
|
||||
|
||||
for (int i=0;i<7;i++)
|
||||
{
|
||||
assertFalse(""+i,header.getField(""+i).contains("xyz"));
|
||||
assertEquals(""+i,i>=4,header.getField(""+i).contains("def"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ public class HttpGeneratorServerTest
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean headerComplete(boolean hasBody,boolean persistent)
|
||||
public boolean headerComplete()
|
||||
{
|
||||
_content= null;
|
||||
return false;
|
||||
|
|
|
@ -735,7 +735,7 @@ public class HttpParserTest
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean headerComplete(boolean hasBody,boolean persistent)
|
||||
public boolean headerComplete()
|
||||
{
|
||||
//System.err.println("headerComplete");
|
||||
_content= null;
|
||||
|
|
|
@ -23,16 +23,20 @@ import java.util.Set;
|
|||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSessionEvent;
|
||||
import javax.servlet.http.HttpSessionListener;
|
||||
|
||||
import org.eclipse.jetty.security.authentication.DeferredAuthentication;
|
||||
import org.eclipse.jetty.server.Authentication;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpChannel;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.server.UserIdentity;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler.Context;
|
||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
import org.eclipse.jetty.server.session.AbstractSessionManager;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
@ -285,6 +289,33 @@ public abstract class SecurityHandler extends HandlerWrapper implements Authenti
|
|||
getInitParameter(name)==null)
|
||||
setInitParameter(name,context.getInitParameter(name));
|
||||
}
|
||||
|
||||
//register a session listener to handle securing sessions when authentication is performed
|
||||
context.getContextHandler().addEventListener(new HttpSessionListener()
|
||||
{
|
||||
|
||||
public void sessionDestroyed(HttpSessionEvent se)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void sessionCreated(HttpSessionEvent se)
|
||||
{
|
||||
//if current request is authenticated, then as we have just created the session, mark it as secure, as it has not yet been returned to a user
|
||||
HttpChannel channel = HttpChannel.getCurrentHttpChannel();
|
||||
|
||||
if (channel == null)
|
||||
return;
|
||||
Request request = channel.getRequest();
|
||||
if (request == null)
|
||||
return;
|
||||
|
||||
if (request.isSecure())
|
||||
{
|
||||
se.getSession().setAttribute(AbstractSessionManager.SESSION_KNOWN_ONLY_TO_AUTHENTICATED, Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// complicated resolution of login and identity service to handle
|
||||
|
|
|
@ -83,7 +83,7 @@ public class BasicAuthenticator extends LoginAuthenticator
|
|||
UserIdentity user = _loginService.login(username,password);
|
||||
if (user!=null)
|
||||
{
|
||||
renewSessionOnAuthentication(request,response);
|
||||
renewSession(request,response);
|
||||
return new UserAuthentication(getAuthMethod(),user);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ public class ClientCertAuthenticator extends LoginAuthenticator
|
|||
UserIdentity user = _loginService.login(username,credential);
|
||||
if (user!=null)
|
||||
{
|
||||
renewSessionOnAuthentication(request,response);
|
||||
renewSession(request,response);
|
||||
return new UserAuthentication(getAuthMethod(),user);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -183,7 +183,7 @@ public class DigestAuthenticator extends LoginAuthenticator
|
|||
UserIdentity user = _loginService.login(digest.username,digest);
|
||||
if (user!=null)
|
||||
{
|
||||
renewSessionOnAuthentication(request,response);
|
||||
renewSession(request,response);
|
||||
return new UserAuthentication(getAuthMethod(),user);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -189,8 +189,8 @@ public class FormAuthenticator extends LoginAuthenticator
|
|||
if (!mandatory)
|
||||
return _deferred;
|
||||
|
||||
if (isLoginOrErrorPage(URIUtil.addPaths(request.getServletPath(),request.getPathInfo())))
|
||||
return Authentication.NOT_CHECKED;
|
||||
if (isLoginOrErrorPage(URIUtil.addPaths(request.getServletPath(),request.getPathInfo())) &&!DeferredAuthentication.isDeferred(response))
|
||||
return _deferred;
|
||||
|
||||
HttpSession session = request.getSession(true);
|
||||
|
||||
|
@ -205,7 +205,7 @@ public class FormAuthenticator extends LoginAuthenticator
|
|||
UserIdentity user = _loginService.login(username,password);
|
||||
if (user!=null)
|
||||
{
|
||||
session=renewSessionOnAuthentication(request,response);
|
||||
session=renewSession(request,response);
|
||||
|
||||
// Redirect to original request
|
||||
String nuri;
|
||||
|
|
|
@ -13,10 +13,6 @@
|
|||
|
||||
package org.eclipse.jetty.security.authentication;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
@ -24,10 +20,10 @@ import javax.servlet.http.HttpSession;
|
|||
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;
|
||||
|
||||
public abstract class LoginAuthenticator implements Authenticator
|
||||
{
|
||||
public final static String SESSION_SECURED="org.eclipse.jetty.security.secured";
|
||||
protected final DeferredAuthentication _deferred=new DeferredAuthentication(this);
|
||||
protected LoginService _loginService;
|
||||
protected IdentityService _identityService;
|
||||
|
@ -53,34 +49,29 @@ public abstract class LoginAuthenticator implements Authenticator
|
|||
return _loginService;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Change the session when the request is authenticated for the first time
|
||||
/** Change the session id.
|
||||
* The session is changed to a new instance with a new ID if and only if:<ul>
|
||||
* <li>A session exists.
|
||||
* <li>The {@link AuthConfiguration#isSessionRenewedOnAuthentication()} returns true.
|
||||
* <li>The session ID has been given to unauthenticated responses
|
||||
* </ul>
|
||||
* @param request
|
||||
* @param response
|
||||
* @return The new session.
|
||||
*/
|
||||
protected HttpSession renewSessionOnAuthentication(HttpServletRequest request, HttpServletResponse response)
|
||||
protected HttpSession renewSession(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
HttpSession httpSession = request.getSession(false);
|
||||
if (_renewSession && httpSession!=null && httpSession.getAttribute(SESSION_SECURED)==null)
|
||||
|
||||
//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)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
Map<String,Object> attributes = new HashMap<String, Object>();
|
||||
for (Enumeration<String> e=httpSession.getAttributeNames();e.hasMoreElements();)
|
||||
{
|
||||
String name=e.nextElement();
|
||||
attributes.put(name,httpSession.getAttribute(name));
|
||||
httpSession.removeAttribute(name);
|
||||
}
|
||||
httpSession.invalidate();
|
||||
httpSession = request.getSession(true);
|
||||
httpSession.setAttribute(SESSION_SECURED,Boolean.TRUE);
|
||||
for (Map.Entry<String, Object> entry: attributes.entrySet())
|
||||
httpSession.setAttribute(entry.getKey(),entry.getValue());
|
||||
httpSession = AbstractSessionManager.renewSession(request, httpSession,true);
|
||||
}
|
||||
}
|
||||
|
||||
return httpSession;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.eclipse.jetty.security.SecurityHandler;
|
|||
import org.eclipse.jetty.server.Authentication;
|
||||
import org.eclipse.jetty.server.UserIdentity;
|
||||
import org.eclipse.jetty.server.UserIdentity.Scope;
|
||||
import org.eclipse.jetty.server.session.AbstractSessionManager;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
@ -102,7 +103,7 @@ public class SessionAuthentication implements Authentication.User, Serializable,
|
|||
if (security!=null)
|
||||
security.logout(this);
|
||||
if (_session!=null)
|
||||
_session.removeAttribute(LoginAuthenticator.SESSION_SECURED);
|
||||
_session.removeAttribute(AbstractSessionManager.SESSION_KNOWN_ONLY_TO_AUTHENTICATED);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -500,14 +500,17 @@ public abstract class HttpChannel
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean headerComplete(boolean hasBody,boolean persistent)
|
||||
public boolean headerComplete()
|
||||
{
|
||||
_requests++;
|
||||
boolean persistent;
|
||||
switch (_version)
|
||||
{
|
||||
case HTTP_0_9:
|
||||
persistent=false;
|
||||
break;
|
||||
case HTTP_1_0:
|
||||
persistent=_request.getHttpFields().contains(HttpHeader.CONNECTION,HttpHeaderValue.KEEP_ALIVE.asString());
|
||||
if (persistent)
|
||||
_response.getHttpFields().add(HttpHeader.CONNECTION,HttpHeaderValue.KEEP_ALIVE);
|
||||
|
||||
|
@ -516,7 +519,8 @@ public abstract class HttpChannel
|
|||
break;
|
||||
|
||||
case HTTP_1_1:
|
||||
|
||||
persistent=!_request.getHttpFields().contains(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE.asString());
|
||||
|
||||
if (!persistent)
|
||||
_response.getHttpFields().add(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE);
|
||||
|
||||
|
@ -537,7 +541,10 @@ public abstract class HttpChannel
|
|||
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
_request.setPersistent(persistent);
|
||||
|
||||
// Either handle now or wait for first content/message complete
|
||||
if (_expect100Continue)
|
||||
|
|
|
@ -252,16 +252,19 @@ public class HttpConnection extends AbstractConnection
|
|||
// Parse the buffer
|
||||
if (_parser.parseNext(_requestBuffer))
|
||||
{
|
||||
// reset header count
|
||||
_headerBytes=0;
|
||||
|
||||
// For most requests, there will not be a body, so we can try to recycle the buffer now
|
||||
releaseRequestBuffer();
|
||||
|
||||
_headerBytes=0;
|
||||
if (!_channel.getRequest().isPersistent())
|
||||
_generator.setPersistent(false);
|
||||
|
||||
// The parser returned true, which indicates the channel is ready
|
||||
// to handle a request. Call the channel and this will either handle the
|
||||
// request/response to completion OR if the request suspends, the channel
|
||||
// will be left in !idle state so our outer loop will exit.
|
||||
if (!_parser.isPersistent())
|
||||
_generator.setPersistent(false);
|
||||
_channel.handle();
|
||||
|
||||
// Return if the channel is still processing the request
|
||||
|
|
|
@ -159,6 +159,7 @@ public class Request implements HttpServletRequest
|
|||
private SessionManager _sessionManager;
|
||||
private long _timeStamp;
|
||||
private long _dispatchTime;
|
||||
private boolean _persistent;
|
||||
|
||||
private HttpURI _uri;
|
||||
|
||||
|
@ -184,7 +185,19 @@ public class Request implements HttpServletRequest
|
|||
{
|
||||
return _in;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public boolean isPersistent()
|
||||
{
|
||||
return _persistent;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void setPersistent(boolean persistent)
|
||||
{
|
||||
_persistent = persistent;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void addEventListener(final EventListener listener)
|
||||
{
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.eclipse.jetty.util.ByteArrayISO8859Writer;
|
|||
public class ErrorHandler extends AbstractHandler
|
||||
{
|
||||
boolean _showStacks=true;
|
||||
boolean _showMessageInTitle=true;
|
||||
String _cacheControl="must-revalidate,no-cache,no-store";
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -86,12 +87,16 @@ public class ErrorHandler extends AbstractHandler
|
|||
/* ------------------------------------------------------------ */
|
||||
protected void writeErrorPageHead(HttpServletRequest request, Writer writer, int code, String message)
|
||||
throws IOException
|
||||
{
|
||||
{
|
||||
writer.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\"/>\n");
|
||||
writer.write("<title>Error ");
|
||||
writer.write(Integer.toString(code));
|
||||
writer.write(' ');
|
||||
write(writer,message);
|
||||
|
||||
if (_showMessageInTitle)
|
||||
{
|
||||
writer.write(' ');
|
||||
write(writer,message);
|
||||
}
|
||||
writer.write("</title>\n");
|
||||
}
|
||||
|
||||
|
@ -175,6 +180,22 @@ public class ErrorHandler extends AbstractHandler
|
|||
{
|
||||
_showStacks = showStacks;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param showMessageInTitle if true, the error message appears in page title
|
||||
*/
|
||||
public void setShowMessageInTitle(boolean showMessageInTitle)
|
||||
{
|
||||
_showMessageInTitle = showMessageInTitle;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public boolean getShowMessageInTitle()
|
||||
{
|
||||
return _showMessageInTitle;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected void write(Writer writer,String string)
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.util.Collections;
|
|||
import java.util.Enumeration;
|
||||
import java.util.EventListener;
|
||||
import java.util.HashSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -69,6 +70,8 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
|
|||
new HashSet<SessionTrackingMode>(
|
||||
Arrays.asList(new SessionTrackingMode[]{SessionTrackingMode.COOKIE,SessionTrackingMode.URL})));
|
||||
|
||||
public final static String SESSION_KNOWN_ONLY_TO_AUTHENTICATED="org.eclipse.jetty.security.sessionKnownOnlytoAuthenticated";
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public final static int __distantFuture=60*60*24*7*52*20;
|
||||
|
||||
|
@ -121,6 +124,28 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement
|
|||
protected final CounterStatistic _sessionsStats = new CounterStatistic();
|
||||
protected final SampleStatistic _sessionTimeStats = new SampleStatistic();
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public static HttpSession renewSession (HttpServletRequest request, HttpSession httpSession, boolean authenticated)
|
||||
{
|
||||
Map<String,Object> attributes = new HashMap<String, Object>();
|
||||
|
||||
for (Enumeration<String> e=httpSession.getAttributeNames();e.hasMoreElements();)
|
||||
{
|
||||
String name=e.nextElement();
|
||||
attributes.put(name,httpSession.getAttribute(name));
|
||||
httpSession.removeAttribute(name);
|
||||
}
|
||||
|
||||
httpSession.invalidate();
|
||||
httpSession = request.getSession(true);
|
||||
if (authenticated)
|
||||
httpSession.setAttribute(SESSION_KNOWN_ONLY_TO_AUTHENTICATED, Boolean.TRUE);
|
||||
for (Map.Entry<String, Object> entry: attributes.entrySet())
|
||||
httpSession.setAttribute(entry.getKey(),entry.getValue());
|
||||
return httpSession;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public AbstractSessionManager()
|
||||
{
|
||||
|
|
|
@ -121,6 +121,19 @@ public class HttpConnectionTest
|
|||
System.err.println(response);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoPath() throws Exception
|
||||
{
|
||||
String response=connector.getResponses("GET http://localhost:80 HTTP/1.1\n"+
|
||||
"Host: localhost:80\n"+
|
||||
"\n");
|
||||
|
||||
int offset=0;
|
||||
offset = checkContains(response,offset,"HTTP/1.1 200");
|
||||
checkContains(response,offset,"pathInfo=/");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEmpty() throws Exception
|
||||
|
@ -183,9 +196,15 @@ public class HttpConnectionTest
|
|||
|
||||
response=connector.getResponses("GET % HTTP/1.1\n"+
|
||||
"Host: localhost\n"+
|
||||
"Connection: close\n"+
|
||||
"Connection: close\n"+
|
||||
"\015\012");
|
||||
checkContains(response,0,"HTTP/1.1 400");
|
||||
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
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -182,10 +182,11 @@ public class HttpURITest
|
|||
|
||||
private final String[][] encoding_tests=
|
||||
{
|
||||
/* 0*/ {"/path/info","/path/info"},
|
||||
/* 1*/ {"/path/%69nfo","/path/info"},
|
||||
/* 2*/ {"http://host/path/%69nfo","/path/info"},
|
||||
/* 3*/ {"http://host/path/%69nf%c2%a4","/path/inf\u00a4"},
|
||||
/* 0*/ {"/path/info","/path/info", "UTF-8"},
|
||||
/* 1*/ {"/path/%69nfo","/path/info", "UTF-8"},
|
||||
/* 2*/ {"http://host/path/%69nfo","/path/info", "UTF-8"},
|
||||
/* 3*/ {"http://host/path/%69nf%c2%a4","/path/inf\u00a4", "UTF-8"},
|
||||
/* 4*/ {"http://host/path/%E5", "/path/\u00e5", "ISO-8859-1"}
|
||||
};
|
||||
|
||||
@Test
|
||||
|
@ -196,7 +197,7 @@ public class HttpURITest
|
|||
for (int t=0;t<encoding_tests.length;t++)
|
||||
{
|
||||
uri.parse(encoding_tests[t][0]);
|
||||
assertEquals(""+t,encoding_tests[t][1],uri.getDecodedPath());
|
||||
assertEquals(""+t,encoding_tests[t][1],uri.getDecodedPath(encoding_tests[t][2]));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,9 @@ import org.eclipse.jetty.server.handler.ContextHandler;
|
|||
import org.eclipse.jetty.server.session.HashSessionIdManager;
|
||||
import org.eclipse.jetty.server.session.HashSessionManager;
|
||||
import org.eclipse.jetty.server.session.HashedSession;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
@ -585,6 +587,7 @@ public class ResponseTest
|
|||
{
|
||||
server.setHandler(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String string, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
response.setStatus(200);
|
||||
|
@ -607,14 +610,14 @@ public class ResponseTest
|
|||
|
||||
LineNumberReader reader = new LineNumberReader(new InputStreamReader(socket.getInputStream()));
|
||||
String line = reader.readLine();
|
||||
assertTrue(line!=null && line.startsWith("HTTP/1.1 200 OK"));
|
||||
Assert.assertThat(line,Matchers.startsWith("HTTP/1.1 200 OK"));
|
||||
// look for blank line
|
||||
while (line!=null && line.length()>0)
|
||||
line = reader.readLine();
|
||||
|
||||
// Read the first line of the GET
|
||||
line = reader.readLine();
|
||||
assertTrue(line!=null && line.startsWith("HTTP/1.1 200 OK"));
|
||||
Assert.assertThat(line,Matchers.startsWith("HTTP/1.1 200 OK"));
|
||||
|
||||
String last=null;
|
||||
while (line!=null)
|
||||
|
|
|
@ -1305,7 +1305,7 @@ public class StandardSession implements ISession, Parser.Listener, Callback<Stan
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("DATA bytes @%x available=%d consumed=%d on %s",dataInfo.hashCode(),dataInfo.available(),dataInfo.consumed(),getStream());
|
||||
return String.format("DATA bytes @%x available=%d consumed=%d on %s", dataInfo.hashCode(), dataInfo.available(), dataInfo.consumed(), getStream());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,10 +16,10 @@ package org.eclipse.jetty.util;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** AttributesMap.
|
||||
|
@ -33,7 +33,7 @@ public class AttributesMap implements Attributes
|
|||
/* ------------------------------------------------------------ */
|
||||
public AttributesMap()
|
||||
{
|
||||
_map=new HashMap<String,Object>();
|
||||
_map=new ConcurrentHashMap<String,Object>();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -45,13 +45,14 @@ public class AttributesMap implements Attributes
|
|||
/* ------------------------------------------------------------ */
|
||||
public AttributesMap(AttributesMap map)
|
||||
{
|
||||
_map=new HashMap<String,Object>(map._map);
|
||||
_map=new ConcurrentHashMap<String,Object>(map._map);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
* @see org.eclipse.jetty.util.Attributes#removeAttribute(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void removeAttribute(String name)
|
||||
{
|
||||
_map.remove(name);
|
||||
|
@ -61,6 +62,7 @@ public class AttributesMap implements Attributes
|
|||
/*
|
||||
* @see org.eclipse.jetty.util.Attributes#setAttribute(java.lang.String, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public void setAttribute(String name, Object attribute)
|
||||
{
|
||||
if (attribute==null)
|
||||
|
@ -73,6 +75,7 @@ public class AttributesMap implements Attributes
|
|||
/*
|
||||
* @see org.eclipse.jetty.util.Attributes#getAttribute(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Object getAttribute(String name)
|
||||
{
|
||||
return _map.get(name);
|
||||
|
@ -82,6 +85,7 @@ public class AttributesMap implements Attributes
|
|||
/*
|
||||
* @see org.eclipse.jetty.util.Attributes#getAttributeNames()
|
||||
*/
|
||||
@Override
|
||||
public Enumeration<String> getAttributeNames()
|
||||
{
|
||||
return Collections.enumeration(_map.keySet());
|
||||
|
@ -120,6 +124,7 @@ public class AttributesMap implements Attributes
|
|||
/*
|
||||
* @see org.eclipse.jetty.util.Attributes#clear()
|
||||
*/
|
||||
@Override
|
||||
public void clearAttributes()
|
||||
{
|
||||
_map.clear();
|
||||
|
|
|
@ -350,7 +350,7 @@ public class StdErrLog extends AbstractLogger
|
|||
this._level = LEVEL_DEBUG;
|
||||
|
||||
for (Logger log : Log.getLoggers().values())
|
||||
{
|
||||
{
|
||||
if (log.getName().startsWith(getName()) && log instanceof StdErrLog)
|
||||
((StdErrLog)log).setLevel(LEVEL_DEBUG);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
//========================================================================
|
||||
//Copyright 2012 Mort Bay Consulting Pty. Ltd.
|
||||
//------------------------------------------------------------------------
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//http://www.apache.org/licenses/LICENSE-2.0
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
//========================================================================
|
||||
|
||||
package org.eclipse.jetty.util.preventers;
|
||||
|
||||
import java.awt.Toolkit;
|
||||
|
||||
/**
|
||||
* AWTLeakPreventer
|
||||
*
|
||||
* See https://issues.jboss.org/browse/AS7-3733
|
||||
*
|
||||
* The java.awt.Toolkit class has a static field that is the default toolkit.
|
||||
* Creating the default toolkit causes the creation of an EventQueue, which has a
|
||||
* classloader field initialized by the thread context class loader.
|
||||
*
|
||||
*/
|
||||
public class AWTLeakPreventer extends AbstractLeakPreventer
|
||||
{
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
|
||||
*/
|
||||
@Override
|
||||
public void prevent(ClassLoader loader)
|
||||
{
|
||||
LOG.debug("Pinning classloader for java.awt.EventQueue using "+loader);
|
||||
Toolkit.getDefaultToolkit();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 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.util.preventers;
|
||||
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/**
|
||||
* AbstractLeakPreventer
|
||||
*
|
||||
* Abstract base class for code that seeks to avoid pinning of webapp classloaders by using the jetty classloader to
|
||||
* proactively call the code that pins them (generally pinned as static data members, or as static
|
||||
* data members that are daemon threads (which use the context classloader)).
|
||||
*
|
||||
* Instances of subclasses of this class should be set with Server.addBean(), which will
|
||||
* ensure that they are called when the Server instance starts up, which will have the jetty
|
||||
* classloader in scope.
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractLeakPreventer extends AbstractLifeCycle
|
||||
{
|
||||
protected static final Logger LOG = Log.getLogger(AbstractLeakPreventer.class);
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
abstract public void prevent(ClassLoader loader);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
try
|
||||
{
|
||||
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
|
||||
prevent(getClass().getClassLoader());
|
||||
super.doStart();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Thread.currentThread().setContextClassLoader(loader);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 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.util.preventers;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
/**
|
||||
* AppContextLeakPreventer
|
||||
*
|
||||
* Cause the classloader that is pinned by AppContext.getAppContext() to be
|
||||
* a container or system classloader, not a webapp classloader.
|
||||
*
|
||||
* Inspired by Tomcat JreMemoryLeakPrevention.
|
||||
*/
|
||||
public class AppContextLeakPreventer extends AbstractLeakPreventer
|
||||
{
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void prevent(ClassLoader loader)
|
||||
{
|
||||
LOG.debug("Pinning classloader for AppContext.getContext() with "+loader);
|
||||
ImageIO.getUseCache();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
//========================================================================
|
||||
//Copyright 2012 Mort Bay Consulting Pty. Ltd.
|
||||
//------------------------------------------------------------------------
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//http://www.apache.org/licenses/LICENSE-2.0
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
//========================================================================
|
||||
|
||||
package org.eclipse.jetty.util.preventers;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
/**
|
||||
* DOMLeakPreventer
|
||||
*
|
||||
* See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6916498
|
||||
*
|
||||
* Prevent the RuntimeException that is a static member of AbstractDOMParser
|
||||
* from pinning a webapp classloader by causing it to be set here by a non-webapp classloader.
|
||||
*
|
||||
* Note that according to the bug report, a heap dump may not identify the GCRoot, making
|
||||
* it difficult to identify the cause of the leak.
|
||||
*
|
||||
*/
|
||||
public class DOMLeakPreventer extends AbstractLeakPreventer
|
||||
{
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
|
||||
*/
|
||||
@Override
|
||||
public void prevent(ClassLoader loader)
|
||||
{
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
try
|
||||
{
|
||||
factory.newDocumentBuilder();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 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.util.preventers;
|
||||
|
||||
import java.sql.DriverManager;
|
||||
|
||||
|
||||
/**
|
||||
* DriverManagerLeakPreventer
|
||||
*
|
||||
* Cause DriverManager.getCallerClassLoader() to be called, which will pin the classloader.
|
||||
*
|
||||
* Inspired by Tomcat JreMemoryLeakPrevention.
|
||||
*/
|
||||
public class DriverManagerLeakPreventer extends AbstractLeakPreventer
|
||||
{
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void prevent(ClassLoader loader)
|
||||
{
|
||||
LOG.debug("Pinning DriverManager classloader with "+loader);
|
||||
DriverManager.getDrivers();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
//========================================================================
|
||||
//Copyright 2012 Mort Bay Consulting Pty. Ltd.
|
||||
//------------------------------------------------------------------------
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//http://www.apache.org/licenses/LICENSE-2.0
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
//========================================================================
|
||||
|
||||
package org.eclipse.jetty.util.preventers;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* GCThreadLeakPreventer
|
||||
*
|
||||
* Prevents a call to sun.misc.GC.requestLatency pinning a webapp classloader
|
||||
* by calling it with a non-webapp classloader. The problem appears to be that
|
||||
* when this method is called, a daemon thread is created which takes the
|
||||
* context classloader. A known caller of this method is the RMI impl. See
|
||||
* http://stackoverflow.com/questions/6626680/does-java-garbage-collection-log-entry-full-gc-system-mean-some-class-called
|
||||
*
|
||||
* This preventer will start the thread with the longest possible interval, although
|
||||
* subsequent calls can vary that. Recommend to only use this class if you're doing
|
||||
* RMI.
|
||||
*
|
||||
* Inspired by Tomcat JreMemoryLeakPrevention.
|
||||
*
|
||||
*/
|
||||
public class GCThreadLeakPreventer extends AbstractLeakPreventer
|
||||
{
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
|
||||
*/
|
||||
@Override
|
||||
public void prevent(ClassLoader loader)
|
||||
{
|
||||
try
|
||||
{
|
||||
Class clazz = Class.forName("sun.misc.GC");
|
||||
Method requestLatency = clazz.getMethod("requestLatency", new Class[] {long.class});
|
||||
requestLatency.invoke(null, Long.valueOf(Long.MAX_VALUE-1));
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
LOG.ignore(e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
//========================================================================
|
||||
//Copyright 2012 Mort Bay Consulting Pty. Ltd.
|
||||
//------------------------------------------------------------------------
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//http://www.apache.org/licenses/LICENSE-2.0
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
//========================================================================
|
||||
|
||||
package org.eclipse.jetty.util.preventers;
|
||||
|
||||
/**
|
||||
* Java2DLeakPreventer
|
||||
*
|
||||
* Prevent pinning of webapp classloader by pre-loading sun.java2d.Disposer class
|
||||
* before webapp classloaders are created.
|
||||
*
|
||||
* See https://issues.apache.org/bugzilla/show_bug.cgi?id=51687
|
||||
*
|
||||
*/
|
||||
public class Java2DLeakPreventer extends AbstractLeakPreventer
|
||||
{
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
|
||||
*/
|
||||
@Override
|
||||
public void prevent(ClassLoader loader)
|
||||
{
|
||||
try
|
||||
{
|
||||
Class.forName("sun.java2d.Disposer", true, loader);
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
LOG.ignore(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
//========================================================================
|
||||
//Copyright 2012 Mort Bay Consulting Pty. Ltd.
|
||||
//------------------------------------------------------------------------
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//http://www.apache.org/licenses/LICENSE-2.0
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
//========================================================================
|
||||
|
||||
package org.eclipse.jetty.util.preventers;
|
||||
|
||||
/**
|
||||
* LDAPLeakPreventer
|
||||
*
|
||||
* If com.sun.jndi.LdapPoolManager class is loaded and the system property
|
||||
* com.sun.jndi.ldap.connect.pool.timeout is set to a nonzero value, a daemon
|
||||
* thread is started which can pin a webapp classloader if it is the first to
|
||||
* load the LdapPoolManager.
|
||||
*
|
||||
* Inspired by Tomcat JreMemoryLeakPrevention
|
||||
*
|
||||
*/
|
||||
public class LDAPLeakPreventer extends AbstractLeakPreventer
|
||||
{
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
|
||||
*/
|
||||
@Override
|
||||
public void prevent(ClassLoader loader)
|
||||
{
|
||||
try
|
||||
{
|
||||
Class.forName("com.sun.jndi.LdapPoolManager", true, loader);
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
LOG.ignore(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
//========================================================================
|
||||
//Copyright 2012 Mort Bay Consulting Pty. Ltd.
|
||||
//------------------------------------------------------------------------
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//http://www.apache.org/licenses/LICENSE-2.0
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
//========================================================================
|
||||
|
||||
package org.eclipse.jetty.util.preventers;
|
||||
|
||||
/**
|
||||
* LoginConfigurationLeakPreventer
|
||||
*
|
||||
* The javax.security.auth.login.Configuration class keeps a static reference to the
|
||||
* thread context classloader. We prevent a webapp context classloader being used for
|
||||
* that by invoking the classloading here.
|
||||
*
|
||||
* Inspired by Tomcat JreMemoryLeakPrevention
|
||||
*/
|
||||
public class LoginConfigurationLeakPreventer extends AbstractLeakPreventer
|
||||
{
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
|
||||
*/
|
||||
@Override
|
||||
public void prevent(ClassLoader loader)
|
||||
{
|
||||
try
|
||||
{
|
||||
Class.forName("javax.security.auth.login.Configuration", true, loader);
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
//========================================================================
|
||||
//Copyright 2012 Mort Bay Consulting Pty. Ltd.
|
||||
//------------------------------------------------------------------------
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//http://www.apache.org/licenses/LICENSE-2.0
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
//========================================================================
|
||||
|
||||
package org.eclipse.jetty.util.preventers;
|
||||
|
||||
import java.security.Security;
|
||||
|
||||
/**
|
||||
* SecurityProviderLeakPreventer
|
||||
*
|
||||
* Some security providers, such as sun.security.pkcs11.SunPKCS11 start a deamon thread,
|
||||
* which will use the thread context classloader. Load them here to ensure the classloader
|
||||
* is not a webapp classloader.
|
||||
*
|
||||
* Inspired by Tomcat JreMemoryLeakPrevention
|
||||
*/
|
||||
public class SecurityProviderLeakPreventer extends AbstractLeakPreventer
|
||||
{
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.util.preventers.AbstractLeakPreventer#prevent(java.lang.ClassLoader)
|
||||
*/
|
||||
@Override
|
||||
public void prevent(ClassLoader loader)
|
||||
{
|
||||
Security.getProviders();
|
||||
}
|
||||
|
||||
}
|
|
@ -19,6 +19,7 @@ import java.net.MalformedURLException;
|
|||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
|
@ -108,7 +109,7 @@ class JarFileResource extends JarResource
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Returns true if the respresenetd resource exists.
|
||||
* Returns true if the represented resource exists.
|
||||
*/
|
||||
@Override
|
||||
public boolean exists()
|
||||
|
@ -240,68 +241,102 @@ class JarFileResource extends JarResource
|
|||
@Override
|
||||
public synchronized String[] list()
|
||||
{
|
||||
|
||||
if(isDirectory() && _list==null)
|
||||
if (isDirectory() && _list==null)
|
||||
{
|
||||
ArrayList<String> list = new ArrayList<>(32);
|
||||
List<String> list = null;
|
||||
try
|
||||
{
|
||||
list = listEntries();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
//Sun's JarURLConnection impl for jar: protocol will close a JarFile in its connect() method if
|
||||
//useCaches == false (eg someone called URLConnection with defaultUseCaches==true).
|
||||
//As their sun.net.www.protocol.jar package caches JarFiles and/or connections, we can wind up in
|
||||
//the situation where the JarFile we have remembered in our _jarFile member has actually been closed
|
||||
//by other code.
|
||||
//So, do one retry to drop a connection and get a fresh JarFile
|
||||
LOG.warn("Retrying list:"+e);
|
||||
LOG.debug(e);
|
||||
release();
|
||||
list = listEntries();
|
||||
}
|
||||
|
||||
checkConnection();
|
||||
|
||||
JarFile jarFile=_jarFile;
|
||||
if(jarFile==null)
|
||||
if (list != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
JarURLConnection jc=(JarURLConnection)((new URL(_jarUrl)).openConnection());
|
||||
jc.setUseCaches(getUseCaches());
|
||||
jarFile=jc.getJarFile();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.ignore(e);
|
||||
}
|
||||
if(jarFile==null)
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
Enumeration<JarEntry> e=jarFile.entries();
|
||||
String dir=_urlString.substring(_urlString.indexOf("!/")+2);
|
||||
while(e.hasMoreElements())
|
||||
{
|
||||
JarEntry entry = e.nextElement();
|
||||
String name=entry.getName().replace('\\','/');
|
||||
if(!name.startsWith(dir) || name.length()==dir.length())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String listName=name.substring(dir.length());
|
||||
int dash=listName.indexOf('/');
|
||||
if (dash>=0)
|
||||
{
|
||||
//when listing jar:file urls, you get back one
|
||||
//entry for the dir itself, which we ignore
|
||||
if (dash==0 && listName.length()==1)
|
||||
continue;
|
||||
//when listing jar:file urls, all files and
|
||||
//subdirs have a leading /, which we remove
|
||||
if (dash==0)
|
||||
listName=listName.substring(dash+1, listName.length());
|
||||
else
|
||||
listName=listName.substring(0,dash+1);
|
||||
|
||||
if (list.contains(listName))
|
||||
continue;
|
||||
}
|
||||
|
||||
list.add(listName);
|
||||
}
|
||||
|
||||
_list=new String[list.size()];
|
||||
list.toArray(_list);
|
||||
_list=new String[list.size()];
|
||||
list.toArray(_list);
|
||||
}
|
||||
}
|
||||
return _list;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private List<String> listEntries ()
|
||||
{
|
||||
checkConnection();
|
||||
|
||||
ArrayList<String> list = new ArrayList<String>(32);
|
||||
JarFile jarFile=_jarFile;
|
||||
if(jarFile==null)
|
||||
{
|
||||
try
|
||||
{
|
||||
JarURLConnection jc=(JarURLConnection)((new URL(_jarUrl)).openConnection());
|
||||
jc.setUseCaches(getUseCaches());
|
||||
jarFile=jc.getJarFile();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
|
||||
e.printStackTrace();
|
||||
LOG.ignore(e);
|
||||
}
|
||||
if(jarFile==null)
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
Enumeration e=jarFile.entries();
|
||||
String dir=_urlString.substring(_urlString.indexOf("!/")+2);
|
||||
while(e.hasMoreElements())
|
||||
{
|
||||
|
||||
JarEntry entry = (JarEntry) e.nextElement();
|
||||
String name=entry.getName().replace('\\','/');
|
||||
if(!name.startsWith(dir) || name.length()==dir.length())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String listName=name.substring(dir.length());
|
||||
int dash=listName.indexOf('/');
|
||||
if (dash>=0)
|
||||
{
|
||||
//when listing jar:file urls, you get back one
|
||||
//entry for the dir itself, which we ignore
|
||||
if (dash==0 && listName.length()==1)
|
||||
continue;
|
||||
//when listing jar:file urls, all files and
|
||||
//subdirs have a leading /, which we remove
|
||||
if (dash==0)
|
||||
listName=listName.substring(dash+1, listName.length());
|
||||
else
|
||||
listName=listName.substring(0,dash+1);
|
||||
|
||||
if (list.contains(listName))
|
||||
continue;
|
||||
}
|
||||
|
||||
list.add(listName);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Return the length of the resource
|
||||
|
|
|
@ -77,18 +77,28 @@ public class StdErrLogTest
|
|||
log.debug("YOU SHOULD NOT SEE THIS!",null,null);
|
||||
|
||||
// Test for backward compat with old (now deprecated) method
|
||||
Logger before = log.getLogger("before");
|
||||
log.setDebugEnabled(true);
|
||||
Logger after = log.getLogger("after");
|
||||
before.debug("testing {} {}","test","debug-before");
|
||||
log.debug("testing {} {}","test","debug-deprecated");
|
||||
after.debug("testing {} {}","test","debug-after");
|
||||
|
||||
log.setDebugEnabled(false);
|
||||
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:xxx:tname: testing test debug-deprecated");
|
||||
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");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
44
pom.xml
44
pom.xml
|
@ -51,7 +51,7 @@
|
|||
</goals>
|
||||
<configuration>
|
||||
<resourceBundles>
|
||||
<resourceBundle>org.eclipse.jetty.toolchain:jetty-artifact-remote-resources:1.0</resourceBundle>
|
||||
<resourceBundle>org.eclipse.jetty.toolchain:jetty-artifact-remote-resources:1.1</resourceBundle>
|
||||
</resourceBundles>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
@ -623,5 +623,47 @@
|
|||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>license-check</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<inherited>false</inherited>
|
||||
<groupId>com.mycila.maven-license-plugin</groupId>
|
||||
<artifactId>maven-license-plugin</artifactId>
|
||||
<version>1.10.b1</version>
|
||||
<configuration>
|
||||
<header>header-template.txt</header>
|
||||
<failIfMissing>true</failIfMissing>
|
||||
<aggregate>true</aggregate>
|
||||
<strictCheck>true</strictCheck>
|
||||
<properties>
|
||||
<copyright-range>${project.inceptionYear}-2012</copyright-range>
|
||||
</properties>
|
||||
<mapping>
|
||||
<java>DOUBLESLASH_STYLE</java>
|
||||
</mapping>
|
||||
<includes>
|
||||
<include>**/*.java</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>jetty-util/src/main/java/org/eclipse/jetty/util/security/UnixCrypt.java</exclude>
|
||||
<exclude>jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/DefaultPolicyLoader.java</exclude>
|
||||
<exclude>jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/PolicyFileScanner.java</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>check-headers</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
|
Loading…
Reference in New Issue