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:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 " && ";
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 " || ";
|
||||
}
|
||||
}
|
||||
@@ -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 " && ";
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 " || ";
|
||||
}
|
||||
}
|
||||
@@ -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 " && ";
|
||||
}
|
||||
}
|
||||
@@ -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 " || ";
|
||||
}
|
||||
}
|
||||
@@ -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 " || ";
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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]))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user