-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add client implementation of distribution interface
Adds functionality to create a Repository client which connects to a remote endpoint. Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
- Loading branch information
Showing
6 changed files
with
1,828 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package client | ||
|
||
import ( | ||
"net/http" | ||
"strings" | ||
) | ||
|
||
// Octet types from RFC 2616. | ||
type octetType byte | ||
|
||
// AuthorizationChallenge carries information | ||
// from a WWW-Authenticate response header. | ||
type AuthorizationChallenge struct { | ||
Scheme string | ||
Parameters map[string]string | ||
} | ||
|
||
var octetTypes [256]octetType | ||
|
||
const ( | ||
isToken octetType = 1 << iota | ||
isSpace | ||
) | ||
|
||
func init() { | ||
// OCTET = <any 8-bit sequence of data> | ||
// CHAR = <any US-ASCII character (octets 0 - 127)> | ||
// CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)> | ||
// CR = <US-ASCII CR, carriage return (13)> | ||
// LF = <US-ASCII LF, linefeed (10)> | ||
// SP = <US-ASCII SP, space (32)> | ||
// HT = <US-ASCII HT, horizontal-tab (9)> | ||
// <"> = <US-ASCII double-quote mark (34)> | ||
// CRLF = CR LF | ||
// LWS = [CRLF] 1*( SP | HT ) | ||
// TEXT = <any OCTET except CTLs, but including LWS> | ||
// separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> | ||
// | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT | ||
// token = 1*<any CHAR except CTLs or separators> | ||
// qdtext = <any TEXT except <">> | ||
|
||
for c := 0; c < 256; c++ { | ||
var t octetType | ||
isCtl := c <= 31 || c == 127 | ||
isChar := 0 <= c && c <= 127 | ||
isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0 | ||
if strings.IndexRune(" \t\r\n", rune(c)) >= 0 { | ||
t |= isSpace | ||
} | ||
if isChar && !isCtl && !isSeparator { | ||
t |= isToken | ||
} | ||
octetTypes[c] = t | ||
} | ||
} | ||
|
||
func parseAuthHeader(header http.Header) []AuthorizationChallenge { | ||
var challenges []AuthorizationChallenge | ||
for _, h := range header[http.CanonicalHeaderKey("WWW-Authenticate")] { | ||
v, p := parseValueAndParams(h) | ||
if v != "" { | ||
challenges = append(challenges, AuthorizationChallenge{Scheme: v, Parameters: p}) | ||
} | ||
} | ||
return challenges | ||
} | ||
|
||
func parseValueAndParams(header string) (value string, params map[string]string) { | ||
params = make(map[string]string) | ||
value, s := expectToken(header) | ||
if value == "" { | ||
return | ||
} | ||
value = strings.ToLower(value) | ||
s = "," + skipSpace(s) | ||
for strings.HasPrefix(s, ",") { | ||
var pkey string | ||
pkey, s = expectToken(skipSpace(s[1:])) | ||
if pkey == "" { | ||
return | ||
} | ||
if !strings.HasPrefix(s, "=") { | ||
return | ||
} | ||
var pvalue string | ||
pvalue, s = expectTokenOrQuoted(s[1:]) | ||
if pvalue == "" { | ||
return | ||
} | ||
pkey = strings.ToLower(pkey) | ||
params[pkey] = pvalue | ||
s = skipSpace(s) | ||
} | ||
return | ||
} | ||
|
||
func skipSpace(s string) (rest string) { | ||
i := 0 | ||
for ; i < len(s); i++ { | ||
if octetTypes[s[i]]&isSpace == 0 { | ||
break | ||
} | ||
} | ||
return s[i:] | ||
} | ||
|
||
func expectToken(s string) (token, rest string) { | ||
i := 0 | ||
for ; i < len(s); i++ { | ||
if octetTypes[s[i]]&isToken == 0 { | ||
break | ||
} | ||
} | ||
return s[:i], s[i:] | ||
} | ||
|
||
func expectTokenOrQuoted(s string) (value string, rest string) { | ||
if !strings.HasPrefix(s, "\"") { | ||
return expectToken(s) | ||
} | ||
s = s[1:] | ||
for i := 0; i < len(s); i++ { | ||
switch s[i] { | ||
case '"': | ||
return s[:i], s[i+1:] | ||
case '\\': | ||
p := make([]byte, len(s)-1) | ||
j := copy(p, s[:i]) | ||
escape := true | ||
for i = i + i; i < len(s); i++ { | ||
b := s[i] | ||
switch { | ||
case escape: | ||
escape = false | ||
p[j] = b | ||
j++ | ||
case b == '\\': | ||
escape = true | ||
case b == '"': | ||
return string(p[:j]), s[i+1:] | ||
default: | ||
p[j] = b | ||
j++ | ||
} | ||
} | ||
return "", "" | ||
} | ||
} | ||
return "", "" | ||
} |
Oops, something went wrong.