All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.threewks.thundr.http.service.gae.HttpResponseImpl Maven / Gradle / Ivy

There is a newer version: 3.0.0
Show newest version
/*
 * This file is a component of thundr, a software library from 3wks.
 * Read more: http://3wks.github.io/thundr/
 * Copyright (C) 2014 3wks, 
 *
 * 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 implements HttpResponse {
	private Future future;
	private HTTPResponse response;
	private Map> headers;
	private Map> headersLower;
	private Map> cookies;
	private URL url;

	public HttpResponseImpl(Future 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 headers = getHeaders(name);
		return headers == null ? null : headers.get(0);
	}

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

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

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

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

	@Override
	public List 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> buildHeaderMap(HTTPResponse response) {
		Map> headers = new LinkedHashMap>();
		for (HTTPHeader header : response.getHeadersUncombined()) {
			String key = header.getName();
			String value = header.getValue();
			List values = headers.get(key);
			if (values == null) {
				values = new ArrayList();
				headers.put(key, values);
			}
			values.add(value);
		}
		return headers;
	}

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

	private Map> buildCookieMap(Map> headersLower) {
		Map> cookies = new LinkedHashMap>();
		for (String setCookieHeader : getCookieHeaders(headersLower)) {
			List cookieSet = parseCookies(setCookieHeader);
			for (HttpCookie httpCookie : cookieSet) {
				String name = httpCookie.getName();
				List existingCookies = cookies.get(name);
				if (existingCookies == null) {
					existingCookies = new ArrayList();
					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 getCookieHeaders(Map> headers) {
		EList cookieHeaders = Expressive.list();
		cookieHeaders.addItems(headers.get(Header.SetCookie.toLowerCase()));
		cookieHeaders.addItems(headers.get(Header.SetCookie2.toLowerCase()));
		return cookieHeaders;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy