KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > coyote > ajp > AjpProcessor


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 JavaDoc;
21 import java.io.IOException JavaDoc;
22 import java.io.InputStream JavaDoc;
23 import java.io.InterruptedIOException JavaDoc;
24 import java.io.OutputStream JavaDoc;
25 import java.net.InetAddress JavaDoc;
26 import java.net.Socket JavaDoc;
27 import java.security.cert.CertificateFactory JavaDoc;
28 import java.security.cert.X509Certificate JavaDoc;
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.commons.logging.Log log
65         = org.apache.commons.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 JavaDoc socket;
165
166     
167     /**
168      * Input stream.
169      */

170     protected InputStream JavaDoc input;
171     
172     
173     /**
174      * Output stream.
175      */

176     protected OutputStream JavaDoc 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 JavaDoc requiredSecret = null;
327     public void setRequiredSecret(String JavaDoc requiredSecret) { this.requiredSecret = requiredSecret; }
328
329
330     // --------------------------------------------------------- Public Methods
331

332
333     /** Get the request associated with this processor.
334      *
335      * @return The request
336      */

337     public Request getRequest() {
338         return request;
339     }
340
341
342     /**
343      * Process pipelined HTTP requests using the specified input and output
344      * streams.
345      *
346      * @throws IOException error during an I/O operation
347      */

348     public boolean process(Socket JavaDoc socket)
349         throws IOException JavaDoc {
350         RequestInfo rp = request.getRequestProcessor();
351         rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
352
353         // Setting up the socket
354
this.socket = socket;
355         input = socket.getInputStream();
356         output = socket.getOutputStream();
357
358         // Error flag
359
error = false;
360
361         while (started && !error) {
362
363             // Parsing the request header
364
try {
365                 // Get first message of the request
366
if (!readMessage(requestHeaderMessage)) {
367                     // This means a connection timeout
368
rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
369                     break;
370                 }
371                 // Check message type, process right away and break if
372
// not regular request processing
373
int type = requestHeaderMessage.getByte();
374                 if (type == Constants.JK_AJP13_CPING_REQUEST) {
375                     try {
376                         output.write(pongMessageArray);
377                     } catch (IOException JavaDoc e) {
378                         error = true;
379                     }
380                     continue;
381                 } else if(type != Constants.JK_AJP13_FORWARD_REQUEST) {
382                     // Usually the servlet didn't read the previous request body
383
if(log.isDebugEnabled()) {
384                         log.debug("Unexpected message: "+type);
385                     }
386                     continue;
387                 }
388
389                 request.setStartTime(System.currentTimeMillis());
390             } catch (IOException JavaDoc e) {
391                 error = true;
392                 break;
393             } catch (Throwable JavaDoc t) {
394                 log.debug(sm.getString("ajpprocessor.header.error"), t);
395                 // 400 - Bad Request
396
response.setStatus(400);
397                 error = true;
398             }
399
400             // Setting up filters, and parse some request headers
401
rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
402             try {
403                 prepareRequest();
404             } catch (Throwable JavaDoc t) {
405                 log.debug(sm.getString("ajpprocessor.request.prepare"), t);
406                 // 400 - Internal Server Error
407
response.setStatus(400);
408                 error = true;
409             }
410
411             // Process the request in the adapter
412
if (!error) {
413                 try {
414                     rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
415                     adapter.service(request, response);
416                 } catch (InterruptedIOException JavaDoc e) {
417                     error = true;
418                 } catch (Throwable JavaDoc t) {
419                     log.error(sm.getString("ajpprocessor.request.process"), t);
420                     // 500 - Internal Server Error
421
response.setStatus(500);
422                     error = true;
423                 }
424             }
425
426             // Finish the response if not done yet
427
if (!finished) {
428                 try {
429                     finish();
430                 } catch (Throwable JavaDoc t) {
431                     error = true;
432                 }
433             }
434
435             // If there was an error, make sure the request is counted as
436
// and error, and update the statistics counter
437
if (error) {
438                 response.setStatus(500);
439             }
440             request.updateCounters();
441
442             rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
443             recycle();
444
445         }
446
447         rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
448         recycle();
449         input = null;
450         output = null;
451         
452         return true;
453
454     }
455
456
457     // ----------------------------------------------------- ActionHook Methods
458

459
460     /**
461      * Send an action to the connector.
462      *
463      * @param actionCode Type of the action
464      * @param param Action parameter
465      */

466     public void action(ActionCode actionCode, Object JavaDoc param) {
467
468         if (actionCode == ActionCode.ACTION_COMMIT) {
469
470             if (response.isCommitted())
471                 return;
472
473             // Validate and write response headers
474
try {
475                 prepareResponse();
476             } catch (IOException JavaDoc e) {
477                 // Set error flag
478
error = true;
479             }
480
481         } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
482
483             if (!response.isCommitted()) {
484                 // Validate and write response headers
485
try {
486                     prepareResponse();
487                 } catch (IOException JavaDoc e) {
488                     // Set error flag
489
error = true;
490                     return;
491                 }
492             }
493
494             try {
495                 flush();
496             } catch (IOException JavaDoc e) {
497                 // Set error flag
498
error = true;
499             }
500
501         } else if (actionCode == ActionCode.ACTION_CLOSE) {
502             // Close
503

504             // End the processing of the current request, and stop any further
505
// transactions with the client
506

507             try {
508                 finish();
509             } catch (IOException JavaDoc e) {
510                 // Set error flag
511
error = true;
512             }
513
514         } else if (actionCode == ActionCode.ACTION_START) {
515
516             started = true;
517
518         } else if (actionCode == ActionCode.ACTION_STOP) {
519
520             started = false;
521
522         } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
523
524             if (!certificates.isNull()) {
525                 ByteChunk certData = certificates.getByteChunk();
526                 X509Certificate JavaDoc jsseCerts[] = null;
527                 ByteArrayInputStream JavaDoc bais =
528                     new ByteArrayInputStream JavaDoc(certData.getBytes(),
529                             certData.getStart(),
530                             certData.getLength());
531                 // Fill the first element.
532
try {
533                     CertificateFactory JavaDoc cf =
534                         CertificateFactory.getInstance("X.509");
535                     X509Certificate JavaDoc cert = (X509Certificate JavaDoc)
536                     cf.generateCertificate(bais);
537                     jsseCerts = new X509Certificate JavaDoc[1];
538                     jsseCerts[0] = cert;
539                     request.setAttribute(JIoEndpoint.CERTIFICATE_KEY, jsseCerts);
540                 } catch (java.security.cert.CertificateException JavaDoc e) {
541                     log.error(sm.getString("ajpprocessor.certs.fail"), e);
542                     return;
543                 }
544             }
545
546         } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
547
548             // Get remote host name using a DNS resolution
549
if (request.remoteHost().isNull()) {
550                 try {
551                     request.remoteHost().setString(InetAddress.getByName
552                             (request.remoteAddr().toString()).getHostName());
553                 } catch (IOException JavaDoc iex) {
554                     // Ignore
555
}
556             }
557
558         } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
559
560             // Copy from local name for now, which should simply be an address
561
request.localAddr().setString(request.localName().toString());
562
563         } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
564
565             // Set the given bytes as the content
566
ByteChunk bc = (ByteChunk) param;
567             bodyBytes.setBytes(bc.getBytes(), bc.getStart(), bc.getLength());
568             request.setContentLength(bc.getLength());
569             first = false;
570             empty = false;
571             replay = true;
572
573         }
574
575
576     }
577
578
579     // ------------------------------------------------------ Connector Methods
580

581
582     /**
583      * Set the associated adapter.
584      *
585      * @param adapter the new adapter
586      */

587     public void setAdapter(Adapter adapter) {
588         this.adapter = adapter;
589     }
590
591
592     /**
593      * Get the associated adapter.
594      *
595      * @return the associated adapter
596      */

597     public Adapter getAdapter() {
598         return adapter;
599     }
600
601
602     // ------------------------------------------------------ Protected Methods
603

604
605     /**
606      * After reading the request headers, we have to setup the request filters.
607      */

608     protected void prepareRequest() {
609
610         // Translate the HTTP method code to a String.
611
byte methodCode = requestHeaderMessage.getByte();
612         if (methodCode != Constants.SC_M_JK_STORED) {
613             String JavaDoc methodName = Constants.methodTransArray[(int)methodCode - 1];
614             request.method().setString(methodName);
615         }
616
617         requestHeaderMessage.getBytes(request.protocol());
618         requestHeaderMessage.getBytes(request.requestURI());
619
620         requestHeaderMessage.getBytes(request.remoteAddr());
621         requestHeaderMessage.getBytes(request.remoteHost());
622         requestHeaderMessage.getBytes(request.localName());
623         request.setLocalPort(requestHeaderMessage.getInt());
624
625         boolean isSSL = requestHeaderMessage.getByte() != 0;
626         if (isSSL) {
627             request.scheme().setString("https");
628         }
629
630         // Decode headers
631
MimeHeaders headers = request.getMimeHeaders();
632
633         int hCount = requestHeaderMessage.getInt();
634         for(int i = 0 ; i < hCount ; i++) {
635             String JavaDoc hName = null;
636
637             // Header names are encoded as either an integer code starting
638
// with 0xA0, or as a normal string (in which case the first
639
// two bytes are the length).
640
int isc = requestHeaderMessage.peekInt();
641             int hId = isc & 0xFF;
642
643             MessageBytes vMB = null;
644             isc &= 0xFF00;
645             if(0xA000 == isc) {
646                 requestHeaderMessage.getInt(); // To advance the read position
647
hName = Constants.headerTransArray[hId - 1];
648                 vMB = headers.addValue(hName);
649             } else {
650                 // reset hId -- if the header currently being read
651
// happens to be 7 or 8 bytes long, the code below
652
// will think it's the content-type header or the
653
// content-length header - SC_REQ_CONTENT_TYPE=7,
654
// SC_REQ_CONTENT_LENGTH=8 - leading to unexpected
655
// behaviour. see bug 5861 for more information.
656
hId = -1;
657                 requestHeaderMessage.getBytes(tmpMB);
658                 ByteChunk bc = tmpMB.getByteChunk();
659                 vMB = headers.addValue(bc.getBuffer(),
660                         bc.getStart(), bc.getLength());
661             }
662
663             requestHeaderMessage.getBytes(vMB);
664
665             if (hId == Constants.SC_REQ_CONTENT_LENGTH ||
666                     (hId == -1 && tmpMB.equalsIgnoreCase("Content-Length"))) {
667                 // just read the content-length header, so set it
668
request.setContentLength( vMB.getInt() );
669             } else if (hId == Constants.SC_REQ_CONTENT_TYPE ||
670                     (hId == -1 && tmpMB.equalsIgnoreCase("Content-Type"))) {
671                 // just read the content-type header, so set it
672
ByteChunk bchunk = vMB.getByteChunk();
673                 request.contentType().setBytes(bchunk.getBytes(),
674                         bchunk.getOffset(),
675                         bchunk.getLength());
676             }
677         }
678
679         // Decode extra attributes
680
boolean secret = false;
681         byte attributeCode;
682         while ((attributeCode = requestHeaderMessage.getByte())
683                 != Constants.SC_A_ARE_DONE) {
684
685             switch (attributeCode) {
686
687             case Constants.SC_A_REQ_ATTRIBUTE :
688                 requestHeaderMessage.getBytes(tmpMB);
689                 String JavaDoc n = tmpMB.toString();
690                 requestHeaderMessage.getBytes(tmpMB);
691                 String JavaDoc v = tmpMB.toString();
692                 request.setAttribute(n, v);
693                 break;
694
695             case Constants.SC_A_CONTEXT :
696                 requestHeaderMessage.getBytes(tmpMB);
697                 // nothing
698
break;
699
700             case Constants.SC_A_SERVLET_PATH :
701                 requestHeaderMessage.getBytes(tmpMB);
702                 // nothing
703
break;
704
705             case Constants.SC_A_REMOTE_USER :
706                 if (tomcatAuthentication) {
707                     // ignore server
708
requestHeaderMessage.getBytes(tmpMB);
709                 } else {
710                     requestHeaderMessage.getBytes(request.getRemoteUser());
711                 }
712                 break;
713
714             case Constants.SC_A_AUTH_TYPE :
715                 if (tomcatAuthentication) {
716                     // ignore server
717
requestHeaderMessage.getBytes(tmpMB);
718                 } else {
719                     requestHeaderMessage.getBytes(request.getAuthType());
720                 }
721                 break;
722
723             case Constants.SC_A_QUERY_STRING :
724                 requestHeaderMessage.getBytes(request.queryString());
725                 break;
726
727             case Constants.SC_A_JVM_ROUTE :
728                 requestHeaderMessage.getBytes(request.instanceId());
729                 break;
730
731             case Constants.SC_A_SSL_CERT :
732                 request.scheme().setString("https");
733                 // SSL certificate extraction is lazy, moved to JkCoyoteHandler
734
requestHeaderMessage.getBytes(certificates);
735                 break;
736
737             case Constants.SC_A_SSL_CIPHER :
738                 request.scheme().setString("https");
739                 requestHeaderMessage.getBytes(tmpMB);
740                 request.setAttribute(JIoEndpoint.CIPHER_SUITE_KEY,
741                                      tmpMB.toString());
742                 break;
743
744             case Constants.SC_A_SSL_SESSION :
745                 request.scheme().setString("https");
746                 requestHeaderMessage.getBytes(tmpMB);
747                 request.setAttribute(JIoEndpoint.SESSION_ID_KEY,
748                                      tmpMB.toString());
749                 break;
750
751             case Constants.SC_A_SSL_KEY_SIZE :
752                 request.setAttribute(JIoEndpoint.KEY_SIZE_KEY,
753                                      new Integer JavaDoc(requestHeaderMessage.getInt()));
754                 break;
755
756             case Constants.SC_A_STORED_METHOD:
757                 requestHeaderMessage.getBytes(request.method());
758                 break;
759
760             case Constants.SC_A_SECRET:
761                 requestHeaderMessage.getBytes(tmpMB);
762                 if (requiredSecret != null) {
763                     secret = true;
764                     if (!tmpMB.equals(requiredSecret)) {
765                         response.setStatus(403);
766                         error = true;
767                     }
768                 }
769                 break;
770
771             default:
772                 // Ignore unknown attribute for backward compatibility
773
break;
774
775             }
776
777         }
778
779         // Check if secret was submitted if required
780
if ((requiredSecret != null) && !secret) {
781             response.setStatus(403);
782             error = true;
783         }
784
785         // Check for a full URI (including protocol://host:port/)
786
ByteChunk uriBC = request.requestURI().getByteChunk();
787         if (uriBC.startsWithIgnoreCase("http", 0)) {
788
789             int pos = uriBC.indexOf("://", 0, 3, 4);
790             int uriBCStart = uriBC.getStart();
791             int slashPos = -1;
792             if (pos != -1) {
793                 byte[] uriB = uriBC.getBytes();
794                 slashPos = uriBC.indexOf('/', pos + 3);
795                 if (slashPos == -1) {
796                     slashPos = uriBC.getLength();
797                     // Set URI as "/"
798
request.requestURI().setBytes
799                         (uriB, uriBCStart + pos + 1, 1);
800                 } else {
801                     request.requestURI().setBytes
802                         (uriB, uriBCStart + slashPos,
803                          uriBC.getLength() - slashPos);
804                 }
805                 MessageBytes hostMB = headers.setValue("host");
806                 hostMB.setBytes(uriB, uriBCStart + pos + 3,
807                                 slashPos - pos - 3);
808             }
809
810         }
811
812         MessageBytes valueMB = request.getMimeHeaders().getValue("host");
813         parseHost(valueMB);
814
815     }
816
817
818     /**
819      * Parse host.
820      */

821     public void parseHost(MessageBytes valueMB) {
822
823         if (valueMB == null || (valueMB != null && valueMB.isNull()) ) {
824             // HTTP/1.0
825
// Default is what the socket tells us. Overriden if a host is
826
// found/parsed
827
request.setServerPort(endpoint.getPort());
828             return;
829         }
830
831         ByteChunk valueBC = valueMB.getByteChunk();
832         byte[] valueB = valueBC.getBytes();
833         int valueL = valueBC.getLength();
834         int valueS = valueBC.getStart();
835         int colonPos = -1;
836         if (hostNameC.length < valueL) {
837             hostNameC = new char[valueL];
838         }
839
840         boolean ipv6 = (valueB[valueS] == '[');
841         boolean bracketClosed = false;
842         for (int i = 0; i < valueL; i++) {
843             char b = (char) valueB[i + valueS];
844             hostNameC[i] = b;
845             if (b == ']') {
846                 bracketClosed = true;
847             } else if (b == ':') {
848                 if (!ipv6 || bracketClosed) {
849                     colonPos = i;
850                     break;
851                 }
852             }
853         }
854
855         if (colonPos < 0) {
856             if (request.scheme().equalsIgnoreCase("https")) {
857                 // 443 - Default HTTPS port
858
request.setServerPort(443);
859             } else {
860                 // 80 - Default HTTTP port
861
request.setServerPort(80);
862             }
863             request.serverName().setChars(hostNameC, 0, valueL);
864         } else {
865
866             request.serverName().setChars(hostNameC, 0, colonPos);
867
868             int port = 0;
869             int mult = 1;
870             for (int i = valueL - 1; i > colonPos; i--) {
871                 int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
872                 if (charValue == -1) {
873                     // Invalid character
874
error = true;
875                     // 400 - Bad request
876
response.setStatus(400);
877                     break;
878                 }
879                 port = port + (charValue * mult);
880                 mult = 10 * mult;
881             }
882             request.setServerPort(port);
883
884         }
885
886     }
887
888
889     /**
890      * When committing the response, we have to validate the set of headers, as
891      * well as setup the response filters.
892      */

893     protected void prepareResponse()
894         throws IOException JavaDoc {
895
896         response.setCommitted(true);
897
898         responseHeaderMessage.reset();
899         responseHeaderMessage.appendByte(Constants.JK_AJP13_SEND_HEADERS);
900
901         // HTTP header contents
902
responseHeaderMessage.appendInt(response.getStatus());
903         String JavaDoc message = response.getMessage();
904         if (message == null){
905             message = HttpMessages.getMessage(response.getStatus());
906         } else {
907             message = message.replace('\n', ' ').replace('\r', ' ');
908         }
909         tmpMB.setString(message);
910         responseHeaderMessage.appendBytes(tmpMB);
911
912         // Special headers
913
MimeHeaders headers = response.getMimeHeaders();
914         String JavaDoc contentType = response.getContentType();
915         if (contentType != null) {
916             headers.setValue("Content-Type").setString(contentType);
917         }
918         String JavaDoc contentLanguage = response.getContentLanguage();
919         if (contentLanguage != null) {
920             headers.setValue("Content-Language").setString(contentLanguage);
921         }
922         int contentLength = response.getContentLength();
923         if (contentLength >= 0) {
924             headers.setValue("Content-Length").setInt(contentLength);
925         }
926
927         // Other headers
928
int numHeaders = headers.size();
929         responseHeaderMessage.appendInt(numHeaders);
930         for (int i = 0; i < numHeaders; i++) {
931             MessageBytes hN = headers.getName(i);
932             responseHeaderMessage.appendBytes(hN);
933             MessageBytes hV=headers.getValue(i);
934             responseHeaderMessage.appendBytes(hV);
935         }
936
937         // Write to buffer
938
responseHeaderMessage.end();
939         output.write(responseHeaderMessage.getBuffer(), 0, responseHeaderMessage.getLen());
940
941     }
942
943
944     /**
945      * Finish AJP response.
946      */

947     protected void finish()
948         throws IOException JavaDoc {
949
950         if (!response.isCommitted()) {
951             // Validate and write response headers
952
try {
953                 prepareResponse();
954             } catch (IOException JavaDoc e) {
955                 // Set error flag
956
error = true;
957             }
958         }
959
960         if (finished)
961             return;
962
963         finished = true;
964
965         // Add the end message
966
output.write(endMessageArray);
967
968     }
969
970
971     /**
972      * Read at least the specified amount of bytes, and place them
973      * in the input buffer.
974      */

975     protected boolean read(byte[] buf, int pos, int n)
976         throws IOException JavaDoc {
977
978         int read = 0;
979         int res = 0;
980         while (read < n) {
981             res = input.read(buf, read + pos, n - read);
982             if (res > 0) {
983                 read += res;
984             } else {
985                 throw new IOException JavaDoc(sm.getString("ajpprotocol.failedread"));
986             }
987         }
988         
989         return true;
990
991     }
992
993
994     /** Receive a chunk of data. Called to implement the
995      * 'special' packet in ajp13 and to receive the data
996      * after we send a GET_BODY packet
997      */

998     public boolean receive() throws IOException JavaDoc {
999
1000        first = false;
1001        bodyMessage.reset();
1002        readMessage(bodyMessage);
1003
1004        // No data received.
1005
if (bodyMessage.getLen() == 0) {
1006            // just the header
1007
// Don't mark 'end of stream' for the first chunk.
1008
return false;
1009        }
1010        int blen = bodyMessage.peekInt();
1011        if (blen == 0) {
1012            return false;
1013        }
1014
1015        bodyMessage.getBytes(bodyBytes);
1016        empty = false;
1017        return true;
1018    }
1019
1020    /**
1021     * Get more request body data from the web server and store it in the
1022     * internal buffer.
1023     *
1024     * @return true if there is more data, false if not.
1025     */

1026    private boolean refillReadBuffer() throws IOException JavaDoc {
1027        // If the server returns an empty packet, assume that that end of
1028
// the stream has been reached (yuck -- fix protocol??).
1029
// FORM support
1030
if (replay) {
1031            endOfStream = true; // we've read everything there is
1032
}
1033        if (endOfStream) {
1034            return false;
1035        }
1036
1037        // Request more data immediately
1038
output.write(getBodyMessageArray);
1039
1040        boolean moreData = receive();
1041        if( !moreData ) {
1042            endOfStream = true;
1043        }
1044        return moreData;
1045    }
1046
1047
1048    /**
1049     * Read an AJP message.
1050     *
1051     * @return true if the message has been read, false if the short read
1052     * didn't return anything
1053     * @throws IOException any other failure, including incomplete reads
1054     */

1055    protected boolean readMessage(AjpMessage message)
1056        throws IOException JavaDoc {
1057
1058        byte[] buf = message.getBuffer();
1059
1060        read(buf, 0, message.getHeaderLength());
1061
1062        message.processHeader();
1063        read(buf, message.getHeaderLength(), message.getLen());
1064
1065        return true;
1066
1067    }
1068
1069
1070    /**
1071     * Recycle the processor.
1072     */

1073    public void recycle() {
1074
1075        // Recycle Request object
1076
first = true;
1077        endOfStream = false;
1078        empty = true;
1079        replay = false;
1080        finished = false;
1081        request.recycle();
1082        response.recycle();
1083        certificates.recycle();
1084
1085    }
1086
1087
1088    /**
1089     * Callback to write data from the buffer.
1090     */

1091    protected void flush()
1092        throws IOException JavaDoc {
1093        // Send the flush message
1094
output.write(flushMessageArray);
1095    }
1096
1097
1098    // ------------------------------------- InputStreamInputBuffer Inner Class
1099

1100
1101    /**
1102     * This class is an input buffer which will read its data from an input
1103     * stream.
1104     */

1105    protected class SocketInputBuffer
1106        implements InputBuffer {
1107
1108
1109        /**
1110         * Read bytes into the specified chunk.
1111         */

1112        public int doRead(ByteChunk chunk, Request req )
1113            throws IOException JavaDoc {
1114
1115            if (endOfStream) {
1116                return -1;
1117            }
1118            if (first && req.getContentLength() > 0) {
1119                // Handle special first-body-chunk
1120
if (!receive()) {
1121                    return 0;
1122                }
1123            } else if (empty) {
1124                if (!refillReadBuffer()) {
1125                    return -1;
1126                }
1127            }
1128            ByteChunk bc = bodyBytes.getByteChunk();
1129            chunk.setBytes(bc.getBuffer(), bc.getStart(), bc.getLength());
1130            empty = true;
1131            return chunk.getLength();
1132
1133        }
1134
1135    }
1136
1137
1138    // ----------------------------------- OutputStreamOutputBuffer Inner Class
1139

1140
1141    /**
1142     * This class is an output buffer which will write data to an output
1143     * stream.
1144     */

1145    protected class SocketOutputBuffer
1146        implements OutputBuffer {
1147
1148
1149        /**
1150         * Write chunk.
1151         */

1152        public int doWrite(ByteChunk chunk, Response res)
1153            throws IOException JavaDoc {
1154
1155            if (!response.isCommitted()) {
1156                // Validate and write response headers
1157
try {
1158                    prepareResponse();
1159                } catch (IOException JavaDoc e) {
1160                    // Set error flag
1161
error = true;
1162                }
1163            }
1164
1165            int len = chunk.getLength();
1166            // 4 - hardcoded, byte[] marshalling overhead
1167
int chunkSize = Constants.MAX_SEND_SIZE;
1168            int off = 0;
1169            while (len > 0) {
1170                int thisTime = len;
1171                if (thisTime > chunkSize) {
1172                    thisTime = chunkSize;
1173                }
1174                len -= thisTime;
1175                responseHeaderMessage.reset();
1176                responseHeaderMessage.appendByte(Constants.JK_AJP13_SEND_BODY_CHUNK);
1177                responseHeaderMessage.appendBytes(chunk.getBytes(), chunk.getOffset() + off, thisTime);
1178                responseHeaderMessage.end();
1179                output.write(responseHeaderMessage.getBuffer(), 0, responseHeaderMessage.getLen());
1180
1181                off += thisTime;
1182            }
1183
1184            return chunk.getLength();
1185
1186        }
1187
1188
1189    }
1190
1191
1192}
1193
Popular Tags