001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.ajp;
018:
019: import java.io.UnsupportedEncodingException;
020:
021: import org.apache.tomcat.util.buf.MessageBytes;
022: import org.apache.tomcat.util.http.MimeHeaders;
023:
024: /**
025: * A single packet for communication between the web server and the
026: * container. Designed to be reused many times with no creation of
027: * garbage. Understands the format of data types for these packets.
028: * Can be used (somewhat confusingly) for both incoming and outgoing
029: * packets.
030: *
031: * @see Ajp14/Ajp13Packet
032: *
033: * @author Henri Gomez [hgomez@apache.org]
034: * @author Dan Milstein [danmil@shore.net]
035: * @author Keith Wannamaker [Keith@Wannamaker.org]
036: * @author Kevin Seguin
037: * @author Costin Manolache
038: */
039: public class Ajp13Packet {
040:
041: public static final String DEFAULT_CHAR_ENCODING = "8859_1";
042: public static final int AJP13_WS_HEADER = 0x1234;
043: public static final int AJP13_SW_HEADER = 0x4142; // 'AB'
044:
045: /**
046: * encoding to use when converting byte[] <-> string
047: */
048: String encoding = DEFAULT_CHAR_ENCODING;
049:
050: /**
051: * Holds the bytes of the packet
052: */
053: byte buff[];
054:
055: /**
056: * The current read or write position in the buffer
057: */
058: int pos;
059:
060: /**
061: * This actually means different things depending on whether the
062: * packet is read or write. For read, it's the length of the
063: * payload (excluding the header). For write, it's the length of
064: * the packet as a whole (counting the header). Oh, well.
065: */
066: int len;
067:
068: /**
069: * Create a new packet with an internal buffer of given size.
070: * @param size packet size
071: */
072: public Ajp13Packet(int size) {
073: buff = new byte[size];
074: }
075:
076: /**
077: * Create a new packet with given bytes
078: * @param b this packet's bytes.
079: */
080: public Ajp13Packet(byte b[]) {
081: buff = b;
082: }
083:
084: /**
085: * Set the encoding to use for byte[] <-> string
086: * conversions.
087: * @param encoding the encoding to use.
088: */
089: public void setEncoding(String encoding) {
090: this .encoding = encoding;
091: }
092:
093: /**
094: * Get the encoding used for byte[] <-> string
095: * conversions.
096: * @return the encoding used.
097: */
098: public String getEncoding() {
099: return encoding;
100: }
101:
102: /**
103: * Get the internal buffer
104: * @return internal buffer
105: */
106: public byte[] getBuff() {
107: return buff;
108: }
109:
110: /**
111: * Get length.
112: * @return length -- This actually means different things depending on whether the
113: * packet is read or write. For read, it's the length of the
114: * payload (excluding the header). For write, it's the length of
115: * the packet as a whole (counting the header). Oh, well.
116: */
117: public int getLen() {
118: return len;
119: }
120:
121: /**
122: * Get offset into internal buffer.
123: * @return offset
124: */
125: public int getByteOff() {
126: return pos;
127: }
128:
129: /**
130: * Set offset into internal buffer.
131: * @param c new offset
132: */
133: public void setByteOff(int c) {
134: pos = c;
135: }
136:
137: /**
138: * Parse the packet header for a packet sent from the web server to
139: * the container. Set the read position to immediately after
140: * the header.
141: *
142: * @return The length of the packet payload, as encoded in the
143: * header, or -1 if the packet doesn't have a valid header.
144: */
145: public int checkIn() {
146: pos = 0;
147: int mark = getInt();
148: len = getInt();
149:
150: if (mark != AJP13_WS_HEADER) {
151: // XXX Logging
152: System.out.println("BAD packet " + mark);
153: dump("In: ");
154: return -1;
155: }
156: return len;
157: }
158:
159: /**
160: * Prepare this packet for accumulating a message from the container to
161: * the web server. Set the write position to just after the header
162: * (but leave the length unwritten, because it is as yet unknown).
163: */
164: public void reset() {
165: len = 4;
166: pos = 4;
167: buff[0] = (byte) (AJP13_SW_HEADER >> 8);
168: buff[1] = (byte) (AJP13_SW_HEADER & 0xFF);
169: }
170:
171: /**
172: * For a packet to be sent to the web server, finish the process of
173: * accumulating data and write the length of the data payload into
174: * the header.
175: */
176: public void end() {
177: len = pos;
178: setInt(2, len - 4);
179: }
180:
181: // ============ Data Writing Methods ===================
182:
183: /**
184: * Write an 32 bit integer at an arbitrary position in the packet,but don't
185: * change the write position.
186: *
187: * @param bpos The 0-indexed position within the buffer at which to
188: * write the integer (where 0 is the beginning of the header).
189: * @param val The integer to write.
190: */
191: private void setInt(int bPos, int val) {
192: buff[bPos] = (byte) ((val >>> 8) & 0xFF);
193: buff[bPos + 1] = (byte) (val & 0xFF);
194: }
195:
196: public void appendInt(int val) {
197: setInt(pos, val);
198: pos += 2;
199: }
200:
201: public void appendByte(byte val) {
202: buff[pos++] = val;
203: }
204:
205: public void appendBool(boolean val) {
206: buff[pos++] = (byte) (val ? 1 : 0);
207: }
208:
209: /**
210: * Write a String out at the current write position. Strings are
211: * encoded with the length in two bytes first, then the string, and
212: * then a terminating \0 (which is <B>not</B> included in the
213: * encoded length). The terminator is for the convenience of the C
214: * code, where it saves a round of copying. A null string is
215: * encoded as a string with length 0.
216: */
217: public void appendString(String str)
218: throws UnsupportedEncodingException {
219: // Dual use of the buffer - as Ajp13Packet and as OutputBuffer
220: // The idea is simple - fewer buffers, smaller footprint and less
221: // memcpy. The code is a bit tricky, but only local to this
222: // function.
223: if (str == null) {
224: setInt(pos, 0);
225: buff[pos + 2] = 0;
226: pos += 3;
227: return;
228: }
229:
230: //
231: // XXX i don't have OutputBuffer in tc4... ks.
232: // fix this later...
233: //
234: byte[] bytes = str.getBytes(encoding);
235: appendBytes(bytes, 0, bytes.length);
236:
237: /* XXX XXX XXX XXX Try to add it back.
238: int strStart=pos;
239:
240: // This replaces the old ( buggy and slow ) str.length()
241: // and str.getBytes(). str.length() is chars, may be != bytes
242: // and getBytes is _very_ slow.
243: // XXX setEncoding !!!
244:
245: ob.setByteOff( pos+2 );
246: try {
247: ob.write( str );
248: ob.flushChars();
249: } catch( IOException ex ) {
250: ex.printStackTrace();
251: }
252: int strEnd=ob.getByteOff();
253:
254: buff[strEnd]=0; // The \0 terminator
255: int strLen=strEnd-strStart;
256: setInt( pos, strEnd - strStart );
257: pos += strLen + 3;
258: */
259: }
260:
261: /**
262: * Copy a chunk of bytes into the packet, starting at the current
263: * write position. The chunk of bytes is encoded with the length
264: * in two bytes first, then the data itself, and finally a
265: * terminating \0 (which is <B>not</B> included in the encoded
266: * length).
267: *
268: * @param b The array from which to copy bytes.
269: * @param off The offset into the array at which to start copying
270: * @param len The number of bytes to copy.
271: */
272: public void appendBytes(byte b[], int off, int numBytes) {
273: appendInt(numBytes);
274: if (pos + numBytes >= buff.length) {
275: System.out.println("Buffer overflow " + buff.length + " "
276: + pos + " " + numBytes);
277: // XXX Log
278: }
279: System.arraycopy(b, off, buff, pos, numBytes);
280: buff[pos + numBytes] = 0; // Terminating \0
281: pos += numBytes + 1;
282: }
283:
284: /**
285: * Write a 32 bits integer at an arbitrary position in the packet, but don't
286: * change the write position.
287: *
288: * @param bpos The 0-indexed position within the buffer at which to
289: * write the integer (where 0 is the beginning of the header).
290: * @param val The integer to write.
291: */
292: private void setLongInt(int bPos, int val) {
293: buff[bPos] = (byte) ((val >>> 24) & 0xFF);
294: buff[bPos + 1] = (byte) ((val >>> 16) & 0xFF);
295: buff[bPos + 2] = (byte) ((val >>> 8) & 0xFF);
296: buff[bPos + 3] = (byte) (val & 0xFF);
297: }
298:
299: public void appendLongInt(int val) {
300: setLongInt(pos, val);
301: pos += 4;
302: }
303:
304: /**
305: * Copy a chunk of bytes into the packet, starting at the current
306: * write position. The chunk of bytes IS NOT ENCODED with ANY length
307: * header.
308: *
309: * @param b The array from which to copy bytes.
310: * @param off The offset into the array at which to start copying
311: * @param len The number of bytes to copy.
312: */
313: public void appendXBytes(byte[] b, int off, int numBytes) {
314: if (pos + numBytes > buff.length) {
315: System.out.println("appendXBytes - Buffer overflow "
316: + buff.length + " " + pos + " " + numBytes);
317: // XXX Log
318: }
319: System.arraycopy(b, off, buff, pos, numBytes);
320: pos += numBytes;
321: }
322:
323: // ============ Data Reading Methods ===================
324:
325: /**
326: * Read an integer from packet, and advance the read position past
327: * it. Integers are encoded as two unsigned bytes with the
328: * high-order byte first, and, as far as I can tell, in
329: * little-endian order within each byte.
330: */
331: public int getInt() {
332: int result = peekInt();
333: pos += 2;
334: return result;
335: }
336:
337: /**
338: * Read an integer from the packet, but don't advance the read
339: * position past it.
340: */
341: public int peekInt() {
342: int b1 = buff[pos] & 0xFF; // No swap, Java order
343: int b2 = buff[pos + 1] & 0xFF;
344:
345: return (b1 << 8) + b2;
346: }
347:
348: public byte getByte() {
349: byte res = buff[pos];
350: pos++;
351: return res;
352: }
353:
354: public byte peekByte() {
355: return buff[pos];
356: }
357:
358: public boolean getBool() {
359: return (getByte() == (byte) 1);
360: }
361:
362: public void getMessageBytes(MessageBytes mb) {
363: int length = getInt();
364: if ((length == 0xFFFF) || (length == -1)) {
365: mb.setString(null);
366: return;
367: }
368: mb.setBytes(buff, pos, length);
369: pos += length;
370: pos++; // Skip the terminating \0
371: }
372:
373: public MessageBytes addHeader(MimeHeaders headers) {
374: int length = getInt();
375: if ((length == 0xFFFF) || (length == -1)) {
376: return null;
377: }
378: MessageBytes vMB = headers.addValue(buff, pos, length);
379: pos += length;
380: pos++; // Skip the terminating \0
381:
382: return vMB;
383: }
384:
385: /**
386: * Read a String from the packet, and advance the read position
387: * past it. See appendString for details on string encoding.
388: **/
389: public String getString()
390: throws java.io.UnsupportedEncodingException {
391: int length = getInt();
392: if ((length == 0xFFFF) || (length == -1)) {
393: // System.out.println("null string " + length);
394: return null;
395: }
396: String s = new String(buff, pos, length, encoding);
397:
398: pos += length;
399: pos++; // Skip the terminating \0
400: return s;
401: }
402:
403: /**
404: * Copy a chunk of bytes from the packet into an array and advance
405: * the read position past the chunk. See appendBytes() for details
406: * on the encoding.
407: *
408: * @return The number of bytes copied.
409: */
410: public int getBytes(byte dest[]) {
411: int length = getInt();
412: if (length > buff.length) {
413: // XXX Should be if(pos + length > buff.legth)?
414: System.out.println("XXX Assert failed, buff too small ");
415: }
416:
417: if ((length == 0xFFFF) || (length == -1)) {
418: System.out.println("null string " + length);
419: return 0;
420: }
421:
422: System.arraycopy(buff, pos, dest, 0, length);
423: pos += length;
424: pos++; // Skip terminating \0 XXX I believe this is wrong but harmless
425: return length;
426: }
427:
428: /**
429: * Read a 32 bits integer from packet, and advance the read position past
430: * it. Integers are encoded as four unsigned bytes with the
431: * high-order byte first, and, as far as I can tell, in
432: * little-endian order within each byte.
433: */
434: public int getLongInt() {
435: int result = peekLongInt();
436: pos += 4;
437: return result;
438: }
439:
440: /**
441: * Copy a chunk of bytes from the packet into an array and advance
442: * the read position past the chunk. See appendXBytes() for details
443: * on the encoding.
444: *
445: * @return The number of bytes copied.
446: */
447: public int getXBytes(byte[] dest, int length) {
448: if (length > buff.length) {
449: // XXX Should be if(pos + length > buff.legth)?
450: System.out.println("XXX Assert failed, buff too small ");
451: }
452:
453: System.arraycopy(buff, pos, dest, 0, length);
454: pos += length;
455: return length;
456: }
457:
458: /**
459: * Read a 32 bits integer from the packet, but don't advance the read
460: * position past it.
461: */
462: public int peekLongInt() {
463: int b1 = buff[pos] & 0xFF; // No swap, Java order
464: b1 <<= 8;
465: b1 |= (buff[pos + 1] & 0xFF);
466: b1 <<= 8;
467: b1 |= (buff[pos + 2] & 0xFF);
468: b1 <<= 8;
469: b1 |= (buff[pos + 3] & 0xFF);
470: return b1;
471: }
472:
473: // ============== Debugging code =========================
474: private String hex(int x) {
475: // if( x < 0) x=256 + x;
476: String h = Integer.toHexString(x);
477: if (h.length() == 1)
478: h = "0" + h;
479: return h.substring(h.length() - 2);
480: }
481:
482: private void hexLine(int start) {
483: int pkgEnd = len + 4;
484: if (pkgEnd > buff.length)
485: pkgEnd = buff.length;
486: for (int i = start; i < start + 16; i++) {
487: if (i < pkgEnd)
488: System.out.print(hex(buff[i]) + " ");
489: else
490: System.out.print(" ");
491: }
492: System.out.print(" | ");
493: for (int i = start; i < start + 16 && i < pkgEnd; i++) {
494: if (Character.isLetterOrDigit((char) buff[i]))
495: System.out.print(new Character((char) buff[i]));
496: else
497: System.out.print(".");
498: }
499: System.out.println();
500: }
501:
502: public void dump(String msg) {
503: System.out.println(msg + ": " + buff + " " + pos + "/"
504: + (len + 4));
505:
506: for (int j = 0; j < len + 4; j += 16)
507: hexLine(j);
508:
509: System.out.println();
510: }
511: }
|