Package org.stagemonitor.web.monitor.filter

Source Code of org.stagemonitor.web.monitor.filter.HttpRequestMonitorFilter

package org.stagemonitor.web.monitor.filter;

import com.codahale.metrics.MetricRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.stagemonitor.core.CorePlugin;
import org.stagemonitor.core.MeasurementSession;
import org.stagemonitor.core.Stagemonitor;
import org.stagemonitor.core.configuration.Configuration;
import org.stagemonitor.requestmonitor.RequestMonitor;
import org.stagemonitor.web.WebPlugin;
import org.stagemonitor.web.monitor.DefaultMonitoredHttpRequestFactory;
import org.stagemonitor.web.monitor.HttpRequestTrace;
import org.stagemonitor.web.monitor.MonitoredHttpRequest;
import org.stagemonitor.web.monitor.MonitoredHttpRequestFactory;
import org.stagemonitor.web.monitor.rum.BommerangJsHtmlInjector;
import org.stagemonitor.web.monitor.widget.StagemonitorWidgetHtmlInjector;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;

import static javax.servlet.DispatcherType.FORWARD;
import static javax.servlet.DispatcherType.REQUEST;

@WebFilter(urlPatterns = "/*", asyncSupported = true, dispatcherTypes = {REQUEST, FORWARD})
public class HttpRequestMonitorFilter extends AbstractExclusionFilter implements Filter {

  private static final Logger logger = LoggerFactory.getLogger(HttpRequestMonitorFilter.class);
  protected final Configuration configuration;
  protected final CorePlugin corePlugin;
  protected final WebPlugin webPlugin;
  protected final RequestMonitor requestMonitor;
  protected final MetricRegistry metricRegistry;
  private final List<HtmlInjector> htmlInjectors = new ArrayList<HtmlInjector>();
  private boolean atLeastServletApi3 = false;
  private final MonitoredHttpRequestFactory monitoredHttpRequestFactory;

  public HttpRequestMonitorFilter() {
    this(Stagemonitor.getConfiguration(), new RequestMonitor(Stagemonitor.getConfiguration()), Stagemonitor.getMetricRegistry());
  }

  public HttpRequestMonitorFilter(Configuration configuration, RequestMonitor requestMonitor, MetricRegistry metricRegistry) {
    super(configuration.getConfig(WebPlugin.class).getExcludedRequestPaths());
    this.configuration = configuration;
    this.webPlugin = configuration.getConfig(WebPlugin.class);
    this.corePlugin = configuration.getConfig(CorePlugin.class);
    this.requestMonitor = requestMonitor;
    this.metricRegistry = metricRegistry;

    final Iterator<MonitoredHttpRequestFactory> requestFactoryIterator = ServiceLoader.load(MonitoredHttpRequestFactory.class).iterator();
    if (!requestFactoryIterator.hasNext()) {
      this.monitoredHttpRequestFactory = new DefaultMonitoredHttpRequestFactory();
    } else {
      this.monitoredHttpRequestFactory = requestFactoryIterator.next();
    }
  }

  @Override
  public void initInternal(FilterConfig filterConfig) throws ServletException {
    final MeasurementSession measurementSession = new MeasurementSession(getApplicationName(filterConfig),
        RequestMonitor.getHostName(), corePlugin.getInstanceName());
    requestMonitor.setMeasurementSession(measurementSession);
    final ServletContext servletContext = filterConfig.getServletContext();
    atLeastServletApi3 = servletContext.getMajorVersion() >= 3;

    htmlInjectors.add(new BommerangJsHtmlInjector(webPlugin, servletContext.getContextPath()));
    htmlInjectors.add(new StagemonitorWidgetHtmlInjector(configuration, webPlugin, servletContext.getContextPath()));
  }

  private String getApplicationName(FilterConfig filterConfig) {
    String name = corePlugin.getApplicationName();
    if (name == null || name.isEmpty()) {
      name = filterConfig.getServletContext().getServletContextName();
    }
    return name;
  }

  @Override
  public final void doFilterInternal(final ServletRequest request, final ServletResponse response, final FilterChain filterChain)
      throws IOException, ServletException {
    setCachingHeadersForBommerangJs(request, response);
    if (corePlugin.isStagemonitorActive() && isHttpRequest(request, response) &&
        !isInternalRequest((HttpServletRequest) request) && onlyMonitorForwardedRequestsIfConfigured(request)) {
      doMonitor((HttpServletRequest) request, response, filterChain);
    } else {
      filterChain.doFilter(request, response);
    }
  }

  private boolean onlyMonitorForwardedRequestsIfConfigured(ServletRequest request) {
    return request.getDispatcherType() != FORWARD || webPlugin.isMonitorOnlyForwardedRequests();
  }

  private boolean isHttpRequest(ServletRequest request, ServletResponse response) {
    return request instanceof HttpServletRequest && response instanceof HttpServletResponse;
  }

  private void doMonitor(HttpServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {

    final StatusExposingByteCountingServletResponse responseWrapper;
    HttpServletResponseBufferWrapper httpServletResponseBufferWrapper = null;
    if (isInjectContentToHtml(request)) {
      httpServletResponseBufferWrapper = new HttpServletResponseBufferWrapper((HttpServletResponse) response);
      responseWrapper = new StatusExposingByteCountingServletResponse(httpServletResponseBufferWrapper);
    } else {
      responseWrapper = new StatusExposingByteCountingServletResponse((HttpServletResponse) response);
    }

    try {
      final RequestMonitor.RequestInformation<HttpRequestTrace> requestInformation = monitorRequest(filterChain, request, responseWrapper);
      if (isInjectContentToHtml(request)) {
        injectHtml(response, request, httpServletResponseBufferWrapper, requestInformation);
      }
    } catch (Exception e) {
      handleException(e);
    }
  }

  private void setCachingHeadersForBommerangJs(ServletRequest request, ServletResponse response) {
    if (isHttpRequest(request, response)) {
      final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
      if (httpServletRequest.getRequestURI().endsWith(BommerangJsHtmlInjector.BOOMERANG_FILENAME)) {
        final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("cache-control", "public, max-age=315360000");
      }
    }
  }

  private boolean isInjectContentToHtml(HttpServletRequest httpServletRequest) {
    return atLeastServletApi3 && isHtmlRequested(httpServletRequest) && isAtLeastOneHtmlInjectorActive() ;
  }

  private boolean isAtLeastOneHtmlInjectorActive() {
    for (HtmlInjector htmlInjector : htmlInjectors) {
      if (htmlInjector.isActive()) {
        return true;
      }
    }
    return false;
  }

  private boolean isHtmlRequested(HttpServletRequest httpServletRequest) {
    final String accept = httpServletRequest.getHeader("accept");
    return accept != null && accept.contains("text/html");
  }

  private boolean isInternalRequest(HttpServletRequest request) {
    return request.getRequestURI().startsWith(request.getContextPath() + "/stagemonitor");
  }

  protected RequestMonitor.RequestInformation<HttpRequestTrace> monitorRequest(FilterChain filterChain, HttpServletRequest httpServletRequest, StatusExposingByteCountingServletResponse responseWrapper) throws Exception {
    final MonitoredHttpRequest monitoredRequest = monitoredHttpRequestFactory.createMonitoredHttpRequest(httpServletRequest, responseWrapper, filterChain, configuration);
    return requestMonitor.monitor(monitoredRequest);
  }

  protected void injectHtml(ServletResponse response, HttpServletRequest httpServletRequest,
                HttpServletResponseBufferWrapper httpServletResponseBufferWrapper,
                RequestMonitor.RequestInformation<HttpRequestTrace> requestInformation) throws IOException {
    if (httpServletResponseBufferWrapper.getContentType() != null
        && httpServletResponseBufferWrapper.getContentType().contains("text/html")
        && httpServletRequest.getAttribute("stagemonitorInjected") == null
        && httpServletResponseBufferWrapper.isUsingWriter()) {
      httpServletRequest.setAttribute("stagemonitorInjected", true);
      String content = httpServletResponseBufferWrapper.getWriter().getOutput().toString();
      for (HtmlInjector htmlInjector : htmlInjectors) {
        if (htmlInjector.isActive()) {
          content = injectBeforeClosingBody(content, htmlInjector.build(requestInformation));
        }
      }
      response.getWriter().write(content);
    } else {
      passthrough(response, httpServletResponseBufferWrapper);
    }
  }

  private void passthrough(ServletResponse response, HttpServletResponseBufferWrapper httpServletResponseBufferWrapper) throws IOException {
    if (httpServletResponseBufferWrapper.isUsingWriter()) {
      response.getWriter().write(httpServletResponseBufferWrapper.getWriter().getOutput().toString());
    } else {
      ByteArrayOutputStream output = httpServletResponseBufferWrapper.getOutputStream().getOutput();
      output.writeTo(response.getOutputStream());
    }
  }

  private String injectBeforeClosingBody(String unmodifiedContent, String contentToInject) {
    final int lastClosingBodyIndex = unmodifiedContent.lastIndexOf("</body>");
    final String modifiedContent;
    if (lastClosingBodyIndex > -1) {
      final StringBuilder modifiedContentStringBuilder = new StringBuilder(unmodifiedContent.length() + contentToInject.length());
      modifiedContentStringBuilder.append(unmodifiedContent.substring(0, lastClosingBodyIndex));
      modifiedContentStringBuilder.append(contentToInject);
      modifiedContentStringBuilder.append(unmodifiedContent.substring(lastClosingBodyIndex));
      modifiedContent = modifiedContentStringBuilder.toString();
    } else {
      // no body close tag found - pass through without injection
      modifiedContent = unmodifiedContent;
    }
    return modifiedContent;
  }

  protected void handleException(Exception e) throws IOException, ServletException {
    if (e instanceof IOException) {
      throw (IOException) e;
    }
    if (e instanceof ServletException) {
      throw (ServletException) e;
    }
    if (e instanceof RuntimeException) {
      throw (RuntimeException) e;
    } else {
      throw new RuntimeException(e);
    }
  }
}
TOP

Related Classes of org.stagemonitor.web.monitor.filter.HttpRequestMonitorFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.