/* * ResponseEntity.java February 2001 * * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net> * * 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.simpleframework.http.core; import static org.simpleframework.http.Protocol.CONTENT_LENGTH; import static org.simpleframework.http.core.ContainerEvent.WRITE_HEADER; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.nio.channels.WritableByteChannel; import java.util.Map; import org.simpleframework.http.ContentType; import org.simpleframework.http.Request; import org.simpleframework.http.Response; import org.simpleframework.http.message.Entity; import org.simpleframework.transport.Channel; import org.simpleframework.transport.Sender; import org.simpleframework.transport.trace.Trace; /** * This is used to represent the HTTP response. This provides methods that can * be used to set various characteristics of the response. The * <code>OutputStream</code> of the <code>Response</code> can be retrieved from * this interface as can the I.P address of the client that will be receiving * the <code>Response</code>. The attributes of the connection can be retrieved * also. This provides a set of methods that can be used to set the attributes * of the stream so the <code>Response</code> can be transported properly. The * headers can be set and will be sent once a commit is made, or when there is * content sent over the output stream. * <p> * This should never allow the message body be sent if it should not be sent * with the headers as of RFC 2616 rules for the presence of a message body. A * message body must not be included with a HEAD request or with a 304 or a 204 * response. A proper implementation of this will prevent a message body being * sent if the response is to a HEAD request of if there is a 304 or 204 * response code. * <p> * It is important to note that the <code>Response</code> controls the * processing of the HTTP pipeline. The next HTTP request is not processed until * the response has committed. The response is committed once the * <code>commit</code> method is invoked if there is NO content body. Committing * with a content body is done only if correct content is given. The * <code>OutputStream</code> acts as a client and commits the response once the * specified content has been written to the issued <code>OutputStream</code>. * * @author Niall Gallagher */ class ResponseEntity extends ResponseMessage implements Response { /** * This is used to buffer the bytes that are sent to the client. */ private ResponseBuffer buffer; /** * This is the conversation used to determine connection type. */ private Conversation support; /** * This is the underlying channel for the connected pipeline. */ private Channel channel; /** * This is the monitor that is used to monitor the response. */ private Monitor monitor; /** * This is the sender object used to deliver to response data. */ private Sender sender; /** * This is used to trace events that occur with the response */ private Trace trace; /** * This is used to determine if the response has been committed. */ private boolean committed; /** * Constructor for the <code>ResponseEntity</code> object. This is used to * create a response instance using the provided request, entity, and * monitor object. To ensure that the response is compatible with client the * <code>Request</code> is used. Also to ensure the next request can be * processed the provided monitor is used to signal response events to the * server kernel. * * @param request * this is the request that was sent by the client * @param entity * this is the entity that contains the channel * @param monitor * this is the monitor used to signal events */ public ResponseEntity(Request request, Entity entity, Monitor monitor) { this.support = new Conversation(request, this); this.buffer = new ResponseBuffer(this, this.support, entity, monitor); this.channel = entity.getChannel(); this.sender = this.channel.getSender(); this.trace = this.channel.getTrace(); this.monitor = monitor; } /** * This represents the time at which the response has fully written. Because * the response is delivered asynchronously to the client this response time * does not represent the time to last byte. It simply represents the time * at which the response has been fully generated and written to the output * buffer or queue. This returns zero if the response has not finished. * * @return this is the time taken to complete the response */ @Override public long getResponseTime() { return this.monitor.getTime(); } /** * This is used as a shortcut for acquiring attributes for the response. * This avoids acquiring the <code>Attributes</code> in order to retrieve * the attribute directly from that object. The attributes contain data * specific to the response. * * @param name * this is the name of the attribute to acquire * * @return this returns the attribute for the specified name */ public Object getAttribute(Object name) { return this.getAttributes().get(name); } /** * This can be used to retrieve certain attributes about this * <code>Response</code>. The attributes contains certain properties about * the <code>Response</code>. For example if this Response goes over a * secure line then there may be any arbitrary attributes. * * @return the response attributes of that have been set */ public Map getAttributes() { return this.channel.getAttributes(); } /** * This should be used when the size of the message body is known. For * performance reasons this should be used so the length of the output is * known. This ensures that Persistent HTTP (PHTTP) connections can be * maintained for both HTTP/1.0 and HTTP/1.1 clients. If the length of the * output is not known HTTP/1.0 clients will require a connection close, * which reduces performance (see RFC 2616). * <p> * This removes any previous Content-Length headers from the message header. * This will then set the appropriate Content-Length header with the correct * length. If a the Connection header is set with the close token then the * semantics of the connection are such that the server will close it once * the <code>OutputStream.close</code> is used. * * @param length * this is the length of the HTTP message body */ @Override public void setContentLength(long length) { this.setValue(CONTENT_LENGTH, String.valueOf(length)); } /** * This determines the charset for <code>PrintStream</code> objects returned * from the <code>getPrintStream</code> method. This will return a valid * charset regardless of whether the Content-Type header has been set, set * without a charset, or not set at all. If unspecified, the charset * returned is <code>ISO-8859-1</code>, as suggested by RFC 2616, section * 3.7.1. * * @return returns the charset used by this response object */ private String getCharset() { ContentType type = this.getContentType(); if (type == null) return "ISO-8859-1"; if (type.getCharset() == null) return "ISO-8859-1"; return type.getCharset(); } /** * Used to write a message body with the <code>Response</code>. The * semantics of this <code>OutputStream</code> will be determined by the * HTTP version of the client, and whether or not the content length has * been set, through the <code>setContentLength</code> method. If the length * of the output is not known then the output is chunked for HTTP/1.1 * clients and closed for HTTP/1.0 clients. * * @return an output stream object used to write the message body */ @Override public OutputStream getOutputStream() throws IOException { return this.buffer; } /** * Used to write a message body with the <code>Response</code>. The * semantics of this <code>OutputStream</code> will be determined by the * HTTP version of the client, and whether or not the content length has * been set, through the <code>setContentLength</code> method. If the length * of the output is not known then the output is chunked for HTTP/1.1 * clients and closed for HTTP/1.0 clients. * <p> * This will ensure that there is buffering done so that the output can be * reset using the <code>reset</code> method. This will enable the specified * number of bytes to be written without committing the response. This * specified size is the minimum size that the response buffer must be. * * @param size * the minimum size that the response buffer must be * * @return an output stream object used to write the message body */ @Override public OutputStream getOutputStream(int size) throws IOException { if (size > 0) { this.buffer.expand(size); } return this.buffer; } /** * This method is provided for convenience so that the HTTP content can be * written using the <code>print</code> methods provided by the * <code>PrintStream</code>. This will basically wrap the * <code>getOutputStream</code> with a buffer size of zero. * <p> * The retrieved <code>PrintStream</code> uses the charset used to describe * the content, with the Content-Type header. This will check the charset * parameter of the contents MIME type. So if the Content-Type was * <code>text/plain; charset=UTF-8</code> the resulting * <code>PrintStream</code> would encode the written data using the UTF-8 * encoding scheme. Care must be taken to ensure that bytes written to the * stream are correctly encoded. * * @return a print stream object used to write the message body */ @Override public PrintStream getPrintStream() throws IOException { return this.getPrintStream(0, this.getCharset()); } /** * This method is provided for convenience so that the HTTP content can be * written using the <code>print</code> methods provided by the * <code>PrintStream</code>. This will basically wrap the * <code>getOutputStream</code> with a specified buffer size. * <p> * The retrieved <code>PrintStream</code> uses the charset used to describe * the content, with the Content-Type header. This will check the charset * parameter of the contents MIME type. So if the Content-Type was * <code>text/plain; charset=UTF-8</code> the resulting * <code>PrintStream</code> would encode the written data using the UTF-8 * encoding scheme. Care must be taken to ensure that bytes written to the * stream are correctly encoded. * * @param size * the minimum size that the response buffer must be * * @return a print stream object used to write the message body */ @Override public PrintStream getPrintStream(int size) throws IOException { return this.getPrintStream(size, this.getCharset()); } /** * This is used to wrap the <code>getOutputStream</code> object in a * <code>PrintStream</code>, which will write content using a specified * charset. The <code>PrintStream</code> created will not buffer the * content, it will write directly to the underlying * <code>OutputStream</code> where it is buffered (if there is a buffer size * greater than zero specified). In future the buffer of the * <code>PrintStream</code> may be usable. * * @param size * the minimum size that the response buffer must be * @param charset * this is the charset used by the resulting stream * * @return a print stream that encodes in the given charset */ private PrintStream getPrintStream(int size, String charset) throws IOException { if (size > 0) { this.buffer.expand(size); } return new PrintStream(this.buffer, false, charset); } /** * Used to write a message body with the <code>Response</code>. The * semantics of this <code>WritableByteChannel</code> are determined by the * HTTP version of the client, and whether or not the content length has * been set, through the <code>setContentLength</code> method. If the length * of the output is not known then the output is chunked for HTTP/1.1 * clients and closed for HTTP/1.0 clients. * * @return a writable byte channel used to write the message body */ @Override public WritableByteChannel getByteChannel() throws IOException { return this.buffer; } /** * Used to write a message body with the <code>Response</code>. The * semantics of this <code>WritableByteChannel</code> are determined by the * HTTP version of the client, and whether or not the content length has * been set, through the <code>setContentLength</code> method. If the length * of the output is not known then the output is chunked for HTTP/1.1 * clients and closed for HTTP/1.0 clients. * <p> * This will ensure that there is buffering done so that the output can be * reset using the <code>reset</code> method. This will enable the specified * number of bytes to be written without committing the response. This * specified size is the minimum size that the response buffer must be. * * @param size * the minimum size that the response buffer must be * * @return a writable byte channel used to write the message body */ @Override public WritableByteChannel getByteChannel(int size) throws IOException { if (size > 0) { this.buffer.expand(size); } return this.buffer; } /** * This is used to determine if the HTTP response message is a keep alive * message or if the underlying socket was closed. Even if the client * requests a connection keep alive and supports persistent connections, the * response can still be closed by the server. This can be explicitly * indicated by the presence of the <code>Connection</code> HTTP header, it * can also be implicitly indicated by using version HTTP/1.0. * * @return this returns true if the connection was closed */ @Override public boolean isKeepAlive() { return this.support.isKeepAlive(); } /** * This can be used to determine whether the <code>Response</code> has been * committed. This is true if the <code>Response</code> was committed, * either due to an explicit invocation of the <code>commit</code> method or * due to the writing of content. If the <code>Response</code> has committed * the <code>reset</code> method will not work in resetting content already * written. * * @return true if the response has been fully committed */ @Override public boolean isCommitted() { return this.committed; } /** * This is used to write the headers that where given to the * <code>Response</code>. Any further attempts to give headers to the * <code>Response</code> will be futile as only the headers that were given * at the time of the first commit will be used in the message header. * <p> * This also performs some final checks on the headers submitted. This is * done to determine the optimal performance of the output. If no specific * Connection header has been specified this will set the connection so that * HTTP/1.0 closes by default. * * @exception IOException * thrown if there was a problem writing */ @Override public void commit() throws IOException { if (!this.committed) { String header = this.toString(); byte[] message = header.getBytes("UTF-8"); this.trace.trace(WRITE_HEADER, header); this.sender.send(message); this.committed = true; } } /** * This can be used to determine whether the <code>Response</code> has been * committed. This is true if the <code>Response</code> was committed, * either due to an explicit invocation of the <code>commit</code> method or * due to the writing of content. If the <code>Response</code> has committed * the <code>reset</code> method will not work in resetting content already * written. * * @throws IOException * thrown if there is a problem resetting */ @Override public void reset() throws IOException { this.buffer.reset(); } /** * This is used to close the connection and commit the request. This * provides the same semantics as closing the output stream and ensures that * the HTTP response is committed. This will throw an exception if the * response can not be committed. * * @throws IOException * thrown if there is a problem writing */ @Override public void close() throws IOException { this.buffer.close(); } }