/*
* Copyright (c) 2008, 2017 Oracle and/or its affiliates. All rights reserved.
* Copyright 2004 The Apache Software Foundation
*
* 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 org.glassfish.grizzly.http.server;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.grizzly.CloseListener;
import org.glassfish.grizzly.CloseType;
import org.glassfish.grizzly.Closeable;
import org.glassfish.grizzly.CompletionHandler;
import org.glassfish.grizzly.GenericCloseListener;
import org.glassfish.grizzly.Grizzly;
import org.glassfish.grizzly.filterchain.FilterChainContext;
import org.glassfish.grizzly.http.Cookie;
import org.glassfish.grizzly.http.Cookies;
import org.glassfish.grizzly.http.HttpContext;
import org.glassfish.grizzly.http.HttpResponsePacket;
import org.glassfish.grizzly.http.Protocol;
import org.glassfish.grizzly.http.io.InputBuffer;
import org.glassfish.grizzly.http.io.NIOOutputStream;
import org.glassfish.grizzly.http.io.NIOWriter;
import org.glassfish.grizzly.http.io.OutputBuffer;
import org.glassfish.grizzly.http.server.io.ServerOutputBuffer;
import org.glassfish.grizzly.http.server.util.Globals;
import org.glassfish.grizzly.http.server.util.HtmlHelper;
import org.glassfish.grizzly.http.util.CharChunk;
import static org.glassfish.grizzly.http.util.Constants.*;
import org.glassfish.grizzly.http.util.ContentType;
import org.glassfish.grizzly.http.util.CookieSerializerUtils;
import org.glassfish.grizzly.http.util.FastHttpDateFormat;
import org.glassfish.grizzly.http.util.Header;
import org.glassfish.grizzly.http.util.HeaderValue;
import org.glassfish.grizzly.http.util.HttpRequestURIDecoder;
import org.glassfish.grizzly.http.util.HttpStatus;
import org.glassfish.grizzly.http.util.MimeHeaders;
import org.glassfish.grizzly.http.util.UEncoder;
import org.glassfish.grizzly.localization.LogMessages;
import org.glassfish.grizzly.utils.DelayedExecutor;
import org.glassfish.grizzly.utils.DelayedExecutor.DelayQueue;
Wrapper object for the Coyote response.
Author: Remy Maucherat, Craig R. McClanahan Version: $Revision: 1.2 $ $Date: 2006/11/02 20:01:44 $
/**
* Wrapper object for the Coyote response.
*
* @author Remy Maucherat
* @author Craig R. McClanahan
* @version $Revision: 1.2 $ $Date: 2006/11/02 20:01:44 $
*/
public class Response {
enum SuspendState {
NONE, SUSPENDED, RESUMING, RESUMED, CANCELLING, CANCELLED
}
private static final Logger LOGGER = Grizzly.logger(Response.class);
static DelayQueue<SuspendTimeout> createDelayQueue(
final DelayedExecutor delayedExecutor) {
return delayedExecutor.createDelayQueue(new DelayQueueWorker(),
new DelayQueueResolver());
}
// ----------------------------------------------------------- Constructors
protected Response() {
urlEncoder.addSafeCharacter('/');
}
// ----------------------------------------------------- Instance Variables
private boolean cacheEnabled = false;
Default locale as mandated by the spec.
/**
* Default locale as mandated by the spec.
*/
private static final Locale DEFAULT_LOCALE = Locale.getDefault();
private static final String HTTP_RESPONSE_DATE_HEADER =
"EEE, dd MMM yyyy HH:mm:ss zzz";
The date format we will use for creating date headers.
/**
* The date format we will use for creating date headers.
*/
protected SimpleDateFormat format = null;
Descriptive information about this Response implementation.
/**
* Descriptive information about this Response implementation.
*/
protected static final String info =
"org.glassfish.grizzly.http.server.Response/2.0";
// ------------------------------------------------------------- Properties
The request with which this response is associated.
/**
* The request with which this response is associated.
*/
protected Request request = null;
Coyote response.
/**
* Coyote response.
*/
protected HttpResponsePacket response;
Grizzly FilterChain
context, related to this HTTP request/response /**
* Grizzly {@link org.glassfish.grizzly.filterchain.FilterChain} context,
* related to this HTTP request/response
*/
protected FilterChainContext ctx;
Grizzly HttpContext
associated with the current Request/Response processing. /**
* Grizzly {@link HttpContext} associated with the current Request/Response
* processing.
*/
protected HttpContext httpContext;
The associated output buffer.
/**
* The associated output buffer.
*/
protected final ServerOutputBuffer outputBuffer = new ServerOutputBuffer();
The associated output stream.
/**
* The associated output stream.
*/
private final NIOOutputStreamImpl outputStream = new NIOOutputStreamImpl();
The associated writer.
/**
* The associated writer.
*/
private final NIOWriterImpl writer = new NIOWriterImpl();
The application commit flag.
/**
* The application commit flag.
*/
protected boolean appCommitted = false;
The error flag.
/**
* The error flag.
*/
protected boolean error = false;
Using output stream flag.
/**
* Using output stream flag.
*/
protected boolean usingOutputStream = false;
Using writer flag.
/**
* Using writer flag.
*/
protected boolean usingWriter = false;
URL encoder.
/**
* URL encoder.
*/
protected final UEncoder urlEncoder = new UEncoder();
Recyclable buffer to hold the redirect URL.
/**
* Recyclable buffer to hold the redirect URL.
*/
protected final CharChunk redirectURLCC = new CharChunk();
protected DelayedExecutor.DelayQueue<SuspendTimeout> delayQueue;
SuspendState suspendState = SuspendState.NONE;
private final SuspendedContextImpl suspendedContext = new SuspendedContextImpl();
private SuspendStatus suspendStatus;
private boolean sendFileEnabled;
private ErrorPageGenerator errorPageGenerator;
// --------------------------------------------------------- Public Methods
public void initialize(final Request request,
final HttpResponsePacket response,
final FilterChainContext ctx,
final DelayedExecutor.DelayQueue<SuspendTimeout> delayQueue,
final HttpServerFilter serverFilter) {
this.request = request;
this.response = response;
sendFileEnabled = ((serverFilter != null)
&& serverFilter.getConfiguration().isSendFileEnabled());
outputBuffer.initialize(this, ctx);
this.ctx = ctx;
this.httpContext = HttpContext.get(ctx);
this.delayQueue = delayQueue;
}
SuspendStatus initSuspendStatus() {
suspendStatus = SuspendStatus.create();
return suspendStatus;
}
Return the Request with which this Response is associated.
/**
* Return the Request with which this Response is associated.
*/
public Request getRequest() {
return request;
}
Get the HttpResponsePacket
. /**
* Get the {@link HttpResponsePacket}.
*/
public HttpResponsePacket getResponse() {
return response;
}
Release all object references, and initialize instance variables, in
preparation for reuse of this object.
/**
* Release all object references, and initialize instance variables, in
* preparation for reuse of this object.
*/
protected void recycle() {
delayQueue = null;
outputBuffer.recycle();
outputStream.recycle();
writer.recycle();
usingOutputStream = false;
usingWriter = false;
appCommitted = false;
error = false;
errorPageGenerator = null;
request = null;
response.recycle();
sendFileEnabled = false;
response = null;
ctx = null;
suspendState = SuspendState.NONE;
cacheEnabled = false;
}
// ------------------------------------------------------- Response Methods
Set the supplier of trailer headers.
The supplier will be called within the scope of whatever thread/call
causes the response content to be completed. Typically this will
be any thread calling close() on the output stream or writer.
The trailers that run afoul of the provisions of section 4.1.2 of
RFC 7230 are ignored.
Params: - trailerSupplier – the supplier of trailer headers
Throws: - IllegalStateException – if it is invoked after the response has
has been committed, or trailers cannot be supported given
the current protocol and/or configuration (chunked transfer
encoding disabled in HTTP/1.1 as an example).
Since: 2.4.0
/**
* Set the supplier of trailer headers.
* The supplier will be called within the scope of whatever thread/call
* causes the response content to be completed. Typically this will
* be any thread calling close() on the output stream or writer.
*
* The trailers that run afoul of the provisions of section 4.1.2 of
* RFC 7230 are ignored.
*
* @param trailerSupplier the supplier of trailer headers
*
* @throws IllegalStateException if it is invoked after the response has
* has been committed, or trailers cannot be supported given
* the current protocol and/or configuration (chunked transfer
* encoding disabled in HTTP/1.1 as an example).
*
* @since 2.4.0
*/
public void setTrailers(Supplier<Map<String, String>> trailerSupplier) {
if (isCommitted()) {
throw new IllegalStateException("Response has already been committed.");
}
final Protocol protocol = request.getProtocol();
if (protocol.equals(Protocol.HTTP_0_9) || protocol.equals(Protocol.HTTP_1_0)) {
throw new IllegalStateException("Trailers not supported by response protocol version " + protocol);
}
if (protocol.equals(Protocol.HTTP_1_1)) {
if (!response.isChunkingAllowed()) {
throw new IllegalStateException("Chunked transfer-encoding disabled.");
}
response.setChunked(true);
}
outputBuffer.setTrailers(trailerSupplier);
}
Returns: the trailers supplier, if any. Since: 2.4.0
/**
* @return the trailers supplier, if any.
*
* @since 2.4.0
*/
public Supplier<Map<String, String>> getTrailers() {
return outputBuffer.getTrailers();
}
Encode the session identifier associated with this response
into the specified URL, if necessary.
Params: - url – URL to be encoded
/**
* Encode the session identifier associated with this response
* into the specified URL, if necessary.
*
* @param url URL to be encoded
*/
public String encodeURL(String url) {
String absolute = toAbsolute(url, false);
if (isEncodeable(absolute)) {
// W3c spec clearly said
if (url.equalsIgnoreCase("")){
url = absolute;
}
return toEncoded(url,request.getSession().getIdInternal());
} else {
return (url);
}
}
Encode the session identifier associated with this response
into the specified redirect URL, if necessary.
Params: - url – URL to be encoded
/**
* Encode the session identifier associated with this response
* into the specified redirect URL, if necessary.
*
* @param url URL to be encoded
*/
public String encodeRedirectURL(String url) {
if (isEncodeable(toAbsolute(url, false))) {
return toEncoded(url, request.getSession().getIdInternal());
} else {
return url;
}
}
Return true if the specified URL should be encoded with
a session identifier. This will be true if all of the following
conditions are met:
- The request we are responding to asked for a valid session
- The requested session ID was not received via a cookie
- The specified URL points back to somewhere within the web
application that is responding to this request
Params: - location – Absolute URL to be validated
/**
* Return <tt>true</tt> if the specified URL should be encoded with
* a session identifier. This will be true if all of the following
* conditions are met:
* <ul>
* <li>The request we are responding to asked for a valid session
* <li>The requested session ID was not received via a cookie
* <li>The specified URL points back to somewhere within the web
* application that is responding to this request
* </ul>
*
* @param location Absolute URL to be validated
*/
protected boolean isEncodeable(final String location) {
if (location == null)
return (false);
// Is this an intra-document reference?
if (location.startsWith("#"))
return (false);
final Session session = request.getSession(false);
return (session != null
&& !request.isRequestedSessionIdFromCookie()
&& doIsEncodeable(request, session, location));
}
private static boolean doIsEncodeable(Request request, Session session,
String location){
// Is this a valid absolute URL?
URL url;
try {
url = new URL(location);
} catch (MalformedURLException e) {
return (false);
}
// Does this URL match down to (and including) the context path?
if (!request.getScheme().equalsIgnoreCase(url.getProtocol()))
return (false);
if (!request.getServerName().equalsIgnoreCase(url.getHost()))
return (false);
int serverPort = request.getServerPort();
if (serverPort == -1) {
if ("https".equals(request.getScheme()))
serverPort = 443;
else
serverPort = 80;
}
int urlPort = url.getPort();
if (urlPort == -1) {
if ("https".equals(url.getProtocol()))
urlPort = 443;
else
urlPort = 80;
}
if (serverPort != urlPort)
return (false);
String contextPath = "/";
String file = url.getFile();
if ((file == null) || !file.startsWith(contextPath)) {
return (false);
}
if (file.contains(";jsessionid=" + session.getIdInternal())) {
return (false);
}
// This URL belongs to our web application, so it is encodeable
return (true);
}
Return descriptive information about this Response implementation and
the corresponding version number, in the format
<description>/<version>
.
/**
* Return descriptive information about this Response implementation and
* the corresponding version number, in the format
* <code><description>/<version></code>.
*/
public String getInfo() {
return info;
}
Set the error flag.
/**
* Set the error flag.
*/
public void setError() {
error = true;
}
Error flag accessor.
/**
* Error flag accessor.
*/
public boolean isError() {
return error;
}
Returns: the ErrorPageGenerator
to be used by sendError(int)
or sendError(int, String)
.
/**
* @return the {@link ErrorPageGenerator} to be used by
* {@link #sendError(int)} or {@link #sendError(int, java.lang.String)}.
*/
public ErrorPageGenerator getErrorPageGenerator() {
return errorPageGenerator;
}
Params: - errorPageGenerator – the custom
ErrorPageGenerator
.
/**
* Sets the {@link ErrorPageGenerator} to be used by
* {@link #sendError(int)} or {@link #sendError(int, java.lang.String)}.
*
* @param errorPageGenerator the custom {@link ErrorPageGenerator}.
*/
public void setErrorPageGenerator(ErrorPageGenerator errorPageGenerator) {
this.errorPageGenerator = errorPageGenerator;
}
// BEGIN S1AS 4878272
Sets detail error message.
Params: - message – detail error message
/**
* Sets detail error message.
*
* @param message detail error message
*/
public void setDetailMessage(String message) {
checkResponse();
response.setReasonPhrase(message);
}
Gets detail error message.
Returns: the detail error message
/**
* Gets detail error message.
*
* @return the detail error message
*/
@SuppressWarnings("unused")
public String getDetailMessage() {
checkResponse();
return response.getReasonPhrase();
}
// END S1AS 4878272
Perform whatever actions are required to flush and close the output
stream or writer, in a single operation.
/**
* Perform whatever actions are required to flush and close the output
* stream or writer, in a single operation.
*/
public void finish() {
// Writing leftover bytes
try {
outputBuffer.endRequest();
} catch (IOException e) {
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST,
LogMessages.WARNING_GRIZZLY_HTTP_SERVER_RESPONSE_FINISH_ERROR(), e);
}
} catch (Throwable t) {
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING,
LogMessages.WARNING_GRIZZLY_HTTP_SERVER_RESPONSE_FINISH_ERROR(), t);
}
}
}
Return the content length that was set or calculated for this Response.
/**
* Return the content length that was set or calculated for this Response.
*/
public int getContentLength() {
checkResponse();
return (int) response.getContentLength();
}
Return the content length that was set or calculated for this Response.
/**
* Return the content length that was set or calculated for this Response.
*/
public long getContentLengthLong() {
checkResponse();
return response.getContentLength();
}
Return the content type that was set or calculated for this response,
or null
if no content type was set.
/**
* Return the content type that was set or calculated for this response,
* or <code>null</code> if no content type was set.
*/
public String getContentType() {
checkResponse();
return response.getContentType();
}
// ------------------------------------------------ ServletResponse Methods
Return the actual buffer size used for this Response.
/**
* Return the actual buffer size used for this Response.
*/
public int getBufferSize() {
return outputBuffer.getBufferSize();
}
Return the character encoding used for this Response.
/**
* Return the character encoding used for this Response.
*/
public String getCharacterEncoding() {
checkResponse();
final String characterEncoding = response.getCharacterEncoding();
if (characterEncoding == null) {
return DEFAULT_HTTP_CHARACTER_ENCODING;
}
return characterEncoding;
}
/*
* Overrides the name of the character encoding used in the body
* of the request. This method must be called prior to reading
* request parameters or reading input using getReader().
*
* @param charset String containing the name of the chararacter encoding.
*/
public void setCharacterEncoding(String charset) {
checkResponse();
if (isCommitted())
return;
// Ignore any call made after the getWriter has been invoked
// The default should be used
if (usingWriter)
return;
response.setCharacterEncoding(charset);
}
Create and return a ServletOutputStream to write the content
associated with this Response.
/**
* Create and return a ServletOutputStream to write the content
* associated with this Response.
*/
public NIOOutputStream createOutputStream() {
outputStream.setOutputBuffer(outputBuffer);
return outputStream;
}
Return the NIOOutputStream
associated with this Response
. This NIOOutputStream
will write content in a non-blocking manner.
Throws: - IllegalStateException – if
getWriter()
or getNIOWriter()
were already invoked.
/**
* <p>
* Return the {@link NIOOutputStream} associated with this {@link Response}.
* This {@link NIOOutputStream} will write content in a non-blocking manner.
* </p>
*
* @throws IllegalStateException if {@link #getWriter()} or {@link #getNIOWriter()}
* were already invoked.
*/
public NIOOutputStream getNIOOutputStream() {
if (usingWriter)
throw new IllegalStateException("Illegal attempt to call getOutputStream() after getWriter() has already been called.");
usingOutputStream = true;
outputStream.setOutputBuffer(outputBuffer);
return outputStream;
}
Return the OutputStream
associated with this Response
.
By default the returned NIOOutputStream
will work as blocking OutputStream
, but it will be possible to call OutputSink.canWrite()
or OutputSink.notifyCanWrite(WriteHandler)
to avoid blocking. Throws: - IllegalStateException – if
getWriter()
or getNIOWriter()
were already invoked.
Returns: the NIOOutputStream
associated with this Response
. Since: 2.1.2
/**
* <p>
* Return the {@link OutputStream} associated with this {@link Response}.
* </p>
*
* By default the returned {@link NIOOutputStream} will work as blocking
* {@link java.io.OutputStream}, but it will be possible to call {@link NIOOutputStream#canWrite()} or
* {@link NIOOutputStream#notifyCanWrite(org.glassfish.grizzly.WriteHandler)} to
* avoid blocking.
*
* @return the {@link NIOOutputStream} associated with this {@link Response}.
*
* @throws IllegalStateException if {@link #getWriter()} or {@link #getNIOWriter()}
* were already invoked.
*
* @since 2.1.2
*/
public OutputStream getOutputStream() {
return getNIOOutputStream();
}
Return the Locale assigned to this response.
/**
* Return the Locale assigned to this response.
*/
public Locale getLocale() {
checkResponse();
Locale locale = response.getLocale();
if (locale == null) {
locale = DEFAULT_LOCALE;
response.setLocale(locale);
}
return locale;
}
Return the NIOWriter
associated with this Response
.
By default the returned NIOWriter
will work as blocking Writer
, but it will be possible to call OutputSink.canWrite()
or OutputSink.notifyCanWrite(WriteHandler)
to avoid blocking. Throws: - IllegalStateException – if
getOutputStream()
or getNIOOutputStream()
were already invoked.
/**
* <p>
* Return the {@link NIOWriter} associated with this {@link Response}.
* </p>
*
* By default the returned {@link NIOWriter} will work as blocking
* {@link java.io.Writer}, but it will be possible to call {@link NIOWriter#canWrite()} or
* {@link NIOWriter#notifyCanWrite(org.glassfish.grizzly.WriteHandler)} to
* avoid blocking.
*
* @throws IllegalStateException if {@link #getOutputStream()} or
* {@link #getNIOOutputStream()} were already invoked.
*/
public Writer getWriter() {
return getNIOWriter();
}
Return the NIOWriter
associated with this Response
. The NIOWriter
will write content in a non-blocking manner.
Throws: - IllegalStateException – if
getOutputStream()
or getNIOOutputStream()
were already invoked.
Returns: the NIOWriter
associated with this Response
. Since: 2.1.2
/**
* <p>
* Return the {@link NIOWriter} associated with this {@link Response}.
* The {@link NIOWriter} will write content in a non-blocking manner.
* </p>
*
* @return the {@link NIOWriter} associated with this {@link Response}.
*
* @throws IllegalStateException if {@link #getOutputStream()} or
* {@link #getNIOOutputStream()} were already invoked.
*
* @since 2.1.2
*/
public NIOWriter getNIOWriter() {
if (usingOutputStream)
throw new IllegalStateException("Illegal attempt to call getWriter() after getOutputStream() has already been called.");
/*
* If the response's character encoding has not been specified as
* described in <code>getCharacterEncoding</code> (i.e., the method
* just returns the default value <code>ISO-8859-1</code>),
* <code>getWriter</code> updates it to <code>ISO-8859-1</code>
* (with the effect that a subsequent call to getContentType() will
* include a charset=ISO-8859-1 component which will also be
* reflected in the Content-Type response header, thereby satisfying
* the Servlet spec requirement that containers must communicate the
* character encoding used for the servlet response's writer to the
* client).
*/
setCharacterEncoding(getCharacterEncoding());
usingWriter = true;
outputBuffer.prepareCharacterEncoder();
writer.setOutputBuffer(outputBuffer);
return writer;
}
Has the output of this response already been committed?
/**
* Has the output of this response already been committed?
*/
public boolean isCommitted() {
checkResponse();
return response.isCommitted();
}
Flush the current buffered content to the network.
Throws: - IOException – if an occur occurs flushing to the wire.
/**
* Flush the current buffered content to the network.
* @throws IOException if an occur occurs flushing to the wire.
*/
public void flush() throws IOException {
outputBuffer.flush();
}
Returns: the OutputBuffer
associated with this Response
.
/**
* @return the {@link OutputBuffer} associated with this
* <code>Response</code>.
*/
public OutputBuffer getOutputBuffer() {
return outputBuffer;
}
Clears any data that exists in the buffer as well as the status code
and headers.
Throws: - IllegalStateException – if this response has already
been committed
/**
* Clears any data that exists in the buffer as well as the status code
* and headers.
*
* @exception IllegalStateException if this response has already
* been committed
*/
public void reset() {
checkResponse();
if (isCommitted()) {
throw new IllegalStateException();
}
response.getHeaders().clear();
response.setContentLanguage(null);
if (response.getContentLength() > 0) {
response.setContentLengthLong(-1L);
}
response.setCharacterEncoding(null);
response.setStatus(null);
response.setContentType((String) null);
response.setLocale(null);
outputBuffer.reset();
usingWriter = false;
usingOutputStream = false;
}
Reset the data buffer but not any status or header information.
Throws: - IllegalStateException – if the response has already
been committed
/**
* Reset the data buffer but not any status or header information.
*
* @exception IllegalStateException if the response has already
* been committed
*/
public void resetBuffer() {
resetBuffer(false);
}
Reset the data buffer and the using Writer/Stream flags but not any
status or header information.
Params: - resetWriterStreamFlags –
true
if the internal
usingWriter
, usingOutputStream
,
isCharacterEncodingSet
flags should also be reset
Throws: - IllegalStateException – if the response has already
been committed
/**
* Reset the data buffer and the using Writer/Stream flags but not any
* status or header information.
*
* @param resetWriterStreamFlags <code>true</code> if the internal
* <code>usingWriter</code>, <code>usingOutputStream</code>,
* <code>isCharacterEncodingSet</code> flags should also be reset
*
* @exception IllegalStateException if the response has already
* been committed
*/
public void resetBuffer(final boolean resetWriterStreamFlags) {
if (isCommitted())
throw new IllegalStateException("Cannot reset buffer after response has been committed.");
outputBuffer.reset();
if (resetWriterStreamFlags) {
usingOutputStream = false;
usingWriter = false;
}
}
Set the buffer size to be used for this Response.
Params: - size – The new buffer size
Throws: - IllegalStateException – if this method is called after
output has been committed for this response
/**
* Set the buffer size to be used for this Response.
*
* @param size The new buffer size
*
* @exception IllegalStateException if this method is called after
* output has been committed for this response
*/
public void setBufferSize(final int size) {
if (isCommitted()) {
throw new IllegalStateException("Unable to change buffer size as the response has been committed");
}
outputBuffer.setBufferSize(size);
}
Set the content length (in bytes) for this Response.
If the length
argument is negative - then HttpPacket
content-length value will be reset to -1 and
Content-Length header (if present) will be removed.
Params: - length – The new content length
/**
* Set the content length (in bytes) for this Response.
*
* If the <code>length</code> argument is negative - then {@link org.glassfish.grizzly.http.HttpPacket}
* content-length value will be reset to <tt>-1</tt> and
* <tt>Content-Length</tt> header (if present) will be removed.
*
* @param length The new content length
*/
public void setContentLengthLong(final long length) {
checkResponse();
if (isCommitted())
return;
if (usingWriter)
return;
response.setContentLengthLong(length);
}
Set the content length (in bytes) for this Response.
If the length
argument is negative - then HttpPacket
content-length value will be reset to -1 and
Content-Length header (if present) will be removed.
Params: - length – The new content length
/**
* Set the content length (in bytes) for this Response.
*
* If the <code>length</code> argument is negative - then {@link org.glassfish.grizzly.http.HttpPacket}
* content-length value will be reset to <tt>-1</tt> and
* <tt>Content-Length</tt> header (if present) will be removed.
*
* @param length The new content length
*/
public void setContentLength(final int length) {
setContentLengthLong(length);
}
Set the content type for this Response.
Params: - type – The new content type
/**
* Set the content type for this Response.
*
* @param type The new content type
*/
public void setContentType(String type) {
checkResponse();
if (isCommitted())
return;
// Ignore charset if getWriter() has already been called
if (usingWriter) {
if (type != null) {
int index = type.indexOf(";");
if (index != -1) {
type = type.substring(0, index);
}
}
}
response.setContentType(type);
}
Set the content type for this Response.
Params: - type – The new content type
/**
* Set the content type for this Response.
*
* @param type The new content type
*/
public void setContentType(final ContentType type) {
checkResponse();
if (isCommitted())
return;
if (type == null) {
response.setContentType((String) null);
return;
}
if (!usingWriter) {
response.setContentType(type);
} else {
// Ignore charset if getWriter() has already been called
response.setContentType(type.getMimeType());
}
}
Set the Locale that is appropriate for this response, including
setting the appropriate character encoding.
Params: - locale – The new locale
/**
* Set the Locale that is appropriate for this response, including
* setting the appropriate character encoding.
*
* @param locale The new locale
*/
public void setLocale(final Locale locale) {
checkResponse();
if (isCommitted())
return;
response.setLocale(locale);
}
// --------------------------------------------------- HttpResponsePacket Methods
Return an array of all cookies set for this response, or
a zero-length array if no cookies have been set.
/**
* Return an array of all cookies set for this response, or
* a zero-length array if no cookies have been set.
*/
public Cookie[] getCookies() {
final Cookies cookies = new Cookies();
cookies.setHeaders(response.getHeaders(), false);
return cookies.get();
}
Return the value for the specified header, or null
if this
header has not been set. If more than one value was added for this
name, only the first is returned; use getHeaderValues() to retrieve all
of them.
Params: - name – Header name to look up
/**
* Return the value for the specified header, or <code>null</code> if this
* header has not been set. If more than one value was added for this
* name, only the first is returned; use getHeaderValues() to retrieve all
* of them.
*
* @param name Header name to look up
*/
public String getHeader(String name) {
checkResponse();
return response.getHeader(name);
}
Return an array of all the header names set for this response, or
a zero-length array if no headers have been set.
/**
* Return an array of all the header names set for this response, or
* a zero-length array if no headers have been set.
*/
public String[] getHeaderNames() {
checkResponse();
MimeHeaders headers = response.getHeaders();
int n = headers.size();
String[] result = new String[n];
for (int i = 0; i < n; i++) {
result[i] = headers.getName(i).toString();
}
return result;
}
Return an array of all the header values associated with the
specified header name, or an zero-length array if there are no such
header values.
Params: - name – Header name to look up
/**
* Return an array of all the header values associated with the
* specified header name, or an zero-length array if there are no such
* header values.
*
* @param name Header name to look up
*/
public String[] getHeaderValues(final String name) {
checkResponse();
final Collection<String> result = new LinkedList<>();
for (final String headerValue : response.getHeaders().values(name)) {
result.add(headerValue);
}
return result.toArray(new String[result.size()]);
}
Return the error message that was set with sendError()
for this Response.
/**
* Return the error message that was set with <code>sendError()</code>
* for this Response.
*/
public String getMessage() {
checkResponse();
return response.getReasonPhrase();
}
Return the HTTP status code associated with this Response.
/**
* Return the HTTP status code associated with this Response.
*/
public int getStatus() {
checkResponse();
return response.getStatus();
}
Reset this response, and specify the values for the HTTP status code
and corresponding message.
Throws: - IllegalStateException – if this response has already been
committed
/**
* Reset this response, and specify the values for the HTTP status code
* and corresponding message.
*
* @exception IllegalStateException if this response has already been
* committed
*/
public void reset(final int status, final String message) {
reset();
setStatus(status, message);
}
// -------------------------------------------- HttpServletResponse Methods
Add the specified Cookie to those that will be included with
this Response.
Params: - cookie – Cookie to be added
/**
* Add the specified Cookie to those that will be included with
* this Response.
*
* @param cookie Cookie to be added
*/
@SuppressWarnings({"unchecked"})
public void addCookie(final Cookie cookie) {
if (isCommitted())
return;
final StringBuilder sb = new StringBuilder();
//web application code can receive a IllegalArgumentException
//from the appendCookieValue invokation
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction() {
@Override
public Object run() {
CookieSerializerUtils.serializeServerCookie(sb, cookie);
return null;
}
});
} else {
CookieSerializerUtils.serializeServerCookie(sb, cookie);
}
// if we reached here, no exception, cookie is valid
// the header name is Set-Cookie for both "old" and v.1 ( RFC2109 )
// RFC2965 is not supported by browsers and the Servlet spec
// asks for 2109.
addHeader(Header.SetCookie, sb.toString());
}
Special method for adding a session cookie as we should be overriding
any previous
/**
* Special method for adding a session cookie as we should be overriding
* any previous
*/
protected void addSessionCookieInternal(final Cookie cookie) {
if (isCommitted())
return;
String name = cookie.getName();
final String headername = Header.SetCookie.toString();
final String startsWith = name + "=";
final StringBuilder sb = new StringBuilder();
//web application code can receive a IllegalArgumentException
//from the appendCookieValue invokation
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
CookieSerializerUtils.serializeServerCookie(sb, cookie);
return null;
}
});
} else {
CookieSerializerUtils.serializeServerCookie(sb, cookie);
}
final String cookieString = sb.toString();
boolean set = false;
MimeHeaders headers = response.getHeaders();
int n = headers.size();
for (int i = 0; i < n; i++) {
if (headers.getName(i).toString().equals(headername)) {
if (headers.getValue(i).toString().startsWith(startsWith)) {
headers.getValue(i).setString(cookieString);
set = true;
}
}
}
if (!set) {
addHeader(headername, cookieString);
}
}
Removes any Set-Cookie response headers whose value contains the
string "JSESSIONID=" or "JSESSIONIDSSO="
/**
* Removes any Set-Cookie response headers whose value contains the
* string "JSESSIONID=" or "JSESSIONIDSSO="
*/
@SuppressWarnings("unused")
protected void removeSessionCookies() {
final String sessionCookieName = request.getSessionCookieName();
final String pattern = sessionCookieName != null
? '^' + sessionCookieName + "(?:SSO)?=.*"
: Globals.SESSION_COOKIE_PATTERN;
response.getHeaders().removeHeaderMatches(Header.SetCookie, pattern);
}
Add the specified date header to the specified value.
Params: - name – Name of the header to set
- value – Date value to be set
/**
* Add the specified date header to the specified value.
*
* @param name Name of the header to set
* @param value Date value to be set
*/
public void addDateHeader(final String name, final long value) {
if (isCommitted())
return;
if (format == null) {
format = new SimpleDateFormat(HTTP_RESPONSE_DATE_HEADER,
Locale.US);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
}
addHeader(name, FastHttpDateFormat.formatDate(value, format));
}
Add the specified date header to the specified value.
Params: - header – the
Header
to set - value – Date value to be set
Since: 2.1.2
/**
* Add the specified date header to the specified value.
*
* @param header the {@link Header} to set
* @param value Date value to be set
*
* @since 2.1.2
*/
public void addDateHeader(final Header header, final long value) {
if (isCommitted())
return;
if (format == null) {
format = new SimpleDateFormat(HTTP_RESPONSE_DATE_HEADER,
Locale.US);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
}
addHeader(header, FastHttpDateFormat.formatDate(value, format));
}
Add the specified header to the specified value.
Params: - name – Name of the header to set
- value – Value to be set
/**
* Add the specified header to the specified value.
*
* @param name Name of the header to set
* @param value Value to be set
*/
public void addHeader(final String name, final String value) {
checkResponse();
if (isCommitted())
return;
response.addHeader(name, value);
}
Add the specified header to the specified value.
Params: - name – Name of the header to set
- value – Value to be set
Since: 2.3.8
/**
* Add the specified header to the specified value.
*
* @param name Name of the header to set
* @param value Value to be set
*
* @since 2.3.8
*/
public void addHeader(final String name, final HeaderValue value) {
checkResponse();
if (isCommitted())
return;
response.addHeader(name, value);
}
Add the specified header to the specified value.
Params: - header – the
Header
to set - value – Value to be set
Since: 2.1.2
/**
* Add the specified header to the specified value.
*
* @param header the {@link Header} to set
* @param value Value to be set
*
* @since 2.1.2
*/
public void addHeader(final Header header, final String value) {
checkResponse();
if (isCommitted())
return;
response.addHeader(header, value);
}
Add the specified header to the specified value.
Params: - header – the
Header
to set - value – Value to be set
Since: 2.3.8
/**
* Add the specified header to the specified value.
*
* @param header the {@link Header} to set
* @param value Value to be set
*
* @since 2.3.8
*/
public void addHeader(final Header header, final HeaderValue value) {
checkResponse();
if (isCommitted())
return;
response.addHeader(header, value);
}
Add the specified integer header to the specified value.
Params: - name – Name of the header to set
- value – Integer value to be set
/**
* Add the specified integer header to the specified value.
*
* @param name Name of the header to set
* @param value Integer value to be set
*/
public void addIntHeader(final String name, final int value) {
if (isCommitted())
return;
addHeader(name, "" + value);
}
Add the specified integer header to the specified value.
Params: - header – the
Header
to set - value – Integer value to be set
Since: 2.1.2
/**
* Add the specified integer header to the specified value.
*
* @param header the {@link Header} to set
* @param value Integer value to be set
*
* @since 2.1.2
*/
@SuppressWarnings("unused")
public void addIntHeader(final Header header, final int value) {
if (isCommitted())
return;
addHeader(header, Integer.toString(value));
}
Has the specified header been set already in this response?
Params: - name – Name of the header to check
/**
* Has the specified header been set already in this response?
*
* @param name Name of the header to check
*/
public boolean containsHeader(final String name) {
checkResponse();
return response.containsHeader(name);
}
Has the specified header been set already in this response?
Params: - header – the
Header
to check
Since: 2.1.2
/**
* Has the specified header been set already in this response?
*
* @param header the {@link Header} to check
*
* @since 2.1.2
*/
@SuppressWarnings("unused")
public boolean containsHeader(final Header header) {
checkResponse();
return response.containsHeader(header);
}
Send an acknowledgment of a request. An acknowledgment in this
case is simply an HTTP response status line, i.e.
HTTP/1.1 [STATUS] [REASON-PHRASE].
Throws: - IOException – if an input/output error occurs
/**
* Send an acknowledgment of a request. An acknowledgment in this
* case is simply an HTTP response status line, i.e.
* <code>HTTP/1.1 [STATUS] [REASON-PHRASE]<code>.
*
* @exception java.io.IOException if an input/output error occurs
*/
public void sendAcknowledgement() throws IOException {
if (isCommitted() || !request.requiresAcknowledgement())
return;
response.setAcknowledgement(true);
outputBuffer.acknowledge();
}
Send an error response with the specified status and a
default message.
Params: - status – HTTP status code to send
Throws: - IllegalStateException – if this response has
already been committed
- IOException – if an input/output error occurs
/**
* Send an error response with the specified status and a
* default message.
*
* @param status HTTP status code to send
*
* @exception IllegalStateException if this response has
* already been committed
* @exception java.io.IOException if an input/output error occurs
*/
public void sendError(int status)
throws IOException {
sendError(status, null);
}
Send an error response with the specified status and message.
Params: - status – HTTP status code to send
- message – Corresponding message to send
Throws: - IllegalStateException – if this response has
already been committed
- IOException – if an input/output error occurs
/**
* Send an error response with the specified status and message.
*
* @param status HTTP status code to send
* @param message Corresponding message to send
*
* @exception IllegalStateException if this response has
* already been committed
* @exception java.io.IOException if an input/output error occurs
*/
public void sendError(final int status, final String message)
throws IOException {
checkResponse();
if (isCommitted())
throw new IllegalStateException("Illegal attempt to call sendError() after the response has been committed.");
setError();
response.getHeaders().removeHeader(Header.TransferEncoding);
response.setContentLanguage(null);
response.setContentLengthLong(-1L);
response.setChunked(false);
response.setCharacterEncoding(null);
response.setContentType((String) null);
response.setLocale(null);
outputBuffer.reset();
usingWriter = false;
usingOutputStream = false;
setStatus(status, message);
String nonNullMsg = message;
if (nonNullMsg == null) {
final HttpStatus httpStatus = HttpStatus.getHttpStatus(status);
if (httpStatus != null && httpStatus.getReasonPhrase() != null) {
nonNullMsg = httpStatus.getReasonPhrase();
} else {
nonNullMsg = "Unknown Error";
}
}
HtmlHelper.sendErrorPage(request, this, getErrorPageGenerator(),
status, nonNullMsg, nonNullMsg, null);
finish();
}
Send a temporary redirect to the specified redirect location URL.
Params: - location – Location URL to redirect to
Throws: - IllegalStateException – if this response has
already been committed
- IOException – if an input/output error occurs
/**
* Send a temporary redirect to the specified redirect location URL.
*
* @param location Location URL to redirect to
*
* @exception IllegalStateException if this response has
* already been committed
* @exception java.io.IOException if an input/output error occurs
*/
public void sendRedirect(String location)
throws IOException {
if (isCommitted())
throw new IllegalStateException("Illegal attempt to redirect the response as the response has been committed.");
// Clear any data content that has been buffered
resetBuffer();
// Generate a temporary redirect to the specified location
try {
String absolute = toAbsolute(location, true);
// END RIMOD 4642650
setStatus(HttpStatus.FOUND_302);
setHeader(Header.Location, absolute);
// According to RFC2616 section 10.3.3 302 Found,
// the response SHOULD contain a short hypertext note with
// a hyperlink to the new URI.
setContentType("text/html");
setLocale(Locale.getDefault());
String filteredMsg = filter(absolute);
StringBuilder sb = new StringBuilder(150 + absolute.length());
sb.append("<html>\r\n");
sb.append("<head><title>Document moved</title></head>\r\n");
sb.append("<body><h1>Document moved</h1>\r\n");
sb.append("This document has moved <a href=\"");
sb.append(filteredMsg);
sb.append("\">here</a>.<p>\r\n");
sb.append("</body>\r\n");
sb.append("</html>\r\n");
try {
getWriter().write(sb.toString());
getWriter().flush();
} catch (IllegalStateException ise1) {
try {
getOutputStream().write(sb.toString().getBytes(
org.glassfish.grizzly.http.util.Constants.DEFAULT_HTTP_CHARSET));
} catch (IllegalStateException ise2) {
// ignore; the RFC says "SHOULD" so it is acceptable
// to omit the body in case of an error
}
}
} catch (IllegalArgumentException e) {
sendError(404);
}
finish();
}
Set the specified date header to the specified value.
Params: - name – Name of the header to set
- value – Date value to be set
/**
* Set the specified date header to the specified value.
*
* @param name Name of the header to set
* @param value Date value to be set
*/
public void setDateHeader(String name, long value) {
if (isCommitted())
return;
if (format == null) {
format = new SimpleDateFormat(HTTP_RESPONSE_DATE_HEADER,
Locale.US);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
}
setHeader(name, FastHttpDateFormat.formatDate(value, format));
}
Set the specified date header to the specified value.
Params: - header – the
Header
to set - value – Date value to be set
Since: 2.1.2
/**
* Set the specified date header to the specified value.
*
* @param header the {@link Header} to set
* @param value Date value to be set
*
* @since 2.1.2
*/
@SuppressWarnings("unused")
public void setDateHeader(final Header header, long value) {
if (isCommitted())
return;
if (format == null) {
format = new SimpleDateFormat(HTTP_RESPONSE_DATE_HEADER,
Locale.US);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
}
setHeader(header, FastHttpDateFormat.formatDate(value, format));
}
Set the specified header to the specified value.
Params: - name – Name of the header to set
- value – Value to be set
/**
* Set the specified header to the specified value.
*
* @param name Name of the header to set
* @param value Value to be set
*/
public void setHeader(final String name, final String value) {
checkResponse();
if (isCommitted())
return;
response.setHeader(name, value);
}
Set the specified header to the specified value.
Params: - name – Name of the header to set
- value – Value to be set
Since: 2.3.8
/**
* Set the specified header to the specified value.
*
* @param name Name of the header to set
* @param value Value to be set
*
* @since 2.3.8
*/
public void setHeader(final String name, final HeaderValue value) {
checkResponse();
if (isCommitted())
return;
response.setHeader(name, value);
}
Set the specified header to the specified value.
Params: - header – the
Header
to set - value – Value to be set
Since: 2.1.2
/**
* Set the specified header to the specified value.
*
* @param header the {@link Header} to set
* @param value Value to be set
*
* @since 2.1.2
*/
public void setHeader(final Header header, final String value) {
checkResponse();
if (isCommitted())
return;
response.setHeader(header, value);
}
Set the specified header to the specified value.
Params: - header – the
Header
to set - value – Value to be set
Since: 2.3.8
/**
* Set the specified header to the specified value.
*
* @param header the {@link Header} to set
* @param value Value to be set
*
* @since 2.3.8
*/
public void setHeader(final Header header, final HeaderValue value) {
checkResponse();
if (isCommitted())
return;
response.setHeader(header, value);
}
Set the specified integer header to the specified value.
Params: - name – Name of the header to set
- value – Integer value to be set
/**
* Set the specified integer header to the specified value.
*
* @param name Name of the header to set
* @param value Integer value to be set
*/
public void setIntHeader(String name, int value) {
if (isCommitted())
return;
setHeader(name, "" + value);
}
Set the specified integer header to the specified value.
Params: - header – the
Header
to set - value – Integer value to be set
Since: 2.1.2
/**
* Set the specified integer header to the specified value.
*
* @param header the {@link Header} to set
* @param value Integer value to be set
*
* @since 2.1.2
*/
@SuppressWarnings("unused")
public void setIntHeader(final Header header, final int value) {
if (isCommitted())
return;
setHeader(header, Integer.toString(value));
}
Set the HTTP status to be returned with this response.
Params: - status – The new HTTP status
/**
* Set the HTTP status to be returned with this response.
*
* @param status The new HTTP status
*/
public void setStatus(int status) {
setStatus(status, null);
}
Set the HTTP status and message to be returned with this response.
Params: - status – The new HTTP status
- message – The associated text message
/**
* Set the HTTP status and message to be returned with this response.
*
* @param status The new HTTP status
* @param message The associated text message
*
*/
public void setStatus(int status, String message) {
checkResponse();
if (isCommitted())
return;
response.setStatus(status);
response.setReasonPhrase(message);
}
Set the HTTP status and message to be returned with this response.
Params: - status –
HttpStatus
to set
/**
* Set the HTTP status and message to be returned with this response.
* @param status {@link HttpStatus} to set
*/
public void setStatus(HttpStatus status) {
checkResponse();
if (isCommitted())
return;
status.setValues(response);
}
// ------------------------------------------------------ Protected Methods
Convert (if necessary) and return the absolute URL that represents the
resource referenced by this possibly relative URL. If this URL is
already absolute, return it unchanged.
Params: - location – URL to be (possibly) converted and then returned
Throws: - IllegalArgumentException – if a MalformedURLException is
thrown when converting the relative URL to an absolute one
/**
* Convert (if necessary) and return the absolute URL that represents the
* resource referenced by this possibly relative URL. If this URL is
* already absolute, return it unchanged.
*
* @param location URL to be (possibly) converted and then returned
*
* @exception IllegalArgumentException if a MalformedURLException is
* thrown when converting the relative URL to an absolute one
*/
@SuppressWarnings({"unchecked"})
protected String toAbsolute(final String location, final boolean normalize) {
if (location == null) {
return null;
}
final boolean leadingSlash = location.startsWith("/");
if (leadingSlash || (!location.contains("://"))) {
final String scheme = request.getScheme();
final String name = request.getServerName();
final int port = request.getServerPort();
redirectURLCC.recycle();
final CharChunk cc = redirectURLCC;
try {
cc.append(scheme, 0, scheme.length());
cc.append("://", 0, 3);
cc.append(name, 0, name.length());
if ((scheme.equals("http") && port != 80)
|| (scheme.equals("https") && port != 443)) {
cc.append(':');
String portS = port + "";
cc.append(portS, 0, portS.length());
}
if (!leadingSlash) {
String relativePath = request.getDecodedRequestURI();
final int pos = relativePath.lastIndexOf('/');
relativePath = relativePath.substring(0, pos);
final String encodedURI;
if (System.getSecurityManager() != null) {
try {
final String frelativePath = relativePath;
encodedURI = AccessController.doPrivileged(
new PrivilegedExceptionAction<String>() {
@Override
public String run() throws IOException {
return urlEncoder.encodeURL(frelativePath);
}
});
} catch (PrivilegedActionException pae) {
throw new IllegalArgumentException(location, pae.getCause());
}
} else {
encodedURI = urlEncoder.encodeURL(relativePath);
}
cc.append(encodedURI, 0, encodedURI.length());
cc.append('/');
}
cc.append(location, 0, location.length());
} catch (IOException e) {
throw new IllegalArgumentException(location, e);
}
if (normalize){
HttpRequestURIDecoder.normalizeChars(cc);
}
return cc.toString();
} else {
return location;
}
}
Filter the specified message string for characters that are sensitive
in HTML. This avoids potential attacks caused by including JavaScript
codes in the request URL that is often reported in error messages.
Params: - message – The message string to be filtered
/**
* Filter the specified message string for characters that are sensitive
* in HTML. This avoids potential attacks caused by including JavaScript
* codes in the request URL that is often reported in error messages.
*
* @param message The message string to be filtered
*/
public static String filter(String message) {
if (message == null)
return (null);
char content[] = new char[message.length()];
message.getChars(0, message.length(), content, 0);
final StringBuilder result = new StringBuilder(content.length + 50);
for (int i = 0; i < content.length; i++) {
switch (content[i]) {
case '<':
result.append("<");
break;
case '>':
result.append(">");
break;
case '&':
result.append("&");
break;
case '"':
result.append(""");
break;
default:
result.append(content[i]);
}
}
return (result.toString());
}
Return the specified URL with the specified session identifier
suitably encoded.
Params: - url – URL to be encoded with the session id
- sessionId – Session id to be included in the encoded URL
/**
* Return the specified URL with the specified session identifier
* suitably encoded.
*
* @param url URL to be encoded with the session id
* @param sessionId Session id to be included in the encoded URL
*/
protected String toEncoded(String url, String sessionId) {
if ((url == null) || (sessionId == null))
return (url);
String path = url;
String query = "";
String anchor = "";
int question = url.indexOf('?');
if (question >= 0) {
path = url.substring(0, question);
query = url.substring(question);
}
int pound = path.indexOf('#');
if (pound >= 0) {
anchor = path.substring(pound);
path = path.substring(0, pound);
}
StringBuilder sb = new StringBuilder(path);
if( sb.length() > 0 ) { // jsessionid can't be first.
sb.append(";jsessionid=");
sb.append(sessionId);
}
String jrouteId = request.getHeader(Constants.PROXY_JROUTE);
if (jrouteId != null) {
sb.append(":");
sb.append(jrouteId);
}
sb.append(anchor);
sb.append(query);
return (sb.toString());
}
Is the file cache enabled?
/**
* Is the file cache enabled?
*/
@SuppressWarnings("unused")
public boolean isCacheEnabled() {
return cacheEnabled;
}
Get the context of the suspended Response.
Returns: the context of the suspended Response.
/**
* Get the context of the suspended <tt>Response</tt>.
*
* @return the context of the suspended <tt>Response</tt>.
*/
public SuspendContext getSuspendContext() {
return suspendedContext;
}
Return true if that suspend()
has been invoked and set to true
Returns: true if that suspend()
has been invoked and set to true
/**
* Return <tt>true<//tt> if that {@link Response#suspend()} has been
* invoked and set to <tt>true</tt>
* @return <tt>true<//tt> if that {@link Response#suspend()} has been
* invoked and set to <tt>true</tt>
*/
public boolean isSuspended() {
checkResponse();
final SuspendState state;
synchronized (suspendedContext) {
state = suspendState;
}
return state == SuspendState.SUSPENDED || state == SuspendState.RESUMING
|| state == SuspendState.CANCELLING;
}
/**
* Suspend the {@link Response}. Suspending a {@link Response} will
* tell the underlying container to avoid recycling objects associated with
* the current instance, and also to avoid committing response.
*/
@SuppressWarnings("deprecation")
public void suspend() {
suspend(DelayedExecutor.UNSET_TIMEOUT, TimeUnit.MILLISECONDS);
}
Suspend the Response
. Suspending a Response
will tell the underlying container to avoid recycling objects associated with the current instance, and also to avoid committing response. Params: - timeout – The maximum amount of time, a
Response
can be suspended. When the timeout expires (because nothing has been written or because the resume()
or cancel()
), the Response
will be automatically resumed and committed. Usage of any methods of a Response
that times out will throw an IllegalStateException
. - timeunit – timeout units
Deprecated: timeout parameters don't make any sense without CompletionHandler
/**
* Suspend the {@link Response}. Suspending a {@link Response} will
* tell the underlying container to avoid recycling objects associated with
* the current instance, and also to avoid committing response.
*
* @param timeout The maximum amount of time,
* a {@link Response} can be suspended. When the timeout expires (because
* nothing has been written or because the {@link Response#resume()}
* or {@link Response#cancel()}), the {@link Response} will be automatically
* resumed and committed. Usage of any methods of a {@link Response} that
* times out will throw an {@link IllegalStateException}.
* @param timeunit timeout units
*
* @deprecated timeout parameters don't make any sense without CompletionHandler
*/
public void suspend(final long timeout, final TimeUnit timeunit) {
suspend(timeout, timeunit, null);
}
Suspend the Response
. Suspending a Response
will tell the underlying container to avoid recycling objects associated with the current instance, and also to avoid committing response. When the resume()
is invoked, the container will make sure CompletionHandler.completed(Object)
is invoked with the original attachment. When the cancel()
is invoked, the container will make sure CompletionHandler.cancelled()
is invoked with the original attachment. If the timeout expires, the CompletionHandler.cancelled()
is invoked with the original attachment and the Response
committed. Params: - timeout – The maximum amount of time the
Response
can be suspended. When the timeout expires (because nothing has been written or because the resume()
or cancel()
), the Response
will be automatically resumed and committed. Usage of any methods of a Response
that times out will throw an IllegalStateException
. - timeunit – timeout units
- completionHandler – a
CompletionHandler
/**
* Suspend the {@link Response}. Suspending a {@link Response} will
* tell the underlying container to avoid recycling objects associated with
* the current instance, and also to avoid committing response. When the
* {@link Response#resume()} is invoked, the container will
* make sure {@link CompletionHandler#completed(Object)}
* is invoked with the original <tt>attachment</tt>. When the
* {@link Response#cancel()} is invoked, the container will
* make sure {@link org.glassfish.grizzly.CompletionHandler#cancelled()}
* is invoked with the original <tt>attachment</tt>. If the timeout expires, the
* {@link org.glassfish.grizzly.CompletionHandler#cancelled()} is invoked with the original <tt>attachment</tt> and
* the {@link Response} committed.
*
* @param timeout The maximum amount of time the {@link Response} can be suspended.
* When the timeout expires (because nothing has been written or because the
* {@link Response#resume()} or {@link Response#cancel()}), the {@link Response}
* will be automatically resumed and committed. Usage of any methods of a
* {@link Response} that times out will throw an {@link IllegalStateException}.
* @param timeunit timeout units
* @param completionHandler a {@link org.glassfish.grizzly.CompletionHandler}
*/
public void suspend(final long timeout, final TimeUnit timeunit,
final CompletionHandler<Response> completionHandler) {
suspend(timeout, timeunit, completionHandler, null);
}
Suspend the Response
. Suspending a Response
will tell the underlying container to avoid recycling objects associated with the current instance, and also to avoid committing response. When the resume()
is invoked, the container will make sure CompletionHandler.completed(Object)
is invoked with the original attachment. When the cancel()
is invoked, the container will make sure CompletionHandler.cancelled()
is invoked with the original attachment. If the timeout expires, the CompletionHandler.cancelled()
is invoked with the original attachment and the Response
committed. Params: - timeout – The maximum amount of time the
Response
can be suspended. When the timeout expires (because nothing has been written or because the resume()
or cancel()
), the Response
will be automatically resumed and committed. Usage of any methods of a Response
that times out will throw an IllegalStateException
. - timeunit – timeout units
- completionHandler – a
CompletionHandler
- timeoutHandler –
TimeoutHandler
to customize the suspended Response timeout logic.
/**
* Suspend the {@link Response}. Suspending a {@link Response} will
* tell the underlying container to avoid recycling objects associated with
* the current instance, and also to avoid committing response. When the
* {@link Response#resume()} is invoked, the container will
* make sure {@link CompletionHandler#completed(Object)}
* is invoked with the original <tt>attachment</tt>. When the
* {@link Response#cancel()} is invoked, the container will
* make sure {@link org.glassfish.grizzly.CompletionHandler#cancelled()}
* is invoked with the original <tt>attachment</tt>. If the timeout expires, the
* {@link org.glassfish.grizzly.CompletionHandler#cancelled()} is invoked with the original <tt>attachment</tt> and
* the {@link Response} committed.
*
* @param timeout The maximum amount of time the {@link Response} can be suspended.
* When the timeout expires (because nothing has been written or because the
* {@link Response#resume()} or {@link Response#cancel()}), the {@link Response}
* will be automatically resumed and committed. Usage of any methods of a
* {@link Response} that times out will throw an {@link IllegalStateException}.
* @param timeunit timeout units
* @param completionHandler a {@link org.glassfish.grizzly.CompletionHandler}
* @param timeoutHandler {@link TimeoutHandler} to customize the suspended <tt>Response</tt> timeout logic.
*/
public void suspend(final long timeout, final TimeUnit timeunit,
final CompletionHandler<Response> completionHandler,
final TimeoutHandler timeoutHandler) {
checkResponse();
if (suspendState != SuspendState.NONE) {
throw new IllegalStateException("Already Suspended");
}
suspendState = SuspendState.SUSPENDED;
suspendStatus.suspend();
suspendedContext.init(completionHandler, timeoutHandler);
HttpServerProbeNotifier.notifyRequestSuspend(
request.httpServerFilter, ctx.getConnection(), request);
httpContext.getCloseable().addCloseListener(suspendedContext.closeListener);
if (timeout > 0) {
final long timeoutMillis =
TimeUnit.MILLISECONDS.convert(timeout, timeunit);
delayQueue.add(suspendedContext.suspendTimeout,
timeoutMillis, TimeUnit.MILLISECONDS);
suspendedContext.suspendTimeout.delayMillis = timeoutMillis;
}
}
Complete the Response
and finish/commit it. If a CompletionHandler
has been defined, its CompletionHandler.completed(Object)
will first be invoked, then the finish()
. Those operations commit the response. /**
* Complete the {@link Response} and finish/commit it. If a
* {@link CompletionHandler} has been defined, its {@link CompletionHandler#completed(Object)}
* will first be invoked, then the {@link Response#finish()}.
* Those operations commit the response.
*/
@SuppressWarnings({"unchecked"})
public void resume() {
checkResponse();
suspendedContext.markResumed();
ctx.resume();
}
Cancel the Response
and finish/commit it. If a CompletionHandler
has been defined, its CompletionHandler.cancelled()
will first be invoked, then the finish()
. Those operations commit the response. Deprecated: pls. use resume()
/**
* Cancel the {@link Response} and finish/commit it. If a
* {@link CompletionHandler} has been defined, its {@link CompletionHandler#cancelled()}
* will first be invoked, then the {@link Response#finish()}.
* Those operations commit the response.
*
* @deprecated pls. use {@link #resume()}
*/
public void cancel() {
checkResponse();
//noinspection deprecation
suspendedContext.markCancelled();
ctx.resume();
}
Make sure the Response
object has been set. /**
* Make sure the {@link Response} object has been set.
*/
final void checkResponse() {
if (response == null) {
throw new IllegalStateException("Internal " +
"org.glassfish.grizzly.http.server.Response has not been set");
}
}
public boolean isSendFileEnabled() {
return sendFileEnabled;
}
public final class SuspendedContextImpl implements SuspendContext {
private int modCount;
CompletionHandler<Response> completionHandler;
SuspendTimeout suspendTimeout;
private CloseListener closeListener;
Marks Response
as resumed, but doesn't resume associated FilterChainContext
invocation. /**
* Marks {@link Response} as resumed, but doesn't resume associated
* {@link FilterChainContext} invocation.
*/
public synchronized boolean markResumed() {
modCount++;
if (suspendState != SuspendState.SUSPENDED) {
if (suspendState == SuspendState.CANCELLED ||
suspendState == SuspendState.CANCELLING) { // Siletly return if processing has been cancelled
return false;
}
throw new IllegalStateException("Not Suspended");
}
suspendState = SuspendState.RESUMING;
httpContext.getCloseable().removeCloseListener(closeListener);
if (completionHandler != null) {
completionHandler.completed(Response.this);
}
reset();
suspendState = SuspendState.RESUMED;
HttpServerProbeNotifier.notifyRequestResume(request.httpServerFilter,
ctx.getConnection(), request);
return true;
}
Marks Response
as cancelled, if expectedModCount corresponds to the current modCount. This method doesn't resume associated FilterChainContext
invocation. /**
* Marks {@link Response} as cancelled, if expectedModCount corresponds
* to the current modCount. This method doesn't resume associated
* {@link FilterChainContext} invocation.
*/
protected synchronized boolean markCancelled(final int expectedModCount) {
if (modCount != expectedModCount) {
return false;
}
modCount++;
if (suspendState != SuspendState.SUSPENDED) {
throw new IllegalStateException("Not Suspended");
}
suspendState = SuspendState.CANCELLING;
httpContext.getCloseable().removeCloseListener(closeListener);
if (completionHandler != null) {
completionHandler.cancelled();
}
suspendState = SuspendState.CANCELLED;
reset();
HttpServerProbeNotifier.notifyRequestCancel(
request.httpServerFilter, ctx.getConnection(), request);
final InputBuffer inputBuffer = request.getInputBuffer();
if (!inputBuffer.isFinished()) {
inputBuffer.terminate();
}
return true;
}
Marks Response
as cancelled, but doesn't resume associated FilterChainContext
invocation. Deprecated:
/**
* Marks {@link Response} as cancelled, but doesn't resume associated
* {@link FilterChainContext} invocation.
* @deprecated
*/
public synchronized void markCancelled() {
markCancelled(modCount);
}
private void init(final CompletionHandler<Response> completionHandler,
final TimeoutHandler timeoutHandler) {
this.completionHandler = completionHandler;
this.suspendTimeout = new SuspendTimeout(modCount, timeoutHandler);
closeListener = new SuspendCloseListener(modCount);
}
void reset() {
suspendTimeout.reset();
suspendTimeout = null;
completionHandler = null;
closeListener = null;
}
@Override
public CompletionHandler<Response> getCompletionHandler() {
return completionHandler;
}
@Override
public TimeoutHandler getTimeoutHandler() {
return suspendTimeout.timeoutHandler;
}
@Override
public long getTimeout(final TimeUnit timeunit) {
return suspendTimeout.getTimeout(timeunit);
}
@Override
public void setTimeout(final long timeout, final TimeUnit timeunit) {
synchronized (suspendedContext) {
if (suspendState != SuspendState.SUSPENDED ||
suspendTimeout == null) {
return;
}
suspendTimeout.setTimeout(timeout, timeunit);
}
}
@Override
public boolean isSuspended() {
return Response.this.isSuspended();
}
public SuspendStatus getSuspendStatus() {
return suspendStatus;
}
@SuppressWarnings("deprecation")
private class SuspendCloseListener implements GenericCloseListener {
private final int expectedModCount;
public SuspendCloseListener(int expectedModCount) {
this.expectedModCount = expectedModCount;
}
@Override
public void onClosed(final Closeable connection,
final CloseType closeType) throws IOException {
checkResponse();
if (suspendedContext.markCancelled(expectedModCount)) {
// ctx.resume();
ctx.completeAndRelease();
}
}
}
}
protected class SuspendTimeout {
private final int expectedModCount;
TimeoutHandler timeoutHandler;
long delayMillis;
volatile long timeoutTimeMillis;
private SuspendTimeout(int modCount, TimeoutHandler timeoutHandler) {
this.expectedModCount = modCount;
this.timeoutHandler = timeoutHandler;
}
boolean onTimeout() {
timeoutTimeMillis = DelayedExecutor.UNSET_TIMEOUT;
final TimeoutHandler localTimeoutHandler = timeoutHandler;
if (localTimeoutHandler == null
|| localTimeoutHandler.onTimeout(Response.this)) {
HttpServerProbeNotifier.notifyRequestTimeout(
request.httpServerFilter, ctx.getConnection(), request);
try {
checkResponse();
//noinspection StatementWithEmptyBody
if (suspendedContext.markCancelled(expectedModCount)) {
// ctx.resume();
}
} catch (Exception ignored) {
}
return true;
} else {
return false;
}
}
private long getTimeout(TimeUnit timeunit) {
if (delayMillis > 0) {
return timeunit.convert(delayMillis, TimeUnit.MILLISECONDS);
} else {
return delayMillis;
}
}
private void setTimeout(long timeout, TimeUnit timeunit) {
if (timeout > 0) {
delayMillis = TimeUnit.MILLISECONDS.convert(timeout, timeunit);
} else {
delayMillis = DelayedExecutor.UNSET_TIMEOUT;
}
delayQueue.add(this, delayMillis, TimeUnit.MILLISECONDS);
}
private void reset() {
timeoutTimeMillis = DelayedExecutor.UNSET_TIMEOUT;
timeoutHandler = null;
}
}
private static class DelayQueueWorker implements
DelayedExecutor.Worker<SuspendTimeout> {
@Override
public boolean doWork(final SuspendTimeout element) {
return element.onTimeout();
}
}
private static class DelayQueueResolver implements
DelayedExecutor.Resolver<SuspendTimeout> {
@Override
public boolean removeTimeout(final SuspendTimeout element) {
if (element.timeoutTimeMillis != DelayedExecutor.UNSET_TIMEOUT) {
element.timeoutTimeMillis = DelayedExecutor.UNSET_TIMEOUT;
return true;
}
return false;
}
@Override
public long getTimeoutMillis(final SuspendTimeout element) {
return element.timeoutTimeMillis;
}
@Override
public void setTimeoutMillis(final SuspendTimeout element, final long timeoutMillis) {
element.timeoutTimeMillis = timeoutMillis;
}
}
}