Files
Athou_commafeed/commafeed-server/src/test/java/com/commafeed/backend/HttpGetterTest.java

225 lines
9.3 KiB
Java
Raw Normal View History

2023-12-18 09:51:27 +01:00
package com.commafeed.backend;
import java.io.IOException;
2024-08-07 08:10:14 +02:00
import java.math.BigInteger;
import java.net.SocketTimeoutException;
import java.util.Arrays;
2023-12-18 09:51:27 +01:00
import java.util.Objects;
2024-08-07 08:10:14 +02:00
import java.util.Optional;
2023-12-18 15:09:04 +01:00
import java.util.concurrent.atomic.AtomicInteger;
2023-12-18 09:51:27 +01:00
import org.apache.commons.io.IOUtils;
2023-12-25 19:41:14 +01:00
import org.apache.hc.client5.http.ConnectTimeoutException;
2024-08-07 08:10:14 +02:00
import org.apache.hc.core5.http.HttpStatus;
2023-12-18 09:51:27 +01:00
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Mockito;
2023-12-18 09:51:27 +01:00
import org.mockserver.client.MockServerClient;
import org.mockserver.junit.jupiter.MockServerExtension;
import org.mockserver.model.ConnectionOptions;
2023-12-18 09:51:27 +01:00
import org.mockserver.model.Delay;
import org.mockserver.model.HttpRequest;
import org.mockserver.model.HttpResponse;
import org.mockserver.model.MediaType;
import com.codahale.metrics.MetricRegistry;
2023-12-18 09:51:27 +01:00
import com.commafeed.CommaFeedConfiguration;
2024-08-07 08:10:14 +02:00
import com.commafeed.CommaFeedVersion;
import com.commafeed.backend.HttpGetter.HttpResponseException;
2023-12-18 09:51:27 +01:00
import com.commafeed.backend.HttpGetter.HttpResult;
import com.commafeed.backend.HttpGetter.NotModifiedException;
import com.google.common.net.HttpHeaders;
2023-12-18 09:51:27 +01:00
2024-08-07 08:10:14 +02:00
import io.quarkus.runtime.configuration.MemorySize;
2023-12-18 09:51:27 +01:00
@ExtendWith(MockServerExtension.class)
class HttpGetterTest {
private static final int TIMEOUT = 10000;
2023-12-18 09:51:27 +01:00
private MockServerClient mockServerClient;
private String feedUrl;
private byte[] feedContent;
private HttpGetter getter;
@BeforeEach
void init(MockServerClient mockServerClient) throws IOException {
this.mockServerClient = mockServerClient;
this.mockServerClient.reset();
this.feedUrl = "http://localhost:" + this.mockServerClient.getPort() + "/";
this.feedContent = IOUtils.toByteArray(Objects.requireNonNull(getClass().getResource("/feed/rss.xml")));
2024-08-07 08:10:14 +02:00
CommaFeedConfiguration config = Mockito.mock(CommaFeedConfiguration.class, Mockito.RETURNS_DEEP_STUBS);
Mockito.when(config.feedRefresh().userAgent()).thenReturn(Optional.of("http-getter-test"));
Mockito.when(config.feedRefresh().httpThreads()).thenReturn(3);
Mockito.when(config.feedRefresh().maxResponseSize()).thenReturn(new MemorySize(new BigInteger("10000")));
2023-12-18 09:51:27 +01:00
2024-08-07 08:10:14 +02:00
this.getter = new HttpGetter(config, Mockito.mock(CommaFeedVersion.class), Mockito.mock(MetricRegistry.class));
2023-12-18 09:51:27 +01:00
}
@ParameterizedTest
@ValueSource(
2024-08-07 08:10:14 +02:00
ints = { HttpStatus.SC_UNAUTHORIZED, HttpStatus.SC_FORBIDDEN, HttpStatus.SC_NOT_FOUND, HttpStatus.SC_INTERNAL_SERVER_ERROR })
2023-12-18 09:51:27 +01:00
void errorCodes(int code) {
this.mockServerClient.when(HttpRequest.request().withMethod("GET")).respond(HttpResponse.response().withStatusCode(code));
HttpResponseException e = Assertions.assertThrows(HttpResponseException.class, () -> getter.getBinary(this.feedUrl, TIMEOUT));
Assertions.assertEquals(code, e.getCode());
2023-12-18 09:51:27 +01:00
}
@Test
void validFeed() throws Exception {
2023-12-18 09:51:27 +01:00
this.mockServerClient.when(HttpRequest.request().withMethod("GET"))
.respond(HttpResponse.response()
.withBody(feedContent)
.withContentType(MediaType.APPLICATION_ATOM_XML)
.withHeader(HttpHeaders.LAST_MODIFIED, "123456")
.withHeader(HttpHeaders.ETAG, "78910"));
HttpResult result = getter.getBinary(this.feedUrl, TIMEOUT);
2023-12-18 09:51:27 +01:00
Assertions.assertArrayEquals(feedContent, result.getContent());
Assertions.assertEquals(MediaType.APPLICATION_ATOM_XML.toString(), result.getContentType());
Assertions.assertEquals("123456", result.getLastModifiedSince());
Assertions.assertEquals("78910", result.getETag());
2024-08-12 17:34:01 +02:00
Assertions.assertTrue(result.getDuration() >= 0);
2023-12-18 09:51:27 +01:00
Assertions.assertEquals(this.feedUrl, result.getUrlAfterRedirect());
}
2024-01-02 08:08:19 +01:00
@ParameterizedTest
@ValueSource(
2024-08-07 08:10:14 +02:00
ints = { HttpStatus.SC_MOVED_PERMANENTLY, HttpStatus.SC_MOVED_TEMPORARILY, HttpStatus.SC_TEMPORARY_REDIRECT,
HttpStatus.SC_PERMANENT_REDIRECT })
2024-01-02 08:08:19 +01:00
void followRedirects(int code) throws Exception {
// first redirect
this.mockServerClient.when(HttpRequest.request().withMethod("GET").withPath("/"))
.respond(HttpResponse.response()
.withStatusCode(code)
.withHeader(HttpHeaders.LOCATION, "http://localhost:" + this.mockServerClient.getPort() + "/redirected"));
// second redirect
2023-12-18 09:51:27 +01:00
this.mockServerClient.when(HttpRequest.request().withMethod("GET").withPath("/redirected"))
.respond(HttpResponse.response()
2024-01-02 08:08:19 +01:00
.withStatusCode(code)
.withHeader(HttpHeaders.LOCATION, "http://localhost:" + this.mockServerClient.getPort() + "/redirected-2"));
2024-01-02 08:08:19 +01:00
// final destination
this.mockServerClient.when(HttpRequest.request().withMethod("GET").withPath("/redirected-2"))
2023-12-18 09:51:27 +01:00
.respond(HttpResponse.response().withBody(feedContent).withContentType(MediaType.APPLICATION_ATOM_XML));
HttpResult result = getter.getBinary(this.feedUrl, TIMEOUT);
Assertions.assertEquals("http://localhost:" + this.mockServerClient.getPort() + "/redirected-2", result.getUrlAfterRedirect());
2023-12-18 09:51:27 +01:00
}
@Test
void dataTimeout() {
int smallTimeout = 500;
2023-12-18 09:51:27 +01:00
this.mockServerClient.when(HttpRequest.request().withMethod("GET"))
.respond(HttpResponse.response().withDelay(Delay.milliseconds(smallTimeout * 2)));
2023-12-18 09:51:27 +01:00
Assertions.assertThrows(SocketTimeoutException.class, () -> getter.getBinary(this.feedUrl, smallTimeout));
2023-12-22 16:04:25 +01:00
}
@Test
void connectTimeout() {
// try to connect to a non-routable address
2024-07-28 09:58:21 +02:00
// https://stackoverflow.com/a/904609
2024-08-07 08:10:14 +02:00
Assertions.assertThrows(ConnectTimeoutException.class, () -> getter.getBinary("http://10.255.255.1", 500));
2023-12-18 09:51:27 +01:00
}
@Test
void userAgent() throws Exception {
2023-12-18 09:51:27 +01:00
this.mockServerClient.when(HttpRequest.request().withMethod("GET").withHeader(HttpHeaders.USER_AGENT, "http-getter-test"))
.respond(HttpResponse.response().withBody("ok"));
HttpResult result = getter.getBinary(this.feedUrl, TIMEOUT);
2023-12-18 09:51:27 +01:00
Assertions.assertEquals("ok", new String(result.getContent()));
}
@Test
2023-12-25 19:41:14 +01:00
void lastModifiedReturns304() {
2023-12-18 09:51:27 +01:00
this.mockServerClient.when(HttpRequest.request().withMethod("GET").withHeader(HttpHeaders.IF_MODIFIED_SINCE, "123456"))
2024-08-07 08:10:14 +02:00
.respond(HttpResponse.response().withStatusCode(HttpStatus.SC_NOT_MODIFIED));
2023-12-18 09:51:27 +01:00
Assertions.assertThrows(NotModifiedException.class, () -> getter.getBinary(this.feedUrl, "123456", null, TIMEOUT));
2023-12-18 09:51:27 +01:00
}
@Test
2023-12-25 19:41:14 +01:00
void eTagReturns304() {
2023-12-18 09:51:27 +01:00
this.mockServerClient.when(HttpRequest.request().withMethod("GET").withHeader(HttpHeaders.IF_NONE_MATCH, "78910"))
2024-08-07 08:10:14 +02:00
.respond(HttpResponse.response().withStatusCode(HttpStatus.SC_NOT_MODIFIED));
2023-12-18 09:51:27 +01:00
Assertions.assertThrows(NotModifiedException.class, () -> getter.getBinary(this.feedUrl, null, "78910", TIMEOUT));
2023-12-18 09:51:27 +01:00
}
2023-12-18 15:09:04 +01:00
@Test
2023-12-25 19:41:14 +01:00
void ignoreCookie() {
2023-12-18 15:09:04 +01:00
AtomicInteger calls = new AtomicInteger();
this.mockServerClient.when(HttpRequest.request().withMethod("GET")).respond(req -> {
calls.incrementAndGet();
if (req.containsHeader(HttpHeaders.COOKIE)) {
throw new Exception("cookie should not be sent by the client");
}
return HttpResponse.response().withBody("ok").withHeader(HttpHeaders.SET_COOKIE, "foo=bar");
});
Assertions.assertDoesNotThrow(() -> getter.getBinary(this.feedUrl, TIMEOUT));
Assertions.assertDoesNotThrow(() -> getter.getBinary(this.feedUrl, TIMEOUT));
Assertions.assertEquals(2, calls.get());
}
@Test
void supportsCompression() {
this.mockServerClient.when(HttpRequest.request().withMethod("GET")).respond(req -> {
String acceptEncodingHeader = req.getFirstHeader(HttpHeaders.ACCEPT_ENCODING);
if (!acceptEncodingHeader.contains("deflate")) {
throw new Exception("deflate should be in the Accept-Encoding header");
}
if (!acceptEncodingHeader.contains("gzip")) {
throw new Exception("gzip should be in the Accept-Encoding header");
}
return HttpResponse.response().withBody("ok");
});
Assertions.assertDoesNotThrow(() -> getter.getBinary(this.feedUrl, TIMEOUT));
}
@Test
void largeFeedWithContentLengthHeader() {
2024-08-07 08:10:14 +02:00
byte[] bytes = new byte[100000];
Arrays.fill(bytes, (byte) 1);
this.mockServerClient.when(HttpRequest.request().withMethod("GET")).respond(HttpResponse.response().withBody(bytes));
IOException e = Assertions.assertThrows(IOException.class, () -> getter.getBinary(this.feedUrl, TIMEOUT));
Assertions.assertEquals("Response size (100000 bytes) exceeds the maximum allowed size (10000 bytes)", e.getMessage());
}
@Test
void largeFeedWithoutContentLengthHeader() {
2024-08-07 08:10:14 +02:00
byte[] bytes = new byte[100000];
Arrays.fill(bytes, (byte) 1);
this.mockServerClient.when(HttpRequest.request().withMethod("GET"))
.respond(HttpResponse.response()
.withBody(bytes)
.withConnectionOptions(ConnectionOptions.connectionOptions().withSuppressContentLengthHeader(true)));
IOException e = Assertions.assertThrows(IOException.class, () -> getter.getBinary(this.feedUrl, TIMEOUT));
Assertions.assertEquals("Response size exceeds the maximum allowed size (10000 bytes)", e.getMessage());
}
@Test
void ignoreInvalidSsl() throws Exception {
this.mockServerClient.when(HttpRequest.request().withMethod("GET")).respond(HttpResponse.response().withBody("ok"));
HttpResult result = getter.getBinary("https://localhost:" + this.mockServerClient.getPort(), TIMEOUT);
Assertions.assertEquals("ok", new String(result.getContent()));
}
2023-12-18 09:51:27 +01:00
}