de.vanita5.twittnuker.util.net.TwidereHttpClientImpl.java Source code

Java tutorial

Introduction

Here is the source code for de.vanita5.twittnuker.util.net.TwidereHttpClientImpl.java

Source

/*
 * Copyright 2007 Yusuke Yamamoto
 *
 * 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 de.vanita5.twittnuker.util.net;

import static android.text.TextUtils.isEmpty;

import android.content.Context;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpDeleteHC4;
import org.apache.http.client.methods.HttpGetHC4;
import org.apache.http.client.methods.HttpHeadHC4;
import org.apache.http.client.methods.HttpPostHC4;
import org.apache.http.client.methods.HttpPutHC4;
import org.apache.http.client.methods.HttpRequestBaseHC4;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.ContentBody;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContextHC4;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.TextUtils;

import de.vanita5.twittnuker.util.ParseUtils;
import de.vanita5.twittnuker.util.Utils;
import de.vanita5.twittnuker.util.net.ssl.HostResolvedSSLConnectionSocketFactory;
import de.vanita5.twittnuker.util.net.ssl.TwidereSSLSocketFactory;

import twitter4j.TwitterException;
import twitter4j.auth.Authorization;
import twitter4j.http.FactoryUtils;
import twitter4j.http.HostAddressResolver;
import twitter4j.http.HttpClientConfiguration;
import twitter4j.http.HttpParameter;
import twitter4j.http.HttpResponseCode;
import twitter4j.http.RequestMethod;
import twitter4j.internal.logging.Logger;
import twitter4j.internal.util.InternalStringUtil;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.Map;

/**
 * HttpClient implementation for Apache HttpClient 4.0.x
 *
 * @author Yusuke Yamamoto - yusuke at mac.com
 * @since Twitter4J 2.1.2
 */
public class TwidereHttpClientImpl implements twitter4j.http.HttpClient, HttpResponseCode {
    private static final Logger logger = Logger.getLogger(TwidereHttpClientImpl.class);
    private final HttpClientConfiguration conf;
    private final CloseableHttpClient client;

    public TwidereHttpClientImpl(final Context context, final HttpClientConfiguration conf) {
        this.conf = conf;
        final HttpClientBuilder clientBuilder = HttpClients.custom();
        final LayeredConnectionSocketFactory factory = TwidereSSLSocketFactory.getSocketFactory(context,
                conf.isSSLErrorIgnored());
        clientBuilder.setSSLSocketFactory(factory);
        final RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
        requestConfigBuilder.setConnectionRequestTimeout(conf.getHttpConnectionTimeout());
        requestConfigBuilder.setConnectTimeout(conf.getHttpConnectionTimeout());
        requestConfigBuilder.setSocketTimeout(conf.getHttpReadTimeout());
        requestConfigBuilder.setRedirectsEnabled(false);
        clientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
        if (conf.isProxyConfigured()) {
            final HttpHost proxy = new HttpHost(conf.getHttpProxyHost(), conf.getHttpProxyPort());
            clientBuilder.setProxy(proxy);
            if (!TextUtils.isEmpty(conf.getHttpProxyUser())) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Proxy AuthUser: " + conf.getHttpProxyUser());
                    logger.debug(
                            "Proxy AuthPassword: " + InternalStringUtil.maskString(conf.getHttpProxyPassword()));
                }
                final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                credentialsProvider.setCredentials(new AuthScope(conf.getHttpProxyHost(), conf.getHttpProxyPort()),
                        new UsernamePasswordCredentials(conf.getHttpProxyUser(), conf.getHttpProxyPassword()));
                clientBuilder.setDefaultCredentialsProvider(credentialsProvider);
            }
        }
        client = clientBuilder.build();
    }

    @Override
    public twitter4j.http.HttpResponse request(final twitter4j.http.HttpRequest req) throws TwitterException {
        final HostAddressResolver resolver = FactoryUtils.getHostAddressResolver(conf);
        final String urlString = req.getURL();
        final URI urlOrig = ParseUtils.parseURI(urlString);
        final String host = urlOrig.getHost(), authority = urlOrig.getAuthority();
        try {
            HttpRequestBaseHC4 commonsRequest;
            final String resolvedHost = resolver != null ? resolver.resolve(host) : null;
            final String resolvedUrl = !isEmpty(resolvedHost)
                    ? urlString.replace("://" + host, "://" + resolvedHost)
                    : urlString;
            final RequestMethod method = req.getMethod();
            if (method == RequestMethod.GET) {
                commonsRequest = new HttpGetHC4(resolvedUrl);
            } else if (method == RequestMethod.POST) {
                final HttpPostHC4 post = new HttpPostHC4(resolvedUrl);
                post.setEntity(getAsEntity(req.getParameters()));
                post.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false);
                commonsRequest = post;
            } else if (method == RequestMethod.DELETE) {
                commonsRequest = new HttpDeleteHC4(resolvedUrl);
            } else if (method == RequestMethod.HEAD) {
                commonsRequest = new HttpHeadHC4(resolvedUrl);
            } else if (method == RequestMethod.PUT) {
                final HttpPutHC4 put = new HttpPutHC4(resolvedUrl);
                put.setEntity(getAsEntity(req.getParameters()));
                commonsRequest = put;
            } else
                throw new TwitterException("Unsupported request method " + method);
            final HttpParams httpParams = commonsRequest.getParams();
            HttpClientParams.setRedirecting(httpParams, false);
            final Map<String, String> headers = req.getRequestHeaders();
            for (final String headerName : headers.keySet()) {
                commonsRequest.addHeader(headerName, headers.get(headerName));
            }
            final Authorization authorization = req.getAuthorization();
            final String authorizationHeader = authorization != null ? authorization.getAuthorizationHeader(req)
                    : null;
            if (authorizationHeader != null) {
                commonsRequest.addHeader(HttpHeaders.AUTHORIZATION, authorizationHeader);
            }
            if (resolvedHost != null && !resolvedHost.isEmpty() && !resolvedHost.equals(host)) {
                commonsRequest.addHeader(HttpHeaders.HOST, authority);
            }

            final ApacheHttpClientHttpResponseImpl res;
            try {
                final HttpContext httpContext = new BasicHttpContextHC4();
                httpContext.setAttribute(HostResolvedSSLConnectionSocketFactory.HTTP_CONTEXT_KEY_ORIGINAL_HOST,
                        host);
                res = new ApacheHttpClientHttpResponseImpl(client.execute(commonsRequest, httpContext), conf);
            } catch (final IllegalStateException e) {
                throw new TwitterException("Please check your API settings.", e);
            } catch (final NullPointerException e) {
                // Bug http://code.google.com/p/android/issues/detail?id=5255
                throw new TwitterException("Please check your APN settings, make sure not to use WAP APNs.", e);
            } catch (final OutOfMemoryError e) {
                // I don't know why OOM thown, but it should be catched.
                System.gc();
                throw new TwitterException("Unknown error", e);
            }
            final int statusCode = res.getStatusCode();
            if (statusCode < OK || statusCode > ACCEPTED)
                throw new TwitterException(res.asString(), req, res);
            return res;
        } catch (final IOException e) {
            // TODO
            if (resolver instanceof TwidereHostAddressResolver) {
                final TwidereHostAddressResolver twidereResolver = (TwidereHostAddressResolver) resolver;
                twidereResolver.removeCachedHost(host);
            }
            throw new TwitterException(e);
        }
    }

    @Override
    public void shutdown() {
        Utils.closeSilently(client);
    }

    private static HttpEntity getAsEntity(final HttpParameter[] params) throws UnsupportedEncodingException {
        if (params == null)
            return null;
        if (!HttpParameter.containsFile(params))
            return new HttpParameterFormEntity(params);
        final MultipartEntityBuilder me = MultipartEntityBuilder.create();
        for (final HttpParameter param : params) {
            if (param.isFile()) {
                final ContentType contentType = ContentType.create(param.getContentType());
                final ContentBody body;
                if (param.getFile() != null) {
                    body = new FileBody(param.getFile(), ContentType.create(param.getContentType()));
                } else {
                    body = new InputStreamBody(param.getFileBody(), contentType, param.getFileName());
                }
                me.addPart(param.getName(), body);
            } else {
                final ContentType contentType = ContentType.TEXT_PLAIN.withCharset(Consts.UTF_8);
                final ContentBody body = new StringBody(param.getValue(), contentType);
                me.addPart(param.getName(), body);
            }
        }
        return me.build();
    }
}