diff options
55 files changed, 739 insertions, 304 deletions
diff --git a/application/pom.xml b/application/pom.xml index 8f9dc7999a0..2f0586145b2 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -139,8 +139,8 @@ <!-- START JETTY embedded jars --> <dependency> - <groupId>org.eclipse.jetty.alpn</groupId> - <artifactId>alpn-api</artifactId> + <groupId>org.eclipse.jetty.http2</groupId> + <artifactId>http2-common</artifactId> </dependency> <dependency> <groupId>org.eclipse.jetty.http2</groupId> @@ -152,35 +152,43 @@ </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-alpn-server</artifactId> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-client</artifactId> - <scope>test</scope> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-continuation</artifactId> - <scope>test</scope> + <artifactId>jetty-http</artifactId> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-io</artifactId> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-jmx</artifactId> - <scope>test</scope> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> - <scope>test</scope> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-servlet</artifactId> - <scope>test</scope> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-servlets</artifactId> - <scope>test</scope> + <artifactId>jetty-util</artifactId> </dependency> <dependency> + <groupId>org.eclipse.jetty.toolchain</groupId> + <artifactId>jetty-jakarta-servlet-api</artifactId> + </dependency> + <!-- END JETTY embedded jars --> + + <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <scope>test</scope> @@ -190,8 +198,6 @@ <artifactId>junit-jupiter-engine</artifactId> <scope>test</scope> </dependency> - <!-- END JETTY embedded jars --> - </dependencies> <build> diff --git a/client/go/script-utils/startcbinary/tuning.go b/client/go/script-utils/startcbinary/tuning.go index dbe4d34dabe..950842babd6 100644 --- a/client/go/script-utils/startcbinary/tuning.go +++ b/client/go/script-utils/startcbinary/tuning.go @@ -4,47 +4,10 @@ package startcbinary import ( - "os" - "strconv" - "strings" - - "github.com/vespa-engine/vespa/client/go/trace" "github.com/vespa-engine/vespa/client/go/util" ) -func (spec *ProgSpec) optionallyReduceBaseFrequency() { - if spec.getenv(ENV_VESPA_TIMER_HZ) == "" { - backticks := util.BackTicksIgnoreStderr - out, _ := backticks.Run("uname", "-r") - if strings.Contains(out, "linuxkit") { - trace.Trace("Running docker on macos. Reducing base frequency from 1000hz to 100hz due to high cost of sampling time. This will reduce timeout accuracy.") - spec.setenv(ENV_VESPA_TIMER_HZ, "100") - } - } -} - -func getThpSizeMb() int { - const fn = "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size" - thp_size := 2 - line, err := os.ReadFile(fn) - if err == nil { - chomped := strings.TrimSuffix(string(line), "\n") - number, err := strconv.Atoi(chomped) - if err == nil { - thp_size = number / (1024 * 1024) - trace.Trace("thp_size", chomped, "=>", thp_size) - } else { - trace.Trace("no thp_size:", err) - } - } else { - trace.Trace("no thp_size:", err) - } - return thp_size -} - func (spec *ProgSpec) configureTuning() { - spec.optionallyReduceBaseFrequency() - util.SetResourceLimit(util.RLIMIT_CORE, util.NO_RLIMIT) - util.SetResourceLimit(util.RLIMIT_NOFILE, 262144) - util.SetResourceLimit(util.RLIMIT_NPROC, 409600) + util.OptionallyReduceTimerFrequency() + util.TuneResourceLimits() } diff --git a/client/go/util/setrlimit.go b/client/go/util/setrlimit.go index 4ec1ea60a3c..6a78e47d35b 100644 --- a/client/go/util/setrlimit.go +++ b/client/go/util/setrlimit.go @@ -5,9 +5,11 @@ package util import ( + "os" + "strconv" + "github.com/vespa-engine/vespa/client/go/trace" "golang.org/x/sys/unix" - "os" ) type ResourceId int @@ -31,6 +33,13 @@ func (rid ResourceId) String() string { return "unknown resource id" } +func readableLimit(val uint64) string { + if val == NO_RLIMIT { + return "unlimited" + } + return strconv.FormatUint(val, 10) +} + func SetResourceLimit(resource ResourceId, newVal uint64) { var current unix.Rlimit err := unix.Getrlimit(int(resource), ¤t) @@ -57,6 +66,6 @@ func SetResourceLimit(resource ResourceId, newVal uint64) { if err != nil { trace.Trace("Failed setting resource limit:", err) } else { - trace.Trace("Resource limit", resource, "adjusted OK:", wanted.Cur, "/", wanted.Max) + trace.Trace("Resource limit", resource, "adjusted OK:", readableLimit(wanted.Cur), "/", readableLimit(wanted.Max)) } } diff --git a/client/go/util/tuning.go b/client/go/util/tuning.go new file mode 100644 index 00000000000..a8f6eab286a --- /dev/null +++ b/client/go/util/tuning.go @@ -0,0 +1,69 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Author: arnej + +package util + +import ( + "os" + "strconv" + "strings" + + "github.com/vespa-engine/vespa/client/go/trace" +) + +const ( + ENV_VESPA_TIMER_HZ = "VESPA_TIMER_HZ" +) + +func OptionallyReduceTimerFrequency() { + if os.Getenv(ENV_VESPA_TIMER_HZ) == "" { + backticks := BackTicksIgnoreStderr + out, _ := backticks.Run("uname", "-r") + if strings.Contains(out, "linuxkit") { + trace.Trace( + "Running docker on macos.", + "Reducing base frequency from 1000hz to 100hz due to high cost of sampling time.", + "This will reduce timeout accuracy.") + os.Setenv(ENV_VESPA_TIMER_HZ, "100") + } + } +} + +func TuneResourceLimits() { + var numfiles uint64 = 262144 + var numprocs uint64 = 409600 + if env := os.Getenv("file_descriptor_limit"); env != "" { + n, err := strconv.Atoi(env) + if err != nil { + numfiles = uint64(n) + } + } + if env := os.Getenv("num_processes_limit"); env != "" { + n, err := strconv.Atoi(env) + if err != nil { + numprocs = uint64(n) + } + } + SetResourceLimit(RLIMIT_CORE, NO_RLIMIT) + SetResourceLimit(RLIMIT_NOFILE, numfiles) + SetResourceLimit(RLIMIT_NPROC, numprocs) +} + +func GetThpSizeMb() int { + const fn = "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size" + thp_size := 2 + line, err := os.ReadFile(fn) + if err == nil { + chomped := strings.TrimSuffix(string(line), "\n") + number, err := strconv.Atoi(chomped) + if err == nil { + thp_size = number / (1024 * 1024) + trace.Trace("thp_size", chomped, "=>", thp_size) + } else { + trace.Trace("no thp_size:", err) + } + } else { + trace.Trace("no thp_size:", err) + } + return thp_size +} diff --git a/client/js/app/src/app/pages/querybuilder/context/parameters.jsx b/client/js/app/src/app/pages/querybuilder/context/parameters.jsx index 377d60bfcd7..0143b2c1538 100644 --- a/client/js/app/src/app/pages/querybuilder/context/parameters.jsx +++ b/client/js/app/src/app/pages/querybuilder/context/parameters.jsx @@ -114,6 +114,7 @@ export default param('root', [ param('metrics', [param('ignore', 'Boolean', { default: false })]), param('weakAnd', [param('replace', 'Boolean', { default: false })]), param('wand', [param('hits', 'Integer', { default: 100 })]), + param('sorting', [param('degrading', 'Boolean', { default: true })]), param('streaming', [ param('userid', 'Integer'), diff --git a/cloud-tenant-base-dependencies-enforcer/pom.xml b/cloud-tenant-base-dependencies-enforcer/pom.xml index 03f14d7d65f..8ba9794d75c 100644 --- a/cloud-tenant-base-dependencies-enforcer/pom.xml +++ b/cloud-tenant-base-dependencies-enforcer/pom.xml @@ -43,8 +43,7 @@ <javax.servlet-api.version>3.1.0</javax.servlet-api.version> <javax.ws.rs-api.version>2.0.1</javax.ws.rs-api.version> <jaxb.version>2.3.0</jaxb.version> - <jetty.version>9.4.49.v20220914</jetty.version> - <jetty-alpn.version>1.1.3.v20160715</jetty-alpn.version> + <jetty.version>11.0.12</jetty.version> <org.lz4.version>1.8.0</org.lz4.version> <org.json.version>20220320</org.json.version> <!-- TODO: Remove on Vespa 9 --> <slf4j.version>1.7.32</slf4j.version> <!-- WARNING: when updated, also update c.y.v.tenant:base pom --> @@ -200,22 +199,20 @@ <include>org.bouncycastle:bcpkix-jdk18on:[${bouncycastle.version}]:jar:test</include> <include>org.bouncycastle:bcprov-jdk18on:[${bouncycastle.version}]:jar:test</include> <include>org.bouncycastle:bcutil-jdk18on:[${bouncycastle.version}]:jar:test</include> - <include>org.eclipse.jetty.alpn:alpn-api:[${jetty-alpn.version}]:jar:test</include> <include>org.eclipse.jetty.http2:http2-common:[${jetty.version}]:jar:test</include> <include>org.eclipse.jetty.http2:http2-hpack:[${jetty.version}]:jar:test</include> <include>org.eclipse.jetty.http2:http2-server:[${jetty.version}]:jar:test</include> + <include>org.eclipse.jetty.toolchain:jetty-jakarta-servlet-api:5.0.2:jar:test</include> + <include>org.eclipse.jetty:jetty-alpn-client:[${jetty.version}]:jar:test</include> <include>org.eclipse.jetty:jetty-alpn-java-server:[${jetty.version}]:jar:test</include> <include>org.eclipse.jetty:jetty-alpn-server:[${jetty.version}]:jar:test</include> <include>org.eclipse.jetty:jetty-client:[${jetty.version}]:jar:test</include> - <include>org.eclipse.jetty:jetty-continuation:[${jetty.version}]:jar:test</include> <include>org.eclipse.jetty:jetty-http:[${jetty.version}]:jar:test</include> <include>org.eclipse.jetty:jetty-io:[${jetty.version}]:jar:test</include> <include>org.eclipse.jetty:jetty-jmx:[${jetty.version}]:jar:test</include> <include>org.eclipse.jetty:jetty-security:[${jetty.version}]:jar:test</include> <include>org.eclipse.jetty:jetty-server:[${jetty.version}]:jar:test</include> <include>org.eclipse.jetty:jetty-servlet:[${jetty.version}]:jar:test</include> - <include>org.eclipse.jetty:jetty-servlets:[${jetty.version}]:jar:test</include> - <include>org.eclipse.jetty:jetty-util-ajax:[${jetty.version}]:jar:test</include> <include>org.eclipse.jetty:jetty-util:[${jetty.version}]:jar:test</include> <include>org.hamcrest:hamcrest-core:1.3:jar:test</include> <include>org.hdrhistogram:HdrHistogram:2.1.8:jar:test</include> diff --git a/config-model/src/main/java/com/yahoo/schema/derived/IndexInfo.java b/config-model/src/main/java/com/yahoo/schema/derived/IndexInfo.java index 277858bed26..54c503a0bbd 100644 --- a/config-model/src/main/java/com/yahoo/schema/derived/IndexInfo.java +++ b/config-model/src/main/java/com/yahoo/schema/derived/IndexInfo.java @@ -189,9 +189,9 @@ public class IndexInfo extends Derived implements IndexInfoConfig.Producer { } private static boolean needLowerCase(ImmutableSDField field) { - return field.doesIndexing() - || field.doesLowerCasing() - || ((field.doesAttributing() || (field.getAttribute() != null)) + return ( field.doesIndexing() && field.getMatching().getCase() != Case.CASED) + || field.doesLowerCasing() + || ((field.doesAttributing() || (field.getAttribute() != null)) && isAnyChildString(field.getDataType()) && field.getMatching().getCase().equals(Case.UNCASED)); } diff --git a/config-model/src/test/java/com/yahoo/schema/derived/CasedIndexTestCase.java b/config-model/src/test/java/com/yahoo/schema/derived/CasedIndexTestCase.java new file mode 100644 index 00000000000..68a5a7b26db --- /dev/null +++ b/config-model/src/test/java/com/yahoo/schema/derived/CasedIndexTestCase.java @@ -0,0 +1,48 @@ +package com.yahoo.schema.derived; + +import com.yahoo.schema.ApplicationBuilder; +import com.yahoo.search.config.IndexInfoConfig; +import org.junit.jupiter.api.Test; + +import java.util.Set; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +/** + * @author bratseth + */ +public class CasedIndexTestCase { + + @Test + public void testCasedIndexDeriving() throws Exception { + var b = new ApplicationBuilder(); + b.addSchema(""" + schema test { + document test { + field a type string { + indexing: summary | index + match: cased + } + } + } + """); + var application = b.build(true); + var config = new DerivedConfiguration(application.schemas().get("test"), b.getRankProfileRegistry()); + var indexInfo = config.getIndexInfo(); + var indexInfoConfigBuilder = new IndexInfoConfig.Builder(); + indexInfo.getConfig(indexInfoConfigBuilder); + assertFalse(commandsOf("test", "a", indexInfoConfigBuilder).contains("lowercase")); + } + + private Set<String> commandsOf(String schema, String field, IndexInfoConfig.Builder indexInfoConfigBuilder) { + var schemaIndexInfo = indexInfoConfigBuilder.build().indexinfo().stream() + .filter(c -> c.name().equals(schema)) + .findAny().get(); + return schemaIndexInfo.command().stream() + .filter(c -> c.indexname().equals(field)) + .map(c -> c.command()) + .collect(Collectors.toSet()); + } + +} diff --git a/container-core/pom.xml b/container-core/pom.xml index ed4b05495e3..52d7f3372f0 100644 --- a/container-core/pom.xml +++ b/container-core/pom.xml @@ -14,6 +14,7 @@ <artifactId>container-core</artifactId> <version>8-SNAPSHOT</version> <packaging>container-plugin</packaging> + <dependencies> <!-- COMPILE scope --> @@ -114,40 +115,119 @@ <!-- START JETTY embedded jars --> <dependency> - <groupId>org.eclipse.jetty.alpn</groupId> - <artifactId>alpn-api</artifactId> + <groupId>org.eclipse.jetty.http2</groupId> + <artifactId>http2-common</artifactId> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> <groupId>org.eclipse.jetty.http2</groupId> <artifactId>http2-server</artifactId> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> + <!-- Required for JDK9ServerALPNProcessor through ServiceLoader API --> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-alpn-java-server</artifactId> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-alpn-server</artifactId> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-client</artifactId> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-continuation</artifactId> + <artifactId>jetty-http</artifactId> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-io</artifactId> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-jmx</artifactId> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-servlet</artifactId> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-servlets</artifactId> + <artifactId>jetty-util</artifactId> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.eclipse.jetty.toolchain</groupId> + <artifactId>jetty-jakarta-servlet-api</artifactId> </dependency> <!-- END JETTY embedded jars --> @@ -256,11 +336,6 @@ <scope>provided</scope> </dependency> <dependency> - <groupId>javax.servlet</groupId> - <artifactId>javax.servlet-api</artifactId> - <scope>provided</scope> - </dependency> - <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <scope>provided</scope> diff --git a/container-core/src/main/java/com/yahoo/container/jdisc/utils/MultiPartFormParser.java b/container-core/src/main/java/com/yahoo/container/jdisc/utils/MultiPartFormParser.java index 104d2f8ae4a..f974eb5f26c 100644 --- a/container-core/src/main/java/com/yahoo/container/jdisc/utils/MultiPartFormParser.java +++ b/container-core/src/main/java/com/yahoo/container/jdisc/utils/MultiPartFormParser.java @@ -2,9 +2,9 @@ package com.yahoo.container.jdisc.utils; import com.yahoo.container.jdisc.HttpRequest; -import org.eclipse.jetty.http.MultiPartFormInputStream; +import jakarta.servlet.http.Part; +import org.eclipse.jetty.server.MultiPartFormInputStream; -import javax.servlet.http.Part; import java.io.IOException; import java.io.InputStream; import java.util.Map; diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/Cookie.java b/container-core/src/main/java/com/yahoo/jdisc/http/Cookie.java index b194124294c..c2faa1cd10a 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/Cookie.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/Cookie.java @@ -2,7 +2,7 @@ package com.yahoo.jdisc.http; import org.eclipse.jetty.http.HttpCookie; -import org.eclipse.jetty.server.CookieCutter; +import org.eclipse.jetty.server.Cookies; import java.util.Arrays; import java.util.HashSet; @@ -180,7 +180,7 @@ public class Cookie { } public static List<Cookie> fromCookieHeader(String headerVal) { - CookieCutter cookieCutter = new CookieCutter(); + Cookies cookieCutter = new Cookies(); cookieCutter.addCookieField(headerVal); return Arrays.stream(cookieCutter.getCookies()) .map(servletCookie -> { diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/HttpRequest.java b/container-core/src/main/java/com/yahoo/jdisc/http/HttpRequest.java index 598a924b327..4ad38a9f965 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/HttpRequest.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/HttpRequest.java @@ -8,15 +8,14 @@ import com.yahoo.jdisc.handler.ContentChannel; import com.yahoo.jdisc.handler.RequestHandler; import com.yahoo.jdisc.handler.ResponseHandler; import com.yahoo.jdisc.service.CurrentContainer; -import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.util.MultiMap; +import org.eclipse.jetty.util.UrlEncoded; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.URI; import java.security.Principal; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; @@ -116,15 +115,10 @@ public class HttpRequest extends Request { } private static Map<String, List<String>> getUriQueryParameters(URI uri) { - MultiMap<String> queryParameters = new MultiMap<>(); - new HttpURI(uri).decodeQueryTo(queryParameters); - - // Do a deep copy so we do not leak Jetty classes outside - Map<String, List<String>> deepCopiedQueryParameters = new HashMap<>(); - for (Map.Entry<String, List<String>> entry : queryParameters.entrySet()) { - deepCopiedQueryParameters.put(entry.getKey(), new ArrayList<>(entry.getValue())); - } - return deepCopiedQueryParameters; + if (uri.getRawQuery() == null) return Map.of(); + MultiMap<String> params = new MultiMap<>(); + UrlEncoded.decodeUtf8To(uri.getRawQuery(), params); + return Map.copyOf(params); } public Method getMethod() { diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/AccessLogRequestLog.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/AccessLogRequestLog.java index 13a63efeaa9..5b51eeee7d6 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/AccessLogRequestLog.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/AccessLogRequestLog.java @@ -8,6 +8,7 @@ import com.yahoo.container.logging.RequestLog; import com.yahoo.container.logging.RequestLogEntry; import com.yahoo.jdisc.http.HttpRequest; import com.yahoo.jdisc.http.ServerConfig; +import jakarta.servlet.http.HttpServletRequest; import org.eclipse.jetty.http2.HTTP2Stream; import org.eclipse.jetty.http2.server.HttpTransportOverHTTP2; import org.eclipse.jetty.server.HttpChannel; @@ -16,7 +17,6 @@ import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; import org.eclipse.jetty.util.component.AbstractLifeCycle; -import javax.servlet.http.HttpServletRequest; import java.security.cert.X509Certificate; import java.time.Duration; import java.time.Instant; diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactory.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactory.java index 6282e334409..6632a9f6cf8 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactory.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactory.java @@ -9,6 +9,8 @@ import com.yahoo.jdisc.http.ssl.impl.DefaultConnectorSsl; import com.yahoo.security.tls.MixedMode; import com.yahoo.security.tls.TransportSecurityUtils; import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory; +import org.eclipse.jetty.http.HttpCompliance; +import org.eclipse.jetty.http.UriCompliance; import org.eclipse.jetty.http2.server.AbstractHTTP2ServerConnectionFactory; import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory; import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory; @@ -137,8 +139,17 @@ public class ConnectorFactory { httpConfig.setOutputBufferSize(connectorConfig.outputBufferSize()); httpConfig.setRequestHeaderSize(connectorConfig.requestHeaderSize()); httpConfig.setResponseHeaderSize(connectorConfig.responseHeaderSize()); + + // Disable use of ByteBuffer.allocateDirect() + httpConfig.setUseInputDirectByteBuffers(false); + httpConfig.setUseOutputDirectByteBuffers(false); + + httpConfig.setHttpCompliance(HttpCompliance.RFC7230); + // TODO Vespa 9 Use default URI compliance (LEGACY == old Jetty 9.4 compliance) + httpConfig.setUriCompliance(UriCompliance.LEGACY); if (isSslEffectivelyEnabled(connectorConfig)) { - httpConfig.addCustomizer(new SecureRequestCustomizer()); + // Explicitly disable SNI checking as Jetty's SNI checking trust manager is not part of our SSLContext trust manager chain + httpConfig.addCustomizer(new SecureRequestCustomizer(false, false, -1, false)); } String serverNameFallback = connectorConfig.serverName().fallback(); if (!serverNameFallback.isBlank()) httpConfig.setServerAuthority(new HostPort(serverNameFallback)); @@ -150,7 +161,7 @@ public class ConnectorFactory { } private HTTP2ServerConnectionFactory newHttp2ConnectionFactory() { - HTTP2ServerConnectionFactory factory = new HTTP2ServerConnectionFactory(newHttpConfiguration()); + HTTP2ServerConnectionFactory factory = new FixedHTTP2ServerConnectionFactory(newHttpConfiguration()); setHttp2Config(factory); return factory; } @@ -169,12 +180,14 @@ public class ConnectorFactory { } private SslConnectionFactory newSslConnectionFactory(Metric metric, ConnectionFactory wrappedFactory) { - SslConnectionFactory connectionFactory = new SslConnectionFactory(createSslContextFactory(), wrappedFactory.getProtocol()); - connectionFactory.addBean(new SslHandshakeFailedListener(metric, connectorConfig.name(), connectorConfig.listenPort())); - return connectionFactory; + var fac = new SslConnectionFactory(createSslContextFactory(), wrappedFactory.getProtocol()); + fac.setDirectBuffersForDecryption(false); + fac.setDirectBuffersForDecryption(false); + fac.addBean(new SslHandshakeFailedListener(metric, connectorConfig.name(), connectorConfig.listenPort())); + return fac; } - private SslContextFactory createSslContextFactory() { + private SslContextFactory.Server createSslContextFactory() { DefaultConnectorSsl ssl = new DefaultConnectorSsl(); sslProvider.configureSsl(ssl, connectorConfig.name(), connectorConfig.listenPort()); return ssl.createSslContextFactory(); diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/FixedHTTP2ServerConnectionFactory.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/FixedHTTP2ServerConnectionFactory.java new file mode 100644 index 00000000000..cd82a3b2ff6 --- /dev/null +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/FixedHTTP2ServerConnectionFactory.java @@ -0,0 +1,122 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.http.server.jetty; + +import org.eclipse.jetty.http2.HTTP2Connection; +import org.eclipse.jetty.http2.ISession; +import org.eclipse.jetty.http2.server.HTTP2ServerConnection; +import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.util.annotation.ManagedObject; +import org.eclipse.jetty.util.annotation.Name; +import org.eclipse.jetty.util.component.Dumpable; +import org.eclipse.jetty.util.component.Graceful; +import org.eclipse.jetty.util.component.LifeCycle; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.logging.Logger; + +/** + * Workaround for <a href="https://github.com/eclipse/jetty.project/issues/8811">eclipse/jetty.project#8811</a>. + * + * @author bjorncs + */ +class FixedHTTP2ServerConnectionFactory extends HTTP2ServerConnectionFactory { + + private static final Logger log = Logger.getLogger(FixedHTTP2ServerConnectionFactory.class.getName()); + + private final HTTP2SessionContainer originalSessionContainer; + private final FixedHTTP2SessionContainer fixedSessionContainer; + + FixedHTTP2ServerConnectionFactory(@Name("config") HttpConfiguration config) { + super(config); + fixedSessionContainer = new FixedHTTP2SessionContainer(); + originalSessionContainer = getBean(HTTP2SessionContainer.class); + removeBean(originalSessionContainer); + addBean(fixedSessionContainer); + } + + @Override + public Connection newConnection(Connector connector, EndPoint endPoint) { + var conn = (HTTP2ServerConnection) super.newConnection(connector, endPoint); + conn.removeEventListener(originalSessionContainer); + conn.addEventListener(fixedSessionContainer); + return conn; + } + + @ManagedObject("The container of HTTP/2 sessions") + private static class FixedHTTP2SessionContainer implements Connection.Listener, Graceful, Dumpable { + + private final Object monitor = new Object(); + private final Set<ISession> sessions = new HashSet<>(); + private CompletableFuture<Void> shutdown; + + @Override + public void onOpened(Connection conn) { + var session = session(conn); + boolean shuttingDown; + synchronized (monitor) { + sessions.add(session); + shuttingDown = shutdown != null; + } + log.fine(() -> "Added session %s".formatted(session)); + LifeCycle.start(session); + if (shuttingDown) session.shutdown(); + } + + @Override + public void onClosed(Connection conn) { + var session = session(conn); + boolean removed; + CompletableFuture<Void> shutdown; + synchronized (monitor) { + removed = sessions.remove(session); + shutdown = this.shutdown != null && sessions.size() == 0 && !this.shutdown.isDone() + ? this.shutdown : null; + } + log.fine(() -> "Removed session %s".formatted(session)); + if (removed) LifeCycle.stop(session); + if (shutdown != null) { + log.fine("Shutdown completed after last session removed"); + shutdown.complete(null); + } + } + + @Override + public void dump(Appendable out, String indent) throws IOException { + synchronized (monitor) { Dumpable.dumpObjects(out, indent, this, sessions); } + } + + @Override public CompletableFuture<Void> shutdown() { + CompletableFuture<Void> shutdown = null; + ISession[] sessionsToClose = null; + synchronized (monitor) { + if (this.shutdown == null) { + shutdown = (this.shutdown = new CompletableFuture<>()); + sessionsToClose = sessions.toArray(ISession[]::new); + } + } + if (sessionsToClose != null) { + log.fine("Shutdown initiated"); + if (sessionsToClose.length > 0) { + for (ISession session : sessionsToClose) { + session.shutdown(); + } + } else { + log.fine("Shutdown completed since no sessions"); + shutdown.complete(null); + } + } + return shutdown; + } + + @Override public boolean isShutdown() { synchronized (monitor) { return shutdown != null; } } + + private static ISession session(Connection conn) { return ((HTTP2Connection)conn).getSession(); } + } +} diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HealthCheckProxyHandler.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HealthCheckProxyHandler.java index ac50cbbb518..342d7ab9c4a 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HealthCheckProxyHandler.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HealthCheckProxyHandler.java @@ -4,14 +4,24 @@ package com.yahoo.jdisc.http.server.jetty; import com.yahoo.concurrent.DaemonThreadFactory; import com.yahoo.jdisc.http.ConnectorConfig; import com.yahoo.security.SslContextBuilder; +import com.yahoo.security.TrustAllX509TrustManager; import com.yahoo.security.tls.TransportSecurityOptions; import com.yahoo.security.tls.TransportSecurityUtils; -import com.yahoo.security.TrustAllX509TrustManager; +import jakarta.servlet.AsyncContext; +import jakarta.servlet.AsyncEvent; +import jakarta.servlet.AsyncListener; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.WriteListener; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.ProxyProtocolClientConnectionFactory; import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.server.DetectorConnectionFactory; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.SslConnectionFactory; @@ -19,14 +29,6 @@ import org.eclipse.jetty.server.handler.HandlerWrapper; import org.eclipse.jetty.util.ssl.SslContextFactory; import javax.net.ssl.SSLContext; -import javax.servlet.AsyncContext; -import javax.servlet.AsyncEvent; -import javax.servlet.AsyncListener; -import javax.servlet.ServletException; -import javax.servlet.ServletOutputStream; -import javax.servlet.WriteListener; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.time.Duration; import java.util.HashMap; @@ -89,7 +91,7 @@ class HealthCheckProxyHandler extends HandlerWrapper { Optional.ofNullable(targetConnector.getConnectionFactory(SslConnectionFactory.class)) .or(() -> Optional.ofNullable(targetConnector.getConnectionFactory(DetectorConnectionFactory.class)) .map(detectorConnFactory -> detectorConnFactory.getBean(SslConnectionFactory.class))) - .map(connFactory -> (SslContextFactory.Server) connFactory.getSslContextFactory()) + .map(SslConnectionFactory::getSslContextFactory) .orElseThrow(() -> new IllegalArgumentException("Health check proxy can only target https port")); boolean proxyProtocol = targetConnector.connectorConfig().proxyProtocol().enabled(); return new ProxyTarget(targetPort, clientTimeout,handlerTimeout, cacheExpiry, sslContextFactory, proxyProtocol); @@ -269,13 +271,14 @@ class HealthCheckProxyHandler extends HandlerWrapper { synchronized (this) { if (client == null) { int timeoutMillis = (int) clientTimeout.toMillis(); - SslContextFactory.Client clientSsl = new SslContextFactory.Client(); + var clientSsl = new SslContextFactory.Client(); clientSsl.setHostnameVerifier((__, ___) -> true); clientSsl.setSslContext(getSslContext(serverSsl)); - HttpClient client = new HttpClient(clientSsl); + var connector = new ClientConnector(); + connector.setSslContextFactory(clientSsl); + HttpClient client = new HttpClient(new HttpClientTransportOverHTTP(connector)); client.setMaxConnectionsPerDestination(4); client.setConnectTimeout(timeoutMillis); - client.setStopTimeout(timeoutMillis); client.setIdleTimeout(timeoutMillis); client.setUserAgentField(new HttpField(HttpHeader.USER_AGENT, "health-check-proxy-client")); client.start(); diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HttpRequestDispatch.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HttpRequestDispatch.java index 9292e2024df..b4c933c1168 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HttpRequestDispatch.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HttpRequestDispatch.java @@ -12,6 +12,11 @@ import com.yahoo.jdisc.handler.RequestHandler; import com.yahoo.jdisc.http.ConnectorConfig; import com.yahoo.jdisc.http.HttpHeaders; import com.yahoo.jdisc.http.HttpRequest; +import jakarta.servlet.AsyncContext; +import jakarta.servlet.AsyncEvent; +import jakarta.servlet.AsyncListener; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.eclipse.jetty.http2.ErrorCode; import org.eclipse.jetty.http2.server.HTTP2ServerConnection; import org.eclipse.jetty.io.Connection; @@ -20,11 +25,6 @@ import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.util.Callback; -import javax.servlet.AsyncContext; -import javax.servlet.AsyncEvent; -import javax.servlet.AsyncListener; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.time.Instant; diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HttpRequestFactory.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HttpRequestFactory.java index 8a298fb3268..d45a8789e4c 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HttpRequestFactory.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HttpRequestFactory.java @@ -3,10 +3,10 @@ package com.yahoo.jdisc.http.server.jetty; import com.yahoo.jdisc.http.HttpRequest; import com.yahoo.jdisc.service.CurrentContainer; +import jakarta.servlet.http.HttpServletRequest; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.util.Utf8Appendable; -import javax.servlet.http.HttpServletRequest; import java.net.InetSocketAddress; import java.net.URI; import java.security.cert.X509Certificate; @@ -94,6 +94,6 @@ class HttpRequestFactory { } private static X509Certificate[] getCertChain(HttpServletRequest servletRequest) { - return (X509Certificate[]) servletRequest.getAttribute("javax.servlet.request.X509Certificate"); + return (X509Certificate[]) servletRequest.getAttribute(RequestUtils.SERVLET_REQUEST_X509CERT); } } diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HttpResponseStatisticsCollector.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HttpResponseStatisticsCollector.java index 3fb81cb5352..81789881b68 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HttpResponseStatisticsCollector.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/HttpResponseStatisticsCollector.java @@ -4,6 +4,11 @@ package com.yahoo.jdisc.http.server.jetty; import com.yahoo.jdisc.Metric; import com.yahoo.jdisc.http.HttpRequest; import com.yahoo.jdisc.http.ServerConfig; +import jakarta.servlet.AsyncEvent; +import jakarta.servlet.AsyncListener; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.AsyncContextEvent; @@ -11,14 +16,8 @@ import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpChannelState; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.HandlerWrapper; -import org.eclipse.jetty.util.FutureCallback; import org.eclipse.jetty.util.component.Graceful; -import javax.servlet.AsyncEvent; -import javax.servlet.AsyncListener; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; @@ -27,12 +26,10 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.Future; -import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.LongAdder; import java.util.function.ObjLongConsumer; import java.util.stream.Collectors; @@ -49,7 +46,7 @@ class HttpResponseStatisticsCollector extends HandlerWrapper implements Graceful static final String requestTypeAttribute = "requestType"; - private final AtomicReference<FutureCallback> shutdown = new AtomicReference<>(); + private final Shutdown shutdown; private final List<String> monitoringHandlerPaths; private final List<String> searchHandlerPaths; private final Set<String> ignoredUserAgents; @@ -66,6 +63,10 @@ class HttpResponseStatisticsCollector extends HandlerWrapper implements Graceful this.monitoringHandlerPaths = monitoringHandlerPaths; this.searchHandlerPaths = searchHandlerPaths; this.ignoredUserAgents = Set.copyOf(ignoredUserAgents); + this.shutdown = new Shutdown(this) { + @Override public boolean isShutdownDone() { return inFlight.get() == 0; } + }; + } private final AsyncListener completionWatcher = new AsyncListener() { @@ -97,7 +98,7 @@ class HttpResponseStatisticsCollector extends HandlerWrapper implements Graceful try { Handler handler = getHandler(); - if (handler != null && shutdown.get() == null && isStarted()) { + if (handler != null && !shutdown.isShutdown() && isStarted()) { handler.handle(path, baseRequest, request, response); } else if ( ! baseRequest.isHandled()) { baseRequest.setHandled(true); @@ -129,14 +130,9 @@ class HttpResponseStatisticsCollector extends HandlerWrapper implements Graceful .increment()); } long live = inFlight.decrementAndGet(); - FutureCallback shutdownCb = shutdown.get(); - if (shutdownCb != null) { - if (flushableResponse != null) { - flushableResponse.flushBuffer(); - } - if (live == 0) { - shutdownCb.succeeded(); - } + if (shutdown.isShutdown()) { + if (flushableResponse != null) flushableResponse.flushBuffer(); + if (live == 0) shutdown.check(); } } @@ -162,35 +158,19 @@ class HttpResponseStatisticsCollector extends HandlerWrapper implements Graceful @Override protected void doStart() throws Exception { - shutdown.set(null); + shutdown.cancel(); super.doStart(); } @Override protected void doStop() throws Exception { + shutdown.cancel(); super.doStop(); - FutureCallback shutdownCb = shutdown.get(); - if ( ! shutdownCb.isDone()) { - shutdownCb.failed(new TimeoutException()); - } } - @Override - public Future<Void> shutdown() { - FutureCallback shutdownCb = new FutureCallback(false); - shutdown.compareAndSet(null, shutdownCb); - shutdownCb = shutdown.get(); - if (inFlight.get() == 0) { - shutdownCb.succeeded(); - } - return shutdownCb; - } + @Override public CompletableFuture<Void> shutdown() { return shutdown.shutdown(); } + @Override public boolean isShutdown() { return shutdown.isShutdown(); } - @Override - public boolean isShutdown() { - FutureCallback futureCallback = shutdown.get(); - return futureCallback != null && futureCallback.isDone(); - } static class Dimensions { final String protocol; diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServlet.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServlet.java index 4b4aff0a9bd..bd052f14867 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServlet.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServlet.java @@ -5,13 +5,13 @@ import com.yahoo.container.logging.AccessLogEntry; import com.yahoo.jdisc.Metric; import com.yahoo.jdisc.handler.OverloadException; import com.yahoo.jdisc.http.HttpRequest.Method; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Request; -import javax.servlet.ServletException; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Enumeration; import java.util.Map; diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscServerConnector.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscServerConnector.java index b3069a64821..b17877cee84 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscServerConnector.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscServerConnector.java @@ -3,16 +3,13 @@ package com.yahoo.jdisc.http.server.jetty; import com.yahoo.jdisc.Metric; import com.yahoo.jdisc.http.ConnectorConfig; -import org.eclipse.jetty.http.HttpCompliance; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.http.HttpServletRequest; import org.eclipse.jetty.io.ConnectionStatistics; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; -import javax.servlet.ServletRequest; -import javax.servlet.http.HttpServletRequest; -import java.net.Socket; -import java.net.SocketException; import java.util.HashMap; import java.util.Map; import java.util.Optional; @@ -26,8 +23,6 @@ class JDiscServerConnector extends ServerConnector { private final Metric.Context metricCtx; private final ConnectionStatistics statistics; private final ConnectorConfig config; - private final boolean tcpKeepAlive; - private final boolean tcpNoDelay; private final Metric metric; private final String connectorName; private final int listenPort; @@ -36,14 +31,13 @@ class JDiscServerConnector extends ServerConnector { ConnectionMetricAggregator connectionMetricAggregator, ConnectionFactory... factories) { super(server, factories); this.config = config; - this.tcpKeepAlive = config.tcpKeepAliveEnabled(); - this.tcpNoDelay = config.tcpNoDelay(); this.metric = metric; this.connectorName = config.name(); this.listenPort = config.listenPort(); this.metricCtx = metric.createContext(createConnectorDimensions(listenPort, connectorName, 0)); this.statistics = new ConnectionStatistics(); + setAcceptedTcpNoDelay(config.tcpNoDelay()); addBean(statistics); ConnectorConfig.Throttling throttlingConfig = config.throttling(); if (throttlingConfig.enabled()) { @@ -56,17 +50,6 @@ class JDiscServerConnector extends ServerConnector { setAcceptQueueSize(config.acceptQueueSize()); setReuseAddress(config.reuseAddress()); setIdleTimeout((long) (config.idleTimeout() * 1000)); - addBean(HttpCompliance.RFC7230); - } - - @Override - protected void configure(final Socket socket) { - super.configure(socket); - try { - socket.setKeepAlive(tcpKeepAlive); - socket.setTcpNoDelay(tcpNoDelay); - } catch (SocketException ignored) { - } } public ConnectionStatistics getStatistics() { diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyConnectionLogger.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyConnectionLogger.java index 2e2eb257b6a..d9a97d621ae 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyConnectionLogger.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyConnectionLogger.java @@ -30,6 +30,7 @@ import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.net.ssl.StandardConstants; import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateEncodingException; @@ -113,7 +114,7 @@ class JettyConnectionLogger extends AbstractLifeCycle implements Connection.List info.setProxyProtocolVersion("v2"); } if (connection.getEndPoint() instanceof ProxyConnectionFactory.ProxyEndPoint) { - InetSocketAddress remoteAddress = connection.getEndPoint().getRemoteAddress(); + var remoteAddress = connection.getEndPoint().getRemoteSocketAddress(); info.setRemoteAddress(remoteAddress); } }); @@ -243,7 +244,7 @@ class JettyConnectionLogger extends AbstractLifeCycle implements Connection.List private long httpBytesSent = 0; private long requests = 0; private long responses = 0; - private InetSocketAddress remoteAddress; + private SocketAddress remoteAddress; private byte[] sslSessionId; private String sslProtocol; private String sslCipherSuite; @@ -290,7 +291,7 @@ class JettyConnectionLogger extends AbstractLifeCycle implements Connection.List synchronized ConnectionInfo incrementResponses() { ++this.responses; return this; } - synchronized ConnectionInfo setRemoteAddress(InetSocketAddress remoteAddress) { + synchronized ConnectionInfo setRemoteAddress(SocketAddress remoteAddress) { this.remoteAddress = remoteAddress; return this; } @@ -354,9 +355,9 @@ class JettyConnectionLogger extends AbstractLifeCycle implements Connection.List builder.withLocalAddress(localAddress.getHostString()) .withLocalPort(localAddress.getPort()); } - if (remoteAddress != null) { - builder.withRemoteAddress(remoteAddress.getHostString()) - .withRemotePort(remoteAddress.getPort()); + if (remoteAddress instanceof InetSocketAddress isa) { + builder.withRemoteAddress(isa.getHostString()) + .withRemotePort(isa.getPort()); } if (sslProtocol != null && sslCipherSuite != null && sslSessionId != null) { builder.withSslProtocol(sslProtocol) diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyHttpServer.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyHttpServer.java index 775c903f5f8..7b723b3a48e 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyHttpServer.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyHttpServer.java @@ -27,8 +27,6 @@ import org.eclipse.jetty.server.handler.gzip.GzipHandler; import org.eclipse.jetty.server.handler.gzip.GzipHttpOutputInterceptor; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.util.log.JavaUtilLog; -import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.thread.QueuedThreadPool; import javax.management.remote.JMXServiceURL; @@ -70,8 +68,6 @@ public class JettyHttpServer extends AbstractServerProvider { if (connectorFactories.allComponents().isEmpty()) throw new IllegalArgumentException("No connectors configured."); - initializeJettyLogging(); - server = new Server(); server.setStopTimeout((long)(serverConfig.stopTimeout() * 1000.0)); server.setRequestLog(new AccessLogRequestLog(requestLog, serverConfig.accessLog())); @@ -96,15 +92,6 @@ public class JettyHttpServer extends AbstractServerProvider { this.metricsReporter = new ServerMetricReporter(metric, server); } - private static void initializeJettyLogging() { - // Note: Jetty is logging stderr if no logger is explicitly configured - try { - Log.setLog(new JavaUtilLog()); - } catch (Exception e) { - throw new RuntimeException("Unable to initialize logging framework for Jetty"); - } - } - private static void setupJmx(Server server, ServerConfig serverConfig) { if (serverConfig.jmx().enabled()) { System.setProperty("java.rmi.server.hostname", "localhost"); @@ -152,7 +139,7 @@ public class JettyHttpServer extends AbstractServerProvider { } StatisticsHandler root = newGenericStatisticsHandler(); addChainToRoot(root, List.of( - newResponseStatisticsHandler(serverCfg), newGzipHandler(serverCfg), perConnectorHandlers)); + newResponseStatisticsHandler(serverCfg), newGzipHandler(), perConnectorHandlers)); return root; } @@ -253,22 +240,18 @@ public class JettyHttpServer extends AbstractServerProvider { return statisticsHandler; } - private static GzipHandler newGzipHandler(ServerConfig serverConfig) { - GzipHandler gzipHandler = new GzipHandlerWithVaryHeaderFixed(); - gzipHandler.setCompressionLevel(serverConfig.responseCompressionLevel()); - gzipHandler.setInflateBufferSize(8 * 1024); - gzipHandler.setIncludedMethods("GET", "POST", "PUT", "PATCH"); - return gzipHandler; - } + private static GzipHandler newGzipHandler() { return new GzipHandlerWithVaryHeaderFixed(); } /** A subclass which overrides Jetty's default behavior of including user-agent in the vary field */ private static class GzipHandlerWithVaryHeaderFixed extends GzipHandler { - @Override - public HttpField getVaryField() { - return GzipHttpOutputInterceptor.VARY_ACCEPT_ENCODING; + GzipHandlerWithVaryHeaderFixed() { + setInflateBufferSize(8 * 1024); + setIncludedMethods("GET", "POST", "PUT", "PATCH"); } + @Override public HttpField getVaryField() { return GzipHttpOutputInterceptor.VARY_ACCEPT_ENCODING; } + } } diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/RequestUtils.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/RequestUtils.java index 1bc862bc787..da4de957739 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/RequestUtils.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/RequestUtils.java @@ -1,12 +1,12 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.jdisc.http.server.jetty; +import jakarta.servlet.http.HttpServletRequest; import org.eclipse.jetty.http2.server.HTTP2ServerConnection; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.Request; - -import javax.servlet.http.HttpServletRequest; +import org.eclipse.jetty.server.SecureRequestCustomizer; /** * @author bjorncs @@ -15,7 +15,7 @@ public class RequestUtils { public static final String JDISC_REQUEST_X509CERT = "jdisc.request.X509Certificate"; public static final String JDISC_REQUEST_CHAIN = "jdisc.request.chain"; public static final String JDISC_RESPONSE_CHAIN = "jdisc.response.chain"; - public static final String SERVLET_REQUEST_X509CERT = "javax.servlet.request.X509Certificate"; + public static final String SERVLET_REQUEST_X509CERT = SecureRequestCustomizer.JAKARTA_SERVLET_REQUEST_X_509_CERTIFICATE; // The local port as reported by servlet spec. This will be influenced by Host header and similar mechanisms. // The request URI uses the local listen port as the URI is used for handler routing/bindings. diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletOutputStreamWriter.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletOutputStreamWriter.java index 4b66715fcf7..d853282a5f5 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletOutputStreamWriter.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletOutputStreamWriter.java @@ -2,9 +2,9 @@ package com.yahoo.jdisc.http.server.jetty; import com.yahoo.jdisc.handler.CompletionHandler; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.WriteListener; -import javax.servlet.ServletOutputStream; -import javax.servlet.WriteListener; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayDeque; diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletRequestReader.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletRequestReader.java index 3703878f595..2f2c48e0b48 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletRequestReader.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletRequestReader.java @@ -3,10 +3,10 @@ package com.yahoo.jdisc.http.server.jetty; import com.yahoo.jdisc.handler.CompletionHandler; import com.yahoo.jdisc.handler.ContentChannel; +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; -import javax.servlet.ReadListener; -import javax.servlet.ServletInputStream; -import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Objects; diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletResponseController.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletResponseController.java index e90dde0e4eb..6afb55f5b13 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletResponseController.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/ServletResponseController.java @@ -9,10 +9,10 @@ import com.yahoo.jdisc.handler.ResponseHandler; import com.yahoo.jdisc.http.HttpHeaders; import com.yahoo.jdisc.http.HttpResponse; import com.yahoo.jdisc.service.BindingSetNotFoundException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.MimeTypes; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/TlsClientAuthenticationEnforcer.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/TlsClientAuthenticationEnforcer.java index b420aabc598..96f0cdebd62 100644 --- a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/TlsClientAuthenticationEnforcer.java +++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/TlsClientAuthenticationEnforcer.java @@ -3,13 +3,13 @@ package com.yahoo.jdisc.http.server.jetty; import com.yahoo.jdisc.Response; import com.yahoo.jdisc.http.ConnectorConfig; +import jakarta.servlet.DispatcherType; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.HandlerWrapper; -import javax.servlet.DispatcherType; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** diff --git a/container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.connector.def b/container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.connector.def index ecbc451ead1..bdcc3f9e40a 100644 --- a/container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.connector.def +++ b/container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.connector.def @@ -28,7 +28,8 @@ reuseAddress bool default=true # The maximum idle time for a connection, which roughly translates to the Socket.setSoTimeout(int). idleTimeout double default=180.0 -# Whether or not to have socket keep alive turned on. +# TODO Vespa 9 Remove +# Has no effect since Jetty 11 upgrade tcpKeepAliveEnabled bool default=false # Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm). diff --git a/container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.server.def b/container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.server.def index f34fd523207..c15cb6b2cc4 100644 --- a/container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.server.def +++ b/container-core/src/main/resources/configdefinitions/jdisc.http.jdisc.http.server.def @@ -4,7 +4,8 @@ namespace=jdisc.http # Whether to enable developer mode, where stack traces etc are visible in response bodies. developerMode bool default=false -# The gzip compression level to use, if compression is enabled in a request. +# TODO Vespa 9 Remove +# Has no effect since Jetty 11 upgrade responseCompressionLevel int default=6 # Whether the request body of POSTed forms should be removed (form parameters are available as request parameters). diff --git a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactoryTest.java b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactoryTest.java index 1ff2783cc53..ce205b1a893 100644 --- a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactoryTest.java +++ b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactoryTest.java @@ -5,6 +5,8 @@ import com.yahoo.jdisc.Metric; import com.yahoo.jdisc.http.ConnectorConfig; import com.yahoo.jdisc.http.ServerConfig; import com.yahoo.jdisc.http.ssl.impl.ConfiguredSslContextFactoryProvider; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; @@ -12,8 +14,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Map; diff --git a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/ErrorResponseContentCreatorTest.java b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/ErrorResponseContentCreatorTest.java index 8b18c8cf09d..fdb9f2226de 100644 --- a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/ErrorResponseContentCreatorTest.java +++ b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/ErrorResponseContentCreatorTest.java @@ -2,9 +2,9 @@ package com.yahoo.jdisc.http.server.jetty; +import jakarta.servlet.http.HttpServletResponse; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServletResponse; import java.nio.charset.StandardCharsets; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpRequestFactoryTest.java b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpRequestFactoryTest.java index a23a3505bcb..e4b82db5b9f 100644 --- a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpRequestFactoryTest.java +++ b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpRequestFactoryTest.java @@ -8,15 +8,17 @@ import com.yahoo.jdisc.Response; import com.yahoo.jdisc.handler.RequestHandler; import com.yahoo.jdisc.http.HttpRequest; import com.yahoo.jdisc.service.CurrentContainer; +import jakarta.servlet.http.HttpServletRequest; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServletRequest; import java.net.URI; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.fail; /** * @author Steinar Knutsen diff --git a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpResponseStatisticsCollectorTest.java b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpResponseStatisticsCollectorTest.java index 165659389ec..502702ccf35 100644 --- a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpResponseStatisticsCollectorTest.java +++ b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpResponseStatisticsCollectorTest.java @@ -2,6 +2,9 @@ package com.yahoo.jdisc.http.server.jetty; import com.yahoo.jdisc.http.server.jetty.HttpResponseStatisticsCollector.StatisticsEntry; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.http.HttpVersion; @@ -10,6 +13,7 @@ import org.eclipse.jetty.http.MetaData.Response; import org.eclipse.jetty.server.AbstractConnector; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; +import org.eclipse.jetty.server.HttpChannelOverHttp; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpTransport; import org.eclipse.jetty.server.Request; @@ -19,9 +23,6 @@ import org.eclipse.jetty.util.Callback; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; @@ -164,8 +165,8 @@ public class HttpResponseStatisticsCollectorTest { } private Request testRequest(String scheme, int responseCode, String httpMethod, String path, com.yahoo.jdisc.Request.RequestType explicitRequestType) { - HttpChannel channel = new HttpChannel(connector, new HttpConfiguration(), null, new DummyTransport()); - MetaData.Request metaData = new MetaData.Request(httpMethod, new HttpURI(scheme + "://" + path), HttpVersion.HTTP_1_1, new HttpFields()); + HttpChannel channel = new HttpChannelOverHttp(null, connector, new HttpConfiguration(), null, new DummyTransport()); + MetaData.Request metaData = new MetaData.Request(httpMethod, HttpURI.build(scheme + "://" + path), HttpVersion.HTTP_1_1, HttpFields.build()); Request req = channel.getRequest(); if (explicitRequestType != null) req.setAttribute("requestType", explicitRequestType); @@ -192,7 +193,7 @@ public class HttpResponseStatisticsCollectorTest { private final class DummyTransport implements HttpTransport { @Override - public void send(Response info, boolean head, ByteBuffer content, boolean lastContent, Callback callback) { + public void send(MetaData.Request request, Response response, ByteBuffer byteBuffer, boolean b, Callback callback) { callback.succeeded(); } @@ -202,11 +203,6 @@ public class HttpResponseStatisticsCollectorTest { } @Override - public boolean isOptimizedForDirectBuffers() { - return false; - } - - @Override public void push(MetaData.Request request) { } diff --git a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerConformanceTest.java b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerConformanceTest.java index 7cce9f2a9ff..ae1a6494acd 100644 --- a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerConformanceTest.java +++ b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerConformanceTest.java @@ -807,6 +807,7 @@ public class HttpServerConformanceTest extends ServerProviderConformanceTest { post.setProtocolVersion(HttpVersion.HTTP_1_1); request = post; } + request.addHeader("Connection", "close"); return executorService.submit(() -> httpClient.execute(request)); } diff --git a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java index 318067ac634..39b6dcdc6d5 100644 --- a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java +++ b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java @@ -186,9 +186,10 @@ public class HttpServerTest { @Test void requireThatServerCanEchoCompressed() throws Exception { final JettyTestDriver driver = JettyTestDriver.newInstance(new EchoRequestHandler()); - SimpleHttpClient client = driver.newClient(true); - client.get("/status.html") - .expectStatusCode(is(OK)); + try (SimpleHttpClient client = driver.newClient(true)) { + client.get("/status.html") + .expectStatusCode(is(OK)); + } assertTrue(driver.close()); } @@ -532,9 +533,9 @@ public class HttpServerTest { .withTrustStore(certificateFile) .build(); - new SimpleHttpClient(trustStoreOnlyCtx, driver.server().getListenPort(), false) - .get("/dummy.html") - .expectStatusCode(is(UNAUTHORIZED)); + try (var c = new SimpleHttpClient(trustStoreOnlyCtx, driver.server().getListenPort(), false)) { + c.get("/dummy.html").expectStatusCode(is(UNAUTHORIZED)); + } assertTrue(driver.close()); } @@ -550,9 +551,9 @@ public class HttpServerTest { .withTrustStore(certificateFile) .build(); - new SimpleHttpClient(trustStoreOnlyCtx, driver.server().getListenPort(), false) - .get("/status.html") - .expectStatusCode(is(OK)); + try (var c = new SimpleHttpClient(trustStoreOnlyCtx, driver.server().getListenPort(), false)) { + c.get("/status.html").expectStatusCode(is(OK)); + } assertTrue(driver.close()); } diff --git a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/ProxyProtocolTest.java b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/ProxyProtocolTest.java index d4d6dcee957..6cd6f05933a 100644 --- a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/ProxyProtocolTest.java +++ b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/ProxyProtocolTest.java @@ -12,6 +12,8 @@ import org.apache.hc.client5.http.ssl.NoopHostnameVerifier; import org.assertj.core.api.Assertions; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; +import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -185,14 +187,15 @@ class ProxyProtocolTest { // Using Jetty's http client as Apache httpclient does not support the proxy-protocol v1/v2. private static HttpClient createJettyHttpClient(Path certificateFile) throws Exception { - SslContextFactory.Client clientSslCtxFactory = new SslContextFactory.Client(); - clientSslCtxFactory.setHostnameVerifier(NoopHostnameVerifier.INSTANCE); - clientSslCtxFactory.setSslContext(new SslContextBuilder().withTrustStore(certificateFile).build()); - - HttpClient client = new HttpClient(clientSslCtxFactory); - client.setConnectTimeout(60*1000); - client.setStopTimeout(60*1000); - client.setIdleTimeout(60*1000); + var ssl = new SslContextFactory.Client(); + ssl.setHostnameVerifier(NoopHostnameVerifier.INSTANCE); + ssl.setSslContext(new SslContextBuilder().withTrustStore(certificateFile).build()); + var connector = new ClientConnector(); + connector.setSslContextFactory(ssl); + HttpClient client = new HttpClient(new HttpClientTransportOverHTTP(connector)); + int timeout = 60 * 1000; + client.setConnectTimeout(timeout); + client.setIdleTimeout(timeout); client.start(); return client; } diff --git a/container-dev/pom.xml b/container-dev/pom.xml index 0c88531a248..9bbb5591fbf 100644 --- a/container-dev/pom.xml +++ b/container-dev/pom.xml @@ -98,8 +98,8 @@ <!-- START JETTY embedded jars --> <exclusion> - <groupId>org.eclipse.jetty.alpn</groupId> - <artifactId>alpn-api</artifactId> + <groupId>org.eclipse.jetty.http2</groupId> + <artifactId>http2-common</artifactId> </exclusion> <exclusion> <groupId>org.eclipse.jetty.http2</groupId> @@ -111,11 +111,19 @@ </exclusion> <exclusion> <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-alpn-server</artifactId> + </exclusion> + <exclusion> + <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-client</artifactId> </exclusion> <exclusion> <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-continuation</artifactId> + <artifactId>jetty-http</artifactId> + </exclusion> + <exclusion> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-io</artifactId> </exclusion> <exclusion> <groupId>org.eclipse.jetty</groupId> @@ -131,7 +139,11 @@ </exclusion> <exclusion> <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-servlets</artifactId> + <artifactId>jetty-util</artifactId> + </exclusion> + <exclusion> + <groupId>org.eclipse.jetty.toolchain</groupId> + <artifactId>jetty-jakarta-servlet-api</artifactId> </exclusion> <!-- END JETTY embedded jars --> </exclusions> diff --git a/container-search/src/main/java/com/yahoo/search/querytransform/VespaLowercasingSearcher.java b/container-search/src/main/java/com/yahoo/search/querytransform/VespaLowercasingSearcher.java index 51d06e74184..d0749630807 100644 --- a/container-search/src/main/java/com/yahoo/search/querytransform/VespaLowercasingSearcher.java +++ b/container-search/src/main/java/com/yahoo/search/querytransform/VespaLowercasingSearcher.java @@ -38,9 +38,7 @@ public class VespaLowercasingSearcher extends LowercasingSearcher { public boolean shouldLowercase(String commonPath, WordItem word, IndexFacts.Session indexFacts) { if (word.isLowercased()) return false; - StringBuilder sb = new StringBuilder(); - sb.append(commonPath).append(".").append(word.getIndexName()); - return indexFacts.getIndex(sb.toString()).isLowercase(); + return indexFacts.getIndex(commonPath + "." + word.getIndexName()).isLowercase(); } } diff --git a/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java b/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java index 1203f69dd3c..76ceb60b3d5 100644 --- a/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java +++ b/container-search/src/main/java/com/yahoo/search/yql/YqlParser.java @@ -132,7 +132,7 @@ public class YqlParser implements Parser { public static final String USER_INPUT_LANGUAGE = "language"; private static final String USER_INPUT_GRAMMAR_RAW = "raw"; private static final String USER_INPUT_GRAMMAR_SEGMENT = "segment"; - private static final String USER_INPUT_GRAMMAR_WEAKAND = "weakAnd"; + private static final Set<String> WEAKAND_GRAMMARS = Set.of("weakAnd", "tokenize"); private static final String USER_INPUT = "userInput"; private static final String USER_QUERY = "userQuery"; private static final String NON_EMPTY = "nonEmpty"; @@ -731,8 +731,9 @@ public class YqlParser implements Parser { } // Set grammar-specific annotations - if (USER_INPUT_GRAMMAR_WEAKAND.equals(grammar) && item instanceof WeakAndItem weakAndItem) { - weakAndItem.setN(getAnnotation(ast, TARGET_HITS, Integer.class, WeakAndItem.defaultN, "'targetHits' (N) for weak and")); + if (WEAKAND_GRAMMARS.contains(grammar) && item instanceof WeakAndItem weakAndItem) { + weakAndItem.setN(getAnnotation(ast, TARGET_HITS, Integer.class, WeakAndItem.defaultN, + "'targetHits' (N) for weak and")); } return item; } diff --git a/container-search/src/test/java/com/yahoo/search/yql/UserInputTestCase.java b/container-search/src/test/java/com/yahoo/search/yql/UserInputTestCase.java index 858d5a16352..d3c77d2ba66 100644 --- a/container-search/src/test/java/com/yahoo/search/yql/UserInputTestCase.java +++ b/container-search/src/test/java/com/yahoo/search/yql/UserInputTestCase.java @@ -91,9 +91,14 @@ public class UserInputTestCase { @Test void testUserInputSettingTargetHits() { + assertTargetHitsIsPropagatedInUserInput("weakAnd"); + assertTargetHitsIsPropagatedInUserInput("tokenize"); + } + + private void assertTargetHitsIsPropagatedInUserInput(String grammar) { URIBuilder builder = searchUri(); builder.setParameter("yql", - "select * from sources * where {grammar: \"weakAnd\", targetHits: 17, defaultIndex: \"f\"}userInput(\"a test\")"); + "select * from sources * where {grammar: \"" + grammar + "\", targetHits: 17, defaultIndex: \"f\"}userInput(\"a test\")"); Query query = searchAndAssertNoErrors(builder); assertEquals("select * from sources * where ({targetNumHits: 17}weakAnd(f contains \"a\", f contains \"test\"))", query.yqlRepresentation()); WeakAndItem weakAnd = (WeakAndItem)query.getModel().getQueryTree().getRoot(); diff --git a/container-search/src/test/java/com/yahoo/search/yql/YqlParserTestCase.java b/container-search/src/test/java/com/yahoo/search/yql/YqlParserTestCase.java index a1dad201958..c15c3b2c5ea 100644 --- a/container-search/src/test/java/com/yahoo/search/yql/YqlParserTestCase.java +++ b/container-search/src/test/java/com/yahoo/search/yql/YqlParserTestCase.java @@ -446,12 +446,6 @@ public class YqlParserTestCase { } @Test - void testTokenizeGrammar() { - Item root = parse("select foo from bar where ({grammar: \"tokenize\", targetHits: 10, defaultIndex: \"default\"}userInput(\"this is a test\"))").getRoot(); - assertEquals("WEAKAND(100) default:this default:is default:a default:test", root.toString()); - } - - @Test void testAccentDropping() { assertFalse(getRootWord("select foo from bar where baz contains " + "( {accentDrop: false} \"colors\")").isNormalizable()); diff --git a/container-test/pom.xml b/container-test/pom.xml index 32a64a98b9e..e0ff7f62e93 100644 --- a/container-test/pom.xml +++ b/container-test/pom.xml @@ -113,8 +113,8 @@ <!-- START JETTY embedded jars --> <dependency> - <groupId>org.eclipse.jetty.alpn</groupId> - <artifactId>alpn-api</artifactId> + <groupId>org.eclipse.jetty.http2</groupId> + <artifactId>http2-common</artifactId> </dependency> <dependency> <groupId>org.eclipse.jetty.http2</groupId> @@ -126,11 +126,19 @@ </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-alpn-server</artifactId> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-client</artifactId> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-continuation</artifactId> + <artifactId>jetty-http</artifactId> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-io</artifactId> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> @@ -146,7 +154,11 @@ </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-servlets</artifactId> + <artifactId>jetty-util</artifactId> + </dependency> + <dependency> + <groupId>org.eclipse.jetty.toolchain</groupId> + <artifactId>jetty-jakarta-servlet-api</artifactId> </dependency> <!-- END JETTY embedded jars --> </dependencies> diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java index 579e34c7667..cbdb6d1d05f 100644 --- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java +++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java @@ -46,15 +46,6 @@ public class Flags { private static volatile TreeMap<FlagId, FlagDefinition> flags = new TreeMap<>(); - public static final UnboundBooleanFlag ROOT_CHAIN_GRAPH = defineFeatureFlag( - "root-chain-graph", true, - List.of("hakonhall"), "2022-10-05", "2022-11-04", - "Whether to run all tasks in the root task chain up to the one failing to converge (false), or " + - "run all tasks in the root task chain whose dependencies have converged (true). And when suspending, " + - "whether to run the tasks in sequence (false) or in reverse sequence (true).", - "On first tick of the root chain after (re)start of host admin.", - ZONE_ID, NODE_TYPE, HOSTNAME); - public static final UnboundDoubleFlag DEFAULT_TERM_WISE_LIMIT = defineDoubleFlag( "default-term-wise-limit", 1.0, List.of("baldersheim"), "2020-12-02", "2023-01-01", diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java index a271dfe571e..da9329747b4 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java @@ -324,7 +324,7 @@ public class Nodes { public Node deallocate(Node node, Agent agent, String reason, NestedTransaction transaction) { if (parkOnDeallocationOf(node, agent)) { - return park(node.hostname(), true, agent, reason, transaction); + return park(node.hostname(), false, agent, reason, transaction); } else { Node.State toState = Node.State.dirty; if (node.state() == Node.State.parked) { diff --git a/parent/pom.xml b/parent/pom.xml index afec99ee8c1..29da1b59155 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -891,9 +891,9 @@ <version>${eclipse-collections.version}</version> </dependency> <dependency> - <groupId>org.eclipse.jetty.alpn</groupId> - <artifactId>alpn-api</artifactId> - <version>${jetty-alpn.version}</version> + <groupId>org.eclipse.jetty.http2</groupId> + <artifactId>http2-common</artifactId> + <version>${jetty.version}</version> </dependency> <dependency> <groupId>org.eclipse.jetty.http2</groupId> @@ -907,32 +907,37 @@ </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-alpn-server</artifactId> + <version>${jetty.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-client</artifactId> <version>${jetty.version}</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-continuation</artifactId> + <artifactId>jetty-http</artifactId> <version>${jetty.version}</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-server</artifactId> + <artifactId>jetty-io</artifactId> <version>${jetty.version}</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-servlet</artifactId> + <artifactId>jetty-jmx</artifactId> <version>${jetty.version}</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-servlets</artifactId> + <artifactId>jetty-server</artifactId> <version>${jetty.version}</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-jmx</artifactId> + <artifactId>jetty-servlet</artifactId> <version>${jetty.version}</version> </dependency> <dependency> @@ -941,6 +946,11 @@ <version>${jetty.version}</version> </dependency> <dependency> + <groupId>org.eclipse.jetty.toolchain</groupId> + <artifactId>jetty-jakarta-servlet-api</artifactId> + <version>${jetty-servlet-api.version}</version> + </dependency> + <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId>jaxb-runtime</artifactId> <version>2.3.2</version> <!-- 2.3.3 has a BROKEN manifest --> @@ -1118,8 +1128,8 @@ <felix.log.version>1.0.1</felix.log.version> <findbugs.version>3.0.2</findbugs.version> <!-- Should be kept in sync with guava --> <hdrhistogram.version>2.1.12</hdrhistogram.version> - <jetty.version>9.4.49.v20220914</jetty.version> - <jetty-alpn.version>1.1.3.v20160715</jetty-alpn.version> + <jetty.version>11.0.12</jetty.version> + <jetty-servlet-api.version>5.0.2</jetty-servlet-api.version> <jjwt.version>0.11.2</jjwt.version> <jna.version>5.11.0</jna.version> <json-smart.version>2.4.8</json-smart.version> diff --git a/screwdriver/release-java-artifacts.sh b/screwdriver/release-java-artifacts.sh index e1f979cb746..e8e98015d39 100755 --- a/screwdriver/release-java-artifacts.sh +++ b/screwdriver/release-java-artifacts.sh @@ -39,7 +39,7 @@ chmod 700 $SD_SOURCE_DIR/screwdriver/deploy chmod 600 $SD_SOURCE_DIR/screwdriver/deploy/* # Build the Java code with the correct version set -screwdriver/replace-vespa-version-in-poms.sh $VESPA_RELEASE . +$SD_SOURCE_DIR/screwdriver/replace-vespa-version-in-poms.sh $VESPA_RELEASE . # We disable javadoc for all modules not marked as public API for MODULE in $(comm -2 -3 \ diff --git a/vespa-hadoop/pom.xml b/vespa-hadoop/pom.xml index b497fda2aab..42ee990e727 100644 --- a/vespa-hadoop/pom.xml +++ b/vespa-hadoop/pom.xml @@ -23,15 +23,76 @@ <jettison.version>1.5.1</jettison.version> <pig.version>0.16.0</pig.version> <tomcat-jasper.version>5.5.23</tomcat-jasper.version> + <jetty9.version>9.4.49.v20220914</jetty9.version> </properties> <dependencyManagement> + <!-- Force 9.4.x version of transitive Jetty dependencies. Required by hadoop libraries --> <dependencies> - <!-- Force newer version of jetty-util (to match Jetty version of jdisc) --> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-alpn-client</artifactId> + <version>${jetty9.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-client</artifactId> + <version>${jetty9.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-http</artifactId> + <version>${jetty9.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-io</artifactId> + <version>${jetty9.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-security</artifactId> + <version>${jetty9.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-server</artifactId> + <version>${jetty9.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-servlet</artifactId> + <version>${jetty9.version}</version> + </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-util</artifactId> - <version>${jetty.version}</version> + <version>${jetty9.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-webapp</artifactId> + <version>${jetty9.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-xml</artifactId> + <version>${jetty9.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty.websocket</groupId> + <artifactId>jetty-websocket-api</artifactId> + <version>${jetty9.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty.websocket</groupId> + <artifactId>jetty-websocket-client</artifactId> + <version>${jetty9.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty.websocket</groupId> + <artifactId>jetty-websocket-common</artifactId> + <version>${jetty9.version}</version> </dependency> <!-- Force fresh woodstox-core without security issue --> <dependency> diff --git a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/Main.java b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/Main.java index 7ca98e4b9ba..11bd8815d77 100644 --- a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/Main.java +++ b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/Main.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.security.tool; import com.yahoo.vespa.security.tool.crypto.DecryptTool; import com.yahoo.vespa.security.tool.crypto.EncryptTool; import com.yahoo.vespa.security.tool.crypto.KeygenTool; +import com.yahoo.vespa.security.tool.crypto.TokenInfoTool; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; @@ -44,7 +45,7 @@ public class Main { } private static final List<Tool> TOOLS = List.of( - new KeygenTool(), new EncryptTool(), new DecryptTool()); + new KeygenTool(), new EncryptTool(), new DecryptTool(), new TokenInfoTool()); private static Optional<Tool> toolFromCliArgs(String[] args) { if (args.length == 0) { diff --git a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/KeygenTool.java b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/KeygenTool.java index a0b9cce710b..d7885dc6455 100644 --- a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/KeygenTool.java +++ b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/KeygenTool.java @@ -64,8 +64,14 @@ public class KeygenTool implements Tool { OPTIONS); } - private void handleExistingFileIfAny(Path filePath, boolean allowOverwrite) throws IOException { - if (filePath.toFile().exists()) { + private static void verifyNotSameKeyPaths(Path privPath, Path pubPath) { + if (privPath.equals(pubPath)) { + throw new IllegalArgumentException("Private and public key output files must be different"); + } + } + + private static void handleExistingFileIfAny(Path filePath, boolean allowOverwrite) throws IOException { + if (Files.exists(filePath)) { if (!allowOverwrite) { throw new IllegalArgumentException(("Output file '%s' already exists. No keys written. " + "If you want to overwrite existing files, specify --%s.") @@ -83,6 +89,7 @@ public class KeygenTool implements Tool { var arguments = invocation.arguments(); var privOutPath = Paths.get(CliUtils.optionOrThrow(arguments, PRIVATE_OUT_FILE_OPTION)); var pubOutPath = Paths.get(CliUtils.optionOrThrow(arguments, PUBLIC_OUT_FILE_OPTION)); + verifyNotSameKeyPaths(privOutPath, pubOutPath); boolean allowOverwrite = arguments.hasOption(OVERWRITE_EXISTING_OPTION); handleExistingFileIfAny(privOutPath, allowOverwrite); diff --git a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/TokenInfoTool.java b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/TokenInfoTool.java new file mode 100644 index 00000000000..dc597e9301f --- /dev/null +++ b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/TokenInfoTool.java @@ -0,0 +1,56 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.security.tool.crypto; + +import com.yahoo.security.SealedSharedKey; +import com.yahoo.text.StringUtilities; +import com.yahoo.vespa.security.tool.Tool; +import com.yahoo.vespa.security.tool.ToolDescription; +import com.yahoo.vespa.security.tool.ToolInvocation; +import org.apache.commons.cli.Option; + +import java.util.List; + +import static com.yahoo.security.ArrayUtils.fromUtf8Bytes; +import static com.yahoo.security.ArrayUtils.hex; + +/** + * Tooling to dump the various components of a decryption token + * + * @author vekterli + */ +public class TokenInfoTool implements Tool { + + private static final List<Option> OPTIONS = List.of(); + + @Override + public String name() { + return "token-info"; + } + + @Override + public ToolDescription description() { + return new ToolDescription( + "<token string>", + "Dumps information about the various components of a token", + "Note: this is a BETA tool version; its interface may be changed at any time", + OPTIONS); + } + + @Override + public int invoke(ToolInvocation invocation) { + var arguments = invocation.arguments(); + var leftoverArgs = arguments.getArgs(); + if (leftoverArgs.length != 1) { + throw new IllegalArgumentException("Expected exactly 1 token string argument"); + } + var token = SealedSharedKey.fromTokenString(leftoverArgs[0]); + var stdOut = invocation.stdOut(); + + stdOut.format("Version: %d\n", token.tokenVersion()); + stdOut.format("Key ID: %s (%s)\n", StringUtilities.escape(fromUtf8Bytes(token.keyId())), hex(token.keyId())); + stdOut.format("HPKE enc: %s\n", hex(token.enc())); + stdOut.format("HPKE ciphertext: %s\n", hex(token.ciphertext())); + + return 0; + } +} diff --git a/vespaclient-java/src/test/java/com/yahoo/vespa/security/tool/CryptoToolsTest.java b/vespaclient-java/src/test/java/com/yahoo/vespa/security/tool/CryptoToolsTest.java index a3651888441..8ba24d2cccc 100644 --- a/vespaclient-java/src/test/java/com/yahoo/vespa/security/tool/CryptoToolsTest.java +++ b/vespaclient-java/src/test/java/com/yahoo/vespa/security/tool/CryptoToolsTest.java @@ -72,6 +72,11 @@ public class CryptoToolsTest { } @Test + void token_info_help_printed_if_help_option_given_to_subtool() throws IOException { + verifyStdoutMatchesFile(List.of("token-info", "--help"), "expected-token-info-help-output.txt"); + } + + @Test void missing_required_parameter_prints_error_message() throws IOException { // We don't test all possible input arguments to all tools, since it'd be too closely // bound to the order in which the implementation checks for argument presence. @@ -108,6 +113,16 @@ public class CryptoToolsTest { .formatted(absPathOf(pubKeyFile))); } + @Test + void keygen_fails_if_priv_and_pub_paths_equal() throws IOException { + Path keyFile = pathInTemp("foo.txt"); + + verifyStderrEquals(List.of("keygen", + "--private-out-file", absPathOf(keyFile), + "--public-out-file", absPathOf(keyFile)), + "Invalid command line arguments: Private and public key output files must be different\n"); + } + // ... but we'll allow it if someone enables the foot-gun option. @Test void keygen_allowed_if_output_file_exists_and_explicit_overwrite_option_specified() throws IOException { @@ -214,6 +229,17 @@ public class CryptoToolsTest { } @Test + void token_info_fails_with_error_message_if_no_token_string_given() throws IOException { + verifyStderrEquals(List.of("token-info"), + "Invalid command line arguments: Expected exactly 1 token string argument\n"); + } + + @Test + void token_info_is_printed_to_stdout() throws IOException { + verifyStdoutMatchesFile(List.of("token-info", TEST_TOKEN), "expected-token-info-output.txt"); + } + + @Test void can_end_to_end_keygen_encrypt_and_decrypt_via_files() throws IOException { String greatSecret = "Dogs can't look up"; diff --git a/vespaclient-java/src/test/resources/expected-help-output.txt b/vespaclient-java/src/test/resources/expected-help-output.txt index 45cf829c981..c973735ad46 100644 --- a/vespaclient-java/src/test/resources/expected-help-output.txt +++ b/vespaclient-java/src/test/resources/expected-help-output.txt @@ -1,4 +1,4 @@ usage: vespa-security <tool> [TOOL OPTIONS] -Where <tool> is one of: keygen, encrypt, decrypt +Where <tool> is one of: keygen, encrypt, decrypt, token-info -h,--help Show help Invoke vespa-security <tool> --help for tool-specific help diff --git a/vespaclient-java/src/test/resources/expected-token-info-help-output.txt b/vespaclient-java/src/test/resources/expected-token-info-help-output.txt new file mode 100644 index 00000000000..32b2085d07f --- /dev/null +++ b/vespaclient-java/src/test/resources/expected-token-info-help-output.txt @@ -0,0 +1,5 @@ +usage: vespa-security token-info <token string> +Dumps information about the various components of a token + -h,--help Show help +Note: this is a BETA tool version; its interface may be changed at any +time diff --git a/vespaclient-java/src/test/resources/expected-token-info-output.txt b/vespaclient-java/src/test/resources/expected-token-info-output.txt new file mode 100644 index 00000000000..f0661533765 --- /dev/null +++ b/vespaclient-java/src/test/resources/expected-token-info-output.txt @@ -0,0 +1,4 @@ +Version: 1 +Key ID: my key ID (6d79206b6579204944) +HPKE enc: 02d4f1249766bf7794a16e59dcd252759de9a0a3c4916d1224e1905cfe82682e +HPKE ciphertext: 577f2015a1494afcdc9820cb092b2358294e9699a819a551b6cd0185268aa214 |