diff --git a/Core/src/main/java/com/songoda/core/http/HttpClient.java b/Core/src/main/java/com/songoda/core/http/HttpClient.java new file mode 100644 index 00000000..95037834 --- /dev/null +++ b/Core/src/main/java/com/songoda/core/http/HttpClient.java @@ -0,0 +1,9 @@ +package com.songoda.core.http; + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; + +public interface HttpClient { + @NotNull HttpResponse get(String url) throws IOException; +} diff --git a/Core/src/main/java/com/songoda/core/http/HttpResponse.java b/Core/src/main/java/com/songoda/core/http/HttpResponse.java new file mode 100644 index 00000000..a5b519bb --- /dev/null +++ b/Core/src/main/java/com/songoda/core/http/HttpResponse.java @@ -0,0 +1,11 @@ +package com.songoda.core.http; + +import java.io.IOException; + +public interface HttpResponse { + int getResponseCode() throws IOException; + + byte[] getBody() throws IOException; + + String getBodyAsString() throws IOException; +} diff --git a/Core/src/main/java/com/songoda/core/http/HttpResponseImpl.java b/Core/src/main/java/com/songoda/core/http/HttpResponseImpl.java new file mode 100644 index 00000000..f713b98d --- /dev/null +++ b/Core/src/main/java/com/songoda/core/http/HttpResponseImpl.java @@ -0,0 +1,54 @@ +package com.songoda.core.http; + +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.nio.charset.StandardCharsets; + +public class HttpResponseImpl implements HttpResponse, AutoCloseable { + protected final HttpURLConnection connection; + + protected byte[] body; + + HttpResponseImpl(HttpURLConnection connection) throws IOException { + this.connection = connection; + + this.connection.connect(); + } + + public int getResponseCode() throws IOException { + int statusCode = this.connection.getResponseCode(); + + if (statusCode == -1) { + throw new IOException("HTTP Status Code is -1"); + } + + return statusCode; + } + + public byte[] getBody() throws IOException { + if (this.body == null) { + try (InputStream in = this.connection.getInputStream(); + InputStream err = this.connection.getErrorStream()) { + if (err != null) { + this.body = IOUtils.toByteArray(err); + } else { + this.body = IOUtils.toByteArray(in); + } + } + } + + return this.body; + } + + public String getBodyAsString() throws IOException { + return new String(getBody(), StandardCharsets.UTF_8); + } + + @Override + public void close() throws Exception { + this.connection.disconnect(); + } +} diff --git a/Core/src/main/java/com/songoda/core/http/SimpleHttpClient.java b/Core/src/main/java/com/songoda/core/http/SimpleHttpClient.java new file mode 100644 index 00000000..3ce9e606 --- /dev/null +++ b/Core/src/main/java/com/songoda/core/http/SimpleHttpClient.java @@ -0,0 +1,21 @@ +package com.songoda.core.http; + +import com.songoda.core.SongodaCore; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; + +public class SimpleHttpClient implements HttpClient { + public @NotNull HttpResponse get(String url) throws IOException { + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + connection.setInstanceFollowRedirects(true); + connection.setConnectTimeout(5000); + connection.setReadTimeout(5000); + + connection.setRequestProperty("User-Agent", "SongodaCore/" + SongodaCore.getVersion() + " (+https://github.com/songoda/SongodaCore)"); + + return new HttpResponseImpl(connection); + } +} diff --git a/Core/src/main/java/com/songoda/core/http/UnexpectedHttpStatusException.java b/Core/src/main/java/com/songoda/core/http/UnexpectedHttpStatusException.java new file mode 100644 index 00000000..3229448c --- /dev/null +++ b/Core/src/main/java/com/songoda/core/http/UnexpectedHttpStatusException.java @@ -0,0 +1,15 @@ +package com.songoda.core.http; + +import java.io.IOException; + +public class UnexpectedHttpStatusException extends IOException { + public final int responseCode; + public final String url; + + public UnexpectedHttpStatusException(int responseCode, String url) { + super("Got HTTP Status Code " + responseCode + ": " + url); + + this.responseCode = responseCode; + this.url = url; + } +} diff --git a/Core/src/test/java/com/songoda/core/http/MockHttpClient.java b/Core/src/test/java/com/songoda/core/http/MockHttpClient.java new file mode 100644 index 00000000..ed329953 --- /dev/null +++ b/Core/src/test/java/com/songoda/core/http/MockHttpClient.java @@ -0,0 +1,22 @@ +package com.songoda.core.http; + +import org.jetbrains.annotations.NotNull; + +import java.util.LinkedList; +import java.util.List; + +public class MockHttpClient implements HttpClient { + public HttpResponse returnValue; + public List callsOnGet = new LinkedList<>(); + + public MockHttpClient(HttpResponse returnValue) { + this.returnValue = returnValue; + } + + @Override + public @NotNull HttpResponse get(String url) { + this.callsOnGet.add(url); + + return this.returnValue; + } +} diff --git a/Core/src/test/java/com/songoda/core/http/MockHttpResponse.java b/Core/src/test/java/com/songoda/core/http/MockHttpResponse.java new file mode 100644 index 00000000..3a9a48cf --- /dev/null +++ b/Core/src/test/java/com/songoda/core/http/MockHttpResponse.java @@ -0,0 +1,25 @@ +package com.songoda.core.http; + +import java.nio.charset.StandardCharsets; + +public class MockHttpResponse implements HttpResponse { + public int responseCode; + public byte[] body; + + public MockHttpResponse(int responseCode, byte[] body) { + this.responseCode = responseCode; + this.body = body; + } + + public int getResponseCode() { + return this.responseCode; + } + + public byte[] getBody() { + return this.body; + } + + public String getBodyAsString() { + return new String(getBody(), StandardCharsets.UTF_8); + } +}