Last active
August 25, 2023 13:17
-
-
Save mikeshepherd/10ab7e5c2cfb933c587f3e01bf1c06a6 to your computer and use it in GitHub Desktop.
Reproduce inconsistent behaviour for NoContent responses
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//> 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