/*
* 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.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMessage;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.cookie.Cookie;
import io.reactivex.netty.channel.AllocatingTransformer;
import io.reactivex.netty.protocol.http.TrailingHeaders;
import io.reactivex.netty.protocol.http.ws.client.WebSocketRequest;
import rx.Observable;
import rx.annotations.Experimental;
import rx.functions.Func0;
import rx.functions.Func1;
import rx.functions.Func2;
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;
import java.util.concurrent.TimeUnit;
/**
* An HTTP request. An instance of a request can only be created from an associated {@link HttpClient} and can be
* modified after creation.
*
* <h2>Request URIs</h2>
*
* While creating a request, the user should provide a URI to be used for the request. The URI can be relative or
* absolute. If the URI is relative (missing host and port information), the target host and port are inferred from the
* {@link HttpClient} that created the request. If the URI is absolute, the host and port are used from the URI.
*
* <h2>Mutations</h2>
*
* All mutations to this request creates a brand new instance.
* <h2>Trailing headers</h2>
*
* One can write HTTP trailing headers by using
*
* <h2> Executing request</h2>
*
* The request is executed every time {@link HttpClientRequest}, or {@link Observable} returned by
* {@code write*Content} is subscribed and is the only way of executing the request.
*
* @param <I> The type of objects read from the request content.
* @param <O> The type of objects read from the response content.
*/
public abstract class HttpClientRequest<I, O> extends Observable<HttpClientResponse<O>> {
protected HttpClientRequest(OnSubscribe<HttpClientResponse<O>> onSubscribe) {
super(onSubscribe);
}
/**
* Uses the passed {@link Observable} as the source of content for this request.
*
* @param contentSource Content source for the request.
*
* @return An new instance of {@link Observable} which can be subscribed to execute the request.
*/
public abstract Observable<HttpClientResponse<O>> writeContent(Observable<I> contentSource);
/**
* Uses the passed {@link Observable} as the source of content for this request. Every item is written and flushed
* immediately.
*
* @param contentSource Content source for the request.
*
* @return An new instance of {@link Observable} which can be subscribed to execute the request.
*/
public abstract Observable<HttpClientResponse<O>> writeContentAndFlushOnEach(Observable<I> contentSource);
/**
* Uses the passed {@link Observable} as the source of content for this request.
*
* @param contentSource Content source for the request.
* @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. All pending
* writes are flushed, iff this function returns, {@code true}.
*
* @return An new instance of {@link Observable} which can be subscribed to execute the request.
*/
public abstract Observable<HttpClientResponse<O>> writeContent(Observable<I> contentSource,
Func1<I, Boolean> flushSelector);
/**
* Uses the passed {@link Observable} as the source of content for this request. This method provides a way to
* write trailing headers.
*
* A new instance of {@link TrailingHeaders} will be created using the passed {@code trailerFactory} and the passed
* {@code trailerMutator} will be invoked for every item emitted from the content source, giving a chance to modify
* the trailing headers instance.
*
* @param contentSource Content source for the request.
* @param trailerFactory A factory function to create a new {@link TrailingHeaders} per subscription of the content.
* @param trailerMutator A function to mutate the trailing header on each item emitted from the content source.
*
* @return An new instance of {@link Observable} which can be subscribed to execute the request.
*/
@Experimental
public abstract <T extends TrailingHeaders> Observable<HttpClientResponse<O>> writeContent(Observable<I> contentSource,
Func0<T> trailerFactory,
Func2<T, I, T> trailerMutator);
/**
* Uses the passed {@link Observable} as the source of content for this request. This method provides a way to
* write trailing headers.
*
* A new instance of {@link TrailingHeaders} will be created using the passed {@code trailerFactory} and the passed
* {@code trailerMutator} will be invoked for every item emitted from the content source, giving a chance to modify
* the trailing headers instance.
*
* @param contentSource Content source for the request.
* @param trailerFactory A factory function to create a new {@link TrailingHeaders} per subscription of the content.
* @param trailerMutator A function to mutate the trailing header on each item emitted from the content source.
* @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. All pending
* writes are flushed, iff this function returns, {@code true}.
*
* @return An new instance of {@link Observable} which can be subscribed to execute the request.
*/
@Experimental
public abstract <T extends TrailingHeaders> Observable<HttpClientResponse<O>> writeContent(Observable<I> contentSource,
Func0<T> trailerFactory,
Func2<T, I, T> trailerMutator,
Func1<I, Boolean> flushSelector);
/**
* Uses the passed {@link Observable} as the source of content for this request.
*
* @param contentSource Content source for the request.
*
* @return An new instance of {@link Observable} which can be subscribed to execute the request.
*/
public abstract Observable<HttpClientResponse<O>> writeStringContent(Observable<String> contentSource);
/**
* Uses the passed {@link Observable} as the source of content for this request.
*
* @param contentSource Content source for the request.
* @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. All pending
* writes are flushed, iff this function returns, {@code true}.
*
* @return An new instance of {@link Observable} which can be subscribed to execute the request.
*/
public abstract Observable<HttpClientResponse<O>> writeStringContent(Observable<String> contentSource,
Func1<String, Boolean> flushSelector);
/**
* Uses the passed {@link Observable} as the source of content for this request. This method provides a way to
* write trailing headers.
*
* A new instance of {@link TrailingHeaders} will be created using the passed {@code trailerFactory} and the passed
* {@code trailerMutator} will be invoked for every item emitted from the content source, giving a chance to modify
* the trailing headers instance.
*
* @param contentSource Content source for the request.
* @param trailerFactory A factory function to create a new {@link TrailingHeaders} per subscription of the content.
* @param trailerMutator A function to mutate the trailing header on each item emitted from the content source.
*
* @return An new instance of {@link Observable} which can be subscribed to execute the request.
*/
@Experimental
public abstract <T extends TrailingHeaders> Observable<HttpClientResponse<O>> writeStringContent(Observable<String> contentSource,
Func0<T> trailerFactory,
Func2<T, String, T> trailerMutator);
/**
* Uses the passed {@link Observable} as the source of content for this request. This method provides a way to
* write trailing headers.
*
* A new instance of {@link TrailingHeaders} will be created using the passed {@code trailerFactory} and the passed
* {@code trailerMutator} will be invoked for every item emitted from the content source, giving a chance to modify
* the trailing headers instance.
*
* @param contentSource Content source for the request.
* @param trailerFactory A factory function to create a new {@link TrailingHeaders} per subscription of the content.
* @param trailerMutator A function to mutate the trailing header on each item emitted from the content source.
* @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. All pending
* writes are flushed, iff this function returns, {@code true}.
*
* @return An new instance of {@link Observable} which can be subscribed to execute the request.
*/
@Experimental
public abstract <T extends TrailingHeaders> Observable<HttpClientResponse<O>> writeStringContent(Observable<String> contentSource,
Func0<T> trailerFactory,
Func2<T, String, T> trailerMutator,
Func1<String, Boolean> flushSelector);
/**
* Uses the passed {@link Observable} as the source of content for this request.
*
* @param contentSource Content source for the request.
*
* @return An new instance of {@link Observable} which can be subscribed to execute the request.
*/
public abstract Observable<HttpClientResponse<O>> writeBytesContent(Observable<byte[]> contentSource);
/**
* Uses the passed {@link Observable} as the source of content for this request.
*
* @param contentSource Content source for the request.
* @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. All pending
* writes are flushed, iff this function returns, {@code true}.
*
* @return An new instance of {@link Observable} which can be subscribed to execute the request.
*/
public abstract Observable<HttpClientResponse<O>> writeBytesContent(Observable<byte[]> contentSource,
Func1<byte[], Boolean> flushSelector);
/**
* Uses the passed {@link Observable} as the source of content for this request. This method provides a way to
* write trailing headers.
*
* A new instance of {@link TrailingHeaders} will be created using the passed {@code trailerFactory} and the passed
* {@code trailerMutator} will be invoked for every item emitted from the content source, giving a chance to modify
* the trailing headers instance.
*
* @param contentSource Content source for the request.
* @param trailerFactory A factory function to create a new {@link TrailingHeaders} per subscription of the content.
* @param trailerMutator A function to mutate the trailing header on each item emitted from the content source.
*
* @return An new instance of {@link Observable} which can be subscribed to execute the request.
*/
@Experimental
public abstract <T extends TrailingHeaders> Observable<HttpClientResponse<O>> writeBytesContent(Observable<byte[]> contentSource,
Func0<T> trailerFactory,
Func2<T, byte[], T> trailerMutator);
/**
* Uses the passed {@link Observable} as the source of content for this request. This method provides a way to
* write trailing headers.
*
* A new instance of {@link TrailingHeaders} will be created using the passed {@code trailerFactory} and the passed
* {@code trailerMutator} will be invoked for every item emitted from the content source, giving a chance to modify
* the trailing headers instance.
*
* @param contentSource Content source for the request.
* @param trailerFactory A factory function to create a new {@link TrailingHeaders} per subscription of the content.
* @param trailerMutator A function to mutate the trailing header on each item emitted from the content source.
* @param flushSelector A {@link Func1} which is invoked for every item emitted from {@code msgs}. All pending
* writes are flushed, iff this function returns, {@code true}.
*
* @return An new instance of {@link Observable} which can be subscribed to execute the request.
*/
@Experimental
public abstract <T extends TrailingHeaders> Observable<HttpClientResponse<O>> writeBytesContent(Observable<byte[]> contentSource,
Func0<T> trailerFactory,
Func2<T, byte[], T> trailerMutator,
Func1<byte[], Boolean> flushSelector);
/**
* Enables read timeout for the response of the newly created and returned request.
*
* @param timeOut Read timeout duration.
* @param timeUnit Read timeout time unit.
*
* @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> readTimeOut(int timeOut, TimeUnit timeUnit);
/**
* Enables following HTTP redirects for the newly created and returned request.
*
* @param maxRedirects Maximum number of redirects allowed.
*
* @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> followRedirects(int maxRedirects);
/**
* Enables/disables following HTTP redirects for the newly created and returned request.
*
* @param follow {@code true} for enabling redirects, {@code false} to disable.
*
* @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> followRedirects(boolean follow);
/**
* Updates the HTTP method of the request and creates a new {@link HttpClientRequest} instance.
*
* @param method New HTTP method to use.
*
* @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> setMethod(HttpMethod method);
/**
* Updates the URI of the request and creates a new {@link HttpClientRequest} instance.
*
* @param newUri New URI to use.
*
* @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> setUri(String newUri);
/**
* Adds an HTTP header with the passed {@code name} and {@code value} to this request.
*
* @param name Name of the header.
* @param value Value for the header.
*
* @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> addHeader(CharSequence name, Object value);
/**
* Adds the HTTP headers from the passed {@code headers} to this request.
*
* @param headers Map of the headers.
*
* @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> addHeaders(Map<? extends CharSequence, ? extends Iterable<Object>> headers);
/**
* Adds the passed {@code cookie} to this request.
*
* @param cookie Cookie to add.
*
* @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> addCookie(Cookie cookie);
/**
* Adds the passed header as a date value to this request. 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 A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> addDateHeader(CharSequence name, Date value);
/**
* Adds multiple date values for the passed header name to this request. 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:
* <p/>
* <PRE>"E, dd MMM yyyy HH:mm:ss z"</PRE>
*
* @param name Name of the header.
* @param values Values for the header.
*
* @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> addDateHeader(CharSequence name, Iterable<Date> values);
/**
* Adds an HTTP header with the passed {@code name} and {@code values} to this request.
*
* @param name Name of the header.
* @param values Values for the header.
*
* @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> addHeaderValues(CharSequence name, Iterable<Object> values);
/**
* Overwrites the current value, if any, of the passed header to the passed date value for this request. 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 A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> setDateHeader(CharSequence name, Date value);
/**
* Overwrites the current value, if any, of the passed header to the passed value for this request.
*
* @param name Name of the header.
* @param value Value of the header.
*
* @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> setHeader(CharSequence name, Object value);
/**
* Overwrites the current values, if any, of the passed headers for this request.
*
* @param headers Map of the headers.
*
* @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> setHeaders(Map<? extends CharSequence, ? extends Iterable<Object>> headers);
/**
* Overwrites the current value, if any, of the passed header to the passed date values for this request. 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 A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> setDateHeader(CharSequence name, Iterable<Date> values);
/**
* Overwrites the current value, if any, of the passed header to the passed values for this request.
*
* @param name Name of the header.
* @param values Values of the header.
*
* @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> setHeaderValues(CharSequence name, Iterable<Object> values);
/**
* Removes the passed header from this request.
*
* @param name Name of the header.
*
* @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> removeHeader(CharSequence name);
/**
* Sets HTTP Connection header to the appropriate value for HTTP keep-alive. This delegates to {@link
* HttpHeaders#setKeepAlive(HttpMessage, boolean)}
*
* @param keepAlive {@code true} to enable keep alive.
*
* @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> setKeepAlive(boolean keepAlive);
/**
* Sets the HTTP transfer encoding to chunked for this request. This delegates to {@link
* HttpHeaders#setTransferEncodingChunked(HttpMessage)}
*
* @return A new instance of the {@link HttpClientRequest} sharing all existing state from this request.
*/
public abstract HttpClientRequest<I, O> setTransferEncodingChunked();
/**
* Creates a new {@code HttpClientRequest} instance modifying the content type using the passed {@code transformer}.
*
* @param transformer Transformer to transform the content stream.
*
* @param <II> New type of the content.
*
* @return A new instance of {@link HttpClientRequest} with the transformed content stream.
*/
public abstract <II> HttpClientRequest<II, O> transformContent(AllocatingTransformer<II, I> transformer);
/**
* Creates a new {@code HttpClientRequest} instance modifying the content type of the response using the
* passed {@code transformer}.
*
* @param transformer Transformer to transform the content stream.
*
* @param <OO> New type of the content.
*
* @return A new instance of {@link HttpClientRequest} with the transformed response content stream.
*/
public abstract <OO> HttpClientRequest<I, OO> transformResponseContent(Transformer<O, OO> transformer);
/**
* Creates a new {@link WebSocketRequest}, inheriting all configurations from this request, that will request an
* upgrade to websockets from the server.
*
* @return A new {@link WebSocketRequest}.
*/
public abstract WebSocketRequest<O> requestWebSocketUpgrade();
/**
* Checks whether a header with the passed name exists for this request.
*
* @param name Header name.
*
* @return {@code true} if the header exists.
*/
public abstract boolean containsHeader(CharSequence name);
/**
* Checks whether a header with the passed name and value exists for this request.
*
* @param name Header name.
* @param value Value to check.
* @param caseInsensitiveValueMatch If the value has to be matched ignoring case.
*
* @return {@code true} if the header with the passed value exists.
*/
public abstract boolean containsHeaderWithValue(CharSequence name, CharSequence value,
boolean caseInsensitiveValueMatch);
/**
* Fetches the value of a header, if exists, for this request.
*
* @param name Name of the header.
*
* @return The value of the header, if it exists, {@code null} otherwise. If there are multiple values for this
* header, the first value is returned.
*/
public abstract String getHeader(CharSequence name);
/**
* Fetches all values of a header, if exists, for this request.
*
* @param name Name of the header.
*
* @return All values of the header, if it exists, {@code null} otherwise.
*/
public abstract List<String> getAllHeaders(CharSequence name);
/**
* 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 a new {@link Set} that contains the names of all headers in this request. Note that modifying the
* returned {@link Set} will not affect the state of this response.
*/
public abstract Set<String> getHeaderNames();
/**
* Returns the HTTP version of this request.
*
* @return The HTTP version of this request.
*/
public abstract HttpVersion getHttpVersion();
/**
* Returns the HTTP method for this request.
*
* @return The HTTP method for this request.
*/
public abstract HttpMethod getMethod();
/**
* Returns the URI for this request. The returned URI does <em>not</em> contain the scheme, host and port portion of
* the URI.
*
* @return The URI for this request.
*/
public abstract String getUri();
}