From 132adb48996704776f452f4a25c4ce9e3d998a66 Mon Sep 17 00:00:00 2001 From: "mai.jh" Date: Tue, 14 Jul 2020 19:37:37 +0800 Subject: [PATCH] [ISSUE##3317]Change the http client implementation that nacos resttemplate depends on from apache to JDk (#3322) * fix:#3317 change the http client implementation that nacos resttemplate depends on from apache to JDk * Use the IoUtils.closeQuietly() method to close the InputStream * change HttpClientBeanHolder logger output object --- .../http/AbstractHttpClientFactory.java | 8 +- .../common/http/HttpClientBeanHolder.java | 2 +- .../http/client/HttpClientResponse.java | 6 +- .../http/client/JdkHttpClientRequest.java | 100 ++++++++++++++++++ .../http/client/JdkHttpClientResponse.java | 78 ++++++++++++++ 5 files changed, 186 insertions(+), 8 deletions(-) create mode 100644 common/src/main/java/com/alibaba/nacos/common/http/client/JdkHttpClientRequest.java create mode 100644 common/src/main/java/com/alibaba/nacos/common/http/client/JdkHttpClientResponse.java diff --git a/common/src/main/java/com/alibaba/nacos/common/http/AbstractHttpClientFactory.java b/common/src/main/java/com/alibaba/nacos/common/http/AbstractHttpClientFactory.java index b3ed2568dc8..e09a29b2102 100644 --- a/common/src/main/java/com/alibaba/nacos/common/http/AbstractHttpClientFactory.java +++ b/common/src/main/java/com/alibaba/nacos/common/http/AbstractHttpClientFactory.java @@ -17,11 +17,10 @@ package com.alibaba.nacos.common.http; import com.alibaba.nacos.common.http.client.DefaultAsyncHttpClientRequest; -import com.alibaba.nacos.common.http.client.DefaultHttpClientRequest; +import com.alibaba.nacos.common.http.client.JdkHttpClientRequest; import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import org.apache.http.client.config.RequestConfig; -import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.nio.client.HttpAsyncClients; import org.slf4j.Logger; @@ -34,9 +33,8 @@ public abstract class AbstractHttpClientFactory implements HttpClientFactory { @Override public final NacosRestTemplate createNacosRestTemplate() { - RequestConfig requestConfig = getRequestConfig(); - return new NacosRestTemplate(assignLogger(), - new DefaultHttpClientRequest(HttpClients.custom().setDefaultRequestConfig(requestConfig).build())); + HttpClientConfig httpClientConfig = buildHttpClientConfig(); + return new NacosRestTemplate(assignLogger(), new JdkHttpClientRequest(httpClientConfig)); } @Override diff --git a/common/src/main/java/com/alibaba/nacos/common/http/HttpClientBeanHolder.java b/common/src/main/java/com/alibaba/nacos/common/http/HttpClientBeanHolder.java index 0e264985d46..ce6f8ca3707 100644 --- a/common/src/main/java/com/alibaba/nacos/common/http/HttpClientBeanHolder.java +++ b/common/src/main/java/com/alibaba/nacos/common/http/HttpClientBeanHolder.java @@ -35,7 +35,7 @@ */ public final class HttpClientBeanHolder { - private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientManager.class); + private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientBeanHolder.class); private static final Map SINGLETON_REST = new HashMap(10); diff --git a/common/src/main/java/com/alibaba/nacos/common/http/client/HttpClientResponse.java b/common/src/main/java/com/alibaba/nacos/common/http/client/HttpClientResponse.java index 0aaac2f3833..e11a26f28e2 100644 --- a/common/src/main/java/com/alibaba/nacos/common/http/client/HttpClientResponse.java +++ b/common/src/main/java/com/alibaba/nacos/common/http/client/HttpClientResponse.java @@ -48,15 +48,17 @@ public interface HttpClientResponse extends Closeable { * Return the HTTP status code. * * @return the HTTP status as an integer + * @throws IOException IOException */ - int getStatusCode(); + int getStatusCode() throws IOException; /** * Return the HTTP status text of the response. * * @return the HTTP status text + * @throws IOException IOException */ - String getStatusText(); + String getStatusText() throws IOException; /** * close response InputStream. diff --git a/common/src/main/java/com/alibaba/nacos/common/http/client/JdkHttpClientRequest.java b/common/src/main/java/com/alibaba/nacos/common/http/client/JdkHttpClientRequest.java new file mode 100644 index 00000000000..538dd061afe --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/http/client/JdkHttpClientRequest.java @@ -0,0 +1,100 @@ +/* + * 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.nacos.common.http.client; + +import com.alibaba.nacos.common.constant.HttpHeaderConsts; +import com.alibaba.nacos.common.http.HttpClientConfig; +import com.alibaba.nacos.common.http.HttpUtils; +import com.alibaba.nacos.common.http.param.Header; +import com.alibaba.nacos.common.http.param.MediaType; +import com.alibaba.nacos.common.model.RequestHttpEntity; +import com.alibaba.nacos.common.utils.JacksonUtils; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +/** + * JDK http client request implement. + * + * @author mai.jh + */ +public class JdkHttpClientRequest implements HttpClientRequest { + + private HttpClientConfig httpClientConfig; + + public JdkHttpClientRequest(HttpClientConfig httpClientConfig) { + this.httpClientConfig = httpClientConfig; + } + + @Override + public HttpClientResponse execute(URI uri, String httpMethod, RequestHttpEntity requestHttpEntity) + throws Exception { + final Object body = requestHttpEntity.getBody(); + final Header headers = requestHttpEntity.getHeaders(); + replaceDefaultConfig(requestHttpEntity.getHttpClientConfig()); + + HttpURLConnection conn = (HttpURLConnection) uri.toURL().openConnection(); + Map headerMap = headers.getHeader(); + if (headerMap != null && headerMap.size() > 0) { + for (Map.Entry entry : headerMap.entrySet()) { + conn.setRequestProperty(entry.getKey(), entry.getValue()); + } + } + + conn.setConnectTimeout(this.httpClientConfig.getConTimeOutMillis()); + conn.setReadTimeout(this.httpClientConfig.getReadTimeOutMillis()); + conn.setRequestMethod(httpMethod); + if (body != null) { + String contentType = headers.getValue(HttpHeaderConsts.CONTENT_TYPE); + String bodyStr = JacksonUtils.toJson(body); + if (MediaType.APPLICATION_FORM_URLENCODED.equals(contentType)) { + Map map = JacksonUtils.toObj(bodyStr, HashMap.class); + bodyStr = HttpUtils.encodingParams(map, headers.getCharset()); + } + if (bodyStr != null) { + conn.setDoOutput(true); + byte[] b = bodyStr.getBytes(); + conn.setRequestProperty("Content-Length", String.valueOf(b.length)); + conn.getOutputStream().write(b, 0, b.length); + conn.getOutputStream().flush(); + conn.getOutputStream().close(); + } + } + conn.connect(); + return new JdkHttpClientResponse(conn); + } + + /** + * Replace the HTTP config created by default with the HTTP config specified in the request. + * + * @param replaceConfig http config + */ + private void replaceDefaultConfig(HttpClientConfig replaceConfig) { + if (replaceConfig == null) { + return; + } + this.httpClientConfig = replaceConfig; + } + + @Override + public void close() throws IOException { + + } +} diff --git a/common/src/main/java/com/alibaba/nacos/common/http/client/JdkHttpClientResponse.java b/common/src/main/java/com/alibaba/nacos/common/http/client/JdkHttpClientResponse.java new file mode 100644 index 00000000000..6ca7650e62b --- /dev/null +++ b/common/src/main/java/com/alibaba/nacos/common/http/client/JdkHttpClientResponse.java @@ -0,0 +1,78 @@ +/* + * 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.nacos.common.http.client; + +import com.alibaba.nacos.common.http.param.Header; +import com.alibaba.nacos.common.utils.IoUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.util.List; +import java.util.Map; + +/** + * JDk http client response implement. + * + * @author mai.jh + */ +public class JdkHttpClientResponse implements HttpClientResponse { + + private final HttpURLConnection conn; + + private InputStream responseStream; + + private Header responseHeader; + + public JdkHttpClientResponse(HttpURLConnection conn) { + this.conn = conn; + } + + @Override + public Header getHeaders() { + if (this.responseHeader == null) { + this.responseHeader = Header.newInstance(); + } + + for (Map.Entry> entry : conn.getHeaderFields().entrySet()) { + this.responseHeader.addParam(entry.getKey(), entry.getValue().get(0)); + } + return this.responseHeader; + } + + @Override + public InputStream getBody() throws IOException { + InputStream errorStream = this.conn.getErrorStream(); + this.responseStream = (errorStream != null ? errorStream : this.conn.getInputStream()); + return this.responseStream; + } + + @Override + public int getStatusCode() throws IOException { + return this.conn.getResponseCode(); + } + + @Override + public String getStatusText() throws IOException { + return this.conn.getResponseMessage(); + } + + @Override + public void close() { + IoUtils.closeQuietly(this.responseStream); + } +}