diff --git a/commafeed-server/pom.xml b/commafeed-server/pom.xml
index 53cd74c4..98a85a7c 100644
--- a/commafeed-server/pom.xml
+++ b/commafeed-server/pom.xml
@@ -464,6 +464,19 @@
5.15.0
test
+
+
+ org.bouncycastle
+ bcprov-jdk18on
+ 1.76
+ test
+
+
+ org.bouncycastle
+ bcpkix-jdk18on
+ 1.76
+ test
+
io.dropwizard
dropwizard-testing
diff --git a/commafeed-server/src/test/java/com/commafeed/backend/HttpGetterTest.java b/commafeed-server/src/test/java/com/commafeed/backend/HttpGetterTest.java
new file mode 100644
index 00000000..c2932a1e
--- /dev/null
+++ b/commafeed-server/src/test/java/com/commafeed/backend/HttpGetterTest.java
@@ -0,0 +1,136 @@
+package com.commafeed.backend;
+
+import java.io.IOException;
+import java.net.SocketTimeoutException;
+import java.util.Objects;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpHeaders;
+import org.apache.http.client.HttpResponseException;
+import org.eclipse.jetty.http.HttpStatus;
+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.mockserver.client.MockServerClient;
+import org.mockserver.junit.jupiter.MockServerExtension;
+import org.mockserver.model.Delay;
+import org.mockserver.model.HttpRequest;
+import org.mockserver.model.HttpResponse;
+import org.mockserver.model.MediaType;
+
+import com.commafeed.CommaFeedConfiguration;
+import com.commafeed.CommaFeedConfiguration.ApplicationSettings;
+import com.commafeed.backend.HttpGetter.HttpResult;
+import com.commafeed.backend.HttpGetter.NotModifiedException;
+
+@ExtendWith(MockServerExtension.class)
+class HttpGetterTest {
+
+ 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")));
+
+ ApplicationSettings settings = new ApplicationSettings();
+ settings.setUserAgent("http-getter-test");
+
+ CommaFeedConfiguration config = new CommaFeedConfiguration();
+ config.setApplicationSettings(settings);
+
+ this.getter = new HttpGetter(config);
+ }
+
+ @ParameterizedTest
+ @ValueSource(
+ ints = { HttpStatus.UNAUTHORIZED_401, HttpStatus.FORBIDDEN_403, HttpStatus.NOT_FOUND_404,
+ HttpStatus.INTERNAL_SERVER_ERROR_500 })
+ 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, 1000));
+ Assertions.assertEquals(code, e.getStatusCode());
+ }
+
+ @Test
+ void validFeed() throws IOException, NotModifiedException {
+ 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, 1000);
+ Assertions.assertArrayEquals(feedContent, result.getContent());
+ Assertions.assertEquals(MediaType.APPLICATION_ATOM_XML.toString(), result.getContentType());
+ Assertions.assertEquals("123456", result.getLastModifiedSince());
+ Assertions.assertEquals("78910", result.getETag());
+ Assertions.assertTrue(result.getDuration() > 0);
+ Assertions.assertEquals(this.feedUrl, result.getUrlAfterRedirect());
+ }
+
+ @Test
+ void followRedirects() throws IOException, NotModifiedException {
+ this.mockServerClient.when(HttpRequest.request().withMethod("GET").withPath("/redirected"))
+ .respond(HttpResponse.response().withBody(feedContent).withContentType(MediaType.APPLICATION_ATOM_XML));
+ this.mockServerClient.when(HttpRequest.request().withMethod("GET"))
+ .respond(HttpResponse.response()
+ .withStatusCode(HttpStatus.MOVED_PERMANENTLY_301)
+ .withHeader(HttpHeaders.LOCATION, "http://localhost:" + this.mockServerClient.getPort() + "/redirected"));
+
+ HttpResult result = getter.getBinary(this.feedUrl, 1000);
+ Assertions.assertEquals("http://localhost:" + this.mockServerClient.getPort() + "/redirected", result.getUrlAfterRedirect());
+ }
+
+ @Test
+ void timeout() {
+ this.mockServerClient.when(HttpRequest.request().withMethod("GET"))
+ .respond(HttpResponse.response().withDelay(Delay.milliseconds(2000)));
+
+ Assertions.assertThrows(SocketTimeoutException.class, () -> getter.getBinary(this.feedUrl, 1000));
+ }
+
+ @Test
+ void userAgent() throws IOException, NotModifiedException {
+ 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, 1000);
+ Assertions.assertEquals("ok", new String(result.getContent()));
+ }
+
+ @Test
+ void ignoreInvalidSsl() throws IOException, NotModifiedException {
+ this.mockServerClient.when(HttpRequest.request().withMethod("GET")).respond(HttpResponse.response().withBody("ok"));
+
+ HttpResult result = getter.getBinary("https://localhost:" + this.mockServerClient.getPort(), 1000);
+ Assertions.assertEquals("ok", new String(result.getContent()));
+ }
+
+ @Test
+ void lastModifiedReturns304() throws IOException, NotModifiedException {
+ this.mockServerClient.when(HttpRequest.request().withMethod("GET").withHeader(HttpHeaders.IF_MODIFIED_SINCE, "123456"))
+ .respond(HttpResponse.response().withStatusCode(HttpStatus.NOT_MODIFIED_304));
+
+ Assertions.assertThrows(NotModifiedException.class, () -> getter.getBinary(this.feedUrl, "123456", null, 1000));
+ }
+
+ @Test
+ void eTagReturns304() throws IOException, NotModifiedException {
+ this.mockServerClient.when(HttpRequest.request().withMethod("GET").withHeader(HttpHeaders.IF_NONE_MATCH, "78910"))
+ .respond(HttpResponse.response().withStatusCode(HttpStatus.NOT_MODIFIED_304));
+
+ Assertions.assertThrows(NotModifiedException.class, () -> getter.getBinary(this.feedUrl, null, "78910", 1000));
+ }
+
+}
\ No newline at end of file