Source code: org/apache/ajp/RequestHandler.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.ByteArrayInputStream;
20 import java.io.IOException;
21 import java.security.cert.CertificateFactory;
22 import java.security.cert.X509Certificate;
23
24 import org.apache.tomcat.util.buf.ByteChunk;
25 import org.apache.tomcat.util.buf.MessageBytes;
26 import org.apache.tomcat.util.http.BaseRequest;
27 import org.apache.tomcat.util.http.HttpMessages;
28 import org.apache.tomcat.util.http.MimeHeaders;
29
30
31 /**
32 * Handle messages related with basic request information.
33 *
34 * This object can handle the following incoming messages:
35 * - "FORWARD_REQUEST" input message ( sent when a request is passed from the web server )
36 * - "PING REQUEST" input message (sent by the web server to determine if tomcat is not frozen,
37 * a PONG REPLY will be sent back)
38 * - "RECEIVE_BODY_CHUNK" input ( sent by container to pass more body, in response to GET_BODY_CHUNK )
39 *
40 * It can handle the following outgoing messages:
41 * - SEND_HEADERS. Pass the status code and headers.
42 * - SEND_BODY_CHUNK. Send a chunk of body
43 * - GET_BODY_CHUNK. Request a chunk of body data
44 * - END_RESPONSE. Notify the end of a request processing.
45 *
46 * @author Henri Gomez [hgomez@apache.org]
47 * @author Dan Milstein [danmil@shore.net]
48 * @author Keith Wannamaker [Keith@Wannamaker.org]
49 * @author Costin Manolache
50 */
51 public class RequestHandler extends AjpHandler
52 {
53 // XXX Will move to a registry system.
54
55 // Prefix codes for message types from server to container
56 public static final byte JK_AJP13_FORWARD_REQUEST = 2;
57 public static final byte JK_AJP13_SHUTDOWN = 7;
58 public static final byte JK_AJP13_PING_REQUEST = 8;
59 public static final byte JK_AJP13_CPING_REQUEST = 10;
60
61 // Prefix codes for message types from container to server
62 public static final byte JK_AJP13_SEND_BODY_CHUNK = 3;
63 public static final byte JK_AJP13_SEND_HEADERS = 4;
64 public static final byte JK_AJP13_END_RESPONSE = 5;
65 public static final byte JK_AJP13_GET_BODY_CHUNK = 6;
66 public static final byte JK_AJP13_CPONG_REPLY = 9;
67
68 // Integer codes for common response header strings
69 public static final int SC_RESP_CONTENT_TYPE = 0xA001;
70 public static final int SC_RESP_CONTENT_LANGUAGE = 0xA002;
71 public static final int SC_RESP_CONTENT_LENGTH = 0xA003;
72 public static final int SC_RESP_DATE = 0xA004;
73 public static final int SC_RESP_LAST_MODIFIED = 0xA005;
74 public static final int SC_RESP_LOCATION = 0xA006;
75 public static final int SC_RESP_SET_COOKIE = 0xA007;
76 public static final int SC_RESP_SET_COOKIE2 = 0xA008;
77 public static final int SC_RESP_SERVLET_ENGINE = 0xA009;
78 public static final int SC_RESP_STATUS = 0xA00A;
79 public static final int SC_RESP_WWW_AUTHENTICATE = 0xA00B;
80
81 // Integer codes for common (optional) request attribute names
82 public static final byte SC_A_CONTEXT = 1; // XXX Unused
83 public static final byte SC_A_SERVLET_PATH = 2; // XXX Unused
84 public static final byte SC_A_REMOTE_USER = 3;
85 public static final byte SC_A_AUTH_TYPE = 4;
86 public static final byte SC_A_QUERY_STRING = 5;
87 public static final byte SC_A_JVM_ROUTE = 6;
88 public static final byte SC_A_SSL_CERT = 7;
89 public static final byte SC_A_SSL_CIPHER = 8;
90 public static final byte SC_A_SSL_SESSION = 9;
91 public static final byte SC_A_SSL_KEY_SIZE = 11; // ajp14 originally, now in ajp13 with jk 1.2/2.0
92 public static final byte SC_A_SECRET = 12;
93 public static final byte SC_A_STORED_METHOD = 13;
94
95 // Used for attributes which are not in the list above
96 public static final byte SC_A_REQ_ATTRIBUTE = 10;
97
98 // Terminates list of attributes
99 public static final byte SC_A_ARE_DONE = (byte)0xFF;
100
101 // Translates integer codes to names of HTTP methods
102 public static final String []methodTransArray = {
103 "OPTIONS",
104 "GET",
105 "HEAD",
106 "POST",
107 "PUT",
108 "DELETE",
109 "TRACE",
110 "PROPFIND",
111 "PROPPATCH",
112 "MKCOL",
113 "COPY",
114 "MOVE",
115 "LOCK",
116 "UNLOCK",
117 "ACL",
118 "REPORT",
119 "VERSION-CONTROL",
120 "CHECKIN",
121 "CHECKOUT",
122 "UNCHECKOUT",
123 "SEARCH",
124 "MKWORKSPACE",
125 "UPDATE",
126 "LABEL",
127 "MERGE",
128 "BASELINE-CONTROL",
129 "MKACTIVITY"
130 };
131 public static final int SC_M_JK_STORED = (byte) 0xFF;
132
133
134 // id's for common request headers
135 public static final int SC_REQ_ACCEPT = 1;
136 public static final int SC_REQ_ACCEPT_CHARSET = 2;
137 public static final int SC_REQ_ACCEPT_ENCODING = 3;
138 public static final int SC_REQ_ACCEPT_LANGUAGE = 4;
139 public static final int SC_REQ_AUTHORIZATION = 5;
140 public static final int SC_REQ_CONNECTION = 6;
141 public static final int SC_REQ_CONTENT_TYPE = 7;
142 public static final int SC_REQ_CONTENT_LENGTH = 8;
143 public static final int SC_REQ_COOKIE = 9;
144 public static final int SC_REQ_COOKIE2 = 10;
145 public static final int SC_REQ_HOST = 11;
146 public static final int SC_REQ_PRAGMA = 12;
147 public static final int SC_REQ_REFERER = 13;
148 public static final int SC_REQ_USER_AGENT = 14;
149
150 // Translates integer codes to request header names
151 public static final String []headerTransArray = {
152 "accept",
153 "accept-charset",
154 "accept-encoding",
155 "accept-language",
156 "authorization",
157 "connection",
158 "content-type",
159 "content-length",
160 "cookie",
161 "cookie2",
162 "host",
163 "pragma",
164 "referer",
165 "user-agent"
166 };
167
168 public RequestHandler()
169 {
170 }
171
172 public void init( Ajp13 ajp14 ) {
173 // register incoming message handlers
174 ajp14.registerMessageType( JK_AJP13_FORWARD_REQUEST,
175 "JK_AJP13_FORWARD_REQUEST",
176 this, null); // 2
177 // register outgoing messages handler
178 ajp14.registerMessageType( JK_AJP13_SEND_BODY_CHUNK, // 3
179 "JK_AJP13_SEND_BODY_CHUNK",
180 this,null );
181 ajp14.registerMessageType( JK_AJP13_SEND_HEADERS, // 4
182 "JK_AJP13_SEND_HEADERS",
183 this,null );
184 ajp14.registerMessageType( JK_AJP13_END_RESPONSE, // 5
185 "JK_AJP13_END_RESPONSE",
186 this,null );
187 ajp14.registerMessageType( JK_AJP13_GET_BODY_CHUNK, // 6
188 "JK_AJP13_GET_BODY_CHUNK",
189 this, null );
190 ajp14.registerMessageType( JK_AJP13_CPING_REQUEST,
191 "JK_AJP13_PING_REQUEST",
192 this, null); // 10
193 ajp14.registerMessageType( JK_AJP13_CPONG_REPLY,
194 "JK_AJP13_PONG_REPLY",
195 this, null); // 9
196 }
197
198 /**
199 * Send a CPONG REPLY to web server to its CPING request
200 *
201 * @param ch the Ajp13 channel
202 * @param outBuf the Ajp13Packet output packet to use
203 */
204 public int sendCPong(Ajp13 ch, Ajp13Packet outBuf)
205 {
206 outBuf.reset();
207 outBuf.appendByte(JK_AJP13_CPONG_REPLY);
208
209 try
210 {
211 ch.send(outBuf);
212 }
213 catch (IOException ioe)
214 {
215 log("can't send pong reply");
216 }
217
218 return (999); // success but no need to process farther
219 }
220
221 // -------------------- Incoming message --------------------
222 public int handleAjpMessage( int type, Ajp13 channel,
223 Ajp13Packet ajp, BaseRequest req )
224 throws IOException
225 {
226 switch( type ) {
227 case RequestHandler.JK_AJP13_FORWARD_REQUEST:
228 return decodeRequest(channel, channel.hBuf, req );
229
230 default:
231 return UNKNOWN;
232 }
233 }
234
235 /**
236 * Parse a FORWARD_REQUEST packet from the web server and store its
237 * properties in the passed-in request object.
238 *
239 * @param req An empty (newly-recycled) request object.
240 * @param msg Holds the packet which has just been sent by the web
241 * server, with its read position just past the packet header (which in
242 * this case includes the prefix code for FORWARD_REQUEST).
243 *
244 * @return 200 in case of a successful decoduing, 500 in case of error.
245 */
246 protected int decodeRequest(Ajp13 ch, Ajp13Packet msg, BaseRequest req)
247 throws IOException
248 {
249
250 if (debug > 0) {
251 log("decodeRequest()");
252 }
253
254 // XXX Awful return values
255
256 boolean isSSL = false;
257
258 // Translate the HTTP method code to a String.
259 byte methodCode = msg.getByte();
260 if (methodCode != SC_M_JK_STORED)
261 req.method().setString(methodTransArray[(int)methodCode - 1]);
262
263 msg.getMessageBytes(req.protocol());
264 msg.getMessageBytes(req.requestURI());
265
266 msg.getMessageBytes(req.remoteAddr());
267 msg.getMessageBytes(req.remoteHost());
268 msg.getMessageBytes(req.serverName());
269 req.setServerPort(msg.getInt());
270
271 isSSL = msg.getBool();
272
273 // Decode headers
274 MimeHeaders headers = req.headers();
275 int hCount = msg.getInt();
276 for(int i = 0 ; i < hCount ; i++) {
277 String hName = null;
278
279 // Header names are encoded as either an integer code starting
280 // with 0xA0, or as a normal string (in which case the first
281 // two bytes are the length).
282 int isc = msg.peekInt();
283 int hId = isc & 0xFF;
284
285 MessageBytes vMB=null;
286 isc &= 0xFF00;
287 if(0xA000 == isc) {
288 //
289 // header name is encoded as an int
290 //
291 msg.getInt(); // To advance the read position
292 hName = headerTransArray[hId - 1];
293 vMB= headers.addValue(hName);
294 msg.getMessageBytes(vMB);
295
296 if (hId == SC_REQ_CONTENT_LENGTH) {
297 // just read content-length header
298 int contentLength = (vMB == null) ? -1 : vMB.getInt();
299 req.setContentLength(contentLength);
300 } else if (hId == SC_REQ_CONTENT_TYPE) {
301 // just read content-type header
302 ByteChunk bchunk = vMB.getByteChunk();
303 req.contentType().setBytes(bchunk.getBytes(),
304 bchunk.getOffset(),
305 bchunk.getLength());
306 } else if (hId == SC_REQ_AUTHORIZATION) {
307 ByteChunk bchunk = vMB.getByteChunk();
308 req.authorization().setBytes(bchunk.getBytes(),
309 bchunk.getOffset(),
310 bchunk.getLength());
311 }
312 } else {
313 //
314 // header name is a string
315 //
316 // XXX Not very elegant
317 vMB = msg.addHeader(headers);
318 if (vMB == null) {
319 return 500; // wrong packet
320 }
321 msg.getMessageBytes(vMB);
322 }
323 }
324
325 byte attributeCode;
326 for(attributeCode = msg.getByte() ;
327 attributeCode != SC_A_ARE_DONE ;
328 attributeCode = msg.getByte()) {
329 switch(attributeCode) {
330 case SC_A_CONTEXT :
331 break;
332
333 case SC_A_SERVLET_PATH :
334 break;
335
336 case SC_A_REMOTE_USER :
337 msg.getMessageBytes(req.remoteUser());
338 break;
339
340 case SC_A_AUTH_TYPE :
341 msg.getMessageBytes(req.authType());
342 break;
343
344 case SC_A_QUERY_STRING :
345 msg.getMessageBytes(req.queryString());
346 break;
347
348 case SC_A_JVM_ROUTE :
349 msg.getMessageBytes(req.jvmRoute());
350 break;
351
352 case SC_A_SSL_CERT :
353 isSSL = true;
354 // Transform the string into certificate.
355 String certString = msg.getString();
356 byte[] certData = certString.getBytes();
357 ByteArrayInputStream bais = new ByteArrayInputStream(certData);
358
359 // Fill the first element.
360 X509Certificate jsseCerts[] = null;
361 try {
362 CertificateFactory cf =
363 CertificateFactory.getInstance("X.509");
364 X509Certificate cert = (X509Certificate)
365 cf.generateCertificate(bais);
366 jsseCerts = new X509Certificate[1];
367 jsseCerts[0] = cert;
368 } catch(java.security.cert.CertificateException e) {
369 log("Certificate convertion failed" + e );
370 }
371
372 req.setAttribute("javax.servlet.request.X509Certificate",
373 jsseCerts);
374 break;
375
376 case SC_A_SSL_CIPHER :
377 isSSL = true;
378 req.setAttribute("javax.servlet.request.cipher_suite",
379 msg.getString());
380 break;
381
382 case SC_A_SECRET :
383 // If a request has a secret attribute, set it on
384 // channel - it'll be visible to the caller ( Interceptor,
385 // Connector ) and it can check it against its settings before
386 // trusting us.
387 String secret=msg.getString();
388 if(secret!=null) {
389 ch.setSecret( secret );
390 }
391 break;
392
393 case SC_A_SSL_SESSION :
394 isSSL = true;
395 req.setAttribute("javax.servlet.request.ssl_session",
396 msg.getString());
397 break;
398
399 case SC_A_REQ_ATTRIBUTE :
400 req.setAttribute(msg.getString(),
401 msg.getString());
402 break;
403
404 case SC_A_SSL_KEY_SIZE: // Ajp13 !
405 isSSL = true;
406 req.setAttribute("javax.servlet.request.key_size",
407 Integer.toString(msg.getInt()));
408 break;
409
410 case SC_A_STORED_METHOD:
411 req.method().setString(msg.getString());
412 break;
413
414 default:
415 // Ignore. Assume a single-string value - we shouldn't
416 // allow anything else.
417 msg.getString();
418 break;
419 }
420 }
421
422 if(isSSL) {
423 req.setScheme(req.SCHEME_HTTPS);
424 req.setSecure(true);
425 }
426
427 // set cookies on request now that we have all headers
428 req.cookies().setHeaders(req.headers());
429
430 // Check to see if there should be a body packet coming along
431 // immediately after
432 if(req.getContentLength() > 0) {
433
434 /* Read present data */
435 int err = ch.receive(ch.inBuf);
436 if(err < 0) {
437 return 500;
438 }
439
440 ch.blen = ch.inBuf.peekInt();
441 ch.pos = 0;
442 ch.inBuf.getBytes(ch.bodyBuff);
443 }
444
445 if (debug > 5) {
446 log(req.toString());
447 }
448
449 return 200; // Success
450 }
451
452
453 // -------------------- Messages from container to server ------------------
454
455 /**
456 * Send the HTTP headers back to the web server and on to the browser.
457 *
458 * @param status The HTTP status code to send.
459 * @param statusMessage the HTTP status message to send.
460 * @param headers The set of all headers.
461 */
462 public void sendHeaders(Ajp13 ch, Ajp13Packet outBuf,
463 int status, String statusMessage,
464 MimeHeaders headers)
465 throws IOException
466 {
467 // XXX if more headers that MAX_SIZE, send 2 packets!
468 if( statusMessage==null ) statusMessage=HttpMessages.getMessage(status);
469 outBuf.reset();
470 outBuf.appendByte(JK_AJP13_SEND_HEADERS);
471 outBuf.appendInt(status);
472
473 outBuf.appendString(statusMessage);
474
475 int numHeaders = headers.size();
476 outBuf.appendInt(numHeaders);
477
478 for( int i=0 ; i < numHeaders ; i++ ) {
479 String headerName = headers.getName(i).toString();
480 int sc = headerNameToSc(headerName);
481 if(-1 != sc) {
482 outBuf.appendInt(sc);
483 } else {
484 outBuf.appendString(headerName);
485 }
486 outBuf.appendString(headers.getValue(i).toString() );
487 }
488
489 outBuf.end();
490 ch.send(outBuf);
491 }
492
493
494 /**
495 * Signal the web server that the servlet has finished handling this
496 * request, and that the connection can be reused.
497 */
498 public void finish(Ajp13 ch, Ajp13Packet outBuf) throws IOException {
499 if (debug > 0) log("finish()");
500
501 outBuf.reset();
502 outBuf.appendByte(JK_AJP13_END_RESPONSE);
503 outBuf.appendBool(true); // Reuse this connection
504 outBuf.end();
505 ch.send(outBuf);
506 }
507
508 /**
509 * Send a chunk of response body data to the web server and on to the
510 * browser.
511 *
512 * @param b A huffer of bytes to send.
513 * @param off The offset into the buffer from which to start sending.
514 * @param len The number of bytes to send.
515 */
516 public void doWrite(Ajp13 ch, Ajp13Packet outBuf,
517 byte b[], int off, int len)
518 throws IOException
519 {
520 if (debug > 0) log("doWrite(byte[], " + off + ", " + len + ")");
521
522 int sent = 0;
523 while(sent < len) {
524 int to_send = len - sent;
525 to_send = to_send > Ajp13.MAX_SEND_SIZE ? Ajp13.MAX_SEND_SIZE : to_send;
526
527 outBuf.reset();
528 outBuf.appendByte(JK_AJP13_SEND_BODY_CHUNK);
529 outBuf.appendBytes(b, off + sent, to_send);
530 ch.send(outBuf);
531 sent += to_send;
532 }
533 }
534
535 // -------------------- Utils --------------------
536
537 /**
538 * Translate an HTTP response header name to an integer code if
539 * possible. Case is ignored.
540 *
541 * @param name The name of the response header to translate.
542 *
543 * @return The code for that header name, or -1 if no code exists.
544 */
545 protected int headerNameToSc(String name)
546 {
547 switch(name.charAt(0)) {
548 case 'c':
549 case 'C':
550 if(name.equalsIgnoreCase("Content-Type")) {
551 return SC_RESP_CONTENT_TYPE;
552 } else if(name.equalsIgnoreCase("Content-Language")) {
553 return SC_RESP_CONTENT_LANGUAGE;
554 } else if(name.equalsIgnoreCase("Content-Length")) {
555 return SC_RESP_CONTENT_LENGTH;
556 }
557 break;
558
559 case 'd':
560 case 'D':
561 if(name.equalsIgnoreCase("Date")) {
562 return SC_RESP_DATE;
563 }
564 break;
565
566 case 'l':
567 case 'L':
568 if(name.equalsIgnoreCase("Last-Modified")) {
569 return SC_RESP_LAST_MODIFIED;
570 } else if(name.equalsIgnoreCase("Location")) {
571 return SC_RESP_LOCATION;
572 }
573 break;
574
575 case 's':
576 case 'S':
577 if(name.equalsIgnoreCase("Set-Cookie")) {
578 return SC_RESP_SET_COOKIE;
579 } else if(name.equalsIgnoreCase("Set-Cookie2")) {
580 return SC_RESP_SET_COOKIE2;
581 }
582 break;
583
584 case 'w':
585 case 'W':
586 if(name.equalsIgnoreCase("WWW-Authenticate")) {
587 return SC_RESP_WWW_AUTHENTICATE;
588 }
589 break;
590 }
591
592 return -1;
593 }
594
595 private int debug=0;
596 private Logger logger = new Logger();
597
598 public void setDebug(int debug) {
599 this.debug = debug;
600 }
601
602 public void setLogger(Logger l) {
603 this.logger = l;
604 }
605
606 void log(String s) {
607 logger.log("[RequestHandler] " + s );
608 }
609
610 // ==================== Servlet Input Support =================
611 // XXX DEPRECATED
612
613 public int available(Ajp13 ch) throws IOException {
614 if (debug > 0) {
615 log("available()");
616 }
617
618 if (ch.pos >= ch.blen) {
619 if( ! refillReadBuffer(ch)) {
620 return 0;
621 }
622 }
623 return ch.blen - ch.pos;
624 }
625
626 /**
627 * Return the next byte of request body data (to a servlet).
628 */
629 public int doRead(Ajp13 ch) throws IOException
630 {
631 if (debug > 0) {
632 log("doRead()");
633 }
634
635 if(ch.pos >= ch.blen) {
636 if( ! refillReadBuffer(ch)) {
637 return -1;
638 }
639 }
640 return ch.bodyBuff[ch.pos++] & 0xFF;
641 }
642
643 /**
644 * Store a chunk of request data into the passed-in byte buffer.
645 *
646 * @param b A buffer to fill with data from the request.
647 * @param off The offset in the buffer at which to start filling.
648 * @param len The number of bytes to copy into the buffer.
649 *
650 * @return The number of bytes actually copied into the buffer, or -1
651 * if the end of the stream has been reached.
652 */
653 public int doRead(Ajp13 ch, byte[] b, int off, int len) throws IOException
654 {
655 if (debug > 0) {
656 log("doRead(byte[], int, int)");
657 }
658
659 if(ch.pos >= ch.blen) {
660 if( ! refillReadBuffer(ch)) {
661 return -1;
662 }
663 }
664
665 if(ch.pos + len <= ch.blen) { // Fear the off by one error
666 // Sanity check b.length > off + len?
667 System.arraycopy(ch.bodyBuff, ch.pos, b, off, len);
668 ch.pos += len;
669 return len;
670 }
671
672 // Not enough data (blen < pos + len)
673 int toCopy = len;
674 while(toCopy > 0) {
675 int bytesRemaining = ch.blen - ch.pos;
676 if(bytesRemaining < 0)
677 bytesRemaining = 0;
678 int c = bytesRemaining < toCopy ? bytesRemaining : toCopy;
679
680 System.arraycopy(ch.bodyBuff, ch.pos, b, off, c);
681
682 toCopy -= c;
683
684 off += c;
685 ch.pos += c; // In case we exactly consume the buffer
686
687 if(toCopy > 0)
688 if( ! refillReadBuffer(ch)) { // Resets blen and pos
689 break;
690 }
691 }
692
693 return len - toCopy;
694 }
695
696 /**
697 * Get more request body data from the web server and store it in the
698 * internal buffer.
699 *
700 * @return true if there is more data, false if not.
701 */
702 public boolean refillReadBuffer(Ajp13 ch) throws IOException
703 {
704 if (debug > 0) {
705 log("refillReadBuffer()");
706 }
707
708 // If the server returns an empty packet, assume that that end of
709 // the stream has been reached (yuck -- fix protocol??).
710
711 // Why not use outBuf??
712 ch.inBuf.reset();
713 ch.inBuf.appendByte(JK_AJP13_GET_BODY_CHUNK);
714 ch.inBuf.appendInt(Ajp13.MAX_READ_SIZE);
715 ch.send(ch.inBuf);
716
717 int err = ch.receive(ch.inBuf);
718 if(err < 0) {
719 throw new IOException();
720 }
721
722 // check for empty packet, which means end of stream
723 if (ch.inBuf.getLen() == 0) {
724 if (debug > 0) {
725 log("refillReadBuffer(): "
726 + "received empty packet -> end of stream");
727 }
728 ch.blen = 0;
729 ch.pos = 0;
730 return false;
731 }
732
733 ch.blen = ch.inBuf.peekInt();
734 ch.pos = 0;
735 ch.inBuf.getBytes(ch.bodyBuff);
736
737 return (ch.blen > 0);
738 }
739
740 // ==================== Servlet Output Support =================
741
742 /**
743 */
744 public void beginSendHeaders(Ajp13 ch, Ajp13Packet outBuf,
745 int status,
746 String statusMessage,
747 int numHeaders) throws IOException {
748
749 if (debug > 0) {
750 log("sendHeaders()");
751 }
752
753 // XXX if more headers that MAX_SIZE, send 2 packets!
754
755 outBuf.reset();
756 outBuf.appendByte(JK_AJP13_SEND_HEADERS);
757
758 if (debug > 0) {
759 log("status is: " + status +
760 "(" + statusMessage + ")");
761 }
762
763 // set status code and message
764 outBuf.appendInt(status);
765 outBuf.appendString(statusMessage);
766
767 // write the number of headers...
768 outBuf.appendInt(numHeaders);
769 }
770
771 public void sendHeader(Ajp13Packet outBuf,
772 String name, String value)
773 throws IOException
774 {
775 int sc = headerNameToSc(name);
776 if(-1 != sc) {
777 outBuf.appendInt(sc);
778 } else {
779 outBuf.appendString(name);
780 }
781 outBuf.appendString(value);
782 }
783
784 public void endSendHeaders(Ajp13 ch, Ajp13Packet outBuf)
785 throws IOException
786 {
787 outBuf.end();
788 ch.send(outBuf);
789 }
790
791 /**
792 * Send the HTTP headers back to the web server and on to the browser.
793 *
794 * @param status The HTTP status code to send.
795 * @param headers The set of all headers.
796 */
797 public void sendHeaders(Ajp13 ch, Ajp13Packet outBuf,
798 int status, MimeHeaders headers)
799 throws IOException
800 {
801 sendHeaders(ch, outBuf, status, HttpMessages.getMessage(status),
802 headers);
803 }
804
805
806 }