forked from Coccodrillo/apns
/
client.go
141 lines (116 loc) · 3.17 KB
/
client.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
133
134
135
136
137
138
139
140
141
package apns
import (
"crypto/tls"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"golang.org/x/crypto/pkcs12"
"golang.org/x/net/http2"
)
const (
// Gateway defines Apple's production server to send apns2
Gateway = "api.push.apple.com"
// SandboxGateway defines Apple's development server to send apns2
SandboxGateway = "api.development.push.apple.com"
)
// Client describes a wrapper to send apns2 message
type Client struct {
gateway string
client *http.Client
}
// CreateClient creates HTTP/2 apns client
func CreateClient(certFile string, password string, isSandbox bool) (*Client, error) {
/* Condition validation: validate certificate's path */
if len(certFile) == 0 {
return nil, fmt.Errorf("Invalid certificate's path.")
}
/* Condition validation: validate certificate's password */
if len(password) == 0 {
return nil, fmt.Errorf("Invalid certificate's password.")
}
// Load certificate
bytes, _ := ioutil.ReadFile(certFile)
key, cert, err := pkcs12.Decode(bytes, password)
/* Condition validation: validate the correctness of loading process */
if err != nil {
return nil, err
}
// Create certificate
certificate := tls.Certificate{
Certificate: [][]byte{cert.Raw},
PrivateKey: key,
Leaf: cert,
}
// Define gateway
var gateway string
if !isSandbox {
gateway = Gateway
} else {
gateway = SandboxGateway
}
// Config TLS
config := &tls.Config{
Certificates: []tls.Certificate{certificate},
ServerName: gateway,
}
// Config transport
transport := &http2.Transport{
TLSClientConfig: config,
}
// Finalize
client := Client{
gateway: gateway,
client: &http.Client{Transport: transport},
}
return &client, nil
}
// SendMessages delivers push message to Apple.
func (c *Client) SendMessages(messages []*Message) []*Response {
/* Condition validation */
if len(messages) == 0 {
return nil
}
responses := make([]*Response, len(messages))
for idx, message := range messages {
// Encode message
request, err := message.Encode(c.gateway)
// Create response nomatter what
response := &Response{}
responses[idx] = response
// Define required info
response.ApnsID = message.ApnsID
response.DeviceID = message.deviceID
response.DeviceToken = base64.StdEncoding.EncodeToString(message.deviceToken)
/* Condition validation: validate encoding process */
if err != nil {
response.Status = 400
response.StatusDescription = StatusCodes[400]
response.Reason = err.Error()
response.ReasonDescription = ReasonCodes[err.Error()]
continue
}
// Send response to Apple server
res, err := c.client.Do(request)
if err == nil {
defer res.Body.Close()
// Define response status
response.Status = res.StatusCode
response.StatusDescription = StatusCodes[res.StatusCode]
if res.StatusCode != 200 {
decoder := json.NewDecoder(res.Body)
err = decoder.Decode(&response)
if err == nil {
response.ReasonDescription = ReasonCodes[response.Reason]
}
}
} else {
response.Status = 503
response.StatusDescription = StatusCodes[503]
response.Reason = "ServiceUnavailable"
response.ReasonDescription = ReasonCodes["ServiceUnavailable"]
}
}
return responses
}