Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Collections from Headers #2471

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -122,7 +122,7 @@ object AsyncHttpClient {

private def toAsyncRequest[F[_]: ConcurrentEffect](request: Request[F]): AsyncRequest = {
val headers = new DefaultHttpHeaders
for (h <- request.headers)
for (h <- request.headers.toList)
headers.add(h.name.value, h.value)
new RequestBuilder(request.method.renderString)
.setUrl(request.uri.renderString)
Expand Down
4 changes: 2 additions & 2 deletions bench/src/main/scala/org/http4s/bench/HttpHeadersBench.scala
Expand Up @@ -10,7 +10,7 @@ import org.openjdk.jmh.annotations._
class HttpHeadersBench {
@Benchmark
def apply(in: HeadersInput) =
Headers(in.headerSeq: _*)
Headers(in.headerSeq.toList)

@Benchmark
def add(in: HeadersInput) = {
Expand Down Expand Up @@ -39,7 +39,7 @@ class HeadersInput {
headerSeq = (0 until size).map { i =>
Header(s"X-Headers-Benchmark-$i", i.toString)
}
headers = Headers(headerSeq: _*)
headers = Headers(headerSeq.toList)
replacement = Header(s"X-Headers-Benchmark-${headers.size / 2}", "replacement")
}
}
Expand Up @@ -132,7 +132,7 @@ private final class Http1Connection[F[_]](

// Side Effecting Code
encodeRequestLine(req, rr)
Http1Stage.encodeHeaders(req.headers, rr, isServer)
Http1Stage.encodeHeaders(req.headers.toList, rr, isServer)
if (userAgent.nonEmpty && req.headers.get(`User-Agent`).isEmpty) {
rr << userAgent.get << "\r\n"
}
Expand Down
Expand Up @@ -206,7 +206,7 @@ class BlazeClientSpec extends Http4sSpec {
val name = address.getHostName
val port = address.getPort
mkClient(1)
.use { client =>
.use { _ =>
val submit = successTimeClient
.expect[String](Uri.fromString(s"http://$name:$port/delayed").yolo)
for {
Expand All @@ -222,7 +222,7 @@ class BlazeClientSpec extends Http4sSpec {
val name = address.getHostName
val port = address.getPort

Ref[IO].of(0L).flatMap { nanos =>
Ref[IO].of(0L).flatMap { _ =>
mkClient(1, requestTimeout = 1.second).use { client =>
val submit =
client.status(Request[IO](uri = Uri.fromString(s"http://$name:$port/simple").yolo))
Expand Down
Expand Up @@ -276,7 +276,7 @@ class Http1ClientStageSpec extends Http4sSpec {
// body is empty due to it being HEAD request
response.body.compile.toVector
.unsafeRunSync()
.foldLeft(0L)((long, byte) => long + 1L) must_== 0L
.foldLeft(0L)((long, _) => long + 1L) must_== 0L
} finally {
tail.shutdown()
}
Expand All @@ -301,7 +301,7 @@ class Http1ClientStageSpec extends Http4sSpec {
} yield hs
}

hs.unsafeRunSync().mkString must_== "Foo: Bar"
hs.map(_.toList.mkString).unsafeRunSync() must_== "Foo: Bar"
}

"Fail to get trailers before they are complete" in {
Expand Down
Expand Up @@ -9,7 +9,7 @@ import org.http4s.blaze.pipeline.{HeadStage, LeafBuilder}
private[blaze] object MockClientBuilder {
def builder(
head: => HeadStage[ByteBuffer],
tail: => BlazeConnection[IO]): ConnectionBuilder[IO, BlazeConnection[IO]] = { req =>
tail: => BlazeConnection[IO]): ConnectionBuilder[IO, BlazeConnection[IO]] = { _ =>
IO {
val t = tail
LeafBuilder(t).base(head)
Expand Down
Expand Up @@ -36,7 +36,9 @@ private[util] object ChunkWriter {
if (trailerHeaders.nonEmpty) {
val rr = new StringWriter(256)
rr << "0\r\n" // Last chunk
trailerHeaders.foreach(h => h.render(rr) << "\r\n") // trailers
trailerHeaders.foreach { h =>
h.render(rr) << "\r\n"; ()
} // trailers
rr << "\r\n" // end of chunks
ByteBuffer.wrap(rr.result.getBytes(ISO_8859_1))
} else ChunkEndBuffer
Expand Down
Expand Up @@ -92,17 +92,19 @@ class Http1WriterSpec extends Http4sSpec {
}

"CachingChunkWriter" should {
runNonChunkedTests(tail => new CachingChunkWriter[IO](tail, IO.pure(Headers()), 1024 * 1024))
runNonChunkedTests(
tail => new CachingChunkWriter[IO](tail, IO.pure(Headers.empty), 1024 * 1024))
}

"CachingStaticWriter" should {
runNonChunkedTests(tail => new CachingChunkWriter[IO](tail, IO.pure(Headers()), 1024 * 1024))
runNonChunkedTests(
tail => new CachingChunkWriter[IO](tail, IO.pure(Headers.empty), 1024 * 1024))
}

"FlushingChunkWriter" should {

def builder(tail: TailStage[ByteBuffer]): FlushingChunkWriter[IO] =
new FlushingChunkWriter[IO](tail, IO.pure(Headers()))
new FlushingChunkWriter[IO](tail, IO.pure(Headers.empty))

"Write a strict chunk" in {
// n.b. in the scalaz-stream version, we could introspect the
Expand Down Expand Up @@ -271,7 +273,7 @@ class Http1WriterSpec extends Http4sSpec {
def builderWithTrailer(tail: TailStage[ByteBuffer]): FlushingChunkWriter[IO] =
new FlushingChunkWriter[IO](
tail,
IO.pure(Headers(Header("X-Trailer", "trailer header value"))))
IO.pure(Headers.of(Header("X-Trailer", "trailer header value"))))

val p = eval(IO(messageBuffer)).flatMap(chunk(_).covary[IO])

Expand Down
Expand Up @@ -207,7 +207,7 @@ private[blaze] class Http1ServerStage[F[_]](
val rr = new StringWriter(512)
rr << req.httpVersion << ' ' << resp.status.code << ' ' << resp.status.reason << "\r\n"

Http1Stage.encodeHeaders(resp.headers, rr, isServer = true)
Http1Stage.encodeHeaders(resp.headers.toList, rr, isServer = true)

val respTransferCoding = `Transfer-Encoding`.from(resp.headers)
val lengthHeader = `Content-Length`.from(resp.headers)
Expand Down
Expand Up @@ -232,6 +232,7 @@ private class Http2NodeStage[F[_]](
if (h.name != headers.`Transfer-Encoding`.name &&
h.name != headers.Connection.name) {
hs += ((h.name.value.toLowerCase(Locale.ROOT), h.value))
()
}
}

Expand Down
Expand Up @@ -29,7 +29,7 @@ private[blaze] trait WebSocketSupport[F[_]] extends Http1ServerStage[F] {
ws match {
case None => super.renderResponse(req, resp, cleanup)
case Some(wsContext) =>
val hdrs = req.headers.map(h => (h.name.toString, h.value))
val hdrs = req.headers.toList.map(h => (h.name.toString, h.value))
if (WebSocketHandshake.isWebSocketRequest(hdrs)) {
WebSocketHandshake.serverHandshake(hdrs) match {
case Left((code, msg)) =>
Expand All @@ -55,8 +55,10 @@ private[blaze] trait WebSocketSupport[F[_]] extends Http1ServerStage[F] {
case (k, v) => sb.append(k).append(": ").append(v).append('\r').append('\n')
}

wsContext.headers.foreach(hdr =>
sb.append(hdr.name).append(": ").append(hdr.value).append('\r').append('\n'))
wsContext.headers.foreach { hdr =>
sb.append(hdr.name).append(": ").append(hdr.value).append('\r').append('\n')
()
}

sb.append('\r').append('\n')

Expand Down
Expand Up @@ -172,7 +172,7 @@ class Http1ServerStageSpec extends Http4sSpec with AfterAll {
val routes = HttpRoutes
.of[IO] {
case _ =>
val headers = Headers(H.`Transfer-Encoding`(TransferCoding.identity))
val headers = Headers.of(H.`Transfer-Encoding`(TransferCoding.identity))
IO.pure(Response[IO](headers = headers)
.withEntity("hello world"))
}
Expand Down Expand Up @@ -432,14 +432,14 @@ class Http1ServerStageSpec extends Http4sSpec with AfterAll {
for {
_ <- req.body.compile.drain
hs <- req.trailerHeaders
resp <- Ok(hs.mkString)
resp <- Ok(hs.toList.mkString)
} yield resp

case req if req.pathInfo == "/bar" =>
for {
// Don't run the body
hs <- req.trailerHeaders
resp <- Ok(hs.mkString)
resp <- Ok(hs.toList.mkString)
} yield resp
}
.orNotFound
Expand All @@ -466,7 +466,7 @@ class Http1ServerStageSpec extends Http4sSpec with AfterAll {
.flatMap { canceled =>
Deferred[IO, Unit].flatMap { gate =>
val req = "POST /sync HTTP/1.1\r\nConnection:keep-alive\r\nContent-Length: 4\r\n\r\ndone"
val app: HttpApp[IO] = HttpApp { req =>
val app: HttpApp[IO] = HttpApp { _ =>
gate.complete(()) >> IO.cancelable(_ => canceled.complete(()))
}
for {
Expand Down
Expand Up @@ -12,7 +12,7 @@ trait EmptyRequestGenerator[F[_]] extends Any with RequestGenerator {

/** Make a [[org.http4s.Request]] using this [[Method]] */
final def apply(uri: Uri, headers: Header*)(implicit F: Applicative[F]): F[Request[F]] =
F.pure(Request(method, uri, headers = Headers(headers: _*)))
F.pure(Request(method, uri, headers = Headers(headers.toList)))
}

trait EntityRequestGenerator[F[_]] extends Any with EmptyRequestGenerator[F] {
Expand All @@ -21,7 +21,7 @@ trait EntityRequestGenerator[F[_]] extends Any with EmptyRequestGenerator[F] {
final def apply[A](body: A, uri: Uri, headers: Header*)(
implicit F: Applicative[F],
w: EntityEncoder[F, A]): F[Request[F]] = {
val h = w.headers ++ headers
val h = w.headers ++ Headers(headers.toList)
val entity = w.toEntity(body)
val newHeaders = entity.length
.map { l =>
Expand Down
Expand Up @@ -107,13 +107,15 @@ abstract class ClientRouteTestBattery(name: String) extends Http4sSpec with Http
}

private def checkResponse(rec: Response[IO], expected: Response[IO]): IO[Boolean] = {
val hs = rec.headers.toSeq
val hs = rec.headers.toList
for {
_ <- IO(rec.status must be_==(expected.status))
body <- rec.body.compile.to[Array]
expBody <- expected.body.compile.to[Array]
_ <- IO(body must_== expBody)
_ <- IO(expected.headers.foreach(h => h must beOneOf(hs: _*)))
_ <- IO(expected.headers.foreach { h =>
h must beOneOf(hs: _*); ()
})
_ <- IO(rec.httpVersion must be_==(expected.httpVersion))
} yield true
}
Expand Down
Expand Up @@ -6,6 +6,7 @@ import fs2.Stream
import scala.concurrent.duration._

class PoolManagerSpec(name: String) extends Http4sSpec {
val _ = name
val key = RequestKey(Uri.Scheme.http, Uri.Authority(host = Uri.IPv4("127.0.0.1")))
class TestConnection extends Connection[IO] {
def isClosed = false
Expand Down
Expand Up @@ -98,7 +98,7 @@ class RetrySpec extends Http4sSpec with Tables {
resubmit(PUT)(RetryPolicy.defaultRetriable) must_== Status.Ok
}
"recklesslyRetriable resubmits bodies on non-idempotent methods" in {
resubmit(POST)((req, result) => RetryPolicy.recklesslyRetriable(result)) must_== Status.Ok
resubmit(POST)((_, result) => RetryPolicy.recklesslyRetriable(result)) must_== Status.Ok
}

"retry exceptions" in {
Expand Down
13 changes: 7 additions & 6 deletions core/src/main/scala/org/http4s/HeaderKey.scala
Expand Up @@ -53,18 +53,19 @@ object HeaderKey {
hs: Headers,
acc: NonEmptyList[HeaderT#Value]): NonEmptyList[HeaderT#Value] =
if (hs.nonEmpty) {
matchHeader(hs.head) match {
matchHeader(hs.toList.head) match {
case Some(header) =>
loop(hs.tail, acc.concatNel(header.values.widen[HeaderT#Value]))
loop(Headers(hs.toList.tail), acc.concatNel(header.values.widen[HeaderT#Value]))
case None =>
loop(hs.tail, acc)
loop(Headers(hs.toList.tail), acc)
}
} else acc
@tailrec def start(hs: Headers): Option[HeaderT] =
if (hs.nonEmpty) {
matchHeader(hs.head) match {
case Some(header) => Some(apply(loop(hs.tail, header.values.widen[HeaderT#Value])))
case None => start(hs.tail)
matchHeader(hs.toList.head) match {
case Some(header) =>
Some(apply(loop(Headers(hs.toList.tail), header.values.widen[HeaderT#Value])))
case None => start(Headers(hs.toList.tail))
}
} else None
start(headers)
Expand Down