KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > coyote > tomcat5 > CoyoteAdapter


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the "License"). You may not use this file except
5  * in compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * glassfish/bootstrap/legal/CDDLv1.0.txt or
9  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * HEADER in each file and include the License file at
15  * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
16  * add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your
18  * own identifying information: Portions Copyright [yyyy]
19  * [name of copyright owner]
20  *
21  * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
22  *
23  * Portions Copyright Apache Software Foundation.
24  */

25
26
27 package org.apache.coyote.tomcat5;
28
29 import java.io.IOException JavaDoc;
30
31 // START S1AS 6188932
32
import java.security.cert.X509Certificate JavaDoc;
33 import java.security.cert.CertificateException JavaDoc;
34 // END S1AS 6188932
35

36 import javax.servlet.http.Cookie JavaDoc;
37 import javax.servlet.http.HttpServletRequest JavaDoc;
38 import javax.servlet.http.HttpServletResponse JavaDoc;
39 import org.apache.catalina.ContainerEvent;
40 import org.apache.catalina.ContainerListener;
41
42 import org.apache.catalina.Context;
43 import org.apache.catalina.Globals;
44 import org.apache.catalina.Wrapper;
45 import org.apache.catalina.core.ContainerBase;
46 import org.apache.catalina.util.StringManager;
47 import com.sun.org.apache.commons.logging.Log;
48 import com.sun.org.apache.commons.logging.LogFactory;
49 import org.apache.coyote.ActionCode;
50 import org.apache.coyote.Adapter;
51 import org.apache.coyote.Request;
52 import org.apache.coyote.Response;
53 /* CR 6309511
54 import org.apache.tomcat.util.buf.B2CConverter;
55  */

56 import org.apache.tomcat.util.buf.ByteChunk;
57 import org.apache.tomcat.util.buf.CharChunk;
58 import org.apache.tomcat.util.buf.MessageBytes;
59 // START GlassFish 936
60
import org.apache.tomcat.util.buf.UEncoder;
61 // END GlassFish 936
62
/* CR 6309511
63 import org.apache.tomcat.util.http.Cookies;
64 import org.apache.tomcat.util.http.ServerCookie;
65  */

66 // START S1AS 6188932
67
import com.sun.appserv.ProxyHandler;
68 // END S1AS 6188932
69

70
71 /**
72  * Implementation of a request processor which delegates the processing to a
73  * Coyote processor.
74  *
75  * @author Craig R. McClanahan
76  * @author Remy Maucherat
77  * @version $Revision: 1.12.2.1 $ $Date: 2006/09/05 19:52:26 $
78  */

79
80 public class CoyoteAdapter
81     implements Adapter
82  {
83     private static Log log = LogFactory.getLog(CoyoteAdapter.class);
84
85     // -------------------------------------------------------------- Constants
86

87
88     public static final int ADAPTER_NOTES = 1;
89
90
91     // ----------------------------------------------------------- Constructors
92

93
94     /**
95      * Construct a new CoyoteProcessor associated with the specified connector.
96      *
97      * @param connector CoyoteConnector that owns this processor
98      * @param id Identifier of this CoyoteProcessor (unique per connector)
99      */

100     public CoyoteAdapter(CoyoteConnector connector) {
101
102         super();
103         this.connector = connector;
104         this.debug = connector.getDebug();
105         // START GlassFish 936
106
urlEncoder.addSafeCharacter('/');
107         // END GlassFish 936
108
}
109
110
111     // ----------------------------------------------------- Instance Variables
112

113
114     /**
115      * The CoyoteConnector with which this processor is associated.
116      */

117     private CoyoteConnector connector = null;
118
119
120     /**
121      * The debugging detail level for this component.
122      */

123     private int debug = 0;
124
125     // START GlassFish 936
126
private UEncoder urlEncoder = new UEncoder();
127     // END GlassFish 936
128

129     /**
130      * The match string for identifying a session ID parameter.
131      */

132     /* CR 6309511
133     private static final String match =
134         ";" + Globals.SESSION_PARAMETER_NAME + "=";
135      */

136
137
138     /**
139      * The match string for identifying a session ID parameter.
140      */

141     /* CR 6309511
142     private static final char[] SESSION_ID = match.toCharArray();
143      */

144
145
146     /**
147      * The string manager for this package.
148      */

149     protected StringManager sm =
150         StringManager.getManager(Constants.Package);
151
152
153     // -------------------------------------------------------- Adapter Methods
154

155
156     /**
157      * Service method.
158      */

159     public void service(Request JavaDoc req, Response JavaDoc res)
160         throws Exception JavaDoc {
161
162         CoyoteRequest request = (CoyoteRequest) req.getNote(ADAPTER_NOTES);
163         CoyoteResponse response = (CoyoteResponse) res.getNote(ADAPTER_NOTES);
164
165         if (request == null) {
166
167             // Create objects
168
request = (CoyoteRequest) connector.createRequest();
169             request.setCoyoteRequest(req);
170             response = (CoyoteResponse) connector.createResponse();
171             response.setCoyoteResponse(res);
172
173             // Link objects
174
request.setResponse(response);
175             response.setRequest(request);
176
177             // Set as notes
178
req.setNote(ADAPTER_NOTES, request);
179             res.setNote(ADAPTER_NOTES, response);
180
181             // Set query string encoding
182
req.getParameters().setQueryStringEncoding
183                 (connector.getURIEncoding());
184         }
185
186         // START SJSAS 6331392
187
// Check connector for disabled state
188
if (!connector.isEnabled()) {
189             String JavaDoc msg = sm.getString("coyoteAdapter.listenerOff",
190                                       String.valueOf(connector.getPort()));
191             if (log.isDebugEnabled()) {
192                 log.debug(msg);
193             }
194             response.sendError(HttpServletResponse.SC_NOT_FOUND, msg);
195             return;
196         }
197         // END SJSAS 6331392
198

199         if (connector.isXpoweredBy()) {
200             response.addHeader("X-Powered-By", "Servlet/2.5");
201         }
202
203         try {
204
205             // Parse and set Catalina and configuration specific
206
// request parameters
207
if ( postParseRequest(req, request, res, response) ) {
208
209                 // START S1AS 6188932
210
boolean authPassthroughEnabled =
211                     connector.getAuthPassthroughEnabled();
212                 ProxyHandler proxyHandler = connector.getProxyHandler();
213                 if (authPassthroughEnabled && proxyHandler != null) {
214
215                     // START SJSAS 6397218
216
if (proxyHandler.getSSLKeysize(
217                             (HttpServletRequest JavaDoc)request.getRequest()) > 0) {
218                         request.setSecure(true);
219                     }
220                     // END SJSAS 6397218
221

222                     X509Certificate JavaDoc[] certs = null;
223                     try {
224                         certs = proxyHandler.getSSLClientCertificateChain(
225                                     request.getRequest());
226                     } catch (CertificateException JavaDoc ce) {
227                         log.error(sm.getString(
228                             "coyoteAdapter.proxyAuthCertError"),
229                             ce);
230                     }
231                     if (certs != null) {
232                         request.setAttribute(Globals.CERTIFICATES_ATTR,
233                                              certs);
234                     }
235                 }
236                 // END S1AS 6188932
237

238                 // Calling the container
239
connector.getContainer().invoke(request, response);
240               
241             }
242
243             response.finishResponse();
244             req.action( ActionCode.ACTION_POST_REQUEST , null);
245
246         } catch (IOException JavaDoc e) {
247             ;
248         } catch (Throwable JavaDoc t) {
249             log.error(sm.getString("coyoteAdapter.service"), t);
250         } finally {
251             // Recycle the wrapper request and response
252
request.recycle();
253             response.recycle();
254         }
255
256     }
257
258
259     // ------------------------------------------------------ Protected Methods
260

261
262     /**
263      * Parse additional request parameters.
264      */

265     protected boolean postParseRequest(Request JavaDoc req, CoyoteRequest request,
266                                        Response JavaDoc res, CoyoteResponse response)
267         throws Exception JavaDoc {
268         // XXX the processor needs to set a correct scheme and port prior to this point,
269
// in ajp13 protocols dont make sense to get the port from the connector..
270
// XXX the processor may have set a correct scheme and port prior to this point,
271
// in ajp13 protocols dont make sense to get the port from the connector...
272
// otherwise, use connector configuration
273
if (! req.scheme().isNull()) {
274             // use processor specified scheme to determine secure state
275
request.setSecure(req.scheme().equals("https"));
276         } else {
277             // use connector scheme and secure configuration, (defaults to
278
// "http" and false respectively)
279
req.scheme().setString(connector.getScheme());
280             request.setSecure(connector.getSecure());
281         }
282
283         // FIXME: the code below doesnt belongs to here,
284
// this is only have sense
285
// in Http11, not in ajp13..
286
// At this point the Host header has been processed.
287
// Override if the proxyPort/proxyHost are set
288
String JavaDoc proxyName = connector.getProxyName();
289         int proxyPort = connector.getProxyPort();
290         if (proxyPort != 0) {
291             req.setServerPort(proxyPort);
292         }
293         if (proxyName != null) {
294             req.serverName().setString(proxyName);
295         }
296
297         // URI decoding
298
MessageBytes decodedURI = req.decodedURI();
299         decodedURI.duplicate(req.requestURI());
300         try {
301           req.getURLDecoder().convert(decodedURI, false);
302         } catch (IOException JavaDoc ioe) {
303           res.setStatus(400);
304           res.setMessage("Invalid URI");
305           throw ioe;
306         }
307
308         // Normalize decoded URI
309
if (!normalize(req.decodedURI())) {
310             res.setStatus(400);
311             res.setMessage("Invalid URI");
312             return false;
313         }
314
315         // Set the remote principal
316
String JavaDoc principal = req.getRemoteUser().toString();
317         if (principal != null) {
318             request.setUserPrincipal(new CoyotePrincipal(principal));
319         }
320
321         // Set the authorization type
322
String JavaDoc authtype = req.getAuthType().toString();
323         if (authtype != null) {
324             request.setAuthType(authtype);
325         }
326
327         /* CR 6309511
328         // URI character decoding
329         convertURI(decodedURI, request);
330
331         // Parse session Id
332         parseSessionId(req, request);
333          */

334         // START CR 6309511
335
// URI character decoding
336
request.convertURI(decodedURI);
337         
338         // Parse session Id
339
request.parseSessionId();
340         // END CR 6309511
341

342         // Remove any remaining parameters (other than session id, which has
343
// already been removed in parseSessionId()) from the URI, so they
344
// won't be considered by the mapping algorithm.
345
CharChunk uriCC = decodedURI.getCharChunk();
346         int semicolon = uriCC.indexOf(';');
347         if (semicolon > 0) {
348             decodedURI.setChars
349                 (uriCC.getBuffer(), uriCC.getStart(), semicolon);
350         }
351
352         // Request mapping.
353
connector.getMapper().map(req.serverName(), decodedURI,
354                                   request.getMappingData());
355         
356         // START SJSAS 6253524
357
// request.setContext((Context) request.getMappingData().context);
358
// END SJSAS 6253524
359
// START SJSAS 6253524
360
Context ctx = (Context) request.getMappingData().context;
361         request.setContext(ctx);
362         // END SJSAS 6253524
363
request.setWrapper((Wrapper) request.getMappingData().wrapper);
364
365         // Filter trace method
366
if (!connector.getAllowTrace()
367                 && req.method().equalsIgnoreCase("TRACE")) {
368             Wrapper wrapper = request.getWrapper();
369             String JavaDoc header = null;
370             if (wrapper != null) {
371                 String JavaDoc[] methods = wrapper.getServletMethods();
372                 if (methods != null) {
373                     for (int i=0; i<methods.length; i++) {
374                         // Exclude TRACE from methods returned in Allow header
375
if ("TRACE".equals(methods[i])) {
376                             continue;
377                         }
378                         if (header == null) {
379                             header = methods[i];
380                         } else {
381                             header += ", " + methods[i];
382                         }
383                     }
384                 }
385             }
386             res.setStatus(405);
387             res.addHeader("Allow", header);
388             res.setMessage("TRACE method is not allowed");
389             return false;
390         }
391
392         // Possible redirect
393
MessageBytes redirectPathMB = request.getMappingData().redirectPath;
394         // START SJSAS 6253524
395
// if (!redirectPathMB.isNull()) {
396
// END SJSAS 6253524
397
// START SJSAS 6253524
398
if (!redirectPathMB.isNull()
399             && (!ctx.hasAdHocPaths()
400                 || (ctx.getAdHocServletName(((HttpServletRequest JavaDoc)
401                         request.getRequest()).getServletPath()) == null))) {
402         // END SJSAS 6253524
403
String JavaDoc redirectPath = redirectPathMB.toString();
404             String JavaDoc query = request.getQueryString();
405             if (request.isRequestedSessionIdFromURL()) {
406                 // This is not optimal, but as this is not very common, it
407
// shouldn't matter
408
redirectPath = redirectPath + ";jsessionid="
409                     + request.getRequestedSessionId();
410             }
411             if (query != null) {
412                 // This is not optimal, but as this is not very common, it
413
// shouldn't matter
414
redirectPath = redirectPath + "?" + query;
415             }
416             // START GlassFish 936
417
redirectPath = urlEncoder.encodeURL(redirectPath);
418             // END GlassFish 936
419
response.sendRedirect(redirectPath);
420             return false;
421         }
422
423         // Parse session Id
424
/* CR 6309511
425         parseSessionCookiesId(req, request);
426          */

427         // START CR 6309511
428
request.parseSessionCookiesId();
429         // END CR 6309511
430

431         // START SJSAS 6346226
432
request.parseJrouteCookie();
433         // END SJSAS 6346226
434

435         return true;
436     }
437
438
439     /**
440      * Parse session id in URL.
441      */

442     /* CR 6309511
443     protected void parseSessionId(Request req, CoyoteRequest request) {
444
445         CharChunk uriCC = req.decodedURI().getCharChunk();
446         int semicolon = uriCC.indexOf(match, 0, match.length(), 0);
447
448         if (semicolon > 0) {
449
450             // Parse session ID, and extract it from the decoded request URI
451             int start = uriCC.getStart();
452             int end = uriCC.getEnd();
453
454             int sessionIdStart = start + semicolon + match.length();
455             int semicolon2 = uriCC.indexOf(';', sessionIdStart);
456             if (semicolon2 >= 0) {
457                 request.setRequestedSessionId
458                     (new String(uriCC.getBuffer(), sessionIdStart,
459                                 semicolon2 - semicolon - match.length()));
460             } else {
461                 request.setRequestedSessionId
462                     (new String(uriCC.getBuffer(), sessionIdStart,
463                                 end - sessionIdStart));
464             }
465             request.setRequestedSessionURL(true);
466
467             // Extract session ID from request URI
468             ByteChunk uriBC = req.requestURI().getByteChunk();
469             start = uriBC.getStart();
470             end = uriBC.getEnd();
471             semicolon = uriBC.indexOf(match, 0, match.length(), 0);
472
473             if (semicolon > 0) {
474                 sessionIdStart = start + semicolon;
475                 semicolon2 = uriCC.indexOf
476                     (';', start + semicolon + match.length());
477                 uriBC.setEnd(start + semicolon);
478                 byte[] buf = uriBC.getBuffer();
479                 if (semicolon2 >= 0) {
480                     for (int i = 0; i < end - start - semicolon2; i++) {
481                         buf[start + semicolon + i]
482                             = buf[start + i + semicolon2];
483                     }
484                     uriBC.setBytes(buf, start, semicolon
485                                    + (end - start - semicolon2));
486                 }
487             }
488
489         } else {
490             request.setRequestedSessionId(null);
491             request.setRequestedSessionURL(false);
492         }
493
494     }
495      */

496
497
498     /**
499      * Parse session id in URL.
500      */

501     /* CR 6309511
502     protected void parseSessionCookiesId(Request req, CoyoteRequest request) {
503
504         // Parse session id from cookies
505         Cookies serverCookies = req.getCookies();
506         int count = serverCookies.getCookieCount();
507         if (count <= 0)
508             return;
509
510         for (int i = 0; i < count; i++) {
511             ServerCookie scookie = serverCookies.getCookie(i);
512             if (scookie.getName().equals(Globals.SESSION_COOKIE_NAME)) {
513                 // Override anything requested in the URL
514                 if (!request.isRequestedSessionIdFromCookie()) {
515                     // Accept only the first session id cookie
516                     convertMB(scookie.getValue());
517                     request.setRequestedSessionId
518                         (scookie.getValue().toString());
519                     request.setRequestedSessionCookie(true);
520                     request.setRequestedSessionURL(false);
521                     if (log.isDebugEnabled())
522                         log.debug(" Requested cookie session id is " +
523                             ((HttpServletRequest) request.getRequest())
524                             .getRequestedSessionId());
525                 } else {
526                     if (!request.isRequestedSessionIdValid()) {
527                         // Replace the session id until one is valid
528                         convertMB(scookie.getValue());
529                         request.setRequestedSessionId
530                             (scookie.getValue().toString());
531                     }
532                 }
533             }
534         }
535
536     }
537      */

538
539
540     /**
541      * Character conversion of the URI.
542      */

543     /* CR 6309511
544     protected void convertURI(MessageBytes uri, CoyoteRequest request)
545         throws Exception {
546
547         ByteChunk bc = uri.getByteChunk();
548         CharChunk cc = uri.getCharChunk();
549         cc.allocate(bc.getLength(), -1);
550
551         String enc = connector.getURIEncoding();
552         if (enc != null) {
553             B2CConverter conv = request.getURIConverter();
554             try {
555                 if (conv == null) {
556                     conv = new B2CConverter(enc);
557                     request.setURIConverter(conv);
558                 } else {
559                     conv.recycle();
560                 }
561             } catch (IOException e) {
562                 // Ignore
563                 log.error("Invalid URI encoding; using HTTP default");
564                 connector.setURIEncoding(null);
565             }
566             if (conv != null) {
567                 try {
568                     conv.convert(bc, cc);
569                     uri.setChars(cc.getBuffer(), cc.getStart(),
570                                  cc.getLength());
571                     return;
572                 } catch (IOException e) {
573                     log.error("Invalid URI character encoding; trying ascii");
574                     cc.recycle();
575                 }
576             }
577         }
578
579         // Default encoding: fast conversion
580         byte[] bbuf = bc.getBuffer();
581         char[] cbuf = cc.getBuffer();
582         int start = bc.getStart();
583         for (int i = 0; i < bc.getLength(); i++) {
584             cbuf[i] = (char) (bbuf[i + start] & 0xff);
585         }
586         uri.setChars(cbuf, 0, bc.getLength());
587
588     }
589      */

590
591
592     /**
593      * Normalize URI.
594      * <p>
595      * This method normalizes "\", "//", "/./" and "/../". This method will
596      * return false when trying to go above the root, or if the URI contains
597      * a null byte.
598      *
599      * @param uriMB URI to be normalized
600      */

601     public static boolean normalize(MessageBytes uriMB) {
602
603         ByteChunk uriBC = uriMB.getByteChunk();
604         byte[] b = uriBC.getBytes();
605         int start = uriBC.getStart();
606         int end = uriBC.getEnd();
607
608         // URL * is acceptable
609
if ((end - start == 1) && b[start] == (byte) '*')
610           return true;
611
612         int pos = 0;
613         int index = 0;
614
615         // Replace '\' with '/'
616
// Check for null byte
617
for (pos = start; pos < end; pos++) {
618             if (b[pos] == (byte) '\\')
619                 b[pos] = (byte) '/';
620             if (b[pos] == (byte) 0)
621                 return false;
622         }
623
624         // The URL must start with '/'
625
if (b[start] != (byte) '/') {
626             return false;
627         }
628
629         // Replace "//" with "/"
630
for (pos = start; pos < (end - 1); pos++) {
631             if (b[pos] == (byte) '/') {
632                 while ((pos + 1 < end) && (b[pos + 1] == (byte) '/')) {
633                     copyBytes(b, pos, pos + 1, end - pos - 1);
634                     end--;
635                 }
636             }
637         }
638
639         // If the URI ends with "/." or "/..", then we append an extra "/"
640
// Note: It is possible to extend the URI by 1 without any side effect
641
// as the next character is a non-significant WS.
642
if (((end - start) > 2) && (b[end - 1] == (byte) '.')) {
643             if ((b[end - 2] == (byte) '/')
644                 || ((b[end - 2] == (byte) '.')
645                     && (b[end - 3] == (byte) '/'))) {
646                 b[end] = (byte) '/';
647                 end++;
648             }
649         }
650
651         uriBC.setEnd(end);
652
653         index = 0;
654
655         // Resolve occurrences of "/./" in the normalized path
656
while (true) {
657             index = uriBC.indexOf("/./", 0, 3, index);
658             if (index < 0)
659                 break;
660             copyBytes(b, start + index, start + index + 2,
661                       end - start - index - 2);
662             end = end - 2;
663             uriBC.setEnd(end);
664         }
665
666         index = 0;
667
668         // Resolve occurrences of "/../" in the normalized path
669
while (true) {
670             index = uriBC.indexOf("/../", 0, 4, index);
671             if (index < 0)
672                 break;
673             // Prevent from going outside our context
674
if (index == 0)
675                 return false;
676             int index2 = -1;
677             for (pos = start + index - 1; (pos >= 0) && (index2 < 0); pos --) {
678                 if (b[pos] == (byte) '/') {
679                     index2 = pos;
680                 }
681             }
682             copyBytes(b, start + index2, start + index + 3,
683                       end - start - index - 3);
684             end = end + index2 - index - 3;
685             uriBC.setEnd(end);
686             index = index2;
687         }
688
689         uriBC.setBytes(b, start, end);
690
691         return true;
692
693     }
694
695
696     // ------------------------------------------------------ Protected Methods
697

698
699     /**
700      * Copy an array of bytes to a different position. Used during
701      * normalization.
702      */

703     protected static void copyBytes(byte[] b, int dest, int src, int len) {
704         for (int pos = 0; pos < len; pos++) {
705             b[pos + dest] = b[pos + src];
706         }
707     }
708
709
710     /**
711      * Log a message on the Logger associated with our Container (if any)
712      *
713      * @param message Message to be logged
714      */

715     protected void log(String JavaDoc message) {
716         log.info( message );
717     }
718
719
720     /**
721      * Log a message on the Logger associated with our Container (if any)
722      *
723      * @param message Message to be logged
724      * @param throwable Associated exception
725      */

726     protected void log(String JavaDoc message, Throwable JavaDoc throwable) {
727         log.error( message, throwable);
728     }
729
730
731      /**
732       * Character conversion of the a US-ASCII MessageBytes.
733       */

734     /* CR 6309511
735      protected void convertMB(MessageBytes mb) {
736  
737         // This is of course only meaningful for bytes
738         if (mb.getType() != MessageBytes.T_BYTES)
739             return;
740         
741         ByteChunk bc = mb.getByteChunk();
742         CharChunk cc = mb.getCharChunk();
743         cc.allocate(bc.getLength(), -1);
744
745         // Default encoding: fast conversion
746         byte[] bbuf = bc.getBuffer();
747         char[] cbuf = cc.getBuffer();
748         int start = bc.getStart();
749         for (int i = 0; i < bc.getLength(); i++) {
750             cbuf[i] = (char) (bbuf[i + start] & 0xff);
751         }
752         mb.setChars(cbuf, 0, bc.getLength());
753    
754      }
755      */

756
757     
758     // START SJSAS 6349248
759
/**
760      * Notify all container event listeners that a particular event has
761      * occurred for this Adapter. The default implementation performs
762      * this notification synchronously using the calling thread.
763      *
764      * @param type Event type
765      * @param data Event data
766      */

767     public void fireAdapterEvent(String JavaDoc type, Object JavaDoc data) {
768         if ( connector != null && connector.getContainer() != null) {
769             try{
770                 ((ContainerBase)connector.getContainer())
771                     .fireContainerEvent(type,data);
772             } catch (Throwable JavaDoc t){
773                 log.error(sm.getString("coyoteAdapter.service"), t);
774             }
775         }
776     }
777     // END SJSAS 6349248
778

779 }
780
Popular Tags