Commit | Line | Data |
61962ef7 |
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 | * |
9c38d1d4 |
12 | * $Id: FCGIInputStream.java,v 1.2 1999/07/29 13:35:50 roberts Exp $ |
61962ef7 |
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) { |
9c38d1d4 |
79 | return buff[rdNext++] & 0xff; |
61962ef7 |
80 | } |
81 | if (isClosed){ |
82 | return -1; |
83 | } |
84 | fill(); |
85 | if (rdNext != stop){ |
9c38d1d4 |
86 | return buff[rdNext++] & 0xff; |
61962ef7 |
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 | } |