FileDocCategorySizeDatePackage
AjpMessage.javaAPI DocApache Tomcat 6.0.1413769Fri Jul 20 04:20:36 BST 2007org.apache.coyote.ajp

AjpMessage

public class AjpMessage extends Object
A single packet for communication between the web server and the container. Designed to be reused many times with no creation of garbage. Understands the format of data types for these packets. Can be used (somewhat confusingly) for both incoming and outgoing packets.
author
Henri Gomez
author
Dan Milstein
author
Keith Wannamaker
author
Kevin Seguin
author
Costin Manolache

Fields Summary
protected static org.apache.juli.logging.Log
log
protected static org.apache.tomcat.util.res.StringManager
sm
The string manager for this package.
protected byte[]
buf
Fixed size buffer.
protected int
pos
The current read or write position in the buffer.
protected int
len
This actually means different things depending on whether the packet is read or write. For read, it's the length of the payload (excluding the header). For write, it's the length of the packet as a whole (counting the header). Oh, well.
Constructors Summary
public AjpMessage(int packetSize)



    // ------------------------------------------------------------ Constructor

    
       
        buf = new byte[packetSize];
    
Methods Summary
public voidappendByte(int val)
Append a byte (1 byte) to the message.

        buf[pos++] = (byte) val;
    
public voidappendByteChunk(org.apache.tomcat.util.buf.ByteChunk bc)
Write a ByteChunk out at the current write position. A null ByteChunk is encoded as a string with length 0.

        if (bc == null) {
            log.error(sm.getString("ajpmessage.null"), 
                    new NullPointerException());
            appendInt(0);
            appendByte(0);
            return;
        }
        appendBytes(bc.getBytes(), bc.getStart(), bc.getLength());
    
public voidappendBytes(byte[] b, int off, int numBytes)
Copy a chunk of bytes into the packet, starting at the current write position. The chunk of bytes is encoded with the length in two bytes first, then the data itself, and finally a terminating \0 (which is not included in the encoded length).

param
b The array from which to copy bytes.
param
off The offset into the array at which to start copying
param
numBytes The number of bytes to copy.

        if (pos + numBytes + 3 > buf.length) {
            log.error(sm.getString("ajpmessage.overflow", "" + numBytes, "" + pos),
                    new ArrayIndexOutOfBoundsException());
            if (log.isDebugEnabled()) {
                dump("Overflow/coBytes");
            }
            return;
        }
        appendInt(numBytes);
        System.arraycopy(b, off, buf, pos, numBytes);
        pos += numBytes;
        appendByte(0);
    
public voidappendBytes(org.apache.tomcat.util.buf.MessageBytes mb)
Write a MessageBytes out at the current write position. A null MessageBytes is encoded as a string with length 0.

        if (mb == null) {
            log.error(sm.getString("ajpmessage.null"), 
                    new NullPointerException());
            appendInt(0);
            appendByte(0);
            return;
        }
        if (mb.getType() == MessageBytes.T_BYTES) {
            ByteChunk bc = mb.getByteChunk();
            appendByteChunk(bc);
        } else if (mb.getType() == MessageBytes.T_CHARS) {
            CharChunk cc = mb.getCharChunk();
            appendCharChunk(cc);
        } else {
            appendString(mb.toString());
        }
    
public voidappendCharChunk(org.apache.tomcat.util.buf.CharChunk cc)
Write a CharChunk out at the current write position. A null CharChunk is encoded as a string with length 0.

        if (cc == null) {
            log.error(sm.getString("ajpmessage.null"), 
                    new NullPointerException());
            appendInt(0);
            appendByte(0);
            return;
        }
        int start = cc.getStart();
        int end = cc.getEnd();
        appendInt(end - start);
        char[] cbuf = cc.getBuffer();
        for (int i = start; i < end; i++) {
            char c = cbuf[i];
            // Note:  This is clearly incorrect for many strings,
            // but is the only consistent approach within the current
            // servlet framework.  It must suffice until servlet output
            // streams properly encode their output.
            if ((c <= 31) && (c != 9)) {
                c = ' ";
            } else if (c == 127) {
                c = ' ";
            }
            appendByte(c);
        }
        appendByte(0);
    
public voidappendInt(int val)
Add a short integer (2 bytes) to the message.

        buf[pos++] = (byte) ((val >>> 8) & 0xFF);
        buf[pos++] = (byte) (val & 0xFF);
    
public voidappendLongInt(int val)
Append an int (4 bytes) to the message.

        buf[pos++] = (byte) ((val >>> 24) & 0xFF);
        buf[pos++] = (byte) ((val >>> 16) & 0xFF);
        buf[pos++] = (byte) ((val >>> 8) & 0xFF);
        buf[pos++] = (byte) (val & 0xFF);
    
public voidappendString(java.lang.String str)
Write a String out at the current write position. Strings are encoded with the length in two bytes first, then the string, and then a terminating \0 (which is not included in the encoded length). The terminator is for the convenience of the C code, where it saves a round of copying. A null string is encoded as a string with length 0.

        if (str == null) {
            log.error(sm.getString("ajpmessage.null"), 
                    new NullPointerException());
            appendInt(0);
            appendByte(0);
            return;
        }
        int len = str.length();
        appendInt(len);
        for (int i = 0; i < len; i++) {
            char c = str.charAt (i);
            // Note:  This is clearly incorrect for many strings,
            // but is the only consistent approach within the current
            // servlet framework.  It must suffice until servlet output
            // streams properly encode their output.
            if ((c <= 31) && (c != 9)) {
                c = ' ";
            } else if (c == 127) {
                c = ' ";
            }
            appendByte(c);
        }
        appendByte(0);
    
public voiddump(java.lang.String msg)
Dump the contents of the message, prefixed with the given String.

        if (log.isDebugEnabled()) {
            log.debug(msg + ": " + buf + " " + pos +"/" + (len + 4));
        }
        int max = pos;
        if (len + 4 > pos)
            max = len+4;
        if (max > 1000)
            max = 1000;
        if (log.isDebugEnabled()) {
            for (int j = 0; j < max; j += 16) { 
                log.debug(hexLine(buf, j, len));
            }
        }
    
public voidend()
For a packet to be sent to the web server, finish the process of accumulating data and write the length of the data payload into the header.

        len = pos;
        int dLen = len - 4;

        buf[0] = (byte) 0x41;
        buf[1] = (byte) 0x42;
        buf[2] = (byte) ((dLen>>>8) & 0xFF);
        buf[3] = (byte) (dLen & 0xFF);
    
public byte[]getBuffer()
Return the underlying byte buffer.

        return buf;
    
public bytegetByte()

        byte res = buf[pos++];
        return res;
    
public voidgetBytes(org.apache.tomcat.util.buf.MessageBytes mb)

        int length = getInt();
        if ((length == 0xFFFF) || (length == -1)) {
            mb.recycle();
            return;
        }
        mb.setBytes(buf, pos, length);
        pos += length;
        pos++; // Skip the terminating \0
    
public intgetBytes(byte[] dest)
Copy a chunk of bytes from the packet into an array and advance the read position past the chunk. See appendBytes() for details on the encoding.

return
The number of bytes copied.

        int length = getInt();
        if (pos + length > buf.length) {
            log.error(sm.getString("ajpmessage.read", "" + length));
            return 0;
        }
	
        if ((length == 0xFFFF) || (length == -1)) {
            return 0;
        }

        System.arraycopy(buf, pos, dest, 0, length);
        pos += length;
        pos++; // Skip terminating \0
        return length;
    
public intgetHeaderLength()

        return 4;
    
public intgetInt()
Read an integer from packet, and advance the read position past it. Integers are encoded as two unsigned bytes with the high-order byte first, and, as far as I can tell, in little-endian order within each byte.

        int b1 = buf[pos++] & 0xFF;
        int b2 = buf[pos++] & 0xFF;
        return (b1<<8) + b2;
    
public intgetLen()
Return the current message length. For read, it's the length of the payload (excluding the header). For write, it's the length of the packet as a whole (counting the header).

        return len;
    
public intgetLongInt()
Read a 32 bits integer from packet, and advance the read position past it. Integers are encoded as four unsigned bytes with the high-order byte first, and, as far as I can tell, in little-endian order within each byte.

        int b1 = buf[pos++] & 0xFF; // No swap, Java order
        b1 <<= 8;
        b1 |= (buf[pos++] & 0xFF);
        b1 <<= 8;
        b1 |= (buf[pos++] & 0xFF);
        b1 <<=8;
        b1 |= (buf[pos++] & 0xFF);
        return  b1;
    
public intgetPacketSize()

        return buf.length;
    
protected static java.lang.Stringhex(int x)

        String h = Integer.toHexString(x);
        if (h.length() == 1) {
            h = "0" + h;
        }
        return h.substring(h.length() - 2);
    
protected static java.lang.StringhexLine(byte[] buf, int start, int len)

        StringBuffer sb = new StringBuffer();
        for (int i = start; i < start + 16 ; i++) {
            if (i < len + 4) {
                sb.append(hex(buf[i]) + " ");
            } else { 
                sb.append("   ");
            }
        }
        sb.append(" | ");
        for (int i = start; i < start + 16 && i < len + 4; i++) {
            if (!Character.isISOControl((char) buf[i])) {
                sb.append(new Character((char) buf[i]));
            } else {
                sb.append(".");
            }
        }
        return sb.toString();
    
public bytepeekByte()

        byte res = buf[pos];
        return res;
    
public intpeekInt()

        int b1 = buf[pos] & 0xFF;
        int b2 = buf[pos+1] & 0xFF;
        return (b1<<8) + b2;
    
public intprocessHeader()

        pos = 0;
        int mark = getInt();
        len = getInt();
        // Verify message signature
        if ((mark != 0x1234) && (mark != 0x4142)) {
            log.error(sm.getString("ajpmessage.invalid", "" + mark));
            if (log.isDebugEnabled()) {
                dump("In: ");
            }
            return -1;
        }
        if (log.isDebugEnabled())  {
            log.debug("Received " + len + " " + buf[0]);
        }
        return len;
    
public voidreset()
Prepare this packet for accumulating a message from the container to the web server. Set the write position to just after the header (but leave the length unwritten, because it is as yet unknown).

 

    
    // --------------------------------------------------------- Public Methods


                                            
       
        len = 4;
        pos = 4;