<commit_before>package com.psddev.dari.util;

import java.io.IOException;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.naming.ldap.LdapContext;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Provides debugging tools for troubleshooting the application.
 *
 * <p>This filter loads:</p>
 *
 * <ul>
 * <li>{@link SourceFilter}</li>
 * <li>{@link LogCaptureFilter}</li>
 * <li>{@link ResourceFilter}</li>
 * </ul>
 */
public class DebugFilter extends AbstractFilter {

    public static final String DEFAULT_INTERCEPT_PATH = "/_debug/";
    public static final String INTERCEPT_PATH_SETTING = "dari/debugFilterInterceptPath";

    public static final String DEBUG_PARAMETER = "_debug";
    public static final String PRODUCTION_PARAMETER = "_prod";

    private static final Logger LOGGER = LoggerFactory.getLogger(DebugFilter.class);
    private static final Pattern NO_FILE_PATTERN = Pattern.compile("File &quot;(.*)&quot; not found");

    public static final String WEB_INF_DEBUG = "/WEB-INF/_debug/";

    private final transient Lazy<Map<String, ServletWrapper>> debugServletWrappers = new Lazy<Map<String, ServletWrapper>>() {

        {
            CodeUtils.addRedefineClassesListener(new CodeUtils.RedefineClassesListener() {
                @Override
                public void redefined(Set<Class<?>> classes) {
                    for (Class<?> c : classes) {
                        if (Servlet.class.isAssignableFrom(c)) {
                            reset();
                            break;
                        }
                    }
                }
            });
        }

        @Override
        protected Map<String, ServletWrapper> create() {
            Map<String, ServletWrapper> wrappers = new TreeMap<String, ServletWrapper>();

            for (Class<? extends Servlet> servletClass : ClassFinder.Static.findClasses(Servlet.class)) {
                try {
                    if (Modifier.isAbstract(servletClass.getModifiers())) {
                        continue;
                    }

                    String path = null;
                    Path pathAnnotation = servletClass.getAnnotation(Path.class);

                    if (pathAnnotation != null) {
                        path = pathAnnotation.value();
                    }

                    if (ObjectUtils.isBlank(path)) {
                        Name nameAnnotation = servletClass.getAnnotation(Name.class);

                        if (nameAnnotation != null) {
                            path = nameAnnotation.value();
                        }
                    }

                    if (ObjectUtils.isBlank(path)) {
                        continue;
                    }

                    wrappers.put(path, new ServletWrapper(servletClass));

                } catch (Throwable ex) {
                    LOGGER.warn(String.format(
                            "Can't load debug servlet [%s]!",
                            servletClass.getName()), ex);
                }
            }

            LOGGER.info("Found debug servlets: {}", wrappers.keySet());
            return wrappers;
        }

        @Override
        protected void destroy(Map<String, ServletWrapper> wrappers) {
            for (ServletWrapper wrapper : wrappers.values()) {
                wrapper.destroyServlet();
            }
        }
    };

    // --- AbstractFilter support ---

    @Override
    protected Iterable<Class<? extends Filter>> dependencies() {
        List<Class<? extends Filter>> dependencies = new ArrayList<Class<? extends Filter>>();
        dependencies.add(SettingsOverrideFilter.class);
        dependencies.add(SourceFilter.class);
        dependencies.add(LogCaptureFilter.class);
        dependencies.add(ResourceFilter.class);
        return dependencies;
    }

    @Override
    protected void doDestroy() {
        debugServletWrappers.reset();
    }

    @Override
    protected void doDispatch(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain chain)
            throws Exception {

        if (Settings.isProduction()) {
            super.doDispatch(request, response, chain);

        } else {
            try {
                super.doDispatch(request, response, chain);

            } catch (Throwable error) {
                Static.writeError(request, response, error);
            }
        }
    }

    private static String getInterceptPath() {<commit_msg>Changes DebugFilter error reporting logic to better handle form post so that the errors aren't hidden from the user in most cases.<commit_after>package com.psddev.dari.util;

import java.io.IOException;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.naming.ldap.LdapContext;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Provides debugging tools for troubleshooting the application.
 *
 * <p>This filter loads:</p>
 *
 * <ul>
 * <li>{@link SourceFilter}</li>
 * <li>{@link LogCaptureFilter}</li>
 * <li>{@link ResourceFilter}</li>
 * </ul>
 */
public class DebugFilter extends AbstractFilter {

    public static final String DEFAULT_INTERCEPT_PATH = "/_debug/";
    public static final String INTERCEPT_PATH_SETTING = "dari/debugFilterInterceptPath";

    public static final String DEBUG_PARAMETER = "_debug";
    public static final String PRODUCTION_PARAMETER = "_prod";

    private static final Logger LOGGER = LoggerFactory.getLogger(DebugFilter.class);
    private static final Pattern NO_FILE_PATTERN = Pattern.compile("File &quot;(.*)&quot; not found");

    public static final String WEB_INF_DEBUG = "/WEB-INF/_debug/";

    private final transient Lazy<Map<String, ServletWrapper>> debugServletWrappers = new Lazy<Map<String, ServletWrapper>>() {

        {
            CodeUtils.addRedefineClassesListener(new CodeUtils.RedefineClassesListener() {
                @Override
                public void redefined(Set<Class<?>> classes) {
                    for (Class<?> c : classes) {
                        if (Servlet.class.isAssignableFrom(c)) {
                            reset();
                            break;
                        }
                    }
                }
            });
        }

        @Override
        protected Map<String, ServletWrapper> create() {
            Map<String, ServletWrapper> wrappers = new TreeMap<String, ServletWrapper>();

            for (Class<? extends Servlet> servletClass : ClassFinder.Static.findClasses(Servlet.class)) {
                try {
                    if (Modifier.isAbstract(servletClass.getModifiers())) {
                        continue;
                    }

                    String path = null;
                    Path pathAnnotation = servletClass.getAnnotation(Path.class);

                    if (pathAnnotation != null) {
                        path = pathAnnotation.value();
                    }

                    if (ObjectUtils.isBlank(path)) {
                        Name nameAnnotation = servletClass.getAnnotation(Name.class);

                        if (nameAnnotation != null) {
                            path = nameAnnotation.value();
                        }
                    }

                    if (ObjectUtils.isBlank(path)) {
                        continue;
                    }

                    wrappers.put(path, new ServletWrapper(servletClass));

                } catch (Throwable ex) {
                    LOGGER.warn(String.format(
                            "Can't load debug servlet [%s]!",
                            servletClass.getName()), ex);
                }
            }

            LOGGER.info("Found debug servlets: {}", wrappers.keySet());
            return wrappers;
        }

        @Override
        protected void destroy(Map<String, ServletWrapper> wrappers) {
            for (ServletWrapper wrapper : wrappers.values()) {
                wrapper.destroyServlet();
            }
        }
    };

    // --- AbstractFilter support ---

    @Override
    protected Iterable<Class<? extends Filter>> dependencies() {
        List<Class<? extends Filter>> dependencies = new ArrayList<Class<? extends Filter>>();
        dependencies.add(SettingsOverrideFilter.class);
        dependencies.add(SourceFilter.class);
        dependencies.add(LogCaptureFilter.class);
        dependencies.add(ResourceFilter.class);
        return dependencies;
    }

    @Override
    protected void doDestroy() {
        debugServletWrappers.reset();
    }

    @Override
    protected void doDispatch(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain chain)
            throws Exception {

        if (Settings.isProduction()) {
            super.doDispatch(request, response, chain);

        } else {
            CapturingHttpServletResponse capturing = new CapturingHttpServletResponse(response);

            try {
                super.doDispatch(request, capturing, chain);
                response.getWriter().write(capturing.getOutput());

            } catch (Exception error) {

                // If the request is a form post and there's been no output
                // so far, it's probably form processing code. Custom error
                // message here will most likely not reach the user, so don't
                // substitute.
                if (JspUtils.isFormPost(request) &&
                        ObjectUtils.isBlank(capturing.getOutput())) {
                    throw error;

                // Discard the output so far and write the error instead.
                } else {
                    Static.writeError(request, response, error);
                }
            }
        }
    }

    private static String getInterceptPath() {
{'fraction_non_alphanumeric': 0.09650180940892641, 'fraction_numerical': 0.0003446493193175943, 'mean_word_length': 2.0560821484992102, 'pattern_counts': {'":': 0, '<': 59, '<?xml version=': 0, '>': 59, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4438314', 'n_tokens_mistral': 2937, 'n_tokens_neox': 2824, 'n_words': 657}