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 final Logger logger = LogManager.getLogger(); private HttpHelper() { throw new UnsupportedOperationException(); } 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) { 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)); } 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 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 ? "statusCode %d".formatted(statusCode) : error.toString()); } } } 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); } } 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(); } } }