diff --git a/0001-Distro-jetty.home.patch b/0001-Distro-jetty.home.patch new file mode 100644 index 0000000000000000000000000000000000000000..269e227d4e337ce0c30dc825f5ae2870b156468f --- /dev/null +++ b/0001-Distro-jetty.home.patch @@ -0,0 +1,25 @@ +From 29d160bf916b4ab358a01496029f9aaa5fba66b3 Mon Sep 17 00:00:00 2001 +From: Mat Booth +Date: Mon, 9 Sep 2019 12:42:10 +0100 +Subject: [PATCH 1/2] Distro jetty.home + +--- + .../org/eclipse/jetty/start/config/CommandLineConfigSource.java | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/config/CommandLineConfigSource.java b/jetty-start/src/main/java/org/eclipse/jetty/start/config/CommandLineConfigSource.java +index 30440c4..cb0ed3d 100644 +--- a/jetty-start/src/main/java/org/eclipse/jetty/start/config/CommandLineConfigSource.java ++++ b/jetty-start/src/main/java/org/eclipse/jetty/start/config/CommandLineConfigSource.java +@@ -120,6 +120,8 @@ public class CommandLineConfigSource implements ConfigSource + try + { + Path home = new File(new URI(m.group(1))).getParentFile().toPath(); ++ if (home.endsWith("/usr/share/java/jetty")) ++ home = new File(home.toString().replaceAll("java/jetty$", "jetty")).toPath(); + setProperty(BaseHome.JETTY_HOME, home.toString(), ORIGIN_INTERNAL_FALLBACK); + return home; + } +-- +2.26.2 + diff --git a/0002-Port-to-servlet-api-4-5.patch b/0002-Port-to-servlet-api-4-5.patch new file mode 100644 index 0000000000000000000000000000000000000000..5078549a35dd4c9b4d95365a98626ba2800cadf5 --- /dev/null +++ b/0002-Port-to-servlet-api-4-5.patch @@ -0,0 +1,76 @@ +From 65b5de2ef9ffc5eacb23c6c4834cc1c513f0eafa Mon Sep 17 00:00:00 2001 +From: Mat Booth +Date: Wed, 19 Aug 2020 13:14:37 +0100 +Subject: [PATCH 2/2] Port to servlet-api 4/5 + +--- + .../jetty/server/handler/ContextHandler.java | 46 +++++++++++++++++++ + 1 file changed, 46 insertions(+) + +diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java +index 7960b0f..4981755 100644 +--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java ++++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java +@@ -2760,6 +2760,13 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu + return null; + } + ++ public ServletRegistration.Dynamic addJspFile(String servletName, String jspFile) ++ { ++ // TODO new in 4.0 ++ LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addJspFile(String, String)"); ++ return null; ++ } ++ + @Override + public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Class servletClass) + { +@@ -2930,6 +2937,45 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu + { + return null; + } ++ ++ public int getSessionTimeout() ++ { ++ // TODO new in 4.0 ++ LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getSessionTimeout()"); ++ return 0; ++ } ++ ++ public void setSessionTimeout(int sessionTimeout) ++ { ++ // TODO new in 4.0 ++ LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "setSessionTimeout(int)"); ++ } ++ ++ public String getRequestCharacterEncoding() ++ { ++ // TODO new in 4.0 ++ LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getRequestCharacterEncoding()"); ++ return null; ++ } ++ ++ public void setRequestCharacterEncoding(String encoding) ++ { ++ // TODO new in 4.0 ++ LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "setRequestCharacterEncoding(String)"); ++ } ++ ++ public String getResponseCharacterEncoding() ++ { ++ // TODO new in 4.0 ++ LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getResponseCharacterEncoding()"); ++ return null; ++ } ++ ++ public void setResponseCharacterEncoding(String encoding) ++ { ++ // TODO new in 4.0 ++ LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "setResponseCharacterEncoding(String)"); ++ } + } + + /** +-- +2.26.2 + diff --git a/CVE-2020-27216.patch b/CVE-2020-27216.patch deleted file mode 100755 index 6b8051ab615f29ede5f197fa434ccca0d8af871b..0000000000000000000000000000000000000000 --- a/CVE-2020-27216.patch +++ /dev/null @@ -1,477 +0,0 @@ -From: Markus Koschany -Date: Sat, 3 Jul 2021 22:05:57 +0200 -Subject: CVE-2020-27216 - -Origin: https://github.com/eclipse/jetty.project/commit/53e0e0e9b25a6309bf24ee3b10984f4145701edb -Origin: https://github.com/eclipse/jetty.project/commit/9ad6beb80543b392c91653f6bfce233fc75b9d5f ---- - .../eclipse/jetty/webapp/WebInfConfiguration.java | 20 +-- - .../server/session/InfinispanTestSupport.java | 173 +++++++++++++-------- - .../test/java/org/eclipse/jetty/TestServer.java | 54 +++---- - 3 files changed, 135 insertions(+), 112 deletions(-) - -diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java -index b94f788..19663bc 100644 ---- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java -+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java -@@ -24,6 +24,8 @@ import java.net.URI; - import java.net.URISyntaxException; - import java.net.URL; - import java.net.URLClassLoader; -+import java.nio.file.Files; -+import java.nio.file.Path; - import java.util.ArrayList; - import java.util.Arrays; - import java.util.List; -@@ -384,14 +386,10 @@ public class WebInfConfiguration extends AbstractConfiguration - @Override - public void cloneConfigure(WebAppContext template, WebAppContext context) throws Exception - { -- File tmpDir=File.createTempFile(WebInfConfiguration.getCanonicalNameForWebAppTmpDir(context),"",template.getTempDirectory().getParentFile()); -- if (tmpDir.exists()) -- { -- IO.delete(tmpDir); -- } -- tmpDir.mkdir(); -- tmpDir.deleteOnExit(); -- context.setTempDirectory(tmpDir); -+ Path tmpDir = Files.createTempDirectory(template.getTempDirectory().getParentFile().toPath(), WebInfConfiguration.getCanonicalNameForWebAppTmpDir(context)); -+ File tmpDirAsFile = tmpDir.toFile(); -+ tmpDirAsFile.deleteOnExit(); -+ context.setTempDirectory(tmpDirAsFile); - } - - -@@ -522,11 +520,7 @@ public class WebInfConfiguration extends AbstractConfiguration - else - { - //ensure file will always be unique by appending random digits -- tmpDir = File.createTempFile(temp, ".dir", parent); -- //delete the file that was created -- tmpDir.delete(); -- //and make a directory of the same name -- tmpDir.mkdirs(); -+ tmpDir = Files.createTempDirectory(parent.toPath(), temp).toFile(); - } - configureTempDirectory(tmpDir, context); - -diff --git a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSupport.java b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSupport.java -index 57ecb1f..663e8dd 100644 ---- a/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSupport.java -+++ b/tests/test-sessions/test-infinispan-sessions/src/test/java/org/eclipse/jetty/server/session/InfinispanTestSupport.java -@@ -1,6 +1,6 @@ - // - // ======================================================================== --// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. -+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. - // ------------------------------------------------------------------------ - // All rights reserved. This program and the accompanying materials - // are made available under the terms of the Eclipse Public License v1.0 -@@ -16,38 +16,44 @@ - // ======================================================================== - // - -- - package org.eclipse.jetty.server.session; - --import static org.junit.jupiter.api.Assertions.assertEquals; --import static org.junit.jupiter.api.Assertions.assertTrue; -- - import java.io.File; -+import java.lang.annotation.ElementType; -+import java.nio.file.Files; -+import java.nio.file.Path; -+import java.util.Properties; - -+import org.eclipse.jetty.toolchain.test.FS; - import org.eclipse.jetty.util.IO; -+import org.hibernate.search.cfg.Environment; -+import org.hibernate.search.cfg.SearchMapping; - import org.infinispan.Cache; --import org.infinispan.configuration.cache.Configuration; - import org.infinispan.configuration.cache.ConfigurationBuilder; -+import org.infinispan.configuration.cache.ConfigurationChildBuilder; -+import org.infinispan.configuration.cache.Index; - import org.infinispan.configuration.global.GlobalConfigurationBuilder; - import org.infinispan.manager.DefaultCacheManager; - import org.infinispan.manager.EmbeddedCacheManager; - -+import static org.junit.jupiter.api.Assertions.assertEquals; -+import static org.junit.jupiter.api.Assertions.assertTrue; -+ - /** - * InfinispanTestSupport -- * -- * - */ - public class InfinispanTestSupport - { -- public static final String DEFAULT_CACHE_NAME = "session_test_cache"; -- public Cache _cache; -- -+ public static final String DEFAULT_CACHE_NAME = "session_test_cache"; -+ public Cache _cache; -+ - public ConfigurationBuilder _builder; -- private File _tmpdir; -+ private File _tmpdir; - private boolean _useFileStore; -+ private boolean _serializeSessionData; - private String _name; -- public static EmbeddedCacheManager _manager; -- -+ public static EmbeddedCacheManager _manager; -+ - static - { - try -@@ -59,53 +65,84 @@ public class InfinispanTestSupport - e.printStackTrace(); - } - } -- -- -- -- -- public InfinispanTestSupport () -+ -+ public InfinispanTestSupport() - { -- this (null); -+ this(null); - } -- -+ - public InfinispanTestSupport(String cacheName) -- { -+ { - if (cacheName == null) -- cacheName = DEFAULT_CACHE_NAME+System.currentTimeMillis(); -- -+ cacheName = DEFAULT_CACHE_NAME + System.currentTimeMillis(); -+ - _name = cacheName; - _builder = new ConfigurationBuilder(); - } -- -- public void setUseFileStore (boolean useFileStore) -+ -+ public void setUseFileStore(boolean useFileStore) - { - _useFileStore = useFileStore; - } -- -- public Cache getCache () -+ -+ public void setSerializeSessionData(boolean serializeSessionData) - { -- return _cache; -+ _serializeSessionData = serializeSessionData; - } - -- public void setup () throws Exception -+ public Cache getCache() - { -- if (_useFileStore) -- { -- _tmpdir = File.createTempFile("infini", "span"); -- _tmpdir.delete(); -- _tmpdir.mkdir(); -- Configuration config = _builder.persistence().addSingleFileStore().location(_tmpdir.getAbsolutePath()).storeAsBinary().build(); -- _manager.defineConfiguration(_name, config); -- } -- else -- { -- _manager.defineConfiguration(_name, _builder.build()); -- } -- _cache = _manager.getCache(_name); -+ return _cache; - } - -+ public void setup(Path root) throws Exception -+ { -+ Path indexesDir = root.resolve("indexes"); -+ FS.ensureDirExists(indexesDir); -+ -+ SearchMapping mapping = new SearchMapping(); -+ mapping.entity(SessionData.class).indexed().providedId().property("expiry", ElementType.FIELD).field(); -+ Properties properties = new Properties(); -+ properties.put(Environment.MODEL_MAPPING, mapping); -+ properties.put("hibernate.search.default.indexBase", indexesDir.toString()); -+ -+ if (_useFileStore) -+ { -+ Path tmpDir = Files.createTempDirectory("infinispan"); -+ _tmpdir = tmpDir.toFile(); -+ -+ ConfigurationChildBuilder b = _builder.indexing() -+ .index(Index.ALL) -+ .addIndexedEntity(SessionData.class) -+ .withProperties(properties) -+ .persistence() -+ .addSingleFileStore() -+ .location(_tmpdir.getAbsolutePath()); -+ if (_serializeSessionData) -+ { -+ b = b.storeAsBinary().enable(); -+ } -+ -+ _manager.defineConfiguration(_name, b.build()); -+ } -+ else -+ { -+ ConfigurationChildBuilder b = _builder.indexing() -+ .withProperties(properties) -+ .index(Index.ALL) -+ .addIndexedEntity(SessionData.class); -+ -+ if (_serializeSessionData) -+ { -+ b = b.storeAsBinary().enable(); -+ } -+ -+ _manager.defineConfiguration(_name, b.build()); -+ } -+ _cache = _manager.getCache(_name); -+ } - -- public void teardown () throws Exception -+ public void teardown() throws Exception - { - _cache.clear(); - _manager.removeCache(_name); -@@ -117,39 +154,41 @@ public class InfinispanTestSupport - } - } - } -- -- -+ - @SuppressWarnings("unchecked") -- public void createSession (SessionData data) -- throws Exception -+ public void createSession(SessionData data) -+ throws Exception - { -- _cache.put(data.getContextPath()+"_"+data.getVhost()+"_"+data.getId(), data); -+ _cache.put(data.getContextPath() + "_" + data.getVhost() + "_" + data.getId(), data); - } - -- -- public void createUnreadableSession (SessionData data) -+ public void createUnreadableSession(SessionData data) - { -- -+ - } -- -- -- public boolean checkSessionExists (SessionData data) -- throws Exception -+ -+ public boolean checkSessionExists(SessionData data) -+ throws Exception - { -- return (_cache.get(data.getContextPath()+"_"+data.getVhost()+"_"+data.getId()) != null); -+ return (_cache.get(data.getContextPath() + "_" + data.getVhost() + "_" + data.getId()) != null); - } -- -- -- public boolean checkSessionPersisted (SessionData data) -- throws Exception -+ -+ public boolean checkSessionPersisted(SessionData data) -+ throws Exception - { -- Object obj = _cache.get(data.getContextPath()+"_"+data.getVhost()+"_"+data.getId()); -+ -+ //evicts the object from memory. Forces the cache to fetch the data from file -+ if (_useFileStore) -+ { -+ _cache.evict(data.getContextPath() + "_" + data.getVhost() + "_" + data.getId()); -+ } -+ -+ Object obj = _cache.get(data.getContextPath() + "_" + data.getVhost() + "_" + data.getId()); - if (obj == null) - return false; -- -+ - SessionData saved = (SessionData)obj; -- -- -+ - //turn an Entity into a Session - assertEquals(data.getId(), saved.getId()); - assertEquals(data.getContextPath(), saved.getContextPath()); -@@ -168,11 +207,11 @@ public class InfinispanTestSupport - //same keys - assertTrue(data.getKeys().equals(saved.getKeys())); - //same values -- for (String name:data.getKeys()) -+ for (String name : data.getKeys()) - { - assertTrue(data.getAttribute(name).equals(saved.getAttribute(name))); - } -- -+ - return true; - } - } -diff --git a/tests/test-webapps/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java b/tests/test-webapps/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java -index a7af064..c1c5dc5 100644 ---- a/tests/test-webapps/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java -+++ b/tests/test-webapps/test-jetty-webapp/src/test/java/org/eclipse/jetty/TestServer.java -@@ -1,6 +1,6 @@ - // - // ======================================================================== --// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. -+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. - // ------------------------------------------------------------------------ - // All rights reserved. This program and the accompanying materials - // are made available under the terms of the Eclipse Public License v1.0 -@@ -18,13 +18,11 @@ - - package org.eclipse.jetty; - --import java.io.File; - import java.io.IOException; - import java.lang.management.ManagementFactory; - import java.nio.file.FileSystems; - import java.nio.file.Files; - import java.nio.file.Path; -- - import javax.servlet.ServletException; - import javax.servlet.http.HttpServletRequest; - import javax.servlet.http.HttpServletResponse; -@@ -65,11 +63,11 @@ public class TestServer - ((StdErrLog)Log.getLog()).setSource(false); - - // TODO don't depend on this file structure -- Path jetty_root = FileSystems.getDefault().getPath(".").toAbsolutePath().normalize(); -- if (!Files.exists(jetty_root.resolve("VERSION.txt"))) -- jetty_root = FileSystems.getDefault().getPath("../../..").toAbsolutePath().normalize(); -- if (!Files.exists(jetty_root.resolve("VERSION.txt"))) -- throw new IllegalArgumentException(jetty_root.toString()); -+ Path jettyRoot = FileSystems.getDefault().getPath(".").toAbsolutePath().normalize(); -+ if (!Files.exists(jettyRoot.resolve("VERSION.txt"))) -+ jettyRoot = FileSystems.getDefault().getPath("../../..").toAbsolutePath().normalize(); -+ if (!Files.exists(jettyRoot.resolve("VERSION.txt"))) -+ throw new IllegalArgumentException(jettyRoot.toString()); - - // Setup Threadpool - QueuedThreadPool threadPool = new QueuedThreadPool(); -@@ -80,10 +78,9 @@ public class TestServer - server.manage(threadPool); - - // Setup JMX -- MBeanContainer mbContainer=new MBeanContainer(ManagementFactory.getPlatformMBeanServer()); -+ MBeanContainer mbContainer = new MBeanContainer(ManagementFactory.getPlatformMBeanServer()); - server.addBean(mbContainer); - server.addBean(Log.getLog()); -- - - // Common HTTP configuration - HttpConfiguration config = new HttpConfiguration(); -@@ -92,21 +89,19 @@ public class TestServer - config.addCustomizer(new SecureRequestCustomizer()); - config.setSendDateHeader(true); - config.setSendServerVersion(true); -- -- -+ - // Http Connector - HttpConnectionFactory http = new HttpConnectionFactory(config); -- ServerConnector httpConnector = new ServerConnector(server,http); -+ ServerConnector httpConnector = new ServerConnector(server, http); - httpConnector.setPort(8080); - httpConnector.setIdleTimeout(30000); - server.addConnector(httpConnector); - -- - // Handlers - HandlerCollection handlers = new HandlerCollection(); - ContextHandlerCollection contexts = new ContextHandlerCollection(); - handlers.setHandlers(new Handler[] -- { contexts, new DefaultHandler() }); -+ {contexts, new DefaultHandler()}); - - // Add restart handler to test the ability to save sessions and restart - RestartHandler restart = new RestartHandler(); -@@ -114,15 +109,14 @@ public class TestServer - - server.setHandler(restart); - -- - // Setup context - HashLoginService login = new HashLoginService(); - login.setName("Test Realm"); -- login.setConfig(jetty_root.resolve("tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/etc/realm.properties").toString()); -+ login.setConfig(jettyRoot.resolve("tests/test-webapps/test-jetty-webapp/src/main/config/demo-base/etc/realm.properties").toString()); - server.addBean(login); - -- File log=File.createTempFile("jetty-yyyy_mm_dd", "log"); -- CustomRequestLog requestLog = new CustomRequestLog(log.toString()); -+ Path logPath = Files.createTempFile("jetty-yyyy_mm_dd", "log"); -+ CustomRequestLog requestLog = new CustomRequestLog(logPath.toString()); - server.setRequestLog(requestLog); - - server.setStopAtShutdown(true); -@@ -130,23 +124,19 @@ public class TestServer - WebAppContext webapp = new WebAppContext(); - webapp.setContextPath("/test"); - webapp.setParentLoaderPriority(true); -- webapp.setResourceBase(jetty_root.resolve("tests/test-webapps/test-jetty-webapp/src/main/webapp").toString()); -- webapp.setAttribute("testAttribute","testValue"); -- File sessiondir=File.createTempFile("sessions",null); -- if (sessiondir.exists()) -- sessiondir.delete(); -- sessiondir.mkdir(); -- sessiondir.deleteOnExit(); -+ webapp.setResourceBase(jettyRoot.resolve("tests/test-webapps/test-jetty-webapp/src/main/webapp").toString()); -+ webapp.setAttribute("testAttribute", "testValue"); -+ Path sessionDir = Files.createTempDirectory("sessions"); - DefaultSessionCache ss = new DefaultSessionCache(webapp.getSessionHandler()); - FileSessionDataStore sds = new FileSessionDataStore(); - ss.setSessionDataStore(sds); -- sds.setStoreDir(sessiondir); -+ sds.setStoreDir(sessionDir.toFile()); - webapp.getSessionHandler().setSessionCache(ss); - - contexts.addHandler(webapp); - - ContextHandler srcroot = new ContextHandler(); -- srcroot.setResourceBase(jetty_root.resolve("tests/test-webapps/test-jetty-webapp/src").toString()); -+ srcroot.setResourceBase(jettyRoot.resolve("tests/test-webapps/test-jetty-webapp/src").toString()); - srcroot.setHandler(new ResourceHandler()); - srcroot.setContextPath("/src"); - contexts.addHandler(srcroot); -@@ -158,17 +148,17 @@ public class TestServer - - private static class RestartHandler extends HandlerWrapper - { -- /* ------------------------------------------------------------ */ -+ - /** - * @see org.eclipse.jetty.server.handler.HandlerWrapper#handle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) - */ - @Override - public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException - { -- super.handle(target,baseRequest,request,response); -+ super.handle(target, baseRequest, request, response); - if (Boolean.valueOf(request.getParameter("restart"))) - { -- final Server server=getServer(); -+ final Server server = getServer(); - - new Thread() - { -@@ -182,7 +172,7 @@ public class TestServer - Thread.sleep(100); - server.start(); - } -- catch(Exception e) -+ catch (Exception e) - { - LOG.warn(e); - } diff --git a/CVE-2020-27223.patch b/CVE-2020-27223.patch deleted file mode 100755 index 7d31ce49eb56789ae332820325ac68e8f54aac83..0000000000000000000000000000000000000000 --- a/CVE-2020-27223.patch +++ /dev/null @@ -1,1196 +0,0 @@ -From: Markus Koschany -Date: Sat, 31 Jul 2021 17:21:57 +0200 -Subject: CVE-2020-27223 - ---- - .../java/org/eclipse/jetty/http/QuotedCSV.java | 280 ++----------------- - .../org/eclipse/jetty/http/QuotedCSVParser.java | 303 +++++++++++++++++++++ - .../org/eclipse/jetty/http/QuotedQualityCSV.java | 140 ++++++---- - .../eclipse/jetty/http/QuotedQualityCSVTest.java | 143 +++++----- - 4 files changed, 479 insertions(+), 387 deletions(-) - create mode 100644 jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSVParser.java - -diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSV.java b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSV.java -index 9ca7dbe..a356213 100644 ---- a/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSV.java -+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSV.java -@@ -1,6 +1,6 @@ - // - // ======================================================================== --// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. -+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. - // ------------------------------------------------------------------------ - // All rights reserved. This program and the accompanying materials - // are made available under the terms of the Eclipse Public License v1.0 -@@ -22,236 +22,36 @@ import java.util.ArrayList; - import java.util.Iterator; - import java.util.List; - --import org.eclipse.jetty.util.QuotedStringTokenizer; -- --/* ------------------------------------------------------------ */ - /** - * Implements a quoted comma separated list of values - * in accordance with RFC7230. - * OWS is removed and quoted characters ignored for parsing. -+ * - * @see "https://tools.ietf.org/html/rfc7230#section-3.2.6" - * @see "https://tools.ietf.org/html/rfc7230#section-7" - */ --public class QuotedCSV implements Iterable --{ -- private enum State { VALUE, PARAM_NAME, PARAM_VALUE}; -- -+public class QuotedCSV extends QuotedCSVParser implements Iterable -+{ - protected final List _values = new ArrayList<>(); -- protected final boolean _keepQuotes; -- -- /* ------------------------------------------------------------ */ -+ - public QuotedCSV(String... values) - { -- this(true,values); -+ this(true, values); - } -- -- /* ------------------------------------------------------------ */ -- public QuotedCSV(boolean keepQuotes,String... values) -- { -- _keepQuotes=keepQuotes; -- for (String v:values) -- addValue(v); -- } -- -- /* ------------------------------------------------------------ */ -- /** Add and parse a value string(s) -- * @param value A value that may contain one or more Quoted CSV items. -- */ -- public void addValue(String value) -+ -+ public QuotedCSV(boolean keepQuotes, String... values) - { -- if (value == null) -- return; -- -- StringBuffer buffer = new StringBuffer(); -- -- int l=value.length(); -- State state=State.VALUE; -- boolean quoted=false; -- boolean sloshed=false; -- int nws_length=0; -- int last_length=0; -- int value_length=-1; -- int param_name=-1; -- int param_value=-1; -- -- for (int i=0;i<=l;i++) -+ super(keepQuotes); -+ for (String v : values) - { -- char c=i==l?0:value.charAt(i); -- -- // Handle quoting https://tools.ietf.org/html/rfc7230#section-3.2.6 -- if (quoted && c!=0) -- { -- if (sloshed) -- sloshed=false; -- else -- { -- switch(c) -- { -- case '\\': -- sloshed=true; -- if (!_keepQuotes) -- continue; -- break; -- case '"': -- quoted=false; -- if (!_keepQuotes) -- continue; -- break; -- } -- } -- -- buffer.append(c); -- nws_length=buffer.length(); -- continue; -- } -- -- // Handle common cases -- switch(c) -- { -- case ' ': -- case '\t': -- if (buffer.length()>last_length) // not leading OWS -- buffer.append(c); -- continue; -- -- case '"': -- quoted=true; -- if (_keepQuotes) -- { -- if (state==State.PARAM_VALUE && param_value<0) -- param_value=nws_length; -- buffer.append(c); -- } -- else if (state==State.PARAM_VALUE && param_value<0) -- param_value=nws_length; -- nws_length=buffer.length(); -- continue; -- -- case ';': -- buffer.setLength(nws_length); // trim following OWS -- if (state==State.VALUE) -- { -- parsedValue(buffer); -- value_length=buffer.length(); -- } -- else -- parsedParam(buffer,value_length,param_name,param_value); -- nws_length=buffer.length(); -- param_name=param_value=-1; -- buffer.append(c); -- last_length=++nws_length; -- state=State.PARAM_NAME; -- continue; -- -- case ',': -- case 0: -- if (nws_length>0) -- { -- buffer.setLength(nws_length); // trim following OWS -- switch(state) -- { -- case VALUE: -- parsedValue(buffer); -- value_length=buffer.length(); -- break; -- case PARAM_NAME: -- case PARAM_VALUE: -- parsedParam(buffer,value_length,param_name,param_value); -- break; -- } -- _values.add(buffer.toString()); -- } -- buffer.setLength(0); -- last_length=0; -- nws_length=0; -- value_length=param_name=param_value=-1; -- state=State.VALUE; -- continue; -- -- case '=': -- switch (state) -- { -- case VALUE: -- // It wasn't really a value, it was a param name -- value_length=param_name=0; -- buffer.setLength(nws_length); // trim following OWS -- String param = buffer.toString(); -- buffer.setLength(0); -- parsedValue(buffer); -- value_length=buffer.length(); -- buffer.append(param); -- buffer.append(c); -- last_length=++nws_length; -- state=State.PARAM_VALUE; -- continue; -- -- case PARAM_NAME: -- buffer.setLength(nws_length); // trim following OWS -- buffer.append(c); -- last_length=++nws_length; -- state=State.PARAM_VALUE; -- continue; -- -- case PARAM_VALUE: -- if (param_value<0) -- param_value=nws_length; -- buffer.append(c); -- nws_length=buffer.length(); -- continue; -- } -- continue; -- -- default: -- { -- switch (state) -- { -- case VALUE: -- { -- buffer.append(c); -- nws_length=buffer.length(); -- continue; -- } -- -- case PARAM_NAME: -- { -- if (param_name<0) -- param_name=nws_length; -- buffer.append(c); -- nws_length=buffer.length(); -- continue; -- } -- -- case PARAM_VALUE: -- { -- if (param_value<0) -- param_value=nws_length; -- buffer.append(c); -- nws_length=buffer.length(); -- continue; -- } -- } -- } -- } -+ addValue(v); - } - } - -- /** -- * Called when a value has been parsed -- * @param buffer Containing the trimmed value, which may be mutated -- */ -- protected void parsedValue(StringBuffer buffer) -- { -- } -- -- /** -- * Called when a parameter has been parsed -- * @param buffer Containing the trimmed value and all parameters, which may be mutated -- * @param valueLength The length of the value -- * @param paramName The index of the start of the parameter just parsed -- * @param paramValue The index of the start of the parameter value just parsed, or -1 -- */ -- protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue) -+ @Override -+ protected void parsedValueAndParams(StringBuffer buffer) - { -+ _values.add(buffer.toString()); - } - - public int size() -@@ -268,67 +68,21 @@ public class QuotedCSV implements Iterable - { - return _values; - } -- -+ - @Override - public Iterator iterator() - { - return _values.iterator(); - } -- -- public static String unquote(String s) -- { -- // handle trivial cases -- int l=s.length(); -- if (s==null || l==0) -- return s; -- -- // Look for any quotes -- int i=0; -- for (;i list = new ArrayList<>(); - for (String s : this) -+ { - list.add(s); -+ } - return list.toString(); - } - } -diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSVParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSVParser.java -new file mode 100644 -index 0000000..7aefcf7 ---- /dev/null -+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedCSVParser.java -@@ -0,0 +1,303 @@ -+// -+// ======================================================================== -+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. -+// ------------------------------------------------------------------------ -+// All rights reserved. This program and the accompanying materials -+// are made available under the terms of the Eclipse Public License v1.0 -+// and Apache License v2.0 which accompanies this distribution. -+// -+// The Eclipse Public License is available at -+// http://www.eclipse.org/legal/epl-v10.html -+// -+// The Apache License v2.0 is available at -+// http://www.opensource.org/licenses/apache2.0.php -+// -+// You may elect to redistribute this code under either of these licenses. -+// ======================================================================== -+// -+ -+package org.eclipse.jetty.http; -+ -+/** -+ * Implements a quoted comma separated list parser -+ * in accordance with RFC7230. -+ * OWS is removed and quoted characters ignored for parsing. -+ * -+ * @see "https://tools.ietf.org/html/rfc7230#section-3.2.6" -+ * @see "https://tools.ietf.org/html/rfc7230#section-7" -+ */ -+public abstract class QuotedCSVParser -+{ -+ private enum State -+ { -+ VALUE, PARAM_NAME, PARAM_VALUE -+ } -+ -+ protected final boolean _keepQuotes; -+ -+ public QuotedCSVParser(boolean keepQuotes) -+ { -+ _keepQuotes = keepQuotes; -+ } -+ -+ public static String unquote(String s) -+ { -+ // handle trivial cases -+ int l = s.length(); -+ if (s == null || l == 0) -+ return s; -+ -+ // Look for any quotes -+ int i = 0; -+ for (; i < l; i++) -+ { -+ char c = s.charAt(i); -+ if (c == '"') -+ break; -+ } -+ if (i == l) -+ return s; -+ -+ boolean quoted = true; -+ boolean sloshed = false; -+ StringBuffer buffer = new StringBuffer(); -+ buffer.append(s, 0, i); -+ i++; -+ for (; i < l; i++) -+ { -+ char c = s.charAt(i); -+ if (quoted) -+ { -+ if (sloshed) -+ { -+ buffer.append(c); -+ sloshed = false; -+ } -+ else if (c == '"') -+ quoted = false; -+ else if (c == '\\') -+ sloshed = true; -+ else -+ buffer.append(c); -+ } -+ else if (c == '"') -+ quoted = true; -+ else -+ buffer.append(c); -+ } -+ return buffer.toString(); -+ } -+ -+ /** -+ * Add and parse a value string(s) -+ * -+ * @param value A value that may contain one or more Quoted CSV items. -+ */ -+ public void addValue(String value) -+ { -+ if (value == null) -+ return; -+ -+ StringBuffer buffer = new StringBuffer(); -+ -+ int l = value.length(); -+ State state = State.VALUE; -+ boolean quoted = false; -+ boolean sloshed = false; -+ int nwsLength = 0; -+ int lastLength = 0; -+ int valueLength = -1; -+ int paramName = -1; -+ int paramValue = -1; -+ -+ for (int i = 0; i <= l; i++) -+ { -+ char c = i == l ? 0 : value.charAt(i); -+ -+ // Handle quoting https://tools.ietf.org/html/rfc7230#section-3.2.6 -+ if (quoted && c != 0) -+ { -+ if (sloshed) -+ sloshed = false; -+ else -+ { -+ switch (c) -+ { -+ case '\\': -+ sloshed = true; -+ if (!_keepQuotes) -+ continue; -+ break; -+ case '"': -+ quoted = false; -+ if (!_keepQuotes) -+ continue; -+ break; -+ } -+ } -+ -+ buffer.append(c); -+ nwsLength = buffer.length(); -+ continue; -+ } -+ -+ // Handle common cases -+ switch (c) -+ { -+ case ' ': -+ case '\t': -+ if (buffer.length() > lastLength) // not leading OWS -+ buffer.append(c); -+ continue; -+ -+ case '"': -+ quoted = true; -+ if (_keepQuotes) -+ { -+ if (state == State.PARAM_VALUE && paramValue < 0) -+ paramValue = nwsLength; -+ buffer.append(c); -+ } -+ else if (state == State.PARAM_VALUE && paramValue < 0) -+ paramValue = nwsLength; -+ nwsLength = buffer.length(); -+ continue; -+ -+ case ';': -+ buffer.setLength(nwsLength); // trim following OWS -+ if (state == State.VALUE) -+ { -+ parsedValue(buffer); -+ valueLength = buffer.length(); -+ } -+ else -+ parsedParam(buffer, valueLength, paramName, paramValue); -+ nwsLength = buffer.length(); -+ paramName = paramValue = -1; -+ buffer.append(c); -+ lastLength = ++nwsLength; -+ state = State.PARAM_NAME; -+ continue; -+ -+ case ',': -+ case 0: -+ if (nwsLength > 0) -+ { -+ buffer.setLength(nwsLength); // trim following OWS -+ switch (state) -+ { -+ case VALUE: -+ parsedValue(buffer); -+ valueLength = buffer.length(); -+ break; -+ case PARAM_NAME: -+ case PARAM_VALUE: -+ parsedParam(buffer, valueLength, paramName, paramValue); -+ break; -+ } -+ parsedValueAndParams(buffer); -+ } -+ buffer.setLength(0); -+ lastLength = 0; -+ nwsLength = 0; -+ valueLength = paramName = paramValue = -1; -+ state = State.VALUE; -+ continue; -+ -+ case '=': -+ switch (state) -+ { -+ case VALUE: -+ // It wasn't really a value, it was a param name -+ valueLength = paramName = 0; -+ buffer.setLength(nwsLength); // trim following OWS -+ String param = buffer.toString(); -+ buffer.setLength(0); -+ parsedValue(buffer); -+ valueLength = buffer.length(); -+ buffer.append(param); -+ buffer.append(c); -+ lastLength = ++nwsLength; -+ state = State.PARAM_VALUE; -+ continue; -+ -+ case PARAM_NAME: -+ buffer.setLength(nwsLength); // trim following OWS -+ buffer.append(c); -+ lastLength = ++nwsLength; -+ state = State.PARAM_VALUE; -+ continue; -+ -+ case PARAM_VALUE: -+ if (paramValue < 0) -+ paramValue = nwsLength; -+ buffer.append(c); -+ nwsLength = buffer.length(); -+ continue; -+ } -+ continue; -+ -+ default: -+ { -+ switch (state) -+ { -+ case VALUE: -+ { -+ buffer.append(c); -+ nwsLength = buffer.length(); -+ continue; -+ } -+ -+ case PARAM_NAME: -+ { -+ if (paramName < 0) -+ paramName = nwsLength; -+ buffer.append(c); -+ nwsLength = buffer.length(); -+ continue; -+ } -+ -+ case PARAM_VALUE: -+ { -+ if (paramValue < 0) -+ paramValue = nwsLength; -+ buffer.append(c); -+ nwsLength = buffer.length(); -+ continue; -+ } -+ } -+ } -+ } -+ } -+ } -+ -+ /** -+ * Called when a value and it's parameters has been parsed -+ * -+ * @param buffer Containing the trimmed value and parameters -+ */ -+ protected void parsedValueAndParams(StringBuffer buffer) -+ { -+ } -+ -+ /** -+ * Called when a value has been parsed (prior to any parameters) -+ * -+ * @param buffer Containing the trimmed value, which may be mutated -+ */ -+ protected void parsedValue(StringBuffer buffer) -+ { -+ } -+ -+ /** -+ * Called when a parameter has been parsed -+ * -+ * @param buffer Containing the trimmed value and all parameters, which may be mutated -+ * @param valueLength The length of the value -+ * @param paramName The index of the start of the parameter just parsed -+ * @param paramValue The index of the start of the parameter value just parsed, or -1 -+ */ -+ protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue) -+ { -+ } -+} -diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedQualityCSV.java b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedQualityCSV.java -index d148d9e..5bc9985 100644 ---- a/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedQualityCSV.java -+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/QuotedQualityCSV.java -@@ -1,6 +1,6 @@ - // - // ======================================================================== --// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. -+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. - // ------------------------------------------------------------------------ - // All rights reserved. This program and the accompanying materials - // are made available under the terms of the Eclipse Public License v1.0 -@@ -21,14 +21,12 @@ package org.eclipse.jetty.http; - import java.util.ArrayList; - import java.util.Iterator; - import java.util.List; -+import java.util.Objects; - import java.util.function.ToIntFunction; -+import java.util.stream.Collectors; - - import org.eclipse.jetty.util.log.Log; - --import static java.lang.Integer.MIN_VALUE; -- --/* ------------------------------------------------------------ */ -- - /** - * Implements a quoted comma separated list of quality values - * in accordance with RFC7230 and RFC7231. -@@ -57,22 +55,19 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable - return 3; - }; - -- private final List _quality = new ArrayList<>(); -+ private final List _qualities = new ArrayList<>(); -+ private QualityValue _lastQuality; - private boolean _sorted = false; - private final ToIntFunction _secondaryOrdering; - -- /* ------------------------------------------------------------ */ -- - /** - * Sorts values with equal quality according to the length of the value String. - */ - public QuotedQualityCSV() - { -- this((ToIntFunction)null); -+ this((ToIntFunction)null); - } - -- /* ------------------------------------------------------------ */ -- - /** - * Sorts values with equal quality according to given order. - * -@@ -83,57 +78,71 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable - this((s) -> - { - for (int i = 0; i < preferredOrder.length; ++i) -+ { - if (preferredOrder[i].equals(s)) - return preferredOrder.length - i; -+ } - - if ("*".equals(s)) - return preferredOrder.length; - -- return MIN_VALUE; -+ return 0; - }); - } - -- /* ------------------------------------------------------------ */ -- - /** - * Orders values with equal quality with the given function. - * -- * @param secondaryOrdering Function to apply an ordering other than specified by quality -+ * @param secondaryOrdering Function to apply an ordering other than specified by quality, highest values are sorted first. - */ - public QuotedQualityCSV(ToIntFunction secondaryOrdering) - { - this._secondaryOrdering = secondaryOrdering == null ? s -> 0 : secondaryOrdering; - } - -- /* ------------------------------------------------------------ */ -+ @Override -+ protected void parsedValueAndParams(StringBuffer buffer) -+ { -+ super.parsedValueAndParams(buffer); -+ -+ // Collect full value with parameters -+ _lastQuality = new QualityValue(_lastQuality._quality, buffer.toString(), _lastQuality._index); -+ _qualities.set(_lastQuality._index, _lastQuality); -+ } -+ - @Override - protected void parsedValue(StringBuffer buffer) - { - super.parsedValue(buffer); - -+ _sorted = false; -+ -+ // This is the just the value, without parameters. - // Assume a quality of ONE -- _quality.add(1.0D); -+ _lastQuality = new QualityValue(1.0D, buffer.toString(), _qualities.size()); -+ _qualities.add(_lastQuality); - } - -- /* ------------------------------------------------------------ */ - @Override - protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue) - { -+ _sorted = false; -+ - if (paramName < 0) - { - if (buffer.charAt(buffer.length() - 1) == ';') - buffer.setLength(buffer.length() - 1); - } - else if (paramValue >= 0 && -- buffer.charAt(paramName) == 'q' && paramValue > paramName && -- buffer.length() >= paramName && buffer.charAt(paramName + 1) == '=') -+ buffer.charAt(paramName) == 'q' && paramValue > paramName && -+ buffer.length() >= paramName && buffer.charAt(paramName + 1) == '=') - { -- Double q; -+ double q; - try - { - q = (_keepQuotes && buffer.charAt(paramValue) == '"') -- ? Double.valueOf(buffer.substring(paramValue + 1, buffer.length() - 1)) -- : Double.valueOf(buffer.substring(paramValue)); -+ ? Double.valueOf(buffer.substring(paramValue + 1, buffer.length() - 1)) -+ : Double.valueOf(buffer.substring(paramValue)); - } - catch (Exception e) - { -@@ -143,8 +152,10 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable - buffer.setLength(Math.max(0, paramName - 1)); - - if (q != 1.0D) -- // replace assumed quality -- _quality.set(_quality.size() - 1, q); -+ { -+ _lastQuality = new QualityValue(q, buffer.toString(), _lastQuality._index); -+ _qualities.set(_lastQuality._index, _lastQuality); -+ } - } - } - -@@ -166,38 +177,73 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable - - protected void sort() - { -+ _values.clear(); -+ _qualities.stream() -+ .filter((qv) -> qv._quality != 0.0D) -+ .sorted() -+ .map(QualityValue::getValue) -+ .collect(Collectors.toCollection(() -> _values)); - _sorted = true; -+ } -+ -+ private class QualityValue implements Comparable -+ { -+ private final double _quality; -+ private final String _value; -+ private final int _index; - -- Double last = 0.0D; -- int lastSecondaryOrder = Integer.MIN_VALUE; -+ private QualityValue(double quality, String value, int index) -+ { -+ _quality = quality; -+ _value = value; -+ _index = index; -+ } - -- for (int i = _values.size(); i-- > 0; ) -+ @Override -+ public int hashCode() - { -- String v = _values.get(i); -- Double q = _quality.get(i); -+ return Double.hashCode(_quality) ^ Objects.hash(_value, _index); -+ } - -- int compare = last.compareTo(q); -- if (compare > 0 || (compare == 0 && _secondaryOrdering.applyAsInt(v) < lastSecondaryOrder)) -+ @Override -+ public boolean equals(Object obj) -+ { -+ if (!(obj instanceof QualityValue)) -+ return false; -+ QualityValue qv = (QualityValue)obj; -+ return _quality == qv._quality && Objects.equals(_value, qv._value) && Objects.equals(_index, qv._index); -+ } -+ -+ private String getValue() -+ { -+ return _value; -+ } -+ -+ @Override -+ public int compareTo(QualityValue o) -+ { -+ // sort highest quality first -+ int compare = Double.compare(o._quality, _quality); -+ if (compare == 0) - { -- _values.set(i, _values.get(i + 1)); -- _values.set(i + 1, v); -- _quality.set(i, _quality.get(i + 1)); -- _quality.set(i + 1, q); -- last = 0.0D; -- lastSecondaryOrder = 0; -- i = _values.size(); -- continue; -+ // then sort secondary order highest first -+ compare = Integer.compare(_secondaryOrdering.applyAsInt(o._value), _secondaryOrdering.applyAsInt(_value)); -+ if (compare == 0) -+ // then sort index lowest first -+ compare = -Integer.compare(o._index, _index); - } -- -- last = q; -- lastSecondaryOrder = _secondaryOrdering.applyAsInt(v); -+ return compare; - } - -- int last_element = _quality.size(); -- while (last_element > 0 && _quality.get(--last_element).equals(0.0D)) -+ @Override -+ public String toString() - { -- _quality.remove(last_element); -- _values.remove(last_element); -+ return String.format("%s@%x[%s,q=%f,i=%d]", -+ getClass().getSimpleName(), -+ hashCode(), -+ _value, -+ _quality, -+ _index); - } - } - } -diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/QuotedQualityCSVTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/QuotedQualityCSVTest.java -index f03657b..b941e95 100644 ---- a/jetty-http/src/test/java/org/eclipse/jetty/http/QuotedQualityCSVTest.java -+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/QuotedQualityCSVTest.java -@@ -1,6 +1,6 @@ - // - // ======================================================================== --// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. -+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. - // ------------------------------------------------------------------------ - // All rights reserved. This program and the accompanying materials - // are made available under the terms of the Eclipse Public License v1.0 -@@ -18,13 +18,12 @@ - - package org.eclipse.jetty.http; - -+import java.util.ArrayList; -+import java.util.List; - - import org.hamcrest.Matchers; - import org.junit.jupiter.api.Test; - --import java.util.ArrayList; --import java.util.List; -- - import static org.hamcrest.MatcherAssert.assertThat; - import static org.hamcrest.Matchers.contains; - -@@ -32,58 +31,58 @@ public class QuotedQualityCSVTest - { - - @Test -- public void test7231_5_3_2_example1() -+ public void test7231Sec532Example1() - { - QuotedQualityCSV values = new QuotedQualityCSV(); - values.addValue(" audio/*; q=0.2, audio/basic"); -- assertThat(values,Matchers.contains("audio/basic","audio/*")); -+ assertThat(values, Matchers.contains("audio/basic", "audio/*")); - } - - @Test -- public void test7231_5_3_2_example2() -+ public void test7231Sec532Example2() - { - QuotedQualityCSV values = new QuotedQualityCSV(); - values.addValue("text/plain; q=0.5, text/html,"); - values.addValue("text/x-dvi; q=0.8, text/x-c"); -- assertThat(values,Matchers.contains("text/html","text/x-c","text/x-dvi","text/plain")); -+ assertThat(values, Matchers.contains("text/html", "text/x-c", "text/x-dvi", "text/plain")); - } -- -+ - @Test -- public void test7231_5_3_2_example3() -+ public void test7231Sec532Example3() - { - QuotedQualityCSV values = new QuotedQualityCSV(); - values.addValue("text/*, text/plain, text/plain;format=flowed, */*"); -- -+ - // Note this sort is only on quality and not the most specific type as per 5.3.2 -- assertThat(values,Matchers.contains("text/*","text/plain","text/plain;format=flowed","*/*")); -+ assertThat(values, Matchers.contains("text/*", "text/plain", "text/plain;format=flowed", "*/*")); - } -- -+ - @Test -- public void test7231_5_3_2_example3_most_specific() -+ public void test7231532Example3MostSpecific() - { - QuotedQualityCSV values = new QuotedQualityCSV(QuotedQualityCSV.MOST_SPECIFIC_MIME_ORDERING); - values.addValue("text/*, text/plain, text/plain;format=flowed, */*"); -- -- assertThat(values,Matchers.contains("text/plain;format=flowed","text/plain","text/*","*/*")); -+ -+ assertThat(values, Matchers.contains("text/plain;format=flowed", "text/plain", "text/*", "*/*")); - } -- -+ - @Test -- public void test7231_5_3_2_example4() -+ public void test7231Sec532Example4() - { - QuotedQualityCSV values = new QuotedQualityCSV(); - values.addValue("text/*;q=0.3, text/html;q=0.7, text/html;level=1,"); - values.addValue("text/html;level=2;q=0.4, */*;q=0.5"); -- assertThat(values,Matchers.contains( -- "text/html;level=1", -- "text/html", -- "*/*", -- "text/html;level=2", -- "text/*" -- )); -+ assertThat(values, Matchers.contains( -+ "text/html;level=1", -+ "text/html", -+ "*/*", -+ "text/html;level=2", -+ "text/*" -+ )); - } -- -+ - @Test -- public void test7231_5_3_4_example1() -+ public void test7231Sec534Example1() - { - QuotedQualityCSV values = new QuotedQualityCSV(); - values.addValue("compress, gzip"); -@@ -91,16 +90,16 @@ public class QuotedQualityCSVTest - values.addValue("*"); - values.addValue("compress;q=0.5, gzip;q=1.0"); - values.addValue("gzip;q=1.0, identity; q=0.5, *;q=0"); -- -- assertThat(values,Matchers.contains( -- "compress", -- "gzip", -- "*", -- "gzip", -- "gzip", -- "compress", -- "identity" -- )); -+ -+ assertThat(values, Matchers.contains( -+ "compress", -+ "gzip", -+ "*", -+ "gzip", -+ "gzip", -+ "compress", -+ "identity" -+ )); - } - - @Test -@@ -108,66 +107,65 @@ public class QuotedQualityCSVTest - { - QuotedQualityCSV values = new QuotedQualityCSV(); - values.addValue(" value 0.5 ; p = v ; q =0.5 , value 1.0 "); -- assertThat(values,Matchers.contains( -- "value 1.0", -- "value 0.5;p=v")); -+ assertThat(values, Matchers.contains( -+ "value 1.0", -+ "value 0.5;p=v")); - } -- -+ - @Test - public void testEmpty() - { - QuotedQualityCSV values = new QuotedQualityCSV(); - values.addValue(",aaaa, , bbbb ,,cccc,"); -- assertThat(values,Matchers.contains( -- "aaaa", -- "bbbb", -- "cccc")); -+ assertThat(values, Matchers.contains( -+ "aaaa", -+ "bbbb", -+ "cccc")); - } -- -+ - @Test - public void testQuoted() - { - QuotedQualityCSV values = new QuotedQualityCSV(); - values.addValue(" value 0.5 ; p = \"v ; q = \\\"0.5\\\" , value 1.0 \" "); -- assertThat(values,Matchers.contains( -- "value 0.5;p=\"v ; q = \\\"0.5\\\" , value 1.0 \"")); -+ assertThat(values, Matchers.contains( -+ "value 0.5;p=\"v ; q = \\\"0.5\\\" , value 1.0 \"")); - } -- -+ - @Test - public void testOpenQuote() - { - QuotedQualityCSV values = new QuotedQualityCSV(); - values.addValue("value;p=\"v"); -- assertThat(values,Matchers.contains( -- "value;p=\"v")); -+ assertThat(values, Matchers.contains( -+ "value;p=\"v")); - } -- -+ - @Test - public void testQuotedQuality() - { - QuotedQualityCSV values = new QuotedQualityCSV(); - values.addValue(" value 0.5 ; p = v ; q = \"0.5\" , value 1.0 "); -- assertThat(values,Matchers.contains( -- "value 1.0", -- "value 0.5;p=v")); -+ assertThat(values, Matchers.contains( -+ "value 1.0", -+ "value 0.5;p=v")); - } -- -+ - @Test - public void testBadQuality() - { - QuotedQualityCSV values = new QuotedQualityCSV(); - values.addValue("value0.5;p=v;q=0.5,value1.0,valueBad;q=X"); -- assertThat(values,Matchers.contains( -- "value1.0", -- "value0.5;p=v")); -+ assertThat(values, Matchers.contains( -+ "value1.0", -+ "value0.5;p=v")); - } -- -+ - @Test - public void testBad() - { - QuotedQualityCSV values = new QuotedQualityCSV(); - -- - // None of these should throw exceptions - values.addValue(null); - values.addValue(""); -@@ -223,13 +221,10 @@ public class QuotedQualityCSVTest - values.addValue("q="); - values.addValue("q=,"); - values.addValue("q=;"); -- - } - -- /* ------------------------------------------------------------ */ -- -- private static final String[] preferBrotli = {"br","gzip"}; -- private static final String[] preferGzip = {"gzip","br"}; -+ private static final String[] preferBrotli = {"br", "gzip"}; -+ private static final String[] preferGzip = {"gzip", "br"}; - private static final String[] noFormats = {}; - - @Test -@@ -295,14 +290,13 @@ public class QuotedQualityCSVTest - values.addValue("gzip, *"); - assertThat(values, contains("*", "gzip")); - } -- - - @Test - public void testSameQuality() - { - QuotedQualityCSV values = new QuotedQualityCSV(); - values.addValue("one;q=0.5,two;q=0.5,three;q=0.5"); -- assertThat(values.getValues(),Matchers.contains("one","two","three")); -+ assertThat(values.getValues(), Matchers.contains("one", "two", "three")); - } - - @Test -@@ -310,10 +304,9 @@ public class QuotedQualityCSVTest - { - QuotedQualityCSV values = new QuotedQualityCSV(); - values.addValue("one,two;,three;x=y"); -- assertThat(values.getValues(),Matchers.contains("one","two","three;x=y")); -+ assertThat(values.getValues(), Matchers.contains("one", "two", "three;x=y")); - } - -- - @Test - public void testQuality() - { -@@ -339,19 +332,15 @@ public class QuotedQualityCSVTest - } - }; - -- - // The provided string is not legal according to some RFCs ( not a token because of = and not a parameter because not preceded by ; ) - // The string is legal according to RFC7239 which allows for just parameters (called forwarded-pairs) - values.addValue("p=0.5,q=0.5"); - -- - // The QuotedCSV implementation is lenient and adopts the later interpretation and thus sees q=0.5 and p=0.5 both as parameters -- assertThat(results,contains("parsedValue: ", "parsedParam: p=0.5", -- "parsedValue: ", "parsedParam: q=0.5")); -- -+ assertThat(results, contains("parsedValue: ", "parsedParam: p=0.5", -+ "parsedValue: ", "parsedParam: q=0.5")); - - // However the QuotedQualityCSV only handles the q parameter and that is consumed from the parameter string. -- assertThat(values,contains("p=0.5", "")); -- -+ assertThat(values, contains("p=0.5", "")); - } - } diff --git a/CVE-2021-28165.patch b/CVE-2021-28165.patch deleted file mode 100755 index 5d1f6a9cdaa8aa31af0c621a062c0093b5e2d502..0000000000000000000000000000000000000000 --- a/CVE-2021-28165.patch +++ /dev/null @@ -1,533 +0,0 @@ -From: Markus Koschany -Date: Sat, 31 Jul 2021 17:24:07 +0200 -Subject: CVE-2021-28165 - ---- - .../org/eclipse/jetty/io/ssl/SslConnection.java | 12 + - .../eclipse/jetty/server/ssl/SSLEngineTest.java | 267 +++++++++++++-------- - 2 files changed, 183 insertions(+), 96 deletions(-) - -diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java -index a2c1fdc..c385f27 100644 ---- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java -+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java -@@ -330,6 +330,11 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr - _decryptedEndPoint.onFillableFail(cause == null ? new IOException() : cause); - } - -+ protected SSLEngineResult unwrap(SSLEngine sslEngine, ByteBuffer input, ByteBuffer output) throws SSLException -+ { -+ return sslEngine.unwrap(input, output); -+ } -+ - @Override - public String toConnectionString() - { -@@ -602,8 +607,15 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr - return filled = -1; - - case BUFFER_UNDERFLOW: -+ if (BufferUtil.space(_encryptedInput) == 0) -+ { -+ BufferUtil.clear(_encryptedInput); -+ throw new SSLHandshakeException("Encrypted buffer max length exceeded"); -+ } -+ - if (net_filled > 0) - continue; // try filling some more -+ - _underflown = true; - if (net_filled < 0 && _sslEngine.getUseClientMode()) - { -diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java -index ae6a5b6..aa1b9c9 100644 ---- a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java -+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLEngineTest.java -@@ -1,6 +1,6 @@ - // - // ======================================================================== --// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. -+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. - // ------------------------------------------------------------------------ - // All rights reserved. This program and the accompanying materials - // are made available under the terms of the Eclipse Public License v1.0 -@@ -34,20 +34,31 @@ import java.net.Socket; - import java.net.SocketException; - import java.net.SocketTimeoutException; - import java.net.URL; -- -+import java.nio.ByteBuffer; -+import java.util.Arrays; -+import java.util.concurrent.atomic.AtomicLong; -+import javax.net.SocketFactory; - import javax.net.ssl.HostnameVerifier; - import javax.net.ssl.HttpsURLConnection; - import javax.net.ssl.SSLContext; -+import javax.net.ssl.SSLEngine; -+import javax.net.ssl.SSLEngineResult; -+import javax.net.ssl.SSLException; - import javax.net.ssl.SSLSession; - import javax.servlet.ServletException; - import javax.servlet.ServletOutputStream; - import javax.servlet.http.HttpServletRequest; - import javax.servlet.http.HttpServletResponse; - -+import org.eclipse.jetty.io.EndPoint; -+import org.eclipse.jetty.io.ssl.SslConnection; -+import org.eclipse.jetty.server.ConnectionFactory; -+import org.eclipse.jetty.server.Connector; - import org.eclipse.jetty.server.HttpConnectionFactory; - import org.eclipse.jetty.server.Request; - import org.eclipse.jetty.server.Server; - import org.eclipse.jetty.server.ServerConnector; -+import org.eclipse.jetty.server.SslConnectionFactory; - import org.eclipse.jetty.server.handler.AbstractHandler; - import org.eclipse.jetty.toolchain.test.MavenTestingUtils; - import org.eclipse.jetty.util.IO; -@@ -60,6 +71,7 @@ import org.junit.jupiter.api.Test; - import static org.hamcrest.MatcherAssert.assertThat; - import static org.hamcrest.Matchers.greaterThan; - import static org.hamcrest.Matchers.is; -+import static org.hamcrest.Matchers.lessThan; - import static org.junit.jupiter.api.Assertions.assertEquals; - import static org.junit.jupiter.api.Assertions.assertNotNull; - -@@ -69,41 +81,45 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; - public class SSLEngineTest - { - // Useful constants -- private static final String HELLO_WORLD="Hello world. The quick brown fox jumped over the lazy dog. How now brown cow. The rain in spain falls mainly on the plain.\n"; -- private static final String JETTY_VERSION= Server.getVersion(); -- private static final String PROTOCOL_VERSION="2.0"; -- -- /** The request. */ -- private static final String REQUEST0_HEADER="POST /r0 HTTP/1.1\n"+"Host: localhost\n"+"Content-Type: text/xml\n"+"Content-Length: "; -- private static final String REQUEST1_HEADER="POST /r1 HTTP/1.1\n"+"Host: localhost\n"+"Content-Type: text/xml\n"+"Connection: close\n"+"Content-Length: "; -- private static final String REQUEST_CONTENT= -- "\n"+ -- "\n"+ -- ""; -- -- private static final String REQUEST0=REQUEST0_HEADER+REQUEST_CONTENT.getBytes().length+"\n\n"+REQUEST_CONTENT; -- private static final String REQUEST1=REQUEST1_HEADER+REQUEST_CONTENT.getBytes().length+"\n\n"+REQUEST_CONTENT; -- -- /** The expected response. */ -- private static final String RESPONSE0="HTTP/1.1 200 OK\n"+ -- "Content-Length: "+HELLO_WORLD.length()+"\n"+ -- "Server: Jetty("+JETTY_VERSION+")\n"+ -- '\n'+ -+ private static final String HELLO_WORLD = "Hello world. The quick brown fox jumped over the lazy dog. How now brown cow. The rain in spain falls mainly on the plain.\n"; -+ private static final String JETTY_VERSION = Server.getVersion(); -+ private static final String PROTOCOL_VERSION = "2.0"; -+ -+ /** -+ * The request. -+ */ -+ private static final String REQUEST0_HEADER = "POST /r0 HTTP/1.1\n" + "Host: localhost\n" + "Content-Type: text/xml\n" + "Content-Length: "; -+ private static final String REQUEST1_HEADER = "POST /r1 HTTP/1.1\n" + "Host: localhost\n" + "Content-Type: text/xml\n" + "Connection: close\n" + "Content-Length: "; -+ private static final String REQUEST_CONTENT = -+ "\n" + -+ "\n" + -+ ""; -+ -+ private static final String REQUEST0 = REQUEST0_HEADER + REQUEST_CONTENT.getBytes().length + "\n\n" + REQUEST_CONTENT; -+ private static final String REQUEST1 = REQUEST1_HEADER + REQUEST_CONTENT.getBytes().length + "\n\n" + REQUEST_CONTENT; -+ -+ /** -+ * The expected response. -+ */ -+ private static final String RESPONSE0 = "HTTP/1.1 200 OK\n" + -+ "Content-Length: " + HELLO_WORLD.length() + "\n" + -+ "Server: Jetty(" + JETTY_VERSION + ")\n" + -+ '\n' + - HELLO_WORLD; -- -- private static final String RESPONSE1="HTTP/1.1 200 OK\n"+ -- "Connection: close\n"+ -- "Content-Length: "+HELLO_WORLD.length()+"\n"+ -- "Server: Jetty("+JETTY_VERSION+")\n"+ -- '\n'+ -+ -+ private static final String RESPONSE1 = "HTTP/1.1 200 OK\n" + -+ "Connection: close\n" + -+ "Content-Length: " + HELLO_WORLD.length() + "\n" + -+ "Server: Jetty(" + JETTY_VERSION + ")\n" + -+ '\n' + - HELLO_WORLD; - -- private static final int BODY_SIZE=300; -+ private static final int BODY_SIZE = 300; - - private Server server; - private ServerConnector connector; -- -+ private SslContextFactory sslContextFactory; - - @BeforeEach - public void startServer() throws Exception -@@ -114,11 +130,11 @@ public class SSLEngineTest - sslContextFactory.setKeyStorePassword("storepwd"); - sslContextFactory.setKeyManagerPassword("keypwd"); - -- server=new Server(); -+ server = new Server(); - HttpConnectionFactory http = new HttpConnectionFactory(); - http.setInputBufferSize(512); - http.getHttpConfiguration().setRequestHeaderSize(512); -- connector=new ServerConnector(server, sslContextFactory, http); -+ connector = new ServerConnector(server, sslContextFactory, http); - connector.setPort(0); - connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration().setSendDateHeader(false); - -@@ -138,19 +154,19 @@ public class SSLEngineTest - server.setHandler(new HelloWorldHandler()); - server.start(); - -- SSLContext ctx=SSLContext.getInstance("TLS"); -- ctx.init(null,SslContextFactory.TRUST_ALL_CERTS,new java.security.SecureRandom()); -+ SSLContext ctx = SSLContext.getInstance("TLS"); -+ ctx.init(null, SslContextFactory.TRUST_ALL_CERTS, new java.security.SecureRandom()); - -- int port=connector.getLocalPort(); -+ int port = connector.getLocalPort(); - -- Socket client=ctx.getSocketFactory().createSocket("localhost",port); -- OutputStream os=client.getOutputStream(); -+ Socket client = ctx.getSocketFactory().createSocket("localhost", port); -+ OutputStream os = client.getOutputStream(); - - String request = -- "GET / HTTP/1.1\r\n"+ -- "Host: localhost:"+port+"\r\n"+ -- "Connection: close\r\n"+ -- "\r\n"; -+ "GET / HTTP/1.1\r\n" + -+ "Host: localhost:" + port + "\r\n" + -+ "Connection: close\r\n" + -+ "\r\n"; - - os.write(request.getBytes()); - os.flush(); -@@ -158,7 +174,7 @@ public class SSLEngineTest - String response = IO.toString(client.getInputStream()); - - assertThat(response, Matchers.containsString("200 OK")); -- assertThat(response,Matchers.containsString(HELLO_WORLD)); -+ assertThat(response, Matchers.containsString(HELLO_WORLD)); - } - - @Test -@@ -167,26 +183,81 @@ public class SSLEngineTest - server.setHandler(new HelloWorldHandler()); - server.start(); - -- SSLContext ctx=SSLContext.getInstance("TLS"); -- ctx.init(null,SslContextFactory.TRUST_ALL_CERTS,new java.security.SecureRandom()); -+ SSLContext ctx = SSLContext.getInstance("TLS"); -+ ctx.init(null, SslContextFactory.TRUST_ALL_CERTS, new java.security.SecureRandom()); - -- int port=connector.getLocalPort(); -+ int port = connector.getLocalPort(); - -- Socket client=ctx.getSocketFactory().createSocket("localhost",port); -- OutputStream os=client.getOutputStream(); -+ Socket client = ctx.getSocketFactory().createSocket("localhost", port); -+ OutputStream os = client.getOutputStream(); - - String request = -- "GET /?dump=102400 HTTP/1.1\r\n"+ -- "Host: localhost:"+port+"\r\n"+ -- "Connection: close\r\n"+ -- "\r\n"; -+ "GET /?dump=102400 HTTP/1.1\r\n" + -+ "Host: localhost:" + port + "\r\n" + -+ "Connection: close\r\n" + -+ "\r\n"; - - os.write(request.getBytes()); - os.flush(); - - String response = IO.toString(client.getInputStream()); - -- assertThat(response.length(),greaterThan(102400)); -+ assertThat(response.length(), greaterThan(102400)); -+ } -+ -+ @Test -+ public void testInvalidLargeTLSFrame() throws Exception -+ { -+ AtomicLong unwraps = new AtomicLong(); -+ ConnectionFactory http = connector.getConnectionFactory(HttpConnectionFactory.class); -+ ConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, http.getProtocol()) -+ { -+ @Override -+ protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) -+ { -+ return new SslConnection(connector.getByteBufferPool(), connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()) -+ { -+ @Override -+ protected SSLEngineResult unwrap(SSLEngine sslEngine, ByteBuffer input, ByteBuffer output) throws SSLException -+ { -+ unwraps.incrementAndGet(); -+ return super.unwrap(sslEngine, input, output); -+ } -+ }; -+ } -+ }; -+ ServerConnector tlsConnector = new ServerConnector(server, 1, 1, ssl, http); -+ server.addConnector(tlsConnector); -+ server.setHandler(new HelloWorldHandler()); -+ server.start(); -+ -+ // Create raw TLS record. -+ byte[] bytes = new byte[20005]; -+ Arrays.fill(bytes, (byte)1); -+ -+ bytes[0] = 22; // record type -+ bytes[1] = 3; // major version -+ bytes[2] = 3; // minor version -+ bytes[3] = 78; // record length 2 bytes / 0x4E20 / decimal 20,000 -+ bytes[4] = 32; // record length -+ bytes[5] = 1; // message type -+ bytes[6] = 0; // message length 3 bytes / 0x004E17 / decimal 19,991 -+ bytes[7] = 78; -+ bytes[8] = 23; -+ -+ SocketFactory socketFactory = SocketFactory.getDefault(); -+ try (Socket client = socketFactory.createSocket("localhost", tlsConnector.getLocalPort())) -+ { -+ client.getOutputStream().write(bytes); -+ -+ // Sleep to see if the server spins. -+ Thread.sleep(1000); -+ assertThat(unwraps.get(), lessThan(128L)); -+ -+ // Read until -1 or read timeout. -+ client.setSoTimeout(1000); -+ IO.readBytes(client.getInputStream()); -+ } - } - - @Test -@@ -195,63 +266,64 @@ public class SSLEngineTest - server.setHandler(new HelloWorldHandler()); - server.start(); - -- final int loops=10; -- final int numConns=20; -+ final int loops = 10; -+ final int numConns = 20; - -- Socket[] client=new Socket[numConns]; -+ Socket[] client = new Socket[numConns]; - -- SSLContext ctx=SSLContext.getInstance("TLSv1.2"); -- ctx.init(null,SslContextFactory.TRUST_ALL_CERTS,new java.security.SecureRandom()); -+ SSLContext ctx = SSLContext.getInstance("TLSv1.2"); -+ ctx.init(null, SslContextFactory.TRUST_ALL_CERTS, new java.security.SecureRandom()); - -- int port=connector.getLocalPort(); -+ int port = connector.getLocalPort(); - - try - { -- for (int l=0;l -1) -- bytes+=len; -+ { -+ bytes += len; -+ } - is.close(); - -- assertEquals(BODY_SIZE,handler.bytes); -- assertEquals(BODY_SIZE,bytes); -+ assertEquals(BODY_SIZE, handler.bytes); -+ assertEquals(BODY_SIZE, bytes); - } - - /** -@@ -327,30 +401,30 @@ public class SSLEngineTest - */ - private static String readResponse(Socket client) throws IOException - { -- BufferedReader br=null; -- StringBuilder sb=new StringBuilder(1000); -+ BufferedReader br = null; -+ StringBuilder sb = new StringBuilder(1000); - - try - { - client.setSoTimeout(5000); -- br=new BufferedReader(new InputStreamReader(client.getInputStream())); -+ br = new BufferedReader(new InputStreamReader(client.getInputStream())); - - String line; - -- while ((line=br.readLine())!=null) -+ while ((line = br.readLine()) != null) - { - sb.append(line); - sb.append('\n'); - } - } -- catch(SocketTimeoutException e) -+ catch (SocketTimeoutException e) - { -- System.err.println("Test timedout: "+e.toString()); -+ System.err.println("Test timedout: " + e.toString()); - e.printStackTrace(); // added to see if we can get more info from failures on CI - } - finally - { -- if (br!=null) -+ if (br != null) - { - br.close(); - } -@@ -364,22 +438,24 @@ public class SSLEngineTest - public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException - { - // System.err.println("HANDLE "+request.getRequestURI()); -- String ssl_id = (String)request.getAttribute("javax.servlet.request.ssl_session_id"); -- assertNotNull(ssl_id); -- -- if (request.getParameter("dump")!=null) -+ String sslId = (String)request.getAttribute("javax.servlet.request.ssl_session_id"); -+ assertNotNull(sslId); -+ -+ if (request.getParameter("dump") != null) - { -- ServletOutputStream out=response.getOutputStream(); -+ ServletOutputStream out = response.getOutputStream(); - byte[] buf = new byte[Integer.parseInt(request.getParameter("dump"))]; - // System.err.println("DUMP "+buf.length); -- for (int i=0;i -1) - { -- bytes+=len; -+ bytes += len; - } - - OutputStream os = response.getOutputStream(); -@@ -412,5 +488,4 @@ public class SSLEngineTest - response.flushBuffer(); - } - } -- - } diff --git a/CVE-2021-28169.patch b/CVE-2021-28169.patch deleted file mode 100755 index 8f39175d4b3f7c525c68a13a71f5cf3d1a2370e7..0000000000000000000000000000000000000000 --- a/CVE-2021-28169.patch +++ /dev/null @@ -1,493 +0,0 @@ -From: Markus Koschany -Date: Sat, 3 Jul 2021 20:47:31 +0200 -Subject: CVE-2021-28169 - -Origin: https://github.com/eclipse/jetty.project/commit/1c05b0bcb181c759e98b060bded0b9376976b055 ---- - .../org/eclipse/jetty/server/ResourceService.java | 4 +- - .../org/eclipse/jetty/servlets/ConcatServlet.java | 4 +- - .../org/eclipse/jetty/servlets/WelcomeFilter.java | 8 +- - .../eclipse/jetty/servlets/ConcatServletTest.java | 34 +++-- - .../eclipse/jetty/servlets/WelcomeFilterTest.java | 143 +++++++++++++++++++++ - .../jetty/webapp/WebAppDefaultServletTest.java | 142 ++++++++++++++++++++ - 6 files changed, 313 insertions(+), 22 deletions(-) - create mode 100644 jetty-servlets/src/test/java/org/eclipse/jetty/servlets/WelcomeFilterTest.java - create mode 100644 jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppDefaultServletTest.java - -diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java -index 048bd71..737f461 100644 ---- a/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java -+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceService.java -@@ -234,7 +234,7 @@ public class ResourceService - // Find the content - content=_contentFactory.getContent(pathInContext,response.getBufferSize()); - if (LOG.isDebugEnabled()) -- LOG.info("content={}",content); -+ LOG.debug("content={}", content); - - // Not found? - if (content==null || !content.getResource().exists()) -@@ -420,7 +420,7 @@ public class ResourceService - return; - } - -- RequestDispatcher dispatcher=context.getRequestDispatcher(welcome); -+ RequestDispatcher dispatcher = context.getRequestDispatcher(URIUtil.encodePath(welcome)); - if (dispatcher!=null) - { - // Forward to the index -diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ConcatServlet.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ConcatServlet.java -index a4b7df0..f1d8e57 100644 ---- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ConcatServlet.java -+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/ConcatServlet.java -@@ -62,6 +62,7 @@ import org.eclipse.jetty.util.URIUtil; - * appropriate. This means that when not in development mode, the servlet must be - * restarted before changed content will be served.

- */ -+@Deprecated - public class ConcatServlet extends HttpServlet - { - private boolean _development; -@@ -126,7 +127,8 @@ public class ConcatServlet extends HttpServlet - } - } - -- RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(path); -+ // Use the original string and not the decoded path as the Dispatcher will decode again. -+ RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(part); - if (dispatcher != null) - dispatchers.add(dispatcher); - } -diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/WelcomeFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/WelcomeFilter.java -index e67a067..22ea603 100644 ---- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/WelcomeFilter.java -+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/WelcomeFilter.java -@@ -28,6 +28,8 @@ import javax.servlet.ServletRequest; - import javax.servlet.ServletResponse; - import javax.servlet.http.HttpServletRequest; - -+import org.eclipse.jetty.util.URIUtil; -+ - /* ------------------------------------------------------------ */ - /** Welcome Filter - * This filter can be used to server an index file for a directory -@@ -42,6 +44,7 @@ import javax.servlet.http.HttpServletRequest; - * - * Requests to "/some/directory" will be redirected to "/some/directory/". - */ -+@Deprecated - public class WelcomeFilter implements Filter - { - private String welcome; -@@ -63,7 +66,10 @@ public class WelcomeFilter implements Filter - { - String path=((HttpServletRequest)request).getServletPath(); - if (welcome!=null && path.endsWith("/")) -- request.getRequestDispatcher(path+welcome).forward(request,response); -+ { -+ String uriInContext = URIUtil.encodePath(URIUtil.addPaths(path, welcome)); -+ request.getRequestDispatcher(uriInContext).forward(request, response); -+ } - else - chain.doFilter(request, response); - } -diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ConcatServletTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ConcatServletTest.java -index 3fcb9af..f8ea087 100644 ---- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ConcatServletTest.java -+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ConcatServletTest.java -@@ -1,6 +1,6 @@ - // - // ======================================================================== --// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. -+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. - // ------------------------------------------------------------------------ - // All rights reserved. This program and the accompanying materials - // are made available under the terms of the Eclipse Public License v1.0 -@@ -18,11 +18,6 @@ - - package org.eclipse.jetty.servlets; - --import static org.junit.jupiter.api.Assertions.assertEquals; --import static org.junit.jupiter.api.Assertions.assertNotNull; --import static org.junit.jupiter.api.Assertions.assertNull; --import static org.junit.jupiter.api.Assertions.assertTrue; -- - import java.io.BufferedReader; - import java.io.File; - import java.io.IOException; -@@ -31,7 +26,6 @@ import java.io.StringReader; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; - import java.nio.file.Path; -- - import javax.servlet.RequestDispatcher; - import javax.servlet.ServletException; - import javax.servlet.http.HttpServlet; -@@ -45,10 +39,14 @@ import org.eclipse.jetty.servlet.ServletHolder; - import org.eclipse.jetty.toolchain.test.MavenTestingUtils; - import org.eclipse.jetty.webapp.WebAppContext; - import org.junit.jupiter.api.AfterEach; -- - import org.junit.jupiter.api.BeforeEach; - import org.junit.jupiter.api.Test; - -+import static org.junit.jupiter.api.Assertions.assertEquals; -+import static org.junit.jupiter.api.Assertions.assertNotNull; -+import static org.junit.jupiter.api.Assertions.assertNull; -+import static org.junit.jupiter.api.Assertions.assertTrue; -+ - public class ConcatServletTest - { - private Server server; -@@ -92,8 +90,8 @@ public class ConcatServletTest - String resource1 = "/resource/one.js"; - String resource2 = "/resource/two.js"; - String uri = contextPath + concatPath + "?" + resource1 + "&" + resource2; -- String request = "" + -- "GET " + uri + " HTTP/1.1\r\n" + -+ String request = -+ "GET " + uri + " HTTP/1.1\r\n" + - "Host: localhost\r\n" + - "Connection: close\r\n" + - "\r\n"; -@@ -139,8 +137,8 @@ public class ConcatServletTest - // Having a path segment and then ".." triggers a special case - // that the ConcatServlet must detect and avoid. - String uri = contextPath + concatPath + "?/trick/../WEB-INF/one.js"; -- String request = "" + -- "GET " + uri + " HTTP/1.1\r\n" + -+ String request = -+ "GET " + uri + " HTTP/1.1\r\n" + - "Host: localhost\r\n" + - "Connection: close\r\n" + - "\r\n"; -@@ -149,8 +147,8 @@ public class ConcatServletTest - - // Make sure ConcatServlet behaves well if it's case insensitive. - uri = contextPath + concatPath + "?/trick/../web-inf/one.js"; -- request = "" + -- "GET " + uri + " HTTP/1.1\r\n" + -+ request = -+ "GET " + uri + " HTTP/1.1\r\n" + - "Host: localhost\r\n" + - "Connection: close\r\n" + - "\r\n"; -@@ -159,8 +157,8 @@ public class ConcatServletTest - - // Make sure ConcatServlet behaves well if encoded. - uri = contextPath + concatPath + "?/trick/..%2FWEB-INF%2Fone.js"; -- request = "" + -- "GET " + uri + " HTTP/1.1\r\n" + -+ request = -+ "GET " + uri + " HTTP/1.1\r\n" + - "Host: localhost\r\n" + - "Connection: close\r\n" + - "\r\n"; -@@ -169,8 +167,8 @@ public class ConcatServletTest - - // Make sure ConcatServlet cannot see file system files. - uri = contextPath + concatPath + "?/trick/../../" + directoryFile.getName(); -- request = "" + -- "GET " + uri + " HTTP/1.1\r\n" + -+ request = -+ "GET " + uri + " HTTP/1.1\r\n" + - "Host: localhost\r\n" + - "Connection: close\r\n" + - "\r\n"; -diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/WelcomeFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/WelcomeFilterTest.java -new file mode 100644 -index 0000000..65e6503 ---- /dev/null -+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/WelcomeFilterTest.java -@@ -0,0 +1,143 @@ -+// -+// ======================================================================== -+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. -+// ------------------------------------------------------------------------ -+// All rights reserved. This program and the accompanying materials -+// are made available under the terms of the Eclipse Public License v1.0 -+// and Apache License v2.0 which accompanies this distribution. -+// -+// The Eclipse Public License is available at -+// http://www.eclipse.org/legal/epl-v10.html -+// -+// The Apache License v2.0 is available at -+// http://www.opensource.org/licenses/apache2.0.php -+// -+// You may elect to redistribute this code under either of these licenses. -+// ======================================================================== -+// -+ -+package org.eclipse.jetty.servlets; -+ -+import java.io.OutputStream; -+import java.nio.charset.StandardCharsets; -+import java.nio.file.Files; -+import java.nio.file.Path; -+import java.util.EnumSet; -+import java.util.stream.Stream; -+import javax.servlet.DispatcherType; -+ -+import org.eclipse.jetty.server.LocalConnector; -+import org.eclipse.jetty.server.Server; -+import org.eclipse.jetty.servlet.FilterHolder; -+import org.eclipse.jetty.toolchain.test.MavenTestingUtils; -+import org.eclipse.jetty.webapp.WebAppContext; -+import org.junit.jupiter.api.AfterEach; -+import org.junit.jupiter.api.BeforeEach; -+import org.junit.jupiter.params.ParameterizedTest; -+import org.junit.jupiter.params.provider.Arguments; -+import org.junit.jupiter.params.provider.MethodSource; -+ -+import static org.hamcrest.MatcherAssert.assertThat; -+import static org.hamcrest.Matchers.containsString; -+import static org.junit.jupiter.api.Assertions.assertNotNull; -+ -+public class WelcomeFilterTest -+{ -+ private Server server; -+ private LocalConnector connector; -+ -+ @BeforeEach -+ public void prepareServer() throws Exception -+ { -+ server = new Server(); -+ connector = new LocalConnector(server); -+ server.addConnector(connector); -+ -+ Path directoryPath = MavenTestingUtils.getTargetTestingDir().toPath(); -+ Files.createDirectories(directoryPath); -+ Path welcomeResource = directoryPath.resolve("welcome.html"); -+ try (OutputStream output = Files.newOutputStream(welcomeResource)) -+ { -+ output.write("

welcome page

".getBytes(StandardCharsets.UTF_8)); -+ } -+ -+ Path otherResource = directoryPath.resolve("other.html"); -+ try (OutputStream output = Files.newOutputStream(otherResource)) -+ { -+ output.write("

other resource

".getBytes(StandardCharsets.UTF_8)); -+ } -+ -+ Path hiddenDirectory = directoryPath.resolve("WEB-INF"); -+ Files.createDirectories(hiddenDirectory); -+ Path hiddenResource = hiddenDirectory.resolve("one.js"); -+ try (OutputStream output = Files.newOutputStream(hiddenResource)) -+ { -+ output.write("CONFIDENTIAL".getBytes(StandardCharsets.UTF_8)); -+ } -+ -+ Path hiddenWelcome = hiddenDirectory.resolve("index.html"); -+ try (OutputStream output = Files.newOutputStream(hiddenWelcome)) -+ { -+ output.write("CONFIDENTIAL".getBytes(StandardCharsets.UTF_8)); -+ } -+ -+ WebAppContext context = new WebAppContext(server, directoryPath.toString(), "/"); -+ server.setHandler(context); -+ String concatPath = "/*"; -+ -+ FilterHolder filterHolder = new FilterHolder(new WelcomeFilter()); -+ filterHolder.setInitParameter("welcome", "welcome.html"); -+ context.addFilter(filterHolder, concatPath, EnumSet.of(DispatcherType.REQUEST)); -+ server.start(); -+ -+ // Verify that I can get the file programmatically, as required by the spec. -+ assertNotNull(context.getServletContext().getResource("/WEB-INF/one.js")); -+ } -+ -+ @AfterEach -+ public void destroy() throws Exception -+ { -+ if (server != null) -+ server.stop(); -+ } -+ -+ public static Stream argumentsStream() -+ { -+ return Stream.of( -+ // Normal requests for the directory are redirected to the welcome page. -+ Arguments.of("/", new String[]{"HTTP/1.1 200 ", "

welcome page

"}), -+ -+ // Try a normal resource (will bypass the filter). -+ Arguments.of("/other.html", new String[]{"HTTP/1.1 200 ", "

other resource

"}), -+ -+ // Cannot access files in WEB-INF. -+ Arguments.of("/WEB-INF/one.js", new String[]{"HTTP/1.1 404 "}), -+ -+ // Cannot serve welcome from WEB-INF. -+ Arguments.of("/WEB-INF/", new String[]{"HTTP/1.1 404 "}), -+ -+ // Try to trick the filter into serving a protected resource. -+ Arguments.of("/WEB-INF/one.js#/", new String[]{"HTTP/1.1 404 "}), -+ Arguments.of("/js/../WEB-INF/one.js#/", new String[]{"HTTP/1.1 404 "}), -+ -+ // Test the URI is not double decoded in the dispatcher. -+ Arguments.of("/%2557EB-INF/one.js%23/", new String[]{"HTTP/1.1 404 "}) -+ ); -+ } -+ -+ @ParameterizedTest -+ @MethodSource("argumentsStream") -+ public void testWelcomeFilter(String uri, String[] contains) throws Exception -+ { -+ String request = -+ "GET " + uri + " HTTP/1.1\r\n" + -+ "Host: localhost\r\n" + -+ "Connection: close\r\n" + -+ "\r\n"; -+ String response = connector.getResponse(request); -+ for (String s : contains) -+ { -+ assertThat(response, containsString(s)); -+ } -+ } -+} -diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppDefaultServletTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppDefaultServletTest.java -new file mode 100644 -index 0000000..933bb7a ---- /dev/null -+++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebAppDefaultServletTest.java -@@ -0,0 +1,142 @@ -+// -+// ======================================================================== -+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. -+// ------------------------------------------------------------------------ -+// All rights reserved. This program and the accompanying materials -+// are made available under the terms of the Eclipse Public License v1.0 -+// and Apache License v2.0 which accompanies this distribution. -+// -+// The Eclipse Public License is available at -+// http://www.eclipse.org/legal/epl-v10.html -+// -+// The Apache License v2.0 is available at -+// http://www.opensource.org/licenses/apache2.0.php -+// -+// You may elect to redistribute this code under either of these licenses. -+// ======================================================================== -+// -+ -+package org.eclipse.jetty.webapp; -+ -+import java.io.OutputStream; -+import java.nio.charset.StandardCharsets; -+import java.nio.file.Files; -+import java.nio.file.Path; -+import java.util.stream.Stream; -+ -+import org.eclipse.jetty.server.LocalConnector; -+import org.eclipse.jetty.server.Server; -+import org.eclipse.jetty.toolchain.test.MavenTestingUtils; -+import org.eclipse.jetty.util.IO; -+import org.junit.jupiter.api.AfterEach; -+import org.junit.jupiter.api.BeforeEach; -+import org.junit.jupiter.params.ParameterizedTest; -+import org.junit.jupiter.params.provider.Arguments; -+import org.junit.jupiter.params.provider.MethodSource; -+ -+import static org.hamcrest.MatcherAssert.assertThat; -+import static org.hamcrest.Matchers.containsString; -+import static org.junit.jupiter.api.Assertions.assertNotNull; -+ -+public class WebAppDefaultServletTest -+{ -+ private Server server; -+ private LocalConnector connector; -+ -+ @BeforeEach -+ public void prepareServer() throws Exception -+ { -+ server = new Server(); -+ connector = new LocalConnector(server); -+ server.addConnector(connector); -+ -+ Path directoryPath = MavenTestingUtils.getTargetTestingDir().toPath(); -+ IO.delete(directoryPath.toFile()); -+ Files.createDirectories(directoryPath); -+ Path welcomeResource = directoryPath.resolve("index.html"); -+ try (OutputStream output = Files.newOutputStream(welcomeResource)) -+ { -+ output.write("

welcome page

".getBytes(StandardCharsets.UTF_8)); -+ } -+ -+ Path otherResource = directoryPath.resolve("other.html"); -+ try (OutputStream output = Files.newOutputStream(otherResource)) -+ { -+ output.write("

other resource

".getBytes(StandardCharsets.UTF_8)); -+ } -+ -+ Path hiddenDirectory = directoryPath.resolve("WEB-INF"); -+ Files.createDirectories(hiddenDirectory); -+ Path hiddenResource = hiddenDirectory.resolve("one.js"); -+ try (OutputStream output = Files.newOutputStream(hiddenResource)) -+ { -+ output.write("this is confidential".getBytes(StandardCharsets.UTF_8)); -+ } -+ -+ // Create directory to trick resource service. -+ Path hackPath = directoryPath.resolve("%57EB-INF/one.js#/"); -+ Files.createDirectories(hackPath); -+ try (OutputStream output = Files.newOutputStream(hackPath.resolve("index.html"))) -+ { -+ output.write("this content does not matter".getBytes(StandardCharsets.UTF_8)); -+ } -+ -+ Path standardHashDir = directoryPath.resolve("welcome#"); -+ Files.createDirectories(standardHashDir); -+ try (OutputStream output = Files.newOutputStream(standardHashDir.resolve("index.html"))) -+ { -+ output.write("standard hash dir welcome".getBytes(StandardCharsets.UTF_8)); -+ } -+ -+ WebAppContext context = new WebAppContext(server, directoryPath.toString(), "/"); -+ server.setHandler(context); -+ server.start(); -+ -+ // Verify that I can get the file programmatically, as required by the spec. -+ assertNotNull(context.getServletContext().getResource("/WEB-INF/one.js")); -+ } -+ -+ @AfterEach -+ public void destroy() throws Exception -+ { -+ if (server != null) -+ server.stop(); -+ } -+ -+ public static Stream argumentsStream() -+ { -+ return Stream.of( -+ Arguments.of("/WEB-INF/", new String[]{"HTTP/1.1 404 "}), -+ Arguments.of("/welcome%23/", new String[]{"HTTP/1.1 200 ", "standard hash dir welcome"}), -+ -+ // Normal requests for the directory are redirected to the welcome page. -+ Arguments.of("/", new String[]{"HTTP/1.1 200 ", "

welcome page

"}), -+ -+ // We can be served other resources. -+ Arguments.of("/other.html", new String[]{"HTTP/1.1 200 ", "

other resource

"}), -+ -+ // The ContextHandler will filter these ones out as as WEB-INF is a protected target. -+ Arguments.of("/WEB-INF/one.js#/", new String[]{"HTTP/1.1 404 "}), -+ Arguments.of("/js/../WEB-INF/one.js#/", new String[]{"HTTP/1.1 404 "}), -+ -+ // Test the URI is not double decoded by the dispatcher that serves the welcome file (we get index.html not one.js). -+ Arguments.of("/%2557EB-INF/one.js%23/", new String[]{"HTTP/1.1 200 ", "this content does not matter"}) -+ ); -+ } -+ -+ @ParameterizedTest -+ @MethodSource("argumentsStream") -+ public void testResourceService(String uri, String[] contains) throws Exception -+ { -+ String request = -+ "GET " + uri + " HTTP/1.1\r\n" + -+ "Host: localhost\r\n" + -+ "Connection: close\r\n" + -+ "\r\n"; -+ String response = connector.getResponse(request); -+ for (String s : contains) -+ { -+ assertThat(response, containsString(s)); -+ } -+ } -+} diff --git a/CVE-2021-34428.patch b/CVE-2021-34428.patch deleted file mode 100755 index 5090be080a75b3620112126284585a7d28e1325d..0000000000000000000000000000000000000000 --- a/CVE-2021-34428.patch +++ /dev/null @@ -1,657 +0,0 @@ -From: Markus Koschany -Date: Sat, 3 Jul 2021 20:28:06 +0200 -Subject: CVE-2021-34428 - -Origin: https://github.com/eclipse/jetty.project/commit/cd6462a6252d083b3c9ea2684aab0b4c9669ed19 ---- - .../org/eclipse/jetty/server/session/Session.java | 9 +- - .../server/session/TestHttpSessionListener.java | 24 +- - .../jetty/server/session/SessionListenerTest.java | 367 +++++++++++++++------ - 3 files changed, 291 insertions(+), 109 deletions(-) - -diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java -index a34bc0f..d667560 100644 ---- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java -+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java -@@ -506,6 +506,7 @@ public class Session implements SessionHandler.SessionIf - { - try (Lock lock = _lock.lock()) - { -+ checkValidForRead(); - return _sessionData.getLastAccessed(); - } - } -@@ -972,14 +973,18 @@ public class Session implements SessionHandler.SessionIf - // do the invalidation - _handler.callSessionDestroyedListeners(this); - } -+ catch (Exception e) -+ { -+ LOG.warn("Error during Session destroy listener", e); -+ } - finally - { - // call the attribute removed listeners and finally mark it - // as invalid - finishInvalidate(); -+ // tell id mgr to remove sessions with same id from all contexts -+ _handler.getSessionIdManager().invalidateAll(_sessionData.getId()); - } -- // tell id mgr to remove sessions with same id from all contexts -- _handler.getSessionIdManager().invalidateAll(_sessionData.getId()); - } - } - catch (Exception e) -diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/TestHttpSessionListener.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/TestHttpSessionListener.java -index 770627b..dd8982f 100644 ---- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/TestHttpSessionListener.java -+++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/TestHttpSessionListener.java -@@ -35,16 +35,18 @@ public class TestHttpSessionListener implements HttpSessionListener - public List createdSessions = new ArrayList<>(); - public List destroyedSessions = new ArrayList<>(); - public boolean accessAttribute = false; -- public Exception ex = null; -+ public boolean lastAccessTime = false; -+ public Exception attributeException = null; -+ public Exception accessTimeException = null; - -- public TestHttpSessionListener(boolean access) -+ public TestHttpSessionListener(boolean accessAttribute, boolean lastAccessTime) - { -- accessAttribute = access; -+ this.accessAttribute = accessAttribute; -+ this.lastAccessTime = lastAccessTime; - } - - public TestHttpSessionListener() - { -- accessAttribute = false; - } - - public void sessionDestroyed(HttpSessionEvent se) -@@ -58,7 +60,19 @@ public class TestHttpSessionListener implements HttpSessionListener - } - catch (Exception e) - { -- ex = e; -+ attributeException = e; -+ } -+ } -+ -+ if (lastAccessTime) -+ { -+ try -+ { -+ se.getSession().getLastAccessedTime(); -+ } -+ catch (Exception e) -+ { -+ accessTimeException = e; - } - } - } -diff --git a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionListenerTest.java b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionListenerTest.java -index ba83986..363d1e3 100644 ---- a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionListenerTest.java -+++ b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionListenerTest.java -@@ -1,6 +1,6 @@ - // - // ======================================================================== --// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. -+// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. - // ------------------------------------------------------------------------ - // All rights reserved. This program and the accompanying materials - // are made available under the terms of the Eclipse Public License v1.0 -@@ -18,19 +18,17 @@ - - package org.eclipse.jetty.server.session; - --import static org.hamcrest.MatcherAssert.assertThat; --import static org.hamcrest.Matchers.isIn; --import static org.junit.jupiter.api.Assertions.assertEquals; --import static org.junit.jupiter.api.Assertions.assertNotEquals; --import static org.junit.jupiter.api.Assertions.assertNotNull; --import static org.junit.jupiter.api.Assertions.assertNull; --import static org.junit.jupiter.api.Assertions.assertTrue; -- - import java.io.IOException; -+import java.io.InputStream; -+import java.io.OutputStream; - import java.io.Serializable; - import java.net.HttpCookie; -+import java.net.URL; -+import java.net.URLClassLoader; -+import java.nio.file.Files; -+import java.nio.file.Path; -+import java.util.Collection; - import java.util.concurrent.TimeUnit; -- - import javax.servlet.ServletException; - import javax.servlet.http.HttpServlet; - import javax.servlet.http.HttpServletRequest; -@@ -38,53 +36,140 @@ import javax.servlet.http.HttpServletResponse; - import javax.servlet.http.HttpSession; - import javax.servlet.http.HttpSessionBindingEvent; - import javax.servlet.http.HttpSessionBindingListener; -+import javax.servlet.http.HttpSessionEvent; -+import javax.servlet.http.HttpSessionListener; - - import org.eclipse.jetty.client.HttpClient; - import org.eclipse.jetty.client.api.ContentResponse; - import org.eclipse.jetty.client.api.Request; -+import org.eclipse.jetty.server.Server; - import org.eclipse.jetty.servlet.ServletContextHandler; - import org.eclipse.jetty.servlet.ServletHolder; -+import org.eclipse.jetty.toolchain.test.IO; -+import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; -+import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; -+import org.eclipse.jetty.util.component.LifeCycle; -+import org.eclipse.jetty.webapp.WebAppContext; - import org.junit.jupiter.api.Test; -+import org.junit.jupiter.api.extension.ExtendWith; -+import org.junit.jupiter.api.Disabled; -+ -+import static org.hamcrest.MatcherAssert.assertThat; -+import static org.hamcrest.Matchers.greaterThan; -+//import static org.hamcrest.Matchers.in; -+import static org.hamcrest.Matchers.is; -+import static org.junit.jupiter.api.Assertions.assertEquals; -+import static org.junit.jupiter.api.Assertions.assertFalse; -+import static org.junit.jupiter.api.Assertions.assertNotEquals; -+import static org.junit.jupiter.api.Assertions.assertNotNull; -+import static org.junit.jupiter.api.Assertions.assertNull; -+import static org.junit.jupiter.api.Assertions.assertTrue; - - /** - * SessionListenerTest - * - * Test that session listeners are called. - */ -+@ExtendWith(WorkDirExtension.class) - public class SessionListenerTest - { -+ public WorkDir workDir; -+ - /** - * Test that listeners are called when a session is deliberately invalidated. -- * -- * @throws Exception - */ -+ @Disabled - @Test - public void testListenerWithInvalidation() throws Exception - { - String contextPath = ""; - String servletMapping = "/server"; - int inactivePeriod = 6; -- int scavengePeriod = -1; -+ int scavengePeriod = -1; - - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); -- SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory(); -- ((AbstractSessionDataStoreFactory)storeFactory).setGracePeriodSec(scavengePeriod); -+ TestSessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory(); -+ storeFactory.setGracePeriodSec(scavengePeriod); - -- TestServer server = new TestServer(0, inactivePeriod, scavengePeriod, -- cacheFactory, storeFactory); -+ TestServer server = new TestServer(0, inactivePeriod, scavengePeriod, -+ cacheFactory, storeFactory); - ServletContextHandler context = server.addContext(contextPath); -- TestHttpSessionListener listener = new TestHttpSessionListener(true); -+ TestHttpSessionListener listener = new TestHttpSessionListener(true, true); - context.getSessionHandler().addEventListener(listener); - TestServlet servlet = new TestServlet(); - ServletHolder holder = new ServletHolder(servlet); - context.addServlet(holder, servletMapping); -+ -+ try -+ { -+ server.start(); -+ int port1 = server.getPort(); -+ -+ HttpClient client = new HttpClient(); -+ client.start(); -+ try -+ { -+ String url = "http://localhost:" + port1 + contextPath + servletMapping; -+ // Create the session -+ ContentResponse response1 = client.GET(url + "?action=init"); -+ assertEquals(HttpServletResponse.SC_OK, response1.getStatus()); -+ String sessionCookie = response1.getHeaders().get("Set-Cookie"); -+ assertNotNull(sessionCookie); -+ assertTrue(TestServlet.bindingListener.bound); -+ -+ String sessionId = TestServer.extractSessionId(sessionCookie); -+ //assertThat(sessionId, is(in(listener.createdSessions))); -+ -+ // Make a request which will invalidate the existing session -+ Request request2 = client.newRequest(url + "?action=test"); -+ ContentResponse response2 = request2.send(); -+ assertEquals(HttpServletResponse.SC_OK, response2.getStatus()); -+ -+ assertTrue(TestServlet.bindingListener.unbound); -+ assertTrue(listener.destroyedSessions.contains(sessionId)); -+ } -+ finally -+ { -+ LifeCycle.stop(client); -+ } -+ } -+ finally -+ { -+ LifeCycle.stop(server); -+ } -+ } - -+ /** -+ * Test that if a session listener throws an exception during sessionDestroyed the session is still invalidated -+ */ -+ @Test -+ public void testListenerWithInvalidationException() throws Exception -+ { -+ String contextPath = ""; -+ String servletMapping = "/server"; -+ int inactivePeriod = 6; -+ int scavengePeriod = -1; -+ -+ DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); -+ cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); -+ TestSessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory(); -+ storeFactory.setGracePeriodSec(scavengePeriod); -+ -+ TestServer server = new TestServer(0, inactivePeriod, scavengePeriod, -+ cacheFactory, storeFactory); -+ ServletContextHandler context = server.addContext(contextPath); -+ ThrowingSessionListener listener = new ThrowingSessionListener(); -+ context.getSessionHandler().addEventListener(listener); -+ TestServlet servlet = new TestServlet(); -+ ServletHolder holder = new ServletHolder(servlet); -+ context.addServlet(holder, servletMapping); -+ - try - { - server.start(); - int port1 = server.getPort(); -- -+ - HttpClient client = new HttpClient(); - client.start(); - try -@@ -92,42 +177,59 @@ public class SessionListenerTest - String url = "http://localhost:" + port1 + contextPath + servletMapping; - // Create the session - ContentResponse response1 = client.GET(url + "?action=init"); -- assertEquals(HttpServletResponse.SC_OK,response1.getStatus()); -+ assertEquals(HttpServletResponse.SC_OK, response1.getStatus()); - String sessionCookie = response1.getHeaders().get("Set-Cookie"); -- assertTrue(sessionCookie != null); -- assertTrue (TestServlet.bindingListener.bound); -- -+ assertNotNull(sessionCookie); -+ assertTrue(TestServlet.bindingListener.bound); -+ - String sessionId = TestServer.extractSessionId(sessionCookie); -- assertThat(sessionId, isIn(listener.createdSessions)); -- -+ - // Make a request which will invalidate the existing session - Request request2 = client.newRequest(url + "?action=test"); - ContentResponse response2 = request2.send(); -- assertEquals(HttpServletResponse.SC_OK,response2.getStatus()); -+ assertEquals(HttpServletResponse.SC_OK, response2.getStatus()); - -- assertTrue (TestServlet.bindingListener.unbound); -- assertTrue (listener.destroyedSessions.contains(sessionId)); -+ assertTrue(TestServlet.bindingListener.unbound); -+ -+ //check session no longer exists -+ assertFalse(context.getSessionHandler().getSessionCache().contains(sessionId)); -+ assertFalse(context.getSessionHandler().getSessionCache().getSessionDataStore().exists(sessionId)); - } - finally - { -- client.stop(); -+ LifeCycle.stop(client); - } - } - finally - { -- server.stop(); -+ LifeCycle.stop(server); - } - } - -- - /** -- * Test that listeners are called when a session expires. -- * -- * @throws Exception -+ * Test that listeners are called when a session expires -+ * and that the listener is able to access webapp classes. - */ -+ @Disabled - @Test - public void testSessionExpiresWithListener() throws Exception - { -+ Path foodir = workDir.getEmptyPathDir(); -+ Path fooClass = foodir.resolve("Foo.class"); -+ -+ //Use a class that would only be known to the webapp classloader -+ try (InputStream foostream = Thread.currentThread().getContextClassLoader().getResourceAsStream("Foo.clazz"); -+ OutputStream out = Files.newOutputStream(fooClass)) -+ { -+ IO.copy(foostream, out); -+ } -+ -+ assertTrue(Files.exists(fooClass)); -+ assertThat(Files.size(fooClass), greaterThan(0L)); -+ -+ URL[] foodirUrls = new URL[]{foodir.toUri().toURL()}; -+ URLClassLoader contextClassLoader = new URLClassLoader(foodirUrls, Thread.currentThread().getContextClassLoader()); -+ - String contextPath = "/"; - String servletMapping = "/server"; - int inactivePeriod = 3; -@@ -135,58 +237,66 @@ public class SessionListenerTest - - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); -- SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory(); -- ((AbstractSessionDataStoreFactory)storeFactory).setGracePeriodSec(scavengePeriod); -+ TestSessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory(); -+ storeFactory.setGracePeriodSec(scavengePeriod); - - TestServer server1 = new TestServer(0, inactivePeriod, scavengePeriod, -- cacheFactory, storeFactory); -+ cacheFactory, storeFactory); - TestServlet servlet = new TestServlet(); - ServletHolder holder = new ServletHolder(servlet); - ServletContextHandler context = server1.addContext(contextPath); -+ context.setClassLoader(contextClassLoader); - context.addServlet(holder, servletMapping); -- TestHttpSessionListener listener = new TestHttpSessionListener(true); -+ //TestHttpSessionListener listener = new TestHttpSessionListenerWithWebappClasses(true, true); -+ TestHttpSessionListener listener = null; - context.getSessionHandler().addEventListener(listener); -- -- server1.start(); -- int port1 = server1.getPort(); - - try - { -+ server1.start(); -+ int port1 = server1.getPort(); -+ - HttpClient client = new HttpClient(); -- client.start(); -- String url = "http://localhost:" + port1 + contextPath + servletMapping.substring(1); -+ try -+ { -+ client.start(); -+ String url = "http://localhost:" + port1 + contextPath + servletMapping.substring(1); - -- //make a request to set up a session on the server -- ContentResponse response1 = client.GET(url + "?action=init"); -- assertEquals(HttpServletResponse.SC_OK,response1.getStatus()); -- String sessionCookie = response1.getHeaders().get("Set-Cookie"); -- assertTrue(sessionCookie != null); -- -- String sessionId = TestServer.extractSessionId(sessionCookie); -+ //make a request to set up a session on the server -+ ContentResponse response1 = client.GET(url + "?action=init"); -+ assertEquals(HttpServletResponse.SC_OK, response1.getStatus()); -+ String sessionCookie = response1.getHeaders().get("Set-Cookie"); -+ assertNotNull(sessionCookie); -+ -+ String sessionId = TestServer.extractSessionId(sessionCookie); -+ -+ //assertThat(sessionId, is(in(listener.createdSessions))); - -- assertThat(sessionId, isIn(listener.createdSessions)); -- -- //and wait until the session should have expired -- Thread.currentThread().sleep(TimeUnit.SECONDS.toMillis(inactivePeriod+(scavengePeriod))); -+ //and wait until the session should have expired -+ Thread.sleep(TimeUnit.SECONDS.toMillis(inactivePeriod + (2 * scavengePeriod))); - -- assertThat(sessionId, isIn(listener.destroyedSessions)); -+ //assertThat(sessionId, is(in(listener.destroyedSessions))); - -- assertNull(listener.ex); -+ assertNull(listener.attributeException); -+ assertNull(listener.accessTimeException); -+ } -+ finally -+ { -+ LifeCycle.stop(client); -+ } - } - finally - { - server1.stop(); -- } -+ } - } -- -+ - /** - * Check that a session that is expired cannot be reused, and expiry listeners are called for it -- * -- * @throws Exception - */ - @Test - public void testExpiredSession() throws Exception -- { -+ { - String contextPath = "/"; - String servletMapping = "/server"; - int inactivePeriod = 4; -@@ -194,65 +304,122 @@ public class SessionListenerTest - - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); -- SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory(); -- ((AbstractSessionDataStoreFactory)storeFactory).setGracePeriodSec(scavengePeriod); -+ TestSessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory(); -+ storeFactory.setGracePeriodSec(scavengePeriod); - - TestServer server1 = new TestServer(0, inactivePeriod, scavengePeriod, -- cacheFactory, storeFactory); -+ cacheFactory, storeFactory); - SimpleTestServlet servlet = new SimpleTestServlet(); - ServletHolder holder = new ServletHolder(servlet); - ServletContextHandler context = server1.addContext(contextPath); - context.addServlet(holder, servletMapping); -- TestHttpSessionListener listener = new TestHttpSessionListener(); -- -+ TestHttpSessionListener listener = new TestHttpSessionListener(true, true); -+ - context.getSessionHandler().addEventListener(listener); -- -- server1.start(); -- int port1 = server1.getPort(); - - try -- { -+ { -+ server1.start(); -+ int port1 = server1.getPort(); -+ - //save a session that has already expired - long now = System.currentTimeMillis(); -- SessionData data = context.getSessionHandler().getSessionCache().getSessionDataStore().newSessionData("1234", now-10, now-5, now-10, 30000); -+ SessionData data = context.getSessionHandler().getSessionCache().getSessionDataStore().newSessionData("1234", now - 10, now - 5, now - 10, 30000); - data.setExpiry(100); //make it expired a long time ago - context.getSessionHandler().getSessionCache().getSessionDataStore().store("1234", data); -- -+ - HttpClient client = new HttpClient(); -- client.start(); -+ try -+ { -+ client.start(); -+ -+ port1 = server1.getPort(); -+ String url = "http://localhost:" + port1 + contextPath + servletMapping.substring(1); -+ -+ //make another request using the id of the expired session -+ Request request = client.newRequest(url + "?action=test"); -+ request.cookie(new HttpCookie("JSESSIONID", "1234")); -+ ContentResponse response = request.send(); -+ assertEquals(HttpServletResponse.SC_OK, response.getStatus()); -+ -+ //should be a new session id -+ String cookie2 = response.getHeaders().get("Set-Cookie"); -+ assertNotEquals("1234", TestServer.extractSessionId(cookie2)); - -- port1 = server1.getPort(); -- String url = "http://localhost:" + port1 + contextPath + servletMapping.substring(1); -- -- //make another request using the id of the expired session -- Request request = client.newRequest(url + "?action=test"); -- request.cookie(new HttpCookie("JSESSIONID", "1234")); -- ContentResponse response = request.send(); -- assertEquals(HttpServletResponse.SC_OK,response.getStatus()); -- -- //should be a new session id -- String cookie2 = response.getHeaders().get("Set-Cookie"); -- assertNotEquals("1234", TestServer.extractSessionId(cookie2)); -- -- assertTrue (listener.destroyedSessions.contains("1234")); -- -- assertNull(listener.ex); -+ assertTrue(listener.destroyedSessions.contains("1234")); - -+ assertNull(listener.attributeException); -+ assertNull(listener.accessTimeException); -+ } -+ finally -+ { -+ LifeCycle.stop(client); -+ } - } - finally - { - server1.stop(); -- } -+ } -+ } -+ -+ public static class MyHttpSessionListener implements HttpSessionListener -+ { -+ @Override -+ public void sessionCreated(HttpSessionEvent se) -+ { -+ } -+ -+ @Override -+ public void sessionDestroyed(HttpSessionEvent se) -+ { -+ } - } - -- -- -+ public static class ThrowingSessionListener implements HttpSessionListener -+ { -+ -+ @Override -+ public void sessionCreated(HttpSessionEvent se) -+ { -+ } -+ -+ @Override -+ public void sessionDestroyed(HttpSessionEvent se) -+ { -+ throw new IllegalStateException("Exception during sessionDestroyed"); -+ } -+ -+ } -+ -+ @Test -+ public void testSessionListeners() -+ { -+ Server server = new Server(); -+ try -+ { -+ WebAppContext wac = new WebAppContext(); -+ -+ wac.setServer(server); -+ server.setHandler(wac); -+ wac.addEventListener(new MyHttpSessionListener()); -+ -+ Collection listeners = wac.getSessionHandler().getBeans(MyHttpSessionListener.class); -+ assertNotNull(listeners); -+ -+ assertEquals(1, listeners.size()); -+ } -+ finally -+ { -+ LifeCycle.stop(server); -+ } -+ } -+ - public static class MySessionBindingListener implements HttpSessionBindingListener, Serializable - { - private static final long serialVersionUID = 1L; - boolean unbound = false; - boolean bound = false; -- -+ - public void valueUnbound(HttpSessionBindingEvent event) - { - unbound = true; -@@ -263,38 +430,34 @@ public class SessionListenerTest - bound = true; - } - } -- -- -- -+ - public static class TestServlet extends HttpServlet - { - private static final long serialVersionUID = 1L; - public static final MySessionBindingListener bindingListener = new MySessionBindingListener(); -- - - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException - { - String action = request.getParameter("action"); -- -+ - if ("init".equals(action)) - { - HttpSession session = request.getSession(true); - session.setAttribute("foo", bindingListener); - assertNotNull(session); -- - } - else if ("test".equals(action)) - { - HttpSession session = request.getSession(false); - assertNotNull(session); -- -+ - //invalidate existing session - session.invalidate(); - } - } - } -- -+ - public static class SimpleTestServlet extends HttpServlet - { - private static final long serialVersionUID = 1L; -@@ -306,7 +469,7 @@ public class SessionListenerTest - if ("test".equals(action)) - { - HttpSession session = request.getSession(true); -- assertTrue(session != null); -+ assertNotNull(session); - } - } - } diff --git a/CVE-2022-2047.patch b/CVE-2022-2047.patch deleted file mode 100644 index 64a7a9a538a2d51eac7d5ef77ca8fadd7e6b75ae..0000000000000000000000000000000000000000 --- a/CVE-2022-2047.patch +++ /dev/null @@ -1,326 +0,0 @@ -From: Markus Koschany -Date: Wed, 17 Aug 2022 12:58:27 +0200 -Subject: CVE-2022-2047 - -Origin: https://github.com/eclipse/jetty.project/pull/8146 ---- - .../java/org/eclipse/jetty/client/HttpRequest.java | 8 +- - .../eclipse/jetty/client/HttpClientURITest.java | 45 ++++++++++ - .../main/java/org/eclipse/jetty/http/HttpURI.java | 25 +++++- - .../java/org/eclipse/jetty/http/HttpURITest.java | 95 ++++++++++++++++++++++ - .../org/eclipse/jetty/proxy/ConnectHandler.java | 2 +- - .../java/org/eclipse/jetty/server/Request.java | 14 +++- - .../eclipse/jetty/server/HttpConnectionTest.java | 12 +-- - 7 files changed, 187 insertions(+), 14 deletions(-) - -diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java -index f6a453f..2d2afa0 100644 ---- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java -+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java -@@ -177,6 +177,8 @@ public class HttpRequest implements Request - String rawPath = uri.getRawPath(); - if (rawPath == null) - rawPath = ""; -+ if (!rawPath.startsWith("/")) -+ rawPath = "/" + rawPath; - this.path = rawPath; - String query = uri.getRawQuery(); - if (query != null) -@@ -855,14 +857,14 @@ public class HttpRequest implements Request - return result; - } - -- private URI newURI(String uri) -+ private URI newURI(String path) - { - try - { - // Handle specially the "OPTIONS *" case, since it is possible to create a URI from "*" (!). -- if ("*".equals(uri)) -+ if ("*".equals(path)) - return null; -- URI result = new URI(uri); -+ URI result = new URI(path); - return result.isOpaque() ? null : result; - } - catch (URISyntaxException x) -diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java -index 9c43512..5a97bdd 100644 ---- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java -+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java -@@ -189,7 +189,7 @@ public class HttpURI - _uri=uri; - - if (HttpMethod.CONNECT.is(method)) -- _path=uri; -+ parse(State.HOST, uri, 0, uri.length()); - else - parse(uri.startsWith("/")?State.PATH:State.START,uri,0,uri.length()); - } -@@ -720,17 +720,30 @@ public class HttpURI - */ - public void setAuthority(String host, int port) - { -+ if (host != null && !isPathValidForAuthority(_path)) -+ throw new IllegalArgumentException("Relative path with authority"); - _host=host; - _port=port; - _uri=null; - } - -+ private boolean isPathValidForAuthority(String path) -+ { -+ if (path == null) -+ return true; -+ if (path.isEmpty() || "*".equals(path)) -+ return true; -+ return path.startsWith("/"); -+ } -+ - /* ------------------------------------------------------------ */ - /** - * @param path the path - */ - public void setPath(String path) - { -+ if (hasAuthority() && !isPathValidForAuthority(path)) -+ throw new IllegalArgumentException("Relative path with authority"); - _uri=null; - _path=path; - _decodedPath=null; -@@ -739,6 +752,8 @@ public class HttpURI - /* ------------------------------------------------------------ */ - public void setPathQuery(String path) - { -+ if (hasAuthority() && !isPathValidForAuthority(path)) -+ throw new IllegalArgumentException("Relative path with authority"); - _uri=null; - _path=null; - _decodedPath=null; -@@ -747,7 +762,13 @@ public class HttpURI - if (path!=null) - parse(State.PATH,path,0,path.length()); - } -- -+ -+ private boolean hasAuthority() -+ { -+ return _host != null; -+ } -+ -+ - /* ------------------------------------------------------------ */ - public void setQuery(String query) - { -diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java -index 63a58cf..6c3e65f 100644 ---- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java -+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java -@@ -32,6 +32,15 @@ import java.nio.charset.StandardCharsets; - import org.eclipse.jetty.util.MultiMap; - import org.junit.jupiter.api.Test; - -+import org.junit.jupiter.params.ParameterizedTest; -+import org.junit.jupiter.params.provider.Arguments; -+import org.junit.jupiter.params.provider.MethodSource; -+import static org.junit.jupiter.api.Assertions.assertEquals; -+import static org.junit.jupiter.api.Assertions.assertThrows; -+import static org.junit.jupiter.api.Assertions.assertTrue; -+import static org.junit.jupiter.api.Assertions.fail; -+import static org.junit.jupiter.api.Assumptions.assumeTrue; -+ - public class HttpURITest - { - @Test -@@ -100,6 +109,32 @@ public class HttpURITest - assertThat(uri.getPath(),is("/bar")); - } - -+ @Test -+ public void testCONNECT() -+ { -+ HttpURI uri = new HttpURI(); -+ -+ uri.parseRequestTarget("CONNECT", "host:80"); -+ assertThat(uri.getHost(), is("host")); -+ assertThat(uri.getPort(), is(80)); -+ assertThat(uri.getPath(), nullValue()); -+ -+ uri.parseRequestTarget("CONNECT", "host"); -+ assertThat(uri.getHost(), is("host")); -+ assertThat(uri.getPort(), is(-1)); -+ assertThat(uri.getPath(), nullValue()); -+ -+ uri.parseRequestTarget("CONNECT", "192.168.0.1:8080"); -+ assertThat(uri.getHost(), is("192.168.0.1")); -+ assertThat(uri.getPort(), is(8080)); -+ assertThat(uri.getPath(), nullValue()); -+ -+ uri.parseRequestTarget("CONNECT", "[::1]:8080"); -+ assertThat(uri.getHost(), is("[::1]")); -+ assertThat(uri.getPort(), is(8080)); -+ assertThat(uri.getPath(), nullValue()); -+ } -+ - @Test - public void testExtB() throws Exception - { -@@ -222,4 +257,64 @@ public class HttpURITest - assertEquals(uri.getAuthority(), "example.com:8888"); - assertEquals(uri.getUser(), "user:password"); - } -+ -+ @Test -+ public void testRelativePathWithAuthority() -+ { -+ assertThrows(IllegalArgumentException.class, () -> -+ { -+ HttpURI httpURI = new HttpURI(); -+ httpURI.setAuthority("host", 0); -+ httpURI.setPath("path"); -+ }); -+ assertThrows(IllegalArgumentException.class, () -> -+ { -+ HttpURI httpURI = new HttpURI(); -+ httpURI.setAuthority("host", 8080); -+ httpURI.setPath(";p=v/url"); -+ }); -+ assertThrows(IllegalArgumentException.class, () -> -+ { -+ HttpURI httpURI = new HttpURI(); -+ httpURI.setAuthority("host", 0); -+ httpURI.setPath(";"); -+ }); -+ -+ assertThrows(IllegalArgumentException.class, () -> -+ { -+ HttpURI httpURI = new HttpURI(); -+ httpURI.setPath("path"); -+ httpURI.setAuthority("host", 0); -+ }); -+ assertThrows(IllegalArgumentException.class, () -> -+ { -+ HttpURI httpURI = new HttpURI(); -+ httpURI.setPath(";p=v/url"); -+ httpURI.setAuthority("host", 8080); -+ }); -+ assertThrows(IllegalArgumentException.class, () -> -+ { -+ HttpURI httpURI = new HttpURI(); -+ httpURI.setPath(";"); -+ httpURI.setAuthority("host", 0); -+ }); -+ -+ HttpURI uri = new HttpURI(); -+ uri.setPath("*"); -+ uri.setAuthority("host", 0); -+ assertEquals("//host*", uri.toString()); -+ uri = new HttpURI(); -+ uri.setAuthority("host", 0); -+ uri.setPath("*"); -+ assertEquals("//host*", uri.toString()); -+ -+ uri = new HttpURI(); -+ uri.setPath(""); -+ uri.setAuthority("host", 0); -+ assertEquals("//host", uri.toString()); -+ uri = new HttpURI(); -+ uri.setAuthority("host", 0); -+ uri.setPath(""); -+ assertEquals("//host", uri.toString()); -+ } - } -diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java -index 6b7d39e..d3dd5c8 100644 ---- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java -+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java -@@ -197,7 +197,7 @@ public class ConnectHandler extends HandlerWrapper - { - if (HttpMethod.CONNECT.is(request.getMethod())) - { -- String serverAddress = request.getRequestURI(); -+ String serverAddress = baseRequest.getHttpURI().getAuthority(); - if (LOG.isDebugEnabled()) - LOG.debug("CONNECT request for {}", serverAddress); - -diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java -index b15bcc7..5b996bb 100644 ---- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java -+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java -@@ -1778,9 +1778,19 @@ public class Request implements HttpServletRequest - - setMethod(request.getMethod()); - HttpURI uri = request.getURI(); -- _originalURI = uri.isAbsolute()&&request.getHttpVersion()!=HttpVersion.HTTP_2?uri.toString():uri.getPathQuery(); -+ String encoded; -+ if (HttpMethod.CONNECT.is(request.getMethod())) -+ { -+ _originalURI = uri.getAuthority(); -+ encoded = "/"; -+ } -+ else -+ { -+ _originalURI = uri.isAbsolute() && request.getHttpVersion() != HttpVersion.HTTP_2 ? uri.toString() : uri.getPathQuery(); -+ encoded = uri.getPath(); -+ } -+ - -- String encoded = uri.getPath(); - String path; - if (encoded==null) - { -diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java -index 918a112..22391f7 100644 ---- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java -+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java -@@ -365,7 +365,7 @@ public class HttpConnectionTest - public void testBadPathDotDotPath() throws Exception - { - String response=connector.getResponse("GET /ooops/../../path HTTP/1.0\r\nHost: localhost:80\r\n\n"); -- checkContains(response,0,"HTTP/1.1 400 Bad URI"); -+ checkContains(response,0,"HTTP/1.1 400"); - } - - @Test -@@ -380,28 +380,28 @@ public class HttpConnectionTest - public void testBadPathEncodedDotDotPath() throws Exception - { - String response=connector.getResponse("GET /ooops/%2e%2e/%2e%2e/path HTTP/1.0\r\nHost: localhost:80\r\n\n"); -- checkContains(response,0,"HTTP/1.1 400 Bad URI"); -+ checkContains(response,0,"HTTP/1.1 400"); - } - - @Test - public void testBadDotDotPath() throws Exception - { - String response=connector.getResponse("GET ../path HTTP/1.0\r\nHost: localhost:80\r\n\n"); -- checkContains(response,0,"HTTP/1.1 400 Bad URI"); -+ checkContains(response,0,"HTTP/1.1 400"); - } - - @Test - public void testBadSlashDotDotPath() throws Exception - { - String response=connector.getResponse("GET /../path HTTP/1.0\r\nHost: localhost:80\r\n\n"); -- checkContains(response,0,"HTTP/1.1 400 Bad URI"); -+ checkContains(response,0,"HTTP/1.1 400"); - } - - @Test - public void testEncodedBadDotDotPath() throws Exception - { - String response=connector.getResponse("GET %2e%2e/path HTTP/1.0\r\nHost: localhost:80\r\n\n"); -- checkContains(response,0,"HTTP/1.1 400 Bad URI"); -+ checkContains(response,0,"HTTP/1.1 400"); - } - - @Test -@@ -1168,7 +1168,7 @@ public class HttpConnectionTest - "12345\r\n"+ - "0;\r\n" + - "\r\n"); -- checkContains(response,offset,"HTTP/1.1 400 Bad Request"); -+ checkContains(response,offset,"HTTP/1.1 400"); - } - catch (Exception e) - { diff --git a/CVE-2022-2048.patch b/CVE-2022-2048.patch deleted file mode 100644 index 42fbca2762365eea0d6ce5b5d7a1da54f154583c..0000000000000000000000000000000000000000 --- a/CVE-2022-2048.patch +++ /dev/null @@ -1,47 +0,0 @@ -From: Markus Koschany -Date: Wed, 17 Aug 2022 12:59:00 +0200 -Subject: CVE-2022-2048 - -Origin: https://github.com/eclipse/jetty.project/issues/7935 ---- - .../jetty/http2/server/HttpChannelOverHTTP2.java | 12 +- - .../org/eclipse/jetty/http2/server/BadURITest.java | 153 +++++++++++++++++++++ - 2 files changed, 157 insertions(+), 8 deletions(-) - create mode 100644 jetty-http2/http2-server/src/test/java/org/eclipse/jetty/http2/server/BadURITest.java - -diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java -index 03b082e..3548497 100644 ---- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java -+++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java -@@ -143,13 +143,11 @@ public class HttpChannelOverHTTP2 extends HttpChannel implements Closeable, Writ - } - catch (BadMessageException x) - { -- onBadMessage(x); -- return null; -+ return () -> onBadMessage(x); - } - catch (Throwable x) - { -- onBadMessage(new BadMessageException(HttpStatus.INTERNAL_SERVER_ERROR_500, null, x)); -- return null; -+ return () -> onBadMessage(new BadMessageException(HttpStatus.INTERNAL_SERVER_ERROR_500, null, x)); - } - } - -@@ -175,13 +173,11 @@ public class HttpChannelOverHTTP2 extends HttpChannel implements Closeable, Writ - } - catch (BadMessageException x) - { -- onBadMessage(x); -- return null; -+ return () -> onBadMessage(x); - } - catch (Throwable x) - { -- onBadMessage(new BadMessageException(HttpStatus.INTERNAL_SERVER_ERROR_500, null, x)); -- return null; -+ return () -> onBadMessage(new BadMessageException(HttpStatus.INTERNAL_SERVER_ERROR_500, null, x)); - } - } - diff --git a/CVE-2023-26048.patch b/CVE-2023-26048.patch deleted file mode 100644 index e423fa4153d300d5eeb5a921ce148d2bc84ff23f..0000000000000000000000000000000000000000 --- a/CVE-2023-26048.patch +++ /dev/null @@ -1,537 +0,0 @@ -From: Markus Koschany -Date: Tue, 26 Sep 2023 20:37:47 +0200 -Subject: CVE-2023-26048 - -Origin: https://github.com/eclipse/jetty.project/pull/9345 ---- - .../jetty/http/MultiPartFormInputStream.java | 76 +++++++----- - .../java/org/eclipse/jetty/server/MultiParts.java | 14 ++- - .../java/org/eclipse/jetty/server/Request.java | 127 ++++++++++++--------- - .../jetty/server/handler/ContextHandler.java | 4 + - .../jetty/util/MultiPartInputStreamParser.java | 24 +++- - 5 files changed, 158 insertions(+), 87 deletions(-) - -diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPartFormInputStream.java b/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPartFormInputStream.java -index 928f59c..a1092f7 100644 ---- a/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPartFormInputStream.java -+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/MultiPartFormInputStream.java -@@ -60,11 +60,14 @@ import org.eclipse.jetty.util.log.Logger; - public class MultiPartFormInputStream - { - private static final Logger LOG = Log.getLogger(MultiPartFormInputStream.class); -+ private static final int DEFAULT_MAX_FORM_KEYS = 1000; - private static final MultiMap EMPTY_MAP = new MultiMap<>(Collections.emptyMap()); -+ private final MultiMap _parts; -+ private final int _maxParts; -+ private int _numParts = 0; - private InputStream _in; - private MultipartConfigElement _config; - private String _contentType; -- private MultiMap _parts; - private Throwable _err; - private File _tmpDir; - private File _contextTmpDir; -@@ -332,26 +335,42 @@ public class MultiPartFormInputStream - * @param contextTmpDir javax.servlet.context.tempdir - */ - public MultiPartFormInputStream(InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir) -+ { -+ this(in, contentType, config, contextTmpDir, DEFAULT_MAX_FORM_KEYS); -+ } -+ -+ /** -+ * @param in Request input stream -+ * @param contentType Content-Type header -+ * @param config MultipartConfigElement -+ * @param contextTmpDir javax.servlet.context.tempdir -+ * @param maxParts the maximum number of parts that can be parsed from the multipart content (0 for no parts allowed, -1 for unlimited parts). -+ */ -+ public MultiPartFormInputStream(InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir, int maxParts) -+ - { - _contentType = contentType; - _config = config; - _contextTmpDir = contextTmpDir; -+ _maxParts = maxParts; - if (_contextTmpDir == null) - _contextTmpDir = new File(System.getProperty("java.io.tmpdir")); - - if (_config == null) - _config = new MultipartConfigElement(_contextTmpDir.getAbsolutePath()); - -+ MultiMap parts = new MultiMap<>(); - if (in instanceof ServletInputStream) - { - if (((ServletInputStream)in).isFinished()) - { -- _parts = EMPTY_MAP; -+ parts = EMPTY_MAP; - _parsed = true; -- return; - } - } -- _in = new BufferedInputStream(in); -+ if (!_parsed) -+ _in = new BufferedInputStream(in); -+ _parts = parts; - } - - /** -@@ -495,16 +514,15 @@ public class MultiPartFormInputStream - if (_parsed) - return; - _parsed = true; -- -+ -+ MultiPartParser parser = null; -+ Handler handler = new Handler(); - try - { -- // initialize -- _parts = new MultiMap<>(); -- - // if its not a multipart request, don't parse it - if (_contentType == null || !_contentType.startsWith("multipart/form-data")) - return; -- -+ - // sort out the location to which to write the files - if (_config.getLocation() == null) - _tmpDir = _contextTmpDir; -@@ -518,10 +536,10 @@ public class MultiPartFormInputStream - else - _tmpDir = new File(_contextTmpDir, _config.getLocation()); - } -- -+ - if (!_tmpDir.exists()) - _tmpDir.mkdirs(); -- -+ - String contentTypeBoundary = ""; - int bstart = _contentType.indexOf("boundary="); - if (bstart >= 0) -@@ -530,22 +548,19 @@ public class MultiPartFormInputStream - bend = (bend < 0 ? _contentType.length() : bend); - contentTypeBoundary = QuotedStringTokenizer.unquote(value(_contentType.substring(bstart, bend)).trim()); - } -- -- Handler handler = new Handler(); -- MultiPartParser parser = new MultiPartParser(handler, contentTypeBoundary); -- -+ -+ parser = new MultiPartParser(handler, contentTypeBoundary); - byte[] data = new byte[_bufferSize]; - int len; - long total = 0; -- -+ - while (true) - { -- -+ - len = _in.read(data); -- -+ - if (len > 0) - { -- - // keep running total of size of bytes read from input and throw an exception if exceeds MultipartConfigElement._maxRequestSize - total += len; - if (_config.getMaxRequestSize() > 0 && total > _config.getMaxRequestSize()) -@@ -553,30 +568,28 @@ public class MultiPartFormInputStream - _err = new IllegalStateException("Request exceeds maxRequestSize (" + _config.getMaxRequestSize() + ")"); - return; - } -- -+ - ByteBuffer buffer = BufferUtil.toBuffer(data); - buffer.limit(len); - if (parser.parse(buffer, false)) - break; -- -+ - if (buffer.hasRemaining()) - throw new IllegalStateException("Buffer did not fully consume"); -- - } - else if (len == -1) - { - parser.parse(BufferUtil.EMPTY_BUFFER, true); - break; - } -- - } -- -+ - // check for exceptions - if (_err != null) - { - return; - } -- -+ - // check we read to the end of the message - if (parser.getState() != MultiPartParser.State.END) - { -@@ -585,19 +598,23 @@ public class MultiPartFormInputStream - else - _err = new IOException("Incomplete Multipart"); - } -- -+ - if (LOG.isDebugEnabled()) - { - LOG.debug("Parsing Complete {} err={}", parser, _err); - } -- - } - catch (Throwable e) - { - _err = e; -+ -+ // Notify parser if failure occurs -+ if (parser != null) -+ parser.parse(BufferUtil.EMPTY_BUFFER, true); - } - } -- -+ -+ - class Handler implements MultiPartParser.Handler - { - private MultiPart _part = null; -@@ -735,6 +752,9 @@ public class MultiPartFormInputStream - public void startPart() - { - reset(); -+ _numParts++; -+ if (_maxParts >= 0 && _numParts > _maxParts) -+ throw new IllegalStateException(String.format("Form with too many parts [%d > %d]", _numParts, _maxParts)); - } - - @Override -diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/MultiParts.java b/jetty-server/src/main/java/org/eclipse/jetty/server/MultiParts.java -index f28b945..1b91649 100644 ---- a/jetty-server/src/main/java/org/eclipse/jetty/server/MultiParts.java -+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/MultiParts.java -@@ -57,7 +57,12 @@ public interface MultiParts extends Closeable - - public MultiPartsHttpParser(InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir, Request request) throws IOException - { -- _httpParser = new MultiPartFormInputStream(in, contentType, config, contextTmpDir); -+ this(in, contentType, config, contextTmpDir, request, ContextHandler.DEFAULT_MAX_FORM_KEYS); -+ } -+ -+ public MultiPartsHttpParser(InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir, Request request, int maxParts) throws IOException -+ { -+ _httpParser = new MultiPartFormInputStream(in, contentType, config, contextTmpDir, maxParts); - _context = request.getContext(); - _httpParser.getParts(); - } -@@ -116,7 +121,12 @@ public interface MultiParts extends Closeable - - public MultiPartsUtilParser(InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir, Request request) throws IOException - { -- _utilParser = new MultiPartInputStreamParser(in, contentType, config, contextTmpDir); -+ this(in, contentType, config, contextTmpDir, request, ContextHandler.DEFAULT_MAX_FORM_KEYS); -+ } -+ -+ public MultiPartsUtilParser(InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir, Request request, int maxParts) throws IOException -+ { -+ _utilParser = new MultiPartInputStreamParser(in, contentType, config, contextTmpDir, maxParts); - _context = request.getContext(); - _utilParser.getParts(); - -diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java -index 5b996bb..8a7e6f9 100644 ---- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java -+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java -@@ -425,6 +425,14 @@ public class Request implements HttpServletRequest - return parameters==null?NO_PARAMS:parameters; - } - -+ private boolean isContentEncodingSupported() -+ { -+ String contentEncoding = getHttpFields().get(HttpHeader.CONTENT_ENCODING); -+ if (contentEncoding == null) -+ return true; -+ return HttpHeaderValue.IDENTITY.is(contentEncoding); -+ } -+ - /* ------------------------------------------------------------ */ - private void extractQueryParameters() - { -@@ -458,33 +466,34 @@ public class Request implements HttpServletRequest - { - String contentType = getContentType(); - if (contentType == null || contentType.isEmpty()) -- _contentParameters=NO_PARAMS; -+ _contentParameters = NO_PARAMS; - else - { -- _contentParameters=new MultiMap<>(); -+ _contentParameters = new MultiMap<>(); - int contentLength = getContentLength(); - if (contentLength != 0 && _inputState == __NONE) - { -- contentType = HttpFields.valueParameters(contentType, null); -- if (MimeTypes.Type.FORM_ENCODED.is(contentType) && -+ String baseType = HttpFields.valueParameters(contentType, null); -+ if (MimeTypes.Type.FORM_ENCODED.is(baseType) && - _channel.getHttpConfiguration().isFormEncodedMethod(getMethod())) - { -- if (_metaData!=null) -+ if (_metaData != null && !isContentEncodingSupported()) - { -- String contentEncoding = getHttpFields().get(HttpHeader.CONTENT_ENCODING); -- if (contentEncoding!=null && !HttpHeaderValue.IDENTITY.is(contentEncoding)) -- throw new BadMessageException(HttpStatus.NOT_IMPLEMENTED_501, "Unsupported Content-Encoding"); -+ throw new BadMessageException(HttpStatus.UNSUPPORTED_MEDIA_TYPE_415, "Unsupported Content-Encoding"); - } -+ - extractFormParameters(_contentParameters); - } -- else if (MimeTypes.Type.MULTIPART_FORM_DATA.is(contentType) && -- getAttribute(__MULTIPART_CONFIG_ELEMENT) != null && -- _multiParts == null) -+ else if (MimeTypes.Type.MULTIPART_FORM_DATA.is(baseType) && -+ getAttribute(__MULTIPART_CONFIG_ELEMENT) != null && -+ _multiParts == null) - { - try - { -- if (_metaData!=null && getHttpFields().contains(HttpHeader.CONTENT_ENCODING)) -- throw new BadMessageException(HttpStatus.NOT_IMPLEMENTED_501,"Unsupported Content-Encoding"); -+ if (_metaData != null && !isContentEncodingSupported()) -+ { -+ throw new BadMessageException(HttpStatus.UNSUPPORTED_MEDIA_TYPE_415, "Unsupported Content-Encoding"); -+ } - getParts(_contentParameters); - } - catch (IOException | ServletException e) -@@ -502,57 +511,30 @@ public class Request implements HttpServletRequest - { - try - { -- int maxFormContentSize = -1; -- int maxFormKeys = -1; -+ int maxFormContentSize = ContextHandler.DEFAULT_MAX_FORM_CONTENT_SIZE; -+ int maxFormKeys = ContextHandler.DEFAULT_MAX_FORM_KEYS; - - if (_context != null) - { -- maxFormContentSize = _context.getContextHandler().getMaxFormContentSize(); -- maxFormKeys = _context.getContextHandler().getMaxFormKeys(); -+ ContextHandler contextHandler = _context.getContextHandler(); -+ maxFormContentSize = contextHandler.getMaxFormContentSize(); -+ maxFormKeys = contextHandler.getMaxFormKeys(); - } -- -- if (maxFormContentSize < 0) -+ else - { -- Object obj = _channel.getServer().getAttribute("org.eclipse.jetty.server.Request.maxFormContentSize"); -- if (obj == null) -- maxFormContentSize = 200000; -- else if (obj instanceof Number) -- { -- Number size = (Number)obj; -- maxFormContentSize = size.intValue(); -- } -- else if (obj instanceof String) -- { -- maxFormContentSize = Integer.parseInt((String)obj); -- } -- } -- -- if (maxFormKeys < 0) -- { -- Object obj = _channel.getServer().getAttribute("org.eclipse.jetty.server.Request.maxFormKeys"); -- if (obj == null) -- maxFormKeys = 1000; -- else if (obj instanceof Number) -- { -- Number keys = (Number)obj; -- maxFormKeys = keys.intValue(); -- } -- else if (obj instanceof String) -- { -- maxFormKeys = Integer.parseInt((String)obj); -- } -+ maxFormContentSize = lookupServerAttribute(ContextHandler.MAX_FORM_CONTENT_SIZE_KEY, maxFormContentSize); -+ maxFormKeys = lookupServerAttribute(ContextHandler.MAX_FORM_KEYS_KEY, maxFormKeys); - } - - int contentLength = getContentLength(); -- if (contentLength > maxFormContentSize && maxFormContentSize > 0) -- { -- throw new IllegalStateException("Form too large: " + contentLength + " > " + maxFormContentSize); -- } -+ if (maxFormContentSize >= 0 && contentLength > maxFormContentSize) -+ throw new IllegalStateException("Form is larger than max length " + maxFormContentSize); -+ - InputStream in = getInputStream(); - if (_input.isAsync()) - throw new IllegalStateException("Cannot extract parameters with async IO"); - -- UrlEncoded.decodeTo(in,params,getCharacterEncoding(),contentLength<0?maxFormContentSize:-1,maxFormKeys); -+ UrlEncoded.decodeTo(in, params, getCharacterEncoding(), maxFormContentSize, maxFormKeys); - } - catch (IOException e) - { -@@ -561,6 +543,16 @@ public class Request implements HttpServletRequest - } - } - -+ private int lookupServerAttribute(String key, int dftValue) -+ { -+ Object attribute = _channel.getServer().getAttribute(key); -+ if (attribute instanceof Number) -+ return ((Number)attribute).intValue(); -+ else if (attribute instanceof String) -+ return Integer.parseInt((String)attribute); -+ return dftValue; -+ } -+ - /* ------------------------------------------------------------ */ - @Override - public AsyncContext getAsyncContext() -@@ -2351,9 +2343,23 @@ public class Request implements HttpServletRequest - if (config == null) - throw new IllegalStateException("No multipart config for servlet"); - -+ int maxFormContentSize = ContextHandler.DEFAULT_MAX_FORM_CONTENT_SIZE; -+ int maxFormKeys = ContextHandler.DEFAULT_MAX_FORM_KEYS; -+ if (_context != null) -+ { -+ ContextHandler contextHandler = _context.getContextHandler(); -+ maxFormContentSize = contextHandler.getMaxFormContentSize(); -+ maxFormKeys = contextHandler.getMaxFormKeys(); -+ } -+ else -+ { -+ maxFormContentSize = lookupServerAttribute(ContextHandler.MAX_FORM_CONTENT_SIZE_KEY, maxFormContentSize); -+ maxFormKeys = lookupServerAttribute(ContextHandler.MAX_FORM_KEYS_KEY, maxFormKeys); -+ } -+ - _multiParts = newMultiParts(getInputStream(), - getContentType(), config, -- (_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null)); -+ (_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null),maxFormKeys); - - setAttribute(__MULTIPARTS, _multiParts); - Collection parts = _multiParts.getParts(); //causes parsing -@@ -2388,11 +2394,16 @@ public class Request implements HttpServletRequest - else - defaultCharset = StandardCharsets.UTF_8; - -+ long formContentSize = 0; - ByteArrayOutputStream os = null; - for (Part p:parts) - { - if (p.getSubmittedFileName() == null) - { -+ formContentSize = Math.addExact(formContentSize, p.getSize()); -+ if (maxFormContentSize >= 0 && formContentSize > maxFormContentSize) -+ throw new IllegalStateException("Form is larger than max length " + maxFormContentSize); -+ - // Servlet Spec 3.0 pg 23, parts without filename must be put into params. - String charset = null; - if (p.getContentType() != null) -@@ -2418,7 +2429,9 @@ public class Request implements HttpServletRequest - } - - -- private MultiParts newMultiParts(ServletInputStream inputStream, String contentType, MultipartConfigElement config, Object object) throws IOException -+ private MultiParts newMultiParts(ServletInputStream inputStream, String -+ contentType, MultipartConfigElement config, Object object, -+ int maxParts) throws IOException - { - MultiPartFormDataCompliance compliance = getHttpChannel().getHttpConfiguration().getMultipartFormDataCompliance(); - if(LOG.isDebugEnabled()) -@@ -2428,12 +2441,14 @@ public class Request implements HttpServletRequest - { - case RFC7578: - return new MultiParts.MultiPartsHttpParser(getInputStream(), getContentType(), config, -- (_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null), this); -+ (_context != -+ null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null), this, maxParts); - - case LEGACY: - default: - return new MultiParts.MultiPartsUtilParser(getInputStream(), getContentType(), config, -- (_context != null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null), this); -+ (_context != -+ null?(File)_context.getAttribute("javax.servlet.context.tempdir"):null), this, maxParts); - - } - } -diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java -index b6ca046..c4cbebd 100644 ---- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java -+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java -@@ -132,6 +132,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu - */ - public static final String MANAGED_ATTRIBUTES = "org.eclipse.jetty.server.context.ManagedAttributes"; - -+ public static final String MAX_FORM_KEYS_KEY = "org.eclipse.jetty.server.Request.maxFormKeys"; -+ public static final String MAX_FORM_CONTENT_SIZE_KEY = "org.eclipse.jetty.server.Request.maxFormContentSize"; -+ public static final int DEFAULT_MAX_FORM_KEYS = 1000; -+ public static final int DEFAULT_MAX_FORM_CONTENT_SIZE = 200000; - /* ------------------------------------------------------------ */ - /** - * Get the current ServletContext implementation. -diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java -index 17e7bb1..d45b8ff 100644 ---- a/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java -+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/MultiPartInputStreamParser.java -@@ -65,8 +65,11 @@ import org.eclipse.jetty.util.log.Logger; - public class MultiPartInputStreamParser - { - private static final Logger LOG = Log.getLogger(MultiPartInputStreamParser.class); -+ private static final int DEFAULT_MAX_FORM_KEYS = 1000; - public static final MultipartConfigElement __DEFAULT_MULTIPART_CONFIG = new MultipartConfigElement(System.getProperty("java.io.tmpdir")); -- public static final MultiMap EMPTY_MAP = new MultiMap(Collections.emptyMap()); -+ public static final MultiMap EMPTY_MAP = new MultiMap<>(Collections.emptyMap()); -+ private final int _maxParts; -+ private int _numParts; - protected InputStream _in; - protected MultipartConfigElement _config; - protected String _contentType; -@@ -411,9 +414,23 @@ public class MultiPartInputStreamParser - */ - public MultiPartInputStreamParser (InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir) - { -+ this(in, contentType, config, contextTmpDir, DEFAULT_MAX_FORM_KEYS); -+ } -+ -+ /** -+ * @param in Request input stream -+ * @param contentType Content-Type header -+ * @param config MultipartConfigElement -+ * @param contextTmpDir javax.servlet.context.tempdir -+ * @param maxParts the maximum number of parts that can be parsed from the multipart content (0 for no parts allowed, -1 for unlimited parts). -+ */ -+ public MultiPartInputStreamParser(InputStream in, String contentType, MultipartConfigElement config, File contextTmpDir, int maxParts) -+ -+ { - _contentType = contentType; - _config = config; - _contextTmpDir = contextTmpDir; -+ _maxParts = maxParts; - if (_contextTmpDir == null) - _contextTmpDir = new File (System.getProperty("java.io.tmpdir")); - -@@ -712,6 +729,11 @@ public class MultiPartInputStreamParser - continue; - } - -+ // Check if we can create a new part. -+ _numParts++; -+ if (_maxParts >= 0 && _numParts > _maxParts) -+ throw new IllegalStateException(String.format("Form with too many parts [%d > %d]", _numParts, _maxParts)); -+ - //Have a new Part - MultiPart part = new MultiPart(name, filename); - part.setHeaders(headers); diff --git a/CVE-2023-26049.patch b/CVE-2023-26049.patch deleted file mode 100644 index b11fdc84745b937c575d1e03cfcb8bc53c470419..0000000000000000000000000000000000000000 --- a/CVE-2023-26049.patch +++ /dev/null @@ -1,831 +0,0 @@ -From: Markus Koschany -Date: Tue, 26 Sep 2023 23:42:03 +0200 -Subject: CVE-2023-26049 - -Origin: https://github.com/eclipse/jetty.project/pull/9352 ---- - .../org/eclipse/jetty/http/CookieCompliance.java | 2 +- - .../org/eclipse/jetty/server/CookieCutter.java | 205 ++++++++++----- - .../org/eclipse/jetty/server/CookieCutterTest.java | 280 ++++++++++++++++----- - .../java/org/eclipse/jetty/server/RequestTest.java | 2 + - 4 files changed, 361 insertions(+), 128 deletions(-) - -diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/CookieCompliance.java b/jetty-http/src/main/java/org/eclipse/jetty/http/CookieCompliance.java -index b2d339c..d514c15 100644 ---- a/jetty-http/src/main/java/org/eclipse/jetty/http/CookieCompliance.java -+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/CookieCompliance.java -@@ -22,4 +22,4 @@ package org.eclipse.jetty.http; - * The compliance for Cookie handling. - * - */ --public enum CookieCompliance { RFC6265, RFC2965 } -+public enum CookieCompliance { RFC6265, RFC2965, RFC6265_LEGACY } -diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/CookieCutter.java b/jetty-server/src/main/java/org/eclipse/jetty/server/CookieCutter.java -index 5dce1cf..e28d262 100644 ---- a/jetty-server/src/main/java/org/eclipse/jetty/server/CookieCutter.java -+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/CookieCutter.java -@@ -107,23 +107,24 @@ public class CookieCutter - _lastCookies=null; - _fieldList.add(_fields++,f); - } -- -- -+ - protected void parseFields() - { -- _lastCookies=null; -- _cookies=null; -- -+ _lastCookies = null; -+ _cookies = null; -+ - List cookies = new ArrayList<>(); - - int version = 0; - - // delete excess fields -- while (_fieldList.size()>_fields) -+ while (_fieldList.size() > _fields) -+ { - _fieldList.remove(_fields); -- -- StringBuilder unquoted=null; -- -+ } -+ -+ StringBuilder unquoted = null; -+ - // For each cookie field - for (String hdr : _fieldList) - { -@@ -132,25 +133,31 @@ public class CookieCutter - - Cookie cookie = null; - -- boolean invalue=false; -- boolean inQuoted=false; -- boolean quoted=false; -- boolean escaped=false; -- int tokenstart=-1; -- int tokenend=-1; -+ boolean invalue = false; -+ boolean inQuoted = false; -+ boolean quoted = false; -+ boolean escaped = false; -+ boolean reject = false; -+ int tokenstart = -1; -+ int tokenend = -1; - for (int i = 0, length = hdr.length(); i <= length; i++) - { -- char c = i==length?0:hdr.charAt(i); -- -- // System.err.printf("i=%d/%d c=%s v=%b q=%b/%b e=%b u=%s s=%d e=%d \t%s=%s%n" ,i,length,c==0?"|":(""+c),invalue,inQuoted,quoted,escaped,unquoted,tokenstart,tokenend,name,value); -- -+ char c = i == length ? 0 : hdr.charAt(i); -+ - // Handle quoted values for name or value - if (inQuoted) - { -+ boolean eol = c == 0 && i == hdr.length(); -+ if (!eol && _compliance != CookieCompliance.RFC2965 && isRFC6265RejectedCharacter(inQuoted, c)) -+ { -+ reject = true; -+ continue; -+ } -+ - if (escaped) - { -- escaped=false; -- if (c>0) -+ escaped = false; -+ if (c > 0) - unquoted.append(c); - else - { -@@ -160,7 +167,7 @@ public class CookieCutter - } - continue; - } -- -+ - switch (c) - { - case '"': -@@ -175,15 +182,24 @@ public class CookieCutter - continue; - - case 0: -- // unterminated quote, let's ignore quotes -+ // unterminated quote -+ if (_compliance == CookieCompliance.RFC6265) -+ continue; -+ // let's ignore quotes - unquoted.setLength(0); - inQuoted = false; - i--; - continue; -- -+ -+ case ';': -+ if (_compliance == CookieCompliance.RFC6265) -+ reject = true; -+ else -+ unquoted.append(c); -+ continue; -+ - default: - unquoted.append(c); -- continue; - } - } - else -@@ -191,7 +207,14 @@ public class CookieCutter - // Handle name and value state machines - if (invalue) - { -- // parse the value -+ boolean eol = c == 0 && i == hdr.length(); -+ if (!eol && _compliance == CookieCompliance.RFC6265 && isRFC6265RejectedCharacter(inQuoted, c)) -+ { -+ reject = true; -+ continue; -+ } -+ -+ // parse the cookie-value - switch (c) - { - case ' ': -@@ -199,19 +222,19 @@ public class CookieCutter - break; - - case ',': -- if (_compliance!=CookieCompliance.RFC2965) -+ if (_compliance != CookieCompliance.RFC2965) - { - if (quoted) - { - // must have been a bad internal quote. let's fix as best we can -- unquoted.append(hdr,tokenstart,i--); -+ unquoted.append(hdr, tokenstart, i--); - inQuoted = true; - quoted = false; - continue; - } -- if (tokenstart<0) -+ if (tokenstart < 0) - tokenstart = i; -- tokenend=i; -+ tokenend = i; - continue; - } - // fall through -@@ -226,8 +249,8 @@ public class CookieCutter - unquoted.setLength(0); - quoted = false; - } -- else if(tokenstart>=0) -- value = tokenend>=tokenstart?hdr.substring(tokenstart, tokenend+1):hdr.substring(tokenstart); -+ else if (tokenstart >= 0) -+ value = tokenend >= tokenstart ? hdr.substring(tokenstart, tokenend + 1) : hdr.substring(tokenstart); - else - value = ""; - -@@ -235,22 +258,22 @@ public class CookieCutter - { - if (name.startsWith("$")) - { -- if (_compliance==CookieCompliance.RFC2965) -+ if (_compliance == CookieCompliance.RFC2965) - { - String lowercaseName = name.toLowerCase(Locale.ENGLISH); -- switch(lowercaseName) -+ switch (lowercaseName) - { - case "$path": -- if (cookie!=null) -+ if (cookie != null) - cookie.setPath(value); - break; - case "$domain": -- if (cookie!=null) -+ if (cookie != null) - cookie.setDomain(value); - break; - case "$port": -- if (cookie!=null) -- cookie.setComment("$port="+value); -+ if (cookie != null) -+ cookie.setComment("$port=" + value); - break; - case "$version": - version = Integer.parseInt(value); -@@ -265,7 +288,10 @@ public class CookieCutter - cookie = new Cookie(name, value); - if (version > 0) - cookie.setVersion(version); -- cookies.add(cookie); -+ if (!reject) -+ { -+ cookies.add(cookie); -+ } - } - } - catch (Exception e) -@@ -275,46 +301,68 @@ public class CookieCutter - - name = null; - tokenstart = -1; -- invalue=false; -+ invalue = false; -+ reject = false; - - break; - } - - case '"': -- if (tokenstart<0) -+ if (tokenstart < 0) - { -- tokenstart=i; -- inQuoted=true; -- if (unquoted==null) -- unquoted=new StringBuilder(); -+ tokenstart = i; -+ inQuoted = true; -+ if (unquoted == null) -+ unquoted = new StringBuilder(); - break; - } -+ else if (_compliance == CookieCompliance.RFC6265) -+ { -+ reject = true; -+ continue; -+ } - // fall through to default case - - default: -+ if (_compliance == CookieCompliance.RFC6265 && quoted) -+ { -+ reject = true; -+ continue; -+ } -+ - if (quoted) - { - // must have been a bad internal quote. let's fix as best we can -- unquoted.append(hdr,tokenstart,i--); -+ unquoted.append(hdr, tokenstart, i--); - inQuoted = true; - quoted = false; - continue; - } -- if (tokenstart<0) -+ -+ if (_compliance == CookieCompliance.RFC6265_LEGACY && isRFC6265RejectedCharacter(inQuoted, c)) -+ reject = true; -+ -+ if (tokenstart < 0) - tokenstart = i; -- tokenend=i; -- continue; -+ tokenend = i; - } - } - else - { -- // parse the name -+ // parse the cookie-name - switch (c) - { - case ' ': - case '\t': - continue; - -+ case ';': -+ // a cookie terminated with no '=' sign. -+ tokenstart = -1; -+ invalue = false; -+ reject = false; -+ continue; -+ - case '=': - if (quoted) - { -@@ -322,8 +370,8 @@ public class CookieCutter - unquoted.setLength(0); - quoted = false; - } -- else if(tokenstart>=0) -- name = tokenend>=tokenstart?hdr.substring(tokenstart, tokenend+1):hdr.substring(tokenstart); -+ else if (tokenstart >= 0) -+ name = tokenend >= tokenstart ? hdr.substring(tokenstart, tokenend + 1) : hdr.substring(tokenstart); - - tokenstart = -1; - invalue = true; -@@ -333,14 +381,18 @@ public class CookieCutter - if (quoted) - { - // must have been a bad internal quote. let's fix as best we can -- unquoted.append(hdr,tokenstart,i--); -+ unquoted.append(hdr, tokenstart, i--); - inQuoted = true; - quoted = false; - continue; - } -- if (tokenstart<0) -- tokenstart=i; -- tokenend=i; -+ -+ if (_compliance != CookieCompliance.RFC2965 && isRFC6265RejectedCharacter(inQuoted, c)) -+ reject = true; -+ -+ if (tokenstart < 0) -+ tokenstart = i; -+ tokenend = i; - continue; - } - } -@@ -348,8 +400,45 @@ public class CookieCutter - } - } - -- _cookies = (Cookie[]) cookies.toArray(new Cookie[cookies.size()]); -- _lastCookies=_cookies; -+ _cookies = cookies.toArray(new Cookie[0]); -+ _lastCookies = _cookies; -+ } -+ -+ -+ protected boolean isRFC6265RejectedCharacter(boolean inQuoted, char c) -+ { -+ // LEGACY test -+ if (_compliance == CookieCompliance.RFC6265_LEGACY) -+ { -+ if (inQuoted) -+ { -+ // We only reject if a Control Character is encountered -+ if (Character.isISOControl(c)) -+ return true; -+ } -+ else -+ { -+ return Character.isISOControl(c) || // control characters -+ c > 127 || // 8-bit characters -+ c == ',' || // comma -+ c == ';'; // semicolon -+ } -+ return false; -+ } -+ -+ /* From RFC6265 - Section 4.1.1 - Syntax -+ * cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E -+ * ; US-ASCII characters excluding CTLs, -+ * ; whitespace DQUOTE, comma, semicolon, -+ * ; and backslash -+ * -+ * Note: DQUOTE and semicolon are used as separator by the parser, -+ * so we can consider them authorized. -+ */ -+ return c > 127 || // 8-bit characters -+ Character.isISOControl(c) || // control characters -+ c == ',' || // comma -+ c == '\\'; // backslash - } - - } -diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/CookieCutterTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/CookieCutterTest.java -index ec534a1..3e84ce6 100644 ---- a/jetty-server/src/test/java/org/eclipse/jetty/server/CookieCutterTest.java -+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/CookieCutterTest.java -@@ -1,6 +1,6 @@ - // - // ======================================================================== --// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. -+// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. - // ------------------------------------------------------------------------ - // All rights reserved. This program and the accompanying materials - // are made available under the terms of the Eclipse Public License v1.0 -@@ -18,18 +18,21 @@ - - package org.eclipse.jetty.server; - --import static org.hamcrest.Matchers.is; --import static org.hamcrest.MatcherAssert.assertThat; -- -+import java.util.Arrays; -+import java.util.List; - import javax.servlet.http.Cookie; - - import org.eclipse.jetty.http.CookieCompliance; --import org.junit.jupiter.api.Disabled; - import org.junit.jupiter.api.Test; -+import org.junit.jupiter.params.ParameterizedTest; -+import org.junit.jupiter.params.provider.MethodSource; -+ -+import static org.hamcrest.MatcherAssert.assertThat; -+import static org.hamcrest.Matchers.is; - - public class CookieCutterTest - { -- private Cookie[] parseCookieHeaders(CookieCompliance compliance,String... headers) -+ private Cookie[] parseCookieHeaders(CookieCompliance compliance, String... headers) - { - CookieCutter cutter = new CookieCutter(compliance); - for (String header : headers) -@@ -38,7 +41,7 @@ public class CookieCutterTest - } - return cutter.getCookies(); - } -- -+ - private void assertCookie(String prefix, Cookie cookie, - String expectedName, - String expectedValue, -@@ -50,142 +53,174 @@ public class CookieCutterTest - assertThat(prefix + ".version", cookie.getVersion(), is(expectedVersion)); - assertThat(prefix + ".path", cookie.getPath(), is(expectedPath)); - } -- -+ - /** - * Example from RFC2109 and RFC2965 - */ - @Test -- public void testRFC_Single() -+ public void testRFCSingle() - { - String rawCookie = "$Version=\"1\"; Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\""; -- -- Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC2965,rawCookie); -- -+ -+ Cookie[] cookies = parseCookieHeaders(CookieCompliance.RFC2965, rawCookie); -+ - assertThat("Cookies.length", cookies.length, is(1)); - assertCookie("Cookies[0]", cookies[0], "Customer", "WILE_E_COYOTE", 1, "/acme"); - } -- -+ -+ /** -+ * Example from RFC2109 and RFC2965. -+ *

-+ * Lenient parsing, input has no spaces after ';' token. -+ *

-+ */ -+ @Test -+ public void testRFCSingleLenientNoSpaces() -+ { -+ String rawCookie = "$Version=\"1\";Customer=\"WILE_E_COYOTE\";$Path=\"/acme\""; -+ -+ Cookie[] cookies = parseCookieHeaders(CookieCompliance.RFC2965, rawCookie); -+ -+ assertThat("Cookies.length", cookies.length, is(1)); -+ assertCookie("Cookies[0]", cookies[0], "Customer", "WILE_E_COYOTE", 1, "/acme"); -+ } -+ - /** - * Example from RFC2109 and RFC2965 - */ - @Test -- public void testRFC_Double() -+ public void testRFCDouble() - { - String rawCookie = "$Version=\"1\"; " + -- "Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"; " + -- "Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\""; -- -- Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC2965,rawCookie); -- -+ "Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"; " + -+ "Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\""; -+ -+ Cookie[] cookies = parseCookieHeaders(CookieCompliance.RFC2965, rawCookie); -+ - assertThat("Cookies.length", cookies.length, is(2)); - assertCookie("Cookies[0]", cookies[0], "Customer", "WILE_E_COYOTE", 1, "/acme"); - assertCookie("Cookies[1]", cookies[1], "Part_Number", "Rocket_Launcher_0001", 1, "/acme"); - } -- -+ - /** - * Example from RFC2109 and RFC2965 - */ - @Test -- public void testRFC_Triple() -+ public void testRFCTriple() - { - String rawCookie = "$Version=\"1\"; " + -- "Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"; " + -- "Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\"; " + -- "Shipping=\"FedEx\"; $Path=\"/acme\""; -- -- Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC2965,rawCookie); -- -+ "Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"; " + -+ "Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\"; " + -+ "Shipping=\"FedEx\"; $Path=\"/acme\""; -+ -+ Cookie[] cookies = parseCookieHeaders(CookieCompliance.RFC2965, rawCookie); -+ - assertThat("Cookies.length", cookies.length, is(3)); - assertCookie("Cookies[0]", cookies[0], "Customer", "WILE_E_COYOTE", 1, "/acme"); - assertCookie("Cookies[1]", cookies[1], "Part_Number", "Rocket_Launcher_0001", 1, "/acme"); - assertCookie("Cookies[2]", cookies[2], "Shipping", "FedEx", 1, "/acme"); - } -- -+ - /** - * Example from RFC2109 and RFC2965 - */ - @Test -- public void testRFC_PathExample() -+ public void testRFCPathExample() - { - String rawCookie = "$Version=\"1\"; " + -- "Part_Number=\"Riding_Rocket_0023\"; $Path=\"/acme/ammo\"; " + -- "Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\""; -- -- Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC2965,rawCookie); -- -+ "Part_Number=\"Riding_Rocket_0023\"; $Path=\"/acme/ammo\"; " + -+ "Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\""; -+ -+ Cookie[] cookies = parseCookieHeaders(CookieCompliance.RFC2965, rawCookie); -+ - assertThat("Cookies.length", cookies.length, is(2)); - assertCookie("Cookies[0]", cookies[0], "Part_Number", "Riding_Rocket_0023", 1, "/acme/ammo"); - assertCookie("Cookies[1]", cookies[1], "Part_Number", "Rocket_Launcher_0001", 1, "/acme"); - } -- -+ - /** - * Example from RFC2109 - */ - @Test -- public void testRFC2109_CookieSpoofingExample() -+ public void testRFC2109CookieSpoofingExample() - { - String rawCookie = "$Version=\"1\"; " + -- "session_id=\"1234\"; " + -- "session_id=\"1111\"; $Domain=\".cracker.edu\""; -- -- Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC2965,rawCookie); -- -+ "session_id=\"1234\"; " + -+ "session_id=\"1111\"; $Domain=\".cracker.edu\""; -+ -+ Cookie[] cookies = parseCookieHeaders(CookieCompliance.RFC2965, rawCookie); -+ - assertThat("Cookies.length", cookies.length, is(2)); - assertCookie("Cookies[0]", cookies[0], "session_id", "1234", 1, null); - assertCookie("Cookies[1]", cookies[1], "session_id", "1111", 1, null); - } -- -+ - /** - * Example from RFC2965 - */ - @Test -- public void testRFC2965_CookieSpoofingExample() -+ public void testRFC2965CookieSpoofingExample() - { - String rawCookie = "$Version=\"1\"; session_id=\"1234\", " + -- "$Version=\"1\"; session_id=\"1111\"; $Domain=\".cracker.edu\""; -- -- -- Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC2965,rawCookie); -+ "$Version=\"1\"; session_id=\"1111\"; $Domain=\".cracker.edu\""; - -+ Cookie[] cookies = parseCookieHeaders(CookieCompliance.RFC2965, rawCookie); - assertThat("Cookies.length", cookies.length, is(2)); - assertCookie("Cookies[0]", cookies[0], "session_id", "1234", 1, null); - assertCookie("Cookies[1]", cookies[1], "session_id", "1111", 1, null); - -- cookies = parseCookieHeaders(CookieCompliance.RFC6265,rawCookie); -- assertThat("Cookies.length", cookies.length, is(2)); -- assertCookie("Cookies[0]", cookies[0], "session_id", "1234\", $Version=\"1", 0, null); -- assertCookie("Cookies[1]", cookies[1], "session_id", "1111", 0, null); -+ cookies = parseCookieHeaders(CookieCompliance.RFC6265, rawCookie); -+ assertThat("Cookies.length", cookies.length, is(1)); -+ assertCookie("Cookies[0]", cookies[0], "session_id", "1111", 0, null); - } -- -+ - /** - * Example from RFC6265 - */ - @Test -- public void testRFC6265_SidExample() -+ public void testRFC6265SidExample() - { - String rawCookie = "SID=31d4d96e407aad42"; -- -- Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC6265,rawCookie); -- -+ -+ Cookie[] cookies = parseCookieHeaders(CookieCompliance.RFC6265, rawCookie); -+ - assertThat("Cookies.length", cookies.length, is(1)); - assertCookie("Cookies[0]", cookies[0], "SID", "31d4d96e407aad42", 0, null); - } -- -+ - /** - * Example from RFC6265 - */ - @Test -- public void testRFC6265_SidLangExample() -+ public void testRFC6265SidLangExample() - { - String rawCookie = "SID=31d4d96e407aad42; lang=en-US"; -- -- Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC6265,rawCookie); -- -+ -+ Cookie[] cookies = parseCookieHeaders(CookieCompliance.RFC6265, rawCookie); -+ -+ assertThat("Cookies.length", cookies.length, is(2)); -+ assertCookie("Cookies[0]", cookies[0], "SID", "31d4d96e407aad42", 0, null); -+ assertCookie("Cookies[1]", cookies[1], "lang", "en-US", 0, null); -+ } -+ -+ /** -+ * Example from RFC6265. -+ *

-+ * Lenient parsing, input has no spaces after ';' token. -+ *

-+ */ -+ @Test -+ public void testRFC6265SidLangExampleLenient() -+ { -+ String rawCookie = "SID=31d4d96e407aad42;lang=en-US"; -+ -+ Cookie[] cookies = parseCookieHeaders(CookieCompliance.RFC6265, rawCookie); -+ - assertThat("Cookies.length", cookies.length, is(2)); - assertCookie("Cookies[0]", cookies[0], "SID", "31d4d96e407aad42", 0, null); - assertCookie("Cookies[1]", cookies[1], "lang", "en-US", 0, null); - } -- -+ - /** - * Basic name=value, following RFC6265 rules - */ -@@ -193,13 +228,13 @@ public class CookieCutterTest - public void testKeyValue() - { - String rawCookie = "key=value"; -- -- Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC6265,rawCookie); -- -+ -+ Cookie[] cookies = parseCookieHeaders(CookieCompliance.RFC6265, rawCookie); -+ - assertThat("Cookies.length", cookies.length, is(1)); - assertCookie("Cookies[0]", cookies[0], "key", "value", 0, null); - } -- -+ - /** - * Basic name=value, following RFC6265 rules - */ -@@ -207,9 +242,116 @@ public class CookieCutterTest - public void testDollarName() - { - String rawCookie = "$key=value"; -- -- Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC6265,rawCookie); -- -+ -+ Cookie[] cookies = parseCookieHeaders(CookieCompliance.RFC6265, rawCookie); -+ - assertThat("Cookies.length", cookies.length, is(0)); - } -+ -+ @Test -+ public void testMultipleCookies() -+ { -+ String rawCookie = "testcookie; server.id=abcd; server.detail=cfg"; -+ -+ // The first cookie "testcookie" should be ignored, per RFC6265, as it's missing the "=" sign. -+ -+ Cookie[] cookies = parseCookieHeaders(CookieCompliance.RFC6265, rawCookie); -+ -+ assertThat("Cookies.length", cookies.length, is(2)); -+ assertCookie("Cookies[0]", cookies[0], "server.id", "abcd", 0, null); -+ assertCookie("Cookies[1]", cookies[1], "server.detail", "cfg", 0, null); -+ } -+ -+ @Test -+ public void testExcessiveSemicolons() -+ { -+ char[] excessive = new char[65535]; -+ Arrays.fill(excessive, ';'); -+ String rawCookie = "foo=bar; " + new String(excessive) + "; xyz=pdq"; -+ -+ Cookie[] cookies = parseCookieHeaders(CookieCompliance.RFC6265, rawCookie); -+ -+ assertThat("Cookies.length", cookies.length, is(2)); -+ assertCookie("Cookies[0]", cookies[0], "foo", "bar", 0, null); -+ assertCookie("Cookies[1]", cookies[1], "xyz", "pdq", 0, null); -+ } -+ -+ @ParameterizedTest -+ @MethodSource("rfc6265Cookies") -+ public void testRFC6265CookieParsing(Param param) -+ { -+ Cookie[] cookies = parseCookieHeaders(CookieCompliance.RFC6265, param.input); -+ -+ assertThat("Cookies.length (" + dump(cookies) + ")", cookies.length, is(param.expected.size())); -+ for (int i = 0; i < cookies.length; i++) -+ { -+ Cookie cookie = cookies[i]; -+ assertThat("Cookies[" + i + "] (" + dump(cookies) + ")", cookie.getName() + "=" + cookie.getValue(), is(param.expected.get(i))); -+ } -+ } -+ -+ public static List rfc6265Cookies() -+ { -+ return Arrays.asList( -+ new Param("A=1; B=2; C=3", "A=1", "B=2", "C=3"), -+ new Param("A=\"1\"; B=2; C=3", "A=1", "B=2", "C=3"), -+ new Param("A=\"1\"; B=\"2\"; C=\"3\"", "A=1", "B=2", "C=3"), -+ new Param("A=1; B=2; C=\"3", "A=1", "B=2"), -+ new Param("A=1 ; B=2; C=3", "A=1", "B=2", "C=3"), -+ new Param("A= 1; B=2; C=3", "A=1", "B=2", "C=3"), -+ new Param("A=\"1; B=2\"; C=3", "C=3"), -+ new Param("A=\"1; B=2; C=3"), -+ new Param("A=\"1 B=2\"; C=3", "A=1 B=2", "C=3"), -+ new Param("A=\"\"1; B=2; C=3", "B=2", "C=3"), -+ new Param("A=\"\" ; B=2; C=3", "A=", "B=2", "C=3"), -+ new Param("A=\"\"; B=2; C=3", "A=", "B=2", "C=3"), -+ new Param("A=1\"\"; B=2; C=3", "B=2", "C=3"), -+ new Param("A=1\"; B=2; C=3", "B=2", "C=3"), -+ new Param("A=1\"1; B=2; C=3", "B=2", "C=3"), -+ new Param("A=\" 1\"; B=2; C=3", "A= 1", "B=2", "C=3"), -+ new Param("A=\"1 \"; B=2; C=3", "A=1 ", "B=2", "C=3"), -+ new Param("A=\" 1 \"; B=2; C=3", "A= 1 ", "B=2", "C=3"), -+ new Param("A=\" 1 1 \"; B=2; C=3", "A= 1 1 ", "B=2", "C=3"), -+ new Param("A=1,; B=2; C=3", "B=2", "C=3"), -+ new Param("A=\"1,\"; B=2; C=3", "B=2", "C=3"), -+ new Param("A=\\1; B=2; C=3", "B=2", "C=3"), -+ new Param("A=\"\\1\"; B=2; C=3", "B=2", "C=3"), -+ new Param("A=1\u0007; B=2; C=3", "B=2", "C=3"), -+ new Param("A=\"1\u0007\"; B=2; C=3", "B=2", "C=3"), -+ new Param("€"), -+ new Param("@={}"), -+ new Param("$X=Y; N=V", "N=V"), -+ new Param("N=V; $X=Y", "N=V") -+ ); -+ } -+ -+ private static String dump(Cookie[] cookies) -+ { -+ StringBuilder sb = new StringBuilder(); -+ for (Cookie cookie : cookies) -+ { -+ sb.append("<").append(cookie.getName()).append(">=<").append(cookie.getValue()).append("> | "); -+ } -+ if (sb.length() > 0) -+ sb.delete(sb.length() - 2, sb.length() - 1); -+ return sb.toString(); -+ } -+ -+ private static class Param -+ { -+ private final String input; -+ private final List expected; -+ -+ public Param(String input, String... expected) -+ { -+ this.input = input; -+ this.expected = Arrays.asList(expected); -+ } -+ -+ @Override -+ public String toString() -+ { -+ return input + " -> " + expected.toString(); -+ } -+ } - } -diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java -index 425a9ae..f119864 100644 ---- a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java -+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java -@@ -61,6 +61,7 @@ import javax.servlet.http.HttpServletResponse; - import javax.servlet.http.Part; - - import org.eclipse.jetty.http.BadMessageException; -+import org.eclipse.jetty.http.CookieCompliance; - import org.eclipse.jetty.http.HttpCompliance; - import org.eclipse.jetty.http.HttpTester; - import org.eclipse.jetty.http.MimeTypes; -@@ -97,6 +98,7 @@ public class RequestTest - http.getHttpConfiguration().setRequestHeaderSize(512); - http.getHttpConfiguration().setResponseHeaderSize(512); - http.getHttpConfiguration().setOutputBufferSize(2048); -+ http.getHttpConfiguration().setRequestCookieCompliance(CookieCompliance.RFC6265_LEGACY); - http.getHttpConfiguration().addCustomizer(new ForwardedRequestCustomizer()); - _connector = new LocalConnector(_server,http); - _server.addConnector(_connector); diff --git a/CVE-2023-36479.patch b/CVE-2023-36479.patch deleted file mode 100644 index 898be762e7dc179f95f7a9e48bfa7633918503ca..0000000000000000000000000000000000000000 --- a/CVE-2023-36479.patch +++ /dev/null @@ -1,50 +0,0 @@ -From: Markus Koschany -Date: Wed, 27 Sep 2023 14:25:09 +0200 -Subject: CVE-2023-36479 - -The org.eclipse.jetty.servlets.CGI Servlet should not be used anymore. -Upstream recommends to use Fast CGI instead. - -Origin: https://github.com/eclipse/jetty.project/pull/9888 ---- - .../src/main/java/org/eclipse/jetty/servlets/CGI.java | 3 +++ - .../test-jetty-webapp/src/main/webapp/WEB-INF/web.xml | 11 ----------- - 2 files changed, 3 insertions(+), 11 deletions(-) - -diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java -index 6322290..55d8f9a 100644 ---- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java -+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java -@@ -67,7 +67,10 @@ import org.eclipse.jetty.util.log.Logger; - *
ignoreExitState
- *
If true then do not act on a non-zero exec exit status")
- * -+ * -+ * @deprecated do not use, no replacement, will be removed in a future release. - */ -+@Deprecated - public class CGI extends HttpServlet - { - private static final long serialVersionUID = -6182088932884791074L; -diff --git a/tests/test-webapps/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml b/tests/test-webapps/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml -index 507771f..978595f 100644 ---- a/tests/test-webapps/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml -+++ b/tests/test-webapps/test-jetty-webapp/src/main/webapp/WEB-INF/web.xml -@@ -121,17 +121,6 @@ - /dispatch/* - - -- -- CGI -- org.eclipse.jetty.servlets.CGI -- 1 -- -- -- -- CGI -- /cgi-bin/* -- -- - - Chat - com.acme.ChatServlet diff --git a/CVE-2023-40167.patch b/CVE-2023-40167.patch deleted file mode 100644 index 9bc23da3a9139ec737071c93b885caf64606c6d0..0000000000000000000000000000000000000000 --- a/CVE-2023-40167.patch +++ /dev/null @@ -1,256 +0,0 @@ -From: Markus Koschany -Date: Tue, 26 Sep 2023 21:06:42 +0200 -Subject: CVE-2023-40167 - -Origin: https://github.com/eclipse/jetty.project/commit/e4d596eafc887bcd813ae6e28295b5ce327def47 ---- - .../java/org/eclipse/jetty/http/HttpParser.java | 47 +++++++------- - .../org/eclipse/jetty/http/HttpParserTest.java | 71 +++++----------------- - 2 files changed, 38 insertions(+), 80 deletions(-) - -diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java -index 2abc4b6..c045498 100644 ---- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java -+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java -@@ -501,7 +501,7 @@ public class HttpParser - /* Quick lookahead for the start state looking for a request method or a HTTP version, - * otherwise skip white space until something else to parse. - */ -- private boolean quickStart(ByteBuffer buffer) -+ private void quickStart(ByteBuffer buffer) - { - if (_requestHandler!=null) - { -@@ -512,7 +512,7 @@ public class HttpParser - buffer.position(buffer.position()+_methodString.length()+1); - - setState(State.SPACE1); -- return false; -+ return; - } - } - else if (_responseHandler!=null) -@@ -522,7 +522,7 @@ public class HttpParser - { - buffer.position(buffer.position()+_version.asString().length()+1); - setState(State.SPACE1); -- return false; -+ return; - } - } - -@@ -543,7 +543,7 @@ public class HttpParser - _string.setLength(0); - _string.append(t.getChar()); - setState(_requestHandler!=null?State.METHOD:State.RESPONSE_VERSION); -- return false; -+ return; - } - case OTEXT: - case SPACE: -@@ -561,7 +561,6 @@ public class HttpParser - throw new BadMessageException(HttpStatus.BAD_REQUEST_400); - } - } -- return false; - } - - /* ------------------------------------------------------------------------------- */ -@@ -979,12 +978,13 @@ public class HttpParser - switch (_header) - { - case CONTENT_LENGTH: -+ long contentLength = convertContentLength(_valueString); - if (_hasContentLength) - { - if(complianceViolation(MULTIPLE_CONTENT_LENGTHS)) - throw new BadMessageException(HttpStatus.BAD_REQUEST_400,MULTIPLE_CONTENT_LENGTHS.description); -- if (convertContentLength(_valueString)!=_contentLength) -- throw new BadMessageException(HttpStatus.BAD_REQUEST_400,MULTIPLE_CONTENT_LENGTHS.description); -+ if (contentLength != _contentLength) -+ throw new BadMessageException(HttpStatus.BAD_REQUEST_400, MULTIPLE_CONTENT_LENGTHS.getDescription()); - } - _hasContentLength = true; - -@@ -993,11 +993,8 @@ public class HttpParser - - if (_endOfContent != EndOfContent.CHUNKED_CONTENT) - { -- _contentLength=convertContentLength(_valueString); -- if (_contentLength <= 0) -- _endOfContent=EndOfContent.NO_CONTENT; -- else -- _endOfContent=EndOfContent.CONTENT_LENGTH; -+ _contentLength = contentLength; -+ _endOfContent = EndOfContent.CONTENT_LENGTH; - } - break; - -@@ -1085,15 +1082,21 @@ public class HttpParser - - private long convertContentLength(String valueString) - { -- try -- { -- return Long.parseLong(valueString); -- } -- catch(NumberFormatException e) -+ if (valueString == null || valueString.length() == 0) -+ throw new BadMessageException("Invalid Content-Length Value", new NumberFormatException()); -+ -+ long value = 0; -+ int length = valueString.length(); -+ -+ for (int i = 0; i < length; i++) - { -- LOG.ignore(e); -- throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Invalid Content-Length Value",e); -+ char c = valueString.charAt(i); -+ if (c < '0' || c > '9') -+ throw new BadMessageException("Invalid Content-Length Value", new NumberFormatException()); -+ -+ value = Math.addExact(Math.multiplyExact(value, 10L), c - '0'); - } -+ return value; - } - - /* ------------------------------------------------------------------------------- */ -@@ -1485,12 +1488,11 @@ public class HttpParser - _methodString=null; - _endOfContent=EndOfContent.UNKNOWN_CONTENT; - _header=null; -- if (quickStart(buffer)) -- return true; -+ quickStart(buffer); - } - - // Request/response line -- if (_state.ordinal()>= State.START.ordinal() && _state.ordinal() _fields = new ArrayList<>(); -- private List _trailers = new ArrayList<>(); -+ private final List _fields = new ArrayList<>(); -+ private final List _trailers = new ArrayList<>(); - private String[] _hdr; - private String[] _val; - private int _headers; diff --git a/CVE-2024-6762.patch b/CVE-2024-6762.patch deleted file mode 100644 index 3bae7196e67be30c1999b4f3c359ae35d735693a..0000000000000000000000000000000000000000 --- a/CVE-2024-6762.patch +++ /dev/null @@ -1,60 +0,0 @@ -From c9fb33ab85959921ff3183311587af02772dda89 Mon Sep 17 00:00:00 2001 -From: Lachlan Roberts -Date: Mon, 1 May 2023 14:40:35 +1000 -Subject: [PATCH 1/2] deprecate PushSessionCacheFilter - -Signed-off-by: Lachlan Roberts ---- - .../java/org/eclipse/jetty/servlets/PushSessionCacheFilter.java | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushSessionCacheFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushSessionCacheFilter.java -index 4fa0ea1028cb..9950dce98bda 100644 ---- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushSessionCacheFilter.java -+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushSessionCacheFilter.java -@@ -41,6 +41,7 @@ - import org.eclipse.jetty.util.log.Log; - import org.eclipse.jetty.util.log.Logger; - -+@Deprecated - public class PushSessionCacheFilter implements Filter - { - private static final String TARGET_ATTR = "PushCacheFilter.target"; - -From 2588cedddca989b6b96e6954ae6e8fc8f3e1c487 Mon Sep 17 00:00:00 2001 -From: Lachlan Roberts -Date: Tue, 2 May 2023 12:02:12 +1000 -Subject: [PATCH 2/2] update javadoc and add log warning message for - PushSessionCacheFilter - -Signed-off-by: Lachlan Roberts ---- - .../eclipse/jetty/servlets/PushSessionCacheFilter.java | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushSessionCacheFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushSessionCacheFilter.java -index 9950dce98bda..81b85cb2b85e 100644 ---- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushSessionCacheFilter.java -+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushSessionCacheFilter.java -@@ -41,6 +41,9 @@ - import org.eclipse.jetty.util.log.Log; - import org.eclipse.jetty.util.log.Logger; - -+/** -+ * @deprecated no replacement for this deprecated http feature -+ */ - @Deprecated - public class PushSessionCacheFilter implements Filter - { -@@ -50,6 +53,11 @@ public class PushSessionCacheFilter implements Filter - private final ConcurrentMap _cache = new ConcurrentHashMap<>(); - private long _associateDelay = 5000L; - -+ public PushSessionCacheFilter() -+ { -+ LOG.warn(PushSessionCacheFilter.class.getSimpleName() + " is an example class not suitable for production."); -+ } -+ - @Override - public void init(FilterConfig config) throws ServletException - { diff --git a/README.en.md b/README.en.md deleted file mode 100644 index a53c4f009bc5192b462f7390044c75c8050ee96e..0000000000000000000000000000000000000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# jetty - -#### Description -{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md deleted file mode 100644 index 55676973371fdb93abc782b06dd70d5cf8c858b6..0000000000000000000000000000000000000000 --- a/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# jetty - -#### 介绍 -{**以下是码云平台说明,您可以替换此简介** -码云是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 -无论是个人、团队、或是企业,都能够用码云实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} - -#### 软件架构 -软件架构说明 - - -#### 安装教程 - -1. xxxx -2. xxxx -3. xxxx - -#### 使用说明 - -1. xxxx -2. xxxx -3. xxxx - -#### 参与贡献 - -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request - - -#### 码云特技 - -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目 -5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/jetty-9.4.16.v20190411.tar.gz b/jetty-9.4.40.v20210413.tar.gz similarity index 68% rename from jetty-9.4.16.v20190411.tar.gz rename to jetty-9.4.40.v20210413.tar.gz index 0e76c17d4f0a6952b3fa1360e1483f3f8fee9925..6d63985260de2624dd43d02c09e52a3bdb59f4a8 100644 Binary files a/jetty-9.4.16.v20190411.tar.gz and b/jetty-9.4.40.v20210413.tar.gz differ diff --git a/jetty.spec b/jetty.spec index e54216488373c3e47aea187ad3dbb02ebd802be7..5d45f8f0def6592fab568d56baec6cc5cb14f7d7 100644 --- a/jetty.spec +++ b/jetty.spec @@ -1,3 +1,33 @@ +# Copyright (c) 2000-2007, JPackage Project +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the +# distribution. +# 3. Neither the name of the JPackage Project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + %global jtuid 110 %global username %{name} %global confdir %{_sysconfdir}/%{name} @@ -8,146 +38,219 @@ %global rundir %{_localstatedir}/run/%{name} %global jettylibdir %{_localstatedir}/lib/%{name} %global appdir %{jettylibdir}/webapps -%global addver .v20190411 -%bcond_with jp_minimal -Name: jetty -Version: 9.4.16 -Release: 8 -Summary: Java Webserver and Servlet Container -License: Apache-2.0 OR EPL-1.0 -URL: http://www.eclipse.org/jetty/ -Source0: https://github.com/eclipse/%{name}.project/archive/%{name}-%{version}%{addver}.tar.gz -Source1: jetty.sh -Source3: jetty.logrotate -Source5: %{name}.service -Source6: LICENSE-MIT -Patch0: CVE-2020-27216.patch -Patch1: CVE-2020-27223.patch -Patch2: CVE-2021-28165.patch -Patch3: CVE-2021-28169.patch -Patch4: CVE-2021-34428.patch -Patch5: CVE-2022-2047.patch -Patch6: CVE-2022-2048.patch -Patch7: CVE-2023-26048.patch -Patch8: CVE-2023-26049.patch -Patch9: CVE-2023-36479.patch -Patch10: CVE-2023-40167.patch -Patch11: CVE-2024-6762.patch - -BuildRequires: maven-local mvn(javax.servlet:javax.servlet-api) < 4.0.0 -BuildRequires: mvn(org.apache.felix:maven-bundle-plugin) -BuildRequires: mvn(org.apache.maven.plugins:maven-shade-plugin) -BuildRequires: mvn(org.codehaus.mojo:build-helper-maven-plugin) mvn(org.slf4j:slf4j-api) + + +%global addver .v20210413 + +# minimal version required to build eclipse and thermostat +# eclipse needs: util, server, http, continuation, io, security, servlet +# thermostat needs: server, jaas, webapp +# above modules need: jmx, xml +%bcond_without jp_minimal + +Name: jetty +Version: 9.4.40 +Release: 13%{?dist} +Summary: Java Webserver and Servlet Container + +# Jetty is dual licensed under both ASL 2.0 and EPL 1.0, see NOTICE.txt +License: ASL 2.0 or EPL-1.0 +URL: http://www.eclipse.org/jetty/ +Source0: https://github.com/eclipse/%{name}.project/archive/%{name}-%{version}%{addver}.tar.gz +Source1: jetty.sh +Source3: jetty.logrotate +Source5: %{name}.service +# MIT license text taken from Utf8Appendable.java +Source6: LICENSE-MIT + +Patch1: 0001-Distro-jetty.home.patch +Patch2: 0002-Port-to-servlet-api-4-5.patch + +BuildRequires: maven-local +BuildRequires: mvn(javax.servlet:javax.servlet-api) +BuildRequires: mvn(org.apache.felix:maven-bundle-plugin) +BuildRequires: mvn(org.apache.maven.plugins:maven-shade-plugin) +BuildRequires: mvn(org.codehaus.mojo:build-helper-maven-plugin) +BuildRequires: mvn(org.slf4j:slf4j-api) + %if %{without jp_minimal} -BuildRequires: maven-local mvn(com.github.jnr:jnr-unixsocket) -BuildRequires: mvn(javax.annotation:javax.annotation-api) mvn(javax.enterprise:cdi-api) -BuildRequires: mvn(javax.servlet:javax.servlet-api) < 4.0.0 -BuildRequires: mvn(javax.servlet.jsp:javax.servlet.jsp-api) mvn(javax.servlet:jstl) -BuildRequires: mvn(javax.transaction:javax.transaction-api) -BuildRequires: mvn(javax.websocket:javax.websocket-api) -BuildRequires: mvn(javax.websocket:javax.websocket-client-api) mvn(org.apache.ant:ant) -BuildRequires: mvn(org.apache.ant:ant-launcher) mvn(org.apache.felix:maven-bundle-plugin) -BuildRequires: mvn(org.apache.maven:maven-artifact) mvn(org.apache.maven:maven-core) -BuildRequires: mvn(org.apache.maven:maven-plugin-api) mvn(org.apache.maven:maven-project) -BuildRequires: mvn(org.apache.maven.plugins:maven-antrun-plugin) -BuildRequires: mvn(org.apache.maven.plugins:maven-assembly-plugin) -BuildRequires: mvn(org.apache.maven.plugins:maven-dependency-plugin) -BuildRequires: mvn(org.apache.maven.plugins:maven-failsafe-plugin) -BuildRequires: mvn(org.apache.maven.plugins:maven-plugin-plugin) -BuildRequires: mvn(org.apache.maven.plugins:maven-remote-resources-plugin) -BuildRequires: mvn(org.apache.maven.plugins:maven-shade-plugin) -BuildRequires: mvn(org.apache.maven.plugins:maven-war-plugin) -BuildRequires: mvn(org.apache.maven.plugin-tools:maven-plugin-annotations) -BuildRequires: mvn(org.apache.maven.plugin-tools:maven-plugin-tools-api) -BuildRequires: mvn(org.apache.maven.shared:maven-artifact-transfer) -BuildRequires: mvn(org.apache.taglibs:taglibs-standard-impl) -BuildRequires: mvn(org.apache.taglibs:taglibs-standard-spec) -BuildRequires: mvn(org.apache.tomcat:tomcat-jasper) -BuildRequires: mvn(org.codehaus.mojo:build-helper-maven-plugin) -BuildRequires: mvn(org.codehaus.mojo:exec-maven-plugin) mvn(org.eclipse.equinox.http:servlet) -BuildRequires: mvn(org.eclipse.jetty.alpn:alpn-api) -BuildRequires: mvn(org.eclipse.jetty.orbit:javax.mail.glassfish) -BuildRequires: mvn(org.eclipse.jetty.orbit:javax.security.auth.message) -BuildRequires: mvn(org.eclipse.jetty.toolchain:jetty-assembly-descriptors) -BuildRequires: mvn(org.eclipse.jetty.toolchain:jetty-schemas) -BuildRequires: mvn(org.eclipse.jetty.toolchain:jetty-test-helper) -BuildRequires: mvn(org.eclipse.osgi:org.eclipse.osgi) -BuildRequires: mvn(org.eclipse.osgi:org.eclipse.osgi.services) -BuildRequires: mvn(org.infinispan:infinispan-core) -BuildRequires: mvn(org.jboss.weld.servlet:weld-servlet-core) -BuildRequires: mvn(org.mongodb:mongo-java-driver) mvn(org.ow2.asm:asm) -BuildRequires: mvn(org.ow2.asm:asm-commons) mvn(org.slf4j:slf4j-api) -BuildRequires: mvn(org.springframework:spring-beans) -BuildRequires: java-1.8.0-openjdk-headless java-1.8.0-openjdk -BuildRequires: mvn(org.eclipse.jetty.toolchain:jetty-artifact-remote-resources) -BuildRequires: mvn(org.eclipse.jetty.toolchain:jetty-distribution-remote-resources) -BuildRequires: mvn(org.eclipse.jetty.toolchain:jetty-test-policy) maven-javadoc-plugin -BuildRequires: glassfish-el systemd junit5 -BuildRequires: jboss-websocket-1.0-api -Requires: jboss-websocket-1.0-api -Requires: java-1.8.0-openjdk-headless -Requires: glassfish-servlet-api < 4.0.0 -%endif # without jp_minimal -BuildArch: noarch +BuildRequires: maven-local +BuildRequires: mvn(com.github.jnr:jnr-unixsocket) +BuildRequires: mvn(javax.annotation:javax.annotation-api) +BuildRequires: mvn(javax.enterprise:cdi-api) +BuildRequires: mvn(javax.servlet:javax.servlet-api) +BuildRequires: mvn(javax.servlet.jsp:javax.servlet.jsp-api) +BuildRequires: mvn(javax.servlet:jstl) +BuildRequires: mvn(javax.transaction:javax.transaction-api) +BuildRequires: mvn(javax.websocket:javax.websocket-api) +BuildRequires: mvn(javax.websocket:javax.websocket-client-api) +BuildRequires: mvn(org.apache.ant:ant) +BuildRequires: mvn(org.apache.ant:ant-launcher) +BuildRequires: mvn(org.apache.felix:maven-bundle-plugin) +BuildRequires: mvn(org.apache.maven:maven-artifact) +BuildRequires: mvn(org.apache.maven:maven-core) +BuildRequires: mvn(org.apache.maven:maven-plugin-api) +BuildRequires: mvn(org.apache.maven:maven-project) +BuildRequires: mvn(org.apache.maven.plugins:maven-antrun-plugin) +BuildRequires: mvn(org.apache.maven.plugins:maven-assembly-plugin) +BuildRequires: mvn(org.apache.maven.plugins:maven-dependency-plugin) +BuildRequires: mvn(org.apache.maven.plugins:maven-failsafe-plugin) +BuildRequires: mvn(org.apache.maven.plugins:maven-plugin-plugin) +BuildRequires: mvn(org.apache.maven.plugins:maven-remote-resources-plugin) +BuildRequires: mvn(org.apache.maven.plugins:maven-shade-plugin) +BuildRequires: mvn(org.apache.maven.plugins:maven-war-plugin) +BuildRequires: mvn(org.apache.maven.plugin-tools:maven-plugin-annotations) +BuildRequires: mvn(org.apache.maven.plugin-tools:maven-plugin-tools-api) +BuildRequires: mvn(org.apache.maven.shared:maven-artifact-transfer) +BuildRequires: mvn(org.apache.taglibs:taglibs-standard-impl) +BuildRequires: mvn(org.apache.taglibs:taglibs-standard-spec) +BuildRequires: mvn(org.apache.tomcat:tomcat-jasper) +BuildRequires: mvn(org.codehaus.mojo:build-helper-maven-plugin) +BuildRequires: mvn(org.codehaus.mojo:exec-maven-plugin) +BuildRequires: mvn(org.eclipse.jetty.alpn:alpn-api) +BuildRequires: mvn(org.eclipse.jetty.orbit:javax.mail.glassfish) +BuildRequires: mvn(org.eclipse.jetty.orbit:javax.security.auth.message) +BuildRequires: mvn(org.eclipse.jetty.toolchain:jetty-assembly-descriptors) +BuildRequires: mvn(org.eclipse.jetty.toolchain:jetty-schemas) +BuildRequires: mvn(org.eclipse.jetty.toolchain:jetty-test-helper) +BuildRequires: mvn(org.jboss.weld.servlet:weld-servlet-core) +BuildRequires: mvn(org.mongodb:mongo-java-driver) +BuildRequires: mvn(org.ow2.asm:asm) +BuildRequires: mvn(org.ow2.asm:asm-commons) +BuildRequires: mvn(org.slf4j:slf4j-api) + +BuildRequires: mvn(org.mortbay.jetty.alpn:alpn-boot) +BuildRequires: mvn(org.eclipse.jetty.toolchain:jetty-artifact-remote-resources) +BuildRequires: mvn(org.eclipse.jetty.toolchain:jetty-distribution-remote-resources) +BuildRequires: mvn(org.eclipse.jetty.toolchain:jetty-test-policy) +#BuildRequires: mvn(org.eclipse.jetty.toolchain.setuid:jetty-setuid-java) +BuildRequires: maven-javadoc-plugin +BuildRequires: glassfish-el +BuildRequires: systemd +BuildRequires: junit5 + +# duplicate providers, choose one +BuildRequires: jboss-websocket-1.0-api +Requires: jboss-websocket-1.0-api +%endif + +BuildArch: noarch +ExclusiveArch: %{java_arches} noarch loongarch64 aarch64 x86_64 + +# jp_minimal doesn't have main package %if %{without jp_minimal} -Requires: javapackages-tools %{name}-annotations = %{version}-%{release} -Requires: %{name}-ant = %{version}-%{release} %{name}-client = %{version}-%{release} -Requires: %{name}-continuation = %{version}-%{release} -Requires: %{name}-deploy = %{version}-%{release} -Requires: %{name}-fcgi-client = %{version}-%{release} -Requires: %{name}-fcgi-server = %{version}-%{release} -Requires: %{name}-http = %{version}-%{release} %{name}-http-spi = %{version}-%{release} -Requires: %{name}-io = %{version}-%{release} %{name}-jaas = %{version}-%{release} -Requires: %{name}-jaspi = %{version}-%{release} %{name}-jmx = %{version}-%{release} -Requires: %{name}-jndi = %{version}-%{release} %{name}-jsp = %{version}-%{release} -Requires: %{name}-jspc-maven-plugin = %{version}-%{release} -Requires: %{name}-maven-plugin = %{version}-%{release} -Requires: %{name}-plus = %{version}-%{release} %{name}-proxy = %{version}-%{release} -Requires: %{name}-rewrite = %{version}-%{release} -Requires: %{name}-security = %{version}-%{release} %{name}-server = %{version}-%{release} -Requires: %{name}-servlet = %{version}-%{release} -Requires: %{name}-servlets = %{version}-%{release} %{name}-spring = %{version}-%{release} -Requires: %{name}-start = %{version}-%{release} -Requires: %{name}-unixsocket = %{version}-%{release} %{name}-util = %{version}-%{release} -Requires: %{name}-util-ajax = %{version}-%{release} -Requires: %{name}-webapp = %{version}-%{release} %{name}-xml = %{version}-%{release} -Requires: %{name}-infinispan = %{version}-%{release} %{name}-cdi = %{version}-%{release} -Requires: %{name}-websocket-api = %{version}-%{release} -Requires: %{name}-websocket-client = %{version}-%{release} -Requires: %{name}-websocket-common = %{version}-%{release} -Requires: %{name}-websocket-server = %{version}-%{release} -Requires: %{name}-websocket-servlet = %{version}-%{release} -Requires: %{name}-javax-websocket-client-impl = %{version}-%{release} -Requires: %{name}-javax-websocket-server-impl = %{version}-%{release} -Requires: %{name}-nosql = %{version}-%{release} -Requires: %{name}-httpservice = %{version}-%{release} -Requires: %{name}-osgi-boot = %{version}-%{release} -Requires: %{name}-osgi-boot-warurl = %{version}-%{release} -Requires: %{name}-osgi-boot-jsp = %{version}-%{release} -Requires: %{name}-osgi-alpn = %{version}-%{release} -Requires: %{name}-quickstart = %{version}-%{release} %{name}-jstl = %{version}-%{release} -Requires: %{name}-alpn-client = %{version}-%{release} -Requires: %{name}-alpn-server = %{version}-%{release} -Requires: %{name}-http2-client = %{version}-%{release} -Requires: %{name}-http2-common = %{version}-%{release} -Requires: %{name}-http2-hpack = %{version}-%{release} -Requires: %{name}-http2-http-client-transport = %{version}-%{release} -Requires: %{name}-http2-server = %{version}-%{release} +# Explicit requires for javapackages-tools since jetty.sh script +# uses /usr/share/java-utils/java-functions +Requires: javapackages-tools +Requires: %{name}-annotations = %{version}-%{release} +Requires: %{name}-ant = %{version}-%{release} +Requires: %{name}-client = %{version}-%{release} +Requires: %{name}-continuation = %{version}-%{release} +Requires: %{name}-deploy = %{version}-%{release} +Requires: %{name}-fcgi-client = %{version}-%{release} +Requires: %{name}-fcgi-server = %{version}-%{release} +Requires: %{name}-http = %{version}-%{release} +Requires: %{name}-http-spi = %{version}-%{release} +Requires: %{name}-io = %{version}-%{release} +Requires: %{name}-jaas = %{version}-%{release} +Requires: %{name}-jaspi = %{version}-%{release} +Requires: %{name}-jmx = %{version}-%{release} +Requires: %{name}-jndi = %{version}-%{release} +Requires: %{name}-jsp = %{version}-%{release} +Requires: %{name}-jspc-maven-plugin = %{version}-%{release} +Requires: %{name}-maven-plugin = %{version}-%{release} +Requires: %{name}-plus = %{version}-%{release} +Requires: %{name}-proxy = %{version}-%{release} +Requires: %{name}-rewrite = %{version}-%{release} +Requires: %{name}-security = %{version}-%{release} +Requires: %{name}-server = %{version}-%{release} +Requires: %{name}-servlet = %{version}-%{release} +Requires: %{name}-servlets = %{version}-%{release} +Requires: %{name}-start = %{version}-%{release} +Requires: %{name}-unixsocket = %{version}-%{release} +Requires: %{name}-util = %{version}-%{release} +Requires: %{name}-util-ajax = %{version}-%{release} +Requires: %{name}-webapp = %{version}-%{release} +Requires: %{name}-xml = %{version}-%{release} +Requires: %{name}-cdi = %{version}-%{release} +Requires: %{name}-websocket-api = %{version}-%{release} +Requires: %{name}-websocket-client = %{version}-%{release} +Requires: %{name}-websocket-common = %{version}-%{release} +Requires: %{name}-websocket-server = %{version}-%{release} +Requires: %{name}-websocket-servlet = %{version}-%{release} +Requires: %{name}-javax-websocket-client-impl = %{version}-%{release} +Requires: %{name}-javax-websocket-server-impl = %{version}-%{release} +Requires: %{name}-nosql = %{version}-%{release} +Requires: %{name}-quickstart = %{version}-%{release} +Requires: %{name}-jstl = %{version}-%{release} +Requires: %{name}-alpn-client = %{version}-%{release} +Requires: %{name}-alpn-server = %{version}-%{release} +Requires: %{name}-http2-client = %{version}-%{release} +Requires: %{name}-http2-common = %{version}-%{release} +Requires: %{name}-http2-hpack = %{version}-%{release} +Requires: %{name}-http2-http-client-transport = %{version}-%{release} +Requires: %{name}-http2-server = %{version}-%{release} + Requires(pre): shadow-utils %{?systemd_ordering} -Provides: group(%username) = %jtuid -Provides: user(%username) = %jtuid -%endif # without jp_minimal -Obsoletes: %{name}-manual < 9.4.0-0.4 -Obsoletes: %{name}-ajp < 9.4.0-0.4 -Obsoletes: %{name}-nested < 9.4.0-0.4 -Obsoletes: %{name}-overlay-deployer < 9.4.0-0.4 -Obsoletes: %{name}-policy < 9.4.0-0.4 -Obsoletes: %{name}-websocket-mux-extension < 9.4.0-0.4 -Obsoletes: %{name}-runner < 9.4.0-0.4 -Obsoletes: %{name}-osgi-npn < 9.4.0-0.4 -Obsoletes: %{name}-monitor < 9.4.0-0.4 -Obsoletes: %{name}-hazelcast < 9.4.14-1 + + +Provides: group(%username) = %jtuid +Provides: user(%username) = %jtuid +%endif + +# Hazelcast in Fedora is too old for jetty to build against (Added in F29) +Obsoletes: %{name}-hazelcast < 9.4.18-1 +# Infinispan in Fedora is too old for jetty to build against (Added in F31) +Obsoletes: %{name}-infinispan < 9.4.18-1 +# Eclipse no longer available (Added in F31) +Obsoletes: %{name}-osgi-alpn < 9.4.18-1 +Obsoletes: %{name}-osgi-boot < 9.4.18-1 +Obsoletes: %{name}-osgi-boot-jsp < 9.4.18-1 +Obsoletes: %{name}-osgi-boot-warurl < 9.4.18-1 +# Spring framework removed from Fedora (Added in F32) +Obsoletes: %{name}-spring < 9.4.24-1 + +%if %{with jp_minimal} +# Remove left-over packages that would have broken deps when built in minimal mode +Obsoletes: %{name}-project < 9.4.20-1 +Obsoletes: %{name}-annotations < 9.4.20-1 +Obsoletes: %{name}-ant < 9.4.20-1 +Obsoletes: %{name}-cdi < 9.4.20-1 +Obsoletes: %{name}-deploy < 9.4.20-1 +Obsoletes: %{name}-fcgi-client < 9.4.20-1 +Obsoletes: %{name}-fcgi-server < 9.4.20-1 +Obsoletes: %{name}-http-spi < 9.4.20-1 +Obsoletes: %{name}-jaspi < 9.4.20-1 +Obsoletes: %{name}-jndi < 9.4.20-1 +Obsoletes: %{name}-jsp < 9.4.20-1 +Obsoletes: %{name}-jstl < 9.4.20-1 +Obsoletes: %{name}-jspc-maven-plugin < 9.4.20-1 +Obsoletes: %{name}-maven-plugin < 9.4.20-1 +Obsoletes: %{name}-plus < 9.4.20-1 +Obsoletes: %{name}-proxy < 9.4.20-1 +Obsoletes: %{name}-quickstart < 9.4.20-1 +Obsoletes: %{name}-rewrite < 9.4.20-1 +Obsoletes: %{name}-servlets < 9.4.20-1 +Obsoletes: %{name}-start < 9.4.20-1 +Obsoletes: %{name}-unixsocket < 9.4.20-1 +Obsoletes: %{name}-websocket-api < 9.4.20-1 +Obsoletes: %{name}-websocket-client < 9.4.20-1 +Obsoletes: %{name}-websocket-common < 9.4.20-1 +Obsoletes: %{name}-websocket-server < 9.4.20-1 +Obsoletes: %{name}-websocket-servlet < 9.4.20-1 +Obsoletes: %{name}-javax-websocket-client-impl < 9.4.20-1 +Obsoletes: %{name}-javax-websocket-server-impl < 9.4.20-1 +Obsoletes: %{name}-alpn-client < 9.4.20-1 +Obsoletes: %{name}-alpn-server < 9.4.20-1 +Obsoletes: %{name}-http2-client < 9.4.20-1 +Obsoletes: %{name}-http2-common < 9.4.20-1 +Obsoletes: %{name}-http2-hpack < 9.4.20-1 +Obsoletes: %{name}-http2-http-client-transport < 9.4.20-1 +Obsoletes: %{name}-http2-server < 9.4.20-1 +Obsoletes: %{name}-nosql < 9.4.20-1 +%endif + %description %global desc \ Jetty is a 100% Java HTTP Server and Servlet Container. This means that you\ @@ -164,317 +267,337 @@ Jetty is available on all Java supported platforms. \ This package contains +# packages in jp_minimal set %package client -Summary: client module for Jetty +Summary: client module for Jetty + %description client %{extdesc} %{summary}. %package continuation -Summary: continuation module for Jetty +Summary: continuation module for Jetty + %description continuation %{extdesc} %{summary}. %package http -Summary: http module for Jetty +Summary: http module for Jetty + %description http %{extdesc} %{summary}. %package http-spi -Summary: http-spi module for Jetty +Summary: http-spi module for Jetty + %description http-spi %{extdesc} %{summary}. %package io -Summary: io module for Jetty -Obsoletes: %{name}-websocket < 9.4.0-0.4 +Summary: io module for Jetty + %description io %{extdesc} %{summary}. %package jaas -Summary: jaas module for Jetty +Summary: jaas module for Jetty + %description jaas %{extdesc} %{summary}. %package jsp -Summary: jsp module for Jetty -Requires: glassfish-el -Requires: glassfish-servlet-api < 4.0.0 +Summary: jsp module for Jetty +Requires: glassfish-el + %description jsp %{extdesc} %{summary}. %package security -Summary: security module for Jetty +Summary: security module for Jetty + %description security %{extdesc} %{summary}. %package server -Summary: server module for Jetty -Requires: glassfish-servlet-api < 4.0.0 +Summary: server module for Jetty + %description server %{extdesc} %{summary}. %package servlet -Summary: servlet module for Jetty +Summary: servlet module for Jetty +# Eclipse no longer available (Added in F31) +Obsoletes: %{name}-httpservice < 9.4.18-1 + %description servlet %{extdesc} %{summary}. %package util -Summary: util module for Jetty -License: (Apache-2.0 OR EPL-1.0) AND MIT +Summary: util module for Jetty +# Utf8Appendable.java is additionally under MIT license +License: (ASL 2.0 or EPL-1.0) and MIT + %description util %{extdesc} %{summary}. +%package util-ajax +Summary: util-ajax module for Jetty + +%description util-ajax +%{extdesc} %{summary}. + %package webapp -Summary: webapp module for Jetty +Summary: webapp module for Jetty + %description webapp %{extdesc} %{summary}. %package jmx -Summary: jmx module for Jetty +Summary: jmx module for Jetty + %description jmx %{extdesc} %{summary}. %package xml -Summary: xml module for Jetty +Summary: xml module for Jetty + %description xml %{extdesc} %{summary}. -%if %{without jp_minimal} + + +%if %{without jp_minimal} %package project -Summary: POM files for Jetty -Obsoletes: %{name}-websocket-parent < 9.4.0-0.4 -Provides: %{name}-websocket-parent = %{version}-%{release} -Obsoletes: %{name}-osgi-project < 9.4.0-0.4 -Provides: %{name}-osgi-project = %{version}-%{release} +Summary: POM files for Jetty +Obsoletes: %{name}-websocket-parent < 9.4.0-0.4 +Provides: %{name}-websocket-parent = %{version}-%{release} +Obsoletes: %{name}-osgi-project < 9.4.0-0.4 +Provides: %{name}-osgi-project = %{version}-%{release} + %description project %{extdesc} %{summary}. %package deploy -Summary: deploy module for Jetty +Summary: deploy module for Jetty + %description deploy %{extdesc} %{summary}. %package annotations -Summary: annotations module for Jetty +Summary: annotations module for Jetty + %description annotations %{extdesc} %{summary}. %package ant -Summary: ant module for Jetty +Summary: ant module for Jetty + %description ant %{extdesc} %{summary}. %package cdi -Summary: Jetty CDI Configuration +Summary: Jetty CDI Configuration + %description cdi %{extdesc} %{summary}. %package fcgi-client -Summary: FastCGI client module for Jetty +Summary: FastCGI client module for Jetty + %description fcgi-client %{extdesc} %{summary}. %package fcgi-server -Summary: FastCGI client module for Jetty -Requires: glassfish-servlet-api < 4.0.0 -%description fcgi-server -%{extdesc} %{summary}. +Summary: FastCGI client module for Jetty -%package infinispan -Summary: infinispan module for Jetty -%description infinispan +%description fcgi-server %{extdesc} %{summary}. %package jaspi -Summary: jaspi module for Jetty +Summary: jaspi module for Jetty + %description jaspi %{extdesc} %{summary}. %package jndi -Summary: jndi module for Jetty +Summary: jndi module for Jetty + %description jndi %{extdesc} %{summary}. %package jspc-maven-plugin -Summary: jspc-maven-plugin module for Jetty +Summary: jspc-maven-plugin module for Jetty + %description jspc-maven-plugin %{extdesc} %{summary}. %package maven-plugin -Summary: maven-plugin module for Jetty +Summary: maven-plugin module for Jetty + %description maven-plugin %{extdesc} %{summary}. %package plus -Summary: plus module for Jetty +Summary: plus module for Jetty + %description plus %{extdesc} %{summary}. %package proxy -Summary: proxy module for Jetty +Summary: proxy module for Jetty + %description proxy %{extdesc} %{summary}. %package rewrite -Summary: rewrite module for Jetty -Requires: glassfish-servlet-api < 4.0.0 +Summary: rewrite module for Jetty + %description rewrite %{extdesc} %{summary}. %package servlets -Summary: servlets module for Jetty -%description servlets -%{extdesc} %{summary}. +Summary: servlets module for Jetty -%package spring -Summary: spring module for Jetty -%description spring +%description servlets %{extdesc} %{summary}. %package start -Summary: start module for Jetty +Summary: start module for Jetty + %description start %{extdesc} %{summary}. %package unixsocket -Summary: unixsocket module for Jetty -%description unixsocket -%{extdesc} %{summary}. +Summary: unixsocket module for Jetty -%package util-ajax -Summary: util-ajax module for Jetty -%description util-ajax +%description unixsocket %{extdesc} %{summary}. %package websocket-api -Summary: websocket-api module for Jetty +Summary: websocket-api module for Jetty + %description websocket-api %{extdesc} %{summary}. %package websocket-client -Summary: websocket-client module for Jetty +Summary: websocket-client module for Jetty + %description websocket-client %{extdesc} %{summary}. %package websocket-common -Summary: websocket-common module for Jetty +Summary: websocket-common module for Jetty + %description websocket-common %{extdesc} %{summary}. %package websocket-server -Summary: websocket-server module for Jetty +Summary: websocket-server module for Jetty + %description websocket-server %{extdesc} %{summary}. %package websocket-servlet -Summary: websocket-servlet module for Jetty -Requires: glassfish-servlet-api < 4.0.0 +Summary: websocket-servlet module for Jetty + %description websocket-servlet %{extdesc} %{summary}. %package javax-websocket-client-impl -Summary: javax-websocket-client-impl module for Jetty +Summary: javax-websocket-client-impl module for Jetty + %description javax-websocket-client-impl %{extdesc} %{summary}. %package javax-websocket-server-impl -Summary: javax-websocket-server-impl module for Jetty +Summary: javax-websocket-server-impl module for Jetty + %description javax-websocket-server-impl %{extdesc} %{summary}. %package nosql -Summary: nosql module for Jetty -%description nosql -%{extdesc} %{summary}. - -%package httpservice -Summary: httpservice module for Jetty -Requires: glassfish-servlet-api < 4.0.0 -%description httpservice -%{extdesc} %{summary}. +Summary: nosql module for Jetty -%package osgi-boot -Summary: osgi-boot module for Jetty -%description osgi-boot -%{extdesc} %{summary}. - -%package osgi-boot-warurl -Summary: osgi-boot-warurl module for Jetty -%description osgi-boot-warurl -%{extdesc} %{summary}. - -%package osgi-boot-jsp -Summary: osgi-boot-jsp module for Jetty -Requires: glassfish-servlet-api < 4.0.0 -%description osgi-boot-jsp -%{extdesc} %{summary}. - -%package osgi-alpn -Summary: osgi-alpn module for Jetty -%description osgi-alpn +%description nosql %{extdesc} %{summary}. %package quickstart -Summary: quickstart module for Jetty +Summary: quickstart module for Jetty + %description quickstart %{extdesc} %{summary}. %package alpn-client -Summary: alpn-client module for Jetty +Summary: alpn-client module for Jetty + %description alpn-client %{extdesc} %{summary}. %package alpn-server -Summary: alpn-server module for Jetty +Summary: alpn-server module for Jetty + %description alpn-server %{extdesc} %{summary}. %package http2-client -Summary: http2-client module for Jetty +Summary: http2-client module for Jetty + %description http2-client %{extdesc} %{summary}. %package http2-common -Summary: http2-common module for Jetty +Summary: http2-common module for Jetty + %description http2-common %{extdesc} %{summary}. %package http2-hpack -Summary: http2-hpack module for Jetty +Summary: http2-hpack module for Jetty + %description http2-hpack %{extdesc} %{summary}. %package http2-http-client-transport -Summary: http2-http-client-transport module for Jetty +Summary: http2-http-client-transport module for Jetty + %description http2-http-client-transport %{extdesc} %{summary}. %package http2-server -Summary: http2-server module for Jetty +Summary: http2-server module for Jetty + %description http2-server %{extdesc} %{summary}. %package jstl -Summary: jstl module for Jetty +Summary: jstl module for Jetty + %description jstl %{extdesc} %{summary}. -%endif # without jp_minimal -%package javadoc -Summary: Javadoc for %{name} -License: (Apache-2.0 OR EPL-1.0) AND MIT -%description javadoc +%endif + +%package javadoc +Summary: Javadoc for %{name} +# some MIT-licensed code (from Utf8Appendable) is used to generate javadoc +License: (ASL 2.0 or EPL-1.0) and MIT + +%description javadoc %{summary}. %prep -%autosetup -n %{name}.project-%{name}-%{version}%{addver} -p1 +%setup -q -n %{name}.project-%{name}-%{version}%{addver} + +%patch -P1 -p1 +%patch -P2 -p1 + find . -name "*.?ar" -exec rm {} \; find . -name "*.class" -exec rm {} \; -%pom_remove_plugin -r :findbugs-maven-plugin + +# Plugins irrelevant or harmful to building the package +%pom_remove_plugin -r :maven-checkstyle-plugin +%pom_remove_plugin -r :spotbugs-maven-plugin %pom_remove_plugin -r :maven-enforcer-plugin -%pom_remove_plugin -r :clirr-maven-plugin %pom_remove_plugin -r :maven-eclipse-plugin -%pom_remove_plugin -r :maven-pmd-plugin %pom_remove_plugin -r :license-maven-plugin %pom_remove_plugin -r :maven-site-plugin %pom_remove_plugin -r :maven-source-plugin @@ -483,58 +606,110 @@ find . -name "*.class" -exec rm {} \; %pom_remove_plugin -r :maven-release-plugin %pom_remove_plugin -r :buildnumber-maven-plugin %pom_remove_plugin -r :h2spec-maven-plugin + +# Unnecessary pom flattening can be skipped %pom_remove_plugin -r :flatten-maven-plugin jetty-bom -%pom_remove_plugin -r :maven-dependency-plugin jetty-http2/http2-http-client-transport -%pom_remove_plugin -r :maven-dependency-plugin jetty-http2/http2-alpn-tests -%pom_remove_plugin -r :maven-dependency-plugin tests/test-http-client-transport -%pom_remove_plugin -r :maven-dependency-plugin tests/test-webapps/test-http2-webapp + %pom_disable_module aggregates/jetty-all -%pom_xpath_replace "pom:groupId[text()='ant']" "org.apache.ant" jetty-ant/pom.xml + +# Reflective use of classes that might not be present in the JDK should be optional OSGi-wise +%pom_xpath_inject "pom:configuration/pom:instructions" \ +"sun.misc;resolution:=optional,com.sun.nio.file;resolution:=optional,*" + %pom_remove_dep "com.sun.net.httpserver:http" jetty-http-spi + %pom_change_dep -r org.mortbay.jasper:apache-jsp org.apache.tomcat:tomcat-jasper + %pom_add_dep 'org.junit.jupiter:junit-jupiter-engine:${junit.version}' tests/test-sessions/test-sessions-common + +# provided by glassfish-jsp-api that has newer version %pom_change_dep -r javax.servlet.jsp:jsp-api javax.servlet.jsp:javax.servlet.jsp-api + +# txt artifact - not installable %pom_remove_plugin ":jetty-version-maven-plugin" %pom_xpath_remove "pom:artifactItem[pom:classifier='version']" jetty-home + +# Disable building source release %pom_xpath_remove 'pom:execution[pom:id="sources"]' jetty-home + +# Unwanted JS in javadoc sed -i '/^\s*\*.*