Merge remote-tracking branch 'origin/jetty-9.4.x'

This commit is contained in:
Jan Bartel 2016-12-08 11:31:00 +11:00
commit c02dec5654
18 changed files with 357 additions and 126 deletions

View File

@ -1,5 +1,32 @@
jetty-10.0.0-SNAPSHOT
jetty-9.4.0.RC3 - 05 December 2016
+ 1051 NCSARequestLog/RolloverFileOutputStream does not roll day after DST
ends
+ 1062 Jetty allows requests to hang under PUT load
+ 1090 Allow WebSocketUpgradeFilter to be used by WEB-INF/web.xml
+ 1092 jetty-runner jstl support
+ 1108 Please improve logging in SslContextFactory when there are no approved
cipher suites
+ 1117 quickstart generator of quickstart-web.xml should keep ids
+ 1118 Filter.destroy() conflicts with ContainerLifeCycle.destroy() in
WebSocketUpgradeFilter
+ 1123 Broken lifecycle for WebSocket's mappings
+ 1124 Allow configuration of WebSocket mappings from Spring
+ 1127 AsyncMiddleManServletTest Test failure
+ 1128 Stats Servlet hidden from classpath
+ 1130 PROXY protocol support reports incorrect remote address
+ 1134 Jetty HTTP/2 client problems
+ 1135 Avoid allocations from Method.getParameterTypes() if possible
+ 1138 Ensure xml validation works on web descriptors
+ 1139 Support configuration of properties during --add-to-start
+ 1142 Do not warn for default settings in sessions
+ 1143 Upgrade google cloud APIs to 0.7.0
+ 117 Support proxies with WebSocketClient
+ 572 Don't reject HTTP/2 requests without body in low threads mode
+ 877 Programmatic servlet mappings cannot override mappings from
webdefault.xml using quickstart
jetty-9.4.0.RC2 - 16 November 2016
+ 240 Missing content for multipart request after upgrade to Jetty > 9.2.7
+ 586 Thread pools and connectors

View File

@ -30,7 +30,6 @@ import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.SessionIdManager;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -209,33 +208,32 @@ public class DefaultSessionIdManager extends ContainerLifeCycle implements Sessi
@Override
public String newSessionId(HttpServletRequest request, long created)
{
synchronized (this)
if (request==null)
return newSessionId(created);
// A requested session ID can only be used if it is in use already.
String requested_id=request.getRequestedSessionId();
if (requested_id!=null)
{
if (request==null)
return newSessionId(created);
// A requested session ID can only be used if it is in use already.
String requested_id=request.getRequestedSessionId();
if (requested_id!=null)
{
String cluster_id=getId(requested_id);
if (isIdInUse(cluster_id))
return cluster_id;
}
// Else reuse any new session ID already defined for this request.
String new_id=(String)request.getAttribute(__NEW_SESSION_ID);
if (new_id!=null&&isIdInUse(new_id))
return new_id;
// pick a new unique ID!
String id = newSessionId(request.hashCode());
request.setAttribute(__NEW_SESSION_ID,id);
return id;
String cluster_id=getId(requested_id);
if (isIdInUse(cluster_id))
return cluster_id;
}
// Else reuse any new session ID already defined for this request.
String new_id=(String)request.getAttribute(__NEW_SESSION_ID);
if (new_id!=null&&isIdInUse(new_id))
return new_id;
// pick a new unique ID!
String id = newSessionId(request.hashCode());
request.setAttribute(__NEW_SESSION_ID,id);
return id;
}
/* ------------------------------------------------------------ */
/**
@ -246,45 +244,49 @@ public class DefaultSessionIdManager extends ContainerLifeCycle implements Sessi
{
// pick a new unique ID!
String id=null;
while (id==null||id.length()==0)
{
long r0=_weakRandom
?(hashCode()^Runtime.getRuntime().freeMemory()^_random.nextInt()^((seedTerm)<<32))
:_random.nextLong();
if (r0<0)
r0=-r0;
// random chance to reseed
if (_reseed>0 && (r0%_reseed)== 1L)
{
if (LOG.isDebugEnabled())
LOG.debug("Reseeding {}",this);
if (_random instanceof SecureRandom)
{
SecureRandom secure = (SecureRandom)_random;
secure.setSeed(secure.generateSeed(8));
}
else
{
_random.setSeed(_random.nextLong()^System.currentTimeMillis()^seedTerm^Runtime.getRuntime().freeMemory());
}
}
long r1=_weakRandom
?(hashCode()^Runtime.getRuntime().freeMemory()^_random.nextInt()^((seedTerm)<<32))
:_random.nextLong();
if (r1<0)
r1=-r1;
id=Long.toString(r0,36)+Long.toString(r1,36);
//add in the id of the node to ensure unique id across cluster
//NOTE this is different to the node suffix which denotes which node the request was received on
if (_workerName!=null)
id=_workerName + id;
id = id+Long.toString(COUNTER.getAndIncrement());
synchronized (_random)
{
while (id==null||id.length()==0)
{
long r0=_weakRandom
?(hashCode()^Runtime.getRuntime().freeMemory()^_random.nextInt()^((seedTerm)<<32))
:_random.nextLong();
if (r0<0)
r0=-r0;
// random chance to reseed
if (_reseed>0 && (r0%_reseed)== 1L)
{
if (LOG.isDebugEnabled())
LOG.debug("Reseeding {}",this);
if (_random instanceof SecureRandom)
{
SecureRandom secure = (SecureRandom)_random;
secure.setSeed(secure.generateSeed(8));
}
else
{
_random.setSeed(_random.nextLong()^System.currentTimeMillis()^seedTerm^Runtime.getRuntime().freeMemory());
}
}
long r1=_weakRandom
?(hashCode()^Runtime.getRuntime().freeMemory()^_random.nextInt()^((seedTerm)<<32))
:_random.nextLong();
if (r1<0)
r1=-r1;
id=Long.toString(r0,36)+Long.toString(r1,36);
//add in the id of the node to ensure unique id across cluster
//NOTE this is different to the node suffix which denotes which node the request was received on
if (_workerName!=null)
id=_workerName + id;
id = id+Long.toString(COUNTER.getAndIncrement());
}
}
return id;
}

View File

@ -38,8 +38,11 @@ import java.net.SocketTimeoutException;
import java.nio.file.Path;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import org.eclipse.jetty.start.Props.Prop;
import org.eclipse.jetty.start.config.CommandLineConfigSource;
import org.eclipse.jetty.start.config.ConfigSource;
/**
* Main start class.
@ -399,6 +402,32 @@ public class Main
doStop(args);
}
if (args.isUpdateIni())
{
for (ConfigSource config : baseHome.getConfigSources())
{
System.out.printf("ConfigSource %s%n",config.getId());
for (StartIni ini : config.getStartInis())
{
for (String line : ini.getAllLines())
{
Matcher m = Module.SET_PROPERTY.matcher(line);
if (m.matches() && m.groupCount()==3)
{
String name = m.group(2);
String value = m.group(3);
Prop p = args.getProperties().getProp(name);
if (p!=null && ("#".equals(m.group(1)) || !value.equals(p.value)))
{
ini.update(baseHome,args.getProperties());
break;
}
}
}
}
}
}
// Check base directory
BaseBuilder baseBuilder = new BaseBuilder(baseHome,args);
if(baseBuilder.build())

View File

@ -61,9 +61,8 @@ import org.eclipse.jetty.start.config.CommandLineConfigSource;
public class Module implements Comparable<Module>
{
private static final String VERSION_UNSPECIFIED = "9.2";
private static Pattern MOD_NAME = Pattern.compile("^(.*)\\.mod",Pattern.CASE_INSENSITIVE);
private static Pattern SET_PROPERTY = Pattern.compile("^(#?)\\s*([^=\\s]+)=(.*)$");
static Pattern MOD_NAME = Pattern.compile("^(.*)\\.mod",Pattern.CASE_INSENSITIVE);
static Pattern SET_PROPERTY = Pattern.compile("^(#?)\\s*([^=\\s]+)=(.*)$");
/** The file of the module */
private final Path _path;

View File

@ -178,6 +178,8 @@ public class StartArgs
private boolean version = false;
private boolean dryRun = false;
private boolean createStartd = false;
private boolean updateIni = false;
private boolean exec = false;
private String exec_properties;
@ -786,6 +788,11 @@ public class StartArgs
return createStartd;
}
public boolean isUpdateIni()
{
return updateIni;
}
public void parse(ConfigSources sources)
{
ListIterator<ConfigSource> iter = sources.reverseListIterator();
@ -899,6 +906,13 @@ public class StartArgs
return;
}
if (arg.equals("--update-ini") || arg.equals("--update-inis"))
{
run = false;
updateIni = true;
return;
}
if ("--list-classpath".equals(arg) || "--version".equals(arg) || "-v".equals(arg) || "--info".equals(arg))
{
listClasspath = true;

View File

@ -18,8 +18,16 @@
package org.eclipse.jetty.start;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.regex.Matcher;
import org.eclipse.jetty.start.Props.Prop;
/**
* Simple Start .INI handler
@ -78,4 +86,40 @@ public class StartIni extends TextFile
{
return basedir;
}
public void update(BaseHome baseHome,Props props) throws IOException
{
String update = getFile().getFileName().toString();
update = update.substring(0,update.lastIndexOf("."));
String source = baseHome.toShortForm(getFile());
try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(getFile(),StandardCharsets.UTF_8,StandardOpenOption.TRUNCATE_EXISTING,StandardOpenOption.CREATE)))
{
for (String line : getAllLines())
{
Matcher m = Module.SET_PROPERTY.matcher(line);
if (m.matches() && m.groupCount()==3)
{
String name = m.group(2);
String value = m.group(3);
Prop p = props.getProp(name);
if (p!=null && ("#".equals(m.group(1)) || !value.equals(p.value)))
{
StartLog.info("%-15s property updated %s=%s",update,name,p.value);
writer.printf("%s=%s%n",name,p.value);
}
else
{
writer.println(line);
}
}
else
{
writer.println(line);
}
}
}
StartLog.info("%-15s updated %s",update,source);
}
}

View File

@ -40,6 +40,7 @@ public class TextFile implements Iterable<String>
{
private final Path file;
private final List<String> lines = new ArrayList<>();
private final List<String> allLines = new ArrayList<>();
public TextFile(Path file) throws FileNotFoundException, IOException
{
@ -62,6 +63,8 @@ public class TextFile implements Iterable<String>
continue;
}
allLines.add(line);
if (line.charAt(0) == '#')
{
continue;
@ -106,6 +109,11 @@ public class TextFile implements Iterable<String>
return lines;
}
public List<String> getAllLines()
{
return allLines;
}
public void init()
{
}
@ -130,4 +138,10 @@ public class TextFile implements Iterable<String>
{
addUniqueLine(line);
}
@Override
public String toString()
{
return file.toString();
}
}

View File

@ -18,8 +18,12 @@
package org.eclipse.jetty.start.config;
import java.util.Collections;
import java.util.Set;
import org.eclipse.jetty.start.Props;
import org.eclipse.jetty.start.RawArgs;
import org.eclipse.jetty.start.StartIni;
/**
* A Configuration Source
@ -72,4 +76,9 @@ public interface ConfigSource
* @return the value of the property, or null if not found
*/
public String getProperty(String key);
public default Set<StartIni> getStartInis()
{
return Collections.emptySet();
}
}

View File

@ -28,7 +28,9 @@ import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jetty.start.FS;
import org.eclipse.jetty.start.NaturalSort;
@ -75,6 +77,7 @@ public class DirConfigSource implements ConfigSource
private final int weight;
private final RawArgs args;
private final Props props;
private final Set<StartIni> startInis = new HashSet<>();
/**
* Create DirConfigSource with specified identifier and directory.
@ -109,6 +112,7 @@ public class DirConfigSource implements ConfigSource
if (FS.canReadFile(iniFile))
{
StartIni ini = new StartIni(iniFile);
startInis.add(ini);
args.addAll(ini.getLines(),iniFile);
parseAllArgs(ini.getLines(),iniFile.toString());
}
@ -149,6 +153,7 @@ public class DirConfigSource implements ConfigSource
{
StartLog.debug("Reading %s/start.d/%s - %s",id,diniFile.getFileName(),diniFile);
StartIni ini = new StartIni(diniFile);
startInis.add(ini);
args.addAll(ini.getLines(),diniFile);
parseAllArgs(ini.getLines(),diniFile.toString());
}
@ -156,6 +161,12 @@ public class DirConfigSource implements ConfigSource
}
}
@Override
public Set<StartIni> getStartInis()
{
return startInis;
}
private void parseAllArgs(List<String> lines, String origin)
{
for (String line : lines)

View File

@ -114,6 +114,10 @@ Module Management:
Note: not all modules have ini templates and thus may
be transitively enabled and not explicitly enabled in
a ini file.
--update-ini Scan all start.ini and start.d/*.ini files and update
any properties with values specified on the command
line. e.g. --update-ini jetty.http.port=8888
--create-startd Ensure that a start.d directory exists for use by
subsequent --add-to-start=*. If a start.ini file exists

View File

@ -12,6 +12,8 @@ PROP|main.prop=value0
PROP|name=value
PROP|name0=changed0
PROP|name1=changed1
PROP|property=value
PROP|property0=value0
# Files / Directories to create
EXISTS|start.d/parameterized.ini

View File

@ -12,6 +12,8 @@ PROP|main.prop=value0
PROP|name=value
PROP|name0=changed0
PROP|name1=changed1
PROP|property=value
PROP|property0=value0
# Files / Directories to create
EXISTS|start.d/parameterized.ini

View File

@ -0,0 +1,20 @@
## The XMLs we expect (order is important)
XML|${jetty.home}/etc/base.xml
XML|${jetty.home}/etc/main.xml
# The LIBs we expect (order is irrelevant)
LIB|${jetty.home}/lib/base.jar
LIB|${jetty.home}/lib/main.jar
LIB|${jetty.home}/lib/other.jar
# The Properties we expect (order is irrelevant)
PROP|main.prop=value0
PROP|name=value
PROP|name0=changed0
PROP|name1=changed1
PROP|property=value
PROP|property0=changed0
PROP|property1=changed1
# Files / Directories to create
EXISTS|start.d/parameterized.ini

View File

@ -0,0 +1,10 @@
--create-startd
other=value
name=changed
name0=changed0
name1=changed1
--add-to-start=parameterized
--update-ini
property0=changed0
property1=changed1

View File

@ -0,0 +1,7 @@
#p=v
property=value
#comment
property0=value0
#comment
#property1=value1

View File

@ -22,7 +22,6 @@ import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
import org.eclipse.jetty.websocket.common.AcceptHash;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
@ -50,18 +49,9 @@ public class HandshakeRFC6455 implements WebSocketHandshake
response.addHeader("Connection","Upgrade");
response.addHeader("Sec-WebSocket-Accept",AcceptHash.hashKey(key));
if (response.getExtensions() != null)
{
String value = ExtensionConfig.toHeaderValue(response.getExtensions());
if (value != null)
{
response.addHeader("Sec-WebSocket-Extensions",value);
}
}
request.complete();
response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
response.setStatusCode(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
response.complete();
}
}

View File

@ -20,6 +20,8 @@ package org.eclipse.jetty.websocket.servlet;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -42,25 +44,59 @@ public class ServletUpgradeResponse implements UpgradeResponse
private Map<String, List<String>> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
private List<ExtensionConfig> extensions = new ArrayList<>();
private boolean success = false;
private int status;
public ServletUpgradeResponse(HttpServletResponse response)
{
this.response = response;
for (String name : response.getHeaderNames())
{
headers.put(name, new ArrayList<String>(response.getHeaders(name)));
}
}
@Override
public void addHeader(String name, String value)
{
this.response.addHeader(name, value);
if (value!=null)
{
List<String> values = headers.get(name);
if (values==null)
{
values = new ArrayList<>();
headers.put(name,values);
}
values.add(value);
}
}
@Override
public void setHeader(String name, String value)
{
// remove from the real response
if (response!=null)
response.setHeader(name,null);
List<String> values = headers.get(name);
if (values==null)
{
values = new ArrayList<>();
headers.put(name,values);
}
else
values.clear();
values.add(value);
}
private void commitHeaders()
public void complete()
{
if (response==null)
return;
// Take a copy of all the real response headers
Map<String, Collection<String>> real = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
for (String name : response.getHeaderNames())
{
real.put(name,response.getHeaders(name));
}
// Transfer all headers to the real HTTP response
for (Map.Entry<String, List<String>> entry : getHeaders().entrySet())
{
@ -69,11 +105,17 @@ public class ServletUpgradeResponse implements UpgradeResponse
response.addHeader(entry.getKey(), value);
}
}
}
public void complete()
{
commitHeaders();
// Prepend the real headers to the copy headers
for (Map.Entry<String, Collection<String>> entry : real.entrySet())
{
String name = entry.getKey();
Collection<String> prepend = entry.getValue();
List<String> values = headers.getOrDefault(name,headers.containsKey(name)?null:new ArrayList<>());
values.addAll(0,prepend);
}
status = response.getStatus();
response = null;
}
@ -92,13 +134,27 @@ public class ServletUpgradeResponse implements UpgradeResponse
@Override
public String getHeader(String name)
{
return response.getHeader(name);
if (response!=null)
{
String value = response.getHeader(name);
if (value!=null)
return value;
}
List<String> values = headers.get(name);
if (values!=null && !values.isEmpty())
return values.get(0);
return null;
}
@Override
public Set<String> getHeaderNames()
{
return getHeaders().keySet();
if (response==null)
return headers.keySet();
Set<String> h = new HashSet<>(response.getHeaderNames());
h.addAll(headers.keySet());
return h;
}
@Override
@ -110,13 +166,20 @@ public class ServletUpgradeResponse implements UpgradeResponse
@Override
public List<String> getHeaders(String name)
{
return getHeaders().get(name);
if (response==null)
return headers.get(name);
List<String> values = new ArrayList<>(response.getHeaders(name));
values.addAll(headers.get(name));
return values.isEmpty()?null:values;
}
@Override
public int getStatusCode()
{
return response.getStatus();
if (response!=null)
return response.getStatus();
return status;
}
@Override
@ -154,20 +217,20 @@ public class ServletUpgradeResponse implements UpgradeResponse
public void sendError(int statusCode, String message) throws IOException
{
setSuccess(false);
applyHeaders();
response.sendError(statusCode, message);
response.flushBuffer(); // commit response
response = null;
HttpServletResponse r = response;
complete();
r.sendError(statusCode, message);
r.flushBuffer();
}
@Override
public void sendForbidden(String message) throws IOException
{
setSuccess(false);
applyHeaders();
response.sendError(HttpServletResponse.SC_FORBIDDEN, message);
response.flushBuffer(); // commit response
response = null;
HttpServletResponse r = response;
complete();
r.sendError(HttpServletResponse.SC_FORBIDDEN, message);
r.flushBuffer();
}
@Override
@ -187,33 +250,11 @@ public class ServletUpgradeResponse implements UpgradeResponse
extensionsNegotiated = true;
}
@Override
public void setHeader(String name, String value)
{
response.setHeader(name, value);
}
private void applyHeaders()
{
// Transfer all headers to the real HTTP response
for (Map.Entry<String, List<String>> entry : getHeaders().entrySet())
{
for (String value : entry.getValue())
{
response.addHeader(entry.getKey(), value);
}
}
}
public void setStatus(int status)
{
response.setStatus(status);
}
@Override
public void setStatusCode(int statusCode)
{
response.setStatus(statusCode);
if (response!=null)
response.setStatus(statusCode);
}
@Override
@ -227,4 +268,10 @@ public class ServletUpgradeResponse implements UpgradeResponse
{
this.success = success;
}
@Override
public String toString()
{
return String.format("r=%s s=%d h=%s",response,status,headers);
}
}

View File

@ -44,7 +44,7 @@ public interface WebSocketServletFactory
try
{
Class<? extends WebSocketServletFactory> wsClazz =
(Class<? extends WebSocketServletFactory>) Class.forName(DEFAULT_IMPL);
(Class<? extends WebSocketServletFactory>) Class.forName(DEFAULT_IMPL,true,Thread.currentThread().getContextClassLoader());
Constructor<? extends WebSocketServletFactory> ctor = wsClazz.getDeclaredConstructor(new Class<?>[]{ServletContext.class, WebSocketPolicy.class});
return ctor.newInstance(ctx, policy);
}