//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
{
_resource.close();
}
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s@%x{r=%s}",this.getClass().getSimpleName(),hashCode(),_resource);
+ }
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
return false;
}
+
+ public boolean contains(HttpHeader header)
+ {
+ for (int i=0;i<_fields.size();i++)
+ {
+ HttpField f=_fields.get(i);
+ if (f.getHeader()==header)
+ return true;
+ }
+ return false;
+ }
public boolean containsKey(String name)
{
}
return false;
}
-
+
+
public String getStringField(HttpHeader header)
{
return getStringField(header.asString());
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
import org.eclipse.jetty.http.HttpTokens.EndOfContent;
import org.eclipse.jetty.util.BufferUtil;
private final int _send;
private final static int SEND_SERVER = 0x01;
private final static int SEND_XPOWEREDBY = 0x02;
-
-
+ private final static Set<String> __assumedContentMethods = new HashSet<>(Arrays.asList(new String[]{HttpMethod.POST.asString(),HttpMethod.PUT.asString()}));
+
/* ------------------------------------------------------------------------------- */
public static void setJettyVersion(String serverVersion)
{
return _endOfContent==EndOfContent.CHUNKED_CONTENT;
}
+ /* ------------------------------------------------------------ */
+ public boolean isNoContent()
+ {
+ return _noContent;
+ }
+
/* ------------------------------------------------------------ */
public void setPersistent(boolean persistent)
{
{
if (BufferUtil.hasContent(content))
{
- LOG.debug("discarding content in COMPLETING");
+ if (LOG.isDebugEnabled())
+ LOG.debug("discarding content in COMPLETING");
BufferUtil.clear(content);
}
case END:
if (BufferUtil.hasContent(content))
{
- LOG.debug("discarding content in COMPLETING");
+ if (LOG.isDebugEnabled())
+ LOG.debug("discarding content in COMPLETING");
BufferUtil.clear(content);
}
return Result.DONE;
{
if (BufferUtil.hasContent(content))
{
- LOG.debug("discarding content in COMPLETING");
+ if (LOG.isDebugEnabled())
+ LOG.debug("discarding content in COMPLETING");
BufferUtil.clear(content);
}
case END:
if (BufferUtil.hasContent(content))
{
- LOG.debug("discarding content in COMPLETING");
+ if (LOG.isDebugEnabled())
+ LOG.debug("discarding content in COMPLETING");
BufferUtil.clear(content);
}
return Result.DONE;
if (values[0]==null)
{
- split = field.getValue().split("\\s*,\\s*");
+ split = StringUtil.csvSplit(field.getValue());
if (split.length>0)
{
values=new HttpHeaderValue[split.length];
long content_length = _contentPrepared+BufferUtil.length(content);
// Do we need to tell the headers about it
- if ((response!=null || content_length>0 || content_type ) && !_noContent)
+ if (content_length>0)
{
header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
BufferUtil.putDecLong(header, content_length);
header.put(HttpTokens.CRLF);
}
+ else if (!_noContent)
+ {
+ if (content_type || response!=null || (request!=null && __assumedContentMethods.contains(request.getMethod())))
+ header.put(CONTENT_LENGTH_0);
+ }
}
else
{
case CONTENT_LENGTH:
long content_length = _info.getContentLength();
- if ((response!=null || content_length>0 || content_type ) && !_noContent)
+ if (content_length>0)
{
- // known length but not actually set.
header.put(HttpHeader.CONTENT_LENGTH.getBytesColonSpace());
BufferUtil.putDecLong(header, content_length);
header.put(HttpTokens.CRLF);
}
+ else if (!_noContent)
+ {
+ if (content_type || response!=null || (request!=null && __assumedContentMethods.contains(request.getMethod())))
+ header.put(CONTENT_LENGTH_0);
+ }
break;
case NO_CONTENT:
- if (response!=null && status >= 200 && status != 204 && status != 304)
- header.put(CONTENT_LENGTH_0);
- break;
+ throw new IllegalStateException();
case EOF_CONTENT:
_persistent = request!=null;
char c=s.charAt(i);
if (c<0 || c>0xff || c=='\r' || c=='\n')
- buffer.put((byte)'?');
+ buffer.put((byte)' ');
else
buffer.put((byte)(0xff&c));
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
TRACE,
CONNECT,
MOVE,
- PROXY;
+ PROXY,
+ PRI;
/* ------------------------------------------------------------ */
/**
- * Optimised lookup to find a method name and trailing space in a byte array.
+ * Optimized lookup to find a method name and trailing space in a byte array.
* @param bytes Array containing ISO-8859-1 characters
* @param position The first valid index
* @param limit The first non valid index
return PROXY;
if (bytes[position+1]=='U' && bytes[position+2]=='T' && bytes[position+3]==' ')
return PUT;
+ if (bytes[position+1]=='R' && bytes[position+2]=='I' && bytes[position+3]==' ')
+ return PRI;
break;
case 'H':
if (bytes[position+1]=='E' && bytes[position+2]=='A' && bytes[position+3]=='D' && length>=5 && bytes[position+4]==' ')
return HEAD;
break;
case 'O':
- if (bytes[position+1]=='O' && bytes[position+2]=='T' && bytes[position+3]=='I' && length>=8 &&
- bytes[position+4]=='O' && bytes[position+5]=='N' && bytes[position+6]=='S' && bytes[position+7]==' ' )
+ if (bytes[position+1]=='P' && bytes[position+2]=='T' && bytes[position+3]=='I' && length>=8 &&
+ bytes[position+4]=='O' && bytes[position+5]=='N' && bytes[position+6]=='S' && bytes[position+7]==' ' )
return OPTIONS;
break;
case 'D':
if (bytes[position+1]=='E' && bytes[position+2]=='L' && bytes[position+3]=='E' && length>=7 &&
- bytes[position+4]=='T' && bytes[position+5]=='E' && bytes[position+6]==' ' )
+ bytes[position+4]=='T' && bytes[position+5]=='E' && bytes[position+6]==' ' )
return DELETE;
break;
case 'T':
if (bytes[position+1]=='R' && bytes[position+2]=='A' && bytes[position+3]=='C' && length>=6 &&
- bytes[position+4]=='E' && bytes[position+5]==' ' )
+ bytes[position+4]=='E' && bytes[position+5]==' ' )
return TRACE;
break;
case 'C':
if (bytes[position+1]=='O' && bytes[position+2]=='N' && bytes[position+3]=='N' && length>=8 &&
- bytes[position+4]=='E' && bytes[position+5]=='C' && bytes[position+6]=='T' && bytes[position+7]==' ' )
+ bytes[position+4]=='E' && bytes[position+5]=='C' && bytes[position+6]=='T' && bytes[position+7]==' ' )
return CONNECT;
break;
case 'M':
- if (bytes[position+1]=='O' && bytes[position+2]=='V' && bytes[position+3]=='E' && bytes[position+4]==' ')
+ if (bytes[position+1]=='O' && bytes[position+2]=='V' && bytes[position+3]=='E' && length>=5 && bytes[position+4]==' ')
return MOVE;
break;
/* ------------------------------------------------------------ */
/**
- * Optimised lookup to find a method name and trailing space in a byte array.
- * @param buffer buffer containing ISO-8859-1 characters
+ * Optimized lookup to find a method name and trailing space in a byte array.
+ * @param buffer buffer containing ISO-8859-1 characters, it is not modified.
* @return A HttpMethod if a match or null if no easy match.
*/
public static HttpMethod lookAheadGet(ByteBuffer buffer)
{
if (buffer.hasArray())
return lookAheadGet(buffer.array(),buffer.arrayOffset()+buffer.position(),buffer.arrayOffset()+buffer.limit());
-
- // TODO use cache and check for space
- // return CACHE.getBest(buffer,0,buffer.remaining());
+
+ int l = buffer.remaining();
+ if (l>=4)
+ {
+ HttpMethod m = CACHE.getBest(buffer,0,l);
+ if (m!=null)
+ {
+ int ml = m.asString().length();
+ if (l>ml && buffer.get(buffer.position()+ml)==' ')
+ return m;
+ }
+ }
return null;
}
return toString();
}
+ /* ------------------------------------------------------------ */
/**
* Converts the given String parameter to an HttpMethod
* @param method the String to get the equivalent HttpMethod from
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.http;
+import static org.eclipse.jetty.http.HttpTokens.*;
+
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
/* ------------------------------------------------------------ */
/** A Parser for HTTP 0.9, 1.0 and 1.1
* <p>
- * The is parser parses HTTP client and server messages from buffers
+ * This parser parses HTTP client and server messages from buffers
* passed in the {@link #parseNext(ByteBuffer)} method. The parsed
* elements of the HTTP message are passed as event calls to the
* {@link HttpHandler} instance the parser is constructed with.
CHUNK_SIZE,
CHUNK_PARAMS,
CHUNK,
+ CHUNK_END,
END,
CLOSED
}
}
/* ------------------------------------------------------------------------------- */
- private static class BadMessage extends Error
+ @SuppressWarnings("serial")
+ private static class BadMessageException extends RuntimeException
{
- private static final long serialVersionUID = 1L;
private final int _code;
- private final String _message;
- BadMessage()
+ private BadMessageException()
{
this(400,null);
}
- BadMessage(int code)
+ private BadMessageException(int code)
{
this(code,null);
}
- BadMessage(String message)
+ private BadMessageException(String message)
{
this(400,message);
}
- BadMessage(int code,String message)
+ private BadMessageException(int code,String message)
{
+ super(message);
_code=code;
- _message=message;
}
-
}
/* ------------------------------------------------------------------------------- */
if (_cr)
{
- if (ch!=HttpTokens.LINE_FEED)
- throw new BadMessage("Bad EOL");
+ if (ch!=LINE_FEED)
+ throw new BadMessageException("Bad EOL");
_cr=false;
return ch;
}
- if (ch>=0 && ch<HttpTokens.SPACE)
+ if (ch>=0 && ch<SPACE)
{
- if (ch==HttpTokens.CARRIAGE_RETURN)
+ if (ch==CARRIAGE_RETURN)
{
if (buffer.hasRemaining())
{
if(_maxHeaderBytes>0 && _state.ordinal()<State.END.ordinal())
_headerBytes++;
ch=buffer.get();
- if (ch!=HttpTokens.LINE_FEED)
- throw new BadMessage("Bad EOL");
+ if (ch!=LINE_FEED)
+ throw new BadMessageException("Bad EOL");
}
else
{
}
}
// Only LF or TAB acceptable special characters
- else if (!(ch==HttpTokens.LINE_FEED || ch==HttpTokens.TAB))
- throw new BadMessage("Illegal character");
+ else if (!(ch==LINE_FEED || ch==TAB))
+ throw new IllegalCharacterException(_state,ch,buffer);
}
return ch;
{
int ch=next(buffer);
- if (ch > HttpTokens.SPACE)
+ if (ch > SPACE)
{
_string.setLength(0);
_string.append((char)ch);
else if (ch==0)
break;
else if (ch<0)
- throw new BadMessage();
+ throw new BadMessageException();
// count this white space as a header byte to avoid DOS
if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
{
LOG.warn("padding is too large >"+_maxHeaderBytes);
- throw new BadMessage(HttpStatus.BAD_REQUEST_400);
+ throw new BadMessageException(HttpStatus.BAD_REQUEST_400);
}
}
return false;
if (_state==State.URI)
{
LOG.warn("URI is too large >"+_maxHeaderBytes);
- throw new BadMessage(HttpStatus.REQUEST_URI_TOO_LONG_414);
+ throw new BadMessageException(HttpStatus.REQUEST_URI_TOO_LONG_414);
}
else
{
LOG.warn("request is too large >"+_maxHeaderBytes);
else
LOG.warn("response is too large >"+_maxHeaderBytes);
- throw new BadMessage(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
+ throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
}
}
switch (_state)
{
case METHOD:
- if (ch == HttpTokens.SPACE)
+ if (ch == SPACE)
{
_length=_string.length();
_methodString=takeString();
_methodString=method.asString();
setState(State.SPACE1);
}
- else if (ch < HttpTokens.SPACE)
- throw new BadMessage(ch<0?"Illegal character":"No URI");
+ else if (ch < SPACE)
+ {
+ if (ch==LINE_FEED)
+ throw new BadMessageException("No URI");
+ else
+ throw new IllegalCharacterException(_state,ch,buffer);
+ }
else
_string.append((char)ch);
break;
String version=takeString();
_version=HttpVersion.CACHE.get(version);
if (_version==null)
- throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Unknown Version");
+ throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Unknown Version");
setState(State.SPACE1);
}
else if (ch < HttpTokens.SPACE)
- throw new BadMessage(ch<0?"Illegal character":"No Status");
+ throw new IllegalCharacterException(_state,ch,buffer);
else
_string.append((char)ch);
break;
if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
{
LOG.warn("URI is too large >"+_maxHeaderBytes);
- throw new BadMessage(HttpStatus.REQUEST_URI_TOO_LONG_414);
+ throw new BadMessageException(HttpStatus.REQUEST_URI_TOO_LONG_414);
}
if (_uri.remaining()<=len)
{
}
else if (ch < HttpTokens.SPACE)
{
- throw new BadMessage(HttpStatus.BAD_REQUEST_400,_requestHandler!=null?"No URI":"No Status");
+ throw new BadMessageException(HttpStatus.BAD_REQUEST_400,_requestHandler!=null?"No URI":"No Status");
}
break;
}
else
{
- throw new BadMessage();
+ throw new BadMessageException();
}
break;
BufferUtil.clear(buffer);
handle=_handler.headerComplete()||handle;
handle=_handler.messageComplete()||handle;
+ return handle;
}
else
{
if (_method==HttpMethod.PROXY)
{
if (!(_requestHandler instanceof ProxyHandler))
- throw new BadMessage();
+ throw new BadMessageException();
_uri.flip();
String protocol=BufferUtil.toString(_uri);
BufferUtil.clear(buffer);
handle=_handler.headerComplete()||handle;
handle=_handler.messageComplete()||handle;
+ return handle;
}
}
else if (ch<0)
- throw new BadMessage();
+ throw new BadMessageException();
break;
case REQUEST_VERSION:
_version=HttpVersion.CACHE.get(takeString());
}
if (_version==null)
- throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Unknown Version");
+ throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Unknown Version");
// Should we try to cache header fields?
if (_connectionFields==null && _version.getVersion()>=HttpVersion.HTTP_1_1.getVersion())
else if (ch>=HttpTokens.SPACE)
_string.append((char)ch);
else
- throw new BadMessage();
+ throw new BadMessageException();
break;
_length=_string.length();
}
else
- throw new BadMessage();
+ throw new BadMessageException();
break;
default:
catch(NumberFormatException e)
{
LOG.ignore(e);
- throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Content-Length");
+ throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad Content-Length");
}
if (_contentLength <= 0)
_endOfContent=EndOfContent.NO_CONTENT;
_endOfContent=EndOfContent.CHUNKED_CONTENT;
else if (_valueString.contains(HttpHeaderValue.CHUNKED.toString()))
{
- throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad chunking");
+ throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad chunking");
}
}
break;
int port=0;
if (host==null || host.length()==0)
{
- throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Host header");
+ throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad Host header");
}
int len=host.length();
{
if (DEBUG)
LOG.debug(e);
- throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Host header");
+ throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad Host header");
}
break loop;
}
{
if (host.charAt(len-1)!=']')
{
- throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad IPv6 Host header");
+ throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad IPv6 Host header");
}
- host = host.substring(1,len-1);
+ host = host.substring(0,len);
}
else if (len!=host.length())
host = host.substring(0,len);
if (_maxHeaderBytes>0 && ++_headerBytes>_maxHeaderBytes)
{
LOG.warn("Header is too large >"+_maxHeaderBytes);
- throw new BadMessage(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
+ throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413);
}
switch (_state)
// End of headers!
// Was there a required host header?
- if (!_host && _version!=HttpVersion.HTTP_1_0 && _requestHandler!=null)
+ if (!_host && _version==HttpVersion.HTTP_1_1 && _requestHandler!=null)
{
- throw new BadMessage(HttpStatus.BAD_REQUEST_400,"No Host");
+ throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"No Host");
}
// is it a response that cannot have a body?
case EOF_CONTENT:
setState(State.EOF_CONTENT);
handle=_handler.headerComplete()||handle;
- break;
+ return handle;
case CHUNKED_CONTENT:
setState(State.CHUNKED_CONTENT);
handle=_handler.headerComplete()||handle;
- break;
+ return handle;
case NO_CONTENT:
handle=_handler.headerComplete()||handle;
setState(State.END);
handle=_handler.messageComplete()||handle;
- break;
+ return handle;
default:
setState(State.CONTENT);
handle=_handler.headerComplete()||handle;
- break;
+ return handle;
}
}
else if (ch<=HttpTokens.SPACE)
- throw new BadMessage();
+ throw new BadMessageException();
else
{
if (buffer.hasRemaining())
_length=_string.length();
break;
}
-
- throw new BadMessage("Illegal character");
+
+ throw new IllegalCharacterException(_state,ch,buffer);
case HEADER_VALUE:
if (ch>HttpTokens.SPACE || ch<0)
setState(State.HEADER);
break;
}
-
- throw new BadMessage("Illegal character");
+
+ throw new IllegalCharacterException(_state,ch,buffer);
case HEADER_IN_VALUE:
if (ch>=HttpTokens.SPACE || ch<0 || ch==HttpTokens.TAB)
setState(State.HEADER);
break;
}
- throw new BadMessage("Illegal character");
+
+ throw new IllegalCharacterException(_state,ch,buffer);
default:
throw new IllegalStateException(_state.toString());
if (_responseStatus>0 && _headResponse)
{
setState(State.END);
- if (_handler.messageComplete())
- return true;
+ return _handler.messageComplete();
}
else
{
// Just ignore data when closed
_headerBytes+=buffer.remaining();
BufferUtil.clear(buffer);
- if (_headerBytes>_maxHeaderBytes)
+ if (_maxHeaderBytes>0 && _headerBytes>_maxHeaderBytes)
{
// Don't want to waste time reading data of a closed request
throw new IllegalStateException("too much data after closed");
return false;
}
- catch(BadMessage e)
+ catch(BadMessageException e)
{
BufferUtil.clear(buffer);
- LOG.warn("badMessage: "+e._code+(e._message!=null?" "+e._message:"")+" for "+_handler);
+ LOG.warn("badMessage: "+e._code+(e.getMessage()!=null?" "+e.getMessage():"")+" for "+_handler);
if (DEBUG)
LOG.debug(e);
setState(State.CLOSED);
- _handler.badMessage(e._code, e._message);
+ _handler.badMessage(e._code, e.getMessage());
return false;
}
catch(Exception e)
if (content == 0)
{
setState(State.END);
- if (_handler.messageComplete())
- return true;
+ return _handler.messageComplete();
}
}
if (content == 0)
{
setState(State.END);
- if (_handler.messageComplete())
- return true;
+ return _handler.messageComplete();
}
else
{
if(_contentPosition == _contentLength)
{
setState(State.END);
- if (_handler.messageComplete())
- return true;
+ return _handler.messageComplete();
}
}
break;
if (ch == HttpTokens.LINE_FEED)
{
if (_chunkLength == 0)
- {
- setState(State.END);
- if (_handler.messageComplete())
- return true;
- }
+ setState(State.CHUNK_END);
else
setState(State.CHUNK);
}
if (ch == HttpTokens.LINE_FEED)
{
if (_chunkLength == 0)
- {
- setState(State.END);
- if (_handler.messageComplete())
- return true;
- }
+ setState(State.CHUNK_END);
else
setState(State.CHUNK);
}
break;
}
+ case CHUNK_END:
+ {
+ // TODO handle chunk trailer
+ ch=next(buffer);
+ if (ch==0)
+ break;
+ if (ch == HttpTokens.LINE_FEED)
+ {
+ setState(State.END);
+ return _handler.messageComplete();
+ }
+ throw new IllegalCharacterException(_state,ch,buffer);
+ }
+
case CLOSED:
{
BufferUtil.clear(buffer);
_state=state;
}
+ /* ------------------------------------------------------------------------------- */
+ public Trie<HttpField> getFieldCache()
+ {
+ return _connectionFields;
+ }
+
+ /* ------------------------------------------------------------------------------- */
+ private String getProxyField(ByteBuffer buffer)
+ {
+ _string.setLength(0);
+ _length=0;
+
+ while (buffer.hasRemaining())
+ {
+ // process each character
+ byte ch=next(buffer);
+ if (ch<=' ')
+ return _string.toString();
+ _string.append((char)ch);
+ }
+ throw new BadMessageException();
+ }
+
/* ------------------------------------------------------------------------------- */
@Override
public String toString()
public int getHeaderCacheSize();
}
+ /* ------------------------------------------------------------------------------- */
+ /* ------------------------------------------------------------------------------- */
+ /* ------------------------------------------------------------------------------- */
public interface ProxyHandler
{
void proxied(String protocol, String sAddr, String dAddr, int sPort, int dPort);
}
-
+
+ /* ------------------------------------------------------------------------------- */
+ /* ------------------------------------------------------------------------------- */
+ /* ------------------------------------------------------------------------------- */
public interface RequestHandler<T> extends HttpHandler<T>
{
/**
public abstract boolean parsedHostHeader(String host,int port);
}
+ /* ------------------------------------------------------------------------------- */
+ /* ------------------------------------------------------------------------------- */
+ /* ------------------------------------------------------------------------------- */
public interface ResponseHandler<T> extends HttpHandler<T>
{
/**
public abstract boolean startResponse(HttpVersion version, int status, String reason);
}
- public Trie<HttpField> getFieldCache()
- {
- return _connectionFields;
- }
-
- private String getProxyField(ByteBuffer buffer)
+ /* ------------------------------------------------------------------------------- */
+ @SuppressWarnings("serial")
+ private static class IllegalCharacterException extends BadMessageException
{
- _string.setLength(0);
- _length=0;
-
- while (buffer.hasRemaining())
+ private IllegalCharacterException(State state,byte ch,ByteBuffer buffer)
{
- // process each character
- byte ch=next(buffer);
- if (ch<=' ')
- return _string.toString();
- _string.append((char)ch);
+ super(400,String.format("Illegal character 0x%X",ch));
+ // Bug #460642 - don't reveal buffers to end user
+ LOG.warn(String.format("Illegal character 0x%X in state=%s for buffer %s",ch,state,BufferUtil.toDetailString(buffer)));
}
- throw new BadMessage();
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
*/
public class HttpStatus
{
- public final static int NOT_SET_000 = 0;
public final static int CONTINUE_100 = 100;
public final static int SWITCHING_PROTOCOLS_101 = 101;
public final static int PROCESSING_102 = 102;
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
{
if (_host==_port)
return null;
- if (_raw[_host]=='[')
- return new String(_raw,_host+1,_port-_host-2,_charset);
return new String(_raw,_host,_port-_host,_charset);
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
HTTP_0_9("HTTP/0.9",9),
HTTP_1_0("HTTP/1.0",10),
HTTP_1_1("HTTP/1.1",11),
- HTTP_2_0("HTTP/2.0",20);
+ HTTP_2("HTTP/2.0",20);
/* ------------------------------------------------------------ */
public final static Trie<HttpVersion> CACHE= new ArrayTrie<HttpVersion>();
switch(bytes[position+7])
{
case '0':
- return HTTP_2_0;
+ return HTTP_2;
}
break;
}
case 9: return HttpVersion.HTTP_0_9;
case 10: return HttpVersion.HTTP_1_0;
case 11: return HttpVersion.HTTP_1_1;
+ case 20: return HttpVersion.HTTP_2;
default: throw new IllegalArgumentException();
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.http;
+import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.eclipse.jetty.util.ArrayTernaryTrie;
+import org.eclipse.jetty.util.IncludeExclude;
+import org.eclipse.jetty.util.Predicate;
import org.eclipse.jetty.util.Trie;
import org.eclipse.jetty.util.URIUtil;
this.mapped = mapped;
}
}
+
+ public static class PathSet extends AbstractSet<String> implements Predicate<String>
+ {
+ private final PathMap<Boolean> _map = new PathMap<>();
+
+ @Override
+ public Iterator<String> iterator()
+ {
+ return _map.keySet().iterator();
+ }
+
+ @Override
+ public int size()
+ {
+ return _map.size();
+ }
+
+ @Override
+ public boolean add(String item)
+ {
+ return _map.put(item,Boolean.TRUE)==null;
+ }
+
+ @Override
+ public boolean remove(Object item)
+ {
+ return _map.remove(item)!=null;
+ }
+
+ @Override
+ public boolean contains(Object o)
+ {
+ return _map.containsKey(o);
+ }
+
+ @Override
+ public boolean test(String s)
+ {
+ return _map.containsMatch(s);
+ }
+
+ public boolean containsMatch(String s)
+ {
+ return _map.containsMatch(s);
+ }
+ public boolean matches(String item)
+ {
+ return _map.containsMatch(item);
+ }
+ }
}
bin=application/octet-stream
cab=application/x-cabinet
cdf=application/x-netcdf
+chm=application/vnd.ms-htmlhelp
class=application/java-vm
cpio=application/x-cpio
cpt=application/mac-compactpro
sv4cpio=application/x-sv4cpio
sv4crc=application/x-sv4crc
svg=image/svg+xml
+svgz=image/svg+xml
swf=application/x-shockwave-flash
t=application/x-troff
tar=application/x-tar
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.http.pathmap;
+
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+
+@ManagedObject("Mapped Resource")
+public class MappedResource<E> implements Comparable<MappedResource<E>>
+{
+ private final PathSpec pathSpec;
+ private final E resource;
+
+ public MappedResource(PathSpec pathSpec, E resource)
+ {
+ this.pathSpec = pathSpec;
+ this.resource = resource;
+ }
+
+ /**
+ * Comparison is based solely on the pathSpec
+ */
+ @Override
+ public int compareTo(MappedResource<E> other)
+ {
+ return this.pathSpec.compareTo(other.pathSpec);
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ MappedResource<?> other = (MappedResource<?>)obj;
+ if (pathSpec == null)
+ {
+ if (other.pathSpec != null)
+ {
+ return false;
+ }
+ }
+ else if (!pathSpec.equals(other.pathSpec))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ @ManagedAttribute(value = "path spec", readonly = true)
+ public PathSpec getPathSpec()
+ {
+ return pathSpec;
+ }
+
+ @ManagedAttribute(value = "resource", readonly = true)
+ public E getResource()
+ {
+ return resource;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = (prime * result) + ((pathSpec == null) ? 0 : pathSpec.hashCode());
+ return result;
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("MappedResource[pathSpec=%s,resource=%s]",pathSpec,resource);
+ }
+}
\ No newline at end of file
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.http.pathmap;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
+import org.eclipse.jetty.util.annotation.ManagedObject;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.component.Dumpable;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * Path Mappings of PathSpec to Resource.
+ * <p>
+ * Sorted into search order upon entry into the Set
+ *
+ * @param <E> the type of mapping endpoint
+ */
+@ManagedObject("Path Mappings")
+public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
+{
+ private static final Logger LOG = Log.getLogger(PathMappings.class);
+ private List<MappedResource<E>> mappings = new ArrayList<MappedResource<E>>();
+ private MappedResource<E> defaultResource = null;
+ private MappedResource<E> rootResource = null;
+
+ @Override
+ public String dump()
+ {
+ return ContainerLifeCycle.dump(this);
+ }
+
+ @Override
+ public void dump(Appendable out, String indent) throws IOException
+ {
+ ContainerLifeCycle.dump(out,indent,mappings);
+ }
+
+ @ManagedAttribute(value = "mappings", readonly = true)
+ public List<MappedResource<E>> getMappings()
+ {
+ return mappings;
+ }
+
+ public void reset()
+ {
+ mappings.clear();
+ }
+
+ /**
+ * Return a list of MappedResource matches for the specified path.
+ *
+ * @param path the path to return matches on
+ * @return the list of mapped resource the path matches on
+ */
+ public List<MappedResource<E>> getMatches(String path)
+ {
+ boolean matchRoot = "/".equals(path);
+
+ List<MappedResource<E>> ret = new ArrayList<>();
+ int len = mappings.size();
+ for (int i = 0; i < len; i++)
+ {
+ MappedResource<E> mr = mappings.get(i);
+
+ switch (mr.getPathSpec().group)
+ {
+ case ROOT:
+ if (matchRoot)
+ ret.add(mr);
+ break;
+ case DEFAULT:
+ if (matchRoot || mr.getPathSpec().matches(path))
+ ret.add(mr);
+ break;
+ default:
+ if (mr.getPathSpec().matches(path))
+ ret.add(mr);
+ break;
+ }
+ }
+ return ret;
+ }
+
+ public MappedResource<E> getMatch(String path)
+ {
+ if (path.equals("/") && rootResource != null)
+ {
+ return rootResource;
+ }
+
+ int len = mappings.size();
+ for (int i = 0; i < len; i++)
+ {
+ MappedResource<E> mr = mappings.get(i);
+ if (mr.getPathSpec().matches(path))
+ {
+ return mr;
+ }
+ }
+ return defaultResource;
+ }
+
+ @Override
+ public Iterator<MappedResource<E>> iterator()
+ {
+ return mappings.iterator();
+ }
+
+ @SuppressWarnings("incomplete-switch")
+ public void put(PathSpec pathSpec, E resource)
+ {
+ MappedResource<E> entry = new MappedResource<>(pathSpec,resource);
+ switch (pathSpec.group)
+ {
+ case DEFAULT:
+ defaultResource = entry;
+ break;
+ case ROOT:
+ rootResource = entry;
+ break;
+ }
+
+ // TODO: add warning when replacing an existing pathspec?
+
+ mappings.add(entry);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Added {} to {}",entry,this);
+ Collections.sort(mappings);
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s[size=%d]",this.getClass().getSimpleName(),mappings.size());
+ }
+}
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.http.pathmap;
+
+/**
+ * The base PathSpec, what all other path specs are based on
+ */
+public abstract class PathSpec implements Comparable<PathSpec>
+{
+ protected String pathSpec;
+ protected PathSpecGroup group;
+ protected int pathDepth;
+ protected int specLength;
+
+ @Override
+ public int compareTo(PathSpec other)
+ {
+ // Grouping (increasing)
+ int diff = this.group.ordinal() - other.group.ordinal();
+ if (diff != 0)
+ {
+ return diff;
+ }
+
+ // Spec Length (decreasing)
+ diff = other.specLength - this.specLength;
+ if (diff != 0)
+ {
+ return diff;
+ }
+
+ // Path Spec Name (alphabetical)
+ return this.pathSpec.compareTo(other.pathSpec);
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ PathSpec other = (PathSpec)obj;
+ if (pathSpec == null)
+ {
+ if (other.pathSpec != null)
+ {
+ return false;
+ }
+ }
+ else if (!pathSpec.equals(other.pathSpec))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public PathSpecGroup getGroup()
+ {
+ return group;
+ }
+
+ /**
+ * Get the number of path elements that this path spec declares.
+ * <p>
+ * This is used to determine longest match logic.
+ *
+ * @return the depth of the path segments that this spec declares
+ */
+ public int getPathDepth()
+ {
+ return pathDepth;
+ }
+
+ /**
+ * Return the portion of the path that is after the path spec.
+ *
+ * @param path
+ * the path to match against
+ * @return the path info portion of the string
+ */
+ public abstract String getPathInfo(String path);
+
+ /**
+ * Return the portion of the path that matches a path spec.
+ *
+ * @param path
+ * the path to match against
+ * @return the match, or null if no match at all
+ */
+ public abstract String getPathMatch(String path);
+
+ /**
+ * The as-provided path spec.
+ *
+ * @return the as-provided path spec
+ */
+ public String getDeclaration()
+ {
+ return pathSpec;
+ }
+
+ /**
+ * Get the relative path.
+ *
+ * @param base
+ * the base the path is relative to
+ * @param path
+ * the additional path
+ * @return the base plus path with pathSpec portion removed
+ */
+ public abstract String getRelativePath(String base, String path);
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = (prime * result) + ((pathSpec == null)?0:pathSpec.hashCode());
+ return result;
+ }
+
+ /**
+ * Test to see if the provided path matches this path spec
+ *
+ * @param path
+ * the path to test
+ * @return true if the path matches this path spec, false otherwise
+ */
+ public abstract boolean matches(String path);
+
+ @Override
+ public String toString()
+ {
+ StringBuilder str = new StringBuilder();
+ str.append(this.getClass().getSimpleName()).append("[\"");
+ str.append(pathSpec);
+ str.append("\",pathDepth=").append(pathDepth);
+ str.append(",group=").append(group);
+ str.append("]");
+ return str.toString();
+ }
+}
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.http.pathmap;
+
+/**
+ * Types of path spec groups.
+ * <p>
+ * This is used to facilitate proper pathspec search order.
+ * <p>
+ * Search Order:
+ * <ol>
+ * <li>{@link PathSpecGroup#ordinal()} [increasing]</li>
+ * <li>{@link PathSpec#specLength} [decreasing]</li>
+ * <li>{@link PathSpec#pathSpec} [natural sort order]</li>
+ * </ol>
+ */
+public enum PathSpecGroup
+{
+ // NOTE: Order of enums determines order of Groups.
+
+ /**
+ * For exactly defined path specs, no glob.
+ */
+ EXACT,
+ /**
+ * For path specs that have a hardcoded prefix and suffix with wildcard glob in the middle.
+ *
+ * <pre>
+ * "^/downloads/[^/]*.zip$" - regex spec
+ * "/a/{var}/c" - uri-template spec
+ * </pre>
+ *
+ * Note: there is no known servlet spec variant of this kind of path spec
+ */
+ MIDDLE_GLOB,
+ /**
+ * For path specs that have a hardcoded prefix and a trailing wildcard glob.
+ * <p>
+ *
+ * <pre>
+ * "/downloads/*" - servlet spec
+ * "/api/*" - servlet spec
+ * "^/rest/.*$" - regex spec
+ * "/bookings/{guest-id}" - uri-template spec
+ * "/rewards/{vip-level}" - uri-template spec
+ * </pre>
+ */
+ PREFIX_GLOB,
+ /**
+ * For path specs that have a wildcard glob with a hardcoded suffix
+ *
+ * <pre>
+ * "*.do" - servlet spec
+ * "*.css" - servlet spec
+ * "^.*\.zip$" - regex spec
+ * </pre>
+ *
+ * Note: there is no known uri-template spec variant of this kind of path spec
+ */
+ SUFFIX_GLOB,
+ /**
+ * The root spec for accessing the Root behavior.
+ *
+ * <pre>
+ * "" - servlet spec (Root Servlet)
+ * null - servlet spec (Root Servlet)
+ * </pre>
+ *
+ * Note: there is no known uri-template spec variant of this kind of path spec
+ */
+ ROOT,
+ /**
+ * The default spec for accessing the Default path behavior.
+ *
+ * <pre>
+ * "/" - servlet spec (Default Servlet)
+ * "/" - uri-template spec (Root Context)
+ * "^/$" - regex spec (Root Context)
+ * </pre>
+ *
+ * Per Servlet Spec, pathInfo is always null for these specs.
+ * If nothing above matches, then default will match.
+ */
+ DEFAULT,
+}
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.http.pathmap;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.eclipse.jetty.util.Predicate;
+
+/**
+ * A Set of PathSpec strings.
+ * <p>
+ * Used by {@link org.eclipse.jetty.util.IncludeExclude} logic
+ */
+public class PathSpecSet implements Set<String>, Predicate<String>
+{
+ private final Set<PathSpec> specs = new TreeSet<>();
+
+ @Override
+ public boolean test(String s)
+ {
+ for (PathSpec spec : specs)
+ {
+ if (spec.matches(s))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ return specs.isEmpty();
+ }
+
+ @Override
+ public Iterator<String> iterator()
+ {
+ return new Iterator<String>()
+ {
+ private Iterator<PathSpec> iter = specs.iterator();
+
+ @Override
+ public boolean hasNext()
+ {
+ return iter.hasNext();
+ }
+
+ @Override
+ public String next()
+ {
+ PathSpec spec = iter.next();
+ if (spec == null)
+ {
+ return null;
+ }
+ return spec.getDeclaration();
+ }
+
+ @Override
+ public void remove()
+ {
+ throw new UnsupportedOperationException("Remove not supported by this Iterator");
+ }
+ };
+ }
+
+ @Override
+ public int size()
+ {
+ return specs.size();
+ }
+
+ @Override
+ public boolean contains(Object o)
+ {
+ if (o instanceof PathSpec)
+ {
+ return specs.contains(o);
+ }
+ if (o instanceof String)
+ {
+ return specs.contains(toPathSpec((String)o));
+ }
+ return false;
+ }
+
+ private PathSpec asPathSpec(Object o)
+ {
+ if (o == null)
+ {
+ return null;
+ }
+ if (o instanceof PathSpec)
+ {
+ return (PathSpec)o;
+ }
+ if (o instanceof String)
+ {
+ return toPathSpec((String)o);
+ }
+ return toPathSpec(o.toString());
+ }
+
+ private PathSpec toPathSpec(String rawSpec)
+ {
+ if ((rawSpec == null) || (rawSpec.length() < 1))
+ {
+ throw new RuntimeException("Path Spec String must start with '^', '/', or '*.': got [" + rawSpec + "]");
+ }
+ if (rawSpec.charAt(0) == '^')
+ {
+ return new RegexPathSpec(rawSpec);
+ }
+ else
+ {
+ return new ServletPathSpec(rawSpec);
+ }
+ }
+
+ @Override
+ public Object[] toArray()
+ {
+ return toArray(new String[specs.size()]);
+ }
+
+ @Override
+ public <T> T[] toArray(T[] a)
+ {
+ int i = 0;
+ for (PathSpec spec : specs)
+ {
+ a[i++] = (T)spec.getDeclaration();
+ }
+ return a;
+ }
+
+ @Override
+ public boolean add(String e)
+ {
+ return specs.add(toPathSpec(e));
+ }
+
+ @Override
+ public boolean remove(Object o)
+ {
+ return specs.remove(asPathSpec(o));
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> coll)
+ {
+ for (Object o : coll)
+ {
+ if (!specs.contains(asPathSpec(o)))
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends String> coll)
+ {
+ boolean ret = false;
+
+ for (String s : coll)
+ {
+ ret |= add(s);
+ }
+
+ return ret;
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> coll)
+ {
+ List<PathSpec> collSpecs = new ArrayList<>();
+ for (Object o : coll)
+ {
+ collSpecs.add(asPathSpec(o));
+ }
+ return specs.retainAll(collSpecs);
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> coll)
+ {
+ List<PathSpec> collSpecs = new ArrayList<>();
+ for (Object o : coll)
+ {
+ collSpecs.add(asPathSpec(o));
+ }
+ return specs.removeAll(collSpecs);
+ }
+
+ @Override
+ public void clear()
+ {
+ specs.clear();
+ }
+}
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.http.pathmap;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class RegexPathSpec extends PathSpec
+{
+ protected Pattern pattern;
+
+ protected RegexPathSpec()
+ {
+ super();
+ }
+
+ public RegexPathSpec(String regex)
+ {
+ super.pathSpec = regex;
+ boolean inGrouping = false;
+ this.pathDepth = 0;
+ this.specLength = pathSpec.length();
+ // build up a simple signature we can use to identify the grouping
+ StringBuilder signature = new StringBuilder();
+ for (char c : pathSpec.toCharArray())
+ {
+ switch (c)
+ {
+ case '[':
+ inGrouping = true;
+ break;
+ case ']':
+ inGrouping = false;
+ signature.append('g'); // glob
+ break;
+ case '*':
+ signature.append('g'); // glob
+ break;
+ case '/':
+ if (!inGrouping)
+ {
+ this.pathDepth++;
+ }
+ break;
+ default:
+ if (!inGrouping)
+ {
+ if (Character.isLetterOrDigit(c))
+ {
+ signature.append('l'); // literal (exact)
+ }
+ }
+ break;
+ }
+ }
+ this.pattern = Pattern.compile(pathSpec);
+
+ // Figure out the grouping based on the signature
+ String sig = signature.toString();
+
+ if (Pattern.matches("^l*$",sig))
+ {
+ this.group = PathSpecGroup.EXACT;
+ }
+ else if (Pattern.matches("^l*g+",sig))
+ {
+ this.group = PathSpecGroup.PREFIX_GLOB;
+ }
+ else if (Pattern.matches("^g+l+$",sig))
+ {
+ this.group = PathSpecGroup.SUFFIX_GLOB;
+ }
+ else
+ {
+ this.group = PathSpecGroup.MIDDLE_GLOB;
+ }
+ }
+
+ public Matcher getMatcher(String path)
+ {
+ return this.pattern.matcher(path);
+ }
+
+ @Override
+ public String getPathInfo(String path)
+ {
+ // Path Info only valid for PREFIX_GLOB types
+ if (group == PathSpecGroup.PREFIX_GLOB)
+ {
+ Matcher matcher = getMatcher(path);
+ if (matcher.matches())
+ {
+ if (matcher.groupCount() >= 1)
+ {
+ String pathInfo = matcher.group(1);
+ if ("".equals(pathInfo))
+ {
+ return "/";
+ }
+ else
+ {
+ return pathInfo;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String getPathMatch(String path)
+ {
+ Matcher matcher = getMatcher(path);
+ if (matcher.matches())
+ {
+ if (matcher.groupCount() >= 1)
+ {
+ int idx = matcher.start(1);
+ if (idx > 0)
+ {
+ if (path.charAt(idx - 1) == '/')
+ {
+ idx--;
+ }
+ return path.substring(0,idx);
+ }
+ }
+ return path;
+ }
+ return null;
+ }
+
+ public Pattern getPattern()
+ {
+ return this.pattern;
+ }
+
+ @Override
+ public String getRelativePath(String base, String path)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public boolean matches(final String path)
+ {
+ int idx = path.indexOf('?');
+ if (idx >= 0)
+ {
+ // match only non-query part
+ return getMatcher(path.substring(0,idx)).matches();
+ }
+ else
+ {
+ // match entire path
+ return getMatcher(path).matches();
+ }
+ }
+}
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.http.pathmap;
+
+import org.eclipse.jetty.util.URIUtil;
+
+public class ServletPathSpec extends PathSpec
+{
+ public ServletPathSpec(String servletPathSpec)
+ {
+ super();
+ assertValidServletPathSpec(servletPathSpec);
+
+ // The Root Path Spec
+ if ((servletPathSpec == null) || (servletPathSpec.length() == 0))
+ {
+ super.pathSpec = "";
+ super.pathDepth = -1; // force this to be at the end of the sort order
+ this.specLength = 1;
+ this.group = PathSpecGroup.ROOT;
+ return;
+ }
+
+ // The Default Path Spec
+ if("/".equals(servletPathSpec))
+ {
+ super.pathSpec = "/";
+ super.pathDepth = -1; // force this to be at the end of the sort order
+ this.specLength = 1;
+ this.group = PathSpecGroup.DEFAULT;
+ return;
+ }
+
+ this.specLength = servletPathSpec.length();
+ super.pathDepth = 0;
+ char lastChar = servletPathSpec.charAt(specLength - 1);
+ // prefix based
+ if ((servletPathSpec.charAt(0) == '/') && (specLength > 1) && (lastChar == '*'))
+ {
+ this.group = PathSpecGroup.PREFIX_GLOB;
+ }
+ // suffix based
+ else if (servletPathSpec.charAt(0) == '*')
+ {
+ this.group = PathSpecGroup.SUFFIX_GLOB;
+ }
+ else
+ {
+ this.group = PathSpecGroup.EXACT;
+ }
+
+ for (int i = 0; i < specLength; i++)
+ {
+ int cp = servletPathSpec.codePointAt(i);
+ if (cp < 128)
+ {
+ char c = (char)cp;
+ switch (c)
+ {
+ case '/':
+ super.pathDepth++;
+ break;
+ }
+ }
+ }
+
+ super.pathSpec = servletPathSpec;
+ }
+
+ private void assertValidServletPathSpec(String servletPathSpec)
+ {
+ if ((servletPathSpec == null) || servletPathSpec.equals(""))
+ {
+ return; // empty path spec
+ }
+
+ int len = servletPathSpec.length();
+ // path spec must either start with '/' or '*.'
+ if (servletPathSpec.charAt(0) == '/')
+ {
+ // Prefix Based
+ if (len == 1)
+ {
+ return; // simple '/' path spec
+ }
+ int idx = servletPathSpec.indexOf('*');
+ if (idx < 0)
+ {
+ return; // no hit on glob '*'
+ }
+ // only allowed to have '*' at the end of the path spec
+ if (idx != (len - 1))
+ {
+ throw new IllegalArgumentException("Servlet Spec 12.2 violation: glob '*' can only exist at end of prefix based matches: bad spec \""+ servletPathSpec +"\"");
+ }
+ }
+ else if (servletPathSpec.startsWith("*."))
+ {
+ // Suffix Based
+ int idx = servletPathSpec.indexOf('/');
+ // cannot have path separator
+ if (idx >= 0)
+ {
+ throw new IllegalArgumentException("Servlet Spec 12.2 violation: suffix based path spec cannot have path separators: bad spec \""+ servletPathSpec +"\"");
+ }
+
+ idx = servletPathSpec.indexOf('*',2);
+ // only allowed to have 1 glob '*', at the start of the path spec
+ if (idx >= 1)
+ {
+ throw new IllegalArgumentException("Servlet Spec 12.2 violation: suffix based path spec cannot have multiple glob '*': bad spec \""+ servletPathSpec +"\"");
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("Servlet Spec 12.2 violation: path spec must start with \"/\" or \"*.\": bad spec \""+ servletPathSpec +"\"");
+ }
+ }
+
+ @Override
+ public String getPathInfo(String path)
+ {
+ // Path Info only valid for PREFIX_GLOB types
+ if (group == PathSpecGroup.PREFIX_GLOB)
+ {
+ if (path.length() == (specLength - 2))
+ {
+ return null;
+ }
+ return path.substring(specLength - 2);
+ }
+
+ return null;
+ }
+
+ @Override
+ public String getPathMatch(String path)
+ {
+ switch (group)
+ {
+ case EXACT:
+ if (pathSpec.equals(path))
+ {
+ return path;
+ }
+ else
+ {
+ return null;
+ }
+ case PREFIX_GLOB:
+ if (isWildcardMatch(path))
+ {
+ return path.substring(0,specLength - 2);
+ }
+ else
+ {
+ return null;
+ }
+ case SUFFIX_GLOB:
+ if (path.regionMatches(path.length() - (specLength - 1),pathSpec,1,specLength - 1))
+ {
+ return path;
+ }
+ else
+ {
+ return null;
+ }
+ case DEFAULT:
+ return path;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public String getRelativePath(String base, String path)
+ {
+ String info = getPathInfo(path);
+ if (info == null)
+ {
+ info = path;
+ }
+
+ if (info.startsWith("./"))
+ {
+ info = info.substring(2);
+ }
+ if (base.endsWith(URIUtil.SLASH))
+ {
+ if (info.startsWith(URIUtil.SLASH))
+ {
+ path = base + info.substring(1);
+ }
+ else
+ {
+ path = base + info;
+ }
+ }
+ else if (info.startsWith(URIUtil.SLASH))
+ {
+ path = base + info;
+ }
+ else
+ {
+ path = base + URIUtil.SLASH + info;
+ }
+ return path;
+ }
+
+ private boolean isWildcardMatch(String path)
+ {
+ // For a spec of "/foo/*" match "/foo" , "/foo/..." but not "/foobar"
+ int cpl = specLength - 2;
+ if ((group == PathSpecGroup.PREFIX_GLOB) && (path.regionMatches(0,pathSpec,0,cpl)))
+ {
+ if ((path.length() == cpl) || ('/' == path.charAt(cpl)))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean matches(String path)
+ {
+ switch (group)
+ {
+ case EXACT:
+ return pathSpec.equals(path);
+ case PREFIX_GLOB:
+ return isWildcardMatch(path);
+ case SUFFIX_GLOB:
+ return path.regionMatches((path.length() - specLength) + 1,pathSpec,1,specLength - 1);
+ case ROOT:
+ // Only "/" matches
+ return ("/".equals(path));
+ case DEFAULT:
+ // If we reached this point, then everything matches
+ return true;
+ default:
+ return false;
+ }
+ }
+}
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.http.pathmap;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * PathSpec for URI Template based declarations
+ *
+ * @see <a href="https://tools.ietf.org/html/rfc6570">URI Templates (Level 1)</a>
+ */
+public class UriTemplatePathSpec extends RegexPathSpec
+{
+ private static final Logger LOG = Log.getLogger(UriTemplatePathSpec.class);
+
+ private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{(.*)\\}");
+ /** Reserved Symbols in URI Template variable */
+ private static final String VARIABLE_RESERVED = ":/?#[]@" + // gen-delims
+ "!$&'()*+,;="; // sub-delims
+ /** Allowed Symbols in a URI Template variable */
+ private static final String VARIABLE_SYMBOLS="-._";
+ private static final Set<String> FORBIDDEN_SEGMENTS;
+
+ static
+ {
+ FORBIDDEN_SEGMENTS = new HashSet<>();
+ FORBIDDEN_SEGMENTS.add("/./");
+ FORBIDDEN_SEGMENTS.add("/../");
+ FORBIDDEN_SEGMENTS.add("//");
+ }
+
+ private String variables[];
+
+ public UriTemplatePathSpec(String rawSpec)
+ {
+ super();
+ Objects.requireNonNull(rawSpec,"Path Param Spec cannot be null");
+
+ if ("".equals(rawSpec) || "/".equals(rawSpec))
+ {
+ super.pathSpec = "/";
+ super.pattern = Pattern.compile("^/$");
+ super.pathDepth = 1;
+ this.specLength = 1;
+ this.variables = new String[0];
+ this.group = PathSpecGroup.EXACT;
+ return;
+ }
+
+ if (rawSpec.charAt(0) != '/')
+ {
+ // path specs must start with '/'
+ StringBuilder err = new StringBuilder();
+ err.append("Syntax Error: path spec \"");
+ err.append(rawSpec);
+ err.append("\" must start with '/'");
+ throw new IllegalArgumentException(err.toString());
+ }
+
+ for (String forbidden : FORBIDDEN_SEGMENTS)
+ {
+ if (rawSpec.contains(forbidden))
+ {
+ StringBuilder err = new StringBuilder();
+ err.append("Syntax Error: segment ");
+ err.append(forbidden);
+ err.append(" is forbidden in path spec: ");
+ err.append(rawSpec);
+ throw new IllegalArgumentException(err.toString());
+ }
+ }
+
+ this.pathSpec = rawSpec;
+
+ StringBuilder regex = new StringBuilder();
+ regex.append('^');
+
+ List<String> varNames = new ArrayList<>();
+ // split up into path segments (ignoring the first slash that will always be empty)
+ String segments[] = rawSpec.substring(1).split("/");
+ char segmentSignature[] = new char[segments.length];
+ this.pathDepth = segments.length;
+ for (int i = 0; i < segments.length; i++)
+ {
+ String segment = segments[i];
+ Matcher mat = VARIABLE_PATTERN.matcher(segment);
+
+ if (mat.matches())
+ {
+ // entire path segment is a variable.
+ String variable = mat.group(1);
+ if (varNames.contains(variable))
+ {
+ // duplicate variable names
+ StringBuilder err = new StringBuilder();
+ err.append("Syntax Error: variable ");
+ err.append(variable);
+ err.append(" is duplicated in path spec: ");
+ err.append(rawSpec);
+ throw new IllegalArgumentException(err.toString());
+ }
+
+ assertIsValidVariableLiteral(variable);
+
+ segmentSignature[i] = 'v'; // variable
+ // valid variable name
+ varNames.add(variable);
+ // build regex
+ regex.append("/([^/]+)");
+ }
+ else if (mat.find(0))
+ {
+ // variable exists as partial segment
+ StringBuilder err = new StringBuilder();
+ err.append("Syntax Error: variable ");
+ err.append(mat.group());
+ err.append(" must exist as entire path segment: ");
+ err.append(rawSpec);
+ throw new IllegalArgumentException(err.toString());
+ }
+ else if ((segment.indexOf('{') >= 0) || (segment.indexOf('}') >= 0))
+ {
+ // variable is split with a path separator
+ StringBuilder err = new StringBuilder();
+ err.append("Syntax Error: invalid path segment /");
+ err.append(segment);
+ err.append("/ variable declaration incomplete: ");
+ err.append(rawSpec);
+ throw new IllegalArgumentException(err.toString());
+ }
+ else if (segment.indexOf('*') >= 0)
+ {
+ // glob segment
+ StringBuilder err = new StringBuilder();
+ err.append("Syntax Error: path segment /");
+ err.append(segment);
+ err.append("/ contains a wildcard symbol (not supported by this uri-template implementation): ");
+ err.append(rawSpec);
+ throw new IllegalArgumentException(err.toString());
+ }
+ else
+ {
+ // valid path segment
+ segmentSignature[i] = 'e'; // exact
+ // build regex
+ regex.append('/');
+ // escape regex special characters
+ for (char c : segment.toCharArray())
+ {
+ if ((c == '.') || (c == '[') || (c == ']') || (c == '\\'))
+ {
+ regex.append('\\');
+ }
+ regex.append(c);
+ }
+ }
+ }
+
+ // Handle trailing slash (which is not picked up during split)
+ if(rawSpec.charAt(rawSpec.length()-1) == '/')
+ {
+ regex.append('/');
+ }
+
+ regex.append('$');
+
+ this.pattern = Pattern.compile(regex.toString());
+
+ int varcount = varNames.size();
+ this.variables = varNames.toArray(new String[varcount]);
+
+ // Convert signature to group
+ String sig = String.valueOf(segmentSignature);
+
+ if (Pattern.matches("^e*$",sig))
+ {
+ this.group = PathSpecGroup.EXACT;
+ }
+ else if (Pattern.matches("^e*v+",sig))
+ {
+ this.group = PathSpecGroup.PREFIX_GLOB;
+ }
+ else if (Pattern.matches("^v+e+",sig))
+ {
+ this.group = PathSpecGroup.SUFFIX_GLOB;
+ }
+ else
+ {
+ this.group = PathSpecGroup.MIDDLE_GLOB;
+ }
+ }
+
+ /**
+ * Validate variable literal name, per RFC6570, Section 2.1 Literals
+ * @param variable
+ * @param pathParamSpec
+ */
+ private void assertIsValidVariableLiteral(String variable)
+ {
+ int len = variable.length();
+
+ int i = 0;
+ int codepoint;
+ boolean valid = (len > 0); // must not be zero length
+
+ while (valid && i < len)
+ {
+ codepoint = variable.codePointAt(i);
+ i += Character.charCount(codepoint);
+
+ // basic letters, digits, or symbols
+ if (isValidBasicLiteralCodepoint(codepoint))
+ {
+ continue;
+ }
+
+ // The ucschar and iprivate pieces
+ if (Character.isSupplementaryCodePoint(codepoint))
+ {
+ continue;
+ }
+
+ // pct-encoded
+ if (codepoint == '%')
+ {
+ if (i + 2 > len)
+ {
+ // invalid percent encoding, missing extra 2 chars
+ valid = false;
+ continue;
+ }
+ codepoint = TypeUtil.convertHexDigit(variable.codePointAt(i++)) << 4;
+ codepoint |= TypeUtil.convertHexDigit(variable.codePointAt(i++));
+
+ // validate basic literal
+ if (isValidBasicLiteralCodepoint(codepoint))
+ {
+ continue;
+ }
+ }
+
+ valid = false;
+ }
+
+ if (!valid)
+ {
+ // invalid variable name
+ StringBuilder err = new StringBuilder();
+ err.append("Syntax Error: variable {");
+ err.append(variable);
+ err.append("} an invalid variable name: ");
+ err.append(pathSpec);
+ throw new IllegalArgumentException(err.toString());
+ }
+ }
+
+ private boolean isValidBasicLiteralCodepoint(int codepoint)
+ {
+ // basic letters or digits
+ if((codepoint >= 'a' && codepoint <= 'z') ||
+ (codepoint >= 'A' && codepoint <= 'Z') ||
+ (codepoint >= '0' && codepoint <= '9'))
+ {
+ return true;
+ }
+
+ // basic allowed symbols
+ if(VARIABLE_SYMBOLS.indexOf(codepoint) >= 0)
+ {
+ return true; // valid simple value
+ }
+
+ // basic reserved symbols
+ if(VARIABLE_RESERVED.indexOf(codepoint) >= 0)
+ {
+ LOG.warn("Detected URI Template reserved symbol [{}] in path spec \"{}\"",(char)codepoint,pathSpec);
+ return false; // valid simple value
+ }
+
+ return false;
+ }
+
+ public Map<String, String> getPathParams(String path)
+ {
+ Matcher matcher = getMatcher(path);
+ if (matcher.matches())
+ {
+ if (group == PathSpecGroup.EXACT)
+ {
+ return Collections.emptyMap();
+ }
+ Map<String, String> ret = new HashMap<>();
+ int groupCount = matcher.groupCount();
+ for (int i = 1; i <= groupCount; i++)
+ {
+ ret.put(this.variables[i - 1],matcher.group(i));
+ }
+ return ret;
+ }
+ return null;
+ }
+
+ public int getVariableCount()
+ {
+ return variables.length;
+ }
+
+ public String[] getVariables()
+ {
+ return this.variables;
+ }
+}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
public abstract class AbstractConnection implements Connection
{
private static final Logger LOG = Log.getLogger(AbstractConnection.class);
-
+
public static final boolean EXECUTE_ONFILLABLE=true;
private final List<Listener> listeners = new CopyOnWriteArrayList<>();
{
this(endp,executor,EXECUTE_ONFILLABLE);
}
-
+
protected AbstractConnection(EndPoint endp, Executor executor, final boolean executeOnfillable)
{
if (executor == null)
{
return _executor;
}
-
+
protected void failedCallback(final Callback callback, final Throwable x)
{
if (NonBlockingThread.isNonBlockingThread())
callback.failed(x);
}
}
-
+
/**
* <p>Utility method to be called to register read interest.</p>
* <p>After a call to this method, {@link #onFillable()} or {@link #onFillInterestedFailed(Throwable)}
*/
public void fillInterested()
{
- LOG.debug("fillInterested {}",this);
-
+ if (LOG.isDebugEnabled())
+ LOG.debug("fillInterested {}",this);
+
while(true)
{
State state=_state.get();
break;
}
}
-
+
public void fillInterested(Callback callback)
{
- LOG.debug("fillInterested {}",this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("fillInterested {}",this);
while(true)
{
break;
}
}
-
+
/**
* <p>Callback method invoked when the endpoint is ready to be read.</p>
* @see #fillInterested()
*/
protected void onFillInterestedFailed(Throwable cause)
{
- LOG.debug("{} onFillInterestedFailed {}", this, cause);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} onFillInterestedFailed {}", this, cause);
if (_endPoint.isOpen())
{
boolean close = true;
}
if (_endPoint.isOpen())
- fillInterested();
+ fillInterested();
}
/**
@Override
public void onOpen()
{
- LOG.debug("onOpen {}", this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("onOpen {}", this);
for (Listener listener : listeners)
listener.onOpened(this);
@Override
public void onClose()
{
- LOG.debug("onClose {}",this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("onClose {}",this);
for (Listener listener : listeners)
listener.onClosed(this);
@Override
public String toString()
{
- return String.format("%s@%x{%s}", getClass().getSimpleName(), hashCode(), _state.get());
+ return String.format("%s@%x[%s,%s]",
+ getClass().getSimpleName(),
+ hashCode(),
+ _state.get(),
+ _endPoint);
}
-
+
public boolean next(State state, State next)
{
if (next==null)
return true;
if(_state.compareAndSet(state,next))
{
- LOG.debug("{}-->{} {}",state,next,this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{}-->{} {}",state,next,this);
if (next!=state)
next.onEnter(AbstractConnection.this);
return true;
}
return false;
}
-
+
private static final class IdleState extends State
{
private IdleState()
{
return _name;
}
-
+
void onEnter(AbstractConnection connection)
{
}
-
+
State fillInterested()
{
throw new IllegalStateException(this.toString());
{
throw new IllegalStateException(this.toString());
}
-
+
State onFailed()
{
throw new IllegalStateException(this.toString());
}
}
-
+
public static final State IDLE=new IdleState();
-
+
public static final State FILL_INTERESTED=new FillInterestedState();
-
+
public static final State FILLING=new FillingState();
-
+
public static final State REFILLING=new RefillingState();
public static final State FILLING_FILL_INTERESTED=new FillingFillInterestedState("FILLING_FILL_INTERESTED");
-
+
public class NestedState extends State
{
private final State _nested;
-
+
NestedState(State nested)
{
super("NESTED("+nested+")");
{
return new NestedState(_nested.onFillable());
}
-
+
@Override
State onFilled()
{
return new NestedState(_nested.onFilled());
}
}
-
-
+
+
public class FillingInterestedCallback extends NestedState
{
private final Callback _callback;
-
+
FillingInterestedCallback(Callback callback,State nested)
{
super("FILLING_INTERESTED_CALLBACK",nested==FILLING?REFILLING:nested);
break;
}
_callback.failed(x);
- }
+ }
};
-
+
connection.getEndPoint().fillInterested(callback);
}
}
-
+
private final Runnable _runOnFillable = new Runnable()
{
@Override
}
}
};
-
-
+
+
private class ReadCallback implements Callback
- {
+ {
@Override
public void succeeded()
{
}
});
}
-
+
@Override
public String toString()
{
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.nio.ByteBuffer;
import java.util.concurrent.TimeoutException;
+import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
return AbstractEndPoint.this.needsFill();
}
};
-
+
private final WriteFlusher _writeFlusher = new WriteFlusher(this)
{
@Override
{
return _remote;
}
-
+
@Override
public Connection getConnection()
{
@Override
public void onOpen()
{
- LOG.debug("onOpen {}",this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("onOpen {}",this);
super.onOpen();
}
public void onClose()
{
super.onClose();
- LOG.debug("onClose {}",this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("onClose {}",this);
_writeFlusher.onClose();
_fillInterest.onClose();
}
-
+
@Override
public void close()
{
boolean input_shutdown=isInputShutdown();
boolean fillFailed = _fillInterest.onFail(timeout);
boolean writeFailed = _writeFlusher.onFail(timeout);
-
- // If the endpoint is half closed and there was no onFail handling, the close here
- // This handles the situation where the connection has completed its close handling
+
+ // If the endpoint is half closed and there was no fill/write handling, then close here.
+ // This handles the situation where the connection has completed its close handling
// and the endpoint is half closed, but the other party does not complete the close.
// This perhaps should not check for half closed, however the servlet spec case allows
- // for a dispatched servlet or suspended request to extend beyond the connections idle
- // time. So if this test would always close an idle endpoint that is not handled, then
+ // for a dispatched servlet or suspended request to extend beyond the connections idle
+ // time. So if this test would always close an idle endpoint that is not handled, then
// we would need a mode to ignore timeouts for some HTTP states
if (isOpen() && (output_shutdown || input_shutdown) && !(fillFailed || writeFailed))
close();
- else
+ else
LOG.debug("Ignored idle endpoint {}",this);
}
+ @Override
+ public void upgrade(Connection newConnection)
+ {
+ Connection old_connection = getConnection();
+
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} upgrading from {} to {}", this, old_connection, newConnection);
+
+ ByteBuffer prefilled = (old_connection instanceof Connection.UpgradeFrom)
+ ?((Connection.UpgradeFrom)old_connection).onUpgradeFrom():null;
+ old_connection.onClose();
+ old_connection.getEndPoint().setConnection(newConnection);
+
+ if (newConnection instanceof Connection.UpgradeTo)
+ ((Connection.UpgradeTo)newConnection).onUpgradeTo(prefilled);
+ else if (BufferUtil.hasContent(prefilled))
+ throw new IllegalStateException();
+
+ newConnection.onOpen();
+ }
+
@Override
public String toString()
{
- return String.format("%s@%x{%s<->%d,%s,%s,%s,%s,%s,%d,%s}",
+ return String.format("%s@%x{%s<->%d,%s,%s,%s,%s,%s,%d/%d,%s}",
getClass().getSimpleName(),
hashCode(),
getRemoteAddress(),
isOutputShutdown()?"OSHUT":"out",
_fillInterest.isInterested()?"R":"-",
_writeFlusher.isInProgress()?"W":"-",
+ getIdleFor(),
getIdleTimeout(),
getConnection()==null?null:getConnection().getClass().getSimpleName());
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
protected void shutdownInput()
{
- LOG.debug("ishut {}", this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("ishut {}", this);
_ishut=true;
if (_oshut)
close();
@Override
public void shutdownOutput()
{
- LOG.debug("oshut {}", this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("oshut {}", this);
_oshut = true;
if (_channel.isOpen())
{
public void close()
{
super.close();
- LOG.debug("close {}", this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("close {}", this);
try
{
_channel.close();
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.io;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.util.Map;
import org.eclipse.jetty.util.log.Log;
* {@link EndPoint} associated with {@code oldConnection}, performing connection lifecycle management.
* <p />
* The {@code oldConnection} will be closed by invoking {@link org.eclipse.jetty.io.Connection#onClose()}
- * and the {@code newConnection} will be opened by invoking {@link org.eclipse.jetty.io.Connection#onOpen()}.
+ * and the {@code newConnection} will be opened by invoking {@link org.eclipse.jetty.io.Connection#onOpen(ByteBuffer)}.
* @param oldConnection the old connection to replace
* @param newConnection the new connection replacement
*/
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.io;
import java.io.Closeable;
-
-import org.eclipse.jetty.util.Callback;
+import java.nio.ByteBuffer;
/**
* <p>A {@link Connection} is associated to an {@link EndPoint} so that I/O events
* happening on the {@link EndPoint} can be processed by the {@link Connection}.</p>
- * <p>A typical implementation of {@link Connection} overrides {@link #onOpen()} to
+ * <p>A typical implementation of {@link Connection} overrides {@link #onOpen(ByteBuffer)} to
* {@link EndPoint#fillInterested(Callback) set read interest} on the {@link EndPoint},
* and when the {@link EndPoint} signals read readyness, this {@link Connection} can
* read bytes from the network and interpret them.</p>
{
public void addListener(Listener listener);
- /**
- * <p>Callback method invoked when this {@link Connection} is opened.</p>
- * <p>Creators of the connection implementation are responsible for calling this method.</p>
- */
public void onOpen();
/**
* @return the {@link EndPoint} associated with this {@link Connection}
*/
public EndPoint getEndPoint();
-
+
/**
* <p>Performs a logical close of this connection.</p>
* <p>For simple connections, this may just mean to delegate the close to the associated
public long getBytesOut();
public long getCreatedTimeStamp();
+ public interface UpgradeFrom extends Connection
+ {
+ /* ------------------------------------------------------------ */
+ /** Take the input buffer from the connection on upgrade.
+ * <p>This method is used to take any unconsumed input from
+ * a connection during an upgrade.
+ * @return A buffer of unconsumed input. The caller must return the buffer
+ * to the bufferpool when consumed and this connection must not.
+ */
+ ByteBuffer onUpgradeFrom();
+ }
+
+ public interface UpgradeTo extends Connection
+ {
+ /**
+ * <p>Callback method invoked when this {@link Connection} is upgraded.</p>
+ * <p>This must be called before {@link #onOpen()}.</p>
+ * @param prefilledBuffer An optional buffer that can contain prefilled data. Typically this
+ * results from an upgrade of one protocol to the other where the old connection has buffered
+ * data destined for the new connection. The new connection must take ownership of the buffer
+ * and is responsible for returning it to the buffer pool
+ */
+ void onUpgradeTo(ByteBuffer prefilled);
+ }
public interface Listener
{
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.nio.channels.WritePendingException;
import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.FutureCallback;
-
/**
*
/**
* @param connection the {@link Connection} associated with this {@link EndPoint}
* @see #getConnection()
+ * @see #upgrade(Connection)
*/
void setConnection(Connection connection);
*/
void onClose();
-
+
+ /** Upgrade connections.
+ * Close the old connection, update the endpoint and open the new connection.
+ * If the oldConnection is an instance of {@link Connection.UpgradeFrom} then
+ * a prefilled buffer is requested and passed to the newConnection if it is an instance
+ * of {@link Connection.UpgradeTo}
+ * @param newConnection The connection to upgrade to
+ */
+ public void upgrade(Connection newConnection);
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
if (!_interested.compareAndSet(null,callback))
{
- LOG.warn("Read pending for "+_interested.get()+" pervented "+callback);
+ LOG.warn("Read pending for "+_interested.get()+" prevented "+callback);
throw new ReadPendingException();
}
try
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
return _idleTimestamp;
}
+ public long getIdleFor()
+ {
+ return System.currentTimeMillis() - getIdleTimestamp();
+ }
+
public long getIdleTimeout()
{
return _idleTimeout;
long idleElapsed = System.currentTimeMillis() - idleTimestamp;
long idleLeft = idleTimeout - idleElapsed;
- LOG.debug("{} idle timeout check, elapsed: {} ms, remaining: {} ms", this, idleElapsed, idleLeft);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} idle timeout check, elapsed: {} ms, remaining: {} ms", this, idleElapsed, idleLeft);
if (idleTimestamp != 0 && idleTimeout > 0)
{
if (idleLeft <= 0)
{
- LOG.debug("{} idle timeout expired", this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} idle timeout expired", this);
try
{
onIdleExpired(new TimeoutException("Idle timeout expired: " + idleElapsed + "/" + idleTimeout + " ms"));
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.io;
import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicLong;
+import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.LeakDetector;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.log.Log;
private final LeakDetector<ByteBuffer> leakDetector = new LeakDetector<ByteBuffer>()
{
+ public String id(ByteBuffer resource)
+ {
+ return BufferUtil.toIDString(resource);
+ }
+
@Override
protected void leaked(LeakInfo leakInfo)
{
+ leaked.incrementAndGet();
LeakTrackingByteBufferPool.this.leaked(leakInfo);
}
};
-
+
+ private final static boolean NOISY = Boolean.getBoolean(LeakTrackingByteBufferPool.class.getName() + ".NOISY");
private final ByteBufferPool delegate;
+ private final AtomicLong leakedReleases = new AtomicLong(0);
+ private final AtomicLong leakedAcquires = new AtomicLong(0);
+ private final AtomicLong leaked = new AtomicLong(0);
public LeakTrackingByteBufferPool(ByteBufferPool delegate)
{
public ByteBuffer acquire(int size, boolean direct)
{
ByteBuffer buffer = delegate.acquire(size, direct);
- if (!leakDetector.acquired(buffer))
- LOG.warn("ByteBuffer {}@{} not tracked", buffer, System.identityHashCode(buffer));
+ boolean leaked = leakDetector.acquired(buffer);
+ if (NOISY || !leaked)
+ {
+ leakedAcquires.incrementAndGet();
+ LOG.info(String.format("ByteBuffer acquire %s leaked.acquired=%s", leakDetector.id(buffer), leaked ? "normal" : "LEAK"),
+ new Throwable("LeakStack.Acquire"));
+ }
return buffer;
}
{
if (buffer == null)
return;
- if (!leakDetector.released(buffer))
- LOG.warn("ByteBuffer {}@{} released but not acquired", buffer, System.identityHashCode(buffer));
+ boolean leaked = leakDetector.released(buffer);
+ if (NOISY || !leaked)
+ {
+ leakedReleases.incrementAndGet();
+ LOG.info(String.format("ByteBuffer release %s leaked.released=%s", leakDetector.id(buffer), leaked ? "normal" : "LEAK"), new Throwable(
+ "LeakStack.Release"));
+ }
delegate.release(buffer);
}
+ public void clearTracking()
+ {
+ leakedAcquires.set(0);
+ leakedReleases.set(0);
+ }
+
+ /**
+ * @return count of BufferPool.acquire() calls that detected a leak
+ */
+ public long getLeakedAcquires()
+ {
+ return leakedAcquires.get();
+ }
+
+ /**
+ * @return count of BufferPool.release() calls that detected a leak
+ */
+ public long getLeakedReleases()
+ {
+ return leakedReleases.get();
+ }
+
+ /**
+ * @return count of resources that were acquired but not released
+ */
+ public long getLeakedResources()
+ {
+ return leaked.get();
+ }
+
protected void leaked(LeakDetector<ByteBuffer>.LeakInfo leakInfo)
{
LOG.warn("ByteBuffer " + leakInfo.getResourceDescription() + " leaked at:", leakInfo.getStackFrames());
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.util.BufferUtil;
if (result == null)
{
int capacity = bucket * factor;
- result = direct ? BufferUtil.allocateDirect(capacity) : BufferUtil.allocate(capacity);
+ result = newByteBuffer(capacity, direct);
}
BufferUtil.clear(result);
return result;
}
+ protected ByteBuffer newByteBuffer(int capacity, boolean direct)
+ {
+ return direct ? BufferUtil.allocateDirect(capacity)
+ : BufferUtil.allocate(capacity);
+ }
+
@Override
public void release(ByteBuffer buffer)
{
{
return direct ? directBuffers : heapBuffers;
}
+
+ public static class Tagged extends MappedByteBufferPool
+ {
+ private final AtomicInteger tag = new AtomicInteger();
+
+ @Override
+ protected ByteBuffer newByteBuffer(int capacity, boolean direct)
+ {
+ ByteBuffer buffer = super.newByteBuffer(capacity + 4, direct);
+ buffer.limit(buffer.capacity());
+ buffer.putInt(tag.incrementAndGet());
+ ByteBuffer slice = buffer.slice();
+ BufferUtil.clear(slice);
+ return slice;
+ }
+ }
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.io;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.util.Map;
import java.util.concurrent.Executor;
+
import javax.net.ssl.SSLEngine;
import org.eclipse.jetty.util.BufferUtil;
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.nio.channels.SocketChannel;
import java.util.List;
-import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Scheduler;
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
{
if (_interestOps.compareAndSet(oldInterestOps, newInterestOps))
{
- LOG.debug("Local interests updated {} -> {} for {}", oldInterestOps, newInterestOps, this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Local interests updating {} -> {} for {}", oldInterestOps, newInterestOps, this);
_selector.updateKey(_updateTask);
}
else
{
- LOG.debug("Local interests update conflict: now {}, was {}, attempted {} for {}", _interestOps.get(), oldInterestOps, newInterestOps, this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Local interests update conflict: now {}, was {}, attempted {} for {}", _interestOps.get(), oldInterestOps, newInterestOps, this);
continue;
}
}
else
{
- LOG.debug("Ignoring local interests update {} -> {} for {}", oldInterestOps, newInterestOps, this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Ignoring local interests update {} -> {} for {}", oldInterestOps, newInterestOps, this);
}
break;
}
private void setKeyInterests(int oldInterestOps, int newInterestOps)
{
- LOG.debug("Key interests updated {} -> {}", oldInterestOps, newInterestOps);
_key.interestOps(newInterestOps);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Key interests updated {} -> {} on {}", oldInterestOps, newInterestOps, this);
}
@Override
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import org.eclipse.jetty.util.ConcurrentArrayQueue;
import org.eclipse.jetty.util.TypeUtil;
+import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.component.Dumpable;
public static final String SUBMIT_KEY_UPDATES = "org.eclipse.jetty.io.SelectorManager.submitKeyUpdates";
public static final int DEFAULT_CONNECT_TIMEOUT = 15000;
protected static final Logger LOG = Log.getLogger(SelectorManager.class);
- private final static boolean __submitKeyUpdates = Boolean.valueOf(System.getProperty(SUBMIT_KEY_UPDATES, "false"));
-
+ private final static boolean __submitKeyUpdates = Boolean.valueOf(System.getProperty(SUBMIT_KEY_UPDATES, "true"));
+
private final Executor executor;
private final Scheduler scheduler;
private final ManagedSelector[] _selectors;
private long _connectTimeout = DEFAULT_CONNECT_TIMEOUT;
private long _selectorIndex;
-
+ private int _priorityDelta;
+
protected SelectorManager(Executor executor, Scheduler scheduler)
{
this(executor, scheduler, (Runtime.getRuntime().availableProcessors() + 1) / 2);
_connectTimeout = milliseconds;
}
+
+ @ManagedAttribute("The priority delta to apply to selector threads")
+ public int getSelectorPriorityDelta()
+ {
+ return _priorityDelta;
+ }
+
+ /**
+ * Sets the selector thread priority delta to the given amount.
+ * <p>This allows the selector threads to run at a different priority.
+ * Typically this would be used to lower the priority to give preference
+ * to handling previously accepted connections rather than accepting
+ * new connections.</p>
+ *
+ * @param selectorPriorityDelta the amount to change the thread priority
+ * delta to (may be negative)
+ * @see Thread#getPriority()
+ */
+ public void setSelectorPriorityDelta(int selectorPriorityDelta)
+ {
+ int oldDelta = _priorityDelta;
+ _priorityDelta = selectorPriorityDelta;
+ if (oldDelta != selectorPriorityDelta && isStarted())
+ {
+ for (ManagedSelector selector : _selectors)
+ {
+ Thread thread = selector._thread;
+ if (thread != null)
+ {
+ int deltaDiff = selectorPriorityDelta - oldDelta;
+ thread.setPriority(Math.max(Thread.MIN_PRIORITY, Math.min(Thread.MAX_PRIORITY, thread.getPriority() - deltaDiff)));
+ }
+ }
+ }
+ }
+
/**
* Executes the given task in a different thread.
*
/**
* <p>Registers a channel to perform a non-blocking connect.</p>
- * <p>The channel must be set in non-blocking mode, and {@link SocketChannel#connect(SocketAddress)}
- * must be called prior to calling this method.</p>
+ * <p>The channel must be set in non-blocking mode, {@link SocketChannel#connect(SocketAddress)}
+ * must be called prior to calling this method, and the connect operation must not be completed
+ * (the return value of {@link SocketChannel#connect(SocketAddress)} must be false).</p>
*
* @param channel the channel to register
* @param attachment the attachment object
+ * @see #accept(SocketChannel, Object)
*/
public void connect(SocketChannel channel, Object attachment)
{
set.submit(set.new Connect(channel, attachment));
}
+ /**
+ * @see #accept(SocketChannel, Object)
+ */
+ public void accept(SocketChannel channel)
+ {
+ accept(channel, null);
+ }
+
/**
* <p>Registers a channel to perform non-blocking read/write operations.</p>
* <p>This method is called just after a channel has been accepted by {@link ServerSocketChannel#accept()},
- * or just after having performed a blocking connect via {@link Socket#connect(SocketAddress, int)}.</p>
+ * or just after having performed a blocking connect via {@link Socket#connect(SocketAddress, int)}, or
+ * just after a non-blocking connect via {@link SocketChannel#connect(SocketAddress)} that completed
+ * successfully.</p>
*
* @param channel the channel to register
+ * @param attachment the attachment object
*/
- public void accept(final SocketChannel channel)
+ public void accept(SocketChannel channel, Object attachment)
{
final ManagedSelector selector = chooseSelector();
- selector.submit(selector.new Accept(channel));
+ selector.submit(selector.new Accept(channel, attachment));
}
-
+
/**
* <p>Registers a server channel for accept operations.
* When a {@link SocketChannel} is accepted from the given {@link ServerSocketChannel}
* then the {@link #accepted(SocketChannel)} method is called, which must be
* overridden by a derivation of this class to handle the accepted channel
- *
+ *
* @param server the server channel to register
*/
- public void acceptor(final ServerSocketChannel server)
+ public void acceptor(ServerSocketChannel server)
{
final ManagedSelector selector = chooseSelector();
selector.submit(selector.new Acceptor(server));
}
-
+
/**
* Callback method when a channel is accepted from the {@link ServerSocketChannel}
* passed to {@link #acceptor(ServerSocketChannel)}.
if (isRunning())
LOG.warn("Exception while notifying connection " + connection, x);
else
- LOG.debug("Exception while notifying connection {}",connection, x);
+ LOG.debug("Exception while notifying connection " + connection, x);
+ throw x;
}
}
@Override
protected void doStop() throws Exception
{
- LOG.debug("Stopping {}", this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Stopping {}", this);
Stop stop = new Stop();
submit(stop);
stop.await(getStopTimeout());
- LOG.debug("Stopped {}", this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Stopped {}", this);
}
/**
* Submit a task to update a selector key. If the System property {@link SelectorManager#SUBMIT_KEY_UPDATES}
- * is set true (default is false), the task is passed to {@link #submit(Runnable)}. Otherwise it is run immediately and the selector
- * woken up if need be.
+ * is set true (default is false), the task is passed to {@link #submit(Runnable)}. Otherwise it is run immediately and the selector
+ * woken up if need be.
* @param update the update to a key
*/
public void updateKey(Runnable update)
}
else
{
- runChange(update);
+ // Run only 1 change at once
+ synchronized (this)
+ {
+ runChange(update);
+ }
if (_state.compareAndSet(State.SELECT, State.WAKEUP))
wakeup();
}
}
-
+
/**
* <p>Submits a change to be executed in the selector thread.</p>
* <p>Changes may be submitted from any thread, and the selector thread woken up
// change to the queue and process the state.
_changes.offer(change);
- LOG.debug("Queued change {}", change);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Queued change {}", change);
out: while (true)
{
{
try
{
- LOG.debug("Running change {}", change);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Running change {}", change);
change.run();
}
catch (Throwable x)
{
_thread = Thread.currentThread();
String name = _thread.getName();
+ int priority = _thread.getPriority();
try
{
- _thread.setName(name + "-selector-" + SelectorManager.this.getClass().getSimpleName()+"@"+Integer.toHexString(SelectorManager.this.hashCode())+"/"+_id);
- LOG.debug("Starting {} on {}", _thread, this);
+ if (_priorityDelta != 0)
+ _thread.setPriority(Math.max(Thread.MIN_PRIORITY, Math.min(Thread.MAX_PRIORITY, priority + _priorityDelta)));
+
+ _thread.setName(String.format("%s-selector-%s@%h/%d", name, SelectorManager.this.getClass().getSimpleName(), SelectorManager.this.hashCode(), _id));
+ if (LOG.isDebugEnabled())
+ LOG.debug("Starting {} on {}", _thread, this);
while (isRunning())
select();
- runChanges();
+ while(isStopping())
+ runChanges();
}
finally
{
- LOG.debug("Stopped {} on {}", _thread, this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Stopped {} on {}", _thread, this);
_thread.setName(name);
+ if (_priorityDelta != 0)
+ _thread.setPriority(priority);
}
}
_state.set(State.CHANGES);
continue;
default:
- throw new IllegalStateException();
+ throw new IllegalStateException();
}
}
// Must check first for SELECT and *then* for WAKEUP
boolean connected = finishConnect(channel);
if (connected)
{
- connect.timeout.cancel();
- key.interestOps(0);
- EndPoint endpoint = createEndPoint(channel, key);
- key.attach(endpoint);
+ if (connect.timeout.cancel())
+ {
+ key.interestOps(0);
+ EndPoint endpoint = createEndPoint(channel, key);
+ key.attach(endpoint);
+ }
+ else
+ {
+ throw new SocketTimeoutException("Concurrent Connect Timeout");
+ }
}
else
{
connect.failed(x);
}
}
-
+
private void processAccept(SelectionKey key)
{
ServerSocketChannel server = (ServerSocketChannel)key.channel();
Connection connection = newConnection(channel, endPoint, selectionKey.attachment());
endPoint.setConnection(connection);
connectionOpened(connection);
- LOG.debug("Created {}", endPoint);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Created {}", endPoint);
return endPoint;
}
public void destroyEndPoint(EndPoint endPoint)
{
- LOG.debug("Destroyed {}", endPoint);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Destroyed {}", endPoint);
Connection connection = endPoint.getConnection();
if (connection != null)
connectionClosed(connection);
try
{
SelectionKey key = _channel.register(_selector, SelectionKey.OP_ACCEPT, null);
- LOG.debug("{} acceptor={}", this, key);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} acceptor={}", this, key);
}
catch (Throwable x)
{
private class Accept implements Runnable
{
- private final SocketChannel _channel;
+ private final SocketChannel channel;
+ private final Object attachment;
- public Accept(SocketChannel channel)
+ private Accept(SocketChannel channel, Object attachment)
{
- this._channel = channel;
+ this.channel = channel;
+ this.attachment = attachment;
}
@Override
{
try
{
- SelectionKey key = _channel.register(_selector, 0, null);
- EndPoint endpoint = createEndPoint(_channel, key);
+ SelectionKey key = channel.register(_selector, 0, attachment);
+ EndPoint endpoint = createEndPoint(channel, key);
key.attach(endpoint);
}
catch (Throwable x)
{
- closeNoExceptions(_channel);
+ closeNoExceptions(channel);
LOG.debug(x);
}
}
private final Object attachment;
private final Scheduler.Task timeout;
- public Connect(SocketChannel channel, Object attachment)
+ private Connect(SocketChannel channel, Object attachment)
{
this.channel = channel;
this.attachment = attachment;
}
}
- protected void failed(Throwable failure)
+ private void failed(Throwable failure)
{
if (failed.compareAndSet(false, true))
{
SocketChannel channel = connect.channel;
if (channel.isConnectionPending())
{
- LOG.debug("Channel {} timed out while connecting, closing it", channel);
- connect.failed(new SocketTimeoutException());
+ if (LOG.isDebugEnabled())
+ LOG.debug("Channel {} timed out while connecting, closing it", channel);
+ connect.failed(new SocketTimeoutException("Connect Timeout"));
}
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
_ioException.initCause(th);
}
- LOG.debug(th);
+ if (LOG.isDebugEnabled())
+ LOG.debug(th);
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
{
private static final Logger LOG = Log.getLogger(WriteFlusher.class);
private static final boolean DEBUG = LOG.isDebugEnabled(); // Easy for the compiler to remove the code if DEBUG==false
- private static final ByteBuffer[] EMPTY_BUFFERS = new ByteBuffer[0];
+ private static final ByteBuffer[] EMPTY_BUFFERS = new ByteBuffer[]{BufferUtil.EMPTY_BUFFER};
private static final EnumMap<StateType, Set<StateType>> __stateTransitions = new EnumMap<>(StateType.class);
private static final State __IDLE = new IdleState();
private static final State __WRITING = new WritingState();
private PendingState(ByteBuffer[] buffers, Callback callback)
{
super(StateType.PENDING);
- _buffers = compact(buffers);
+ _buffers = buffers;
_callback = callback;
}
if (_callback!=null)
_callback.succeeded();
}
-
- /**
- * Compacting the buffers is needed because the semantic of WriteFlusher is
- * to write the buffers and if the caller sees that the buffer is consumed,
- * then it can recycle it.
- * If we do not compact, then it is possible that we store a consumed buffer,
- * which is then recycled and refilled; when the WriteFlusher is invoked to
- * complete the write, it will write the refilled bytes, garbling the content.
- *
- * @param buffers the buffers to compact
- * @return the compacted buffers
- */
- private ByteBuffer[] compact(ByteBuffer[] buffers)
- {
- int length = buffers.length;
-
- // Just one element, no need to compact
- if (length < 2)
- return buffers;
-
- // How many still have content ?
- int consumed = 0;
- while (consumed < length && BufferUtil.isEmpty(buffers[consumed]))
- ++consumed;
-
- // All of them still have content, no need to compact
- if (consumed == 0)
- return buffers;
-
- // None has content, return empty
- if (consumed == length)
- return EMPTY_BUFFERS;
-
- return Arrays.copyOfRange(buffers,consumed,length);
- }
}
/**
try
{
- boolean flushed=_endPoint.flush(buffers);
- if (DEBUG)
- LOG.debug("flushed {}", flushed);
+ buffers=flush(buffers);
- // Are we complete?
- for (ByteBuffer b : buffers)
+ // if we are incomplete?
+ if (buffers!=null)
{
- if (!flushed||BufferUtil.hasContent(b))
- {
- PendingState pending=new PendingState(buffers, callback);
- if (updateState(__WRITING,pending))
- onIncompleteFlushed();
- else
- fail(pending);
- return;
- }
+ if (DEBUG)
+ LOG.debug("flushed incomplete");
+ PendingState pending=new PendingState(buffers, callback);
+ if (updateState(__WRITING,pending))
+ onIncompleteFlushed();
+ else
+ fail(pending);
+ return;
}
// If updateState didn't succeed, we don't care as our buffers have been written
* {@link #onFail(Throwable)} or {@link #onClose()}
*/
public void completeWrite()
- {
+ {
if (DEBUG)
LOG.debug("completeWrite: {}", this);
{
ByteBuffer[] buffers = pending.getBuffers();
- boolean flushed=_endPoint.flush(buffers);
- if (DEBUG)
- LOG.debug("flushed {}", flushed);
+ buffers=flush(buffers);
- // Are we complete?
- for (ByteBuffer b : buffers)
+ // if we are incomplete?
+ if (buffers!=null)
{
- if (!flushed || BufferUtil.hasContent(b))
- {
- if (updateState(__COMPLETING,pending))
- onIncompleteFlushed();
- else
- fail(pending);
- return;
- }
+ if (DEBUG)
+ LOG.debug("flushed incomplete {}",BufferUtil.toDetailString(buffers));
+ if (buffers!=pending.getBuffers())
+ pending=new PendingState(buffers, pending._callback);
+ if (updateState(__COMPLETING,pending))
+ onIncompleteFlushed();
+ else
+ fail(pending);
+ return;
}
// If updateState didn't succeed, we don't care as our buffers have been written
}
}
+ /* ------------------------------------------------------------ */
+ /** Flush the buffers iteratively until no progress is made
+ * @param buffers The buffers to flush
+ * @return The unflushed buffers, or null if all flushed
+ * @throws IOException
+ */
+ protected ByteBuffer[] flush(ByteBuffer[] buffers) throws IOException
+ {
+ boolean progress=true;
+ while(progress && buffers!=null)
+ {
+ int before=buffers.length==0?0:buffers[0].remaining();
+ boolean flushed=_endPoint.flush(buffers);
+ int r=buffers.length==0?0:buffers[0].remaining();
+
+ if (flushed)
+ return null;
+
+ progress=before!=r;
+
+ int not_empty=0;
+ while(r==0)
+ {
+ if (++not_empty==buffers.length)
+ {
+ buffers=null;
+ not_empty=0;
+ break;
+ }
+ progress=true;
+ r=buffers[not_empty].remaining();
+ }
+
+ if (not_empty>0)
+ buffers=Arrays.copyOfRange(buffers,not_empty,buffers.length);
+ }
+
+ // If buffers is null, then flush has returned false but has consumed all the data!
+ // This is probably SSL being unable to flush the encrypted buffer, so return EMPTY_BUFFERS
+ // and that will keep this WriteFlusher pending.
+ return buffers==null?EMPTY_BUFFERS:buffers;
+ }
+
/* ------------------------------------------------------------ */
/** Notify the flusher of a failure
* @param cause The cause of the failure
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
{
@Override
public void succeeded()
- {
+ {
}
@Override
getFillInterest().onFail(x);
getWriteFlusher().onFail(x);
}
-
+
},x);
}
};
// all data could be wrapped. So either we need to write some encrypted data,
// OR if we are handshaking we need to read some encrypted data OR
// if neither then we should just try the flush again.
- boolean flush = false;
+ boolean try_again = false;
synchronized (DecryptedEndPoint.this)
{
if (DEBUG)
}
else
{
- flush = true;
+ // We can get here because the WriteFlusher might not see progress
+ // when it has just flushed the encrypted data, but not consumed anymore
+ // of the application buffers. This is mostly avoided by another iteration
+ // within DecryptedEndPoint flush(), but I cannot convince myself that
+ // this is never ever the case.
+ try_again = true;
}
}
- if (flush)
+
+
+ if (try_again)
{
// If the output is closed,
if (isOutputShutdown())
else
{
// try to flush what is pending
- getWriteFlusher().completeWrite();
+ // because this is a special case (see above) we could probably
+ // avoid the dispatch, but best to be sure
+ getExecutor().execute(_runCompletWrite);
}
}
}
// Let's unwrap even if we have no net data because in that
// case we want to fall through to the handshake handling
int pos = BufferUtil.flipToFill(app_in);
- SSLEngineResult unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in);
- BufferUtil.flipToFlush(app_in, pos);
+ SSLEngineResult unwrapResult;
+ try
+ {
+ unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in);
+ }
+ finally
+ {
+ BufferUtil.flipToFlush(app_in, pos);
+ }
if (DEBUG)
LOG.debug("{} unwrap {}", SslConnection.this, unwrapResult);
HandshakeStatus unwrapHandshakeStatus = unwrapResult.getHandshakeStatus();
Status unwrapResultStatus = unwrapResult.getStatus();
- _underFlown = unwrapResultStatus == Status.BUFFER_UNDERFLOW;
+ // Extra check on unwrapResultStatus == OK with zero length buffer is due
+ // to SSL client on android (see bug #454773)
+ _underFlown = unwrapResultStatus == Status.BUFFER_UNDERFLOW || unwrapResultStatus == Status.OK && unwrapResult.bytesConsumed()==0 && unwrapResult.bytesProduced()==0;
if (_underFlown)
{
// or shutting down the output.
return -1;
}
+ case NEED_UNWRAP:
+ {
+ // We expected to read more, but we got closed.
+ // Return -1 to indicate to the application to drive the close.
+ return -1;
+ }
default:
{
throw new IllegalStateException();
}
catch (Exception e)
{
- getEndPoint().close();
+ close();
throw e;
}
finally
// We call sslEngine.wrap to try to take bytes from appOut buffers and encrypt them into the _netOut buffer
BufferUtil.compact(_encryptedOutput);
int pos = BufferUtil.flipToFill(_encryptedOutput);
- SSLEngineResult wrapResult = _sslEngine.wrap(appOuts, _encryptedOutput);
+ SSLEngineResult wrapResult;
+ try
+ {
+ wrapResult=_sslEngine.wrap(appOuts, _encryptedOutput);
+ }
+ finally
+ {
+ BufferUtil.flipToFlush(_encryptedOutput, pos);
+ }
+
if (DEBUG)
LOG.debug("{} wrap {}", SslConnection.this, wrapResult);
- BufferUtil.flipToFlush(_encryptedOutput, pos);
if (wrapResult.bytesConsumed()>0)
consumed+=wrapResult.bytesConsumed();
+ Status wrapResultStatus = wrapResult.getStatus();
boolean allConsumed=true;
- // clear empty buffers to prevent position creeping up the buffer
for (ByteBuffer b : appOuts)
- {
- if (BufferUtil.isEmpty(b))
- BufferUtil.clear(b);
- else
+ if (BufferUtil.hasContent(b))
allConsumed=false;
- }
-
- Status wrapResultStatus = wrapResult.getStatus();
// and deal with the results returned from the sslEngineWrap
switch (wrapResultStatus)
{
if (DEBUG)
LOG.debug("{} renegotiation denied", SslConnection.this);
- shutdownOutput();
+ getEndPoint().shutdownOutput();
return allConsumed;
}
// if we have net bytes, let's try to flush them
if (BufferUtil.hasContent(_encryptedOutput))
- getEndPoint().flush(_encryptedOutput);
+ if (!getEndPoint().flush(_encryptedOutput))
+ getEndPoint().flush(_encryptedOutput); // one retry
// But we also might have more to do for the handshaking state.
switch (handshakeStatus)
{
case NOT_HANDSHAKING:
- // Return with the number of bytes consumed (which may be 0)
+ // If we have not consumed all and had just finished handshaking, then we may
+ // have just flushed the last handshake in the encrypted buffers, so we should
+ // try again.
+ if (!allConsumed && wrapResult.getHandshakeStatus()==HandshakeStatus.FINISHED && BufferUtil.isEmpty(_encryptedOutput))
+ continue;
+
+ // Return true if we consumed all the bytes and encrypted are all flushed
return allConsumed && BufferUtil.isEmpty(_encryptedOutput);
case NEED_TASK:
}
}
}
- catch (Exception e)
- {
- getEndPoint().close();
- throw e;
- }
finally
{
if (DEBUG)
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.security.Constraint;
mappings.put(ALL_METHODS,roleInfo);
}
}
- else
- {
- //combine with any entry that covers all methods
- if (httpMethod == null)
- {
- for (Map.Entry<String, RoleInfo> entry : mappings.entrySet())
- {
- if (entry.getKey() != null)
- {
- RoleInfo specific = entry.getValue();
- specific.combine(roleInfo);
- }
- }
- }
- }
}
/* ------------------------------------------------------------ */
@Override
protected RoleInfo prepareConstraintInfo(String pathInContext, Request request)
{
- Map<String, RoleInfo> mappings = (Map<String, RoleInfo>)_constraintMap.match(pathInContext);
+ Map<String, RoleInfo> mappings = _constraintMap.match(pathInContext);
if (mappings != null)
{
{
String scheme = httpConfig.getSecureScheme();
int port = httpConfig.getSecurePort();
- String url = ("https".equalsIgnoreCase(scheme) && port==443)
- ? "https://"+request.getServerName()+request.getRequestURI()
- : scheme + "://" + request.getServerName() + ":" + port + request.getRequestURI();
- if (request.getQueryString() != null)
- url += "?" + request.getQueryString();
+
+ String url = URIUtil.newURI(scheme, request.getServerName(), port,request.getRequestURI(),request.getQueryString());
response.setContentLength(0);
response.sendRedirect(url);
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
/**
* @version $Rev: 4466 $ $Date: 2009-02-10 23:42:54 +0100 (Tue, 10 Feb 2009) $
+ * @deprecated
*/
public interface CrossContextPsuedoSession<T>
{
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
/**
* @version $Rev: 4660 $ $Date: 2009-02-25 17:29:53 +0100 (Wed, 25 Feb 2009) $
+ * @deprecated
*/
public class HashCrossContextPsuedoSession<T> implements CrossContextPsuedoSession<T>
{
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
private PropertyUserStore _propertyUserStore;
private String _config;
private Resource _configResource;
- private Scanner _scanner;
private int _refreshInterval = 0;// default is not to reload
/* ------------------------------------------------------------ */
protected void doStop() throws Exception
{
super.doStop();
- if (_scanner != null)
- _scanner.stop();
- _scanner = null;
+ if (_propertyUserStore != null)
+ _propertyUserStore.stop();
+ _propertyUserStore = null;
}
/* ------------------------------------------------------------ */
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import javax.security.auth.Subject;
-import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.UserIdentity;
/* ------------------------------------------------------------ */
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
/* ------------------------------------------------------------ */
/**
- * HashMapped User Realm with JDBC as data source. JDBCLoginService extends
- * HashULoginService and adds a method to fetch user information from database.
+ * HashMapped User Realm with JDBC as data source.
* The login() method checks the inherited Map for the user. If the user is not
* found, it will fetch details from the database and populate the inherited
* Map. It then calls the superclass login() method to perform the actual
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.util.Scanner;
import org.eclipse.jetty.util.Scanner.BulkListener;
+import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
String[] roleArray = IdentityService.NO_ROLES;
if (roles != null && roles.length() > 0)
{
- roleArray = roles.split(",");
+ roleArray = StringUtil.csvSplit(roles);
}
known.add(username);
Credential credential = Credential.getCredential(credentials);
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import org.eclipse.jetty.server.handler.ContextHandler.Context;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.server.session.AbstractSession;
-import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
}
@Override
- public Collection<String> getHeaderNames()
- {
- return Collections.emptyList();
- }
+ public Collection<String> getHeaderNames()
+ {
+ return Collections.emptyList();
+ }
- @Override
- public String getHeader(String arg0)
- {
- return null;
- }
+ @Override
+ public String getHeader(String arg0)
+ {
+ return null;
+ }
- @Override
- public Collection<String> getHeaders(String arg0)
- {
+ @Override
+ public Collection<String> getHeaders(String arg0)
+ {
return Collections.emptyList();
- }
+ }
- @Override
- public int getStatus()
- {
- return 0;
- }
+ @Override
+ public int getStatus()
+ {
+ return 0;
+ }
};
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.security.ServerAuthException;
import org.eclipse.jetty.security.UserAuthentication;
import org.eclipse.jetty.server.Authentication;
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
*/
public SpnegoAuthenticator( String authMethod )
{
- _authMethod = authMethod;
+ _authMethod = authMethod;
}
@Override
{
try
{
- if (DeferredAuthentication.isDeferred(res))
- {
+ if (DeferredAuthentication.isDeferred(res))
+ {
return Authentication.UNAUTHENTICATED;
- }
+ }
LOG.debug("SpengoAuthenticator: sending challenge");
res.setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), HttpHeader.NEGOTIATE.asString());
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.server;
import java.io.IOException;
-import java.net.Socket;
-import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import org.eclipse.jetty.io.ArrayByteBufferPool;
import org.eclipse.jetty.io.ByteBufferPool;
-import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.io.ssl.SslConnection;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.Dumpable;
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.ScheduledExecutorScheduler;
import org.eclipse.jetty.util.thread.Scheduler;
private final Scheduler _scheduler;
private final ByteBufferPool _byteBufferPool;
private final Thread[] _acceptors;
- private final Set<EndPoint> _endpoints = Collections.newSetFromMap(new ConcurrentHashMap());
+ private final Set<EndPoint> _endpoints = Collections.newSetFromMap(new ConcurrentHashMap<EndPoint, Boolean>());
private final Set<EndPoint> _immutableEndPoints = Collections.unmodifiableSet(_endpoints);
private volatile CountDownLatch _stopping;
private long _idleTimeout = 30000;
private String _defaultProtocol;
private ConnectionFactory _defaultConnectionFactory;
private String _name;
+ private int _acceptorPriorityDelta;
/**
* @param executor An executor for this connector or null to use the servers executor
* @param scheduler A scheduler for this connector or null to either a {@link Scheduler} set as a server bean or if none set, then a new {@link ScheduledExecutorScheduler} instance.
* @param pool A buffer pool for this connector or null to either a {@link ByteBufferPool} set as a server bean or none set, the new {@link ArrayByteBufferPool} instance.
- * @param acceptors the number of acceptor threads to use, or 0 for a default value.
+ * @param acceptors the number of acceptor threads to use, or -1 for a default value. If 0, then no acceptor threads will be launched and some other mechanism will need to be used to accept new connections.
* @param factories The Connection Factories to use.
*/
public AbstractConnector(
int cores = Runtime.getRuntime().availableProcessors();
if (acceptors < 0)
- acceptors = 1 + cores / 16;
- if (acceptors > 2 * cores)
- LOG.warn("Acceptors should be <= 2*availableProcessors: " + this);
+ acceptors=Math.max(1, Math.min(4,cores/8));
+ if (acceptors > cores)
+ LOG.warn("Acceptors should be <= availableProcessors: " + this);
_acceptors = new Thread[acceptors];
}
_stopping=new CountDownLatch(_acceptors.length);
for (int i = 0; i < _acceptors.length; i++)
- getExecutor().execute(new Acceptor(i));
+ {
+ Acceptor a = new Acceptor(i);
+ addBean(a);
+ getExecutor().execute(a);
+ }
LOG.info("Started {}", this);
}
_stopping=null;
super.doStop();
+
+ for (Acceptor a : getBeans(Acceptor.class))
+ removeBean(a);
LOG.info("Stopped {}", this);
}
}
}
+ @ManagedAttribute("The priority delta to apply to acceptor threads")
+ public int getAcceptorPriorityDelta()
+ {
+ return _acceptorPriorityDelta;
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Set the acceptor thread priority delta.
+ * <p>This allows the acceptor thread to run at a different priority.
+ * Typically this would be used to lower the priority to give preference
+ * to handling previously accepted connections rather than accepting
+ * new connections</p>
+ * @param acceptorPriorityDelta
+ */
+ public void setAcceptorPriorityDelta(int acceptorPriorityDelta)
+ {
+ int old=_acceptorPriorityDelta;
+ _acceptorPriorityDelta = acceptorPriorityDelta;
+ if (old!=acceptorPriorityDelta && isStarted())
+ {
+ for (Thread thread : _acceptors)
+ thread.setPriority(Math.max(Thread.MIN_PRIORITY,Math.min(Thread.MAX_PRIORITY,thread.getPriority()-old+acceptorPriorityDelta)));
+ }
+ }
@Override
@ManagedAttribute("Protocols supported by this connector")
private class Acceptor implements Runnable
{
private final int _acceptor;
+ private String _name;
private Acceptor(int id)
{
@Override
public void run()
{
- Thread current = Thread.currentThread();
- String name = current.getName();
- current.setName(name + "-acceptor-" + _acceptor + "-" + AbstractConnector.this);
+ final Thread thread = Thread.currentThread();
+ String name=thread.getName();
+ _name=String.format("%s-acceptor-%d@%x-%s",name,_acceptor,hashCode(),AbstractConnector.this.toString());
+ thread.setName(_name);
+
+ int priority=thread.getPriority();
+ if (_acceptorPriorityDelta!=0)
+ thread.setPriority(Math.max(Thread.MIN_PRIORITY,Math.min(Thread.MAX_PRIORITY,priority+_acceptorPriorityDelta)));
synchronized (AbstractConnector.this)
{
- _acceptors[_acceptor] = current;
+ _acceptors[_acceptor] = thread;
}
try
}
finally
{
- current.setName(name);
+ thread.setName(name);
+ if (_acceptorPriorityDelta!=0)
+ thread.setPriority(priority);
synchronized (AbstractConnector.this)
{
stopping.countDown();
}
}
+
+ @Override
+ public String toString()
+ {
+ String name=_name;
+ if (name==null)
+ return String.format("acceptor-%d@%x", _acceptor, hashCode());
+ return name;
+ }
+
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.PathMap;
-import org.eclipse.jetty.server.handler.StatisticsHandler;
import org.eclipse.jetty.util.DateCache;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
if (authentication instanceof Authentication.User)
buf.append(((Authentication.User)authentication).getUserIdentity().getUserPrincipal().getName());
else
- buf.append(" - ");
+ buf.append("-");
buf.append(" [");
if (_logDateCache != null)
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.Graceful;
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.net.InetSocketAddress;
-import javax.servlet.ServletRequest;
-
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme;
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jetty.server.handler.HandlerCollection;
-import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.ManagedOperation;
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * Display an optional Warning Message if the {jetty.home} and {jetty.base} are the same directory.
+ * <p>
+ * This is to warn about not recommended approach to setting up the Jetty Distribution.
+ */
+public class HomeBaseWarning
+{
+ private static final Logger LOG = Log.getLogger(HomeBaseWarning.class);
+
+ public HomeBaseWarning()
+ {
+ boolean showWarn = false;
+
+ String home = System.getProperty("jetty.home");
+ String base = System.getProperty("jetty.base");
+
+ if (StringUtil.isBlank(base))
+ {
+ // no base defined? then we are likely running
+ // via direct command line.
+ return;
+ }
+
+ Path homePath = new File(home).toPath();
+ Path basePath = new File(base).toPath();
+
+ try
+ {
+ showWarn = Files.isSameFile(homePath,basePath);
+ }
+ catch (IOException e)
+ {
+ LOG.ignore(e);
+ // Can't definitively determine this state
+ return;
+ }
+
+ if (showWarn)
+ {
+ StringBuilder warn = new StringBuilder();
+ warn.append("This instance of Jetty is not running from a separate {jetty.base} directory");
+ warn.append(", this is not recommended. See documentation at http://www.eclipse.org/jetty/documentation/current/startup.html");
+ LOG.warn("{}",warn.toString());
+ }
+ }
+}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.util.Objects;
-import javax.servlet.http.HttpServletRequest;
-
/**
* Customizes requests that lack the {@code Host} header (for example, HTTP 1.0 requests).
* <p />
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.charset.StandardCharsets;
+import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
protected static HttpChannel<?> setCurrentHttpChannel(HttpChannel<?> channel)
{
HttpChannel<?> last=__currentChannel.get();
- if (channel==null)
- __currentChannel.remove();
- else
- __currentChannel.set(channel);
+ __currentChannel.set(channel);
return last;
}
input.init(_state);
_request = new Request(this, input);
_response = new Response(this, new HttpOutput(this));
+
+ if (LOG.isDebugEnabled())
+ LOG.debug("new {} -> {},{},{}",this,_endPoint,_endPoint.getConnection(),_state);
}
public HttpChannelState getState()
{
return _transport;
}
-
+
+ /**
+ * Get the idle timeout.
+ * <p>This is implemented as a call to {@link EndPoint#getIdleTimeout()}, but may be
+ * overridden by channels that have timeouts different from their connections.
+ */
+ public long getIdleTimeout()
+ {
+ return _endPoint.getIdleTimeout();
+ }
+
+ /**
+ * Set the idle timeout.
+ * <p>This is implemented as a call to {@link EndPoint#setIdleTimeout(long)}, but may be
+ * overridden by channels that have timeouts different from their connections.
+ */
+ public void setIdleTimeout(long timeoutMs)
+ {
+ _endPoint.setIdleTimeout(timeoutMs);
+ }
+
public ByteBufferPool getByteBufferPool()
{
return _connector.getByteBufferPool();
*/
public boolean handle()
{
- LOG.debug("{} handle enter", this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} handle enter", this);
final HttpChannel<?>last = setCurrentHttpChannel(this);
boolean error=false;
try
{
- LOG.debug("{} action {}",this,action);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} action {}",this,action);
switch(action)
{
_response.getHttpOutput().reopen();
_request.setDispatcherType(DispatcherType.REQUEST);
- for (HttpConfiguration.Customizer customizer : _configuration.getCustomizers())
- customizer.customize(getConnector(),_configuration,_request);
+ List<HttpConfiguration.Customizer> customizers = _configuration.getCustomizers();
+ if (!customizers.isEmpty())
+ {
+ for (HttpConfiguration.Customizer customizer : customizers)
+ customizer.customize(getConnector(), _configuration, _request);
+ }
getServer().handle(this);
break;
_response.setStatusWithReason(500,reason);
-
- ErrorHandler eh = ErrorHandler.getErrorHandler(getServer(),_state.getContextHandler());
+
+ ErrorHandler eh = ErrorHandler.getErrorHandler(getServer(),_state.getContextHandler());
if (eh instanceof ErrorHandler.ErrorPageMapper)
{
String error_page=((ErrorHandler.ErrorPageMapper)eh).getErrorPage((HttpServletRequest)_state.getAsyncContextEvent().getSuppliedRequest());
else
_response.getHttpOutput().run();
break;
- }
+ }
default:
break loop;
else
{
error=true;
- throw e;
+ LOG.warn(String.valueOf(_uri), e);
+ _state.error(e);
+ _request.setHandled(true);
+ handleException(e);
}
}
catch (Exception e)
}
}
+ if (action==Action.COMPLETE)
+ {
+ try
+ {
+ _state.completed();
+
+ if (!_response.isCommitted() && !_request.isHandled())
+ {
+ _response.sendError(404);
+ }
+ else
+ {
+ // Complete generating the response
+ _response.closeOutput();
+ }
+ }
+ catch(EofException|ClosedChannelException e)
+ {
+ LOG.debug(e);
+ }
+ catch(Exception e)
+ {
+ LOG.warn("complete failed",e);
+ }
+ finally
+ {
+ _request.setHandled(true);
+ _transport.completed();
+ }
+ }
}
finally
{
Thread.currentThread().setName(threadName);
}
- if (action==Action.COMPLETE)
- {
- try
- {
- _state.completed();
-
- if (!_response.isCommitted() && !_request.isHandled())
- _response.sendError(404);
- else
- // Complete generating the response
- _response.closeOutput();
- }
- catch(EofException|ClosedChannelException e)
- {
- LOG.debug(e);
- }
- catch(Exception e)
- {
- LOG.warn("complete failed",e);
- }
- finally
- {
- _request.setHandled(true);
- _transport.completed();
- }
- }
-
- LOG.debug("{} handle exit, result {}", this, action);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} handle exit, result {}", this, action);
return action!=Action.WAIT;
}
}
else if (isCommitted())
{
- _transport.abort();
+ abort();
if (!(x instanceof EofException))
LOG.warn("Could not send response error 500: "+x);
}
@Override
public String toString()
{
- return String.format("%s@%x{r=%s,a=%s,uri=%s}",
+ return String.format("%s@%x{r=%s,c=%b,a=%s,uri=%s}",
getClass().getSimpleName(),
hashCode(),
_requests,
+ _committed.get(),
_state.getState(),
- _state.getState()==HttpChannelState.State.IDLE?"-":_request.getRequestURI()
- );
+ _uri);
}
@Override
_request.setServerPort(dPort);
_request.setRemoteAddr(InetSocketAddress.createUnresolved(sAddr,sPort));
}
-
+
@Override
public boolean startRequest(HttpMethod httpMethod, String method, ByteBuffer uri, HttpVersion version)
{
LOG.ignore(e);
path = _uri.getDecodedPath(StandardCharsets.ISO_8859_1);
}
-
+
String info = URIUtil.canonicalPath(path);
if (info == null)
default:
String[] values = value.split(",");
- for (int i = 0; values != null && i < values.length; i++)
+ for (int i = 0; i < values.length; i++)
{
expect = HttpHeaderValue.CACHE.get(values[i].trim());
if (expect == null)
public boolean headerComplete()
{
_requests.incrementAndGet();
+ HttpFields fields = _response.getHttpFields();
switch (_version)
{
case HTTP_0_9:
break;
case HTTP_1_0:
- if (_configuration.getSendDateHeader())
- _response.getHttpFields().put(_connector.getServer().getDateField());
+ if (_configuration.getSendDateHeader() && !fields.contains(HttpHeader.DATE))
+ _response.getHttpFields().add(_connector.getServer().getDateField());
break;
case HTTP_1_1:
- if (_configuration.getSendDateHeader())
- _response.getHttpFields().put(_connector.getServer().getDateField());
+ if (_configuration.getSendDateHeader() && !fields.contains(HttpHeader.DATE))
+ _response.getHttpFields().add(_connector.getServer().getDateField());
if (_expect)
{
@Override
public boolean messageComplete()
{
- LOG.debug("{} messageComplete", this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} messageComplete", this);
_request.getHttpInput().messageComplete();
return true;
}
{
if (_state.unhandle()==Action.COMPLETE)
_state.completed();
- else
+ else
throw new IllegalStateException();
}
}
protected boolean sendResponse(ResponseInfo info, ByteBuffer content, boolean complete, final Callback callback)
{
- // TODO check that complete only set true once by changing _committed to AtomicRef<Enum>
boolean committing = _committed.compareAndSet(false, true);
if (committing)
{
if (info==null)
info = _response.newResponseInfo();
- // wrap callback to process 100 or 500 responses
+ // wrap callback to process 100 responses
final int status=info.getStatus();
final Callback committed = (status<200&&status>=100)?new Commit100Callback(callback):new CommitCallback(callback);
/**
* If a write or similar to this channel fails this method should be called. The standard implementation
- * of {@link #failed()} is a noop. But the different implementations of HttpChannel might want to take actions.
+ * is to call {@link HttpTransport#abort()}
*/
- public void failed()
+ public void abort()
{
+ _transport.abort();
}
private class CommitCallback implements Callback
@Override
public void succeeded()
{
- _committed.set(false);
- super.succeeded();
+ if (_committed.compareAndSet(true, false))
+ super.succeeded();
+ else
+ super.failed(new IllegalStateException());
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
{
private static final Logger LOG = Log.getLogger(HttpChannelState.class);
- private final static long DEFAULT_TIMEOUT=30000L;
+ private final static long DEFAULT_TIMEOUT=Long.getLong("org.eclipse.jetty.server.HttpChannelState.DEFAULT_TIMEOUT",30000L);
/** The dispatched state of the HttpChannel, used to control the overall livecycle
*/
ASYNC_WOKEN, // A thread has been dispatch to handle from ASYNCWAIT
ASYNC_IO, // Has been dispatched for async IO
COMPLETING, // Request is completable
- COMPLETED // Request is complete
+ COMPLETED, // Request is complete
+ UPGRADED // Request upgraded the connection
}
/**
case DISPATCHED:
case ASYNC_IO:
throw new IllegalStateException(getStatusString());
+ case UPGRADED:
+ return;
default:
break;
}
_event=null;
}
}
+
+ public void upgrade()
+ {
+ synchronized (this)
+ {
+ switch(_state)
+ {
+ case IDLE:
+ case COMPLETED:
+ break;
+ default:
+ throw new IllegalStateException(getStatusString());
+ }
+ _asyncListeners=null;
+ _state=State.UPGRADED;
+ _async=null;
+ _initial=true;
+ _asyncRead=false;
+ _asyncWrite=false;
+ _timeoutMs=DEFAULT_TIMEOUT;
+ cancelTimeout();
+ _event=null;
+ }
+ }
+
protected void scheduleDispatch()
{
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
private List<Customizer> _customizers=new CopyOnWriteArrayList<>();
private int _outputBufferSize=32*1024;
+ private int _outputAggregationSize=_outputBufferSize/4;
private int _requestHeaderSize=8*1024;
private int _responseHeaderSize=8*1024;
private int _headerCacheSize=512;
private int _securePort;
private String _secureScheme = HttpScheme.HTTPS.asString();
- private boolean _sendServerVersion = true; //send Server: header
- private boolean _sendXPoweredBy = false; //send X-Powered-By: header
- private boolean _sendDateHeader = true; //send Date: header
+ private boolean _sendServerVersion = true;
+ private boolean _sendXPoweredBy = false;
+ private boolean _sendDateHeader = true;
+ private boolean _delayDispatchUntilContent = false;
+ /* ------------------------------------------------------------ */
+ /**
+ * <p>An interface that allows a request object to be customized
+ * for a particular HTTP connector configuration. Unlike Filters, customizer are
+ * applied before the request is submitted for processing and can be specific to the
+ * connector on which the request was received.
+ *
+ * <p>Typically Customizers perform tasks such as: <ul>
+ * <li>process header fields that may be injected by a proxy or load balancer.
+ * <li>setup attributes that may come from the connection/connector such as SSL Session IDs
+ * <li>Allow a request to be marked as secure or authenticated if those have been offloaded
+ * and communicated by header, cookie or other out-of-band mechanism
+ * <li>Set request attributes/fields that are determined by the connector on which the
+ * request was received
+ * </ul>
+ */
public interface Customizer
{
public void customize(Connector connector, HttpConfiguration channelConfig, Request request);
{
_customizers.addAll(config._customizers);
_outputBufferSize=config._outputBufferSize;
+ _outputAggregationSize=config._outputAggregationSize;
_requestHeaderSize=config._requestHeaderSize;
_responseHeaderSize=config._responseHeaderSize;
_securePort=config._securePort;
{
return _customizers;
}
-
+
+ /* ------------------------------------------------------------ */
public <T> T getCustomizer(Class<T> type)
{
for (Customizer c : _customizers)
return null;
}
+ /* ------------------------------------------------------------ */
@ManagedAttribute("The size in bytes of the output buffer used to aggregate HTTP output")
public int getOutputBufferSize()
{
return _outputBufferSize;
}
-
+
+ /* ------------------------------------------------------------ */
+ @ManagedAttribute("The maximum size in bytes for HTTP output to be aggregated")
+ public int getOutputAggregationSize()
+ {
+ return _outputAggregationSize;
+ }
+
+ /* ------------------------------------------------------------ */
@ManagedAttribute("The maximum allowed size in bytes for a HTTP request header")
public int getRequestHeaderSize()
{
return _requestHeaderSize;
}
-
+
+ /* ------------------------------------------------------------ */
@ManagedAttribute("The maximum allowed size in bytes for a HTTP response header")
public int getResponseHeaderSize()
{
return _responseHeaderSize;
}
+ /* ------------------------------------------------------------ */
@ManagedAttribute("The maximum allowed size in bytes for a HTTP header field cache")
public int getHeaderCacheSize()
{
return _headerCacheSize;
}
+ /* ------------------------------------------------------------ */
@ManagedAttribute("The port to which Integral or Confidential security constraints are redirected")
public int getSecurePort()
{
return _securePort;
}
-
+
+ /* ------------------------------------------------------------ */
@ManagedAttribute("The scheme with which Integral or Confidential security constraints are redirected")
public String getSecureScheme()
{
return _secureScheme;
}
+ /* ------------------------------------------------------------ */
public void setSendServerVersion (boolean sendServerVersion)
{
_sendServerVersion = sendServerVersion;
}
+ /* ------------------------------------------------------------ */
@ManagedAttribute("if true, send the Server header in responses")
public boolean getSendServerVersion()
{
return _sendServerVersion;
}
-
+
+ /* ------------------------------------------------------------ */
public void setSendXPoweredBy (boolean sendXPoweredBy)
{
_sendXPoweredBy=sendXPoweredBy;
}
+ /* ------------------------------------------------------------ */
@ManagedAttribute("if true, send the X-Powered-By header in responses")
public boolean getSendXPoweredBy()
{
return _sendXPoweredBy;
}
+ /* ------------------------------------------------------------ */
public void setSendDateHeader(boolean sendDateHeader)
{
_sendDateHeader = sendDateHeader;
}
+ /* ------------------------------------------------------------ */
@ManagedAttribute("if true, include the date in HTTP headers")
public boolean getSendDateHeader()
{
return _sendDateHeader;
}
-
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @param delay if true, delay the application dispatch until content is available
+ */
+ public void setDelayDispatchUntilContent(boolean delay)
+ {
+ _delayDispatchUntilContent = delay;
+ }
+
+ /* ------------------------------------------------------------ */
+ @ManagedAttribute("if true, delay the application dispatch until content is available")
+ public boolean isDelayDispatchUntilContent()
+ {
+ return _delayDispatchUntilContent;
+ }
+
/* ------------------------------------------------------------ */
/**
* <p>Set the {@link Customizer}s that are invoked for every
* request received.</p>
- * <p>Customisers are often used to interpret optional headers (eg {@link ForwardedRequestCustomizer}) or
+ * <p>Customizers are often used to interpret optional headers (eg {@link ForwardedRequestCustomizer}) or
* optional protocol semantics (eg {@link SecureRequestCustomizer}).
- * @param customizers
+ * @param customizers the list of customizers
*/
public void setCustomizers(List<Customizer> customizers)
{
* before being sent to the client. A larger buffer can improve performance by allowing
* a content producer to run without blocking, however larger buffers consume more memory and
* may induce some latency before a client starts processing the content.
- * @param responseBufferSize buffer size in bytes.
+ * @param outputBufferSize buffer size in bytes.
*/
- public void setOutputBufferSize(int responseBufferSize)
+ public void setOutputBufferSize(int outputBufferSize)
{
- _outputBufferSize = responseBufferSize;
+ _outputBufferSize = outputBufferSize;
+ setOutputAggregationSize(outputBufferSize / 4);
}
+ /* ------------------------------------------------------------ */
+ /**
+ * Set the max size of the response content write that is copied into the aggregate buffer.
+ * Writes that are smaller of this size are copied into the aggregate buffer, while
+ * writes that are larger of this size will cause the aggregate buffer to be flushed
+ * and the write to be executed without being copied.
+ * @param outputAggregationSize the max write size that is aggregated
+ */
+ public void setOutputAggregationSize(int outputAggregationSize)
+ {
+ _outputAggregationSize = outputAggregationSize;
+ }
+
/* ------------------------------------------------------------ */
/** Set the maximum size of a request header.
* <p>Larger headers will allow for more and/or larger cookies plus larger form content encoded
}
/* ------------------------------------------------------------ */
- /** Set the TCP/IP port used for CONFIDENTIAL and INTEGRAL
- * redirections.
- * @param confidentialPort
+ /** Set the TCP/IP port used for CONFIDENTIAL and INTEGRAL redirections.
+ * @param securePort the secure port to redirect to.
*/
- public void setSecurePort(int confidentialPort)
+ public void setSecurePort(int securePort)
{
- _securePort = confidentialPort;
+ _securePort = securePort;
}
/* ------------------------------------------------------------ */
- /** Set the URI scheme used for CONFIDENTIAL and INTEGRAL
- * redirections.
- * @param confidentialScheme A string like"https"
+ /** Set the URI scheme used for CONFIDENTIAL and INTEGRAL redirections.
+ * @param secureScheme A scheme string like "https"
*/
- public void setSecureScheme(String confidentialScheme)
+ public void setSecureScheme(String secureScheme)
{
- _secureScheme = confidentialScheme;
+ _secureScheme = secureScheme;
}
@Override
public String toString()
{
- return String.format("%s@%x{%d,%d/%d,%s://:%d,%s}",this.getClass().getSimpleName(),hashCode(),_outputBufferSize,_requestHeaderSize,_responseHeaderSize,_secureScheme,_securePort,_customizers);
+ return String.format("%s@%x{%d/%d,%d/%d,%s://:%d,%s}",
+ this.getClass().getSimpleName(),
+ hashCode(),
+ _outputBufferSize, _outputAggregationSize,
+ _requestHeaderSize,_responseHeaderSize,
+ _secureScheme,_securePort,
+ _customizers);
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.io.IOException;
import java.nio.ByteBuffer;
+import java.nio.channels.WritePendingException;
import java.util.concurrent.RejectedExecutionException;
import org.eclipse.jetty.http.HttpGenerator;
/**
* <p>A {@link Connection} that handles the HTTP protocol.</p>
*/
-public class HttpConnection extends AbstractConnection implements Runnable, HttpTransport
+public class HttpConnection extends AbstractConnection implements Runnable, HttpTransport, Connection.UpgradeFrom
{
public static final String UPGRADE_CONNECTION_ATTRIBUTE = "org.eclipse.jetty.server.HttpConnection.UPGRADE";
private static final boolean REQUEST_BUFFER_DIRECT=false;
private final HttpParser _parser;
private volatile ByteBuffer _requestBuffer = null;
private volatile ByteBuffer _chunk = null;
+ private final SendCallback _sendCallback = new SendCallback();
/* ------------------------------------------------------------ */
/** Get the current connection that this thread is dispatched to.
- * Note that a thread may be processing a request asynchronously and
- * thus not be dispatched to the connection.
+ * Note that a thread may be processing a request asynchronously and
+ * thus not be dispatched to the connection.
* @see Request#getAttribute(String) for a more general way to access the HttpConnection
* @return the current HttpConnection or null
*/
protected static HttpConnection setCurrentConnection(HttpConnection connection)
{
HttpConnection last=__currentConnection.get();
- if (connection==null)
- __currentConnection.remove();
- else
- __currentConnection.set(connection);
+ __currentConnection.set(connection);
return last;
}
HttpInput<ByteBuffer> input = newHttpInput();
_channel = newHttpChannel(input);
_parser = newHttpParser();
- LOG.debug("New HTTP Connection {}", this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("New HTTP Connection {}", this);
}
protected HttpGenerator newHttpGenerator()
{
return new HttpGenerator(_config.getSendServerVersion(),_config.getSendXPoweredBy());
}
-
+
protected HttpInput<ByteBuffer> newHttpInput()
{
return new HttpInputOverHTTP(this);
}
-
+
protected HttpChannelOverHttp newHttpChannel(HttpInput<ByteBuffer> httpInput)
{
return new HttpChannelOverHttp(_connector, _config, getEndPoint(), this, httpInput);
}
-
+
protected HttpParser newHttpParser()
{
return new HttpParser(newRequestHandler(), getHttpConfiguration().getRequestHeaderSize());
return getHttpChannel().getRequests();
}
+ @Override
+ public ByteBuffer onUpgradeFrom()
+ {
+ if (BufferUtil.hasContent(_requestBuffer))
+ {
+ ByteBuffer buffer = _requestBuffer;
+ _requestBuffer=null;
+ return buffer;
+ }
+ return null;
+ }
+
void releaseRequestBuffer()
{
if (_requestBuffer != null && !_requestBuffer.hasRemaining())
_bufferPool.release(buffer);
}
}
-
+
public ByteBuffer getRequestBuffer()
{
if (_requestBuffer == null)
@Override
public void onFillable()
{
- LOG.debug("{} onFillable {}", this, _channel.getState());
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} onFillable {}", this, _channel.getState());
final HttpConnection last=setCurrentConnection(this);
int filled=Integer.MAX_VALUE;
// Do we need some data to parse
if (BufferUtil.isEmpty(_requestBuffer))
{
- // If the previous iteration filled 0 bytes or saw a close, then break here
+ // If the previous iteration filled 0 bytes or saw a close, then break here
if (filled<=0)
break;
-
+
// Can we fill?
if(getEndPoint().isInputShutdown())
{
filled = getEndPoint().fill(_requestBuffer);
if (filled==0) // Do a retry on fill 0 (optimization for SSL connections)
filled = getEndPoint().fill(_requestBuffer);
-
+
// tell parser
if (filled < 0)
_parser.atEOF();
}
}
-
+
// Parse the buffer
if (_parser.parseNext(_requestBuffer==null?BufferUtil.EMPTY_BUFFER:_requestBuffer))
{
close();
}
finally
- {
+ {
setCurrentConnection(last);
if (!suspended && getEndPoint().isOpen() && getEndPoint().getConnection()==this)
{
{
// Not in a race here for the request buffer with #onFillable because an async consumer of
// content would only be started after onFillable has given up control.
- // In a little bit of a race with #completed, but then not sure if it is legal to be doing
+ // In a little bit of a race with #completed, but then not sure if it is legal to be doing
// async calls to IO and have a completed call at the same time.
ByteBuffer requestBuffer = getRequestBuffer();
if (parsed)
break;
-
+
// OK lets read some data
int filled=getEndPoint().fill(requestBuffer);
if (LOG.isDebugEnabled()) // Avoid boxing of variable 'filled'
}
}
}
-
+
@Override
public void completed()
{
Connection connection = (Connection)_channel.getRequest().getAttribute(UPGRADE_CONNECTION_ATTRIBUTE);
if (connection != null)
{
- LOG.debug("Upgrade from {} to {}", this, connection);
- onClose();
- getEndPoint().setConnection(connection);
- connection.onOpen();
+ _channel.getState().upgrade();
+ getEndPoint().upgrade(connection);
_channel.reset();
_parser.reset();
_generator.reset();
return;
}
}
-
+
// Finish consuming the request
// If we are still expecting
if (_channel.isExpecting100Continue())
+ {
// close to seek EOF
_parser.close();
+ }
else if (_parser.inContentState() && _generator.isPersistent())
- // Complete reading the request
- _channel.getRequest().getHttpInput().consumeAll();
+ {
+ // If we are async, then we have problems to complete neatly
+ if (_channel.getRequest().getHttpInput().isAsync())
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("unconsumed async input {}", this);
+ _channel.abort();
+ }
+ else
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("unconsumed input {}", this);
+ // Complete reading the request
+ if (!_channel.getRequest().getHttpInput().consumeAll())
+ _channel.abort();
+ }
+ }
// Reset the channel, parsers and generator
_channel.reset();
_parser.reset();
else
_parser.close();
-
+
// Not in a race here with onFillable, because it has given up control before calling handle.
// in a slight race with #completed, but not sure what to do with that anyway.
releaseRequestBuffer();
getEndPoint().close();
}
}
- // else the parser must be closed, so seek the EOF if we are still open
+ // else the parser must be closed, so seek the EOF if we are still open
else if (getEndPoint().isOpen())
fillInterested();
}
fillInterested();
}
+ @Override
+ public void onClose()
+ {
+ _sendCallback.close();
+ super.onClose();
+ }
+
@Override
public void run()
{
onFillable();
}
-
@Override
public void send(ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback)
{
- if (info==null)
- new ContentCallback(content,lastContent,callback).iterate();
- else
- {
- // If we are still expecting a 100 continues
- if (_channel.isExpecting100Continue())
- // then we can't be persistent
- _generator.setPersistent(false);
- new CommitCallback(info,content,lastContent,callback).iterate();
- }
+ // If we are still expecting a 100 continues when we commit
+ if (info!=null && _channel.isExpecting100Continue())
+ // then we can't be persistent
+ _generator.setPersistent(false);
+
+ if(_sendCallback.reset(info,content,lastContent,callback))
+ _sendCallback.iterate();
}
@Override
public void send(ByteBuffer content, boolean lastContent, Callback callback)
{
- new ContentCallback(content,lastContent,callback).iterate();
+ if (!lastContent && BufferUtil.isEmpty(content))
+ callback.succeeded();
+ else if (_sendCallback.reset(null,content,lastContent,callback))
+ _sendCallback.iterate();
+ }
+
+ @Override
+ public void abort()
+ {
+ // Do a direct close of the output, as this may indicate to a client that the
+ // response is bad either with RST or by abnormal completion of chunked response.
+ getEndPoint().close();
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s[p=%s,g=%s,c=%s]",
+ super.toString(),
+ _parser,
+ _generator,
+ _channel);
}
-
protected class HttpChannelOverHttp extends HttpChannel<ByteBuffer>
{
public HttpChannelOverHttp(Connector connector, HttpConfiguration config, EndPoint endPoint, HttpTransport transport, HttpInput<ByteBuffer> input)
{
super(connector,config,endPoint,transport,input);
}
-
+
@Override
public void earlyEOF()
{
getResponse().getHttpFields().add(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE);
break;
}
+ case HTTP_2:
+ {
+ persistent=false;
+ badMessage(400,null);
+ return true;
+ }
default:
{
throw new IllegalStateException();
if (!persistent)
_generator.setPersistent(false);
- return super.headerComplete();
+ if (!super.headerComplete())
+ return false;
+
+ // Should we delay dispatch until we have some content?
+ // We should not delay if there is no content expect or client is expecting 100 or the response is already committed or the request buffer already has something in it to parse
+ if (getHttpConfiguration().isDelayDispatchUntilContent() && _parser.getContentLength() > 0 &&
+ !isExpecting100Continue() && !isCommitted() && BufferUtil.isEmpty(_requestBuffer))
+ return false;
+
+ return true;
}
@Override
}
@Override
- public void failed()
+ public void abort()
{
- getEndPoint().shutdownOutput();
+ super.abort();
+ _generator.setPersistent(false);
}
-
@Override
public boolean messageComplete()
}
}
- private class CommitCallback extends IteratingCallback
+ private class SendCallback extends IteratingCallback
{
- final ByteBuffer _content;
- final boolean _lastContent;
- final ResponseInfo _info;
- final Callback _callback;
- ByteBuffer _header;
+ private ResponseInfo _info;
+ private ByteBuffer _content;
+ private boolean _lastContent;
+ private Callback _callback;
+ private ByteBuffer _header;
+ private boolean _shutdownOut;
+
+ private SendCallback()
+ {
+ super(true);
+ }
- CommitCallback(ResponseInfo info, ByteBuffer content, boolean last, Callback callback)
+ private boolean reset(ResponseInfo info, ByteBuffer content, boolean last, Callback callback)
{
- _info=info;
- _content=content;
- _lastContent=last;
- _callback=callback;
+ if (reset())
+ {
+ _info = info;
+ _content = content;
+ _lastContent = last;
+ _callback = callback;
+ _header = null;
+ _shutdownOut = false;
+ return true;
+ }
+
+ if (isClosed())
+ callback.failed(new EofException());
+ else
+ callback.failed(new WritePendingException());
+ return false;
}
@Override
public Action process() throws Exception
{
+ if (_callback==null)
+ throw new IllegalStateException();
+
ByteBuffer chunk = _chunk;
while (true)
{
{
case NEED_HEADER:
{
- // Look for optimisation to avoid allocating a _header buffer
- /*
- Cannot use this optimisation unless we work out how not to overwrite data in user passed arrays.
- if (_lastContent && _content!=null && !_content.isReadOnly() && _content.hasArray() && BufferUtil.space(_content)>_config.getResponseHeaderSize() )
- {
- // use spare space in content buffer for header buffer
- int p=_content.position();
- int l=_content.limit();
- _content.position(l);
- _content.limit(l+_config.getResponseHeaderSize());
- _header=_content.slice();
- _header.limit(0);
- _content.position(p);
- _content.limit(l);
- }
- else
- */
- _header = _bufferPool.acquire(_config.getResponseHeaderSize(), HEADER_BUFFER_DIRECT);
-
+ _header = _bufferPool.acquire(_config.getResponseHeaderSize(), HEADER_BUFFER_DIRECT);
continue;
}
case NEED_CHUNK:
}
case FLUSH:
{
- // Don't write the chunk or the content if this is a HEAD response
- if (_channel.getRequest().isHead())
+ // Don't write the chunk or the content if this is a HEAD response, or any other type of response that should have no content
+ if (_channel.getRequest().isHead() || _generator.isNoContent())
{
BufferUtil.clear(chunk);
BufferUtil.clear(_content);
getEndPoint().write(this, _content);
}
else
- continue;
+ {
+ succeeded(); // nothing to write
+ }
return Action.SCHEDULED;
}
case SHUTDOWN_OUT:
{
- getEndPoint().shutdownOutput();
+ _shutdownOut=true;
continue;
}
case DONE:
{
- if (_header!=null)
- {
- // don't release header in spare content buffer
- if (!_lastContent || _content==null || !_content.hasArray() || !_header.hasArray() || _content.array()!=_header.array())
- _bufferPool.release(_header);
- }
return Action.SUCCEEDED;
}
case CONTINUE:
}
}
- @Override
- protected void completed()
- {
- _callback.succeeded();
- }
-
- @Override
- public void failed(final Throwable x)
- {
- super.failed(x);
- failedCallback(_callback,x);
- }
- }
-
- private class ContentCallback extends IteratingCallback
- {
- final ByteBuffer _content;
- final boolean _lastContent;
- final Callback _callback;
-
- ContentCallback(ByteBuffer content, boolean last, Callback callback)
+ private void releaseHeader()
{
- _content=content;
- _lastContent=last;
- _callback=callback;
+ ByteBuffer h=_header;
+ _header=null;
+ if (h!=null)
+ _bufferPool.release(h);
}
@Override
- public Action process() throws Exception
+ protected void onCompleteSuccess()
{
- ByteBuffer chunk = _chunk;
- while (true)
- {
- HttpGenerator.Result result = _generator.generateResponse(null, null, chunk, _content, _lastContent);
- if (LOG.isDebugEnabled())
- LOG.debug("{} generate: {} ({},{})@{}",
- this,
- result,
- BufferUtil.toSummaryString(_content),
- _lastContent,
- _generator.getState());
-
- switch (result)
- {
- case NEED_HEADER:
- throw new IllegalStateException();
- case NEED_CHUNK:
- {
- chunk = _chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, CHUNK_BUFFER_DIRECT);
- continue;
- }
- case FLUSH:
- {
- // Don't write the chunk or the content if this is a HEAD response
- if (_channel.getRequest().isHead())
- {
- BufferUtil.clear(chunk);
- BufferUtil.clear(_content);
- continue;
- }
- else if (BufferUtil.hasContent(chunk))
- {
- if (BufferUtil.hasContent(_content))
- getEndPoint().write(this, chunk, _content);
- else
- getEndPoint().write(this, chunk);
- }
- else if (BufferUtil.hasContent(_content))
- {
- getEndPoint().write(this, _content);
- }
- else
- continue;
- return Action.SCHEDULED;
- }
- case SHUTDOWN_OUT:
- {
- getEndPoint().shutdownOutput();
- continue;
- }
- case DONE:
- {
- return Action.SUCCEEDED;
- }
- case CONTINUE:
- {
- break;
- }
- default:
- {
- throw new IllegalStateException("generateResponse="+result);
- }
- }
- }
+ releaseHeader();
+ _callback.succeeded();
+ if (_shutdownOut)
+ getEndPoint().shutdownOutput();
}
@Override
- protected void completed()
+ public void onCompleteFailure(final Throwable x)
{
- _callback.succeeded();
+ releaseHeader();
+ failedCallback(_callback,x);
+ if (_shutdownOut)
+ getEndPoint().shutdownOutput();
}
@Override
- public void failed(final Throwable x)
+ public String toString()
{
- super.failed(x);
- failedCallback(_callback,x);
+ return String.format("%s[i=%s,cb=%s]",super.toString(),_info,_callback);
}
}
-
- @Override
- public void abort()
- {
- // Do a direct close of the output, as this may indicate to a client that the
- // response is bad either with RST or by abnormal completion of chunked response.
- getEndPoint().close();
- }
-
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.io.IOException;
import java.util.Objects;
+
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
{
if (_eofState != null)
{
- LOG.debug("{} eof {}", this, _eofState);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} eof {}", this, _eofState);
_contentState = _eofState;
}
}
{
if (!isEOF())
{
- LOG.debug("{} early EOF", this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} early EOF", this);
_eofState = EARLY_EOF;
if (_listener == null)
return;
_channelState.onReadPossible();
}
+
+ public boolean isEarlyEOF()
+ {
+ synchronized (lock())
+ {
+ return _contentState==EARLY_EOF;
+ }
+ }
+
/**
* This method should be called to signal that all the expected
* content arrived.
{
if (!isEOF())
{
- LOG.debug("{} EOF", this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} EOF", this);
_eofState = EOF;
if (_listener == null)
return;
_channelState.onReadPossible();
}
- public void consumeAll()
+ public boolean consumeAll()
{
synchronized (lock())
{
+ // Don't bother reading if we already know there was an error.
+ if (_onError != null)
+ return false;
+
try
{
while (!isFinished())
else
consume(item, remaining(item));
}
+ return true;
}
catch (IOException e)
{
LOG.debug(e);
+ return false;
}
}
}
return _contentState==ASYNC;
}
}
-
+
/**
* @return whether an EOF has been detected, even though there may be content to consume.
*/
}
}
+
@Override
public boolean isReady()
{
@Override
public void setReadListener(ReadListener readListener)
{
- readListener = Objects.requireNonNull(readListener);
- synchronized (lock())
+ try
{
- if (_contentState != STREAM)
- throw new IllegalStateException("state=" + _contentState);
- _contentState = ASYNC;
- _listener = readListener;
- _notReady = true;
+ readListener = Objects.requireNonNull(readListener);
+ boolean content;
+ synchronized (lock())
+ {
+ if (_contentState != STREAM)
+ throw new IllegalStateException("state=" + _contentState);
+ _contentState = ASYNC;
+ _listener = readListener;
+ _notReady = true;
+
+ content = getNextContent()!=null || isEOF();
+
+ }
+ if (content)
+ _channelState.onReadPossible();
+ else
+ unready();
+ }
+ catch(IOException e)
+ {
+ throw new RuntimeIOException(e);
}
- _channelState.onReadPossible();
}
public void failed(Throwable x)
}
}
+ @Override
+ public String toString()
+ {
+ return String.format("%s@%x[r=%d,s=%s,e=%s,f=%s]",
+ getClass().getSimpleName(),
+ hashCode(),
+ _contentRead,
+ _contentState,
+ _eofState,
+ _onError);
+ }
+
protected static abstract class State
{
public void waitForContent(HttpInput<?> in) throws IOException
@Override
public int noContent() throws IOException
{
- throw new EofException();
+ throw new EofException("Early EOF");
}
@Override
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
try (Blocker blocker=_readBlocker.acquire())
{
_httpConnection.fillInterested(blocker);
- LOG.debug("{} block readable on {}",this,blocker);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} block readable on {}",this,blocker);
blocker.block();
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
{
private static Logger LOG = Log.getLogger(HttpOutput.class);
private final HttpChannel<?> _channel;
- private final SharedBlockingCallback _writeblock=new SharedBlockingCallback();
+ private final SharedBlockingCallback _writeblock=new SharedBlockingCallback()
+ {
+ @Override
+ protected long getIdleTimeout()
+ {
+ return _channel.getIdleTimeout();
+ }
+ };
private long _written;
private ByteBuffer _aggregate;
private int _bufferSize;
public HttpOutput(HttpChannel<?> channel)
{
_channel = channel;
- _bufferSize = _channel.getHttpConfiguration().getOutputBufferSize();
- _commitSize=_bufferSize/4;
+ HttpConfiguration config = channel.getHttpConfiguration();
+ _bufferSize = config.getOutputBufferSize();
+ _commitSize = config.getOutputAggregationSize();
+ if (_commitSize>_bufferSize)
+ {
+ LOG.warn("OutputAggregationSize {} exceeds bufferSize {}",_commitSize,_bufferSize);
+ _commitSize=_bufferSize;
+ }
}
public HttpChannel<?> getHttpChannel()
catch(IOException e)
{
LOG.debug(e);
- _channel.failed();
+ _channel.abort();
}
releaseBuffer();
return;
catch(IOException e)
{
LOG.debug(e);
- _channel.failed();
+ _channel.abort();
}
releaseBuffer();
return;
write(_aggregate, complete && len==0);
// should we fill aggregate again from the buffer?
- if (len>0 && !complete && len<=_commitSize)
+ if (len>0 && !complete && len<=_commitSize && len<=BufferUtil.space(_aggregate))
{
BufferUtil.append(_aggregate, b, off, len);
return;
* @param httpContent The content to send
* @param callback The callback to use to notify success or failure
*/
- public void sendContent(HttpContent httpContent, Callback callback) throws IOException
+ public void sendContent(HttpContent httpContent, Callback callback)
{
if (BufferUtil.hasContent(_aggregate))
- throw new IOException("written");
+ {
+ callback.failed(new IOException("cannot sendContent() after write()"));
+ return;
+ }
if (_channel.isCommitted())
- throw new IOException("committed");
+ {
+ callback.failed(new IOException("committed"));
+ return;
+ }
while (true)
{
continue;
break;
case ERROR:
- throw new EofException(_onError);
+ callback.failed(new EofException(_onError));
+ return;
+
case CLOSED:
- throw new EofException("Closed");
+ callback.failed(new EofException("Closed"));
+ return;
default:
throw new IllegalStateException();
}
if (buffer!=null)
{
+ if (LOG.isDebugEnabled())
+ LOG.debug("sendContent({}=={},{},direct={})",httpContent,BufferUtil.toDetailString(buffer),callback,_channel.useDirectBuffers());
+
sendContent(buffer,callback);
return;
}
- ReadableByteChannel rbc=httpContent.getReadableByteChannel();
- if (rbc!=null)
+ try
{
- // Close of the rbc is done by the async sendContent
- sendContent(rbc,callback);
- return;
- }
+ ReadableByteChannel rbc=httpContent.getReadableByteChannel();
+ if (rbc!=null)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("sendContent({}=={},{},direct={})",httpContent,rbc,callback,_channel.useDirectBuffers());
+ // Close of the rbc is done by the async sendContent
+ sendContent(rbc,callback);
+ return;
+ }
- InputStream in = httpContent.getInputStream();
- if ( in!=null )
+ InputStream in = httpContent.getInputStream();
+ if ( in!=null )
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("sendContent({}=={},{},direct={})",httpContent,in,callback,_channel.useDirectBuffers());
+ sendContent(in,callback);
+ return;
+ }
+ }
+ catch(Throwable th)
{
- sendContent(in,callback);
+ callback.failed(th);
return;
}
{
Throwable th=_onError;
_onError=null;
- LOG.debug("onError",th);
+ if (LOG.isDebugEnabled())
+ LOG.debug("onError",th);
_writeListener.onError(th);
close();
}
}
- continue loop;
+ continue;
}
-
+
switch(_state.get())
{
- case READY:
case CLOSED:
// even though a write is not possible, because a close has
// occurred, we need to call onWritePossible to tell async
// producer that the last write completed.
+ // so fall through
+ case READY:
try
{
_writeListener.onWritePossible();
_onError=e;
}
break;
+
default:
-
+ _onError=new IllegalStateException("state="+_state.get());
}
}
}
private abstract class AsyncICB extends IteratingCallback
{
@Override
- protected void completed()
+ protected void onCompleteSuccess()
{
while(true)
{
}
@Override
- public void failed(Throwable e)
+ public void onCompleteFailure(Throwable e)
{
- super.failed(e);
- _onError=e;
+ _onError=e==null?new IOException():e;
_channel.getState().onWritePossible();
}
}
}
@Override
- protected void completed()
+ protected void onCompleteSuccess()
{
- super.completed();
+ super.onCompleteSuccess();
if (_complete)
closed();
}
}
@Override
- public void failed(Throwable x)
+ public void onCompleteFailure(Throwable x)
{
- super.failed(x);
+ super.onCompleteFailure(x);
_channel.getByteBufferPool().release(_buffer);
try
{
}
@Override
- public void failed(Throwable x)
+ public void onCompleteFailure(Throwable x)
{
- super.failed(x);
+ super.onCompleteFailure(x);
_channel.getByteBufferPool().release(_buffer);
try
{
}
}
}
-
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import org.eclipse.jetty.util.Callback;
public interface HttpTransport
-{
+{
void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback);
void send(ByteBuffer content, boolean lastContent, Callback callback);
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
* @param size Size of the resource.
* @return LazyList of satisfiable ranges
*/
- public static List<InclusiveByteRange> satisfiableRanges(Enumeration headers, long size)
+ public static List<InclusiveByteRange> satisfiableRanges(Enumeration<String> headers, long size)
{
Object satRanges=null;
headers:
while (headers.hasMoreElements())
{
- String header = (String) headers.nextElement();
+ String header = headers.nextElement();
StringTokenizer tok = new StringTokenizer(header,"=,",false);
String t=null;
try
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
*/
public ByteBuffer getResponses(ByteBuffer requestsBuffer,long idleFor,TimeUnit units) throws Exception
{
- LOG.debug("requests {}", BufferUtil.toUTF8String(requestsBuffer));
+ if (LOG.isDebugEnabled())
+ LOG.debug("requests {}", BufferUtil.toUTF8String(requestsBuffer));
LocalEndPoint endp = executeRequest(requestsBuffer);
endp.waitUntilClosedOrIdleFor(idleFor,units);
ByteBuffer responses = endp.takeOutput();
- endp.getConnection().close();
- LOG.debug("responses {}", BufferUtil.toUTF8String(responses));
+ if (endp.isOutputShutdown())
+ endp.close();
+ if (LOG.isDebugEnabled())
+ LOG.debug("responses {}", BufferUtil.toUTF8String(responses));
return responses;
}
private LocalEndPoint executeRequest(ByteBuffer rawRequest)
{
+ if (!isStarted())
+ throw new IllegalStateException("!STARTED");
LocalEndPoint endp = new LocalEndPoint();
endp.setInput(rawRequest);
_connects.add(endp);
@Override
protected void accept(int acceptorID) throws IOException, InterruptedException
{
- LOG.debug("accepting {}", acceptorID);
+ if (LOG.isDebugEnabled())
+ LOG.debug("accepting {}", acceptorID);
LocalEndPoint endPoint = _connects.take();
endPoint.onOpen();
onEndPointOpened(endPoint);
{
if (size==getOutput().remaining())
{
- LOG.debug("idle for {} {}",idleFor,units);
+ if (LOG.isDebugEnabled())
+ LOG.debug("idle for {} {}",idleFor,units);
return;
}
size=getOutput().remaining();
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.server;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.util.List;
+
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.server.ConnectionFactory;
-import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
if (engine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
{
// Here the SSL handshake is finished, but the protocol has not been negotiated.
- LOG.debug("{} could not negotiate protocol, SSLEngine: {}", this, engine);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} could not negotiate protocol, SSLEngine: {}", this, engine);
close();
}
else
ConnectionFactory connectionFactory = connector.getConnectionFactory(protocol);
if (connectionFactory == null)
{
- LOG.debug("{} application selected protocol '{}', but no correspondent {} has been configured",
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} application selected protocol '{}', but no correspondent {} has been configured",
this, protocol, ConnectionFactory.class.getName());
close();
}
else
{
EndPoint endPoint = getEndPoint();
- Connection oldConnection = endPoint.getConnection();
Connection newConnection = connectionFactory.newConnection(connector, endPoint);
- LOG.debug("{} switching from {} to {}", this, oldConnection, newConnection);
- oldConnection.onClose();
- endPoint.setConnection(newConnection);
- getEndPoint().getConnection().onOpen();
+ endPoint.upgrade(newConnection);
}
}
}
else if (filled < 0)
{
// Something went bad, we need to close.
- LOG.debug("{} closing on client close", this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} closing on client close", this);
close();
}
else
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
{
boolean wasEmpty = _inputQ.isEmpty();
_inputQ.add(item);
- LOG.debug("{} queued {}", this, item);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} queued {}", this, item);
if (wasEmpty)
{
if (!onAsyncRead())
{
_inputQ.pollUnsafe();
onContentConsumed(item);
- LOG.debug("{} consumed {}", this, item);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} consumed {}", this, item);
item = _inputQ.peekUnsafe();
}
return item;
{
try
{
- LOG.debug("{} waiting for content", this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} waiting for content", this);
lock().wait();
}
catch (InterruptedException e)
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.util.List;
import java.util.Locale;
import java.util.Map;
+
import javax.servlet.AsyncContext;
import javax.servlet.AsyncListener;
import javax.servlet.DispatcherType;
private final HttpFields _fields=new HttpFields();
private final List<ServletRequestAttributeListener> _requestAttributeListeners=new ArrayList<>();
private final HttpInput<?> _input;
-
+
public static class MultiPartCleanerListener implements ServletRequestListener
{
@Override
{
//nothing to do, multipart config set up by ServletHolder.handle()
}
-
+
}
-
-
+
+
private boolean _secure;
private boolean _asyncSupported = true;
private HttpURI _uri;
private MultiPartInputStreamParser _multiPartInputStream; //if the request is a multi-part mime
private AsyncContextState _async;
-
+
/* ------------------------------------------------------------ */
public Request(HttpChannel<?> channel, HttpInput<?> input)
{
}
catch (IOException | ServletException e)
{
- if (LOG.isDebugEnabled())
- LOG.warn(e);
- else
- LOG.warn(e.toString());
+ LOG.warn(e);
+ throw new RuntimeException(e);
}
}
public AsyncContext getAsyncContext()
{
HttpChannelState state = getHttpChannelState();
- if (_async==null || state.isInitial() && !state.isAsync())
+ if (_async==null || !state.isAsyncStarted())
throw new IllegalStateException(state.getStatusString());
-
+
return _async;
}
{
return _input.getContentRead();
}
-
+
/* ------------------------------------------------------------ */
/*
* @see javax.servlet.ServletRequest#getContentType()
{
if (_cookies == null || _cookies.getCookies().length == 0)
return null;
-
+
return _cookies.getCookies();
}
//Javadoc for Request.getCookies() stipulates null for no cookies
if (_cookies == null || _cookies.getCookies().length == 0)
return null;
-
+
return _cookies.getCookies();
}
/* ------------------------------------------------------------ */
/**
* Access the underlying Remote {@link InetSocketAddress} for this request.
- *
+ *
* @return the remote {@link InetSocketAddress} for this request, or null if the request has no remote (see {@link ServletRequest#getRemoteAddr()} for
* conditions that result in no remote address)
*/
InetSocketAddress remote=_remote;
if (remote==null)
remote=_channel.getRemoteAddress();
-
+
if (remote==null)
return "";
-
+
InetAddress address = remote.getAddress();
if (address==null)
return remote.getHostString();
-
+
return address.getHostAddress();
}
// Return host from header field
String hostPort = _fields.getStringField(HttpHeader.HOST);
-
+
_port=0;
if (hostPort != null)
{
}
if (hostPort.charAt(0)=='[')
{
- if (hostPort.charAt(len-1)!=']')
+ if (hostPort.charAt(len-1)!=']')
{
LOG.warn("Bad IPv6 "+hostPort);
_serverName=hostPort;
_port=0;
return _serverName;
}
- _serverName = hostPort.substring(1,len-1);
+ _serverName = hostPort.substring(0,len);
}
else if (len==hostPort.length())
_serverName=hostPort;
if (!create)
return null;
-
+
if (getResponse().isCommitted())
throw new IllegalStateException("Response is committed");
UserIdentity user = ((Authentication.User)_authentication).getUserIdentity();
return user.getUserPrincipal();
}
-
+
return null;
}
{
if (_context != null)
throw new IllegalStateException("Request in context!");
-
+
if (_inputState == __READER)
{
try
setQueryEncoding(value == null?null:value.toString());
else if ("org.eclipse.jetty.server.sendContent".equals(name))
LOG.warn("Deprecated: org.eclipse.jetty.server.sendContent");
-
+
if (_attributes == null)
_attributes = new AttributesMap();
_attributes.setAttribute(name,value);
public void setHandled(boolean h)
{
_handled = h;
- Response r=getResponse();
- if (_handled && r.getStatus()==0)
- r.setStatus(200);
}
/* ------------------------------------------------------------ */
{
_remote = addr;
}
-
+
/* ------------------------------------------------------------ */
/**
* @param requestedSessionId
@Override
public String toString()
{
- return (_handled?"[":"(") + getMethod() + " " + _uri + (_handled?"]@":")@") + hashCode() + " " + super.toString();
+ return String.format("%s%s%s %s%s@%x",
+ getClass().getSimpleName(),
+ _handled ? "[" : "(",
+ getMethod(),
+ _uri,
+ _handled ? "]" : ")",
+ hashCode());
}
/* ------------------------------------------------------------ */
if (_multiPartInputStream == null)
{
MultipartConfigElement config = (MultipartConfigElement)getAttribute(__MULTIPART_CONFIG_ELEMENT);
-
+
if (config == null)
throw new IllegalStateException("No multipart config for servlet");
-
+
_multiPartInputStream = new MultiPartInputStreamParser(getInputStream(),
- getContentType(), config,
+ getContentType(), config,
(_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null));
-
+
setAttribute(__MULTIPART_INPUT_STREAM, _multiPartInputStream);
setAttribute(__MULTIPART_CONTEXT, _context);
Collection<Part> parts = _multiPartInputStream.getParts(); //causes parsing
{
//Instantiate an instance and inject it
T h = getContext().createInstance(handlerClass);
-
+
//TODO handle the rest of the upgrade process
-
+
return h;
}
catch (Exception e)
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import org.eclipse.jetty.http.DateGenerator;
import org.eclipse.jetty.http.HttpContent;
-import org.eclipse.jetty.http.HttpContent.ResourceAsHttpContent;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
private final boolean _etagSupported;
private final boolean _useFileMappedBuffer;
- private int _maxCachedFileSize =4*1024*1024;
+ private int _maxCachedFileSize =128*1024*1024;
private int _maxCachedFiles=2048;
- private int _maxCacheSize =32*1024*1024;
+ private int _maxCacheSize =256*1024*1024;
/* ------------------------------------------------------------ */
/** Constructor.
{
try
{
- if (_useFileMappedBuffer && resource.getFile()!=null)
+ if (_useFileMappedBuffer && resource.getFile()!=null && resource.length()<Integer.MAX_VALUE)
return BufferUtil.toMappedBuffer(resource.getFile());
return BufferUtil.toBuffer(resource,true);
@Override
public String toString()
{
- return String.format("%s %s %d %s %s",_resource,_resource.exists(),_resource.lastModified(),_contentType,_lastModifiedBytes);
+ return String.format("CachedContent@%x{r=%s,e=%b,lm=%s,ct=%s}",hashCode(),_resource,_resource.exists(),BufferUtil.toString(_lastModifiedBytes),_contentType);
}
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
private final HttpFields _fields = new HttpFields();
private final AtomicInteger _include = new AtomicInteger();
private HttpOutput _out;
- private int _status = HttpStatus.NOT_SET_000;
+ private int _status = HttpStatus.OK_200;
private String _reason;
private Locale _locale;
private MimeTypes.Type _mimeType;
protected void recycle()
{
- _status = HttpStatus.NOT_SET_000;
+ _status = HttpStatus.OK_200;
_reason = null;
_locale = null;
_mimeType = null;
@Override
public void sendError(int sc) throws IOException
{
- if (sc == 102)
- sendProcessing();
- else
- sendError(sc, null);
+ sendError(sc, null);
}
@Override
if (isIncluding())
return;
+ switch(code)
+ {
+ case -1:
+ _channel.abort();
+ return;
+ case 102:
+ sendProcessing();
+ return;
+ default:
+ }
+
if (isCommitted())
LOG.warn("Committed before "+code+" "+message);
setContentType(MimeTypes.Type.TEXT_HTML_8859_1.toString());
try (ByteArrayISO8859Writer writer= new ByteArrayISO8859Writer(2048);)
{
- if (message != null)
- {
- message= StringUtil.replace(message, "&", "&");
- message= StringUtil.replace(message, "<", "<");
- message= StringUtil.replace(message, ">", ">");
- }
+ message=StringUtil.sanitizeXmlString(message);
String uri= request.getRequestURI();
- if (uri!=null)
- {
- uri= StringUtil.replace(uri, "&", "&");
- uri= StringUtil.replace(uri, "<", "<");
- uri= StringUtil.replace(uri, ">", ">");
- }
+ uri=StringUtil.sanitizeXmlString(uri);
writer.write("<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html;charset=ISO-8859-1\"/>\n");
writer.write("<title>Error ");
if (!URIUtil.hasScheme(location))
{
StringBuilder buf = _channel.getRequest().getRootURL();
-
- if (location.startsWith("//"))
+ if (location.startsWith("/"))
{
- buf.delete(0, buf.length());
- buf.append(_channel.getRequest().getScheme());
- buf.append(":");
- buf.append(location);
+ // absolute in context
+ location=URIUtil.canonicalPath(location);
}
- else if (location.startsWith("/"))
- buf.append(location);
else
{
- String path = _channel.getRequest().getRequestURI();
- String parent = (path.endsWith("/")) ? path : URIUtil.parentPath(path);
- location = URIUtil.addPaths(parent, location);
- if (location == null)
- throw new IllegalStateException("path cannot be above root");
+ // relative to request
+ String path=_channel.getRequest().getRequestURI();
+ String parent=(path.endsWith("/"))?path:URIUtil.parentPath(path);
+ location=URIUtil.canonicalPath(URIUtil.addPaths(parent,location));
if (!location.startsWith("/"))
buf.append('/');
- buf.append(location);
- }
-
- location = buf.toString();
- HttpURI uri = new HttpURI(location);
- String path = uri.getDecodedPath();
- String canonical = URIUtil.canonicalPath(path);
- if (canonical == null)
- throw new IllegalArgumentException();
- if (!canonical.equals(path))
- {
- buf = _channel.getRequest().getRootURL();
- buf.append(URIUtil.encodePath(canonical));
- String param=uri.getParam();
- if (param!=null)
- {
- buf.append(';');
- buf.append(param);
- }
- String query=uri.getQuery();
- if (query!=null)
- {
- buf.append('?');
- buf.append(query);
- }
- String fragment=uri.getFragment();
- if (fragment!=null)
- {
- buf.append('#');
- buf.append(fragment);
- }
- location = buf.toString();
}
+
+ if(location==null)
+ throw new IllegalStateException("path cannot be above root");
+ buf.append(location);
+
+ location=buf.toString();
}
resetBuffer();
String connection = _channel.getRequest().getHttpFields().getStringField(HttpHeader.CONNECTION);
if (connection != null)
{
- String[] values = connection.split(",");
- for (int i = 0; values != null && i < values.length; i++)
+ for (String value: StringUtil.csvSplit(null,connection,0,connection.length()))
{
- HttpHeaderValue cb = HttpHeaderValue.CACHE.get(values[0].trim());
+ HttpHeaderValue cb = HttpHeaderValue.CACHE.get(value);
if (cb != null)
{
protected ResponseInfo newResponseInfo()
{
- if (_status == HttpStatus.NOT_SET_000)
- _status = HttpStatus.OK_200;
return new ResponseInfo(_channel.getRequest().getHttpVersion(), _fields, getLongContentLength(), getStatus(), getReason(), _channel.getRequest().isHead());
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.security.cert.X509Certificate;
+import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSession;
+import javax.servlet.ServletRequest;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.io.ssl.SslConnection;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+/* ------------------------------------------------------------ */
+/** Customizer that extracts the attribute from an {@link SSLContext}
+ * and sets them on the request with {@link ServletRequest#setAttribute(String, Object)}
+ * according to Servlet Specification Requirements.
+ */
public class SecureRequestCustomizer implements HttpConfiguration.Customizer
{
private static final Logger LOG = Log.getLogger(SecureRequestCustomizer.class);
*/
public static final String CACHED_INFO_ATTR = CachedInfo.class.getName();
+ private String sslSessionAttribute = "org.eclipse.jetty.servlet.request.ssl_session";
@Override
public void customize(Connector connector, HttpConfiguration channelConfig, Request request)
SSLEngine sslEngine=sslConnection.getSSLEngine();
customize(sslEngine,request);
}
-
}
/* ------------------------------------------------------------ */
/*
- * Allow the Listener a chance to customise the request. before the server
- * does its stuff. <br>
- * This allows the required attributes to be set for SSL requests. <br>
+ * Customise the request attributes to be set for SSL requests. <br>
* The requirements of the Servlet specs are:
* <ul>
* <li> an attribute named "javax.servlet.request.ssl_session_id" of type
request.setAttribute("javax.servlet.request.cipher_suite",cipherSuite);
request.setAttribute("javax.servlet.request.key_size",keySize);
request.setAttribute("javax.servlet.request.ssl_session_id", idStr);
+ request.setAttribute(getSslSessionAttribute(), sslSession);
}
catch (Exception e)
{
LOG.warn(Log.EXCEPTION,e);
}
}
+
+ public void setSslSessionAttribute(String attribute)
+ {
+ this.sslSessionAttribute = attribute;
+ }
+
+ public String getSslSessionAttribute()
+ {
+ return sslSessionAttribute;
+ }
@Override
public String toString()
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.server;
import java.io.IOException;
-import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
+
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.HandlerWrapper;
+import org.eclipse.jetty.server.handler.StatisticsHandler;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.AttributesMap;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.Uptime;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.Name;
{
return _stopAtShutdown;
}
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Set a graceful stop time.
+ * The {@link StatisticsHandler} must be configured so that open connections can
+ * be tracked for a graceful shutdown.
+ * @see org.eclipse.jetty.util.component.ContainerLifeCycle#setStopTimeout(long)
+ */
+ @Override
+ public void setStopTimeout(long stopTimeout)
+ {
+ super.setStopTimeout(stopTimeout);
+ }
/* ------------------------------------------------------------ */
+ /** Set stop server at shutdown behaviour.
+ * @param stop If true, this server instance will be explicitly stopped when the
+ * JVM is shutdown. Otherwise the JVM is stopped with the server running.
+ * @see Runtime#addShutdownHook(Thread)
+ * @see ShutdownThread
+ */
public void setStopAtShutdown(boolean stop)
{
//if we now want to stop
@Override
protected void doStart() throws Exception
{
+ //If the Server should be stopped when the jvm exits, register
+ //with the shutdown handler thread.
if (getStopAtShutdown())
- {
ShutdownThread.register(this);
- }
+ //Register the Server with the handler thread for receiving
+ //remote stop commands
+ ShutdownMonitor.register(this);
+
+ //Start a thread waiting to receive "stop" commands.
ShutdownMonitor.getInstance().start(); // initialize
LOG.info("jetty-" + getVersion());
// check size of thread pool
SizedThreadPool pool = getBean(SizedThreadPool.class);
int max=pool==null?-1:pool.getMaxThreads();
- int needed=1;
+ int selectors=0;
+ int acceptors=0;
if (mex.size()==0)
{
for (Connector connector : _connectors)
{
if (connector instanceof AbstractConnector)
- needed+=((AbstractConnector)connector).getAcceptors();
+ acceptors+=((AbstractConnector)connector).getAcceptors();
+
if (connector instanceof ServerConnector)
- needed+=((ServerConnector)connector).getSelectorManager().getSelectorCount();
+ selectors+=((ServerConnector)connector).getSelectorManager().getSelectorCount();
}
}
+ int needed=1+selectors+acceptors;
if (max>0 && needed>max)
- throw new IllegalStateException("Insufficient max threads in ThreadPool: max="+max+" < needed="+needed);
+ throw new IllegalStateException(String.format("Insufficient threads: max=%d < needed(acceptors=%d + selectors=%d + request=1)",max,acceptors,selectors));
try
{
mex.ifExceptionThrow();
- LOG.info(String.format("Started @%dms",ManagementFactory.getRuntimeMXBean().getUptime()));
+ LOG.info(String.format("Started @%dms",Uptime.getUptime()));
}
@Override
if (stopTimeout>0)
{
long stop_by=System.currentTimeMillis()+stopTimeout;
- LOG.debug("Graceful shutdown {} by ",this,new Date(stop_by));
+ if (LOG.isDebugEnabled())
+ LOG.debug("Graceful shutdown {} by ",this,new Date(stop_by));
// Wait for shutdowns
for (Future<Void> future: futures)
}
catch (Exception e)
{
- mex.add(e.getCause());
+ mex.add(e);
}
}
}
if (getStopAtShutdown())
ShutdownThread.deregister(this);
+
+ //Unregister the Server with the handler thread for receiving
+ //remote stop commands as we are stopped already
+ ShutdownMonitor.deregister(this);
+
mex.ifExceptionThrow();
final Response response=connection.getResponse();
if (LOG.isDebugEnabled())
- LOG.debug(request.getDispatcherType()+" "+target+" on "+connection);
+ LOG.debug(request.getDispatcherType()+" "+request.getMethod()+" "+target+" on "+connection);
- if ("*".equals(target))
+ if (HttpMethod.OPTIONS.is(request.getMethod()) || "*".equals(target))
{
+ if (!HttpMethod.OPTIONS.is(request.getMethod()))
+ response.sendError(HttpStatus.BAD_REQUEST_400);
handleOptions(request,response);
if (!request.isHandled())
handle(target, request, request, response);
*/
protected void handleOptions(Request request,Response response) throws IOException
{
- if (!HttpMethod.OPTIONS.is(request.getMethod()))
- response.sendError(HttpStatus.BAD_REQUEST_400);
- request.setHandled(true);
- response.setStatus(200);
- response.getHttpFields().put(HttpHeader.ALLOW,"GET,POST,HEAD,OPTIONS");
- response.setContentLength(0);
- response.closeOutput();
}
/* ------------------------------------------------------------ */
final Request baseRequest=connection.getRequest();
final String path=event.getPath();
-
+
if (path!=null)
{
// this is a dispatch with a path
ServletContext context=event.getServletContext();
- HttpURI uri = new HttpURI(context==null?path:URIUtil.addPaths(context.getContextPath(),path));
+ HttpURI uri = new HttpURI(URIUtil.addPaths(context==null?null:context.getContextPath(), path));
baseRequest.setUri(uri);
baseRequest.setRequestURI(null);
- baseRequest.setPathInfo(baseRequest.getRequestURI());
+ baseRequest.setPathInfo(uri.getDecodedPath());
if (uri.getQuery()!=null)
baseRequest.mergeQueryParameters(uri.getQuery(), true); //we have to assume dispatch path and query are UTF8
}
if (LOG.isDebugEnabled())
{
- LOG.debug(request.getDispatcherType()+" "+target+" on "+connection);
+ LOG.debug(request.getDispatcherType()+" "+request.getMethod()+" "+target+" on "+connection);
handle(target, baseRequest, request, response);
LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus());
}
/**
* @return The URI of the first {@link NetworkConnector} and first {@link ContextHandler}, or null
*/
+ @SuppressWarnings("resource")
public URI getURI()
{
NetworkConnector connector=null;
return this.getClass().getName()+"@"+Integer.toHexString(hashCode());
}
+ /* ------------------------------------------------------------ */
@Override
public void dump(Appendable out,String indent) throws IOException
{
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.io.IOException;
import java.net.InetSocketAddress;
-import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.nio.channels.Channel;
import java.nio.channels.SelectionKey;
-import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Executor;
import org.eclipse.jetty.io.SelectChannelEndPoint;
import org.eclipse.jetty.io.SelectorManager;
import org.eclipse.jetty.io.SelectorManager.ManagedSelector;
-import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.Name;
* the number of acceptor threads to use, or -1 for a default value. Acceptors accept new TCP/IP connections. If 0, then
* the selector threads are used to accept connections.
* @param selectors
- * the number of selector threads, or -1 for a default value. Selectors notice and schedule established connection that can make IO progress.
+ * the number of selector threads, or <=0 for a default value. Selectors notice and schedule established connection that can make IO progress.
*/
public ServerConnector(
@Name("server") Server server,
{
this(server,null,null,null,acceptors,selectors,new HttpConnectionFactory());
}
+
+ /* ------------------------------------------------------------ */
+ /** HTTP Server Connection.
+ * <p>Construct a ServerConnector with a private instance of {@link HttpConnectionFactory} as the only factory.</p>
+ * @param server The {@link Server} this connector will accept connection for.
+ * @param acceptors
+ * the number of acceptor threads to use, or -1 for a default value. Acceptors accept new TCP/IP connections. If 0, then
+ * the selector threads are used to accept connections.
+ * @param selectors
+ * the number of selector threads, or <=0 for a default value. Selectors notice and schedule established connection that can make IO progress.
+ * @param factories Zero or more {@link ConnectionFactory} instances used to create and configure connections.
+ */
+ public ServerConnector(
+ @Name("server") Server server,
+ @Name("acceptors") int acceptors,
+ @Name("selectors") int selectors,
+ @Name("factories") ConnectionFactory... factories)
+ {
+ this(server,null,null,null,acceptors,selectors,factories);
+ }
/* ------------------------------------------------------------ */
/** Generic Server Connection with default configuration.
* the number of acceptor threads to use, or -1 for a default value. Acceptors accept new TCP/IP connections. If 0, then
* the selector threads are used to accept connections.
* @param selectors
- * the number of selector threads, or -1 for a default value. Selectors notice and schedule established connection that can make IO progress.
+ * the number of selector threads, or <=0 for a default value. Selectors notice and schedule established connection that can make IO progress.
*/
public ServerConnector(
@Name("server") Server server,
* @param server
* The server this connector will be accept connection for.
* @param executor
- * An executor used to run tasks for handling requests, acceptors and selectors. I
+ * An executor used to run tasks for handling requests, acceptors and selectors.
* If null then use the servers executor
* @param scheduler
* A scheduler used to schedule timeouts. If null then use the servers scheduler
* the number of acceptor threads to use, or -1 for a default value. Acceptors accept new TCP/IP connections. If 0, then
* the selector threads are used to accept connections.
* @param selectors
- * the number of selector threads, or -1 for a default value. Selectors notice and schedule established connection that can make IO progress.
+ * the number of selector threads, or <=0 for a default value. Selectors notice and schedule established connection that can make IO progress.
* @param factories
* Zero or more {@link ConnectionFactory} instances used to create and configure connections.
*/
@Name("factories") ConnectionFactory... factories)
{
super(server,executor,scheduler,bufferPool,acceptors,factories);
- _manager = new ServerConnectorManager(getExecutor(), getScheduler(), selectors > 0 ? selectors : Runtime.getRuntime().availableProcessors());
+ _manager = new ServerConnectorManager(getExecutor(), getScheduler(),
+ selectors>0?selectors:Math.max(1,Math.min(4,Runtime.getRuntime().availableProcessors()/2)));
addBean(_manager, true);
}
return channel!=null && channel.isOpen();
}
+
+ @ManagedAttribute("The priority delta to apply to selector threads")
+ public int getSelectorPriorityDelta()
+ {
+ return _manager.getSelectorPriorityDelta();
+ }
+
+ /**
+ * Sets the selector thread priority delta to the given amount.
+ * <p>This allows the selector threads to run at a different priority.
+ * Typically this would be used to lower the priority to give preference
+ * to handling previously accepted connections rather than accepting
+ * new connections.</p>
+ *
+ * @param selectorPriorityDelta the amount to set the thread priority delta to
+ * (may be negative)
+ * @see Thread#getPriority()
+ */
+ public void setSelectorPriorityDelta(int selectorPriorityDelta)
+ {
+ _manager.setSelectorPriorityDelta(selectorPriorityDelta);
+ }
+
/**
* @return whether this connector uses a channel inherited from the JVM.
* @see System#inheritedChannel()
serverChannel = ServerSocketChannel.open();
InetSocketAddress bindAddress = getHost() == null ? new InetSocketAddress(getPort()) : new InetSocketAddress(getHost(), getPort());
- serverChannel.socket().bind(bindAddress, getAcceptQueueSize());
serverChannel.socket().setReuseAddress(getReuseAddress());
+ serverChannel.socket().bind(bindAddress, getAcceptQueueSize());
_localPort = serverChannel.socket().getLocalPort();
if (_localPort <= 0)
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
super(request);
}
+ @Override
public String getAuthType()
{
return null;
}
+ @Override
public Cookie[] getCookies()
{
return null;
}
+ @Override
public long getDateHeader(String name)
{
return 0;
}
+ @Override
public String getHeader(String name)
{
return null;
}
- public Enumeration getHeaders(String name)
+ @Override
+ public Enumeration<String> getHeaders(String name)
{
return null;
}
- public Enumeration getHeaderNames()
+ @Override
+ public Enumeration<String> getHeaderNames()
{
return null;
}
+ @Override
public int getIntHeader(String name)
{
return 0;
}
+ @Override
public String getMethod()
{
return null;
}
+ @Override
public String getPathInfo()
{
return null;
}
+ @Override
public String getPathTranslated()
{
return null;
}
+ @Override
public String getContextPath()
{
return null;
}
+ @Override
public String getQueryString()
{
return null;
}
+ @Override
public String getRemoteUser()
{
return null;
}
+ @Override
public boolean isUserInRole(String role)
{
return false;
}
+ @Override
public Principal getUserPrincipal()
{
return null;
}
+ @Override
public String getRequestedSessionId()
{
return null;
}
+ @Override
public String getRequestURI()
{
return null;
}
+ @Override
public StringBuffer getRequestURL()
{
return null;
}
+ @Override
public String getServletPath()
{
return null;
}
+ @Override
public HttpSession getSession(boolean create)
{
return null;
}
+ @Override
public HttpSession getSession()
{
return null;
}
+ @Override
public boolean isRequestedSessionIdValid()
{
return false;
}
+ @Override
public boolean isRequestedSessionIdFromCookie()
{
return false;
}
+ @Override
public boolean isRequestedSessionIdFromURL()
{
return false;
}
+ @Override
public boolean isRequestedSessionIdFromUrl()
{
return false;
/**
* @see javax.servlet.http.HttpServletRequest#authenticate(javax.servlet.http.HttpServletResponse)
*/
+ @Override
public boolean authenticate(HttpServletResponse response) throws IOException, ServletException
{
return false;
/**
* @see javax.servlet.http.HttpServletRequest#getPart(java.lang.String)
*/
+ @Override
public Part getPart(String name) throws IOException, ServletException
{
return null;
/**
* @see javax.servlet.http.HttpServletRequest#getParts()
*/
+ @Override
public Collection<Part> getParts() throws IOException, ServletException
{
return null;
/**
* @see javax.servlet.http.HttpServletRequest#login(java.lang.String, java.lang.String)
*/
+ @Override
public void login(String username, String password) throws ServletException
{
/**
* @see javax.servlet.http.HttpServletRequest#logout()
*/
+ @Override
public void logout() throws ServletException
{
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import javax.servlet.SessionCookieConfig;
import javax.servlet.SessionTrackingMode;
-import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.net.InetAddress;
+import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+import org.eclipse.jetty.util.component.Destroyable;
+import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.thread.ShutdownThread;
/**
* Shutdown/Stop Monitor thread.
* <p>
- * This thread listens on the port specified by the STOP.PORT system parameter (defaults to -1 for not listening) for request authenticated with the key given
- * by the STOP.KEY system parameter (defaults to "eclipse") for admin requests.
+ * This thread listens on the host/port specified by the STOP.HOST/STOP.PORT system parameter (defaults to 127.0.0.1/-1 for not listening) for
+ * request authenticated with the key given by the STOP.KEY system parameter (defaults to "eclipse") for admin requests.
* <p>
* If the stop port is set to zero, then a random port is assigned and the port number is printed to stdout.
* <p>
*/
public class ShutdownMonitor
{
+ private final Set<LifeCycle> _lifeCycles = new CopyOnWriteArraySet<LifeCycle>();
+
// Implementation of safe lazy init, using Initialization on Demand Holder technique.
static class Holder
{
{
return Holder.instance;
}
+
+ /* ------------------------------------------------------------ */
+ public static synchronized void register(LifeCycle... lifeCycles)
+ {
+ getInstance()._lifeCycles.addAll(Arrays.asList(lifeCycles));
+ }
+
+ /* ------------------------------------------------------------ */
+ public static synchronized void deregister(LifeCycle lifeCycle)
+ {
+ getInstance()._lifeCycles.remove(lifeCycle);
+ }
+
+ /* ------------------------------------------------------------ */
+ public static synchronized boolean isRegistered(LifeCycle lifeCycle)
+ {
+ return getInstance()._lifeCycles.contains(lifeCycle);
+ }
+
+ /* ------------------------------------------------------------ */
/**
- * ShutdownMonitorThread
+ * ShutdownMonitorRunnable
*
* Thread for listening to STOP.PORT for command to stop Jetty.
* If ShowndownMonitor.exitVm is true, then Sytem.exit will also be
* called after the stop.
*
*/
- public class ShutdownMonitorThread extends Thread
+ private class ShutdownMonitorRunnable implements Runnable
{
-
- public ShutdownMonitorThread ()
+ public ShutdownMonitorRunnable()
{
- setDaemon(true);
- setName("ShutdownMonitor");
+ startListenSocket();
}
@Override
String cmd = lin.readLine();
debug("command=%s",cmd);
- if ("stop".equals(cmd))
+ if ("stop".equalsIgnoreCase(cmd)) //historic, for backward compatibility
{
- // Graceful Shutdown
- debug("Issuing graceful shutdown..");
- ShutdownThread.getInstance().run();
-
- //Stop accepting any more
- close(serverSocket);
- serverSocket = null;
+ //Stop the lifecycles, only if they are registered with the ShutdownThread, only destroying if vm is exiting
+ debug("Issuing stop...");
- //Shutdown input from client
- shutdownInput(socket);
+ for (LifeCycle l:_lifeCycles)
+ {
+ try
+ {
+ if (l.isStarted() && ShutdownThread.isRegistered(l))
+ {
+ l.stop();
+ }
+
+ if ((l instanceof Destroyable) && exitVm)
+ ((Destroyable)l).destroy();
+ }
+ catch (Exception e)
+ {
+ debug(e);
+ }
+ }
+
+ //Stop accepting any more commands
+ stopInput(socket);
// Reply to client
debug("Informing client that we are stopped.");
- out.write("Stopped\r\n".getBytes(StandardCharsets.UTF_8));
- out.flush();
+ informClient(out, "Stopped\r\n");
- // Shutdown Monitor
- socket.shutdownOutput();
- close(socket);
- socket = null;
- debug("Shutting down monitor");
+ //Stop the output and close the monitor socket
+ stopOutput(socket);
if (exitVm)
{
System.exit(0);
}
}
- else if ("status".equals(cmd))
+ else if ("forcestop".equalsIgnoreCase(cmd))
{
+ debug("Issuing force stop...");
+
+ //Ensure that objects are stopped, destroyed only if vm is forcibly exiting
+ stopLifeCycles(exitVm);
+
+ //Stop accepting any more commands
+ stopInput(socket);
+
// Reply to client
- out.write("OK\r\n".getBytes(StandardCharsets.UTF_8));
- out.flush();
+ debug("Informing client that we are stopped.");
+ informClient(out, "Stopped\r\n");
+
+ //Stop the output and close the monitor socket
+ stopOutput(socket);
+
+ //Honour any pre-setup config to stop the jvm when this command is given
+ if (exitVm)
+ {
+ // Kill JVM
+ debug("Killing JVM");
+ System.exit(0);
+ }
+ }
+ else if ("stopexit".equalsIgnoreCase(cmd))
+ {
+ debug("Issuing stop and exit...");
+ //Make sure that objects registered with the shutdown thread will be stopped
+ stopLifeCycles(true);
+
+ //Stop accepting any more input
+ stopInput(socket);
+
+ // Reply to client
+ debug("Informing client that we are stopped.");
+ informClient(out, "Stopped\r\n");
+
+ //Stop the output and close the monitor socket
+ stopOutput(socket);
+
+ debug("Killing JVM");
+ System.exit(0);
+ }
+ else if ("exit".equalsIgnoreCase(cmd))
+ {
+ debug("Killing JVM");
+ System.exit(0);
+ }
+ else if ("status".equalsIgnoreCase(cmd))
+ {
+ // Reply to client
+ informClient(out, "OK\r\n");
}
}
catch (Exception e)
}
}
- public void start()
+ public void stopInput (Socket socket)
{
- if (isAlive())
- {
- // TODO why are we reentrant here?
- if (DEBUG)
- System.err.printf("ShutdownMonitorThread already started");
- return; // cannot start it again
- }
+ //Stop accepting any more input
+ close(serverSocket);
+ serverSocket = null;
+ //Shutdown input from client
+ shutdownInput(socket);
+ }
+
+ public void stopOutput (Socket socket) throws IOException
+ {
+ socket.shutdownOutput();
+ close(socket);
+ socket = null;
+ debug("Shutting down monitor");
+ serverSocket = null;
+ }
+
+ public void informClient (OutputStream out, String message) throws IOException
+ {
+ out.write(message.getBytes(StandardCharsets.UTF_8));
+ out.flush();
+ }
- startListenSocket();
-
- if (serverSocket == null)
+ /**
+ * Stop the registered lifecycles, optionally
+ * calling destroy on them.
+ *
+ * @param destroy
+ */
+ public void stopLifeCycles (boolean destroy)
+ {
+ for (LifeCycle l:_lifeCycles)
{
- return;
+ try
+ {
+ if (l.isStarted())
+ {
+ l.stop();
+ }
+
+ if ((l instanceof Destroyable) && destroy)
+ ((Destroyable)l).destroy();
+ }
+ catch (Exception e)
+ {
+ debug(e);
+ }
}
- if (DEBUG)
- System.err.println("Starting ShutdownMonitorThread");
- super.start();
}
-
- private void startListenSocket()
+
+ public void startListenSocket()
{
if (port < 0)
{
try
{
- serverSocket = new ServerSocket(port,1,InetAddress.getByName("127.0.0.1"));
+ serverSocket = new ServerSocket();
+ serverSocket.setReuseAddress(true);
+ serverSocket.bind(new InetSocketAddress(InetAddress.getByName(host), port), 1);
if (port == 0)
{
// server assigned port in use
}
private boolean DEBUG;
+ private String host;
private int port;
private String key;
private boolean exitVm;
private ServerSocket serverSocket;
- private ShutdownMonitorThread thread;
-
-
+ private Thread thread;
/**
* Create a ShutdownMonitor using configuration from the System properties.
*/
private ShutdownMonitor()
{
- Properties props = System.getProperties();
-
- this.DEBUG = props.containsKey("DEBUG");
+ this.DEBUG = System.getProperty("DEBUG") != null;
// Use values passed thru via /jetty-start/
- this.port = Integer.parseInt(props.getProperty("STOP.PORT","-1"));
- this.key = props.getProperty("STOP.KEY",null);
+ this.host = System.getProperty("STOP.HOST","127.0.0.1");
+ this.port = Integer.parseInt(System.getProperty("STOP.PORT","-1"));
+ this.key = System.getProperty("STOP.KEY",null);
this.exitVm = true;
}
this.DEBUG = flag;
}
+ /**
+ * @param exitVm
+ */
public void setExitVm(boolean exitVm)
{
synchronized (this)
protected void start() throws Exception
{
- ShutdownMonitorThread t = null;
+ Thread t = null;
+
synchronized (this)
{
if (thread != null && thread.isAlive())
{
- // TODO why are we reentrant here?
if (DEBUG)
System.err.printf("ShutdownMonitorThread already started");
return; // cannot start it again
}
- thread = new ShutdownMonitorThread();
+ thread = new Thread(new ShutdownMonitorRunnable());
+ thread.setDaemon(true);
+ thread.setName("ShutdownMonitor");
t = thread;
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
@Override
protected void doStart() throws Exception
{
- LOG.debug("starting {}",this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("starting {}",this);
if (_server==null)
LOG.warn("No Server set for {}",this);
super.doStart();
@Override
protected void doStop() throws Exception
{
- LOG.debug("stopping {}",this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("stopping {}",this);
super.doStop();
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
}
/* ------------------------------------------------------------ */
+ @SuppressWarnings("unchecked")
@Override
public <T extends Handler> T getChildHandlerByClass(Class<T> byclass)
{
{
for (Handler h:branches)
{
+ @SuppressWarnings("unchecked")
T container = (T)h;
Handler[] candidates = container.getChildHandlersByClass(handler.getClass());
if (candidates!=null)
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
URI real = file.toPath().toRealPath().toUri();
if (real.equals(resource.getAlias()))
{
- LOG.debug("Allow symlink {} --> {}",resource,real);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Allow symlink {} --> {}",resource,real);
return true;
}
}
}
if (resource.getAlias().equals(d.toURI()))
{
- LOG.debug("Allow symlink {} --> {}",resource,d);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Allow symlink {} --> {}",resource,d);
return true;
}
}
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.handler;
+
+import java.io.IOException;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.DispatcherType;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Request;
+
+
+/* ------------------------------------------------------------ */
+/** A handler wrapper that provides the framework to asynchronously
+ * delay the handling of a request. While it uses standard servlet
+ * API for asynchronous servlets, it adjusts the dispatch type of the
+ * request so that it does not appear to be asynchronous during the
+ * delayed dispatch.
+ *
+ */
+public class AsyncDelayHandler extends HandlerWrapper
+{
+ public final static String AHW_ATTR = "o.e.j.s.h.AsyncHandlerWrapper";
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ if (!isStarted() || _handler==null)
+ return;
+
+ // Get the dispatcher types
+ DispatcherType ctype = baseRequest.getDispatcherType();
+ DispatcherType dtype = (DispatcherType)baseRequest.getAttribute(AHW_ATTR);
+ Object async_context_path=null;
+ Object async_path_info=null;
+ Object async_query_string=null;
+ Object async_request_uri=null;
+ Object async_servlet_path=null;
+
+ // Is this request a restarted one?
+ boolean restart=false;
+ if (dtype!=null)
+ {
+ // fake the dispatch type to the original
+ baseRequest.setAttribute(AHW_ATTR,null);
+ baseRequest.setDispatcherType(dtype);
+ restart=true;
+
+ async_context_path=baseRequest.getAttribute(AsyncContext.ASYNC_CONTEXT_PATH);
+ baseRequest.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,null);
+ async_path_info=baseRequest.getAttribute(AsyncContext.ASYNC_PATH_INFO);
+ baseRequest.setAttribute(AsyncContext.ASYNC_PATH_INFO,null);
+ async_query_string=baseRequest.getAttribute(AsyncContext.ASYNC_QUERY_STRING);
+ baseRequest.setAttribute(AsyncContext.ASYNC_QUERY_STRING,null);
+ async_request_uri=baseRequest.getAttribute(AsyncContext.ASYNC_REQUEST_URI);
+ baseRequest.setAttribute(AsyncContext.ASYNC_REQUEST_URI,null);
+ async_servlet_path=baseRequest.getAttribute(AsyncContext.ASYNC_SERVLET_PATH);
+ baseRequest.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,null);
+ }
+
+ // Should we handle this request now?
+ if (!startHandling(baseRequest,restart))
+ {
+ // No, so go async and remember dispatch type
+ AsyncContext context = baseRequest.startAsync();
+ baseRequest.setAttribute(AHW_ATTR,ctype);
+
+ delayHandling(baseRequest, context);
+ return;
+ }
+
+ // Handle the request
+ try
+ {
+ _handler.handle(target,baseRequest, request, response);
+ }
+ finally
+ {
+ if(restart)
+ {
+ // reset the request
+ baseRequest.setDispatcherType(ctype);
+ baseRequest.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,async_context_path);
+ baseRequest.setAttribute(AsyncContext.ASYNC_PATH_INFO,async_path_info);
+ baseRequest.setAttribute(AsyncContext.ASYNC_QUERY_STRING,async_query_string);
+ baseRequest.setAttribute(AsyncContext.ASYNC_REQUEST_URI,async_request_uri);
+ baseRequest.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,async_servlet_path);
+ }
+
+ // signal the request is leaving the handler
+ endHandling(baseRequest);
+ }
+
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Called to indicate that a request has been presented for handling
+ * @param request The request to handle
+ * @param restart True if this request is being restarted after a delay
+ * @return True if the request should be handled now
+ */
+ protected boolean startHandling(Request request, boolean restart)
+ {
+ return true;
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Called to indicate that a requests handling is being delayed/
+ * The implementation should arrange for context.dispatch() to be
+ * called when the request should be handled. It may also set
+ * timeouts on the context.
+ *
+ * @param request The request to be delayed
+ * @param context The AsyncContext of the delayed request
+ */
+ protected void delayHandling(Request request,AsyncContext context)
+ {
+ context.dispatch();
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Called to indicated the handling of the request is ending.
+ * This is only the end of the current dispatch of the request and
+ * if the request is asynchronous, it may be handled again.
+ * @param request The request
+ */
+ protected void endHandling(Request request)
+ {
+
+ }
+
+
+}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.ClassLoaderDump;
-import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Dispatcher;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HandlerContainer;
public class ContextHandler extends ScopedHandler implements Attributes, Graceful
{
public final static int SERVLET_MAJOR_VERSION=3;
- public final static int SERVLET_MINOR_VERSION=0;
- public static final Class[] SERVLET_LISTENER_TYPES = new Class[] {ServletContextListener.class,
+ public final static int SERVLET_MINOR_VERSION=1;
+ public static final Class<?>[] SERVLET_LISTENER_TYPES = new Class[] {ServletContextListener.class,
ServletContextAttributeListener.class,
ServletRequestListener.class,
ServletRequestAttributeListener.class};
/*
* @see javax.servlet.ServletContext#getInitParameterNames()
*/
- @SuppressWarnings("rawtypes")
- public Enumeration getInitParameterNames()
+ public Enumeration<String> getInitParameterNames()
{
return Collections.enumeration(_initParams.keySet());
}
_managedAttributes = new HashMap<String, Object>();
String[] attributes = managedAttributes.split(",");
for (String attribute : attributes)
- _managedAttributes.put(attribute,null);
+ {
+ _managedAttributes.put(attribute.trim(),null);
+ }
Enumeration<String> e = _scontext.getAttributeNames();
while (e.hasMoreElements())
/* ------------------------------------------------------------ */
protected void callContextInitialized (ServletContextListener l, ServletContextEvent e)
{
- LOG.debug("contextInitialized: {}->{}",e,l);
+ if (LOG.isDebugEnabled())
+ LOG.debug("contextInitialized: {}->{}",e,l);
l.contextInitialized(e);
}
/* ------------------------------------------------------------ */
protected void callContextDestroyed (ServletContextListener l, ServletContextEvent e)
{
- LOG.debug("contextDestroyed: {}->{}",e,l);
+ if (LOG.isDebugEnabled())
+ LOG.debug("contextDestroyed: {}->{}",e,l);
l.contextDestroyed(e);
}
_scontext.clearAttributes();
}
- /* ------------------------------------------------------------ */
- /*
- * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
- */
- public boolean checkContext(final String target, final Request baseRequest, final HttpServletResponse response) throws IOException
+ public boolean checkVirtualHost(final Request baseRequest)
{
- DispatcherType dispatch = baseRequest.getDispatcherType();
-
- switch (_availability)
- {
- case SHUTDOWN:
- case UNAVAILABLE:
- baseRequest.setHandled(true);
- response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
- return false;
- default:
- if ((DispatcherType.REQUEST.equals(dispatch) && baseRequest.isHandled()))
- return false;
- }
-
- // Check the vhosts
if (_vhosts != null && _vhosts.length > 0)
{
String vhost = normalizeHostname(baseRequest.getServerName());
if (!match || connectorName && !connectorMatch)
return false;
}
-
+ return true;
+ }
+
+ public boolean checkContextPath(String uri)
+ {
// Are we not the root context?
if (_contextPath.length() > 1)
{
// reject requests that are not for us
- if (!target.startsWith(_contextPath))
+ if (!uri.startsWith(_contextPath))
return false;
- if (target.length() > _contextPath.length() && target.charAt(_contextPath.length()) != '/')
+ if (uri.length() > _contextPath.length() && uri.charAt(_contextPath.length()) != '/')
return false;
+ }
+ return true;
+ }
+
+ /* ------------------------------------------------------------ */
+ /*
+ * @see org.eclipse.jetty.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+ */
+ public boolean checkContext(final String target, final Request baseRequest, final HttpServletResponse response) throws IOException
+ {
+ DispatcherType dispatch = baseRequest.getDispatcherType();
- // redirect null path infos
- if (!_allowNullPathInfo && _contextPath.length() == target.length())
- {
- // context request must end with /
+ // Check the vhosts
+ if (!checkVirtualHost(baseRequest))
+ return false;
+
+ if (!checkContextPath(target))
+ return false;
+
+ // Are we not the root context?
+ // redirect null path infos
+ if (!_allowNullPathInfo && _contextPath.length() == target.length() && _contextPath.length()>1)
+ {
+ // context request must end with /
+ baseRequest.setHandled(true);
+ if (baseRequest.getQueryString() != null)
+ response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH) + "?" + baseRequest.getQueryString());
+ else
+ response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH));
+ return false;
+ }
+
+ switch (_availability)
+ {
+ case SHUTDOWN:
+ case UNAVAILABLE:
baseRequest.setHandled(true);
- if (baseRequest.getQueryString() != null)
- response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH) + "?" + baseRequest.getQueryString());
- else
- response.sendRedirect(URIUtil.addPaths(baseRequest.getRequestURI(),URIUtil.SLASH));
+ response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
return false;
- }
+ default:
+ if ((DispatcherType.REQUEST.equals(dispatch) && baseRequest.isHandled()))
+ return false;
}
return true;
if (!_requestAttributeListeners.isEmpty())
{
- ListIterator<ServletRequestAttributeListener> iter = _requestAttributeListeners.listIterator(_requestAttributeListeners.size());
for (int i=_requestAttributeListeners.size();i-->0;)
baseRequest.removeEventListener(_requestAttributeListeners.get(i));
}
}
/* ------------------------------------------------------------ */
+ /**
+ * @param path
+ * @param resource
+ * @return True if the alias is OK
+ */
public boolean checkAlias(String path, Resource resource)
{
// Is the resource aliased?
- if (resource.getAlias() != null)
+ if (resource.getAlias() != null)
{
if (LOG.isDebugEnabled())
LOG.debug("Aliased resource: " + resource + "~=" + resource.getAlias());
matched_path = context_path;
}
- if (matched_path.equals(context_path))
+ if (matched_path != null && matched_path.equals(context_path))
contexts.add(ch);
}
}
/*
* @see javax.servlet.ServletContext#getInitParameterNames()
*/
- @SuppressWarnings("unchecked")
@Override
public Enumeration<String> getInitParameterNames()
{
try
{
+ @SuppressWarnings("unchecked")
Class<? extends EventListener> clazz = _classLoader==null?Loader.loadClass(ContextHandler.class,className):_classLoader.loadClass(className);
addListener(clazz);
}
//classloader, or a parent of it
try
{
- Class reflect = Loader.loadClass(getClass(), "sun.reflect.Reflection");
+ Class<?> reflect = Loader.loadClass(getClass(), "sun.reflect.Reflection");
Method getCallerClass = reflect.getMethod("getCallerClass", Integer.TYPE);
- Class caller = (Class)getCallerClass.invoke(null, 2);
+ Class<?> caller = (Class<?>)getCallerClass.invoke(null, 2);
boolean ok = false;
ClassLoader callerLoader = caller.getClassLoader();
@Override
public String getServerInfo()
{
+ // NOTE: DO NOT CHANGE
+ // this is used by weld to detect Jetty
+ // implementation version
+ // See: https://github.com/weld/core/blob/master/environments/servlet/core/src/main/java/org/jboss/weld/environment/jetty/JettyContainer.java
+ // and its touch(ContainerContext) method
return "jetty/" + Server.getVersion();
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.server.handler;
import java.io.IOException;
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
{
private static final Logger LOG = Log.getLogger(ContextHandlerCollection.class);
- private volatile Trie<ContextHandler[]> _contexts;
+ private final ConcurrentMap<ContextHandler,Handler> _contextBranches = new ConcurrentHashMap<>();
+ private volatile Trie<Map.Entry<String,Branch[]>> _pathBranches;
private Class<? extends ContextHandler> _contextClass = ContextHandler.class;
/* ------------------------------------------------------------ */
@ManagedOperation("update the mapping of context path to context")
public void mapContexts()
{
- int capacity=512;
+ _contextBranches.clear();
+
+ if (getHandlers()==null)
+ {
+ _pathBranches=new ArrayTernaryTrie<>(false,16);
+ return;
+ }
+
+ // Create map of contextPath to handler Branch
+ Map<String,Branch[]> map = new HashMap<>();
+ for (Handler handler:getHandlers())
+ {
+ Branch branch=new Branch(handler);
+ for (String contextPath : branch.getContextPaths())
+ {
+ Branch[] branches=map.get(contextPath);
+ map.put(contextPath, ArrayUtil.addToArray(branches, branch, Branch.class));
+ }
+
+ for (ContextHandler context : branch.getContextHandlers())
+ _contextBranches.putIfAbsent(context, branch.getHandler());
+ }
+
+ // Sort the branches so those with virtual hosts are considered before those without
+ for (Map.Entry<String,Branch[]> entry: map.entrySet())
+ {
+ Branch[] branches=entry.getValue();
+ Branch[] sorted=new Branch[branches.length];
+ int i=0;
+ for (Branch branch:branches)
+ if (branch.hasVirtualHost())
+ sorted[i++]=branch;
+ for (Branch branch:branches)
+ if (!branch.hasVirtualHost())
+ sorted[i++]=branch;
+ entry.setValue(sorted);
+ }
// Loop until we have a big enough trie to hold all the context paths
- Trie<ContextHandler[]> trie;
+ int capacity=512;
+ Trie<Map.Entry<String,Branch[]>> trie;
loop: while(true)
{
trie=new ArrayTernaryTrie<>(false,capacity);
-
- Handler[] branches = getHandlers();
-
- // loop over each group of contexts
- for (int b=0;branches!=null && b<branches.length;b++)
+ for (Map.Entry<String,Branch[]> entry: map.entrySet())
{
- Handler[] handlers=null;
-
- if (branches[b] instanceof ContextHandler)
+ if (!trie.put(entry.getKey().substring(1),entry))
{
- handlers = new Handler[]{ branches[b] };
- }
- else if (branches[b] instanceof HandlerContainer)
- {
- handlers = ((HandlerContainer)branches[b]).getChildHandlersByClass(ContextHandler.class);
- }
- else
- continue;
-
- // for each context handler in a group
- for (int i=0;handlers!=null && i<handlers.length;i++)
- {
- ContextHandler handler=(ContextHandler)handlers[i];
- String contextPath=handler.getContextPath().substring(1);
- ContextHandler[] contexts=trie.get(contextPath);
-
- if (!trie.put(contextPath,ArrayUtil.addToArray(contexts,handler,ContextHandler.class)))
- {
- capacity+=512;
- continue loop;
- }
+ capacity+=512;
+ continue loop;
}
}
-
- break;
+ break loop;
}
+
- // Sort the contexts so those with virtual hosts are considered before those without
- for (String ctx : trie.keySet())
+ if (LOG.isDebugEnabled())
{
- ContextHandler[] contexts=trie.get(ctx);
- ContextHandler[] sorted=new ContextHandler[contexts.length];
- int i=0;
- for (ContextHandler handler:contexts)
- if (handler.getVirtualHosts()!=null && handler.getVirtualHosts().length>0)
- sorted[i++]=handler;
- for (ContextHandler handler:contexts)
- if (handler.getVirtualHosts()==null || handler.getVirtualHosts().length==0)
- sorted[i++]=handler;
- trie.put(ctx,sorted);
+ for (String ctx : trie.keySet())
+ LOG.debug("{}->{}",ctx,Arrays.asList(trie.get(ctx).getValue()));
}
-
- //for (String ctx : trie.keySet())
- // System.err.printf("'%s'->%s%n",ctx,Arrays.asList(trie.get(ctx)));
- _contexts=trie;
+ _pathBranches=trie;
}
-
/* ------------------------------------------------------------ */
/*
* @see org.eclipse.jetty.server.server.handler.HandlerCollection#setHandlers(org.eclipse.jetty.server.server.Handler[])
{
Handler[] handlers = getHandlers();
if (handlers==null || handlers.length==0)
- return;
+ return;
- HttpChannelState async = baseRequest.getHttpChannelState();
- if (async.isAsync())
- {
- ContextHandler context=async.getContextHandler();
- if (context!=null)
- {
- context.handle(target,baseRequest,request, response);
- return;
- }
- }
-
- // data structure which maps a request to a context; first-best match wins
- // { context path => [ context ] }
- // }
- if (target.startsWith("/"))
- {
- int limit = target.length()-1;
-
- while (limit>=0)
- {
- // Get best match
- ContextHandler[] contexts = _contexts.getBest(target,1,limit);
- if (contexts==null)
- break;
+ HttpChannelState async = baseRequest.getHttpChannelState();
+ if (async.isAsync())
+ {
+ ContextHandler context=async.getContextHandler();
+ if (context!=null)
+ {
+ Handler branch = _contextBranches.get(context);
+
+ if (branch==null)
+ context.handle(target,baseRequest,request, response);
+ else
+ branch.handle(target, baseRequest, request, response);
+ return;
+ }
+ }
+
+ // data structure which maps a request to a context; first-best match wins
+ // { context path => [ context ] }
+ // }
+ if (target.startsWith("/"))
+ {
+ int limit = target.length()-1;
- int l=contexts[0].getContextPath().length();
- if (l==1 || target.length()==l || target.charAt(l)=='/')
- {
- for (ContextHandler handler : contexts)
- {
- handler.handle(target,baseRequest, request, response);
- if (baseRequest.isHandled())
- return;
- }
- }
-
- limit=l-2;
- }
- }
- else
- {
+ while (limit>=0)
+ {
+ // Get best match
+ Map.Entry<String,Branch[]> branches = _pathBranches.getBest(target,1,limit);
+
+
+ if (branches==null)
+ break;
+
+ int l=branches.getKey().length();
+ if (l==1 || target.length()==l || target.charAt(l)=='/')
+ {
+ for (Branch branch : branches.getValue())
+ {
+ branch.getHandler().handle(target,baseRequest, request, response);
+ if (baseRequest.isHandled())
+ return;
+ }
+ }
+
+ limit=l-2;
+ }
+ }
+ else
+ {
// This may not work in all circumstances... but then I think it should never be called
- for (int i=0;i<handlers.length;i++)
- {
- handlers[i].handle(target,baseRequest, request, response);
- if ( baseRequest.isHandled())
- return;
- }
- }
+ for (int i=0;i<handlers.length;i++)
+ {
+ handlers[i].handle(target,baseRequest, request, response);
+ if ( baseRequest.isHandled())
+ return;
+ }
+ }
}
-
/* ------------------------------------------------------------ */
/** Add a context handler.
* @param contextPath The context path to add
_contextClass = contextClass;
}
+ /* ------------------------------------------------------------ */
+ /* ------------------------------------------------------------ */
+ /* ------------------------------------------------------------ */
+ private final static class Branch
+ {
+ private final Handler _handler;
+ private final ContextHandler[] _contexts;
+
+ Branch(Handler handler)
+ {
+ _handler=handler;
+
+ if (handler instanceof ContextHandler)
+ {
+ _contexts = new ContextHandler[]{(ContextHandler)handler};
+ }
+ else if (handler instanceof HandlerContainer)
+ {
+ Handler[] contexts=((HandlerContainer)handler).getChildHandlersByClass(ContextHandler.class);
+ _contexts = new ContextHandler[contexts.length];
+ System.arraycopy(contexts, 0, _contexts, 0, contexts.length);
+ }
+ else
+ _contexts = new ContextHandler[0];
+ }
+
+ Set<String> getContextPaths()
+ {
+ Set<String> set = new HashSet<String>();
+ for (ContextHandler context:_contexts)
+ set.add(context.getContextPath());
+ return set;
+ }
+
+ boolean hasVirtualHost()
+ {
+ for (ContextHandler context:_contexts)
+ if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
+ return true;
+ return false;
+ }
+
+ ContextHandler[] getContextHandlers()
+ {
+ return _contexts;
+ }
+
+ Handler getHandler()
+ {
+ return _handler;
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("{%s,%s}",_handler,Arrays.asList(_contexts));
+ }
+ }
+
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
response.setContentType(MimeTypes.Type.TEXT_HTML.toString());
- ByteArrayISO8859Writer writer = new ByteArrayISO8859Writer(1500);
-
- writer.write("<HTML>\n<HEAD>\n<TITLE>Error 404 - Not Found");
- writer.write("</TITLE>\n<BODY>\n<H2>Error 404 - Not Found.</H2>\n");
- writer.write("No context on this server matched or handled this request.<BR>");
- writer.write("Contexts known to this server are: <ul>");
+ try (ByteArrayISO8859Writer writer = new ByteArrayISO8859Writer(1500);)
+ {
+ writer.write("<HTML>\n<HEAD>\n<TITLE>Error 404 - Not Found");
+ writer.write("</TITLE>\n<BODY>\n<H2>Error 404 - Not Found.</H2>\n");
+ writer.write("No context on this server matched or handled this request.<BR>");
+ writer.write("Contexts known to this server are: <ul>");
- Server server = getServer();
- Handler[] handlers = server==null?null:server.getChildHandlersByClass(ContextHandler.class);
+ Server server = getServer();
+ Handler[] handlers = server==null?null:server.getChildHandlersByClass(ContextHandler.class);
- for (int i=0;handlers!=null && i<handlers.length;i++)
- {
- ContextHandler context = (ContextHandler)handlers[i];
- if (context.isRunning())
- {
- writer.write("<li><a href=\"");
- if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
- writer.write("http://"+context.getVirtualHosts()[0]+":"+request.getLocalPort());
- writer.write(context.getContextPath());
- if (context.getContextPath().length()>1 && context.getContextPath().endsWith("/"))
- writer.write("/");
- writer.write("\">");
- writer.write(context.getContextPath());
- if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
- writer.write(" @ "+context.getVirtualHosts()[0]+":"+request.getLocalPort());
- writer.write(" ---> ");
- writer.write(context.toString());
- writer.write("</a></li>\n");
- }
- else
+ for (int i=0;handlers!=null && i<handlers.length;i++)
{
- writer.write("<li>");
- writer.write(context.getContextPath());
- if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
- writer.write(" @ "+context.getVirtualHosts()[0]+":"+request.getLocalPort());
- writer.write(" ---> ");
- writer.write(context.toString());
- if (context.isFailed())
- writer.write(" [failed]");
- if (context.isStopped())
- writer.write(" [stopped]");
- writer.write("</li>\n");
+ ContextHandler context = (ContextHandler)handlers[i];
+ if (context.isRunning())
+ {
+ writer.write("<li><a href=\"");
+ if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
+ writer.write("http://"+context.getVirtualHosts()[0]+":"+request.getLocalPort());
+ writer.write(context.getContextPath());
+ if (context.getContextPath().length()>1 && context.getContextPath().endsWith("/"))
+ writer.write("/");
+ writer.write("\">");
+ writer.write(context.getContextPath());
+ if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
+ writer.write(" @ "+context.getVirtualHosts()[0]+":"+request.getLocalPort());
+ writer.write(" ---> ");
+ writer.write(context.toString());
+ writer.write("</a></li>\n");
+ }
+ else
+ {
+ writer.write("<li>");
+ writer.write(context.getContextPath());
+ if (context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
+ writer.write(" @ "+context.getVirtualHosts()[0]+":"+request.getLocalPort());
+ writer.write(" ---> ");
+ writer.write(context.toString());
+ if (context.isFailed())
+ writer.write(" [failed]");
+ if (context.isStopped())
+ writer.write(" [stopped]");
+ writer.write("</li>\n");
+ }
}
- }
- writer.write("</ul><hr>");
- writer.write("<a href=\"http://eclipse.org/jetty\"><img border=0 src=\"/favicon.ico\"/></a> ");
- writer.write("<a href=\"http://eclipse.org/jetty\">Powered by Jetty:// Java Web Server</a><hr/>\n");
+ writer.write("</ul><hr>");
+ writer.write("<a href=\"http://eclipse.org/jetty\"><img border=0 src=\"/favicon.ico\"/></a> ");
+ writer.write("<a href=\"http://eclipse.org/jetty\">Powered by Jetty:// Java Web Server</a><hr/>\n");
- writer.write("\n</BODY>\n</HTML>\n");
- writer.flush();
- response.setContentLength(writer.size());
- try (OutputStream out=response.getOutputStream())
- {
- writer.writeTo(out);
- }
+ writer.write("\n</BODY>\n</HTML>\n");
+ writer.flush();
+ response.setContentLength(writer.size());
+ try (OutputStream out=response.getOutputStream())
+ {
+ writer.writeTo(out);
+ }
+ }
}
/* ------------------------------------------------------------ */
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.ByteArrayISO8859Writer;
+import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
if (string==null)
return;
- for (int i=0;i<string.length();i++)
- {
- char c=string.charAt(i);
-
- switch(c)
- {
- case '&' :
- writer.write("&");
- break;
- case '<' :
- writer.write("<");
- break;
- case '>' :
- writer.write(">");
- break;
-
- default:
- if (Character.isISOControl(c) && !Character.isWhitespace(c))
- writer.write('?');
- else
- writer.write(c);
- }
- }
+ writer.write(StringUtil.sanitizeXmlString(string));
}
/* ------------------------------------------------------------ */
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.server.handler;
import java.io.IOException;
+import java.util.Arrays;
import java.util.List;
import javax.servlet.ServletException;
child.destroy();
super.destroy();
}
+
+ /* ------------------------------------------------------------ */
+ @Override
+ public String toString()
+ {
+ Handler[] handlers=getHandlers();
+ return super.toString()+(handlers==null?"[]":Arrays.asList(getHandlers()).toString());
+ }
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
-import org.eclipse.jetty.util.component.LifeCycle;
/* ------------------------------------------------------------ */
/** A <code>HandlerWrapper</code> acts as a {@link Handler} but delegates the {@link Handler#handle handle} method and
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpConnection;
import org.eclipse.jetty.server.Request;
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
- HttpConnection connection = HttpConnection.getCurrentConnection();
- final EndPoint endp = connection==null?null:connection.getEndPoint();
-
- final long idle_timeout;
- if (endp==null)
- idle_timeout=-1;
- else
- {
- idle_timeout=endp.getIdleTimeout();
- endp.setIdleTimeout(_idleTimeoutMs);
- }
+ final HttpChannel<?> channel = baseRequest.getHttpChannel();
+ final long idle_timeout=baseRequest.getHttpChannel().getIdleTimeout();
+ channel.setIdleTimeout(_idleTimeoutMs);
try
{
}
finally
{
- if (endp!=null)
- {
if (_applyToAsync && request.isAsyncStarted())
{
request.getAsyncContext().addListener(new AsyncListener()
@Override
public void onError(AsyncEvent event) throws IOException
{
- endp.setIdleTimeout(idle_timeout);
+ channel.setIdleTimeout(idle_timeout);
}
@Override
public void onComplete(AsyncEvent event) throws IOException
{
- endp.setIdleTimeout(idle_timeout);
+ channel.setIdleTimeout(idle_timeout);
}
});
}
else
- endp.setIdleTimeout(idle_timeout);
- }
+ channel.setIdleTimeout(idle_timeout);
}
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
@Override
public void onError(AsyncEvent event) throws IOException
{
+ HttpServletResponse response = (HttpServletResponse)event.getAsyncContext().getResponse();
+ if (!response.isCommitted())
+ response.setStatus(500);
}
{
super.handle(target, baseRequest, request, response);
}
+ catch(Error|IOException|ServletException|RuntimeException e)
+ {
+ if (!response.isCommitted() && !baseRequest.getHttpChannelState().isAsync())
+ response.setStatus(500);
+ throw e;
+ }
finally
{
if (_requestLog != null && baseRequest.getDispatcherType().equals(DispatcherType.REQUEST))
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
String _cacheControl;
boolean _directory;
boolean _etags;
- int _minMemoryMappedContentLength=-1;
+ int _minMemoryMappedContentLength=1024;
int _minAsyncContentLength=0;
/* ------------------------------------------------------------ */
*/
public Resource getStylesheet()
{
- if(_stylesheet != null)
- {
- return _stylesheet;
- }
- else
- {
- if(_defaultStylesheet == null)
- {
- _defaultStylesheet = Resource.newResource(this.getClass().getResource("/jetty-dir.css"));
- }
- return _defaultStylesheet;
- }
+ if(_stylesheet != null)
+ {
+ return _stylesheet;
+ }
+ else
+ {
+ if(_defaultStylesheet == null)
+ {
+ _defaultStylesheet = Resource.newResource(this.getClass().getResource("/jetty-dir.css"));
+ }
+ return _defaultStylesheet;
+ }
}
/* ------------------------------------------------------------ */
_stylesheet = null;
}
}
- catch(Exception e)
- {
- LOG.warn(e.toString());
- LOG.debug(e);
- throw new IllegalArgumentException(stylesheet);
- }
+ catch(Exception e)
+ {
+ LOG.warn(e.toString());
+ LOG.debug(e);
+ throw new IllegalArgumentException(stylesheet);
+ }
}
/* ------------------------------------------------------------ */
if (path==null || !path.startsWith("/"))
throw new MalformedURLException(path);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} getResource({})",_context==null?_baseResource:_context,_baseResource,path);
+
Resource base = _baseResource;
if (base==null)
{
if (_context==null)
return null;
- base=_context.getBaseResource();
- if (base==null)
- return null;
+ return _context.getResource(path);
}
try
{
path=URIUtil.canonicalPath(path);
- return base.addPath(path);
+ Resource r = base.addPath(path);
+
+ if (r!=null && r.getAlias()!=null && (_context==null || !_context.checkAlias(path, r)))
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("resource={} alias={}",r,r.getAlias());
+ return null;
+ }
+ return r;
}
catch(Exception e)
{
}
Resource resource = getResource(request);
+
+ if (LOG.isDebugEnabled())
+ {
+ if (resource==null)
+ LOG.debug("resource=null");
+ else
+ LOG.debug("resource={} alias={} exists={}",resource,resource.getAlias(),resource.exists());
+ }
+
+
// If resource is not found
if (resource==null || !resource.exists())
{
// handle directories
if (resource.isDirectory())
{
- if (!request.getPathInfo().endsWith(URIUtil.SLASH))
+ String pathInfo = request.getPathInfo();
+ boolean endsWithSlash=(pathInfo==null?request.getServletPath():pathInfo).endsWith(URIUtil.SLASH);
+ if (!endsWithSlash)
{
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getRequestURI(),URIUtil.SLASH)));
return;
resource.length()>=min_async_size)
{
final AsyncContext async = request.startAsync();
+ async.setTimeout(0);
Callback callback = new Callback()
{
@Override
// Can we use a memory mapped file?
if (_minMemoryMappedContentLength>0 &&
resource.length()>_minMemoryMappedContentLength &&
+ resource.length()<Integer.MAX_VALUE &&
resource instanceof FileResource)
{
ByteBuffer buffer = BufferUtil.toMappedBuffer(resource.getFile());
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.handler;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnection;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.util.URIUtil;
+
+/**
+ * Secured Redirect Handler
+ * <p>
+ * Using information present in the {@link HttpConfiguration}, will attempt to redirect to the {@link HttpConfiguration#getSecureScheme()} and
+ * {@link HttpConfiguration#getSecurePort()} for any request that {@link HttpServletRequest#isSecure()} == false.
+ */
+public class SecuredRedirectHandler extends AbstractHandler
+{
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ HttpChannel<?> channel = baseRequest.getHttpChannel();
+ if (baseRequest.isSecure() || (channel == null))
+ {
+ // nothing to do
+ return;
+ }
+
+ HttpConfiguration httpConfig = channel.getHttpConfiguration();
+ if (httpConfig == null)
+ {
+ // no config, show error
+ response.sendError(HttpStatus.FORBIDDEN_403,"No http configuration available");
+ return;
+ }
+
+ if (httpConfig.getSecurePort() > 0)
+ {
+ String scheme = httpConfig.getSecureScheme();
+ int port = httpConfig.getSecurePort();
+
+ String url = URIUtil.newURI(scheme,baseRequest.getServerName(),port,baseRequest.getRequestURI(),baseRequest.getQueryString());
+ response.setContentLength(0);
+ response.sendRedirect(url);
+ }
+ else
+ {
+ response.sendError(HttpStatus.FORBIDDEN_403,"Not Secure");
+ }
+
+ baseRequest.setHandled(true);
+ }
+}
\ No newline at end of file
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
/* ------------------------------------------------------------ */
/**
- * A handler that shuts the server down on a valid request. Used to do "soft" restarts from Java. If _exitJvm ist set to true a hard System.exit() call is being
- * made.
+ * A handler that shuts the server down on a valid request. Used to do "soft" restarts from Java.
+ * If _exitJvm is set to true a hard System.exit() call is being made.
+ * If _sendShutdownAtStart is set to true, starting the server will try to shut down an existing server at the same port.
+ * If _sendShutdownAtStart is set to true, make a http call to
+ * "http://localhost:" + port + "/shutdown?token=" + shutdownCookie
+ * in order to shut down the server.
*
* This handler is a contribution from Johannes Brodwall: https://bugs.eclipse.org/bugs/show_bug.cgi?id=357687
*
Server server = new Server(8080);
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[]
- { someOtherHandler, new ShutdownHandler("secret password") });
+ { someOtherHandler, new ShutdownHandler("secret password", false, true) });
server.setHandler(handlers);
server.start();
</pre>
/**
* @param shutdownToken
+ * a secret password to avoid unauthorized shutdown attempts
+ * @param exitJVM If true, when the shutdown is executed, the handler class System.exit()
* @param sendShutdownAtStart If true, a shutdown is sent as a HTTP post
- * during startup, which will shutdown any previously running instances of
- * this server with an identically configured ShutdownHandler
+ * during startup, which will shutdown any previously running instances of
+ * this server with an identically configured ShutdownHandler
*/
public ShutdownHandler(String shutdownToken, boolean exitJVM, boolean sendShutdownAtStart)
{
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod("POST");
connection.getResponseCode();
- LOG.info("Shutting down " + url + ": " + connection.getResponseMessage());
+ LOG.info("Shutting down " + url + ": " + connection.getResponseCode() + " " + connection.getResponseMessage());
}
catch (SocketException e)
{
}
}
+ @SuppressWarnings("resource")
private String getServerUrl()
{
NetworkConnector connector=null;
}
LOG.info("Shutting down by request from " + request.getRemoteAddr());
+ doShutdown(baseRequest, response);
+ }
+
+ protected void doShutdown(Request baseRequest, HttpServletResponse response) throws IOException {
+ for (Connector connector : getServer().getConnectors()) {
+ connector.shutdown();
+ }
+
+ response.sendError(200, "Connectors closed, commencing full shutdown");
+ baseRequest.setHandled(true);
final Server server=getServer();
new Thread()
private boolean hasCorrectSecurityToken(HttpServletRequest request)
{
String tok = request.getParameter("token");
- LOG.debug("Token: {}", tok);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Token: {}", tok);
return _shutdownToken.equals(tok);
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
}
}
- private void updateResponse(Request request)
+ protected void updateResponse(Request request)
{
Response response = request.getResponse();
- switch (response.getStatus() / 100)
+ if (request.isHandled())
{
- case 0:
- if (request.isHandled())
+ switch (response.getStatus() / 100)
+ {
+ case 1:
+ _responses1xx.incrementAndGet();
+ break;
+ case 2:
_responses2xx.incrementAndGet();
- else
+ break;
+ case 3:
+ _responses3xx.incrementAndGet();
+ break;
+ case 4:
_responses4xx.incrementAndGet();
- break;
- case 1:
- _responses1xx.incrementAndGet();
- break;
- case 2:
- _responses2xx.incrementAndGet();
- break;
- case 3:
- _responses3xx.incrementAndGet();
- break;
- case 4:
- _responses4xx.incrementAndGet();
- break;
- case 5:
- _responses5xx.incrementAndGet();
- break;
- default:
- break;
+ break;
+ case 5:
+ _responses5xx.incrementAndGet();
+ break;
+ default:
+ break;
+ }
}
+ else
+ // will fall through to not found handler
+ _responses4xx.incrementAndGet();
_responsesTotalBytes.addAndGet(response.getContentCount());
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.server.session;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
-import java.util.List;
import java.util.Map;
import java.util.Set;
-
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSessionActivationListener;
{
try
{
- LOG.debug("invalidate {}",_clusterId);
+ if (LOG.isDebugEnabled())
+ LOG.debug("invalidate {}",_clusterId);
if (isValid())
clearAttributes();
}
}
/* ------------------------------------------------------------ */
- @SuppressWarnings({ "unchecked" })
@Override
public Enumeration<String> getAttributeNames()
{
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
// random chance to reseed
if (_reseed>0 && (r0%_reseed)== 1L)
{
- LOG.debug("Reseeding {}",this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Reseeding {}",this);
if (_random instanceof SecureRandom)
{
SecureRandom secure = (SecureRandom)_random;
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
_context=ContextHandler.getCurrentContext();
_loader=Thread.currentThread().getContextClassLoader();
- if (_sessionIdManager==null)
+ final Server server=getSessionHandler().getServer();
+ synchronized (server)
{
- final Server server=getSessionHandler().getServer();
- synchronized (server)
+ if (_sessionIdManager==null)
{
_sessionIdManager=server.getSessionIdManager();
if (_sessionIdManager==null)
_sessionAttributeListeners.remove(listener);
if (listener instanceof HttpSessionListener)
_sessionListeners.remove(listener);
+ if (listener instanceof HttpSessionIdListener)
+ _sessionIdListeners.remove(listener);
+ removeBean(listener);
}
/* ------------------------------------------------------------ */
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
-import org.eclipse.jetty.server.SessionIdManager;
-
/* ------------------------------------------------------------ */
/**
* HashSessionIdManager. An in-memory implementation of the session ID manager.
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.server.session;
import java.io.DataInputStream;
-import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
-import org.eclipse.jetty.server.SessionIdManager;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
-import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
import org.eclipse.jetty.util.thread.Scheduler;
}
finally
{
- if (_timer != null && _timer.isRunning())
- _timer.schedule(this, _scavengePeriodMs, TimeUnit.MILLISECONDS);
+ if (_timer != null && _timer.isRunning()) {
+ _task = _timer.schedule(this, _scavengePeriodMs, TimeUnit.MILLISECONDS);
+ }
}
}
}
finally
{
if (_timer != null && _timer.isRunning())
- _timer.schedule(this, _savePeriodMs, TimeUnit.MILLISECONDS);
+ _saveTask = _timer.schedule(this, _savePeriodMs, TimeUnit.MILLISECONDS);
}
}
}
ServletContext context = ContextHandler.getCurrentContext();
if (context!=null)
_timer = (Scheduler)context.getAttribute("org.eclipse.jetty.server.session.timer");
- }
+ }
if (_timer == null)
{
}
else
addBean(_timer,false);
-
+
super.doStart();
setScavengePeriod(getScavengePeriod());
{
if (_saveTask!=null)
_saveTask.cancel();
+
_saveTask=null;
if (_task!=null)
_task.cancel();
+
_task=null;
_timer=null;
}
+
// This will callback invalidate sessions - where we decide if we will save
super.doStop();
_scavengePeriodMs=period;
- if (_timer!=null && (period!=old_period || _task==null))
+ synchronized (this)
{
- synchronized (this)
+ if (_timer!=null && (period!=old_period || _task==null))
{
if (_task!=null)
{
_task.cancel();
_task = null;
}
+
_task = _timer.schedule(new Scavenger(),_scavengePeriodMs, TimeUnit.MILLISECONDS);
}
}
{
File file = new File(_storeDir,idInCuster);
- FileInputStream in = null;
Exception error = null;
- try
+ if (!file.exists())
{
- if (file.exists())
+ if (LOG.isDebugEnabled())
{
- in = new FileInputStream(file);
- HashedSession session = restoreSession(in, null);
- addSession(session, false);
- session.didActivate();
- return session;
+ LOG.debug("Not loading: {}",file);
}
+ return null;
+ }
+
+ try (FileInputStream in = new FileInputStream(file))
+ {
+ HashedSession session = restoreSession(in,null);
+ addSession(session,false);
+ session.didActivate();
+ return session;
}
catch (Exception e)
{
}
finally
{
- if (in != null) IO.close(in);
-
if (error != null)
{
if (isDeleteUnrestorableSessions() && file.exists() && file.getParentFile().equals(_storeDir) )
}
}
else
- file.delete(); //delete successfully restored file
+ {
+ // delete successfully restored file
+ file.delete();
+ }
}
return null;
}
if (session == null)
session = (HashedSession)newSession(created, accessed, clusterId);
+
session.setRequests(requests);
+ // Attributes
int size = di.readInt();
restoreSessionAttributes(di, size, session);
int maxIdle = di.readInt();
session.setMaxInactiveInterval(maxIdle);
}
- catch (EOFException e)
+ catch (IOException e)
{
LOG.debug("No maxInactiveInterval persisted for session "+clusterId);
LOG.ignore(e);
return session;
}
-
+
+ @SuppressWarnings("resource")
private void restoreSessionAttributes (InputStream is, int size, HashedSession session)
throws Exception
{
if (size>0)
{
+ // input stream should not be closed here
ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is);
for (int i=0; i<size;i++)
{
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
throws Exception
{
File file = null;
- FileOutputStream fos = null;
if (!_saveFailed && _hashSessionManager._storeDir != null)
{
- try
+ file = new File(_hashSessionManager._storeDir, super.getId());
+ if (file.exists())
+ {
+ file.delete();
+ }
+
+ try(FileOutputStream fos = new FileOutputStream(file,false))
{
- file = new File(_hashSessionManager._storeDir, super.getId());
- if (file.exists())
- file.delete();
- file.createNewFile();
- fos = new FileOutputStream(file);
save(fos);
- IO.close(fos);
}
catch (Exception e)
{
saveFailed(); // We won't try again for this session
- if (fos != null) IO.close(fos);
- if (file != null) file.delete(); // No point keeping the file if we didn't save the whole session
+ if (file != null)
+ file.delete(); // No point keeping the file if we didn't save the whole session
throw e;
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
if (_dbAdaptor == null)
throw new IllegalStateException ("No DBAdaptor");
String longType = _dbAdaptor.getLongType();
- return "alter table "+getTableName()+" add "+getMaxIntervalColumn()+" "+longType+" not null default "+MAX_INTERVAL_NOT_SET;
+ String stem = "alter table "+getTableName()+" add "+getMaxIntervalColumn()+" "+longType;
+ if (_dbAdaptor.getDBName().contains("oracle"))
+ return stem + " default "+ MAX_INTERVAL_NOT_SET + " not null";
+ else
+ return stem +" not null default "+ MAX_INTERVAL_NOT_SET;
}
private void checkNotNull(String s)
throws SQLException
{
_dbName = dbMeta.getDatabaseProductName().toLowerCase(Locale.ENGLISH);
- LOG.debug ("Using database {}",_dbName);
+ if (LOG.isDebugEnabled())
+ LOG.debug ("Using database {}",_dbName);
_isLower = dbMeta.storesLowerCaseIdentifiers();
_isUpper = dbMeta.storesUpperCaseIdentifiers();
}
finally
{
if (_scheduler != null && _scheduler.isRunning())
- _scheduler.schedule(this, _scavengeIntervalMs, TimeUnit.MILLISECONDS);
+ _task = _scheduler.schedule(this, _scavengeIntervalMs, TimeUnit.MILLISECONDS);
}
}
}
if (LOG.isDebugEnabled())
LOG.debug("Scavenging every "+_scavengeIntervalMs+" ms");
- //if (_timer!=null && (period!=old_period || _task==null))
- if (_scheduler != null && (period!=old_period || _task==null))
+ synchronized (this)
{
- synchronized (this)
+ //if (_timer!=null && (period!=old_period || _task==null))
+ if (_scheduler != null && (period!=old_period || _task==null))
{
if (_task!=null)
_task.cancel();
_ownScheduler = true;
_scheduler.start();
}
+ else if (!_scheduler.isStarted())
+ throw new IllegalStateException("Shared scheduler not started");
setScavengeInterval(getScavengeInterval());
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSessionEvent;
-import javax.servlet.http.HttpSessionListener;
-
-import org.eclipse.jetty.server.SessionIdManager;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.session.JDBCSessionIdManager.SessionTableSchema;
import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
{
if (memSession==null)
{
- LOG.debug("getSession("+idInCluster+"): no session in session map. Reloading session data from db.");
+ if (LOG.isDebugEnabled())
+ LOG.debug("getSession("+idInCluster+"): no session in session map. Reloading session data from db.");
session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
}
else if ((now - memSession._lastSaved) >= (_saveIntervalSec * 1000L))
{
- LOG.debug("getSession("+idInCluster+"): stale session. Reloading session data from db.");
+ if (LOG.isDebugEnabled())
+ LOG.debug("getSession("+idInCluster+"): stale session. Reloading session data from db.");
session = loadSession(idInCluster, canonicalize(_context.getContextPath()), getVirtualHost(_context));
}
else
{
- LOG.debug("getSession("+idInCluster+"): session in session map");
+ if (LOG.isDebugEnabled())
+ LOG.debug("getSession("+idInCluster+"): session in session map");
session = memSession;
}
}
}
else
{
- LOG.debug("getSession ({}): Session has expired", idInCluster);
+ if (LOG.isDebugEnabled())
+ LOG.debug("getSession ({}): Session has expired", idInCluster);
//ensure that the session id for the expired session is deleted so that a new session with the
//same id cannot be created (because the idInUse() test would succeed)
_jdbcSessionIdMgr.removeSession(idInCluster);
{
//the session loaded from the db and the one in memory are the same, so keep using the one in memory
session = memSession;
- LOG.debug("getSession({}): Session not stale {}", idInCluster,session);
+ if (LOG.isDebugEnabled())
+ LOG.debug("getSession({}): Session not stale {}", idInCluster,session);
}
}
else
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session");
public final static EnumSet<SessionTrackingMode> DEFAULT_TRACKING = EnumSet.of(SessionTrackingMode.COOKIE,SessionTrackingMode.URL);
-
- public static final Class[] SESSION_LISTENER_TYPES = new Class[] {HttpSessionAttributeListener.class,
- HttpSessionIdListener.class,
- HttpSessionListener.class};
+
+ @SuppressWarnings("unchecked")
+ public static final Class<? extends EventListener>[] SESSION_LISTENER_TYPES =
+ new Class[] {HttpSessionAttributeListener.class,
+ HttpSessionIdListener.class,
+ HttpSessionListener.class};
requested_session_id = cookies[i].getValue();
requested_session_id_from_cookie = true;
- LOG.debug("Got Session ID {} from cookie",requested_session_id);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Got Session ID {} from cookie",requested_session_id);
if (requested_session_id != null)
{
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
+import java.util.StringTokenizer;
import javax.servlet.AsyncContext;
import javax.servlet.RequestDispatcher;
/* ------------------------------------------------------------ */
/** The default servlet.
+ *
* This servlet, normally mapped to /, provides the handling for static
* content, OPTION and TRACE methods for the context.
* The following initParameters are supported, these can be set either
* If set to true, it will use mapped file buffer to serve static content
* when using NIO connector. Setting this value to false means that
* a direct buffer will be used instead of a mapped file buffer.
- * By default, this is set to true.
+ * This is set to false by default by this class, but may be overridden
+ * by eg webdefault.xml
*
* cacheControl If set, all static content will have this value set as the cache-control
* header.
+ *
+ * otherGzipFileExtensions
+ * Other file extensions that signify that a file is gzip compressed. Eg ".svgz"
*
*
* </PRE>
private String _relativeResourceBase;
private ServletHandler _servletHandler;
private ServletHolder _defaultHolder;
+ private List<String> _gzipEquivalentFileExtensions;
/* ------------------------------------------------------------ */
@Override
throw new UnavailableException("resourceCache specified with resource bases");
_cache=(ResourceCache)_servletContext.getAttribute(resourceCache);
- LOG.debug("Cache {}={}",resourceCache,_cache);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Cache {}={}",resourceCache,_cache);
}
_etags = getInitBoolean("etags",_etags);
try
{
- if (_cache==null && max_cached_files>0)
+ if (_cache==null && (max_cached_files!=-2 || max_cache_size!=-2 || max_cached_file_size!=-2))
{
_cache= new ResourceCache(null,this,_mimeTypes,_useFileMappedBuffer,_etags);
- if (max_cache_size>0)
+ if (max_cache_size>=0)
_cache.setMaxCacheSize(max_cache_size);
if (max_cached_file_size>=-1)
_cache.setMaxCachedFileSize(max_cached_file_size);
LOG.warn(Log.EXCEPTION,e);
throw new UnavailableException(e.toString());
}
-
- _servletHandler= _contextHandler.getChildHandlerByClass(ServletHandler.class);
- for (ServletHolder h :_servletHandler.getServlets())
- if (h.getServletInstance()==this)
- _defaultHolder=h;
-
- if (LOG.isDebugEnabled())
- LOG.debug("resource base = "+_resourceBase);
+ _gzipEquivalentFileExtensions = new ArrayList<String>();
+ String otherGzipExtensions = getInitParameter("otherGzipFileExtensions");
+ if (otherGzipExtensions != null)
+ {
+ //comma separated list
+ StringTokenizer tok = new StringTokenizer(otherGzipExtensions,",",false);
+ while (tok.hasMoreTokens())
+ {
+ String s = tok.nextToken().trim();
+ _gzipEquivalentFileExtensions.add((s.charAt(0)=='.'?s:"."+s));
+ }
+ }
+ else
+ {
+ //.svgz files are gzipped svg files and must be served with Content-Encoding:gzip
+ _gzipEquivalentFileExtensions.add(".svgz");
+ }
+
+ _servletHandler= _contextHandler.getChildHandlerByClass(ServletHandler.class);
+ for (ServletHolder h :_servletHandler.getServlets())
+ if (h.getServletInstance()==this)
+ _defaultHolder=h;
+
+
+ if (LOG.isDebugEnabled())
+ LOG.debug("resource base = "+_resourceBase);
}
/**
// Find the resource and content
Resource resource=null;
HttpContent content=null;
+ boolean close_content=true;
try
{
// is gzip enabled?
}
if (LOG.isDebugEnabled())
- LOG.debug("uri="+request.getRequestURI()+" resource="+resource+(content!=null?" content":""));
+ LOG.debug(String.format("uri=%s, resource=%s, content=%s",request.getRequestURI(),resource,content));
// Handle resource
if (resource==null || !resource.exists())
if (included.booleanValue() || passConditionalHeaders(request,response, resource,content))
{
- if (gzip)
+ if (gzip || isGzippedContent(pathInContext))
{
response.setHeader(HttpHeader.CONTENT_ENCODING.asString(),"gzip");
String mt=_servletContext.getMimeType(pathInContext);
if (mt!=null)
response.setContentType(mt);
}
- sendData(request,response,included.booleanValue(),resource,content,reqRanges);
+ close_content=sendData(request,response,included.booleanValue(),resource,content,reqRanges);
}
}
}
// else look for a welcome file
else if (null!=(welcome=getWelcomeFile(pathInContext)))
{
- LOG.debug("welcome={}",welcome);
+ if (LOG.isDebugEnabled())
+ LOG.debug("welcome={}",welcome);
if (_redirectWelcome)
{
// Redirect to the index
}
finally
{
- if (content!=null)
- content.release();
- else if (resource!=null)
- resource.close();
+ if (close_content)
+ {
+ if (content!=null)
+ content.release();
+ else if (resource!=null)
+ resource.close();
+ }
}
}
+ /**
+ * @param resource
+ * @return
+ */
+ protected boolean isGzippedContent(String path)
+ {
+ if (path == null) return false;
+
+ for (String suffix:_gzipEquivalentFileExtensions)
+ if (path.endsWith(suffix))
+ return true;
+ return false;
+ }
+
/* ------------------------------------------------------------ */
private boolean hasDefinedRange(Enumeration<String> reqRanges)
{
}
/* ------------------------------------------------------------ */
- protected void sendData(HttpServletRequest request,
+ protected boolean sendData(HttpServletRequest request,
HttpServletResponse response,
boolean include,
Resource resource,
- HttpContent content,
+ final HttpContent content,
Enumeration<String> reqRanges)
throws IOException
{
final long content_length = (content==null)?resource.length():content.getContentLength();
-
+
// Get the output stream (or writer)
OutputStream out =null;
boolean written;
out = new WriterOutputStream(response.getWriter());
written=true; // there may be data in writer buffer, so assume written
}
+
+ if (LOG.isDebugEnabled())
+ LOG.debug(String.format("sendData content=%s out=%s async=%b",content,out,request.isAsyncSupported()));
if ( reqRanges == null || !reqRanges.hasMoreElements() || content_length<0)
{
if (request.isAsyncSupported())
{
final AsyncContext context = request.startAsync();
+ context.setTimeout(0);
((HttpOutput)out).sendContent(content,new Callback()
{
public void succeeded()
{
context.complete();
+ content.release();
}
@Override
else
LOG.warn(x);
context.complete();
+ content.release();
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("DefaultServlet@%x$CB", DefaultServlet.this.hashCode());
}
});
+ return false;
}
// otherwise write content blocking
- else
- {
- ((HttpOutput)out).sendContent(content);
- }
+ ((HttpOutput)out).sendContent(content);
+
}
}
else
response.setHeader(HttpHeader.CONTENT_RANGE.asString(),
InclusiveByteRange.to416HeaderRangeString(content_length));
resource.writeTo(out,0,content_length);
- return;
+ return true;
}
// if there is only a single valid range (must be satisfiable
response.setHeader(HttpHeader.CONTENT_RANGE.asString(),
singleSatisfiableRange.toHeaderRangeString(content_length));
resource.writeTo(out,singleSatisfiableRange.getFirst(content_length),singleLength);
- return;
+ return true;
}
// multiple non-overlapping valid ranges cause a multipart
in.close();
multi.close();
}
- return;
+ return true;
}
/* ------------------------------------------------------------ */
protected void writeHeaders(HttpServletResponse response,HttpContent content,long count)
- {
+ {
+ if (content == null)
+ {
+ // No content, then no headers to process
+ // This is possible during bypass write because of wrapping
+ // See .sendData() for more details.
+ return;
+ }
+
if (content.getContentType()!=null && response.getContentType()==null)
response.setContentType(content.getContentType().toString());
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
}
_config=new Config();
- LOG.debug("Filter.init {}",_filter);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Filter.init {}",_filter);
_filter.init(_config);
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import javax.servlet.DispatcherType;
import org.eclipse.jetty.http.PathMap;
-import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
*/
public static int dispatch(DispatcherType type)
{
- switch(type)
- {
- case REQUEST:
- return REQUEST;
- case ASYNC:
- return ASYNC;
- case FORWARD:
- return FORWARD;
- case INCLUDE:
- return INCLUDE;
- case ERROR:
- return ERROR;
- }
+ switch(type)
+ {
+ case REQUEST:
+ return REQUEST;
+ case ASYNC:
+ return ASYNC;
+ case FORWARD:
+ return FORWARD;
+ case INCLUDE:
+ return INCLUDE;
+ case ERROR:
+ return ERROR;
+ }
throw new IllegalArgumentException(type.toString());
}
*/
boolean appliesTo(int type)
{
- if (_dispatches==0)
- return type==REQUEST || type==ASYNC && _holder.isAsyncSupported();
+ if (_dispatches==0)
+ return type==REQUEST || type==ASYNC && _holder.isAsyncSupported();
return (_dispatches&type)!=0;
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
}
/* ------------------------------------------------------------ */
- public Enumeration getInitParameterNames()
+ public Enumeration<String> getInitParameterNames()
{
if (_initParams==null)
return Collections.enumeration(Collections.EMPTY_LIST);
}
/* -------------------------------------------------------- */
- public Enumeration getInitParameterNames()
+ public Enumeration<String> getInitParameterNames()
{
return Holder.this.getInitParameterNames();
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
private ContextHandler _contextHandler;
private ServletHandler _servletHandler;
- private Map.Entry _invokerEntry;
- private Map _parameters;
+ private Map.Entry<String, ServletHolder> _invokerEntry;
+ private Map<String, String> _parameters;
private boolean _nonContextServlets;
private boolean _verbose;
while (handler!=null && !(handler instanceof ServletHandler) && (handler instanceof HandlerWrapper))
handler=((HandlerWrapper)handler).getHandler();
_servletHandler = (ServletHandler)handler;
- Enumeration e = getInitParameterNames();
+ Enumeration<String> e = getInitParameterNames();
while(e.hasMoreElements())
{
- String param=(String)e.nextElement();
+ String param=e.nextElement();
String value=getInitParameter(param);
String lvalue=value.toLowerCase(Locale.ENGLISH);
if ("nonContextServlets".equals(param))
else
{
if (_parameters==null)
- _parameters=new HashMap();
+ _parameters=new HashMap<String, String>();
_parameters.put(param,value);
}
}
/* ------------------------------------------------------------ */
protected void service(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException
+ throws ServletException, IOException
{
// Get the requested path and info
boolean included=false;
ServletMapping mapping = new ServletMapping();
mapping.setServletName(servlet);
mapping.setPathSpec(URIUtil.addPaths(servlet_path,servlet)+"/*");
- _servletHandler.setServletMappings((ServletMapping[])ArrayUtil.addToArray(_servletHandler.getServletMappings(), mapping, ServletMapping.class));
+ _servletHandler.setServletMappings(ArrayUtil.addToArray(_servletHandler.getServletMappings(), mapping, ServletMapping.class));
}
else
{
// Check for existing mapping (avoid threaded race).
String path=URIUtil.addPaths(servlet_path,servlet);
- Map.Entry entry = _servletHandler.getHolderEntry(path);
+ Map.Entry<String, ServletHolder> entry = _servletHandler.getHolderEntry(path);
if (entry!=null && !entry.equals(_invokerEntry))
{
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.servlet;
import java.io.IOException;
+import java.util.Locale;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
/* ------------------------------------------------------------ */
/** Servlet handling JSP Property Group mappings
* <p>
- * This servlet is mapped to by any URL pattern for a JSP property group.
+ * This servlet is mapped to by any URL pattern for a JSP property group.
* Resources handled by this servlet that are not directories will be passed
- * directly to the JSP servlet. Resources that are directories will be
+ * directly to the JSP servlet. Resources that are directories will be
* passed directly to the default servlet.
*/
public class JspPropertyGroupServlet extends GenericServlet
{
private static final long serialVersionUID = 3681783214726776945L;
-
+
public final static String NAME = "__org.eclipse.jetty.servlet.JspPropertyGroupServlet__";
private final ServletHandler _servletHandler;
private final ContextHandler _contextHandler;
private ServletHolder _dftServlet;
private ServletHolder _jspServlet;
private boolean _starJspMapped;
-
+
public JspPropertyGroupServlet(ContextHandler context, ServletHandler servletHandler)
{
_contextHandler=context;
- _servletHandler=servletHandler;
+ _servletHandler=servletHandler;
}
-
+
@Override
public void init() throws ServletException
- {
+ {
String jsp_name = "jsp";
ServletMapping servlet_mapping =_servletHandler.getServletMapping("*.jsp");
if (servlet_mapping!=null)
{
_starJspMapped=true;
-
+
//now find the jsp servlet, ignoring the mapping that is for ourself
ServletMapping[] mappings = _servletHandler.getServletMappings();
for (ServletMapping m:mappings)
}
}
}
-
+
jsp_name=servlet_mapping.getServletName();
}
_jspServlet=_servletHandler.getServlet(jsp_name);
-
+
String dft_name="default";
ServletMapping default_mapping=_servletHandler.getServletMapping("/");
if (default_mapping!=null)
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
- {
+ {
HttpServletRequest request = null;
if (req instanceof HttpServletRequest)
request = (HttpServletRequest)req;
servletPath = request.getServletPath();
pathInfo = request.getPathInfo();
}
-
+
String pathInContext=URIUtil.addPaths(servletPath,pathInfo);
-
+
if (pathInContext.endsWith("/"))
{
_dftServlet.getServlet().service(req,res);
}
- else if (_starJspMapped && pathInContext.toLowerCase().endsWith(".jsp"))
+ else if (_starJspMapped && pathInContext.toLowerCase(Locale.ENGLISH).endsWith(".jsp"))
{
_jspServlet.getServlet().service(req,res);
}
else
{
-
+
Resource resource = _contextHandler.getResource(pathInContext);
if (resource!=null && resource.isDirectory())
_dftServlet.getServlet().service(req,res);
else
_jspServlet.getServlet().service(req,res);
}
-
+
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import javax.servlet.FilterRegistration;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
-import javax.servlet.ServletContainerInitializer;
-import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
decorator.decorate(holder.getListener());
}
}
- }
- }
-
+ }
+ }
+
super.startContext();
// OK to Initialize servlet handler now that all relevant object trees have been started
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
private ServletHolder[] _servlets=new ServletHolder[0];
private ServletMapping[] _servletMappings;
- private Map<String,ServletMapping> _servletPathMappings = new HashMap<String,ServletMapping>();
-
private final Map<String,FilterHolder> _filterNameMap= new HashMap<>();
private List<FilterMapping> _filterPathMappings;
private MultiMap<FilterMapping> _filterNameMappings;
private ListenerHolder[] _listeners=new ListenerHolder[0];
- protected final ConcurrentMap<?, ?> _chainCache[] = new ConcurrentMap[FilterMapping.ALL];
- protected final Queue<?>[] _chainLRU = new Queue[FilterMapping.ALL];
-
+ @SuppressWarnings("unchecked")
+ protected final ConcurrentMap<String, FilterChain> _chainCache[] = new ConcurrentMap[FilterMapping.ALL];
+
+ @SuppressWarnings("unchecked")
+ protected final Queue<String>[] _chainLRU = new Queue[FilterMapping.ALL];
+
/* ------------------------------------------------------------ */
if (getServletMapping("/")==null && _ensureDefaultServlet)
{
- LOG.debug("Adding Default404Servlet to {}",this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Adding Default404Servlet to {}",this);
addServletWithMapping(Default404Servlet.class,"/");
updateMappings();
getServletMapping("/").setDefault(true);
_filterPathMappings=null;
_filterNameMappings=null;
_servletPathMap=null;
- _servletPathMappings=null;
}
/* ------------------------------------------------------------ */
*/
public ServletMapping getServletMapping(String pathSpec)
{
- if (pathSpec == null || _servletPathMappings == null)
+ if (pathSpec == null || _servletMappings == null)
return null;
- return _servletPathMappings.get(pathSpec);
+ ServletMapping mapping = null;
+ for (int i=0; i<_servletMappings.length && mapping == null; i++)
+ {
+ ServletMapping m = _servletMappings[i];
+ if (m.getPathSpecs() != null)
+ {
+ for (String p:m.getPathSpecs())
+ {
+ if (pathSpec.equals(p))
+ {
+ mapping = m;
+ break;
+ }
+ }
+ }
+ }
+ return mapping;
}
/* ------------------------------------------------------------ */
}
}
- LOG.debug("chain={}",chain);
+ if (LOG.isDebugEnabled())
+ LOG.debug("chain={}",chain);
+
Throwable th=null;
try
{
res = ((ServletResponseHttpWrapper)res).getResponse();
// Do the filter/handling thang
+ servlet_holder.prepare(baseRequest, req, res);
+
if (chain!=null)
chain.doFilter(req, res);
else
if (filters.size() > 0)
chain= new CachedChain(filters, servletHolder);
- final Map<String,FilterChain> cache=(Map<String, FilterChain>)_chainCache[dispatch];
- final Queue<String> lru=(Queue<String>)_chainLRU[dispatch];
-
- // Do we have too many cached chains?
- while (_maxFilterChainsCacheSize>0 && cache.size()>=_maxFilterChainsCacheSize)
- {
- // The LRU list is not atomic with the cache map, so be prepared to invalidate if
- // a key is not found to delete.
- // Delete by LRU (where U==created)
- String k=lru.poll();
- if (k==null)
- {
- cache.clear();
- break;
- }
- cache.remove(k);
- }
-
- cache.put(key,chain);
- lru.add(key);
+ final Map<String,FilterChain> cache=_chainCache[dispatch];
+ final Queue<String> lru=_chainLRU[dispatch];
+
+ // Do we have too many cached chains?
+ while (_maxFilterChainsCacheSize>0 && cache.size()>=_maxFilterChainsCacheSize)
+ {
+ // The LRU list is not atomic with the cache map, so be prepared to invalidate if
+ // a key is not found to delete.
+ // Delete by LRU (where U==created)
+ String k=lru.poll();
+ if (k==null)
+ {
+ cache.clear();
+ break;
+ }
+ cache.remove(k);
+ }
+
+ cache.put(key,chain);
+ lru.add(key);
}
else if (filters.size() > 0)
chain = new Chain(baseRequest,filters, servletHolder);
{
try
{
- /* if (servlet.getClassName() == null && servlet.getForcedPath() != null)
- {
- ServletHolder forced_holder = _servletPathMap.match(servlet.getForcedPath());
- if (forced_holder == null || forced_holder.getClassName() == null)
- {
- mx.add(new IllegalStateException("No forced path servlet for " + servlet.getForcedPath()));
- continue;
- }
- System.err.println("ServletHandler setting forced path classname to "+forced_holder.getClassName()+ " for "+servlet.getForcedPath());
- servlet.setClassName(forced_holder.getClassName());
- }*/
-
servlet.start();
servlet.initialize();
}
//for each path, look at the mappings where it is referenced
//if a mapping is for a servlet that is not enabled, skip it
Set<ServletMapping> mappings = sms.get(pathSpec);
-
-
-
+
ServletMapping finalMapping = null;
for (ServletMapping mapping : mappings)
{
}
_servletPathMap=pm;
- _servletPathMappings=servletPathMappings;
}
// flush filter chain cache
/* ------------------------------------------------------------ */
protected void notFound(Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
- LOG.debug("Not Found {}",request.getRequestURI());
+ if (LOG.isDebugEnabled())
+ LOG.debug("Not Found {}",request.getRequestURI());
if (getHandler()!=null)
nextHandle(URIUtil.addPaths(request.getServletPath(),request.getPathInfo()),baseRequest,request,response);
}
{
updateBeans(_filterMappings,filterMappings);
_filterMappings = filterMappings;
- updateMappings();
+ if (isStarted()) updateMappings();
invalidateChainsCache();
}
{
updateBeans(_servletMappings,servletMappings);
_servletMappings = servletMappings;
- updateMappings();
+ if (isStarted()) updateMappings();
invalidateChainsCache();
}
// pass to next filter
if (_filterHolder!=null)
{
- LOG.debug("call filter {}", _filterHolder);
+ if (LOG.isDebugEnabled())
+ LOG.debug("call filter {}", _filterHolder);
Filter filter= _filterHolder.getFilter();
- if (_filterHolder.isAsyncSupported())
+
+ //if the request already does not support async, then the setting for the filter
+ //is irrelevant. However if the request supports async but this filter does not
+ //temporarily turn it off for the execution of the filter
+ boolean requestAsyncSupported = baseRequest.isAsyncSupported();
+ try
+ {
+ if (!_filterHolder.isAsyncSupported() && requestAsyncSupported)
+ baseRequest.setAsyncSupported(false);
filter.doFilter(request, response, _next);
- else
+ }
+ finally
{
- final boolean suspendable=baseRequest.isAsyncSupported();
- if (suspendable)
- {
- try
- {
- baseRequest.setAsyncSupported(false);
- filter.doFilter(request, response, _next);
- }
- finally
- {
- baseRequest.setAsyncSupported(true);
- }
- }
- else
- filter.doFilter(request, response, _next);
+ baseRequest.setAsyncSupported(requestAsyncSupported);
}
return;
}
LOG.debug("call filter " + holder);
Filter filter= holder.getFilter();
- if (holder.isAsyncSupported() || !_baseRequest.isAsyncSupported())
+ //if the request already does not support async, then the setting for the filter
+ //is irrelevant. However if the request supports async but this filter does not
+ //temporarily turn it off for the execution of the filter
+ boolean requestAsyncSupported = _baseRequest.isAsyncSupported();
+ try
{
+ if (!holder.isAsyncSupported() && requestAsyncSupported)
+ _baseRequest.setAsyncSupported(false);
filter.doFilter(request, response, this);
}
- else
+ finally
{
- try
- {
- _baseRequest.setAsyncSupported(false);
- filter.doFilter(request, response, this);
- }
- finally
- {
- _baseRequest.setAsyncSupported(true);
- }
+ _baseRequest.setAsyncSupported(requestAsyncSupported);
}
-
return;
}
notFound((request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest(), srequest, (HttpServletResponse)response);
else
{
- LOG.debug("call servlet {}", _servletHolder);
+ if (LOG.isDebugEnabled())
+ LOG.debug("call servlet {}", _servletHolder);
_servletHolder.handle(_baseRequest,request, response);
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.servlet;
+import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
@ManagedObject("Servlet Holder")
public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope, Comparable<ServletHolder>
{
- private static final Logger LOG = Log.getLogger(ServletHolder.class);
/* ---------------------------------------------------------------- */
+ private static final Logger LOG = Log.getLogger(ServletHolder.class);
private int _initOrder = -1;
private boolean _initOnStartup=false;
+ private boolean _initialized = false;
private Map<String, String> _roleMap;
private String _forcedPath;
private String _runAsRole;
private RunAsToken _runAsToken;
private IdentityService _identityService;
private ServletRegistration.Dynamic _registration;
-
+ private JspContainer _jspContainer;
private transient Servlet _servlet;
private transient Config _config;
private transient boolean _enabled = true;
private transient UnavailableException _unavailableEx;
+ public static final String GLASSFISH_SENTINEL_CLASS = "org.glassfish.jsp.api.ResourceInjector";
+ public static final String APACHE_SENTINEL_CLASS = "org.apache.tomcat.InstanceManager";
public static final String JSP_GENERATED_PACKAGE_NAME = "org.eclipse.jetty.servlet.jspPackagePrefix";
public static final Map<String,String> NO_MAPPED_ROLES = Collections.emptyMap();
+ public static enum JspContainer {GLASSFISH, APACHE, OTHER};
/* ---------------------------------------------------------------- */
/** Constructor .
{
// Look for a precompiled JSP Servlet
String precompiled=getClassNameForJsp(_forcedPath);
- LOG.debug("Checking for precompiled servlet {} for jsp {}", precompiled, _forcedPath);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Checking for precompiled servlet {} for jsp {}", precompiled, _forcedPath);
ServletHolder jsp=getServletHandler().getServlet(precompiled);
- if (jsp!=null)
+ if (jsp!=null && jsp.getClassName() != null)
{
- LOG.debug("JSP file {} for {} mapped to Servlet {}",_forcedPath, getName(),jsp.getClassName());
+ if (LOG.isDebugEnabled())
+ LOG.debug("JSP file {} for {} mapped to Servlet {}",_forcedPath, getName(),jsp.getClassName());
// set the className for this servlet to the precompiled one
setClassName(jsp.getClassName());
}
jsp=getServletHandler().getServlet("jsp");
if (jsp!=null)
{
- LOG.debug("JSP file {} for {} mapped to Servlet {}",_forcedPath, getName(),jsp.getClassName());
+ if (LOG.isDebugEnabled())
+ LOG.debug("JSP file {} for {} mapped to Servlet class {}",_forcedPath, getName(),jsp.getClassName());
setClassName(jsp.getClassName());
//copy jsp init params that don't exist for this servlet
for (Map.Entry<String, String> entry:jsp.getInitParameters().entrySet())
if (!_initParams.containsKey(entry.getKey()))
setInitParameter(entry.getKey(), entry.getValue());
}
- //jsp specific: set up the jsp-file on the JspServlet so it can precompile iff load-on-startup is >=0
- if (_initOnStartup)
- setInitParameter("jspFile", _forcedPath);
+ //jsp specific: set up the jsp-file on the JspServlet. If load-on-startup is >=0 and the jsp container supports
+ //precompilation, the jsp will be compiled when this holder is initialized. If not load on startup, or the
+ //container does not support startup precompilation, it will be compiled at runtime when handling a request for this jsp.
+ //See also adaptForcedPathToJspContainer
+ setInitParameter("jspFile", _forcedPath);
}
}
}
if (_class!=null && javax.servlet.SingleThreadModel.class.isAssignableFrom(_class))
_servlet = new SingleThreadedWrapper();
-
+
}
public void initialize ()
throws Exception
{
- super.initialize();
- if (_extInstance || _initOnStartup)
- {
- try
- {
- initServlet();
- }
- catch(Exception e)
+ if(!_initialized){
+ super.initialize();
+ if (_extInstance || _initOnStartup)
{
- if (_servletHandler.isStartWithUnavailable())
- LOG.ignore(e);
- else
- throw e;
+ try
+ {
+ initServlet();
+ }
+ catch(Exception e)
+ {
+ if (_servletHandler.isStartWithUnavailable())
+ LOG.ignore(e);
+ else
+ throw e;
+ }
}
}
+ _initialized = true;
}
_servlet=null;
_config=null;
+ _initialized = false;
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
private void initServlet()
- throws ServletException
+ throws ServletException
{
Object old_run_as = null;
try
if (isJspServlet())
{
initJspServlet();
+ detectJspContainer();
}
initMultiPart();
- LOG.debug("Filter.init {}",_servlet);
+ if (_forcedPath != null && _jspContainer == null)
+ {
+ detectJspContainer();
+ }
+
+ if (LOG.isDebugEnabled())
+ LOG.debug("Servlet.init {} for {}",_servlet,getName());
_servlet.init(_config);
}
catch (UnavailableException e)
if ("?".equals(getInitParameter("classpath")))
{
String classpath = ch.getClassPath();
- LOG.debug("classpath=" + classpath);
+ if (LOG.isDebugEnabled())
+ LOG.debug("classpath=" + classpath);
if (classpath != null)
setInitParameter("classpath", classpath);
}
+
+ /* ensure scratch dir */
+ File scratch = null;
+ if (getInitParameter("scratchdir") == null)
+ {
+ File tmp = (File)getServletHandler().getServletContext().getAttribute(ServletContext.TEMPDIR);
+ scratch = new File(tmp, "jsp");
+ setInitParameter("scratchdir", scratch.getAbsolutePath());
+ }
+
+ scratch = new File (getInitParameter("scratchdir"));
+ if (!scratch.exists()) scratch.mkdir();
}
/* ------------------------------------------------------------ */
{
_runAsRole = role;
}
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Prepare to service a request.
+ *
+ * @param baseRequest
+ * @param request
+ * @param response
+ * @throws ServletException
+ * @throws UnavailableException
+ */
+ protected void prepare (Request baseRequest, ServletRequest request, ServletResponse response)
+ throws ServletException, UnavailableException
+ {
+ ensureInstance();
+ MultipartConfigElement mpce = ((Registration)getRegistration()).getMultipartConfig();
+ if (mpce != null)
+ baseRequest.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce);
+ }
+
+ public synchronized Servlet ensureInstance()
+ throws ServletException, UnavailableException
+ {
+ if (_class==null)
+ throw new UnavailableException("Servlet Not Initialized");
+ Servlet servlet=_servlet;
+ if (!isStarted())
+ throw new UnavailableException("Servlet not initialized", -1);
+ if (_unavailable!=0 || (!_initOnStartup && servlet==null))
+ servlet=getServlet();
+ if (servlet==null)
+ throw new UnavailableException("Could not instantiate "+_class);
+
+ return servlet;
+ }
/* ------------------------------------------------------------ */
/** Service a request with this servlet.
+ * @param baseRequest
+ * @param request
+ * @param response
+ * @throws ServletException
+ * @throws UnavailableException
+ * @throws IOException
*/
public void handle(Request baseRequest,
ServletRequest request,
if (_class==null)
throw new UnavailableException("Servlet Not Initialized");
- Servlet servlet=_servlet;
- synchronized(this)
- {
- if (!isStarted())
- throw new UnavailableException("Servlet not initialized", -1);
- if (_unavailable!=0 || (!_initOnStartup && servlet==null))
- servlet=getServlet();
- if (servlet==null)
- throw new UnavailableException("Could not instantiate "+_class);
- }
+ Servlet servlet = ensureInstance();
// Service the request
boolean servlet_error=true;
{
// Handle aliased path
if (_forcedPath!=null)
- // TODO complain about poor naming to the Jasper folks
- request.setAttribute("org.apache.catalina.jsp_file",_forcedPath);
+ adaptForcedPathToJspContainer(request);
// Handle run as
if (_identityService!=null)
if (!isAsyncSupported())
baseRequest.setAsyncSupported(false);
- MultipartConfigElement mpce = ((Registration)getRegistration()).getMultipartConfig();
- if (mpce != null)
- request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce);
-
servlet.service(request,response);
servlet_error=false;
}
if (_servlet == null)
return false;
- Class c = _servlet.getClass();
+ Class<?> c = _servlet.getClass();
boolean result = false;
while (c != null && !result)
return false;
return ("org.apache.jasper.servlet.JspServlet".equals(classname));
}
-
-
+
+ /* ------------------------------------------------------------ */
+ private void adaptForcedPathToJspContainer (ServletRequest request)
+ {
+ if (_forcedPath != null && _jspContainer != null && JspContainer.GLASSFISH.equals(_jspContainer))
+ {
+ //if container is glassfish, set the request attribute org.apache.catalina.jsp_file to
+ //ensure the delegate jsp will be compiled
+
+ request.setAttribute("org.apache.catalina.jsp_file",_forcedPath);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ private void detectJspContainer ()
+ {
+ if (_jspContainer == null)
+ {
+ //check for glassfish
+ try
+ {
+ //if container is glassfish, set the request attribute org.apache.catalina.jsp_file to
+ //ensure the delegate jsp will be compiled
+ Loader.loadClass(Holder.class, GLASSFISH_SENTINEL_CLASS);
+ if (LOG.isDebugEnabled())LOG.debug("Glassfish jasper detected");
+ _jspContainer = JspContainer.GLASSFISH;
+ }
+ catch (ClassNotFoundException e)
+ {
+ try
+ {
+ //check for apache
+ Loader.loadClass(Holder.class, APACHE_SENTINEL_CLASS);
+ if (LOG.isDebugEnabled())LOG.debug("Apache jasper detected");
+ _jspContainer = JspContainer.APACHE;
+ }
+ catch (ClassNotFoundException x)
+ {
+ if (LOG.isDebugEnabled())LOG.debug("Other jasper detected");
+ _jspContainer = JspContainer.OTHER;
+ }
+ }
+ }
+ }
+
/* ------------------------------------------------------------ */
private String getNameOfJspClass (String jsp)
{
jsp = jsp.substring(i);
try
{
- Class jspUtil = Loader.loadClass(Holder.class, "org.apache.jasper.compiler.JspUtil");
+ Class<?> jspUtil = Loader.loadClass(Holder.class, "org.apache.jasper.compiler.JspUtil");
Method makeJavaIdentifier = jspUtil.getMethod("makeJavaIdentifier", String.class);
return (String)makeJavaIdentifier.invoke(null, jsp);
}
return "";
try
{
- Class jspUtil = Loader.loadClass(Holder.class, "org.apache.jasper.compiler.JspUtil");
+ Class<?> jspUtil = Loader.loadClass(Holder.class, "org.apache.jasper.compiler.JspUtil");
Method makeJavaPackage = jspUtil.getMethod("makeJavaPackage", String.class);
return (String)makeJavaPackage.invoke(null, jsp.substring(0,i));
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.Attributes;
+import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
public class ServletTester extends ContainerLifeCycle
{
+ private static final Logger LOG = Log.getLogger(ServletTester.class);
+
private final Server _server=new Server();
private final LocalConnector _connector=new LocalConnector(_server);
private final ServletContextHandler _context;
return _context.getAttribute(name);
}
- public Enumeration getAttributeNames()
+ public Enumeration<String> getAttributeNames()
{
return _context.getAttributeNames();
}
return _context.setInitParameter(name,value);
}
- public Enumeration getInitParameterNames()
+ public Enumeration<String> getInitParameterNames()
{
return _context.getInitParameterNames();
}
_context.setResourceBase(resourceBase);
}
- private final ServletHandler _handler;
-
public ServletTester()
{
this("/",ServletContextHandler.SECURITY|ServletContextHandler.SESSIONS);
public ServletTester(String contextPath,int options)
{
_context=new ServletContextHandler(_server,contextPath,options);
- _handler=_context.getServletHandler();
_server.setConnectors(new Connector[]{_connector});
addBean(_server);
}
public String getResponses(String request) throws Exception
{
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("Request: {}",request);
+ }
return _connector.getResponses(request);
}
-
+
+ public String getResponses(String request, long idleFor,TimeUnit units) throws Exception
+ {
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("Request: {}",request);
+ }
+ return _connector.getResponses(request, idleFor, units);
+ }
+
public ByteBuffer getResponses(ByteBuffer request) throws Exception
{
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("Request (Buffer): {}",BufferUtil.toUTF8String(request));
+ }
return _connector.getResponses(request);
}
+
+ public ByteBuffer getResponses(ByteBuffer requestsBuffer,long idleFor,TimeUnit units) throws Exception
+ {
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("Requests (Buffer): {}",BufferUtil.toUTF8String(requestsBuffer));
+ }
+ return _connector.getResponses(requestsBuffer, idleFor, units);
+ }
- /* ------------------------------------------------------------ */
/** Create a port based connector.
* This methods adds a port connector to the server
* @return A URL to access the server via the connector.
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.lang.reflect.Field;
import java.util.Iterator;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletContextEvent;
try
{
//Check that the BeanELResolver class is on the classpath
- Class beanELResolver = Loader.loadClass(this.getClass(), "javax.el.BeanELResolver");
+ Class<?> beanELResolver = Loader.loadClass(this.getClass(), "javax.el.BeanELResolver");
//Get a reference via reflection to the properties field which is holding class references
Field field = getField(beanELResolver);
//Get rid of references
purgeEntries(field);
- LOG.debug("javax.el.BeanELResolver purged");
+ if (LOG.isDebugEnabled())
+ LOG.debug("javax.el.BeanELResolver purged");
}
catch (ClassNotFoundException e)
{
//BeanELResolver not on classpath, ignore
}
- catch (SecurityException e)
- {
- LOG.warn("Cannot purge classes from javax.el.BeanELResolver", e);
- }
- catch (IllegalArgumentException e)
- {
- LOG.warn("Cannot purge classes from javax.el.BeanELResolver", e);
- }
- catch (IllegalAccessException e)
+ catch (SecurityException | IllegalArgumentException | IllegalAccessException e)
{
LOG.warn("Cannot purge classes from javax.el.BeanELResolver", e);
}
}
- protected Field getField (Class beanELResolver)
+ protected Field getField (Class<?> beanELResolver)
throws SecurityException, NoSuchFieldException
{
if (beanELResolver == null)
if (!properties.isAccessible())
properties.setAccessible(true);
- ConcurrentHashMap map = (ConcurrentHashMap) properties.get(null);
+ Map map = (Map) properties.get(null);
if (map == null)
return;
- Iterator<Class> itor = map.keySet().iterator();
+ Iterator<Class<?>> itor = map.keySet().iterator();
while (itor.hasNext())
{
- Class clazz = itor.next();
- LOG.debug("Clazz: "+clazz+" loaded by "+clazz.getClassLoader());
+ Class<?> clazz = itor.next();
+ if (LOG.isDebugEnabled())
+ LOG.debug("Clazz: "+clazz+" loaded by "+clazz.getClassLoader());
if (Thread.currentThread().getContextClassLoader().equals(clazz.getClassLoader()))
{
itor.remove();
- LOG.debug("removed");
+ if (LOG.isDebugEnabled())
+ LOG.debug("removed");
}
else
- LOG.debug("not removed: "+"contextclassloader="+Thread.currentThread().getContextClassLoader()+"clazz's classloader="+clazz.getClassLoader());
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("not removed: "+"contextclassloader="+Thread.currentThread().getContextClassLoader()+"clazz's classloader="+clazz.getClassLoader());
+ }
}
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
/* ------------------------------------------------------------ */
-/** A Ternary Trie String lookup data structure.
+/**
+ * <p>A Ternary Trie String lookup data structure.</p>
* This Trie is of a fixed size and cannot grow (which can be a good thing with regards to DOS when used as a cache).
* <p>
* The Trie is stored in 3 arrays:<dl>
* indicates that the _tree row is a complete key rather than an intermediate character of a longer key.</dd>
* <dt>V[] _value</dt><dd>An array of values corresponding to the _key array</dd>
* </dl>
+ * </p>
* <p>The lookup of a value will iterate through the _tree array matching characters. If the equal tree branch is followed,
* then the _key array is looked up to see if this is a complete match. If a match is found then the _value array is looked up
* to return the matching value.
* </p>
+ * <p>
+ * This Trie may be instantiated either as case sensitive or insensitive.
+ * </p>
+ * <p>This Trie is not Threadsafe and contains no mutual exclusion
+ * or deliberate memory barriers. It is intended for an ArrayTrie to be
+ * built by a single thread and then used concurrently by multiple threads
+ * and not mutated during that access. If concurrent mutations of the
+ * Trie is required external locks need to be applied.
+ * </p>
+ *
* @param <V>
*/
public class ArrayTernaryTrie<V> extends AbstractTrie<V>
* The value (if any) for a Trie row.
* A row may be a leaf, a node or both in the Trie tree.
*/
- private final Object[] _value;
+ private final V[] _value;
/**
* The number of rows allocated
*/
private char _rows;
+ /* ------------------------------------------------------------ */
+ /** Create a case insensitive Trie of default capacity.
+ */
public ArrayTernaryTrie()
{
this(128);
}
+ /* ------------------------------------------------------------ */
+ /** Create a Trie of default capacity
+ * @param insensitive true if the Trie is insensitive to the case of the key.
+ */
public ArrayTernaryTrie(boolean insensitive)
{
this(insensitive,128);
}
- public ArrayTernaryTrie(int capacityInNodes)
+ /* ------------------------------------------------------------ */
+ /** Create a case insensitive Trie
+ * @param capacity The capacity of the Trie, which is in the worst case
+ * is the total number of characters of all keys stored in the Trie.
+ * The capacity needed is dependent of the shared prefixes of the keys.
+ * For example, a capacity of 6 nodes is required to store keys "foo"
+ * and "bar", but a capacity of only 4 is required to
+ * store "bar" and "bat".
+ */
+ public ArrayTernaryTrie(int capacity)
{
- this(true,capacityInNodes);
+ this(true,capacity);
}
- public ArrayTernaryTrie(boolean insensitive, int capacityInNodes)
+ /* ------------------------------------------------------------ */
+ /** Create a Trie
+ * @param insensitive true if the Trie is insensitive to the case of the key.
+ * @param capacity The capacity of the Trie, which is in the worst case
+ * is the total number of characters of all keys stored in the Trie.
+ * The capacity needed is dependent of the shared prefixes of the keys.
+ * For example, a capacity of 6 nodes is required to store keys "foo"
+ * and "bar", but a capacity of only 4 is required to
+ * store "bar" and "bat".
+ */
+ public ArrayTernaryTrie(boolean insensitive, int capacity)
{
super(insensitive);
- _value=new Object[capacityInNodes];
- _tree=new char[capacityInNodes*ROW_SIZE];
- _key=new String[capacityInNodes];
+ _value=(V[])new Object[capacity];
+ _tree=new char[capacity*ROW_SIZE];
+ _key=new String[capacity];
}
/* ------------------------------------------------------------ */
}
}
- return (V)_value[t];
+ return _value[t];
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.util.Set;
/* ------------------------------------------------------------ */
-/** A Trie String lookup data structure using a fixed size array.
+/**
+ * <p>A Trie String lookup data structure using a fixed size array.</p>
* <p>This implementation is always case insensitive and is optimal for
- * a small number of fixed strings with few special characters.
+ * a small number of fixed strings with few special characters. The
+ * Trie is stored in an array of lookup tables, each indexed by the
+ * next character of the key. Frequently used characters are directly
+ * indexed in each lookup table, whilst infrequently used characters
+ * must use a big character table.
+ * </p>
+ * <p>This Trie is very space efficient if the key characters are
+ * from ' ', '+', '-', ':', ';', '.', 'A' to 'Z' or 'a' to 'z'.
+ * Other ISO-8859-1 characters can be used by the key, but less space
+ * efficiently.
+ * </p>
+ * <p>This Trie is not Threadsafe and contains no mutual exclusion
+ * or deliberate memory barriers. It is intended for an ArrayTrie to be
+ * built by a single thread and then used concurrently by multiple threads
+ * and not mutated during that access. If concurrent mutations of the
+ * Trie is required external locks need to be applied.
* </p>
* @param <V>
*/
private static final int[] __lookup =
{ // 0 1 2 3 4 5 6 7 8 9 A B C D E F
/*0*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- /*1*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 30,
- /*2*/31, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, -1, 27, -1, -1,
+ /*1*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ /*2*/31, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, -1, 27, 30, -1,
/*3*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 28, 29, -1, -1, -1, -1,
/*4*/-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
/*5*/15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
* The value (if any) for a Trie row.
* A row may be a leaf, a node or both in the Trie tree.
*/
- private final Object[] _value;
+ private final V[] _value;
/**
* A big index for each row.
this(128);
}
- public ArrayTrie(int capacityInNodes)
+ /* ------------------------------------------------------------ */
+ /**
+ * @param capacity The capacity of the trie, which at the worst case
+ * is the total number of characters of all keys stored in the Trie.
+ * The capacity needed is dependent of the shared prefixes of the keys.
+ * For example, a capacity of 6 nodes is required to store keys "foo"
+ * and "bar", but a capacity of only 4 is required to
+ * store "bar" and "bat".
+ */
+ @SuppressWarnings("unchecked")
+ public ArrayTrie(int capacity)
{
super(true);
- _value=new Object[capacityInNodes];
- _rowIndex=new char[capacityInNodes*32];
- _key=new String[capacityInNodes];
+ _value=(V[])new Object[capacity];
+ _rowIndex=new char[capacity*32];
+ _key=new String[capacity];
}
}
}
}
+
+ if (t>=_key.length)
+ {
+ _rows=(char)_key.length;
+ return false;
+ }
+
_key[t]=v==null?null:s;
- V old=(V)_value[t];
_value[t] = v;
return true;
}
return null;
}
}
- return (V)_value[t];
+ return _value[t];
}
/* ------------------------------------------------------------ */
}
- private <V> void toString(Appendable out, int t)
+ private void toString(Appendable out, int t)
{
if (_value[t]!=null)
{
@Override
public boolean isFull()
{
- return _rows+1==_key.length;
+ return _rows+1>=_key.length;
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
{
}
- public static void updateMin(AtomicLong currentMin, long newValue)
+ public static boolean updateMin(AtomicLong currentMin, long newValue)
{
long oldValue = currentMin.get();
while (newValue < oldValue)
{
if (currentMin.compareAndSet(oldValue, newValue))
- break;
+ return true;
oldValue = currentMin.get();
}
+ return false;
}
- public static void updateMax(AtomicLong currentMax, long newValue)
+ public static boolean updateMax(AtomicLong currentMax, long newValue)
{
long oldValue = currentMax.get();
while (newValue > oldValue)
{
if (currentMax.compareAndSet(oldValue, newValue))
- break;
+ return true;
oldValue = currentMax.get();
}
+ return false;
}
- public static void updateMin(AtomicInteger currentMin, int newValue)
+ public static boolean updateMin(AtomicInteger currentMin, int newValue)
{
int oldValue = currentMin.get();
while (newValue < oldValue)
{
if (currentMin.compareAndSet(oldValue, newValue))
- break;
+ return true;
oldValue = currentMin.get();
}
+ return false;
}
- public static void updateMax(AtomicInteger currentMax, int newValue)
+ public static boolean updateMax(AtomicInteger currentMax, int newValue)
{
int oldValue = currentMax.get();
while (newValue > oldValue)
{
if (currentMax.compareAndSet(oldValue, newValue))
- break;
+ return true;
oldValue = currentMax.get();
}
+ return false;
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
-import java.nio.charset.UnsupportedCharsetException;
/** Fast B64 Encoder/Decoder as described in RFC 1421.
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
-import java.nio.Buffer;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
}
/* ------------------------------------------------------------ */
- /** Appends a byte to a buffer
+ /** Appends a buffer to a buffer
* @param to Buffer is flush mode
- * @param b bytes to append
+ * @param b buffer to append
*/
public static int append(ByteBuffer to, ByteBuffer b)
{
public static void writeTo(ByteBuffer buffer, OutputStream out) throws IOException
{
if (buffer.hasArray())
- out.write(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
+ {
+ out.write(buffer.array(),buffer.arrayOffset() + buffer.position(),buffer.remaining());
+ // update buffer position, in way similar to non-array version of writeTo
+ buffer.position(buffer.position() + buffer.remaining());
+ }
else
{
byte[] bytes = new byte[TEMP_BUFFER_SIZE];
return builder.toString();
}
+
+
+ /* ------------------------------------------------------------ */
+ /** Convert Buffer to string ID independent of content
+ * @param buffer
+ */
+ private static void idString(ByteBuffer buffer, StringBuilder out)
+ {
+ out.append(buffer.getClass().getSimpleName());
+ out.append("@");
+ if (buffer.hasArray() && buffer.arrayOffset()==4)
+ {
+ out.append('T');
+ byte[] array = buffer.array();
+ TypeUtil.toHex(array[0],out);
+ TypeUtil.toHex(array[1],out);
+ TypeUtil.toHex(array[2],out);
+ TypeUtil.toHex(array[3],out);
+ }
+ else
+ out.append(Integer.toHexString(System.identityHashCode(buffer)));
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Convert Buffer to string ID independent of content
+ * @param buffer
+ * @return A string showing the buffer ID
+ */
+ public static String toIDString(ByteBuffer buffer)
+ {
+ StringBuilder buf = new StringBuilder();
+ idString(buffer,buf);
+ return buf.toString();
+ }
+
+
+ /* ------------------------------------------------------------ */
+ /** Convert Buffer to a detail debug string of pointers and content
+ * @param buffer
+ * @return A string showing the pointers and content of the buffer
+ */
public static String toDetailString(ByteBuffer buffer)
{
if (buffer == null)
return "null";
StringBuilder buf = new StringBuilder();
- buf.append(buffer.getClass().getSimpleName());
- buf.append("@");
- if (buffer.hasArray())
- buf.append(Integer.toHexString(((Object)buffer.array()).hashCode()));
- else
- buf.append(Integer.toHexString(buf.hashCode()));
+ idString(buffer,buf);
buf.append("[p=");
buf.append(buffer.position());
buf.append(",l=");
buf.append(buffer.remaining());
buf.append("]={");
+ appendDebugString(buf,buffer);
+
+ buf.append("}");
+
+ return buf.toString();
+ }
+
+ private static void appendDebugString(StringBuilder buf,ByteBuffer buffer)
+ {
for (int i = 0; i < buffer.position(); i++)
{
- char c = (char)buffer.get(i);
- if (c >= ' ' && c <= 127)
- buf.append(c);
- else if (c == '\r' || c == '\n')
- buf.append('|');
- else
- buf.append('\ufffd');
+ appendContentChar(buf,buffer.get(i));
if (i == 16 && buffer.position() > 32)
{
buf.append("...");
buf.append("<<<");
for (int i = buffer.position(); i < buffer.limit(); i++)
{
- char c = (char)buffer.get(i);
- if (c >= ' ' && c <= 127)
- buf.append(c);
- else if (c == '\r' || c == '\n')
- buf.append('|');
- else
- buf.append('\ufffd');
+ appendContentChar(buf,buffer.get(i));
if (i == buffer.position() + 16 && buffer.limit() > buffer.position() + 32)
{
buf.append("...");
buffer.limit(buffer.capacity());
for (int i = limit; i < buffer.capacity(); i++)
{
- char c = (char)buffer.get(i);
- if (c >= ' ' && c <= 127)
- buf.append(c);
- else if (c == '\r' || c == '\n')
- buf.append('|');
- else
- buf.append('\ufffd');
+ appendContentChar(buf,buffer.get(i));
if (i == limit + 16 && buffer.capacity() > limit + 32)
{
buf.append("...");
}
}
buffer.limit(limit);
- buf.append("}");
-
- return buf.toString();
}
+ private static void appendContentChar(StringBuilder buf, byte b)
+ {
+ if (b == '\\')
+ buf.append("\\\\");
+ else if (b >= ' ')
+ buf.append((char)b);
+ else if (b == '\r')
+ buf.append("\\r");
+ else if (b == '\n')
+ buf.append("\\n");
+ else if (b == '\t')
+ buf.append("\\t");
+ else
+ buf.append("\\x").append(TypeUtil.toHexString(b));
+ }
private final static int[] decDivisors =
{1000000000, 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1};
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
// ========================================================================
//
-/*
- * Copyright (c) 2012 the original author or authors.
- *
- * 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;
/**
* <p>A callback abstraction that handles completed/failed events of asynchronous operations.</p>
*
- * <p>Semantically this is equivalent to an optimise Promise<Void>, but callback is a more meaningful
+ * <p>Semantically this is equivalent to an optimise Promise<Void>, but callback is a more meaningful
* name than EmptyPromise</p>
*/
public interface Callback
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
boolean hasNonPublicInterface = false;
// define proxy in class loader of non-public interface(s), if any
- Class[] classObjs = new Class[interfaces.length];
+ Class<?>[] classObjs = new Class[interfaces.length];
for (int i = 0; i < interfaces.length; i++)
{
- Class cl = Class.forName(interfaces[i], false, loader);
+ Class<?> cl = Class.forName(interfaces[i], false, loader);
if ((cl.getModifiers() & Modifier.PUBLIC) == 0)
{
if (hasNonPublicInterface)
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.util;
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
/**
* A callback to be used by driver code that needs to know whether the callback has been
*/
public abstract class CompletableCallback implements Callback
{
- private final AtomicBoolean completed = new AtomicBoolean();
+ private final AtomicReference<State> state = new AtomicReference<>(State.IDLE);
@Override
public void succeeded()
{
- if (!tryComplete())
- resume();
+ while (true)
+ {
+ State current = state.get();
+ switch (current)
+ {
+ case IDLE:
+ {
+ if (state.compareAndSet(current, State.SUCCEEDED))
+ return;
+ break;
+ }
+ case COMPLETED:
+ {
+ if (state.compareAndSet(current, State.SUCCEEDED))
+ {
+ resume();
+ return;
+ }
+ break;
+ }
+ case FAILED:
+ {
+ return;
+ }
+ default:
+ {
+ throw new IllegalStateException(current.toString());
+ }
+ }
+ }
}
@Override
public void failed(Throwable x)
{
- if (!tryComplete())
- abort(x);
+ while (true)
+ {
+ State current = state.get();
+ switch (current)
+ {
+ case IDLE:
+ case COMPLETED:
+ {
+ if (state.compareAndSet(current, State.FAILED))
+ {
+ abort(x);
+ return;
+ }
+ break;
+ }
+ case FAILED:
+ {
+ return;
+ }
+ default:
+ {
+ throw new IllegalStateException(current.toString());
+ }
+ }
+ }
}
/**
public abstract void resume();
/**
- * Callback method invoked when this callback is failed
- * <em>after</em> a first call to {@link #tryComplete()}.
+ * Callback method invoked when this callback is failed.
*/
public abstract void abort(Throwable failure);
*/
public boolean tryComplete()
{
- return completed.compareAndSet(false, true);
+ while (true)
+ {
+ State current = state.get();
+ switch (current)
+ {
+ case IDLE:
+ {
+ if (state.compareAndSet(current, State.COMPLETED))
+ return true;
+ break;
+ }
+ case SUCCEEDED:
+ case FAILED:
+ {
+ return false;
+ }
+ default:
+ {
+ throw new IllegalStateException(current.toString());
+ }
+ }
+ }
+ }
+
+ private enum State
+ {
+ IDLE, SUCCEEDED, FAILED, COMPLETED
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;
return _blocks.compareAndSet(TAIL_OFFSET,current,update);
}
+ @SuppressWarnings("unchecked")
@Override
public T poll()
{
}
else
{
- Object element = currentHeadBlock.peek(head);
+ T element = currentHeadBlock.peek(head);
if (element == REMOVED_ELEMENT)
{
// Already removed, try next index
}
else
{
- return (T)element;
+ return element;
}
}
}
advance();
- if (element != REMOVED_ELEMENT)
- return (T)element;
+ if (element != REMOVED_ELEMENT) {
+ @SuppressWarnings("unchecked")
+ T e = (T)element;
+ return e;
+ }
}
}
elements = new AtomicReferenceArray<>(blockSize);
}
- public Object peek(int index)
+ @SuppressWarnings("unchecked")
+ public E peek(int index)
{
- return elements.get(index);
+ return (E)elements.get(index);
}
public boolean store(int index, E item)
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * <p>A callback wrapper that succeeds the wrapped callback when the count is
+ * reached, or on first failure.</p>
+ * <p>This callback is particularly useful when an async operation is split
+ * into multiple parts, for example when an original byte buffer that needs
+ * to be written, along with a callback, is split into multiple byte buffers,
+ * since it allows the original callback to be wrapped and notified only when
+ * the last part has been processed.</p>
+ * <p>Example:</p>
+ * <pre>
+ * public void process(EndPoint endPoint, ByteBuffer buffer, Callback callback)
+ * {
+ * ByteBuffer[] buffers = split(buffer);
+ * CountCallback countCallback = new CountCallback(callback, buffers.length);
+ * endPoint.write(countCallback, buffers);
+ * }
+ * </pre>
+ */
+public class CountingCallback implements Callback
+{
+ private final Callback callback;
+ private final AtomicInteger count;
+
+ public CountingCallback(Callback callback, int count)
+ {
+ this.callback = callback;
+ this.count = new AtomicInteger(count);
+ }
+
+ @Override
+ public void succeeded()
+ {
+ // Forward success on the last success.
+ while (true)
+ {
+ int current = count.get();
+
+ // Already completed ?
+ if (current == 0)
+ return;
+
+ if (count.compareAndSet(current, current - 1))
+ {
+ if (current == 1)
+ callback.succeeded();
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void failed(Throwable failure)
+ {
+ // Forward failure on the first failure.
+ while (true)
+ {
+ int current = count.get();
+
+ // Already completed ?
+ if (current == 0)
+ return;
+
+ if (count.compareAndSet(current, 0))
+ {
+ callback.failed(failure);
+ return;
+ }
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s@%x", getClass().getSimpleName(), hashCode());
+ }
+}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
+++ /dev/null
-//
-// ========================================================================
-// Copyright (c) 1995-2014 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;
-
-/**
- * Utility class that splits calls to {@link #invoke(Object)} into calls to {@link #fork(Object)} or {@link #call(Object)}
- * depending on the max number of reentrant calls to {@link #invoke(Object)}.
- * <p/>
- * This class prevents {@link StackOverflowError}s in case of methods that end up invoking themselves,
- * such is common for {@link Callback#succeeded()}.
- * <p/>
- * Typical use case is:
- * <pre>
- * public void reentrantMethod(Object param)
- * {
- * if (condition || tooManyReenters)
- * fork(param)
- * else
- * call(param)
- * }
- * </pre>
- * Calculating {@code tooManyReenters} usually involves using a {@link ThreadLocal} and algebra on the
- * number of reentrant invocations, which is factored out in this class for convenience.
- * <p />
- * The same code using this class becomes:
- * <pre>
- * private final ForkInvoker invoker = ...;
- *
- * public void reentrantMethod(Object param)
- * {
- * invoker.invoke(param);
- * }
- * </pre>
- *
- */
-public abstract class ForkInvoker<T>
-{
- private static final ThreadLocal<Integer> __invocations = new ThreadLocal<Integer>()
- {
- @Override
- protected Integer initialValue()
- {
- return 0;
- }
- };
- private final int _maxInvocations;
-
- /**
- * Creates an instance with the given max number of reentrant calls to {@link #invoke(Object)}
- * <p/>
- * If {@code maxInvocations} is zero or negative, it is interpreted
- * as if the max number of reentrant calls is infinite.
- *
- * @param maxInvocations the max number of reentrant calls to {@link #invoke(Object)}
- */
- public ForkInvoker(int maxInvocations)
- {
- _maxInvocations = maxInvocations;
- }
-
- /**
- * Invokes either {@link #fork(Object)} or {@link #call(Object)}.
- * If {@link #condition()} returns true, {@link #fork(Object)} is invoked.
- * Otherwise, if the max number of reentrant calls is positive and the
- * actual number of reentrant invocations exceeds it, {@link #fork(Object)} is invoked.
- * Otherwise, {@link #call(Object)} is invoked.
- * @param arg TODO
- *
- * @return true if {@link #fork(Object)} has been called, false otherwise
- */
- public boolean invoke(T arg)
- {
- boolean countInvocations = _maxInvocations > 0;
- int invocations = __invocations.get();
- if (condition() || countInvocations && invocations > _maxInvocations)
- {
- fork(arg);
- return true;
- }
- else
- {
- if (countInvocations)
- __invocations.set(invocations + 1);
- try
- {
- call(arg);
- return false;
- }
- finally
- {
- if (countInvocations)
- __invocations.set(invocations);
- }
- }
- }
-
- /**
- * Subclasses should override this method returning true if they want
- * {@link #invoke(Object)} to call {@link #fork(Object)}.
- *
- * @return true if {@link #invoke(Object)} should call {@link #fork(Object)}, false otherwise
- */
- protected boolean condition()
- {
- return false;
- }
-
- /**
- * Executes the forked invocation
- * @param arg TODO
- */
- public abstract void fork(T arg);
-
- /**
- * Executes the direct, non-forked, invocation
- * @param arg TODO
- */
- public abstract void call(T arg);
-}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
@Override
public String toString()
{
- return String.format("FutureCallback@%x{%b,%b}",hashCode(),_done,_cause==COMPLETED);
+ return String.format("FutureCallback@%x{%b,%b}",hashCode(),_done.get(),_cause==COMPLETED);
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
@Override
public String toString()
{
- return String.format("FutureCallback@%x{%b,%b,%s}",hashCode(),_done,_cause==COMPLETED,_result);
+ return String.format("FutureCallback@%x{%b,%b,%s}",hashCode(),_done.get(),_cause==COMPLETED,_result);
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.channels.GatheringByteChannel;
import java.nio.charset.Charset;
import org.eclipse.jetty.util.log.Log;
{
if (writer != null)
writer.close();
- } catch (IOException e)
+ }
+ catch (IOException e)
{
LOG.ignore(e);
}
copy(in,bout);
return bout.toByteArray();
}
+
+ /* ------------------------------------------------------------ */
+ /**
+ * A gathering write utility wrapper.
+ * <p>This method wraps a gather write with a loop that handles the limitations of some operating systems that
+ * have a limit on the number of buffers written. The method loops on the write until either all the content
+ * is written or no progress is made.
+ * @param out The GatheringgByteChannel to write to
+ * @param buffers The buffers to write
+ * @param offset The offset into the buffers array
+ * @param length The length in buffers to write
+ * @return The total bytes written
+ * @throws IOException
+ */
+ public static long write(GatheringByteChannel out, ByteBuffer[] buffers, int offset, int length) throws IOException
+ {
+ long total=0;
+ write: while (length>0)
+ {
+ // Write as much as we can
+ long wrote=out.write(buffers,offset,length);
+
+ // If we can't write any more, give up
+ if (wrote==0)
+ break;
+
+ // count the total
+ total+=wrote;
+
+ // Look for unwritten content
+ for (int i=offset;i<buffers.length;i++)
+ {
+ if (buffers[i].hasRemaining())
+ {
+ // loop with new offset and length;
+ length=length-(i-offset);
+ offset=i;
+ continue write;
+ }
+ }
+ length=0;
+ }
+
+ return total;
+ }
+
/* ------------------------------------------------------------ */
/**
}
private static NullWrite __nullWriter = new NullWrite();
private static PrintWriter __nullPrintWriter = new PrintWriter(__nullWriter);
+
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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;
+
+import java.util.HashSet;
+import java.util.Set;
+
+
+/** Utility class to maintain a set of inclusions and exclusions.
+ * <p>Maintains a set of included and excluded elements. The method {@link #matches(Object)}
+ * will return true IFF the passed object is not in the excluded set AND ( either the
+ * included set is empty OR the object is in the included set)
+ * <p>The type of the underlying {@link Set} used may be passed into the
+ * constructor, so special sets like Servlet PathMap may be used.
+ * <p>
+ * @param <ITEM> The type of element
+ */
+public class IncludeExclude<ITEM>
+{
+ private final Set<ITEM> _includes;
+ private final Predicate<ITEM> _includePredicate;
+ private final Set<ITEM> _excludes;
+ private final Predicate<ITEM> _excludePredicate;
+
+ private static class SetContainsPredicate<ITEM> implements Predicate<ITEM>
+ {
+ private final Set<ITEM> set;
+
+ public SetContainsPredicate(Set<ITEM> set)
+ {
+ this.set = set;
+ }
+
+ @Override
+ public boolean test(ITEM item)
+ {
+ return set.contains(item);
+ }
+ }
+
+ /**
+ * Default constructor over {@link HashSet}
+ */
+ public IncludeExclude()
+ {
+ this(HashSet.class);
+ }
+
+ /**
+ * Construct an IncludeExclude
+ * @param setClass The type of {@link Set} to using internally
+ * @param predicate A predicate function to test if a passed ITEM is matched by the passed SET}
+ */
+ public <SET extends Set<ITEM>> IncludeExclude(Class<SET> setClass)
+ {
+ try
+ {
+ _includes = setClass.newInstance();
+ _excludes = setClass.newInstance();
+
+ if(_includes instanceof Predicate) {
+ _includePredicate = (Predicate<ITEM>)_includes;
+ } else {
+ _includePredicate = new SetContainsPredicate<>(_includes);
+ }
+
+ if(_excludes instanceof Predicate) {
+ _excludePredicate = (Predicate<ITEM>)_excludes;
+ } else {
+ _excludePredicate = new SetContainsPredicate<>(_excludes);
+ }
+ }
+ catch (InstantiationException | IllegalAccessException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Construct an IncludeExclude
+ *
+ * @param includeSet the Set of items that represent the included space
+ * @param includePredicate the Predicate for included item testing (null for simple {@link Set#contains(Object)} test)
+ * @param excludeSet the Set of items that represent the excluded space
+ * @param excludePredicate the Predicate for excluded item testing (null for simple {@link Set#contains(Object)} test)
+ */
+ public <SET extends Set<ITEM>> IncludeExclude(Set<ITEM> includeSet, Predicate<ITEM> includePredicate, Set<ITEM> excludeSet, Predicate<ITEM> excludePredicate)
+ {
+ _includes = includeSet;
+ _includePredicate = includePredicate;
+ _excludes = excludeSet;
+ _excludePredicate = excludePredicate;
+ }
+
+ public void include(ITEM element)
+ {
+ _includes.add(element);
+ }
+
+ public void include(ITEM... element)
+ {
+ for (ITEM e: element)
+ _includes.add(e);
+ }
+
+ public void exclude(ITEM element)
+ {
+ _excludes.add(element);
+ }
+
+ public void exclude(ITEM... element)
+ {
+ for (ITEM e: element)
+ _excludes.add(e);
+ }
+
+ public boolean matches(ITEM e)
+ {
+ if (!_includes.isEmpty() && !_includePredicate.test(e))
+ return false;
+ return !_excludePredicate.test(e);
+ }
+
+ public int size()
+ {
+ return _includes.size()+_excludes.size();
+ }
+
+ public Set<ITEM> getIncluded()
+ {
+ return _includes;
+ }
+
+ public Set<ITEM> getExcluded()
+ {
+ return _excludes;
+ }
+
+ public void clear()
+ {
+ _includes.clear();
+ _excludes.clear();
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s@%x{i=%s,ip=%s,e=%s,ep=%s}",this.getClass().getSimpleName(),hashCode(),_includes,_includePredicate,_excludes,_excludePredicate);
+ }
+}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.util;
+import java.nio.channels.ClosedChannelException;
import java.util.concurrent.atomic.AtomicReference;
/**
*/
public abstract class IteratingCallback implements Callback
{
+ /**
+ * The internal states of this callback
+ */
+ private enum State
+ {
+ /**
+ * This callback is IDLE, ready to iterate.
+ */
+ IDLE,
+
+ /**
+ * This callback is iterating calls to {@link #process()} and is dealing with
+ * the returns. To get into processing state, it much of held the lock state
+ * and set iterating to true.
+ */
+ PROCESSING,
+
+ /**
+ * Waiting for a schedule callback
+ */
+ PENDING,
+
+ /**
+ * Called by a schedule callback
+ */
+ CALLED,
+
+ /**
+ * The overall job has succeeded as indicated by a {@link Action#SUCCEEDED} return
+ * from {@link IteratingCallback#process()}
+ */
+ SUCCEEDED,
+
+ /**
+ * The overall job has failed as indicated by a call to {@link IteratingCallback#failed(Throwable)}
+ */
+ FAILED,
+
+ /**
+ * This callback has been closed and cannot be reset.
+ */
+ CLOSED,
+
+ /**
+ * State is locked while leaving processing state to check the iterate boolean
+ */
+ LOCKED
+ }
+
/**
* The indication of the overall progress of the overall job that
* implementations of {@link #process()} must return.
* may have not yet been invoked.
*/
SCHEDULED,
+
/**
* Indicates that {@link #process()} has completed the overall job.
*/
- SUCCEEDED,
- /**
- * Indicates that {@link #process()} has failed the overall job.
- */
- FAILED
+ SUCCEEDED
}
- private final AtomicReference<State> _state = new AtomicReference<>(State.INACTIVE);
-
+ private final AtomicReference<State> _state;
+ private boolean _iterate;
+
+
+ protected IteratingCallback()
+ {
+ _state = new AtomicReference<>(State.IDLE);
+ }
+
+ protected IteratingCallback(boolean needReset)
+ {
+ _state = new AtomicReference<>(needReset ? State.SUCCEEDED : State.IDLE);
+ }
+
/**
* Method called by {@link #iterate()} to process the sub task.
* <p/>
* <li>{@link Action#SCHEDULED} when the sub task asynchronous execution
* has been started</li>
* <li>{@link Action#SUCCEEDED} when the overall job is completed</li>
- * <li>{@link Action#FAILED} when the overall job cannot be completed</li>
* </ul>
*
* @throws Exception if the sub task processing throws
*/
protected abstract Action process() throws Exception;
+ /**
+ * @deprecated Use {@link #onCompleteSuccess()} instead.
+ */
+ @Deprecated
+ protected void completed()
+ {
+ }
+
/**
* Invoked when the overall task has completed successfully.
+ *
+ * @see #onCompleteFailure(Throwable)
*/
- protected abstract void completed();
+ protected void onCompleteSuccess()
+ {
+ completed();
+ }
+
+ /**
+ * Invoked when the overall task has completed with a failure.
+ *
+ * @see #onCompleteSuccess()
+ */
+ protected void onCompleteFailure(Throwable x)
+ {
+ }
/**
* This method must be invoked by applications to start the processing
- * of sub tasks.
- * <p/>
- * If {@link #process()} returns {@link Action#IDLE}, then this method
- * should be called again to restart processing.
- * It is safe to call iterate multiple times from multiple threads since only
- * the first thread to move the state out of INACTIVE will actually do any iteration
- * and processing.
+ * of sub tasks. It can be called at any time by any thread, and it's
+ * contract is that when called, then the {@link #process()} method will
+ * be called during or soon after, either by the calling thread or by
+ * another thread.
*/
public void iterate()
{
- try
+ loop: while (true)
{
- while (true)
+ State state=_state.get();
+ switch (state)
{
- switch (_state.get())
- {
- case INACTIVE:
- {
- if (processIterations())
- return;
- break;
- }
- case ITERATING:
- {
- if (_state.compareAndSet(State.ITERATING, State.ITERATE_AGAIN))
- return;
- break;
- }
- default:
- {
- return;
- }
- }
+ case PENDING:
+ case CALLED:
+ // process will be called when callback is handled
+ break loop;
+
+ case IDLE:
+ if (!_state.compareAndSet(state,State.PROCESSING))
+ continue;
+ processing();
+ break loop;
+
+ case PROCESSING:
+ if (!_state.compareAndSet(state,State.LOCKED))
+ continue;
+ // Tell the thread that is processing that it must iterate again
+ _iterate=true;
+ _state.set(State.PROCESSING);
+ break loop;
+
+ case LOCKED:
+ Thread.yield();
+ continue loop;
+
+ case FAILED:
+ case SUCCEEDED:
+ break loop;
+
+ case CLOSED:
+ default:
+ throw new IllegalStateException("state="+state);
}
}
- catch (Throwable x)
- {
- failed(x);
- }
}
- private boolean processIterations() throws Exception
+ private void processing()
{
- // Keeps iterating as long as succeeded() is called during process().
- // If we are in INACTIVE state, either this is the first iteration or
- // succeeded()/failed() were called already.
- while (_state.compareAndSet(State.INACTIVE, State.ITERATING))
+ // This should only ever be called when in processing state, however a failed or close call
+ // may happen concurrently, so state is not assumed.
+
+ // While we are processing
+ processing: while (true)
{
- // Method process() can only be called by one thread at a time because
- // it is guarded by the CaS above. However, the case blocks below may
- // be executed concurrently in this case: T1 calls process() which
- // executes the asynchronous sub task, which calls succeeded(), which
- // moves the state into INACTIVE, then returns SCHEDULED; T2 calls
- // iterate(), state is now INACTIVE and process() is called again and
- // returns another action. Now we have 2 threads that may execute the
- // action case blocks below concurrently; therefore each case block
- // has to be prepared to fail the CaS it's doing.
+ // Call process to get the action that we have to take.
+ Action action;
+ try
+ {
+ action = process();
+ }
+ catch (Throwable x)
+ {
+ failed(x);
+ break processing;
+ }
- Action action = process();
- switch (action)
+ // loop until we have successfully acted on the action we have just received
+ acting: while(true)
{
- case IDLE:
+ // action handling needs to know the state
+ State state=_state.get();
+
+ switch (state)
{
- // No more progress can be made.
- if (_state.compareAndSet(State.ITERATING, State.INACTIVE))
- return true;
+ case PROCESSING:
+ {
+ switch (action)
+ {
+ case IDLE:
+ {
+ // lock the state
+ if (!_state.compareAndSet(state,State.LOCKED))
+ continue acting;
- // Was iterate() called again since we already decided to go INACTIVE ?
- // If so, try another iteration as more work may have been added
- // while the previous call to process() was returning.
- if (_state.compareAndSet(State.ITERATE_AGAIN, State.INACTIVE))
- continue;
+ // Has iterate been called while we were processing?
+ if (_iterate)
+ {
+ // yes, so skip idle and keep processing
+ _iterate=false;
+ _state.set(State.PROCESSING);
+ continue processing;
+ }
- // State may have changed concurrently, try again.
- continue;
- }
- case SCHEDULED:
- {
- // The sub task is executing, and the callback for it may or
- // may not have already been called yet, which we figure out below.
- // Can double CaS here because state never changes directly ITERATING_AGAIN --> ITERATE.
- if (_state.compareAndSet(State.ITERATING, State.ACTIVE) ||
- _state.compareAndSet(State.ITERATE_AGAIN, State.ACTIVE))
- // Not called back yet, so wait.
- return true;
- // Call back must have happened, so iterate.
- continue;
- }
- case SUCCEEDED:
- {
- // The overall job has completed.
- if (completeSuccess())
- completed();
- return true;
- }
- case FAILED:
- {
- completeFailure();
- return true;
- }
- default:
- {
- throw new IllegalStateException(toString());
+ // No, so we can go idle
+ _state.set(State.IDLE);
+ break processing;
+ }
+
+ case SCHEDULED:
+ {
+ if (!_state.compareAndSet(state, State.PENDING))
+ continue acting;
+ // we won the race against the callback, so the callback has to process and we can break processing
+ break processing;
+ }
+
+ case SUCCEEDED:
+ {
+ if (!_state.compareAndSet(state, State.LOCKED))
+ continue acting;
+ _iterate=false;
+ _state.set(State.SUCCEEDED);
+ onCompleteSuccess();
+ break processing;
+ }
+
+ default:
+ throw new IllegalStateException("state="+state+" action="+action);
+ }
+ }
+
+ case CALLED:
+ {
+ switch (action)
+ {
+ case SCHEDULED:
+ {
+ if (!_state.compareAndSet(state, State.PROCESSING))
+ continue acting;
+ // we lost the race, so we have to keep processing
+ continue processing;
+ }
+
+ default:
+ throw new IllegalStateException("state="+state+" action="+action);
+ }
+ }
+
+ case LOCKED:
+ Thread.yield();
+ continue acting;
+
+ case SUCCEEDED:
+ case FAILED:
+ case CLOSED:
+ break processing;
+
+ case IDLE:
+ case PENDING:
+ default:
+ throw new IllegalStateException("state="+state+" action="+action);
}
}
}
- return false;
}
-
+
/**
* Invoked when the sub task succeeds.
* Subclasses that override this method must always remember to call
@Override
public void succeeded()
{
- while (true)
+ loop: while (true)
{
- State current = _state.get();
- switch (current)
+ State state = _state.get();
+ switch (state)
{
- case ITERATE_AGAIN:
- case ITERATING:
+ case PROCESSING:
{
- if (_state.compareAndSet(current, State.INACTIVE))
- return;
- continue;
+ if (!_state.compareAndSet(state, State.CALLED))
+ continue loop;
+ break loop;
}
- case ACTIVE:
+ case PENDING:
{
- // If we can move from ACTIVE to INACTIVE
- // then we are responsible to call iterate().
- if (_state.compareAndSet(current, State.INACTIVE))
- iterate();
- // If we can't CaS, then failed() must have been
- // called, and we just return.
- return;
+ if (!_state.compareAndSet(state, State.PROCESSING))
+ continue loop;
+ processing();
+ break loop;
}
- case INACTIVE:
+ case CLOSED:
+ case FAILED:
{
- // Support the case where the callback is scheduled
- // externally without a call to iterate().
- iterate();
- return;
+ // Too late!
+ break loop;
}
+ case LOCKED:
+ {
+ Thread.yield();
+ continue loop;
+ }
default:
{
- throw new IllegalStateException(toString());
+ throw new IllegalStateException("state="+state);
}
}
}
@Override
public void failed(Throwable x)
{
- completeFailure();
- }
-
- private boolean completeSuccess()
- {
- while (true)
+ loop: while (true)
{
- State current = _state.get();
- if (current == State.FAILED)
+ State state = _state.get();
+ switch (state)
{
- // Success arrived too late, sorry.
- return false;
- }
- else
- {
- if (_state.compareAndSet(current, State.SUCCEEDED))
- return true;
+ case SUCCEEDED:
+ case FAILED:
+ case IDLE:
+ case CLOSED:
+ case CALLED:
+ {
+ // too late!.
+ break loop;
+ }
+ case LOCKED:
+ {
+ Thread.yield();
+ continue loop;
+ }
+ case PENDING:
+ case PROCESSING:
+ {
+ if (!_state.compareAndSet(state, State.FAILED))
+ continue loop;
+
+ onCompleteFailure(x);
+ break loop;
+ }
+ default:
+ throw new IllegalStateException("state="+state);
}
}
}
- private void completeFailure()
+ public void close()
{
- while (true)
+ loop: while (true)
{
- State current = _state.get();
- if (current == State.SUCCEEDED)
+ State state = _state.get();
+ switch (state)
{
- // Failed arrived too late, sorry.
- return;
- }
- else
- {
- if (_state.compareAndSet(current, State.FAILED))
- break;
+ case IDLE:
+ case SUCCEEDED:
+ case FAILED:
+ {
+ if (!_state.compareAndSet(state, State.CLOSED))
+ continue loop;
+ break loop;
+ }
+ case CLOSED:
+ {
+ break loop;
+ }
+ case LOCKED:
+ {
+ Thread.yield();
+ continue loop;
+ }
+ default:
+ {
+ if (!_state.compareAndSet(state, State.CLOSED))
+ continue loop;
+ onCompleteFailure(new ClosedChannelException());
+ break loop;
+ }
}
}
}
- /**
+ /*
+ * only for testing
* @return whether this callback is idle and {@link #iterate()} needs to be called
*/
- public boolean isIdle()
+ boolean isIdle()
{
- return _state.get() == State.INACTIVE;
+ return _state.get() == State.IDLE;
}
+ public boolean isClosed()
+ {
+ return _state.get() == State.CLOSED;
+ }
+
/**
* @return whether this callback has failed
*/
return _state.get() == State.SUCCEEDED;
}
+ /**
+ * Resets this callback.
+ * <p/>
+ * A callback can only be reset to IDLE from the
+ * SUCCEEDED or FAILED states or if it is already IDLE.
+ *
+ * @return true if the reset was successful
+ */
+ public boolean reset()
+ {
+ while (true)
+ {
+ State state=_state.get();
+ switch(state)
+ {
+ case IDLE:
+ return true;
+
+ case SUCCEEDED:
+ if (!_state.compareAndSet(state, State.LOCKED))
+ continue;
+ _iterate=false;
+ _state.set(State.IDLE);
+ return true;
+
+ case FAILED:
+ if (!_state.compareAndSet(state, State.LOCKED))
+ continue;
+ _iterate=false;
+ _state.set(State.IDLE);
+ return true;
+
+ case LOCKED:
+ Thread.yield();
+ continue;
+
+ default:
+ return false;
+ }
+ }
+ }
+
@Override
public String toString()
{
return String.format("%s[%s]", super.toString(), _state);
}
-
- /**
- * The internal states of this callback
- */
- private enum State
- {
- /**
- * This callback is inactive, ready to iterate.
- */
- INACTIVE,
- /**
- * This callback is iterating and {@link #process()} has scheduled an
- * asynchronous operation by returning {@link Action#SCHEDULED}, but
- * the operation is still undergoing.
- */
- ACTIVE,
- /**
- * This callback is iterating and {@link #process()} has been called
- * but not returned yet.
- */
- ITERATING,
- /**
- * While this callback was iterating, another request for iteration
- * has been issued, so the iteration must continue even if a previous
- * call to {@link #process()} returned {@link Action#IDLE}.
- */
- ITERATE_AGAIN,
- /**
- * The overall job has succeeded.
- */
- SUCCEEDED,
- /**
- * The overall job has failed.
- */
- FAILED
- }
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
}
@Override
- protected void completed()
+ protected void onCompleteSuccess()
{
_callback.succeeded();
}
-
+
@Override
- public void failed(Throwable x)
+ protected void onCompleteFailure(Throwable x)
{
- super.failed(x);
_callback.failed(x);
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
/**
* A facility to detect improper usage of resource pools.
* <p>
- * Resource pools usually have a method to acquire a pooled resource
- * and a method to released it back to the pool.
+ * Resource pools usually have a method to acquire a pooled resource and a method to released it back to the pool.
* <p>
- * To detect if client code acquires a resource but never releases it,
- * the resource pool can be modified to use a {@link LeakDetector}.
- * The modified resource pool should call {@link #acquired(Object)} every time
- * the method to acquire a resource is called, and {@link #released(Object)}
- * every time the method to release the resource is called.
+ * To detect if client code acquires a resource but never releases it, the resource pool can be modified to use a
+ * {@link LeakDetector}. The modified resource pool should call {@link #acquired(Object)} every time the method to
+ * acquire a resource is called, and {@link #released(Object)} every time the method to release the resource is called.
* {@link LeakDetector} keeps track of these resources and invokes method
- * {@link #leaked(org.eclipse.jetty.util.LeakDetector.LeakInfo)} when it detects that a resource
- * has been leaked (that is, acquired but never released).
+ * {@link #leaked(org.eclipse.jetty.util.LeakDetector.LeakInfo)} when it detects that a resource has been leaked (that
+ * is, acquired but never released).
* <p>
- * To detect whether client code releases a resource without having
- * acquired it, the resource pool can be modified to check the return value
- * of {@link #released(Object)}: if false, it means that the resource was
- * not acquired.
+ * To detect whether client code releases a resource without having acquired it, the resource pool can be modified to
+ * check the return value of {@link #released(Object)}: if false, it means that the resource was not acquired.
* <p>
* IMPLEMENTATION NOTES
* <p>
- * This class relies on {@link System#identityHashCode(Object)} to create
- * a unique id for each resource passed to {@link #acquired(Object)} and
- * {@link #released(Object)}. {@link System#identityHashCode(Object)} does
- * not guarantee that it will not generate the same number for different
- * objects, but in practice the chance of collision is rare.
+ * This class relies on {@link System#identityHashCode(Object)} to create a unique id for each resource passed to
+ * {@link #acquired(Object)} and {@link #released(Object)}. {@link System#identityHashCode(Object)} does not guarantee
+ * that it will not generate the same number for different objects, but in practice the chance of collision is rare.
* <p>
- * {@link LeakDetector} uses {@link PhantomReference}s to detect leaks.
- * {@link PhantomReference}s are enqueued in their {@link ReferenceQueue}
- * <em>after</em> they have been garbage collected (differently from
- * {@link WeakReference}s that are enqueued <em>before</em>).
- * Since the resource is now garbage collected, {@link LeakDetector} checks
- * whether it has been released and if not, it reports a leak.
- * Using {@link PhantomReference}s is better than overriding {@link #finalize()}
- * and works also in those cases where {@link #finalize()} is not
- * overridable.
+ * {@link LeakDetector} uses {@link PhantomReference}s to detect leaks. {@link PhantomReference}s are enqueued in their
+ * {@link ReferenceQueue} <em>after</em> they have been garbage collected (differently from {@link WeakReference}s that
+ * are enqueued <em>before</em>). Since the resource is now garbage collected, {@link LeakDetector} checks whether it
+ * has been released and if not, it reports a leak. Using {@link PhantomReference}s is better than overriding
+ * {@link #finalize()} and works also in those cases where {@link #finalize()} is not overridable.
*
* @param <T> the resource type.
*/
* Tracks the resource as been acquired.
*
* @param resource the resource that has been acquired
- * @return whether the resource has been tracked
+ * @return true whether the resource has been acquired normally, false if the resource has detected a leak (meaning
+ * that another acquire occurred before a release of the same resource)
* @see #released(Object)
*/
public boolean acquired(T resource)
{
String id = id(resource);
- return resources.putIfAbsent(id, new LeakInfo(resource, id)) == null;
+ LeakInfo info = resources.putIfAbsent(id, new LeakInfo(resource,id));
+ if (info != null)
+ {
+ // Leak detected, prior acquire exists (not released) or id clash.
+ return false;
+ }
+ // Normal behavior.
+ return true;
}
/**
* Tracks the resource as been released.
*
* @param resource the resource that has been released
- * @return whether the resource has been acquired
+ * @return true whether the resource has been released normally (based on a previous acquire). false if the resource
+ * has been released without a prior acquire (such as a double release scenario)
* @see #acquired(Object)
*/
public boolean released(T resource)
{
String id = id(resource);
- return resources.remove(id) != null;
+ LeakInfo info = resources.remove(id);
+ if (info != null)
+ {
+ // Normal behavior.
+ return true;
+ }
+
+ // Leak detected (released without acquire).
+ return false;
}
/**
* @param resource the resource to generate the unique ID for
* @return the unique ID of the given resource
*/
- protected String id(T resource)
+ public String id(T resource)
{
return String.valueOf(System.identityHashCode(resource));
}
protected void doStart() throws Exception
{
super.doStart();
- thread = new Thread(this, getClass().getSimpleName());
+ thread = new Thread(this,getClass().getSimpleName());
thread.setDaemon(true);
thread.start();
}
@Override
protected void doStop() throws Exception
{
- thread.interrupt();
super.doStop();
+ thread.interrupt();
}
@Override
{
@SuppressWarnings("unchecked")
LeakInfo leakInfo = (LeakInfo)queue.remove();
- LOG.debug("Resource GC'ed: {}", leakInfo);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Resource GC'ed: {}",leakInfo);
if (resources.remove(leakInfo.id) != null)
leaked(leakInfo);
}
*/
protected void leaked(LeakInfo leakInfo)
{
- LOG.warn("Resource leaked: " + leakInfo.description, leakInfo.stackFrames);
+ LOG.warn("Resource leaked: " + leakInfo.description,leakInfo.stackFrames);
}
/**
private LeakInfo(T referent, String id)
{
- super(referent, queue);
+ super(referent,queue);
this.id = id;
this.description = referent.toString();
this.stackFrames = new Throwable();
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.util;
-import java.io.PrintStream;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/* ------------------------------------------------------------ */
public void add(Throwable e)
{
+ if (e==null)
+ throw new IllegalArgumentException();
+
if(nested == null)
{
+ initCause(e);
nested = new ArrayList<>();
}
+ else
+ addSuppressed(e);
if (e instanceof MultiException)
{
/* ------------------------------------------------------------ */
public List<Throwable> getThrowables()
{
- if(nested == null) {
+ if(nested == null)
return Collections.emptyList();
- }
return nested;
}
return str.toString();
}
- /* ------------------------------------------------------------ */
- @Override
- public void printStackTrace()
- {
- super.printStackTrace();
- if(nested != null) {
- for(Throwable t: nested) {
- t.printStackTrace();
- }
- }
- }
-
-
- /* ------------------------------------------------------------------------------- */
- /**
- * @see java.lang.Throwable#printStackTrace(java.io.PrintStream)
- */
- @Override
- public void printStackTrace(PrintStream out)
- {
- super.printStackTrace(out);
- if(nested != null) {
- for(Throwable t: nested) {
- t.printStackTrace(out);
- }
- }
- }
-
- /* ------------------------------------------------------------------------------- */
- /**
- * @see java.lang.Throwable#printStackTrace(java.io.PrintWriter)
- */
- @Override
- public void printStackTrace(PrintWriter out)
- {
- super.printStackTrace(out);
- if(nested != null) {
- for(Throwable t: nested) {
- t.printStackTrace(out);
- }
- }
- }
-
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
protected InputStream _in;
protected MultipartConfigElement _config;
protected String _contentType;
- protected MultiMap _parts;
+ protected MultiMap<Part> _parts;
protected File _tmpDir;
protected File _contextTmpDir;
protected boolean _deleteOnExit;
protected OutputStream _out;
protected ByteArrayOutputStream2 _bout;
protected String _contentType;
- protected MultiMap _headers;
+ protected MultiMap<String> _headers;
protected long _size = 0;
protected boolean _temporary = true;
protected void open()
throws IOException
{
- //We will either be writing to a file, if it has a filename on the content-disposition
- //and otherwise a byte-array-input-stream, OR if we exceed the getFileSizeThreshold, we
- //will need to change to write to a file.
- if (_filename != null && _filename.trim().length() > 0)
- {
- createFile();
- }
- else
- {
- //Write to a buffer in memory until we discover we've exceed the
- //MultipartConfig fileSizeThreshold
- _out = _bout= new ByteArrayOutputStream2();
- }
+ //Write to a buffer in memory until we discover we've exceed the
+ //MultipartConfig fileSizeThreshold
+ _out = _bout= new ByteArrayOutputStream2();
}
protected void close()
if (MultiPartInputStreamParser.this._config.getFileSizeThreshold() > 0 && _size + 1 > MultiPartInputStreamParser.this._config.getFileSizeThreshold() && _file==null)
createFile();
+
_out.write(b);
_size ++;
}
if (MultiPartInputStreamParser.this._config.getFileSizeThreshold() > 0 && _size + length > MultiPartInputStreamParser.this._config.getFileSizeThreshold() && _file==null)
createFile();
-
+
_out.write(bytes, offset, length);
_size += length;
}
throws IOException
{
_file = File.createTempFile("MultiPart", "", MultiPartInputStreamParser.this._tmpDir);
+
if (_deleteOnExit)
_file.deleteOnExit();
FileOutputStream fos = new FileOutputStream(_file);
- protected void setHeaders(MultiMap headers)
+ protected void setHeaders(MultiMap<String> headers)
{
_headers = headers;
}
if (_parts == null)
return Collections.emptyList();
- Collection<Object> values = _parts.values();
+ Collection<List<Part>> values = _parts.values();
List<Part> parts = new ArrayList<Part>();
- for (Object o: values)
+ for (List<Part> o: values)
{
List<Part> asList = LazyList.getList(o, false);
parts.addAll(asList);
throws IOException, ServletException
{
parse();
- Collection<Object> values = _parts.values();
+ Collection<List<Part>> values = _parts.values();
List<Part> parts = new ArrayList<Part>();
- for (Object o: values)
+ for (List<Part> o: values)
{
List<Part> asList = LazyList.getList(o, false);
parts.addAll(asList);
//initialize
long total = 0; //keep running total of size of bytes read from input and throw an exception if exceeds MultipartConfigElement._maxRequestSize
- _parts = new MultiMap();
+ _parts = new MultiMap<Part>();
//if its not a multipart request, don't parse it
if (_contentType == null || !_contentType.startsWith("multipart/form-data"))
String contentType=null;
String contentTransferEncoding=null;
- MultiMap headers = new MultiMap();
+ MultiMap<String> headers = new MultiMap<String>();
while(true)
{
line=((ReadLineInputStream)_in).readLine();
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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;
+
+/**
+ * Temporary implementation of Java 8's <code>java.util.function.Predicate</code>
+ * <p>
+ * To be removed for Java 8 only versions of Jetty.
+ *
+ * @param <ITEM> the item to test
+ */
+public interface Predicate<ITEM>
+{
+ boolean test(ITEM item);
+}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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;
+
+import java.util.AbstractSet;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * A Set of Regular expressions strings.
+ * <p>
+ * Provides the efficient {@link #matches(String)} method to check for a match against all the combined Regex's
+ */
+public class RegexSet extends AbstractSet<String> implements Predicate<String>
+{
+ private final Set<String> _patterns=new HashSet<String>();
+ private final Set<String> _unmodifiable=Collections.unmodifiableSet(_patterns);
+ private Pattern _pattern;
+
+ @Override
+ public Iterator<String> iterator()
+ {
+ return _unmodifiable.iterator();
+ }
+
+ @Override
+ public int size()
+ {
+ return _patterns.size();
+ }
+
+ @Override
+ public boolean add(String pattern)
+ {
+ boolean added = _patterns.add(pattern);
+ if (added)
+ updatePattern();
+ return added;
+ }
+
+ @Override
+ public boolean remove(Object pattern)
+ {
+ boolean removed = _patterns.remove(pattern);
+
+ if (removed)
+ updatePattern();
+ return removed;
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ return _patterns.isEmpty();
+ }
+
+ @Override
+ public void clear()
+ {
+ _patterns.clear();
+ _pattern=null;
+ }
+
+ private void updatePattern()
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append("^(");
+ for (String pattern: _patterns)
+ {
+ if (builder.length()>2)
+ builder.append('|');
+ builder.append('(');
+ builder.append(pattern);
+ builder.append(')');
+ }
+ builder.append(")$");
+ _pattern = Pattern.compile(builder.toString());
+ }
+
+ @Override
+ public boolean test(String s)
+ {
+ return _pattern!=null && _pattern.matcher(s).matches();
+ }
+
+ public boolean matches(String s)
+ {
+ return _pattern!=null && _pattern.matcher(s).matches();
+ }
+}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
{
if ((_filter == null) || ((_filter != null) && _filter.accept(f.getParentFile(), f.getName())))
{
- LOG.debug("scan accepted {}",f);
+ if (LOG.isDebugEnabled())
+ LOG.debug("scan accepted {}",f);
String name = f.getCanonicalPath();
- scanInfoMap.put(name, new TimeNSize(f.lastModified(),f.length()));
+ scanInfoMap.put(name, new TimeNSize(f.lastModified(),f.isDirectory()?0:f.length()));
}
else
- LOG.debug("scan rejected {}",f);
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("scan rejected {}",f);
+ }
}
// If it is a directory, scan if it is a known directory or the depth is OK.
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
*/
public class SharedBlockingCallback
{
- private static final Logger LOG = Log.getLogger(SharedBlockingCallback.class);
+ static final Logger LOG = Log.getLogger(SharedBlockingCallback.class);
+
+ final ReentrantLock _lock = new ReentrantLock();
+ final Condition _idle = _lock.newCondition();
+ final Condition _complete = _lock.newCondition();
private static Throwable IDLE = new Throwable()
}
};
- final Blocker _blocker;
+ Blocker _blocker;
public SharedBlockingCallback()
{
- this(new Blocker());
+ _blocker=new Blocker();
}
- protected SharedBlockingCallback(Blocker blocker)
+ protected long getIdleTimeout()
{
- _blocker=blocker;
+ return -1;
}
public Blocker acquire() throws IOException
{
- _blocker._lock.lock();
+ _lock.lock();
+ long idle = getIdleTimeout();
try
{
while (_blocker._state != IDLE)
- _blocker._idle.await();
+ {
+ if (idle>0 && (idle < Long.MAX_VALUE/2))
+ {
+ // Wait a little bit longer than the blocker might block
+ if (!_idle.await(idle*2,TimeUnit.MILLISECONDS))
+ throw new IOException(new TimeoutException());
+ }
+ else
+ _idle.await();
+ }
_blocker._state = null;
}
catch (final InterruptedException e)
{
- throw new InterruptedIOException()
- {
- {
- initCause(e);
- }
- };
+ throw new InterruptedIOException();
}
finally
{
- _blocker._lock.unlock();
+ _lock.unlock();
}
return _blocker;
}
+ protected void notComplete(Blocker blocker)
+ {
+ LOG.warn("Blocker not complete {}",blocker);
+ if (LOG.isDebugEnabled())
+ LOG.debug(new Throwable());
+ }
/* ------------------------------------------------------------ */
/** A Closeable Callback.
* Uses the auto close mechanism to check block has been called OK.
*/
- public static class Blocker implements Callback, Closeable
+ public class Blocker implements Callback, Closeable
{
- final ReentrantLock _lock = new ReentrantLock();
- final Condition _idle = _lock.newCondition();
- final Condition _complete = _lock.newCondition();
Throwable _state = IDLE;
-
- public Blocker()
+
+ protected Blocker()
{
}
_state = SUCCEEDED;
_complete.signalAll();
}
- else if (_state == IDLE)
- throw new IllegalStateException("IDLE");
+ else
+ throw new IllegalStateException(_state);
}
finally
{
{
if (_state == null)
{
- // TODO remove when feedback received on 435322
if (cause==null)
- LOG.warn("null failed cause (please report stack trace) ",new Throwable());
- _state = cause==null?FAILED:cause;
+ _state=FAILED;
+ else if (cause instanceof BlockerTimeoutException)
+ // Not this blockers timeout
+ _state=new IOException(cause);
+ else
+ _state=cause;
_complete.signalAll();
}
- else if (_state == IDLE)
- throw new IllegalStateException("IDLE");
+ else
+ throw new IllegalStateException(_state);
}
finally
{
LOG.warn("Blocking a NonBlockingThread: ",new Throwable());
_lock.lock();
+ long idle = getIdleTimeout();
try
{
while (_state == null)
{
- // TODO remove this debug timout!
- // This is here to help debug 435322,
- if (!_complete.await(10,TimeUnit.MINUTES))
+ if (idle>0 && (idle < Long.MAX_VALUE/2))
{
- IOException x = new IOException("DEBUG timeout");
- LOG.warn("Blocked too long (please report!!!) "+this, x);
- _state=x;
+ // Wait a little bit longer than expected callback idle timeout
+ if (!_complete.await(idle+idle/2,TimeUnit.MILLISECONDS))
+ // The callback has not arrived in sufficient time.
+ // We will synthesize a TimeoutException
+ _state=new BlockerTimeoutException();
}
+ else
+ _complete.await();
}
if (_state == SUCCEEDED)
}
catch (final InterruptedException e)
{
- throw new InterruptedIOException()
- {
- {
- initCause(e);
- }
- };
+ throw new InterruptedIOException();
}
finally
{
if (_state == IDLE)
throw new IllegalStateException("IDLE");
if (_state == null)
- LOG.debug("Blocker not complete",new Throwable());
+ notComplete(this);
}
finally
{
- _state = IDLE;
- _idle.signalAll();
- _lock.unlock();
+ try
+ {
+ // If the blocker timed itself out, remember the state
+ if (_state instanceof BlockerTimeoutException)
+ // and create a new Blocker
+ _blocker=new Blocker();
+ else
+ // else reuse Blocker
+ _state = IDLE;
+ _idle.signalAll();
+ _complete.signalAll();
+ }
+ finally
+ {
+ _lock.unlock();
+ }
}
}
_lock.lock();
try
{
- return String.format("%s@%x{%s}",SharedBlockingCallback.class.getSimpleName(),hashCode(),_state);
+ return String.format("%s@%x{%s}",Blocker.class.getSimpleName(),hashCode(),_state);
}
finally
{
}
}
}
+
+ private static class BlockerTimeoutException extends TimeoutException
+ {
+ }
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import org.eclipse.jetty.util.thread.Scheduler;
/**
- * Creates asynchronously {@link SocketAddress} instances, returning them through a {@link Promise},
- * in order to avoid blocking on DNS lookup.
- * <p />
- * {@link InetSocketAddress#InetSocketAddress(String, int)} attempts to perform a DNS resolution of
- * the host name, and this may block for several seconds.
- * This class creates the {@link InetSocketAddress} in a separate thread and provides the result
- * through a {@link Promise}, with the possibility to specify a timeout for the operation.
- * <p />
- * Example usage:
- * <pre>
- * SocketAddressResolver resolver = new SocketAddressResolver(executor, scheduler);
- * resolver.resolve("www.google.com", 80, new Promise<SocketAddress>()
- * {
- * public void succeeded(SocketAddress result)
- * {
- * // The address was resolved
- * }
- *
- * public void failed(Throwable failure)
- * {
- * // The address resolution failed
- * }
- * });
- * </pre>
+ * <p>Creates {@link SocketAddress} instances, returning them through a {@link Promise}.</p>
*/
-public class SocketAddressResolver
+public interface SocketAddressResolver
{
- private static final Logger LOG = Log.getLogger(SocketAddressResolver.class);
-
- private final Executor executor;
- private final Scheduler scheduler;
- private final long timeout;
-
- /**
- * Creates a new instance with the given executor (to perform DNS resolution in a separate thread),
- * the given scheduler (to cancel the operation if it takes too long) and the given timeout, in milliseconds.
- *
- * @param executor the thread pool to use to perform DNS resolution in pooled threads
- * @param scheduler the scheduler to schedule tasks to cancel DNS resolution if it takes too long
- * @param timeout the timeout, in milliseconds, for the DNS resolution to complete
- */
- public SocketAddressResolver(Executor executor, Scheduler scheduler, long timeout)
- {
- this.executor = executor;
- this.scheduler = scheduler;
- this.timeout = timeout;
- }
-
- public Executor getExecutor()
- {
- return executor;
- }
-
- public Scheduler getScheduler()
- {
- return scheduler;
- }
-
- public long getTimeout()
- {
- return timeout;
- }
-
/**
* Resolves the given host and port, returning a {@link SocketAddress} through the given {@link Promise}
* with the default timeout.
* @param host the host to resolve
* @param port the port of the resulting socket address
* @param promise the callback invoked when the resolution succeeds or fails
- * @see #resolve(String, int, long, Promise)
*/
- public void resolve(String host, int port, Promise<SocketAddress> promise)
+ public void resolve(String host, int port, Promise<SocketAddress> promise);
+
+ /**
+ * <p>Creates {@link SocketAddress} instances synchronously in the caller thread.</p>
+ */
+ public static class Sync implements SocketAddressResolver
{
- resolve(host, port, timeout, promise);
+ @Override
+ public void resolve(String host, int port, Promise<SocketAddress> promise)
+ {
+ try
+ {
+ InetSocketAddress result = new InetSocketAddress(host, port);
+ if (result.isUnresolved())
+ promise.failed(new UnresolvedAddressException());
+ else
+ promise.succeeded(result);
+ }
+ catch (Throwable x)
+ {
+ promise.failed(x);
+ }
+ }
}
/**
- * Resolves the given host and port, returning a {@link SocketAddress} through the given {@link Promise}
- * with the given timeout.
+ * <p>Creates {@link SocketAddress} instances asynchronously in a different thread.</p>
+ * <p>{@link InetSocketAddress#InetSocketAddress(String, int)} attempts to perform a DNS
+ * resolution of the host name, and this may block for several seconds.
+ * This class creates the {@link InetSocketAddress} in a separate thread and provides the result
+ * through a {@link Promise}, with the possibility to specify a timeout for the operation.</p>
+ * <p>Example usage:</p>
+ * <pre>
+ * SocketAddressResolver resolver = new SocketAddressResolver.Async(executor, scheduler, timeout);
+ * resolver.resolve("www.google.com", 80, new Promise<SocketAddress>()
+ * {
+ * public void succeeded(SocketAddress result)
+ * {
+ * // The address was resolved
+ * }
*
- * @param host the host to resolve
- * @param port the port of the resulting socket address
- * @param timeout the timeout, in milliseconds, for the DNS resolution to complete
- * @param promise the callback invoked when the resolution succeeds or fails
+ * public void failed(Throwable failure)
+ * {
+ * // The address resolution failed
+ * }
+ * });
+ * </pre>
*/
- protected void resolve(final String host, final int port, final long timeout, final Promise<SocketAddress> promise)
+ public static class Async implements SocketAddressResolver
{
- executor.execute(new Runnable()
+ private static final Logger LOG = Log.getLogger(SocketAddressResolver.class);
+
+ private final Executor executor;
+ private final Scheduler scheduler;
+ private final long timeout;
+
+ /**
+ * Creates a new instance with the given executor (to perform DNS resolution in a separate thread),
+ * the given scheduler (to cancel the operation if it takes too long) and the given timeout, in milliseconds.
+ *
+ * @param executor the thread pool to use to perform DNS resolution in pooled threads
+ * @param scheduler the scheduler to schedule tasks to cancel DNS resolution if it takes too long
+ * @param timeout the timeout, in milliseconds, for the DNS resolution to complete
+ */
+ public Async(Executor executor, Scheduler scheduler, long timeout)
+ {
+ this.executor = executor;
+ this.scheduler = scheduler;
+ this.timeout = timeout;
+ }
+
+ public Executor getExecutor()
+ {
+ return executor;
+ }
+
+ public Scheduler getScheduler()
+ {
+ return scheduler;
+ }
+
+ public long getTimeout()
{
- @Override
- public void run()
+ return timeout;
+ }
+
+ @Override
+ public void resolve(final String host, final int port, final Promise<SocketAddress> promise)
+ {
+ executor.execute(new Runnable()
{
- Scheduler.Task task = null;
- final AtomicBoolean complete = new AtomicBoolean();
- if (timeout > 0)
+ @Override
+ public void run()
{
- final Thread thread = Thread.currentThread();
- task = scheduler.schedule(new Runnable()
+ Scheduler.Task task = null;
+ final AtomicBoolean complete = new AtomicBoolean();
+ if (timeout > 0)
{
- @Override
- public void run()
+ final Thread thread = Thread.currentThread();
+ task = scheduler.schedule(new Runnable()
{
- if (complete.compareAndSet(false, true))
+ @Override
+ public void run()
{
- promise.failed(new TimeoutException());
- thread.interrupt();
+ if (complete.compareAndSet(false, true))
+ {
+ promise.failed(new TimeoutException());
+ thread.interrupt();
+ }
}
- }
- }, timeout, TimeUnit.MILLISECONDS);
- }
+ }, timeout, TimeUnit.MILLISECONDS);
+ }
- try
- {
- long start = System.nanoTime();
- InetSocketAddress result = new InetSocketAddress(host, port);
- long elapsed = System.nanoTime() - start;
- LOG.debug("Resolved {} in {} ms", host, TimeUnit.NANOSECONDS.toMillis(elapsed));
- if (complete.compareAndSet(false, true))
+ try
{
- if (result.isUnresolved())
- promise.failed(new UnresolvedAddressException());
- else
- promise.succeeded(result);
+ long start = System.nanoTime();
+ InetSocketAddress result = new InetSocketAddress(host, port);
+ long elapsed = System.nanoTime() - start;
+ if (LOG.isDebugEnabled())
+ LOG.debug("Resolved {} in {} ms", host, TimeUnit.NANOSECONDS.toMillis(elapsed));
+ if (complete.compareAndSet(false, true))
+ {
+ if (result.isUnresolved())
+ promise.failed(new UnresolvedAddressException());
+ else
+ promise.succeeded(result);
+ }
+ }
+ catch (Throwable x)
+ {
+ if (complete.compareAndSet(false, true))
+ promise.failed(x);
+ }
+ finally
+ {
+ if (task != null)
+ task.cancel();
+ // Reset the interrupted status before releasing the thread to the pool
+ Thread.interrupted();
}
}
- catch (Throwable x)
- {
- if (complete.compareAndSet(false, true))
- promise.failed(x);
- }
- finally
- {
- if (task != null)
- task.cancel();
- // Reset the interrupted status before releasing the thread to the pool
- Thread.interrupted();
- }
- }
- });
+ });
+ }
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public static final String ALL_INTERFACES="0.0.0.0";
public static final String CRLF="\015\012";
- public static final String __LINE_SEPARATOR=
- System.getProperty("line.separator","\n");
+
+ /** @deprecated use {@link System#lineSeparator()} instead */
+ @Deprecated
+ public static final String __LINE_SEPARATOR = System.lineSeparator();
public static final String __ISO_8859_1="ISO-8859-1";
public final static String __UTF8="UTF-8";
}
}
+ /**
+ * Find the index of a control characters in String
+ * <p>
+ * This will return a result on the first occurrence of a control character, regardless if
+ * there are more than one.
+ * </p>
+ * <p>
+ * Note: uses codepoint version of {@link Character#isISOControl(int)} to support Unicode better.
+ * </p>
+ *
+ * <pre>
+ * indexOfControlChars(null) == -1
+ * indexOfControlChars("") == -1
+ * indexOfControlChars("\r\n") == 0
+ * indexOfControlChars("\t") == 0
+ * indexOfControlChars(" ") == -1
+ * indexOfControlChars("a") == -1
+ * indexOfControlChars(".") == -1
+ * indexOfControlChars(";\n") == 1
+ * indexOfControlChars("abc\f") == 3
+ * indexOfControlChars("z\010") == 1
+ * indexOfControlChars(":\u001c") == 1
+ * </pre>
+ *
+ * @param str
+ * the string to test.
+ * @return the index of first control character in string, -1 if no control characters encountered
+ */
+ public static int indexOfControlChars(String str)
+ {
+ if (str == null)
+ {
+ return -1;
+ }
+ int len = str.length();
+ for (int i = 0; i < len; i++)
+ {
+ if (Character.isISOControl(str.codePointAt(i)))
+ {
+ // found a control character, we can stop searching now
+ return i;
+ }
+ }
+ // no control characters
+ return -1;
+ }
+
/* ------------------------------------------------------------ */
/**
* Test if a string is null or only has whitespace characters in it.
return str.substring(0,maxSize);
}
+ /**
+ * Parse the string representation of a list using {@link #csvSplit(List,String,int,int)}
+ * @param s The string to parse, expected to be enclosed as '[...]'
+ * @return An array of parsed values.
+ */
public static String[] arrayFromString(String s)
{
if (s==null)
if (s.length()==2)
return new String[]{};
- return s.substring(1,s.length()-1).split(" *, *");
+ return csvSplit(s,1,s.length()-2);
+ }
+
+ /**
+ * Parse a CSV string using {@link #csvSplit(List,String, int, int)}
+ * @param s The string to parse
+ * @return An array of parsed values.
+ */
+ public static String[] csvSplit(String s)
+ {
+ if (s==null)
+ return null;
+ return csvSplit(s,0,s.length());
+ }
+
+ /**
+ * Parse a CSV string using {@link #csvSplit(List,String, int, int)}
+ * @param s The string to parse
+ * @param off The offset into the string to start parsing
+ * @param len The len in characters to parse
+ * @return An array of parsed values.
+ */
+ public static String[] csvSplit(String s, int off,int len)
+ {
+ if (s==null)
+ return null;
+ if (off<0 || len<0 || off>s.length())
+ throw new IllegalArgumentException();
+
+ List<String> list = new ArrayList<>();
+ csvSplit(list,s,off,len);
+ return list.toArray(new String[list.size()]);
+ }
+
+ enum CsvSplitState { PRE_DATA, QUOTE, SLOSH, DATA, WHITE, POST_DATA };
+
+ /** Split a quoted comma separated string to a list
+ * <p>Handle <a href="https://www.ietf.org/rfc/rfc4180.txt">rfc4180</a>-like
+ * CSV strings, with the exceptions:<ul>
+ * <li>quoted values may contain double quotes escaped with back-slash
+ * <li>Non-quoted values are trimmed of leading trailing white space
+ * <li>trailing commas are ignored
+ * <li>double commas result in a empty string value
+ * </ul>
+ * @param list The Collection to split to (or null to get a new list)
+ * @param s The string to parse
+ * @param off The offset into the string to start parsing
+ * @param len The len in characters to parse
+ * @return list containing the parsed list values
+ */
+ public static List<String> csvSplit(List<String> list,String s, int off,int len)
+ {
+ if (list==null)
+ list=new ArrayList<>();
+ CsvSplitState state = CsvSplitState.PRE_DATA;
+ StringBuilder out = new StringBuilder();
+ int last=-1;
+ while (len>0)
+ {
+ char ch = s.charAt(off++);
+ len--;
+
+ switch(state)
+ {
+ case PRE_DATA:
+ if (Character.isWhitespace(ch))
+ continue;
+
+ if ('"'==ch)
+ {
+ state=CsvSplitState.QUOTE;
+ continue;
+ }
+
+ if (','==ch)
+ {
+ list.add("");
+ continue;
+ }
+
+ state=CsvSplitState.DATA;
+ out.append(ch);
+ continue;
+
+ case DATA:
+ if (Character.isWhitespace(ch))
+ {
+ last=out.length();
+ out.append(ch);
+ state=CsvSplitState.WHITE;
+ continue;
+ }
+
+ if (','==ch)
+ {
+ list.add(out.toString());
+ out.setLength(0);
+ state=CsvSplitState.PRE_DATA;
+ continue;
+ }
+
+ out.append(ch);
+ continue;
+
+ case WHITE:
+ if (Character.isWhitespace(ch))
+ {
+ out.append(ch);
+ continue;
+ }
+
+ if (','==ch)
+ {
+ out.setLength(last);
+ list.add(out.toString());
+ out.setLength(0);
+ state=CsvSplitState.PRE_DATA;
+ continue;
+ }
+
+ state=CsvSplitState.DATA;
+ out.append(ch);
+ last=-1;
+ continue;
+
+ case QUOTE:
+ if ('\\'==ch)
+ {
+ state=CsvSplitState.SLOSH;
+ continue;
+ }
+ if ('"'==ch)
+ {
+ list.add(out.toString());
+ out.setLength(0);
+ state=CsvSplitState.POST_DATA;
+ continue;
+ }
+ out.append(ch);
+ continue;
+
+ case SLOSH:
+ out.append(ch);
+ state=CsvSplitState.QUOTE;
+ continue;
+
+ case POST_DATA:
+ if (','==ch)
+ {
+ state=CsvSplitState.PRE_DATA;
+ continue;
+ }
+ continue;
+ }
+ }
+
+ switch(state)
+ {
+ case PRE_DATA:
+ case POST_DATA:
+ break;
+
+ case DATA:
+ case QUOTE:
+ case SLOSH:
+ list.add(out.toString());
+ break;
+
+ case WHITE:
+ out.setLength(last);
+ list.add(out.toString());
+ break;
+ }
+
+ return list;
+ }
+
+ public static String sanitizeXmlString(String html)
+ {
+ if (html==null)
+ return null;
+
+ int i=0;
+
+ // Are there any characters that need sanitizing?
+ loop: for (;i<html.length();i++)
+ {
+ char c=html.charAt(i);
+
+ switch(c)
+ {
+ case '&' :
+ case '<' :
+ case '>' :
+ case '\'':
+ case '"':
+ break loop;
+
+ default:
+ if (Character.isISOControl(c) && !Character.isWhitespace(c))
+ break loop;
+ }
+ }
+
+ // No characters need sanitizing, so return original string
+ if (i==html.length())
+ return html;
+
+ // Create builder with OK content so far
+ StringBuilder out = new StringBuilder(html.length()*4/3);
+ out.append(html,0,i);
+
+ // sanitize remaining content
+ for (;i<html.length();i++)
+ {
+ char c=html.charAt(i);
+
+ switch(c)
+ {
+ case '&' :
+ out.append("&");
+ break;
+ case '<' :
+ out.append("<");
+ break;
+ case '>' :
+ out.append(">");
+ break;
+ case '\'':
+ out.append("'");
+ break;
+ case '"':
+ out.append(""");
+ break;
+
+ default:
+ if (Character.isISOControl(c) && !Character.isWhitespace(c))
+ out.append('?');
+ else
+ out.append(c);
+ }
+ }
+ return out.toString();
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
* <p>This implementation is always case insensitive and is optimal for
* a variable number of fixed strings with few special characters.
* </p>
+ * <p>This Trie is stored in a Tree and is unlimited in capacity</p>
+ *
+ * <p>This Trie is not Threadsafe and contains no mutual exclusion
+ * or deliberate memory barriers. It is intended for an ArrayTrie to be
+ * built by a single thread and then used concurrently by multiple threads
+ * and not mutated during that access. If concurrent mutations of the
+ * Trie is required external locks need to be applied.
+ * </p>
+ *
* @param <V>
*/
public class TreeTrie<V> extends AbstractTrie<V>
private static final int[] __lookup =
{ // 0 1 2 3 4 5 6 7 8 9 A B C D E F
/*0*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- /*1*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 30,
- /*2*/31, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, -1, 27, -1, -1,
+ /*1*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ /*2*/31, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, -1, 27, 30, -1,
/*3*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 28, 29, -1, -1, -1, -1,
/*4*/-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
/*5*/15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
// target has no annotations
if ( parameterAnnotations == null || parameterAnnotations.length == 0 )
- {
- LOG.debug("Target has no parameter annotations");
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Target has no parameter annotations");
return constructor.newInstance(arguments);
}
else
if (namedArgMap.containsKey(param.value()))
{
- LOG.debug("placing named {} in position {}", param.value(), count);
+ if (LOG.isDebugEnabled())
+ LOG.debug("placing named {} in position {}", param.value(), count);
swizzled[count] = namedArgMap.get(param.value());
}
else
{
- LOG.debug("placing {} in position {}", arguments[count], count);
+ if (LOG.isDebugEnabled())
+ LOG.debug("placing {} in position {}", arguments[count], count);
swizzled[count] = arguments[count];
}
++count;
}
else
{
- LOG.debug("passing on annotation {}", annotation);
+ if (LOG.isDebugEnabled())
+ LOG.debug("passing on annotation {}", annotation);
}
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
}
return false;
}
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Create a new URI from the arguments, handling IPv6 host encoding and default ports
+ * @param scheme
+ * @param server
+ * @param port
+ * @param path
+ * @param query
+ * @return A String URI
+ */
+ public static String newURI(String scheme,String server, int port,String path,String query)
+ {
+ StringBuilder builder = newURIBuilder(scheme, server, port);
+ builder.append(path);
+ if (query!=null && query.length()>0)
+ builder.append('?').append(query);
+ return builder.toString();
+ }
+ /* ------------------------------------------------------------ */
+ /**
+ * Create a new URI StringBuilder from the arguments, handling IPv6 host encoding and default ports
+ * @param scheme
+ * @param server
+ * @param port
+ * @return a StringBuilder containing URI prefix
+ */
+ public static StringBuilder newURIBuilder(String scheme,String server, int port)
+ {
+ StringBuilder builder = new StringBuilder();
+ appendSchemeHostPort(builder, scheme, server, port);
+ return builder;
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Append scheme, host and port URI prefix, handling IPv6 address encoding and default ports</p>
+ * @param url StringBuilder to append to
+ * @param scheme
+ * @param server
+ * @param port
+ */
public static void appendSchemeHostPort(StringBuilder url,String scheme,String server, int port)
{
if (server.indexOf(':')>=0&&server.charAt(0)!='[')
else
url.append(scheme).append("://").append(server);
- if (port > 0 && (("http".equalsIgnoreCase(scheme) && port != 80) || ("https".equalsIgnoreCase(scheme) && port != 443)))
- url.append(':').append(port);
+ if (port > 0)
+ {
+ switch(scheme)
+ {
+ case "http":
+ if (port!=80)
+ url.append(':').append(port);
+ break;
+
+ case "https":
+ if (port!=443)
+ url.append(':').append(port);
+ break;
+
+ default:
+ url.append(':').append(port);
+ }
+ }
}
+ /* ------------------------------------------------------------ */
+ /**
+ * Append scheme, host and port URI prefix, handling IPv6 address encoding and default ports</p>
+ * @param url StringBuffer to append to
+ * @param scheme
+ * @param server
+ * @param port
+ */
public static void appendSchemeHostPort(StringBuffer url,String scheme,String server, int port)
{
synchronized (url)
else
url.append(scheme).append("://").append(server);
- if (port > 0 && (("http".equalsIgnoreCase(scheme) && port != 80) || ("https".equalsIgnoreCase(scheme) && port != 443)))
- url.append(':').append(port);
+ if (port > 0)
+ {
+ switch(scheme)
+ {
+ case "http":
+ if (port!=80)
+ url.append(':').append(port);
+ break;
+
+ case "https":
+ if (port!=443)
+ url.append(':').append(port);
+ break;
+
+ default:
+ url.append(':').append(port);
+ }
+ }
+ }
+ }
+
+ public static boolean equalsIgnoreEncodings(String uriA, String uriB)
+ {
+ int lenA=uriA.length();
+ int lenB=uriB.length();
+ int a=0;
+ int b=0;
+
+ while (a<lenA && b<lenB)
+ {
+ int oa=uriA.charAt(a++);
+ int ca=oa;
+ if (ca=='%')
+ ca=TypeUtil.convertHexDigit(uriA.charAt(a++))*16+TypeUtil.convertHexDigit(uriA.charAt(a++));
+
+ int ob=uriB.charAt(b++);
+ int cb=ob;
+ if (cb=='%')
+ cb=TypeUtil.convertHexDigit(uriB.charAt(b++))*16+TypeUtil.convertHexDigit(uriB.charAt(b++));
+
+ if (ca=='/' && oa!=ob)
+ return false;
+
+ if (ca!=cb )
+ return URIUtil.decodePath(uriA).equals(URIUtil.decodePath(uriB));
}
+ return a==lenA && b==lenB;
}
}
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Provide for a Uptime class that is compatible with Android, GAE, and the new Java 8 compact profiles
+ */
+public class Uptime
+{
+ public static final int NOIMPL = -1;
+
+ public static interface Impl
+ {
+ public long getUptime();
+ }
+
+ public static class DefaultImpl implements Impl
+ {
+ public Object mxBean;
+ public Method uptimeMethod;
+
+ public DefaultImpl()
+ {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ try
+ {
+ Class<?> mgmtFactory = Class.forName("java.lang.management.ManagementFactory",true,cl);
+ Class<?> runtimeClass = Class.forName("java.lang.management.RuntimeMXBean",true,cl);
+ Class<?> noparams[] = new Class<?>[0];
+ Method mxBeanMethod = mgmtFactory.getMethod("getRuntimeMXBean",noparams);
+ if (mxBeanMethod == null)
+ {
+ throw new UnsupportedOperationException("method getRuntimeMXBean() not found");
+ }
+ mxBean = mxBeanMethod.invoke(mgmtFactory);
+ if (mxBean == null)
+ {
+ throw new UnsupportedOperationException("getRuntimeMXBean() method returned null");
+ }
+ uptimeMethod = runtimeClass.getMethod("getUptime",noparams);
+ if (mxBean == null)
+ {
+ throw new UnsupportedOperationException("method getUptime() not found");
+ }
+ }
+ catch (ClassNotFoundException |
+ NoClassDefFoundError |
+ NoSuchMethodException |
+ SecurityException |
+ IllegalAccessException |
+ IllegalArgumentException |
+ InvocationTargetException e)
+ {
+ throw new UnsupportedOperationException("Implementation not available in this environment",e);
+ }
+ }
+
+ @Override
+ public long getUptime()
+ {
+ try
+ {
+ return (long)uptimeMethod.invoke(mxBean);
+ }
+ catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
+ {
+ return NOIMPL;
+ }
+ }
+ }
+
+ private static final Uptime INSTANCE = new Uptime();
+
+ public static Uptime getInstance()
+ {
+ return INSTANCE;
+ }
+
+ private Impl impl;
+
+ private Uptime()
+ {
+ try
+ {
+ impl = new DefaultImpl();
+ }
+ catch (UnsupportedOperationException e)
+ {
+ System.err.printf("Defaulting Uptime to NOIMPL due to (%s) %s%n",e.getClass().getName(),e.getMessage());
+ impl = null;
+ }
+ }
+
+ public Impl getImpl()
+ {
+ return impl;
+ }
+
+ public void setImpl(Impl impl)
+ {
+ this.impl = impl;
+ }
+
+ public static long getUptime()
+ {
+ Uptime u = getInstance();
+ if (u == null || u.impl == null)
+ {
+ return NOIMPL;
+ }
+ return u.impl.getUptime();
+ }
+}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
int c;
int totalLength = 0;
- ByteArrayOutputStream2 output = new ByteArrayOutputStream2();
- int size=0;
-
- while ((c=in.read())>0)
+ try(ByteArrayOutputStream2 output = new ByteArrayOutputStream2();)
{
- switch ((char) c)
+ int size=0;
+
+ while ((c=in.read())>0)
{
- case '&':
- size=output.size();
- value = size==0?"":output.toString(charset);
- output.setCount(0);
- if (key != null)
- {
- map.add(key,value);
- }
- else if (value!=null&&value.length()>0)
- {
- map.add(value,"");
- }
- key = null;
- value=null;
- if (maxKeys>0 && map.size()>maxKeys)
- throw new IllegalStateException("Form too many keys");
- break;
- case '=':
- if (key!=null)
- {
- output.write(c);
+ switch ((char) c)
+ {
+ case '&':
+ size=output.size();
+ value = size==0?"":output.toString(charset);
+ output.setCount(0);
+ if (key != null)
+ {
+ map.add(key,value);
+ }
+ else if (value!=null&&value.length()>0)
+ {
+ map.add(value,"");
+ }
+ key = null;
+ value=null;
+ if (maxKeys>0 && map.size()>maxKeys)
+ throw new IllegalStateException("Form too many keys");
break;
- }
- size=output.size();
- key = size==0?"":output.toString(charset);
- output.setCount(0);
- break;
- case '+':
- output.write(' ');
- break;
- case '%':
- int code0=in.read();
- if ('u'==code0)
- {
- int code1=in.read();
- if (code1>=0)
+ case '=':
+ if (key!=null)
{
- int code2=in.read();
- if (code2>=0)
+ output.write(c);
+ break;
+ }
+ size=output.size();
+ key = size==0?"":output.toString(charset);
+ output.setCount(0);
+ break;
+ case '+':
+ output.write(' ');
+ break;
+ case '%':
+ int code0=in.read();
+ if ('u'==code0)
+ {
+ int code1=in.read();
+ if (code1>=0)
{
- int code3=in.read();
- if (code3>=0)
- output.write(new String(Character.toChars((convertHexDigit(code0)<<12)+(convertHexDigit(code1)<<8)+(convertHexDigit(code2)<<4)+convertHexDigit(code3))).getBytes(charset));
+ int code2=in.read();
+ if (code2>=0)
+ {
+ int code3=in.read();
+ if (code3>=0)
+ output.write(new String(Character.toChars((convertHexDigit(code0)<<12)+(convertHexDigit(code1)<<8)+(convertHexDigit(code2)<<4)+convertHexDigit(code3))).getBytes(charset));
+ }
}
+
}
-
- }
- else if (code0>=0)
- {
- int code1=in.read();
- if (code1>=0)
- output.write((convertHexDigit(code0)<<4)+convertHexDigit(code1));
- }
- break;
- default:
- output.write(c);
- break;
+ else if (code0>=0)
+ {
+ int code1=in.read();
+ if (code1>=0)
+ output.write((convertHexDigit(code0)<<4)+convertHexDigit(code1));
+ }
+ break;
+ default:
+ output.write(c);
+ break;
+ }
+
+ totalLength++;
+ if (maxLength>=0 && totalLength > maxLength)
+ throw new IllegalStateException("Form too large");
}
-
- totalLength++;
- if (maxLength>=0 && totalLength > maxLength)
- throw new IllegalStateException("Form too large");
- }
- size=output.size();
- if (key != null)
- {
- value = size==0?"":output.toString(charset);
- output.setCount(0);
- map.add(key,value);
+ size=output.size();
+ if (key != null)
+ {
+ value = size==0?"":output.toString(charset);
+ output.setCount(0);
+ map.add(key,value);
+ }
+ else if (size>0)
+ map.add(output.toString(charset),"");
}
- else if (size>0)
- map.add(output.toString(charset),"");
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.nio.ByteBuffer;
-import org.eclipse.jetty.util.Utf8Appendable.NotUtf8Exception;
-
/**
* Stateful parser for lines of UTF8 formatted text, looking for <code>"\n"</code> as a line termination character.
* <p>
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.util.component;
-import java.lang.management.ManagementFactory;
import java.util.concurrent.CopyOnWriteArrayList;
+import org.eclipse.jetty.util.Uptime;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.log.Log;
{
_state = __STARTED;
if (LOG.isDebugEnabled())
-
- LOG.debug(STARTED+" @{}ms {}",ManagementFactory.getRuntimeMXBean().getUptime(),this);
+ LOG.debug(STARTED+" @{}ms {}",Uptime.getUptime(),this);
for (Listener listener : _listeners)
listener.lifeCycleStarted(this);
}
private void setStarting()
{
- LOG.debug("starting {}",this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("starting {}",this);
_state = __STARTING;
for (Listener listener : _listeners)
listener.lifeCycleStarting(this);
private void setStopping()
{
- LOG.debug("stopping {}",this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("stopping {}",this);
_state = __STOPPING;
for (Listener listener : _listeners)
listener.lifeCycleStopping(this);
private void setStopped()
{
_state = __STOPPED;
- LOG.debug("{} {}",STOPPED,this);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} {}",STOPPED,this);
for (Listener listener : _listeners)
listener.lifeCycleStopped(this);
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
throw new RuntimeException(e);
}
- LOG.debug("{} added {}",this,new_bean);
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} added {}",this,new_bean);
return true;
}
{
if (_beans.remove(bean))
{
+ boolean wasManaged = bean.isManaged();
unmanage(bean);
removeEventListener((Container.Listener)bean._bean);
// stop managed beans
- if (bean._managed==Managed.MANAGED && bean._bean instanceof LifeCycle)
+ if (wasManaged && bean._bean instanceof LifeCycle)
{
try
{
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
{
if (file.exists())
{
- LOG.debug("Destroy {}",file);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Destroy {}",file);
IO.delete(file);
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
public void debug(String msg, long arg)
{
if (isDebugEnabled())
- debug(msg,new Long(arg));
+ {
+ debug(msg,new Object[] { new Long(arg) });
+ }
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.io.IOException;
import java.io.InputStream;
-import java.lang.management.ManagementFactory;
import java.lang.reflect.Method;
import java.net.URL;
import java.security.AccessController;
import java.util.concurrent.ConcurrentMap;
import org.eclipse.jetty.util.Loader;
+import org.eclipse.jetty.util.Uptime;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
/**
return;
__initialized = true;
- final long uptime=ManagementFactory.getRuntimeMXBean().getUptime();
-
try
{
Class<?> log_class = Loader.loadClass(Log.class, __logClass);
}
if (LOG!=null)
- LOG.info(String.format("Logging initialized @%dms",uptime));
+ LOG.info(String.format("Logging initialized @%dms",Uptime.getUptime()));
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
*/
public static int getLoggingLevel(Properties props, final String name)
{
+ if ((props == null) || (props.isEmpty()))
+ {
+ // Default Logging Level
+ return getLevelId("log.LEVEL","INFO");
+ }
+
// Calculate the level this named logger should operate under.
// Checking with FQCN first, then each package segment from longest to shortest.
String nameSegment = name;
builder.append(string);
}
- private void format(StringBuilder buffer, Throwable thrown)
+ protected void format(StringBuilder buffer, Throwable thrown)
+ {
+ format(buffer,thrown,"");
+ }
+
+ protected void format(StringBuilder buffer, Throwable thrown, String indent)
{
if (thrown == null)
{
}
else
{
- buffer.append(EOL);
+ buffer.append(EOL).append(indent);
format(buffer,thrown.toString());
StackTraceElement[] elements = thrown.getStackTrace();
for (int i = 0; elements != null && i < elements.length; i++)
{
- buffer.append(EOL).append("\tat ");
+ buffer.append(EOL).append(indent).append("\tat ");
format(buffer,elements[i].toString());
}
+ for (Throwable suppressed:thrown.getSuppressed())
+ {
+ buffer.append(EOL).append(indent).append("Suppressed: ");
+ format(buffer,suppressed,"\t|"+indent);
+ }
+
Throwable cause = thrown.getCause();
if (cause != null && cause != thrown)
{
- buffer.append(EOL).append("Caused by: ");
- format(buffer,cause);
+ buffer.append(EOL).append(indent).append("Caused by: ");
+ format(buffer,cause,indent);
}
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
@Override
public void prevent(ClassLoader loader)
{
- LOG.debug("Pinning classloader for java.awt.EventQueue using "+loader);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Pinning classloader for java.awt.EventQueue using "+loader);
Toolkit.getDefaultToolkit();
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
@Override
public void prevent(ClassLoader loader)
{
- LOG.debug("Pinning classloader for AppContext.getContext() with "+loader);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Pinning classloader for AppContext.getContext() with "+loader);
ImageIO.getUseCache();
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
@Override
public void prevent(ClassLoader loader)
{
- LOG.debug("Pinning DriverManager classloader with "+loader);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Pinning DriverManager classloader with "+loader);
DriverManager.getDrivers();
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
{
try
{
- Class clazz = Class.forName("sun.misc.GC");
+ 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));
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.net.URLConnection;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
+import java.nio.file.InvalidPathException;
import java.nio.file.StandardOpenOption;
import java.security.Permission;
import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
{
// Try standard API to convert URL to file.
file =new File(url.toURI());
+ assertValidPath(file.toString());
}
catch (URISyntaxException e)
{
_file=file;
_uri=normalizeURI(_file,url.toURI());
- _alias=checkAlias(_file);
+ _alias=checkFileAlias(_file);
}
/* -------------------------------------------------------- */
_file=file;
URI file_uri=_file.toURI();
_uri=normalizeURI(_file,uri);
-
- if (!_uri.equals(file_uri.toString()))
- {
- // URI and File URI are different. Is it just an encoding difference?
- if (!file_uri.toString().equals(URIUtil.decodePath(uri.toString())))
- _alias=_file.toURI();
- else
- _alias=checkAlias(_file);
- }
+ assertValidPath(file.toString());
+
+ // Is it a URI alias?
+ if (!URIUtil.equalsIgnoreEncodings(_uri,file_uri.toString()))
+ _alias=_file.toURI();
else
- _alias=checkAlias(_file);
+ _alias=checkFileAlias(_file);
}
/* -------------------------------------------------------- */
FileResource(File file)
{
+ assertValidPath(file.toString());
_file=file;
_uri=normalizeURI(_file,_file.toURI());
- _alias=checkAlias(_file);
+ _alias=checkFileAlias(_file);
}
/* -------------------------------------------------------- */
}
/* -------------------------------------------------------- */
- private static URI checkAlias(File file)
+ private static URI checkFileAlias(File file)
{
try
{
if (!abs.equals(can))
{
- LOG.debug("ALIAS abs={} can={}",abs,can);
+ if (LOG.isDebugEnabled())
+ LOG.debug("ALIAS abs={} can={}",abs,can);
URI alias=new File(can).toURI();
// Have to encode the path as File.toURI does not!
public Resource addPath(String path)
throws IOException,MalformedURLException
{
+ assertValidPath(path);
path = org.eclipse.jetty.util.URIUtil.canonicalPath(path);
if (path==null)
}
catch(final URISyntaxException e)
{
- throw new MalformedURLException(){{initCause(e);}};
+ throw new InvalidPathException(path, e.getMessage());
}
return new FileResource(uri);
}
-
-
+
+ private void assertValidPath(String path)
+ {
+ int idx = StringUtil.indexOfControlChars(path);
+ if (idx >= 0)
+ {
+ throw new InvalidPathException(path, "Invalid Character at index " + idx);
+ }
+ }
+
/* ------------------------------------------------------------ */
@Override
public URI getAlias()
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
{
try
{
- LOG.debug("Closing JarFile "+_jarFile.getName());
+ if (LOG.isDebugEnabled())
+ LOG.debug("Closing JarFile "+_jarFile.getName());
_jarFile.close();
}
catch ( IOException ioe )
* Returns true if the represented resource exists.
*/
@Override
+
public boolean exists()
{
if (_exists)
else
{
// Can we find a file for it?
- JarFile jarFile=null;
+ boolean close_jar_file= false;
+ JarFile jar_file=null;
if (check)
// Yes
- jarFile=_jarFile;
+ jar_file=_jarFile;
else
{
// No - so lets look if the root entry exists.
{
JarURLConnection c=(JarURLConnection)((new URL(_jarUrl)).openConnection());
c.setUseCaches(getUseCaches());
- jarFile=c.getJarFile();
+ jar_file=c.getJarFile();
+ close_jar_file = !getUseCaches();
}
catch(Exception e)
{
}
// Do we need to look more closely?
- if (jarFile!=null && _entry==null && !_directory)
+ if (jar_file!=null && _entry==null && !_directory)
{
// OK - we have a JarFile, lets look at the entries for our path
- Enumeration<JarEntry> e=jarFile.entries();
+ Enumeration<JarEntry> e=jar_file.entries();
while(e.hasMoreElements())
{
JarEntry entry = e.nextElement();
}
}
}
+
+ if(close_jar_file && jar_file!=null)
+ {
+ try
+ {
+ jar_file.close();
+ }
+ catch (IOException ioe)
+ {
+ LOG.ignore(ioe);
+ }
+ }
}
_exists= ( _directory || _entry!=null);
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
/* ------------------------------------------------------------ */
/**
- * Returns true if the respresenetd resource exists.
+ * Returns true if the represented resource exists.
*/
@Override
public boolean exists()
{
checkConnection();
if (!_urlString.endsWith("!/"))
- return new FilterInputStream(super.getInputStream())
+ return new FilterInputStream(getInputStream(false))
{
@Override
public void close() throws IOException {this.in=IO.getClosedStream();}
return is;
}
+
+
+
/* ------------------------------------------------------------ */
@Override
public void copyTo(File directory)
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.resource;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.file.DirectoryIteratorException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.InvalidPathException;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.FileTime;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * Java NIO Path equivalent of FileResource.
+ */
+public class PathResource extends Resource
+{
+ private static final Logger LOG = Log.getLogger(PathResource.class);
+
+ private final Path path;
+ private final URI uri;
+ private LinkOption linkOptions[] = new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
+
+ public PathResource(File file)
+ {
+ this(file.toPath());
+ }
+
+ public PathResource(Path path)
+ {
+ this.path = path;
+ assertValidPath(path);
+ this.uri = this.path.toUri();
+ }
+
+ public PathResource(URI uri) throws IOException
+ {
+ if (!uri.isAbsolute())
+ {
+ throw new IllegalArgumentException("not an absolute uri");
+ }
+
+ if (!uri.getScheme().equalsIgnoreCase("file"))
+ {
+ throw new IllegalArgumentException("not file: scheme");
+ }
+
+ Path path;
+ try
+ {
+ path = new File(uri).toPath();
+ }
+ catch (InvalidPathException e)
+ {
+ throw e;
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw e;
+ }
+ catch (Exception e)
+ {
+ LOG.ignore(e);
+ throw new IOException("Unable to build Path from: " + uri,e);
+ }
+
+ this.path = path;
+ this.uri = path.toUri();
+ }
+
+ public PathResource(URL url) throws IOException, URISyntaxException
+ {
+ this(url.toURI());
+ }
+
+ @Override
+ public Resource addPath(String apath) throws IOException, MalformedURLException
+ {
+ return new PathResource(this.path.getFileSystem().getPath(path.toString(), apath));
+ }
+
+ private void assertValidPath(Path path)
+ {
+ String str = path.toString();
+ int idx = StringUtil.indexOfControlChars(str);
+ if(idx >= 0)
+ {
+ throw new InvalidPathException(str, "Invalid Character at index " + idx);
+ }
+ }
+
+ @Override
+ public void close()
+ {
+ // not applicable for FileSytem / Path
+ }
+
+ @Override
+ public boolean delete() throws SecurityException
+ {
+ try
+ {
+ return Files.deleteIfExists(path);
+ }
+ catch (IOException e)
+ {
+ LOG.ignore(e);
+ return false;
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ PathResource other = (PathResource)obj;
+ if (path == null)
+ {
+ if (other.path != null)
+ {
+ return false;
+ }
+ }
+ else if (!path.equals(other.path))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean exists()
+ {
+ return Files.exists(path,linkOptions);
+ }
+
+ @Override
+ public File getFile() throws IOException
+ {
+ return path.toFile();
+ }
+
+ public boolean getFollowLinks()
+ {
+ return (linkOptions != null) && (linkOptions.length > 0) && (linkOptions[0] == LinkOption.NOFOLLOW_LINKS);
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException
+ {
+ return Files.newInputStream(path,StandardOpenOption.READ);
+ }
+
+ @Override
+ public String getName()
+ {
+ return path.toAbsolutePath().toString();
+ }
+
+ @Override
+ public ReadableByteChannel getReadableByteChannel() throws IOException
+ {
+ return FileChannel.open(path,StandardOpenOption.READ);
+ }
+
+ @Override
+ public URI getURI()
+ {
+ return this.uri;
+ }
+
+ @Override
+ public URL getURL()
+ {
+ try
+ {
+ return path.toUri().toURL();
+ }
+ catch (MalformedURLException e)
+ {
+ return null;
+ }
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = (prime * result) + ((path == null)?0:path.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean isContainedIn(Resource r) throws MalformedURLException
+ {
+ // not applicable for FileSystem / path
+ return false;
+ }
+
+ @Override
+ public boolean isDirectory()
+ {
+ return Files.isDirectory(path,linkOptions);
+ }
+
+ @Override
+ public long lastModified()
+ {
+ try
+ {
+ FileTime ft = Files.getLastModifiedTime(path,linkOptions);
+ return ft.toMillis();
+ }
+ catch (IOException e)
+ {
+ LOG.ignore(e);
+ return 0;
+ }
+ }
+
+ @Override
+ public long length()
+ {
+ try
+ {
+ return Files.size(path);
+ }
+ catch (IOException e)
+ {
+ // in case of error, use File.length logic of 0L
+ return 0L;
+ }
+ }
+
+ @Override
+ public URI getAlias()
+ {
+ if (Files.isSymbolicLink(path))
+ {
+ try
+ {
+ return path.toRealPath().toUri();
+ }
+ catch (IOException e)
+ {
+ LOG.debug(e);
+ return null;
+ }
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ @Override
+ public String[] list()
+ {
+ try (DirectoryStream<Path> dir = Files.newDirectoryStream(path))
+ {
+ List<String> entries = new ArrayList<>();
+ for (Path entry : dir)
+ {
+ String name = entry.getFileName().toString();
+
+ if (Files.isDirectory(entry))
+ {
+ name += "/";
+ }
+
+ entries.add(name);
+ }
+ int size = entries.size();
+ return entries.toArray(new String[size]);
+ }
+ catch (DirectoryIteratorException e)
+ {
+ LOG.debug(e);
+ }
+ catch (IOException e)
+ {
+ LOG.debug(e);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean renameTo(Resource dest) throws SecurityException
+ {
+ if (dest instanceof PathResource)
+ {
+ PathResource destRes = (PathResource)dest;
+ try
+ {
+ Path result = Files.move(path,destRes.path,StandardCopyOption.ATOMIC_MOVE);
+ return Files.exists(result,linkOptions);
+ }
+ catch (IOException e)
+ {
+ LOG.ignore(e);
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public void setFollowLinks(boolean followLinks)
+ {
+ if (followLinks)
+ {
+ linkOptions = new LinkOption[0];
+ }
+ else
+ {
+ linkOptions = new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
+ }
+ }
+}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
}
}
- return newResource(url);
+ return newResource(url, useCaches);
}
/* ------------------------------------------------------------ */
/**
* Returns an URL representing the given resource
*/
+ // TODO: should deprecate this one and only use getURI()
public abstract URL getURL();
/* ------------------------------------------------------------ */
/**
* Deletes the given resource
*/
+ // TODO: can throw IOException
public abstract boolean delete()
throws SecurityException;
/**
* Rename the given resource
*/
+ // TODO: can throw IOException
public abstract boolean renameTo( Resource dest)
throws SecurityException;
* Returns a list of resource names contained in the given resource
* The resource names are not URL encoded.
*/
+ // TODO: can throw IOException
public abstract String[] list();
/* ------------------------------------------------------------ */
buf.append("</TD></TR>");
}
buf.append("</TABLE>\n");
- buf.append("</BODY></HTML>\n");
+ buf.append("</BODY></HTML>\n");
return buf.toString();
}
private static String deTag(String raw)
{
- return StringUtil.replace( StringUtil.replace(raw,"<","<"), ">", ">");
+ return StringUtil.sanitizeXmlString(raw);
}
/* ------------------------------------------------------------ */
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
return _url.toExternalForm();
}
+
/* ------------------------------------------------------------ */
/**
- * Returns an input stream to the resource
+ * Returns an input stream to the resource. The underlying
+ * url connection will be nulled out to prevent re-use.
*/
@Override
public synchronized InputStream getInputStream()
throws java.io.IOException
+ {
+ return getInputStream (true); //backwards compatibility
+ }
+
+
+
+ /* ------------------------------------------------------------ */
+ /**
+ * Returns an input stream to the resource, optionally nulling
+ * out the underlying url connection. If the connection is not
+ * nulled out, a subsequent call to getInputStream() may return
+ * an existing and already in-use input stream - this depends on
+ * the url protocol. Eg JarURLConnection does not reuse inputstreams.
+ *
+ * @param resetConnection if true the connection field is set to null
+ */
+ protected synchronized InputStream getInputStream(boolean resetConnection)
+ throws java.io.IOException
{
if (!checkConnection())
throw new IOException( "Invalid resource");
}
finally
{
- _connection=null;
+ if (resetConnection)
+ {
+ _connection=null;
+ if (LOG.isDebugEnabled()) LOG.debug("Connection nulled");
+ }
}
}
path = URIUtil.canonicalPath(path);
- return newResource(URIUtil.addPaths(_url.toExternalForm(),path));
+ return newResource(URIUtil.addPaths(_url.toExternalForm(),URIUtil.encodePath(path)), _useCaches);
}
/* ------------------------------------------------------------ */
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
digest = __md.digest();
}
if (digest == null || digest.length != _digest.length) return false;
+ boolean digestMismatch = false;
for (int i = 0; i < digest.length; i++)
- if (digest[i] != _digest[i]) return false;
- return true;
+ digestMismatch |= (digest[i] != _digest[i]);
+ return !digestMismatch;
}
else if (credentials instanceof MD5)
{
MD5 md5 = (MD5) credentials;
if (_digest.length != md5._digest.length) return false;
+ boolean digestMismatch = false;
for (int i = 0; i < _digest.length; i++)
- if (_digest[i] != md5._digest[i]) return false;
- return true;
+ digestMismatch |= (_digest[i] != md5._digest[i]);
+ return !digestMismatch;
}
else if (credentials instanceof Credential)
{
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.Locale;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/**
* Password utility class.
- *
+ *
* This utility class gets a password or pass phrase either by:
- *
+ *
* <PRE>
* + Password is set as a system property.
* + The password is prompted for and read from standard input
* + A program is run to get the password.
* </pre>
- *
+ *
* Passwords that begin with OBF: are de obfuscated. Passwords can be obfuscated
* by run org.eclipse.util.Password as a main class. Obfuscated password are
* required if a system needs to recover the full password (eg. so that it may
* a secure(ish) way to store passwords that only need to be checked rather than
* recovered. Note that it is not strong security - specially if simple
* passwords are used.
- *
- *
+ *
+ *
*/
public class Password extends Credential
{
/* ------------------------------------------------------------ */
/**
* Constructor.
- *
+ *
* @param password The String password.
*/
public Password(String password)
@Override
public boolean equals(Object o)
{
- if (this == o)
+ if (this == o)
return true;
- if (null == o)
+ if (null == o)
return false;
if (o instanceof Password)
return p._pw == _pw || (null != _pw && _pw.equals(p._pw));
}
- if (o instanceof String)
+ if (o instanceof String)
return o.equals(_pw);
return false;
byte b2 = b[b.length - (i + 1)];
if (b1<0 || b2<0)
{
- int i0 = (0xff&b1)*256 + (0xff&b2);
- String x = Integer.toString(i0, 36).toLowerCase();
+ int i0 = (0xff&b1)*256 + (0xff&b2);
+ String x = Integer.toString(i0, 36).toLowerCase(Locale.ENGLISH);
buf.append("U0000",0,5-x.length());
buf.append(x);
}
int i1 = 127 + b1 + b2;
int i2 = 127 + b1 - b2;
int i0 = i1 * 256 + i2;
- String x = Integer.toString(i0, 36).toLowerCase();
+ String x = Integer.toString(i0, 36).toLowerCase(Locale.ENGLISH);
int j0 = Integer.parseInt(x, 36);
int j1 = (i0 / 256);
int j2 = (i0 % 256);
byte bx = (byte) ((j1 + j2 - 254) / 2);
-
+
buf.append("000",0,4-x.length());
buf.append(x);
}
* <LI>Prompting for a password
* <LI>Using promptDft if nothing was entered.
* </UL>
- *
+ *
* @param realm The realm name for the password, used as a SystemProperty
* name.
* @param dft The default password.
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
private final Set<String> _excludeProtocols = new LinkedHashSet<>();
/** Included protocols. */
- private Set<String> _includeProtocols = null;
+ private final Set<String> _includeProtocols = new LinkedHashSet<>();
/** Excluded cipher suites. */
private final Set<String> _excludeCipherSuites = new LinkedHashSet<>();
+
/** Included cipher suites. */
- private Set<String> _includeCipherSuites = null;
+ private final Set<String> _includeCipherSuites = new LinkedHashSet<>();
/** Keystore path. */
private String _keyStorePath;
public SslContextFactory(boolean trustAll)
{
setTrustAll(trustAll);
+ addExcludeProtocols("SSL", "SSLv2", "SSLv2Hello", "SSLv3");
}
/**
if (_trustAll)
{
- LOG.debug("No keystore or trust store configured. ACCEPTING UNTRUSTED CERTIFICATES!!!!!");
+ if (LOG.isDebugEnabled())
+ LOG.debug("No keystore or trust store configured. ACCEPTING UNTRUSTED CERTIFICATES!!!!!");
// Create a trust manager that does not validate certificate chains
trust_managers = TRUST_ALL_CERTS;
}
SecureRandom secureRandom = (_secureRandomAlgorithm == null)?null:SecureRandom.getInstance(_secureRandomAlgorithm);
- SSLContext context = SSLContext.getInstance(_sslProtocol);
+ SSLContext context = _sslProvider == null ? SSLContext.getInstance(_sslProtocol) : SSLContext.getInstance(_sslProtocol, _sslProvider);
context.init(null, trust_managers, secureRandom);
_context = context;
}
TrustManager[] trustManagers = getTrustManagers(trustStore,crls);
SecureRandom secureRandom = (_secureRandomAlgorithm == null)?null:SecureRandom.getInstance(_secureRandomAlgorithm);
- SSLContext context = _sslProvider == null ? SSLContext.getInstance(_sslProtocol) : SSLContext.getInstance(_sslProtocol,_sslProvider);
+ SSLContext context = _sslProvider == null ? SSLContext.getInstance(_sslProtocol) : SSLContext.getInstance(_sslProtocol, _sslProvider);
context.init(keyManagers,trustManagers,secureRandom);
_context = context;
}
SSLEngine engine = newSSLEngine();
- LOG.debug("Enabled Protocols {} of {}",Arrays.asList(engine.getEnabledProtocols()),Arrays.asList(engine.getSupportedProtocols()));
if (LOG.isDebugEnabled())
+ {
+ LOG.debug("Enabled Protocols {} of {}",Arrays.asList(engine.getEnabledProtocols()),Arrays.asList(engine.getSupportedProtocols()));
LOG.debug("Enabled Ciphers {} of {}",Arrays.asList(engine.getEnabledCipherSuites()),Arrays.asList(engine.getSupportedCipherSuites()));
+ }
}
}
public void setIncludeProtocols(String... protocols)
{
checkNotStarted();
- _includeProtocols = new LinkedHashSet<>(Arrays.asList(protocols));
+ _includeProtocols.clear();
+ _includeProtocols.addAll(Arrays.asList(protocols));
}
/**
public void setIncludeCipherSuites(String... cipherSuites)
{
checkNotStarted();
- _includeCipherSuites = new LinkedHashSet<>(Arrays.asList(cipherSuites));
+ _includeCipherSuites.clear();
+ _includeCipherSuites.addAll(Arrays.asList(cipherSuites));
}
/**
Set<String> selected_protocols = new LinkedHashSet<>();
// Set the starting protocols - either from the included or enabled list
- if (_includeProtocols!=null)
+ if (!_includeProtocols.isEmpty())
{
// Use only the supported included protocols
for (String protocol : _includeProtocols)
Set<String> selected_ciphers = new CopyOnWriteArraySet<>();
// Set the starting ciphers - either from the included or enabled list
- if (_includeCipherSuites!=null)
- processIncludeCipherSuites(supportedCipherSuites, selected_ciphers);
- else
+ if (_includeCipherSuites.isEmpty())
selected_ciphers.addAll(Arrays.asList(enabledCipherSuites));
+ else
+ processIncludeCipherSuites(supportedCipherSuites, selected_ciphers);
removeExcludedCipherSuites(selected_ciphers);
return selected_ciphers.toArray(new String[selected_ciphers.size()]);
}
- private void processIncludeCipherSuites(String[] supportedCipherSuites, Set<String> selected_ciphers)
+ protected void processIncludeCipherSuites(String[] supportedCipherSuites, Set<String> selected_ciphers)
{
for (String cipherSuite : _includeCipherSuites)
{
}
}
- private void removeExcludedCipherSuites(Set<String> selected_ciphers)
+ protected void removeExcludedCipherSuites(Set<String> selected_ciphers)
{
for (String excludeCipherSuite : _excludeCipherSuites)
{
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
}
finally
{
- __nonBlockingThread.remove();
+ __nonBlockingThread.set(Boolean.FALSE);
}
}
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.eclipse.jetty.util.BlockingArrayQueue;
-import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.util.ConcurrentHashSet;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.ManagedOperation;
private final AtomicInteger _threadsStarted = new AtomicInteger();
private final AtomicInteger _threadsIdle = new AtomicInteger();
private final AtomicLong _lastShrink = new AtomicLong();
- private final ConcurrentLinkedQueue<Thread> _threads = new ConcurrentLinkedQueue<>();
+ private final ConcurrentHashSet<Thread> _threads=new ConcurrentHashSet<Thread>();
private final Object _joinLock = new Object();
private final BlockingQueue<Runnable> _jobs;
private String _name = "qtp" + hashCode();
setStopTimeout(5000);
if (queue==null)
- queue=new BlockingArrayQueue<>(_minThreads, _minThreads);
+ {
+ int capacity=Math.max(_minThreads, 8);
+ queue=new BlockingArrayQueue<>(capacity, capacity);
+ }
_jobs=queue;
-
}
@Override
StringBuilder dmp = new StringBuilder();
for (StackTraceElement element : unstopped.getStackTrace())
{
- dmp.append(StringUtil.__LINE_SEPARATOR).append("\tat ").append(element);
+ dmp.append(System.lineSeparator()).append("\tat ").append(element);
}
LOG.warn("Couldn't stop {}{}", unstopped, dmp.toString());
}
LOG.warn("{} rejected {}", this, job);
throw new RejectedExecutionException(job.toString());
}
+ else
+ {
+ // Make sure there is at least one thread executing the job.
+ if (getThreads() == 0)
+ startThreads(1);
+ }
}
/**
return _threadsIdle.get();
}
+ /**
+ * @return The number of busy threads in the pool
+ */
+ @ManagedAttribute("total number of busy threads in the pool")
+ public int getBusyThreads()
+ {
+ return getThreads() - getIdleThreads();
+ }
+
/**
* @return True if the pool is at maxThreads and there are not more idle threads than queued jobs
*/
private boolean startThreads(int threadsToStart)
{
- while (threadsToStart > 0)
+ while (threadsToStart > 0 && isRunning())
{
int threads = _threadsStarted.get();
if (threads >= _maxThreads)
thread.start();
started = true;
+ --threadsToStart;
}
finally
{
if (!started)
_threadsStarted.decrementAndGet();
}
- if (started)
- threadsToStart--;
}
return true;
}
return new Thread(runnable);
}
-
@Override
@ManagedOperation("dump thread state")
public String dump()
@Override
public void dump(Appendable out, String indent) throws IOException
{
- out.append(String.valueOf(thread.getId())).append(' ').append(thread.getName()).append(' ').append(thread.getState().toString()).append(idle ? " IDLE" : "").append('\n');
+ out.append(String.valueOf(thread.getId())).append(' ').append(thread.getName()).append(' ').append(thread.getState().toString()).append(idle ? " IDLE" : "");
+ if (thread.getPriority()!=Thread.NORM_PRIORITY)
+ out.append(" prio=").append(String.valueOf(thread.getPriority()));
+ out.append(System.lineSeparator());
if (!idle)
ContainerLifeCycle.dump(out, indent, Arrays.asList(trace));
}
}
else
{
- dump.add(thread.getId() + " " + thread.getName() + " " + thread.getState() + " @ " + (trace.length > 0 ? trace[0] : "???") + (idle ? " IDLE" : ""));
+ int p=thread.getPriority();
+ dump.add(thread.getId() + " " + thread.getName() + " " + thread.getState() + " @ " + (trace.length > 0 ? trace[0] : "???") + (idle ? " IDLE" : "")+ (p==Thread.NORM_PRIORITY?"":(" prio="+p)));
}
}
public void run()
{
boolean shrink = false;
+ boolean ignore = false;
try
{
Runnable job = _jobs.poll();
{
runJob(job);
if (Thread.interrupted())
+ {
+ ignore=true;
break loop;
+ }
job = _jobs.poll();
}
long now = System.nanoTime();
if (last == 0 || (now - last) > TimeUnit.MILLISECONDS.toNanos(_idleTimeout))
{
- shrink = _lastShrink.compareAndSet(last, now) &&
- _threadsStarted.compareAndSet(size, size - 1);
- if (shrink)
+ if (_lastShrink.compareAndSet(last, now) && _threadsStarted.compareAndSet(size, size - 1))
{
- return;
+ shrink=true;
+ break loop;
}
}
}
}
catch (InterruptedException e)
{
+ ignore=true;
LOG.ignore(e);
}
catch (Throwable e)
}
finally
{
- if (!shrink)
- _threadsStarted.decrementAndGet();
+ if (!shrink && isRunning())
+ {
+ if (!ignore)
+ LOG.warn("Unexpected thread death: {} in {}",this,QueuedThreadPool.this);
+ // This is an unexpected thread death!
+ if (_threadsStarted.decrementAndGet()<getMaxThreads())
+ startThreads(1);
+ }
_threads.remove(Thread.currentThread());
}
}
if (thread.getId() == id)
{
StringBuilder buf = new StringBuilder();
- buf.append(thread.getId()).append(" ").append(thread.getName()).append(" ").append(thread.getState()).append(":\n");
+ buf.append(thread.getId()).append(" ").append(thread.getName()).append(" ");
+ buf.append(thread.getState()).append(":").append(System.lineSeparator());
for (StackTraceElement element : thread.getStackTrace())
- buf.append(" at ").append(element.toString()).append('\n');
+ buf.append(" at ").append(element.toString()).append(System.lineSeparator());
return buf.toString();
}
}
return null;
}
-
-
}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
package org.eclipse.jetty.util.thread;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.component.Dumpable;
/**
* Implementation of {@link Scheduler} based on JDK's {@link ScheduledThreadPoolExecutor}.
* queue even if the task did not fire, which provides a huge benefit in the performance
* of garbage collection in young generation.
*/
-public class ScheduledExecutorScheduler extends AbstractLifeCycle implements Scheduler
+public class ScheduledExecutorScheduler extends AbstractLifeCycle implements Scheduler, Dumpable
{
private final String name;
private final boolean daemon;
+ private final ClassLoader classloader;
private volatile ScheduledThreadPoolExecutor scheduler;
+ private volatile Thread thread;
public ScheduledExecutorScheduler()
{
this(null, false);
- }
+ }
public ScheduledExecutorScheduler(String name, boolean daemon)
+ {
+ this (name,daemon, Thread.currentThread().getContextClassLoader());
+ }
+
+ public ScheduledExecutorScheduler(String name, boolean daemon, ClassLoader threadFactoryClassLoader)
{
this.name = name == null ? "Scheduler-" + hashCode() : name;
this.daemon = daemon;
+ this.classloader = threadFactoryClassLoader;
}
@Override
@Override
public Thread newThread(Runnable r)
{
- Thread thread = new Thread(r, name);
+ Thread thread = ScheduledExecutorScheduler.this.thread = new Thread(r, name);
thread.setDaemon(daemon);
+ thread.setContextClassLoader(classloader);
return thread;
}
});
@Override
public Task schedule(Runnable task, long delay, TimeUnit unit)
{
- ScheduledFuture<?> result = scheduler.schedule(task, delay, unit);
+ ScheduledThreadPoolExecutor s = scheduler;
+ if (s==null)
+ return new Task(){
+ @Override
+ public boolean cancel()
+ {
+ return false;
+ }};
+
+ ScheduledFuture<?> result = s.schedule(task, delay, unit);
return new ScheduledFutureTask(result);
}
+ @Override
+ public String dump()
+ {
+ return ContainerLifeCycle.dump(this);
+ }
+
+ @Override
+ public void dump(Appendable out, String indent) throws IOException
+ {
+ ContainerLifeCycle.dumpObject(out, this);
+ Thread thread = this.thread;
+ if (thread != null)
+ {
+ List<StackTraceElement> frames = Arrays.asList(thread.getStackTrace());
+ ContainerLifeCycle.dump(out, indent, frames);
+ }
+ }
+
private class ScheduledFutureTask implements Task
{
private final ScheduledFuture<?> scheduledFuture;
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
_thread.unhook();
}
+ /* ------------------------------------------------------------ */
+ public static synchronized boolean isRegistered(LifeCycle lifeCycle)
+ {
+ return _thread._lifeCycles.contains(lifeCycle);
+ }
+
/* ------------------------------------------------------------ */
@Override
public void run()
lifeCycle.stop();
LOG.debug("Stopped {}",lifeCycle);
}
-
+
if (lifeCycle instanceof Destroyable)
{
((Destroyable)lifeCycle).destroy();
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.thread;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * <p>This spin lock is a lock designed to protect VERY short sections
+ * of critical code. Threads attempting to take the lock will spin
+ * forever until the lock is available, thus it is important that
+ * the code protected by this lock is extremely simple and non
+ * blocking. The reason for this lock is that it prevents a thread
+ * from giving up a CPU core when contending for the lock.</p>
+ * <pre>
+ * try(SpinLock.Lock lock = spinlock.lock())
+ * {
+ * // something very quick and non blocking
+ * }
+ * </pre>
+ * <p>Further analysis however, shows that spin locks behave really
+ * bad under heavy contention and where the number of threads
+ * exceeds the number of cores, which are common scenarios for a
+ * server, so this class was removed from usage, preferring
+ * standard locks instead.</p>
+ * @deprecated Do not use it anymore, prefer normal locks
+ */
+@Deprecated
+public class SpinLock
+{
+ private final AtomicReference<Thread> _lock = new AtomicReference<>(null);
+ private final Lock _unlock = new Lock();
+
+ public Lock lock()
+ {
+ Thread thread = Thread.currentThread();
+ while(true)
+ {
+ if (!_lock.compareAndSet(null,thread))
+ {
+ if (_lock.get()==thread)
+ throw new IllegalStateException("SpinLock is not reentrant");
+ continue;
+ }
+ return _unlock;
+ }
+ }
+
+ public boolean isLocked()
+ {
+ return _lock.get()!=null;
+ }
+
+ public boolean isLockedThread()
+ {
+ return _lock.get()==Thread.currentThread();
+ }
+
+ public class Lock implements AutoCloseable
+ {
+ @Override
+ public void close()
+ {
+ _lock.set(null);
+ }
+ }
+}
--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2016 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.thread;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * <p>A utility class to perform periodic sweeping of resources.</p>
+ * <p>{@link Sweepable} resources may be added to or removed from a
+ * {@link Sweeper} and the resource implementation decides whether
+ * it should be swept or not.</p>
+ * <p>If a {@link Sweepable} resources is itself a container of
+ * other sweepable resources, it will forward the sweep operation
+ * to children resources, and so on recursively.</p>
+ * <p>Typical usage is to add {@link Sweeper} as a bean to an existing
+ * container:</p>
+ * <pre>
+ * Server server = new Server();
+ * server.addBean(new Sweeper(), true);
+ * server.start();
+ * </pre>
+ * Code that knows it has sweepable resources can then lookup the
+ * {@link Sweeper} and offer the sweepable resources to it:
+ * <pre>
+ * class MyComponent implements Sweeper.Sweepable
+ * {
+ * private final long creation;
+ * private volatile destroyed;
+ *
+ * MyComponent(Server server)
+ * {
+ * this.creation = System.nanoTime();
+ * Sweeper sweeper = server.getBean(Sweeper.class);
+ * sweeper.offer(this);
+ * }
+ *
+ * void destroy()
+ * {
+ * destroyed = true;
+ * }
+ *
+ * @Override
+ * public boolean sweep()
+ * {
+ * return destroyed;
+ * }
+ * }
+ * </pre>
+ */
+public class Sweeper extends AbstractLifeCycle implements Runnable
+{
+ private static final Logger LOG = Log.getLogger(Sweeper.class);
+
+ private final AtomicReference<List<Sweepable>> items = new AtomicReference<>();
+ private final AtomicReference<Scheduler.Task> task = new AtomicReference<>();
+ private final Scheduler scheduler;
+ private final long period;
+
+ public Sweeper(Scheduler scheduler, long period)
+ {
+ this.scheduler = scheduler;
+ this.period = period;
+ }
+
+ @Override
+ protected void doStart() throws Exception
+ {
+ super.doStart();
+ items.set(new CopyOnWriteArrayList<Sweepable>());
+ activate();
+ }
+
+ @Override
+ protected void doStop() throws Exception
+ {
+ deactivate();
+ items.set(null);
+ super.doStop();
+ }
+
+ public int getSize()
+ {
+ List<Sweepable> refs = items.get();
+ return refs == null ? 0 : refs.size();
+ }
+
+ public boolean offer(Sweepable sweepable)
+ {
+ List<Sweepable> refs = items.get();
+ if (refs == null)
+ return false;
+ refs.add(sweepable);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Resource offered {}", sweepable);
+ return true;
+ }
+
+ public boolean remove(Sweepable sweepable)
+ {
+ List<Sweepable> refs = items.get();
+ return refs != null && refs.remove(sweepable);
+ }
+
+ @Override
+ public void run()
+ {
+ List<Sweepable> refs = items.get();
+ if (refs == null)
+ return;
+ for (Sweepable sweepable : refs)
+ {
+ try
+ {
+ if (sweepable.sweep())
+ {
+ refs.remove(sweepable);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Resource swept {}", sweepable);
+ }
+ }
+ catch (Throwable x)
+ {
+ LOG.info("Exception while sweeping " + sweepable, x);
+ }
+ }
+ activate();
+ }
+
+ private void activate()
+ {
+ if (isRunning())
+ {
+ Scheduler.Task t = scheduler.schedule(this, period, TimeUnit.MILLISECONDS);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Scheduled in {} ms sweep task {}", period, t);
+ task.set(t);
+ }
+ else
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Skipping sweep task scheduling");
+ }
+ }
+
+ private void deactivate()
+ {
+ Scheduler.Task t = task.getAndSet(null);
+ if (t != null)
+ {
+ boolean cancelled = t.cancel();
+ if (LOG.isDebugEnabled())
+ LOG.debug("Cancelled ({}) sweep task {}", cancelled, t);
+ }
+ }
+
+ /**
+ * <p>A {@link Sweepable} resource implements this interface to
+ * signal to a {@link Sweeper} or to a parent container if it
+ * needs to be swept or not.</p>
+ * <p>Typical implementations will check their own internal state
+ * and return true or false from {@link #sweep()} to indicate
+ * whether they should be swept.</p>
+ */
+ public interface Sweepable
+ {
+ /**
+ * @return whether this resource should be swept
+ */
+ public boolean sweep();
+ }
+}
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
-import org.eclipse.jetty.util.component.LifeCycle;
/* ------------------------------------------------------------ */
/** ThreadPool.
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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
//
// ========================================================================
-// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// Copyright (c) 1995-2016 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