1 /* 2 * @(#)FCGIInputStream.java 3 * 4 * FastCGi compatibility package Interface 5 * 6 * 7 * Copyright (c) 1996 Open Market, Inc. 8 * 9 * See the file "LICENSE.TERMS" for information on usage and redistribution 10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * $Id: FCGIInputStream.java,v 1.4 2000/03/21 12:12:25 robs Exp $ 13 */ 14 package com.fastcgi; 15 16 import java.io.*; 17 18 /** 19 * This stream manages buffered reads of FCGI messages. 20 */ 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 $"; 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 */ FCGIInputStream(FileInputStream inStream, int bufLen, int streamType, FCGIRequest inReq)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 */ read()78 public int read() throws IOException { 79 if (rdNext != stop) { 80 return buff[rdNext++] & 0xff; 81 } 82 if (isClosed){ 83 return -1; 84 } 85 fill(); 86 if (rdNext != stop){ 87 return buff[rdNext++] & 0xff; 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 */ read(byte b[])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 */ read(byte b[], int off, int len)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 */ fill()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 */ skip(long n)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 */ setFCGIError(int errnum)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 */ setException(Exception errexpt)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 */ clearFCGIError()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 */ clearException()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 */ getFCGIError()339 public int getFCGIError() { 340 return errno; 341 } 342 /* 343 * accessor method since var is private 344 */ getException()345 public Exception getException() { 346 return errex; 347 } 348 /* 349 * Re-initializes the stream to read data of the specified type. 350 */ setReaderType(int streamType)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 */ close()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 available()377 public int available() throws IOException { 378 return stop - rdNext + in.available(); 379 } 380 381 } 382