1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.jk.common;
19
20 import java.io.IOException;
21
22 import org.apache.jk.core.Msg;
23 import org.apache.tomcat.util.buf.ByteChunk;
24 import org.apache.tomcat.util.buf.MessageBytes;
25
26 /**
27 * A single packet for communication between the web server and the
28 * container. Designed to be reused many times with no creation of
29 * garbage. Understands the format of data types for these packets.
30 * Can be used (somewhat confusingly) for both incoming and outgoing
31 * packets.
32 *
33 * See Ajp14/Ajp13Packet.java.
34 *
35 * @author Henri Gomez [hgomez@apache.org]
36 * @author Dan Milstein [danmil@shore.net]
37 * @author Keith Wannamaker [Keith@Wannamaker.org]
38 * @author Kevin Seguin
39 * @author Costin Manolache
40 */
41 public class MsgAjp extends Msg {
42 private static org.apache.juli.logging.Log log=
43 org.apache.juli.logging.LogFactory.getLog( MsgAjp.class );
44
45 // that's the original buffer size in ajp13 - otherwise we'll get interoperability problems.
46 private byte buf[];
47 // The current read or write position in the buffer
48 private int pos;
49 /**
50 * This actually means different things depending on whether the
51 * packet is read or write. For read, it's the length of the
52 * payload (excluding the header). For write, it's the length of
53 * the packet as a whole (counting the header). Oh, well.
54 */
55 private int len;
56
57 /**
58 * The maximum packet size
59 */
60 private int bufsize;
61
62 /**
63 * Constructor that takes a buffer size
64 */
65 public MsgAjp(int bsize) {
66 if(bsize < 8*1024) {
67 bsize = 8*1024;
68 }
69 bufsize = bsize;
70 buf = new byte[bsize];
71
72 }
73
74 /**
75 * No arg constructor.
76 * @deprecated Use the buffer size constructor.
77 */
78 public MsgAjp() {
79 this(8*1024);
80 }
81
82 /**
83 * Prepare this packet for accumulating a message from the container to
84 * the web server. Set the write position to just after the header
85 * (but leave the length unwritten, because it is as yet unknown).
86 */
87 public void reset() {
88 len = 4;
89 pos = 4;
90 }
91
92 /**
93 * For a packet to be sent to the web server, finish the process of
94 * accumulating data and write the length of the data payload into
95 * the header.
96 */
97 public void end() {
98 len=pos;
99 int dLen=len-4;
100
101 buf[0] = (byte)0x41;
102 buf[1] = (byte)0x42;
103 buf[2]= (byte)((dLen>>>8 ) & 0xFF );
104 buf[3] = (byte)(dLen & 0xFF);
105 }
106
107 public byte[] getBuffer() {
108 return buf;
109 }
110
111 public int getLen() {
112 return len;
113 }
114
115 // ============ Data Writing Methods ===================
116
117 /**
118 * Add an int.
119 *
120 * @param val The integer to write.
121 */
122 public void appendInt( int val ) {
123 buf[pos++] = (byte) ((val >>> 8) & 0xFF);
124 buf[pos++] = (byte) (val & 0xFF);
125 }
126
127 public void appendByte( int val ) {
128 buf[pos++] = (byte)val;
129 }
130
131 public void appendLongInt( int val ) {
132 buf[pos++] = (byte) ((val >>> 24) & 0xFF);
133 buf[pos++] = (byte) ((val >>> 16) & 0xFF);
134 buf[pos++] = (byte) ((val >>> 8) & 0xFF);
135 buf[pos++] = (byte) (val & 0xFF);
136 }
137
138 /**
139 * Write a String out at the current write position. Strings are
140 * encoded with the length in two bytes first, then the string, and
141 * then a terminating \0 (which is <B>not</B> included in the
142 * encoded length). The terminator is for the convenience of the C
143 * code, where it saves a round of copying. A null string is
144 * encoded as a string with length 0.
145 */
146 public void appendBytes(MessageBytes mb) throws IOException {
147 if(mb==null || mb.isNull() ) {
148 appendInt( 0);
149 appendByte(0);
150 return;
151 }
152
153 // XXX Convert !!
154 ByteChunk bc= mb.getByteChunk();
155 appendByteChunk(bc);
156 }
157
158 public void appendByteChunk(ByteChunk bc) throws IOException {
159 if(bc==null) {
160 log.error("appendByteChunk() null");
161 appendInt( 0);
162 appendByte(0);
163 return;
164 }
165
166 byte[] bytes = bc.getBytes();
167 int start=bc.getStart();
168 int length = bc.getLength();
169 appendInt( length );
170 cpBytes(bytes, start, length);
171 appendByte(0);
172 }
173
174 /**
175 * Copy a chunk of bytes into the packet, starting at the current
176 * write position. The chunk of bytes is encoded with the length
177 * in two bytes first, then the data itself, and finally a
178 * terminating \0 (which is <B>not</B> included in the encoded
179 * length).
180 *
181 * @param b The array from which to copy bytes.
182 * @param off The offset into the array at which to start copying
183 * @param numBytes The number of bytes to copy.
184 */
185 public void appendBytes( byte b[], int off, int numBytes ) {
186 appendInt( numBytes );
187 cpBytes( b, off, numBytes );
188 appendByte(0);
189 }
190
191 private void cpBytes( byte b[], int off, int numBytes ) {
192 if( pos + numBytes >= buf.length ) {
193 log.error("Buffer overflow: buffer.len=" + buf.length + " pos=" +
194 pos + " data=" + numBytes );
195 dump("Overflow/coBytes");
196 log.error( "Overflow ", new Throwable());
197 return;
198 }
199 System.arraycopy( b, off, buf, pos, numBytes);
200 pos += numBytes;
201 // buf[pos + numBytes] = 0; // Terminating \0
202 }
203
204
205
206 // ============ Data Reading Methods ===================
207
208 /**
209 * Read an integer from packet, and advance the read position past
210 * it. Integers are encoded as two unsigned bytes with the
211 * high-order byte first, and, as far as I can tell, in
212 * little-endian order within each byte.
213 */
214 public int getInt() {
215 int b1 = buf[pos++] & 0xFF; // No swap, Java order
216 int b2 = buf[pos++] & 0xFF;
217
218 return (b1<<8) + b2;
219 }
220
221 public int peekInt() {
222 int b1 = buf[pos] & 0xFF; // No swap, Java order
223 int b2 = buf[pos+1] & 0xFF;
224
225 return (b1<<8) + b2;
226 }
227
228 public byte getByte() {
229 byte res = buf[pos++];
230 return res;
231 }
232
233 public byte peekByte() {
234 byte res = buf[pos];
235 return res;
236 }
237
238 public void getBytes(MessageBytes mb) {
239 int length = getInt();
240 if( (length == 0xFFFF) || (length == -1) ) {
241 mb.recycle();
242 return;
243 }
244 mb.setBytes( buf, pos, length );
245 mb.getCharChunk().recycle();
246 pos += length;
247 pos++; // Skip the terminating \0
248 }
249
250 /**
251 * Copy a chunk of bytes from the packet into an array and advance
252 * the read position past the chunk. See appendBytes() for details
253 * on the encoding.
254 *
255 * @return The number of bytes copied.
256 */
257 public int getBytes(byte dest[]) {
258 int length = getInt();
259 if( length > buf.length ) {
260 // XXX Should be if(pos + length > buff.legth)?
261 log.error("getBytes() buffer overflow " + length + " " + buf.length );
262 }
263
264 if( (length == 0xFFFF) || (length == -1) ) {
265 log.info("Null string " + length);
266 return 0;
267 }
268
269 System.arraycopy( buf, pos, dest, 0, length );
270 pos += length;
271 pos++; // Skip terminating \0 XXX I believe this is wrong but harmless
272 return length;
273 }
274
275 /**
276 * Read a 32 bits integer from packet, and advance the read position past
277 * it. Integers are encoded as four unsigned bytes with the
278 * high-order byte first, and, as far as I can tell, in
279 * little-endian order within each byte.
280 */
281 public int getLongInt() {
282 int b1 = buf[pos++] & 0xFF; // No swap, Java order
283 b1 <<= 8;
284 b1 |= (buf[pos++] & 0xFF);
285 b1 <<= 8;
286 b1 |= (buf[pos++] & 0xFF);
287 b1 <<=8;
288 b1 |= (buf[pos++] & 0xFF);
289 return b1;
290 }
291
292 public int getHeaderLength() {
293 return 4;
294 }
295
296 public int processHeader() {
297 pos = 0;
298 int mark = getInt();
299 len = getInt();
300
301 if( mark != 0x1234 && mark != 0x4142 ) {
302 // XXX Logging
303 log.error("BAD packet signature " + mark);
304 dump( "In: " );
305 return -1;
306 }
307
308 if( log.isDebugEnabled() )
309 log.debug( "Received " + len + " " + buf[0] );
310 return len;
311 }
312
313 public void dump(String msg) {
314 if( log.isDebugEnabled() )
315 log.debug( msg + ": " + buf + " " + pos +"/" + (len + 4));
316 int max=pos;
317 if( len + 4 > pos )
318 max=len+4;
319 if( max >1000 ) max=1000;
320 if( log.isDebugEnabled() )
321 for( int j=0; j < max; j+=16 )
322 log.debug( hexLine( buf, j, len ));
323
324 }
325
326 /* -------------------- Utilities -------------------- */
327 // XXX Move to util package
328
329 public static String hexLine( byte buf[], int start, int len ) {
330 StringBuffer sb=new StringBuffer();
331 for( int i=start; i< start+16 ; i++ ) {
332 if( i < len + 4)
333 sb.append( hex( buf[i] ) + " ");
334 else
335 sb.append( " " );
336 }
337 sb.append(" | ");
338 for( int i=start; i < start+16 && i < len + 4; i++ ) {
339 if( ! Character.isISOControl( (char)buf[i] ))
340 sb.append( new Character((char)buf[i]) );
341 else
342 sb.append( "." );
343 }
344 return sb.toString();
345 }
346
347 private static String hex( int x ) {
348 // if( x < 0) x=256 + x;
349 String h=Integer.toHexString( x );
350 if( h.length() == 1 ) h = "0" + h;
351 return h.substring( h.length() - 2 );
352 }
353
354 }