File: parse.go

package info (click to toggle)
golang-github-jimstudt-http-authentication 0.0~git20140401.3eca13d-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 284 kB
  • sloc: makefile: 2
file content (154 lines) | stat: -rw-r--r-- 3,146 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package digest

import (
	"fmt"
	"io"
	"log"
	"strings"
	"testing"
)

// Tear apart the Authorization header value.
// PAY ATTENTION: this is large and complicated relative to other ones I've seen
// based on Split() using ',', ' ', and '=' in various orders. It is also probably
// correct even if the realm contains a '=', ' ', or '"' character, or if the
// sender uses HT, CR, or LF in their whitespace.
//
// The map that comes back looks like { "qop": "auth", "ns":"00000001", etc... }
func parseAuthorization(auth *strings.Reader) (map[string]string, error) {
	parts := map[string]string{}

	skipLWS := func(r *strings.Reader) error {
		for {
			ch, err := r.ReadByte()
			if err == io.EOF {
				return nil
			}
			if err != nil {
				return err
			}
			if ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' {
				// its white, we skip it and stay in loop
			} else {
				if err := r.UnreadByte(); err != nil {
					return err
				}
				break
			}
		}
		return nil
	}

	readName := func(r *strings.Reader) (string, error) {
		name := []byte{}

		for {
			ch, err := r.ReadByte()
			if err == io.EOF {
				break
			}
			if err != nil {
				return "", err
			}

			if (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '-' {
				name = append(name, ch)
			} else {
				if err := r.UnreadByte(); err != nil {
					return "", err
				}
				break
			}
		}
		if len(name) == 0 {
			return "", fmt.Errorf("expected name, got didn't get one")
		}
		return string(name), nil
	}

	readValue := func(r *strings.Reader) (string, error) {
		ch, err := r.ReadByte()
		if err != nil {
			return "", err
		}

		if ch == '"' {
			v := []byte{}
			for {
				ch, err := r.ReadByte()
				if err != nil {
					return "", fmt.Errorf("premature end of value: %s", err.Error())
				}
				if ch == '\\' {
					ch2, err := r.ReadByte()
					if err != nil {
						return "", fmt.Errorf("premature end of value: %s", err.Error())
					}
					v = append(v, ch2)
				} else if ch == '"' {
					break
				} else {
					v = append(v, ch)
				}
			}
			return string(v), nil
		} else {
			r.UnreadByte()
			return readName(r) // handles unquoted values, like true/false in the "stale" paramter
		}
	}

	for {
		name, err := readName(auth)
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}

		if err := skipLWS(auth); err != nil {
			return nil, err
		}

		eq, err := auth.ReadByte()
		if err == io.EOF || eq != '=' {
			return nil, fmt.Errorf("Malformed %s parameter, no equals", name)
		}

		if err := skipLWS(auth); err != nil {
			return nil, err
		}

		val, err := readValue(auth)
		if err != nil {
			return nil, err
		}

		parts[name] = val

		comma, err := auth.ReadByte()
		if err == io.EOF {
			break // our exit
		}
		if err != nil {
			return nil, err
		}
		if comma != ',' {
			return nil, fmt.Errorf("expected comma, got %v", comma)
		}

		if err := skipLWS(auth); err != nil {
			if err == io.EOF {
				break // our exit, finding an EOF after a value and some whitespace
			}
			return nil, err
		}
	}

	if testing.Verbose() {
		log.Printf("auth header = %#v", parts)
	}

	return parts, nil
}