mirror of
https://github.com/unmojang/authlib-injector.git
synced 2025-10-02 07:42:38 -04:00
Support reverse proxy
This commit is contained in:
parent
bdd5f4bfcb
commit
9cfb6325a3
@ -1,13 +1,26 @@
|
||||
package moe.yushi.authlibinjector.httpd;
|
||||
|
||||
import static moe.yushi.authlibinjector.util.IOUtils.transfer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import moe.yushi.authlibinjector.internal.fi.iki.elonen.IHTTPSession;
|
||||
import moe.yushi.authlibinjector.internal.fi.iki.elonen.IStatus;
|
||||
import moe.yushi.authlibinjector.internal.fi.iki.elonen.NanoHTTPD;
|
||||
import moe.yushi.authlibinjector.internal.fi.iki.elonen.Response;
|
||||
import moe.yushi.authlibinjector.internal.fi.iki.elonen.Status;
|
||||
@ -15,8 +28,8 @@ import moe.yushi.authlibinjector.util.Logging;
|
||||
|
||||
public class URLProcessor {
|
||||
|
||||
private static final Pattern URL_REGEX = Pattern.compile("^https?:\\/\\/(?<domain>[^\\/]+)(?<path>\\/.*)$");
|
||||
private static final Pattern LOCAL_URL_REGEX = Pattern.compile("^/(?<domain>[^\\/]+)(?<path>\\/.*)$");
|
||||
private static final Pattern URL_REGEX = Pattern.compile("^(?<protocol>https?):\\/\\/(?<domain>[^\\/]+)(?<path>\\/.*)$");
|
||||
private static final Pattern LOCAL_URL_REGEX = Pattern.compile("^/(?<protocol>https?)/(?<domain>[^\\/]+)(?<path>\\/.*)$");
|
||||
|
||||
private List<URLFilter> filters;
|
||||
private URLRedirector redirector;
|
||||
@ -31,17 +44,18 @@ public class URLProcessor {
|
||||
if (!matcher.find()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
String protocol = matcher.group("protocol");
|
||||
String domain = matcher.group("domain");
|
||||
String path = matcher.group("path");
|
||||
|
||||
Optional<String> result = transform(domain, path);
|
||||
Optional<String> result = transform(protocol, domain, path);
|
||||
if (result.isPresent()) {
|
||||
Logging.TRANSFORM.fine("Transformed url [" + inputUrl + "] to [" + result.get() + "]");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Optional<String> transform(String domain, String path) {
|
||||
private Optional<String> transform(String protocol, String domain, String path) {
|
||||
boolean handleLocally = false;
|
||||
for (URLFilter filter : filters) {
|
||||
if (filter.canHandle(domain, path)) {
|
||||
@ -51,7 +65,7 @@ public class URLProcessor {
|
||||
}
|
||||
|
||||
if (handleLocally) {
|
||||
return Optional.of("http://127.0.0.1:" + getLocalApiPort() + "/" + domain + path);
|
||||
return Optional.of("http://127.0.0.1:" + getLocalApiPort() + "/" + protocol + "/" + domain + path);
|
||||
}
|
||||
|
||||
return redirector.redirect(domain, path);
|
||||
@ -81,6 +95,7 @@ public class URLProcessor {
|
||||
public Response serve(IHTTPSession session) {
|
||||
Matcher matcher = LOCAL_URL_REGEX.matcher(session.getUri());
|
||||
if (matcher.find()) {
|
||||
String protocol = matcher.group("protocol");
|
||||
String domain = matcher.group("domain");
|
||||
String path = matcher.group("path");
|
||||
for (URLFilter filter : filters) {
|
||||
@ -90,7 +105,7 @@ public class URLProcessor {
|
||||
result = filter.handle(domain, path, session);
|
||||
} catch (Throwable e) {
|
||||
Logging.HTTPD.log(Level.WARNING, "An error occurred while processing request [" + session.getUri() + "]", e);
|
||||
return Response.newFixedLength(Status.INTERNAL_ERROR, null, null);
|
||||
return Response.newFixedLength(Status.INTERNAL_ERROR, MIME_PLAINTEXT, "Internal Server Error");
|
||||
}
|
||||
|
||||
if (result.isPresent()) {
|
||||
@ -99,11 +114,93 @@ public class URLProcessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logging.HTTPD.fine("No handler is found for [" + session.getUri() + "]");
|
||||
return Response.newFixedLength(Status.NOT_FOUND, MIME_PLAINTEXT, "Not Found");
|
||||
String target = redirector.redirect(domain, path)
|
||||
.orElseGet(() -> protocol + "://" + domain + path);
|
||||
try {
|
||||
return reverseProxy(session, target);
|
||||
} catch (IOException e) {
|
||||
Logging.HTTPD.log(Level.WARNING, "Reserve proxy error", e);
|
||||
return Response.newFixedLength(Status.BAD_GATEWAY, MIME_PLAINTEXT, "Bad Gateway");
|
||||
}
|
||||
} else {
|
||||
Logging.HTTPD.fine("No handler is found for [" + session.getUri() + "]");
|
||||
return Response.newFixedLength(Status.NOT_FOUND, MIME_PLAINTEXT, "Not Found");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static final Set<String> ignoredHeaders = new HashSet<>(Arrays.asList("host", "expect", "connection", "keep-alive", "transfer-encoding"));
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
private Response reverseProxy(IHTTPSession session, String upstream) throws IOException {
|
||||
String method = session.getMethod();
|
||||
|
||||
String url = session.getQueryParameterString() == null ? upstream : upstream + "?" + session.getQueryParameterString();
|
||||
|
||||
Map<String, String> requestHeaders = session.getHeaders();
|
||||
ignoredHeaders.forEach(requestHeaders::remove);
|
||||
|
||||
InputStream clientIn = session.getInputStream();
|
||||
|
||||
Logging.HTTPD.fine(() -> "Reserve proxy: > " + method + " " + url + ", headers: " + requestHeaders);
|
||||
|
||||
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.setRequestMethod(method);
|
||||
conn.setDoOutput(clientIn != null);
|
||||
requestHeaders.forEach(conn::setRequestProperty);
|
||||
conn.connect();
|
||||
|
||||
if (clientIn != null) {
|
||||
try (OutputStream upstreamOut = conn.getOutputStream()) {
|
||||
transfer(clientIn, upstreamOut);
|
||||
}
|
||||
}
|
||||
|
||||
int responseCode = conn.getResponseCode();
|
||||
String reponseMessage = conn.getResponseMessage();
|
||||
Map<String, List<String>> responseHeaders = new LinkedHashMap<>();
|
||||
conn.getHeaderFields().forEach((name, values) -> {
|
||||
if (name != null && !ignoredHeaders.contains(name.toLowerCase())) {
|
||||
responseHeaders.put(name, values);
|
||||
}
|
||||
});
|
||||
InputStream upstreamIn;
|
||||
try {
|
||||
upstreamIn = conn.getInputStream();
|
||||
} catch (IOException e) {
|
||||
upstreamIn = conn.getErrorStream();
|
||||
}
|
||||
Logging.HTTPD.fine(() -> "Reserve proxy: < " + responseCode + " " + reponseMessage + " , headers: " + responseHeaders);
|
||||
|
||||
IStatus status = new IStatus() {
|
||||
@Override
|
||||
public int getRequestStatus() {
|
||||
return responseCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return responseCode + " " + reponseMessage;
|
||||
}
|
||||
};
|
||||
|
||||
long contentLength = -1;
|
||||
for (Entry<String, List<String>> header : responseHeaders.entrySet()) {
|
||||
if ("content-length".equalsIgnoreCase(header.getKey())) {
|
||||
contentLength = Long.parseLong(header.getValue().get(0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
Response response;
|
||||
if (contentLength != -1) {
|
||||
response = Response.newFixedLength(status, null, upstreamIn, contentLength);
|
||||
} else {
|
||||
response = Response.newChunked(status, null, upstreamIn);
|
||||
}
|
||||
responseHeaders.forEach((name, values) -> values.forEach(value -> response.addHeader(name, value)));
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ public enum Status implements IStatus {
|
||||
|
||||
INTERNAL_ERROR(500, "Internal Server Error"),
|
||||
NOT_IMPLEMENTED(501, "Not Implemented"),
|
||||
BAD_GATEWAY(502, "Bad Gateway"),
|
||||
SERVICE_UNAVAILABLE(503, "Service Unavailable"),
|
||||
UNSUPPORTED_HTTP_VERSION(505, "HTTP Version Not Supported");
|
||||
|
||||
|
@ -40,12 +40,16 @@ public final class IOUtils {
|
||||
|
||||
public static byte[] asBytes(InputStream in) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
transfer(in, out);
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
public static void transfer(InputStream from, OutputStream to) throws IOException {
|
||||
byte[] buf = new byte[8192];
|
||||
int read;
|
||||
while ((read = in.read(buf)) != -1) {
|
||||
out.write(buf, 0, read);
|
||||
while ((read = from.read(buf)) != -1) {
|
||||
to.write(buf, 0, read);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
public static String asString(byte[] bytes) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user