Skip to content

Instantly share code, notes, and snippets.

@mikeshepherd
Last active August 25, 2023 13:17
Show Gist options
  • Save mikeshepherd/10ab7e5c2cfb933c587f3e01bf1c06a6 to your computer and use it in GitHub Desktop.
Save mikeshepherd/10ab7e5c2cfb933c587f3e01bf1c06a6 to your computer and use it in GitHub Desktop.
Reproduce inconsistent behaviour for NoContent responses
//> using scala "2.13.10"
//> using option "-feature"
//> using lib "org.http4s::http4s-ember-client:0.23.23"
//> using lib "org.http4s::http4s-ember-server:0.23.23"
//> using lib "org.http4s::http4s-dsl:0.23.23"
import cats.effect._
import cats.implicits._
import com.comcast.ip4s._
import org.http4s._
import org.http4s.Uri
import org.http4s.client.Client
import org.http4s.ember.client.EmberClientBuilder
import org.http4s.ember.server.EmberServerBuilder
import org.typelevel.ci._
import scodec.bits.ByteVector
import org.http4s.dsl.Http4sDsl
import fs2.Stream
import org.http4s.client.dsl.Http4sClientDsl
import org.http4s.headers.`Content-Length`
object Http4sReproduction extends IOApp.Simple with Http4sDsl[IO] with Http4sClientDsl[IO] {
val routes = HttpRoutes
.of[IO] { case GET -> Root / "example" =>
IO(
Response(
Status.NoContent,
body = Stream.emits("{}".toList.map(_.toByte)),
headers = Headers(`Content-Length`(2L))
)
)
}
.orNotFound
val serverOverTcp = {
val server = EmberServerBuilder
.default[IO]
.withHost(ipv4"0.0.0.0")
.withPort(port"8080")
.withHttpApp(routes)
.build
val client = EmberClientBuilder
.default[IO]
.build
(server, client).mapN { (_, client) => client }
}
val serverOverCode = Resource.pure[IO, Client[IO]](Client.fromHttpApp(routes))
val uri = Uri.unsafeFromString("http://localhost:8080")
val logResponse = (response: Response[IO]) =>
IO.println(
s"Got content-length: [${response.headers.get(ci"Content-Length").map(_.head.value)}]"
) *>
response.as[ByteVector].flatMap(body => IO.println(s"Got actual length [${body.length}]"))
val request = GET(uri = uri / "example")
val overTcp =
IO.println("Running over tcp") *> serverOverTcp.use(
_.run(request).use(logResponse)
)
val overCode =
IO.println("Running in code") *> serverOverCode.use(
_.run(request).use(logResponse)
)
// TCP: Content-Length header: 2, actual body length: 0
// Direct: Content-Length header: 2, actual body length: 2
val example = overTcp.attempt *> overCode.attempt
val run: IO[Unit] = example.void
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment