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