001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.coyote.ajp;
019:
020: import org.apache.tomcat.util.buf.ByteChunk;
021: import org.apache.tomcat.util.buf.CharChunk;
022: import org.apache.tomcat.util.buf.MessageBytes;
023: import org.apache.tomcat.util.res.StringManager;
024:
025: /**
026: * A single packet for communication between the web server and the
027: * container. Designed to be reused many times with no creation of
028: * garbage. Understands the format of data types for these packets.
029: * Can be used (somewhat confusingly) for both incoming and outgoing
030: * packets.
031: *
032: * @author Henri Gomez
033: * @author Dan Milstein
034: * @author Keith Wannamaker
035: * @author Kevin Seguin
036: * @author Costin Manolache
037: */
038: public class AjpMessage {
039:
040: protected static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
041: .getLog(AjpMessage.class);
042:
043: /**
044: * The string manager for this package.
045: */
046: protected static StringManager sm = StringManager
047: .getManager(Constants.Package);
048:
049: // ------------------------------------------------------------ Constructor
050:
051: public AjpMessage(int packetSize) {
052: buf = new byte[packetSize];
053: }
054:
055: // ----------------------------------------------------- Instance Variables
056:
057: /**
058: * Fixed size buffer.
059: */
060: protected byte buf[] = null;
061:
062: /**
063: * The current read or write position in the buffer.
064: */
065: protected int pos;
066:
067: /**
068: * This actually means different things depending on whether the
069: * packet is read or write. For read, it's the length of the
070: * payload (excluding the header). For write, it's the length of
071: * the packet as a whole (counting the header). Oh, well.
072: */
073: protected int len;
074:
075: // --------------------------------------------------------- Public Methods
076:
077: /**
078: * Prepare this packet for accumulating a message from the container to
079: * the web server. Set the write position to just after the header
080: * (but leave the length unwritten, because it is as yet unknown).
081: */
082: public void reset() {
083: len = 4;
084: pos = 4;
085: }
086:
087: /**
088: * For a packet to be sent to the web server, finish the process of
089: * accumulating data and write the length of the data payload into
090: * the header.
091: */
092: public void end() {
093: len = pos;
094: int dLen = len - 4;
095:
096: buf[0] = (byte) 0x41;
097: buf[1] = (byte) 0x42;
098: buf[2] = (byte) ((dLen >>> 8) & 0xFF);
099: buf[3] = (byte) (dLen & 0xFF);
100: }
101:
102: /**
103: * Return the underlying byte buffer.
104: */
105: public byte[] getBuffer() {
106: return buf;
107: }
108:
109: /**
110: * Return the current message length. For read, it's the length of the
111: * payload (excluding the header). For write, it's the length of
112: * the packet as a whole (counting the header).
113: */
114: public int getLen() {
115: return len;
116: }
117:
118: /**
119: * Add a short integer (2 bytes) to the message.
120: */
121: public void appendInt(int val) {
122: buf[pos++] = (byte) ((val >>> 8) & 0xFF);
123: buf[pos++] = (byte) (val & 0xFF);
124: }
125:
126: /**
127: * Append a byte (1 byte) to the message.
128: */
129: public void appendByte(int val) {
130: buf[pos++] = (byte) val;
131: }
132:
133: /**
134: * Append an int (4 bytes) to the message.
135: */
136: public void appendLongInt(int val) {
137: buf[pos++] = (byte) ((val >>> 24) & 0xFF);
138: buf[pos++] = (byte) ((val >>> 16) & 0xFF);
139: buf[pos++] = (byte) ((val >>> 8) & 0xFF);
140: buf[pos++] = (byte) (val & 0xFF);
141: }
142:
143: /**
144: * Write a MessageBytes out at the current write position.
145: * A null MessageBytes is encoded as a string with length 0.
146: */
147: public void appendBytes(MessageBytes mb) {
148: if (mb == null) {
149: log.error(sm.getString("ajpmessage.null"),
150: new NullPointerException());
151: appendInt(0);
152: appendByte(0);
153: return;
154: }
155: if (mb.getType() == MessageBytes.T_BYTES) {
156: ByteChunk bc = mb.getByteChunk();
157: appendByteChunk(bc);
158: } else if (mb.getType() == MessageBytes.T_CHARS) {
159: CharChunk cc = mb.getCharChunk();
160: appendCharChunk(cc);
161: } else {
162: appendString(mb.toString());
163: }
164: }
165:
166: /**
167: * Write a ByteChunk out at the current write position.
168: * A null ByteChunk is encoded as a string with length 0.
169: */
170: public void appendByteChunk(ByteChunk bc) {
171: if (bc == null) {
172: log.error(sm.getString("ajpmessage.null"),
173: new NullPointerException());
174: appendInt(0);
175: appendByte(0);
176: return;
177: }
178: appendBytes(bc.getBytes(), bc.getStart(), bc.getLength());
179: }
180:
181: /**
182: * Write a CharChunk out at the current write position.
183: * A null CharChunk is encoded as a string with length 0.
184: */
185: public void appendCharChunk(CharChunk cc) {
186: if (cc == null) {
187: log.error(sm.getString("ajpmessage.null"),
188: new NullPointerException());
189: appendInt(0);
190: appendByte(0);
191: return;
192: }
193: int start = cc.getStart();
194: int end = cc.getEnd();
195: appendInt(end - start);
196: char[] cbuf = cc.getBuffer();
197: for (int i = start; i < end; i++) {
198: char c = cbuf[i];
199: // Note: This is clearly incorrect for many strings,
200: // but is the only consistent approach within the current
201: // servlet framework. It must suffice until servlet output
202: // streams properly encode their output.
203: if ((c <= 31) && (c != 9)) {
204: c = ' ';
205: } else if (c == 127) {
206: c = ' ';
207: }
208: appendByte(c);
209: }
210: appendByte(0);
211: }
212:
213: /**
214: * Write a String out at the current write position. Strings are
215: * encoded with the length in two bytes first, then the string, and
216: * then a terminating \0 (which is <B>not</B> included in the
217: * encoded length). The terminator is for the convenience of the C
218: * code, where it saves a round of copying. A null string is
219: * encoded as a string with length 0.
220: */
221: public void appendString(String str) {
222: if (str == null) {
223: log.error(sm.getString("ajpmessage.null"),
224: new NullPointerException());
225: appendInt(0);
226: appendByte(0);
227: return;
228: }
229: int len = str.length();
230: appendInt(len);
231: for (int i = 0; i < len; i++) {
232: char c = str.charAt(i);
233: // Note: This is clearly incorrect for many strings,
234: // but is the only consistent approach within the current
235: // servlet framework. It must suffice until servlet output
236: // streams properly encode their output.
237: if ((c <= 31) && (c != 9)) {
238: c = ' ';
239: } else if (c == 127) {
240: c = ' ';
241: }
242: appendByte(c);
243: }
244: appendByte(0);
245: }
246:
247: /**
248: * Copy a chunk of bytes into the packet, starting at the current
249: * write position. The chunk of bytes is encoded with the length
250: * in two bytes first, then the data itself, and finally a
251: * terminating \0 (which is <B>not</B> included in the encoded
252: * length).
253: *
254: * @param b The array from which to copy bytes.
255: * @param off The offset into the array at which to start copying
256: * @param numBytes The number of bytes to copy.
257: */
258: public void appendBytes(byte[] b, int off, int numBytes) {
259: if (pos + numBytes + 3 > buf.length) {
260: log.error(sm.getString("ajpmessage.overflow",
261: "" + numBytes, "" + pos),
262: new ArrayIndexOutOfBoundsException());
263: if (log.isDebugEnabled()) {
264: dump("Overflow/coBytes");
265: }
266: return;
267: }
268: appendInt(numBytes);
269: System.arraycopy(b, off, buf, pos, numBytes);
270: pos += numBytes;
271: appendByte(0);
272: }
273:
274: /**
275: * Read an integer from packet, and advance the read position past
276: * it. Integers are encoded as two unsigned bytes with the
277: * high-order byte first, and, as far as I can tell, in
278: * little-endian order within each byte.
279: */
280: public int getInt() {
281: int b1 = buf[pos++] & 0xFF;
282: int b2 = buf[pos++] & 0xFF;
283: return (b1 << 8) + b2;
284: }
285:
286: public int peekInt() {
287: int b1 = buf[pos] & 0xFF;
288: int b2 = buf[pos + 1] & 0xFF;
289: return (b1 << 8) + b2;
290: }
291:
292: public byte getByte() {
293: byte res = buf[pos++];
294: return res;
295: }
296:
297: public byte peekByte() {
298: byte res = buf[pos];
299: return res;
300: }
301:
302: public void getBytes(MessageBytes mb) {
303: int length = getInt();
304: if ((length == 0xFFFF) || (length == -1)) {
305: mb.recycle();
306: return;
307: }
308: mb.setBytes(buf, pos, length);
309: pos += length;
310: pos++; // Skip the terminating \0
311: }
312:
313: /**
314: * Copy a chunk of bytes from the packet into an array and advance
315: * the read position past the chunk. See appendBytes() for details
316: * on the encoding.
317: *
318: * @return The number of bytes copied.
319: */
320: public int getBytes(byte[] dest) {
321: int length = getInt();
322: if (pos + length > buf.length) {
323: log.error(sm.getString("ajpmessage.read", "" + length));
324: return 0;
325: }
326:
327: if ((length == 0xFFFF) || (length == -1)) {
328: return 0;
329: }
330:
331: System.arraycopy(buf, pos, dest, 0, length);
332: pos += length;
333: pos++; // Skip terminating \0
334: return length;
335: }
336:
337: /**
338: * Read a 32 bits integer from packet, and advance the read position past
339: * it. Integers are encoded as four unsigned bytes with the
340: * high-order byte first, and, as far as I can tell, in
341: * little-endian order within each byte.
342: */
343: public int getLongInt() {
344: int b1 = buf[pos++] & 0xFF; // No swap, Java order
345: b1 <<= 8;
346: b1 |= (buf[pos++] & 0xFF);
347: b1 <<= 8;
348: b1 |= (buf[pos++] & 0xFF);
349: b1 <<= 8;
350: b1 |= (buf[pos++] & 0xFF);
351: return b1;
352: }
353:
354: public int getHeaderLength() {
355: return 4;
356: }
357:
358: public int getPacketSize() {
359: return buf.length;
360: }
361:
362: public int processHeader() {
363: pos = 0;
364: int mark = getInt();
365: len = getInt();
366: // Verify message signature
367: if ((mark != 0x1234) && (mark != 0x4142)) {
368: log.error(sm.getString("ajpmessage.invalid", "" + mark));
369: if (log.isDebugEnabled()) {
370: dump("In: ");
371: }
372: return -1;
373: }
374: if (log.isDebugEnabled()) {
375: log.debug("Received " + len + " " + buf[0]);
376: }
377: return len;
378: }
379:
380: /**
381: * Dump the contents of the message, prefixed with the given String.
382: */
383: public void dump(String msg) {
384: if (log.isDebugEnabled()) {
385: log.debug(msg + ": " + buf + " " + pos + "/" + (len + 4));
386: }
387: int max = pos;
388: if (len + 4 > pos)
389: max = len + 4;
390: if (max > 1000)
391: max = 1000;
392: if (log.isDebugEnabled()) {
393: for (int j = 0; j < max; j += 16) {
394: log.debug(hexLine(buf, j, len));
395: }
396: }
397: }
398:
399: // ------------------------------------------------------ Protected Methods
400:
401: protected static String hexLine(byte buf[], int start, int len) {
402: StringBuffer sb = new StringBuffer();
403: for (int i = start; i < start + 16; i++) {
404: if (i < len + 4) {
405: sb.append(hex(buf[i]) + " ");
406: } else {
407: sb.append(" ");
408: }
409: }
410: sb.append(" | ");
411: for (int i = start; i < start + 16 && i < len + 4; i++) {
412: if (!Character.isISOControl((char) buf[i])) {
413: sb.append(new Character((char) buf[i]));
414: } else {
415: sb.append(".");
416: }
417: }
418: return sb.toString();
419: }
420:
421: protected static String hex(int x) {
422: String h = Integer.toHexString(x);
423: if (h.length() == 1) {
424: h = "0" + h;
425: }
426: return h.substring(h.length() - 2);
427: }
428:
429: }
|