initial commit
[fcgi] / java / FCGIInterface.java
1 /*
2  * @(#)FCGIInterface.java
3  *
4  *
5  *      FastCGi compatibility package Interface
6  *
7  *
8  *  Copyright (c) 1996 Open Market, Inc.
9  *
10  * See the file "LICENSE.TERMS" for information on usage and redistribution
11  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12  *
13  * $Id: FCGIInterface.java,v 1.4 2000/03/27 15:37:25 robs Exp $
14  */
15 package com.fastcgi;
16
17 import java.net.*;
18 import java.io.*;
19 import java.util.Properties;
20
21 /*
22  * This is the FastCGI interface that the application calls to communicate with the
23  * FastCGI web server. This version is single threaded, and handles one request at
24  * a time, which is why we can have a static variable for it.
25  */
26 public class FCGIInterface 
27 {
28     private static final String RCSID = "$Id: FCGIInterface.java,v 1.4 2000/03/27 15:37:25 robs Exp $";
29
30     /*
31     * Class variables
32     */
33     public static FCGIRequest request = null;
34     public static boolean acceptCalled = false;
35     public static boolean isFCGI = true;
36     public static Properties startupProps;
37     public static ServerSocket srvSocket;
38
39     /*
40     * Accepts a new request from the HTTP server and creates
41     * a conventional execution environment for the request.
42     * If the application was invoked as a FastCGI server,
43     * the first call to FCGIaccept indicates that the application
44     * has completed its initialization and is ready to accept
45     * a request.  Subsequent calls to FCGI_accept indicate that
46     * the application has completed its processing of the
47     * current request and is ready to accept a new request.
48     * If the application was invoked as a CGI program, the first
49     * call to FCGIaccept is essentially a no-op and the second
50     * call returns EOF (-1) as does an error. Application should exit.
51     *
52     * If the application was invoked as a FastCGI server,
53     * and this is not the first call to this procedure,
54     * FCGIaccept first flushes any buffered output to the HTTP server.
55     *
56     * On every call, FCGIaccept accepts the new request and
57     * reads the FCGI_PARAMS stream into System.props. It also creates
58     * streams that understand FastCGI protocol and take input from
59     * the HTTP server send output and error output to the HTTP server,
60     * and assigns these new streams to System.in, System.out and
61     * System.err respectively.
62     *
63     * For now, we will just return an int to the caller, which is why
64     * this method catches, but doen't throw Exceptions.
65     *
66     */
67     public int FCGIaccept() {
68         int acceptResult = 0;
69
70         /*
71          * If first call, mark it and if fcgi save original system properties,
72          * If not first call, and  we are cgi, we should be gone.
73          */
74         if (!acceptCalled){
75             isFCGI = System.getProperties().containsKey("FCGI_PORT");
76             acceptCalled = true;
77             if (isFCGI) {
78                 /*
79                  * save original system properties (nonrequest)
80                  * and get a server socket
81                  */
82                 startupProps = new Properties(System.getProperties());
83                 String str =
84                     new String(System.getProperty("FCGI_PORT"));
85                 if (str.length() <= 0) {
86                     return -1;
87                 }
88                 int portNum = Integer.parseInt(str);
89
90                 try {
91                     srvSocket = new ServerSocket(portNum);
92                 } catch (IOException e) {
93                     if (request != null)
94                     {
95                         request.socket = null;
96                     }
97                     srvSocket = null;
98                     request = null;
99                     return -1;
100                 }
101             }
102         }
103         else {
104             if (!isFCGI){
105                 return -1;
106             }
107         }
108         /*
109          * If we are cgi, just leave everything as is, otherwise set up env
110          */
111         if (isFCGI){
112             try {
113                 acceptResult = FCGIAccept();
114             } catch (IOException e) {
115                 return -1;
116             }
117             if (acceptResult < 0){
118                 return -1;
119             }
120
121             /*
122             * redirect stdin, stdout and stderr to fcgi socket
123             */
124             System.setIn(new BufferedInputStream(request.inStream, 8192));
125             System.setOut(new PrintStream(new BufferedOutputStream(
126                 request.outStream, 8192)));
127             System.setErr(new PrintStream(new BufferedOutputStream(
128                 request.errStream, 512)));
129             System.setProperties(request.params);
130         }
131         return 0;
132     }
133
134     /*
135      * Accepts a new request from the HTTP server.
136      * Finishes the request accepted by the previous call
137      * to FCGI_Accept. Sets up the FCGI environment and reads
138      * saved and per request environmental varaibles into
139      * the request object. (This is redundant on System.props
140      * as long as we can handle only one request object.)
141      */
142     int FCGIAccept() throws IOException{
143
144         boolean isNewConnection;
145         boolean errCloseEx = false;
146         boolean outCloseEx = false;
147
148         if (request != null) {
149             /*
150              * Complete the previous request
151              */
152             System.err.close();
153             System.out.close();
154             boolean prevRequestfailed = (errCloseEx || outCloseEx ||
155                 request.inStream.getFCGIError() != 0 ||
156                 request.inStream.getException() != null);
157             if (prevRequestfailed || !request.keepConnection ) {
158                 request.socket.close();
159                 request.socket = null;
160             }
161             if (prevRequestfailed) {
162                 request = null;
163                 return -1;
164             }
165         }
166         else    {
167             /*
168              * Get a Request and initialize some variables
169              */
170             request = new FCGIRequest();
171             request.socket = null;
172             request.inStream = null;
173         }
174         isNewConnection = false;
175
176         /*
177          * if connection isnt open accept a new connection (blocking)
178          */
179         for(;;) {
180             if (request.socket == null){
181                 try {
182                     request.socket = srvSocket.accept();
183                 } catch (IOException e) {
184                     request.socket = null;
185                     request = null;
186                     return -1;
187                 }
188                 isNewConnection = true;
189             }
190
191             /* Try reading from new connection. If the read fails and
192              * it was an old connection the web server probably closed it;
193              * try making a new connection before giving up
194              */
195             request.isBeginProcessed = false;
196             request.inStream =
197                 new FCGIInputStream((FileInputStream)request.
198                 socket.getInputStream(),
199                 8192, 0, request);
200             request.inStream.fill();
201             if (request.isBeginProcessed) {
202                 break;
203             }
204             request.socket.close();
205
206                 request.socket = null;
207             if (isNewConnection) {
208                 return -1;
209             }
210         }
211         /*
212         * Set up the objects for the new request
213         */
214         request.params = new Properties(startupProps);
215         switch(request.role) {
216         case FCGIGlobalDefs.def_FCGIResponder:
217             request.params.put("ROLE","RESPONDER");
218             break;
219         case FCGIGlobalDefs.def_FCGIAuthorizer:
220             request.params.put("ROLE", "AUTHORIZER");
221             break;
222         case FCGIGlobalDefs.def_FCGIFilter:
223             request.params.put("ROLE", "FILTER");
224             break;
225         default:
226             return -1;
227         }
228         request.inStream.setReaderType(FCGIGlobalDefs.def_FCGIParams);
229         /*
230          * read the rest of request parameters
231          */
232         if (new FCGIMessage(request.inStream).readParams(request.params) < 0) {
233             return -1;
234         }
235         request.inStream.setReaderType(FCGIGlobalDefs.def_FCGIStdin);
236         request.outStream
237             =  new FCGIOutputStream((FileOutputStream)request.socket.
238             getOutputStream(), 8192,
239             FCGIGlobalDefs.def_FCGIStdout,request);
240         request.errStream
241             = new FCGIOutputStream((FileOutputStream)request.socket.
242             getOutputStream(), 512,
243             FCGIGlobalDefs.def_FCGIStderr,request);
244         request.numWriters = 2;
245         return 0;
246     }
247 }