some more #include juggling
[catagits/fcgi2.git] / java / FCGIInputStream.java
1 /*
2  * @(#)FCGIInputStream.java
3  *
4  *      FastCGi compatibility package Interface
5  *
6  *
7  *  Copyright (c) 1996 Open Market, Inc.
8  *
9  * See the file "LICENSE.TERMS" for information on usage and redistribution
10  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11  *
12  * $Id: FCGIInputStream.java,v 1.1 1999/01/31 02:45:49 roberts Exp $
13  */
14
15 import java.io.*;
16 import  FCGIRequest;
17 import  FCGIGlobalDefs;
18
19 /**
20  * This stream manages buffered reads of FCGI messages.
21  */
22 public class FCGIInputStream extends InputStream {
23
24     /* Stream vars */
25
26     public int rdNext;
27     public int stop;
28     public boolean isClosed;
29
30     /* require methods to set, get and clear */
31     private int errno;
32     private Exception errex;
33
34     /* data vars */
35
36     public byte buff[];
37     public int buffLen;
38     public int buffStop;
39     public int type;
40     public int contentLen;
41     public int paddingLen;
42     public boolean skip;
43     public boolean eorStop;
44     public FCGIRequest request;
45
46     public InputStream in;
47
48
49     /**
50     * Creates a new input stream to manage fcgi prototcol stuff
51     * @param in the input stream  bufLen  length of buffer streamType
52     */
53     public FCGIInputStream(FileInputStream inStream, int bufLen,
54         int streamType,
55         FCGIRequest inReq) {
56
57         in = inStream;
58         buffLen = Math.min(bufLen,FCGIGlobalDefs.def_FCGIMaxLen);
59         buff = new byte[buffLen];
60         type = streamType;
61         stop = rdNext = buffStop = 0;
62         isClosed = false;
63         contentLen = 0;
64         paddingLen = 0;
65         skip = false;
66         eorStop = false;
67         request = inReq;
68
69     }
70     /**
71     * Reads a byte of data. This method will block if no input is
72     * available.
73     * @return  the byte read, or -1 if the end of the
74     *      stream is reached.
75     * @exception IOException If an I/O error has occurred.
76     */
77     public int read() throws IOException {
78         if (rdNext != stop) {
79             return buff[rdNext++];
80         }
81         if (isClosed){
82             return -1;
83         }
84         fill();
85         if (rdNext != stop){
86             return buff[rdNext++];
87         }
88         return -1;
89     }
90     /**
91     * Reads into an array of bytes.  This method will
92     * block until some input is available.
93     * @param b the buffer into which the data is read
94     * @return  the actual number of bytes read, -1 is
95     *      returned when the end of the stream is reached.
96     * @exception IOException If an I/O error has occurred.
97     */
98     public int read(byte b[]) throws IOException {
99         return read(b, 0, b.length);
100     }
101
102     /**
103     * Reads into an array of bytes.
104     * Blocks until some input is available.
105     * @param b the buffer into which the data is read
106     * @param off the start offset of the data
107     * @param len the maximum number of bytes read
108     * @return  the actual number of bytes read, -1 is
109     *      returned when the end of the stream is reached.
110     * @exception IOException If an I/O error has occurred.
111     */
112     public int read(byte b[], int off, int len) throws IOException {
113         int m, bytesMoved;
114
115         if (len <= 0){
116             return 0;
117         }
118         /*
119         *Fast path: len bytes already available.
120         */
121
122         if (len <= stop - rdNext){
123             System.arraycopy(buff, rdNext, b, off, len);
124             rdNext += len;
125             return len;
126         }
127         /*
128         *General case: stream is closed or fill needs to be called
129         */
130         bytesMoved = 0;
131         for(;;){
132             if (rdNext != stop){
133                 m = Math.min(len - bytesMoved, stop - rdNext);
134                 System.arraycopy(buff, rdNext, b, off, m);
135                 bytesMoved += m;
136                 rdNext += m;
137                 if (bytesMoved == len)
138                     return bytesMoved;
139                 off += m;
140             }
141             if (isClosed){
142                 return bytesMoved;
143             }
144             fill();
145
146         }
147     }
148     /**
149     * Reads into an array of bytes.  This method will
150     * block until some input is available.
151     * @param b the buffer into which the data is read
152     * @param off the start offset of the data
153     * @param len the maximum number of bytes read
154     * @return  the actual number of bytes read, -1 is
155     *      returned when the end of the stream is reached.
156     * @exception IOException If an I/O error has occurred.
157     */
158     public void  fill() throws IOException {
159         byte[] headerBuf = new byte[FCGIGlobalDefs.def_FCGIHeaderLen];
160         int headerLen = 0;
161         int status = 0;
162         int count = 0;
163         for(;;) {
164             /*
165             * If buffer is empty, do a read
166             */
167             if (rdNext == buffStop) {
168                 try {
169                     count = in.read(buff, 0, buffLen);
170                 } catch (IOException e) {
171                     setException(e);
172                     return;
173                 }
174                 if (count == 0) {
175                     setFCGIError(FCGIGlobalDefs.def_FCGIProtocolError);
176                     return;
177                 }
178                 rdNext = 0;
179                 buffStop = count;       // 1 more than we read
180             }
181             /* Now buf is not empty: If the current record contains more content
182              * bytes, deliver all that are present in buff to callers buffer
183              * unless he asked for less than we have, in which case give him less
184              */
185             if (contentLen > 0) {
186                 count = Math.min(contentLen, buffStop - rdNext);
187                 contentLen -= count;
188                 if (!skip) {
189                     stop = rdNext + count;
190                     return;
191                 }
192                 else {
193                     rdNext += count;
194                     if (contentLen > 0) {
195                         continue;
196                     }
197                     else {
198                         skip = false;
199                     }
200                 }
201             }
202             /* Content has been consumed by client.
203              * If record was padded, skip over padding
204              */
205             if (paddingLen > 0) {
206                 count = Math.min(paddingLen, buffStop - rdNext);
207                 paddingLen -= count;
208                 rdNext += count;
209                 if (paddingLen > 0) {
210                     continue;    // more padding to read
211                 }
212             }
213             /* All done with current record, including the padding.
214              * If we are in a recursive call from Process Header, deliver EOF
215              */
216             if (eorStop){
217                 stop = rdNext;
218                 isClosed = true;
219                 return;
220             }
221             /*
222              * Fill header with bytes from input buffer - get the whole header.
223              */
224             count = Math.min(headerBuf.length - headerLen, buffStop - rdNext);
225             System.arraycopy(buff,rdNext, headerBuf, headerLen, count);
226             headerLen += count;
227             rdNext  += count;
228             if (headerLen < headerBuf.length) {
229                 continue;
230             }
231             headerLen = 0;
232             /*
233              * Interperet the header. eorStop prevents ProcessHeader from
234              * reading past the end of record when using stream to read content
235              */
236             eorStop = true;
237             stop = rdNext;
238             status = 0;
239             status = new FCGIMessage(this).processHeader(headerBuf);
240             eorStop = false;
241             isClosed = false;
242             switch (status){
243             case FCGIGlobalDefs.def_FCGIStreamRecord:
244                 if (contentLen == 0) {
245                     stop = rdNext;
246                     isClosed = true;
247                     return;
248                 }
249                 break;
250             case FCGIGlobalDefs.def_FCGISkip:
251                 skip = true;
252                 break;
253             case FCGIGlobalDefs.def_FCGIBeginRecord:
254                 /*
255                 * If this header marked the beginning of a new
256                 * request, return role info to caller
257                 */
258                 return;
259             case FCGIGlobalDefs.def_FCGIMgmtRecord:
260                 break;
261             default:
262                 /*
263                 * ASSERT
264                 */
265                 setFCGIError(status);
266                 return;
267
268             }
269         }
270     }
271
272     /**
273      * Skips n bytes of input.
274      * @param n the number of bytes to be skipped
275      * @return  the actual number of bytes skipped.
276      * @exception IOException If an I/O error has occurred.
277      */
278     public long skip(long n) throws IOException {
279         byte data[] = new byte[(int)n];
280         return in.read(data);
281     }
282
283     /*
284      * An FCGI error has occurred. Save the error code in the stream
285      * for diagnostic purposes and set the stream state so that
286      * reads return EOF
287      */
288     public void setFCGIError(int errnum) {
289         /*
290         * Preserve only the first error.
291         */
292         if(errno == 0) {
293             errno = errnum;
294         }
295         isClosed = true;
296     }
297     /*
298     * An Exception has occurred. Save the Exception in the stream
299     * for diagnostic purposes and set the stream state so that
300     * reads return EOF
301     */
302     public void setException(Exception errexpt) {
303         /*
304         * Preserve only the first error.
305         */
306         if(errex == null) {
307             errex = errexpt;
308         }
309         isClosed = true;
310     }
311
312     /*
313     * Clear the stream error code and end-of-file indication.
314     */
315     public void clearFCGIError() {
316         errno = 0;
317         /*
318         * isClosed = false;
319         * XXX: should clear isClosed but work is needed to make it safe
320         * to do so.
321         */
322     }
323     /*
324     * Clear the stream error code and end-of-file indication.
325     */
326     public void clearException() {
327         errex = null;
328         /*
329         * isClosed = false;
330         * XXX: should clear isClosed but work is needed to make it safe
331         * to do so.
332         */
333     }
334
335     /*
336     * accessor method since var is private
337     */
338     public int getFCGIError() {
339         return errno;
340     }
341     /*
342     * accessor method since var is private
343     */
344     public Exception getException() {
345         return errex;
346     }
347     /*
348     * Re-initializes the stream to read data of the specified type.
349     */
350     public void setReaderType(int streamType) {
351
352         type = streamType;
353         eorStop = false;
354         skip = false;
355         contentLen = 0;
356         paddingLen = 0;
357         stop = rdNext;
358         isClosed = false;
359     }
360
361     /*
362     * Close the stream. This method does not really exist for BufferedInputStream in java,
363     * but is implemented here for compatibility with the FCGI structures being used. It
364     * doent really throw any IOExceptions either, but that's there for compatiblity with
365     * the InputStreamInterface.
366     */
367     public void close() throws IOException{
368         isClosed = true;
369         stop = rdNext;
370     }
371
372     /*
373     * Returns the number of bytes that can be read without blocking.
374     */
375
376     public int available() throws IOException {
377         return stop - rdNext + in.available();
378     }
379
380 }