@Test
  public void testResponseFilterOriginalRequestNotModified() throws IOException {
    mockServer
        .when(request().withMethod("GET").withPath("/modifiedendpoint"), Times.exactly(1))
        .respond(response().withStatusCode(200).withBody("success"));

    proxy = new BrowserMobProxyServer();
    proxy.start();

    proxy.addRequestFilter(
        new RequestFilter() {
          @Override
          public HttpResponse filterRequest(
              HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) {
            if (request.getUri().endsWith("/originalendpoint")) {
              request.setUri(request.getUri().replaceAll("originalendpoint", "modifiedendpoint"));
            }

            return null;
          }
        });

    final AtomicReference<String> originalRequestUri = new AtomicReference<>();

    proxy.addResponseFilter(
        new ResponseFilter() {
          @Override
          public void filterResponse(
              HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) {
            originalRequestUri.set(messageInfo.getOriginalRequest().getUri());
          }
        });

    try (CloseableHttpClient httpClient =
        NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) {
      CloseableHttpResponse response =
          httpClient.execute(
              new HttpGet("http://localhost:" + mockServerPort + "/originalendpoint"));

      assertEquals(
          "Expected server to return a 200", 200, response.getStatusLine().getStatusCode());
      assertThat(
          "Expected URI on originalRequest to match actual URI of original HTTP request",
          originalRequestUri.get(),
          endsWith("/originalendpoint"));
    }
  }
  @Test
  public void testRequestFilterCanModifyHttpsRequestBody() throws IOException {
    final String originalText = "original body";
    final String newText = "modified body";

    mockServer
        .when(
            request().withMethod("PUT").withPath("/modifyrequest").withBody(newText),
            Times.exactly(1))
        .respond(response().withStatusCode(200).withBody("success"));

    proxy = new BrowserMobProxyServer();
    proxy.setTrustAllServers(true);
    proxy.start();

    proxy.addRequestFilter(
        new RequestFilter() {
          @Override
          public HttpResponse filterRequest(
              HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) {
            if (contents.isText()) {
              if (contents.getTextContents().equals(originalText)) {
                contents.setTextContents(newText);
              }
            }

            return null;
          }
        });

    try (CloseableHttpClient httpClient =
        NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) {
      HttpPut request = new HttpPut("https://localhost:" + mockServerPort + "/modifyrequest");
      request.setEntity(new StringEntity(originalText));
      CloseableHttpResponse response = httpClient.execute(request);
      String responseBody =
          NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent());

      assertEquals(
          "Expected server to return a 200", 200, response.getStatusLine().getStatusCode());
      assertEquals("Did not receive expected response from mock server", "success", responseBody);
    }
  }
  @Test
  public void testMitmDisabledHttpsRequestFilterNotAvailable() throws IOException {
    mockServer
        .when(request().withMethod("GET").withPath("/mitmdisabled"), Times.exactly(1))
        .respond(response().withStatusCode(200).withBody("success"));

    proxy = new BrowserMobProxyServer();
    proxy.setMitmDisabled(true);

    proxy.start();

    final AtomicBoolean connectRequestFilterFired = new AtomicBoolean(false);
    final AtomicBoolean getRequestFilterFired = new AtomicBoolean(false);

    proxy.addRequestFilter(
        new RequestFilter() {
          @Override
          public HttpResponse filterRequest(
              HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) {
            if (request.getMethod().equals(HttpMethod.CONNECT)) {
              connectRequestFilterFired.set(true);
            } else if (request.getMethod().equals(HttpMethod.GET)) {
              getRequestFilterFired.set(true);
            }
            return null;
          }
        });

    try (CloseableHttpClient httpClient =
        NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) {
      CloseableHttpResponse response =
          httpClient.execute(new HttpGet("https://localhost:" + mockServerPort + "/mitmdisabled"));

      assertEquals(
          "Expected server to return a 200", 200, response.getStatusLine().getStatusCode());

      assertTrue("Expected request filter to fire on CONNECT", connectRequestFilterFired.get());
      assertFalse(
          "Expected request filter to fail to fire on GET because MITM is disabled",
          getRequestFilterFired.get());
    }
  }
  @Post
  @At("/:port/filter/request")
  public Reply<?> addRequestFilter(@Named("port") int port, Request<String> request)
      throws IOException, ScriptException {
    LegacyProxyServer legacyProxy = proxyManager.get(port);
    if (legacyProxy == null) {
      return Reply.saying().notFound();
    }

    if (!(legacyProxy instanceof BrowserMobProxyServer)) {
      return Reply.saying().badRequest();
    }

    BrowserMobProxy proxy = (BrowserMobProxy) legacyProxy;

    JavascriptRequestResponseFilter requestResponseFilter = new JavascriptRequestResponseFilter();

    String script = getEntityBodyFromRequest(request);
    requestResponseFilter.setRequestFilterScript(script);

    proxy.addRequestFilter(requestResponseFilter);

    return Reply.saying().ok();
  }
  @Test
  public void testHttpsResponseFilterUrlReflectsModifications() throws IOException {
    mockServer
        .when(request().withMethod("GET").withPath("/urlreflectsmodifications"), Times.exactly(1))
        .respond(response().withStatusCode(200).withBody("success"));

    proxy = new BrowserMobProxyServer();
    proxy.setTrustAllServers(true);
    proxy.start();

    final AtomicReference<String> requestFilterOriginalUrl = new AtomicReference<>();
    final AtomicReference<String> requestFilterUrl = new AtomicReference<>();

    proxy.addRequestFilter(
        new RequestFilter() {
          @Override
          public HttpResponse filterRequest(
              HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) {
            requestFilterOriginalUrl.set(messageInfo.getOriginalUrl());
            requestFilterUrl.set(messageInfo.getUrl());
            return null;
          }
        });

    // request filters get added to the beginning of the filter chain, so add this uri-modifying
    // request filter after
    // adding the capturing request filter above.
    proxy.addRequestFilter(
        new RequestFilter() {
          @Override
          public HttpResponse filterRequest(
              HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) {
            if (request.getUri().endsWith("/originalurl")) {
              String newUrl =
                  request.getUri().replaceAll("originalurl", "urlreflectsmodifications");
              request.setUri(newUrl);
            }
            return null;
          }
        });

    final AtomicReference<String> responseFilterOriginalUrl = new AtomicReference<>();
    final AtomicReference<String> responseFilterUrl = new AtomicReference<>();

    proxy.addResponseFilter(
        new ResponseFilter() {
          @Override
          public void filterResponse(
              HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) {
            responseFilterOriginalUrl.set(messageInfo.getOriginalUrl());
            responseFilterUrl.set(messageInfo.getUrl());
          }
        });

    try (CloseableHttpClient httpClient =
        NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) {
      String originalRequestUrl = "https://localhost:" + mockServerPort + "/originalurl";
      String modifiedRequestUrl =
          "https://localhost:" + mockServerPort + "/urlreflectsmodifications";
      CloseableHttpResponse response = httpClient.execute(new HttpGet(originalRequestUrl));

      assertEquals(
          "Expected server to return a 200", 200, response.getStatusLine().getStatusCode());
      assertEquals(
          "Expected originalUrl in request filter to match actual request URL",
          originalRequestUrl,
          requestFilterOriginalUrl.get());
      assertEquals(
          "Expected url in request filter to match modified request URL",
          modifiedRequestUrl,
          requestFilterUrl.get());

      assertEquals(
          "Expected originalUrl in response filter to match actual request URL",
          originalRequestUrl,
          responseFilterOriginalUrl.get());
      assertEquals(
          "Expected url in response filter to match modified request URL",
          modifiedRequestUrl,
          responseFilterUrl.get());
    }
  }
  @Test
  public void testHttpResponseFilterMessageInfoPopulated() throws IOException {
    mockServer
        .when(
            request()
                .withMethod("GET")
                .withPath("/httpmessageinfopopulated")
                .withQueryStringParameter("param1", "value1"),
            Times.exactly(1))
        .respond(response().withStatusCode(200).withBody("success"));

    proxy = new BrowserMobProxyServer();
    proxy.start();

    final AtomicReference<ChannelHandlerContext> requestCtx = new AtomicReference<>();
    final AtomicReference<HttpRequest> requestOriginalRequest = new AtomicReference<>();
    final AtomicBoolean requestIsHttps = new AtomicBoolean(false);
    final AtomicReference<String> requestFilterOriginalUrl = new AtomicReference<>();
    final AtomicReference<String> requestFilterUrl = new AtomicReference<>();

    proxy.addRequestFilter(
        new RequestFilter() {
          @Override
          public HttpResponse filterRequest(
              HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) {
            requestCtx.set(messageInfo.getChannelHandlerContext());
            requestOriginalRequest.set(messageInfo.getOriginalRequest());
            requestIsHttps.set(messageInfo.isHttps());
            requestFilterOriginalUrl.set(messageInfo.getOriginalUrl());
            requestFilterUrl.set(messageInfo.getUrl());
            return null;
          }
        });

    final AtomicReference<ChannelHandlerContext> responseCtx = new AtomicReference<>();
    final AtomicReference<HttpRequest> responseOriginalRequest = new AtomicReference<>();
    final AtomicBoolean responseIsHttps = new AtomicBoolean(false);
    final AtomicReference<String> responseFilterOriginalUrl = new AtomicReference<>();
    final AtomicReference<String> responseFilterUrl = new AtomicReference<>();

    proxy.addResponseFilter(
        new ResponseFilter() {
          @Override
          public void filterResponse(
              HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) {
            responseCtx.set(messageInfo.getChannelHandlerContext());
            responseOriginalRequest.set(messageInfo.getOriginalRequest());
            responseIsHttps.set(messageInfo.isHttps());
            responseFilterOriginalUrl.set(messageInfo.getOriginalUrl());
            responseFilterUrl.set(messageInfo.getUrl());
          }
        });

    try (CloseableHttpClient httpClient =
        NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) {
      String requestUrl =
          "http://localhost:" + mockServerPort + "/httpmessageinfopopulated?param1=value1";
      CloseableHttpResponse response = httpClient.execute(new HttpGet(requestUrl));

      assertEquals(
          "Expected server to return a 200", 200, response.getStatusLine().getStatusCode());
      assertNotNull(
          "Expected ChannelHandlerContext to be populated in request filter", requestCtx.get());
      assertNotNull(
          "Expected originalRequest to be populated in request filter",
          requestOriginalRequest.get());
      assertFalse("Expected isHttps to return false in request filter", requestIsHttps.get());
      assertEquals(
          "Expected originalUrl in request filter to match actual request URL",
          requestUrl,
          requestFilterOriginalUrl.get());
      assertEquals(
          "Expected url in request filter to match actual request URL",
          requestUrl,
          requestFilterUrl.get());

      assertNotNull(
          "Expected ChannelHandlerContext to be populated in response filter", responseCtx.get());
      assertNotNull(
          "Expected originalRequest to be populated in response filter",
          responseOriginalRequest.get());
      assertFalse("Expected isHttps to return false in response filter", responseIsHttps.get());
      assertEquals(
          "Expected originalUrl in response filter to match actual request URL",
          requestUrl,
          responseFilterOriginalUrl.get());
      assertEquals(
          "Expected url in response filter to match actual request URL",
          requestUrl,
          responseFilterUrl.get());
    }
  }