diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/helper/HttpHelper.java b/LaunchServer/src/main/java/pro/gravit/launchserver/helper/HttpHelper.java new file mode 100644 index 00000000..436969c7 --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/helper/HttpHelper.java @@ -0,0 +1,172 @@ +package pro.gravit.launchserver.helper; + +import com.google.gson.JsonElement; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import pro.gravit.launcher.Launcher; +import pro.gravit.launcher.request.RequestException; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.lang.reflect.Type; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.Flow; +import java.util.function.Function; + +public final class HttpHelper { + private static transient final Logger logger = LogManager.getLogger(); + private HttpHelper() { + throw new UnsupportedOperationException(); + } + + public static class HttpOptional { + protected final T result; + protected final E error; + protected final int statusCode; + + public HttpOptional(T result, E error, int statusCode) { + this.result = result; + this.error = error; + this.statusCode = statusCode; + } + + public T result() { + return result; + } + public E error() { + return error; + } + public int statusCode() { + return statusCode; + } + public boolean isSuccessful() { + return statusCode >= 200 && statusCode < 300; + } + public T getOrThrow() throws RequestException { + if(isSuccessful()) { + return result; + } else { + throw new RequestException(error == null ? String.format("statusCode %d", statusCode) : error.toString()); + } + } + } + + public interface HttpErrorHandler { + HttpOptional apply(HttpResponse response); + } + + public interface HttpJsonErrorHandler extends HttpErrorHandler { + HttpOptional applyJson(JsonElement response, int statusCode); + default HttpOptional apply(HttpResponse response) { + try(Reader reader = new InputStreamReader(response.body())) { + var element = Launcher.gsonManager.gson.fromJson(reader, JsonElement.class); + return applyJson(element, response.statusCode()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + public static final class BasicJsonHttpErrorHandler implements HttpJsonErrorHandler { + private final Class type; + + public BasicJsonHttpErrorHandler(Class type) { + this.type = type; + } + + @Override + public HttpOptional applyJson(JsonElement response, int statusCode) { + return new HttpOptional<>(Launcher.gsonManager.gson.fromJson(response, type), null, statusCode); + } + } + + public static HttpOptional send(HttpClient client, HttpRequest request, HttpErrorHandler handler) throws IOException { + try { + var response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()); + return handler.apply(response); + } catch (InterruptedException e) { + throw new IOException(e); + } + } + + + public static CompletableFuture> sendAsync(HttpClient client, HttpRequest request, HttpErrorHandler handler) throws IOException { + return client.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()).thenApply(handler::apply); + } + + public static HttpResponse.BodyHandler ofJsonResult(Class type) { + return ofJsonResult((Type) type); + } + + public static HttpResponse.BodyHandler ofJsonResult(Type type) { + return new JsonBodyHandler<>(HttpResponse.BodyHandlers.ofInputStream(), (input) -> { + try(Reader reader = new InputStreamReader(input)) { + return Launcher.gsonManager.gson.fromJson(reader, type); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + + public static HttpRequest.BodyPublisher jsonBodyPublisher(T obj) { + return HttpRequest.BodyPublishers.ofString(Launcher.gsonManager.gson.toJson(obj)); + } + + private static class JsonBodyHandler implements HttpResponse.BodyHandler { + private final HttpResponse.BodyHandler delegate; + private final Function func; + + private JsonBodyHandler(HttpResponse.BodyHandler delegate, Function func) { + this.delegate = delegate; + this.func = func; + } + + @Override + public HttpResponse.BodySubscriber apply(HttpResponse.ResponseInfo responseInfo) { + return new JsonBodySubscriber<>(delegate.apply(responseInfo), func); + } + } + + private static class JsonBodySubscriber implements HttpResponse.BodySubscriber { + private final HttpResponse.BodySubscriber delegate; + private final Function func; + + private JsonBodySubscriber(HttpResponse.BodySubscriber delegate, Function func) { + this.delegate = delegate; + this.func = func; + } + + @Override + public CompletionStage getBody() { + return delegate.getBody().thenApply(func); + } + + @Override + public void onSubscribe(Flow.Subscription subscription) { + delegate.onSubscribe(subscription); + } + + @Override + public void onNext(List item) { + delegate.onNext(item); + } + + @Override + public void onError(Throwable throwable) { + delegate.onError(throwable); + } + + @Override + public void onComplete() { + delegate.onComplete(); + } + } +}