Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/ajp/Ajp13Packet.java


1   /*
2    *  Copyright 1999-2004 The Apache Software Foundation
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  
17  package org.apache.ajp;
18  
19  import java.io.UnsupportedEncodingException;
20  
21  import org.apache.tomcat.util.buf.MessageBytes;
22  import org.apache.tomcat.util.http.MimeHeaders;
23  
24  
25  /**
26   * A single packet for communication between the web server and the
27   * container.  Designed to be reused many times with no creation of
28   * garbage.  Understands the format of data types for these packets.
29   * Can be used (somewhat confusingly) for both incoming and outgoing
30   * packets.  
31   *
32   * See Ajp14/Ajp13Packet.
33   *
34   * @author Henri Gomez [hgomez@apache.org]
35   * @author Dan Milstein [danmil@shore.net]
36   * @author Keith Wannamaker [Keith@Wannamaker.org]
37   * @author Kevin Seguin
38   * @author Costin Manolache
39   */
40  public class Ajp13Packet {
41      
42      private static org.apache.commons.logging.Log log=
43          org.apache.commons.logging.LogFactory.getLog( Ajp13Packet.class );
44      
45      public static final String DEFAULT_CHAR_ENCODING = "8859_1";
46      public static final int    AJP13_WS_HEADER       = 0x1234;
47      public static final int    AJP13_SW_HEADER       = 0x4142;  // 'AB'
48  
49      /**
50       * encoding to use when converting byte[] <-> string
51       */
52      String encoding = DEFAULT_CHAR_ENCODING;
53  
54      /**
55       * Holds the bytes of the packet
56       */
57      byte buff[];
58  
59      /**
60       * The current read or write position in the buffer
61       */
62      int pos;    
63  
64      /**
65       * This actually means different things depending on whether the
66       * packet is read or write.  For read, it's the length of the
67       * payload (excluding the header).  For write, it's the length of
68       * the packet as a whole (counting the header).  Oh, well.
69       */
70      int len; 
71  
72      /**
73       * Create a new packet with an internal buffer of given size.
74       * @param size packet size
75       */
76      public Ajp13Packet( int size ) {
77          buff = new byte[size];
78      }
79  
80      /**
81       * Create a new packet with given bytes
82       * @param b this packet's bytes.
83       */
84      public Ajp13Packet( byte b[] ) {
85          buff = b;
86      }
87  
88      /**
89       * Set the encoding to use for byte[] <-> string
90       * conversions.
91       * @param encoding the encoding to use.
92       */
93      public void setEncoding(String encoding) {
94          this.encoding = encoding;
95      }
96  
97      /**
98       * Get the encoding used for byte[] <-> string
99       * conversions.
100      * @return the encoding used.
101      */
102     public String getEncoding() {
103         return encoding;
104     }
105 
106     /**
107      * Get the internal buffer
108      * @return internal buffer
109      */
110     public byte[] getBuff() {
111         return buff;
112     }
113 
114     /**
115      * Get length.
116      * @return length -- This actually means different things depending on whether the
117      *                   packet is read or write.  For read, it's the length of the
118      *                   payload (excluding the header).  For write, it's the length of
119      *                   the packet as a whole (counting the header).  Oh, well.
120      */
121     public int getLen() {
122         return len;
123     }
124 
125     /**
126      * Get offset into internal buffer.
127      * @return offset
128      */
129     public int getByteOff() {
130         return pos;
131     }
132 
133     /**
134      * Set offset into internal buffer.
135      * @param c new offset
136      */
137     public void setByteOff(int c) {
138         pos=c;
139     }
140 
141     /** 
142      * Parse the packet header for a packet sent from the web server to
143      * the container.  Set the read position to immediately after
144      * the header.
145      *
146      * @return The length of the packet payload, as encoded in the
147      * header, or -1 if the packet doesn't have a valid header.  
148      */
149     public int checkIn() {
150         pos = 0;
151         int mark = getInt();
152         len      = getInt();
153       
154         if( mark != AJP13_WS_HEADER ) {
155             if (log.isDebugEnabled())
156                 log.debug("BAD packet " + mark);
157             dump( "In: " );
158             return -1;
159         }
160         return len;
161     }
162     
163     /**
164      * Prepare this packet for accumulating a message from the container to
165      * the web server.  Set the write position to just after the header
166      * (but leave the length unwritten, because it is as yet unknown).
167      */
168     public void reset() {
169         len = 4;
170         pos = 4;
171         buff[0] = (byte)(AJP13_SW_HEADER >> 8);
172         buff[1] = (byte)(AJP13_SW_HEADER & 0xFF);
173     }
174   
175     /**
176      * For a packet to be sent to the web server, finish the process of
177      * accumulating data and write the length of the data payload into
178      * the header.  
179      */
180     public void end() {
181         len = pos;
182         setInt( 2, len-4 );
183     }
184   
185     // ============ Data Writing Methods ===================
186 
187     /**
188      * Write an 32 bit integer at an arbitrary position in the packet,but don't
189      * change the write position.
190      *
191      * @param bpos The 0-indexed position within the buffer at which to
192      * write the integer (where 0 is the beginning of the header).
193      * @param val The integer to write.
194      */
195     private void setInt( int bPos, int val ) {
196         buff[bPos]   = (byte) ((val >>>  8) & 0xFF);
197         buff[bPos+1] = (byte) (val & 0xFF);
198     }
199 
200     public void appendInt( int val ) {
201         setInt( pos, val );
202         pos += 2;
203     }
204   
205     public void appendByte( byte val ) {
206         buff[pos++] = val;
207     }
208   
209     public void appendBool( boolean val) {
210         buff[pos++] = (byte) (val ? 1 : 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) throws UnsupportedEncodingException {
222         // Dual use of the buffer - as Ajp13Packet and as OutputBuffer
223         // The idea is simple - fewer buffers, smaller footprint and less
224         // memcpy. The code is a bit tricky, but only local to this
225         // function.
226         if(str == null) {
227             setInt( pos, 0);
228             buff[pos + 2] = 0;
229             pos += 3;
230             return;
231         }
232 
233         //
234         // XXX i don't have OutputBuffer in tc4... ks.
235         // fix this later...
236         //
237         byte[] bytes = str.getBytes(encoding);
238         appendBytes(bytes, 0, bytes.length);
239         
240         /* XXX XXX XXX XXX Try to add it back.
241         int strStart=pos;
242 
243         // This replaces the old ( buggy and slow ) str.length()
244         // and str.getBytes(). str.length() is chars, may be != bytes
245         // and getBytes is _very_ slow.
246         // XXX setEncoding !!!
247 
248         ob.setByteOff( pos+2 ); 
249         try {
250             ob.write( str );
251             ob.flushChars();
252         } catch( IOException ex ) {
253             ex.printStackTrace();
254         }
255         int strEnd=ob.getByteOff();
256         
257         buff[strEnd]=0; // The \0 terminator
258         int strLen=strEnd-strStart;
259         setInt( pos, strEnd - strStart );
260         pos += strLen + 3; 
261         */
262     }
263 
264     /** 
265      * Copy a chunk of bytes into the packet, starting at the current
266      * write position.  The chunk of bytes is encoded with the length
267      * in two bytes first, then the data itself, and finally a
268      * terminating \0 (which is <B>not</B> included in the encoded
269      * length).
270      *
271      * @param b The array from which to copy bytes.
272      * @param off The offset into the array at which to start copying
273      * @param numBytes The number of bytes to copy.  
274      */
275     public void appendBytes( byte b[], int off, int numBytes ) {
276         appendInt( numBytes );
277         if( pos + numBytes >= buff.length ) {
278             if (log.isDebugEnabled())
279                 log.debug("Buffer overflow " + buff.length + " " + pos + " " + numBytes );
280         }
281         System.arraycopy( b, off, buff, pos, numBytes);
282         buff[pos + numBytes] = 0; // Terminating \0
283         pos += numBytes + 1;
284     }
285 
286         /**
287      * Write a 32 bits integer at an arbitrary position in the packet, but don't
288      * change the write position.
289      *
290      * @param bpos The 0-indexed position within the buffer at which to
291      * write the integer (where 0 is the beginning of the header).
292      * @param val The integer to write.
293      */
294     private void setLongInt( int bPos, int val ) {
295         buff[bPos]   = (byte) ((val >>>  24) & 0xFF);
296         buff[bPos+1] = (byte) ((val >>>  16) & 0xFF);
297         buff[bPos+2] = (byte) ((val >>>   8) & 0xFF);
298         buff[bPos+3] = (byte) (val & 0xFF);
299     }
300 
301     public void appendLongInt( int val ) {
302         setLongInt( pos, val );
303         pos += 4;
304     }
305 
306     /**
307      * Copy a chunk of bytes into the packet, starting at the current
308      * write position.  The chunk of bytes IS NOT ENCODED with ANY length
309      * header.
310      *
311      * @param b The array from which to copy bytes.
312      * @param off The offset into the array at which to start copying
313      * @param numBytes The number of bytes to copy.
314      */
315     public void appendXBytes(byte[] b, int off, int numBytes) {
316         if( pos + numBytes > buff.length ) {
317             if (log.isDebugEnabled())
318                 log.debug("appendXBytes - Buffer overflow " + buff.length + " " + pos + " " + numBytes );
319         }
320         System.arraycopy( b, off, buff, pos, numBytes);
321         pos += numBytes;
322     }
323   
324     
325     // ============ Data Reading Methods ===================
326 
327     /**
328      * Read an integer from packet, and advance the read position past
329      * it.  Integers are encoded as two unsigned bytes with the
330      * high-order byte first, and, as far as I can tell, in
331      * little-endian order within each byte.  
332      */
333     public int getInt() {
334         int result = peekInt();
335         pos += 2;
336         return result;
337     }
338 
339     /**
340      * Read an integer from the packet, but don't advance the read
341      * position past it.  
342      */
343     public int peekInt() {
344         int b1 = buff[pos] & 0xFF;  // No swap, Java order
345         int b2 = buff[pos + 1] & 0xFF;
346 
347         return  (b1<<8) + b2;
348     }
349 
350     public byte getByte() {
351         byte res = buff[pos];
352         pos++;
353         return res;
354     }
355 
356     public byte peekByte() {
357         return buff[pos];
358     }
359 
360     public boolean getBool() {
361         return (getByte() == (byte) 1);
362     }
363 
364     public void getMessageBytes(MessageBytes mb) {
365         int length = getInt();
366         if( (length == 0xFFFF) || (length == -1) ) {
367             mb.setString( null );
368             return;
369         }
370         mb.setBytes( buff, pos, length );
371         pos += length;
372         pos++; // Skip the terminating \0
373     }
374     
375     public MessageBytes addHeader(MimeHeaders headers) {
376         int length = getInt();
377         if( (length == 0xFFFF) || (length == -1) ) {
378             return null;
379         }
380         MessageBytes vMB=headers.addValue( buff, pos, length );
381         pos += length;
382         pos++; // Skip the terminating \0
383       
384         return vMB;
385     }
386   
387     /**
388      * Read a String from the packet, and advance the read position
389      * past it.  See appendString for details on string encoding.
390      **/
391     public String getString() throws java.io.UnsupportedEncodingException {
392         int length = getInt();
393         if( (length == 0xFFFF) || (length == -1) ) {
394             if (log.isDebugEnabled())
395                 log.debug("null string " + length);
396             return null;
397         }
398         String s = new String(buff, pos, length, encoding);
399 
400         pos += length;
401         pos++; // Skip the terminating \0
402         return s;
403     }
404 
405     /**
406      * Copy a chunk of bytes from the packet into an array and advance
407      * the read position past the chunk.  See appendBytes() for details
408      * on the encoding.
409      *
410      * @return The number of bytes copied.
411      */
412     public int getBytes(byte dest[]) {
413         int length = getInt();
414         if( length > buff.length ) {
415             // XXX Should be if(pos + length > buff.legth)?
416             if (log.isDebugEnabled())
417                 log.debug("XXX Assert failed, buff too small ");
418         }
419   
420         if( (length == 0xFFFF) || (length == -1) ) {
421             if (log.isDebugEnabled())
422                 log.debug("null string " + length);
423             return 0;
424         }
425 
426         System.arraycopy( buff, pos,  dest, 0, length );
427         pos += length;
428         pos++; // Skip terminating \0  XXX I believe this is wrong but harmless
429         return length;
430     }
431 
432         /**
433      * Read a 32 bits integer from packet, and advance the read position past
434      * it.  Integers are encoded as four unsigned bytes with the
435      * high-order byte first, and, as far as I can tell, in
436      * little-endian order within each byte.
437      */
438     public int getLongInt() {
439         int result = peekLongInt();
440         pos += 4;
441         return result;
442     }
443 
444     /**
445      * Copy a chunk of bytes from the packet into an array and advance
446      * the read position past the chunk.  See appendXBytes() for details
447      * on the encoding.
448      *
449      * @return The number of bytes copied.
450      */
451     public int getXBytes(byte[] dest, int length) {
452         if( length > buff.length ) {
453         // XXX Should be if(pos + length > buff.legth)?
454             if (log.isDebugEnabled())
455                 log.debug("XXX Assert failed, buff too small ");
456         }
457 
458         System.arraycopy( buff, pos,  dest, 0, length );
459         pos += length;
460         return length;
461     }
462 
463     /**
464      * Read a 32 bits integer from the packet, but don't advance the read
465      * position past it.
466      */
467     public int peekLongInt() {
468         int b1 = buff[pos] & 0xFF;  // No swap, Java order
469         b1 <<= 8;
470         b1 |= (buff[pos + 1] & 0xFF);
471         b1 <<= 8;
472         b1 |= (buff[pos + 2] & 0xFF);
473         b1 <<=8;
474         b1 |= (buff[pos + 3] & 0xFF);
475         return  b1;
476     }
477 
478     // ============== Debugging code =========================
479     private String hex( int x ) {
480         //      if( x < 0) x=256 + x;
481         String h=Integer.toHexString( x );
482         if( h.length() == 1 ) h = "0" + h;
483         return h.substring( h.length() - 2 );
484     }
485 
486     private void hexLine( int start ) {
487   int pkgEnd = len + 4;
488   if( pkgEnd > buff.length )
489       pkgEnd = buff.length;
490         for( int i=start; i< start+16 ; i++ ) {
491             if( i < pkgEnd) {
492                 if (log.isDebugEnabled())
493                     log.debug( hex( buff[i] ) + " ");
494             } else {
495                 if (log.isDebugEnabled()) 
496                     log.debug( "   " );
497             }
498         }
499         if (log.isDebugEnabled()) 
500             log.debug(" | ");
501         for( int i=start; i < start+16 && i < pkgEnd; i++ ) {
502             if( Character.isLetterOrDigit( (char)buff[i] )) {
503                 if (log.isDebugEnabled()) 
504                     log.debug( new Character((char)buff[i]) );
505             } else {
506                 if (log.isDebugEnabled()) 
507                     log.debug( "." );
508             }
509         }
510     }
511     
512     public void dump(String msg) {
513         if (log.isDebugEnabled())
514             log.debug( msg + ": " + buff + " " + pos +"/" + (len + 4));
515 
516         for( int j=0; j < len + 4; j+=16 )
517             hexLine( j );
518 
519     }
520 }