From 4949b8039767033e4371c7e80340930dbbdc8799 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 13 Sep 2018 15:11:45 +1000 Subject: [PATCH 001/113] Issue #2815 HPack Opaque Bytes HPack as specified allows for values to be opaque bytes, even though HTTP only allows ISO-8859-1 within a header. This updates HPack, so that strings are converted to UTF-8 and then encoded as bytes iff non ISO-8859-1 characters are discovered. Signed-off-by: Greg Wilkins --- .../jetty/http2/hpack/HpackEncoder.java | 30 +++++++-- .../eclipse/jetty/http2/hpack/Huffman.java | 67 +++++++++++++++++-- .../eclipse/jetty/http2/hpack/HpackTest.java | 23 ++++++- 3 files changed, 110 insertions(+), 10 deletions(-) diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java index 9f9ee924f97..e980322de2d 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.http2.hpack; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.util.EnumSet; import java.util.Set; import java.util.stream.Collectors; @@ -390,20 +391,39 @@ public class HpackEncoder if (huffman) { // huffman literal value - buffer.put((byte)0x80); - NBitInteger.encode(buffer,7,Huffman.octetsNeeded(value)); - Huffman.encode(buffer,value); + buffer.put((byte)0x80).mark(); + + try + { + NBitInteger.encode(buffer,7,Huffman.octetsNeeded(value)); + Huffman.encode(buffer,value); + } + catch(Throwable th) + { + LOG.ignore(th); + byte[] bytes = value.getBytes(StandardCharsets.UTF_8); + + NBitInteger.encode(buffer,7,Huffman.octetsNeeded(bytes)); + Huffman.encode(buffer,bytes); + } } else { // add literal assuming iso_8859_1 - buffer.put((byte)0x00); + buffer.put((byte)0x00).mark(); NBitInteger.encode(buffer,7,value.length()); for (int i=0;i127) - throw new IllegalArgumentException(); + { + // Not iso_8859_1, so re-encode remaining as UTF-8 + buffer.reset(); + byte[] bytes = value.getBytes(StandardCharsets.UTF_8); + NBitInteger.encode(buffer,7,bytes.length); + buffer.put(bytes,0,bytes.length); + return; + } buffer.put((byte)c); } } diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java index ddb2c4a97df..f2d5309ec44 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java @@ -20,6 +20,8 @@ package org.eclipse.jetty.http2.hpack; import java.nio.ByteBuffer; +import org.eclipse.jetty.util.Utf8StringBuilder; + public class Huffman { @@ -352,7 +354,7 @@ public class Huffman public static String decode(ByteBuffer buffer,int length) throws HpackException.CompressionException { - StringBuilder out = new StringBuilder(length*2); + Utf8StringBuilder utf8 = new Utf8StringBuilder(length*2); int node = 0; int current = 0; int bits = 0; @@ -379,7 +381,7 @@ public class Huffman throw new HpackException.CompressionException("EOS in content"); // terminal node - out.append(rowsym[node]); + utf8.append((byte)(0xFF&rowsym[node])); bits -= rowbits[node]; node = 0; } @@ -410,7 +412,7 @@ public class Huffman break; } - out.append(rowsym[node]); + utf8.append((byte)(0xFF&rowsym[node])); bits -= rowbits[node]; node = 0; } @@ -418,7 +420,7 @@ public class Huffman if(node != 0) throw new HpackException.CompressionException("Bad termination"); - return out.toString(); + return utf8.toString(); } public static int octetsNeeded(String s) @@ -426,11 +428,21 @@ public class Huffman return octetsNeeded(CODES,s); } + public static int octetsNeeded(byte[] b) + { + return octetsNeeded(CODES,b); + } + public static void encode(ByteBuffer buffer,String s) { encode(CODES,buffer,s); } + public static void encode(ByteBuffer buffer,byte[] b) + { + encode(CODES,buffer,b); + } + public static int octetsNeededLC(String s) { return octetsNeeded(LCCODES,s); @@ -456,6 +468,18 @@ public class Huffman return (needed+7) / 8; } + private static int octetsNeeded(final int[][] table,byte[] b) + { + int needed=0; + int len = b.length; + for (int i=0;i= 8) + { + n -= 8; + array[p++]=(byte)(current >> n); + } + } + + if (n > 0) + { + current <<= (8 - n); + current |= (0xFF >>> n); + array[p++]=(byte)current; + } + + buffer.position(p-buffer.arrayOffset()); + } } diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java index 355ffb2953f..734089330e3 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java @@ -136,8 +136,29 @@ public class HpackTest { assertThat(e.getMessage(),containsString("Header too large")); } - } + + + @Test + public void encodeDecodeNonAscii() throws Exception + { + HpackEncoder encoder = new HpackEncoder(); + HpackDecoder decoder = new HpackDecoder(4096,8192); + ByteBuffer buffer = BufferUtil.allocate(16*1024); + + HttpFields fields0 = new HttpFields(); + fields0.add("Cookie","[\uD842\uDF9F]"); + fields0.add("custom-key","[\uD842\uDF9F]"); + Response original0 = new MetaData.Response(HttpVersion.HTTP_2,200,fields0); + + BufferUtil.clearToFill(buffer); + encoder.encode(buffer,original0); + BufferUtil.flipToFlush(buffer,0); + Response decoded0 = (Response)decoder.decode(buffer); + + assertMetadataSame(original0,decoded0); + } + @Test public void evictReferencedFieldTest() throws Exception From 5ccaea3c7415d807b2167e48ba0304a8e4a9fd26 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 12 Aug 2019 17:20:50 +0200 Subject: [PATCH 002/113] Fixes #3952 - Server configuration for direct/heap ByteBuffers. Updated server-side to use direct/heap ByteBuffers based on getters and setters in the relevant components. Made HTTP/1.1, HTTP/2, and WebSocket use the same mechanism. Removed unused obsoleted methods: * EndPoint.isOptimizedForDirectBuffers() * HttpTransport.isOptimizedForDirectBuffers() * HttpOutput.Interceptor.isOptimizedForDirectBuffers() * HttpChannel.useDirectBuffers() Signed-off-by: Simone Bordet --- .../fcgi/server/HttpTransportOverFCGI.java | 6 --- .../eclipse/jetty/http2/HTTP2Connection.java | 24 +++++++++- .../jetty/http2/generator/FrameGenerator.java | 5 ++ .../jetty/http2/generator/Generator.java | 7 ++- .../http2/generator/HeaderGenerator.java | 18 ++++++- .../http2/generator/HeadersGenerator.java | 2 +- .../http2/generator/PushPromiseGenerator.java | 2 +- .../jetty/http2/hpack/HpackDecoder.java | 20 ++------ .../jetty/http2/hpack/HpackEncoder.java | 5 +- .../AbstractHTTP2ServerConnectionFactory.java | 28 ++++++++++- .../http2/server/HTTP2ServerConnection.java | 1 + .../http2/server/HttpChannelOverHTTP2.java | 12 +++++ .../http2/server/HttpTransportOverHTTP2.java | 8 ---- .../eclipse/jetty/io/AbstractEndPoint.java | 6 --- .../org/eclipse/jetty/io/ChannelEndPoint.java | 6 --- .../java/org/eclipse/jetty/io/EndPoint.java | 7 --- .../org/eclipse/jetty/server/HttpChannel.java | 14 +----- .../jetty/server/HttpChannelOverHttp.java | 6 +++ .../jetty/server/HttpConfiguration.java | 48 ++++++++++++------- .../eclipse/jetty/server/HttpConnection.java | 40 ++++++++++++---- .../jetty/server/HttpConnectionFactory.java | 48 ++++++++++++++----- .../org/eclipse/jetty/server/HttpOutput.java | 34 ++++++------- .../eclipse/jetty/server/HttpTransport.java | 7 --- .../jetty/server/ProxyConnectionFactory.java | 6 --- .../handler/BufferedResponseHandler.java | 6 --- .../gzip/GzipHttpOutputInterceptor.java | 6 --- .../eclipse/jetty/server/HttpOutputTest.java | 6 --- .../eclipse/jetty/server/ResponseTest.java | 6 --- .../websocket/core/internal/FrameFlusher.java | 36 +++++++++----- .../core/internal/WebSocketConnection.java | 34 +++++++++++-- .../server/internal/RFC6455Handshaker.java | 5 ++ .../extensions/ValidationExtensionTest.java | 9 ++-- .../websocket/core/internal/MockEndpoint.java | 6 --- .../core/server/WebSocketServerTest.java | 26 ---------- 34 files changed, 285 insertions(+), 215 deletions(-) diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java index 09d9b2dbb51..ec693a7fae7 100644 --- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java +++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java @@ -49,12 +49,6 @@ public class HttpTransportOverFCGI implements HttpTransport this.request = request; } - @Override - public boolean isOptimizedForDirectBuffers() - { - return false; - } - @Override public void send(MetaData.Response info, boolean head, ByteBuffer content, boolean lastContent, Callback callback) { diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java index 2e1238c1731..12b0f65130f 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java @@ -56,6 +56,8 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher. private final ISession session; private final int bufferSize; private final ExecutionStrategy strategy; + private boolean useInputDirectBuffers; + private boolean useOutputDirectBuffers; public HTTP2Connection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize) { @@ -99,6 +101,26 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher. producer.setInputBuffer(buffer); } + public boolean isUseInputDirectByteBuffers() + { + return useInputDirectBuffers; + } + + public void setUseInputDirectByteBuffers(boolean useInputDirectBuffers) + { + this.useInputDirectBuffers = useInputDirectBuffers; + } + + public boolean isUseOutputDirectByteBuffers() + { + return useOutputDirectBuffers; + } + + public void setUseOutputDirectByteBuffers(boolean useOutputDirectBuffers) + { + this.useOutputDirectBuffers = useOutputDirectBuffers; + } + @Override public void onOpen() { @@ -389,7 +411,7 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher. { private NetworkBuffer() { - super(byteBufferPool, bufferSize, false); + super(byteBufferPool, bufferSize, isUseInputDirectByteBuffers()); } private void put(ByteBuffer source) diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/FrameGenerator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/FrameGenerator.java index 0194beccc27..ede232e2f25 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/FrameGenerator.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/FrameGenerator.java @@ -44,4 +44,9 @@ public abstract class FrameGenerator { return headerGenerator.getMaxFrameSize(); } + + public boolean isUseDirectByteBuffers() + { + return headerGenerator.isUseDirectByteBuffers(); + } } diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/Generator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/Generator.java index b7288d3897a..fb09eb57024 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/Generator.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/Generator.java @@ -38,10 +38,15 @@ public class Generator } public Generator(ByteBufferPool byteBufferPool, int maxDynamicTableSize, int maxHeaderBlockFragment) + { + this(byteBufferPool, true, maxDynamicTableSize, maxHeaderBlockFragment); + } + + public Generator(ByteBufferPool byteBufferPool, boolean directBuffers, int maxDynamicTableSize, int maxHeaderBlockFragment) { this.byteBufferPool = byteBufferPool; - headerGenerator = new HeaderGenerator(); + headerGenerator = new HeaderGenerator(directBuffers); hpackEncoder = new HpackEncoder(maxDynamicTableSize); this.generators = new FrameGenerator[FrameType.values().length]; diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeaderGenerator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeaderGenerator.java index 33283a72389..16f9e017ab9 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeaderGenerator.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeaderGenerator.java @@ -27,10 +27,26 @@ import org.eclipse.jetty.io.ByteBufferPool; public class HeaderGenerator { private int maxFrameSize = Frame.DEFAULT_MAX_LENGTH; + private final boolean directBuffers; + + public HeaderGenerator() + { + this(true); + } + + public HeaderGenerator(boolean directBuffers) + { + this.directBuffers = directBuffers; + } + + public boolean isUseDirectByteBuffers() + { + return directBuffers; + } public ByteBuffer generate(ByteBufferPool.Lease lease, FrameType frameType, int capacity, int length, int flags, int streamId) { - ByteBuffer header = lease.acquire(capacity, true); + ByteBuffer header = lease.acquire(capacity, isUseDirectByteBuffers()); header.put((byte)((length & 0x00_FF_00_00) >>> 16)); header.put((byte)((length & 0x00_00_FF_00) >>> 8)); header.put((byte)((length & 0x00_00_00_FF))); diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeadersGenerator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeadersGenerator.java index 4ff03bd28e5..f3e0b5cd271 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeadersGenerator.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeadersGenerator.java @@ -67,7 +67,7 @@ public class HeadersGenerator extends FrameGenerator flags = Flags.PRIORITY; int maxFrameSize = getMaxFrameSize(); - ByteBuffer hpacked = lease.acquire(maxFrameSize, false); + ByteBuffer hpacked = lease.acquire(maxFrameSize, isUseDirectByteBuffers()); BufferUtil.clearToFill(hpacked); encoder.encode(hpacked, metaData); int hpackedLength = hpacked.position(); diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/PushPromiseGenerator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/PushPromiseGenerator.java index d4fe2640ef9..d3a4033b587 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/PushPromiseGenerator.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/PushPromiseGenerator.java @@ -58,7 +58,7 @@ public class PushPromiseGenerator extends FrameGenerator int extraSpace = 4; maxFrameSize -= extraSpace; - ByteBuffer hpacked = lease.acquire(maxFrameSize, false); + ByteBuffer hpacked = lease.acquire(maxFrameSize, isUseDirectByteBuffers()); BufferUtil.clearToFill(hpacked); encoder.encode(hpacked, metaData); int hpackedLength = hpacked.position(); diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java index 34c47201932..ae10f58711f 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java @@ -24,7 +24,7 @@ import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http2.hpack.HpackContext.Entry; -import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -76,13 +76,8 @@ public class HpackDecoder while (buffer.hasRemaining()) { - if (LOG.isDebugEnabled() && buffer.hasArray()) - { - int l = Math.min(buffer.remaining(), 32); - LOG.debug("decode {}{}", - TypeUtil.toHexString(buffer.array(), buffer.arrayOffset() + buffer.position(), l), - l < buffer.remaining() ? "..." : ""); - } + if (LOG.isDebugEnabled()) + LOG.debug("decode {}", BufferUtil.toHexString(buffer)); byte b = buffer.get(); if (b < 0) @@ -258,14 +253,9 @@ public class HpackDecoder public static String toASCIIString(ByteBuffer buffer, int length) { StringBuilder builder = new StringBuilder(length); - int position = buffer.position(); - int start = buffer.arrayOffset() + position; - int end = start + length; - buffer.position(position + length); - byte[] array = buffer.array(); - for (int i = start; i < end; i++) + for (int i = 0; i < length; ++i) { - builder.append((char)(0x7f & array[i])); + builder.append((char)(0x7F & buffer.get())); } return builder.toString(); } diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java index eaa17a53ee4..084de2f8e23 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java @@ -34,9 +34,9 @@ import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.http2.hpack.HpackContext.Entry; import org.eclipse.jetty.http2.hpack.HpackContext.StaticEntry; import org.eclipse.jetty.util.ArrayTrie; +import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.Trie; -import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -354,9 +354,8 @@ public class HpackEncoder if (_debug) { - int e = buffer.position(); if (LOG.isDebugEnabled()) - LOG.debug("encode {}:'{}' to '{}'", encoding, field, TypeUtil.toHexString(buffer.array(), buffer.arrayOffset() + p, e - p)); + LOG.debug("encode {}:'{}' to '{}'", encoding, field, BufferUtil.toHexString(buffer)); } } diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java index a7492e814f8..becbea36dac 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java @@ -60,6 +60,8 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne private int maxSettingsKeys = SettingsFrame.DEFAULT_MAX_KEYS; private FlowControlStrategy.Factory flowControlStrategyFactory = () -> new BufferingFlowControlStrategy(0.5F); private long streamIdleTimeout; + private boolean _useInputDirectBuffers; + private boolean _useOutputDirectBuffers; public AbstractHTTP2ServerConnectionFactory(@Name("config") HttpConfiguration httpConfiguration) { @@ -78,6 +80,8 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne this.httpConfiguration = Objects.requireNonNull(httpConfiguration); addBean(httpConfiguration); setInputBufferSize(Frame.DEFAULT_MAX_LENGTH + Frame.HEADER_LENGTH); + setUseInputDirectByteBuffers(httpConfiguration.isUseInputDirectByteBuffers()); + setUseOutputDirectByteBuffers(httpConfiguration.isUseOutputDirectByteBuffers()); } @ManagedAttribute("The HPACK dynamic table maximum size") @@ -178,6 +182,26 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne this.maxSettingsKeys = maxSettingsKeys; } + public boolean isUseInputDirectByteBuffers() + { + return _useInputDirectBuffers; + } + + public void setUseInputDirectByteBuffers(boolean useInputDirectBuffers) + { + _useInputDirectBuffers = useInputDirectBuffers; + } + + public boolean isUseOutputDirectByteBuffers() + { + return _useOutputDirectBuffers; + } + + public void setUseOutputDirectByteBuffers(boolean useOutputDirectBuffers) + { + _useOutputDirectBuffers = useOutputDirectBuffers; + } + public HttpConfiguration getHttpConfiguration() { return httpConfiguration; @@ -200,7 +224,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne { ServerSessionListener listener = newSessionListener(connector, endPoint); - Generator generator = new Generator(connector.getByteBufferPool(), getMaxDynamicTableSize(), getMaxHeaderBlockFragment()); + Generator generator = new Generator(connector.getByteBufferPool(), isUseOutputDirectByteBuffers(), getMaxDynamicTableSize(), getMaxHeaderBlockFragment()); FlowControlStrategy flowControl = getFlowControlStrategyFactory().newFlowControlStrategy(); HTTP2ServerSession session = new HTTP2ServerSession(connector.getScheduler(), endPoint, generator, listener, flowControl); session.setMaxLocalStreams(getMaxConcurrentStreams()); @@ -222,6 +246,8 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne HTTP2Connection connection = new HTTP2ServerConnection(connector.getByteBufferPool(), connector.getExecutor(), endPoint, httpConfiguration, parser, session, getInputBufferSize(), listener); + connection.setUseInputDirectByteBuffers(isUseInputDirectByteBuffers()); + connection.setUseOutputDirectByteBuffers(isUseOutputDirectByteBuffers()); connection.addListener(sessionContainer); return configure(connection, connector, endPoint); } diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java index 331696417d2..166443bf7f8 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java @@ -287,6 +287,7 @@ public class HTTP2ServerConnection extends HTTP2Connection implements Connection HttpTransportOverHTTP2 transport = new HttpTransportOverHTTP2(connector, this); transport.setStream(stream); channel = newServerHttpChannelOverHTTP2(connector, httpConfig, transport); + channel.setUseOutputDirectByteBuffers(isUseOutputDirectByteBuffers()); if (LOG.isDebugEnabled()) LOG.debug("Creating channel {} for {}", channel, this); } diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java index 617025a5465..4297bc4ce41 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java @@ -55,6 +55,7 @@ public class HttpChannelOverHTTP2 extends HttpChannel implements Closeable, Writ private boolean _expect100Continue; private boolean _delayedUntilContent; + private boolean _useOutputDirectBuffers; public HttpChannelOverHTTP2(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransportOverHTTP2 transport) { @@ -66,6 +67,17 @@ public class HttpChannelOverHTTP2 extends HttpChannel implements Closeable, Writ return getHttpTransport().getStream(); } + @Override + public boolean isUseOutputDirectByteBuffers() + { + return _useOutputDirectBuffers; + } + + public void setUseOutputDirectByteBuffers(boolean useOutputDirectBuffers) + { + _useOutputDirectBuffers = useOutputDirectBuffers; + } + @Override public boolean isExpecting100Continue() { diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java index 11cd845cce0..afb50e1e05a 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java @@ -58,14 +58,6 @@ public class HttpTransportOverHTTP2 implements HttpTransport this.connection = connection; } - @Override - public boolean isOptimizedForDirectBuffers() - { - // Because sent buffers are passed directly to the endpoint without - // copying we can defer to the endpoint - return connection.getEndPoint().isOptimizedForDirectBuffers(); - } - public IStream getStream() { return stream; diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java index d247a9ed5dd..2731a523519 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractEndPoint.java @@ -327,12 +327,6 @@ public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint _connection = connection; } - @Override - public boolean isOptimizedForDirectBuffers() - { - return false; - } - protected void reset() { _state.set(State.OPEN); diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java index 0d58fde8245..b4c40418676 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ChannelEndPoint.java @@ -175,12 +175,6 @@ public abstract class ChannelEndPoint extends AbstractEndPoint implements Manage _gather = (channel instanceof GatheringByteChannel) ? (GatheringByteChannel)channel : null; } - @Override - public boolean isOptimizedForDirectBuffers() - { - return true; - } - @Override public boolean isOpen() { diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/EndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/EndPoint.java index 875ad2521da..74212828d9d 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/EndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/EndPoint.java @@ -269,13 +269,6 @@ public interface EndPoint extends Closeable */ void onClose(Throwable cause); - /** - * Is the endpoint optimized for DirectBuffer usage - * - * @return True if direct buffers can be used optimally. - */ - boolean isOptimizedForDirectBuffers(); - /** * Upgrade connections. * Close the old connection, update the endpoint and open the new connection. diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java index bd46a1215a2..7ceb9d4728b 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java @@ -44,7 +44,6 @@ import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.ChannelEndPoint; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.QuietException; @@ -228,12 +227,6 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor return _configuration; } - @Override - public boolean isOptimizedForDirectBuffers() - { - return getHttpTransport().isOptimizedForDirectBuffers(); - } - public Server getServer() { return _connector.getServer(); @@ -952,12 +945,9 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor return _connector.getScheduler(); } - /** - * @return true if the HttpChannel can efficiently use direct buffer (typically this means it is not over SSL or a multiplexed protocol) - */ - public boolean useDirectBuffers() + public boolean isUseOutputDirectByteBuffers() { - return getEndPoint() instanceof ChannelEndPoint; + return getHttpConfiguration().isUseOutputDirectByteBuffers(); } /** diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java index 24d3d94328d..2b812ebd7a0 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java @@ -69,6 +69,12 @@ public class HttpChannelOverHttp extends HttpChannel implements HttpParser.Reque _metadata.setURI(new HttpURI()); } + @Override + public boolean isUseOutputDirectByteBuffers() + { + return _httpConnection.isUseOutputDirectByteBuffers(); + } + @Override protected HttpInput newHttpInput(HttpChannelState state) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java index 26276476b4c..acf95042a28 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java @@ -34,8 +34,6 @@ 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.component.DumpableCollection; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; /** * HTTP Configuration. @@ -51,8 +49,6 @@ import org.eclipse.jetty.util.log.Logger; @ManagedObject("HTTP Configuration") public class HttpConfiguration implements Dumpable { - private static final Logger LOG = Log.getLogger(HttpConfiguration.class); - public static final String SERVER_VERSION = "Jetty(" + Jetty.VERSION + ")"; private final List _customizers = new CopyOnWriteArrayList<>(); private final Trie _formEncodedMethods = new TreeTrie<>(); @@ -71,7 +67,8 @@ public class HttpConfiguration implements Dumpable private boolean _delayDispatchUntilContent = true; private boolean _persistentConnectionsEnabled = true; private int _maxErrorDispatches = 10; - private boolean _useDirectByteBuffers = false; + private boolean _useInputDirectByteBuffers = true; + private boolean _useOutputDirectByteBuffers = true; private long _minRequestDataRate; private long _minResponseDataRate; private HttpCompliance _httpCompliance = HttpCompliance.RFC7230; @@ -128,6 +125,7 @@ public class HttpConfiguration implements Dumpable _requestHeaderSize = config._requestHeaderSize; _responseHeaderSize = config._responseHeaderSize; _headerCacheSize = config._headerCacheSize; + _headerCacheCaseSensitive = config._headerCacheCaseSensitive; _secureScheme = config._secureScheme; _securePort = config._securePort; _idleTimeout = config._idleTimeout; @@ -137,9 +135,11 @@ public class HttpConfiguration implements Dumpable _delayDispatchUntilContent = config._delayDispatchUntilContent; _persistentConnectionsEnabled = config._persistentConnectionsEnabled; _maxErrorDispatches = config._maxErrorDispatches; - _useDirectByteBuffers = config._useDirectByteBuffers; + _useInputDirectByteBuffers = config._useInputDirectByteBuffers; + _useOutputDirectByteBuffers = config._useOutputDirectByteBuffers; _minRequestDataRate = config._minRequestDataRate; _minResponseDataRate = config._minResponseDataRate; + _httpCompliance = config._httpCompliance; _requestCookieCompliance = config._requestCookieCompliance; _responseCookieCompliance = config._responseCookieCompliance; _notifyRemoteAsyncErrors = config._notifyRemoteAsyncErrors; @@ -329,17 +329,31 @@ public class HttpConfiguration implements Dumpable } /** - * @param useDirectByteBuffers if true, use direct byte buffers for requests + * @param useInputDirectByteBuffers whether to use direct ByteBuffers for reading */ - public void setUseDirectByteBuffers(boolean useDirectByteBuffers) + public void setUseInputDirectByteBuffers(boolean useInputDirectByteBuffers) { - _useDirectByteBuffers = useDirectByteBuffers; + _useInputDirectByteBuffers = useInputDirectByteBuffers; } - @ManagedAttribute("Whether to use direct byte buffers for requests") - public boolean isUseDirectByteBuffers() + @ManagedAttribute("Whether to use direct ByteBuffers for reading") + public boolean isUseInputDirectByteBuffers() { - return _useDirectByteBuffers; + return _useInputDirectByteBuffers; + } + + /** + * @param useOutputDirectByteBuffers whether to use direct ByteBuffers for writing + */ + public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers) + { + _useOutputDirectByteBuffers = useOutputDirectByteBuffers; + } + + @ManagedAttribute("Whether to use direct ByteBuffers for writing") + public boolean isUseOutputDirectByteBuffers() + { + return _useOutputDirectByteBuffers; } /** @@ -557,7 +571,7 @@ public class HttpConfiguration implements Dumpable } /** - * @return The CookieCompliance used for parsing request Cookie headers. + * @return The CookieCompliance used for parsing request {@code Cookie} headers. * @see #getResponseCookieCompliance() */ public CookieCompliance getRequestCookieCompliance() @@ -566,7 +580,7 @@ public class HttpConfiguration implements Dumpable } /** - * @return The CookieCompliance used for generating response Set-Cookie headers + * @return The CookieCompliance used for generating response {@code Set-Cookie} headers * @see #getRequestCookieCompliance() */ public CookieCompliance getResponseCookieCompliance() @@ -575,8 +589,7 @@ public class HttpConfiguration implements Dumpable } /** - * @param cookieCompliance The CookieCompliance to use for parsing request Cookie headers. - * @see #setRequestCookieCompliance(CookieCompliance) + * @param cookieCompliance The CookieCompliance to use for parsing request {@code Cookie} headers. */ public void setRequestCookieCompliance(CookieCompliance cookieCompliance) { @@ -584,8 +597,7 @@ public class HttpConfiguration implements Dumpable } /** - * @param cookieCompliance The CookieCompliance to use for generating response Set-Cookie headers - * @see #setResponseCookieCompliance(CookieCompliance) + * @param cookieCompliance The CookieCompliance to use for generating response {@code Set-Cookie} headers */ public void setResponseCookieCompliance(CookieCompliance cookieCompliance) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index 3d717ca4497..e10d4ab7ea3 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -73,6 +73,8 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http private final boolean _recordHttpComplianceViolations; private final LongAdder bytesIn = new LongAdder(); private final LongAdder bytesOut = new LongAdder(); + private boolean _useInputDirectBuffers; + private boolean _useOutputDirectBuffers; /** * Get the current connection that this thread is dispatched to. @@ -164,12 +166,6 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http return _generator; } - @Override - public boolean isOptimizedForDirectBuffers() - { - return getEndPoint().isOptimizedForDirectBuffers(); - } - @Override public long getMessagesIn() { @@ -182,6 +178,26 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http return getHttpChannel().getRequests(); } + public boolean isUseInputDirectByteBuffers() + { + return _useInputDirectBuffers; + } + + public void setUseInputDirectByteBuffers(boolean useInputDirectBuffers) + { + _useInputDirectBuffers = useInputDirectBuffers; + } + + public boolean isUseOutputDirectByteBuffers() + { + return _useOutputDirectBuffers; + } + + public void setUseOutputDirectByteBuffers(boolean useOutputDirectBuffers) + { + _useOutputDirectBuffers = useOutputDirectBuffers; + } + @Override public ByteBuffer onUpgradeFrom() { @@ -224,7 +240,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http public ByteBuffer getRequestBuffer() { if (_requestBuffer == null) - _requestBuffer = _bufferPool.acquire(getInputBufferSize(), _config.isUseDirectByteBuffers()); + { + boolean useDirectBuffers = isUseInputDirectByteBuffers(); + _requestBuffer = _bufferPool.acquire(getInputBufferSize(), useDirectBuffers); + } return _requestBuffer; } @@ -737,6 +756,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http if (_callback == null) throw new IllegalStateException(); + boolean useDirectBuffers = isUseOutputDirectByteBuffers(); ByteBuffer chunk = _chunk; while (true) { @@ -757,19 +777,19 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http case NEED_HEADER: { - _header = _bufferPool.acquire(_config.getResponseHeaderSize(), _config.isUseDirectByteBuffers()); + _header = _bufferPool.acquire(_config.getResponseHeaderSize(), useDirectBuffers); continue; } case NEED_CHUNK: { - chunk = _chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, _config.isUseDirectByteBuffers()); + chunk = _chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, useDirectBuffers); continue; } case NEED_CHUNK_TRAILER: { if (_chunk != null) _bufferPool.release(_chunk); - chunk = _chunk = _bufferPool.acquire(_config.getResponseHeaderSize(), _config.isUseDirectByteBuffers()); + chunk = _chunk = _bufferPool.acquire(_config.getResponseHeaderSize(), useDirectBuffers); continue; } case FLUSH: diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java index beee4fbe7a5..b29bdfc4bf9 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java @@ -18,6 +18,8 @@ package org.eclipse.jetty.server; +import java.util.Objects; + import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; @@ -32,7 +34,9 @@ import org.eclipse.jetty.util.annotation.Name; public class HttpConnectionFactory extends AbstractConnectionFactory implements HttpConfiguration.ConnectionFactory { private final HttpConfiguration _config; - private boolean _recordHttpComplianceViolations = false; + private boolean _recordHttpComplianceViolations; + private boolean _useInputDirectBuffers; + private boolean _useOutputDirectBuffers; public HttpConnectionFactory() { @@ -42,10 +46,10 @@ public class HttpConnectionFactory extends AbstractConnectionFactory implements public HttpConnectionFactory(@Name("config") HttpConfiguration config) { super(HttpVersion.HTTP_1_1.asString()); - _config = config; - if (config == null) - throw new IllegalArgumentException("Null HttpConfiguration"); + _config = Objects.requireNonNull(config); addBean(_config); + setUseInputDirectByteBuffers(_config.isUseInputDirectByteBuffers()); + setUseOutputDirectByteBuffers(_config.isUseOutputDirectByteBuffers()); } @Override @@ -59,15 +63,37 @@ public class HttpConnectionFactory extends AbstractConnectionFactory implements return _recordHttpComplianceViolations; } - @Override - public Connection newConnection(Connector connector, EndPoint endPoint) - { - HttpConnection conn = new HttpConnection(_config, connector, endPoint, isRecordHttpComplianceViolations()); - return configure(conn, connector, endPoint); - } - public void setRecordHttpComplianceViolations(boolean recordHttpComplianceViolations) { this._recordHttpComplianceViolations = recordHttpComplianceViolations; } + + public boolean isUseInputDirectByteBuffers() + { + return _useInputDirectBuffers; + } + + public void setUseInputDirectByteBuffers(boolean useInputDirectBuffers) + { + _useInputDirectBuffers = useInputDirectBuffers; + } + + public boolean isUseOutputDirectByteBuffers() + { + return _useOutputDirectBuffers; + } + + public void setUseOutputDirectByteBuffers(boolean useOutputDirectBuffers) + { + _useOutputDirectBuffers = useOutputDirectBuffers; + } + + @Override + public Connection newConnection(Connector connector, EndPoint endPoint) + { + HttpConnection connection = new HttpConnection(_config, connector, endPoint, isRecordHttpComplianceViolations()); + connection.setUseInputDirectByteBuffers(isUseInputDirectByteBuffers()); + connection.setUseOutputDirectByteBuffers(isUseOutputDirectByteBuffers()); + return configure(connection, connector, endPoint); + } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java index a010a69e9a7..6036807f978 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java @@ -103,14 +103,6 @@ public class HttpOutput extends ServletOutputStream implements Runnable */ Interceptor getNextInterceptor(); - /** - * @return True if the Interceptor is optimized to receive direct - * {@link ByteBuffer}s in the {@link #write(ByteBuffer, boolean, Callback)} - * method. If false is returned, then passing direct buffers may cause - * inefficiencies. - */ - boolean isOptimizedForDirectBuffers(); - /** * Reset the buffers. *

If the Interceptor contains buffers then reset them. @@ -458,8 +450,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable boolean last = isLastContentToWrite(len); if (!last && len <= _commitSize) { - if (_aggregate == null) - _aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), _interceptor.isOptimizedForDirectBuffers()); + ensureAggregate(); // YES - fill the aggregate with content from the buffer int filled = BufferUtil.fill(_aggregate, b, off, len); @@ -500,12 +491,10 @@ public class HttpOutput extends ServletOutputStream implements Runnable // handle blocking write // Should we aggregate? - int capacity = getBufferSize(); boolean last = isLastContentToWrite(len); if (!last && len <= _commitSize) { - if (_aggregate == null) - _aggregate = _channel.getByteBufferPool().acquire(capacity, _interceptor.isOptimizedForDirectBuffers()); + ensureAggregate(); // YES - fill the aggregate with content from the buffer int filled = BufferUtil.fill(_aggregate, b, off, len); @@ -559,6 +548,12 @@ public class HttpOutput extends ServletOutputStream implements Runnable closed(); } + private void ensureAggregate() + { + if (_aggregate == null) + _aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), _channel.isUseOutputDirectByteBuffers()); + } + public void write(ByteBuffer buffer) throws IOException { // This write always bypasses aggregate buffer @@ -630,8 +625,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable switch (_state.get()) { case OPEN: - if (_aggregate == null) - _aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), _interceptor.isOptimizedForDirectBuffers()); + ensureAggregate(); BufferUtil.append(_aggregate, (byte)b); // Check if all written or full @@ -650,8 +644,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable if (!_state.compareAndSet(OutputState.READY, OutputState.PENDING)) continue; - if (_aggregate == null) - _aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), _interceptor.isOptimizedForDirectBuffers()); + ensureAggregate(); BufferUtil.append(_aggregate, (byte)b); // Check if all written or full @@ -984,7 +977,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable break; } - ByteBuffer buffer = _channel.useDirectBuffers() ? httpContent.getDirectBuffer() : null; + ByteBuffer buffer = _channel.isUseOutputDirectByteBuffers() ? httpContent.getDirectBuffer() : null; if (buffer == null) buffer = httpContent.getIndirectBuffer(); @@ -1406,6 +1399,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable { super(callback); _in = in; + // Reading from InputStream requires byte[], don't use direct buffers. _buffer = _channel.getByteBufferPool().acquire(getBufferSize(), false); } @@ -1458,7 +1452,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable * An iterating callback that will take content from a * ReadableByteChannel and write it to the {@link HttpChannel}. * A {@link ByteBuffer} of size {@link HttpOutput#getBufferSize()} is used that will be direct if - * {@link HttpChannel#useDirectBuffers()} is true. + * {@link HttpChannel#isUseOutputDirectByteBuffers()} is true. * This callback is passed to the {@link HttpChannel#write(ByteBuffer, boolean, Callback)} to * be notified as each buffer is written and only once all the input is consumed will the * wrapped {@link Callback#succeeded()} method be called. @@ -1473,7 +1467,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable { super(callback); _in = in; - _buffer = _channel.getByteBufferPool().acquire(getBufferSize(), _channel.useDirectBuffers()); + _buffer = _channel.getByteBufferPool().acquire(getBufferSize(), _channel.isUseOutputDirectByteBuffers()); } @Override diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpTransport.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpTransport.java index 2d6faeaa447..5302ecbb8ff 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpTransport.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpTransport.java @@ -71,11 +71,4 @@ public interface HttpTransport * @param failure the failure that caused the abort. */ void abort(Throwable failure); - - /** - * Is the underlying transport optimized for DirectBuffer usage - * - * @return True if direct buffers can be used optimally. - */ - boolean isOptimizedForDirectBuffers(); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java index 43261cd5c1b..308be3bb929 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java @@ -601,12 +601,6 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory _local = local; } - @Override - public boolean isOptimizedForDirectBuffers() - { - return _endp.isOptimizedForDirectBuffers(); - } - @Override public InetSocketAddress getLocalAddress() { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java index bbb24c536b0..8c28bcdd7b9 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/BufferedResponseHandler.java @@ -271,12 +271,6 @@ public class BufferedResponseHandler extends HandlerWrapper return _next; } - @Override - public boolean isOptimizedForDirectBuffers() - { - return false; - } - protected void commit(Queue buffers, Callback callback) { // If only 1 buffer diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java index 4a582348277..e1178fe2788 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java @@ -93,12 +93,6 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor return _interceptor; } - @Override - public boolean isOptimizedForDirectBuffers() - { - return false; // No point as deflator is in user space. - } - @Override public void write(ByteBuffer content, boolean complete, Callback callback) { diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java index b0f753b279e..a3ecb471d4d 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java @@ -651,12 +651,6 @@ public class HttpOutputTest _next.write(BufferUtil.toBuffer(s), complete, callback); } - @Override - public boolean isOptimizedForDirectBuffers() - { - return _next.isOptimizedForDirectBuffers(); - } - @Override public Interceptor getNextInterceptor() { diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java index c45c312d861..55d997a3c60 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java @@ -173,12 +173,6 @@ public class ResponseTest { _channelError = failure; } - - @Override - public boolean isOptimizedForDirectBuffers() - { - return false; - } }); } diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameFlusher.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameFlusher.java index f53331d4b88..d915d455f2c 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameFlusher.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameFlusher.java @@ -50,6 +50,8 @@ public class FrameFlusher extends IteratingCallback private static final Logger LOG = Log.getLogger(FrameFlusher.class); private static final Throwable CLOSED_CHANNEL = new ClosedChannelException(); + private final LongAdder messagesOut = new LongAdder(); + private final LongAdder bytesOut = new LongAdder(); private final ByteBufferPool bufferPool; private final EndPoint endPoint; private final int bufferSize; @@ -62,13 +64,12 @@ public class FrameFlusher extends IteratingCallback private final List previousEntries; private final List failedEntries; - private ByteBuffer batchBuffer = null; + private ByteBuffer batchBuffer; private boolean canEnqueue = true; private boolean flushed = true; private Throwable closedCause; - private LongAdder messagesOut = new LongAdder(); - private LongAdder bytesOut = new LongAdder(); - private long idleTimeout = 0; + private long idleTimeout; + private boolean useDirectBuffers; public FrameFlusher(ByteBufferPool bufferPool, Scheduler scheduler, Generator generator, EndPoint endPoint, int bufferSize, int maxGather) { @@ -84,6 +85,16 @@ public class FrameFlusher extends IteratingCallback this.timeoutScheduler = scheduler; } + public boolean isUseDirectByteBuffers() + { + return useDirectBuffers; + } + + public void setUseDirectByteBuffers(boolean useDirectBuffers) + { + this.useDirectBuffers = useDirectBuffers; + } + /** * Enqueue a Frame to be written to the endpoint. * @@ -225,7 +236,7 @@ public class FrameFlusher extends IteratingCallback // Acquire a batchBuffer if we don't have one if (batchBuffer == null) { - batchBuffer = bufferPool.acquire(bufferSize, true); + batchBuffer = acquireBuffer(bufferSize); buffers.add(batchBuffer); } @@ -249,7 +260,10 @@ public class FrameFlusher extends IteratingCallback else { // Add headers and payload to the list of buffers - buffers.add(entry.generateHeaderBytes()); + // TODO: release this buffer. + ByteBuffer buffer = acquireBuffer(Generator.MAX_HEADER_LENGTH); + buffers.add(buffer); + entry.generateHeaderBytes(buffer); flush = true; ByteBuffer payload = entry.frame.getPayload(); if (BufferUtil.hasContent(payload)) @@ -308,6 +322,11 @@ public class FrameFlusher extends IteratingCallback return Action.SCHEDULED; } + private ByteBuffer acquireBuffer(int capacity) + { + return bufferPool.acquire(capacity, isUseDirectByteBuffers()); + } + private int getQueueSize() { synchronized (this) @@ -474,11 +493,6 @@ public class FrameFlusher extends IteratingCallback super(frame, callback, batch); } - private ByteBuffer generateHeaderBytes() - { - return headerBuffer = generator.generateHeaderBytes(frame); - } - private void generateHeaderBytes(ByteBuffer buffer) { int pos = BufferUtil.flipToFill(buffer); diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java index dacc62bb685..3dcc9a6244e 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java @@ -68,6 +68,8 @@ public class WebSocketConnection extends AbstractConnection implements Connectio // Read / Parse variables private RetainableByteBuffer networkBuffer; + private boolean useInputDirectBuffers; + private boolean useOutputDirectBuffers; /** * Create a WSConnection. @@ -132,6 +134,26 @@ public class WebSocketConnection extends AbstractConnection implements Connectio return getEndPoint().getRemoteAddress(); } + public boolean isUseInputDirectByteBuffers() + { + return useInputDirectBuffers; + } + + public void setUseInputDirectByteBuffers(boolean useInputDirectBuffers) + { + this.useInputDirectBuffers = useInputDirectBuffers; + } + + public boolean isUseOutputDirectByteBuffers() + { + return useOutputDirectBuffers; + } + + public void setUseOutputDirectByteBuffers(boolean useOutputDirectBuffers) + { + this.useOutputDirectBuffers = useOutputDirectBuffers; + } + /** * Physical connection disconnect. *

@@ -222,7 +244,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio synchronized (this) { if (networkBuffer == null) - networkBuffer = new RetainableByteBuffer(bufferPool, getInputBufferSize()); + networkBuffer = newNetworkBuffer(getInputBufferSize()); } } @@ -237,10 +259,15 @@ public class WebSocketConnection extends AbstractConnection implements Connectio throw new IllegalStateException(); networkBuffer.release(); - networkBuffer = new RetainableByteBuffer(bufferPool, getInputBufferSize()); + networkBuffer = newNetworkBuffer(getInputBufferSize()); } } + private RetainableByteBuffer newNetworkBuffer(int capacity) + { + return new RetainableByteBuffer(bufferPool, capacity, isUseInputDirectByteBuffers()); + } + private void releaseNetworkBuffer() { synchronized (this) @@ -445,7 +472,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio { synchronized (this) { - networkBuffer = new RetainableByteBuffer(bufferPool, prefilled.remaining()); + networkBuffer = newNetworkBuffer(prefilled.remaining()); } ByteBuffer buffer = networkBuffer.getBuffer(); BufferUtil.clearToFill(buffer); @@ -572,6 +599,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio private Flusher(Scheduler scheduler, int bufferSize, Generator generator, EndPoint endpoint) { super(bufferPool, scheduler, generator, endpoint, bufferSize, 8); + setUseDirectByteBuffers(isUseOutputDirectByteBuffers()); } @Override diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java index b5c7db8dbd7..a867ac67f23 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java @@ -201,6 +201,11 @@ public final class RFC6455Handshaker implements Handshaker // Create a connection WebSocketConnection connection = newWebSocketConnection(httpChannel.getEndPoint(), connector.getExecutor(), connector.getScheduler(), connector.getByteBufferPool(), coreSession); + // TODO: perhaps use of direct buffers should be WebSocket specific + // rather than inheriting the setting from HttpConfiguration. + HttpConfiguration httpConfig = httpChannel.getHttpConfiguration(); + connection.setUseInputDirectByteBuffers(httpConfig.isUseInputDirectByteBuffers()); + connection.setUseOutputDirectByteBuffers(httpChannel.isUseOutputDirectByteBuffers()); if (LOG.isDebugEnabled()) LOG.debug("connection {}", connection); if (connection == null) diff --git a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/extensions/ValidationExtensionTest.java b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/extensions/ValidationExtensionTest.java index ee65299e348..0553d617ce8 100644 --- a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/extensions/ValidationExtensionTest.java +++ b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/extensions/ValidationExtensionTest.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; +import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.websocket.core.CloseStatus; import org.eclipse.jetty.websocket.core.ExtensionConfig; import org.eclipse.jetty.websocket.core.Frame; @@ -88,7 +89,7 @@ public class ValidationExtensionTest extends WebSocketTester Frame frame = serverHandler.receivedFrames.poll(5, TimeUnit.SECONDS); assertNotNull(frame); assertThat(frame.getOpCode(), is(OpCode.BINARY)); - assertThat(frame.getPayload().array(), is(nonUtf8Payload)); + assertThat(BufferUtil.toArray(frame.getPayload()), is(nonUtf8Payload)); //close normally client.getOutputStream().write(RawFrameBuilder.buildClose(CloseStatus.NORMAL_STATUS, true)); @@ -113,13 +114,13 @@ public class ValidationExtensionTest extends WebSocketTester Frame frame = serverHandler.receivedFrames.poll(5, TimeUnit.SECONDS); assertNotNull(frame); assertThat(frame.getOpCode(), is(OpCode.TEXT)); - assertThat(frame.getPayload().array(), is(initialPayload)); + assertThat(BufferUtil.toArray(frame.getPayload()), is(initialPayload)); client.getOutputStream().write(RawFrameBuilder.buildFrame(OpCode.CONTINUATION, continuationPayload, true)); frame = serverHandler.receivedFrames.poll(5, TimeUnit.SECONDS); assertNotNull(frame); assertThat(frame.getOpCode(), is(OpCode.CONTINUATION)); - assertThat(frame.getPayload().array(), is(continuationPayload)); + assertThat(BufferUtil.toArray(frame.getPayload()), is(continuationPayload)); //close normally client.getOutputStream().write(RawFrameBuilder.buildClose(CloseStatus.NORMAL_STATUS, true)); @@ -144,7 +145,7 @@ public class ValidationExtensionTest extends WebSocketTester Frame frame = serverHandler.receivedFrames.poll(5, TimeUnit.SECONDS); assertNotNull(frame); assertThat(frame.getOpCode(), is(OpCode.TEXT)); - assertThat(frame.getPayload().array(), is(initialPayload)); + assertThat(BufferUtil.toArray(frame.getPayload()), is(initialPayload)); client.getOutputStream().write(RawFrameBuilder.buildFrame(OpCode.CONTINUATION, incompleteContinuationPayload, true)); frame = receiveFrame(client.getInputStream()); diff --git a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/internal/MockEndpoint.java b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/internal/MockEndpoint.java index 76b1e6866b1..d7f264d7fa7 100644 --- a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/internal/MockEndpoint.java +++ b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/internal/MockEndpoint.java @@ -164,12 +164,6 @@ public class MockEndpoint implements EndPoint throw new UnsupportedOperationException(NOT_SUPPORTED); } - @Override - public boolean isOptimizedForDirectBuffers() - { - throw new UnsupportedOperationException(NOT_SUPPORTED); - } - @Override public void upgrade(Connection newConnection) { diff --git a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/server/WebSocketServerTest.java b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/server/WebSocketServerTest.java index 1f69e9810db..60ddf4b3a7d 100644 --- a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/server/WebSocketServerTest.java +++ b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/server/WebSocketServerTest.java @@ -24,7 +24,6 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.util.BlockingArrayQueue; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -43,8 +42,6 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.sameInstance; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -207,29 +204,6 @@ public class WebSocketServerTest extends WebSocketTester } assertThat(serverHandler.receivedFrames.size(), is(5)); assertThat(receivedCallbacks.size(), is(5)); - - byte[] first = serverHandler.receivedFrames.poll().getPayload().array(); - assertThat(serverHandler.receivedFrames.poll().getPayload().array(), sameInstance(first)); - assertThat(serverHandler.receivedFrames.poll().getPayload().array(), sameInstance(first)); - byte[] second = serverHandler.receivedFrames.poll().getPayload().array(); - assertThat(serverHandler.receivedFrames.poll().getPayload().array(), sameInstance(second)); - assertThat(first, not(sameInstance(second))); - - ByteBufferPool pool = server.getServer().getConnectors()[0].getByteBufferPool(); - - assertThat(pool.acquire(first.length, false).array(), not(sameInstance(first))); - receivedCallbacks.poll().succeeded(); - assertThat(pool.acquire(first.length, false).array(), not(sameInstance(first))); - receivedCallbacks.poll().succeeded(); - assertThat(pool.acquire(first.length, false).array(), not(sameInstance(first))); - receivedCallbacks.poll().succeeded(); - assertThat(pool.acquire(first.length, false).array(), sameInstance(first)); - - assertThat(pool.acquire(second.length, false).array(), not(sameInstance(second))); - receivedCallbacks.poll().succeeded(); - assertThat(pool.acquire(second.length, false).array(), not(sameInstance(second))); - receivedCallbacks.poll().succeeded(); - assertThat(pool.acquire(second.length, false).array(), sameInstance(second)); } } From 583e443c669d66995d40fcca949649ed6b5ccc24 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 23 Aug 2019 15:53:14 -0500 Subject: [PATCH 003/113] Removing @Deprecated methods / classes from jetty-9.4.x merge Signed-off-by: Joakim Erdfelt --- .../org/eclipse/jetty/client/HttpClient.java | 21 ---- .../jaspi/modules/BasicAuthModule.java | 104 ------------------ .../authentication/SessionAuthentication.java | 13 --- 3 files changed, 138 deletions(-) delete mode 100644 jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BasicAuthModule.java diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java index 3d6035dc92e..cb76dd61cf0 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java @@ -497,27 +497,6 @@ public class HttpClient extends ContainerLifeCycle return uri; } - /** - * Returns a {@link Destination} for the given scheme, host and port. - * Applications may use {@link Destination}s to create {@link Connection}s - * that will be outside HttpClient's pooling mechanism, to explicitly - * control the connection lifecycle (in particular their termination with - * {@link Connection#close()}). - * - * @param scheme the destination scheme - * @param host the destination host - * @param port the destination port - * @return the destination - * @see #getDestinations() - * @deprecated use {@link #resolveDestination(Request)} instead - */ - @Deprecated - public Destination getDestination(String scheme, String host, int port) - { - Origin origin = createOrigin(scheme, host, port); - return resolveDestination(new HttpDestination.Key(origin, null)); - } - public Destination resolveDestination(Request request) { Origin origin = createOrigin(request.getScheme(), request.getHost(), request.getPort()); diff --git a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BasicAuthModule.java b/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BasicAuthModule.java deleted file mode 100644 index 16ed2106783..00000000000 --- a/jetty-jaspi/src/main/java/org/eclipse/jetty/security/jaspi/modules/BasicAuthModule.java +++ /dev/null @@ -1,104 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2019 Mort Bay Consulting 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.security.jaspi.modules; - -import java.io.IOException; -import java.util.Map; -import javax.security.auth.Subject; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.UnsupportedCallbackException; -import javax.security.auth.message.AuthException; -import javax.security.auth.message.AuthStatus; -import javax.security.auth.message.MessageInfo; -import javax.security.auth.message.MessagePolicy; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.eclipse.jetty.http.HttpHeader; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.util.security.Constraint; - -@Deprecated -public class BasicAuthModule extends BaseAuthModule -{ - private static final Logger LOG = Log.getLogger(BasicAuthModule.class); - - private String realmName; - - private static final String REALM_KEY = "org.eclipse.jetty.security.jaspi.modules.RealmName"; - - public BasicAuthModule() - { - } - - public BasicAuthModule(CallbackHandler callbackHandler, String realmName) - { - super(callbackHandler); - this.realmName = realmName; - } - - @Override - public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, - CallbackHandler handler, Map options) - throws AuthException - { - super.initialize(requestPolicy, responsePolicy, handler, options); - realmName = (String)options.get(REALM_KEY); - } - - @Override - public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, - Subject serviceSubject) - throws AuthException - { - HttpServletRequest request = (HttpServletRequest)messageInfo.getRequestMessage(); - HttpServletResponse response = (HttpServletResponse)messageInfo.getResponseMessage(); - String credentials = request.getHeader(HttpHeader.AUTHORIZATION.asString()); - - try - { - if (credentials != null) - { - if (LOG.isDebugEnabled()) - LOG.debug("Credentials: " + credentials); - if (login(clientSubject, credentials, Constraint.__BASIC_AUTH, messageInfo)) - { - return AuthStatus.SUCCESS; - } - } - - if (!isMandatory(messageInfo)) - { - return AuthStatus.SUCCESS; - } - response.setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), "basic realm=\"" + realmName + '"'); - response.sendError(HttpServletResponse.SC_UNAUTHORIZED); - return AuthStatus.SEND_CONTINUE; - } - catch (IOException e) - { - throw new AuthException(e.getMessage()); - } - catch (UnsupportedCallbackException e) - { - throw new AuthException(e.getMessage()); - } - } -} diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java index eaa66a0d81a..d421434582c 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/SessionAuthentication.java @@ -23,7 +23,6 @@ import java.io.ObjectInputStream; import java.io.Serializable; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionActivationListener; -import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; import javax.servlet.http.HttpSessionEvent; @@ -113,16 +112,4 @@ public class SessionAuthentication extends AbstractUserAuthentication _session = se.getSession(); } } - - @Override - @Deprecated - public void valueBound(HttpSessionBindingEvent event) - { - } - - @Override - @Deprecated - public void valueUnbound(HttpSessionBindingEvent event) - { - } } From 7904f965fd02e9cc562f2103090eb841f2797c4b Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 27 Aug 2019 13:33:24 -0500 Subject: [PATCH 004/113] Restoring BasicAuthModule for src/test/java usage only Signed-off-by: Joakim Erdfelt --- .../jetty/security/jaspi/BasicAuthModule.java | 104 ++++++++++++++++++ jetty-jaspi/src/test/resources/jaspi.xml | 2 +- 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/BasicAuthModule.java diff --git a/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/BasicAuthModule.java b/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/BasicAuthModule.java new file mode 100644 index 00000000000..d00b0f63d88 --- /dev/null +++ b/jetty-jaspi/src/test/java/org/eclipse/jetty/security/jaspi/BasicAuthModule.java @@ -0,0 +1,104 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.security.jaspi; + +import java.io.IOException; +import java.util.Map; +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.message.AuthException; +import javax.security.auth.message.AuthStatus; +import javax.security.auth.message.MessageInfo; +import javax.security.auth.message.MessagePolicy; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.security.jaspi.modules.BaseAuthModule; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.security.Constraint; + +public class BasicAuthModule extends BaseAuthModule +{ + private static final Logger LOG = Log.getLogger(BasicAuthModule.class); + + private String realmName; + + private static final String REALM_KEY = "org.eclipse.jetty.security.jaspi.modules.RealmName"; + + public BasicAuthModule() + { + } + + public BasicAuthModule(CallbackHandler callbackHandler, String realmName) + { + super(callbackHandler); + this.realmName = realmName; + } + + @Override + public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, + CallbackHandler handler, Map options) + throws AuthException + { + super.initialize(requestPolicy, responsePolicy, handler, options); + realmName = (String)options.get(REALM_KEY); + } + + @Override + public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, + Subject serviceSubject) + throws AuthException + { + HttpServletRequest request = (HttpServletRequest)messageInfo.getRequestMessage(); + HttpServletResponse response = (HttpServletResponse)messageInfo.getResponseMessage(); + String credentials = request.getHeader(HttpHeader.AUTHORIZATION.asString()); + + try + { + if (credentials != null) + { + if (LOG.isDebugEnabled()) + LOG.debug("Credentials: " + credentials); + if (login(clientSubject, credentials, Constraint.__BASIC_AUTH, messageInfo)) + { + return AuthStatus.SUCCESS; + } + } + + if (!isMandatory(messageInfo)) + { + return AuthStatus.SUCCESS; + } + response.setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), "basic realm=\"" + realmName + '"'); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + return AuthStatus.SEND_CONTINUE; + } + catch (IOException e) + { + throw new AuthException(e.getMessage()); + } + catch (UnsupportedCallbackException e) + { + throw new AuthException(e.getMessage()); + } + } +} diff --git a/jetty-jaspi/src/test/resources/jaspi.xml b/jetty-jaspi/src/test/resources/jaspi.xml index 23a2ba5c7ed..1e31d2c996b 100644 --- a/jetty-jaspi/src/test/resources/jaspi.xml +++ b/jetty-jaspi/src/test/resources/jaspi.xml @@ -10,7 +10,7 @@ true - org.eclipse.jetty.security.jaspi.modules.BasicAuthModule + org.eclipse.jetty.security.jaspi.BasicAuthModule org.eclipse.jetty.security.jaspi.modules.RealmName=TestRealm From d33b96f4110b0cedd97af021bca28e6d58338d03 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Thu, 29 Aug 2019 07:39:19 +1000 Subject: [PATCH 005/113] add OpenId module to support OpenId Connect authentication Signed-off-by: Lachlan Roberts --- jetty-bom/pom.xml | 5 + jetty-home/pom.xml | 5 + jetty-openid/pom.xml | 64 +++ .../src/main/config/etc/jetty-openid.xml | 24 + .../src/main/config/modules/openid.mod | 31 ++ .../openid/openid-baseloginservice.xml | 10 + .../security/openid/OpenIdAuthenticator.java | 481 ++++++++++++++++++ .../openid/OpenIdAuthenticatorFactory.java | 40 ++ .../security/openid/OpenIdConfiguration.java | 118 +++++ .../security/openid/OpenIdCredentials.java | 181 +++++++ .../security/openid/OpenIdLoginService.java | 134 +++++ .../security/openid/OpenIdUserIdentity.java | 61 +++ .../security/openid/OpenIdUserPrincipal.java | 50 ++ .../openid/OpenIdAuthenticationDemo.java | 217 ++++++++ .../test/resources/jetty-logging.properties | 3 + .../jetty/util/security/Constraint.java | 5 +- pom.xml | 1 + 17 files changed, 1429 insertions(+), 1 deletion(-) create mode 100644 jetty-openid/pom.xml create mode 100644 jetty-openid/src/main/config/etc/jetty-openid.xml create mode 100644 jetty-openid/src/main/config/modules/openid.mod create mode 100644 jetty-openid/src/main/config/modules/openid/openid-baseloginservice.xml create mode 100644 jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java create mode 100644 jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticatorFactory.java create mode 100644 jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java create mode 100644 jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java create mode 100644 jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java create mode 100644 jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserIdentity.java create mode 100644 jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserPrincipal.java create mode 100644 jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java create mode 100755 jetty-openid/src/test/resources/jetty-logging.properties diff --git a/jetty-bom/pom.xml b/jetty-bom/pom.xml index 9c3b6c8b20e..60a6720c55e 100644 --- a/jetty-bom/pom.xml +++ b/jetty-bom/pom.xml @@ -299,6 +299,11 @@ jetty-security 9.4.21-SNAPSHOT + + org.eclipse.jetty + jetty-openid + 9.4.21-SNAPSHOT + org.eclipse.jetty jetty-server diff --git a/jetty-home/pom.xml b/jetty-home/pom.xml index 02201232284..89fca7a12e5 100644 --- a/jetty-home/pom.xml +++ b/jetty-home/pom.xml @@ -711,6 +711,11 @@ jetty-alpn-openjdk8-server ${project.version} + + org.eclipse.jetty + jetty-openid + ${project.version} + org.eclipse.jetty jetty-alpn-conscrypt-server diff --git a/jetty-openid/pom.xml b/jetty-openid/pom.xml new file mode 100644 index 00000000000..99d0afc8bf2 --- /dev/null +++ b/jetty-openid/pom.xml @@ -0,0 +1,64 @@ + + + org.eclipse.jetty + jetty-project + 9.4.21-SNAPSHOT + + + 4.0.0 + jetty-openid + Jetty :: OpenID + Jetty OpenID Connect infrastructure + http://www.eclipse.org/jetty + + + ${project.groupId}.openid + + + + + + org.codehaus.mojo + findbugs-maven-plugin + + org.eclipse.jetty.security.openid.* + + + + + + + + org.eclipse.jetty + jetty-server + ${project.version} + + + org.eclipse.jetty + jetty-security + ${project.version} + + + org.eclipse.jetty + jetty-util-ajax + ${project.version} + + + org.eclipse.jetty + jetty-servlet + ${project.version} + test + + + org.eclipse.jetty.tests + jetty-http-tools + ${project.version} + test + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + diff --git a/jetty-openid/src/main/config/etc/jetty-openid.xml b/jetty-openid/src/main/config/etc/jetty-openid.xml new file mode 100644 index 00000000000..df21f0ffc3a --- /dev/null +++ b/jetty-openid/src/main/config/etc/jetty-openid.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jetty-openid/src/main/config/modules/openid.mod b/jetty-openid/src/main/config/modules/openid.mod new file mode 100644 index 00000000000..869ddd5cac7 --- /dev/null +++ b/jetty-openid/src/main/config/modules/openid.mod @@ -0,0 +1,31 @@ +DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Adds OpenId Connect authentication. + +[depend] +security + +[lib] +lib/jetty-openid-${jetty.version}.jar +lib/jetty-util-ajax-${jetty.version}.jar + +[files] +basehome:modules/openid/openid-baseloginservice.xml|etc/openid-baseloginservice.xml + +[xml] +etc/openid-baseloginservice.xml +etc/jetty-openid.xml + +[ini-template] +## Identity Provider +# jetty.openid.identityProvider=https://accounts.google.com/ + +## Client ID +# jetty.openid.clientId=1051168419525-5nl60mkugb77p9j194mrh287p1e0ahfi.apps.googleusercontent.com + +## Client Secret +# jetty.openid.clientSecret=XT_MIsSv_aUCGollauCaJY8S + +## Scopes +# jetty.openid.scopes=email,profile diff --git a/jetty-openid/src/main/config/modules/openid/openid-baseloginservice.xml b/jetty-openid/src/main/config/modules/openid/openid-baseloginservice.xml new file mode 100644 index 00000000000..89bbc3de290 --- /dev/null +++ b/jetty-openid/src/main/config/modules/openid/openid-baseloginservice.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java new file mode 100644 index 00000000000..1608d7a5acb --- /dev/null +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java @@ -0,0 +1,481 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.security.openid; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.SecureRandom; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.http.MimeTypes; +import org.eclipse.jetty.security.LoginService; +import org.eclipse.jetty.security.ServerAuthException; +import org.eclipse.jetty.security.UserAuthentication; +import org.eclipse.jetty.security.authentication.DeferredAuthentication; +import org.eclipse.jetty.security.authentication.LoginAuthenticator; +import org.eclipse.jetty.security.authentication.SessionAuthentication; +import org.eclipse.jetty.server.Authentication; +import org.eclipse.jetty.server.Authentication.User; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.util.MultiMap; +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; + +/** + * OpenId Connect Authenticator. + * + *

This authenticator implements authentication using OpenId Connect on top of OAuth 2.0. + * + *

The authenticator redirects unauthenticated requests to the identity providers authorization endpoint + * which will eventually redirect back to the redirectUri with an authorization code which will be exchanged with + * the token_endpoint for an id_token. The request is then restored back to the original uri requested. + * {@link SessionAuthentication} is then used to wrap Authentication results so that they are associated with the session.

+ */ +public class OpenIdAuthenticator extends LoginAuthenticator +{ + private static final Logger LOG = Log.getLogger(OpenIdAuthenticator.class); + + public static final String __USER_CLAIMS = "org.eclipse.jetty.security.openid.user_claims"; + public static final String __RESPONSE_JSON = "org.eclipse.jetty.security.openid.response"; + public static final String __ERROR_PAGE = "org.eclipse.jetty.security.openid.error_page"; + public static final String __J_URI = "org.eclipse.jetty.security.openid.URI"; + public static final String __J_POST = "org.eclipse.jetty.security.openid.POST"; + public static final String __J_METHOD = "org.eclipse.jetty.security.openid.METHOD"; + public static final String __CSRF_TOKEN = "org.eclipse.jetty.security.openid.csrf_token"; + public static final String __J_SECURITY_CHECK = "/j_security_check"; + + private OpenIdConfiguration _configuration; + private String _errorPage; + private String _errorPath; + private boolean _alwaysSaveUri; + + public OpenIdAuthenticator() + { + } + + public OpenIdAuthenticator(OpenIdConfiguration configuration, String errorPage) + { + this._configuration = configuration; + if (errorPage != null) + setErrorPage(errorPage); + } + + @Override + public void setConfiguration(AuthConfiguration configuration) + { + super.setConfiguration(configuration); + + String error = configuration.getInitParameter(__ERROR_PAGE); + if (error != null) + setErrorPage(error); + + if (_configuration != null) + return; + + LoginService loginService = configuration.getLoginService(); + if (!(loginService instanceof OpenIdLoginService)) + throw new IllegalArgumentException("invalid LoginService"); + this._configuration = ((OpenIdLoginService)loginService).getConfiguration(); + } + + @Override + public String getAuthMethod() + { + return Constraint.__OPENID_AUTH; + } + + /** + * If true, uris that cause a redirect to a login page will always + * be remembered. If false, only the first uri that leads to a login + * page redirect is remembered. + * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=379909 + * + * @param alwaysSave true to always save the uri + */ + public void setAlwaysSaveUri(boolean alwaysSave) + { + _alwaysSaveUri = alwaysSave; + } + + public boolean getAlwaysSaveUri() + { + return _alwaysSaveUri; + } + + private void setErrorPage(String path) + { + if (path == null || path.trim().length() == 0) + { + _errorPath = null; + _errorPage = null; + } + else + { + if (!path.startsWith("/")) + { + LOG.warn("error-page must start with /"); + path = "/" + path; + } + _errorPage = path; + _errorPath = path; + + if (_errorPath.indexOf('?') > 0) + _errorPath = _errorPath.substring(0, _errorPath.indexOf('?')); + } + } + + @Override + public UserIdentity login(String username, Object credentials, ServletRequest request) + { + if (LOG.isDebugEnabled()) + LOG.debug("login {} {} {}", username, credentials, request); + + UserIdentity user = super.login(username, credentials, request); + if (user != null) + { + HttpSession session = ((HttpServletRequest)request).getSession(); + Authentication cached = new SessionAuthentication(getAuthMethod(), user, credentials); + session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached); + session.setAttribute(__USER_CLAIMS, ((OpenIdCredentials)credentials).getClaims()); + session.setAttribute(__RESPONSE_JSON, ((OpenIdCredentials)credentials).getResponse()); + } + return user; + } + + @Override + public void logout(ServletRequest request) + { + super.logout(request); + HttpServletRequest httpRequest = (HttpServletRequest)request; + HttpSession session = httpRequest.getSession(false); + + if (session == null) + return; + + //clean up session + session.removeAttribute(SessionAuthentication.__J_AUTHENTICATED); + session.removeAttribute(__USER_CLAIMS); + session.removeAttribute(__RESPONSE_JSON); + } + + @Override + public void prepareRequest(ServletRequest request) + { + //if this is a request resulting from a redirect after auth is complete + //(ie its from a redirect to the original request uri) then due to + //browser handling of 302 redirects, the method may not be the same as + //that of the original request. Replace the method and original post + //params (if it was a post). + // + //See Servlet Spec 3.1 sec 13.6.3 + HttpServletRequest httpRequest = (HttpServletRequest)request; + HttpSession session = httpRequest.getSession(false); + if (session == null || session.getAttribute(SessionAuthentication.__J_AUTHENTICATED) == null) + return; //not authenticated yet + + String juri = (String)session.getAttribute(__J_URI); + if (juri == null || juri.length() == 0) + return; //no original uri saved + + String method = (String)session.getAttribute(__J_METHOD); + if (method == null || method.length() == 0) + return; //didn't save original request method + + StringBuffer buf = httpRequest.getRequestURL(); + if (httpRequest.getQueryString() != null) + buf.append("?").append(httpRequest.getQueryString()); + + if (!juri.equals(buf.toString())) + return; //this request is not for the same url as the original + + //restore the original request's method on this request + if (LOG.isDebugEnabled()) + LOG.debug("Restoring original method {} for {} with method {}", method, juri, httpRequest.getMethod()); + Request baseRequest = Request.getBaseRequest(request); + baseRequest.setMethod(method); + } + + @Override + public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException + { + final HttpServletRequest request = (HttpServletRequest)req; + final HttpServletResponse response = (HttpServletResponse)res; + final Request baseRequest = Request.getBaseRequest(request); + final Response baseResponse = baseRequest.getResponse(); + + String uri = request.getRequestURI(); + if (uri == null) + uri = URIUtil.SLASH; + + mandatory |= isJSecurityCheck(uri); + if (!mandatory) + return new DeferredAuthentication(this); + + if (isErrorPage(URIUtil.addPaths(request.getServletPath(), request.getPathInfo())) && !DeferredAuthentication.isDeferred(response)) + return new DeferredAuthentication(this); + + try + { + // Handle a request for authentication. + if (isJSecurityCheck(uri)) + { + String authCode = request.getParameter("code"); + if (authCode != null) + { + // Verify anti-forgery state token + String state = request.getParameter("state"); + String antiForgeryToken = (String)request.getSession().getAttribute(__CSRF_TOKEN); + if (antiForgeryToken == null || !antiForgeryToken.equals(state)) + { + LOG.warn("auth failed 403: invalid state parameter"); + if (response != null) + response.sendError(HttpServletResponse.SC_FORBIDDEN); + return Authentication.SEND_FAILURE; + } + + // Attempt to login with the provided authCode + OpenIdCredentials credentials = new OpenIdCredentials(authCode, getRedirectUri(request), _configuration); + UserIdentity user = login(null, credentials, request); + HttpSession session = request.getSession(false); + if (user != null) + { + // Redirect to original request + String nuri; + synchronized (session) + { + nuri = (String)session.getAttribute(__J_URI); + + if (nuri == null || nuri.length() == 0) + { + nuri = request.getContextPath(); + if (nuri.length() == 0) + nuri = URIUtil.SLASH; + } + } + OpenIdAuthentication openIdAuth = new OpenIdAuthentication(getAuthMethod(), user); + LOG.debug("authenticated {}->{}", openIdAuth, nuri); + + response.setContentLength(0); + int redirectCode = (baseRequest.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER); + baseResponse.sendRedirect(redirectCode, response.encodeRedirectURL(nuri)); + return openIdAuth; + } + } + + // not authenticated + if (LOG.isDebugEnabled()) + LOG.debug("OpenId authentication FAILED"); + if (_errorPage == null) + { + if (LOG.isDebugEnabled()) + LOG.debug("auth failed 403"); + if (response != null) + response.sendError(HttpServletResponse.SC_FORBIDDEN); + } + else + { + if (LOG.isDebugEnabled()) + LOG.debug("auth failed {}", _errorPage); + int redirectCode = (baseRequest.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER); + baseResponse.sendRedirect(redirectCode, response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(), _errorPage))); + } + + return Authentication.SEND_FAILURE; + } + + // Look for cached authentication + HttpSession session = request.getSession(false); + Authentication authentication = session == null ? null : (Authentication)session.getAttribute(SessionAuthentication.__J_AUTHENTICATED); + if (authentication != null) + { + // Has authentication been revoked? + if (authentication instanceof Authentication.User && + _loginService != null && + !_loginService.validate(((Authentication.User)authentication).getUserIdentity())) + { + LOG.debug("auth revoked {}", authentication); + session.removeAttribute(SessionAuthentication.__J_AUTHENTICATED); + } + else + { + synchronized (session) + { + String jUri = (String)session.getAttribute(__J_URI); + if (jUri != null) + { + //check if the request is for the same url as the original and restore + //params if it was a post + LOG.debug("auth retry {}->{}", authentication, jUri); + StringBuffer buf = request.getRequestURL(); + if (request.getQueryString() != null) + buf.append("?").append(request.getQueryString()); + + if (jUri.equals(buf.toString())) + { + MultiMap jPost = (MultiMap)session.getAttribute(__J_POST); + if (jPost != null) + { + LOG.debug("auth rePOST {}->{}", authentication, jUri); + baseRequest.setContentParameters(jPost); + } + session.removeAttribute(__J_URI); + session.removeAttribute(__J_METHOD); + session.removeAttribute(__J_POST); + } + } + } + LOG.debug("auth {}", authentication); + return authentication; + } + } + + + // if we can't send challenge + if (DeferredAuthentication.isDeferred(response)) + { + LOG.debug("auth deferred {}", session == null ? null : session.getId()); + return Authentication.UNAUTHENTICATED; + } + + // remember the current URI + session = (session != null ? session : request.getSession(true)); + synchronized (session) + { + // But only if it is not set already, or we save every uri that leads to a login redirect + if (session.getAttribute(__J_URI) == null || _alwaysSaveUri) + { + StringBuffer buf = request.getRequestURL(); + if (request.getQueryString() != null) + buf.append("?").append(request.getQueryString()); + session.setAttribute(__J_URI, buf.toString()); + session.setAttribute(__J_METHOD, request.getMethod()); + + if (MimeTypes.Type.FORM_ENCODED.is(req.getContentType()) && HttpMethod.POST.is(request.getMethod())) + { + MultiMap formParameters = new MultiMap<>(); + baseRequest.extractFormParameters(formParameters); + session.setAttribute(__J_POST, formParameters); + } + } + } + + // send the the challenge + String challengeUri = getChallengeUri(request); + LOG.debug("challenge {}->{}", session.getId(), challengeUri); + int redirectCode = (baseRequest.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER); + baseResponse.sendRedirect(redirectCode, response.encodeRedirectURL(challengeUri)); + + return Authentication.SEND_CONTINUE; + } + catch (IOException e) + { + throw new ServerAuthException(e); + } + } + + public boolean isJSecurityCheck(String uri) + { + int jsc = uri.indexOf(__J_SECURITY_CHECK); + + if (jsc < 0) + return false; + int e = jsc + __J_SECURITY_CHECK.length(); + if (e == uri.length()) + return true; + char c = uri.charAt(e); + return c == ';' || c == '#' || c == '/' || c == '?'; + } + + public boolean isErrorPage(String pathInContext) + { + return pathInContext != null && (pathInContext.equals(_errorPath)); + } + + private String getRedirectUri(HttpServletRequest request) + { + final StringBuffer redirectUri = new StringBuffer(128); + URIUtil.appendSchemeHostPort(redirectUri, request.getScheme(), + request.getServerName(), request.getServerPort()); + redirectUri.append(request.getContextPath()); + redirectUri.append(__J_SECURITY_CHECK); + return redirectUri.toString(); + } + + protected String getChallengeUri(HttpServletRequest request) + { + HttpSession session = request.getSession(); + String antiForgeryToken; + synchronized (session) + { + antiForgeryToken = (session.getAttribute(__CSRF_TOKEN) == null) + ? new BigInteger(130, new SecureRandom()).toString(32) + : (String)session.getAttribute(__CSRF_TOKEN); + session.setAttribute(__CSRF_TOKEN, antiForgeryToken); + } + + // any custom scopes requested from configuration + StringBuilder scopes = new StringBuilder(); + for (String s : _configuration.getScopes()) + { + scopes.append("%20" + s); + } + + return _configuration.getAuthEndpoint() + + "?client_id=" + _configuration.getClientId() + + "&redirect_uri=" + getRedirectUri(request) + + "&scope=openid" + scopes + + "&state=" + antiForgeryToken + + "&response_type=code"; + } + + @Override + public boolean secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, User validatedUser) + { + return true; + } + + /** + * This Authentication represents a just completed OpenId Connect authentication. + * Subsequent requests from the same user are authenticated by the presents + * of a {@link SessionAuthentication} instance in their session. + */ + public static class OpenIdAuthentication extends UserAuthentication implements Authentication.ResponseSent + { + public OpenIdAuthentication(String method, UserIdentity userIdentity) + { + super(method, userIdentity); + } + + @Override + public String toString() + { + return "OpenId" + super.toString(); + } + } +} \ No newline at end of file diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticatorFactory.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticatorFactory.java new file mode 100644 index 00000000000..86eea6cdbde --- /dev/null +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticatorFactory.java @@ -0,0 +1,40 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.security.openid; + +import javax.servlet.ServletContext; + +import org.eclipse.jetty.security.Authenticator; +import org.eclipse.jetty.security.DefaultAuthenticatorFactory; +import org.eclipse.jetty.security.IdentityService; +import org.eclipse.jetty.security.LoginService; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.security.Constraint; + +public class OpenIdAuthenticatorFactory extends DefaultAuthenticatorFactory +{ + @Override + public Authenticator getAuthenticator(Server server, ServletContext context, Authenticator.AuthConfiguration configuration, IdentityService identityService, LoginService loginService) + { + String auth = configuration.getAuthMethod(); + if (Constraint.__OPENID_AUTH.equalsIgnoreCase(auth)) + return new OpenIdAuthenticator(); + return super.getAuthenticator(server, context, configuration, identityService, loginService); + } +} \ No newline at end of file diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java new file mode 100644 index 00000000000..8390eb5e037 --- /dev/null +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java @@ -0,0 +1,118 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.security.openid; + +import java.io.InputStream; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.ajax.JSON; + +public class OpenIdConfiguration +{ + private static String CONFIG_PATH = "/.well-known/openid-configuration"; + + private final String identityProvider; + private final String authEndpoint; + private final String tokenEndpoint; + private final String clientId; + private final String clientSecret; + private final Map discoveryDocument; + + private List scopes = new ArrayList<>(); + + public OpenIdConfiguration(String provider, String clientId, String clientSecret) + { + this.identityProvider = provider; + this.clientId = clientId; + this.clientSecret = clientSecret; + + try + { + if (provider.endsWith("/")) + provider = provider.substring(0, provider.length() - 1); + + URI providerUri = URI.create(provider + CONFIG_PATH); + InputStream inputStream = providerUri.toURL().openConnection().getInputStream(); + String content = IO.toString(inputStream); + discoveryDocument = (Map)JSON.parse(content); + } + catch (Throwable e) + { + throw new IllegalArgumentException("invalid identity provider", e); + } + + if (discoveryDocument.get("issuer") == null) + throw new IllegalArgumentException(); + + authEndpoint = (String)discoveryDocument.get("authorization_endpoint"); + if (authEndpoint == null) + throw new IllegalArgumentException("authorization_endpoint"); + + tokenEndpoint = (String)discoveryDocument.get("token_endpoint"); + if (tokenEndpoint == null) + throw new IllegalArgumentException("token_endpoint"); + } + + public Map getDiscoveryDocument() + { + return discoveryDocument; + } + + public String getAuthEndpoint() + { + return authEndpoint; + } + + public String getClientId() + { + return clientId; + } + + public String getClientSecret() + { + return clientSecret; + } + + public String getIdentityProvider() + { + return identityProvider; + } + + public String getTokenEndpoint() + { + return tokenEndpoint; + } + + public void addScopes(String... scopes) + { + for (String scope : scopes) + { + this.scopes.add(scope); + } + } + + public List getScopes() + { + return scopes; + } +} diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java new file mode 100644 index 00000000000..21095ff768d --- /dev/null +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java @@ -0,0 +1,181 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.security.openid; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; + +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.ajax.JSON; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +public class OpenIdCredentials +{ + private static final Logger LOG = Log.getLogger(OpenIdCredentials.class); + + private final String redirectUri; + private final OpenIdConfiguration configuration; + private String authCode; + private Map response; + private Map claims; + + public OpenIdCredentials(String authCode, String redirectUri, OpenIdConfiguration configuration) + { + this.authCode = authCode; + this.redirectUri = redirectUri; + this.configuration = configuration; + } + + public String getUserId() + { + return (String)claims.get("sub"); + } + + public Map getClaims() + { + return claims; + } + + public Map getResponse() + { + return response; + } + + public void redeemAuthCode() throws IOException + { + if (LOG.isDebugEnabled()) + LOG.debug("redeemAuthCode() {}", this); + + if (authCode != null) + { + try + { + String jwt = getJWT(); + decodeJWT(jwt); + + if (LOG.isDebugEnabled()) + LOG.debug("userInfo {}", claims); + } + finally + { + // reset authCode as it can only be used once + authCode = null; + } + } + } + + public boolean validate() + { + if (authCode != null) + return false; + + // Check audience should be clientId + String audience = (String)claims.get("aud"); + if (!configuration.getIdentityProvider().equals(audience)) + { + LOG.warn("Audience claim MUST contain the value of the Issuer Identifier for the OP", this); + //return false; + } + + String issuer = (String)claims.get("iss"); + if (!configuration.getClientId().equals(issuer)) + { + LOG.warn("Issuer claim MUST be the client_id of the OAuth Client {}", this); + //return false; + } + + // Check expiry + long expiry = (Long)claims.get("exp"); + long currentTimeSeconds = (long)(System.currentTimeMillis() / 1000F); + if (currentTimeSeconds > expiry) + { + if (LOG.isDebugEnabled()) + LOG.debug("OpenId Credentials expired {}", this); + return false; + } + + return true; + } + + private void decodeJWT(String jwt) throws IOException + { + if (LOG.isDebugEnabled()) + LOG.debug("decodeJWT {}", jwt); + + String[] sections = jwt.split("\\."); + if (sections.length != 3) + throw new IllegalArgumentException("JWT does not contain 3 sections"); + + String jwtHeaderString = new String(Base64.getDecoder().decode(sections[0]), StandardCharsets.UTF_8); + String jwtClaimString = new String(Base64.getDecoder().decode(sections[1]), StandardCharsets.UTF_8); + String jwtSignature = sections[2]; + + Map jwtHeader = (Map)JSON.parse(jwtHeaderString); + LOG.debug("JWT Header: {}", jwtHeader); + + // validate signature + LOG.warn("Signature NOT validated {}", jwtSignature); + + // response should be a set of name/value pairs + claims = (Map)JSON.parse(jwtClaimString); + } + + private String getJWT() throws IOException + { + if (LOG.isDebugEnabled()) + LOG.debug("getJWT {}", authCode); + + // Use the auth code to get the id_token from the OpenID Provider + String urlParameters = "code=" + authCode + + "&client_id=" + configuration.getClientId() + + "&client_secret=" + configuration.getClientSecret() + + "&redirect_uri=" + redirectUri + + "&grant_type=authorization_code"; + + byte[] payload = urlParameters.getBytes(StandardCharsets.UTF_8); + URL url = new URL(configuration.getTokenEndpoint()); + HttpURLConnection connection = (HttpURLConnection)url.openConnection(); + connection.setDoOutput(true); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Host", configuration.getIdentityProvider()); + connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + connection.setRequestProperty("charset", "utf-8"); + + try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) + { + wr.write(payload); + } + + // get response and extract id_token jwt + InputStream content = (InputStream)connection.getContent(); + response = (Map)JSON.parse(IO.toString(content)); + + if (LOG.isDebugEnabled()) + LOG.debug("responseMap: {}", response); + + return (String)response.get("id_token"); + } +} diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java new file mode 100644 index 00000000000..8a3914ce605 --- /dev/null +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java @@ -0,0 +1,134 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.security.openid; + +import java.io.IOException; +import java.security.Principal; +import javax.security.auth.Subject; +import javax.servlet.ServletRequest; + +import org.eclipse.jetty.security.IdentityService; +import org.eclipse.jetty.security.LoginService; +import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.util.component.ContainerLifeCycle; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +public class OpenIdLoginService extends ContainerLifeCycle implements LoginService +{ + private static final Logger LOG = Log.getLogger(OpenIdLoginService.class); + + private final OpenIdConfiguration _configuration; + private final LoginService loginService; + private IdentityService identityService; + + public OpenIdLoginService(OpenIdConfiguration configuration) + { + this(configuration, null); + } + + public OpenIdLoginService(OpenIdConfiguration configuration, LoginService loginService) + { + _configuration = configuration; + this.loginService = loginService; + addBean(this.loginService); + } + + @Override + public String getName() + { + return _configuration.getIdentityProvider(); + } + + public OpenIdConfiguration getConfiguration() + { + return _configuration; + } + + @Override + public UserIdentity login(String identifier, Object credentials, ServletRequest req) + { + if (LOG.isDebugEnabled()) + LOG.debug("login({}, {}, {})", identifier, credentials, req); + + OpenIdCredentials openIdCredentials = (OpenIdCredentials)credentials; + try + { + openIdCredentials.redeemAuthCode(); + if (!openIdCredentials.validate()) + return null; + } + catch (IOException e) + { + LOG.warn(e); + return null; + } + + OpenIdUserPrincipal userPrincipal = new OpenIdUserPrincipal(openIdCredentials); + Subject subject = new Subject(); + subject.getPrincipals().add(userPrincipal); + subject.getPrivateCredentials().add(credentials); + subject.setReadOnly(); + + if (loginService != null) + { + UserIdentity userIdentity = loginService.login(openIdCredentials.getUserId(), "", req); + if (userIdentity == null) + return null; + + return new OpenIdUserIdentity(subject, userPrincipal, userIdentity); + } + + return identityService.newUserIdentity(subject, userPrincipal, new String[0]); + } + + @Override + public boolean validate(UserIdentity user) + { + Principal userPrincipal = user.getUserPrincipal(); + if (!(userPrincipal instanceof OpenIdUserPrincipal)) + return false; + + OpenIdCredentials credentials = ((OpenIdUserPrincipal)userPrincipal).getCredentials(); + return credentials.validate(); + } + + @Override + public IdentityService getIdentityService() + { + return loginService == null ? identityService : loginService.getIdentityService(); + } + + @Override + public void setIdentityService(IdentityService service) + { + if (isRunning()) + throw new IllegalStateException("Running"); + + if (loginService != null) + loginService.setIdentityService(service); + else + identityService = service; + } + + @Override + public void logout(UserIdentity user) + { + } +} diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserIdentity.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserIdentity.java new file mode 100644 index 00000000000..1477484c5e1 --- /dev/null +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserIdentity.java @@ -0,0 +1,61 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.security.openid; + +import java.security.Principal; +import javax.security.auth.Subject; + +import org.eclipse.jetty.server.UserIdentity; + +public class OpenIdUserIdentity implements UserIdentity +{ + private final Subject subject; + private final Principal userPrincipal; + private final UserIdentity userIdentity; + + public OpenIdUserIdentity(Subject subject, Principal userPrincipal) + { + this(subject, userPrincipal, null); + } + + public OpenIdUserIdentity(Subject subject, Principal userPrincipal, UserIdentity userIdentity) + { + this.subject = subject; + this.userPrincipal = userPrincipal; + this.userIdentity = userIdentity; + } + + @Override + public Subject getSubject() + { + return subject; + } + + @Override + public Principal getUserPrincipal() + { + return userPrincipal; + } + + @Override + public boolean isUserInRole(String role, Scope scope) + { + return userIdentity == null ? false : userIdentity.isUserInRole(role, scope); + } +} diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserPrincipal.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserPrincipal.java new file mode 100644 index 00000000000..018547ed34f --- /dev/null +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserPrincipal.java @@ -0,0 +1,50 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.security.openid; + +import java.io.Serializable; +import java.security.Principal; + +public class OpenIdUserPrincipal implements Principal, Serializable +{ + private static final long serialVersionUID = -6226920753748399662L; + private final OpenIdCredentials _credentials; + + public OpenIdUserPrincipal(OpenIdCredentials credentials) + { + _credentials = credentials; + } + + public OpenIdCredentials getCredentials() + { + return _credentials; + } + + @Override + public String getName() + { + return _credentials.getUserId(); + } + + @Override + public String toString() + { + return _credentials.getUserId(); + } +} \ No newline at end of file diff --git a/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java b/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java new file mode 100644 index 00000000000..323af0d16f1 --- /dev/null +++ b/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java @@ -0,0 +1,217 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.security.openid; + +import java.io.IOException; +import java.security.Principal; +import java.util.Map; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.MimeTypes; +import org.eclipse.jetty.security.Authenticator; +import org.eclipse.jetty.security.ConstraintMapping; +import org.eclipse.jetty.security.ConstraintSecurityHandler; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.util.security.Constraint; + +public class OpenIdAuthenticationDemo +{ + public static class AdminPage extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + response.getWriter().println("

this is the admin page "+request.getUserPrincipal()+": Home

"); + } + } + + public static class LoginPage extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + response.getWriter().println("

you logged in Home

"); + } + } + + public static class LogoutPage extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + request.getSession().invalidate(); + response.sendRedirect("/"); + } + } + + public static class HomePage extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + response.setContentType(MimeTypes.Type.TEXT_HTML.asString()); + response.getWriter().println("

Home Page

"); + + Principal userPrincipal = request.getUserPrincipal(); + if (userPrincipal != null) + { + Map userInfo = (Map)request.getSession().getAttribute(OpenIdAuthenticator.__USER_CLAIMS); + response.getWriter().println("

Welcome: " + userInfo.get("name") + "

"); + response.getWriter().println("Profile
"); + response.getWriter().println("Admin
"); + response.getWriter().println("Logout
"); + } + else + { + response.getWriter().println("

Please Login Login

"); + } + } + } + + public static class ProfilePage extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + response.setContentType(MimeTypes.Type.TEXT_HTML.asString()); + Map userInfo = (Map)request.getSession().getAttribute(OpenIdAuthenticator.__USER_CLAIMS); + + response.getWriter().println("\n" + + "
\n" + + " \n" + + "

"+ userInfo.get("name") +"

\n" + + "

"+userInfo.get("email")+"

\n" + + "

UserId: " + userInfo.get("sub") +"

\n" + + "
"); + + response.getWriter().println("Home
"); + response.getWriter().println("Logout
"); + } + } + + public static class ErrorPage extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + response.setContentType(MimeTypes.Type.TEXT_HTML.asString()); + response.getWriter().println("

error: not authorized

"); + response.getWriter().println("

" + request.getUserPrincipal() + "

"); + } + } + + public static void main(String[] args) throws Exception + { + Server server = new Server(8080); + ServletContextHandler context = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS); + + // Add servlets + context.addServlet(ProfilePage.class, "/profile"); + context.addServlet(LoginPage.class, "/login"); + context.addServlet(AdminPage.class, "/admin"); + context.addServlet(LogoutPage.class, "/logout"); + context.addServlet(HomePage.class, "/*"); + context.addServlet(ErrorPage.class, "/error"); + + // configure security constraints + Constraint constraint = new Constraint(); + constraint.setName(Constraint.__OPENID_AUTH); + constraint.setRoles(new String[]{"**"}); + constraint.setAuthenticate(true); + + Constraint adminConstraint = new Constraint(); + adminConstraint.setName(Constraint.__OPENID_AUTH); + adminConstraint.setRoles(new String[]{"admin"}); + adminConstraint.setAuthenticate(true); + + // constraint mappings + ConstraintMapping profileMapping = new ConstraintMapping(); + profileMapping.setConstraint(constraint); + profileMapping.setPathSpec("/profile"); + ConstraintMapping loginMapping = new ConstraintMapping(); + loginMapping.setConstraint(constraint); + loginMapping.setPathSpec("/login"); + ConstraintMapping adminMapping = new ConstraintMapping(); + adminMapping.setConstraint(adminConstraint); + adminMapping.setPathSpec("/admin"); + + // security handler + ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler(); + securityHandler.setRealmName("OpenID Connect Authentication"); + securityHandler.addConstraintMapping(profileMapping); + securityHandler.addConstraintMapping(loginMapping); + securityHandler.addConstraintMapping(adminMapping); + + + // Google Authentication + OpenIdConfiguration configuration = new OpenIdConfiguration( + "https://accounts.google.com/", + "1051168419525-5nl60mkugb77p9j194mrh287p1e0ahfi.apps.googleusercontent.com", + "XT_MIsSv_aUCGollauCaJY8S"); + configuration.addScopes("email", "profile"); + + /* + // Microsoft Authentication + OpenIdConfiguration configuration = new OpenIdConfiguration( + "https://login.microsoftonline.com/common/v2.0", + "5f05dea8-2bd9-45de-b30f-cf5c102b8784", + "IfhQJKi-5[vxhh_=ldqt0y4PkV3z_1ca"); + */ + + /* + // Yahoo Authentication + OpenIdConfiguration configuration = new OpenIdConfiguration( + "https://login.yahoo.com", + "dj0yJmk9ME5Id05yTkdGNDdPJmQ9WVdrOU9VcHVZWEp4TkdrbWNHbzlNQS0tJnM9Y29uc3VtZXJzZWNyZXQmc3Y9MCZ4PTE2", + "1e7f0eeb0ba0af9d9198f9be760f66ae3ea9e3b5"); + configuration.addScopes("sdps-r"); + */ + + /* + // Create a realm.properties file to associate roles with users + Path tmpDir = Paths.get(System.getProperty("java.io.tmpdir")); + Path tmpPath = Files.createTempFile(tmpDir, "realm", ".properties"); + tmpPath.toFile().deleteOnExit(); + try (BufferedWriter writer = Files.newBufferedWriter(tmpPath, StandardCharsets.UTF_8, StandardOpenOption.WRITE)) + { + // :[, ...] + writer.write("114260987481616800581:,admin"); + } + + // This must be added to the OpenIdLoginService in constructor below + HashLoginService hashLoginService = new HashLoginService(); + hashLoginService.setConfig(tmpPath.toAbsolutePath().toString()); + hashLoginService.setHotReload(true); + */ + + // Configure OpenIdLoginService optionally providing a base LoginService to provide user roles + OpenIdLoginService loginService = new OpenIdLoginService(configuration);//, hashLoginService); + securityHandler.setLoginService(loginService); + + Authenticator authenticator = new OpenIdAuthenticator(configuration, "/error"); + securityHandler.setAuthenticator(authenticator); + context.setSecurityHandler(securityHandler); + + server.start(); + server.join(); + } +} diff --git a/jetty-openid/src/test/resources/jetty-logging.properties b/jetty-openid/src/test/resources/jetty-logging.properties new file mode 100755 index 00000000000..c63d0a5bf4b --- /dev/null +++ b/jetty-openid/src/test/resources/jetty-logging.properties @@ -0,0 +1,3 @@ +# Setup default logging implementation for during testing +org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog +# org.eclipse.jetty.security.openid.LEVEL=DEBUG \ No newline at end of file diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/security/Constraint.java b/jetty-util/src/main/java/org/eclipse/jetty/util/security/Constraint.java index db4b96db940..89c41a3ea25 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/security/Constraint.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/security/Constraint.java @@ -37,6 +37,8 @@ public class Constraint implements Cloneable, Serializable public static final String __SPNEGO_AUTH = "SPNEGO"; public static final String __NEGOTIATE_AUTH = "NEGOTIATE"; + public static final String __OPENID_AUTH = "OPENID"; + public static boolean validateMethod(String method) { if (method == null) @@ -48,7 +50,8 @@ public class Constraint implements Cloneable, Serializable method.equals(__CERT_AUTH) || method.equals(__CERT_AUTH2) || method.equals(__SPNEGO_AUTH) || - method.equals(__NEGOTIATE_AUTH)); + method.equals(__NEGOTIATE_AUTH) || + method.equals(__OPENID_AUTH)); } public static final int DC_UNSET = -1; diff --git a/pom.xml b/pom.xml index e717c2a81c1..6d8d4a864d6 100644 --- a/pom.xml +++ b/pom.xml @@ -94,6 +94,7 @@ jetty-server jetty-xml jetty-security + jetty-openid jetty-servlet jetty-webapp jetty-fcgi From 19369636e3bda2f1a40d81d3bb97d79aab99aa8f Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Thu, 29 Aug 2019 17:47:37 +1000 Subject: [PATCH 006/113] add additional validation, renaming and cleanups Signed-off-by: Lachlan Roberts --- .../src/main/config/etc/jetty-openid.xml | 4 +- .../src/main/config/modules/openid.mod | 2 +- .../openid/openid-baseloginservice.xml | 2 +- .../security/openid/OpenIdConfiguration.java | 8 +-- .../security/openid/OpenIdCredentials.java | 67 +++++++++++-------- .../security/openid/OpenIdLoginService.java | 4 +- .../openid/OpenIdAuthenticationDemo.java | 5 +- 7 files changed, 52 insertions(+), 40 deletions(-) diff --git a/jetty-openid/src/main/config/etc/jetty-openid.xml b/jetty-openid/src/main/config/etc/jetty-openid.xml index df21f0ffc3a..2ee2041c489 100644 --- a/jetty-openid/src/main/config/etc/jetty-openid.xml +++ b/jetty-openid/src/main/config/etc/jetty-openid.xml @@ -1,8 +1,8 @@ - + - + diff --git a/jetty-openid/src/main/config/modules/openid.mod b/jetty-openid/src/main/config/modules/openid.mod index 869ddd5cac7..cdc30710d96 100644 --- a/jetty-openid/src/main/config/modules/openid.mod +++ b/jetty-openid/src/main/config/modules/openid.mod @@ -19,7 +19,7 @@ etc/jetty-openid.xml [ini-template] ## Identity Provider -# jetty.openid.identityProvider=https://accounts.google.com/ +# jetty.openid.openIdProvider=https://accounts.google.com/ ## Client ID # jetty.openid.clientId=1051168419525-5nl60mkugb77p9j194mrh287p1e0ahfi.apps.googleusercontent.com diff --git a/jetty-openid/src/main/config/modules/openid/openid-baseloginservice.xml b/jetty-openid/src/main/config/modules/openid/openid-baseloginservice.xml index 89bbc3de290..38ada52f4f8 100644 --- a/jetty-openid/src/main/config/modules/openid/openid-baseloginservice.xml +++ b/jetty-openid/src/main/config/modules/openid/openid-baseloginservice.xml @@ -1,5 +1,5 @@ - + diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java index 917f08d5546..4bfc75ac236 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java @@ -37,6 +37,7 @@ public class OpenIdLoginService extends ContainerLifeCycle implements LoginServi private final OpenIdConfiguration _configuration; private final LoginService loginService; private IdentityService identityService; + private boolean authenticateNewUsers; public OpenIdLoginService(OpenIdConfiguration configuration) { @@ -90,14 +91,22 @@ public class OpenIdLoginService extends ContainerLifeCycle implements LoginServi { UserIdentity userIdentity = loginService.login(openIdCredentials.getUserId(), "", req); if (userIdentity == null) + { + if (authenticateNewUsers) + return getIdentityService().newUserIdentity(subject, userPrincipal, new String[0]); return null; - + } return new OpenIdUserIdentity(subject, userPrincipal, userIdentity); } return identityService.newUserIdentity(subject, userPrincipal, new String[0]); } + public void authenticateNewUsers(boolean authenticateNewUsers) + { + this.authenticateNewUsers = authenticateNewUsers; + } + @Override public boolean validate(UserIdentity user) { diff --git a/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java b/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java index 36f2cd50a4e..d3e5168dc25 100644 --- a/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java +++ b/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java @@ -18,7 +18,13 @@ package org.eclipse.jetty.security.openid; +import java.io.BufferedWriter; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; import java.security.Principal; import java.util.Map; import javax.servlet.http.HttpServlet; @@ -29,6 +35,7 @@ import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.security.Authenticator; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; +import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.util.security.Constraint; @@ -187,7 +194,7 @@ public class OpenIdAuthenticationDemo configuration.addScopes("sdps-r"); */ - /* + // Create a realm.properties file to associate roles with users Path tmpDir = Paths.get(System.getProperty("java.io.tmpdir")); Path tmpPath = Files.createTempFile(tmpDir, "realm", ".properties"); @@ -202,10 +209,9 @@ public class OpenIdAuthenticationDemo HashLoginService hashLoginService = new HashLoginService(); hashLoginService.setConfig(tmpPath.toAbsolutePath().toString()); hashLoginService.setHotReload(true); - */ // Configure OpenIdLoginService optionally providing a base LoginService to provide user roles - OpenIdLoginService loginService = new OpenIdLoginService(configuration);//, hashLoginService); + OpenIdLoginService loginService = new OpenIdLoginService(configuration, hashLoginService); securityHandler.setLoginService(loginService); Authenticator authenticator = new OpenIdAuthenticator(configuration, "/error"); From 3d673d869df2f6272e5bca9e96e528133e8b6a3e Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 30 Aug 2019 09:54:09 -0500 Subject: [PATCH 009/113] Testing of embedded examples Signed-off-by: Joakim Erdfelt --- .../eclipse/jetty/embedded/ManyHandlers.java | 53 +++++-- .../jetty/embedded/ManyHandlersTest.java | 132 ++++++++++++++++++ 2 files changed, 176 insertions(+), 9 deletions(-) create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyHandlersTest.java diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyHandlers.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyHandlers.java index 49bc494302f..c29b0c432b3 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyHandlers.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyHandlers.java @@ -30,6 +30,8 @@ import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.server.handler.HandlerList; @@ -99,20 +101,23 @@ public class ManyHandlers HttpServletResponse response) throws IOException, ServletException { - request.setAttribute("welcome", "Hello"); + response.setHeader("X-Welcome", "Greetings from WelcomeWrapHandler"); super.handle(target, baseRequest, request, response); } } - public static void main(String[] args) throws Exception + public static Server createServer(int port) throws IOException { - Server server = new Server(8080); + Server server = new Server(port); // create the handlers Handler param = new ParamHandler(); HandlerWrapper wrapper = new WelcomeWrapHandler(); Handler hello = new HelloHandler(); - Handler dft = new DefaultHandler(); + GzipHandler gzipHandler = new GzipHandler(); + gzipHandler.setMinGzipSize(10); + gzipHandler.addIncludedMimeTypes("text/plain"); + gzipHandler.addIncludedMimeTypes("text/html"); // configure request logging File requestLogFile = File.createTempFile("demo", "log"); @@ -120,16 +125,46 @@ public class ManyHandlers server.setRequestLog(ncsaLog); // create the handler collections - HandlerCollection handlers = new HandlerCollection(); - HandlerList list = new HandlerList(); + HandlerList handlers = new HandlerList(); - // link them all together + // wrap contexts around specific handlers wrapper.setHandler(hello); - list.setHandlers(new Handler[]{param, new GzipHandler()}); - handlers.setHandlers(new Handler[]{list, dft}); + ContextHandler helloContext = new ContextHandler("/hello"); + helloContext.setHandler(wrapper); + ContextHandler paramContext = new ContextHandler("/params"); + paramContext.setHandler(param); + + ContextHandlerCollection contexts = new ContextHandlerCollection(helloContext, paramContext); + + // Wrap Contexts with GZIP + gzipHandler.setHandler(contexts); + + // Set the top level Handler List + handlers.addHandler(gzipHandler); + handlers.addHandler(new DefaultHandler()); server.setHandler(handlers); + /* At this point you have the following handler hierarchy. + * + * Server.handler: + * HandlerList + * \- GzipHandler + * | \- ContextHandlerCollection + * | \- ContextHandler ("/hello") + * | | \- WelcomeWrapHandler + * | | \- HelloHandler + * | \- ContextHandler ("/params") + * | \- ParamHandler + * \- DefaultHandler + */ + + return server; + } + + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); server.start(); server.join(); } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyHandlersTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyHandlersTest.java new file mode 100644 index 00000000000..7a77a278688 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyHandlersTest.java @@ -0,0 +1,132 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URI; +import java.util.Map; +import java.util.zip.GZIPInputStream; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.ajax.JSON; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ManyHandlersTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ManyHandlers.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetParams() throws IOException + { + URI uri = server.getURI().resolve("/params?a=b&foo=bar"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + http.setRequestProperty("Accept-Encoding", "gzip"); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // dumpResponseHeaders(http); + + // test gzip + assertGzippedResponse(http); + + // test response content + String responseBody = getResponseBody(http); + Object jsonObj = JSON.parse(responseBody); + Map jsonMap = (Map)jsonObj; + assertThat("Response JSON keys.size", jsonMap.keySet().size(), is(2)); + } + + @Test + public void testGetHello() throws IOException + { + URI uri = server.getURI().resolve("/hello"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + http.setRequestProperty("Accept-Encoding", "gzip"); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // dumpResponseHeaders(http); + + // test gzip + assertGzippedResponse(http); + + // test expected header from wrapper + String welcome = http.getHeaderField("X-Welcome"); + assertThat("X-Welcome header", welcome, containsString("Greetings from WelcomeWrapHandler")); + + // test response content + String responseBody = getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Hello")); + } + + private void assertGzippedResponse(HttpURLConnection http) + { + String value = http.getHeaderField("Content-Encoding"); + assertThat("Content-Encoding", value, containsString("gzip")); + } + + private String getResponseBody(HttpURLConnection http) throws IOException + { + try (InputStream in = http.getInputStream(); + GZIPInputStream gzipInputStream = new GZIPInputStream(in)) + { + return IO.toString(gzipInputStream, UTF_8); + } + } + + @SuppressWarnings("unused") + private void dumpResponseHeaders(HttpURLConnection http) + { + int i = 0; + while (true) + { + String field = http.getHeaderField(i); + if (field == null) + return; + String key = http.getHeaderFieldKey(i); + if (key != null) + { + System.out.printf("%s: ", key); + } + System.out.println(field); + i++; + } + } +} From f6efff70d76689b1d0adedae1ea9e53e26dd2daf Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 30 Aug 2019 13:47:16 -0500 Subject: [PATCH 010/113] Issue #3989 - Selector failure notification Signed-off-by: Joakim Erdfelt --- .../org/eclipse/jetty/io/ManagedSelector.java | 73 ++-- .../jetty/test/FailedSelectorTest.java | 378 ++++++++++++++++++ 2 files changed, 420 insertions(+), 31 deletions(-) create mode 100644 tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java index d048ce6b312..3f805ff2743 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java @@ -43,6 +43,7 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.component.DumpableCollection; @@ -122,12 +123,32 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable start._started.await(); } + protected void onSelectFailed(Throwable cause) throws IOException + { + LOG.info("Restarting selector: " + toString(), cause); + } + + private void notifySelectFailed(Throwable cause) + { + try + { + onSelectFailed(cause); + } + catch (IOException e) + { + LOG.info("Failure while calling onSelectFailed()", e); + } + } + public int size() { Selector s = _selector; if (s == null) return 0; - return s.keys().size(); + Set keys = s.keys(); + if (keys == null) + return 0; + return keys.size(); } @Override @@ -210,7 +231,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable catch (RejectedExecutionException x) { if (task instanceof Closeable) - closeNoExceptions((Closeable)task); + IO.close((Closeable)task); } } @@ -246,19 +267,6 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable } } - private static void closeNoExceptions(Closeable closeable) - { - try - { - if (closeable != null) - closeable.close(); - } - catch (Throwable x) - { - LOG.ignore(x); - } - } - private void createEndPoint(SelectableChannel channel, SelectionKey selectionKey) throws IOException { EndPoint endPoint = _selectorManager.newEndPoint(channel, this, selectionKey); @@ -496,15 +504,18 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable } catch (Throwable x) { - _selector = null; + Selector selector = _selector; if (isRunning()) + { LOG.warn(x); + notifySelectFailed(x); + } else { LOG.warn(x.toString()); LOG.debug(x); } - closeNoExceptions(_selector); + IO.close(_selector); } return false; } @@ -541,13 +552,13 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable { LOG.debug("Ignoring cancelled key for channel {}", key.channel()); if (attachment instanceof EndPoint) - closeNoExceptions((EndPoint)attachment); + IO.close((Closeable)(EndPoint)attachment); } catch (Throwable x) { LOG.warn("Could not process key for channel " + key.channel(), x); if (attachment instanceof EndPoint) - closeNoExceptions((EndPoint)attachment); + IO.close((Closeable)(EndPoint)attachment); } } else @@ -556,7 +567,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable LOG.debug("Selector loop ignoring invalid key for channel {}", key.channel()); Object attachment = key.attachment(); if (attachment instanceof EndPoint) - closeNoExceptions((EndPoint)attachment); + IO.close((Closeable)(EndPoint)attachment); } } return null; @@ -661,7 +672,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable } catch (Throwable x) { - closeNoExceptions(_channel); + IO.close(_channel); LOG.warn(x); } } @@ -683,7 +694,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable } catch (Throwable x) { - closeNoExceptions(channel); + IO.close(channel); LOG.warn("Accept failed for channel " + channel, x); } @@ -722,7 +733,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable public void close() { LOG.debug("closed accept of {}", channel); - closeNoExceptions(channel); + IO.close(channel); } @Override @@ -735,7 +746,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable } catch (Throwable x) { - closeNoExceptions(channel); + IO.close(channel); _selectorManager.onAcceptFailed(channel, x); LOG.debug(x); } @@ -758,7 +769,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable protected void failed(Throwable failure) { - closeNoExceptions(channel); + IO.close(channel); LOG.warn(String.valueOf(failure)); LOG.debug(failure); _selectorManager.onAcceptFailed(channel, failure); @@ -808,7 +819,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable if (failed.compareAndSet(false, true)) { timeout.cancel(); - closeNoExceptions(channel); + IO.close(channel); ManagedSelector.this._selectorManager.connectionFailed(channel, failure, attachment); } } @@ -864,12 +875,12 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable { if (_closed == null) { - closeNoExceptions(closeable); + IO.close(closeable); } else if (!_closed.contains(closeable)) { _closed.add(closeable); - closeNoExceptions(closeable); + IO.close(closeable); } } } @@ -894,12 +905,12 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable { Object attachment = key.attachment(); if (attachment instanceof EndPoint) - closeNoExceptions((EndPoint)attachment); + IO.close((Closeable)(EndPoint)attachment); } } _selector = null; - closeNoExceptions(selector); + IO.close(selector); _stopped.countDown(); } } @@ -924,7 +935,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable } catch (Throwable failure) { - closeNoExceptions(_connect.channel); + IO.close(_connect.channel); LOG.warn(String.valueOf(failure)); LOG.debug(failure); _connect.failed(failure); diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java new file mode 100644 index 00000000000..99b6dd6ca7b --- /dev/null +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java @@ -0,0 +1,378 @@ +// +// ======================================================================== +// Copyright (c) 1995-2018 Mort Bay Consulting 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.test; + +import java.io.IOException; +import java.nio.channels.Selector; +import java.util.Collection; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Function; +import javax.servlet.AsyncContext; +import javax.servlet.AsyncEvent; +import javax.servlet.AsyncListener; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.io.ManagedSelector; +import org.eclipse.jetty.io.SelectorManager; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.DefaultHandler; +import org.eclipse.jetty.server.handler.HandlerList; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.thread.Scheduler; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class FailedSelectorTest +{ + private HttpClient client; + private Server server; + private AsyncCloseSelectorServlet asyncCloseSelectorServlet; + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @BeforeEach + public void startClient() throws Exception + { + client = new HttpClient(); + client.setIdleTimeout(2000); + client.setMaxConnectionsPerDestination(1); + client.start(); + } + + @AfterEach + public void stopClient() throws Exception + { + client.stop(); + } + + public void startServer(Function customizeServerConsumer) throws Exception + { + server = new Server(); + + ServerConnector connector = customizeServerConsumer.apply(server); + server.addConnector(connector); + + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + context.addServlet(HelloServlet.class, "/hello"); + + ServletHolder closeHolder = new ServletHolder(new CloseSelectorServlet(connector)); + context.addServlet(closeHolder, "/selector/close"); + + asyncCloseSelectorServlet = new AsyncCloseSelectorServlet(connector); + ServletHolder asyncCloseHolder = new ServletHolder(asyncCloseSelectorServlet); + asyncCloseHolder.setAsyncSupported(true); + context.addServlet(asyncCloseHolder, "/selector/async-close"); + + HandlerList handlers = new HandlerList(); + handlers.addHandler(context); + handlers.addHandler(new DefaultHandler()); + + server.setHandler(handlers); + + server.start(); + } + + @Test + public void testRebuildServerSelectorNormal() throws Exception + { + CountDownLatch failedLatch = new CountDownLatch(1); + + startServer((server) -> + { + CustomServerConnector connector = new CustomServerConnector(server, failedLatch, 1, 1); + connector.setPort(0); + return connector; + }); + + // Request /hello + assertRequestHello(); + + // Request /selector/close + assertRequestSelectorClose("/selector/close"); + + // Wait for selectors to close from action above + assertTrue(failedLatch.await(2, TimeUnit.SECONDS)); + + // Request /hello + assertRequestHello(); + } + + @Test + @Disabled + public void testRebuildServerSelectorAsync() throws Exception + { + CountDownLatch failedLatch = new CountDownLatch(1); + + startServer((server) -> + { + CustomServerConnector connector = new CustomServerConnector(server, failedLatch, 1, 1); + connector.setPort(0); + return connector; + }); + + // Request /hello + assertRequestHello(); + + // Request /selector/async-close + assertRequestSelectorClose("/selector/async-close"); + + // Wait for selectors to close from action above + assertTrue(failedLatch.await(2, TimeUnit.SECONDS)); + + // Ensure that Async Listener onError was called + assertTrue(asyncCloseSelectorServlet.onErrorLatch.await(2, TimeUnit.SECONDS)); + + // Request /hello + assertRequestHello(); + } + + private void assertRequestSelectorClose(String path) throws InterruptedException, ExecutionException, TimeoutException + { + ContentResponse response = client.newRequest(server.getURI().resolve(path)) + .method(HttpMethod.GET) + .header(HttpHeader.CONNECTION, "close") + .send(); + + assertThat("/selector/close status", response.getStatus(), is(HttpStatus.OK_200)); + assertThat("/selector/close response", response.getContentAsString(), startsWith("Closing selectors ")); + } + + private void assertRequestHello() throws InterruptedException, ExecutionException, TimeoutException + { + ContentResponse response = client.newRequest(server.getURI().resolve("/hello")) + .method(HttpMethod.GET) + .header(HttpHeader.CONNECTION, "close") + .send(); + + assertThat("/hello status", response.getStatus(), is(HttpStatus.OK_200)); + assertThat("/hello response", response.getContentAsString(), startsWith("Hello ")); + } + + public static class CustomServerConnector extends ServerConnector + { + private final CountDownLatch failedLatch; + + public CustomServerConnector(Server server, CountDownLatch failedLatch, int acceptors, int selectors) + { + super(server, acceptors, selectors); + this.failedLatch = failedLatch; + } + + @Override + protected SelectorManager newSelectorManager(Executor executor, Scheduler scheduler, int selectors) + { + return new ServerConnectorManager(executor, scheduler, selectors) + { + @Override + protected ManagedSelector newSelector(int id) + { + return new CustomManagedSelector(this, id, failedLatch); + } + }; + } + } + + public static class CustomManagedSelector extends ManagedSelector + { + private static final Logger LOG = Log.getLogger(CustomManagedSelector.class); + private final CountDownLatch failedLatch; + + public CustomManagedSelector(SelectorManager selectorManager, int id, CountDownLatch failedLatch) + { + super(selectorManager, id); + this.failedLatch = failedLatch; + } + + @Override + protected void onSelectFailed(Throwable cause) + { + try + { + LOG.debug("onSelectFailed()", cause); + // this.startSelector(); + } + catch (Exception ex) + { + LOG.warn(ex); + } + failedLatch.countDown(); + } + } + + public static class HelloServlet extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + resp.setContentType("text/plain"); + resp.setCharacterEncoding("utf-8"); + resp.getWriter().printf("Hello %s:%d%n", req.getRemoteAddr(), req.getRemotePort()); + } + } + + private static class InterruptSelector implements Runnable + { + private static final Logger LOG = Log.getLogger(InterruptSelector.class); + private final ServerConnector connector; + + public InterruptSelector(ServerConnector connector) + { + this.connector = connector; + } + + @Override + public void run() + { + SelectorManager selectorManager = connector.getSelectorManager(); + Collection managedSelectors = selectorManager.getBeans(ManagedSelector.class); + for (ManagedSelector managedSelector : managedSelectors) + { + if (managedSelector instanceof CustomManagedSelector) + { + CustomManagedSelector customManagedSelector = (CustomManagedSelector)managedSelector; + Selector selector = customManagedSelector.getSelector(); + LOG.debug("Closing selector {}}", selector); + IO.close(selector); + } + } + } + } + + public static class CloseSelectorServlet extends HttpServlet + { + private static final int DELAY_MS = 500; + private ServerConnector connector; + private ScheduledExecutorService scheduledExecutorService; + + public CloseSelectorServlet(ServerConnector connector) + { + this.connector = connector; + scheduledExecutorService = Executors.newScheduledThreadPool(5); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + resp.setContentType("text/plain"); + resp.setCharacterEncoding("utf-8"); + resp.getWriter().printf("Closing selectors in %,d ms%n", DELAY_MS); + scheduledExecutorService.schedule(new InterruptSelector(connector), DELAY_MS, TimeUnit.MILLISECONDS); + } + } + + public static class AsyncCloseSelectorServlet extends HttpServlet + { + private static final int DELAY_MS = 200; + private ServerConnector connector; + private ScheduledExecutorService scheduledExecutorService; + public CountDownLatch onErrorLatch = new CountDownLatch(1); + + public AsyncCloseSelectorServlet(ServerConnector connector) + { + this.connector = connector; + scheduledExecutorService = Executors.newScheduledThreadPool(5); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + resp.setContentType("text/plain"); + resp.setCharacterEncoding("utf-8"); + ServletOutputStream out = resp.getOutputStream(); + out.print("Closing selectors " + DELAY_MS); + + AsyncContext asyncContext = req.startAsync(); + asyncContext.setTimeout(0); + asyncContext.addListener(new AsyncListener() + { + @Override + public void onComplete(AsyncEvent event) + { + } + + @Override + public void onTimeout(AsyncEvent event) + { + } + + @Override + public void onError(AsyncEvent event) + { + resp.setStatus(500); + event.getAsyncContext().complete(); + onErrorLatch.countDown(); + } + + @Override + public void onStartAsync(AsyncEvent event) + { + } + }); + + scheduledExecutorService.schedule(new InterruptSelector(connector), DELAY_MS, TimeUnit.MILLISECONDS); + /* trigger EofException after selector close + scheduledExecutorService.schedule(() -> + { + byte[] b = new byte[128 * 1024 * 1024]; + Arrays.fill(b, (byte)'x'); + try + { + out.write(b); + out.flush(); + } + catch (IOException e) + { + e.printStackTrace(System.out); + } + }, DELAY_MS * 2, TimeUnit.MILLISECONDS); + */ + } + } +} \ No newline at end of file From 9305914d6a66552b761eb6c519d94c266f1daa20 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 4 Sep 2019 11:16:24 -0500 Subject: [PATCH 011/113] Issue #3989 - Selector failure notification Signed-off-by: Joakim Erdfelt --- .../org/eclipse/jetty/io/ManagedSelector.java | 31 +++++++++++-------- .../jetty/test/FailedSelectorTest.java | 9 +++--- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java index 3f805ff2743..60fc3615d0f 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java @@ -107,7 +107,16 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable protected void doStart() throws Exception { super.doStart(); + startSelector(); + // Set started only if we really are started + Start start = new Start(); + submit(start); + start._started.await(); + } + + protected void startSelector() throws IOException, InterruptedException + { _selector = _selectorManager.newSelector(); // The producer used by the strategies will never @@ -116,16 +125,12 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable // The normal strategy obtains the produced task, schedules // a new thread to produce more, runs the task and then exits. _selectorManager.execute(_strategy::produce); - - // Set started only if we really are started - Start start = new Start(); - submit(start); - start._started.await(); } - protected void onSelectFailed(Throwable cause) throws IOException + protected void onSelectFailed(Throwable cause) throws Exception { LOG.info("Restarting selector: " + toString(), cause); + startSelector(); } private void notifySelectFailed(Throwable cause) @@ -134,9 +139,9 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable { onSelectFailed(cause); } - catch (IOException e) + catch (Throwable x) { - LOG.info("Failure while calling onSelectFailed()", e); + LOG.info("Failure while calling onSelectFailed()", x); } } @@ -515,7 +520,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable LOG.warn(x.toString()); LOG.debug(x); } - IO.close(_selector); + IO.close(selector); } return false; } @@ -552,13 +557,13 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable { LOG.debug("Ignoring cancelled key for channel {}", key.channel()); if (attachment instanceof EndPoint) - IO.close((Closeable)(EndPoint)attachment); + IO.close((EndPoint)attachment); } catch (Throwable x) { LOG.warn("Could not process key for channel " + key.channel(), x); if (attachment instanceof EndPoint) - IO.close((Closeable)(EndPoint)attachment); + IO.close((EndPoint)attachment); } } else @@ -567,7 +572,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable LOG.debug("Selector loop ignoring invalid key for channel {}", key.channel()); Object attachment = key.attachment(); if (attachment instanceof EndPoint) - IO.close((Closeable)(EndPoint)attachment); + IO.close((EndPoint)attachment); } } return null; @@ -905,7 +910,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable { Object attachment = key.attachment(); if (attachment instanceof EndPoint) - IO.close((Closeable)(EndPoint)attachment); + IO.close((EndPoint)attachment); } } diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java index 99b6dd6ca7b..d22f595549d 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java @@ -231,18 +231,17 @@ public class FailedSelectorTest } @Override - protected void onSelectFailed(Throwable cause) + protected void onSelectFailed(Throwable cause) throws Exception { try { LOG.debug("onSelectFailed()", cause); - // this.startSelector(); + this.startSelector(); } - catch (Exception ex) + finally { - LOG.warn(ex); + failedLatch.countDown(); } - failedLatch.countDown(); } } From 74dbec4e8d6e64f9c567b38aafaeeb9c2b21bfe0 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 4 Sep 2019 15:46:46 -0500 Subject: [PATCH 012/113] More testing of embedded examples Signed-off-by: Joakim Erdfelt --- .../jetty/embedded/AsyncEchoServlet.java | 3 +- .../eclipse/jetty/embedded/ExampleServer.java | 10 +- .../jetty/embedded/ExampleServerXml.java | 19 ++- .../jetty/embedded/FastFileServer.java | 12 +- .../eclipse/jetty/embedded/FileServer.java | 21 ++- .../eclipse/jetty/embedded/FileServerXml.java | 20 ++- .../org/eclipse/jetty/embedded/JarServer.java | 27 +++- .../jetty/embedded/JettyDistribution.java | 36 +++-- .../eclipse/jetty/embedded/LikeJettyXml.java | 62 +++++---- .../jetty/embedded/ManyConnectors.java | 37 +++--- .../eclipse/jetty/embedded/ManyContexts.java | 22 +-- .../src/main/{resources => other}/content.jar | Bin .../embedded/src/main/resources/etc/keystore | Bin 0 -> 3697 bytes .../src/main/resources/etc/keystore.pkf | 20 +++ .../src/main/resources/exampleserver.xml | 4 +- .../src/main/resources/fileserver.xml | 8 +- .../jetty/embedded/ExampleServerTest.java | 97 ++++++++++++++ .../jetty/embedded/ExampleServerXmlTest.java | 64 +++++++++ .../jetty/embedded/FastFileServerTest.java | 85 ++++++++++++ .../jetty/embedded/FileServerTest.java | 86 ++++++++++++ .../jetty/embedded/FileServerXmlTest.java | 85 ++++++++++++ .../org/eclipse/jetty/embedded/HttpUtil.java | 125 ++++++++++++++++++ .../eclipse/jetty/embedded/JarServerTest.java | 78 +++++++++++ .../jetty/embedded/LikeJettyXmlTest.java | 76 +++++++++++ .../jetty/embedded/ManyConnectorsTest.java | 76 +++++++++++ .../jetty/embedded/ManyContextsTest.java | 108 +++++++++++++++ .../jetty/embedded/ManyHandlersTest.java | 50 +------ .../eclipse/jetty/embedded/ServerUtil.java | 88 ++++++++++++ 28 files changed, 1178 insertions(+), 141 deletions(-) rename examples/embedded/src/main/{resources => other}/content.jar (100%) create mode 100644 examples/embedded/src/main/resources/etc/keystore create mode 100644 examples/embedded/src/main/resources/etc/keystore.pkf create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerXmlTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/FastFileServerTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerXmlTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/HttpUtil.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/JarServerTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyConnectorsTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyContextsTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerUtil.java diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/AsyncEchoServlet.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/AsyncEchoServlet.java index e225703dc2b..7a53073e6ee 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/AsyncEchoServlet.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/AsyncEchoServlet.java @@ -22,7 +22,6 @@ import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; import javax.servlet.AsyncContext; import javax.servlet.ReadListener; -import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; import javax.servlet.WriteListener; @@ -35,7 +34,7 @@ public class AsyncEchoServlet extends HttpServlet private static final long serialVersionUID = 1L; @Override - protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException { AsyncContext asyncContext = request.startAsync(request, response); asyncContext.setTimeout(0); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServer.java index b4d5ac45fff..57f28601b94 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServer.java @@ -28,12 +28,12 @@ import org.eclipse.jetty.servlet.ServletContextHandler; public class ExampleServer { - public static void main(String[] args) throws Exception + public static Server createServer(int port) { Server server = new Server(); ServerConnector connector = new ServerConnector(server); - connector.setPort(8080); + connector.setPort(port); server.setConnectors(new Connector[]{connector}); ServletContextHandler context = new ServletContextHandler(); @@ -45,6 +45,12 @@ public class ExampleServer handlers.setHandlers(new Handler[]{context, new DefaultHandler()}); server.setHandler(handlers); + return server; + } + + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServerXml.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServerXml.java index e65f001967a..ef6b253136a 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServerXml.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServerXml.java @@ -18,21 +18,30 @@ package org.eclipse.jetty.embedded; +import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.xml.XmlConfiguration; /** * Configures and Starts a Jetty server from an XML declaration. - *

- * See exampleserver.xml - *

*/ public class ExampleServerXml { - public static void main(String[] args) throws Exception + public static Server createServer(int port) throws Exception { // Find Jetty XML (in classpath) that configures and starts Server. + // See src/main/resources/exampleserver.xml Resource serverXml = Resource.newSystemResource("exampleserver.xml"); - XmlConfiguration.main(serverXml.getFile().getAbsolutePath()); + XmlConfiguration xml = new XmlConfiguration(serverXml); + xml.getProperties().put("http.port", Integer.toString(port)); + Server server = (Server)xml.configure(); + return server; + } + + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); + server.start(); + server.join(); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java index bfcc5493637..f9508d0d12c 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java @@ -58,17 +58,23 @@ import org.eclipse.jetty.util.resource.Resource; */ public class FastFileServer { - public static void main(String[] args) throws Exception + public static Server createServer(int port, File resourceBase) { - Server server = new Server(8080); + Server server = new Server(port); HandlerList handlers = new HandlerList(); handlers.setHandlers(new Handler[]{ - new FastFileHandler(new File(System.getProperty("user.dir"))), + new FastFileHandler(resourceBase), new DefaultHandler() }); server.setHandler(handlers); + return server; + } + public static void main(String[] args) throws Exception + { + File directory = new File(System.getProperty("user.dir")); + Server server = createServer(8080, directory); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java index 1934f2e7312..3a0e78a8164 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java @@ -18,11 +18,16 @@ package org.eclipse.jetty.embedded; +import java.nio.file.Path; +import java.nio.file.Paths; + import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.util.resource.PathResource; +import org.eclipse.jetty.util.resource.Resource; /** * Simple Jetty FileServer. @@ -30,12 +35,12 @@ import org.eclipse.jetty.server.handler.ResourceHandler; */ public class FileServer { - public static void main(String[] args) throws Exception + public static Server createServer(int port, Resource baseResource) throws Exception { // Create a basic Jetty server object that will listen on port 8080. Note that if you set this to port 0 // then a randomly available port will be assigned that you can either look in the logs for the port, // or programmatically obtain it for use in test cases. - Server server = new Server(8080); + Server server = new Server(port); // Create the ResourceHandler. It is the object that will actually handle the request for a given file. It is // a Jetty Handler object so it is suitable for chaining with other handlers as you will see in other examples. @@ -45,13 +50,23 @@ public class FileServer // In this example it is the current directory but it can be configured to anything that the jvm has access to. resourceHandler.setDirectoriesListed(true); resourceHandler.setWelcomeFiles(new String[]{"index.html"}); - resourceHandler.setResourceBase("."); + resourceHandler.setBaseResource(baseResource); // Add the ResourceHandler to the server. HandlerList handlers = new HandlerList(); handlers.setHandlers(new Handler[]{resourceHandler, new DefaultHandler()}); server.setHandler(handlers); + return server; + } + + public static void main(String[] args) throws Exception + { + Path userDir = Paths.get(System.getProperty("user.dir")); + PathResource pathResource = new PathResource(userDir); + + Server server = createServer(8080, pathResource); + // Start things up! By using the server.join() the server thread will join with the current thread. // See "http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.html#join()" for more details. server.start(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServerXml.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServerXml.java index 8b37cdb6fdd..9b56cf65c6d 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServerXml.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServerXml.java @@ -18,6 +18,9 @@ package org.eclipse.jetty.embedded; +import java.nio.file.Path; +import java.nio.file.Paths; + import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.xml.XmlConfiguration; @@ -28,17 +31,24 @@ import org.eclipse.jetty.xml.XmlConfiguration; * This server is identical to {@link FileServer}, except that it is configured * via an {@link XmlConfiguration} config file that does the identical work. *

- *

- * See fileserver.xml - *

*/ public class FileServerXml { - public static void main(String[] args) throws Exception + public static Server createServer(int port, Path baseResource) throws Exception { + // Find Jetty XML (in classpath) that configures and starts Server. + // See src/main/resources/fileserver.xml Resource fileServerXml = Resource.newSystemResource("fileserver.xml"); XmlConfiguration configuration = new XmlConfiguration(fileServerXml); - Server server = (Server)configuration.configure(); + configuration.getProperties().put("http.port", Integer.toString(port)); + configuration.getProperties().put("fileserver.baseresource", baseResource.toAbsolutePath().toString()); + return (Server)configuration.configure(); + } + + public static void main(String[] args) throws Exception + { + Path userDir = Paths.get(System.getProperty("user.dir")); + Server server = createServer(8080, userDir); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JarServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JarServer.java index 9b32d6a16cc..4d31cf86512 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JarServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JarServer.java @@ -18,7 +18,11 @@ package org.eclipse.jetty.embedded; -import org.eclipse.jetty.server.Handler; +import java.io.FileNotFoundException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerList; @@ -28,24 +32,35 @@ import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.resource.Resource; /** - * + * Example of serving content from a JAR file. + * The JAR file in this example does not belong to any Classpath. */ public class JarServer { - public static void main(String[] args) throws Exception + public static Server createServer(int port) throws Exception { - Server server = new Server(8080); + Server server = new Server(port); + + Path jarFile = Paths.get("src/main/other/content.jar"); + if (!Files.exists(jarFile)) + throw new FileNotFoundException(jarFile.toString()); ServletContextHandler context = new ServletContextHandler(); Resource.setDefaultUseCaches(true); - Resource base = Resource.newResource("jar:file:src/main/resources/content.jar!/"); + Resource base = Resource.newResource("jar:" + jarFile.toAbsolutePath().toUri().toASCIIString() + "!/"); context.setBaseResource(base); context.addServlet(new ServletHolder(new DefaultServlet()), "/"); HandlerList handlers = new HandlerList(); - handlers.setHandlers(new Handler[]{context, new DefaultHandler()}); + handlers.addHandler(context); + handlers.addHandler(new DefaultHandler()); server.setHandler(handlers); + return server; + } + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java index 97118cfe32e..813e8b785d5 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java @@ -18,8 +18,9 @@ package org.eclipse.jetty.embedded; -import java.io.File; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.log.Log; @@ -48,7 +49,7 @@ public class JettyDistribution { try { - Path working = new File(".").getAbsoluteFile().getCanonicalFile().toPath(); + Path working = Paths.get(System.getProperty("user.dir")); while (distro == null && working != null) { distro = asJettyDistribution(working.resolve("jetty-distribution/target/distribution").toString()); @@ -63,32 +64,43 @@ public class JettyDistribution DISTRIBUTION = distro; } - private static Path asJettyDistribution(String test) + private static Path asJettyDistribution(String jettyHome) { try { - if (StringUtil.isBlank(test)) + if (jettyHome == null) { - LOG.info("asJettyDistribution {} is blank", test); return null; } - File dir = new File(test); - if (!dir.exists() || !dir.isDirectory()) + if (StringUtil.isBlank(jettyHome)) { - LOG.info("asJettyDistribution {} is not a directory", test); + LOG.debug("asJettyDistribution {} is blank", jettyHome); return null; } - File demoBase = new File(dir, "demo-base"); - if (!demoBase.exists() || !demoBase.isDirectory()) + Path dir = Paths.get(jettyHome); + if (!Files.exists(dir)) { - LOG.info("asJettyDistribution {} has no demo-base", test); + LOG.debug("asJettyDistribution {} does not exist", jettyHome); + return null; + } + + if (!Files.isDirectory(dir)) + { + LOG.info("asJettyDistribution {} is not a directory", jettyHome); + return null; + } + + Path demoBase = dir.resolve("demo-base"); + if (!Files.exists(demoBase) || !Files.isDirectory(demoBase)) + { + LOG.info("asJettyDistribution {} has no demo-base", jettyHome); return null; } LOG.info("asJettyDistribution {}", dir); - return dir.getAbsoluteFile().getCanonicalFile().toPath(); + return dir.toAbsolutePath(); } catch (Exception e) { diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java index 54a9737464c..261e22a9f96 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java @@ -18,8 +18,11 @@ package org.eclipse.jetty.embedded; -import java.io.File; +import java.io.FileNotFoundException; import java.lang.management.ManagementFactory; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.PropertiesConfigurationManager; @@ -58,21 +61,21 @@ import org.eclipse.jetty.webapp.Configuration; */ public class LikeJettyXml { - public static void main(String[] args) throws Exception + public static Server createServer(int port, int securePort, boolean addDebugListener) throws Exception { // Path to as-built jetty-distribution directory String jettyHomeBuild = JettyDistribution.DISTRIBUTION.toString(); // Find jetty home and base directories String homePath = System.getProperty("jetty.home", jettyHomeBuild); - File homeDir = new File(homePath); + Path homeDir = Paths.get(homePath); - String basePath = System.getProperty("jetty.base", homeDir + "/demo-base"); - File baseDir = new File(basePath); + String basePath = System.getProperty("jetty.base", homeDir.resolve("demo-base").toString()); + Path baseDir = Paths.get(basePath); // Configure jetty.home and jetty.base system properties - String jettyHome = homeDir.getAbsolutePath(); - String jettyBase = baseDir.getAbsolutePath(); + String jettyHome = homeDir.toAbsolutePath().toString(); + String jettyBase = baseDir.toAbsolutePath().toString(); System.setProperty("jetty.home", jettyHome); System.setProperty("jetty.base", jettyBase); @@ -90,7 +93,7 @@ public class LikeJettyXml // HTTP Configuration HttpConfiguration httpConfig = new HttpConfiguration(); httpConfig.setSecureScheme("https"); - httpConfig.setSecurePort(8443); + httpConfig.setSecurePort(securePort); httpConfig.setOutputBufferSize(32768); httpConfig.setRequestHeaderSize(8192); httpConfig.setResponseHeaderSize(8192); @@ -104,11 +107,6 @@ public class LikeJettyXml handlers.setHandlers(new Handler[]{contexts, new DefaultHandler()}); server.setHandler(handlers); - // Extra options - server.setDumpAfterStart(true); - server.setDumpBeforeStop(false); - server.setStopAtShutdown(true); - // === jetty-jmx.xml === MBeanContainer mbContainer = new MBeanContainer( ManagementFactory.getPlatformMBeanServer()); @@ -117,24 +115,21 @@ public class LikeJettyXml // === jetty-http.xml === ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfig)); - http.setPort(8080); + http.setPort(port); http.setIdleTimeout(30000); server.addConnector(http); // === jetty-https.xml === // SSL Context Factory + Path keystorePath = Paths.get("src/main/resources/etc/keystore").toAbsolutePath(); + if (!Files.exists(keystorePath)) + throw new FileNotFoundException(keystorePath.toString()); SslContextFactory sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(jettyHome + "/../../../jetty-server/src/test/config/etc/keystore"); + sslContextFactory.setKeyStorePath(keystorePath.toString()); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); - sslContextFactory.setTrustStorePath(jettyHome + "/../../../jetty-server/src/test/config/etc/keystore"); + sslContextFactory.setTrustStorePath(keystorePath.toString()); sslContextFactory.setTrustStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); - sslContextFactory.setExcludeCipherSuites("SSL_RSA_WITH_DES_CBC_SHA", - "SSL_DHE_RSA_WITH_DES_CBC_SHA", "SSL_DHE_DSS_WITH_DES_CBC_SHA", - "SSL_RSA_EXPORT_WITH_RC4_40_MD5", - "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", - "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", - "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"); // SSL HTTP Configuration HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig); @@ -144,14 +139,17 @@ public class LikeJettyXml ServerConnector sslConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), new HttpConnectionFactory(httpsConfig)); - sslConnector.setPort(8443); + sslConnector.setPort(securePort); server.addConnector(sslConnector); // === jetty-deploy.xml === DeploymentManager deployer = new DeploymentManager(); - DebugListener debug = new DebugListener(System.err, true, true, true); - server.addBean(debug); - deployer.addLifeCycleBinding(new DebugListenerBinding(debug)); + if (addDebugListener) + { + DebugListener debug = new DebugListener(System.err, true, true, true); + server.addBean(debug); + deployer.addLifeCycleBinding(new DebugListenerBinding(debug)); + } deployer.setContexts(contexts); deployer.setContextAttribute( "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", @@ -215,6 +213,18 @@ public class LikeJettyXml login.setHotReload(false); server.addBean(login); + return server; + } + + public static void main(String[] args) throws Exception + { + Server server = createServer(8080, 8443, true); + + // Extra options + server.setDumpAfterStart(true); + server.setDumpBeforeStop(false); + server.setStopAtShutdown(true); + // Start the server server.start(); server.join(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java index 13b41236678..71d527f40e7 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java @@ -18,8 +18,10 @@ package org.eclipse.jetty.embedded; -import java.io.File; import java.io.FileNotFoundException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.server.Connector; @@ -36,23 +38,13 @@ import org.eclipse.jetty.util.ssl.SslContextFactory; */ public class ManyConnectors { - public static void main(String[] args) throws Exception + public static Server createServer(int plainPort, int securePort) throws Exception { // Since this example shows off SSL configuration, we need a keystore - // with the appropriate key. These lookup of jetty.home is purely a hack - // to get access to a keystore that we use in many unit tests and should - // probably be a direct path to your own keystore. - - String jettyDistKeystore = "../../jetty-distribution/target/distribution/demo-base/etc/keystore"; - String keystorePath = System.getProperty("example.keystore", jettyDistKeystore); - File keystoreFile = new File(keystorePath); - if (!keystoreFile.exists()) - { - keystorePath = "jetty-distribution/target/distribution/demo-base/etc/keystore"; - keystoreFile = new File(keystorePath); - if (!keystoreFile.exists()) - throw new FileNotFoundException(keystoreFile.getAbsolutePath()); - } + // with the appropriate key. + Path keystorePath = Paths.get("src/main/resources/etc/keystore").toAbsolutePath(); + if (!Files.exists(keystorePath)) + throw new FileNotFoundException(keystorePath.toString()); // Create a basic jetty server object without declaring the port. Since // we are configuring connectors directly we'll be setting ports on @@ -67,7 +59,7 @@ public class ManyConnectors // done. The port for secured communication is also set here. HttpConfiguration httpConfig = new HttpConfiguration(); httpConfig.setSecureScheme("https"); - httpConfig.setSecurePort(8443); + httpConfig.setSecurePort(securePort); httpConfig.setOutputBufferSize(32768); // HTTP connector @@ -77,7 +69,7 @@ public class ManyConnectors // configure an idle timeout. ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfig)); - http.setPort(8080); + http.setPort(plainPort); http.setIdleTimeout(30000); // SSL Context Factory for HTTPS @@ -88,7 +80,7 @@ public class ManyConnectors // keystore to be used. SslContextFactory sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath()); + sslContextFactory.setKeyStorePath(keystorePath.toString()); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); @@ -118,7 +110,7 @@ public class ManyConnectors ServerConnector https = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), new HttpConnectionFactory(httpsConfig)); - https.setPort(8443); + https.setPort(securePort); https.setIdleTimeout(500000); // Here you see the server having multiple connectors registered with @@ -132,7 +124,12 @@ public class ManyConnectors // Set a handler server.setHandler(new HelloHandler()); + return server; + } + public static void main(String[] args) throws Exception + { + Server server = createServer(8080, 8443); // Start the server server.start(); server.dumpStdErr(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyContexts.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyContexts.java index dc1f10985f5..1aae23f5b5d 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyContexts.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyContexts.java @@ -18,39 +18,41 @@ package org.eclipse.jetty.embedded; -import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; public class ManyContexts { - public static void main(String[] args) throws Exception + public static Server createServer(int port) { - Server server = new Server(8080); + Server server = new Server(port); ContextHandler context = new ContextHandler("/"); context.setContextPath("/"); context.setHandler(new HelloHandler("Root Hello")); ContextHandler contextFR = new ContextHandler("/fr"); - contextFR.setHandler(new HelloHandler("Bonjoir")); + contextFR.setHandler(new HelloHandler("Bonjour")); ContextHandler contextIT = new ContextHandler("/it"); - contextIT.setHandler(new HelloHandler("Bongiorno")); + contextIT.setHandler(new HelloHandler("Buongiorno")); ContextHandler contextV = new ContextHandler("/"); contextV.setVirtualHosts(new String[]{"127.0.0.2"}); contextV.setHandler(new HelloHandler("Virtual Hello")); - ContextHandlerCollection contexts = new ContextHandlerCollection(); - contexts.setHandlers(new Handler[]{ - context, contextFR, contextIT, - contextV - }); + ContextHandlerCollection contexts = new ContextHandlerCollection( + context, contextFR, contextIT, contextV + ); server.setHandler(contexts); + return server; + } + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); server.start(); server.dumpStdErr(); server.join(); diff --git a/examples/embedded/src/main/resources/content.jar b/examples/embedded/src/main/other/content.jar similarity index 100% rename from examples/embedded/src/main/resources/content.jar rename to examples/embedded/src/main/other/content.jar diff --git a/examples/embedded/src/main/resources/etc/keystore b/examples/embedded/src/main/resources/etc/keystore new file mode 100644 index 0000000000000000000000000000000000000000..d6592f95ee93575d659f28e1c2ebb81191484986 GIT binary patch literal 3697 zcmd6pX*d+@{>NvrWFIDxY+1`X7+JztLzb~8nyh1tFhllAM#9*skfo5_gREH|Ye<*} zjioF#A|{kAS+bnz>746yuJeEMe|KKoFTTI)zOU?< z#FyiM0S zgr$YM*b&YG_ zBYG^7qphlg*P^t%*8HVu*QIYChB#Y3%{|xDITzOcS5-xyIyE%==@R8Y2F%EN$6tMG zwQ~+5^ZD_b{g(m9_2BQ0{&XXjG03a(Cfv|JEqWr-i^`OAfZB{rft_(B9hc^H$E)_a zjgLn%qTnla5ZwQM;E?px_@7IR$8USXlh(u`>^GMh9l|HpP569{Uh#aQL+p(OHnqe z)(^mk4f`p1`(oj2Np3=Cw1)BGr;+8tJmlLZNPmq-TqKgs#r8Y1q7IwBBO@4BD*1NS zBDbi)W>k;I3;j$!MM*5gnNFBdvoq0<+V!T{qf(LrAI1dGb`iTaVNLu#EV;tO#x)c^@!-Q)A?S< z?%W8;T{x!36yKF7cVg1G!7S0?Q)zgxTU&-sGxU7Z;ZOz?lFbZ?nlsYypI`VqD#}nm zIY7RC&6o@WaXNPA9~tE`Sxpu6s9ERtoptCD=Q7mnt0vjZ!RyA2LInmLiX&I_U;9XH zzBP&BwK2(XK5`zqsk6U+b?dtUY#q^aYRgiXQ9=`KZ&S;NZtvLkutD1^3!-9lM-F=%E00v__Y1q(j|bzhne1L zzpOFaVm?e=8qekKJ61kqX}x%>OvS44k4H{lWrfBLoUB^#dt2=%@HSr`BsB;*&Buol zvO#Z%BPw-=N-4V?Kguu>t*Z1lx8lEXlpan^K8vpU3>PDBVXzrWZYBKPJD1|kbEI!5 zYOu1mv-LS!NV9!cdC52BhSF4#DCG5@5_yY=6TDK*J)1r#Y|3-sE;Bi|2@XYFJJCR}`0OVC88V)?m`{9}V6z+VXX2E-#W-N8I*qRc$Pm4xc zEZWN{!_)u(a5F3t+yILN<>rAwKoE$I-nu~)#>T)T6>Su~!v+Mh&;pYXrvRZPCfJsa z0m^9Wd&3tO?EA0EiT|oF{<|Up6Z?6J87g3g^A8Z#bqN*L!};C}^bWxKVuURMLgj=_ z1Ki~l*kPPYs50{+-@(Xg6;x{w7)u=Z}F2@~|l=!;}?a3JOZ9 zFeQh-rK*DJKjr^cvyniN-_4|v1{?|Gpdkpz42lE-0a2{d=TvW{yQ{>!rTSsB;34Ob zD#A-j0<+zvdn-Jndahyp+$dkSnL__2i|S&`^EJ}owEt73gtB_}ycu&i3&)=UX38&i zj5HU!Zj-nLWoNmu!AGxnEiBRwrZ}^Vufg?Mle6E6pmEARSN%4Nb3TImCLU7%@)F`; z-qOtqSy>%F_amkLSlj?7VJ?*Rcrf7YE2X^c72W-4qGM*bmh?L@9Cgmq?y`uS>}uA8 z&DT7UE8u-Hpdlz}Z0(0<>%0+(qlLxGmEJd8TULo{H1rSYgip}6)Hx`A%^z zT*#!j^NIrTXS`K0J6kuBZ7i?bz4Nw$MbQOK^AktWS6xY4_&GZfS=M1m!9<^ zCya2V*0d6}XLX?6`t=dS9>WLvlcmw!|0?g{r2A>w%UvB*G@di{tkUm=~ z#6P_nJ(+SQFnJ-nJiT*+QN`PA`C(LB8}XX>T}mci&QNr=D26904C9d7mMuH!lNGd{ zJK;E2-`%uKQ6cW-C}GtV--vzEz%#d|_p%z4#D2Ywa8NBmLuN15bU!Wr;gpbroUi&J zlc@JFY7Z^NhVzxYK}>p^hde~d9I6$4D4y`lsma+h+DX)FJeDRCe0*a*4fwyX>G1=r z7DnO=@g|&_$B=F{66zg^RN70xjAFhSo@C$D5q-~EaM3+nSv99@b(HnB0Ld%XtSw_H zGyKA^Nu48vQVN>b0*axU-<&IvD4LISE zPFNoja@A!c0D19a@6GJEy!;+t7iaO{--#xl&ky0}0K>d55-Oca#3fbux;odo6{y>k zq?h|gqxYPY4f&?l80EVPa&0%z zj&=BPpW|+E>#f3hX|cvFgFXGLmIT4HrtTKA66yt|44xsyzhoh7q>pTH3$S@?p)ZyL z_d*^lF#V{Hx%|qxI}AWftkp#MQqmF(0UNL9RdiSNLe<0tvQa!Fb<`J@ zC+m`2ExT6e=JGHM}J=Q8hY6xoY7?8TI;m8h4&CMMH1K-9+E@8n`5 z=y>l@Lt@zcq#D`gDXZ0>134LP-`2RG=>n|~Tb*5XlipYBS)M7#=-CSH^XbhUcP6hjZT$zBKxwpx zoJ0%C9T1(`<_6{W1fcuLq{M#_=>N$iei+{`CPDuXCMgKR1b>l;2Z=}f3R~j>{oT;Q z`sg6EHxBQE_6<^xDz!Vh~l$4d>a2l3qN`XeHU-Eyh zU&7Ph9sLO=LL$voB8Wf$G;G5^$JI-nK#Z;5O^dLx8}zh&^z}&?HY!!NNsiSBLC)iU z4msl&aksF7TTLrIa~C1Cih7cDy3G1bNRnl_}%xr@88H;K8>^i}vh97R(z zFf691ePm#~wOhcL$(!k5#$bR#YUlEA%I`P-U?ZIlzg%tMKD+JA`8O4QZ;y82U|L_A zoELO&jAkx(YEO-nb4+V_HrQ;aKC@d8GYodSrbwOZFbWedevPhswrtLGQ{?N*rin(+ zbUDXIDrI_w>+QEG^dSq%tv>h%17}n;%Q@1Kx$6}eERle)tkEOs_W{e1t@VHDnEGdv wEFzAHc&(-O`dvH6;mJRF{6sVzs8PZB{SN^zS470bpS=8~^|S literal 0 HcmV?d00001 diff --git a/examples/embedded/src/main/resources/etc/keystore.pkf b/examples/embedded/src/main/resources/etc/keystore.pkf new file mode 100644 index 00000000000..443818e87df --- /dev/null +++ b/examples/embedded/src/main/resources/etc/keystore.pkf @@ -0,0 +1,20 @@ +Bag Attributes + friendlyName: jetty + localKeyID: 54 69 6D 65 20 31 34 32 33 31 39 38 30 39 33 31 31 35 +Key Attributes: +-----BEGIN PRIVATE KEY----- +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIPh4Q0t4xklXTzX +N2VAb47r5n7idAupp4CTNEhhT6lS70iA+A8i4+0lSEHWAogvd9jl3H7SvScr30QM +4ieC0JCGSOwGc8f+yqKrO56PPd5OuqW380BJ0r74jJczU9CcsuavHD7e6mRLUnmj +xM20NSxrcicMiPUHY1mJZtN9swtxAgMBAAECgYADS9P6Jll0uXBZIu/pgfDH27GJ +HlPULstW9VbrMDNzgfUlFMQebLrRpIrnyleJ29Xc//HA4beEkR4lb0T/w88+pEkt +7fhYeqRLPIfpDOgzloynnsoPcd8f/PypbimQrNLmBiG1178nVcy4Yoh5lYVIJwtU +3VriqDlvAfTLrrx8AQJBAMLWuh27Hb8xs3LRg4UD7hcv8tJejstm08Y+czRz7cO0 +RENa3aDjGFSegc+IUfdez7BP8uDw+PwE+jybmTvaliECQQCtR/anCY1WS28/bKvy +lmIwoI15eraBdVFkN0Hfxh+9PfR3rMD5uyvukT5GgTtY/XxADyafSTaipDJiZHJI +EitRAkBjeCBYYVjUbVlBuvi8Bb+dktsSzzdzXDGtueAy3SR7jyJyiIcxRf775Fg9 +TUkbUwoQ5yAF+sACWcAvBPj796JBAkAEZEeHEkHnxv+pztpIyrDwZJFRW9/WRh/q +90+PGVlilXhltBYr/idt43Z9mPblGX+VrAyhitx8oMa6IauX0gYRAkEAgnyVeXrD +jDLUZRA3P8Gu27k1k6GjbTYiUz3HKCz2/6+MZ2MK2qqwafgqocji029Q6dHdPD7a +4QnRlvraUnyQLA== +-----END PRIVATE KEY----- diff --git a/examples/embedded/src/main/resources/exampleserver.xml b/examples/embedded/src/main/resources/exampleserver.xml index 8f843d7a504..53063929766 100644 --- a/examples/embedded/src/main/resources/exampleserver.xml +++ b/examples/embedded/src/main/resources/exampleserver.xml @@ -7,7 +7,9 @@ - 8080 + + + diff --git a/examples/embedded/src/main/resources/fileserver.xml b/examples/embedded/src/main/resources/fileserver.xml index 126f0987c8e..843dd3d6217 100644 --- a/examples/embedded/src/main/resources/fileserver.xml +++ b/examples/embedded/src/main/resources/fileserver.xml @@ -7,7 +7,9 @@ - 8080 + + +
@@ -22,7 +24,9 @@ index.html - . + + +
diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerTest.java new file mode 100644 index 00000000000..1f6cbb6bbbd --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerTest.java @@ -0,0 +1,97 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringBufferInputStream; +import java.net.HttpURLConnection; +import java.net.URI; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.IO; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ExampleServerTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ExampleServer.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws IOException + { + URI uri = server.getURI().resolve("/hello"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Hello")); + } + + @Test + public void testGetEcho() throws IOException + { + URI uri = server.getURI().resolve("/echo/a/greeting"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + + // Use a POST request + http.setRequestMethod("POST"); + http.setDoOutput(true); + + // The POST body content + String postBody = "Greetings from " + ExampleServerTest.class; + http.setRequestProperty("Content-Type", "text/plain; charset=utf-8"); + + try (StringBufferInputStream in = new StringBufferInputStream(postBody); + OutputStream out = http.getOutputStream()) + { + IO.copy(in, out); + } + + // Check the response status code + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString(postBody)); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerXmlTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerXmlTest.java new file mode 100644 index 00000000000..bd7b9231a04 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerXmlTest.java @@ -0,0 +1,64 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ExampleServerXmlTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ExampleServerXml.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws IOException + { + URI uri = server.getURI().resolve("/hello"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FastFileServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FastFileServerTest.java new file mode 100644 index 00000000000..9f8962f9441 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FastFileServerTest.java @@ -0,0 +1,85 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +@ExtendWith(WorkDirExtension.class) +public class FastFileServerTest +{ + private static final String TEXT_CONTENT = "I am an old man and I have known a great " + + "many troubles, but most of them never happened. - Mark Twain"; + public WorkDir workDir; + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + Path baseDir = workDir.getEmptyPathDir(); + + Path textFile = baseDir.resolve("simple.txt"); + try (BufferedWriter writer = Files.newBufferedWriter(textFile, UTF_8)) + { + writer.write(TEXT_CONTENT); + } + + server = FastFileServer.createServer(0, baseDir.toFile()); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetSimpleText() throws IOException + { + URI uri = server.getURI().resolve("/simple.txt"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + assertThat("Content-Type", http.getHeaderField("Content-Type"), is("text/plain")); + assertThat("Content-Length", http.getHeaderField("Content-Length"), is(Integer.toString(TEXT_CONTENT.length()))); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response body", responseBody, is(TEXT_CONTENT)); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerTest.java new file mode 100644 index 00000000000..6d9276a20aa --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerTest.java @@ -0,0 +1,86 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.resource.PathResource; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +@ExtendWith(WorkDirExtension.class) +public class FileServerTest +{ + private static final String TEXT_CONTENT = "I am an old man and I have known a great " + + "many troubles, but most of them never happened. - Mark Twain"; + public WorkDir workDir; + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + Path baseDir = workDir.getEmptyPathDir(); + + Path textFile = baseDir.resolve("simple.txt"); + try (BufferedWriter writer = Files.newBufferedWriter(textFile, UTF_8)) + { + writer.write(TEXT_CONTENT); + } + + server = FileServer.createServer(0, new PathResource(baseDir)); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetSimpleText() throws IOException + { + URI uri = server.getURI().resolve("/simple.txt"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + assertThat("Content-Type", http.getHeaderField("Content-Type"), is("text/plain")); + assertThat("Content-Length", http.getHeaderField("Content-Length"), is(Integer.toString(TEXT_CONTENT.length()))); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response body", responseBody, is(TEXT_CONTENT)); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerXmlTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerXmlTest.java new file mode 100644 index 00000000000..6e2e6093655 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerXmlTest.java @@ -0,0 +1,85 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +@ExtendWith(WorkDirExtension.class) +public class FileServerXmlTest +{ + private static final String TEXT_CONTENT = "I am an old man and I have known a great " + + "many troubles, but most of them never happened. - Mark Twain"; + public WorkDir workDir; + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + Path baseDir = workDir.getEmptyPathDir(); + + Path textFile = baseDir.resolve("simple.txt"); + try (BufferedWriter writer = Files.newBufferedWriter(textFile, UTF_8)) + { + writer.write(TEXT_CONTENT); + } + + server = FileServerXml.createServer(0, baseDir); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetSimpleText() throws IOException + { + URI uri = server.getURI().resolve("/simple.txt"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + assertThat("Content-Type", http.getHeaderField("Content-Type"), is("text/plain")); + assertThat("Content-Length", http.getHeaderField("Content-Length"), is(Integer.toString(TEXT_CONTENT.length()))); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response body", responseBody, is(TEXT_CONTENT)); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/HttpUtil.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/HttpUtil.java new file mode 100644 index 00000000000..174d4899472 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/HttpUtil.java @@ -0,0 +1,125 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.zip.GZIPInputStream; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.eclipse.jetty.util.IO; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; + +public class HttpUtil +{ + public static void assertGzippedResponse(HttpURLConnection http) + { + String value = http.getHeaderField("Content-Encoding"); + assertThat("Content-Encoding", value, containsString("gzip")); + } + + public static String getGzippedResponseBody(HttpURLConnection http) throws IOException + { + try (InputStream in = http.getInputStream(); + GZIPInputStream gzipInputStream = new GZIPInputStream(in)) + { + return IO.toString(gzipInputStream, UTF_8); + } + } + + public static String getResponseBody(HttpURLConnection http) throws IOException + { + try (InputStream in = http.getInputStream()) + { + return IO.toString(in, UTF_8); + } + } + + public static void dumpResponseHeaders(HttpURLConnection http) + { + int i = 0; + while (true) + { + String field = http.getHeaderField(i); + if (field == null) + return; + String key = http.getHeaderFieldKey(i); + if (key != null) + { + System.out.printf("%s: ", key); + } + System.out.println(field); + i++; + } + } + + /** + * Disable the Hostname and Certificate verification in {@link HttpsURLConnection} + */ + public static void disableSecureConnectionVerification() throws NoSuchAlgorithmException, KeyManagementException + { + TrustManager[] trustAllCerts = new TrustManager[]{new TrustAllCerts()}; + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + HttpsURLConnection.setDefaultHostnameVerifier(new AllHostnamesValid()); + } + + public static class TrustAllCerts implements X509TrustManager + { + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException + { + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException + { + } + + @Override + public X509Certificate[] getAcceptedIssuers() + { + return null; + } + } + + public static class AllHostnamesValid implements HostnameVerifier + { + + @Override + public boolean verify(String s, SSLSession sslSession) + { + return true; + } + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/JarServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/JarServerTest.java new file mode 100644 index 00000000000..cf24d55b4d8 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/JarServerTest.java @@ -0,0 +1,78 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class JarServerTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = JarServer.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetDir0Test0() throws IOException + { + URI uri = server.getURI().resolve("/dir0/test0.txt"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("test0")); + } + + @Test + public void testGetDir1Test1() throws IOException + { + URI uri = server.getURI().resolve("/dir1/test1.txt"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("test1")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java new file mode 100644 index 00000000000..07dc253df04 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java @@ -0,0 +1,76 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.util.Map; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class LikeJettyXmlTest +{ + private Server server; + private URI serverPlainUri; + private URI serverSslUri; + + @BeforeEach + public void startServer() throws Exception + { + server = LikeJettyXml.createServer(0, 0, false); + server.start(); + + System.err.println("Server URI is " + server.getURI()); + + Map ports = ServerUtil.fixDynamicPortConfigurations(server); + + // Establish base URI's that use "localhost" to prevent tripping over + // the "REMOTE ACCESS" warnings in demo-base + serverPlainUri = URI.create("http://localhost:" + ports.get("plain") + "/"); + serverSslUri = URI.create("https://localhost:" + ports.get("secure") + "/"); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetTest() throws IOException + { + URI uri = serverPlainUri.resolve("/test/"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyConnectorsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyConnectorsTest.java new file mode 100644 index 00000000000..7e4977c5953 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyConnectorsTest.java @@ -0,0 +1,76 @@ +package org.eclipse.jetty.embedded; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.util.Map; +import javax.net.ssl.HttpsURLConnection; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ManyConnectorsTest +{ + private Server server; + private URI serverPlainUri; + private URI serverSslUri; + + @BeforeEach + public void startServer() throws Exception + { + server = ManyConnectors.createServer(0, 0); + server.start(); + + System.err.println("Server URI is " + server.getURI()); + + Map ports = ServerUtil.fixDynamicPortConfigurations(server); + + // Establish base URI's that use "localhost" to prevent tripping over + // the "REMOTE ACCESS" warnings in demo-base + serverPlainUri = URI.create("http://localhost:" + ports.get("plain") + "/"); + serverSslUri = URI.create("https://localhost:" + ports.get("secure") + "/"); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testPlainGetHello() throws IOException + { + URI helloUri = serverPlainUri.resolve("/hello"); + + HttpURLConnection http = (HttpURLConnection)helloUri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Hello")); + } + + @Test + public void testSecureGetHello() throws Exception + { + HttpUtil.disableSecureConnectionVerification(); + URI helloUri = serverSslUri.resolve("/hello"); + + HttpsURLConnection https = (HttpsURLConnection)helloUri.toURL().openConnection(); + assertThat("HTTPS Response Status", https.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(https); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyContextsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyContextsTest.java new file mode 100644 index 00000000000..9a15aa89526 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyContextsTest.java @@ -0,0 +1,108 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ManyContextsTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ManyContexts.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetRootHello() throws IOException + { + URI uri = server.getURI().resolve("/"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Root Hello")); + } + + @Test + public void testGetFrenchHello() throws IOException + { + URI uri = server.getURI().resolve("/fr"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Bonjour")); + } + + @Test + public void testGetItalianGoodMorning() throws IOException + { + URI uri = server.getURI().resolve("/it"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Buongiorno")); + } + + @Test + public void testGetVirtualHostHello() throws IOException + { + int port = server.getURI().getPort(); + + URI uri = URI.create("http://127.0.0.2:" + port + "/"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Virtual Hello")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyHandlersTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyHandlersTest.java index 7a77a278688..c33dc34fb05 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyHandlersTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyHandlersTest.java @@ -19,20 +19,16 @@ package org.eclipse.jetty.embedded; import java.io.IOException; -import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URI; import java.util.Map; -import java.util.zip.GZIPInputStream; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.ajax.JSON; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; @@ -62,13 +58,13 @@ public class ManyHandlersTest http.setRequestProperty("Accept-Encoding", "gzip"); assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); - // dumpResponseHeaders(http); + // HttpUtil.dumpResponseHeaders(http); // test gzip - assertGzippedResponse(http); + HttpUtil.assertGzippedResponse(http); // test response content - String responseBody = getResponseBody(http); + String responseBody = HttpUtil.getGzippedResponseBody(http); Object jsonObj = JSON.parse(responseBody); Map jsonMap = (Map)jsonObj; assertThat("Response JSON keys.size", jsonMap.keySet().size(), is(2)); @@ -82,51 +78,17 @@ public class ManyHandlersTest http.setRequestProperty("Accept-Encoding", "gzip"); assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); - // dumpResponseHeaders(http); + // HttpUtil.dumpResponseHeaders(http); // test gzip - assertGzippedResponse(http); + HttpUtil.assertGzippedResponse(http); // test expected header from wrapper String welcome = http.getHeaderField("X-Welcome"); assertThat("X-Welcome header", welcome, containsString("Greetings from WelcomeWrapHandler")); // test response content - String responseBody = getResponseBody(http); + String responseBody = HttpUtil.getGzippedResponseBody(http); assertThat("Response Content", responseBody, containsString("Hello")); } - - private void assertGzippedResponse(HttpURLConnection http) - { - String value = http.getHeaderField("Content-Encoding"); - assertThat("Content-Encoding", value, containsString("gzip")); - } - - private String getResponseBody(HttpURLConnection http) throws IOException - { - try (InputStream in = http.getInputStream(); - GZIPInputStream gzipInputStream = new GZIPInputStream(in)) - { - return IO.toString(gzipInputStream, UTF_8); - } - } - - @SuppressWarnings("unused") - private void dumpResponseHeaders(HttpURLConnection http) - { - int i = 0; - while (true) - { - String field = http.getHeaderField(i); - if (field == null) - return; - String key = http.getHeaderFieldKey(i); - if (key != null) - { - System.out.printf("%s: ", key); - } - System.out.println(field); - i++; - } - } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerUtil.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerUtil.java new file mode 100644 index 00000000000..ad41662e45c --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerUtil.java @@ -0,0 +1,88 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; + +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class ServerUtil +{ + /** + * Fix the HttpConfiguration entries for securePort after the dynamic ports have been bound. + * + * @param server the server to correct. + */ + public static Map fixDynamicPortConfigurations(Server server) + { + // Fix ports in HttpConfiguration (since we are using dynamic port assignment for this testcase) + HttpConfiguration plainHttpConfiguration = null; + HttpConfiguration secureHttpConfiguration = null; + int plainHttpPort = -1; + int secureHttpPort = -1; + + for (Connector connector : server.getConnectors()) + { + if (connector instanceof ServerConnector) + { + ServerConnector serverConnector = (ServerConnector)connector; + SslConnectionFactory sslConnectionFactory = serverConnector.getConnectionFactory(SslConnectionFactory.class); + HttpConnectionFactory httpConnectionFactory = serverConnector.getConnectionFactory(HttpConnectionFactory.class); + if (httpConnectionFactory != null) + { + HttpConfiguration configuration = httpConnectionFactory.getHttpConfiguration(); + if (sslConnectionFactory != null) + { + secureHttpConfiguration = configuration; + secureHttpPort = serverConnector.getLocalPort(); + } + else + { + plainHttpConfiguration = configuration; + plainHttpPort = serverConnector.getLocalPort(); + } + } + } + } + + assertNotNull(plainHttpConfiguration, "Plain HTTP Configuration"); + assertNotEquals(plainHttpPort, -1, "Dynamic Plain HTTP Port"); + + assertNotNull(secureHttpConfiguration, "Secure HTTP Configuration"); + assertNotEquals(secureHttpPort, -1, "Dynamic Secure Port"); + + plainHttpConfiguration.setSecurePort(secureHttpPort); + secureHttpConfiguration.setSecurePort(secureHttpPort); + + Map ports = new HashMap<>(); + ports.put("plain", plainHttpPort); + ports.put("secure", secureHttpPort); + + return ports; + } +} From f0743f4d2ad37134d2108bb3ff914087c57b436a Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 4 Sep 2019 16:27:15 -0500 Subject: [PATCH 013/113] Fixing testcase license header Signed-off-by: Joakim Erdfelt --- .../jetty/embedded/ManyConnectorsTest.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyConnectorsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyConnectorsTest.java index 7e4977c5953..fa822c2f7ca 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyConnectorsTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyConnectorsTest.java @@ -1,3 +1,21 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; import java.io.IOException; From 20f7a473ee5e991330ff3f866df2fb7a3d2f6753 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 4 Sep 2019 16:32:52 -0500 Subject: [PATCH 014/113] Fixing License header date range --- .../test/java/org/eclipse/jetty/test/FailedSelectorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java index d22f595549d..9ab0b34d353 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java @@ -1,6 +1,6 @@ // // ======================================================================== -// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd. +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 From 829cccaef76e93361b48ba499047ac358786dd38 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 5 Sep 2019 12:09:50 -0500 Subject: [PATCH 015/113] Adding message if unable to find jetty-distribution Signed-off-by: Joakim Erdfelt --- .../java/org/eclipse/jetty/embedded/JettyDistribution.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java index 813e8b785d5..0ccc7ae3845 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java @@ -61,6 +61,12 @@ public class JettyDistribution LOG.warn(cause); } } + + if (distro == null) + { + throw new RuntimeException("Unable to find built jetty-distribution, run the build and try again."); + } + DISTRIBUTION = distro; } From d8efb62d139b1b724c71499aae69d3094c68723f Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 5 Sep 2019 12:12:49 -0500 Subject: [PATCH 016/113] Issue #3989 - Select Failure does not attempt to restart selector + Changes from PR review Signed-off-by: Joakim Erdfelt --- .../java/org/eclipse/jetty/io/ManagedSelector.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java index 60fc3615d0f..1bf6aa7a0c4 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java @@ -107,16 +107,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable protected void doStart() throws Exception { super.doStart(); - startSelector(); - // Set started only if we really are started - Start start = new Start(); - submit(start); - start._started.await(); - } - - protected void startSelector() throws IOException, InterruptedException - { _selector = _selectorManager.newSelector(); // The producer used by the strategies will never @@ -129,8 +120,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable protected void onSelectFailed(Throwable cause) throws Exception { - LOG.info("Restarting selector: " + toString(), cause); - startSelector(); + // override to change behavior } private void notifySelectFailed(Throwable cause) @@ -512,7 +502,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable Selector selector = _selector; if (isRunning()) { - LOG.warn(x); + LOG.warn("Fatal select() failure", x); notifySelectFailed(x); } else From 1d57d5f0891f64015c627c97238b03a9016ac253 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 5 Sep 2019 13:58:48 -0500 Subject: [PATCH 017/113] Updating versions table Signed-off-by: Joakim Erdfelt --- .../main/asciidoc/quick-start/introduction/what-version.adoc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/jetty-documentation/src/main/asciidoc/quick-start/introduction/what-version.adoc b/jetty-documentation/src/main/asciidoc/quick-start/introduction/what-version.adoc index b4164bb19c7..8e29d400900 100644 --- a/jetty-documentation/src/main/asciidoc/quick-start/introduction/what-version.adoc +++ b/jetty-documentation/src/main/asciidoc/quick-start/introduction/what-version.adoc @@ -33,9 +33,12 @@ _____ [width="100%",cols="12%,9%,15%,6%,21%,10%,6%,21%",options="header",] |======================================================================= |Version |Year |Home |JVM |Protocols |Servlet |JSP |Status -|9.4 |2016- |Eclipse |1.8 |HTTP/1.1 (RFC 7230), HTTP/2 (RFC 7540), WebSocket (RFC 6455, JSR 356), FastCGI |3.1 |2.3 |Stable +|10 |2019- |Eclipse |11- |HTTP/1.1 (RFC 7230), HTTP/2 (RFC 7540), WebSocket (RFC 6455, JSR 356), FastCGI |4.0.2 |2.3 |*UNSTABLE / Alpha* +|9.4 |2016- |Eclipse |1.8-12.0.1 |HTTP/1.1 (RFC 7230), HTTP/2 (RFC 7540), WebSocket (RFC 6455, JSR 356), FastCGI |3.1 |2.3 |Stable |9.3 |2015- |Eclipse |1.8 |HTTP/1.1 (RFC 7230), HTTP/2 (RFC 7540), WebSocket (RFC 6455, JSR 356), FastCGI |3.1 |2.3 |Stable |9.2 |2014-2018 |Eclipse |1.7 |HTTP/1.1 RFC2616, javax.websocket, SPDY v3 |3.1 |2.3 |Deprecated / *End of Life January 2018* +|9.1 |2013-2014 |Eclipse |1.7 |HTTP/1.1 RFC2616 |3.1 |2.3 |Deprecated / *End of Life May 2014* +|9.0 |2013-2013 |Eclipse |1.7 |HTTP/1.1 RFC2616 |3.1-beta |2.3 |Deprecated / *End of Life November 2013* |8 |2009-2014 |Eclipse/Codehaus |1.6 |HTTP/1.1 RFC2616, WebSocket RFC 6455, SPDY v3 |3.0 |2.2 |Deprecated / *End of Life November 2014* |7 |2008-2014 |Eclipse/Codehaus |1.5 |HTTP/1.1 RFC2616, WebSocket RFC 6455, SPDY v3 |2.5 |2.1 |Deprecated / *End of Life November 2014* |6 |2006-2010 |Codehaus |1.4-1.5 |HTTP/1.1 RFC2616 |2.5 |2.0 |Deprecated / *End of Life November 2010* From 3171ad5fe6d829c7673bd15938529d2f5f0d14fe Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 5 Sep 2019 20:23:59 -0500 Subject: [PATCH 018/113] Issue #3989 - Updating testcase to restart server on select failure Signed-off-by: Joakim Erdfelt --- .../jetty/test/FailedSelectorTest.java | 87 ++++++++++++++----- 1 file changed, 63 insertions(+), 24 deletions(-) diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java index 9ab0b34d353..d540660f59c 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.test; import java.io.IOException; +import java.net.URI; import java.nio.channels.Selector; import java.util.Collection; import java.util.concurrent.CountDownLatch; @@ -38,7 +39,9 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.HttpClientTransport; import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; @@ -66,6 +69,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class FailedSelectorTest { + private static final Logger LOG = Log.getLogger(FailedSelectorTest.class); private HttpClient client; private Server server; private AsyncCloseSelectorServlet asyncCloseSelectorServlet; @@ -79,9 +83,11 @@ public class FailedSelectorTest @BeforeEach public void startClient() throws Exception { - client = new HttpClient(); - client.setIdleTimeout(2000); + HttpClientTransport transport = new HttpClientTransportOverHTTP(1); + client = new HttpClient(transport, null); + client.setIdleTimeout(1000); client.setMaxConnectionsPerDestination(1); + client.setMaxRequestsQueuedPerDestination(1); client.start(); } @@ -94,6 +100,8 @@ public class FailedSelectorTest public void startServer(Function customizeServerConsumer) throws Exception { server = new Server(); + server.setStopTimeout(1000); + server.setStopAtShutdown(true); ServerConnector connector = customizeServerConsumer.apply(server); server.addConnector(connector); @@ -126,8 +134,9 @@ public class FailedSelectorTest startServer((server) -> { - CustomServerConnector connector = new CustomServerConnector(server, failedLatch, 1, 1); + CustomServerConnector connector = new CustomServerConnector(server, 1, 1, new RestartServerTask(server, failedLatch)); connector.setPort(0); + connector.setIdleTimeout(1000); return connector; }); @@ -139,6 +148,7 @@ public class FailedSelectorTest // Wait for selectors to close from action above assertTrue(failedLatch.await(2, TimeUnit.SECONDS)); + LOG.info("Got failedLatch"); // Request /hello assertRequestHello(); @@ -152,8 +162,9 @@ public class FailedSelectorTest startServer((server) -> { - CustomServerConnector connector = new CustomServerConnector(server, failedLatch, 1, 1); + CustomServerConnector connector = new CustomServerConnector(server, 1, 1, new RestartServerTask(server, failedLatch)); connector.setPort(0); + connector.setIdleTimeout(1000); return connector; }); @@ -165,6 +176,7 @@ public class FailedSelectorTest // Wait for selectors to close from action above assertTrue(failedLatch.await(2, TimeUnit.SECONDS)); + LOG.info("Got failedLatch"); // Ensure that Async Listener onError was called assertTrue(asyncCloseSelectorServlet.onErrorLatch.await(2, TimeUnit.SECONDS)); @@ -175,7 +187,10 @@ public class FailedSelectorTest private void assertRequestSelectorClose(String path) throws InterruptedException, ExecutionException, TimeoutException { - ContentResponse response = client.newRequest(server.getURI().resolve(path)) + URI dest = server.getURI().resolve(path); + LOG.info("Requesting GET on {}", dest); + + ContentResponse response = client.newRequest(dest) .method(HttpMethod.GET) .header(HttpHeader.CONNECTION, "close") .send(); @@ -186,7 +201,9 @@ public class FailedSelectorTest private void assertRequestHello() throws InterruptedException, ExecutionException, TimeoutException { - ContentResponse response = client.newRequest(server.getURI().resolve("/hello")) + URI dest = server.getURI().resolve("/hello"); + LOG.info("Requesting GET on {}", dest); + ContentResponse response = client.newRequest(dest) .method(HttpMethod.GET) .header(HttpHeader.CONNECTION, "close") .send(); @@ -195,14 +212,44 @@ public class FailedSelectorTest assertThat("/hello response", response.getContentAsString(), startsWith("Hello ")); } + public static class RestartServerTask implements Runnable + { + private final Server server; + private final CountDownLatch latch; + + public RestartServerTask(Server server, CountDownLatch latch) + { + this.server = server; + this.latch = latch; + } + + @Override + public void run() + { + try + { + server.stop(); + server.start(); + } + catch (Exception e) + { + e.printStackTrace(); + } + finally + { + latch.countDown(); + } + } + } + public static class CustomServerConnector extends ServerConnector { - private final CountDownLatch failedLatch; + private final Runnable onSelectFailureTask; - public CustomServerConnector(Server server, CountDownLatch failedLatch, int acceptors, int selectors) + public CustomServerConnector(Server server, int acceptors, int selectors, Runnable onSelectFailureTask) { super(server, acceptors, selectors); - this.failedLatch = failedLatch; + this.onSelectFailureTask = onSelectFailureTask; } @Override @@ -213,7 +260,7 @@ public class FailedSelectorTest @Override protected ManagedSelector newSelector(int id) { - return new CustomManagedSelector(this, id, failedLatch); + return new CustomManagedSelector(this, id, onSelectFailureTask); } }; } @@ -221,27 +268,18 @@ public class FailedSelectorTest public static class CustomManagedSelector extends ManagedSelector { - private static final Logger LOG = Log.getLogger(CustomManagedSelector.class); - private final CountDownLatch failedLatch; + private final Runnable onSelectFailureTask; - public CustomManagedSelector(SelectorManager selectorManager, int id, CountDownLatch failedLatch) + public CustomManagedSelector(SelectorManager selectorManager, int id, Runnable onSelectFailureTask) { super(selectorManager, id); - this.failedLatch = failedLatch; + this.onSelectFailureTask = onSelectFailureTask; } @Override - protected void onSelectFailed(Throwable cause) throws Exception + protected void onSelectFailed(Throwable cause) { - try - { - LOG.debug("onSelectFailed()", cause); - this.startSelector(); - } - finally - { - failedLatch.countDown(); - } + new Thread(onSelectFailureTask, "onSelectFailedTask").start(); } } @@ -301,6 +339,7 @@ public class FailedSelectorTest { resp.setContentType("text/plain"); resp.setCharacterEncoding("utf-8"); + resp.setHeader("Connection", "close"); resp.getWriter().printf("Closing selectors in %,d ms%n", DELAY_MS); scheduledExecutorService.schedule(new InterruptSelector(connector), DELAY_MS, TimeUnit.MILLISECONDS); } From 2770afb280712b5ec673f662635b594e865f8d93 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Fri, 6 Sep 2019 16:03:22 +1000 Subject: [PATCH 019/113] fix claim validation Signed-off-by: Lachlan Roberts --- .../security/openid/OpenIdAuthenticator.java | 12 +++--- .../security/openid/OpenIdConfiguration.java | 14 ++++++- .../security/openid/OpenIdCredentials.java | 41 +++++++++++-------- .../security/openid/OpenIdLoginService.java | 7 ++-- .../security/openid/OpenIdUserIdentity.java | 5 --- .../openid/OpenIdAuthenticationDemo.java | 8 ++-- 6 files changed, 51 insertions(+), 36 deletions(-) diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java index 1608d7a5acb..14caa20af5b 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java @@ -61,8 +61,8 @@ public class OpenIdAuthenticator extends LoginAuthenticator { private static final Logger LOG = Log.getLogger(OpenIdAuthenticator.class); - public static final String __USER_CLAIMS = "org.eclipse.jetty.security.openid.user_claims"; - public static final String __RESPONSE_JSON = "org.eclipse.jetty.security.openid.response"; + public static final String __CLAIMS = "org.eclipse.jetty.security.openid.claims"; + public static final String __RESPONSE = "org.eclipse.jetty.security.openid.response"; public static final String __ERROR_PAGE = "org.eclipse.jetty.security.openid.error_page"; public static final String __J_URI = "org.eclipse.jetty.security.openid.URI"; public static final String __J_POST = "org.eclipse.jetty.security.openid.POST"; @@ -162,8 +162,8 @@ public class OpenIdAuthenticator extends LoginAuthenticator HttpSession session = ((HttpServletRequest)request).getSession(); Authentication cached = new SessionAuthentication(getAuthMethod(), user, credentials); session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached); - session.setAttribute(__USER_CLAIMS, ((OpenIdCredentials)credentials).getClaims()); - session.setAttribute(__RESPONSE_JSON, ((OpenIdCredentials)credentials).getResponse()); + session.setAttribute(__CLAIMS, ((OpenIdCredentials)credentials).getClaims()); + session.setAttribute(__RESPONSE, ((OpenIdCredentials)credentials).getResponse()); } return user; } @@ -180,8 +180,8 @@ public class OpenIdAuthenticator extends LoginAuthenticator //clean up session session.removeAttribute(SessionAuthentication.__J_AUTHENTICATED); - session.removeAttribute(__USER_CLAIMS); - session.removeAttribute(__RESPONSE_JSON); + session.removeAttribute(__CLAIMS); + session.removeAttribute(__RESPONSE); } @Override diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java index fe75553da1e..8284bb286ee 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java @@ -26,12 +26,16 @@ import java.util.Map; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.ajax.JSON; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; public class OpenIdConfiguration { + private static final Logger LOG = Log.getLogger(OpenIdConfiguration.class); private static String CONFIG_PATH = "/.well-known/openid-configuration"; private final String openIdProvider; + private final String issuer; private final String authEndpoint; private final String tokenEndpoint; private final String clientId; @@ -55,13 +59,16 @@ public class OpenIdConfiguration InputStream inputStream = providerUri.toURL().openConnection().getInputStream(); String content = IO.toString(inputStream); discoveryDocument = (Map)JSON.parse(content); + if (LOG.isDebugEnabled()) + LOG.debug("discovery document {}", discoveryDocument); } catch (Throwable e) { throw new IllegalArgumentException("invalid identity provider", e); } - if (discoveryDocument.get("issuer") == null) + issuer = (String)discoveryDocument.get("issuer"); + if (issuer == null) throw new IllegalArgumentException(); authEndpoint = (String)discoveryDocument.get("authorization_endpoint"); @@ -93,6 +100,11 @@ public class OpenIdConfiguration return clientSecret; } + public String getIssuer() + { + return issuer; + } + public String getOpenIdProvider() { return openIdProvider; diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java index 3d116d8c65f..7b787482ea3 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java @@ -91,7 +91,8 @@ public class OpenIdCredentials claims = decodeJWT(idToken); if (LOG.isDebugEnabled()) - LOG.debug("userClaims {}", claims); + LOG.debug("claims {}", claims); + validateClaims(); } finally { @@ -101,27 +102,35 @@ public class OpenIdCredentials } } - public boolean validate() + private void validateClaims() { - if (authCode != null) - return false; + // Issuer Identifier for the OpenID Provider MUST exactly match the value of the iss (issuer) Claim. + if (!configuration.getIssuer().equals(claims.get("iss"))) + { + LOG.warn("Invalid \"iss\" Claim: was {}, expected {}", claims.get("iss"), configuration.getIssuer()); + throw new IllegalArgumentException("invalid iss claim"); + } // The aud (audience) Claim MUST contain the client_id value. - String audience = (String)claims.get("aud"); - if (!configuration.getClientId().equals(audience)) + if (!configuration.getClientId().equals(claims.get("aud"))) { - LOG.warn("Audience claim MUST contain the value of the Issuer Identifier for the OP"); - return false; + LOG.warn("Audience Claim MUST contain the client_id value"); + throw new IllegalArgumentException("invalid aud claim"); } - // TODO: this does not work for microsoft - // Issuer Identifier for the OpenID Provider MUST exactly match the value of the iss (issuer) Claim. - String issuer = (String)claims.get("iss"); - if (!configuration.getOpenIdProvider().equals(issuer)) + // If an azp (authorized party) Claim is present, verify that its client_id is the Claim Value. + Object azp = claims.get("azp"); + if (azp != null && !configuration.getClientId().equals(azp)) { - //LOG.warn(""); - //return false; + LOG.warn("Authorized party claim value should be the client_id"); + throw new IllegalArgumentException("invalid azp claim"); } + } + + public boolean isExpired() + { + if (authCode != null || claims == null) + return true; // Check expiry long expiry = (Long)claims.get("exp"); @@ -130,10 +139,10 @@ public class OpenIdCredentials { if (LOG.isDebugEnabled()) LOG.debug("OpenId Credentials expired {}", this); - return false; + return true; } - return true; + return false; } protected Map decodeJWT(String jwt) throws IOException diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java index 4bfc75ac236..0b80fe8d735 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java @@ -18,7 +18,6 @@ package org.eclipse.jetty.security.openid; -import java.io.IOException; import java.security.Principal; import javax.security.auth.Subject; import javax.servlet.ServletRequest; @@ -72,10 +71,10 @@ public class OpenIdLoginService extends ContainerLifeCycle implements LoginServi try { openIdCredentials.redeemAuthCode(); - if (!openIdCredentials.validate()) + if (openIdCredentials.isExpired()) return null; } - catch (IOException e) + catch (Throwable e) { LOG.warn(e); return null; @@ -115,7 +114,7 @@ public class OpenIdLoginService extends ContainerLifeCycle implements LoginServi return false; OpenIdCredentials credentials = ((OpenIdUserPrincipal)userPrincipal).getCredentials(); - return credentials.validate(); + return !credentials.isExpired(); } @Override diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserIdentity.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserIdentity.java index 1477484c5e1..3b943c7a6e6 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserIdentity.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserIdentity.java @@ -29,11 +29,6 @@ public class OpenIdUserIdentity implements UserIdentity private final Principal userPrincipal; private final UserIdentity userIdentity; - public OpenIdUserIdentity(Subject subject, Principal userPrincipal) - { - this(subject, userPrincipal, null); - } - public OpenIdUserIdentity(Subject subject, Principal userPrincipal, UserIdentity userIdentity) { this.subject = subject; diff --git a/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java b/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java index d3e5168dc25..2d6ec2a0dde 100644 --- a/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java +++ b/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java @@ -81,7 +81,7 @@ public class OpenIdAuthenticationDemo Principal userPrincipal = request.getUserPrincipal(); if (userPrincipal != null) { - Map userInfo = (Map)request.getSession().getAttribute(OpenIdAuthenticator.__USER_CLAIMS); + Map userInfo = (Map)request.getSession().getAttribute(OpenIdAuthenticator.__CLAIMS); response.getWriter().println("

Welcome: " + userInfo.get("name") + "

"); response.getWriter().println("Profile
"); response.getWriter().println("Admin
"); @@ -100,7 +100,7 @@ public class OpenIdAuthenticationDemo protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType(MimeTypes.Type.TEXT_HTML.asString()); - Map userInfo = (Map)request.getSession().getAttribute(OpenIdAuthenticator.__USER_CLAIMS); + Map userInfo = (Map)request.getSession().getAttribute(OpenIdAuthenticator.__CLAIMS); response.getWriter().println("\n" + "
\n" + @@ -180,7 +180,7 @@ public class OpenIdAuthenticationDemo /* // Microsoft Authentication OpenIdConfiguration configuration = new OpenIdConfiguration( - "https://login.microsoftonline.com/common/v2.0", + "https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0", "5f05dea8-2bd9-45de-b30f-cf5c102b8784", "IfhQJKi-5[vxhh_=ldqt0y4PkV3z_1ca"); */ @@ -211,7 +211,7 @@ public class OpenIdAuthenticationDemo hashLoginService.setHotReload(true); // Configure OpenIdLoginService optionally providing a base LoginService to provide user roles - OpenIdLoginService loginService = new OpenIdLoginService(configuration, hashLoginService); + OpenIdLoginService loginService = new OpenIdLoginService(configuration);//, hashLoginService); securityHandler.setLoginService(loginService); Authenticator authenticator = new OpenIdAuthenticator(configuration, "/error"); From 0fd989f0cd345ecb2161c02c1c079489787481cf Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 6 Sep 2019 08:24:51 -0500 Subject: [PATCH 020/113] Issue #3989 - Restoring _selector to null on failure Signed-off-by: Joakim Erdfelt --- jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java | 1 + 1 file changed, 1 insertion(+) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java index 1bf6aa7a0c4..c9c659c4530 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java @@ -510,6 +510,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable LOG.warn(x.toString()); LOG.debug(x); } + _selector = null; IO.close(selector); } return false; From cf2074114d4596fc521efd3705a9bae825b97bbb Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 6 Sep 2019 08:58:04 -0500 Subject: [PATCH 021/113] Jetty version change to Min JVM with footnotes Signed-off-by: Joakim Erdfelt --- .../introduction/what-version.adoc | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/jetty-documentation/src/main/asciidoc/quick-start/introduction/what-version.adoc b/jetty-documentation/src/main/asciidoc/quick-start/introduction/what-version.adoc index 8e29d400900..a9a35434445 100644 --- a/jetty-documentation/src/main/asciidoc/quick-start/introduction/what-version.adoc +++ b/jetty-documentation/src/main/asciidoc/quick-start/introduction/what-version.adoc @@ -32,14 +32,14 @@ _____ .Jetty Versions [width="100%",cols="12%,9%,15%,6%,21%,10%,6%,21%",options="header",] |======================================================================= -|Version |Year |Home |JVM |Protocols |Servlet |JSP |Status -|10 |2019- |Eclipse |11- |HTTP/1.1 (RFC 7230), HTTP/2 (RFC 7540), WebSocket (RFC 6455, JSR 356), FastCGI |4.0.2 |2.3 |*UNSTABLE / Alpha* -|9.4 |2016- |Eclipse |1.8-12.0.1 |HTTP/1.1 (RFC 7230), HTTP/2 (RFC 7540), WebSocket (RFC 6455, JSR 356), FastCGI |3.1 |2.3 |Stable -|9.3 |2015- |Eclipse |1.8 |HTTP/1.1 (RFC 7230), HTTP/2 (RFC 7540), WebSocket (RFC 6455, JSR 356), FastCGI |3.1 |2.3 |Stable -|9.2 |2014-2018 |Eclipse |1.7 |HTTP/1.1 RFC2616, javax.websocket, SPDY v3 |3.1 |2.3 |Deprecated / *End of Life January 2018* -|9.1 |2013-2014 |Eclipse |1.7 |HTTP/1.1 RFC2616 |3.1 |2.3 |Deprecated / *End of Life May 2014* -|9.0 |2013-2013 |Eclipse |1.7 |HTTP/1.1 RFC2616 |3.1-beta |2.3 |Deprecated / *End of Life November 2013* -|8 |2009-2014 |Eclipse/Codehaus |1.6 |HTTP/1.1 RFC2616, WebSocket RFC 6455, SPDY v3 |3.0 |2.2 |Deprecated / *End of Life November 2014* +|Version |Year |Home |Min JVM |Protocols |Servlet |JSP |Status +|10 |2019- |Eclipse |11 ^(1)^ |HTTP/1.1 (RFC 7230), HTTP/2 (RFC 7540), WebSocket (RFC 6455, JSR 356), FastCGI |4.0.2 |2.3 |*UNSTABLE / Alpha* +|9.4 |2016- |Eclipse |1.8 |HTTP/1.1 (RFC 7230), HTTP/2 (RFC 7540), WebSocket (RFC 6455, JSR 356), FastCGI |3.1 |2.3 |Stable +|9.3 |2015- |Eclipse |1.8 ^(2)^ |HTTP/1.1 (RFC 7230), HTTP/2 (RFC 7540), WebSocket (RFC 6455, JSR 356), FastCGI |3.1 |2.3 |Stable +|9.2 |2014-2018 |Eclipse |1.7 ^(2)^ |HTTP/1.1 RFC2616, javax.websocket, SPDY v3 |3.1 |2.3 |Deprecated / *End of Life January 2018* +|9.1 |2013-2014 |Eclipse |1.7 ^(2)^ |HTTP/1.1 RFC2616 |3.1 |2.3 |Deprecated / *End of Life May 2014* +|9.0 |2013-2013 |Eclipse |1.7 ^(2)^ |HTTP/1.1 RFC2616 |3.1-beta |2.3 |Deprecated / *End of Life November 2013* +|8 |2009-2014 |Eclipse/Codehaus |1.6 ^(2)^ |HTTP/1.1 RFC2616, WebSocket RFC 6455, SPDY v3 |3.0 |2.2 |Deprecated / *End of Life November 2014* |7 |2008-2014 |Eclipse/Codehaus |1.5 |HTTP/1.1 RFC2616, WebSocket RFC 6455, SPDY v3 |2.5 |2.1 |Deprecated / *End of Life November 2014* |6 |2006-2010 |Codehaus |1.4-1.5 |HTTP/1.1 RFC2616 |2.5 |2.0 |Deprecated / *End of Life November 2010* |5 |2003-2009 |Sourceforge |1.2-1.5 |HTTP/1.1 RFC2616 |2.4 |2.0 |Antique @@ -48,3 +48,6 @@ _____ |2 |1998-2000 |Mortbay |1.1 |HTTP/1.0 RFC1945 |2.1 |1.0 |Legendary |1 |1995-1998 |Mortbay |1.0 |HTTP/1.0 RFC1945 |- |- |Mythical |======================================================================= + + 1. JPMS module support is optional + 2. JDK9 and newer is not supported if using MultiRelease JAR Files, or Bytecode / Annotation scanning. \ No newline at end of file From 7618eae91534178a375e021a65c141da19833c9d Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 6 Sep 2019 09:26:06 -0500 Subject: [PATCH 022/113] Issue #4064 - MinimalServlets test and ServletHolder fix + Also made ContextHandler warning message about features that are unimplemented (and you should use ServletContextHandler) more clear. (this helped with diagnosing where the bug was in ServletHolder) Signed-off-by: Joakim Erdfelt --- .../jetty/embedded/MinimalServlets.java | 25 +++++--- .../jetty/embedded/MinimalServletsTest.java | 64 +++++++++++++++++++ .../jetty/server/handler/ContextHandler.java | 46 ++++++------- .../eclipse/jetty/servlet/ServletHolder.java | 7 +- 4 files changed, 106 insertions(+), 36 deletions(-) create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/MinimalServletsTest.java diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/MinimalServlets.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/MinimalServlets.java index 5ce9a24016c..dab7237a89b 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/MinimalServlets.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/MinimalServlets.java @@ -19,7 +19,6 @@ package org.eclipse.jetty.embedded; import java.io.IOException; -import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -29,13 +28,13 @@ import org.eclipse.jetty.servlet.ServletHandler; public class MinimalServlets { - public static void main(String[] args) throws Exception + + public static Server createServer(int port) { - // Create a basic jetty server object that will listen on port 8080. // Note that if you set this to port 0 then a randomly available port // will be assigned that you can either look in the logs for the port, // or programmatically obtain it for use in test cases. - Server server = new Server(8080); + Server server = new Server(port); // The ServletHandler is a dead simple way to create a context handler // that is backed by an instance of a Servlet. @@ -51,13 +50,19 @@ public class MinimalServlets // through a web.xml @WebServlet annotation, or anything similar. handler.addServletWithMapping(HelloServlet.class, "/*"); + return server; + } + + public static void main(String[] args) throws Exception + { + // Create a basic jetty server object that will listen on port 8080. + Server server = createServer(8080); + // Start things up! server.start(); // The use of server.join() the will make the current thread join and - // wait until the server is done executing. - // See - // http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join() + // wait until the server thread is done executing. server.join(); } @@ -66,11 +71,11 @@ public class MinimalServlets { @Override protected void doGet(HttpServletRequest request, - HttpServletResponse response) throws ServletException, - IOException + HttpServletResponse response) throws IOException { - response.setContentType("text/html"); response.setStatus(HttpServletResponse.SC_OK); + response.setContentType("text/html"); + response.setCharacterEncoding("utf-8"); response.getWriter().println("

Hello from HelloServlet

"); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/MinimalServletsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/MinimalServletsTest.java new file mode 100644 index 00000000000..c1abf3da16d --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/MinimalServletsTest.java @@ -0,0 +1,64 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class MinimalServletsTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = MinimalServlets.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws IOException + { + URI uri = server.getURI().resolve("/hello"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java index 4297a04a031..6d08d5c7d11 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java @@ -126,7 +126,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu public static final int DEFAULT_LISTENER_TYPE_INDEX = 1; public static final int EXTENDED_LISTENER_TYPE_INDEX = 0; - private static final String __unimplmented = "Unimplemented - use org.eclipse.jetty.servlet.ServletContextHandler"; + private static final String UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER = "Unimplemented {} - use org.eclipse.jetty.servlet.ServletContextHandler"; private static final Logger LOG = Log.getLogger(ContextHandler.class); @@ -2479,7 +2479,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu @Override public JspConfigDescriptor getJspConfigDescriptor() { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getJspConfigDescriptor()"); return null; } @@ -2673,130 +2673,130 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu @Override public Dynamic addFilter(String filterName, Class filterClass) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addFilter(String, Class)"); return null; } @Override public Dynamic addFilter(String filterName, Filter filter) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addFilter(String, Filter)"); return null; } @Override public Dynamic addFilter(String filterName, String className) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addFilter(String, String)"); return null; } @Override public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Class servletClass) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addServlet(String, Class)"); return null; } @Override public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addServlet(String, Servlet)"); return null; } @Override public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, String className) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addServlet(String, String)"); return null; } @Override public T createFilter(Class c) throws ServletException { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "createFilter(Class)"); return null; } @Override public T createServlet(Class c) throws ServletException { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "createServlet(Class)"); return null; } @Override public Set getDefaultSessionTrackingModes() { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getDefaultSessionTrackingModes()"); return null; } @Override public Set getEffectiveSessionTrackingModes() { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getEffectiveSessionTrackingModes()"); return null; } @Override public FilterRegistration getFilterRegistration(String filterName) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getFilterRegistration(String)"); return null; } @Override public Map getFilterRegistrations() { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getFilterRegistrations()"); return null; } @Override public ServletRegistration getServletRegistration(String servletName) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getServletRegistration(String)"); return null; } @Override public Map getServletRegistrations() { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getServletRegistrations()"); return null; } @Override public SessionCookieConfig getSessionCookieConfig() { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getSessionCookieConfig()"); return null; } @Override public void setSessionTrackingModes(Set sessionTrackingModes) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "setSessionTrackingModes(Set)"); } @Override public void addListener(String className) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addListener(String)"); } @Override public void addListener(T t) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addListener(T)"); } @Override public void addListener(Class listenerClass) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addListener(Class)"); } @Override @@ -2843,14 +2843,14 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu @Override public JspConfigDescriptor getJspConfigDescriptor() { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getJspConfigDescriptor()"); return null; } @Override public void declareRoles(String... roleNames) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "declareRoles(String...)"); } @Override diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java index 9fb2b7e4ff6..599dd3be680 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java @@ -1265,9 +1265,10 @@ public class ServletHolder extends Holder implements UserIdentity.Scope try { ServletContext ctx = getServletHandler().getServletContext(); - if (ctx == null) - return getHeldClass().getDeclaredConstructor().newInstance(); - return ctx.createServlet(getHeldClass()); + if (ctx instanceof ServletContextHandler.Context) + return ctx.createServlet(getHeldClass()); + return getHeldClass().getDeclaredConstructor().newInstance(); + } catch (ServletException ex) { From fdaa67e7ec74e01cb6804a629745c6dc62b85617 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 6 Sep 2019 14:02:41 -0500 Subject: [PATCH 023/113] More testing of embedded examples Signed-off-by: Joakim Erdfelt --- .../eclipse/jetty/embedded/DumpServlet.java | 20 ++- .../jetty/embedded/ManyServletContexts.java | 12 +- .../eclipse/jetty/embedded/OneConnector.java | 10 +- .../eclipse/jetty/embedded/OneContext.java | 10 +- .../eclipse/jetty/embedded/OneHandler.java | 11 +- .../jetty/embedded/OneServletContext.java | 63 +++++--- .../embedded/ManyServletContextsTest.java | 106 +++++++++++++ .../jetty/embedded/OneConnectorTest.java | 64 ++++++++ .../jetty/embedded/OneContextTest.java | 64 ++++++++ .../jetty/embedded/OneHandlerTest.java | 64 ++++++++ .../jetty/embedded/OneServletContextTest.java | 149 ++++++++++++++++++ 11 files changed, 543 insertions(+), 30 deletions(-) create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyServletContextsTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneConnectorTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneContextTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneHandlerTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextTest.java diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/DumpServlet.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/DumpServlet.java index 38372558bd8..0f21dccf722 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/DumpServlet.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/DumpServlet.java @@ -20,6 +20,8 @@ package org.eclipse.jetty.embedded; import java.io.IOException; import java.io.PrintWriter; +import java.util.Collections; +import javax.servlet.ServletContext; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -45,12 +47,28 @@ public class DumpServlet extends HttpServlet out.println("pathInfo=" + request.getPathInfo()); out.println("session=" + request.getSession(true).getId()); + ServletContext servletContext = getServletContext(); + String r = request.getParameter("resource"); if (r != null) { - out.println("resource(" + r + ")=" + getServletContext().getResource(r)); + out.println("resource(" + r + ")=" + servletContext.getResource(r)); } + Collections.list(request.getAttributeNames()) + .stream() + .filter((name) -> name.startsWith("X-")) + .sorted() + .forEach((name) -> + out.println("request.attribute[" + name + "]=" + request.getAttribute(name))); + + Collections.list(servletContext.getAttributeNames()) + .stream() + .filter((name) -> name.startsWith("X-")) + .sorted() + .forEach((name) -> + out.println("servletContext.attribute[" + name + "]=" + servletContext.getAttribute(name))); + out.println(""); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyServletContexts.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyServletContexts.java index 53fa7317c04..5dd7e8fa327 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyServletContexts.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyServletContexts.java @@ -29,9 +29,9 @@ import org.eclipse.jetty.servlet.ServletHolder; public class ManyServletContexts { - public static void main(String[] args) throws Exception + public static Server createServer(int port) { - Server server = new Server(8080); + Server server = new Server(port); // Setup JMX MBeanContainer mbContainer = new MBeanContainer( @@ -48,7 +48,7 @@ public class ManyServletContexts // Add servlets to root context root.addServlet(new ServletHolder(new HelloServlet("Hello")), "/"); root.addServlet(new ServletHolder(new HelloServlet("Ciao")), "/it/*"); - root.addServlet(new ServletHolder(new HelloServlet("Bonjoir")), "/fr/*"); + root.addServlet(new ServletHolder(new HelloServlet("Bonjour")), "/fr/*"); // Configure context "/other" for servlets ServletContextHandler other = new ServletContextHandler(contexts, @@ -57,6 +57,12 @@ public class ManyServletContexts other.addServlet(DefaultServlet.class.getCanonicalName(), "/"); other.addServlet(new ServletHolder(new HelloServlet("YO!")), "*.yo"); + return server; + } + + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); server.start(); server.dumpStdErr(); server.join(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneConnector.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneConnector.java index c6debe8a23b..ff897e5cff8 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneConnector.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneConnector.java @@ -26,7 +26,7 @@ import org.eclipse.jetty.server.ServerConnector; */ public class OneConnector { - public static void main(String[] args) throws Exception + public static Server createServer(int port) throws Exception { // The Server Server server = new Server(); @@ -34,7 +34,7 @@ public class OneConnector // HTTP connector ServerConnector http = new ServerConnector(server); http.setHost("localhost"); - http.setPort(8080); + http.setPort(port); http.setIdleTimeout(30000); // Set the connector @@ -42,6 +42,12 @@ public class OneConnector // Set a handler server.setHandler(new HelloHandler()); + return server; + } + + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); // Start the server server.start(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneContext.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneContext.java index 1331e6abb55..59bf20d9e86 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneContext.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneContext.java @@ -23,9 +23,9 @@ import org.eclipse.jetty.server.handler.ContextHandler; public class OneContext { - public static void main(String[] args) throws Exception + public static Server createServer(int port) { - Server server = new Server(8080); + Server server = new Server(port); // Add a single handler on context "/hello" ContextHandler context = new ContextHandler(); @@ -35,6 +35,12 @@ public class OneContext // Can be accessed using http://localhost:8080/hello server.setHandler(context); + return server; + } + + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); // Start the server server.start(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneHandler.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneHandler.java index b9a2f942f75..00aab92ac78 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneHandler.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneHandler.java @@ -22,11 +22,16 @@ import org.eclipse.jetty.server.Server; public class OneHandler { + public static Server createServer(int port) + { + Server server = new Server(port); + server.setHandler(new HelloHandler()); + return server; + } + public static void main(String[] args) throws Exception { - Server server = new Server(8080); - server.setHandler(new HelloHandler()); - + Server server = createServer(8080); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContext.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContext.java index 720309dd917..34f1e250e3a 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContext.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContext.java @@ -19,8 +19,9 @@ package org.eclipse.jetty.embedded; import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.EnumSet; -import javax.servlet.DispatcherType; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -31,38 +32,58 @@ import javax.servlet.ServletRequest; import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ListenerHolder; import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.resource.PathResource; +import org.eclipse.jetty.util.resource.Resource; + +import static javax.servlet.DispatcherType.ASYNC; +import static javax.servlet.DispatcherType.REQUEST; public class OneServletContext { - public static void main(String[] args) throws Exception + public static Server createServer(int port, Resource baseResource) { - Server server = new Server(8080); + Server server = new Server(port); - ServletContextHandler context = new ServletContextHandler( - ServletContextHandler.SESSIONS); + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); - context.setResourceBase(System.getProperty("java.io.tmpdir")); + context.setBaseResource(baseResource); server.setHandler(context); - // Add dump servlet - context.addServlet( - context.addServlet(DumpServlet.class, "/dump/*"), - "*.dump"); + // add hello servlet context.addServlet(HelloServlet.class, "/hello/*"); + + // Add dump servlet on multiple url-patterns + ServletHolder debugHolder = new ServletHolder("debug", DumpServlet.class); + context.addServlet(debugHolder, "/dump/*"); + context.addServlet(debugHolder, "*.dump"); + + // add default servlet (for error handling and static resources) context.addServlet(DefaultServlet.class, "/"); - context.addFilter(TestFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); - context.addFilter(TestFilter.class, "/test", EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC)); - context.addFilter(TestFilter.class, "*.test", EnumSet.of(DispatcherType.REQUEST, DispatcherType.INCLUDE, DispatcherType.FORWARD)); + // sprinkle in a few filters to demonstrate behaviors + context.addFilter(TestFilter.class, "/test/*", EnumSet.of(REQUEST)); + context.addFilter(TestFilter.class, "*.test", EnumSet.of(REQUEST, ASYNC)); + // and a few listeners to show other ways of working with servlets context.getServletHandler().addListener(new ListenerHolder(InitListener.class)); context.getServletHandler().addListener(new ListenerHolder(RequestListener.class)); + return server; + } + + public static void main(String[] args) throws Exception + { + Path tempDir = Paths.get(System.getProperty("java.io.tmpdir")); + + Server server = createServer(8080, new PathResource(tempDir)); + server.start(); server.dumpStdErr(); server.join(); @@ -71,14 +92,18 @@ public class OneServletContext public static class TestFilter implements Filter { @Override - public void init(FilterConfig filterConfig) throws ServletException + public void init(FilterConfig filterConfig) { - } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + if (response instanceof HttpServletResponse) + { + HttpServletResponse httpServletResponse = (HttpServletResponse)response; + httpServletResponse.setHeader("X-TestFilter", "true"); + } chain.doFilter(request, response); } @@ -94,6 +119,7 @@ public class OneServletContext @Override public void contextInitialized(ServletContextEvent sce) { + sce.getServletContext().setAttribute("X-Init", "true"); } @Override @@ -105,15 +131,14 @@ public class OneServletContext public static class RequestListener implements ServletRequestListener { @Override - public void requestDestroyed(ServletRequestEvent sre) + public void requestInitialized(ServletRequestEvent sre) { - + sre.getServletRequest().setAttribute("X-ReqListener", "true"); } @Override - public void requestInitialized(ServletRequestEvent sre) + public void requestDestroyed(ServletRequestEvent sre) { - } } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyServletContextsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyServletContextsTest.java new file mode 100644 index 00000000000..0883388ff84 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyServletContextsTest.java @@ -0,0 +1,106 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ManyServletContextsTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ManyServletContexts.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws IOException + { + URI uri = server.getURI().resolve("/hello"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Hello")); + } + + @Test + public void testGetItalianHello() throws IOException + { + URI uri = server.getURI().resolve("/it/hello"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Ciao")); + } + + @Test + public void testGetFrenchHello() throws IOException + { + URI uri = server.getURI().resolve("/fr/hello"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Bonjour")); + } + + @Test + public void testGetOtherYo() throws IOException + { + URI uri = server.getURI().resolve("/other/hello.yo"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("YO!")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneConnectorTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneConnectorTest.java new file mode 100644 index 00000000000..014a3838446 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneConnectorTest.java @@ -0,0 +1,64 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class OneConnectorTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = OneConnector.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws IOException + { + URI uri = server.getURI().resolve("/hello"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneContextTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneContextTest.java new file mode 100644 index 00000000000..0654b7b7350 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneContextTest.java @@ -0,0 +1,64 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class OneContextTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = OneContext.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws IOException + { + URI uri = server.getURI().resolve("/hello"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneHandlerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneHandlerTest.java new file mode 100644 index 00000000000..cd65fb494d0 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneHandlerTest.java @@ -0,0 +1,64 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class OneHandlerTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = OneHandler.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws IOException + { + URI uri = server.getURI().resolve("/hello"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextTest.java new file mode 100644 index 00000000000..dbe97c555e0 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextTest.java @@ -0,0 +1,149 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.resource.PathResource; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +@ExtendWith(WorkDirExtension.class) +public class OneServletContextTest +{ + private static final String TEXT_CONTENT = "The secret of getting ahead is getting started. - Mark Twain"; + public WorkDir workDir; + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + Path baseDir = workDir.getEmptyPathDir(); + + Path textFile = baseDir.resolve("simple.txt"); + try (BufferedWriter writer = Files.newBufferedWriter(textFile, UTF_8)) + { + writer.write(TEXT_CONTENT); + } + + server = OneServletContext.createServer(0, new PathResource(baseDir)); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws IOException + { + URI uri = server.getURI().resolve("/hello/there"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Hello")); + } + + @Test + public void testGetDumpViaPathInfo() throws IOException + { + URI uri = server.getURI().resolve("/dump/something"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, + allOf( + containsString("DumpServlet"), + containsString("servletPath=/dump"), + containsString("pathInfo=/something") + ) + ); + } + + @Test + public void testGetDumpSuffix() throws IOException + { + URI uri = server.getURI().resolve("/another.dump"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, + allOf( + containsString("DumpServlet"), + containsString("servletPath=/another.dump"), + containsString("pathInfo=null") + ) + ); + } + + @Test + public void testGetTestDumpSuffix() throws IOException + { + URI uri = server.getURI().resolve("/test/another.dump"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + String filterResponeHeader = http.getHeaderField("X-TestFilter"); + assertThat("X-TestFilter header", filterResponeHeader, is("true")); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, + allOf( + containsString("DumpServlet"), + containsString("servletPath=/test/another.dump"), + containsString("pathInfo=null"), + containsString("request.attribute[X-ReqListener]=true"), + containsString("servletContext.attribute[X-Init]=true") + ) + ); + } +} From f8041b23bdf3652c5893fa3a32d2e2cd9ed497ad Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 6 Sep 2019 14:54:01 -0500 Subject: [PATCH 024/113] Issue #3989 - Cleaning up ManagedSelector.doStop() for dead selector Signed-off-by: Joakim Erdfelt --- .../org/eclipse/jetty/io/ManagedSelector.java | 13 +- .../jetty/test/FailedSelectorTest.java | 138 ++---------------- 2 files changed, 25 insertions(+), 126 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java index c9c659c4530..5628ffc90b8 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java @@ -116,6 +116,11 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable // The normal strategy obtains the produced task, schedules // a new thread to produce more, runs the task and then exits. _selectorManager.execute(_strategy::produce); + + // Set started only if we really are started + Start start = new Start(); + submit(start); + start._started.await(); } protected void onSelectFailed(Throwable cause) throws Exception @@ -151,7 +156,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable { // doStop might be called for a failed managedSelector, // We do not want to wait twice, so we only stop once for each start - if (_started.compareAndSet(true, false)) + if (_started.compareAndSet(true, false) && _selector != null) { // Close connections, but only wait a single selector cycle for it to take effect CloseConnections closeConnections = new CloseConnections(); @@ -499,7 +504,9 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable } catch (Throwable x) { - Selector selector = _selector; + IO.close(_selector); + _selector = null; + if (isRunning()) { LOG.warn("Fatal select() failure", x); @@ -510,8 +517,6 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable LOG.warn(x.toString()); LOG.debug(x); } - _selector = null; - IO.close(selector); } return false; } diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java index d540660f59c..371eb208896 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java @@ -30,10 +30,6 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Function; -import javax.servlet.AsyncContext; -import javax.servlet.AsyncEvent; -import javax.servlet.AsyncListener; -import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -56,10 +52,10 @@ import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.Scheduler; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -72,29 +68,30 @@ public class FailedSelectorTest private static final Logger LOG = Log.getLogger(FailedSelectorTest.class); private HttpClient client; private Server server; - private AsyncCloseSelectorServlet asyncCloseSelectorServlet; @AfterEach - public void stopServer() throws Exception + public void stopServerAndClient() throws Exception { + LOG.info("Deathing Server"); server.stop(); + LOG.info("Deathing Client"); + client.stop(); } @BeforeEach public void startClient() throws Exception { HttpClientTransport transport = new HttpClientTransportOverHTTP(1); + QueuedThreadPool qtp = new QueuedThreadPool(); + qtp.setName("Client"); + qtp.setStopTimeout(1000); client = new HttpClient(transport, null); - client.setIdleTimeout(1000); - client.setMaxConnectionsPerDestination(1); - client.setMaxRequestsQueuedPerDestination(1); - client.start(); - } + client.setExecutor(qtp); - @AfterEach - public void stopClient() throws Exception - { - client.stop(); + client.setIdleTimeout(1000); +// client.setMaxConnectionsPerDestination(1); +// client.setMaxRequestsQueuedPerDestination(1); + client.start(); } public void startServer(Function customizeServerConsumer) throws Exception @@ -113,11 +110,6 @@ public class FailedSelectorTest ServletHolder closeHolder = new ServletHolder(new CloseSelectorServlet(connector)); context.addServlet(closeHolder, "/selector/close"); - asyncCloseSelectorServlet = new AsyncCloseSelectorServlet(connector); - ServletHolder asyncCloseHolder = new ServletHolder(asyncCloseSelectorServlet); - asyncCloseHolder.setAsyncSupported(true); - context.addServlet(asyncCloseHolder, "/selector/async-close"); - HandlerList handlers = new HandlerList(); handlers.addHandler(context); handlers.addHandler(new DefaultHandler()); @@ -128,7 +120,7 @@ public class FailedSelectorTest } @Test - public void testRebuildServerSelectorNormal() throws Exception + public void testRestartServerOnSelectFailure() throws Exception { CountDownLatch failedLatch = new CountDownLatch(1); @@ -152,37 +144,8 @@ public class FailedSelectorTest // Request /hello assertRequestHello(); - } - @Test - @Disabled - public void testRebuildServerSelectorAsync() throws Exception - { - CountDownLatch failedLatch = new CountDownLatch(1); - - startServer((server) -> - { - CustomServerConnector connector = new CustomServerConnector(server, 1, 1, new RestartServerTask(server, failedLatch)); - connector.setPort(0); - connector.setIdleTimeout(1000); - return connector; - }); - - // Request /hello - assertRequestHello(); - - // Request /selector/async-close - assertRequestSelectorClose("/selector/async-close"); - - // Wait for selectors to close from action above - assertTrue(failedLatch.await(2, TimeUnit.SECONDS)); - LOG.info("Got failedLatch"); - - // Ensure that Async Listener onError was called - assertTrue(asyncCloseSelectorServlet.onErrorLatch.await(2, TimeUnit.SECONDS)); - - // Request /hello - assertRequestHello(); + LOG.info("Test done"); } private void assertRequestSelectorClose(String path) throws InterruptedException, ExecutionException, TimeoutException @@ -233,7 +196,7 @@ public class FailedSelectorTest } catch (Exception e) { - e.printStackTrace(); + LOG.warn(e); } finally { @@ -344,73 +307,4 @@ public class FailedSelectorTest scheduledExecutorService.schedule(new InterruptSelector(connector), DELAY_MS, TimeUnit.MILLISECONDS); } } - - public static class AsyncCloseSelectorServlet extends HttpServlet - { - private static final int DELAY_MS = 200; - private ServerConnector connector; - private ScheduledExecutorService scheduledExecutorService; - public CountDownLatch onErrorLatch = new CountDownLatch(1); - - public AsyncCloseSelectorServlet(ServerConnector connector) - { - this.connector = connector; - scheduledExecutorService = Executors.newScheduledThreadPool(5); - } - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException - { - resp.setContentType("text/plain"); - resp.setCharacterEncoding("utf-8"); - ServletOutputStream out = resp.getOutputStream(); - out.print("Closing selectors " + DELAY_MS); - - AsyncContext asyncContext = req.startAsync(); - asyncContext.setTimeout(0); - asyncContext.addListener(new AsyncListener() - { - @Override - public void onComplete(AsyncEvent event) - { - } - - @Override - public void onTimeout(AsyncEvent event) - { - } - - @Override - public void onError(AsyncEvent event) - { - resp.setStatus(500); - event.getAsyncContext().complete(); - onErrorLatch.countDown(); - } - - @Override - public void onStartAsync(AsyncEvent event) - { - } - }); - - scheduledExecutorService.schedule(new InterruptSelector(connector), DELAY_MS, TimeUnit.MILLISECONDS); - /* trigger EofException after selector close - scheduledExecutorService.schedule(() -> - { - byte[] b = new byte[128 * 1024 * 1024]; - Arrays.fill(b, (byte)'x'); - try - { - out.write(b); - out.flush(); - } - catch (IOException e) - { - e.printStackTrace(System.out); - } - }, DELAY_MS * 2, TimeUnit.MILLISECONDS); - */ - } - } } \ No newline at end of file From b7b744160f7de81e7a0d9f63851406daaa4c0da3 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 6 Sep 2019 16:10:30 -0500 Subject: [PATCH 025/113] Issue #3989 - Tests for both Restart Server and Selector Signed-off-by: Joakim Erdfelt --- .../org/eclipse/jetty/io/ManagedSelector.java | 14 +- .../jetty/test/FailedSelectorTest.java | 294 +++++++++++------- 2 files changed, 199 insertions(+), 109 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java index 5628ffc90b8..35e9da322af 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java @@ -267,6 +267,16 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable } } + protected void endPointOpened(EndPoint endPoint) + { + _selectorManager.endPointOpened(endPoint); + } + + protected void endPointClosed(EndPoint endPoint) + { + _selectorManager.endPointClosed(endPoint); + } + private void createEndPoint(SelectableChannel channel, SelectionKey selectionKey) throws IOException { EndPoint endPoint = _selectorManager.newEndPoint(channel, this, selectionKey); @@ -274,7 +284,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable endPoint.setConnection(connection); selectionKey.attach(endPoint); endPoint.onOpen(); - _selectorManager.endPointOpened(endPoint); + endPointOpened(endPoint); _selectorManager.connectionOpened(connection); if (LOG.isDebugEnabled()) LOG.debug("Created {}", endPoint); @@ -967,7 +977,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable Connection connection = endPoint.getConnection(); if (connection != null) _selectorManager.connectionClosed(connection); - _selectorManager.endPointClosed(endPoint); + ManagedSelector.this.endPointClosed(endPoint); } @Override diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java index 371eb208896..259e2c72550 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java @@ -22,6 +22,8 @@ import java.io.IOException; import java.net.URI; import java.nio.channels.Selector; import java.util.Collection; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -29,6 +31,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; import java.util.function.Function; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -41,6 +44,7 @@ import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.ManagedSelector; import org.eclipse.jetty.io.SelectorManager; import org.eclipse.jetty.server.Server; @@ -72,9 +76,7 @@ public class FailedSelectorTest @AfterEach public void stopServerAndClient() throws Exception { - LOG.info("Deathing Server"); server.stop(); - LOG.info("Deathing Client"); client.stop(); } @@ -89,8 +91,8 @@ public class FailedSelectorTest client.setExecutor(qtp); client.setIdleTimeout(1000); -// client.setMaxConnectionsPerDestination(1); -// client.setMaxRequestsQueuedPerDestination(1); + client.setMaxConnectionsPerDestination(1); + client.setMaxRequestsQueuedPerDestination(1); client.start(); } @@ -126,7 +128,7 @@ public class FailedSelectorTest startServer((server) -> { - CustomServerConnector connector = new CustomServerConnector(server, 1, 1, new RestartServerTask(server, failedLatch)); + RestartSelectorCustomConnector connector = new RestartSelectorCustomConnector(server, 1, 1, new RestartServerTask(server, failedLatch)); connector.setPort(0); connector.setIdleTimeout(1000); return connector; @@ -140,12 +142,35 @@ public class FailedSelectorTest // Wait for selectors to close from action above assertTrue(failedLatch.await(2, TimeUnit.SECONDS)); - LOG.info("Got failedLatch"); + + // Request /hello + assertRequestHello(); + } + + @Test + public void testRestartSelectorOnSelectFailure() throws Exception + { + CountDownLatch failedLatch = new CountDownLatch(1); + + startServer((server) -> + { + RestartSelectorCustomConnector connector = new RestartSelectorCustomConnector(server, 1, 1, new RestartSelectorTask(failedLatch)); + connector.setPort(0); + connector.setIdleTimeout(1000); + return connector; + }); // Request /hello assertRequestHello(); - LOG.info("Test done"); + // Request /selector/close + assertRequestSelectorClose("/selector/close"); + + // Wait for selectors to close from action above + assertTrue(failedLatch.await(2, TimeUnit.SECONDS)); + + // Request /hello + assertRequestHello(); } private void assertRequestSelectorClose(String path) throws InterruptedException, ExecutionException, TimeoutException @@ -175,77 +200,6 @@ public class FailedSelectorTest assertThat("/hello response", response.getContentAsString(), startsWith("Hello ")); } - public static class RestartServerTask implements Runnable - { - private final Server server; - private final CountDownLatch latch; - - public RestartServerTask(Server server, CountDownLatch latch) - { - this.server = server; - this.latch = latch; - } - - @Override - public void run() - { - try - { - server.stop(); - server.start(); - } - catch (Exception e) - { - LOG.warn(e); - } - finally - { - latch.countDown(); - } - } - } - - public static class CustomServerConnector extends ServerConnector - { - private final Runnable onSelectFailureTask; - - public CustomServerConnector(Server server, int acceptors, int selectors, Runnable onSelectFailureTask) - { - super(server, acceptors, selectors); - this.onSelectFailureTask = onSelectFailureTask; - } - - @Override - protected SelectorManager newSelectorManager(Executor executor, Scheduler scheduler, int selectors) - { - return new ServerConnectorManager(executor, scheduler, selectors) - { - @Override - protected ManagedSelector newSelector(int id) - { - return new CustomManagedSelector(this, id, onSelectFailureTask); - } - }; - } - } - - public static class CustomManagedSelector extends ManagedSelector - { - private final Runnable onSelectFailureTask; - - public CustomManagedSelector(SelectorManager selectorManager, int id, Runnable onSelectFailureTask) - { - super(selectorManager, id); - this.onSelectFailureTask = onSelectFailureTask; - } - - @Override - protected void onSelectFailed(Throwable cause) - { - new Thread(onSelectFailureTask, "onSelectFailedTask").start(); - } - } - public static class HelloServlet extends HttpServlet { @Override @@ -257,34 +211,6 @@ public class FailedSelectorTest } } - private static class InterruptSelector implements Runnable - { - private static final Logger LOG = Log.getLogger(InterruptSelector.class); - private final ServerConnector connector; - - public InterruptSelector(ServerConnector connector) - { - this.connector = connector; - } - - @Override - public void run() - { - SelectorManager selectorManager = connector.getSelectorManager(); - Collection managedSelectors = selectorManager.getBeans(ManagedSelector.class); - for (ManagedSelector managedSelector : managedSelectors) - { - if (managedSelector instanceof CustomManagedSelector) - { - CustomManagedSelector customManagedSelector = (CustomManagedSelector)managedSelector; - Selector selector = customManagedSelector.getSelector(); - LOG.debug("Closing selector {}}", selector); - IO.close(selector); - } - } - } - } - public static class CloseSelectorServlet extends HttpServlet { private static final int DELAY_MS = 500; @@ -304,7 +230,161 @@ public class FailedSelectorTest resp.setCharacterEncoding("utf-8"); resp.setHeader("Connection", "close"); resp.getWriter().printf("Closing selectors in %,d ms%n", DELAY_MS); - scheduledExecutorService.schedule(new InterruptSelector(connector), DELAY_MS, TimeUnit.MILLISECONDS); + scheduledExecutorService.schedule(new InterruptSelectorTask(connector), DELAY_MS, TimeUnit.MILLISECONDS); + } + } + + public static class RestartSelectorCustomConnector extends ServerConnector + { + private final Consumer onSelectFailConsumer; + + public RestartSelectorCustomConnector(Server server, int acceptors, int selectors, Consumer onSelectFailConsumer) + { + super(server, acceptors, selectors); + this.onSelectFailConsumer = onSelectFailConsumer; + } + + @Override + protected SelectorManager newSelectorManager(Executor executor, Scheduler scheduler, int selectors) + { + return new ServerConnectorManager(executor, scheduler, selectors) + { + @Override + protected ManagedSelector newSelector(int id) + { + return new CustomManagedSelector(this, id, onSelectFailConsumer); + } + }; + } + } + + public static class CustomManagedSelector extends ManagedSelector + { + private final Set endpoints = ConcurrentHashMap.newKeySet(); + private final Consumer onSelectFailConsumer; + + public CustomManagedSelector(SelectorManager selectorManager, int id, Consumer onSelectFailConsumer) + { + super(selectorManager, id); + this.onSelectFailConsumer = onSelectFailConsumer; + } + + @Override + protected void endPointOpened(EndPoint endPoint) + { + super.endPointOpened(endPoint); + endpoints.add(endPoint); + } + + @Override + protected void endPointClosed(EndPoint endPoint) + { + super.endPointClosed(endPoint); + endpoints.remove(endPoint); + } + + @Override + protected void onSelectFailed(Throwable cause) + { + endpoints.forEach((endpoint) -> + { + if (endpoint.getConnection() != null) + { + IO.close(endpoint.getConnection()); + } + IO.close(endpoint); + }); + endpoints.clear(); + + new Thread(() -> onSelectFailConsumer.accept(this), "OnSelectFailedTask").start(); + } + } + + private static class RestartSelectorTask implements Consumer + { + private static final Logger LOG = Log.getLogger(RestartSelectorTask.class); + private final CountDownLatch latch; + + public RestartSelectorTask(CountDownLatch latch) + { + this.latch = latch; + } + + @Override + public void accept(CustomManagedSelector customManagedSelector) + { + try + { + customManagedSelector.stop(); + customManagedSelector.start(); + } + catch (Exception e) + { + LOG.warn(e); + } + finally + { + latch.countDown(); + } + } + } + + private static class RestartServerTask implements Consumer + { + private static final Logger LOG = Log.getLogger(RestartServerTask.class); + private final Server server; + private final CountDownLatch latch; + + public RestartServerTask(Server server, CountDownLatch latch) + { + this.server = server; + this.latch = latch; + } + + @Override + public void accept(CustomManagedSelector customManagedSelector) + { + try + { + server.stop(); + server.start(); + } + catch (Exception e) + { + LOG.warn(e); + } + finally + { + latch.countDown(); + } + } + } + + private static class InterruptSelectorTask implements Runnable + { + private static final Logger LOG = Log.getLogger(InterruptSelectorTask.class); + private final ServerConnector connector; + + public InterruptSelectorTask(ServerConnector connector) + { + this.connector = connector; + } + + @Override + public void run() + { + SelectorManager selectorManager = connector.getSelectorManager(); + Collection managedSelectors = selectorManager.getBeans(ManagedSelector.class); + for (ManagedSelector managedSelector : managedSelectors) + { + if (managedSelector instanceof CustomManagedSelector) + { + CustomManagedSelector customManagedSelector = (CustomManagedSelector)managedSelector; + Selector selector = customManagedSelector.getSelector(); + LOG.debug("Closing selector {}}", selector); + IO.close(selector); + } + } } } } \ No newline at end of file From 597458df7c4d744d8eb95a733535d52c7c9d114e Mon Sep 17 00:00:00 2001 From: dreis2211 Date: Sun, 8 Sep 2019 01:44:05 +0200 Subject: [PATCH 026/113] Tolerate multiple Accept-Encoding headers in GzipHandler Signed-off-by: dreis2211 --- .../server/handler/gzip/GzipHandler.java | 11 ++++---- .../jetty/servlet/GzipHandlerTest.java | 28 +++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java index 8351b402e5b..5cd8db254e0 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java @@ -35,6 +35,7 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.CompressedContentFormat; import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.http.HttpMethod; @@ -442,7 +443,8 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory @Override public Deflater getDeflater(Request request, long contentLength) { - String ua = request.getHttpFields().get(HttpHeader.USER_AGENT); + HttpFields httpFields = request.getHttpFields(); + String ua = httpFields.get(HttpHeader.USER_AGENT); if (ua != null && !isAgentGzipable(ua)) { LOG.debug("{} excluded user agent {}", this, request); @@ -456,16 +458,13 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory } // check the accept encoding header - HttpField accept = request.getHttpFields().getField(HttpHeader.ACCEPT_ENCODING); - - if (accept == null) + if (!httpFields.contains(HttpHeader.ACCEPT_ENCODING)) { LOG.debug("{} excluded !accept {}", this, request); return null; } - boolean gzip = accept.contains("gzip"); - if (!gzip) + if (!httpFields.contains(HttpHeader.ACCEPT_ENCODING, "gzip")) { LOG.debug("{} excluded not gzip accept {}", this, request); return null; diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java index 1362ac6f4d4..a40da2f7161 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/GzipHandlerTest.java @@ -285,6 +285,34 @@ public class GzipHandlerTest assertEquals(__content, testOut.toString("UTF8")); } + @Test + public void testGzipHandlerWithMultipleAcceptEncodingHeaders() throws Exception + { + // generated and parsed test + HttpTester.Request request = HttpTester.newRequest(); + HttpTester.Response response; + + request.setMethod("GET"); + request.setURI("/ctx/content?vary=Accept-Encoding,Other"); + request.setVersion("HTTP/1.0"); + request.setHeader("Host", "tester"); + request.setHeader("accept-encoding", "deflate"); + request.setHeader("accept-encoding", "gzip"); + + response = HttpTester.parseResponse(_connector.getResponse(request.generate())); + + assertThat(response.getStatus(), is(200)); + assertThat(response.get("Content-Encoding"), Matchers.equalToIgnoringCase("gzip")); + assertThat(response.get("ETag"), is(__contentETagGzip)); + assertThat(response.getCSV("Vary", false), Matchers.contains("Accept-Encoding", "Other")); + + InputStream testIn = new GZIPInputStream(new ByteArrayInputStream(response.getContentBytes())); + ByteArrayOutputStream testOut = new ByteArrayOutputStream(); + IO.copy(testIn, testOut); + + assertEquals(__content, testOut.toString("UTF8")); + } + @Test public void testGzipNotMicro() throws Exception { From c904a14d6a4e883e5d3e3b66f36afdb334715050 Mon Sep 17 00:00:00 2001 From: tomoya yokota Date: Mon, 9 Sep 2019 13:52:48 +0900 Subject: [PATCH 027/113] fix Session#finishInvalidate throwsConcurrentModificationException (#4036) * fix Session#finishInvalidate throws java.util.ConcurrentModificationException * Change constructor _attribute field must assigned to ConcurrentHashMap Signed-off-by: tomoya-yokota --- .../eclipse/jetty/server/session/SessionData.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java index 2f5a9d8cefd..cf6617f78f9 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java @@ -161,11 +161,6 @@ public class SessionData implements Serializable } public SessionData(String id, String cpath, String vhost, long created, long accessed, long lastAccessed, long maxInactiveMs) - { - this(id, cpath, vhost, created, accessed, lastAccessed, maxInactiveMs, new ConcurrentHashMap()); - } - - public SessionData(String id, String cpath, String vhost, long created, long accessed, long lastAccessed, long maxInactiveMs, Map attributes) { _id = id; setContextPath(cpath); @@ -175,7 +170,13 @@ public class SessionData implements Serializable _lastAccessed = lastAccessed; _maxInactiveMs = maxInactiveMs; calcAndSetExpiry(); - _attributes = attributes; + _attributes = new ConcurrentHashMap<>(); + } + + public SessionData(String id, String cpath, String vhost, long created, long accessed, long lastAccessed, long maxInactiveMs, Map attributes) + { + this(id, cpath, vhost, created, accessed, lastAccessed, maxInactiveMs); + putAllAttributes(attributes); } /** From be69598a48f453fb85bae8da26faa7f76dba7d3e Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Mon, 9 Sep 2019 14:57:35 +1000 Subject: [PATCH 028/113] add javadoc Signed-off-by: Lachlan Roberts --- .../security/openid/OpenIdConfiguration.java | 12 ++++++++++ .../security/openid/OpenIdCredentials.java | 10 ++++++++ .../security/openid/OpenIdLoginService.java | 23 +++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java index 8284bb286ee..d68c862b731 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java @@ -29,6 +29,12 @@ import org.eclipse.jetty.util.ajax.JSON; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +/** + * Holds the configuration for an OpenID Connect service. + * + * This uses the OpenID Provider URL with the path {@link #CONFIG_PATH} to discover + * the required information about the OIDC service. + */ public class OpenIdConfiguration { private static final Logger LOG = Log.getLogger(OpenIdConfiguration.class); @@ -44,6 +50,12 @@ public class OpenIdConfiguration private List scopes = new ArrayList<>(); + /** + * Create an OpenID configuration for a specific OIDC provider. + * @param provider The URL of the OpenID provider. + * @param clientId OAuth 2.0 Client Identifier valid at the Authorization Server. + * @param clientSecret The client secret known only by the Client and the Authorization Server. + */ public OpenIdConfiguration(String provider, String clientId, String clientSecret) { this.openIdProvider = provider; diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java index 7b787482ea3..5dbc406ce11 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java @@ -32,6 +32,16 @@ import org.eclipse.jetty.util.ajax.JSON; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +/** + *

The credentials of an user to be authenticated with OpenID Connect. This will contain + * the OpenID ID Token and the OAuth 2.0 Access Token.

+ * + *

+ * This is constructed with an authorization code from the authentication request. This authorization code + * is then exchanged using {@link #redeemAuthCode()} for a response containing the ID Token and Access Token. + * The response is then validated against the {@link OpenIdConfiguration}. + *

+ */ public class OpenIdCredentials { private static final Logger LOG = Log.getLogger(OpenIdCredentials.class); diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java index 0b80fe8d735..c2df0b02115 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java @@ -29,6 +29,13 @@ import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +/** + * The implementation of {@link LoginService} required to use OpenID Connect. + * + *

+ * Can contain an optional wrapped {@link LoginService} which is used to store role information about users. + *

+ */ public class OpenIdLoginService extends ContainerLifeCycle implements LoginService { private static final Logger LOG = Log.getLogger(OpenIdLoginService.class); @@ -43,6 +50,13 @@ public class OpenIdLoginService extends ContainerLifeCycle implements LoginServi this(configuration, null); } + /** + * Use a wrapped {@link LoginService} to store information about user roles. + * Users in the wrapped loginService must be stored with their username as + * the value of the sub (subject) Claim, and a credentials value of the empty string. + * @param configuration the OpenID configuration to use. + * @param loginService the wrapped LoginService to defer to for user roles. + */ public OpenIdLoginService(OpenIdConfiguration configuration, LoginService loginService) { _configuration = configuration; @@ -101,6 +115,15 @@ public class OpenIdLoginService extends ContainerLifeCycle implements LoginServi return identityService.newUserIdentity(subject, userPrincipal, new String[0]); } + /** + * This setting is only meaningful if a wrapped {@link LoginService} has been set. + *

+ * If set to true, any users not found by the wrapped {@link LoginService} will still + * be authenticated but with no roles, if set to false users will not be + * authenticated unless they are discovered by the wrapped {@link LoginService}. + *

+ * @param authenticateNewUsers + */ public void authenticateNewUsers(boolean authenticateNewUsers) { this.authenticateNewUsers = authenticateNewUsers; From 82e8fc0b0ecded5070d02b3e3661ae0b711d2a80 Mon Sep 17 00:00:00 2001 From: dreis2211 Date: Mon, 9 Sep 2019 07:06:40 +0200 Subject: [PATCH 029/113] Simplify check for Accept-Encoding header Signed-off-by: dreis2211 --- .../org/eclipse/jetty/server/handler/gzip/GzipHandler.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java index 5cd8db254e0..45e54e1ccaf 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java @@ -458,12 +458,6 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory } // check the accept encoding header - if (!httpFields.contains(HttpHeader.ACCEPT_ENCODING)) - { - LOG.debug("{} excluded !accept {}", this, request); - return null; - } - if (!httpFields.contains(HttpHeader.ACCEPT_ENCODING, "gzip")) { LOG.debug("{} excluded not gzip accept {}", this, request); From dc26739502a2d7bf29fbe4eef9ee89dde77e8b92 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Mon, 9 Sep 2019 16:58:36 +1000 Subject: [PATCH 030/113] changes from review Signed-off-by: Lachlan Roberts --- .../src/main/config/modules/openid.mod | 4 +- .../security/openid/OpenIdAuthenticator.java | 76 +++++++++---------- .../security/openid/OpenIdCredentials.java | 32 ++++---- .../security/openid/OpenIdUserPrincipal.java | 4 +- .../openid/OpenIdAuthenticationDemo.java | 4 +- .../test/resources/jetty-logging.properties | 1 - .../jetty/util/security/Constraint.java | 1 - 7 files changed, 57 insertions(+), 65 deletions(-) diff --git a/jetty-openid/src/main/config/modules/openid.mod b/jetty-openid/src/main/config/modules/openid.mod index 04365d8e736..c9a4cc7476e 100644 --- a/jetty-openid/src/main/config/modules/openid.mod +++ b/jetty-openid/src/main/config/modules/openid.mod @@ -22,10 +22,10 @@ etc/jetty-openid.xml # jetty.openid.openIdProvider=https://accounts.google.com/ ## The Client Identifier -# jetty.openid.clientId=1051168419525-5nl60mkugb77p9j194mrh287p1e0ahfi.apps.googleusercontent.com +# jetty.openid.clientId=test1234.apps.googleusercontent.com ## The Client Secret -# jetty.openid.clientSecret=XT_MIsSv_aUCGollauCaJY8S +# jetty.openid.clientSecret=XT_Mafv_aUCGheuCaKY8P ## Additional Scopes to Request # jetty.openid.scopes=email,profile diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java index 14caa20af5b..5e2018a37ec 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.security.openid; import java.io.IOException; import java.math.BigInteger; +import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; @@ -43,6 +44,7 @@ import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.UserIdentity; import org.eclipse.jetty.util.MultiMap; import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.UrlEncoded; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.security.Constraint; @@ -61,14 +63,14 @@ public class OpenIdAuthenticator extends LoginAuthenticator { private static final Logger LOG = Log.getLogger(OpenIdAuthenticator.class); - public static final String __CLAIMS = "org.eclipse.jetty.security.openid.claims"; - public static final String __RESPONSE = "org.eclipse.jetty.security.openid.response"; - public static final String __ERROR_PAGE = "org.eclipse.jetty.security.openid.error_page"; - public static final String __J_URI = "org.eclipse.jetty.security.openid.URI"; - public static final String __J_POST = "org.eclipse.jetty.security.openid.POST"; - public static final String __J_METHOD = "org.eclipse.jetty.security.openid.METHOD"; - public static final String __CSRF_TOKEN = "org.eclipse.jetty.security.openid.csrf_token"; - public static final String __J_SECURITY_CHECK = "/j_security_check"; + public static final String CLAIMS = "org.eclipse.jetty.security.openid.claims"; + public static final String RESPONSE = "org.eclipse.jetty.security.openid.response"; + public static final String ERROR_PAGE = "org.eclipse.jetty.security.openid.error_page"; + public static final String J_URI = "org.eclipse.jetty.security.openid.URI"; + public static final String J_POST = "org.eclipse.jetty.security.openid.POST"; + public static final String J_METHOD = "org.eclipse.jetty.security.openid.METHOD"; + public static final String CSRF_TOKEN = "org.eclipse.jetty.security.openid.csrf_token"; + public static final String J_SECURITY_CHECK = "/j_security_check"; private OpenIdConfiguration _configuration; private String _errorPage; @@ -91,7 +93,7 @@ public class OpenIdAuthenticator extends LoginAuthenticator { super.setConfiguration(configuration); - String error = configuration.getInitParameter(__ERROR_PAGE); + String error = configuration.getInitParameter(ERROR_PAGE); if (error != null) setErrorPage(error); @@ -114,7 +116,6 @@ public class OpenIdAuthenticator extends LoginAuthenticator * If true, uris that cause a redirect to a login page will always * be remembered. If false, only the first uri that leads to a login * page redirect is remembered. - * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=379909 * * @param alwaysSave true to always save the uri */ @@ -162,8 +163,8 @@ public class OpenIdAuthenticator extends LoginAuthenticator HttpSession session = ((HttpServletRequest)request).getSession(); Authentication cached = new SessionAuthentication(getAuthMethod(), user, credentials); session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, cached); - session.setAttribute(__CLAIMS, ((OpenIdCredentials)credentials).getClaims()); - session.setAttribute(__RESPONSE, ((OpenIdCredentials)credentials).getResponse()); + session.setAttribute(CLAIMS, ((OpenIdCredentials)credentials).getClaims()); + session.setAttribute(RESPONSE, ((OpenIdCredentials)credentials).getResponse()); } return user; } @@ -180,8 +181,8 @@ public class OpenIdAuthenticator extends LoginAuthenticator //clean up session session.removeAttribute(SessionAuthentication.__J_AUTHENTICATED); - session.removeAttribute(__CLAIMS); - session.removeAttribute(__RESPONSE); + session.removeAttribute(CLAIMS); + session.removeAttribute(RESPONSE); } @Override @@ -199,11 +200,11 @@ public class OpenIdAuthenticator extends LoginAuthenticator if (session == null || session.getAttribute(SessionAuthentication.__J_AUTHENTICATED) == null) return; //not authenticated yet - String juri = (String)session.getAttribute(__J_URI); + String juri = (String)session.getAttribute(J_URI); if (juri == null || juri.length() == 0) return; //no original uri saved - String method = (String)session.getAttribute(__J_METHOD); + String method = (String)session.getAttribute(J_METHOD); if (method == null || method.length() == 0) return; //didn't save original request method @@ -250,7 +251,7 @@ public class OpenIdAuthenticator extends LoginAuthenticator { // Verify anti-forgery state token String state = request.getParameter("state"); - String antiForgeryToken = (String)request.getSession().getAttribute(__CSRF_TOKEN); + String antiForgeryToken = (String)request.getSession().getAttribute(CSRF_TOKEN); if (antiForgeryToken == null || !antiForgeryToken.equals(state)) { LOG.warn("auth failed 403: invalid state parameter"); @@ -269,7 +270,7 @@ public class OpenIdAuthenticator extends LoginAuthenticator String nuri; synchronized (session) { - nuri = (String)session.getAttribute(__J_URI); + nuri = (String)session.getAttribute(J_URI); if (nuri == null || nuri.length() == 0) { @@ -326,7 +327,7 @@ public class OpenIdAuthenticator extends LoginAuthenticator { synchronized (session) { - String jUri = (String)session.getAttribute(__J_URI); + String jUri = (String)session.getAttribute(J_URI); if (jUri != null) { //check if the request is for the same url as the original and restore @@ -338,15 +339,15 @@ public class OpenIdAuthenticator extends LoginAuthenticator if (jUri.equals(buf.toString())) { - MultiMap jPost = (MultiMap)session.getAttribute(__J_POST); + MultiMap jPost = (MultiMap)session.getAttribute(J_POST); if (jPost != null) { LOG.debug("auth rePOST {}->{}", authentication, jUri); baseRequest.setContentParameters(jPost); } - session.removeAttribute(__J_URI); - session.removeAttribute(__J_METHOD); - session.removeAttribute(__J_POST); + session.removeAttribute(J_URI); + session.removeAttribute(J_METHOD); + session.removeAttribute(J_POST); } } } @@ -355,7 +356,6 @@ public class OpenIdAuthenticator extends LoginAuthenticator } } - // if we can't send challenge if (DeferredAuthentication.isDeferred(response)) { @@ -368,19 +368,19 @@ public class OpenIdAuthenticator extends LoginAuthenticator synchronized (session) { // But only if it is not set already, or we save every uri that leads to a login redirect - if (session.getAttribute(__J_URI) == null || _alwaysSaveUri) + if (session.getAttribute(J_URI) == null || _alwaysSaveUri) { StringBuffer buf = request.getRequestURL(); if (request.getQueryString() != null) buf.append("?").append(request.getQueryString()); - session.setAttribute(__J_URI, buf.toString()); - session.setAttribute(__J_METHOD, request.getMethod()); + session.setAttribute(J_URI, buf.toString()); + session.setAttribute(J_METHOD, request.getMethod()); if (MimeTypes.Type.FORM_ENCODED.is(req.getContentType()) && HttpMethod.POST.is(request.getMethod())) { MultiMap formParameters = new MultiMap<>(); baseRequest.extractFormParameters(formParameters); - session.setAttribute(__J_POST, formParameters); + session.setAttribute(J_POST, formParameters); } } } @@ -401,11 +401,11 @@ public class OpenIdAuthenticator extends LoginAuthenticator public boolean isJSecurityCheck(String uri) { - int jsc = uri.indexOf(__J_SECURITY_CHECK); + int jsc = uri.indexOf(J_SECURITY_CHECK); if (jsc < 0) return false; - int e = jsc + __J_SECURITY_CHECK.length(); + int e = jsc + J_SECURITY_CHECK.length(); if (e == uri.length()) return true; char c = uri.charAt(e); @@ -423,7 +423,7 @@ public class OpenIdAuthenticator extends LoginAuthenticator URIUtil.appendSchemeHostPort(redirectUri, request.getScheme(), request.getServerName(), request.getServerPort()); redirectUri.append(request.getContextPath()); - redirectUri.append(__J_SECURITY_CHECK); + redirectUri.append(J_SECURITY_CHECK); return redirectUri.toString(); } @@ -433,23 +433,23 @@ public class OpenIdAuthenticator extends LoginAuthenticator String antiForgeryToken; synchronized (session) { - antiForgeryToken = (session.getAttribute(__CSRF_TOKEN) == null) + antiForgeryToken = (session.getAttribute(CSRF_TOKEN) == null) ? new BigInteger(130, new SecureRandom()).toString(32) - : (String)session.getAttribute(__CSRF_TOKEN); - session.setAttribute(__CSRF_TOKEN, antiForgeryToken); + : (String)session.getAttribute(CSRF_TOKEN); + session.setAttribute(CSRF_TOKEN, antiForgeryToken); } // any custom scopes requested from configuration StringBuilder scopes = new StringBuilder(); for (String s : _configuration.getScopes()) { - scopes.append("%20" + s); + scopes.append(" " + s); } return _configuration.getAuthEndpoint() + - "?client_id=" + _configuration.getClientId() + - "&redirect_uri=" + getRedirectUri(request) + - "&scope=openid" + scopes + + "?client_id=" + UrlEncoded.encodeString(_configuration.getClientId(), StandardCharsets.UTF_8) + + "&redirect_uri=" + UrlEncoded.encodeString(getRedirectUri(request), StandardCharsets.UTF_8) + + "&scope=openid" + UrlEncoded.encodeString(scopes.toString(), StandardCharsets.UTF_8) + "&state=" + antiForgeryToken + "&response_type=code"; } diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java index 5dbc406ce11..f8eba14de3a 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java @@ -28,6 +28,7 @@ import java.util.Base64; import java.util.Map; import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.UrlEncoded; import org.eclipse.jetty.util.ajax.JSON; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -116,25 +117,16 @@ public class OpenIdCredentials { // Issuer Identifier for the OpenID Provider MUST exactly match the value of the iss (issuer) Claim. if (!configuration.getIssuer().equals(claims.get("iss"))) - { - LOG.warn("Invalid \"iss\" Claim: was {}, expected {}", claims.get("iss"), configuration.getIssuer()); - throw new IllegalArgumentException("invalid iss claim"); - } + throw new IllegalArgumentException("Issuer Identifier MUST exactly match the iss Claim"); // The aud (audience) Claim MUST contain the client_id value. if (!configuration.getClientId().equals(claims.get("aud"))) - { - LOG.warn("Audience Claim MUST contain the client_id value"); - throw new IllegalArgumentException("invalid aud claim"); - } + throw new IllegalArgumentException("Audience Claim MUST contain the client_id value"); // If an azp (authorized party) Claim is present, verify that its client_id is the Claim Value. Object azp = claims.get("azp"); if (azp != null && !configuration.getClientId().equals(azp)) - { - LOG.warn("Authorized party claim value should be the client_id"); - throw new IllegalArgumentException("invalid azp claim"); - } + throw new IllegalArgumentException("Authorized party claim value should be the client_id"); } public boolean isExpired() @@ -187,9 +179,9 @@ public class OpenIdCredentials // Use the authorization code to get the id_token from the OpenID Provider String urlParameters = "code=" + authCode + - "&client_id=" + configuration.getClientId() + - "&client_secret=" + configuration.getClientSecret() + - "&redirect_uri=" + redirectUri + + "&client_id=" + UrlEncoded.encodeString(configuration.getClientId(), StandardCharsets.UTF_8) + + "&client_secret=" + UrlEncoded.encodeString(configuration.getClientSecret(), StandardCharsets.UTF_8) + + "&redirect_uri=" + UrlEncoded.encodeString(redirectUri, StandardCharsets.UTF_8) + "&grant_type=authorization_code"; URL url = new URL(configuration.getTokenEndpoint()); @@ -198,14 +190,18 @@ public class OpenIdCredentials connection.setRequestMethod("POST"); connection.setRequestProperty("Host", configuration.getOpenIdProvider()); connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - connection.setRequestProperty("charset", "utf-8"); try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) { wr.write(urlParameters.getBytes(StandardCharsets.UTF_8)); } - InputStream content = (InputStream)connection.getContent(); - return (Map)JSON.parse(IO.toString(content)); + Map result; + try (InputStream content = (InputStream)connection.getContent()) + { + result = (Map)JSON.parse(IO.toString(content)); + } + + return result; } } diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserPrincipal.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserPrincipal.java index 018547ed34f..7034e4efd94 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserPrincipal.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserPrincipal.java @@ -18,12 +18,10 @@ package org.eclipse.jetty.security.openid; -import java.io.Serializable; import java.security.Principal; -public class OpenIdUserPrincipal implements Principal, Serializable +public class OpenIdUserPrincipal implements Principal { - private static final long serialVersionUID = -6226920753748399662L; private final OpenIdCredentials _credentials; public OpenIdUserPrincipal(OpenIdCredentials credentials) diff --git a/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java b/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java index 2d6ec2a0dde..b92f0935c0e 100644 --- a/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java +++ b/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java @@ -81,7 +81,7 @@ public class OpenIdAuthenticationDemo Principal userPrincipal = request.getUserPrincipal(); if (userPrincipal != null) { - Map userInfo = (Map)request.getSession().getAttribute(OpenIdAuthenticator.__CLAIMS); + Map userInfo = (Map)request.getSession().getAttribute(OpenIdAuthenticator.CLAIMS); response.getWriter().println("

Welcome: " + userInfo.get("name") + "

"); response.getWriter().println("Profile
"); response.getWriter().println("Admin
"); @@ -100,7 +100,7 @@ public class OpenIdAuthenticationDemo protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType(MimeTypes.Type.TEXT_HTML.asString()); - Map userInfo = (Map)request.getSession().getAttribute(OpenIdAuthenticator.__CLAIMS); + Map userInfo = (Map)request.getSession().getAttribute(OpenIdAuthenticator.CLAIMS); response.getWriter().println("\n" + "
\n" + diff --git a/jetty-openid/src/test/resources/jetty-logging.properties b/jetty-openid/src/test/resources/jetty-logging.properties index c63d0a5bf4b..b569144d1eb 100755 --- a/jetty-openid/src/test/resources/jetty-logging.properties +++ b/jetty-openid/src/test/resources/jetty-logging.properties @@ -1,3 +1,2 @@ -# Setup default logging implementation for during testing org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog # org.eclipse.jetty.security.openid.LEVEL=DEBUG \ No newline at end of file diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/security/Constraint.java b/jetty-util/src/main/java/org/eclipse/jetty/util/security/Constraint.java index 89c41a3ea25..40f4c808ea7 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/security/Constraint.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/security/Constraint.java @@ -36,7 +36,6 @@ public class Constraint implements Cloneable, Serializable public static final String __CERT_AUTH2 = "CLIENT-CERT"; public static final String __SPNEGO_AUTH = "SPNEGO"; public static final String __NEGOTIATE_AUTH = "NEGOTIATE"; - public static final String __OPENID_AUTH = "OPENID"; public static boolean validateMethod(String method) From 9134def3457dd955e50ad9c62daf18c82c5cba77 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Mon, 9 Sep 2019 17:34:39 +1000 Subject: [PATCH 031/113] OpenIdAuthenticator javadoc update Signed-off-by: Lachlan Roberts --- .../security/openid/OpenIdAuthenticator.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java index 5e2018a37ec..bc814984ed0 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java @@ -50,14 +50,17 @@ import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.security.Constraint; /** - * OpenId Connect Authenticator. + *

Implements authentication using OpenId Connect on top of OAuth 2.0. * - *

This authenticator implements authentication using OpenId Connect on top of OAuth 2.0. - * - *

The authenticator redirects unauthenticated requests to the identity providers authorization endpoint - * which will eventually redirect back to the redirectUri with an authorization code which will be exchanged with - * the token_endpoint for an id_token. The request is then restored back to the original uri requested. - * {@link SessionAuthentication} is then used to wrap Authentication results so that they are associated with the session.

+ *

The OpenIdAuthenticator redirects unauthenticated requests to the OpenID Connect Provider. The End-User is + * eventually redirected back with an Authorization Code to the /j_security_check URI within the context. + * The Authorization Code is then used to authenticate the user through the {@link OpenIdCredentials} and {@link OpenIdLoginService}. + *

+ *

+ * Once a user is authenticated the OpenID Claims can be retrieved through an attribute on the session with the key {@link #CLAIMS}. + * The full response containing the OAuth 2.0 Access Token can be obtained with the session attribute {@link #RESPONSE}. + *

+ *

{@link SessionAuthentication} is then used to wrap Authentication results so that they are associated with the session.

*/ public class OpenIdAuthenticator extends LoginAuthenticator { From 692c01750057ca2cd1a54aaf6b07700364c1a62b Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 9 Sep 2019 15:57:45 +0200 Subject: [PATCH 032/113] Fixes #4058 - Review Locker. Removes the Locker class, replaced by AutoLock. Removed usages of Locker.isLocked() from the session code since it was not necessary. Took the chance to do a little code cleanup. Signed-off-by: Simone Bordet --- .../eclipse/jetty/io/ByteArrayEndPoint.java | 47 ++++---- .../jetty/server/AbstractConnector.java | 22 ++-- .../java/org/eclipse/jetty/server/Server.java | 7 +- .../server/handler/ThreadLimitHandler.java | 20 ++-- .../server/session/AbstractSessionCache.java | 64 +++-------- .../eclipse/jetty/server/session/Session.java | 101 +++++++----------- .../jetty/server/session/SessionCache.java | 7 +- .../jetty/server/session/SessionHandler.java | 5 +- .../eclipse/jetty/util/IteratingCallback.java | 83 +++++++------- .../thread/{Locker.java => AutoLock.java} | 42 +++----- .../util/thread/ReservedThreadExecutor.java | 8 +- .../strategy/ExecuteProduceConsume.java | 22 ++-- .../util/thread/strategy/ProduceConsume.java | 8 +- .../strategy/ProduceExecuteConsume.java | 11 +- .../{LockerTest.java => AutoLockTest.java} | 60 +++++------ .../jetty/server/session/IdleSessionTest.java | 24 +++-- 16 files changed, 215 insertions(+), 316 deletions(-) rename jetty-util/src/main/java/org/eclipse/jetty/util/thread/{Locker.java => AutoLock.java} (68%) rename jetty-util/src/test/java/org/eclipse/jetty/util/thread/{LockerTest.java => AutoLockTest.java} (71%) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java index 14948563718..cdfd2fac200 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java @@ -36,7 +36,7 @@ import java.util.concurrent.locks.Condition; 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.Locker; +import org.eclipse.jetty.util.thread.AutoLock; import org.eclipse.jetty.util.thread.Scheduler; /** @@ -68,24 +68,13 @@ public class ByteArrayEndPoint extends AbstractEndPoint private static final ByteBuffer EOF = BufferUtil.allocate(0); - private final Runnable _runFillable = new Runnable() - { - @Override - public void run() - { - getFillInterest().fillable(); - } - }; - - private final Locker _locker = new Locker(); - private final Condition _hasOutput = _locker.newCondition(); + private final Runnable _runFillable = () -> getFillInterest().fillable(); + private final AutoLock _lock = new AutoLock(); + private final Condition _hasOutput = _lock.newCondition(); private final Queue _inQ = new ArrayDeque<>(); private ByteBuffer _out; private boolean _growOutput; - /** - * - */ public ByteArrayEndPoint() { this(null, 0, null, null); @@ -138,7 +127,7 @@ public class ByteArrayEndPoint extends AbstractEndPoint public void doShutdownOutput() { super.doShutdownOutput(); - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { _hasOutput.signalAll(); } @@ -148,7 +137,7 @@ public class ByteArrayEndPoint extends AbstractEndPoint public void doClose() { super.doClose(); - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { _hasOutput.signalAll(); } @@ -180,7 +169,7 @@ public class ByteArrayEndPoint extends AbstractEndPoint @Override protected void needsFillInterest() throws IOException { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { if (!isOpen()) throw new ClosedChannelException(); @@ -205,7 +194,7 @@ public class ByteArrayEndPoint extends AbstractEndPoint public void addInput(ByteBuffer in) { boolean fillable = false; - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { if (isEOF(_inQ.peek())) throw new RuntimeIOException(new EOFException()); @@ -238,7 +227,7 @@ public class ByteArrayEndPoint extends AbstractEndPoint public void addInputAndExecute(ByteBuffer in) { boolean fillable = false; - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { if (isEOF(_inQ.peek())) throw new RuntimeIOException(new EOFException()); @@ -263,7 +252,7 @@ public class ByteArrayEndPoint extends AbstractEndPoint */ public ByteBuffer getOutput() { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { return _out; } @@ -293,7 +282,7 @@ public class ByteArrayEndPoint extends AbstractEndPoint { ByteBuffer b; - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { b = _out; _out = BufferUtil.allocate(b.capacity()); @@ -314,7 +303,7 @@ public class ByteArrayEndPoint extends AbstractEndPoint { ByteBuffer b; - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { while (BufferUtil.isEmpty(_out) && !isOutputShutdown()) { @@ -351,7 +340,7 @@ public class ByteArrayEndPoint extends AbstractEndPoint */ public void setOutput(ByteBuffer out) { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { _out = out; } @@ -359,7 +348,7 @@ public class ByteArrayEndPoint extends AbstractEndPoint } /** - * @return true if there are bytes remaining to be read from the encoded input + * @return {@code true} if there are bytes remaining to be read from the encoded input */ public boolean hasMore() { @@ -373,7 +362,7 @@ public class ByteArrayEndPoint extends AbstractEndPoint public int fill(ByteBuffer buffer) throws IOException { int filled = 0; - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { while (true) { @@ -418,7 +407,7 @@ public class ByteArrayEndPoint extends AbstractEndPoint public boolean flush(ByteBuffer... buffers) throws IOException { boolean flushed = true; - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { if (!isOpen()) throw new IOException("CLOSED"); @@ -467,7 +456,7 @@ public class ByteArrayEndPoint extends AbstractEndPoint @Override public void reset() { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { _inQ.clear(); _hasOutput.signalAll(); @@ -507,7 +496,7 @@ public class ByteArrayEndPoint extends AbstractEndPoint int q; ByteBuffer b; String o; - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { q = _inQ.size(); b = _inQ.peek(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java index 1723bcc73df..d076742d121 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java @@ -52,7 +52,7 @@ import org.eclipse.jetty.util.component.Graceful; 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.Locker; +import org.eclipse.jetty.util.thread.AutoLock; import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; import org.eclipse.jetty.util.thread.Scheduler; import org.eclipse.jetty.util.thread.ThreadPoolBudget; @@ -144,8 +144,8 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co { protected static final Logger LOG = Log.getLogger(AbstractConnector.class); - private final Locker _locker = new Locker(); - private final Condition _setAccepting = _locker.newCondition(); + private final AutoLock _lock = new AutoLock(); + private final Condition _setAccepting = _lock.newCondition(); private final Map _factories = new LinkedHashMap<>(); // Order is important on server side, so we use a LinkedHashMap private final Server _server; private final Executor _executor; @@ -231,7 +231,7 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co * Get the {@link HttpChannel.Listener}s added to the connector * as a single combined Listener. * This is equivalent to a listener that iterates over the individual - * listeners returned from getBeans(HttpChannel.Listener.class);, + * listeners returned from {@code getBeans(HttpChannel.Listener.class);}, * except that:
    *
  • The result is precomputed, so it is more efficient
  • *
  • The result is ordered by the order added.
  • @@ -332,7 +332,7 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co protected void interruptAcceptors() { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { for (Thread thread : _acceptors) { @@ -387,7 +387,7 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co public void join(long timeout) throws InterruptedException { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { for (Thread thread : _acceptors) { @@ -404,7 +404,7 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co */ public boolean isAccepting() { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { return _accepting; } @@ -412,7 +412,7 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co public void setAccepting(boolean accepting) { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { _accepting = accepting; _setAccepting.signalAll(); @@ -422,7 +422,7 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co @Override public ConnectionFactory getConnectionFactory(String protocol) { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { return _factories.get(StringUtil.asciiToLowerCase(protocol)); } @@ -431,7 +431,7 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co @Override public T getConnectionFactory(Class factoryType) { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { for (ConnectionFactory f : _factories.values()) { @@ -683,7 +683,7 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co { while (isRunning()) { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { if (!_accepting && isRunning()) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java index 282baf968c1..4618a8edf2f 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java @@ -57,7 +57,7 @@ import org.eclipse.jetty.util.component.AttributeContainerMap; import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.util.thread.Locker; +import org.eclipse.jetty.util.thread.AutoLock; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.ShutdownThread; import org.eclipse.jetty.util.thread.ThreadPool; @@ -83,8 +83,7 @@ public class Server extends HandlerWrapper implements Attributes private boolean _dumpBeforeStop = false; private ErrorHandler _errorHandler; private RequestLog _requestLog; - - private final Locker _dateLocker = new Locker(); + private final AutoLock _dateLock = new AutoLock(); private volatile DateField _dateField; public Server() @@ -315,7 +314,7 @@ public class Server extends HandlerWrapper implements Attributes if (df == null || df._seconds != seconds) { - try (Locker.Lock lock = _dateLocker.lock()) + try (AutoLock lock = _dateLock.lock()) { df = _dateField; if (df == null || df._seconds != seconds) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ThreadLimitHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ThreadLimitHandler.java index 7666bcb0641..feab2f5644a 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ThreadLimitHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ThreadLimitHandler.java @@ -48,7 +48,7 @@ import org.eclipse.jetty.util.annotation.ManagedOperation; import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.util.thread.Locker; +import org.eclipse.jetty.util.thread.AutoLock; /** *

    Handler to limit the threads per IP address for DOS protection

    @@ -241,7 +241,7 @@ public class ThreadLimitHandler extends HandlerWrapper } } - protected Remote getRemote(Request baseRequest) + private Remote getRemote(Request baseRequest) { Remote remote = (Remote)baseRequest.getAttribute(REMOTE); if (remote != null) @@ -329,11 +329,11 @@ public class ThreadLimitHandler extends HandlerWrapper return (comma >= 0) ? forwardedFor.substring(comma + 1).trim() : forwardedFor; } - private final class Remote implements Closeable + private static final class Remote implements Closeable { private final String _ip; private final int _limit; - private final Locker _locker = new Locker(); + private final AutoLock _lock = new AutoLock(); private int _permits; private Deque> _queue = new ArrayDeque<>(); private final CompletableFuture _permitted = CompletableFuture.completedFuture(this); @@ -346,7 +346,7 @@ public class ThreadLimitHandler extends HandlerWrapper public CompletableFuture acquire() { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { // Do we have available passes? if (_permits < _limit) @@ -358,16 +358,16 @@ public class ThreadLimitHandler extends HandlerWrapper } // No pass available, so queue a new future - CompletableFuture pass = new CompletableFuture(); + CompletableFuture pass = new CompletableFuture<>(); _queue.addLast(pass); return pass; } } @Override - public void close() throws IOException + public void close() { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { // reduce the allocated passes _permits--; @@ -396,14 +396,14 @@ public class ThreadLimitHandler extends HandlerWrapper @Override public String toString() { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { return String.format("R[ip=%s,p=%d,l=%d,q=%d]", _ip, _permits, _limit, _queue.size()); } } } - private final class RFC7239 extends QuotedCSV + private static final class RFC7239 extends QuotedCSV { String _for; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCache.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCache.java index 11b1ec7453b..8cac60600cc 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCache.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCache.java @@ -29,7 +29,7 @@ import org.eclipse.jetty.util.annotation.ManagedObject; 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.thread.Locker.Lock; +import org.eclipse.jetty.util.thread.AutoLock; /** * AbstractSessionCache @@ -110,9 +110,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements */ public abstract Session newSession(HttpServletRequest request, SessionData data); - /** - * @see org.eclipse.jetty.server.session.SessionCache#newSession(javax.servlet.http.HttpServletRequest, java.lang.String, long, long) - */ @Override public Session newSession(HttpServletRequest request, String id, long time, long maxInactiveMs) { @@ -169,7 +166,7 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements /** * PlaceHolder */ - protected class PlaceHolderSession extends Session + protected static class PlaceHolderSession extends Session { /** @@ -199,9 +196,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements return _handler; } - /** - * @see org.eclipse.jetty.server.session.SessionCache#initialize(org.eclipse.jetty.server.session.SessionContext) - */ @Override public void initialize(SessionContext context) { @@ -248,9 +242,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements return _sessionDataStore; } - /** - * @see org.eclipse.jetty.server.session.SessionCache#setSessionDataStore(org.eclipse.jetty.server.session.SessionDataStore) - */ @Override public void setSessionDataStore(SessionDataStore sessionStore) { @@ -258,9 +249,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements _sessionDataStore = sessionStore; } - /** - * @see org.eclipse.jetty.server.session.SessionCache#getEvictionPolicy() - */ @ManagedAttribute(value = "session eviction policy", readonly = true) @Override public int getEvictionPolicy() @@ -272,8 +260,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements * -1 means we never evict inactive sessions. * 0 means we evict a session after the last request for it exits * >0 is the number of seconds after which we evict inactive sessions from the cache - * - * @see org.eclipse.jetty.server.session.SessionCache#setEvictionPolicy(int) */ @Override public void setEvictionPolicy(int evictionTimeout) @@ -308,7 +294,7 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements * If a session's data cannot be loaded from the store without error, remove * it from the persistent store. * - * @param removeUnloadableSessions if true unloadable sessions will be removed from session store + * @param removeUnloadableSessions if {@code true} unloadable sessions will be removed from session store */ @Override public void setRemoveUnloadableSessions(boolean removeUnloadableSessions) @@ -322,8 +308,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements * If the session object is not in this session store, try getting * the data for it from a SessionDataStore associated with the * session manager. The usage count of the session is incremented. - * - * @see org.eclipse.jetty.server.session.SessionCache#get(java.lang.String) */ @Override public Session get(String id) throws Exception @@ -339,8 +323,8 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements * * @param id The session to retrieve * @param enter if true, the usage count of the session will be incremented - * @return - * @throws Exception + * @return the Session object + * @throws Exception if the session cannot be retrieved */ protected Session getAndEnter(String id, boolean enter) throws Exception { @@ -361,7 +345,7 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements //didn't get a session, try and create one and put in a placeholder for it PlaceHolderSession phs = new PlaceHolderSession(_handler, new SessionData(id, null, null, 0, 0, 0, 0)); - Lock phsLock = phs.lock(); + AutoLock phsLock = phs.lock(); Session s = doPutIfAbsent(id, phs); if (s == null) { @@ -377,7 +361,7 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements break; } - try (Lock lock = session.lock()) + try (AutoLock lock = session.lock()) { //swap it in instead of the placeholder boolean success = doReplace(id, phs, session); @@ -414,7 +398,7 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements { //my placeholder didn't win, check the session returned phsLock.close(); - try (Lock lock = s.lock()) + try (AutoLock lock = s.lock()) { //is it a placeholder? or is a non-resident session? In both cases, chuck it away and start again if (!s.isResident() || s instanceof PlaceHolderSession) @@ -433,7 +417,7 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements else { //check the session returned - try (Lock lock = session.lock()) + try (AutoLock lock = session.lock()) { //is it a placeholder? or is it passivated? In both cases, chuck it away and start again if (!session.isResident() || session instanceof PlaceHolderSession) @@ -494,8 +478,8 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements * Add an entirely new session (created by the application calling Request.getSession(true)) * to the cache. The usage count of the fresh session is incremented. * - * @param id the id - * @param session + * @param id the session id + * @param session the new session to add */ @Override public void add(String id, Session session) throws Exception @@ -503,7 +487,7 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements if (id == null || session == null) throw new IllegalArgumentException("Add key=" + id + " session=" + (session == null ? "null" : session.getId())); - try (Lock lock = session.lock()) + try (AutoLock lock = session.lock()) { if (session.getSessionHandler() == null) throw new IllegalStateException("Session " + id + " is not managed"); @@ -521,9 +505,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements } } - /** - * @deprecated - */ @Override public void put(String id, Session session) throws Exception { @@ -542,8 +523,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements * * If the evictionPolicy == SessionCache.EVICT_ON_SESSION_EXIT then after we have saved * the session, we evict it from the cache. - * - * @see org.eclipse.jetty.server.session.SessionCache#release(java.lang.String, org.eclipse.jetty.server.session.Session) */ @Override public void release(String id, Session session) throws Exception @@ -551,7 +530,7 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements if (id == null || session == null) throw new IllegalArgumentException("Put key=" + id + " session=" + (session == null ? "null" : session.getId())); - try (Lock lock = session.lock()) + try (AutoLock lock = session.lock()) { if (session.getSessionHandler() == null) throw new IllegalStateException("Session " + id + " is not managed"); @@ -630,7 +609,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements * it will check with the data store. * * @throws Exception the Exception - * @see org.eclipse.jetty.server.session.SessionCache#exists(java.lang.String) */ @Override public boolean exists(String id) throws Exception @@ -639,7 +617,7 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements Session s = doGet(id); if (s != null) { - try (Lock lock = s.lock()) + try (AutoLock lock = s.lock()) { //wait for the lock and check the validity of the session return s.isValid(); @@ -653,8 +631,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements /** * Check to see if this cache contains an entry for the session * corresponding to the session id. - * - * @see org.eclipse.jetty.server.session.SessionCache#contains(java.lang.String) */ @Override public boolean contains(String id) throws Exception @@ -665,8 +641,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements /** * Remove a session object from this store and from any backing store. - * - * @see org.eclipse.jetty.server.session.SessionCache#delete(java.lang.String) */ @Override public Session delete(String id) throws Exception @@ -691,9 +665,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements return doDelete(id); } - /** - * @see org.eclipse.jetty.server.session.SessionCache#checkExpiration(Set) - */ @Override public Set checkExpiration(Set candidates) { @@ -741,7 +712,7 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements if (LOG.isDebugEnabled()) LOG.debug("Checking for idle {}", session.getId()); - try (Lock s = session.lock()) + try (AutoLock lock = session.lock()) { if (getEvictionPolicy() > 0 && session.isIdleLongerThan(getEvictionPolicy()) && session.isValid() && session.isResident() && session.getRequests() <= 0) @@ -803,7 +774,7 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements if (session == null) return; - try (Lock lock = session.lock()) + try (AutoLock lock = session.lock()) { final String oldId = session.getId(); session.checkValidForWrite(); //can't change id on invalid session @@ -826,9 +797,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements } } - /** - * @see org.eclipse.jetty.server.session.SessionCache#setSaveOnInactiveEviction(boolean) - */ @Override public void setSaveOnInactiveEviction(boolean saveOnEvict) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java index f33b4d83f1f..8af585f4059 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java @@ -36,8 +36,7 @@ import javax.servlet.http.HttpSessionEvent; import org.eclipse.jetty.io.CyclicTimeout; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.util.thread.Locker; -import org.eclipse.jetty.util.thread.Locker.Lock; +import org.eclipse.jetty.util.thread.AutoLock; /** * Session @@ -73,15 +72,11 @@ public class Session implements SessionHandler.SessionIf VALID, INVALID, INVALIDATING, CHANGING } - ; - public enum IdState { SET, CHANGING } - ; - protected final SessionData _sessionData; // the actual data associated with // a session @@ -98,7 +93,7 @@ public class Session implements SessionHandler.SessionIf protected State _state = State.VALID; // state of the session:valid,invalid // or being invalidated - protected Locker _lock = new Locker(); // sync lock + protected AutoLock _lock = new AutoLock(); protected Condition _stateChangeCompleted = _lock.newCondition(); protected boolean _resident = false; protected final SessionInactivityTimer _sessionInactivityTimer; @@ -128,7 +123,7 @@ public class Session implements SessionHandler.SessionIf long now = System.currentTimeMillis(); //handle what to do with the session after the timer expired getSessionHandler().sessionInactivityTimerExpired(Session.this, now); - try (Lock lock = Session.this.lock()) + try (AutoLock lock = Session.this.lock()) { //grab the lock and check what happened to the session: if it didn't get evicted and //it hasn't expired, we need to reset the timer @@ -213,7 +208,7 @@ public class Session implements SessionHandler.SessionIf */ public long getRequests() { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { return _requests; } @@ -226,7 +221,7 @@ public class Session implements SessionHandler.SessionIf protected void cookieSet() { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { _sessionData.setCookieSet(_sessionData.getAccessed()); } @@ -234,7 +229,7 @@ public class Session implements SessionHandler.SessionIf protected void use() { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { _requests++; @@ -247,7 +242,7 @@ public class Session implements SessionHandler.SessionIf protected boolean access(long time) { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { if (!isValid() || !isResident()) return false; @@ -267,7 +262,7 @@ public class Session implements SessionHandler.SessionIf protected void complete() { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { _requests--; @@ -294,7 +289,7 @@ public class Session implements SessionHandler.SessionIf */ protected boolean isExpiredAt(long time) { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { return _sessionData.isExpiredAt(time); } @@ -309,7 +304,7 @@ public class Session implements SessionHandler.SessionIf protected boolean isIdleLongerThan(int sec) { long now = System.currentTimeMillis(); - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { return ((_sessionData.getAccessed() + (sec * 1000)) <= now); } @@ -350,7 +345,7 @@ public class Session implements SessionHandler.SessionIf */ public void unbindValue(java.lang.String name, Object value) { - if (value != null && value instanceof HttpSessionBindingListener) + if (value instanceof HttpSessionBindingListener) ((HttpSessionBindingListener)value).valueUnbound(new HttpSessionBindingEvent(this, name)); } @@ -363,7 +358,7 @@ public class Session implements SessionHandler.SessionIf */ public void bindValue(java.lang.String name, Object value) { - if (value != null && value instanceof HttpSessionBindingListener) + if (value instanceof HttpSessionBindingListener) ((HttpSessionBindingListener)value).valueBound(new HttpSessionBindingEvent(this, name)); } @@ -373,9 +368,9 @@ public class Session implements SessionHandler.SessionIf public void didActivate() { HttpSessionEvent event = new HttpSessionEvent(this); - for (Iterator iter = _sessionData.getKeys().iterator(); iter.hasNext();) + for (String name : _sessionData.getKeys()) { - Object value = _sessionData.getAttribute(iter.next()); + Object value = _sessionData.getAttribute(name); if (value instanceof HttpSessionActivationListener) { HttpSessionActivationListener listener = (HttpSessionActivationListener)value; @@ -390,9 +385,9 @@ public class Session implements SessionHandler.SessionIf public void willPassivate() { HttpSessionEvent event = new HttpSessionEvent(this); - for (Iterator iter = _sessionData.getKeys().iterator(); iter.hasNext();) + for (String name : _sessionData.getKeys()) { - Object value = _sessionData.getAttribute(iter.next()); + Object value = _sessionData.getAttribute(name); if (value instanceof HttpSessionActivationListener) { HttpSessionActivationListener listener = (HttpSessionActivationListener)value; @@ -403,7 +398,7 @@ public class Session implements SessionHandler.SessionIf public boolean isValid() { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { return _state == State.VALID; } @@ -411,21 +406,15 @@ public class Session implements SessionHandler.SessionIf public boolean isInvalid() { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { return _state == State.INVALID || _state == State.INVALIDATING; } } - public boolean isChanging() - { - checkLocked(); - return _state == State.CHANGING; - } - public long getCookieSetTime() { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { return _sessionData.getCookieSet(); } @@ -434,7 +423,7 @@ public class Session implements SessionHandler.SessionIf @Override public long getCreationTime() throws IllegalStateException { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { checkValidForRead(); return _sessionData.getCreated(); @@ -447,7 +436,7 @@ public class Session implements SessionHandler.SessionIf @Override public String getId() { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { return _sessionData.getId(); } @@ -474,7 +463,7 @@ public class Session implements SessionHandler.SessionIf @Override public long getLastAccessedTime() { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { if (isInvalid()) { @@ -501,7 +490,7 @@ public class Session implements SessionHandler.SessionIf @Override public void setMaxInactiveInterval(int secs) { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { _sessionData.setMaxInactiveMs((long)secs * 1000L); _sessionData.calcAndSetExpiry(); @@ -530,7 +519,7 @@ public class Session implements SessionHandler.SessionIf { long time = 0; - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { long remaining = _sessionData.getExpiry() - now; long maxInactive = _sessionData.getMaxInactiveMs(); @@ -594,7 +583,7 @@ public class Session implements SessionHandler.SessionIf @Override public int getMaxInactiveInterval() { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { long maxInactiveMs = _sessionData.getMaxInactiveMs(); return (int)(maxInactiveMs < 0 ? -1 : maxInactiveMs / 1000); @@ -624,8 +613,6 @@ public class Session implements SessionHandler.SessionIf */ protected void checkValidForWrite() throws IllegalStateException { - checkLocked(); - if (_state == State.INVALID) throw new IllegalStateException("Not valid for write: id=" + _sessionData.getId() + " created=" + _sessionData.getCreated() + @@ -649,8 +636,6 @@ public class Session implements SessionHandler.SessionIf */ protected void checkValidForRead() throws IllegalStateException { - checkLocked(); - if (_state == State.INVALID) throw new IllegalStateException("Invalid for read: id=" + _sessionData.getId() + " created=" + _sessionData.getCreated() + @@ -666,19 +651,13 @@ public class Session implements SessionHandler.SessionIf throw new IllegalStateException("Invalid for read: id=" + _sessionData.getId() + " not resident"); } - protected void checkLocked() throws IllegalStateException - { - if (!_lock.isLocked()) - throw new IllegalStateException("Session not locked"); - } - /** * @see javax.servlet.http.HttpSession#getAttribute(java.lang.String) */ @Override public Object getAttribute(String name) { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { checkValidForRead(); return _sessionData.getAttribute(name); @@ -692,7 +671,7 @@ public class Session implements SessionHandler.SessionIf @Deprecated(since = "Servlet API 2.2") public Object getValue(String name) { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { checkValidForRead(); return _sessionData.getAttribute(name); @@ -705,11 +684,11 @@ public class Session implements SessionHandler.SessionIf @Override public Enumeration getAttributeNames() { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { checkValidForRead(); final Iterator itor = _sessionData.getKeys().iterator(); - return new Enumeration() + return new Enumeration<>() { @Override @@ -745,7 +724,7 @@ public class Session implements SessionHandler.SessionIf @Deprecated(since = "Servlet API 2.2") public String[] getValueNames() throws IllegalStateException { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { checkValidForRead(); Iterator itor = _sessionData.getKeys().iterator(); @@ -768,7 +747,7 @@ public class Session implements SessionHandler.SessionIf public void setAttribute(String name, Object value) { Object old = null; - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { // if session is not valid, don't accept the set checkValidForWrite(); @@ -822,7 +801,7 @@ public class Session implements SessionHandler.SessionIf String id = null; String extendedId = null; - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { while (true) { @@ -858,7 +837,7 @@ public class Session implements SessionHandler.SessionIf String newId = _handler._sessionIdManager.renewSessionId(id, extendedId, request); - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { switch (_state) { @@ -936,7 +915,7 @@ public class Session implements SessionHandler.SessionIf * * @return the lock */ - public Lock lock() + public AutoLock lock() { return _lock.lock(); } @@ -948,7 +927,7 @@ public class Session implements SessionHandler.SessionIf { boolean result = false; - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { while (true) @@ -1007,7 +986,7 @@ public class Session implements SessionHandler.SessionIf */ protected void finishInvalidate() throws IllegalStateException { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { try { @@ -1045,7 +1024,7 @@ public class Session implements SessionHandler.SessionIf @Override public boolean isNew() throws IllegalStateException { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { checkValidForRead(); return _newSession; @@ -1054,7 +1033,7 @@ public class Session implements SessionHandler.SessionIf public void setIdChanged(boolean changed) { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { _idChanged = changed; } @@ -1062,7 +1041,7 @@ public class Session implements SessionHandler.SessionIf public boolean isIdChanged() { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { return _idChanged; } @@ -1099,7 +1078,7 @@ public class Session implements SessionHandler.SessionIf @Override public String toString() { - try (Lock lock = _lock.lock()) + try (AutoLock lock = _lock.lock()) { return String.format("%s@%x{id=%s,x=%s,req=%d,res=%b}", getClass().getSimpleName(), diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionCache.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionCache.java index 69bcceeafa1..a9b38f95651 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionCache.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionCache.java @@ -128,9 +128,9 @@ public interface SessionCache extends LifeCycle * @throws Exception if any error occurred * @deprecated @see release */ + @Deprecated void put(String id, Session session) throws Exception; - - + /** * Finish using a Session. This is called by the SessionHandler * once a request is finished with a Session. SessionCache @@ -142,8 +142,7 @@ public interface SessionCache extends LifeCycle * @throws Exception if any error occurred */ void release(String id, Session session) throws Exception; - - + /** * Check to see if a Session is in the cache. Does NOT consult * the SessionDataStore. diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java index 4f4dc81d758..0d6e2c48a7f 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java @@ -29,7 +29,6 @@ import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; - import javax.servlet.DispatcherType; import javax.servlet.ServletException; import javax.servlet.SessionCookieConfig; @@ -59,7 +58,7 @@ import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.statistic.CounterStatistic; import org.eclipse.jetty.util.statistic.SampleStatistic; -import org.eclipse.jetty.util.thread.Locker.Lock; +import org.eclipse.jetty.util.thread.AutoLock; import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; import org.eclipse.jetty.util.thread.Scheduler; @@ -1264,7 +1263,7 @@ public class SessionHandler extends ScopedHandler //1. valid //2. expired //3. idle - try (Lock lock = session.lock()) + try (AutoLock lock = session.lock()) { if (session.getRequests() > 0) return; //session can't expire or be idle if there is a request in it diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/IteratingCallback.java b/jetty-util/src/main/java/org/eclipse/jetty/util/IteratingCallback.java index 9e82816f2be..1fc1d9523a4 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/IteratingCallback.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/IteratingCallback.java @@ -20,7 +20,7 @@ package org.eclipse.jetty.util; import java.io.IOException; -import org.eclipse.jetty.util.thread.Locker; +import org.eclipse.jetty.util.thread.AutoLock; /** * This specialized callback implements a pattern that allows @@ -125,7 +125,7 @@ public abstract class IteratingCallback implements Callback SUCCEEDED } - private Locker _locker = new Locker(); + private final AutoLock _lock = new AutoLock(); private State _state; private boolean _iterate; @@ -188,35 +188,31 @@ public abstract class IteratingCallback implements Callback { boolean process = false; - loop: - while (true) + try (AutoLock lock = _lock.lock()) { - try (Locker.Lock lock = _locker.lock()) + switch (_state) { - switch (_state) - { - case PENDING: - case CALLED: - // process will be called when callback is handled - break loop; + case PENDING: + case CALLED: + // process will be called when callback is handled + break; - case IDLE: - _state = State.PROCESSING; - process = true; - break loop; + case IDLE: + _state = State.PROCESSING; + process = true; + break; - case PROCESSING: - _iterate = true; - break loop; + case PROCESSING: + _iterate = true; + break; - case FAILED: - case SUCCEEDED: - break loop; + case FAILED: + case SUCCEEDED: + break; - case CLOSED: - default: - throw new IllegalStateException(toString()); - } + case CLOSED: + default: + throw new IllegalStateException(toString()); } } if (process) @@ -243,11 +239,11 @@ public abstract class IteratingCallback implements Callback catch (Throwable x) { failed(x); - break processing; + break; } // acted on the action we have just received - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { switch (_state) { @@ -295,18 +291,11 @@ public abstract class IteratingCallback implements Callback case CALLED: { - switch (action) - { - case SCHEDULED: - { - // we lost the race, so we have to keep processing - _state = State.PROCESSING; - continue processing; - } - - default: - throw new IllegalStateException(String.format("%s[action=%s]", this, action)); - } + if (action != Action.SCHEDULED) + throw new IllegalStateException(String.format("%s[action=%s]", this, action)); + // we lost the race, so we have to keep processing + _state = State.PROCESSING; + continue processing; } case SUCCEEDED: @@ -335,7 +324,7 @@ public abstract class IteratingCallback implements Callback public void succeeded() { boolean process = false; - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { switch (_state) { @@ -375,7 +364,7 @@ public abstract class IteratingCallback implements Callback public void failed(Throwable x) { boolean failure = false; - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { switch (_state) { @@ -405,7 +394,7 @@ public abstract class IteratingCallback implements Callback public void close() { String failure = null; - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { switch (_state) { @@ -434,7 +423,7 @@ public abstract class IteratingCallback implements Callback */ boolean isIdle() { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { return _state == State.IDLE; } @@ -442,7 +431,7 @@ public abstract class IteratingCallback implements Callback public boolean isClosed() { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { return _state == State.CLOSED; } @@ -453,7 +442,7 @@ public abstract class IteratingCallback implements Callback */ public boolean isFailed() { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { return _state == State.FAILED; } @@ -464,7 +453,7 @@ public abstract class IteratingCallback implements Callback */ public boolean isSucceeded() { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { return _state == State.SUCCEEDED; } @@ -481,7 +470,7 @@ public abstract class IteratingCallback implements Callback */ public boolean reset() { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { switch (_state) { diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/Locker.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/AutoLock.java similarity index 68% rename from jetty-util/src/main/java/org/eclipse/jetty/util/thread/Locker.java rename to jetty-util/src/main/java/org/eclipse/jetty/util/thread/AutoLock.java index b56180bd90e..9bcab7eff2a 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/Locker.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/AutoLock.java @@ -22,37 +22,27 @@ import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /** - *

    Convenience auto closeable {@link java.util.concurrent.locks.ReentrantLock} wrapper.

    - * + * Reentrant lock that can be used in a try-with-resources statement. *
    - * try (Locker.Lock lock = locker.lock())
    + * try (AutoLock lock = this.lock.lock())
      * {
    - *   // something
    + *     // Something
      * }
      * 
    */ -public class Locker +public class AutoLock implements AutoCloseable { private final ReentrantLock _lock = new ReentrantLock(); - private final Lock _unlock = new Lock(); /** *

    Acquires the lock.

    * - * @return the lock to unlock + * @return this AutoLock for unlocking */ - public Lock lock() + public AutoLock lock() { _lock.lock(); - return _unlock; - } - - /** - * @return whether this lock has been acquired - */ - public boolean isLocked() - { - return _lock.isLocked(); + return this; } /** @@ -63,15 +53,15 @@ public class Locker return _lock.newCondition(); } - /** - *

    The unlocker object that unlocks when it is closed.

    - */ - public class Lock implements AutoCloseable + // Package-private for testing only. + boolean isLocked() { - @Override - public void close() - { - _lock.unlock(); - } + return _lock.isLocked(); + } + + @Override + public void close() + { + _lock.unlock(); } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java index 3c7fa26e90b..d34792634ef 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java @@ -248,8 +248,8 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec private class ReservedThread implements Runnable { - private final Locker _locker = new Locker(); - private final Condition _wakeup = _locker.newCondition(); + private final AutoLock _lock = new AutoLock(); + private final Condition _wakeup = _lock.newCondition(); private boolean _starting = true; private Runnable _task = null; @@ -258,7 +258,7 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec if (LOG.isDebugEnabled()) LOG.debug("{} offer {}", this, task); - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { _task = task; _wakeup.signal(); @@ -280,7 +280,7 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec { boolean idle = false; - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { if (_task == null) { diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ExecuteProduceConsume.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ExecuteProduceConsume.java index d32a779f543..bb14efbeb76 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ExecuteProduceConsume.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ExecuteProduceConsume.java @@ -22,11 +22,10 @@ import java.util.concurrent.Executor; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.thread.AutoLock; import org.eclipse.jetty.util.thread.ExecutionStrategy; import org.eclipse.jetty.util.thread.Invocable; import org.eclipse.jetty.util.thread.Invocable.InvocationType; -import org.eclipse.jetty.util.thread.Locker; -import org.eclipse.jetty.util.thread.Locker.Lock; /** *

    A strategy where the thread that produces will always run the resulting task.

    @@ -45,7 +44,7 @@ public class ExecuteProduceConsume implements ExecutionStrategy, Runnable { private static final Logger LOG = Log.getLogger(ExecuteProduceConsume.class); - private final Locker _locker = new Locker(); + private final AutoLock _lock = new AutoLock(); private final Runnable _runProduce = new RunProduce(); private final Producer _producer; private final Executor _executor; @@ -67,7 +66,7 @@ public class ExecuteProduceConsume implements ExecutionStrategy, Runnable LOG.debug("{} execute", this); boolean produce = false; - try (Lock locked = _locker.lock()) + try (AutoLock lock = _lock.lock()) { // If we are idle and a thread is not producing if (_idle) @@ -98,7 +97,7 @@ public class ExecuteProduceConsume implements ExecutionStrategy, Runnable if (LOG.isDebugEnabled()) LOG.debug("{} spawning", this); boolean dispatch = false; - try (Lock locked = _locker.lock()) + try (AutoLock lock = _lock.lock()) { if (_idle) dispatch = true; @@ -115,7 +114,7 @@ public class ExecuteProduceConsume implements ExecutionStrategy, Runnable if (LOG.isDebugEnabled()) LOG.debug("{} run", this); boolean produce = false; - try (Lock locked = _locker.lock()) + try (AutoLock lock = _lock.lock()) { _pending = false; if (!_idle && !_producing) @@ -145,7 +144,7 @@ public class ExecuteProduceConsume implements ExecutionStrategy, Runnable LOG.debug("{} produced {}", this, task); boolean dispatch = false; - try (Lock locked = _locker.lock()) + try (AutoLock lock = _lock.lock()) { // Finished producing _producing = false; @@ -191,13 +190,12 @@ public class ExecuteProduceConsume implements ExecutionStrategy, Runnable // Run the task. if (LOG.isDebugEnabled()) LOG.debug("{} run {}", this, task); - if (task != null) - task.run(); + task.run(); if (LOG.isDebugEnabled()) LOG.debug("{} ran {}", this, task); // Once we have run the task, we can try producing again. - try (Lock locked = _locker.lock()) + try (AutoLock lock = _lock.lock()) { // Is another thread already producing or we are now idle? if (_producing || _idle) @@ -212,7 +210,7 @@ public class ExecuteProduceConsume implements ExecutionStrategy, Runnable public Boolean isIdle() { - try (Lock locked = _locker.lock()) + try (AutoLock lock = _lock.lock()) { return _idle; } @@ -223,7 +221,7 @@ public class ExecuteProduceConsume implements ExecutionStrategy, Runnable { StringBuilder builder = new StringBuilder(); builder.append("EPC "); - try (Lock locked = _locker.lock()) + try (AutoLock lock = _lock.lock()) { builder.append(_idle ? "Idle/" : ""); builder.append(_producing ? "Prod/" : ""); diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ProduceConsume.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ProduceConsume.java index f37a94a40cf..15de66f6694 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ProduceConsume.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ProduceConsume.java @@ -22,8 +22,8 @@ import java.util.concurrent.Executor; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.thread.AutoLock; import org.eclipse.jetty.util.thread.ExecutionStrategy; -import org.eclipse.jetty.util.thread.Locker; /** *

    A strategy where the caller thread iterates over task production, submitting each @@ -33,7 +33,7 @@ public class ProduceConsume implements ExecutionStrategy, Runnable { private static final Logger LOG = Log.getLogger(ExecuteProduceConsume.class); - private final Locker _locker = new Locker(); + private final AutoLock _lock = new AutoLock(); private final Producer _producer; private final Executor _executor; private State _state = State.IDLE; @@ -47,7 +47,7 @@ public class ProduceConsume implements ExecutionStrategy, Runnable @Override public void produce() { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { switch (_state) { @@ -73,7 +73,7 @@ public class ProduceConsume implements ExecutionStrategy, Runnable if (task == null) { - try (Locker.Lock lock = _locker.lock()) + try (AutoLock lock = _lock.lock()) { switch (_state) { diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ProduceExecuteConsume.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ProduceExecuteConsume.java index 0b7dc16c7bf..a648cc4e118 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ProduceExecuteConsume.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy/ProduceExecuteConsume.java @@ -22,11 +22,10 @@ import java.util.concurrent.Executor; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.thread.AutoLock; import org.eclipse.jetty.util.thread.ExecutionStrategy; import org.eclipse.jetty.util.thread.Invocable; import org.eclipse.jetty.util.thread.Invocable.InvocationType; -import org.eclipse.jetty.util.thread.Locker; -import org.eclipse.jetty.util.thread.Locker.Lock; /** *

    A strategy where the caller thread iterates over task production, submitting each @@ -36,7 +35,7 @@ public class ProduceExecuteConsume implements ExecutionStrategy { private static final Logger LOG = Log.getLogger(ProduceExecuteConsume.class); - private final Locker _locker = new Locker(); + private final AutoLock _lock = new AutoLock(); private final Producer _producer; private final Executor _executor; private State _state = State.IDLE; @@ -50,7 +49,7 @@ public class ProduceExecuteConsume implements ExecutionStrategy @Override public void produce() { - try (Lock locked = _locker.lock()) + try (AutoLock lock = _lock.lock()) { switch (_state) { @@ -77,7 +76,7 @@ public class ProduceExecuteConsume implements ExecutionStrategy if (task == null) { - try (Lock locked = _locker.lock()) + try (AutoLock lock = _lock.lock()) { switch (_state) { @@ -106,7 +105,7 @@ public class ProduceExecuteConsume implements ExecutionStrategy @Override public void dispatch() { - _executor.execute(() -> produce()); + _executor.execute(this::produce); } private enum State diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/LockerTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/AutoLockTest.java similarity index 71% rename from jetty-util/src/test/java/org/eclipse/jetty/util/thread/LockerTest.java rename to jetty-util/src/test/java/org/eclipse/jetty/util/thread/AutoLockTest.java index 967969d771d..3b388d0771e 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/LockerTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/AutoLockTest.java @@ -26,19 +26,15 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -public class LockerTest +public class AutoLockTest { - public LockerTest() - { - } - @Test public void testLocked() { - Locker lock = new Locker(); + AutoLock lock = new AutoLock(); assertFalse(lock.isLocked()); - try (Locker.Lock l = lock.lock()) + try (AutoLock l = lock.lock()) { assertTrue(lock.isLocked()); } @@ -53,10 +49,10 @@ public class LockerTest @Test public void testLockedException() { - Locker lock = new Locker(); + AutoLock lock = new AutoLock(); assertFalse(lock.isLocked()); - try (Locker.Lock l = lock.lock()) + try (AutoLock l = lock.lock()) { assertTrue(lock.isLocked()); throw new Exception(); @@ -76,27 +72,23 @@ public class LockerTest @Test public void testContend() throws Exception { - final Locker lock = new Locker(); + AutoLock lock = new AutoLock(); final CountDownLatch held0 = new CountDownLatch(1); final CountDownLatch hold0 = new CountDownLatch(1); - Thread thread0 = new Thread() + Thread thread0 = new Thread(() -> { - @Override - public void run() + try (AutoLock l = lock.lock()) { - try (Locker.Lock l = lock.lock()) - { - held0.countDown(); - hold0.await(); - } - catch (InterruptedException e) - { - e.printStackTrace(); - } + held0.countDown(); + hold0.await(); } - }; + catch (InterruptedException e) + { + e.printStackTrace(); + } + }); thread0.start(); held0.await(); @@ -104,22 +96,18 @@ public class LockerTest final CountDownLatch held1 = new CountDownLatch(1); final CountDownLatch hold1 = new CountDownLatch(1); - Thread thread1 = new Thread() + Thread thread1 = new Thread(() -> { - @Override - public void run() + try (AutoLock l = lock.lock()) { - try (Locker.Lock l = lock.lock()) - { - held1.countDown(); - hold1.await(); - } - catch (InterruptedException e) - { - e.printStackTrace(); - } + held1.countDown(); + hold1.await(); } - }; + catch (InterruptedException e) + { + e.printStackTrace(); + } + }); thread1.start(); // thread1 will be spinning here assertFalse(held1.await(100, TimeUnit.MILLISECONDS)); diff --git a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java index c66713e9f72..1074f0f8cd9 100644 --- a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java +++ b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java @@ -34,11 +34,13 @@ import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.StacklessLogging; -import org.eclipse.jetty.util.thread.Locker.Lock; +import org.eclipse.jetty.util.thread.AutoLock; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -94,7 +96,7 @@ public class IdleSessionTest ContentResponse response = client.GET(url + "?action=init"); assertEquals(HttpServletResponse.SC_OK, response.getStatus()); String sessionCookie = response.getHeaders().get("Set-Cookie"); - assertTrue(sessionCookie != null); + assertNotNull(sessionCookie); //ensure request has finished being handled synchronizer.await(5, TimeUnit.SECONDS); @@ -148,7 +150,7 @@ public class IdleSessionTest response = client.GET(url + "?action=init"); assertEquals(HttpServletResponse.SC_OK, response.getStatus()); sessionCookie = response.getHeaders().get("Set-Cookie"); - assertTrue(sessionCookie != null); + assertNotNull(sessionCookie); id = TestServer.extractSessionId(sessionCookie); //ensure request has finished being handled @@ -220,7 +222,7 @@ public class IdleSessionTest ContentResponse response = client.GET(url + "?action=init"); assertEquals(HttpServletResponse.SC_OK, response.getStatus()); String sessionCookie = response.getHeaders().get("Set-Cookie"); - assertTrue(sessionCookie != null); + assertNotNull(sessionCookie); //ensure request has finished being handled synchronizer.await(5, TimeUnit.SECONDS); @@ -265,7 +267,7 @@ public class IdleSessionTest response = client.GET(url + "?action=init"); assertEquals(HttpServletResponse.SC_OK, response.getStatus()); sessionCookie = response.getHeaders().get("Set-Cookie"); - assertTrue(sessionCookie != null); + assertNotNull(sessionCookie); id = TestServer.extractSessionId(sessionCookie); //ensure request has finished being handled @@ -317,7 +319,7 @@ public class IdleSessionTest session.setAttribute("value", 1); originalId = session.getId(); Session s = (Session)session; - try (Lock lock = s.lock()) + try (AutoLock lock = s.lock()) { assertTrue(s.isResident()); } @@ -326,21 +328,21 @@ public class IdleSessionTest else if ("test".equals(action)) { HttpSession session = request.getSession(false); - assertTrue(session != null); - assertTrue(originalId.equals(session.getId())); + assertNotNull(session); + assertEquals(originalId, session.getId()); Session s = (Session)session; - try (Lock lock = s.lock();) + try (AutoLock lock = s.lock()) { assertTrue(s.isResident()); } Integer v = (Integer)session.getAttribute("value"); - session.setAttribute("value", v.intValue() + 1); + session.setAttribute("value", v + 1); _session = session; } else if ("testfail".equals(action)) { HttpSession session = request.getSession(false); - assertTrue(session == null); + assertNull(session); _session = session; } } From eb5d4ece3936deb602403d0d986212ef8e46a959 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 9 Sep 2019 09:56:16 -0500 Subject: [PATCH 033/113] Issue #3974 - disabling flaky AsyncMiddleManServletTest Signed-off-by: Joakim Erdfelt --- .../java/org/eclipse/jetty/proxy/AsyncMiddleManServletTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncMiddleManServletTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncMiddleManServletTest.java index c64d64fb571..a9dbb178f84 100644 --- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncMiddleManServletTest.java +++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncMiddleManServletTest.java @@ -78,6 +78,7 @@ import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.StacklessLogging; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.OS; @@ -88,6 +89,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +@Disabled("See issue #3974") public class AsyncMiddleManServletTest { private static final Logger LOG = Log.getLogger(AsyncMiddleManServletTest.class); From c7867cdd5ee24101dfcc872741b6ba6d7ed1a820 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 9 Sep 2019 19:47:06 +0200 Subject: [PATCH 034/113] Fixes #3956 - Remove and warn illegal HTTP/2 response headers. Implemented RFC 7540 8.1.2.2. Fields that should be removed are now dropped. Signed-off-by: Simone Bordet --- .../jetty/http2/hpack/HpackEncoder.java | 38 ++++---- .../eclipse/jetty/http2/hpack/HpackTest.java | 88 +++++++++++++++---- 2 files changed, 89 insertions(+), 37 deletions(-) diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java index bc3b244c8d0..1d9443e7aad 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java @@ -20,8 +20,9 @@ package org.eclipse.jetty.http2.hpack; import java.nio.ByteBuffer; import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; import java.util.Set; -import java.util.stream.Collectors; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; @@ -33,9 +34,7 @@ import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.http2.hpack.HpackContext.Entry; import org.eclipse.jetty.http2.hpack.HpackContext.StaticEntry; -import org.eclipse.jetty.util.ArrayTrie; import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.Trie; import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -75,9 +74,8 @@ public class HpackEncoder HttpHeader.AUTHORIZATION, HttpHeader.SET_COOKIE, HttpHeader.SET_COOKIE2); - private static final PreEncodedHttpField CONNECTION_TE = new PreEncodedHttpField(HttpHeader.CONNECTION, "te"); - private static final PreEncodedHttpField TE_TRAILERS = new PreEncodedHttpField(HttpHeader.TE, "trailers"); - private static final Trie specialHopHeaders = new ArrayTrie<>(6); + private static final EnumSet HEADERS_TO_REMOVE = EnumSet.of(HttpHeader.CONNECTION, HttpHeader.KEEP_ALIVE, + HttpHeader.PROXY_CONNECTION, HttpHeader.TRANSFER_ENCODING, HttpHeader.UPGRADE); static { @@ -85,8 +83,6 @@ public class HpackEncoder { __status[code.getCode()] = new PreEncodedHttpField(HttpHeader.C_STATUS, Integer.toString(code.getCode())); } - specialHopHeaders.put("close", true); - specialHopHeaders.put("te", true); } private final HpackContext _context; @@ -180,26 +176,30 @@ public class HpackEncoder encode(buffer, status); } - // Add all non-connection fields. + // Remove fields as specified in RFC 7540, 8.1.2.2. HttpFields fields = metadata.getFields(); if (fields != null) { - Set hopHeaders = fields.getCSV(HttpHeader.CONNECTION, false).stream() - .filter(v -> specialHopHeaders.get(v) == Boolean.TRUE) - .map(StringUtil::asciiToLowerCase) - .collect(Collectors.toSet()); + // For example: Connection: Close, TE, Upgrade, Custom. + List values = fields.getCSV(HttpHeader.CONNECTION, false); + Set hopHeaders = new HashSet<>(); + for (String value : values) + { + // Keep TE as a special case. + if (!"TE".equalsIgnoreCase(value)) + hopHeaders.add(StringUtil.asciiToLowerCase(value)); + } for (HttpField field : fields) { - if (field.getHeader() == HttpHeader.CONNECTION) + HttpHeader header = field.getHeader(); + if (header != null && HEADERS_TO_REMOVE.contains(header)) continue; - if (!hopHeaders.isEmpty() && hopHeaders.contains(StringUtil.asciiToLowerCase(field.getName()))) + if (hopHeaders.contains(StringUtil.asciiToLowerCase(field.getName()))) continue; - if (field.getHeader() == HttpHeader.TE) + if (header == HttpHeader.TE) { - if (!field.contains("trailers")) + if (!"trailers".equalsIgnoreCase(field.getValue())) continue; - encode(buffer, CONNECTION_TE); - encode(buffer, TE_TRAILERS); } encode(buffer, field); } diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java index 7b9218d3585..565b4158bb9 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java @@ -67,7 +67,7 @@ public class HpackTest BufferUtil.flipToFlush(buffer, 0); Response decoded0 = (Response)decoder.decode(buffer); original0.getFields().put(new HttpField(HttpHeader.CONTENT_ENCODING, "")); - assertMetadataSame(original0, decoded0); + assertMetaDataResponseSame(original0, decoded0); // Same again? BufferUtil.clearToFill(buffer); @@ -75,7 +75,7 @@ public class HpackTest BufferUtil.flipToFlush(buffer, 0); Response decoded0b = (Response)decoder.decode(buffer); - assertMetadataSame(original0, decoded0b); + assertMetaDataResponseSame(original0, decoded0b); HttpFields fields1 = new HttpFields(); fields1.add(HttpHeader.CONTENT_TYPE, "text/plain"); @@ -93,7 +93,7 @@ public class HpackTest BufferUtil.flipToFlush(buffer, 0); Response decoded1 = (Response)decoder.decode(buffer); - assertMetadataSame(original1, decoded1); + assertMetaDataResponseSame(original1, decoded1); assertEquals("custom-key", decoded1.getFields().getField("Custom-Key").getName()); } @@ -112,9 +112,9 @@ public class HpackTest BufferUtil.clearToFill(buffer); encoder.encode(buffer, original0); BufferUtil.flipToFlush(buffer, 0); - MetaData decoded0 = (MetaData)decoder.decode(buffer); + MetaData decoded0 = decoder.decode(buffer); - assertMetadataSame(original0, decoded0); + assertMetaDataSame(original0, decoded0); HttpFields fields1 = new HttpFields(); fields1.add("1234567890", "1234567890123456789012345678901234567890"); @@ -151,14 +151,14 @@ public class HpackTest BufferUtil.clearToFill(buffer); encoder.encode(buffer, original0); BufferUtil.flipToFlush(buffer, 0); - MetaData decoded0 = (MetaData)decoder.decode(buffer); + MetaData decoded0 = decoder.decode(buffer); assertEquals(2, encoder.getHpackContext().size()); assertEquals(2, decoder.getHpackContext().size()); assertEquals("123456789012345678901234567890123456788901234567890", encoder.getHpackContext().get(HpackContext.STATIC_TABLE.length + 1).getHttpField().getName()); - assertEquals("foo", encoder.getHpackContext().get(HpackContext.STATIC_TABLE.length + 0).getHttpField().getName()); + assertEquals("foo", encoder.getHpackContext().get(HpackContext.STATIC_TABLE.length).getHttpField().getName()); - assertMetadataSame(original0, decoded0); + assertMetaDataSame(original0, decoded0); HttpFields fields1 = new HttpFields(); fields1.add("123456789012345678901234567890123456788901234567890", "other_value"); @@ -168,32 +168,84 @@ public class HpackTest BufferUtil.clearToFill(buffer); encoder.encode(buffer, original1); BufferUtil.flipToFlush(buffer, 0); - MetaData decoded1 = (MetaData)decoder.decode(buffer); - assertMetadataSame(original1, decoded1); + MetaData decoded1 = decoder.decode(buffer); + assertMetaDataSame(original1, decoded1); assertEquals(2, encoder.getHpackContext().size()); assertEquals(2, decoder.getHpackContext().size()); - assertEquals("x", encoder.getHpackContext().get(HpackContext.STATIC_TABLE.length + 0).getHttpField().getName()); + assertEquals("x", encoder.getHpackContext().get(HpackContext.STATIC_TABLE.length).getHttpField().getName()); assertEquals("foo", encoder.getHpackContext().get(HpackContext.STATIC_TABLE.length + 1).getHttpField().getName()); } - private void assertMetadataSame(MetaData.Response expected, MetaData.Response actual) + @Test + public void testHopHeadersAreRemoved() throws Exception + { + HpackEncoder encoder = new HpackEncoder(); + HpackDecoder decoder = new HpackDecoder(4096, 16384); + + HttpFields input = new HttpFields(); + input.put(HttpHeader.ACCEPT, "*"); + input.put(HttpHeader.CONNECTION, "TE, Upgrade, Custom"); + input.put("Custom", "Pizza"); + input.put(HttpHeader.KEEP_ALIVE, "true"); + input.put(HttpHeader.PROXY_CONNECTION, "foo"); + input.put(HttpHeader.TE, "1234567890abcedf"); + input.put(HttpHeader.TRANSFER_ENCODING, "chunked"); + input.put(HttpHeader.UPGRADE, "gold"); + + ByteBuffer buffer = BufferUtil.allocate(2048); + BufferUtil.clearToFill(buffer); + encoder.encode(buffer, new MetaData(HttpVersion.HTTP_2, input)); + BufferUtil.flipToFlush(buffer, 0); + MetaData metaData = decoder.decode(buffer); + HttpFields output = metaData.getFields(); + + assertEquals(1, output.size()); + assertEquals("*", output.get(HttpHeader.ACCEPT)); + } + + @Test + public void testTETrailers() throws Exception + { + HpackEncoder encoder = new HpackEncoder(); + HpackDecoder decoder = new HpackDecoder(4096, 16384); + + HttpFields input = new HttpFields(); + input.put(HttpHeader.CONNECTION, "TE"); + String teValue = "trailers"; + input.put(HttpHeader.TE, teValue); + String trailerValue = "Custom"; + input.put(HttpHeader.TRAILER, trailerValue); + + ByteBuffer buffer = BufferUtil.allocate(2048); + BufferUtil.clearToFill(buffer); + encoder.encode(buffer, new MetaData(HttpVersion.HTTP_2, input)); + BufferUtil.flipToFlush(buffer, 0); + MetaData metaData = decoder.decode(buffer); + HttpFields output = metaData.getFields(); + + assertEquals(2, output.size()); + assertEquals(teValue, output.get(HttpHeader.TE)); + assertEquals(trailerValue, output.get(HttpHeader.TRAILER)); + } + + private void assertMetaDataResponseSame(MetaData.Response expected, MetaData.Response actual) { assertThat("Response.status", actual.getStatus(), is(expected.getStatus())); assertThat("Response.reason", actual.getReason(), is(expected.getReason())); - assertMetadataSame((MetaData)expected, (MetaData)actual); + assertMetaDataSame(expected, actual); } - private void assertMetadataSame(MetaData expected, MetaData actual) + private void assertMetaDataSame(MetaData expected, MetaData actual) { assertThat("Metadata.contentLength", actual.getContentLength(), is(expected.getContentLength())); assertThat("Metadata.version" + ".version", actual.getHttpVersion(), is(expected.getHttpVersion())); - assertHttpFieldsSame("Metadata.fields", expected.getFields(), actual.getFields()); + assertHttpFieldsSame(expected.getFields(), actual.getFields()); } - private void assertHttpFieldsSame(String msg, HttpFields expected, HttpFields actual) + private void assertHttpFieldsSame(HttpFields expected, HttpFields actual) { - assertThat(msg + ".size", actual.size(), is(expected.size())); + assertThat("metaData.fields.size", actual.size(), is(expected.size())); for (HttpField actualField : actual) { @@ -203,7 +255,7 @@ public class HpackTest // during testing. continue; } - assertThat(msg + ".contains(" + actualField + ")", expected.contains(actualField), is(true)); + assertThat("metaData.fields.contains(" + actualField + ")", expected.contains(actualField), is(true)); } } } From 13413c8027cf139fc939d48b9de2d5248962f64d Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 9 Sep 2019 17:58:06 -0500 Subject: [PATCH 035/113] More testing of embedded examples Signed-off-by: Joakim Erdfelt --- .../embedded/OneServletContextJmxStats.java | 11 +- .../OneServletContextWithSession.java | 19 +++- .../org/eclipse/jetty/embedded/OneWebApp.java | 18 ++-- .../jetty/embedded/OneWebAppWithJsp.java | 52 +++++---- .../eclipse/jetty/embedded/ProxyServer.java | 14 ++- .../eclipse/jetty/embedded/RewriteServer.java | 21 ++-- .../jetty/embedded/SecuredHelloHandler.java | 24 +++-- .../jetty/embedded/ServerWithAnnotations.java | 22 +++- .../eclipse/jetty/embedded/ServerWithJMX.java | 17 ++- .../jetty/embedded/ServerWithJNDI.java | 27 +++-- .../jetty/embedded/SimplestServer.java | 12 ++- .../jetty/embedded/SplitFileServer.java | 46 ++++---- .../jetty/embedded/WebSocketJsrServer.java | 15 ++- .../jetty/embedded/WebSocketServer.java | 17 +-- .../OneServletContextJmxStatsTest.java | 97 +++++++++++++++++ .../OneServletContextWithSessionTest.java | 91 ++++++++++++++++ .../eclipse/jetty/embedded/OneWebAppTest.java | 64 +++++++++++ .../jetty/embedded/OneWebAppWithJspTest.java | 102 ++++++++++++++++++ .../jetty/embedded/ProxyServerTest.java | 70 ++++++++++++ .../jetty/embedded/RewriteServerTest.java | 78 ++++++++++++++ .../embedded/SecuredHelloHandlerTest.java | 78 ++++++++++++++ .../embedded/ServerWithAnnotationsTest.java | 66 ++++++++++++ .../jetty/embedded/ServerWithJMXTest.java | 62 +++++++++++ .../jetty/embedded/ServerWithJNDITest.java | 73 +++++++++++++ .../jetty/embedded/SimplestServerTest.java | 56 ++++++++++ .../jetty/embedded/SplitFileServerTest.java | 95 ++++++++++++++++ .../java/com/acme/test/AnnotationTest.java | 6 +- 27 files changed, 1147 insertions(+), 106 deletions(-) create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextJmxStatsTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextWithSessionTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/ProxyServerTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/RewriteServerTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/SecuredHelloHandlerTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJMXTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/SimplestServerTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/SplitFileServerTest.java diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextJmxStats.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextJmxStats.java index 557c4588b65..6179716d3e3 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextJmxStats.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextJmxStats.java @@ -28,9 +28,10 @@ import org.eclipse.jetty.servlet.ServletContextHandler; public class OneServletContextJmxStats { - public static void main(String[] args) throws Exception + public static Server createServer(int port) { - Server server = new Server(8080); + Server server = new Server(port); + // Add JMX tracking to Server server.addBean(new MBeanContainer(ManagementFactory .getPlatformMBeanServer())); @@ -45,6 +46,12 @@ public class OneServletContextJmxStats // Add Connector Statistics tracking to all connectors ServerConnectionStatistics.addToAllConnectors(server); + return server; + } + + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); server.start(); server.join(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextWithSession.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextWithSession.java index 35d18c2d848..b2af0ebf2d2 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextWithSession.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextWithSession.java @@ -18,24 +18,29 @@ package org.eclipse.jetty.embedded; +import java.nio.file.Path; +import java.nio.file.Paths; + import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.session.DefaultSessionCache; import org.eclipse.jetty.server.session.NullSessionDataStore; import org.eclipse.jetty.server.session.SessionCache; import org.eclipse.jetty.server.session.SessionHandler; import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.util.resource.PathResource; +import org.eclipse.jetty.util.resource.Resource; public class OneServletContextWithSession { - public static void main(String[] args) throws Exception + public static Server createServer(int port, Resource baseResource) { - Server server = new Server(8080); + Server server = new Server(port); // Create a ServletContext, with a session handler enabled. ServletContextHandler context = new ServletContextHandler( ServletContextHandler.SESSIONS); context.setContextPath("/"); - context.setResourceBase(System.getProperty("java.io.tmpdir")); + context.setBaseResource(baseResource); server.setHandler(context); // Access the SessionHandler from the context. @@ -55,6 +60,14 @@ public class OneServletContextWithSession // Servlet to read/set the greeting stored in the session. // Can be accessed using http://localhost:8080/hello context.addServlet(HelloSessionServlet.class, "/"); + return server; + } + + public static void main(String[] args) throws Exception + { + Path dir = Paths.get(System.getProperty("user.dir")); + PathResource baseResource = new PathResource(dir); + Server server = createServer(8080, baseResource); server.start(); server.dumpStdErr(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java index 720bdaa3494..f5c229732b3 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java @@ -19,26 +19,19 @@ package org.eclipse.jetty.embedded; import java.io.File; -import java.lang.management.ManagementFactory; -import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.WebAppContext; public class OneWebApp { - public static void main(String[] args) throws Exception + public static Server createServer(int port) { // Create a basic jetty server object that will listen on port 8080. // Note that if you set this to port 0 then a randomly available port // will be assigned that you can either look in the logs for the port, // or programmatically obtain it for use in test cases. - Server server = new Server(8080); - - // Setup JMX - MBeanContainer mbContainer = new MBeanContainer( - ManagementFactory.getPlatformMBeanServer()); - server.addBean(mbContainer); + Server server = new Server(port); // The WebAppContext is the entity that controls the environment in // which a web application lives and breathes. In this example the @@ -55,6 +48,12 @@ public class OneWebApp // A WebAppContext is a ContextHandler as well so it needs to be set to // the server so it is aware of where to send the appropriate requests. server.setHandler(webapp); + return server; + } + + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); // Start things up! server.start(); @@ -63,7 +62,6 @@ public class OneWebApp // The use of server.join() the will make the current thread join and // wait until the server is done executing. - // See http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join() server.join(); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java index bb9348e62ca..66ea025650c 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java @@ -18,49 +18,45 @@ package org.eclipse.jetty.embedded; -import java.io.File; -import java.lang.management.ManagementFactory; +import java.io.FileNotFoundException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; -import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.PathResource; import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.WebAppContext; public class OneWebAppWithJsp { - public static void main(String[] args) throws Exception + public static Server createServer(int port) throws FileNotFoundException { // Create a basic jetty server object that will listen on port 8080. // Note that if you set this to port 0 then // a randomly available port will be assigned that you can either look // in the logs for the port, // or programmatically obtain it for use in test cases. - Server server = new Server(8080); - - // Setup JMX - MBeanContainer mbContainer = new MBeanContainer( - ManagementFactory.getPlatformMBeanServer()); - server.addBean(mbContainer); + Server server = new Server(port); // The WebAppContext is the entity that controls the environment in - // which a web application lives and - // breathes. In this example the context path is being set to "/" so it + // which a web application lives and breathes. + // In this example the context path is being set to "/" so it // is suitable for serving root context - // requests and then we see it setting the location of the war. A whole - // host of other configurations are + // requests and then we see it setting the location of the war. + // A whole host of other configurations are // available, ranging from configuring to support annotation scanning in - // the webapp (through - // PlusConfiguration) to choosing where the webapp will unpack itself. + // the webapp (through PlusConfiguration), to choosing where + // the webapp will unpack itself. WebAppContext webapp = new WebAppContext(); webapp.setContextPath("/"); - File warFile = new File( - "jetty-distribution/target/distribution/demo-base/webapps/test.war"); - if (!warFile.exists()) + Path warFile = JettyDistribution.resolve("demo-base/webapps/test.war"); + if (!Files.exists(warFile)) { - throw new RuntimeException("Unable to find WAR File: " + warFile.getAbsolutePath()); + throw new FileNotFoundException(warFile.toString()); } - webapp.setWar(warFile.getAbsolutePath()); + webapp.setWarResource(new PathResource(warFile)); webapp.setExtractWAR(true); // This webapp will use jsps and jstl. We need to enable the @@ -93,11 +89,22 @@ public class OneWebAppWithJsp // its own we register it as a bean with the Jetty server object so it // can be started and stopped according to the lifecycle of the server // itself. + URL realmProps = OneWebAppWithJsp.class.getClassLoader().getResource("realm.properties"); + if (realmProps == null) + throw new FileNotFoundException("Unable to find realm.properties"); + HashLoginService loginService = new HashLoginService(); loginService.setName("Test Realm"); - loginService.setConfig("examples/embedded/src/test/resources/realm.properties"); + loginService.setConfig(realmProps.toExternalForm()); server.addBean(loginService); + return server; + } + + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); + // Start things up! server.start(); @@ -105,7 +112,6 @@ public class OneWebAppWithJsp // The use of server.join() the will make the current thread join and // wait until the server is done executing. - // See http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join() server.join(); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ProxyServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ProxyServer.java index ff3412f6cb3..265e2dda38f 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ProxyServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ProxyServer.java @@ -27,11 +27,13 @@ import org.eclipse.jetty.servlet.ServletHolder; public class ProxyServer { - public static void main(String[] args) throws Exception + public static Server createServer(int port) { Server server = new Server(); + + // Establish listening connector ServerConnector connector = new ServerConnector(server); - connector.setPort(8888); + connector.setPort(port); server.addConnector(connector); // Setup proxy handler to handle CONNECT methods @@ -45,6 +47,14 @@ public class ProxyServer proxyServlet.setInitParameter("blackList", "www.eclipse.org"); context.addServlet(proxyServlet, "/*"); + return server; + } + + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); + server.start(); + server.join(); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/RewriteServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/RewriteServer.java index a74427f0ffd..47682203643 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/RewriteServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/RewriteServer.java @@ -18,27 +18,29 @@ package org.eclipse.jetty.embedded; +import java.util.Arrays; + import org.eclipse.jetty.rewrite.RewriteCustomizer; import org.eclipse.jetty.rewrite.handler.CompactPathRule; import org.eclipse.jetty.rewrite.handler.RewriteRegexRule; -import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; public class RewriteServer { - public static void main(String[] args) throws Exception + public static Server createServer(int port) { - Server server = new Server(8080); - - HttpConfiguration config = server.getConnectors()[0].getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration(); + Server server = new Server(port); RewriteCustomizer rewrite = new RewriteCustomizer(); - config.addCustomizer(rewrite); rewrite.addRule(new CompactPathRule()); rewrite.addRule(new RewriteRegexRule("(.*)foo(.*)", "$1FOO$2")); + Arrays.stream(server.getConnectors()) + .forEach((connector) -> connector.getConnectionFactory(HttpConnectionFactory.class) + .getHttpConfiguration().addCustomizer(rewrite)); + ServletContextHandler context = new ServletContextHandler( ServletContextHandler.SESSIONS); context.setContextPath("/"); @@ -46,6 +48,13 @@ public class RewriteServer context.addServlet(DumpServlet.class, "/*"); + return server; + } + + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); + server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java index aaa0c806a5e..545f6249ed4 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java @@ -18,6 +18,8 @@ package org.eclipse.jetty.embedded; +import java.io.FileNotFoundException; +import java.net.URL; import java.util.Collections; import org.eclipse.jetty.security.ConstraintMapping; @@ -30,13 +32,13 @@ import org.eclipse.jetty.util.security.Constraint; public class SecuredHelloHandler { - public static void main(String[] args) throws Exception + public static Server createServer(int port) throws FileNotFoundException { // Create a basic jetty server object that will listen on port 8080. // Note that if you set this to port 0 then a randomly available port // will be assigned that you can either look in the logs for the port, // or programmatically obtain it for use in test cases. - Server server = new Server(8080); + Server server = new Server(port); // Since this example is for our test webapp, we need to setup a // LoginService so this shows how to create a very simple hashmap based @@ -46,8 +48,13 @@ public class SecuredHelloHandler // started and stopped according to the lifecycle of the server itself. // In this example the name can be whatever you like since we are not // dealing with webapp realms. + ClassLoader classLoader = SecuredHelloHandler.class.getClassLoader(); + URL realmProps = classLoader.getResource("realm.properties"); + if (realmProps == null) + throw new FileNotFoundException("Unable to find realm.properties"); + LoginService loginService = new HashLoginService("MyRealm", - "src/test/resources/realm.properties"); + realmProps.toExternalForm()); server.addBean(loginService); // A security handler is a jetty handler that secures content behind a @@ -68,7 +75,7 @@ public class SecuredHelloHandler constraint.setRoles(new String[]{"user", "admin"}); // Binds a url pattern with the previously created constraint. The roles - // for this constraing mapping are mined from the Constraint itself + // for this constraint mapping are mined from the Constraint itself // although methods exist to declare and bind roles separately as well. ConstraintMapping mapping = new ConstraintMapping(); mapping.setPathSpec("/*"); @@ -92,13 +99,18 @@ public class SecuredHelloHandler // chain the hello handler into the security handler security.setHandler(hh); + return server; + } + + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); + // Start things up! server.start(); // The use of server.join() the will make the current thread join and // wait until the server is done executing. - // See - // http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join() server.join(); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithAnnotations.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithAnnotations.java index 42dda96c7a0..39c03e5b8d6 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithAnnotations.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithAnnotations.java @@ -19,6 +19,9 @@ package org.eclipse.jetty.embedded; import java.io.File; +import java.io.FileNotFoundException; +import java.net.URL; +import javax.naming.NamingException; import org.eclipse.jetty.plus.jndi.EnvEntry; import org.eclipse.jetty.plus.jndi.NamingDump; @@ -34,10 +37,10 @@ import org.eclipse.jetty.webapp.WebAppContext; */ public class ServerWithAnnotations { - public static final void main(String[] args) throws Exception + public static Server createServer(int port) throws NamingException, FileNotFoundException { // Create the server - Server server = new Server(8080); + Server server = new Server(port); // Enable parsing of jndi-related parts of web.xml and jetty-env.xml Configuration.ClassList classlist = Configuration.ClassList @@ -63,7 +66,7 @@ public class ServerWithAnnotations new Transaction(new com.acme.MockUserTransaction()); // Define an env entry with webapp scope. - // THIS ENTRY IS OVERRIDEN BY THE ENTRY IN jetty-env.xml + // THIS ENTRY IS OVERRIDDEN BY THE ENTRY IN jetty-env.xml new EnvEntry(webapp, "maxAmount", 100d, true); // Register a mock DataSource scoped to the webapp @@ -73,10 +76,21 @@ public class ServerWithAnnotations server.addBean(new NamingDump()); // Configure a LoginService + ClassLoader classLoader = ServerWithAnnotations.class.getClassLoader(); + URL realmProps = classLoader.getResource("realm.properties"); + if (realmProps == null) + throw new FileNotFoundException("Unable to find realm.properties"); + HashLoginService loginService = new HashLoginService(); loginService.setName("Test Realm"); - loginService.setConfig("examples/embedded/src/test/resources/realm.properties"); + loginService.setConfig(realmProps.toExternalForm()); server.addBean(loginService); + return server; + } + + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); server.start(); server.dumpStdErr(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJMX.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJMX.java index 95b38e56a18..c2ab812f691 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJMX.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJMX.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.embedded; import java.lang.management.ManagementFactory; +import java.net.MalformedURLException; import javax.management.remote.JMXServiceURL; import org.eclipse.jetty.jmx.ConnectorServer; @@ -26,17 +27,16 @@ import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.server.Server; /** - * The simplest possible Jetty server. + * A Jetty Server with JMX enabled for remote connections */ public class ServerWithJMX { - public static void main(String[] args) throws Exception + public static Server createServer(int port) throws MalformedURLException { - // === jetty-jmx.xml === + Server server = new Server(port); + MBeanContainer mbContainer = new MBeanContainer( ManagementFactory.getPlatformMBeanServer()); - - Server server = new Server(8080); server.addBean(mbContainer); ConnectorServer jmx = new ConnectorServer( @@ -48,6 +48,13 @@ public class ServerWithJMX "org.eclipse.jetty.jmx:name=rmiconnectorserver"); server.addBean(jmx); + return server; + } + + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); + server.start(); server.dumpStdErr(); server.join(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJNDI.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJNDI.java index 59ec7075115..7539690718a 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJNDI.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJNDI.java @@ -18,10 +18,12 @@ package org.eclipse.jetty.embedded; -import java.io.File; +import java.nio.file.Path; import java.util.Properties; +import javax.naming.NamingException; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.PathResource; import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.WebAppContext; @@ -30,11 +32,10 @@ import org.eclipse.jetty.webapp.WebAppContext; */ public class ServerWithJNDI { - public static void main(String[] args) throws Exception + public static Server createServer(int port) throws NamingException { - // Create the server - Server server = new Server(8080); + Server server = new Server(port); // Enable parsing of jndi-related parts of web.xml and jetty-env.xml Configuration.ClassList classlist = Configuration.ClassList @@ -46,9 +47,8 @@ public class ServerWithJNDI // Create a WebApp WebAppContext webapp = new WebAppContext(); webapp.setContextPath("/"); - File warFile = new File( - "../../jetty-distribution/target/distribution/demo-base/webapps/test-jndi.war"); - webapp.setWar(warFile.getAbsolutePath()); + Path testJndiWar = JettyDistribution.resolve("demo-base/webapps/test-jndi.war"); + webapp.setWarResource(new PathResource(testJndiWar)); server.setHandler(webapp); // Register new transaction manager in JNDI @@ -64,7 +64,7 @@ public class ServerWithJNDI // java.lang.Integer // 4000 // - new org.eclipse.jetty.plus.jndi.EnvEntry(server, "woggle", new Integer(4000), false); + new org.eclipse.jetty.plus.jndi.EnvEntry(server, "woggle", 4000, false); // Define an env entry with webapp scope. // At runtime, the webapp accesses this as java:comp/env/wiggle @@ -77,7 +77,7 @@ public class ServerWithJNDI // Note that the last arg of "true" means that this definition for // "wiggle" would override an entry of the // same name in web.xml - new org.eclipse.jetty.plus.jndi.EnvEntry(webapp, "wiggle", new Double(100), true); + new org.eclipse.jetty.plus.jndi.EnvEntry(webapp, "wiggle", 100d, true); // Register a reference to a mail service scoped to the webapp. // This must be linked to the webapp by an entry in web.xml: @@ -87,7 +87,8 @@ public class ServerWithJNDI // Container // // At runtime the webapp accesses this as java:comp/env/mail/Session - org.eclipse.jetty.jndi.factories.MailSessionReference mailref = new org.eclipse.jetty.jndi.factories.MailSessionReference(); + org.eclipse.jetty.jndi.factories.MailSessionReference mailref = + new org.eclipse.jetty.jndi.factories.MailSessionReference(); mailref.setUser("CHANGE-ME"); mailref.setPassword("CHANGE-ME"); Properties props = new Properties(); @@ -109,6 +110,12 @@ public class ServerWithJNDI // java:comp/env/jdbc/mydatasource new org.eclipse.jetty.plus.jndi.Resource( webapp, "jdbc/mydatasource", new com.acme.MockDataSource()); + return server; + } + + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); server.start(); server.join(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SimplestServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SimplestServer.java index a48235c55d5..1ba0293f2ac 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SimplestServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SimplestServer.java @@ -25,11 +25,19 @@ import org.eclipse.jetty.server.Server; */ public class SimplestServer { + public static Server createServer(int port) + { + Server server = new Server(port); + // This has a connector listening on port specified + // and no handlers, meaning all requests will result + // in a 404 response + return server; + } + public static void main(String[] args) throws Exception { - Server server = new Server(8080); + Server server = createServer(8080); server.start(); - server.dumpStdErr(); server.join(); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SplitFileServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SplitFileServer.java index 4a681baa93e..b4abb97afd0 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SplitFileServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SplitFileServer.java @@ -18,16 +18,14 @@ package org.eclipse.jetty.embedded; -import java.io.File; +import java.nio.file.Paths; -import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.ResourceHandler; -import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.resource.PathResource; import org.eclipse.jetty.util.resource.Resource; /** @@ -37,59 +35,67 @@ import org.eclipse.jetty.util.resource.Resource; */ public class SplitFileServer { - public static void main(String[] args) throws Exception + public static Server createServer(int port, Resource baseResource0, Resource baseResource1) { // Create the Server object and a corresponding ServerConnector and then // set the port for the connector. In this example the server will - // listen on port 8090. If you set this to port 0 then when the server + // listen on port 8080. If you set this to port 0 then when the server // has been started you can called connector.getLocalPort() to // programmatically get the port the server started on. Server server = new Server(); ServerConnector connector = new ServerConnector(server); - connector.setPort(8090); - server.setConnectors(new Connector[]{connector}); + connector.setPort(port); + server.addConnector(connector); // Create a Context Handler and ResourceHandler. The ContextHandler is // getting set to "/" path but this could be anything you like for - // builing out your url. Note how we are setting the ResourceBase using + // building out your url. Note how we are setting the ResourceBase using // our jetty maven testing utilities to get the proper resource // directory, you needn't use these, you simply need to supply the paths // you are looking to serve content from. ResourceHandler rh0 = new ResourceHandler(); + rh0.setDirectoriesListed(false); ContextHandler context0 = new ContextHandler(); context0.setContextPath("/"); - File dir0 = MavenTestingUtils.getTestResourceDir("dir0"); - context0.setBaseResource(Resource.newResource(dir0)); + context0.setBaseResource(baseResource0); context0.setHandler(rh0); // Rinse and repeat the previous item, only specifying a different // resource base. ResourceHandler rh1 = new ResourceHandler(); + rh1.setDirectoriesListed(false); ContextHandler context1 = new ContextHandler(); context1.setContextPath("/"); - File dir1 = MavenTestingUtils.getTestResourceDir("dir1"); - context1.setBaseResource(Resource.newResource(dir1)); + context1.setBaseResource(baseResource1); context1.setHandler(rh1); // Create a ContextHandlerCollection and set the context handlers to it. // This will let jetty process urls against the declared contexts in // order to match up content. - ContextHandlerCollection contexts = new ContextHandlerCollection(); - contexts.setHandlers(new Handler[]{context0, context1}); - + ContextHandlerCollection contexts = new ContextHandlerCollection( + context0, context1 + ); server.setHandler(contexts); + return server; + } - // Start things up! - server.start(); + public static void main(String[] args) throws Exception + { + Resource resource0 = new PathResource(Paths.get("src/test/resources/dir0")); + Resource resource1 = new PathResource(Paths.get("src/test/resources/dir1")); + + Server server = createServer(8080, resource0, resource1); // Dump the server state - System.out.println(server.dump()); + server.setDumpAfterStart(true); + + // Start things up! + server.start(); // The use of server.join() the will make the current thread join and // wait until the server is done executing. - // See http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join() server.join(); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java index c880bf229ad..19da249f39a 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java @@ -18,6 +18,8 @@ package org.eclipse.jetty.embedded; +import javax.servlet.ServletException; +import javax.websocket.DeploymentException; import javax.websocket.OnMessage; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; @@ -47,9 +49,9 @@ public class WebSocketJsrServer } } - public static void main(String[] args) throws Exception + public static Server createServer(int port) throws DeploymentException, ServletException { - Server server = new Server(8080); + Server server = new Server(port); HandlerList handlers = new HandlerList(); @@ -68,8 +70,15 @@ public class WebSocketJsrServer handlers.addHandler(new DefaultHandler()); server.setHandler(handlers); + + return server; + } + + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); + server.start(); - context.dumpStdErr(); server.join(); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketServer.java index ea92c922a17..25f7bf45a77 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketServer.java @@ -20,7 +20,6 @@ package org.eclipse.jetty.embedded; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.WebSocket; @@ -61,20 +60,24 @@ public class WebSocketServer } } - public static void main(String[] args) throws Exception + public static Server createServer(int port) { - Server server = new Server(8080); + Server server = new Server(port); - ServletContextHandler context = new ServletContextHandler( - ServletContextHandler.SESSIONS); + ServletContextHandler context = new ServletContextHandler(); context.setContextPath("/"); server.setHandler(context); // Add the echo socket servlet to the /echo path map - context.addServlet(new ServletHolder(EchoServlet.class), "/echo"); + context.addServlet(EchoServlet.class, "/echo"); + return server; + } + + public static void main(String[] args) throws Exception + { + Server server = createServer(8080); server.start(); - context.dumpStdErr(); server.join(); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextJmxStatsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextJmxStatsTest.java new file mode 100644 index 00000000000..471b7cbb160 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextJmxStatsTest.java @@ -0,0 +1,97 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.util.Set; +import javax.management.MBeanServer; +import javax.management.ObjectInstance; +import javax.management.ObjectName; + +import org.eclipse.jetty.io.ConnectionStatistics; +import org.eclipse.jetty.jmx.MBeanContainer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + +@ExtendWith(WorkDirExtension.class) +public class OneServletContextJmxStatsTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = OneServletContextJmxStats.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetDumpViaPathInfo() throws IOException + { + URI uri = server.getURI().resolve("/dump/something"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, + allOf( + containsString("DumpServlet"), + containsString("servletPath=/dump"), + containsString("pathInfo=/something") + ) + ); + } + + @Test + public void testJmxConnectStatsPresent() throws Exception + { + MBeanContainer mbeanContainer = server.getBean(MBeanContainer.class); + MBeanServer mbeanServer = mbeanContainer.getMBeanServer(); + + String domain = ConnectionStatistics.class.getPackage().getName(); + Set mbeanNames = mbeanServer.queryNames(ObjectName.getInstance(domain + ":type=connectionstatistics,*"), null); + ObjectName connStatsName = mbeanNames.stream().findFirst().get(); + ObjectInstance mbeanConnStats = mbeanServer.getObjectInstance(connStatsName); + Number connections = (Number)mbeanServer.getAttribute(connStatsName, "connections"); + assertThat("stats[connections]", connections, is(notNullValue())); + assertThat("stats[connections]", connections.longValue(), greaterThanOrEqualTo(0L)); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextWithSessionTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextWithSessionTest.java new file mode 100644 index 00000000000..a2c81f2e49a --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextWithSessionTest.java @@ -0,0 +1,91 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.resource.PathResource; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +@ExtendWith(WorkDirExtension.class) +public class OneServletContextWithSessionTest +{ + private static final String TEXT_CONTENT = "Do the right thing. It will gratify some people and astonish the rest. - Mark Twain"; + public WorkDir workDir; + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + Path baseDir = workDir.getEmptyPathDir(); + + Path textFile = baseDir.resolve("simple.txt"); + try (BufferedWriter writer = Files.newBufferedWriter(textFile, UTF_8)) + { + writer.write(TEXT_CONTENT); + } + + server = OneServletContextWithSession.createServer(0, new PathResource(baseDir)); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws IOException + { + URI uri = server.getURI().resolve("/"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + String setCookieValue = http.getHeaderField("Set-Cookie"); + assertThat("Set-Cookie value", setCookieValue, containsString("JSESSIONID=")); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, + allOf( + containsString("session.getId() = "), + containsString("session.isNew() = true") + ) + ); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java new file mode 100644 index 00000000000..fb9491d799e --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java @@ -0,0 +1,64 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class OneWebAppTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = OneWebApp.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetAsyncRest() throws IOException + { + URI uri = server.getURI().resolve("/testAsync?items=mouse,beer,gnome"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Asynchronous: mouse,beer,gnome")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java new file mode 100644 index 00000000000..64502de287f --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java @@ -0,0 +1,102 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class OneWebAppWithJspTest +{ + private Server server; + private URI serverLocalUri; + + @BeforeEach + public void startServer() throws Exception + { + server = OneWebAppWithJsp.createServer(0); + server.start(); + + // Use URI based on "localhost" to get past "REMOTE ACCESS!" protection of demo war + serverLocalUri = URI.create("http://localhost:" + server.getURI().getPort() + "/"); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetDumpInfo() throws IOException + { + URI uri = serverLocalUri.resolve("/dump/info"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("getProtocol: HTTP/1.1")); + } + + @Test + public void testGetJspExpr() throws IOException + { + URI uri = serverLocalUri.resolve("/jsp/expr.jsp?A=1"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + String userAgent = OneWebAppWithJspTest.class.getSimpleName(); + http.setRequestProperty("User-Agent", userAgent); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("" + userAgent + "")); + } + + @Test + public void testGetJstlExpr() throws IOException + { + URI uri = serverLocalUri.resolve("/jsp/jstl.jsp"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("

    JSTL Example

    ")); + for (int i = 1; i <= 10; i++) + { + assertThat("Reponse content (counting)", responseBody, containsString("" + i)); + } + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ProxyServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ProxyServerTest.java new file mode 100644 index 00000000000..1e59560abb0 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ProxyServerTest.java @@ -0,0 +1,70 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.URI; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ProxyServerTest +{ + private Server server; + private Proxy javaHttpProxy; + + @BeforeEach + public void startServer() throws Exception + { + server = ProxyServer.createServer(0); + server.start(); + + URI uri = server.getURI(); + javaHttpProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(uri.getHost(), uri.getPort())); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetProxiedRFC() throws IOException + { + URI uri = URI.create("https://tools.ietf.org/rfc/rfc7230.txt"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(javaHttpProxy); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/RewriteServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/RewriteServerTest.java new file mode 100644 index 00000000000..c1bcb8392bc --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/RewriteServerTest.java @@ -0,0 +1,78 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class RewriteServerTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = RewriteServer.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetRewriteFooInName() throws IOException + { + URI destUri = server.getURI().resolve("/do-be-foo-be-do"); + HttpURLConnection http = (HttpURLConnection)destUri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("requestURI=/do-be-FOO-be-do")); + } + + @Test + public void testGetRewriteFooInPath() throws IOException + { + URI destUri = server.getURI().resolve("/do/be/foo/be/do.it"); + HttpURLConnection http = (HttpURLConnection)destUri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("requestURI=/do/be/FOO/be/do.it")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SecuredHelloHandlerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SecuredHelloHandlerTest.java new file mode 100644 index 00000000000..9658dfcf9b6 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SecuredHelloHandlerTest.java @@ -0,0 +1,78 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.util.Base64; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class SecuredHelloHandlerTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = SecuredHelloHandler.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetWithoutAuth() throws IOException + { + URI destUri = server.getURI().resolve("/hello"); + HttpURLConnection http = (HttpURLConnection)destUri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_UNAUTHORIZED)); + + // HttpUtil.dumpResponseHeaders(http); + } + + @Test + public void testGetWithAuth() throws IOException + { + URI destUri = server.getURI().resolve("/hello"); + HttpURLConnection http = (HttpURLConnection)destUri.toURL().openConnection(); + String authEncoded = Base64.getEncoder().encodeToString("user:password".getBytes(UTF_8)); + http.setRequestProperty("Authorization", "Basic " + authEncoded); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("

    Hello World

    ")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java new file mode 100644 index 00000000000..ca812e86323 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java @@ -0,0 +1,66 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; + +public class ServerWithAnnotationsTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ServerWithAnnotations.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetTest() throws IOException + { + URI destUri = server.getURI().resolve("/test"); + HttpURLConnection http = (HttpURLConnection)destUri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("maxAmount=55.0")); + assertThat("Response Content", responseBody, not(containsString(""))); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJMXTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJMXTest.java new file mode 100644 index 00000000000..7094f4413c8 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJMXTest.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.util.Optional; +import java.util.Set; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.eclipse.jetty.jmx.MBeanContainer; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ServerWithJMXTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ServerWithJMX.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetTest() throws Exception + { + MBeanContainer mbeanContainer = server.getBean(MBeanContainer.class); + MBeanServer mbeanServer = mbeanContainer.getMBeanServer(); + + String name = "org.eclipse.jetty.jmx:name=rmiconnectorserver,*"; + Set mbeanNames = mbeanServer.queryNames(ObjectName.getInstance(name), null); + Optional rmiConnectorNameOptional = mbeanNames.stream().findFirst(); + assertTrue(rmiConnectorNameOptional.isPresent(), "Has RMI Connector Server"); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java new file mode 100644 index 00000000000..d1f1f2ace17 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java @@ -0,0 +1,73 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.net.HttpURLConnection; +import java.net.URI; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; + +public class ServerWithJNDITest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ServerWithJNDI.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetTest() throws Exception + { + URI destUri = server.getURI().resolve("/test"); + HttpURLConnection http = (HttpURLConnection)destUri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, + allOf( + containsString("java:comp/env/woggle"), + containsString("java:comp/env/gargle"), + containsString("java:comp/env/wiggle") + ) + ); + + assertThat("Response Content", responseBody, not(containsString(""))); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SimplestServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SimplestServerTest.java new file mode 100644 index 00000000000..b14fc747eb1 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SimplestServerTest.java @@ -0,0 +1,56 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.net.HttpURLConnection; +import java.net.URI; + +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class SimplestServerTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = SimplestServer.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetTest() throws Exception + { + URI destUri = server.getURI().resolve("/test"); + HttpURLConnection http = (HttpURLConnection)destUri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_NOT_FOUND)); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SplitFileServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SplitFileServerTest.java new file mode 100644 index 00000000000..e54ac8ae0af --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SplitFileServerTest.java @@ -0,0 +1,95 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.PathResource; +import org.eclipse.jetty.util.resource.Resource; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class SplitFileServerTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + Path path0 = Paths.get("src/test/resources/dir0"); + Path path1 = Paths.get("src/test/resources/dir1"); + Resource resource0 = new PathResource(path0); + Resource resource1 = new PathResource(path1); + + server = SplitFileServer.createServer(0, resource0, resource1); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetTest0() throws IOException + { + URI uri = server.getURI().resolve("/test0.txt"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("test0")); + } + + @Test + public void testGetTest1() throws IOException + { + URI uri = server.getURI().resolve("/test1.txt"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + + // HttpUtil.dumpResponseHeaders(http); + + // test response content + String responseBody = HttpUtil.getResponseBody(http); + assertThat("Response Content", responseBody, containsString("test1")); + } + + @Test + public void testGetTest2() throws IOException + { + URI uri = server.getURI().resolve("/test2.txt"); + HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); + assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_NOT_FOUND)); + } +} diff --git a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/test/AnnotationTest.java b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/test/AnnotationTest.java index f734a7f0ef9..4a028b968f0 100644 --- a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/test/AnnotationTest.java +++ b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/test/AnnotationTest.java @@ -295,19 +295,19 @@ public class AnnotationTest extends HttpServlet out.println("private Double minAmount;"); out.println(""); if (maxAmount == null) - out.println("

    Result: " + envResult + ": FAIL"); + out.println("

    Result: " + envResult + ": FAIL"); else out.println("

    Result: " + envResult + ": " + (maxAmount.compareTo(new Double(55)) == 0 ? " PASS" : " FAIL") + ""); out.println("
    JNDI Lookup Result: " + envLookupResult + ""); if (minAmount == null) - out.println("

    Result: " + envResult2 + ": FAIL"); + out.println("

    Result: " + envResult2 + ": FAIL"); else out.println("
    Result: " + envResult2 + ": " + (minAmount.compareTo(new Double("0.99")) == 0 ? " PASS" : " FAIL") + ""); out.println("
    JNDI Lookup Result: " + envLookupResult2 + ""); if (avgAmount == null) - out.println("

    Result: " + envResult3 + ": FAIL"); + out.println("

    Result: " + envResult3 + ": FAIL"); else out.println("
    Result: " + envResult3 + ": " + (avgAmount.compareTo(new Double("1.25")) == 0 ? " PASS" : " FAIL") + ""); out.println("
    JNDI Lookup Result: " + envLookupResult3 + "

    "); From 9ef9b63071b8c8fa0fbaf41525e33693570ab3ef Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 9 Sep 2019 19:06:13 -0500 Subject: [PATCH 036/113] More testing of embedded examples Signed-off-by: Joakim Erdfelt --- examples/embedded/pom.xml | 7 +- ...TestXml.java => AbstractEmbeddedTest.java} | 31 ++++- .../jetty/embedded/ExampleServerTest.java | 49 +++---- .../jetty/embedded/ExampleServerXmlTest.java | 19 +-- .../jetty/embedded/FastFileServerTest.java | 27 ++-- .../jetty/embedded/FileServerTest.java | 27 ++-- .../jetty/embedded/FileServerXmlTest.java | 27 ++-- .../org/eclipse/jetty/embedded/HttpUtil.java | 125 ------------------ .../eclipse/jetty/embedded/JarServerTest.java | 31 +++-- .../jetty/embedded/LikeJettyXmlTest.java | 37 ++++-- .../jetty/embedded/ManyConnectorsTest.java | 37 +++--- .../jetty/embedded/ManyContextsTest.java | 55 ++++---- .../jetty/embedded/ManyHandlersTest.java | 47 ++++--- .../embedded/ManyServletContextsTest.java | 55 ++++---- .../jetty/embedded/MinimalServletsTest.java | 19 +-- .../jetty/embedded/OneConnectorTest.java | 19 +-- .../jetty/embedded/OneContextTest.java | 19 +-- .../jetty/embedded/OneHandlerTest.java | 19 +-- .../OneServletContextJmxStatsTest.java | 22 +-- .../jetty/embedded/OneServletContextTest.java | 59 +++++---- .../OneServletContextWithSessionTest.java | 22 +-- .../eclipse/jetty/embedded/OneWebAppTest.java | 19 +-- .../jetty/embedded/OneWebAppWithJspTest.java | 46 ++++--- .../jetty/embedded/ProxyServerTest.java | 26 ++-- .../jetty/embedded/RewriteServerTest.java | 35 ++--- .../embedded/SecuredHelloHandlerTest.java | 39 +++--- .../embedded/ServerWithAnnotationsTest.java | 18 +-- .../jetty/embedded/ServerWithJMXTest.java | 2 +- .../jetty/embedded/ServerWithJNDITest.java | 15 ++- .../jetty/embedded/SimplestServerTest.java | 11 +- .../jetty/embedded/SplitFileServerTest.java | 32 ++--- .../embedded/WebSocketJsrServerTest.java | 110 +++++++++++++++ .../jetty/embedded/WebSocketServerTest.java | 110 +++++++++++++++ .../test/resources/jetty-logging.properties | 10 ++ 34 files changed, 739 insertions(+), 487 deletions(-) rename examples/embedded/src/test/java/org/eclipse/jetty/embedded/{TestXml.java => AbstractEmbeddedTest.java} (50%) delete mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/HttpUtil.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/WebSocketJsrServerTest.java create mode 100644 examples/embedded/src/test/java/org/eclipse/jetty/embedded/WebSocketServerTest.java create mode 100644 examples/embedded/src/test/resources/jetty-logging.properties diff --git a/examples/embedded/pom.xml b/examples/embedded/pom.xml index fe409025f30..881462073b3 100644 --- a/examples/embedded/pom.xml +++ b/examples/embedded/pom.xml @@ -132,7 +132,12 @@ org.eclipse.jetty.toolchain jetty-test-helper - + test + + + org.eclipse.jetty.websocket + websocket-client + ${project.version} org.eclipse.jetty diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/TestXml.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/AbstractEmbeddedTest.java similarity index 50% rename from examples/embedded/src/test/java/org/eclipse/jetty/embedded/TestXml.java rename to examples/embedded/src/test/java/org/eclipse/jetty/embedded/AbstractEmbeddedTest.java index bb45cf6c124..9187fe17611 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/TestXml.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/AbstractEmbeddedTest.java @@ -18,14 +18,33 @@ package org.eclipse.jetty.embedded; -import org.eclipse.jetty.xml.XmlConfiguration; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; -public class TestXml +public abstract class AbstractEmbeddedTest { - public static void main(String[] args) throws Exception + public HttpClient client; + + @BeforeEach + public void startClient() throws Exception { - System.setProperty("jetty.home", "../jetty-distribution/target/distribution"); - XmlConfiguration.main("../jetty-jmx/src/main/config/etc/jetty-jmx.xml", - "../jetty-server/src/main/config/etc/jetty.xml"); + SslContextFactory ssl = new SslContextFactory.Client(true); + client = new HttpClient(ssl); + client.start(); + } + + @AfterEach + public void stopClient() throws Exception + { + client.stop(); + } + + protected void dumpResponseHeaders(ContentResponse response) + { + System.out.printf("%s %s %s%n", response.getVersion(), response.getStatus(), response.getReason()); + System.out.println(response.getHeaders()); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerTest.java index 1f6cbb6bbbd..367b521b28e 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerTest.java @@ -18,14 +18,13 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.io.OutputStream; -import java.io.StringBufferInputStream; -import java.net.HttpURLConnection; import java.net.URI; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.util.IO; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -34,7 +33,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -public class ExampleServerTest +public class ExampleServerTest extends AbstractEmbeddedTest { private Server server; @@ -52,46 +51,40 @@ public class ExampleServerTest } @Test - public void testGetHello() throws IOException + public void testGetHello() throws Exception { URI uri = server.getURI().resolve("/hello"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); - // HttpUtil.dumpResponseHeaders(http); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Hello")); } @Test - public void testGetEcho() throws IOException + public void testGetEcho() throws Exception { URI uri = server.getURI().resolve("/echo/a/greeting"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - // Use a POST request - http.setRequestMethod("POST"); - http.setDoOutput(true); - - // The POST body content String postBody = "Greetings from " + ExampleServerTest.class; - http.setRequestProperty("Content-Type", "text/plain; charset=utf-8"); - - try (StringBufferInputStream in = new StringBufferInputStream(postBody); - OutputStream out = http.getOutputStream()) - { - IO.copy(in, out); - } + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.POST) + .content(new StringContentProvider(postBody)) + .send(); // Check the response status code - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString(postBody)); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerXmlTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerXmlTest.java index bd7b9231a04..23b28198e05 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerXmlTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerXmlTest.java @@ -18,10 +18,11 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -31,7 +32,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -public class ExampleServerXmlTest +public class ExampleServerXmlTest extends AbstractEmbeddedTest { private Server server; @@ -49,16 +50,18 @@ public class ExampleServerXmlTest } @Test - public void testGetHello() throws IOException + public void testGetHello() throws Exception { URI uri = server.getURI().resolve("/hello"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Hello")); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FastFileServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FastFileServerTest.java index 9f8962f9441..861ad3c8814 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FastFileServerTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FastFileServerTest.java @@ -19,12 +19,14 @@ package org.eclipse.jetty.embedded; import java.io.BufferedWriter; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; @@ -38,7 +40,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; @ExtendWith(WorkDirExtension.class) -public class FastFileServerTest +public class FastFileServerTest extends AbstractEmbeddedTest { private static final String TEXT_CONTENT = "I am an old man and I have known a great " + "many troubles, but most of them never happened. - Mark Twain"; @@ -67,19 +69,24 @@ public class FastFileServerTest } @Test - public void testGetSimpleText() throws IOException + public void testGetSimpleText() throws Exception { URI uri = server.getURI().resolve("/simple.txt"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); - assertThat("Content-Type", http.getHeaderField("Content-Type"), is("text/plain")); - assertThat("Content-Length", http.getHeaderField("Content-Length"), is(Integer.toString(TEXT_CONTENT.length()))); + HttpFields responseHeaders = response.getHeaders(); + + assertThat("Content-Type", responseHeaders.get("Content-Type"), is("text/plain")); + assertThat("Content-Length", responseHeaders.getLongField("Content-Length"), + is((long)TEXT_CONTENT.length())); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response body", responseBody, is(TEXT_CONTENT)); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerTest.java index 6d9276a20aa..14ac4e5a53d 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerTest.java @@ -19,12 +19,14 @@ package org.eclipse.jetty.embedded; import java.io.BufferedWriter; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; @@ -39,7 +41,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; @ExtendWith(WorkDirExtension.class) -public class FileServerTest +public class FileServerTest extends AbstractEmbeddedTest { private static final String TEXT_CONTENT = "I am an old man and I have known a great " + "many troubles, but most of them never happened. - Mark Twain"; @@ -68,19 +70,24 @@ public class FileServerTest } @Test - public void testGetSimpleText() throws IOException + public void testGetSimpleText() throws Exception { URI uri = server.getURI().resolve("/simple.txt"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); - assertThat("Content-Type", http.getHeaderField("Content-Type"), is("text/plain")); - assertThat("Content-Length", http.getHeaderField("Content-Length"), is(Integer.toString(TEXT_CONTENT.length()))); + HttpFields responseHeaders = response.getHeaders(); + + assertThat("Content-Type", responseHeaders.get("Content-Type"), is("text/plain")); + assertThat("Content-Length", responseHeaders.getLongField("Content-Length"), + is((long)TEXT_CONTENT.length())); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response body", responseBody, is(TEXT_CONTENT)); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerXmlTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerXmlTest.java index 6e2e6093655..813879e36c4 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerXmlTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerXmlTest.java @@ -19,12 +19,14 @@ package org.eclipse.jetty.embedded; import java.io.BufferedWriter; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; @@ -38,7 +40,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; @ExtendWith(WorkDirExtension.class) -public class FileServerXmlTest +public class FileServerXmlTest extends AbstractEmbeddedTest { private static final String TEXT_CONTENT = "I am an old man and I have known a great " + "many troubles, but most of them never happened. - Mark Twain"; @@ -67,19 +69,24 @@ public class FileServerXmlTest } @Test - public void testGetSimpleText() throws IOException + public void testGetSimpleText() throws Exception { URI uri = server.getURI().resolve("/simple.txt"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); - assertThat("Content-Type", http.getHeaderField("Content-Type"), is("text/plain")); - assertThat("Content-Length", http.getHeaderField("Content-Length"), is(Integer.toString(TEXT_CONTENT.length()))); + HttpFields responseHeaders = response.getHeaders(); + + assertThat("Content-Type", responseHeaders.get("Content-Type"), is("text/plain")); + assertThat("Content-Length", responseHeaders.getLongField("Content-Length"), + is((long)TEXT_CONTENT.length())); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response body", responseBody, is(TEXT_CONTENT)); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/HttpUtil.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/HttpUtil.java deleted file mode 100644 index 174d4899472..00000000000 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/HttpUtil.java +++ /dev/null @@ -1,125 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; - -import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.zip.GZIPInputStream; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSession; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; - -import org.eclipse.jetty.util.IO; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; - -public class HttpUtil -{ - public static void assertGzippedResponse(HttpURLConnection http) - { - String value = http.getHeaderField("Content-Encoding"); - assertThat("Content-Encoding", value, containsString("gzip")); - } - - public static String getGzippedResponseBody(HttpURLConnection http) throws IOException - { - try (InputStream in = http.getInputStream(); - GZIPInputStream gzipInputStream = new GZIPInputStream(in)) - { - return IO.toString(gzipInputStream, UTF_8); - } - } - - public static String getResponseBody(HttpURLConnection http) throws IOException - { - try (InputStream in = http.getInputStream()) - { - return IO.toString(in, UTF_8); - } - } - - public static void dumpResponseHeaders(HttpURLConnection http) - { - int i = 0; - while (true) - { - String field = http.getHeaderField(i); - if (field == null) - return; - String key = http.getHeaderFieldKey(i); - if (key != null) - { - System.out.printf("%s: ", key); - } - System.out.println(field); - i++; - } - } - - /** - * Disable the Hostname and Certificate verification in {@link HttpsURLConnection} - */ - public static void disableSecureConnectionVerification() throws NoSuchAlgorithmException, KeyManagementException - { - TrustManager[] trustAllCerts = new TrustManager[]{new TrustAllCerts()}; - SSLContext sc = SSLContext.getInstance("SSL"); - sc.init(null, trustAllCerts, new java.security.SecureRandom()); - HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); - HttpsURLConnection.setDefaultHostnameVerifier(new AllHostnamesValid()); - } - - public static class TrustAllCerts implements X509TrustManager - { - @Override - public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException - { - } - - @Override - public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException - { - } - - @Override - public X509Certificate[] getAcceptedIssuers() - { - return null; - } - } - - public static class AllHostnamesValid implements HostnameVerifier - { - - @Override - public boolean verify(String s, SSLSession sslSession) - { - return true; - } - } -} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/JarServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/JarServerTest.java index cf24d55b4d8..4e4bf368547 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/JarServerTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/JarServerTest.java @@ -18,10 +18,11 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -31,7 +32,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -public class JarServerTest +public class JarServerTest extends AbstractEmbeddedTest { private Server server; @@ -49,30 +50,34 @@ public class JarServerTest } @Test - public void testGetDir0Test0() throws IOException + public void testGetDir0Test0() throws Exception { URI uri = server.getURI().resolve("/dir0/test0.txt"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("test0")); } @Test - public void testGetDir1Test1() throws IOException + public void testGetDir1Test1() throws Exception { URI uri = server.getURI().resolve("/dir1/test1.txt"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("test1")); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java index 07dc253df04..6496b3abe44 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java @@ -18,11 +18,12 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; import java.util.Map; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -32,7 +33,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -public class LikeJettyXmlTest +public class LikeJettyXmlTest extends AbstractEmbeddedTest { private Server server; private URI serverPlainUri; @@ -44,8 +45,6 @@ public class LikeJettyXmlTest server = LikeJettyXml.createServer(0, 0, false); server.start(); - System.err.println("Server URI is " + server.getURI()); - Map ports = ServerUtil.fixDynamicPortConfigurations(server); // Establish base URI's that use "localhost" to prevent tripping over @@ -61,16 +60,34 @@ public class LikeJettyXmlTest } @Test - public void testGetTest() throws IOException + public void testGetTest() throws Exception { URI uri = serverPlainUri.resolve("/test/"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } + + @Test + public void testGetTestSsl() throws Exception + { + URI uri = serverSslUri.resolve("/test/"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Hello")); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyConnectorsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyConnectorsTest.java index fa822c2f7ca..0a19dac2859 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyConnectorsTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyConnectorsTest.java @@ -18,12 +18,12 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; import java.util.Map; -import javax.net.ssl.HttpsURLConnection; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -33,7 +33,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -public class ManyConnectorsTest +public class ManyConnectorsTest extends AbstractEmbeddedTest { private Server server; private URI serverPlainUri; @@ -45,8 +45,6 @@ public class ManyConnectorsTest server = ManyConnectors.createServer(0, 0); server.start(); - System.err.println("Server URI is " + server.getURI()); - Map ports = ServerUtil.fixDynamicPortConfigurations(server); // Establish base URI's that use "localhost" to prevent tripping over @@ -62,33 +60,36 @@ public class ManyConnectorsTest } @Test - public void testPlainGetHello() throws IOException + public void testPlainGetHello() throws Exception { - URI helloUri = serverPlainUri.resolve("/hello"); + URI uri = serverPlainUri.resolve("/hello"); - HttpURLConnection http = (HttpURLConnection)helloUri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Hello")); } @Test public void testSecureGetHello() throws Exception { - HttpUtil.disableSecureConnectionVerification(); - URI helloUri = serverSslUri.resolve("/hello"); + URI uri = serverSslUri.resolve("/hello"); - HttpsURLConnection https = (HttpsURLConnection)helloUri.toURL().openConnection(); - assertThat("HTTPS Response Status", https.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(https); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Hello")); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyContextsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyContextsTest.java index 9a15aa89526..bd439aee552 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyContextsTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyContextsTest.java @@ -18,10 +18,11 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -31,7 +32,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -public class ManyContextsTest +public class ManyContextsTest extends AbstractEmbeddedTest { private Server server; @@ -49,60 +50,68 @@ public class ManyContextsTest } @Test - public void testGetRootHello() throws IOException + public void testGetRootHello() throws Exception { URI uri = server.getURI().resolve("/"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Root Hello")); } @Test - public void testGetFrenchHello() throws IOException + public void testGetFrenchHello() throws Exception { URI uri = server.getURI().resolve("/fr"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Bonjour")); } @Test - public void testGetItalianGoodMorning() throws IOException + public void testGetItalianGoodMorning() throws Exception { URI uri = server.getURI().resolve("/it"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Buongiorno")); } @Test - public void testGetVirtualHostHello() throws IOException + public void testGetVirtualHostHello() throws Exception { int port = server.getURI().getPort(); URI uri = URI.create("http://127.0.0.2:" + port + "/"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Virtual Hello")); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyHandlersTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyHandlersTest.java index c33dc34fb05..a6641d7c3d7 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyHandlersTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyHandlersTest.java @@ -18,11 +18,13 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; import java.util.Map; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.ajax.JSON; import org.junit.jupiter.api.AfterEach; @@ -33,7 +35,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -public class ManyHandlersTest +public class ManyHandlersTest extends AbstractEmbeddedTest { private Server server; @@ -51,44 +53,53 @@ public class ManyHandlersTest } @Test - public void testGetParams() throws IOException + public void testGetParams() throws Exception { URI uri = server.getURI().resolve("/params?a=b&foo=bar"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - http.setRequestProperty("Accept-Encoding", "gzip"); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); - // HttpUtil.dumpResponseHeaders(http); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .header(HttpHeader.ACCEPT_ENCODING, "gzip") + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); // test gzip - HttpUtil.assertGzippedResponse(http); + // Test that Gzip was used to produce the response + String contentEncoding = response.getHeaders().get(HttpHeader.CONTENT_ENCODING); + assertThat("Content-Encoding", contentEncoding, containsString("gzip")); // test response content - String responseBody = HttpUtil.getGzippedResponseBody(http); + String responseBody = response.getContentAsString(); Object jsonObj = JSON.parse(responseBody); Map jsonMap = (Map)jsonObj; assertThat("Response JSON keys.size", jsonMap.keySet().size(), is(2)); } @Test - public void testGetHello() throws IOException + public void testGetHello() throws Exception { URI uri = server.getURI().resolve("/hello"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - http.setRequestProperty("Accept-Encoding", "gzip"); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .header(HttpHeader.ACCEPT_ENCODING, "gzip") + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test gzip - HttpUtil.assertGzippedResponse(http); + // Test that Gzip was used to produce the response + String contentEncoding = response.getHeaders().get(HttpHeader.CONTENT_ENCODING); + assertThat("Content-Encoding", contentEncoding, containsString("gzip")); // test expected header from wrapper - String welcome = http.getHeaderField("X-Welcome"); + String welcome = response.getHeaders().get("X-Welcome"); assertThat("X-Welcome header", welcome, containsString("Greetings from WelcomeWrapHandler")); // test response content - String responseBody = HttpUtil.getGzippedResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Hello")); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyServletContextsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyServletContextsTest.java index 0883388ff84..6cc21115957 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyServletContextsTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyServletContextsTest.java @@ -18,10 +18,11 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -31,7 +32,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -public class ManyServletContextsTest +public class ManyServletContextsTest extends AbstractEmbeddedTest { private Server server; @@ -49,58 +50,66 @@ public class ManyServletContextsTest } @Test - public void testGetHello() throws IOException + public void testGetHello() throws Exception { URI uri = server.getURI().resolve("/hello"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Hello")); } @Test - public void testGetItalianHello() throws IOException + public void testGetItalianHello() throws Exception { URI uri = server.getURI().resolve("/it/hello"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Ciao")); } @Test - public void testGetFrenchHello() throws IOException + public void testGetFrenchHello() throws Exception { URI uri = server.getURI().resolve("/fr/hello"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Bonjour")); } @Test - public void testGetOtherYo() throws IOException + public void testGetOtherYo() throws Exception { URI uri = server.getURI().resolve("/other/hello.yo"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("YO!")); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/MinimalServletsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/MinimalServletsTest.java index c1abf3da16d..18354a1f07c 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/MinimalServletsTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/MinimalServletsTest.java @@ -18,10 +18,11 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -31,7 +32,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -public class MinimalServletsTest +public class MinimalServletsTest extends AbstractEmbeddedTest { private Server server; @@ -49,16 +50,18 @@ public class MinimalServletsTest } @Test - public void testGetHello() throws IOException + public void testGetHello() throws Exception { URI uri = server.getURI().resolve("/hello"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Hello")); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneConnectorTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneConnectorTest.java index 014a3838446..d700bb0272b 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneConnectorTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneConnectorTest.java @@ -18,10 +18,11 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -31,7 +32,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -public class OneConnectorTest +public class OneConnectorTest extends AbstractEmbeddedTest { private Server server; @@ -49,16 +50,18 @@ public class OneConnectorTest } @Test - public void testGetHello() throws IOException + public void testGetHello() throws Exception { URI uri = server.getURI().resolve("/hello"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Hello")); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneContextTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneContextTest.java index 0654b7b7350..9d12b7f1a36 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneContextTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneContextTest.java @@ -18,10 +18,11 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -31,7 +32,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -public class OneContextTest +public class OneContextTest extends AbstractEmbeddedTest { private Server server; @@ -49,16 +50,18 @@ public class OneContextTest } @Test - public void testGetHello() throws IOException + public void testGetHello() throws Exception { URI uri = server.getURI().resolve("/hello"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Hello")); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneHandlerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneHandlerTest.java index cd65fb494d0..920e24c59f4 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneHandlerTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneHandlerTest.java @@ -18,10 +18,11 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -31,7 +32,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -public class OneHandlerTest +public class OneHandlerTest extends AbstractEmbeddedTest { private Server server; @@ -49,16 +50,18 @@ public class OneHandlerTest } @Test - public void testGetHello() throws IOException + public void testGetHello() throws Exception { URI uri = server.getURI().resolve("/hello"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Hello")); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextJmxStatsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextJmxStatsTest.java index 471b7cbb160..ae627ca6c9c 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextJmxStatsTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextJmxStatsTest.java @@ -18,14 +18,15 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; import java.util.Set; import javax.management.MBeanServer; import javax.management.ObjectInstance; import javax.management.ObjectName; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.io.ConnectionStatistics; import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.server.Server; @@ -34,6 +35,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.opentest4j.AssertionFailedError; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; @@ -43,7 +45,7 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; @ExtendWith(WorkDirExtension.class) -public class OneServletContextJmxStatsTest +public class OneServletContextJmxStatsTest extends AbstractEmbeddedTest { private Server server; @@ -61,16 +63,18 @@ public class OneServletContextJmxStatsTest } @Test - public void testGetDumpViaPathInfo() throws IOException + public void testGetDumpViaPathInfo() throws Exception { URI uri = server.getURI().resolve("/dump/something"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, allOf( containsString("DumpServlet"), @@ -88,7 +92,7 @@ public class OneServletContextJmxStatsTest String domain = ConnectionStatistics.class.getPackage().getName(); Set mbeanNames = mbeanServer.queryNames(ObjectName.getInstance(domain + ":type=connectionstatistics,*"), null); - ObjectName connStatsName = mbeanNames.stream().findFirst().get(); + ObjectName connStatsName = mbeanNames.stream().findFirst().orElseThrow(AssertionFailedError::new); ObjectInstance mbeanConnStats = mbeanServer.getObjectInstance(connStatsName); Number connections = (Number)mbeanServer.getAttribute(connStatsName, "connections"); assertThat("stats[connections]", connections, is(notNullValue())); diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextTest.java index dbe97c555e0..5ed2433a912 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextTest.java @@ -19,12 +19,13 @@ package org.eclipse.jetty.embedded; import java.io.BufferedWriter; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; @@ -41,7 +42,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; @ExtendWith(WorkDirExtension.class) -public class OneServletContextTest +public class OneServletContextTest extends AbstractEmbeddedTest { private static final String TEXT_CONTENT = "The secret of getting ahead is getting started. - Mark Twain"; public WorkDir workDir; @@ -69,30 +70,34 @@ public class OneServletContextTest } @Test - public void testGetHello() throws IOException + public void testGetHello() throws Exception { URI uri = server.getURI().resolve("/hello/there"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Hello")); } @Test - public void testGetDumpViaPathInfo() throws IOException + public void testGetDumpViaPathInfo() throws Exception { URI uri = server.getURI().resolve("/dump/something"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, allOf( containsString("DumpServlet"), @@ -103,16 +108,18 @@ public class OneServletContextTest } @Test - public void testGetDumpSuffix() throws IOException + public void testGetDumpSuffix() throws Exception { URI uri = server.getURI().resolve("/another.dump"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, allOf( containsString("DumpServlet"), @@ -123,19 +130,21 @@ public class OneServletContextTest } @Test - public void testGetTestDumpSuffix() throws IOException + public void testGetTestDumpSuffix() throws Exception { URI uri = server.getURI().resolve("/test/another.dump"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); - String filterResponeHeader = http.getHeaderField("X-TestFilter"); - assertThat("X-TestFilter header", filterResponeHeader, is("true")); + String filterResponseHeader = response.getHeaders().get("X-TestFilter"); + assertThat("X-TestFilter header", filterResponseHeader, is("true")); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, allOf( containsString("DumpServlet"), diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextWithSessionTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextWithSessionTest.java index a2c81f2e49a..0b96d4dedbc 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextWithSessionTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextWithSessionTest.java @@ -19,12 +19,14 @@ package org.eclipse.jetty.embedded; import java.io.BufferedWriter; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; @@ -41,7 +43,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; @ExtendWith(WorkDirExtension.class) -public class OneServletContextWithSessionTest +public class OneServletContextWithSessionTest extends AbstractEmbeddedTest { private static final String TEXT_CONTENT = "Do the right thing. It will gratify some people and astonish the rest. - Mark Twain"; public WorkDir workDir; @@ -69,18 +71,20 @@ public class OneServletContextWithSessionTest } @Test - public void testGetHello() throws IOException + public void testGetHello() throws Exception { URI uri = server.getURI().resolve("/"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); - String setCookieValue = http.getHeaderField("Set-Cookie"); + // dumpResponseHeaders(response); + String setCookieValue = response.getHeaders().get(HttpHeader.SET_COOKIE); assertThat("Set-Cookie value", setCookieValue, containsString("JSESSIONID=")); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, allOf( containsString("session.getId() = "), diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java index fb9491d799e..303b2b5c47f 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java @@ -18,10 +18,11 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -31,7 +32,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -public class OneWebAppTest +public class OneWebAppTest extends AbstractEmbeddedTest { private Server server; @@ -49,16 +50,18 @@ public class OneWebAppTest } @Test - public void testGetAsyncRest() throws IOException + public void testGetAsyncRest() throws Exception { URI uri = server.getURI().resolve("/testAsync?items=mouse,beer,gnome"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Asynchronous: mouse,beer,gnome")); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java index 64502de287f..5003dc87739 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java @@ -18,10 +18,11 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -31,7 +32,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -public class OneWebAppWithJspTest +public class OneWebAppWithJspTest extends AbstractEmbeddedTest { private Server server; private URI serverLocalUri; @@ -53,46 +54,51 @@ public class OneWebAppWithJspTest } @Test - public void testGetDumpInfo() throws IOException + public void testGetDumpInfo() throws Exception { URI uri = serverLocalUri.resolve("/dump/info"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("getProtocol: HTTP/1.1")); } @Test - public void testGetJspExpr() throws IOException + public void testGetJspExpr() throws Exception { URI uri = serverLocalUri.resolve("/jsp/expr.jsp?A=1"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - String userAgent = OneWebAppWithJspTest.class.getSimpleName(); - http.setRequestProperty("User-Agent", userAgent); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); + String userAgent = client.getUserAgentField().getValue(); assertThat("Response Content", responseBody, containsString("" + userAgent + "")); } @Test - public void testGetJstlExpr() throws IOException + public void testGetJstlExpr() throws Exception { URI uri = serverLocalUri.resolve("/jsp/jstl.jsp"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("

    JSTL Example

    ")); for (int i = 1; i <= 10; i++) { diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ProxyServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ProxyServerTest.java index 1e59560abb0..c53d8a96740 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ProxyServerTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ProxyServerTest.java @@ -18,12 +18,12 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.InetSocketAddress; -import java.net.Proxy; import java.net.URI; +import org.eclipse.jetty.client.HttpProxy; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -33,10 +33,9 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -public class ProxyServerTest +public class ProxyServerTest extends AbstractEmbeddedTest { private Server server; - private Proxy javaHttpProxy; @BeforeEach public void startServer() throws Exception @@ -45,7 +44,7 @@ public class ProxyServerTest server.start(); URI uri = server.getURI(); - javaHttpProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(uri.getHost(), uri.getPort())); + client.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", uri.getPort())); } @AfterEach @@ -55,16 +54,19 @@ public class ProxyServerTest } @Test - public void testGetProxiedRFC() throws IOException + public void testGetProxiedRFC() throws Exception { URI uri = URI.create("https://tools.ietf.org/rfc/rfc7230.txt"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(javaHttpProxy); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); - // HttpUtil.dumpResponseHeaders(http); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing")); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/RewriteServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/RewriteServerTest.java index c1bcb8392bc..775f550208c 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/RewriteServerTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/RewriteServerTest.java @@ -18,10 +18,11 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -31,7 +32,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -public class RewriteServerTest +public class RewriteServerTest extends AbstractEmbeddedTest { private Server server; @@ -49,30 +50,34 @@ public class RewriteServerTest } @Test - public void testGetRewriteFooInName() throws IOException + public void testGetRewriteFooInName() throws Exception { - URI destUri = server.getURI().resolve("/do-be-foo-be-do"); - HttpURLConnection http = (HttpURLConnection)destUri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + URI uri = server.getURI().resolve("/do-be-foo-be-do"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("requestURI=/do-be-FOO-be-do")); } @Test - public void testGetRewriteFooInPath() throws IOException + public void testGetRewriteFooInPath() throws Exception { - URI destUri = server.getURI().resolve("/do/be/foo/be/do.it"); - HttpURLConnection http = (HttpURLConnection)destUri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + URI uri = server.getURI().resolve("/do/be/foo/be/do.it"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("requestURI=/do/be/FOO/be/do.it")); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SecuredHelloHandlerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SecuredHelloHandlerTest.java index 9658dfcf9b6..892330b392c 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SecuredHelloHandlerTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SecuredHelloHandlerTest.java @@ -18,11 +18,13 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; import java.util.Base64; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -33,7 +35,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -public class SecuredHelloHandlerTest +public class SecuredHelloHandlerTest extends AbstractEmbeddedTest { private Server server; @@ -51,28 +53,33 @@ public class SecuredHelloHandlerTest } @Test - public void testGetWithoutAuth() throws IOException + public void testGetWithoutAuth() throws Exception { - URI destUri = server.getURI().resolve("/hello"); - HttpURLConnection http = (HttpURLConnection)destUri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_UNAUTHORIZED)); + URI uri = server.getURI().resolve("/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.UNAUTHORIZED_401)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); } @Test - public void testGetWithAuth() throws IOException + public void testGetWithAuth() throws Exception { - URI destUri = server.getURI().resolve("/hello"); - HttpURLConnection http = (HttpURLConnection)destUri.toURL().openConnection(); - String authEncoded = Base64.getEncoder().encodeToString("user:password".getBytes(UTF_8)); - http.setRequestProperty("Authorization", "Basic " + authEncoded); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + URI uri = server.getURI().resolve("/hello"); - // HttpUtil.dumpResponseHeaders(http); + String authEncoded = Base64.getEncoder().encodeToString("user:password".getBytes(UTF_8)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .header(HttpHeader.AUTHORIZATION, "Basic " + authEncoded) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("

    Hello World

    ")); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java index ca812e86323..b135429a900 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java @@ -18,10 +18,10 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -32,7 +32,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; -public class ServerWithAnnotationsTest +public class ServerWithAnnotationsTest extends AbstractEmbeddedTest { private Server server; @@ -50,16 +50,16 @@ public class ServerWithAnnotationsTest } @Test - public void testGetTest() throws IOException + public void testGetTest() throws Exception { - URI destUri = server.getURI().resolve("/test"); - HttpURLConnection http = (HttpURLConnection)destUri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + URI uri = server.getURI().resolve("/test"); + ContentResponse response = client.GET(uri); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("maxAmount=55.0")); assertThat("Response Content", responseBody, not(containsString(""))); } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJMXTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJMXTest.java index 7094f4413c8..9a5d3ab9d2a 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJMXTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJMXTest.java @@ -31,7 +31,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; -public class ServerWithJMXTest +public class ServerWithJMXTest extends AbstractEmbeddedTest { private Server server; diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java index d1f1f2ace17..acea706892b 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java @@ -18,9 +18,10 @@ package org.eclipse.jetty.embedded; -import java.net.HttpURLConnection; import java.net.URI; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -32,7 +33,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; -public class ServerWithJNDITest +public class ServerWithJNDITest extends AbstractEmbeddedTest { private Server server; @@ -52,14 +53,14 @@ public class ServerWithJNDITest @Test public void testGetTest() throws Exception { - URI destUri = server.getURI().resolve("/test"); - HttpURLConnection http = (HttpURLConnection)destUri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + URI uri = server.getURI().resolve("/test"); + ContentResponse response = client.GET(uri); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, allOf( containsString("java:comp/env/woggle"), diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SimplestServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SimplestServerTest.java index b14fc747eb1..a21a8e438e6 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SimplestServerTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SimplestServerTest.java @@ -18,9 +18,10 @@ package org.eclipse.jetty.embedded; -import java.net.HttpURLConnection; import java.net.URI; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -29,7 +30,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -public class SimplestServerTest +public class SimplestServerTest extends AbstractEmbeddedTest { private Server server; @@ -49,8 +50,8 @@ public class SimplestServerTest @Test public void testGetTest() throws Exception { - URI destUri = server.getURI().resolve("/test"); - HttpURLConnection http = (HttpURLConnection)destUri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_NOT_FOUND)); + URI uri = server.getURI().resolve("/test"); + ContentResponse response = client.GET(uri); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.NOT_FOUND_404)); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SplitFileServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SplitFileServerTest.java index e54ac8ae0af..5e5411260fc 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SplitFileServerTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SplitFileServerTest.java @@ -18,12 +18,12 @@ package org.eclipse.jetty.embedded; -import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; import java.nio.file.Path; import java.nio.file.Paths; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.resource.PathResource; import org.eclipse.jetty.util.resource.Resource; @@ -35,7 +35,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -public class SplitFileServerTest +public class SplitFileServerTest extends AbstractEmbeddedTest { private Server server; @@ -58,38 +58,38 @@ public class SplitFileServerTest } @Test - public void testGetTest0() throws IOException + public void testGetTest0() throws Exception { URI uri = server.getURI().resolve("/test0.txt"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.GET(uri); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("test0")); } @Test - public void testGetTest1() throws IOException + public void testGetTest1() throws Exception { URI uri = server.getURI().resolve("/test1.txt"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_OK)); + ContentResponse response = client.GET(uri); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); - // HttpUtil.dumpResponseHeaders(http); + // dumpResponseHeaders(response); // test response content - String responseBody = HttpUtil.getResponseBody(http); + String responseBody = response.getContentAsString(); assertThat("Response Content", responseBody, containsString("test1")); } @Test - public void testGetTest2() throws IOException + public void testGetTest2() throws Exception { URI uri = server.getURI().resolve("/test2.txt"); - HttpURLConnection http = (HttpURLConnection)uri.toURL().openConnection(); - assertThat("HTTP Response Status", http.getResponseCode(), is(HttpURLConnection.HTTP_NOT_FOUND)); + ContentResponse response = client.GET(uri); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.NOT_FOUND_404)); } } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/WebSocketJsrServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/WebSocketJsrServerTest.java new file mode 100644 index 00000000000..34a74d8a24a --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/WebSocketJsrServerTest.java @@ -0,0 +1,110 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.net.URI; +import java.util.concurrent.LinkedBlockingQueue; +import javax.websocket.CloseReason; +import javax.websocket.ContainerProvider; +import javax.websocket.Endpoint; +import javax.websocket.EndpointConfig; +import javax.websocket.MessageHandler; +import javax.websocket.Session; +import javax.websocket.WebSocketContainer; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.websocket.api.util.WSURI; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class WebSocketJsrServerTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = WebSocketJsrServer.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetEcho() throws Exception + { + WebSocketContainer javaxWebSocketClient = ContainerProvider.getWebSocketContainer(); + javaxWebSocketClient.setDefaultMaxSessionIdleTimeout(2000); + try + { + URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/echo")); + + TrackingClientEndpoint clientEndpoint = new TrackingClientEndpoint(); + + Session session = javaxWebSocketClient.connectToServer(clientEndpoint, wsUri); + session.getBasicRemote().sendText("Hello World"); + + String response = clientEndpoint.messages.poll(2, SECONDS); + assertThat("Response", response, is("Hello World")); + } + finally + { + LifeCycle.stop(javaxWebSocketClient); + } + } + + public static class TrackingClientEndpoint extends Endpoint implements MessageHandler.Whole + { + public LinkedBlockingQueue messages = new LinkedBlockingQueue<>(); + + @Override + public void onMessage(String message) + { + messages.offer(message); + } + + @Override + public void onOpen(Session session, EndpointConfig config) + { + session.addMessageHandler(this); + } + + @Override + public void onError(Session session, Throwable thr) + { + super.onError(session, thr); + } + + @Override + public void onClose(Session session, CloseReason closeReason) + { + super.onClose(session, closeReason); + } + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/WebSocketServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/WebSocketServerTest.java new file mode 100644 index 00000000000..faf74c41495 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/WebSocketServerTest.java @@ -0,0 +1,110 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import java.net.URI; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.WebSocket; +import org.eclipse.jetty.websocket.api.util.WSURI; +import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class WebSocketServerTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = WebSocketServer.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetEcho() throws Exception + { + WebSocketClient webSocketClient = new WebSocketClient(); + webSocketClient.setMaxIdleTimeout(2000); + try + { + webSocketClient.start(); + URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/echo")); + + TrackingClientEndpoint clientEndpoint = new TrackingClientEndpoint(); + + Future sessionFut = webSocketClient.connect(clientEndpoint, wsUri); + Session session = sessionFut.get(2, SECONDS); + session.getRemote().sendString("Hello World"); + + String response = clientEndpoint.messages.poll(2, SECONDS); + assertThat("Response", response, is("Hello World")); + } + finally + { + LifeCycle.stop(webSocketClient); + } + } + + @WebSocket + public static class TrackingClientEndpoint + { + private static final Logger LOG = Log.getLogger(TrackingClientEndpoint.class); + public LinkedBlockingQueue messages = new LinkedBlockingQueue<>(); + + @OnWebSocketMessage + public void onMessage(String message) + { + messages.offer(message); + } + + @OnWebSocketError + public void onError(Throwable cause) + { + LOG.warn(cause); + } + + @OnWebSocketClose + public void onClose(int statusCode, String reason) + { + LOG.debug("Closed({}, {})", statusCode, reason); + } + } +} diff --git a/examples/embedded/src/test/resources/jetty-logging.properties b/examples/embedded/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..b12fc7beedf --- /dev/null +++ b/examples/embedded/src/test/resources/jetty-logging.properties @@ -0,0 +1,10 @@ +org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog +org.eclipse.jetty.LEVEL=WARN +#org.eclipse.jetty.STACKS=true +#org.eclipse.jetty.STACKS=false +#org.eclipse.jetty.io.LEVEL=DEBUG +#org.eclipse.jetty.io.ssl.LEVEL=DEBUG +#org.eclipse.jetty.server.LEVEL=DEBUG +#org.eclipse.jetty.servlets.LEVEL=DEBUG +#org.eclipse.jetty.alpn.LEVEL=DEBUG +#org.eclipse.jetty.jmx.LEVEL=DEBUG From c67ac736df581bb0935abf13aee8dbcb6a72dbe2 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Tue, 10 Sep 2019 10:21:38 +1000 Subject: [PATCH 037/113] implement serializable for auth in cluster Signed-off-by: Lachlan Roberts --- .../eclipse/jetty/security/openid/OpenIdAuthenticator.java | 3 +-- .../eclipse/jetty/security/openid/OpenIdConfiguration.java | 6 ++++-- .../eclipse/jetty/security/openid/OpenIdCredentials.java | 4 +++- .../eclipse/jetty/security/openid/OpenIdUserPrincipal.java | 4 +++- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java index bc814984ed0..79725897567 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java @@ -319,8 +319,7 @@ public class OpenIdAuthenticator extends LoginAuthenticator if (authentication != null) { // Has authentication been revoked? - if (authentication instanceof Authentication.User && - _loginService != null && + if (authentication instanceof Authentication.User && _loginService != null && !_loginService.validate(((Authentication.User)authentication).getUserIdentity())) { LOG.debug("auth revoked {}", authentication); diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java index d68c862b731..c237676b04b 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.security.openid; import java.io.InputStream; +import java.io.Serializable; import java.net.URI; import java.util.ArrayList; import java.util.List; @@ -35,10 +36,11 @@ import org.eclipse.jetty.util.log.Logger; * This uses the OpenID Provider URL with the path {@link #CONFIG_PATH} to discover * the required information about the OIDC service. */ -public class OpenIdConfiguration +public class OpenIdConfiguration implements Serializable { private static final Logger LOG = Log.getLogger(OpenIdConfiguration.class); - private static String CONFIG_PATH = "/.well-known/openid-configuration"; + private static final long serialVersionUID = 2227941990601349102L; + private static final String CONFIG_PATH = "/.well-known/openid-configuration"; private final String openIdProvider; private final String issuer; diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java index f8eba14de3a..20917f2bec7 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.security.openid; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.Serializable; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; @@ -43,9 +44,10 @@ import org.eclipse.jetty.util.log.Logger; * The response is then validated against the {@link OpenIdConfiguration}. *

    */ -public class OpenIdCredentials +public class OpenIdCredentials implements Serializable { private static final Logger LOG = Log.getLogger(OpenIdCredentials.class); + private static final long serialVersionUID = 4766053233370044796L; private final String redirectUri; private final OpenIdConfiguration configuration; diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserPrincipal.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserPrincipal.java index 7034e4efd94..6ebb46df2a2 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserPrincipal.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserPrincipal.java @@ -18,10 +18,12 @@ package org.eclipse.jetty.security.openid; +import java.io.Serializable; import java.security.Principal; -public class OpenIdUserPrincipal implements Principal +public class OpenIdUserPrincipal implements Principal, Serializable { + private static final long serialVersionUID = 1521094652756670469L; private final OpenIdCredentials _credentials; public OpenIdUserPrincipal(OpenIdCredentials credentials) From f90a07371c9354e19755a3aa7deddf1e84a006d1 Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Tue, 10 Sep 2019 10:24:58 +1000 Subject: [PATCH 038/113] Issue #4022 Add extra ServletRegistration tests & checkstyle fixes --- .../servlet/ServletContextHandlerTest.java | 113 +++++++++++++++++- 1 file changed, 107 insertions(+), 6 deletions(-) diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java index 14d7ccfa616..b7f7d43d60e 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java @@ -667,7 +667,7 @@ public class ServletContextHandlerTest } @Test - public void testAddServletFromFilter() throws Exception + public void testAddServletByClassFromFilter() throws Exception { //A servlet cannot be added from a Filter Logger logger = Log.getLogger(ContextHandler.class.getName() + "ROOT"); @@ -718,6 +718,110 @@ public class ServletContextHandlerTest } } + @Test + public void testAddServletByInstanceFromFilter() throws Exception + { + //A servlet cannot be added from a Filter + Logger logger = Log.getLogger(ContextHandler.class.getName() + "ROOT"); + + try (StacklessLogging stackless = new StacklessLogging(logger)) + { + ServletContextHandler context = new ServletContextHandler(); + context.setLogger(logger); + FilterHolder holder = new FilterHolder(new Filter() + { + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + ServletRegistration rego = filterConfig.getServletContext().addServlet("hello", new HelloServlet()); + rego.addMapping("/hello/*"); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + } + + @Override + public void destroy() + { + } + + }); + context.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST)); + context.getServletHandler().setStartWithUnavailable(false); + context.setContextPath("/"); + _server.setHandler(context); + _server.start(); + fail("Servlet can only be added from SCI or SCL"); + } + catch (Exception e) + { + if (!(e instanceof IllegalStateException)) + { + if (e instanceof ServletException) + { + assertTrue(e.getCause() instanceof IllegalStateException); + } + else + fail(e); + } + } + } + + @Test + public void testAddServletByClassNameFromFilter() throws Exception + { + //A servlet cannot be added from a Filter + Logger logger = Log.getLogger(ContextHandler.class.getName() + "ROOT"); + + try (StacklessLogging stackless = new StacklessLogging(logger)) + { + ServletContextHandler context = new ServletContextHandler(); + context.setLogger(logger); + FilterHolder holder = new FilterHolder(new Filter() + { + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + ServletRegistration rego = filterConfig.getServletContext().addServlet("hello", HelloServlet.class.getName()); + rego.addMapping("/hello/*"); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + } + + @Override + public void destroy() + { + } + + }); + context.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST)); + context.getServletHandler().setStartWithUnavailable(false); + context.setContextPath("/"); + _server.setHandler(context); + _server.start(); + fail("Servlet can only be added from SCI or SCL"); + } + catch (Exception e) + { + if (!(e instanceof IllegalStateException)) + { + if (e instanceof ServletException) + { + assertTrue(e.getCause() instanceof IllegalStateException); + } + else + fail(e); + } + } + } + @Test public void testAddServletFromSCL() throws Exception { @@ -770,6 +874,7 @@ public class ServletContextHandlerTest rego.addMapping("/hello/*"); } } + root.addBean(new MySCIStarter(root.getServletContext(), new ServletAddingSCI()), true); _server.start(); @@ -797,7 +902,6 @@ public class ServletContextHandlerTest request.append("\n"); String response = _connector.getResponse(request.toString()); - int result; assertThat("Response", response, containsString("Test")); context.addServlet(HelloServlet.class, "/hello"); @@ -950,7 +1054,6 @@ public class ServletContextHandlerTest request.append("\n"); String response = _connector.getResponse(request.toString()); - int result; assertThat("Response", response, containsString("Test")); assertEquals(extra, context.getSessionHandler().getHandler()); @@ -995,7 +1098,6 @@ public class ServletContextHandlerTest request.append("\n"); String response = _connector.getResponse(request.toString()); - int result; assertThat("Response", response, containsString("Test")); context.stop(); @@ -1016,7 +1118,7 @@ public class ServletContextHandlerTest @Test public void testSetSecurityHandler() throws Exception { - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS|ServletContextHandler.SECURITY|ServletContextHandler.GZIP); + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS | ServletContextHandler.SECURITY | ServletContextHandler.GZIP); assertNotNull(context.getSessionHandler()); SessionHandler sessionHandler = context.getSessionHandler(); assertNotNull(context.getSecurityHandler()); @@ -1094,7 +1196,6 @@ public class ServletContextHandlerTest request.append("\n"); String response = _connector.getResponse(request.toString()); - int result; assertThat("Response", response, containsString("Test")); context.stop(); From 4f87da5f250acfdec7fe2fcb47e71435291f05e6 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 9 Sep 2019 20:25:49 -0500 Subject: [PATCH 039/113] Attempting to understand why jetty-distribution is not found on CI Signed-off-by: Joakim Erdfelt --- .../eclipse/jetty/embedded/JettyDistribution.java | 14 +++++++++++--- .../src/test/resources/jetty-logging.properties | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java index 0ccc7ae3845..21a6540c9c3 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java @@ -42,19 +42,25 @@ public class JettyDistribution static { Path distro = asJettyDistribution(System.getProperty("jetty.home")); + LOG.debug("JettyDistribution(prop(jetty.home)) = " + distro); if (distro == null) + { distro = asJettyDistribution(System.getenv().get("JETTY_HOME")); + LOG.debug("JettyDistribution(env(JETTY_HOME)) = " + distro); + } if (distro == null) { try { Path working = Paths.get(System.getProperty("user.dir")); + LOG.debug("JettyDistribution(prop(user.dir)) = " + working); while (distro == null && working != null) { distro = asJettyDistribution(working.resolve("jetty-distribution/target/distribution").toString()); working = working.getParent(); } + LOG.debug("JettyDistribution(working.resolve(...)) = " + distro); } catch (Throwable cause) { @@ -64,9 +70,11 @@ public class JettyDistribution if (distro == null) { + LOG.info("JettyDistribution() = FAILURE"); throw new RuntimeException("Unable to find built jetty-distribution, run the build and try again."); } + LOG.debug("JettyDistribution() FOUND = " + distro); DISTRIBUTION = distro; } @@ -94,18 +102,18 @@ public class JettyDistribution if (!Files.isDirectory(dir)) { - LOG.info("asJettyDistribution {} is not a directory", jettyHome); + LOG.debug("asJettyDistribution {} is not a directory", jettyHome); return null; } Path demoBase = dir.resolve("demo-base"); if (!Files.exists(demoBase) || !Files.isDirectory(demoBase)) { - LOG.info("asJettyDistribution {} has no demo-base", jettyHome); + LOG.debug("asJettyDistribution {} has no demo-base", jettyHome); return null; } - LOG.info("asJettyDistribution {}", dir); + LOG.debug("asJettyDistribution {}", dir); return dir.toAbsolutePath(); } catch (Exception e) diff --git a/examples/embedded/src/test/resources/jetty-logging.properties b/examples/embedded/src/test/resources/jetty-logging.properties index b12fc7beedf..b17c80c3330 100644 --- a/examples/embedded/src/test/resources/jetty-logging.properties +++ b/examples/embedded/src/test/resources/jetty-logging.properties @@ -1,5 +1,6 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog org.eclipse.jetty.LEVEL=WARN +org.eclipse.jetty.embedded.JettyDistribution.LEVEL=DEBUG #org.eclipse.jetty.STACKS=true #org.eclipse.jetty.STACKS=false #org.eclipse.jetty.io.LEVEL=DEBUG From f6aea229798f2a29da3efbae9fc916337f10d6ea Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 9 Sep 2019 20:35:02 -0500 Subject: [PATCH 040/113] Issue #3989 - Cleaning up FailedSelectorTest + Based on PR review + Squelching logging output + Removing stacks from ManagedSelector logging Signed-off-by: Joakim Erdfelt --- .../jetty/test/FailedSelectorTest.java | 29 +++++++++++-------- .../test/resources/jetty-logging.properties | 1 + 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java index 259e2c72550..5fc00669aff 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java @@ -56,6 +56,7 @@ import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.log.StacklessLogging; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.Scheduler; import org.junit.jupiter.api.AfterEach; @@ -72,12 +73,14 @@ public class FailedSelectorTest private static final Logger LOG = Log.getLogger(FailedSelectorTest.class); private HttpClient client; private Server server; + private StacklessLogging stacklessManagedSelector; @AfterEach public void stopServerAndClient() throws Exception { server.stop(); client.stop(); + stacklessManagedSelector.close(); } @BeforeEach @@ -98,6 +101,8 @@ public class FailedSelectorTest public void startServer(Function customizeServerConsumer) throws Exception { + stacklessManagedSelector = new StacklessLogging(ManagedSelector.class); + server = new Server(); server.setStopTimeout(1000); server.setStopAtShutdown(true); @@ -138,7 +143,7 @@ public class FailedSelectorTest assertRequestHello(); // Request /selector/close - assertRequestSelectorClose("/selector/close"); + assertRequestSelectorClose(); // Wait for selectors to close from action above assertTrue(failedLatch.await(2, TimeUnit.SECONDS)); @@ -164,7 +169,7 @@ public class FailedSelectorTest assertRequestHello(); // Request /selector/close - assertRequestSelectorClose("/selector/close"); + assertRequestSelectorClose(); // Wait for selectors to close from action above assertTrue(failedLatch.await(2, TimeUnit.SECONDS)); @@ -173,9 +178,9 @@ public class FailedSelectorTest assertRequestHello(); } - private void assertRequestSelectorClose(String path) throws InterruptedException, ExecutionException, TimeoutException + private void assertRequestSelectorClose() throws InterruptedException, ExecutionException, TimeoutException { - URI dest = server.getURI().resolve(path); + URI dest = server.getURI().resolve("/selector/close"); LOG.info("Requesting GET on {}", dest); ContentResponse response = client.newRequest(dest) @@ -183,8 +188,8 @@ public class FailedSelectorTest .header(HttpHeader.CONNECTION, "close") .send(); - assertThat("/selector/close status", response.getStatus(), is(HttpStatus.OK_200)); - assertThat("/selector/close response", response.getContentAsString(), startsWith("Closing selectors ")); + assertThat(dest + " status", response.getStatus(), is(HttpStatus.OK_200)); + assertThat(dest + " response", response.getContentAsString(), startsWith("Closing selectors ")); } private void assertRequestHello() throws InterruptedException, ExecutionException, TimeoutException @@ -196,8 +201,8 @@ public class FailedSelectorTest .header(HttpHeader.CONNECTION, "close") .send(); - assertThat("/hello status", response.getStatus(), is(HttpStatus.OK_200)); - assertThat("/hello response", response.getContentAsString(), startsWith("Hello ")); + assertThat(dest + " status", response.getStatus(), is(HttpStatus.OK_200)); + assertThat(dest + " response", response.getContentAsString(), startsWith("Hello ")); } public static class HelloServlet extends HttpServlet @@ -230,7 +235,7 @@ public class FailedSelectorTest resp.setCharacterEncoding("utf-8"); resp.setHeader("Connection", "close"); resp.getWriter().printf("Closing selectors in %,d ms%n", DELAY_MS); - scheduledExecutorService.schedule(new InterruptSelectorTask(connector), DELAY_MS, TimeUnit.MILLISECONDS); + scheduledExecutorService.schedule(new ForceCloseSelectorTask(connector), DELAY_MS, TimeUnit.MILLISECONDS); } } @@ -360,12 +365,12 @@ public class FailedSelectorTest } } - private static class InterruptSelectorTask implements Runnable + private static class ForceCloseSelectorTask implements Runnable { - private static final Logger LOG = Log.getLogger(InterruptSelectorTask.class); + private static final Logger LOG = Log.getLogger(ForceCloseSelectorTask.class); private final ServerConnector connector; - public InterruptSelectorTask(ServerConnector connector) + public ForceCloseSelectorTask(ServerConnector connector) { this.connector = connector; } diff --git a/tests/test-integration/src/test/resources/jetty-logging.properties b/tests/test-integration/src/test/resources/jetty-logging.properties index 1531468c2c1..fdc5a51caba 100644 --- a/tests/test-integration/src/test/resources/jetty-logging.properties +++ b/tests/test-integration/src/test/resources/jetty-logging.properties @@ -1,3 +1,4 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog +org.eclipse.jetty.LEVEL=WARN #org.eclipse.jetty.LEVEL=DEBUG #org.eclipse.jetty.websocket.LEVEL=DEBUG From bec14ff870f4ba4f2442e0f951ed73d4b5173f50 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 9 Sep 2019 20:47:16 -0500 Subject: [PATCH 041/113] More testing of embedded examples Signed-off-by: Joakim Erdfelt --- .../org/eclipse/jetty/embedded/JettyDistribution.java | 11 +++++++---- .../org/eclipse/jetty/embedded/LikeJettyXmlTest.java | 3 +++ .../org/eclipse/jetty/embedded/OneWebAppTest.java | 3 +++ .../eclipse/jetty/embedded/OneWebAppWithJspTest.java | 3 +++ .../jetty/embedded/ServerWithAnnotationsTest.java | 3 +++ .../eclipse/jetty/embedded/ServerWithJNDITest.java | 3 +++ 6 files changed, 22 insertions(+), 4 deletions(-) diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java index 21a6540c9c3..3e8038040ff 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java @@ -70,11 +70,12 @@ public class JettyDistribution if (distro == null) { - LOG.info("JettyDistribution() = FAILURE"); - throw new RuntimeException("Unable to find built jetty-distribution, run the build and try again."); + LOG.info("JettyDistribution() FAILURE: NOT FOUND"); + } + else + { + LOG.debug("JettyDistribution() FOUND = " + distro); } - - LOG.debug("JettyDistribution() FOUND = " + distro); DISTRIBUTION = distro; } @@ -125,6 +126,8 @@ public class JettyDistribution public static Path resolve(String path) { + if (DISTRIBUTION == null) + throw new RuntimeException("jetty-distribution not found"); return DISTRIBUTION.resolve(path); } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java index 6496b3abe44..3cd3cf1a479 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java @@ -32,6 +32,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assumptions.assumeTrue; public class LikeJettyXmlTest extends AbstractEmbeddedTest { @@ -42,6 +43,8 @@ public class LikeJettyXmlTest extends AbstractEmbeddedTest @BeforeEach public void startServer() throws Exception { + assumeTrue(JettyDistribution.DISTRIBUTION != null, "jetty-distribution not found"); + server = LikeJettyXml.createServer(0, 0, false); server.start(); diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java index 303b2b5c47f..4801f8c1e15 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java @@ -31,6 +31,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assumptions.assumeTrue; public class OneWebAppTest extends AbstractEmbeddedTest { @@ -39,6 +40,8 @@ public class OneWebAppTest extends AbstractEmbeddedTest @BeforeEach public void startServer() throws Exception { + assumeTrue(JettyDistribution.DISTRIBUTION != null, "jetty-distribution not found"); + server = OneWebApp.createServer(0); server.start(); } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java index 5003dc87739..53569bc7e32 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java @@ -31,6 +31,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assumptions.assumeTrue; public class OneWebAppWithJspTest extends AbstractEmbeddedTest { @@ -40,6 +41,8 @@ public class OneWebAppWithJspTest extends AbstractEmbeddedTest @BeforeEach public void startServer() throws Exception { + assumeTrue(JettyDistribution.DISTRIBUTION != null, "jetty-distribution not found"); + server = OneWebAppWithJsp.createServer(0); server.start(); diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java index b135429a900..5abee62e47d 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java @@ -31,6 +31,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assumptions.assumeTrue; public class ServerWithAnnotationsTest extends AbstractEmbeddedTest { @@ -39,6 +40,8 @@ public class ServerWithAnnotationsTest extends AbstractEmbeddedTest @BeforeEach public void startServer() throws Exception { + assumeTrue(JettyDistribution.DISTRIBUTION != null, "jetty-distribution not found"); + server = ServerWithAnnotations.createServer(0); server.start(); } diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java index acea706892b..2065a2f0e09 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java @@ -32,6 +32,7 @@ import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assumptions.assumeTrue; public class ServerWithJNDITest extends AbstractEmbeddedTest { @@ -40,6 +41,8 @@ public class ServerWithJNDITest extends AbstractEmbeddedTest @BeforeEach public void startServer() throws Exception { + assumeTrue(JettyDistribution.DISTRIBUTION != null, "jetty-distribution not found"); + server = ServerWithJNDI.createServer(0); server.start(); } From 10de54fa074654698e5450cb8012a6df10379da0 Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Tue, 10 Sep 2019 14:48:20 +1000 Subject: [PATCH 042/113] Issue #4072 Add test for SessionAuthentication serialization (#4074) Signed-off-by: Jan Bartel --- .../security/SessionAuthenticationTest.java | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 jetty-security/src/test/java/org/eclipse/jetty/security/SessionAuthenticationTest.java diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/SessionAuthenticationTest.java b/jetty-security/src/test/java/org/eclipse/jetty/security/SessionAuthenticationTest.java new file mode 100644 index 00000000000..a30d3337524 --- /dev/null +++ b/jetty-security/src/test/java/org/eclipse/jetty/security/SessionAuthenticationTest.java @@ -0,0 +1,93 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.security; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import org.eclipse.jetty.security.authentication.SessionAuthentication; +import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.security.Password; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * SessionAuthenticationTest + * + */ +public class SessionAuthenticationTest +{ + /** + * Check that a SessionAuthenticator is serializable, and that + * the deserialized SessionAuthenticator contains the same authentication + * and authorization information. + */ + @Test + public void testSessionAuthenticationSerialization() + throws Exception + { + + ContextHandler contextHandler = new ContextHandler(); + SecurityHandler securityHandler = new ConstraintSecurityHandler(); + contextHandler.setHandler(securityHandler); + TestLoginService loginService = new TestLoginService("SessionAuthTest"); + Password pwd = new Password("foo"); + loginService.putUser("foo", pwd, new String[]{"boss", "worker"}); + securityHandler.setLoginService(loginService); + securityHandler.setAuthMethod("FORM"); + UserIdentity user = loginService.login("foo", pwd, null); + assertNotNull(user); + assertNotNull(user.getUserPrincipal()); + assertEquals("foo", user.getUserPrincipal().getName()); + SessionAuthentication sessionAuth = new SessionAuthentication("FORM", user, pwd); + assertTrue(sessionAuth.isUserInRole(null, "boss")); + contextHandler.handle(new Runnable() + { + public void run() + { + try + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(sessionAuth); + oos.close(); + ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())); + SessionAuthentication reactivatedSessionAuth = (SessionAuthentication)ois.readObject(); + assertNotNull(reactivatedSessionAuth); + assertNotNull(reactivatedSessionAuth.getUserIdentity()); + assertNotNull(reactivatedSessionAuth.getUserIdentity().getUserPrincipal()); + assertEquals("foo", reactivatedSessionAuth.getUserIdentity().getUserPrincipal().getName()); + assertNotNull(reactivatedSessionAuth.getUserIdentity().getSubject()); + assertTrue(reactivatedSessionAuth.isUserInRole(null, "boss")); + } + catch (Exception e) + { + fail(e); + } + } + }); + } +} From b851af0a5b654a910655ccd63bb97891a82ec895 Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Tue, 10 Sep 2019 16:04:01 +1000 Subject: [PATCH 043/113] Issue #4003 Fix quickstart (#4008) * Issue #4003 Fix quickstart. Modes: AUTO: use quickstart xml if present, start normally otherwise GENERATE: re/generate quickstart xml QUICKSTART: use a pregenerated quickstart xml, fail otherwise Signed-off-by: Jan Bartel * Issue #4003 Cleanup quickstart Removed TerminateException in favour of Server.setDryRun(boolean) and AbstractLifeCycle.StopException Signed-off-by: Greg Wilkins --- .../src/main/config/etc/jetty-deploy.xml | 4 +- .../jetty/deploy/DeploymentManager.java | 5 +- .../PropertiesConfigurationManager.java | 42 +++- .../bindings/GlobalWebappConfigBinding.java | 11 +- .../deploy/providers/ScanningAppProvider.java | 20 +- .../deploy/providers/WebAppProvider.java | 1 + .../maven/plugin/JettyEffectiveWebXml.java | 42 ++-- .../maven/plugin/JettyRunForkedMojo.java | 36 ++-- .../maven/plugin/JettyWebAppContext.java | 87 +-------- .../plugin/MavenQuickStartConfiguration.java | 53 ----- .../eclipse/jetty/maven/plugin/Starter.java | 14 +- .../maven/plugin/WebAppPropertyConverter.java | 9 +- .../main/config/etc/example-quickstart.xml | 27 --- .../src/main/config/etc/jetty-quickstart.xml | 31 +++ .../jetty-quickstart.d/quickstart-webapp.xml | 28 +++ .../src/main/config/modules/quickstart.mod | 17 +- .../quickstart/PreconfigureQuickStartWar.java | 29 ++- .../quickstart/QuickStartConfiguration.java | 125 +++++++----- .../QuickStartGeneratorConfiguration.java | 47 ++--- .../jetty/quickstart/QuickStartWebApp.java | 90 --------- .../jetty/quickstart/TestQuickStart.java | 33 ++-- .../java/org/eclipse/jetty/server/Server.java | 53 +++-- .../jetty/server/handler/AbstractHandler.java | 2 +- .../handler/AbstractHandlerContainer.java | 2 +- .../jetty/server/handler/ContextHandler.java | 5 +- .../server/handler/HandlerCollection.java | 2 +- .../jetty/server/handler/HandlerWrapper.java | 2 +- .../util/component/AbstractLifeCycle.java | 181 +++++++++++------- .../util/component/ContainerLifeCycle.java | 6 +- .../org/eclipse/jetty/webapp/MetaData.java | 2 +- .../jetty/webapp/WebInfConfiguration.java | 4 + .../eclipse/jetty/xml/XmlConfiguration.java | 22 ++- .../jetty/tests/distribution/BadAppTests.java | 4 +- .../jetty/tests/distribution/CDITests.java | 2 +- .../tests/distribution/DemoBaseTests.java | 6 +- .../tests/distribution/DistributionTests.java | 64 ++++++- .../distribution/DynamicListenerTests.java | 2 +- .../tests/distribution/OsgiAppTests.java | 2 +- .../jetty/quickstart/QuickStartTest.java | 31 ++- .../eclipse/jetty/quickstart/Quickstart.java | 12 +- 40 files changed, 612 insertions(+), 543 deletions(-) delete mode 100644 jetty-quickstart/src/main/config/etc/example-quickstart.xml create mode 100644 jetty-quickstart/src/main/config/etc/jetty-quickstart.xml create mode 100644 jetty-quickstart/src/main/config/modules/jetty-quickstart.d/quickstart-webapp.xml delete mode 100644 jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartWebApp.java diff --git a/jetty-deploy/src/main/config/etc/jetty-deploy.xml b/jetty-deploy/src/main/config/etc/jetty-deploy.xml index c279a388389..d244576c146 100644 --- a/jetty-deploy/src/main/config/etc/jetty-deploy.xml +++ b/jetty-deploy/src/main/config/etc/jetty-deploy.xml @@ -36,9 +36,9 @@ --> - + - + jetty.deploy.monitoredPath diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java index 64de4a6444e..5e53d5f8930 100644 --- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java +++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java @@ -172,7 +172,7 @@ public class DeploymentManager extends ContainerLifeCycle for (AppProvider provider : providers) { if (_providers.add(provider)) - addBean(provider); + addBean(provider, true); } } @@ -186,7 +186,7 @@ public class DeploymentManager extends ContainerLifeCycle if (isRunning()) throw new IllegalStateException(); _providers.add(provider); - addBean(provider); + addBean(provider, true); } public void setLifeCycleBindings(Collection bindings) @@ -523,6 +523,7 @@ public class DeploymentManager extends ContainerLifeCycle catch (Throwable t) { LOG.warn("Unable to reach node goal: " + nodeName, t); + // migrate to FAILED node Node failed = _lifecycle.getNodeByName(AppLifeCycle.FAILED); appentry.setLifeCycleNode(failed); diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/PropertiesConfigurationManager.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/PropertiesConfigurationManager.java index e7708db5b08..4bd3c538afb 100644 --- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/PropertiesConfigurationManager.java +++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/PropertiesConfigurationManager.java @@ -20,7 +20,6 @@ package org.eclipse.jetty.deploy; import java.io.FileNotFoundException; import java.io.IOException; -import java.net.MalformedURLException; import java.util.HashMap; import java.util.Map; import java.util.Properties; @@ -29,6 +28,7 @@ import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedOperation; import org.eclipse.jetty.util.annotation.Name; +import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.resource.Resource; /** @@ -37,21 +37,33 @@ import org.eclipse.jetty.util.resource.Resource; * Supplies properties defined in a file. */ @ManagedObject("Configure deployed webapps via properties") -public class PropertiesConfigurationManager implements ConfigurationManager +public class PropertiesConfigurationManager implements ConfigurationManager, Dumpable { private String _properties; - private final Map _map = new HashMap(); + private final Map _map = new HashMap<>(); public PropertiesConfigurationManager(String properties) { + if (properties != null) + { + try + { + setFile(properties); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } } public PropertiesConfigurationManager() { + this(null); } @ManagedAttribute("A file or URL of properties") - public void setFile(String resource) throws MalformedURLException, IOException + public void setFile(String resource) throws IOException { _properties = resource; _map.clear(); @@ -75,7 +87,7 @@ public class PropertiesConfigurationManager implements ConfigurationManager @Override public Map getProperties() { - return new HashMap<>(_map); + return _map; } private void loadProperties(String resource) throws FileNotFoundException, IOException @@ -86,9 +98,25 @@ public class PropertiesConfigurationManager implements ConfigurationManager Properties properties = new Properties(); properties.load(file.getInputStream()); for (Map.Entry entry : properties.entrySet()) - { _map.put(entry.getKey().toString(), String.valueOf(entry.getValue())); - } } } + + @Override + public String toString() + { + return String.format("%s@%x{%s}", this.getClass(), hashCode(), _properties); + } + + @Override + public String dump() + { + return Dumpable.dump(this); + } + + @Override + public void dump(Appendable out, String indent) throws IOException + { + Dumpable.dumpObjects(out, indent, toString(), _map); + } } diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBinding.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBinding.java index 17f8353a26a..b57afd183c1 100644 --- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBinding.java +++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBinding.java @@ -20,7 +20,9 @@ package org.eclipse.jetty.deploy.bindings; import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppLifeCycle; +import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.graph.Node; +import org.eclipse.jetty.deploy.providers.WebAppProvider; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -63,7 +65,7 @@ public class GlobalWebappConfigBinding implements AppLifeCycle.Binding { return new String[]{"deploying"}; } - + @Override public void processBinding(Node node, App app) throws Exception { @@ -94,6 +96,13 @@ public class GlobalWebappConfigBinding implements AppLifeCycle.Binding XmlConfiguration jettyXmlConfig = new XmlConfiguration(globalContextSettings); Resource resource = Resource.newResource(app.getOriginId()); app.getDeploymentManager().scope(jettyXmlConfig, resource); + AppProvider appProvider = app.getAppProvider(); + if (appProvider instanceof WebAppProvider) + { + WebAppProvider webAppProvider = ((WebAppProvider)appProvider); + if (webAppProvider.getConfigurationManager() != null) + jettyXmlConfig.getProperties().putAll(webAppProvider.getConfigurationManager().getProperties()); + } WebAppClassLoader.runWithServerClassAccess(() -> { jettyXmlConfig.configure(context); diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java index 0507816c76b..dae686bf0ea 100644 --- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java +++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java @@ -36,7 +36,7 @@ import org.eclipse.jetty.util.Scanner; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedOperation; -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; import org.eclipse.jetty.util.resource.Resource; @@ -45,7 +45,7 @@ import org.eclipse.jetty.util.resource.Resource; * */ @ManagedObject("Abstract Provider for loading webapps") -public abstract class ScanningAppProvider extends AbstractLifeCycle implements AppProvider +public abstract class ScanningAppProvider extends ContainerLifeCycle implements AppProvider { private static final Logger LOG = Log.getLogger(ScanningAppProvider.class); @@ -81,11 +81,13 @@ public abstract class ScanningAppProvider extends AbstractLifeCycle implements A protected ScanningAppProvider() { + this(null); } protected ScanningAppProvider(FilenameFilter filter) { _filenameFilter = filter; + addBean(_appMap); } protected void setFilenameFilter(FilenameFilter filter) @@ -142,15 +144,19 @@ public abstract class ScanningAppProvider extends AbstractLifeCycle implements A _scanner.setFilenameFilter(_filenameFilter); _scanner.setReportDirs(true); _scanner.addListener(_scannerListener); - _scanner.start(); + + addBean(_scanner); + + super.doStart(); } @Override protected void doStop() throws Exception { + super.doStop(); if (_scanner != null) { - _scanner.stop(); + removeBean(_scanner); _scanner.removeListener(_scannerListener); _scanner = null; } @@ -307,4 +313,10 @@ public abstract class ScanningAppProvider extends AbstractLifeCycle implements A ); _scanner.scan(); } + + @Override + public String toString() + { + return String.format("%s@%x%s", this.getClass(), hashCode(), _monitored); + } } diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java index 5217e862ce9..8c26d9e4da8 100644 --- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java +++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java @@ -206,6 +206,7 @@ public class WebAppProvider extends ScanningAppProvider */ public void setConfigurationManager(ConfigurationManager configurationManager) { + updateBean(_configurationManager, configurationManager); _configurationManager = configurationManager; } diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyEffectiveWebXml.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyEffectiveWebXml.java index 9749cb16c04..ca2f83df9aa 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyEffectiveWebXml.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyEffectiveWebXml.java @@ -28,8 +28,9 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.eclipse.jetty.annotations.AnnotationConfiguration; +import org.eclipse.jetty.quickstart.QuickStartConfiguration; +import org.eclipse.jetty.quickstart.QuickStartConfiguration.Mode; import org.eclipse.jetty.util.IO; -import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.thread.QueuedThreadPool; @@ -92,30 +93,23 @@ public class JettyEffectiveWebXml extends JettyRunMojo configureWebApplication(); //set the webapp up to do very little other than generate the quickstart-web.xml + if (effectiveWebXml == null) + { + deleteOnExit = true; + effectiveWebXml = new File(target, "effective-web.xml"); + effectiveWebXml.deleteOnExit(); + } + Resource descriptor = Resource.newResource(effectiveWebXml); + if (!effectiveWebXml.getParentFile().exists()) + effectiveWebXml.getParentFile().mkdirs(); + if (!effectiveWebXml.exists()) + effectiveWebXml.createNewFile(); + webApp.setCopyWebDir(false); webApp.setCopyWebInf(false); - webApp.setGenerateQuickStart(true); - - //if the user didn't nominate a file to generate into, pick the name and - //make sure that it is deleted on exit - if (webApp.getQuickStartWebDescriptor() == null) - { - if (effectiveWebXml == null) - { - deleteOnExit = true; - effectiveWebXml = new File(target, "effective-web.xml"); - effectiveWebXml.deleteOnExit(); - } - - Resource descriptor = Resource.newResource(effectiveWebXml); - - if (!effectiveWebXml.getParentFile().exists()) - effectiveWebXml.getParentFile().mkdirs(); - if (!effectiveWebXml.exists()) - effectiveWebXml.createNewFile(); - - webApp.setQuickStartWebDescriptor(descriptor); - } + webApp.addConfiguration(new QuickStartConfiguration()); + webApp.setAttribute(QuickStartConfiguration.MODE, Mode.GENERATE); + webApp.setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, descriptor); ServerSupport.addWebApplication(server, webApp); @@ -158,7 +152,7 @@ public class JettyEffectiveWebXml extends JettyRunMojo try { //just show the result in the log - getLog().info(IO.toString(webApp.getQuickStartWebDescriptor().getInputStream())); + getLog().info(IO.toString(((Resource)webApp.getAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML)).getInputStream())); } catch (Exception e) { diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java index cd255b6962c..feada7f24e0 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java @@ -43,6 +43,8 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.eclipse.jetty.annotations.AnnotationConfiguration; +import org.eclipse.jetty.quickstart.QuickStartConfiguration; +import org.eclipse.jetty.quickstart.QuickStartConfiguration.Mode; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.resource.Resource; @@ -209,22 +211,19 @@ public class JettyRunForkedMojo extends JettyRunMojo configureWebApplication(); //set the webapp up to do very little other than generate the quickstart-web.xml + if (forkWebXml == null) + forkWebXml = new File(target, "fork-web.xml"); + + if (!forkWebXml.getParentFile().exists()) + forkWebXml.getParentFile().mkdirs(); + if (!forkWebXml.exists()) + forkWebXml.createNewFile(); + + webApp.addConfiguration(new MavenQuickStartConfiguration()); + webApp.setAttribute(QuickStartConfiguration.MODE, Mode.GENERATE); + webApp.setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, Resource.newResource(forkWebXml)); webApp.setCopyWebDir(false); - webApp.setCopyWebInf(false); - webApp.setGenerateQuickStart(true); - - if (webApp.getQuickStartWebDescriptor() == null) - { - if (forkWebXml == null) - forkWebXml = new File(target, "fork-web.xml"); - - if (!forkWebXml.getParentFile().exists()) - forkWebXml.getParentFile().mkdirs(); - if (!forkWebXml.exists()) - forkWebXml.createNewFile(); - - webApp.setQuickStartWebDescriptor(Resource.newResource(forkWebXml)); - } + webApp.setCopyWebInf(false); //add webapp to our fake server instance ServerSupport.addWebApplication(server, webApp); @@ -240,11 +239,10 @@ public class JettyRunForkedMojo extends JettyRunMojo //leave everything unpacked for the forked process to use webApp.setPersistTempDirectory(true); + File props = null; webApp.start(); //just enough to generate the quickstart - //save config of the webapp BEFORE we stop - final File props = prepareConfiguration(); - + props = prepareConfiguration(); webApp.stop(); if (tpool != null) @@ -311,7 +309,7 @@ public class JettyRunForkedMojo extends JettyRunMojo if (PluginLog.getLog().isDebugEnabled()) PluginLog.getLog().debug("Forked cli:" + Arrays.toString(cmd.toArray())); - + PluginLog.getLog().info("Forked process starting"); //set up extra environment vars if there are any diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java index 79c8509ad70..8e765504b1e 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java @@ -34,7 +34,6 @@ import org.eclipse.jetty.annotations.AnnotationConfiguration; import org.eclipse.jetty.plus.webapp.EnvConfiguration; import org.eclipse.jetty.plus.webapp.PlusConfiguration; import org.eclipse.jetty.quickstart.QuickStartConfiguration; -import org.eclipse.jetty.quickstart.QuickStartConfiguration.Mode; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.FilterMapping; import org.eclipse.jetty.servlet.ServletHolder; @@ -105,8 +104,6 @@ public class JettyWebAppContext extends WebAppContext */ private boolean _baseAppFirst = true; - private boolean _isGenerateQuickStart; - public JettyWebAppContext() throws Exception { super(); @@ -117,6 +114,8 @@ public class JettyWebAppContext extends WebAppContext addConfiguration(new EnvConfiguration()); addConfiguration(new PlusConfiguration()); addConfiguration(new AnnotationConfiguration()); + + setAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE, "origin"); } public void setContainerIncludeJarPattern(String pattern) @@ -210,27 +209,6 @@ public class JettyWebAppContext extends WebAppContext return attr == null ? null : attr.toString(); } - /** - * Toggle whether or not the origin attribute will be generated into the - * xml. - * - * @param generateOrigin if true then the origin of each xml element is - * added, otherwise it is omitted. - */ - public void setGenerateOrigin(boolean generateOrigin) - { - setAttribute(QuickStartConfiguration.GENERATE_ORIGIN, generateOrigin); - } - - /** - * @return true if the origin attribute will be generated, false otherwise - */ - public boolean isGenerateOrigin() - { - Object attr = getAttribute(QuickStartConfiguration.GENERATE_ORIGIN); - return attr == null ? false : Boolean.valueOf(attr.toString()); - } - public List getOverlays() { return _overlays; @@ -246,35 +224,6 @@ public class JettyWebAppContext extends WebAppContext return _baseAppFirst; } - /** - * Set the file to use into which to generate the quickstart output. - * - * @param quickStartWebXml the full path to the file to use - */ - public void setQuickStartWebDescriptor(String quickStartWebXml) throws Exception - { - setQuickStartWebDescriptor(Resource.newResource(quickStartWebXml)); - } - - /** - * Set the Resource to use into which to generate the quickstart output. - */ - protected void setQuickStartWebDescriptor(Resource quickStartWebXml) - { - setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, quickStartWebXml.toString()); - } - - public Resource getQuickStartWebDescriptor() throws Exception - { - Object o = getAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML); - if (o == null) - return null; - else if (o instanceof Resource) - return (Resource)o; - else - return Resource.newResource((String)o); - } - /** * This method is provided as a convenience for jetty maven plugin * configuration @@ -307,41 +256,9 @@ public class JettyWebAppContext extends WebAppContext return _webInfClasses; } - /** - * If true, a quickstart for the webapp is generated. - * - * @param quickStart if true the quickstart is generated, false otherwise - */ - public void setGenerateQuickStart(boolean quickStart) - { - _isGenerateQuickStart = quickStart; - } - - public boolean isGenerateQuickStart() - { - return _isGenerateQuickStart; - } - @Override public void doStart() throws Exception { - - // choose if this will be a quickstart or normal start - if (!isGenerateQuickStart() && getQuickStartWebDescriptor() != null) - { - MavenQuickStartConfiguration quickStart = new MavenQuickStartConfiguration(); - quickStart.setMode(Mode.QUICKSTART); - quickStart.setQuickStartWebXml(getQuickStartWebDescriptor()); - addConfiguration(quickStart); - } - else if (isGenerateQuickStart()) - { - MavenQuickStartConfiguration quickStart = new MavenQuickStartConfiguration(); - quickStart.setMode(Mode.GENERATE); - quickStart.setQuickStartWebXml(getQuickStartWebDescriptor()); - addConfiguration(quickStart); - } - // Set up the pattern that tells us where the jars are that need // scanning diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenQuickStartConfiguration.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenQuickStartConfiguration.java index 597c313555e..63d643a7a24 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenQuickStartConfiguration.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenQuickStartConfiguration.java @@ -18,15 +18,12 @@ package org.eclipse.jetty.maven.plugin; -import java.io.File; - import org.eclipse.jetty.quickstart.QuickStartConfiguration; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceCollection; -import org.eclipse.jetty.webapp.WebAppClassLoader; import org.eclipse.jetty.webapp.WebAppContext; /** @@ -36,56 +33,6 @@ public class MavenQuickStartConfiguration extends QuickStartConfiguration { private static final Logger LOG = Log.getLogger(QuickStartConfiguration.class); - private Resource _quickStartWebXml; //the descriptor to use for starting/generating quickstart - - public void setQuickStartWebXml(Resource quickStartWebXml) - { - _quickStartWebXml = quickStartWebXml; - } - - @Override - public Resource getQuickStartWebXml(WebAppContext context) throws Exception - { - if (_quickStartWebXml == null) - return super.getQuickStartWebXml(context); - - return _quickStartWebXml; - } - - @Override - public void preConfigure(WebAppContext context) throws Exception - { - //check that webapp is suitable for quick start - if (context.getBaseResource() == null) - throw new IllegalStateException("No location for webapp"); - - //look for quickstart-web.xml in WEB-INF of webapp - Resource quickStartWebXml = getQuickStartWebXml(context); - if (LOG.isDebugEnabled()) - LOG.debug("quickStartWebXml={}", quickStartWebXml); - super.preConfigure(context); - } - - @Override - public void configure(WebAppContext context) throws Exception - { - JettyWebAppContext jwac = (JettyWebAppContext)context; - - //put the classes dir and all dependencies into the classpath - if (jwac.getClassPathFiles() != null) - { - if (LOG.isDebugEnabled()) - LOG.debug("Setting up classpath ..."); - for (File classPathFile : jwac.getClassPathFiles()) - { - ((WebAppClassLoader)context.getClassLoader()).addClassPath(classPathFile.getCanonicalPath()); - } - } - - //Set up the quickstart environment for the context - super.configure(context); - } - @Override public void deconfigure(WebAppContext context) throws Exception { diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java index 7d76d8124a5..9c25a5c8328 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.eclipse.jetty.quickstart.QuickStartConfiguration; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ShutdownMonitor; @@ -71,14 +72,9 @@ public class Starter //configure webapp from properties file describing unassembled webapp configureWebApp(); - - //make it a quickstart if the quickstart-web.xml file exists - if (webApp.getTempDirectory() != null) - { - File qs = new File(webApp.getTempDirectory(), "quickstart-web.xml"); - if (qs.exists() && qs.isFile()) - webApp.setQuickStartWebDescriptor(Resource.newResource(qs)); - } + + webApp.addConfiguration(new QuickStartConfiguration()); + webApp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); ServerSupport.addWebApplication(server, webApp); @@ -232,7 +228,9 @@ public class Starter public static final void main(String[] args) { if (args == null) + { System.exit(1); + } Starter starter = null; try diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/WebAppPropertyConverter.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/WebAppPropertyConverter.java index d5ca0c745e6..fed62949ce8 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/WebAppPropertyConverter.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/WebAppPropertyConverter.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import org.eclipse.jetty.quickstart.QuickStartConfiguration; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.resource.Resource; @@ -71,9 +72,10 @@ public class WebAppPropertyConverter props.put("web.xml", webApp.getDescriptor()); } - if (webApp.getQuickStartWebDescriptor() != null) + Object tmp = webApp.getAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML); + if (tmp != null) { - props.put("quickstart.web.xml", webApp.getQuickStartWebDescriptor().getFile().getAbsolutePath()); + props.put("quickstart.web.xml", tmp.toString()); } //sort out the context path @@ -183,11 +185,10 @@ public class WebAppPropertyConverter if (!StringUtil.isBlank(str)) webApp.setDescriptor(str); - //TODO the WebAppStarter class doesn't set up the QUICKSTART_CONFIGURATION_CLASSES, but the Starter class does!!! str = props.getProperty("quickstart.web.xml"); if (!StringUtil.isBlank(str)) { - webApp.setQuickStartWebDescriptor(Resource.newResource(new File(str))); + webApp.setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, Resource.newResource(str)); } // - the tmp directory diff --git a/jetty-quickstart/src/main/config/etc/example-quickstart.xml b/jetty-quickstart/src/main/config/etc/example-quickstart.xml deleted file mode 100644 index c042d89d724..00000000000 --- a/jetty-quickstart/src/main/config/etc/example-quickstart.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - true - / - /application.war - - diff --git a/jetty-quickstart/src/main/config/etc/jetty-quickstart.xml b/jetty-quickstart/src/main/config/etc/jetty-quickstart.xml new file mode 100644 index 00000000000..84d030260b0 --- /dev/null +++ b/jetty-quickstart/src/main/config/etc/jetty-quickstart.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + /etc/quickstart-webapp.xml + + + + + + diff --git a/jetty-quickstart/src/main/config/modules/jetty-quickstart.d/quickstart-webapp.xml b/jetty-quickstart/src/main/config/modules/jetty-quickstart.d/quickstart-webapp.xml new file mode 100644 index 00000000000..b0f07545e95 --- /dev/null +++ b/jetty-quickstart/src/main/config/modules/jetty-quickstart.d/quickstart-webapp.xml @@ -0,0 +1,28 @@ + + + + + + org.eclipse.jetty.quickstart.origin + + + + + org.eclipse.jetty.quickstart.xml + + + + + org.eclipse.jetty.quickstart.mode + + + + + + + + true + false + false + + diff --git a/jetty-quickstart/src/main/config/modules/quickstart.mod b/jetty-quickstart/src/main/config/modules/quickstart.mod index 102801714b6..c531ea648d0 100644 --- a/jetty-quickstart/src/main/config/modules/quickstart.mod +++ b/jetty-quickstart/src/main/config/modules/quickstart.mod @@ -6,8 +6,21 @@ deployment of preconfigured webapplications. [depend] server -plus -annotations +deploy [lib] lib/jetty-quickstart-${jetty.version}.jar + +[xml] +etc/jetty-quickstart.xml + +[files] +basehome:modules/jetty-quickstart.d/quickstart-webapp.xml|etc/quickstart-webapp.xml + + +[ini-template] + +# Modes are AUTO, GENERATE, QUICKSTART +# jetty.quickstart.mode=AUTO +# jetty.quickstart.origin=origin +# jetty.quickstart.xml= diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.java index 627f676588a..92814695c33 100644 --- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.java +++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.java @@ -20,11 +20,15 @@ package org.eclipse.jetty.quickstart; import java.util.Locale; +import org.eclipse.jetty.annotations.AnnotationConfiguration; +import org.eclipse.jetty.plus.webapp.EnvConfiguration; +import org.eclipse.jetty.plus.webapp.PlusConfiguration; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.JarResource; import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.xml.XmlConfiguration; public class PreconfigureQuickStartWar @@ -98,7 +102,13 @@ public class PreconfigureQuickStartWar final Server server = new Server(); - QuickStartWebApp webapp = new QuickStartWebApp(); + WebAppContext webapp = new WebAppContext(); + webapp.addConfiguration(new QuickStartConfiguration(), + new EnvConfiguration(), + new PlusConfiguration(), + new AnnotationConfiguration()); + webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.GENERATE); + webapp.setAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE, ""); if (xml != null) { @@ -108,10 +118,21 @@ public class PreconfigureQuickStartWar xmlConfiguration.configure(webapp); } webapp.setResourceBase(dir.getFile().getAbsolutePath()); - webapp.setMode(QuickStartConfiguration.Mode.GENERATE); server.setHandler(webapp); - server.start(); - server.stop(); + try + { + server.setDryRun(true); + server.start(); + } + catch (Exception e) + { + throw e; + } + finally + { + if (!server.isStopped()) + server.stop(); + } } private static void error(String message) diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java index 2462ad4e326..bc69cf9fa39 100644 --- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java +++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java @@ -18,6 +18,7 @@ package org.eclipse.jetty.quickstart; +import java.io.File; import java.io.IOException; import java.util.HashSet; import java.util.Set; @@ -26,6 +27,8 @@ import java.util.stream.Collectors; import org.eclipse.jetty.annotations.AnnotationConfiguration; import org.eclipse.jetty.annotations.AnnotationDecorator; import org.eclipse.jetty.annotations.ServletContainerInitializersStarter; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; @@ -39,17 +42,16 @@ import org.eclipse.jetty.webapp.WebXmlConfiguration; /** * QuickStartConfiguration *

    - * Re-inflate a deployable webapp from a saved effective-web.xml - * which combines all pre-parsed web xml descriptors and annotations. + * Prepare for quickstart generation, or usage. */ public class QuickStartConfiguration extends AbstractConfiguration { private static final Logger LOG = Log.getLogger(QuickStartConfiguration.class); public static final Set> __replacedConfigurations = new HashSet<>(); - public static final String ORIGIN_ATTRIBUTE = "org.eclipse.jetty.quickstart.ORIGIN_ATTRIBUTE"; - public static final String GENERATE_ORIGIN = "org.eclipse.jetty.quickstart.GENERATE_ORIGIN"; - public static final String QUICKSTART_WEB_XML = "org.eclipse.jetty.quickstart.QUICKSTART_WEB_XML"; + public static final String ORIGIN_ATTRIBUTE = "org.eclipse.jetty.quickstart.origin"; + public static final String QUICKSTART_WEB_XML = "org.eclipse.jetty.quickstart.xml"; + public static final String MODE = "org.eclipse.jetty.quickstart.mode"; static { @@ -59,41 +61,35 @@ public class QuickStartConfiguration extends AbstractConfiguration __replacedConfigurations.add(org.eclipse.jetty.annotations.AnnotationConfiguration.class); } - ; + /** Configure the server for the quickstart mode. + *

    In practise this means calling server.setDryRun(true) for GENERATE mode

    + * @see Server#setDryRun(boolean) + * @param server The server to configure + * @param mode The quickstart mode + */ + public static void configureMode(Server server, String mode) + { + if (mode != null && Mode.valueOf(mode) == Mode.GENERATE) + server.setDryRun(true); + } public enum Mode { - DISABLED, // No Quick start GENERATE, // Generate quickstart-web.xml and then stop AUTO, // use or generate depending on the existance of quickstart-web.xml QUICKSTART // Use quickstart-web.xml } - ; - private Mode _mode = Mode.AUTO; private boolean _quickStart; public QuickStartConfiguration() { - super(true); + super(false); addDependencies(WebInfConfiguration.class); addDependents(WebXmlConfiguration.class); } - public void setMode(Mode mode) - { - _mode = mode; - } - - public Mode getMode() - { - return _mode; - } - - /** - * @see org.eclipse.jetty.webapp.AbstractConfiguration#preConfigure(org.eclipse.jetty.webapp.WebAppContext) - */ @Override public void preConfigure(WebAppContext context) throws Exception { @@ -106,39 +102,46 @@ public class QuickStartConfiguration extends AbstractConfiguration Resource quickStartWebXml = getQuickStartWebXml(context); LOG.debug("quickStartWebXml={} exists={}", quickStartWebXml, quickStartWebXml.exists()); + //Get the mode + Mode mode = (Mode)context.getAttribute(MODE); + if (mode != null) + _mode = mode; + _quickStart = false; + switch (_mode) { - case DISABLED: - super.preConfigure(context); - break; - case GENERATE: { + if (quickStartWebXml.exists()) + LOG.info("Regenerating {}", quickStartWebXml); + else + LOG.info("Generating {}", quickStartWebXml); + super.preConfigure(context); + //generate the quickstart file then abort QuickStartGeneratorConfiguration generator = new QuickStartGeneratorConfiguration(true); configure(generator, context); context.addConfiguration(generator); break; } - case AUTO: { if (quickStartWebXml.exists()) - quickStart(context, quickStartWebXml); + { + quickStart(context); + } else { + if (LOG.isDebugEnabled()) + LOG.debug("No quickstart xml file, starting webapp {} normally", context); super.preConfigure(context); - QuickStartGeneratorConfiguration generator = new QuickStartGeneratorConfiguration(false); - configure(generator, context); - context.addConfiguration(generator); } break; } - case QUICKSTART: if (quickStartWebXml.exists()) - quickStart(context, quickStartWebXml); + quickStart(context); else throw new IllegalStateException("No " + quickStartWebXml); break; @@ -151,27 +154,20 @@ public class QuickStartConfiguration extends AbstractConfiguration protected void configure(QuickStartGeneratorConfiguration generator, WebAppContext context) throws IOException { Object attr; - attr = context.getAttribute(GENERATE_ORIGIN); - if (attr != null) - generator.setGenerateOrigin(Boolean.valueOf(attr.toString())); attr = context.getAttribute(ORIGIN_ATTRIBUTE); if (attr != null) generator.setOriginAttribute(attr.toString()); - attr = context.getAttribute(QUICKSTART_WEB_XML); - if (attr instanceof Resource) - generator.setQuickStartWebXml((Resource)attr); - else if (attr != null) - generator.setQuickStartWebXml(Resource.newResource(attr.toString())); + + generator.setQuickStartWebXml((Resource)context.getAttribute(QUICKSTART_WEB_XML)); } - /** - * @see org.eclipse.jetty.webapp.AbstractConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext) - */ @Override public void configure(WebAppContext context) throws Exception { if (!_quickStart) + { super.configure(context); + } else { //add the processor to handle normal web.xml content @@ -195,14 +191,15 @@ public class QuickStartConfiguration extends AbstractConfiguration } } - protected void quickStart(WebAppContext context, Resource quickStartWebXml) + protected void quickStart(WebAppContext context) throws Exception { + LOG.info("Quickstarting {}", context); _quickStart = true; context.setConfigurations(context.getWebAppConfigurations().stream() .filter(c -> !__replacedConfigurations.contains(c.replaces()) && !__replacedConfigurations.contains(c.getClass())) .collect(Collectors.toList()).toArray(new Configuration[]{})); - context.getMetaData().setWebXml(quickStartWebXml); + context.getMetaData().setWebXml((Resource)context.getAttribute(QUICKSTART_WEB_XML)); context.getServletContext().setEffectiveMajorVersion(context.getMetaData().getWebXml().getMajorVersion()); context.getServletContext().setEffectiveMinorVersion(context.getMetaData().getWebXml().getMinorVersion()); } @@ -216,12 +213,38 @@ public class QuickStartConfiguration extends AbstractConfiguration */ public Resource getQuickStartWebXml(WebAppContext context) throws Exception { + Object attr = context.getAttribute(QUICKSTART_WEB_XML); + if (attr instanceof Resource) + return (Resource)attr; + Resource webInf = context.getWebInf(); if (webInf == null || !webInf.exists()) - throw new IllegalStateException("No WEB-INF"); - LOG.debug("webinf={}", webInf); + { + File tmp = new File(context.getBaseResource().getFile(), "WEB-INF"); + tmp.mkdirs(); + webInf = context.getWebInf(); + } - Resource quickStartWebXml = webInf.addPath("quickstart-web.xml"); - return quickStartWebXml; + Resource qstart; + if (attr == null || StringUtil.isBlank(attr.toString())) + { + qstart = webInf.addPath("quickstart-web.xml"); + } + else + { + try + { + // Try a relative resolution + qstart = Resource.newResource(webInf.getFile().toPath().resolve(attr.toString())); + } + catch (Throwable th) + { + // try as a resource + qstart = (Resource.newResource(attr.toString())); + } + context.setAttribute(QUICKSTART_WEB_XML, qstart); + } + context.setAttribute(QUICKSTART_WEB_XML, qstart); + return qstart; } } diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartGeneratorConfiguration.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartGeneratorConfiguration.java index 20cfab4535b..753f32dce0d 100644 --- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartGeneratorConfiguration.java +++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartGeneratorConfiguration.java @@ -52,6 +52,7 @@ import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletMapping; import org.eclipse.jetty.util.QuotedStringTokenizer; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; @@ -61,10 +62,11 @@ import org.eclipse.jetty.webapp.MetaData; import org.eclipse.jetty.webapp.MetaData.OriginInfo; import org.eclipse.jetty.webapp.MetaInfConfiguration; import org.eclipse.jetty.webapp.WebAppContext; +import org.eclipse.jetty.webapp.WebInfConfiguration; import org.eclipse.jetty.xml.XmlAppendable; /** - * QuickStartDescriptorGenerator + * QuickStartGeneratorConfiguration *

    * Generate an effective web.xml from a WebAppContext, including all components * from web.xml, web-fragment.xmls annotations etc. @@ -81,10 +83,9 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration protected final boolean _abort; protected String _originAttribute; - protected boolean _generateOrigin; protected int _count; protected Resource _quickStartWebXml; - + public QuickStartGeneratorConfiguration() { this(false); @@ -116,22 +117,6 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration return _originAttribute; } - /** - * @return the generateOrigin - */ - public boolean isGenerateOrigin() - { - return _generateOrigin; - } - - /** - * @param generateOrigin the generateOrigin to set - */ - public void setGenerateOrigin(boolean generateOrigin) - { - _generateOrigin = generateOrigin; - } - public Resource getQuickStartWebXml() { return _quickStartWebXml; @@ -163,8 +148,6 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration if (context.getBaseResource() == null) throw new IllegalArgumentException("No base resource for " + this); - LOG.info("Quickstart generating"); - MetaData md = context.getMetaData(); Map webappAttr = new HashMap<>(); @@ -195,13 +178,13 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration //the META-INF/resources discovered addContextParamFromAttribute(context, out, MetaInfConfiguration.METAINF_RESOURCES, normalizer); - // the default-context-path, if presernt + // the default-context-path, if present String defaultContextPath = (String)context.getAttribute("default-context-path"); if (defaultContextPath != null) out.tag("default-context-path", defaultContextPath); //add the name of the origin attribute, if it is being used - if (_generateOrigin) + if (StringUtil.isNotBlank(_originAttribute)) { out.openTag("context-param") .tag("param-name", ORIGIN) @@ -766,7 +749,7 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration */ public Map origin(MetaData md, String name) { - if (!(_generateOrigin || LOG.isDebugEnabled())) + if (StringUtil.isBlank(_originAttribute)) return Collections.emptyMap(); if (name == null) return Collections.emptyMap(); @@ -792,13 +775,19 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration { MetaData metadata = context.getMetaData(); metadata.resolve(context); - - Resource quickStartWebXml = _quickStartWebXml; - if (_quickStartWebXml == null) - quickStartWebXml = context.getWebInf().addPath("/quickstart-web.xml"); - try (FileOutputStream fos = new FileOutputStream(quickStartWebXml.getFile(), false)) + try (FileOutputStream fos = new FileOutputStream(_quickStartWebXml.getFile(), false)) { generateQuickStartWebXml(context, fos); + LOG.info("Generated {}", _quickStartWebXml); + if (context.getAttribute(WebInfConfiguration.TEMPORARY_RESOURCE_BASE) != null && !context.isPersistTempDirectory()) + LOG.warn("Generated to non persistent location: " + _quickStartWebXml); } } + + @Override + public void deconfigure(WebAppContext context) throws Exception + { + super.deconfigure(context); + } + } diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartWebApp.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartWebApp.java deleted file mode 100644 index 0cf95f8f1b5..00000000000 --- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartWebApp.java +++ /dev/null @@ -1,90 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2019 Mort Bay Consulting 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.quickstart; - -import org.eclipse.jetty.annotations.AnnotationConfiguration; -import org.eclipse.jetty.plus.webapp.EnvConfiguration; -import org.eclipse.jetty.plus.webapp.PlusConfiguration; -import org.eclipse.jetty.quickstart.QuickStartConfiguration.Mode; -import org.eclipse.jetty.webapp.WebAppContext; - -/** - * QuickStartWar - */ -public class QuickStartWebApp extends WebAppContext -{ - private final QuickStartConfiguration _quickStartConfiguration; - - private String _originAttribute; - private boolean _generateOrigin; - - public QuickStartWebApp() - { - super(); - addConfiguration( - _quickStartConfiguration = new QuickStartConfiguration(), - new EnvConfiguration(), - new PlusConfiguration(), - new AnnotationConfiguration()); - setExtractWAR(true); - setCopyWebDir(false); - setCopyWebInf(false); - } - - public void setOriginAttribute(String name) - { - setAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE, name); - } - - /** - * @return the originAttribute - */ - public String getOriginAttribute() - { - Object attr = getAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE); - return attr == null ? null : attr.toString(); - } - - /** - * @param generateOrigin the generateOrigin to set - */ - public void setGenerateOrigin(boolean generateOrigin) - { - setAttribute(QuickStartConfiguration.GENERATE_ORIGIN, generateOrigin); - } - - /** - * @return the generateOrigin - */ - public boolean isGenerateOrigin() - { - Object attr = getAttribute(QuickStartConfiguration.GENERATE_ORIGIN); - return attr == null ? false : Boolean.valueOf(attr.toString()); - } - - public Mode getMode() - { - return _quickStartConfiguration.getMode(); - } - - public void setMode(Mode mode) - { - _quickStartConfiguration.setMode(mode); - } -} diff --git a/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/TestQuickStart.java b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/TestQuickStart.java index 6e7b3ed3834..82e0c376f8f 100644 --- a/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/TestQuickStart.java +++ b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/TestQuickStart.java @@ -27,6 +27,7 @@ import org.eclipse.jetty.servlet.ListenerHolder; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.toolchain.test.FS; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.webapp.WebAppContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -61,10 +62,11 @@ public class TestQuickStart Server server = new Server(); //generate a quickstart-web.xml - QuickStartWebApp quickstart = new QuickStartWebApp(); + WebAppContext quickstart = new WebAppContext(); + quickstart.addConfiguration(new QuickStartConfiguration()); + quickstart.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.GENERATE); + quickstart.setAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE, "origin"); quickstart.setResourceBase(testDir.getAbsolutePath()); - quickstart.setMode(QuickStartConfiguration.Mode.GENERATE); - quickstart.setGenerateOrigin(true); ServletHolder fooHolder = new ServletHolder(); fooHolder.setServlet(new FooServlet()); fooHolder.setName("foo"); @@ -73,19 +75,22 @@ public class TestQuickStart lholder.setListener(new FooContextListener()); quickstart.getServletHandler().addListener(lholder); server.setHandler(quickstart); + server.setDryRun(true); server.start(); - server.stop(); assertTrue(quickstartXml.exists()); //now run the webapp again purely from the generated quickstart - QuickStartWebApp webapp = new QuickStartWebApp(); + WebAppContext webapp = new WebAppContext(); webapp.setResourceBase(testDir.getAbsolutePath()); - webapp.setMode(QuickStartConfiguration.Mode.QUICKSTART); + webapp.addConfiguration(new QuickStartConfiguration()); + webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); webapp.setClassLoader(new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader())); server.setHandler(webapp); + server.setDryRun(false); server.start(); + server.dumpStdErr(); //verify that FooServlet is now mapped to / and not the DefaultServlet ServletHolder sh = webapp.getServletHandler().getMappedServlet("/").getResource(); @@ -104,28 +109,30 @@ public class TestQuickStart Server server = new Server(); // generate a quickstart-web.xml - QuickStartWebApp quickstart = new QuickStartWebApp(); + WebAppContext quickstart = new WebAppContext(); quickstart.setResourceBase(testDir.getAbsolutePath()); - quickstart.setMode(QuickStartConfiguration.Mode.GENERATE); - quickstart.setGenerateOrigin(true); + quickstart.addConfiguration(new QuickStartConfiguration()); + quickstart.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.GENERATE); + quickstart.setAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE, "origin"); quickstart.setDescriptor(MavenTestingUtils.getTestResourceFile("web.xml").getAbsolutePath()); quickstart.setContextPath("/foo"); server.setHandler(quickstart); + server.setDryRun(true); server.start(); - assertEquals("/foo", quickstart.getContextPath()); assertFalse(quickstart.isContextPathDefault()); - server.stop(); assertTrue(quickstartXml.exists()); // quick start - QuickStartWebApp webapp = new QuickStartWebApp(); + WebAppContext webapp = new WebAppContext(); + webapp.addConfiguration(new QuickStartConfiguration()); + quickstart.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); webapp.setResourceBase(testDir.getAbsolutePath()); - webapp.setMode(QuickStartConfiguration.Mode.QUICKSTART); webapp.setClassLoader(new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader())); server.setHandler(webapp); + server.setDryRun(false); server.start(); // verify the context path is the default-context-path diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java index 282baf968c1..b1a7e0a8ba4 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java @@ -79,10 +79,11 @@ public class Server extends HandlerWrapper implements Attributes private final List _connectors = new CopyOnWriteArrayList<>(); private SessionIdManager _sessionIdManager; private boolean _stopAtShutdown; - private boolean _dumpAfterStart = false; - private boolean _dumpBeforeStop = false; + private boolean _dumpAfterStart; + private boolean _dumpBeforeStop; private ErrorHandler _errorHandler; private RequestLog _requestLog; + private boolean _dryRun; private final Locker _dateLocker = new Locker(); private volatile DateField _dateField; @@ -131,6 +132,16 @@ public class Server extends HandlerWrapper implements Attributes setServer(this); } + public boolean isDryRun() + { + return _dryRun; + } + + public void setDryRun(boolean dryRun) + { + _dryRun = dryRun; + } + public RequestLog getRequestLog() { return _requestLog; @@ -367,25 +378,33 @@ public class Server extends HandlerWrapper implements Attributes MultiException mex = new MultiException(); // Open network connector to ensure ports are available - _connectors.stream().filter(NetworkConnector.class::isInstance).map(NetworkConnector.class::cast).forEach(connector -> + if (!_dryRun) { - try + _connectors.stream().filter(NetworkConnector.class::isInstance).map(NetworkConnector.class::cast).forEach(connector -> { - connector.open(); - } - catch (Throwable th) - { - mex.add(th); - } - }); - - // Throw now if verified start sequence and there was an open exception - mex.ifExceptionThrow(); + try + { + connector.open(); + } + catch (Throwable th) + { + mex.add(th); + } + }); + // Throw now if verified start sequence and there was an open exception + mex.ifExceptionThrow(); + } // Start the server and components, but not connectors! // #start(LifeCycle) is overridden so that connectors are not started super.doStart(); + if (_dryRun) + { + LOG.info(String.format("Started(dry run) %s @%dms", this, Uptime.getUptime())); + throw new StopException(); + } + // start connectors for (Connector connector : _connectors) { @@ -400,9 +419,8 @@ public class Server extends HandlerWrapper implements Attributes _connectors.stream().filter(LifeCycle::isRunning).map(Object.class::cast).forEach(LifeCycle::stop); } } - mex.ifExceptionThrow(); - LOG.info(String.format("Started @%dms", Uptime.getUptime())); + LOG.info(String.format("Started %s @%dms", this, Uptime.getUptime())); } catch (Throwable th) { @@ -423,7 +441,7 @@ public class Server extends HandlerWrapper implements Attributes } finally { - if (isDumpAfterStart()) + if (isDumpAfterStart() && !(_dryRun && isDumpBeforeStop())) dumpStdErr(); } } @@ -442,6 +460,7 @@ public class Server extends HandlerWrapper implements Attributes if (isDumpBeforeStop()) dumpStdErr(); + LOG.info(String.format("Stopped %s", this)); if (LOG.isDebugEnabled()) LOG.debug("doStop {}", this); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandler.java index cb2e2257eb7..b02064d54a3 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandler.java @@ -123,7 +123,7 @@ public abstract class AbstractHandler extends ContainerLifeCycle implements Hand if (_server == server) return; if (isStarted()) - throw new IllegalStateException(STARTED); + throw new IllegalStateException(getState()); _server = server; } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java index e56645f5dcf..5f533d6f549 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java @@ -128,7 +128,7 @@ public abstract class AbstractHandlerContainer extends AbstractHandler implement return; if (isStarted()) - throw new IllegalStateException(STARTED); + throw new IllegalStateException(getState()); super.setServer(server); Handler[] handlers = getHandlers(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java index 4840dbeff33..bd37d9d350d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java @@ -2231,7 +2231,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu @Override public void log(String message, Throwable throwable) { - _logger.warn(message, throwable); + if (throwable == null) + _logger.warn(message); + else + _logger.warn(message, throwable); } /* diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java index dfea9fc93b7..9bbda66b116 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java @@ -82,7 +82,7 @@ public class HandlerCollection extends AbstractHandlerContainer public void setHandlers(Handler[] handlers) { if (!_mutableWhenRunning && isStarted()) - throw new IllegalStateException(STARTED); + throw new IllegalStateException(getState()); while (true) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java index 27a53fecf98..0c0897f5c1f 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java @@ -74,7 +74,7 @@ public class HandlerWrapper extends AbstractHandlerContainer public void setHandler(Handler handler) { if (isStarted()) - throw new IllegalStateException(STARTED); + throw new IllegalStateException(getState()); // check for loops if (handler == this || (handler instanceof HandlerContainer && diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java index a8626a2bc27..75b9aa9f74f 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java @@ -34,27 +34,39 @@ public abstract class AbstractLifeCycle implements LifeCycle { private static final Logger LOG = Log.getLogger(AbstractLifeCycle.class); - public static final String STOPPED = "STOPPED"; - public static final String FAILED = "FAILED"; - public static final String STARTING = "STARTING"; - public static final String STARTED = "STARTED"; - public static final String STOPPING = "STOPPING"; - public static final String RUNNING = "RUNNING"; + enum State + { + STOPPED, + STARTING, + STARTED, + STOPPING, + FAILED + } + + public static final String STOPPED = State.STOPPED.toString(); + public static final String FAILED = State.FAILED.toString(); + public static final String STARTING = State.STARTING.toString(); + public static final String STARTED = State.STARTED.toString(); + public static final String STOPPING = State.STOPPING.toString(); private final CopyOnWriteArrayList _listeners = new CopyOnWriteArrayList(); private final Object _lock = new Object(); - private static final int STATE_FAILED = -1; - private static final int STATE_STOPPED = 0; - private static final int STATE_STARTING = 1; - private static final int STATE_STARTED = 2; - private static final int STATE_STOPPING = 3; - private volatile int _state = STATE_STOPPED; + private volatile State _state = State.STOPPED; private long _stopTimeout = 30000; + /** + * Method to override to start the lifecycle + * @throws StopException If thrown, the lifecycle will immediately be stopped. + * @throws Exception If there was a problem starting. Will cause a transition to FAILED state + */ protected void doStart() throws Exception { } + /** + * Method to override to stop the lifecycle + * @throws Exception If there was a problem stopping. Will cause a transition to FAILED state + */ protected void doStop() throws Exception { } @@ -66,11 +78,31 @@ public abstract class AbstractLifeCycle implements LifeCycle { try { - if (_state == STATE_STARTED || _state == STATE_STARTING) - return; - setStarting(); - doStart(); - setStarted(); + switch (_state) + { + case STARTED: + return; + + case STARTING: + case STOPPING: + throw new IllegalStateException(getState()); + + default: + try + { + setStarting(); + doStart(); + setStarted(); + } + catch (StopException e) + { + if (LOG.isDebugEnabled()) + LOG.debug(e); + setStopping(); + doStop(); + setStopped(); + } + } } catch (Throwable e) { @@ -87,11 +119,20 @@ public abstract class AbstractLifeCycle implements LifeCycle { try { - if (_state == STATE_STOPPING || _state == STATE_STOPPED) - return; - setStopping(); - doStop(); - setStopped(); + switch (_state) + { + case STOPPED: + return; + + case STARTING: + case STOPPING: + throw new IllegalStateException(getState()); + + default: + setStopping(); + doStop(); + setStopped(); + } } catch (Throwable e) { @@ -104,39 +145,45 @@ public abstract class AbstractLifeCycle implements LifeCycle @Override public boolean isRunning() { - final int state = _state; - - return state == STATE_STARTED || state == STATE_STARTING; + final State state = _state; + switch (state) + { + case STARTED: + case STARTING: + return true; + default: + return false; + } } @Override public boolean isStarted() { - return _state == STATE_STARTED; + return _state == State.STARTED; } @Override public boolean isStarting() { - return _state == STATE_STARTING; + return _state == State.STARTING; } @Override public boolean isStopping() { - return _state == STATE_STOPPING; + return _state == State.STOPPING; } @Override public boolean isStopped() { - return _state == STATE_STOPPED; + return _state == State.STOPPED; } @Override public boolean isFailed() { - return _state == STATE_FAILED; + return _state == State.FAILED; } @Override @@ -154,85 +201,73 @@ public abstract class AbstractLifeCycle implements LifeCycle @ManagedAttribute(value = "Lifecycle State for this instance", readonly = true) public String getState() { - switch (_state) - { - case STATE_FAILED: - return FAILED; - case STATE_STARTING: - return STARTING; - case STATE_STARTED: - return STARTED; - case STATE_STOPPING: - return STOPPING; - case STATE_STOPPED: - return STOPPED; - default: - return null; - } + return _state.toString(); } public static String getState(LifeCycle lc) { + if (lc instanceof AbstractLifeCycle) + return ((AbstractLifeCycle)lc)._state.toString(); if (lc.isStarting()) - return STARTING; + return State.STARTING.toString(); if (lc.isStarted()) - return STARTED; + return State.STARTED.toString(); if (lc.isStopping()) - return STOPPING; + return State.STOPPING.toString(); if (lc.isStopped()) - return STOPPED; - return FAILED; + return State.STOPPED.toString(); + return State.FAILED.toString(); } private void setStarted() { - _state = STATE_STARTED; - if (LOG.isDebugEnabled()) - LOG.debug(STARTED + " @{}ms {}", Uptime.getUptime(), this); - for (Listener listener : _listeners) + if (_state == State.STARTING) { - listener.lifeCycleStarted(this); + _state = State.STARTED; + if (LOG.isDebugEnabled()) + LOG.debug("STARTED @{}ms {}", Uptime.getUptime(), this); + for (Listener listener : _listeners) + listener.lifeCycleStarted(this); } } private void setStarting() { if (LOG.isDebugEnabled()) - LOG.debug("starting {}", this); - _state = STATE_STARTING; + LOG.debug("STARTING {}", this); + _state = State.STARTING; for (Listener listener : _listeners) - { listener.lifeCycleStarting(this); - } } private void setStopping() { if (LOG.isDebugEnabled()) - LOG.debug("stopping {}", this); - _state = STATE_STOPPING; + LOG.debug("STOPPING {}", this); + _state = State.STOPPING; for (Listener listener : _listeners) - { listener.lifeCycleStopping(this); - } } private void setStopped() { - _state = STATE_STOPPED; - if (LOG.isDebugEnabled()) - LOG.debug("{} {}", STOPPED, this); - for (Listener listener : _listeners) + if (_state == State.STOPPING) { - listener.lifeCycleStopped(this); + _state = State.STOPPED; + if (LOG.isDebugEnabled()) + LOG.debug("STOPPED {}", this); + for (Listener listener : _listeners) + { + listener.lifeCycleStopped(this); + } } } private void setFailed(Throwable th) { - _state = STATE_FAILED; + _state = State.FAILED; if (LOG.isDebugEnabled()) - LOG.warn(FAILED + " " + this + ": " + th, th); + LOG.warn("FAILED " + this + ": " + th, th); for (Listener listener : _listeners) { listener.lifeCycleFailure(this, th); @@ -290,4 +325,10 @@ public abstract class AbstractLifeCycle implements LifeCycle } return String.format("%s@%x{%s}", name, hashCode(), getState()); } + + /** + * An exception, which if thrown by doStart will immediately stop the component + */ + public class StopException extends RuntimeException + {} } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java index 188e5d65dc3..689f2a51d6f 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java @@ -100,6 +100,8 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container, { for (Bean b : _beans) { + if (!isStarting()) + break; if (b._bean instanceof LifeCycle) { LifeCycle l = (LifeCycle)b._bean; @@ -127,8 +129,6 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container, } } } - - super.doStart(); } catch (Throwable th) { @@ -193,6 +193,8 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container, MultiException mex = new MultiException(); for (Bean b : reverse) { + if (!isStopping()) + break; if (b._managed == Managed.MANAGED && b._bean instanceof LifeCycle) { LifeCycle l = (LifeCycle)b._bean; diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java index 0758e441616..3ce9d5cbf51 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java @@ -403,7 +403,7 @@ public class MetaData p.process(context, getWebXml()); for (WebDescriptor wd : getOverrideWebs()) { - LOG.debug("process {} {}", context, wd); + LOG.debug("process {} {} {}", context, p, wd); p.process(context, wd); } } diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java index a25f4e399bf..99a8c3202bd 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java @@ -38,6 +38,7 @@ public class WebInfConfiguration extends AbstractConfiguration private static final Logger LOG = Log.getLogger(WebInfConfiguration.class); public static final String TEMPDIR_CONFIGURED = "org.eclipse.jetty.tmpdirConfigured"; + public static final String TEMPORARY_RESOURCE_BASE = "org.eclipse.jetty.webapp.tmpResourceBase"; protected Resource _preUnpackBaseResource; @@ -338,8 +339,11 @@ public class WebInfConfiguration extends AbstractConfiguration } if (extractedWebAppDir == null) + { // Then extract it if necessary to the temporary location extractedWebAppDir = new File(context.getTempDirectory(), "webapp"); + context.setAttribute(TEMPORARY_RESOURCE_BASE, extractedWebAppDir); + } if (webApp.getFile() != null && webApp.getFile().isDirectory()) { diff --git a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java index dd4000c758f..de09b819320 100644 --- a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java +++ b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java @@ -36,6 +36,7 @@ import java.nio.file.Paths; import java.security.AccessController; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -1747,6 +1748,8 @@ public class XmlConfiguration properties.putAll(System.getProperties()); // For all arguments, load properties + if (LOG.isDebugEnabled()) + LOG.debug("args={}", Arrays.asList(args)); for (String arg : args) { if (arg.indexOf('=') >= 0) @@ -1785,17 +1788,34 @@ public class XmlConfiguration } } + if (LOG.isDebugEnabled()) + LOG.debug("objects={}", Arrays.asList(objects)); + // For all objects created by XmlConfigurations, start them if they are lifecycles. + List started = new ArrayList<>(objects.size()); for (Object obj : objects) { if (obj instanceof LifeCycle) { LifeCycle lc = (LifeCycle)obj; if (!lc.isRunning()) + { lc.start(); + if (lc.isStarted()) + started.add(lc); + else + { + // Failed to start a component, so stop all started components + Collections.reverse(started); + for (LifeCycle slc : started) + { + slc.stop(); + } + break; + } + } } } - return null; }); } diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/BadAppTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/BadAppTests.java index 9248a2dc5e4..21b1ffc4684 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/BadAppTests.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/BadAppTests.java @@ -103,7 +103,7 @@ public class BadAppTests extends AbstractDistributionTest int port = distribution.freePort(); try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + port + "/badapp/"); @@ -143,7 +143,7 @@ public class BadAppTests extends AbstractDistributionTest int port = distribution.freePort(); try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + port + "/badapp/"); diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/CDITests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/CDITests.java index dd7d90cda29..f0d7f981e84 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/CDITests.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/CDITests.java @@ -101,7 +101,7 @@ public class CDITests extends AbstractDistributionTest int port = distribution.freePort(); try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + port + "/demo/greetings"); diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DemoBaseTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DemoBaseTests.java index 40828383857..2422bda5f76 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DemoBaseTests.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DemoBaseTests.java @@ -56,7 +56,7 @@ public class DemoBaseTests extends AbstractDistributionTest try (DistributionTester.Run run1 = distribution.start(args)) { - assertTrue(run1.awaitConsoleLogsFor("Started @", 20, TimeUnit.SECONDS)); + assertTrue(run1.awaitConsoleLogsFor("Started Server@", 20, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + httpPort + "/test/jsp/dump.jsp"); @@ -88,7 +88,7 @@ public class DemoBaseTests extends AbstractDistributionTest try (DistributionTester.Run run1 = distribution.start(args)) { - assertTrue(run1.awaitConsoleLogsFor("Started @", 20, TimeUnit.SECONDS)); + assertTrue(run1.awaitConsoleLogsFor("Started Server@", 20, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response; @@ -133,7 +133,7 @@ public class DemoBaseTests extends AbstractDistributionTest try (DistributionTester.Run run1 = distribution.start(args)) { - assertTrue(run1.awaitConsoleLogsFor("Started @", 20, TimeUnit.SECONDS)); + assertTrue(run1.awaitConsoleLogsFor("Started Server@", 20, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.POST("http://localhost:" + httpPort + "/test-spec/asy/xx").send(); diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java index 99bfbb945e0..6779e9d3ee6 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java @@ -41,6 +41,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -63,7 +64,7 @@ public class DistributionTests extends AbstractDistributionTest int port = distribution.freePort(); try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + port); @@ -75,6 +76,57 @@ public class DistributionTests extends AbstractDistributionTest } } + @Test + public void testQuickStartGenerationAndRun() throws Exception + { + String jettyVersion = System.getProperty("jettyVersion"); + DistributionTester distribution = DistributionTester.Builder.newInstance() + .jettyVersion(jettyVersion) + .mavenLocalRepository(System.getProperty("mavenRepoPath")) + .build(); + + String[] args1 = { + "--create-startd", + "--approve-all-licenses", + "--add-to-start=resources,server,http,webapp,deploy,jsp,servlet,servlets,quickstart" + }; + + try (DistributionTester.Run run1 = distribution.start(args1)) + { + assertTrue(run1.awaitFor(5, TimeUnit.SECONDS)); + assertEquals(0, run1.getExitValue()); + + File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion); + distribution.installWarFile(war, "test"); + + + try (DistributionTester.Run run2 = distribution.start("jetty.quickstart.mode=GENERATE")) + { + assertTrue(run2.awaitConsoleLogsFor("QuickStartGeneratorConfiguration:main: Generated", 10, TimeUnit.SECONDS)); + Path unpackedWebapp = distribution.getJettyBase().resolve("webapps").resolve("test"); + assertTrue(Files.exists(unpackedWebapp)); + Path webInf = unpackedWebapp.resolve("WEB-INF"); + assertTrue(Files.exists(webInf)); + Path quickstartWebXml = webInf.resolve("quickstart-web.xml"); + assertTrue(Files.exists(quickstartWebXml)); + assertNotEquals(0, Files.size(quickstartWebXml)); + + int port = distribution.freePort(); + + try (DistributionTester.Run run3 = distribution.start("jetty.http.port=" + port, "jetty.quickstart.mode=QUICKSTART")) + { + assertTrue(run3.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); + + startHttpClient(); + ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp"); + assertEquals(HttpStatus.OK_200, response.getStatus()); + assertThat(response.getContentAsString(), containsString("Hello")); + assertThat(response.getContentAsString(), not(containsString("<%"))); + } + } + } + } + @Test public void testSimpleWebAppWithJSP() throws Exception { @@ -100,7 +152,7 @@ public class DistributionTests extends AbstractDistributionTest int port = distribution.freePort(); try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp"); @@ -141,7 +193,7 @@ public class DistributionTests extends AbstractDistributionTest }; try (DistributionTester.Run run2 = distribution.start(args2)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp"); @@ -177,7 +229,7 @@ public class DistributionTests extends AbstractDistributionTest int port = distribution.freePort(); try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); HTTP2Client h2Client = new HTTP2Client(); startHttpClient(() -> new HttpClient(new HttpClientTransportOverHTTP2(h2Client))); @@ -229,7 +281,7 @@ public class DistributionTests extends AbstractDistributionTest try (DistributionTester.Run run2 = distribution.start("jetty.unixsocket.path=" + sockFile.toString())) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(() -> new HttpClient(new HttpClientTransportOverUnixSockets(sockFile.toString()))); ContentResponse response = client.GET("http://localhost/test/index.jsp"); @@ -272,7 +324,7 @@ public class DistributionTests extends AbstractDistributionTest int port = distribution.freePort(); try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp"); diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DynamicListenerTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DynamicListenerTests.java index c80ca9c0941..a16a5f475d2 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DynamicListenerTests.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DynamicListenerTests.java @@ -78,7 +78,7 @@ public class DynamicListenerTests int port = distribution.freePort(); try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + port + "/test/testservlet/foo"); diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/OsgiAppTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/OsgiAppTests.java index 09cb1ef8478..c509d7e95d3 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/OsgiAppTests.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/OsgiAppTests.java @@ -57,7 +57,7 @@ public class OsgiAppTests extends AbstractDistributionTest int port = distribution.freePort(); try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + port + "/test/info"); diff --git a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartTest.java b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartTest.java index f1470a41104..c6f31c01639 100644 --- a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartTest.java +++ b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartTest.java @@ -24,6 +24,9 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; +import org.eclipse.jetty.annotations.AnnotationConfiguration; +import org.eclipse.jetty.plus.webapp.EnvConfiguration; +import org.eclipse.jetty.plus.webapp.PlusConfiguration; import org.eclipse.jetty.server.NetworkConnector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; @@ -31,6 +34,7 @@ import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.resource.PathResource; import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebDescriptor; import org.eclipse.jetty.xml.XmlConfiguration; import org.eclipse.jetty.xml.XmlParser.Node; @@ -47,6 +51,7 @@ public class QuickStartTest @Test public void testStandardTestWar() throws Exception { + //Generate the quickstart PreconfigureStandardTestWar.main(new String[]{}); WebDescriptor descriptor = new WebDescriptor(Resource.newResource("./target/test-standard-preconfigured/WEB-INF/quickstart-web.xml")); @@ -65,8 +70,12 @@ public class QuickStartTest Server server = new Server(0); - QuickStartWebApp webapp = new QuickStartWebApp(); - webapp.setMode(QuickStartConfiguration.Mode.AUTO); + WebAppContext webapp = new WebAppContext(); + webapp.addConfiguration(new QuickStartConfiguration(), + new EnvConfiguration(), + new PlusConfiguration(), + new AnnotationConfiguration()); + webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); webapp.setWar(war); webapp.setContextPath("/"); @@ -93,6 +102,7 @@ public class QuickStartTest @Test public void testSpecWar() throws Exception { + //Generate the quickstart xml PreconfigureSpecWar.main(new String[]{}); Path webXmlPath = MavenTestingUtils.getTargetPath().resolve("test-spec-preconfigured/WEB-INF/quickstart-web.xml"); @@ -114,8 +124,12 @@ public class QuickStartTest Server server = new Server(0); - QuickStartWebApp webapp = new QuickStartWebApp(); - webapp.setMode(QuickStartConfiguration.Mode.AUTO); + WebAppContext webapp = new WebAppContext(); + webapp.addConfiguration(new QuickStartConfiguration(), + new EnvConfiguration(), + new PlusConfiguration(), + new AnnotationConfiguration()); + webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); webapp.setWar(war); webapp.setContextPath("/"); @@ -142,6 +156,7 @@ public class QuickStartTest @Test public void testJNDIWar() throws Exception { + //Generate the quickstart PreconfigureJNDIWar.main(new String[]{}); WebDescriptor descriptor = new WebDescriptor(Resource.newResource("./target/test-jndi-preconfigured/WEB-INF/quickstart-web.xml")); @@ -160,8 +175,12 @@ public class QuickStartTest Server server = new Server(0); - QuickStartWebApp webapp = new QuickStartWebApp(); - webapp.setMode(QuickStartConfiguration.Mode.AUTO); + WebAppContext webapp = new WebAppContext(); + webapp.addConfiguration(new QuickStartConfiguration(), + new EnvConfiguration(), + new PlusConfiguration(), + new AnnotationConfiguration()); + webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); webapp.setWar(war); webapp.setContextPath("/"); diff --git a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/Quickstart.java b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/Quickstart.java index df47e4c038a..4f02f21ee4f 100644 --- a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/Quickstart.java +++ b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/Quickstart.java @@ -18,8 +18,12 @@ package org.eclipse.jetty.quickstart; +import org.eclipse.jetty.annotations.AnnotationConfiguration; +import org.eclipse.jetty.plus.webapp.EnvConfiguration; +import org.eclipse.jetty.plus.webapp.PlusConfiguration; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.xml.XmlConfiguration; public class Quickstart @@ -40,8 +44,12 @@ public class Quickstart Server server = new Server(8080); - QuickStartWebApp webapp = new QuickStartWebApp(); - webapp.setMode(QuickStartConfiguration.Mode.AUTO); + WebAppContext webapp = new WebAppContext(); + webapp.addConfiguration(new QuickStartConfiguration(), + new EnvConfiguration(), + new PlusConfiguration(), + new AnnotationConfiguration()); + webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); webapp.setWar(war); webapp.setContextPath("/"); From 8dfa4c941a742d8fc3e30f6453635c6852c8d17d Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 10 Sep 2019 03:40:30 -0500 Subject: [PATCH 044/113] Server.stop() is now optional because of assumption on start Signed-off-by: Joakim Erdfelt --- .../test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java | 3 ++- .../test/java/org/eclipse/jetty/embedded/OneWebAppTest.java | 3 ++- .../java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java | 3 ++- .../org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java | 3 ++- .../java/org/eclipse/jetty/embedded/ServerWithJNDITest.java | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java index 3cd3cf1a479..445ebe9d4db 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java @@ -25,6 +25,7 @@ import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -59,7 +60,7 @@ public class LikeJettyXmlTest extends AbstractEmbeddedTest @AfterEach public void stopServer() throws Exception { - server.stop(); + LifeCycle.stop(server); } @Test diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java index 4801f8c1e15..7f9eae15003 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java @@ -24,6 +24,7 @@ import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -49,7 +50,7 @@ public class OneWebAppTest extends AbstractEmbeddedTest @AfterEach public void stopServer() throws Exception { - server.stop(); + LifeCycle.stop(server); } @Test diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java index 53569bc7e32..9f858925596 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java @@ -24,6 +24,7 @@ import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -53,7 +54,7 @@ public class OneWebAppWithJspTest extends AbstractEmbeddedTest @AfterEach public void stopServer() throws Exception { - server.stop(); + LifeCycle.stop(server); } @Test diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java index 5abee62e47d..3662334ea26 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java @@ -23,6 +23,7 @@ import java.net.URI; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -49,7 +50,7 @@ public class ServerWithAnnotationsTest extends AbstractEmbeddedTest @AfterEach public void stopServer() throws Exception { - server.stop(); + LifeCycle.stop(server); } @Test diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java index 2065a2f0e09..603b4ae8023 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java @@ -23,6 +23,7 @@ import java.net.URI; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -50,7 +51,7 @@ public class ServerWithJNDITest extends AbstractEmbeddedTest @AfterEach public void stopServer() throws Exception { - server.stop(); + LifeCycle.stop(server); } @Test From 68382b95491a7d6edd3e99f73941d6f248265e5a Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 10 Sep 2019 03:46:26 -0500 Subject: [PATCH 045/113] Issue #3989 - Cleaning up ManagedSelector.onSelectFailed + Based on PR review Signed-off-by: Joakim Erdfelt --- .../org/eclipse/jetty/io/ManagedSelector.java | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java index 35e9da322af..a39890d7557 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java @@ -123,23 +123,11 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable start._started.await(); } - protected void onSelectFailed(Throwable cause) throws Exception + protected void onSelectFailed(Throwable cause) { // override to change behavior } - private void notifySelectFailed(Throwable cause) - { - try - { - onSelectFailed(cause); - } - catch (Throwable x) - { - LOG.info("Failure while calling onSelectFailed()", x); - } - } - public int size() { Selector s = _selector; @@ -520,7 +508,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable if (isRunning()) { LOG.warn("Fatal select() failure", x); - notifySelectFailed(x); + onSelectFailed(x); } else { From 253aa8a650684dcfabe5c1e495e10e5953830e86 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 10 Sep 2019 03:57:58 -0500 Subject: [PATCH 046/113] Fixing typo in email address Signed-off-by: Joakim Erdfelt --- KEYS.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KEYS.txt b/KEYS.txt index acf04a8cc33..73b9fb49a4e 100644 --- a/KEYS.txt +++ b/KEYS.txt @@ -2,6 +2,6 @@ Jan Bartel AED5 EE6C 45D0 FE8D 5D1B 164F 27DE D4BF 6216 DB8F Jesse McConnell 2A68 4B57 436A 81FA 8706 B53C 61C3 351A 438A 3B7D Joakim Erdfelt 5989 BAF7 6217 B843 D66B E55B 2D0E 1FB8 FE4B 68B4 -Joakim Erdfelt B59B 67FD 7904 9843 67F9 3180 0818 D9D6 8FB6 7BAC +Joakim Erdfelt B59B 67FD 7904 9843 67F9 3180 0818 D9D6 8FB6 7BAC Joakim Erdfelt BFBB 21C2 46D7 7768 3628 7A48 A04E 0C74 ABB3 5FEA Simone Bordet 8B09 6546 B1A8 F026 56B1 5D3B 1677 D141 BCF3 584D From b2dc5830eda11e97be4fbeaaa175009d55d7bdc4 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 10 Sep 2019 08:53:21 -0500 Subject: [PATCH 047/113] Adding configurable ports (per PR review) Signed-off-by: Joakim Erdfelt --- .../eclipse/jetty/embedded/ExampleServer.java | 3 +- .../jetty/embedded/ExampleServerXml.java | 3 +- .../eclipse/jetty/embedded/ExampleUtil.java | 86 +++++++++++++++++++ .../jetty/embedded/FastFileServer.java | 3 +- .../eclipse/jetty/embedded/FileServer.java | 3 +- .../eclipse/jetty/embedded/FileServerXml.java | 3 +- .../eclipse/jetty/embedded/HelloWorld.java | 3 +- .../eclipse/jetty/embedded/Http2Server.java | 31 ++++--- .../org/eclipse/jetty/embedded/JarServer.java | 3 +- .../jetty/embedded/JettyDistribution.java | 9 +- .../eclipse/jetty/embedded/LikeJettyXml.java | 8 +- .../jetty/embedded/ManyConnectors.java | 4 +- .../eclipse/jetty/embedded/ManyContexts.java | 3 +- .../eclipse/jetty/embedded/ManyHandlers.java | 3 +- .../jetty/embedded/ManyServletContexts.java | 3 +- .../jetty/embedded/MinimalServlets.java | 3 +- .../eclipse/jetty/embedded/OneConnector.java | 3 +- .../eclipse/jetty/embedded/OneContext.java | 3 +- .../eclipse/jetty/embedded/OneHandler.java | 3 +- .../jetty/embedded/OneServletContext.java | 3 +- .../embedded/OneServletContextJmxStats.java | 3 +- .../OneServletContextWithSession.java | 3 +- .../org/eclipse/jetty/embedded/OneWebApp.java | 3 +- .../jetty/embedded/OneWebAppWithJsp.java | 3 +- .../eclipse/jetty/embedded/ProxyServer.java | 3 +- .../eclipse/jetty/embedded/RewriteServer.java | 3 +- .../jetty/embedded/SecuredHelloHandler.java | 3 +- .../jetty/embedded/ServerWithAnnotations.java | 3 +- .../eclipse/jetty/embedded/ServerWithJMX.java | 3 +- .../jetty/embedded/ServerWithJNDI.java | 3 +- .../jetty/embedded/SimplestServer.java | 3 +- .../jetty/embedded/SplitFileServer.java | 3 +- .../jetty/embedded/WebSocketJsrServer.java | 3 +- .../jetty/embedded/WebSocketServer.java | 3 +- 34 files changed, 178 insertions(+), 47 deletions(-) create mode 100644 examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleUtil.java diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServer.java index 57f28601b94..f003bafe00a 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServer.java @@ -50,7 +50,8 @@ public class ExampleServer public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServerXml.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServerXml.java index ef6b253136a..1ca2a34440f 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServerXml.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServerXml.java @@ -40,7 +40,8 @@ public class ExampleServerXml public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleUtil.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleUtil.java new file mode 100644 index 00000000000..ce4d77c8966 --- /dev/null +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleUtil.java @@ -0,0 +1,86 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.embedded; + +import org.eclipse.jetty.util.StringUtil; + +public class ExampleUtil +{ + /** + * Get a port, possibly configured from Command line or System property. + * + * @param args the command line arguments + * @param propertyName the property name + * @param defValue the default value + * @return the configured port + */ + public static int getPort(String[] args, String propertyName, int defValue) + { + for (String arg : args) + { + if (arg.startsWith(propertyName + "=")) + { + String value = arg.substring(propertyName.length() + 2); + int port = toInt(value); + if (isValidPort(port)) + return port; + } + } + + String value = System.getProperty(propertyName); + int port = toInt(value); + if (isValidPort(port)) + return port; + + return defValue; + } + + /** + * Test if port is in the valid range to be used. + * + * @param port the port to test + * @return true if valid + */ + private static boolean isValidPort(int port) + { + return (port >= 0) && (port <= 65535); + } + + /** + * Parse an int, ignoring any {@link NumberFormatException} + * + * @param value the string value to parse + * @return the int (if parsed), or -1 if not parsed. + */ + private static int toInt(String value) + { + if (StringUtil.isBlank(value)) + return -1; + + try + { + return Integer.parseInt(value); + } + catch (NumberFormatException ignored) + { + // ignored + return -1; + } + } +} diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java index f9508d0d12c..febd097151f 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java @@ -73,8 +73,9 @@ public class FastFileServer public static void main(String[] args) throws Exception { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); File directory = new File(System.getProperty("user.dir")); - Server server = createServer(8080, directory); + Server server = createServer(port, directory); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java index 3a0e78a8164..b9d6714f2cc 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java @@ -62,10 +62,11 @@ public class FileServer public static void main(String[] args) throws Exception { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); Path userDir = Paths.get(System.getProperty("user.dir")); PathResource pathResource = new PathResource(userDir); - Server server = createServer(8080, pathResource); + Server server = createServer(port, pathResource); // Start things up! By using the server.join() the server thread will join with the current thread. // See "http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.html#join()" for more details. diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServerXml.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServerXml.java index 9b56cf65c6d..73481ef4e34 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServerXml.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServerXml.java @@ -47,8 +47,9 @@ public class FileServerXml public static void main(String[] args) throws Exception { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); Path userDir = Paths.get(System.getProperty("user.dir")); - Server server = createServer(8080, userDir); + Server server = createServer(port, userDir); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloWorld.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloWorld.java index 151432fe6b9..5218c5e9249 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloWorld.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloWorld.java @@ -51,7 +51,8 @@ public class HelloWorld extends AbstractHandler public static void main(String[] args) throws Exception { - Server server = new Server(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = new Server(port); server.setHandler(new HelloWorld()); server.start(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java index fa69dba7c23..6aa321b336a 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java @@ -18,9 +18,12 @@ package org.eclipse.jetty.embedded; -import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.lang.management.ManagementFactory; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Date; import java.util.EnumSet; import javax.servlet.DispatcherType; @@ -54,6 +57,7 @@ import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlets.PushCacheFilter; +import org.eclipse.jetty.util.resource.PathResource; import org.eclipse.jetty.util.ssl.SslContextFactory; /** @@ -63,6 +67,8 @@ public class Http2Server { public static void main(String... args) throws Exception { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + int securePort = ExampleUtil.getPort(args, "jetty.https.port", 8443); Server server = new Server(); MBeanContainer mbContainer = new MBeanContainer( @@ -70,10 +76,11 @@ public class Http2Server server.addBean(mbContainer); ServletContextHandler context = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS); - String docroot = "src/main/resources/docroot"; - if (!new File(docroot).exists()) - docroot = "examples/embedded/src/main/resources/docroot"; - context.setResourceBase(docroot); + Path docroot = Paths.get("src/main/resources/docroot"); + if (!Files.exists(docroot)) + throw new FileNotFoundException(docroot.toString()); + + context.setBaseResource(new PathResource(docroot)); context.addFilter(PushCacheFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); // context.addFilter(PushSessionCacheFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST)); context.addFilter(PushedTilesFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); @@ -84,21 +91,21 @@ public class Http2Server // HTTP Configuration HttpConfiguration httpConfig = new HttpConfiguration(); httpConfig.setSecureScheme("https"); - httpConfig.setSecurePort(8443); + httpConfig.setSecurePort(securePort); httpConfig.setSendXPoweredBy(true); httpConfig.setSendServerVersion(true); // HTTP Connector ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfig), new HTTP2CServerConnectionFactory(httpConfig)); - http.setPort(8080); + http.setPort(port); server.addConnector(http); // SSL Context Factory for HTTPS and HTTP/2 - String jettyDistro = System.getProperty("jetty.distro", "../../jetty-distribution/target/distribution"); - if (!new File(jettyDistro).exists()) - jettyDistro = "jetty-distribution/target/distribution"; + Path keystorePath = Paths.get("src/main/resources/etc/keystore").toAbsolutePath(); + if (!Files.exists(keystorePath)) + throw new FileNotFoundException(keystorePath.toString()); SslContextFactory sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(jettyDistro + "/demo-base/etc/keystore"); + sslContextFactory.setKeyStorePath(keystorePath.toString()); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR); @@ -120,7 +127,7 @@ public class Http2Server // HTTP/2 Connector ServerConnector http2Connector = new ServerConnector(server, ssl, alpn, h2, new HttpConnectionFactory(httpsConfig)); - http2Connector.setPort(8443); + http2Connector.setPort(securePort); server.addConnector(http2Connector); ALPN.debug = false; diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JarServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JarServer.java index 4d31cf86512..027f5f922d6 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JarServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JarServer.java @@ -60,7 +60,8 @@ public class JarServer public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java index 3e8038040ff..f8450325c3e 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java @@ -124,11 +124,16 @@ public class JettyDistribution return null; } - public static Path resolve(String path) + public static Path get() { if (DISTRIBUTION == null) throw new RuntimeException("jetty-distribution not found"); - return DISTRIBUTION.resolve(path); + return DISTRIBUTION; + } + + public static Path resolve(String path) + { + return get().resolve(path); } public static void main(String... arg) diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java index 261e22a9f96..e051212fef3 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java @@ -64,10 +64,10 @@ public class LikeJettyXml public static Server createServer(int port, int securePort, boolean addDebugListener) throws Exception { // Path to as-built jetty-distribution directory - String jettyHomeBuild = JettyDistribution.DISTRIBUTION.toString(); + Path jettyHomeBuild = JettyDistribution.get(); // Find jetty home and base directories - String homePath = System.getProperty("jetty.home", jettyHomeBuild); + String homePath = System.getProperty("jetty.home", jettyHomeBuild.toString()); Path homeDir = Paths.get(homePath); String basePath = System.getProperty("jetty.base", homeDir.resolve("demo-base").toString()); @@ -218,7 +218,9 @@ public class LikeJettyXml public static void main(String[] args) throws Exception { - Server server = createServer(8080, 8443, true); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + int securePort = ExampleUtil.getPort(args, "jetty.https.port", 8443); + Server server = createServer(port, securePort, true); // Extra options server.setDumpAfterStart(true); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java index 71d527f40e7..27a9e714f2d 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java @@ -129,7 +129,9 @@ public class ManyConnectors public static void main(String[] args) throws Exception { - Server server = createServer(8080, 8443); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + int securePort = ExampleUtil.getPort(args, "jetty.https.port", 8443); + Server server = createServer(port, securePort); // Start the server server.start(); server.dumpStdErr(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyContexts.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyContexts.java index 1aae23f5b5d..d64735e964f 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyContexts.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyContexts.java @@ -52,7 +52,8 @@ public class ManyContexts public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.dumpStdErr(); server.join(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyHandlers.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyHandlers.java index c29b0c432b3..e6dfce9a1b9 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyHandlers.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyHandlers.java @@ -164,7 +164,8 @@ public class ManyHandlers public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyServletContexts.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyServletContexts.java index 5dd7e8fa327..d1adc4be4c3 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyServletContexts.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyServletContexts.java @@ -62,7 +62,8 @@ public class ManyServletContexts public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.dumpStdErr(); server.join(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/MinimalServlets.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/MinimalServlets.java index dab7237a89b..76a9fe82cda 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/MinimalServlets.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/MinimalServlets.java @@ -56,7 +56,8 @@ public class MinimalServlets public static void main(String[] args) throws Exception { // Create a basic jetty server object that will listen on port 8080. - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); // Start things up! server.start(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneConnector.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneConnector.java index ff897e5cff8..6e183117536 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneConnector.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneConnector.java @@ -47,7 +47,8 @@ public class OneConnector public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); // Start the server server.start(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneContext.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneContext.java index 59bf20d9e86..c27556f0f1e 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneContext.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneContext.java @@ -40,7 +40,8 @@ public class OneContext public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); // Start the server server.start(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneHandler.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneHandler.java index 00aab92ac78..10df55d687d 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneHandler.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneHandler.java @@ -31,7 +31,8 @@ public class OneHandler public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContext.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContext.java index 34f1e250e3a..aa468d089cb 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContext.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContext.java @@ -80,9 +80,10 @@ public class OneServletContext public static void main(String[] args) throws Exception { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); Path tempDir = Paths.get(System.getProperty("java.io.tmpdir")); - Server server = createServer(8080, new PathResource(tempDir)); + Server server = createServer(port, new PathResource(tempDir)); server.start(); server.dumpStdErr(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextJmxStats.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextJmxStats.java index 6179716d3e3..9a33e10ea69 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextJmxStats.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextJmxStats.java @@ -51,7 +51,8 @@ public class OneServletContextJmxStats public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.join(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextWithSession.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextWithSession.java index b2af0ebf2d2..992bfec14ea 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextWithSession.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextWithSession.java @@ -65,9 +65,10 @@ public class OneServletContextWithSession public static void main(String[] args) throws Exception { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); Path dir = Paths.get(System.getProperty("user.dir")); PathResource baseResource = new PathResource(dir); - Server server = createServer(8080, baseResource); + Server server = createServer(port, baseResource); server.start(); server.dumpStdErr(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java index f5c229732b3..46d5b70ce08 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java @@ -53,7 +53,8 @@ public class OneWebApp public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); // Start things up! server.start(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java index 66ea025650c..3e3d5bd9132 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java @@ -103,7 +103,8 @@ public class OneWebAppWithJsp public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); // Start things up! server.start(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ProxyServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ProxyServer.java index 265e2dda38f..872c57f102b 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ProxyServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ProxyServer.java @@ -52,7 +52,8 @@ public class ProxyServer public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.join(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/RewriteServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/RewriteServer.java index 47682203643..0347886b29b 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/RewriteServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/RewriteServer.java @@ -53,7 +53,8 @@ public class RewriteServer public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.join(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java index 545f6249ed4..50e53c3c700 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java @@ -104,7 +104,8 @@ public class SecuredHelloHandler public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); // Start things up! server.start(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithAnnotations.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithAnnotations.java index 39c03e5b8d6..3c8d2a96c7a 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithAnnotations.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithAnnotations.java @@ -90,7 +90,8 @@ public class ServerWithAnnotations public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.dumpStdErr(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJMX.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJMX.java index c2ab812f691..c3c14df0b49 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJMX.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJMX.java @@ -53,7 +53,8 @@ public class ServerWithJMX public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.dumpStdErr(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJNDI.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJNDI.java index 7539690718a..b364700b95d 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJNDI.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJNDI.java @@ -115,7 +115,8 @@ public class ServerWithJNDI public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.join(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SimplestServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SimplestServer.java index 1ba0293f2ac..3bdb6d4fa27 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SimplestServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SimplestServer.java @@ -36,7 +36,8 @@ public class SimplestServer public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SplitFileServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SplitFileServer.java index b4abb97afd0..f8b46272b56 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SplitFileServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SplitFileServer.java @@ -83,10 +83,11 @@ public class SplitFileServer public static void main(String[] args) throws Exception { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); Resource resource0 = new PathResource(Paths.get("src/test/resources/dir0")); Resource resource1 = new PathResource(Paths.get("src/test/resources/dir1")); - Server server = createServer(8080, resource0, resource1); + Server server = createServer(port, resource0, resource1); // Dump the server state server.setDumpAfterStart(true); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java index 19da249f39a..8d901dcb5c8 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java @@ -76,7 +76,8 @@ public class WebSocketJsrServer public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.join(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketServer.java index 25f7bf45a77..172b5ffb36a 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketServer.java @@ -75,7 +75,8 @@ public class WebSocketServer public static void main(String[] args) throws Exception { - Server server = createServer(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.join(); From e69ee7b14766044b7078351e5e868500b304e7bc Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 10 Sep 2019 09:02:07 -0500 Subject: [PATCH 048/113] Ensuring jetty-distribution builds before eexample-embedded Signed-off-by: Joakim Erdfelt --- examples/embedded/pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/embedded/pom.xml b/examples/embedded/pom.xml index 881462073b3..ae55acd9ff5 100644 --- a/examples/embedded/pom.xml +++ b/examples/embedded/pom.xml @@ -146,6 +146,13 @@ tests test + + org.eclipse.jetty + jetty-distribution + ${project.version} + tar.gz + test + From 627ca1bb70de079ed06c37f065e85645f792a671 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 10 Sep 2019 10:36:08 -0500 Subject: [PATCH 049/113] More tweaks for testing + Moved realm.properties to src/main/resources/etc/ + Ensured that surefire's useManifestOnlyJar=false to enable taglibs to be found, and jstl to work. Signed-off-by: Joakim Erdfelt --- examples/embedded/pom.xml | 12 +++++++++++ .../jetty/embedded/OneWebAppWithJsp.java | 6 ++++-- .../jetty/embedded/SecuredHelloHandler.java | 5 +++-- .../jetty/embedded/ServerWithAnnotations.java | 5 +++-- .../jetty/embedded/WebSocketJsrServer.java | 20 +++++++++---------- .../src/main/resources/etc/realm.properties | 20 +++++++++++++++++++ 6 files changed, 51 insertions(+), 17 deletions(-) create mode 100644 examples/embedded/src/main/resources/etc/realm.properties diff --git a/examples/embedded/pom.xml b/examples/embedded/pom.xml index ae55acd9ff5..fb2eed394d5 100644 --- a/examples/embedded/pom.xml +++ b/examples/embedded/pom.xml @@ -155,6 +155,18 @@ + + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + + jdk9 diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java index 3e3d5bd9132..87175b015a8 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java @@ -89,9 +89,11 @@ public class OneWebAppWithJsp // its own we register it as a bean with the Jetty server object so it // can be started and stopped according to the lifecycle of the server // itself. - URL realmProps = OneWebAppWithJsp.class.getClassLoader().getResource("realm.properties"); + String realmResourceName = "etc/realm.properties"; + ClassLoader classLoader = OneWebAppWithJsp.class.getClassLoader(); + URL realmProps = classLoader.getResource(realmResourceName); if (realmProps == null) - throw new FileNotFoundException("Unable to find realm.properties"); + throw new FileNotFoundException("Unable to find " + realmResourceName); HashLoginService loginService = new HashLoginService(); loginService.setName("Test Realm"); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java index 50e53c3c700..1f339b33fa2 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java @@ -48,10 +48,11 @@ public class SecuredHelloHandler // started and stopped according to the lifecycle of the server itself. // In this example the name can be whatever you like since we are not // dealing with webapp realms. + String realmResourceName = "etc/realm.properties"; ClassLoader classLoader = SecuredHelloHandler.class.getClassLoader(); - URL realmProps = classLoader.getResource("realm.properties"); + URL realmProps = classLoader.getResource(realmResourceName); if (realmProps == null) - throw new FileNotFoundException("Unable to find realm.properties"); + throw new FileNotFoundException("Unable to find " + realmResourceName); LoginService loginService = new HashLoginService("MyRealm", realmProps.toExternalForm()); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithAnnotations.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithAnnotations.java index 3c8d2a96c7a..e7a71deecf0 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithAnnotations.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithAnnotations.java @@ -76,10 +76,11 @@ public class ServerWithAnnotations server.addBean(new NamingDump()); // Configure a LoginService + String realmResourceName = "etc/realm.properties"; ClassLoader classLoader = ServerWithAnnotations.class.getClassLoader(); - URL realmProps = classLoader.getResource("realm.properties"); + URL realmProps = classLoader.getResource(realmResourceName); if (realmProps == null) - throw new FileNotFoundException("Unable to find realm.properties"); + throw new FileNotFoundException("Unable to find " + realmResourceName); HashLoginService loginService = new HashLoginService(); loginService.setName("Test Realm"); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java index 8d901dcb5c8..1f67d7852ae 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java @@ -18,8 +18,6 @@ package org.eclipse.jetty.embedded; -import javax.servlet.ServletException; -import javax.websocket.DeploymentException; import javax.websocket.OnMessage; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; @@ -28,7 +26,6 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.websocket.jsr356.server.ServerContainer; import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer; /** @@ -49,23 +46,24 @@ public class WebSocketJsrServer } } - public static Server createServer(int port) throws DeploymentException, ServletException + public static Server createServer(int port) { Server server = new Server(port); HandlerList handlers = new HandlerList(); - ServletContextHandler context = new ServletContextHandler( - ServletContextHandler.SESSIONS); + ServletContextHandler context = new ServletContextHandler(); context.setContextPath("/"); handlers.addHandler(context); // Enable javax.websocket configuration for the context - ServerContainer wsContainer = WebSocketServerContainerInitializer - .configureContext(context); - - // Add your websockets to the container - wsContainer.addEndpoint(EchoJsrSocket.class); + WebSocketServerContainerInitializer.configure(context, + (servletContext, serverContainer) -> + { + // Add your websocket to the javax.websocket.server.ServerContainer + serverContainer.addEndpoint(EchoJsrSocket.class); + } + ); handlers.addHandler(new DefaultHandler()); diff --git a/examples/embedded/src/main/resources/etc/realm.properties b/examples/embedded/src/main/resources/etc/realm.properties new file mode 100644 index 00000000000..f4b3490e910 --- /dev/null +++ b/examples/embedded/src/main/resources/etc/realm.properties @@ -0,0 +1,20 @@ +# +# This file defines users passwords and roles for a HashUserRealm +# +# The format is +# : [, ...] +# +# Passwords may be clear text, obfuscated or checksummed. The class +# org.eclipse.jetty.util.security.Password should be used to generate obfuscated +# passwords or password checksums +# +# If DIGEST Authentication is used, the password must be in a recoverable +# format, either plain text or OBF:. +# +jetty:MD5:164c88b302622e17050af52c89945d44,user +admin:CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin,user +other:OBF:1xmk1w261u9r1w1c1xmq,user +plain:plain,user +user:password,user +# This entry is for digest auth. The credential is a MD5 hash of username:realmname:password +digest:MD5:6e120743ad67abfbc385bc2bb754e297,user From a563cdca7646713edb8323344efab02a5f4b1a6d Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Wed, 11 Sep 2019 09:10:35 +1000 Subject: [PATCH 050/113] Issue #4025 Add flushOnResponseCommit mode to sessions. (#4049) * Issue #4025 Add flushOnCommit mode to sessions. Added flushOnCommit mode to write a session to the backing store as the response commits, before any bytes are returned to the client. Signed-off-by: Jan Bartel --- .../session-configuration-sessioncache.adoc | 26 +- .../etc/sessions/session-cache-hash.xml | 3 +- .../etc/sessions/session-cache-null.xml | 6 +- .../config/modules/session-cache-hash.mod | 8 +- .../config/modules/session-cache-null.mod | 2 +- .../org/eclipse/jetty/server/HttpChannel.java | 6 +- .../org/eclipse/jetty/server/Request.java | 44 +- .../server/session/AbstractSessionCache.java | 57 +- .../session/AbstractSessionCacheFactory.java | 114 ++++ .../session/AbstractSessionDataStore.java | 12 +- .../session/DefaultSessionCacheFactory.java | 75 +-- .../server/session/NullSessionCache.java | 150 ----- .../session/NullSessionCacheFactory.java | 66 +-- .../eclipse/jetty/server/session/Session.java | 31 +- .../jetty/server/session/SessionCache.java | 26 +- .../jetty/server/session/SessionData.java | 28 + .../jetty/server/session/SessionHandler.java | 25 +- .../session/AbstractSessionCacheTest.java | 513 ++++++++++++++++++ .../session/DefaultSessionCacheTest.java | 309 +---------- .../server/session/NullSessionCacheTest.java | 383 +++++-------- .../session/SessionEvictionFailureTest.java | 2 + 21 files changed, 1045 insertions(+), 841 deletions(-) create mode 100644 jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCacheFactory.java create mode 100644 tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/AbstractSessionCacheTest.java diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-sessioncache.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-sessioncache.adoc index d06c0f74f28..cd7ebfa5a0e 100644 --- a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-sessioncache.adoc +++ b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-sessioncache.adoc @@ -34,6 +34,7 @@ Once the `session-cache-hash` module has been enabled, you can view a list of al #jetty.session.saveOnInactiveEvict=false #jetty.session.saveOnCreate=false #jetty.session.removeUnloadableSessions=false +#jetty.session.flushOnResponseCommit=false ---- jetty.session.evictionPolicy:: @@ -63,6 +64,12 @@ jetty.session.removeUnloadableSessions:: Boolean, default `false`. Controls whether a session that cannot be restored - for example because it is corrupted - from the `SessionDataStore` is deleted by the `SessionDataStore`. +jetty.session.flushOnResponseCommit:: +Boolean, default `false`. +If true, if a session is "dirty" - ie its attributes have changed - it will be written to the backing store as the response is about to commit. +This ensures that all subsequent requests whether to the same or different node will see the updated session data. +If false, a dirty session will only be written to the backing store when the last simultaneous request for it leaves the session. + For more general information on the uses of these configuration properties, see link:#sessions-details[Session Components]. @@ -72,4 +79,21 @@ The `NullSessionCache` is a trivial implementation of the `SessionCache` that do You may need to use it if your clustering setup does not have a sticky load balancer, or if you want absolutely minimal support for sessions. If you use this in conjunction with the `NullSessionDataStore`, then sessions will neither be retained in memory nor persisted. -To enable the `NullSessionCache`, enable the `sesssion-cache-null` link:#startup-modules[module]. +To enable the `NullSessionCache`, enable the `sesssion-cache-null` link:#startup-modules[module]. +Configuration options are: + +jetty.session.saveOnCreate:: +Boolean, default `false`. +Controls whether a session that is newly created will be immediately saved to the `SessionDataStore` or lazily saved as the last request for the session exits. + +jetty.session.removeUnloadableSessions:: +Boolean, default `false`. +Controls whether a session that cannot be restored - for example because it is corrupted - from the `SessionDataStore` is deleted by the `SessionDataStore`. + +jetty.session.flushOnResponseCommit:: +Boolean, default `false`. +If true, if a session is "dirty" - ie its attributes have changed - it will be written to the backing store as the response is about to commit. +This ensures that all subsequent requests whether to the same or different node will see the updated session data. +If false, a dirty session will only be written to the backing store when the last simultaneous request for it leaves the session. + +For more general information on the uses of these configuration properties, see link:#sessions-details[Session Components]. diff --git a/jetty-server/src/main/config/etc/sessions/session-cache-hash.xml b/jetty-server/src/main/config/etc/sessions/session-cache-hash.xml index 5280fb42f75..43e4b09a280 100644 --- a/jetty-server/src/main/config/etc/sessions/session-cache-hash.xml +++ b/jetty-server/src/main/config/etc/sessions/session-cache-hash.xml @@ -4,7 +4,7 @@ - + @@ -13,6 +13,7 @@ + diff --git a/jetty-server/src/main/config/etc/sessions/session-cache-null.xml b/jetty-server/src/main/config/etc/sessions/session-cache-null.xml index 3f030bf05b4..a9dc52b83e6 100644 --- a/jetty-server/src/main/config/etc/sessions/session-cache-null.xml +++ b/jetty-server/src/main/config/etc/sessions/session-cache-null.xml @@ -11,11 +11,7 @@ - - - - - + diff --git a/jetty-server/src/main/config/modules/session-cache-hash.mod b/jetty-server/src/main/config/modules/session-cache-hash.mod index 32ab705c7a2..2d336bc1d99 100644 --- a/jetty-server/src/main/config/modules/session-cache-hash.mod +++ b/jetty-server/src/main/config/modules/session-cache-hash.mod @@ -1,10 +1,9 @@ DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html [description] -Enable first level session cache in ConcurrentHashMap. -If not enabled, sessions will use a HashSessionCache by default, so enabling -via this module is only needed if the configuration properties need to be -changed. +Enable first level session cache. If this module is not enabled, sessions will +use the DefaultSessionCache by default, so enabling via this module is only needed +if the configuration properties need to be changed from their defaults. [tags] session @@ -23,3 +22,4 @@ etc/sessions/session-cache-hash.xml #jetty.session.saveOnInactiveEvict=false #jetty.session.saveOnCreate=false #jetty.session.removeUnloadableSessions=false +#jetty.session.flushOnResponseCommit=false diff --git a/jetty-server/src/main/config/modules/session-cache-null.mod b/jetty-server/src/main/config/modules/session-cache-null.mod index 6069c8f8168..2a94f59cb82 100644 --- a/jetty-server/src/main/config/modules/session-cache-null.mod +++ b/jetty-server/src/main/config/modules/session-cache-null.mod @@ -18,4 +18,4 @@ etc/sessions/session-cache-null.xml [ini-template] #jetty.session.saveOnCreate=false #jetty.session.removeUnloadableSessions=false -#jetty.session.writeThroughMode=ON_EXIT +#jetty.session.flushOnResponseCommit=false diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java index e48d6f22156..11b0f74a2fb 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java @@ -824,15 +824,15 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor if (info == null) info = _response.newResponseMetaData(); commit(info); - + _combinedListener.onResponseBegin(_request); + _request.onResponseCommit(); + // wrap callback to process 100 responses final int status = info.getStatus(); final Callback committed = (status < HttpStatus.OK_200 && status >= HttpStatus.CONTINUE_100) ? new Send100Callback(callback) : new SendCallback(callback, content, true, complete); - _combinedListener.onResponseBegin(_request); - // committing write _transport.send(info, _request.isHead(), content, complete, committed); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index ef94f6cbf80..cf417816ac5 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -222,7 +222,7 @@ public class Request implements HttpServletRequest private long _timeStamp; private MultiParts _multiParts; //if the request is a multi-part mime private AsyncContextState _async; - private List _sessions; //list of sessions used during lifetime of request + private List _sessions; //list of sessions used during lifetime of request public Request(HttpChannel channel, HttpInput input) { @@ -377,32 +377,41 @@ public class Request implements HttpServletRequest */ public void enterSession(HttpSession s) { - if (s == null) + if (!(s instanceof Session)) return; if (_sessions == null) _sessions = new ArrayList<>(); if (LOG.isDebugEnabled()) LOG.debug("Request {} entering session={}", this, s); - _sessions.add(s); + _sessions.add((Session)s); } /** * Complete this request's access to a session. * - * @param s the session + * @param session the session */ - private void leaveSession(HttpSession s) + private void leaveSession(Session session) { - if (s == null) - return; - - Session session = (Session)s; if (LOG.isDebugEnabled()) LOG.debug("Request {} leaving session {}", this, session); session.getSessionHandler().complete(session); } + /** + * A response is being committed for a session, + * potentially write the session out before the + * client receives the response. + * @param session the session + */ + private void commitSession(Session session) + { + if (LOG.isDebugEnabled()) + LOG.debug("Response {} committing for session {}", this, session); + session.getSessionHandler().commit(session); + } + private MultiMap getParameters() { if (!_contentParamsExtracted) @@ -1516,13 +1525,26 @@ public class Request implements HttpServletRequest */ public void onCompleted() { - if (_sessions != null && _sessions.size() > 0) + if (_sessions != null) { - for (HttpSession s:_sessions) + for (Session s:_sessions) leaveSession(s); } } + /** + * Called when a response is about to be committed, ie sent + * back to the client + */ + public void onResponseCommit() + { + if (_sessions != null) + { + for (Session s:_sessions) + commitSession(s); + } + } + /** * Find a session that this request has already entered for the * given SessionHandler diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCache.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCache.java index 096bc9e6395..48ccac95481 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCache.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCache.java @@ -91,6 +91,12 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements * deleted from the SessionDataStore. */ protected boolean _removeUnloadableSessions; + + /** + * If true, when a response is about to be committed back to the client, + * a dirty session will be flushed to the session store. + */ + protected boolean _flushOnResponseCommit; /** * Create a new Session object from pre-existing session data @@ -296,6 +302,18 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements _removeUnloadableSessions = removeUnloadableSessions; } + @Override + public void setFlushOnResponseCommit(boolean flushOnResponseCommit) + { + _flushOnResponseCommit = flushOnResponseCommit; + } + + @Override + public boolean isFlushOnResponseCommit() + { + return _flushOnResponseCommit; + } + /** * Get a session object. * @@ -501,6 +519,42 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements } } + /** + * A response that has accessed this session is about to + * be returned to the client. Pass the session to the store + * to persist, so that any changes will be visible to + * subsequent requests on the same node (if using NullSessionCache), + * or on other nodes. + */ + @Override + public void commit(Session session) throws Exception + { + if (session == null) + return; + + try (Lock lock = session.lock()) + { + //only write the session out at this point if the attributes changed. If only + //the lastAccess/expiry time changed defer the write until the last request exits + if (session.getSessionData().isDirty() && _flushOnResponseCommit) + { + if (LOG.isDebugEnabled()) + LOG.debug("Flush session {} on response commit", session); + //save the session + if (!_sessionDataStore.isPassivating()) + { + _sessionDataStore.store(session.getId(), session.getSessionData()); + } + else + { + session.willPassivate(); + _sessionDataStore.store(session.getId(), session.getSessionData()); + session.didActivate(); + } + } + } + } + /** * @deprecated */ @@ -739,6 +793,8 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements if (_sessionDataStore.isPassivating()) session.willPassivate(); + //Fake being dirty to force the write + session.getSessionData().setDirty(true); _sessionDataStore.store(session.getId(), session.getSessionData()); } @@ -748,7 +804,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements catch (Exception e) { LOG.warn("Passivation of idle session {} failed", session.getId(), e); - //session.updateInactivityTimer(); } } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCacheFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCacheFactory.java new file mode 100644 index 00000000000..b29e5585a19 --- /dev/null +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCacheFactory.java @@ -0,0 +1,114 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.session; + +/** + * AbstractSessionCacheFactory + * + * Base class for SessionCacheFactories. + * + */ +public abstract class AbstractSessionCacheFactory implements SessionCacheFactory +{ + int _evictionPolicy; + boolean _saveOnInactiveEvict; + boolean _saveOnCreate; + boolean _removeUnloadableSessions; + boolean _flushOnResponseCommit; + + /** + * @return the flushOnResponseCommit + */ + public boolean isFlushOnResponseCommit() + { + return _flushOnResponseCommit; + } + + /** + * @param flushOnResponseCommit the flushOnResponseCommit to set + */ + public void setFlushOnResponseCommit(boolean flushOnResponseCommit) + { + _flushOnResponseCommit = flushOnResponseCommit; + } + + /** + * @return the saveOnCreate + */ + public boolean isSaveOnCreate() + { + return _saveOnCreate; + } + + /** + * @param saveOnCreate the saveOnCreate to set + */ + public void setSaveOnCreate(boolean saveOnCreate) + { + _saveOnCreate = saveOnCreate; + } + + /** + * @return the removeUnloadableSessions + */ + public boolean isRemoveUnloadableSessions() + { + return _removeUnloadableSessions; + } + + /** + * @param removeUnloadableSessions the removeUnloadableSessions to set + */ + public void setRemoveUnloadableSessions(boolean removeUnloadableSessions) + { + _removeUnloadableSessions = removeUnloadableSessions; + } + + /** + * @return the evictionPolicy + */ + public int getEvictionPolicy() + { + return _evictionPolicy; + } + + /** + * @param evictionPolicy the evictionPolicy to set + */ + public void setEvictionPolicy(int evictionPolicy) + { + _evictionPolicy = evictionPolicy; + } + + /** + * @return the saveOnInactiveEvict + */ + public boolean isSaveOnInactiveEvict() + { + return _saveOnInactiveEvict; + } + + /** + * @param saveOnInactiveEvict the saveOnInactiveEvict to set + */ + public void setSaveOnInactiveEvict(boolean saveOnInactiveEvict) + { + _saveOnInactiveEvict = saveOnInactiveEvict; + } +} diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java index b876c2e81ef..01dc84e87d6 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java @@ -129,10 +129,14 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem long savePeriodMs = (_savePeriodSec <= 0 ? 0 : TimeUnit.SECONDS.toMillis(_savePeriodSec)); if (LOG.isDebugEnabled()) - LOG.debug("Store: id={}, dirty={}, lsave={}, period={}, elapsed={}", id, data.isDirty(), data.getLastSaved(), savePeriodMs, (System.currentTimeMillis() - lastSave)); + { + LOG.debug("Store: id={}, mdirty={}, dirty={}, lsave={}, period={}, elapsed={}", id, data.isMetaDataDirty(), + data.isDirty(), data.getLastSaved(), savePeriodMs, (System.currentTimeMillis() - lastSave)); + } - //save session if attribute changed or never been saved or time between saves exceeds threshold - if (data.isDirty() || (lastSave <= 0) || ((System.currentTimeMillis() - lastSave) >= savePeriodMs)) + //save session if attribute changed, never been saved or metadata changed (eg expiry time) and save interval exceeded + if (data.isDirty() || (lastSave <= 0) || + (data.isMetaDataDirty() && ((System.currentTimeMillis() - lastSave) >= savePeriodMs))) { //set the last saved time to now data.setLastSaved(System.currentTimeMillis()); @@ -140,7 +144,7 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem { //call the specific store method, passing in previous save time doStore(id, data, lastSave); - data.setDirty(false); //only undo the dirty setting if we saved it + data.clean(); //unset all dirty flags } catch (Exception e) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/DefaultSessionCacheFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/DefaultSessionCacheFactory.java index b1261647414..87b945e5a0b 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/DefaultSessionCacheFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/DefaultSessionCacheFactory.java @@ -23,80 +23,8 @@ package org.eclipse.jetty.server.session; * * Factory for creating new DefaultSessionCaches. */ -public class DefaultSessionCacheFactory implements SessionCacheFactory +public class DefaultSessionCacheFactory extends AbstractSessionCacheFactory { - int _evictionPolicy; - boolean _saveOnInactiveEvict; - boolean _saveOnCreate; - boolean _removeUnloadableSessions; - - /** - * @return the saveOnCreate - */ - public boolean isSaveOnCreate() - { - return _saveOnCreate; - } - - /** - * @param saveOnCreate the saveOnCreate to set - */ - public void setSaveOnCreate(boolean saveOnCreate) - { - _saveOnCreate = saveOnCreate; - } - - /** - * @return the removeUnloadableSessions - */ - public boolean isRemoveUnloadableSessions() - { - return _removeUnloadableSessions; - } - - /** - * @param removeUnloadableSessions the removeUnloadableSessions to set - */ - public void setRemoveUnloadableSessions(boolean removeUnloadableSessions) - { - _removeUnloadableSessions = removeUnloadableSessions; - } - - /** - * @return the evictionPolicy - */ - public int getEvictionPolicy() - { - return _evictionPolicy; - } - - /** - * @param evictionPolicy the evictionPolicy to set - */ - public void setEvictionPolicy(int evictionPolicy) - { - _evictionPolicy = evictionPolicy; - } - - /** - * @return the saveOnInactiveEvict - */ - public boolean isSaveOnInactiveEvict() - { - return _saveOnInactiveEvict; - } - - /** - * @param saveOnInactiveEvict the saveOnInactiveEvict to set - */ - public void setSaveOnInactiveEvict(boolean saveOnInactiveEvict) - { - _saveOnInactiveEvict = saveOnInactiveEvict; - } - - /** - * @see org.eclipse.jetty.server.session.SessionCacheFactory#getSessionCache(org.eclipse.jetty.server.session.SessionHandler) - */ @Override public SessionCache getSessionCache(SessionHandler handler) { @@ -105,6 +33,7 @@ public class DefaultSessionCacheFactory implements SessionCacheFactory cache.setSaveOnInactiveEviction(isSaveOnInactiveEvict()); cache.setSaveOnCreate(isSaveOnCreate()); cache.setRemoveUnloadableSessions(isRemoveUnloadableSessions()); + cache.setFlushOnResponseCommit(isFlushOnResponseCommit()); return cache; } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionCache.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionCache.java index e22919eed7b..c589aab4bdb 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionCache.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionCache.java @@ -18,12 +18,7 @@ package org.eclipse.jetty.server.session; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSessionAttributeListener; -import javax.servlet.http.HttpSessionBindingEvent; /** * NullSessionCache @@ -35,151 +30,6 @@ import javax.servlet.http.HttpSessionBindingEvent; */ public class NullSessionCache extends AbstractSessionCache { - /** - * If the writethrough mode is ALWAYS or NEW, then use an - * attribute listener to ascertain when the attribute has changed. - * - */ - public class WriteThroughAttributeListener implements HttpSessionAttributeListener - { - Set _sessionsBeingWritten = ConcurrentHashMap.newKeySet(); - - @Override - public void attributeAdded(HttpSessionBindingEvent event) - { - doAttributeChanged(event); - } - - @Override - public void attributeRemoved(HttpSessionBindingEvent event) - { - doAttributeChanged(event); - } - - @Override - public void attributeReplaced(HttpSessionBindingEvent event) - { - doAttributeChanged(event); - } - - private void doAttributeChanged(HttpSessionBindingEvent event) - { - if (_writeThroughMode == WriteThroughMode.ON_EXIT) - return; - - Session session = (Session)event.getSession(); - - SessionDataStore store = getSessionDataStore(); - - if (store == null) - return; - - if (_writeThroughMode == WriteThroughMode.ALWAYS || (_writeThroughMode == WriteThroughMode.NEW && session.isNew())) - { - //ensure that a call to willPassivate doesn't result in a passivation - //listener removing an attribute, which would cause this listener to - //be called again - if (_sessionsBeingWritten.add(session)) - { - try - { - //should hold the lock on the session, but as sessions are never shared - //with the NullSessionCache, there can be no other thread modifying the - //same session at the same time (although of course there can be another - //request modifying its copy of the session data, so it is impossible - //to guarantee the order of writes). - if (store.isPassivating()) - session.willPassivate(); - store.store(session.getId(), session.getSessionData()); - if (store.isPassivating()) - session.didActivate(); - } - catch (Exception e) - { - LOG.warn("Write through of {} failed", e); - } - finally - { - _sessionsBeingWritten.remove(session); - } - } - } - } - } - - /** - * Defines the circumstances a session will be written to the backing store. - */ - public enum WriteThroughMode - { - /** - * ALWAYS means write through every attribute change. - */ - ALWAYS, - /** - * NEW means to write through every attribute change only - * while the session is freshly created, ie its id has not yet been returned to the client - */ - NEW, - /** - * ON_EXIT means write the session only when the request exits - * (which is the default behaviour of AbstractSessionCache) - */ - ON_EXIT - } - - private WriteThroughMode _writeThroughMode = WriteThroughMode.ON_EXIT; - protected WriteThroughAttributeListener _listener = null; - - /** - * @return the writeThroughMode - */ - public WriteThroughMode getWriteThroughMode() - { - return _writeThroughMode; - } - - /** - * @param writeThroughMode the writeThroughMode to set - */ - public void setWriteThroughMode(WriteThroughMode writeThroughMode) - { - if (getSessionHandler() == null) - throw new IllegalStateException("No SessionHandler"); - - //assume setting null is the same as ON_EXIT - if (writeThroughMode == null) - { - if (_listener != null) - getSessionHandler().removeEventListener(_listener); - _listener = null; - _writeThroughMode = WriteThroughMode.ON_EXIT; - return; - } - - switch (writeThroughMode) - { - case ON_EXIT: - { - if (_listener != null) - getSessionHandler().removeEventListener(_listener); - _listener = null; - break; - } - case NEW: - case ALWAYS: - { - if (_listener == null) - { - _listener = new WriteThroughAttributeListener(); - getSessionHandler().addEventListener(_listener); - } - break; - } - } - _writeThroughMode = writeThroughMode; - } - /** * @param handler The SessionHandler related to this SessionCache */ diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionCacheFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionCacheFactory.java index dd4a4cd0986..40bf5f45f9d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionCacheFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionCacheFactory.java @@ -18,75 +18,51 @@ package org.eclipse.jetty.server.session; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + /** * NullSessionCacheFactory * * Factory for NullSessionCaches. */ -public class NullSessionCacheFactory implements SessionCacheFactory +public class NullSessionCacheFactory extends AbstractSessionCacheFactory { - boolean _saveOnCreate; - boolean _removeUnloadableSessions; - NullSessionCache.WriteThroughMode _writeThroughMode; - - /** - * @return the writeThroughMode - */ - public NullSessionCache.WriteThroughMode getWriteThroughMode() + private static final Logger LOG = Log.getLogger("org.eclipse.jetty.server.session"); + + @Override + public int getEvictionPolicy() { - return _writeThroughMode; + return SessionCache.EVICT_ON_SESSION_EXIT; //never actually stored } - /** - * @param writeThroughMode the writeThroughMode to set - */ - public void setWriteThroughMode(NullSessionCache.WriteThroughMode writeThroughMode) + @Override + public void setEvictionPolicy(int evictionPolicy) { - _writeThroughMode = writeThroughMode; + if (LOG.isDebugEnabled()) + LOG.debug("Ignoring eviction policy setting for NullSessionCaches"); } - /** - * @return the saveOnCreate - */ - public boolean isSaveOnCreate() + @Override + public boolean isSaveOnInactiveEvict() { - return _saveOnCreate; + return false; //never kept in cache } - /** - * @param saveOnCreate the saveOnCreate to set - */ - public void setSaveOnCreate(boolean saveOnCreate) + @Override + public void setSaveOnInactiveEvict(boolean saveOnInactiveEvict) { - _saveOnCreate = saveOnCreate; + if (LOG.isDebugEnabled()) + LOG.debug("Ignoring eviction policy setting for NullSessionCaches"); } - /** - * @return the removeUnloadableSessions - */ - public boolean isRemoveUnloadableSessions() - { - return _removeUnloadableSessions; - } - - /** - * @param removeUnloadableSessions the removeUnloadableSessions to set - */ - public void setRemoveUnloadableSessions(boolean removeUnloadableSessions) - { - _removeUnloadableSessions = removeUnloadableSessions; - } - - /** - * @see org.eclipse.jetty.server.session.SessionCacheFactory#getSessionCache(org.eclipse.jetty.server.session.SessionHandler) - */ @Override public SessionCache getSessionCache(SessionHandler handler) { NullSessionCache cache = new NullSessionCache(handler); cache.setSaveOnCreate(isSaveOnCreate()); cache.setRemoveUnloadableSessions(isRemoveUnloadableSessions()); - cache.setWriteThroughMode(_writeThroughMode); + cache.setFlushOnResponseCommit(isFlushOnResponseCommit()); return cache; } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java index 76d146b2ea3..9d89cefa49c 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java @@ -379,16 +379,31 @@ public class Session implements SessionHandler.SessionIf */ public void didActivate() { - HttpSessionEvent event = new HttpSessionEvent(this); - for (Iterator iter = _sessionData.getKeys().iterator(); iter.hasNext();) + //A passivate listener might remove a non-serializable attribute that + //the activate listener might put back in again, which would spuriously + //set the dirty bit to true, causing another round of passivate/activate + //when the request exits. The store clears the dirty bit if it does a + //save, so ensure dirty flag is set to the value determined by the store, + //not a passivation listener. + boolean dirty = getSessionData().isDirty(); + + try { - Object value = _sessionData.getAttribute(iter.next()); - if (value instanceof HttpSessionActivationListener) + HttpSessionEvent event = new HttpSessionEvent(this); + for (Iterator iter = _sessionData.getKeys().iterator(); iter.hasNext();) { - HttpSessionActivationListener listener = (HttpSessionActivationListener)value; - listener.sessionDidActivate(event); + Object value = _sessionData.getAttribute(iter.next()); + if (value instanceof HttpSessionActivationListener) + { + HttpSessionActivationListener listener = (HttpSessionActivationListener)value; + listener.sessionDidActivate(event); + } } } + finally + { + getSessionData().setDirty(dirty); + } } /** @@ -512,6 +527,10 @@ public class Session implements SessionHandler.SessionIf { _sessionData.setMaxInactiveMs((long)secs * 1000L); _sessionData.calcAndSetExpiry(); + //dirty metadata writes can be skipped, but changing the + //maxinactiveinterval should write the session out because + //it may affect the session on other nodes, or on the same + //node in the case of the nullsessioncache _sessionData.setDirty(true); if (LOG.isDebugEnabled()) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionCache.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionCache.java index eb2fabc35a6..1b16173d73e 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionCache.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionCache.java @@ -164,7 +164,17 @@ public interface SessionCache extends LifeCycle * @throws Exception if any error occurred */ void release(String id, Session session) throws Exception; - + + /** + * Called when a response is about to be committed. The + * cache can write the session to ensure that the + * SessionDataStore contains changes to the session + * that occurred during the lifetime of the request. This + * can help ensure that if a subsequent request goes to a + * different server, it will be able to see the session + * changes via the shared store. + */ + void commit(Session session) throws Exception; /** * Check to see if a Session is in the cache. Does NOT consult @@ -288,4 +298,18 @@ public interface SessionCache extends LifeCycle * @return if true unloadable session will be deleted */ boolean isRemoveUnloadableSessions(); + + /** + * If true, a dirty session will be written to the SessionDataStore + * just before a response is returned to the client. This ensures + * that subsequent requests to either the same node or a different + * node see the changed session data. + */ + void setFlushOnResponseCommit(boolean flushOnResponse); + + /** + * @return true if dirty sessions should be written + * before the response is committed. + */ + boolean isFlushOnResponseCommit(); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java index cf6617f78f9..bd2cb95d4dd 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java @@ -58,6 +58,7 @@ public class SessionData implements Serializable protected Map _attributes; protected boolean _dirty; protected long _lastSaved; //time in msec since last save + protected boolean _metaDataDirty; //non-attribute data has changed /** * Serialize the attribute map of the session. @@ -235,6 +236,22 @@ public class SessionData implements Serializable _dirty = dirty; } + /** + * @return the metaDataDirty + */ + public boolean isMetaDataDirty() + { + return _metaDataDirty; + } + + /** + * @param metaDataDirty true means non-attribute data has changed + */ + public void setMetaDataDirty(boolean metaDataDirty) + { + _metaDataDirty = metaDataDirty; + } + /** * @param name the name of the attribute * @return the value of the attribute named @@ -267,6 +284,15 @@ public class SessionData implements Serializable setDirty(true); } + /** + * Clear all dirty flags. + */ + public void clean() + { + setDirty(false); + setMetaDataDirty(false); + } + public void putAllAttributes(Map attributes) { _attributes.putAll(attributes); @@ -366,11 +392,13 @@ public class SessionData implements Serializable public void calcAndSetExpiry(long time) { setExpiry(calcExpiry(time)); + setMetaDataDirty(true); } public void calcAndSetExpiry() { setExpiry(calcExpiry()); + setMetaDataDirty(true); } public long getCreated() diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java index 7c958c465eb..9d79e8507bd 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java @@ -350,10 +350,9 @@ public class SessionHandler extends ScopedHandler } /** - * Called by the {@link SessionHandler} when a session is last accessed by a request. + * Called when a request is finally leaving a session. * * @param session the session object - * @see #access(HttpSession, boolean) */ public void complete(HttpSession session) { @@ -373,6 +372,28 @@ public class SessionHandler extends ScopedHandler LOG.warn(e); } } + + /** + * Called when a response is about to be committed. + * We might take this opportunity to persist the session + * so that any subsequent requests to other servers + * will see the modifications. + */ + public void commit(HttpSession session) + { + if (session == null) + return; + + Session s = ((SessionIf)session).getSession(); + try + { + _sessionCache.commit(s); + } + catch (Exception e) + { + LOG.warn(e); + } + } @Deprecated public void complete(Session session, Request baseRequest) diff --git a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/AbstractSessionCacheTest.java b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/AbstractSessionCacheTest.java new file mode 100644 index 00000000000..c889b27bb85 --- /dev/null +++ b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/AbstractSessionCacheTest.java @@ -0,0 +1,513 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.session; + +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.servlet.http.HttpSessionActivationListener; +import javax.servlet.http.HttpSessionEvent; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Base class for all tests on all flavours of SessionCache + * + */ +public abstract class AbstractSessionCacheTest +{ + + public static class TestSessionActivationListener implements HttpSessionActivationListener + { + public int passivateCalls = 0; + public int activateCalls = 0; + + @Override + public void sessionWillPassivate(HttpSessionEvent se) + { + ++passivateCalls; + } + + @Override + public void sessionDidActivate(HttpSessionEvent se) + { + ++activateCalls; + } + } + + public abstract AbstractSessionCacheFactory newSessionCacheFactory(int evictionPolicy, boolean saveOnCreate, + boolean saveOnInactiveEvict, boolean removeUnloadableSessions, + boolean flushOnResponseCommit); + + /** + * Test that a new Session object can be created from + * previously persisted data (SessionData). + */ + @Test + public void testNewSessionFromPersistedData() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); + cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); + DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(true);//fake passivation + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + + context.start(); + + long now = System.currentTimeMillis(); + //fake persisted data + SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); + Session session = cache.newSession(data); + assertNotNull(session); + assertEquals("1234", session.getId()); + } + + + /** + * Test that the cache can load from the SessionDataStore + */ + @Test + public void testGetSessionNotInCache() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + AbstractSessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); + SessionCache cache = cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + //put session data into the store + long now = System.currentTimeMillis(); + SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); + store.store("1234", data); + + assertFalse(cache.contains("1234")); + + Session session = cache.get("1234"); + assertEquals(1, session.getRequests()); + assertNotNull(session); + assertEquals("1234", session.getId()); + assertEquals(now - 20, session.getCreationTime()); + } + + @Test + public void testCommit() throws Exception + { + //Test state of session with call to commit + + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + //flushOnResponseCommit is true + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, true); + SessionCache cache = cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + //Mimic various states of a session when a response is about + //to be committed: + + //call commit: session has not changed, should not be written + store._numSaves.set(0); //clear save counter + Session session = createUnExpiredSession(cache, store, "1234"); + cache.add("1234", session); + session.getSessionData().setLastSaved(100);//simulate previously saved + commitAndCheckSaveState(cache, store, session, false, true, false, true, 0, 0); + + //call commit: session has changed, should be written + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "456"); + cache.add("456", session); + session.getSessionData().setLastSaved(100);//simulate previously saved + session.setAttribute("foo", "bar"); + commitAndCheckSaveState(cache, store, session, true, true, false, false, 0, 1); + + //call commit: only the metadata has changed will not be written + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "678"); + cache.add("678", session); + session.getSessionData().setLastSaved(100);//simulate previously saved + session.getSessionData().calcAndSetExpiry(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1)); + commitAndCheckSaveState(cache, store, session, false, true, false, true, 0, 0); + + //Test again with a savePeriod set - as savePeriod only + //affects saving when the session is not dirty, the savePeriod + //should not affect whether or not the session is saved on call + //to commit + store.setSavePeriodSec(60); + + //call commit: session has not changed, should not be written anyway + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "890"); + cache.add("890", session); + session.getSessionData().setLastSaved(100);//simulate previously saved + commitAndCheckSaveState(cache, store, session, false, true, false, true, 0, 0); + + //call commit: session has changed so session must be written + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "012"); + cache.add("012", session); + session.getSessionData().setLastSaved(100);//simulate previously saved + session.setAttribute("foo", "bar"); + commitAndCheckSaveState(cache, store, session, true, true, false, false, 0, 1); + + //call commit: only the metadata has changed will not be written + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "234"); + session.getSessionData().setMetaDataDirty(true); + cache.add("234", session); + session.getSessionData().setLastSaved(100);//simulate previously saved + session.getSessionData().calcAndSetExpiry(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1)); + commitAndCheckSaveState(cache, store, session, false, true, false, true, 0, 0); + } + + @Test + public void testCommitAndRelease() throws Exception + { + //test what happens with various states of a session when commit + //is called before release + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + //flushOnResponseCommit is true + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, true); + SessionCache cache = cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + //Mimic various states of a session when a response is about + //to be committed: + + //call commit: session has not changed, should not be written + Session session = createUnExpiredSession(cache, store, "1234"); + cache.add("1234", session); + commitAndCheckSaveState(cache, store, session, false, true, false, true, 0, 0); + //call release: session has not changed, but metadata has, should be written + cache.release("1234", session); + assertEquals(1, store._numSaves.get()); + assertFalse(session.getSessionData().isDirty()); + assertFalse(session.getSessionData().isMetaDataDirty()); + + //call commit: session has changed, should be written + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "456"); + cache.add("456", session); + session.setAttribute("foo", "bar"); + session.getSessionData().setLastSaved(100);//simulate not "new" session, ie has been previously saved + commitAndCheckSaveState(cache, store, session, true, true, false, false, 0, 1); + //call release: session not dirty but release changes metadata, so it will be saved + cache.release("456", session); + assertEquals(2, store._numSaves.get()); + assertFalse(session.getSessionData().isDirty()); + assertFalse(session.getSessionData().isMetaDataDirty()); + + //call commit: only the metadata has changed will not be written + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "678"); + session.getSessionData().calcAndSetExpiry(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1)); + session.getSessionData().setLastSaved(100); //simulate session not being "new", ie never previously saved + cache.add("678", session); + commitAndCheckSaveState(cache, store, session, false, true, false, true, 0, 0); + //call release: the metadata is dirty session should be written + cache.release("678", session); + assertEquals(1, store._numSaves.get()); + assertFalse(session.getSessionData().isDirty()); + assertFalse(session.getSessionData().isMetaDataDirty()); + + //Test again with a savePeriod set - only save if time last saved exceeds 60sec + store.setSavePeriodSec(60); + + //call commit: session has not changed, should not be written anyway + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "890"); + cache.add("890", session); + session.getSessionData().setLastSaved(100); //simulate last save long time ago + session.getSessionData().setMetaDataDirty(false); + commitAndCheckSaveState(cache, store, session, false, false, false, false, 0, 0); + //call release: not dirty but release sets metadata true, plus save period exceeded so write + cache.release("1234", session); + assertEquals(1, store._numSaves.get()); + assertFalse(session.getSessionData().isDirty()); + assertFalse(session.getSessionData().isMetaDataDirty()); + + //call commit: session has changed so session must be written + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "012"); + cache.add("012", session); + session.getSessionData().setLastSaved(100);//simulate previously saved session + session.setAttribute("foo", "bar"); + session.getSessionData().setMetaDataDirty(false); + commitAndCheckSaveState(cache, store, session, true, false, false, false, 0, 1); + //call release: not dirty, release sets metadirty true (recalc expiry) but previous save too recent to exceed save period --> no write + cache.release("012", session); + assertEquals(1, store._numSaves.get()); + assertFalse(session.getSessionData().isDirty()); + assertTrue(session.getSessionData().isMetaDataDirty()); + + //call commit: only the metadata has changed will not be written + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "234"); + session.getSessionData().calcAndSetExpiry(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1)); + session.getSessionData().setLastSaved(System.currentTimeMillis());//simulate session last saved recently + commitAndCheckSaveState(cache, store, session, false, true, false, true, 0, 0); + //call release: not dirty, release sets metadirty true (recalc expiry) but not within saveperiod so skip write + cache.release("1234", session); + assertEquals(0, store._numSaves.get()); + assertFalse(session.getSessionData().isDirty()); + assertTrue(session.getSessionData().isMetaDataDirty()); + } + + /** + * Test the exist method. + */ + @Test + public void testExists() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); + SessionCache cache = (SessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + //test one that doesn't exist at all + assertFalse(cache.exists("1234")); + + //test one that only exists in the store + long now = System.currentTimeMillis(); + SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); + store.store("1234", data); + assertTrue(cache.exists("1234")); + + //test one that exists in the cache also + Session session = cache.newSession(data); + cache.add("1234", session); + assertTrue(cache.exists("1234")); + } + + /** + * Test the delete method. + */ + @Test + public void testDelete() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, true, false, false, false); + SessionCache cache = cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + //test remove non-existent session + Session session = cache.delete("1234"); + assertNull(session); + + //test remove of existing session in store only + long now = System.currentTimeMillis(); + SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); + store.store("1234", data); + session = cache.delete("1234"); + assertNotNull(session); + assertFalse(store.exists("1234")); + assertFalse(cache.contains("1234")); + + //test remove of session in both store and cache + session = cache.newSession(null, "1234",now - 20, TimeUnit.MINUTES.toMillis(10));//saveOnCreate ensures write to store + cache.add("1234", session); + assertTrue(store.exists("1234")); + assertTrue(cache.contains("1234")); + session = cache.delete("1234"); + assertNotNull(session); + assertFalse(store.exists("1234")); + assertFalse(cache.contains("1234")); + } + + @Test + public void testExpiration() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); + SessionCache cache = cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + //test no candidates, no data in store + Set result = cache.checkExpiration(Collections.emptySet()); + assertTrue(result.isEmpty()); + + //test candidates that are in the cache and NOT expired + long now = System.currentTimeMillis(); + SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); + data.setExpiry(now + TimeUnit.DAYS.toMillis(1)); + Session session = cache.newSession(data); + cache.add("1234", session); + cache.release("1234", session); + assertTrue(cache.exists("1234")); + result = cache.checkExpiration(Collections.singleton("1234")); + assertTrue(result.isEmpty()); + + //test candidates that are in the cache AND expired + data.setExpiry(1); + result = cache.checkExpiration(Collections.singleton("1234")); + assertEquals(1, result.size()); + assertEquals("1234", result.iterator().next()); + + //test candidates that are not in the cache + SessionData data2 = store.newSessionData("567", now - 50, now - 40, now - 30, TimeUnit.MINUTES.toMillis(10)); + data2.setExpiry(1); + store.store("567", data2); + + result = cache.checkExpiration(Collections.emptySet()); + assertThat(result, containsInAnyOrder("1234", "567")); + } + + @Test + public void testSaveOnCreateTrue() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, true, false, false, false); + SessionCache cache = cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + long now = System.currentTimeMillis(); + cache.newSession(null, "1234", now, TimeUnit.MINUTES.toMillis(10)); + assertTrue(store.exists("1234")); + } + + @Test + public void testSaveOnCreateFalse() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); + SessionCache cache = cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + long now = System.currentTimeMillis(); + cache.newSession(null, "1234", now, TimeUnit.MINUTES.toMillis(10)); + assertFalse(store.exists("1234")); + } + + public void commitAndCheckSaveState(SessionCache cache, TestSessionDataStore store, Session session, + boolean expectedBeforeDirty, boolean expectedBeforeMetaDirty, + boolean expectedAfterDirty, boolean expectedAfterMetaDirty, + int expectedBeforeNumSaves, int expectedAfterNumSaves) + throws Exception + { + assertEquals(expectedBeforeDirty, session.getSessionData().isDirty()); + assertEquals(expectedBeforeMetaDirty, session.getSessionData().isMetaDataDirty()); + assertEquals(expectedBeforeNumSaves, store._numSaves.get()); + cache.commit(session); + assertEquals(expectedAfterDirty, session.getSessionData().isDirty()); + assertEquals(expectedAfterMetaDirty, session.getSessionData().isMetaDataDirty()); + assertEquals(expectedAfterNumSaves, store._numSaves.get()); + } + + public Session createUnExpiredSession(SessionCache cache, SessionDataStore store, String id) + { + long now = System.currentTimeMillis(); + SessionData data = store.newSessionData(id, now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); + data.setExpiry(now + TimeUnit.DAYS.toMillis(1)); + return cache.newSession(data); + } +} diff --git a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/DefaultSessionCacheTest.java b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/DefaultSessionCacheTest.java index 21b9bc44b83..46a812a5976 100644 --- a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/DefaultSessionCacheTest.java +++ b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/DefaultSessionCacheTest.java @@ -18,25 +18,19 @@ package org.eclipse.jetty.server.session; -import java.util.Collections; import java.util.Random; -import java.util.Set; import java.util.concurrent.TimeUnit; + import javax.servlet.http.HttpSession; -import javax.servlet.http.HttpSessionActivationListener; -import javax.servlet.http.HttpSessionEvent; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.junit.jupiter.api.Test; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsInAnyOrder; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -44,25 +38,21 @@ import static org.junit.jupiter.api.Assertions.fail; /** * DefaultSessionCacheTest */ -public class DefaultSessionCacheTest +public class DefaultSessionCacheTest extends AbstractSessionCacheTest { - public static class TestSessionActivationListener implements HttpSessionActivationListener + @Override + public AbstractSessionCacheFactory newSessionCacheFactory(int evictionPolicy, boolean saveOnCreate, + boolean saveOnInactiveEvict, boolean removeUnloadableSessions, + boolean flushOnResponseCommit) { - public int passivateCalls = 0; - public int activateCalls = 0; - - @Override - public void sessionWillPassivate(HttpSessionEvent se) - { - ++passivateCalls; - } - - @Override - public void sessionDidActivate(HttpSessionEvent se) - { - ++activateCalls; - } + DefaultSessionCacheFactory factory = new DefaultSessionCacheFactory(); + factory.setEvictionPolicy(evictionPolicy); + factory.setSaveOnCreate(saveOnCreate); + factory.setSaveOnInactiveEvict(saveOnInactiveEvict); + factory.setRemoveUnloadableSessions(removeUnloadableSessions); + factory.setFlushOnResponseCommit(flushOnResponseCommit); + return factory; } @Test @@ -72,9 +62,8 @@ public class DefaultSessionCacheTest int inactivePeriod = 20; int scavengePeriod = 3; - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setSaveOnCreate(true); //ensures that a session is persisted as soon as it is created - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); + AbstractSessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); + SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory(); TestServer server = new TestServer(0, inactivePeriod, scavengePeriod, cacheFactory, storeFactory); ServletContextHandler contextHandler = server.addContext("/test"); @@ -203,8 +192,7 @@ public class DefaultSessionCacheTest context.setContextPath("/test"); context.setServer(server); - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); + AbstractSessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); TestSessionDataStore store = new TestSessionDataStore(true);//fake passivation @@ -234,38 +222,6 @@ public class DefaultSessionCacheTest assertEquals(1, listener.activateCalls); } - /** - * Test that a new Session object can be created from - * previously persisted data (SessionData). - */ - @Test - public void testNewSessionFromPersistedData() - throws Exception - { - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); - DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(true);//fake passivation - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); - - context.start(); - - long now = System.currentTimeMillis(); - //fake persisted data - SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); - Session session = cache.newSession(data); - assertNotNull(session); - assertEquals("1234", session.getId()); - } - /** * Test that a session id can be renewed. */ @@ -318,8 +274,7 @@ public class DefaultSessionCacheTest context.setContextPath("/test"); context.setServer(server); - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); + AbstractSessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); TestSessionDataStore store = new TestSessionDataStore(); @@ -338,42 +293,6 @@ public class DefaultSessionCacheTest assertTrue(((AbstractSessionCache)cache).contains("1234")); } - /** - * Test that the cache can load from the SessionDataStore - */ - @Test - public void testGetSessionNotInCache() - throws Exception - { - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); - DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(); - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); - context.start(); - - //put session data into the store - long now = System.currentTimeMillis(); - SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); - store.store("1234", data); - - assertFalse(cache.contains("1234")); - - Session session = cache.get("1234"); - assertEquals(1, session.getRequests()); - assertNotNull(session); - assertEquals("1234", session.getId()); - assertEquals(now - 20, session.getCreationTime()); - } - @Test public void testAdd() throws Exception @@ -384,8 +303,7 @@ public class DefaultSessionCacheTest context.setContextPath("/test"); context.setServer(server); - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); + AbstractSessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); TestSessionDataStore store = new TestSessionDataStore(); @@ -418,8 +336,7 @@ public class DefaultSessionCacheTest context.setContextPath("/test"); context.setServer(server); - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); TestSessionDataStore store = new TestSessionDataStore(); @@ -458,9 +375,8 @@ public class DefaultSessionCacheTest context.setContextPath("/test"); context.setServer(server); - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); - DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); + SessionCache cache = (SessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); TestSessionDataStore store = new TestSessionDataStore(); cache.setSessionDataStore(store); @@ -478,139 +394,6 @@ public class DefaultSessionCacheTest assertTrue(cache.contains("1234")); } - /** - * Test the exist method. - */ - @Test - public void testExists() - throws Exception - { - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); - DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(); - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); - context.start(); - - //test one that doesn't exist at all - assertFalse(cache.exists("1234")); - - //test one that only exists in the store - long now = System.currentTimeMillis(); - SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); - store.store("1234", data); - assertTrue(cache.exists("1234")); - - //test one that exists in the cache also - Session session = cache.newSession(data); - cache.add("1234", session); - assertTrue(cache.exists("1234")); - } - - /** - * Test the delete method. - */ - @Test - public void testDelete() - throws Exception - { - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); - cacheFactory.setSaveOnCreate(true); - DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(); - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); - context.start(); - - //test remove non-existent session - Session session = cache.delete("1234"); - assertNull(session); - - //test remove of existing session in store only - long now = System.currentTimeMillis(); - SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); - store.store("1234", data); - session = cache.delete("1234"); - assertNotNull(session); - assertFalse(store.exists("1234")); - assertFalse(cache.contains("1234")); - - //test remove of session in both store and cache - session = cache.newSession(null, "1234",now - 20, TimeUnit.MINUTES.toMillis(10));//saveOnCreate ensures write to store - cache.add("1234", session); - assertTrue(store.exists("1234")); - assertTrue(cache.contains("1234")); - session = cache.delete("1234"); - assertNotNull(session); - assertFalse(store.exists("1234")); - assertFalse(cache.contains("1234")); - } - - @Test - public void testExpiration() - throws Exception - { - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); - DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(); - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); - context.start(); - - //test no candidates, no data in store - Set result = cache.checkExpiration(Collections.emptySet()); - assertTrue(result.isEmpty()); - - //test candidates that are in the cache and NOT expired - long now = System.currentTimeMillis(); - SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); - data.setExpiry(now + TimeUnit.DAYS.toMillis(1)); - Session session = cache.newSession(data); - cache.add("1234", session); - cache.release("1234", session); - assertTrue(cache.exists("1234")); - result = cache.checkExpiration(Collections.singleton("1234")); - assertTrue(result.isEmpty()); - - //test candidates that are in the cache AND expired - data.setExpiry(1); - result = cache.checkExpiration(Collections.singleton("1234")); - assertEquals(1, result.size()); - assertEquals("1234", result.iterator().next()); - - //test candidates that are not in the cache - SessionData data2 = store.newSessionData("567", now - 50, now - 40, now - 30, TimeUnit.MINUTES.toMillis(10)); - data2.setExpiry(1); - store.store("567", data2); - - result = cache.checkExpiration(Collections.emptySet()); - assertThat(result, containsInAnyOrder("1234", "567")); - } - @Test public void testCheckInactiveSession() throws Exception @@ -725,54 +508,4 @@ public class DefaultSessionCacheTest SessionData retrieved = store.load("1234"); assertEquals(accessed, retrieved.getAccessed()); //check that we persisted the session before we evicted } - - @Test - public void testSaveOnCreateTrue() - throws Exception - { - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); - cacheFactory.setSaveOnCreate(true); - DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(); - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); - context.start(); - - long now = System.currentTimeMillis(); - cache.newSession(null, "1234", now, TimeUnit.MINUTES.toMillis(10)); - assertTrue(store.exists("1234")); - } - - @Test - public void testSaveOnCreateFalse() - throws Exception - { - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); - cacheFactory.setSaveOnCreate(false); - DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(); - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); - context.start(); - - long now = System.currentTimeMillis(); - cache.newSession(null, "1234", now, TimeUnit.MINUTES.toMillis(10)); - assertFalse(store.exists("1234")); - } } diff --git a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/NullSessionCacheTest.java b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/NullSessionCacheTest.java index 5e63b62101b..6dce6c5e0ef 100644 --- a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/NullSessionCacheTest.java +++ b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/NullSessionCacheTest.java @@ -18,129 +18,37 @@ package org.eclipse.jetty.server.session; -import java.io.Serializable; import java.util.concurrent.TimeUnit; -import javax.servlet.http.HttpSessionActivationListener; -import javax.servlet.http.HttpSessionEvent; - import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; /** * NullSessionCacheTest */ -public class NullSessionCacheTest -{ - public static class SerializableTestObject implements Serializable, HttpSessionActivationListener +public class NullSessionCacheTest extends AbstractSessionCacheTest +{ + @Override + public AbstractSessionCacheFactory newSessionCacheFactory(int evictionPolicy, boolean saveOnCreate, + boolean saveOnInactiveEvict, boolean removeUnloadableSessions, + boolean flushOnResponseCommit) { - int count; - static int passivates = 0; - static int activates = 0; - - public SerializableTestObject(int i) - { - count = i; - } - - @Override - public void sessionWillPassivate(HttpSessionEvent se) - { - //should never be called, as we are replaced with the - //non-serializable object and thus passivate will be called on that - ++passivates; - } - - @Override - public void sessionDidActivate(HttpSessionEvent se) - { - ++activates; - //remove myself, replace with something serializable - se.getSession().setAttribute("pv", new TestObject(count)); - } - } - - public static class TestObject implements HttpSessionActivationListener - { - int i; - static int passivates = 0; - static int activates = 0; - - public TestObject(int j) - { - i = j; - } - - @Override - public void sessionWillPassivate(HttpSessionEvent se) - { - ++passivates; - //remove myself, replace with something serializable - se.getSession().setAttribute("pv", new SerializableTestObject(i)); - } - - @Override - public void sessionDidActivate(HttpSessionEvent se) - { - //this should never be called because we replace ourselves during passivation, - //so it is the SerializableTestObject that is activated instead - ++activates; - } - } - - @Test - public void testWritesWithPassivation() throws Exception - { - //Test that a session that is in the process of being saved cannot cause - //another save via a passivation listener - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - NullSessionCacheFactory cacheFactory = new NullSessionCacheFactory(); - cacheFactory.setWriteThroughMode(NullSessionCache.WriteThroughMode.ALWAYS); - - NullSessionCache cache = (NullSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(true); //pretend to passivate - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); - - context.start(); - - //make a session - long now = System.currentTimeMillis(); - SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); - data.setExpiry(now + TimeUnit.DAYS.toMillis(1)); - Session session = cache.newSession(null, data); //mimic a request making a session - cache.add("1234", session); - //at this point the session should not be saved to the store - assertEquals(0, store._numSaves.get()); - - //set an attribute that is not serializable, should cause a save - TestObject obj = new TestObject(1); - session.setAttribute("pv", obj); - assertTrue(cache._listener._sessionsBeingWritten.isEmpty()); - assertTrue(store.exists("1234")); - assertEquals(1, store._numSaves.get()); - assertEquals(1, TestObject.passivates); - assertEquals(0, TestObject.activates); - assertEquals(1, SerializableTestObject.activates); - assertEquals(0, SerializableTestObject.passivates); + NullSessionCacheFactory factory = new NullSessionCacheFactory(); + factory.setSaveOnCreate(saveOnCreate); + factory.setRemoveUnloadableSessions(removeUnloadableSessions); + factory.setFlushOnResponseCommit(flushOnResponseCommit); + return factory; } @Test - public void testChangeWriteThroughMode() throws Exception + public void testShutdownWithSessionStore() + throws Exception { Server server = new Server(); @@ -148,157 +56,45 @@ public class NullSessionCacheTest context.setContextPath("/test"); context.setServer(server); - NullSessionCacheFactory cacheFactory = new NullSessionCacheFactory(); + AbstractSessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); + SessionCache cache = cacheFactory.getSessionCache(context.getSessionHandler()); - NullSessionCache cache = (NullSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(); + TestSessionDataStore store = new TestSessionDataStore(true);//fake passivation cache.setSessionDataStore(store); context.getSessionHandler().setSessionCache(cache); - - assertEquals(NullSessionCache.WriteThroughMode.ON_EXIT, cache.getWriteThroughMode()); - assertNull(cache._listener); - - //change mode to NEW - cache.setWriteThroughMode(NullSessionCache.WriteThroughMode.NEW); - assertEquals(NullSessionCache.WriteThroughMode.NEW, cache.getWriteThroughMode()); - assertNotNull(cache._listener); - assertEquals(1, context.getSessionHandler()._sessionAttributeListeners.size()); - assertTrue(context.getSessionHandler()._sessionAttributeListeners.contains(cache._listener)); - - //change mode to ALWAYS from NEW, listener should remain - NullSessionCache.WriteThroughAttributeListener old = cache._listener; - cache.setWriteThroughMode(NullSessionCache.WriteThroughMode.ALWAYS); - assertEquals(NullSessionCache.WriteThroughMode.ALWAYS, cache.getWriteThroughMode()); - assertNotNull(cache._listener); - assertSame(old,cache._listener); - assertEquals(1, context.getSessionHandler()._sessionAttributeListeners.size()); - - //check null is same as ON_EXIT - cache.setWriteThroughMode(null); - assertEquals(NullSessionCache.WriteThroughMode.ON_EXIT, cache.getWriteThroughMode()); - assertNull(cache._listener); - assertEquals(0, context.getSessionHandler()._sessionAttributeListeners.size()); - - //change to ON_EXIT - cache.setWriteThroughMode(NullSessionCache.WriteThroughMode.ON_EXIT); - assertEquals(NullSessionCache.WriteThroughMode.ON_EXIT, cache.getWriteThroughMode()); - assertNull(cache._listener); - assertEquals(0, context.getSessionHandler()._sessionAttributeListeners.size()); - } - - @Test - public void testWriteThroughAlways() throws Exception - { - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - NullSessionCacheFactory cacheFactory = new NullSessionCacheFactory(); - cacheFactory.setWriteThroughMode(NullSessionCache.WriteThroughMode.ALWAYS); - - NullSessionCache cache = (NullSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(); - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); context.start(); - //make a session + //put a session in the cache and store long now = System.currentTimeMillis(); SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); - data.setExpiry(now + TimeUnit.DAYS.toMillis(1)); - Session session = cache.newSession(null, data); //mimic a request making a session + Session session = cache.newSession(data); + TestSessionActivationListener listener = new TestSessionActivationListener(); cache.add("1234", session); - //at this point the session should not be saved to the store - assertEquals(0, store._numSaves.get()); - - //check each call to set attribute results in a store - session.setAttribute("colour", "blue"); - assertTrue(store.exists("1234")); - assertEquals(1, store._numSaves.get()); - - //mimic releasing the session after the request is finished - cache.release("1234", session); - assertTrue(store.exists("1234")); + //cache never contains the session assertFalse(cache.contains("1234")); - assertEquals(2, store._numSaves.get()); - - //simulate a new request using the previously created session - //the session should not now be new - session = cache.get("1234"); //get the session again - session.access(now); //simulate a request - session.setAttribute("spin", "left"); - assertTrue(store.exists("1234")); - assertEquals(3, store._numSaves.get()); - cache.release("1234", session); //finish with the session + session.setAttribute("aaa", listener); + //write session out on release + cache.release("1234", session); + assertEquals(1, store._numSaves.get()); + assertEquals(1, listener.passivateCalls); + assertEquals(0, listener.activateCalls); //NullSessionCache always evicts on release, so never reactivates - assertFalse(session.isResident()); + assertTrue(store.exists("1234")); + //cache never contains session + assertFalse(cache.contains("1234")); + + context.stop(); //calls shutdown + + //session should still exist in store + assertTrue(store.exists("1234")); + //cache never contains the session + assertFalse(cache.contains("1234")); + //shutdown does not save session + assertEquals(1, listener.passivateCalls); + assertEquals(0, listener.activateCalls); } - @Test - public void testWriteThroughNew() throws Exception - { - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - NullSessionCacheFactory cacheFactory = new NullSessionCacheFactory(); - cacheFactory.setWriteThroughMode(NullSessionCache.WriteThroughMode.NEW); - - NullSessionCache cache = (NullSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(); - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); - context.start(); - - //make a session - long now = System.currentTimeMillis(); - SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); - data.setExpiry(now + TimeUnit.DAYS.toMillis(1)); - Session session = cache.newSession(null, data); //mimic a request making a session - cache.add("1234", session); - //at this point the session should not be saved to the store - assertEquals(0, store._numSaves.get()); - assertTrue(session.isNew()); - - //check each call to set attribute results in a store while the session is new - session.setAttribute("colour", "blue"); - assertTrue(store.exists("1234")); - assertEquals(1, store._numSaves.get()); - session.setAttribute("charge", "positive"); - assertEquals(2, store._numSaves.get()); - - //mimic releasing the session after the request is finished - cache.release("1234", session); - assertTrue(store.exists("1234")); - assertFalse(cache.contains("1234")); - assertEquals(3, store._numSaves.get()); //even if the session isn't dirty, we will save the access time - - - //simulate a new request using the previously created session - //the session should not now be new, so setAttribute should - //not result in a save - session = cache.get("1234"); //get the session again - session.access(now); //simulate a request - assertFalse(session.isNew()); - assertEquals(3, store._numSaves.get()); - session.setAttribute("spin", "left"); - assertTrue(store.exists("1234")); - assertEquals(3, store._numSaves.get()); - session.setAttribute("flavor", "charm"); - assertEquals(3, store._numSaves.get()); - cache.release("1234", session); //finish with the session - assertEquals(4, store._numSaves.get());//release session should write it out - assertFalse(session.isResident()); - } - @Test public void testNotCached() throws Exception { @@ -324,7 +120,7 @@ public class NullSessionCacheTest data.setExpiry(now + TimeUnit.DAYS.toMillis(1)); Session session = cache.newSession(null, data); //mimic a request making a session cache.add("1234", session); - assertFalse(cache.contains("1234"));//null cache doesn't actually store the session + assertFalse(cache.contains("1234"));//null cache doesn't actually retain the session //mimic releasing the session after the request is finished cache.release("1234", session); @@ -336,7 +132,104 @@ public class NullSessionCacheTest session.access(now); //simulate a request cache.release("1234", session); //finish with the session assertFalse(cache.contains("1234")); - assertFalse(session.isResident()); } + + /** + * Test contains method. + */ + @Test + public void testContains() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); + SessionCache cache = (SessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + //test one that doesn't exist + assertFalse(cache.contains("1234")); + + //test one that exists + long now = System.currentTimeMillis(); + SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); + Session session = cache.newSession(data); + cache.add("1234", session); + assertFalse(cache.contains("1234")); + } + + /** + * Test the exist method. + */ + @Test + public void testExists() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); + SessionCache cache = (SessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + //test one that doesn't exist anywhere at all + assertFalse(cache.exists("1234")); + + //test one that only exists in the store + long now = System.currentTimeMillis(); + SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); + store.store("1234", data); + assertTrue(cache.exists("1234")); + } + + /** + * Test the delete method. + */ + @Test + public void testDelete() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, true, false, false, false); + SessionCache cache = cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + //test remove non-existent session + Session session = cache.delete("1234"); + assertNull(session); + + //test remove of existing session in store only + long now = System.currentTimeMillis(); + SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); + store.store("1234", data); + session = cache.delete("1234"); + assertNull(session); //NullSessionCache never returns the session that was removed from the cache because it was never in the cache! + assertFalse(store.exists("1234")); + assertFalse(cache.contains("1234")); + } } diff --git a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionEvictionFailureTest.java b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionEvictionFailureTest.java index 9a7a0d90801..245cb63c94c 100644 --- a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionEvictionFailureTest.java +++ b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionEvictionFailureTest.java @@ -37,6 +37,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * SessionEvictionFailureTest @@ -192,6 +193,7 @@ public class SessionEvictionFailureTest // Make another request to see if the session is still in the cache and can be used, //allow it to be saved this time + assertTrue(context.getSessionHandler().getSessionCache().contains(TestServer.extractSessionId(sessionCookie))); Request request = client.newRequest(url + "?action=test"); response = request.send(); assertEquals(HttpServletResponse.SC_OK, response.getStatus()); From ac15b1a093bcf6f8d6192dc1b984e62bf63f4e34 Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Wed, 11 Sep 2019 09:38:13 +1000 Subject: [PATCH 051/113] Issue #4076 Fix restart of quickstarted webapp (#4078) Signed-off-by: Jan Bartel --- .../jetty/quickstart/QuickStartConfiguration.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java index bc69cf9fa39..2dc417e42d8 100644 --- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java +++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java @@ -191,6 +191,18 @@ public class QuickStartConfiguration extends AbstractConfiguration } } + @Override + public void postConfigure(WebAppContext context) throws Exception + { + super.postConfigure(context); + ServletContainerInitializersStarter starter = (ServletContainerInitializersStarter)context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZER_STARTER); + if (starter != null) + { + context.removeBean(starter); + context.removeAttribute(AnnotationConfiguration.CONTAINER_INITIALIZER_STARTER); + } + } + protected void quickStart(WebAppContext context) throws Exception { From 03629b461669e7f513eda48a75e2e50d40fd3f5d Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Wed, 11 Sep 2019 10:05:24 +1000 Subject: [PATCH 052/113] Add missing issue 2140 to VERSION.txt --- VERSION.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/VERSION.txt b/VERSION.txt index 26288cd7b37..152172ece90 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -97,6 +97,7 @@ jetty-9.4.18.v20190429 - 29 April 2019 + 3609 Fix infinispan start module dependencies jetty-9.4.17.v20190418 - 18 April 2019 + + 2140 Infinispan and hazelcast changes to scavenge zombie expired sessions. + 3464 Split SslContextFactory into Client and Server + 3549 Directory Listing on Windows reveals Resource Base path + 3555 DefaultHandler Reveals Base Resource Path of each Context From bc96561865d993aa5b4e73378a99da1f79e20f56 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 11 Sep 2019 10:18:17 +1000 Subject: [PATCH 053/113] fixed bad merge Signed-off-by: Greg Wilkins --- .../main/java/org/eclipse/jetty/http2/hpack/Huffman.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java index 0bb3e584ee4..8197eb62898 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java @@ -358,9 +358,9 @@ public class Huffman return decode(buffer, buffer.remaining()); } - public static String decode(ByteBuffer buffer, int length) throws HpackException.CompressionException - { - StringBuilder out = new StringBuilder(length * 2); + public static String decode(ByteBuffer buffer,int length) throws HpackException.CompressionException + { + Utf8StringBuilder utf8 = new Utf8StringBuilder(length*2); int node = 0; int current = 0; int bits = 0; From 90cf7c80bdb965b44f86a204269ce53fbb9bc466 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 11 Sep 2019 10:50:42 +1000 Subject: [PATCH 054/113] avoid exceptions for non iso characters Signed-off-by: Greg Wilkins --- .../jetty/http2/hpack/HpackContext.java | 2 + .../jetty/http2/hpack/HpackEncoder.java | 16 +++---- .../eclipse/jetty/http2/hpack/Huffman.java | 47 +++++++++++-------- .../jetty/http2/hpack/HuffmanTest.java | 5 +- 4 files changed, 40 insertions(+), 30 deletions(-) diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java index dda8ec30cb7..412f09ebc2a 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java @@ -461,6 +461,8 @@ public class HpackContext if (value != null && value.length() > 0) { int huffmanLen = Huffman.octetsNeeded(value); + if (huffmanLen < 0) + throw new IllegalStateException("bad value"); int lenLen = NBitInteger.octectsNeeded(7, huffmanLen); _huffmanValue = new byte[1 + lenLen + huffmanLen]; ByteBuffer buffer = ByteBuffer.wrap(_huffmanValue); diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java index 0e2aeec44da..a6c3bda169b 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java @@ -392,18 +392,18 @@ public class HpackEncoder if (huffman) { // huffman literal value - buffer.put((byte)0x80).mark(); + buffer.put((byte)0x80); - try + int needed = Huffman.octetsNeeded(value); + if (needed >= 0) { - NBitInteger.encode(buffer,7,Huffman.octetsNeeded(value)); - Huffman.encode(buffer,value); + NBitInteger.encode(buffer, 7, needed); + Huffman.encode(buffer, value); } - catch(Throwable th) + else { - LOG.ignore(th); + // Not iso_8859_1 byte[] bytes = value.getBytes(StandardCharsets.UTF_8); - NBitInteger.encode(buffer,7,Huffman.octetsNeeded(bytes)); Huffman.encode(buffer,bytes); } @@ -418,7 +418,7 @@ public class HpackEncoder char c = value.charAt(i); if (c < ' ' || c > 127) { - // Not iso_8859_1, so re-encode remaining as UTF-8 + // Not iso_8859_1, so re-encode as UTF-8 buffer.reset(); byte[] bytes = value.getBytes(StandardCharsets.UTF_8); NBitInteger.encode(buffer,7,bytes.length); diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java index 8197eb62898..4645a6cebfb 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java @@ -360,7 +360,7 @@ public class Huffman public static String decode(ByteBuffer buffer,int length) throws HpackException.CompressionException { - Utf8StringBuilder utf8 = new Utf8StringBuilder(length*2); + Utf8StringBuilder utf8 = new Utf8StringBuilder(length * 2); int node = 0; int current = 0; int bits = 0; @@ -380,7 +380,7 @@ public class Huffman throw new HpackException.CompressionException("EOS in content"); // terminal node - utf8.append((byte)(0xFF&rowsym[node])); + utf8.append((byte)(0xFF & rowsym[node])); bits -= rowbits[node]; node = 0; } @@ -413,7 +413,7 @@ public class Huffman break; } - utf8.append((byte)(0xFF&rowsym[node])); + utf8.append((byte)(0xFF & rowsym[node])); bits -= rowbits[node]; node = 0; } @@ -462,7 +462,7 @@ public class Huffman { char c = s.charAt(i); if (c >= 128 || c < ' ') - throw new IllegalArgumentException(); + return -1; needed += table[c][1]; } @@ -470,17 +470,23 @@ public class Huffman } private static int octetsNeeded(final int[][] table,byte[] b) - { - int needed=0; + { + int needed = 0; int len = b.length; - for (int i=0;i= 8) + while (n >= 8) { n -= 8; - array[p++]=(byte)(current >> n); + array[p++] = (byte)(current >> n); } } - if (n > 0) + if (n > 0) { - current <<= (8 - n); - current |= (0xFF >>> n); - array[p++]=(byte)current; + current <<= (8 - n); + current |= (0xFF >>> n); + array[p++] = (byte)current; } - - buffer.position(p-buffer.arrayOffset()); + + buffer.position(p - buffer.arrayOffset()); } } diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HuffmanTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HuffmanTest.java index 802427f1866..5a947b8fd78 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HuffmanTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HuffmanTest.java @@ -25,11 +25,13 @@ import java.util.stream.Stream; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.TypeUtil; +import org.hamcrest.Matchers; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -77,8 +79,7 @@ public class HuffmanTest { String s = "bad '" + bad + "'"; - assertThrows(IllegalArgumentException.class, - () -> Huffman.octetsNeeded(s)); + assertThat(Huffman.octetsNeeded(s), Matchers.is(-1)); assertThrows(BufferOverflowException.class, () -> Huffman.encode(BufferUtil.allocate(32), s)); From 85cdc0d6c4c7dc860959178280f26c23ba400f99 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Wed, 11 Sep 2019 12:14:47 +1000 Subject: [PATCH 055/113] Reworked OpenId demo into test using a local test OpenIdProvider Signed-off-by: Lachlan Roberts --- jetty-openid/pom.xml | 6 + .../security/openid/OpenIdCredentials.java | 5 +- ...emo.java => OpenIdAuthenticationTest.java} | 278 +++++++++--------- .../jetty/security/openid/OpenIdProvider.java | 236 +++++++++++++++ .../test/resources/jetty-logging.properties | 1 + 5 files changed, 386 insertions(+), 140 deletions(-) rename jetty-openid/src/test/java/org/eclipse/jetty/security/openid/{OpenIdAuthenticationDemo.java => OpenIdAuthenticationTest.java} (55%) create mode 100644 jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdProvider.java diff --git a/jetty-openid/pom.xml b/jetty-openid/pom.xml index 58415f2455c..0868ea578ad 100644 --- a/jetty-openid/pom.xml +++ b/jetty-openid/pom.xml @@ -54,5 +54,11 @@ jetty-test-helper test + + org.eclipse.jetty + jetty-client + ${project.version} + test + diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java index 20917f2bec7..c32d236f732 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java @@ -158,8 +158,9 @@ public class OpenIdCredentials implements Serializable if (sections.length != 3) throw new IllegalArgumentException("JWT does not contain 3 sections"); - String jwtHeaderString = new String(Base64.getDecoder().decode(sections[0]), StandardCharsets.UTF_8); - String jwtClaimString = new String(Base64.getDecoder().decode(sections[1]), StandardCharsets.UTF_8); + Base64.Decoder decoder = Base64.getDecoder(); + String jwtHeaderString = new String(decoder.decode(sections[0]), StandardCharsets.UTF_8); + String jwtClaimString = new String(decoder.decode(sections[1]), StandardCharsets.UTF_8); String jwtSignature = sections[2]; Map jwtHeader = (Map)JSON.parse(jwtHeaderString); diff --git a/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java b/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationTest.java similarity index 55% rename from jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java rename to jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationTest.java index b92f0935c0e..54dd613c52d 100644 --- a/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationDemo.java +++ b/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdAuthenticationTest.java @@ -18,123 +18,53 @@ package org.eclipse.jetty.security.openid; -import java.io.BufferedWriter; import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; import java.security.Principal; import java.util.Map; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.eclipse.jetty.http.MimeTypes; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.security.Authenticator; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; -import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.util.security.Constraint; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -public class OpenIdAuthenticationDemo +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class OpenIdAuthenticationTest { - public static class AdminPage extends HttpServlet + public static final String CLIENT_ID = "testClient101"; + public static final String CLIENT_SECRET = "secret37989798"; + + private OpenIdProvider openIdProvider; + private Server server; + private ServerConnector connector; + private HttpClient client; + + @BeforeEach + public void setup() throws Exception { - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException - { - response.getWriter().println("

    this is the admin page "+request.getUserPrincipal()+": Home

    "); - } - } + openIdProvider = new OpenIdProvider(CLIENT_ID, CLIENT_SECRET); + openIdProvider.start(); - public static class LoginPage extends HttpServlet - { - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException - { - response.getWriter().println("

    you logged in Home

    "); - } - } - - public static class LogoutPage extends HttpServlet - { - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException - { - request.getSession().invalidate(); - response.sendRedirect("/"); - } - } - - public static class HomePage extends HttpServlet - { - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException - { - response.setContentType(MimeTypes.Type.TEXT_HTML.asString()); - response.getWriter().println("

    Home Page

    "); - - Principal userPrincipal = request.getUserPrincipal(); - if (userPrincipal != null) - { - Map userInfo = (Map)request.getSession().getAttribute(OpenIdAuthenticator.CLAIMS); - response.getWriter().println("

    Welcome: " + userInfo.get("name") + "

    "); - response.getWriter().println("Profile
    "); - response.getWriter().println("Admin
    "); - response.getWriter().println("Logout
    "); - } - else - { - response.getWriter().println("

    Please Login Login

    "); - } - } - } - - public static class ProfilePage extends HttpServlet - { - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException - { - response.setContentType(MimeTypes.Type.TEXT_HTML.asString()); - Map userInfo = (Map)request.getSession().getAttribute(OpenIdAuthenticator.CLAIMS); - - response.getWriter().println("\n" + - "
    \n" + - " \n" + - "

    "+ userInfo.get("name") +"

    \n" + - "

    "+userInfo.get("email")+"

    \n" + - "

    UserId: " + userInfo.get("sub") +"

    \n" + - "
    "); - - response.getWriter().println("Home
    "); - response.getWriter().println("Logout
    "); - } - } - - public static class ErrorPage extends HttpServlet - { - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException - { - response.setContentType(MimeTypes.Type.TEXT_HTML.asString()); - response.getWriter().println("

    error: not authorized

    "); - response.getWriter().println("

    " + request.getUserPrincipal() + "

    "); - } - } - - public static void main(String[] args) throws Exception - { - Server server = new Server(8080); + server = new Server(); + connector = new ServerConnector(server); + server.addConnector(connector); ServletContextHandler context = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS); // Add servlets - context.addServlet(ProfilePage.class, "/profile"); context.addServlet(LoginPage.class, "/login"); - context.addServlet(AdminPage.class, "/admin"); context.addServlet(LogoutPage.class, "/logout"); context.addServlet(HomePage.class, "/*"); context.addServlet(ErrorPage.class, "/error"); @@ -168,47 +98,8 @@ public class OpenIdAuthenticationDemo securityHandler.addConstraintMapping(loginMapping); securityHandler.addConstraintMapping(adminMapping); - /**/ - // Google Authentication - OpenIdConfiguration configuration = new OpenIdConfiguration( - "https://accounts.google.com/", - "1051168419525-5nl60mkugb77p9j194mrh287p1e0ahfi.apps.googleusercontent.com", - "XT_MIsSv_aUCGollauCaJY8S"); - configuration.addScopes("email", "profile"); - /**/ - - /* - // Microsoft Authentication - OpenIdConfiguration configuration = new OpenIdConfiguration( - "https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0", - "5f05dea8-2bd9-45de-b30f-cf5c102b8784", - "IfhQJKi-5[vxhh_=ldqt0y4PkV3z_1ca"); - */ - - /* - // Yahoo Authentication - OpenIdConfiguration configuration = new OpenIdConfiguration( - "https://login.yahoo.com", - "dj0yJmk9ME5Id05yTkdGNDdPJmQ9WVdrOU9VcHVZWEp4TkdrbWNHbzlNQS0tJnM9Y29uc3VtZXJzZWNyZXQmc3Y9MCZ4PTE2", - "1e7f0eeb0ba0af9d9198f9be760f66ae3ea9e3b5"); - configuration.addScopes("sdps-r"); - */ - - - // Create a realm.properties file to associate roles with users - Path tmpDir = Paths.get(System.getProperty("java.io.tmpdir")); - Path tmpPath = Files.createTempFile(tmpDir, "realm", ".properties"); - tmpPath.toFile().deleteOnExit(); - try (BufferedWriter writer = Files.newBufferedWriter(tmpPath, StandardCharsets.UTF_8, StandardOpenOption.WRITE)) - { - // :[, ...] - writer.write("114260987481616800581:,admin"); - } - - // This must be added to the OpenIdLoginService in constructor below - HashLoginService hashLoginService = new HashLoginService(); - hashLoginService.setConfig(tmpPath.toAbsolutePath().toString()); - hashLoginService.setHotReload(true); + // Authentication using local OIDC Provider + OpenIdConfiguration configuration = new OpenIdConfiguration(openIdProvider.getProvider(), CLIENT_ID, CLIENT_SECRET); // Configure OpenIdLoginService optionally providing a base LoginService to provide user roles OpenIdLoginService loginService = new OpenIdLoginService(configuration);//, hashLoginService); @@ -219,6 +110,117 @@ public class OpenIdAuthenticationDemo context.setSecurityHandler(securityHandler); server.start(); - server.join(); + String redirectUri = "http://localhost:"+connector.getLocalPort() + "/j_security_check"; + openIdProvider.addRedirectUri(redirectUri); + + client = new HttpClient(); + client.start(); + } + + @AfterEach + public void stop() throws Exception + { + openIdProvider.stop(); + server.stop(); + } + + @Test + public void testLoginLogout() throws Exception + { + String appUriString = "http://localhost:"+connector.getLocalPort(); + + // Initially not authenticated + ContentResponse response = client.GET(appUriString + "/"); + assertThat(response.getStatus(), is(HttpStatus.OK_200)); + String[] content = response.getContentAsString().split("\n"); + assertThat(content.length, is(1)); + assertThat(content[0], is("not authenticated")); + + // Request to login is success + response = client.GET(appUriString + "/login"); + assertThat(response.getStatus(), is(HttpStatus.OK_200)); + content = response.getContentAsString().split("\n"); + assertThat(content.length, is(1)); + assertThat(content[0], is("success")); + + // Now authenticated we can get info + response = client.GET(appUriString + "/"); + assertThat(response.getStatus(), is(HttpStatus.OK_200)); + content = response.getContentAsString().split("\n"); + assertThat(content.length, is(3)); + assertThat(content[0], is("userId: 123456789")); + assertThat(content[1], is("name: FirstName LastName")); + assertThat(content[2], is("email: FirstName@fake-email.com")); + + // Request to admin page gives 403 as we do not have admin role + response = client.GET(appUriString + "/admin"); + assertThat(response.getStatus(), is(HttpStatus.FORBIDDEN_403)); + + // We are no longer authenticated after logging out + response = client.GET(appUriString + "/logout"); + assertThat(response.getStatus(), is(HttpStatus.OK_200)); + content = response.getContentAsString().split("\n"); + assertThat(content.length, is(1)); + assertThat(content[0], is("not authenticated")); + } + + public static class LoginPage extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + response.getWriter().println("success"); + } + } + + public static class LogoutPage extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + request.getSession().invalidate(); + response.sendRedirect("/"); + } + } + + public static class AdminPage extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + Map userInfo = (Map)request.getSession().getAttribute(OpenIdAuthenticator.CLAIMS); + response.getWriter().println(userInfo.get("sub") + ": success"); + } + } + + public static class HomePage extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + response.setContentType("text/plain"); + Principal userPrincipal = request.getUserPrincipal(); + if (userPrincipal != null) + { + Map userInfo = (Map)request.getSession().getAttribute(OpenIdAuthenticator.CLAIMS); + response.getWriter().println("userId: " + userInfo.get("sub")); + response.getWriter().println("name: " + userInfo.get("name")); + response.getWriter().println("email: " + userInfo.get("email")); + } + else + { + response.getWriter().println("not authenticated"); + } + } + } + + public static class ErrorPage extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException + { + response.setContentType("text/plain"); + response.getWriter().println("not authorized"); + } } } diff --git a/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdProvider.java b/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdProvider.java new file mode 100644 index 00000000000..11170b86890 --- /dev/null +++ b/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdProvider.java @@ -0,0 +1,236 @@ +package org.eclipse.jetty.security.openid; + +import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.UUID; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.component.ContainerLifeCycle; + +public class OpenIdProvider extends ContainerLifeCycle +{ + private static final String CONFIG_PATH = "/.well-known/openid-configuration"; + private static final String AUTH_PATH = "/auth"; + private static final String TOKEN_PATH = "/token"; + private final Map issuedAuthCodes = new HashMap<>(); + + protected final String clientId; + protected final String clientSecret; + protected final List redirectUris = new ArrayList<>(); + + private String provider; + private Server server; + private ServerConnector connector; + + public OpenIdProvider(String clientId, String clientSecret) + { + this.clientId = clientId; + this.clientSecret = clientSecret; + + server = new Server(); + connector = new ServerConnector(server); + server.addConnector(connector); + + ServletContextHandler contextHandler = new ServletContextHandler(); + contextHandler.setContextPath("/"); + contextHandler.addServlet(new ServletHolder(new OpenIdConfigServlet()), CONFIG_PATH); + contextHandler.addServlet(new ServletHolder(new OpenIdAuthEndpoint()), AUTH_PATH); + contextHandler.addServlet(new ServletHolder(new OpenIdTokenEndpoint()), TOKEN_PATH); + server.setHandler(contextHandler); + + addBean(server); + } + + @Override + protected void doStart() throws Exception + { + super.doStart(); + provider = "http://localhost:" + connector.getLocalPort(); + } + + public String getProvider() + { + if (!isStarted()) + throw new IllegalStateException(); + return provider; + } + + public void addRedirectUri(String uri) + { + redirectUris.add(uri); + } + + public class OpenIdAuthEndpoint extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + if (!clientId.equals(req.getParameter("client_id"))) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid client_id"); + return; + } + + String redirectUri = req.getParameter("redirect_uri"); + if (!redirectUris.contains(redirectUri)) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid redirect_uri"); + return; + } + + String scopeString = req.getParameter("scope"); + List scopes = (scopeString == null) ? Collections.emptyList() : Arrays.asList(StringUtil.csvSplit(scopeString)); + if (!scopes.contains("openid")) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "no openid scope"); + return; + } + + if (!"code".equals(req.getParameter("response_type"))) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "response_type must be code"); + return; + } + + String state = req.getParameter("state"); + if (state == null) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "no state param"); + return; + } + + String authCode = UUID.randomUUID().toString().replace("-", ""); + User user = new User(123456789, "FirstName", "LastName"); + issuedAuthCodes.put(authCode, user); + + final Request baseRequest = Request.getBaseRequest(req); + final Response baseResponse = baseRequest.getResponse(); + redirectUri += "?code=" + authCode + "&state=" + state; + int redirectCode = (baseRequest.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? + HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER); + baseResponse.sendRedirect(redirectCode, resp.encodeRedirectURL(redirectUri)); + } + } + + public class OpenIdTokenEndpoint extends HttpServlet + { + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + String code = req.getParameter("code"); + + if (!clientId.equals(req.getParameter("client_id")) || + !clientSecret.equals(req.getParameter("client_secret")) || + !redirectUris.contains(req.getParameter("redirect_uri")) || + !"authorization_code".equals(req.getParameter("grant_type")) || + code == null) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "bad auth request"); + return; + } + + User user = issuedAuthCodes.remove(code); + if (user == null) + { + resp.sendError(HttpServletResponse.SC_FORBIDDEN, "invalid auth code"); + return; + } + + String jwtHeader = "{\"INFO\": \"this is not used or checked in our implementation\"}"; + String jwtBody = user.getIdToken(); + String jwtSignature = "we do not validate signature as we use the authorization code flow"; + + Base64.Encoder encoder = Base64.getEncoder(); + String jwt = encoder.encodeToString(jwtHeader.getBytes()) + "." + + encoder.encodeToString(jwtBody.getBytes()) + "." + + encoder.encodeToString(jwtSignature.getBytes()); + + String accessToken = "ABCDEFG"; + long expiry = System.currentTimeMillis() + Duration.ofMinutes(10).toMillis(); + String response = "{" + + "\"access_token\": \"" + accessToken + "\"," + + "\"id_token\": \"" + jwt + "\"," + + "\"expires_in\": " + expiry + "," + + "\"token_type\": \"Bearer\"" + + "}"; + + resp.setContentType("text/plain"); + resp.getWriter().print(response); + } + } + + public class OpenIdConfigServlet extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + String discoveryDocument = "{" + + "\"issuer\": \"" + provider + "\"," + + "\"authorization_endpoint\": \"" + provider + AUTH_PATH + "\"," + + "\"token_endpoint\": \"" + provider + TOKEN_PATH + "\"," + + "}"; + + resp.getWriter().write(discoveryDocument); + } + } + + public class User + { + private long subject; + private String firstName; + private String lastName; + + public User(String firstName, String lastName) + { + this(new Random().nextLong(), firstName, lastName); + } + + public User(long subject, String firstName, String lastName) + { + this.subject = subject; + this.firstName = firstName; + this.lastName = lastName; + } + + public String getFirstName() + { + return firstName; + } + + public String getLastName() + { + return lastName; + } + + public String getIdToken() + { + return "{" + + "\"iss\": \"" + provider + "\"," + + "\"sub\": \"" + subject + "\"," + + "\"aud\": \"" + clientId + "\"," + + "\"exp\": " + System.currentTimeMillis() + Duration.ofMinutes(1).toMillis() + "," + + "\"name\": \"" + firstName + " " + lastName + "\"," + + "\"email\": \"" + firstName + "@fake-email.com" + "\"" + + "}"; + } + } +} diff --git a/jetty-openid/src/test/resources/jetty-logging.properties b/jetty-openid/src/test/resources/jetty-logging.properties index b569144d1eb..c73ac07f8ac 100755 --- a/jetty-openid/src/test/resources/jetty-logging.properties +++ b/jetty-openid/src/test/resources/jetty-logging.properties @@ -1,2 +1,3 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog +# org.eclipse.jetty.LEVEL=DEBUG # org.eclipse.jetty.security.openid.LEVEL=DEBUG \ No newline at end of file From fbd1ba0c0987ade09244d343edc6a0310a62cf2e Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Wed, 11 Sep 2019 13:00:22 +1000 Subject: [PATCH 056/113] add missing licence header Signed-off-by: Lachlan Roberts --- .../jetty/security/openid/OpenIdProvider.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdProvider.java b/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdProvider.java index 11170b86890..83bfb3441b7 100644 --- a/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdProvider.java +++ b/jetty-openid/src/test/java/org/eclipse/jetty/security/openid/OpenIdProvider.java @@ -1,3 +1,21 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.security.openid; import java.io.IOException; From 37f96bdf2b3c654a92289617ccba1509a663d130 Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Wed, 11 Sep 2019 17:22:14 +1000 Subject: [PATCH 057/113] Issue #4075 Accept pattern such /On* and do exact path match (#4080) * Issue #4075 accept url-pattern such /On* as exact match Signed-off-by: olivier lamy * changes after review from Greg Signed-off-by: olivier lamy --- .../jetty/http/pathmap/ServletPathSpec.java | 19 ++++++++++++------- .../jetty/http/pathmap/PathMappingsTest.java | 3 ++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java index 612e2df5ea7..d214323e6e7 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java @@ -20,9 +20,14 @@ package org.eclipse.jetty.http.pathmap; 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; public class ServletPathSpec extends PathSpec { + + private static final Logger LOG = Log.getLogger(ServletPathSpec.class); + /** * If a servlet or filter path mapping isn't a suffix mapping, ensure * it starts with '/' @@ -69,13 +74,13 @@ public class ServletPathSpec extends PathSpec super.pathDepth = 0; char lastChar = servletPathSpec.charAt(specLength - 1); // prefix based - if ((servletPathSpec.charAt(0) == '/') && (specLength > 1) && (lastChar == '*')) + if (servletPathSpec.charAt(0) == '/' && servletPathSpec.endsWith("/*")) { this.group = PathSpecGroup.PREFIX_GLOB; this.prefix = servletPathSpec.substring(0, specLength - 2); } // suffix based - else if (servletPathSpec.charAt(0) == '*') + else if (servletPathSpec.charAt(0) == '*' && servletPathSpec.length() > 1) { this.group = PathSpecGroup.SUFFIX_GLOB; this.suffix = servletPathSpec.substring(2, specLength); @@ -84,6 +89,11 @@ public class ServletPathSpec extends PathSpec { this.group = PathSpecGroup.EXACT; this.prefix = servletPathSpec; + if (servletPathSpec.endsWith("*") ) + { + LOG.warn("Suspicious URL pattern: '{}'; see sections 12.1 and 12.2 of the Servlet specification", + servletPathSpec); + } } for (int i = 0; i < specLength; i++) @@ -130,11 +140,6 @@ public class ServletPathSpec extends PathSpec { throw new IllegalArgumentException("Servlet Spec 12.2 violation: glob '*' can only exist at end of prefix based matches: bad spec \"" + servletPathSpec + "\""); } - - if (idx < 1 || servletPathSpec.charAt(idx - 1) != '/') - { - throw new IllegalArgumentException("Servlet Spec 12.2 violation: suffix glob '*' can only exist after '/': bad spec \"" + servletPathSpec + "\""); - } } else if (servletPathSpec.startsWith("*.")) { diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/pathmap/PathMappingsTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/pathmap/PathMappingsTest.java index 018dc430f43..a609f55d8ba 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/pathmap/PathMappingsTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/pathmap/PathMappingsTest.java @@ -231,6 +231,8 @@ public class PathMappingsTest assertTrue(!new ServletPathSpec("/foo/*").matches("/bar/anything"), "!match /foo/*"); assertTrue(new ServletPathSpec("*.foo").matches("anything.foo"), "match *.foo"); assertTrue(!new ServletPathSpec("*.foo").matches("anything.bar"), "!match *.foo"); + assertTrue(new ServletPathSpec("/On*").matches("/On*"), "match /On*"); + assertTrue(!new ServletPathSpec("/On*").matches("/One"), "!match /One"); assertEquals("10", p.getMatch("/").getResource(), "match / with ''"); @@ -287,7 +289,6 @@ public class PathMappingsTest @ValueSource(strings = { "*", "/foo/*/bar", - "/foo*", "*/foo", "*.foo/*" }) From 387faa7e33fb2fa90801f24402fba8204bc889ca Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Wed, 11 Sep 2019 15:31:57 +1000 Subject: [PATCH 058/113] clean up websocket jetty-logging.properties files Signed-off-by: Lachlan Roberts --- .../test/resources/jetty-logging.properties | 22 +--------- .../test/resources/jetty-logging.properties | 5 +-- .../test/resources/jetty-logging.properties | 34 ++------------- .../test/resources/jetty-logging.properties | 9 ++-- .../test/resources/jetty-logging.properties | 17 -------- .../test/resources/jetty-logging.properties | 42 ++----------------- .../test/resources/jetty-logging.properties | 23 +--------- 7 files changed, 15 insertions(+), 137 deletions(-) diff --git a/jetty-websocket/javax-websocket-common/src/test/resources/jetty-logging.properties b/jetty-websocket/javax-websocket-common/src/test/resources/jetty-logging.properties index 4aebf62b5f8..ef7003a8baa 100644 --- a/jetty-websocket/javax-websocket-common/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/javax-websocket-common/src/test/resources/jetty-logging.properties @@ -1,25 +1,5 @@ -# -# -# ======================================================================== -# Copyright (c) 1995-2017 Mort Bay Consulting 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. -# ======================================================================== -# -# -# org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -org.eclipse.jetty.LEVEL=WARN +# org.eclipse.jetty.LEVEL=DEBUG # org.eclipse.jetty.util.log.stderr.LONG=true # org.eclipse.jetty.server.AbstractConnector.LEVEL=DEBUG # org.eclipse.jetty.io.WriteFlusher.LEVEL=DEBUG diff --git a/jetty-websocket/javax-websocket-server/src/test/resources/jetty-logging.properties b/jetty-websocket/javax-websocket-server/src/test/resources/jetty-logging.properties index d9e757d813a..cfafdb369a4 100644 --- a/jetty-websocket/javax-websocket-server/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/javax-websocket-server/src/test/resources/jetty-logging.properties @@ -1,11 +1,10 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -org.eclipse.jetty.LEVEL=WARN +# org.eclipse.jetty.LEVEL=DEBUG # org.eclipse.jetty.websocket.LEVEL=DEBUG # org.eclipse.jetty.websocket.LEVEL=INFO # org.eclipse.jetty.websocket.LEVEL=WARN # org.eclipse.jetty.websocket.common.io.LEVEL=DEBUG # org.eclipse.jetty.websocket.common.WebSocketSession.LEVEL=DEBUG -# org.eclipse.jetty.websocket.jsr356.LEVEL=DEBUG ### Show state changes on BrowserDebugTool # -- LEAVE THIS AT DEBUG LEVEL -- -org.eclipse.jetty.websocket.jsr356.server.browser.LEVEL=DEBUG +org.eclipse.jetty.websocket.javax.server.browser.LEVEL=DEBUG diff --git a/jetty-websocket/javax-websocket-tests/src/test/resources/jetty-logging.properties b/jetty-websocket/javax-websocket-tests/src/test/resources/jetty-logging.properties index d078063b659..e9d6afe39e7 100644 --- a/jetty-websocket/javax-websocket-tests/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/javax-websocket-tests/src/test/resources/jetty-logging.properties @@ -1,25 +1,5 @@ -# -# -# ======================================================================== -# Copyright (c) 1995-2017 Mort Bay Consulting 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. -# ======================================================================== -# -# -# org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -org.eclipse.jetty.LEVEL=WARN +# org.eclipse.jetty.LEVEL=DEBUG # org.eclipse.jetty.util.log.stderr.LONG=true # org.eclipse.jetty.server.AbstractConnector.LEVEL=DEBUG # org.eclipse.jetty.io.WriteFlusher.LEVEL=DEBUG @@ -28,16 +8,8 @@ org.eclipse.jetty.LEVEL=WARN # org.eclipse.jetty.io.LEVEL=DEBUG # org.eclipse.jetty.io.ManagedSelector.LEVEL=INFO # org.eclipse.jetty.websocket.LEVEL=DEBUG -# org.eclipse.jetty.websocket.core.internal.WebSocketCoreSessionsion.LEVEL=DEBUG -# org.eclipse.jetty.websocket.jsr356.tests.LEVEL=DEBUG +# org.eclipse.jetty.websocket.core.internal.WebSocketCoreSession.LEVEL=DEBUG # org.eclipse.jetty.websocket.LEVEL=INFO -# org.eclipse.jetty.websocket.jsr356.messages.LEVEL=DEBUG # org.eclipse.jetty.websocket.tests.LEVEL=DEBUG # org.eclipse.jetty.websocket.tests.client.LEVEL=DEBUG -# org.eclipse.jetty.websocket.tests.client.jsr356.LEVEL=DEBUG -# org.eclipse.jetty.websocket.tests.server.LEVEL=DEBUG -# org.eclipse.jetty.websocket.tests.server.jsr356.LEVEL=DEBUG -### Showing any unintended (ignored) errors from CompletionCallback -# org.eclipse.jetty.websocket.common.CompletionCallback.LEVEL=ALL -### Disabling intentional error out of RFCSocket -org.eclipse.jetty.websocket.tests.server.RFCSocket.LEVEL=OFF +# org.eclipse.jetty.websocket.tests.server.LEVEL=DEBUG \ No newline at end of file diff --git a/jetty-websocket/jetty-websocket-client/src/test/resources/jetty-logging.properties b/jetty-websocket/jetty-websocket-client/src/test/resources/jetty-logging.properties index b813365ce20..b88f6f45f02 100644 --- a/jetty-websocket/jetty-websocket-client/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/jetty-websocket-client/src/test/resources/jetty-logging.properties @@ -1,5 +1,4 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -org.eclipse.jetty.LEVEL=WARN # org.eclipse.jetty.LEVEL=DEBUG # org.eclipse.jetty.io.LEVEL=INFO # org.eclipse.jetty.client.LEVEL=DEBUG @@ -8,11 +7,9 @@ org.eclipse.jetty.LEVEL=WARN # org.eclipse.jetty.websocket.LEVEL=DEBUG # org.eclipse.jetty.websocket.client.LEVEL=DEBUG # org.eclipse.jetty.websocket.client.ClientCloseTest.LEVEL=DEBUG -org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.LEVEL=DEBUG +# org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.LEVEL=DEBUG # org.eclipse.jetty.websocket.common.io.IOState.LEVEL=DEBUG # org.eclipse.jetty.websocket.common.test.LEVEL=DEBUG # org.eclipse.jetty.websocket.common.Generator.LEVEL=DEBUG -org.eclipse.jetty.websocket.common.Parser.LEVEL=DEBUG -# org.eclipse.jetty.websocket.client.TrackingSocket.LEVEL=DEBUG -### Hide the stacktraces during testing -org.eclipse.jetty.websocket.client.internal.io.UpgradeConnection.STACKS=false +# org.eclipse.jetty.websocket.common.Parser.LEVEL=DEBUG +# org.eclipse.jetty.websocket.client.TrackingSocket.LEVEL=DEBUG \ No newline at end of file diff --git a/jetty-websocket/jetty-websocket-common/src/test/resources/jetty-logging.properties b/jetty-websocket/jetty-websocket-common/src/test/resources/jetty-logging.properties index af83047e755..07faa86dbce 100644 --- a/jetty-websocket/jetty-websocket-common/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/jetty-websocket-common/src/test/resources/jetty-logging.properties @@ -1,18 +1 @@ -# -# ======================================================================== -# Copyright (c) 1995-2017 Mort Bay Consulting 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. -# ======================================================================== -# org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog \ No newline at end of file diff --git a/jetty-websocket/jetty-websocket-tests/src/test/resources/jetty-logging.properties b/jetty-websocket/jetty-websocket-tests/src/test/resources/jetty-logging.properties index 8806e105177..c5aaeeb584b 100644 --- a/jetty-websocket/jetty-websocket-tests/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/jetty-websocket-tests/src/test/resources/jetty-logging.properties @@ -1,44 +1,10 @@ -# -# -# ======================================================================== -# Copyright (c) 1995-2017 Mort Bay Consulting 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. -# ======================================================================== -# -# -# org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -org.eclipse.jetty.LEVEL=WARN -# org.eclipse.jetty.websocket.tests.LEVEL=DEBUG -# org.eclipse.jetty.util.log.stderr.LONG=true +# org.eclipse.jetty.LEVEL=DEBUG +# org.eclipse.jetty.websocket.LEVEL=DEBUG +# org.eclipse.jetty.websocket.test.LEVEL=DEBUG # org.eclipse.jetty.server.AbstractConnector.LEVEL=DEBUG # org.eclipse.jetty.io.WriteFlusher.LEVEL=DEBUG # org.eclipse.jetty.io.FillInterest.LEVEL=DEBUG # org.eclipse.jetty.client.LEVEL=DEBUG # org.eclipse.jetty.io.LEVEL=DEBUG -# org.eclipse.jetty.io.ManagedSelector.LEVEL=INFO -# org.eclipse.jetty.websocket.LEVEL=DEBUG -# org.eclipse.jetty.websocket.core.internal.WebSocketCoreSessionsion.LEVEL=DEBUG -# org.eclipse.jetty.websocket.jsr356.tests.LEVEL=DEBUG -# org.eclipse.jetty.websocket.LEVEL=INFO -# org.eclipse.jetty.websocket.jsr356.messages.LEVEL=DEBUG -# org.eclipse.jetty.websocket.tests.LEVEL=DEBUG -# org.eclipse.jetty.websocket.tests.client.LEVEL=DEBUG -# org.eclipse.jetty.websocket.tests.client.jsr356.LEVEL=DEBUG -# org.eclipse.jetty.websocket.tests.server.LEVEL=DEBUG -# org.eclipse.jetty.websocket.tests.server.jsr356.LEVEL=DEBUG -### Showing any unintended (ignored) errors from CompletionCallback -# org.eclipse.jetty.websocket.common.CompletionCallback.LEVEL=ALL -### Disabling intentional error out of RFCSocket -org.eclipse.jetty.websocket.tests.server.RFCSocket.LEVEL=OFF +# org.eclipse.jetty.io.ManagedSelector.LEVEL=INFO \ No newline at end of file diff --git a/jetty-websocket/websocket-core/src/test/resources/jetty-logging.properties b/jetty-websocket/websocket-core/src/test/resources/jetty-logging.properties index 039b5a51133..03fa0413842 100644 --- a/jetty-websocket/websocket-core/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/websocket-core/src/test/resources/jetty-logging.properties @@ -1,22 +1,5 @@ -# -# ======================================================================== -# Copyright (c) 1995-2017 Mort Bay Consulting 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. -# ======================================================================== -# org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -org.eclipse.jetty.LEVEL=WARN +# org.eclipse.jetty.LEVEL=DEBUG # org.eclipse.jetty.io.LEVEL=DEBUG # org.eclipse.jetty.websocket.core.LEVEL=DEBUG # org.eclipse.jetty.util.log.stderr.LONG=true @@ -28,6 +11,4 @@ org.eclipse.jetty.LEVEL=WARN # org.eclipse.jetty.io.ManagedSelector.LEVEL=DEBUG # org.eclipse.jetty.websocket.LEVEL=DEBUG # org.eclipse.jetty.websocket.LEVEL=INFO -# org.eclipse.jetty.websocket.core.LEVEL=DEBUG -### Showing any unintended (ignored) errors from CompletionCallback -# org.eclipse.jetty.websocket.core.CompletionCallback.LEVEL=ALL +# org.eclipse.jetty.websocket.core.LEVEL=DEBUG \ No newline at end of file From 2a754edeb4db394ebfb99fa06e109136287804f3 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 11 Sep 2019 15:05:56 -0500 Subject: [PATCH 059/113] Removing bad reference to jetty-http:tests Signed-off-by: Joakim Erdfelt --- examples/embedded/pom.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/examples/embedded/pom.xml b/examples/embedded/pom.xml index 52d0095caa2..cd11566a15b 100644 --- a/examples/embedded/pom.xml +++ b/examples/embedded/pom.xml @@ -140,13 +140,6 @@ ${project.version} test
    - - org.eclipse.jetty - jetty-http - ${project.version} - tests - test - org.eclipse.jetty jetty-distribution From 63798f666403cdd0c93ccaeb2f091a82896e87ad Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Wed, 11 Sep 2019 23:55:25 +0200 Subject: [PATCH 060/113] Fixes #3956 - Remove and warn illegal HTTP/2 response headers. Updates after review. Signed-off-by: Simone Bordet --- .../jetty/http2/hpack/HpackEncoder.java | 48 +++++++++---------- .../http2/hpack/HpackFieldPreEncoder.java | 8 ++-- .../eclipse/jetty/http2/hpack/HpackTest.java | 16 ++++--- 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java index 1d9443e7aad..436cad4e9cf 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java @@ -21,7 +21,6 @@ package org.eclipse.jetty.http2.hpack; import java.nio.ByteBuffer; import java.util.EnumSet; import java.util.HashSet; -import java.util.List; import java.util.Set; import org.eclipse.jetty.http.HttpField; @@ -41,15 +40,15 @@ import org.eclipse.jetty.util.log.Logger; public class HpackEncoder { - public static final Logger LOG = Log.getLogger(HpackEncoder.class); - private static final HttpField[] __status = new HttpField[599]; - static final EnumSet __DO_NOT_HUFFMAN = + private static final Logger LOG = Log.getLogger(HpackEncoder.class); + private static final HttpField[] STATUSES = new HttpField[599]; + static final EnumSet DO_NOT_HUFFMAN = EnumSet.of( HttpHeader.AUTHORIZATION, HttpHeader.CONTENT_MD5, HttpHeader.PROXY_AUTHENTICATE, HttpHeader.PROXY_AUTHORIZATION); - static final EnumSet __DO_NOT_INDEX = + static final EnumSet DO_NOT_INDEX = EnumSet.of( // HttpHeader.C_PATH, // TODO more data needed // HttpHeader.DATE, // TODO more data needed @@ -69,19 +68,20 @@ public class HpackEncoder HttpHeader.LAST_MODIFIED, HttpHeader.SET_COOKIE, HttpHeader.SET_COOKIE2); - static final EnumSet __NEVER_INDEX = + static final EnumSet NEVER_INDEX = EnumSet.of( HttpHeader.AUTHORIZATION, HttpHeader.SET_COOKIE, HttpHeader.SET_COOKIE2); - private static final EnumSet HEADERS_TO_REMOVE = EnumSet.of(HttpHeader.CONNECTION, HttpHeader.KEEP_ALIVE, + private static final EnumSet IGNORED_HEADERS = EnumSet.of(HttpHeader.CONNECTION, HttpHeader.KEEP_ALIVE, HttpHeader.PROXY_CONNECTION, HttpHeader.TRANSFER_ENCODING, HttpHeader.UPGRADE); + private static final PreEncodedHttpField TE_TRAILERS = new PreEncodedHttpField(HttpHeader.TE, "trailers"); static { for (HttpStatus.Code code : HttpStatus.Code.values()) { - __status[code.getCode()] = new PreEncodedHttpField(HttpHeader.C_STATUS, Integer.toString(code.getCode())); + STATUSES[code.getCode()] = new PreEncodedHttpField(HttpHeader.C_STATUS, Integer.toString(code.getCode())); } } @@ -170,7 +170,7 @@ public class HpackEncoder { MetaData.Response response = (MetaData.Response)metadata; int code = response.getStatus(); - HttpField status = code < __status.length ? __status[code] : null; + HttpField status = code < STATUSES.length ? STATUSES[code] : null; if (status == null) status = new HttpField.IntValueHttpField(HttpHeader.C_STATUS, code); encode(buffer, status); @@ -181,26 +181,26 @@ public class HpackEncoder if (fields != null) { // For example: Connection: Close, TE, Upgrade, Custom. - List values = fields.getCSV(HttpHeader.CONNECTION, false); - Set hopHeaders = new HashSet<>(); - for (String value : values) + Set hopHeaders = null; + for (String value : fields.getCSV(HttpHeader.CONNECTION, false)) { - // Keep TE as a special case. - if (!"TE".equalsIgnoreCase(value)) - hopHeaders.add(StringUtil.asciiToLowerCase(value)); + if (hopHeaders == null) + hopHeaders = new HashSet<>(); + hopHeaders.add(StringUtil.asciiToLowerCase(value)); } for (HttpField field : fields) { HttpHeader header = field.getHeader(); - if (header != null && HEADERS_TO_REMOVE.contains(header)) - continue; - if (hopHeaders.contains(StringUtil.asciiToLowerCase(field.getName()))) + if (header != null && IGNORED_HEADERS.contains(header)) continue; if (header == HttpHeader.TE) { - if (!"trailers".equalsIgnoreCase(field.getValue())) - continue; + if (field.contains("trailers")) + encode(buffer, TE_TRAILERS); + continue; } + if (hopHeaders != null && hopHeaders.contains(StringUtil.asciiToLowerCase(field.getName()))) + continue; encode(buffer, field); } } @@ -319,12 +319,12 @@ public class HpackEncoder if (_debug) encoding = indexed ? "PreEncodedIdx" : "PreEncoded"; } - else if (__DO_NOT_INDEX.contains(header)) + else if (DO_NOT_INDEX.contains(header)) { // Non indexed field indexed = false; - boolean neverIndex = __NEVER_INDEX.contains(header); - boolean huffman = !__DO_NOT_HUFFMAN.contains(header); + boolean neverIndex = NEVER_INDEX.contains(header); + boolean huffman = !DO_NOT_HUFFMAN.contains(header); encodeName(buffer, neverIndex ? (byte)0x10 : (byte)0x00, 4, header.asString(), name); encodeValue(buffer, huffman, field.getValue()); @@ -347,7 +347,7 @@ public class HpackEncoder { // indexed indexed = true; - boolean huffman = !__DO_NOT_HUFFMAN.contains(header); + boolean huffman = !DO_NOT_HUFFMAN.contains(header); encodeName(buffer, (byte)0x40, 6, header.asString(), name); encodeValue(buffer, huffman, field.getValue()); if (_debug) diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackFieldPreEncoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackFieldPreEncoder.java index 2b3d30dc345..ba54ecb2dad 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackFieldPreEncoder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackFieldPreEncoder.java @@ -46,7 +46,7 @@ public class HpackFieldPreEncoder implements HttpFieldPreEncoder @Override public byte[] getEncodedField(HttpHeader header, String name, String value) { - boolean notIndexed = HpackEncoder.__DO_NOT_INDEX.contains(header); + boolean notIndexed = HpackEncoder.DO_NOT_INDEX.contains(header); ByteBuffer buffer = BufferUtil.allocate(name.length() + value.length() + 10); BufferUtil.clearToFill(buffer); @@ -56,8 +56,8 @@ public class HpackFieldPreEncoder implements HttpFieldPreEncoder if (notIndexed) { // Non indexed field - boolean neverIndex = HpackEncoder.__NEVER_INDEX.contains(header); - huffman = !HpackEncoder.__DO_NOT_HUFFMAN.contains(header); + boolean neverIndex = HpackEncoder.NEVER_INDEX.contains(header); + huffman = !HpackEncoder.DO_NOT_HUFFMAN.contains(header); buffer.put(neverIndex ? (byte)0x10 : (byte)0x00); bits = 4; } @@ -72,7 +72,7 @@ public class HpackFieldPreEncoder implements HttpFieldPreEncoder { // indexed buffer.put((byte)0x40); - huffman = !HpackEncoder.__DO_NOT_HUFFMAN.contains(header); + huffman = !HpackEncoder.DO_NOT_HUFFMAN.contains(header); bits = 6; } diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java index 565b4158bb9..b3bba049da8 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java @@ -106,7 +106,7 @@ public class HpackTest HttpFields fields0 = new HttpFields(); fields0.add("1234567890", "1234567890123456789012345678901234567890"); - fields0.add("Cookie", "abcdeffhijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR"); + fields0.add("Cookie", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR"); MetaData original0 = new MetaData(HttpVersion.HTTP_2, fields0); BufferUtil.clearToFill(buffer); @@ -118,7 +118,7 @@ public class HpackTest HttpFields fields1 = new HttpFields(); fields1.add("1234567890", "1234567890123456789012345678901234567890"); - fields1.add("Cookie", "abcdeffhijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR"); + fields1.add("Cookie", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR"); fields1.add("x", "y"); MetaData original1 = new MetaData(HttpVersion.HTTP_2, fields1); @@ -143,9 +143,11 @@ public class HpackTest HpackDecoder decoder = new HpackDecoder(200, 1024); ByteBuffer buffer = BufferUtil.allocateDirect(16 * 1024); + String longEnoughToBeEvicted = "012345678901234567890123456789012345678901234567890"; + HttpFields fields0 = new HttpFields(); - fields0.add("123456789012345678901234567890123456788901234567890", "value"); - fields0.add("foo", "abcdeffhijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR"); + fields0.add(longEnoughToBeEvicted, "value"); + fields0.add("foo", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); MetaData original0 = new MetaData(HttpVersion.HTTP_2, fields0); BufferUtil.clearToFill(buffer); @@ -155,13 +157,13 @@ public class HpackTest assertEquals(2, encoder.getHpackContext().size()); assertEquals(2, decoder.getHpackContext().size()); - assertEquals("123456789012345678901234567890123456788901234567890", encoder.getHpackContext().get(HpackContext.STATIC_TABLE.length + 1).getHttpField().getName()); + assertEquals(longEnoughToBeEvicted, encoder.getHpackContext().get(HpackContext.STATIC_TABLE.length + 1).getHttpField().getName()); assertEquals("foo", encoder.getHpackContext().get(HpackContext.STATIC_TABLE.length).getHttpField().getName()); assertMetaDataSame(original0, decoded0); HttpFields fields1 = new HttpFields(); - fields1.add("123456789012345678901234567890123456788901234567890", "other_value"); + fields1.add(longEnoughToBeEvicted, "other_value"); fields1.add("x", "y"); MetaData original1 = new MetaData(HttpVersion.HTTP_2, fields1); @@ -189,7 +191,7 @@ public class HpackTest input.put("Custom", "Pizza"); input.put(HttpHeader.KEEP_ALIVE, "true"); input.put(HttpHeader.PROXY_CONNECTION, "foo"); - input.put(HttpHeader.TE, "1234567890abcedf"); + input.put(HttpHeader.TE, "1234567890abcdef"); input.put(HttpHeader.TRANSFER_ENCODING, "chunked"); input.put(HttpHeader.UPGRADE, "gold"); From 905bf82f80a3152fc4c889f6586307066c6206f9 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 12 Sep 2019 10:14:58 +1000 Subject: [PATCH 061/113] reformatting Signed-off-by: Greg Wilkins --- .../jetty/http/pathmap/ServletPathSpec.java | 2 +- .../jetty/http2/hpack/HpackDecoder.java | 2 +- .../jetty/http2/hpack/HpackEncoder.java | 8 +++---- .../eclipse/jetty/http2/hpack/Huffman.java | 21 +++++++++--------- .../session/AbstractSessionDataStore.java | 22 ++++++++----------- 5 files changed, 25 insertions(+), 30 deletions(-) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java index d214323e6e7..e9d9de80ccc 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java @@ -89,7 +89,7 @@ public class ServletPathSpec extends PathSpec { this.group = PathSpecGroup.EXACT; this.prefix = servletPathSpec; - if (servletPathSpec.endsWith("*") ) + if (servletPathSpec.endsWith("*")) { LOG.warn("Suspicious URL pattern: '{}'; see sections 12.1 and 12.2 of the Servlet specification", servletPathSpec); diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java index bb1d3ede5fb..69f0cca8139 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java @@ -177,7 +177,7 @@ public class HpackDecoder else name = toASCIIString(buffer, length); check: - for (int i = name.length(); i-- > 0;) + for (int i = name.length(); i-- > 0; ) { char c = name.charAt(i); if (c > 0xff) diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java index a6c3bda169b..38f9c4f4cbe 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java @@ -404,8 +404,8 @@ public class HpackEncoder { // Not iso_8859_1 byte[] bytes = value.getBytes(StandardCharsets.UTF_8); - NBitInteger.encode(buffer,7,Huffman.octetsNeeded(bytes)); - Huffman.encode(buffer,bytes); + NBitInteger.encode(buffer, 7, Huffman.octetsNeeded(bytes)); + Huffman.encode(buffer, bytes); } } else @@ -421,8 +421,8 @@ public class HpackEncoder // Not iso_8859_1, so re-encode as UTF-8 buffer.reset(); byte[] bytes = value.getBytes(StandardCharsets.UTF_8); - NBitInteger.encode(buffer,7,bytes.length); - buffer.put(bytes,0,bytes.length); + NBitInteger.encode(buffer, 7, bytes.length); + buffer.put(bytes, 0, bytes.length); return; } buffer.put((byte)c); diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java index 4645a6cebfb..85cc3a9ebc6 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java @@ -358,8 +358,8 @@ public class Huffman return decode(buffer, buffer.remaining()); } - public static String decode(ByteBuffer buffer,int length) throws HpackException.CompressionException - { + public static String decode(ByteBuffer buffer, int length) throws HpackException.CompressionException + { Utf8StringBuilder utf8 = new Utf8StringBuilder(length * 2); int node = 0; int current = 0; @@ -430,20 +430,20 @@ public class Huffman } public static int octetsNeeded(byte[] b) - { - return octetsNeeded(CODES,b); + { + return octetsNeeded(CODES, b); } - + public static void encode(ByteBuffer buffer, String s) { encode(CODES, buffer, s); } - public static void encode(ByteBuffer buffer,byte[] b) + public static void encode(ByteBuffer buffer, byte[] b) { - encode(CODES,buffer,b); + encode(CODES, buffer, b); } - + public static int octetsNeededLC(String s) { return octetsNeeded(LCCODES, s); @@ -469,7 +469,7 @@ public class Huffman return (needed + 7) / 8; } - private static int octetsNeeded(final int[][] table,byte[] b) + private static int octetsNeeded(final int[][] table, byte[] b) { int needed = 0; int len = b.length; @@ -485,7 +485,6 @@ public class Huffman * @param table The table to encode by * @param buffer The buffer to encode to * @param s The string to encode - * @return True if the string could be encoded, false otherwise (and the buffer may have been modified). */ private static void encode(final int[][] table, ByteBuffer buffer, String s) { @@ -519,7 +518,7 @@ public class Huffman } } - private static void encode(final int[][] table,ByteBuffer buffer,byte[] b) + private static void encode(final int[][] table, ByteBuffer buffer, byte[] b) { long current = 0; int n = 0; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java index 01dc84e87d6..12bb46d86e9 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java @@ -81,24 +81,20 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem public SessionData load(String id) throws Exception { if (!isStarted()) - throw new IllegalStateException ("Not started"); + throw new IllegalStateException("Not started"); final AtomicReference reference = new AtomicReference(); final AtomicReference exception = new AtomicReference(); - Runnable r = new Runnable() + Runnable r = () -> { - @Override - public void run() + try { - try - { - reference.set(doLoad(id)); - } - catch (Exception e) - { - exception.set(e); - } + reference.set(doLoad(id)); + } + catch (Exception e) + { + exception.set(e); } }; @@ -165,7 +161,7 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem public Set getExpired(Set candidates) { if (!isStarted()) - throw new IllegalStateException ("Not started"); + throw new IllegalStateException("Not started"); try { From eb1c77daf42a6a49e90e13f6f0c3b27a3e0835d2 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Thu, 12 Sep 2019 10:22:15 +1000 Subject: [PATCH 062/113] remove empty lines Signed-off-by: Lachlan Roberts --- .../org/eclipse/jetty/security/openid/OpenIdConfiguration.java | 2 +- .../org/eclipse/jetty/security/openid/OpenIdCredentials.java | 2 +- .../org/eclipse/jetty/security/openid/OpenIdUserIdentity.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java index c237676b04b..26eb27e65e1 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java @@ -141,4 +141,4 @@ public class OpenIdConfiguration implements Serializable { return scopes; } -} +} \ No newline at end of file diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java index c32d236f732..70f00b80deb 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java @@ -207,4 +207,4 @@ public class OpenIdCredentials implements Serializable return result; } -} +} \ No newline at end of file diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserIdentity.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserIdentity.java index 3b943c7a6e6..658b63d3850 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserIdentity.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserIdentity.java @@ -53,4 +53,4 @@ public class OpenIdUserIdentity implements UserIdentity { return userIdentity == null ? false : userIdentity.isUserInRole(role, scope); } -} +} \ No newline at end of file From 82a00524d17b79a9b48dd9279395866d7ce0cbd4 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Thu, 12 Sep 2019 10:52:06 +1000 Subject: [PATCH 063/113] remove unnecessary connection upgrade check Signed-off-by: Lachlan Roberts --- .../main/java/org/eclipse/jetty/server/HttpConnection.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index 80c31516e00..722f368b618 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -257,11 +257,6 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http // Parse the request buffer. boolean handle = parseRequestBuffer(); - // If there was a connection upgrade, the other - // connection took over, nothing more to do here. - if (getEndPoint().getConnection() != this) - break; - // Handle channel event if (handle) { From d48c258deb54af200636311240a513cf1665354c Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 12 Sep 2019 17:56:09 +1000 Subject: [PATCH 064/113] Issue #97 ServletHolder unavailable handling Took the opportunity to do a major cleanup of the Holders. ServletHolder now uses wrapping to achieve optional behaviour rather than lots of if statements. Signed-off-by: Greg Wilkins --- .../org/eclipse/jetty/servlet/BaseHolder.java | 30 +- .../eclipse/jetty/servlet/FilterHolder.java | 57 +- .../org/eclipse/jetty/servlet/Holder.java | 23 +- .../eclipse/jetty/servlet/ListenerHolder.java | 64 ++- .../jetty/servlet/ServletContextHandler.java | 15 + .../eclipse/jetty/servlet/ServletHandler.java | 7 + .../eclipse/jetty/servlet/ServletHolder.java | 501 +++++++++++------- .../eclipse/jetty/servlet/ErrorPageTest.java | 111 +++- .../org/eclipse/jetty/servlet/HolderTest.java | 80 --- .../jetty/servlet/ServletHolderTest.java | 57 +- 10 files changed, 548 insertions(+), 397 deletions(-) delete mode 100644 jetty-servlet/src/test/java/org/eclipse/jetty/servlet/HolderTest.java diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/BaseHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/BaseHolder.java index ddcaa278423..5c688222192 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/BaseHolder.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/BaseHolder.java @@ -43,11 +43,11 @@ public abstract class BaseHolder extends AbstractLifeCycle implements Dumpabl { private static final Logger LOG = Log.getLogger(BaseHolder.class); - protected final Source _source; - protected transient Class _class; - protected String _className; - protected boolean _extInstance; - protected ServletHandler _servletHandler; + private final Source _source; + private Class _class; + private String _className; + private T _instance; + private ServletHandler _servletHandler; protected BaseHolder(Source source) { @@ -101,7 +101,7 @@ public abstract class BaseHolder extends AbstractLifeCycle implements Dumpabl public void doStop() throws Exception { - if (!_extInstance) + if (_instance == null) _class = null; } @@ -163,12 +163,26 @@ public abstract class BaseHolder extends AbstractLifeCycle implements Dumpabl } } + protected synchronized void setInstance(T instance) + { + _instance = instance; + if (instance == null) + setHeldClass(null); + else + setHeldClass((Class)instance.getClass()); + } + + protected synchronized T getInstance() + { + return _instance; + } + /** * @return True if this holder was created for a specific instance. */ - public boolean isInstance() + public synchronized boolean isInstance() { - return _extInstance; + return _instance != null; } @Override diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java index 3881bb05ff3..9b0f0b6261a 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/FilterHolder.java @@ -91,11 +91,10 @@ public class FilterHolder extends Holder { super.doStart(); - if (!javax.servlet.Filter.class - .isAssignableFrom(_class)) + if (!javax.servlet.Filter.class.isAssignableFrom(getHeldClass())) { - String msg = _class + " is not a javax.servlet.Filter"; - super.stop(); + String msg = getHeldClass() + " is not a javax.servlet.Filter"; + doStop(); throw new IllegalStateException(msg); } } @@ -103,15 +102,18 @@ public class FilterHolder extends Holder @Override public void initialize() throws Exception { - if (!_initialized) + synchronized (this) { - super.initialize(); + if (_filter != null) + return; + super.initialize(); + _filter = getInstance(); if (_filter == null) { try { - ServletContext context = _servletHandler.getServletContext(); + ServletContext context = getServletHandler().getServletContext(); _filter = (context instanceof ServletContextHandler.Context) ? context.createFilter(getHeldClass()) : getHeldClass().getDeclaredConstructor().newInstance(); @@ -126,37 +128,30 @@ public class FilterHolder extends Holder throw ex; } } - _config = new Config(); if (LOG.isDebugEnabled()) LOG.debug("Filter.init {}", _filter); _filter.init(_config); } - - _initialized = true; } @Override public void doStop() throws Exception { + super.doStop(); + _config = null; if (_filter != null) { try { destroyInstance(_filter); } - catch (Exception e) + finally { - LOG.warn(e); + _filter = null; } } - if (!_extInstance) - _filter = null; - - _config = null; - _initialized = false; - super.doStop(); } @Override @@ -172,11 +167,7 @@ public class FilterHolder extends Holder public synchronized void setFilter(Filter filter) { - _filter = filter; - _extInstance = true; - setHeldClass(filter.getClass()); - if (getName() == null) - setName(filter.getClass().getName()); + setInstance(filter); } public Filter getFilter() @@ -187,19 +178,19 @@ public class FilterHolder extends Holder @Override public void dump(Appendable out, String indent) throws IOException { - if (_initParams.isEmpty()) + if (getInitParameters().isEmpty()) Dumpable.dumpObjects(out, indent, this, _filter == null ? getHeldClass() : _filter); else Dumpable.dumpObjects(out, indent, this, _filter == null ? getHeldClass() : _filter, - new DumpableCollection("initParams", _initParams.entrySet())); + new DumpableCollection("initParams", getInitParameters().entrySet())); } @Override public String toString() { - return String.format("%s@%x==%s,inst=%b,async=%b", _name, hashCode(), _className, _filter != null, isAsyncSupported()); + return String.format("%s@%x==%s,inst=%b,async=%b", getName(), hashCode(), getClassName(), _filter != null, isAsyncSupported()); } public FilterRegistration.Dynamic getRegistration() @@ -220,9 +211,9 @@ public class FilterHolder extends Holder mapping.setServletNames(servletNames); mapping.setDispatcherTypes(dispatcherTypes); if (isMatchAfter) - _servletHandler.addFilterMapping(mapping); + getServletHandler().addFilterMapping(mapping); else - _servletHandler.prependFilterMapping(mapping); + getServletHandler().prependFilterMapping(mapping); } @Override @@ -234,15 +225,15 @@ public class FilterHolder extends Holder mapping.setPathSpecs(urlPatterns); mapping.setDispatcherTypes(dispatcherTypes); if (isMatchAfter) - _servletHandler.addFilterMapping(mapping); + getServletHandler().addFilterMapping(mapping); else - _servletHandler.prependFilterMapping(mapping); + getServletHandler().prependFilterMapping(mapping); } @Override public Collection getServletNameMappings() { - FilterMapping[] mappings = _servletHandler.getFilterMappings(); + FilterMapping[] mappings = getServletHandler().getFilterMappings(); List names = new ArrayList(); for (FilterMapping mapping : mappings) { @@ -258,7 +249,7 @@ public class FilterHolder extends Holder @Override public Collection getUrlPatternMappings() { - FilterMapping[] mappings = _servletHandler.getFilterMappings(); + FilterMapping[] mappings = getServletHandler().getFilterMappings(); List patterns = new ArrayList(); for (FilterMapping mapping : mappings) { @@ -277,7 +268,7 @@ public class FilterHolder extends Holder @Override public String getFilterName() { - return _name; + return getName(); } } } diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java index 07010fd9192..7ee8392b5c1 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Holder.java @@ -45,16 +45,15 @@ public abstract class Holder extends BaseHolder { private static final Logger LOG = Log.getLogger(Holder.class); - protected final Map _initParams = new HashMap(3); - protected String _displayName; - protected boolean _asyncSupported; - protected String _name; - protected boolean _initialized = false; + private final Map _initParams = new HashMap(3); + private String _displayName; + private boolean _asyncSupported; + private String _name; protected Holder(Source source) { super(source); - switch (_source.getOrigin()) + switch (getSource().getOrigin()) { case JAVAX_API: case DESCRIPTOR: @@ -98,6 +97,14 @@ public abstract class Holder extends BaseHolder return _name; } + @Override + protected synchronized void setInstance(T instance) + { + super.setInstance(instance); + if (getName() == null) + setName(String.format("%s@%x", instance.getClass().getName(), instance.hashCode())); + } + public void destroyInstance(Object instance) throws Exception { @@ -175,7 +182,7 @@ public abstract class Holder extends BaseHolder @Override public String toString() { - return String.format("%s@%x==%s", _name, hashCode(), _className); + return String.format("%s@%x==%s", _name, hashCode(), getClassName()); } protected class HolderConfig @@ -183,7 +190,7 @@ public abstract class Holder extends BaseHolder public ServletContext getServletContext() { - return _servletHandler.getServletContext(); + return getServletHandler().getServletContext(); } public String getInitParameter(String param) diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ListenerHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ListenerHolder.java index 245101dd63b..9313237ea4e 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ListenerHolder.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ListenerHolder.java @@ -64,52 +64,66 @@ public class ListenerHolder extends BaseHolder */ public void setListener(EventListener listener) { - _listener = listener; - _extInstance = true; - setHeldClass(_listener.getClass()); + setInstance(listener); } @Override public void doStart() throws Exception { super.doStart(); - if (!java.util.EventListener.class.isAssignableFrom(_class)) + if (!java.util.EventListener.class.isAssignableFrom(getHeldClass())) { - String msg = _class + " is not a java.util.EventListener"; + String msg = getHeldClass() + " is not a java.util.EventListener"; super.stop(); throw new IllegalStateException(msg); } - + ContextHandler contextHandler = ContextHandler.getCurrentContext().getContextHandler(); - if (_listener == null) + if (contextHandler != null) { - //create an instance of the listener and decorate it - try + _listener = getInstance(); + if (_listener == null) { - ServletContext scontext = contextHandler.getServletContext(); - _listener = (scontext instanceof ServletContextHandler.Context) - ? scontext.createListener(getHeldClass()) - : getHeldClass().getDeclaredConstructor().newInstance(); - } - catch (ServletException ex) - { - Throwable cause = ex.getRootCause(); - if (cause instanceof InstantiationException) - throw (InstantiationException)cause; - if (cause instanceof IllegalAccessException) - throw (IllegalAccessException)cause; - throw ex; + //create an instance of the listener and decorate it + try + { + ServletContext scontext = contextHandler.getServletContext(); + _listener = (scontext instanceof ServletContextHandler.Context) + ? scontext.createListener(getHeldClass()) + : getHeldClass().getDeclaredConstructor().newInstance(); + } + catch (ServletException ex) + { + Throwable cause = ex.getRootCause(); + if (cause instanceof InstantiationException) + throw (InstantiationException)cause; + if (cause instanceof IllegalAccessException) + throw (IllegalAccessException)cause; + throw ex; + } } + contextHandler.addEventListener(_listener); } - contextHandler.addEventListener(_listener); } @Override public void doStop() throws Exception { super.doStop(); - if (!_extInstance) - _listener = null; + if (_listener != null) + { + try + { + ContextHandler contextHandler = ContextHandler.getCurrentContext().getContextHandler(); + if (contextHandler != null) + contextHandler.removeEventListener(_listener); + getServletHandler().destroyListener(_listener); + } + finally + { + _listener = null; + } + } } @Override diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java index db28cfdbdb7..a2b790839d0 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java @@ -733,6 +733,11 @@ public class ServletContextHandler extends ContextHandler _objFactory.destroy(filter); } + void destroyListener(EventListener listener) + { + _objFactory.destroy(listener); + } + public static ServletContextHandler getServletContextHandler(ServletContext context) { ContextHandler handler = getContextHandler(context); @@ -1286,6 +1291,11 @@ public class ServletContextHandler extends ContextHandler } } + public void destroyFilter(T f) + { + _objFactory.destroy(f); + } + @Override public T createServlet(Class c) throws ServletException { @@ -1301,6 +1311,11 @@ public class ServletContextHandler extends ContextHandler } } + public void destroyServlet(T s) + { + _objFactory.destroy(s); + } + @Override public Set getDefaultSessionTrackingModes() { diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java index 897a9ccba98..df9f9249b1c 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; +import java.util.EventListener; import java.util.HashMap; import java.util.List; import java.util.ListIterator; @@ -1736,6 +1737,12 @@ public class ServletHandler extends ScopedHandler _contextHandler.destroyFilter(filter); } + void destroyListener(EventListener listener) + { + if (_contextHandler != null) + _contextHandler.destroyListener(listener); + } + @SuppressWarnings("serial") public static class Default404Servlet extends HttpServlet { diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java index 9fb2b7e4ff6..6ba6598ffca 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java @@ -32,6 +32,8 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; +import java.util.concurrent.TimeUnit; +import javax.servlet.GenericServlet; import javax.servlet.MultipartConfigElement; import javax.servlet.Servlet; import javax.servlet.ServletConfig; @@ -43,6 +45,7 @@ import javax.servlet.ServletResponse; import javax.servlet.ServletSecurityElement; import javax.servlet.SingleThreadModel; import javax.servlet.UnavailableException; +import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.security.IdentityService; import org.eclipse.jetty.security.RunAsToken; @@ -70,7 +73,6 @@ import org.eclipse.jetty.util.log.Logger; @ManagedObject("Servlet Holder") public class ServletHolder extends Holder implements UserIdentity.Scope, Comparable { - private static final Logger LOG = Log.getLogger(ServletHolder.class); private int _initOrder = -1; private boolean _initOnStartup = false; @@ -82,11 +84,9 @@ public class ServletHolder extends Holder implements UserIdentity.Scope private ServletRegistration.Dynamic _registration; private JspContainer _jspContainer; - private Servlet _servlet; - private long _unavailable; + private volatile Servlet _servlet; private Config _config; private boolean _enabled = true; - private UnavailableException _unavailableEx; public static final String APACHE_SENTINEL_CLASS = "org.apache.tomcat.InstanceManager"; public static final String JSP_GENERATED_PACKAGE_NAME = "org.eclipse.jetty.servlet.jspPackagePrefix"; @@ -167,7 +167,10 @@ public class ServletHolder extends Holder implements UserIdentity.Scope */ public UnavailableException getUnavailableException() { - return _unavailableEx; + Servlet servlet = _servlet; + if (servlet instanceof UnavailableServlet) + return ((UnavailableServlet)servlet).getUnavailableException(); + return null; } public synchronized void setServlet(Servlet servlet) @@ -175,11 +178,7 @@ public class ServletHolder extends Holder implements UserIdentity.Scope if (servlet == null || servlet instanceof SingleThreadModel) throw new IllegalArgumentException(); - _extInstance = true; - _servlet = servlet; - setHeldClass(servlet.getClass()); - if (getName() == null) - setName(servlet.getClass().getName() + "-" + super.hashCode()); + setInstance(servlet); } @ManagedAttribute(value = "initialization order", readonly = true) @@ -218,20 +217,20 @@ public class ServletHolder extends Holder implements UserIdentity.Scope if (sh._initOrder > _initOrder) return -1; - // consider _className, need to position properly when one is configured but not the other + // consider getClassName(), need to position properly when one is configured but not the other int c; - if (_className == null && sh._className == null) + if (getClassName() == null && sh.getClassName() == null) c = 0; - else if (_className == null) + else if (getClassName() == null) c = -1; - else if (sh._className == null) + else if (sh.getClassName() == null) c = 1; else - c = _className.compareTo(sh._className); + c = getClassName().compareTo(sh.getClassName()); - // if _initOrder and _className are the same, consider the _name + // if _initOrder and getClassName() are the same, consider the getName() if (c == 0) - c = _name.compareTo(sh._name); + c = getName().compareTo(sh.getName()); return c; } @@ -245,7 +244,7 @@ public class ServletHolder extends Holder implements UserIdentity.Scope @Override public int hashCode() { - return _name == null ? System.identityHashCode(this) : _name.hashCode(); + return getName() == null ? System.identityHashCode(this) : getName().hashCode(); } /** @@ -309,7 +308,6 @@ public class ServletHolder extends Holder implements UserIdentity.Scope public void doStart() throws Exception { - _unavailable = 0; if (!_enabled) return; @@ -342,7 +340,7 @@ public class ServletHolder extends Holder implements UserIdentity.Scope //copy jsp init params that don't exist for this servlet for (Map.Entry entry : jsp.getInitParameters().entrySet()) { - if (!_initParams.containsKey(entry.getKey())) + if (!getInitParameters().containsKey(entry.getKey())) setInitParameter(entry.getKey(), entry.getValue()); } //jsp specific: set up the jsp-file on the JspServlet. If load-on-startup is >=0 and the jsp container supports @@ -365,7 +363,7 @@ public class ServletHolder extends Holder implements UserIdentity.Scope catch (UnavailableException ex) { makeUnavailable(ex); - if (_servletHandler.isStartWithUnavailable()) + if (getServletHandler().isStartWithUnavailable()) { LOG.ignore(ex); return; @@ -382,7 +380,7 @@ public class ServletHolder extends Holder implements UserIdentity.Scope catch (UnavailableException ex) { makeUnavailable(ex); - if (_servletHandler.isStartWithUnavailable()) + if (getServletHandler().isStartWithUnavailable()) { LOG.ignore(ex); return; @@ -394,15 +392,23 @@ public class ServletHolder extends Holder implements UserIdentity.Scope //check if we need to forcibly set load-on-startup checkInitOnStartup(); - _identityService = _servletHandler.getIdentityService(); - if (_identityService != null && _runAsRole != null) - _runAsToken = _identityService.newRunAsToken(_runAsRole); + if (_runAsRole == null) + { + _identityService = null; + _runAsToken = null; + } + else + { + _identityService = getServletHandler().getIdentityService(); + if (_identityService != null) + _runAsToken = _identityService.newRunAsToken(_runAsRole); + } _config = new Config(); synchronized (this) { - if (_class != null && javax.servlet.SingleThreadModel.class.isAssignableFrom(_class)) + if (getHeldClass() != null && javax.servlet.SingleThreadModel.class.isAssignableFrom(getHeldClass())) _servlet = new SingleThreadedWrapper(); } } @@ -411,57 +417,37 @@ public class ServletHolder extends Holder implements UserIdentity.Scope public void initialize() throws Exception { - if (!_initialized) + synchronized (this) { - super.initialize(); - if (_extInstance || _initOnStartup) + if (_servlet == null && (_initOnStartup || isInstance())) { - try - { - initServlet(); - } - catch (Exception e) - { - if (_servletHandler.isStartWithUnavailable()) - LOG.ignore(e); - else - throw e; - } + super.initialize(); + initServlet(); } } - _initialized = true; } @Override public void doStop() throws Exception { - Object oldRunAs = null; - if (_servlet != null) + synchronized (this) { - try + Servlet servlet = _servlet; + if (servlet != null) { - if (_identityService != null) - oldRunAs = _identityService.setRunAs(_identityService.getSystemUserIdentity(), _runAsToken); - - destroyInstance(_servlet); - } - catch (Exception e) - { - LOG.warn(e); - } - finally - { - if (_identityService != null) - _identityService.unsetRunAs(oldRunAs); + _servlet = null; + try + { + destroyInstance(servlet); + } + catch (Exception e) + { + LOG.warn(e); + } } + _config = null; } - - if (!_extInstance) - _servlet = null; - - _config = null; - _initialized = false; } @Override @@ -481,41 +467,24 @@ public class ServletHolder extends Holder implements UserIdentity.Scope * @return The servlet * @throws ServletException if unable to init the servlet on first use */ - public synchronized Servlet getServlet() + public Servlet getServlet() throws ServletException { Servlet servlet = _servlet; - if (servlet != null && _unavailable == 0) - return servlet; - - synchronized (this) + if (servlet == null) { - // Handle previous unavailability - if (_unavailable != 0) + synchronized (this) { - if (_unavailable < 0 || _unavailable > 0 && System.currentTimeMillis() < _unavailable) - throw _unavailableEx; - _unavailable = 0; - _unavailableEx = null; - } - - servlet = _servlet; - if (servlet != null) - return servlet; - - if (isRunning()) - { - if (_class == null) - throw new UnavailableException("Servlet Not Initialized"); - if (_unavailable != 0 || !_initOnStartup) - initServlet(); servlet = _servlet; - if (servlet == null) - throw new UnavailableException("Could not instantiate " + _class); + if (servlet == null && isRunning()) + { + if (getHeldClass() != null) + initServlet(); + servlet = _servlet; + } } - - return servlet; } + return servlet; } /** @@ -525,13 +494,7 @@ public class ServletHolder extends Holder implements UserIdentity.Scope */ public Servlet getServletInstance() { - Servlet servlet = _servlet; - if (servlet != null) - return servlet; - synchronized (this) - { - return _servlet; - } + return _servlet; } /** @@ -542,9 +505,9 @@ public class ServletHolder extends Holder implements UserIdentity.Scope public void checkServletType() throws UnavailableException { - if (_class == null || !javax.servlet.Servlet.class.isAssignableFrom(_class)) + if (getHeldClass() == null || !javax.servlet.Servlet.class.isAssignableFrom(getHeldClass())) { - throw new UnavailableException("Servlet " + _class + " is not a javax.servlet.Servlet"); + throw new UnavailableException("Servlet " + getHeldClass() + " is not a javax.servlet.Servlet"); } } @@ -553,18 +516,7 @@ public class ServletHolder extends Holder implements UserIdentity.Scope */ public boolean isAvailable() { - if (isStarted() && _unavailable == 0) - return true; - try - { - getServlet(); - } - catch (Exception e) - { - LOG.ignore(e); - } - - return isStarted() && _unavailable == 0; + return (isStarted() && !(_servlet instanceof UnavailableServlet)); } /** @@ -575,30 +527,19 @@ public class ServletHolder extends Holder implements UserIdentity.Scope */ private void checkInitOnStartup() { - if (_class == null) + if (getHeldClass() == null) return; - if ((_class.getAnnotation(javax.servlet.annotation.ServletSecurity.class) != null) && !_initOnStartup) + if ((getHeldClass().getAnnotation(javax.servlet.annotation.ServletSecurity.class) != null) && !_initOnStartup) setInitOrder(Integer.MAX_VALUE); } - private void makeUnavailable(UnavailableException e) + private Servlet makeUnavailable(UnavailableException e) { - if (_unavailableEx == e && _unavailable != 0) - return; - - _servletHandler.getServletContext().log("unavailable", e); - - _unavailableEx = e; - _unavailable = -1; - if (e.isPermanent()) - _unavailable = -1; - else + synchronized (this) { - if (_unavailableEx.getUnavailableSeconds() > 0) - _unavailable = System.currentTimeMillis() + 1000 * _unavailableEx.getUnavailableSeconds(); - else - _unavailable = System.currentTimeMillis() + 5000; // TODO configure + _servlet = new UnavailableServlet(e, _servlet); + return _servlet; } } @@ -608,37 +549,39 @@ public class ServletHolder extends Holder implements UserIdentity.Scope makeUnavailable((UnavailableException)e); else { - ServletContext ctx = _servletHandler.getServletContext(); + ServletContext ctx = getServletHandler().getServletContext(); if (ctx == null) LOG.info("unavailable", e); else ctx.log("unavailable", e); - _unavailableEx = new UnavailableException(String.valueOf(e), -1) + UnavailableException unavailable = new UnavailableException(String.valueOf(e), -1) { { initCause(e); } }; - _unavailable = -1; + makeUnavailable(unavailable); } } private synchronized void initServlet() throws ServletException { - Object oldRunAs = null; try { + if (_servlet == null) + _servlet = getInstance(); if (_servlet == null) _servlet = newInstance(); if (_config == null) _config = new Config(); // Handle run as - if (_identityService != null) - { - oldRunAs = _identityService.setRunAs(_identityService.getSystemUserIdentity(), _runAsToken); - } + if (_identityService != null && _runAsToken != null) + _servlet = new RunAsServlet(_servlet, _identityService, _runAsToken); + + if (!isAsyncSupported()) + _servlet = new NotAsyncServlet(_servlet); // Handle configuring servlets that implement org.apache.jasper.servlet.JspServlet if (isJspServlet()) @@ -658,30 +601,21 @@ public class ServletHolder extends Holder implements UserIdentity.Scope catch (UnavailableException e) { makeUnavailable(e); - _servlet = null; - _config = null; - throw e; + if (getServletHandler().isStartWithUnavailable()) + LOG.warn(e); + else + throw e; } catch (ServletException e) { makeUnavailable(e.getCause() == null ? e : e.getCause()); - _servlet = null; - _config = null; throw e; } catch (Exception e) { makeUnavailable(e); - _servlet = null; - _config = null; throw new ServletException(this.toString(), e); } - finally - { - // pop run-as role - if (_identityService != null) - _identityService.unsetRunAs(oldRunAs); - } } /** @@ -692,7 +626,7 @@ public class ServletHolder extends Holder implements UserIdentity.Scope ContextHandler ch = ContextHandler.getContextHandler(getServletHandler().getServletContext()); /* Set the webapp's classpath for Jasper */ - ch.setAttribute("org.apache.catalina.jsp_classpath", ch.getClassPath()); + ch.setAttribute("org.apache.catalina.jspgetHeldClass()path", ch.getClassPath()); /* Set up other classpath attribute */ if ("?".equals(getInitParameter("classpath"))) @@ -818,56 +752,23 @@ public class ServletHolder extends Holder implements UserIdentity.Scope UnavailableException, IOException { - if (_class == null) - throw new UnavailableException("Servlet Not Initialized"); - - Servlet servlet = getServlet(); - - // Service the request - Object oldRunAs = null; - boolean suspendable = baseRequest.isAsyncSupported(); try { - // Handle aliased path - if (_forcedPath != null) - adaptForcedPathToJspContainer(request); - - // Handle run as - if (_identityService != null) - oldRunAs = _identityService.setRunAs(baseRequest.getResolvedUserIdentity(), _runAsToken); - - if (baseRequest.isAsyncSupported() && !isAsyncSupported()) - { - try - { - baseRequest.setAsyncSupported(false, this.toString()); - servlet.service(request, response); - } - finally - { - baseRequest.setAsyncSupported(true, null); - } - } - else - servlet.service(request, response); + Servlet servlet = getServlet(); + if (servlet == null) + throw new UnavailableException("Servlet Not Initialized"); + servlet.service(request, response); } catch (UnavailableException e) { - makeUnavailable(e); - throw _unavailableEx; - } - finally - { - // Pop run-as role. - if (_identityService != null) - _identityService.unsetRunAs(oldRunAs); + makeUnavailable(e).service(request, response); } } protected boolean isJspServlet() { Servlet servlet = getServletInstance(); - Class c = servlet == null ? _class : servlet.getClass(); + Class c = servlet == null ? getHeldClass() : servlet.getClass(); while (c != null) { @@ -885,11 +786,6 @@ public class ServletHolder extends Holder implements UserIdentity.Scope return ("org.apache.jasper.servlet.JspServlet".equals(classname)); } - private void adaptForcedPathToJspContainer(ServletRequest request) - { - //no-op for apache jsp - } - private void detectJspContainer() { if (_jspContainer == null) @@ -1057,7 +953,7 @@ public class ServletHolder extends Holder implements UserIdentity.Scope Set clash = null; for (String pattern : urlPatterns) { - ServletMapping mapping = _servletHandler.getServletMapping(pattern); + ServletMapping mapping = getServletHandler().getServletMapping(pattern); if (mapping != null) { //if the servlet mapping was from a default descriptor, then allow it to be overridden @@ -1078,7 +974,7 @@ public class ServletHolder extends Holder implements UserIdentity.Scope ServletMapping mapping = new ServletMapping(Source.JAVAX_API); mapping.setServletName(ServletHolder.this.getName()); mapping.setPathSpecs(urlPatterns); - _servletHandler.addServletMapping(mapping); + getServletHandler().addServletMapping(mapping); return Collections.emptySet(); } @@ -1086,7 +982,7 @@ public class ServletHolder extends Holder implements UserIdentity.Scope @Override public Collection getMappings() { - ServletMapping[] mappings = _servletHandler.getServletMappings(); + ServletMapping[] mappings = getServletHandler().getServletMappings(); List patterns = new ArrayList(); if (mappings != null) { @@ -1140,7 +1036,7 @@ public class ServletHolder extends Holder implements UserIdentity.Scope @Override public Set setServletSecurity(ServletSecurityElement securityElement) { - return _servletHandler.setServletSecurity(this, securityElement); + return getServletHandler().setServletSecurity(this, securityElement); } } @@ -1287,18 +1183,221 @@ public class ServletHolder extends Holder implements UserIdentity.Scope @Override public void dump(Appendable out, String indent) throws IOException { - if (_initParams.isEmpty()) + if (getInitParameters().isEmpty()) Dumpable.dumpObjects(out, indent, this, _servlet == null ? getHeldClass() : _servlet); else Dumpable.dumpObjects(out, indent, this, _servlet == null ? getHeldClass() : _servlet, - new DumpableCollection("initParams", _initParams.entrySet())); + new DumpableCollection("initParams", getInitParameters().entrySet())); } @Override public String toString() { - return String.format("%s@%x==%s,jsp=%s,order=%d,inst=%b,async=%b", _name, hashCode(), _className, _forcedPath, _initOrder, _servlet != null, isAsyncSupported()); + return String.format("%s@%x==%s,jsp=%s,order=%d,inst=%b,async=%b", getName(), hashCode(), getClassName(), _forcedPath, _initOrder, _servlet != null, isAsyncSupported()); + } + + private class UnavailableServlet extends GenericServlet + { + final UnavailableException _unavailableException; + final Servlet _servlet; + final long _available; + + public UnavailableServlet(UnavailableException unavailableException, Servlet servlet) + { + _unavailableException = unavailableException; + + if (unavailableException.isPermanent()) + { + _servlet = null; + _available = -1; + if (servlet != null) + { + try + { + destroyInstance(servlet); + } + catch (Throwable th) + { + if (th != unavailableException) + unavailableException.addSuppressed(th); + } + } + } + else + { + _servlet = servlet; + _available = System.nanoTime() + TimeUnit.SECONDS.toNanos(unavailableException.getUnavailableSeconds()); + } + } + + @Override + public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException + { + if (_available == -1) + ((HttpServletResponse)res).sendError(HttpServletResponse.SC_NOT_FOUND); + else if (System.nanoTime() < _available) + ((HttpServletResponse)res).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); + else + { + synchronized (ServletHolder.this) + { + ServletHolder.this._servlet = this._servlet; + _servlet.service(req,res); + } + } + } + + @Override + public void destroy() + { + if (_servlet != null) + { + try + { + destroyInstance(_servlet); + } + catch (Throwable th) + { + LOG.warn(th); + } + } + } + + public UnavailableException getUnavailableException() + { + return _unavailableException; + } + } + + private static class WrapperServlet implements Servlet + { + final Servlet _servlet; + + public WrapperServlet(Servlet servlet) + { + _servlet = servlet; + } + + @Override + public void init(ServletConfig config) throws ServletException + { + _servlet.init(config); + } + + @Override + public ServletConfig getServletConfig() + { + return _servlet.getServletConfig(); + } + + @Override + public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException + { + _servlet.service(req, res); + } + + @Override + public String getServletInfo() + { + return _servlet.getServletInfo(); + } + + @Override + public void destroy() + { + _servlet.destroy(); + } + + @Override + public String toString() + { + return String.format("%s:%s", this.getClass().getSimpleName(), _servlet.toString()); + } + } + + private static class RunAsServlet extends WrapperServlet + { + final IdentityService _identityService; + final RunAsToken _runAsToken; + + public RunAsServlet(Servlet servlet, IdentityService identityService, RunAsToken runAsToken) + { + super(servlet); + _identityService = identityService; + _runAsToken = runAsToken; + } + + @Override + public void init(ServletConfig config) throws ServletException + { + Object oldRunAs = _identityService.setRunAs(_identityService.getSystemUserIdentity(), _runAsToken); + try + { + _servlet.init(config); + } + finally + { + _identityService.unsetRunAs(oldRunAs); + } + } + + @Override + public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException + { + Object oldRunAs = _identityService.setRunAs(_identityService.getSystemUserIdentity(), _runAsToken); + try + { + _servlet.service(req, res); + } + finally + { + _identityService.unsetRunAs(oldRunAs); + } + } + + @Override + public void destroy() + { + Object oldRunAs = _identityService.setRunAs(_identityService.getSystemUserIdentity(), _runAsToken); + try + { + _servlet.destroy(); + } + finally + { + _identityService.unsetRunAs(oldRunAs); + } + } + } + + private static class NotAsyncServlet extends WrapperServlet + { + public NotAsyncServlet(Servlet servlet) + { + super(servlet); + } + + @Override + public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException + { + if (req.isAsyncSupported()) + { + try + { + ((Request)req).setAsyncSupported(false, this.toString()); + _servlet.service(req, res); + } + finally + { + ((Request)req).setAsyncSupported(true, null); + } + } + else + { + _servlet.service(req, res); + } + } } } diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ErrorPageTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ErrorPageTest.java index a82e7a63452..5e589464a05 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ErrorPageTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ErrorPageTest.java @@ -26,6 +26,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import javax.servlet.AsyncContext; import javax.servlet.DispatcherType; @@ -36,6 +37,7 @@ import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; +import javax.servlet.UnavailableException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -58,6 +60,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; public class ErrorPageTest @@ -67,6 +70,8 @@ public class ErrorPageTest private StacklessLogging _stackless; private static CountDownLatch __asyncSendErrorCompleted; private ErrorPageErrorHandler _errorPageErrorHandler; + private static AtomicBoolean __destroyed; + private ServletContextHandler _context; @BeforeEach public void init() throws Exception @@ -75,25 +80,24 @@ public class ErrorPageTest _connector = new LocalConnector(_server); _server.addConnector(_connector); - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SECURITY | ServletContextHandler.NO_SESSIONS); + _context = new ServletContextHandler(ServletContextHandler.NO_SECURITY | ServletContextHandler.NO_SESSIONS); - _server.setHandler(context); + _server.setHandler(_context); - context.setContextPath("/"); - - context.addFilter(SingleDispatchFilter.class, "/*", EnumSet.allOf(DispatcherType.class)); - - context.addServlet(DefaultServlet.class, "/"); - context.addServlet(FailServlet.class, "/fail/*"); - context.addServlet(FailClosedServlet.class, "/fail-closed/*"); - context.addServlet(ErrorServlet.class, "/error/*"); - context.addServlet(AppServlet.class, "/app/*"); - context.addServlet(LongerAppServlet.class, "/longer.app/*"); - context.addServlet(SyncSendErrorServlet.class, "/sync/*"); - context.addServlet(AsyncSendErrorServlet.class, "/async/*"); - context.addServlet(NotEnoughServlet.class, "/notenough/*"); - context.addServlet(DeleteServlet.class, "/delete/*"); - context.addServlet(ErrorAndStatusServlet.class, "/error-and-status/*"); + _context.setContextPath("/"); + _context.addFilter(SingleDispatchFilter.class, "/*", EnumSet.allOf(DispatcherType.class)); + _context.addServlet(DefaultServlet.class, "/"); + _context.addServlet(FailServlet.class, "/fail/*"); + _context.addServlet(FailClosedServlet.class, "/fail-closed/*"); + _context.addServlet(ErrorServlet.class, "/error/*"); + _context.addServlet(AppServlet.class, "/app/*"); + _context.addServlet(LongerAppServlet.class, "/longer.app/*"); + _context.addServlet(SyncSendErrorServlet.class, "/sync/*"); + _context.addServlet(AsyncSendErrorServlet.class, "/async/*"); + _context.addServlet(NotEnoughServlet.class, "/notenough/*"); + _context.addServlet(UnavailableServlet.class, "/unavailable/*"); + _context.addServlet(DeleteServlet.class, "/delete/*"); + _context.addServlet(ErrorAndStatusServlet.class, "/error-and-status/*"); HandlerWrapper noopHandler = new HandlerWrapper() { @@ -106,10 +110,10 @@ public class ErrorPageTest super.handle(target, baseRequest, request, response); } }; - context.insertHandler(noopHandler); + _context.insertHandler(noopHandler); _errorPageErrorHandler = new ErrorPageErrorHandler(); - context.setErrorHandler(_errorPageErrorHandler); + _context.setErrorHandler(_errorPageErrorHandler); _errorPageErrorHandler.addErrorPage(595, "/error/595"); _errorPageErrorHandler.addErrorPage(597, "/sync"); _errorPageErrorHandler.addErrorPage(599, "/error/599"); @@ -408,6 +412,45 @@ public class ErrorPageTest assertThat(response, Matchers.endsWith("SomeBytes")); } + @Test + public void testPermanentlyUnavailable() throws Exception + { + try (StacklessLogging ignore =new StacklessLogging(_context.getLogger())) + { + try (StacklessLogging ignore2 = new StacklessLogging(HttpChannel.class)) + { + __destroyed = new AtomicBoolean(false); + String response = _connector.getResponse("GET /unavailable/info HTTP/1.0\r\n\r\n"); + assertThat(response, Matchers.containsString("HTTP/1.1 404 ")); + assertTrue(__destroyed.get()); + } + } + } + @Test + public void testUnavailable() throws Exception + { + try (StacklessLogging ignore =new StacklessLogging(_context.getLogger())) + { + try (StacklessLogging ignore2 = new StacklessLogging(HttpChannel.class)) + { + __destroyed = new AtomicBoolean(false); + String response = _connector.getResponse("GET /unavailable/info?for=1 HTTP/1.0\r\n\r\n"); + assertThat(response, Matchers.containsString("HTTP/1.1 503 ")); + assertFalse(__destroyed.get()); + + response = _connector.getResponse("GET /unavailable/info?ok=true HTTP/1.0\r\n\r\n"); + assertThat(response, Matchers.containsString("HTTP/1.1 503 ")); + assertFalse(__destroyed.get()); + + Thread.sleep(1500); + + response = _connector.getResponse("GET /unavailable/info?ok=true HTTP/1.0\r\n\r\n"); + assertThat(response, Matchers.containsString("HTTP/1.1 200 ")); + assertFalse(__destroyed.get()); + } + } + } + public static class AppServlet extends HttpServlet implements Servlet { @Override @@ -618,6 +661,35 @@ public class ErrorPageTest } } + + public static class UnavailableServlet extends HttpServlet implements Servlet + { + @Override + protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + String ok = request.getParameter("ok"); + if (Boolean.parseBoolean(ok)) + { + response.setStatus(200); + response.flushBuffer(); + return; + } + + String f = request.getParameter("for"); + if (f == null) + throw new UnavailableException("testing permanent"); + + throw new UnavailableException("testing periodic", Integer.parseInt(f)); + } + + @Override + public void destroy() + { + if (__destroyed != null) + __destroyed.set(true); + } + } + public static class SingleDispatchFilter implements Filter { ConcurrentMap dispatches = new ConcurrentHashMap<>(); @@ -665,7 +737,6 @@ public class ErrorPageTest @Override public void destroy() { - } } } diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/HolderTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/HolderTest.java deleted file mode 100644 index d29eb5baa67..00000000000 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/HolderTest.java +++ /dev/null @@ -1,80 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2019 Mort Bay Consulting 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. -// ======================================================================== -// - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.servlet; - -import java.util.Collections; -import java.util.Set; -import javax.servlet.ServletRegistration; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * - */ -public class HolderTest -{ - - @Test - public void testInitParams() throws Exception - { - ServletHolder holder = new ServletHolder(Source.JAVAX_API); - ServletRegistration reg = holder.getRegistration(); - - assertThrows(IllegalArgumentException.class,() -> reg.setInitParameter(null, "foo")); - - assertThrows(IllegalArgumentException.class,() -> reg.setInitParameter("foo", null)); - - reg.setInitParameter("foo", "bar"); - assertFalse(reg.setInitParameter("foo", "foo")); - - Set clash = reg.setInitParameters(Collections.singletonMap("foo", "bax")); - assertTrue(clash != null && clash.size() == 1, "should be one clash"); - - assertThrows(IllegalArgumentException.class,() -> reg.setInitParameters(Collections.singletonMap((String)null, "bax"))); - assertThrows(IllegalArgumentException.class,() -> reg.setInitParameters(Collections.singletonMap("foo", (String)null))); - - Set clash2 = reg.setInitParameters(Collections.singletonMap("FOO", "bax")); - assertTrue(clash2.isEmpty(), "should be no clash"); - assertEquals(2, reg.getInitParameters().size(), "setInitParameters should not replace existing non-clashing init parameters"); - } -} diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletHolderTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletHolderTest.java index f9553980962..c2c5b43dc6e 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletHolderTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletHolderTest.java @@ -18,11 +18,11 @@ package org.eclipse.jetty.servlet; -import javax.servlet.ServletException; +import java.util.Collections; +import java.util.Set; +import javax.servlet.ServletRegistration; import javax.servlet.UnavailableException; import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.MultiException; @@ -32,18 +32,41 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import java.io.IOException; - public class ServletHolderTest { - public static class FakeServlet extends HttpServlet { } - + + @Test + public void testInitParams() throws Exception + { + ServletHolder holder = new ServletHolder(Source.JAVAX_API); + ServletRegistration reg = holder.getRegistration(); + + assertThrows(IllegalArgumentException.class,() -> reg.setInitParameter(null, "foo")); + + assertThrows(IllegalArgumentException.class,() -> reg.setInitParameter("foo", null)); + + reg.setInitParameter("foo", "bar"); + assertFalse(reg.setInitParameter("foo", "foo")); + + Set clash = reg.setInitParameters(Collections.singletonMap("foo", "bax")); + assertTrue(clash != null && clash.size() == 1, "should be one clash"); + + assertThrows(IllegalArgumentException.class,() -> reg.setInitParameters(Collections.singletonMap((String)null, "bax"))); + assertThrows(IllegalArgumentException.class,() -> reg.setInitParameters(Collections.singletonMap("foo", (String)null))); + + Set clash2 = reg.setInitParameters(Collections.singletonMap("FOO", "bax")); + assertTrue(clash2.isEmpty(), "should be no clash"); + assertEquals(2, reg.getInitParameters().size(), "setInitParameters should not replace existing non-clashing init parameters"); + } @Test public void testTransitiveCompareTo() throws Exception @@ -78,26 +101,16 @@ public class ServletHolderTest ServletHolder h = new ServletHolder(); h.setName("test"); - assertEquals(null, h.getClassNameForJsp(null)); - - assertEquals(null, h.getClassNameForJsp("")); - - assertEquals(null, h.getClassNameForJsp("/blah/")); - - assertEquals(null, h.getClassNameForJsp("//blah///")); - - assertEquals(null, h.getClassNameForJsp("/a/b/c/blah/")); - + assertNull(h.getClassNameForJsp(null)); + assertNull(h.getClassNameForJsp("")); + assertNull(h.getClassNameForJsp("/blah/")); + assertNull(h.getClassNameForJsp("//blah///")); + assertNull(h.getClassNameForJsp("/a/b/c/blah/")); assertEquals("org.apache.jsp.a.b.c.blah", h.getClassNameForJsp("/a/b/c/blah")); - assertEquals("org.apache.jsp.blah_jsp", h.getClassNameForJsp("/blah.jsp")); - assertEquals("org.apache.jsp.blah_jsp", h.getClassNameForJsp("//blah.jsp")); - assertEquals("org.apache.jsp.blah_jsp", h.getClassNameForJsp("blah.jsp")); - assertEquals("org.apache.jsp.a.b.c.blah_jsp", h.getClassNameForJsp("/a/b/c/blah.jsp")); - assertEquals("org.apache.jsp.a.b.c.blah_jsp", h.getClassNameForJsp("a/b/c/blah.jsp")); } From 8c1e0b07144c545d0b45690e5d9db785002afc39 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Thu, 12 Sep 2019 12:14:15 +0200 Subject: [PATCH 065/113] Fixes #4082 - Debug logging causes NullPointerException in client. Using BufferUtil to guard against NPEs. Signed-off-by: Simone Bordet --- .../org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java index 15e239eee06..d81a75be207 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java @@ -173,10 +173,10 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res boolean complete = this.complete; this.complete = false; if (LOG.isDebugEnabled()) - LOG.debug("Parsed {}, remaining {} {}", handle, buffer.remaining(), parser); + LOG.debug("Parsed {}, remaining {} {}", handle, BufferUtil.length(buffer), parser); if (handle) return true; - if (!buffer.hasRemaining()) + if (!BufferUtil.hasContent(buffer)) return false; if (complete) { From afa987ac643da3e866a47e6466d2a497d6d3e759 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 12 Sep 2019 21:39:36 +1000 Subject: [PATCH 066/113] Update from review reformat Use ByteBuffer API Signed-off-by: Greg Wilkins --- .../eclipse/jetty/http2/hpack/Huffman.java | 9 ++----- .../eclipse/jetty/http2/hpack/HpackTest.java | 25 +++++++++---------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java index 85cc3a9ebc6..9edd8a0fd7f 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/Huffman.java @@ -523,9 +523,6 @@ public class Huffman long current = 0; int n = 0; - byte[] array = buffer.array(); - int p = buffer.arrayOffset() + buffer.position(); - int len = b.length; for (int i = 0; i < len; i++) { @@ -540,7 +537,7 @@ public class Huffman while (n >= 8) { n -= 8; - array[p++] = (byte)(current >> n); + buffer.put((byte)(current >> n)); } } @@ -548,9 +545,7 @@ public class Huffman { current <<= (8 - n); current |= (0xFF >>> n); - array[p++] = (byte)current; + buffer.put((byte)(current)); } - - buffer.position(p - buffer.arrayOffset()); } } diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java index c2a2306fee5..b45ee3b49e5 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java @@ -135,27 +135,26 @@ public class HpackTest assertThat(e.getMessage(), containsString("Header too large")); } } - + @Test public void encodeDecodeNonAscii() throws Exception { HpackEncoder encoder = new HpackEncoder(); - HpackDecoder decoder = new HpackDecoder(4096,8192); - ByteBuffer buffer = BufferUtil.allocate(16*1024); - + HpackDecoder decoder = new HpackDecoder(4096, 8192); + ByteBuffer buffer = BufferUtil.allocate(16 * 1024); + HttpFields fields0 = new HttpFields(); - fields0.add("Cookie","[\uD842\uDF9F]"); - fields0.add("custom-key","[\uD842\uDF9F]"); - Response original0 = new MetaData.Response(HttpVersion.HTTP_2,200,fields0); - + fields0.add("Cookie", "[\uD842\uDF9F]"); + fields0.add("custom-key", "[\uD842\uDF9F]"); + Response original0 = new MetaData.Response(HttpVersion.HTTP_2, 200, fields0); + BufferUtil.clearToFill(buffer); - encoder.encode(buffer,original0); - BufferUtil.flipToFlush(buffer,0); + encoder.encode(buffer, original0); + BufferUtil.flipToFlush(buffer, 0); Response decoded0 = (Response)decoder.decode(buffer); - - assertMetadataSame(original0,decoded0); + + assertMetadataSame(original0, decoded0); } - @Test public void evictReferencedFieldTest() throws Exception From 6e8b610f303949654d5d6045de9a8528957737b5 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Thu, 12 Sep 2019 16:44:19 +0200 Subject: [PATCH 067/113] Fixed compilation errors after merge. Signed-off-by: Simone Bordet --- .../src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java index 1300c437f9b..220bd5225be 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java @@ -153,7 +153,7 @@ public class HpackTest BufferUtil.flipToFlush(buffer, 0); Response decoded0 = (Response)decoder.decode(buffer); - assertMetadataSame(original0, decoded0); + assertMetaDataSame(original0, decoded0); } @Test From a117c4428a8b411d21ba469e208614d5398784d1 Mon Sep 17 00:00:00 2001 From: olivier lamy Date: Fri, 13 Sep 2019 06:22:09 +1000 Subject: [PATCH 068/113] fix typo Signed-off-by: olivier lamy --- .../test/java/org/eclipse/jetty/http2/hpack/HpackTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java index 1300c437f9b..245f925cd2f 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java @@ -153,9 +153,9 @@ public class HpackTest BufferUtil.flipToFlush(buffer, 0); Response decoded0 = (Response)decoder.decode(buffer); - assertMetadataSame(original0, decoded0); + assertMetaDataSame(original0, decoded0); } - + @Test public void evictReferencedFieldTest() throws Exception { From 92e4d73dcb69266984f5a566fe8885cf3ab8f733 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Fri, 13 Sep 2019 09:52:28 +1000 Subject: [PATCH 069/113] Issue #1036 Configure Scheduler Allows scheduler configuration Signed-off-by: Greg Wilkins --- jetty-server/src/main/config/etc/jetty.xml | 6 ++- .../src/main/config/modules/server.mod | 5 +++ .../thread/ScheduledExecutorScheduler.java | 39 +++++++++++++++---- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/jetty-server/src/main/config/etc/jetty.xml b/jetty-server/src/main/config/etc/jetty.xml index 02a43bf2e56..a09928522af 100644 --- a/jetty-server/src/main/config/etc/jetty.xml +++ b/jetty-server/src/main/config/etc/jetty.xml @@ -15,7 +15,11 @@ - + + + + + diff --git a/jetty-server/src/main/config/modules/server.mod b/jetty-server/src/main/config/modules/server.mod index dd039a01fb1..55ad19b9bec 100644 --- a/jetty-server/src/main/config/modules/server.mod +++ b/jetty-server/src/main/config/modules/server.mod @@ -84,3 +84,8 @@ patch-module: servlet.api=lib/jetty-schemas-3.1.jar ## Dump the state of the Jetty server, components, and webapps before shutdown # jetty.server.dumpBeforeStop=false + +## Scheduler Configuration +# jetty.scheduler.name= +# jetty.scheduler.deamon=false +# jetty.scheduler.threads=-1 diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java index a0c889d162c..90a629b5205 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java @@ -22,7 +22,10 @@ import java.io.IOException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import org.eclipse.jetty.util.ProcessorUtils; +import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.component.Dumpable; @@ -40,6 +43,8 @@ public class ScheduledExecutorScheduler extends AbstractLifeCycle implements Sch private final boolean daemon; private final ClassLoader classloader; private final ThreadGroup threadGroup; + private final int threads; + private final AtomicInteger count = new AtomicInteger(); private volatile ScheduledThreadPoolExecutor scheduler; private volatile Thread thread; @@ -50,28 +55,48 @@ public class ScheduledExecutorScheduler extends AbstractLifeCycle implements Sch public ScheduledExecutorScheduler(String name, boolean daemon) { - this(name, daemon, Thread.currentThread().getContextClassLoader()); + this(name, daemon, null); } - public ScheduledExecutorScheduler(String name, boolean daemon, ClassLoader threadFactoryClassLoader) + public ScheduledExecutorScheduler(@Name("name") String name, @Name("daemon") boolean daemon, @Name("threads") int threads) { - this(name, daemon, threadFactoryClassLoader, null); + this(name, daemon, null, null, threads); } - public ScheduledExecutorScheduler(String name, boolean daemon, ClassLoader threadFactoryClassLoader, ThreadGroup threadGroup) + public ScheduledExecutorScheduler(String name, boolean daemon, ClassLoader classLoader) + { + this(name, daemon, classLoader, null); + } + + public ScheduledExecutorScheduler(String name, boolean daemon, ClassLoader classLoader, ThreadGroup threadGroup) + { + this(name, daemon, classLoader, threadGroup, -1); + } + + /** + * @param name The name of the scheduler threads or null for automatic name + * @param daemon True if scheduler threads should be daemon + * @param classLoader The classloader to run the threads with or null to use the current thread context classloader + * @param threadGroup The threadgroup to use or null for no thread group + * @param threads The number of threads to pass to the the core {@link ScheduledThreadPoolExecutor} or -1 for a + * heuristic determined number of threads. + */ + public ScheduledExecutorScheduler(@Name("name") String name, @Name("daemon") boolean daemon, @Name("classLoader") ClassLoader classLoader, @Name("threadGroup") ThreadGroup threadGroup, @Name("threads") int threads) { this.name = name == null ? "Scheduler-" + hashCode() : name; this.daemon = daemon; - this.classloader = threadFactoryClassLoader == null ? Thread.currentThread().getContextClassLoader() : threadFactoryClassLoader; + this.classloader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader; this.threadGroup = threadGroup; + this.threads = threads; } @Override protected void doStart() throws Exception { - scheduler = new ScheduledThreadPoolExecutor(1, r -> + int size = threads > 0 ? threads : Math.max(1, ProcessorUtils.availableProcessors() / 4); + scheduler = new ScheduledThreadPoolExecutor(size, r -> { - Thread thread = ScheduledExecutorScheduler.this.thread = new Thread(threadGroup, r, name); + Thread thread = ScheduledExecutorScheduler.this.thread = new Thread(threadGroup, r, name + "-" + count.incrementAndGet()); thread.setDaemon(daemon); thread.setContextClassLoader(classloader); return thread; From c2eff992b7d7ccb856f9f85aae26b592f159f7c6 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Fri, 13 Sep 2019 11:15:13 +1000 Subject: [PATCH 070/113] Issue #3982 - fix to WebSocket bytesIn for flaky WebSocketStatsTest Signed-off-by: Lachlan Roberts --- .../tests/WebSocketConnectionStatsTest.java | 179 ------------------ .../jetty/websocket/common/Parser.java | 10 - .../io/AbstractWebSocketConnection.java | 5 +- 3 files changed, 4 insertions(+), 190 deletions(-) delete mode 100644 jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketConnectionStatsTest.java diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketConnectionStatsTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketConnectionStatsTest.java deleted file mode 100644 index 148720950c4..00000000000 --- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketConnectionStatsTest.java +++ /dev/null @@ -1,179 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.websocket.tests; - -import java.net.URI; -import java.nio.ByteBuffer; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.Connection; -import org.eclipse.jetty.io.ConnectionStatistics; -import org.eclipse.jetty.io.MappedByteBufferPool; -import org.eclipse.jetty.server.HttpConnection; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.StatusCode; -import org.eclipse.jetty.websocket.api.WebSocketPolicy; -import org.eclipse.jetty.websocket.client.WebSocketClient; -import org.eclipse.jetty.websocket.common.CloseInfo; -import org.eclipse.jetty.websocket.common.Generator; -import org.eclipse.jetty.websocket.common.WebSocketFrame; -import org.eclipse.jetty.websocket.common.frames.TextFrame; -import org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection; -import org.eclipse.jetty.websocket.servlet.WebSocketServlet; -import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class WebSocketConnectionStatsTest -{ - public static class MyWebSocketServlet extends WebSocketServlet - { - @Override - public void configure(WebSocketServletFactory factory) - { - factory.setCreator((req, resp) -> new EchoSocket()); - } - } - - private Server server; - private ServerConnector connector; - private WebSocketClient client; - private ConnectionStatistics statistics; - private CountDownLatch wsUpgradeComplete = new CountDownLatch(1); - private CountDownLatch wsConnectionClosed = new CountDownLatch(1); - - @BeforeEach - public void start() throws Exception - { - statistics = new ConnectionStatistics() - { - @Override - public void onClosed(Connection connection) - { - super.onClosed(connection); - - if (connection instanceof AbstractWebSocketConnection) - wsConnectionClosed.countDown(); - else if (connection instanceof HttpConnection) - wsUpgradeComplete.countDown(); - } - }; - - server = new Server(); - connector = new ServerConnector(server); - connector.addBean(statistics); - server.addConnector(connector); - - ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); - contextHandler.setContextPath("/"); - contextHandler.addServlet(MyWebSocketServlet.class, "/testPath"); - server.setHandler(contextHandler); - - client = new WebSocketClient(); - - server.start(); - client.start(); - } - - @AfterEach - public void stop() throws Exception - { - client.stop(); - server.stop(); - } - - long getFrameByteSize(WebSocketFrame frame) - { - ByteBufferPool bufferPool = new MappedByteBufferPool(); - Generator generator = new Generator(WebSocketPolicy.newClientPolicy(), bufferPool); - ByteBuffer buffer = bufferPool.acquire(frame.getPayloadLength() + 10, true); - int pos = BufferUtil.flipToFill(buffer); - generator.generateWholeFrame(frame, buffer); - return buffer.position() - pos; - } - - @Disabled("Flaky test see issue #3982") - @Test - public void echoStatsTest() throws Exception - { - URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/testPath"); - EventSocket socket = new EventSocket(); - Future connect = client.connect(socket, uri); - - final long numMessages = 10000; - final String msgText = "hello world"; - - long upgradeSentBytes; - long upgradeReceivedBytes; - - try (Session session = connect.get(5, TimeUnit.SECONDS)) - { - wsUpgradeComplete.await(5, TimeUnit.SECONDS); - upgradeSentBytes = statistics.getSentBytes(); - upgradeReceivedBytes = statistics.getReceivedBytes(); - - for (int i = 0; i < numMessages; i++) - { - session.getRemote().sendString(msgText); - } - session.close(StatusCode.NORMAL, null); - - assertTrue(socket.closed.await(5, TimeUnit.SECONDS)); - assertTrue(wsConnectionClosed.await(5, TimeUnit.SECONDS)); - } - - assertThat(statistics.getConnectionsMax(), is(1L)); - assertThat(statistics.getConnections(), is(0L)); - - assertThat(statistics.getSentMessages(), is(numMessages + 2L)); - assertThat(statistics.getReceivedMessages(), is(numMessages + 2L)); - - WebSocketFrame textFrame = new TextFrame().setPayload(msgText); - WebSocketFrame closeFrame = new CloseInfo(socket.closeCode, socket.closeReason).asFrame(); - - final long textFrameSize = getFrameByteSize(textFrame); - final long closeFrameSize = getFrameByteSize(closeFrame); - final int maskSize = 4; // We use 4 byte mask for client frames - - // Pointless Sanity Checks - // assertThat("Upgrade Sent Bytes", upgradeSentBytes, is(197L)); - // assertThat("Upgrade Received Bytes", upgradeReceivedBytes, is(261L)); - // assertThat("Text Frame Size", textFrameSize, is(13L)); - // assertThat("Close Frame Size", closeFrameSize, is(4L)); - - final long expectedSent = upgradeSentBytes + numMessages * textFrameSize + closeFrameSize; - final long expectedReceived = upgradeReceivedBytes + numMessages * (textFrameSize + maskSize) + closeFrameSize + maskSize; - - assertThat("stats.sendBytes", statistics.getSentBytes(), is(expectedSent)); - assertThat("stats.receivedBytes", statistics.getReceivedBytes(), is(expectedReceived)); - } -} diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java index e6d1981b87c..853dd2c4486 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/Parser.java @@ -65,7 +65,6 @@ public class Parser // Stats (where a message is defined as a WebSocket frame) private final LongAdder messagesIn = new LongAdder(); - private final LongAdder bytesIn = new LongAdder(); // State specific private State state = State.START; @@ -250,8 +249,6 @@ public class Parser try { - int startingBytes = buffer.remaining(); - // attempt to parse a frame from the buffer if (parseFrame(buffer)) { @@ -266,8 +263,6 @@ public class Parser } reset(); } - - bytesIn.add(startingBytes - buffer.remaining()); } catch (Throwable t) { @@ -657,11 +652,6 @@ public class Parser return messagesIn.longValue(); } - public long getBytesIn() - { - return bytesIn.longValue(); - } - @Override public String toString() { diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java index 003627a6b04..de24fd7af75 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.LongAdder; import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.AbstractEndPoint; @@ -139,6 +140,7 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp private final ConnectionState connectionState = new ConnectionState(); private final FrameFlusher flusher; private final String id; + private final LongAdder bytesIn = new LongAdder(); private WebSocketSession session; private List extensions = new ArrayList<>(); private ByteBuffer prefillBuffer; @@ -472,6 +474,7 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp return; } + bytesIn.add(filled); if (LOG.isDebugEnabled()) LOG.debug("Filled {} bytes - {}", filled, BufferUtil.toDetailString(buffer)); } @@ -668,7 +671,7 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp @Override public long getBytesIn() { - return parser.getBytesIn(); + return bytesIn.longValue(); } /** From 2d6e9b0efcfd4e9c4e02108f846c2de83f053e7f Mon Sep 17 00:00:00 2001 From: olivier lamy Date: Fri, 13 Sep 2019 11:17:34 +1000 Subject: [PATCH 071/113] use maven invoker plugin 3.2.1 released version Signed-off-by: olivier lamy --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2f5bd60e1ca..0174b5fa075 100644 --- a/pom.xml +++ b/pom.xml @@ -518,7 +518,7 @@ org.apache.maven.plugins maven-invoker-plugin - 3.2.1-SNAPSHOT + 3.2.1 true true From 63c7a4496924b031dc358c0c8ce696509e2c010d Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Fri, 13 Sep 2019 11:18:57 +1000 Subject: [PATCH 072/113] Issue #3982 - fix to WebSocket bytesIn for flaky WebSocketStatsTest Signed-off-by: Lachlan Roberts --- .../websocket/tests/WebSocketStatsTest.java | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketStatsTest.java diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketStatsTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketStatsTest.java new file mode 100644 index 00000000000..268a5eaeb9d --- /dev/null +++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketStatsTest.java @@ -0,0 +1,169 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.websocket.tests; + +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.ConnectionStatistics; +import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.server.HttpConnection; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.WebSocketPolicy; +import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.eclipse.jetty.websocket.common.CloseInfo; +import org.eclipse.jetty.websocket.common.Generator; +import org.eclipse.jetty.websocket.common.WebSocketFrame; +import org.eclipse.jetty.websocket.common.frames.TextFrame; +import org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection; +import org.eclipse.jetty.websocket.servlet.WebSocketServlet; +import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class WebSocketStatsTest +{ + public static class MyWebSocketServlet extends WebSocketServlet + { + @Override + public void configure(WebSocketServletFactory factory) + { + factory.setCreator((req, resp) -> new EchoSocket()); + } + } + + private Server server; + private ServerConnector connector; + private WebSocketClient client; + private ConnectionStatistics statistics; + private CountDownLatch wsUpgradeComplete = new CountDownLatch(1); + private CountDownLatch wsConnectionClosed = new CountDownLatch(1); + + @BeforeEach + public void start() throws Exception + { + statistics = new ConnectionStatistics() + { + @Override + public void onClosed(Connection connection) + { + super.onClosed(connection); + + if (connection instanceof AbstractWebSocketConnection) + wsConnectionClosed.countDown(); + else if (connection instanceof HttpConnection) + wsUpgradeComplete.countDown(); + } + }; + + server = new Server(); + connector = new ServerConnector(server); + connector.addBean(statistics); + server.addConnector(connector); + + ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); + contextHandler.setContextPath("/"); + contextHandler.addServlet(MyWebSocketServlet.class, "/testPath"); + server.setHandler(contextHandler); + + client = new WebSocketClient(); + + server.start(); + client.start(); + } + + @AfterEach + public void stop() throws Exception + { + client.stop(); + server.stop(); + } + + long getFrameByteSize(WebSocketFrame frame) + { + ByteBufferPool bufferPool = new MappedByteBufferPool(); + Generator generator = new Generator(WebSocketPolicy.newClientPolicy(), bufferPool); + ByteBuffer buffer = bufferPool.acquire(frame.getPayloadLength() + 10, true); + int pos = BufferUtil.flipToFill(buffer); + generator.generateWholeFrame(frame, buffer); + return buffer.position() - pos; + } + + @Test + public void echoStatsTest() throws Exception + { + URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/testPath"); + EventSocket socket = new EventSocket(); + Future connect = client.connect(socket, uri); + + final long numMessages = 10000; + final String msgText = "hello world"; + + long upgradeSentBytes; + long upgradeReceivedBytes; + + try (Session session = connect.get(5, TimeUnit.SECONDS)) + { + wsUpgradeComplete.await(5, TimeUnit.SECONDS); + upgradeSentBytes = statistics.getSentBytes(); + upgradeReceivedBytes = statistics.getReceivedBytes(); + + for (int i = 0; i < numMessages; i++) + { + session.getRemote().sendString(msgText); + } + } + + assertTrue(socket.closed.await(5, TimeUnit.SECONDS)); + assertTrue(wsConnectionClosed.await(5, TimeUnit.SECONDS)); + + assertThat(statistics.getConnectionsMax(), is(1L)); + assertThat(statistics.getConnections(), is(0L)); + + assertThat(statistics.getSentMessages(), is(numMessages + 2L)); + assertThat(statistics.getReceivedMessages(), is(numMessages + 2L)); + + WebSocketFrame textFrame = new TextFrame().setPayload(msgText); + WebSocketFrame closeFrame = new CloseInfo(socket.closeCode, socket.closeReason).asFrame(); + + final long textFrameSize = getFrameByteSize(textFrame); + final long closeFrameSize = getFrameByteSize(closeFrame); + final int maskSize = 4; // We use 4 byte mask for client frames + + final long expectedSent = upgradeSentBytes + numMessages * textFrameSize + closeFrameSize; + final long expectedReceived = upgradeReceivedBytes + numMessages * (textFrameSize + maskSize) + closeFrameSize + maskSize; + + assertThat("stats.sendBytes", statistics.getSentBytes(), is(expectedSent)); + assertThat("stats.receivedBytes", statistics.getReceivedBytes(), is(expectedReceived)); + } +} From 22fba9aa91ebd4e2d2c01cee80aa3e560797efe1 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Fri, 13 Sep 2019 11:28:05 +1000 Subject: [PATCH 073/113] Issue #3106 - deprecate old WebSocket stats mechanism Signed-off-by: Lachlan Roberts --- .../jetty/websocket/common/io/AbstractWebSocketConnection.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java index de24fd7af75..5c872db308b 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java @@ -101,6 +101,7 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp } } + @Deprecated public static class Stats { private AtomicLong countFillInterestedEvents = new AtomicLong(0); @@ -402,6 +403,7 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp return scheduler; } + @Deprecated() public Stats getStats() { return stats; From ae0ab46066117284fdcffff6daf26c629307f1a7 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Fri, 13 Sep 2019 11:40:11 +1000 Subject: [PATCH 074/113] Issue #97 Unavailable handling Added test for servlet component lifecycle Fixed bug where we were destroying listeners before calling them. Signed-off-by: Greg Wilkins --- .../jetty/server/handler/ContextHandler.java | 14 +- .../jetty/servlet/ServletLifeCycleTest.java | 225 ++++++++++++++++++ 2 files changed, 236 insertions(+), 3 deletions(-) create mode 100644 jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletLifeCycleTest.java diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java index 4297a04a031..884669780f3 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java @@ -893,9 +893,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu protected void stopContext() throws Exception { - // stop all the handler hierarchy - super.doStop(); - // Call the context listeners ServletContextEvent event = new ServletContextEvent(_scontext); Collections.reverse(_destroySerletContextListeners); @@ -911,6 +908,17 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu ex.add(x); } } + + // stop all the handler hierarchy + try + { + super.doStop(); + } + catch (Exception x) + { + ex.add(x); + } + ex.ifExceptionThrow(); } diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletLifeCycleTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletLifeCycleTest.java new file mode 100644 index 00000000000..f813bcefb4e --- /dev/null +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletLifeCycleTest.java @@ -0,0 +1,225 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting 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.servlet; + +import java.io.IOException; +import java.util.EnumSet; +import java.util.EventListener; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import javax.servlet.DispatcherType; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.Servlet; +import javax.servlet.ServletConfig; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import org.eclipse.jetty.server.LocalConnector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.Decorator; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class ServletLifeCycleTest +{ + static final Queue events = new ConcurrentLinkedQueue<>(); + + @Test + public void testLifeCycle() throws Exception + { + Server server = new Server(0); + LocalConnector connector = new LocalConnector(server); + server.addConnector(connector); + + ServletContextHandler context = new ServletContextHandler(server, "/"); + + context.getObjectFactory().addDecorator(new TestDecorator()); + + ServletHandler sh = context.getServletHandler(); + sh.addListener(new ListenerHolder(TestListener.class)); + context.addEventListener(context.getServletContext().createListener(TestListener2.class)); + + sh.addFilterWithMapping(TestFilter.class,"/*", EnumSet.of(DispatcherType.REQUEST)); + sh.addFilterWithMapping(new FilterHolder(context.getServletContext().createFilter(TestFilter2.class)),"/*", EnumSet.of(DispatcherType.REQUEST)); + + sh.addServletWithMapping(TestServlet.class, "/1/*").setInitOrder(1); + sh.addServletWithMapping(TestServlet2.class, "/2/*").setInitOrder(-1); + sh.addServletWithMapping(new ServletHolder(context.getServletContext().createServlet(TestServlet3.class)) {{setInitOrder(1);}}, "/3/*"); + + assertThat(events, Matchers.contains( + "Decorate class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestListener2", + "Decorate class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestFilter2", + "Decorate class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestServlet3")); + + events.clear(); + server.start(); + assertThat(events, Matchers.contains( + "Decorate class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestListener", + "ContextInitialized class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestListener2", + "ContextInitialized class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestListener", + "Decorate class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestFilter", + "init class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestFilter", + "init class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestFilter2", + "Decorate class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestServlet", + "init class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestServlet", + "init class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestServlet3")); + + events.clear(); + connector.getResponse("GET /2/info HTTP/1.0\r\n\r\n"); + + assertThat(events, Matchers.contains( + "Decorate class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestServlet2", + "init class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestServlet2", + "doFilter class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestFilter", + "doFilter class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestFilter2", + "service class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestServlet2")); + + events.clear(); + server.stop(); + + assertThat(events, Matchers.contains( + "contextDestroyed class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestListener", + "contextDestroyed class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestListener2", + "destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestFilter2", + "Destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestFilter2", + "destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestFilter", + "Destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestFilter", + "Destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestServlet3", + "destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestServlet3", + "Destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestServlet2", + "destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestServlet2", + "Destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestServlet", + "destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestServlet", + "Destroy class org.eclipse.jetty.servlet.ServletLifeCycleTest$TestListener")); + + // Listener added before start is not destroyed + EventListener[] listeners = context.getEventListeners(); + assertThat(listeners.length, is(1)); + assertThat(listeners[0].getClass(), is(TestListener2.class)); + } + + public static class TestDecorator implements Decorator + { + @Override + public T decorate(T o) + { + events.add("Decorate " + o.getClass()); + return o; + } + + @Override + public void destroy(Object o) + { + events.add("Destroy " + o.getClass()); + } + } + + public static class TestListener implements ServletContextListener + { + @Override + public void contextInitialized(ServletContextEvent sce) + { + events.add("ContextInitialized " + this.getClass()); + } + + @Override + public void contextDestroyed(ServletContextEvent sce) + { + events.add("contextDestroyed " + this.getClass()); + } + } + public static class TestListener2 extends TestListener + { + } + + public static class TestFilter implements Filter + { + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + events.add("init " + this.getClass()); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException + { + events.add("doFilter " + this.getClass()); + chain.doFilter(request, response); + } + + @Override + public void destroy() + { + events.add("destroy " + this.getClass()); + } + } + + public static class TestFilter2 extends TestFilter + { + } + + public static class TestServlet implements Servlet + { + @Override + public void init(ServletConfig config) throws ServletException + { + events.add("init " + this.getClass()); + } + + @Override + public ServletConfig getServletConfig() + { + return null; + } + + @Override + public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException + { + events.add("service " + this.getClass()); + } + + @Override + public String getServletInfo() + { + return null; + } + + @Override + public void destroy() + { + events.add("destroy " + this.getClass()); + } + } + + public static class TestServlet2 extends TestServlet + { + } + + public static class TestServlet3 extends TestServlet + { + } +} From b3912d54feb441671b26a1eb5964306ea4dccbc0 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Fri, 13 Sep 2019 22:01:57 +0200 Subject: [PATCH 075/113] Fixes #137 - Support OAuth. Review and code cleanups. Signed-off-by: Simone Bordet --- .../security/openid/OpenIdAuthenticator.java | 30 ++++++++++------- .../security/openid/OpenIdConfiguration.java | 11 +++---- .../security/openid/OpenIdCredentials.java | 32 +++++++++++-------- .../security/openid/OpenIdLoginService.java | 13 +++++--- .../security/openid/OpenIdUserIdentity.java | 4 +-- 5 files changed, 52 insertions(+), 38 deletions(-) diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java index 79725897567..41fce0be5ee 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdAuthenticator.java @@ -127,7 +127,7 @@ public class OpenIdAuthenticator extends LoginAuthenticator _alwaysSaveUri = alwaysSave; } - public boolean getAlwaysSaveUri() + public boolean isAlwaysSaveUri() { return _alwaysSaveUri; } @@ -283,7 +283,8 @@ public class OpenIdAuthenticator extends LoginAuthenticator } } OpenIdAuthentication openIdAuth = new OpenIdAuthentication(getAuthMethod(), user); - LOG.debug("authenticated {}->{}", openIdAuth, nuri); + if (LOG.isDebugEnabled()) + LOG.debug("authenticated {}->{}", openIdAuth, nuri); response.setContentLength(0); int redirectCode = (baseRequest.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER); @@ -322,7 +323,8 @@ public class OpenIdAuthenticator extends LoginAuthenticator if (authentication instanceof Authentication.User && _loginService != null && !_loginService.validate(((Authentication.User)authentication).getUserIdentity())) { - LOG.debug("auth revoked {}", authentication); + if (LOG.isDebugEnabled()) + LOG.debug("auth revoked {}", authentication); session.removeAttribute(SessionAuthentication.__J_AUTHENTICATED); } else @@ -334,17 +336,20 @@ public class OpenIdAuthenticator extends LoginAuthenticator { //check if the request is for the same url as the original and restore //params if it was a post - LOG.debug("auth retry {}->{}", authentication, jUri); + if (LOG.isDebugEnabled()) + LOG.debug("auth retry {}->{}", authentication, jUri); StringBuffer buf = request.getRequestURL(); if (request.getQueryString() != null) buf.append("?").append(request.getQueryString()); if (jUri.equals(buf.toString())) { + @SuppressWarnings("unchecked") MultiMap jPost = (MultiMap)session.getAttribute(J_POST); if (jPost != null) { - LOG.debug("auth rePOST {}->{}", authentication, jUri); + if (LOG.isDebugEnabled()) + LOG.debug("auth rePOST {}->{}", authentication, jUri); baseRequest.setContentParameters(jPost); } session.removeAttribute(J_URI); @@ -353,7 +358,8 @@ public class OpenIdAuthenticator extends LoginAuthenticator } } } - LOG.debug("auth {}", authentication); + if (LOG.isDebugEnabled()) + LOG.debug("auth {}", authentication); return authentication; } } @@ -361,7 +367,8 @@ public class OpenIdAuthenticator extends LoginAuthenticator // if we can't send challenge if (DeferredAuthentication.isDeferred(response)) { - LOG.debug("auth deferred {}", session == null ? null : session.getId()); + if (LOG.isDebugEnabled()) + LOG.debug("auth deferred {}", session == null ? null : session.getId()); return Authentication.UNAUTHENTICATED; } @@ -370,7 +377,7 @@ public class OpenIdAuthenticator extends LoginAuthenticator synchronized (session) { // But only if it is not set already, or we save every uri that leads to a login redirect - if (session.getAttribute(J_URI) == null || _alwaysSaveUri) + if (session.getAttribute(J_URI) == null || isAlwaysSaveUri()) { StringBuffer buf = request.getRequestURL(); if (request.getQueryString() != null) @@ -389,7 +396,8 @@ public class OpenIdAuthenticator extends LoginAuthenticator // send the the challenge String challengeUri = getChallengeUri(request); - LOG.debug("challenge {}->{}", session.getId(), challengeUri); + if (LOG.isDebugEnabled()) + LOG.debug("challenge {}->{}", session.getId(), challengeUri); int redirectCode = (baseRequest.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER); baseResponse.sendRedirect(redirectCode, response.encodeRedirectURL(challengeUri)); @@ -445,7 +453,7 @@ public class OpenIdAuthenticator extends LoginAuthenticator StringBuilder scopes = new StringBuilder(); for (String s : _configuration.getScopes()) { - scopes.append(" " + s); + scopes.append(" ").append(s); } return _configuration.getAuthEndpoint() + @@ -480,4 +488,4 @@ public class OpenIdAuthenticator extends LoginAuthenticator return "OpenId" + super.toString(); } } -} \ No newline at end of file +} diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java index 26eb27e65e1..3d14aa2d36c 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java @@ -22,6 +22,7 @@ import java.io.InputStream; import java.io.Serializable; import java.net.URI; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -49,8 +50,7 @@ public class OpenIdConfiguration implements Serializable private final String clientId; private final String clientSecret; private final Map discoveryDocument; - - private List scopes = new ArrayList<>(); + private final List scopes = new ArrayList<>(); /** * Create an OpenID configuration for a specific OIDC provider. @@ -131,14 +131,11 @@ public class OpenIdConfiguration implements Serializable public void addScopes(String... scopes) { - for (String scope : scopes) - { - this.scopes.add(scope); - } + Collections.addAll(this.scopes, scopes); } public List getScopes() { return scopes; } -} \ No newline at end of file +} diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java index 70f00b80deb..85df9e28b04 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdCredentials.java @@ -189,22 +189,26 @@ public class OpenIdCredentials implements Serializable URL url = new URL(configuration.getTokenEndpoint()); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); - connection.setDoOutput(true); - connection.setRequestMethod("POST"); - connection.setRequestProperty("Host", configuration.getOpenIdProvider()); - connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - - try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) + try { - wr.write(urlParameters.getBytes(StandardCharsets.UTF_8)); - } + connection.setDoOutput(true); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Host", configuration.getOpenIdProvider()); + connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); - Map result; - try (InputStream content = (InputStream)connection.getContent()) + try (DataOutputStream wr = new DataOutputStream(connection.getOutputStream())) + { + wr.write(urlParameters.getBytes(StandardCharsets.UTF_8)); + } + + try (InputStream content = (InputStream)connection.getContent()) + { + return (Map)JSON.parse(IO.toString(content)); + } + } + finally { - result = (Map)JSON.parse(IO.toString(content)); + connection.disconnect(); } - - return result; } -} \ No newline at end of file +} diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java index c2df0b02115..512ff474b71 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdLoginService.java @@ -105,7 +105,7 @@ public class OpenIdLoginService extends ContainerLifeCycle implements LoginServi UserIdentity userIdentity = loginService.login(openIdCredentials.getUserId(), "", req); if (userIdentity == null) { - if (authenticateNewUsers) + if (isAuthenticateNewUsers()) return getIdentityService().newUserIdentity(subject, userPrincipal, new String[0]); return null; } @@ -115,6 +115,11 @@ public class OpenIdLoginService extends ContainerLifeCycle implements LoginServi return identityService.newUserIdentity(subject, userPrincipal, new String[0]); } + public boolean isAuthenticateNewUsers() + { + return authenticateNewUsers; + } + /** * This setting is only meaningful if a wrapped {@link LoginService} has been set. *

    @@ -122,9 +127,9 @@ public class OpenIdLoginService extends ContainerLifeCycle implements LoginServi * be authenticated but with no roles, if set to false users will not be * authenticated unless they are discovered by the wrapped {@link LoginService}. *

    - * @param authenticateNewUsers + * @param authenticateNewUsers whether to authenticate users not found by a wrapping LoginService */ - public void authenticateNewUsers(boolean authenticateNewUsers) + public void setAuthenticateNewUsers(boolean authenticateNewUsers) { this.authenticateNewUsers = authenticateNewUsers; } @@ -162,4 +167,4 @@ public class OpenIdLoginService extends ContainerLifeCycle implements LoginServi public void logout(UserIdentity user) { } -} \ No newline at end of file +} diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserIdentity.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserIdentity.java index 658b63d3850..f375e138c50 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserIdentity.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdUserIdentity.java @@ -51,6 +51,6 @@ public class OpenIdUserIdentity implements UserIdentity @Override public boolean isUserInRole(String role, Scope scope) { - return userIdentity == null ? false : userIdentity.isUserInRole(role, scope); + return userIdentity != null && userIdentity.isUserInRole(role, scope); } -} \ No newline at end of file +} From 85705ca8eb4cdfa2d3f27270eba890f9b2ded9ab Mon Sep 17 00:00:00 2001 From: olivier lamy Date: Sat, 14 Sep 2019 09:11:16 +1000 Subject: [PATCH 076/113] fix build Signed-off-by: olivier lamy --- jetty-bom/pom.xml | 2 +- jetty-openid/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jetty-bom/pom.xml b/jetty-bom/pom.xml index 9804322bce3..fcdf3fe7d3d 100644 --- a/jetty-bom/pom.xml +++ b/jetty-bom/pom.xml @@ -287,7 +287,7 @@ org.eclipse.jetty jetty-openid - 9.4.21-SNAPSHOT + 10.0.0-SNAPSHOT org.eclipse.jetty diff --git a/jetty-openid/pom.xml b/jetty-openid/pom.xml index 0868ea578ad..48dcc6adaf1 100644 --- a/jetty-openid/pom.xml +++ b/jetty-openid/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 9.4.21-SNAPSHOT + 10.0.0-SNAPSHOT 4.0.0 From b027aa5afa6951a1c9552f14560997dab40f277e Mon Sep 17 00:00:00 2001 From: Lachlan Date: Mon, 16 Sep 2019 17:21:33 +1000 Subject: [PATCH 077/113] Fixes #3705 - revert ClientUpgradeRequest error handling logic (#4090) Signed-off-by: Lachlan Roberts --- .../websocket/core/client/ClientUpgradeRequest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/client/ClientUpgradeRequest.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/client/ClientUpgradeRequest.java index 139e355df63..c1b6057e41f 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/client/ClientUpgradeRequest.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/client/ClientUpgradeRequest.java @@ -18,6 +18,7 @@ package org.eclipse.jetty.websocket.core.client; +import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.Base64; @@ -247,19 +248,18 @@ public abstract class ClientUpgradeRequest extends HttpRequest implements Respon } Throwable failure = result.getFailure(); - boolean wrapFailure = !((failure instanceof java.net.SocketException) || - (failure instanceof java.io.InterruptedIOException) || - (failure instanceof UpgradeException)); + boolean wrapFailure = !((failure instanceof IOException) || (failure instanceof UpgradeException)); if (wrapFailure) failure = new UpgradeException(requestURI, responseStatusCode, responseLine, failure); handleException(failure); + return; } if (responseStatusCode != HttpStatus.SWITCHING_PROTOCOLS_101) { // Failed to upgrade (other reason) - handleException( - new UpgradeException(requestURI, responseStatusCode, "Failed to upgrade to websocket: Unexpected HTTP Response Status Code: " + responseLine)); + handleException(new UpgradeException(requestURI, responseStatusCode, + "Failed to upgrade to websocket: Unexpected HTTP Response Status Code: " + responseLine)); } } From 422b9bded561acba9e9d9a3471b9f71c537efaa6 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 16 Sep 2019 22:57:55 +0200 Subject: [PATCH 078/113] Fixes #3747 - Make Jetty Demo work with JPMS. Introduced module `jdbc` and made other modules that require JDBC depend on it. Modified demo.ini to enable the `jdbc` module because some webapp descriptor of the demo requires JDBC classes. Now the demo can be run fine on the module-path just by adding --jpms. Signed-off-by: Simone Bordet --- jetty-server/src/main/config/modules/jdbc.mod | 4 ++++ jetty-server/src/main/config/modules/session-store-jdbc.mod | 5 +---- .../src/main/config/modules/sessions/jdbc/datasource.mod | 3 +++ .../src/main/config/modules/sessions/jdbc/driver.mod | 3 +++ .../src/main/config/demo-base/start.d/demo.ini | 2 ++ 5 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 jetty-server/src/main/config/modules/jdbc.mod diff --git a/jetty-server/src/main/config/modules/jdbc.mod b/jetty-server/src/main/config/modules/jdbc.mod new file mode 100644 index 00000000000..a78fcc256aa --- /dev/null +++ b/jetty-server/src/main/config/modules/jdbc.mod @@ -0,0 +1,4 @@ +DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[jpms] +add-modules: java.sql diff --git a/jetty-server/src/main/config/modules/session-store-jdbc.mod b/jetty-server/src/main/config/modules/session-store-jdbc.mod index 297cf2c69a4..e97457d0781 100644 --- a/jetty-server/src/main/config/modules/session-store-jdbc.mod +++ b/jetty-server/src/main/config/modules/session-store-jdbc.mod @@ -10,6 +10,7 @@ session session-store [depend] +jdbc sessions sessions/jdbc/${db-connection-type} @@ -54,7 +55,3 @@ db-connection-type=datasource #jetty.session.jdbc.schema.maxIntervalColumn=maxInterval #jetty.session.jdbc.schema.mapColumn=map #jetty.session.jdbc.schema.table=JettySessions - - - - diff --git a/jetty-server/src/main/config/modules/sessions/jdbc/datasource.mod b/jetty-server/src/main/config/modules/sessions/jdbc/datasource.mod index be665ff9f3b..d30b911a3b9 100644 --- a/jetty-server/src/main/config/modules/sessions/jdbc/datasource.mod +++ b/jetty-server/src/main/config/modules/sessions/jdbc/datasource.mod @@ -3,5 +3,8 @@ DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-m [description] JDBC Datasource connections for session storage +[depends] +jdbc + [xml] etc/sessions/jdbc/datasource.xml diff --git a/jetty-server/src/main/config/modules/sessions/jdbc/driver.mod b/jetty-server/src/main/config/modules/sessions/jdbc/driver.mod index eb7391a807d..ad387114cd6 100644 --- a/jetty-server/src/main/config/modules/sessions/jdbc/driver.mod +++ b/jetty-server/src/main/config/modules/sessions/jdbc/driver.mod @@ -3,5 +3,8 @@ DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-m [description] JDBC Driver connections for session storage +[depend] +jdbc + [xml] etc/sessions/jdbc/driver.xml diff --git a/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/start.d/demo.ini b/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/start.d/demo.ini index 5921b619377..1960b88974b 100644 --- a/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/start.d/demo.ini +++ b/tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/start.d/demo.ini @@ -21,3 +21,5 @@ org.eclipse.jetty.websocket.jsr356=false etc/test-realm.xml jetty.demo.realm=etc/realm.properties +# JDBC needed by test-jndi and test-spec +--module=jdbc From 42f1214796961e15d73ea3ba91550f8a20d41539 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Tue, 17 Sep 2019 10:54:22 +1000 Subject: [PATCH 079/113] fix OpenID module after rename of authenticateNewUsers Signed-off-by: Lachlan Roberts --- jetty-openid/src/main/config/etc/jetty-openid.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-openid/src/main/config/etc/jetty-openid.xml b/jetty-openid/src/main/config/etc/jetty-openid.xml index 0cb6d538849..65506e85ca8 100644 --- a/jetty-openid/src/main/config/etc/jetty-openid.xml +++ b/jetty-openid/src/main/config/etc/jetty-openid.xml @@ -18,7 +18,7 @@ - + From c37c62323c311afd7bbd07d15927b2fd5a65d5ec Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Tue, 17 Sep 2019 12:41:06 +1000 Subject: [PATCH 080/113] updates from review Signed-off-by: Greg Wilkins --- .../jetty/util/thread/ScheduledExecutorScheduler.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java index 90a629b5205..891b6f6d9f7 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ScheduledExecutorScheduler.java @@ -24,7 +24,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import org.eclipse.jetty.util.ProcessorUtils; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.component.Dumpable; @@ -83,7 +83,7 @@ public class ScheduledExecutorScheduler extends AbstractLifeCycle implements Sch */ public ScheduledExecutorScheduler(@Name("name") String name, @Name("daemon") boolean daemon, @Name("classLoader") ClassLoader classLoader, @Name("threadGroup") ThreadGroup threadGroup, @Name("threads") int threads) { - this.name = name == null ? "Scheduler-" + hashCode() : name; + this.name = StringUtil.isBlank(name) ? "Scheduler-" + hashCode() : name; this.daemon = daemon; this.classloader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader; this.threadGroup = threadGroup; @@ -93,7 +93,7 @@ public class ScheduledExecutorScheduler extends AbstractLifeCycle implements Sch @Override protected void doStart() throws Exception { - int size = threads > 0 ? threads : Math.max(1, ProcessorUtils.availableProcessors() / 4); + int size = threads > 0 ? threads : 1; scheduler = new ScheduledThreadPoolExecutor(size, r -> { Thread thread = ScheduledExecutorScheduler.this.thread = new Thread(threadGroup, r, name + "-" + count.incrementAndGet()); From bb27fa6cc0cc9a875c9db7096eea22138c26560e Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Tue, 17 Sep 2019 11:18:14 +0200 Subject: [PATCH 081/113] Issue #3747 - Make Jetty Demo work with JPMS. Added distribution test for running with JPMS. Signed-off-by: Simone Bordet --- .../tests/distribution/DemoBaseTests.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DemoBaseTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DemoBaseTests.java index 40828383857..a4f06e74c49 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DemoBaseTests.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DemoBaseTests.java @@ -24,6 +24,8 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.http.HttpStatus; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnJre; +import org.junit.jupiter.api.condition.JRE; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -142,4 +144,33 @@ public class DemoBaseTests extends AbstractDistributionTest assertThat(response.getContentAsString(), not(containsString("FAIL"))); } } + + @Test + @DisabledOnJre(JRE.JAVA_8) + public void testJPMS() throws Exception + { + String jettyVersion = System.getProperty("jettyVersion"); + DistributionTester distribution = DistributionTester.Builder.newInstance() + .jettyVersion(jettyVersion) + .jettyBase(Paths.get("demo-base")) + .mavenLocalRepository(System.getProperty("mavenRepoPath")) + .build(); + + int httpPort = distribution.freePort(); + int httpsPort = distribution.freePort(); + String[] args = { + "--jpms", + "jetty.http.port=" + httpPort, + "jetty.httpConfig.port=" + httpsPort, + "jetty.ssl.port=" + httpsPort + }; + try (DistributionTester.Run run = distribution.start(args)) + { + assertTrue(run.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + + startHttpClient(); + ContentResponse response = client.GET("http://localhost:" + httpPort + "/test/hello"); + assertEquals(HttpStatus.OK_200, response.getStatus()); + } + } } From 92b477e49f39ac769fbee82c7acb3697b48c3384 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Tue, 17 Sep 2019 11:29:06 +0200 Subject: [PATCH 082/113] Fixed test after merge. Signed-off-by: Simone Bordet --- .../org/eclipse/jetty/tests/distribution/DemoBaseTests.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DemoBaseTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DemoBaseTests.java index ff589660fd2..4590273f4c8 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DemoBaseTests.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DemoBaseTests.java @@ -24,8 +24,6 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.http.HttpStatus; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnJre; -import org.junit.jupiter.api.condition.JRE; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -146,7 +144,6 @@ public class DemoBaseTests extends AbstractDistributionTest } @Test - @DisabledOnJre(JRE.JAVA_8) public void testJPMS() throws Exception { String jettyVersion = System.getProperty("jettyVersion"); @@ -166,7 +163,7 @@ public class DemoBaseTests extends AbstractDistributionTest }; try (DistributionTester.Run run = distribution.start(args)) { - assertTrue(run.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + httpPort + "/test/hello"); From 0eec727a099ba9ea48a4b8a2f9af8b4a2a577f36 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Tue, 17 Sep 2019 12:16:10 +0200 Subject: [PATCH 083/113] After review, renamed *directBuffers to *directByteBuffers. Signed-off-by: Simone Bordet --- .../eclipse/jetty/http2/HTTP2Connection.java | 16 +++++------ .../jetty/http2/generator/Generator.java | 4 +-- .../http2/generator/HeaderGenerator.java | 8 +++--- .../AbstractHTTP2ServerConnectionFactory.java | 16 +++++------ .../http2/server/HttpChannelOverHTTP2.java | 8 +++--- .../eclipse/jetty/server/HttpConnection.java | 28 +++++++++---------- .../jetty/server/HttpConnectionFactory.java | 16 +++++------ .../websocket/core/internal/FrameFlusher.java | 8 +++--- .../core/internal/WebSocketConnection.java | 16 +++++------ 9 files changed, 60 insertions(+), 60 deletions(-) diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java index 12b0f65130f..6aaedb6e5c6 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java @@ -56,8 +56,8 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher. private final ISession session; private final int bufferSize; private final ExecutionStrategy strategy; - private boolean useInputDirectBuffers; - private boolean useOutputDirectBuffers; + private boolean useInputDirectByteBuffers; + private boolean useOutputDirectByteBuffers; public HTTP2Connection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize) { @@ -103,22 +103,22 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher. public boolean isUseInputDirectByteBuffers() { - return useInputDirectBuffers; + return useInputDirectByteBuffers; } - public void setUseInputDirectByteBuffers(boolean useInputDirectBuffers) + public void setUseInputDirectByteBuffers(boolean useInputDirectByteBuffers) { - this.useInputDirectBuffers = useInputDirectBuffers; + this.useInputDirectByteBuffers = useInputDirectByteBuffers; } public boolean isUseOutputDirectByteBuffers() { - return useOutputDirectBuffers; + return useOutputDirectByteBuffers; } - public void setUseOutputDirectByteBuffers(boolean useOutputDirectBuffers) + public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers) { - this.useOutputDirectBuffers = useOutputDirectBuffers; + this.useOutputDirectByteBuffers = useOutputDirectByteBuffers; } @Override diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/Generator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/Generator.java index fb09eb57024..b71b136335b 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/Generator.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/Generator.java @@ -42,11 +42,11 @@ public class Generator this(byteBufferPool, true, maxDynamicTableSize, maxHeaderBlockFragment); } - public Generator(ByteBufferPool byteBufferPool, boolean directBuffers, int maxDynamicTableSize, int maxHeaderBlockFragment) + public Generator(ByteBufferPool byteBufferPool, boolean useDirectByteBuffers, int maxDynamicTableSize, int maxHeaderBlockFragment) { this.byteBufferPool = byteBufferPool; - headerGenerator = new HeaderGenerator(directBuffers); + headerGenerator = new HeaderGenerator(useDirectByteBuffers); hpackEncoder = new HpackEncoder(maxDynamicTableSize); this.generators = new FrameGenerator[FrameType.values().length]; diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeaderGenerator.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeaderGenerator.java index 16f9e017ab9..404fb6d72bc 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeaderGenerator.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/generator/HeaderGenerator.java @@ -27,21 +27,21 @@ import org.eclipse.jetty.io.ByteBufferPool; public class HeaderGenerator { private int maxFrameSize = Frame.DEFAULT_MAX_LENGTH; - private final boolean directBuffers; + private final boolean useDirectByteBuffers; public HeaderGenerator() { this(true); } - public HeaderGenerator(boolean directBuffers) + public HeaderGenerator(boolean useDirectByteBuffers) { - this.directBuffers = directBuffers; + this.useDirectByteBuffers = useDirectByteBuffers; } public boolean isUseDirectByteBuffers() { - return directBuffers; + return useDirectByteBuffers; } public ByteBuffer generate(ByteBufferPool.Lease lease, FrameType frameType, int capacity, int length, int flags, int streamId) diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java index 417ca7aefed..d8ff0c3ee62 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java @@ -64,8 +64,8 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne private RateControl rateControl = new WindowRateControl(20, Duration.ofSeconds(1)); private FlowControlStrategy.Factory flowControlStrategyFactory = () -> new BufferingFlowControlStrategy(0.5F); private long streamIdleTimeout; - private boolean _useInputDirectBuffers; - private boolean _useOutputDirectBuffers; + private boolean _useInputDirectByteBuffers; + private boolean _useOutputDirectByteBuffers; public AbstractHTTP2ServerConnectionFactory(@Name("config") HttpConfiguration httpConfiguration) { @@ -198,22 +198,22 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne public boolean isUseInputDirectByteBuffers() { - return _useInputDirectBuffers; + return _useInputDirectByteBuffers; } - public void setUseInputDirectByteBuffers(boolean useInputDirectBuffers) + public void setUseInputDirectByteBuffers(boolean useInputDirectByteBuffers) { - _useInputDirectBuffers = useInputDirectBuffers; + _useInputDirectByteBuffers = useInputDirectByteBuffers; } public boolean isUseOutputDirectByteBuffers() { - return _useOutputDirectBuffers; + return _useOutputDirectByteBuffers; } - public void setUseOutputDirectByteBuffers(boolean useOutputDirectBuffers) + public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers) { - _useOutputDirectBuffers = useOutputDirectBuffers; + _useOutputDirectByteBuffers = useOutputDirectByteBuffers; } public HttpConfiguration getHttpConfiguration() diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java index bef53fdab4b..93ca8fd6626 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java @@ -57,7 +57,7 @@ public class HttpChannelOverHTTP2 extends HttpChannel implements Closeable, Writ private boolean _expect100Continue; private boolean _delayedUntilContent; - private boolean _useOutputDirectBuffers; + private boolean _useOutputDirectByteBuffers; public HttpChannelOverHTTP2(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransportOverHTTP2 transport) { @@ -72,12 +72,12 @@ public class HttpChannelOverHTTP2 extends HttpChannel implements Closeable, Writ @Override public boolean isUseOutputDirectByteBuffers() { - return _useOutputDirectBuffers; + return _useOutputDirectByteBuffers; } - public void setUseOutputDirectByteBuffers(boolean useOutputDirectBuffers) + public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers) { - _useOutputDirectBuffers = useOutputDirectBuffers; + _useOutputDirectByteBuffers = useOutputDirectByteBuffers; } @Override diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index a33d842188d..3ad7cf25ec8 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -72,8 +72,8 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http private final boolean _recordHttpComplianceViolations; private final LongAdder bytesIn = new LongAdder(); private final LongAdder bytesOut = new LongAdder(); - private boolean _useInputDirectBuffers; - private boolean _useOutputDirectBuffers; + private boolean _useInputDirectByteBuffers; + private boolean _useOutputDirectByteBuffers; /** * Get the current connection that this thread is dispatched to. @@ -179,22 +179,22 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http public boolean isUseInputDirectByteBuffers() { - return _useInputDirectBuffers; + return _useInputDirectByteBuffers; } - public void setUseInputDirectByteBuffers(boolean useInputDirectBuffers) + public void setUseInputDirectByteBuffers(boolean useInputDirectByteBuffers) { - _useInputDirectBuffers = useInputDirectBuffers; + _useInputDirectByteBuffers = useInputDirectByteBuffers; } public boolean isUseOutputDirectByteBuffers() { - return _useOutputDirectBuffers; + return _useOutputDirectByteBuffers; } - public void setUseOutputDirectByteBuffers(boolean useOutputDirectBuffers) + public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers) { - _useOutputDirectBuffers = useOutputDirectBuffers; + _useOutputDirectByteBuffers = useOutputDirectByteBuffers; } @Override @@ -240,8 +240,8 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http { if (_requestBuffer == null) { - boolean useDirectBuffers = isUseInputDirectByteBuffers(); - _requestBuffer = _bufferPool.acquire(getInputBufferSize(), useDirectBuffers); + boolean useDirectByteBuffers = isUseInputDirectByteBuffers(); + _requestBuffer = _bufferPool.acquire(getInputBufferSize(), useDirectByteBuffers); } return _requestBuffer; } @@ -750,7 +750,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http if (_callback == null) throw new IllegalStateException(); - boolean useDirectBuffers = isUseOutputDirectByteBuffers(); + boolean useDirectByteBuffers = isUseOutputDirectByteBuffers(); ByteBuffer chunk = _chunk; while (true) { @@ -771,19 +771,19 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http case NEED_HEADER: { - _header = _bufferPool.acquire(_config.getResponseHeaderSize(), useDirectBuffers); + _header = _bufferPool.acquire(_config.getResponseHeaderSize(), useDirectByteBuffers); continue; } case NEED_CHUNK: { - chunk = _chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, useDirectBuffers); + chunk = _chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, useDirectByteBuffers); continue; } case NEED_CHUNK_TRAILER: { if (_chunk != null) _bufferPool.release(_chunk); - chunk = _chunk = _bufferPool.acquire(_config.getResponseHeaderSize(), useDirectBuffers); + chunk = _chunk = _bufferPool.acquire(_config.getResponseHeaderSize(), useDirectByteBuffers); continue; } case FLUSH: diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java index b29bdfc4bf9..fc078ab1d90 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java @@ -35,8 +35,8 @@ public class HttpConnectionFactory extends AbstractConnectionFactory implements { private final HttpConfiguration _config; private boolean _recordHttpComplianceViolations; - private boolean _useInputDirectBuffers; - private boolean _useOutputDirectBuffers; + private boolean _useInputDirectByteBuffers; + private boolean _useOutputDirectByteBuffers; public HttpConnectionFactory() { @@ -70,22 +70,22 @@ public class HttpConnectionFactory extends AbstractConnectionFactory implements public boolean isUseInputDirectByteBuffers() { - return _useInputDirectBuffers; + return _useInputDirectByteBuffers; } - public void setUseInputDirectByteBuffers(boolean useInputDirectBuffers) + public void setUseInputDirectByteBuffers(boolean useInputDirectByteBuffers) { - _useInputDirectBuffers = useInputDirectBuffers; + _useInputDirectByteBuffers = useInputDirectByteBuffers; } public boolean isUseOutputDirectByteBuffers() { - return _useOutputDirectBuffers; + return _useOutputDirectByteBuffers; } - public void setUseOutputDirectByteBuffers(boolean useOutputDirectBuffers) + public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers) { - _useOutputDirectBuffers = useOutputDirectBuffers; + _useOutputDirectByteBuffers = useOutputDirectByteBuffers; } @Override diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameFlusher.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameFlusher.java index d915d455f2c..a99e193d78f 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameFlusher.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameFlusher.java @@ -69,7 +69,7 @@ public class FrameFlusher extends IteratingCallback private boolean flushed = true; private Throwable closedCause; private long idleTimeout; - private boolean useDirectBuffers; + private boolean useDirectByteBuffers; public FrameFlusher(ByteBufferPool bufferPool, Scheduler scheduler, Generator generator, EndPoint endPoint, int bufferSize, int maxGather) { @@ -87,12 +87,12 @@ public class FrameFlusher extends IteratingCallback public boolean isUseDirectByteBuffers() { - return useDirectBuffers; + return useDirectByteBuffers; } - public void setUseDirectByteBuffers(boolean useDirectBuffers) + public void setUseDirectByteBuffers(boolean useDirectByteBuffers) { - this.useDirectBuffers = useDirectBuffers; + this.useDirectByteBuffers = useDirectByteBuffers; } /** diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java index 3dcc9a6244e..d0a7706c049 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java @@ -68,8 +68,8 @@ public class WebSocketConnection extends AbstractConnection implements Connectio // Read / Parse variables private RetainableByteBuffer networkBuffer; - private boolean useInputDirectBuffers; - private boolean useOutputDirectBuffers; + private boolean useInputDirectByteBuffers; + private boolean useOutputDirectByteBuffers; /** * Create a WSConnection. @@ -136,22 +136,22 @@ public class WebSocketConnection extends AbstractConnection implements Connectio public boolean isUseInputDirectByteBuffers() { - return useInputDirectBuffers; + return useInputDirectByteBuffers; } - public void setUseInputDirectByteBuffers(boolean useInputDirectBuffers) + public void setUseInputDirectByteBuffers(boolean useInputDirectByteBuffers) { - this.useInputDirectBuffers = useInputDirectBuffers; + this.useInputDirectByteBuffers = useInputDirectByteBuffers; } public boolean isUseOutputDirectByteBuffers() { - return useOutputDirectBuffers; + return useOutputDirectByteBuffers; } - public void setUseOutputDirectByteBuffers(boolean useOutputDirectBuffers) + public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers) { - this.useOutputDirectBuffers = useOutputDirectBuffers; + this.useOutputDirectByteBuffers = useOutputDirectByteBuffers; } /** From f10ff81d1cd18d8d3712aedbe7f3c620bd62390b Mon Sep 17 00:00:00 2001 From: Lachlan Date: Wed, 18 Sep 2019 10:42:32 +1000 Subject: [PATCH 084/113] Fix Secure javax.websocket Client examples (#4035) Signed-off-by: Lachlan Roberts --- .../javax-websocket-client/pom.xml | 6 ++++ .../SecureClientContainerExample.java | 15 ++++++--- .../SecureWebSocketContainerExample.java | 32 +++---------------- .../examples/jetty-websocket-httpclient.xml | 22 ------------- .../test/resources/jetty-logging.properties | 28 ++++++++++++++++ .../resources/jetty-websocket-httpclient.xml | 22 +++++++++++++ 6 files changed, 71 insertions(+), 54 deletions(-) rename jetty-websocket/{javax-websocket-client-impl => javax-websocket-client}/src/test/java/examples/SecureClientContainerExample.java (81%) delete mode 100644 jetty-websocket/javax-websocket-client/src/test/resources/examples/jetty-websocket-httpclient.xml create mode 100644 jetty-websocket/javax-websocket-client/src/test/resources/jetty-logging.properties create mode 100644 jetty-websocket/javax-websocket-client/src/test/resources/jetty-websocket-httpclient.xml diff --git a/jetty-websocket/javax-websocket-client/pom.xml b/jetty-websocket/javax-websocket-client/pom.xml index c4a74057925..bdbaea8cb23 100644 --- a/jetty-websocket/javax-websocket-client/pom.xml +++ b/jetty-websocket/javax-websocket-client/pom.xml @@ -29,6 +29,12 @@ org.eclipse.jetty.toolchain jetty-javax-websocket-api + + org.eclipse.jetty + jetty-xml + ${project.version} + test + org.eclipse.jetty.toolchain jetty-test-helper diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/examples/SecureClientContainerExample.java b/jetty-websocket/javax-websocket-client/src/test/java/examples/SecureClientContainerExample.java similarity index 81% rename from jetty-websocket/javax-websocket-client-impl/src/test/java/examples/SecureClientContainerExample.java rename to jetty-websocket/javax-websocket-client/src/test/java/examples/SecureClientContainerExample.java index dbba5d2365c..31b53627d70 100644 --- a/jetty-websocket/javax-websocket-client-impl/src/test/java/examples/SecureClientContainerExample.java +++ b/jetty-websocket/javax-websocket-client/src/test/java/examples/SecureClientContainerExample.java @@ -24,9 +24,11 @@ import javax.websocket.ClientEndpointConfig; import javax.websocket.WebSocketContainer; import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; +import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.ssl.SslContextFactory; -import org.eclipse.jetty.websocket.jsr356.ClientContainer; +import org.eclipse.jetty.websocket.javax.client.JavaxWebSocketClientContainer; public class SecureClientContainerExample { @@ -73,11 +75,14 @@ public class SecureClientContainerExample */ public static WebSocketContainer getConfiguredWebSocketContainer() throws Exception { - SslContextFactory ssl = new SslContextFactory.Client(); + SslContextFactory.Client ssl = new SslContextFactory.Client(); ssl.setExcludeCipherSuites(); // echo.websocket.org use WEAK cipher suites - HttpClient httpClient = new HttpClient(ssl); - ClientContainer clientContainer = new ClientContainer(httpClient); - clientContainer.getClient().addManaged(httpClient); // allow clientContainer to own httpClient (for start/stop lifecycle) + ClientConnector clientConnector = new ClientConnector(); + clientConnector.setSslContextFactory(ssl); + + HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP(clientConnector)); + JavaxWebSocketClientContainer clientContainer = new JavaxWebSocketClientContainer(httpClient); + clientContainer.addManaged(httpClient); // allow clientContainer to own httpClient (for start/stop lifecycle) clientContainer.start(); return clientContainer; } diff --git a/jetty-websocket/javax-websocket-client/src/test/java/examples/SecureWebSocketContainerExample.java b/jetty-websocket/javax-websocket-client/src/test/java/examples/SecureWebSocketContainerExample.java index b736f10a2e4..d8309eb9533 100644 --- a/jetty-websocket/javax-websocket-client/src/test/java/examples/SecureWebSocketContainerExample.java +++ b/jetty-websocket/javax-websocket-client/src/test/java/examples/SecureWebSocketContainerExample.java @@ -21,14 +21,12 @@ package examples; import java.io.FileNotFoundException; import java.net.URI; import java.net.URL; -import java.net.URLClassLoader; import java.util.concurrent.TimeUnit; import javax.websocket.ClientEndpointConfig; import javax.websocket.ContainerProvider; import javax.websocket.WebSocketContainer; import org.eclipse.jetty.util.component.LifeCycle; -import org.eclipse.jetty.util.thread.ThreadClassLoaderScope; public class SecureWebSocketContainerExample { @@ -69,47 +67,27 @@ public class SecureWebSocketContainerExample * Since javax.websocket does not have an API for configuring SSL, each implementation * of javax.websocket has to come up with their own SSL configuration mechanism. *

    - * When the call to {@link javax.websocket.ContainerProvider}.{@link ContainerProvider#getWebSocketContainer()} - * occurs, that needs to have a started and available WebSocket Client. - * Jetty's {@code WebSocketClient} must have a Jetty {@code HttpClient} started as well. - * If you want SSL, then that configuration has to be passed into the Jetty {@code HttpClient} at initialization. + * When the {@link WebSocketContainer} is used it will need to create and start a Jetty WebSocket Client. + * The {@code WebSocketClient} must use a Jetty {@code HttpClient} which can be configured for SSL, this + * configuration needs to be passed into the Jetty {@code HttpClient} at initialization. *

    *

    * How Jetty makes this available, is via the {@code jetty-websocket-httpclient.xml} classloader resource * along with the jetty-xml artifact. *

    - *

    - * This method will look for the file in the classloader resources, and then - * sets up a {@link URLClassLoader} to make that {@code jetty-websocket-httpclient.xml} available - * for this specific example. - * If we had put the `jetty-websocket-httpclient.xml` in the root of a JAR file loaded by this - * project then you can skip all of the classloader trickery this method performs. - *

    - * * @return the client WebSocketContainer * @see javax.websocket issue #210 */ public static WebSocketContainer getConfiguredWebSocketContainer() throws Exception { URL jettyHttpClientConfigUrl = Thread.currentThread().getContextClassLoader() - .getResource("examples/jetty-websocket-httpclient.xml"); + .getResource("jetty-websocket-httpclient.xml"); if (jettyHttpClientConfigUrl == null) { throw new FileNotFoundException("Unable to find Jetty HttpClient configuration XML"); } - URI jettyConfigDirUri = jettyHttpClientConfigUrl.toURI().resolve("./"); - - ClassLoader parentClassLoader = Thread.currentThread().getContextClassLoader(); - URL[] urls = new URL[]{ - jettyConfigDirUri.toURL() - }; - URLClassLoader classLoader = new URLClassLoader(urls, parentClassLoader); - - try (ThreadClassLoaderScope ignore = new ThreadClassLoaderScope(classLoader)) - { - return ContainerProvider.getWebSocketContainer(); - } + return ContainerProvider.getWebSocketContainer(); } } diff --git a/jetty-websocket/javax-websocket-client/src/test/resources/examples/jetty-websocket-httpclient.xml b/jetty-websocket/javax-websocket-client/src/test/resources/examples/jetty-websocket-httpclient.xml deleted file mode 100644 index 672007a92cd..00000000000 --- a/jetty-websocket/javax-websocket-client/src/test/resources/examples/jetty-websocket-httpclient.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - false - - - - TLS/1.3 - - - - - - - - - - - 5000 - diff --git a/jetty-websocket/javax-websocket-client/src/test/resources/jetty-logging.properties b/jetty-websocket/javax-websocket-client/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..e6624861732 --- /dev/null +++ b/jetty-websocket/javax-websocket-client/src/test/resources/jetty-logging.properties @@ -0,0 +1,28 @@ +# +# +# ======================================================================== +# Copyright (c) 1995-2017 Mort Bay Consulting 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. +# ======================================================================== +# +# +org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog +org.eclipse.jetty.LEVEL=INFO +# org.eclipse.jetty.websocket.LEVEL=DEBUG +# org.eclipse.jetty.server.AbstractConnector.LEVEL=DEBUG +# org.eclipse.jetty.io.WriteFlusher.LEVEL=DEBUG +# org.eclipse.jetty.io.FillInterest.LEVEL=DEBUG +# org.eclipse.jetty.client.LEVEL=DEBUG +# org.eclipse.jetty.io.LEVEL=DEBUG +# org.eclipse.jetty.io.ManagedSelector.LEVEL=INFO \ No newline at end of file diff --git a/jetty-websocket/javax-websocket-client/src/test/resources/jetty-websocket-httpclient.xml b/jetty-websocket/javax-websocket-client/src/test/resources/jetty-websocket-httpclient.xml new file mode 100644 index 00000000000..f2bab081984 --- /dev/null +++ b/jetty-websocket/javax-websocket-client/src/test/resources/jetty-websocket-httpclient.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 1b3ee6ee17d2653938eb01305f86f3b2311bd004 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 18 Sep 2019 10:50:53 +1000 Subject: [PATCH 085/113] Configuration cleanup Signed-off-by: Greg Wilkins --- .../maven/plugin/JettyWebAppContext.java | 2 +- .../quickstart/QuickStartConfiguration.java | 4 +- .../QuickStartGeneratorConfiguration.java | 2 +- .../jetty/webapp/AbstractConfiguration.java | 12 ++-- .../eclipse/jetty/webapp/Configuration.java | 8 +-- .../eclipse/jetty/webapp/Configurations.java | 2 +- .../eclipse/jetty/webapp/WebAppContext.java | 60 ++++++++++++------- .../jetty/webapp/WebAppContextTest.java | 10 ++-- .../jetty/test/DeploymentErrorTest.java | 6 +- 9 files changed, 62 insertions(+), 44 deletions(-) diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java index 8e765504b1e..f6a26bbf404 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java @@ -316,7 +316,7 @@ public class JettyWebAppContext extends WebAppContext try { // inject configurations with config from maven plugin - for (Configuration c : getWebAppConfigurations()) + for (Configuration c : getConfigurations()) { if (c instanceof EnvConfiguration && getJettyEnvXml() != null) ((EnvConfiguration)c).setJettyEnvResource(new PathResource(new File(getJettyEnvXml()))); diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java index 2dc417e42d8..bb1a9c3d345 100644 --- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java +++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java @@ -85,7 +85,7 @@ public class QuickStartConfiguration extends AbstractConfiguration public QuickStartConfiguration() { - super(false); + super(true); addDependencies(WebInfConfiguration.class); addDependents(WebXmlConfiguration.class); } @@ -208,7 +208,7 @@ public class QuickStartConfiguration extends AbstractConfiguration { LOG.info("Quickstarting {}", context); _quickStart = true; - context.setConfigurations(context.getWebAppConfigurations().stream() + context.setConfigurations(context.getConfigurations().stream() .filter(c -> !__replacedConfigurations.contains(c.replaces()) && !__replacedConfigurations.contains(c.getClass())) .collect(Collectors.toList()).toArray(new Configuration[]{})); context.getMetaData().setWebXml((Resource)context.getAttribute(QUICKSTART_WEB_XML)); diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartGeneratorConfiguration.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartGeneratorConfiguration.java index 753f32dce0d..5588f4b907b 100644 --- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartGeneratorConfiguration.java +++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartGeneratorConfiguration.java @@ -93,7 +93,7 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration public QuickStartGeneratorConfiguration(boolean abort) { - super(true); + super(false); _count = 0; _abort = abort; } diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/AbstractConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/AbstractConfiguration.java index fa357cb63c3..2ed1ede0717 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/AbstractConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/AbstractConfiguration.java @@ -26,7 +26,7 @@ import java.util.stream.Collectors; public class AbstractConfiguration implements Configuration { - private final boolean _disabledByDefault; + private final boolean _enabledByDefault; private final List _after = new ArrayList<>(); private final List _beforeThis = new ArrayList<>(); private final ClassMatcher _system = new ClassMatcher(); @@ -34,12 +34,12 @@ public class AbstractConfiguration implements Configuration protected AbstractConfiguration() { - this(false); + this(true); } - protected AbstractConfiguration(boolean disabledByDefault) + protected AbstractConfiguration(boolean enabledByDefault) { - _disabledByDefault = disabledByDefault; + _enabledByDefault = enabledByDefault; } /** @@ -196,9 +196,9 @@ public class AbstractConfiguration implements Configuration } @Override - public boolean isDisabledByDefault() + public boolean isEnabledByDefault() { - return _disabledByDefault; + return _enabledByDefault; } @Override diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configuration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configuration.java index 48e1bef7a26..ab1d90ae41c 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configuration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configuration.java @@ -34,12 +34,12 @@ import org.eclipse.jetty.util.TopologicalSort; *

    *

    Configuration instances are discovered by the {@link Configurations} class using either the * {@link ServiceLoader} mechanism or by an explicit call to {@link Configurations#setKnown(String...)}. - * By default, all Configurations that do not implement the {@link #isDisabledByDefault()} interface + * By default, all Configurations that do not return false from {@link #isEnabledByDefault()} * are applied to all {@link WebAppContext}s within the JVM. However a Server wide default {@link Configurations} * collection may also be defined with {@link Configurations#setServerDefault(org.eclipse.jetty.server.Server)}. * Furthermore, each individual Context may have its Configurations list explicitly set and/or amended with * {@link WebAppContext#setConfigurations(Configuration[])}, {@link WebAppContext#addConfiguration(Configuration...)} - * or {@link WebAppContext#getWebAppConfigurations()}. + * or {@link WebAppContext#getConfigurations()}. *

    *

    Since Jetty-9.4, Configurations are self ordering using the {@link #getDependencies()} and * {@link #getDependents()} methods for a {@link TopologicalSort} initiated by {@link Configurations#sort()} @@ -171,9 +171,9 @@ public interface Configuration void destroy(WebAppContext context) throws Exception; /** - * @return true if configuration is disabled by default + * @return true if configuration is enabled by default */ - boolean isDisabledByDefault(); + boolean isEnabledByDefault(); /** * @return true if configuration should be aborted diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configurations.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configurations.java index 9868d7f570a..5c2963ca937 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configurations.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configurations.java @@ -209,7 +209,7 @@ public class Configurations extends AbstractList implements Dumpa if (configurations == null) { configurations = new Configurations(Configurations.getKnown().stream() - .filter(c -> !c.isDisabledByDefault()) + .filter(c -> c.isEnabledByDefault()) .map(c -> c.getClass().getName()) .toArray(String[]::new)); } diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java index 786fcf1b1b2..ca79a191156 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java @@ -95,7 +95,7 @@ import org.eclipse.jetty.util.resource.ResourceCollection; *

      *
    • Add all Server class inclusions from all known configurations {@link Configurations#getKnown()}
    • *
    • {@link #loadConfigurations()}, which uses either explicitly set Configurations or takes the server - * default (which is all known non {@link Configuration#isDisabledByDefault()} Configurations.
    • + * default (which is all known {@link Configuration#isEnabledByDefault()} Configurations. *
    • Sort the configurations using {@link TopologicalSort} in {@link Configurations#sort()}.
    • *
    • Add all Server class exclusions from this webapps {@link Configurations}
    • *
    • Add all System classes inclusions and exclusions for this webapps {@link Configurations}
    • @@ -183,10 +183,10 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL "org.eclipse.jetty." // hide jetty classes ); - private final Configurations _configurations = new Configurations(); private final ClassMatcher _systemClasses = new ClassMatcher(__dftSystemClasses); private final ClassMatcher _serverClasses = new ClassMatcher(__dftServerClasses); + private Configurations _configurations; private String _defaultsDescriptor = WEB_DEFAULTS_XML; private String _descriptor = null; private final List _overrideDescriptors = new ArrayList<>(); @@ -570,18 +570,21 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL { // Prepare for configuration MultiException mx = new MultiException(); - for (Configuration configuration : _configurations) + if (_configurations != null) { - try + for (Configuration configuration : _configurations) { - configuration.destroy(this); - } - catch (Exception e) - { - mx.add(e); + try + { + configuration.destroy(this); + } + catch (Exception e) + { + mx.add(e); + } } } - _configurations.clear(); + _configurations = null; super.destroy(); mx.ifExceptionThrowRuntime(); } @@ -615,10 +618,9 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL /** * @return Returns the configurations. */ - public Configurations getWebAppConfigurations() + public Configurations getConfigurations() { - if (_configurations.size() == 0) - loadConfigurations(); + loadConfigurations(); return _configurations; } @@ -885,10 +887,18 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL protected void loadConfigurations() { //if the configuration instances have been set explicitly, use them - if (!_configurations.isEmpty()) + if (_configurations != null) return; + if (isStarted()) + throw new IllegalStateException(); + _configurations = newConfigurations(); + } - _configurations.add(Configurations.getServerDefault(getServer()).toArray()); + protected Configurations newConfigurations() + { + Configurations configurations = new Configurations(); + configurations.add(Configurations.getServerDefault(getServer()).toArray()); + return configurations; } @Override @@ -958,8 +968,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL */ public void setConfigurationClasses(String[] configurations) { - if (isStarted()) - throw new IllegalStateException(); + loadConfigurations(); _configurations.set(configurations); } @@ -973,15 +982,12 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL */ public void setConfigurations(Configuration[] configurations) { - if (isStarted()) - throw new IllegalStateException(); + loadConfigurations(); _configurations.set(configurations); } public void addConfiguration(Configuration... configuration) { - if (isStarted()) - throw new IllegalStateException(); loadConfigurations(); _configurations.add(configuration); } @@ -997,6 +1003,18 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL return null; } + public void removeConfiguration(Configuration... configurations) + { + loadConfigurations(); + _configurations.remove(configurations); + } + + public void removeConfiguration(Class... configurations) + { + loadConfigurations(); + _configurations.remove(configurations); + } + /** * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml * diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppContextTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppContextTest.java index 20e90ef451d..04f40e0f02d 100644 --- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppContextTest.java +++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppContextTest.java @@ -100,7 +100,7 @@ public class WebAppContextTest { Configurations.cleanKnown(); String[] known_and_enabled = Configurations.getKnown().stream() - .filter(c -> !c.isDisabledByDefault()) + .filter(c -> c.isEnabledByDefault()) .map(c -> c.getClass().getName()) .toArray(String[]::new); @@ -108,7 +108,7 @@ public class WebAppContextTest //test if no classnames set, its the defaults WebAppContext wac = new WebAppContext(); - assertThat(wac.getWebAppConfigurations().stream() + assertThat(wac.getConfigurations().stream() .map(c -> c.getClass().getName()) .collect(Collectors.toList()), Matchers.containsInAnyOrder(known_and_enabled)); @@ -126,7 +126,7 @@ public class WebAppContextTest Configurations.cleanKnown(); WebAppContext wac = new WebAppContext(); wac.setServer(new Server()); - assertThat(wac.getWebAppConfigurations().stream().map(c -> c.getClass().getName()).collect(Collectors.toList()), + assertThat(wac.getConfigurations().stream().map(c -> c.getClass().getName()).collect(Collectors.toList()), Matchers.contains( "org.eclipse.jetty.webapp.JmxConfiguration", "org.eclipse.jetty.webapp.WebInfConfiguration", @@ -144,14 +144,14 @@ public class WebAppContextTest Configuration[] configs = {new WebInfConfiguration()}; WebAppContext wac = new WebAppContext(); wac.setConfigurations(configs); - assertThat(wac.getWebAppConfigurations(), Matchers.contains(configs)); + assertThat(wac.getConfigurations(), Matchers.contains(configs)); //test that explicit config instances override any from server String[] classNames = {"x.y.z"}; Server server = new Server(); server.setAttribute(Configuration.ATTR, classNames); wac.setServer(server); - assertThat(wac.getWebAppConfigurations(), Matchers.contains(configs)); + assertThat(wac.getConfigurations(), Matchers.contains(configs)); } @Test diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/DeploymentErrorTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/DeploymentErrorTest.java index 64b462b2c3a..c7338965105 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/DeploymentErrorTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/DeploymentErrorTest.java @@ -193,7 +193,7 @@ public class DeploymentErrorTest assertThat("ContextHandler.isAvailable", context.isAvailable(), is(false)); WebAppContext webapp = (WebAppContext)context; TrackedConfiguration trackedConfiguration = null; - for (Configuration webappConfig : webapp.getWebAppConfigurations()) + for (Configuration webappConfig : webapp.getConfigurations()) { if (webappConfig instanceof TrackedConfiguration) trackedConfiguration = (TrackedConfiguration)webappConfig; @@ -239,7 +239,7 @@ public class DeploymentErrorTest assertThat("ContextHandler.isAvailable", context.isAvailable(), is(false)); WebAppContext webapp = (WebAppContext)context; TrackedConfiguration trackedConfiguration = null; - for (Configuration webappConfig : webapp.getWebAppConfigurations()) + for (Configuration webappConfig : webapp.getConfigurations()) { if (webappConfig instanceof TrackedConfiguration) trackedConfiguration = (TrackedConfiguration)webappConfig; @@ -285,7 +285,7 @@ public class DeploymentErrorTest assertThat("ContextHandler.isAvailable", context.isAvailable(), is(false)); WebAppContext webapp = (WebAppContext)context; TrackedConfiguration trackedConfiguration = null; - for (Configuration webappConfig : webapp.getWebAppConfigurations()) + for (Configuration webappConfig : webapp.getConfigurations()) { if (webappConfig instanceof TrackedConfiguration) trackedConfiguration = (TrackedConfiguration)webappConfig; From 289c70ec9b6d52993f9838475028397ddab87f35 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 18 Sep 2019 14:45:06 +1000 Subject: [PATCH 086/113] Issue #4003 Cleanup quickstart * cleanup from review Signed-off-by: Greg Wilkins --- .../org/eclipse/jetty/webapp/WebAppContext.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java index ca79a191156..f82f579bb9b 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java @@ -968,7 +968,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL */ public void setConfigurationClasses(String[] configurations) { - loadConfigurations(); + if (_configurations == null) + _configurations = new Configurations(); _configurations.set(configurations); } @@ -982,7 +983,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL */ public void setConfigurations(Configuration[] configurations) { - loadConfigurations(); + if (_configurations == null) + _configurations = new Configurations(); _configurations.set(configurations); } @@ -1005,14 +1007,14 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL public void removeConfiguration(Configuration... configurations) { - loadConfigurations(); - _configurations.remove(configurations); + if (_configurations != null) + _configurations.remove(configurations); } public void removeConfiguration(Class... configurations) { - loadConfigurations(); - _configurations.remove(configurations); + if (_configurations != null) + _configurations.remove(configurations); } /** From 157910d79a4a3554774292b767c70de334db27a6 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 18 Sep 2019 15:58:46 +1000 Subject: [PATCH 087/113] Issue #4084 make deprecated warnings only with debug in jetty-9 Signed-off-by: Greg Wilkins --- .../eclipse/jetty/xml/XmlConfiguration.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java index 46d38a08e1e..2d9bb367657 100644 --- a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java +++ b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java @@ -737,32 +737,36 @@ public class XmlConfiguration private Object invokeConstructor(Constructor constructor, Object... args) throws IllegalAccessException, InvocationTargetException, InstantiationException { Object result = constructor.newInstance(args); - if (constructor.getAnnotation(Deprecated.class) != null) - LOG.warn("Deprecated constructor {} in {}", constructor, _configuration); + if (LOG.isDebugEnabled()) + if (constructor.getAnnotation(Deprecated.class) != null) + LOG.warn("Deprecated constructor {} in {}", constructor, _configuration); return result; } private Object invokeMethod(Method method, Object obj, Object... args) throws IllegalAccessException, InvocationTargetException { Object result = method.invoke(obj, args); - if (method.getAnnotation(Deprecated.class) != null) - LOG.warn("Deprecated method {} in {}", method, _configuration); + if (LOG.isDebugEnabled()) + if (method.getAnnotation(Deprecated.class) != null) + LOG.warn("Deprecated method {} in {}", method, _configuration); return result; } private Object getField(Field field, Object object) throws IllegalAccessException { Object result = field.get(object); - if (field.getAnnotation(Deprecated.class) != null) - LOG.warn("Deprecated field {} in {}", field, _configuration); + if (LOG.isDebugEnabled()) + if (field.getAnnotation(Deprecated.class) != null) + LOG.warn("Deprecated field {} in {}", field, _configuration); return result; } private void setField(Field field, Object obj, Object arg) throws IllegalAccessException { field.set(obj, arg); - if (field.getAnnotation(Deprecated.class) != null) - LOG.warn("Deprecated field {} in {}", field, _configuration); + if (LOG.isDebugEnabled()) + if (field.getAnnotation(Deprecated.class) != null) + LOG.warn("Deprecated field {} in {}", field, _configuration); } /** From 6a32fb0ca5ad55ca4ea5e0d3fb8eb1d9391083d8 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 18 Sep 2019 16:29:19 +1000 Subject: [PATCH 088/113] Issue #4003 Cleanup quickstart * cleanup from review Signed-off-by: Greg Wilkins --- .../maven/plugin/JettyWebAppContext.java | 29 +++++++++++-------- .../eclipse/jetty/webapp/Configurations.java | 21 ++++++++++++++ .../eclipse/jetty/webapp/WebAppContext.java | 7 +---- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java index f6a26bbf404..302d267f00d 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java @@ -42,10 +42,10 @@ 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; -import org.eclipse.jetty.util.resource.PathResource; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceCollection; import org.eclipse.jetty.webapp.Configuration; +import org.eclipse.jetty.webapp.Configurations; import org.eclipse.jetty.webapp.MetaInfConfiguration; import org.eclipse.jetty.webapp.WebAppContext; @@ -310,22 +310,27 @@ public class JettyWebAppContext extends WebAppContext } @Override - protected void loadConfigurations() + protected Configurations newConfigurations() { - super.loadConfigurations(); - try + Configurations configurations = super.newConfigurations(); + if (getJettyEnvXml() != null) { - // inject configurations with config from maven plugin - for (Configuration c : getConfigurations()) + try { - if (c instanceof EnvConfiguration && getJettyEnvXml() != null) - ((EnvConfiguration)c).setJettyEnvResource(new PathResource(new File(getJettyEnvXml()))); + // inject configurations with config from maven plugin + for (Configuration c : configurations) + { + if (c instanceof EnvConfiguration) + ((EnvConfiguration)c).setJettyEnvResource(Resource.newResource(getJettyEnvXml())); + } + } + catch (IOException e) + { + throw new RuntimeException(e); } } - catch (Exception e) - { - throw new RuntimeException(e); - } + + return configurations; } @Override diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configurations.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configurations.java index 5c2963ca937..2839445e540 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configurations.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/Configurations.java @@ -279,6 +279,27 @@ public class Configurations extends AbstractList implements Dumpa } } + public T get(Class configClass) + { + for (Configuration configuration : _configurations) + { + if (configClass.isAssignableFrom(configuration.getClass())) + return (T)configuration; + } + return null; + } + + public List getConfigurations(Class configClass) + { + List list = new ArrayList<>(); + for (Configuration configuration : _configurations) + { + if (configClass.isAssignableFrom(configuration.getClass())) + list.add((T)configuration); + } + return list; + } + public void clear() { _configurations.clear(); diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java index f82f579bb9b..7bdb0e1a387 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java @@ -997,12 +997,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL public T getConfiguration(Class configClass) { loadConfigurations(); - for (Configuration configuration : _configurations) - { - if (configClass.isAssignableFrom(configuration.getClass())) - return (T)configuration; - } - return null; + return _configurations.get(configClass); } public void removeConfiguration(Configuration... configurations) From 69d52b226335be62389aaf63e763882845b00107 Mon Sep 17 00:00:00 2001 From: olivier lamy Date: Wed, 18 Sep 2019 09:54:52 -0400 Subject: [PATCH 089/113] fix unit test Signed-off-by: olivier lamy --- .../test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java index fa029e17008..6b911a49412 100644 --- a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java +++ b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java @@ -1075,6 +1075,7 @@ public class XmlConfigurationTest ByteArrayOutputStream logBytes = null; Logger logger = Log.getLogger(XmlConfiguration.class); + logger.setDebugEnabled(true); if (logger instanceof StdErrLog) { StdErrLog stdErrLog = (StdErrLog)logger; @@ -1084,6 +1085,7 @@ public class XmlConfigurationTest xmlConfiguration.configure(); + logger.setDebugEnabled(false); if (logBytes != null) { String[] lines = logBytes.toString(UTF_8.name()).split(System.lineSeparator()); From 2268983a3900d3f5ec5e3dabb0e8ee3f115a887c Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 18 Sep 2019 16:18:41 -0500 Subject: [PATCH 090/113] Revert "Issue #4084 make deprecated warnings only with debug in jetty-9" This reverts commit 157910d79a4a3554774292b767c70de334db27a6. --- .../eclipse/jetty/xml/XmlConfiguration.java | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java index 2d9bb367657..46d38a08e1e 100644 --- a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java +++ b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java @@ -737,36 +737,32 @@ public class XmlConfiguration private Object invokeConstructor(Constructor constructor, Object... args) throws IllegalAccessException, InvocationTargetException, InstantiationException { Object result = constructor.newInstance(args); - if (LOG.isDebugEnabled()) - if (constructor.getAnnotation(Deprecated.class) != null) - LOG.warn("Deprecated constructor {} in {}", constructor, _configuration); + if (constructor.getAnnotation(Deprecated.class) != null) + LOG.warn("Deprecated constructor {} in {}", constructor, _configuration); return result; } private Object invokeMethod(Method method, Object obj, Object... args) throws IllegalAccessException, InvocationTargetException { Object result = method.invoke(obj, args); - if (LOG.isDebugEnabled()) - if (method.getAnnotation(Deprecated.class) != null) - LOG.warn("Deprecated method {} in {}", method, _configuration); + if (method.getAnnotation(Deprecated.class) != null) + LOG.warn("Deprecated method {} in {}", method, _configuration); return result; } private Object getField(Field field, Object object) throws IllegalAccessException { Object result = field.get(object); - if (LOG.isDebugEnabled()) - if (field.getAnnotation(Deprecated.class) != null) - LOG.warn("Deprecated field {} in {}", field, _configuration); + if (field.getAnnotation(Deprecated.class) != null) + LOG.warn("Deprecated field {} in {}", field, _configuration); return result; } private void setField(Field field, Object obj, Object arg) throws IllegalAccessException { field.set(obj, arg); - if (LOG.isDebugEnabled()) - if (field.getAnnotation(Deprecated.class) != null) - LOG.warn("Deprecated field {} in {}", field, _configuration); + if (field.getAnnotation(Deprecated.class) != null) + LOG.warn("Deprecated field {} in {}", field, _configuration); } /** From 168a95d334ecc102ff505ecca4c9b47a152dee1a Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Thu, 19 Sep 2019 08:56:01 +1000 Subject: [PATCH 091/113] Issue #4096 - allow thread to exit ReservedThreadExecutor on stop Signed-off-by: Lachlan Roberts --- .../eclipse/jetty/util/thread/ReservedThreadExecutor.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java index f17271670d6..37173216848 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java @@ -163,6 +163,9 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec { if (_lease != null) _lease.close(); + + super.doStop(); + while (true) { ReservedThread thread = _stack.pollFirst(); @@ -171,7 +174,6 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec _size.decrementAndGet(); thread.stop(); } - super.doStop(); } @Override @@ -277,7 +279,7 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec LOG.debug("{} waiting", this); Runnable task = null; - while (task == null) + while (isRunning() && task == null) { boolean idle = false; From bb659e0e5825165d14bf9d0c95b9b519e21a2f6f Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 19 Sep 2019 13:25:53 -0500 Subject: [PATCH 092/113] Issue #4084 - squelch deprecated warning on call. + The XML warning on Deprecated will be squelched (set to DEBUG) if the call has a (or ) that is using its default value. All other uses will still result in WARN level logging event. Signed-off-by: Joakim Erdfelt --- .../eclipse/jetty/xml/XmlConfiguration.java | 90 +++++- .../jetty/xml/AnnotatedTestConfiguration.java | 18 ++ .../jetty/xml/XmlConfigurationTest.java | 296 ++++++++++++++++-- 3 files changed, 369 insertions(+), 35 deletions(-) diff --git a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java index 46d38a08e1e..db8fcf154cd 100644 --- a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java +++ b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java @@ -566,6 +566,7 @@ public class XmlConfiguration String attr = node.getAttribute("name"); String name = "set" + attr.substring(0, 1).toUpperCase(Locale.ENGLISH) + attr.substring(1); Object value = value(obj, node); + String defaultValue = defaultValue(obj, node); Object[] arg = {value}; Class oClass = nodeClass(node); @@ -578,8 +579,16 @@ public class XmlConfiguration if (value != null) vClass[0] = value.getClass(); + boolean isUsingDefaultValue = ((value != null) && (defaultValue.equalsIgnoreCase(value.toString()))); + if (LOG.isDebugEnabled()) - LOG.debug("XML " + (obj != null ? obj.toString() : oClass.getName()) + "." + name + "(" + value + ")"); + { + LOG.debug("XML {}.{}({}) [{}]", + (obj != null ? obj.toString() : oClass.getName()), + name, + value, + isUsingDefaultValue ? "DEFAULT" : "NEW"); + } MultiException me = new MultiException(); @@ -587,7 +596,7 @@ public class XmlConfiguration try { Method set = oClass.getMethod(name, vClass); - invokeMethod(set, obj, arg); + invokeMethod(set, obj, arg, isUsingDefaultValue); return; } catch (IllegalArgumentException | IllegalAccessException | NoSuchMethodException e) @@ -602,7 +611,7 @@ public class XmlConfiguration Field type = vClass[0].getField("TYPE"); vClass[0] = (Class)type.get(null); Method set = oClass.getMethod(name, vClass); - invokeMethod(set, obj, arg); + invokeMethod(set, obj, arg, isUsingDefaultValue); return; } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException | NoSuchMethodException e) @@ -619,7 +628,7 @@ public class XmlConfiguration { try { - setField(field, obj, value); + setField(field, obj, value, isUsingDefaultValue); return; } catch (IllegalArgumentException e) @@ -630,7 +639,7 @@ public class XmlConfiguration try { value = TypeUtil.valueOf(field.getType(), ((String)value).trim()); - setField(field, obj, value); + setField(field, obj, value, isUsingDefaultValue); return; } catch (Exception e2) @@ -664,7 +673,7 @@ public class XmlConfiguration try { set = setter; - invokeMethod(set, obj, arg); + invokeMethod(set, obj, arg, isUsingDefaultValue); return; } catch (IllegalArgumentException | IllegalAccessException e) @@ -679,7 +688,8 @@ public class XmlConfiguration { if (paramTypes[0].isAssignableFrom(c)) { - invokeMethod(setter, obj, convertArrayToCollection(value, c)); + Object[] args = {convertArrayToCollection(value, c)}; + invokeMethod(setter, obj, args, isUsingDefaultValue); return; } } @@ -712,7 +722,7 @@ public class XmlConfiguration Constructor cons = sClass.getConstructor(vClass); arg[0] = cons.newInstance(arg); _configuration.initializeDefaults(arg[0]); - invokeMethod(set, obj, arg); + invokeMethod(set, obj, arg, isUsingDefaultValue); return; } catch (NoSuchMethodException | IllegalAccessException | InstantiationException e) @@ -742,11 +752,21 @@ public class XmlConfiguration return result; } - private Object invokeMethod(Method method, Object obj, Object... args) throws IllegalAccessException, InvocationTargetException + private Object invokeMethod(Method method, Object obj, Object[] args) throws IllegalAccessException, InvocationTargetException + { + return invokeMethod(method, obj, args, false); + } + + private Object invokeMethod(Method method, Object obj, Object[] args, boolean isUsingDefaultValue) throws IllegalAccessException, InvocationTargetException { Object result = method.invoke(obj, args); if (method.getAnnotation(Deprecated.class) != null) - LOG.warn("Deprecated method {} in {}", method, _configuration); + { + if (isUsingDefaultValue) + LOG.debug("Deprecated method {} in {}", method, _configuration); + else + LOG.warn("Deprecated method {} in {}", method, _configuration); + } return result; } @@ -758,11 +778,16 @@ public class XmlConfiguration return result; } - private void setField(Field field, Object obj, Object arg) throws IllegalAccessException + private void setField(Field field, Object obj, Object arg, boolean isUsingDefaultValue) throws IllegalAccessException { field.set(obj, arg); if (field.getAnnotation(Deprecated.class) != null) - LOG.warn("Deprecated field {} in {}", field, _configuration); + { + if (isUsingDefaultValue) + LOG.debug("Deprecated field {} in {}", field, _configuration); + else + LOG.warn("Deprecated field {} in {}", field, _configuration); + } } /** @@ -851,7 +876,7 @@ public class XmlConfiguration { // Try calling a getXxx method. Method method = oClass.getMethod("get" + name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1)); - obj = invokeMethod(method, obj); + obj = invokeMethod(method, obj, null, false); } if (id != null) _configuration.getIdMap().put(id, obj); @@ -1394,6 +1419,45 @@ public class XmlConfiguration return value; } + /** + * Check children for all {@code } and {@code } and return + * the String representation of any declared {@code default="value"} attributes. + * + * @param obj the enclosing obj + * @param node the XML node + * @return a String representing all {@code } and {@code } values appended together + */ + private String defaultValue(Object obj, XmlParser.Node node) throws Exception + { + StringBuilder ret = new StringBuilder(); + + appendDefaultPropertyValues(ret, node); + + return ret.toString(); + } + + private void appendDefaultPropertyValues(StringBuilder defValues, XmlParser.Node node) throws Exception + { + for (Object child : node) + { + if (child instanceof XmlParser.Node) + { + XmlParser.Node childNode = (XmlParser.Node)child; + String tag = childNode.getTag(); + if ("Property".equals(tag) || "SystemProperty".equals(tag)) + { + AttrOrElementNode aoeNode = new AttrOrElementNode(childNode, "Id", "Name", "Deprecated", "Default"); + String dftValue = aoeNode.getString("Default"); + if (dftValue != null) + { + defValues.append(dftValue); + } + } + appendDefaultPropertyValues(defValues, childNode); + } + } + } + /** *

      Returns the scalar value of an element

      . *

      If no value type is specified, then white space is trimmed out of the value. diff --git a/jetty-xml/src/test/java/org/eclipse/jetty/xml/AnnotatedTestConfiguration.java b/jetty-xml/src/test/java/org/eclipse/jetty/xml/AnnotatedTestConfiguration.java index 9d1ff60db16..32796563ece 100644 --- a/jetty-xml/src/test/java/org/eclipse/jetty/xml/AnnotatedTestConfiguration.java +++ b/jetty-xml/src/test/java/org/eclipse/jetty/xml/AnnotatedTestConfiguration.java @@ -27,6 +27,10 @@ public class AnnotatedTestConfiguration private String third; private String deprecated; private AnnotatedTestConfiguration nested; + + // Do not remove deprecation, used in tests. + @Deprecated + private long timeout = -1; // Do not remove deprecation, used in tests. @Deprecated public String obsolete; @@ -97,4 +101,18 @@ public class AnnotatedTestConfiguration { return deprecated; } + + // Do not remove deprecation, used in tests. + @Deprecated + public long getTimeout() + { + return timeout; + } + + // Do not remove deprecation, used in tests. + @Deprecated + public void setTimeout(long value) + { + this.timeout = value; + } } diff --git a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java index 6b911a49412..0f062989031 100644 --- a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java +++ b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java @@ -22,15 +22,18 @@ import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; +import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; import java.util.stream.Collectors; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; @@ -1061,45 +1064,294 @@ public class XmlConfigurationTest } @Test - public void testDeprecated() throws Exception + public void testDeprecatedMany() throws Exception { Class testClass = AnnotatedTestConfiguration.class; XmlConfiguration xmlConfiguration = asXmlConfiguration( "" + " foo" + + " " + " " + " " + " " + " " + ""); - ByteArrayOutputStream logBytes = null; - Logger logger = Log.getLogger(XmlConfiguration.class); - logger.setDebugEnabled(true); - if (logger instanceof StdErrLog) + List logLines; + try (StdErrCapture logCapture = new StdErrCapture(XmlConfiguration.class)) { - StdErrLog stdErrLog = (StdErrLog)logger; - logBytes = new ByteArrayOutputStream(); - stdErrLog.setStdErrStream(new PrintStream(logBytes)); + xmlConfiguration.getProperties().put("test.timeout", "-1"); + xmlConfiguration.configure(); + logLines = logCapture.getLines(); } - xmlConfiguration.configure(); + logLines.forEach(System.err::println); // dump capture logs - logger.setDebugEnabled(false); - if (logBytes != null) + List warnings = logLines.stream() + .filter(line -> line.contains(":WARN:")) + .filter(line -> line.contains(testClass.getSimpleName())) + .collect(Collectors.toList()); + warnings.forEach(System.out::println); // dump warnings captured + // 1. Deprecated constructor + // 2. Deprecated method + // 3. Deprecated method + // 4. Deprecated method + // 5. Deprecated field + // 6. Deprecated field + assertEquals(6, warnings.size()); + } + + @Test + public void testDeprecatedPropertyUnSet() throws Exception + { + Class testClass = AnnotatedTestConfiguration.class; + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " " + + ""); + assertDeprecatedPropertyUnSet(testClass, xmlConfiguration); + } + + @Test + public void testDeprecatedPropertyUnSetWhiteSpace() throws Exception + { + Class testClass = AnnotatedTestConfiguration.class; + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " " + + " " + + " " + + ""); + assertDeprecatedPropertyUnSet(testClass, xmlConfiguration); + } + + private void assertDeprecatedPropertyUnSet(Class testClass, XmlConfiguration xmlConfiguration) throws Exception + { + List logLines; + try (StdErrCapture logCapture = new StdErrCapture(XmlConfiguration.class)) { + // Leave this line alone, as this tests what happens if property is unset, + // so that it relies on the value + // xmlConfiguration.getProperties().put("test.timeout", "-1"); + xmlConfiguration.configure(); + logLines = logCapture.getLines(); + } + + logLines.forEach(System.err::println); // dump capture logs + + List warnings = logLines.stream() + .filter(LogPredicates.deprecatedWarnings(testClass)) + .collect(Collectors.toList()); + // warnings.forEach(System.out::println); // dump warnings captured + String[] expected = { + "Deprecated constructor public org.eclipse.jetty.xml.AnnotatedTestConfiguration" + }; + + assertHasExpectedLines("Warnings", warnings, expected); + } + + @Test + public void testDeprecatedPropertySetToDefaultValue() throws Exception + { + Class testClass = AnnotatedTestConfiguration.class; + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " " + + ""); + + assertDeprecatedPropertySetToDefaultValue(testClass, xmlConfiguration); + } + + @Test + public void testDeprecatedPropertySetToDefaultValueWhiteSpace() throws Exception + { + Class testClass = AnnotatedTestConfiguration.class; + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " " + + " " + + " " + + ""); + + assertDeprecatedPropertySetToDefaultValue(testClass, xmlConfiguration); + } + + private void assertDeprecatedPropertySetToDefaultValue(Class testClass, XmlConfiguration xmlConfiguration) throws Exception + { + List logLines; + try (StdErrCapture logCapture = new StdErrCapture(XmlConfiguration.class)) + { + // Leave this line alone, as this tests what happens if property is set, + // and has the same value as declared on + xmlConfiguration.getProperties().put("test.timeout", "-1"); + xmlConfiguration.configure(); + logLines = logCapture.getLines(); + } + + logLines.forEach(System.err::println); // dump capture logs + + List warnings = logLines.stream() + .filter(LogPredicates.deprecatedWarnings(testClass)) + .collect(Collectors.toList()); + warnings.forEach(System.out::println); // dump captured WARN + + String[] expected = { + "Deprecated constructor public org.eclipse.jetty.xml.AnnotatedTestConfiguration", + }; + assertHasExpectedLines("Warnings", warnings, expected); + + List debugs = logLines.stream() + .filter(LogPredicates.deprecatedDebug(testClass)) + .collect(Collectors.toList()); + + expected = new String[]{ + "Deprecated method public void org.eclipse.jetty.xml.AnnotatedTestConfiguration.setTimeout(long)" + }; + + debugs.forEach(System.out::println); // dump captured DEBUG + assertHasExpectedLines("Debugs", debugs, expected); + } + + @Test + public void testDeprecatedPropertySetToNewValue() throws Exception + { + Class testClass = AnnotatedTestConfiguration.class; + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " " + + ""); + + List logLines; + try (StdErrCapture logCapture = new StdErrCapture(XmlConfiguration.class)) + { + // Leave this line alone, as this tests what happens if property is set, + // and has the same value as declared on + xmlConfiguration.getProperties().put("test.timeout", "30000"); + xmlConfiguration.configure(); + logLines = logCapture.getLines(); + } + + logLines.forEach(System.err::println); // dump capture logs + + List warnings = logLines.stream() + .filter(LogPredicates.deprecatedWarnings(testClass)) + .collect(Collectors.toList()); + // warnings.forEach(System.out::println); // dump warnings captured + String[] expected = { + "Deprecated constructor public org.eclipse.jetty.xml.AnnotatedTestConfiguration", + "Deprecated method public void org.eclipse.jetty.xml.AnnotatedTestConfiguration.setTimeout(long)" + }; + assertThat("Count of warnings", warnings.size(), is(expected.length)); + for (int i = 0; i < expected.length; i++) + { + assertThat("Warning[" + i + "]", warnings.get(i), containsString(expected[i])); + } + } + + @Test + public void testSetDeprecatedMultipleProperties() throws Exception + { + Class testClass = AnnotatedTestConfiguration.class; + XmlConfiguration xmlConfiguration = asXmlConfiguration( + "" + + " " + + " " + + " " + + " " + + ""); + + List logLines; + try (StdErrCapture logCapture = new StdErrCapture(XmlConfiguration.class)) + { + // Leave this line alone, as this tests what happens if property is set, + // and has the same value as declared on + // xmlConfiguration.getProperties().put("test.timeout", "30000"); + xmlConfiguration.configure(); + logLines = logCapture.getLines(); + } + + logLines.forEach(System.err::println); // dump capture logs + + List warnings = logLines.stream() + .filter(LogPredicates.deprecatedWarnings(testClass)) + .collect(Collectors.toList()); + // warnings.forEach(System.out::println); // dump warnings captured + String[] expected = { + "Deprecated constructor public org.eclipse.jetty.xml.AnnotatedTestConfiguration", + "Deprecated field public java.lang.String org.eclipse.jetty.xml.AnnotatedTestConfiguration.obsolete" + }; + assertThat("Count of warnings", warnings.size(), is(expected.length)); + for (int i = 0; i < expected.length; i++) + { + assertThat("Warning[" + i + "]", warnings.get(i), containsString(expected[i])); + } + } + + private static class LogPredicates + { + public static Predicate deprecatedWarnings(Class testClass) + { + return (line) -> line.contains(":WARN:") && + line.contains(": Deprecated ") && + line.contains(testClass.getName()); + } + + public static Predicate deprecatedDebug(Class testClass) + { + return (line) -> line.contains(":DBUG:") && + line.contains(": Deprecated ") && + line.contains(testClass.getName()); + } + } + + private void assertHasExpectedLines(String type, List actualLines, String[] expectedLines) + { + assertThat("Count of " + type, actualLines.size(), is(expectedLines.length)); + for (int i = 0; i < expectedLines.length; i++) + { + assertThat(type + "[" + i + "]", actualLines.get(i), containsString(expectedLines[i])); + } + } + + private static class StdErrCapture implements AutoCloseable + { + private ByteArrayOutputStream logBytes; + private List loggers = new ArrayList<>(); + private final PrintStream logStream; + + public StdErrCapture(Class... classes) + { + for (Class clazz : classes) + { + Logger logger = Log.getLogger(clazz); + loggers.add(logger); + } + + logBytes = new ByteArrayOutputStream(); + logStream = new PrintStream(logBytes); + + loggers.forEach((logger) -> + { + logger.setDebugEnabled(true); + if (logger instanceof StdErrLog) + { + StdErrLog stdErrLog = (StdErrLog)logger; + stdErrLog.setStdErrStream(logStream); + } + }); + } + + public List getLines() throws UnsupportedEncodingException + { + logStream.flush(); String[] lines = logBytes.toString(UTF_8.name()).split(System.lineSeparator()); - List warnings = Arrays.stream(lines) - .filter(line -> line.contains(":WARN:")) - .filter(line -> line.contains(testClass.getSimpleName())) - .collect(Collectors.toList()); - // 1. Deprecated constructor - // 2. Deprecated method - // 3. Deprecated method - // 4. Deprecated method - // 5. Deprecated field - // 6. Deprecated field - assertEquals(6, warnings.size()); + return Arrays.asList(lines); + } + + @Override + public void close() + { + loggers.forEach((logger) -> logger.setDebugEnabled(false)); } } } From 8e20f23b5260dedad6ae086a671331883985e396 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 16 Sep 2019 14:42:52 -0500 Subject: [PATCH 093/113] Removing aggregate javadoc, as it's incompatible with release on JDK 9+ --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a2b8c15143c..a6e68ce953c 100644 --- a/pom.xml +++ b/pom.xml @@ -272,7 +272,7 @@ true false - javadoc:aggregate-jar deploy + deploy -Peclipse-release clean install forked-path From 5a52235464917eadc473b9744708ace623971805 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Fri, 20 Sep 2019 10:30:31 +1000 Subject: [PATCH 094/113] Issue #4104 - WebSocketSession will reject outgoing frames if closed Outgoing frames will now go RemoteEndpoint->Session->ExtensionStack instead of just RemoteEndpoint->ExtensionStack. This will allow the Session to check whether it has been closed before allowing the frame through the ExtensionStack. Signed-off-by: Lachlan Roberts --- .../jetty/websocket/tests/EventSocket.java | 2 +- .../websocket/tests/WriteAfterStopTest.java | 15 +++------- .../client/WebSocketUpgradeRequest.java | 4 +-- .../client/io/WebSocketClientConnection.java | 20 ------------- .../websocket/common/WebSocketSession.java | 28 +++++++++++++++++-- .../io/AbstractWebSocketConnection.java | 13 +++++++++ .../server/WebSocketServerConnection.java | 23 +-------------- 7 files changed, 46 insertions(+), 59 deletions(-) diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/EventSocket.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/EventSocket.java index 3671e3ccf86..8b5c5220536 100644 --- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/EventSocket.java +++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/EventSocket.java @@ -37,7 +37,7 @@ public class EventSocket { private static Logger LOG = Log.getLogger(EventSocket.class); - protected Session session; + public Session session; private String behavior; public volatile Throwable failure = null; public volatile int closeCode = -1; diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WriteAfterStopTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WriteAfterStopTest.java index 6aca5cbe627..79bf0f40099 100644 --- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WriteAfterStopTest.java +++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WriteAfterStopTest.java @@ -19,20 +19,17 @@ package org.eclipse.jetty.websocket.tests; import java.net.URI; -import java.nio.channels.ClosedChannelException; import java.util.concurrent.TimeUnit; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.util.log.StacklessLogging; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.StatusCode; +import org.eclipse.jetty.websocket.api.WebSocketException; import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; import org.eclipse.jetty.websocket.client.WebSocketClient; -import org.eclipse.jetty.websocket.common.WebSocketSession; -import org.eclipse.jetty.websocket.common.extensions.compress.CompressExtension; import org.eclipse.jetty.websocket.servlet.WebSocketServlet; import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; import org.junit.jupiter.api.AfterEach; @@ -105,12 +102,8 @@ public class WriteAfterStopTest assertThat(clientSocket.closeCode, is(StatusCode.NORMAL)); assertThat(serverSocket.closeCode, is(StatusCode.NORMAL)); - ((WebSocketSession)session).stop(); - - try (StacklessLogging stacklessLogging = new StacklessLogging(CompressExtension.class)) - { - assertThrows(ClosedChannelException.class, - () -> session.getRemote().sendString("hello world")); - } + WebSocketException failure = assertThrows(WebSocketException.class, () -> + clientSocket.session.getRemote().sendString("this should fail before ExtensionStack")); + assertThat(failure.getMessage(), is("Session closed")); } } \ No newline at end of file diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketUpgradeRequest.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketUpgradeRequest.java index c8b6259e019..62867c658c1 100644 --- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketUpgradeRequest.java +++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketUpgradeRequest.java @@ -601,7 +601,6 @@ public class WebSocketUpgradeRequest extends HttpRequest implements CompleteList session.setUpgradeResponse(new ClientUpgradeResponse(response)); connection.addListener(session); - ExtensionStack extensionStack = new ExtensionStack(getExtensionFactory()); List extensions = new ArrayList<>(); HttpField extField = response.getHeaders().getField(HttpHeader.SEC_WEBSOCKET_EXTENSIONS); if (extField != null) @@ -619,8 +618,9 @@ public class WebSocketUpgradeRequest extends HttpRequest implements CompleteList } } } - extensionStack.negotiate(extensions); + ExtensionStack extensionStack = new ExtensionStack(getExtensionFactory()); + extensionStack.negotiate(extensions); extensionStack.configure(connection.getParser()); extensionStack.configure(connection.getGenerator()); diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientConnection.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientConnection.java index b249eb4f332..780a7edba48 100644 --- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientConnection.java +++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/io/WebSocketClientConnection.java @@ -18,7 +18,6 @@ package org.eclipse.jetty.websocket.client.io; -import java.net.InetSocketAddress; import java.util.concurrent.Executor; import org.eclipse.jetty.io.ByteBufferPool; @@ -28,7 +27,6 @@ import org.eclipse.jetty.websocket.api.BatchMode; import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.api.WriteCallback; import org.eclipse.jetty.websocket.api.extensions.Frame; -import org.eclipse.jetty.websocket.api.extensions.IncomingFrames; import org.eclipse.jetty.websocket.client.masks.Masker; import org.eclipse.jetty.websocket.client.masks.RandomMasker; import org.eclipse.jetty.websocket.common.WebSocketFrame; @@ -47,18 +45,6 @@ public class WebSocketClientConnection extends AbstractWebSocketConnection this.masker = new RandomMasker(); } - @Override - public InetSocketAddress getLocalAddress() - { - return getEndPoint().getLocalAddress(); - } - - @Override - public InetSocketAddress getRemoteAddress() - { - return getEndPoint().getRemoteAddress(); - } - /** * Override to set the masker. */ @@ -71,10 +57,4 @@ public class WebSocketClientConnection extends AbstractWebSocketConnection } super.outgoingFrame(frame, callback, batchMode); } - - @Override - public void setNextIncomingFrames(IncomingFrames incoming) - { - getParser().setIncomingFramesHandler(incoming); - } } diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java index 700ba25a55c..ef7ccc94161 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java @@ -48,7 +48,9 @@ import org.eclipse.jetty.websocket.api.StatusCode; import org.eclipse.jetty.websocket.api.SuspendToken; import org.eclipse.jetty.websocket.api.UpgradeRequest; import org.eclipse.jetty.websocket.api.UpgradeResponse; +import org.eclipse.jetty.websocket.api.WebSocketException; import org.eclipse.jetty.websocket.api.WebSocketPolicy; +import org.eclipse.jetty.websocket.api.WriteCallback; import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory; import org.eclipse.jetty.websocket.api.extensions.Frame; import org.eclipse.jetty.websocket.api.extensions.IncomingFrames; @@ -59,7 +61,7 @@ import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope; import org.eclipse.jetty.websocket.common.scopes.WebSocketSessionScope; @ManagedObject("A Jetty WebSocket Session") -public class WebSocketSession extends ContainerLifeCycle implements Session, RemoteEndpointFactory, WebSocketSessionScope, IncomingFrames, Connection.Listener +public class WebSocketSession extends ContainerLifeCycle implements Session, RemoteEndpointFactory, WebSocketSessionScope, IncomingFrames, OutgoingFrames, Connection.Listener { private static final Logger LOG = Log.getLogger(WebSocketSession.class); private final WebSocketContainerScope containerScope; @@ -334,6 +336,26 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem } } + @Override + public void outgoingFrame(Frame frame, WriteCallback callback, BatchMode batchMode) + { + if (onCloseCalled.get()) + { + try + { + if (callback != null) + callback.writeFailed(new WebSocketException("Session closed")); + } + catch (Throwable x) + { + LOG.debug("Exception while notifying failure of callback " + callback, x); + } + return; + } + + outgoingHandler.outgoingFrame(frame, callback, batchMode); + } + @Override public boolean isOpen() { @@ -420,7 +442,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem @Override public WebSocketRemoteEndpoint newRemoteEndpoint(LogicalConnection connection, OutgoingFrames outgoingFrames, BatchMode batchMode) { - return new WebSocketRemoteEndpoint(connection, outgoingHandler, getBatchMode()); + return new WebSocketRemoteEndpoint(connection, outgoingFrames, getBatchMode()); } /** @@ -443,7 +465,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem if (connection.opening()) { // Connect remote - remote = remoteEndpointFactory.newRemoteEndpoint(connection, outgoingHandler, getBatchMode()); + remote = remoteEndpointFactory.newRemoteEndpoint(connection, this, getBatchMode()); if (LOG.isDebugEnabled()) LOG.debug("[{}] {}.open() remote={}", policy.getBehavior(), this.getClass().getSimpleName(), remote); diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java index 5c872db308b..87d5f3de069 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/AbstractWebSocketConnection.java @@ -49,6 +49,7 @@ import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.api.WriteCallback; import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig; import org.eclipse.jetty.websocket.api.extensions.Frame; +import org.eclipse.jetty.websocket.api.extensions.IncomingFrames; import org.eclipse.jetty.websocket.common.CloseInfo; import org.eclipse.jetty.websocket.common.Generator; import org.eclipse.jetty.websocket.common.LogicalConnection; @@ -392,6 +393,12 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp return this.policy; } + @Override + public InetSocketAddress getLocalAddress() + { + return getEndPoint().getLocalAddress(); + } + @Override public InetSocketAddress getRemoteAddress() { @@ -649,6 +656,12 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp setInitialBuffer(prefilled); } + @Override + public void setNextIncomingFrames(IncomingFrames incoming) + { + getParser().setIncomingFramesHandler(incoming); + } + /** * @return the number of WebSocket frames received over this connection */ diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerConnection.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerConnection.java index 4662b24a11c..7c085b89951 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerConnection.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerConnection.java @@ -18,7 +18,6 @@ package org.eclipse.jetty.websocket.server; -import java.net.InetSocketAddress; import java.util.concurrent.Executor; import org.eclipse.jetty.io.ByteBufferPool; @@ -26,7 +25,6 @@ import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.thread.Scheduler; import org.eclipse.jetty.websocket.api.WebSocketPolicy; -import org.eclipse.jetty.websocket.api.extensions.IncomingFrames; import org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection; public class WebSocketServerConnection extends AbstractWebSocketConnection implements Connection.UpgradeTo @@ -34,27 +32,8 @@ public class WebSocketServerConnection extends AbstractWebSocketConnection imple public WebSocketServerConnection(EndPoint endp, Executor executor, Scheduler scheduler, WebSocketPolicy policy, ByteBufferPool bufferPool) { super(endp, executor, scheduler, policy, bufferPool); + if (policy.getIdleTimeout() > 0) - { endp.setIdleTimeout(policy.getIdleTimeout()); - } - } - - @Override - public InetSocketAddress getLocalAddress() - { - return getEndPoint().getLocalAddress(); - } - - @Override - public InetSocketAddress getRemoteAddress() - { - return getEndPoint().getRemoteAddress(); - } - - @Override - public void setNextIncomingFrames(IncomingFrames incoming) - { - getParser().setIncomingFramesHandler(incoming); } } From ebe28c27e446ec40395512bebc28ff3b2bc00a70 Mon Sep 17 00:00:00 2001 From: Marc-Olivier Fleury Date: Fri, 20 Sep 2019 14:26:49 +0200 Subject: [PATCH 095/113] using computeIfAbsent to avoid the duplicate registering of Destinations in the MBean Server Signed-off-by: Marc-Olivier Fleury --- .../org/eclipse/jetty/client/HttpClient.java | 24 ++++++------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java index 82b418f6911..fe57f3241c4 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java @@ -540,24 +540,14 @@ public class HttpClient extends ContainerLifeCycle port = normalizePort(scheme, port); Origin origin = new Origin(scheme, host, port); - HttpDestination destination = destinations.get(origin); - if (destination == null) - { - destination = transport.newHttpDestination(origin); - addManaged(destination); - HttpDestination existing = destinations.putIfAbsent(origin, destination); - if (existing != null) - { - removeBean(destination); - destination = existing; + return destinations.computeIfAbsent(origin, o -> { + HttpDestination newDestination = getTransport().newHttpDestination(o); + addManaged(newDestination); + if (LOG.isDebugEnabled()) { + LOG.debug("Created {}", newDestination); } - else - { - if (LOG.isDebugEnabled()) - LOG.debug("Created {}", destination); - } - } - return destination; + return newDestination; + }); } protected boolean removeDestination(HttpDestination destination) From a7856dd6d07557ff39836bb8cc8bdb51fed38480 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 20 Sep 2019 17:13:10 -0500 Subject: [PATCH 096/113] Issue #4084 - Removing System.err|out usage in tests Signed-off-by: Joakim Erdfelt --- .../jetty/xml/XmlConfigurationTest.java | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java index 0f062989031..f00ad2389a3 100644 --- a/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java +++ b/jetty-xml/src/test/java/org/eclipse/jetty/xml/XmlConfigurationTest.java @@ -1085,13 +1085,10 @@ public class XmlConfigurationTest logLines = logCapture.getLines(); } - logLines.forEach(System.err::println); // dump capture logs - List warnings = logLines.stream() .filter(line -> line.contains(":WARN:")) .filter(line -> line.contains(testClass.getSimpleName())) .collect(Collectors.toList()); - warnings.forEach(System.out::println); // dump warnings captured // 1. Deprecated constructor // 2. Deprecated method // 3. Deprecated method @@ -1137,12 +1134,9 @@ public class XmlConfigurationTest logLines = logCapture.getLines(); } - logLines.forEach(System.err::println); // dump capture logs - List warnings = logLines.stream() .filter(LogPredicates.deprecatedWarnings(testClass)) .collect(Collectors.toList()); - // warnings.forEach(System.out::println); // dump warnings captured String[] expected = { "Deprecated constructor public org.eclipse.jetty.xml.AnnotatedTestConfiguration" }; @@ -1188,12 +1182,9 @@ public class XmlConfigurationTest logLines = logCapture.getLines(); } - logLines.forEach(System.err::println); // dump capture logs - List warnings = logLines.stream() .filter(LogPredicates.deprecatedWarnings(testClass)) .collect(Collectors.toList()); - warnings.forEach(System.out::println); // dump captured WARN String[] expected = { "Deprecated constructor public org.eclipse.jetty.xml.AnnotatedTestConfiguration", @@ -1208,7 +1199,6 @@ public class XmlConfigurationTest "Deprecated method public void org.eclipse.jetty.xml.AnnotatedTestConfiguration.setTimeout(long)" }; - debugs.forEach(System.out::println); // dump captured DEBUG assertHasExpectedLines("Debugs", debugs, expected); } @@ -1231,12 +1221,9 @@ public class XmlConfigurationTest logLines = logCapture.getLines(); } - logLines.forEach(System.err::println); // dump capture logs - List warnings = logLines.stream() .filter(LogPredicates.deprecatedWarnings(testClass)) .collect(Collectors.toList()); - // warnings.forEach(System.out::println); // dump warnings captured String[] expected = { "Deprecated constructor public org.eclipse.jetty.xml.AnnotatedTestConfiguration", "Deprecated method public void org.eclipse.jetty.xml.AnnotatedTestConfiguration.setTimeout(long)" @@ -1265,17 +1252,14 @@ public class XmlConfigurationTest { // Leave this line alone, as this tests what happens if property is set, // and has the same value as declared on - // xmlConfiguration.getProperties().put("test.timeout", "30000"); + // xmlConfiguration.getProperties().put("obs.1", "30000"); xmlConfiguration.configure(); logLines = logCapture.getLines(); } - logLines.forEach(System.err::println); // dump capture logs - List warnings = logLines.stream() .filter(LogPredicates.deprecatedWarnings(testClass)) .collect(Collectors.toList()); - // warnings.forEach(System.out::println); // dump warnings captured String[] expected = { "Deprecated constructor public org.eclipse.jetty.xml.AnnotatedTestConfiguration", "Deprecated field public java.lang.String org.eclipse.jetty.xml.AnnotatedTestConfiguration.obsolete" From ac93ff6272d9dc8b19df30d575c35287abfefd15 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Mon, 23 Sep 2019 11:56:25 +1000 Subject: [PATCH 097/113] Issue #4096 resolve race between doStop and adding new threads to stack Now using the _size AtomicInteger to resolve the race between stopping and adding to the stack. A _size of -1 now means no more threads can be added to the stack, and the loop in doStop() will now only exit if it can set the size to -1. Signed-off-by: Lachlan Roberts --- .../util/thread/ReservedThreadExecutor.java | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java index 37173216848..a8a27ad4bce 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java @@ -155,6 +155,7 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec public void doStart() throws Exception { _lease = ThreadPoolBudget.leaseFrom(getExecutor(), this, _capacity); + _size.set(0); super.doStart(); } @@ -168,9 +169,21 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec while (true) { + int size = _size.get(); + // If no reserved threads left try setting size to -1 to + // atomically prevent other threads adding themselves to stack. + if (size == 0 && _size.compareAndSet(size, -1)) + break; + ReservedThread thread = _stack.pollFirst(); if (thread == null) - break; + { + // Reserved thread must have incremented size but not yet added itself to queue. + // We will spin until it is added. + Thread.yield(); // TODO: use Thread.onSpinWait() in jetty-10 + continue; + } + _size.decrementAndGet(); thread.stop(); } @@ -278,11 +291,12 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec if (LOG.isDebugEnabled()) LOG.debug("{} waiting", this); - Runnable task = null; - while (isRunning() && task == null) + while (true) { - boolean idle = false; + if (!isRunning()) + return STOP; + boolean idle = false; try (Locker.Lock lock = _locker.lock()) { if (_task == null) @@ -299,8 +313,16 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec LOG.ignore(e); } } - task = _task; - _task = null; + else + { + Runnable task = _task; + _task = null; + + if (LOG.isDebugEnabled()) + LOG.debug("{} task={}", this, task); + + return task; + } } if (idle) @@ -315,11 +337,6 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec tryExecute(STOP); } } - - if (LOG.isDebugEnabled()) - LOG.debug("{} task={}", this, task); - - return task; } @Override @@ -332,6 +349,8 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec while (true) { int size = _size.get(); + if (size < 0) + return; if (size >= _capacity) { if (LOG.isDebugEnabled()) From a6b1922473abc186111f85f4dbe43988aeae17e7 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 23 Sep 2019 10:30:35 -0500 Subject: [PATCH 098/113] Adding JDK13 to build Signed-off-by: Joakim Erdfelt --- Jenkinsfile | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Jenkinsfile b/Jenkinsfile index 554ea76bf1f..d08cc7ff520 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -63,6 +63,17 @@ pipeline { } } + stage("Build / Test - JDK13") { + agent { node { label 'linux' } } + steps { + timeout(time: 120, unit: 'MINUTES') { + mavenBuild("jdk13", "-Pmongodb install", "maven3", true) + warnings consoleParsers: [[parserName: 'Maven'], [parserName: 'Java']] + junit testResults: '**/target/surefire-reports/*.xml,**/target/invoker-reports/TEST*.xml' + } + } + } + stage("Build Javadoc") { agent { node { label 'linux' } } steps { From b3be28086b33d264966d83dd59d7a8750d1e87af Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 23 Sep 2019 17:59:37 +0200 Subject: [PATCH 099/113] Fixes #4113 - HttpClient fails with JDK 13 and TLS 1.3. 1. Now forwarding the fillable event rather than assuming that is due to garbage bytes or by a server close. This ensures that a HTTP read consumes the TLS bytes and the `NewSessionTicket` message. 2. Avoid to set the `SslConnection` onto the `EndPoint` in `SslClientConnectionFactory` - this allows upgrades to work properly, for example when tunnelling through a secure proxy. Signed-off-by: Simone Bordet --- .../jetty/client/http/HttpConnectionOverHTTP.java | 12 +----------- .../jetty/io/ssl/SslClientConnectionFactory.java | 1 - 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java index 85c56b31d0e..c81530703ac 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java @@ -148,17 +148,7 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec @Override public void onFillable() { - HttpExchange exchange = channel.getHttpExchange(); - if (exchange != null) - { - channel.receive(); - } - else - { - // If there is no exchange, then could be either a remote close, - // or garbage bytes; in both cases we close the connection - close(); - } + channel.receive(); } @Override diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java index 9feb0e07140..236d4397f78 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java @@ -97,7 +97,6 @@ public class SslClientConnectionFactory implements ClientConnectionFactory context.put(SSL_ENGINE_CONTEXT_KEY, engine); SslConnection sslConnection = newSslConnection(byteBufferPool, executor, endPoint, engine); - endPoint.setConnection(sslConnection); EndPoint appEndPoint = sslConnection.getDecryptedEndPoint(); appEndPoint.setConnection(connectionFactory.newConnection(appEndPoint, context)); From 7413483fc5f48d8b7300c16296165f4aef9a61d5 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 24 Sep 2019 15:35:00 -0500 Subject: [PATCH 100/113] Removing JDK12 build Signed-off-by: Joakim Erdfelt --- Jenkinsfile | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index d08cc7ff520..b75d5340a1c 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -52,17 +52,6 @@ pipeline { } } - stage("Build / Test - JDK12") { - agent { node { label 'linux' } } - steps { - timeout(time: 120, unit: 'MINUTES') { - mavenBuild("jdk12", "-Pmongodb install", "maven3", true) - warnings consoleParsers: [[parserName: 'Maven'], [parserName: 'Java']] - junit testResults: '**/target/surefire-reports/*.xml,**/target/invoker-reports/TEST*.xml' - } - } - } - stage("Build / Test - JDK13") { agent { node { label 'linux' } } steps { From 11b60db4c36d3bf294bacfb16872588101a265dd Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 25 Sep 2019 12:13:56 +1000 Subject: [PATCH 101/113] Issue #4047 Graceful Write (#4100) Added test to reproduce issue Fixed bug from #2772 where output was shutdown on DONE without checking for END. Fixed aggregation logic to aggregate last write if aggregation already started Improved comments and clarify conditions Signed-off-by: Greg Wilkins --- .../org/eclipse/jetty/http/HttpGenerator.java | 2 +- .../eclipse/jetty/server/HttpConnection.java | 10 ++- .../org/eclipse/jetty/server/HttpOutput.java | 9 +- .../jetty/server/GracefulStopTest.java | 86 +++++++++++++++++++ .../jetty/util/component/Graceful.java | 24 +++++- 5 files changed, 121 insertions(+), 10 deletions(-) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java index e23bb1bad04..e772d39ea67 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java @@ -78,7 +78,7 @@ public class HttpGenerator FLUSH, // The buffers previously generated should be flushed CONTINUE, // Continue generating the message SHUTDOWN_OUT, // Need EOF to be signaled - DONE // Message generation complete + DONE // The current phase of generation is complete } // other statics diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index 80c31516e00..4e80aab1150 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -732,9 +732,9 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http { HttpGenerator.Result result = _generator.generateResponse(_info, _head, _header, chunk, _content, _lastContent); if (LOG.isDebugEnabled()) - LOG.debug("{} generate: {} ({},{},{})@{}", - this, + LOG.debug("generate: {} for {} ({},{},{})@{}", result, + this, BufferUtil.toSummaryString(_header), BufferUtil.toSummaryString(_content), _lastContent, @@ -826,8 +826,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http } case DONE: { - // If shutdown after commit, we can still close here. - if (getConnector().isShutdown()) + // If this is the end of the response and the connector was shutdown after response was committed, + // we can't add the Connection:close header, but we are still allowed to close the connection + // by shutting down the output. + if (getConnector().isShutdown() && _generator.isEnd() && _generator.isPersistent()) _shutdownOut = true; return Action.SUCCEEDED; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java index 655e8cd5dbd..3da6c658ce6 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java @@ -591,17 +591,18 @@ public class HttpOutput extends ServletOutputStream implements Runnable // handle blocking write // Should we aggregate? - int capacity = getBufferSize(); + // Yes - if the write is smaller than the commitSize (==aggregate buffer size) + // and the write is not the last one, or is last but will fit in an already allocated aggregate buffer. boolean last = isLastContentToWrite(len); - if (!last && len <= _commitSize) + if (len <= _commitSize && (!last || len <= BufferUtil.space(_aggregate))) { acquireBuffer(); // YES - fill the aggregate with content from the buffer int filled = BufferUtil.fill(_aggregate, b, off, len); - // return if we are not complete, not full and filled all the content - if (filled == len && !BufferUtil.isFull(_aggregate)) + // return if we are not the last write and have aggregated all of the content + if (!last && filled == len && !BufferUtil.isFull(_aggregate)) return; // adjust offset/length diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/GracefulStopTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/GracefulStopTest.java index 3b365ac95e9..fd889fcbfbe 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/GracefulStopTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/GracefulStopTest.java @@ -56,11 +56,13 @@ import org.junit.jupiter.api.condition.DisabledOnOs; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.startsWith; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -166,6 +168,66 @@ public class GracefulStopTest client.close(); } + + /** + * Test completed writes during shutdown do not close output + * @throws Exception on test failure + */ + @Test + public void testWriteDuringShutdown() throws Exception + { + Server server = new Server(); + server.setStopTimeout(1000); + + ServerConnector connector = new ServerConnector(server); + connector.setPort(0); + server.addConnector(connector); + + ABHandler handler = new ABHandler(); + StatisticsHandler stats = new StatisticsHandler(); + server.setHandler(stats); + stats.setHandler(handler); + + server.start(); + + Thread stopper = new Thread(() -> + { + try + { + handler.latchA.await(); + server.stop(); + } + catch (Exception e) + { + e.printStackTrace(); + } + }); + stopper.start(); + + final int port = connector.getLocalPort(); + try(Socket client = new Socket("127.0.0.1", port)) + { + client.getOutputStream().write(( + "GET / HTTP/1.1\r\n" + + "Host: localhost:" + port + "\r\n" + + "\r\n" + ).getBytes()); + client.getOutputStream().flush(); + + while (!connector.isShutdown()) + Thread.sleep(10); + + handler.latchB.countDown(); + + String response = IO.toString(client.getInputStream()); + assertThat(response, startsWith("HTTP/1.1 200 ")); + assertThat(response, containsString("Content-Length: 2")); + assertThat(response, containsString("Connection: close")); + assertThat(response, endsWith("ab")); + } + stopper.join(); + } + /** * Test of standard graceful timeout mechanism when a block request does * complete. Note that even though the request completes after 100ms, the @@ -736,6 +798,30 @@ public class GracefulStopTest } } + static class ABHandler extends AbstractHandler + { + final CountDownLatch latchA = new CountDownLatch(1); + final CountDownLatch latchB = new CountDownLatch(1); + + @Override + public void handle(String s, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException + { + response.setContentLength(2); + response.getOutputStream().write("a".getBytes()); + try + { + latchA.countDown(); + latchB.await(); + } + catch (InterruptedException e) + { + throw new RuntimeException(e); + } + response.flushBuffer(); + response.getOutputStream().write("b".getBytes()); + } + } + static class TestHandler extends AbstractHandler { final CountDownLatch latch = new CountDownLatch(1); diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Graceful.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Graceful.java index 541f39933f6..7408e042e17 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Graceful.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Graceful.java @@ -21,9 +21,25 @@ package org.eclipse.jetty.util.component; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicReference; +import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.FutureCallback; -/* A Lifecycle that can be gracefully shutdown. +/** + *

      Jetty components that wish to be part of a Graceful shutdown implement this interface so that + * the {@link Graceful#shutdown()} method will be called to initiate a shutdown. Shutdown operations + * can fall into the following categories:

      + *
        + *
      • Preventing new load from being accepted (eg connectors stop accepting connections)
      • + *
      • Preventing existing load expanding (eg stopping existing connections accepting new requests)
      • + *
      • Waiting for existing load to complete (eg waiting for active request count to reduce to 0)
      • + *
      • Performing cleanup operations that may take time (eg closing an SSL connection)
      • + *
      + *

      The {@link Future} returned by the the shutdown call will be completed to indicate the shutdown operation is completed. + * Some shutdown operations may be instantaneous and always return a completed future. + *

      + * Graceful shutdown is typically orchestrated by the doStop methods of Server or ContextHandler (for a full or partial + * shutdown respectively). + *

      */ public interface Graceful { @@ -31,6 +47,12 @@ public interface Graceful boolean isShutdown(); + /** + * A utility Graceful that uses a {@link FutureCallback} to indicate if shutdown is completed. + * By default the {@link FutureCallback} is returned as already completed, but the {@link #newShutdownCallback()} method + * can be overloaded to return a non-completed callback that will require a {@link Callback#succeeded()} or + * {@link Callback#failed(Throwable)} call to be completed. + */ class Shutdown implements Graceful { private final AtomicReference _shutdown = new AtomicReference<>(); From 3edc6c9102effadbf73e647a344e2e6df1fbfa9a Mon Sep 17 00:00:00 2001 From: Lachlan Date: Wed, 25 Sep 2019 14:55:13 +1000 Subject: [PATCH 102/113] Issue #3734 - throw ISE for WebSocket suspend after close (jetty-9.4) (#4098) * Issue #3734 - throw ISE for WebSocket suspend after close Signed-off-by: Lachlan Roberts * Issue #3734 - suspend is error if onClose() has been called Signed-off-by: Lachlan Roberts --- .../jetty/websocket/tests/SuspendResumeTest.java | 6 +++--- .../jetty/websocket/common/WebSocketSession.java | 11 ++++++----- .../eclipse/jetty/websocket/common/io/ReadState.java | 10 ++-------- .../jetty/websocket/common/io/ReadStateTest.java | 9 ++++----- 4 files changed, 15 insertions(+), 21 deletions(-) diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/SuspendResumeTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/SuspendResumeTest.java index 184d57aa2c6..fbfdeca6f88 100644 --- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/SuspendResumeTest.java +++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/SuspendResumeTest.java @@ -41,6 +41,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; public class SuspendResumeTest @@ -195,8 +196,7 @@ public class SuspendResumeTest assertThat(clientSocket.closeCode, is(StatusCode.NORMAL)); assertThat(serverSocket.closeCode, is(StatusCode.NORMAL)); - // suspend the client so that no read events occur - SuspendToken suspendToken = clientSocket.session.suspend(); - suspendToken.resume(); + // suspend after closed throws ISE + assertThrows(IllegalStateException.class, () -> clientSocket.session.suspend()); } } diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java index 700ba25a55c..c4dc03c7a58 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSession.java @@ -68,7 +68,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem private final EventDriver websocket; private final Executor executor; private final WebSocketPolicy policy; - private final AtomicBoolean closed = new AtomicBoolean(); + private final AtomicBoolean onCloseCalled = new AtomicBoolean(false); private ClassLoader classLoader; private ExtensionFactory extensionFactory; private RemoteEndpointFactory remoteEndpointFactory; @@ -80,7 +80,6 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem private UpgradeRequest upgradeRequest; private UpgradeResponse upgradeResponse; private CompletableFuture openFuture; - private AtomicBoolean onCloseCalled = new AtomicBoolean(false); public WebSocketSession(WebSocketContainerScope containerScope, URI requestURI, EventDriver websocket, LogicalConnection connection) { @@ -338,10 +337,9 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem public boolean isOpen() { if (this.connection == null) - { return false; - } - return !closed.get() && this.connection.isOpen(); + + return !onCloseCalled.get() && this.connection.isOpen(); } @Override @@ -546,6 +544,9 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Rem @Override public SuspendToken suspend() { + if (onCloseCalled.get()) + throw new IllegalStateException("Not open"); + return connection.suspend(); } diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/ReadState.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/ReadState.java index e80937700d5..20752a50a14 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/ReadState.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/io/ReadState.java @@ -87,10 +87,8 @@ class ReadState /** * Requests that reads from the connection be suspended. - * - * @return whether the suspending was successful */ - boolean suspending() + void suspending() { synchronized (this) { @@ -101,9 +99,7 @@ class ReadState { case READING: state = State.SUSPENDING; - return true; - case EOF: - return false; + break; default: throw new IllegalStateException(toString(state)); } @@ -131,8 +127,6 @@ class ReadState ByteBuffer bb = buffer; buffer = null; return bb; - case EOF: - return null; default: throw new IllegalStateException(toString(state)); } diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/ReadStateTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/ReadStateTest.java index 5fdc72e7e77..ea27653275d 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/ReadStateTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/ReadStateTest.java @@ -27,7 +27,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; public class ReadStateTest { @@ -50,7 +49,7 @@ public class ReadStateTest ReadState readState = new ReadState(); assertThat("Initially reading", readState.isReading(), is(true)); - assertTrue(readState.suspending()); + readState.suspending(); assertThat("Suspending doesn't take effect immediately", readState.isSuspended(), is(false)); assertNull(readState.resume()); @@ -64,7 +63,7 @@ public class ReadStateTest ReadState readState = new ReadState(); assertThat("Initially reading", readState.isReading(), is(true)); - assertThat(readState.suspending(), is(true)); + readState.suspending(); assertThat("Suspending doesn't take effect immediately", readState.isSuspended(), is(false)); ByteBuffer content = BufferUtil.toBuffer("content"); @@ -84,8 +83,8 @@ public class ReadStateTest assertThat(readState.isReading(), is(false)); assertThat(readState.isSuspended(), is(true)); - assertThat(readState.suspending(), is(false)); + assertThrows(IllegalStateException.class, readState::suspending); assertThat(readState.getAction(content), is(ReadState.Action.EOF)); - assertNull(readState.resume()); + assertThrows(IllegalStateException.class, readState::resume); } } From f6ca4f138ac90d35bc9bda55ff73034c994273b8 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 25 Sep 2019 12:42:32 -0500 Subject: [PATCH 103/113] Testing gpg signature setup --- CONTRIBUTING.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9391cc18ab2..ef6433b76c8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -53,10 +53,10 @@ Be sure to search for existing bugs before you create another one. Remember that Reporting Security Issues ----------------- -There are a number of avenues for reporting security issues to the Jetty project available. -If the issue is directly related to Jetty itself then reporting to the Jetty developers is encouraged. -The most direct method is to mail [security@webtide.com](mailto:security@webtide.com). -Webtide is comprised of the active committers of the Jetty project is our preferred reporting method. +There are a number of avenues for reporting security issues to the Jetty project available. +If the issue is directly related to Jetty itself then reporting to the Jetty developers is encouraged. +The most direct method is to mail [security@webtide.com](mailto:security@webtide.com). +Webtide is comprised of the active committers of the Jetty project is our preferred reporting method. We are flexible in how we work with reporters of security issues but we reserve the right to act in the interests of the Jetty project in all circumstances. If the issue is related to Eclipse or its Jetty integration then we encourage you to reach out to [security@eclipse.org](mailto:security@eclipse.org). From ba728eee5d9e8cfd3b7449b1246546ca2306bb25 Mon Sep 17 00:00:00 2001 From: Lachlan Date: Thu, 26 Sep 2019 08:15:07 +1000 Subject: [PATCH 104/113] Issue #4105 - atomically increment idle count when starting new thread in QTP (#4118) * Issue #4105 - starting a thread in QTP now increments idle count Signed-off-by: Lachlan Roberts * Issue #4105 - improve comments in test Signed-off-by: Lachlan Roberts --- .../jetty/util/thread/QueuedThreadPool.java | 21 ++-- .../util/thread/QueuedThreadPoolTest.java | 112 ++++++++++++++++++ 2 files changed, 120 insertions(+), 13 deletions(-) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java index 41f02b80e8b..6a110badd5d 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/QueuedThreadPool.java @@ -176,7 +176,6 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP removeBean(_tryExecutor); _tryExecutor = TryExecutor.NO_TRY; - // Signal the Runner threads that we are stopping int threads = _counts.getAndSetHi(Integer.MIN_VALUE); @@ -483,7 +482,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP public void execute(Runnable job) { // Determine if we need to start a thread, use and idle thread or just queue this job - boolean startThread; + int startThread; while (true) { // Get the atomic counts @@ -501,10 +500,10 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP // Start a thread if we have insufficient idle threads to meet demand // and we are not at max threads. - startThread = (idle <= 0 && threads < _maxThreads); + startThread = (idle <= 0 && threads < _maxThreads) ? 1 : 0; // The job will be run by an idle thread when available - if (!_counts.compareAndSet(counts, threads + (startThread ? 1 : 0), idle - 1)) + if (!_counts.compareAndSet(counts, threads + startThread, idle + startThread - 1)) continue; break; @@ -513,7 +512,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP if (!_jobs.offer(job)) { // reverse our changes to _counts. - if (addCounts(startThread ? -1 : 0, 1)) + if (addCounts(-startThread, 1 - startThread)) LOG.warn("{} rejected {}", this, job); throw new RejectedExecutionException(job.toString()); } @@ -522,7 +521,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP LOG.debug("queue {} startThread={}", job, startThread); // Start a thread if one was needed - if (startThread) + while (startThread-- > 0) startThread(); } @@ -617,7 +616,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP if (threads < _minThreads || (idle < 0 && threads < _maxThreads)) { // Then try to start a thread. - if (_counts.compareAndSet(counts, threads + 1, idle)) + if (_counts.compareAndSet(counts, threads + 1, idle + 1)) startThread(); // Otherwise continue to check state again. continue; @@ -645,7 +644,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP finally { if (!started) - addCounts(-1, 0); // threads, idle + addCounts(-1, -1); // threads, idle } } @@ -859,13 +858,8 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP LOG.debug("Runner started for {}", QueuedThreadPool.this); Runnable job = null; - try { - // All threads start idle (not yet taken a job) - if (!addCounts(0, 1)) - return; - while (true) { // If we had a job, signal that we are idle again @@ -873,6 +867,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP { if (!addCounts(0, 1)) break; + job = null; } // else check we are still running else if (_counts.getHi() == Integer.MIN_VALUE) diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java index edcd78a8137..892addbdeff 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/QueuedThreadPoolTest.java @@ -45,6 +45,61 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest private static final Logger LOG = Log.getLogger(QueuedThreadPoolTest.class); private final AtomicInteger _jobs = new AtomicInteger(); + private static class TestQueuedThreadPool extends QueuedThreadPool + { + private final AtomicInteger _started; + private final CountDownLatch _enteredRemoveThread; + private final CountDownLatch _exitRemoveThread; + + public TestQueuedThreadPool(AtomicInteger started, CountDownLatch enteredRemoveThread, CountDownLatch exitRemoveThread) + { + _started = started; + _enteredRemoveThread = enteredRemoveThread; + _exitRemoveThread = exitRemoveThread; + } + + public void superStartThread() + { + super.startThread(); + } + + @Override + protected void startThread() + { + switch (_started.incrementAndGet()) + { + case 1: + case 2: + case 3: + super.startThread(); + break; + + case 4: + // deliberately not start thread + break; + + default: + throw new IllegalStateException("too many threads started"); + } + } + + @Override + protected void removeThread(Thread thread) + { + try + { + _enteredRemoveThread.countDown(); + _exitRemoveThread.await(); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + + super.removeThread(thread); + } + } + private class RunningJob implements Runnable { final CountDownLatch _run = new CountDownLatch(1); @@ -450,6 +505,63 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest tp.stop(); } + @Test + public void testEnsureThreads() throws Exception + { + AtomicInteger started = new AtomicInteger(0); + + CountDownLatch enteredRemoveThread = new CountDownLatch(1); + CountDownLatch exitRemoveThread = new CountDownLatch(1); + TestQueuedThreadPool tp = new TestQueuedThreadPool(started, enteredRemoveThread, exitRemoveThread); + + tp.setMinThreads(2); + tp.setMaxThreads(10); + tp.setIdleTimeout(400); + tp.setThreadsPriority(Thread.NORM_PRIORITY - 1); + + tp.start(); + waitForIdle(tp, 2); + waitForThreads(tp, 2); + + RunningJob job1 = new RunningJob(); + RunningJob job2 = new RunningJob(); + RunningJob job3 = new RunningJob(); + tp.execute(job1); + tp.execute(job2); + tp.execute(job3); + + waitForThreads(tp, 3); + waitForIdle(tp, 0); + + // We stop job3, the thread becomes idle, thread decides to shrink, and then blocks in removeThread(). + job3.stop(); + assertTrue(enteredRemoveThread.await(5, TimeUnit.SECONDS)); + waitForThreads(tp, 3); + waitForIdle(tp, 1); + + // Executing job4 will not start a new thread because we already have 1 idle thread. + RunningJob job4 = new RunningJob(); + tp.execute(job4); + + // Allow thread to exit from removeThread(). + // The 4th thread is not actually started in our startThread() until tp.superStartThread() is called. + // Delay by 1000ms to check that ensureThreads is only starting one thread even though it is slow to start. + assertThat(started.get(), is(3)); + exitRemoveThread.countDown(); + Thread.sleep(1000); + + // Now startThreads() should have been called 4 times. + // Actually start the thread, and job4 should be run. + assertThat(started.get(), is(4)); + tp.superStartThread(); + assertTrue(job4._run.await(5, TimeUnit.SECONDS)); + + job1.stop(); + job2.stop(); + job4.stop(); + tp.stop(); + } + @Test public void testMaxStopTime() throws Exception { From d12df30df7fe812e1b9537d6c8b3e666df0ea133 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Thu, 26 Sep 2019 18:18:13 +0200 Subject: [PATCH 105/113] Issue #3787 - Jetty client throws EOFException instead of SSLHandshakeException on certificate errors. Code cleanups. Signed-off-by: Simone Bordet --- .../main/java/org/eclipse/jetty/client/HttpReceiver.java | 5 ++++- .../main/java/org/eclipse/jetty/client/HttpSender.java | 5 ++++- .../org/eclipse/jetty/client/http/HttpSenderOverHTTP.java | 8 +++++++- jetty-io/src/test/resources/jetty-logging.properties | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java index 991768f1121..b053fbb0dd6 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java @@ -410,6 +410,9 @@ public abstract class HttpReceiver if (exchange == null) return false; + if (LOG.isDebugEnabled()) + LOG.debug("Response failure " + exchange.getResponse(), failure); + // Mark atomically the response as completed, with respect // to concurrency between response success and response failure. if (exchange.responseComplete(failure)) @@ -514,7 +517,7 @@ public abstract class HttpReceiver HttpResponse response = exchange.getResponse(); if (LOG.isDebugEnabled()) - LOG.debug("Response failure {} {} on {}: {}", response, exchange, getHttpChannel(), failure); + LOG.debug("Response abort {} {} on {}: {}", response, exchange, getHttpChannel(), failure); List listeners = exchange.getConversation().getResponseListeners(); ResponseNotifier notifier = getHttpDestination().getResponseNotifier(); notifier.notifyFailure(listeners, response, failure); diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java index e9e74b7c6f4..5e430d39515 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java @@ -344,6 +344,9 @@ public abstract class HttpSender implements AsyncContentProvider.Listener if (exchange == null) return; + if (LOG.isDebugEnabled()) + LOG.debug("Request failure " + exchange.getRequest(), failure); + // Mark atomically the request as completed, with respect // to concurrency between request success and request failure. if (exchange.requestComplete(failure)) @@ -559,7 +562,7 @@ public abstract class HttpSender implements AsyncContentProvider.Listener Request request = exchange.getRequest(); if (LOG.isDebugEnabled()) - LOG.debug("Request failure {} {} on {}: {}", request, exchange, getHttpChannel(), failure); + LOG.debug("Request abort {} {} on {}: {}", request, exchange, getHttpChannel(), failure); HttpDestination destination = getHttpChannel().getHttpDestination(); destination.getRequestNotifier().notifyFailure(request, failure); diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpSenderOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpSenderOverHTTP.java index 1edae133609..cba89775783 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpSenderOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpSenderOverHTTP.java @@ -288,7 +288,6 @@ public class HttpSenderOverHTTP extends HttpSender public void failed(Throwable x) { release(); - callback.failed(x); super.failed(x); } @@ -299,6 +298,13 @@ public class HttpSenderOverHTTP extends HttpSender callback.succeeded(); } + @Override + protected void onCompleteFailure(Throwable cause) + { + super.onCompleteFailure(cause); + callback.failed(cause); + } + private void release() { ByteBufferPool bufferPool = httpClient.getByteBufferPool(); diff --git a/jetty-io/src/test/resources/jetty-logging.properties b/jetty-io/src/test/resources/jetty-logging.properties index 257743ed54b..0e7fd71dc25 100644 --- a/jetty-io/src/test/resources/jetty-logging.properties +++ b/jetty-io/src/test/resources/jetty-logging.properties @@ -1,5 +1,5 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -org.eclipse.jetty.LEVEL=INFO +#org.eclipse.jetty.LEVEL=DEBUG #org.eclipse.jetty.io.AbstractConnection.LEVEL=DEBUG #org.eclipse.jetty.io.ManagedSelector.LEVEL=DEBUG #org.eclipse.jetty.io.ssl.SslConnection.LEVEL=DEBUG From 2c75e876a38bd8b1e472c71415c962412285be7c Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Thu, 26 Sep 2019 18:30:43 +0200 Subject: [PATCH 106/113] Fixes #3787 - Jetty client throws EOFException instead of SSLHandshakeException on certificate errors. Now exceptions thrown by fill() or flush() are stored in a field. Further fill() operations will rethrow the original exception rather than returning -1. Returning -1 to application was causing them to close() with a generic failure that was triggering the EOFException reported in this issue. Now applications see the original exception and can close() with the proper cause. Re-enabled HostnameVerificationTest that was reproducing this issue reliably but was @Disabled a while back and never re-enabled. Signed-off-by: Simone Bordet --- .../client/HostnameVerificationTest.java | 28 +++--- .../eclipse/jetty/io/ssl/SslConnection.java | 96 +++++++++++++------ 2 files changed, 79 insertions(+), 45 deletions(-) diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HostnameVerificationTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HostnameVerificationTest.java index 9ce3ab9955e..d7e7f8ac62f 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HostnameVerificationTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HostnameVerificationTest.java @@ -22,7 +22,6 @@ import java.io.IOException; import java.security.cert.CertificateException; import java.util.concurrent.ExecutionException; import javax.net.ssl.SSLHandshakeException; -import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -36,7 +35,6 @@ import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -47,7 +45,6 @@ import static org.junit.jupiter.api.Assertions.fail; * This test class runs tests to make sure that hostname verification (http://www.ietf.org/rfc/rfc2818.txt * section 3.1) is configurable in SslContextFactory and works as expected. */ -@Disabled public class HostnameVerificationTest { private SslContextFactory clientSslContextFactory = new SslContextFactory.Client(); @@ -70,7 +67,7 @@ public class HostnameVerificationTest server.setHandler(new DefaultHandler() { @Override - public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException { baseRequest.setHandled(true); response.getWriter().write("foobar"); @@ -94,30 +91,33 @@ public class HostnameVerificationTest { client.stop(); server.stop(); - server.join(); } /** * This test is supposed to verify that hostname verification works as described in: * http://www.ietf.org/rfc/rfc2818.txt section 3.1. It uses a certificate with a common name different to localhost * and sends a request to localhost. This should fail with an SSLHandshakeException. - * - * @throws Exception on test failure */ @Test - public void simpleGetWithHostnameVerificationEnabledTest() throws Exception + public void simpleGetWithHostnameVerificationEnabledTest() { clientSslContextFactory.setEndpointIdentificationAlgorithm("HTTPS"); String uri = "https://localhost:" + connector.getLocalPort() + "/"; - ExecutionException x = assertThrows(ExecutionException.class, () -> - { - client.GET(uri); - }); + ExecutionException x = assertThrows(ExecutionException.class, () -> client.GET(uri)); + Throwable cause = x.getCause(); assertThat(cause, Matchers.instanceOf(SSLHandshakeException.class)); - Throwable root = cause.getCause().getCause(); - assertThat(root, Matchers.instanceOf(CertificateException.class)); + + // Search for the CertificateException. + Throwable certificateException = cause.getCause(); + while (certificateException != null) + { + if (certificateException instanceof CertificateException) + break; + certificateException = certificateException.getCause(); + } + assertThat(certificateException, Matchers.instanceOf(CertificateException.class)); } /** diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index c1b4398b6eb..0facdc5a1e6 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -146,7 +146,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr @Override public InvocationType getInvocationType() { - return getDecryptedEndPoint().getFillInterest().getCallbackInvocationType(); + return _decryptedEndPoint.getFillInterest().getCallbackInvocationType(); } }; @@ -364,6 +364,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr public class DecryptedEndPoint extends AbstractEndPoint { private final Callback _incompleteWriteCallback = new IncompleteWriteCallback(); + private Throwable _failure; public DecryptedEndPoint() { @@ -529,12 +530,12 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr case NEED_WRAP: if (_flushState == FlushState.IDLE && flush(BufferUtil.EMPTY_BUFFER)) { + rethrow(_failure); if (_sslEngine.isInboundDone()) - // TODO this is probably a JVM bug, work around it by -1 - return -1; + return filled = -1; continue; } - // handle in needsFillInterest + // Handle in needsFillInterest(). return filled = 0; default: @@ -598,6 +599,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr switch (unwrap) { case CLOSED: + rethrow(_failure); return filled = -1; case BUFFER_UNDERFLOW: @@ -607,6 +609,8 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr if (netFilled < 0 && _sslEngine.getUseClientMode()) { closeInbound(); + if (_flushState == FlushState.WAIT_FOR_FILL) + throw new SSLHandshakeException("Abruptly closed by peer"); return filled = -1; } return filled = netFilled; @@ -639,15 +643,14 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr } catch (Throwable x) { - handshakeFailed(x); - + Throwable failure = handleException(x, "fill"); + handshakeFailed(failure); if (_flushState == FlushState.WAIT_FOR_FILL) { _flushState = FlushState.IDLE; - getExecutor().execute(() -> _decryptedEndPoint.getWriteFlusher().onFail(x)); + getExecutor().execute(() -> _decryptedEndPoint.getWriteFlusher().onFail(failure)); } - - throw x; + throw failure; } finally { @@ -676,10 +679,9 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr } catch (Throwable x) { - if (LOG.isDebugEnabled()) - LOG.debug(SslConnection.this.toString(), x); close(x); - throw x; + rethrow(x); + return -1; } } @@ -694,15 +696,18 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr synchronized (_decryptedEndPoint) { if (LOG.isDebugEnabled()) - { - LOG.debug(">needFillInterest uf={} {}", _underflown, SslConnection.this); - LOG.debug("ei={} di={}", BufferUtil.toDetailString(_encryptedInput), BufferUtil.toDetailString(_decryptedInput)); - } + LOG.debug(">needFillInterest s={}/{} uf={} ei={} di={} {}", + _flushState, + _fillState, + _underflown, + BufferUtil.toDetailString(_encryptedInput), + BufferUtil.toDetailString(_decryptedInput), + SslConnection.this); if (_fillState != FillState.IDLE) return; - // Fillable if we have decrypted Input OR encrypted input that has not yet been underflown. + // Fillable if we have decrypted input OR enough encrypted input. fillable = BufferUtil.hasContent(_decryptedInput) || (BufferUtil.hasContent(_encryptedInput) && !_underflown); HandshakeStatus status = _sslEngine.getHandshakeStatus(); @@ -966,8 +971,9 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr } catch (Throwable x) { - handshakeFailed(x); - throw x; + Throwable failure = handleException(x, "flush"); + handshakeFailed(failure); + throw failure; } finally { @@ -979,10 +985,9 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr } catch (Throwable x) { - if (LOG.isDebugEnabled()) - LOG.debug(SslConnection.this.toString(), x); close(x); - throw x; + rethrow(x); + return false; } } @@ -1092,7 +1097,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr boolean ishut = endp.isInputShutdown(); boolean oshut = endp.isOutputShutdown(); if (LOG.isDebugEnabled()) - LOG.debug("shutdownOutput: {} oshut={}, ishut={} {}", SslConnection.this, oshut, ishut); + LOG.debug("shutdownOutput: {} oshut={}, ishut={}", SslConnection.this, oshut, ishut); closeOutbound(); @@ -1110,15 +1115,11 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr { if (!flush(BufferUtil.EMPTY_BUFFER) && !close) { - Thread.yield(); - // if we still can't flush, but we are not closing the endpoint, + // If we still can't flush, but we are not closing the endpoint, // let's just flush the encrypted output in the background. - // and continue as if we are closed. The assumption here is that - // the encrypted buffer will contain the entire close handshake - // and that a call to flush(EMPTY_BUFFER) is not needed. - endp.write(Callback.from(() -> - { - }, t -> endp.close()), _encryptedOutput); + ByteBuffer write = _encryptedOutput; + if (BufferUtil.hasContent(write)) + endp.write(Callback.from(Callback.NOOP::succeeded, t -> endp.close()), write); } } @@ -1282,6 +1283,39 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr return TLS_1_3.equals(protocol); } + private Throwable handleException(Throwable x, String context) + { + synchronized (_decryptedEndPoint) + { + if (_failure == null) + { + _failure = x; + if (LOG.isDebugEnabled()) + LOG.debug(this + " stored " + context + " exception", x); + } + else if (x != _failure) + { + _failure.addSuppressed(x); + if (LOG.isDebugEnabled()) + LOG.debug(this + " suppressed " + context + " exception", x); + } + return _failure; + } + } + + private void rethrow(Throwable x) throws IOException + { + if (x == null) + return; + if (x instanceof RuntimeException) + throw (RuntimeException)x; + if (x instanceof Error) + throw (Error)x; + if (x instanceof IOException) + throw (IOException)x; + throw new IOException(x); + } + @Override public String toString() { From ad2770ddc2253c3713550c250807e479adb9833a Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Fri, 27 Sep 2019 00:15:34 +0200 Subject: [PATCH 107/113] Fixes #3787 - Jetty client throws EOFException instead of SSLHandshakeException on certificate errors. Updates after review. Signed-off-by: Simone Bordet --- .../eclipse/jetty/io/ssl/SslConnection.java | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index 0facdc5a1e6..43c15ecfa75 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -452,14 +452,10 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr LOG.debug("onFillableFail {}", SslConnection.this, failure); _fillState = FillState.IDLE; - switch (_flushState) + if (_flushState == FlushState.WAIT_FOR_FILL) { - case WAIT_FOR_FILL: - _flushState = FlushState.IDLE; - fail = true; - break; - default: - break; + _flushState = FlushState.IDLE; + fail = true; } } @@ -530,7 +526,9 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr case NEED_WRAP: if (_flushState == FlushState.IDLE && flush(BufferUtil.EMPTY_BUFFER)) { - rethrow(_failure); + Throwable failure = _failure; + if (failure != null) + rethrow(failure); if (_sslEngine.isInboundDone()) return filled = -1; continue; @@ -599,7 +597,9 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr switch (unwrap) { case CLOSED: - rethrow(_failure); + Throwable failure = _failure; + if (failure != null) + rethrow(failure); return filled = -1; case BUFFER_UNDERFLOW: @@ -608,9 +608,14 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr _underflown = true; if (netFilled < 0 && _sslEngine.getUseClientMode()) { - closeInbound(); + Throwable closeFailure = closeInbound(); if (_flushState == FlushState.WAIT_FOR_FILL) - throw new SSLHandshakeException("Abruptly closed by peer"); + { + Throwable handshakeFailure = new SSLHandshakeException("Abruptly closed by peer"); + if (closeFailure != null) + handshakeFailure.initCause(closeFailure); + throw handshakeFailure; + } return filled = -1; } return filled = netFilled; @@ -681,7 +686,8 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr { close(x); rethrow(x); - return -1; + // Never reached. + throw new AssertionError(); } } @@ -804,23 +810,25 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr } } - private void closeInbound() throws SSLException + private Throwable closeInbound() throws SSLException { HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus(); try { _sslEngine.closeInbound(); + return null; } catch (SSLException x) { if (handshakeStatus == HandshakeStatus.NOT_HANDSHAKING && !isAllowMissingCloseMessage()) throw x; - else - LOG.ignore(x); + LOG.ignore(x); + return x; } catch (Throwable x) { LOG.ignore(x); + return x; } } @@ -987,7 +995,8 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr { close(x); rethrow(x); - return false; + // Never reached. + throw new AssertionError(); } } @@ -1305,8 +1314,6 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr private void rethrow(Throwable x) throws IOException { - if (x == null) - return; if (x instanceof RuntimeException) throw (RuntimeException)x; if (x instanceof Error) From aa417f939eb151f6f8e6c810cce91e90012c1383 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Fri, 27 Sep 2019 10:18:35 +1000 Subject: [PATCH 108/113] Fix WebSocket CompressExtension releaseDeflater() bug. Signed-off-by: Lachlan Roberts --- .../websocket/core/internal/compress/CompressExtension.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/compress/CompressExtension.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/compress/CompressExtension.java index adc99d598bc..b20386f3427 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/compress/CompressExtension.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/compress/CompressExtension.java @@ -119,8 +119,8 @@ public abstract class CompressExtension extends AbstractExtension public void releaseDeflater() { - getInflaterPool().release(inflaterImpl); - inflaterImpl = null; + getDeflaterPool().release(deflaterImpl); + deflaterImpl = null; } /** From 4d3423e88fc5a744c6ec370fe7e4d5aad240994d Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Fri, 27 Sep 2019 15:18:57 +0200 Subject: [PATCH 109/113] Fixes #3787 - Jetty client throws EOFException instead of SSLHandshakeException on certificate errors. Marked the test with @Tag("external") since it contacts the external server tools.ietf.org. Signed-off-by: Simone Bordet --- .../test/java/org/eclipse/jetty/embedded/ProxyServerTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ProxyServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ProxyServerTest.java index c53d8a96740..279cb339347 100644 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ProxyServerTest.java +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ProxyServerTest.java @@ -27,6 +27,7 @@ import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Server; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -53,6 +54,7 @@ public class ProxyServerTest extends AbstractEmbeddedTest server.stop(); } + @Tag("external") @Test public void testGetProxiedRFC() throws Exception { From 201264d17b5a15350098422b0f7b963b7754fb24 Mon Sep 17 00:00:00 2001 From: Travis Spencer Date: Sat, 28 Sep 2019 14:21:01 +0200 Subject: [PATCH 110/113] Don't add scopes if none are provided Signed-off-by: Travis Spencer --- .../org/eclipse/jetty/security/openid/OpenIdConfiguration.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java index 3d14aa2d36c..f3c9134b4a0 100644 --- a/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java +++ b/jetty-openid/src/main/java/org/eclipse/jetty/security/openid/OpenIdConfiguration.java @@ -131,7 +131,8 @@ public class OpenIdConfiguration implements Serializable public void addScopes(String... scopes) { - Collections.addAll(this.scopes, scopes); + if (scopes != null) + Collections.addAll(this.scopes, scopes); } public List getScopes() From 8e51a4d5325d6e91c934e5c646f5f392e93aff89 Mon Sep 17 00:00:00 2001 From: Marc-Olivier Fleury Date: Mon, 30 Sep 2019 08:29:39 +0200 Subject: [PATCH 111/113] adjusted formatting to follow standards Signed-off-by: Marc-Olivier Fleury --- .../src/main/java/org/eclipse/jetty/client/HttpClient.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java index fe57f3241c4..fddd0af85bf 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java @@ -540,12 +540,12 @@ public class HttpClient extends ContainerLifeCycle port = normalizePort(scheme, port); Origin origin = new Origin(scheme, host, port); - return destinations.computeIfAbsent(origin, o -> { + return destinations.computeIfAbsent(origin, o -> + { HttpDestination newDestination = getTransport().newHttpDestination(o); addManaged(newDestination); - if (LOG.isDebugEnabled()) { + if (LOG.isDebugEnabled()) LOG.debug("Created {}", newDestination); - } return newDestination; }); } From a85a18e0d88c66337d9138bf2b3370c7994227d1 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 30 Sep 2019 08:53:12 +0200 Subject: [PATCH 112/113] Fixes #3787 - Jetty client throws EOFException instead of SSLHandshakeException on certificate errors. Fixed indent. Signed-off-by: Simone Bordet --- .../src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index 43c15ecfa75..6e61f5ad451 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -533,7 +533,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr return filled = -1; continue; } - // Handle in needsFillInterest(). + // Handle in needsFillInterest(). return filled = 0; default: From 5b829a10e4d46b1d0d708b3bff9b2bfa213a53b7 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Tue, 1 Oct 2019 17:23:29 +0200 Subject: [PATCH 113/113] Cosmetics. Signed-off-by: Simone Bordet --- .../eclipse/jetty/util/thread/ReservedThreadExecutor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java index a8a27ad4bce..30bc6dc9d2c 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java @@ -81,8 +81,8 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec _executor = executor; _capacity = reservedThreads(executor, capacity); _stack = new ConcurrentLinkedDeque<>(); - - LOG.debug("{}", this); + if (LOG.isDebugEnabled()) + LOG.debug("{}", this); } /** @@ -180,7 +180,7 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec { // Reserved thread must have incremented size but not yet added itself to queue. // We will spin until it is added. - Thread.yield(); // TODO: use Thread.onSpinWait() in jetty-10 + Thread.yield(); continue; }