/*
* Copyright 2016 Netflix, Inc.
*
* 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 io.reactivex.netty.protocol.http.client;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMessage;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.cookie.Cookie;
import io.reactivex.netty.channel.Connection;
import io.reactivex.netty.channel.ContentSource;
import io.reactivex.netty.protocol.http.internal.HttpMessageFormatter;
import io.reactivex.netty.protocol.http.sse.ServerSentEvent;
import rx.Observable;
import rx.Observable.Transformer;
import rx.Subscriber;
import java.text.ParseException;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* HTTP response for {@link HttpClient}
*
* <h2>Thread safety</h2>
*
* This object is not thread-safe and must not be used by multiple threads.
*
* <h2>Mutability</h2>
*
* Headers and trailing headers can be mutated for this response.
*/
public abstract class HttpClientResponse<T> {
/**
* Returns the HTTP version for this response.
*
* @return The HTTP version for this response.
*/
public abstract HttpVersion getHttpVersion();
/**
* Returns the HTTP status for this response.
*
* @return The HTTP status for this response.
*/
public abstract HttpResponseStatus getStatus();
/**
* Returns an immutable map of cookie names and cookies contained in this response.
*
* @return An immutable map of cookie names and cookies contained in this response.
*/
public abstract Map<String, Set<Cookie>> getCookies();
/**
* Checks if there is a header with the passed name in this response.
*
* @param name Name of the header.
*
* @return {@code true} if there is a header with the passed name in this response.
*/
public abstract boolean containsHeader(CharSequence name);
/**
* Checks if there is a header with the passed name and value in this response.
*
* @param name Name of the header.
* @param value Value of the header.
* @param ignoreCaseValue {@code true} then the value comparision is done ignoring case.
*
* @return {@code true} if there is a header with the passed name and value in this response.
*/
public abstract boolean containsHeader(CharSequence name, CharSequence value, boolean ignoreCaseValue);
/**
* Returns an iterator over the header entries. Multiple values for the same header appear as separate entries in
* the returned iterator.
*
* @return An iterator over the header entries
*/
public abstract Iterator<Entry<CharSequence, CharSequence>> headerIterator();
/**
* Returns the value of a header with the specified name. If there are more than one values for the specified name,
* the first value is returned.
*
* @param name The name of the header to search
* @return The first header value or {@code null} if there is no such header
*/
public abstract String getHeader(CharSequence name);
/**
* Returns the value of a header with the specified name. If there are more than one values for the specified name,
* the first value is returned.
*
* @param name The name of the header to search
* @param defaultValue Default if the header does not exist.
*
* @return The first header value or {@code defaultValue} if there is no such header
*/
public abstract String getHeader(CharSequence name, String defaultValue);
/**
* Returns the values of headers with the specified name
*
* @param name The name of the headers to search
*
* @return A {@link List} of header values which will be empty if no values are found
*/
public abstract List<String> getAllHeaderValues(CharSequence name);
/**
* Returns the length of the content.
*
* @return the content length
*
* @throws NumberFormatException if the message does not have the {@code "Content-Length"} header or its value is
* not a number.
*/
public abstract long getContentLength();
/**
* Returns the length of the content.
*
* @param defaultValue Default value if the message does not have a {@code "Content-Length"} header or its value is
* not a number
*
* @return the content length or {@code defaultValue} if this message does not have the {@code "Content-Length"}
* header or its value is not a number
*/
public abstract long getContentLength(long defaultValue);
/**
* Returns the date header value with the specified header name. If there are more than one header value for the
* specified header name, the first value is returned.
* The value is parsed as per the
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">HTTP specifications</a> using the format:
* <PRE>"E, dd MMM yyyy HH:mm:ss z"</PRE>
*
* @param name The name of the header to search
*
* @return the header value
*
* @throws ParseException if there is no such header or the header value is not a formatted date
*/
public abstract long getDateHeader(CharSequence name) throws ParseException;
/**
* Returns the date header value with the specified header name. If there are more than one header value for the
* specified header name, the first value is returned.
* The value is parsed as per the
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">HTTP specifications</a> using the format:
* <PRE>"E, dd MMM yyyy HH:mm:ss z"</PRE>
*
* @param name The name of the header to search
* @param defaultValue Default value if there is no header with this name.
*
* @return the header value or {@code defaultValue} if there is no header with this name.
*/
public abstract long getDateHeader(CharSequence name, long defaultValue);
/**
* Returns the value of the {@code "Host"} header.
*/
public abstract String getHostHeader();
/**
* Returns the value of the {@code "Host"} header.
*
* @param defaultValue Default if the header does not exist.
*
* @return The value of the {@code "Host"} header or {@code defaultValue} if there is no such header.
*/
public abstract String getHost(String defaultValue);
/**
* Returns the integer header value with the specified header name. If there are more than one header value for
* the specified header name, the first value is returned.
*
* @param name The name of the header to search
*
* @return the header value
*
* @throws NumberFormatException if there is no such header or the header value is not a number
*/
public abstract int getIntHeader(CharSequence name);
/**
* Returns the integer header value with the specified header name. If there are more than one header value for
* the specified header name, the first value is returned.
*
* @param name The name of the header to search
* @param defaultValue Default if the header does not exist.
*
* @return the header value or the {@code defaultValue} if there is no such header or the header value is not a
* number
*/
public abstract int getIntHeader(CharSequence name, int defaultValue);
/**
* Returns {@code true} if and only if this response has the content-length header set.
*/
public abstract boolean isContentLengthSet();
/**
* Returns {@code true} if and only if the connection can remain open and thus 'kept alive'. This methods respects
* the value of the {@code "Connection"} header first and then the return value of
* {@link HttpVersion#isKeepAliveDefault()}.
*/
public abstract boolean isKeepAlive();
/**
* Checks to see if the transfer encoding of this response is chunked
*
* @return True if transfer encoding is chunked, otherwise false
*/
public abstract boolean isTransferEncodingChunked();
/**
* Returns a new {@link Set} that contains the names of all headers in this response. Note that modifying the
* returned {@link Set} will not affect the state of this response.
*/
public abstract Set<String> getHeaderNames();
/**
* Adds an HTTP header with the passed {@code name} and {@code value} to this response.
*
* @param name Name of the header.
* @param value Value for the header.
*
* @return {@code this}
*/
public abstract HttpClientResponse<T> addHeader(CharSequence name, Object value);
/**
* Adds the passed {@code cookie} to this response.
*
* @param cookie Cookie to add.
*
* @return {@code this}
*/
public abstract HttpClientResponse<T> addCookie(Cookie cookie);
/**
* Adds the passed header as a date value to this response. The date is formatted using netty's
* {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the date as per the
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">HTTP specifications</a> into the format:
* <PRE>"E, dd MMM yyyy HH:mm:ss z"</PRE>
*
* @param name Name of the header.
* @param value Value of the header.
*
* @return {@code this}
*/
public abstract HttpClientResponse<T> addDateHeader(CharSequence name, Date value);
/**
* Adds multiple date values for the passed header name to this response. The date values are formatted using netty's
* {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the date as per the
* <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">HTTP specifications</a> into the format:
*
* <PRE>"E, dd MMM yyyy HH:mm:ss z"</PRE>
*
* @param name Name of the header.
* @param values Values for the header.
*
* @return {@code this}
*/
public abstract HttpClientResponse<T> addDateHeader(CharSequence name, Iterable<Date> values);
/**
* Adds an HTTP header with the passed {@code name} and {@code values} to this response.
*
* @param name Name of the header.
* @param values Values for the header.
*
* @return {@code this}
*/
public abstract HttpClientResponse<T> addHeader(CharSequence name, Iterable<Object> values);
/**
* Overwrites the current value, if any, of the passed header to the passed date value for this response. The date is
* formatted using netty's {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the date
* as per the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">HTTP specifications</a> into
* the format:
* <p/>
* <PRE>"E, dd MMM yyyy HH:mm:ss z"</PRE>
*
* @param name Name of the header.
* @param value Value of the header.
*
* @return {@code this}
*/
public abstract HttpClientResponse<T> setDateHeader(CharSequence name, Date value);
/**
* Overwrites the current value, if any, of the passed header to the passed value for this response.
*
* @param name Name of the header.
* @param value Value of the header.
*
* @return {@code this}
*/
public abstract HttpClientResponse<T> setHeader(CharSequence name, Object value);
/**
* Overwrites the current value, if any, of the passed header to the passed date values for this response. The date
* is formatted using netty's {@link HttpHeaders#addDateHeader(HttpMessage, CharSequence, Date)} which formats the
* date as per the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1">HTTP specifications</a>
* into the format:
* <p/>
* <PRE>"E, dd MMM yyyy HH:mm:ss z"</PRE>
*
* @param name Name of the header.
* @param values Values of the header.
*
* @return {@code this}
*/
public abstract HttpClientResponse<T> setDateHeader(CharSequence name, Iterable<Date> values);
/**
* Overwrites the current value, if any, of the passed header to the passed values for this response.
*
* @param name Name of the header.
* @param values Values of the header.
*
* @return {@code this}
*/
public abstract HttpClientResponse<T> setHeader(CharSequence name, Iterable<Object> values);
/**
* Removes the passed header from this response.
*
* @param name Name of the header.
*
* @return {@code this}
*/
public abstract HttpClientResponse<T> removeHeader(CharSequence name);
/**
* Returns the content as a stream of <a href="http://www.w3.org/TR/eventsource/">Server sent events</a>.
* There can only be one {@link Subscriber} to the returned {@link Observable}, any subsequent subscriptions will
* get an error.
*
* @return Stream of content as {@link ServerSentEvent} messages.
*/
public abstract ContentSource<ServerSentEvent> getContentAsServerSentEvents();
/**
* Returns the content as a stream. There can only be one {@link Subscriber} to the returned {@link Observable}, any
* subsequent subscriptions will get an error.
*
* @return Stream of content.
*/
public abstract ContentSource<T> getContent();
/**
* Marks the content to be discarded. This means that the content can not be read from this response from now.
*
* @return An {@link Observable}, subscription to which will discard the content. This {@code Observable} will
* error/complete when the content errors/completes and unsubscription from here will unsubscribe from the content.
*/
public abstract Observable<Void> discardContent();
/**
* Transforms the type of objects read from the content of this response, using the supplied {@code transformer}.
*
* @return A new instance of {@code HttpClientResponse} with transformed content.
*/
public abstract <TT> HttpClientResponse<TT> transformContent(Transformer<T, TT> transformer);
/**
* Returns the underlying channel on which this response was received.
*
* @return The underlying channel on which this response was received.
*/
public abstract Channel unsafeNettyChannel();
/**
* Returns the underlying connection on which this response was received.
*
* @return The underlying connection on which this response was received.
*/
public abstract Connection<?, ?> unsafeConnection();
public String toString() {
return HttpMessageFormatter.formatResponse(getHttpVersion(), getStatus(), headerIterator());
}
}