--- /dev/null
+/*
+ * @(#)FCGIGlobalDefs.java
+ *
+ *
+ * FastCGi compatibility package Interface
+ *
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * $Id: FCGIGlobalDefs.java,v 1.1 1999/01/31 02:45:51 roberts Exp $
+ */
+
+/* This class contains FCGI global definitions corresponding to
+ * the #defs in the C version.
+ */
+import java.io.PrintStream;
+
+public abstract class FCGIGlobalDefs {
+
+ public static final int def_FCGIMaxLen = 0xffff;
+ /*
+ * Define Length of FCGI message bodies in bytes
+ */
+ public static final int def_FCGIHeaderLen = 8;
+ public static final int def_FCGIEndReqBodyLen = 8;
+ public static final int def_FCGIBeginReqBodyLen = 8;
+ public static final int def_FCGIUnknownBodyTypeBodyLen = 8;
+ /*
+ * Header defines
+ */
+ public static int def_FCGIVersion1 = 1;
+ /* FCGI Record Types */
+ public static final int def_FCGIBeginRequest = 1;
+ public static final int def_FCGIAbortRequest = 2;
+ public static final int def_FCGIEndRequest = 3;
+ public static final int def_FCGIParams = 4;
+ public static final int def_FCGIStdin = 5;
+ public static final int def_FCGIStdout = 6;
+ public static final int def_FCGIStderr = 7;
+ public static final int def_FCGIData = 8;
+ public static final int def_FCGIGetValues = 9;
+ public static final int def_FCGIGetValuesResult = 10;
+ public static final int def_FCGIUnknownType = 11;
+ public static final int def_FCGIMaxType = def_FCGIUnknownType;
+ /* Request ID Values */
+ public static final int def_FCGINullRequestID = 0;
+ /*
+ * Begin Request defines
+ */
+ /* Mask flags */
+ public static int def_FCGIKeepConn = 1;
+ /* Roles */
+ public static final int def_FCGIResponder = 1;
+ public static final int def_FCGIAuthorizer = 2;
+ public static final int def_FCGIFilter = 3;
+ /*
+ * End Request defines
+ */
+ /* Protocol status */
+ public static final int def_FCGIRequestComplete = 0;
+ public static final int def_FCGICantMpxConn = 1;
+ public static final int def_FCGIOverload = 2;
+ public static final int def_FCGIUnknownRole = 3;
+ /*
+ * Get Values, Get Values Results defines
+ */
+ public static final String def_FCGIMaxConns = "FCGI_MAX_CONNS";
+ public static final String def_FCGIMaxReqs = "FCGI_MAX_REQS";
+ public static final String def_FCGIMpxsConns = "FCGI_MPXS_CONNS";
+ /*
+ * Return codes for Process* functions
+ */
+ public static final int def_FCGIStreamRecord = 0;
+ public static final int def_FCGISkip = 1;
+ public static final int def_FCGIBeginRecord = 2;
+ public static final int def_FCGIMgmtRecord = 3;
+ /*
+ * Error Codes
+ */
+ public static final int def_FCGIUnsupportedVersion = -2;
+ public static final int def_FCGIProtocolError = -3;
+ public static final int def_FCGIParamsError = -4;
+ public static final int def_FCGICallSeqError = -5;
+}
--- /dev/null
+/*
+ * @(#)FCGIInputStream.java
+ *
+ * FastCGi compatibility package Interface
+ *
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * $Id: FCGIInputStream.java,v 1.1 1999/01/31 02:45:49 roberts Exp $
+ */
+
+import java.io.*;
+import FCGIRequest;
+import FCGIGlobalDefs;
+
+/**
+ * This stream manages buffered reads of FCGI messages.
+ */
+public class FCGIInputStream extends InputStream {
+
+ /* Stream vars */
+
+ public int rdNext;
+ public int stop;
+ public boolean isClosed;
+
+ /* require methods to set, get and clear */
+ private int errno;
+ private Exception errex;
+
+ /* data vars */
+
+ public byte buff[];
+ public int buffLen;
+ public int buffStop;
+ public int type;
+ public int contentLen;
+ public int paddingLen;
+ public boolean skip;
+ public boolean eorStop;
+ public FCGIRequest request;
+
+ public InputStream in;
+
+
+ /**
+ * Creates a new input stream to manage fcgi prototcol stuff
+ * @param in the input stream bufLen length of buffer streamType
+ */
+ public FCGIInputStream(FileInputStream inStream, int bufLen,
+ int streamType,
+ FCGIRequest inReq) {
+
+ in = inStream;
+ buffLen = Math.min(bufLen,FCGIGlobalDefs.def_FCGIMaxLen);
+ buff = new byte[buffLen];
+ type = streamType;
+ stop = rdNext = buffStop = 0;
+ isClosed = false;
+ contentLen = 0;
+ paddingLen = 0;
+ skip = false;
+ eorStop = false;
+ request = inReq;
+
+ }
+ /**
+ * Reads a byte of data. This method will block if no input is
+ * available.
+ * @return the byte read, or -1 if the end of the
+ * stream is reached.
+ * @exception IOException If an I/O error has occurred.
+ */
+ public int read() throws IOException {
+ if (rdNext != stop) {
+ return buff[rdNext++];
+ }
+ if (isClosed){
+ return -1;
+ }
+ fill();
+ if (rdNext != stop){
+ return buff[rdNext++];
+ }
+ return -1;
+ }
+ /**
+ * Reads into an array of bytes. This method will
+ * block until some input is available.
+ * @param b the buffer into which the data is read
+ * @return the actual number of bytes read, -1 is
+ * returned when the end of the stream is reached.
+ * @exception IOException If an I/O error has occurred.
+ */
+ public int read(byte b[]) throws IOException {
+ return read(b, 0, b.length);
+ }
+
+ /**
+ * Reads into an array of bytes.
+ * Blocks until some input is available.
+ * @param b the buffer into which the data is read
+ * @param off the start offset of the data
+ * @param len the maximum number of bytes read
+ * @return the actual number of bytes read, -1 is
+ * returned when the end of the stream is reached.
+ * @exception IOException If an I/O error has occurred.
+ */
+ public int read(byte b[], int off, int len) throws IOException {
+ int m, bytesMoved;
+
+ if (len <= 0){
+ return 0;
+ }
+ /*
+ *Fast path: len bytes already available.
+ */
+
+ if (len <= stop - rdNext){
+ System.arraycopy(buff, rdNext, b, off, len);
+ rdNext += len;
+ return len;
+ }
+ /*
+ *General case: stream is closed or fill needs to be called
+ */
+ bytesMoved = 0;
+ for(;;){
+ if (rdNext != stop){
+ m = Math.min(len - bytesMoved, stop - rdNext);
+ System.arraycopy(buff, rdNext, b, off, m);
+ bytesMoved += m;
+ rdNext += m;
+ if (bytesMoved == len)
+ return bytesMoved;
+ off += m;
+ }
+ if (isClosed){
+ return bytesMoved;
+ }
+ fill();
+
+ }
+ }
+ /**
+ * Reads into an array of bytes. This method will
+ * block until some input is available.
+ * @param b the buffer into which the data is read
+ * @param off the start offset of the data
+ * @param len the maximum number of bytes read
+ * @return the actual number of bytes read, -1 is
+ * returned when the end of the stream is reached.
+ * @exception IOException If an I/O error has occurred.
+ */
+ public void fill() throws IOException {
+ byte[] headerBuf = new byte[FCGIGlobalDefs.def_FCGIHeaderLen];
+ int headerLen = 0;
+ int status = 0;
+ int count = 0;
+ for(;;) {
+ /*
+ * If buffer is empty, do a read
+ */
+ if (rdNext == buffStop) {
+ try {
+ count = in.read(buff, 0, buffLen);
+ } catch (IOException e) {
+ setException(e);
+ return;
+ }
+ if (count == 0) {
+ setFCGIError(FCGIGlobalDefs.def_FCGIProtocolError);
+ return;
+ }
+ rdNext = 0;
+ buffStop = count; // 1 more than we read
+ }
+ /* Now buf is not empty: If the current record contains more content
+ * bytes, deliver all that are present in buff to callers buffer
+ * unless he asked for less than we have, in which case give him less
+ */
+ if (contentLen > 0) {
+ count = Math.min(contentLen, buffStop - rdNext);
+ contentLen -= count;
+ if (!skip) {
+ stop = rdNext + count;
+ return;
+ }
+ else {
+ rdNext += count;
+ if (contentLen > 0) {
+ continue;
+ }
+ else {
+ skip = false;
+ }
+ }
+ }
+ /* Content has been consumed by client.
+ * If record was padded, skip over padding
+ */
+ if (paddingLen > 0) {
+ count = Math.min(paddingLen, buffStop - rdNext);
+ paddingLen -= count;
+ rdNext += count;
+ if (paddingLen > 0) {
+ continue; // more padding to read
+ }
+ }
+ /* All done with current record, including the padding.
+ * If we are in a recursive call from Process Header, deliver EOF
+ */
+ if (eorStop){
+ stop = rdNext;
+ isClosed = true;
+ return;
+ }
+ /*
+ * Fill header with bytes from input buffer - get the whole header.
+ */
+ count = Math.min(headerBuf.length - headerLen, buffStop - rdNext);
+ System.arraycopy(buff,rdNext, headerBuf, headerLen, count);
+ headerLen += count;
+ rdNext += count;
+ if (headerLen < headerBuf.length) {
+ continue;
+ }
+ headerLen = 0;
+ /*
+ * Interperet the header. eorStop prevents ProcessHeader from
+ * reading past the end of record when using stream to read content
+ */
+ eorStop = true;
+ stop = rdNext;
+ status = 0;
+ status = new FCGIMessage(this).processHeader(headerBuf);
+ eorStop = false;
+ isClosed = false;
+ switch (status){
+ case FCGIGlobalDefs.def_FCGIStreamRecord:
+ if (contentLen == 0) {
+ stop = rdNext;
+ isClosed = true;
+ return;
+ }
+ break;
+ case FCGIGlobalDefs.def_FCGISkip:
+ skip = true;
+ break;
+ case FCGIGlobalDefs.def_FCGIBeginRecord:
+ /*
+ * If this header marked the beginning of a new
+ * request, return role info to caller
+ */
+ return;
+ case FCGIGlobalDefs.def_FCGIMgmtRecord:
+ break;
+ default:
+ /*
+ * ASSERT
+ */
+ setFCGIError(status);
+ return;
+
+ }
+ }
+ }
+
+ /**
+ * Skips n bytes of input.
+ * @param n the number of bytes to be skipped
+ * @return the actual number of bytes skipped.
+ * @exception IOException If an I/O error has occurred.
+ */
+ public long skip(long n) throws IOException {
+ byte data[] = new byte[(int)n];
+ return in.read(data);
+ }
+
+ /*
+ * An FCGI error has occurred. Save the error code in the stream
+ * for diagnostic purposes and set the stream state so that
+ * reads return EOF
+ */
+ public void setFCGIError(int errnum) {
+ /*
+ * Preserve only the first error.
+ */
+ if(errno == 0) {
+ errno = errnum;
+ }
+ isClosed = true;
+ }
+ /*
+ * An Exception has occurred. Save the Exception in the stream
+ * for diagnostic purposes and set the stream state so that
+ * reads return EOF
+ */
+ public void setException(Exception errexpt) {
+ /*
+ * Preserve only the first error.
+ */
+ if(errex == null) {
+ errex = errexpt;
+ }
+ isClosed = true;
+ }
+
+ /*
+ * Clear the stream error code and end-of-file indication.
+ */
+ public void clearFCGIError() {
+ errno = 0;
+ /*
+ * isClosed = false;
+ * XXX: should clear isClosed but work is needed to make it safe
+ * to do so.
+ */
+ }
+ /*
+ * Clear the stream error code and end-of-file indication.
+ */
+ public void clearException() {
+ errex = null;
+ /*
+ * isClosed = false;
+ * XXX: should clear isClosed but work is needed to make it safe
+ * to do so.
+ */
+ }
+
+ /*
+ * accessor method since var is private
+ */
+ public int getFCGIError() {
+ return errno;
+ }
+ /*
+ * accessor method since var is private
+ */
+ public Exception getException() {
+ return errex;
+ }
+ /*
+ * Re-initializes the stream to read data of the specified type.
+ */
+ public void setReaderType(int streamType) {
+
+ type = streamType;
+ eorStop = false;
+ skip = false;
+ contentLen = 0;
+ paddingLen = 0;
+ stop = rdNext;
+ isClosed = false;
+ }
+
+ /*
+ * Close the stream. This method does not really exist for BufferedInputStream in java,
+ * but is implemented here for compatibility with the FCGI structures being used. It
+ * doent really throw any IOExceptions either, but that's there for compatiblity with
+ * the InputStreamInterface.
+ */
+ public void close() throws IOException{
+ isClosed = true;
+ stop = rdNext;
+ }
+
+ /*
+ * Returns the number of bytes that can be read without blocking.
+ */
+
+ public int available() throws IOException {
+ return stop - rdNext + in.available();
+ }
+
+}
--- /dev/null
+/*
+ * @(#)FCGIInterface.java
+ *
+ *
+ * FastCGi compatibility package Interface
+ *
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * $Id: FCGIInterface.java,v 1.1 1999/01/31 02:45:49 roberts Exp $
+ */
+
+import java.net.*;
+import java.io.*;
+import java.util.Properties;
+import FCGIGlobalDefs;
+import FCGIRequest;
+import FCGIInputStream;
+import FCGIOutputStream;
+import FCGIMessage;
+
+/*
+ * This is the FastCGI interface that the application calls to communicate with the
+ * FastCGI web server. This version is single threaded, and handles one request at
+ * a time, which is why we can have a static variable for it.
+ */
+public class FCGIInterface {
+
+ /*
+ * Class variables
+ */
+ public static FCGIRequest request = null;
+ public static boolean acceptCalled = false;
+ public static boolean isFCGI = true;
+ public static Properties startupProps;
+ public static ServerSocket srvSocket;
+
+ /*
+ * Accepts a new request from the HTTP server and creates
+ * a conventional execution environment for the request.
+ * If the application was invoked as a FastCGI server,
+ * the first call to FCGIaccept indicates that the application
+ * has completed its initialization and is ready to accept
+ * a request. Subsequent calls to FCGI_accept indicate that
+ * the application has completed its processing of the
+ * current request and is ready to accept a new request.
+ * If the application was invoked as a CGI program, the first
+ * call to FCGIaccept is essentially a no-op and the second
+ * call returns EOF (-1) as does an error. Application should exit.
+ *
+ * If the application was invoked as a FastCGI server,
+ * and this is not the first call to this procedure,
+ * FCGIaccept first flushes any buffered output to the HTTP server.
+ *
+ * On every call, FCGIaccept accepts the new request and
+ * reads the FCGI_PARAMS stream into System.props. It also creates
+ * streams that understand FastCGI protocol and take input from
+ * the HTTP server send output and error output to the HTTP server,
+ * and assigns these new streams to System.in, System.out and
+ * System.err respectively.
+ *
+ * For now, we will just return an int to the caller, which is why
+ * this method catches, but doen't throw Exceptions.
+ *
+ */
+ public int FCGIaccept() {
+ int acceptResult = 0;
+
+ /*
+ * If first call, mark it and if fcgi save original system properties,
+ * If not first call, and we are cgi, we should be gone.
+ */
+ if (!acceptCalled){
+ isFCGI = System.getProperties().containsKey("FCGI_PORT");
+ acceptCalled = true;
+ if (isFCGI) {
+ /*
+ * save original system properties (nonrequest)
+ * and get a server socket
+ */
+ System.out.close();
+ System.err.close();
+ startupProps = new Properties(System.getProperties());
+ String str =
+ new String(System.getProperty("FCGI_PORT"));
+ if (str.length() <= 0) {
+ return -1;
+ }
+ int portNum = Integer.parseInt(str);
+
+ try {
+ srvSocket = new ServerSocket(portNum);
+ } catch (IOException e) {
+ request.socket = null;
+ srvSocket = null;
+ request = null;
+ return -1;
+ }
+ }
+ }
+ else {
+ if (!isFCGI){
+ return -1;
+ }
+ }
+ /*
+ * If we are cgi, just leave everything as is, otherwise set up env
+ */
+ if (isFCGI){
+ try {
+ acceptResult = FCGIAccept();
+ } catch (IOException e) {
+ return -1;
+ }
+ if (acceptResult < 0){
+ return -1;
+ }
+
+ /*
+ * redirect stdin, stdout and stderr to fcgi socket
+ */
+ System.setIn(new BufferedInputStream(request.inStream, 8192));
+ System.setOut(new PrintStream(new BufferedOutputStream(
+ request.outStream, 8192)));
+ System.setErr(new PrintStream(new BufferedOutputStream(
+ request.errStream, 512)));
+ System.setProperties(request.params);
+ }
+ return 0;
+ }
+
+ /*
+ * Accepts a new request from the HTTP server.
+ * Finishes the request accepted by the previous call
+ * to FCGI_Accept. Sets up the FCGI environment and reads
+ * saved and per request environmental varaibles into
+ * the request object. (This is redundant on System.props
+ * as long as we can handle only one request object.)
+ */
+ int FCGIAccept() throws IOException{
+
+ boolean isNewConnection;
+ boolean errCloseEx = false;
+ boolean outCloseEx = false;
+
+ if (request != null) {
+ /*
+ * Complete the previous request
+ */
+ System.err.close();
+ System.out.close();
+ boolean prevRequestfailed = (errCloseEx || outCloseEx ||
+ request.inStream.getFCGIError() != 0 ||
+ request.inStream.getException() != null);
+ if (prevRequestfailed || !request.keepConnection ) {
+ request.socket.close();
+ request.socket = null;
+ }
+ if (prevRequestfailed) {
+ request = null;
+ return -1;
+ }
+ }
+ else {
+ /*
+ * Get a Request and initialize some variables
+ */
+ request = new FCGIRequest();
+ request.socket = null;
+ request.inStream = null;
+ }
+ isNewConnection = false;
+
+ /*
+ * if connection isnt open accept a new connection (blocking)
+ */
+ for(;;) {
+ if (request.socket == null){
+ try {
+ request.socket = srvSocket.accept();
+ } catch (IOException e) {
+ request.socket = null;
+ request = null;
+ return -1;
+ }
+ isNewConnection = true;
+ }
+
+ /* Try reading from new connection. If the read fails and
+ * it was an old connection the web server probably closed it;
+ * try making a new connection before giving up
+ */
+ request.isBeginProcessed = false;
+ request.inStream =
+ new FCGIInputStream((FileInputStream)request.
+ socket.getInputStream(),
+ 8192, 0, request);
+ request.inStream.fill();
+ if (request.isBeginProcessed) {
+ break;
+ }
+ request.socket.close();
+
+ request.socket = null;
+ if (isNewConnection) {
+ return -1;
+ }
+ }
+ /*
+ * Set up the objects for the new request
+ */
+ request.params = new Properties(startupProps);
+ switch(request.role) {
+ case FCGIGlobalDefs.def_FCGIResponder:
+ request.params.put("ROLE","RESPONDER");
+ break;
+ case FCGIGlobalDefs.def_FCGIAuthorizer:
+ request.params.put("ROLE", "AUTHORIZER");
+ break;
+ case FCGIGlobalDefs.def_FCGIFilter:
+ request.params.put("ROLE", "FILTER");
+ break;
+ default:
+ return -1;
+ }
+ request.inStream.setReaderType(FCGIGlobalDefs.def_FCGIParams);
+ /*
+ * read the rest of request parameters
+ */
+ if (new FCGIMessage(request.inStream).readParams(request.params) < 0) {
+ return -1;
+ }
+ request.inStream.setReaderType(FCGIGlobalDefs.def_FCGIStdin);
+ request.outStream
+ = new FCGIOutputStream((FileOutputStream)request.socket.
+ getOutputStream(), 8192,
+ FCGIGlobalDefs.def_FCGIStdout,request);
+ request.errStream
+ = new FCGIOutputStream((FileOutputStream)request.socket.
+ getOutputStream(), 512,
+ FCGIGlobalDefs.def_FCGIStderr,request);
+ request.numWriters = 2;
+ return 0;
+ }
+}
--- /dev/null
+/*
+ * @(#)FCGIMessage.java
+ *
+ *
+ * FastCGi compatibility package Interface
+ *
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * $Id: FCGIMessage.java,v 1.1 1999/01/31 02:45:52 roberts Exp $
+ */
+
+import java.io.*;
+import java.util.Properties;
+
+/* This class handles reading and building the fastcgi messages.
+ * For reading incoming mesages, we pass the input
+ * stream as a param to the constructor rather than to each method.
+ * Methods that build messages use and return internal buffers, so they
+ * dont need a stream.
+ */
+
+public class FCGIMessage {
+
+ /*
+ * Instance variables
+ */
+ /*
+ * FCGI Message Records
+ * The logical structures of the FCGI Message Records.
+ * Fields are originally 1 unsigned byte in message
+ * unless otherwise noted.
+ */
+ /*
+ * FCGI Header
+ */
+ private int h_version;
+ private int h_type;
+ private int h_requestID; // 2 bytes
+ private int h_contentLength; // 2 bytes
+ private int h_paddingLength;
+ /*
+ * FCGI BeginRequest body.
+ */
+ private int br_role; // 2 bytes
+ private int br_flags;
+
+ private FCGIInputStream in;
+
+ /*
+ * constructor - Java would do this implicitly.
+ */
+ public FCGIMessage(){
+ super();
+ }
+ /*
+ * constructor - get the stream.
+ */
+ public FCGIMessage(FCGIInputStream instream){
+ in = instream;
+ }
+
+ /*
+ * Message Reading Methods
+ */
+
+ /*
+ * Interpret the FCGI Message Header. Processes FCGI
+ * BeginRequest and Management messages. Param hdr is the header.
+ * The calling routine has to keep track of the stream reading
+ * management or use FCGIInputStream.fill() which does just that.
+ */
+ public int processHeader(byte[] hdr) throws IOException{
+ processHeaderBytes(hdr);
+ if (h_version != FCGIGlobalDefs.def_FCGIVersion1) {
+ return(FCGIGlobalDefs.def_FCGIUnsupportedVersion);
+ }
+ in.contentLen = h_contentLength;
+ in.paddingLen = h_paddingLength;
+ if (h_type == FCGIGlobalDefs.def_FCGIBeginRequest) {
+ return processBeginRecord(h_requestID);
+ }
+ if (h_requestID == FCGIGlobalDefs.def_FCGINullRequestID) {
+ return processManagementRecord(h_type);
+ }
+ if (h_requestID != in.request.requestID) {
+ return(FCGIGlobalDefs.def_FCGISkip);
+ }
+ if (h_type != in.type) {
+ return(FCGIGlobalDefs.def_FCGIProtocolError);
+ }
+ return(FCGIGlobalDefs.def_FCGIStreamRecord);
+ }
+
+ /* Put the unsigned bytes in the incoming FCGI header into
+ * integer form for Java, concatinating bytes when needed.
+ * Because Java has no unsigned byte type, we have to be careful
+ * about signed numeric promotion to int.
+ */
+ private void processHeaderBytes(byte[] hdrBuf){
+ h_version = hdrBuf[0] & 0xFF;
+ h_type = hdrBuf[1] & 0xFF;
+ h_requestID = ((hdrBuf[2] & 0xFF) << 8) | (hdrBuf[3] & 0xFF);
+ h_contentLength = ((hdrBuf[4] & 0xFF) << 8) | (hdrBuf[5] & 0xFF);
+ h_paddingLength = hdrBuf[6] & 0xFF;
+ }
+
+ /*
+ * Reads FCGI Begin Request Record.
+ */
+ public int processBeginRecord(int requestID) throws IOException {
+ byte beginReqBody[];
+ byte endReqMsg[];
+ if (requestID == 0 || in.contentLen
+ != FCGIGlobalDefs.def_FCGIEndReqBodyLen) {
+ return FCGIGlobalDefs.def_FCGIProtocolError;
+ }
+ /*
+ * If the webserver is multiplexing the connection,
+ * this library can't deal with it, so repond with
+ * FCGIEndReq message with protocolStatus FCGICantMpxConn
+ */
+ if (in.request.isBeginProcessed) {
+ endReqMsg = new byte[FCGIGlobalDefs.def_FCGIHeaderLen
+ + FCGIGlobalDefs.def_FCGIEndReqBodyLen];
+ System.arraycopy(makeHeader(
+ FCGIGlobalDefs.def_FCGIEndRequest,
+ requestID,
+ FCGIGlobalDefs.def_FCGIEndReqBodyLen,
+ 0), 0, endReqMsg, 0,
+ FCGIGlobalDefs.def_FCGIHeaderLen);
+ System.arraycopy(makeEndrequestBody(0,
+ FCGIGlobalDefs.def_FCGICantMpxConn), 0,
+ endReqMsg,
+ FCGIGlobalDefs.def_FCGIHeaderLen,
+ FCGIGlobalDefs.def_FCGIEndReqBodyLen);
+ /*
+ * since isBeginProcessed is first set below,this
+ * can't be out first call, so request.out is properly set
+ */
+ try {
+ in.request.outStream.write(endReqMsg, 0,
+ FCGIGlobalDefs.def_FCGIHeaderLen
+ + FCGIGlobalDefs.def_FCGIEndReqBodyLen);
+ } catch (IOException e){
+ in.request.outStream.setException(e);
+ return -1;
+ }
+ }
+ /*
+ * Accept this new request. Read the record body
+ */
+ in.request.requestID = requestID;
+ beginReqBody =
+ new byte[FCGIGlobalDefs.def_FCGIBeginReqBodyLen];
+ if (in.read(beginReqBody, 0,
+ FCGIGlobalDefs.def_FCGIBeginReqBodyLen) !=
+ FCGIGlobalDefs.def_FCGIBeginReqBodyLen) {
+ return FCGIGlobalDefs.def_FCGIProtocolError;
+ }
+ br_flags = beginReqBody[2] & 0xFF;
+ in.request.keepConnection
+ = (br_flags & FCGIGlobalDefs.def_FCGIKeepConn) != 0;
+ br_role = ((beginReqBody[0] & 0xFF) << 8) | (beginReqBody[1] & 0xFF);
+ in.request.role = br_role;
+ in.request.isBeginProcessed = true;
+ return FCGIGlobalDefs.def_FCGIBeginRecord;
+ }
+
+ /*
+ * Reads and Responds to a Management Message. The only type of
+ * management message this library understands is FCGIGetValues.
+ * The only variables that this library's FCGIGetValues understands
+ * are def_FCGIMaxConns, def_FCGIMaxReqs, and def_FCGIMpxsConns.
+ * Ignore the other management variables, and repsond to other
+ * management messages with FCGIUnknownType.
+ */
+ public int processManagementRecord(int type) throws IOException {
+
+ byte[] response = new byte[64];
+ int wrndx = response[FCGIGlobalDefs.def_FCGIHeaderLen];
+ int value, len, plen;
+ if (type == FCGIGlobalDefs.def_FCGIGetValues) {
+ Properties tmpProps = new Properties();
+ readParams(tmpProps);
+
+ if (in.getFCGIError() != 0 || in.contentLen != 0) {
+ return FCGIGlobalDefs.def_FCGIProtocolError;
+ }
+ if (tmpProps.containsKey(
+ FCGIGlobalDefs.def_FCGIMaxConns)) {
+ makeNameVal(
+ FCGIGlobalDefs.def_FCGIMaxConns, "1",
+ response, wrndx);
+ }
+ else {
+ if (tmpProps.containsKey(
+ FCGIGlobalDefs.def_FCGIMaxReqs)) {
+ makeNameVal(
+ FCGIGlobalDefs.def_FCGIMaxReqs, "1",
+ response, wrndx);
+ }
+ else {
+ if (tmpProps.containsKey(
+ FCGIGlobalDefs.def_FCGIMaxConns)) {
+ makeNameVal(
+ FCGIGlobalDefs.def_FCGIMpxsConns, "0",
+ response, wrndx);
+ }
+ }
+ }
+ plen = 64 - wrndx;
+ len = wrndx - FCGIGlobalDefs.def_FCGIHeaderLen;
+ System.arraycopy(makeHeader(
+ FCGIGlobalDefs.def_FCGIGetValuesResult,
+ FCGIGlobalDefs.def_FCGINullRequestID,
+ len, plen), 0,
+ response, 0,
+ FCGIGlobalDefs.def_FCGIHeaderLen);
+ }
+ else {
+ plen = len =
+ FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen;
+ System.arraycopy(makeHeader(
+ FCGIGlobalDefs.def_FCGIUnknownType,
+ FCGIGlobalDefs.def_FCGINullRequestID,
+ len, 0), 0,
+ response, 0,
+ FCGIGlobalDefs.def_FCGIHeaderLen);
+ System.arraycopy(makeUnknownTypeBodyBody(h_type), 0,
+ response,
+ FCGIGlobalDefs.def_FCGIHeaderLen,
+ FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen);
+ }
+ /*
+ * No guarantee that we have a request yet, so
+ * dont use fcgi output stream to reference socket, instead
+ * use the FileInputStream that refrences it. Also
+ * nowhere to save exception, since this is not FCGI stream.
+ */
+
+ try {
+ in.request.socket.getOutputStream().write(response, 0,
+ FCGIGlobalDefs.def_FCGIHeaderLen +
+ FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen);
+
+ } catch (IOException e){
+ return -1;
+ }
+ return FCGIGlobalDefs.def_FCGIMgmtRecord;
+ }
+
+ /*
+ * Makes a name/value with name = string of some length, and
+ * value a 1 byte integer. Pretty specific to what we are doing
+ * above.
+ */
+ void makeNameVal(String name, String value, byte[] dest, int pos) {
+ int nameLen = name.length();
+ if (nameLen < 0x80) {
+ dest[pos++] = (byte)nameLen;
+ }else {
+ dest[pos++] = (byte)(((nameLen >> 24) | 0x80) & 0xff);
+ dest[pos++] = (byte)((nameLen >> 16) & 0xff);
+ dest[pos++] = (byte)((nameLen >> 8) & 0xff);
+ dest[pos++] = (byte)nameLen;
+ }
+ int valLen = value.length();
+ if (valLen < 0x80) {
+ dest[pos++] = (byte)valLen;
+ }else {
+ dest[pos++] = (byte)(((valLen >> 24) | 0x80) & 0xff);
+ dest[pos++] = (byte)((valLen >> 16) & 0xff);
+ dest[pos++] = (byte)((valLen >> 8) & 0xff);
+ dest[pos++] = (byte)valLen;
+ }
+ name.getBytes(0, nameLen, dest, pos);
+ pos += nameLen;
+ value.getBytes(0, valLen, dest, pos);
+ pos += valLen;
+ }
+
+ /*
+ * Read FCGI name-value pairs from a stream until EOF. Put them
+ * into a Properties object, storing both as strings.
+ */
+ public int readParams(Properties props) throws IOException{
+ int nameLen, valueLen;
+ byte lenBuff[] = new byte[3];
+ int i = 1;
+
+ while ((nameLen = in.read()) != -1) {
+ i++;
+ if ((nameLen & 0x80) != 0) {
+ if ((in.read( lenBuff, 0, 3)) != 3) {
+ in.setFCGIError(
+ FCGIGlobalDefs.def_FCGIParamsError);
+ return -1;
+ }
+ nameLen = ((nameLen & 0x7f) << 24)
+ | ((lenBuff[0] & 0xFF) << 16)
+ | ((lenBuff[1] & 0xFF) << 8)
+ | (lenBuff[2] & 0xFF);
+ }
+
+ if ((valueLen = in.read()) == -1) {
+ in.setFCGIError(
+ FCGIGlobalDefs.def_FCGIParamsError);
+ return -1;
+ }
+ if ((valueLen & 0x80) != 0) {
+ if ((in.read( lenBuff, 0, 3)) != 3) {
+ in.setFCGIError(
+ FCGIGlobalDefs.def_FCGIParamsError);
+ return -1;
+ }
+ valueLen = ((valueLen & 0x7f) << 24)
+ | ((lenBuff[0] & 0xFF) << 16)
+ | ((lenBuff[1] & 0xFF) << 8)
+ | (lenBuff[2] & 0xFF);
+ }
+
+ /*
+ * nameLen and valueLen are now valid; read the name
+ * and the value from the stream and construct a standard
+ * environmental entity
+ */
+ byte[] name = new byte[nameLen];
+ byte[] value = new byte[valueLen];
+ if (in.read(name ,0, nameLen) != nameLen) {
+ in.setFCGIError(
+ FCGIGlobalDefs.def_FCGIParamsError);
+ return -1;
+ }
+
+ if(in.read(value, 0, valueLen) != valueLen) {
+ in.setFCGIError(
+ FCGIGlobalDefs.def_FCGIParamsError);
+ return -1;
+ }
+ String strName = new String(name, 0, 0, name.length);
+ String strValue = new String(value, 0, 0, value.length);
+ props.put(strName, strValue);
+ }
+ return 0;
+
+
+ }
+ /*
+ * Message Building Methods
+ */
+
+ /*
+ * Build an FCGI Message Header -
+ */
+ public byte[] makeHeader(int type,
+ int requestId,
+ int contentLength,
+ int paddingLength) {
+ byte[] header = new byte[FCGIGlobalDefs.def_FCGIHeaderLen];
+ header[0] = (byte)FCGIGlobalDefs.def_FCGIVersion1;
+ header[1] = (byte)type;
+ header[2] = (byte)((requestId >> 8) & 0xff);
+ header[3] = (byte)((requestId ) & 0xff);
+ header[4] = (byte)((contentLength >> 8) & 0xff);
+ header[5] = (byte)((contentLength ) & 0xff);
+ header[6] = (byte)paddingLength;
+ header[7] = 0; //reserved byte
+ return header;
+ }
+ /*
+ * Build an FCGI Message End Request Body
+ */
+ public byte[] makeEndrequestBody(int appStatus,int protocolStatus){
+ byte body[] = new byte[FCGIGlobalDefs.def_FCGIEndReqBodyLen];
+ body[0] = (byte)((appStatus >> 24) & 0xff);
+ body[1] = (byte)((appStatus >> 16) & 0xff);
+ body[2] = (byte)((appStatus >> 8) & 0xff);
+ body[3] = (byte)((appStatus ) & 0xff);
+ body[4] = (byte)protocolStatus;
+ for (int i = 5; i < 8; i++) {
+ body[i] = 0;
+ }
+ return body;
+ }
+ /*
+ * Build an FCGI Message UnknownTypeBodyBody
+ */
+ public byte[] makeUnknownTypeBodyBody(int type){
+ byte body[] =
+ new byte[FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen];
+ body[0] = (byte)type;
+ for (int i = 1;
+ i < FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen; i++) {
+ body[i] = 0;
+ }
+ return body;
+ }
+
+} //end class
--- /dev/null
+/*
+ * @(#)FCGIOutputStream.java
+ *
+ * FastCGi compatibility package Interface
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * $Id: FCGIOutputStream.java,v 1.1 1999/01/31 02:45:53 roberts Exp $
+ */
+
+import java.io.*;
+import FCGIRequest;
+import FCGIGlobalDefs;
+import FCGIMessage;
+import FCGIInputStream;
+
+/**
+ * This stream understands FCGI prototcol.
+ */
+
+public class FCGIOutputStream extends OutputStream {
+
+ /* Stream vars */
+
+ public int wrNext;
+ public int stop;
+ public boolean isClosed;
+
+ /* require methods to set, get and clear */
+ private int errno;
+ private Exception errex;
+
+ /* data vars */
+
+ public byte buff[];
+ public int buffLen;
+ public int buffStop;
+ public int type;
+ public boolean isAnythingWritten;
+ public boolean rawWrite;
+ public FCGIRequest request;
+
+ public FileOutputStream out;
+
+ /**
+ * Creates a new output stream to manage fcgi prototcol stuff
+ * @param out the output stream buflen length of buffer streamType
+ */
+ public FCGIOutputStream(FileOutputStream outStream,
+ int bufLen, int streamType,
+ FCGIRequest inreq) {
+ out = outStream;
+ buffLen = Math.min(bufLen, FCGIGlobalDefs.def_FCGIMaxLen);
+ buff = new byte[buffLen];
+ type = streamType;
+ stop = buffStop = buffLen;
+ isAnythingWritten = false;
+ rawWrite = false;
+ wrNext = FCGIGlobalDefs.def_FCGIHeaderLen;
+ isClosed = false;
+ request = inreq;
+ }
+
+ /**
+ * Writes a byte to the output stream.
+ */
+ public void write(int c) throws IOException {
+ if(wrNext != stop) {
+ buff[wrNext++] = (byte)c;
+ return;
+ }
+ if(isClosed) {
+ throw new EOFException();
+ }
+ empty(false);
+ if(wrNext != stop) {
+ buff[wrNext++] = (byte)c;
+ return;
+ }
+ /* NOTE: ASSERT(stream->isClosed); */
+ /* bug in emptyBuffProc if not */
+ throw new EOFException();
+ }
+
+ /**
+ * Writes an array of bytes. This method will block until the bytes
+ * are actually written.
+ * @param b the data to be written
+ */
+ public void write(byte b[]) throws IOException{
+ write(b, 0, b.length);
+ }
+
+ /**
+ * Writes len consecutive bytes from off in the array b
+ * into the output stream. Performs no interpretation
+ * of the output bytes. Making the user convert the string to
+ * bytes is in line with current Java practice.
+ */
+ public void write(byte b[], int off, int len) throws IOException {
+ int m, bytesMoved;
+ /*
+ * Fast path: room for n bytes in the buffer
+ */
+ if(len <= (stop - wrNext)) {
+ System.arraycopy(b, off, buff, wrNext, len);
+ wrNext += len;
+ return;
+ }
+ /*
+ * General case: stream is closed or buffer empty procedure
+ * needs to be called
+ */
+ bytesMoved = 0;
+ for (;;) {
+ if(wrNext != stop) {
+ m = Math.min(len - bytesMoved, stop - wrNext);
+ System.arraycopy(b, off, buff, wrNext, m);
+ bytesMoved += m;
+ wrNext += m;
+ if(bytesMoved == len) {
+ return;
+ }
+ off += m;
+ }
+ if(isClosed) {
+ throw new EOFException();
+ }
+ empty(false);
+ }
+ }
+
+ /**
+ * Encapsulates any buffered stream content in a FastCGI
+ * record. If !doClose, writes the data, making the buffer
+ * empty.
+ */
+ public void empty(boolean doClose) throws IOException {
+ int cLen;
+ /*
+ * Alignment padding omitted in Java
+ */
+ if (!rawWrite) {
+ cLen = wrNext - FCGIGlobalDefs.def_FCGIHeaderLen;
+ if(cLen > 0) {
+ System.arraycopy(new FCGIMessage().makeHeader(type,
+ request.requestID, cLen, 0),
+ 0, buff, 0,
+ FCGIGlobalDefs.def_FCGIHeaderLen);
+ }
+ else {
+ wrNext = 0;
+ }
+ }
+ if (doClose) {
+ writeCloseRecords();
+ }
+ if (wrNext != 0) {
+ isAnythingWritten = true;
+ try {
+ out.write(buff, 0, wrNext);
+ } catch (IOException e) {
+ setException(e);
+ return;
+ }
+ wrNext = 0;
+ }
+ /*
+ * The buffer is empty.
+ */
+ if(!rawWrite) {
+ wrNext += FCGIGlobalDefs.def_FCGIHeaderLen;
+ }
+ }
+
+ /**
+ * Close the stream.
+ */
+ public void close() throws IOException {
+ if (isClosed) {
+ return;
+ }
+ empty(true);
+ /*
+ * if isClosed, will return with EOFException from write.
+ */
+ isClosed = true;
+ stop = wrNext;
+ return;
+ }
+
+ /**
+ * Flushes any buffered output.
+ * Server-push is a legitimate application of flush.
+ * Otherwise, it is not very useful, since FCGIAccept
+ * does it implicitly. flush may reduce performance
+ * by increasing the total number of operating system calls
+ * the application makes.
+ */
+ public void flush() throws IOException {
+ if (isClosed) {
+ return;
+ }
+ empty(false);
+ /*
+ * if isClosed, will return with EOFException from write.
+ */
+ return;
+ }
+
+ /**
+ * An FCGI error has occurred. Save the error code in the stream
+ * for diagnostic purposes and set the stream state so that
+ * reads return EOF
+ */
+ public void setFCGIError(int errnum) {
+ /*
+ * Preserve only the first error.
+ */
+ if (errno == 0) {
+ errno = errnum;
+ }
+ isClosed = true;
+ }
+
+ /**
+ * An Exception has occurred. Save the Exception in the stream
+ * for diagnostic purposes and set the stream state so that
+ * reads return EOF
+ */
+ public void setException(Exception errexpt) {
+ /*
+ * Preserve only the first error.
+ */
+ if (errex == null) {
+ errex = errexpt;
+ }
+ isClosed = true;
+ }
+
+ /**
+ * Clear the stream error code and end-of-file indication.
+ */
+ public void clearFCGIError() {
+ errno = 0;
+ /*
+ * isClosed = false;
+ * XXX: should clear isClosed but work is needed to make it safe
+ * to do so.
+ */
+ }
+
+ /**
+ * Clear the stream error code and end-of-file indication.
+ */
+ public void clearException() {
+ errex = null;
+ /*
+ * isClosed = false;
+ * XXX: should clear isClosed but work is needed to make it safe
+ * to do so.
+ */
+ }
+
+ /**
+ * accessor method since var is private
+ */
+ public int etFCGIError() {
+ return errno;
+ }
+
+ /**
+ * accessor method since var is private
+ */
+ public Exception getException() {
+ return errex;
+ }
+
+ /**
+ * Writes an EOF record for the stream content if necessary.
+ * If this is the last writer to close, writes an FCGI_END_REQUEST
+ * record.
+ */
+ public void writeCloseRecords() throws IOException {
+ FCGIMessage msg = new FCGIMessage();
+ /*
+ * Enter rawWrite mode so final records won't be
+ * encapsulated as
+ * stream data.
+ */
+ rawWrite = true;
+ /*
+ * Generate EOF for stream content if needed.
+ */
+ if(!(type == FCGIGlobalDefs.def_FCGIStderr
+ && wrNext == 0
+ && !isAnythingWritten)) {
+ byte hdr[] =
+ new byte[FCGIGlobalDefs.def_FCGIHeaderLen];
+ System.arraycopy(msg.makeHeader(type,
+ request.requestID,
+ 0, 0),
+ 0, hdr,0,
+ FCGIGlobalDefs.def_FCGIHeaderLen);
+ write(hdr, 0, hdr.length);
+ }
+ /*
+ * Generate FCGI_END_REQUEST record if needed.
+ */
+ if(request.numWriters == 1) {
+ byte endReq[] =
+ new byte[FCGIGlobalDefs.def_FCGIHeaderLen
+ + FCGIGlobalDefs.def_FCGIEndReqBodyLen];
+ System.arraycopy(msg.makeHeader(
+ FCGIGlobalDefs.def_FCGIEndRequest,
+ request.requestID,
+ FCGIGlobalDefs.def_FCGIEndReqBodyLen,0),
+ 0, endReq, 0,
+ FCGIGlobalDefs.def_FCGIHeaderLen);
+ System.arraycopy(msg.makeEndrequestBody(
+ request.appStatus,
+ FCGIGlobalDefs.def_FCGIRequestComplete),
+ 0,endReq,
+ FCGIGlobalDefs.def_FCGIHeaderLen,
+ FCGIGlobalDefs.def_FCGIEndReqBodyLen);
+ write(endReq,0, FCGIGlobalDefs.def_FCGIHeaderLen
+ + FCGIGlobalDefs.def_FCGIEndReqBodyLen);
+ }
+ request.numWriters--;
+ }
+}
+
--- /dev/null
+/*
+ * @(#)FCGIRequest.java
+ *
+ * FastCGi compatibility package Interface
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE.TERMS" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * $Id: FCGIRequest.java,v 1.1 1999/01/31 02:45:55 roberts Exp $
+ */
+
+import java.net.*;
+import java.io.FileDescriptor;
+import java.util.Properties;
+import FCGIInputStream;
+import FCGIOutputStream;
+public class FCGIRequest {
+
+ /* This class has no methods. Right now we are single threaded
+ * so there is only one request object at any given time which
+ * is refrenced by an FCGIInterface class variable . All of this
+ * object's data could just as easily be declared directly there.
+ * When we thread, this will change, so we might as well use a
+ * seperate class. In line with this thinking, though somewhat
+ * more perversely, we kept the socket here.
+ */
+ /*
+ * class variables
+ */
+ /*public static Socket socket; */
+ // same for all requests
+
+ /*
+ * instance variables
+ */
+ public Socket socket;
+ public boolean isBeginProcessed;
+ public int requestID;
+ public boolean keepConnection;
+ public int role;
+ public int appStatus;
+ public int numWriters;
+ public FCGIInputStream inStream;
+ public FCGIOutputStream outStream;
+ public FCGIOutputStream errStream;
+ public Properties params;
+}
+
+