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