/**
   * Determines the request type.
   *
   * @param request
   * @return
   */
  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;
  }
  /**
   * 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.
  }