1   //========================================================================
2   //$Id$
3   //Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
4   //------------------------------------------------------------------------
5   //Licensed under the Apache License, Version 2.0 (the "License");
6   //you may not use this file except in compliance with the License.
7   //You may obtain a copy of the License at 
8   //http://www.apache.org/licenses/LICENSE-2.0
9   //Unless required by applicable law or agreed to in writing, software
10  //distributed under the License is distributed on an "AS IS" BASIS,
11  //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  //See the License for the specific language governing permissions and
13  //limitations under the License.
14  //========================================================================
15  package org.mortbay.jetty.handler.rewrite;
16  
17  import java.io.IOException;
18  
19  import javax.servlet.ServletException;
20  import javax.servlet.http.HttpServletRequest;
21  import javax.servlet.http.HttpServletResponse;
22  
23  import org.mortbay.jetty.HttpConnection;
24  import org.mortbay.jetty.Request;
25  import org.mortbay.jetty.handler.HandlerWrapper;
26  import org.mortbay.jetty.servlet.PathMap;
27  import org.mortbay.log.Log;
28  import org.mortbay.util.LazyList;
29  
30  /* ------------------------------------------------------------ */
31  /**
32   *<p> Rewrite handler is responsible for managing the rules. Its capabilities
33   * is not only limited for url rewrites such as RewritePatternRule or RewriteRegexRule. 
34   * There is also handling for cookies, headers, redirection, setting status or error codes 
35   * whenever the rule finds a match. 
36   * 
37   * <p> The rules can be matched by the ff. options: pattern matching of PathMap 
38   * (class PatternRule), regular expressions (class RegexRule) or certain conditions set 
39   * (e.g. MsieSslRule - the requests must be in SSL mode).
40   * 
41   * Here are the list of rules:
42   * <ul>
43   * <li> CookiePatternRule - adds a new cookie in response. </li>
44   * <li> HeaderPatternRule - adds/modifies the HTTP headers in response. </li>
45   * <li> RedirectPatternRule - sets the redirect location. </li>
46   * <li> ResponsePatternRule - sets the status/error codes. </li>
47   * <li> RewritePatternRule - rewrites the requested URI. </li>
48   * <li> RewriteRegexRule - rewrites the requested URI using regular expression for pattern matching. </li>
49   * <li> MsieSslRule - disables the keep alive on SSL for IE5 and IE6. </li>
50   * <li> LegacyRule - the old version of rewrite. </li>
51   * </ul>
52   * 
53   * Here is a typical jetty.xml configuration would be: <pre>
54   * 
55   *   &lt;Set name="handler"&gt;
56   *     &lt;New id="Handlers" class="org.mortbay.jetty.handler.RewriteHandler"&gt;
57   *       &lt;Set name="rules"&gt;
58   *         &lt;Array type="org.mortbay.jetty.handler.rules.Rule"&gt;
59   *
60   *           &lt;Item&gt; 
61   *             &lt;New id="rewrite" class="org.mortbay.jetty.handler.rules.RewritePatternRule"&gt;
62   *               &lt;Set name="pattern"&gt;/*&lt;/Set&gt;
63   *               &lt;Set name="replacement"&gt;/test&lt;/Set&gt;
64   *             &lt;/New&gt;
65   *           &lt;/Item&gt;
66   *
67   *           &lt;Item&gt; 
68   *             &lt;New id="response" class="org.mortbay.jetty.handler.rules.ResponsePatternRule"&gt;
69   *               &lt;Set name="pattern"&gt;/session/&lt;/Set&gt;
70   *               &lt;Set name="code"&gt;400&lt;/Set&gt;
71   *               &lt;Set name="reason"&gt;Setting error code 400&lt;/Set&gt;
72   *             &lt;/New&gt;
73   *           &lt;/Item&gt;
74   *
75   *           &lt;Item&gt; 
76   *             &lt;New id="header" class="org.mortbay.jetty.handler.rules.HeaderPatternRule"&gt;
77   *               &lt;Set name="pattern"&gt;*.jsp&lt;/Set&gt;
78   *               &lt;Set name="name"&gt;server&lt;/Set&gt;
79   *               &lt;Set name="value"&gt;dexter webserver&lt;/Set&gt;
80   *             &lt;/New&gt;
81   *           &lt;/Item&gt;
82   *
83   *           &lt;Item&gt; 
84   *             &lt;New id="header" class="org.mortbay.jetty.handler.rules.HeaderPatternRule"&gt;
85   *               &lt;Set name="pattern"&gt;*.jsp&lt;/Set&gt;
86   *               &lt;Set name="name"&gt;title&lt;/Set&gt;
87   *               &lt;Set name="value"&gt;driven header purpose&lt;/Set&gt;
88   *             &lt;/New&gt;
89   *           &lt;/Item&gt;
90   *
91   *           &lt;Item&gt; 
92   *             &lt;New id="redirect" class="org.mortbay.jetty.handler.rules.RedirectPatternRule"&gt;
93   *               &lt;Set name="pattern"&gt;/test/dispatch&lt;/Set&gt;
94   *               &lt;Set name="location"&gt;http://jetty.mortbay.org&lt;/Set&gt;
95   *             &lt;/New&gt;
96   *           &lt;/Item&gt;
97   *
98   *           &lt;Item&gt; 
99   *             &lt;New id="regexRewrite" class="org.mortbay.jetty.handler.rules.RewriteRegexRule"&gt;
100  *               &lt;Set name="regex"&gt;/test-jaas/$&lt;/Set&gt;
101  *               &lt;Set name="replacement"&gt;/demo&lt;/Set&gt;
102  *             &lt;/New&gt;
103  *           &lt;/Item&gt;
104  *
105  *         &lt;/Array&gt;
106  *       &lt;/Set&gt;
107  *
108  *       &lt;Set name="handler"&gt;
109  *         &lt;New id="Handlers" class="org.mortbay.jetty.handler.HandlerCollection"&gt;
110  *           &lt;Set name="handlers"&gt;
111  *            &lt;Array type="org.mortbay.jetty.Handler"&gt;
112  *              &lt;Item&gt;
113  *                &lt;New id="Contexts" class="org.mortbay.jetty.handler.ContextHandlerCollection"/&gt;
114  *              &lt;/Item&gt;
115  *              &lt;Item&gt;
116  *                &lt;New id="DefaultHandler" class="org.mortbay.jetty.handler.DefaultHandler"/&gt;
117  *              &lt;/Item&gt;
118  *              &lt;Item&gt;
119  *                &lt;New id="RequestLog" class="org.mortbay.jetty.handler.RequestLogHandler"/&gt;
120  *              &lt;/Item&gt;
121  *            &lt;/Array&gt;
122  *           &lt;/Set&gt;
123  *         &lt;/New&gt;
124  *       &lt;/Set&gt;
125  *
126  *     &lt;/New&gt;
127  *   &lt;/Set&gt;
128  * </pre>
129  * 
130  */
131 public class RewriteHandler extends HandlerWrapper
132 {
133     private Rule[] _rules;
134     
135 
136     private String _originalPathAttribute;
137     private boolean _rewriteRequestURI=true;
138     private boolean _rewritePathInfo=true;
139     
140    
141     private LegacyRule _legacy;
142 
143     /* ------------------------------------------------------------ */
144     private LegacyRule getLegacyRule()
145     {
146         if (_legacy==null)
147         {
148             _legacy= new LegacyRule();
149             addRule(_legacy);
150         }
151         return _legacy;
152     }
153     
154 
155     /* ------------------------------------------------------------ */
156     /**
157      * To enable configuration from jetty.xml on rewriteRequestURI, rewritePathInfo and
158      * originalPathAttribute
159      * 
160      * @param legacyRule old style rewrite rule
161      */
162     public void setLegacyRule(LegacyRule legacyRule)
163     {
164         _legacy = legacyRule;
165     }
166 
167     /* ------------------------------------------------------------ */
168     /**
169      * Returns the list of rules.
170      * @return an array of {@link Rule}.
171      */
172     public Rule[] getRules()
173     {
174         return _rules;
175     }
176 
177     /* ------------------------------------------------------------ */
178     /**
179      * Assigns the rules to process.
180      * @param rules an array of {@link Rule}. 
181      */
182     public void setRules(Rule[] rules)
183     {
184         if (_legacy==null)
185             _rules = rules;
186         else
187         {
188             _rules=null;
189             addRule(_legacy);
190             if (rules!=null)
191                 for (Rule rule:rules)
192                     addRule(rule);
193         }
194     }
195 
196     /* ------------------------------------------------------------ */
197     /**
198      * Add a Rule
199      * @param rule The rule to add to the end of the rules array
200      */
201     public void addRule(Rule rule)
202     {
203         _rules = (Rule[])LazyList.addToArray(_rules,rule,Rule.class);
204     }
205    
206 
207     /* ------------------------------------------------------------ */
208     /**
209      * @return the rewriteRequestURI If true, this handler will rewrite the value
210      * returned by {@link HttpServletRequest#getRequestURI()}.
211      */
212     public boolean isRewriteRequestURI()
213     {
214         return _rewriteRequestURI;
215     }
216 
217     /* ------------------------------------------------------------ */
218     /**
219      * @param rewriteRequestURI true if this handler will rewrite the value
220      * returned by {@link HttpServletRequest#getRequestURI()}.
221      */
222     public void setRewriteRequestURI(boolean rewriteRequestURI)
223     {
224         _rewriteRequestURI=rewriteRequestURI;
225     }
226 
227     /* ------------------------------------------------------------ */
228     /**
229      * @return true if this handler will rewrite the value
230      * returned by {@link HttpServletRequest#getPathInfo()}.
231      */
232     public boolean isRewritePathInfo()
233     {
234         return _rewritePathInfo;
235     }
236 
237     /* ------------------------------------------------------------ */
238     /**
239      * @param rewritePathInfo true if this handler will rewrite the value
240      * returned by {@link HttpServletRequest#getPathInfo()}.
241      */
242     public void setRewritePathInfo(boolean rewritePathInfo)
243     {
244         _rewritePathInfo=rewritePathInfo;
245     }
246 
247     /* ------------------------------------------------------------ */
248     /**
249      * @return the originalPathAttribte. If non null, this string will be used
250      * as the attribute name to store the original request path.
251      */
252     public String getOriginalPathAttribute()
253     {
254         return _originalPathAttribute;
255     }
256 
257     /* ------------------------------------------------------------ */
258     /**
259      * @param originalPathAttribte If non null, this string will be used
260      * as the attribute name to store the original request path.
261      */
262     public void setOriginalPathAttribute(String originalPathAttribte)
263     {
264         _originalPathAttribute=originalPathAttribte;
265     }
266 
267 
268     /* ------------------------------------------------------------ */
269     /**
270      * @deprecated 
271      */
272     public PathMap getRewrite()
273     {
274         return getLegacyRule().getRewrite();
275     }
276 
277     /* ------------------------------------------------------------ */
278     /**
279      * @deprecated
280      */
281     public void setRewrite(PathMap rewrite)
282     {
283         getLegacyRule().setRewrite(rewrite);
284     }
285 
286     /* ------------------------------------------------------------ */
287     /**
288      * @deprecated
289      */
290     public void addRewriteRule(String pattern, String prefix)
291     {
292         getLegacyRule().addRewriteRule(pattern,prefix);
293     }
294     
295     /* ------------------------------------------------------------ */
296     /* (non-Javadoc)
297      * @see org.mortbay.jetty.handler.HandlerWrapper#handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
298      */
299     public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException, ServletException
300     {
301         if (isStarted())
302         {
303             boolean handled=false;
304                 
305             boolean original_set=_originalPathAttribute==null;
306                 
307             for (int i = 0, len= _rules.length; i < len; i++)
308             {
309                 Rule rule = _rules[i];
310                
311                 String applied=rule.matchAndApply(target,request, response);
312                 if (applied!=null)
313                 {       
314                     Log.debug("applied {}",rule);
315                     if (!target.equals(applied))
316                     { 
317                         Log.debug("rewrote {} to {}",target,applied);
318                         if (!original_set)
319                         {
320                             original_set=true;
321                             request.setAttribute(_originalPathAttribute, target);
322                         }     
323                         
324                         if (_rewriteRequestURI)
325                             ((Request)request).setRequestURI(applied);
326 
327                         if (_rewritePathInfo)
328                             ((Request)request).setPathInfo(applied);
329 
330                         target=applied;
331                     }
332                     
333                     if (rule.isHandling())
334                     {
335                         Log.debug("handling {}",rule);
336                         handled=true;
337                         (request instanceof Request?(Request)request:HttpConnection.getCurrentConnection().getRequest()).setHandled(true);
338                     }
339 
340                     if (rule.isTerminating())
341                     {
342                         Log.debug("terminating {}",rule);
343                         break;
344                     }
345                 }
346             }
347             
348             if (!handled)
349             {
350                 super.handle(target, request, response, dispatch);
351             }
352         }
353     }
354     
355 }