OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / net / webdav / if.go
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.
4
5 package webdav
6
7 // The If header is covered by Section 10.4.
8 // http://www.webdav.org/specs/rfc4918.html#HEADER_If
9
10 import (
11         "strings"
12 )
13
14 // ifHeader is a disjunction (OR) of ifLists.
15 type ifHeader struct {
16         lists []ifList
17 }
18
19 // ifList is a conjunction (AND) of Conditions, and an optional resource tag.
20 type ifList struct {
21         resourceTag string
22         conditions  []Condition
23 }
24
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 {
31         case '(':
32                 return parseNoTagLists(s)
33         case angleTokenType:
34                 return parseTaggedLists(s)
35         default:
36                 return ifHeader{}, false
37         }
38 }
39
40 func parseNoTagLists(s string) (h ifHeader, ok bool) {
41         for {
42                 l, remaining, ok := parseList(s)
43                 if !ok {
44                         return ifHeader{}, false
45                 }
46                 h.lists = append(h.lists, l)
47                 if remaining == "" {
48                         return h, true
49                 }
50                 s = remaining
51         }
52 }
53
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)
58                 switch tokenType {
59                 case angleTokenType:
60                         if !first && n == 0 {
61                                 return ifHeader{}, false
62                         }
63                         resourceTag, n = tokenStr, 0
64                         s = remaining
65                 case '(':
66                         n++
67                         l, remaining, ok := parseList(s)
68                         if !ok {
69                                 return ifHeader{}, false
70                         }
71                         l.resourceTag = resourceTag
72                         h.lists = append(h.lists, l)
73                         if remaining == "" {
74                                 return h, true
75                         }
76                         s = remaining
77                 default:
78                         return ifHeader{}, false
79                 }
80         }
81 }
82
83 func parseList(s string) (l ifList, remaining string, ok bool) {
84         tokenType, _, s := lex(s)
85         if tokenType != '(' {
86                 return ifList{}, "", false
87         }
88         for {
89                 tokenType, _, remaining = lex(s)
90                 if tokenType == ')' {
91                         if len(l.conditions) == 0 {
92                                 return ifList{}, "", false
93                         }
94                         return l, remaining, true
95                 }
96                 c, remaining, ok := parseCondition(s)
97                 if !ok {
98                         return ifList{}, "", false
99                 }
100                 l.conditions = append(l.conditions, c)
101                 s = remaining
102         }
103 }
104
105 func parseCondition(s string) (c Condition, remaining string, ok bool) {
106         tokenType, tokenStr, s := lex(s)
107         if tokenType == notTokenType {
108                 c.Not = true
109                 tokenType, tokenStr, s = lex(s)
110         }
111         switch tokenType {
112         case strTokenType, angleTokenType:
113                 c.Token = tokenStr
114         case squareTokenType:
115                 c.ETag = tokenStr
116         default:
117                 return Condition{}, "", false
118         }
119         return c, s, true
120 }
121
122 // Single-rune tokens like '(' or ')' have a token type equal to their rune.
123 // All other tokens have a negative token type.
124 const (
125         errTokenType    = rune(-1)
126         eofTokenType    = rune(-2)
127         strTokenType    = rune(-3)
128         notTokenType    = rune(-4)
129         angleTokenType  = rune(-5)
130         squareTokenType = rune(-6)
131 )
132
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] == ' ') {
138                 s = s[1:]
139         }
140         if len(s) == 0 {
141                 return eofTokenType, "", ""
142         }
143         i := 0
144 loop:
145         for ; i < len(s); i++ {
146                 switch s[i] {
147                 case '\t', ' ', '(', ')', '<', '>', '[', ']':
148                         break loop
149                 }
150         }
151
152         if i != 0 {
153                 tokenStr, remaining = s[:i], s[i:]
154                 if tokenStr == "Not" {
155                         return notTokenType, "", remaining
156                 }
157                 return strTokenType, tokenStr, remaining
158         }
159
160         j := 0
161         switch s[0] {
162         case '<':
163                 j, tokenType = strings.IndexByte(s, '>'), angleTokenType
164         case '[':
165                 j, tokenType = strings.IndexByte(s, ']'), squareTokenType
166         default:
167                 return rune(s[0]), "", s[1:]
168         }
169         if j < 0 {
170                 return errTokenType, "", ""
171         }
172         return tokenType, s[1:j], s[j+1:]
173 }