-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathnotificationhub.go
132 lines (112 loc) · 3.37 KB
/
notificationhub.go
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
package notificationhubs
import (
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"fmt"
"io"
"net/http"
"net/url"
"path"
"strings"
"github.com/daresaydigital/azure-notificationhubs-go/utils"
)
// NotificationHub is a client for sending messages through Azure Notification Hubs
type NotificationHub struct {
SasKeyValue string
SasKeyName string
HubURL *url.URL
client utils.HTTPClient
expirationTimeGenerator utils.ExpirationTimeGenerator
}
// newNotificationHub initializes and returns NotificationHub pointer
func newNotificationHub(connectionString, hubPath string) *NotificationHub {
var (
connData = strings.Split(connectionString, ";")
_url = &url.URL{}
sasKeyName = ""
sasKeyValue = ""
)
for _, connItem := range connData {
if strings.HasPrefix(connItem, paramEndpoint) {
hubURL, err := url.Parse(connItem[len(paramEndpoint):])
if err == nil {
_url = hubURL
}
continue
}
if strings.HasPrefix(connItem, paramSaasKeyName) {
sasKeyName = connItem[len(paramSaasKeyName):]
continue
}
if strings.HasPrefix(connItem, paramSaasKeyValue) {
sasKeyValue = connItem[len(paramSaasKeyValue):]
continue
}
}
if _url.Scheme == schemeServiceBus || _url.Scheme == "" {
_url.Scheme = schemeDefault
}
_url.Path = hubPath
_url.RawQuery = url.Values{apiVersionParam: {apiVersionValue}}.Encode()
return &NotificationHub{
SasKeyName: sasKeyName,
SasKeyValue: sasKeyValue,
HubURL: _url,
client: utils.NewHubHTTPClient(),
expirationTimeGenerator: utils.NewExpirationTimeGenerator(),
}
}
// SetHTTPClient makes it possible to use a custom http client
func (h *NotificationHub) SetHTTPClient(c utils.HTTPClient) {
h.client = c
}
// SetExpirationTimeGenerator makes is possible to use a custom generator
func (h *NotificationHub) SetExpirationTimeGenerator(e utils.ExpirationTimeGenerator) {
h.expirationTimeGenerator = e
}
// generateSasToken generates and returns
// azure notification hub shared access signature token
func (h *NotificationHub) generateSasToken() string {
uri := &url.URL{
Host: h.HubURL.Host,
Scheme: h.HubURL.Scheme,
}
targetURI := strings.ToLower(uri.String())
expires := h.expirationTimeGenerator.GenerateTimestamp()
toSign := fmt.Sprintf("%s\n%d", url.QueryEscape(targetURI), expires)
mac := hmac.New(sha256.New, []byte(h.SasKeyValue))
mac.Write([]byte(toSign))
macb := mac.Sum(nil)
signature := base64.StdEncoding.EncodeToString(macb)
tokenParams := url.Values{
"sr": {targetURI},
"sig": {signature},
"se": {fmt.Sprintf("%d", expires)},
"skn": {h.SasKeyName},
}
return fmt.Sprintf("SharedAccessSignature %s", tokenParams.Encode())
}
// exec request using method to url
func (h *NotificationHub) exec(ctx context.Context, method string, url *url.URL, headers Headers, buf io.Reader) ([]byte, *http.Response, error) {
headers["Authorization"] = h.generateSasToken()
req, err := http.NewRequest(method, url.String(), buf)
if err != nil {
return nil, nil, err
}
req = req.WithContext(ctx)
for header, val := range headers {
req.Header.Set(header, val)
}
return h.client.Exec(req)
}
// generate an URL for path
func (h *NotificationHub) generateAPIURL(endpoint string) *url.URL {
return &url.URL{
Host: h.HubURL.Host,
Scheme: h.HubURL.Scheme,
Path: path.Join(h.HubURL.Path, endpoint),
RawQuery: h.HubURL.RawQuery,
}
}