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 io.jboot.core.spi.JbootSpiLoader; 019import io.jboot.utils.ArrayUtil; 020import io.jboot.utils.ClassUtil; 021import io.jboot.utils.StrUtil; 022 023import javax.servlet.http.HttpServletRequest; 024import java.io.Serializable; 025import java.util.*; 026 027/** 028 * @author michael yang (fuhai999@gmail.com) 029 * @Date: 2020/3/22 030 */ 031public class JbootGatewayConfig implements Serializable { 032 033 public static final String DEFAULT_PROXY_CONTENT_TYPE = "text/html;charset=utf-8"; 034 public static final GatewayInterceptor[] EMPTY_GATEWAY_INTERCEPTOR_ARRAY = new GatewayInterceptor[0]; 035 036 private String name; 037 private Set<String> uri; 038 039 040 // 是否启用健康检查 041 private boolean uriHealthCheckEnable; 042 043 // URI 健康检查路径,要求服务 statusCode = 200 044 // 当配置 uriHealthCheckPath 后,健康检查的 url 地址为 uri + uriHealthCheckPath 045 private String uriHealthCheckPath; 046 047 // 是否启用 048 private boolean enable = false; 049 050 // 是否启用 sentinel 限流 051 private boolean sentinelEnable = false; 052 // sentinel 被限流后跳转地址 053 private String sentinelBlockPage; 054 // sentinel 被渲染的json内容,如果配置 sentinelBlockPage,则 sentinelBlockJsonMap 配置无效 055 private Map<String, String> sentinelBlockJsonMap; 056 057 // 设置 http 代理的参数 058 private int proxyReadTimeout = 10000; //10s 059 private int proxyConnectTimeout = 5000; //5s 060 private int proxyRetries = 2; //2 times 061 private String proxyContentType = DEFAULT_PROXY_CONTENT_TYPE; 062 063 064 private String[] pathEquals; 065 private String[] pathContains; 066 private String[] pathStartsWith; 067 private String[] pathEndsWith; 068 069 070 private String[] hostEquals; 071 private String[] hostContains; 072 private String[] hostStartsWith; 073 private String[] hostEndsWith; 074 075 076 private Map<String, String> queryEquals; 077 private String[] queryContains; 078 079 //拦截器配置,一般可以用于对请求进行 鉴权 等处理 080 private String[] interceptors; 081 private String loadBalanceStrategy; 082 083// 暂时不支持 cookie 084// private Map<String, String> cookieEquals; 085// private String[] cookieContains; 086 087 //不健康的 URI 地址 088 private Set<String> unHealthUris = Collections.synchronizedSet(new HashSet<>()); 089 090 091 public String getName() { 092 return name; 093 } 094 095 public void setName(String name) { 096 this.name = name; 097 } 098 099 100 public Set<String> getUri() { 101 return uri; 102 } 103 104 public void setUri(Set<String> uri) { 105 this.uri = uri; 106 } 107 108 //服务发现的 URI 列表 109 private Set<String> discoveryUris; 110 111 //健康的 URI 缓存 112 private String[] healthUris; 113 114 //健康 URI 是否有变化的标识 115 private boolean healthUriChanged = true; 116 117 public String[] getHealthUris() { 118 if (healthUriChanged) { 119 synchronized (this) { 120 if (healthUriChanged) { 121 if ((uri == null || uri.isEmpty()) && (discoveryUris == null || discoveryUris.isEmpty())) { 122 healthUris = null; 123 } else { 124 HashSet<String> healthUriSet = new HashSet<>(); 125 if (uri != null && !uri.isEmpty()) { 126 healthUriSet.addAll(uri); 127 } 128 129 if (discoveryUris != null && !discoveryUris.isEmpty()) { 130 healthUriSet.addAll(discoveryUris); 131 } 132 133 if (!unHealthUris.isEmpty()) { 134 healthUriSet.removeAll(unHealthUris); 135 } 136 healthUris = healthUriSet.isEmpty() ? null : healthUriSet.toArray(new String[healthUriSet.size()]); 137 138 } 139 healthUriChanged = false; 140 } 141 } 142 } 143 return healthUris; 144 } 145 146 public boolean isUriHealthCheckEnable() { 147 return uriHealthCheckEnable; 148 } 149 150 public void setUriHealthCheckEnable(boolean uriHealthCheckEnable) { 151 this.uriHealthCheckEnable = uriHealthCheckEnable; 152 } 153 154 public String getUriHealthCheckPath() { 155 return uriHealthCheckPath; 156 } 157 158 public void setUriHealthCheckPath(String uriHealthCheckPath) { 159 this.uriHealthCheckPath = uriHealthCheckPath; 160 } 161 162 163 public boolean isEnable() { 164 return enable; 165 } 166 167 public void setEnable(boolean enable) { 168 this.enable = enable; 169 } 170 171 public boolean isSentinelEnable() { 172 return sentinelEnable; 173 } 174 175 public void setSentinelEnable(boolean sentinelEnable) { 176 this.sentinelEnable = sentinelEnable; 177 } 178 179 public String getSentinelBlockPage() { 180 return sentinelBlockPage; 181 } 182 183 public void setSentinelBlockPage(String sentinelBlockPage) { 184 this.sentinelBlockPage = sentinelBlockPage; 185 } 186 187 public Map<String, String> getSentinelBlockJsonMap() { 188 return sentinelBlockJsonMap; 189 } 190 191 public void setSentinelBlockJsonMap(Map<String, String> sentinelBlockJsonMap) { 192 this.sentinelBlockJsonMap = sentinelBlockJsonMap; 193 } 194 195 public int getProxyReadTimeout() { 196 return proxyReadTimeout; 197 } 198 199 public void setProxyReadTimeout(int proxyReadTimeout) { 200 this.proxyReadTimeout = proxyReadTimeout; 201 } 202 203 public int getProxyConnectTimeout() { 204 return proxyConnectTimeout; 205 } 206 207 public void setProxyConnectTimeout(int proxyConnectTimeout) { 208 this.proxyConnectTimeout = proxyConnectTimeout; 209 } 210 211 public int getProxyRetries() { 212 return proxyRetries; 213 } 214 215 public void setProxyRetries(int proxyRetries) { 216 this.proxyRetries = proxyRetries; 217 } 218 219 public String getProxyContentType() { 220 return proxyContentType; 221 } 222 223 public void setProxyContentType(String proxyContentType) { 224 this.proxyContentType = proxyContentType; 225 } 226 227 public String[] getPathEquals() { 228 return pathEquals; 229 } 230 231 public void setPathEquals(String[] pathEquals) { 232 this.pathEquals = pathEquals; 233 } 234 235 public String[] getPathContains() { 236 return pathContains; 237 } 238 239 public void setPathContains(String[] pathContains) { 240 this.pathContains = pathContains; 241 } 242 243 public String[] getPathStartsWith() { 244 return pathStartsWith; 245 } 246 247 public void setPathStartsWith(String[] pathStartsWith) { 248 this.pathStartsWith = pathStartsWith; 249 } 250 251 public String[] getPathEndsWith() { 252 return pathEndsWith; 253 } 254 255 public void setPathEndsWith(String[] pathEndsWith) { 256 this.pathEndsWith = pathEndsWith; 257 } 258 259 public String[] getHostEquals() { 260 return hostEquals; 261 } 262 263 public void setHostEquals(String[] hostEquals) { 264 this.hostEquals = hostEquals; 265 } 266 267 public String[] getHostContains() { 268 return hostContains; 269 } 270 271 public void setHostContains(String[] hostContains) { 272 this.hostContains = hostContains; 273 } 274 275 public String[] getHostStartsWith() { 276 return hostStartsWith; 277 } 278 279 public void setHostStartsWith(String[] hostStartsWith) { 280 this.hostStartsWith = hostStartsWith; 281 } 282 283 public String[] getHostEndsWith() { 284 return hostEndsWith; 285 } 286 287 public void setHostEndsWith(String[] hostEndsWith) { 288 this.hostEndsWith = hostEndsWith; 289 } 290 291 public Map<String, String> getQueryEquals() { 292 return queryEquals; 293 } 294 295 public void setQueryEquals(Map<String, String> queryEquals) { 296 this.queryEquals = queryEquals; 297 } 298 299 public String[] getQueryContains() { 300 return queryContains; 301 } 302 303 public void setQueryContains(String[] queryContains) { 304 this.queryContains = queryContains; 305 } 306 307 public String[] getInterceptors() { 308 return interceptors; 309 } 310 311 public void setInterceptors(String[] interceptors) { 312 this.interceptors = interceptors; 313 } 314 315 316 private GatewayInterceptor[] gatewayInterceptors; 317 318 public GatewayInterceptor[] getGatewayInterceptors() { 319 if (gatewayInterceptors == null) { 320 if (interceptors == null || interceptors.length == 0) { 321 gatewayInterceptors = EMPTY_GATEWAY_INTERCEPTOR_ARRAY; 322 } else { 323 gatewayInterceptors = new GatewayInterceptor[interceptors.length]; 324 for (int i = 0; i < interceptors.length; i++) { 325// GatewayInterceptor interceptor = ClassUtil.newInstance(interceptors[i]); 326 GatewayInterceptor interceptor = JbootSpiLoader.load(GatewayInterceptor.class, interceptors[i]); 327 if (interceptor == null) { 328 throw new NullPointerException("can not new instance by class:" + interceptors[i]); 329 } 330 gatewayInterceptors[i] = interceptor; 331 } 332 } 333 } 334 return gatewayInterceptors; 335 } 336 337 338 public void setGatewayInterceptors(GatewayInterceptor[] gatewayInterceptors) { 339 this.gatewayInterceptors = gatewayInterceptors; 340 } 341 342 343 public String getLoadBalanceStrategy() { 344 return loadBalanceStrategy; 345 } 346 347 public void setLoadBalanceStrategy(String loadBalanceStrategy) { 348 this.loadBalanceStrategy = loadBalanceStrategy; 349 } 350 351 private GatewayLoadBalanceStrategy gatewayLoadBalanceStrategy; 352 353 public GatewayLoadBalanceStrategy buildLoadBalanceStrategy() { 354 if (gatewayLoadBalanceStrategy != null) { 355 return gatewayLoadBalanceStrategy; 356 } 357 358 if (StrUtil.isBlank(loadBalanceStrategy)) { 359 gatewayLoadBalanceStrategy = GatewayLoadBalanceStrategy.DEFAULT_STRATEGY; 360 } else { 361 GatewayLoadBalanceStrategy glbs = ClassUtil.newInstance(loadBalanceStrategy); 362 if (glbs == null) { 363 throw new NullPointerException("Can not new instance by class: " + loadBalanceStrategy); 364 } 365 gatewayLoadBalanceStrategy = glbs; 366 } 367 368 return gatewayLoadBalanceStrategy; 369 } 370 371 public void setGatewayLoadBalanceStrategy(GatewayLoadBalanceStrategy strategy) { 372 this.gatewayLoadBalanceStrategy = strategy; 373 } 374 375 376 public boolean matches(HttpServletRequest request) { 377 if (request == null) { 378 return false; 379 } 380 381 String path = request.getServletPath(); 382 if (pathEquals != null) { 383 for (String p : pathEquals) { 384 if (path.equals(p)) { 385 return true; 386 } 387 } 388 } 389 390 if (pathContains != null) { 391 for (String p : pathContains) { 392 if (path.contains(p)) { 393 return true; 394 } 395 } 396 } 397 398 if (pathStartsWith != null) { 399 for (String p : pathStartsWith) { 400 if (path.startsWith(p)) { 401 return true; 402 } 403 } 404 } 405 406 if (pathEndsWith != null) { 407 for (String p : pathEndsWith) { 408 if (path.endsWith(p)) { 409 return true; 410 } 411 } 412 } 413 414 String host = request.getServerName(); 415 if (hostEquals != null) { 416 for (String h : hostEquals) { 417 if (host.equals(h)) { 418 return true; 419 } 420 } 421 } 422 423 if (hostContains != null) { 424 for (String h : hostContains) { 425 if (host.contains(h)) { 426 return true; 427 } 428 } 429 } 430 431 if (hostStartsWith != null) { 432 for (String h : hostStartsWith) { 433 if (host.startsWith(h)) { 434 return true; 435 } 436 } 437 } 438 439 if (hostEndsWith != null) { 440 for (String h : hostEndsWith) { 441 if (host.endsWith(h)) { 442 return true; 443 } 444 } 445 } 446 447 if (queryContains != null || queryEquals != null) { 448 Map<String, String> queryMap = StrUtil.queryStringToMap(request.getQueryString()); 449 if (!queryMap.isEmpty()) { 450 451 if (queryContains != null) { 452 for (String q : queryContains) { 453 if (queryMap.containsKey(q)) { 454 return true; 455 } 456 } 457 } 458 459 if (queryEquals != null) { 460 for (Map.Entry<String, String> e : queryEquals.entrySet()) { 461 String queryValue = queryMap.get(e.getKey()); 462 if (Objects.equals(queryValue, e.getValue())) { 463 return true; 464 } 465 } 466 } 467 } 468 } 469 470 471 return false; 472 } 473 474 public void syncDiscoveryUris(Collection<String> syncUris) { 475 if (ArrayUtil.isSameElements(syncUris, discoveryUris)) { 476 return; 477 } 478 479 if (syncUris == null || syncUris.isEmpty()) { 480 discoveryUris = null; 481 healthUriChanged = true; 482 return; 483 } 484 485 if (discoveryUris == null) { 486 discoveryUris = new HashSet<>(syncUris); 487 healthUriChanged = true; 488 } else { 489 discoveryUris.clear(); 490 discoveryUris.addAll(syncUris); 491 healthUriChanged = true; 492 } 493 } 494 495 496 public void addUnHealthUri(String uri) { 497 if (unHealthUris.add(uri)) { 498 healthUriChanged = true; 499 } 500 } 501 502 503 public void removeUnHealthUri(String uri) { 504 if (unHealthUris.size() > 0) { 505 if (unHealthUris.remove(uri)) { 506 healthUriChanged = true; 507 } 508 } 509 } 510 511 512}