1
0
mirror of https://gitee.com/mirrors/Spring-Cloud-Alibaba.git synced 2021-06-26 13:25:11 +08:00

polish #761 update pkg name & maven coordinate for Greenwich

This commit is contained in:
fangjian0423
2019-07-23 11:03:04 +08:00
parent 3998ea23f7
commit 883c66f251
599 changed files with 4993 additions and 4526 deletions

View File

@@ -0,0 +1,55 @@
/*
* 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 com.alibaba.cloud.dubbo.http;
import org.apache.dubbo.common.io.UnsafeByteArrayInputStream;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import java.io.IOException;
import java.io.InputStream;
/**
* Byte array {@link HttpInputMessage} implementation
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
class ByteArrayHttpInputMessage implements HttpInputMessage {
private final HttpHeaders httpHeaders;
private final InputStream inputStream;
public ByteArrayHttpInputMessage(byte[] body) {
this(new HttpHeaders(), body);
}
public ByteArrayHttpInputMessage(HttpHeaders httpHeaders, byte[] body) {
this.httpHeaders = httpHeaders;
this.inputStream = new UnsafeByteArrayInputStream(body);
}
@Override
public InputStream getBody() throws IOException {
return inputStream;
}
@Override
public HttpHeaders getHeaders() {
return httpHeaders;
}
}

View File

@@ -0,0 +1,131 @@
/*
* 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 com.alibaba.cloud.dubbo.http;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import java.util.List;
import java.util.Map;
import static org.springframework.web.util.UriComponentsBuilder.fromPath;
/**
* Default {@link HttpRequest} implementation
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DefaultHttpRequest implements HttpRequest {
private final String method;
private final URI uri;
private final HttpHeaders headers = new HttpHeaders();
public DefaultHttpRequest(String method, String path, Map<String, List<String>> params,
Map<String, List<String>> headers) {
this.method = method == null ? HttpMethod.GET.name() : method.toUpperCase();
this.uri = buildURI(path, params);
this.headers.putAll(headers);
}
private URI buildURI(String path, Map<String, List<String>> params) {
UriComponentsBuilder builder = fromPath(path)
.queryParams(new LinkedMultiValueMap<>(params));
return builder.build().toUri();
}
@Override
public HttpMethod getMethod() {
return HttpMethod.resolve(getMethodValue());
}
@Override
public String getMethodValue() {
return method;
}
@Override
public URI getURI() {
return uri;
}
@Override
public HttpHeaders getHeaders() {
return headers;
}
public static Builder builder() {
return new Builder();
}
/**
* {@link HttpRequest} Builder
*/
public static class Builder {
String method;
String path;
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
public Builder method(String method) {
this.method = method;
return this;
}
public Builder path(String path) {
this.path = path;
return this;
}
public Builder param(String name, String value) {
this.params.add(name, value);
return this;
}
public Builder header(String name, String value) {
this.headers.add(name, value);
return this;
}
public Builder params(Map<String, List<String>> params) {
this.params.putAll(params);
return this;
}
public Builder headers(Map<String, List<String>> headers) {
this.headers.putAll(headers);
return this;
}
public HttpRequest build() {
return new DefaultHttpRequest(method, path, params, headers);
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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 com.alibaba.cloud.dubbo.http;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpRequest;
import org.springframework.util.MultiValueMap;
/**
* HTTP Server Request
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public interface HttpServerRequest extends HttpRequest, HttpInputMessage {
/**
* Return a path of current HTTP request
*
* @return
*/
String getPath();
/**
* Return a map with parsed and decoded query parameter values.
*/
MultiValueMap<String, String> getQueryParams();
}

View File

@@ -0,0 +1,100 @@
/*
* 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 com.alibaba.cloud.dubbo.http;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import org.springframework.util.MultiValueMap;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Map;
import static com.alibaba.cloud.dubbo.http.util.HttpUtils.getParameters;
/**
* Mutable {@link HttpServerRequest} implementation
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class MutableHttpServerRequest implements HttpServerRequest {
private final HttpMethod httpMethod;
private final URI uri;
private final String path;
private final MultiValueMap<String, String> queryParams;
private final HttpHeaders httpHeaders;
private final HttpInputMessage httpInputMessage;
public MutableHttpServerRequest(HttpRequest httpRequest, byte[] body) {
this.httpMethod = httpRequest.getMethod();
this.uri = httpRequest.getURI();
this.path = uri.getPath();
this.httpHeaders = httpRequest.getHeaders();
this.queryParams = getParameters(httpRequest);
this.httpInputMessage = new ByteArrayHttpInputMessage(body);
}
public MutableHttpServerRequest params(Map<String, String> params) {
queryParams.setAll(params);
return this;
}
@Override
public InputStream getBody() throws IOException {
return httpInputMessage.getBody();
}
@Override
public HttpMethod getMethod() {
return httpMethod;
}
// Override method since Spring Framework 5.0
@Override
public String getMethodValue() {
return httpMethod.name();
}
@Override
public URI getURI() {
return uri;
}
@Override
public HttpHeaders getHeaders() {
return httpHeaders;
}
@Override
public String getPath() {
return path;
}
@Override
public MultiValueMap<String, String> getQueryParams() {
return queryParams;
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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 com.alibaba.cloud.dubbo.http.converter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
/**
* {@link HttpMessageConverter} Holder with {@link MediaType}.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class HttpMessageConverterHolder {
private final MediaType mediaType;
private final HttpMessageConverter<?> converter;
public HttpMessageConverterHolder(MediaType mediaType, HttpMessageConverter<?> converter) {
this.mediaType = mediaType;
this.converter = converter;
}
public MediaType getMediaType() {
return mediaType;
}
public HttpMessageConverter<?> getConverter() {
return converter;
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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 com.alibaba.cloud.dubbo.http.matcher;
import java.util.Collection;
import java.util.Iterator;
/**
* Abstract {@link HttpRequestMatcher} implementation
*
* @author Rossen Stoyanchev
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public abstract class AbstractHttpRequestMatcher implements HttpRequestMatcher {
/**
* Return the discrete items a request condition is composed of.
* <p>
* For example URL patterns, HTTP request methods, param expressions, etc.
*
* @return a collection of objects, never {@code null}
*/
protected abstract Collection<?> getContent();
/**
* The notation to use when printing discrete items of content.
* <p>
* For example {@code " || "} for URL patterns or {@code " && "} for param
* expressions.
*/
protected abstract String getToStringInfix();
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null || getClass() != other.getClass()) {
return false;
}
return getContent().equals(((AbstractHttpRequestMatcher) other).getContent());
}
@Override
public int hashCode() {
return getContent().hashCode();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("[");
for (Iterator<?> iterator = getContent().iterator(); iterator.hasNext();) {
Object expression = iterator.next();
builder.append(expression.toString());
if (iterator.hasNext()) {
builder.append(getToStringInfix());
}
}
builder.append("]");
return builder.toString();
}
}

View File

@@ -0,0 +1,91 @@
/*
* 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 com.alibaba.cloud.dubbo.http.matcher;
import org.springframework.http.MediaType;
/**
* The some source code is scratched from org.springframework.web.servlet.mvc.condition.AbstractMediaTypeExpression
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class AbstractMediaTypeExpression implements MediaTypeExpression, Comparable<AbstractMediaTypeExpression> {
private final MediaType mediaType;
private final boolean negated;
AbstractMediaTypeExpression(String expression) {
if (expression.startsWith("!")) {
this.negated = true;
expression = expression.substring(1);
} else {
this.negated = false;
}
this.mediaType = MediaType.parseMediaType(expression);
}
AbstractMediaTypeExpression(MediaType mediaType, boolean negated) {
this.mediaType = mediaType;
this.negated = negated;
}
@Override
public MediaType getMediaType() {
return this.mediaType;
}
@Override
public boolean isNegated() {
return this.negated;
}
@Override
public int compareTo(AbstractMediaTypeExpression other) {
return MediaType.SPECIFICITY_COMPARATOR.compare(this.getMediaType(), other.getMediaType());
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null || getClass() != other.getClass()) {
return false;
}
AbstractMediaTypeExpression otherExpr = (AbstractMediaTypeExpression) other;
return (this.mediaType.equals(otherExpr.mediaType) && this.negated == otherExpr.negated);
}
@Override
public int hashCode() {
return this.mediaType.hashCode();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (this.negated) {
builder.append('!');
}
builder.append(this.mediaType.toString());
return builder.toString();
}
}

View File

@@ -0,0 +1,146 @@
/*
* 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 com.alibaba.cloud.dubbo.http.matcher;
import org.springframework.http.HttpRequest;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import static org.springframework.util.StringUtils.trimWhitespace;
/**
* The some source code is scratched from org.springframework.web.servlet.mvc.condition.AbstractNameValueExpression
*
* @author Rossen Stoyanchev
* @author Arjen Poutsma
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
abstract class AbstractNameValueExpression<T> implements NameValueExpression<T> {
protected final String name;
protected final T value;
protected final boolean negated;
AbstractNameValueExpression(String expression) {
int separator = expression.indexOf('=');
if (separator == -1) {
this.negated = expression.startsWith("!");
this.name = trimWhitespace((this.negated ? expression.substring(1) : expression));
this.value = null;
} else {
this.negated = (separator > 0) && (expression.charAt(separator - 1) == '!');
this.name = trimWhitespace((this.negated ? expression.substring(0, separator - 1)
: expression.substring(0, separator)));
String valueExpression = getValueExpression(expression, separator);
this.value = isExcludedValue(valueExpression) ? null : parseValue(valueExpression);
}
}
private String getValueExpression(String expression, int separator) {
return trimWhitespace(expression.substring(separator + 1));
}
/**
* Exclude the pattern value Expression: "{value}", subclass could override this method.
*
* @param valueExpression
* @return
*/
protected boolean isExcludedValue(String valueExpression) {
return StringUtils.hasText(valueExpression) &&
valueExpression.startsWith("{")
&& valueExpression.endsWith("}");
}
@Override
public String getName() {
return this.name;
}
@Override
public T getValue() {
return this.value;
}
@Override
public boolean isNegated() {
return this.negated;
}
public final boolean match(HttpRequest request) {
boolean isMatch;
if (this.value != null) {
isMatch = matchValue(request);
} else {
isMatch = matchName(request);
}
return (this.negated ? !isMatch : isMatch);
}
protected abstract boolean isCaseSensitiveName();
protected abstract T parseValue(String valueExpression);
protected abstract boolean matchName(HttpRequest request);
protected abstract boolean matchValue(HttpRequest request);
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null || getClass() != other.getClass()) {
return false;
}
AbstractNameValueExpression<?> that = (AbstractNameValueExpression<?>) other;
return ((isCaseSensitiveName() ? this.name.equals(that.name) : this.name.equalsIgnoreCase(that.name)) &&
ObjectUtils.nullSafeEquals(this.value, that.value) && this.negated == that.negated);
}
@Override
public int hashCode() {
int result = (isCaseSensitiveName() ? this.name.hashCode() : this.name.toLowerCase().hashCode());
result = 31 * result + (this.value != null ? this.value.hashCode() : 0);
result = 31 * result + (this.negated ? 1 : 0);
return result;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if (this.value != null) {
builder.append(this.name);
if (this.negated) {
builder.append('!');
}
builder.append('=');
builder.append(this.value);
} else {
if (this.negated) {
builder.append('!');
}
builder.append(this.name);
}
return builder.toString();
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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 com.alibaba.cloud.dubbo.http.matcher;
import org.springframework.http.HttpRequest;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
/**
* Composite {@link HttpRequestMatcher} implementation
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public abstract class CompositeHttpRequestMatcher extends AbstractHttpRequestMatcher {
private final List<HttpRequestMatcher> matchers = new LinkedList<>();
public CompositeHttpRequestMatcher(HttpRequestMatcher... matchers) {
this.matchers.addAll(Arrays.asList(matchers));
}
public CompositeHttpRequestMatcher and(HttpRequestMatcher matcher) {
this.matchers.add(matcher);
return this;
}
@Override
public boolean match(HttpRequest request) {
for (HttpRequestMatcher matcher : matchers) {
if (!matcher.match(request)) {
return false;
}
}
return true;
}
protected List<HttpRequestMatcher> getMatchers() {
return this.matchers;
}
@Override
protected Collection<?> getContent() {
List<Object> content = new LinkedList<>();
for (HttpRequestMatcher matcher : getMatchers()) {
if (matcher instanceof AbstractHttpRequestMatcher) {
content.addAll(((AbstractHttpRequestMatcher) matcher).getContent());
}
}
return content;
}
@Override
protected String getToStringInfix() {
return " && ";
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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 com.alibaba.cloud.dubbo.http.matcher;
import org.springframework.http.MediaType;
/**
* Parses and matches a single media type expression to a request's 'Content-Type' header.
* <p>
* The source code is scratched from
* org.springframework.web.servlet.mvc.condition.ConsumesRequestCondition.ConsumeMediaTypeExpression
*
* @author Rossen Stoyanchev
* @author Arjen Poutsma
*/
class ConsumeMediaTypeExpression extends AbstractMediaTypeExpression {
ConsumeMediaTypeExpression(String expression) {
super(expression);
}
ConsumeMediaTypeExpression(MediaType mediaType, boolean negated) {
super(mediaType, negated);
}
public final boolean match(MediaType contentType) {
boolean match = getMediaType().includes(contentType);
return (!isNegated() ? match : !match);
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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 com.alibaba.cloud.dubbo.http.matcher;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.util.ObjectUtils;
/**
* Parses and matches a single header expression to a request.
* <p>
* The some source code is scratched from org.springframework.web.servlet.mvc.condition.HeadersRequestCondition.HeaderExpression
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
class HeaderExpression extends AbstractNameValueExpression<String> {
HeaderExpression(String expression) {
super(expression);
}
@Override
protected boolean isCaseSensitiveName() {
return false;
}
@Override
protected String parseValue(String valueExpression) {
return valueExpression;
}
@Override
protected boolean matchName(HttpRequest request) {
HttpHeaders httpHeaders = request.getHeaders();
return httpHeaders.containsKey(this.name);
}
@Override
protected boolean matchValue(HttpRequest request) {
HttpHeaders httpHeaders = request.getHeaders();
String headerValue = httpHeaders.getFirst(this.name);
return ObjectUtils.nullSafeEquals(this.value, headerValue);
}
}

View File

@@ -0,0 +1,123 @@
/*
* 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 com.alibaba.cloud.dubbo.http.matcher;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.MediaType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* {@link HttpRequest} 'Content-Type' header {@link HttpRequestMatcher matcher}
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class HttpRequestConsumersMatcher extends AbstractHttpRequestMatcher {
private final List<ConsumeMediaTypeExpression> expressions;
/**
* Creates a new instance from 0 or more "consumes" expressions.
*
* @param consumes consumes expressions if 0 expressions are provided,
* the condition will match to every request
*/
public HttpRequestConsumersMatcher(String... consumes) {
this(consumes, null);
}
/**
* Creates a new instance with "consumes" and "header" expressions.
* "Header" expressions where the header name is not 'Content-Type' or have
* no header value defined are ignored. If 0 expressions are provided in
* total, the condition will match to every request
*
* @param consumes consumes expressions
* @param headers headers expressions
*/
public HttpRequestConsumersMatcher(String[] consumes, String[] headers) {
this(parseExpressions(consumes, headers));
}
/**
* Private constructor accepting parsed media type expressions.
*/
private HttpRequestConsumersMatcher(Collection<ConsumeMediaTypeExpression> expressions) {
this.expressions = new ArrayList<>(expressions);
Collections.sort(this.expressions);
}
@Override
public boolean match(HttpRequest request) {
if (expressions.isEmpty()) {
return true;
}
HttpHeaders httpHeaders = request.getHeaders();
MediaType contentType = httpHeaders.getContentType();
if (contentType == null) {
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
for (ConsumeMediaTypeExpression expression : expressions) {
if (!expression.match(contentType)) {
return false;
}
}
return true;
}
private static Set<ConsumeMediaTypeExpression> parseExpressions(String[] consumes, String[] headers) {
Set<ConsumeMediaTypeExpression> result = new LinkedHashSet<>();
if (headers != null) {
for (String header : headers) {
HeaderExpression expr = new HeaderExpression(header);
if ("Content-Type".equalsIgnoreCase(expr.name) && expr.value != null) {
for (MediaType mediaType : MediaType.parseMediaTypes(expr.value)) {
result.add(new ConsumeMediaTypeExpression(mediaType, expr.negated));
}
}
}
}
for (String consume : consumes) {
result.add(new ConsumeMediaTypeExpression(consume));
}
return result;
}
@Override
protected Collection<ConsumeMediaTypeExpression> getContent() {
return this.expressions;
}
@Override
protected String getToStringInfix() {
return " || ";
}
}

View File

@@ -0,0 +1,71 @@
/*
* 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 com.alibaba.cloud.dubbo.http.matcher;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* {@link HttpRequest} headers {@link HttpRequestMatcher matcher}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class HttpRequestHeadersMatcher extends AbstractHttpRequestMatcher {
private final Set<HeaderExpression> expressions;
public HttpRequestHeadersMatcher(String... headers) {
this.expressions = parseExpressions(headers);
}
private static Set<HeaderExpression> parseExpressions(String... headers) {
Set<HeaderExpression> expressions = new LinkedHashSet<>();
for (String header : headers) {
HeaderExpression expr = new HeaderExpression(header);
if (HttpHeaders.ACCEPT.equalsIgnoreCase(expr.name) ||
HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(expr.name)) {
continue;
}
expressions.add(expr);
}
return expressions;
}
@Override
public boolean match(HttpRequest request) {
for (HeaderExpression expression : this.expressions) {
if (!expression.match(request)) {
return false;
}
}
return true;
}
@Override
protected Collection<HeaderExpression> getContent() {
return this.expressions;
}
@Override
protected String getToStringInfix() {
return " && ";
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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 com.alibaba.cloud.dubbo.http.matcher;
import org.springframework.http.HttpRequest;
/**
* {@link HttpRequest} Matcher
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public interface HttpRequestMatcher {
/**
* Match {@link HttpRequest} or not
*
* @param request The {@link HttpRequest} instance
* @return if matched, return <code>true</code>, or <code>false</code>.
*/
boolean match(HttpRequest request);
}

View File

@@ -0,0 +1,82 @@
/*
* 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 com.alibaba.cloud.dubbo.http.matcher;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import org.springframework.util.StringUtils;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import static org.springframework.http.HttpMethod.resolve;
/**
* {@link HttpRequest} {@link HttpMethod methods} {@link HttpRequestMatcher matcher}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class HttpRequestMethodsMatcher extends AbstractHttpRequestMatcher {
private final Set<HttpMethod> methods;
public HttpRequestMethodsMatcher(String... methods) {
this.methods = resolveHttpMethods(methods);
}
private Set<HttpMethod> resolveHttpMethods(String[] methods) {
Set<HttpMethod> httpMethods = new LinkedHashSet<>(methods.length);
for (String method : methods) {
if (!StringUtils.hasText(method)) {
continue;
}
HttpMethod httpMethod = resolve(method);
httpMethods.add(httpMethod);
}
return httpMethods;
}
public Set<HttpMethod> getMethods() {
return methods;
}
@Override
public boolean match(HttpRequest request) {
boolean matched = false;
HttpMethod httpMethod = request.getMethod();
if (httpMethod != null) {
for (HttpMethod method : getMethods()) {
if (httpMethod.equals(method)) {
matched = true;
break;
}
}
}
return matched;
}
@Override
protected Collection<HttpMethod> getContent() {
return methods;
}
@Override
protected String getToStringInfix() {
return " || ";
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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 com.alibaba.cloud.dubbo.http.matcher;
import org.springframework.http.HttpRequest;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* {@link HttpRequest} parameters {@link HttpRequestMatcher matcher}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class HttpRequestParamsMatcher extends AbstractHttpRequestMatcher {
private final Set<ParamExpression> expressions;
/**
* @param params The pattern of params :
* <ul>
* <li>name=value</li>
* <li>name</li>
* </ul>
*/
public HttpRequestParamsMatcher(String... params) {
this.expressions = parseExpressions(params);
}
@Override
public boolean match(HttpRequest request) {
if (CollectionUtils.isEmpty(expressions)) {
return true;
}
for (ParamExpression paramExpression : expressions) {
if (paramExpression.match(request)) {
return true;
}
}
return false;
}
private static Set<ParamExpression> parseExpressions(String... params) {
Set<ParamExpression> expressions = new LinkedHashSet<>();
for (String param : params) {
expressions.add(new ParamExpression(param));
}
return expressions;
}
@Override
protected Collection<ParamExpression> getContent() {
return this.expressions;
}
@Override
protected String getToStringInfix() {
return " && ";
}
}

View File

@@ -0,0 +1,117 @@
/*
* 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 com.alibaba.cloud.dubbo.http.matcher;
import org.springframework.http.HttpRequest;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* {@link HttpRequest} {@link URI} {@link HttpRequestMatcher matcher}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class HttpRequestPathMatcher extends AbstractHttpRequestMatcher {
private final Set<String> patterns;
private final PathMatcher pathMatcher;
public HttpRequestPathMatcher(String... patterns) {
this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns));
this.pathMatcher = new AntPathMatcher();
}
@Override
public boolean match(HttpRequest request) {
List<String> matches = getMatchingPatterns(request);
return !matches.isEmpty();
}
public List<String> getMatchingPatterns(HttpRequest request) {
String path = getPath(request);
List<String> matches = getMatchingPatterns(path);
return matches;
}
public List<String> getMatchingPatterns(String lookupPath) {
List<String> matches = new ArrayList<>();
for (String pattern : this.patterns) {
String match = getMatchingPattern(pattern, lookupPath);
if (match != null) {
matches.add(match);
}
}
if (matches.size() > 1) {
matches.sort(this.pathMatcher.getPatternComparator(lookupPath));
}
return matches;
}
private String getMatchingPattern(String pattern, String lookupPath) {
if (pattern.equals(lookupPath)) {
return pattern;
}
boolean hasSuffix = pattern.indexOf('.') != -1;
if (!hasSuffix && this.pathMatcher.match(pattern + ".*", lookupPath)) {
return pattern + ".*";
}
if (this.pathMatcher.match(pattern, lookupPath)) {
return pattern;
}
if (!pattern.endsWith("/") && this.pathMatcher.match(pattern + "/", lookupPath)) {
return pattern + "/";
}
return null;
}
private String getPath(HttpRequest request) {
URI uri = request.getURI();
return uri.getPath();
}
private static Set<String> prependLeadingSlash(String[] patterns) {
Set<String> result = new LinkedHashSet<>(patterns.length);
for (String pattern : patterns) {
if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
pattern = "/" + pattern;
}
result.add(pattern);
}
return result;
}
@Override
protected Collection<String> getContent() {
return this.patterns;
}
@Override
protected String getToStringInfix() {
return " || ";
}
}

View File

@@ -0,0 +1,119 @@
/*
* 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 com.alibaba.cloud.dubbo.http.matcher;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.MediaType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* {@link HttpRequest} 'Accept' header {@link HttpRequestMatcher matcher}
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class HttpRequestProducesMatcher extends AbstractHttpRequestMatcher {
private final List<ProduceMediaTypeExpression> expressions;
/**
* Creates a new instance from "produces" expressions. If 0 expressions
* are provided in total, this condition will match to any request.
*
* @param produces produces expressions
*/
public HttpRequestProducesMatcher(String... produces) {
this(produces, null);
}
/**
* Creates a new instance with "produces" and "header" expressions. "Header"
* expressions where the header name is not 'Accept' or have no header value
* defined are ignored. If 0 expressions are provided in total, this condition
* will match to any request.
*
* @param produces produces expressions
* @param headers headers expressions
*/
public HttpRequestProducesMatcher(String[] produces, String[] headers) {
this(parseExpressions(produces, headers));
}
/**
* Private constructor accepting parsed media type expressions.
*/
private HttpRequestProducesMatcher(Collection<ProduceMediaTypeExpression> expressions) {
this.expressions = new ArrayList<>(expressions);
Collections.sort(this.expressions);
}
@Override
public boolean match(HttpRequest request) {
if (expressions.isEmpty()) {
return true;
}
HttpHeaders httpHeaders = request.getHeaders();
List<MediaType> acceptedMediaTypes = httpHeaders.getAccept();
for (ProduceMediaTypeExpression expression : expressions) {
if (!expression.match(acceptedMediaTypes)) {
return false;
}
}
return true;
}
private static Set<ProduceMediaTypeExpression> parseExpressions(String[] produces, String[] headers) {
Set<ProduceMediaTypeExpression> result = new LinkedHashSet<>();
if (headers != null) {
for (String header : headers) {
HeaderExpression expr = new HeaderExpression(header);
if (HttpHeaders.ACCEPT.equalsIgnoreCase(expr.name) && expr.value != null) {
for (MediaType mediaType : MediaType.parseMediaTypes(expr.value)) {
result.add(new ProduceMediaTypeExpression(mediaType, expr.negated));
}
}
}
}
for (String produce : produces) {
result.add(new ProduceMediaTypeExpression(produce));
}
return result;
}
@Override
protected Collection<ProduceMediaTypeExpression> getContent() {
return expressions;
}
@Override
protected String getToStringInfix() {
return " || ";
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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 com.alibaba.cloud.dubbo.http.matcher;
import org.springframework.http.MediaType;
/**
* A contract for media type expressions (e.g. "text/plain", "!text/plain") as
* defined in the for "consumes" and "produces".
* <p>
* The source code is scratched from org.springframework.web.servlet.mvc.condition.MediaTypeExpression
*
* @author Rossen Stoyanchev
*/
interface MediaTypeExpression {
MediaType getMediaType();
boolean isNegated();
}

View File

@@ -0,0 +1,38 @@
/*
* 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 com.alibaba.cloud.dubbo.http.matcher;
/**
* A contract for {@code "name!=value"} style expression used to specify request
* parameters and request header in HTTP request
* <p>
* The some source code is scratched from org.springframework.web.servlet.mvc.condition.NameValueExpression
*
* @param <T> the value type
* @author Rossen Stoyanchev
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
interface NameValueExpression<T> {
String getName();
T getValue();
boolean isNegated();
}

View File

@@ -0,0 +1,62 @@
/*
* 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 com.alibaba.cloud.dubbo.http.matcher;
import org.springframework.http.HttpRequest;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import static com.alibaba.cloud.dubbo.http.util.HttpUtils.getParameters;
/**
* Parses and matches a single param expression to a request.
* <p>
* The some source code is scratched from org.springframework.web.servlet.mvc.condition.ParamsRequestCondition.ParamExpression
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
class ParamExpression extends AbstractNameValueExpression<String> {
ParamExpression(String expression) {
super(expression);
}
@Override
protected boolean isCaseSensitiveName() {
return true;
}
@Override
protected String parseValue(String valueExpression) {
return valueExpression;
}
@Override
protected boolean matchName(HttpRequest request) {
MultiValueMap<String, String> parametersMap = getParameters(request);
return parametersMap.containsKey(this.name);
}
@Override
protected boolean matchValue(HttpRequest request) {
MultiValueMap<String, String> parametersMap = getParameters(request);
String parameterValue = parametersMap.getFirst(this.name);
return ObjectUtils.nullSafeEquals(this.value, parameterValue);
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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 com.alibaba.cloud.dubbo.http.matcher;
import org.springframework.http.MediaType;
import java.util.List;
/**
* Parses and matches a single media type expression to a request's 'Accept' header.
* <p>
* The source code is scratched from
* org.springframework.web.servlet.mvc.condition.ProducesRequestCondition.ProduceMediaTypeExpression
*
* @author Rossen Stoyanchev
* @author Arjen Poutsma
*/
class ProduceMediaTypeExpression extends AbstractMediaTypeExpression {
ProduceMediaTypeExpression(String expression) {
super(expression);
}
ProduceMediaTypeExpression(MediaType mediaType, boolean negated) {
super(mediaType, negated);
}
public final boolean match(List<MediaType> acceptedMediaTypes) {
boolean match = matchMediaType(acceptedMediaTypes);
return (!isNegated() ? match : !match);
}
private boolean matchMediaType(List<MediaType> acceptedMediaTypes) {
for (MediaType acceptedMediaType : acceptedMediaTypes) {
if (getMediaType().isCompatibleWith(acceptedMediaType)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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 com.alibaba.cloud.dubbo.http.matcher;
import com.alibaba.cloud.dubbo.metadata.RequestMetadata;
import static com.alibaba.cloud.dubbo.http.util.HttpUtils.toNameAndValues;
/**
* {@link RequestMetadata} {@link HttpRequestMatcher} implementation
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class RequestMetadataMatcher extends CompositeHttpRequestMatcher {
public RequestMetadataMatcher(RequestMetadata metadata) {
super(
// method
new HttpRequestMethodsMatcher(metadata.getMethod()),
// url
new HttpRequestPathMatcher(metadata.getPath()),
// params
new HttpRequestParamsMatcher(toNameAndValues(metadata.getParams())),
// headers
new HttpRequestHeadersMatcher(toNameAndValues(metadata.getHeaders())),
// consumes
new HttpRequestConsumersMatcher(metadata.getConsumes().toArray(new String[0])),
// produces
new HttpRequestProducesMatcher(metadata.getProduces().toArray(new String[0]))
);
}
}

View File

@@ -0,0 +1,228 @@
/*
* 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 com.alibaba.cloud.dubbo.http.util;
import com.alibaba.cloud.dubbo.http.converter.HttpMessageConverterHolder;
import com.alibaba.cloud.dubbo.metadata.RequestMetadata;
import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.MediaType;
import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import static java.util.Collections.unmodifiableList;
/**
* {@link HttpMessageConverter} Resolver
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class HttpMessageConverterResolver {
private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType("application");
private final List<HttpMessageConverter<?>> messageConverters;
private final List<MediaType> allSupportedMediaTypes;
private final ClassLoader classLoader;
public HttpMessageConverterResolver(List<HttpMessageConverter<?>> messageConverters, ClassLoader classLoader) {
this.messageConverters = messageConverters;
this.allSupportedMediaTypes = getAllSupportedMediaTypes(messageConverters);
this.classLoader = classLoader;
}
public HttpMessageConverterHolder resolve(HttpRequest request, Class<?> parameterType) {
HttpMessageConverterHolder httpMessageConverterHolder = null;
HttpHeaders httpHeaders = request.getHeaders();
MediaType contentType = httpHeaders.getContentType();
if (contentType == null) {
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter genericConverter = (GenericHttpMessageConverter) converter;
if (genericConverter.canRead(parameterType, parameterType, contentType)) {
httpMessageConverterHolder = new HttpMessageConverterHolder(contentType, converter);
break;
}
} else {
if (converter.canRead(parameterType, contentType)) {
httpMessageConverterHolder = new HttpMessageConverterHolder(contentType, converter);
break;
}
}
}
return httpMessageConverterHolder;
}
/**
* Resolve the most match {@link HttpMessageConverter} from {@link RequestMetadata}
*
* @param requestMetadata {@link RequestMetadata}
* @param restMethodMetadata {@link RestMethodMetadata}
* @return
*/
public HttpMessageConverterHolder resolve(RequestMetadata requestMetadata, RestMethodMetadata
restMethodMetadata) {
HttpMessageConverterHolder httpMessageConverterHolder = null;
Class<?> returnValueClass = resolveReturnValueClass(restMethodMetadata);
/**
* @see AbstractMessageConverterMethodProcessor#writeWithMessageConverters(Object, MethodParameter, ServletServerHttpRequest, ServletServerHttpResponse)
*/
List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(requestMetadata);
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(restMethodMetadata, returnValueClass);
Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();
for (MediaType requestedType : requestedMediaTypes) {
for (MediaType producibleType : producibleMediaTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
if (compatibleMediaTypes.isEmpty()) {
return httpMessageConverterHolder;
}
List<MediaType> mediaTypes = new ArrayList<>(compatibleMediaTypes);
MediaType.sortBySpecificityAndQuality(mediaTypes);
MediaType selectedMediaType = null;
for (MediaType mediaType : mediaTypes) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
} else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
if (messageConverter.canWrite(returnValueClass, selectedMediaType)) {
httpMessageConverterHolder = new HttpMessageConverterHolder(selectedMediaType, messageConverter);
break;
}
}
}
return httpMessageConverterHolder;
}
public List<MediaType> getAllSupportedMediaTypes() {
return unmodifiableList(allSupportedMediaTypes);
}
private Class<?> resolveReturnValueClass(RestMethodMetadata restMethodMetadata) {
String returnClassName = restMethodMetadata.getMethod().getReturnType();
return ClassUtils.resolveClassName(returnClassName, classLoader);
}
/**
* Resolve the {@link MediaType media-types}
*
* @param requestMetadata {@link RequestMetadata} from client side
* @return non-null {@link List}
*/
private List<MediaType> getAcceptableMediaTypes(RequestMetadata requestMetadata) {
return requestMetadata.getProduceMediaTypes();
}
/**
* Returns
* the media types that can be produced: <ul> <li>The producible media types specified in the request mappings, or
* <li>Media types of configured converters that can write the specific return value, or <li>{@link MediaType#ALL}
* </ul>
*
* @param restMethodMetadata {@link RestMethodMetadata} from server side
* @param returnValueClass the class of return value
* @return non-null {@link List}
*/
private List<MediaType> getProducibleMediaTypes(RestMethodMetadata restMethodMetadata, Class<?>
returnValueClass) {
RequestMetadata serverRequestMetadata = restMethodMetadata.getRequest();
List<MediaType> mediaTypes = serverRequestMetadata.getProduceMediaTypes();
if (!CollectionUtils.isEmpty(mediaTypes)) { // Empty
return mediaTypes;
} else if (!this.allSupportedMediaTypes.isEmpty()) {
List<MediaType> result = new ArrayList<>();
for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter.canWrite(returnValueClass, null)) {
result.addAll(converter.getSupportedMediaTypes());
}
}
return result;
} else {
return Collections.singletonList(MediaType.ALL);
}
}
/**
* Return the media types
* supported by all provided message converters sorted by specificity via {@link
* MediaType#sortBySpecificity(List)}.
*
* @param messageConverters
* @return
*/
private List<MediaType> getAllSupportedMediaTypes(List<HttpMessageConverter<?>> messageConverters) {
Set<MediaType> allSupportedMediaTypes = new LinkedHashSet<MediaType>();
for (HttpMessageConverter<?> messageConverter : messageConverters) {
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
}
List<MediaType> result = new ArrayList<MediaType>(allSupportedMediaTypes);
MediaType.sortBySpecificity(result);
return unmodifiableList(result);
}
/**
* Return the more specific of the acceptable and the producible media types
* with the q-value of the former.
*/
private MediaType getMostSpecificMediaType(MediaType acceptType, MediaType produceType) {
MediaType produceTypeToUse = produceType.copyQualityValue(acceptType);
return (MediaType.SPECIFICITY_COMPARATOR.compare(acceptType, produceTypeToUse) <= 0 ? acceptType : produceTypeToUse);
}
}

View File

@@ -0,0 +1,239 @@
/*
* 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 com.alibaba.cloud.dubbo.http.util;
import org.springframework.http.HttpRequest;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.springframework.util.StringUtils.delimitedListToStringArray;
import static org.springframework.util.StringUtils.hasText;
import static org.springframework.util.StringUtils.trimAllWhitespace;
/**
* Http Utilities class
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public abstract class HttpUtils {
private static final String UTF_8 = "UTF-8";
private static final String EQUAL = "=";
private static final String AND = "&";
private static final String SEMICOLON = ";";
private static final String QUESTION_MASK = "?";
/**
* The empty value
*/
private static final String EMPTY_VALUE = "";
/**
* Normalize path:
* <ol>
* <li>To remove query string if presents</li>
* <li>To remove duplicated slash("/") if exists</li>
* </ol>
*
* @param path path to be normalized
* @return a normalized path if required
*/
public static String normalizePath(String path) {
if (!hasText(path)) {
return path;
}
String normalizedPath = path;
int index = normalizedPath.indexOf(QUESTION_MASK);
if (index > -1) {
normalizedPath = normalizedPath.substring(0, index);
}
return StringUtils.replace(normalizedPath, "//", "/");
}
/**
* Get Parameters from the specified {@link HttpRequest request}
*
* @param request the specified {@link HttpRequest request}
* @return
*/
public static MultiValueMap<String, String> getParameters(HttpRequest request) {
URI uri = request.getURI();
return getParameters(uri.getQuery());
}
/**
* Get Parameters from the specified query string.
* <p>
*
* @param queryString The query string
* @return The query parameters
*/
public static MultiValueMap<String, String> getParameters(String queryString) {
return getParameters(delimitedListToStringArray(queryString, AND));
}
/**
* Get Parameters from the specified pairs of name-value.
* <p>
*
* @param pairs The pairs of name-value
* @return The query parameters
*/
public static MultiValueMap<String, String> getParameters(Iterable<String> pairs) {
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
if (pairs != null) {
for (String pair : pairs) {
String[] nameAndValue = delimitedListToStringArray(pair, EQUAL);
String name = decode(nameAndValue[0]);
String value = nameAndValue.length < 2 ? null : nameAndValue[1];
value = decode(value);
addParam(parameters, name, value);
}
}
return parameters;
}
/**
* Get Parameters from the specified pairs of name-value.
* <p>
*
* @param pairs The pairs of name-value
* @return The query parameters
*/
public static MultiValueMap<String, String> getParameters(String... pairs) {
return getParameters(Arrays.asList(pairs));
}
// /**
// * Parse a read-only {@link MultiValueMap} of {@link HttpCookie} from {@link HttpHeaders}
// *
// * @param httpHeaders {@link HttpHeaders}
// * @return non-null, the key is a cookie name , the value is {@link HttpCookie}
// */
// public static MultiValueMap<String, HttpCookie> parseCookies(HttpHeaders httpHeaders) {
//
// String cookie = httpHeaders.getFirst(COOKIE);
//
// String[] cookieNameAndValues = StringUtils.delimitedListToStringArray(cookie, SEMICOLON);
//
// MultiValueMap<String, HttpCookie> cookies = new LinkedMultiValueMap<>(cookieNameAndValues.length);
//
// for (String cookeNameAndValue : cookieNameAndValues) {
// String[] nameAndValue = delimitedListToStringArray(trimWhitespace(cookeNameAndValue), EQUAL);
// String name = nameAndValue[0];
// String value = nameAndValue.length < 2 ? null : nameAndValue[1];
// HttpCookie httpCookie = new HttpCookie(name, value);
// cookies.add(name, httpCookie);
// }
//
// return cookies;
// }
/**
* To the name and value line sets
*
* @param nameAndValuesMap {@link MultiValueMap} the map of name and values
* @return non-null
*/
public static Set<String> toNameAndValuesSet(Map<String, List<String>> nameAndValuesMap) {
Set<String> nameAndValues = new LinkedHashSet<>();
for (Map.Entry<String, List<String>> entry : nameAndValuesMap.entrySet()) {
String name = entry.getKey();
List<String> values = entry.getValue();
for (String value : values) {
String nameAndValue = name + EQUAL + value;
nameAndValues.add(nameAndValue);
}
}
return nameAndValues;
}
public static String[] toNameAndValues(Map<String, List<String>> nameAndValuesMap) {
return toNameAndValuesSet(nameAndValuesMap).toArray(new String[0]);
}
/**
* Generate a string of query string from the specified request parameters {@link Map}
*
* @param params the specified request parameters {@link Map}
* @return non-null
*/
public static String toQueryString(Map<String, List<String>> params) {
StringBuilder builder = new StringBuilder();
for (String line : toNameAndValuesSet(params)) {
builder.append(line).append(AND);
}
return builder.toString();
}
/**
* Decode value
*
* @param value the value requires to decode
* @return the decoded value
*/
public static String decode(String value) {
if (value == null) {
return value;
}
String decodedValue = value;
try {
decodedValue = URLDecoder.decode(value, UTF_8);
} catch (UnsupportedEncodingException ex) {
}
return decodedValue;
}
/**
* encode value
*
* @param value the value requires to encode
* @return the encoded value
*/
public static String encode(String value) {
String encodedValue = value;
try {
encodedValue = URLEncoder.encode(value, UTF_8);
} catch (UnsupportedEncodingException ex) {
}
return encodedValue;
}
private static void addParam(MultiValueMap<String, String> paramsMap, String name, String value) {
String paramValue = trimAllWhitespace(value);
if (!StringUtils.hasText(paramValue)) {
paramValue = EMPTY_VALUE;
}
paramsMap.add(trimAllWhitespace(name), paramValue);
}
}