All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.github.sseserver.util.ApacheHttpUtil Maven / Gradle / Ivy
package com.github.sseserver.util;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.DnsResolver;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.conn.SystemDefaultDnsResolver;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.codecs.DefaultHttpRequestWriterFactory;
import org.apache.http.impl.nio.codecs.DefaultHttpResponseParserFactory;
import org.apache.http.impl.nio.conn.ManagedNHttpClientConnectionFactory;
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.nio.client.HttpAsyncClient;
import org.apache.http.nio.conn.ManagedNHttpClientConnection;
import org.apache.http.nio.conn.NHttpConnectionFactory;
import org.apache.http.nio.conn.NoopIOSessionStrategy;
import org.apache.http.nio.conn.SchemeIOSessionStrategy;
import org.apache.http.nio.entity.NByteArrayEntity;
import org.apache.http.nio.reactor.ConnectingIOReactor;
import org.apache.http.nio.reactor.IOReactorException;
import org.apache.http.nio.util.HeapByteBufferAllocator;
import org.apache.http.protocol.HttpContext;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.http.HttpHeaders;
import org.springframework.util.StringUtils;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.CodingErrorAction;
import java.util.ArrayList;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class ApacheHttpUtil {
private static final AtomicInteger acceptThreadId = new AtomicInteger();
private static final AtomicInteger ioThreadId = new AtomicInteger();
public static long keepaliveSeconds = Long.getLong("sseserver.ApacheHttpUtil.keepaliveSeconds",
60L);
public static int defaultMaxPerRoute = Integer.getInteger("sseserver.ApacheHttpUtil.defaultMaxPerRoute",
100);
public static SpringUtil.AsyncClientHttpRequestFactory newRequestFactory(int connectTimeout, int readTimeout, int maxThreads, String threadName) {
// HTTPConnection工厂 :配置请求/解析响应
NHttpConnectionFactory connFactory =
new ManagedNHttpClientConnectionFactory(
DefaultHttpRequestWriterFactory.INSTANCE,
DefaultHttpResponseParserFactory.INSTANCE, HeapByteBufferAllocator.INSTANCE);
// 为支持的协议方案创建自定义连接套接字工厂的注册表。
Registry sessionStrategyRegistry = RegistryBuilder.create()
.register("http", NoopIOSessionStrategy.INSTANCE)
.build();
//DNS解析器
DnsResolver dnsResolver = SystemDefaultDnsResolver.INSTANCE;
// Create I/O reactor configuration
IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
.setIoThreadCount(maxThreads)
.setConnectTimeout(connectTimeout)
.setSoTimeout(readTimeout)
.setSoKeepAlive(true)
.setTcpNoDelay(false)
.build();
ThreadFactory acceptThreadFactory = r -> {
Thread thread = new Thread(r, threadName + "-Accept-" + acceptThreadId.incrementAndGet());
thread.setDaemon(true);
return thread;
};
ThreadFactory ioThreadFactory = r -> {
Thread thread = new Thread(r, threadName + "-IO-" + ioThreadId.incrementAndGet());
thread.setDaemon(true);
return thread;
};
// 创建一个定制的I/O reactort
ConnectingIOReactor ioReactor;
try {
ioReactor = new DefaultConnectingIOReactor(ioReactorConfig, acceptThreadFactory);
} catch (IOReactorException e) {
throw new IllegalStateException("ApacheHttpUtil.newRequestFactory fail : " + e, e);
}
// 使用自定义配置创建连接管理器。
PoolingNHttpClientConnectionManager asyncConnManager = new PoolingNHttpClientConnectionManager(
ioReactor, connFactory, sessionStrategyRegistry, null, dnsResolver, keepaliveSeconds, TimeUnit.SECONDS);
//创建连接配置
ConnectionConfig connectionConfig = ConnectionConfig.custom()
.setMalformedInputAction(CodingErrorAction.IGNORE)
.setUnmappableInputAction(CodingErrorAction.IGNORE)
.setCharset(Charset.forName("UTF-8"))
.build();
// 将连接管理器配置为默认使用或针对特定主机使用连接配置。
asyncConnManager.setDefaultConnectionConfig(connectionConfig);
// 配置永久连接的最大总数或每个路由限制
// 可以保留在池中或由连接管理器租用。
//每个路由的默认最大连接,每个路由实际最大连接为默认为DefaultMaxPreRoute控制,而MaxTotal是控制整个池子最大数
asyncConnManager.setMaxTotal(maxThreads);
asyncConnManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
// 创建全局请求配置
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setCookieSpec(CookieSpecs.IGNORE_COOKIES)
.setConnectionRequestTimeout(readTimeout)
.setSocketTimeout((int) (keepaliveSeconds * 1000))
.setConnectTimeout(connectTimeout)
.setExpectContinueEnabled(true)
.build();
/**
* KeepAlive策略
*/
class CustomConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy {
@Override
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
long keepAliveDuration = DefaultConnectionKeepAliveStrategy.INSTANCE.getKeepAliveDuration(response, context);
return keepAliveDuration > 0 ? keepAliveDuration : keepaliveSeconds * 1000L;
}
}
CustomConnectionKeepAliveStrategy keepAliveStrategy = new CustomConnectionKeepAliveStrategy();
// Create an HttpClientUtils with the given custom dependencies and configuration.
HttpAsyncClient asyncHttpClient = HttpAsyncClients.custom()
.setConnectionManager(asyncConnManager)
.setDefaultRequestConfig(defaultRequestConfig)
.setThreadFactory(ioThreadFactory)
// .setConnectionManagerShared(true)
.setMaxConnTotal(maxThreads)
// 有 Keep-Alive 认里面的值,没有的话永久有效
// .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
// 换成自定义的
.setKeepAliveStrategy(keepAliveStrategy)
.build();
HttpComponentsAsyncClientHttpRequestFactory factory = new HttpComponentsAsyncClientHttpRequestFactory(asyncHttpClient);
factory.startAsyncClient();
return factory;
}
public static class HttpComponentsAsyncClientHttpRequestFactory
implements SpringUtil.AsyncClientHttpRequestFactory, DisposableBean {
private final HttpAsyncClient asyncClient;
public HttpComponentsAsyncClientHttpRequestFactory(HttpAsyncClient asyncClient) {
this.asyncClient = asyncClient;
}
public HttpAsyncClient startAsyncClient() {
HttpAsyncClient client = asyncClient;
if (client instanceof CloseableHttpAsyncClient) {
CloseableHttpAsyncClient closeableAsyncClient = (CloseableHttpAsyncClient) client;
if (!closeableAsyncClient.isRunning()) {
closeableAsyncClient.start();
}
}
return client;
}
protected HttpUriRequest createHttpUriRequest(String httpMethod, URI uri) {
switch (httpMethod) {
case "GET":
return new HttpGet(uri);
case "HEAD":
return new HttpHead(uri);
case "POST":
return new HttpPost(uri);
case "PUT":
return new HttpPut(uri);
case "PATCH":
return new HttpPatch(uri);
case "DELETE":
return new ApacheHttpUtil.HttpDelete(uri);
case "OPTIONS":
return new HttpOptions(uri);
case "TRACE":
return new HttpTrace(uri);
default:
throw new IllegalArgumentException("Invalid HTTP method: " + httpMethod);
}
}
@Override
public SpringUtil.AsyncClientHttpRequest createAsyncRequest(URI uri, String httpMethod) throws IOException {
HttpAsyncClient client = startAsyncClient();
HttpUriRequest httpRequest = createHttpUriRequest(httpMethod, uri);
HttpContext context = HttpClientContext.create();
return new HttpComponentsAsyncClientHttpRequest(client, httpRequest, context);
}
@Override
public void destroy() throws Exception {
HttpAsyncClient httpClient = asyncClient;
if (httpClient instanceof Closeable) {
((Closeable) httpClient).close();
}
}
}
final static class HttpComponentsAsyncClientHttpRequest extends SpringUtil.AbstractBufferingAsyncClientHttpRequest {
private final HttpAsyncClient httpClient;
private final HttpUriRequest httpRequest;
private final HttpContext httpContext;
HttpComponentsAsyncClientHttpRequest(HttpAsyncClient client, HttpUriRequest request, HttpContext context) {
this.httpClient = client;
this.httpRequest = request;
this.httpContext = context;
}
@Override
public String getMethod() {
return this.httpRequest.getMethod();
}
@Override
protected CompletableFuture> executeInternal(SpringUtil.HttpHeaders headers, byte[] bufferedOutput) throws IOException {
addHeaders(this.httpRequest, headers);
if (this.httpRequest instanceof HttpEntityEnclosingRequest) {
HttpEntityEnclosingRequest entityEnclosingRequest = (HttpEntityEnclosingRequest) this.httpRequest;
HttpEntity requestEntity = new NByteArrayEntity(bufferedOutput);
entityEnclosingRequest.setEntity(requestEntity);
}
CompletableFuture> future = new CompletableFuture<>();
this.httpClient.execute(this.httpRequest, this.httpContext, new FutureCallback() {
@Override
public void completed(HttpResponse httpResponse) {
future.complete(new ApacheClientHttpResponse(httpResponse));
}
@Override
public void failed(Exception e) {
future.completeExceptionally(e);
}
@Override
public void cancelled() {
future.completeExceptionally(new IOException("cancelled"));
}
});
return future;
}
static void addHeaders(HttpUriRequest httpRequest, SpringUtil.HttpHeaders headers) {
headers.forEach((headerName, headerValues) -> {
if (HttpHeaders.COOKIE.equalsIgnoreCase(headerName)) { // RFC 6265
String headerValue = StringUtils.collectionToDelimitedString(headerValues, "; ");
httpRequest.addHeader(headerName, headerValue);
} else if (!"Content-Length".equalsIgnoreCase(headerName) &&
!"Transfer-Encoding".equalsIgnoreCase(headerName)) {
for (String headerValue : headerValues) {
httpRequest.addHeader(headerName, headerValue);
}
}
});
}
}
static class ApacheClientHttpResponse extends SpringUtil.HttpEntity {
private SpringUtil.HttpHeaders headers;
private final HttpResponse httpResponse;
public ApacheClientHttpResponse(HttpResponse httpResponse) {
this.httpResponse = httpResponse;
}
@Override
public SpringUtil.HttpHeaders getHeaders() {
if (this.headers == null) {
this.headers = new SpringUtil.HttpHeaders();
for (Header header : httpResponse.getAllHeaders()) {
this.headers.computeIfAbsent(header.getName(), e -> new ArrayList<>())
.add(header.getValue());
}
}
return this.headers;
}
@Override
public int getStatus() {
return httpResponse.getStatusLine().getStatusCode();
}
@Override
public InputStream getBody() {
HttpEntity entity = httpResponse.getEntity();
try {
return entity != null ? entity.getContent() : new ByteArrayInputStream(new byte[0]);
} catch (IOException e) {
LambdaUtil.sneakyThrows(e);
return null;
}
}
}
private static class HttpDelete extends HttpEntityEnclosingRequestBase {
public HttpDelete(URI uri) {
super();
setURI(uri);
}
@Override
public String getMethod() {
return "DELETE";
}
}
}