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 | * |
07c41236 |
13 | * $Id: FCGIInterface.java,v 1.2 2000/03/21 12:02:29 robs Exp $ |
61962ef7 |
14 | */ |
07c41236 |
15 | package com.fastcgi; |
61962ef7 |
16 | |
17 | import java.net.*; |
18 | import java.io.*; |
19 | import java.util.Properties; |
61962ef7 |
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 | } |