/*
* Copyright (c) 2010-2011 Sonatype, Inc. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/
package com.ning.http.client.providers.jdk;
import com.ning.http.client.Cookie;
import com.ning.http.client.FluentCaseInsensitiveStringsMap;
import com.ning.http.client.HttpResponseBodyPart;
import com.ning.http.client.HttpResponseHeaders;
import com.ning.http.client.HttpResponseStatus;
import com.ning.http.client.Response;
import com.ning.http.util.AsyncHttpProviderUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
public class JDKResponse implements Response {
private final static String DEFAULT_CHARSET = "ISO-8859-1";
private final static String HEADERS_NOT_COMPUTED = "Response's headers hasn't been computed by your AsyncHandler.";
private final URI uri;
private final Collection<HttpResponseBodyPart> bodyParts;
private final HttpResponseHeaders headers;
private final HttpResponseStatus status;
private final List<Cookie> cookies = new ArrayList<Cookie>();
private AtomicBoolean contentComputed = new AtomicBoolean(false);
private String content;
public JDKResponse(HttpResponseStatus status,
HttpResponseHeaders headers,
Collection<HttpResponseBodyPart> bodyParts) {
this.bodyParts = bodyParts;
this.headers = headers;
this.status = status;
uri = this.status.getUrl();
}
/* @Override */
public int getStatusCode() {
return status.getStatusCode();
}
/* @Override */
public String getStatusText() {
return status.getStatusText();
}
/* @Override */
public String getResponseBody() throws IOException {
return getResponseBody(DEFAULT_CHARSET);
}
public String getResponseBody(String charset) throws IOException {
String contentType = getContentType();
if (contentType != null && charset == null) {
charset = AsyncHttpProviderUtils.parseCharset(contentType);
}
if (charset == null) {
charset = DEFAULT_CHARSET;
}
if (!contentComputed.get()) {
contentToString(charset);
}
return content;
}
String contentToString(String charset) throws UnsupportedEncodingException {
StringBuilder b = new StringBuilder();
for (HttpResponseBodyPart bp : bodyParts) {
b.append(new String(bp.getBodyPartBytes(), charset));
}
content = b.toString();
return content;
}
/* @Override */
public InputStream getResponseBodyAsStream() throws IOException {
if (contentComputed.get()) {
return new ByteArrayInputStream(content.getBytes(DEFAULT_CHARSET));
}
if (bodyParts.size() > 0) {
return new ByteArrayCollectionInputStream(bodyParts.toArray(new HttpResponseBodyPart[bodyParts.size()]));
} else {
return new ByteArrayInputStream("".getBytes());
}
}
private static class ByteArrayCollectionInputStream extends InputStream {
private final HttpResponseBodyPart[] parts;
private int currentPos = 0;
private int bytePos = -1;
private byte[] active;
private int available = 0;
public ByteArrayCollectionInputStream(HttpResponseBodyPart[] parts) {
this.parts = parts;
active = parts[0].getBodyPartBytes();
computeLength(parts);
}
private void computeLength(HttpResponseBodyPart[] parts){
if (available == 0) {
for (HttpResponseBodyPart p : parts) {
available += p.getBodyPartBytes().length;
}
}
}
@Override
public int available() throws IOException {
return available;
}
@Override
public int read() throws IOException {
if (++bytePos >= active.length) {
// No more bytes, so step to the next array.
if (++currentPos >= parts.length) {
return -1;
}
bytePos = 0;
active = parts[currentPos].getBodyPartBytes();
}
return active[bytePos] & 0xFF;
}
}
/* @Override */
public String getResponseBodyExcerpt(int maxLength) throws IOException {
return getResponseBodyExcerpt(maxLength, DEFAULT_CHARSET);
}
public String getResponseBodyExcerpt(int maxLength, String charset) throws IOException {
String contentType = getContentType();
if (contentType != null && charset == null) {
charset = AsyncHttpProviderUtils.parseCharset(contentType);
}
if (charset == null) {
charset = DEFAULT_CHARSET;
}
if (!contentComputed.get()) {
contentToString(charset == null ? DEFAULT_CHARSET : charset);
}
return content.length() <= maxLength ? content : content.substring(0, maxLength);
}
/* @Override */
public URI getUri() throws MalformedURLException {
return uri;
}
/* @Override */
public String getContentType() {
if (headers == null) {
throw new IllegalStateException(HEADERS_NOT_COMPUTED);
}
return headers.getHeaders().getFirstValue("Content-Type");
}
/* @Override */
public String getHeader(String name) {
if (headers == null) {
throw new IllegalStateException();
}
return headers.getHeaders().getFirstValue(name);
}
/* @Override */
public List<String> getHeaders(String name) {
if (headers == null) {
throw new IllegalStateException(HEADERS_NOT_COMPUTED);
}
return headers.getHeaders().get(name);
}
/* @Override */
public FluentCaseInsensitiveStringsMap getHeaders() {
if (headers == null) {
throw new IllegalStateException(HEADERS_NOT_COMPUTED);
}
return headers.getHeaders();
}
/* @Override */
public boolean isRedirected() {
return (status.getStatusCode() >= 300) && (status.getStatusCode() <= 399);
}
/* @Override */
public List<Cookie> getCookies() {
if (headers == null) {
throw new IllegalStateException(HEADERS_NOT_COMPUTED);
}
if (cookies.isEmpty()) {
for (Map.Entry<String, List<String>> header : headers.getHeaders().entrySet()) {
if (header.getKey().equalsIgnoreCase("Set-Cookie")) {
// TODO: ask for parsed header
List<String> v = header.getValue();
for (String value : v) {
Cookie cookie = AsyncHttpProviderUtils.parseCookie(value);
cookies.add(cookie);
}
}
}
}
return Collections.unmodifiableList(cookies);
}
/**
* {@inheritDoc}
*/
/* @Override */
public boolean hasResponseStatus() {
return (bodyParts != null ? true : false);
}
/**
* {@inheritDoc}
*/
/* @Override */
public boolean hasResponseHeaders() {
return (headers != null ? true : false);
}
/**
* {@inheritDoc}
*/
/* @Override */
public boolean hasResponseBody() {
return (bodyParts != null && bodyParts.size() > 0 ? true : false);
}
}