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 java.io.ByteArrayInputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InterruptedIOException;
24 import java.io.OutputStream;
25 import java.net.InetAddress;
26 import java.net.Socket;
27 import java.security.cert.CertificateFactory;
28 import java.security.cert.X509Certificate;
29
30 import org.apache.coyote.ActionCode;
31 import org.apache.coyote.ActionHook;
32 import org.apache.coyote.Adapter;
33 import org.apache.coyote.InputBuffer;
34 import org.apache.coyote.OutputBuffer;
35 import org.apache.coyote.Request;
36 import org.apache.coyote.RequestInfo;
37 import org.apache.coyote.Response;
38 import org.apache.tomcat.util.buf.ByteChunk;
39 import org.apache.tomcat.util.buf.HexUtils;
40 import org.apache.tomcat.util.buf.MessageBytes;
41 import org.apache.tomcat.util.http.HttpMessages;
42 import org.apache.tomcat.util.http.MimeHeaders;
43 import org.apache.tomcat.util.net.JIoEndpoint;
44 import org.apache.tomcat.util.res.StringManager;
45
46
47 /**
48 * Processes HTTP requests.
49 *
50 * @author Remy Maucherat
51 * @author Henri Gomez
52 * @author Dan Milstein
53 * @author Keith Wannamaker
54 * @author Kevin Seguin
55 * @author Costin Manolache
56 * @author Bill Barker
57 */
58 public class AjpProcessor implements ActionHook {
59
60
61 /**
62 * Logger.
63 */
64 protected static org.apache.juli.logging.Log log
65 = org.apache.juli.logging.LogFactory.getLog(AjpProcessor.class);
66
67 /**
68 * The string manager for this package.
69 */
70 protected static StringManager sm =
71 StringManager.getManager(Constants.Package);
72
73
74 // ----------------------------------------------------------- Constructors
75
76
77 public AjpProcessor(int packetSize, JIoEndpoint endpoint) {
78
79 this.endpoint = endpoint;
80
81 request = new Request();
82 request.setInputBuffer(new SocketInputBuffer());
83
84 response = new Response();
85 response.setHook(this);
86 response.setOutputBuffer(new SocketOutputBuffer());
87 request.setResponse(response);
88
89 requestHeaderMessage = new AjpMessage(packetSize);
90 responseHeaderMessage = new AjpMessage(packetSize);
91 bodyMessage = new AjpMessage(packetSize);
92
93 // Cause loading of HexUtils
94 int foo = HexUtils.DEC[0];
95
96 // Cause loading of HttpMessages
97 HttpMessages.getMessage(200);
98
99 }
100
101
102 // ----------------------------------------------------- Instance Variables
103
104
105 /**
106 * Associated adapter.
107 */
108 protected Adapter adapter = null;
109
110
111 /**
112 * Request object.
113 */
114 protected Request request = null;
115
116
117 /**
118 * Response object.
119 */
120 protected Response response = null;
121
122
123 /**
124 * Header message. Note that this header is merely the one used during the
125 * processing of the first message of a "request", so it might not be a request
126 * header. It will stay unchanged during the processing of the whole request.
127 */
128 protected AjpMessage requestHeaderMessage = null;
129
130
131 /**
132 * Message used for response header composition.
133 */
134 protected AjpMessage responseHeaderMessage = null;
135
136
137 /**
138 * Body message.
139 */
140 protected AjpMessage bodyMessage = null;
141
142
143 /**
144 * Body message.
145 */
146 protected MessageBytes bodyBytes = MessageBytes.newInstance();
147
148
149 /**
150 * State flag.
151 */
152 protected boolean started = false;
153
154
155 /**
156 * Error flag.
157 */
158 protected boolean error = false;
159
160
161 /**
162 * Socket associated with the current connection.
163 */
164 protected Socket socket;
165
166
167 /**
168 * Input stream.
169 */
170 protected InputStream input;
171
172
173 /**
174 * Output stream.
175 */
176 protected OutputStream output;
177
178
179 /**
180 * Host name (used to avoid useless B2C conversion on the host name).
181 */
182 protected char[] hostNameC = new char[0];
183
184
185 /**
186 * Associated endpoint.
187 */
188 protected JIoEndpoint endpoint;
189
190
191 /**
192 * The socket timeout used when reading the first block of the request
193 * header.
194 */
195 protected long readTimeout;
196
197
198 /**
199 * Temp message bytes used for processing.
200 */
201 protected MessageBytes tmpMB = MessageBytes.newInstance();
202
203
204 /**
205 * Byte chunk for certs.
206 */
207 protected MessageBytes certificates = MessageBytes.newInstance();
208
209
210 /**
211 * End of stream flag.
212 */
213 protected boolean endOfStream = false;
214
215
216 /**
217 * Body empty flag.
218 */
219 protected boolean empty = true;
220
221
222 /**
223 * First read.
224 */
225 protected boolean first = true;
226
227
228 /**
229 * Replay read.
230 */
231 protected boolean replay = false;
232
233
234 /**
235 * Finished response.
236 */
237 protected boolean finished = false;
238
239
240 /**
241 * Direct buffer used for sending right away a get body message.
242 */
243 protected static final byte[] getBodyMessageArray;
244
245
246 /**
247 * Direct buffer used for sending right away a pong message.
248 */
249 protected static final byte[] pongMessageArray;
250
251
252 /**
253 * End message array.
254 */
255 protected static final byte[] endMessageArray;
256
257 /**
258 * Flush message array.
259 */
260 protected static final byte[] flushMessageArray;
261
262
263 // ----------------------------------------------------- Static Initializer
264
265
266 static {
267
268 // Set the get body message buffer
269
270 AjpMessage getBodyMessage = new AjpMessage(16);
271 getBodyMessage.reset();
272 getBodyMessage.appendByte(Constants.JK_AJP13_GET_BODY_CHUNK);
273 getBodyMessage.appendInt(Constants.MAX_READ_SIZE);
274 getBodyMessage.end();
275 getBodyMessageArray = new byte[getBodyMessage.getLen()];
276 System.arraycopy(getBodyMessage.getBuffer(), 0, getBodyMessageArray,
277 0, getBodyMessage.getLen());
278
279 // Set the read body message buffer
280 AjpMessage pongMessage = new AjpMessage(16);
281 pongMessage.reset();
282 pongMessage.appendByte(Constants.JK_AJP13_CPONG_REPLY);
283 pongMessage.end();
284 pongMessageArray = new byte[pongMessage.getLen()];
285 System.arraycopy(pongMessage.getBuffer(), 0, pongMessageArray,
286 0, pongMessage.getLen());
287
288 // Allocate the end message array
289 AjpMessage endMessage = new AjpMessage(16);
290 endMessage.reset();
291 endMessage.appendByte(Constants.JK_AJP13_END_RESPONSE);
292 endMessage.appendByte(1);
293 endMessage.end();
294 endMessageArray = new byte[endMessage.getLen()];
295 System.arraycopy(endMessage.getBuffer(), 0, endMessageArray, 0,
296 endMessage.getLen());
297
298 // Allocate the flush message array
299 AjpMessage flushMessage = new AjpMessage(16);
300 flushMessage.reset();
301 flushMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK);
302 flushMessage.appendInt(0);
303 flushMessage.appendByte(0);
304 flushMessage.end();
305 flushMessageArray = new byte[flushMessage.getLen()];
306 System.arraycopy(flushMessage.getBuffer(), 0, flushMessageArray, 0,
307 flushMessage.getLen());
308
309 }
310
311
312 // ------------------------------------------------------------- Properties
313
314
315 /**
316 * Use Tomcat authentication ?
317 */
318 protected boolean tomcatAuthentication = true;
319 public boolean getTomcatAuthentication() { return tomcatAuthentication; }
320 public void setTomcatAuthentication(boolean tomcatAuthentication) { this.tomcatAuthentication = tomcatAuthentication; }
321
322
323 /**
324 * Required secret.
325 */
326 protected String requiredSecret = null;
327 public void setRequiredSecret(String requiredSecret) { this.requiredSecret = requiredSecret; }
328
329
330 /**
331 * The number of milliseconds Tomcat will wait for a subsequent request
332 * before closing the connection. The default is the same as for
333 * Apache HTTP Server (15 000 milliseconds).
334 */
335 protected int keepAliveTimeout = -1;
336 public int getKeepAliveTimeout() { return keepAliveTimeout; }
337 public void setKeepAliveTimeout(int timeout) { keepAliveTimeout = timeout; }
338
339
340 // --------------------------------------------------------- Public Methods
341
342
343 /** Get the request associated with this processor.
344 *
345 * @return The request
346 */
347 public Request getRequest() {
348 return request;
349 }
350
351
352 /**
353 * Process pipelined HTTP requests using the specified input and output
354 * streams.
355 *
356 * @throws IOException error during an I/O operation
357 */
358 public void process(Socket socket)
359 throws IOException {
360 RequestInfo rp = request.getRequestProcessor();
361 rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
362
363 // Setting up the socket
364 this.socket = socket;
365 input = socket.getInputStream();
366 output = socket.getOutputStream();
367 int soTimeout = -1;
368 if (keepAliveTimeout > 0) {
369 soTimeout = socket.getSoTimeout();
370 }
371
372 // Error flag
373 error = false;
374
375 while (started && !error) {
376
377 // Parsing the request header
378 try {
379 // Set keep alive timeout if enabled
380 if (keepAliveTimeout > 0) {
381 socket.setSoTimeout(keepAliveTimeout);
382 }
383 // Get first message of the request
384 if (!readMessage(requestHeaderMessage)) {
385 // This means a connection timeout
386 rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
387 break;
388 }
389 // Set back timeout if keep alive timeout is enabled
390 if (keepAliveTimeout > 0) {
391 socket.setSoTimeout(soTimeout);
392 }
393 // Check message type, process right away and break if
394 // not regular request processing
395 int type = requestHeaderMessage.getByte();
396 if (type == Constants.JK_AJP13_CPING_REQUEST) {
397 try {
398 output.write(pongMessageArray);
399 } catch (IOException e) {
400 error = true;
401 }
402 continue;
403 } else if(type != Constants.JK_AJP13_FORWARD_REQUEST) {
404 // Usually the servlet didn't read the previous request body
405 if(log.isDebugEnabled()) {
406 log.debug("Unexpected message: "+type);
407 }
408 continue;
409 }
410
411 request.setStartTime(System.currentTimeMillis());
412 } catch (IOException e) {
413 error = true;
414 break;
415 } catch (Throwable t) {
416 log.debug(sm.getString("ajpprocessor.header.error"), t);
417 // 400 - Bad Request
418 response.setStatus(400);
419 error = true;
420 }
421
422 // Setting up filters, and parse some request headers
423 rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
424 try {
425 prepareRequest();
426 } catch (Throwable t) {
427 log.debug(sm.getString("ajpprocessor.request.prepare"), t);
428 // 400 - Internal Server Error
429 response.setStatus(400);
430 error = true;
431 }
432
433 // Process the request in the adapter
434 if (!error) {
435 try {
436 rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
437 adapter.service(request, response);
438 } catch (InterruptedIOException e) {
439 error = true;
440 } catch (Throwable t) {
441 log.error(sm.getString("ajpprocessor.request.process"), t);
442 // 500 - Internal Server Error
443 response.setStatus(500);
444 error = true;
445 }
446 }
447
448 // Finish the response if not done yet
449 if (!finished) {
450 try {
451 finish();
452 } catch (Throwable t) {
453 error = true;
454 }
455 }
456
457 // If there was an error, make sure the request is counted as
458 // and error, and update the statistics counter
459 if (error) {
460 response.setStatus(500);
461 }
462 request.updateCounters();
463
464 rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
465 recycle();
466
467 }
468
469 rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
470 recycle();
471 input = null;
472 output = null;
473
474 }
475
476
477 // ----------------------------------------------------- ActionHook Methods
478
479
480 /**
481 * Send an action to the connector.
482 *
483 * @param actionCode Type of the action
484 * @param param Action parameter
485 */
486 public void action(ActionCode actionCode, Object param) {
487
488 if (actionCode == ActionCode.ACTION_COMMIT) {
489
490 if (response.isCommitted())
491 return;
492
493 // Validate and write response headers
494 try {
495 prepareResponse();
496 } catch (IOException e) {
497 // Set error flag
498 error = true;
499 }
500
501 } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
502
503 if (!response.isCommitted()) {
504 // Validate and write response headers
505 try {
506 prepareResponse();
507 } catch (IOException e) {
508 // Set error flag
509 error = true;
510 return;
511 }
512 }
513
514 try {
515 flush();
516 } catch (IOException e) {
517 // Set error flag
518 error = true;
519 }
520
521 } else if (actionCode == ActionCode.ACTION_CLOSE) {
522 // Close
523
524 // End the processing of the current request, and stop any further
525 // transactions with the client
526
527 try {
528 finish();
529 } catch (IOException e) {
530 // Set error flag
531 error = true;
532 }
533
534 } else if (actionCode == ActionCode.ACTION_START) {
535
536 started = true;
537
538 } else if (actionCode == ActionCode.ACTION_STOP) {
539
540 started = false;
541
542 } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
543
544 if (!certificates.isNull()) {
545 ByteChunk certData = certificates.getByteChunk();
546 X509Certificate jsseCerts[] = null;
547 ByteArrayInputStream bais =
548 new ByteArrayInputStream(certData.getBytes(),
549 certData.getStart(),
550 certData.getLength());
551 // Fill the first element.
552 try {
553 CertificateFactory cf =
554 CertificateFactory.getInstance("X.509");
555 X509Certificate cert = (X509Certificate)
556 cf.generateCertificate(bais);
557 jsseCerts = new X509Certificate[1];
558 jsseCerts[0] = cert;
559 request.setAttribute(JIoEndpoint.CERTIFICATE_KEY, jsseCerts);
560 } catch (java.security.cert.CertificateException e) {
561 log.error(sm.getString("ajpprocessor.certs.fail"), e);
562 return;
563 }
564 }
565
566 } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
567
568 // Get remote host name using a DNS resolution
569 if (request.remoteHost().isNull()) {
570 try {
571 request.remoteHost().setString(InetAddress.getByName
572 (request.remoteAddr().toString()).getHostName());
573 } catch (IOException iex) {
574 // Ignore
575 }
576 }
577
578 } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
579
580 // Copy from local name for now, which should simply be an address
581 request.localAddr().setString(request.localName().toString());
582
583 } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
584
585 // Set the given bytes as the content
586 ByteChunk bc = (ByteChunk) param;
587 int length = bc.getLength();
588 bodyBytes.setBytes(bc.getBytes(), bc.getStart(), length);
589 request.setContentLength(length);
590 first = false;
591 empty = false;
592 replay = true;
593
594 }
595
596
597 }
598
599
600 // ------------------------------------------------------ Connector Methods
601
602
603 /**
604 * Set the associated adapter.
605 *
606 * @param adapter the new adapter
607 */
608 public void setAdapter(Adapter adapter) {
609 this.adapter = adapter;
610 }
611
612
613 /**
614 * Get the associated adapter.
615 *
616 * @return the associated adapter
617 */
618 public Adapter getAdapter() {
619 return adapter;
620 }
621
622
623 // ------------------------------------------------------ Protected Methods
624
625
626 /**
627 * After reading the request headers, we have to setup the request filters.
628 */
629 protected void prepareRequest() {
630
631 // Translate the HTTP method code to a String.
632 byte methodCode = requestHeaderMessage.getByte();
633 if (methodCode != Constants.SC_M_JK_STORED) {
634 String methodName = Constants.methodTransArray[(int)methodCode - 1];
635 request.method().setString(methodName);
636 }
637
638 requestHeaderMessage.getBytes(request.protocol());
639 requestHeaderMessage.getBytes(request.requestURI());
640
641 requestHeaderMessage.getBytes(request.remoteAddr());
642 requestHeaderMessage.getBytes(request.remoteHost());
643 requestHeaderMessage.getBytes(request.localName());
644 request.setLocalPort(requestHeaderMessage.getInt());
645
646 boolean isSSL = requestHeaderMessage.getByte() != 0;
647 if (isSSL) {
648 request.scheme().setString("https");
649 }
650
651 // Decode headers
652 MimeHeaders headers = request.getMimeHeaders();
653
654 int hCount = requestHeaderMessage.getInt();
655 for(int i = 0 ; i < hCount ; i++) {
656 String hName = null;
657
658 // Header names are encoded as either an integer code starting
659 // with 0xA0, or as a normal string (in which case the first
660 // two bytes are the length).
661 int isc = requestHeaderMessage.peekInt();
662 int hId = isc & 0xFF;
663
664 MessageBytes vMB = null;
665 isc &= 0xFF00;
666 if(0xA000 == isc) {
667 requestHeaderMessage.getInt(); // To advance the read position
668 hName = Constants.headerTransArray[hId - 1];
669 vMB = headers.addValue(hName);
670 } else {
671 // reset hId -- if the header currently being read
672 // happens to be 7 or 8 bytes long, the code below
673 // will think it's the content-type header or the
674 // content-length header - SC_REQ_CONTENT_TYPE=7,
675 // SC_REQ_CONTENT_LENGTH=8 - leading to unexpected
676 // behaviour. see bug 5861 for more information.
677 hId = -1;
678 requestHeaderMessage.getBytes(tmpMB);
679 ByteChunk bc = tmpMB.getByteChunk();
680 vMB = headers.addValue(bc.getBuffer(),
681 bc.getStart(), bc.getLength());
682 }
683
684 requestHeaderMessage.getBytes(vMB);
685
686 if (hId == Constants.SC_REQ_CONTENT_LENGTH ||
687 (hId == -1 && tmpMB.equalsIgnoreCase("Content-Length"))) {
688 // just read the content-length header, so set it
689 long cl = vMB.getLong();
690 if(cl < Integer.MAX_VALUE)
691 request.setContentLength( (int)cl );
692 } else if (hId == Constants.SC_REQ_CONTENT_TYPE ||
693 (hId == -1 && tmpMB.equalsIgnoreCase("Content-Type"))) {
694 // just read the content-type header, so set it
695 ByteChunk bchunk = vMB.getByteChunk();
696 request.contentType().setBytes(bchunk.getBytes(),
697 bchunk.getOffset(),
698 bchunk.getLength());
699 }
700 }
701
702 // Decode extra attributes
703 boolean secret = false;
704 byte attributeCode;
705 while ((attributeCode = requestHeaderMessage.getByte())
706 != Constants.SC_A_ARE_DONE) {
707
708 switch (attributeCode) {
709
710 case Constants.SC_A_REQ_ATTRIBUTE :
711 requestHeaderMessage.getBytes(tmpMB);
712 String n = tmpMB.toString();
713 requestHeaderMessage.getBytes(tmpMB);
714 String v = tmpMB.toString();
715 request.setAttribute(n, v);
716 break;
717
718 case Constants.SC_A_CONTEXT :
719 requestHeaderMessage.getBytes(tmpMB);
720 // nothing
721 break;
722
723 case Constants.SC_A_SERVLET_PATH :
724 requestHeaderMessage.getBytes(tmpMB);
725 // nothing
726 break;
727
728 case Constants.SC_A_REMOTE_USER :
729 if (tomcatAuthentication) {
730 // ignore server
731 requestHeaderMessage.getBytes(tmpMB);
732 } else {
733 requestHeaderMessage.getBytes(request.getRemoteUser());
734 }
735 break;
736
737 case Constants.SC_A_AUTH_TYPE :
738 if (tomcatAuthentication) {
739 // ignore server
740 requestHeaderMessage.getBytes(tmpMB);
741 } else {
742 requestHeaderMessage.getBytes(request.getAuthType());
743 }
744 break;
745
746 case Constants.SC_A_QUERY_STRING :
747 requestHeaderMessage.getBytes(request.queryString());
748 break;
749
750 case Constants.SC_A_JVM_ROUTE :
751 requestHeaderMessage.getBytes(request.instanceId());
752 break;
753
754 case Constants.SC_A_SSL_CERT :
755 request.scheme().setString("https");
756 // SSL certificate extraction is lazy, moved to JkCoyoteHandler
757 requestHeaderMessage.getBytes(certificates);
758 break;
759
760 case Constants.SC_A_SSL_CIPHER :
761 request.scheme().setString("https");
762 requestHeaderMessage.getBytes(tmpMB);
763 request.setAttribute(JIoEndpoint.CIPHER_SUITE_KEY,
764 tmpMB.toString());
765 break;
766
767 case Constants.SC_A_SSL_SESSION :
768 request.scheme().setString("https");
769 requestHeaderMessage.getBytes(tmpMB);
770 request.setAttribute(JIoEndpoint.SESSION_ID_KEY,
771 tmpMB.toString());
772 break;
773
774 case Constants.SC_A_SSL_KEY_SIZE :
775 request.setAttribute(JIoEndpoint.KEY_SIZE_KEY,
776 new Integer(requestHeaderMessage.getInt()));
777 break;
778
779 case Constants.SC_A_STORED_METHOD:
780 requestHeaderMessage.getBytes(request.method());
781 break;
782
783 case Constants.SC_A_SECRET:
784 requestHeaderMessage.getBytes(tmpMB);
785 if (requiredSecret != null) {
786 secret = true;
787 if (!tmpMB.equals(requiredSecret)) {
788 response.setStatus(403);
789 error = true;
790 }
791 }
792 break;
793
794 default:
795 // Ignore unknown attribute for backward compatibility
796 break;
797
798 }
799
800 }
801
802 // Check if secret was submitted if required
803 if ((requiredSecret != null) && !secret) {
804 response.setStatus(403);
805 error = true;
806 }
807
808 // Check for a full URI (including protocol://host:port/)
809 ByteChunk uriBC = request.requestURI().getByteChunk();
810 if (uriBC.startsWithIgnoreCase("http", 0)) {
811
812 int pos = uriBC.indexOf("://", 0, 3, 4);
813 int uriBCStart = uriBC.getStart();
814 int slashPos = -1;
815 if (pos != -1) {
816 byte[] uriB = uriBC.getBytes();
817 slashPos = uriBC.indexOf('/', pos + 3);
818 if (slashPos == -1) {
819 slashPos = uriBC.getLength();
820 // Set URI as "/"
821 request.requestURI().setBytes
822 (uriB, uriBCStart + pos + 1, 1);
823 } else {
824 request.requestURI().setBytes
825 (uriB, uriBCStart + slashPos,
826 uriBC.getLength() - slashPos);
827 }
828 MessageBytes hostMB = headers.setValue("host");
829 hostMB.setBytes(uriB, uriBCStart + pos + 3,
830 slashPos - pos - 3);
831 }
832
833 }
834
835 MessageBytes valueMB = request.getMimeHeaders().getValue("host");
836 parseHost(valueMB);
837
838 }
839
840
841 /**
842 * Parse host.
843 */
844 public void parseHost(MessageBytes valueMB) {
845
846 if (valueMB == null || (valueMB != null && valueMB.isNull()) ) {
847 // HTTP/1.0
848 // Default is what the socket tells us. Overriden if a host is
849 // found/parsed
850 request.setServerPort(endpoint.getPort());
851 return;
852 }
853
854 ByteChunk valueBC = valueMB.getByteChunk();
855 byte[] valueB = valueBC.getBytes();
856 int valueL = valueBC.getLength();
857 int valueS = valueBC.getStart();
858 int colonPos = -1;
859 if (hostNameC.length < valueL) {
860 hostNameC = new char[valueL];
861 }
862
863 boolean ipv6 = (valueB[valueS] == '[');
864 boolean bracketClosed = false;
865 for (int i = 0; i < valueL; i++) {
866 char b = (char) valueB[i + valueS];
867 hostNameC[i] = b;
868 if (b == ']') {
869 bracketClosed = true;
870 } else if (b == ':') {
871 if (!ipv6 || bracketClosed) {
872 colonPos = i;
873 break;
874 }
875 }
876 }
877
878 if (colonPos < 0) {
879 if (request.scheme().equalsIgnoreCase("https")) {
880 // 443 - Default HTTPS port
881 request.setServerPort(443);
882 } else {
883 // 80 - Default HTTTP port
884 request.setServerPort(80);
885 }
886 request.serverName().setChars(hostNameC, 0, valueL);
887 } else {
888
889 request.serverName().setChars(hostNameC, 0, colonPos);
890
891 int port = 0;
892 int mult = 1;
893 for (int i = valueL - 1; i > colonPos; i--) {
894 int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
895 if (charValue == -1) {
896 // Invalid character
897 error = true;
898 // 400 - Bad request
899 response.setStatus(400);
900 break;
901 }
902 port = port + (charValue * mult);
903 mult = 10 * mult;
904 }
905 request.setServerPort(port);
906
907 }
908
909 }
910
911
912 /**
913 * When committing the response, we have to validate the set of headers, as
914 * well as setup the response filters.
915 */
916 protected void prepareResponse()
917 throws IOException {
918
919 response.setCommitted(true);
920
921 responseHeaderMessage.reset();
922 responseHeaderMessage.appendByte(Constants.JK_AJP13_SEND_HEADERS);
923
924 // HTTP header contents
925 responseHeaderMessage.appendInt(response.getStatus());
926 String message = response.getMessage();
927 if (message == null){
928 message = HttpMessages.getMessage(response.getStatus());
929 } else {
930 message = message.replace('\n', ' ').replace('\r', ' ');
931 }
932 tmpMB.setString(message);
933 responseHeaderMessage.appendBytes(tmpMB);
934
935 // Special headers
936 MimeHeaders headers = response.getMimeHeaders();
937 String contentType = response.getContentType();
938 if (contentType != null) {
939 headers.setValue("Content-Type").setString(contentType);
940 }
941 String contentLanguage = response.getContentLanguage();
942 if (contentLanguage != null) {
943 headers.setValue("Content-Language").setString(contentLanguage);
944 }
945 long contentLength = response.getContentLengthLong();
946 if (contentLength >= 0) {
947 headers.setValue("Content-Length").setLong(contentLength);
948 }
949
950 // Other headers
951 int numHeaders = headers.size();
952 responseHeaderMessage.appendInt(numHeaders);
953 for (int i = 0; i < numHeaders; i++) {
954 MessageBytes hN = headers.getName(i);
955 int hC = Constants.getResponseAjpIndex(hN.toString());
956 if (hC > 0) {
957 responseHeaderMessage.appendInt(hC);
958 }
959 else {
960 responseHeaderMessage.appendBytes(hN);
961 }
962 MessageBytes hV=headers.getValue(i);
963 responseHeaderMessage.appendBytes(hV);
964 }
965
966 // Write to buffer
967 responseHeaderMessage.end();
968 output.write(responseHeaderMessage.getBuffer(), 0, responseHeaderMessage.getLen());
969
970 }
971
972
973 /**
974 * Finish AJP response.
975 */
976 protected void finish()
977 throws IOException {
978
979 if (!response.isCommitted()) {
980 // Validate and write response headers
981 try {
982 prepareResponse();
983 } catch (IOException e) {
984 // Set error flag
985 error = true;
986 }
987 }
988
989 if (finished)
990 return;
991
992 finished = true;
993
994 // Add the end message
995 output.write(endMessageArray);
996
997 }
998
999
1000 /**
1001 * Read at least the specified amount of bytes, and place them
1002 * in the input buffer.
1003 */
1004 protected boolean read(byte[] buf, int pos, int n)
1005 throws IOException {
1006
1007 int read = 0;
1008 int res = 0;
1009 while (read < n) {
1010 res = input.read(buf, read + pos, n - read);
1011 if (res > 0) {
1012 read += res;
1013 } else {
1014 throw new IOException(sm.getString("ajpprotocol.failedread"));
1015 }
1016 }
1017
1018 return true;
1019
1020 }
1021
1022
1023 /** Receive a chunk of data. Called to implement the
1024 * 'special' packet in ajp13 and to receive the data
1025 * after we send a GET_BODY packet
1026 */
1027 public boolean receive() throws IOException {
1028
1029 first = false;
1030 bodyMessage.reset();
1031 readMessage(bodyMessage);
1032
1033 // No data received.
1034 if (bodyMessage.getLen() == 0) {
1035 // just the header
1036 // Don't mark 'end of stream' for the first chunk.
1037 return false;
1038 }
1039 int blen = bodyMessage.peekInt();
1040 if (blen == 0) {
1041 return false;
1042 }
1043
1044 bodyMessage.getBytes(bodyBytes);
1045 empty = false;
1046 return true;
1047 }
1048
1049 /**
1050 * Get more request body data from the web server and store it in the
1051 * internal buffer.
1052 *
1053 * @return true if there is more data, false if not.
1054 */
1055 private boolean refillReadBuffer() throws IOException {
1056 // If the server returns an empty packet, assume that that end of
1057 // the stream has been reached (yuck -- fix protocol??).
1058 // FORM support
1059 if (replay) {
1060 endOfStream = true; // we've read everything there is
1061 }
1062 if (endOfStream) {
1063 return false;
1064 }
1065
1066 // Request more data immediately
1067 output.write(getBodyMessageArray);
1068
1069 boolean moreData = receive();
1070 if( !moreData ) {
1071 endOfStream = true;
1072 }
1073 return moreData;
1074 }
1075
1076
1077 /**
1078 * Read an AJP message.
1079 *
1080 * @return true if the message has been read, false if the short read
1081 * didn't return anything
1082 * @throws IOException any other failure, including incomplete reads
1083 */
1084 protected boolean readMessage(AjpMessage message)
1085 throws IOException {
1086
1087 byte[] buf = message.getBuffer();
1088
1089 read(buf, 0, message.getHeaderLength());
1090
1091 message.processHeader();
1092 read(buf, message.getHeaderLength(), message.getLen());
1093
1094 return true;
1095
1096 }
1097
1098
1099 /**
1100 * Recycle the processor.
1101 */
1102 public void recycle() {
1103
1104 // Recycle Request object
1105 first = true;
1106 endOfStream = false;
1107 empty = true;
1108 replay = false;
1109 finished = false;
1110 request.recycle();
1111 response.recycle();
1112 certificates.recycle();
1113
1114 }
1115
1116
1117 /**
1118 * Callback to write data from the buffer.
1119 */
1120 protected void flush()
1121 throws IOException {
1122 // Send the flush message
1123 output.write(flushMessageArray);
1124 }
1125
1126
1127 // ------------------------------------- InputStreamInputBuffer Inner Class
1128
1129
1130 /**
1131 * This class is an input buffer which will read its data from an input
1132 * stream.
1133 */
1134 protected class SocketInputBuffer
1135 implements InputBuffer {
1136
1137
1138 /**
1139 * Read bytes into the specified chunk.
1140 */
1141 public int doRead(ByteChunk chunk, Request req )
1142 throws IOException {
1143
1144 if (endOfStream) {
1145 return -1;
1146 }
1147 if (first && req.getContentLengthLong() > 0) {
1148 // Handle special first-body-chunk
1149 if (!receive()) {
1150 return 0;
1151 }
1152 } else if (empty) {
1153 if (!refillReadBuffer()) {
1154 return -1;
1155 }
1156 }
1157 ByteChunk bc = bodyBytes.getByteChunk();
1158 chunk.setBytes(bc.getBuffer(), bc.getStart(), bc.getLength());
1159 empty = true;
1160 return chunk.getLength();
1161
1162 }
1163
1164 }
1165
1166
1167 // ----------------------------------- OutputStreamOutputBuffer Inner Class
1168
1169
1170 /**
1171 * This class is an output buffer which will write data to an output
1172 * stream.
1173 */
1174 protected class SocketOutputBuffer
1175 implements OutputBuffer {
1176
1177
1178 /**
1179 * Write chunk.
1180 */
1181 public int doWrite(ByteChunk chunk, Response res)
1182 throws IOException {
1183
1184 if (!response.isCommitted()) {
1185 // Validate and write response headers
1186 try {
1187 prepareResponse();
1188 } catch (IOException e) {
1189 // Set error flag
1190 error = true;
1191 }
1192 }
1193
1194 int len = chunk.getLength();
1195 // 4 - hardcoded, byte[] marshalling overhead
1196 int chunkSize = Constants.MAX_SEND_SIZE;
1197 int off = 0;
1198 while (len > 0) {
1199 int thisTime = len;
1200 if (thisTime > chunkSize) {
1201 thisTime = chunkSize;
1202 }
1203 len -= thisTime;
1204 responseHeaderMessage.reset();
1205 responseHeaderMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK);
1206 responseHeaderMessage.appendBytes(chunk.getBytes(), chunk.getOffset() + off, thisTime);
1207 responseHeaderMessage.end();
1208 output.write(responseHeaderMessage.getBuffer(), 0, responseHeaderMessage.getLen());
1209
1210 off += thisTime;
1211 }
1212
1213 return chunk.getLength();
1214
1215 }
1216
1217
1218 }
1219
1220
1221 }