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