View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2015 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.websocket.server.pathmap;
20  
21  import org.eclipse.jetty.util.URIUtil;
22  
23  public class ServletPathSpec extends PathSpec
24  {
25      public static final String PATH_SPEC_SEPARATORS = ":,";
26  
27      /**
28       * Get multi-path spec splits.
29       * 
30       * @param servletPathSpec
31       *            the path spec that might contain multiple declared path specs
32       * @return the individual path specs found.
33       */
34      public static ServletPathSpec[] getMultiPathSpecs(String servletPathSpec)
35      {
36          String pathSpecs[] = servletPathSpec.split(PATH_SPEC_SEPARATORS);
37          int len = pathSpecs.length;
38          ServletPathSpec sps[] = new ServletPathSpec[len];
39          for (int i = 0; i < len; i++)
40          {
41              sps[i] = new ServletPathSpec(pathSpecs[i]);
42          }
43          return sps;
44      }
45  
46      public ServletPathSpec(String servletPathSpec)
47      {
48          super();
49          assertValidServletPathSpec(servletPathSpec);
50  
51          // The Path Spec for Default Servlet
52          if ((servletPathSpec == null) || (servletPathSpec.length() == 0) || "/".equals(servletPathSpec))
53          {
54              super.pathSpec = "/";
55              super.pathDepth = -1; // force this to be last in sort order
56              this.specLength = 1;
57              this.group = PathSpecGroup.DEFAULT;
58              return;
59          }
60  
61          this.specLength = servletPathSpec.length();
62          super.pathDepth = 0;
63          char lastChar = servletPathSpec.charAt(specLength - 1);
64          // prefix based
65          if ((servletPathSpec.charAt(0) == '/') && (specLength > 1) && (lastChar == '*'))
66          {
67              this.group = PathSpecGroup.PREFIX_GLOB;
68          }
69          // suffix based
70          else if (servletPathSpec.charAt(0) == '*')
71          {
72              this.group = PathSpecGroup.SUFFIX_GLOB;
73          }
74          else
75          {
76              this.group = PathSpecGroup.EXACT;
77          }
78  
79          for (int i = 0; i < specLength; i++)
80          {
81              int cp = servletPathSpec.codePointAt(i);
82              if (cp < 128)
83              {
84                  char c = (char)cp;
85                  switch (c)
86                  {
87                      case '/':
88                          super.pathDepth++;
89                          break;
90                  }
91              }
92          }
93  
94          super.pathSpec = servletPathSpec;
95      }
96  
97      private void assertValidServletPathSpec(String servletPathSpec)
98      {
99          if ((servletPathSpec == null) || servletPathSpec.equals(""))
100         {
101             return; // empty path spec
102         }
103 
104         // Ensure we don't have path spec separators here in our single path spec.
105         for (char c : PATH_SPEC_SEPARATORS.toCharArray())
106         {
107             if (servletPathSpec.indexOf(c) >= 0)
108             {
109                 throw new IllegalArgumentException("Servlet Spec 12.2 violation: encountered Path Spec Separator [" + PATH_SPEC_SEPARATORS
110                         + "] within specified path spec. did you forget to split this path spec up?");
111             }
112         }
113 
114         int len = servletPathSpec.length();
115         // path spec must either start with '/' or '*.'
116         if (servletPathSpec.charAt(0) == '/')
117         {
118             // Prefix Based
119             if (len == 1)
120             {
121                 return; // simple '/' path spec
122             }
123             int idx = servletPathSpec.indexOf('*');
124             if (idx < 0)
125             {
126                 return; // no hit on glob '*'
127             }
128             // only allowed to have '*' at the end of the path spec
129             if (idx != (len - 1))
130             {
131                 throw new IllegalArgumentException("Servlet Spec 12.2 violation: glob '*' can only exist at end of prefix based matches");
132             }
133         }
134         else if (servletPathSpec.startsWith("*."))
135         {
136             // Suffix Based
137             int idx = servletPathSpec.indexOf('/');
138             // cannot have path separator
139             if (idx >= 0)
140             {
141                 throw new IllegalArgumentException("Servlet Spec 12.2 violation: suffix based path spec cannot have path separators");
142             }
143 
144             idx = servletPathSpec.indexOf('*',2);
145             // only allowed to have 1 glob '*', at the start of the path spec
146             if (idx >= 1)
147             {
148                 throw new IllegalArgumentException("Servlet Spec 12.2 violation: suffix based path spec cannot have multiple glob '*'");
149             }
150         }
151         else
152         {
153             throw new IllegalArgumentException("Servlet Spec 12.2 violation: path spec must start with \"/\" or \"*.\"");
154         }
155     }
156 
157     @Override
158     public String getPathInfo(String path)
159     {
160         // Path Info only valid for PREFIX_GLOB types
161         if (group == PathSpecGroup.PREFIX_GLOB)
162         {
163             if (path.length() == (specLength - 2))
164             {
165                 return null;
166             }
167             return path.substring(specLength - 2);
168         }
169 
170         return null;
171     }
172 
173     @Override
174     public String getPathMatch(String path)
175     {
176         switch (group)
177         {
178             case EXACT:
179                 if (pathSpec.equals(path))
180                 {
181                     return path;
182                 }
183                 else
184                 {
185                     return null;
186                 }
187             case PREFIX_GLOB:
188                 if (isWildcardMatch(path))
189                 {
190                     return path.substring(0,specLength - 2);
191                 }
192                 else
193                 {
194                     return null;
195                 }
196             case SUFFIX_GLOB:
197                 if (path.regionMatches(path.length() - (specLength - 1),pathSpec,1,specLength - 1))
198                 {
199                     return path;
200                 }
201                 else
202                 {
203                     return null;
204                 }
205             case DEFAULT:
206                 return path;
207             default:
208                 return null;
209         }
210     }
211 
212     @Override
213     public String getRelativePath(String base, String path)
214     {
215         String info = getPathInfo(path);
216         if (info == null)
217         {
218             info = path;
219         }
220 
221         if (info.startsWith("./"))
222         {
223             info = info.substring(2);
224         }
225         if (base.endsWith(URIUtil.SLASH))
226         {
227             if (info.startsWith(URIUtil.SLASH))
228             {
229                 path = base + info.substring(1);
230             }
231             else
232             {
233                 path = base + info;
234             }
235         }
236         else if (info.startsWith(URIUtil.SLASH))
237         {
238             path = base + info;
239         }
240         else
241         {
242             path = base + URIUtil.SLASH + info;
243         }
244         return path;
245     }
246 
247     private boolean isExactMatch(String path)
248     {
249         if (group == PathSpecGroup.EXACT)
250         {
251             if (pathSpec.equals(path))
252             {
253                 return true;
254             }
255             return (path.charAt(path.length() - 1) == '/') && (path.equals(pathSpec + '/'));
256         }
257         return false;
258     }
259 
260     private boolean isWildcardMatch(String path)
261     {
262         // For a spec of "/foo/*" match "/foo" , "/foo/..." but not "/foobar"
263         int cpl = specLength - 2;
264         if ((group == PathSpecGroup.PREFIX_GLOB) && (path.regionMatches(0,pathSpec,0,cpl)))
265         {
266             if ((path.length() == cpl) || ('/' == path.charAt(cpl)))
267             {
268                 return true;
269             }
270         }
271         return false;
272     }
273 
274     @Override
275     public boolean matches(String path)
276     {
277         switch (group)
278         {
279             case EXACT:
280                 return isExactMatch(path);
281             case PREFIX_GLOB:
282                 return isWildcardMatch(path);
283             case SUFFIX_GLOB:
284                 return path.regionMatches((path.length() - specLength) + 1,pathSpec,1,specLength - 1);
285             case DEFAULT:
286                 return true;
287             default:
288                 return false;
289         }
290     }
291 }