5 * FastCGi compatibility package Interface
8 * Copyright (c) 1996 Open Market, Inc.
10 * See the file "LICENSE.TERMS" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 * $Id: FCGIMessage.java,v 1.1 1999/01/31 02:45:52 roberts Exp $
17 import java.util.Properties;
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
26 public class FCGIMessage {
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.
40 private int h_version;
42 private int h_requestID; // 2 bytes
43 private int h_contentLength; // 2 bytes
44 private int h_paddingLength;
46 * FCGI BeginRequest body.
48 private int br_role; // 2 bytes
51 private FCGIInputStream in;
54 * constructor - Java would do this implicitly.
60 * constructor - get the stream.
62 public FCGIMessage(FCGIInputStream instream){
67 * Message Reading Methods
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.
76 public int processHeader(byte[] hdr) throws IOException{
77 processHeaderBytes(hdr);
78 if (h_version != FCGIGlobalDefs.def_FCGIVersion1) {
79 return(FCGIGlobalDefs.def_FCGIUnsupportedVersion);
81 in.contentLen = h_contentLength;
82 in.paddingLen = h_paddingLength;
83 if (h_type == FCGIGlobalDefs.def_FCGIBeginRequest) {
84 return processBeginRecord(h_requestID);
86 if (h_requestID == FCGIGlobalDefs.def_FCGINullRequestID) {
87 return processManagementRecord(h_type);
89 if (h_requestID != in.request.requestID) {
90 return(FCGIGlobalDefs.def_FCGISkip);
92 if (h_type != in.type) {
93 return(FCGIGlobalDefs.def_FCGIProtocolError);
95 return(FCGIGlobalDefs.def_FCGIStreamRecord);
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.
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;
112 * Reads FCGI Begin Request Record.
114 public int processBeginRecord(int requestID) throws IOException {
117 if (requestID == 0 || in.contentLen
118 != FCGIGlobalDefs.def_FCGIEndReqBodyLen) {
119 return FCGIGlobalDefs.def_FCGIProtocolError;
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
126 if (in.request.isBeginProcessed) {
127 endReqMsg = new byte[FCGIGlobalDefs.def_FCGIHeaderLen
128 + FCGIGlobalDefs.def_FCGIEndReqBodyLen];
129 System.arraycopy(makeHeader(
130 FCGIGlobalDefs.def_FCGIEndRequest,
132 FCGIGlobalDefs.def_FCGIEndReqBodyLen,
134 FCGIGlobalDefs.def_FCGIHeaderLen);
135 System.arraycopy(makeEndrequestBody(0,
136 FCGIGlobalDefs.def_FCGICantMpxConn), 0,
138 FCGIGlobalDefs.def_FCGIHeaderLen,
139 FCGIGlobalDefs.def_FCGIEndReqBodyLen);
141 * since isBeginProcessed is first set below,this
142 * can't be out first call, so request.out is properly set
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);
154 * Accept this new request. Read the record body
156 in.request.requestID = requestID;
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;
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;
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.
181 public int processManagementRecord(int type) throws IOException {
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);
190 if (in.getFCGIError() != 0 || in.contentLen != 0) {
191 return FCGIGlobalDefs.def_FCGIProtocolError;
193 if (tmpProps.containsKey(
194 FCGIGlobalDefs.def_FCGIMaxConns)) {
196 FCGIGlobalDefs.def_FCGIMaxConns, "1",
200 if (tmpProps.containsKey(
201 FCGIGlobalDefs.def_FCGIMaxReqs)) {
203 FCGIGlobalDefs.def_FCGIMaxReqs, "1",
207 if (tmpProps.containsKey(
208 FCGIGlobalDefs.def_FCGIMaxConns)) {
210 FCGIGlobalDefs.def_FCGIMpxsConns, "0",
216 len = wrndx - FCGIGlobalDefs.def_FCGIHeaderLen;
217 System.arraycopy(makeHeader(
218 FCGIGlobalDefs.def_FCGIGetValuesResult,
219 FCGIGlobalDefs.def_FCGINullRequestID,
222 FCGIGlobalDefs.def_FCGIHeaderLen);
226 FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen;
227 System.arraycopy(makeHeader(
228 FCGIGlobalDefs.def_FCGIUnknownType,
229 FCGIGlobalDefs.def_FCGINullRequestID,
232 FCGIGlobalDefs.def_FCGIHeaderLen);
233 System.arraycopy(makeUnknownTypeBodyBody(h_type), 0,
235 FCGIGlobalDefs.def_FCGIHeaderLen,
236 FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen);
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.
246 in.request.socket.getOutputStream().write(response, 0,
247 FCGIGlobalDefs.def_FCGIHeaderLen +
248 FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen);
250 } catch (IOException e){
253 return FCGIGlobalDefs.def_FCGIMgmtRecord;
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
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;
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;
271 int valLen = value.length();
273 dest[pos++] = (byte)valLen;
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;
280 name.getBytes(0, nameLen, dest, pos);
282 value.getBytes(0, valLen, dest, pos);
287 * Read FCGI name-value pairs from a stream until EOF. Put them
288 * into a Properties object, storing both as strings.
290 public int readParams(Properties props) throws IOException{
291 int nameLen, valueLen;
292 byte lenBuff[] = new byte[3];
295 while ((nameLen = in.read()) != -1) {
297 if ((nameLen & 0x80) != 0) {
298 if ((in.read( lenBuff, 0, 3)) != 3) {
300 FCGIGlobalDefs.def_FCGIParamsError);
303 nameLen = ((nameLen & 0x7f) << 24)
304 | ((lenBuff[0] & 0xFF) << 16)
305 | ((lenBuff[1] & 0xFF) << 8)
306 | (lenBuff[2] & 0xFF);
309 if ((valueLen = in.read()) == -1) {
311 FCGIGlobalDefs.def_FCGIParamsError);
314 if ((valueLen & 0x80) != 0) {
315 if ((in.read( lenBuff, 0, 3)) != 3) {
317 FCGIGlobalDefs.def_FCGIParamsError);
320 valueLen = ((valueLen & 0x7f) << 24)
321 | ((lenBuff[0] & 0xFF) << 16)
322 | ((lenBuff[1] & 0xFF) << 8)
323 | (lenBuff[2] & 0xFF);
327 * nameLen and valueLen are now valid; read the name
328 * and the value from the stream and construct a standard
329 * environmental entity
331 byte[] name = new byte[nameLen];
332 byte[] value = new byte[valueLen];
333 if (in.read(name ,0, nameLen) != nameLen) {
335 FCGIGlobalDefs.def_FCGIParamsError);
339 if(in.read(value, 0, valueLen) != valueLen) {
341 FCGIGlobalDefs.def_FCGIParamsError);
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);
353 * Message Building Methods
357 * Build an FCGI Message Header -
359 public byte[] makeHeader(int type,
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
375 * Build an FCGI Message End Request Body
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++) {
390 * Build an FCGI Message UnknownTypeBodyBody
392 public byte[] makeUnknownTypeBodyBody(int type){
394 new byte[FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen];
395 body[0] = (byte)type;
397 i < FCGIGlobalDefs.def_FCGIUnknownBodyTypeBodyLen; i++) {