This repository has been archived by the owner on Apr 24, 2024. It is now read-only.
/
ProtocolParameterRules.scala
152 lines (100 loc) · 4.88 KB
/
ProtocolParameterRules.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
* Copyright (C) 2011-2013 spray.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package spray.http
package parser
import org.parboiled.scala._
import org.parboiled.errors.ParsingException
// direct implementation of http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html
private[parser] trait ProtocolParameterRules {
this: Parser ⇒
import BasicRules._
/* 3.1 HTTP Version */
def HttpVersion = rule { "HTTP" ~ "/" ~ oneOrMore(Digit) ~ "." ~ oneOrMore(Digit) }
/* 3.3 Date/Time Formats */
/* 3.3.1 Full Date */
def HttpDate: Rule1[DateTime] = rule { (`RFC1123/RFC850 Date` | ASCTimeDate) ~ OptWS }
// we are a bit more lenient than the spec since we also allow a mixture of RFC1123 and RFC850 dates
def `RFC1123/RFC850 Date` = rule {
(Wkday | Weekday) ~ str(", ") ~ (Date1 | Date2) ~ ch(' ') ~ Time ~ ch(' ') ~ (str("GMT") | str("UTC")) ~~> {
(wkday, day, month, year, hour, min, sec) ⇒ createDateTime(year, month, day, hour, min, sec, wkday)
}
}
def ASCTimeDate = rule {
Wkday ~ ch(' ') ~ Date3 ~ ch(' ') ~ Time ~ ch(' ') ~ Digit4 ~~> {
(wkday, month, day, hour, min, sec, year) ⇒ createDateTime(year, month, day, hour, min, sec, wkday)
}
}
private def createDateTime(year: Int, month: Int, day: Int, hour: Int, min: Int, sec: Int, wkday: Int) = {
val dt = DateTime(year, month, day, hour, min, sec)
if (dt.weekday != wkday)
throw new ParsingException("Illegal weekday in date: is '" + DateTime.WEEKDAYS(wkday) +
"' but should be '" + DateTime.WEEKDAYS(dt.weekday) + "')" + dt)
dt
}
def Date1 = rule { Digit2 ~ ch(' ') ~ Month ~ ch(' ') ~ Digit4 }
def Date2 = rule { Digit2 ~ ch('-') ~ Month ~ ch('-') ~ Digit4 }
def Date3 = rule { Month ~ ch(' ') ~ (Digit2 | ch(' ') ~ Digit ~> (_.toInt)) }
def Time = rule { Digit2 ~ ch(':') ~ Digit2 ~ ch(':') ~ Digit2 }
def Wkday = rule { stringIndexRule(0, "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat") }
def Weekday = rule { stringIndexRule(0, "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday") }
def Month = rule { stringIndexRule(1, "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec") }
def Digit2 = rule { group(Digit ~ Digit) ~> (_.toInt) }
def Digit4 = rule { group(Digit ~ Digit ~ Digit ~ Digit) ~> (_.toInt) }
private def stringIndexRule(indexDelta: Int, strings: String*) = strings.zipWithIndex.map {
case (s, ix) ⇒ str(s) ~ push(ix + indexDelta)
} reduce (_ | _)
/* 3.3.2 Delta Seconds */
def DeltaSeconds = rule { oneOrMore(Digit) ~> (_.toLong) }
/* 3.4 Character Sets */
def Charset = rule { Token }
/* 3.5 Content Codings */
def ContentCoding = rule { Token }
/* 3.6 Transfer Codings */
def TransferCoding = rule { "chunked" | TransferExtension ~ DROP2 }
def TransferExtension = rule { Token ~ zeroOrMore(";" ~ Parameter) }
def Parameter = rule { Attribute ~ "=" ~ Value ~~> ((_, _)) }
def Attribute = rule { Token }
def Value = rule { Token | QuotedString }
/* 3.6.1 Chunked Transfer Codings */
// TODO: implement chunked transfers
/* 3.7 Media Types */
def MediaTypeDef: Rule3[String, String, List[(String, String)]] = rule {
Type ~ "/" ~ Subtype ~ zeroOrMore(";" ~ Parameter)
}
def Type = rule { Token }
def Subtype = rule { Token }
/* 3.8 Product Tokens */
def Product: Rule2[String, String] = rule { Token ~ (ch('/') ~ Token | push("")) }
def ProductVersionComment = rule(
Product ~ OptWS ~ Comment ~~> (ProductVersion(_, _, _))
| Product ~~> (ProductVersion(_, _))
| Comment ~~> (ProductVersion("", "", _)))
def ProductVersionComments = rule { oneOrMore(ProductVersionComment, separator = OptWS) ~ EOI }
/* 3.9 Quality Values */
def QValue = rule(
// more loose than the spec which only allows 1 to max. 3 digits/zeros
ch('0') ~ optional(ch('.') ~ zeroOrMore(Digit)) ~ OptWS
| ch('1') ~ optional(ch('.') ~ zeroOrMore(ch('0'))) ~ OptWS)
/* 3.10 Language Tags */
// RFC2616 definition, extended in order to also accept
// language-tags defined by https://tools.ietf.org/html/bcp47
def LanguageTag = rule { PrimaryTag ~ zeroOrMore("-" ~ SubTag) }
def PrimaryTag = rule { oneOrMore(Alpha) ~> identityFunc ~ OptWS }
def SubTag = rule { oneOrMore(AlphaNum) ~> identityFunc ~ OptWS }
/* 3.11 Entity Tags */
def EntityTag = rule { optional("W/") ~ OpaqueTag }
def OpaqueTag = rule { QuotedString }
}