github.com/Andyfoo/golang/x/net@v0.0.0-20190901054642-57c1bf301704/webdav/if.go (about) 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 }