1 // ========================================================================
2 // Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // All rights reserved. This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v1.0
6 // and Apache License v2.0 which accompanies this distribution.
7 // The Eclipse Public License is available at
8 // http://www.eclipse.org/legal/epl-v10.html
9 // The Apache License v2.0 is available at
10 // http://www.opensource.org/licenses/apache2.0.php
11 // You may elect to redistribute this code under either of these licenses.
12 // ========================================================================
13
14 package org.eclipse.jetty.servlets;
15
16 import java.io.IOException;
17 import java.util.Map;
18 import java.util.concurrent.ConcurrentHashMap;
19 import java.util.regex.Matcher;
20 import java.util.regex.Pattern;
21
22 import javax.servlet.Filter;
23 import javax.servlet.FilterChain;
24 import javax.servlet.FilterConfig;
25 import javax.servlet.ServletException;
26 import javax.servlet.ServletRequest;
27 import javax.servlet.ServletResponse;
28 import javax.servlet.http.HttpServletRequest;
29
30 /* ------------------------------------------------------------ */
31 /** User Agent Filter.
32 * <p>
33 * This filter allows efficient matching of user agent strings for
34 * downstream or extended filters to use for browser specific logic.
35 * </p>
36 * <p>
37 * The filter is configured with the following init parameters:
38 * <dl>
39 * <dt>attribute</dt><dd>If set, then the request attribute of this name is set with the matched user agent string</dd>
40 * <dt>cacheSize</dt><dd>The size of the user-agent cache, used to avoid reparsing of user agent strings. The entire cache is flushed
41 * when this size is reached</dd>
42 * <dt>userAgent</dt><dd>A regex {@link Pattern} to extract the essential elements of the user agent.
43 * The concatenation of matched pattern groups is used as the user agent name</dd>
44 * <dl>
45 * An example value for pattern is <code>(?:Mozilla[^\(]*\(compatible;\s*+([^;]*);.*)|(?:.*?([^\s]+/[^\s]+).*)</code>. These two
46 * pattern match the common compatibility user-agent strings and extract the real user agent, failing that, the first
47 * element of the agent string is returned.
48 *
49 *
50 */
51 public class UserAgentFilter implements Filter
52 {
53 private Pattern _pattern;
54 private Map _agentCache = new ConcurrentHashMap();
55 private int _agentCacheSize=1024;
56 private String _attribute;
57
58 /* ------------------------------------------------------------ */
59 /* (non-Javadoc)
60 * @see javax.servlet.Filter#destroy()
61 */
62 public void destroy()
63 {
64 }
65
66 /* ------------------------------------------------------------ */
67 /* (non-Javadoc)
68 * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
69 */
70 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
71 {
72 if (_attribute!=null && _pattern!=null)
73 {
74 String ua=getUserAgent(request);
75 request.setAttribute(_attribute,ua);
76 }
77 chain.doFilter(request,response);
78 }
79
80 /* ------------------------------------------------------------ */
81 /* (non-Javadoc)
82 * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
83 */
84 public void init(FilterConfig filterConfig) throws ServletException
85 {
86 _attribute=filterConfig.getInitParameter("attribute");
87
88 String p=filterConfig.getInitParameter("userAgent");
89 if (p!=null)
90 _pattern=Pattern.compile(p);
91
92 String size=filterConfig.getInitParameter("cacheSize");
93 if (size!=null)
94 _agentCacheSize=Integer.parseInt(size);
95 }
96
97 /* ------------------------------------------------------------ */
98 public String getUserAgent(ServletRequest request)
99 {
100 String ua=((HttpServletRequest)request).getHeader("User-Agent");
101 return getUserAgent(ua);
102 }
103
104 /* ------------------------------------------------------------ */
105 /** Get UserAgent.
106 * The configured agent patterns are used to match against the passed user agent string.
107 * If any patterns match, the concatenation of pattern groups is returned as the user agent
108 * string. Match results are cached.
109 * @param ua A user agent string
110 * @return The matched pattern groups or the original user agent string
111 */
112 public String getUserAgent(String ua)
113 {
114 if (ua==null)
115 return null;
116
117 String tag = (String)_agentCache.get(ua);
118
119
120 if (tag==null)
121 {
122 Matcher matcher=_pattern.matcher(ua);
123 if (matcher.matches())
124 {
125 if(matcher.groupCount()>0)
126 {
127 for (int g=1;g<=matcher.groupCount();g++)
128 {
129 String group=matcher.group(g);
130 if (group!=null)
131 tag=tag==null?group:(tag+group);
132 }
133 }
134 else
135 tag=matcher.group();
136 }
137 else
138 tag=ua;
139
140 if (_agentCache.size()>=_agentCacheSize)
141 _agentCache.clear();
142 _agentCache.put(ua,tag);
143
144 }
145 return tag;
146 }
147 }