Browse Source

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
master
Jason Joo Eric Zhao 4 years ago
parent
commit
c5071550fa
2 changed files with 99 additions and 10 deletions
  1. +44
    -10
      sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/client/SentinelApiClient.java
  2. +55
    -0
      sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/client/SentinelApiClientTest.java

+ 44
- 10
sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/client/SentinelApiClient.java View File

@@ -69,6 +69,7 @@ import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.DefaultRedirectStrategy;
@@ -93,6 +94,8 @@ public class SentinelApiClient {
private static Logger logger = LoggerFactory.getLogger(SentinelApiClient.class);

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 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 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 MODIFY_CLUSTER_SERVER_TRANSPORT_CONFIG_PATH = "cluster/server/modifyTransportConfig";
@@ -127,6 +129,7 @@ public class SentinelApiClient {
private CloseableHttpAsyncClient httpClient;

private static final SentinelVersion version160 = new SentinelVersion(1, 6, 0);
private static final SentinelVersion version171 = new SentinelVersion(1, 7, 1);
@Autowired
private AppManagement appManagement;
@@ -151,6 +154,30 @@ public class SentinelApiClient {
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) {
StringBuilder queryStringBuilder = new StringBuilder();
for (Entry<String, String> entry : params.entrySet()) {
@@ -169,7 +196,15 @@ public class SentinelApiClient {
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);
if (params != null && params.size() > 0) {
List<NameValuePair> list = new ArrayList<>(params.size());
@@ -177,6 +212,9 @@ public class SentinelApiClient {
list.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
httpPost.setEntity(new UrlEncodedFormEntity(list, Consts.UTF_8));
if (!supportEnhancedContentType) {
httpPost.setHeader(HTTP_HEADER_CONTENT_TYPE, HTTP_HEADER_CONTENT_TYPE_URLENCODED);
}
}
return httpPost;
}
@@ -193,7 +231,7 @@ public class SentinelApiClient {
private String getBody(HttpResponse response) throws Exception {
Charset charset = null;
try {
String contentTypeStr = response.getFirstHeader("Content-type").getValue();
String contentTypeStr = response.getFirstHeader(HTTP_HEADER_CONTENT_TYPE).getValue();
if (StringUtil.isNotEmpty(contentTypeStr)) {
ContentType contentType = ContentType.parse(contentTypeStr);
charset = contentType.getCharset();
@@ -250,12 +288,7 @@ public class SentinelApiClient {
if (params == null) {
params = Collections.emptyMap();
}
boolean supportPost = 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);
if (!useHttpPost || !supportPost) {
if (!useHttpPost || !isSupportPost(app, ip, port)) {
// Using GET in older versions, append parameters after url
if (!params.isEmpty()) {
if (urlBuilder.indexOf("?") == -1) {
@@ -268,7 +301,8 @@ public class SentinelApiClient {
return executeCommand(new HttpGet(urlBuilder.toString()));
} else {
// Using POST
return executeCommand(postRequest(urlBuilder.toString(), params));
return executeCommand(
postRequest(urlBuilder.toString(), params, isSupportEnhancedContentType(app, ip, port)));
}
}


+ 55
- 0
sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/client/SentinelApiClientTest.java View File

@@ -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…
Cancel
Save