Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/jetty-12.0.x' into jetty-12.1.x
Browse files Browse the repository at this point in the history
  • Loading branch information
janbartel committed May 28, 2024
2 parents c50d325 + 7e36f3c commit 810ecca
Show file tree
Hide file tree
Showing 85 changed files with 781 additions and 635 deletions.
4 changes: 2 additions & 2 deletions documentation/jetty/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
<name>Documentation :: Content Root</name>

<properties>
<!-- build cache must be skipped for antora goal to work and for run.jetty.classpath value to be computed properly -->
<maven.build.cache.skipCache>true</maven.build.cache.skipCache>
<!-- build cache must be disabled for antora goal to work and for run.jetty.classpath value to be computed properly -->
<maven.build.cache.enabled>false</maven.build.cache.enabled>
</properties>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,24 +147,19 @@ interface ContentListener extends AsyncContentListener
* Callback method invoked when the response content has been received, parsed and there is demand.
* This method may be invoked multiple times, and the {@code content} buffer
* must be consumed (or copied) before returning from this method.
* This method is also always invoked when content arrives as demand is automatically registered on return.
*
* @param response the response containing the response line data and the headers
* @param content the content bytes received
* @throws Exception an uncaught exception will abort the response and eventually reclaim the ByteBuffer, if applicable
*/
void onContent(Response response, ByteBuffer content);
void onContent(Response response, ByteBuffer content) throws Exception;

@Override
default void onContent(Response response, Content.Chunk chunk, Runnable demander)
default void onContent(Response response, Content.Chunk chunk, Runnable demander) throws Exception
{
try
{
onContent(response, chunk.getByteBuffer());
demander.run();
}
catch (Throwable x)
{
response.abort(x);
}
onContent(response, chunk.getByteBuffer());
demander.run();
}
}

Expand All @@ -175,7 +170,19 @@ default void onContent(Response response, Content.Chunk chunk, Runnable demander
*/
interface AsyncContentListener extends ContentSourceListener
{
void onContent(Response response, Content.Chunk chunk, Runnable demander);
/**
* Callback method invoked when the response content has been received, parsed and there is demand.
* The {@code chunk} must be consumed, copied, or retained before returning from this method as
* it is then automatically released.
* The {@code demander} must be run before this method may be invoked again.
*
* @param response the response containing the response line data and the headers
* @param chunk the chunk received
* @param demander the runnable to be run to demand the next chunk
* @throws Exception an uncaught exception will abort the response, release the chunk and fail the content source
* from which the chunk was read from
*/
void onContent(Response response, Content.Chunk chunk, Runnable demander) throws Exception;

@Override
default void onContentSource(Response response, Content.Source contentSource)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,31 @@

public class AsyncContentListenerTest
{
@Test
public void testOnContentThrowingException()
{
TestSource originalSource = new TestSource(
Content.Chunk.from(ByteBuffer.wrap(new byte[] {1}), false),
Content.Chunk.from(ByteBuffer.wrap(new byte[] {2}), false),
Content.Chunk.from(ByteBuffer.wrap(new byte[] {3}), true)
);
Response.AsyncContentListener asyncContentListener = (response, chunk, demander) ->
{
throw new NumberFormatException();
};

HttpResponse response = new HttpResponse(new HttpRequest(new HttpClient(), new HttpConversation(), URI.create("http://localhost")));
asyncContentListener.onContentSource(response, originalSource);

// Assert that the source was failed.
Content.Chunk lastChunk = originalSource.read();
assertThat(Content.Chunk.isFailure(lastChunk, true), is(true));
assertThat(lastChunk.getFailure(), instanceOf(NumberFormatException.class));

// Assert that the response was aborted.
assertThat(response.getRequest().getAbortCause(), instanceOf(NumberFormatException.class));
}

@Test
public void testTransientFailureBecomesTerminal()
{
Expand Down Expand Up @@ -70,7 +95,7 @@ public void testTransientFailureBecomesTerminal()
originalSource.close();
}

private static class TestSource extends ChunksContentSource implements Closeable
static class TestSource extends ChunksContentSource implements Closeable
{
private Content.Chunk[] chunks;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.client;

import java.net.URI;
import java.nio.ByteBuffer;

import org.eclipse.jetty.client.AsyncContentListenerTest.TestSource;
import org.eclipse.jetty.client.transport.HttpConversation;
import org.eclipse.jetty.client.transport.HttpRequest;
import org.eclipse.jetty.client.transport.HttpResponse;
import org.eclipse.jetty.io.Content;
import org.junit.jupiter.api.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;

public class ContentListenerTest
{
@Test
public void testOnContentThrowingException()
{
TestSource originalSource = new TestSource(
Content.Chunk.from(ByteBuffer.wrap(new byte[] {1}), false),
Content.Chunk.from(ByteBuffer.wrap(new byte[] {2}), false),
Content.Chunk.from(ByteBuffer.wrap(new byte[] {3}), true)
);
Response.ContentListener contentListener = (response, content) ->
{
throw new NumberFormatException();
};

HttpResponse response = new HttpResponse(new HttpRequest(new HttpClient(), new HttpConversation(), URI.create("http://localhost")));
contentListener.onContentSource(response, originalSource);

// Assert that the source was failed.
Content.Chunk lastChunk = originalSource.read();
assertThat(Content.Chunk.isFailure(lastChunk, true), is(true));
assertThat(lastChunk.getFailure(), instanceOf(NumberFormatException.class));

// Assert that the response was aborted.
assertThat(response.getRequest().getAbortCause(), instanceOf(NumberFormatException.class));
}
}
2 changes: 1 addition & 1 deletion jetty-core/jetty-deploy/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
{
requires java.xml;
requires org.eclipse.jetty.xml;
requires org.eclipse.jetty.server;
requires transitive org.eclipse.jetty.server;
requires org.slf4j;

// Only required if using JMX.
Expand Down
7 changes: 6 additions & 1 deletion jetty-core/jetty-http-spi/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
// ========================================================================
//

import com.sun.net.httpserver.spi.HttpServerProvider;
import org.eclipse.jetty.http.spi.JettyHttpServerProvider;

module org.eclipse.jetty.http.spi
{
requires transitive jdk.httpserver;
Expand All @@ -19,4 +22,6 @@
requires transitive org.eclipse.jetty.util;

exports org.eclipse.jetty.http.spi;
}

provides HttpServerProvider with JettyHttpServerProvider;
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
import java.util.Map;

import org.eclipse.jetty.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Cookie parser
Expand All @@ -31,9 +29,8 @@
@Deprecated (forRemoval = true)
public class CookieCache implements CookieParser.Handler, ComplianceViolation.Listener
{
protected static final Logger LOG = LoggerFactory.getLogger(CookieCache.class);
protected final List<String> _rawFields = new ArrayList<>();
protected List<HttpCookie> _cookieList;
private final List<String> _rawFields = new ArrayList<>();
private List<HttpCookie> _cookieList;
private final CookieParser _parser;
private List<ComplianceViolation.Event> _violations;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
@Deprecated
public class CookieCutter implements CookieParser
{
protected static final Logger LOG = LoggerFactory.getLogger(CookieCutter.class);
private static final Logger LOG = LoggerFactory.getLogger(CookieCutter.class);

private final CookieParser.Handler _handler;
private final CookieCompliance _complianceMode;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

package org.eclipse.jetty.http;

import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
Expand All @@ -24,6 +25,7 @@
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
Expand Down Expand Up @@ -596,7 +598,41 @@ default Enumeration<String> getFieldNames()
*/
default Set<String> getFieldNamesCollection()
{
return stream().map(HttpField::getName).collect(Collectors.toSet());
Set<HttpHeader> seenByHeader = EnumSet.noneOf(HttpHeader.class);
Set<String> seenByName = null;
List<String> list = new ArrayList<>(size());

for (HttpField f : this)
{
HttpHeader header = f.getHeader();
if (header == null)
{
if (seenByName == null)
seenByName = new TreeSet<>(String::compareToIgnoreCase);
if (seenByName.add(f.getName()))
list.add(f.getName());
}
else if (seenByHeader.add(header))
{
list.add(f.getName());
}
}

// use the list to retain a rough ordering
return new AbstractSet<>()
{
@Override
public Iterator<String> iterator()
{
return list.iterator();
}

@Override
public int size()
{
return list.size();
}
};
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
*/
public class HttpParser
{
public static final Logger LOG = LoggerFactory.getLogger(HttpParser.class);
private static final Logger LOG = LoggerFactory.getLogger(HttpParser.class);
public static final int INITIAL_URI_LENGTH = 256;
private static final int MAX_CHUNK_LENGTH = Integer.MAX_VALUE / 16 - 16;
private static final String UNMATCHED_VALUE = "\u0000";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
import java.util.Locale;

import org.eclipse.jetty.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.eclipse.jetty.http.CookieCompliance.Violation.ATTRIBUTES;
import static org.eclipse.jetty.http.CookieCompliance.Violation.ATTRIBUTE_VALUES;
Expand All @@ -34,8 +32,6 @@
*/
public class RFC6265CookieParser implements CookieParser
{
protected static final Logger LOG = LoggerFactory.getLogger(RFC6265CookieParser.class);

private final CookieParser.Handler _handler;
private final CookieCompliance _complianceMode;
private final ComplianceViolation.Listener _complianceListener;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,47 @@ public void testGet()
assertThrows(NoSuchElementException.class, () -> header.getField(2));
}

@Test
public void testCaseInsensitive()
{
HttpFields header = HttpFields.build()
.add("expect", "100")
.add("RaNdOm", "value")
.add("Accept-Charset", "UTF-8")
.add("accept-charset", "UTF-16")
.add("foo-bar", "one")
.add("Foo-Bar", "two")
.asImmutable();

assertThat(header.get("expect"), is("100"));
assertThat(header.get("Expect"), is("100"));
assertThat(header.get("EXPECT"), is("100"));
assertThat(header.get("eXpEcT"), is("100"));
assertThat(header.get(HttpHeader.EXPECT), is("100"));

assertThat(header.get("random"), is("value"));
assertThat(header.get("Random"), is("value"));
assertThat(header.get("RANDOM"), is("value"));
assertThat(header.get("rAnDoM"), is("value"));
assertThat(header.get("RaNdOm"), is("value"));

assertThat(header.get("Accept-Charset"), is("UTF-8"));
assertThat(header.get("accept-charset"), is("UTF-8"));
assertThat(header.get(HttpHeader.ACCEPT_CHARSET), is("UTF-8"));

assertThat(header.getValuesList("Accept-Charset"), contains("UTF-8", "UTF-16"));
assertThat(header.getValuesList("accept-charset"), contains("UTF-8", "UTF-16"));
assertThat(header.getValuesList(HttpHeader.ACCEPT_CHARSET), contains("UTF-8", "UTF-16"));

assertThat(header.get("foo-bar"), is("one"));
assertThat(header.get("Foo-Bar"), is("one"));
assertThat(header.getValuesList("foo-bar"), contains("one", "two"));
assertThat(header.getValuesList("Foo-Bar"), contains("one", "two"));

// We know the order of the set is deterministic
assertThat(header.getFieldNamesCollection(), contains("expect", "RaNdOm", "Accept-Charset", "foo-bar"));
}

@ParameterizedTest
@MethodSource("mutables")
public void testGetKnown(HttpFields.Mutable header)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public int length()
public byte[] take()
{
if (_length == 0)
return BufferUtil.EMPTY_BUFFER.array();
return BufferUtil.EMPTY_BYTES;
byte[] bytes = new byte[_length];
int offset = 0;
for (Chunk chunk : _chunks)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
public abstract class SelectorManager extends ContainerLifeCycle implements Dumpable
{
public static final int DEFAULT_CONNECT_TIMEOUT = 15000;
protected static final Logger LOG = LoggerFactory.getLogger(SelectorManager.class);
private static final Logger LOG = LoggerFactory.getLogger(SelectorManager.class);

private final Executor executor;
private final Scheduler scheduler;
Expand Down
4 changes: 2 additions & 2 deletions jetty-core/jetty-plus/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@

module org.eclipse.jetty.plus
{
requires org.eclipse.jetty.security;
requires transitive org.eclipse.jetty.security;
requires org.eclipse.jetty.util;
requires org.slf4j;

requires transitive java.naming;
// Only required if using DataSourceLoginService.
requires static java.sql;
requires static transitive java.sql;

exports org.eclipse.jetty.plus.annotation;
exports org.eclipse.jetty.plus.jndi;
Expand Down
2 changes: 1 addition & 1 deletion jetty-core/jetty-security/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
requires transitive org.eclipse.jetty.util;
requires transitive org.slf4j;
requires static java.security.jgss;
requires static java.sql;
requires static transitive java.sql;

exports org.eclipse.jetty.security;
exports org.eclipse.jetty.security.authentication;
Expand Down
Loading

0 comments on commit 810ecca

Please sign in to comment.