diff --git a/commafeed-server/pom.xml b/commafeed-server/pom.xml
index 6c998751..e4751b71 100644
--- a/commafeed-server/pom.xml
+++ b/commafeed-server/pom.xml
@@ -456,11 +456,6 @@
ayza-for-apache5
10.1.0
-
- org.brotli
- dec
- 0.1.2
-
com.github.seancfoley
ipaddress
diff --git a/commafeed-server/src/main/java/com/commafeed/backend/HttpClientFactory.java b/commafeed-server/src/main/java/com/commafeed/backend/HttpClientFactory.java
index ff6c88b1..70429c86 100644
--- a/commafeed-server/src/main/java/com/commafeed/backend/HttpClientFactory.java
+++ b/commafeed-server/src/main/java/com/commafeed/backend/HttpClientFactory.java
@@ -19,27 +19,27 @@ import nl.altindag.ssl.apache5.util.Apache5SslUtils;
import org.apache.hc.client5.http.DnsResolver;
import org.apache.hc.client5.http.SystemDefaultDnsResolver;
+import org.apache.hc.client5.http.async.methods.InflatingAsyncDataConsumer;
+import org.apache.hc.client5.http.async.methods.InflatingGzipDataConsumer;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.TlsConfig;
-import org.apache.hc.client5.http.entity.DeflateInputStream;
-import org.apache.hc.client5.http.entity.InputStreamFactory;
import org.apache.hc.client5.http.entity.compress.ContentCoding;
import org.apache.hc.client5.http.impl.DefaultRedirectStrategy;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
-import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
-import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
-import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
-import org.apache.hc.client5.http.io.HttpClientConnectionManager;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
+import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
+import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.protocol.RedirectStrategy;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.message.BasicHeader;
+import org.apache.hc.core5.http.nio.AsyncDataConsumer;
import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
-import org.brotli.dec.BrotliInputStream;
import java.net.InetAddress;
import java.net.URI;
@@ -48,7 +48,7 @@ import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.SequencedMap;
-import java.util.zip.GZIPInputStream;
+import java.util.function.UnaryOperator;
@Singleton
@RequiredArgsConstructor
@@ -60,8 +60,8 @@ public class HttpClientFactory {
private final CommaFeedConfiguration config;
private final CommaFeedVersion version;
- public CloseableHttpClient newClient(int poolSize) {
- PoolingHttpClientConnectionManager connectionManager =
+ public CloseableHttpAsyncClient newClient(int poolSize) {
+ PoolingAsyncClientConnectionManager connectionManager =
newConnectionManager(config, poolSize);
String userAgent =
config.httpClient()
@@ -74,42 +74,46 @@ public class HttpClientFactory {
return newClient(config, connectionManager, userAgent);
}
- private CloseableHttpClient newClient(
+ private CloseableHttpAsyncClient newClient(
CommaFeedConfiguration config,
- HttpClientConnectionManager connectionManager,
+ PoolingAsyncClientConnectionManager connectionManager,
String userAgent) {
List headers = new ArrayList<>();
headers.add(new BasicHeader(HttpHeaders.ACCEPT_LANGUAGE, "en"));
headers.add(new BasicHeader(HttpHeaders.PRAGMA, "No-cache"));
headers.add(new BasicHeader(HttpHeaders.CACHE_CONTROL, "no-cache"));
- SequencedMap contentDecoderMap = new LinkedHashMap<>();
- contentDecoderMap.put(ContentCoding.GZIP.token(), GZIPInputStream::new);
- contentDecoderMap.put(ContentCoding.DEFLATE.token(), DeflateInputStream::new);
- contentDecoderMap.put(ContentCoding.BROTLI.token(), BrotliInputStream::new);
+ SequencedMap> contentDecoderMap =
+ new LinkedHashMap<>();
+ contentDecoderMap.put(
+ ContentCoding.DEFLATE.token(), d -> new InflatingAsyncDataConsumer(d, null));
+ contentDecoderMap.put(ContentCoding.GZIP.token(), InflatingGzipDataConsumer::new);
RedirectStrategy redirectStrategy =
config.httpClient().blockLocalAddresses()
? new BlockLocalAddressesRedirectStrategy(DNS_RESOLVER)
: new DefaultRedirectStrategy();
- return HttpClientBuilder.create()
- .disableConnectionState()
- .useSystemProperties()
- .disableAutomaticRetries()
- .disableCookieManagement()
- .setUserAgent(userAgent)
- .setDefaultHeaders(headers)
- .setConnectionManager(connectionManager)
- .evictExpiredConnections()
- .evictIdleConnections(
- TimeValue.of(config.httpClient().idleConnectionsEvictionInterval()))
- .setContentDecoderRegistry(new LinkedHashMap<>(contentDecoderMap))
- .setRedirectStrategy(redirectStrategy)
- .build();
+ CloseableHttpAsyncClient client =
+ HttpAsyncClients.custom()
+ .disableConnectionState()
+ .useSystemProperties()
+ .disableAutomaticRetries()
+ .disableCookieManagement()
+ .setUserAgent(userAgent)
+ .setDefaultHeaders(headers)
+ .setConnectionManager(connectionManager)
+ .evictExpiredConnections()
+ .evictIdleConnections(
+ TimeValue.of(config.httpClient().idleConnectionsEvictionInterval()))
+ .setContentDecoderMap(new LinkedHashMap<>(contentDecoderMap))
+ .setRedirectStrategy(redirectStrategy)
+ .build();
+ client.start();
+ return client;
}
- private PoolingHttpClientConnectionManager newConnectionManager(
+ private PoolingAsyncClientConnectionManager newConnectionManager(
CommaFeedConfiguration config, int poolSize) {
SSLFactory sslFactory =
SSLFactory.builder().withUnsafeTrustMaterial().withUnsafeHostnameVerifier().build();
@@ -118,8 +122,8 @@ public class HttpClientFactory {
? new BlockLocalAddressesDnsResolver(DNS_RESOLVER)
: DNS_RESOLVER;
- return PoolingHttpClientConnectionManagerBuilder.create()
- .setTlsSocketStrategy(Apache5SslUtils.toTlsSocketStrategy(sslFactory))
+ return PoolingAsyncClientConnectionManagerBuilder.create()
+ .setTlsStrategy(Apache5SslUtils.toTlsStrategy(sslFactory))
.setDefaultConnectionConfig(
ConnectionConfig.custom()
.setConnectTimeout(Timeout.of(config.httpClient().connectTimeout()))
@@ -129,6 +133,7 @@ public class HttpClientFactory {
.build())
.setDefaultTlsConfig(
TlsConfig.custom()
+ .setVersionPolicy(HttpVersionPolicy.NEGOTIATE)
.setHandshakeTimeout(
Timeout.of(config.httpClient().sslHandshakeTimeout()))
.build())
diff --git a/commafeed-server/src/main/java/com/commafeed/backend/HttpGetter.java b/commafeed-server/src/main/java/com/commafeed/backend/HttpGetter.java
index 9c44c626..6dd28f06 100644
--- a/commafeed-server/src/main/java/com/commafeed/backend/HttpGetter.java
+++ b/commafeed-server/src/main/java/com/commafeed/backend/HttpGetter.java
@@ -6,7 +6,6 @@ import com.commafeed.CommaFeedConfiguration.HttpClientCache;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Iterables;
-import com.google.common.io.ByteStreams;
import com.google.common.net.HttpHeaders;
import jakarta.inject.Singleton;
@@ -21,35 +20,44 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
+import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
+import org.apache.hc.client5.http.async.methods.SimpleRequestProducer;
import org.apache.hc.client5.http.config.RequestConfig;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.protocol.RedirectLocations;
import org.apache.hc.client5.http.utils.DateUtils;
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.concurrent.FutureCallback;
+import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.Message;
import org.apache.hc.core5.http.NameValuePair;
-import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
+import org.apache.hc.core5.http.nio.AsyncRequestProducer;
+import org.apache.hc.core5.http.nio.AsyncResponseConsumer;
+import org.apache.hc.core5.http.nio.entity.AbstractBinAsyncEntityConsumer;
+import org.apache.hc.core5.http.nio.support.BasicResponseConsumer;
import org.apache.hc.core5.util.Timeout;
import org.jboss.resteasy.reactive.common.headers.CacheControlDelegate;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.net.URI;
+import java.nio.ByteBuffer;
import java.time.Duration;
import java.time.Instant;
import java.time.InstantSource;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
-/** Smart HTTP getter: handles gzip, ssl, last modified and etag headers */
+/** Smart HTTP getter: handles gzip, ssl, last modified, and etag headers */
@Singleton
@Slf4j
public class HttpGetter {
private final CommaFeedConfiguration config;
private final InstantSource instantSource;
- private final CloseableHttpClient client;
+ private final CloseableHttpAsyncClient client;
private final Cache cache;
public HttpGetter(
@@ -157,63 +165,83 @@ public class HttpGetter {
.setProtocolUpgradeEnabled(false)
.build());
- return client.execute(
- request.toClassicHttpRequest(),
- context,
- resp -> {
- byte[] content =
- resp.getEntity() == null
- ? null
- : toByteArray(
- resp.getEntity(),
- config.httpClient().maxResponseSize().asLongValue());
- int code = resp.getCode();
- String lastModifiedHeader =
- Optional.ofNullable(resp.getFirstHeader(HttpHeaders.LAST_MODIFIED))
- .map(NameValuePair::getValue)
- .map(StringUtils::trimToNull)
- .orElse(null);
- String eTagHeader =
- Optional.ofNullable(resp.getFirstHeader(HttpHeaders.ETAG))
- .map(NameValuePair::getValue)
- .map(StringUtils::trimToNull)
- .orElse(null);
+ AsyncRequestProducer req = SimpleRequestProducer.create(request.toSimpleHttpRequest());
+ AsyncResponseConsumer> resp =
+ new BasicResponseConsumer<>(
+ new LimitedBytesEntityConsumer(
+ config.httpClient().maxResponseSize().asLongValue()));
+ FutureCallback> callback =
+ new FutureCallback<>() {
+ @Override
+ public void completed(
+ Message result) {}
- CacheControl cacheControl =
- Optional.ofNullable(resp.getFirstHeader(HttpHeaders.CACHE_CONTROL))
- .map(NameValuePair::getValue)
- .map(StringUtils::trimToNull)
- .map(HttpGetter::toCacheControl)
- .orElse(null);
+ @Override
+ public void failed(Exception ex) {}
- Instant retryAfter =
- Optional.ofNullable(resp.getFirstHeader(HttpHeaders.RETRY_AFTER))
- .map(NameValuePair::getValue)
- .map(StringUtils::trimToNull)
- .map(this::toInstant)
- .orElse(null);
+ @Override
+ public void cancelled() {}
+ };
+ Future> future =
+ client.execute(req, resp, context, callback);
- String contentType =
- Optional.ofNullable(resp.getEntity())
- .map(HttpEntity::getContentType)
- .orElse(null);
- String urlAfterRedirect =
- Optional.ofNullable(context.getRedirectLocations())
- .map(RedirectLocations::getAll)
- .map(l -> Iterables.getLast(l, null))
- .map(URI::toString)
- .orElse(request.getUrl());
+ try {
+ Message message = future.get();
- return new HttpResponse(
- code,
- lastModifiedHeader,
- eTagHeader,
- cacheControl,
- retryAfter,
- content,
- contentType,
- urlAfterRedirect);
- });
+ byte[] content = message.getBody();
+ int code = message.getHead().getCode();
+ String lastModifiedHeader =
+ Optional.ofNullable(message.getHead().getFirstHeader(HttpHeaders.LAST_MODIFIED))
+ .map(NameValuePair::getValue)
+ .map(StringUtils::trimToNull)
+ .orElse(null);
+ String eTagHeader =
+ Optional.ofNullable(message.getHead().getFirstHeader(HttpHeaders.ETAG))
+ .map(NameValuePair::getValue)
+ .map(StringUtils::trimToNull)
+ .orElse(null);
+
+ CacheControl cacheControl =
+ Optional.ofNullable(message.getHead().getFirstHeader(HttpHeaders.CACHE_CONTROL))
+ .map(NameValuePair::getValue)
+ .map(StringUtils::trimToNull)
+ .map(HttpGetter::toCacheControl)
+ .orElse(null);
+
+ Instant retryAfter =
+ Optional.ofNullable(message.getHead().getFirstHeader(HttpHeaders.RETRY_AFTER))
+ .map(NameValuePair::getValue)
+ .map(StringUtils::trimToNull)
+ .map(this::toInstant)
+ .orElse(null);
+
+ String contentType =
+ Optional.ofNullable(message.getHead().getFirstHeader(HttpHeaders.CONTENT_TYPE))
+ .map(NameValuePair::getValue)
+ .map(StringUtils::trimToNull)
+ .orElse(null);
+ String urlAfterRedirect =
+ Optional.ofNullable(context.getRedirectLocations())
+ .map(RedirectLocations::getAll)
+ .map(l -> Iterables.getLast(l, null))
+ .map(URI::toString)
+ .orElse(request.getUrl());
+
+ return new HttpResponse(
+ code,
+ lastModifiedHeader,
+ eTagHeader,
+ cacheControl,
+ retryAfter,
+ content,
+ contentType,
+ urlAfterRedirect);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException("interrupted while fetching " + request.getUrl(), e);
+ } catch (ExecutionException e) {
+ throw Lombok.sneakyThrow(e.getCause());
+ }
}
private static CacheControl toCacheControl(String headerValue) {
@@ -237,28 +265,6 @@ public class HttpGetter {
return DateUtils.parseStandardDate(headerValue);
}
- private static byte[] toByteArray(HttpEntity entity, long maxBytes) throws IOException {
- if (entity.getContentLength() > maxBytes) {
- throw new IOException(
- "Response size (%s bytes) exceeds the maximum allowed size (%s bytes)"
- .formatted(entity.getContentLength(), maxBytes));
- }
-
- try (InputStream input = entity.getContent()) {
- if (input == null) {
- return null;
- }
-
- byte[] bytes = ByteStreams.limit(input, maxBytes + 1).readAllBytes();
- if (bytes.length > maxBytes) {
- throw new IOException(
- "Response size exceeds the maximum allowed size (%s bytes)"
- .formatted(maxBytes));
- }
- return bytes;
- }
- }
-
private static Cache newCache(CommaFeedConfiguration config) {
HttpClientCache cacheConfig = config.httpClient().cache();
if (!cacheConfig.enabled()) {
@@ -274,6 +280,43 @@ public class HttpGetter {
.build();
}
+ @RequiredArgsConstructor
+ private static class LimitedBytesEntityConsumer extends AbstractBinAsyncEntityConsumer {
+
+ private final long maxBytes;
+ private final ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ @Override
+ protected void streamStart(ContentType contentType) {}
+
+ @Override
+ protected int capacityIncrement() {
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ protected void data(ByteBuffer src, boolean endOfStream) throws IOException {
+ if (out.size() + src.remaining() > maxBytes) {
+ throw new IOException(
+ "Response size exceeds the maximum allowed size (%s bytes)"
+ .formatted(maxBytes));
+ }
+ byte[] chunk = new byte[src.remaining()];
+ src.get(chunk);
+ out.write(chunk, 0, chunk.length);
+ }
+
+ @Override
+ protected byte[] generateContent() {
+ return out.toByteArray();
+ }
+
+ @Override
+ public void releaseResources() {
+ out.reset();
+ }
+ }
+
public static class SchemeNotAllowedException extends Exception {
private static final long serialVersionUID = 1L;
@@ -336,15 +379,15 @@ public class HttpGetter {
return new HttpRequestBuilder().url(url);
}
- public ClassicHttpRequest toClassicHttpRequest() {
- ClassicHttpRequest req = ClassicRequestBuilder.get(url).build();
+ public SimpleHttpRequest toSimpleHttpRequest() {
+ SimpleRequestBuilder builder = SimpleRequestBuilder.get(url);
if (lastModified != null) {
- req.addHeader(HttpHeaders.IF_MODIFIED_SINCE, lastModified);
+ builder.addHeader(HttpHeaders.IF_MODIFIED_SINCE, lastModified);
}
if (eTag != null) {
- req.addHeader(HttpHeaders.IF_NONE_MATCH, eTag);
+ builder.addHeader(HttpHeaders.IF_NONE_MATCH, eTag);
}
- return req;
+ return builder.build();
}
}
diff --git a/commafeed-server/src/main/java/com/commafeed/backend/service/PushNotificationService.java b/commafeed-server/src/main/java/com/commafeed/backend/service/PushNotificationService.java
index e91fcaf4..0c2cbd92 100644
--- a/commafeed-server/src/main/java/com/commafeed/backend/service/PushNotificationService.java
+++ b/commafeed-server/src/main/java/com/commafeed/backend/service/PushNotificationService.java
@@ -16,26 +16,28 @@ import jakarta.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
-import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.config.RequestConfig;
-import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.NameValuePair;
-import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.apache.hc.core5.net.WWWFormCodec;
import org.apache.hc.core5.util.Timeout;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.ExecutionException;
@Singleton
@Slf4j
public class PushNotificationService {
- private final CloseableHttpClient httpClient;
+ private final CloseableHttpAsyncClient httpClient;
private final Meter meter;
private final CommaFeedConfiguration config;
@@ -97,13 +99,13 @@ public class PushNotificationService {
return;
}
- HttpPost request = new HttpPost(serverUrl + "/" + topic);
- request.setConfig(
+ SimpleRequestBuilder request = SimpleRequestBuilder.post(serverUrl + "/" + topic);
+ request.setRequestConfig(
RequestConfig.custom()
.setResponseTimeout(Timeout.of(config.httpClient().responseTimeout()))
.build());
request.addHeader("Title", feedTitle);
- request.setEntity(new StringEntity(entryTitle, StandardCharsets.UTF_8));
+ request.setBody(entryTitle, ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8));
if (StringUtils.isNotBlank(entryUrl)) {
request.addHeader("Click", entryUrl);
@@ -113,15 +115,7 @@ public class PushNotificationService {
request.addHeader("Authorization", "Bearer " + settings.getUserSecret());
}
- httpClient.execute(
- request,
- response -> {
- if (response.getCode() >= 400) {
- throw new PushNotificationException(
- "ntfy notification failed with status " + response.getCode());
- }
- return null;
- });
+ send("ntfy", request.build());
}
private void sendGotify(
@@ -152,23 +146,15 @@ public class PushNotificationService {
.put("click", new JsonObject().put("url", entryUrl))));
}
- HttpPost request = new HttpPost(serverUrl + "/message");
- request.setConfig(
+ SimpleRequestBuilder request = SimpleRequestBuilder.post(serverUrl + "/message");
+ request.setRequestConfig(
RequestConfig.custom()
.setResponseTimeout(Timeout.of(config.httpClient().responseTimeout()))
.build());
request.addHeader("X-Gotify-Key", token);
- request.setEntity(new StringEntity(json.toString(), ContentType.APPLICATION_JSON));
+ request.setBody(json.toString(), ContentType.APPLICATION_JSON);
- httpClient.execute(
- request,
- response -> {
- if (response.getCode() >= 400) {
- throw new PushNotificationException(
- "gotify notification failed with status " + response.getCode());
- }
- return null;
- });
+ send("gotify", request.build());
}
private void sendPushover(
@@ -194,22 +180,32 @@ public class PushNotificationService {
params.add(new BasicNameValuePair("url", entryUrl));
}
- HttpPost request = new HttpPost("https://api.pushover.net/1/messages.json");
- request.setConfig(
+ SimpleRequestBuilder request =
+ SimpleRequestBuilder.post("https://api.pushover.net/1/messages.json");
+ request.setRequestConfig(
RequestConfig.custom()
.setResponseTimeout(Timeout.of(config.httpClient().responseTimeout()))
.build());
- request.setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8));
+ request.setBody(
+ WWWFormCodec.format(params, StandardCharsets.UTF_8),
+ ContentType.APPLICATION_FORM_URLENCODED.withCharset(StandardCharsets.UTF_8));
- httpClient.execute(
- request,
- response -> {
- if (response.getCode() >= 400) {
- throw new PushNotificationException(
- "pushover notification failed with status " + response.getCode());
- }
- return null;
- });
+ send("pushover", request.build());
+ }
+
+ private void send(String name, SimpleHttpRequest request) throws IOException {
+ try {
+ SimpleHttpResponse response = httpClient.execute(request, null).get();
+ if (response.getCode() >= 400) {
+ throw new PushNotificationException(
+ name + " notification failed with status " + response.getCode());
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IOException("interrupted while sending " + name + " notification", e);
+ } catch (ExecutionException e) {
+ throw new IOException(e.getCause());
+ }
}
public static class PushNotificationException extends RuntimeException {
diff --git a/commafeed-server/src/test/java/com/commafeed/backend/HttpGetterTest.java b/commafeed-server/src/test/java/com/commafeed/backend/HttpGetterTest.java
index 22126aba..d8534d82 100644
--- a/commafeed-server/src/test/java/com/commafeed/backend/HttpGetterTest.java
+++ b/commafeed-server/src/test/java/com/commafeed/backend/HttpGetterTest.java
@@ -29,6 +29,7 @@ import org.mockserver.model.Delay;
import org.mockserver.model.HttpRequest;
import org.mockserver.model.HttpResponse;
import org.mockserver.model.MediaType;
+import org.mockserver.model.Protocol;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -359,8 +360,7 @@ class HttpGetterTest {
IOException e = Assertions.assertThrows(IOException.class, () -> getter.get(this.feedUrl));
Assertions.assertEquals(
- "Response size (100000 bytes) exceeds the maximum allowed size (10000 bytes)",
- e.getMessage());
+ "Response size exceeds the maximum allowed size (10000 bytes)", e.getMessage());
}
@Test
@@ -416,7 +416,7 @@ class HttpGetterTest {
@Nested
class Compression {
- private static final String ACCEPT_ENCODING = "gzip, deflate, br";
+ private static final String ACCEPT_ENCODING = "deflate, gzip";
@Test
void gzip() throws Exception {
@@ -590,4 +590,30 @@ class HttpGetterTest {
UnknownHostException.class, () -> getter.get("http://[::a00:1]")); // 10.0.0.1
}
}
+
+ @Nested
+ class Protocols {
+ @Test
+ void usesHttp2OverTls() throws Exception {
+ mockServerClient
+ .when(HttpRequest.request().withMethod("GET").withProtocol(Protocol.HTTP_2))
+ .respond(HttpResponse.response().withBody("h2"));
+ mockServerClient
+ .when(HttpRequest.request().withMethod("GET").withProtocol(Protocol.HTTP_1_1))
+ .respond(HttpResponse.response().withBody("h1"));
+
+ HttpResult result = getter.get("https://localhost:" + mockServerClient.getPort());
+ Assertions.assertEquals("h2", new String(result.content()));
+ }
+
+ @Test
+ void fallsBackToHttp1OverCleartext() throws Exception {
+ mockServerClient
+ .when(HttpRequest.request().withMethod("GET").withProtocol(Protocol.HTTP_1_1))
+ .respond(HttpResponse.response().withBody("h1"));
+
+ HttpResult result = getter.get("http://localhost:" + mockServerClient.getPort());
+ Assertions.assertEquals("h1", new String(result.content()));
+ }
+ }
}