diff --git a/documentation/jetty-asciidoctor-extensions/pom.xml b/documentation/jetty-asciidoctor-extensions/pom.xml
index 102bb0241c20..f2f654984073 100644
--- a/documentation/jetty-asciidoctor-extensions/pom.xml
+++ b/documentation/jetty-asciidoctor-extensions/pom.xml
@@ -37,7 +37,7 @@
org.eclipse.jetty.tests
- jetty-home-tester
+ jetty-testers
compile
diff --git a/documentation/jetty-asciidoctor-extensions/src/main/java/org/eclipse/jetty/docs/JettyIncludeExtension.java b/documentation/jetty-asciidoctor-extensions/src/main/java/org/eclipse/jetty/docs/JettyIncludeExtension.java
index 35d7abb9d167..5e7527cc2e1a 100644
--- a/documentation/jetty-asciidoctor-extensions/src/main/java/org/eclipse/jetty/docs/JettyIncludeExtension.java
+++ b/documentation/jetty-asciidoctor-extensions/src/main/java/org/eclipse/jetty/docs/JettyIncludeExtension.java
@@ -29,7 +29,7 @@
import org.asciidoctor.extension.IncludeProcessor;
import org.asciidoctor.extension.PreprocessorReader;
import org.asciidoctor.jruby.extension.spi.ExtensionRegistry;
-import org.eclipse.jetty.tests.hometester.JettyHomeTester;
+import org.eclipse.jetty.tests.testers.JettyHomeTester;
/**
*
Asciidoctor include extension that includes into
diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/ComplianceViolation.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/ComplianceViolation.java
index 6c3068d52ea1..938dbd108a1b 100644
--- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/ComplianceViolation.java
+++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/ComplianceViolation.java
@@ -13,8 +13,14 @@
package org.eclipse.jetty.http;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
+import org.eclipse.jetty.util.Attributes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
/**
* A Compliance Violation represents a requirement of an RFC, specification or Jetty implementation
* that may be allowed to be violated if it is included in a {@link ComplianceViolation.Mode}.
@@ -75,13 +81,119 @@ interface Mode
Set extends ComplianceViolation> getAllowed();
}
+ record Event(ComplianceViolation.Mode mode, ComplianceViolation violation, String details)
+ {
+ @Override
+ public String toString()
+ {
+ return String.format("%s (see %s) in mode %s for %s",
+ violation.getDescription(), violation.getURL(), mode, details);
+ }
+ }
+
/**
* A listener that can be notified of violations.
*/
interface Listener
{
+ Listener NOOP = new Listener() {};
+
+ /**
+ * Initialize the listener in preparation for a new request life cycle.
+ * @return The Listener instance to use for the request life cycle.
+ */
+ default Listener initialize()
+ {
+ return this;
+ }
+
+ /**
+ * A new Request has begun.
+ *
+ * @param request the request attributes, or null if the Request does not exist yet (eg: during parsing of HTTP/1.1 headers, before request is created)
+ */
+ default void onRequestBegin(Attributes request)
+ {
+ }
+
+ /**
+ * A Request has ended.
+ *
+ * @param request the request attributes, or null if Request does not exist yet (eg: during handling of a {@link BadMessageException})
+ */
+ default void onRequestEnd(Attributes request)
+ {
+ }
+
+ /**
+ * The compliance violation event.
+ *
+ * @param event the compliance violation event
+ */
+ default void onComplianceViolation(Event event)
+ {
+ onComplianceViolation(event.mode, event.violation, event.details);
+ }
+
+ /**
+ * The compliance violation event.
+ *
+ * @param mode the mode
+ * @param violation the violation
+ * @param details the details
+ * @deprecated use {@link #onComplianceViolation(Event)} instead. Will be removed in Jetty 12.1.0
+ */
+ @Deprecated(since = "12.0.6", forRemoval = true)
default void onComplianceViolation(Mode mode, ComplianceViolation violation, String details)
{
}
}
+
+ class LoggingListener implements Listener
+ {
+ private static final Logger LOG = LoggerFactory.getLogger(ComplianceViolation.class);
+
+ @Override
+ public void onComplianceViolation(Event event)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug(event.toString());
+ }
+ }
+
+ class CapturingListener implements Listener
+ {
+ public static final String VIOLATIONS_ATTR_KEY = "org.eclipse.jetty.http.compliance.violations";
+
+ private final List events;
+
+ public CapturingListener()
+ {
+ this(null);
+ }
+
+ private CapturingListener(List events)
+ {
+ this.events = events;
+ }
+
+ @Override
+ public Listener initialize()
+ {
+ return new CapturingListener(new ArrayList<>());
+ }
+
+ @Override
+ public void onRequestBegin(Attributes request)
+ {
+ if (request != null)
+ request.setAttribute(VIOLATIONS_ATTR_KEY, events);
+ }
+
+ @Override
+ public void onComplianceViolation(Event event)
+ {
+ events.add(event);
+ }
+ }
}
diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/ComplianceViolationException.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/ComplianceViolationException.java
new file mode 100644
index 000000000000..ea13e58029f6
--- /dev/null
+++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/ComplianceViolationException.java
@@ -0,0 +1,35 @@
+//
+// ========================================================================
+// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License v. 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+// which is available at https://www.apache.org/licenses/LICENSE-2.0.
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.http;
+
+public class ComplianceViolationException extends IllegalArgumentException
+{
+ private final ComplianceViolation.Event event;
+
+ public ComplianceViolationException(ComplianceViolation.Mode mode, ComplianceViolation violation, String details)
+ {
+ this(new ComplianceViolation.Event(mode, violation, details));
+ }
+
+ public ComplianceViolationException(ComplianceViolation.Event event)
+ {
+ super(event.toString());
+ this.event = event;
+ }
+
+ public ComplianceViolation.Event getEvent()
+ {
+ return event;
+ }
+}
diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/CookieCache.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/CookieCache.java
index f6c37d860af3..5e6c34961b62 100644
--- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/CookieCache.java
+++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/CookieCache.java
@@ -31,21 +31,30 @@
* cookies are not re-parsed.
*
*/
-public class CookieCache implements CookieParser.Handler
+public class CookieCache implements CookieParser.Handler, ComplianceViolation.Listener
{
protected static final Logger LOG = LoggerFactory.getLogger(CookieCache.class);
protected final List _rawFields = new ArrayList<>();
protected List _cookieList;
private final CookieParser _parser;
+ private List _violations;
public CookieCache()
{
- this(CookieCompliance.RFC6265, null);
+ this(CookieCompliance.RFC6265);
}
- public CookieCache(CookieCompliance compliance, ComplianceViolation.Listener complianceListener)
+ public CookieCache(CookieCompliance compliance)
{
- _parser = CookieParser.newParser(this, compliance, complianceListener);
+ _parser = CookieParser.newParser(this, compliance, this);
+ }
+
+ @Override
+ public void onComplianceViolation(ComplianceViolation.Event event)
+ {
+ if (_violations == null)
+ _violations = new ArrayList<>();
+ _violations.add(event);
}
@Override
@@ -67,6 +76,11 @@ public void addCookie(String cookieName, String cookieValue, int cookieVersion,
}
public List getCookies(HttpFields headers)
+ {
+ return getCookies(headers, ComplianceViolation.Listener.NOOP);
+ }
+
+ public List getCookies(HttpFields headers, ComplianceViolation.Listener complianceViolationListener)
{
boolean building = false;
ListIterator raw = _rawFields.listIterator();
@@ -136,6 +150,8 @@ public List getCookies(HttpFields headers)
_cookieList = new ArrayList<>();
try
{
+ if (_violations != null)
+ _violations.clear();
_parser.parseFields(_rawFields);
}
catch (CookieParser.InvalidCookieException invalidCookieException)
@@ -144,6 +160,9 @@ public List getCookies(HttpFields headers)
}
}
+ if (_violations != null && !_violations.isEmpty())
+ _violations.forEach(complianceViolationListener::onComplianceViolation);
+
return _cookieList == null ? Collections.emptyList() : _cookieList;
}
diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/CookieCutter.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/CookieCutter.java
index 3478ae767f26..b81ceef8258b 100644
--- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/CookieCutter.java
+++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/CookieCutter.java
@@ -366,7 +366,7 @@ else if (tokenstart >= 0)
protected void reportComplianceViolation(CookieCompliance.Violation violation, String reason)
{
if (_complianceListener != null)
- _complianceListener.onComplianceViolation(_complianceMode, violation, reason);
+ _complianceListener.onComplianceViolation(new ComplianceViolation.Event(_complianceMode, violation, reason));
}
protected boolean isRFC6265RejectedCharacter(char c)
diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpCompliance.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpCompliance.java
index 95c1e0412714..02b514935626 100644
--- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpCompliance.java
+++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpCompliance.java
@@ -20,6 +20,7 @@
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
+import org.eclipse.jetty.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -156,8 +157,14 @@ public String getDescription()
/**
* The request attribute which may be set to record any allowed HTTP violations.
+ * @deprecated use {@link ComplianceViolation.CapturingListener#VIOLATIONS_ATTR_KEY} instead.
+ * (Note: new ATTR captures all Compliance violations, not just HTTP.
+ * Make sure you have {@code HttpConnectionFactory.setRecordHttpComplianceViolations(true)}.
+ * Also make sure that a {@link ComplianceViolation.CapturingListener} has been added as a bean to
+ * either the {@code Connector} or {@code Server} for the Attribute to be created.)
*/
- public static final String VIOLATIONS_ATTR = "org.eclipse.jetty.http.compliance.violations";
+ @Deprecated(since = "12.0.6", forRemoval = true)
+ public static final String VIOLATIONS_ATTR = ComplianceViolation.CapturingListener.VIOLATIONS_ATTR_KEY;
/**
* The HttpCompliance mode that supports RFC 7230
@@ -210,7 +217,8 @@ public static HttpCompliance valueOf(String name)
if (compliance.getName().equals(name))
return compliance;
}
- LOG.warn("Unknown HttpCompliance mode {}", name);
+ if (name.indexOf(',') == -1) // skip warning if delimited, will be handled by .from() properly as a CUSTOM mode.
+ LOG.warn("Unknown HttpCompliance mode {}", name);
return null;
}
@@ -351,4 +359,65 @@ private static Set copyOf(Set violations)
return EnumSet.noneOf(Violation.class);
return EnumSet.copyOf(violations);
}
+
+ public static void checkHttpCompliance(MetaData.Request request, HttpCompliance mode,
+ ComplianceViolation.Listener listener)
+ {
+ boolean seenContentLength = false;
+ boolean seenTransferEncoding = false;
+ boolean seenHostHeader = false;
+
+ HttpFields fields = request.getHttpFields();
+ for (HttpField httpField: fields)
+ {
+ if (httpField.getHeader() == null)
+ continue;
+
+ switch (httpField.getHeader())
+ {
+ case CONTENT_LENGTH ->
+ {
+ if (seenContentLength)
+ assertAllowed(Violation.MULTIPLE_CONTENT_LENGTHS, mode, listener);
+ String[] lengths = httpField.getValues();
+ if (lengths.length > 1)
+ assertAllowed(Violation.MULTIPLE_CONTENT_LENGTHS, mode, listener);
+ if (seenTransferEncoding)
+ assertAllowed(Violation.TRANSFER_ENCODING_WITH_CONTENT_LENGTH, mode, listener);
+ seenContentLength = true;
+ }
+ case TRANSFER_ENCODING ->
+ {
+ if (seenContentLength)
+ assertAllowed(Violation.TRANSFER_ENCODING_WITH_CONTENT_LENGTH, mode, listener);
+ seenTransferEncoding = true;
+ }
+ case HOST ->
+ {
+ if (seenHostHeader)
+ assertAllowed(Violation.DUPLICATE_HOST_HEADERS, mode, listener);
+ String[] hostValues = httpField.getValues();
+ if (hostValues.length > 1)
+ assertAllowed(Violation.DUPLICATE_HOST_HEADERS, mode, listener);
+ for (String hostValue: hostValues)
+ if (StringUtil.isBlank(hostValue))
+ assertAllowed(Violation.UNSAFE_HOST_HEADER, mode, listener);
+ String authority = request.getHttpURI().getHost();
+ if (StringUtil.isBlank(authority))
+ assertAllowed(Violation.UNSAFE_HOST_HEADER, mode, listener);
+ seenHostHeader = true;
+ }
+ }
+ }
+ }
+
+ private static void assertAllowed(Violation violation, HttpCompliance mode, ComplianceViolation.Listener listener)
+ {
+ if (mode.allows(violation))
+ listener.onComplianceViolation(new ComplianceViolation.Event(
+ mode, violation, violation.getDescription()
+ ));
+ else
+ throw new BadMessageException(violation.getDescription());
+ }
}
diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java
index 0a407f776469..3dd28fe43dd1 100644
--- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java
+++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java
@@ -229,7 +229,6 @@ public enum State
private final HttpHandler _handler;
private final RequestHandler _requestHandler;
private final ResponseHandler _responseHandler;
- private final ComplianceViolation.Listener _complianceListener;
private final int _maxHeaderBytes;
private final HttpCompliance _complianceMode;
private final Utf8StringBuilder _uri = new Utf8StringBuilder(INITIAL_URI_LENGTH);
@@ -309,7 +308,6 @@ private HttpParser(RequestHandler requestHandler, ResponseHandler responseHandle
_responseHandler = responseHandler;
_maxHeaderBytes = maxHeaderBytes;
_complianceMode = compliance;
- _complianceListener = (ComplianceViolation.Listener)(_handler instanceof ComplianceViolation.Listener ? _handler : null);
}
public long getBeginNanoTime()
@@ -357,8 +355,8 @@ protected void reportComplianceViolation(Violation violation)
protected void reportComplianceViolation(Violation violation, String reason)
{
- if (_complianceListener != null)
- _complianceListener.onComplianceViolation(_complianceMode, violation, reason);
+ if (_requestHandler != null)
+ _requestHandler.onViolation(new ComplianceViolation.Event(_complianceMode, violation, reason));
}
protected String caseInsensitiveHeader(String orig, String normative)
@@ -1520,6 +1518,10 @@ public boolean parseNext(ByteBuffer buffer)
// Start a request/response
if (_state == State.START)
{
+ if (_requestHandler != null)
+ _requestHandler.messageBegin();
+ if (_responseHandler != null)
+ _responseHandler.messageBegin();
_version = null;
_method = null;
_methodString = null;
@@ -1990,6 +1992,8 @@ public String toString()
*/
public interface HttpHandler
{
+ default void messageBegin() {}
+
boolean content(ByteBuffer item);
boolean headerComplete();
@@ -2020,6 +2024,14 @@ default void parsedTrailer(HttpField field)
*/
void earlyEOF();
+ /**
+ * Called to indicate that a {@link ComplianceViolation} has occurred.
+ * @param event the Compliance Violation event
+ */
+ default void onViolation(ComplianceViolation.Event event)
+ {
+ }
+
/**
* Called to signal that a bad HTTP message has been received.
*
@@ -2128,7 +2140,7 @@ else if (!_cache.put(field))
public boolean cacheable(HttpHeader header, String valueString)
{
- return isEnabled() && header != null && valueString.length() <= _size;
+ return isEnabled() && header != null && valueString != null && valueString.length() <= _size;
}
private void prepare()
diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPartCompliance.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPartCompliance.java
new file mode 100644
index 000000000000..5ff3b819e684
--- /dev/null
+++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPartCompliance.java
@@ -0,0 +1,111 @@
+//
+// ========================================================================
+// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License v. 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+// which is available at https://www.apache.org/licenses/LICENSE-2.0.
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.http;
+
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The compliance mode for MultiPart handling.
+ */
+public class MultiPartCompliance implements ComplianceViolation.Mode
+{
+ public enum Violation implements ComplianceViolation
+ {
+ CONTENT_TRANSFER_ENCODING("https://tools.ietf.org/html/rfc7578#section-4.7", "Content-Transfer-Encoding is deprecated");
+
+ private final String url;
+ private final String description;
+
+ Violation(String url, String description)
+ {
+ this.url = url;
+ this.description = description;
+ }
+
+ @Override
+ public String getName()
+ {
+ return name();
+ }
+
+ @Override
+ public String getURL()
+ {
+ return url;
+ }
+
+ @Override
+ public String getDescription()
+ {
+ return description;
+ }
+ }
+
+ public static final MultiPartCompliance RFC7578 = new MultiPartCompliance(
+ "RFC7578", EnumSet.of(Violation.CONTENT_TRANSFER_ENCODING));
+
+ private static final List KNOWN_MODES = Arrays.asList(RFC7578);
+
+ public static MultiPartCompliance valueOf(String name)
+ {
+ for (MultiPartCompliance compliance : KNOWN_MODES)
+ {
+ if (compliance.getName().equals(name))
+ return compliance;
+ }
+ return null;
+ }
+
+ private final String name;
+ private final Set violations;
+
+ public MultiPartCompliance(String name, Set violations)
+ {
+ this.name = name;
+ this.violations = violations;
+ }
+
+ @Override
+ public String getName()
+ {
+ return name;
+ }
+
+ @Override
+ public boolean allows(ComplianceViolation violation)
+ {
+ return violations.contains(violation);
+ }
+
+ @Override
+ public Set extends ComplianceViolation> getKnown()
+ {
+ return EnumSet.allOf(Violation.class);
+ }
+
+ @Override
+ public Set extends ComplianceViolation> getAllowed()
+ {
+ return violations;
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s@%x%s", name, hashCode(), violations);
+ }
+}
diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPartFormData.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPartFormData.java
index c8b056f71a34..7e3b43efd664 100644
--- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPartFormData.java
+++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPartFormData.java
@@ -77,13 +77,22 @@ private MultiPartFormData()
{
}
+ /**
+ * @deprecated use {@link #from(Attributes, ComplianceViolation.Listener, String, Function)} instead. This method will be removed in Jetty 12.1.0
+ */
+ @Deprecated(since = "12.0.6", forRemoval = true)
public static CompletableFuture from(Attributes attributes, String boundary, Function> parse)
+ {
+ return from(attributes, ComplianceViolation.Listener.NOOP, boundary, parse);
+ }
+
+ public static CompletableFuture from(Attributes attributes, ComplianceViolation.Listener listener, String boundary, Function> parse)
{
@SuppressWarnings("unchecked")
CompletableFuture futureParts = (CompletableFuture)attributes.getAttribute(MultiPartFormData.class.getName());
if (futureParts == null)
{
- futureParts = parse.apply(new Parser(boundary));
+ futureParts = parse.apply(new Parser(boundary, listener));
attributes.setAttribute(MultiPartFormData.class.getName(), futureParts);
}
return futureParts;
@@ -200,6 +209,7 @@ public static class Parser
{
private final PartsListener listener = new PartsListener();
private final MultiPart.Parser parser;
+ private ComplianceViolation.Listener complianceViolationListener;
private boolean useFilesForPartsWithoutFileName;
private Path filesDirectory;
private long maxFileSize = -1;
@@ -209,8 +219,14 @@ public static class Parser
private Parts parts;
public Parser(String boundary)
+ {
+ this(boundary, null);
+ }
+
+ public Parser(String boundary, ComplianceViolation.Listener complianceViolationListener)
{
parser = new MultiPart.Parser(Objects.requireNonNull(boundary), listener);
+ this.complianceViolationListener = complianceViolationListener != null ? complianceViolationListener : ComplianceViolation.Listener.NOOP;
}
public CompletableFuture parse(Content.Source content)
@@ -517,6 +533,13 @@ public void onPart(String name, String fileName, HttpFields headers)
memoryFileSize = 0;
try (AutoLock ignored = lock.lock())
{
+ if (headers.contains("content-transfer-encoding"))
+ {
+ String value = headers.get("content-transfer-encoding");
+ if (!"8bit".equalsIgnoreCase(value) && !"binary".equalsIgnoreCase(value))
+ complianceViolationListener.onComplianceViolation(new ComplianceViolation.Event(MultiPartCompliance.RFC7578, MultiPartCompliance.Violation.CONTENT_TRANSFER_ENCODING, value));
+ }
+
MultiPart.Part part;
if (fileChannel != null)
part = new MultiPart.PathPart(name, fileName, headers, filePath);
diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/RFC6265CookieParser.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/RFC6265CookieParser.java
index 1cae2f74e673..e64856a70233 100644
--- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/RFC6265CookieParser.java
+++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/RFC6265CookieParser.java
@@ -444,7 +444,7 @@ else if (_complianceMode.allows(ATTRIBUTES))
protected void reportComplianceViolation(CookieCompliance.Violation violation, String reason)
{
if (_complianceListener != null)
- _complianceListener.onComplianceViolation(_complianceMode, violation, reason);
+ _complianceListener.onComplianceViolation(new ComplianceViolation.Event(_complianceMode, violation, reason));
}
}
diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/Syntax.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/Syntax.java
index 91fa85c74d66..fa24eeb59694 100644
--- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/Syntax.java
+++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/Syntax.java
@@ -108,7 +108,7 @@ public static void requireValidRFC6265CookieValue(String value)
// Has starting DQUOTE
if (valueLen <= 1 || (value.charAt(valueLen - 1) != '"'))
{
- throw new IllegalArgumentException("RFC6265 Cookie values must have balanced DQUOTES (if used)");
+ throw new ComplianceViolationException(CookieCompliance.RFC6265, CookieCompliance.Violation.INVALID_COOKIES, "RFC6265 Cookie values must have balanced DQUOTES (if used)");
}
// adjust search range to exclude DQUOTES
@@ -122,16 +122,16 @@ public static void requireValidRFC6265CookieValue(String value)
// 0x00 - 0x1F are low order control characters
// 0x7F is the DEL control character
if ((c <= 0x1F) || (c == 0x7F))
- throw new IllegalArgumentException("RFC6265 Cookie values may not contain control characters");
+ throw new ComplianceViolationException(CookieCompliance.RFC6265, CookieCompliance.Violation.INVALID_COOKIES, "RFC6265 Cookie values may not contain control characters");
if ((c == ' ' /* 0x20 */) ||
(c == '"' /* 0x2C */) ||
(c == ';' /* 0x3B */) ||
(c == '\\' /* 0x5C */))
{
- throw new IllegalArgumentException("RFC6265 Cookie values may not contain character: [" + c + "]");
+ throw new ComplianceViolationException(CookieCompliance.RFC6265, CookieCompliance.Violation.INVALID_COOKIES, "RFC6265 Cookie values may not contain character: [" + c + "]");
}
if (c >= 0x80)
- throw new IllegalArgumentException("RFC6265 Cookie values characters restricted to US-ASCII: 0x" + Integer.toHexString(c));
+ throw new ComplianceViolationException(CookieCompliance.RFC6265, CookieCompliance.Violation.INVALID_COOKIES, "RFC6265 Cookie values characters restricted to US-ASCII: 0x" + Integer.toHexString(c));
}
}
}
diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/UriCompliance.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/UriCompliance.java
index ff4d1ad8270c..fffb1cd8bdc7 100644
--- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/UriCompliance.java
+++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/UriCompliance.java
@@ -191,7 +191,8 @@ public static UriCompliance valueOf(String name)
if (compliance.getName().equals(name))
return compliance;
}
- LOG.warn("Unknown UriCompliance mode {}", name);
+ if (name.indexOf(',') == -1) // skip warning if delimited, will be handled by .from() properly as a CUSTOM mode.
+ LOG.warn("Unknown UriCompliance mode {}", name);
return null;
}
@@ -255,10 +256,6 @@ public static UriCompliance from(String spec)
if (exclude)
element = element.substring(1);
- // Ignore removed name. TODO: remove in future release.
- if (element.equals("NON_CANONICAL_AMBIGUOUS_PATHS"))
- continue;
-
Violation section = Violation.valueOf(element);
if (exclude)
violations.remove(section);
@@ -268,8 +265,6 @@ public static UriCompliance from(String spec)
compliance = new UriCompliance("CUSTOM" + __custom.getAndIncrement(), violations);
}
- if (LOG.isDebugEnabled())
- LOG.debug("UriCompliance from {}->{}", spec, compliance);
return compliance;
}
@@ -360,12 +355,14 @@ private static Set copyOf(Set violations)
return EnumSet.copyOf(violations);
}
- public static String checkUriCompliance(UriCompliance compliance, HttpURI uri)
+ public static String checkUriCompliance(UriCompliance compliance, HttpURI uri, ComplianceViolation.Listener listener)
{
for (UriCompliance.Violation violation : UriCompliance.Violation.values())
{
if (uri.hasViolation(violation) && (compliance == null || !compliance.allows(violation)))
return violation.getDescription();
+ else if (listener != null)
+ listener.onComplianceViolation(new ComplianceViolation.Event(compliance, violation, uri.toString()));
}
return null;
}
diff --git a/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java b/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
index 59823a34d89a..5b99702c9b3a 100644
--- a/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
+++ b/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
@@ -557,12 +557,23 @@ public void testNoValue(String eoln)
ByteBuffer buffer = BufferUtil.toBuffer(
"GET / HTTP/1.0" + eoln +
"Host: localhost" + eoln +
- "Name0: " + eoln +
+ "Name0: " + eoln +
"Name1:" + eoln +
+ "Authorization: " + eoln +
+ "Authorization:" + eoln +
eoln);
- HttpParser.RequestHandler handler = new Handler();
+ HttpParser.RequestHandler handler = new Handler()
+ {
+ @Override
+ public void badMessage(HttpException failure)
+ {
+ ((Throwable)failure).printStackTrace();
+ super.badMessage(failure);
+ }
+ };
HttpParser parser = new HttpParser(handler);
+ parser.setHeaderCacheSize(1024);
parseAll(parser, buffer);
assertTrue(_headerCompleted);
@@ -576,7 +587,11 @@ public void testNoValue(String eoln)
assertEquals("", _val[1]);
assertEquals("Name1", _hdr[2]);
assertEquals("", _val[2]);
- assertEquals(2, _headers);
+ assertEquals("Authorization", _hdr[3]);
+ assertEquals("", _val[3]);
+ assertEquals("Authorization", _hdr[4]);
+ assertEquals("", _val[4]);
+ assertEquals(4, _headers);
}
@ParameterizedTest
@@ -3237,7 +3252,7 @@ public void init()
private boolean _messageCompleted;
private final List _complianceViolation = new ArrayList<>();
- private class Handler implements HttpParser.RequestHandler, HttpParser.ResponseHandler, ComplianceViolation.Listener
+ private class Handler implements HttpParser.RequestHandler, HttpParser.ResponseHandler
{
@Override
public boolean content(ByteBuffer ref)
@@ -3337,9 +3352,9 @@ public void earlyEOF()
}
@Override
- public void onComplianceViolation(ComplianceViolation.Mode mode, ComplianceViolation violation, String reason)
+ public void onViolation(ComplianceViolation.Event event)
{
- _complianceViolation.add(violation);
+ _complianceViolation.add(event.violation());
}
}
diff --git a/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/RFC6265CookieParserTest.java b/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/RFC6265CookieParserTest.java
index 5741ff1136f9..dc84b00087fb 100644
--- a/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/RFC6265CookieParserTest.java
+++ b/jetty-core/jetty-http/src/test/java/org/eclipse/jetty/http/RFC6265CookieParserTest.java
@@ -51,7 +51,7 @@ public void testRFC2965Single()
assertCookie("Cookies[1]", cookies.get(1), "Customer", "WILE_E_COYOTE", 0, null);
assertCookie("Cookies[2]", cookies.get(2), "$Path", "/acme", 0, null);
- // There attributes are seen as just normal cookies, so no violations
+ // Normal cookies with attributes, so no violations
assertThat(parser.violations.size(), is(0));
// Same again, but allow attributes which are ignored
@@ -60,7 +60,7 @@ public void testRFC2965Single()
assertThat("Cookies.length", cookies.size(), is(1));
assertCookie("Cookies[0]", cookies.get(0), "Customer", "WILE_E_COYOTE", 0, null);
- // There attributes are seen as just normal cookies, so no violations
+ // There are 2 attributes that are seen as violations
assertThat(parser.violations.size(), is(2));
// Same again, but allow attributes which are not ignored
@@ -69,10 +69,10 @@ public void testRFC2965Single()
assertThat("Cookies.length", cookies.size(), is(1));
assertCookie("Cookies[0]", cookies.get(0), "Customer", "WILE_E_COYOTE", 1, "/acme");
- // There attributes are seen as just normal cookies, so no violations
+ // There are 2 attributes that are seen as violations
assertThat(parser.violations.size(), is(2));
- // Same test with RFC 6265 strict should throw.
+ // Same test, but with RFC 6265 strict.
parser = new TestCookieParser(CookieCompliance.RFC6265_STRICT);
cookies = parser.parseFields(rawCookie);
assertThat("Cookies.length", cookies.size(), is(3));
@@ -80,7 +80,7 @@ public void testRFC2965Single()
assertCookie("Cookies[1]", cookies.get(1), "Customer", "WILE_E_COYOTE", 0, null);
assertCookie("Cookies[2]", cookies.get(2), "$Path", "/acme", 0, null);
- // There attributes are seen as just normal cookies, so no violations
+ // Normal cookies with attributes, so no violations
assertThat(parser.violations.size(), is(0));
}
@@ -444,9 +444,9 @@ private TestCookieParser(CookieCompliance compliance)
}
@Override
- public void onComplianceViolation(ComplianceViolation.Mode mode, ComplianceViolation violation, String details)
+ public void onComplianceViolation(ComplianceViolation.Event event)
{
- violations.add((CookieCompliance.Violation)violation);
+ violations.add((CookieCompliance.Violation)event.violation());
}
private List parseFields(String... fields)
diff --git a/jetty-core/jetty-http2/jetty-http2-server/src/main/config/modules/http2.mod b/jetty-core/jetty-http2/jetty-http2-server/src/main/config/modules/http2.mod
index 2447ef364ec9..34081544e885 100644
--- a/jetty-core/jetty-http2/jetty-http2-server/src/main/config/modules/http2.mod
+++ b/jetty-core/jetty-http2/jetty-http2-server/src/main/config/modules/http2.mod
@@ -12,7 +12,9 @@ ssl
alpn
[lib]
-lib/http2/*.jar
+lib/http2/jetty-http2-common-${jetty.version}.jar
+lib/http2/jetty-http2-hpack-${jetty.version}.jar
+lib/http2/jetty-http2-server-${jetty.version}.jar
[xml]
etc/jetty-http2.xml
diff --git a/jetty-core/jetty-http2/jetty-http2-server/src/main/config/modules/http2c.mod b/jetty-core/jetty-http2/jetty-http2-server/src/main/config/modules/http2c.mod
index eb40a13ed66c..573d4158a50a 100644
--- a/jetty-core/jetty-http2/jetty-http2-server/src/main/config/modules/http2c.mod
+++ b/jetty-core/jetty-http2/jetty-http2-server/src/main/config/modules/http2c.mod
@@ -10,7 +10,9 @@ http
http
[lib]
-lib/http2/*.jar
+lib/http2/jetty-http2-common-${jetty.version}.jar
+lib/http2/jetty-http2-hpack-${jetty.version}.jar
+lib/http2/jetty-http2-server-${jetty.version}.jar
[xml]
etc/jetty-http2c.xml
diff --git a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HTTP2ServerConnection.java b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HTTP2ServerConnection.java
index 15855ed47600..8446dcea492b 100644
--- a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HTTP2ServerConnection.java
+++ b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HTTP2ServerConnection.java
@@ -258,6 +258,7 @@ private HttpChannel pollHttpChannel()
httpChannel = httpChannels.poll();
if (httpChannel == null)
httpChannel = httpChannelFactory.newHttpChannel(this);
+ httpChannel.initialize();
return httpChannel;
}
diff --git a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HttpStreamOverHTTP2.java b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HttpStreamOverHTTP2.java
index e237ad9b6fdb..195fe08b98c3 100644
--- a/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HttpStreamOverHTTP2.java
+++ b/jetty-core/jetty-http2/jetty-http2-server/src/main/java/org/eclipse/jetty/http2/server/internal/HttpStreamOverHTTP2.java
@@ -18,6 +18,8 @@
import java.util.function.BiConsumer;
import java.util.function.Supplier;
+import org.eclipse.jetty.http.ComplianceViolation;
+import org.eclipse.jetty.http.HttpCompliance;
import org.eclipse.jetty.http.HttpException;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpMethod;
@@ -38,6 +40,7 @@
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpStream;
+import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.TunnelSupport;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
@@ -82,7 +85,14 @@ public Runnable onRequest(HeadersFrame frame)
{
_requestMetaData = (MetaData.Request)frame.getMetaData();
+ // Grab freshly initialized ComplianceViolation.Listener here, no need to reinitialize.
+ ComplianceViolation.Listener listener = _httpChannel.getComplianceViolationListener();
Runnable handler = _httpChannel.onRequest(_requestMetaData);
+ Request request = _httpChannel.getRequest();
+ listener.onRequestBegin(request);
+ // Note UriCompliance is done by HandlerInvoker
+ HttpCompliance httpCompliance = _httpChannel.getConnectionMetaData().getHttpConfiguration().getHttpCompliance();
+ HttpCompliance.checkHttpCompliance(_requestMetaData, httpCompliance, listener);
if (frame.isEndStream())
{
diff --git a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HTTP2CServerTest.java b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HTTP2CServerTest.java
index ffbc90e291df..a2c657488894 100644
--- a/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HTTP2CServerTest.java
+++ b/jetty-core/jetty-http2/jetty-http2-tests/src/test/java/org/eclipse/jetty/http2/tests/HTTP2CServerTest.java
@@ -304,7 +304,7 @@ public void testHTTP20DirectWithoutH2C() throws Exception
@Override
public Connection newConnection(Connector connector, EndPoint endPoint)
{
- HttpConnection connection = new HttpConnection(getHttpConfiguration(), connector, endPoint, isRecordHttpComplianceViolations())
+ HttpConnection connection = new HttpConnection(getHttpConfiguration(), connector, endPoint)
{
@Override
public void onFillable()
diff --git a/jetty-core/jetty-http3/jetty-http3-server/src/main/config/modules/http3.mod b/jetty-core/jetty-http3/jetty-http3-server/src/main/config/modules/http3.mod
index eff3d069164e..7069e512d3d3 100644
--- a/jetty-core/jetty-http3/jetty-http3-server/src/main/config/modules/http3.mod
+++ b/jetty-core/jetty-http3/jetty-http3-server/src/main/config/modules/http3.mod
@@ -15,7 +15,14 @@ quiche
work
[lib]
-lib/http3/*.jar
+lib/http3/jetty-http3-common-${jetty.version}.jar
+lib/http3/jetty-http3-qpack-${jetty.version}.jar
+lib/http3/jetty-http3-server-${jetty.version}.jar
+lib/http3/jetty-quic-common-${jetty.version}.jar
+lib/http3/jetty-quic-quiche-common-${jetty.version}.jar
+lib/http3/jetty-quic-quiche-foreign-incubator-${jetty.version}.jar
+lib/http3/jetty-quic-quiche-jna-${jetty.version}.jar
+lib/http3/jetty-quic-server-${jetty.version}.jar
[xml]
etc/jetty-http3.xml
diff --git a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpStreamOverHTTP3.java b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpStreamOverHTTP3.java
index 491c03238994..ff784c695521 100644
--- a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpStreamOverHTTP3.java
+++ b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/HttpStreamOverHTTP3.java
@@ -19,6 +19,8 @@
import java.util.function.BiConsumer;
import java.util.function.Supplier;
+import org.eclipse.jetty.http.ComplianceViolation;
+import org.eclipse.jetty.http.HttpCompliance;
import org.eclipse.jetty.http.HttpException;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpMethod;
@@ -33,6 +35,7 @@
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpStream;
+import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.thread.AutoLock;
@@ -72,7 +75,14 @@ public Runnable onRequest(HeadersFrame frame)
{
requestMetaData = (MetaData.Request)frame.getMetaData();
+ // Grab freshly initialized ComplianceViolation.Listener here, no need to reinitialize.
+ ComplianceViolation.Listener listener = httpChannel.getComplianceViolationListener();
Runnable handler = httpChannel.onRequest(requestMetaData);
+ Request request = this.httpChannel.getRequest();
+ listener.onRequestBegin(request);
+ // Note UriCompliance is done by HandlerInvoker
+ HttpCompliance httpCompliance = httpChannel.getConnectionMetaData().getHttpConfiguration().getHttpCompliance();
+ HttpCompliance.checkHttpCompliance(requestMetaData, httpCompliance, listener);
if (frame.isLast())
{
diff --git a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3StreamConnection.java b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3StreamConnection.java
index 5ded0ab5e897..215f95f3e527 100644
--- a/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3StreamConnection.java
+++ b/jetty-core/jetty-http3/jetty-http3-server/src/main/java/org/eclipse/jetty/http3/server/internal/ServerHTTP3StreamConnection.java
@@ -53,6 +53,7 @@ public Runnable onRequest(HTTP3StreamServer stream, HeadersFrame frame)
{
// Create new metadata for every request as the local or remote address may have changed.
HttpChannel httpChannel = httpChannelFactory.newHttpChannel(new MetaData());
+ httpChannel.initialize();
HttpStreamOverHTTP3 httpStream = new HttpStreamOverHTTP3(this, httpChannel, stream);
httpChannel.setHttpStream(httpStream);
stream.setAttachment(httpStream);
diff --git a/jetty-core/jetty-security/src/main/java/org/eclipse/jetty/security/jaas/spi/AbstractLoginModule.java b/jetty-core/jetty-security/src/main/java/org/eclipse/jetty/security/jaas/spi/AbstractLoginModule.java
index 318e835adc1e..48227ca90b7f 100644
--- a/jetty-core/jetty-security/src/main/java/org/eclipse/jetty/security/jaas/spi/AbstractLoginModule.java
+++ b/jetty-core/jetty-security/src/main/java/org/eclipse/jetty/security/jaas/spi/AbstractLoginModule.java
@@ -34,8 +34,9 @@
/**
* AbstractLoginModule
*
- * Abstract base class for all LoginModules. Subclasses should
- * just need to implement getUserInfo method.
+ * Abstract base class for all {@link LoginModule LoginModules}. Subclasses should
+ * implement {@link #getUser(String)} method, and subclass {@link JAASUser} to implement
+ * {@link JAASUser#doFetchRoles()} method.
*/
public abstract class AbstractLoginModule implements LoginModule
{
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java
index 13bd50fec21d..e74a5f86e6e0 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java
@@ -31,6 +31,7 @@
import java.util.concurrent.locks.Condition;
import java.util.stream.Collectors;
+import org.eclipse.jetty.http.ComplianceViolation;
import org.eclipse.jetty.io.ArrayByteBufferPool;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.EndPoint;
@@ -270,6 +271,10 @@ public int getAcceptors()
@Override
protected void doStart() throws Exception
{
+ if (!getBeans(ComplianceViolation.Listener.class).isEmpty() ||
+ !getServer().getBeans(ComplianceViolation.Listener.class).isEmpty())
+ LOG.warn("ComplianceViolation.Listeners must now be set on HttpConfiguration");
+
getConnectionFactories().stream()
.filter(ConnectionFactory.Configuring.class::isInstance)
.map(ConnectionFactory.Configuring.class::cast)
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
index 382fc544bf89..3d565d51ca02 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
@@ -17,6 +17,7 @@
import java.util.function.Consumer;
import java.util.function.Predicate;
+import org.eclipse.jetty.http.ComplianceViolation;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.server.internal.HttpChannelState;
import org.eclipse.jetty.util.thread.Invocable;
@@ -100,9 +101,33 @@ public interface HttpChannel extends Invocable
/**
* Recycle the HttpChannel, so that a new cycle of calling {@link #setHttpStream(HttpStream)},
* {@link #onRequest(MetaData.Request)} etc. may be performed on the channel.
+ * @see #initialize()
*/
void recycle();
+ /**
+ * Initialize the HttpChannel when a new cycle of request handling begins.
+ * @see #recycle()
+ */
+ void initialize();
+
+ /**
+ * @return the active {@link ComplianceViolation.Listener}
+ */
+ ComplianceViolation.Listener getComplianceViolationListener();
+
+ /**
+ * @param request attempt to resolve the HttpChannel from the provided request
+ * @return the HttpChannel if found
+ * @throws IllegalStateException if unable to find HttpChannel
+ */
+ static HttpChannel from(Request request)
+ {
+ if (Request.unWrap(request).getComponents() instanceof HttpChannel httpChannel)
+ return httpChannel;
+ throw new IllegalStateException("Unable to find HttpChannel from " + request);
+ }
+
/**
* A factory for {@link HttpChannel} instances.
*
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java
index 7e0a1cf0cb3d..0812943338ce 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConfiguration.java
@@ -15,15 +15,19 @@
import java.io.IOException;
import java.net.SocketAddress;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
+import org.eclipse.jetty.http.ComplianceViolation;
import org.eclipse.jetty.http.CookieCompliance;
import org.eclipse.jetty.http.HttpCompliance;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.http.MultiPartCompliance;
import org.eclipse.jetty.http.UriCompliance;
import org.eclipse.jetty.util.HostPort;
import org.eclipse.jetty.util.Index;
@@ -52,6 +56,7 @@ public class HttpConfiguration implements Dumpable
.caseSensitive(false)
.mutable()
.build();
+ private final List _complianceViolationListeners = new ArrayList<>();
private int _outputBufferSize = 32 * 1024;
private int _outputAggregationSize = _outputBufferSize / 4;
private int _requestHeaderSize = 8 * 1024;
@@ -75,6 +80,7 @@ public class HttpConfiguration implements Dumpable
private UriCompliance _uriCompliance = UriCompliance.DEFAULT;
private CookieCompliance _requestCookieCompliance = CookieCompliance.RFC6265;
private CookieCompliance _responseCookieCompliance = CookieCompliance.RFC6265;
+ private MultiPartCompliance _multiPartCompliance = MultiPartCompliance.RFC7578;
private boolean _notifyRemoteAsyncErrors = true;
private boolean _relativeRedirectAllowed = true;
private HostPort _serverAuthority;
@@ -147,6 +153,8 @@ public HttpConfiguration(HttpConfiguration config)
_httpCompliance = config._httpCompliance;
_requestCookieCompliance = config._requestCookieCompliance;
_responseCookieCompliance = config._responseCookieCompliance;
+ _multiPartCompliance = config._multiPartCompliance;
+ _complianceViolationListeners.addAll(config._complianceViolationListeners);
_notifyRemoteAsyncErrors = config._notifyRemoteAsyncErrors;
_relativeRedirectAllowed = config._relativeRedirectAllowed;
_uriCompliance = config._uriCompliance;
@@ -628,6 +636,50 @@ public void setResponseCookieCompliance(CookieCompliance cookieCompliance)
_responseCookieCompliance = cookieCompliance == null ? CookieCompliance.RFC6265 : cookieCompliance;
}
+ /**
+ * @return the {@link MultiPartCompliance} used for validating multipart form syntax.
+ */
+ public MultiPartCompliance getMultiPartCompliance()
+ {
+ return _multiPartCompliance;
+ }
+
+ /**
+ * @param multiPartCompliance the {@link MultiPartCompliance} used for validating multipart form syntax.
+ */
+ public void setMultiPartCompliance(MultiPartCompliance multiPartCompliance)
+ {
+ this._multiPartCompliance = multiPartCompliance;
+ }
+
+ /**
+ * Add a {@link ComplianceViolation.Listener} to the configuration
+ * @param listener the listener to add
+ */
+ public void addComplianceViolationListener(ComplianceViolation.Listener listener)
+ {
+ this._complianceViolationListeners.add(Objects.requireNonNull(listener));
+ }
+
+ /**
+ * Remove a {@link ComplianceViolation.Listener} from the configuration
+ * @param listener the listener to remove
+ * @return {@code true} if this list contained the specified element
+ */
+ public boolean removeComplianceViolationListener(ComplianceViolation.Listener listener)
+ {
+ return this._complianceViolationListeners.remove(Objects.requireNonNull(listener));
+ }
+
+ /**
+ * Get the list of configured {@link ComplianceViolation.Listener} to use.
+ * @return the list of configured listeners
+ */
+ public List getComplianceViolationListeners()
+ {
+ return this._complianceViolationListeners;
+ }
+
/**
* Set whether remote errors, when detected, are notified to async applications.
* @param notifyRemoteAsyncErrors whether remote errors, when detected, are notified to async applications
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java
index 55167f4ae631..82b373364c15 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnectionFactory.java
@@ -15,11 +15,14 @@
import java.util.Objects;
+import org.eclipse.jetty.http.ComplianceViolation;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.internal.HttpConnection;
import org.eclipse.jetty.util.annotation.Name;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* A Connection Factory for HTTP Connections.
@@ -29,8 +32,8 @@
*/
public class HttpConnectionFactory extends AbstractConnectionFactory implements HttpConfiguration.ConnectionFactory
{
+ private static final Logger LOG = LoggerFactory.getLogger(HttpConnectionFactory.class);
private final HttpConfiguration _config;
- private boolean _recordHttpComplianceViolations;
private boolean _useInputDirectByteBuffers;
private boolean _useOutputDirectByteBuffers;
@@ -54,14 +57,25 @@ public HttpConfiguration getHttpConfiguration()
return _config;
}
+ /**
+ * @deprecated use {@link HttpConfiguration#getComplianceViolationListeners()} instead to know if there
+ * are any {@link ComplianceViolation.Listener} to notify. this method will be removed in Jetty 12.1.0
+ */
+ @Deprecated(since = "12.0.6", forRemoval = true)
public boolean isRecordHttpComplianceViolations()
{
- return _recordHttpComplianceViolations;
+ return !_config.getComplianceViolationListeners().isEmpty();
}
+ /**
+ * Does nothing.
+ * @deprecated use {@link HttpConfiguration#addComplianceViolationListener(ComplianceViolation.Listener)} instead.
+ * this method will be removed in Jetty 12.1.0
+ */
+ @Deprecated(since = "12.0.6", forRemoval = true)
public void setRecordHttpComplianceViolations(boolean recordHttpComplianceViolations)
{
- this._recordHttpComplianceViolations = recordHttpComplianceViolations;
+ _config.addComplianceViolationListener(new ComplianceViolation.LoggingListener());
}
public boolean isUseInputDirectByteBuffers()
@@ -87,7 +101,7 @@ public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers)
@Override
public Connection newConnection(Connector connector, EndPoint endPoint)
{
- HttpConnection connection = new HttpConnection(_config, connector, endPoint, isRecordHttpComplianceViolations());
+ HttpConnection connection = new HttpConnection(_config, connector, endPoint);
connection.setUseInputDirectByteBuffers(isUseInputDirectByteBuffers());
connection.setUseOutputDirectByteBuffers(isUseOutputDirectByteBuffers());
return configure(connection, connector, endPoint);
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpCookieUtils.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpCookieUtils.java
index 815807dc123f..8e81362a754d 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpCookieUtils.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/HttpCookieUtils.java
@@ -18,6 +18,7 @@
import java.util.Locale;
import java.util.Map;
+import org.eclipse.jetty.http.ComplianceViolationException;
import org.eclipse.jetty.http.CookieCompliance;
import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpField;
@@ -211,9 +212,17 @@ public static String getRFC6265SetCookie(HttpCookie httpCookie)
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Bad cookie name");
- // Name is checked for legality by servlet spec, but can also be passed directly so check again for quoting
- // Per RFC6265, Cookie.name follows RFC2616 Section 2.2 token rules
- Syntax.requireValidRFC2616Token(name, "RFC6265 Cookie name");
+ try
+ {
+ // Name is checked for legality by servlet spec, but can also be passed directly so check again for quoting
+ // Per RFC6265, Cookie.name follows RFC2616 Section 2.2 token rules
+ Syntax.requireValidRFC2616Token(name, "RFC6265 Cookie name");
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new ComplianceViolationException(CookieCompliance.RFC6265, CookieCompliance.Violation.INVALID_COOKIES, "RFC6265 Cookie name must be a valid RFC2616 Token");
+ }
+
// Ensure that Per RFC6265, Cookie.value follows syntax rules
String value = httpCookie.getValue();
Syntax.requireValidRFC6265CookieValue(value);
@@ -399,13 +408,14 @@ private HttpCookieUtils()
public static class SetCookieHttpField extends HttpField
{
private final HttpCookie _cookie;
- private final CookieCompliance _compliance;
+ private final String _value;
public SetCookieHttpField(HttpCookie cookie, CookieCompliance compliance)
{
super(HttpHeader.SET_COOKIE, (String)null);
this._cookie = cookie;
- _compliance = compliance;
+ // trigger compliance check
+ this._value = getSetCookie(this._cookie, compliance);
}
public HttpCookie getHttpCookie()
@@ -416,7 +426,7 @@ public HttpCookie getHttpCookie()
@Override
public String getValue()
{
- return getSetCookie(_cookie, _compliance);
+ return this._value;
}
}
}
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
index 6af4e71d07b8..9ea357ce6c3b 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
@@ -589,12 +589,11 @@ static List getCookies(Request request)
CookieCache cookieCache = (CookieCache)request.getComponents().getCache().getAttribute(CACHE_ATTRIBUTE);
if (cookieCache == null)
{
- // TODO compliance listeners?
- cookieCache = new CookieCache(request.getConnectionMetaData().getHttpConfiguration().getRequestCookieCompliance(), null);
+ cookieCache = new CookieCache(request.getConnectionMetaData().getHttpConfiguration().getRequestCookieCompliance());
request.getComponents().getCache().setAttribute(CACHE_ATTRIBUTE, cookieCache);
}
- cookies = cookieCache.getCookies(request.getHeaders());
+ cookies = cookieCache.getCookies(request.getHeaders(), HttpChannel.from(request).getComplianceViolationListener());
request.setAttribute(COOKIE_ATTRIBUTE, cookies);
return cookies;
}
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
index cdca4131fc10..fbf17e921413 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java
@@ -20,6 +20,7 @@
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;
+import org.eclipse.jetty.http.ComplianceViolationException;
import org.eclipse.jetty.http.CookieCompliance;
import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpException;
@@ -378,7 +379,15 @@ static void addCookie(Response response, HttpCookie cookie)
Request request = response.getRequest();
CookieCompliance compliance = request.getConnectionMetaData().getHttpConfiguration().getResponseCookieCompliance();
- response.getHeaders().add(new HttpCookieUtils.SetCookieHttpField(HttpCookieUtils.checkSameSite(cookie, request.getContext()), compliance));
+ try
+ {
+ response.getHeaders().add(new HttpCookieUtils.SetCookieHttpField(HttpCookieUtils.checkSameSite(cookie, request.getContext()), compliance));
+ }
+ catch (ComplianceViolationException e)
+ {
+ HttpChannel.from(request).getComplianceViolationListener().onComplianceViolation(e.getEvent());
+ throw e;
+ }
// Expire responses with set-cookie headers, so they do not get cached.
if (!response.getHeaders().contains(HttpHeader.EXPIRES))
@@ -402,7 +411,17 @@ static void putCookie(Response response, HttpCookie cookie)
Request request = response.getRequest();
HttpConfiguration httpConfiguration = request.getConnectionMetaData().getHttpConfiguration();
CookieCompliance compliance = httpConfiguration.getResponseCookieCompliance();
- HttpField setCookie = new HttpCookieUtils.SetCookieHttpField(HttpCookieUtils.checkSameSite(cookie, request.getContext()), compliance);
+
+ HttpField setCookie;
+ try
+ {
+ setCookie = new HttpCookieUtils.SetCookieHttpField(HttpCookieUtils.checkSameSite(cookie, request.getContext()), compliance);
+ }
+ catch (ComplianceViolationException e)
+ {
+ HttpChannel.from(request).getComplianceViolationListener().onComplianceViolation(e.getEvent());
+ throw e;
+ }
boolean expires = false;
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
index 2220fd636104..06b35d83832a 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java
@@ -16,7 +16,9 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.WritePendingException;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeoutException;
@@ -27,6 +29,7 @@
import java.util.function.Supplier;
import org.eclipse.jetty.http.BadMessageException;
+import org.eclipse.jetty.http.ComplianceViolation;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
@@ -118,6 +121,7 @@ private enum StreamSendState
private Throwable _callbackFailure;
private Attributes _cache;
private boolean _expects100Continue;
+ private ComplianceViolation.Listener _complianceViolationListener;
public HttpChannelState(ConnectionMetaData connectionMetaData)
{
@@ -127,6 +131,24 @@ public HttpChannelState(ConnectionMetaData connectionMetaData)
_writeInvoker = new HttpChannelSerializedInvoker();
}
+ @Override
+ public void initialize()
+ {
+ List listeners = _connectionMetaData.getHttpConfiguration().getComplianceViolationListeners();
+ _complianceViolationListener = switch (listeners.size())
+ {
+ case 0 -> ComplianceViolation.Listener.NOOP;
+ case 1 -> listeners.get(0).initialize();
+ default -> new InitializedCompositeComplianceViolationListener(listeners);
+ };
+ }
+
+ @Override
+ public ComplianceViolation.Listener getComplianceViolationListener()
+ {
+ return _complianceViolationListener;
+ }
+
@Override
public void recycle()
{
@@ -157,6 +179,7 @@ public void recycle()
_onFailure = null;
_callbackFailure = null;
_expects100Continue = false;
+ _complianceViolationListener = null;
}
}
@@ -572,7 +595,7 @@ public void run()
HttpURI uri = request.getHttpURI();
if (uri.hasViolations())
{
- String badMessage = UriCompliance.checkUriCompliance(getConnectionMetaData().getHttpConfiguration().getUriCompliance(), uri);
+ String badMessage = UriCompliance.checkUriCompliance(getConnectionMetaData().getHttpConfiguration().getUriCompliance(), uri, HttpChannel.from(request).getComplianceViolationListener());
if (badMessage != null)
throw new BadMessageException(badMessage);
}
@@ -1742,4 +1765,96 @@ protected void onError(Runnable task, Throwable failure)
failureTask.run();
}
}
+
+ /**
+ * A Listener that represents multiple user {@link ComplianceViolation.Listener} instances
+ */
+ private static class InitializedCompositeComplianceViolationListener implements ComplianceViolation.Listener
+ {
+ private static final Logger LOG = LoggerFactory.getLogger(InitializedCompositeComplianceViolationListener.class);
+ private final List _listeners;
+
+ /**
+ * Construct a new ComplianceViolations that will initialize the list of listeners and notify events to all.
+ *
+ * @param unInitializedListeners the user listeners to initialized and notify. Null or empty list is not allowed..
+ */
+ public InitializedCompositeComplianceViolationListener(List unInitializedListeners)
+ {
+ List initialized = null;
+ for (ComplianceViolation.Listener listener : unInitializedListeners)
+ {
+ ComplianceViolation.Listener listening = listener.initialize();
+ if (listening != listener)
+ {
+ initialized = new ArrayList<>(unInitializedListeners.size());
+ for (ComplianceViolation.Listener l : unInitializedListeners)
+ {
+ if (l == listener)
+ break;
+ initialized.add(l);
+ }
+ }
+ if (initialized != null)
+ initialized.add(listening);
+ }
+
+ _listeners = initialized == null ? unInitializedListeners : initialized;
+ }
+
+ @Override
+ public void onRequestEnd(Attributes request)
+ {
+ for (ComplianceViolation.Listener listener : _listeners)
+ {
+ try
+ {
+ listener.onRequestEnd(request);
+ }
+ catch (Exception e)
+ {
+ LOG.warn("Unable to notify ComplianceViolation.Listener implementation at {} of onRequestEnd {}", listener, request, e);
+ }
+ }
+ }
+
+ @Override
+ public void onRequestBegin(Attributes request)
+ {
+ for (ComplianceViolation.Listener listener : _listeners)
+ {
+ try
+ {
+ listener.onRequestBegin(request);
+ }
+ catch (Exception e)
+ {
+ LOG.warn("Unable to notify ComplianceViolation.Listener implementation at {} of onRequestBegin {}", listener, request, e);
+ }
+ }
+ }
+
+ @Override
+ public ComplianceViolation.Listener initialize()
+ {
+ throw new IllegalStateException("already initialized");
+ }
+
+ @Override
+ public void onComplianceViolation(ComplianceViolation.Event event)
+ {
+ assert event != null;
+ for (ComplianceViolation.Listener listener : _listeners)
+ {
+ try
+ {
+ listener.onComplianceViolation(event);
+ }
+ catch (Exception e)
+ {
+ LOG.warn("Unable to notify ComplianceViolation.Listener implementation at {} of event {}", listener, event, e);
+ }
+ }
+ }
+ }
}
diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpConnection.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpConnection.java
index 7035d901d2e1..1d938ee8f6f2 100644
--- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpConnection.java
+++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpConnection.java
@@ -16,7 +16,6 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.WritePendingException;
-import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -100,7 +99,6 @@ public class HttpConnection extends AbstractMetaDataConnection implements Runnab
private final Lazy _attributes = new Lazy();
private final DemandContentCallback _demandContentCallback = new DemandContentCallback();
private final SendCallback _sendCallback = new SendCallback();
- private final boolean _recordHttpComplianceViolations;
private final LongAdder bytesIn = new LongAdder();
private final LongAdder bytesOut = new LongAdder();
private final AtomicBoolean _handling = new AtomicBoolean(false);
@@ -133,7 +131,16 @@ protected static HttpConnection setCurrentConnection(HttpConnection connection)
return last;
}
+ /**
+ * @deprecated use {@link #HttpConnection(HttpConfiguration, Connector, EndPoint)} instead. Will be removed in Jetty 12.1.0
+ */
+ @Deprecated(since = "12.0.6", forRemoval = true)
public HttpConnection(HttpConfiguration configuration, Connector connector, EndPoint endPoint, boolean recordComplianceViolations)
+ {
+ this(configuration, connector, endPoint);
+ }
+
+ public HttpConnection(HttpConfiguration configuration, Connector connector, EndPoint endPoint)
{
super(connector, configuration, endPoint);
_id = __connectionIdGenerator.getAndIncrement();
@@ -142,7 +149,6 @@ public HttpConnection(HttpConfiguration configuration, Connector connector, EndP
_httpChannel = newHttpChannel(connector.getServer(), configuration);
_requestHandler = newRequestHandler();
_parser = newHttpParser(configuration.getHttpCompliance());
- _recordHttpComplianceViolations = recordComplianceViolations;
if (LOG.isDebugEnabled())
LOG.debug("New HTTP Connection {}", this);
}
@@ -153,9 +159,13 @@ public InvocationType getInvocationType()
return getServer().getInvocationType();
}
+ /**
+ * @deprecated No replacement, no longer used within {@link HttpConnection}, will be removed in Jetty 12.1.0
+ */
+ @Deprecated(since = "12.0.6", forRemoval = true)
public boolean isRecordHttpComplianceViolations()
{
- return _recordHttpComplianceViolations;
+ return false;
}
protected HttpGenerator newHttpGenerator()
@@ -305,30 +315,6 @@ public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers)
_useOutputDirectByteBuffers = useOutputDirectByteBuffers;
}
- protected void onComplianceViolation(ComplianceViolation.Mode mode, ComplianceViolation violation, String details)
- {
- //TODO configure this somewhere else
- //TODO what about cookie compliance
- //TODO what about http2 & 3
- //TODO test this in core
- if (isRecordHttpComplianceViolations())
- {
- HttpStreamOverHTTP1 stream = _stream.get();
- if (stream != null)
- {
- if (stream._complianceViolations == null)
- {
- stream._complianceViolations = new ArrayList<>();
- }
- String record = String.format("%s (see %s) in mode %s for %s in %s",
- violation.getDescription(), violation.getURL(), mode, details, HttpConnection.this);
- stream._complianceViolations.add(record);
- if (LOG.isDebugEnabled())
- LOG.debug(record);
- }
- }
- }
-
@Override
public ByteBuffer onUpgradeFrom()
{
@@ -577,6 +563,7 @@ private boolean parseRequestBuffer()
if (_parser.isTerminated())
throw new RuntimeIOException("Parser is terminated");
+
boolean handle = _parser.parseNext(_retainableByteBuffer == null ? BufferUtil.EMPTY_BUFFER : _retainableByteBuffer.getByteBuffer());
if (LOG.isDebugEnabled())
@@ -933,10 +920,16 @@ public String toString()
}
}
- protected class RequestHandler implements HttpParser.RequestHandler, ComplianceViolation.Listener
+ protected class RequestHandler implements HttpParser.RequestHandler
{
private Throwable _failure;
+ @Override
+ public void messageBegin()
+ {
+ _httpChannel.initialize();
+ }
+
@Override
public void startRequest(String method, String uri, HttpVersion version)
{
@@ -983,6 +976,12 @@ public boolean contentComplete()
return false;
}
+ @Override
+ public void onViolation(ComplianceViolation.Event event)
+ {
+ getHttpChannel().getComplianceViolationListener().onComplianceViolation(event);
+ }
+
@Override
public void parsedTrailer(HttpField field)
{
@@ -1001,6 +1000,8 @@ public boolean messageComplete()
stream._chunk = new Trailers(_trailers.asImmutable());
else
stream._chunk = Content.Chunk.EOF;
+
+ getHttpChannel().getComplianceViolationListener().onRequestBegin(getHttpChannel().getRequest());
return false;
}
@@ -1010,6 +1011,8 @@ public void badMessage(HttpException failure)
if (LOG.isDebugEnabled())
LOG.debug("badMessage {} {}", HttpConnection.this, failure);
+ getHttpChannel().getComplianceViolationListener().onRequestEnd(getHttpChannel().getRequest());
+
_failure = (Throwable)failure;
_generator.setPersistent(false);
@@ -1070,12 +1073,6 @@ public void earlyEOF()
getServer().getThreadPool().execute(todo);
}
}
-
- @Override
- public void onComplianceViolation(ComplianceViolation.Mode mode, ComplianceViolation violation, String details)
- {
- HttpConnection.this.onComplianceViolation(mode, violation, details);
- }
}
protected class HttpStreamOverHTTP1 implements HttpStream
@@ -1184,12 +1181,12 @@ public Runnable headerComplete()
if (_uri.hasViolations())
{
compliance = getHttpConfiguration().getUriCompliance();
- String badMessage = UriCompliance.checkUriCompliance(compliance, _uri);
+ String badMessage = UriCompliance.checkUriCompliance(compliance, _uri, getHttpChannel().getComplianceViolationListener());
if (badMessage != null)
throw new BadMessageException(badMessage);
}
- // Check host field matches the authority in the any absolute URI or is not blank
+ // Check host field matches the authority in the absolute URI or is not blank
if (_hostField != null)
{
if (_uri.isAbsolute())
@@ -1198,7 +1195,9 @@ public Runnable headerComplete()
{
HttpCompliance httpCompliance = getHttpConfiguration().getHttpCompliance();
if (httpCompliance.allows(MISMATCHED_AUTHORITY))
- onComplianceViolation(httpCompliance, MISMATCHED_AUTHORITY, _uri.asString());
+ {
+ getHttpChannel().getComplianceViolationListener().onComplianceViolation(new ComplianceViolation.Event(httpCompliance, MISMATCHED_AUTHORITY, _uri.asString()));
+ }
else
throw new BadMessageException("Authority!=Host");
}
@@ -1242,6 +1241,9 @@ public boolean is100ContinueExpected()
Runnable handle = _httpChannel.onRequest(_request);
++_requests;
+ Request request = _httpChannel.getRequest();
+ getHttpChannel().getComplianceViolationListener().onRequestBegin(request);
+
if (_complianceViolations != null && !_complianceViolations.isEmpty())
{
_httpChannel.getRequest().setAttribute(HttpCompliance.VIOLATIONS_ATTR, _complianceViolations);
diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/DelayedServerTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/DelayedServerTest.java
index 0047a14fc050..3a1ec920d762 100644
--- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/DelayedServerTest.java
+++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/DelayedServerTest.java
@@ -48,7 +48,7 @@ private static class DelayedHttpConnection extends HttpConnection
{
public DelayedHttpConnection(HttpConfiguration config, Connector connector, EndPoint endPoint)
{
- super(config, connector, endPoint, false);
+ super(config, connector, endPoint);
}
@Override
diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ExtendedServerTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ExtendedServerTest.java
index b5517412145a..7d9aa6812f80 100644
--- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ExtendedServerTest.java
+++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ExtendedServerTest.java
@@ -91,7 +91,7 @@ private static class ExtendedHttpConnection extends HttpConnection
{
public ExtendedHttpConnection(HttpConfiguration config, Connector connector, EndPoint endPoint)
{
- super(config, connector, endPoint, false);
+ super(config, connector, endPoint);
}
@Override
diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ForwardedRequestCustomizerTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ForwardedRequestCustomizerTest.java
index a2e154d19d97..2b8b97036a4d 100644
--- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ForwardedRequestCustomizerTest.java
+++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ForwardedRequestCustomizerTest.java
@@ -85,7 +85,7 @@ public void init() throws Exception
@Override
public Connection newConnection(Connector connector, EndPoint endPoint)
{
- HttpConnection connection = new HttpConnection(getHttpConfiguration(), connector, endPoint, isRecordHttpComplianceViolations())
+ HttpConnection connection = new HttpConnection(getHttpConfiguration(), connector, endPoint)
{
@Override
public SocketAddress getLocalSocketAddress()
diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
index 5e7b46a85c87..885bc213560c 100644
--- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
+++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
@@ -760,10 +760,23 @@ public void testHeadChunked() throws Exception
assertEquals(postHeaders, headHeaders);
}
+ @Test
+ public void testHostOnlyPort() throws Exception
+ {
+ String response;
+
+ response = _connector.getResponse("""
+ GET /foo HTTP/1.1
+ Host: :1234
+ Connection: close
+
+ """);
+ checkContains(response, 0, "HTTP/1.1 400");
+ }
+
@Test
public void testBadHostPort() throws Exception
{
- LOG.info("badMessage: Number formate exception expected ...");
String response;
response = _connector.getResponse("GET http://localhost:EXPECTED_NUMBER_FORMAT_EXCEPTION/ HTTP/1.1\r\n" +
diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseCompleteTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseCompleteTest.java
index e6e1d003aa77..482cfe2a1e5c 100644
--- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseCompleteTest.java
+++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseCompleteTest.java
@@ -129,7 +129,7 @@ public void testHandleCallbackCompleting(boolean throwFromHandler) throws Except
@Override
public Connection newConnection(Connector connector, EndPoint endPoint)
{
- HttpConnection connection = new HttpConnection(getHttpConfiguration(), connector, endPoint, isRecordHttpComplianceViolations())
+ HttpConnection connection = new HttpConnection(getHttpConfiguration(), connector, endPoint)
{
@Override
protected HttpStreamOverHTTP1 newHttpStream(String method, String uri, HttpVersion version)
diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ServerHttpCookieTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ServerHttpCookieTest.java
index a8575d5dbcd3..d22f562142dc 100644
--- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ServerHttpCookieTest.java
+++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ServerHttpCookieTest.java
@@ -16,6 +16,7 @@
import java.util.List;
import java.util.stream.Stream;
+import org.eclipse.jetty.http.ComplianceViolation;
import org.eclipse.jetty.http.CookieCompliance;
import org.eclipse.jetty.http.CookieParser;
import org.eclipse.jetty.http.HttpCookie;
@@ -63,14 +64,9 @@ public boolean handle(Request request, Response response, Callback callback) thr
Fields.Field setCookie = parameters.get("SetCookie");
if (setCookie != null)
{
- CookieParser parser = CookieParser.newParser(new CookieParser.Handler()
- {
- @Override
- public void addCookie(String name, String value, int version, String domain, String path, String comment)
- {
- Response.addCookie(response, HttpCookie.build(name, value, version).domain(domain).path(path).comment(comment).build());
- }
- }, RFC2965, null);
+ ComplianceViolation.Listener complianceViolationListener = HttpChannel.from(request).getComplianceViolationListener();
+ CookieParser parser = CookieParser.newParser((name, value, version, domain, path, comment) ->
+ Response.addCookie(response, HttpCookie.build(name, value, version).domain(domain).path(path).comment(comment).build()), RFC2965, complianceViolationListener);
parser.parseField(setCookie.getValue());
}
@@ -82,7 +78,9 @@ public void addCookie(String name, String value, int version, String domain, Str
return true;
}
});
- _httpConfiguration = _connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration();
+ HttpConnectionFactory httpConnectionFactory = _connector.getConnectionFactory(HttpConnectionFactory.class);
+ httpConnectionFactory.setRecordHttpComplianceViolations(true);
+ _httpConfiguration = httpConnectionFactory.getHttpConfiguration();
_server.start();
}
diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ServerTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ServerTest.java
index 60f5c817b331..a29615be93ef 100644
--- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ServerTest.java
+++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/ServerTest.java
@@ -63,7 +63,7 @@ public void prepare() throws Exception
@Override
public Connection newConnection(Connector connector, EndPoint endPoint)
{
- HttpConnection connection = new HttpConnection(getHttpConfiguration(), connector, endPoint, isRecordHttpComplianceViolations())
+ HttpConnection connection = new HttpConnection(getHttpConfiguration(), connector, endPoint)
{
@Override
protected HttpChannel newHttpChannel(Server server, HttpConfiguration configuration)
diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/SlowClientWithPipelinedRequestTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/SlowClientWithPipelinedRequestTest.java
index 9defb422283a..12069ad7e157 100644
--- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/SlowClientWithPipelinedRequestTest.java
+++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/SlowClientWithPipelinedRequestTest.java
@@ -47,7 +47,7 @@ public void startServer(Handler handler) throws Exception
@Override
public Connection newConnection(Connector connector, EndPoint endPoint)
{
- return configure(new HttpConnection(getHttpConfiguration(), connector, endPoint, isRecordHttpComplianceViolations())
+ return configure(new HttpConnection(getHttpConfiguration(), connector, endPoint)
{
@Override
public void onFillable()
diff --git a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/StopTest.java b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/StopTest.java
index 8406d6f1a72f..48bcf7e9a61b 100644
--- a/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/StopTest.java
+++ b/jetty-core/jetty-server/src/test/java/org/eclipse/jetty/server/StopTest.java
@@ -134,7 +134,7 @@ public void testSlowClose(long stopTimeout, long closeWait, Matcher stopTi
public Connection newConnection(Connector con, EndPoint endPoint)
{
// Slow closing connection
- HttpConnection conn = new HttpConnection(getHttpConfiguration(), con, endPoint, isRecordHttpComplianceViolations())
+ HttpConnection conn = new HttpConnection(getHttpConfiguration(), con, endPoint)
{
@Override
public void onClose(Throwable cause)
diff --git a/jetty-core/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java b/jetty-core/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java
index 30d73fa2f089..de9008af6a87 100644
--- a/jetty-core/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java
+++ b/jetty-core/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java
@@ -67,7 +67,7 @@ public class StartArgs
String tag = System.getProperty(JETTY_TAG_NAME_KEY);
// Use META-INF/MANIFEST.MF versions
- if (ver == null)
+ if (Utils.isBlank(ver))
{
ver = ManifestUtils.getManifest(StartArgs.class)
.map(Manifest::getMainAttributes)
diff --git a/jetty-core/jetty-unixdomain-server/src/main/config/modules/unixdomain-http.mod b/jetty-core/jetty-unixdomain-server/src/main/config/modules/unixdomain-http.mod
index 689b226e904f..1614cce12752 100644
--- a/jetty-core/jetty-unixdomain-server/src/main/config/modules/unixdomain-http.mod
+++ b/jetty-core/jetty-unixdomain-server/src/main/config/modules/unixdomain-http.mod
@@ -9,7 +9,7 @@ unixdomain
server
[lib]
-lib/jetty-unixdomain-server-*.jar
+lib/jetty-unixdomain-server-${jetty.version}.jar
[xml]
etc/jetty-unixdomain-http.xml
diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java
index 9023669ffff6..d304ef864ad2 100644
--- a/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java
+++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-common/src/main/java/org/eclipse/jetty/websocket/common/JettyWebSocketFrameHandler.java
@@ -145,15 +145,16 @@ public void onOpen(CoreSession coreSession, Callback callback)
container.notifySessionListeners((listener) -> listener.onWebSocketSessionOpened(session));
callback.succeeded();
+
+ if (openHandle != null)
+ autoDemand();
+ else
+ internalDemand();
}
catch (Throwable cause)
{
callback.failed(new WebSocketException(endpointInstance.getClass().getSimpleName() + " OPEN method error: " + cause.getMessage(), cause));
}
- finally
- {
- autoDemand();
- }
}
private static MessageSink createMessageSink(Class extends MessageSink> sinkClass, WebSocketSession session, MethodHandle msgHandle, boolean autoDemanding)
@@ -320,7 +321,7 @@ private void onPingFrame(Frame frame, Callback callback)
public void succeed()
{
callback.succeeded();
- autoDemand();
+ internalDemand();
}
@Override
@@ -328,6 +329,7 @@ public void fail(Throwable x)
{
// Ignore failures, we might be output closed and receive a PING.
callback.succeeded();
+ internalDemand();
}
});
}
@@ -355,7 +357,7 @@ private void onPongFrame(Frame frame, Callback callback)
}
else
{
- autoDemand();
+ internalDemand();
}
}
@@ -384,7 +386,7 @@ private void acceptFrame(Frame frame, Callback callback)
if (activeMessageSink == null)
{
callback.succeeded();
- autoDemand();
+ internalDemand();
return;
}
@@ -403,7 +405,12 @@ boolean isAutoDemand()
private void autoDemand()
{
if (isAutoDemand())
- session.getCoreSession().demand();
+ internalDemand();
+ }
+
+ private void internalDemand()
+ {
+ session.getCoreSession().demand();
}
public String toString()
diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/pom.xml b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/pom.xml
index c21c1f345ed4..a3533dcceb9b 100644
--- a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/pom.xml
+++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/pom.xml
@@ -17,6 +17,11 @@
+
+ org.awaitility
+ awaitility
+ test
+
org.eclipse.jetty
jetty-alpn-java-server
diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ExplicitDemandTest.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ExplicitDemandTest.java
index 9f6c5366c03f..49478cb482e3 100644
--- a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ExplicitDemandTest.java
+++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ExplicitDemandTest.java
@@ -15,12 +15,17 @@
import java.io.IOException;
import java.net.URI;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.websocket.api.Callback;
+import org.eclipse.jetty.websocket.api.Frame;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.client.WebSocketClient;
@@ -29,6 +34,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import static org.awaitility.Awaitility.await;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertNull;
@@ -55,9 +61,45 @@ public void onMessage(String message) throws IOException
}
}
+ @WebSocket(autoDemand = false)
+ public static class ListenerSocket implements Session.Listener
+ {
+ final List frames = new CopyOnWriteArrayList<>();
+
+ @Override
+ public void onWebSocketFrame(Frame frame, Callback callback)
+ {
+ frames.add(frame);
+ callback.succeed();
+ }
+ }
+
+ @WebSocket(autoDemand = false)
+ public static class PingSocket extends ListenerSocket
+ {
+ Session session;
+
+ @Override
+ public void onWebSocketOpen(Session session)
+ {
+ this.session = session;
+ session.demand();
+ }
+
+ @Override
+ public void onWebSocketFrame(Frame frame, Callback callback)
+ {
+ super.onWebSocketFrame(frame, callback);
+ if (frame.getType() == Frame.Type.TEXT)
+ session.sendPing(ByteBuffer.wrap("server-ping".getBytes(StandardCharsets.UTF_8)), Callback.NOOP);
+ }
+ }
+
private final Server server = new Server();
private final WebSocketClient client = new WebSocketClient();
private final SuspendSocket serverSocket = new SuspendSocket();
+ private final ListenerSocket listenerSocket = new ListenerSocket();
+ private final PingSocket pingSocket = new PingSocket();
private ServerConnector connector;
@BeforeEach
@@ -67,7 +109,11 @@ public void start() throws Exception
server.addConnector(connector);
WebSocketUpgradeHandler wsHandler = WebSocketUpgradeHandler.from(server, container ->
- container.addMapping("/suspend", (rq, rs, cb) -> serverSocket));
+ {
+ container.addMapping("/suspend", (rq, rs, cb) -> serverSocket);
+ container.addMapping("/listenerSocket", (rq, rs, cb) -> listenerSocket);
+ container.addMapping("/ping", (rq, rs, cb) -> pingSocket);
+ });
server.setHandler(wsHandler);
server.start();
@@ -114,4 +160,57 @@ public void testNoDemandWhenProcessingFrame() throws Exception
assertNull(clientSocket.error);
assertNull(serverSocket.error);
}
+
+ @Test
+ public void testNoAutoDemand() throws Exception
+ {
+ URI uri = new URI("ws://localhost:" + connector.getLocalPort() + "/listenerSocket");
+ ListenerSocket listenerSocket = new ListenerSocket();
+ Future connect = client.connect(listenerSocket, uri);
+ Session session = connect.get(5, TimeUnit.SECONDS);
+
+ session.sendPing(ByteBuffer.wrap("ping-0".getBytes(StandardCharsets.UTF_8)), Callback.NOOP);
+ session.sendText("test-text", Callback.NOOP);
+ session.sendPing(ByteBuffer.wrap("ping-1".getBytes(StandardCharsets.UTF_8)), Callback.NOOP);
+
+ await().atMost(5, TimeUnit.SECONDS).until(listenerSocket.frames::size, is(2));
+ Frame frame0 = listenerSocket.frames.get(0);
+ assertThat(frame0.getType(), is(Frame.Type.PONG));
+ assertThat(StandardCharsets.UTF_8.decode(frame0.getPayload()).toString(), is("ping-0"));
+ Frame frame1 = listenerSocket.frames.get(1);
+ assertThat(frame1.getType(), is(Frame.Type.PONG));
+ assertThat(StandardCharsets.UTF_8.decode(frame1.getPayload()).toString(), is("ping-1"));
+
+ session.close();
+ await().atMost(5, TimeUnit.SECONDS).until(listenerSocket.frames::size, is(3));
+ assertThat(listenerSocket.frames.get(2).getType(), is(Frame.Type.CLOSE));
+ }
+
+ @Test
+ public void testServerPing() throws Exception
+ {
+ URI uri = new URI("ws://localhost:" + connector.getLocalPort() + "/ping");
+ PingSocket pingSocket = new PingSocket();
+ Future connect = client.connect(pingSocket, uri);
+ Session session = connect.get(5, TimeUnit.SECONDS);
+
+ session.sendText("send-me-a-ping", Callback.NOOP);
+
+ await().atMost(5, TimeUnit.SECONDS).until(pingSocket.frames::size, is(1));
+ Frame frame = pingSocket.frames.get(0);
+ assertThat(frame.getType(), is(Frame.Type.PING));
+ assertThat(StandardCharsets.UTF_8.decode(frame.getPayload()).toString(), is("server-ping"));
+
+ session.sendText("send-me-another-ping", Callback.NOOP);
+
+ await().atMost(5, TimeUnit.SECONDS).until(pingSocket.frames::size, is(2));
+ frame = pingSocket.frames.get(1);
+ assertThat(frame.getType(), is(Frame.Type.PING));
+ assertThat(StandardCharsets.UTF_8.decode(frame.getPayload()).toString(), is("server-ping"));
+
+ session.close();
+ await().atMost(5, TimeUnit.SECONDS).until(pingSocket.frames::size, is(3));
+ frame = pingSocket.frames.get(2);
+ assertThat(frame.getType(), is(Frame.Type.CLOSE));
+ }
}
diff --git a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/FrameListenerTest.java b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/FrameListenerTest.java
index da733b12c554..6378deb4128e 100644
--- a/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/FrameListenerTest.java
+++ b/jetty-core/jetty-websocket/jetty-websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/FrameListenerTest.java
@@ -129,14 +129,12 @@ public void testPartialText() throws Exception
public static class FrameEndpoint implements Session.Listener
{
- public Session session;
public CountDownLatch closeLatch = new CountDownLatch(1);
public LinkedBlockingQueue frameEvents = new LinkedBlockingQueue<>();
@Override
public void onWebSocketOpen(Session session)
{
- this.session = session;
session.demand();
}
@@ -149,7 +147,6 @@ public void onWebSocketFrame(Frame frame, Callback callback)
BufferUtil.toUTF8String(frame.getPayload()),
frame.getPayloadLength()));
callback.succeed();
- session.demand();
}
@Override
diff --git a/jetty-ee10/jetty-ee10-annotations/src/main/config/modules/ee10-annotations.mod b/jetty-ee10/jetty-ee10-annotations/src/main/config/modules/ee10-annotations.mod
index b617100a5dca..c3b24d3716f2 100644
--- a/jetty-ee10/jetty-ee10-annotations/src/main/config/modules/ee10-annotations.mod
+++ b/jetty-ee10/jetty-ee10-annotations/src/main/config/modules/ee10-annotations.mod
@@ -9,9 +9,17 @@ ee10
[depend]
ee10-plus
+[ini]
+ee10.asm.version?=@asm.version@
+ee10.jakarta.annotation.api.version?=@jakarta.annotation.api.version@
+
[lib]
lib/jetty-ee10-annotations-${jetty.version}.jar
-lib/ee10-annotations/*.jar
+lib/ee10-annotations/asm-${ee10.asm.version}.jar
+lib/ee10-annotations/asm-analysis-${ee10.asm.version}.jar
+lib/ee10-annotations/asm-commons-${ee10.asm.version}.jar
+lib/ee10-annotations/asm-tree-${ee10.asm.version}.jar
+lib/ee10-annotations/jakarta.annotation-api-${ee10.jakarta.annotation.api.version}.jar
[jpms]
add-modules:org.objectweb.asm
diff --git a/jetty-ee10/jetty-ee10-apache-jsp/src/main/config/modules/ee10-apache-jsp.mod b/jetty-ee10/jetty-ee10-apache-jsp/src/main/config/modules/ee10-apache-jsp.mod
index 77cce3ca8abe..e06a1bddb299 100644
--- a/jetty-ee10/jetty-ee10-apache-jsp/src/main/config/modules/ee10-apache-jsp.mod
+++ b/jetty-ee10/jetty-ee10-apache-jsp/src/main/config/modules/ee10-apache-jsp.mod
@@ -10,7 +10,17 @@ ee10
ee10-servlet
ee10-annotations
+[ini]
+ee10.jakarta.el.api.version?=@jakarta.el.api.version@
+ee10.jakarta.servlet.jsp.api.version?=@jakarta.servlet.jsp.api.version@
+eclipse.jdt.ecj.version?=@eclipse.jdt.ecj.version@
+ee10.jsp.impl.version?=@jsp.impl.version@
+
[lib]
-lib/ee10-apache-jsp/*.jar
+lib/ee10-apache-jsp/jakarta.el.jakarta.el-api-${ee10.jakarta.el.api.version}.jar
+lib/ee10-apache-jsp/jakarta.servlet.jsp.jakarta.servlet.jsp-api-${ee10.jakarta.servlet.jsp.api.version}.jar
+lib/ee10-apache-jsp/org.eclipse.jdt.ecj-${eclipse.jdt.ecj.version}.jar
+lib/ee10-apache-jsp/org.mortbay.jasper.apache-el-${ee10.jsp.impl.version}.jar
+lib/ee10-apache-jsp/org.mortbay.jasper.apache-jsp-${ee10.jsp.impl.version}.jar
lib/jetty-ee10-apache-jsp-${jetty.version}.jar
diff --git a/jetty-ee10/jetty-ee10-glassfish-jstl/src/main/config/modules/ee10-glassfish-jstl.mod b/jetty-ee10/jetty-ee10-glassfish-jstl/src/main/config/modules/ee10-glassfish-jstl.mod
index 4bb6bb07b567..18405bc1b141 100644
--- a/jetty-ee10/jetty-ee10-glassfish-jstl/src/main/config/modules/ee10-glassfish-jstl.mod
+++ b/jetty-ee10/jetty-ee10-glassfish-jstl/src/main/config/modules/ee10-glassfish-jstl.mod
@@ -9,5 +9,10 @@ ee10
[depends]
ee10-apache-jsp
+[ini]
+ee10.jakarta.servlet.jsp.jstl.api.version?=@jakarta.servlet.jsp.jstl.api.version@
+ee10.jakarta.servlet.jsp.jstl.impl.version?=@jakarta.servlet.jsp.jstl.impl.version@
+
[lib]
-lib/ee10-glassfish-jstl/*.jar
+lib/ee10-glassfish-jstl/jakarta.servlet.jsp.jstl.jakarta.servlet.jsp.jstl-api-${ee10.jakarta.servlet.jsp.jstl.api.version}.jar
+lib/ee10-glassfish-jstl/org.glassfish.web.jakarta.servlet.jsp.jstl-${ee10.jakarta.servlet.jsp.jstl.impl.version}.jar
diff --git a/jetty-ee10/jetty-ee10-jaspi/src/main/config/modules/ee10-jaspi.mod b/jetty-ee10/jetty-ee10-jaspi/src/main/config/modules/ee10-jaspi.mod
index f463d213d06c..9b65c07c0b9c 100644
--- a/jetty-ee10/jetty-ee10-jaspi/src/main/config/modules/ee10-jaspi.mod
+++ b/jetty-ee10/jetty-ee10-jaspi/src/main/config/modules/ee10-jaspi.mod
@@ -13,9 +13,12 @@ security
ee10-security
auth-config-factory
+[ini]
+ee10.jakarta.authentication.api.version?=@jakarta.authentication.api.version@
+
[lib]
lib/jetty-ee10-jaspi-${jetty.version}.jar
-lib/ee10-jaspi/*.jar
+lib/ee10-jaspi/jakarta.authentication-api-${ee10.jakarta.authentication.api.version}.jar
[xml]
etc/jaspi/jetty-ee10-jaspi-authmoduleconfig.xml
diff --git a/jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/ee10-maven.mod b/jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/ee10-maven.mod
index 8920fbe1cd81..344d4aaaa825 100644
--- a/jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/ee10-maven.mod
+++ b/jetty-ee10/jetty-ee10-maven-plugin/src/main/resources/ee10-maven.mod
@@ -12,7 +12,7 @@ ee10-webapp
ee10-annotations
[lib]
-lib/maven-ee10/**.jar
+lib/maven-ee10/*.jar
[xml]
etc/jetty-ee10-maven.xml
diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty-deploy.xml b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty-deploy.xml
index 88fc1a79e598..e630c9a766e1 100644
--- a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty-deploy.xml
+++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty-deploy.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty-http.xml b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty-http.xml
index bbd9ab19ed56..cbf909a9518b 100644
--- a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty-http.xml
+++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty-http.xml
@@ -1,5 +1,5 @@
-
+
@@ -20,7 +20,7 @@
-
+
diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty.xml b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty.xml
index 5d006b44c9af..ed1e4f3433e4 100644
--- a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty.xml
+++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/jettyhome/etc/jetty.xml
@@ -1,5 +1,5 @@
-
+
@@ -24,45 +24,34 @@
-
-
-
- -
-
-
- -
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
+
+
java.naming.factory.initial
diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-connector-listener.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-connector-listener.xml
new file mode 100644
index 000000000000..de169be5ef0f
--- /dev/null
+++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http-connector-listener.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [
+
+
+
+ boot.http.port
+
+
+
+ ]
+
+
diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http.xml b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http.xml
deleted file mode 100644
index 98f6d4e56d25..000000000000
--- a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/config/etc/jetty-http.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- foo.foo
-
-
-
-
-
-
-
-
-
-
-
diff --git a/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestJettyOSGiBootWithBundleJettyHome.java b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestJettyOSGiBootWithBundleJettyHome.java
new file mode 100644
index 000000000000..d2bfb7afd8fb
--- /dev/null
+++ b/jetty-ee10/jetty-ee10-osgi/test-jetty-ee10-osgi/src/test/java/org/eclipse/jetty/ee10/osgi/test/TestJettyOSGiBootWithBundleJettyHome.java
@@ -0,0 +1,92 @@
+//
+// ========================================================================
+// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
+//
+// This program and the accompanying materials are made available under the
+// terms of the Eclipse Public License v. 2.0 which is available at
+// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+// which is available at https://www.apache.org/licenses/LICENSE-2.0.
+//
+// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+// ========================================================================
+//
+
+package org.eclipse.jetty.ee10.osgi.test;
+
+import java.util.ArrayList;
+import javax.inject.Inject;
+
+import org.eclipse.jetty.client.ContentResponse;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.http.HttpStatus;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.CoreOptions;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.BundleContext;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+
+/**
+ * Pax-Exam to make sure the jetty-ee10-osgi-boot can be started along with the
+ * httpservice web-bundle. Then make sure we can deploy an OSGi service on the
+ * top of this.
+ */
+@RunWith(PaxExam.class)
+public class TestJettyOSGiBootWithBundleJettyHome
+{
+ @Inject
+ BundleContext bundleContext = null;
+
+ @Configuration
+ public static Option[] configure()
+ {
+ ArrayList
""".replace("$P", String.valueOf(fcgiPort)), StandardOpenOption.CREATE);
- int httpPort = distribution.freePort();
+ int httpPort = Tester.freePort();
try (JettyHomeTester.Run run2 = distribution.start("jetty.http.port=" + httpPort, "etc/fcgi-connector.xml"))
{
assertTrue(run2.awaitConsoleLogsFor("Started oejs.Server@", START_TIMEOUT, TimeUnit.SECONDS));
@@ -1395,7 +1410,7 @@ public void testEEFastCGIProxying(String env) throws Exception
Files.writeString(jettyLogging, loggingConfig, StandardOpenOption.TRUNCATE_EXISTING);
// Add a FastCGI connector to simulate, for example, php-fpm.
- int fcgiPort = distribution.freePort();
+ int fcgiPort = Tester.freePort();
Path jettyBaseEtc = jettyBase.resolve("etc");
Files.createDirectories(jettyBaseEtc);
Path fcgiConnectorXML = jettyBaseEtc.resolve("fcgi-connector.xml");
@@ -1476,7 +1491,7 @@ public void testEEFastCGIProxying(String env) throws Exception
environment=$ENV
""".replace("$ENV", env), StandardOpenOption.CREATE);
- int httpPort = distribution.freePort();
+ int httpPort = Tester.freePort();
try (JettyHomeTester.Run run2 = distribution.start("jetty.http.port=" + httpPort, "etc/fcgi-connector.xml"))
{
assertTrue(run2.awaitConsoleLogsFor("Started oejs.Server@", START_TIMEOUT, TimeUnit.SECONDS));
@@ -1507,7 +1522,7 @@ public void testVirtualThreadPoolPreview() throws Exception
assertTrue(run1.awaitFor(10, TimeUnit.SECONDS));
assertEquals(0, run1.getExitValue());
- int httpPort = distribution.freePort();
+ int httpPort = Tester.freePort();
try (JettyHomeTester.Run run2 = distribution.start(List.of("jetty.http.selectors=1", "jetty.http.port=" + httpPort)))
{
assertTrue(run2.awaitConsoleLogsFor("Started oejs.Server@", 10, TimeUnit.SECONDS));
@@ -1537,7 +1552,7 @@ public void testVirtualThreadPool() throws Exception
assertTrue(run1.awaitFor(START_TIMEOUT, TimeUnit.SECONDS));
assertEquals(0, run1.getExitValue());
- int httpPort = distribution.freePort();
+ int httpPort = Tester.freePort();
try (JettyHomeTester.Run run2 = distribution.start(List.of("jetty.http.selectors=1", "jetty.http.port=" + httpPort)))
{
assertTrue(run2.awaitConsoleLogsFor("Started oejs.Server@", START_TIMEOUT, TimeUnit.SECONDS));
@@ -1580,7 +1595,7 @@ public void testRangeRequestMultiPartRangeResponse(String env) throws Exception
""";
Files.writeString(jettyLogging, loggingConfig, StandardOpenOption.TRUNCATE_EXISTING);
- int httpPort = distribution.freePort();
+ int httpPort = Tester.freePort();
String contextPath = "/" + toEnvironment("demo-simple", env);
try (JettyHomeTester.Run run2 = distribution.start(List.of("jetty.http.selectors=1", "jetty.http.port=" + httpPort)))
{
@@ -1618,7 +1633,7 @@ public void testXmlDeployWarNotInWebapps(String env) throws Exception
.jettyBase(jettyBase)
.build();
- int httpPort = distribution.freePort();
+ int httpPort = Tester.freePort();
String[] argsConfig = {
"--add-modules=http," + toEnvironment("deploy", env) + "," + toEnvironment("webapp", env)
@@ -1635,11 +1650,11 @@ public void testXmlDeployWarNotInWebapps(String env) throws Exception
};
// Put war into ${jetty.base}/wars/ directory
- File srcWar = distribution.resolveArtifact("org.eclipse.jetty." + env + ".demos:jetty-" + env + "-demo-simple-webapp:war:" + jettyVersion);
+ Path srcWar = distribution.resolveArtifact("org.eclipse.jetty." + env + ".demos:jetty-" + env + "-demo-simple-webapp:war:" + jettyVersion);
Path warsDir = jettyBase.resolve("wars");
FS.ensureDirExists(warsDir);
Path destWar = warsDir.resolve("demo.war");
- Files.copy(srcWar.toPath(), destWar);
+ Files.copy(srcWar, destWar);
// Create XML for deployable
String xml = """
@@ -1702,7 +1717,7 @@ public void testInetAccessHandler() throws Exception
assertTrue(run1.awaitFor(10, TimeUnit.SECONDS));
assertEquals(0, run1.getExitValue());
- int httpPort = distribution.freePort();
+ int httpPort = Tester.freePort();
List args = List.of(
"jetty.inetaccess.exclude=|/excludedPath/*",
"jetty.http.port=" + httpPort);
@@ -1726,6 +1741,64 @@ public void testInetAccessHandler() throws Exception
}
}
+ @Test
+ public void testSendDateHeader() throws Exception
+ {
+ Path jettyBase = newTestJettyBaseDirectory();
+ String jettyVersion = System.getProperty("jettyVersion");
+ JettyHomeTester distribution = JettyHomeTester.Builder.newInstance()
+ .jettyVersion(jettyVersion)
+ .jettyBase(jettyBase)
+ .build();
+
+ try (JettyHomeTester.Run run1 = distribution.start("--add-modules=http"))
+ {
+ assertTrue(run1.awaitFor(10, TimeUnit.SECONDS));
+ assertEquals(0, run1.getExitValue());
+
+ int httpPort = distribution.freePort();
+ List args = List.of(
+ "jetty.http.port=" + httpPort,
+ "jetty.httpConfig.sendDateHeader=true"
+ );
+ try (JettyHomeTester.Run run2 = distribution.start(args))
+ {
+ assertTrue(run2.awaitConsoleLogsFor("Started oejs.Server@", START_TIMEOUT, TimeUnit.SECONDS));
+ startHttpClient();
+
+ List hostHeaders = new ArrayList<>();
+ hostHeaders.add("localhost");
+ hostHeaders.add("127.0.0.1");
+ try
+ {
+ InetAddress localhost = InetAddress.getLocalHost();
+ hostHeaders.add(localhost.getHostName());
+ hostHeaders.add(localhost.getHostAddress());
+ }
+ catch (UnknownHostException e)
+ {
+ LOG.debug("Unable to obtain InetAddress.LocalHost", e);
+ }
+
+ for (String hostHeader: hostHeaders)
+ {
+ ContentResponse response = client.newRequest("http://" + hostHeader + ":" + httpPort + "/")
+ .timeout(15, TimeUnit.SECONDS)
+ .send();
+ assertEquals(HttpStatus.NOT_FOUND_404, response.getStatus());
+ String date = response.getHeaders().get(HttpHeader.DATE);
+ String msg = "Request to [%s]: Response Header [Date]".formatted(hostHeader);
+ assertThat(msg, date, notNullValue());
+ // asserting an exact value is tricky as the Date header is dynamic,
+ // so we just assert that it has some content and isn't blank
+ assertTrue(StringUtil.isNotBlank(date), msg);
+ assertThat(msg, date, containsString(","));
+ assertThat(msg, date, containsString(":"));
+ }
+ }
+ }
+ }
+
@Test
public void testCrossOriginModule() throws Exception
{
@@ -1739,7 +1812,7 @@ public void testCrossOriginModule() throws Exception
run1.awaitFor(START_TIMEOUT, TimeUnit.SECONDS);
assertThat(run1.getExitValue(), is(0));
- int httpPort1 = distribution.freePort();
+ int httpPort1 = Tester.freePort();
try (JettyHomeTester.Run run2 = distribution.start(List.of("jetty.http.port=" + httpPort1)))
{
assertThat(run2.awaitConsoleLogsFor("Started oejs.Server", START_TIMEOUT, TimeUnit.SECONDS), is(true));
@@ -1756,7 +1829,7 @@ public void testCrossOriginModule() throws Exception
assertTrue(response.getHeaders().contains(HttpHeader.ACCESS_CONTROL_ALLOW_ORIGIN));
}
- int httpPort2 = distribution.freePort();
+ int httpPort2 = Tester.freePort();
List args = List.of(
"jetty.http.port=" + httpPort2,
// Allow a different origin.
diff --git a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DynamicListenerTests.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DynamicListenerTests.java
index 0c0748c4e636..1d94802a5962 100644
--- a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DynamicListenerTests.java
+++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/DynamicListenerTests.java
@@ -18,7 +18,8 @@
import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.tests.hometester.JettyHomeTester;
+import org.eclipse.jetty.tests.testers.JettyHomeTester;
+import org.eclipse.jetty.tests.testers.Tester;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@@ -53,7 +54,7 @@ public void testSimpleWebAppWithJSP(String env) throws Exception
assertEquals(0, run1.getExitValue());
}
- int port = distribution.freePort();
+ int port = Tester.freePort();
try (JettyHomeTester.Run run2 = distribution.start("jetty.http.port=" + port))
{
assertTrue(run2.awaitConsoleLogsFor("Started oejs.Server@", START_TIMEOUT, TimeUnit.SECONDS));
diff --git a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/GzipModuleTests.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/GzipModuleTests.java
index 352b63f06d9f..7a100b1280b5 100644
--- a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/GzipModuleTests.java
+++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/GzipModuleTests.java
@@ -13,14 +13,14 @@
package org.eclipse.jetty.tests.distribution;
-import java.io.File;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.tests.hometester.JettyHomeTester;
+import org.eclipse.jetty.tests.testers.JettyHomeTester;
+import org.eclipse.jetty.tests.testers.Tester;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@@ -46,7 +46,7 @@ public void testGzipDefault(String env) throws Exception
.jettyBase(jettyBase)
.build();
- int httpPort = distribution.freePort();
+ int httpPort = Tester.freePort();
String[] argsConfig = {
"--add-modules=http,gzip," + toEnvironment("deploy", env) + "," + toEnvironment("webapp", env)
@@ -61,8 +61,8 @@ public void testGzipDefault(String env) throws Exception
"jetty.http.port=" + httpPort
};
- File war = distribution.resolveArtifact("org.eclipse.jetty." + env + ".demos:jetty-" + env + "-demo-simple-webapp:war:" + jettyVersion);
- distribution.installWarFile(war, "demo");
+ Path war = distribution.resolveArtifact("org.eclipse.jetty." + env + ".demos:jetty-" + env + "-demo-simple-webapp:war:" + jettyVersion);
+ distribution.installWar(war, "demo");
try (JettyHomeTester.Run runStart = distribution.start(argsStart))
{
@@ -88,7 +88,7 @@ public void testGzipDefaultExcludedMimeType(String env) throws Exception
.jettyBase(jettyBase)
.build();
- int httpPort = distribution.freePort();
+ int httpPort = Tester.freePort();
String[] argsConfig = {
"--add-modules=gzip,http," + toEnvironment("deploy", env) + "," + toEnvironment("webapp", env)
@@ -103,8 +103,8 @@ public void testGzipDefaultExcludedMimeType(String env) throws Exception
"jetty.http.port=" + httpPort
};
- File war = distribution.resolveArtifact("org.eclipse.jetty." + env + ".demos:jetty-" + env + "-demo-simple-webapp:war:" + jettyVersion);
- distribution.installWarFile(war, "demo");
+ Path war = distribution.resolveArtifact("org.eclipse.jetty." + env + ".demos:jetty-" + env + "-demo-simple-webapp:war:" + jettyVersion);
+ distribution.installWar(war, "demo");
try (JettyHomeTester.Run runStart = distribution.start(argsStart))
{
@@ -131,7 +131,7 @@ public void testGzipAddWebappSpecificExcludeMimeType(String env) throws Exceptio
.jettyBase(jettyBase)
.build();
- int httpPort = distribution.freePort();
+ int httpPort = Tester.freePort();
String[] argsConfig = {
"--add-modules=gzip,http," + toEnvironment("deploy", env) + "," + toEnvironment("webapp", env)
@@ -147,8 +147,8 @@ public void testGzipAddWebappSpecificExcludeMimeType(String env) throws Exceptio
"jetty.gzip.excludedMimeTypeList=image/vnd.microsoft.icon"
};
- File war = distribution.resolveArtifact("org.eclipse.jetty." + env + " .demos:jetty-" + env + "-demo-simple-webapp:war:" + jettyVersion);
- distribution.installWarFile(war, "demo");
+ Path war = distribution.resolveArtifact("org.eclipse.jetty." + env + " .demos:jetty-" + env + "-demo-simple-webapp:war:" + jettyVersion);
+ distribution.installWar(war, "demo");
try (JettyHomeTester.Run runStart = distribution.start(argsStart))
{
diff --git a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/LoggingOptionsTests.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/LoggingOptionsTests.java
index e51359ffad40..34a50de73128 100644
--- a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/LoggingOptionsTests.java
+++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/LoggingOptionsTests.java
@@ -13,7 +13,6 @@
package org.eclipse.jetty.tests.distribution;
-import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
@@ -24,7 +23,8 @@
import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.tests.hometester.JettyHomeTester;
+import org.eclipse.jetty.tests.testers.JettyHomeTester;
+import org.eclipse.jetty.tests.testers.Tester;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
@@ -169,10 +169,10 @@ public void testLoggingConfiguration(String env, String loggingModules, List(Collections.singletonList("jetty.http.port=" + port));
args.addAll(getSecondStartExtraArgs());
argsStart = args.toArray(new String[0]);
diff --git a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/session/HazelcastSessionDistributionTests.java b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/session/HazelcastSessionDistributionTests.java
index 4fe9d1150f0f..2a3afa57778b 100644
--- a/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/session/HazelcastSessionDistributionTests.java
+++ b/tests/test-distribution/test-distribution-common/src/test/java/org/eclipse/jetty/tests/distribution/session/HazelcastSessionDistributionTests.java
@@ -13,7 +13,6 @@
package org.eclipse.jetty.tests.distribution.session;
-import java.io.File;
import java.io.OutputStream;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
@@ -29,7 +28,8 @@
import org.eclipse.jetty.client.ContentResponse;
import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.tests.hometester.JettyHomeTester;
+import org.eclipse.jetty.tests.testers.JettyHomeTester;
+import org.eclipse.jetty.tests.testers.Tester;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
@@ -161,10 +161,10 @@ public void testHazelcastRemoteAndPartOfCluster() throws Exception
assertTrue(run1.awaitFor(START_TIMEOUT, TimeUnit.SECONDS));
assertEquals(0, run1.getExitValue());
- File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-session-webapp:war:" + jettyVersion);
- distribution.installWarFile(war, "test");
+ Path war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-session-webapp:war:" + jettyVersion);
+ distribution.installWar(war, "test");
- int port = distribution.freePort();
+ int port = Tester.freePort();
List argsStart = Arrays.asList(
"jetty.http.port=" + port,
"jetty.session.hazelcast.onlyClient=false",
diff --git a/tests/test-distribution/test-ee10-distribution/pom.xml b/tests/test-distribution/test-ee10-distribution/pom.xml
index 5be75047bdd0..3c664deea446 100644
--- a/tests/test-distribution/test-ee10-distribution/pom.xml
+++ b/tests/test-distribution/test-ee10-distribution/pom.xml
@@ -13,41 +13,9 @@
${project.groupId}.ee10.distribution
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- org.eclipse.jetty.tests
- test-distribution-common
-
-
- org.eclipse.jetty.toolchain
- jetty-test-helper
-
-
- org.junit.jupiter
- junit-jupiter
-
-
-
- org.slf4j
- slf4j-api
-
org.eclipse.jetty
jetty-client
@@ -56,14 +24,11 @@
org.eclipse.jetty
jetty-openid
- ${project.version}
test
-
org.eclipse.jetty
jetty-slf4j-impl
- ${project.version}
test
@@ -79,6 +44,11 @@
war
test
+
+ org.eclipse.jetty.tests
+ jetty-testers
+ test
+
org.eclipse.jetty.tests
test-distribution-common
diff --git a/tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/OpenIdTests.java b/tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/OpenIdTests.java
index 8746d2796394..78d8f31fa518 100644
--- a/tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/OpenIdTests.java
+++ b/tests/test-distribution/test-ee10-distribution/src/test/java/org/eclipse/jetty/ee10/tests/distribution/OpenIdTests.java
@@ -13,7 +13,6 @@
package org.eclipse.jetty.ee10.tests.distribution;
-import java.io.File;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
@@ -21,7 +20,8 @@
import org.eclipse.jetty.ee10.tests.distribution.openid.OpenIdProvider;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.tests.distribution.AbstractJettyHomeTest;
-import org.eclipse.jetty.tests.hometester.JettyHomeTester;
+import org.eclipse.jetty.tests.testers.JettyHomeTester;
+import org.eclipse.jetty.tests.testers.Tester;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -56,10 +56,10 @@ public void testOpenID() throws Exception
assertTrue(run1.awaitFor(START_TIMEOUT, TimeUnit.SECONDS));
assertEquals(0, run1.getExitValue());
- File webApp = distribution.resolveArtifact("org.eclipse.jetty.ee10:jetty-ee10-test-openid-webapp:war:" + jettyVersion);
- distribution.installWarFile(webApp, "test");
+ Path webApp = distribution.resolveArtifact("org.eclipse.jetty.ee10:jetty-ee10-test-openid-webapp:war:" + jettyVersion);
+ distribution.installWar(webApp, "test");
- int port = distribution.freePort();
+ int port = Tester.freePort();
openIdProvider.addRedirectUri("http://localhost:" + port + "/test/j_security_check");
openIdProvider.start();
String[] args2 = {
diff --git a/tests/test-distribution/test-ee9-distribution/pom.xml b/tests/test-distribution/test-ee9-distribution/pom.xml
index d41933d8c204..2b93c7353124 100644
--- a/tests/test-distribution/test-ee9-distribution/pom.xml
+++ b/tests/test-distribution/test-ee9-distribution/pom.xml
@@ -13,40 +13,9 @@
${project.groupId}.ee9.distribution
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- org.eclipse.jetty.tests
- test-distribution-common
-
-
- org.eclipse.jetty.toolchain
- jetty-test-helper
-
-
- org.junit.jupiter
- junit-jupiter
-
-
- org.slf4j
- slf4j-api
-
org.eclipse.jetty
jetty-client
@@ -55,7 +24,6 @@
org.eclipse.jetty
jetty-slf4j-impl
- ${project.version}
test
@@ -77,6 +45,11 @@
war
test
+
+ org.eclipse.jetty.tests
+ jetty-testers
+ test
+
org.eclipse.jetty.tests
test-distribution-common
diff --git a/tests/test-distribution/test-ee9-distribution/src/test/java/org/eclipse/jetty/ee9/tests/distribution/OpenIdTests.java b/tests/test-distribution/test-ee9-distribution/src/test/java/org/eclipse/jetty/ee9/tests/distribution/OpenIdTests.java
index ff8b0fb236c6..410dd07f49c1 100644
--- a/tests/test-distribution/test-ee9-distribution/src/test/java/org/eclipse/jetty/ee9/tests/distribution/OpenIdTests.java
+++ b/tests/test-distribution/test-ee9-distribution/src/test/java/org/eclipse/jetty/ee9/tests/distribution/OpenIdTests.java
@@ -13,7 +13,6 @@
package org.eclipse.jetty.ee9.tests.distribution;
-import java.io.File;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
@@ -21,7 +20,8 @@
import org.eclipse.jetty.ee9.tests.distribution.openid.OpenIdProvider;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.tests.distribution.AbstractJettyHomeTest;
-import org.eclipse.jetty.tests.hometester.JettyHomeTester;
+import org.eclipse.jetty.tests.testers.JettyHomeTester;
+import org.eclipse.jetty.tests.testers.Tester;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -59,10 +59,10 @@ public void testOpenID() throws Exception
assertTrue(run1.awaitFor(START_TIMEOUT, TimeUnit.SECONDS));
assertEquals(0, run1.getExitValue());
- File webApp = distribution.resolveArtifact("org.eclipse.jetty.ee9:jetty-ee9-test-openid-webapp:war:" + jettyVersion);
- distribution.installWarFile(webApp, "test");
+ Path webApp = distribution.resolveArtifact("org.eclipse.jetty.ee9:jetty-ee9-test-openid-webapp:war:" + jettyVersion);
+ distribution.installWar(webApp, "test");
- int port = distribution.freePort();
+ int port = Tester.freePort();
openIdProvider.addRedirectUri("http://localhost:" + port + "/test/j_security_check");
openIdProvider.start();
String[] args2 = {