All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.vertx.httpproxy.impl.ReverseProxy Maven / Gradle / Ivy

There is a newer version: 3.5.45
Show newest version
/**
 * XXX 复制4.4.3版本的io.vertx.httpproxy.impl.ReverseProxy类的代码,让websocket也支持代理拦截器
 * Copyright (c) 2011-2020 Contributors to the Eclipse Foundation
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
 * which is available at https://www.apache.org/licenses/LICENSE-2.0.
 *
 * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
 */
package io.vertx.httpproxy.impl;

import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpClientRequest;
import io.vertx.core.http.HttpClientResponse;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.http.HttpVersion;
import io.vertx.core.net.NetSocket;
import io.vertx.httpproxy.*;
import io.vertx.httpproxy.cache.CacheOptions;
import io.vertx.httpproxy.spi.cache.Cache;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.function.BiFunction;

@Slf4j
public class ReverseProxy implements HttpProxy {

    private final HttpClient client;
    private final boolean supportWebSocket;
    private BiFunction> selector = (req, client) -> Future.failedFuture("No origin available");
    private final List interceptors = new ArrayList<>();

    public ReverseProxy(ProxyOptions options, HttpClient client) {
        CacheOptions cacheOptions = options.getCacheOptions();
        if (cacheOptions != null) {
            Cache cache = cacheOptions.newCache();
            addInterceptor(new CachingFilter(cache));
        }
        this.client = client;
        this.supportWebSocket = options.getSupportWebSocket();
    }

    @Override
    public HttpProxy originRequestProvider(BiFunction> provider) {
        selector = provider;
        return this;
    }

    @Override
    public HttpProxy addInterceptor(ProxyInterceptor interceptor) {
        interceptors.add(interceptor);
        return this;
    }


    @Override
    public void handle(HttpServerRequest request) {
        ProxyRequest proxyRequest = ProxyRequest.reverseProxy(request);

        // Encoding sanity check
        Boolean chunked = HttpUtils.isChunked(request.headers());
        if (chunked == null) {
            end(proxyRequest, 400);
            return;
        }

        // WebSocket upgrade tunneling
        if (supportWebSocket &&
                request.version() == HttpVersion.HTTP_1_1 &&
                request.method() == HttpMethod.GET &&
                request.headers().contains(HttpHeaders.CONNECTION, HttpHeaders.UPGRADE, true)) {
            handleWebSocketUpgrade(proxyRequest);
            return;
        }

        Proxy proxy = new Proxy(proxyRequest);
        proxy.filters = interceptors.listIterator();
        proxy.sendRequest().compose(proxy::sendProxyResponse);
    }

    private void handleWebSocketUpgrade(ProxyRequest proxyRequest) {
        // XXX 添加调用拦截器修改代理请求
        interceptors.forEach(proxyInterceptor -> proxyInterceptor.modifyProxyRequest(proxyRequest));

        HttpServerRequest proxiedRequest = proxyRequest.proxiedRequest();
        resolveOrigin(proxiedRequest).onComplete(ar -> {
            if (ar.succeeded()) {
                HttpClientRequest request = ar.result();
                request.setMethod(HttpMethod.GET);

                // XXX 过滤器可能要改变目标请求的uri,所以这里用代理请求的URI,而不是用被代理请求的URI
                // request.setURI(proxiedRequest.uri());
                request.setURI(proxyRequest.getURI());
                log.debug("handleWebSocketUpgrade: request.getURI()={}", request.getURI());

                request.headers().addAll(proxiedRequest.headers());
                Future fut2 = request.connect();
                proxiedRequest.handler(request::write);
                proxiedRequest.endHandler(v -> request.end());
                proxiedRequest.resume();
                fut2.onComplete(ar2 -> {
                    if (ar2.succeeded()) {
                        HttpClientResponse proxiedResponse = ar2.result();
                        if (proxiedResponse.statusCode() == 101) {
                            HttpServerResponse response = proxiedRequest.response();
                            response.setStatusCode(101);
                            response.headers().addAll(proxiedResponse.headers());
                            Future otherso = proxiedRequest.toNetSocket();
                            otherso.onComplete(ar3 -> {
                                if (ar3.succeeded()) {
                                    NetSocket responseSocket = ar3.result();
                                    NetSocket proxyResponseSocket = proxiedResponse.netSocket();
                                    responseSocket.handler(proxyResponseSocket::write);
                                    proxyResponseSocket.handler(responseSocket::write);
                                    responseSocket.closeHandler(v -> proxyResponseSocket.close());
                                    proxyResponseSocket.closeHandler(v -> responseSocket.close());
                                } else {
                                    // Find reproducer
                                    System.err.println("Handle this case");
                                    ar3.cause().printStackTrace();
                                }
                            });
                        } else {
                            // Rejection
                            proxiedRequest.resume();
                            end(proxyRequest, proxiedResponse.statusCode());
                        }
                    } else {
                        proxiedRequest.resume();
                        end(proxyRequest, 502);
                    }
                });
            } else {
                proxiedRequest.resume();
                end(proxyRequest, 502);
            }
        });
    }

    private void end(ProxyRequest proxyRequest, int sc) {
        proxyRequest
                .response()
                .release()
                .setStatusCode(sc)
                .putHeader(HttpHeaders.CONTENT_LENGTH, "0")
                .setBody(null)
                .send();
    }

    private Future resolveOrigin(HttpServerRequest proxiedRequest) {
        return selector.apply(proxiedRequest, client);
    }

    private class Proxy implements ProxyContext {

        private final ProxyRequest request;
        private ProxyResponse response;
        private final Map attachments = new HashMap<>();
        private ListIterator filters;

        private Proxy(ProxyRequest request) {
            this.request = request;
        }

        @Override
        public void set(String name, Object value) {
            attachments.put(name, value);
        }

        @Override
        public  T get(String name, Class type) {
            Object o = attachments.get(name);
            return type.isInstance(o) ? type.cast(o) : null;
        }

        @Override
        public ProxyRequest request() {
            return request;
        }

        @Override
        public Future sendRequest() {
            if (filters.hasNext()) {
                ProxyInterceptor next = filters.next();
                return next.handleProxyRequest(this);
            } else {
                return sendProxyRequest(request);
            }
        }

        @Override
        public ProxyResponse response() {
            return response;
        }

        @Override
        public Future sendResponse() {
            if (filters.hasPrevious()) {
                ProxyInterceptor filter = filters.previous();
                return filter.handleProxyResponse(this);
            } else {
                return response.send();
            }
        }

        private Future sendProxyRequest(ProxyRequest proxyRequest) {
            Future f = resolveOrigin(proxyRequest.proxiedRequest());
            f.onFailure(err -> {
                // Should this be done here ? I don't think so
                HttpServerRequest proxiedRequest = proxyRequest.proxiedRequest();
                proxiedRequest.resume();
                Promise promise = Promise.promise();
                proxiedRequest.exceptionHandler(promise::tryFail);
                proxiedRequest.endHandler(promise::tryComplete);
                promise.future().onComplete(ar2 -> {
                    end(proxyRequest, 502);
                });
            });
            return f.compose(a -> sendProxyRequest(proxyRequest, a));
        }

        private Future sendProxyRequest(ProxyRequest proxyRequest, HttpClientRequest request) {
            Future fut = proxyRequest.send(request);
            fut.onFailure(err -> {
                proxyRequest.proxiedRequest().response().setStatusCode(502).end();
            });
            return fut;
        }

        private Future sendProxyResponse(ProxyResponse response) {

            this.response = response;

            // Check validity
            Boolean chunked = HttpUtils.isChunked(response.headers());
            if (chunked == null) {
                // response.request().release(); // Is it needed ???
                end(response.request(), 501);
                return Future.succeededFuture(); // should use END future here ???
            }

            return sendResponse();
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy