Put the classes in the com.fastcgi package
[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 *
07c41236 13 * $Id: FCGIMessage.java,v 1.2 2000/03/21 12:02:29 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
27public 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