1 // Copyright 2014 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
7 // The If header is covered by Section 10.4.
8 // http://www.webdav.org/specs/rfc4918.html#HEADER_If
14 // ifHeader is a disjunction (OR) of ifLists.
15 type ifHeader struct {
19 // ifList is a conjunction (AND) of Conditions, and an optional resource tag.
22 conditions []Condition
25 // parseIfHeader parses the "If: foo bar" HTTP header. The httpHeader string
26 // should omit the "If:" prefix and have any "\r\n"s collapsed to a " ", as is
27 // returned by req.Header.Get("If") for a http.Request req.
28 func parseIfHeader(httpHeader string) (h ifHeader, ok bool) {
29 s := strings.TrimSpace(httpHeader)
30 switch tokenType, _, _ := lex(s); tokenType {
32 return parseNoTagLists(s)
34 return parseTaggedLists(s)
36 return ifHeader{}, false
40 func parseNoTagLists(s string) (h ifHeader, ok bool) {
42 l, remaining, ok := parseList(s)
44 return ifHeader{}, false
46 h.lists = append(h.lists, l)
54 func parseTaggedLists(s string) (h ifHeader, ok bool) {
55 resourceTag, n := "", 0
56 for first := true; ; first = false {
57 tokenType, tokenStr, remaining := lex(s)
61 return ifHeader{}, false
63 resourceTag, n = tokenStr, 0
67 l, remaining, ok := parseList(s)
69 return ifHeader{}, false
71 l.resourceTag = resourceTag
72 h.lists = append(h.lists, l)
78 return ifHeader{}, false
83 func parseList(s string) (l ifList, remaining string, ok bool) {
84 tokenType, _, s := lex(s)
86 return ifList{}, "", false
89 tokenType, _, remaining = lex(s)
91 if len(l.conditions) == 0 {
92 return ifList{}, "", false
94 return l, remaining, true
96 c, remaining, ok := parseCondition(s)
98 return ifList{}, "", false
100 l.conditions = append(l.conditions, c)
105 func parseCondition(s string) (c Condition, remaining string, ok bool) {
106 tokenType, tokenStr, s := lex(s)
107 if tokenType == notTokenType {
109 tokenType, tokenStr, s = lex(s)
112 case strTokenType, angleTokenType:
114 case squareTokenType:
117 return Condition{}, "", false
122 // Single-rune tokens like '(' or ')' have a token type equal to their rune.
123 // All other tokens have a negative token type.
125 errTokenType = rune(-1)
126 eofTokenType = rune(-2)
127 strTokenType = rune(-3)
128 notTokenType = rune(-4)
129 angleTokenType = rune(-5)
130 squareTokenType = rune(-6)
133 func lex(s string) (tokenType rune, tokenStr string, remaining string) {
134 // The net/textproto Reader that parses the HTTP header will collapse
135 // Linear White Space that spans multiple "\r\n" lines to a single " ",
136 // so we don't need to look for '\r' or '\n'.
137 for len(s) > 0 && (s[0] == '\t' || s[0] == ' ') {
141 return eofTokenType, "", ""
145 for ; i < len(s); i++ {
147 case '\t', ' ', '(', ')', '<', '>', '[', ']':
153 tokenStr, remaining = s[:i], s[i:]
154 if tokenStr == "Not" {
155 return notTokenType, "", remaining
157 return strTokenType, tokenStr, remaining
163 j, tokenType = strings.IndexByte(s, '>'), angleTokenType
165 j, tokenType = strings.IndexByte(s, ']'), squareTokenType
167 return rune(s[0]), "", s[1:]
170 return errTokenType, "", ""
172 return tokenType, s[1:j], s[j+1:]