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.coyote.ajp;
19
20 import org.apache.tomcat.util.buf.ByteChunk;
21 import org.apache.tomcat.util.buf.CharChunk;
22 import org.apache.tomcat.util.buf.MessageBytes;
23 import org.apache.tomcat.util.res.StringManager;
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 * @author Henri Gomez
33 * @author Dan Milstein
34 * @author Keith Wannamaker
35 * @author Kevin Seguin
36 * @author Costin Manolache
37 */
38 public class AjpMessage {
39
40
41 protected static org.apache.juli.logging.Log log =
42 org.apache.juli.logging.LogFactory.getLog(AjpMessage.class);
43
44 /**
45 * The string manager for this package.
46 */
47 protected static StringManager sm =
48 StringManager.getManager(Constants.Package);
49
50
51 // ------------------------------------------------------------ Constructor
52
53
54 public AjpMessage(int packetSize) {
55 buf = new byte[packetSize];
56 }
57
58
59 // ----------------------------------------------------- Instance Variables
60
61
62 /**
63 * Fixed size buffer.
64 */
65 protected byte buf[] = null;
66
67
68 /**
69 * The current read or write position in the buffer.
70 */
71 protected int pos;
72
73
74 /**
75 * This actually means different things depending on whether the
76 * packet is read or write. For read, it's the length of the
77 * payload (excluding the header). For write, it's the length of
78 * the packet as a whole (counting the header). Oh, well.
79 */
80 protected int len;
81
82
83 // --------------------------------------------------------- Public Methods
84
85
86 /**
87 * Prepare this packet for accumulating a message from the container to
88 * the web server. Set the write position to just after the header
89 * (but leave the length unwritten, because it is as yet unknown).
90 */
91 public void reset() {
92 len = 4;
93 pos = 4;
94 }
95
96
97 /**
98 * For a packet to be sent to the web server, finish the process of
99 * accumulating data and write the length of the data payload into
100 * the header.
101 */
102 public void end() {
103 len = pos;
104 int dLen = len - 4;
105
106 buf[0] = (byte) 0x41;
107 buf[1] = (byte) 0x42;
108 buf[2] = (byte) ((dLen>>>8) & 0xFF);
109 buf[3] = (byte) (dLen & 0xFF);
110 }
111
112
113 /**
114 * Return the underlying byte buffer.
115 */
116 public byte[] getBuffer() {
117 return buf;
118 }
119
120
121 /**
122 * Return the current message length. For read, it's the length of the
123 * payload (excluding the header). For write, it's the length of
124 * the packet as a whole (counting the header).
125 */
126 public int getLen() {
127 return len;
128 }
129
130
131 /**
132 * Add a short integer (2 bytes) to the message.
133 */
134 public void appendInt(int val) {
135 buf[pos++] = (byte) ((val >>> 8) & 0xFF);
136 buf[pos++] = (byte) (val & 0xFF);
137 }
138
139
140 /**
141 * Append a byte (1 byte) to the message.
142 */
143 public void appendByte(int val) {
144 buf[pos++] = (byte) val;
145 }
146
147
148 /**
149 * Append an int (4 bytes) to the message.
150 */
151 public void appendLongInt(int val) {
152 buf[pos++] = (byte) ((val >>> 24) & 0xFF);
153 buf[pos++] = (byte) ((val >>> 16) & 0xFF);
154 buf[pos++] = (byte) ((val >>> 8) & 0xFF);
155 buf[pos++] = (byte) (val & 0xFF);
156 }
157
158
159 /**
160 * Write a MessageBytes out at the current write position.
161 * A null MessageBytes is encoded as a string with length 0.
162 */
163 public void appendBytes(MessageBytes mb) {
164 if (mb == null) {
165 log.error(sm.getString("ajpmessage.null"),
166 new NullPointerException());
167 appendInt(0);
168 appendByte(0);
169 return;
170 }
171 if (mb.getType() == MessageBytes.T_BYTES) {
172 ByteChunk bc = mb.getByteChunk();
173 appendByteChunk(bc);
174 } else if (mb.getType() == MessageBytes.T_CHARS) {
175 CharChunk cc = mb.getCharChunk();
176 appendCharChunk(cc);
177 } else {
178 appendString(mb.toString());
179 }
180 }
181
182
183 /**
184 * Write a ByteChunk out at the current write position.
185 * A null ByteChunk is encoded as a string with length 0.
186 */
187 public void appendByteChunk(ByteChunk bc) {
188 if (bc == null) {
189 log.error(sm.getString("ajpmessage.null"),
190 new NullPointerException());
191 appendInt(0);
192 appendByte(0);
193 return;
194 }
195 appendBytes(bc.getBytes(), bc.getStart(), bc.getLength());
196 }
197
198
199 /**
200 * Write a CharChunk out at the current write position.
201 * A null CharChunk is encoded as a string with length 0.
202 */
203 public void appendCharChunk(CharChunk cc) {
204 if (cc == null) {
205 log.error(sm.getString("ajpmessage.null"),
206 new NullPointerException());
207 appendInt(0);
208 appendByte(0);
209 return;
210 }
211 int start = cc.getStart();
212 int end = cc.getEnd();
213 appendInt(end - start);
214 char[] cbuf = cc.getBuffer();
215 for (int i = start; i < end; i++) {
216 char c = cbuf[i];
217 // Note: This is clearly incorrect for many strings,
218 // but is the only consistent approach within the current
219 // servlet framework. It must suffice until servlet output
220 // streams properly encode their output.
221 if ((c <= 31) && (c != 9)) {
222 c = ' ';
223 } else if (c == 127) {
224 c = ' ';
225 }
226 appendByte(c);
227 }
228 appendByte(0);
229 }
230
231
232 /**
233 * Write a String out at the current write position. Strings are
234 * encoded with the length in two bytes first, then the string, and
235 * then a terminating \0 (which is <B>not</B> included in the
236 * encoded length). The terminator is for the convenience of the C
237 * code, where it saves a round of copying. A null string is
238 * encoded as a string with length 0.
239 */
240 public void appendString(String str) {
241 if (str == null) {
242 log.error(sm.getString("ajpmessage.null"),
243 new NullPointerException());
244 appendInt(0);
245 appendByte(0);
246 return;
247 }
248 int len = str.length();
249 appendInt(len);
250 for (int i = 0; i < len; i++) {
251 char c = str.charAt (i);
252 // Note: This is clearly incorrect for many strings,
253 // but is the only consistent approach within the current
254 // servlet framework. It must suffice until servlet output
255 // streams properly encode their output.
256 if ((c <= 31) && (c != 9)) {
257 c = ' ';
258 } else if (c == 127) {
259 c = ' ';
260 }
261 appendByte(c);
262 }
263 appendByte(0);
264 }
265
266
267 /**
268 * Copy a chunk of bytes into the packet, starting at the current
269 * write position. The chunk of bytes is encoded with the length
270 * in two bytes first, then the data itself, and finally a
271 * terminating \0 (which is <B>not</B> included in the encoded
272 * length).
273 *
274 * @param b The array from which to copy bytes.
275 * @param off The offset into the array at which to start copying
276 * @param numBytes The number of bytes to copy.
277 */
278 public void appendBytes(byte[] b, int off, int numBytes) {
279 if (pos + numBytes + 3 > buf.length) {
280 log.error(sm.getString("ajpmessage.overflow", "" + numBytes, "" + pos),
281 new ArrayIndexOutOfBoundsException());
282 if (log.isDebugEnabled()) {
283 dump("Overflow/coBytes");
284 }
285 return;
286 }
287 appendInt(numBytes);
288 System.arraycopy(b, off, buf, pos, numBytes);
289 pos += numBytes;
290 appendByte(0);
291 }
292
293
294 /**
295 * Read an integer from packet, and advance the read position past
296 * it. Integers are encoded as two unsigned bytes with the
297 * high-order byte first, and, as far as I can tell, in
298 * little-endian order within each byte.
299 */
300 public int getInt() {
301 int b1 = buf[pos++] & 0xFF;
302 int b2 = buf[pos++] & 0xFF;
303 return (b1<<8) + b2;
304 }
305
306
307 public int peekInt() {
308 int b1 = buf[pos] & 0xFF;
309 int b2 = buf[pos+1] & 0xFF;
310 return (b1<<8) + b2;
311 }
312
313
314 public byte getByte() {
315 byte res = buf[pos++];
316 return res;
317 }
318
319
320 public byte peekByte() {
321 byte res = buf[pos];
322 return res;
323 }
324
325
326 public void getBytes(MessageBytes mb) {
327 int length = getInt();
328 if ((length == 0xFFFF) || (length == -1)) {
329 mb.recycle();
330 return;
331 }
332 mb.setBytes(buf, pos, length);
333 pos += length;
334 pos++; // Skip the terminating \0
335 }
336
337
338 /**
339 * Copy a chunk of bytes from the packet into an array and advance
340 * the read position past the chunk. See appendBytes() for details
341 * on the encoding.
342 *
343 * @return The number of bytes copied.
344 */
345 public int getBytes(byte[] dest) {
346 int length = getInt();
347 if (pos + length > buf.length) {
348 log.error(sm.getString("ajpmessage.read", "" + length));
349 return 0;
350 }
351
352 if ((length == 0xFFFF) || (length == -1)) {
353 return 0;
354 }
355
356 System.arraycopy(buf, pos, dest, 0, length);
357 pos += length;
358 pos++; // Skip terminating \0
359 return length;
360 }
361
362
363 /**
364 * Read a 32 bits integer from packet, and advance the read position past
365 * it. Integers are encoded as four unsigned bytes with the
366 * high-order byte first, and, as far as I can tell, in
367 * little-endian order within each byte.
368 */
369 public int getLongInt() {
370 int b1 = buf[pos++] & 0xFF; // No swap, Java order
371 b1 <<= 8;
372 b1 |= (buf[pos++] & 0xFF);
373 b1 <<= 8;
374 b1 |= (buf[pos++] & 0xFF);
375 b1 <<=8;
376 b1 |= (buf[pos++] & 0xFF);
377 return b1;
378 }
379
380
381 public int getHeaderLength() {
382 return 4;
383 }
384
385
386 public int getPacketSize() {
387 return buf.length;
388 }
389
390
391 public int processHeader() {
392 pos = 0;
393 int mark = getInt();
394 len = getInt();
395 // Verify message signature
396 if ((mark != 0x1234) && (mark != 0x4142)) {
397 log.error(sm.getString("ajpmessage.invalid", "" + mark));
398 if (log.isDebugEnabled()) {
399 dump("In: ");
400 }
401 return -1;
402 }
403 if (log.isDebugEnabled()) {
404 log.debug("Received " + len + " " + buf[0]);
405 }
406 return len;
407 }
408
409
410 /**
411 * Dump the contents of the message, prefixed with the given String.
412 */
413 public void dump(String msg) {
414 if (log.isDebugEnabled()) {
415 log.debug(msg + ": " + buf + " " + pos +"/" + (len + 4));
416 }
417 int max = pos;
418 if (len + 4 > pos)
419 max = len+4;
420 if (max > 1000)
421 max = 1000;
422 if (log.isDebugEnabled()) {
423 for (int j = 0; j < max; j += 16) {
424 log.debug(hexLine(buf, j, len));
425 }
426 }
427 }
428
429
430 // ------------------------------------------------------ Protected Methods
431
432
433 protected static String hexLine(byte buf[], int start, int len) {
434 StringBuffer sb = new StringBuffer();
435 for (int i = start; i < start + 16 ; i++) {
436 if (i < len + 4) {
437 sb.append(hex(buf[i]) + " ");
438 } else {
439 sb.append(" ");
440 }
441 }
442 sb.append(" | ");
443 for (int i = start; i < start + 16 && i < len + 4; i++) {
444 if (!Character.isISOControl((char) buf[i])) {
445 sb.append(new Character((char) buf[i]));
446 } else {
447 sb.append(".");
448 }
449 }
450 return sb.toString();
451 }
452
453
454 protected static String hex(int x) {
455 String h = Integer.toHexString(x);
456 if (h.length() == 1) {
457 h = "0" + h;
458 }
459 return h.substring(h.length() - 2);
460 }
461
462
463 }