Commit | Line | Data |
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 |
14 | package com.fastcgi; |
61962ef7 |
15 | |
16 | import java.io.*; |
61962ef7 |
17 | |
18 | /** |
19 | * This stream manages buffered reads of FCGI messages. |
20 | */ |
05f58140 |
21 | public 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 | } |