ASF Bugzilla – Attachment 30384 Details for
Bug 55046
CORS Filter
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Revised patch to address several comments.
CORSFilterV2.patch (text/plain), 161.94 KB, created by
Mohit Soni
on 2013-06-04 05:41:12 UTC
(
hide
)
Description:
Revised patch to address several comments.
Filename:
MIME Type:
Creator:
Mohit Soni
Created:
2013-06-04 05:41:12 UTC
Size:
161.94 KB
patch
obsolete
>Index: test/org/apache/catalina/filters/TesterFilterChain.java >=================================================================== >--- test/org/apache/catalina/filters/TesterFilterChain.java (revision 0) >+++ test/org/apache/catalina/filters/TesterFilterChain.java (revision 0) >@@ -0,0 +1,36 @@ >+/** >+ * Copyright 2012-2013 eBay Software Foundation, All Rights Reserved. >+ * >+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not >+ * use this file except in compliance with the License. You may obtain a copy of >+ * the License at >+ * >+ * http://www.apache.org/licenses/LICENSE-2.0 >+ * >+ * Unless required by applicable law or agreed to in writing, software >+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT >+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the >+ * License for the specific language governing permissions and limitations under >+ * the License. >+ */ >+package org.apache.catalina.filters; >+ >+import java.io.IOException; >+ >+import javax.servlet.FilterChain; >+import javax.servlet.ServletException; >+import javax.servlet.ServletRequest; >+import javax.servlet.ServletResponse; >+ >+/** >+ * A mock {@link FilterChain}. >+ */ >+public class TesterFilterChain implements FilterChain { >+ >+ @Override >+ public void doFilter(ServletRequest request, ServletResponse response) >+ throws IOException, ServletException { >+ // NoOp >+ } >+ >+} >\ No newline at end of file > >Property changes on: test/org/apache/catalina/filters/TesterFilterChain.java >___________________________________________________________________ >Added: svn:eol-style > + native > >Index: test/org/apache/catalina/filters/TesterServletContext.java >=================================================================== >--- test/org/apache/catalina/filters/TesterServletContext.java (revision 0) >+++ test/org/apache/catalina/filters/TesterServletContext.java (revision 0) >@@ -0,0 +1,316 @@ >+/** >+ * Copyright 2012-2013 eBay Software Foundation, All Rights Reserved. >+ * >+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not >+ * use this file except in compliance with the License. You may obtain a copy of >+ * the License at >+ * >+ * http://www.apache.org/licenses/LICENSE-2.0 >+ * >+ * Unless required by applicable law or agreed to in writing, software >+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT >+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the >+ * License for the specific language governing permissions and limitations under >+ * the License. >+ */ >+package org.apache.catalina.filters; >+ >+import java.io.InputStream; >+import java.net.MalformedURLException; >+import java.net.URL; >+import java.util.Enumeration; >+import java.util.EventListener; >+import java.util.Map; >+import java.util.Set; >+ >+import javax.servlet.Filter; >+import javax.servlet.FilterRegistration; >+import javax.servlet.RequestDispatcher; >+import javax.servlet.Servlet; >+import javax.servlet.ServletContext; >+import javax.servlet.ServletException; >+import javax.servlet.ServletRegistration; >+import javax.servlet.ServletRegistration.Dynamic; >+import javax.servlet.SessionCookieConfig; >+import javax.servlet.SessionTrackingMode; >+import javax.servlet.descriptor.JspConfigDescriptor; >+ >+/** >+ * A Mock {@link ServletContext}. >+ * >+ * @author mosoni >+ * >+ */ >+public class TesterServletContext implements ServletContext { >+ >+ @Override >+ public String getContextPath() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public ServletContext getContext(String uripath) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public int getMajorVersion() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public int getMinorVersion() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getMimeType(String file) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Set<String> getResourcePaths(String path) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public URL getResource(String path) throws MalformedURLException { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public InputStream getResourceAsStream(String path) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public RequestDispatcher getRequestDispatcher(String path) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public RequestDispatcher getNamedDispatcher(String name) { >+ >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Servlet getServlet(String name) throws ServletException { >+ >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Enumeration<Servlet> getServlets() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Enumeration<String> getServletNames() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void log(String msg) { >+ // NOOP >+ } >+ >+ @Override >+ public void log(Exception exception, String msg) { >+ // NOOP >+ } >+ >+ @Override >+ public void log(String message, Throwable throwable) { >+ // NOOP >+ } >+ >+ @Override >+ public String getRealPath(String path) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getServerInfo() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getInitParameter(String name) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Enumeration<String> getInitParameterNames() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Object getAttribute(String name) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Enumeration<String> getAttributeNames() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void setAttribute(String name, Object object) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void removeAttribute(String name) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getServletContextName() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public int getEffectiveMajorVersion() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public int getEffectiveMinorVersion() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public boolean setInitParameter(String name, String value) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Dynamic addServlet(String servletName, String className) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Dynamic addServlet(String servletName, Servlet servlet) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Dynamic addServlet(String servletName, >+ Class<? extends Servlet> servletClass) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public <T extends Servlet> T createServlet(Class<T> c) >+ throws ServletException { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public ServletRegistration getServletRegistration(String servletName) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Map<String, ? extends ServletRegistration> getServletRegistrations() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public javax.servlet.FilterRegistration.Dynamic addFilter( >+ String filterName, String className) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public javax.servlet.FilterRegistration.Dynamic addFilter( >+ String filterName, Filter filter) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public javax.servlet.FilterRegistration.Dynamic addFilter( >+ String filterName, Class<? extends Filter> filterClass) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public <T extends Filter> T createFilter(Class<T> c) >+ throws ServletException { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public FilterRegistration getFilterRegistration(String filterName) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Map<String, ? extends FilterRegistration> getFilterRegistrations() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public SessionCookieConfig getSessionCookieConfig() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void setSessionTrackingModes( >+ Set<SessionTrackingMode> sessionTrackingModes) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Set<SessionTrackingMode> getDefaultSessionTrackingModes() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void addListener(String className) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public <T extends EventListener> void addListener(T t) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void addListener(Class<? extends EventListener> listenerClass) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public <T extends EventListener> T createListener(Class<T> c) >+ throws ServletException { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public JspConfigDescriptor getJspConfigDescriptor() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public ClassLoader getClassLoader() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void declareRoles(String... roleNames) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getVirtualServerName() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+} >\ No newline at end of file > >Property changes on: test/org/apache/catalina/filters/TesterServletContext.java >___________________________________________________________________ >Added: svn:eol-style > + native > >Index: test/org/apache/catalina/filters/TesterHttpServletRequest.java >=================================================================== >--- test/org/apache/catalina/filters/TesterHttpServletRequest.java (revision 0) >+++ test/org/apache/catalina/filters/TesterHttpServletRequest.java (revision 0) >@@ -0,0 +1,444 @@ >+/** >+ * Copyright 2012-2013 eBay Software Foundation, All Rights Reserved. >+ * >+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not >+ * use this file except in compliance with the License. You may obtain a copy of >+ * the License at >+ * >+ * http://www.apache.org/licenses/LICENSE-2.0 >+ * >+ * Unless required by applicable law or agreed to in writing, software >+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT >+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the >+ * License for the specific language governing permissions and limitations under >+ * the License. >+ */ >+package org.apache.catalina.filters; >+ >+import java.io.BufferedReader; >+import java.io.IOException; >+import java.io.UnsupportedEncodingException; >+import java.security.Principal; >+import java.util.ArrayList; >+import java.util.Collection; >+import java.util.Collections; >+import java.util.Enumeration; >+import java.util.HashMap; >+import java.util.HashSet; >+import java.util.List; >+import java.util.Locale; >+import java.util.Map; >+ >+import javax.servlet.AsyncContext; >+import javax.servlet.DispatcherType; >+import javax.servlet.RequestDispatcher; >+import javax.servlet.ServletContext; >+import javax.servlet.ServletException; >+import javax.servlet.ServletInputStream; >+import javax.servlet.ServletRequest; >+import javax.servlet.ServletResponse; >+import javax.servlet.http.Cookie; >+import javax.servlet.http.HttpServletRequest; >+import javax.servlet.http.HttpServletResponse; >+import javax.servlet.http.HttpSession; >+import javax.servlet.http.HttpUpgradeHandler; >+import javax.servlet.http.Part; >+ >+/** >+ * A mock {@link HttpServletRequest}. >+ */ >+public class TesterHttpServletRequest implements HttpServletRequest { >+ >+ private final Map<String, Object> attributes = new HashMap<String, Object>(); >+ private final Map<String, List<String>> headers = new HashMap<String, List<String>>(); >+ private String method; >+ private String contentType; >+ >+ @Override >+ public Object getAttribute(String name) { >+ return attributes.get(name); >+ } >+ >+ @Override >+ public Enumeration<String> getAttributeNames() { >+ return Collections.enumeration(attributes.keySet()); >+ } >+ >+ @Override >+ public String getCharacterEncoding() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void setCharacterEncoding(String env) >+ throws UnsupportedEncodingException { >+ // NO-OP. >+ } >+ >+ @Override >+ public int getContentLength() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getContentType() { >+ return this.contentType; >+ } >+ >+ /** >+ * Method to set content type for test. >+ * >+ * @param contentType >+ * The type of content. >+ */ >+ public void setContentType(String contentType) { >+ this.contentType = contentType; >+ } >+ >+ @Override >+ public ServletInputStream getInputStream() throws IOException { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getParameter(String name) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Enumeration<String> getParameterNames() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String[] getParameterValues(String name) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Map<String, String[]> getParameterMap() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getProtocol() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getScheme() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getServerName() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public int getServerPort() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public BufferedReader getReader() throws IOException { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getRemoteAddr() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getRemoteHost() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void setAttribute(String name, Object o) { >+ attributes.put(name, o); >+ } >+ >+ @Override >+ public void removeAttribute(String name) { >+ attributes.remove(name); >+ } >+ >+ @Override >+ public Locale getLocale() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Enumeration<Locale> getLocales() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public boolean isSecure() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public RequestDispatcher getRequestDispatcher(String path) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getRealPath(String path) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public int getRemotePort() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getLocalName() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getLocalAddr() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public int getLocalPort() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getAuthType() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Cookie[] getCookies() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public long getDateHeader(String name) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getHeader(String name) { >+ List<String> list = headers.get(name); >+ if (list != null) { >+ return CORSFilter.join(new HashSet<String>(list), ","); >+ } >+ return null; >+ } >+ >+ /** >+ * Method to set header name and value for test. >+ * >+ * @param name >+ * Name of header. >+ * @param value >+ * Value of header. >+ */ >+ public void setHeader(String name, String value) { >+ List<String> values = new ArrayList<String>(); >+ values.add(value); >+ headers.put(name, values); >+ } >+ >+ @Override >+ public Enumeration<String> getHeaders(String name) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Enumeration<String> getHeaderNames() { >+ return Collections.enumeration(headers.keySet()); >+ } >+ >+ @Override >+ public int getIntHeader(String name) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getMethod() { >+ return method; >+ } >+ >+ /** >+ * Method to set HTTP method type, for test. >+ * >+ * @param method >+ * The type of HTTP method. >+ */ >+ public void setMethod(String method) { >+ this.method = method; >+ } >+ >+ @Override >+ public String getPathInfo() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getPathTranslated() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getContextPath() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getQueryString() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getRemoteUser() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public boolean isUserInRole(String role) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Principal getUserPrincipal() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getRequestedSessionId() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getRequestURI() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public StringBuffer getRequestURL() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getServletPath() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public HttpSession getSession(boolean create) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public HttpSession getSession() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public boolean isRequestedSessionIdValid() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public boolean isRequestedSessionIdFromCookie() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public boolean isRequestedSessionIdFromURL() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public boolean isRequestedSessionIdFromUrl() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public long getContentLengthLong() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public ServletContext getServletContext() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public AsyncContext startAsync() throws IllegalStateException { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public AsyncContext startAsync(ServletRequest servletRequest, >+ ServletResponse servletResponse) throws IllegalStateException { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public boolean isAsyncStarted() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public boolean isAsyncSupported() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public AsyncContext getAsyncContext() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public DispatcherType getDispatcherType() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String changeSessionId() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public boolean authenticate(HttpServletResponse response) >+ throws IOException, ServletException { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void login(String username, String password) throws ServletException { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void logout() throws ServletException { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Collection<Part> getParts() throws IOException, ServletException { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Part getPart(String name) throws IOException, ServletException { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public <T extends HttpUpgradeHandler> T upgrade( >+ Class<T> httpUpgradeHandlerClass) throws IOException { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+} >\ No newline at end of file > >Property changes on: test/org/apache/catalina/filters/TesterHttpServletRequest.java >___________________________________________________________________ >Added: svn:eol-style > + native > >Index: test/org/apache/catalina/filters/TesterFilterConfigs.java >=================================================================== >--- test/org/apache/catalina/filters/TesterFilterConfigs.java (revision 0) >+++ test/org/apache/catalina/filters/TesterFilterConfigs.java (revision 0) >@@ -0,0 +1,355 @@ >+/** >+ * Copyright 2012-2013 eBay Software Foundation, All Rights Reserved. >+ * >+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not >+ * use this file except in compliance with the License. You may obtain a copy of >+ * the License at >+ * >+ * http://www.apache.org/licenses/LICENSE-2.0 >+ * >+ * Unless required by applicable law or agreed to in writing, software >+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT >+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the >+ * License for the specific language governing permissions and limitations under >+ * the License. >+ */ >+package org.apache.catalina.filters; >+ >+import java.util.Enumeration; >+ >+import javax.servlet.FilterConfig; >+import javax.servlet.ServletContext; >+ >+/** >+ * A collection of mock {@link FilterConfig}. >+ */ >+public class TesterFilterConfigs { >+ /** >+ * A HTTPS origin. >+ */ >+ public static final String HTTPS_WWW_APACHE_ORG = "https://www.apache.org"; >+ /** >+ * A HTTP origin. >+ */ >+ public static final String HTTP_TOMCAT_APACHE_ORG = "http://tomcat.apache.org"; >+ /** >+ * A sample header to test exposed headers. >+ */ >+ public static final String EXPOSED_HEADERS = "X-CUSTOM-HEADER"; >+ /** >+ * Any origin >+ */ >+ public static final String ANY_ORIGIN = "*"; >+ >+ /** >+ * A mock {@link ServletContext} object. >+ */ >+ public static final TesterServletContext mockServletContext = new TesterServletContext(); >+ >+ /** >+ * Returns a {@link FilterConfig} object, with default configuration. >+ * >+ * @return A {@link FilterConfig} object. >+ */ >+ public static FilterConfig getDefaultFilterConfig() { >+ final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS; >+ final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS; >+ final String allowedOrigins = CORSFilter.DEFAULT_ALLOWED_ORIGINS; >+ final String exposedHeaders = CORSFilter.DEFAULT_EXPOSED_HEADERS; >+ final String supportCredentials = CORSFilter.DEFAULT_SUPPORTS_CREDENTIALS; >+ final String preflightMaxAge = CORSFilter.DEFAULT_PREFLIGHT_MAXAGE; >+ final String loggingEnabled = CORSFilter.DEFAULT_LOGGING_ENABLED; >+ final String decorateRequest = CORSFilter.DEFAULT_DECORATE_REQUEST; >+ >+ return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods, >+ allowedOrigins, exposedHeaders, supportCredentials, >+ preflightMaxAge, loggingEnabled, decorateRequest); >+ } >+ >+ /** >+ * A variation of default {@link FilterConfig}, with support of PUT method >+ * and credentials. >+ * >+ * @return A {@link FilterConfig} object. >+ */ >+ public static FilterConfig getFilterConfigAnyOriginAndSupportsCredentials() { >+ final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS; >+ final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS >+ + ",PUT"; >+ final String allowedOrigins = CORSFilter.DEFAULT_ALLOWED_ORIGINS; >+ final String exposedHeaders = CORSFilter.DEFAULT_EXPOSED_HEADERS; >+ final String supportCredentials = "true"; >+ final String preflightMaxAge = CORSFilter.DEFAULT_PREFLIGHT_MAXAGE; >+ final String loggingEnabled = CORSFilter.DEFAULT_LOGGING_ENABLED; >+ final String decorateRequest = CORSFilter.DEFAULT_DECORATE_REQUEST; >+ >+ return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods, >+ allowedOrigins, exposedHeaders, supportCredentials, >+ preflightMaxAge, loggingEnabled, decorateRequest); >+ } >+ >+ /** >+ * A variation of default {@link FilterConfig}, with support of PUT method >+ * and credentials disabled.. >+ * >+ * @return A {@link FilterConfig} object. >+ */ >+ public static FilterConfig getFilterConfigAnyOriginAndSupportsCredentialsDisabled() { >+ final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS; >+ final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS >+ + ",PUT"; >+ final String allowedOrigins = CORSFilter.DEFAULT_ALLOWED_ORIGINS; >+ final String exposedHeaders = CORSFilter.DEFAULT_EXPOSED_HEADERS; >+ final String supportCredentials = "false"; >+ final String preflightMaxAge = CORSFilter.DEFAULT_PREFLIGHT_MAXAGE; >+ final String loggingEnabled = CORSFilter.DEFAULT_LOGGING_ENABLED; >+ final String decorateRequest = CORSFilter.DEFAULT_DECORATE_REQUEST; >+ >+ return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods, >+ allowedOrigins, exposedHeaders, supportCredentials, >+ preflightMaxAge, loggingEnabled, decorateRequest); >+ } >+ >+ /** >+ * A variation of default {@link FilterConfig}, with support of PUT method >+ * and any origin disabled, that is configured using specific origins. >+ * >+ * @return A {@link FilterConfig} object. >+ */ >+ public static FilterConfig getFilterConfigSpecificOriginAndSupportsCredentialsDisabled() { >+ final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS; >+ final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS >+ + ",PUT"; >+ final String allowedOrigins = HTTP_TOMCAT_APACHE_ORG + "," >+ + HTTPS_WWW_APACHE_ORG; >+ final String exposedHeaders = CORSFilter.DEFAULT_EXPOSED_HEADERS; >+ final String supportCredentials = "false"; >+ final String preflightMaxAge = CORSFilter.DEFAULT_PREFLIGHT_MAXAGE; >+ final String loggingEnabled = CORSFilter.DEFAULT_LOGGING_ENABLED; >+ final String decorateRequest = CORSFilter.DEFAULT_DECORATE_REQUEST; >+ >+ return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods, >+ allowedOrigins, exposedHeaders, supportCredentials, >+ preflightMaxAge, loggingEnabled, decorateRequest); >+ } >+ >+ /** >+ * A variation of default {@link FilterConfig}, with exposed headers. >+ * >+ * @return A {@link FilterConfig} object. >+ */ >+ public static FilterConfig getFilterConfigWithExposedHeaders() { >+ final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS; >+ final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS; >+ final String allowedOrigins = CORSFilter.DEFAULT_ALLOWED_ORIGINS; >+ final String exposedHeaders = EXPOSED_HEADERS; >+ final String supportCredentials = CORSFilter.DEFAULT_SUPPORTS_CREDENTIALS; >+ final String preflightMaxAge = CORSFilter.DEFAULT_PREFLIGHT_MAXAGE; >+ final String loggingEnabled = CORSFilter.DEFAULT_LOGGING_ENABLED; >+ final String decorateRequest = CORSFilter.DEFAULT_DECORATE_REQUEST; >+ >+ return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods, >+ allowedOrigins, exposedHeaders, supportCredentials, >+ preflightMaxAge, loggingEnabled, decorateRequest); >+ } >+ >+ /** >+ * A variation of default {@link FilterConfig}, with support of PUT method, >+ * a HTTPS origin >+ * >+ * @return A {@link FilterConfig} object. >+ */ >+ public static FilterConfig getSecureFilterConfig() { >+ final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS; >+ final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS >+ + ",PUT"; >+ final String allowedOrigins = HTTPS_WWW_APACHE_ORG; >+ final String exposedHeaders = CORSFilter.DEFAULT_EXPOSED_HEADERS; >+ final String supportCredentials = "true"; >+ final String preflightMaxAge = CORSFilter.DEFAULT_PREFLIGHT_MAXAGE; >+ final String loggingEnabled = "true"; >+ final String decorateRequest = CORSFilter.DEFAULT_DECORATE_REQUEST; >+ >+ return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods, >+ allowedOrigins, exposedHeaders, supportCredentials, >+ preflightMaxAge, loggingEnabled, decorateRequest); >+ } >+ >+ /** >+ * A {@link FilterConfig} with all <code>null</code> attributes, i.e. will >+ * use default config. >+ * >+ * @return A {@link FilterConfig} object. >+ */ >+ public static FilterConfig getNullFilterConfig() { >+ return generateFilterConfig(null, null, null, null, null, null, null, >+ null); >+ } >+ >+ /** >+ * A variation of default {@link FilterConfig}, with support of PUT method, >+ * a HTTPS origin and a HTTP origin. >+ * >+ * @return A {@link FilterConfig} object. >+ */ >+ public static FilterConfig getSpecificOriginFilterConfig() { >+ final String allowedOrigins = HTTPS_WWW_APACHE_ORG + "," >+ + HTTP_TOMCAT_APACHE_ORG; >+ >+ final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS; >+ final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS >+ + ",PUT"; >+ final String exposedHeaders = CORSFilter.DEFAULT_EXPOSED_HEADERS; >+ final String supportCredentials = CORSFilter.DEFAULT_SUPPORTS_CREDENTIALS; >+ final String preflightMaxAge = CORSFilter.DEFAULT_PREFLIGHT_MAXAGE; >+ final String loggingEnabled = CORSFilter.DEFAULT_LOGGING_ENABLED; >+ final String decorateRequest = CORSFilter.DEFAULT_DECORATE_REQUEST; >+ >+ return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods, >+ allowedOrigins, exposedHeaders, supportCredentials, >+ preflightMaxAge, loggingEnabled, decorateRequest); >+ } >+ >+ /** >+ * A variation of default {@link FilterConfig}, with support of PUT method, >+ * a HTTPS origin, a HTTP origin, and a negative max age header, which >+ * indicates browsers to not cache pre-flight response. >+ * >+ * @return A {@link FilterConfig} object. >+ */ >+ public static FilterConfig getSpecificOriginFilterConfigNegativeMaxAge() { >+ final String allowedOrigins = HTTPS_WWW_APACHE_ORG + "," >+ + HTTP_TOMCAT_APACHE_ORG; >+ >+ final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS; >+ final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS >+ + ",PUT"; >+ final String exposedHeaders = CORSFilter.DEFAULT_EXPOSED_HEADERS; >+ final String supportCredentials = CORSFilter.DEFAULT_SUPPORTS_CREDENTIALS; >+ final String preflightMaxAge = "-1"; >+ final String loggingEnabled = CORSFilter.DEFAULT_LOGGING_ENABLED; >+ final String decorateRequest = CORSFilter.DEFAULT_DECORATE_REQUEST; >+ >+ return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods, >+ allowedOrigins, exposedHeaders, supportCredentials, >+ preflightMaxAge, loggingEnabled, decorateRequest); >+ } >+ >+ /** >+ * A variation of default {@link FilterConfig}, with an invalid pre-flight >+ * max age value. It should be a integer. >+ * >+ * @return A {@link FilterConfig} object. >+ */ >+ public static FilterConfig getFilterConfigInvalidMaxPreflightAge() { >+ final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS; >+ final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS; >+ final String allowedOrigins = CORSFilter.DEFAULT_ALLOWED_ORIGINS; >+ final String exposedHeaders = CORSFilter.DEFAULT_EXPOSED_HEADERS; >+ final String supportCredentials = CORSFilter.DEFAULT_SUPPORTS_CREDENTIALS; >+ final String preflightMaxAge = "abc"; >+ final String loggingEnabled = CORSFilter.DEFAULT_LOGGING_ENABLED; >+ final String decorateRequest = CORSFilter.DEFAULT_DECORATE_REQUEST; >+ >+ return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods, >+ allowedOrigins, exposedHeaders, supportCredentials, >+ preflightMaxAge, loggingEnabled, decorateRequest); >+ } >+ >+ /** >+ * A {@link FilterConfig} with all properties as empty strings. >+ * >+ * @return A {@link FilterConfig} object. >+ */ >+ public static FilterConfig getEmptyFilterConfig() { >+ final String allowedHttpHeaders = ""; >+ final String allowedHttpMethods = ""; >+ final String allowedOrigins = ""; >+ final String exposedHeaders = ""; >+ final String supportCredentials = ""; >+ final String preflightMaxAge = ""; >+ final String loggingEnabled = ""; >+ final String decorateRequest = ""; >+ >+ return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods, >+ allowedOrigins, exposedHeaders, supportCredentials, >+ preflightMaxAge, loggingEnabled, decorateRequest); >+ } >+ >+ /** >+ * A variation of default {@link FilterConfig}, that disables adding >+ * attributes to HttpServletRequest. >+ * >+ * @return A {@link FilterConfig} object. >+ */ >+ public static FilterConfig getFilterConfigDecorateRequestDisabled() { >+ final String allowedHttpHeaders = CORSFilter.DEFAULT_ALLOWED_HTTP_HEADERS; >+ final String allowedHttpMethods = CORSFilter.DEFAULT_ALLOWED_HTTP_METHODS; >+ final String allowedOrigins = CORSFilter.DEFAULT_ALLOWED_ORIGINS; >+ final String exposedHeaders = CORSFilter.DEFAULT_EXPOSED_HEADERS; >+ final String supportCredentials = CORSFilter.DEFAULT_SUPPORTS_CREDENTIALS; >+ final String preflightMaxAge = CORSFilter.DEFAULT_PREFLIGHT_MAXAGE; >+ final String loggingEnabled = CORSFilter.DEFAULT_LOGGING_ENABLED; >+ final String decorateRequest = "false"; >+ >+ return generateFilterConfig(allowedHttpHeaders, allowedHttpMethods, >+ allowedOrigins, exposedHeaders, supportCredentials, >+ preflightMaxAge, loggingEnabled, decorateRequest); >+ } >+ >+ private static FilterConfig generateFilterConfig( >+ final String allowedHttpHeaders, final String allowedHttpMethods, >+ final String allowedOrigins, final String exposedHeaders, >+ final String supportCredentials, final String preflightMaxAge, >+ final String loggingEnabled, final String decorateRequest) { >+ FilterConfig filterConfig = new FilterConfig() { >+ >+ @Override >+ public String getFilterName() { >+ return "cors-filter"; >+ } >+ >+ @Override >+ public ServletContext getServletContext() { >+ return mockServletContext; >+ } >+ >+ @Override >+ public String getInitParameter(String name) { >+ if (CORSFilter.PARAM_CORS_ALLOWED_HEADERS >+ .equalsIgnoreCase(name)) { >+ return allowedHttpHeaders; >+ } else if (CORSFilter.PARAM_CORS_ALLOWED_METHODS >+ .equalsIgnoreCase(name)) { >+ return allowedHttpMethods; >+ } else if (CORSFilter.PARAM_CORS_ALLOWED_ORIGINS >+ .equalsIgnoreCase(name)) { >+ return allowedOrigins; >+ } else if (CORSFilter.PARAM_CORS_EXPOSED_HEADERS >+ .equalsIgnoreCase(name)) { >+ return exposedHeaders; >+ } else if (CORSFilter.PARAM_CORS_SUPPORT_CREDENTIALS >+ .equalsIgnoreCase(name)) { >+ return supportCredentials; >+ } else if (CORSFilter.PARAM_CORS_PREFLIGHT_MAXAGE >+ .equalsIgnoreCase(name)) { >+ return preflightMaxAge; >+ } else if (CORSFilter.PARAM_CORS_LOGGING_ENABLED >+ .equalsIgnoreCase(name)) { >+ return loggingEnabled; >+ } else if (CORSFilter.PARAM_CORS_REQUEST_DECORATE >+ .equalsIgnoreCase(name)) { >+ return decorateRequest; >+ } >+ return null; >+ } >+ >+ @Override >+ public Enumeration<String> getInitParameterNames() { >+ return null; >+ } >+ }; >+ >+ return filterConfig; >+ } >+} >\ No newline at end of file > >Property changes on: test/org/apache/catalina/filters/TesterFilterConfigs.java >___________________________________________________________________ >Added: svn:eol-style > + native > >Index: test/org/apache/catalina/filters/TesterHttpServletResponse.java >=================================================================== >--- test/org/apache/catalina/filters/TesterHttpServletResponse.java (revision 0) >+++ test/org/apache/catalina/filters/TesterHttpServletResponse.java (revision 0) >@@ -0,0 +1,238 @@ >+/** >+ * Copyright 2012-2013 eBay Software Foundation, All Rights Reserved. >+ * >+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not >+ * use this file except in compliance with the License. You may obtain a copy of >+ * the License at >+ * >+ * http://www.apache.org/licenses/LICENSE-2.0 >+ * >+ * Unless required by applicable law or agreed to in writing, software >+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT >+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the >+ * License for the specific language governing permissions and limitations under >+ * the License. >+ */ >+package org.apache.catalina.filters; >+ >+import java.io.IOException; >+import java.io.PrintWriter; >+import java.io.StringWriter; >+import java.util.ArrayList; >+import java.util.Collection; >+import java.util.List; >+import java.util.Locale; >+ >+import javax.servlet.ServletOutputStream; >+import javax.servlet.http.Cookie; >+import javax.servlet.http.HttpServletResponse; >+ >+/** >+ * A mock {@link HttpServletResponse}. >+ */ >+public class TesterHttpServletResponse implements HttpServletResponse { >+ List<String> headerNames = new ArrayList<String>(); >+ List<String> headerValues = new ArrayList<String>(); >+ PrintWriter pw; >+ int status; >+ >+ @Override >+ public String getCharacterEncoding() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getContentType() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public ServletOutputStream getOutputStream() throws IOException { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public PrintWriter getWriter() throws IOException { >+ if (pw == null) { >+ pw = new PrintWriter(new StringWriter()); >+ } >+ return pw; >+ } >+ >+ @Override >+ public void setCharacterEncoding(String charset) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void setContentLength(int len) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void setContentType(String type) { >+ // NO-OP >+ } >+ >+ @Override >+ public void setBufferSize(int size) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public int getBufferSize() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void flushBuffer() throws IOException { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void resetBuffer() { >+ // NO-OP >+ } >+ >+ @Override >+ public boolean isCommitted() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void reset() { >+ // NO-OP >+ } >+ >+ @Override >+ public void setLocale(Locale loc) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Locale getLocale() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void addCookie(Cookie cookie) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public boolean containsHeader(String name) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String encodeURL(String url) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String encodeRedirectURL(String url) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String encodeUrl(String url) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String encodeRedirectUrl(String url) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void sendError(int sc, String msg) throws IOException { >+ // NO-OP >+ } >+ >+ @Override >+ public void sendError(int sc) throws IOException { >+ // NO-OP >+ } >+ >+ @Override >+ public void sendRedirect(String location) throws IOException { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void setDateHeader(String name, long date) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void addDateHeader(String name, long date) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public String getHeader(String name) { >+ int index = headerNames.indexOf(name); >+ if (index != -1) { >+ return headerValues.get(index); >+ } >+ return null; >+ } >+ >+ @Override >+ public void setHeader(String name, String value) { >+ int index = headerNames.indexOf(name); >+ if (index != -1) { >+ headerValues.set(index, value); >+ } else { >+ headerNames.add(name); >+ headerValues.add(value); >+ } >+ } >+ >+ @Override >+ public void addHeader(String name, String value) { >+ headerNames.add(name); >+ headerValues.add(value); >+ } >+ >+ @Override >+ public void setIntHeader(String name, int value) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void addIntHeader(String name, int value) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public void setStatus(int sc) { >+ this.status = sc; >+ } >+ >+ @Override >+ public int getStatus() { >+ return this.status; >+ } >+ >+ @Override >+ public void setStatus(int sc, String sm) { >+ // NO-OP >+ } >+ >+ @Override >+ public void setContentLengthLong(long length) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Collection<String> getHeaders(String name) { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+ @Override >+ public Collection<String> getHeaderNames() { >+ throw new RuntimeException("Not implemented"); >+ } >+ >+} >\ No newline at end of file > >Property changes on: test/org/apache/catalina/filters/TesterHttpServletResponse.java >___________________________________________________________________ >Added: svn:eol-style > + native > >Index: test/org/apache/catalina/filters/TestCORSFilter.java >=================================================================== >--- test/org/apache/catalina/filters/TestCORSFilter.java (revision 0) >+++ test/org/apache/catalina/filters/TestCORSFilter.java (revision 0) >@@ -0,0 +1,1562 @@ >+/** >+ * Copyright 2012-2013 eBay Software Foundation, All Rights Reserved. >+ * >+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not >+ * use this file except in compliance with the License. You may obtain a copy of >+ * the License at >+ * >+ * http://www.apache.org/licenses/LICENSE-2.0 >+ * >+ * Unless required by applicable law or agreed to in writing, software >+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT >+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the >+ * License for the specific language governing permissions and limitations under >+ * the License. >+ */ >+package org.apache.catalina.filters; >+ >+import java.io.IOException; >+import java.util.LinkedHashSet; >+import java.util.Set; >+ >+import javax.servlet.FilterChain; >+import javax.servlet.ServletException; >+import javax.servlet.http.HttpServletRequest; >+import javax.servlet.http.HttpServletResponse; >+ >+import org.junit.Assert; >+import org.junit.Test; >+ >+/** >+ * Unit tests for {@link CORSFilter}. >+ */ >+public class TestCORSFilter { >+ private final FilterChain filterChain = new TesterFilterChain(); >+ >+ /** >+ * Tests if a GET request is treated as simple request. >+ * >+ * @See http://www.w3.org/TR/cors/#simple-method >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test >+ public void testDoFilterSimpleGET() throws IOException, ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); >+ request.setMethod("GET"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ corsFilter.doFilter(request, response, filterChain); >+ >+ Assert.assertTrue(response.getHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( >+ "https://www.apache.org")); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals( >+ Boolean.TRUE)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals( >+ CORSFilter.CORSRequestType.SIMPLE.name().toLowerCase())); >+ } >+ >+ /** >+ * Tests if a POST request is treated as simple request. >+ * >+ * @See http://www.w3.org/TR/cors/#simple-method >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test >+ public void testDoFilterSimplePOST() throws IOException, ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); >+ request.setContentType("text/plain"); >+ request.setMethod("POST"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ corsFilter.doFilter(request, response, filterChain); >+ >+ Assert.assertTrue(response.getHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( >+ "https://www.apache.org")); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals( >+ Boolean.TRUE)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals( >+ CORSFilter.CORSRequestType.SIMPLE.name().toLowerCase())); >+ } >+ >+ /** >+ * Tests if a HEAD request is treated as simple request. >+ * >+ * @See http://www.w3.org/TR/cors/#simple-method >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test >+ public void testDoFilterSimpleHEAD() throws IOException, ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); >+ request.setMethod("HEAD"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ corsFilter.doFilter(request, response, filterChain); >+ >+ Assert.assertTrue(response.getHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( >+ "https://www.apache.org")); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals( >+ Boolean.TRUE)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals( >+ CORSFilter.CORSRequestType.SIMPLE.name().toLowerCase())); >+ } >+ >+ /** >+ * Test the presence of specific origin in response, when '*' is not used. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test >+ public void testDoFilterSimpleSpecificHeader() throws IOException, >+ ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); >+ request.setMethod("POST"); >+ request.setContentType("text/plain"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig()); >+ corsFilter.doFilter(request, response, filterChain); >+ >+ Assert.assertTrue(response.getHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals( >+ Boolean.TRUE)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals( >+ CORSFilter.CORSRequestType.SIMPLE.name().toLowerCase())); >+ } >+ >+ /** >+ * Tests the prsence of the origin (and not '*') in the response, when >+ * supports credentials is enabled alongwith any origin, '*'. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test >+ public void testDoFilterSimpleAnyOriginAndSupportsCredentials() >+ throws IOException, ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); >+ request.setMethod("GET"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs >+ .getFilterConfigAnyOriginAndSupportsCredentials()); >+ corsFilter.doFilter(request, response, filterChain); >+ >+ Assert.assertTrue(response.getHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertTrue(response.getHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS) >+ .equals("true")); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals( >+ Boolean.TRUE)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals( >+ CORSFilter.CORSRequestType.SIMPLE.name().toLowerCase())); >+ } >+ >+ /** >+ * Tests the presence of the origin (and not '*') in the response, when >+ * supports credentials is enabled alongwith any origin, '*'. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test >+ public void testDoFilterSimpleAnyOriginAndSupportsCredentialsDisabled() >+ throws IOException, ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); >+ request.setMethod("GET"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs >+ .getFilterConfigAnyOriginAndSupportsCredentialsDisabled()); >+ corsFilter.doFilter(request, response, filterChain); >+ >+ Assert.assertTrue(response.getHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( >+ TesterFilterConfigs.ANY_ORIGIN)); >+ Assert.assertNull(response >+ .getHeader(CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals( >+ Boolean.TRUE)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals( >+ CORSFilter.CORSRequestType.SIMPLE.name().toLowerCase())); >+ } >+ >+ /** >+ * Tests the presence of exposed headers in response, if configured. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test >+ public void testDoFilterSimpleWithExposedHeaders() throws IOException, >+ ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); >+ request.setMethod("POST"); >+ request.setContentType("text/plain"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter >+ .init(TesterFilterConfigs.getFilterConfigWithExposedHeaders()); >+ corsFilter.doFilter(request, response, filterChain); >+ >+ Assert.assertTrue(response.getHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( >+ "https://www.apache.org")); >+ Assert.assertTrue(response.getHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS) >+ .equals(TesterFilterConfigs.EXPOSED_HEADERS)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals( >+ Boolean.TRUE)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals( >+ CORSFilter.CORSRequestType.SIMPLE.name().toLowerCase())); >+ } >+ >+ /** >+ * Checks if an OPTIONS request is processed as pre-flight. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test >+ public void testDoFilterPreflight() throws IOException, ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, >+ "Content-Type"); >+ request.setMethod("OPTIONS"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig()); >+ corsFilter.doFilter(request, response, filterChain); >+ >+ Assert.assertTrue(response.getHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals( >+ Boolean.TRUE)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals( >+ CORSFilter.CORSRequestType.PRE_FLIGHT.name().toLowerCase())); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS).equals( >+ "Content-Type")); >+ } >+ >+ /** >+ * Checks if an OPTIONS request is processed as pre-flight where any origin >+ * is enabled. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test >+ public void testDoFilterPreflightAnyOrigin() throws IOException, >+ ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, >+ "Content-Type"); >+ request.setMethod("OPTIONS"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig()); >+ corsFilter.doFilter(request, response, filterChain); >+ >+ Assert.assertTrue(response.getHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals( >+ Boolean.TRUE)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals( >+ CORSFilter.CORSRequestType.PRE_FLIGHT.name().toLowerCase())); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS).equals( >+ "Content-Type")); >+ } >+ >+ /** >+ * Checks if an OPTIONS request is processed as pre-flight. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test >+ public void testDoFilterPreflightInvalidOrigin() throws IOException, >+ ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ "http://www.example.com"); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, >+ "Content-Type"); >+ request.setMethod("OPTIONS"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig()); >+ corsFilter.doFilter(request, response, filterChain); >+ >+ Assert.assertEquals(response.getStatus(), >+ HttpServletResponse.SC_FORBIDDEN); >+ } >+ >+ /** >+ * Tests the case when a negative max-age header is provided. In that case >+ * the browser should not cache the response. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test >+ public void testDoFilterPreflightNegativeMaxAge() throws IOException, >+ ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, >+ "Content-Type"); >+ request.setMethod("OPTIONS"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs >+ .getSpecificOriginFilterConfigNegativeMaxAge()); >+ corsFilter.doFilter(request, response, filterChain); >+ >+ Assert.assertTrue(response.getHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertNull(response >+ .getHeader(CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_MAX_AGE)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals( >+ Boolean.TRUE)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals( >+ CORSFilter.CORSRequestType.PRE_FLIGHT.name().toLowerCase())); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS).equals( >+ "Content-Type")); >+ } >+ >+ /** >+ * Tests a preflight request with credentials enabled. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test >+ public void testDoFilterPreflightWithCredentials() throws IOException, >+ ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, >+ "Content-Type"); >+ request.setMethod("OPTIONS"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getSecureFilterConfig()); >+ corsFilter.doFilter(request, response, filterChain); >+ >+ Assert.assertTrue(response.getHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertTrue(response.getHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS) >+ .equals("true")); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals( >+ Boolean.TRUE)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals( >+ CORSFilter.CORSRequestType.PRE_FLIGHT.name().toLowerCase())); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS).equals( >+ "Content-Type")); >+ } >+ >+ /** >+ * Tests a preflight request, when specific origin is enabled and >+ * credentials are disabled. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test >+ public void testDoFilterPreflightWithoutCredentialsAndSpecificOrigin() >+ throws IOException, ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, >+ "Content-Type"); >+ request.setMethod("OPTIONS"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs >+ .getFilterConfigSpecificOriginAndSupportsCredentialsDisabled()); >+ corsFilter.doFilter(request, response, filterChain); >+ >+ Assert.assertTrue(response.getHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertNull(response >+ .getHeader(CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals( >+ Boolean.TRUE)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals( >+ CORSFilter.CORSRequestType.PRE_FLIGHT.name().toLowerCase())); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS).equals( >+ "Content-Type")); >+ } >+ >+ /** >+ * Negative test, when a CORS request arrives, with a null origin. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test >+ public void testDoFilterNullOrigin() throws IOException, ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ >+ request.setMethod("POST"); >+ request.setContentType("text/plain"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ CORSFilter.CORSRequestType requestType = corsFilter >+ .checkRequestType(request); >+ Assert.assertEquals(CORSFilter.CORSRequestType.NOT_CORS, requestType); >+ >+ corsFilter.doFilter(request, response, filterChain); >+ >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals( >+ Boolean.FALSE)); >+ } >+ >+ /** >+ * Tests a CORS request, from an origin that's not allowed. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test >+ public void testDoFilterInvalidCORSOriginNotAllowed() throws IOException, >+ ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, "www.google.com"); >+ request.setMethod("POST"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig()); >+ corsFilter.doFilter(request, response, filterChain); >+ >+ Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN, >+ response.getStatus()); >+ } >+ >+ /** >+ * Negative case, when a null request and null response is used. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test(expected = ServletException.class) >+ public void testDoFilterNullRequestNullResponse() throws IOException, >+ ServletException { >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ corsFilter.doFilter(null, null, filterChain); >+ } >+ >+ /** >+ * Negative case, when a null request is used. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test(expected = ServletException.class) >+ public void testDoFilterNullRequestResponse() throws IOException, >+ ServletException { >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ corsFilter.doFilter(null, response, filterChain); >+ } >+ >+ /** >+ * Negative case, when a null response is used. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test(expected = ServletException.class) >+ public void testDoFilterRequestNullResponse() throws IOException, >+ ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ corsFilter.doFilter(request, null, filterChain); >+ } >+ >+ /** >+ * Tests filter init for defaults. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test >+ public void testInitDefaultFilterConfig() throws IOException, >+ ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); >+ request.setMethod("GET"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(null); >+ corsFilter.doFilter(request, response, filterChain); >+ >+ Assert.assertTrue(response.getHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( >+ "https://www.apache.org")); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals( >+ Boolean.TRUE)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals( >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG)); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals( >+ CORSFilter.CORSRequestType.SIMPLE.name().toLowerCase())); >+ } >+ >+ /** >+ * Tests filter init when an invalid filter config is provided with invalid >+ * max age. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test(expected = ServletException.class) >+ public void testInitInvalidFilterConfig() throws IOException, >+ ServletException { >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs >+ .getFilterConfigInvalidMaxPreflightAge()); >+ // If we don't get an exception at this point, then all mocked objects >+ // worked as expected. >+ } >+ >+ /** >+ * Tests if a non-simple request is given to simple request handler. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test(expected = IllegalArgumentException.class) >+ public void testNotSimple() throws IOException, ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, >+ "Content-Type"); >+ request.setMethod("OPTIONS"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ corsFilter.handleSimpleCORS(request, response, filterChain); >+ } >+ >+ /** >+ * When a non-preflight request is given to a pre-flight requets handler. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test(expected = IllegalArgumentException.class) >+ public void testNotPreflight() throws IOException, ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); >+ request.setMethod("GET"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ corsFilter.handlePreflightCORS(request, response, filterChain); >+ } >+ >+ /** >+ * Tests when a null request and a null response is provided. >+ */ >+ @Test(expected = IllegalArgumentException.class) >+ public void testDecorateCORSPropertiesNullRequestNullCORSRequestType() { >+ CORSFilter.decorateCORSProperties(null, null); >+ } >+ >+ /** >+ * Tests when a null request is provided. >+ */ >+ @Test(expected = IllegalArgumentException.class) >+ public void testDecorateCORSPropertiesNullRequestValidCORSRequestType() { >+ CORSFilter.decorateCORSProperties(null, >+ CORSFilter.CORSRequestType.SIMPLE); >+ } >+ >+ /** >+ * Tests when a null response is provided. >+ */ >+ @Test(expected = IllegalArgumentException.class) >+ public void testDecorateCORSPropertiesValidRequestNullRequestType() { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ CORSFilter.decorateCORSProperties(request, null); >+ } >+ >+ /** >+ * Test a normal non cross-origin request. >+ */ >+ @Test >+ public void testDecorateCORSPropertiesCORSRequestTypeNotCORS() { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ CORSFilter.decorateCORSProperties(request, >+ CORSFilter.CORSRequestType.NOT_CORS); >+ Assert.assertTrue(request.getAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST).equals( >+ Boolean.FALSE)); >+ } >+ >+ /** >+ * Tests an invalid request type. >+ * >+ * @throws ServletException >+ */ >+ @Test >+ public void testDecorateCORSPropertiesCORSRequestTypeInvalidCORS() { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ CORSFilter.decorateCORSProperties(request, >+ CORSFilter.CORSRequestType.INVALID_CORS); >+ Assert.assertNull(request >+ .getAttribute(CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)); >+ } >+ >+ /** >+ * Tests a simple request type when any origin is enabled. >+ * >+ * @throws ServletException >+ */ >+ @Test >+ public void testCheckSimpleRequestTypeAnyOrigin() throws ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, "http://www.w3.org"); >+ request.setMethod("GET"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ CORSFilter.CORSRequestType requestType = corsFilter >+ .checkRequestType(request); >+ Assert.assertEquals(CORSFilter.CORSRequestType.SIMPLE, requestType); >+ } >+ >+ /** >+ * Happy path test, when a valid CORS Simple request arrives. >+ * >+ * @throws ServletException >+ */ >+ @Test >+ public void testCheckSimpleRequestType() throws ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); >+ request.setMethod("GET"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ CORSFilter.CORSRequestType requestType = corsFilter >+ .checkRequestType(request); >+ Assert.assertEquals(CORSFilter.CORSRequestType.SIMPLE, requestType); >+ } >+ >+ /** >+ * Happy path test, when a valid CORS Simple request arrives. >+ * >+ * @throws ServletException >+ */ >+ @Test >+ public void testCheckActualRequestType() throws ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); >+ request.setMethod("PUT"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ CORSFilter.CORSRequestType requestType = corsFilter >+ .checkRequestType(request); >+ Assert.assertEquals(CORSFilter.CORSRequestType.ACTUAL, requestType); >+ } >+ >+ /** >+ * Happy path test, when a valid CORS Simple request arrives. >+ * >+ * @throws ServletException >+ */ >+ @Test >+ public void testCheckActualRequestTypeMethodPOSTNotSimpleHeaders() >+ throws ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); >+ request.setMethod("POST"); >+ request.setContentType("application/json"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ CORSFilter.CORSRequestType requestType = corsFilter >+ .checkRequestType(request); >+ Assert.assertEquals(CORSFilter.CORSRequestType.ACTUAL, requestType); >+ } >+ >+ /** >+ * Happy path test, when a valid CORS Pre-flight request arrives. >+ * >+ * @throws ServletException >+ */ >+ @Test >+ public void testCheckPreFlightRequestType() throws ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, >+ "Content-Type"); >+ request.setMethod("OPTIONS"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ CORSFilter.CORSRequestType requestType = corsFilter >+ .checkRequestType(request); >+ Assert.assertEquals(CORSFilter.CORSRequestType.PRE_FLIGHT, requestType); >+ } >+ >+ /** >+ * when a valid CORS Pre-flight request arrives, with no >+ * Access-Control-Request-Method >+ * >+ * @throws ServletException >+ * @throws IOException >+ */ >+ @Test >+ public void testCheckPreFlightRequestTypeNoACRM() throws ServletException, >+ IOException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); >+ >+ request.setMethod("OPTIONS"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ CORSFilter.CORSRequestType requestType = corsFilter >+ .checkRequestType(request); >+ Assert.assertEquals(CORSFilter.CORSRequestType.ACTUAL, requestType); >+ } >+ >+ /** >+ * when a valid CORS Pre-flight request arrives, with empty >+ * Access-Control-Request-Method >+ * >+ * @throws ServletException >+ * @throws IOException >+ */ >+ @Test >+ public void testCheckPreFlightRequestTypeEmptyACRM() >+ throws ServletException, IOException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, ""); >+ request.setMethod("OPTIONS"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ CORSFilter.CORSRequestType requestType = corsFilter >+ .checkRequestType(request); >+ Assert.assertEquals(CORSFilter.CORSRequestType.INVALID_CORS, >+ requestType); >+ } >+ >+ /** >+ * Happy path test, when a valid CORS Pre-flight request arrives. >+ * >+ * @throws ServletException >+ */ >+ @Test >+ public void testCheckPreFlightRequestTypeNoHeaders() >+ throws ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); >+ request.setMethod("OPTIONS"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ CORSFilter.CORSRequestType requestType = corsFilter >+ .checkRequestType(request); >+ Assert.assertEquals(CORSFilter.CORSRequestType.PRE_FLIGHT, requestType); >+ } >+ >+ /** >+ * Section 6.2.3 >+ * >+ * @throws ServletException >+ * @throws IOException >+ */ >+ @Test >+ public void testCheckPreFlightRequestTypeInvalidRequestMethod() >+ throws ServletException, IOException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, >+ "POLITE"); >+ request.setMethod("OPTIONS"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ corsFilter.doFilter(request, response, filterChain); >+ Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN, >+ response.getStatus()); >+ } >+ >+ /** >+ * Section Section 6.2.5 >+ * >+ * @throws ServletException >+ * @throws IOException >+ */ >+ @Test >+ public void testCheckPreFlightRequestTypeUnsupportedRequestMethod() >+ throws ServletException, IOException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, >+ "TRACE"); >+ request.setMethod("OPTIONS"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ corsFilter.doFilter(request, response, filterChain); >+ Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN, >+ response.getStatus()); >+ } >+ >+ /** >+ * Section Section 6.2.6 >+ * >+ * @throws ServletException >+ * @throws IOException >+ */ >+ @Test >+ public void testCheckPreFlightRequestTypeUnsupportedRequestHeaders() >+ throws ServletException, IOException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, >+ "X-ANSWER"); >+ request.setMethod("OPTIONS"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getSecureFilterConfig()); >+ corsFilter.doFilter(request, response, filterChain); >+ Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN, >+ response.getStatus()); >+ } >+ >+ /** >+ * Section Section 6.2.7 >+ * >+ * @throws ServletException >+ * @throws IOException >+ */ >+ @Test >+ public void testCheckPreFlightRequestTypeAnyOriginNoWithCredentials() >+ throws ServletException, IOException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, >+ "Origin"); >+ request.setMethod("OPTIONS"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs >+ .getFilterConfigAnyOriginAndSupportsCredentialsDisabled()); >+ corsFilter.doFilter(request, response, filterChain); >+ Assert.assertTrue(response.getHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( >+ "*")); >+ Assert.assertNull(response >+ .getHeader(CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS)); >+ } >+ >+ /** >+ * Test when a preflight request is made from an origin that's not allowed. >+ * >+ * @throws ServletException >+ * @throws IOException >+ */ >+ @Test >+ public void testCheckPreFlightRequestTypeOriginNotAllowed() >+ throws ServletException, IOException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, "www.ebay.com"); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); >+ request.setMethod("OPTIONS"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getSecureFilterConfig()); >+ corsFilter.doFilter(request, response, filterChain); >+ Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN, >+ response.getStatus()); >+ } >+ >+ /** >+ * Happy path test, when a valid CORS Pre-flight request arrives. >+ * >+ * @throws ServletException >+ */ >+ @Test >+ public void testCheckPreFlightRequestTypeEmptyHeaders() >+ throws ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); >+ request.setHeader( >+ CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, ""); >+ request.setMethod("OPTIONS"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ CORSFilter.CORSRequestType requestType = corsFilter >+ .checkRequestType(request); >+ Assert.assertEquals(CORSFilter.CORSRequestType.PRE_FLIGHT, requestType); >+ } >+ >+ /** >+ * Negative test, when a CORS request arrives, with an empty origin. >+ * >+ * @throws ServletException >+ */ >+ @Test >+ public void testCheckNotCORSRequestTypeEmptyOrigin() >+ throws ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, ""); >+ request.setMethod("GET"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ CORSFilter.CORSRequestType requestType = corsFilter >+ .checkRequestType(request); >+ Assert.assertEquals(CORSFilter.CORSRequestType.INVALID_CORS, >+ requestType); >+ } >+ >+ /** >+ * Tests for failure, when a different domain is used, that's not in the >+ * allowed list of origins. >+ * >+ * @throws ServletException >+ * @throws IOException >+ */ >+ @Test >+ public void testCheckInvalidOrigin() throws ServletException, IOException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, "www.example.com"); >+ request.setMethod("GET"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig()); >+ corsFilter.doFilter(request, response, filterChain); >+ Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN, >+ response.getStatus()); >+ } >+ >+ /** >+ * Tests for failure, when a different sub-domain is used, that's not in the >+ * allowed list of origins. >+ * >+ * @throws ServletException >+ * @throws IOException >+ */ >+ @Test >+ public void testCheckInvalidOriginNotAllowedSubdomain() >+ throws ServletException, IOException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ "http://commons.apache.org"); >+ request.setMethod("GET"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig()); >+ corsFilter.doFilter(request, response, filterChain); >+ Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN, >+ response.getStatus()); >+ } >+ >+ /** >+ * PUT is not an allowed request method. >+ * >+ * @throws ServletException >+ * @throws IOException >+ */ >+ @Test >+ public void testCheckInvalidRequestMethod() throws ServletException, >+ IOException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ "http://tomcat.apache.org"); >+ request.setMethod("PUT"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ corsFilter.doFilter(request, response, filterChain); >+ Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN, >+ response.getStatus()); >+ } >+ >+ /** >+ * When requestMethod is null >+ * >+ * @throws ServletException >+ */ >+ @Test >+ public void testCheckNullRequestMethod() throws ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ "http://tomcat.apache.org"); >+ request.setMethod(null); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig()); >+ CORSFilter.CORSRequestType requestType = corsFilter >+ .checkRequestType(request); >+ Assert.assertEquals(CORSFilter.CORSRequestType.INVALID_CORS, >+ requestType); >+ } >+ >+ /** >+ * "http://tomcat.apache.org" is an allowed origin and >+ * "https://tomcat.apache.org" is not, because scheme doesn't match >+ * >+ * @throws ServletException >+ */ >+ @Test >+ public void testCheckForSchemeVariance() throws ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ "https://tomcat.apache.org"); >+ request.setMethod("POST"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig()); >+ CORSFilter.CORSRequestType requestType = corsFilter >+ .checkRequestType(request); >+ Assert.assertEquals(CORSFilter.CORSRequestType.INVALID_CORS, >+ requestType); >+ } >+ >+ /** >+ * "http://tomcat.apache.org" is an allowed origin and >+ * "http://tomcat.apache.org:8080" is not, because ports doesn't match >+ * >+ * @throws ServletException >+ * @throws IOException >+ */ >+ @Test >+ public void testCheckForPortVariance() throws ServletException, IOException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ "http://tomcat.apache.org:8080"); >+ request.setMethod("GET"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig()); >+ corsFilter.doFilter(request, response, filterChain); >+ Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN, >+ response.getStatus()); >+ } >+ >+ /** >+ * Tests for failure, when an invalid {@link HttpServletRequest} is >+ * encountered. >+ * >+ * @throws ServletException >+ */ >+ @Test(expected = IllegalArgumentException.class) >+ public void testCheckRequestTypeNull() throws ServletException { >+ HttpServletRequest request = null; >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.checkRequestType(request); >+ } >+ >+ /** >+ * Tests a normal join of multiple elements, with comma separator. >+ */ >+ @Test >+ public void testJoin() { >+ Set<String> elements = new LinkedHashSet<String>(); >+ String separator = ","; >+ elements.add("world"); >+ elements.add("peace"); >+ String join = CORSFilter.join(elements, separator); >+ Assert.assertTrue("world,peace".equals(join)); >+ } >+ >+ /** >+ * Tests join with only 1 element. >+ */ >+ @Test >+ public void testJoinSingleElement() { >+ Set<String> elements = new LinkedHashSet<String>(); >+ String separator = ","; >+ elements.add("world"); >+ String join = CORSFilter.join(elements, separator); >+ Assert.assertTrue("world".equals(join)); >+ } >+ >+ /** >+ * Tests join when a null separator is provided, the default comma is used. >+ */ >+ @Test >+ public void testJoinSepNull() { >+ Set<String> elements = new LinkedHashSet<String>(); >+ String separator = null; >+ elements.add("world"); >+ elements.add("peace"); >+ String join = CORSFilter.join(elements, separator); >+ Assert.assertTrue("world,peace".equals(join)); >+ } >+ >+ /** >+ * Tests join when elements collection is null. >+ */ >+ @Test >+ public void testJoinElementsNull() { >+ Set<String> elements = null; >+ String separator = ","; >+ String join = CORSFilter.join(elements, separator); >+ >+ Assert.assertNull(join); >+ } >+ >+ /** >+ * Tests join when atleast one null elements is there. >+ */ >+ @Test >+ public void testJoinOneNullElement() { >+ Set<String> elements = new LinkedHashSet<String>(); >+ String separator = ","; >+ elements.add(null); >+ elements.add("peace"); >+ String join = CORSFilter.join(elements, separator); >+ Assert.assertTrue(",peace".equals(join)); >+ } >+ >+ /** >+ * Tests join when null elements are there. >+ */ >+ @Test >+ public void testJoinAllNullElements() { >+ Set<String> elements = new LinkedHashSet<String>(); >+ String separator = ","; >+ elements.add(null); >+ elements.add(null); >+ String join = CORSFilter.join(elements, separator); >+ Assert.assertTrue("".equals(join)); >+ } >+ >+ /** >+ * Tests join, for empty strings. >+ */ >+ @Test >+ public void testJoinAllEmptyElements() { >+ Set<String> elements = new LinkedHashSet<String>(); >+ String separator = ","; >+ elements.add(""); >+ elements.add(""); >+ String join = CORSFilter.join(elements, separator); >+ Assert.assertTrue("".equals(join)); >+ } >+ >+ /** >+ * Tests join, with a pipe separator. >+ */ >+ @Test >+ public void testJoinPipeSeparator() { >+ Set<String> elements = new LinkedHashSet<String>(); >+ String separator = "|"; >+ elements.add("world"); >+ elements.add("peace"); >+ String join = CORSFilter.join(elements, separator); >+ Assert.assertTrue("world|peace".equals(join)); >+ } >+ >+ /** >+ * Tests default filter config. >+ * >+ * @throws ServletException >+ */ >+ @Test >+ public void testWithFilterConfig() throws ServletException { >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ Assert.assertTrue(corsFilter.getAllowedHttpHeaders().size() == 6); >+ Assert.assertTrue(corsFilter.getAllowedHttpMethods().size() == 4); >+ Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0); >+ Assert.assertTrue(corsFilter.isAnyOriginAllowed()); >+ Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0); >+ Assert.assertTrue(corsFilter.isSupportsCredentials()); >+ Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800); >+ Assert.assertTrue(!corsFilter.isLoggingEnabled()); >+ } >+ >+ /** >+ * Tests an invalid non-number max age. >+ * >+ * @throws ServletException >+ */ >+ @Test(expected = ServletException.class) >+ public void testWithFilterConfigInvalidPreflightAge() >+ throws ServletException { >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs >+ .getFilterConfigInvalidMaxPreflightAge()); >+ } >+ >+ /** >+ * Tests empty config object. >+ * >+ * @throws ServletException >+ */ >+ @Test >+ public void testWithStringParserEmpty() throws ServletException { >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getEmptyFilterConfig()); >+ Assert.assertTrue(corsFilter.getAllowedHttpHeaders().size() == 0); >+ Assert.assertTrue(corsFilter.getAllowedHttpMethods().size() == 0); >+ Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0); >+ Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0); >+ Assert.assertFalse(corsFilter.isSupportsCredentials()); >+ Assert.assertTrue(corsFilter.getPreflightMaxAge() == 0); >+ Assert.assertTrue(!corsFilter.isLoggingEnabled()); >+ } >+ >+ /** >+ * If an init param is null, it's default value will be used. >+ * >+ * @throws ServletException >+ */ >+ @Test >+ public void testWithStringParserNull() throws ServletException { >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getNullFilterConfig()); >+ Assert.assertTrue(corsFilter.getAllowedHttpHeaders().size() == 6); >+ Assert.assertTrue(corsFilter.getAllowedHttpMethods().size() == 4); >+ Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0); >+ Assert.assertTrue(corsFilter.isAnyOriginAllowed()); >+ Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0); >+ Assert.assertTrue(corsFilter.isSupportsCredentials()); >+ Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800); >+ Assert.assertTrue(!corsFilter.isLoggingEnabled()); >+ } >+ >+ /** >+ * A valid origin. >+ */ >+ @Test >+ public void testValidOrigin() { >+ Assert.assertTrue(CORSFilter.isValidOrigin("http://www.w3.org")); >+ } >+ >+ /** >+ * Invalid origin, \r\n >+ */ >+ @Test >+ public void testInValidOriginCRLF() { >+ Assert.assertFalse(CORSFilter.isValidOrigin("http://www.w3.org\r\n")); >+ } >+ >+ /** >+ * Invalid origin, encoded chars >+ */ >+ @Test >+ public void testInValidOriginEncodedCRLF1() { >+ Assert.assertFalse(CORSFilter.isValidOrigin("http://www.w3.org%0d%0a")); >+ } >+ >+ /** >+ * Invalid origin, encoded chars in caps. >+ */ >+ @Test >+ public void testInValidOriginEncodedCRLF2() { >+ Assert.assertFalse(CORSFilter.isValidOrigin("http://www.w3.org%0D%0A")); >+ } >+ >+ /** >+ * Invalid origin, double encoded chars. >+ */ >+ @Test >+ public void testInValidOriginEncodedCRLF3() { >+ Assert.assertFalse(CORSFilter >+ .isValidOrigin("http://www.w3.org%0%0d%0ad%0%0d%0aa")); >+ } >+ >+ /** >+ * Test for CRLF, an invalid request. \r\n characters. >+ * >+ * @throws ServletException >+ */ >+ @Test >+ public void testCheckInvalidCRLF1() throws ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ "http://www.w3.org\r\n"); >+ request.setMethod("GET"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ CORSFilter.CORSRequestType requestType = corsFilter >+ .checkRequestType(request); >+ Assert.assertEquals(CORSFilter.CORSRequestType.INVALID_CORS, >+ requestType); >+ } >+ >+ /** >+ * Test for CRLF, an invalid request. >+ * >+ * @throws ServletException >+ */ >+ @Test >+ public void testCheckInvalidCRLF2() throws ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ "http://www.w3.org\r"); >+ request.setMethod("GET"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ CORSFilter.CORSRequestType requestType = corsFilter >+ .checkRequestType(request); >+ Assert.assertEquals(CORSFilter.CORSRequestType.INVALID_CORS, >+ requestType); >+ } >+ >+ /** >+ * Test for CRLF, an invalid request. encoded chars >+ * >+ * @throws ServletException >+ */ >+ @Test >+ public void testCheckInvalidCRLF3() throws ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ "http://www.w3.org%0d%0a"); >+ request.setMethod("GET"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ CORSFilter.CORSRequestType requestType = corsFilter >+ .checkRequestType(request); >+ Assert.assertEquals(CORSFilter.CORSRequestType.INVALID_CORS, >+ requestType); >+ } >+ >+ /** >+ * Test for CRLF, an invalid request. encoded chars in caps. >+ * >+ * @throws ServletException >+ */ >+ @Test >+ public void testCheckInvalidCRLF4() throws ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ "http://www.w3.org%0D%0A"); >+ request.setMethod("GET"); >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); >+ CORSFilter.CORSRequestType requestType = corsFilter >+ .checkRequestType(request); >+ Assert.assertEquals(CORSFilter.CORSRequestType.INVALID_CORS, >+ requestType); >+ } >+ >+ /** >+ * Request should not be decorated. >+ * >+ * @throws IOException >+ * @throws ServletException >+ */ >+ @Test >+ public void testDecorateRequestDisabled() throws IOException, >+ ServletException { >+ TesterHttpServletRequest request = new TesterHttpServletRequest(); >+ request.setHeader(CORSFilter.REQUEST_HEADER_ORIGIN, >+ TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); >+ request.setMethod("GET"); >+ TesterHttpServletResponse response = new TesterHttpServletResponse(); >+ >+ CORSFilter corsFilter = new CORSFilter(); >+ corsFilter.init(TesterFilterConfigs >+ .getFilterConfigDecorateRequestDisabled()); >+ corsFilter.doFilter(request, response, filterChain); >+ >+ Assert.assertTrue(response.getHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals( >+ "https://www.apache.org")); >+ Assert.assertNull(request >+ .getAttribute(CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)); >+ Assert.assertNull(request >+ .getAttribute(CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN)); >+ Assert.assertNull(request >+ .getAttribute(CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS)); >+ Assert.assertNull(request >+ .getAttribute(CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE)); >+ } >+ >+ /** >+ * Not sure how to test doing nothing. Sigh! >+ */ >+ @Test >+ public void testDestroy() { >+ // Nothing to test. >+ // NO-OP >+ } >+} >\ No newline at end of file > >Property changes on: test/org/apache/catalina/filters/TestCORSFilter.java >___________________________________________________________________ >Added: svn:eol-style > + native > >Index: java/org/apache/catalina/filters/CORSFilter.java >=================================================================== >--- java/org/apache/catalina/filters/CORSFilter.java (revision 0) >+++ java/org/apache/catalina/filters/CORSFilter.java (revision 0) >@@ -0,0 +1,1155 @@ >+/** >+ * Copyright 2012-2013 eBay Software Foundation, All Rights Reserved. >+ * >+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not >+ * use this file except in compliance with the License. You may obtain a copy of >+ * the License at >+ * >+ * http://www.apache.org/licenses/LICENSE-2.0 >+ * >+ * Unless required by applicable law or agreed to in writing, software >+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT >+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the >+ * License for the specific language governing permissions and limitations under >+ * the License. >+ */ >+package org.apache.catalina.filters; >+ >+import java.io.IOException; >+import java.net.URI; >+import java.net.URISyntaxException; >+import java.util.Arrays; >+import java.util.Collection; >+import java.util.HashSet; >+import java.util.LinkedList; >+import java.util.List; >+import java.util.Set; >+ >+import javax.servlet.Filter; >+import javax.servlet.FilterChain; >+import javax.servlet.FilterConfig; >+import javax.servlet.ServletException; >+import javax.servlet.ServletRequest; >+import javax.servlet.ServletResponse; >+import javax.servlet.http.HttpServletRequest; >+import javax.servlet.http.HttpServletResponse; >+ >+/** >+ * <p> >+ * A {@link Filter} that enable client-side cross-origin requests by >+ * implementing W3C's CORS (<b>C</b>ross-<b>O</b>rigin <b>R</b>esource >+ * <b>S</b>haring) specification for resources. Each {@link HttpServletRequest} >+ * request is inspected as per specification, and appropriate response headers >+ * are added to {@link HttpServletResponse}. >+ * </p> >+ * >+ * <p> >+ * By default, it also sets following request attributes, that helps to >+ * determine nature of request downstream. >+ * <ul> >+ * <li><b>cors.isCorsRequest:</b> Flag to determine if request is a CORS >+ * request. Set to <code>true</code> if CORS request; <code>false</code> >+ * otherwise.</li> >+ * <li><b>cors.request.origin:</b> The Origin URL, i.e. the URL of the page from >+ * where the request is originated.</li> >+ * <li> >+ * <b>cors.request.type:</b> Type of request. Possible values: >+ * <ul> >+ * <li>SIMPLE: A request which is not preceded by a pre-flight request.</li> >+ * <li>ACTUAL: A request which is preceded by a pre-flight request.</li> >+ * <li>PRE_FLIGHT: A pre-flight request.</li> >+ * <li>NOT_CORS: A normal same-origin request.</li> >+ * <li>INVALID_CORS: A cross-origin request, which is invalid.</li> >+ * </ul> >+ * </li> >+ * <li><b>cors.request.headers:</b> Request headers sent as >+ * 'Access-Control-Request-Headers' header, for pre-flight request.</li> >+ * </ul> >+ * </p> >+ * >+ * @author Mohit Soni >+ * @see <a href="http://www.w3.org/TR/cors/">CORS specification</a> >+ * >+ */ >+public final class CORSFilter implements Filter { >+ // ----------------------------------------------------- Instance variables >+ /** >+ * Holds filter configuration. >+ */ >+ private FilterConfig filterConfig; >+ >+ /** >+ * A {@link Collection} of origins consisting of zero or more origins that >+ * are allowed access to the resource. >+ */ >+ private final Collection<String> allowedOrigins; >+ >+ /** >+ * Determines if any origin is allowed to make request. >+ */ >+ private boolean anyOriginAllowed; >+ >+ /** >+ * A {@link Collection} of methods consisting of zero or more methods that >+ * are supported by the resource. >+ */ >+ private final Collection<String> allowedHttpMethods; >+ >+ /** >+ * A {@link Collection} of headers consisting of zero or more header field >+ * names that are supported by the resource. >+ */ >+ private final Collection<String> allowedHttpHeaders; >+ >+ /** >+ * A {@link Collection} of exposed headers consisting of zero or more header >+ * field names of headers other than the simple response headers that the >+ * resource might use and can be exposed. >+ */ >+ private final Collection<String> exposedHeaders; >+ >+ /** >+ * A supports credentials flag that indicates whether the resource supports >+ * user credentials in the request. It is true when the resource does and >+ * false otherwise. >+ */ >+ private boolean supportsCredentials; >+ >+ /** >+ * Indicates (in seconds) how long the results of a pre-flight request can >+ * be cached in a pre-flight result cache. >+ */ >+ private long preflightMaxAge; >+ >+ /** >+ * Controls access log logging. >+ */ >+ private boolean loggingEnabled; >+ >+ /** >+ * Determines if the request should be decorated or not. >+ */ >+ private boolean decorateRequest; >+ >+ // --------------------------------------------------------- Constructor(s) >+ /** >+ * Initializes configuration {@link Collection} objects. >+ */ >+ public CORSFilter() { >+ this.allowedOrigins = new HashSet<String>(); >+ this.allowedHttpMethods = new HashSet<String>(); >+ this.allowedHttpHeaders = new HashSet<String>(); >+ this.exposedHeaders = new HashSet<String>(); >+ } >+ >+ // --------------------------------------------------------- Public methods >+ @Override >+ public void doFilter(final ServletRequest servletRequest, >+ final ServletResponse servletResponse, final FilterChain filterChain) >+ throws IOException, ServletException { >+ if (!(servletRequest instanceof HttpServletRequest) >+ || !(servletResponse instanceof HttpServletResponse)) { >+ String message = "CORS doesn't support non-HTTP request or response."; >+ throw new ServletException(message); >+ } >+ >+ // Safe to downcast at this point. >+ HttpServletRequest request = (HttpServletRequest) servletRequest; >+ HttpServletResponse response = (HttpServletResponse) servletResponse; >+ >+ // Determines the CORS request type. >+ CORSFilter.CORSRequestType requestType = checkRequestType(request); >+ >+ // Adds CORS specific attributes to request. >+ if (decorateRequest) { >+ CORSFilter.decorateCORSProperties(request, requestType); >+ } >+ switch (requestType) { >+ case SIMPLE: >+ // Handles a Simple CORS request. >+ this.handleSimpleCORS(request, response, filterChain); >+ break; >+ case ACTUAL: >+ // Handles an Actual CORS request. >+ this.handleSimpleCORS(request, response, filterChain); >+ break; >+ case PRE_FLIGHT: >+ // Handles a Pre-flight CORS request. >+ this.handlePreflightCORS(request, response, filterChain); >+ break; >+ case NOT_CORS: >+ // Handles a Normal request that is not a cross-origin request. >+ this.handleNonCORS(request, response, filterChain); >+ break; >+ default: >+ // Handles a CORS request that violates specification. >+ this.handleInvalidCORS(request, response, filterChain); >+ break; >+ } >+ } >+ >+ @Override >+ public void init(final FilterConfig filterConfig) throws ServletException { >+ // Initialize defaults >+ parseAndStore(DEFAULT_ALLOWED_ORIGINS, DEFAULT_ALLOWED_HTTP_METHODS, >+ DEFAULT_ALLOWED_HTTP_HEADERS, DEFAULT_EXPOSED_HEADERS, >+ DEFAULT_SUPPORTS_CREDENTIALS, DEFAULT_PREFLIGHT_MAXAGE, >+ DEFAULT_LOGGING_ENABLED, DEFAULT_DECORATE_REQUEST); >+ >+ this.filterConfig = filterConfig; >+ this.loggingEnabled = false; >+ >+ if (filterConfig != null) { >+ String configAllowedOrigins = filterConfig >+ .getInitParameter(PARAM_CORS_ALLOWED_ORIGINS); >+ String configAllowedHttpMethods = filterConfig >+ .getInitParameter(PARAM_CORS_ALLOWED_METHODS); >+ String configAllowedHttpHeaders = filterConfig >+ .getInitParameter(PARAM_CORS_ALLOWED_HEADERS); >+ String configExposedHeaders = filterConfig >+ .getInitParameter(PARAM_CORS_EXPOSED_HEADERS); >+ String configSupportsCredentials = filterConfig >+ .getInitParameter(PARAM_CORS_SUPPORT_CREDENTIALS); >+ String configPreflightMaxAge = filterConfig >+ .getInitParameter(PARAM_CORS_PREFLIGHT_MAXAGE); >+ String configLoggingEnabled = filterConfig >+ .getInitParameter(PARAM_CORS_LOGGING_ENABLED); >+ String configDecorateRequest = filterConfig >+ .getInitParameter(PARAM_CORS_REQUEST_DECORATE); >+ >+ parseAndStore(configAllowedOrigins, configAllowedHttpMethods, >+ configAllowedHttpHeaders, configExposedHeaders, >+ configSupportsCredentials, configPreflightMaxAge, >+ configLoggingEnabled, configDecorateRequest); >+ } >+ } >+ >+ // --------------------------------------------------------------- Handlers >+ /** >+ * Handles a CORS request of type {@link CORSRequestType}.SIMPLE. >+ * >+ * @param request >+ * The {@link HttpServletRequest} object. >+ * @param response >+ * The {@link HttpServletResponse} object. >+ * @param filterChain >+ * The {@link FilterChain} object. >+ * @throws IOException >+ * @throws ServletException >+ * @see <a href="http://www.w3.org/TR/cors/#resource-requests">Simple >+ * Cross-Origin Request, Actual Request, and Redirects</a> >+ */ >+ public void handleSimpleCORS(final HttpServletRequest request, >+ final HttpServletResponse response, final FilterChain filterChain) >+ throws IOException, ServletException { >+ CORSFilter.CORSRequestType requestType = checkRequestType(request); >+ if (!(requestType == CORSFilter.CORSRequestType.SIMPLE || requestType == CORSFilter.CORSRequestType.ACTUAL)) { >+ String message = "Expects a HttpServletRequest object of type " >+ + CORSFilter.CORSRequestType.SIMPLE + " or " >+ + CORSFilter.CORSRequestType.ACTUAL; >+ throw new IllegalArgumentException(message); >+ } >+ >+ final String origin = request >+ .getHeader(CORSFilter.REQUEST_HEADER_ORIGIN); >+ final String method = request.getMethod(); >+ >+ // Section 6.1.2 >+ if (!isOriginAllowed(origin)) { >+ handleInvalidCORS(request, response, filterChain); >+ return; >+ } >+ >+ if (!allowedHttpMethods.contains(method)) { >+ handleInvalidCORS(request, response, filterChain); >+ return; >+ } >+ >+ // Section 6.1.3 >+ // Add a single Access-Control-Allow-Origin header. >+ if (anyOriginAllowed && !supportsCredentials) { >+ // If resource doesn't support credentials and if any origin is >+ // allowed >+ // to make CORS request, return header with '*'. >+ response.addHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, "*"); >+ } else { >+ // If the resource supports credentials add a single >+ // Access-Control-Allow-Origin header, with the value of the Origin >+ // header as value. >+ response.addHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, >+ origin); >+ } >+ // Section 6.1.3 >+ // If the resource supports credentials, add a single >+ // Access-Control-Allow-Credentials header with the case-sensitive >+ // string "true" as value. >+ if (supportsCredentials) { >+ response.addHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS, >+ "true"); >+ } >+ >+ // Section 6.1.4 >+ // If the list of exposed headers is not empty add one or more >+ // Access-Control-Expose-Headers headers, with as values the header >+ // field names given in the list of exposed headers. >+ if ((exposedHeaders != null) && (exposedHeaders.size() > 0)) { >+ String exposedHeadersString = join(exposedHeaders, ","); >+ response.addHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, >+ exposedHeadersString); >+ } >+ >+ // Forward the request down the filter chain. >+ filterChain.doFilter(request, response); >+ } >+ >+ /** >+ * Handles CORS pre-flight request. >+ * >+ * @param request >+ * The {@link HttpServletRequest} object. >+ * @param response >+ * The {@link HttpServletResponse} object. >+ * @param filterChain >+ * The {@link FilterChain} object. >+ * @throws IOException >+ * @throws ServletException >+ */ >+ public void handlePreflightCORS(final HttpServletRequest request, >+ final HttpServletResponse response, final FilterChain filterChain) >+ throws IOException, ServletException { >+ CORSRequestType requestType = checkRequestType(request); >+ if (requestType != CORSRequestType.PRE_FLIGHT) { >+ throw new IllegalArgumentException( >+ "Expects a HttpServletRequest object of type " >+ + CORSRequestType.PRE_FLIGHT.name().toLowerCase()); >+ } >+ >+ final String origin = request >+ .getHeader(CORSFilter.REQUEST_HEADER_ORIGIN); >+ >+ // Section 6.2.2 >+ if (!isOriginAllowed(origin)) { >+ handleInvalidCORS(request, response, filterChain); >+ return; >+ } >+ >+ // Section 6.2.3 >+ String accessControlRequestMethod = request >+ .getHeader(CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD); >+ if (accessControlRequestMethod == null >+ || (!HTTP_METHODS.contains(accessControlRequestMethod.trim()))) { >+ handleInvalidCORS(request, response, filterChain); >+ return; >+ } else { >+ accessControlRequestMethod = accessControlRequestMethod.trim(); >+ } >+ >+ // Section 6.2.4 >+ String accessControlRequestHeadersHeader = request >+ .getHeader(CORSFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS); >+ List<String> accessControlRequestHeaders = new LinkedList<String>(); >+ if (accessControlRequestHeadersHeader != null >+ && !accessControlRequestHeadersHeader.trim().isEmpty()) { >+ String[] headers = accessControlRequestHeadersHeader.trim().split( >+ ","); >+ for (String header : headers) { >+ accessControlRequestHeaders.add(header.trim().toLowerCase()); >+ } >+ } >+ >+ // Section 6.2.5 >+ if (!allowedHttpMethods.contains(accessControlRequestMethod)) { >+ handleInvalidCORS(request, response, filterChain); >+ return; >+ } >+ >+ // Section 6.2.6 >+ if (!accessControlRequestHeaders.isEmpty()) { >+ for (String header : accessControlRequestHeaders) { >+ if (!allowedHttpHeaders.contains(header)) { >+ handleInvalidCORS(request, response, filterChain); >+ return; >+ } >+ } >+ } >+ >+ // Section 6.2.7 >+ if (supportsCredentials) { >+ response.addHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, >+ origin); >+ response.addHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS, >+ "true"); >+ } else { >+ if (anyOriginAllowed) { >+ response.addHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, >+ "*"); >+ } else { >+ response.addHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, >+ origin); >+ } >+ } >+ >+ // Section 6.2.8 >+ if (preflightMaxAge > 0) { >+ response.addHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_MAX_AGE, >+ String.valueOf(preflightMaxAge)); >+ } >+ >+ // Section 6.2.9 >+ response.addHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_METHODS, >+ accessControlRequestMethod); >+ >+ // Section 6.2.10 >+ if ((allowedHttpHeaders != null) && (!allowedHttpHeaders.isEmpty())) { >+ response.addHeader( >+ CORSFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_HEADERS, >+ join(allowedHttpHeaders, ",")); >+ } >+ >+ // Do not forward the request down the filter chain. >+ } >+ >+ /** >+ * Handles a request, that's not a CORS request, but is a valid request i.e. >+ * it is not a cross-origin request. This implementation, just forwards the >+ * request down the filter chain. >+ * >+ * @param request >+ * The {@link HttpServletRequest} object. >+ * @param response >+ * The {@link HttpServletResponse} object. >+ * @param filterChain >+ * The {@link FilterChain} object. >+ * @throws IOException >+ * @throws ServletException >+ */ >+ public void handleNonCORS(final HttpServletRequest request, >+ final HttpServletResponse response, final FilterChain filterChain) >+ throws IOException, ServletException { >+ // Let request pass. >+ filterChain.doFilter(request, response); >+ } >+ >+ /** >+ * Handles a CORS request that violates specification. >+ * >+ * @param request >+ * The {@link HttpServletRequest} object. >+ * @param response >+ * The {@link HttpServletResponse} object. >+ * @param filterChain >+ * The {@link FilterChain} object. >+ * @throws IOException >+ * @throws ServletException >+ */ >+ public void handleInvalidCORS(final HttpServletRequest request, >+ final HttpServletResponse response, final FilterChain filterChain) { >+ String origin = request.getHeader(CORSFilter.REQUEST_HEADER_ORIGIN); >+ String method = request.getMethod(); >+ String accessControlRequestHeaders = request >+ .getHeader(REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS); >+ >+ String message = "Invalid CORS request; Origin=" + origin + ";Method=" >+ + method; >+ if (accessControlRequestHeaders != null) { >+ message = message + ";Access-Control-Request-Headers=" >+ + accessControlRequestHeaders; >+ } >+ response.setContentType("text/plain"); >+ response.setStatus(HttpServletResponse.SC_FORBIDDEN); >+ response.resetBuffer(); >+ >+ log(message); >+ } >+ >+ @Override >+ public void destroy() { >+ // NOOP >+ } >+ >+ // -------------------------------------------------------- Utility methods >+ /** >+ * Decorates the {@link HttpServletRequest}, with CORS attributes. >+ * <ul> >+ * <li><b>cors.isCorsRequest:</b> Flag to determine if request is a CORS >+ * request. Set to <code>true</code> if CORS request; <code>false</code> >+ * otherwise.</li> >+ * <li><b>cors.request.origin:</b> The Origin URL.</li> >+ * <li><b>cors.request.type:</b> Type of request. Values: >+ * <code>simple</code> or <code>preflight</code> or <code>not_cors</code> or >+ * <code>invalid_cors</code></li> >+ * <li><b>cors.request.headers:</b> Request headers sent as >+ * 'Access-Control-Request-Headers' header, for pre-flight request.</li> >+ * </ul> >+ * >+ * @param request >+ * The {@link HttpServletRequest} object. >+ * @param corsRequestType >+ * The {@link CORSRequestType} object. >+ */ >+ public static void decorateCORSProperties(final HttpServletRequest request, >+ final CORSRequestType corsRequestType) { >+ if (request == null) { >+ throw new IllegalArgumentException( >+ "HttpServletRequest object is null"); >+ } >+ >+ if (corsRequestType == null) { >+ throw new IllegalArgumentException("CORSRequestType object is null"); >+ } >+ >+ switch (corsRequestType) { >+ case SIMPLE: >+ request.setAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST, >+ Boolean.TRUE); >+ request.setAttribute(CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN, >+ request.getHeader(CORSFilter.REQUEST_HEADER_ORIGIN)); >+ request.setAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE, >+ corsRequestType.name().toLowerCase()); >+ break; >+ case ACTUAL: >+ request.setAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST, >+ Boolean.TRUE); >+ request.setAttribute(CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN, >+ request.getHeader(CORSFilter.REQUEST_HEADER_ORIGIN)); >+ request.setAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE, >+ corsRequestType.name().toLowerCase()); >+ break; >+ case PRE_FLIGHT: >+ request.setAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST, >+ Boolean.TRUE); >+ request.setAttribute(CORSFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN, >+ request.getHeader(CORSFilter.REQUEST_HEADER_ORIGIN)); >+ request.setAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE, >+ corsRequestType.name().toLowerCase()); >+ String headers = request >+ .getHeader(REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS); >+ if (headers == null) { >+ headers = ""; >+ } >+ request.setAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS, headers); >+ break; >+ case NOT_CORS: >+ request.setAttribute( >+ CORSFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST, >+ Boolean.FALSE); >+ break; >+ default: >+ // Don't set any attributes >+ break; >+ } >+ } >+ >+ /** >+ * Joins elements of {@link Set} into a string, where each element is >+ * separated by the provided separator. >+ * >+ * @param elements >+ * The {@link Set} containing elements to join together. >+ * @param joinSeparator >+ * The character to be used for separating elements. >+ * @return The joined {@link String}; <code>null</code> if elements >+ * {@link Set} is null. >+ */ >+ public static String join(final Collection<String> elements, >+ final String joinSeparator) { >+ String separator = ","; >+ if (elements == null) { >+ return null; >+ } >+ if (joinSeparator != null) { >+ separator = joinSeparator; >+ } >+ StringBuilder buffer = new StringBuilder(); >+ boolean isFirst = true; >+ for (String element : elements) { >+ if (!isFirst) { >+ buffer.append(separator); >+ } else { >+ isFirst = false; >+ } >+ >+ if (element != null) { >+ buffer.append(element); >+ } >+ } >+ >+ return buffer.toString(); >+ } >+ >+ /** >+ * Determines the request type. >+ * >+ * @param request >+ * @return {@link CORSRequestType} The type of request. >+ */ >+ public CORSRequestType checkRequestType(final HttpServletRequest request) { >+ CORSRequestType requestType = CORSRequestType.INVALID_CORS; >+ if (request == null) { >+ throw new IllegalArgumentException( >+ "HttpServletRequest object is null"); >+ } >+ String originHeader = request.getHeader(REQUEST_HEADER_ORIGIN); >+ // Section 6.1.1 and Section 6.2.1 >+ if (originHeader != null) { >+ if (originHeader.isEmpty()) { >+ requestType = CORSRequestType.INVALID_CORS; >+ } else if (!isValidOrigin(originHeader)) { >+ requestType = CORSRequestType.INVALID_CORS; >+ } else { >+ String method = request.getMethod(); >+ if (method != null && HTTP_METHODS.contains(method)) { >+ if ("OPTIONS".equals(method)) { >+ String accessControlRequestMethodHeader = request >+ .getHeader(REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD); >+ if (accessControlRequestMethodHeader != null >+ && !accessControlRequestMethodHeader.isEmpty()) { >+ requestType = CORSRequestType.PRE_FLIGHT; >+ } else if (accessControlRequestMethodHeader != null >+ && accessControlRequestMethodHeader.isEmpty()) { >+ requestType = CORSRequestType.INVALID_CORS; >+ } else { >+ requestType = CORSRequestType.ACTUAL; >+ } >+ } else if ("GET".equals(method) || "HEAD".equals(method)) { >+ requestType = CORSRequestType.SIMPLE; >+ } else if ("POST".equals(method)) { >+ String contentType = request.getContentType(); >+ if (contentType != null) { >+ contentType = contentType.toLowerCase().trim(); >+ if (SIMPLE_HTTP_REQUEST_CONTENT_TYPE_VALUES >+ .contains(contentType)) { >+ requestType = CORSRequestType.SIMPLE; >+ } else { >+ requestType = CORSRequestType.ACTUAL; >+ } >+ } >+ } else if (COMPLEX_HTTP_METHODS.contains(method)) { >+ requestType = CORSRequestType.ACTUAL; >+ } >+ } >+ } >+ } else { >+ requestType = CORSRequestType.NOT_CORS; >+ } >+ >+ return requestType; >+ } >+ >+ /** >+ * Checks if the Origin is allowed to make a CORS request. >+ * >+ * @param origin >+ * The Origin. >+ * @return <code>true</code> if origin is allowed; <code>false</code> >+ * otherwise. >+ */ >+ private boolean isOriginAllowed(final String origin) { >+ if (anyOriginAllowed) { >+ return true; >+ } >+ >+ // If 'Origin' header is a case-sensitive match of any of allowed >+ // origins, then return true, else return false. >+ return allowedOrigins.contains(origin); >+ } >+ >+ private void log(String message) { >+ if (loggingEnabled) { >+ filterConfig.getServletContext().log(message); >+ } >+ } >+ >+ /** >+ * Parses each param-value and populates configuration variables. If a param >+ * is provided, it overrides the default. >+ * >+ * @param allowedOrigins >+ * A {@link String} of comma separated origins. >+ * @param allowedHttpMethods >+ * A {@link String} of comma separated HTTP methods. >+ * @param allowedHttpHeaders >+ * A {@link String} of comma separated HTTP headers. >+ * @param exposedHeaders >+ * A {@link String} of comma separated headers that needs to be >+ * exposed. >+ * @param supportsCredentials >+ * "true" if support credentials needs to be enabled. >+ * @param preflightMaxAge >+ * The amount of seconds the user agent is allowed to cache the >+ * result of the pre-flight request. >+ * @param loggingEnabled >+ * Flag to control logging to access log. >+ * @throws ServletException >+ */ >+ private void parseAndStore(final String allowedOrigins, >+ final String allowedHttpMethods, final String allowedHttpHeaders, >+ final String exposedHeaders, final String supportsCredentials, >+ final String preflightMaxAge, final String loggingEnabled, >+ final String decorateRequest) throws ServletException { >+ if (allowedOrigins != null) { >+ if (allowedOrigins.trim().equals("*")) { >+ this.anyOriginAllowed = true; >+ } else { >+ this.anyOriginAllowed = false; >+ Set<String> setAllowedOrigins = parseStringToSet(allowedOrigins); >+ this.allowedOrigins.clear(); >+ this.allowedOrigins.addAll(setAllowedOrigins); >+ } >+ } >+ >+ if (allowedHttpMethods != null) { >+ Set<String> setAllowedHttpMethods = parseStringToSet(allowedHttpMethods); >+ this.allowedHttpMethods.clear(); >+ this.allowedHttpMethods.addAll(setAllowedHttpMethods); >+ } >+ >+ if (allowedHttpHeaders != null) { >+ Set<String> setAllowedHttpHeaders = parseStringToSet(allowedHttpHeaders); >+ Set<String> lowerCaseHeaders = new HashSet<String>(); >+ for (String header : setAllowedHttpHeaders) { >+ String lowerCase = header.toLowerCase(); >+ lowerCaseHeaders.add(lowerCase); >+ } >+ this.allowedHttpHeaders.clear(); >+ this.allowedHttpHeaders.addAll(lowerCaseHeaders); >+ } >+ >+ if (exposedHeaders != null) { >+ Set<String> setExposedHeaders = parseStringToSet(exposedHeaders); >+ this.exposedHeaders.clear(); >+ this.exposedHeaders.addAll(setExposedHeaders); >+ } >+ >+ if (supportsCredentials != null) { >+ // For any value other then 'true' this will be false. >+ this.supportsCredentials = Boolean >+ .parseBoolean(supportsCredentials); >+ } >+ >+ if (preflightMaxAge != null) { >+ try { >+ if (!preflightMaxAge.isEmpty()) { >+ this.preflightMaxAge = Long.parseLong(preflightMaxAge); >+ } else { >+ this.preflightMaxAge = 0L; >+ } >+ } catch (NumberFormatException e) { >+ throw new ServletException("Unable to parse preflightMaxAge", e); >+ } >+ } >+ >+ if (loggingEnabled != null) { >+ // For any value other then 'true' this will be false. >+ this.loggingEnabled = Boolean.parseBoolean(loggingEnabled); >+ } >+ >+ if (decorateRequest != null) { >+ // For any value other then 'true' this will be false. >+ this.decorateRequest = Boolean.parseBoolean(decorateRequest); >+ } >+ } >+ >+ /** >+ * Takes a comma separated list and returns a Set<String>. >+ * >+ * @param data >+ * A comma separated list of strings. >+ * @return Set<String> >+ */ >+ private Set<String> parseStringToSet(final String data) { >+ String[] splits; >+ >+ if (data != null && data.length() > 0) { >+ splits = data.split(","); >+ } else { >+ splits = new String[] {}; >+ } >+ >+ Set<String> set = new HashSet<String>(); >+ if (splits.length > 0) { >+ for (String split : splits) { >+ set.add(split.trim()); >+ } >+ } >+ >+ return set; >+ } >+ >+ /** >+ * Checks if a given origin is valid or not. Criteria: >+ * <ul> >+ * <li>If an encoded character is present in origin, it's not valid.</li> >+ * <li>Origin should be a valid {@link URI}</li> >+ * </ul> >+ * >+ * @param origin >+ * @see <a href="http://tools.ietf.org/html/rfc952">RFC952</a> >+ * @return true, if origin is a valid URI; false, if either it contains CRLF >+ * characters, or is not a valid URI or is missing scheme. >+ */ >+ public static boolean isValidOrigin(String origin) { >+ // Checks for encoded characters. Helps prevent CRLF injection. >+ if (origin.contains("%")) { >+ return false; >+ } >+ >+ URI originURI; >+ >+ try { >+ originURI = new URI(origin); >+ } catch (URISyntaxException e) { >+ return false; >+ } >+ // If scheme for URI is null, return false. Return true otherwise. >+ return originURI.getScheme() != null; >+ >+ } >+ >+ // -------------------------------------------------------------- Accessors >+ /** >+ * Determines if logging is enabled or not. >+ * >+ * @return <code>true</code> if it's enabled; false otherwise. >+ */ >+ public boolean isLoggingEnabled() { >+ return loggingEnabled; >+ } >+ >+ /** >+ * Determines if any origin is allowed to make CORS request. >+ * >+ * @return <code>true</code> if it's enabled; false otherwise. >+ */ >+ public boolean isAnyOriginAllowed() { >+ return anyOriginAllowed; >+ } >+ >+ /** >+ * Returns a {@link Collection} of headers that should be exposed by >+ * browser. >+ * >+ * @return A {@link Collection} of {@link String}. >+ */ >+ public Collection<String> getExposedHeaders() { >+ return exposedHeaders; >+ } >+ >+ /** >+ * Determines is supports credentials is enabled >+ * >+ * @return <code>true</code> if supported, <code>false</code> otherwise. >+ */ >+ public boolean isSupportsCredentials() { >+ return supportsCredentials; >+ } >+ >+ /** >+ * Returns the preflight response cache time in seconds. >+ * >+ * @return Time to cache in seconds. >+ */ >+ public long getPreflightMaxAge() { >+ return preflightMaxAge; >+ } >+ >+ /** >+ * Returns the {@link Set} of allowed origins that are allowed to make >+ * requests. >+ * >+ * @return {@link Set} of {@link String} >+ */ >+ public Collection<String> getAllowedOrigins() { >+ return allowedOrigins; >+ } >+ >+ /** >+ * Returns a {@link Set} of HTTP methods that are allowed to make requests. >+ * >+ * @return {@link Set} of {@link String} >+ */ >+ public Collection<String> getAllowedHttpMethods() { >+ return allowedHttpMethods; >+ } >+ >+ /** >+ * Returns a {@link Set} of headers support by resource. >+ * >+ * @return {@link Set} of {@link String} >+ */ >+ public Collection<String> getAllowedHttpHeaders() { >+ return allowedHttpHeaders; >+ } >+ >+ // -------------------------------------------------- CORS Response Headers >+ /** >+ * The Access-Control-Allow-Origin header indicates whether a resource can >+ * be shared based by returning the value of the Origin request header in >+ * the response. >+ */ >+ public static final String RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin"; >+ >+ /** >+ * The Access-Control-Allow-Credentials header indicates whether the >+ * response to request can be exposed when the omit credentials flag is >+ * unset. When part of the response to a preflight request it indicates that >+ * the actual request can include user credentials. >+ */ >+ public static final String RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials"; >+ >+ /** >+ * The Access-Control-Expose-Headers header indicates which headers are safe >+ * to expose to the API of a CORS API specification >+ */ >+ public static final String RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers"; >+ >+ /** >+ * The Access-Control-Max-Age header indicates how long the results of a >+ * preflight request can be cached in a preflight result cache. >+ */ >+ public static final String RESPONSE_HEADER_ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age"; >+ >+ /** >+ * The Access-Control-Allow-Methods header indicates, as part of the >+ * response to a preflight request, which methods can be used during the >+ * actual request. >+ */ >+ public static final String RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods"; >+ >+ /** >+ * The Access-Control-Allow-Headers header indicates, as part of the >+ * response to a preflight request, which header field names can be used >+ * during the actual request. >+ */ >+ public static final String RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers"; >+ >+ // -------------------------------------------------- CORS Request Headers >+ /** >+ * The Origin header indicates where the cross-origin request or preflight >+ * request originates from. >+ */ >+ public static final String REQUEST_HEADER_ORIGIN = "Origin"; >+ >+ /** >+ * The Access-Control-Request-Method header indicates which method will be >+ * used in the actual request as part of the preflight request. >+ */ >+ public static final String REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method"; >+ >+ /** >+ * The Access-Control-Request-Headers header indicates which headers will be >+ * used in the actual request as part of the preflight request. >+ */ >+ public static final String REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers"; >+ >+ // ----------------------------------------------------- Request attributes >+ /** >+ * The prefix to a CORS request attribute. >+ */ >+ public static final String HTTP_REQUEST_ATTRIBUTE_PREFIX = "cors."; >+ >+ /** >+ * Attribute that contains the origin of the request. >+ */ >+ public static final String HTTP_REQUEST_ATTRIBUTE_ORIGIN = HTTP_REQUEST_ATTRIBUTE_PREFIX >+ + "request.origin"; >+ >+ /** >+ * Boolean value, suggesting if the request is a CORS request or not. >+ */ >+ public static final String HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST = HTTP_REQUEST_ATTRIBUTE_PREFIX >+ + "isCorsRequest"; >+ >+ /** >+ * Type of CORS request, of type {@link CORSRequestType}. >+ */ >+ public static final String HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE = HTTP_REQUEST_ATTRIBUTE_PREFIX >+ + "request.type"; >+ >+ /** >+ * Request headers sent as 'Access-Control-Request-Headers' header, for >+ * pre-flight request. >+ */ >+ public static final String HTTP_REQUEST_ATTRIBUTE_REQUEST_HEADERS = HTTP_REQUEST_ATTRIBUTE_PREFIX >+ + "request.headers"; >+ >+ // -------------------------------------------------------------- Constants >+ /** >+ * Enumerates varies types of CORS requests. Also, provides utility methods >+ * to determine the request type. >+ */ >+ public static enum CORSRequestType { >+ /** >+ * A simple HTTP request, i.e. it shouldn't be pre-flighted. >+ */ >+ SIMPLE, >+ /** >+ * A HTTP request that needs to be pre-flighted. >+ */ >+ ACTUAL, >+ /** >+ * A pre-flight CORS request, to get meta information, before a >+ * non-simple HTTP request is sent. >+ */ >+ PRE_FLIGHT, >+ /** >+ * Not a CORS request, but a normal request. >+ */ >+ NOT_CORS, >+ /** >+ * An invalid CORS request, i.e. it qualifies to be a CORS request, but >+ * fails to be a valid one. >+ */ >+ INVALID_CORS >+ } >+ >+ /** >+ * {@link Collection} of HTTP methods. Case sensitive. >+ * >+ * @see <a >+ * href="http://tools.ietf.org/html/rfc2616#section-5.1.1">RFC2616</a> >+ */ >+ public static final Collection<String> HTTP_METHODS = new HashSet<String>( >+ Arrays.asList("OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", >+ "TRACE", "CONNECT")); >+ /** >+ * {@link Collection} of non-simple HTTP methods. Case sensitive. >+ */ >+ public static final Collection<String> COMPLEX_HTTP_METHODS = new HashSet<String>( >+ Arrays.asList("PUT", "DELETE", "TRACE", "CONNECT")); >+ /** >+ * {@link Collection} of Simple HTTP methods. Case sensitive. >+ * >+ * @see <a href="http://www.w3.org/TR/cors/#terminology">Terminology</a> >+ */ >+ public static final Collection<String> SIMPLE_HTTP_METHODS = new HashSet<String>( >+ Arrays.asList("GET", "POST", "HEAD")); >+ >+ /** >+ * {@link Collection} of Simple HTTP request headers. Case in-sensitive. >+ * >+ * @see <a href="http://www.w3.org/TR/cors/#terminology">Terminology</a> >+ */ >+ public static final Collection<String> SIMPLE_HTTP_REQUEST_HEADERS = new HashSet<String>( >+ Arrays.asList("Accept", "Accept-Language", "Content-Language")); >+ >+ /** >+ * {@link Collection} of Simple HTTP request headers. Case in-sensitive. >+ * >+ * @see <a href="http://www.w3.org/TR/cors/#terminology">Terminology</a> >+ */ >+ public static final Collection<String> SIMPLE_HTTP_RESPONSE_HEADERS = new HashSet<String>( >+ Arrays.asList("Cache-Control", "Content-Language", "Content-Type", >+ "Expires", "Last-Modified", "Pragma")); >+ >+ /** >+ * {@link Collection} of Simple HTTP request headers. Case in-sensitive. >+ * >+ * @see <a href="http://www.w3.org/TR/cors/#terminology">Terminology</a> >+ */ >+ public static final Collection<String> SIMPLE_HTTP_REQUEST_CONTENT_TYPE_VALUES = new HashSet<String>( >+ Arrays.asList("application/x-www-form-urlencoded", >+ "multipart/form-data", "text/plain")); >+ >+ // ------------------------------------------------ Configuration Defaults >+ /** >+ * By default, all origins are allowed to make requests. >+ */ >+ public static final String DEFAULT_ALLOWED_ORIGINS = "*"; >+ >+ /** >+ * By default, following methods are supported: GET, POST, HEAD and OPTIONS. >+ */ >+ public static final String DEFAULT_ALLOWED_HTTP_METHODS = "GET,POST,HEAD,OPTIONS"; >+ >+ /** >+ * By default, time duration to cache pre-flight response is 30 mins. >+ */ >+ public static final String DEFAULT_PREFLIGHT_MAXAGE = "1800"; >+ >+ /** >+ * By default, support credentials is turned on. >+ */ >+ public static final String DEFAULT_SUPPORTS_CREDENTIALS = "true"; >+ >+ /** >+ * By default, following headers are supported: >+ * Origin,Accept,X-Requested-With, Content-Type, >+ * Access-Control-Request-Method, and Access-Control-Request-Headers. >+ */ >+ public static final String DEFAULT_ALLOWED_HTTP_HEADERS = "Origin,Accept,X-Requested-With,Content-Type," >+ + "Access-Control-Request-Method,Access-Control-Request-Headers"; >+ >+ /** >+ * By default, none of the headers are exposed in response. >+ */ >+ public static final String DEFAULT_EXPOSED_HEADERS = ""; >+ >+ /** >+ * By default, access log logging is turned off >+ */ >+ public static final String DEFAULT_LOGGING_ENABLED = "false"; >+ >+ /** >+ * By default, request is decorated with CORS attributes. >+ */ >+ public static final String DEFAULT_DECORATE_REQUEST = "true"; >+ >+ // ----------------------------------------Filter Config Init param-name(s) >+ /** >+ * Key to retrieve allowed origins from {@link FilterConfig}. >+ */ >+ public static final String PARAM_CORS_ALLOWED_ORIGINS = "cors.allowed.origins"; >+ >+ /** >+ * Key to retrieve support credentials from {@link FilterConfig}. >+ */ >+ public static final String PARAM_CORS_SUPPORT_CREDENTIALS = "cors.support.credentials"; >+ >+ /** >+ * Key to retrieve exposed headers from {@link FilterConfig}. >+ */ >+ public static final String PARAM_CORS_EXPOSED_HEADERS = "cors.exposed.headers"; >+ >+ /** >+ * Key to retrieve allowed headers from {@link FilterConfig}. >+ */ >+ public static final String PARAM_CORS_ALLOWED_HEADERS = "cors.allowed.headers"; >+ >+ /** >+ * Key to retrieve allowed methods from {@link FilterConfig}. >+ */ >+ public static final String PARAM_CORS_ALLOWED_METHODS = "cors.allowed.methods"; >+ >+ /** >+ * Key to retrieve preflight max age from {@link FilterConfig}. >+ */ >+ public static final String PARAM_CORS_PREFLIGHT_MAXAGE = "cors.preflight.maxage"; >+ >+ /** >+ * Key to retrieve access log logging flag. >+ */ >+ public static final String PARAM_CORS_LOGGING_ENABLED = "cors.logging.enabled"; >+ >+ /** >+ * Key to determine if request should be decorated. >+ */ >+ public static final String PARAM_CORS_REQUEST_DECORATE = "cors.request.decorate"; >+} >\ No newline at end of file > >Property changes on: java/org/apache/catalina/filters/CORSFilter.java >___________________________________________________________________ >Added: svn:eol-style > + native > >Index: webapps/docs/config/filter.xml >=================================================================== >--- webapps/docs/config/filter.xml (revision 1488477) >+++ webapps/docs/config/filter.xml (working copy) >@@ -1309,6 +1309,118 @@ > > </section> > >+<section name="CORS Filter"> >+ <subsection name="Introduction"> >+ <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> >+ <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> >+ <p>The minimal configuration required to use this filter is:</p> >+ <source> >+<filter> >+ <filter-name>CORSFilter</filter-name> >+ <filter-class>org.apache.catalina.filters.CORSFilter</filter-class> >+</filter> >+<filter-mapping> >+ <filter-name>CORSFilter</filter-name> >+ <url-pattern>/*</url-pattern> >+</filter-mapping> >+ </source> >+ </subsection> >+ <subsection name="Filter Class Name"> >+ <p>The filter class name for the CORS Filter is <strong><code>org.apache.catalina.filters.CORSFilter</code></strong>.</p> >+ </subsection> >+ <subsection name="Initialisation parameters"> >+ <p>The CORS Filter supports following initialisation parameters:</p> >+ <attributes> >+ <attribute name="cors.allowed.origins" required="false"> >+ <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> >+ </attribute> >+ <attribute name="cors.allowed.methods" required="false"> >+ <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> >+ </attribute> >+ <attribute name="cors.allowed.headers" required="false"> >+ <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> >+ </attribute> >+ <attribute name="cors.exposed.headers" required="false"> >+ <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> >+ </attribute> >+ <attribute name="cors.preflight.maxage" required="false"> >+ <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> >+ </attribute> >+ <attribute name="cors.support.credentials" required="false"> >+ <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> >+ </attribute> >+ <attribute name="cors.logging.enabled" required="false"> >+ <p>A flag to control logging to container logs. <strong>Defaults:</strong> <code>false</code></p> >+ </attribute> >+ <attribute name="cors.request.decorate" required="false"> >+ <p>A flag to control if CORS specific attributes should be added to HttpServletRequest object or not. <strong>Defaults:</strong> <code>true</code></p> >+ </attribute> >+ </attributes> >+ <p>Here's an example of a more advanced configuration, that overrides defaults:</p> >+ <source> >+<filter> >+ <filter-name>CORSFilter</filter-name> >+ <filter-class>org.apache.catalina.filters.CORSFilter</filter-class> >+ <init-param> >+ <description>A comma separated list of allowed origins</description> >+ <param-name>cors.allowed.origins</param-name> >+ <param-value>*</param-value> >+ </init-param> >+ <init-param> >+ <description>A comma separated list of HTTP verbs, using which a cross-origin request can be made.</description> >+ <param-name>cors.allowed.methods</param-name> >+ <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value> >+ </init-param> >+ <init-param> >+ <description>A comma separated list of allowed headers when making a non simple cross-origin request.</description> >+ <param-name>cors.allowed.headers</param-name> >+ <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value> >+ </init-param> >+ <init-param> >+ <description>A comma separated list of non-standard response headers that will be exposed to XMLHttpRequest object.</description> >+ <param-name>cors.exposed.headers</param-name> >+ <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value> >+ </init-param> >+ <init-param> >+ <description>A flag that suggests if a cross-origin request is supported with cookies, BASIC Authentication, etc.</description> >+ <param-name>cors.support.credentials</param-name> >+ <param-value>true</param-value> >+ </init-param> >+ <init-param> >+ <description>A flag control logging</description> >+ <param-name>cors.logging.enabled</param-name> >+ <param-value>true</param-value> >+ </init-param> >+ <init-param> >+ <description>Indicates how long (in seconds) the results of a preflight request can be cached in a preflight result cache.</description> >+ <param-name>cors.preflight.maxage</param-name> >+ <param-value>10</param-value> >+ </init-param> >+</filter> >+<filter-mapping> >+ <filter-name>CORS Filter</filter-name> >+ <url-pattern>/*</url-pattern> >+</filter-mapping> >+ </source> >+ </subsection> >+ <subsection name="CORS Filter and HttpServletRequest attributes" required="false"> >+ <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> >+ <ul> >+ <li><strong>cors.isCorsRequest:</strong> Flag to determine if a request is a CORS request.</li> >+ <li><strong>cors.request.origin:</strong> The Origin URL, i.e. the URL of the page from where the request is originated.</li> >+ <li><strong>cors.request.type:</strong> Type of CORS request. Possible values: >+ <ul> >+ <li><code>SIMPLE</code>: A request which is not preceded by a pre-flight request.</li> >+ <li><code>ACTUAL</code>: A request which is preceded by a pre-flight request.</li> >+ <li><code>PRE_FLIGHT</code>: A pre-flight request.</li> >+ <li><code>NOT_CORS</code>: A normal same-origin request.</li> >+ <li><code>INVALID_CORS</code>: A cross-origin request, which is invalid.</li> >+ </ul> >+ </li> >+ <li><strong>cors.request.headers:</strong> Request headers sent as 'Access-Control-Request-Headers' header, for a pre-flight request.</li> >+ </ul> >+ </subsection> >+</section> > > </body> >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 55046
:
30363
|
30364
| 30384 |
30395