forked from MirrorHub/authlib-injector
support prefetched metadata & refactor
This commit is contained in:
parent
cd5b42140c
commit
a671ee8339
8 changed files with 86 additions and 238 deletions
|
@ -2,12 +2,16 @@ package org.to2mbn.authlibinjector;
|
||||||
|
|
||||||
import static java.util.Optional.empty;
|
import static java.util.Optional.empty;
|
||||||
import static java.util.Optional.of;
|
import static java.util.Optional.of;
|
||||||
|
import static org.to2mbn.authlibinjector.util.IOUtils.readURL;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.instrument.ClassFileTransformer;
|
import java.lang.instrument.ClassFileTransformer;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import org.to2mbn.authlibinjector.transform.ClassTransformer;
|
import org.to2mbn.authlibinjector.transform.ClassTransformer;
|
||||||
|
import org.to2mbn.authlibinjector.transform.SkinWhitelistTransformUnit;
|
||||||
|
import org.to2mbn.authlibinjector.transform.YggdrasilApiTransformUnit;
|
||||||
|
import org.to2mbn.authlibinjector.transform.YggdrasilKeyTransformUnit;
|
||||||
|
|
||||||
public final class AuthlibInjector {
|
public final class AuthlibInjector {
|
||||||
|
|
||||||
|
@ -19,60 +23,82 @@ public final class AuthlibInjector {
|
||||||
private AuthlibInjector() {}
|
private AuthlibInjector() {}
|
||||||
|
|
||||||
private static boolean booted = false;
|
private static boolean booted = false;
|
||||||
|
private static boolean debug = "true".equals(System.getProperty("org.to2mbn.authlibinjector.debug"));
|
||||||
|
|
||||||
public static void log(String message, Object... args) {
|
public static void info(String message, Object... args) {
|
||||||
System.err.println("[authlib-injector] " + MessageFormat.format(message, args));
|
System.err.println("[authlib-injector] " + MessageFormat.format(message, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void debug(String message, Object... args) {
|
||||||
|
if (debug) {
|
||||||
|
info(message, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void bootstrap(Consumer<ClassFileTransformer> transformerRegistry) {
|
public static void bootstrap(Consumer<ClassFileTransformer> transformerRegistry) {
|
||||||
if (booted) {
|
if (booted) {
|
||||||
log("already booted, skipping");
|
info("already booted, skipping");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
booted = true;
|
booted = true;
|
||||||
|
|
||||||
Optional<InjectorConfig> optionalConfig = configure();
|
Optional<YggdrasilConfiguration> optionalConfig = configure();
|
||||||
if (!optionalConfig.isPresent()) {
|
if (!optionalConfig.isPresent()) {
|
||||||
log("no config is found, exiting");
|
info("no config available");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
InjectorConfig config = optionalConfig.get();
|
transformerRegistry.accept(createTransformer(optionalConfig.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Optional<YggdrasilConfiguration> configure() {
|
||||||
|
String apiRoot = System.getProperty("org.to2mbn.authlibinjector.config");
|
||||||
|
if (apiRoot == null) return empty();
|
||||||
|
info("api root: {0}", apiRoot);
|
||||||
|
|
||||||
|
String metadataResponse = System.getProperty("org.to2mbn.authlibinjector.config.prefetched");
|
||||||
|
|
||||||
|
if (metadataResponse == null) {
|
||||||
|
info("fetching metadata");
|
||||||
|
try {
|
||||||
|
metadataResponse = readURL(apiRoot);
|
||||||
|
} catch (IOException e) {
|
||||||
|
info("unable to fetch metadata: {0}", e);
|
||||||
|
return empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
info("prefetched metadata detected");
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("metadata: {0}", metadataResponse);
|
||||||
|
|
||||||
|
YggdrasilConfiguration configuration;
|
||||||
|
try {
|
||||||
|
configuration = YggdrasilConfiguration.parse(apiRoot, metadataResponse);
|
||||||
|
} catch (IOException e) {
|
||||||
|
info("unable to parse metadata: {0}\n"
|
||||||
|
+ "metadata to parse:\n"
|
||||||
|
+ "{1}",
|
||||||
|
e, metadataResponse);
|
||||||
|
return empty();
|
||||||
|
}
|
||||||
|
debug("parsed metadata: {0}", configuration);
|
||||||
|
return of(configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ClassTransformer createTransformer(YggdrasilConfiguration config) {
|
||||||
ClassTransformer transformer = new ClassTransformer();
|
ClassTransformer transformer = new ClassTransformer();
|
||||||
|
transformer.debugSaveClass = debug;
|
||||||
if (config.isDebug()) transformer.debug = true;
|
|
||||||
|
|
||||||
for (String ignore : nonTransformablePackages)
|
for (String ignore : nonTransformablePackages)
|
||||||
transformer.ignores.add(ignore);
|
transformer.ignores.add(ignore);
|
||||||
|
|
||||||
config.applyTransformers(transformer.units);
|
transformer.units.add(new YggdrasilApiTransformUnit(config.getApiRoot()));
|
||||||
transformerRegistry.accept(transformer);
|
transformer.units.add(new SkinWhitelistTransformUnit(config.getSkinDomains().toArray(new String[0])));
|
||||||
}
|
config.getDecodedPublickey().ifPresent(
|
||||||
|
key -> transformer.units.add(new YggdrasilKeyTransformUnit(key.getEncoded())));
|
||||||
|
|
||||||
private static Optional<InjectorConfig> configure() {
|
return transformer;
|
||||||
String url = System.getProperty("org.to2mbn.authlibinjector.config");
|
|
||||||
if (url == null) {
|
|
||||||
return empty();
|
|
||||||
}
|
|
||||||
log("trying to config remotely: {0}", url);
|
|
||||||
|
|
||||||
InjectorConfig config = new InjectorConfig();
|
|
||||||
config.setDebug("true".equals(System.getProperty("org.to2mbn.authlibinjector.debug")));
|
|
||||||
|
|
||||||
RemoteConfiguration remoteConfig;
|
|
||||||
try {
|
|
||||||
remoteConfig = RemoteConfiguration.fetch(url);
|
|
||||||
} catch (IOException e) {
|
|
||||||
log("unable to configure remotely: {0}", e);
|
|
||||||
return empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.isDebug()) {
|
|
||||||
log("fetched remote config: {0}", remoteConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteConfig.applyToInjectorConfig(config);
|
|
||||||
return of(config);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
package org.to2mbn.authlibinjector;
|
|
||||||
|
|
||||||
import static org.to2mbn.authlibinjector.util.KeyUtils.decodePublicKey;
|
|
||||||
import java.util.List;
|
|
||||||
import org.to2mbn.authlibinjector.transform.SkinWhitelistTransformUnit;
|
|
||||||
import org.to2mbn.authlibinjector.transform.TransformUnit;
|
|
||||||
import org.to2mbn.authlibinjector.transform.YggdrasilApiTransformUnit;
|
|
||||||
import org.to2mbn.authlibinjector.transform.YggdrasilKeyTransformUnit;
|
|
||||||
|
|
||||||
public class InjectorConfig {
|
|
||||||
|
|
||||||
private String apiRoot;
|
|
||||||
private List<String> skinWhitelistDomains;
|
|
||||||
private String publicKey;
|
|
||||||
private boolean debug;
|
|
||||||
|
|
||||||
public String getApiRoot() {
|
|
||||||
return apiRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setApiRoot(String apiRoot) {
|
|
||||||
this.apiRoot = apiRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getSkinWhitelistDomains() {
|
|
||||||
return skinWhitelistDomains;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSkinWhitelistDomains(List<String> skinWhitelistDomains) {
|
|
||||||
this.skinWhitelistDomains = skinWhitelistDomains;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPublicKey() {
|
|
||||||
return publicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPublicKey(String publicKey) {
|
|
||||||
this.publicKey = publicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDebug() {
|
|
||||||
return debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDebug(boolean debug) {
|
|
||||||
this.debug = debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void applyTransformers(List<TransformUnit> units) {
|
|
||||||
units.add(new YggdrasilApiTransformUnit(apiRoot));
|
|
||||||
units.add(new SkinWhitelistTransformUnit(skinWhitelistDomains.toArray(new String[0])));
|
|
||||||
if (publicKey != null) {
|
|
||||||
units.add(new YggdrasilKeyTransformUnit(decodePublicKey(publicKey)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,8 +6,6 @@ import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.Optional.empty;
|
import static java.util.Optional.empty;
|
||||||
import static java.util.Optional.of;
|
import static java.util.Optional.of;
|
||||||
import static java.util.Optional.ofNullable;
|
import static java.util.Optional.ofNullable;
|
||||||
import static org.to2mbn.authlibinjector.util.HttpRequester.http;
|
|
||||||
import static org.to2mbn.authlibinjector.util.IOUtils.asJson;
|
|
||||||
import static org.to2mbn.authlibinjector.util.KeyUtils.decodePublicKey;
|
import static org.to2mbn.authlibinjector.util.KeyUtils.decodePublicKey;
|
||||||
import static org.to2mbn.authlibinjector.util.KeyUtils.loadX509PublicKey;
|
import static org.to2mbn.authlibinjector.util.KeyUtils.loadX509PublicKey;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -21,13 +19,13 @@ import java.util.TreeMap;
|
||||||
import org.to2mbn.authlibinjector.internal.org.json.JSONException;
|
import org.to2mbn.authlibinjector.internal.org.json.JSONException;
|
||||||
import org.to2mbn.authlibinjector.internal.org.json.JSONObject;
|
import org.to2mbn.authlibinjector.internal.org.json.JSONObject;
|
||||||
|
|
||||||
public class RemoteConfiguration {
|
public class YggdrasilConfiguration {
|
||||||
|
|
||||||
public static RemoteConfiguration fetch(String apiRoot) throws IOException {
|
public static YggdrasilConfiguration parse(String apiRoot, String metadataResponse) throws IOException {
|
||||||
if (!apiRoot.endsWith("/")) apiRoot += "/";
|
if (!apiRoot.endsWith("/")) apiRoot += "/";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
JSONObject response = asJson(http.request("GET", apiRoot));
|
JSONObject response = new JSONObject(metadataResponse);
|
||||||
|
|
||||||
List<String> skinDomains = new ArrayList<>();
|
List<String> skinDomains = new ArrayList<>();
|
||||||
ofNullable(response.optJSONArray("skinDomains"))
|
ofNullable(response.optJSONArray("skinDomains"))
|
||||||
|
@ -54,7 +52,7 @@ public class RemoteConfiguration {
|
||||||
.map(JSONObject::toMap)
|
.map(JSONObject::toMap)
|
||||||
.ifPresent(it -> it.forEach((k, v) -> meta.put(k, String.valueOf(v))));
|
.ifPresent(it -> it.forEach((k, v) -> meta.put(k, String.valueOf(v))));
|
||||||
|
|
||||||
return new RemoteConfiguration(apiRoot, unmodifiableList(skinDomains), signaturePublickey, unmodifiableMap(meta), decodedPublickey);
|
return new YggdrasilConfiguration(apiRoot, unmodifiableList(skinDomains), signaturePublickey, unmodifiableMap(meta), decodedPublickey);
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
throw new IOException("Invalid json", e);
|
throw new IOException("Invalid json", e);
|
||||||
}
|
}
|
||||||
|
@ -66,7 +64,7 @@ public class RemoteConfiguration {
|
||||||
private Optional<PublicKey> decodedPublickey;
|
private Optional<PublicKey> decodedPublickey;
|
||||||
private Map<String, String> meta;
|
private Map<String, String> meta;
|
||||||
|
|
||||||
public RemoteConfiguration(String apiRoot, List<String> skinDomains, Optional<String> signaturePublickey, Map<String, String> meta, Optional<PublicKey> decodedPublickey) {
|
public YggdrasilConfiguration(String apiRoot, List<String> skinDomains, Optional<String> signaturePublickey, Map<String, String> meta, Optional<PublicKey> decodedPublickey) {
|
||||||
this.apiRoot = requireNonNull(apiRoot);
|
this.apiRoot = requireNonNull(apiRoot);
|
||||||
this.skinDomains = requireNonNull(skinDomains);
|
this.skinDomains = requireNonNull(skinDomains);
|
||||||
this.signaturePublickey = requireNonNull(signaturePublickey);
|
this.signaturePublickey = requireNonNull(signaturePublickey);
|
||||||
|
@ -94,14 +92,8 @@ public class RemoteConfiguration {
|
||||||
return decodedPublickey;
|
return decodedPublickey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyToInjectorConfig(InjectorConfig config) {
|
|
||||||
config.setApiRoot(apiRoot);
|
|
||||||
config.setPublicKey(signaturePublickey.orElse(null));
|
|
||||||
config.setSkinWhitelistDomains(skinDomains);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "RemoteConfiguration [apiRoot=" + apiRoot + ", skinDomains=" + skinDomains + ", signaturePublickey=" + signaturePublickey + ", meta=" + meta + "]";
|
return "YggdrasilConfiguration [apiRoot=" + apiRoot + ", skinDomains=" + skinDomains + ", signaturePublickey=" + signaturePublickey + ", meta=" + meta + "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,14 +1,14 @@
|
||||||
package org.to2mbn.authlibinjector.javaagent;
|
package org.to2mbn.authlibinjector.javaagent;
|
||||||
|
|
||||||
import static org.to2mbn.authlibinjector.AuthlibInjector.bootstrap;
|
import static org.to2mbn.authlibinjector.AuthlibInjector.bootstrap;
|
||||||
import static org.to2mbn.authlibinjector.AuthlibInjector.log;
|
import static org.to2mbn.authlibinjector.AuthlibInjector.info;
|
||||||
import java.lang.instrument.Instrumentation;
|
import java.lang.instrument.Instrumentation;
|
||||||
|
|
||||||
public class AuthlibInjectorPremain {
|
public class AuthlibInjectorPremain {
|
||||||
|
|
||||||
public static void premain(String arg, Instrumentation instrumentation) {
|
public static void premain(String arg, Instrumentation instrumentation) {
|
||||||
try {
|
try {
|
||||||
log("launched from javaagent");
|
info("launched from javaagent");
|
||||||
if (arg != null && !arg.isEmpty()) {
|
if (arg != null && !arg.isEmpty()) {
|
||||||
System.setProperty("org.to2mbn.authlibinjector.config", arg);
|
System.setProperty("org.to2mbn.authlibinjector.config", arg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.to2mbn.authlibinjector.transform;
|
package org.to2mbn.authlibinjector.transform;
|
||||||
|
|
||||||
import static org.to2mbn.authlibinjector.AuthlibInjector.log;
|
import static org.to2mbn.authlibinjector.AuthlibInjector.debug;
|
||||||
|
import static org.to2mbn.authlibinjector.AuthlibInjector.info;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.instrument.ClassFileTransformer;
|
import java.lang.instrument.ClassFileTransformer;
|
||||||
import java.lang.instrument.IllegalClassFormatException;
|
import java.lang.instrument.IllegalClassFormatException;
|
||||||
|
@ -21,7 +22,7 @@ public class ClassTransformer implements ClassFileTransformer {
|
||||||
|
|
||||||
public List<TransformUnit> units = new ArrayList<>();
|
public List<TransformUnit> units = new ArrayList<>();
|
||||||
public Set<String> ignores = new HashSet<>();
|
public Set<String> ignores = new HashSet<>();
|
||||||
public boolean debug;
|
public boolean debugSaveClass;
|
||||||
|
|
||||||
private static class TransformHandle {
|
private static class TransformHandle {
|
||||||
|
|
||||||
|
@ -51,7 +52,7 @@ public class ClassTransformer implements ClassFileTransformer {
|
||||||
ClassReader reader = new ClassReader(classBuffer);
|
ClassReader reader = new ClassReader(classBuffer);
|
||||||
reader.accept(optionalVisitor.get(), 0);
|
reader.accept(optionalVisitor.get(), 0);
|
||||||
if (currentModified) {
|
if (currentModified) {
|
||||||
log("transform {0} using {1}", className, unit);
|
info("transform {0} using {1}", className, unit);
|
||||||
modified = true;
|
modified = true;
|
||||||
classBuffer = writer.toByteArray();
|
classBuffer = writer.toByteArray();
|
||||||
}
|
}
|
||||||
|
@ -82,18 +83,16 @@ public class ClassTransformer implements ClassFileTransformer {
|
||||||
units.forEach(handle::accept);
|
units.forEach(handle::accept);
|
||||||
if (handle.getResult().isPresent()) {
|
if (handle.getResult().isPresent()) {
|
||||||
byte[] classBuffer = handle.getResult().get();
|
byte[] classBuffer = handle.getResult().get();
|
||||||
if (debug) {
|
if (debugSaveClass) {
|
||||||
saveClassFile(className, classBuffer);
|
saveClassFile(className, classBuffer);
|
||||||
}
|
}
|
||||||
return classBuffer;
|
return classBuffer;
|
||||||
} else {
|
} else {
|
||||||
if (debug) {
|
debug("no transform performed on {0}", className);
|
||||||
log("no transform performed on {0}", className);
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
log("unable to transform {0}: {1}", internalClassName, e);
|
info("unable to transform {0}: {1}", internalClassName, e);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,7 +103,7 @@ public class ClassTransformer implements ClassFileTransformer {
|
||||||
try {
|
try {
|
||||||
Files.write(Paths.get(className + "_dump.class"), classBuffer, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
Files.write(Paths.get(className + "_dump.class"), classBuffer, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log("unable to dump class {0}: {1}", className, e);
|
info("unable to dump class {0}: {1}", className, e);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package org.to2mbn.authlibinjector.transform;
|
package org.to2mbn.authlibinjector.transform;
|
||||||
|
|
||||||
import static org.objectweb.asm.Opcodes.ASM6;
|
import static org.objectweb.asm.Opcodes.ASM6;
|
||||||
import static org.to2mbn.authlibinjector.AuthlibInjector.log;
|
import static org.to2mbn.authlibinjector.AuthlibInjector.info;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import org.objectweb.asm.ClassVisitor;
|
import org.objectweb.asm.ClassVisitor;
|
||||||
|
@ -29,7 +29,7 @@ public class LdcTransformUnit implements TransformUnit {
|
||||||
Optional<String> transformed = ldcMapper.apply((String) cst);
|
Optional<String> transformed = ldcMapper.apply((String) cst);
|
||||||
if (transformed.isPresent() && !transformed.get().equals(cst)) {
|
if (transformed.isPresent() && !transformed.get().equals(cst)) {
|
||||||
modifiedCallback.run();
|
modifiedCallback.run();
|
||||||
log("transform [{0}] to [{1}]", cst, transformed.get());
|
info("transform [{0}] to [{1}]", cst, transformed.get());
|
||||||
super.visitLdcInsn(transformed.get());
|
super.visitLdcInsn(transformed.get());
|
||||||
} else {
|
} else {
|
||||||
super.visitLdcInsn(cst);
|
super.visitLdcInsn(cst);
|
||||||
|
|
|
@ -1,114 +0,0 @@
|
||||||
package org.to2mbn.authlibinjector.util;
|
|
||||||
|
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
|
||||||
import static org.to2mbn.authlibinjector.util.IOUtils.asString;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class HttpRequester {
|
|
||||||
|
|
||||||
/** Common http requester */
|
|
||||||
public static final HttpRequester http = new HttpRequester();
|
|
||||||
|
|
||||||
private int timeout = 15000;
|
|
||||||
|
|
||||||
public int getTimeout() {
|
|
||||||
return timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTimeout(int timeout) {
|
|
||||||
this.timeout = timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String request(String method, String url) throws IOException {
|
|
||||||
return request(method, url, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String request(String method, String url, Map<String, String> headers) throws IOException {
|
|
||||||
HttpURLConnection conn = createConnection(url, headers);
|
|
||||||
conn.setRequestMethod(method);
|
|
||||||
try {
|
|
||||||
conn.connect();
|
|
||||||
try (InputStream in = conn.getInputStream()) {
|
|
||||||
return asString(in);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
try (InputStream in = conn.getErrorStream()) {
|
|
||||||
return readErrorStream(in, e);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
conn.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String requestWithPayload(String method, String url, Object payload, String contentType) throws IOException {
|
|
||||||
return requestWithPayload(method, url, payload, contentType, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String requestWithPayload(String method, String url, Object payload, String contentType, Map<String, String> headers) throws IOException {
|
|
||||||
byte[] bytePayload;
|
|
||||||
if (payload instanceof byte[]) {
|
|
||||||
bytePayload = (byte[]) payload;
|
|
||||||
} else if (payload == null) {
|
|
||||||
bytePayload = new byte[0];
|
|
||||||
} else {
|
|
||||||
bytePayload = String.valueOf(payload).getBytes(UTF_8);
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpURLConnection conn = createConnection(url, headers);
|
|
||||||
conn.setRequestMethod(method);
|
|
||||||
conn.setRequestProperty("Content-Type", contentType);
|
|
||||||
conn.setRequestProperty("Content-Length", String.valueOf(bytePayload.length));
|
|
||||||
conn.setDoOutput(true);
|
|
||||||
|
|
||||||
try {
|
|
||||||
conn.connect();
|
|
||||||
try (OutputStream out = conn.getOutputStream()) {
|
|
||||||
out.write(bytePayload);
|
|
||||||
}
|
|
||||||
try (InputStream in = conn.getInputStream()) {
|
|
||||||
return asString(in);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
try (InputStream in = conn.getErrorStream()) {
|
|
||||||
return readErrorStream(in, e);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
conn.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String readErrorStream(InputStream in, IOException e) throws IOException {
|
|
||||||
if (in == null)
|
|
||||||
throw e;
|
|
||||||
|
|
||||||
try {
|
|
||||||
return asString(in);
|
|
||||||
} catch (IOException e1) {
|
|
||||||
if (e != e1)
|
|
||||||
e1.addSuppressed(e);
|
|
||||||
|
|
||||||
throw e1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private HttpURLConnection createConnection(String url, Map<String, String> headers) throws IOException {
|
|
||||||
HttpURLConnection conn = createConnection(new URL(url));
|
|
||||||
if (headers != null)
|
|
||||||
headers.forEach((key, value) -> conn.setRequestProperty(key, value));
|
|
||||||
return conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
private HttpURLConnection createConnection(URL url) throws IOException {
|
|
||||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
|
||||||
conn.setConnectTimeout(timeout);
|
|
||||||
conn.setReadTimeout(timeout);
|
|
||||||
conn.setUseCaches(false);
|
|
||||||
return conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -6,11 +6,16 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import org.to2mbn.authlibinjector.internal.org.json.JSONException;
|
import java.net.URL;
|
||||||
import org.to2mbn.authlibinjector.internal.org.json.JSONObject;
|
|
||||||
|
|
||||||
public final class IOUtils {
|
public final class IOUtils {
|
||||||
|
|
||||||
|
public static String readURL(String url) throws IOException {
|
||||||
|
try (InputStream in = new URL(url).openStream()) {
|
||||||
|
return asString(in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static String asString(InputStream in) throws IOException {
|
public static String asString(InputStream in) throws IOException {
|
||||||
CharArrayWriter w = new CharArrayWriter();
|
CharArrayWriter w = new CharArrayWriter();
|
||||||
Reader reader = new InputStreamReader(in, UTF_8);
|
Reader reader = new InputStreamReader(in, UTF_8);
|
||||||
|
@ -22,10 +27,6 @@ public final class IOUtils {
|
||||||
return new String(w.toCharArray());
|
return new String(w.toCharArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JSONObject asJson(String data) throws JSONException {
|
|
||||||
return new JSONObject(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IOUtils() {}
|
private IOUtils() {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue