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.
org.rapidoid.http.HttpClientUtil Maven / Gradle / Ivy
/*-
* #%L
* rapidoid-http-client
* %%
* Copyright (C) 2014 - 2017 Nikolche Mihajlovski and contributors
* %%
* 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.
* #L%
*/
package org.rapidoid.http;
import org.apache.http.*;
import org.apache.http.client.RedirectStrategy;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.NoConnectionReuseStrategy;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.DefaultRedirectStrategy;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.nio.entity.NByteArrayEntity;
import org.apache.http.protocol.HttpContext;
import org.rapidoid.RapidoidThing;
import org.rapidoid.activity.RapidoidThreadFactory;
import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since;
import org.rapidoid.commons.Err;
import org.rapidoid.commons.Str;
import org.rapidoid.concurrent.*;
import org.rapidoid.io.IO;
import org.rapidoid.io.Upload;
import org.rapidoid.log.Log;
import org.rapidoid.net.tls.TLSUtil;
import org.rapidoid.u.U;
import org.rapidoid.util.Msc;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CancellationException;
@Authors("Nikolche Mihajlovski")
@Since("5.1.0")
public class HttpClientUtil extends RapidoidThing {
private static final RedirectStrategy NO_REDIRECTS = new RedirectStrategy() {
@Override
public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context)
throws ProtocolException {
return false;
}
@Override
public HttpUriRequest getRedirect(HttpRequest request, HttpResponse response, HttpContext context)
throws ProtocolException {
return null;
}
};
static CloseableHttpAsyncClient client(HttpClient client) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
ConnectionReuseStrategy reuseStrategy = client.reuseConnections() ? new DefaultConnectionReuseStrategy() : new NoConnectionReuseStrategy();
HttpAsyncClientBuilder builder = HttpAsyncClients.custom()
.setThreadFactory(new RapidoidThreadFactory("http-client", true))
.disableConnectionState()
.disableAuthCaching()
.setMaxConnPerRoute(client.maxConnPerRoute())
.setMaxConnTotal(client.maxConnTotal())
.setConnectionReuseStrategy(reuseStrategy)
.setRedirectStrategy(client.followRedirects() ? new DefaultRedirectStrategy() : NO_REDIRECTS);
if (!client.validateSSL()) {
builder.setSSLContext(TLSUtil.createTrustingContext());
builder.setSSLHostnameVerifier(new AllowAllHostnameVerifier());
}
if (!U.isEmpty(client.cookies())) {
BasicCookieStore cookieStore = new BasicCookieStore();
for (Map.Entry e : client.cookies().entrySet()) {
BasicClientCookie cookie = new BasicClientCookie(e.getKey(), e.getValue());
String host = client.host();
U.notNull(host, "HTTP client host");
cookie.setDomain(getDomain(host));
cookie.setPath("/");
cookieStore.addCookie(cookie);
}
builder = builder.setDefaultCookieStore(cookieStore);
}
if (client.userAgent() != null) {
builder = builder.setUserAgent(client.userAgent());
}
if (!client.keepCookies() && U.isEmpty(client.cookies())) {
builder = builder.disableCookieManagement();
}
return builder.build();
}
private static String getDomain(String host) {
String url = host;
url = Str.triml(url, "http://");
url = Str.triml(url, "https://");
return url.split("(/|:)")[0];
}
static HttpRequestBase createRequest(HttpReq config) {
Map headers = U.safe(config.headers());
Map cookies = U.safe(config.cookies());
String url = config.url();
url = Msc.urlWithProtocol(url);
HttpRequestBase req = createReq(config, url);
for (Map.Entry e : headers.entrySet()) {
req.addHeader(e.getKey(), e.getValue());
}
if (U.notEmpty(cookies)) {
req.addHeader("Cookie", joinCookiesAsHeader(cookies));
}
switch (config.verb()) {
case POST:
case PUT:
case PATCH:
HttpEntityEnclosingRequestBase entityEnclosingReq = (HttpEntityEnclosingRequestBase) req;
if (config.body() != null) {
entityEnclosingReq.setEntity(byteBody(config));
} else if (U.notEmpty(config.data()) || U.notEmpty(config.files())) {
entityEnclosingReq.setEntity(paramsBody(config.data(), config.files()));
}
break;
}
req.setConfig(reqConfig(config));
return req;
}
private static String joinCookiesAsHeader(Map cookies) {
StringBuilder allCookies = new StringBuilder();
for (Iterator> it = cookies.entrySet().iterator(); it.hasNext(); ) {
Map.Entry e = it.next();
allCookies.append(e.getKey());
allCookies.append("=");
allCookies.append(e.getValue());
if (it.hasNext()) {
allCookies.append("; ");
}
}
return allCookies.toString();
}
private static RequestConfig reqConfig(HttpReq config) {
return RequestConfig.custom()
.setSocketTimeout(config.socketTimeout())
.setConnectTimeout(config.connectTimeout())
.setConnectionRequestTimeout(config.connectionRequestTimeout())
.build();
}
private static NByteArrayEntity paramsBody(Map data, Map> files) {
data = U.safe(data);
files = U.safe(files);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
for (Map.Entry> entry : files.entrySet()) {
for (Upload file : entry.getValue()) {
builder = builder.addBinaryBody(entry.getKey(), file.content(), ContentType.DEFAULT_BINARY, file.filename());
}
}
for (Map.Entry entry : data.entrySet()) {
String name = entry.getKey();
String value = String.valueOf(entry.getValue());
builder = builder.addTextBody(name, value, ContentType.DEFAULT_TEXT);
}
ByteArrayOutputStream stream = new ByteArrayOutputStream();
try {
builder.build().writeTo(stream);
} catch (IOException e) {
throw U.rte(e);
}
byte[] bytes = stream.toByteArray();
return new NByteArrayEntity(bytes, ContentType.MULTIPART_FORM_DATA);
}
private static NByteArrayEntity byteBody(HttpReq config) {
NByteArrayEntity entity = new NByteArrayEntity(config.body());
if (config.contentType() != null) {
entity.setContentType(config.contentType());
}
return entity;
}
private static HttpRequestBase createReq(HttpReq config, String url) {
HttpRequestBase req;
switch (config.verb()) {
case GET:
req = new HttpGet(url);
break;
case POST:
req = new HttpPost(url);
break;
case PUT:
req = new HttpPut(url);
break;
case DELETE:
req = new HttpDelete(url);
break;
case PATCH:
req = new HttpPatch(url);
break;
case OPTIONS:
req = new HttpOptions(url);
break;
case HEAD:
req = new HttpHead(url);
break;
case TRACE:
req = new HttpTrace(url);
break;
default:
throw Err.notExpected();
}
return req;
}
static Future request(HttpReq config, CloseableHttpAsyncClient client,
Callback callback, boolean close) {
HttpRequestBase req = createRequest(config);
if (Log.isDebugEnabled()) Log.debug("Starting HTTP request", "request", req.getRequestLine());
Promise promise = Promises.create();
FutureCallback cb = callback(client, callback, promise, close);
client.execute(req, cb);
return promise;
}
private static FutureCallback callback(final CloseableHttpAsyncClient client,
final Callback callback,
final Callback promise,
final boolean close) {
return new FutureCallback() {
@Override
public void completed(HttpResponse response) {
HttpResp resp;
try {
resp = response(response);
} catch (Exception e) {
Callbacks.error(callback, e);
Callbacks.error(promise, e);
if (close) {
close(client);
}
return;
}
Callbacks.success(callback, resp);
Callbacks.success(promise, resp);
if (close) {
close(client);
}
}
@Override
public void failed(Exception e) {
Callbacks.error(callback, e);
Callbacks.error(promise, e);
if (close) {
close(client);
}
}
@Override
public void cancelled() {
Callbacks.error(callback, new CancellationException());
Callbacks.error(promise, new CancellationException());
if (close) {
close(client);
}
}
};
}
private static HttpResp response(HttpResponse response) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintWriter printer = new PrintWriter(baos);
printer.print(response.getStatusLine() + "");
printer.print("\n");
Map headers = U.map();
for (Header hdr : response.getAllHeaders()) {
printer.print(hdr.getName());
printer.print(": ");
printer.print(hdr.getValue());
printer.print("\n");
headers.put(hdr.getName(), hdr.getValue());
}
printer.print("\n");
printer.flush();
HttpEntity entity = response.getEntity();
byte[] body = entity != null ? IO.loadBytes(response.getEntity().getContent()) : new byte[0];
baos.write(body);
byte[] raw = baos.toByteArray();
return new HttpResp(raw, response.getStatusLine().getStatusCode(), headers, body);
}
static void close(CloseableHttpAsyncClient client) {
try {
Log.debug("Closing HTTP client", "client", client);
client.close();
} catch (IOException e) {
throw U.rte(e);
}
}
}