使用Java实现nginx反向代理的功能

需求

访问某个url后缀时,例如:/abc/*,自动路由到指定http服务地址http://myhost/*

代码

httpServer.createContext("/abc", BeanFactory.proxyHandler("yourhost:8080"));

反向代理处理器

public class ProxyHandler implements HttpHandler {

    private final static Logger log = Logger.getLogger(ProxyHandler.class.getName());

    private final static List<String> RESERVED_HEADERS = List.of("connection", "host", "content-length");

    private final String contextPath;
    private final String targetHost;

    public ProxyHandler(String contextPath, String targetHost) {
        this.contextPath = contextPath;
        this.targetHost = targetHost;
    }

    @Override
    public void handle(HttpExchange exchange) throws IOException {
        try (exchange) {
            URI requestURI = exchange.getRequestURI();
            String path = requestURI.getPath();
            // 重写请求路径,去除代理路径前缀
            String targetPath = path.substring(this.contextPath.length());
            HttpResponse<byte[]> response;
            try {
                response = this.requestToTargetHost(this.targetHost,
                        targetPath,
                        exchange.getRequestHeaders(),
                        exchange.getRequestMethod(),
                        exchange.getRequestBody().readAllBytes());
                byte[] bytes = response.body();
                for (Map.Entry<String, List<String>> entry : response.headers().map().entrySet()) {
                    if (entry.getKey().equalsIgnoreCase("content-type")) {
                        exchange.getResponseHeaders().put(entry.getKey(), entry.getValue());
                    }
                }
                exchange.sendResponseHeaders(200, bytes.length);
                exchange.getResponseHeaders().set("Access-Control-Allow-Origin", "*");

                OutputStream responseOutputStream = exchange.getResponseBody();
                responseOutputStream.write(bytes);
                responseOutputStream.flush();
                responseOutputStream.close();
            } catch (IOException e) {
                if (e instanceof ConnectException) {
                    log.severe("ConnectException: " + e);
                    responseError(exchange, "Connect failed.");
                } else {
                    log.severe("IOException: " + e);
                    responseError(exchange, "Network input error");
                }
            } catch (InterruptedException e) {
                log.severe("InterruptedException: " + e);
                responseError(exchange, "Service is not available.");
            }
        }
    }

    private static void responseError(HttpExchange exchange, byte[] bytes) throws IOException {
        exchange.sendResponseHeaders(502, bytes.length);
        exchange.getResponseHeaders().set("Access-Control-Allow-Origin", "*");
        OutputStream responseOutputStream = exchange.getResponseBody();
        responseOutputStream.write(bytes);
        responseOutputStream.flush();
        responseOutputStream.close();
    }

    private static void responseError(HttpExchange exchange, String message) throws IOException {
        byte[] bytes = message.getBytes();
        responseError(exchange, bytes);
    }

    HttpResponse<byte[]> requestToTargetHost(String targetHost, String targetUri, Headers headers, String method, byte[] bytes) throws IOException, InterruptedException {
        try (HttpClient httpClient = HttpClient.newBuilder().build()) {
            String prefix = targetHost.contains("http://") || targetHost.contains("https://") ? "" : "http://";
            String hostAndUri = prefix + targetHost + targetUri;
            HttpRequest.Builder builder = HttpRequest.newBuilder();
            for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
                if (!RESERVED_HEADERS.contains(entry.getKey().toLowerCase())) {
                    builder.header(entry.getKey(), entry.getValue().getFirst());
                }
            }
            HttpRequest request = builder
                    .method(method, HttpRequest.BodyPublishers.ofByteArray(bytes))
                    .uri(URI.create(hostAndUri))
                    .build();
            return httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray());
        }
    }

}

posted @ 2024-06-14 14:40  漠孤烟  阅读(18)  评论(0)    收藏  举报