github.com/sevki/docker@v1.7.1/registry/authchallenge.go (about) 1 package registry 2 3 import ( 4 "net/http" 5 "strings" 6 ) 7 8 // Octet types from RFC 2616. 9 type octetType byte 10 11 // AuthorizationChallenge carries information 12 // from a WWW-Authenticate response header. 13 type AuthorizationChallenge struct { 14 Scheme string 15 Parameters map[string]string 16 } 17 18 var octetTypes [256]octetType 19 20 const ( 21 isToken octetType = 1 << iota 22 isSpace 23 ) 24 25 func init() { 26 // OCTET = <any 8-bit sequence of data> 27 // CHAR = <any US-ASCII character (octets 0 - 127)> 28 // CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)> 29 // CR = <US-ASCII CR, carriage return (13)> 30 // LF = <US-ASCII LF, linefeed (10)> 31 // SP = <US-ASCII SP, space (32)> 32 // HT = <US-ASCII HT, horizontal-tab (9)> 33 // <"> = <US-ASCII double-quote mark (34)> 34 // CRLF = CR LF 35 // LWS = [CRLF] 1*( SP | HT ) 36 // TEXT = <any OCTET except CTLs, but including LWS> 37 // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> 38 // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT 39 // token = 1*<any CHAR except CTLs or separators> 40 // qdtext = <any TEXT except <">> 41 42 for c := 0; c < 256; c++ { 43 var t octetType 44 isCtl := c <= 31 || c == 127 45 isChar := 0 <= c && c <= 127 46 isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0 47 if strings.IndexRune(" \t\r\n", rune(c)) >= 0 { 48 t |= isSpace 49 } 50 if isChar && !isCtl && !isSeparator { 51 t |= isToken 52 } 53 octetTypes[c] = t 54 } 55 } 56 57 func parseAuthHeader(header http.Header) []*AuthorizationChallenge { 58 var challenges []*AuthorizationChallenge 59 for _, h := range header[http.CanonicalHeaderKey("WWW-Authenticate")] { 60 v, p := parseValueAndParams(h) 61 if v != "" { 62 challenges = append(challenges, &AuthorizationChallenge{Scheme: v, Parameters: p}) 63 } 64 } 65 return challenges 66 } 67 68 func parseValueAndParams(header string) (value string, params map[string]string) { 69 params = make(map[string]string) 70 value, s := expectToken(header) 71 if value == "" { 72 return 73 } 74 value = strings.ToLower(value) 75 s = "," + skipSpace(s) 76 for strings.HasPrefix(s, ",") { 77 var pkey string 78 pkey, s = expectToken(skipSpace(s[1:])) 79 if pkey == "" { 80 return 81 } 82 if !strings.HasPrefix(s, "=") { 83 return 84 } 85 var pvalue string 86 pvalue, s = expectTokenOrQuoted(s[1:]) 87 if pvalue == "" { 88 return 89 } 90 pkey = strings.ToLower(pkey) 91 params[pkey] = pvalue 92 s = skipSpace(s) 93 } 94 return 95 } 96 97 func skipSpace(s string) (rest string) { 98 i := 0 99 for ; i < len(s); i++ { 100 if octetTypes[s[i]]&isSpace == 0 { 101 break 102 } 103 } 104 return s[i:] 105 } 106 107 func expectToken(s string) (token, rest string) { 108 i := 0 109 for ; i < len(s); i++ { 110 if octetTypes[s[i]]&isToken == 0 { 111 break 112 } 113 } 114 return s[:i], s[i:] 115 } 116 117 func expectTokenOrQuoted(s string) (value string, rest string) { 118 if !strings.HasPrefix(s, "\"") { 119 return expectToken(s) 120 } 121 s = s[1:] 122 for i := 0; i < len(s); i++ { 123 switch s[i] { 124 case '"': 125 return s[:i], s[i+1:] 126 case '\\': 127 p := make([]byte, len(s)-1) 128 j := copy(p, s[:i]) 129 escape := true 130 for i = i + i; i < len(s); i++ { 131 b := s[i] 132 switch { 133 case escape: 134 escape = false 135 p[j] = b 136 j++ 137 case b == '\\': 138 escape = true 139 case b == '"': 140 return string(p[:j]), s[i+1:] 141 default: 142 p[j] = b 143 j++ 144 } 145 } 146 return "", "" 147 } 148 } 149 return "", "" 150 }