View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.http.pathmap;
20  
21  import org.eclipse.jetty.util.URIUtil;
22  
23  public class ServletPathSpec extends PathSpec
24  {
25      public ServletPathSpec(String servletPathSpec)
26      {
27          super();
28          assertValidServletPathSpec(servletPathSpec);
29  
30          // The Root Path Spec
31          if ((servletPathSpec == null) || (servletPathSpec.length() == 0))
32          {
33              super.pathSpec = "";
34              super.pathDepth = -1; // force this to be at the end of the sort order
35              this.specLength = 1;
36              this.group = PathSpecGroup.ROOT;
37              return;
38          }
39  
40          // The Default Path Spec
41          if("/".equals(servletPathSpec))
42          {
43              super.pathSpec = "/";
44              super.pathDepth = -1; // force this to be at the end of the sort order
45              this.specLength = 1;
46              this.group = PathSpecGroup.DEFAULT;
47              return;
48          }
49          
50          this.specLength = servletPathSpec.length();
51          super.pathDepth = 0;
52          char lastChar = servletPathSpec.charAt(specLength - 1);
53          // prefix based
54          if ((servletPathSpec.charAt(0) == '/') && (specLength > 1) && (lastChar == '*'))
55          {
56              this.group = PathSpecGroup.PREFIX_GLOB;
57          }
58          // suffix based
59          else if (servletPathSpec.charAt(0) == '*')
60          {
61              this.group = PathSpecGroup.SUFFIX_GLOB;
62          }
63          else
64          {
65              this.group = PathSpecGroup.EXACT;
66          }
67  
68          for (int i = 0; i < specLength; i++)
69          {
70              int cp = servletPathSpec.codePointAt(i);
71              if (cp < 128)
72              {
73                  char c = (char)cp;
74                  switch (c)
75                  {
76                      case '/':
77                          super.pathDepth++;
78                          break;
79                  }
80              }
81          }
82  
83          super.pathSpec = servletPathSpec;
84      }
85  
86      private void assertValidServletPathSpec(String servletPathSpec)
87      {
88          if ((servletPathSpec == null) || servletPathSpec.equals(""))
89          {
90              return; // empty path spec
91          }
92  
93          int len = servletPathSpec.length();
94          // path spec must either start with '/' or '*.'
95          if (servletPathSpec.charAt(0) == '/')
96          {
97              // Prefix Based
98              if (len == 1)
99              {
100                 return; // simple '/' path spec
101             }
102             int idx = servletPathSpec.indexOf('*');
103             if (idx < 0)
104             {
105                 return; // no hit on glob '*'
106             }
107             // only allowed to have '*' at the end of the path spec
108             if (idx != (len - 1))
109             {
110                 throw new IllegalArgumentException("Servlet Spec 12.2 violation: glob '*' can only exist at end of prefix based matches: bad spec \""+ servletPathSpec +"\"");
111             }
112         }
113         else if (servletPathSpec.startsWith("*."))
114         {
115             // Suffix Based
116             int idx = servletPathSpec.indexOf('/');
117             // cannot have path separator
118             if (idx >= 0)
119             {
120                 throw new IllegalArgumentException("Servlet Spec 12.2 violation: suffix based path spec cannot have path separators: bad spec \""+ servletPathSpec +"\"");
121             }
122 
123             idx = servletPathSpec.indexOf('*',2);
124             // only allowed to have 1 glob '*', at the start of the path spec
125             if (idx >= 1)
126             {
127                 throw new IllegalArgumentException("Servlet Spec 12.2 violation: suffix based path spec cannot have multiple glob '*': bad spec \""+ servletPathSpec +"\"");
128             }
129         }
130         else
131         {
132             throw new IllegalArgumentException("Servlet Spec 12.2 violation: path spec must start with \"/\" or \"*.\": bad spec \""+ servletPathSpec +"\"");
133         }
134     }
135 
136     @Override
137     public String getPathInfo(String path)
138     {
139         // Path Info only valid for PREFIX_GLOB types
140         if (group == PathSpecGroup.PREFIX_GLOB)
141         {
142             if (path.length() == (specLength - 2))
143             {
144                 return null;
145             }
146             return path.substring(specLength - 2);
147         }
148 
149         return null;
150     }
151 
152     @Override
153     public String getPathMatch(String path)
154     {
155         switch (group)
156         {
157             case EXACT:
158                 if (pathSpec.equals(path))
159                 {
160                     return path;
161                 }
162                 else
163                 {
164                     return null;
165                 }
166             case PREFIX_GLOB:
167                 if (isWildcardMatch(path))
168                 {
169                     return path.substring(0,specLength - 2);
170                 }
171                 else
172                 {
173                     return null;
174                 }
175             case SUFFIX_GLOB:
176                 if (path.regionMatches(path.length() - (specLength - 1),pathSpec,1,specLength - 1))
177                 {
178                     return path;
179                 }
180                 else
181                 {
182                     return null;
183                 }
184             case DEFAULT:
185                 return path;
186             default:
187                 return null;
188         }
189     }
190 
191     @Override
192     public String getRelativePath(String base, String path)
193     {
194         String info = getPathInfo(path);
195         if (info == null)
196         {
197             info = path;
198         }
199 
200         if (info.startsWith("./"))
201         {
202             info = info.substring(2);
203         }
204         if (base.endsWith(URIUtil.SLASH))
205         {
206             if (info.startsWith(URIUtil.SLASH))
207             {
208                 path = base + info.substring(1);
209             }
210             else
211             {
212                 path = base + info;
213             }
214         }
215         else if (info.startsWith(URIUtil.SLASH))
216         {
217             path = base + info;
218         }
219         else
220         {
221             path = base + URIUtil.SLASH + info;
222         }
223         return path;
224     }
225 
226     private boolean isWildcardMatch(String path)
227     {
228         // For a spec of "/foo/*" match "/foo" , "/foo/..." but not "/foobar"
229         int cpl = specLength - 2;
230         if ((group == PathSpecGroup.PREFIX_GLOB) && (path.regionMatches(0,pathSpec,0,cpl)))
231         {
232             if ((path.length() == cpl) || ('/' == path.charAt(cpl)))
233             {
234                 return true;
235             }
236         }
237         return false;
238     }
239 
240     @Override
241     public boolean matches(String path)
242     {
243         switch (group)
244         {
245             case EXACT:
246                 return pathSpec.equals(path);
247             case PREFIX_GLOB:
248                 return isWildcardMatch(path);
249             case SUFFIX_GLOB:
250                 return path.regionMatches((path.length() - specLength) + 1,pathSpec,1,specLength - 1);
251             case ROOT:
252                 // Only "/" matches
253                 return ("/".equals(path));
254             case DEFAULT:
255                 // If we reached this point, then everything matches
256                 return true;
257             default:
258                 return false;
259         }
260     }
261 }