/*
* 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.server;
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.cookie.Cookie;
import io.reactivex.netty.channel.AllocatingTransformer;
import io.reactivex.netty.channel.Connection;
import io.reactivex.netty.protocol.http.sse.ServerSentEvent;
import io.reactivex.netty.protocol.http.ws.server.WebSocketHandler;
import io.reactivex.netty.protocol.http.ws.server.WebSocketHandshaker;
import rx.Observable;
import rx.annotations.Experimental;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.Set;
/**
* An HTTP server response.
*
* <h2>Thread safety</h2>
*
* This object is <b>not</b> thread safe and should not be accessed from multiple threads.
*
* @param <C> The type of objects written as the content of the response.
*/
public abstract class HttpServerResponse<C> extends ResponseContentWriter<C> {
protected HttpServerResponse(OnSubscribe<Void> f) {
super(f);
}
/**
* Returns the status of this response. If the status is not explicitly set, the default value is
* {@link HttpResponseStatus#OK}
*
* @return The status of this response.
*/
public abstract HttpResponseStatus getStatus();
/**
* 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 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 java.util.List} of header values which will be empty if no values are found
*/
public abstract List<String> getAllHeaderValues(CharSequence name);
/**
* 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 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 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 HttpServerResponse<C> addHeader(CharSequence name, Object value);
/**
* Adds the passed {@code cookie} to this response.
*
* @param cookie Cookie to add.
*
* @return {@code this}
*/
public abstract HttpServerResponse<C> 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 HttpServerResponse<C> 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 HttpServerResponse<C> 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 HttpServerResponse<C> 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 HttpServerResponse<C> 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 HttpServerResponse<C> 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 HttpServerResponse<C> 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 HttpServerResponse<C> setHeader(CharSequence name, Iterable<Object> values);
/**
* Removes the passed header from this response.
*
* @param name Name of the header.
*
* @return {@code this}
*/
public abstract HttpServerResponse<C> removeHeader(CharSequence name);
/**
* Sets the status for the response.
*
* @param status Status to set.
*
* @return {@code this}
*/
public abstract HttpServerResponse<C> setStatus(HttpResponseStatus status);
/**
* Sets the HTTP transfer encoding to chunked for this response. This delegates to
* {@link HttpHeaders#setTransferEncodingChunked(HttpMessage)}
*
* @return {@code this}
*/
public abstract HttpServerResponse<C> setTransferEncodingChunked();
/**
* This is a performance optimization to <em>not</em> flush the channel on every response send.
*
* <h2>When NOT to use</h2>
* This can be used
* only when the processing for a server is not asynchronous, in which case, one would have to flush the responses
* written explicitly (done on completion of the {@link Observable} written). Something like this:
*
<PRE>
resp.sendHeaders()
.writeStringAndFlushOnEach(Observable.interval(1, TimeUnit.SECONDS))
.map(aLong -> "Interval =>" + aLong)
)
</PRE>
*
* <h2>When to use</h2>
*
* This can be used when the response is written synchronously from a {@link RequestHandler}, something like:
*
<PRE>
response.writeString(Observable.just("Hello world");
</PRE>
*
* When set, this will make the channel to be flushed only when all the requests available on the channel are
* read. Thus, making it possible to do a gathering write for all pipelined requests on a connection. This reduces
* the number of system calls and is helpful in "Hello World" benchmarks.
*/
public abstract HttpServerResponse<C> flushOnlyOnReadComplete();
/**
* Sends the headers for this response when the returned {@code Observable} is subscribed. Alternatively, one can
* continue to write contents using the returned {@link ResponseContentWriter}
*
* @return {@link ResponseContentWriter} which can be subscribed to only send the headers or to write payload.
*/
public abstract ResponseContentWriter<C> sendHeaders();
/**
* Converts this response to enable writing {@link ServerSentEvent}s.
*
* @return This response with writing of {@link ServerSentEvent} enabled.
*/
@Experimental
public abstract HttpServerResponse<ServerSentEvent> transformToServerSentEvents();
/**
* Creates a new {@code HttpServerResponse} instance modifying the content type using the passed {@code transformer}.
*
* @param transformer Transformer to transform the content stream.
*
* @param <CC> New type of the content.
*
* @return A new instance of {@link HttpServerResponse} with the transformed content stream.
*/
public abstract <CC> HttpServerResponse<CC> transformContent(AllocatingTransformer<CC, C> transformer);
/**
* Accepts the upgrade to websockets, if requested and after sending a successful handshake response,
* invokes the passed handler to handle the websocket connection.
*
* If any changes to this response are required for the handshake, they should be done before invoking this method.
*
* @return {@link WebSocketHandshaker} for sending a handshake to the client. Subscription to the handshaker, will
* send the handshake.
*/
public abstract WebSocketHandshaker acceptWebSocketUpgrade(WebSocketHandler handler);
/**
* Disposes this response. If the response is not yet set then this will attempt to send an error response if the
* connection is still open.
*
* @return An {@link Observable}, subscription to which will dispose this response.
*/
public abstract Observable<Void> dispose();
/**
* 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();
}