1 /*
2  * JBoss, Home of Professional Open Source.
3  * Copyright 2014 Red Hat, Inc., and individual contributors
4  * as indicated by the @author tags.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */

18
19 package io.undertow.servlet.handlers;
20
21 import io.undertow.UndertowMessages;
22 import io.undertow.util.SubstringMap;
23
24 import java.util.HashMap;
25 import java.util.Map;
26
27 /**
28  * Class that maintains the complete set of servlet path matches.
29  *
30  *
31  * @author Stuart Douglas
32  */

33 class ServletPathMatchesData {
34
35     private final Map<String, ServletPathMatch> exactPathMatches;
36
37     private final SubstringMap<PathMatch> prefixMatches;
38
39     private final Map<String, ServletChain> nameMatches;
40
41     ServletPathMatchesData(final Map<String, ServletChain> exactPathMatches, final SubstringMap<PathMatch> prefixMatches, final Map<String, ServletChain> nameMatches) {
42         this.prefixMatches = prefixMatches;
43         this.nameMatches = nameMatches;
44         Map<String, ServletPathMatch> newExactPathMatches = new HashMap<>();
45         for (Map.Entry<String, ServletChain> entry : exactPathMatches.entrySet()) {
46             newExactPathMatches.put(entry.getKey(), new ServletPathMatch(entry.getValue(), entry.getKey(), entry.getValue().isDefaultServletMapping()));
47         }
48         this.exactPathMatches = newExactPathMatches;
49
50     }
51
52     public ServletChain getServletHandlerByName(final String name) {
53         return nameMatches.get(name);
54     }
55
56     public ServletPathMatch getServletHandlerByExactPath(final String path) {
57         return exactPathMatches.get(path);
58     }
59
60     public ServletPathMatch getServletHandlerByPath(final String path) {
61         ServletPathMatch exact = exactPathMatches.get(path);
62         if (exact != null) {
63             return exact;
64         }
65         SubstringMap.SubstringMatch<PathMatch> match = prefixMatches.get(path, path.length());
66         if (match != null) {
67             return handleMatch(path, match.getValue(), path.lastIndexOf('.'));
68         }
69         int extensionPos = -1;
70         for (int i = path.length() - 1; i >= 0; --i) {
71             final char c = path.charAt(i);
72              if (c == '/') {
73                 match = prefixMatches.get(path, i);
74                 if (match != null) {
75                     return handleMatch(path, match.getValue(), extensionPos);
76                 }
77             } else if (c == '.' && extensionPos == -1) {
78                     extensionPos = i;
79             }
80         }
81         //this should never happen
82         //as the default servlet is aways registered under /*
83         throw UndertowMessages.MESSAGES.servletPathMatchFailed();
84     }
85
86     private ServletPathMatch handleMatch(final String path, final PathMatch match, final int extensionPos) {
87         if (match.extensionMatches.isEmpty()) {
88             return new ServletPathMatch(match.defaultHandler, path, match.requireWelcomeFileMatch);
89         }
90         if (extensionPos == -1) {
91             return new ServletPathMatch(match.defaultHandler, path, match.requireWelcomeFileMatch);
92         }
93         final String ext;
94         ext = path.substring(extensionPos + 1, path.length());
95         ServletChain handler = match.extensionMatches.get(ext);
96         if (handler != null) {
97             return new ServletPathMatch(handler, path, handler.getManagedServlet().getServletInfo().isRequireWelcomeFileMapping());
98         }
99         return new ServletPathMatch(match.defaultHandler, path, match.requireWelcomeFileMatch);
100     }
101
102     public static Builder builder() {
103         return new Builder();
104     }
105
106     public static final class Builder {
107
108         private final Map<String, ServletChain> exactPathMatches = new HashMap<>();
109
110         private final SubstringMap<PathMatch> prefixMatches = new SubstringMap<PathMatch>();
111
112         private final Map<String, ServletChain> nameMatches = new HashMap<>();
113
114         public void addExactMatch(final String exactMatch, final ServletChain match) {
115             exactPathMatches.put(exactMatch, match);
116         }
117
118         public void addPrefixMatch(final String prefix, final ServletChain match, final boolean requireWelcomeFileMatch) {
119             SubstringMap.SubstringMatch<PathMatch> mt = prefixMatches.get(prefix);
120             PathMatch m;
121             if (mt == null) {
122                 prefixMatches.put(prefix, m = new PathMatch(match));
123             } else {
124                 m = mt.getValue();
125             }
126             m.defaultHandler = match;
127             m.requireWelcomeFileMatch = requireWelcomeFileMatch;
128         }
129
130         public void addExtensionMatch(final String prefix, final String extension, final ServletChain match) {
131             SubstringMap.SubstringMatch<PathMatch> mt = prefixMatches.get(prefix);
132             PathMatch m;
133             if (mt == null) {
134                 prefixMatches.put(prefix, m = new PathMatch(null));
135             } else {
136                 m = mt.getValue();
137             }
138             m.extensionMatches.put(extension, match);
139         }
140
141         public void addNameMatch(final String name, final ServletChain match) {
142             nameMatches.put(name, match);
143         }
144
145         public ServletPathMatchesData build() {
146             return new ServletPathMatchesData(exactPathMatches, prefixMatches, nameMatches);
147         }
148
149     }
150
151
152     private static class PathMatch {
153
154         private final Map<String, ServletChain> extensionMatches = new HashMap<>();
155         private volatile ServletChain defaultHandler;
156         private volatile boolean requireWelcomeFileMatch;
157
158         PathMatch(final ServletChain defaultHandler) {
159             this.defaultHandler = defaultHandler;
160         }
161     }
162
163
164 }
165