Put the RCS keyword '$Id: $' in each of the classes.
[catagits/fcgi2.git] / java / FCGIMessage.java
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  *
13  * $Id: FCGIMessage.java,v 1.3 2000/03/21 12:12:26 robs Exp $
14  */
15 package com.fastcgi;
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     private static final String RCSID = "$Id: FCGIMessage.java,v 1.3 2000/03/21 12:12:26 robs Exp $";
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         }
283         name.getBytes(0, nameLen, dest, pos);
284         pos += nameLen;
285         value.getBytes(0, valLen, dest, pos);
286         pos += valLen;
287     }
288
289     /*
290      * Read FCGI name-value pairs from a stream until EOF. Put them
291      * into a Properties object, storing both as strings.
292      */
293     public int readParams(Properties props) throws IOException{
294         int nameLen, valueLen;
295         byte lenBuff[] = new byte[3];
296         int i = 1;
297
298         while ((nameLen = in.read()) != -1) {
299             i++;
300             if ((nameLen & 0x80) != 0) {
301                 if ((in.read( lenBuff, 0, 3)) != 3) {
302                     in.setFCGIError(
303                         FCGIGlobalDefs.def_FCGIParamsError);
304                     return -1;
305                 }
306                 nameLen = ((nameLen & 0x7f) << 24)
307                     | ((lenBuff[0] & 0xFF) << 16)
308                     | ((lenBuff[1] & 0xFF) << 8)
309                     | (lenBuff[2] & 0xFF);
310             }
311
312             if ((valueLen = in.read()) == -1) {
313                 in.setFCGIError(
314                     FCGIGlobalDefs.def_FCGIParamsError);
315                 return -1;
316             }
317             if ((valueLen & 0x80) != 0) {
318                 if ((in.read( lenBuff, 0, 3)) != 3) {
319                     in.setFCGIError(
320                         FCGIGlobalDefs.def_FCGIParamsError);
321                     return -1;
322                 }
323                 valueLen = ((valueLen & 0x7f) << 24)
324                     | ((lenBuff[0] & 0xFF) << 16)
325                     | ((lenBuff[1] & 0xFF) << 8)
326                     | (lenBuff[2] & 0xFF);
327             }
328
329             /*
330              * nameLen and valueLen are now valid; read the name
331              * and the value from the stream and construct a standard
332              * environmental entity
333              */
334             byte[] name  = new byte[nameLen];
335             byte[] value = new byte[valueLen];
336             if (in.read(name ,0,  nameLen) != nameLen) {
337                 in.setFCGIError(
338                     FCGIGlobalDefs.def_FCGIParamsError);
339                 return -1;
340             }
341
342             if(in.read(value, 0, valueLen) != valueLen) {
343                 in.setFCGIError(
344                     FCGIGlobalDefs.def_FCGIParamsError);
345                 return -1;
346             }
347             String strName  = new String(name, 0, 0, name.length);
348             String strValue = new String(value, 0, 0, value.length);
349             props.put(strName, strValue);
350         }
351         return 0;
352
353
354     }
355     /*
356      * Message Building Methods
357      */
358
359     /*
360      * Build an FCGI Message Header -
361      */
362     public byte[] makeHeader(int type,
363         int requestId,
364         int contentLength,
365         int paddingLength) {
366         byte[] header = new byte[FCGIGlobalDefs.def_FCGIHeaderLen];
367         header[0]   = (byte)FCGIGlobalDefs.def_FCGIVersion1;
368         header[1]   = (byte)type;
369         header[2]   = (byte)((requestId      >> 8) & 0xff);
370         header[3]   = (byte)((requestId          ) & 0xff);
371         header[4]   = (byte)((contentLength  >> 8) & 0xff);
372         header[5]   = (byte)((contentLength      ) & 0xff);
373         header[6]   = (byte)paddingLength;
374         header[7]   =  0;  //reserved byte
375         return header;
376     }
377     /*
378      * Build an FCGI Message End Request Body
379      */
380     public byte[] makeEndrequestBody(int appStatus,int protocolStatus){
381         byte body[] = new byte[FCGIGlobalDefs.def_FCGIEndReqBodyLen];
382         body[0] = (byte)((appStatus >> 24) & 0xff);
383         body[1] = (byte)((appStatus >> 16) & 0xff);
384         body[2] = (byte)((appStatus >>  8) & 0xff);
385         body[3] = (byte)((appStatus      ) & 0xff);
386         body[4] = (byte)protocolStatus;
387         for (int i = 5; i < 8; i++) {
388             body[i] = 0;
389         }
390         return body;
391     }
392     /*
393      * Build an FCGI Message UnknownTypeBodyBody
394      */
395     public byte[] makeUnknownTypeBodyBody(int type){
396         byte body[] =
397             new byte[FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen];
398         body[0] = (byte)type;
399         for (int i = 1;
400             i < FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen; i++) {
401             body[i] = 0;
402         }
403         return body;
404     }
405
406 } //end class