Put the classes in the com.fastcgi package
[catagits/fcgi2.git] / java / FCGIOutputStream.java
1 /*
2  * @(#)FCGIOutputStream.java
3  *
4  *      FastCGi compatibility package Interface
5  *
6  *  Copyright (c) 1996 Open Market, Inc.
7  *
8  * See the file "LICENSE.TERMS" for information on usage and redistribution
9  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10  *
11  * $Id: FCGIOutputStream.java,v 1.2 2000/03/21 12:02:29 robs Exp $
12  */
13 package com.fastcgi;
14
15 import java.io.*;
16
17 /**
18  * This stream understands FCGI prototcol.
19  */
20
21 public class FCGIOutputStream extends OutputStream {
22
23     /* Stream vars */
24
25     public int wrNext;
26     public int stop;
27     public boolean isClosed;
28
29     /* require methods to set, get and clear */
30     private int errno;
31     private Exception errex;
32
33     /* data vars */
34
35     public byte buff[];
36     public int buffLen;
37     public int buffStop;
38     public int type;
39     public boolean isAnythingWritten;
40     public boolean rawWrite;
41     public FCGIRequest request;
42
43     public FileOutputStream out;
44
45     /**
46     * Creates a new output stream to manage fcgi prototcol stuff
47     * @param out the output stream  buflen  length of buffer streamType
48     */
49     public FCGIOutputStream(FileOutputStream outStream,
50         int bufLen, int streamType,
51         FCGIRequest inreq) {
52         out = outStream;
53         buffLen = Math.min(bufLen, FCGIGlobalDefs.def_FCGIMaxLen);
54         buff = new byte[buffLen];
55         type = streamType;
56         stop = buffStop = buffLen;
57         isAnythingWritten = false;
58         rawWrite = false;
59         wrNext = FCGIGlobalDefs.def_FCGIHeaderLen;
60         isClosed = false;
61         request = inreq;
62     }
63
64     /**
65     * Writes a byte to the output stream.
66     */
67     public void  write(int c) throws IOException {
68         if(wrNext != stop) {
69             buff[wrNext++] = (byte)c;
70             return;
71         }
72         if(isClosed) {
73             throw new EOFException();
74         }
75         empty(false);
76         if(wrNext != stop) {
77             buff[wrNext++] = (byte)c;
78             return;
79         }
80         /* NOTE: ASSERT(stream->isClosed); */
81         /* bug in emptyBuffProc if not */
82         throw new EOFException();
83     }
84
85     /**
86     * Writes an array of bytes. This method will block until the bytes
87     * are actually written.
88     * @param b  the data to be written
89     */
90     public  void write(byte b[]) throws IOException{
91         write(b, 0, b.length);
92     }
93
94     /**
95     * Writes len consecutive bytes from off in the array b
96     * into the output stream.  Performs no interpretation
97     * of the output bytes. Making the user convert the string to
98     * bytes is in line with current Java practice.
99     */
100     public void write(byte b[], int off, int len) throws IOException {
101         int m, bytesMoved;
102         /*
103         * Fast path: room for n bytes in the buffer
104         */
105         if(len <= (stop - wrNext)) {
106             System.arraycopy(b, off, buff, wrNext, len);
107             wrNext += len;
108             return;
109         }
110         /*
111         * General case: stream is closed or buffer empty procedure
112         * needs to be called
113         */
114         bytesMoved = 0;
115         for (;;) {
116             if(wrNext != stop) {
117                 m = Math.min(len - bytesMoved, stop - wrNext);
118                 System.arraycopy(b, off, buff, wrNext, m);
119                 bytesMoved += m;
120                 wrNext += m;
121                 if(bytesMoved == len) {
122                     return;
123                 }
124                 off += m;
125             }
126             if(isClosed) {
127                 throw new EOFException();
128             }
129             empty(false);
130         }
131     }
132
133     /**
134     * Encapsulates any buffered stream content in a FastCGI
135     * record.  If !doClose, writes the data, making the buffer
136     * empty.
137     */
138     public void empty(boolean doClose) throws IOException {
139         int cLen;
140         /*
141         * Alignment padding omitted in Java
142         */
143         if (!rawWrite) {
144             cLen = wrNext - FCGIGlobalDefs.def_FCGIHeaderLen;
145             if(cLen > 0) {
146                 System.arraycopy(new FCGIMessage().makeHeader(type,
147                     request.requestID, cLen, 0),
148                     0, buff, 0,
149                     FCGIGlobalDefs.def_FCGIHeaderLen);
150             }
151             else {
152                 wrNext = 0;
153             }
154         }
155         if (doClose) {
156             writeCloseRecords();
157         }
158         if (wrNext != 0) {
159             isAnythingWritten = true;
160             try {
161                 out.write(buff, 0, wrNext);
162             } catch (IOException e) {
163                 setException(e);
164                 return;
165             }
166             wrNext = 0;
167         }
168         /*
169         * The buffer is empty.
170         */
171         if(!rawWrite) {
172             wrNext += FCGIGlobalDefs.def_FCGIHeaderLen;
173         }
174     }
175
176     /**
177     * Close the stream.
178     */
179     public void  close() throws IOException {
180         if (isClosed) {
181             return;
182         }
183         empty(true);
184         /*
185         * if isClosed, will return with EOFException from write.
186         */
187         isClosed = true;
188         stop = wrNext;
189         return;
190     }
191
192     /**
193     * Flushes any buffered output.
194     * Server-push is a legitimate application of flush.
195     * Otherwise, it is not very useful, since FCGIAccept
196     * does it implicitly.  flush may reduce performance
197     * by increasing the total number of operating system calls
198     * the application makes.
199     */
200     public void flush() throws IOException {
201         if (isClosed) {
202             return;
203         }
204         empty(false);
205         /*
206         * if isClosed, will return with EOFException from write.
207         */
208         return;
209     }
210
211     /**
212     * An FCGI error has occurred. Save the error code in the stream
213     * for diagnostic purposes and set the stream state so that
214     * reads return EOF
215     */
216     public void setFCGIError(int errnum) {
217         /*
218         * Preserve only the first error.
219         */
220         if (errno == 0) {
221             errno = errnum;
222         }
223         isClosed = true;
224     }
225
226     /**
227     * An Exception has occurred. Save the Exception in the stream
228     * for diagnostic purposes and set the stream state so that
229     * reads return EOF
230     */
231     public void setException(Exception errexpt) {
232         /*
233         * Preserve only the first error.
234         */
235         if (errex == null) {
236             errex = errexpt;
237         }
238         isClosed = true;
239     }
240
241     /**
242     * Clear the stream error code and end-of-file indication.
243     */
244     public void clearFCGIError() {
245         errno = 0;
246         /*
247         * isClosed = false;
248         * XXX: should clear isClosed but work is needed to make it safe
249         * to do so.
250         */
251     }
252
253     /**
254     * Clear the stream error code and end-of-file indication.
255     */
256     public void clearException() {
257         errex = null;
258         /*
259         * isClosed = false;
260         * XXX: should clear isClosed but work is needed to make it safe
261         * to do so.
262         */
263     }
264
265     /**
266     * accessor method since var is private
267     */
268     public int etFCGIError() {
269         return errno;
270     }
271
272     /**
273     * accessor method since var is private
274     */
275     public Exception getException() {
276         return errex;
277     }
278
279     /**
280     * Writes an EOF record for the stream content if necessary.
281     * If this is the last writer to close, writes an FCGI_END_REQUEST
282     * record.
283     */
284     public void writeCloseRecords() throws IOException {
285         FCGIMessage msg = new FCGIMessage();
286         /*
287         * Enter rawWrite mode so final records won't be
288         * encapsulated as
289         * stream data.
290         */
291         rawWrite = true;
292         /*
293         * Generate EOF for stream content if needed.
294         */
295         if(!(type == FCGIGlobalDefs.def_FCGIStderr
296             && wrNext == 0
297             && !isAnythingWritten)) {
298             byte hdr[] =
299                 new byte[FCGIGlobalDefs.def_FCGIHeaderLen];
300             System.arraycopy(msg.makeHeader(type,
301                 request.requestID,
302                 0, 0),
303                 0, hdr,0,
304                 FCGIGlobalDefs.def_FCGIHeaderLen);
305             write(hdr, 0, hdr.length);
306         }
307         /*
308         * Generate FCGI_END_REQUEST record if needed.
309         */
310         if(request.numWriters == 1) {
311             byte endReq[] =
312                 new byte[FCGIGlobalDefs.def_FCGIHeaderLen
313                 + FCGIGlobalDefs.def_FCGIEndReqBodyLen];
314             System.arraycopy(msg.makeHeader(
315                 FCGIGlobalDefs.def_FCGIEndRequest,
316                 request.requestID,
317                 FCGIGlobalDefs.def_FCGIEndReqBodyLen,0),
318                 0, endReq, 0,
319                 FCGIGlobalDefs.def_FCGIHeaderLen);
320             System.arraycopy(msg.makeEndrequestBody(
321                 request.appStatus,
322                 FCGIGlobalDefs.def_FCGIRequestComplete),
323                 0,endReq,
324                 FCGIGlobalDefs.def_FCGIHeaderLen,
325                 FCGIGlobalDefs.def_FCGIEndReqBodyLen);
326             write(endReq,0, FCGIGlobalDefs.def_FCGIHeaderLen
327                 + FCGIGlobalDefs.def_FCGIEndReqBodyLen);
328         }
329         request.numWriters--;
330     }
331 }
332