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