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

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

    final AtomicBoolean requestContentsNull = new AtomicBoolean(false);
    final AtomicBoolean responseContentsNull = new AtomicBoolean(false);

    proxy.addFirstHttpFilterFactory(
        new RequestFilterAdapter.FilterSource(
            new RequestFilter() {
              @Override
              public HttpResponse filterRequest(
                  HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) {
                if (contents == null) {
                  requestContentsNull.set(true);
                }

                return null;
              }
            },
            0));

    proxy.addFirstHttpFilterFactory(
        new ResponseFilterAdapter.FilterSource(
            new ResponseFilter() {
              @Override
              public void filterResponse(
                  HttpResponse response,
                  HttpMessageContents contents,
                  HttpMessageInfo messageInfo) {
                if (contents == null) {
                  responseContentsNull.set(true);
                }
              }
            },
            0));

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

      assertEquals(
          "Expected server to return a 200", 200, response.getStatusLine().getStatusCode());
      assertTrue(
          "Expected HttpMessageContents to be null in RequestFilter because HTTP message aggregation is disabled",
          requestContentsNull.get());
      assertTrue(
          "Expected HttpMessageContents to be null in ResponseFilter because HTTP message aggregation is disabled",
          responseContentsNull.get());
    }
  }
  /** Helper method for executing response modification tests. */
  private void testModifiedResponse(final String originalText, final String newText)
      throws IOException {
    mockServer
        .when(request().withMethod("GET").withPath("/modifyresponse"), Times.exactly(1))
        .respond(
            response()
                .withStatusCode(200)
                .withHeader(new Header(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=utf-8"))
                .withBody(originalText));

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

    proxy.addFirstHttpFilterFactory(
        new HttpFiltersSourceAdapter() {
          @Override
          public HttpFilters filterRequest(HttpRequest originalRequest) {
            return new HttpFiltersAdapter(originalRequest) {
              @Override
              public HttpObject proxyToClientResponse(HttpObject httpObject) {
                if (httpObject instanceof FullHttpResponse) {
                  FullHttpResponse httpResponseAndContent = (FullHttpResponse) httpObject;

                  String bodyContent = HttpObjectUtil.extractHttpEntityBody(httpResponseAndContent);

                  if (bodyContent.equals(originalText)) {
                    HttpObjectUtil.replaceTextHttpEntityBody(httpResponseAndContent, newText);
                  }
                }

                return super.proxyToClientResponse(httpObject);
              }
            };
          }

          @Override
          public int getMaximumResponseBufferSizeInBytes() {
            return 10000;
          }
        });

    try (CloseableHttpClient httpClient =
        NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) {
      CloseableHttpResponse response =
          httpClient.execute(new HttpGet("http://localhost:" + mockServerPort + "/modifyresponse"));
      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", newText, responseBody);
    }
  }
  @Test
  public void testCanModifyRequest() throws IOException {
    mockServer
        .when(request().withMethod("GET").withPath("/modifyrequest"), Times.exactly(1))
        .respond(
            response()
                .withStatusCode(200)
                .withHeader(new Header(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=utf-8"))
                .withBody("success"));

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

    proxy.addFirstHttpFilterFactory(
        new HttpFiltersSourceAdapter() {
          @Override
          public HttpFilters filterRequest(HttpRequest originalRequest) {
            return new HttpFiltersAdapter(originalRequest) {
              @Override
              public HttpResponse clientToProxyRequest(HttpObject httpObject) {
                if (httpObject instanceof HttpRequest) {
                  HttpRequest httpRequest = (HttpRequest) httpObject;
                  httpRequest.setUri(
                      httpRequest.getUri().replace("/originalrequest", "/modifyrequest"));
                }

                return super.clientToProxyRequest(httpObject);
              }
            };
          }
        });

    try (CloseableHttpClient httpClient =
        NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) {
      CloseableHttpResponse response =
          httpClient.execute(
              new HttpGet("http://localhost:" + mockServerPort + "/originalrequest"));
      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 testCanBypassFilterForRequest() throws IOException, InterruptedException {
    mockServer
        .when(request().withMethod("GET").withPath("/bypassfilter"), Times.exactly(2))
        .respond(response().withStatusCode(200).withBody("success"));

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

    final AtomicInteger filtersSourceHitCount = new AtomicInteger();
    final AtomicInteger filterHitCount = new AtomicInteger();

    proxy.addFirstHttpFilterFactory(
        new HttpFiltersSourceAdapter() {
          @Override
          public HttpFilters filterRequest(HttpRequest originalRequest) {
            if (filtersSourceHitCount.getAndIncrement() == 0) {
              return null;
            } else {
              return new HttpFiltersAdapter(originalRequest) {
                @Override
                public void serverToProxyResponseReceived() {
                  filterHitCount.incrementAndGet();
                }
              };
            }
          }
        });

    // during the first request, the filterRequest(...) method should return null, which will
    // prevent the filter instance from
    // being added to the filter chain
    try (CloseableHttpClient httpClient =
        NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) {
      CloseableHttpResponse response =
          httpClient.execute(new HttpGet("http://localhost:" + mockServerPort + "/bypassfilter"));
      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);
    }

    Thread.sleep(500);

    assertEquals(
        "Expected filters source to be invoked on first request", 1, filtersSourceHitCount.get());
    assertEquals(
        "Expected filter instance to be bypassed on first request", 0, filterHitCount.get());

    // during the second request, the filterRequest(...) method will return a filter instance, which
    // should be invoked during processing
    try (CloseableHttpClient httpClient =
        NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) {
      CloseableHttpResponse response =
          httpClient.execute(new HttpGet("http://localhost:" + mockServerPort + "/bypassfilter"));
      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);
    }

    Thread.sleep(500);

    assertEquals(
        "Expected filters source to be invoked again on second request",
        2,
        filtersSourceHitCount.get());
    assertEquals(
        "Expected filter instance to be invoked on second request (only)", 1, filterHitCount.get());
  }
  @Test
  public void testCanShortCircuitResponse() throws IOException {
    mockServer
        .when(request().withMethod("GET").withPath("/regular200"), Times.exactly(1))
        .respond(response().withStatusCode(200).withBody("success"));

    // this response should be "short-circuited" by the interceptor
    mockServer
        .when(request().withMethod("GET").withPath("/shortcircuit204"), Times.exactly(1))
        .respond(response().withStatusCode(200).withBody("success"));

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

    final AtomicBoolean interceptorFired = new AtomicBoolean(false);
    final AtomicBoolean shortCircuitFired = new AtomicBoolean(false);

    proxy.addFirstHttpFilterFactory(
        new HttpFiltersSourceAdapter() {
          @Override
          public HttpFilters filterRequest(HttpRequest originalRequest) {
            return new HttpFiltersAdapter(originalRequest) {
              @Override
              public HttpResponse clientToProxyRequest(HttpObject httpObject) {
                if (httpObject instanceof HttpRequest) {
                  interceptorFired.set(true);

                  HttpRequest httpRequest = (HttpRequest) httpObject;

                  if (httpRequest.getMethod().equals(HttpMethod.GET)
                      && httpRequest.getUri().contains("/shortcircuit204")) {
                    HttpResponse httpResponse =
                        new DefaultHttpResponse(
                            httpRequest.getProtocolVersion(), HttpResponseStatus.NO_CONTENT);

                    shortCircuitFired.set(true);

                    return httpResponse;
                  }
                }

                return super.clientToProxyRequest(httpObject);
              }
            };
          }
        });

    try (CloseableHttpClient httpClient =
        NewProxyServerTestUtil.getNewHttpClient(proxy.getPort())) {
      CloseableHttpResponse response =
          httpClient.execute(new HttpGet("http://localhost:" + mockServerPort + "/regular200"));
      String responseBody =
          NewProxyServerTestUtil.toStringAndClose(response.getEntity().getContent());

      assertTrue("Expected interceptor to fire", interceptorFired.get());
      assertFalse(
          "Did not expected short circuit interceptor code to execute", shortCircuitFired.get());

      assertEquals(
          "Expected server to return a 200", 200, response.getStatusLine().getStatusCode());
      assertEquals("Did not receive expected response from mock server", "success", responseBody);
    }

    interceptorFired.set(false);

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

      assertTrue("Expected interceptor to fire", interceptorFired.get());
      assertTrue("Expected interceptor to short-circuit response", shortCircuitFired.get());

      assertEquals(
          "Expected interceptor to return a 204 (No Content)",
          204,
          response.getStatusLine().getStatusCode());
      assertNull("Expected no entity attached to response", response.getEntity());
    }
  }