Fix url parsing with trailing slash

Refs: #1545
This commit is contained in:
eikek
2022-07-07 11:31:36 +02:00
parent e9cb3d2b42
commit 2c9e012c96
5 changed files with 22 additions and 12 deletions

View File

@ -50,7 +50,7 @@ object Contact {
p match { p match {
case LenientUri.RootPath => false case LenientUri.RootPath => false
case LenientUri.EmptyPath => false case LenientUri.EmptyPath => false
case LenientUri.NonEmptyPath(segs) => case LenientUri.NonEmptyPath(segs, _) =>
Ident.fromString(segs.last).isRight && Ident.fromString(segs.last).isRight &&
segs.init.takeRight(3) == List("open", "upload", "item") segs.init.takeRight(3) == List("open", "upload", "item")
} }

View File

@ -121,7 +121,7 @@ object LenientUri {
val isRoot = true val isRoot = true
val isEmpty = false val isEmpty = false
def /(seg: String): Path = def /(seg: String): Path =
NonEmptyPath(NonEmptyList.of(seg)) NonEmptyPath(NonEmptyList.of(seg), false)
def asString = "/" def asString = "/"
} }
case object EmptyPath extends Path { case object EmptyPath extends Path {
@ -129,20 +129,22 @@ object LenientUri {
val isRoot = false val isRoot = false
val isEmpty = true val isEmpty = true
def /(seg: String): Path = def /(seg: String): Path =
NonEmptyPath(NonEmptyList.of(seg)) NonEmptyPath(NonEmptyList.of(seg), false)
def asString = "" def asString = ""
} }
case class NonEmptyPath(segs: NonEmptyList[String]) extends Path { case class NonEmptyPath(segs: NonEmptyList[String], trailingSlash: Boolean)
extends Path {
def segments = segs.toList def segments = segs.toList
val isEmpty = false val isEmpty = false
val isRoot = false val isRoot = false
private val slashSuffix = if (trailingSlash) "/" else ""
def /(seg: String): Path = def /(seg: String): Path =
copy(segs = segs.append(seg)) copy(segs = segs.append(seg))
def asString = def asString =
segs.head match { segs.head match {
case "." => segments.map(percentEncode).mkString("/") case "." => segments.map(percentEncode).mkString("/") + slashSuffix
case ".." => segments.map(percentEncode).mkString("/") case ".." => segments.map(percentEncode).mkString("/") + slashSuffix
case _ => "/" + segments.map(percentEncode).mkString("/") case _ => "/" + segments.map(percentEncode).mkString("/") + slashSuffix
} }
} }
@ -157,14 +159,14 @@ object LenientUri {
str.trim match { str.trim match {
case "/" => Right(RootPath) case "/" => Right(RootPath)
case "" => Right(EmptyPath) case "" => Right(EmptyPath)
case _ => case uriStr =>
Either.fromOption( Either.fromOption(
stripLeading(str, '/') stripLeading(uriStr, '/')
.split('/') .split('/')
.toList .toList
.traverse(percentDecode) .traverse(percentDecode)
.flatMap(NonEmptyList.fromList) .flatMap(NonEmptyList.fromList)
.map(NonEmptyPath.apply), .map(NonEmptyPath(_, uriStr.endsWith("/"))),
s"Invalid path: $str" s"Invalid path: $str"
) )
} }

View File

@ -62,7 +62,7 @@ object UrlMatcher {
// strip path to only match prefixes // strip path to only match prefixes
val mPath: LenientUri.Path = val mPath: LenientUri.Path =
NonEmptyList.fromList(url.path.segments.take(pathSegmentCount)) match { NonEmptyList.fromList(url.path.segments.take(pathSegmentCount)) match {
case Some(nel) => LenientUri.NonEmptyPath(nel) case Some(nel) => LenientUri.NonEmptyPath(nel, false)
case None => LenientUri.RootPath case None => LenientUri.RootPath
} }

View File

@ -29,4 +29,11 @@ class LenientUriTest extends FunSuite {
) )
assertEquals(LenientUri.percentDecode("a%25b%5Cc%7Cd%23e"), "a%b\\c|d#e".some) assertEquals(LenientUri.percentDecode("a%25b%5Cc%7Cd%23e"), "a%b\\c|d#e".some)
} }
test("parse with trailing slash") {
assertEquals(LenientUri.unsafe("http://a.com/").asString, "http://a.com/")
assertEquals(LenientUri.unsafe("http://a.com").asString, "http://a.com")
assertEquals(LenientUri.unsafe("http://a.com/path").asString, "http://a.com/path")
assertEquals(LenientUri.unsafe("http://a.com/path/").asString, "http://a.com/path/")
}
} }

View File

@ -24,7 +24,8 @@ object FileUrlReader {
scheme = Nel.of(scheme), scheme = Nel.of(scheme),
authority = Some(""), authority = Some(""),
path = LenientUri.NonEmptyPath( path = LenientUri.NonEmptyPath(
Nel.of(key.collective.id, key.category.id.id, key.id.id) Nel.of(key.collective.id, key.category.id.id, key.id.id),
false
), ),
query = None, query = None,
fragment = None fragment = None