org.mortbay.jetty.HttpConnection Maven / Gradle / Ivy
//========================================================================
//$Id: HttpConnection.java,v 1.13 2005/11/25 21:01:45 gregwilkins Exp $
//Copyright 2004-2005 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 org.mortbay.jetty;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.mortbay.io.Buffer;
import org.mortbay.io.BufferCache.CachedBuffer;
import org.mortbay.io.Connection;
import org.mortbay.io.EndPoint;
import org.mortbay.io.RuntimeIOException;
import org.mortbay.io.nio.SelectChannelEndPoint;
import org.mortbay.log.Log;
import org.mortbay.resource.Resource;
import org.mortbay.util.QuotedStringTokenizer;
import org.mortbay.util.StringUtil;
import org.mortbay.util.URIUtil;
import org.mortbay.util.ajax.Continuation;
/**
*
* A HttpConnection represents the connection of a HTTP client to the server and
* is created by an instance of a {@link Connector}. It's prime function is to
* associate {@link Request} and {@link Response} instances with a
* {@link EndPoint}.
*
*
* A connection is also the prime mechanism used by jetty to recycle objects
* without pooling. The {@link Request},{@link Response}, {@link HttpParser},
* {@link HttpGenerator} and {@link HttpFields} instances are all recycled for
* the duration of a connection. Where appropriate, allocated buffers are also
* kept associated with the connection via the parser and/or generator.
*
*
*
* @author gregw
*
*/
public class HttpConnection implements Connection
{
private static int UNKNOWN = -2;
private static ThreadLocal __currentConnection = new ThreadLocal();
private long _timeStamp = System.currentTimeMillis();
private int _requests;
private boolean _handling;
private boolean _destroy;
protected final Connector _connector;
protected final EndPoint _endp;
protected final Server _server;
protected final HttpURI _uri;
protected final Parser _parser;
protected final HttpFields _requestFields;
protected final Request _request;
protected ServletInputStream _in;
protected final Generator _generator;
protected final HttpFields _responseFields;
protected final Response _response;
protected Output _out;
protected OutputWriter _writer;
protected PrintWriter _printWriter;
int _include;
private Object _associatedObject; // associated object
private transient int _expect = UNKNOWN;
private transient int _version = UNKNOWN;
private transient boolean _head = false;
private transient boolean _host = false;
private transient boolean _delayedHandling = false;
/* ------------------------------------------------------------ */
public static HttpConnection getCurrentConnection()
{
return (HttpConnection)__currentConnection.get();
}
/* ------------------------------------------------------------ */
protected static void setCurrentConnection(HttpConnection connection)
{
__currentConnection.set(connection);
}
/* ------------------------------------------------------------ */
/**
* Constructor
*
*/
public HttpConnection(Connector connector, EndPoint endpoint, Server server)
{
_uri = URIUtil.__CHARSET==StringUtil.__UTF8?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
_connector = connector;
_endp = endpoint;
_parser = new HttpParser(_connector,endpoint,new RequestHandler(),_connector.getHeaderBufferSize(),_connector.getRequestBufferSize());
_requestFields = new HttpFields();
_responseFields = new HttpFields();
_request = new Request(this);
_response = new Response(this);
_generator = new HttpGenerator(_connector,_endp,_connector.getHeaderBufferSize(),_connector.getResponseBufferSize());
_generator.setSendServerVersion(server.getSendServerVersion());
_server = server;
}
protected HttpConnection(Connector connector, EndPoint endpoint, Server server,
Parser parser, Generator generator, Request request)
{
_uri = URIUtil.__CHARSET==StringUtil.__UTF8?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);
_connector = connector;
_endp = endpoint;
_parser = parser;
_requestFields = new HttpFields();
_responseFields = new HttpFields();
_request = request;
_response = new Response(this);
_generator = generator;
_generator.setSendServerVersion(server.getSendServerVersion());
_server = server;
}
/* ------------------------------------------------------------ */
public void destroy()
{
synchronized (this)
{
_destroy = true;
if (!_handling)
{
if (_parser != null)
_parser.reset(true);
if (_generator != null)
_generator.reset(true);
if (_requestFields != null)
_requestFields.destroy();
if (_responseFields != null)
_responseFields.destroy();
}
}
}
/* ------------------------------------------------------------ */
/**
* @return the parser used by this connection
*/
public Parser getParser()
{
return _parser;
}
/* ------------------------------------------------------------ */
/**
* @return the number of requests handled by this connection
*/
public int getRequests()
{
return _requests;
}
/* ------------------------------------------------------------ */
/**
* @return The time this connection was established.
*/
public long getTimeStamp()
{
return _timeStamp;
}
/* ------------------------------------------------------------ */
/**
* @return Returns the associatedObject.
*/
public Object getAssociatedObject()
{
return _associatedObject;
}
/* ------------------------------------------------------------ */
/**
* @param associatedObject
* The associatedObject to set.
*/
public void setAssociatedObject(Object associatedObject)
{
_associatedObject = associatedObject;
}
/* ------------------------------------------------------------ */
/**
* @return Returns the connector.
*/
public Connector getConnector()
{
return _connector;
}
/* ------------------------------------------------------------ */
/**
* @return Returns the requestFields.
*/
public HttpFields getRequestFields()
{
return _requestFields;
}
/* ------------------------------------------------------------ */
/**
* @return Returns the responseFields.
*/
public HttpFields getResponseFields()
{
return _responseFields;
}
/* ------------------------------------------------------------ */
/**
* @return The result of calling {@link #getConnector}.
* {@link Connector#isConfidential(Request) isCondidential}
* (request), or false if there is no connector.
*/
public boolean isConfidential(Request request)
{
if (_connector != null)
return _connector.isConfidential(request);
return false;
}
/* ------------------------------------------------------------ */
/**
* Find out if the request is INTEGRAL security.
*
* @param request
* @return true
if there is a {@link #getConnector() connector}
* and it considers request
to be
* {@link Connector#isIntegral(Request) integral}
*/
public boolean isIntegral(Request request)
{
if (_connector != null)
return _connector.isIntegral(request);
return false;
}
/* ------------------------------------------------------------ */
/**
* @return The {@link EndPoint} for this connection.
*/
public EndPoint getEndPoint()
{
return _endp;
}
/* ------------------------------------------------------------ */
/**
* @return false
(this method is not yet implemented)
*/
public boolean getResolveNames()
{
return _connector.getResolveNames();
}
/* ------------------------------------------------------------ */
/**
* @return Returns the request.
*/
public Request getRequest()
{
return _request;
}
/* ------------------------------------------------------------ */
/**
* @return Returns the response.
*/
public Response getResponse()
{
return _response;
}
/* ------------------------------------------------------------ */
/**
* @return The input stream for this connection. The stream will be created
* if it does not already exist.
*/
public ServletInputStream getInputStream()
{
if (_in == null)
_in = new HttpParser.Input(((HttpParser)_parser),_connector.getMaxIdleTime());
return _in;
}
/* ------------------------------------------------------------ */
/**
* @return The output stream for this connection. The stream will be created
* if it does not already exist.
*/
public ServletOutputStream getOutputStream()
{
if (_out == null)
_out = new Output();
return _out;
}
/* ------------------------------------------------------------ */
/**
* @return A {@link PrintWriter} wrapping the {@link #getOutputStream output
* stream}. The writer is created if it does not already exist.
*/
public PrintWriter getPrintWriter(String encoding)
{
getOutputStream();
if (_writer == null)
{
_writer = new OutputWriter();
_printWriter = new PrintWriter(_writer)
{
/* ------------------------------------------------------------ */
/*
* @see java.io.PrintWriter#close()
*/
public void close()
{
try
{
out.close();
}
catch (IOException e)
{
Log.debug(e);
setError();
}
}
};
}
_writer.setCharacterEncoding(encoding);
return _printWriter;
}
/* ------------------------------------------------------------ */
public boolean isResponseCommitted()
{
return _generator.isCommitted();
}
/* ------------------------------------------------------------ */
public void handle() throws IOException
{
// Loop while more in buffer
boolean more_in_buffer = true; // assume true until proven otherwise
int no_progress = 0;
while (more_in_buffer)
{
try
{
synchronized (this)
{
if (_handling)
throw new IllegalStateException(); // TODO delete this
// check
_handling = true;
}
setCurrentConnection(this);
long io = 0;
Continuation continuation = _request.getContinuation();
if (continuation != null && continuation.isPending())
{
Log.debug("resume continuation {}",continuation);
if (_request.getMethod() == null)
throw new IllegalStateException();
handleRequest();
}
else
{
// If we are not ended then parse available
if (!_parser.isComplete())
io = _parser.parseAvailable();
// Do we have more generating to do?
// Loop here because some writes may take multiple steps and
// we need to flush them all before potentially blocking in
// the
// next loop.
while (_generator.isCommitted() && !_generator.isComplete())
{
long written = _generator.flush();
io += written;
if (written <= 0)
break;
if (_endp.isBufferingOutput())
_endp.flush();
}
// Flush buffers
if (_endp.isBufferingOutput())
{
_endp.flush();
if (!_endp.isBufferingOutput())
no_progress = 0;
}
if (io > 0)
no_progress = 0;
else if (no_progress++ >= 2)
return;
}
}
catch (HttpException e)
{
if (Log.isDebugEnabled())
{
Log.debug("uri=" + _uri);
Log.debug("fields=" + _requestFields);
Log.debug(e);
}
_generator.sendError(e.getStatus(),e.getReason(),null,true);
_parser.reset(true);
_endp.close();
throw e;
}
finally
{
setCurrentConnection(null);
more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput();
synchronized (this)
{
_handling = false;
if (_destroy)
{
destroy();
return;
}
}
if (_parser.isComplete() && _generator.isComplete() && !_endp.isBufferingOutput())
{
if (!_generator.isPersistent())
{
_parser.reset(true);
more_in_buffer = false;
}
if (more_in_buffer)
{
reset(false);
more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput();
}
else
reset(true);
no_progress = 0;
}
Continuation continuation = _request.getContinuation();
if (continuation != null && continuation.isPending())
{
break;
}
else if (_generator.isCommitted() && !_generator.isComplete() && _endp instanceof SelectChannelEndPoint) // TODO
// remove
// SelectChannel
// dependency
((SelectChannelEndPoint)_endp).setWritable(false);
}
}
}
/* ------------------------------------------------------------ */
public void reset(boolean returnBuffers)
{
_parser.reset(returnBuffers); // TODO maybe only release when low on
// resources
_requestFields.clear();
_request.recycle();
_generator.reset(returnBuffers); // TODO maybe only release when low on
// resources
_responseFields.clear();
_response.recycle();
_uri.clear();
}
/* ------------------------------------------------------------ */
protected void handleRequest() throws IOException
{
if (_server.isRunning())
{
boolean retrying = false;
boolean error = false;
String threadName = null;
String info=null;
try
{
info = URIUtil.canonicalPath(_uri.getDecodedPath());
if (info == null)
throw new HttpException(400);
_request.setPathInfo(info);
if (_out != null)
_out.reopen();
if (Log.isDebugEnabled())
{
threadName = Thread.currentThread().getName();
Thread.currentThread().setName(threadName + " - " + _uri);
}
_connector.customize(_endp,_request);
_server.handle(this);
}
catch (RetryRequest r)
{
if (Log.isDebugEnabled())
Log.ignore(r);
retrying = true;
}
catch (EofException e)
{
Log.ignore(e);
error = true;
}
catch (HttpException e)
{
Log.debug(e);
_request.setHandled(true);
_response.sendError(e.getStatus(),e.getReason());
error = true;
}
catch (RuntimeIOException e)
{
Log.debug(e);
_request.setHandled(true);
error = true;
}
catch (Throwable e)
{
if (e instanceof ThreadDeath)
throw (ThreadDeath)e;
if (info==null)
{
Log.warn(_uri+": "+e);
Log.debug(e);
_request.setHandled(true);
_generator.sendError(400,null,null,true);
}
else
{
Log.warn(""+_uri,e);
_request.setHandled(true);
_generator.sendError(500,null,null,true);
}
error = true;
}
finally
{
if (threadName != null)
Thread.currentThread().setName(threadName);
if (!retrying)
{
if (_request.getContinuation() != null)
{
Log.debug("continuation still pending {}");
_request.getContinuation().reset();
}
if (_endp.isOpen())
{
if (_generator.isPersistent())
_connector.persist(_endp);
if (error)
_endp.close();
else
{
if (!_response.isCommitted() && !_request.isHandled())
_response.sendError(HttpServletResponse.SC_NOT_FOUND);
_response.complete();
}
}
else
{
_response.complete(); // TODO ????????????
}
}
}
}
}
/* ------------------------------------------------------------ */
public void commitResponse(boolean last) throws IOException
{
if (!_generator.isCommitted())
{
_generator.setResponse(_response.getStatus(),_response.getReason());
try
{
_generator.completeHeader(_responseFields,last);
}
catch(IOException io)
{
throw io;
}
catch(RuntimeException e)
{
Log.warn("header full: "+e);
if (Log.isDebugEnabled() && _generator instanceof HttpGenerator)
Log.debug(((HttpGenerator)_generator)._header.toDetailString(),e);
_response.reset();
_generator.reset(true);
_generator.setResponse(HttpStatus.ORDINAL_500_Internal_Server_Error,null);
_generator.completeHeader(_responseFields,HttpGenerator.LAST);
_generator.complete();
throw e;
}
}
if (last)
_generator.complete();
}
/* ------------------------------------------------------------ */
public void completeResponse() throws IOException
{
if (!_generator.isCommitted())
{
_generator.setResponse(_response.getStatus(),_response.getReason());
try
{
_generator.completeHeader(_responseFields,HttpGenerator.LAST);
}
catch(IOException io)
{
throw io;
}
catch(RuntimeException e)
{
Log.warn("header full: "+e);
Log.debug(e);
_response.reset();
_generator.reset(true);
_generator.setResponse(HttpStatus.ORDINAL_500_Internal_Server_Error,null);
_generator.completeHeader(_responseFields,HttpGenerator.LAST);
_generator.complete();
throw e;
}
}
_generator.complete();
}
/* ------------------------------------------------------------ */
public void flushResponse() throws IOException
{
try
{
commitResponse(HttpGenerator.MORE);
_generator.flush();
}
catch (IOException e)
{
throw (e instanceof EofException)?e:new EofException(e);
}
}
/* ------------------------------------------------------------ */
public Generator getGenerator()
{
return _generator;
}
/* ------------------------------------------------------------ */
public boolean isIncluding()
{
return _include > 0;
}
/* ------------------------------------------------------------ */
public void include()
{
_include++;
}
/* ------------------------------------------------------------ */
public void included()
{
_include--;
if (_out != null)
_out.reopen();
}
/* ------------------------------------------------------------ */
public boolean isIdle()
{
return _generator.isIdle() && (_parser.isIdle() || _delayedHandling);
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
private class RequestHandler extends HttpParser.EventHandler
{
private String _charset;
/*
*
* @see
* org.mortbay.jetty.HttpParser.EventHandler#startRequest(org.mortbay
* .io.Buffer, org.mortbay.io.Buffer, org.mortbay.io.Buffer)
*/
public void startRequest(Buffer method, Buffer uri, Buffer version) throws IOException
{
_host = false;
_expect = UNKNOWN;
_delayedHandling = false;
_charset = null;
if (_request.getTimeStamp() == 0)
_request.setTimeStamp(System.currentTimeMillis());
_request.setMethod(method.toString());
try
{
_uri.parse(uri.array(),uri.getIndex(),uri.length());
_request.setUri(_uri);
if (version == null)
{
_request.setProtocol(HttpVersions.HTTP_0_9);
_version = HttpVersions.HTTP_0_9_ORDINAL;
}
else
{
version = HttpVersions.CACHE.get(version);
_version = HttpVersions.CACHE.getOrdinal(version);
if (_version <= 0)
_version = HttpVersions.HTTP_1_0_ORDINAL;
_request.setProtocol(version.toString());
}
_head = method == HttpMethods.HEAD_BUFFER; // depends on method
// being decached.
}
catch (Exception e)
{
throw new HttpException(HttpStatus.ORDINAL_400_Bad_Request,null,e);
}
}
/*
* @see
* org.mortbay.jetty.HttpParser.EventHandler#parsedHeaderValue(org.mortbay
* .io.Buffer)
*/
public void parsedHeader(Buffer name, Buffer value)
{
int ho = HttpHeaders.CACHE.getOrdinal(name);
switch (ho)
{
case HttpHeaders.HOST_ORDINAL:
// TODO check if host matched a host in the URI.
_host = true;
break;
case HttpHeaders.EXPECT_ORDINAL:
value = HttpHeaderValues.CACHE.lookup(value);
_expect = HttpHeaderValues.CACHE.getOrdinal(value);
break;
case HttpHeaders.ACCEPT_ENCODING_ORDINAL:
case HttpHeaders.USER_AGENT_ORDINAL:
value = HttpHeaderValues.CACHE.lookup(value);
break;
case HttpHeaders.CONTENT_TYPE_ORDINAL:
value = MimeTypes.CACHE.lookup(value);
_charset = MimeTypes.getCharsetFromContentType(value);
break;
case HttpHeaders.CONNECTION_ORDINAL:
// looks rather clumsy, but the idea is to optimize for a
// single valued header
int ordinal = HttpHeaderValues.CACHE.getOrdinal(value);
switch (ordinal)
{
case -1:
{
String[] values = value.toString().split(",");
for (int i = 0; values != null && i < values.length; i++)
{
CachedBuffer cb = HttpHeaderValues.CACHE.get(values[i].trim());
if (cb != null)
{
switch (cb.getOrdinal())
{
case HttpHeaderValues.CLOSE_ORDINAL:
_responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
_generator.setPersistent(false);
break;
case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
if (_version == HttpVersions.HTTP_1_0_ORDINAL)
_responseFields.add(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE_BUFFER);
break;
}
}
}
break;
}
case HttpHeaderValues.CLOSE_ORDINAL:
_responseFields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
_generator.setPersistent(false);
break;
case HttpHeaderValues.KEEP_ALIVE_ORDINAL:
if (_version == HttpVersions.HTTP_1_0_ORDINAL)
_responseFields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE_BUFFER);
break;
}
}
_requestFields.add(name,value);
}
/*
* @see org.mortbay.jetty.HttpParser.EventHandler#headerComplete()
*/
public void headerComplete() throws IOException
{
_requests++;
_generator.setVersion(_version);
switch (_version)
{
case HttpVersions.HTTP_0_9_ORDINAL:
break;
case HttpVersions.HTTP_1_0_ORDINAL:
_generator.setHead(_head);
break;
case HttpVersions.HTTP_1_1_ORDINAL:
_generator.setHead(_head);
if (_server.getSendDateHeader())
_responseFields.put(HttpHeaders.DATE_BUFFER,_request.getTimeStampBuffer(),_request.getTimeStamp());
if (!_host)
{
_generator.setResponse(HttpStatus.ORDINAL_400_Bad_Request,null);
_responseFields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER);
_generator.completeHeader(_responseFields,true);
_generator.complete();
return;
}
if (_expect != UNKNOWN)
{
if (_expect == HttpHeaderValues.CONTINUE_ORDINAL)
{
// TODO delay sending 100 response until a read is
// attempted.
if (((HttpParser)_parser).getHeaderBuffer() == null || ((HttpParser)_parser).getHeaderBuffer().length() < 2)
{
_generator.setResponse(HttpStatus.ORDINAL_100_Continue,null);
_generator.completeHeader(null,true);
_generator.complete();
_generator.reset(false);
}
}
else if (_expect == HttpHeaderValues.PROCESSING_ORDINAL)
{
}
else
{
_generator.sendError(HttpStatus.ORDINAL_417_Expectation_Failed,null,null,true);
return;
}
}
break;
default:
}
if (_charset != null)
_request.setCharacterEncodingUnchecked(_charset);
// Either handle now or wait for first content
if (((HttpParser)_parser).getContentLength() <= 0 && !((HttpParser)_parser).isChunking())
handleRequest();
else
_delayedHandling = true;
}
/* ------------------------------------------------------------ */
/*
* @see org.mortbay.jetty.HttpParser.EventHandler#content(int,
* org.mortbay.io.Buffer)
*/
public void content(Buffer ref) throws IOException
{
if (_delayedHandling)
{
_delayedHandling = false;
handleRequest();
}
}
/*
* (non-Javadoc)
*
* @see org.mortbay.jetty.HttpParser.EventHandler#messageComplete(int)
*/
public void messageComplete(long contentLength) throws IOException
{
if (_delayedHandling)
{
_delayedHandling = false;
handleRequest();
}
}
/*
* (non-Javadoc)
*
* @see
* org.mortbay.jetty.HttpParser.EventHandler#startResponse(org.mortbay
* .io.Buffer, int, org.mortbay.io.Buffer)
*/
public void startResponse(Buffer version, int status, Buffer reason)
{
Log.debug("Bad request!: " + version + " " + status + " " + reason);
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
public class Output extends AbstractGenerator.Output
{
Output()
{
super((AbstractGenerator)HttpConnection.this._generator,_connector.getMaxIdleTime());
}
/* ------------------------------------------------------------ */
/*
* @see java.io.OutputStream#close()
*/
public void close() throws IOException
{
if (_closed)
return;
if (!isIncluding() && !_generator.isCommitted())
commitResponse(HttpGenerator.LAST);
else
flushResponse();
super.close();
}
/* ------------------------------------------------------------ */
/*
* @see java.io.OutputStream#flush()
*/
public void flush() throws IOException
{
if (!_generator.isCommitted())
commitResponse(HttpGenerator.MORE);
super.flush();
}
/* ------------------------------------------------------------ */
/*
* @see javax.servlet.ServletOutputStream#print(java.lang.String)
*/
public void print(String s) throws IOException
{
if (_closed)
throw new IOException("Closed");
PrintWriter writer = getPrintWriter(null);
writer.print(s);
}
/* ------------------------------------------------------------ */
public void sendResponse(Buffer response) throws IOException
{
((HttpGenerator)_generator).sendResponse(response);
}
/* ------------------------------------------------------------ */
public void sendContent(Object content) throws IOException
{
Resource resource = null;
if (_closed)
throw new IOException("Closed");
if (_generator.getContentWritten() > 0)
throw new IllegalStateException("!empty");
if (content instanceof HttpContent)
{
HttpContent c = (HttpContent)content;
Buffer contentType = c.getContentType();
if (contentType != null && !_responseFields.containsKey(HttpHeaders.CONTENT_TYPE_BUFFER))
{
String enc = _response.getSetCharacterEncoding();
if(enc==null)
_responseFields.add(HttpHeaders.CONTENT_TYPE_BUFFER, contentType);
else
{
if(contentType instanceof CachedBuffer)
{
CachedBuffer content_type = ((CachedBuffer)contentType).getAssociate(enc);
if(content_type!=null)
_responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER, content_type);
else
{
_responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER,
contentType+";charset="+QuotedStringTokenizer.quote(enc,";= "));
}
}
else
{
_responseFields.put(HttpHeaders.CONTENT_TYPE_BUFFER,
contentType+";charset="+QuotedStringTokenizer.quote(enc,";= "));
}
}
}
if (c.getContentLength() > 0)
_responseFields.putLongField(HttpHeaders.CONTENT_LENGTH_BUFFER,c.getContentLength());
Buffer lm = c.getLastModified();
long lml = c.getResource().lastModified();
if (lm != null)
_responseFields.put(HttpHeaders.LAST_MODIFIED_BUFFER,lm,lml);
else if (c.getResource() != null)
{
if (lml != -1)
_responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER,lml);
}
content = c.getBuffer();
if (content == null)
content = c.getInputStream();
}
else if (content instanceof Resource)
{
resource = (Resource)content;
_responseFields.putDateField(HttpHeaders.LAST_MODIFIED_BUFFER,resource.lastModified());
content = resource.getInputStream();
}
if (content instanceof Buffer)
{
_generator.addContent((Buffer)content,HttpGenerator.LAST);
commitResponse(HttpGenerator.LAST);
}
else if (content instanceof InputStream)
{
InputStream in = (InputStream)content;
try
{
int max = _generator.prepareUncheckedAddContent();
Buffer buffer = _generator.getUncheckedBuffer();
int len = buffer.readFrom(in,max);
while (len >= 0)
{
_generator.completeUncheckedAddContent();
_out.flush();
max = _generator.prepareUncheckedAddContent();
buffer = _generator.getUncheckedBuffer();
len = buffer.readFrom(in,max);
}
_generator.completeUncheckedAddContent();
_out.flush();
}
finally
{
if (resource != null)
resource.release();
else
in.close();
}
}
else
throw new IllegalArgumentException("unknown content type?");
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
public class OutputWriter extends AbstractGenerator.OutputWriter
{
OutputWriter()
{
super(HttpConnection.this._out);
}
}
}