org.asynchttpclient.providers.netty.request.NettyRequestFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.asynchttpclient.providers.netty.request.NettyRequestFactory.java

Source

/*
 * Copyright 2010-2013 Ning, Inc.
 *
 * Ning licenses this file to you 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 org.asynchttpclient.providers.netty.request;

import static org.asynchttpclient.providers.netty.util.HttpUtil.isNTLM;
import static org.asynchttpclient.providers.netty.util.HttpUtil.isSecure;
import static org.asynchttpclient.providers.netty.util.HttpUtil.isWebSocket;
import static org.asynchttpclient.util.AsyncHttpProviderUtils.DEFAULT_CHARSET;
import static org.asynchttpclient.util.MiscUtil.isNonEmpty;

import org.asynchttpclient.AsyncHttpClientConfig;
import org.asynchttpclient.FluentStringsMap;
import org.asynchttpclient.ProxyServer;
import org.asynchttpclient.Realm;
import org.asynchttpclient.Request;
import org.asynchttpclient.cookie.CookieEncoder;
import org.asynchttpclient.generators.FileBodyGenerator;
import org.asynchttpclient.generators.InputStreamBodyGenerator;
import org.asynchttpclient.ntlm.NTLMEngine;
import org.asynchttpclient.ntlm.NTLMEngineException;
import org.asynchttpclient.providers.netty.NettyAsyncHttpProvider;
import org.asynchttpclient.providers.netty.NettyAsyncHttpProviderConfig;
import org.asynchttpclient.providers.netty.request.body.NettyBody;
import org.asynchttpclient.providers.netty.request.body.NettyBodyBody;
import org.asynchttpclient.providers.netty.request.body.NettyByteArrayBody;
import org.asynchttpclient.providers.netty.request.body.NettyFileBody;
import org.asynchttpclient.providers.netty.request.body.NettyInputStreamBody;
import org.asynchttpclient.providers.netty.request.body.NettyMultipartBody;
import org.asynchttpclient.providers.netty.ws.WebSocketUtil;
import org.asynchttpclient.spnego.SpnegoEngine;
import org.asynchttpclient.util.AsyncHttpProviderUtils;
import org.asynchttpclient.util.AuthenticatorUtils;
import org.asynchttpclient.util.UTF8UrlEncoder;

import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpVersion;

import java.io.IOException;
import java.net.URI;
import java.nio.charset.Charset;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Map.Entry;

public final class NettyRequestFactory {

    public static final String GZIP_DEFLATE = HttpHeaders.Values.GZIP + "," + HttpHeaders.Values.DEFLATE;

    private final AsyncHttpClientConfig config;
    private final NettyAsyncHttpProviderConfig nettyConfig;

    public NettyRequestFactory(AsyncHttpClientConfig config, NettyAsyncHttpProviderConfig nettyConfig) {
        this.config = config;
        this.nettyConfig = nettyConfig;
    }

    private String requestUri(URI uri, ProxyServer proxyServer, HttpMethod method) {
        if (method == HttpMethod.CONNECT)
            return AsyncHttpProviderUtils.getAuthority(uri);

        else if (proxyServer != null && !(isSecure(uri) && config.isUseRelativeURIsWithSSLProxies()))
            return uri.toString();

        else if (uri.getRawQuery() != null)
            return uri.getRawPath() + "?" + uri.getRawQuery();

        else
            return uri.getRawPath();
    }

    private String hostHeader(Request request, URI uri, Realm realm) {

        String hostHeader = null;

        String host = request.getVirtualHost() != null ? request.getVirtualHost()
                : AsyncHttpProviderUtils.getHost(uri);

        if (host != null) {
            if (request.getVirtualHost() != null || uri.getPort() == -1)
                hostHeader = host;
            else
                hostHeader = host + ":" + uri.getPort();
        }

        return hostHeader;
    }

    private String authorizationHeader(Request request, URI uri, ProxyServer proxyServer, Realm realm)
            throws IOException {

        String authorizationHeader = null;

        if (realm != null && realm.getUsePreemptiveAuth()) {

            switch (realm.getAuthScheme()) {
            case BASIC:
                authorizationHeader = AuthenticatorUtils.computeBasicAuthentication(realm);
                break;
            case DIGEST:
                if (isNonEmpty(realm.getNonce())) {
                    try {
                        authorizationHeader = AuthenticatorUtils.computeDigestAuthentication(realm);
                    } catch (NoSuchAlgorithmException e) {
                        throw new SecurityException(e);
                    }
                }
                break;
            case NTLM:
                String domain;
                if (proxyServer != null && proxyServer.getNtlmDomain() != null) {
                    domain = proxyServer.getNtlmDomain();
                } else {
                    domain = realm.getNtlmDomain();
                }
                try {
                    String msg = NTLMEngine.INSTANCE.generateType1Msg("NTLM " + domain, realm.getNtlmHost());
                    authorizationHeader = "NTLM " + msg;
                } catch (NTLMEngineException e) {
                    throw new IOException(e);
                }
                break;
            case KERBEROS:
            case SPNEGO:

                String host;
                if (proxyServer != null)
                    host = proxyServer.getHost();
                else if (request.getVirtualHost() != null)
                    host = request.getVirtualHost();
                else
                    host = AsyncHttpProviderUtils.getHost(uri);

                if (host == null)
                    host = "127.0.0.1";

                try {
                    authorizationHeader = "Negotiate " + SpnegoEngine.instance().generateToken(host);
                } catch (Throwable e) {
                    throw new IOException(e);
                }
                break;
            case NONE:
                break;
            default:
                throw new IllegalStateException("Invalid Authentication " + realm);
            }
        }

        return authorizationHeader;
    }

    private String proxyAuthorizationHeader(Request request, ProxyServer proxyServer, HttpMethod method)
            throws IOException {

        String proxyAuthorization = null;

        if (method == HttpMethod.CONNECT) {
            List<String> auth = request.getHeaders().get(HttpHeaders.Names.PROXY_AUTHORIZATION);
            if (isNTLM(auth)) {
                proxyAuthorization = auth.get(0);
            }

        } else if (proxyServer != null && proxyServer.getPrincipal() != null) {
            if (isNonEmpty(proxyServer.getNtlmDomain())) {
                List<String> auth = request.getHeaders().get(HttpHeaders.Names.PROXY_AUTHORIZATION);
                if (!isNTLM(auth)) {
                    try {
                        String msg = NTLMEngine.INSTANCE.generateType1Msg(proxyServer.getNtlmDomain(),
                                proxyServer.getHost());
                        proxyAuthorization = "NTLM " + msg;
                    } catch (NTLMEngineException e) {
                        IOException ie = new IOException();
                        ie.initCause(e);
                        throw ie;
                    }
                }
            } else {
                proxyAuthorization = AuthenticatorUtils.computeBasicAuthentication(proxyServer);
            }
        }

        return proxyAuthorization;
    }

    private byte[] computeBodyFromParams(FluentStringsMap params, Charset bodyCharset) {

        StringBuilder sb = new StringBuilder();
        for (Entry<String, List<String>> paramEntry : params) {
            String key = paramEntry.getKey();
            for (String value : paramEntry.getValue()) {
                UTF8UrlEncoder.appendEncoded(sb, key);
                sb.append("=");
                UTF8UrlEncoder.appendEncoded(sb, value);
                sb.append("&");
            }
        }
        sb.setLength(sb.length() - 1);
        return sb.toString().getBytes(bodyCharset);
    }

    private NettyBody body(Request request, HttpMethod method) throws IOException {
        NettyBody nettyBody = null;
        if (method != HttpMethod.CONNECT) {

            Charset bodyCharset = request.getBodyEncoding() == null ? DEFAULT_CHARSET
                    : Charset.forName(request.getBodyEncoding());

            if (request.getByteData() != null) {
                nettyBody = new NettyByteArrayBody(request.getByteData());

            } else if (request.getStringData() != null) {
                nettyBody = new NettyByteArrayBody(request.getStringData().getBytes(bodyCharset));

            } else if (request.getStreamData() != null) {
                nettyBody = new NettyInputStreamBody(request.getStreamData());

            } else if (isNonEmpty(request.getParams())) {

                String contentType = null;
                if (!request.getHeaders().containsKey(HttpHeaders.Names.CONTENT_TYPE))
                    contentType = HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED;

                nettyBody = new NettyByteArrayBody(computeBodyFromParams(request.getParams(), bodyCharset),
                        contentType);

            } else if (request.getParts() != null) {
                nettyBody = new NettyMultipartBody(request.getParts(), request.getHeaders(), nettyConfig);

            } else if (request.getFile() != null) {
                nettyBody = new NettyFileBody(request.getFile(), nettyConfig);

            } else if (request.getBodyGenerator() instanceof FileBodyGenerator) {
                FileBodyGenerator fileBodyGenerator = (FileBodyGenerator) request.getBodyGenerator();
                nettyBody = new NettyFileBody(fileBodyGenerator.getFile(), fileBodyGenerator.getRegionSeek(),
                        fileBodyGenerator.getRegionLength(), nettyConfig);

            } else if (request.getBodyGenerator() instanceof InputStreamBodyGenerator) {
                nettyBody = new NettyInputStreamBody(
                        InputStreamBodyGenerator.class.cast(request.getBodyGenerator()).getInputStream());

            } else if (request.getBodyGenerator() != null) {
                nettyBody = new NettyBodyBody(request.getBodyGenerator().createBody(), nettyConfig);
            }
        }

        return nettyBody;
    }

    public NettyRequest newNettyRequest(Request request, URI uri, boolean forceConnect, ProxyServer proxyServer)
            throws IOException {

        HttpMethod method = forceConnect ? HttpMethod.CONNECT : HttpMethod.valueOf(request.getMethod());
        HttpVersion httpVersion = method == HttpMethod.CONNECT ? HttpVersion.HTTP_1_0 : HttpVersion.HTTP_1_1;
        String requestUri = requestUri(uri, proxyServer, method);

        NettyBody body = body(request, method);

        HttpRequest httpRequest;
        NettyRequest nettyRequest;
        if (body instanceof NettyByteArrayBody) {
            byte[] bytes = NettyByteArrayBody.class.cast(body).getBytes();
            httpRequest = new DefaultFullHttpRequest(httpVersion, method, requestUri,
                    Unpooled.wrappedBuffer(bytes));
            // body is passed as null as it's written directly with the request
            nettyRequest = new NettyRequest(httpRequest, null);

        } else if (body == null) {
            httpRequest = new DefaultFullHttpRequest(httpVersion, method, requestUri);
            nettyRequest = new NettyRequest(httpRequest, null);

        } else {
            httpRequest = new DefaultHttpRequest(httpVersion, method, requestUri);
            nettyRequest = new NettyRequest(httpRequest, body);
        }

        if (method != HttpMethod.CONNECT) {
            // assign headers as configured on request
            for (Entry<String, List<String>> header : request.getHeaders()) {
                httpRequest.headers().set(header.getKey(), header.getValue());
            }

            if (isNonEmpty(request.getCookies()))
                httpRequest.headers().set(HttpHeaders.Names.COOKIE, CookieEncoder.encode(request.getCookies()));

            if (config.isCompressionEnabled())
                httpRequest.headers().set(HttpHeaders.Names.ACCEPT_ENCODING, GZIP_DEFLATE);
        }

        if (body != null) {
            if (body.getContentLength() >= 0)
                httpRequest.headers().set(HttpHeaders.Names.CONTENT_LENGTH, body.getContentLength());
            else
                httpRequest.headers().set(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED);

            if (body.getContentType() != null)
                httpRequest.headers().set(HttpHeaders.Names.CONTENT_TYPE, body.getContentType());
        }

        // connection header and friends
        boolean webSocket = isWebSocket(uri.getScheme());
        if (method != HttpMethod.CONNECT && webSocket) {
            httpRequest.headers().set(HttpHeaders.Names.UPGRADE, HttpHeaders.Values.WEBSOCKET);
            httpRequest.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.UPGRADE);
            httpRequest.headers().set(HttpHeaders.Names.ORIGIN, "http://" + uri.getHost() + ":"
                    + (uri.getPort() == -1 ? isSecure(uri.getScheme()) ? 443 : 80 : uri.getPort()));
            httpRequest.headers().set(HttpHeaders.Names.SEC_WEBSOCKET_KEY, WebSocketUtil.getKey());
            httpRequest.headers().set(HttpHeaders.Names.SEC_WEBSOCKET_VERSION, "13");

        } else if (!httpRequest.headers().contains(HttpHeaders.Names.CONNECTION)) {
            httpRequest.headers().set(HttpHeaders.Names.CONNECTION,
                    AsyncHttpProviderUtils.keepAliveHeaderValue(config));
        }

        Realm realm = request.getRealm() != null ? request.getRealm() : config.getRealm();

        String hostHeader = hostHeader(request, uri, realm);
        if (hostHeader != null)
            httpRequest.headers().set(HttpHeaders.Names.HOST, hostHeader);

        String authorizationHeader = authorizationHeader(request, uri, proxyServer, realm);
        if (authorizationHeader != null)
            // don't override authorization but append
            httpRequest.headers().add(HttpHeaders.Names.AUTHORIZATION, authorizationHeader);

        String proxyAuthorizationHeader = proxyAuthorizationHeader(request, proxyServer, method);
        if (proxyAuthorizationHeader != null)
            httpRequest.headers().set(HttpHeaders.Names.PROXY_AUTHORIZATION, proxyAuthorizationHeader);

        // Add default accept headers
        if (!httpRequest.headers().contains(HttpHeaders.Names.ACCEPT))
            httpRequest.headers().set(HttpHeaders.Names.ACCEPT, "*/*");

        // Add default user agent
        if (!httpRequest.headers().contains(HttpHeaders.Names.USER_AGENT)) {
            String userAgent = config.getUserAgent() != null ? config.getUserAgent()
                    : AsyncHttpProviderUtils.constructUserAgent(NettyAsyncHttpProvider.class, config);
            httpRequest.headers().set(HttpHeaders.Names.USER_AGENT, userAgent);
        }

        return nettyRequest;
    }
}