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