001: // Reply.java
002: // $Id: Reply.java,v 1.38 2003/02/25 17:52:01 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1996.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.jigsaw.http;
007:
008: import java.io.ByteArrayInputStream;
009: import java.io.DataOutputStream;
010: import java.io.FileDescriptor;
011: import java.io.IOException;
012: import java.io.InputStream;
013: import java.io.OutputStream;
014: import java.io.UnsupportedEncodingException;
015:
016: import org.w3c.www.mime.MimeType;
017:
018: import org.w3c.www.http.HTTP;
019: import org.w3c.www.http.HeaderValue;
020: import org.w3c.www.http.HttpEntityMessage;
021: import org.w3c.www.http.HttpFactory;
022: import org.w3c.www.http.HttpMessage;
023: import org.w3c.www.http.HttpMimeType;
024: import org.w3c.www.http.HttpReplyMessage;
025: import org.w3c.www.http.HttpRequestMessage;
026: import org.w3c.www.http.HttpTokenList;
027:
028: import org.w3c.tools.resources.ReplyInterface;
029: import org.w3c.tools.resources.RequestInterface;
030: import org.w3c.tools.resources.ResourceFilter;
031:
032: public class Reply extends HttpReplyMessage implements ReplyInterface {
033: protected static HttpMimeType DEFAULT_TYPE = null;
034: protected static HttpTokenList CONNECTION = null;
035:
036: private static String ka = "Keep-Alive";
037: private static String pc = "Proxy-Connection";
038: private static String cl = "close";
039:
040: static {
041: String connection[] = { "Keep-Alive" };
042: CONNECTION = HttpFactory.makeStringList(connection);
043: DEFAULT_TYPE = HttpFactory.makeMimeType(MimeType.TEXT_HTML);
044: }
045:
046: InputStream is = null;
047: Client client = null;
048: boolean keep = true;
049: Request request = null;
050: private boolean sendBody = true;
051: private boolean dynamic = false;
052:
053: /**
054: * set the reply to be a reply for dynamic content
055: * @param a boolean, true if the reply is generated by a dynamic
056: */
057: public void setDynamic(boolean dyn) {
058: dynamic = dyn;
059: }
060:
061: /**
062: * is is dynamic or not?
063: */
064: public boolean isDynamic() {
065: return dynamic;
066: }
067:
068: public void setStatus(Integer status) {
069: setStatus(status.intValue());
070: }
071:
072: public boolean hasContentLength() {
073: return hasHeader(H_CONTENT_LENGTH);
074: }
075:
076: public boolean hasContentType() {
077: return (hasHeader(H_CONTENT_TYPE) && (getHeaderValue(
078: H_CONTENT_TYPE).getValue() != null));
079: }
080:
081: public void setKeepAlive(String value) {
082: setValue(ka, value);
083: }
084:
085: public void setProxyConnection(String value) {
086: setValue(pc, value);
087: }
088:
089: public boolean keepProxyConnection() {
090: throw new RuntimeException(
091: "keepProxyConnection: not implemented!");
092: }
093:
094: /**
095: * @deprecated
096: */
097:
098: public FileDescriptor getInputFileDescriptor() {
099: return null;
100: }
101:
102: public void setKeepConnection(boolean onoff) {
103: this .keep = onoff;
104: }
105:
106: public boolean tryKeepConnection() {
107: if (!keep) {
108: addConnection(cl);
109: return false;
110: } else if (major >= 1) {
111: if (minor >= 1)
112: return true;
113: if (hasContentLength() || (is == null)) {
114: if (is_proxy)
115: addProxyConnection(ka);
116: else
117: addConnection(ka);
118: return true;
119: }
120: }
121: return false;
122: }
123:
124: /**
125: * Is this reply a proxy reply.
126: */
127: protected boolean is_proxy = false;
128:
129: /**
130: * Mark this reply as being a proxy reply.
131: */
132:
133: public void setProxy(boolean onoff) {
134: is_proxy = onoff;
135: }
136:
137: /**
138: * Sets the reply stream to the given HtmlGenerator stream.
139: * @param g The HtmlGenerator whose output is to be used as the reply body.
140: */
141:
142: public void setStream(org.w3c.jigsaw.html.HtmlGenerator g) {
143: g.close();
144: setContentLength(g.length());
145: setContentType(g.getMimeType());
146: if (sendBody)
147: setStream(g.getInputStream(), true);
148: }
149:
150: public boolean hasStream() {
151: return is != null;
152: }
153:
154: /**
155: * Open this reply body stream.
156: * This is used to send the reply body back to the client.
157: * @return An InputStream containing the reply body, which is dumped
158: * back to the client.
159: */
160:
161: public InputStream openStream() {
162: return is;
163: }
164:
165: public void setStream(InputStream is) {
166: setStream(is, false);
167: }
168:
169: public synchronized void setStream(InputStream is, boolean closeOld) {
170: if (sendBody) {
171: if (closeOld && (this .is != null)) {
172: try {
173: this .is.close();
174: } catch (IOException ioex) {
175: }
176: ;
177: }
178: this .is = is;
179: }
180: }
181:
182: protected ResourceFilter filters[] = null;
183: protected int infilters = -1;
184:
185: protected void setFilters(ResourceFilter filters[], int infilters) {
186: this .filters = filters;
187: this .infilters = infilters;
188: }
189:
190: protected OutputStream output = null;
191:
192: /**
193: * Get the reply output stream.
194: * @param doEmit Emit that reply before giving out the output stream.
195: * @return An OutputStream instance.
196: * @exception IOException If the output stream could not get opened.
197: */
198:
199: public synchronized OutputStream getOutputStream(boolean doEmit)
200: throws IOException {
201: if (output != null)
202: return output;
203: // Build the output stream:
204: output = client.getOutputStream();
205: // Call any filters:
206: while (--infilters >= 0)
207: output = filters[infilters].outputFilter(request, this ,
208: output);
209: if (doEmit) {
210: DataOutputStream dataOutput = new DataOutputStream(output);
211: emit(dataOutput);
212: dataOutput.flush();
213: setStatus(HTTP.DONE);
214: }
215: // Disable keep-connection:
216: keep = false;
217: return output;
218: }
219:
220: /**
221: * Get that reply output stream.
222: * The reply is first emitted to the stream, and the opened stream
223: * is returned back to the caller.
224: * @return An OutputStream instance.
225: * @exception IOException If the output stream could not get opened.
226: */
227:
228: public OutputStream getOutputStream() throws IOException {
229: return getOutputStream(true);
230: }
231:
232: /**
233: * Should this reply be chunked ?
234: * @return If so, the reply should prepare itself to send back the
235: * appropriate transfer encoding header, and return
236: * <strong>true</strong>, otherwise it should just return
237: * <strong>false</strong>.
238: */
239:
240: protected Boolean chunkable = null;
241: // FIXME should be an HttpTokenList
242: protected static String chunked = "chunked";
243:
244: public boolean canChunkTransfer() {
245: // Have we already compute this ?
246: if (chunkable == null) {
247: // Compute wether we can chunk the reply:
248: if (hasContentLength() || (is == null)) {
249: chunkable = Boolean.FALSE;
250: } else if ((major >= 1) && (minor >= 1)) {
251: // String connections[] = getConnection();
252: chunkable = Boolean.TRUE;
253: // if (connections != null) {
254: // for (int i = 0; i< connections.length; i++) {
255: // if (connections[i].equalsIgnoreCase("close")) {
256: // chunkable = Boolean.FALSE;
257: // }
258: // }
259: // }
260: // if (chunkable == Boolean.TRUE)
261: addTransferEncoding(chunked);
262: } else {
263: chunkable = Boolean.FALSE;
264: }
265: }
266: return (chunkable == Boolean.TRUE);
267: }
268:
269: /**
270: * Set this reply content.
271: * This method allows to set the reply content to a simple String instance.
272: * @param msg The reply content.
273: * @param encoding, the encoding of the reply
274: */
275: public void setContent(String msg, String encoding) {
276: if (!hasContentType())
277: setHeaderValue(H_CONTENT_TYPE, DEFAULT_TYPE);
278: byte byteBuffer[];
279: try {
280: byteBuffer = msg.getBytes(encoding);
281: } catch (UnsupportedEncodingException ex) {
282: throw new RuntimeException(this .getClass().getName()
283: + "[setContent] Unable to convert"
284: + "properly char to bytes");
285: }
286: setContentLength(byteBuffer.length);
287: if (sendBody) {
288: ByteArrayInputStream bis = new ByteArrayInputStream(
289: byteBuffer);
290: setStream(bis, true);
291: }
292: }
293:
294: // FIXME the bug fix should be in HttpMessage.hasHeader, but it would
295: // be more time consuming, as it would add checks for all headers
296: // and only Content-Type seems to be problematic
297:
298: /**
299: * @param out The output stream to emit the message to.
300: * @param what (fixme doc)
301: * @exception IOException If the message couldn't be emited to the
302: * given stream, due to IO errors.
303: */
304: public void emit(OutputStream out, int what) throws IOException {
305: int status = getStatus();
306: if (!hasContentType()
307: && ((status != HTTP.CONTINUE) || (status != HTTP.SWITCHING))) {
308: setHeaderValue(H_CONTENT_TYPE, DEFAULT_TYPE);
309: }
310: super .emit(out, what);
311: }
312:
313: public void dump(OutputStream out) {
314: if (!hasContentType()) {
315: setHeaderValue(H_CONTENT_TYPE, DEFAULT_TYPE);
316: }
317: super .dump(out);
318: }
319:
320: /**
321: * Set this reply content.
322: * This method allows to set the reply content to a simple String instance.
323: * encoding will be by default "ISO8859_1"
324: * @param msg The reply content.
325: */
326: public void setContent(String msg) {
327: setContent(msg, "ISO-8859-1");
328: }
329:
330: /**
331: * Get the entity MIME type.
332: * @return An HttpMimeType object describing the entity's type, or
333: * <strong>null</strong> if udefined.
334: */
335: public MimeType getContentType() {
336: MimeType mt = super .getContentType();
337: if (mt == null) {
338: return MimeType.APPLICATION_OCTET_STREAM;
339: } else {
340: return mt;
341: }
342: }
343:
344: /**
345: * Create a new Reply instance for the given client.
346: * @param client The client to who this reply is directed.
347: */
348:
349: public Reply(Client client) {
350: this .client = client;
351: }
352:
353: /**
354: * Create a new reply for the given client.
355: * @param client The client ot who the reply is directed.
356: * @reply status The reply status code.
357: */
358:
359: public Reply(Client client, Request request, short major,
360: short minor, int status) {
361: this (client);
362: this .request = request;
363: this .major = major;
364: this .minor = minor;
365: this .keep = true;
366: this .setServer((client != null) ? client.getServer()
367: .getSoftware() : null);
368: this .setStatus(status);
369: if (request != null)
370: if (request.getMethod().endsWith(HTTP.HEAD))
371: sendBody = false;
372: }
373: }
|