blob: f41d6490a51fb0f7fde4c74647d13eb1de529225 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.servicecomb.provider.springmvc.reference;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.servicecomb.common.rest.RestConst;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
/**
* 默认不支持下面第1个场景,需要做出修正
* cse://app:ms/path to cse://app/ms/path
* cse://ms/path to cse://ms/path
*/
@SuppressWarnings("deprecation")
// TODO : upgrade to spring 5 will having warning's , we'll fix it later
public class CseUriTemplateHandler extends org.springframework.web.util.DefaultUriTemplateHandler {
private static final String SCHEME_PATTERN = "([^:/?#]+):";
private static final String USERINFO_PATTERN = "([^@\\[/?#]*)";
private static final String HOST_IPV4_PATTERN = "[^\\[/?#:]*";
private static final String HOST_IPV6_PATTERN = "\\[[\\p{XDigit}:.]*[%\\p{Alnum}]*]";
private static final String HOST_PATTERN = "(" + HOST_IPV6_PATTERN + "|" + HOST_IPV4_PATTERN + ")";
private static final String PORT_PATTERN = "(\\d*(?:\\{[^/]+?})?)";
private static final String PATH_PATTERN = "([^?#]*)";
private static final String QUERY_PATTERN = "([^#]*)";
private static final String LAST_PATTERN = "(.*)";
// Regex patterns that matches URIs. See RFC 3986, appendix B
private static final Pattern URI_PATTERN = Pattern.compile(
"^(" + SCHEME_PATTERN + ")?" + "(//(" + USERINFO_PATTERN + "@)?" + HOST_PATTERN + "(:" + PORT_PATTERN +
")?" + ")?" + PATH_PATTERN + "(\\?" + QUERY_PATTERN + ")?" + "(#" + LAST_PATTERN + ")?");
public CseUriTemplateHandler() {
setStrictEncoding(true);
}
@Override
protected URI expandInternal(String uriTemplate, Map<String, ?> uriVariables) {
UriComponentsBuilder uriComponentsBuilder = initUriComponentsBuilder(uriTemplate);
UriComponents uriComponents = expandAndEncode(uriComponentsBuilder, uriVariables);
return createUri(uriTemplate, uriComponents);
}
@Override
protected URI expandInternal(String uriTemplate, Object... uriVariables) {
UriComponentsBuilder uriComponentsBuilder = initUriComponentsBuilder(uriTemplate);
UriComponents uriComponents = expandAndEncode(uriComponentsBuilder, uriVariables);
return createUri(uriTemplate, uriComponents);
}
@Override
protected UriComponentsBuilder initUriComponentsBuilder(String uriTemplate) {
UriComponentsBuilder builder = fromUriString(uriTemplate);
if (shouldParsePath() && !isStrictEncoding()) {
List<String> pathSegments = builder.build().getPathSegments();
builder.replacePath(null);
for (String pathSegment : pathSegments) {
builder.pathSegment(pathSegment);
}
}
return builder;
}
private static UriComponentsBuilder fromUriString(String uri) {
Assert.notNull(uri, "URI must not be null");
Matcher matcher = URI_PATTERN.matcher(uri);
if (matcher.matches()) {
UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
String scheme = matcher.group(2);
String userInfo = matcher.group(5);
String host = matcher.group(6);
String port = matcher.group(8);
String path = matcher.group(9);
String query = matcher.group(11);
String fragment = matcher.group(13);
boolean opaque = false;
if (StringUtils.hasLength(scheme)) {
String rest = uri.substring(scheme.length());
if (!rest.startsWith(":/")) {
opaque = true;
}
}
builder.scheme(scheme);
if (opaque) {
String ssp = uri.substring(scheme.length()).substring(1);
if (StringUtils.hasLength(fragment)) {
ssp = ssp.substring(0, ssp.length() - (fragment.length() + 1));
}
builder.schemeSpecificPart(ssp);
} else {
builder.userInfo(userInfo);
builder.host(host);
if (StringUtils.hasLength(port)) {
builder.port(port);
}
builder.path(path);
builder.query(query);
}
if (StringUtils.hasText(fragment)) {
builder.fragment(fragment);
}
return builder;
} else {
throw new IllegalArgumentException("[" + uri + "] is not a valid URI");
}
}
private URI createUri(String uriTemplate, UriComponents uriComponents) {
String strUri = uriComponents.toUriString();
Matcher matcher = URI_PATTERN.matcher(uriTemplate);
matcher.matches(); // should always be true
String scheme = matcher.group(2);
String host = matcher.group(6);
if (isCrossApp(uriTemplate, scheme, host)) {
int idx;
if (RestConst.SCHEME.equals(scheme)) {
idx = strUri.indexOf('/', RestConst.URI_PREFIX.length());
} else {
idx = strUri.indexOf('/', RestConst.URI_PREFIX_NEW.length());
}
strUri = strUri.substring(0, idx) + ":" + strUri.substring(idx + 1);
}
try {
// Avoid further encoding (in the case of strictEncoding=true)
return new URI(strUri);
} catch (URISyntaxException ex) {
throw new IllegalStateException("Could not create URI object: " + ex.getMessage(), ex);
}
}
private boolean isCrossApp(String uriTemplate, String scheme, String host) {
int pos;
if (RestConst.SCHEME.equals(scheme)) {
pos = RestConst.URI_PREFIX.length() + host.length();
} else {
pos = RestConst.URI_PREFIX_NEW.length() + host.length();
}
return uriTemplate.charAt(pos) == ':';
}
}