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