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