001: package com.quadcap.http.server22;
002:
003: /* Copyright 1998 - 2003 Quadcap Software. All rights reserved.
004: *
005: * This software is distributed under the Quadcap Free Software License.
006: * This software may be used or modified for any purpose, personal or
007: * commercial. Open Source redistributions are permitted. Commercial
008: * redistribution of larger works derived from, or works which bundle
009: * this software requires a "Commercial Redistribution License"; see
010: * http://www.quadcap.com/purchase.
011: *
012: * Redistributions qualify as "Open Source" under one of the following terms:
013: *
014: * Redistributions are made at no charge beyond the reasonable cost of
015: * materials and delivery.
016: *
017: * Redistributions are accompanied by a copy of the Source Code or by an
018: * irrevocable offer to provide a copy of the Source Code for up to three
019: * years at the cost of materials and delivery. Such redistributions
020: * must allow further use, modification, and redistribution of the Source
021: * Code under substantially the same terms as this license.
022: *
023: * Redistributions of source code must retain the copyright notices as they
024: * appear in each source code file, these license terms, and the
025: * disclaimer/limitation of liability set forth as paragraph 6 below.
026: *
027: * Redistributions in binary form must reproduce this Copyright Notice,
028: * these license terms, and the disclaimer/limitation of liability set
029: * forth as paragraph 6 below, in the documentation and/or other materials
030: * provided with the distribution.
031: *
032: * The Software is provided on an "AS IS" basis. No warranty is
033: * provided that the Software is free of defects, or fit for a
034: * particular purpose.
035: *
036: * Limitation of Liability. Quadcap Software shall not be liable
037: * for any damages suffered by the Licensee or any third party resulting
038: * from use of the Software.
039: */
040:
041: import java.io.ByteArrayOutputStream;
042: import java.io.BufferedOutputStream;
043: import java.io.IOException;
044: import java.io.OutputStream;
045:
046: import javax.servlet.ServletOutputStream;
047:
048: import com.quadcap.net.server.WorkerOutputStream;
049:
050: import com.quadcap.util.Debug;
051:
052: /**
053: * An output stream for writing servlet responses. This is an
054: * abstract class, to be implemented by a network services
055: * implementor. Servlet writers use the output stream to return data
056: * to clients. They access it via the ServletResponse's
057: * getOutputStream method, available from within the servlet's service
058: * method. Subclasses of ServletOutputStream must provide an
059: * implementation of the write(int) method.
060: * <p>
061: *
062: * This implementation is friendly to servlets which return "small"
063: * responses and don't compute Content-Length -- it will compute this
064: * header for them automatically.
065: *
066: * @author Stan Bailes
067: */
068: public class HttpOutputStream extends ServletOutputStream {
069: WorkerOutputStream os;
070: HttpResponse res;
071: ByteArrayOutputStream bos = new ByteArrayOutputStream();
072: boolean committed = false;
073: boolean autoFlush = true;
074:
075: int bufferSize = 32 * 1024;
076:
077: /**
078: * Construct a new http output stream, attached to the specified
079: * worker output stream.
080: *
081: * @param os the output stream.
082: */
083: public void reset(WorkerOutputStream os) {
084: this .os = os;
085: this .bos.reset();
086: this .committed = false;
087: }
088:
089: /**
090: * Write a byte to the stream
091: *
092: * @param b the byte
093: */
094: public void write(int b) throws IOException {
095: if (committed) {
096: os.write(b);
097: } else if (bos.size() >= bufferSize) {
098: res.writeHeaders();
099: bos.writeTo(os);
100: committed = true;
101: os.write(b);
102: } else {
103: bos.write(b);
104: }
105: }
106:
107: /**
108: * Write a bunch of bytes in one shot.
109: *
110: * @param buf a buffer where those bytes live
111: * @param off the position in the buffer of the first byte to write
112: * @param len the number of bytes to write
113: *
114: * @exception IOException may be thrown
115: */
116: public void write(byte[] buf, int off, int len) throws IOException {
117: if (committed) {
118: os.write(buf, off, len);
119: } else if (bos.size() + len >= bufferSize) {
120: res.writeHeaders();
121: bos.writeTo(os);
122: committed = true;
123: os.write(buf, off, len);
124: } else {
125: bos.write(buf, off, len);
126: }
127: }
128:
129: /**
130: * Flush any buffered output to tht client
131: */
132: public final void flushBuffered() throws IOException {
133: if (!committed) {
134: int len = bos.size();
135: if (len > 0) {
136: res.maybeSetContentLength(len);
137: }
138: res.writeHeaders();
139: bos.writeTo(os);
140: committed = true;
141: }
142: os.flush();
143: }
144:
145: /**
146: * Flush the underlying stream
147: */
148: public final void flush() throws IOException {
149: flushBuffered();
150: }
151:
152: /**
153: * Close does nothing, to prevent clients from closing streams when they
154: * really don't want to
155: */
156: public final void close() throws IOException {
157: flushBuffered();
158: }
159:
160: public final void reallyClose() throws IOException {
161: os.close();
162: }
163:
164: /**
165: * Discard any bytes written so far if possible, and return true if
166: * we were able to do so.
167: */
168: public boolean discard() {
169: if (!committed) {
170: bos.reset();
171: return true;
172: }
173: return false;
174: }
175:
176: /**
177: * Set up the connection with the http response object so we can
178: * control flushing the headers when we've reached our buffered
179: * limit.
180: *
181: * @param res the http response object associated with this request
182: */
183: void setResponse(HttpResponse res) {
184: this .res = res;
185: }
186:
187: /**
188: * Return the underlying output stream
189: */
190: WorkerOutputStream getOutputStream() {
191: return os;
192: }
193:
194: final void setBufferSize(int size) throws IllegalStateException {
195: if (committed) {
196: throw new IllegalStateException(
197: "Response already committed");
198: }
199: this .bufferSize = size;
200: }
201:
202: final int getBufferSize() {
203: return bufferSize;
204: }
205:
206: final boolean isCommitted() {
207: return committed;
208: }
209:
210: final void reset() throws IllegalStateException {
211: if (committed) {
212: throw new IllegalStateException(
213: "Response already committed");
214: }
215: bos.reset();
216: }
217:
218: final boolean getAutoFlush() {
219: return autoFlush;
220: }
221:
222: final void setAutoFlush(boolean autoFlush) {
223: this .autoFlush = autoFlush;
224: }
225:
226: public int getRemaining() {
227: return bufferSize - bos.size();
228: }
229: }
|