// ========================================================================
// $Id: ServletHttpResponse.java,v 1.65 2006/04/04 22:28:05 gregwilkins Exp $
// Copyright 2000-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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 net.lightbody.bmp.proxy.jetty.jetty.servlet;
import net.lightbody.bmp.proxy.jetty.http.HttpContext;
import net.lightbody.bmp.proxy.jetty.http.HttpFields;
import net.lightbody.bmp.proxy.jetty.http.HttpOutputStream;
import net.lightbody.bmp.proxy.jetty.http.HttpResponse;
import net.lightbody.bmp.proxy.jetty.log.LogFactory;
import net.lightbody.bmp.proxy.jetty.util.*;
import org.apache.commons.logging.Log;
import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Locale;
/* ------------------------------------------------------------ */
/** Servlet Response Wrapper.
* This class wraps a Jetty HTTP response as a 2.2 Servlet
* response.
*
* Note that this wrapper is not synchronized and if a response is to
* be operated on by multiple threads, then higher level
* synchronizations may be required.
*
* @version $Id: ServletHttpResponse.java,v 1.65 2006/04/04 22:28:05 gregwilkins Exp $
* @author Greg Wilkins (gregw)
*/
public class ServletHttpResponse implements HttpServletResponse
{
private static Log log = LogFactory.getLog(ServletHttpResponse.class);
public static final int
DISABLED=-1,
NO_OUT=0,
OUTPUTSTREAM_OUT=1,
WRITER_OUT=2;
private static ServletWriter __nullServletWriter;
private static ServletOut __nullServletOut;
static
{
try{
__nullServletWriter =
new ServletWriter(IO.getNullStream());
__nullServletOut =
new ServletOut(IO.getNullStream());
}
catch (Exception e)
{
log.fatal(e); System.exit(1);
}
}
/* ------------------------------------------------------------ */
private HttpResponse _httpResponse;
private ServletHttpRequest _servletHttpRequest;
private int _outputState=NO_OUT;
private ServletOut _out =null;
private ServletWriter _writer=null;
private HttpSession _session=null;
private boolean _noSession=false;
private Locale _locale=null;
private boolean _charEncodingSetInContentType=false;
/* ------------------------------------------------------------ */
public ServletHttpResponse(ServletHttpRequest request,HttpResponse response)
{
_servletHttpRequest=request;
_servletHttpRequest.setServletHttpResponse(this);
_httpResponse=response;
}
/* ------------------------------------------------------------ */
void recycle()
{
_outputState=NO_OUT;
_out=null;
_writer=null;
_session=null;
_noSession=false;
_locale=null;
_charEncodingSetInContentType=false;
}
/* ------------------------------------------------------------ */
int getOutputState()
{
return _outputState;
}
/* ------------------------------------------------------------ */
void setOutputState(int s)
throws IOException
{
if (s<0)
{
_outputState=DISABLED;
if (_writer!=null)
_writer.disable();
_writer=null;
if (_out!=null)
_out.disable();
_out=null;
}
else
_outputState=s;
}
/* ------------------------------------------------------------ */
HttpResponse getHttpResponse()
{
return _httpResponse;
}
/* ------------------------------------------------------------ */
void commit()
throws IOException
{
if (_writer!=null && _writer.isWritten())
_writer.flush();
else
_httpResponse.commit();
}
/* ------------------------------------------------------------ */
void complete()
throws IOException
{
_httpResponse.completing();
commit();
setOutputState(DISABLED);
}
/* ------------------------------------------------------------ */
public boolean isCommitted()
{
return _httpResponse.isCommitted();
}
/* ------------------------------------------------------------ */
boolean isDirty()
{
return _httpResponse.isDirty();
}
/* ------------------------------------------------------------ */
public void setBufferSize(int size)
{
HttpOutputStream out = (HttpOutputStream)_httpResponse.getOutputStream();
if (out.isWritten() || _writer!=null && _writer.isWritten())
throw new IllegalStateException("Output written");
out.setBufferSize(size);
}
/* ------------------------------------------------------------ */
public int getBufferSize()
{
return ((HttpOutputStream)_httpResponse.getOutputStream()).getBufferSize();
}
/* ------------------------------------------------------------ */
public void flushBuffer()
throws IOException
{
if (((HttpOutputStream)_httpResponse.getOutputStream()).isClosed())
return;
if (_writer!=null)
_writer.flush();
if (_out!=null)
_out.flush();
if (_writer==null && _out==null)
_httpResponse.getOutputStream().flush();
if (!_httpResponse.isCommitted())
_httpResponse.commit();
}
/* ------------------------------------------------------------ */
public void resetBuffer()
{
if (isCommitted())
throw new IllegalStateException("Committed");
((HttpOutputStream)_httpResponse.getOutputStream()).resetBuffer();
if (_writer!=null) _writer.reset();
}
/* ------------------------------------------------------------ */
public void reset()
{
resetBuffer();
_httpResponse.reset();
}
/* ------------------------------------------------------------ */
/**
* Sets the locale of the response, setting the headers (including the
* Content-Type's charset) as appropriate. This method should be called
* before a call to {@link #getWriter}. By default, the response locale
* is the default locale for the server.
*
* @see #getLocale
* @param locale the Locale of the response
*/
public void setLocale(Locale locale)
{
if (locale == null || isCommitted())
return;
_locale = locale;
setHeader(HttpFields.__ContentLanguage,locale.toString().replace('_','-'));
if (this._outputState==0)
{
/* get current MIME type from Content-Type header */
String type=_httpResponse.getField(HttpFields.__ContentType);
if (type==null)
{
// servlet did not set Content-Type yet
// so lets assume default one
type="application/octet-stream";
}
HttpContext httpContext=_servletHttpRequest.getServletHandler().getHttpContext();
if (httpContext instanceof ServletHttpContext)
{
String charset = ((ServletHttpContext)httpContext).getLocaleEncoding(locale);
if (charset != null && charset.length()>0)
{
int semi=type.indexOf(';');
if (semi<0)
type += "; charset="+charset;
else if (!_charEncodingSetInContentType)
type = type.substring(0,semi)+"; charset="+charset;
setHeader(HttpFields.__ContentType,type);
}
}
}
}
/* ------------------------------------------------------------ */
public Locale getLocale()
{
if (_locale==null)
return Locale.getDefault();
return _locale;
}
/* ------------------------------------------------------------ */
public void addCookie(Cookie cookie)
{
_httpResponse.addSetCookie(cookie);
}
/* ------------------------------------------------------------ */
public boolean containsHeader(String name)
{
return _httpResponse.containsField(name);
}
/* ------------------------------------------------------------ */
public String encodeURL(String url)
{
// should not encode if cookies in evidence
if (_servletHttpRequest==null ||
_servletHttpRequest.isRequestedSessionIdFromCookie() &&
_servletHttpRequest.getServletHandler().isUsingCookies())
return url;
// get session;
if (_session==null && !_noSession)
{
_session=_servletHttpRequest.getSession(false);
_noSession=(_session==null);
}
// no session or no url
if (_session == null || url==null)
return url;
// invalid session
String id = _session.getId();
if (id == null)
return url;
// Check host and port are for this server
// TODO not implemented
// Already encoded
int prefix=url.indexOf(SessionManager.__SessionUrlPrefix);
if (prefix!=-1)
{
int suffix=url.indexOf("?",prefix);
if (suffix<0)
suffix=url.indexOf("#",prefix);
if (suffix<=prefix)
return url.substring(0,prefix+SessionManager.__SessionUrlPrefix.length())+id;
return url.substring(0,prefix+SessionManager.__SessionUrlPrefix.length())+id+
url.substring(suffix);
}
// edit the session
int suffix=url.indexOf('?');
if (suffix<0)
suffix=url.indexOf('#');
if (suffix<0)
return url+SessionManager.__SessionUrlPrefix+id;
return url.substring(0,suffix)+
SessionManager.__SessionUrlPrefix+id+url.substring(suffix);
}
/* ------------------------------------------------------------ */
public String encodeRedirectURL(String url)
{
return encodeURL(url);
}
/* ------------------------------------------------------------ */
/**
* @deprecated As of version 2.1, use encodeURL(String url) instead
*/
public String encodeUrl(String url)
{
return encodeURL(url);
}
/* ------------------------------------------------------------ */
/**
* @deprecated As of version 2.1, use
* encodeRedirectURL(String url) instead
*/
public String encodeRedirectUrl(String url)
{
return encodeRedirectURL(url);
}
/* ------------------------------------------------------------ */
public void sendError(int status, String message)
throws IOException
{
// Find error page.
String error_page =
_servletHttpRequest.getServletHandler().getErrorPage(status,_servletHttpRequest);
resetBuffer();
// Handle error page?
if (error_page==null)
{
// handle normally
_httpResponse.sendError(status,message);
}
else
{
_httpResponse.setStatus(status,message);
if (message == null)
{
message= (String)HttpResponse.__statusMsg.get(TypeUtil.newInteger(status));
if (message == null)
message= "" + status;
}
// handle error page
ServletHolder holder = _servletHttpRequest.getServletHolder();
if (holder!=null)
_servletHttpRequest.setAttribute(ServletHandler.__J_S_ERROR_SERVLET_NAME,
holder.getName());
_servletHttpRequest.setAttribute(ServletHandler.__J_S_ERROR_REQUEST_URI,
_servletHttpRequest.getRequestURI());
_servletHttpRequest.setAttribute(ServletHandler.__J_S_ERROR_STATUS_CODE,
new Integer(status));
_servletHttpRequest.setAttribute(ServletHandler.__J_S_ERROR_MESSAGE,
message);
RequestDispatcher dispatcher=
_servletHttpRequest.getServletHandler().getServletContext()
.getRequestDispatcher(error_page);
try
{
((Dispatcher)dispatcher).error(_servletHttpRequest,this);
}
catch(ServletException e)
{
log.warn(LogSupport.EXCEPTION,e);
_httpResponse.sendError(status,message);
}
}
complete();
}
/* ------------------------------------------------------------ */
public void sendError(int status)
throws IOException
{
sendError(status,null);
}
/* ------------------------------------------------------------ */
public void sendRedirect(String url)
throws IOException
{
if (url==null)
throw new IllegalArgumentException();
if (!URI.hasScheme(url))
{
StringBuffer buf = _servletHttpRequest.getHttpRequest().getRootURL();
if (url.startsWith("/"))
buf.append(URI.canonicalPath(url));
else
{
String path=_servletHttpRequest.getRequestURI();
String parent=(path.endsWith("/"))?path:URI.parentPath(path);
url=URI.canonicalPath(URI.addPaths(parent,url));
if (!url.startsWith("/"))
buf.append('/');
buf.append(url);
}
url=buf.toString();
}
resetBuffer();
_httpResponse.setField(HttpFields.__Location,url);
_httpResponse.setStatus(HttpResponse.__302_Moved_Temporarily);
complete();
}
/* ------------------------------------------------------------ */
public void setDateHeader(String name, long value)
{
try{_httpResponse.setDateField(name,value);}
catch(IllegalStateException e){LogSupport.ignore(log,e);}
}
/* ------------------------------------------------------------ */
public void setHeader(String name, String value)
{
try{_httpResponse.setField(name,value);}
catch(IllegalStateException e){LogSupport.ignore(log,e);}
}
/* ------------------------------------------------------------ */
public void setIntHeader(String name, int value)
{
try{_httpResponse.setIntField(name,value);}
catch(IllegalStateException e){LogSupport.ignore(log,e);}
}
/* ------------------------------------------------------------ */
public void addDateHeader(String name, long value)
{
try{_httpResponse.addDateField(name,value);}
catch(IllegalStateException e){LogSupport.ignore(log,e);}
}
/* ------------------------------------------------------------ */
public void addHeader(String name, String value)
{
try{_httpResponse.addField(name,value);}
catch(IllegalStateException e){LogSupport.ignore(log,e);}
}
/* ------------------------------------------------------------ */
public void addIntHeader(String name, int value)
{
try{_httpResponse.addIntField(name,value);}
catch(IllegalStateException e){LogSupport.ignore(log,e);}
}
/* ------------------------------------------------------------ */
public void setStatus(int status)
{
_httpResponse.setStatus(status);
}
/* ------------------------------------------------------------ */
/**
* @deprecated As of version 2.1 of the Servlet spec.
* To set a status code
* use <code>setStatus(int)</code>, to send an error with a description
* use <code>sendError(int, String)</code>.
*
* Sets the status code and message for this response.
*
* @param status the status code
* @param message the status message
*/
public void setStatus(int status, String message)
{
setStatus(status);
_httpResponse.setReason(message);
}
/* ------------------------------------------------------------ */
public ServletOutputStream getOutputStream()
{
if (_outputState==DISABLED)
return __nullServletOut;
if (_outputState!=NO_OUT && _outputState!=OUTPUTSTREAM_OUT)
throw new IllegalStateException();
if (_writer!=null)
{
_writer.flush();
_writer.disable();
_writer=null;
}
if (_out==null)
_out = new ServletOut(_servletHttpRequest.getHttpRequest()
.getOutputStream());
_outputState=OUTPUTSTREAM_OUT;
return _out;
}
/* ------------------------------------------------------------ */
public PrintWriter getWriter()
throws java.io.IOException
{
if (_outputState==DISABLED)
return __nullServletWriter;
if (_outputState!=NO_OUT && _outputState!=WRITER_OUT)
throw new IllegalStateException();
// If we are switching modes, flush output to try avoid overlaps.
if (_out!=null)
_out.flush();
/* if there is no writer yet */
if (_writer==null)
{
/* get encoding from Content-Type header */
String encoding = _httpResponse.getCharacterEncoding();
if (encoding==null)
{
if (_servletHttpRequest!=null)
{
/* implementation of educated defaults */
String mimeType = _httpResponse.getMimeType();
encoding = _servletHttpRequest.getServletHandler()
.getHttpContext().getEncodingByMimeType(mimeType);
}
if (encoding==null)
encoding = StringUtil.__ISO_8859_1;
_httpResponse.setCharacterEncoding(encoding,true);
}
/* construct Writer using correct encoding */
_writer = new ServletWriter(_httpResponse.getOutputStream(), encoding);
}
_outputState=WRITER_OUT;
return _writer;
}
/* ------------------------------------------------------------ */
public void setContentLength(int len)
{
// Protect from setting after committed as default handling
// of a servlet HEAD request ALWAYS sets content length, even
// if the getHandling committed the response!
if (!isCommitted())
setIntHeader(HttpFields.__ContentLength,len);
}
/* ------------------------------------------------------------ */
public String getContentType()
{
return _httpResponse.getContentType();
}
/* ------------------------------------------------------------ */
public void setContentType(String contentType)
{
if (isCommitted() || contentType==null)
return;
int semi=contentType.indexOf(';');
if (semi>0)
{
int charset0=contentType.indexOf("charset=",semi);
if (charset0>0)
{
if (_outputState==WRITER_OUT)
{
// need to strip charset= from params
int charset1=contentType.indexOf(' ',charset0);
if ((charset0==semi+1 && charset1<0) ||
(charset0==semi+2 && charset1<0 && contentType.charAt(semi+1)==' ' ))
_httpResponse.setContentType(contentType.substring(0,semi));
else if (charset1<0)
_httpResponse.setContentType(contentType.substring(0, charset0).trim());
else
_httpResponse.setContentType(contentType.substring(0, charset0)+contentType.substring(charset1));
}
else
{
_charEncodingSetInContentType=true;
_httpResponse.setContentType(contentType);
}
}
else
_httpResponse.setContentType(contentType);
}
else
_httpResponse.setContentType(contentType);
if (_locale!=null)
setLocale(_locale);
}
/* ------------------------------------------------------------ */
public void setCharacterEncoding(String encoding)
{
if (this._outputState==0 && !isCommitted())
{
_charEncodingSetInContentType=true;
_httpResponse.setCharacterEncoding(encoding,true);
}
}
/* ------------------------------------------------------------ */
public String getCharacterEncoding()
{
String encoding=_httpResponse.getCharacterEncoding();
return (encoding==null)?StringUtil.__ISO_8859_1:encoding;
}
/* ------------------------------------------------------------ */
public String toString()
{
return _httpResponse.toString();
}
/* ------------------------------------------------------------ */
/** Unwrap a ServletResponse.
*
* @see javax.servlet.ServletResponseWrapper
* @see javax.servlet.http.HttpServletResponseWrapper
* @param response
* @return The core ServletHttpResponse which must be the
* underlying response object
*/
public static ServletHttpResponse unwrap(ServletResponse response)
{
while (!(response instanceof ServletHttpResponse))
{
if (response instanceof ServletResponseWrapper)
{
ServletResponseWrapper wrapper =
(ServletResponseWrapper)response;
response=wrapper.getResponse();
}
else
throw new IllegalArgumentException("Does not wrap ServletHttpResponse");
}
return (ServletHttpResponse)response;
}
}