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