<commit_before>/*
  Copyright (C) 2013-2018 Expedia Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */
package com.hotels.styx.proxy

import com.google.common.base.Charsets
import com.google.common.base.Charsets._
import com.hotels.styx._
import com.hotels.styx.api.HttpResponseStatus._
import com.hotels.styx.support.configuration.{HttpBackend, Origins}
import com.hotels.styx.support.{NettyOrigins, TestClientSupport}
import com.hotels.styx.utils.HttpTestClient
import io.netty.buffer.Unpooled
import io.netty.buffer.Unpooled._
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.http.HttpHeaders.Names._
import io.netty.handler.codec.http.HttpHeaders.Values._
import io.netty.handler.codec.http.HttpMethod.GET
import io.netty.handler.codec.http.HttpVersion._
import io.netty.handler.codec.http.LastHttpContent.EMPTY_LAST_CONTENT
import io.netty.handler.codec.http._
import org.scalatest.FunSpec
import org.scalatest.concurrent.Eventually
import com.hotels.styx.api.FullHttpRequest.get
import scala.concurrent.duration.{Duration, _}


class ChunkedDownloadSpec extends FunSpec
  with StyxProxySpec
  with DefaultStyxConfiguration
  with NettyOrigins
  with TestClientSupport
  with Eventually {

  val (originOne, originOneServer) = originAndCustomResponseWebServer("NettyOrigin")

  override protected def beforeAll(): Unit = {
    super.beforeAll()
    styxServer.setBackends(
      "/chunkedDownloadSpec/" -> HttpBackend("appOne", Origins(originOneServer))
    )
  }

  override protected def afterAll(): Unit = {
    originOneServer.stopAsync().awaitTerminated()
    super.afterAll()
  }

  describe("Downloads of chunked data.") {

    it("Proxies a response with chunked HTTP content.") {
      originRespondingWith(response200OkWithThreeChunks("a" * 10, "b" * 20, "c" * 30))

      val request = get(styxServer.routerURL("/chunkedDownloadSpec/1")).build()
      val response = decodedRequest(request)

      response.status() should be(OK)
      response.contentLength().isPresent should be(false)

      assert(response.bodyAs(UTF_8) == "a" * 10 + "b" * 20 + "c" * 30, s"\nReceived incorrect content: ${response.bodyAs(UTF_8)}")
    }

    it("Proxies long lasting HTTP Chunked downloads without triggering gateway read timeout.") {
      val messageBody = "Foo bar 0123456789012345678901234567890123456789\\n" * 100
      originRespondingWith(response200OkWithSlowChunkedMessageBody(messageBody))

      val request = get(styxServer.routerURL("/chunkedDownloadSpec/2")).build()
      val response = decodedRequest(request)

      response.status() should be(OK)

      assert(response.bodyAs(UTF_8).hashCode() == messageBody.hashCode, s"\nReceived incorrect content: ${response.bodyAs(UTF_8)}, \nexpected: $messageBody")
    }

    it("Cancels the HTTP download request when browser closes the connection.") {
      val messageBody = "Foo bar 0123456789012345678901234567890123456789\\n" * 100
      originRespondingWith(response200OkWithSlowChunkedMessageBody(messageBody))

      val request: DefaultFullHttpRequest = nettyGetRequest("/chunkedDownloadSpec/3")

      val client = newTestClientInstance("localhost", styxServer.httpPort)
      client.write(request)
      ensureResponseDidNotArrive(client)

      client.disconnect()

      eventually(timeout(5 seconds)) {
        assert(noBusyConnectionsToOrigin, "Connection remains busy.")
        assert(noAvailableConnectionsInPool, "Connection was not closed.")
      }
    }
  }

  def noBusyConnectionsToOrigin = {
    styxServer.metricsSnapshot.gauge(s"origins.appOne.localhost:${originOne.host.getPort}.connectionspool.busy-connections").get == 0
  }

  def noAvailableConnectionsInPool = {
    styxServer.metricsSnapshot.gauge(s"origins.appOne.localhost:${originOne.host.getPort}.connectionspool.available-connections").get == 0
  }

  def ensureResponseDidNotArrive(client: HttpTestClient) = {
    val response = client.waitForResponse(500, MILLISECONDS).asInstanceOf[FullHttpResponse]
    assert(response == null, "Response should not have arrived yet.")<commit_msg>Fix intermittently failing ChunkedDownloadSpec (#256)<commit_after>/*
  Copyright (C) 2013-2018 Expedia Inc.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */
package com.hotels.styx.proxy

import com.google.common.base.Charsets
import com.google.common.base.Charsets._
import com.hotels.styx._
import com.hotels.styx.api.HttpResponseStatus._
import com.hotels.styx.support.configuration.{HttpBackend, Origins}
import com.hotels.styx.support.{NettyOrigins, TestClientSupport}
import com.hotels.styx.utils.HttpTestClient
import io.netty.buffer.Unpooled
import io.netty.buffer.Unpooled._
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.http.HttpHeaders.Names._
import io.netty.handler.codec.http.HttpHeaders.Values._
import io.netty.handler.codec.http.HttpMethod.GET
import io.netty.handler.codec.http.HttpVersion._
import io.netty.handler.codec.http.LastHttpContent.EMPTY_LAST_CONTENT
import io.netty.handler.codec.http._
import org.scalatest.FunSpec
import org.scalatest.concurrent.Eventually
import com.hotels.styx.api.FullHttpRequest.get
import com.hotels.styx.api.extension.Origin

import scala.concurrent.duration.{Duration, _}


class ChunkedDownloadSpec extends FunSpec
  with StyxProxySpec
  with DefaultStyxConfiguration
  with NettyOrigins
  with TestClientSupport
  with Eventually {

  val (originOne, originOneServer) = originAndCustomResponseWebServer("NettyOrigin-01")
  val (originTwo, originTwoServer) = originAndCustomResponseWebServer("NettyOrigin-02")

  override protected def beforeAll(): Unit = {
    super.beforeAll()
    styxServer.setBackends(
      "/chunkedDownloadSpec/a/" -> HttpBackend("appOne", Origins(originOneServer)),
      "/chunkedDownloadSpec/b/" -> HttpBackend("appTwo", Origins(originTwoServer))
    )
  }

  override protected def afterAll(): Unit = {
    originOneServer.stopAsync().awaitTerminated()
    super.afterAll()
  }

  describe("Downloads of chunked data.") {

    it("Proxies a response with chunked HTTP content.") {
      originRespondingWith(response200OkWithThreeChunks("a" * 10, "b" * 20, "c" * 30))

      val request = get(styxServer.routerURL("/chunkedDownloadSpec/a/1")).build()
      val response = decodedRequest(request)

      response.status() should be(OK)
      response.contentLength().isPresent should be(false)

      assert(response.bodyAs(UTF_8) == "a" * 10 + "b" * 20 + "c" * 30, s"\nReceived incorrect content: ${response.bodyAs(UTF_8)}")
    }

    it("Proxies long lasting HTTP Chunked downloads without triggering gateway read timeout.") {
      val messageBody = "Foo bar 0123456789012345678901234567890123456789\\n" * 100
      originRespondingWith(response200OkWithSlowChunkedMessageBody(messageBody))

      val request = get(styxServer.routerURL("/chunkedDownloadSpec/a/2")).build()
      val response = decodedRequest(request)

      response.status() should be(OK)

      assert(response.bodyAs(UTF_8).hashCode() == messageBody.hashCode, s"\nReceived incorrect content: ${response.bodyAs(UTF_8)}, \nexpected: $messageBody")
    }

    it("Cancels the HTTP download request when browser closes the connection.") {
      assert(noBusyConnectionsToOrigin(originTwo), "Connection remains busy.")
      assert(noAvailableConnectionsInPool(originTwo), "Connection was not closed.")

      val messageBody = "Foo bar 0123456789012345678901234567890123456789\\n" * 100
      originRespondingWith(response200OkWithSlowChunkedMessageBody(messageBody))

      val request: DefaultFullHttpRequest = nettyGetRequest("/chunkedDownloadSpec/b/3")

      val client = newTestClientInstance("localhost", styxServer.httpPort)
      client.write(request)
      ensureResponseDidNotArrive(client)

      client.disconnect()

      eventually(timeout(5 seconds)) {
        assert(noBusyConnectionsToOrigin(originTwo), "Connection remains busy.")
        assert(noAvailableConnectionsInPool(originTwo), "Connection was not closed.")
      }
    }
  }

  def noBusyConnectionsToOrigin(origin: Origin) = {
    styxServer.metricsSnapshot.gauge(s"origins.appTwo.localhost:${origin.host.getPort}.connectionspool.busy-connections").get == 0
  }

  def noAvailableConnectionsInPool(origin: Origin) = {
    styxServer.metricsSnapshot.gauge(s"origins.appTwo.localhost:${origin.host.getPort}.connectionspool.available-connections").get == 0
  }

  def ensureResponseDidNotArrive(client: HttpTestClient) = {
    val response = client.waitForResponse(500, MILLISECONDS).asInstanceOf[FullHttpResponse]
    assert(response == null, "Response should not have arrived yet.")
{'fraction_non_alphanumeric': 0.10235318617242815, 'fraction_numerical': 0.02821740941274469, 'mean_word_length': 4.643360752056404, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 6, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4336750', 'n_tokens_mistral': 3059, 'n_tokens_neox': 2778, 'n_words': 623}