~juju-qa/ubuntu/xenial/juju/xenial-2.0-beta3

« back to all changes in this revision

Viewing changes to src/github.com/juju/usso/url.go

  • Committer: Martin Packman
  • Date: 2016-03-30 19:31:08 UTC
  • mfrom: (1.1.41)
  • Revision ID: martin.packman@canonical.com-20160330193108-h9iz3ak334uk0z5r
Merge new upstream source 2.0~beta3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2015 Canonical Ltd.
 
2
// Licensed under the LGPLv3, see LICENSE file for details.
 
3
 
 
4
package usso
 
5
 
 
6
import (
 
7
        "fmt"
 
8
        "net/url"
 
9
        "sort"
 
10
        "strings"
 
11
)
 
12
 
 
13
// Remove the standard ports from the URL.
 
14
func normalizeHost(scheme, hostSpec string) string {
 
15
        standardPorts := map[string]string{
 
16
                "http":  "80",
 
17
                "https": "443",
 
18
        }
 
19
        hostParts := strings.Split(hostSpec, ":")
 
20
        if len(hostParts) == 2 && hostParts[1] == standardPorts[scheme] {
 
21
                // There's a port, but it's the default one.  Leave it out.
 
22
                return hostParts[0]
 
23
        }
 
24
        return hostSpec
 
25
}
 
26
 
 
27
// Normalize the URL according to OAuth specs.
 
28
func NormalizeURL(inputUrl string) (string, error) {
 
29
        parsedUrl, err := url.Parse(inputUrl)
 
30
        if err != nil {
 
31
                return "", err
 
32
        }
 
33
 
 
34
        host := normalizeHost(parsedUrl.Scheme, parsedUrl.Host)
 
35
        normalizedUrl := fmt.Sprintf(
 
36
                "%v://%v%v", parsedUrl.Scheme, host, parsedUrl.Path)
 
37
        return normalizedUrl, nil
 
38
}
 
39
 
 
40
type parameterSlice []parameter
 
41
 
 
42
func (p parameterSlice) Len() int {
 
43
        return len(p)
 
44
}
 
45
 
 
46
func (p parameterSlice) Less(i, j int) bool {
 
47
        if p[i].key < p[j].key {
 
48
                return true
 
49
        }
 
50
        if p[i].key == p[j].key {
 
51
                return p[i].value < p[j].value
 
52
        }
 
53
        return false
 
54
}
 
55
 
 
56
func (p parameterSlice) Swap(i, j int) {
 
57
        p[i], p[j] = p[j], p[i]
 
58
}
 
59
 
 
60
func (p parameterSlice) String() string {
 
61
        ss := make([]string, len(p))
 
62
        for i, param := range p {
 
63
                ss[i] = param.String()
 
64
        }
 
65
        return strings.Join(ss, "&")
 
66
}
 
67
 
 
68
type parameter struct {
 
69
        key, value string
 
70
}
 
71
 
 
72
func (p parameter) String() string {
 
73
        return fmt.Sprintf("%s=%s", p.key, p.value)
 
74
}
 
75
 
 
76
// Normalize the parameters in the query string according to
 
77
// http://tools.ietf.org/html/rfc5849#section-3.4.1.3.2.
 
78
// url.Values.Encode encoded the GET parameters in a consistent order we
 
79
// do the encoding ourselves.
 
80
func NormalizeParameters(parameters url.Values) (string, error) {
 
81
        var ps parameterSlice
 
82
        for k, vs := range parameters {
 
83
                if k == "oauth_signature" {
 
84
                        continue
 
85
                }
 
86
                k = escape(k)
 
87
                for _, v := range vs {
 
88
                        v = escape(v)
 
89
                        ps = append(ps, parameter{k, v})
 
90
                }
 
91
        }
 
92
        sort.Sort(ps)
 
93
        return ps.String(), nil
 
94
}
 
95
 
 
96
var escaped = [4]uint64{
 
97
        0xFC009FFFFFFFFFFF,
 
98
        0xB800000178000001,
 
99
        0xFFFFFFFFFFFFFFFF,
 
100
        0xFFFFFFFFFFFFFFFF,
 
101
}
 
102
 
 
103
// escape percent encodes s as defined in
 
104
// http://tools.ietf.org/html/rfc5849#section-3.6.
 
105
//
 
106
// Note: this is slightly different from the output of url.QueryEscape.
 
107
func escape(s string) string {
 
108
        var count int
 
109
        for i := 0; i < len(s); i++ {
 
110
                if (escaped[s[i]>>6]>>(s[i]&0x3f))&1 == 1 {
 
111
                        count++
 
112
                }
 
113
        }
 
114
        if count == 0 {
 
115
                return s
 
116
        }
 
117
        buf := make([]byte, len(s)+2*count)
 
118
        j := 0
 
119
        for i := 0; i < len(s); i++ {
 
120
                if (escaped[s[i]>>6]>>(s[i]&0x3f))&1 == 1 {
 
121
                        buf[j] = '%'
 
122
                        buf[j+1] = "0123456789ABCDEF"[s[i]>>4]
 
123
                        buf[j+2] = "0123456789ABCDEF"[s[i]&0xf]
 
124
                        j += 3
 
125
                        continue
 
126
                }
 
127
                buf[j] = s[i]
 
128
                j++
 
129
        }
 
130
        return string(buf)
 
131
}