rename this file so it is visible by metacpan et al
[catagits/fcgi2.git] / java / FCGIOutputStream.java
CommitLineData
61962ef7 1/*
2 * @(#)FCGIOutputStream.java
3 *
4 * FastCGi compatibility package Interface
5 *
6 * Copyright (c) 1996 Open Market, Inc.
7 *
af1b4cad 8 * See the file "LICENSE" for information on usage and redistribution
61962ef7 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 13package com.fastcgi;
61962ef7 14
15import java.io.*;
61962ef7 16
17/**
18 * This stream understands FCGI prototcol.
19 */
20
05f58140 21public 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