Skip to content

Commit f74a6dd

Browse files
committed
[BC] add urgency and topic + TTL for each notification
1 parent 7f56de6 commit f74a6dd

File tree

3 files changed

+88
-52
lines changed

3 files changed

+88
-52
lines changed

README.md

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -88,45 +88,59 @@ $webPush = new WebPush($apiKeys);
8888
$webPush->sendNotification($endpoint, null, null, null, true);
8989
```
9090

91-
### Payload length and security
92-
Payload will be encrypted by the library. The maximum payload length is 4078 bytes (or ASCII characters).
93-
94-
However, when you encrypt a string of a certain length, the resulting string will always have the same length,
95-
no matter how many times you encrypt the initial string. This can make attackers guess the content of the payload.
96-
In order to circumvent this, this library can add some null padding to the initial payload, so that all the input of the encryption process
97-
will have the same length. This way, all the output of the encryption process will also have the same length and attackers won't be able to
98-
guess the content of your payload. The downside of this approach is that you will use more bandwidth than if you didn't pad the string.
99-
That's why the library provides the option to disable this security measure:
91+
### Notification options
92+
Each notification can have a specific Time To Live, urgency, and topic.
93+
You can change the default options with `setDefaultOptions()` or in the constructor:
10094

10195
```php
10296
<?php
10397

10498
use Minishlink\WebPush\WebPush;
10599

106-
$webPush = new WebPush();
107-
$webPush->setAutomaticPadding(false); // disable automatic padding
100+
$defaultOptions = array(
101+
'TTL' => 300, // defaults to 4 weeks
102+
'urgency' => 'normal',
103+
'topic' => 'new_event',
104+
);
105+
106+
// for every notifications
107+
$webPush = new WebPush(array(), $defaultOptions);
108+
$webPush->setDefaultOptions($defaultOptions);
109+
110+
// or for one notification
111+
$webPush->sendNotification($endpoint, $payload, $userPublicKey, $userAuthToken, true, array('TTL' => 5000));
108112
```
109113

110-
### Time To Live
114+
#### TTL
111115
Time To Live (TTL, in seconds) is how long a push message is retained by the push service (eg. Mozilla) in case the user browser
112116
is not yet accessible (eg. is not connected). You may want to use a very long time for important notifications. The default TTL is 4 weeks.
113117
However, if you send multiple nonessential notifications, set a TTL of 0: the push notification will be delivered only
114118
if the user is currently connected. For other cases, you should use a minimum of one day if your users have multiple time
115119
zones, and if they don't several hours will suffice.
116120

121+
#### urgency
122+
Urgency can be either "very-low", "low", "normal", or "high". If the browser vendor has implemented this feature, it will save battery life on mobile devices (cf. [protocol](https://tools.ietf.org/html/draft-ietf-webpush-protocol-08#section-5.3)).
123+
124+
#### topic
125+
Similar to the old `collapse_key` on legacy GCM servers, this string will make the vendor show to the user only the last notification of this topic (cf. [protocol]((cf. [protocol](https://tools.ietf.org/html/draft-ietf-webpush-protocol-08#section-5.4)))).
126+
127+
### Payload length and security
128+
Payload will be encrypted by the library. The maximum payload length is 4078 bytes (or ASCII characters).
129+
130+
However, when you encrypt a string of a certain length, the resulting string will always have the same length,
131+
no matter how many times you encrypt the initial string. This can make attackers guess the content of the payload.
132+
In order to circumvent this, this library can add some null padding to the initial payload, so that all the input of the encryption process
133+
will have the same length. This way, all the output of the encryption process will also have the same length and attackers won't be able to
134+
guess the content of your payload. The downside of this approach is that you will use more bandwidth than if you didn't pad the string.
135+
That's why the library provides the option to disable this security measure:
136+
117137
```php
118138
<?php
119139

120140
use Minishlink\WebPush\WebPush;
121141

122-
$webPush = new WebPush(); // default TTL is 4 weeks
123-
// send some important notifications...
124-
125-
$webPush->setTTL(3600);
126-
// send some not so important notifications
127-
128-
$webPush->setTTL(0);
129-
// send some trivial notifications
142+
$webPush = new WebPush();
143+
$webPush->setAutomaticPadding(false); // disable automatic padding
130144
```
131145

132146
### Changing the browser client
@@ -141,7 +155,7 @@ use Minishlink\WebPush\WebPush;
141155

142156
$client = new \Buzz\Client\Curl();
143157
$timeout = 20; // seconds
144-
$webPush = new WebPush(array(), null, $timeout, $client);
158+
$webPush = new WebPush(array(), array(), $timeout, $client);
145159
```
146160

147161
You have access to the inner browser if you want to configure it further.

src/Notification.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,16 @@ class Notification
2525
/** @var string */
2626
private $userAuthToken;
2727

28-
public function __construct($endpoint, $payload, $userPublicKey, $userAuthToken)
28+
/** @var array Options : TTL, urgency, topic **/
29+
private $options;
30+
31+
public function __construct($endpoint, $payload, $userPublicKey, $userAuthToken, $options)
2932
{
3033
$this->endpoint = $endpoint;
3134
$this->payload = $payload;
3235
$this->userPublicKey = $userPublicKey;
3336
$this->userAuthToken = $userAuthToken;
37+
$this->options = $options;
3438
}
3539

3640
/**
@@ -64,4 +68,19 @@ public function getUserAuthToken()
6468
{
6569
return $this->userAuthToken;
6670
}
71+
72+
/**
73+
* @param array $defaultOptions
74+
* @return array
75+
*/
76+
public function getOptions(array $defaultOptions = array())
77+
{
78+
$options = $this->options;
79+
$options['TTL'] = array_key_exists('TTL', $options) ? $options['TTL'] : $defaultOptions['TTL'];
80+
$options['urgency'] = array_key_exists('urgency', $options) ? $options['urgency'] : $defaultOptions['urgency'];
81+
$options['topic'] = array_key_exists('topic', $options) ? $options['topic'] : $defaultOptions['topic'];
82+
83+
return $options;
84+
}
85+
6786
}

src/WebPush.php

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ class WebPush
3535
'GCM' => self::GCM_URL,
3636
);
3737

38-
/** @var int Time To Live of notifications */
39-
private $TTL;
38+
/** @var array Default options : TTL, urgency, topic */
39+
private $defaultOptions;
4040

4141
/** @var bool Automatic padding of payloads, if disabled, trade security for bandwidth */
4242
private $automaticPadding = true;
@@ -48,14 +48,14 @@ class WebPush
4848
* WebPush constructor.
4949
*
5050
* @param array $apiKeys Some servers needs authentication. Provide your API keys here. (eg. array('GCM' => 'GCM_API_KEY'))
51-
* @param int|null $TTL Time To Live of notifications, default being 4 weeks.
51+
* @param array $defaultOptions TTL, urgency, topic
5252
* @param int|null $timeout Timeout of POST request
5353
* @param AbstractClient|null $client
5454
*/
55-
public function __construct(array $apiKeys = array(), $TTL = 2419200, $timeout = 30, AbstractClient $client = null)
55+
public function __construct(array $apiKeys = array(), $defaultOptions = array(), $timeout = 30, AbstractClient $client = null)
5656
{
5757
$this->apiKeys = $apiKeys;
58-
$this->TTL = $TTL;
58+
$this->setDefaultOptions($defaultOptions);
5959

6060
$client = isset($client) ? $client : new MultiCurl();
6161
$client->setTimeout($timeout);
@@ -72,17 +72,13 @@ public function __construct(array $apiKeys = array(), $TTL = 2419200, $timeout =
7272
* @param string|null $userPublicKey
7373
* @param string|null $userAuthToken
7474
* @param bool $flush If you want to flush directly (usually when you send only one notification)
75-
*
75+
* @param array $options Array with several options tied to this notification. If not set, will use the default options that you can set in the WebPush object.
7676
* @return array|bool Return an array of information if $flush is set to true and the queued requests has failed.
7777
* Else return true.
7878
* @throws \ErrorException
7979
*/
80-
public function sendNotification($endpoint, $payload = null, $userPublicKey = null, $userAuthToken = null, $flush = false)
80+
public function sendNotification($endpoint, $payload = null, $userPublicKey = null, $userAuthToken = null, $flush = false, $options = array())
8181
{
82-
if (isset($userAuthToken) && is_bool($userAuthToken)) {
83-
throw new \ErrorException('The API has changed: sendNotification now takes the optional user auth token as parameter.');
84-
}
85-
8682
if(isset($payload)) {
8783
if (strlen($payload) > Encryption::MAX_PAYLOAD_LENGTH) {
8884
throw new \ErrorException('Size of payload must not be greater than '.Encryption::MAX_PAYLOAD_LENGTH.' octets.');
@@ -93,7 +89,7 @@ public function sendNotification($endpoint, $payload = null, $userPublicKey = nu
9389

9490
// sort notification by server type
9591
$type = $this->sortEndpoint($endpoint);
96-
$this->notificationsByServerType[$type][] = new Notification($endpoint, $payload, $userPublicKey, $userAuthToken);
92+
$this->notificationsByServerType[$type][] = new Notification($endpoint, $payload, $userPublicKey, $userAuthToken, $options);
9793

9894
if ($flush) {
9995
$res = $this->flush();
@@ -190,6 +186,7 @@ private function prepareAndSend(array $notifications, $serverType)
190186
$payload = $notification->getPayload();
191187
$userPublicKey = $notification->getUserPublicKey();
192188
$userAuthToken = $notification->getUserAuthToken();
189+
$options = $notification->getOptions($this->getDefaultOptions());
193190

194191
if (isset($payload) && isset($userPublicKey) && isset($userAuthToken)) {
195192
$encrypted = Encryption::encrypt($payload, $userPublicKey, $userAuthToken, $this->nativePayloadEncryptionSupport);
@@ -200,19 +197,27 @@ private function prepareAndSend(array $notifications, $serverType)
200197
'Content-Encoding' => 'aesgcm',
201198
'Encryption' => 'keyid="p256dh";salt="'.$encrypted['salt'].'"',
202199
'Crypto-Key' => 'keyid="p256dh";dh="'.$encrypted['localPublicKey'].'"',
203-
'TTL' => $this->TTL,
204200
);
205201

206202
$content = $encrypted['cipherText'];
207203
} else {
208204
$headers = array(
209205
'Content-Length' => 0,
210-
'TTL' => $this->TTL,
211206
);
212207

213208
$content = '';
214209
}
215210

211+
$headers['TTL'] = $options['TTL'];
212+
213+
if (isset($options['urgency'])) {
214+
$headers['Urgency'] = $options['urgency'];
215+
}
216+
217+
if (isset($options['topic'])) {
218+
$headers['Topic'] = $options['topic'];
219+
}
220+
216221
if ($serverType === 'GCM') {
217222
$headers['Authorization'] = 'key='.$this->apiKeys['GCM'];
218223
}
@@ -278,42 +283,40 @@ public function setBrowser($browser)
278283
}
279284

280285
/**
281-
* @return int
286+
* @return boolean
282287
*/
283-
public function getTTL()
288+
public function isAutomaticPadding()
284289
{
285-
return $this->TTL;
290+
return $this->automaticPadding;
286291
}
287292

288293
/**
289-
* @param int $TTL
294+
* @param boolean $automaticPadding
290295
*
291296
* @return WebPush
292297
*/
293-
public function setTTL($TTL)
298+
public function setAutomaticPadding($automaticPadding)
294299
{
295-
$this->TTL = $TTL;
300+
$this->automaticPadding = $automaticPadding;
296301

297302
return $this;
298303
}
299304

300305
/**
301-
* @return boolean
306+
* @return array
302307
*/
303-
public function isAutomaticPadding()
308+
public function getDefaultOptions()
304309
{
305-
return $this->automaticPadding;
310+
return $this->defaultOptions;
306311
}
307312

308313
/**
309-
* @param boolean $automaticPadding
310-
*
311-
* @return WebPush
314+
* @param array $defaultOptions Keys 'TTL' (Time To Live, defaults 4 weeks), 'urgency', and 'topic'
312315
*/
313-
public function setAutomaticPadding($automaticPadding)
316+
public function setDefaultOptions(array $defaultOptions)
314317
{
315-
$this->automaticPadding = $automaticPadding;
316-
317-
return $this;
318+
$this->defaultOptions['TTL'] = array_key_exists('TTL', $defaultOptions) ? $defaultOptions['TTL'] : 2419200;
319+
$this->defaultOptions['urgency'] = array_key_exists('urgency', $defaultOptions) ? $defaultOptions['urgency'] : null;
320+
$this->defaultOptions['topic'] = array_key_exists('topic', $defaultOptions) ? $defaultOptions['topic'] : null;
318321
}
319322
}

0 commit comments

Comments
 (0)