dashboard: Improve the compatibility on the Content-Type header of POST request (#1260)
* Better compatibility of the dashboard to legacy and new sentinel-transport-simple-http (on Content-Type header), related to #1207
This commit is contained in:
parent
b136848873
commit
c5071550fa
|
|
@ -69,6 +69,7 @@ import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
import org.apache.http.client.methods.HttpPost;
|
import org.apache.http.client.methods.HttpPost;
|
||||||
import org.apache.http.client.methods.HttpUriRequest;
|
import org.apache.http.client.methods.HttpUriRequest;
|
||||||
|
import org.apache.http.client.utils.URLEncodedUtils;
|
||||||
import org.apache.http.concurrent.FutureCallback;
|
import org.apache.http.concurrent.FutureCallback;
|
||||||
import org.apache.http.entity.ContentType;
|
import org.apache.http.entity.ContentType;
|
||||||
import org.apache.http.impl.client.DefaultRedirectStrategy;
|
import org.apache.http.impl.client.DefaultRedirectStrategy;
|
||||||
|
|
@ -93,6 +94,8 @@ public class SentinelApiClient {
|
||||||
private static Logger logger = LoggerFactory.getLogger(SentinelApiClient.class);
|
private static Logger logger = LoggerFactory.getLogger(SentinelApiClient.class);
|
||||||
|
|
||||||
private static final Charset DEFAULT_CHARSET = Charset.forName(SentinelConfig.charset());
|
private static final Charset DEFAULT_CHARSET = Charset.forName(SentinelConfig.charset());
|
||||||
|
private static final String HTTP_HEADER_CONTENT_TYPE = "Content-Type";
|
||||||
|
private static final String HTTP_HEADER_CONTENT_TYPE_URLENCODED = ContentType.create(URLEncodedUtils.CONTENT_TYPE).toString();
|
||||||
|
|
||||||
private static final String RESOURCE_URL_PATH = "jsonTree";
|
private static final String RESOURCE_URL_PATH = "jsonTree";
|
||||||
private static final String CLUSTER_NODE_PATH = "clusterNode";
|
private static final String CLUSTER_NODE_PATH = "clusterNode";
|
||||||
|
|
@ -106,7 +109,6 @@ public class SentinelApiClient {
|
||||||
private static final String FETCH_CLUSTER_CLIENT_CONFIG_PATH = "cluster/client/fetchConfig";
|
private static final String FETCH_CLUSTER_CLIENT_CONFIG_PATH = "cluster/client/fetchConfig";
|
||||||
private static final String MODIFY_CLUSTER_CLIENT_CONFIG_PATH = "cluster/client/modifyConfig";
|
private static final String MODIFY_CLUSTER_CLIENT_CONFIG_PATH = "cluster/client/modifyConfig";
|
||||||
|
|
||||||
private static final String FETCH_CLUSTER_SERVER_ALL_CONFIG_PATH = "cluster/server/fetchConfig";
|
|
||||||
private static final String FETCH_CLUSTER_SERVER_BASIC_INFO_PATH = "cluster/server/info";
|
private static final String FETCH_CLUSTER_SERVER_BASIC_INFO_PATH = "cluster/server/info";
|
||||||
|
|
||||||
private static final String MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH = "cluster/server/modifyTransportConfig";
|
private static final String MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH = "cluster/server/modifyTransportConfig";
|
||||||
|
|
@ -127,6 +129,7 @@ public class SentinelApiClient {
|
||||||
private CloseableHttpAsyncClient httpClient;
|
private CloseableHttpAsyncClient httpClient;
|
||||||
|
|
||||||
private static final SentinelVersion version160 = new SentinelVersion(1, 6, 0);
|
private static final SentinelVersion version160 = new SentinelVersion(1, 6, 0);
|
||||||
|
private static final SentinelVersion version171 = new SentinelVersion(1, 7, 1);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AppManagement appManagement;
|
private AppManagement appManagement;
|
||||||
|
|
@ -151,6 +154,30 @@ public class SentinelApiClient {
|
||||||
return statusCode == 400 && StringUtil.isNotEmpty(body) && body.contains(CommandConstants.MSG_UNKNOWN_COMMAND_PREFIX);
|
return statusCode == 400 && StringUtil.isNotEmpty(body) && body.contains(CommandConstants.MSG_UNKNOWN_COMMAND_PREFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean isSupportPost(String app, String ip, int port) {
|
||||||
|
return StringUtil.isNotEmpty(app) && Optional.ofNullable(appManagement.getDetailApp(app))
|
||||||
|
.flatMap(e -> e.getMachine(ip, port))
|
||||||
|
.flatMap(m -> VersionUtils.parseVersion(m.getVersion())
|
||||||
|
.map(v -> v.greaterOrEqual(version160)))
|
||||||
|
.orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check wheter target instance (identified by tuple of app-ip:port)
|
||||||
|
* supports the form of "xxxxx; xx=xx" in "Content-Type" header.
|
||||||
|
*
|
||||||
|
* @param app target app name
|
||||||
|
* @param ip target node's address
|
||||||
|
* @param port target node's port
|
||||||
|
*/
|
||||||
|
protected boolean isSupportEnhancedContentType(String app, String ip, int port) {
|
||||||
|
return StringUtil.isNotEmpty(app) && Optional.ofNullable(appManagement.getDetailApp(app))
|
||||||
|
.flatMap(e -> e.getMachine(ip, port))
|
||||||
|
.flatMap(m -> VersionUtils.parseVersion(m.getVersion())
|
||||||
|
.map(v -> v.greaterOrEqual(version171)))
|
||||||
|
.orElse(false);
|
||||||
|
}
|
||||||
|
|
||||||
private StringBuilder queryString(Map<String, String> params) {
|
private StringBuilder queryString(Map<String, String> params) {
|
||||||
StringBuilder queryStringBuilder = new StringBuilder();
|
StringBuilder queryStringBuilder = new StringBuilder();
|
||||||
for (Entry<String, String> entry : params.entrySet()) {
|
for (Entry<String, String> entry : params.entrySet()) {
|
||||||
|
|
@ -169,7 +196,15 @@ public class SentinelApiClient {
|
||||||
return queryStringBuilder;
|
return queryStringBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpUriRequest postRequest(String url, Map<String, String> params) {
|
/**
|
||||||
|
* Build an `HttpUriRequest` in POST way.
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @param params
|
||||||
|
* @param supportEnhancedContentType see {@link #isSupportEnhancedContentType(String, String, int)}
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected static HttpUriRequest postRequest(String url, Map<String, String> params, boolean supportEnhancedContentType) {
|
||||||
HttpPost httpPost = new HttpPost(url);
|
HttpPost httpPost = new HttpPost(url);
|
||||||
if (params != null && params.size() > 0) {
|
if (params != null && params.size() > 0) {
|
||||||
List<NameValuePair> list = new ArrayList<>(params.size());
|
List<NameValuePair> list = new ArrayList<>(params.size());
|
||||||
|
|
@ -177,6 +212,9 @@ public class SentinelApiClient {
|
||||||
list.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
|
list.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
|
||||||
}
|
}
|
||||||
httpPost.setEntity(new UrlEncodedFormEntity(list, Consts.UTF_8));
|
httpPost.setEntity(new UrlEncodedFormEntity(list, Consts.UTF_8));
|
||||||
|
if (!supportEnhancedContentType) {
|
||||||
|
httpPost.setHeader(HTTP_HEADER_CONTENT_TYPE, HTTP_HEADER_CONTENT_TYPE_URLENCODED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return httpPost;
|
return httpPost;
|
||||||
}
|
}
|
||||||
|
|
@ -193,7 +231,7 @@ public class SentinelApiClient {
|
||||||
private String getBody(HttpResponse response) throws Exception {
|
private String getBody(HttpResponse response) throws Exception {
|
||||||
Charset charset = null;
|
Charset charset = null;
|
||||||
try {
|
try {
|
||||||
String contentTypeStr = response.getFirstHeader("Content-type").getValue();
|
String contentTypeStr = response.getFirstHeader(HTTP_HEADER_CONTENT_TYPE).getValue();
|
||||||
if (StringUtil.isNotEmpty(contentTypeStr)) {
|
if (StringUtil.isNotEmpty(contentTypeStr)) {
|
||||||
ContentType contentType = ContentType.parse(contentTypeStr);
|
ContentType contentType = ContentType.parse(contentTypeStr);
|
||||||
charset = contentType.getCharset();
|
charset = contentType.getCharset();
|
||||||
|
|
@ -250,12 +288,7 @@ public class SentinelApiClient {
|
||||||
if (params == null) {
|
if (params == null) {
|
||||||
params = Collections.emptyMap();
|
params = Collections.emptyMap();
|
||||||
}
|
}
|
||||||
boolean supportPost = StringUtil.isNotEmpty(app) && Optional.ofNullable(appManagement.getDetailApp(app))
|
if (!useHttpPost || !isSupportPost(app, ip, port)) {
|
||||||
.flatMap(e -> e.getMachine(ip, port))
|
|
||||||
.flatMap(m -> VersionUtils.parseVersion(m.getVersion())
|
|
||||||
.map(v -> v.greaterOrEqual(version160)))
|
|
||||||
.orElse(false);
|
|
||||||
if (!useHttpPost || !supportPost) {
|
|
||||||
// Using GET in older versions, append parameters after url
|
// Using GET in older versions, append parameters after url
|
||||||
if (!params.isEmpty()) {
|
if (!params.isEmpty()) {
|
||||||
if (urlBuilder.indexOf("?") == -1) {
|
if (urlBuilder.indexOf("?") == -1) {
|
||||||
|
|
@ -268,7 +301,8 @@ public class SentinelApiClient {
|
||||||
return executeCommand(new HttpGet(urlBuilder.toString()));
|
return executeCommand(new HttpGet(urlBuilder.toString()));
|
||||||
} else {
|
} else {
|
||||||
// Using POST
|
// Using POST
|
||||||
return executeCommand(postRequest(urlBuilder.toString(), params));
|
return executeCommand(
|
||||||
|
postRequest(urlBuilder.toString(), params, isSupportEnhancedContentType(app, ip, port)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.alibaba.csp.sentinel.dashboard.client;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.http.HttpException;
|
||||||
|
import org.apache.http.client.methods.HttpUriRequest;
|
||||||
|
import org.apache.http.protocol.RequestContent;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class SentinelApiClientTest {
|
||||||
|
@Test
|
||||||
|
public void postRequest() throws HttpException, IOException {
|
||||||
|
// Processor is required because it will determine the final request body including
|
||||||
|
// headers before outgoing.
|
||||||
|
RequestContent processor = new RequestContent();
|
||||||
|
Map<String, String> params = new HashMap<String, String>();
|
||||||
|
params.put("a", "1");
|
||||||
|
params.put("b", "2+");
|
||||||
|
params.put("c", "3 ");
|
||||||
|
|
||||||
|
HttpUriRequest request;
|
||||||
|
|
||||||
|
request = SentinelApiClient.postRequest("/test", params, false);
|
||||||
|
assertNotNull(request);
|
||||||
|
processor.process(request, null);
|
||||||
|
assertNotNull(request.getFirstHeader("Content-Type"));
|
||||||
|
assertEquals("application/x-www-form-urlencoded", request.getFirstHeader("Content-Type").getValue());
|
||||||
|
|
||||||
|
request = SentinelApiClient.postRequest("/test", params, true);
|
||||||
|
assertNotNull(request);
|
||||||
|
processor.process(request, null);
|
||||||
|
assertNotNull(request.getFirstHeader("Content-Type"));
|
||||||
|
assertEquals("application/x-www-form-urlencoded; charset=UTF-8", request.getFirstHeader("Content-Type").getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue