Undertow achieve reverse proxy

Undertow as a web server, supports reverse proxy, more suitable for small platforms or develop test scenarios, using the following record

First reference undertow as a web server, springboot project I used in pom, the configuration reference

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>
        <!--  
        <dependency>
            <groupId>org.mitre.dsmiley.httpproxy</groupId>
            <artifactId>smiley-http-proxy-servlet</artifactId>
            <version>1.11</version>
        </dependency>
        -->
    </dependencies>

Note: Some of the above comments, because httpclient started the agent, using relatively simple, are interested can look at Baidu, then guess undertow should have a reverse proxy functionality, re-introduced smilley a bit redundant

undertow default reverse proxy can only proxy the entire service, similar nginx proxy is not a specific path, and I happened to need that the project will be forwarded / base path that starts Request Broker to other services, and therefore took a clever here, look Code

package com.iflytek.research.datawood.config;

import java.net.URI;

import javax.annotation.Resource;

import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import io.undertow.server.HandlerWrapper;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.DisallowedMethodsHandler;
import io.undertow.server.handlers.proxy.ProxyClient;
import io.undertow.server.handlers.proxy.ProxyHandler;
import io.undertow.server.handlers.proxy.SimpleProxyClientProvider;
import io.undertow.util.HttpString;

/**
 * @author ljgeng
 *
 */
@Configuration
public class UndertowConfiguration {
    
    @Resource
    private ProxyProperties ProxyProperties;
    
    @Bean
    public WebServerFactoryCustomizer<UndertowServletWebServerFactory> custom(){
        return new WebServerFactoryCustomizer<UndertowServletWebServerFactory>() {
            
            @Override
            public void customize(UndertowServletWebServerFactory factory) {
                factory.addDeploymentInfoCustomizers(deploymentInfo -> {
                    deploymentInfo.addInitialHandlerChainWrapper(new GatewayProxyHandlerWrapper());
                });
            }
            
            
        }; 
    } 
    
    /**
     * Gateway  反向代理
     *  http://undertow.io/undertow-docs/undertow-docs-2.0.0/index.html#reverse-proxy
     * @author ljgeng
     *
     */
    public class GatewayProxyHandlerWrapper implements HandlerWrapper {

        @Override
        public HttpHandler wrap(HttpHandler handler) {
            // 代理路径
            ProxyClient proxyClientProvider = new ProxyClientProvider(URI.create(ProxyProperties.getGateway().getUrl()));
            HttpString[] disallowedHttpMethods = { HttpString.tryFromString("TRACE"),
                    HttpString.tryFromString("TRACK") };
            return new ProxyHandler(proxyClientProvider, 0,  new DisallowedMethodsHandler(handler, disallowedHttpMethods));
        }
        
    }
    
    public class ProxyClientProvider extends SimpleProxyClientProvider {
        public ProxyClientProvider(URI uri) {
            super(uri);
        }
        
        /**
         *  重写findTarget 方法: 指定转发的路径。
         */
        @Override
        public ProxyTarget findTarget(HttpServerExchange exchange) {
            // 代理/base路径
            if (exchange.getRequestPath().startsWith("/base")) {
                // 修改路径,去除base
                exchange.setResolvedPath("/base");
                return super.findTarget(exchange);
            }
            return null;
        }
        
    }
    
}

Mainly to see ProxyClientProvider, it re-implements SimpleProxyClientProvider findTarget method is called when the request path begins with / base parent class method, otherwise it returns null. Method exchange.setResolvedPath ( "/ base") is to make the path to the agent no longer contains base. Some may wonder why this returns null can bypass the proxy, to clarify this problem, then we should look handleRequest method ProxyHandler in the end of what has been done (undertow for further processing of the request by handleRequest handler)

public void handleRequest(final HttpServerExchange exchange) throws Exception {
        final ProxyClient.ProxyTarget target = proxyClient.findTarget(exchange);
        if (target == null) {
            log.debugf("No proxy target for request to %s", exchange.getRequestURL());
            next.handleRequest(exchange);
            return;
        }
        if(exchange.isResponseStarted()) {
            //we can't proxy a request that has already started, this is basically a server configuration error
            UndertowLogger.REQUEST_LOGGER.cannotProxyStartedRequest(exchange);
            exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR);
            exchange.endExchange();
            return;
        }
        final long timeout = maxRequestTime > 0 ? System.currentTimeMillis() + maxRequestTime : 0;
        int maxRetries = maxConnectionRetries;
        if(target instanceof ProxyClient.MaxRetriesProxyTarget) {
            maxRetries = Math.max(maxRetries, ((ProxyClient.MaxRetriesProxyTarget) target).getMaxRetries());
        }
        final ProxyClientHandler clientHandler = new ProxyClientHandler(exchange, target, timeout, maxRetries, idempotentRequestPredicate);
        if (timeout > 0) {
            final XnioExecutor.Key key = WorkerUtils.executeAfter(exchange.getIoThread(), new Runnable() {
                @Override
                public void run() {
                    clientHandler.cancel(exchange);
                }
            }, maxRequestTime, TimeUnit.MILLISECONDS);
            exchange.putAttachment(TIMEOUT_KEY, key);
            exchange.addExchangeCompleteListener(new ExchangeCompletionListener() {
                @Override
                public void exchangeEvent(HttpServerExchange exchange, NextListener nextListener) {
                    key.remove();
                    nextListener.proceed();
                }
            });
        }
        exchange.dispatch(exchange.isInIoThread() ? SameThreadExecutor.INSTANCE : exchange.getIoThread(), clientHandler);
    }

We can clearly see, if the target is null, go to the next handler no longer continue to take the logic of the agent.

Guess you like

Origin www.cnblogs.com/ljgeng/p/11240012.html