Put the classes in the com.fastcgi package
[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 *
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 13package com.fastcgi;
61962ef7 14
15import java.io.*;
61962ef7 16
17/**
18 * This stream understands FCGI prototcol.
19 */
20
21public 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