Commit | Line | Data |
61962ef7 |
1 | /* |
2 | * @(#)FCGIMessage.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: FCGIMessage.java,v 1.2 2000/03/21 12:02:29 robs Exp $ |
61962ef7 |
14 | */ |
07c41236 |
15 | package com.fastcgi; |
61962ef7 |
16 | |
17 | import java.io.*; |
18 | import java.util.Properties; |
19 | |
20 | /* This class handles reading and building the fastcgi messages. |
21 | * For reading incoming mesages, we pass the input |
22 | * stream as a param to the constructor rather than to each method. |
23 | * Methods that build messages use and return internal buffers, so they |
24 | * dont need a stream. |
25 | */ |
26 | |
27 | public class FCGIMessage { |
28 | |
29 | /* |
30 | * Instance variables |
31 | */ |
32 | /* |
33 | * FCGI Message Records |
34 | * The logical structures of the FCGI Message Records. |
35 | * Fields are originally 1 unsigned byte in message |
36 | * unless otherwise noted. |
37 | */ |
38 | /* |
39 | * FCGI Header |
40 | */ |
41 | private int h_version; |
42 | private int h_type; |
43 | private int h_requestID; // 2 bytes |
44 | private int h_contentLength; // 2 bytes |
45 | private int h_paddingLength; |
46 | /* |
47 | * FCGI BeginRequest body. |
48 | */ |
49 | private int br_role; // 2 bytes |
50 | private int br_flags; |
51 | |
52 | private FCGIInputStream in; |
53 | |
54 | /* |
55 | * constructor - Java would do this implicitly. |
56 | */ |
57 | public FCGIMessage(){ |
58 | super(); |
59 | } |
60 | /* |
61 | * constructor - get the stream. |
62 | */ |
63 | public FCGIMessage(FCGIInputStream instream){ |
64 | in = instream; |
65 | } |
66 | |
67 | /* |
68 | * Message Reading Methods |
69 | */ |
70 | |
71 | /* |
72 | * Interpret the FCGI Message Header. Processes FCGI |
73 | * BeginRequest and Management messages. Param hdr is the header. |
74 | * The calling routine has to keep track of the stream reading |
75 | * management or use FCGIInputStream.fill() which does just that. |
76 | */ |
77 | public int processHeader(byte[] hdr) throws IOException{ |
78 | processHeaderBytes(hdr); |
79 | if (h_version != FCGIGlobalDefs.def_FCGIVersion1) { |
80 | return(FCGIGlobalDefs.def_FCGIUnsupportedVersion); |
81 | } |
82 | in.contentLen = h_contentLength; |
83 | in.paddingLen = h_paddingLength; |
84 | if (h_type == FCGIGlobalDefs.def_FCGIBeginRequest) { |
85 | return processBeginRecord(h_requestID); |
86 | } |
87 | if (h_requestID == FCGIGlobalDefs.def_FCGINullRequestID) { |
88 | return processManagementRecord(h_type); |
89 | } |
90 | if (h_requestID != in.request.requestID) { |
91 | return(FCGIGlobalDefs.def_FCGISkip); |
92 | } |
93 | if (h_type != in.type) { |
94 | return(FCGIGlobalDefs.def_FCGIProtocolError); |
95 | } |
96 | return(FCGIGlobalDefs.def_FCGIStreamRecord); |
97 | } |
98 | |
99 | /* Put the unsigned bytes in the incoming FCGI header into |
100 | * integer form for Java, concatinating bytes when needed. |
101 | * Because Java has no unsigned byte type, we have to be careful |
102 | * about signed numeric promotion to int. |
103 | */ |
104 | private void processHeaderBytes(byte[] hdrBuf){ |
105 | h_version = hdrBuf[0] & 0xFF; |
106 | h_type = hdrBuf[1] & 0xFF; |
107 | h_requestID = ((hdrBuf[2] & 0xFF) << 8) | (hdrBuf[3] & 0xFF); |
108 | h_contentLength = ((hdrBuf[4] & 0xFF) << 8) | (hdrBuf[5] & 0xFF); |
109 | h_paddingLength = hdrBuf[6] & 0xFF; |
110 | } |
111 | |
112 | /* |
113 | * Reads FCGI Begin Request Record. |
114 | */ |
115 | public int processBeginRecord(int requestID) throws IOException { |
116 | byte beginReqBody[]; |
117 | byte endReqMsg[]; |
118 | if (requestID == 0 || in.contentLen |
119 | != FCGIGlobalDefs.def_FCGIEndReqBodyLen) { |
120 | return FCGIGlobalDefs.def_FCGIProtocolError; |
121 | } |
122 | /* |
123 | * If the webserver is multiplexing the connection, |
124 | * this library can't deal with it, so repond with |
125 | * FCGIEndReq message with protocolStatus FCGICantMpxConn |
126 | */ |
127 | if (in.request.isBeginProcessed) { |
128 | endReqMsg = new byte[FCGIGlobalDefs.def_FCGIHeaderLen |
129 | + FCGIGlobalDefs.def_FCGIEndReqBodyLen]; |
130 | System.arraycopy(makeHeader( |
131 | FCGIGlobalDefs.def_FCGIEndRequest, |
132 | requestID, |
133 | FCGIGlobalDefs.def_FCGIEndReqBodyLen, |
134 | 0), 0, endReqMsg, 0, |
135 | FCGIGlobalDefs.def_FCGIHeaderLen); |
136 | System.arraycopy(makeEndrequestBody(0, |
137 | FCGIGlobalDefs.def_FCGICantMpxConn), 0, |
138 | endReqMsg, |
139 | FCGIGlobalDefs.def_FCGIHeaderLen, |
140 | FCGIGlobalDefs.def_FCGIEndReqBodyLen); |
141 | /* |
142 | * since isBeginProcessed is first set below,this |
143 | * can't be out first call, so request.out is properly set |
144 | */ |
145 | try { |
146 | in.request.outStream.write(endReqMsg, 0, |
147 | FCGIGlobalDefs.def_FCGIHeaderLen |
148 | + FCGIGlobalDefs.def_FCGIEndReqBodyLen); |
149 | } catch (IOException e){ |
150 | in.request.outStream.setException(e); |
151 | return -1; |
152 | } |
153 | } |
154 | /* |
155 | * Accept this new request. Read the record body |
156 | */ |
157 | in.request.requestID = requestID; |
158 | beginReqBody = |
159 | new byte[FCGIGlobalDefs.def_FCGIBeginReqBodyLen]; |
160 | if (in.read(beginReqBody, 0, |
161 | FCGIGlobalDefs.def_FCGIBeginReqBodyLen) != |
162 | FCGIGlobalDefs.def_FCGIBeginReqBodyLen) { |
163 | return FCGIGlobalDefs.def_FCGIProtocolError; |
164 | } |
165 | br_flags = beginReqBody[2] & 0xFF; |
166 | in.request.keepConnection |
167 | = (br_flags & FCGIGlobalDefs.def_FCGIKeepConn) != 0; |
168 | br_role = ((beginReqBody[0] & 0xFF) << 8) | (beginReqBody[1] & 0xFF); |
169 | in.request.role = br_role; |
170 | in.request.isBeginProcessed = true; |
171 | return FCGIGlobalDefs.def_FCGIBeginRecord; |
172 | } |
173 | |
174 | /* |
175 | * Reads and Responds to a Management Message. The only type of |
176 | * management message this library understands is FCGIGetValues. |
177 | * The only variables that this library's FCGIGetValues understands |
178 | * are def_FCGIMaxConns, def_FCGIMaxReqs, and def_FCGIMpxsConns. |
179 | * Ignore the other management variables, and repsond to other |
180 | * management messages with FCGIUnknownType. |
181 | */ |
182 | public int processManagementRecord(int type) throws IOException { |
183 | |
184 | byte[] response = new byte[64]; |
185 | int wrndx = response[FCGIGlobalDefs.def_FCGIHeaderLen]; |
186 | int value, len, plen; |
187 | if (type == FCGIGlobalDefs.def_FCGIGetValues) { |
188 | Properties tmpProps = new Properties(); |
189 | readParams(tmpProps); |
190 | |
191 | if (in.getFCGIError() != 0 || in.contentLen != 0) { |
192 | return FCGIGlobalDefs.def_FCGIProtocolError; |
193 | } |
194 | if (tmpProps.containsKey( |
195 | FCGIGlobalDefs.def_FCGIMaxConns)) { |
196 | makeNameVal( |
197 | FCGIGlobalDefs.def_FCGIMaxConns, "1", |
198 | response, wrndx); |
199 | } |
200 | else { |
201 | if (tmpProps.containsKey( |
202 | FCGIGlobalDefs.def_FCGIMaxReqs)) { |
203 | makeNameVal( |
204 | FCGIGlobalDefs.def_FCGIMaxReqs, "1", |
205 | response, wrndx); |
206 | } |
207 | else { |
208 | if (tmpProps.containsKey( |
209 | FCGIGlobalDefs.def_FCGIMaxConns)) { |
210 | makeNameVal( |
211 | FCGIGlobalDefs.def_FCGIMpxsConns, "0", |
212 | response, wrndx); |
213 | } |
214 | } |
215 | } |
216 | plen = 64 - wrndx; |
217 | len = wrndx - FCGIGlobalDefs.def_FCGIHeaderLen; |
218 | System.arraycopy(makeHeader( |
219 | FCGIGlobalDefs.def_FCGIGetValuesResult, |
220 | FCGIGlobalDefs.def_FCGINullRequestID, |
221 | len, plen), 0, |
222 | response, 0, |
223 | FCGIGlobalDefs.def_FCGIHeaderLen); |
224 | } |
225 | else { |
226 | plen = len = |
227 | FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen; |
228 | System.arraycopy(makeHeader( |
229 | FCGIGlobalDefs.def_FCGIUnknownType, |
230 | FCGIGlobalDefs.def_FCGINullRequestID, |
231 | len, 0), 0, |
232 | response, 0, |
233 | FCGIGlobalDefs.def_FCGIHeaderLen); |
234 | System.arraycopy(makeUnknownTypeBodyBody(h_type), 0, |
235 | response, |
236 | FCGIGlobalDefs.def_FCGIHeaderLen, |
237 | FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen); |
238 | } |
239 | /* |
240 | * No guarantee that we have a request yet, so |
241 | * dont use fcgi output stream to reference socket, instead |
242 | * use the FileInputStream that refrences it. Also |
243 | * nowhere to save exception, since this is not FCGI stream. |
244 | */ |
245 | |
246 | try { |
247 | in.request.socket.getOutputStream().write(response, 0, |
248 | FCGIGlobalDefs.def_FCGIHeaderLen + |
249 | FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen); |
250 | |
251 | } catch (IOException e){ |
252 | return -1; |
253 | } |
254 | return FCGIGlobalDefs.def_FCGIMgmtRecord; |
255 | } |
256 | |
257 | /* |
258 | * Makes a name/value with name = string of some length, and |
259 | * value a 1 byte integer. Pretty specific to what we are doing |
260 | * above. |
261 | */ |
262 | void makeNameVal(String name, String value, byte[] dest, int pos) { |
263 | int nameLen = name.length(); |
264 | if (nameLen < 0x80) { |
265 | dest[pos++] = (byte)nameLen; |
266 | }else { |
267 | dest[pos++] = (byte)(((nameLen >> 24) | 0x80) & 0xff); |
268 | dest[pos++] = (byte)((nameLen >> 16) & 0xff); |
269 | dest[pos++] = (byte)((nameLen >> 8) & 0xff); |
270 | dest[pos++] = (byte)nameLen; |
271 | } |
272 | int valLen = value.length(); |
273 | if (valLen < 0x80) { |
274 | dest[pos++] = (byte)valLen; |
275 | }else { |
276 | dest[pos++] = (byte)(((valLen >> 24) | 0x80) & 0xff); |
277 | dest[pos++] = (byte)((valLen >> 16) & 0xff); |
278 | dest[pos++] = (byte)((valLen >> 8) & 0xff); |
279 | dest[pos++] = (byte)valLen; |
280 | } |
281 | name.getBytes(0, nameLen, dest, pos); |
282 | pos += nameLen; |
283 | value.getBytes(0, valLen, dest, pos); |
284 | pos += valLen; |
285 | } |
286 | |
287 | /* |
288 | * Read FCGI name-value pairs from a stream until EOF. Put them |
289 | * into a Properties object, storing both as strings. |
290 | */ |
291 | public int readParams(Properties props) throws IOException{ |
292 | int nameLen, valueLen; |
293 | byte lenBuff[] = new byte[3]; |
294 | int i = 1; |
295 | |
296 | while ((nameLen = in.read()) != -1) { |
297 | i++; |
298 | if ((nameLen & 0x80) != 0) { |
299 | if ((in.read( lenBuff, 0, 3)) != 3) { |
300 | in.setFCGIError( |
301 | FCGIGlobalDefs.def_FCGIParamsError); |
302 | return -1; |
303 | } |
304 | nameLen = ((nameLen & 0x7f) << 24) |
305 | | ((lenBuff[0] & 0xFF) << 16) |
306 | | ((lenBuff[1] & 0xFF) << 8) |
307 | | (lenBuff[2] & 0xFF); |
308 | } |
309 | |
310 | if ((valueLen = in.read()) == -1) { |
311 | in.setFCGIError( |
312 | FCGIGlobalDefs.def_FCGIParamsError); |
313 | return -1; |
314 | } |
315 | if ((valueLen & 0x80) != 0) { |
316 | if ((in.read( lenBuff, 0, 3)) != 3) { |
317 | in.setFCGIError( |
318 | FCGIGlobalDefs.def_FCGIParamsError); |
319 | return -1; |
320 | } |
321 | valueLen = ((valueLen & 0x7f) << 24) |
322 | | ((lenBuff[0] & 0xFF) << 16) |
323 | | ((lenBuff[1] & 0xFF) << 8) |
324 | | (lenBuff[2] & 0xFF); |
325 | } |
326 | |
327 | /* |
328 | * nameLen and valueLen are now valid; read the name |
329 | * and the value from the stream and construct a standard |
330 | * environmental entity |
331 | */ |
332 | byte[] name = new byte[nameLen]; |
333 | byte[] value = new byte[valueLen]; |
334 | if (in.read(name ,0, nameLen) != nameLen) { |
335 | in.setFCGIError( |
336 | FCGIGlobalDefs.def_FCGIParamsError); |
337 | return -1; |
338 | } |
339 | |
340 | if(in.read(value, 0, valueLen) != valueLen) { |
341 | in.setFCGIError( |
342 | FCGIGlobalDefs.def_FCGIParamsError); |
343 | return -1; |
344 | } |
345 | String strName = new String(name, 0, 0, name.length); |
346 | String strValue = new String(value, 0, 0, value.length); |
347 | props.put(strName, strValue); |
348 | } |
349 | return 0; |
350 | |
351 | |
352 | } |
353 | /* |
354 | * Message Building Methods |
355 | */ |
356 | |
357 | /* |
358 | * Build an FCGI Message Header - |
359 | */ |
360 | public byte[] makeHeader(int type, |
361 | int requestId, |
362 | int contentLength, |
363 | int paddingLength) { |
364 | byte[] header = new byte[FCGIGlobalDefs.def_FCGIHeaderLen]; |
365 | header[0] = (byte)FCGIGlobalDefs.def_FCGIVersion1; |
366 | header[1] = (byte)type; |
367 | header[2] = (byte)((requestId >> 8) & 0xff); |
368 | header[3] = (byte)((requestId ) & 0xff); |
369 | header[4] = (byte)((contentLength >> 8) & 0xff); |
370 | header[5] = (byte)((contentLength ) & 0xff); |
371 | header[6] = (byte)paddingLength; |
372 | header[7] = 0; //reserved byte |
373 | return header; |
374 | } |
375 | /* |
376 | * Build an FCGI Message End Request Body |
377 | */ |
378 | public byte[] makeEndrequestBody(int appStatus,int protocolStatus){ |
379 | byte body[] = new byte[FCGIGlobalDefs.def_FCGIEndReqBodyLen]; |
380 | body[0] = (byte)((appStatus >> 24) & 0xff); |
381 | body[1] = (byte)((appStatus >> 16) & 0xff); |
382 | body[2] = (byte)((appStatus >> 8) & 0xff); |
383 | body[3] = (byte)((appStatus ) & 0xff); |
384 | body[4] = (byte)protocolStatus; |
385 | for (int i = 5; i < 8; i++) { |
386 | body[i] = 0; |
387 | } |
388 | return body; |
389 | } |
390 | /* |
391 | * Build an FCGI Message UnknownTypeBodyBody |
392 | */ |
393 | public byte[] makeUnknownTypeBodyBody(int type){ |
394 | byte body[] = |
395 | new byte[FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen]; |
396 | body[0] = (byte)type; |
397 | for (int i = 1; |
398 | i < FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen; i++) { |
399 | body[i] = 0; |
400 | } |
401 | return body; |
402 | } |
403 | |
404 | } //end class |