001/**
002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com).
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * http://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.jboot.components.gateway;
017
018import com.jfinal.kit.Ret;
019import io.jboot.Jboot;
020import io.jboot.utils.StrUtil;
021import io.jboot.web.render.JbootJsonRender;
022
023import javax.servlet.http.HttpServletRequest;
024import javax.servlet.http.HttpServletResponse;
025import java.net.ConnectException;
026
027
028public class GatewayInvocation {
029
030    private JbootGatewayConfig config;
031    private GatewayInterceptor[] inters;
032    private HttpServletRequest request;
033    private HttpServletResponse response;
034    private GatewayHttpProxy proxy;
035    private String proxyUrl;
036
037    //是否跳过错误渲染,如果跳过,那么则由拦截器通过 getResponse() 自行渲染
038    private boolean skipExceptionRender = false;
039
040    private static boolean devMode = Jboot.isDevMode();
041
042    private int index = 0;
043
044
045    public GatewayInvocation(JbootGatewayConfig config, HttpServletRequest request, HttpServletResponse response) {
046        this.config = config;
047        this.request = request;
048        this.response = response;
049        this.inters = config.getGatewayInterceptors();
050        this.proxy = new GatewayHttpProxy(config);
051        this.proxyUrl = buildProxyUrl(config, request);
052    }
053
054
055    public void invoke() {
056        if (inters.length == 0) {
057            doInvoke();
058            return;
059        }
060        if (index < inters.length) {
061            inters[index++].intercept(this);
062        } else if (index++ >= inters.length) {
063            doInvoke();
064        }
065    }
066
067
068    protected void doInvoke() {
069        if (StrUtil.isBlank(proxyUrl)) {
070            renderError(null, GatewayErrorRender.noneHealthUrl, config, request, response);
071            return;
072        }
073
074        if (devMode) {
075            System.out.println("Jboot Gateway >>> " + proxyUrl);
076        }
077
078        //启用 Sentinel 限流
079        if (config.isSentinelEnable()) {
080            new GatewaySentinelProcesser().process(proxy, proxyUrl, config, request, response, skipExceptionRender);
081        }else {
082
083            //未启用 Sentinel 的情况
084            proxy.sendRequest(proxyUrl, request, response);
085
086            Exception exception = proxy.getException();
087            if (exception != null && !skipExceptionRender) {
088                if (exception instanceof ConnectException) {
089                    Ret connectionError = Ret.fail().set("errorCode", 2).set("message", "Can not connect to target server: " + proxyUrl);
090                    renderError(exception, connectionError, config, request, response);
091                } else {
092                    Ret ret = Ret.fail().set("errorCode", 9).set("message", exception.getMessage());
093                    renderError(exception, ret, config, request, response);
094                }
095            }
096        }
097
098    }
099
100
101    private static void renderError(Exception error, Ret errorMessage, JbootGatewayConfig config, HttpServletRequest request, HttpServletResponse response) {
102        GatewayErrorRender errorRender = JbootGatewayManager.me().getGatewayErrorRender();
103        if (errorRender != null) {
104            errorRender.renderError(error, errorMessage, config, request, response);
105        } else {
106            new JbootJsonRender(errorMessage).setContext(request, response).render();
107        }
108    }
109
110
111    private static String buildProxyUrl(JbootGatewayConfig config, HttpServletRequest request) {
112        //配置负载均衡策略
113        GatewayLoadBalanceStrategy lbs = config.buildLoadBalanceStrategy();
114
115        //通过负载均衡策略获取 URL 地址
116        String url = lbs.getUrl(config, request);
117        if (StrUtil.isBlank(url)) {
118            return null;
119        }
120
121        StringBuilder sb = new StringBuilder(url);
122        if (StrUtil.isNotBlank(request.getRequestURI())) {
123            sb.append(request.getRequestURI());
124        }
125
126        if (StrUtil.isNotBlank(request.getQueryString())) {
127            sb.append("?").append(request.getQueryString());
128        }
129
130        return sb.toString();
131    }
132
133
134    public JbootGatewayConfig getConfig() {
135        return config;
136    }
137
138    public void setConfig(JbootGatewayConfig config) {
139        this.config = config;
140    }
141
142    public GatewayInterceptor[] getInters() {
143        return inters;
144    }
145
146    public void setInters(GatewayInterceptor[] inters) {
147        this.inters = inters;
148    }
149
150    public HttpServletRequest getRequest() {
151        return request;
152    }
153
154    public void setRequest(HttpServletRequest request) {
155        this.request = request;
156    }
157
158    public HttpServletResponse getResponse() {
159        return response;
160    }
161
162    public void setResponse(HttpServletResponse response) {
163        this.response = response;
164    }
165
166    public int getIndex() {
167        return index;
168    }
169
170    public void setIndex(int index) {
171        this.index = index;
172    }
173
174    public GatewayHttpProxy getProxy() {
175        return proxy;
176    }
177
178
179    public void setProxy(GatewayHttpProxy proxy) {
180        this.proxy = proxy;
181    }
182
183    public boolean hasException() {
184        return proxy.getException() != null;
185    }
186
187    public String getProxyUrl() {
188        return proxyUrl;
189    }
190
191    public void setProxyUrl(String proxyUrl) {
192        this.proxyUrl = proxyUrl;
193    }
194
195    public boolean isSkipExceptionRender() {
196        return skipExceptionRender;
197    }
198
199    public void setSkipExceptionRender(boolean skipExceptionRender) {
200        this.skipExceptionRender = skipExceptionRender;
201    }
202}