+hco #16822 add model for Referer header

This commit is contained in:
Mathias 2015-02-17 17:08:44 +01:00
parent bffdb998b6
commit d6bc3ec4c5
6 changed files with 53 additions and 6 deletions

View file

@ -0,0 +1,19 @@
/**
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.model.japi.headers;
import akka.http.model.japi.Uri;
/**
* Model for the `Referer` header.
* Specification: http://tools.ietf.org/html/rfc7231#section-5.5.2
*/
public abstract class Referer extends akka.http.model.HttpHeader {
public abstract Uri getUri();
public static Referer create(Uri uri) {
return new akka.http.model.headers.Referer(akka.http.model.japi.Util.convertUriToScala(uri));
}
}

View file

@ -537,6 +537,19 @@ final case class `Remote-Address`(address: RemoteAddress) extends japi.headers.R
protected def companion = `Remote-Address`
}
// http://tools.ietf.org/html/rfc7231#section-5.5.2
object Referer extends ModeledCompanion
final case class Referer(uri: Uri) extends japi.headers.Referer with ModeledHeader {
require(uri.fragment == None, "Referer header URI must not contain a fragment")
require(uri.authority.userinfo.isEmpty, "Referer header URI must not contain a userinfo component")
def renderValue[R <: Rendering](r: R): r.type = { import UriRendering.UriRenderer; r ~~ uri }
protected def companion = Referer
/** Java API */
def getUri: akka.http.model.japi.Uri = uri.asJava
}
// http://tools.ietf.org/html/rfc7231#section-7.4.2
object Server extends ModeledCompanion {
def apply(products: String): Server = apply(ProductVersion.parseMultiple(products))

View file

@ -96,10 +96,10 @@ object HeaderParser {
"link",
"location",
"origin",
"range",
"proxy-authenticate",
"proxy-authorization",
"range",
"referer",
"server",
"set-cookie",
"transfer-encoding",

View file

@ -168,6 +168,13 @@ private[parser] trait SimpleHeaders { this: Parser with CommonRules with CommonA
// http://tools.ietf.org/html/rfc7233#section-3.1
def `range` = rule { `byte-ranges-specifier` /*| `other-ranges-specifier` */ ~ EOI ~> (Range(_, _)) }
// http://tools.ietf.org/html/rfc7231#section-5.5.2
def referer = rule {
// we are bit more relaxed than the spec here by also parsing a potential fragment
// but catch it in the `Referer` instance validation (with a `require` in the constructor)
runSubParser(new UriParser(_).`URI-reference-pushed`) ~ EOI ~> (Referer(_))
}
// http://tools.ietf.org/html/rfc7231#section-7.4.2
def server = rule { products ~> (Server(_)) }

View file

@ -153,7 +153,8 @@ class HttpHeaderParserSpec extends WordSpec with Matchers with BeforeAndAfterAll
| | ┌─o-r-i-g-i-n-:- (origin)
| | | ┌─e-n-t-i-c-a-t-e-:- (proxy-authenticate)
| | ┌─p-r-o-x-y---a-u-t-h-o-r-i-z-a-t-i-o-n-:- (proxy-authorization)
| | | └─r-a-n-g-e-:- (range)
| | | | ┌─a-n-g-e-:- (range)
| | | └─r-e-f-e-r-e-r-:- (referer)
| └─s-e-r-v-e-r-:- (server)
| | └─t---c-o-o-k-i-e-:- (set-cookie)
| | ┌─t-r-a-n-s-f-e-r---e-n-c-o-d-i-n-g-:- (transfer-encoding)
@ -162,7 +163,7 @@ class HttpHeaderParserSpec extends WordSpec with Matchers with BeforeAndAfterAll
| └─x---f-o-r-w-a-r-d-e-d---f-o-r-:- (x-forwarded-for)
|""" -> parser.formatTrie
}
parser.formatSizes shouldEqual "602 nodes, 42 branchData rows, 57 values"
parser.formatSizes shouldEqual "610 nodes, 43 branchData rows, 58 values"
parser.contentHistogram shouldEqual
Map("connection" -> 3, "Content-Length" -> 1, "accept" -> 2, "cache-control" -> 2, "expect" -> 1)
}
@ -234,8 +235,8 @@ class HttpHeaderParserSpec extends WordSpec with Matchers with BeforeAndAfterAll
}
randomHeaders.take(300).foldLeft(0) {
case (acc, rawHeader) acc + parseAndCache(rawHeader.toString + "\r\nx", rawHeader)
} shouldEqual 99 // number of cache hits
parser.formatSizes shouldEqual "3040 nodes, 115 branchData rows, 255 values"
} shouldEqual 98 // number of cache hits
parser.formatSizes shouldEqual "3036 nodes, 115 branchData rows, 255 values"
}
"continue parsing modelled headers even if the overall cache capacity is reached" in new TestSetup() {
@ -247,7 +248,7 @@ class HttpHeaderParserSpec extends WordSpec with Matchers with BeforeAndAfterAll
randomHostHeaders.take(300).foldLeft(0) {
case (acc, header) acc + parseAndCache(header.toString + "\r\nx", header)
} shouldEqual 12 // number of cache hits
parser.formatSizes shouldEqual "766 nodes, 51 branchData rows, 69 values"
parser.formatSizes shouldEqual "774 nodes, 52 branchData rows, 70 values"
}
"continue parsing raw headers even if the header-specific cache capacity is reached" in new TestSetup() {

View file

@ -339,6 +339,13 @@ class HttpHeaderSpec extends FreeSpec with Matchers {
`Proxy-Authorization`(GenericHttpCredentials("Fancy", Map("yes" -> "no", "nonce" -> """4\2""")))
}
"Referer" in {
"Referer: https://spray.io/secure" =!= Referer(Uri("https://spray.io/secure"))
"Referer: /en-us/default.aspx?foo=bar" =!= Referer(Uri("/en-us/default.aspx?foo=bar"))
"Referer: https://akka.io/#sec" =!= ErrorInfo("Illegal HTTP header 'Referer': requirement failed",
"Referer header URI must not contain a fragment")
}
"Server" in {
"Server: as fghf.fdf/xx" =!= `Server`(Vector(ProductVersion("as"), ProductVersion("fghf.fdf", "xx")))
}