Skip to content

Commit

Permalink
Merge pull request http4s#6905 from http4s/post-0.22.15
Browse files Browse the repository at this point in the history
Cleanups after v0.22.15
  • Loading branch information
rossabaker authored Jan 4, 2023
2 parents 0e2d067 + 7e2ab5d commit f446326
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 54 deletions.
15 changes: 12 additions & 3 deletions core/src/main/scala/org/http4s/ProductIdOrComment.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,15 @@ import org.http4s.util.Writer

sealed trait ProductIdOrComment extends Renderable
object ProductIdOrComment {
private[http4s] val serverAgentParser: P[(ProductId, List[ProductIdOrComment])] = {
@deprecated("Use serverAgentParser(Int) instead", "0.22.15")
private[http4s] val serverAgentParser: P[(ProductId, List[ProductIdOrComment])] =
serverAgentParser(Rfc7230.CommentDefaultMaxDepth)

private[http4s] def serverAgentParser(maxDepth: Int): P[(ProductId, List[ProductIdOrComment])] = {
val rws = P.charIn(' ', ' ').rep.void
ProductId.parser ~ (rws *> (ProductId.parser.orElse(ProductComment.parser))).rep0
ProductId.parser ~ (rws *> (ProductId.parser.orElse(ProductComment.parser(maxDepth)))).rep0
}

}

final case class ProductId(value: String, version: Option[String] = None)
Expand Down Expand Up @@ -55,6 +60,10 @@ final case class ProductComment(value: String) extends ProductIdOrComment {
}

object ProductComment {
private[http4s] val parser = Rfc7230.comment.map(ProductComment.apply)
private[http4s] def parser(maxDepth: Int): P[ProductComment] =
Rfc7230.comment(maxDepth).map(ProductComment.apply)

@deprecated("Use parser(Int) instead", "0.22.15")
private[http4s] val parser: P[ProductComment] =
parser(Rfc7230.CommentDefaultMaxDepth)
}
3 changes: 2 additions & 1 deletion core/src/main/scala/org/http4s/headers/HeaderCompanion.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ private[headers] abstract class HeaderCompanion[A](_name: String) {

implicit val headerInstance: Header[A, _ <: Header.Type]

private val invalidHeader = s"Invalid $name header"
private val invalidHeader = s"Invalid ${_name} header"

def parse(s: String): ParseResult[A] =
ParseResult.fromParser(parser, invalidHeader)(s)

Expand Down
49 changes: 35 additions & 14 deletions core/src/main/scala/org/http4s/headers/Server.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,58 @@
package org.http4s
package headers

import cats.parse.Parser
import org.http4s.internal.parsing.Rfc7230
import org.http4s.util.Renderable
import org.http4s.util.Writer
import org.typelevel.ci.CIString

import scala.annotation.nowarn

object Server extends HeaderCompanion[Server]("Server") {

override val name: CIString = super.name

def apply(id: ProductId, tail: ProductIdOrComment*): Server =
apply(id, tail.toList)

private[http4s] val parser =
ProductIdOrComment.serverAgentParser.map {
@nowarn("cat=deprecation")
@deprecated("Use parse(Int) instead", "0.22.15")
override def parse(s: String): ParseResult[`Server`] =
parse(Rfc7230.CommentDefaultMaxDepth)(s)

def parse(maxDepth: Int)(s: String): ParseResult[`Server`] =
parsePartiallyApplied(maxDepth)(s)

private def parsePartiallyApplied(maxDepth: Int): String => ParseResult[`Server`] =
ParseResult.fromParser(parser(maxDepth), "Invalid Server header")

@deprecated("Use parser(Int) instead", "0.22.15")
private[http4s] val parser: Parser[Server] =
parser(Rfc7230.CommentDefaultMaxDepth)

private[http4s] def parser(maxDepth: Int): Parser[Server] =
ProductIdOrComment.serverAgentParser(maxDepth).map {
case (product: ProductId, tokens: List[ProductIdOrComment]) =>
Server(product, tokens)
}

implicit val headerInstance: Header[Server, Header.Single] =
createRendered { h =>
new Renderable {
def render(writer: Writer): writer.type = {
writer << h.product
h.rest.foreach {
case p: ProductId => writer << ' ' << p
case ProductComment(c) => writer << ' ' << '(' << c << ')'
Header.createRendered(
name,
h =>
new Renderable {
def render(writer: Writer): writer.type = {
writer << h.product
h.rest.foreach {
case p: ProductId => writer << ' ' << p
case ProductComment(c) => writer << ' ' << '(' << c << ')'
}
writer
}
writer
}
}
}

},
parsePartiallyApplied(100),
)
}

/** Server header
Expand Down
19 changes: 17 additions & 2 deletions core/src/main/scala/org/http4s/headers/User-Agent.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package org.http4s
package headers

import org.http4s.Header
import org.http4s.internal.parsing.Rfc7230
import org.http4s.util.Renderable
import org.http4s.util.Renderer
import org.http4s.util.Writer
Expand All @@ -30,15 +31,29 @@ object `User-Agent` {

val name = ci"User-Agent"

@deprecated("Use parse(Int)(String) instead", "0.22.15")
def parse(s: String): ParseResult[`User-Agent`] =
ParseResult.fromParser(parser, "Invalid User-Agent header")(s)
parse(Rfc7230.CommentDefaultMaxDepth)(s)

def parse(maxDepth: Int)(s: String): ParseResult[`User-Agent`] =
parsePartiallyApplied(maxDepth)(s)

private def parsePartiallyApplied(maxDepth: Int): String => ParseResult[`User-Agent`] =
ParseResult.fromParser(parser(maxDepth), "Invalid User-Agent header")

@deprecated("Use parser(Int) instead", "0.22.15")
private[http4s] val parser =
ProductIdOrComment.serverAgentParser.map {
case (product: ProductId, tokens: List[ProductIdOrComment]) =>
`User-Agent`(product, tokens)
}

private[http4s] def parser(maxDepth: Int) =
ProductIdOrComment.serverAgentParser(maxDepth).map {
case (product: ProductId, tokens: List[ProductIdOrComment]) =>
`User-Agent`(product, tokens)
}

implicit val headerInstance: Header[`User-Agent`, Header.Single] =
Header.createRendered(
name,
Expand All @@ -54,7 +69,7 @@ object `User-Agent` {
}

},
parse,
parsePartiallyApplied(100),
)

implicit def convert(implicit
Expand Down
17 changes: 15 additions & 2 deletions core/src/main/scala/org/http4s/internal/parsing/Rfc7230.scala
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,23 @@ private[http4s] object Rfc7230 {
.orElse(obsText)

/* "(" *( ctext / quoted-pair / comment ) ")" */
val comment: Parser[String] = Parser.recursive[String] { (comment: Parser[String]) =>
between(char('('), cText.orElse(quotedPair).orElse(comment).rep0.string, char(')'))
def comment(maxDepth: Int): Parser[String] = {
def go(n: Int): Parser[String] =
between(
char('('),
if (n <= 0) Parser.failWith("exceeded maximum comment depth")
else cText.orElse(quotedPair).orElse(go(n - 1)).rep0.string,
char(')'),
)
go(maxDepth)
}

final val CommentDefaultMaxDepth = 100

@deprecated("Use comment(Int) instead", "0.22.15")
private[http4s] val comment: Parser[String] =
comment(CommentDefaultMaxDepth)

def headerRep[A](element: Parser[A]): Parser0[List[A]] =
headerRep1(element).?.map(_.fold(List.empty[A])(_.toList))

Expand Down
23 changes: 23 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,29 @@
Maintenance branches are merged before each new release. This change log is
ordered chronologically, so each release contains all changes described below it.

# v0.22.15 (2023-01-04)

## What's Changed
### http4s-core
* Fixes [CVE-2023-22465](https://github.com/http4s/http4s/security/advisories/GHSA-54w6-vxfh-fw7f)

### Behind the scenes
* Set LoggingHandler in NettyTestServer to the default DEBUG level by @RafalSumislawski in https://github.com/http4s/http4s/pull/6497

**Full Changelog**: https://github.com/http4s/http4s/compare/v0.22.14...v0.22.15

# v0.21.34 (2023-01-04)

## What's changed

### http4s-core
* Fixes [CVE-2023-22465](https://github.com/http4s/http4s/security/advisories/GHSA-54w6-vxfh-fw7f)

## Behind the scenes
* Don't publish website from 0.21 by @armanbilge in https://github.com/http4s/http4s/pull/6151

**Full Changelog**: https://github.com/http4s/http4s/compare/v0.21.33...v0.21.34

# v0.22.14 (2022-06-21)

This release is binary compatible with 0.22.x series. Routine maintenance has stopped on 0.22.x, but we'll continue to entertain patches from the community. All users are encouraged to upgrade to 0.23 (the latest stable series, on Cats-Effect 3).
Expand Down
68 changes: 43 additions & 25 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 7 additions & 7 deletions tests/src/test/scala/org/http4s/parser/SimpleHeadersSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -180,18 +180,18 @@ class SimpleHeadersSpec extends Http4sSuite {
val header = `User-Agent`(ProductId("foo", Some("bar")), List(ProductId("foo")))
assertEquals(header.value, "foo/bar foo")

assertEquals(`User-Agent`.parse(header.value), Right(header))
assertEquals(`User-Agent`.parse(100)(header.value), Right(header))

val header2 =
`User-Agent`(ProductId("foo"), List(ProductId("bar", Some("biz")), ProductComment("blah")))
assertEquals(header2.value, "foo bar/biz (blah)")
assertEquals(`User-Agent`.parse(header2.value), Right(header2))
assertEquals(`User-Agent`.parse(188)(header2.value), Right(header2))

val headerstr = "Mozilla/5.0 (Android; Mobile; rv:30.0) Gecko/30.0 Firefox/30.0"

val headerraw = Header.Raw(`User-Agent`.name, headerstr)

val parsed = `User-Agent`.parse(headerraw.value)
val parsed = `User-Agent`.parse(100)(headerraw.value)
assertEquals(
parsed,
Right(
Expand All @@ -212,16 +212,16 @@ class SimpleHeadersSpec extends Http4sSuite {
val header = Server(ProductId("foo", Some("bar")), List(ProductComment("foo")))
assertEquals(header.value, "foo/bar (foo)")

assertEquals(Server.parse(header.toRaw1.value), Right(header))
assertEquals(Server.parse(100)(header.toRaw1.value), Right(header))

val header2 =
Server(ProductId("foo"), List(ProductId("bar", Some("biz")), ProductComment("blah")))
assertEquals(header2.value, "foo bar/biz (blah)")
assertEquals(Server.parse(header2.toRaw1.value), Right(header2))
assertEquals(Server.parse(100)(header2.toRaw1.value), Right(header2))

val headerstr = "nginx/1.14.0 (Ubuntu)"
assertEquals(
Server.parse(headerstr),
Server.parse(100)(headerstr),
Right(
Server(
ProductId("nginx", Some("1.14.0")),
Expand All @@ -234,7 +234,7 @@ class SimpleHeadersSpec extends Http4sSuite {

val headerstr2 = "CERN/3.0 libwww/2.17"
assertEquals(
Server.parse(headerstr2),
Server.parse(100)(headerstr2),
Right(
Server(
ProductId("CERN", Some("3.0")),
Expand Down

0 comments on commit f446326

Please sign in to comment.