com.caucho.server.http.HttpServletResponseImpl Maven / Gradle / Ivy
/*
* Copyright (c) 1998-2018 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.server.http;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import com.caucho.VersionFactory;
import com.caucho.server.httpcache.AbstractCacheFilterChain;
import com.caucho.server.session.CookieImpl;
import com.caucho.server.session.SessionManager;
import com.caucho.server.util.CauchoSystem;
import com.caucho.server.webapp.ErrorPageManager;
import com.caucho.server.webapp.WebApp;
import com.caucho.util.HTTPUtil;
import com.caucho.util.L10N;
/**
* User facade for http responses.
*/
public final class HttpServletResponseImpl extends AbstractCauchoResponse
implements CauchoResponse
{
private static final Logger log
= Logger.getLogger(HttpServletResponseImpl.class.getName());
private static final L10N L = new L10N(HttpServletResponseImpl.class);
private static final HashMap _errors;
private final HttpServletRequestImpl _request;
private AbstractHttpResponse _response;
private int _status = 200;
private String _statusMessage = "OK";
private String _sessionId;
private ArrayList _cookiesOut;
private AbstractResponseStream _responseStream;
private ServletOutputStreamImpl _outputStream;
private ResponseWriter _writer;
private String _setCharEncoding;
private String _charEncoding;
private String _contentType;
private Locale _locale;
private boolean _hasError;
private boolean _forbidForward;
private AbstractCacheFilterChain _cacheInvocation;
// send a Cache-Control: no-cache
private boolean _isNoCache;
// cache private, e.g. for session cookie
private boolean _isPrivateCache;
// application has set cache control
private boolean _isCacheControl;
// rewrite: cache disable unless a Vary exists (to handle rewrite issues)
private boolean _isNoCacheUnlessVary;
// disable the cache
private boolean _isDisableCache;
public HttpServletResponseImpl(HttpServletRequestImpl request,
AbstractHttpResponse response)
{
_request = request;
_response = response;
_responseStream = response.getResponseStream();
}
public HttpServletRequestImpl getRequest()
{
return _request;
}
//
// servlet response
//
//
// output stream
//
/**
* Returns an output stream for writing to the client. You can use
* the output stream to write binary data.
*/
@Override
public ServletOutputStream getOutputStream()
throws IOException
{
if (_outputStream != null)
return _outputStream;
// server/1b08 (tck)
if (_writer != null)
throw new IllegalStateException(L.l("getOutputStream() can't be called after getWriter()."));
try {
// jsp/0510
_responseStream.clearBuffer();
} catch (Exception e) {
// server/1b32
log.log(Level.FINER, e.toString(), e);
}
_outputStream = _response.getResponseOutputStream();
_outputStream.init(_responseStream);
/*
// server/10a2
if (! _hasWriter) {
// jsp/0510 vs server/1b00
// _responseStream.setOutputStreamOnly(true);
}
*/
// jsp/1cie, jsp/1civ
// _responseStream.setEncoding(null);
return _outputStream;
}
/**
* Returns a PrintWriter with the proper character encoding for writing
* text data to the client.
*/
@Override
public PrintWriter getWriter()
throws IOException
{
if (_writer != null)
return _writer;
if (_outputStream != null) {
if (_response.isClosed()) {
// jsp/017o
_writer = _response.getResponsePrintWriter();
_writer.init(_responseStream);
return _writer;
}
throw new IllegalStateException(L.l("getWriter() can't be called after getOutputStream()."));
}
String encoding = getCharacterEncoding();
_writer = _response.getResponsePrintWriter();
_writer.init(_responseStream);
if (encoding != null) {
_responseStream.setEncoding(encoding);
}
return _writer;
}
/**
* Sets the output buffer size to size
. The servlet engine
* may round the size up.
*
* @param size the new output buffer size.
*/
@Override
public void setBufferSize(int size)
{
_responseStream.setBufferSize(size);
}
/**
* Returns the size of the output buffer.
*/
@Override
public int getBufferSize()
{
return _responseStream.getBufferSize();
}
// needed to support JSP
public int getRemaining()
{
return _responseStream.getRemaining();
}
/**
* Flushes the buffer to the client.
*/
public void flushBuffer()
throws IOException
{
// server/10sn
_responseStream.flush();
}
/**
* Returns true if some data has actually been send to the client. The
* data will be sent if the buffer overflows or if it's explicitly flushed.
*/
@Override
public final boolean isCommitted()
{
AbstractHttpResponse response = _response;
if (response != null) {
return response.isCommitted();
}
else {
return true;
}
}
/**
* Resets the output stream, clearing headers and the output buffer.
* Calling reset()
after data has been committed is illegal.
*
* @throws IllegalStateException if isCommitted()
is true.
*/
public void reset()
{
reset(false);
}
/**
* Clears the response for a forward()
*
* @param force if not true and the response stream has committed,
* throw the IllegalStateException.
*/
void reset(boolean force)
{
if (! force && isCommitted()) {
throw new IllegalStateException(L.l("response cannot be reset() after committed"));
}
_responseStream.clearBuffer();
_status = 200;
_statusMessage = "OK";
_setCharEncoding = null;
_charEncoding = null;
_locale = null;
_outputStream = null;
_writer = null;
try {
_responseStream.setLocale(null);
_responseStream.setEncoding(null);
} catch (Exception e) {
}
if (_cookiesOut != null)
_cookiesOut.clear();
_response.reset();
}
/**
* Resets the output stream, clearing headers and the output buffer.
* Calling reset()
after data has been committed is illegal.
*
* @throws IllegalStateException if isCommitted()
is true.
*/
@Override
public void resetBuffer()
{
_responseStream.clearBuffer();
// jsp/15ma, server/2h7m
if (_responseStream.isCommitted())
_responseStream.killCaching();
/*
if (_currentWriter instanceof JspPrintWriter)
((JspPrintWriter) _currentWriter).clear();
*/
}
/**
* Explicitly sets the length of the result value. Normally, the servlet
* engine will handle this.
*/
@Override
public void setContentLength(int len)
{
if (_outputStream == null && _writer == null) {
_response.setContentLength(len);
}
}
/**
* Explicitly sets the length of the result value. Normally, the servlet
* engine will handle this.
*/
@Override
public void setContentLength(long length)
{
if (_outputStream == null && _writer == null) {
_response.setContentLength(length);
}
}
/**
* Disables the response
*
* @since Servlet 3.0
*/
public void disable()
{
}
/**
* Enables the response
*
* @since Servlet 3.0
*/
public void enable()
{
}
/**
* Returns true if the response is disabled
*
* @since Servlet 3.0
*/
public boolean isDisabled()
{
return false;
}
public void setLocale(Locale locale)
{
_locale = locale;
if (_setCharEncoding == null && ! isCommitted()) {
_charEncoding = getRequest().getWebApp().getLocaleEncoding(locale);
// server/12n0
// _setCharEncoding = _charEncoding;
try {
if (_charEncoding != null) {
// _originalStream.setEncoding(_charEncoding);
_responseStream.setEncoding(_charEncoding);
}
} catch (IOException e) {
}
}
StringBuilder cb = new StringBuilder();
cb.append(locale.getLanguage());
if (locale.getCountry() != null && ! "".equals(locale.getCountry())) {
cb.append("-");
cb.append(locale.getCountry());
if (locale.getVariant() != null && ! "".equals(locale.getVariant())) {
cb.append("-");
cb.append(locale.getVariant());
}
}
setHeader("Content-Language", cb.toString());
}
public Locale getLocale()
{
if (_locale != null)
return _locale;
else
return Locale.getDefault();
}
//
// proxy caching
//
/**
* Sets true if the cache is only for the browser, but not
* Resin's cache or proxies.
*
* Since proxy caching also caches headers, cached pages with
* session ids can't be cached in the browser.
*
* XXX: but doesn't this just mean that Resin shouldn't
* send the session information back if the page is cached?
* Because a second request where everything is identical
* would see the same response except for the cookies.
*/
public void setPrivateCache(boolean isPrivate)
{
// XXX: let the webApp override this?
_isPrivateCache = isPrivate;
// server/12dm
killCache();
}
/**
* Sets true if the cache is only for the browser and
* Resin's cache but not proxies.
*/
public void setPrivateOrResinCache(boolean isPrivate)
{
// XXX: let the webApp override this?
_isPrivateCache = isPrivate;
}
/**
* Sets the cache invocation to indicate that the response might be
* cacheable.
*/
@Override
public void setCacheInvocation(AbstractCacheFilterChain cacheInvocation)
{
assert(_cacheInvocation == null || cacheInvocation == null);
_cacheInvocation = cacheInvocation;
}
public final AbstractCacheFilterChain getCacheInvocation()
{
return _cacheInvocation;
}
@Override
public boolean isCaching()
{
return _cacheInvocation != null;
}
/**
* Set no cache w/o vary
*/
public void setNoCacheUnlessVary(boolean isNoCacheUnlessVary)
{
_isNoCacheUnlessVary = isNoCacheUnlessVary;
}
/**
* Return true if no-cache without var.
*/
public boolean isNoCacheUnlessVary()
{
return _isNoCacheUnlessVary;
}
/**
* Returns the value of the private cache.
*/
public boolean getPrivateCache()
{
return _isPrivateCache;
}
/**
* Returns true if the response should contain a Cache-Control: private
*/
public boolean isPrivateCache()
{
return ! _isCacheControl && _isPrivateCache;
}
/**
* True if the application has a set a cache-control directive
* that Resin doesn't understand.
*/
public boolean isCacheControl()
{
return _isCacheControl;
}
/**
* True if the application has a set a cache-control directive
* that Resin doesn't understand.
*/
public void setCacheControl(boolean isCacheControl)
{
// server/13d9
_isCacheControl = isCacheControl;
// killCache();
}
/**
* Set if the page is non-cacheable.
*/
public void setNoCache(boolean isNoCache)
{
_isNoCache = isNoCache;
killCache();
}
/**
* Returns true if the page is non-cacheable
*/
public boolean isNoCache()
{
return _isNoCache;
}
/**
* Set if the page is non-cacheable.
*/
@Override
public void killCache()
{
_isDisableCache = true;
_responseStream.killCaching();
}
public boolean isDisableCache()
{
return _isDisableCache;
}
//
// HttpServletResponse methods
//
/**
* Sets the HTTP status
*
* @param code the HTTP status code
*/
@Override
public void setStatus(int code)
{
setStatus(code, null);
}
/**
* Sets the HTTP status
*
* @param code the HTTP status code
* @param message the HTTP status message
*/
@Override
public void setStatus(int code, String message)
{
if (code < 0)
code = 500;
if (message != null) {
}
else if (code == SC_OK)
message = "OK";
else if (code == SC_NOT_MODIFIED)
message = "Not Modified";
else if (message == null) {
message = _errors.get(code);
if (message == null)
message = L.l("Internal Server Error");
}
// server/2h0g
if (code != SC_OK && code != SC_NOT_MODIFIED)
killCache();
if (code == SC_BAD_REQUEST || code == SC_SWITCHING_PROTOCOLS) {
_request.killKeepalive("servletResponse: bad request: " + code + " " + message);
}
_status = code;
_statusMessage = message;
}
/**
* Sends an HTTP error page based on the status code
*
* @param code the HTTP status code
*/
@Override
public void sendError(int code)
throws IOException
{
sendError(code, null);
}
/**
* Sends an HTTP error to the browser.
*
* @param code the HTTP error code
* @param value a string message
*/
@Override
public void sendError(int code, String value)
throws IOException
{
if (code == SC_NOT_MODIFIED && isProxyCacheFill()) {
setStatus(code, value);
if (handleNotModified()) {
return;
}
}
if (isCommitted())
throw new IllegalStateException(L.l("sendError() forbidden after buffer has been committed."));
//_currentWriter = null;
//setStream(_originalStream);
resetBuffer();
if (code != SC_NOT_MODIFIED)
killCache();
/* XXX: if we've already got an error, won't this just mask it?
if (responseStream.isCommitted())
throw new IllegalStateException("response can't sendError() after commit");
*/
WebApp webApp = getRequest().getWebApp();
ErrorPageManager errorManager = null;
if (webApp != null && webApp.isActive())
errorManager = webApp.getErrorPageManager();
else
errorManager = getRequest().getServer().getErrorPageManager();
setStatus(code, value);
try {
if (code == SC_NOT_MODIFIED || code == SC_NO_CONTENT) {
_response.finishInvocation();
return;
}
else if (errorManager != null) {
// server/10su
errorManager.sendError(_request, this, code, _statusMessage);
// _request.killKeepalive();
// close, but don't force a flush
// XXX: finish(false);
_response.finishInvocation();
return;
}
setContentType("text/html");
ServletOutputStream s = getOutputStream();
s.println("");
if (! isCommitted()) {
s.print("
");
s.print(code);
s.print(" ");
s.print(_statusMessage);
s.println(" ");
}
s.println("");
s.print("");
s.print(code);
s.print(" ");
s.print(_statusMessage);
s.println("
");
if (code == SC_NOT_FOUND) {
s.println(L.l("{0} was not found on this server.",
HTTPUtil.encodeString(getRequest().getPageURI())));
}
else if (code == SC_SERVICE_UNAVAILABLE) {
s.println(L.l("The server is temporarily unavailable due to maintenance downtime or excessive load."));
}
String version = null;
if (webApp == null) {
}
else if (webApp.getServer() != null
&& webApp.getServer().getServerHeader() != null) {
version = webApp.getServer().getServerHeader();
}
else if (CauchoSystem.isTesting()) {
}
else
version = VersionFactory.getFullVersion();
if (version != null) {
s.println("
");
s.println("");
s.println(version);
s.println("");
}
s.println("");
} catch (Exception e) {
log.fine(L.l("Error in processing sendError code={0}, status='{1}'",
code, value));
log.log(Level.FINE, e.toString(), e);
}
_request.killKeepalive("servlet error: " + code + " " + value);
// close, but don't force a flush
_response.finishInvocation();
}
private boolean isProxyCacheFill()
{
return true;
}
/**
* Handle a SC_NOT_MODIFIED response. If we've got a cache, fill the
* results from the cache.
*
* @return true if we filled from the cache
*/
boolean handleNotModified()
throws IOException
{
if (_status != SC_NOT_MODIFIED) {
return false;
}
else if (isProxyCacheFill()) {
return true;
}
/*
else if (isProxyCacheFill()) {
if (isCommitted()) {
return false;
}
// need to unclose because the not modified might be detected only
// when flushing the data
// _responseStream.clearClosed();
AbstractCacheFilterChain cacheInvocation = _cacheInvocation;
_cacheInvocation = null;
// XXX: complications with filters
if (cacheInvocation != null
&& cacheInvocation.fillFromCache(getRequest(), this,
matchCacheEntry)) {
matchCacheEntry.updateExpiresDate();
_response.finishInvocation(); // Don't force a flush to avoid extra TCP packet
return true;
}
}
*/
// server/13dh
else if (_cacheInvocation != null) {
WebApp webApp = _request.getWebApp();
long maxAge = webApp.getMaxAge(_request.getRequestURI());
if (maxAge > 0 && ! containsHeader("Cache-Control")) {
addHeader("Cache-Control", "max-age=" + (maxAge / 1000L));
}
}
return false;
}
/**
* Sets the browser content type. If the value contains a charset,
* the output encoding will be changed to match.
*
* For example, to set the output encoding to use UTF-8 instead of
* the default ISO-8859-1 (Latin-1), use the following:
*
* setContentType("text/html; charset=UTF-8");
*
*/
@Override
public void setContentType(String value)
{
if (isCommitted()) {
return;
}
// jsp/0511
if (_locale == null && _setCharEncoding == null) {
_charEncoding = null;
}
if (value == null) {
_contentType = null;
return;
}
ContentType item = AbstractHttpResponse.parseContentType(value);
_contentType = item.getContentType();
String encoding = item.getEncoding();
// server/172k
// _setCharEncoding = encoding;
if (encoding != null) {
setCharacterEncoding(encoding);
}
else if (_charEncoding != null || _setCharEncoding != null) {
return;
}
// XXX: conflict with servlet exception throwing order?
try {
encoding = getCharacterEncoding();
_responseStream.setEncoding(encoding);
} catch (Exception e) {
log.log(Level.FINE, e.toString(), e);
}
}
/**
* Gets the content type.
*/
public String getContentType()
{
if (_contentType == null) {
return null;
}
if (_setCharEncoding != null) {
return _contentType + "; charset=" + _setCharEncoding;
}
String charEncoding = getCharacterEncoding();
if (charEncoding != null
&& (_contentType.startsWith("text/")
|| _contentType.equals("application/json")
|| _contentType.startsWith("multipart/"))) {
return _contentType + "; charset=" + charEncoding;
}
else {
return _contentType;
}
}
/**
* Gets the content type.
*/
public String getContentTypeImpl()
{
return _contentType;
}
/**
* Gets the character encoding assigned by the developer. Used for JSP
* encoding.
*/
@Override
public String getCharacterEncodingAssigned()
{
return _setCharEncoding;
}
/**
* Gets the character encoding.
*/
@Override
public String getCharacterEncoding()
{
String charEncoding = _charEncoding;
if (charEncoding == null) {
charEncoding = _setCharEncoding;
if (charEncoding == null) {
/*
String contentType = getContentTypeImpl();
if (contentType == null
|| ! contentType.startsWith("text/")) {
return null;
}
*/
WebApp webApp = _request.getWebApp();
if (webApp != null) {
if (webApp.getJsp() != null)
charEncoding = webApp.getJsp().getCharacterEncoding();
if (charEncoding == null)
charEncoding = webApp.getCharacterEncoding();
}
if (charEncoding == null) {
// server/085a
charEncoding = "utf-8";
}
}
_charEncoding = charEncoding;
}
return charEncoding;
}
/**
* Gets the character encoding.
*/
public String getCharacterEncodingImpl()
{
// server/172d
// server/2u00
String setCharEncoding = _setCharEncoding;
if (setCharEncoding != null) {
return setCharEncoding;
}
String contentType = getContentTypeImpl();
if (contentType == null
|| contentType.startsWith("text/")
|| contentType.startsWith("multipart/")) {
// server/1b5a, #4778
return getCharacterEncoding();
}
else {
return null;
}
}
/**
* Sets the character encoding.
*/
@Override
public void setCharacterEncoding(String encoding)
{
if (isCommitted()) {
return;
}
if (_writer != null) {
// server/172k
if (encoding != null
&& _charEncoding != null
&& ! encoding.equalsIgnoreCase(_charEncoding)) {
if (log.isLoggable(Level.FINE))
log.fine(_request.getRequestURI() + ": setEncoding(" + encoding + ") ignored because writer already initialized with charset=" + _charEncoding);
}
return;
}
if (encoding == null
|| encoding.equals("ISO-8859-1")
|| encoding.equals("")) {
_setCharEncoding = "iso-8859-1";
}
else
_setCharEncoding = encoding;
_charEncoding = _setCharEncoding;
try {
_responseStream.setEncoding(encoding);
} catch (Exception e) {
log.log(Level.INFO, e.toString(), e);
}
}
/**
* Sends a redirect to the browser. If the URL is relative, it gets
* combined with the current url.
*
* @param url the possibly relative url to send to the browser
*/
@Override
public void sendRedirect(String url)
throws IOException
{
if (url == null)
throw new NullPointerException();
if (isCommitted())
throw new IllegalStateException(L.l("Can't sendRedirect() after data has committed to the client."));
_responseStream.clearBuffer();
// server/10c4
// reset();
resetBuffer();
setStatus(SC_MOVED_TEMPORARILY);
String encoding = getCharacterEncoding();
boolean isLatin1 = "iso-8859-1".equals(encoding);
String path = encodeAbsoluteRedirect(url);
setHeader("Location", path);
if (isLatin1)
setHeader("Content-Type", "text/html; charset=iso-8859-1");
else
setHeader("Content-Type", "text/html; charset=utf-8");
String msg = "The URL has moved here";
// The data is required for some WAP devices that can't handle an
// empty response.
if (_writer != null) {
_writer.println(msg);
}
else {
ServletOutputStream out = getOutputStream();
out.println(msg);
}
// closeConnection();
_request.saveSession(); // #503
close();
}
public String encodeAbsoluteRedirect(String url)
{
return encodeAbsoluteRedirect(url, getCharacterEncoding());
}
public String encodeAbsoluteRedirect(String url, String encoding)
{
String path = getAbsolutePath(url);
return escapeUrl(path, encoding);
}
public static String escapeUrl(String path, String encoding)
{
boolean isLatin1 = "iso-8859-1".equals(encoding);
return escapeUrl(path, isLatin1);
}
public static String escapeUrl(String path, boolean isLatin1)
{
StringBuilder cb = new StringBuilder();
for (int i = 0; i < path.length(); i++) {
char ch = path.charAt(i);
if (ch == '<') {
cb.append("%3c");
}
else if (ch == '>') {
cb.append("%3e");
}
else if (ch == '"') {
cb.append("%22");
}
else if (ch < 0x80)
cb.append(ch);
else if (isLatin1) {
addHex(cb, ch);
}
else if (ch < 0x800) {
int d1 = 0xc0 + ((ch >> 6) & 0x1f);
int d2 = 0x80 + (ch & 0x3f);
addHex(cb, d1);
addHex(cb, d2);
}
else {
int d1 = 0xe0 + ((ch >> 12) & 0xf);
int d2 = 0x80 + ((ch >> 6) & 0x3f);
int d3 = 0x80 + (ch & 0x3f);
addHex(cb, d1);
addHex(cb, d2);
addHex(cb, d3);
}
}
return cb.toString();
}
/**
* Returns the absolute path for a given relative path.
*
* @param path the possibly relative url to send to the browser
*/
private String getAbsolutePath(String path)
{
int slash = path.indexOf('/');
int len = path.length();
for (int i = 0; i < len; i++) {
char ch = path.charAt(i);
if (ch == ':') {
return path;
}
else if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z') {
continue;
}
else {
break;
}
}
WebApp webApp = getRequest().getWebApp();
String hostPrefix = null;
String host = _request.getHeader("Host");
String serverName = webApp.getHostName();
if (serverName == null
|| serverName.equals("")
|| serverName.equals("default")) {
serverName = _request.getServerName();
}
int port = _request.getServerPort();
if (hostPrefix != null && ! hostPrefix.equals("")) {
}
else if (serverName.startsWith("https:")) {
hostPrefix = serverName;
}
else if (serverName.startsWith("http:")) {
hostPrefix = _request.getScheme() + serverName.substring(4);
}
else if (host != null) {
hostPrefix = _request.getScheme() + "://" + host;
}
else {
hostPrefix = _request.getScheme() + "://" + serverName;
if (serverName.indexOf(':') < 0
&& port != 0 && port != 80 && port != 443)
hostPrefix += ":" + port;
}
if (slash == 0)
return hostPrefix + path;
String uri = _request.getRequestURI();
String contextPath = _request.getContextPath();
String queryString = null;
int p = path.indexOf('?');
if (p > 0) {
queryString = path.substring(p + 1);
path = path.substring(0, p);
}
if (uri.equals(contextPath)) {
path = uri + "/" + path;
}
else {
p = uri.lastIndexOf('/');
if (p >= 0)
path = uri.substring(0, p + 1) + path;
}
try {
if (queryString != null)
return hostPrefix + webApp.getInvocationDecoder().normalizeUri(path) + '?' + queryString;
else
return hostPrefix + webApp.getInvocationDecoder().normalizeUri(path);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static void addHex(StringBuilder cb, int hex)
{
int d1 = (hex >> 4) & 0xf;
int d2 = (hex) & 0xf;
cb.append('%');
cb.append(d1 < 10 ? (char) (d1 + '0') : (char) (d1 - 10 + 'a'));
cb.append(d2 < 10 ? (char) (d2 + '0') : (char) (d2 - 10 + 'a'));
}
/**
* Sets a header. This will override a previous header
* with the same name.
*
* @param name the header name
* @param value the header value
*/
@Override
public void setHeader(String name, String value)
{
_response.setHeader(name, value);
}
/**
* Adds a header. If another header with the same name exists, both
* will be sent to the client.
*
* @param name the header name
* @param value the header value
*/
public void addHeader(String name, String value)
{
_response.addHeader(name, value);
}
/**
* Returns true if the output headers include name
*
* @param name the header name to test
*/
public boolean containsHeader(String name)
{
return _response.containsHeader(name);
}
/**
* Sets a header by converting a date to a string.
*
*
To set the page to expire in 15 seconds use the following:
*
* long now = System.currentTime();
* response.setDateHeader("Expires", now + 15000);
*
*
* @param name name of the header
* @param date the date in milliseconds since the epoch.
*/
public void setDateHeader(String name, long date)
{
_response.setDateHeader(name, date);
}
/**
* Adds a header by converting a date to a string.
*
* @param name name of the header
* @param date the date in milliseconds since the epoch.
*/
public void addDateHeader(String name, long date)
{
_response.addDateHeader(name, date);
}
/**
* Sets a header by converting an integer value to a string.
*
* @param name name of the header
* @param value the value as an integer
*/
public void setIntHeader(String name, int value)
{
_response.setIntHeader(name, value);
}
/**
* Adds a header by converting an integer value to a string.
*
* @param name name of the header
* @param value the value as an integer
*/
public void addIntHeader(String name, int value)
{
_response.addIntHeader(name, value);
}
/**
* Adds a cookie to the response.
*
* @param cookie the response cookie
*/
@Override
public void addCookie(Cookie cookie)
{
_request.setHasCookie();
if (cookie == null)
return;
if (_cookiesOut == null)
_cookiesOut = new ArrayList();
_cookiesOut.add(cookie);
}
public Cookie getCookie(String name)
{
if (_cookiesOut == null)
return null;
for (int i = _cookiesOut.size() - 1; i >= 0; i--) {
Cookie cookie = _cookiesOut.get(i);
if (cookie.getName().equals(name))
return cookie;
}
return null;
}
public ArrayList getCookies()
{
return _cookiesOut;
}
public String getSessionId()
{
return _sessionId;
}
@Override
public void setSessionId(String id)
{
_sessionId = id;
// XXX: server/1315 vs server/0506 vs server/170k vs server/01r(e,f)
// could also set the nocache=JSESSIONID
WebApp webApp = _request.getWebApp();
if (webApp != null && webApp.getSessionManager().enableSessionCookies()) {
setPrivateOrResinCache(true);
}
}
protected void addServletCookie(WebApp webApp)
{
/* XXX:
if (_sessionId != null && ! _hasSessionCookie) {
_hasSessionCookie = true;
addServletCookie(webApp);
}
*/
// server/003a
if (_sessionId != null
&& webApp != null
&& webApp.getSessionManager().enableSessionCookies()) {
addCookie(createServletCookie(webApp));
}
}
protected Cookie createServletCookie(WebApp webApp)
{
SessionManager manager = webApp.getSessionManager();
String cookieName;
if (_request.isSecure())
cookieName = manager.getSSLCookieName();
else
cookieName = manager.getCookieName();
CookieImpl cookie = new CookieImpl(cookieName, _sessionId);
cookie.setVersion(manager.getCookieVersion());
String domain = webApp.generateCookieDomain(_request);
if (domain != null)
cookie.setDomain(domain);
long maxAge = manager.getCookieMaxAge();
if (maxAge > 0)
cookie.setMaxAge((int) (maxAge / 1000));
cookie.setPath(manager.getPath());
if (manager.getComment() != null)
cookie.setComment(manager.getComment());
cookie.setPort(manager.getCookiePort());
if (manager.isSecure()) {
// server/12zc (tck) vs server/01io (#4372)
/*
if (_request.isSecure())
cookie.setSecure(true);
*/
cookie.setSecure(true);
}
else if (manager.isCookieSecure()) {
if (_request.isSecure())
cookie.setSecure(true);
}
if (manager.isCookieHttpOnly()) {
cookie.setHttpOnly(true);
}
cookie.setSameSite(manager.getCookieSameSite());
return cookie;
}
/**
* Encodes session information in a URL. Calling this will enable
* sessions for users who have disabled cookies.
*
* @param string the url to encode
* @return a url with session information encoded
*/
@Override
public String encodeURL(String string)
{
HttpServletRequestImpl request = getRequest();
WebApp webApp = request.getWebApp();
if (webApp == null) {
return string;
}
if (request.isRequestedSessionIdFromCookie()) {
return string;
}
HttpSession session = request.getSession(false);
if (session == null) {
return string;
}
SessionManager sessionManager = webApp.getSessionManager();
if (! sessionManager.enableSessionUrls()) {
return string;
}
StringBuilder cb = new StringBuilder();
String altPrefix = sessionManager.getAlternateSessionPrefix();
if (altPrefix == null) {
// standard url rewriting
int p = string.indexOf('?');
if (p == 0) {
cb.append(string);
}
else if (p > 0) {
cb.append(string, 0, p);
cb.append(sessionManager.getSessionPrefix());
cb.append(session.getId());
cb.append(string, p, string.length());
}
else if ((p = string.indexOf('#')) >= 0) {
cb.append(string, 0, p);
cb.append(sessionManager.getSessionPrefix());
cb.append(session.getId());
cb.append(string, p, string.length());
}
else {
cb.append(string);
cb.append(sessionManager.getSessionPrefix());
cb.append(session.getId());
}
}
else {
int p = string.indexOf("://");
if (p < 0) {
cb.append(altPrefix);
cb.append(session.getId());
if (! string.startsWith("/")) {
cb.append(_request.getContextPath());
cb.append('/');
}
cb.append(string);
}
else {
int q = string.indexOf('/', p + 3);
if (q < 0) {
cb.append(string);
cb.append(altPrefix);
cb.append(session.getId());
}
else {
cb.append(string.substring(0, q));
cb.append(altPrefix);
cb.append(session.getId());
cb.append(string.substring(q));
}
}
}
return cb.toString();
}
@Override
public String encodeRedirectURL(String string)
{
String path = encodeURL(string);
String encoding = getCharacterEncoding();
boolean isLatin1 = "iso-8859-1".equals(encoding);
// server/1u3k, #4699
return escapeUrl(path, isLatin1);
}
/**
* @deprecated
*/
public String encodeRedirectUrl(String string)
{
return encodeRedirectURL(string);
}
/**
* @deprecated
*/
@Override
public String encodeUrl(String string)
{
return encodeURL(string);
}
//
// CauchoResponse methods
//
@Override
public AbstractResponseStream getResponseStream()
{
// jsp/(1cie, 1civ, 1ciw, 1cir), server/053y
if (_responseStream.getEncoding() == null) {
String encoding = getCharacterEncoding();
// server/053y
try {
_responseStream.setEncoding(encoding);
} catch (Exception e) {
}
}
return _responseStream;
}
@Override
public void setResponseStream(AbstractResponseStream responseStream)
{
_responseStream = responseStream;
if (_outputStream != null)
_outputStream.init(responseStream);
if (_writer != null)
_writer.init(responseStream);
}
@Override
public boolean isCauchoResponseStream()
{
return _responseStream.isCauchoResponseStream();
}
/*
public void setFlushBuffer(FlushBuffer out)
{
_response.setFlushBuffer(out);
}
public FlushBuffer getFlushBuffer()
{
return _response.getFlushBuffer();
}
*/
@Override
public String getHeader(String key)
{
return _response.getHeader(key);
}
ArrayList getHeaderKeys()
{
return _response.getHeaderKeys();
}
ArrayList getHeaderValues()
{
return _response.getHeaderValues();
}
@Override
public void setFooter(String key, String value)
{
_response.setFooter(key, value);
}
@Override
public void addFooter(String key, String value)
{
_response.addFooter(key, value);
}
// XXX: really close invocation
@Override
public void close()
throws IOException
{
// tck - jsp include
AbstractHttpResponse response = _response;
if (response != null) {
response.close();
}
}
/**
* When set to true, RequestDispatcher.forward() is disallowed on
* this stream.
*/
@Override
public void setForbidForward(boolean forbid)
{
_forbidForward = forbid;
}
/**
* Returns true if RequestDispatcher.forward() is disallowed on
* this stream.
*/
@Override
public boolean getForbidForward()
{
return _forbidForward;
}
@Override
public boolean hasError()
{
return _hasError;
}
@Override
public void setHasError(boolean error)
{
_hasError = error;
}
//
// HttpServletRequestImpl methods
//
@Override
public ServletResponse getResponse()
{
return null;
}
/*
public ServletResponse getResponse()
{
AbstractHttpResponse response = _response;
if (response == null)
throw new IllegalStateException(L.l("{0} is not longer valid because it has already been closed.",
this));
return response;
}
*/
@Override
public AbstractHttpResponse getAbstractHttpResponse()
{
return _response;
}
@Override
public int getStatus()
{
return _status;
}
public String getStatusMessage()
{
return _statusMessage;
}
public Collection getHeaders(String name)
{
// XXX: test
return _response.getHeaders(name);
}
public Collection getHeaderNames()
{
// XXX: test
return _response.getHeaderNames();
}
public void setForwardEnclosed(boolean isForwardEnclosed) {
}
public boolean isForwardEnclosed()
{
return false;
}
public void closeImpl()
throws IOException
{
AbstractHttpResponse response = _response;
_response = null;
if (response != null)
response.close();
}
@Override
public String toString()
{
return getClass().getSimpleName() + "[" + _response + "]";
}
static {
_errors = new HashMap();
_errors.put(100, "Continue");
_errors.put(101, "Switching Protocols");
_errors.put(200, "OK");
_errors.put(201, "Created");
_errors.put(202, "Accepted");
_errors.put(203, "Non-Authoritative Information");
_errors.put(204, "No Content");
_errors.put(205, "Reset Content");
_errors.put(206, "Partial Content");
_errors.put(300, "Multiple Choices");
_errors.put(301, "Moved Permanently");
_errors.put(302, "Found");
_errors.put(303, "See Other");
_errors.put(304, "Not Modified");
_errors.put(305, "Use Proxy");
_errors.put(307, "Temporary Redirect");
_errors.put(400, "Bad Request");
_errors.put(401, "Unauthorized");
_errors.put(402, "Payment Required");
_errors.put(403, "Forbidden");
_errors.put(404, "Not Found");
_errors.put(405, "Method Not Allowed");
_errors.put(406, "Not Acceptable");
_errors.put(407, "Proxy Authentication Required");
_errors.put(408, "Request Timeout");
_errors.put(409, "Conflict");
_errors.put(410, "Gone");
_errors.put(411, "Length Required");
_errors.put(412, "Precondition Failed");
_errors.put(413, "Request Entity Too Large");
_errors.put(414, "Request-URI Too Long");
_errors.put(415, "Unsupported Media Type");
_errors.put(416, "Requested Range Not Satisfiable");
_errors.put(417, "Expectation Failed");
_errors.put(500, "Internal Server Error");
_errors.put(501, "Not Implemented");
_errors.put(502, "Bad Gateway");
_errors.put(503, "Service Temporarily Unavailable");
_errors.put(504, "Gateway Timeout");
_errors.put(505, "Http Version Not Supported");
}
}