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 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 getKnown() + { + return EnumSet.allOf(Violation.class); + } + + @Override + public Set 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 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 = {