Commit | Line | Data |
61962ef7 |
1 | /* |
2 | * @(#)FCGIOutputStream.java |
3 | * |
4 | * FastCGi compatibility package Interface |
5 | * |
6 | * Copyright (c) 1996 Open Market, Inc. |
7 | * |
8 | * See the file "LICENSE.TERMS" for information on usage and redistribution |
9 | * of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
10 | * |
07c41236 |
11 | * $Id: FCGIOutputStream.java,v 1.2 2000/03/21 12:02:29 robs Exp $ |
61962ef7 |
12 | */ |
07c41236 |
13 | package com.fastcgi; |
61962ef7 |
14 | |
15 | import java.io.*; |
61962ef7 |
16 | |
17 | /** |
18 | * This stream understands FCGI prototcol. |
19 | */ |
20 | |
21 | public class FCGIOutputStream extends OutputStream { |
22 | |
23 | /* Stream vars */ |
24 | |
25 | public int wrNext; |
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 boolean isAnythingWritten; |
40 | public boolean rawWrite; |
41 | public FCGIRequest request; |
42 | |
43 | public FileOutputStream out; |
44 | |
45 | /** |
46 | * Creates a new output stream to manage fcgi prototcol stuff |
47 | * @param out the output stream buflen length of buffer streamType |
48 | */ |
49 | public FCGIOutputStream(FileOutputStream outStream, |
50 | int bufLen, int streamType, |
51 | FCGIRequest inreq) { |
52 | out = outStream; |
53 | buffLen = Math.min(bufLen, FCGIGlobalDefs.def_FCGIMaxLen); |
54 | buff = new byte[buffLen]; |
55 | type = streamType; |
56 | stop = buffStop = buffLen; |
57 | isAnythingWritten = false; |
58 | rawWrite = false; |
59 | wrNext = FCGIGlobalDefs.def_FCGIHeaderLen; |
60 | isClosed = false; |
61 | request = inreq; |
62 | } |
63 | |
64 | /** |
65 | * Writes a byte to the output stream. |
66 | */ |
67 | public void write(int c) throws IOException { |
68 | if(wrNext != stop) { |
69 | buff[wrNext++] = (byte)c; |
70 | return; |
71 | } |
72 | if(isClosed) { |
73 | throw new EOFException(); |
74 | } |
75 | empty(false); |
76 | if(wrNext != stop) { |
77 | buff[wrNext++] = (byte)c; |
78 | return; |
79 | } |
80 | /* NOTE: ASSERT(stream->isClosed); */ |
81 | /* bug in emptyBuffProc if not */ |
82 | throw new EOFException(); |
83 | } |
84 | |
85 | /** |
86 | * Writes an array of bytes. This method will block until the bytes |
87 | * are actually written. |
88 | * @param b the data to be written |
89 | */ |
90 | public void write(byte b[]) throws IOException{ |
91 | write(b, 0, b.length); |
92 | } |
93 | |
94 | /** |
95 | * Writes len consecutive bytes from off in the array b |
96 | * into the output stream. Performs no interpretation |
97 | * of the output bytes. Making the user convert the string to |
98 | * bytes is in line with current Java practice. |
99 | */ |
100 | public void write(byte b[], int off, int len) throws IOException { |
101 | int m, bytesMoved; |
102 | /* |
103 | * Fast path: room for n bytes in the buffer |
104 | */ |
105 | if(len <= (stop - wrNext)) { |
106 | System.arraycopy(b, off, buff, wrNext, len); |
107 | wrNext += len; |
108 | return; |
109 | } |
110 | /* |
111 | * General case: stream is closed or buffer empty procedure |
112 | * needs to be called |
113 | */ |
114 | bytesMoved = 0; |
115 | for (;;) { |
116 | if(wrNext != stop) { |
117 | m = Math.min(len - bytesMoved, stop - wrNext); |
118 | System.arraycopy(b, off, buff, wrNext, m); |
119 | bytesMoved += m; |
120 | wrNext += m; |
121 | if(bytesMoved == len) { |
122 | return; |
123 | } |
124 | off += m; |
125 | } |
126 | if(isClosed) { |
127 | throw new EOFException(); |
128 | } |
129 | empty(false); |
130 | } |
131 | } |
132 | |
133 | /** |
134 | * Encapsulates any buffered stream content in a FastCGI |
135 | * record. If !doClose, writes the data, making the buffer |
136 | * empty. |
137 | */ |
138 | public void empty(boolean doClose) throws IOException { |
139 | int cLen; |
140 | /* |
141 | * Alignment padding omitted in Java |
142 | */ |
143 | if (!rawWrite) { |
144 | cLen = wrNext - FCGIGlobalDefs.def_FCGIHeaderLen; |
145 | if(cLen > 0) { |
146 | System.arraycopy(new FCGIMessage().makeHeader(type, |
147 | request.requestID, cLen, 0), |
148 | 0, buff, 0, |
149 | FCGIGlobalDefs.def_FCGIHeaderLen); |
150 | } |
151 | else { |
152 | wrNext = 0; |
153 | } |
154 | } |
155 | if (doClose) { |
156 | writeCloseRecords(); |
157 | } |
158 | if (wrNext != 0) { |
159 | isAnythingWritten = true; |
160 | try { |
161 | out.write(buff, 0, wrNext); |
162 | } catch (IOException e) { |
163 | setException(e); |
164 | return; |
165 | } |
166 | wrNext = 0; |
167 | } |
168 | /* |
169 | * The buffer is empty. |
170 | */ |
171 | if(!rawWrite) { |
172 | wrNext += FCGIGlobalDefs.def_FCGIHeaderLen; |
173 | } |
174 | } |
175 | |
176 | /** |
177 | * Close the stream. |
178 | */ |
179 | public void close() throws IOException { |
180 | if (isClosed) { |
181 | return; |
182 | } |
183 | empty(true); |
184 | /* |
185 | * if isClosed, will return with EOFException from write. |
186 | */ |
187 | isClosed = true; |
188 | stop = wrNext; |
189 | return; |
190 | } |
191 | |
192 | /** |
193 | * Flushes any buffered output. |
194 | * Server-push is a legitimate application of flush. |
195 | * Otherwise, it is not very useful, since FCGIAccept |
196 | * does it implicitly. flush may reduce performance |
197 | * by increasing the total number of operating system calls |
198 | * the application makes. |
199 | */ |
200 | public void flush() throws IOException { |
201 | if (isClosed) { |
202 | return; |
203 | } |
204 | empty(false); |
205 | /* |
206 | * if isClosed, will return with EOFException from write. |
207 | */ |
208 | return; |
209 | } |
210 | |
211 | /** |
212 | * An FCGI error has occurred. Save the error code in the stream |
213 | * for diagnostic purposes and set the stream state so that |
214 | * reads return EOF |
215 | */ |
216 | public void setFCGIError(int errnum) { |
217 | /* |
218 | * Preserve only the first error. |
219 | */ |
220 | if (errno == 0) { |
221 | errno = errnum; |
222 | } |
223 | isClosed = true; |
224 | } |
225 | |
226 | /** |
227 | * An Exception has occurred. Save the Exception in the stream |
228 | * for diagnostic purposes and set the stream state so that |
229 | * reads return EOF |
230 | */ |
231 | public void setException(Exception errexpt) { |
232 | /* |
233 | * Preserve only the first error. |
234 | */ |
235 | if (errex == null) { |
236 | errex = errexpt; |
237 | } |
238 | isClosed = true; |
239 | } |
240 | |
241 | /** |
242 | * Clear the stream error code and end-of-file indication. |
243 | */ |
244 | public void clearFCGIError() { |
245 | errno = 0; |
246 | /* |
247 | * isClosed = false; |
248 | * XXX: should clear isClosed but work is needed to make it safe |
249 | * to do so. |
250 | */ |
251 | } |
252 | |
253 | /** |
254 | * Clear the stream error code and end-of-file indication. |
255 | */ |
256 | public void clearException() { |
257 | errex = null; |
258 | /* |
259 | * isClosed = false; |
260 | * XXX: should clear isClosed but work is needed to make it safe |
261 | * to do so. |
262 | */ |
263 | } |
264 | |
265 | /** |
266 | * accessor method since var is private |
267 | */ |
268 | public int etFCGIError() { |
269 | return errno; |
270 | } |
271 | |
272 | /** |
273 | * accessor method since var is private |
274 | */ |
275 | public Exception getException() { |
276 | return errex; |
277 | } |
278 | |
279 | /** |
280 | * Writes an EOF record for the stream content if necessary. |
281 | * If this is the last writer to close, writes an FCGI_END_REQUEST |
282 | * record. |
283 | */ |
284 | public void writeCloseRecords() throws IOException { |
285 | FCGIMessage msg = new FCGIMessage(); |
286 | /* |
287 | * Enter rawWrite mode so final records won't be |
288 | * encapsulated as |
289 | * stream data. |
290 | */ |
291 | rawWrite = true; |
292 | /* |
293 | * Generate EOF for stream content if needed. |
294 | */ |
295 | if(!(type == FCGIGlobalDefs.def_FCGIStderr |
296 | && wrNext == 0 |
297 | && !isAnythingWritten)) { |
298 | byte hdr[] = |
299 | new byte[FCGIGlobalDefs.def_FCGIHeaderLen]; |
300 | System.arraycopy(msg.makeHeader(type, |
301 | request.requestID, |
302 | 0, 0), |
303 | 0, hdr,0, |
304 | FCGIGlobalDefs.def_FCGIHeaderLen); |
305 | write(hdr, 0, hdr.length); |
306 | } |
307 | /* |
308 | * Generate FCGI_END_REQUEST record if needed. |
309 | */ |
310 | if(request.numWriters == 1) { |
311 | byte endReq[] = |
312 | new byte[FCGIGlobalDefs.def_FCGIHeaderLen |
313 | + FCGIGlobalDefs.def_FCGIEndReqBodyLen]; |
314 | System.arraycopy(msg.makeHeader( |
315 | FCGIGlobalDefs.def_FCGIEndRequest, |
316 | request.requestID, |
317 | FCGIGlobalDefs.def_FCGIEndReqBodyLen,0), |
318 | 0, endReq, 0, |
319 | FCGIGlobalDefs.def_FCGIHeaderLen); |
320 | System.arraycopy(msg.makeEndrequestBody( |
321 | request.appStatus, |
322 | FCGIGlobalDefs.def_FCGIRequestComplete), |
323 | 0,endReq, |
324 | FCGIGlobalDefs.def_FCGIHeaderLen, |
325 | FCGIGlobalDefs.def_FCGIEndReqBodyLen); |
326 | write(endReq,0, FCGIGlobalDefs.def_FCGIHeaderLen |
327 | + FCGIGlobalDefs.def_FCGIEndReqBodyLen); |
328 | } |
329 | request.numWriters--; |
330 | } |
331 | } |
332 | |