View | Details | Raw Unified | Return to bug 55046
Collapse All | Expand All

(-)test/org/apache/catalina/filters/TesterFilterChain.java (+36 lines)
Line 0 Link Here
1
/**
2
 * Copyright 2012-2013 eBay Software Foundation, All Rights Reserved.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5
 * use this file except in compliance with the License. You may obtain a copy of
6
 * the License at
7
 *
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
 * License for the specific language governing permissions and limitations under
14
 * the License.
15
 */
16
package org.apache.catalina.filters;
17
18
import java.io.IOException;
19
20
import javax.servlet.FilterChain;
21
import javax.servlet.ServletException;
22
import javax.servlet.ServletRequest;
23
import javax.servlet.ServletResponse;
24
25
/**
26
 * A mock {@link FilterChain}.
27
 */
28
public class TesterFilterChain implements FilterChain {
29
30
    @Override
31
    public void doFilter(ServletRequest request, ServletResponse response)
32
            throws IOException, ServletException {
33
        // NoOp
34
    }
35
36
}
0
  + native
37
  + native
(-)test/org/apache/catalina/filters/TesterServletContext.java (+316 lines)
Line 0 Link Here
1
/**
2
 * Copyright 2012-2013 eBay Software Foundation, All Rights Reserved.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5
 * use this file except in compliance with the License. You may obtain a copy of
6
 * the License at
7
 *
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
 * License for the specific language governing permissions and limitations under
14
 * the License.
15
 */
16
package org.apache.catalina.filters;
17
18
import java.io.InputStream;
19
import java.net.MalformedURLException;
20
import java.net.URL;
21
import java.util.Enumeration;
22
import java.util.EventListener;
23
import java.util.Map;
24
import java.util.Set;
25
26
import javax.servlet.Filter;
27
import javax.servlet.FilterRegistration;
28
import javax.servlet.RequestDispatcher;
29
import javax.servlet.Servlet;
30
import javax.servlet.ServletContext;
31
import javax.servlet.ServletException;
32
import javax.servlet.ServletRegistration;
33
import javax.servlet.ServletRegistration.Dynamic;
34
import javax.servlet.SessionCookieConfig;
35
import javax.servlet.SessionTrackingMode;
36
import javax.servlet.descriptor.JspConfigDescriptor;
37
38
/**
39
 * A Mock {@link ServletContext}.
40
 *
41
 * @author mosoni
42
 *
43
 */
44
public class TesterServletContext implements ServletContext {
45
46
    @Override
47
    public String getContextPath() {
48
        throw new RuntimeException("Not implemented");
49
    }
50
51
    @Override
52
    public ServletContext getContext(String uripath) {
53
        throw new RuntimeException("Not implemented");
54
    }
55
56
    @Override
57
    public int getMajorVersion() {
58
        throw new RuntimeException("Not implemented");
59
    }
60
61
    @Override
62
    public int getMinorVersion() {
63
        throw new RuntimeException("Not implemented");
64
    }
65
66
    @Override
67
    public String getMimeType(String file) {
68
        throw new RuntimeException("Not implemented");
69
    }
70
71
    @Override
72
    public Set<String> getResourcePaths(String path) {
73
        throw new RuntimeException("Not implemented");
74
    }
75
76
    @Override
77
    public URL getResource(String path) throws MalformedURLException {
78
        throw new RuntimeException("Not implemented");
79
    }
80
81
    @Override
82
    public InputStream getResourceAsStream(String path) {
83
        throw new RuntimeException("Not implemented");
84
    }
85
86
    @Override
87
    public RequestDispatcher getRequestDispatcher(String path) {
88
        throw new RuntimeException("Not implemented");
89
    }
90
91
    @Override
92
    public RequestDispatcher getNamedDispatcher(String name) {
93
94
        throw new RuntimeException("Not implemented");
95
    }
96
97
    @Override
98
    public Servlet getServlet(String name) throws ServletException {
99
100
        throw new RuntimeException("Not implemented");
101
    }
102
103
    @Override
104
    public Enumeration<Servlet> getServlets() {
105
        throw new RuntimeException("Not implemented");
106
    }
107
108
    @Override
109
    public Enumeration<String> getServletNames() {
110
        throw new RuntimeException("Not implemented");
111
    }
112
113
    @Override
114
    public void log(String msg) {
115
        // NOOP
116
    }
117
118
    @Override
119
    public void log(Exception exception, String msg) {
120
        // NOOP
121
    }
122
123
    @Override
124
    public void log(String message, Throwable throwable) {
125
        // NOOP
126
    }
127
128
    @Override
129
    public String getRealPath(String path) {
130
        throw new RuntimeException("Not implemented");
131
    }
132
133
    @Override
134
    public String getServerInfo() {
135
        throw new RuntimeException("Not implemented");
136
    }
137
138
    @Override
139
    public String getInitParameter(String name) {
140
        throw new RuntimeException("Not implemented");
141
    }
142
143
    @Override
144
    public Enumeration<String> getInitParameterNames() {
145
        throw new RuntimeException("Not implemented");
146
    }
147
148
    @Override
149
    public Object getAttribute(String name) {
150
        throw new RuntimeException("Not implemented");
151
    }
152
153
    @Override
154
    public Enumeration<String> getAttributeNames() {
155
        throw new RuntimeException("Not implemented");
156
    }
157
158
    @Override
159
    public void setAttribute(String name, Object object) {
160
        throw new RuntimeException("Not implemented");
161
    }
162
163
    @Override
164
    public void removeAttribute(String name) {
165
        throw new RuntimeException("Not implemented");
166
    }
167
168
    @Override
169
    public String getServletContextName() {
170
        throw new RuntimeException("Not implemented");
171
    }
172
173
    @Override
174
    public int getEffectiveMajorVersion() {
175
        throw new RuntimeException("Not implemented");
176
    }
177
178
    @Override
179
    public int getEffectiveMinorVersion() {
180
        throw new RuntimeException("Not implemented");
181
    }
182
183
    @Override
184
    public boolean setInitParameter(String name, String value) {
185
        throw new RuntimeException("Not implemented");
186
    }
187
188
    @Override
189
    public Dynamic addServlet(String servletName, String className) {
190
        throw new RuntimeException("Not implemented");
191
    }
192
193
    @Override
194
    public Dynamic addServlet(String servletName, Servlet servlet) {
195
        throw new RuntimeException("Not implemented");
196
    }
197
198
    @Override
199
    public Dynamic addServlet(String servletName,
200
            Class<? extends Servlet> servletClass) {
201
        throw new RuntimeException("Not implemented");
202
    }
203
204
    @Override
205
    public <T extends Servlet> T createServlet(Class<T> c)
206
            throws ServletException {
207
        throw new RuntimeException("Not implemented");
208
    }
209
210
    @Override
211
    public ServletRegistration getServletRegistration(String servletName) {
212
        throw new RuntimeException("Not implemented");
213
    }
214
215
    @Override
216
    public Map<String, ? extends ServletRegistration> getServletRegistrations() {
217
        throw new RuntimeException("Not implemented");
218
    }
219
220
    @Override
221
    public javax.servlet.FilterRegistration.Dynamic addFilter(
222
            String filterName, String className) {
223
        throw new RuntimeException("Not implemented");
224
    }
225
226
    @Override
227
    public javax.servlet.FilterRegistration.Dynamic addFilter(
228
            String filterName, Filter filter) {
229
        throw new RuntimeException("Not implemented");
230
    }
231
232
    @Override
233
    public javax.servlet.FilterRegistration.Dynamic addFilter(
234
            String filterName, Class<? extends Filter> filterClass) {
235
        throw new RuntimeException("Not implemented");
236
    }
237
238
    @Override
239
    public <T extends Filter> T createFilter(Class<T> c)
240
            throws ServletException {
241
        throw new RuntimeException("Not implemented");
242
    }
243
244
    @Override
245
    public FilterRegistration getFilterRegistration(String filterName) {
246
        throw new RuntimeException("Not implemented");
247
    }
248
249
    @Override
250
    public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
251
        throw new RuntimeException("Not implemented");
252
    }
253
254
    @Override
255
    public SessionCookieConfig getSessionCookieConfig() {
256
        throw new RuntimeException("Not implemented");
257
    }
258
259
    @Override
260
    public void setSessionTrackingModes(
261
            Set<SessionTrackingMode> sessionTrackingModes) {
262
        throw new RuntimeException("Not implemented");
263
    }
264
265
    @Override
266
    public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
267
        throw new RuntimeException("Not implemented");
268
    }
269
270
    @Override
271
    public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
272
        throw new RuntimeException("Not implemented");
273
    }
274
275
    @Override
276
    public void addListener(String className) {
277
        throw new RuntimeException("Not implemented");
278
    }
279
280
    @Override
281
    public <T extends EventListener> void addListener(T t) {
282
        throw new RuntimeException("Not implemented");
283
    }
284
285
    @Override
286
    public void addListener(Class<? extends EventListener> listenerClass) {
287
        throw new RuntimeException("Not implemented");
288
    }
289
290
    @Override
291
    public <T extends EventListener> T createListener(Class<T> c)
292
            throws ServletException {
293
        throw new RuntimeException("Not implemented");
294
    }
295
296
    @Override
297
    public JspConfigDescriptor getJspConfigDescriptor() {
298
        throw new RuntimeException("Not implemented");
299
    }
300
301
    @Override
302
    public ClassLoader getClassLoader() {
303
        throw new RuntimeException("Not implemented");
304
    }
305
306
    @Override
307
    public void declareRoles(String... roleNames) {
308
        throw new RuntimeException("Not implemented");
309
    }
310
311
    @Override
312
    public String getVirtualServerName() {
313
        throw new RuntimeException("Not implemented");
314
    }
315
316
}
0
  + native
317
  + native
(-)test/org/apache/catalina/filters/TesterHttpServletRequest.java (+444 lines)
Line 0 Link Here
1
/**
2
 * Copyright 2012-2013 eBay Software Foundation, All Rights Reserved.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5
 * use this file except in compliance with the License. You may obtain a copy of
6
 * the License at
7
 *
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
 * License for the specific language governing permissions and limitations under
14
 * the License.
15
 */
16
package org.apache.catalina.filters;
17
18
import java.io.BufferedReader;
19
import java.io.IOException;
20
import java.io.UnsupportedEncodingException;
21
import java.security.Principal;
22
import java.util.ArrayList;
23
import java.util.Collection;
24
import java.util.Collections;
25
import java.util.Enumeration;
26
import java.util.HashMap;
27
import java.util.HashSet;
28
import java.util.List;
29
import java.util.Locale;
30
import java.util.Map;
31
32
import javax.servlet.AsyncContext;
33
import javax.servlet.DispatcherType;
34
import javax.servlet.RequestDispatcher;
35
import javax.servlet.ServletContext;
36
import javax.servlet.ServletException;
37
import javax.servlet.ServletInputStream;
38
import javax.servlet.ServletRequest;
39
import javax.servlet.ServletResponse;
40
import javax.servlet.http.Cookie;
41
import javax.servlet.http.HttpServletRequest;
42
import javax.servlet.http.HttpServletResponse;
43
import javax.servlet.http.HttpSession;
44
import javax.servlet.http.HttpUpgradeHandler;
45
import javax.servlet.http.Part;
46
47
/**
48
 * A mock {@link HttpServletRequest}.
49
 */
50
public class TesterHttpServletRequest implements HttpServletRequest {
51
52
    private final Map<String, Object> attributes = new HashMap<String, Object>();
53
    private final Map<String, List<String>> headers = new HashMap<String, List<String>>();
54
    private String method;
55
    private String contentType;
56
57
    @Override
58
    public Object getAttribute(String name) {
59
        return attributes.get(name);
60
    }
61
62
    @Override
63
    public Enumeration<String> getAttributeNames() {
64
        return Collections.enumeration(attributes.keySet());
65
    }
66
67
    @Override
68
    public String getCharacterEncoding() {
69
        throw new RuntimeException("Not implemented");
70
    }
71
72
    @Override
73
    public void setCharacterEncoding(String env)
74
            throws UnsupportedEncodingException {
75
        // NO-OP.
76
    }
77
78
    @Override
79
    public int getContentLength() {
80
        throw new RuntimeException("Not implemented");
81
    }
82
83
    @Override
84
    public String getContentType() {
85
        return this.contentType;
86
    }
87
88
    /**
89
     * Method to set content type for test.
90
     *
91
     * @param contentType
92
     *            The type of content.
93
     */
94
    public void setContentType(String contentType) {
95
        this.contentType = contentType;
96
    }
97
98
    @Override
99
    public ServletInputStream getInputStream() throws IOException {
100
        throw new RuntimeException("Not implemented");
101
    }
102
103
    @Override
104
    public String getParameter(String name) {
105
        throw new RuntimeException("Not implemented");
106
    }
107
108
    @Override
109
    public Enumeration<String> getParameterNames() {
110
        throw new RuntimeException("Not implemented");
111
    }
112
113
    @Override
114
    public String[] getParameterValues(String name) {
115
        throw new RuntimeException("Not implemented");
116
    }
117
118
    @Override
119
    public Map<String, String[]> getParameterMap() {
120
        throw new RuntimeException("Not implemented");
121
    }
122
123
    @Override
124
    public String getProtocol() {
125
        throw new RuntimeException("Not implemented");
126
    }
127
128
    @Override
129
    public String getScheme() {
130
        throw new RuntimeException("Not implemented");
131
    }
132
133
    @Override
134
    public String getServerName() {
135
        throw new RuntimeException("Not implemented");
136
    }
137
138
    @Override
139
    public int getServerPort() {
140
        throw new RuntimeException("Not implemented");
141
    }
142
143
    @Override
144
    public BufferedReader getReader() throws IOException {
145
        throw new RuntimeException("Not implemented");
146
    }
147
148
    @Override
149
    public String getRemoteAddr() {
150
        throw new RuntimeException("Not implemented");
151
    }
152
153
    @Override
154
    public String getRemoteHost() {
155
        throw new RuntimeException("Not implemented");
156
    }
157
158
    @Override
159
    public void setAttribute(String name, Object o) {
160
        attributes.put(name, o);
161
    }
162
163
    @Override
164
    public void removeAttribute(String name) {
165
        attributes.remove(name);
166
    }
167
168
    @Override
169
    public Locale getLocale() {
170
        throw new RuntimeException("Not implemented");
171
    }
172
173
    @Override
174
    public Enumeration<Locale> getLocales() {
175
        throw new RuntimeException("Not implemented");
176
    }
177
178
    @Override
179
    public boolean isSecure() {
180
        throw new RuntimeException("Not implemented");
181
    }
182
183
    @Override
184
    public RequestDispatcher getRequestDispatcher(String path) {
185
        throw new RuntimeException("Not implemented");
186
    }
187
188
    @Override
189
    public String getRealPath(String path) {
190
        throw new RuntimeException("Not implemented");
191
    }
192
193
    @Override
194
    public int getRemotePort() {
195
        throw new RuntimeException("Not implemented");
196
    }
197
198
    @Override
199
    public String getLocalName() {
200
        throw new RuntimeException("Not implemented");
201
    }
202
203
    @Override
204
    public String getLocalAddr() {
205
        throw new RuntimeException("Not implemented");
206
    }
207
208
    @Override
209
    public int getLocalPort() {
210
        throw new RuntimeException("Not implemented");
211
    }
212
213
    @Override
214
    public String getAuthType() {
215
        throw new RuntimeException("Not implemented");
216
    }
217
218
    @Override
219
    public Cookie[] getCookies() {
220
        throw new RuntimeException("Not implemented");
221
    }
222
223
    @Override
224
    public long getDateHeader(String name) {
225
        throw new RuntimeException("Not implemented");
226
    }
227
228
    @Override
229
    public String getHeader(String name) {
230
        List<String> list = headers.get(name);
231
        if (list != null) {
232
            return CORSFilter.join(new HashSet<String>(list), ",");
233
        }
234
        return null;
235
    }
236
237
    /**
238
     * Method to set header name and value for test.
239
     *
240
     * @param name
241
     *            Name of header.
242
     * @param value
243
     *            Value of header.
244
     */
245
    public void setHeader(String name, String value) {
246
        List<String> values = new ArrayList<String>();
247
        values.add(value);
248
        headers.put(name, values);
249
    }
250
251
    @Override
252
    public Enumeration<String> getHeaders(String name) {
253
        throw new RuntimeException("Not implemented");
254
    }
255
256
    @Override
257
    public Enumeration<String> getHeaderNames() {
258
        return Collections.enumeration(headers.keySet());
259
    }
260
261
    @Override
262
    public int getIntHeader(String name) {
263
        throw new RuntimeException("Not implemented");
264
    }
265
266
    @Override
267
    public String getMethod() {
268
        return method;
269
    }
270
271
    /**
272
     * Method to set HTTP method type, for test.
273
     *
274
     * @param method
275
     *            The type of HTTP method.
276
     */
277
    public void setMethod(String method) {
278
        this.method = method;
279
    }
280
281
    @Override
282
    public String getPathInfo() {
283
        throw new RuntimeException("Not implemented");
284
    }
285
286
    @Override
287
    public String getPathTranslated() {
288
        throw new RuntimeException("Not implemented");
289
    }
290
291
    @Override
292
    public String getContextPath() {
293
        throw new RuntimeException("Not implemented");
294
    }
295
296
    @Override
297
    public String getQueryString() {
298
        throw new RuntimeException("Not implemented");
299
    }
300
301
    @Override
302
    public String getRemoteUser() {
303
        throw new RuntimeException("Not implemented");
304
    }
305
306
    @Override
307
    public boolean isUserInRole(String role) {
308
        throw new RuntimeException("Not implemented");
309
    }
310
311
    @Override
312
    public Principal getUserPrincipal() {
313
        throw new RuntimeException("Not implemented");
314
    }
315
316
    @Override
317
    public String getRequestedSessionId() {
318
        throw new RuntimeException("Not implemented");
319
    }
320
321
    @Override
322
    public String getRequestURI() {
323
        throw new RuntimeException("Not implemented");
324
    }
325
326
    @Override
327
    public StringBuffer getRequestURL() {
328
        throw new RuntimeException("Not implemented");
329
    }
330
331
    @Override
332
    public String getServletPath() {
333
        throw new RuntimeException("Not implemented");
334
    }
335
336
    @Override
337
    public HttpSession getSession(boolean create) {
338
        throw new RuntimeException("Not implemented");
339
    }
340
341
    @Override
342
    public HttpSession getSession() {
343
        throw new RuntimeException("Not implemented");
344
    }
345
346
    @Override
347
    public boolean isRequestedSessionIdValid() {
348
        throw new RuntimeException("Not implemented");
349
    }
350
351
    @Override
352
    public boolean isRequestedSessionIdFromCookie() {
353
        throw new RuntimeException("Not implemented");
354
    }
355
356
    @Override
357
    public boolean isRequestedSessionIdFromURL() {
358
        throw new RuntimeException("Not implemented");
359
    }
360
361
    @Override
362
    public boolean isRequestedSessionIdFromUrl() {
363
        throw new RuntimeException("Not implemented");
364
    }
365
366
    @Override
367
    public long getContentLengthLong() {
368
        throw new RuntimeException("Not implemented");
369
    }
370
371
    @Override
372
    public ServletContext getServletContext() {
373
        throw new RuntimeException("Not implemented");
374
    }
375
376
    @Override
377
    public AsyncContext startAsync() throws IllegalStateException {
378
        throw new RuntimeException("Not implemented");
379
    }
380
381
    @Override
382
    public AsyncContext startAsync(ServletRequest servletRequest,
383
            ServletResponse servletResponse) throws IllegalStateException {
384
        throw new RuntimeException("Not implemented");
385
    }
386
387
    @Override
388
    public boolean isAsyncStarted() {
389
        throw new RuntimeException("Not implemented");
390
    }
391
392
    @Override
393
    public boolean isAsyncSupported() {
394
        throw new RuntimeException("Not implemented");
395
    }
396
397
    @Override
398
    public AsyncContext getAsyncContext() {
399
        throw new RuntimeException("Not implemented");
400
    }
401
402
    @Override
403
    public DispatcherType getDispatcherType() {
404
        throw new RuntimeException("Not implemented");
405
    }
406
407
    @Override
408
    public String changeSessionId() {
409
        throw new RuntimeException("Not implemented");
410
    }
411
412
    @Override
413
    public boolean authenticate(HttpServletResponse response)
414
            throws IOException, ServletException {
415
        throw new RuntimeException("Not implemented");
416
    }
417
418
    @Override
419
    public void login(String username, String password) throws ServletException {
420
        throw new RuntimeException("Not implemented");
421
    }
422
423
    @Override
424
    public void logout() throws ServletException {
425
        throw new RuntimeException("Not implemented");
426
    }
427
428
    @Override
429
    public Collection<Part> getParts() throws IOException, ServletException {
430
        throw new RuntimeException("Not implemented");
431
    }
432
433
    @Override
434
    public Part getPart(String name) throws IOException, ServletException {
435
        throw new RuntimeException("Not implemented");
436
    }
437
438
    @Override
439
    public <T extends HttpUpgradeHandler> T upgrade(
440
            Class<T> httpUpgradeHandlerClass) throws IOException {
441
        throw new RuntimeException("Not implemented");
442
    }
443
444
}
0
  + native
445
  + native
(-)test/org/apache/catalina/filters/TesterFilterConfigs.java (+355 lines)
Line 0 Link Here
1
/**
2
 * Copyright 2012-2013 eBay Software Foundation, All Rights Reserved.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5
 * use this file except in compliance with the License. You may obtain a copy of
6
 * the License at
7
 *
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
 * License for the specific language governing permissions and limitations under
14
 * the License.
15
 */
16
package org.apache.catalina.filters;
17
18
import java.util.Enumeration;
19
20
import javax.servlet.FilterConfig;
21
import javax.servlet.ServletContext;
22
23
/**
24
 * A collection of mock {@link FilterConfig}.
25
 */
26
public class TesterFilterConfigs {
27
    /**
28
     * A HTTPS origin.
29
     */
30
    public static final String HTTPS_WWW_APACHE_ORG = "https://www.apache.org";
31
    /**
32
     * A HTTP origin.
33
     */
34
    public static final String HTTP_TOMCAT_APACHE_ORG = "http://tomcat.apache.org";
35
    /**
36
     * A sample header to test exposed headers.
37
     */
38
    public static final String EXPOSED_HEADERS = "X-CUSTOM-HEADER";
39
    /**
40
     * Any origin
41
     */
42
    public static final String ANY_ORIGIN = "*";
43
44
    /**
45
     * A mock {@link ServletContext} object.
46
     */
47
    public static final TesterServletContext mockServletContext = new TesterServletContext();
48
49
    /**
50
     * Returns a {@link FilterConfig} object, with default configuration.
51
     *
52
     * @return A {@link FilterConfig} object.
53
     */
54
    public static FilterConfig getDefaultFilterConfig() {
55
        final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
56
        final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS;
57
        final String allowedOrigins = CORSFilter.DEFAULT_ALLOWED_ORIGINS;
58
        final String exposedHeaders = CORSFilter.DEFAULT_EXPOSED_HEADERS;
59
        final String supportCredentials = CORSFilter.DEFAULT_SUPPORTS_CREDENTIALS;
60
        final String preflightMaxAge = CORSFilter.DEFAULT_PREFLIGHT_MAXAGE;
61
        final String loggingEnabled = CORSFilter.DEFAULT_LOGGING_ENABLED;
62
        final String decorateRequest = CORSFilter.DEFAULT_DECORATE_REQUEST;
63
64
        return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods,
65
                allowedOrigins, exposedHeaders, supportCredentials,
66
                preflightMaxAge, loggingEnabled, decorateRequest);
67
    }
68
69
    /**
70
     * A variation of default {@link FilterConfig}, with support of PUT method
71
     * and credentials.
72
     *
73
     * @return A {@link FilterConfig} object.
74
     */
75
    public static FilterConfig getFilterConfigAnyOriginAndSupportsCredentials() {
76
        final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
77
        final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS
78
                + ",PUT";
79
        final String allowedOrigins = CORSFilter.DEFAULT_ALLOWED_ORIGINS;
80
        final String exposedHeaders = CORSFilter.DEFAULT_EXPOSED_HEADERS;
81
        final String supportCredentials = "true";
82
        final String preflightMaxAge = CORSFilter.DEFAULT_PREFLIGHT_MAXAGE;
83
        final String loggingEnabled = CORSFilter.DEFAULT_LOGGING_ENABLED;
84
        final String decorateRequest = CORSFilter.DEFAULT_DECORATE_REQUEST;
85
86
        return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods,
87
                allowedOrigins, exposedHeaders, supportCredentials,
88
                preflightMaxAge, loggingEnabled, decorateRequest);
89
    }
90
91
    /**
92
     * A variation of default {@link FilterConfig}, with support of PUT method
93
     * and credentials disabled..
94
     *
95
     * @return A {@link FilterConfig} object.
96
     */
97
    public static FilterConfig getFilterConfigAnyOriginAndSupportsCredentialsDisabled() {
98
        final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
99
        final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS
100
                + ",PUT";
101
        final String allowedOrigins = CORSFilter.DEFAULT_ALLOWED_ORIGINS;
102
        final String exposedHeaders = CORSFilter.DEFAULT_EXPOSED_HEADERS;
103
        final String supportCredentials = "false";
104
        final String preflightMaxAge = CORSFilter.DEFAULT_PREFLIGHT_MAXAGE;
105
        final String loggingEnabled = CORSFilter.DEFAULT_LOGGING_ENABLED;
106
        final String decorateRequest = CORSFilter.DEFAULT_DECORATE_REQUEST;
107
108
        return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods,
109
                allowedOrigins, exposedHeaders, supportCredentials,
110
                preflightMaxAge, loggingEnabled, decorateRequest);
111
    }
112
113
    /**
114
     * A variation of default {@link FilterConfig}, with support of PUT method
115
     * and any origin disabled, that is configured using specific origins.
116
     *
117
     * @return A {@link FilterConfig} object.
118
     */
119
    public static FilterConfig getFilterConfigSpecificOriginAndSupportsCredentialsDisabled() {
120
        final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
121
        final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS
122
                + ",PUT";
123
        final String allowedOrigins = HTTP_TOMCAT_APACHE_ORG + ","
124
                + HTTPS_WWW_APACHE_ORG;
125
        final String exposedHeaders = CORSFilter.DEFAULT_EXPOSED_HEADERS;
126
        final String supportCredentials = "false";
127
        final String preflightMaxAge = CORSFilter.DEFAULT_PREFLIGHT_MAXAGE;
128
        final String loggingEnabled = CORSFilter.DEFAULT_LOGGING_ENABLED;
129
        final String decorateRequest = CORSFilter.DEFAULT_DECORATE_REQUEST;
130
131
        return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods,
132
                allowedOrigins, exposedHeaders, supportCredentials,
133
                preflightMaxAge, loggingEnabled, decorateRequest);
134
    }
135
136
    /**
137
     * A variation of default {@link FilterConfig}, with exposed headers.
138
     *
139
     * @return A {@link FilterConfig} object.
140
     */
141
    public static FilterConfig getFilterConfigWithExposedHeaders() {
142
        final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
143
        final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS;
144
        final String allowedOrigins = CORSFilter.DEFAULT_ALLOWED_ORIGINS;
145
        final String exposedHeaders = EXPOSED_HEADERS;
146
        final String supportCredentials = CORSFilter.DEFAULT_SUPPORTS_CREDENTIALS;
147
        final String preflightMaxAge = CORSFilter.DEFAULT_PREFLIGHT_MAXAGE;
148
        final String loggingEnabled = CORSFilter.DEFAULT_LOGGING_ENABLED;
149
        final String decorateRequest = CORSFilter.DEFAULT_DECORATE_REQUEST;
150
151
        return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods,
152
                allowedOrigins, exposedHeaders, supportCredentials,
153
                preflightMaxAge, loggingEnabled, decorateRequest);
154
    }
155
156
    /**
157
     * A variation of default {@link FilterConfig}, with support of PUT method,
158
     * a HTTPS origin
159
     *
160
     * @return A {@link FilterConfig} object.
161
     */
162
    public static FilterConfig getSecureFilterConfig() {
163
        final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
164
        final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS
165
                + ",PUT";
166
        final String allowedOrigins = HTTPS_WWW_APACHE_ORG;
167
        final String exposedHeaders = CORSFilter.DEFAULT_EXPOSED_HEADERS;
168
        final String supportCredentials = "true";
169
        final String preflightMaxAge = CORSFilter.DEFAULT_PREFLIGHT_MAXAGE;
170
        final String loggingEnabled = "true";
171
        final String decorateRequest = CORSFilter.DEFAULT_DECORATE_REQUEST;
172
173
        return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods,
174
                allowedOrigins, exposedHeaders, supportCredentials,
175
                preflightMaxAge, loggingEnabled, decorateRequest);
176
    }
177
178
    /**
179
     * A {@link FilterConfig} with all <code>null</code> attributes, i.e. will
180
     * use default config.
181
     *
182
     * @return A {@link FilterConfig} object.
183
     */
184
    public static FilterConfig getNullFilterConfig() {
185
        return generateFilterConfig(null, null, null, null, null, null, null,
186
                null);
187
    }
188
189
    /**
190
     * A variation of default {@link FilterConfig}, with support of PUT method,
191
     * a HTTPS origin and a HTTP origin.
192
     *
193
     * @return A {@link FilterConfig} object.
194
     */
195
    public static FilterConfig getSpecificOriginFilterConfig() {
196
        final String allowedOrigins = HTTPS_WWW_APACHE_ORG + ","
197
                + HTTP_TOMCAT_APACHE_ORG;
198
199
        final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
200
        final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS
201
                + ",PUT";
202
        final String exposedHeaders = CORSFilter.DEFAULT_EXPOSED_HEADERS;
203
        final String supportCredentials = CORSFilter.DEFAULT_SUPPORTS_CREDENTIALS;
204
        final String preflightMaxAge = CORSFilter.DEFAULT_PREFLIGHT_MAXAGE;
205
        final String loggingEnabled = CORSFilter.DEFAULT_LOGGING_ENABLED;
206
        final String decorateRequest = CORSFilter.DEFAULT_DECORATE_REQUEST;
207
208
        return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods,
209
                allowedOrigins, exposedHeaders, supportCredentials,
210
                preflightMaxAge, loggingEnabled, decorateRequest);
211
    }
212
213
    /**
214
     * A variation of default {@link FilterConfig}, with support of PUT method,
215
     * a HTTPS origin, a HTTP origin, and a negative max age header, which
216
     * indicates browsers to not cache pre-flight response.
217
     *
218
     * @return A {@link FilterConfig} object.
219
     */
220
    public static FilterConfig getSpecificOriginFilterConfigNegativeMaxAge() {
221
        final String allowedOrigins = HTTPS_WWW_APACHE_ORG + ","
222
                + HTTP_TOMCAT_APACHE_ORG;
223
224
        final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
225
        final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS
226
                + ",PUT";
227
        final String exposedHeaders = CORSFilter.DEFAULT_EXPOSED_HEADERS;
228
        final String supportCredentials = CORSFilter.DEFAULT_SUPPORTS_CREDENTIALS;
229
        final String preflightMaxAge = "-1";
230
        final String loggingEnabled = CORSFilter.DEFAULT_LOGGING_ENABLED;
231
        final String decorateRequest = CORSFilter.DEFAULT_DECORATE_REQUEST;
232
233
        return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods,
234
                allowedOrigins, exposedHeaders, supportCredentials,
235
                preflightMaxAge, loggingEnabled, decorateRequest);
236
    }
237
238
    /**
239
     * A variation of default {@link FilterConfig}, with an invalid pre-flight
240
     * max age value. It should be a integer.
241
     *
242
     * @return A {@link FilterConfig} object.
243
     */
244
    public static FilterConfig getFilterConfigInvalidMaxPreflightAge() {
245
        final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
246
        final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS;
247
        final String allowedOrigins = CORSFilter.DEFAULT_ALLOWED_ORIGINS;
248
        final String exposedHeaders = CORSFilter.DEFAULT_EXPOSED_HEADERS;
249
        final String supportCredentials = CORSFilter.DEFAULT_SUPPORTS_CREDENTIALS;
250
        final String preflightMaxAge = "abc";
251
        final String loggingEnabled = CORSFilter.DEFAULT_LOGGING_ENABLED;
252
        final String decorateRequest = CORSFilter.DEFAULT_DECORATE_REQUEST;
253
254
        return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods,
255
                allowedOrigins, exposedHeaders, supportCredentials,
256
                preflightMaxAge, loggingEnabled, decorateRequest);
257
    }
258
259
    /**
260
     * A {@link FilterConfig} with all properties as empty strings.
261
     *
262
     * @return A {@link FilterConfig} object.
263
     */
264
    public static FilterConfig getEmptyFilterConfig() {
265
        final String allowedHttpHeaders = "";
266
        final String allowedHttpMethods = "";
267
        final String allowedOrigins = "";
268
        final String exposedHeaders = "";
269
        final String supportCredentials = "";
270
        final String preflightMaxAge = "";
271
        final String loggingEnabled = "";
272
        final String decorateRequest = "";
273
274
        return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods,
275
                allowedOrigins, exposedHeaders, supportCredentials,
276
                preflightMaxAge, loggingEnabled, decorateRequest);
277
    }
278
279
    /**
280
     * A variation of default {@link FilterConfig}, that disables adding
281
     * attributes to HttpServletRequest.
282
     *
283
     * @return A {@link FilterConfig} object.
284
     */
285
    public static FilterConfig getFilterConfigDecorateRequestDisabled() {
286
        final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
287
        final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS;
288
        final String allowedOrigins = CORSFilter.DEFAULT_ALLOWED_ORIGINS;
289
        final String exposedHeaders = CORSFilter.DEFAULT_EXPOSED_HEADERS;
290
        final String supportCredentials = CORSFilter.DEFAULT_SUPPORTS_CREDENTIALS;
291
        final String preflightMaxAge = CORSFilter.DEFAULT_PREFLIGHT_MAXAGE;
292
        final String loggingEnabled = CORSFilter.DEFAULT_LOGGING_ENABLED;
293
        final String decorateRequest = "false";
294
295
        return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods,
296
                allowedOrigins, exposedHeaders, supportCredentials,
297
                preflightMaxAge, loggingEnabled, decorateRequest);
298
    }
299
300
    private static FilterConfig generateFilterConfig(
301
            final String allowedHttpHeaders, final String allowedHttpMethods,
302
            final String allowedOrigins, final String exposedHeaders,
303
            final String supportCredentials, final String preflightMaxAge,
304
            final String loggingEnabled, final String decorateRequest) {
305
        FilterConfig filterConfig = new FilterConfig() {
306
307
            @Override
308
            public String getFilterName() {
309
                return "cors-filter";
310
            }
311
312
            @Override
313
            public ServletContext getServletContext() {
314
                return mockServletContext;
315
            }
316
317
            @Override
318
            public String getInitParameter(String name) {
319
                if (CORSFilter.PARAM_CORS_ALLOWED_HEADERS
320
                        .equalsIgnoreCase(name)) {
321
                    return allowedHttpHeaders;
322
                } else if (CORSFilter.PARAM_CORS_ALLOWED_METHODS
323
                        .equalsIgnoreCase(name)) {
324
                    return allowedHttpMethods;
325
                } else if (CORSFilter.PARAM_CORS_ALLOWED_ORIGINS
326
                        .equalsIgnoreCase(name)) {
327
                    return allowedOrigins;
328
                } else if (CORSFilter.PARAM_CORS_EXPOSED_HEADERS
329
                        .equalsIgnoreCase(name)) {
330
                    return exposedHeaders;
331
                } else if (CORSFilter.PARAM_CORS_SUPPORT_CREDENTIALS
332
                        .equalsIgnoreCase(name)) {
333
                    return supportCredentials;
334
                } else if (CORSFilter.PARAM_CORS_PREFLIGHT_MAXAGE
335
                        .equalsIgnoreCase(name)) {
336
                    return preflightMaxAge;
337
                } else if (CORSFilter.PARAM_CORS_LOGGING_ENABLED
338
                        .equalsIgnoreCase(name)) {
339
                    return loggingEnabled;
340
                } else if (CORSFilter.PARAM_CORS_REQUEST_DECORATE
341
                        .equalsIgnoreCase(name)) {
342
                    return decorateRequest;
343
                }
344
                return null;
345
            }
346
347
            @Override
348
            public Enumeration<String> getInitParameterNames() {
349
                return null;
350
            }
351
        };
352
353
        return filterConfig;
354
    }
355
}
0
  + native
356
  + native
(-)test/org/apache/catalina/filters/TesterHttpServletResponse.java (+238 lines)
Line 0 Link Here
1
/**
2
 * Copyright 2012-2013 eBay Software Foundation, All Rights Reserved.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5
 * use this file except in compliance with the License. You may obtain a copy of
6
 * the License at
7
 *
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
 * License for the specific language governing permissions and limitations under
14
 * the License.
15
 */
16
package org.apache.catalina.filters;
17
18
import java.io.IOException;
19
import java.io.PrintWriter;
20
import java.io.StringWriter;
21
import java.util.ArrayList;
22
import java.util.Collection;
23
import java.util.List;
24
import java.util.Locale;
25
26
import javax.servlet.ServletOutputStream;
27
import javax.servlet.http.Cookie;
28
import javax.servlet.http.HttpServletResponse;
29
30
/**
31
 * A mock {@link HttpServletResponse}.
32
 */
33
public class TesterHttpServletResponse implements HttpServletResponse {
34
    List<String> headerNames = new ArrayList<String>();
35
    List<String> headerValues = new ArrayList<String>();
36
    PrintWriter pw;
37
    int status;
38
39
    @Override
40
    public String getCharacterEncoding() {
41
        throw new RuntimeException("Not implemented");
42
    }
43
44
    @Override
45
    public String getContentType() {
46
        throw new RuntimeException("Not implemented");
47
    }
48
49
    @Override
50
    public ServletOutputStream getOutputStream() throws IOException {
51
        throw new RuntimeException("Not implemented");
52
    }
53
54
    @Override
55
    public PrintWriter getWriter() throws IOException {
56
        if (pw == null) {
57
            pw = new PrintWriter(new StringWriter());
58
        }
59
        return pw;
60
    }
61
62
    @Override
63
    public void setCharacterEncoding(String charset) {
64
        throw new RuntimeException("Not implemented");
65
    }
66
67
    @Override
68
    public void setContentLength(int len) {
69
        throw new RuntimeException("Not implemented");
70
    }
71
72
    @Override
73
    public void setContentType(String type) {
74
        // NO-OP
75
    }
76
77
    @Override
78
    public void setBufferSize(int size) {
79
        throw new RuntimeException("Not implemented");
80
    }
81
82
    @Override
83
    public int getBufferSize() {
84
        throw new RuntimeException("Not implemented");
85
    }
86
87
    @Override
88
    public void flushBuffer() throws IOException {
89
        throw new RuntimeException("Not implemented");
90
    }
91
92
    @Override
93
    public void resetBuffer() {
94
        // NO-OP
95
    }
96
97
    @Override
98
    public boolean isCommitted() {
99
        throw new RuntimeException("Not implemented");
100
    }
101
102
    @Override
103
    public void reset() {
104
        // NO-OP
105
    }
106
107
    @Override
108
    public void setLocale(Locale loc) {
109
        throw new RuntimeException("Not implemented");
110
    }
111
112
    @Override
113
    public Locale getLocale() {
114
        throw new RuntimeException("Not implemented");
115
    }
116
117
    @Override
118
    public void addCookie(Cookie cookie) {
119
        throw new RuntimeException("Not implemented");
120
    }
121
122
    @Override
123
    public boolean containsHeader(String name) {
124
        throw new RuntimeException("Not implemented");
125
    }
126
127
    @Override
128
    public String encodeURL(String url) {
129
        throw new RuntimeException("Not implemented");
130
    }
131
132
    @Override
133
    public String encodeRedirectURL(String url) {
134
        throw new RuntimeException("Not implemented");
135
    }
136
137
    @Override
138
    public String encodeUrl(String url) {
139
        throw new RuntimeException("Not implemented");
140
    }
141
142
    @Override
143
    public String encodeRedirectUrl(String url) {
144
        throw new RuntimeException("Not implemented");
145
    }
146
147
    @Override
148
    public void sendError(int sc, String msg) throws IOException {
149
        // NO-OP
150
    }
151
152
    @Override
153
    public void sendError(int sc) throws IOException {
154
        // NO-OP
155
    }
156
157
    @Override
158
    public void sendRedirect(String location) throws IOException {
159
        throw new RuntimeException("Not implemented");
160
    }
161
162
    @Override
163
    public void setDateHeader(String name, long date) {
164
        throw new RuntimeException("Not implemented");
165
    }
166
167
    @Override
168
    public void addDateHeader(String name, long date) {
169
        throw new RuntimeException("Not implemented");
170
    }
171
172
    @Override
173
    public String getHeader(String name) {
174
        int index = headerNames.indexOf(name);
175
        if (index != -1) {
176
            return headerValues.get(index);
177
        }
178
        return null;
179
    }
180
181
    @Override
182
    public void setHeader(String name, String value) {
183
        int index = headerNames.indexOf(name);
184
        if (index != -1) {
185
            headerValues.set(index, value);
186
        } else {
187
            headerNames.add(name);
188
            headerValues.add(value);
189
        }
190
    }
191
192
    @Override
193
    public void addHeader(String name, String value) {
194
        headerNames.add(name);
195
        headerValues.add(value);
196
    }
197
198
    @Override
199
    public void setIntHeader(String name, int value) {
200
        throw new RuntimeException("Not implemented");
201
    }
202
203
    @Override
204
    public void addIntHeader(String name, int value) {
205
        throw new RuntimeException("Not implemented");
206
    }
207
208
    @Override
209
    public void setStatus(int sc) {
210
        this.status = sc;
211
    }
212
213
    @Override
214
    public int getStatus() {
215
        return this.status;
216
    }
217
218
    @Override
219
    public void setStatus(int sc, String sm) {
220
        // NO-OP
221
    }
222
223
    @Override
224
    public void setContentLengthLong(long length) {
225
        throw new RuntimeException("Not implemented");
226
    }
227
228
    @Override
229
    public Collection<String> getHeaders(String name) {
230
        throw new RuntimeException("Not implemented");
231
    }
232
233
    @Override
234
    public Collection<String> getHeaderNames() {
235
        throw new RuntimeException("Not implemented");
236
    }
237
238
}
0
  + native
239
  + native
(-)test/org/apache/catalina/filters/TestCORSFilter.java (+1562 lines)
Line 0 Link Here
1
/**
2
 * Copyright 2012-2013 eBay Software Foundation, All Rights Reserved.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5
 * use this file except in compliance with the License. You may obtain a copy of
6
 * the License at
7
 *
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
 * License for the specific language governing permissions and limitations under
14
 * the License.
15
 */
16
package org.apache.catalina.filters;
17
18
import java.io.IOException;
19
import java.util.LinkedHashSet;
20
import java.util.Set;
21
22
import javax.servlet.FilterChain;
23
import javax.servlet.ServletException;
24
import javax.servlet.http.HttpServletRequest;
25
import javax.servlet.http.HttpServletResponse;
26
27
import org.junit.Assert;
28
import org.junit.Test;
29
30
/**
31
 * Unit tests for {@link CORSFilter}.
32
 */
33
public class TestCORSFilter {
34
    private final FilterChain filterChain = new TesterFilterChain();
35
36
    /**
37
     * Tests if a GET request is treated as simple request.
38
     *
39
     * @See http://www.w3.org/TR/cors/#simple-method
40
     * @throws IOException
41
     * @throws ServletException
42
     */
43
    @Test
44
    public void testDoFilterSimpleGET() throws IOException, ServletException {
45
        TesterHttpServletRequest request = new TesterHttpServletRequest();
46
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
47
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
48
        request.setMethod("GET");
49
        TesterHttpServletResponse response = new TesterHttpServletResponse();
50
51
        CORSFilter corsFilter = new CORSFilter();
52
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
53
        corsFilter.doFilter(request, response, filterChain);
54
55
        Assert.assertTrue(response.getHeader(
56
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
57
                "https://www.apache.org"));
58
        Assert.assertTrue(request.getAttribute(
59
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals(
60
                Boolean.TRUE));
61
        Assert.assertTrue(request.getAttribute(
62
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
63
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
64
        Assert.assertTrue(request.getAttribute(
65
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
66
                CORSFilter.CORSRequestType.SIMPLE.name().toLowerCase()));
67
    }
68
69
    /**
70
     * Tests if a POST request is treated as simple request.
71
     *
72
     * @See http://www.w3.org/TR/cors/#simple-method
73
     * @throws IOException
74
     * @throws ServletException
75
     */
76
    @Test
77
    public void testDoFilterSimplePOST() throws IOException, ServletException {
78
        TesterHttpServletRequest request = new TesterHttpServletRequest();
79
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
80
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
81
        request.setContentType("text/plain");
82
        request.setMethod("POST");
83
        TesterHttpServletResponse response = new TesterHttpServletResponse();
84
85
        CORSFilter corsFilter = new CORSFilter();
86
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
87
        corsFilter.doFilter(request, response, filterChain);
88
89
        Assert.assertTrue(response.getHeader(
90
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
91
                "https://www.apache.org"));
92
        Assert.assertTrue(request.getAttribute(
93
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals(
94
                Boolean.TRUE));
95
        Assert.assertTrue(request.getAttribute(
96
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
97
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
98
        Assert.assertTrue(request.getAttribute(
99
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
100
                CORSFilter.CORSRequestType.SIMPLE.name().toLowerCase()));
101
    }
102
103
    /**
104
     * Tests if a HEAD request is treated as simple request.
105
     *
106
     * @See http://www.w3.org/TR/cors/#simple-method
107
     * @throws IOException
108
     * @throws ServletException
109
     */
110
    @Test
111
    public void testDoFilterSimpleHEAD() throws IOException, ServletException {
112
        TesterHttpServletRequest request = new TesterHttpServletRequest();
113
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
114
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
115
        request.setMethod("HEAD");
116
        TesterHttpServletResponse response = new TesterHttpServletResponse();
117
118
        CORSFilter corsFilter = new CORSFilter();
119
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
120
        corsFilter.doFilter(request, response, filterChain);
121
122
        Assert.assertTrue(response.getHeader(
123
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
124
                "https://www.apache.org"));
125
        Assert.assertTrue(request.getAttribute(
126
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals(
127
                Boolean.TRUE));
128
        Assert.assertTrue(request.getAttribute(
129
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
130
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
131
        Assert.assertTrue(request.getAttribute(
132
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
133
                CORSFilter.CORSRequestType.SIMPLE.name().toLowerCase()));
134
    }
135
136
    /**
137
     * Test the presence of specific origin in response, when '*' is not used.
138
     *
139
     * @throws IOException
140
     * @throws ServletException
141
     */
142
    @Test
143
    public void testDoFilterSimpleSpecificHeader() throws IOException,
144
            ServletException {
145
        TesterHttpServletRequest request = new TesterHttpServletRequest();
146
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
147
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
148
        request.setMethod("POST");
149
        request.setContentType("text/plain");
150
        TesterHttpServletResponse response = new TesterHttpServletResponse();
151
152
        CORSFilter corsFilter = new CORSFilter();
153
        corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig());
154
        corsFilter.doFilter(request, response, filterChain);
155
156
        Assert.assertTrue(response.getHeader(
157
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
158
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
159
        Assert.assertTrue(request.getAttribute(
160
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals(
161
                Boolean.TRUE));
162
        Assert.assertTrue(request.getAttribute(
163
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
164
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
165
        Assert.assertTrue(request.getAttribute(
166
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
167
                CORSFilter.CORSRequestType.SIMPLE.name().toLowerCase()));
168
    }
169
170
    /**
171
     * Tests the prsence of the origin (and not '*') in the response, when
172
     * supports credentials is enabled alongwith any origin, '*'.
173
     *
174
     * @throws IOException
175
     * @throws ServletException
176
     */
177
    @Test
178
    public void testDoFilterSimpleAnyOriginAndSupportsCredentials()
179
            throws IOException, ServletException {
180
        TesterHttpServletRequest request = new TesterHttpServletRequest();
181
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
182
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
183
        request.setMethod("GET");
184
        TesterHttpServletResponse response = new TesterHttpServletResponse();
185
186
        CORSFilter corsFilter = new CORSFilter();
187
        corsFilter.init(TesterFilterConfigs
188
                .getFilterConfigAnyOriginAndSupportsCredentials());
189
        corsFilter.doFilter(request, response, filterChain);
190
191
        Assert.assertTrue(response.getHeader(
192
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
193
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
194
        Assert.assertTrue(response.getHeader(
195
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS)
196
                .equals("true"));
197
        Assert.assertTrue(request.getAttribute(
198
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals(
199
                Boolean.TRUE));
200
        Assert.assertTrue(request.getAttribute(
201
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
202
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
203
        Assert.assertTrue(request.getAttribute(
204
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
205
                CORSFilter.CORSRequestType.SIMPLE.name().toLowerCase()));
206
    }
207
208
    /**
209
     * Tests the presence of the origin (and not '*') in the response, when
210
     * supports credentials is enabled alongwith any origin, '*'.
211
     *
212
     * @throws IOException
213
     * @throws ServletException
214
     */
215
    @Test
216
    public void testDoFilterSimpleAnyOriginAndSupportsCredentialsDisabled()
217
            throws IOException, ServletException {
218
        TesterHttpServletRequest request = new TesterHttpServletRequest();
219
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
220
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
221
        request.setMethod("GET");
222
        TesterHttpServletResponse response = new TesterHttpServletResponse();
223
224
        CORSFilter corsFilter = new CORSFilter();
225
        corsFilter.init(TesterFilterConfigs
226
                .getFilterConfigAnyOriginAndSupportsCredentialsDisabled());
227
        corsFilter.doFilter(request, response, filterChain);
228
229
        Assert.assertTrue(response.getHeader(
230
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
231
                TesterFilterConfigs.ANY_ORIGIN));
232
        Assert.assertNull(response
233
                .getHeader(CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS));
234
        Assert.assertTrue(request.getAttribute(
235
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals(
236
                Boolean.TRUE));
237
        Assert.assertTrue(request.getAttribute(
238
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
239
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
240
        Assert.assertTrue(request.getAttribute(
241
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
242
                CORSFilter.CORSRequestType.SIMPLE.name().toLowerCase()));
243
    }
244
245
    /**
246
     * Tests the presence of exposed headers in response, if configured.
247
     *
248
     * @throws IOException
249
     * @throws ServletException
250
     */
251
    @Test
252
    public void testDoFilterSimpleWithExposedHeaders() throws IOException,
253
            ServletException {
254
        TesterHttpServletRequest request = new TesterHttpServletRequest();
255
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
256
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
257
        request.setMethod("POST");
258
        request.setContentType("text/plain");
259
        TesterHttpServletResponse response = new TesterHttpServletResponse();
260
261
        CORSFilter corsFilter = new CORSFilter();
262
        corsFilter
263
                .init(TesterFilterConfigs.getFilterConfigWithExposedHeaders());
264
        corsFilter.doFilter(request, response, filterChain);
265
266
        Assert.assertTrue(response.getHeader(
267
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
268
                "https://www.apache.org"));
269
        Assert.assertTrue(response.getHeader(
270
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS)
271
                .equals(TesterFilterConfigs.EXPOSED_HEADERS));
272
        Assert.assertTrue(request.getAttribute(
273
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals(
274
                Boolean.TRUE));
275
        Assert.assertTrue(request.getAttribute(
276
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
277
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
278
        Assert.assertTrue(request.getAttribute(
279
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
280
                CORSFilter.CORSRequestType.SIMPLE.name().toLowerCase()));
281
    }
282
283
    /**
284
     * Checks if an OPTIONS request is processed as pre-flight.
285
     *
286
     * @throws IOException
287
     * @throws ServletException
288
     */
289
    @Test
290
    public void testDoFilterPreflight() throws IOException, ServletException {
291
        TesterHttpServletRequest request = new TesterHttpServletRequest();
292
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
293
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
294
        request.setHeader(
295
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT");
296
        request.setHeader(
297
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS,
298
                "Content-Type");
299
        request.setMethod("OPTIONS");
300
        TesterHttpServletResponse response = new TesterHttpServletResponse();
301
302
        CORSFilter corsFilter = new CORSFilter();
303
        corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig());
304
        corsFilter.doFilter(request, response, filterChain);
305
306
        Assert.assertTrue(response.getHeader(
307
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
308
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
309
        Assert.assertTrue(request.getAttribute(
310
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals(
311
                Boolean.TRUE));
312
        Assert.assertTrue(request.getAttribute(
313
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
314
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
315
        Assert.assertTrue(request.getAttribute(
316
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
317
                CORSFilter.CORSRequestType.PRE_FLIGHT.name().toLowerCase()));
318
        Assert.assertTrue(request.getAttribute(
319
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS).equals(
320
                "Content-Type"));
321
    }
322
323
    /**
324
     * Checks if an OPTIONS request is processed as pre-flight where any origin
325
     * is enabled.
326
     *
327
     * @throws IOException
328
     * @throws ServletException
329
     */
330
    @Test
331
    public void testDoFilterPreflightAnyOrigin() throws IOException,
332
            ServletException {
333
        TesterHttpServletRequest request = new TesterHttpServletRequest();
334
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
335
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
336
        request.setHeader(
337
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT");
338
        request.setHeader(
339
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS,
340
                "Content-Type");
341
        request.setMethod("OPTIONS");
342
        TesterHttpServletResponse response = new TesterHttpServletResponse();
343
344
        CORSFilter corsFilter = new CORSFilter();
345
        corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig());
346
        corsFilter.doFilter(request, response, filterChain);
347
348
        Assert.assertTrue(response.getHeader(
349
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
350
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
351
        Assert.assertTrue(request.getAttribute(
352
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals(
353
                Boolean.TRUE));
354
        Assert.assertTrue(request.getAttribute(
355
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
356
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
357
        Assert.assertTrue(request.getAttribute(
358
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
359
                CORSFilter.CORSRequestType.PRE_FLIGHT.name().toLowerCase()));
360
        Assert.assertTrue(request.getAttribute(
361
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS).equals(
362
                "Content-Type"));
363
    }
364
365
    /**
366
     * Checks if an OPTIONS request is processed as pre-flight.
367
     *
368
     * @throws IOException
369
     * @throws ServletException
370
     */
371
    @Test
372
    public void testDoFilterPreflightInvalidOrigin() throws IOException,
373
            ServletException {
374
        TesterHttpServletRequest request = new TesterHttpServletRequest();
375
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
376
                "http://www.example.com");
377
        request.setHeader(
378
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT");
379
        request.setHeader(
380
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS,
381
                "Content-Type");
382
        request.setMethod("OPTIONS");
383
        TesterHttpServletResponse response = new TesterHttpServletResponse();
384
385
        CORSFilter corsFilter = new CORSFilter();
386
        corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig());
387
        corsFilter.doFilter(request, response, filterChain);
388
389
        Assert.assertEquals(response.getStatus(),
390
                HttpServletResponse.SC_FORBIDDEN);
391
    }
392
393
    /**
394
     * Tests the case when a negative max-age header is provided. In that case
395
     * the browser should not cache the response.
396
     *
397
     * @throws IOException
398
     * @throws ServletException
399
     */
400
    @Test
401
    public void testDoFilterPreflightNegativeMaxAge() throws IOException,
402
            ServletException {
403
        TesterHttpServletRequest request = new TesterHttpServletRequest();
404
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
405
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
406
        request.setHeader(
407
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT");
408
        request.setHeader(
409
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS,
410
                "Content-Type");
411
        request.setMethod("OPTIONS");
412
        TesterHttpServletResponse response = new TesterHttpServletResponse();
413
414
        CORSFilter corsFilter = new CORSFilter();
415
        corsFilter.init(TesterFilterConfigs
416
                .getSpecificOriginFilterConfigNegativeMaxAge());
417
        corsFilter.doFilter(request, response, filterChain);
418
419
        Assert.assertTrue(response.getHeader(
420
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
421
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
422
        Assert.assertNull(response
423
                .getHeader(CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_MAX_AGE));
424
        Assert.assertTrue(request.getAttribute(
425
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals(
426
                Boolean.TRUE));
427
        Assert.assertTrue(request.getAttribute(
428
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
429
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
430
        Assert.assertTrue(request.getAttribute(
431
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
432
                CORSFilter.CORSRequestType.PRE_FLIGHT.name().toLowerCase()));
433
        Assert.assertTrue(request.getAttribute(
434
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS).equals(
435
                "Content-Type"));
436
    }
437
438
    /**
439
     * Tests a preflight request with credentials enabled.
440
     *
441
     * @throws IOException
442
     * @throws ServletException
443
     */
444
    @Test
445
    public void testDoFilterPreflightWithCredentials() throws IOException,
446
            ServletException {
447
        TesterHttpServletRequest request = new TesterHttpServletRequest();
448
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
449
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
450
        request.setHeader(
451
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT");
452
        request.setHeader(
453
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS,
454
                "Content-Type");
455
        request.setMethod("OPTIONS");
456
        TesterHttpServletResponse response = new TesterHttpServletResponse();
457
458
        CORSFilter corsFilter = new CORSFilter();
459
        corsFilter.init(TesterFilterConfigs.getSecureFilterConfig());
460
        corsFilter.doFilter(request, response, filterChain);
461
462
        Assert.assertTrue(response.getHeader(
463
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
464
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
465
        Assert.assertTrue(response.getHeader(
466
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS)
467
                .equals("true"));
468
        Assert.assertTrue(request.getAttribute(
469
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals(
470
                Boolean.TRUE));
471
        Assert.assertTrue(request.getAttribute(
472
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
473
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
474
        Assert.assertTrue(request.getAttribute(
475
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
476
                CORSFilter.CORSRequestType.PRE_FLIGHT.name().toLowerCase()));
477
        Assert.assertTrue(request.getAttribute(
478
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS).equals(
479
                "Content-Type"));
480
    }
481
482
    /**
483
     * Tests a preflight request, when specific origin is enabled and
484
     * credentials are disabled.
485
     *
486
     * @throws IOException
487
     * @throws ServletException
488
     */
489
    @Test
490
    public void testDoFilterPreflightWithoutCredentialsAndSpecificOrigin()
491
            throws IOException, ServletException {
492
        TesterHttpServletRequest request = new TesterHttpServletRequest();
493
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
494
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
495
        request.setHeader(
496
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT");
497
        request.setHeader(
498
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS,
499
                "Content-Type");
500
        request.setMethod("OPTIONS");
501
        TesterHttpServletResponse response = new TesterHttpServletResponse();
502
503
        CORSFilter corsFilter = new CORSFilter();
504
        corsFilter.init(TesterFilterConfigs
505
                .getFilterConfigSpecificOriginAndSupportsCredentialsDisabled());
506
        corsFilter.doFilter(request, response, filterChain);
507
508
        Assert.assertTrue(response.getHeader(
509
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
510
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
511
        Assert.assertNull(response
512
                .getHeader(CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS));
513
        Assert.assertTrue(request.getAttribute(
514
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals(
515
                Boolean.TRUE));
516
        Assert.assertTrue(request.getAttribute(
517
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
518
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
519
        Assert.assertTrue(request.getAttribute(
520
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
521
                CORSFilter.CORSRequestType.PRE_FLIGHT.name().toLowerCase()));
522
        Assert.assertTrue(request.getAttribute(
523
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS).equals(
524
                "Content-Type"));
525
    }
526
527
    /**
528
     * Negative test, when a CORS request arrives, with a null origin.
529
     *
530
     * @throws IOException
531
     * @throws ServletException
532
     */
533
    @Test
534
    public void testDoFilterNullOrigin() throws IOException, ServletException {
535
        TesterHttpServletRequest request = new TesterHttpServletRequest();
536
537
        request.setMethod("POST");
538
        request.setContentType("text/plain");
539
        TesterHttpServletResponse response = new TesterHttpServletResponse();
540
541
        CORSFilter corsFilter = new CORSFilter();
542
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
543
        CORSFilter.CORSRequestType requestType = corsFilter
544
                .checkRequestType(request);
545
        Assert.assertEquals(CORSFilter.CORSRequestType.NOT_CORS, requestType);
546
547
        corsFilter.doFilter(request, response, filterChain);
548
549
        Assert.assertTrue(request.getAttribute(
550
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals(
551
                Boolean.FALSE));
552
    }
553
554
    /**
555
     * Tests a CORS request, from an origin that's not allowed.
556
     *
557
     * @throws IOException
558
     * @throws ServletException
559
     */
560
    @Test
561
    public void testDoFilterInvalidCORSOriginNotAllowed() throws IOException,
562
            ServletException {
563
        TesterHttpServletRequest request = new TesterHttpServletRequest();
564
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, "www.google.com");
565
        request.setMethod("POST");
566
        TesterHttpServletResponse response = new TesterHttpServletResponse();
567
568
        CORSFilter corsFilter = new CORSFilter();
569
        corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig());
570
        corsFilter.doFilter(request, response, filterChain);
571
572
        Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN,
573
                response.getStatus());
574
    }
575
576
    /**
577
     * Negative case, when a null request and null response is used.
578
     *
579
     * @throws IOException
580
     * @throws ServletException
581
     */
582
    @Test(expected = ServletException.class)
583
    public void testDoFilterNullRequestNullResponse() throws IOException,
584
            ServletException {
585
        CORSFilter corsFilter = new CORSFilter();
586
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
587
        corsFilter.doFilter(null, null, filterChain);
588
    }
589
590
    /**
591
     * Negative case, when a null request is used.
592
     *
593
     * @throws IOException
594
     * @throws ServletException
595
     */
596
    @Test(expected = ServletException.class)
597
    public void testDoFilterNullRequestResponse() throws IOException,
598
            ServletException {
599
        TesterHttpServletResponse response = new TesterHttpServletResponse();
600
        CORSFilter corsFilter = new CORSFilter();
601
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
602
        corsFilter.doFilter(null, response, filterChain);
603
    }
604
605
    /**
606
     * Negative case, when a null response is used.
607
     *
608
     * @throws IOException
609
     * @throws ServletException
610
     */
611
    @Test(expected = ServletException.class)
612
    public void testDoFilterRequestNullResponse() throws IOException,
613
            ServletException {
614
        TesterHttpServletRequest request = new TesterHttpServletRequest();
615
        CORSFilter corsFilter = new CORSFilter();
616
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
617
        corsFilter.doFilter(request, null, filterChain);
618
    }
619
620
    /**
621
     * Tests filter init for defaults.
622
     *
623
     * @throws IOException
624
     * @throws ServletException
625
     */
626
    @Test
627
    public void testInitDefaultFilterConfig() throws IOException,
628
            ServletException {
629
        TesterHttpServletRequest request = new TesterHttpServletRequest();
630
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
631
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
632
        request.setMethod("GET");
633
        TesterHttpServletResponse response = new TesterHttpServletResponse();
634
635
        CORSFilter corsFilter = new CORSFilter();
636
        corsFilter.init(null);
637
        corsFilter.doFilter(request, response, filterChain);
638
639
        Assert.assertTrue(response.getHeader(
640
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
641
                "https://www.apache.org"));
642
        Assert.assertTrue(request.getAttribute(
643
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals(
644
                Boolean.TRUE));
645
        Assert.assertTrue(request.getAttribute(
646
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
647
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
648
        Assert.assertTrue(request.getAttribute(
649
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
650
                CORSFilter.CORSRequestType.SIMPLE.name().toLowerCase()));
651
    }
652
653
    /**
654
     * Tests filter init when an invalid filter config is provided with invalid
655
     * max age.
656
     *
657
     * @throws IOException
658
     * @throws ServletException
659
     */
660
    @Test(expected = ServletException.class)
661
    public void testInitInvalidFilterConfig() throws IOException,
662
            ServletException {
663
        CORSFilter corsFilter = new CORSFilter();
664
        corsFilter.init(TesterFilterConfigs
665
                .getFilterConfigInvalidMaxPreflightAge());
666
        // If we don't get an exception at this point, then all mocked objects
667
        // worked as expected.
668
    }
669
670
    /**
671
     * Tests if a non-simple request is given to simple request handler.
672
     *
673
     * @throws IOException
674
     * @throws ServletException
675
     */
676
    @Test(expected = IllegalArgumentException.class)
677
    public void testNotSimple() throws IOException, ServletException {
678
        TesterHttpServletRequest request = new TesterHttpServletRequest();
679
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
680
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
681
        request.setHeader(
682
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT");
683
        request.setHeader(
684
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS,
685
                "Content-Type");
686
        request.setMethod("OPTIONS");
687
        TesterHttpServletResponse response = new TesterHttpServletResponse();
688
689
        CORSFilter corsFilter = new CORSFilter();
690
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
691
        corsFilter.handleSimpleCORS(request, response, filterChain);
692
    }
693
694
    /**
695
     * When a non-preflight request is given to a pre-flight requets handler.
696
     *
697
     * @throws IOException
698
     * @throws ServletException
699
     */
700
    @Test(expected = IllegalArgumentException.class)
701
    public void testNotPreflight() throws IOException, ServletException {
702
        TesterHttpServletRequest request = new TesterHttpServletRequest();
703
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
704
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
705
        request.setMethod("GET");
706
        TesterHttpServletResponse response = new TesterHttpServletResponse();
707
708
        CORSFilter corsFilter = new CORSFilter();
709
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
710
        corsFilter.handlePreflightCORS(request, response, filterChain);
711
    }
712
713
    /**
714
     * Tests when a null request and a null response is provided.
715
     */
716
    @Test(expected = IllegalArgumentException.class)
717
    public void testDecorateCORSPropertiesNullRequestNullCORSRequestType() {
718
        CORSFilter.decorateCORSProperties(null, null);
719
    }
720
721
    /**
722
     * Tests when a null request is provided.
723
     */
724
    @Test(expected = IllegalArgumentException.class)
725
    public void testDecorateCORSPropertiesNullRequestValidCORSRequestType() {
726
        CORSFilter.decorateCORSProperties(null,
727
                CORSFilter.CORSRequestType.SIMPLE);
728
    }
729
730
    /**
731
     * Tests when a null response is provided.
732
     */
733
    @Test(expected = IllegalArgumentException.class)
734
    public void testDecorateCORSPropertiesValidRequestNullRequestType() {
735
        TesterHttpServletRequest request = new TesterHttpServletRequest();
736
        CORSFilter.decorateCORSProperties(request, null);
737
    }
738
739
    /**
740
     * Test a normal non cross-origin request.
741
     */
742
    @Test
743
    public void testDecorateCORSPropertiesCORSRequestTypeNotCORS() {
744
        TesterHttpServletRequest request = new TesterHttpServletRequest();
745
        CORSFilter.decorateCORSProperties(request,
746
                CORSFilter.CORSRequestType.NOT_CORS);
747
        Assert.assertTrue(request.getAttribute(
748
                CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals(
749
                Boolean.FALSE));
750
    }
751
752
    /**
753
     * Tests an invalid request type.
754
     *
755
     * @throws ServletException
756
     */
757
    @Test
758
    public void testDecorateCORSPropertiesCORSRequestTypeInvalidCORS() {
759
        TesterHttpServletRequest request = new TesterHttpServletRequest();
760
        CORSFilter.decorateCORSProperties(request,
761
                CORSFilter.CORSRequestType.INVALID_CORS);
762
        Assert.assertNull(request
763
                .getAttribute(CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST));
764
    }
765
766
    /**
767
     * Tests a simple request type when any origin is enabled.
768
     *
769
     * @throws ServletException
770
     */
771
    @Test
772
    public void testCheckSimpleRequestTypeAnyOrigin() throws ServletException {
773
        TesterHttpServletRequest request = new TesterHttpServletRequest();
774
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, "http://www.w3.org");
775
        request.setMethod("GET");
776
        CORSFilter corsFilter = new CORSFilter();
777
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
778
        CORSFilter.CORSRequestType requestType = corsFilter
779
                .checkRequestType(request);
780
        Assert.assertEquals(CORSFilter.CORSRequestType.SIMPLE, requestType);
781
    }
782
783
    /**
784
     * Happy path test, when a valid CORS Simple request arrives.
785
     *
786
     * @throws ServletException
787
     */
788
    @Test
789
    public void testCheckSimpleRequestType() throws ServletException {
790
        TesterHttpServletRequest request = new TesterHttpServletRequest();
791
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
792
                TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG);
793
        request.setMethod("GET");
794
        CORSFilter corsFilter = new CORSFilter();
795
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
796
        CORSFilter.CORSRequestType requestType = corsFilter
797
                .checkRequestType(request);
798
        Assert.assertEquals(CORSFilter.CORSRequestType.SIMPLE, requestType);
799
    }
800
801
    /**
802
     * Happy path test, when a valid CORS Simple request arrives.
803
     *
804
     * @throws ServletException
805
     */
806
    @Test
807
    public void testCheckActualRequestType() throws ServletException {
808
        TesterHttpServletRequest request = new TesterHttpServletRequest();
809
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
810
                TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG);
811
        request.setMethod("PUT");
812
        CORSFilter corsFilter = new CORSFilter();
813
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
814
        CORSFilter.CORSRequestType requestType = corsFilter
815
                .checkRequestType(request);
816
        Assert.assertEquals(CORSFilter.CORSRequestType.ACTUAL, requestType);
817
    }
818
819
    /**
820
     * Happy path test, when a valid CORS Simple request arrives.
821
     *
822
     * @throws ServletException
823
     */
824
    @Test
825
    public void testCheckActualRequestTypeMethodPOSTNotSimpleHeaders()
826
            throws ServletException {
827
        TesterHttpServletRequest request = new TesterHttpServletRequest();
828
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
829
                TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG);
830
        request.setMethod("POST");
831
        request.setContentType("application/json");
832
        CORSFilter corsFilter = new CORSFilter();
833
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
834
        CORSFilter.CORSRequestType requestType = corsFilter
835
                .checkRequestType(request);
836
        Assert.assertEquals(CORSFilter.CORSRequestType.ACTUAL, requestType);
837
    }
838
839
    /**
840
     * Happy path test, when a valid CORS Pre-flight request arrives.
841
     *
842
     * @throws ServletException
843
     */
844
    @Test
845
    public void testCheckPreFlightRequestType() throws ServletException {
846
        TesterHttpServletRequest request = new TesterHttpServletRequest();
847
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
848
                TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG);
849
        request.setHeader(
850
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT");
851
        request.setHeader(
852
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS,
853
                "Content-Type");
854
        request.setMethod("OPTIONS");
855
        CORSFilter corsFilter = new CORSFilter();
856
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
857
        CORSFilter.CORSRequestType requestType = corsFilter
858
                .checkRequestType(request);
859
        Assert.assertEquals(CORSFilter.CORSRequestType.PRE_FLIGHT, requestType);
860
    }
861
862
    /**
863
     * when a valid CORS Pre-flight request arrives, with no
864
     * Access-Control-Request-Method
865
     *
866
     * @throws ServletException
867
     * @throws IOException
868
     */
869
    @Test
870
    public void testCheckPreFlightRequestTypeNoACRM() throws ServletException,
871
            IOException {
872
        TesterHttpServletRequest request = new TesterHttpServletRequest();
873
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
874
                TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG);
875
876
        request.setMethod("OPTIONS");
877
        CORSFilter corsFilter = new CORSFilter();
878
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
879
        CORSFilter.CORSRequestType requestType = corsFilter
880
                .checkRequestType(request);
881
        Assert.assertEquals(CORSFilter.CORSRequestType.ACTUAL, requestType);
882
    }
883
884
    /**
885
     * when a valid CORS Pre-flight request arrives, with empty
886
     * Access-Control-Request-Method
887
     *
888
     * @throws ServletException
889
     * @throws IOException
890
     */
891
    @Test
892
    public void testCheckPreFlightRequestTypeEmptyACRM()
893
            throws ServletException, IOException {
894
        TesterHttpServletRequest request = new TesterHttpServletRequest();
895
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
896
                TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG);
897
        request.setHeader(
898
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "");
899
        request.setMethod("OPTIONS");
900
        CORSFilter corsFilter = new CORSFilter();
901
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
902
        CORSFilter.CORSRequestType requestType = corsFilter
903
                .checkRequestType(request);
904
        Assert.assertEquals(CORSFilter.CORSRequestType.INVALID_CORS,
905
                requestType);
906
    }
907
908
    /**
909
     * Happy path test, when a valid CORS Pre-flight request arrives.
910
     *
911
     * @throws ServletException
912
     */
913
    @Test
914
    public void testCheckPreFlightRequestTypeNoHeaders()
915
            throws ServletException {
916
        TesterHttpServletRequest request = new TesterHttpServletRequest();
917
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
918
                TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG);
919
        request.setHeader(
920
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT");
921
        request.setMethod("OPTIONS");
922
        CORSFilter corsFilter = new CORSFilter();
923
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
924
        CORSFilter.CORSRequestType requestType = corsFilter
925
                .checkRequestType(request);
926
        Assert.assertEquals(CORSFilter.CORSRequestType.PRE_FLIGHT, requestType);
927
    }
928
929
    /**
930
     * Section 6.2.3
931
     *
932
     * @throws ServletException
933
     * @throws IOException
934
     */
935
    @Test
936
    public void testCheckPreFlightRequestTypeInvalidRequestMethod()
937
            throws ServletException, IOException {
938
        TesterHttpServletRequest request = new TesterHttpServletRequest();
939
        TesterHttpServletResponse response = new TesterHttpServletResponse();
940
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
941
                TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG);
942
        request.setHeader(
943
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD,
944
                "POLITE");
945
        request.setMethod("OPTIONS");
946
        CORSFilter corsFilter = new CORSFilter();
947
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
948
        corsFilter.doFilter(request, response, filterChain);
949
        Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN,
950
                response.getStatus());
951
    }
952
953
    /**
954
     * Section Section 6.2.5
955
     *
956
     * @throws ServletException
957
     * @throws IOException
958
     */
959
    @Test
960
    public void testCheckPreFlightRequestTypeUnsupportedRequestMethod()
961
            throws ServletException, IOException {
962
        TesterHttpServletRequest request = new TesterHttpServletRequest();
963
        TesterHttpServletResponse response = new TesterHttpServletResponse();
964
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
965
                TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG);
966
        request.setHeader(
967
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD,
968
                "TRACE");
969
        request.setMethod("OPTIONS");
970
        CORSFilter corsFilter = new CORSFilter();
971
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
972
        corsFilter.doFilter(request, response, filterChain);
973
        Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN,
974
                response.getStatus());
975
    }
976
977
    /**
978
     * Section Section 6.2.6
979
     *
980
     * @throws ServletException
981
     * @throws IOException
982
     */
983
    @Test
984
    public void testCheckPreFlightRequestTypeUnsupportedRequestHeaders()
985
            throws ServletException, IOException {
986
        TesterHttpServletRequest request = new TesterHttpServletRequest();
987
        TesterHttpServletResponse response = new TesterHttpServletResponse();
988
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
989
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
990
        request.setHeader(
991
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT");
992
        request.setHeader(
993
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS,
994
                "X-ANSWER");
995
        request.setMethod("OPTIONS");
996
        CORSFilter corsFilter = new CORSFilter();
997
        corsFilter.init(TesterFilterConfigs.getSecureFilterConfig());
998
        corsFilter.doFilter(request, response, filterChain);
999
        Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN,
1000
                response.getStatus());
1001
    }
1002
1003
    /**
1004
     * Section Section 6.2.7
1005
     *
1006
     * @throws ServletException
1007
     * @throws IOException
1008
     */
1009
    @Test
1010
    public void testCheckPreFlightRequestTypeAnyOriginNoWithCredentials()
1011
            throws ServletException, IOException {
1012
        TesterHttpServletRequest request = new TesterHttpServletRequest();
1013
        TesterHttpServletResponse response = new TesterHttpServletResponse();
1014
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
1015
                TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG);
1016
        request.setHeader(
1017
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT");
1018
        request.setHeader(
1019
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS,
1020
                "Origin");
1021
        request.setMethod("OPTIONS");
1022
        CORSFilter corsFilter = new CORSFilter();
1023
        corsFilter.init(TesterFilterConfigs
1024
                .getFilterConfigAnyOriginAndSupportsCredentialsDisabled());
1025
        corsFilter.doFilter(request, response, filterChain);
1026
        Assert.assertTrue(response.getHeader(
1027
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
1028
                "*"));
1029
        Assert.assertNull(response
1030
                .getHeader(CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS));
1031
    }
1032
1033
    /**
1034
     * Test when a preflight request is made from an origin that's not allowed.
1035
     *
1036
     * @throws ServletException
1037
     * @throws IOException
1038
     */
1039
    @Test
1040
    public void testCheckPreFlightRequestTypeOriginNotAllowed()
1041
            throws ServletException, IOException {
1042
        TesterHttpServletRequest request = new TesterHttpServletRequest();
1043
        TesterHttpServletResponse response = new TesterHttpServletResponse();
1044
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, "www.ebay.com");
1045
        request.setHeader(
1046
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT");
1047
        request.setMethod("OPTIONS");
1048
        CORSFilter corsFilter = new CORSFilter();
1049
        corsFilter.init(TesterFilterConfigs.getSecureFilterConfig());
1050
        corsFilter.doFilter(request, response, filterChain);
1051
        Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN,
1052
                response.getStatus());
1053
    }
1054
1055
    /**
1056
     * Happy path test, when a valid CORS Pre-flight request arrives.
1057
     *
1058
     * @throws ServletException
1059
     */
1060
    @Test
1061
    public void testCheckPreFlightRequestTypeEmptyHeaders()
1062
            throws ServletException {
1063
        TesterHttpServletRequest request = new TesterHttpServletRequest();
1064
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
1065
                TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG);
1066
        request.setHeader(
1067
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT");
1068
        request.setHeader(
1069
                CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, "");
1070
        request.setMethod("OPTIONS");
1071
        CORSFilter corsFilter = new CORSFilter();
1072
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
1073
        CORSFilter.CORSRequestType requestType = corsFilter
1074
                .checkRequestType(request);
1075
        Assert.assertEquals(CORSFilter.CORSRequestType.PRE_FLIGHT, requestType);
1076
    }
1077
1078
    /**
1079
     * Negative test, when a CORS request arrives, with an empty origin.
1080
     *
1081
     * @throws ServletException
1082
     */
1083
    @Test
1084
    public void testCheckNotCORSRequestTypeEmptyOrigin()
1085
            throws ServletException {
1086
        TesterHttpServletRequest request = new TesterHttpServletRequest();
1087
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, "");
1088
        request.setMethod("GET");
1089
        CORSFilter corsFilter = new CORSFilter();
1090
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
1091
        CORSFilter.CORSRequestType requestType = corsFilter
1092
                .checkRequestType(request);
1093
        Assert.assertEquals(CORSFilter.CORSRequestType.INVALID_CORS,
1094
                requestType);
1095
    }
1096
1097
    /**
1098
     * Tests for failure, when a different domain is used, that's not in the
1099
     * allowed list of origins.
1100
     *
1101
     * @throws ServletException
1102
     * @throws IOException
1103
     */
1104
    @Test
1105
    public void testCheckInvalidOrigin() throws ServletException, IOException {
1106
        TesterHttpServletRequest request = new TesterHttpServletRequest();
1107
        TesterHttpServletResponse response = new TesterHttpServletResponse();
1108
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, "www.example.com");
1109
        request.setMethod("GET");
1110
        CORSFilter corsFilter = new CORSFilter();
1111
        corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig());
1112
        corsFilter.doFilter(request, response, filterChain);
1113
        Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN,
1114
                response.getStatus());
1115
    }
1116
1117
    /**
1118
     * Tests for failure, when a different sub-domain is used, that's not in the
1119
     * allowed list of origins.
1120
     *
1121
     * @throws ServletException
1122
     * @throws IOException
1123
     */
1124
    @Test
1125
    public void testCheckInvalidOriginNotAllowedSubdomain()
1126
            throws ServletException, IOException {
1127
        TesterHttpServletRequest request = new TesterHttpServletRequest();
1128
        TesterHttpServletResponse response = new TesterHttpServletResponse();
1129
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
1130
                "http://commons.apache.org");
1131
        request.setMethod("GET");
1132
        CORSFilter corsFilter = new CORSFilter();
1133
        corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig());
1134
        corsFilter.doFilter(request, response, filterChain);
1135
        Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN,
1136
                response.getStatus());
1137
    }
1138
1139
    /**
1140
     * PUT is not an allowed request method.
1141
     *
1142
     * @throws ServletException
1143
     * @throws IOException
1144
     */
1145
    @Test
1146
    public void testCheckInvalidRequestMethod() throws ServletException,
1147
            IOException {
1148
        TesterHttpServletRequest request = new TesterHttpServletRequest();
1149
        TesterHttpServletResponse response = new TesterHttpServletResponse();
1150
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
1151
                "http://tomcat.apache.org");
1152
        request.setMethod("PUT");
1153
        CORSFilter corsFilter = new CORSFilter();
1154
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
1155
        corsFilter.doFilter(request, response, filterChain);
1156
        Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN,
1157
                response.getStatus());
1158
    }
1159
1160
    /**
1161
     * When requestMethod is null
1162
     *
1163
     * @throws ServletException
1164
     */
1165
    @Test
1166
    public void testCheckNullRequestMethod() throws ServletException {
1167
        TesterHttpServletRequest request = new TesterHttpServletRequest();
1168
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
1169
                "http://tomcat.apache.org");
1170
        request.setMethod(null);
1171
        CORSFilter corsFilter = new CORSFilter();
1172
        corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig());
1173
        CORSFilter.CORSRequestType requestType = corsFilter
1174
                .checkRequestType(request);
1175
        Assert.assertEquals(CORSFilter.CORSRequestType.INVALID_CORS,
1176
                requestType);
1177
    }
1178
1179
    /**
1180
     * "http://tomcat.apache.org" is an allowed origin and
1181
     * "https://tomcat.apache.org" is not, because scheme doesn't match
1182
     *
1183
     * @throws ServletException
1184
     */
1185
    @Test
1186
    public void testCheckForSchemeVariance() throws ServletException {
1187
        TesterHttpServletRequest request = new TesterHttpServletRequest();
1188
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
1189
                "https://tomcat.apache.org");
1190
        request.setMethod("POST");
1191
        CORSFilter corsFilter = new CORSFilter();
1192
        corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig());
1193
        CORSFilter.CORSRequestType requestType = corsFilter
1194
                .checkRequestType(request);
1195
        Assert.assertEquals(CORSFilter.CORSRequestType.INVALID_CORS,
1196
                requestType);
1197
    }
1198
1199
    /**
1200
     * "http://tomcat.apache.org" is an allowed origin and
1201
     * "http://tomcat.apache.org:8080" is not, because ports doesn't match
1202
     *
1203
     * @throws ServletException
1204
     * @throws IOException
1205
     */
1206
    @Test
1207
    public void testCheckForPortVariance() throws ServletException, IOException {
1208
        TesterHttpServletRequest request = new TesterHttpServletRequest();
1209
        TesterHttpServletResponse response = new TesterHttpServletResponse();
1210
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
1211
                "http://tomcat.apache.org:8080");
1212
        request.setMethod("GET");
1213
        CORSFilter corsFilter = new CORSFilter();
1214
        corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig());
1215
        corsFilter.doFilter(request, response, filterChain);
1216
        Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN,
1217
                response.getStatus());
1218
    }
1219
1220
    /**
1221
     * Tests for failure, when an invalid {@link HttpServletRequest} is
1222
     * encountered.
1223
     *
1224
     * @throws ServletException
1225
     */
1226
    @Test(expected = IllegalArgumentException.class)
1227
    public void testCheckRequestTypeNull() throws ServletException {
1228
        HttpServletRequest request = null;
1229
        CORSFilter corsFilter = new CORSFilter();
1230
        corsFilter.checkRequestType(request);
1231
    }
1232
1233
    /**
1234
     * Tests a normal join of multiple elements, with comma separator.
1235
     */
1236
    @Test
1237
    public void testJoin() {
1238
        Set<String> elements = new LinkedHashSet<String>();
1239
        String separator = ",";
1240
        elements.add("world");
1241
        elements.add("peace");
1242
        String join = CORSFilter.join(elements, separator);
1243
        Assert.assertTrue("world,peace".equals(join));
1244
    }
1245
1246
    /**
1247
     * Tests join with only 1 element.
1248
     */
1249
    @Test
1250
    public void testJoinSingleElement() {
1251
        Set<String> elements = new LinkedHashSet<String>();
1252
        String separator = ",";
1253
        elements.add("world");
1254
        String join = CORSFilter.join(elements, separator);
1255
        Assert.assertTrue("world".equals(join));
1256
    }
1257
1258
    /**
1259
     * Tests join when a null separator is provided, the default comma is used.
1260
     */
1261
    @Test
1262
    public void testJoinSepNull() {
1263
        Set<String> elements = new LinkedHashSet<String>();
1264
        String separator = null;
1265
        elements.add("world");
1266
        elements.add("peace");
1267
        String join = CORSFilter.join(elements, separator);
1268
        Assert.assertTrue("world,peace".equals(join));
1269
    }
1270
1271
    /**
1272
     * Tests join when elements collection is null.
1273
     */
1274
    @Test
1275
    public void testJoinElementsNull() {
1276
        Set<String> elements = null;
1277
        String separator = ",";
1278
        String join = CORSFilter.join(elements, separator);
1279
1280
        Assert.assertNull(join);
1281
    }
1282
1283
    /**
1284
     * Tests join when atleast one null elements is there.
1285
     */
1286
    @Test
1287
    public void testJoinOneNullElement() {
1288
        Set<String> elements = new LinkedHashSet<String>();
1289
        String separator = ",";
1290
        elements.add(null);
1291
        elements.add("peace");
1292
        String join = CORSFilter.join(elements, separator);
1293
        Assert.assertTrue(",peace".equals(join));
1294
    }
1295
1296
    /**
1297
     * Tests join when null elements are there.
1298
     */
1299
    @Test
1300
    public void testJoinAllNullElements() {
1301
        Set<String> elements = new LinkedHashSet<String>();
1302
        String separator = ",";
1303
        elements.add(null);
1304
        elements.add(null);
1305
        String join = CORSFilter.join(elements, separator);
1306
        Assert.assertTrue("".equals(join));
1307
    }
1308
1309
    /**
1310
     * Tests join, for empty strings.
1311
     */
1312
    @Test
1313
    public void testJoinAllEmptyElements() {
1314
        Set<String> elements = new LinkedHashSet<String>();
1315
        String separator = ",";
1316
        elements.add("");
1317
        elements.add("");
1318
        String join = CORSFilter.join(elements, separator);
1319
        Assert.assertTrue("".equals(join));
1320
    }
1321
1322
    /**
1323
     * Tests join, with a pipe separator.
1324
     */
1325
    @Test
1326
    public void testJoinPipeSeparator() {
1327
        Set<String> elements = new LinkedHashSet<String>();
1328
        String separator = "|";
1329
        elements.add("world");
1330
        elements.add("peace");
1331
        String join = CORSFilter.join(elements, separator);
1332
        Assert.assertTrue("world|peace".equals(join));
1333
    }
1334
1335
    /**
1336
     * Tests default filter config.
1337
     *
1338
     * @throws ServletException
1339
     */
1340
    @Test
1341
    public void testWithFilterConfig() throws ServletException {
1342
        CORSFilter corsFilter = new CORSFilter();
1343
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
1344
        Assert.assertTrue(corsFilter.getAllowedHttpHeaders().size() == 6);
1345
        Assert.assertTrue(corsFilter.getAllowedHttpMethods().size() == 4);
1346
        Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0);
1347
        Assert.assertTrue(corsFilter.isAnyOriginAllowed());
1348
        Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0);
1349
        Assert.assertTrue(corsFilter.isSupportsCredentials());
1350
        Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800);
1351
        Assert.assertTrue(!corsFilter.isLoggingEnabled());
1352
    }
1353
1354
    /**
1355
     * Tests an invalid non-number max age.
1356
     *
1357
     * @throws ServletException
1358
     */
1359
    @Test(expected = ServletException.class)
1360
    public void testWithFilterConfigInvalidPreflightAge()
1361
            throws ServletException {
1362
        CORSFilter corsFilter = new CORSFilter();
1363
        corsFilter.init(TesterFilterConfigs
1364
                .getFilterConfigInvalidMaxPreflightAge());
1365
    }
1366
1367
    /**
1368
     * Tests empty config object.
1369
     *
1370
     * @throws ServletException
1371
     */
1372
    @Test
1373
    public void testWithStringParserEmpty() throws ServletException {
1374
        CORSFilter corsFilter = new CORSFilter();
1375
        corsFilter.init(TesterFilterConfigs.getEmptyFilterConfig());
1376
        Assert.assertTrue(corsFilter.getAllowedHttpHeaders().size() == 0);
1377
        Assert.assertTrue(corsFilter.getAllowedHttpMethods().size() == 0);
1378
        Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0);
1379
        Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0);
1380
        Assert.assertFalse(corsFilter.isSupportsCredentials());
1381
        Assert.assertTrue(corsFilter.getPreflightMaxAge() == 0);
1382
        Assert.assertTrue(!corsFilter.isLoggingEnabled());
1383
    }
1384
1385
    /**
1386
     * If an init param is null, it's default value will be used.
1387
     *
1388
     * @throws ServletException
1389
     */
1390
    @Test
1391
    public void testWithStringParserNull() throws ServletException {
1392
        CORSFilter corsFilter = new CORSFilter();
1393
        corsFilter.init(TesterFilterConfigs.getNullFilterConfig());
1394
        Assert.assertTrue(corsFilter.getAllowedHttpHeaders().size() == 6);
1395
        Assert.assertTrue(corsFilter.getAllowedHttpMethods().size() == 4);
1396
        Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0);
1397
        Assert.assertTrue(corsFilter.isAnyOriginAllowed());
1398
        Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0);
1399
        Assert.assertTrue(corsFilter.isSupportsCredentials());
1400
        Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800);
1401
        Assert.assertTrue(!corsFilter.isLoggingEnabled());
1402
    }
1403
1404
    /**
1405
     * A valid origin.
1406
     */
1407
    @Test
1408
    public void testValidOrigin() {
1409
        Assert.assertTrue(CORSFilter.isValidOrigin("http://www.w3.org"));
1410
    }
1411
1412
    /**
1413
     * Invalid origin, \r\n
1414
     */
1415
    @Test
1416
    public void testInValidOriginCRLF() {
1417
        Assert.assertFalse(CORSFilter.isValidOrigin("http://www.w3.org\r\n"));
1418
    }
1419
1420
    /**
1421
     * Invalid origin, encoded chars
1422
     */
1423
    @Test
1424
    public void testInValidOriginEncodedCRLF1() {
1425
        Assert.assertFalse(CORSFilter.isValidOrigin("http://www.w3.org%0d%0a"));
1426
    }
1427
1428
    /**
1429
     * Invalid origin, encoded chars in caps.
1430
     */
1431
    @Test
1432
    public void testInValidOriginEncodedCRLF2() {
1433
        Assert.assertFalse(CORSFilter.isValidOrigin("http://www.w3.org%0D%0A"));
1434
    }
1435
1436
    /**
1437
     * Invalid origin, double encoded chars.
1438
     */
1439
    @Test
1440
    public void testInValidOriginEncodedCRLF3() {
1441
        Assert.assertFalse(CORSFilter
1442
                .isValidOrigin("http://www.w3.org%0%0d%0ad%0%0d%0aa"));
1443
    }
1444
1445
    /**
1446
     * Test for CRLF, an invalid request. \r\n characters.
1447
     *
1448
     * @throws ServletException
1449
     */
1450
    @Test
1451
    public void testCheckInvalidCRLF1() throws ServletException {
1452
        TesterHttpServletRequest request = new TesterHttpServletRequest();
1453
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
1454
                "http://www.w3.org\r\n");
1455
        request.setMethod("GET");
1456
        CORSFilter corsFilter = new CORSFilter();
1457
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
1458
        CORSFilter.CORSRequestType requestType = corsFilter
1459
                .checkRequestType(request);
1460
        Assert.assertEquals(CORSFilter.CORSRequestType.INVALID_CORS,
1461
                requestType);
1462
    }
1463
1464
    /**
1465
     * Test for CRLF, an invalid request.
1466
     *
1467
     * @throws ServletException
1468
     */
1469
    @Test
1470
    public void testCheckInvalidCRLF2() throws ServletException {
1471
        TesterHttpServletRequest request = new TesterHttpServletRequest();
1472
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
1473
                "http://www.w3.org\r");
1474
        request.setMethod("GET");
1475
        CORSFilter corsFilter = new CORSFilter();
1476
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
1477
        CORSFilter.CORSRequestType requestType = corsFilter
1478
                .checkRequestType(request);
1479
        Assert.assertEquals(CORSFilter.CORSRequestType.INVALID_CORS,
1480
                requestType);
1481
    }
1482
1483
    /**
1484
     * Test for CRLF, an invalid request. encoded chars
1485
     *
1486
     * @throws ServletException
1487
     */
1488
    @Test
1489
    public void testCheckInvalidCRLF3() throws ServletException {
1490
        TesterHttpServletRequest request = new TesterHttpServletRequest();
1491
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
1492
                "http://www.w3.org%0d%0a");
1493
        request.setMethod("GET");
1494
        CORSFilter corsFilter = new CORSFilter();
1495
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
1496
        CORSFilter.CORSRequestType requestType = corsFilter
1497
                .checkRequestType(request);
1498
        Assert.assertEquals(CORSFilter.CORSRequestType.INVALID_CORS,
1499
                requestType);
1500
    }
1501
1502
    /**
1503
     * Test for CRLF, an invalid request. encoded chars in caps.
1504
     *
1505
     * @throws ServletException
1506
     */
1507
    @Test
1508
    public void testCheckInvalidCRLF4() throws ServletException {
1509
        TesterHttpServletRequest request = new TesterHttpServletRequest();
1510
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
1511
                "http://www.w3.org%0D%0A");
1512
        request.setMethod("GET");
1513
        CORSFilter corsFilter = new CORSFilter();
1514
        corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig());
1515
        CORSFilter.CORSRequestType requestType = corsFilter
1516
                .checkRequestType(request);
1517
        Assert.assertEquals(CORSFilter.CORSRequestType.INVALID_CORS,
1518
                requestType);
1519
    }
1520
1521
    /**
1522
     * Request should not be decorated.
1523
     *
1524
     * @throws IOException
1525
     * @throws ServletException
1526
     */
1527
    @Test
1528
    public void testDecorateRequestDisabled() throws IOException,
1529
            ServletException {
1530
        TesterHttpServletRequest request = new TesterHttpServletRequest();
1531
        request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN,
1532
                TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
1533
        request.setMethod("GET");
1534
        TesterHttpServletResponse response = new TesterHttpServletResponse();
1535
1536
        CORSFilter corsFilter = new CORSFilter();
1537
        corsFilter.init(TesterFilterConfigs
1538
                .getFilterConfigDecorateRequestDisabled());
1539
        corsFilter.doFilter(request, response, filterChain);
1540
1541
        Assert.assertTrue(response.getHeader(
1542
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
1543
                "https://www.apache.org"));
1544
        Assert.assertNull(request
1545
                .getAttribute(CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST));
1546
        Assert.assertNull(request
1547
                .getAttribute(CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN));
1548
        Assert.assertNull(request
1549
                .getAttribute(CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS));
1550
        Assert.assertNull(request
1551
                .getAttribute(CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE));
1552
    }
1553
1554
    /**
1555
     * Not sure how to test doing nothing. Sigh!
1556
     */
1557
    @Test
1558
    public void testDestroy() {
1559
        // Nothing to test.
1560
        // NO-OP
1561
    }
1562
}
0
  + native
1563
  + native
(-)java/org/apache/catalina/filters/CORSFilter.java (+1155 lines)
Line 0 Link Here
1
/**
2
 * Copyright 2012-2013 eBay Software Foundation, All Rights Reserved.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5
 * use this file except in compliance with the License. You may obtain a copy of
6
 * the License at
7
 *
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
 * License for the specific language governing permissions and limitations under
14
 * the License.
15
 */
16
package org.apache.catalina.filters;
17
18
import java.io.IOException;
19
import java.net.URI;
20
import java.net.URISyntaxException;
21
import java.util.Arrays;
22
import java.util.Collection;
23
import java.util.HashSet;
24
import java.util.LinkedList;
25
import java.util.List;
26
import java.util.Set;
27
28
import javax.servlet.Filter;
29
import javax.servlet.FilterChain;
30
import javax.servlet.FilterConfig;
31
import javax.servlet.ServletException;
32
import javax.servlet.ServletRequest;
33
import javax.servlet.ServletResponse;
34
import javax.servlet.http.HttpServletRequest;
35
import javax.servlet.http.HttpServletResponse;
36
37
/**
38
 * <p>
39
 * A {@link Filter} that enable client-side cross-origin requests by
40
 * implementing W3C's CORS (<b>C</b>ross-<b>O</b>rigin <b>R</b>esource
41
 * <b>S</b>haring) specification for resources. Each {@link HttpServletRequest}
42
 * request is inspected as per specification, and appropriate response headers
43
 * are added to {@link HttpServletResponse}.
44
 * </p>
45
 *
46
 * <p>
47
 * By default, it also sets following request attributes, that helps to
48
 * determine nature of request downstream.
49
 * <ul>
50
 * <li><b>cors.isCorsRequest:</b> Flag to determine if request is a CORS
51
 * request. Set to <code>true</code> if CORS request; <code>false</code>
52
 * otherwise.</li>
53
 * <li><b>cors.request.origin:</b> The Origin URL, i.e. the URL of the page from
54
 * where the request is originated.</li>
55
 * <li>
56
 * <b>cors.request.type:</b> Type of request. Possible values:
57
 * <ul>
58
 * <li>SIMPLE: A request which is not preceded by a pre-flight request.</li>
59
 * <li>ACTUAL: A request which is preceded by a pre-flight request.</li>
60
 * <li>PRE_FLIGHT: A pre-flight request.</li>
61
 * <li>NOT_CORS: A normal same-origin request.</li>
62
 * <li>INVALID_CORS: A cross-origin request, which is invalid.</li>
63
 * </ul>
64
 * </li>
65
 * <li><b>cors.request.headers:</b> Request headers sent as
66
 * 'Access-Control-Request-Headers' header, for pre-flight request.</li>
67
 * </ul>
68
 * </p>
69
 *
70
 * @author Mohit Soni
71
 * @see <a href="http://www.w3.org/TR/cors/">CORS specification</a>
72
 *
73
 */
74
public final class CORSFilter implements Filter {
75
    // ----------------------------------------------------- Instance variables
76
    /**
77
     * Holds filter configuration.
78
     */
79
    private FilterConfig filterConfig;
80
81
    /**
82
     * A {@link Collection} of origins consisting of zero or more origins that
83
     * are allowed access to the resource.
84
     */
85
    private final Collection<String> allowedOrigins;
86
87
    /**
88
     * Determines if any origin is allowed to make request.
89
     */
90
    private boolean anyOriginAllowed;
91
92
    /**
93
     * A {@link Collection} of methods consisting of zero or more methods that
94
     * are supported by the resource.
95
     */
96
    private final Collection<String> allowedHttpMethods;
97
98
    /**
99
     * A {@link Collection} of headers consisting of zero or more header field
100
     * names that are supported by the resource.
101
     */
102
    private final Collection<String> allowedHttpHeaders;
103
104
    /**
105
     * A {@link Collection} of exposed headers consisting of zero or more header
106
     * field names of headers other than the simple response headers that the
107
     * resource might use and can be exposed.
108
     */
109
    private final Collection<String> exposedHeaders;
110
111
    /**
112
     * A supports credentials flag that indicates whether the resource supports
113
     * user credentials in the request. It is true when the resource does and
114
     * false otherwise.
115
     */
116
    private boolean supportsCredentials;
117
118
    /**
119
     * Indicates (in seconds) how long the results of a pre-flight request can
120
     * be cached in a pre-flight result cache.
121
     */
122
    private long preflightMaxAge;
123
124
    /**
125
     * Controls access log logging.
126
     */
127
    private boolean loggingEnabled;
128
129
    /**
130
     * Determines if the request should be decorated or not.
131
     */
132
    private boolean decorateRequest;
133
134
    // --------------------------------------------------------- Constructor(s)
135
    /**
136
     * Initializes configuration {@link Collection} objects.
137
     */
138
    public CORSFilter() {
139
        this.allowedOrigins = new HashSet<String>();
140
        this.allowedHttpMethods = new HashSet<String>();
141
        this.allowedHttpHeaders = new HashSet<String>();
142
        this.exposedHeaders = new HashSet<String>();
143
    }
144
145
    // --------------------------------------------------------- Public methods
146
    @Override
147
    public void doFilter(final ServletRequest servletRequest,
148
            final ServletResponse servletResponse, final FilterChain filterChain)
149
            throws IOException, ServletException {
150
        if (!(servletRequest instanceof HttpServletRequest)
151
                || !(servletResponse instanceof HttpServletResponse)) {
152
            String message = "CORS doesn't support non-HTTP request or response.";
153
            throw new ServletException(message);
154
        }
155
156
        // Safe to downcast at this point.
157
        HttpServletRequest request = (HttpServletRequest) servletRequest;
158
        HttpServletResponse response = (HttpServletResponse) servletResponse;
159
160
        // Determines the CORS request type.
161
        CORSFilter.CORSRequestType requestType = checkRequestType(request);
162
163
        // Adds CORS specific attributes to request.
164
        if (decorateRequest) {
165
            CORSFilter.decorateCORSProperties(request, requestType);
166
        }
167
        switch (requestType) {
168
        case SIMPLE:
169
            // Handles a Simple CORS request.
170
            this.handleSimpleCORS(request, response, filterChain);
171
            break;
172
        case ACTUAL:
173
            // Handles an Actual CORS request.
174
            this.handleSimpleCORS(request, response, filterChain);
175
            break;
176
        case PRE_FLIGHT:
177
            // Handles a Pre-flight CORS request.
178
            this.handlePreflightCORS(request, response, filterChain);
179
            break;
180
        case NOT_CORS:
181
            // Handles a Normal request that is not a cross-origin request.
182
            this.handleNonCORS(request, response, filterChain);
183
            break;
184
        default:
185
            // Handles a CORS request that violates specification.
186
            this.handleInvalidCORS(request, response, filterChain);
187
            break;
188
        }
189
    }
190
191
    @Override
192
    public void init(final FilterConfig filterConfig) throws ServletException {
193
        // Initialize defaults
194
        parseAndStore(DEFAULT_ALLOWED_ORIGINS, DEFAULT_ALLOWED_HTTP_METHODS,
195
                DEFAULT_ALLOWED_HTTP_HEADERS, DEFAULT_EXPOSED_HEADERS,
196
                DEFAULT_SUPPORTS_CREDENTIALS, DEFAULT_PREFLIGHT_MAXAGE,
197
                DEFAULT_LOGGING_ENABLED, DEFAULT_DECORATE_REQUEST);
198
199
        this.filterConfig = filterConfig;
200
        this.loggingEnabled = false;
201
202
        if (filterConfig != null) {
203
            String configAllowedOrigins = filterConfig
204
                    .getInitParameter(PARAM_CORS_ALLOWED_ORIGINS);
205
            String configAllowedHttpMethods = filterConfig
206
                    .getInitParameter(PARAM_CORS_ALLOWED_METHODS);
207
            String configAllowedHttpHeaders = filterConfig
208
                    .getInitParameter(PARAM_CORS_ALLOWED_HEADERS);
209
            String configExposedHeaders = filterConfig
210
                    .getInitParameter(PARAM_CORS_EXPOSED_HEADERS);
211
            String configSupportsCredentials = filterConfig
212
                    .getInitParameter(PARAM_CORS_SUPPORT_CREDENTIALS);
213
            String configPreflightMaxAge = filterConfig
214
                    .getInitParameter(PARAM_CORS_PREFLIGHT_MAXAGE);
215
            String configLoggingEnabled = filterConfig
216
                    .getInitParameter(PARAM_CORS_LOGGING_ENABLED);
217
            String configDecorateRequest = filterConfig
218
                    .getInitParameter(PARAM_CORS_REQUEST_DECORATE);
219
220
            parseAndStore(configAllowedOrigins, configAllowedHttpMethods,
221
                    configAllowedHttpHeaders, configExposedHeaders,
222
                    configSupportsCredentials, configPreflightMaxAge,
223
                    configLoggingEnabled, configDecorateRequest);
224
        }
225
    }
226
227
    // --------------------------------------------------------------- Handlers
228
    /**
229
     * Handles a CORS request of type {@link CORSRequestType}.SIMPLE.
230
     *
231
     * @param request
232
     *            The {@link HttpServletRequest} object.
233
     * @param response
234
     *            The {@link HttpServletResponse} object.
235
     * @param filterChain
236
     *            The {@link FilterChain} object.
237
     * @throws IOException
238
     * @throws ServletException
239
     * @see <a href="http://www.w3.org/TR/cors/#resource-requests">Simple
240
     *      Cross-Origin Request, Actual Request, and Redirects</a>
241
     */
242
    public void handleSimpleCORS(final HttpServletRequest request,
243
            final HttpServletResponse response, final FilterChain filterChain)
244
            throws IOException, ServletException {
245
        CORSFilter.CORSRequestType requestType = checkRequestType(request);
246
        if (!(requestType == CORSFilter.CORSRequestType.SIMPLE || requestType == CORSFilter.CORSRequestType.ACTUAL)) {
247
            String message = "Expects a HttpServletRequest object of type "
248
                    + CORSFilter.CORSRequestType.SIMPLE + " or "
249
                    + CORSFilter.CORSRequestType.ACTUAL;
250
            throw new IllegalArgumentException(message);
251
        }
252
253
        final String origin = request
254
                .getHeader(CORSFilter.REQUEST_HEADER_ORIGIN);
255
        final String method = request.getMethod();
256
257
        // Section 6.1.2
258
        if (!isOriginAllowed(origin)) {
259
            handleInvalidCORS(request, response, filterChain);
260
            return;
261
        }
262
263
        if (!allowedHttpMethods.contains(method)) {
264
            handleInvalidCORS(request, response, filterChain);
265
            return;
266
        }
267
268
        // Section 6.1.3
269
        // Add a single Access-Control-Allow-Origin header.
270
        if (anyOriginAllowed && !supportsCredentials) {
271
            // If resource doesn't support credentials and if any origin is
272
            // allowed
273
            // to make CORS request, return header with '*'.
274
            response.addHeader(
275
                    CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, "*");
276
        } else {
277
            // If the resource supports credentials add a single
278
            // Access-Control-Allow-Origin header, with the value of the Origin
279
            // header as value.
280
            response.addHeader(
281
                    CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
282
                    origin);
283
        }
284
        // Section 6.1.3
285
        // If the resource supports credentials, add a single
286
        // Access-Control-Allow-Credentials header with the case-sensitive
287
        // string "true" as value.
288
        if (supportsCredentials) {
289
            response.addHeader(
290
                    CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS,
291
                    "true");
292
        }
293
294
        // Section 6.1.4
295
        // If the list of exposed headers is not empty add one or more
296
        // Access-Control-Expose-Headers headers, with as values the header
297
        // field names given in the list of exposed headers.
298
        if ((exposedHeaders != null) && (exposedHeaders.size() > 0)) {
299
            String exposedHeadersString = join(exposedHeaders, ",");
300
            response.addHeader(
301
                    CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS,
302
                    exposedHeadersString);
303
        }
304
305
        // Forward the request down the filter chain.
306
        filterChain.doFilter(request, response);
307
    }
308
309
    /**
310
     * Handles CORS pre-flight request.
311
     *
312
     * @param request
313
     *            The {@link HttpServletRequest} object.
314
     * @param response
315
     *            The {@link HttpServletResponse} object.
316
     * @param filterChain
317
     *            The {@link FilterChain} object.
318
     * @throws IOException
319
     * @throws ServletException
320
     */
321
    public void handlePreflightCORS(final HttpServletRequest request,
322
            final HttpServletResponse response, final FilterChain filterChain)
323
            throws IOException, ServletException {
324
        CORSRequestType requestType = checkRequestType(request);
325
        if (requestType != CORSRequestType.PRE_FLIGHT) {
326
            throw new IllegalArgumentException(
327
                    "Expects a HttpServletRequest object of type "
328
                            + CORSRequestType.PRE_FLIGHT.name().toLowerCase());
329
        }
330
331
        final String origin = request
332
                .getHeader(CORSFilter.REQUEST_HEADER_ORIGIN);
333
334
        // Section 6.2.2
335
        if (!isOriginAllowed(origin)) {
336
            handleInvalidCORS(request, response, filterChain);
337
            return;
338
        }
339
340
        // Section 6.2.3
341
        String accessControlRequestMethod = request
342
                .getHeader(CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD);
343
        if (accessControlRequestMethod == null
344
                || (!HTTP_METHODS.contains(accessControlRequestMethod.trim()))) {
345
            handleInvalidCORS(request, response, filterChain);
346
            return;
347
        } else {
348
            accessControlRequestMethod = accessControlRequestMethod.trim();
349
        }
350
351
        // Section 6.2.4
352
        String accessControlRequestHeadersHeader = request
353
                .getHeader(CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS);
354
        List<String> accessControlRequestHeaders = new LinkedList<String>();
355
        if (accessControlRequestHeadersHeader != null
356
                && !accessControlRequestHeadersHeader.trim().isEmpty()) {
357
            String[] headers = accessControlRequestHeadersHeader.trim().split(
358
                    ",");
359
            for (String header : headers) {
360
                accessControlRequestHeaders.add(header.trim().toLowerCase());
361
            }
362
        }
363
364
        // Section 6.2.5
365
        if (!allowedHttpMethods.contains(accessControlRequestMethod)) {
366
            handleInvalidCORS(request, response, filterChain);
367
            return;
368
        }
369
370
        // Section 6.2.6
371
        if (!accessControlRequestHeaders.isEmpty()) {
372
            for (String header : accessControlRequestHeaders) {
373
                if (!allowedHttpHeaders.contains(header)) {
374
                    handleInvalidCORS(request, response, filterChain);
375
                    return;
376
                }
377
            }
378
        }
379
380
        // Section 6.2.7
381
        if (supportsCredentials) {
382
            response.addHeader(
383
                    CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
384
                    origin);
385
            response.addHeader(
386
                    CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS,
387
                    "true");
388
        } else {
389
            if (anyOriginAllowed) {
390
                response.addHeader(
391
                        CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
392
                        "*");
393
            } else {
394
                response.addHeader(
395
                        CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
396
                        origin);
397
            }
398
        }
399
400
        // Section 6.2.8
401
        if (preflightMaxAge > 0) {
402
            response.addHeader(
403
                    CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_MAX_AGE,
404
                    String.valueOf(preflightMaxAge));
405
        }
406
407
        // Section 6.2.9
408
        response.addHeader(
409
                CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_METHODS,
410
                accessControlRequestMethod);
411
412
        // Section 6.2.10
413
        if ((allowedHttpHeaders != null) && (!allowedHttpHeaders.isEmpty())) {
414
            response.addHeader(
415
                    CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_HEADERS,
416
                    join(allowedHttpHeaders, ","));
417
        }
418
419
        // Do not forward the request down the filter chain.
420
    }
421
422
    /**
423
     * Handles a request, that's not a CORS request, but is a valid request i.e.
424
     * it is not a cross-origin request. This implementation, just forwards the
425
     * request down the filter chain.
426
     *
427
     * @param request
428
     *            The {@link HttpServletRequest} object.
429
     * @param response
430
     *            The {@link HttpServletResponse} object.
431
     * @param filterChain
432
     *            The {@link FilterChain} object.
433
     * @throws IOException
434
     * @throws ServletException
435
     */
436
    public void handleNonCORS(final HttpServletRequest request,
437
            final HttpServletResponse response, final FilterChain filterChain)
438
            throws IOException, ServletException {
439
        // Let request pass.
440
        filterChain.doFilter(request, response);
441
    }
442
443
    /**
444
     * Handles a CORS request that violates specification.
445
     *
446
     * @param request
447
     *            The {@link HttpServletRequest} object.
448
     * @param response
449
     *            The {@link HttpServletResponse} object.
450
     * @param filterChain
451
     *            The {@link FilterChain} object.
452
     * @throws IOException
453
     * @throws ServletException
454
     */
455
    public void handleInvalidCORS(final HttpServletRequest request,
456
            final HttpServletResponse response, final FilterChain filterChain) {
457
        String origin = request.getHeader(CORSFilter.REQUEST_HEADER_ORIGIN);
458
        String method = request.getMethod();
459
        String accessControlRequestHeaders = request
460
                .getHeader(REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS);
461
462
        String message = "Invalid CORS request; Origin=" + origin + ";Method="
463
                + method;
464
        if (accessControlRequestHeaders != null) {
465
            message = message + ";Access-Control-Request-Headers="
466
                    + accessControlRequestHeaders;
467
        }
468
        response.setContentType("text/plain");
469
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
470
        response.resetBuffer();
471
472
        log(message);
473
    }
474
475
    @Override
476
    public void destroy() {
477
        // NOOP
478
    }
479
480
    // -------------------------------------------------------- Utility methods
481
    /**
482
     * Decorates the {@link HttpServletRequest}, with CORS attributes.
483
     * <ul>
484
     * <li><b>cors.isCorsRequest:</b> Flag to determine if request is a CORS
485
     * request. Set to <code>true</code> if CORS request; <code>false</code>
486
     * otherwise.</li>
487
     * <li><b>cors.request.origin:</b> The Origin URL.</li>
488
     * <li><b>cors.request.type:</b> Type of request. Values:
489
     * <code>simple</code> or <code>preflight</code> or <code>not_cors</code> or
490
     * <code>invalid_cors</code></li>
491
     * <li><b>cors.request.headers:</b> Request headers sent as
492
     * 'Access-Control-Request-Headers' header, for pre-flight request.</li>
493
     * </ul>
494
     *
495
     * @param request
496
     *            The {@link HttpServletRequest} object.
497
     * @param corsRequestType
498
     *            The {@link CORSRequestType} object.
499
     */
500
    public static void decorateCORSProperties(final HttpServletRequest request,
501
            final CORSRequestType corsRequestType) {
502
        if (request == null) {
503
            throw new IllegalArgumentException(
504
                    "HttpServletRequest object is null");
505
        }
506
507
        if (corsRequestType == null) {
508
            throw new IllegalArgumentException("CORSRequestType object is null");
509
        }
510
511
        switch (corsRequestType) {
512
        case SIMPLE:
513
            request.setAttribute(
514
                    CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST,
515
                    Boolean.TRUE);
516
            request.setAttribute(CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN,
517
                    request.getHeader(CORSFilter.REQUEST_HEADER_ORIGIN));
518
            request.setAttribute(
519
                    CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE,
520
                    corsRequestType.name().toLowerCase());
521
            break;
522
        case ACTUAL:
523
            request.setAttribute(
524
                    CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST,
525
                    Boolean.TRUE);
526
            request.setAttribute(CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN,
527
                    request.getHeader(CORSFilter.REQUEST_HEADER_ORIGIN));
528
            request.setAttribute(
529
                    CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE,
530
                    corsRequestType.name().toLowerCase());
531
            break;
532
        case PRE_FLIGHT:
533
            request.setAttribute(
534
                    CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST,
535
                    Boolean.TRUE);
536
            request.setAttribute(CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN,
537
                    request.getHeader(CORSFilter.REQUEST_HEADER_ORIGIN));
538
            request.setAttribute(
539
                    CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE,
540
                    corsRequestType.name().toLowerCase());
541
            String headers = request
542
                    .getHeader(REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS);
543
            if (headers == null) {
544
                headers = "";
545
            }
546
            request.setAttribute(
547
                    CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS, headers);
548
            break;
549
        case NOT_CORS:
550
            request.setAttribute(
551
                    CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST,
552
                    Boolean.FALSE);
553
            break;
554
        default:
555
            // Don't set any attributes
556
            break;
557
        }
558
    }
559
560
    /**
561
     * Joins elements of {@link Set} into a string, where each element is
562
     * separated by the provided separator.
563
     *
564
     * @param elements
565
     *            The {@link Set} containing elements to join together.
566
     * @param joinSeparator
567
     *            The character to be used for separating elements.
568
     * @return The joined {@link String}; <code>null</code> if elements
569
     *         {@link Set} is null.
570
     */
571
    public static String join(final Collection<String> elements,
572
            final String joinSeparator) {
573
        String separator = ",";
574
        if (elements == null) {
575
            return null;
576
        }
577
        if (joinSeparator != null) {
578
            separator = joinSeparator;
579
        }
580
        StringBuilder buffer = new StringBuilder();
581
        boolean isFirst = true;
582
        for (String element : elements) {
583
            if (!isFirst) {
584
                buffer.append(separator);
585
            } else {
586
                isFirst = false;
587
            }
588
589
            if (element != null) {
590
                buffer.append(element);
591
            }
592
        }
593
594
        return buffer.toString();
595
    }
596
597
    /**
598
     * Determines the request type.
599
     *
600
     * @param request
601
     * @return {@link CORSRequestType} The type of request.
602
     */
603
    public CORSRequestType checkRequestType(final HttpServletRequest request) {
604
        CORSRequestType requestType = CORSRequestType.INVALID_CORS;
605
        if (request == null) {
606
            throw new IllegalArgumentException(
607
                    "HttpServletRequest object is null");
608
        }
609
        String originHeader = request.getHeader(REQUEST_HEADER_ORIGIN);
610
        // Section 6.1.1 and Section 6.2.1
611
        if (originHeader != null) {
612
            if (originHeader.isEmpty()) {
613
                requestType = CORSRequestType.INVALID_CORS;
614
            } else if (!isValidOrigin(originHeader)) {
615
                requestType = CORSRequestType.INVALID_CORS;
616
            } else {
617
                String method = request.getMethod();
618
                if (method != null && HTTP_METHODS.contains(method)) {
619
                    if ("OPTIONS".equals(method)) {
620
                        String accessControlRequestMethodHeader = request
621
                                .getHeader(REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD);
622
                        if (accessControlRequestMethodHeader != null
623
                                && !accessControlRequestMethodHeader.isEmpty()) {
624
                            requestType = CORSRequestType.PRE_FLIGHT;
625
                        } else if (accessControlRequestMethodHeader != null
626
                                && accessControlRequestMethodHeader.isEmpty()) {
627
                            requestType = CORSRequestType.INVALID_CORS;
628
                        } else {
629
                            requestType = CORSRequestType.ACTUAL;
630
                        }
631
                    } else if ("GET".equals(method) || "HEAD".equals(method)) {
632
                        requestType = CORSRequestType.SIMPLE;
633
                    } else if ("POST".equals(method)) {
634
                        String contentType = request.getContentType();
635
                        if (contentType != null) {
636
                            contentType = contentType.toLowerCase().trim();
637
                            if (SIMPLE_HTTP_REQUEST_CONTENT_TYPE_VALUES
638
                                    .contains(contentType)) {
639
                                requestType = CORSRequestType.SIMPLE;
640
                            } else {
641
                                requestType = CORSRequestType.ACTUAL;
642
                            }
643
                        }
644
                    } else if (COMPLEX_HTTP_METHODS.contains(method)) {
645
                        requestType = CORSRequestType.ACTUAL;
646
                    }
647
                }
648
            }
649
        } else {
650
            requestType = CORSRequestType.NOT_CORS;
651
        }
652
653
        return requestType;
654
    }
655
656
    /**
657
     * Checks if the Origin is allowed to make a CORS request.
658
     *
659
     * @param origin
660
     *            The Origin.
661
     * @return <code>true</code> if origin is allowed; <code>false</code>
662
     *         otherwise.
663
     */
664
    private boolean isOriginAllowed(final String origin) {
665
        if (anyOriginAllowed) {
666
            return true;
667
        }
668
669
        // If 'Origin' header is a case-sensitive match of any of allowed
670
        // origins, then return true, else return false.
671
        return allowedOrigins.contains(origin);
672
    }
673
674
    private void log(String message) {
675
        if (loggingEnabled) {
676
            filterConfig.getServletContext().log(message);
677
        }
678
    }
679
680
    /**
681
     * Parses each param-value and populates configuration variables. If a param
682
     * is provided, it overrides the default.
683
     *
684
     * @param allowedOrigins
685
     *            A {@link String} of comma separated origins.
686
     * @param allowedHttpMethods
687
     *            A {@link String} of comma separated HTTP methods.
688
     * @param allowedHttpHeaders
689
     *            A {@link String} of comma separated HTTP headers.
690
     * @param exposedHeaders
691
     *            A {@link String} of comma separated headers that needs to be
692
     *            exposed.
693
     * @param supportsCredentials
694
     *            "true" if support credentials needs to be enabled.
695
     * @param preflightMaxAge
696
     *            The amount of seconds the user agent is allowed to cache the
697
     *            result of the pre-flight request.
698
     * @param loggingEnabled
699
     *            Flag to control logging to access log.
700
     * @throws ServletException
701
     */
702
    private void parseAndStore(final String allowedOrigins,
703
            final String allowedHttpMethods, final String allowedHttpHeaders,
704
            final String exposedHeaders, final String supportsCredentials,
705
            final String preflightMaxAge, final String loggingEnabled,
706
            final String decorateRequest) throws ServletException {
707
        if (allowedOrigins != null) {
708
            if (allowedOrigins.trim().equals("*")) {
709
                this.anyOriginAllowed = true;
710
            } else {
711
                this.anyOriginAllowed = false;
712
                Set<String> setAllowedOrigins = parseStringToSet(allowedOrigins);
713
                this.allowedOrigins.clear();
714
                this.allowedOrigins.addAll(setAllowedOrigins);
715
            }
716
        }
717
718
        if (allowedHttpMethods != null) {
719
            Set<String> setAllowedHttpMethods = parseStringToSet(allowedHttpMethods);
720
            this.allowedHttpMethods.clear();
721
            this.allowedHttpMethods.addAll(setAllowedHttpMethods);
722
        }
723
724
        if (allowedHttpHeaders != null) {
725
            Set<String> setAllowedHttpHeaders = parseStringToSet(allowedHttpHeaders);
726
            Set<String> lowerCaseHeaders = new HashSet<String>();
727
            for (String header : setAllowedHttpHeaders) {
728
                String lowerCase = header.toLowerCase();
729
                lowerCaseHeaders.add(lowerCase);
730
            }
731
            this.allowedHttpHeaders.clear();
732
            this.allowedHttpHeaders.addAll(lowerCaseHeaders);
733
        }
734
735
        if (exposedHeaders != null) {
736
            Set<String> setExposedHeaders = parseStringToSet(exposedHeaders);
737
            this.exposedHeaders.clear();
738
            this.exposedHeaders.addAll(setExposedHeaders);
739
        }
740
741
        if (supportsCredentials != null) {
742
            // For any value other then 'true' this will be false.
743
            this.supportsCredentials = Boolean
744
                    .parseBoolean(supportsCredentials);
745
        }
746
747
        if (preflightMaxAge != null) {
748
            try {
749
                if (!preflightMaxAge.isEmpty()) {
750
                    this.preflightMaxAge = Long.parseLong(preflightMaxAge);
751
                } else {
752
                    this.preflightMaxAge = 0L;
753
                }
754
            } catch (NumberFormatException e) {
755
                throw new ServletException("Unable to parse preflightMaxAge", e);
756
            }
757
        }
758
759
        if (loggingEnabled != null) {
760
            // For any value other then 'true' this will be false.
761
            this.loggingEnabled = Boolean.parseBoolean(loggingEnabled);
762
        }
763
764
        if (decorateRequest != null) {
765
            // For any value other then 'true' this will be false.
766
            this.decorateRequest = Boolean.parseBoolean(decorateRequest);
767
        }
768
    }
769
770
    /**
771
     * Takes a comma separated list and returns a Set<String>.
772
     *
773
     * @param data
774
     *            A comma separated list of strings.
775
     * @return Set<String>
776
     */
777
    private Set<String> parseStringToSet(final String data) {
778
        String[] splits;
779
780
        if (data != null && data.length() > 0) {
781
            splits = data.split(",");
782
        } else {
783
            splits = new String[] {};
784
        }
785
786
        Set<String> set = new HashSet<String>();
787
        if (splits.length > 0) {
788
            for (String split : splits) {
789
                set.add(split.trim());
790
            }
791
        }
792
793
        return set;
794
    }
795
796
    /**
797
     * Checks if a given origin is valid or not. Criteria:
798
     * <ul>
799
     * <li>If an encoded character is present in origin, it's not valid.</li>
800
     * <li>Origin should be a valid {@link URI}</li>
801
     * </ul>
802
     *
803
     * @param origin
804
     * @see <a href="http://tools.ietf.org/html/rfc952">RFC952</a>
805
     * @return true, if origin is a valid URI; false, if either it contains CRLF
806
     *         characters, or is not a valid URI or is missing scheme.
807
     */
808
    public static boolean isValidOrigin(String origin) {
809
        // Checks for encoded characters. Helps prevent CRLF injection.
810
        if (origin.contains("%")) {
811
            return false;
812
        }
813
814
        URI originURI;
815
816
        try {
817
            originURI = new URI(origin);
818
        } catch (URISyntaxException e) {
819
            return false;
820
        }
821
        // If scheme for URI is null, return false. Return true otherwise.
822
        return originURI.getScheme() != null;
823
824
    }
825
826
    // -------------------------------------------------------------- Accessors
827
    /**
828
     * Determines if logging is enabled or not.
829
     *
830
     * @return <code>true</code> if it's enabled; false otherwise.
831
     */
832
    public boolean isLoggingEnabled() {
833
        return loggingEnabled;
834
    }
835
836
    /**
837
     * Determines if any origin is allowed to make CORS request.
838
     *
839
     * @return <code>true</code> if it's enabled; false otherwise.
840
     */
841
    public boolean isAnyOriginAllowed() {
842
        return anyOriginAllowed;
843
    }
844
845
    /**
846
     * Returns a {@link Collection} of headers that should be exposed by
847
     * browser.
848
     *
849
     * @return A {@link Collection} of {@link String}.
850
     */
851
    public Collection<String> getExposedHeaders() {
852
        return exposedHeaders;
853
    }
854
855
    /**
856
     * Determines is supports credentials is enabled
857
     *
858
     * @return <code>true</code> if supported, <code>false</code> otherwise.
859
     */
860
    public boolean isSupportsCredentials() {
861
        return supportsCredentials;
862
    }
863
864
    /**
865
     * Returns the preflight response cache time in seconds.
866
     *
867
     * @return Time to cache in seconds.
868
     */
869
    public long getPreflightMaxAge() {
870
        return preflightMaxAge;
871
    }
872
873
    /**
874
     * Returns the {@link Set} of allowed origins that are allowed to make
875
     * requests.
876
     *
877
     * @return {@link Set} of {@link String}
878
     */
879
    public Collection<String> getAllowedOrigins() {
880
        return allowedOrigins;
881
    }
882
883
    /**
884
     * Returns a {@link Set} of HTTP methods that are allowed to make requests.
885
     *
886
     * @return {@link Set} of {@link String}
887
     */
888
    public Collection<String> getAllowedHttpMethods() {
889
        return allowedHttpMethods;
890
    }
891
892
    /**
893
     * Returns a {@link Set} of headers support by resource.
894
     *
895
     * @return {@link Set} of {@link String}
896
     */
897
    public Collection<String> getAllowedHttpHeaders() {
898
        return allowedHttpHeaders;
899
    }
900
901
    // -------------------------------------------------- CORS Response Headers
902
    /**
903
     * The Access-Control-Allow-Origin header indicates whether a resource can
904
     * be shared based by returning the value of the Origin request header in
905
     * the response.
906
     */
907
    public static final String RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
908
909
    /**
910
     * The Access-Control-Allow-Credentials header indicates whether the
911
     * response to request can be exposed when the omit credentials flag is
912
     * unset. When part of the response to a preflight request it indicates that
913
     * the actual request can include user credentials.
914
     */
915
    public static final String RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
916
917
    /**
918
     * The Access-Control-Expose-Headers header indicates which headers are safe
919
     * to expose to the API of a CORS API specification
920
     */
921
    public static final String RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers";
922
923
    /**
924
     * The Access-Control-Max-Age header indicates how long the results of a
925
     * preflight request can be cached in a preflight result cache.
926
     */
927
    public static final String RESPONSE_HEADER_ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age";
928
929
    /**
930
     * The Access-Control-Allow-Methods header indicates, as part of the
931
     * response to a preflight request, which methods can be used during the
932
     * actual request.
933
     */
934
    public static final String RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
935
936
    /**
937
     * The Access-Control-Allow-Headers header indicates, as part of the
938
     * response to a preflight request, which header field names can be used
939
     * during the actual request.
940
     */
941
    public static final String RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
942
943
    // -------------------------------------------------- CORS Request Headers
944
    /**
945
     * The Origin header indicates where the cross-origin request or preflight
946
     * request originates from.
947
     */
948
    public static final String REQUEST_HEADER_ORIGIN = "Origin";
949
950
    /**
951
     * The Access-Control-Request-Method header indicates which method will be
952
     * used in the actual request as part of the preflight request.
953
     */
954
    public static final String REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method";
955
956
    /**
957
     * The Access-Control-Request-Headers header indicates which headers will be
958
     * used in the actual request as part of the preflight request.
959
     */
960
    public static final String REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
961
962
    // ----------------------------------------------------- Request attributes
963
    /**
964
     * The prefix to a CORS request attribute.
965
     */
966
    public static final String HTTP_REQUEST_ATTRIBUTE_PREFIX = "cors.";
967
968
    /**
969
     * Attribute that contains the origin of the request.
970
     */
971
    public static final String HTTP_REQUEST_ATTRIBUTE_ORIGIN = HTTP_REQUEST_ATTRIBUTE_PREFIX
972
            + "request.origin";
973
974
    /**
975
     * Boolean value, suggesting if the request is a CORS request or not.
976
     */
977
    public static final String HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST = HTTP_REQUEST_ATTRIBUTE_PREFIX
978
            + "isCorsRequest";
979
980
    /**
981
     * Type of CORS request, of type {@link CORSRequestType}.
982
     */
983
    public static final String HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE = HTTP_REQUEST_ATTRIBUTE_PREFIX
984
            + "request.type";
985
986
    /**
987
     * Request headers sent as 'Access-Control-Request-Headers' header, for
988
     * pre-flight request.
989
     */
990
    public static final String HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS = HTTP_REQUEST_ATTRIBUTE_PREFIX
991
            + "request.headers";
992
993
    // -------------------------------------------------------------- Constants
994
    /**
995
     * Enumerates varies types of CORS requests. Also, provides utility methods
996
     * to determine the request type.
997
     */
998
    public static enum CORSRequestType {
999
        /**
1000
         * A simple HTTP request, i.e. it shouldn't be pre-flighted.
1001
         */
1002
        SIMPLE,
1003
        /**
1004
         * A HTTP request that needs to be pre-flighted.
1005
         */
1006
        ACTUAL,
1007
        /**
1008
         * A pre-flight CORS request, to get meta information, before a
1009
         * non-simple HTTP request is sent.
1010
         */
1011
        PRE_FLIGHT,
1012
        /**
1013
         * Not a CORS request, but a normal request.
1014
         */
1015
        NOT_CORS,
1016
        /**
1017
         * An invalid CORS request, i.e. it qualifies to be a CORS request, but
1018
         * fails to be a valid one.
1019
         */
1020
        INVALID_CORS
1021
    }
1022
1023
    /**
1024
     * {@link Collection} of HTTP methods. Case sensitive.
1025
     *
1026
     * @see <a
1027
     *      href="http://tools.ietf.org/html/rfc2616#section-5.1.1">RFC2616</a>
1028
     */
1029
    public static final Collection<String> HTTP_METHODS = new HashSet<String>(
1030
            Arrays.asList("OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE",
1031
                    "TRACE", "CONNECT"));
1032
    /**
1033
     * {@link Collection} of non-simple HTTP methods. Case sensitive.
1034
     */
1035
    public static final Collection<String> COMPLEX_HTTP_METHODS = new HashSet<String>(
1036
            Arrays.asList("PUT", "DELETE", "TRACE", "CONNECT"));
1037
    /**
1038
     * {@link Collection} of Simple HTTP methods. Case sensitive.
1039
     *
1040
     * @see <a href="http://www.w3.org/TR/cors/#terminology">Terminology</a>
1041
     */
1042
    public static final Collection<String> SIMPLE_HTTP_METHODS = new HashSet<String>(
1043
            Arrays.asList("GET", "POST", "HEAD"));
1044
1045
    /**
1046
     * {@link Collection} of Simple HTTP request headers. Case in-sensitive.
1047
     *
1048
     * @see <a href="http://www.w3.org/TR/cors/#terminology">Terminology</a>
1049
     */
1050
    public static final Collection<String> SIMPLE_HTTP_REQUEST_HEADERS = new HashSet<String>(
1051
            Arrays.asList("Accept", "Accept-Language", "Content-Language"));
1052
1053
    /**
1054
     * {@link Collection} of Simple HTTP request headers. Case in-sensitive.
1055
     *
1056
     * @see <a href="http://www.w3.org/TR/cors/#terminology">Terminology</a>
1057
     */
1058
    public static final Collection<String> SIMPLE_HTTP_RESPONSE_HEADERS = new HashSet<String>(
1059
            Arrays.asList("Cache-Control", "Content-Language", "Content-Type",
1060
                    "Expires", "Last-Modified", "Pragma"));
1061
1062
    /**
1063
     * {@link Collection} of Simple HTTP request headers. Case in-sensitive.
1064
     *
1065
     * @see <a href="http://www.w3.org/TR/cors/#terminology">Terminology</a>
1066
     */
1067
    public static final Collection<String> SIMPLE_HTTP_REQUEST_CONTENT_TYPE_VALUES = new HashSet<String>(
1068
            Arrays.asList("application/x-www-form-urlencoded",
1069
                    "multipart/form-data", "text/plain"));
1070
1071
    // ------------------------------------------------ Configuration Defaults
1072
    /**
1073
     * By default, all origins are allowed to make requests.
1074
     */
1075
    public static final String DEFAULT_ALLOWED_ORIGINS = "*";
1076
1077
    /**
1078
     * By default, following methods are supported: GET, POST, HEAD and OPTIONS.
1079
     */
1080
    public static final String DEFAULT_ALLOWED_HTTP_METHODS = "GET,POST,HEAD,OPTIONS";
1081
1082
    /**
1083
     * By default, time duration to cache pre-flight response is 30 mins.
1084
     */
1085
    public static final String DEFAULT_PREFLIGHT_MAXAGE = "1800";
1086
1087
    /**
1088
     * By default, support credentials is turned on.
1089
     */
1090
    public static final String DEFAULT_SUPPORTS_CREDENTIALS = "true";
1091
1092
    /**
1093
     * By default, following headers are supported:
1094
     * Origin,Accept,X-Requested-With, Content-Type,
1095
     * Access-Control-Request-Method, and Access-Control-Request-Headers.
1096
     */
1097
    public static final String DEFAULT_ALLOWED_HTTP_HEADERS = "Origin,Accept,X-Requested-With,Content-Type,"
1098
            + "Access-Control-Request-Method,Access-Control-Request-Headers";
1099
1100
    /**
1101
     * By default, none of the headers are exposed in response.
1102
     */
1103
    public static final String DEFAULT_EXPOSED_HEADERS = "";
1104
1105
    /**
1106
     * By default, access log logging is turned off
1107
     */
1108
    public static final String DEFAULT_LOGGING_ENABLED = "false";
1109
1110
    /**
1111
     * By default, request is decorated with CORS attributes.
1112
     */
1113
    public static final String DEFAULT_DECORATE_REQUEST = "true";
1114
1115
    // ----------------------------------------Filter Config Init param-name(s)
1116
    /**
1117
     * Key to retrieve allowed origins from {@link FilterConfig}.
1118
     */
1119
    public static final String PARAM_CORS_ALLOWED_ORIGINS = "cors.allowed.origins";
1120
1121
    /**
1122
     * Key to retrieve support credentials from {@link FilterConfig}.
1123
     */
1124
    public static final String PARAM_CORS_SUPPORT_CREDENTIALS = "cors.support.credentials";
1125
1126
    /**
1127
     * Key to retrieve exposed headers from {@link FilterConfig}.
1128
     */
1129
    public static final String PARAM_CORS_EXPOSED_HEADERS = "cors.exposed.headers";
1130
1131
    /**
1132
     * Key to retrieve allowed headers from {@link FilterConfig}.
1133
     */
1134
    public static final String PARAM_CORS_ALLOWED_HEADERS = "cors.allowed.headers";
1135
1136
    /**
1137
     * Key to retrieve allowed methods from {@link FilterConfig}.
1138
     */
1139
    public static final String PARAM_CORS_ALLOWED_METHODS = "cors.allowed.methods";
1140
1141
    /**
1142
     * Key to retrieve preflight max age from {@link FilterConfig}.
1143
     */
1144
    public static final String PARAM_CORS_PREFLIGHT_MAXAGE = "cors.preflight.maxage";
1145
1146
    /**
1147
     * Key to retrieve access log logging flag.
1148
     */
1149
    public static final String PARAM_CORS_LOGGING_ENABLED = "cors.logging.enabled";
1150
1151
    /**
1152
     * Key to determine if request should be decorated.
1153
     */
1154
    public static final String PARAM_CORS_REQUEST_DECORATE = "cors.request.decorate";
1155
}
0
  + native
1156
  + native
(-)webapps/docs/config/filter.xml (+112 lines)
Lines 1309-1314 Link Here
1309
1309
1310
</section>
1310
</section>
1311
1311
1312
<section name="CORS Filter">
1313
  <subsection name="Introduction">
1314
    <p>This filter is an implementation of W3C's CORS (Cross-Origin Resource Sharing) <a href="http://www.w3.org/TR/cors/">specification</a>, which is a mechanism that enables cross-origin requests.</p>
1315
    <p>The filter works by adding required <code>Access-Control-*</code> headers to HttpServletResponse object. The filter also protects against HTTP response splitting. If request is invalid, or is not permitted, then request is rejected with HTTP status code 403 (Forbidden). A flowchart that demonstrate request processing by this filter is available <a href="../images/cors-flowchart.png">here</a>.</p>
1316
    <p>The minimal configuration required to use this filter is:</p>
1317
    <source>
1318
&lt;filter&gt;
1319
  &lt;filter-name&gt;CORSFilter&lt;/filter-name&gt;
1320
  &lt;filter-class&gt;org.apache.catalina.filters.CORSFilter&lt;/filter-class&gt;
1321
&lt;/filter&gt;
1322
&lt;filter-mapping&gt;
1323
  &lt;filter-name>CORSFilter&lt;/filter-name&gt;
1324
  &lt;url-pattern>/*&lt;/url-pattern&gt;
1325
&lt;/filter-mapping&gt;
1326
    </source>
1327
  </subsection>
1328
  <subsection name="Filter Class Name">
1329
    <p>The filter class name for the CORS Filter is <strong><code>org.apache.catalina.filters.CORSFilter</code></strong>.</p>
1330
  </subsection>
1331
  <subsection name="Initialisation parameters">
1332
    <p>The CORS Filter supports following initialisation parameters:</p>
1333
    <attributes>
1334
      <attribute name="cors.allowed.origins" required="false">
1335
        <p>A list of <a href="http://tools.ietf.org/html/rfc6454">origins</a> that are allowed to access the resource. A <code>'*'</code> can be specified to enable access to resource from any origin. Otherwise, a whitelist of comma separated origins can be provided. Ex: http://www.w3.org, https://www.apache.org. <strong>Defaults:</strong> <code>*</code> (Any origin is allowed to access the resource).</p>
1336
      </attribute>
1337
      <attribute name="cors.allowed.methods" required="false">
1338
        <p>A comma separated list of HTTP methods that can be used to access the resource, using cross-origin requests. These are the methods which will also be included as part of 'Access-Control-Allow-Methods' header in a pre-flight response. Ex: <code>GET,POST</code>. <strong>Defaults:</strong> <code>GET,POST,HEAD,OPTIONS</code></p>
1339
      </attribute>
1340
      <attribute name="cors.allowed.headers" required="false">
1341
        <p>A comma separated list of request headers that can be used when making an actual request. These header will also be returned as part of <code>'Access-Control-Allow-Headers'</code> header in a pre-flight response. Ex: <code>Origin,Accept</code>. <strong>Defaults:</strong> <code>Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers</code></p>
1342
      </attribute>
1343
      <attribute name="cors.exposed.headers" required="false">
1344
        <p>A comma separated list of headers other than the simple response headers that browsers are allowed to access. These are the headers which will also be included as part of 'Access-Control-Expose-Headers' header in the pre-flight response. Ex: <code>X-CUSTOM-HEADER-PING,X-CUSTOM-HEADER-PONG</code>. <strong>Default:</strong> None. Non-simple headers are not exposed by default.</p>
1345
      </attribute>
1346
      <attribute name="cors.preflight.maxage" required="false">
1347
        <p>The amount of seconds, browser is allowed to cache the result of the pre-flight request. This will be included as part of <code>'Access-Control-Max-Age'</code> header in the pre-flight response. A negative value will prevent CORS Filter from adding this response header from pre-flight response. <strong>Defaults:</strong> <code>1800</code></p>
1348
      </attribute>
1349
      <attribute name="cors.support.credentials" required="false">
1350
        <p>A flag that indicates whether the resource supports user credentials. This flag is exposed as part of <code>'Access-Control-Allow-Credentials'</code> header in a pre-flight response. It helps browser determine whether or not an actual request can be made using credentials. <strong>Defaults:</strong> <code>true</code></p>
1351
      </attribute>
1352
      <attribute name="cors.logging.enabled" required="false">
1353
        <p>A flag to control logging to container logs. <strong>Defaults:</strong> <code>false</code></p>
1354
      </attribute>
1355
      <attribute name="cors.request.decorate" required="false">
1356
        <p>A flag to control if CORS specific attributes should be added to HttpServletRequest object or not. <strong>Defaults:</strong> <code>true</code></p>
1357
      </attribute>
1358
    </attributes>
1359
    <p>Here's an example of a more advanced configuration, that overrides defaults:</p>
1360
    <source>
1361
&lt;filter&gt;
1362
  &lt;filter-name&gt;CORSFilter&lt;/filter-name&gt;
1363
  &lt;filter-class&gt;org.apache.catalina.filters.CORSFilter&lt;/filter-class&gt;
1364
  &lt;init-param&gt;
1365
    &lt;description&gt;A comma separated list of allowed origins&lt;/description&gt;
1366
    &lt;param-name&gt;cors.allowed.origins&lt;/param-name&gt;
1367
    &lt;param-value&gt;*&lt;/param-value&gt;
1368
  &lt;/init-param&gt;
1369
  &lt;init-param&gt;
1370
    &lt;description&gt;A comma separated list of HTTP verbs, using which a cross-origin request can be made.&lt;/description&gt;
1371
    &lt;param-name&gt;cors.allowed.methods&lt;/param-name&gt;
1372
    &lt;param-value&gt;GET,POST,HEAD,OPTIONS,PUT&lt;/param-value&gt;
1373
  &lt;/init-param&gt;
1374
  &lt;init-param&gt;
1375
    &lt;description&gt;A comma separated list of allowed headers when making a non simple cross-origin request.&lt;/description&gt;
1376
    &lt;param-name&gt;cors.allowed.headers&lt;/param-name&gt;
1377
    &lt;param-value&gt;Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers&lt;/param-value&gt;
1378
  &lt;/init-param&gt;
1379
  &lt;init-param&gt;
1380
    &lt;description&gt;A comma separated list of non-standard response headers that will be exposed to XMLHttpRequest object.&lt;/description&gt;
1381
    &lt;param-name&gt;cors.exposed.headers&lt;/param-name&gt;
1382
    &lt;param-value&gt;Access-Control-Allow-Origin,Access-Control-Allow-Credentials&lt;/param-value&gt;
1383
  &lt;/init-param&gt;
1384
  &lt;init-param&gt;
1385
    &lt;description&gt;A flag that suggests if a cross-origin request is supported with cookies, BASIC Authentication, etc.&lt;/description&gt;
1386
    &lt;param-name&gt;cors.support.credentials&lt;/param-name&gt;
1387
    &lt;param-value&gt;true&lt;/param-value&gt;
1388
  &lt;/init-param&gt;
1389
  &lt;init-param&gt;
1390
    &lt;description&gt;A flag control logging&lt;/description&gt;
1391
    &lt;param-name&gt;cors.logging.enabled&lt;/param-name&gt;
1392
    &lt;param-value&gt;true&lt;/param-value&gt;
1393
  &lt;/init-param&gt;
1394
  &lt;init-param&gt;
1395
    &lt;description&gt;Indicates how long (in seconds) the results of a preflight request can be cached in a preflight result cache.&lt;/description&gt;
1396
    &lt;param-name&gt;cors.preflight.maxage&lt;/param-name&gt;
1397
    &lt;param-value&gt;10&lt;/param-value&gt;
1398
  &lt;/init-param&gt;
1399
&lt;/filter&gt;
1400
&lt;filter-mapping&gt;
1401
  &lt;filter-name&gt;CORS Filter&lt;/filter-name&gt;
1402
  &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
1403
&lt;/filter-mapping&gt;
1404
    </source>
1405
  </subsection>
1406
  <subsection name="CORS Filter and HttpServletRequest attributes" required="false">
1407
    <p>CORS Filter adds information about a request, in the HttpServletRequest object, for consumption downstream. Following attributes are set, if <code>cors.request.decorate</code> initialisation parameter is <code>true</code>:</p>
1408
    <ul>
1409
      <li><strong>cors.isCorsRequest:</strong> Flag to determine if a request is a CORS request.</li>
1410
      <li><strong>cors.request.origin:</strong> The Origin URL, i.e. the URL of the page from where the request is originated.</li>
1411
      <li><strong>cors.request.type:</strong> Type of CORS request. Possible values:
1412
        <ul>
1413
          <li><code>SIMPLE</code>: A request which is not preceded by a pre-flight request.</li>
1414
          <li><code>ACTUAL</code>: A request which is preceded by a pre-flight request.</li>
1415
          <li><code>PRE_FLIGHT</code>: A pre-flight request.</li>
1416
          <li><code>NOT_CORS</code>: A normal same-origin request.</li>
1417
          <li><code>INVALID_CORS</code>: A cross-origin request, which is invalid.</li>
1418
        </ul>
1419
      </li>
1420
      <li><strong>cors.request.headers:</strong> Request headers sent as 'Access-Control-Request-Headers' header, for a pre-flight request.</li>
1421
    </ul>
1422
  </subsection>
1423
</section>
1312
1424
1313
</body>
1425
</body>
1314
1426

Return to bug 55046