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 }