1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.eclipse.jetty.spdy.http;
18
19 import java.util.Arrays;
20 import java.util.Collections;
21 import java.util.LinkedHashSet;
22 import java.util.List;
23 import java.util.Set;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.ConcurrentMap;
26 import java.util.regex.Pattern;
27
28 import org.eclipse.jetty.spdy.api.Headers;
29 import org.eclipse.jetty.spdy.api.Stream;
30 import org.eclipse.jetty.util.log.Log;
31 import org.eclipse.jetty.util.log.Logger;
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public class ReferrerPushStrategy implements PushStrategy
59 {
60 private static final Logger logger = Log.getLogger(ReferrerPushStrategy.class);
61 private final ConcurrentMap<String, Set<String>> resources = new ConcurrentHashMap<>();
62 private final Set<Pattern> pushRegexps = new LinkedHashSet<>();
63 private final Set<Pattern> allowedPushOrigins = new LinkedHashSet<>();
64
65 public ReferrerPushStrategy()
66 {
67 this(Arrays.asList(".*\\.css", ".*\\.js", ".*\\.png", ".*\\.jpg", ".*\\.gif"));
68 }
69
70 public ReferrerPushStrategy(List<String> pushRegexps)
71 {
72 this(pushRegexps, Collections.<String>emptyList());
73 }
74
75 public ReferrerPushStrategy(List<String> pushRegexps, List<String> allowedPushOrigins)
76 {
77 for (String pushRegexp : pushRegexps)
78 this.pushRegexps.add(Pattern.compile(pushRegexp));
79 for (String allowedPushOrigin : allowedPushOrigins)
80 this.allowedPushOrigins.add(Pattern.compile(allowedPushOrigin.replace(".", "\\.").replace("*", ".*")));
81 }
82
83 @Override
84 public Set<String> apply(Stream stream, Headers requestHeaders, Headers responseHeaders)
85 {
86 Set<String> result = Collections.emptySet();
87 String scheme = requestHeaders.get("scheme").value();
88 String host = requestHeaders.get("host").value();
89 String origin = new StringBuilder(scheme).append("://").append(host).toString();
90 String url = requestHeaders.get("url").value();
91 String absoluteURL = new StringBuilder(origin).append(url).toString();
92 logger.debug("Applying push strategy for {}", absoluteURL);
93 if (isValidMethod(requestHeaders.get("method").value()))
94 {
95 if (isMainResource(url, responseHeaders))
96 {
97 result = pushResources(absoluteURL);
98 }
99 else if (isPushResource(url, responseHeaders))
100 {
101 Headers.Header referrerHeader = requestHeaders.get("referer");
102 if (referrerHeader != null)
103 {
104 String referrer = referrerHeader.value();
105 Set<String> pushResources = resources.get(referrer);
106 if (pushResources == null || !pushResources.contains(url))
107 buildMetadata(origin, url, referrer);
108 else
109 result = pushResources(absoluteURL);
110 }
111 }
112 }
113 logger.debug("Push resources for {}: {}", absoluteURL, result);
114 return result;
115 }
116
117 private boolean isValidMethod(String method)
118 {
119 return "GET".equalsIgnoreCase(method);
120 }
121
122 private boolean isMainResource(String url, Headers responseHeaders)
123 {
124 return !isPushResource(url, responseHeaders);
125 }
126
127 private boolean isPushResource(String url, Headers responseHeaders)
128 {
129 for (Pattern pushRegexp : pushRegexps)
130 {
131 if (pushRegexp.matcher(url).matches())
132 return true;
133 }
134 return false;
135 }
136
137 private Set<String> pushResources(String absoluteURL)
138 {
139 Set<String> pushResources = resources.get(absoluteURL);
140 if (pushResources == null)
141 return Collections.emptySet();
142 return Collections.unmodifiableSet(pushResources);
143 }
144
145 private void buildMetadata(String origin, String url, String referrer)
146 {
147 if (referrer.startsWith(origin) || isPushOriginAllowed(origin))
148 {
149 Set<String> pushResources = resources.get(referrer);
150 if (pushResources == null)
151 {
152 pushResources = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
153 Set<String> existing = resources.putIfAbsent(referrer, pushResources);
154 if (existing != null)
155 pushResources = existing;
156 }
157 pushResources.add(url);
158 logger.debug("Built push metadata for {}: {}", referrer, pushResources);
159 }
160 }
161
162 private boolean isPushOriginAllowed(String origin)
163 {
164 for (Pattern allowedPushOrigin : allowedPushOrigins)
165 {
166 if (allowedPushOrigin.matcher(origin).matches())
167 return true;
168 }
169 return false;
170 }
171 }