github.com/afein/docker@v1.8.2/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  }