/*
* Copyright 2010 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 com.ning.http.client.providers.netty;
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 org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBufferInputStream;
import org.jboss.netty.buffer.ChannelBuffers;
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;
/**
* Wrapper around the {@link com.ning.http.client.Response} API.
*/
public class NettyResponse 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>();
public NettyResponse(HttpResponseStatus status,
HttpResponseHeaders headers,
Collection<HttpResponseBodyPart> bodyParts) {
this.status = status;
this.headers = headers;
this.bodyParts = bodyParts;
uri = 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;
}
return contentToString(charset);
}
String contentToString(String charset) throws UnsupportedEncodingException {
StringBuilder b = new StringBuilder();
for (HttpResponseBodyPart bp : bodyParts) {
b.append(new String(bp.getBodyPartBytes(), charset));
}
return b.toString();
}
/* @Override */
public InputStream getResponseBodyAsStream() throws IOException {
ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
for (HttpResponseBodyPart bp : bodyParts) {
// Ugly. TODO
// (1) We must remove the downcast,
// (2) we need a CompositeByteArrayInputStream to avoid
// copying the bytes.
if (bp.getClass().isAssignableFrom(ResponseBodyPart.class)) {
buf.writeBytes(bp.getBodyPartBytes());
}
}
return new ChannelBufferInputStream(buf);
}
/* @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;
}
String response = contentToString(charset);
return response.length() <= maxLength ? response : response.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 (status != 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);
}
}