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

com.wl4g.component.common.web.WebUtils2 Maven / Gradle / Ivy

/*
 * Copyright 2017 ~ 2025 the original author or authors. 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.wl4g.component.common.web;

import static com.wl4g.component.common.collection.CollectionUtils2.isEmptyArray;
import static com.wl4g.component.common.collection.CollectionUtils2.safeMap;
import static com.wl4g.component.common.lang.Assert2.hasTextOf;
import static com.wl4g.component.common.lang.Assert2.notNull;
import static com.wl4g.component.common.lang.Assert2.notNullOf;
import static com.wl4g.component.common.lang.StringUtils2.isDomain;
import static com.wl4g.component.common.web.UserAgentUtils.isBrowser;
import static java.lang.String.format;
import static java.lang.System.getenv;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static java.util.Locale.US;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static java.util.stream.Collectors.toMap;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.apache.commons.lang3.StringUtils.contains;
import static org.apache.commons.lang3.StringUtils.containsAny;
import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
import static org.apache.commons.lang3.StringUtils.isAnyBlank;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isEmpty;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.join;
import static org.apache.commons.lang3.StringUtils.replaceIgnoreCase;
import static org.apache.commons.lang3.StringUtils.split;
import static org.apache.commons.lang3.StringUtils.startsWithIgnoreCase;
import static org.apache.commons.lang3.StringUtils.trimToEmpty;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.StringTokenizer;
import java.util.function.Predicate;

import javax.annotation.Nullable;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

import org.apache.commons.collections.EnumerationUtils;
import org.apache.commons.lang3.StringUtils;
import org.omg.CORBA.ServerRequest;

import com.google.common.annotations.Beta;
import com.google.common.base.Charsets;
import com.google.common.net.MediaType;
import com.wl4g.component.common.collection.CollectionUtils2;
import com.wl4g.component.common.collection.multimap.LinkedMultiValueMap;
import com.wl4g.component.common.collection.multimap.MultiValueMap;
import com.wl4g.component.common.jvm.JvmRuntimeKit;
import com.wl4g.component.common.lang.Assert2;
import com.wl4g.component.common.lang.StringUtils2;

/**
 * Generic Web utilitys.
 * 
 * @author Wangl.sir <[email protected]>
 * @version v1.0
 * @date 2018年11月30日
 * @since
 */
@Beta
public abstract class WebUtils2 {

	/**
	 * Gets HTTP remote IP address 
* Warning: Be careful if you are implementing security, as all of these * headers are easy to fake. * * @param request * HTTP request * @return Real remote client IP */ public static String getHttpRemoteAddr(HttpServletRequest request) { for (String header : HEADER_REAL_IP) { String ip = request.getHeader(header); if (isNotBlank(ip) && !"Unknown".equalsIgnoreCase(ip)) { return ip; } } return request.getRemoteAddr(); } /** * Output JSON data with default settings * * @param response * @param json * @throws IOException */ public static void writeJson(HttpServletResponse response, String json) throws IOException { write(response, HttpServletResponse.SC_OK, MediaType.JSON_UTF_8.toString(), json.getBytes(Charsets.UTF_8)); } /** * Output message * * @param response * @param status * @param contentType * @param body * @throws IOException */ public static void write(@NotNull HttpServletResponse response, int status, @NotBlank String contentType, @Nullable byte[] body) throws IOException { notNullOf(response, "response"); hasTextOf(contentType, "contentType"); OutputStream out = null; response.setCharacterEncoding("UTF-8"); response.setStatus(status); response.setContentType(contentType); if (!isNull(body)) { out = response.getOutputStream(); out.write(body); response.flushBuffer(); // out.close(); // [Cannot close !!!] } } /** * Check that the requested resource is a base media file? * * @param request * @return */ public static boolean isMediaRequest(HttpServletRequest request) { return isMediaRequest(request.getRequestURI()); } /** * Check that the requested resource is a base media file? * * @param path * @return */ public static boolean isMediaRequest(String path) { String ext = StringUtils2.getFilenameExtension(path); for (String media : MEDIA_BASE) { if (equalsIgnoreCase(ext, media)) { return true; } } return false; } /** * Is true
* * @param request * @param value * @param defaultValue * @return Return TRUE with true/t/y/yes/on/1/enabled */ public static boolean isTrue(ServletRequest request, String keyname, boolean defaultValue) { return isTrue(request.getParameter(keyname), defaultValue); } /** * Is true
* * @param value * @return Return TRUE with true/t/y/yes/on/1/enabled */ public static boolean isTrue(String value) { return isTrue(value, false); } /** * Is true
* * @param value * @param defaultValue * @return Return TRUE with true/t/y/yes/on/1/enabled */ public static boolean isTrue(String value, boolean defaultValue) { return StringUtils2.isTrue(value, defaultValue); } /** * To query URL parameters. * *
	 * toQueryParams("application=iam-example&redirect_url=http://my.com/index") == {application->iam-example, redirect_url=>http://my.com/index}
	 * toQueryParams("application=iam-example&redirect_url=http://my.com/index/#/me") == {application->iam-example, redirect_url=>http://my.com/index/#/me}
	 * 
* * @param urlQuery * @return */ public static Map toQueryParams(@Nullable String urlQuery) { Map parameters = new LinkedHashMap<>(4); if (isBlank(urlQuery)) return parameters; try { // Remove the character to the left of the '?' int separQuestIndex = urlQuery.lastIndexOf("?"); if (separQuestIndex > 0) { urlQuery = urlQuery.substring(separQuestIndex + 1); } String[] paramPairs = urlQuery.split("&"); for (int i = 0; i < paramPairs.length; i++) { String[] parts = trimToEmpty(paramPairs[i]).split("="); if (parts.length >= 2) { parameters.put(parts[0], parts[1]); } } return parameters; } catch (Exception e) { throw new IllegalArgumentException(format("Illegal parameter format. '%s'", urlQuery), e); } } /** * Map to query URL * * @param uri * @param queryParams * @return */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static String applyQueryURL(@Nullable String uri, @Nullable Map queryParams) { if (CollectionUtils2.isEmpty(queryParams) || isBlank(uri)) { return uri; } URI _uri = URI.create(uri); // Merge origin-older uri query parameters. Map mergeParams = new HashMap<>(toQueryParams(_uri.getQuery())); mergeParams.putAll(queryParams); // Gets base URI. StringBuffer url = new StringBuffer(uri); // Relative path? if (!isAnyBlank(_uri.getScheme(), _uri.getHost())) { url.setLength(0); // Reset url.append(getBaseURIForDefault(_uri.getScheme(), _uri.getHost(), _uri.getPort())); url.append(_uri.getPath()); } if (url.lastIndexOf("?") == -1) { url.append("?"); } // To URI parameters string for (Iterator it = mergeParams.keySet().iterator(); it.hasNext();) { Object key = it.next(); url.append(key); url.append("="); // Prevents any occurrence of a value string null Object value = mergeParams.get(key); if (value != null) { url.append(value); // "null" } if (it.hasNext()) { url.append("&"); } } return url.toString(); } /** * Reject http request methods. * * @param allowMode * @param request * @param response * @param methods * @throws UnsupportedOperationException */ public static void rejectRequestMethod(boolean allowMode, @NotNull ServletRequest request, @NotNull ServletResponse response, String... methods) throws UnsupportedOperationException { notNullOf(request, "request"); notNullOf(response, "response"); if (!isEmptyArray(methods)) { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; boolean rejected1 = true, rejected2 = false; for (String method : methods) { if (method.equalsIgnoreCase(req.getMethod())) { if (allowMode) { rejected1 = false; } else { rejected2 = true; } break; } } if ((allowMode && rejected1) || (!allowMode && rejected2)) { resp.setStatus(405); throw new UnsupportedOperationException(format("No support '%s' request method", req.getMethod())); } } } /** * Parse the given string with matrix variables. An example string would * look like this {@code "q1=a;q1=b;q2=a,b,c"}. The resulting map would * contain keys {@code "q1"} and {@code "q2"} with values {@code ["a","b"]} * and {@code ["a","b","c"]} respectively. * * @param matrixVariables * the unparsed matrix variables string * @return a map with matrix variable names and values (never {@code null}) */ public static MultiValueMap parseMatrixVariables(@Nullable String matrixVariables) { MultiValueMap result = new LinkedMultiValueMap<>(); if (!isBlank(matrixVariables)) { return result; } StringTokenizer pairs = new StringTokenizer(matrixVariables, ";"); while (pairs.hasMoreTokens()) { String pair = pairs.nextToken(); int index = pair.indexOf('='); if (index != -1) { String name = pair.substring(0, index); String rawValue = pair.substring(index + 1); for (String value : StringUtils2.commaDelimitedListToStringArray(rawValue)) { result.add(name, value); } } else { result.add(pair, ""); } } return result; } /** * Gets multi map first value. * * @param params * @return */ public static String getMultiMapFirstValue(@Nullable Map> params, String name) { if (isNull(params)) { return null; } return params.entrySet().stream().filter(e -> equalsIgnoreCase(e.getKey(), name)) .map(e -> CollectionUtils2.isEmpty(e.getValue()) ? e.getValue().get(0) : null).filter(e -> !isNull(e)).findFirst() .orElse(null); } /** * Gets request parameter value by name * * @param request * @param paramName * @param required * @return */ public static String getRequestParam(ServletRequest request, String paramName, boolean required) { String paramValue = request.getParameter(paramName); String cleanedValue = paramValue; if (paramValue != null) { cleanedValue = paramValue.trim(); if (cleanedValue.equals(EMPTY)) { cleanedValue = null; } } if (required) { hasTextOf(cleanedValue, paramName); } return cleanedValue; } /** * Extract request parameters with first value * * @param request * @return */ public static Map getFirstParameters(@Nullable ServletRequest request) { return nonNull(request) ? safeMap(request.getParameterMap()).entrySet().stream() .collect(toMap(e -> e.getKey(), e -> isEmptyArray(e.getValue()) ? null : e.getValue()[0])) : emptyMap(); } /** * Get full request query URL * * @param request * @return e.g:https://portal.mydomain.com/myapp/index?cid=xx&tid=xxx => * https://portal.mydomain.com/myapp/index?cid=xx&tid=xxx */ public static String getFullRequestURL(HttpServletRequest request) { return getFullRequestURL(request, true); } /** * Get full request query URL * * @param request * @param includeQuery * Does it contain query parameters? * @return e.g:https://portal.mydomain.com/myapp/index?cid=xx&tid=xxx => * https://portal.mydomain.com/myapp/index?cid=xx&tid=xxx */ public static String getFullRequestURL(HttpServletRequest request, boolean includeQuery) { String queryString = includeQuery ? request.getQueryString() : null; return request.getRequestURL().toString() + (StringUtils.isEmpty(queryString) ? "" : ("?" + queryString)); } /** * Get full request query URI * * @param request * @return e.g:https://portal.mydomain.com/myapp/index?cid=xx&tid=xxx => * /myapp/index?cid=xx&tid=xxx */ public static String getFullRequestURI(HttpServletRequest request) { String queryString = request.getQueryString(); return request.getRequestURI() + (StringUtils.isEmpty(queryString) ? "" : ("?" + queryString)); } /** * Has HTTP Request header * * @param request * @return */ public static boolean hasHeader(HttpServletRequest request, String name) { Enumeration names = request.getHeaderNames(); while (names.hasMoreElements()) { if (StringUtils.equalsIgnoreCase(names.nextElement(), name)) { return true; } } return false; } /** * Gets HTTP request headers. * * @param request * @return */ public static Map getRequestHeaders(@NotNull HttpServletRequest request) { return getRequestHeaders(request, null); } /** * Gets HTTP request headers. * * @param request * @param filter * @return */ @SuppressWarnings("unchecked") public static Map getRequestHeaders(@NotNull HttpServletRequest request, @Nullable Predicate filter) { notNullOf(request, "request"); filter = isNull(filter) ? defaultStringAnyFilter : filter; List headerNames = EnumerationUtils.toList(request.getHeaderNames()); return headerNames.stream().filter(filter).map(name -> singletonMap(name, request.getHeader((String) name))) .flatMap(e -> e.entrySet().stream()).collect(toMap(e -> e.getKey(), e -> e.getValue())); } /** * Is XHR Request * * @param request * @return */ public static boolean isXHRRequest(@NotNull HttpServletRequest request) { notNullOf(request, "request"); return isXHRRequest(new RequestExtractor() { @Override public String getHeader(String name) { return request.getHeader("X-Requested-With"); } }); } /** * Is XHR Request * * @param extractor * @return */ public static boolean isXHRRequest(@NotNull RequestExtractor extractor) { notNullOf(extractor, "extractor"); return equalsIgnoreCase(extractor.getHeader("X-Requested-With"), "XMLHttpRequest"); } /** * URL encode by UTF-8 * * @param url * plain URL * @return */ public static String safeEncodeURL(String url) { try { if (!contains(trimToEmpty(url).toLowerCase(US), URL_SEPAR_SLASH)) { return URLEncoder.encode(url, "UTF-8"); } } catch (UnsupportedEncodingException e) { throw new IllegalArgumentException(e); } return url; } /** * URL decode by UTF-8 * * @param url * encode URL * @return */ public static String safeDecodeURL(String url) { try { if (containsAny(trimToEmpty(url).toLowerCase(US), URL_SEPAR_SLASH, URL_SEPAR_QUEST, URL_SEPAR_COLON)) { return URLDecoder.decode(url, "UTF-8"); } } catch (UnsupportedEncodingException e) { throw new IllegalArgumentException(e); } return url; } /** * Extract top level domain string.
* *
	 *extDomainString("my.wl4g.com")                         =>  my.wl4g.com
	 *extDomainString("//my.wl4g.com/myapp1")                =>  my.wl4g.com
	 *extDomainString("/myapp1/api/v2/list")                 =>  ""
	 *extDomainString("http://my.wl4g.com.cn/myapp1")        =>  my.wl4g.com.cn
	 *extDomainString("https://my2.my1.wl4g.com:80/myapp1")  =>  my2.my1.wl4g.com
	 * 
* * @param hostOrUri * @return */ public static String extDomainString(String hostOrUri) { if (isBlank(hostOrUri)) { return hostOrUri; } hostOrUri = safeDecodeURL(hostOrUri); String domain = hostOrUri; // Is host? if (containsAny(hostOrUri, '/')) { // Is URI? domain = URI.create(hostOrUri).getHost(); } // Check domain available? // isTrueOf(isDomain(domain), format("hostOrUri: %s", hostOrUri)); if (!isDomain(domain) || isBlank(domain)) { return EMPTY; } return domain; } /** * Extract top level domain string.
* *
	 *extTopDomainString("my.wl4g.com")                         =>  wl4g.com
	 *extTopDomainString("//my.wl4g.com/myapp1")                =>  wl4g.com
	 *extTopDomainString("/myapp1/api/v2/list")                 =>  ""
	 *extTopDomainString("http://my.wl4g.com.cn/myapp1")        =>  wl4g.com.cn
	 *extTopDomainString("https://my2.my1.wl4g.com:80/myapp1")  =>  wl4g.com
	 * 
* * @param hostOrUri * @return */ public static String extTopDomainString(String hostOrUri) { String domain = extDomainString(hostOrUri); if (isBlank(domain)) { // Available? return EMPTY; } String[] parts = split(domain, "."); int endIndex = 2; if (domain.endsWith("com.cn")) { // Special parse endIndex = 3; } StringBuffer topDomain = new StringBuffer(); for (int i = 0; i < parts.length; i++) { if (i >= (parts.length - endIndex)) { topDomain.append(parts[i]); if (i < (parts.length - 1)) { topDomain.append("."); } } } return topDomain.toString(); } /** * Domain names equals two URIs are equal (including secondary and tertiary * domain names, etc. Exact matching) * * e.g.
* *
	 * isEqualWithDomain("http://my.wl4g.com/myapp1","http://my.wl4g.com/myapp2")=true
	 * isEqualWithDomain("http://my1.domin.com/myapp1","http://my.wl4g.com/myapp2")=false
	 * isEqualWithDomain("http://my.wl4g.com:80/myapp1","http://my.wl4g.com:8080/myapp2")=true
	 * isEqualWithDomain("https://my.wl4g.com:80/myapp1","http://my.wl4g.com:8080/myapp2")=true
	 * isEqualWithDomain("http://localhost","http://localhost:8080/myapp2")=true
	 * isEqualWithDomain("http://127.0.0.1","http://127.0.0.1:8080/myapp2")=true
	 * 
* * @param uria * @param urib * @return */ public static boolean isEqualWithDomain(String uria, String urib) { if (isNull(uria) || isNull(urib)) { return false; } return URI.create(safeDecodeURL(uria)).getHost().equals(URI.create(safeDecodeURL(urib)).getHost()); } /** * Check whether the wildcard domain uri belongs to the same origin.
* * e.g: * *
	 * {@link #isSameWildcardOrigin}("http://*.aa.domain.com/API/v2", "http://bb.aa.domain.com/API/v2", true) == true
	 * {@link #isSameWildcardOrigin}("http://*.aa.domain.com/API/v2", "https://bb.aa.domain.com/API/v2", true) == false
	 * {@link #isSameWildcardOrigin}("http://*.aa.domain.com/api/v2/", "http://bb.aa.domain.com/API/v2", true) == true
	 * {@link #isSameWildcardOrigin}("http://bb.*.domain.com", "https://bb.aa.domain.com", false) == true
	 * {@link #isSameWildcardOrigin}("http://*.aa.domain.com", "https://bb.aa.domain.com", true) == false
	 * {@link #isSameWildcardOrigin}("http://*.aa.domain.com:8080", "http://bb.aa.domain.com:8080/", true) == true
	 * {@link #isSameWildcardOrigin}("http://*.aa.domain.com:8080", "http://bb.aa.domain.com:8443/v2/xx", true) == true
	 * {@link #isSameWildcardOrigin}("http://*.aa.domain.com:*", "http://bb.aa.domain.com:8443/v2/xx", true) == true
	 * 
* * @param defWildcardUri * Definition wildcard URI * @param requestUri * @param checkScheme * @return */ public static boolean isSameWildcardOrigin(String defWildcardUri, String requestUri, boolean checkScheme) { if (isBlank(defWildcardUri) || isBlank(requestUri)) return false; if (defWildcardUri.equals(requestUri)) // URL equaled? return true; // Scheme matched? URI uri1 = URI.create(defWildcardUri); URI uri2 = URI.create(requestUri); final boolean schemeMatched = uri1.getScheme().equalsIgnoreCase(uri2.getScheme()); if (checkScheme && !schemeMatched) return false; // Hostname equaled? String hostname1 = extractWildcardEndpoint(defWildcardUri); String hostname2 = extractWildcardEndpoint(requestUri); if (equalsIgnoreCase(hostname1, hostname2)) return true; // Hostname wildcard matched? boolean wildcardHostnameMatched = false; String[] parts1 = split(hostname1, "."); String[] parts2 = split(hostname2, "."); for (int i = 0; i < parts1.length; i++) { if (equalsIgnoreCase(parts1[i], "*")) { if (i < (hostname1.length() - 1) && i < (hostname2.length() - 1)) { String compare1 = join(parts1, ".", i + 1, parts1.length); String compare2 = join(parts2, ".", i + 1, parts2.length); if (equalsIgnoreCase(compare1, compare2)) { wildcardHostnameMatched = true; break; } } } } // Check scheme matched. if (checkScheme && wildcardHostnameMatched) { return schemeMatched; } return wildcardHostnameMatched; } /** * Extract domain text from {@link URI}.
* Uri resolution cannot be used here because it may fail when there are * wildcards, e.g, * {@link URI#create}("http://*.aa.domain.com/api/v2/).gethost() is * null.
* *
	 * {@link #extractWildcardHostName}("http://*.domain.com/v2/xx") == *.domain.com
	 * {@link #extractWildcardHostName}("http://*.aa.domain.com:*") == *.aa.domain.com
	 * {@link #extractWildcardHostName}("http://*.bb.domain.com:8080/v2/xx") == *.bb.domain.com
	 * 
* * @param wildcardUri * @return */ public static String extractWildcardEndpoint(String wildcardUri) { if (isEmpty(wildcardUri)) return EMPTY; wildcardUri = trimToEmpty(safeEncodeURL(wildcardUri)).toLowerCase(US); String noPrefix = wildcardUri.substring(wildcardUri.indexOf(URL_SEPAR_PROTO) + URL_SEPAR_PROTO.length()); int slashIndex = noPrefix.indexOf(URL_SEPAR_SLASH); String serverName = noPrefix; if (slashIndex > 0) { serverName = noPrefix.substring(0, slashIndex); } // Check domain illegal? // e.g, http://*.domain.com:8080[allow] // http://*.domain.com:*[allow] // http://*.aa.*.domain.com[noallow] String hostname = serverName; if (serverName.contains(URL_SEPAR_COLON)) { hostname = serverName.substring(0, serverName.indexOf(URL_SEPAR_COLON)); } Assert2.isTrue(hostname.indexOf("*") == hostname.lastIndexOf("*"), "Illegal serverName: %s, contains multiple wildcards!", serverName); return safeDecodeURL(hostname); } /** * Determine whether the requested URL belongs to the domain. e.g:
* withInDomain("my.domain.com","http://my.domain.com/myapp") = true
* withInDomain("my.domain.com","https://my.domain.com/myapp") = true
* withInDomain("my.domain.com","https://my1.domain.com/myapp") = false *
* withInDomain("*.domain.com", "https://other1.domain.com/myapp") = true *
* * @param domain * @param url * @return */ public static boolean withInDomain(String domain, String url) { notNull(domain, "'domain' must not be null"); notNull(url, "'requestUrl' must not be null"); try { String hostname = new URI(safeDecodeURL(cleanURI(url))).getHost(); if (!domain.contains("*")) { Assert2.isTrue(isDomain(domain), String.format("Illegal domain[%s] name format", domain)); return equalsIgnoreCase(domain, hostname); } if (domain.startsWith("*")) { return equalsIgnoreCase(domain.substring(1), hostname.substring(hostname.indexOf("."))); } return false; } catch (URISyntaxException e) { throw new IllegalArgumentException(e); } } /** * Determine whether the requested URL belongs to the base URI. e.g:
* withInURL("https://domain/myapp/login","https://domain/myapp/login?t=123") * == true
* withInURL("https://domain/myapp","https://domain/myapp/login?t=123") == * true
* withInURL("https://domain/myapp/login?r=abc","https://domain/myapp/login?t=123") * == true
* withInURL("https://domain/myapp/login?r=abc","http://domain/myapp/login?t=123") * == false
*
* * @param baseUrl * @param url * @return */ public static boolean withInURL(String baseUrl, String url) { if (baseUrl == null || url == null) { return false; } try { // If it's a URL in decoding format URI baseUrl0 = new URI(safeDecodeURL(cleanURI(baseUrl))); URI uri0 = new URI(safeDecodeURL(cleanURI(url))); return (StringUtils.startsWithIgnoreCase(uri0.getRawPath(), baseUrl0.getRawPath()) && StringUtils.equalsIgnoreCase(uri0.getScheme(), baseUrl0.getScheme()) && StringUtils.equalsIgnoreCase(uri0.getHost(), baseUrl0.getHost()) && uri0.getPort() == baseUrl0.getPort()); } catch (Exception e) { // Ignore } return false; } /** * Get HTTP request RFC standard based URI * * @param request * @param hasCtxPath * @return */ public static String getRFCBaseURI(HttpServletRequest request, boolean hasCtxPath) { // Context path String ctxPath = request.getContextPath(); notNull(ctxPath, "Http request contextPath must not be null"); ctxPath = !hasCtxPath ? "" : ctxPath; // Scheme String scheme = request.getScheme(); for (String schemeKey : HEADER_REAL_PROTOCOL) { String scheme0 = request.getHeader(schemeKey); if (!isBlank(scheme0)) { scheme = scheme0; break; } } // Host & Port String serverName = request.getServerName(); int port = request.getServerPort(); for (String hostKey : HEADER_REAL_HOST) { String host = request.getHeader(hostKey); if (!isBlank(host)) { // me.domain.com:8080 serverName = host; if (host.contains(":")) { String[] part = split(host, ":"); serverName = part[0]; if (!isBlank(part[1])) { port = Integer.parseInt(part[1]); } } else if (equalsIgnoreCase(scheme, "HTTP")) { port = 80; } else if (equalsIgnoreCase(scheme, "HTTPS")) { port = 443; } break; } } return getBaseURIForDefault(scheme, serverName, port) + ctxPath; } /** * Obtain base URI for default.
* *
	 * getBaseURIForDefault("http", "my.com", 8080) == "http://my.com:8080"
	 * getBaseURIForDefault("http", "my.com", 80) == "http://my.com"
	 * getBaseURIForDefault("https", "my.com", 443) == "https://my.com"
	 * getBaseURIForDefault("https", "my.com", -1) == "https://my.com"
	 * 
* * @param scheme * @param serverName * @param port * @return */ public static String getBaseURIForDefault(String scheme, String serverName, int port) { notNull(scheme, "Http request scheme must not be empty"); notNull(serverName, "Http request serverName must not be empty"); StringBuffer baseUri = new StringBuffer(scheme).append("://").append(serverName); if (port > 0) { Assert2.isTrue((port > 0 && port < 65536), "Http server port must be greater than 0 and less than 65536"); if (!((equalsIgnoreCase(scheme, "HTTP") && port == 80) || (equalsIgnoreCase(scheme, "HTTPS") && port == 443))) { baseUri.append(":").append(port); } } return baseUri.toString(); } /** * Clean request URI.
* *
	 * cleanURI("https://my.domain.com//myapp///index?t=123") => "https://my.domain.com/myapp/index?t=123"
	 * 
* * @param uri * @return */ @Beta public static String cleanURI(String uri) { if (isBlank(uri)) { return uri; } // Check syntax uri = URI.create(uri).toString(); /** * Cleaning.
* Note: that you cannot change the original URI case. */ try { String encodeUrl = safeEncodeURL(uri); String pathUrl = encodeUrl, schema = EMPTY; if (encodeUrl.toLowerCase(US).contains(URL_SEPAR_PROTO)) { // Start from "://" int startIndex = encodeUrl.toLowerCase(US).indexOf(URL_SEPAR_PROTO); schema = encodeUrl.substring(0, startIndex) + URL_SEPAR_PROTO; pathUrl = encodeUrl.substring(startIndex + URL_SEPAR_PROTO.length()); } // Cleanup for: '/shopping/order//list' => '/shopping/order/list' String lastCleanUrl = pathUrl; for (int i = 0; i < 256; i++) { // https://www.ietf.org/rfc/rfc2616.txt#3.2.1 String cleanUrl = replaceIgnoreCase(lastCleanUrl, (URL_SEPAR_SLASH2).toUpperCase(), URL_SEPAR_SLASH); if (StringUtils.equals(cleanUrl, lastCleanUrl)) { break; } else { lastCleanUrl = cleanUrl; } } return safeDecodeURL(schema + lastCleanUrl); } catch (Exception e) { throw new IllegalArgumentException(e); } } /** * Obtain the available request remember URL, for example: used to log in * successfully and redirect to the last remembered URL * * @param request * @return */ public static String getAvaliableRequestRememberUrl(HttpServletRequest request) { String rememberUrl = request.getHeader("Referer"); // #[RFC7231], https://tools.ietf.org/html/rfc7231#section-5.5.2 rememberUrl = isNotBlank(rememberUrl) ? rememberUrl : request.getHeader("Referrer"); // Fallback if (isBlank(rememberUrl) && request.getMethod().equalsIgnoreCase("GET")) { rememberUrl = getFullRequestURL(request, true); } return rememberUrl; } /** * Check whether the URI is relative to the path. * * e.g.
* *
	 * isRelativeUri("//my.wl4g.com/myapp1") = false 
* isRelativeUri("http://my.wl4g.com/myapp1") = false
* isRelativeUri("https://my.wl4g.com:80/myapp1") = false
* isRelativeUri("/myapp1/api/v2/list") = true
*
* * @param uri * @return */ public static boolean isRelativeUri(String uri) { if (isBlank(uri)) return false; return isBlank(URI.create(safeDecodeURL(uri)).getScheme()) && !uri.startsWith("//"); } /** * Check to see if the printing servlet is enabled to request the wrong * stack information. * * @param request * @return */ @Beta public static boolean isStacktraceRequest(@Nullable ServletRequest request) { if (isNull(request)) { return false; } if (JvmRuntimeKit.isJVMDebugging) { return true; } String stacktrace = request.getParameter(PARAM_STACKTRACE); if (request instanceof HttpServletRequest) { if (isBlank(stacktrace)) { stacktrace = ((HttpServletRequest) request).getHeader(PARAM_STACKTRACE); } if (isBlank(stacktrace)) { stacktrace = CookieUtils.getCookie((HttpServletRequest) request, PARAM_STACKTRACE); } } if (isBlank(stacktrace)) { return false; } return isTrue(stacktrace.toLowerCase(US), false); } /** * Generic dynamic web message response type processing enumeration. * * @author Wangl.sir <[email protected]> * @version v1.0 * @date 2019年1月4日 * @since */ public static enum ResponseType { AUTO, WEBURI, JSON; /** * Default get response type parameter name. */ public static final String DEFAULT_RESPTYPE_NAME = "response_type"; /** * Get the name of the corresponding data type parameter. Note that * NGINX defaults to replace the underlined header, such as: * *
		 * header(response_type: json) => header(responsetype: json)
		 * 
* * and how to disable this feature of NGINX: * *
		 * http {
		 * 	underscores_in_headers on;
		 * }
		 * 
*/ public static final String[] RESPTYPE_NAMES = { DEFAULT_RESPTYPE_NAME, "responsetype", "Response-Type" }; /** * Safe converter string to {@link ResponseType} * * @param respType * @return */ public static final ResponseType safeOf(String respType) { for (ResponseType t : values()) { if (String.valueOf(respType).equalsIgnoreCase(t.name())) { return t; } } return null; } /** * Check whether the response is in JSON format * * @param respTypeValue * @param request * @return */ public static boolean isRespJSON(@NotBlank final String respTypeValue, @NotNull final HttpServletRequest request) { return determineResponseWithJson(safeOf(respTypeValue), new RequestExtractor() { @Override public String getQueryParam(String name) { return request.getParameter(name); } @Override public String getHeader(String name) { return request.getHeader(name); } }); } /** * Check whether the response is in JSON format * * @param request * @return */ public static boolean isRespJSON(@NotNull final HttpServletRequest request) { return isRespJSON(new RequestExtractor() { @Override public String getQueryParam(String name) { return request.getParameter(name); } @Override public String getHeader(String name) { return request.getHeader(name); } }, null); } /** * Check whether the response is in JSON format. * * @param extractor * request wrapper * @param respTypeName * response type paremter name. * @return */ public static boolean isRespJSON(@NotNull RequestExtractor extractor, @Nullable String respTypeName) { notNullOf(extractor, "request"); List respTypeNames = asList(RESPTYPE_NAMES); if (!isBlank(respTypeName)) { respTypeNames.add(respTypeName); } for (String name : respTypeNames) { String respTypeValue = extractor.getQueryParam(name); respTypeValue = isBlank(respTypeValue) ? extractor.getHeader(name) : respTypeValue; if (!isBlank(respTypeValue)) { return determineResponseWithJson(safeOf(respTypeValue), extractor); } } // Using default auto mode return determineResponseWithJson(ResponseType.AUTO, extractor); } /** * Determine response JSON message * * @param respType * @param extractor * @return */ private static boolean determineResponseWithJson(ResponseType respType, @NotNull RequestExtractor extractor) { notNullOf(extractor, "request"); // Using default strategy if (Objects.isNull(respType)) { respType = ResponseType.AUTO; } // Has header(accept:application/json) boolean hasAccpetJson = false; for (String typePart : String.valueOf(extractor.getHeader("Accept")).split(",")) { if (startsWithIgnoreCase(typePart, "application/json")) { hasAccpetJson = true; break; } } // Has header(origin:xx.domain.com) boolean hasOrigin = !isBlank(extractor.getHeader("Origin")); // Is header[XHR] ? boolean isXhr = isXHRRequest(extractor); switch (respType) { // Matching case JSON: return true; case WEBURI: return false; case AUTO: /* * When it's a browser request and not an XHR and token request * (no X-Requested-With: XMLHttpRequest and token at the head of * the line), it responds to the rendering page, otherwise it * responds to JSON. */ return isBrowser(extractor.getHeader("User-Agent")) ? (isXhr || hasAccpetJson || hasOrigin) : true; default: throw new IllegalStateException(format("Illegal response type %s", respType)); } } } /** * Request extractor wrapper, It is mainly to solve the request types of * different models or protocols, such as: Xxx{@link ServletRequest} or * {@link HttpServletRequest} or {@link ServerRequest}(reactive) etc * * @author Wangl.sir * @version v1.0 2020-09-15 * @since */ public static interface RequestExtractor { /** * Gets query parameter by name. * * @param name * @return */ default String getQueryParam(String name) { throw new UnsupportedOperationException(); } /** * Gets headers parameter by name. * * @param name * @return */ default String getHeader(String name) { throw new UnsupportedOperationException(); } } /** * URL scheme(HTTPS) */ public static final String URL_SCHEME_HTTPS = "https"; /** * URL scheme(HTTP) */ public static final String URL_SCHEME_HTTP = "http"; /** * URL separator(/) */ public static final String URL_SEPAR_SLASH = "%2f"; /** * URL double separator(//) */ public static final String URL_SEPAR_SLASH2 = URL_SEPAR_SLASH + URL_SEPAR_SLASH; /** * URL separator(?) */ public static final String URL_SEPAR_QUEST = "%3f"; /** * URL colon separator(:) */ public static final String URL_SEPAR_COLON = "%3a"; /** * Protocol separators, such as * https://my.domain.com=>https%3A%2F%2Fmy.domain.com */ public static final String URL_SEPAR_PROTO = URL_SEPAR_COLON + URL_SEPAR_SLASH + URL_SEPAR_SLASH; /** * Request the header key name of real client IP.
* *
	 *	一、没有使用代理服务器的情况:
	 *	      REMOTE_ADDR = 您的 IP
	 *	      HTTP_VIA = 没数值或不显示
	 *	      HTTP_X_FORWARDED_FOR = 没数值或不显示
	 *	二、使用透明代理服务器的情况:Transparent Proxies
	 *	      REMOTE_ADDR = 最后一个代理服务器 IP 
	 *	      HTTP_VIA = 代理服务器 IP
	 *	      HTTP_X_FORWARDED_FOR = 您的真实 IP ,经过多个代理服务器时,这个值类似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。
	 *	   这类代理服务器还是将您的信息转发给您的访问对象,无法达到隐藏真实身份的目的。
	 *	三、使用普通匿名代理服务器的情况:Anonymous Proxies
	 *	      REMOTE_ADDR = 最后一个代理服务器 IP 
	 *	      HTTP_VIA = 代理服务器 IP
	 *	      HTTP_X_FORWARDED_FOR = 代理服务器 IP ,经过多个代理服务器时,这个值类似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。
	 *	   隐藏了您的真实IP,但是向访问对象透露了您是使用代理服务器访问他们的。
	 *	四、使用欺骗性代理服务器的情况:Distorting Proxies
	 *	      REMOTE_ADDR = 代理服务器 IP 
	 *	      HTTP_VIA = 代理服务器 IP 
	 *	      HTTP_X_FORWARDED_FOR = 随机的 IP ,经过多个代理服务器时,这个值类似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。
	 *	   告诉了访问对象您使用了代理服务器,但编造了一个虚假的随机IP代替您的真实IP欺骗它。
	 *	五、使用高匿名代理服务器的情况:High Anonymity Proxies (Elite proxies)
	 *	      REMOTE_ADDR = 代理服务器 IP
	 *	      HTTP_VIA = 没数值或不显示
	 *	      HTTP_X_FORWARDED_FOR = 没数值或不显示 ,经过多个代理服务器时,这个值类似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。
	 * 
*/ public static final String[] HEADER_REAL_IP = { "X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP", "X-Real-IP", "REMOTE_ADDR", "Remote-Addr", "RemoteAddr", // RemoteAddr "REMOTE_IP", "Remote-Ip", "RemoteIp", // RemoteIp: Aliyun-SLB "HTTP_X_FORWARDED_FOR", "Http-X-Forwarded-For", "HttpXForwardedFor", // HttpXForwardedFor "HTTP_X_FORWARDED", "Http-X-Forwarded", "HttpXForwarded", // HttpXForwarded "HTTP_Client_IP", "Http-Client-Ip", "HttpClientIp", // HttpClientIp "HTTP_X_CLUSTER_CLIENT_IP", "Http-X-Cluster-Client-Ip", "HttpXClusterClientIp", // HttpXClusterClientIp "HTTP_FORWARDED_FOR", "Http-Forwarded-For", "HttpForwardedFor", // HttpForwardedFor "HTTP_VIA ", "Http-Via", "HttpVia" }; // HttpVia /** * Request the header key name of real protocol scheme. */ public static final String[] HEADER_REAL_PROTOCOL = { "X-Forwarded-Proto" }; /** * Request the header key name of real host */ public static final String[] HEADER_REAL_HOST = { "Host" }; /** * Common media file suffix definitions */ public static final String[] MEDIA_BASE = new String[] { "ico", "icon", "css", "js", "html", "shtml", "htm", "jsp", "jspx", "jsf", "aspx", "asp", "php", "jpeg", "jpg", "png", "bmp", "gif", "tif", "pic", "swf", "svg", "ttf", "eot", "eot@", "woff", "woff2", "wd3", "txt", "doc", "docx", "wps", "ppt", "pptx", "pdf", "excel", "xls", "xlsx", "avi", "wav", "mp3", "amr", "mp4", "aiff", "rar", "tar.gz", "tar", "zip", "gzip", "ipa", "plist", "apk", "7-zip" }; /** * Controlling enabled unified exception handling stacktrace information. */ public static final String PARAM_STACKTRACE = getenv().getOrDefault("XCLOUD_COMPONENT_REQUEST_STACKTRACE_PARAM", "x-stacktrace"); private static final Predicate defaultStringAnyFilter = name -> true; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy