164 lines
6.2 KiB
Java
164 lines
6.2 KiB
Java
|
package io.gitlab.jfronny.libjf;
|
||
|
|
||
|
import java.io.IOException;
|
||
|
import java.io.InputStream;
|
||
|
import java.io.InputStreamReader;
|
||
|
import java.lang.reflect.Type;
|
||
|
import java.net.URI;
|
||
|
import java.net.URISyntaxException;
|
||
|
import java.net.URLEncoder;
|
||
|
import java.net.http.HttpClient;
|
||
|
import java.net.http.HttpRequest;
|
||
|
import java.net.http.HttpResponse;
|
||
|
import java.nio.charset.StandardCharsets;
|
||
|
import java.util.List;
|
||
|
import java.util.Map;
|
||
|
import java.util.Optional;
|
||
|
import java.util.regex.Pattern;
|
||
|
import java.util.stream.Collectors;
|
||
|
import java.util.stream.Stream;
|
||
|
|
||
|
public class HttpUtils {
|
||
|
private static final HttpClient CLIENT = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.ALWAYS).build();
|
||
|
|
||
|
private enum Method {
|
||
|
GET, POST
|
||
|
}
|
||
|
|
||
|
public static class Request {
|
||
|
private static final Pattern CURSEFORGE_API = Pattern.compile("(?:http(s)?://)?addons-ecs\\.forgesvc\\.net/api/+");
|
||
|
private final String url;
|
||
|
private final HttpRequest.Builder builder;
|
||
|
private Method method;
|
||
|
private int sent = 0;
|
||
|
|
||
|
public Request(Method method, String url) throws URISyntaxException {
|
||
|
this.url = url.replace(" ", "%20");
|
||
|
this.builder = HttpRequest.newBuilder()
|
||
|
.uri(new URI(this.url))
|
||
|
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36");
|
||
|
this.method = method;
|
||
|
}
|
||
|
|
||
|
public Request bearer(String token) {
|
||
|
builder.header("Authorization", "Bearer " + token);
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
public Request header(String name, String value) {
|
||
|
builder.header(name, value);
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
public Request bodyString(String string) {
|
||
|
builder.header("Content-Type", "text/plain");
|
||
|
builder.method(method.name(), HttpRequest.BodyPublishers.ofString(string));
|
||
|
method = null;
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
public Request bodyForm(String string) {
|
||
|
builder.header("Content-Type", "application/x-www-form-urlencoded");
|
||
|
builder.method(method.name(), HttpRequest.BodyPublishers.ofString(string));
|
||
|
method = null;
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
public Request bodyForm(Map<String, String> entries) {
|
||
|
return bodyForm(entries.entrySet()
|
||
|
.stream()
|
||
|
.map(entry -> URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8) + '=' + URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8))
|
||
|
.collect(Collectors.joining("&")));
|
||
|
}
|
||
|
|
||
|
public Request bodyJson(String string) {
|
||
|
builder.header("Content-Type", "application/json");
|
||
|
builder.method(method.name(), HttpRequest.BodyPublishers.ofString(string));
|
||
|
method = null;
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
public Request bodyJson(Object object) {
|
||
|
builder.header("Content-Type", "application/json");
|
||
|
builder.method(method.name(), HttpRequest.BodyPublishers.ofString(LibJf.GSON.toJson(object)));
|
||
|
method = null;
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
private <T> T _send(String accept, HttpResponse.BodyHandler<T> responseBodyHandler) throws IOException {
|
||
|
sent++;
|
||
|
if (sent > 3) throw new IOException("Attempted third redirect, stopping");
|
||
|
builder.header("Accept", accept);
|
||
|
if (method != null) builder.method(method.name(), HttpRequest.BodyPublishers.noBody());
|
||
|
|
||
|
HttpResponse<T> res;
|
||
|
try {
|
||
|
res = CLIENT.send(builder.build(), responseBodyHandler);
|
||
|
} catch (InterruptedException e) {
|
||
|
throw new IOException("Could not send request", e);
|
||
|
}
|
||
|
if (res.statusCode() == 200) return res.body();
|
||
|
Optional<String> location = res.headers().firstValue("location");
|
||
|
// Redirect
|
||
|
if (location.isPresent() && (res.statusCode() == 302 || res.statusCode() == 307) && method == Method.GET) {
|
||
|
try {
|
||
|
return HttpUtils.get(location.get())._send(accept, responseBodyHandler);
|
||
|
} catch (URISyntaxException e) {
|
||
|
throw new IOException("Could not follow redirect", e);
|
||
|
}
|
||
|
}
|
||
|
// CurseForge serverside error
|
||
|
if (CURSEFORGE_API.matcher(url).matches() && res.statusCode() >= 500 && res.statusCode() < 600) {
|
||
|
try {
|
||
|
Thread.sleep(1000);
|
||
|
} catch (InterruptedException e) {
|
||
|
throw new IOException("Could not sleep before resending request" + e);
|
||
|
}
|
||
|
return _send(accept, responseBodyHandler);
|
||
|
}
|
||
|
throw new IOException("Unexpected return method: " + res.statusCode() + " (URL=" + url + ")");
|
||
|
}
|
||
|
|
||
|
public void send() throws IOException {
|
||
|
_send("*/*", HttpResponse.BodyHandlers.discarding());
|
||
|
}
|
||
|
|
||
|
public InputStream sendInputStream() throws IOException {
|
||
|
return _send("*/*", HttpResponse.BodyHandlers.ofInputStream());
|
||
|
}
|
||
|
|
||
|
public String sendString() throws IOException {
|
||
|
return _send("*/*", HttpResponse.BodyHandlers.ofString());
|
||
|
}
|
||
|
|
||
|
public Stream<String> sendLines() throws IOException {
|
||
|
return _send("*/*", HttpResponse.BodyHandlers.ofLines());
|
||
|
}
|
||
|
|
||
|
public <T> T sendJson(Type type) throws IOException {
|
||
|
InputStream in = _send("application/json", HttpResponse.BodyHandlers.ofInputStream());
|
||
|
return in == null ? null : LibJf.GSON.fromJson(new InputStreamReader(in), type);
|
||
|
}
|
||
|
|
||
|
private String getString(Object a) throws IOException {
|
||
|
if (a instanceof InputStream s) return new String(s.readAllBytes());
|
||
|
if (a instanceof String s) return s;
|
||
|
if (a instanceof Stream s) return ((Stream<String>)s).collect(Collectors.joining());
|
||
|
return "";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static Request get(String url) throws URISyntaxException {
|
||
|
return new Request(Method.GET, url);
|
||
|
}
|
||
|
|
||
|
public static Request post(String url) throws URISyntaxException {
|
||
|
return new Request(Method.POST, url);
|
||
|
}
|
||
|
}
|