org.rapidoid.http.HttpClientUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.rapidoid.http.HttpClientUtil.java

Source

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.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.u.U;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CancellationException;

/*
 * #%L
 * rapidoid-http-client
 * %%
 * Copyright (C) 2014 - 2016 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%
 */

@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) {

        ConnectionReuseStrategy reuseStrategy = client.reuseConnections() ? new DefaultConnectionReuseStrategy()
                : new NoConnectionReuseStrategy();

        HttpAsyncClientBuilder builder = HttpAsyncClients.custom()
                .setThreadFactory(new RapidoidThreadFactory("http-client")).disableConnectionState()
                .disableAuthCaching().setMaxConnPerRoute(client.maxConnPerRoute())
                .setMaxConnTotal(client.maxConnTotal()).setConnectionReuseStrategy(reuseStrategy)
                .setRedirectStrategy(client.followRedirects() ? new DefaultRedirectStrategy() : NO_REDIRECTS);

        if (!U.isEmpty(client.cookies())) {
            BasicCookieStore cookieStore = new BasicCookieStore();

            for (Map.Entry<String, String> 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<String, String> headers = U.safe(config.headers());

        String url = config.url();

        HttpRequestBase req = createReq(config, url);

        for (Map.Entry<String, String> e : headers.entrySet()) {
            req.addHeader(e.getKey(), e.getValue());
        }

        if (config.verb() == HttpVerb.POST || config.verb() == HttpVerb.PUT || config.verb() == HttpVerb.PATCH) {
            HttpEntityEnclosingRequestBase entityEnclosingReq = (HttpEntityEnclosingRequestBase) req;
            entityEnclosingReq.setEntity(
                    config.body() != null ? byteBody(config) : paramsBody(config.data(), config.files()));
        }

        req.setConfig(reqConfig(config));

        return req;
    }

    private static RequestConfig reqConfig(HttpReq config) {
        return RequestConfig.custom().setSocketTimeout(config.socketTimeout())
                .setConnectTimeout(config.connectTimeout())
                .setConnectionRequestTimeout(config.connectionRequestTimeout()).build();
    }

    private static NByteArrayEntity paramsBody(Map<String, Object> data, Map<String, List<Upload>> files) {
        data = U.safe(data);
        files = U.safe(files);

        MultipartEntityBuilder builder = MultipartEntityBuilder.create();

        for (Map.Entry<String, List<Upload>> entry : files.entrySet()) {
            for (Upload file : entry.getValue()) {
                builder = builder.addBinaryBody(entry.getKey(), file.content(), ContentType.DEFAULT_BINARY,
                        file.filename());
            }
        }

        for (Map.Entry<String, Object> 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<HttpResp> request(HttpReq config, CloseableHttpAsyncClient client, Callback<HttpResp> callback,
            boolean close) {

        HttpRequestBase req = createRequest(config);
        Log.debug("Starting HTTP request", "request", req.getRequestLine());

        Promise<HttpResp> promise = Promises.create();
        FutureCallback<HttpResponse> cb = callback(client, callback, promise, close);
        client.execute(req, cb);

        return promise;
    }

    private static <T> FutureCallback<HttpResponse> callback(final CloseableHttpAsyncClient client,
            final Callback<HttpResp> callback, final Callback<HttpResp> promise, final boolean close) {

        return new FutureCallback<HttpResponse>() {

            @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<String, String> 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);
        }
    }

}