File: url.go

package info (click to toggle)
golang-github-juju-usso 1.0.1-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, trixie
  • size: 192 kB
  • sloc: makefile: 3
file content (131 lines) | stat: -rw-r--r-- 2,792 bytes parent folder | download
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
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENSE file for details.

package usso

import (
	"fmt"
	"net/url"
	"sort"
	"strings"
)

// Remove the standard ports from the URL.
func normalizeHost(scheme, hostSpec string) string {
	standardPorts := map[string]string{
		"http":  "80",
		"https": "443",
	}
	hostParts := strings.Split(hostSpec, ":")
	if len(hostParts) == 2 && hostParts[1] == standardPorts[scheme] {
		// There's a port, but it's the default one.  Leave it out.
		return hostParts[0]
	}
	return hostSpec
}

// Normalize the URL according to OAuth specs.
func NormalizeURL(inputUrl string) (string, error) {
	parsedUrl, err := url.Parse(inputUrl)
	if err != nil {
		return "", err
	}

	host := normalizeHost(parsedUrl.Scheme, parsedUrl.Host)
	normalizedUrl := fmt.Sprintf(
		"%v://%v%v", parsedUrl.Scheme, host, parsedUrl.Path)
	return normalizedUrl, nil
}

type parameterSlice []parameter

func (p parameterSlice) Len() int {
	return len(p)
}

func (p parameterSlice) Less(i, j int) bool {
	if p[i].key < p[j].key {
		return true
	}
	if p[i].key == p[j].key {
		return p[i].value < p[j].value
	}
	return false
}

func (p parameterSlice) Swap(i, j int) {
	p[i], p[j] = p[j], p[i]
}

func (p parameterSlice) String() string {
	ss := make([]string, len(p))
	for i, param := range p {
		ss[i] = param.String()
	}
	return strings.Join(ss, "&")
}

type parameter struct {
	key, value string
}

func (p parameter) String() string {
	return fmt.Sprintf("%s=%s", p.key, p.value)
}

// Normalize the parameters in the query string according to
// http://tools.ietf.org/html/rfc5849#section-3.4.1.3.2.
// url.Values.Encode encoded the GET parameters in a consistent order we
// do the encoding ourselves.
func NormalizeParameters(parameters url.Values) (string, error) {
	var ps parameterSlice
	for k, vs := range parameters {
		if k == "oauth_signature" {
			continue
		}
		k = escape(k)
		for _, v := range vs {
			v = escape(v)
			ps = append(ps, parameter{k, v})
		}
	}
	sort.Sort(ps)
	return ps.String(), nil
}

var escaped = [4]uint64{
	0xFC009FFFFFFFFFFF,
	0xB800000178000001,
	0xFFFFFFFFFFFFFFFF,
	0xFFFFFFFFFFFFFFFF,
}

// escape percent encodes s as defined in
// http://tools.ietf.org/html/rfc5849#section-3.6.
//
// Note: this is slightly different from the output of url.QueryEscape.
func escape(s string) string {
	var count int
	for i := 0; i < len(s); i++ {
		if (escaped[s[i]>>6]>>(s[i]&0x3f))&1 == 1 {
			count++
		}
	}
	if count == 0 {
		return s
	}
	buf := make([]byte, len(s)+2*count)
	j := 0
	for i := 0; i < len(s); i++ {
		if (escaped[s[i]>>6]>>(s[i]&0x3f))&1 == 1 {
			buf[j] = '%'
			buf[j+1] = "0123456789ABCDEF"[s[i]>>4]
			buf[j+2] = "0123456789ABCDEF"[s[i]&0xf]
			j += 3
			continue
		}
		buf[j] = s[i]
		j++
	}
	return string(buf)
}