Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #12612 - Use Compression classes for client decoding. #12613

Merged
merged 21 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f53dd63
Fixes #12612 - Use Compression classes for client decoding.
sbordet Dec 5, 2024
7a06103
Updated OSGi dependencies, now that jetty-client depends on jetty-com…
sbordet Dec 5, 2024
f6ce763
Now using ServiceLoader to load the Compression implementations.
sbordet Dec 5, 2024
8abb27d
Reorganized the Jetty modules so that the client can use them without…
sbordet Dec 5, 2024
05d6b15
* Removed EncoderSink.canEncode(), since it is too late to skip encod…
sbordet Dec 6, 2024
4463087
Fixes #12618 - Update jetty-compression for OSGi.
sbordet Dec 6, 2024
a1ed64d
Fixed client.mod dependencies.
sbordet Dec 6, 2024
f7806b8
Updated test dependencies.
sbordet Dec 7, 2024
4fb2116
Updated documentation and migration guide.
sbordet Dec 8, 2024
8e2b8ac
Updates from reviews.
sbordet Dec 9, 2024
f2eb965
Fixed typo.
sbordet Dec 9, 2024
601b940
Fixed typos.
sbordet Dec 9, 2024
c55a3d4
Updates from review.
sbordet Dec 9, 2024
e3cf38f
Updated test after implementing minCompressSize.
sbordet Dec 10, 2024
72bb0b1
Updates from review.
sbordet Dec 12, 2024
cb2c159
Updates from review.
sbordet Dec 13, 2024
1c32a08
Fixed ContentSourceTransformer.
sbordet Dec 13, 2024
78bfe48
Merged branch 'jetty-12.1.x' into 'fix/jetty-12.1.x/12612/client-comp…
sbordet Dec 13, 2024
fc23160
Implemented support for quality values in Accept-Encoding.
sbordet Dec 15, 2024
50067d7
Added module compression-all and correspondent distribution test.
sbordet Dec 15, 2024
9660e12
Added module documentation.
sbordet Dec 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions jetty-core/jetty-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@
<artifactId>jetty-jmx</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.compression</groupId>
<artifactId>jetty-compression-common</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.compression</groupId>
<artifactId>jetty-compression-gzip</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
Expand Down Expand Up @@ -77,6 +85,17 @@

<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Import-Package>${osgi.slf4j.import.packages},org.eclipse.jetty.compression;resolution:=optional,*</Import-Package>
<Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)";resolution:=optional, osgi.serviceloader; filter:="(osgi.serviceloader=org.eclipse.jetty.compression.Compression)";resolution:=optional;cardinality:=multiple</Require-Capability>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
Expand Down
2 changes: 2 additions & 0 deletions jetty-core/jetty-client/src/main/config/modules/client.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ client
lib/jetty-alpn-client-${jetty.version}.jar
lib/jetty-alpn-java-client-${jetty.version}.jar
lib/jetty-client-${jetty.version}.jar
lib/jetty-compression-common-${jetty.version}.jar
lib/jetty-compression-gzip-${jetty.version}.jar
4 changes: 4 additions & 0 deletions jetty-core/jetty-client/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
requires org.eclipse.jetty.alpn.client;
requires org.slf4j;

requires org.eclipse.jetty.compression;
requires org.eclipse.jetty.compression.gzip;
gregw marked this conversation as resolved.
Show resolved Hide resolved
requires transitive org.eclipse.jetty.http;

// Only required if using JMX.
Expand All @@ -30,4 +32,6 @@

exports org.eclipse.jetty.client.jmx to
org.eclipse.jetty.jmx;

uses org.eclipse.jetty.compression.Compression;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,68 +13,50 @@

package org.eclipse.jetty.client;

import java.nio.ByteBuffer;
import java.text.DecimalFormat;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.io.RetainableByteBuffer;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.util.component.ContainerLifeCycle;

/**
* {@link ContentDecoder} decodes content bytes of a response.
* <p>Groups abstractions related to response content decoding.</p>
*
* @see Factory
* @see Factories
* @see HttpClient#getContentDecoderFactories()
*/
public interface ContentDecoder
{
/**
* <p>Processes the response just before the decoding of the response content.</p>
* <p>Typical processing may involve modifying the response headers, for example
* by temporarily removing the {@code Content-Length} header, or modifying the
* {@code Content-Encoding} header.</p>
* <p>A factory for {@link Content.Source} that decode response content.</p>
* <p>A {@code Factory} has an {@link #getEncoding() encoding} and a
* {@link #getWeight() weight} that are used in the {@code Accept-Encoding}
* request header and in the {@code Content-Encoding} response headers.</p>
* <p>{@code Factory} instances are configured in {@link HttpClient} via
* {@link HttpClient#getContentDecoderFactories()}.</p>
*/
public default void beforeDecoding(Response response)
{
}

/**
* <p>Decodes the bytes in the given {@code buffer} and returns the decoded bytes.</p>
* <p>The returned {@link RetainableByteBuffer} <b>will</b> eventually be released via
* {@link RetainableByteBuffer#release()} by the code that called this method.</p>
*
* @param buffer the buffer containing encoded bytes
* @return a buffer containing decoded bytes
*/
public abstract RetainableByteBuffer decode(ByteBuffer buffer);

/**
* <p>Processes the exchange after the response content has been decoded.</p>
* <p>Typical processing may involve modifying the response headers, for example
* updating the {@code Content-Length} header to the length of the decoded
* response content.
*/
public default void afterDecoding(Response response)
{
}

/**
* Factory for {@link ContentDecoder}s; subclasses must implement {@link #newContentDecoder()}.
* <p>
* {@link Factory} have an {@link #getEncoding() encoding}, which is the string used in
* {@code Accept-Encoding} request header and in {@code Content-Encoding} response headers.
* <p>
* {@link Factory} instances are configured in {@link HttpClient} via
* {@link HttpClient#getContentDecoderFactories()}.
*/
public abstract static class Factory
abstract class Factory extends ContainerLifeCycle
{
private final String encoding;
private final float weight;

protected Factory(String encoding)
{
this.encoding = encoding;
this(encoding, -1F);
}

protected Factory(String encoding, float weight)
{
this.encoding = Objects.requireNonNull(encoding);
if (weight != -1F && !(weight >= 0F && weight <= 1F))
throw new IllegalArgumentException("Invalid weight: " + weight);
this.weight = weight;
}

/**
Expand All @@ -85,6 +67,14 @@ public String getEncoding()
return encoding;
}

/**
* @return the weight (between 0 and 1, at most 3 decimal digits) to use for the {@code Accept-Encoding} request header
*/
public float getWeight()
{
return weight;
}

@Override
public boolean equals(Object obj)
{
Expand All @@ -102,14 +92,16 @@ public int hashCode()
}

/**
* Factory method for {@link ContentDecoder}s
* <p>Creates a {@link Content.Source} that decodes the
* chunks of the given {@link Content.Source} parameter.</p>
*
* @return a new instance of a {@link ContentDecoder}
* @param contentSource the encoded {@link Content.Source}
* @return the decoded {@link Content.Source}
*/
public abstract ContentDecoder newContentDecoder();
public abstract Content.Source newDecoderContentSource(Content.Source contentSource);
}

public static class Factories implements Iterable<ContentDecoder.Factory>
class Factories extends ContainerLifeCycle implements Iterable<ContentDecoder.Factory>
{
private final Map<String, Factory> factories = new LinkedHashMap<>();
private HttpField acceptEncodingField;
Expand All @@ -125,6 +117,11 @@ public Iterator<Factory> iterator()
return factories.values().iterator();
}

public boolean isEmpty()
{
return factories.isEmpty();
}

public void clear()
{
factories.clear();
Expand All @@ -134,8 +131,20 @@ public void clear()
public Factory put(Factory factory)
{
Factory result = factories.put(factory.getEncoding(), factory);
String value = String.join(",", factories.keySet());
acceptEncodingField = new HttpField(HttpHeader.ACCEPT_ENCODING, value);
updateBean(result, factory, true);

StringBuilder header = new StringBuilder();
factories.forEach((encoding, value) ->
{
if (!header.isEmpty())
header.append(", ");
header.append(encoding);
float weight = value.getWeight();
if (weight != -1F)
header.append(";q=").append(new DecimalFormat("#.###").format(weight));
});
acceptEncodingField = new HttpField(HttpHeader.ACCEPT_ENCODING, header.toString());

return result;
}
}
Expand Down

This file was deleted.

Loading
Loading