com.threewks.thundr.http.service.gae.HttpResponseImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.threewks.thundr.http.service.gae.HttpResponseImpl.java

Source

/*
 * This file is a component of thundr, a software library from 3wks.
 * Read more: http://3wks.github.io/thundr/
 * Copyright (C) 2014 3wks, <thundr@3wks.com.au>
 *
 * 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 com.threewks.thundr.http.service.gae;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpCookie;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import org.apache.commons.lang3.StringUtils;

import com.atomicleopard.expressive.EList;
import com.atomicleopard.expressive.Expressive;
import com.google.appengine.api.urlfetch.HTTPHeader;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.threewks.thundr.http.Header;
import com.threewks.thundr.http.service.BaseHttpResponse;
import com.threewks.thundr.http.service.HttpException;
import com.threewks.thundr.http.service.HttpRequestException;
import com.threewks.thundr.http.service.HttpResponse;
import com.threewks.thundr.http.service.HttpResponseException;

public class HttpResponseImpl extends BaseHttpResponse<HttpResponseImpl> implements HttpResponse {
    private Future<HTTPResponse> future;
    private HTTPResponse response;
    private Map<String, List<String>> headers;
    private Map<String, List<String>> headersLower;
    private Map<String, List<HttpCookie>> cookies;
    private URL url;

    public HttpResponseImpl(Future<HTTPResponse> future, HttpServiceImpl service, URL url) {
        super(service);
        this.future = future;
        this.url = url;
    }

    @Override
    public int getStatus() {
        return response().getResponseCode();
    }

    @Override
    public String getContentType() {
        return getHeader(Header.ContentType);
    }

    @Override
    public String getHeader(String name) {
        List<String> headers = getHeaders(name);
        return headers == null ? null : headers.get(0);
    }

    @Override
    public List<String> getHeaders(String name) {
        response();
        List<String> headers = headersLower.get(StringUtils.lowerCase(name));
        return headers == null ? null : Collections.unmodifiableList(headers);
    }

    @Override
    public Map<String, List<String>> getHeaders() {
        response();
        return Collections.unmodifiableMap(headers);
    }

    @Override
    public HttpCookie getCookie(String cookieName) {
        List<HttpCookie> cookies = getCookies(cookieName);
        return cookies == null ? null : cookies.get(0);
    }

    @Override
    public List<HttpCookie> getCookies(String name) {
        response();
        return cookies.get(name);
    }

    @Override
    public List<HttpCookie> getCookies() {
        response();
        return Expressive.flatten(cookies.values());
    }

    @Override
    public String getBody() {
        byte[] data = getBodyAsBytes();
        String characterEncoding = getCharset();
        try {
            return data == null ? "" : new String(data, characterEncoding);
        } catch (UnsupportedEncodingException e) {
            throw new HttpException(e, "Failed to read body as string- character encoding '%s' not supported: %s",
                    characterEncoding, e.getMessage());
        }
    }

    @Override
    public byte[] getBodyAsBytes() {
        return response().getContent();
    }

    @Override
    public InputStream getBodyAsStream() {
        byte[] content = response().getContent();
        content = content == null ? new byte[0] : content;
        return new ByteArrayInputStream(content);
    }

    @Override
    public URI getUri() {
        try {
            // final url is only non-null when we follow redirects
            URL finalUrl = response().getFinalUrl();
            return finalUrl == null ? url.toURI() : finalUrl.toURI();
        } catch (URISyntaxException e) {
            throw new HttpResponseException(e, "Uri cannot be parsed: %s", e.getMessage());
        }
    }

    private HTTPResponse response() {
        if (response == null) {
            try {
                response = future.get();
                headers = buildHeaderMap(response);
                headersLower = buildHeadersLowerMap(headers);
                cookies = buildCookieMap(headersLower);
                return response;
            } catch (InterruptedException e) {
                throw new HttpRequestException("Failed to wait for completion of asynchronous request: %s",
                        e.getMessage());
            } catch (ExecutionException e) {
                throw new HttpRequestException(e, "Failed to get result for asynchronous request: %s",
                        e.getMessage());
            }
        }
        return response;
    }

    private Map<String, List<String>> buildHeaderMap(HTTPResponse response) {
        Map<String, List<String>> headers = new LinkedHashMap<String, List<String>>();
        for (HTTPHeader header : response.getHeadersUncombined()) {
            String key = header.getName();
            String value = header.getValue();
            List<String> values = headers.get(key);
            if (values == null) {
                values = new ArrayList<String>();
                headers.put(key, values);
            }
            values.add(value);
        }
        return headers;
    }

    private Map<String, List<String>> buildHeadersLowerMap(Map<String, List<String>> headers) {
        Map<String, List<String>> lowercase = new LinkedHashMap<String, List<String>>();
        for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
            lowercase.put(StringUtils.lowerCase(entry.getKey()), new ArrayList<>(entry.getValue()));
        }
        return lowercase;
    }

    private Map<String, List<HttpCookie>> buildCookieMap(Map<String, List<String>> headersLower) {
        Map<String, List<HttpCookie>> cookies = new LinkedHashMap<String, List<HttpCookie>>();
        for (String setCookieHeader : getCookieHeaders(headersLower)) {
            List<HttpCookie> cookieSet = parseCookies(setCookieHeader);
            for (HttpCookie httpCookie : cookieSet) {
                String name = httpCookie.getName();
                List<HttpCookie> existingCookies = cookies.get(name);
                if (existingCookies == null) {
                    existingCookies = new ArrayList<HttpCookie>();
                    cookies.put(name, existingCookies);
                }
                existingCookies.add(httpCookie);
            }
        }
        return cookies;
    }

    /**
     * Get all cookie headers from the given header map.
     * 
     * Note: this will get all "Set-Cookie" and "Set-Cookie2" headers.
     * 
     * @param headers the map of headers to get the set cookie headers from.
     * @return a list of headers.
     */
    static List<String> getCookieHeaders(Map<String, List<String>> headers) {
        EList<String> cookieHeaders = Expressive.list();
        cookieHeaders.addItems(headers.get(Header.SetCookie.toLowerCase()));
        cookieHeaders.addItems(headers.get(Header.SetCookie2.toLowerCase()));
        return cookieHeaders;
    }
}