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