Put the classes in the com.fastcgi package
[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.2 2000/03/21 12:02:29 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     /*
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