All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.muserver.rest.CORSConfig Maven / Gradle / Ivy

There is a newer version: 2.0.3
Show newest version
package io.muserver.rest;

import io.muserver.*;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toSet;

/**
 * CORS configuration for REST resources. Create this using {@link CORSConfigBuilder#corsConfig()}
 */
public class CORSConfig {

    public final boolean allowCredentials;
    public final Collection allowedOrigins;
    public final List allowedOriginRegex;
    public final Collection exposedHeaders;
    public final long maxAge;
    public final Collection allowedHeaders;
    private final String exposedHeadersCSV;
    private final String allowedHeadersCSV;

    CORSConfig(boolean allowCredentials, Collection allowedOrigins, List allowedOriginRegex, Collection allowedHeaders, Collection exposedHeaders, long maxAge) {
        Mutils.notNull("allowedOriginRegex", allowedOriginRegex);
        this.allowCredentials = allowCredentials;
        this.allowedOrigins = allowedOrigins == null ? null : Collections.unmodifiableCollection(allowedOrigins);
        this.allowedOriginRegex = allowedOriginRegex;
        this.maxAge = maxAge;
        this.allowedHeaders = allowedHeaders;
        this.allowedHeadersCSV = String.join(", ", allowedHeaders);
        this.exposedHeaders = Collections.unmodifiableCollection(exposedHeaders);
        this.exposedHeadersCSV = String.join(", ", exposedHeaders);
    }

    /**
     * Adds CORS headers to the response, if neeeded.
     *
     * @param request        The request
     * @param response       The response to add headers to
     * @param allowedMethods The methods
     * @return Returns true if any Access Control headers were added; otherwise false. (Note: the Vary: origin header is always added.
     */
    public boolean writeHeaders(MuRequest request, MuResponse response, Set allowedMethods) {
        boolean written = writeHeadersInternal(request, response, null);
        if (written) {
            response.headers().set(HeaderNames.ACCESS_CONTROL_ALLOW_METHODS, getAllowedString(allowedMethods));
        }
        return written;
    }

    boolean writeHeadersInternal(MuRequest request, MuResponse response, Set matchedMethodsForPath) {

        response.headers().add(HeaderNames.VARY, HeaderNames.ORIGIN);

        String origin = request.headers().get(HeaderNames.ORIGIN);
        if (Mutils.nullOrEmpty(origin)) {
            return false;
        }

        Headers respHeaders = response.headers();
        if (allowCors(origin)) {
            respHeaders.set(HeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN, origin);
            if (matchedMethodsForPath != null) {
                String allowed = getAllowedMethods(matchedMethodsForPath);
                respHeaders.set(HeaderNames.ACCESS_CONTROL_ALLOW_METHODS, allowed);
            }
            if (request.method() == Method.OPTIONS) {
                respHeaders.set(HeaderNames.ACCESS_CONTROL_MAX_AGE, maxAge);
                if (!allowedHeaders.isEmpty()) {
                    respHeaders.set(HeaderNames.ACCESS_CONTROL_ALLOW_HEADERS, allowedHeadersCSV);
                }
            }
            if (!exposedHeaders.isEmpty()) {
                respHeaders.set(HeaderNames.ACCESS_CONTROL_EXPOSE_HEADERS, exposedHeadersCSV);
            }
            if (allowCredentials) {
                respHeaders.set(HeaderNames.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
            }
            return true;
        } else {
            respHeaders.set(HeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN, "null");
            return false;
        }

    }

    static String getAllowedMethods(Set matchedMethodsForPath) {
        Set methods = matchedMethodsForPath.stream().map(m -> m.resourceMethod.httpMethod).collect(toSet());
        return getAllowedString(methods);
    }


    static String getAllowedString(Set allowed) {
        if (allowed.contains(Method.GET)) {
            allowed.add(Method.HEAD);
        }
        allowed.add(Method.OPTIONS);
        return allowed.stream().map(Enum::name).sorted().collect(joining(", "));
    }

    boolean allowCors(String origin) {
        if (allowedOrigins == null || allowedOrigins.contains(origin)) {
            return true;
        }
        for (Pattern pattern : allowedOriginRegex) {
            if (pattern.matcher(origin).matches()) {
                return true;
            }
        }
        return false;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy