Merge branch 'jetty-10.0.x' into jetty-11.0.x

This commit is contained in:
olivier lamy 2020-04-10 18:46:25 +10:00
commit 7e03bb250e
26 changed files with 854 additions and 230 deletions

View File

@ -0,0 +1,525 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.client;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.client.util.FormRequestContent;
import org.eclipse.jetty.client.util.StringRequestContent;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.ManagedSelector;
import org.eclipse.jetty.io.NetworkTrafficListener;
import org.eclipse.jetty.io.NetworkTrafficSocketChannelEndPoint;
import org.eclipse.jetty.io.SelectorManager;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.NetworkTrafficServerConnector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Fields;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class NetworkTrafficListenerTest
{
private static final String END_OF_CONTENT = "~";
private Server server;
private NetworkTrafficServerConnector connector;
private NetworkTrafficHttpClient client;
private void start(Handler handler) throws Exception
{
startServer(handler);
startClient();
}
private void startServer(Handler handler) throws Exception
{
server = new Server();
connector = new NetworkTrafficServerConnector(server);
connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendDateHeader(false);
connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendServerVersion(false);
server.addConnector(connector);
server.setHandler(handler);
server.start();
}
private void startClient() throws Exception
{
client = new NetworkTrafficHttpClient(new AtomicReference<>());
client.start();
}
@AfterEach
public void dispose() throws Exception
{
if (client != null)
client.stop();
if (server != null)
server.stop();
}
@Test
public void testOpenedClosedAreInvoked() throws Exception
{
startServer(null);
CountDownLatch openedLatch = new CountDownLatch(1);
CountDownLatch closedLatch = new CountDownLatch(1);
connector.setNetworkTrafficListener(new NetworkTrafficListener()
{
public volatile Socket socket;
@Override
public void opened(Socket socket)
{
this.socket = socket;
openedLatch.countDown();
}
@Override
public void closed(Socket socket)
{
if (this.socket == socket)
closedLatch.countDown();
}
});
int port = connector.getLocalPort();
// Connect to the server
try (Socket ignored = new Socket("localhost", port))
{
assertTrue(openedLatch.await(10, TimeUnit.SECONDS));
}
assertTrue(closedLatch.await(10, TimeUnit.SECONDS));
}
@Test
public void testTrafficWithNoResponseContentOnNonPersistentConnection() throws Exception
{
start(new AbstractHandler()
{
@Override
public void handle(String uri, Request request, HttpServletRequest servletRequest, HttpServletResponse servletResponse)
{
request.setHandled(true);
}
});
AtomicReference<String> serverIncoming = new AtomicReference<>("");
CountDownLatch serverIncomingLatch = new CountDownLatch(1);
AtomicReference<String> serverOutgoing = new AtomicReference<>("");
CountDownLatch serverOutgoingLatch = new CountDownLatch(1);
connector.setNetworkTrafficListener(new NetworkTrafficListener()
{
@Override
public void incoming(Socket socket, ByteBuffer bytes)
{
serverIncoming.set(serverIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
serverIncomingLatch.countDown();
}
@Override
public void outgoing(Socket socket, ByteBuffer bytes)
{
serverOutgoing.set(serverOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
serverOutgoingLatch.countDown();
}
});
AtomicReference<String> clientIncoming = new AtomicReference<>("");
CountDownLatch clientIncomingLatch = new CountDownLatch(1);
AtomicReference<String> clientOutgoing = new AtomicReference<>("");
CountDownLatch clientOutgoingLatch = new CountDownLatch(1);
client.listener.set(new NetworkTrafficListener()
{
@Override
public void outgoing(Socket socket, ByteBuffer bytes)
{
clientOutgoing.set(clientOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
clientOutgoingLatch.countDown();
}
@Override
public void incoming(Socket socket, ByteBuffer bytes)
{
clientIncoming.set(clientIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
clientIncomingLatch.countDown();
}
});
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString())
.send();
assertEquals(HttpStatus.OK_200, response.getStatus());
assertTrue(clientOutgoingLatch.await(1, TimeUnit.SECONDS));
assertTrue(serverIncomingLatch.await(1, TimeUnit.SECONDS));
assertTrue(serverOutgoingLatch.await(1, TimeUnit.SECONDS));
assertTrue(clientIncomingLatch.await(1, TimeUnit.SECONDS));
assertEquals(clientOutgoing.get(), serverIncoming.get());
assertEquals(serverOutgoing.get(), clientIncoming.get());
}
@Test
public void testTrafficWithResponseContentOnPersistentConnection() throws Exception
{
String responseContent = "response_content" + END_OF_CONTENT;
start(new AbstractHandler()
{
@Override
public void handle(String uri, Request request, HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException
{
request.setHandled(true);
ServletOutputStream output = servletResponse.getOutputStream();
output.write(responseContent.getBytes(StandardCharsets.UTF_8));
}
});
AtomicReference<String> serverIncoming = new AtomicReference<>("");
CountDownLatch serverIncomingLatch = new CountDownLatch(1);
AtomicReference<String> serverOutgoing = new AtomicReference<>("");
CountDownLatch serverOutgoingLatch = new CountDownLatch(1);
connector.setNetworkTrafficListener(new NetworkTrafficListener()
{
@Override
public void incoming(Socket socket, ByteBuffer bytes)
{
serverIncoming.set(serverIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
serverIncomingLatch.countDown();
}
@Override
public void outgoing(Socket socket, ByteBuffer bytes)
{
serverOutgoing.set(serverOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
serverOutgoingLatch.countDown();
}
});
AtomicReference<String> clientIncoming = new AtomicReference<>("");
CountDownLatch clientIncomingLatch = new CountDownLatch(1);
AtomicReference<String> clientOutgoing = new AtomicReference<>("");
CountDownLatch clientOutgoingLatch = new CountDownLatch(1);
client.listener.set(new NetworkTrafficListener()
{
@Override
public void outgoing(Socket socket, ByteBuffer bytes)
{
clientOutgoing.set(clientOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
clientOutgoingLatch.countDown();
}
@Override
public void incoming(Socket socket, ByteBuffer bytes)
{
clientIncoming.set(clientIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
clientIncomingLatch.countDown();
}
});
ContentResponse response = client.newRequest("localhost", connector.getLocalPort()).send();
assertEquals(HttpStatus.OK_200, response.getStatus());
assertEquals(responseContent, response.getContentAsString());
assertTrue(clientOutgoingLatch.await(1, TimeUnit.SECONDS));
assertTrue(serverIncomingLatch.await(1, TimeUnit.SECONDS));
assertTrue(serverOutgoingLatch.await(1, TimeUnit.SECONDS));
assertTrue(clientIncomingLatch.await(1, TimeUnit.SECONDS));
assertEquals(clientOutgoing.get(), serverIncoming.get());
assertEquals(serverOutgoing.get(), clientIncoming.get());
}
@Test
public void testTrafficWithResponseContentChunkedOnPersistentConnection() throws Exception
{
String responseContent = "response_content";
String responseChunk1 = responseContent.substring(0, responseContent.length() / 2);
String responseChunk2 = responseContent.substring(responseContent.length() / 2);
start(new AbstractHandler()
{
@Override
public void handle(String uri, Request request, HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException
{
request.setHandled(true);
ServletOutputStream output = servletResponse.getOutputStream();
output.write(responseChunk1.getBytes(StandardCharsets.UTF_8));
output.flush();
output.write(responseChunk2.getBytes(StandardCharsets.UTF_8));
output.flush();
}
});
AtomicReference<String> serverIncoming = new AtomicReference<>("");
CountDownLatch serverIncomingLatch = new CountDownLatch(1);
AtomicReference<String> serverOutgoing = new AtomicReference<>("");
CountDownLatch serverOutgoingLatch = new CountDownLatch(1);
connector.setNetworkTrafficListener(new NetworkTrafficListener()
{
@Override
public void incoming(Socket socket, ByteBuffer bytes)
{
serverIncoming.set(serverIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
serverIncomingLatch.countDown();
}
@Override
public void outgoing(Socket socket, ByteBuffer bytes)
{
serverOutgoing.set(serverOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
if (serverOutgoing.get().endsWith("\r\n0\r\n\r\n"))
serverOutgoingLatch.countDown();
}
});
AtomicReference<String> clientIncoming = new AtomicReference<>("");
CountDownLatch clientIncomingLatch = new CountDownLatch(1);
AtomicReference<String> clientOutgoing = new AtomicReference<>("");
CountDownLatch clientOutgoingLatch = new CountDownLatch(1);
client.listener.set(new NetworkTrafficListener()
{
@Override
public void outgoing(Socket socket, ByteBuffer bytes)
{
clientOutgoing.set(clientOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
clientOutgoingLatch.countDown();
}
@Override
public void incoming(Socket socket, ByteBuffer bytes)
{
clientIncoming.set(clientIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
if (clientIncoming.get().endsWith("\r\n0\r\n\r\n"))
clientIncomingLatch.countDown();
}
});
ContentResponse response = client.newRequest("localhost", connector.getLocalPort()).send();
assertEquals(HttpStatus.OK_200, response.getStatus());
assertTrue(clientOutgoingLatch.await(1, TimeUnit.SECONDS));
assertTrue(serverIncomingLatch.await(1, TimeUnit.SECONDS));
assertTrue(serverOutgoingLatch.await(1, TimeUnit.SECONDS));
assertTrue(clientIncomingLatch.await(1, TimeUnit.SECONDS));
assertEquals(clientOutgoing.get(), serverIncoming.get());
assertEquals(serverOutgoing.get(), clientIncoming.get());
}
@Test
public void testTrafficWithRequestContentWithResponseRedirectOnPersistentConnection() throws Exception
{
String location = "/redirect";
start(new AbstractHandler()
{
@Override
public void handle(String uri, Request request, HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException
{
request.setHandled(true);
servletResponse.sendRedirect(location);
}
});
AtomicReference<String> serverIncoming = new AtomicReference<>("");
CountDownLatch serverIncomingLatch = new CountDownLatch(1);
AtomicReference<String> serverOutgoing = new AtomicReference<>("");
CountDownLatch serverOutgoingLatch = new CountDownLatch(1);
connector.setNetworkTrafficListener(new NetworkTrafficListener()
{
@Override
public void incoming(Socket socket, ByteBuffer bytes)
{
serverIncoming.set(serverIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
serverIncomingLatch.countDown();
}
@Override
public void outgoing(Socket socket, ByteBuffer bytes)
{
serverOutgoing.set(serverOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
serverOutgoingLatch.countDown();
}
});
AtomicReference<String> clientIncoming = new AtomicReference<>("");
CountDownLatch clientIncomingLatch = new CountDownLatch(1);
AtomicReference<String> clientOutgoing = new AtomicReference<>("");
CountDownLatch clientOutgoingLatch = new CountDownLatch(1);
client.listener.set(new NetworkTrafficListener()
{
@Override
public void outgoing(Socket socket, ByteBuffer bytes)
{
clientOutgoing.set(clientOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
clientOutgoingLatch.countDown();
}
@Override
public void incoming(Socket socket, ByteBuffer bytes)
{
clientIncoming.set(clientIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
clientIncomingLatch.countDown();
}
});
client.setFollowRedirects(false);
Fields fields = new Fields();
fields.put("a", "1");
fields.put("b", "2");
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.body(new FormRequestContent(fields))
.send();
assertEquals(HttpStatus.FOUND_302, response.getStatus());
assertTrue(clientOutgoingLatch.await(1, TimeUnit.SECONDS));
assertTrue(serverIncomingLatch.await(1, TimeUnit.SECONDS));
assertTrue(serverOutgoingLatch.await(1, TimeUnit.SECONDS));
assertTrue(clientIncomingLatch.await(1, TimeUnit.SECONDS));
assertEquals(clientOutgoing.get(), serverIncoming.get());
assertEquals(serverOutgoing.get(), clientIncoming.get());
}
@Test
public void testTrafficWithBigRequestContentOnPersistentConnection() throws Exception
{
start(new AbstractHandler()
{
@Override
public void handle(String uri, Request request, HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException
{
// Read and discard the request body to make the test more
// reliable, otherwise there is a race between request body
// upload and response download
InputStream input = servletRequest.getInputStream();
byte[] buffer = new byte[4096];
while (true)
{
int read = input.read(buffer);
if (read < 0)
break;
}
request.setHandled(true);
}
});
AtomicReference<String> serverIncoming = new AtomicReference<>("");
AtomicReference<String> serverOutgoing = new AtomicReference<>("");
CountDownLatch serverOutgoingLatch = new CountDownLatch(1);
connector.setNetworkTrafficListener(new NetworkTrafficListener()
{
@Override
public void incoming(Socket socket, ByteBuffer bytes)
{
serverIncoming.set(serverIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
}
@Override
public void outgoing(Socket socket, ByteBuffer bytes)
{
serverOutgoing.set(serverOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
serverOutgoingLatch.countDown();
}
});
AtomicReference<String> clientIncoming = new AtomicReference<>("");
CountDownLatch clientIncomingLatch = new CountDownLatch(1);
AtomicReference<String> clientOutgoing = new AtomicReference<>("");
client.listener.set(new NetworkTrafficListener()
{
@Override
public void outgoing(Socket socket, ByteBuffer bytes)
{
clientOutgoing.set(clientOutgoing.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
}
@Override
public void incoming(Socket socket, ByteBuffer bytes)
{
clientIncoming.set(clientIncoming.get() + BufferUtil.toString(bytes, StandardCharsets.UTF_8));
clientIncomingLatch.countDown();
}
});
// Generate a large request content.
String requestContent = "0123456789ABCDEF";
for (int i = 0; i < 16; ++i)
{
requestContent += requestContent;
}
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.body(new StringRequestContent(requestContent))
.send();
assertEquals(HttpStatus.OK_200, response.getStatus());
assertTrue(serverOutgoingLatch.await(1, TimeUnit.SECONDS));
assertTrue(clientIncomingLatch.await(1, TimeUnit.SECONDS));
assertEquals(clientOutgoing.get(), serverIncoming.get());
assertTrue(clientOutgoing.get().length() > requestContent.length());
assertEquals(serverOutgoing.get(), clientIncoming.get());
}
private static class NetworkTrafficHttpClient extends HttpClient
{
private final AtomicReference<NetworkTrafficListener> listener;
private NetworkTrafficHttpClient(AtomicReference<NetworkTrafficListener> listener)
{
super(new HttpClientTransportOverHTTP(new ClientConnector()
{
@Override
protected SelectorManager newSelectorManager()
{
return new ClientSelectorManager(getExecutor(), getScheduler(), getSelectors())
{
@Override
protected EndPoint newEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey selectionKey)
{
return new NetworkTrafficSocketChannelEndPoint(channel, selector, selectionKey, getScheduler(), getIdleTimeout().toMillis(), listener.get());
}
};
}
}));
this.listener = listener;
}
}
}

View File

@ -41,7 +41,7 @@
<asciidoctorj.epub.version>1.5.0-alpha.8.1</asciidoctorj.epub.version>
<asciidoctorj.version>1.5.8.1</asciidoctorj.version>
<jruby.version>1.7.27</jruby.version>
<web.resources.version>1.1</web.resources.version>
<web.resources.version>1.2</web.resources.version>
<web.resources.directory>${project.build.directory}/web-resources</web.resources.directory>
<!-- old properties -->
<html.directory>${project.build.directory}/current</html.directory>

View File

@ -23,8 +23,11 @@
:revdate: {TIMESTAMP}
:toc: left
:toc-title: Contribution Guide
:toc-image: ../../common/images/jetty-logo.svg
:toc-image-url: /jetty/index.html
:toc-style:
:header-style: eclipse-thin
:breadcrumb-style: eclipse-thin
:footer-style: default
:breadcrumb: Home:../index.html | Contribution Guide:./index.html
// docinfo lets you pull in shared content and/or influence via render type
@ -43,12 +46,6 @@ endif::[]
// options for special blocks, code snippets, screen, etc
:sub-order: attributes+
// suppress document footer generation
//:nofooter:
// suppress Eclipse footer
:no-eclipse-footer:
// uncomment to allow include::https:// style content inclusion
//:allow-uri-read: true

View File

@ -49,7 +49,7 @@ If `true`, welcome files are redirected rather that forwarded.
welcomeServlets::
If `true`, attempt to dispatch to welcome files that are servlets, but only after no matching static
resources could be found. If `false`, then a welcome file must exist on disk. If `exact`, then exact
servlet matches are supported without an existing file. Default is `true`. This must be `false` if you want directory listings,
servlet matches are supported without an existing file. Default is `false`. This must be `false` if you want directory listings,
but have index.jsp in your welcome file list.
precompressed::
If set to a comma separated list of encoding types (that may be listed in a requests Accept-Encoding header) to file extension mappings to look for and serve.

View File

@ -23,8 +23,11 @@
:revdate: {TIMESTAMP}
:toc: left
:toc-title: Distribution Guide
:toc-image: ../../common/images/jetty-logo.svg
:toc-image-url: /jetty/index.html
:toc-style:
:header-style: eclipse-thin
:breadcrumb-style: eclipse-thin
:footer-style: default
:breadcrumb: Home:../index.html | Distribution Guide:./index.html
// docinfo lets you pull in shared content and/or influence via render type
@ -43,12 +46,6 @@ endif::[]
// options for special blocks, code snippets, screen, etc
:sub-order: attributes+
// suppress document footer generation
//:nofooter:
// suppress Eclipse footer
:no-eclipse-footer:
// uncomment to allow include::https:// style content inclusion
//:allow-uri-read: true

View File

@ -34,74 +34,105 @@ This configuration is essentially the multiple logger configuration with added c
The technique used by this configuration is to provide an link:{JDURL}org/eclipse/jetty/deploy/AppLifeCycle.Binding.html[AppLifeCycle.Binding] against the link:{JDURL}/org/eclipse/jetty/deploy/AppLifeCycle.html[`"deploying"`node] that modifies the
link:{JDURL}/org/eclipse/jetty/webapp/WebAppContext.html#getSystemClasspathPattern()[WebAppContext.getSystemClasspathPattern().add(String)] for the common logging classes.
See https://github.com/jetty-project/jetty-webapp-logging/blob/master/src/main/java/org/eclipse/jetty/webapp/logging/CentralizedWebAppLoggingBinding.java[org.eclipse.jetty.logging.CentralizedWebAppLoggingBinding] for actual implementation.
See https://github.com/jetty-project/jetty-webapp-logging/blob/master/jetty-webapp-logging/src/main/java/org/eclipse/jetty/webapp/logging/CentralizedWebAppLoggingBinding.java[org.eclipse.jetty.logging.CentralizedWebAppLoggingBinding] for actual implementation.
A convenient replacement `logging` module has been created to bootstrap your `${jetty.base}` directory for capturing all Jetty server logging from multiple logging frameworks into a single logging output file managed by Logback.
[source, screen, subs="{sub-order}"]
[source,screen,subs="{sub-order}"]
....
[mybase]$ mkdir modules
[mybase]$ cd modules
[modules]$ curl -O https://raw.githubusercontent.com/jetty-project/logging-modules/master/capture-all/logging.mod
[mybase]$ curl -O https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-webapp-logging/9.4.27/jetty-webapp-logging-9.4.27-config.jar
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1416 100 1416 0 0 4241 0 --:--:-- --:--:-- --:--:-- 4252
100 3402 100 3402 0 0 15823 0 --:--:-- --:--:-- --:--:-- 15750
[master]$ curl -O https://raw.githubusercontent.com/jetty-project/logging-modules/master/centralized/webapp-logging.mod
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 660 100 660 0 0 2032 0 --:--:-- --:--:-- --:--:-- 2037
[modules]$ cd ..
[mybase]$ jar -xf jetty-webapp-logging-9.4.27-config.jar
[mybase]$ java -jar /opt/jetty-dist/start.jar --add-to-start=logging,webapp-logging
INFO: logging initialised in ${jetty.base}/start.ini (appended)
MKDIR: ${jetty.base}/logs
DOWNLOAD: https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.6.6/slf4j-api-1.6.6.jar to lib/logging/slf4j-api-1.6.6.jar
DOWNLOAD: https://repo1.maven.org/maven2/org/slf4j/log4j-over-slf4j/1.6.6/log4j-over-slf4j-1.6.6.jar to lib/logging/log4j-over-slf4j-1.6.6.jar
DOWNLOAD: https://repo1.maven.org/maven2/org/slf4j/jul-to-slf4j/1.6.6/jul-to-slf4j-1.6.6.jar to lib/logging/jul-to-slf4j-1.6.6.jar
DOWNLOAD: https://repo1.maven.org/maven2/org/slf4j/jcl-over-slf4j/1.6.6/jcl-over-slf4j-1.6.6.jar to lib/logging/jcl-over-slf4j-1.6.6.jar
DOWNLOAD: https://repo1.maven.org/maven2/ch/qos/logback/logback-core/1.0.7/logback-core-1.0.7.jar to lib/logging/logback-core-1.0.7.jar
DOWNLOAD: https://repo1.maven.org/maven2/ch/qos/logback/logback-classic/1.0.7/logback-classic-1.0.7.jar to lib/logging/logback-classic-1.0.7.jar
DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/capture-all/logback.xml to resources/logback.xml
DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/capture-all/jetty-logging.properties to resources/jetty-logging.properties
DOWNLOAD: https://raw.githubusercontent.com/jetty-project/logging-modules/master/capture-all/jetty-logging.xml to etc/jetty-logging.xml
INFO: resources initialised transitively
INFO: resources enabled in ${jetty.base}/start.ini
INFO: webapp-logging initialised in ${jetty.base}/start.ini (appended)
DOWNLOAD: https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-webapp-logging/9.0.0/jetty-webapp-logging-9.0.0.jar to lib/webapp-logging/jetty-webapp-logging-9.0.0.jar
DOWNLOAD: https://raw.githubusercontent.com/jetty-project/jetty-webapp-logging/master/src/main/config/etc/jetty-webapp-logging.xml to etc/jetty-webapp-logging.xml
DOWNLOAD: https://raw.githubusercontent.com/jetty-project/jetty-webapp-logging/master/src/main/config/etc/jetty-mdc-handler.xml to etc/jetty-mdc-handler.xml
INFO: deploy initialised transitively
INFO: deploy enabled in ${jetty.base}/start.ini
INFO: logging initialised transitively
INFO: resources initialised transitively
INFO: resources enabled in ${jetty.base}/start.ini
[mybase]$ java -jar /opt/jetty-hom/start.jar --create-startd --add-to-start=centralized-webapp-logging
[mybase]$ java -jar /opt/jetty-dist/start.jar
ALERT: There are enabled module(s) with licenses.
The following 2 module(s):
+ contains software not provided by the Eclipse Foundation!
+ contains software not covered by the Eclipse Public License!
+ has not been audited for compliance with its license
Module: logback-impl
+ Logback: the reliable, generic, fast and flexible logging framework.
+ Copyright (C) 1999-2012, QOS.ch. All rights reserved.
+ This program and the accompanying materials are dual-licensed under
+ either:
+ the terms of the Eclipse Public License v1.0
+ as published by the Eclipse Foundation:
+ http://www.eclipse.org/legal/epl-v10.html
+ or (per the licensee's choosing) under
+ the terms of the GNU Lesser General Public License version 2.1
+ as published by the Free Software Foundation:
+ http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
Module: slf4j-api
+ SLF4J is distributed under the MIT License.
+ Copyright (c) 2004-2013 QOS.ch
+ All rights reserved.
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Proceed (y/N)? y
INFO : slf4j-api transitively enabled
INFO : log4j-over-slf4j transitively enabled
INFO : jcl-slf4j transitively enabled
INFO : logback-impl transitively enabled
INFO : jul-slf4j transitively enabled
INFO : slf4j-logback transitively enabled
INFO : centralized-webapp-logging initialized in ${jetty.base}/start.d/centralized-webapp-logging.ini
INFO : logging-logback transitively enabled
INFO : resources transitively enabled
MKDIR : ${jetty.base}/lib/slf4j
DOWNLD: https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar to ${jetty.base}/lib/slf4j/slf4j-api-1.7.25.jar
MKDIR : ${jetty.base}/lib/logging
DOWNLD: https://repo1.maven.org/maven2/org/slf4j/log4j-over-slf4j/1.7.25/log4j-over-slf4j-1.7.25.jar to ${jetty.base}/lib/logging/log4j-over-slf4j-1.7.25.jar
DOWNLD: https://repo1.maven.org/maven2/org/slf4j/jcl-over-slf4j/1.7.25/jcl-over-slf4j-1.7.25.jar to ${jetty.base}/lib/slf4j/jcl-over-slf4j-1.7.25.jar
MKDIR : ${jetty.base}/lib/logback
DOWNLD: https://repo1.maven.org/maven2/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar to ${jetty.base}/lib/logback/logback-core-1.2.3.jar
DOWNLD: https://repo1.maven.org/maven2/org/slf4j/jul-to-slf4j/1.7.25/jul-to-slf4j-1.7.25.jar to ${jetty.base}/lib/slf4j/jul-to-slf4j-1.7.25.jar
COPY : ${jetty.home}/modules/jul-slf4j/etc/java-util-logging.properties to ${jetty.base}/etc/java-util-logging.properties
DOWNLD: https://repo1.maven.org/maven2/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar to ${jetty.base}/lib/logback/logback-classic-1.2.3.jar
MKDIR : ${jetty.base}/logs
DOWNLD: https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-webapp-logging/9.4.27/jetty-webapp-logging-9.4.27.jar to ${jetty.base}/lib/logging/jetty-webapp-logging-9.4.27.jar
INFO : Base directory was modified
$
....
The replacement `logging.mod` performs a number of tasks.
This replacement `centralized-webapp-logging.mod` performs a number of tasks.
. `mybase` is a `${jetty.base}` directory.
. The jetty-distribution is unpacked (and untouched) into `/opt/jetty-dist/` and becomes the `${jetty.home}` directory for this demonstration.
. The `curl` command downloads the replacement `logging.mod` and puts it into the `${jetty.base}/modules/` directory for use by mybase only.
. The `start.jar --add-to-start=logging,webapp-logging` command performs a number of steps to make the logging module available to the `${jetty.base}` configuration.
.. Several entries are added to the `${jetty.base}/start.ini` configuration.
* `--module=logging` is added to enable the logging module.
* `--module=webapp-logging` is added to enable the webapp-logging module.
.. Required `${jetty.base}` directories are created: `${jetty.base}/logs` and `${jetty.base}/resources`.
.. Required logging libraries are downloaded (if not present already) to the `${jetty.base}/lib/logging/` directory:
. `mybase` is a `${jetty.base}` directory.
. The jetty-distribution is unpacked (and untouched) into `/opt/jetty-dist/` and becomes the `${jetty.home}` directory for this demonstration.
. The `curl` command downloads the replacement config overlay for the `${jetty.base}/modules/` directory to use.
. The `start.jar --add-to-start=centralized-webapp-logging` command performs a number of steps to make the centralized-webapp-logging module available to the `${jetty.base}` configuration.
.. A new `${jetty.base}/start.d/centralized-webapp-logging.ini` configuration was created.
.. Required `${jetty.base}` directories are created: `${jetty.base}/logs` and `${jetty.base}/resources`.
.. Required logging libraries are downloaded (if not present already) to the `${jetty.base}/lib/logging/` directory:
* `slf4j-api.jar` - API jar for Slf4j (used by most of the rest of the jars)
* `log4j-over-slf4j.jar` - Slf4j jar that captures all log4j emitted logging events
* `jul-to-slf4j.jar` - Slf4j jar that captures all java.util.logging events
* `jcl-over-slf4j.jar` - Slf4j jar that captures all commons-logging events
* `logback-classic.jar` - the Slf4j adapter jar that routes all of the captured logging events to logback itself.
* `logback-core.jar` - the logback implementation jar, that handles all of the filtering and output of the logging events.
.. Required webapp-logging library is downloaded (if not present already) to the `${jetty.base}/lib/webapp-logging/` directory:
.. Required webapp-logging library is downloaded (if not present already) to the `${jetty.base}/lib/webapp-logging/` directory:
* `jetty-webapp-logging.jar` - the Jetty side deployment manger app-lifecycle bindings for modifying the `WebAppClassloaders` of deployed webapps.
.. Required configuration files are downloaded (if not present already) to the `${jetty.base}/resources/` directory: `jetty-logging.properties`, and `logback.xml`.
.. Required initialization commands are downloaded (if not present already) to the `${jetty.base}/etc/` directory: `jetty-logging.xml`, `jetty-webapp-logging.xml`, and `jetty-mdc-handler.xml`.
At this point the Jetty `mybase` is configured so that the jetty server itself will log using slf4j, and all other logging events from other Jetty Server components (such as database drivers, security layers, jsp, mail, and other 3rd party server components) are routed to logback for filtering and output.
@ -109,4 +140,5 @@ All webapps deployed via the `DeploymentManager` have their `WebAppClassLoader`
The server classpath can be verified by using the `start.jar --list-config` command.
In essence, Jetty is now configured to emit its own logging events to slf4j, and various slf4j bridge jars are acting on behalf of log4j, `java.util.logging`, and `commons-logging`, routing all of the logging events to logback (a slf4j adapter) for routing (to console, file, etc...).
In essence, Jetty is now configured to emit its own logging events to slf4j, and various slf4j bridge jars are acting on behalf of `log4j`, `java.util.logging`, and `commons-logging`, routing all of the logging events to `logback`
(a slf4j implementation) for routing (to console, file, etc...).

View File

@ -23,8 +23,12 @@
:revdate: {TIMESTAMP}
:toc: left
:toc-title: Embedded Guide
:toc-image: ../../common/images/jetty-logo.svg
:toc-image-url: /jetty/index.html
:toc-style:
:header-style: eclipse-thin
:breadcrumb-style: eclipse-thin
:footer-style: default
:breadcrumb: Home:../index.html | Embedded Guide:./index.html
// docinfo lets you pull in shared content and/or influence via render type
@ -43,12 +47,6 @@ endif::[]
// options for special blocks, code snippets, screen, etc
:sub-order: attributes+
// suppress document footer generation
//:nofooter:
// suppress Eclipse footer
:no-eclipse-footer:
// uncomment to allow include::https:// style content inclusion
//:allow-uri-read: true

View File

@ -23,15 +23,14 @@
:revdate: {TIMESTAMP}
:toc: left
:toc-title: Quickstart Guide
:toc-image: ../../common/images/jetty-logo.svg
:toc-image-url: /jetty/index.html
:toc-style:
:header-style: eclipse-thin
:breadcrumb-style: eclipse-thin
:footer-style: default
:breadcrumb: Home:../index.html | Quickstart Guide:./index.html
// docinfo lets you pull in shared content and/or influence via render type
//:docinfodir: {DOCINFODIR}/documentation
//:docinfo1:
// html specific directives
ifdef::backend-html5[]
:safe-mode-unsafe:
@ -44,10 +43,6 @@ endif::[]
// options for special blocks, code snippets, screen, etc
:sub-order: attributes+
// suppress document footer generation
//:nofooter:
:no-eclipse-footer:
// uncomment to allow include::https:// style content inclusion
//:allow-uri-read: true

View File

@ -1,6 +1,6 @@
# Mapping of mime type to inferred or assumed charset
# inferred charsets are used for encoding/decoding and explicitly set in associated metadata
# assumed charsets are used for encoding/decoding, but are not set in associated metadata
# inferred charsets are used for encoding/decoding and explicitly set in Content-Type
# assumed charsets are used for encoding/decoding, but are not set in Content-Type
# In this file, assumed charsets are indicated with a leading '-'
text/html=utf-8

View File

@ -301,7 +301,7 @@ public class ClientConnector extends ContainerLifeCycle
protected class ClientSelectorManager extends SelectorManager
{
protected ClientSelectorManager(Executor executor, Scheduler scheduler, int selectors)
public ClientSelectorManager(Executor executor, Scheduler scheduler, int selectors)
{
super(executor, scheduler, selectors);
}

View File

@ -24,7 +24,7 @@ import java.nio.ByteBuffer;
/**
* <p>A listener for raw network traffic within Jetty.</p>
* <p>{@link NetworkTrafficListener}s can be installed in a
* <code>org.eclipse.jetty.server.nio.NetworkTrafficSelectChannelConnector</code>,
* {@code org.eclipse.jetty.server.NetworkTrafficServerConnector},
* and are notified of the following network traffic events:</p>
* <ul>
* <li>Connection opened, when the server has accepted the connection from a remote client</li>
@ -45,7 +45,9 @@ public interface NetworkTrafficListener
*
* @param socket the socket associated with the remote client
*/
public void opened(Socket socket);
default void opened(Socket socket)
{
}
/**
* <p>Callback method invoked when bytes sent by a remote client arrived on the server.</p>
@ -53,7 +55,9 @@ public interface NetworkTrafficListener
* @param socket the socket associated with the remote client
* @param bytes the read-only buffer containing the incoming bytes
*/
public void incoming(Socket socket, ByteBuffer bytes);
default void incoming(Socket socket, ByteBuffer bytes)
{
}
/**
* <p>Callback method invoked when bytes are sent to a remote client from the server.</p>
@ -62,7 +66,9 @@ public interface NetworkTrafficListener
* @param socket the socket associated with the remote client
* @param bytes the read-only buffer containing the outgoing bytes
*/
public void outgoing(Socket socket, ByteBuffer bytes);
default void outgoing(Socket socket, ByteBuffer bytes)
{
}
/**
* <p>Callback method invoked when a connection to a remote client has been closed.</p>
@ -74,31 +80,7 @@ public interface NetworkTrafficListener
*
* @param socket the (closed) socket associated with the remote client
*/
public void closed(Socket socket);
/**
* <p>A commodity class that implements {@link NetworkTrafficListener} with empty methods.</p>
*/
public static class Adapter implements NetworkTrafficListener
default void closed(Socket socket)
{
@Override
public void opened(Socket socket)
{
}
@Override
public void incoming(Socket socket, ByteBuffer bytes)
{
}
@Override
public void outgoing(Socket socket, ByteBuffer bytes)
{
}
@Override
public void closed(Socket socket)
{
}
}
}

View File

@ -19,27 +19,28 @@
package org.eclipse.jetty.io;
import java.io.IOException;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.List;
import org.eclipse.jetty.util.thread.Scheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NetworkTrafficSelectChannelEndPoint extends SocketChannelEndPoint
/**
* <p>A specialized version of {@link SocketChannelEndPoint} that supports {@link NetworkTrafficListener}s.</p>
*/
public class NetworkTrafficSocketChannelEndPoint extends SocketChannelEndPoint
{
private static final Logger LOG = LoggerFactory.getLogger(NetworkTrafficSelectChannelEndPoint.class);
private static final Logger LOG = LoggerFactory.getLogger(NetworkTrafficSocketChannelEndPoint.class);
private final List<NetworkTrafficListener> listeners;
private final NetworkTrafficListener listener;
public NetworkTrafficSelectChannelEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key, Scheduler scheduler, long idleTimeout, List<NetworkTrafficListener> listeners) throws IOException
public NetworkTrafficSocketChannelEndPoint(SelectableChannel channel, ManagedSelector selectSet, SelectionKey key, Scheduler scheduler, long idleTimeout, NetworkTrafficListener listener)
{
super(channel, selectSet, key, scheduler);
setIdleTimeout(idleTimeout);
this.listeners = listeners;
this.listener = listener;
}
@Override
@ -60,7 +61,7 @@ public class NetworkTrafficSelectChannelEndPoint extends SocketChannelEndPoint
{
int position = b.position();
ByteBuffer view = b.slice();
flushed &= super.flush(b);
flushed = super.flush(b);
int l = b.position() - position;
view.limit(view.position() + l);
notifyOutgoing(view);
@ -75,76 +76,63 @@ public class NetworkTrafficSelectChannelEndPoint extends SocketChannelEndPoint
public void onOpen()
{
super.onOpen();
if (listeners != null && !listeners.isEmpty())
if (listener != null)
{
for (NetworkTrafficListener listener : listeners)
try
{
try
{
listener.opened(getSocket());
}
catch (Exception x)
{
LOG.warn("listener.opened failure", x);
}
listener.opened(getSocket());
}
catch (Throwable x)
{
LOG.info("Exception while invoking listener " + listener, x);
}
}
}
@Override
public void onClose(Throwable cause)
public void onClose(Throwable failure)
{
super.onClose(cause);
if (listeners != null && !listeners.isEmpty())
super.onClose(failure);
if (listener != null)
{
for (NetworkTrafficListener listener : listeners)
try
{
try
{
listener.closed(getSocket());
}
catch (Exception x)
{
LOG.warn("listener.closed failure", x);
}
listener.closed(getSocket());
}
catch (Throwable x)
{
LOG.info("Exception while invoking listener " + listener, x);
}
}
}
public void notifyIncoming(ByteBuffer buffer, int read)
{
if (listeners != null && !listeners.isEmpty() && read > 0)
if (listener != null && read > 0)
{
for (NetworkTrafficListener listener : listeners)
try
{
try
{
ByteBuffer view = buffer.asReadOnlyBuffer();
listener.incoming(getSocket(), view);
}
catch (Exception x)
{
LOG.warn("listener.incoming() failure", x);
}
ByteBuffer view = buffer.asReadOnlyBuffer();
listener.incoming(getSocket(), view);
}
catch (Throwable x)
{
LOG.info("Exception while invoking listener " + listener, x);
}
}
}
public void notifyOutgoing(ByteBuffer view)
{
if (listeners != null && !listeners.isEmpty() && view.hasRemaining())
if (listener != null && view.hasRemaining())
{
Socket socket = getSocket();
for (NetworkTrafficListener listener : listeners)
try
{
try
{
listener.outgoing(socket, view);
}
catch (Exception x)
{
LOG.warn("listener.outgoing() failure", x);
}
listener.outgoing(getSocket(), view);
}
catch (Throwable x)
{
LOG.info("Exception while invoking listener " + listener, x);
}
}
}

View File

@ -18,29 +18,26 @@
package org.eclipse.jetty.server;
import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ChannelEndPoint;
import org.eclipse.jetty.io.ManagedSelector;
import org.eclipse.jetty.io.NetworkTrafficListener;
import org.eclipse.jetty.io.NetworkTrafficSelectChannelEndPoint;
import org.eclipse.jetty.io.NetworkTrafficSocketChannelEndPoint;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.Scheduler;
/**
* <p>A specialized version of {@link ServerConnector} that supports {@link NetworkTrafficListener}s.</p>
* <p>{@link NetworkTrafficListener}s can be added and removed dynamically before and after this connector has
* been started without causing {@link java.util.ConcurrentModificationException}s.</p>
* <p>A {@link NetworkTrafficListener} can be set and unset dynamically before and after this connector has
* been started.</p>
*/
public class NetworkTrafficServerConnector extends ServerConnector
{
private final List<NetworkTrafficListener> listeners = new CopyOnWriteArrayList<>();
private volatile NetworkTrafficListener listener;
public NetworkTrafficServerConnector(Server server)
{
@ -68,25 +65,24 @@ public class NetworkTrafficServerConnector extends ServerConnector
}
/**
* @param listener the listener to add
* @param listener the listener to set, or null to unset
*/
public void addNetworkTrafficListener(NetworkTrafficListener listener)
public void setNetworkTrafficListener(NetworkTrafficListener listener)
{
listeners.add(listener);
this.listener = listener;
}
/**
* @param listener the listener to remove
* @return the listener
*/
public void removeNetworkTrafficListener(NetworkTrafficListener listener)
public NetworkTrafficListener getNetworkTrafficListener()
{
listeners.remove(listener);
return listener;
}
@Override
protected ChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException
protected ChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key)
{
NetworkTrafficSelectChannelEndPoint endPoint = new NetworkTrafficSelectChannelEndPoint(channel, selectSet, key, getScheduler(), getIdleTimeout(), listeners);
return endPoint;
return new NetworkTrafficSocketChannelEndPoint(channel, selectSet, key, getScheduler(), getIdleTimeout(), getNetworkTrafficListener());
}
}

View File

@ -107,7 +107,7 @@ public class Response implements HttpServletResponse
NOT_SET, INFERRED, SET_LOCALE, SET_CONTENT_TYPE, SET_CHARACTER_ENCODING
}
private static final EnumSet<EncodingFrom> __localeOverride = EnumSet.of(EncodingFrom.NOT_SET, EncodingFrom.INFERRED);
private static final EnumSet<EncodingFrom> __localeOverride = EnumSet.of(EncodingFrom.NOT_SET, EncodingFrom.INFERRED, EncodingFrom.SET_LOCALE);
private static final EnumSet<EncodingFrom> __explicitCharset = EnumSet.of(EncodingFrom.SET_LOCALE, EncodingFrom.SET_CHARACTER_ENCODING);
public Response(HttpChannel channel, HttpOutput out)

View File

@ -1,23 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
/**
* Jetty Server : Core Server Connector
*/
package org.eclipse.jetty.server.nio;

View File

@ -279,7 +279,8 @@ public class SessionHandler extends ScopedHandler
}
/**
* Call the session lifecycle listeners
* Call the session lifecycle listeners in
* the reverse order they were added.
*
* @param session the session on which to call the lifecycle listeners
*/
@ -310,7 +311,8 @@ public class SessionHandler extends ScopedHandler
}
/**
* Call the session lifecycle listeners
* Call the session lifecycle listeners in the order
* they were added.
*
* @param session the session on which to call the lifecycle listeners
*/
@ -322,9 +324,9 @@ public class SessionHandler extends ScopedHandler
if (_sessionListeners != null)
{
HttpSessionEvent event = new HttpSessionEvent(session);
for (int i = _sessionListeners.size() - 1; i >= 0; i--)
for (HttpSessionListener l : _sessionListeners)
{
_sessionListeners.get(i).sessionCreated(event);
l.sessionCreated(event);
}
}
}

View File

@ -33,6 +33,7 @@ import jakarta.servlet.ServletException;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.NetworkTrafficListener;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.BufferUtil;
@ -78,7 +79,7 @@ public class NetworkTrafficListenerTest
final CountDownLatch openedLatch = new CountDownLatch(1);
final CountDownLatch closedLatch = new CountDownLatch(1);
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
connector.setNetworkTrafficListener(new NetworkTrafficListener.Adapter()
{
public volatile Socket socket;
@ -122,7 +123,7 @@ public class NetworkTrafficListenerTest
final CountDownLatch incomingLatch = new CountDownLatch(1);
final AtomicReference<String> outgoingData = new AtomicReference<>("");
final CountDownLatch outgoingLatch = new CountDownLatch(1);
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
connector.setNetworkTrafficListener(new NetworkTrafficListener.Adapter()
{
@Override
public void incoming(Socket socket, ByteBuffer bytes)
@ -188,7 +189,7 @@ public class NetworkTrafficListenerTest
final CountDownLatch incomingLatch = new CountDownLatch(1);
final AtomicReference<String> outgoingData = new AtomicReference<>("");
final CountDownLatch outgoingLatch = new CountDownLatch(2);
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
connector.setNetworkTrafficListener(new NetworkTrafficListener.Adapter()
{
@Override
public void incoming(Socket socket, ByteBuffer bytes)
@ -258,7 +259,7 @@ public class NetworkTrafficListenerTest
final CountDownLatch incomingLatch = new CountDownLatch(1);
final AtomicReference<String> outgoingData = new AtomicReference<>("");
final CountDownLatch outgoingLatch = new CountDownLatch(1);
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
connector.setNetworkTrafficListener(new NetworkTrafficListener.Adapter()
{
@Override
public void incoming(Socket socket, ByteBuffer bytes)
@ -328,7 +329,7 @@ public class NetworkTrafficListenerTest
final CountDownLatch incomingLatch = new CountDownLatch(1);
final AtomicReference<String> outgoingData = new AtomicReference<>("");
final CountDownLatch outgoingLatch = new CountDownLatch(1);
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
connector.setNetworkTrafficListener(new NetworkTrafficListener.Adapter()
{
@Override
public void incoming(Socket socket, ByteBuffer bytes)
@ -404,7 +405,7 @@ public class NetworkTrafficListenerTest
final AtomicReference<String> incomingData = new AtomicReference<>("");
final AtomicReference<String> outgoingData = new AtomicReference<>("");
final CountDownLatch outgoingLatch = new CountDownLatch(1);
connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter()
connector.setNetworkTrafficListener(new NetworkTrafficListener.Adapter()
{
@Override
public void incoming(Socket socket, ByteBuffer bytes)

View File

@ -54,6 +54,7 @@ import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.io.AbstractEndPoint;
import org.eclipse.jetty.io.ByteArrayEndPoint;
import org.eclipse.jetty.server.handler.AbstractHandler;
@ -474,6 +475,67 @@ public class ResponseTest
response.getWriter();
assertThat("iso-8859-1", Matchers.equalTo(response.getCharacterEncoding()));
}
@Test
public void testLocaleAndContentTypeEncoding() throws Exception
{
_server.stop();
MimeTypes.getInferredEncodings().put("text/html", "iso-8859-1");
ContextHandler handler = new ContextHandler();
handler.addLocaleEncoding("ja", "euc-jp");
handler.addLocaleEncoding("zh_CN", "gb18030");
_server.setHandler(handler);
handler.setHandler(new DumpHandler());
_server.start();
Response response = getResponse();
response.getHttpChannel().getRequest().setContext(handler.getServletContext());
response.setContentType("text/html");
assertEquals("iso-8859-1", response.getCharacterEncoding());
// setLocale should change character encoding based on
// locale-encoding-mapping-list
response.setLocale(Locale.JAPAN);
assertEquals("euc-jp", response.getCharacterEncoding());
// setLocale should change character encoding based on
// locale-encoding-mapping-list
response.setLocale(Locale.CHINA);
assertEquals("gb18030", response.getCharacterEncoding());
// setContentType here doesn't define character encoding
response.setContentType("text/html");
assertEquals("gb18030", response.getCharacterEncoding());
// setCharacterEncoding should still be able to change encoding
response.setCharacterEncoding("utf-8");
assertEquals("utf-8", response.getCharacterEncoding());
// setLocale should not override explicit character encoding request
response.setLocale(Locale.JAPAN);
assertEquals("utf-8", response.getCharacterEncoding());
// setContentType should still be able to change encoding
response.setContentType("text/html;charset=gb18030");
assertEquals("gb18030", response.getCharacterEncoding());
// setCharacterEncoding should still be able to change encoding
response.setCharacterEncoding("utf-8");
assertEquals("utf-8", response.getCharacterEncoding());
// getWriter should freeze the character encoding
PrintWriter pw = response.getWriter();
assertEquals("utf-8", response.getCharacterEncoding());
// setCharacterEncoding should no longer be able to change the encoding
response.setCharacterEncoding("iso-8859-1");
assertEquals("utf-8", response.getCharacterEncoding());
// setLocale should not override explicit character encoding request
response.setLocale(Locale.JAPAN);
assertEquals("utf-8", response.getCharacterEncoding());
}
@Test
public void testContentTypeCharacterEncoding() throws Exception

View File

@ -23,8 +23,15 @@ import java.util.Collections;
import java.util.HashSet;
import jakarta.servlet.SessionTrackingMode;
import jakarta.servlet.http.HttpSession;
import jakarta.servlet.http.HttpSessionEvent;
import jakarta.servlet.http.HttpSessionListener;
import org.eclipse.jetty.server.Server;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class SessionHandlerTest
@ -35,7 +42,64 @@ public class SessionHandlerTest
SessionHandler sessionHandler = new SessionHandler();
sessionHandler.setSessionTrackingModes(new HashSet<>(Arrays.asList(SessionTrackingMode.COOKIE, SessionTrackingMode.URL)));
sessionHandler.setSessionTrackingModes(Collections.singleton(SessionTrackingMode.SSL));
assertThrows(IllegalArgumentException.class,() ->
sessionHandler.setSessionTrackingModes(new HashSet<>(Arrays.asList(SessionTrackingMode.SSL, SessionTrackingMode.URL))));
assertThrows(IllegalArgumentException.class, () -> sessionHandler.setSessionTrackingModes(new HashSet<>(Arrays.asList(SessionTrackingMode.SSL, SessionTrackingMode.URL))));
}
@Test
public void testSessionListenerOrdering()
throws Exception
{
final StringBuffer result = new StringBuffer();
class Listener1 implements HttpSessionListener
{
@Override
public void sessionCreated(HttpSessionEvent se)
{
result.append("Listener1 create;");
}
@Override
public void sessionDestroyed(HttpSessionEvent se)
{
result.append("Listener1 destroy;");
}
}
class Listener2 implements HttpSessionListener
{
@Override
public void sessionCreated(HttpSessionEvent se)
{
result.append("Listener2 create;");
}
@Override
public void sessionDestroyed(HttpSessionEvent se)
{
result.append("Listener2 destroy;");
}
}
Server server = new Server();
SessionHandler sessionHandler = new SessionHandler();
try
{
sessionHandler.addEventListener(new Listener1());
sessionHandler.addEventListener(new Listener2());
sessionHandler.setServer(server);
sessionHandler.start();
Session session = new Session(sessionHandler, new SessionData("aa", "_", "0.0", 0, 0, 0, 0));
sessionHandler.callSessionCreatedListeners(session);
sessionHandler.callSessionDestroyedListeners(session);
assertEquals("Listener1 create;Listener2 create;Listener2 destroy;Listener1 destroy;", result.toString());
}
finally
{
sessionHandler.stop();
}
}
}

View File

@ -67,7 +67,7 @@ import org.slf4j.LoggerFactory;
* resources could be found. If false, then a welcome
* file must exist on disk. If "exact", then exact
* servlet matches are supported without an existing file.
* Default is true.
* Default is false.
*
* This must be false if you want directory listings,
* but have index.jsp in your welcome file list.

View File

@ -47,7 +47,7 @@ public class CertificateUtils
}
if (!store.exists())
throw new IllegalStateException("no valid keystore");
throw new IllegalStateException(store.getName() + " is not a valid keystore");
try (InputStream inStream = store.getInputStream())
{

View File

@ -217,7 +217,7 @@ public class SslContextFactoryTest
cf.setTrustStorePath("/foo");
cf.start();
});
assertThat(x.getMessage(), containsString("no valid keystore"));
assertThat(x.getMessage(), equalTo("/foo is not a valid keystore"));
}
}

View File

@ -82,7 +82,7 @@
* resources could be found. If false, then a welcome
* file must exist on disk. If "exact", then exact
* servlet matches are supported without an existing file.
* Default is true.
* Default is false.
*
* This must be false if you want directory listings,
* but have index.jsp in your welcome file list.

View File

@ -1,2 +1,3 @@
# Jetty Logging using jetty-slf4j-impl
org.eclipse.jetty.logging.appender.MESSAGE_ESCAPE=false
#org.eclipse.jetty.LEVEL=DEBUG

View File

@ -64,7 +64,7 @@
<dependency>
<groupId>org.apache.openwebbeans</groupId>
<artifactId>openwebbeans-jetty9</artifactId>
<version>2.0.11</version>
<version>2.0.15</version>
</dependency>
</dependencies>
</project>

View File

@ -1,9 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure id="webAppCtx" class="org.eclipse.jetty.webapp.WebAppContext">
<!-- Enable OWB ServletContainerInitializer
See: https://issues.apache.org/jira/browse/OWB-1296 -->
<Call name="setInitParameter">
<Arg>openwebbeans.web.sci.active</Arg>
<Arg>true</Arg>
</Call>
<New id="BeanManager" class="org.eclipse.jetty.plus.jndi.Resource">
<Arg>
<Ref refid="webAppCtx"/>
<Ref refid="webAppCtx" />
</Arg>
<Arg>BeanManager</Arg>
<Arg>