mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 04:41:11 +00:00
Bug 1874195 - Remove PushServiceHttp2 r=asuth
Differential Revision: https://phabricator.services.mozilla.com/D227265
This commit is contained in:
parent
8ea4bdb3a3
commit
5dd76590be
@ -2,14 +2,9 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
|
||||
|
||||
import { clearTimeout, setTimeout } from "resource://gre/modules/Timer.sys.mjs";
|
||||
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
||||
|
||||
export var PushServiceWebSocket;
|
||||
export var PushServiceHttp2;
|
||||
|
||||
const lazy = {};
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
@ -20,22 +15,10 @@ XPCOMUtils.defineLazyServiceGetter(
|
||||
);
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
PushCrypto: "resource://gre/modules/PushCrypto.sys.mjs",
|
||||
PushServiceWebSocket: "resource://gre/modules/PushServiceWebSocket.sys.mjs",
|
||||
pushBroadcastService: "resource://gre/modules/PushBroadcastService.sys.mjs",
|
||||
});
|
||||
|
||||
const CONNECTION_PROTOCOLS = (function () {
|
||||
if ("android" != AppConstants.MOZ_WIDGET_TOOLKIT) {
|
||||
({ PushServiceWebSocket } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/PushServiceWebSocket.sys.mjs"
|
||||
));
|
||||
({ PushServiceHttp2 } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/PushServiceHttp2.sys.mjs"
|
||||
));
|
||||
return [PushServiceWebSocket, PushServiceHttp2];
|
||||
}
|
||||
return [];
|
||||
})();
|
||||
|
||||
ChromeUtils.defineLazyGetter(lazy, "console", () => {
|
||||
let { ConsoleAPI } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/Console.sys.mjs"
|
||||
@ -84,17 +67,8 @@ function getServiceForServerURI(uri) {
|
||||
"testing.allowInsecureServerURL",
|
||||
false
|
||||
);
|
||||
if (AppConstants.MOZ_WIDGET_TOOLKIT == "android") {
|
||||
if (uri.scheme == "https" || (allowInsecure && uri.scheme == "http")) {
|
||||
return CONNECTION_PROTOCOLS;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (uri.scheme == "wss" || (allowInsecure && uri.scheme == "ws")) {
|
||||
return PushServiceWebSocket;
|
||||
}
|
||||
if (uri.scheme == "https" || (allowInsecure && uri.scheme == "http")) {
|
||||
return PushServiceHttp2;
|
||||
return lazy.PushServiceWebSocket;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -706,17 +680,6 @@ export var PushService = {
|
||||
.then(record => this._notifySubscriptionChangeObservers(record));
|
||||
},
|
||||
|
||||
/**
|
||||
* Replaces an existing registration and notifies the associated service
|
||||
* worker.
|
||||
*
|
||||
* @param {String} aOldKey The registration ID to replace.
|
||||
* @param {PushRecord} aNewRecord The new record.
|
||||
* @returns {Promise} Resolves once the worker has been notified.
|
||||
*/
|
||||
updateRegistrationAndNotifyApp(aOldKey, aNewRecord) {
|
||||
return this.updateRecordAndNotifyApp(aOldKey, _ => aNewRecord);
|
||||
},
|
||||
/**
|
||||
* Updates a registration and notifies the associated service worker.
|
||||
*
|
||||
|
@ -1,824 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { PushDB } from "resource://gre/modules/PushDB.sys.mjs";
|
||||
import { PushRecord } from "resource://gre/modules/PushRecord.sys.mjs";
|
||||
|
||||
import { NetUtil } from "resource://gre/modules/NetUtil.sys.mjs";
|
||||
import { clearTimeout, setTimeout } from "resource://gre/modules/Timer.sys.mjs";
|
||||
|
||||
import { PushCrypto } from "resource://gre/modules/PushCrypto.sys.mjs";
|
||||
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineLazyGetter(lazy, "console", () => {
|
||||
let { ConsoleAPI } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/Console.sys.mjs"
|
||||
);
|
||||
return new ConsoleAPI({
|
||||
maxLogLevelPref: "dom.push.loglevel",
|
||||
prefix: "PushServiceHttp2",
|
||||
});
|
||||
});
|
||||
|
||||
const prefs = Services.prefs.getBranch("dom.push.");
|
||||
|
||||
const kPUSHHTTP2DB_DB_NAME = "pushHttp2";
|
||||
const kPUSHHTTP2DB_DB_VERSION = 5; // Change this if the IndexedDB format changes
|
||||
const kPUSHHTTP2DB_STORE_NAME = "pushHttp2";
|
||||
|
||||
/**
|
||||
* A proxy between the PushService and connections listening for incoming push
|
||||
* messages. The PushService can silence messages from the connections by
|
||||
* setting PushSubscriptionListener._pushService to null. This is required
|
||||
* because it can happen that there is an outstanding push message that will
|
||||
* be send on OnStopRequest but the PushService may not be interested in these.
|
||||
* It's easier to stop listening than to have checks at specific points.
|
||||
*/
|
||||
var PushSubscriptionListener = function (pushService, uri) {
|
||||
lazy.console.debug("PushSubscriptionListener()");
|
||||
this._pushService = pushService;
|
||||
this.uri = uri;
|
||||
};
|
||||
|
||||
PushSubscriptionListener.prototype = {
|
||||
QueryInterface: ChromeUtils.generateQI([
|
||||
"nsIHttpPushListener",
|
||||
"nsIStreamListener",
|
||||
]),
|
||||
|
||||
getInterface(aIID) {
|
||||
return this.QueryInterface(aIID);
|
||||
},
|
||||
|
||||
onStartRequest() {
|
||||
lazy.console.debug("PushSubscriptionListener: onStartRequest()");
|
||||
// We do not do anything here.
|
||||
},
|
||||
|
||||
onDataAvailable(aRequest, aStream, aOffset, aCount) {
|
||||
lazy.console.debug("PushSubscriptionListener: onDataAvailable()");
|
||||
// Nobody should send data, but just to be sure, otherwise necko will
|
||||
// complain.
|
||||
if (aCount === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let inputStream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(
|
||||
Ci.nsIScriptableInputStream
|
||||
);
|
||||
|
||||
inputStream.init(aStream);
|
||||
inputStream.read(aCount);
|
||||
},
|
||||
|
||||
onStopRequest(aRequest, aStatusCode) {
|
||||
lazy.console.debug("PushSubscriptionListener: onStopRequest()");
|
||||
if (!this._pushService) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._pushService.connOnStop(
|
||||
aRequest,
|
||||
Components.isSuccessCode(aStatusCode),
|
||||
this.uri
|
||||
);
|
||||
},
|
||||
|
||||
onPush(associatedChannel, pushChannel) {
|
||||
lazy.console.debug("PushSubscriptionListener: onPush()");
|
||||
var pushChannelListener = new PushChannelListener(this);
|
||||
pushChannel.asyncOpen(pushChannelListener);
|
||||
},
|
||||
|
||||
disconnect() {
|
||||
this._pushService = null;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* The listener for pushed messages. The message data is collected in
|
||||
* OnDataAvailable and send to the app in OnStopRequest.
|
||||
*/
|
||||
var PushChannelListener = function (pushSubscriptionListener) {
|
||||
lazy.console.debug("PushChannelListener()");
|
||||
this._mainListener = pushSubscriptionListener;
|
||||
this._message = [];
|
||||
this._ackUri = null;
|
||||
};
|
||||
|
||||
PushChannelListener.prototype = {
|
||||
onStartRequest(aRequest) {
|
||||
this._ackUri = aRequest.URI.spec;
|
||||
},
|
||||
|
||||
onDataAvailable(aRequest, aStream, aOffset, aCount) {
|
||||
lazy.console.debug("PushChannelListener: onDataAvailable()");
|
||||
|
||||
if (aCount === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let inputStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
|
||||
Ci.nsIBinaryInputStream
|
||||
);
|
||||
|
||||
inputStream.setInputStream(aStream);
|
||||
let chunk = new ArrayBuffer(aCount);
|
||||
inputStream.readArrayBuffer(aCount, chunk);
|
||||
this._message.push(chunk);
|
||||
},
|
||||
|
||||
onStopRequest(aRequest, aStatusCode) {
|
||||
lazy.console.debug(
|
||||
"PushChannelListener: onStopRequest()",
|
||||
"status code",
|
||||
aStatusCode
|
||||
);
|
||||
if (
|
||||
Components.isSuccessCode(aStatusCode) &&
|
||||
this._mainListener &&
|
||||
this._mainListener._pushService
|
||||
) {
|
||||
let headers = {
|
||||
encryption_key: getHeaderField(aRequest, "Encryption-Key"),
|
||||
crypto_key: getHeaderField(aRequest, "Crypto-Key"),
|
||||
encryption: getHeaderField(aRequest, "Encryption"),
|
||||
encoding: getHeaderField(aRequest, "Content-Encoding"),
|
||||
};
|
||||
let msg = PushCrypto.concatArray(this._message);
|
||||
|
||||
this._mainListener._pushService._pushChannelOnStop(
|
||||
this._mainListener.uri,
|
||||
this._ackUri,
|
||||
headers,
|
||||
msg
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function getHeaderField(aRequest, name) {
|
||||
try {
|
||||
return aRequest.getRequestHeader(name);
|
||||
} catch (e) {
|
||||
// getRequestHeader can throw.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
var PushServiceDelete = function (resolve, reject) {
|
||||
this._resolve = resolve;
|
||||
this._reject = reject;
|
||||
};
|
||||
|
||||
PushServiceDelete.prototype = {
|
||||
onStartRequest() {},
|
||||
|
||||
onDataAvailable(aRequest, aStream, aOffset, aCount) {
|
||||
// Nobody should send data, but just to be sure, otherwise necko will
|
||||
// complain.
|
||||
if (aCount === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let inputStream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(
|
||||
Ci.nsIScriptableInputStream
|
||||
);
|
||||
|
||||
inputStream.init(aStream);
|
||||
inputStream.read(aCount);
|
||||
},
|
||||
|
||||
onStopRequest(aRequest, aStatusCode) {
|
||||
if (Components.isSuccessCode(aStatusCode)) {
|
||||
this._resolve();
|
||||
} else {
|
||||
this._reject(new Error("Error removing subscription: " + aStatusCode));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
var SubscriptionListener = function (
|
||||
aSubInfo,
|
||||
aResolve,
|
||||
aReject,
|
||||
aServerURI,
|
||||
aPushServiceHttp2
|
||||
) {
|
||||
lazy.console.debug("SubscriptionListener()");
|
||||
this._subInfo = aSubInfo;
|
||||
this._resolve = aResolve;
|
||||
this._reject = aReject;
|
||||
this._serverURI = aServerURI;
|
||||
this._service = aPushServiceHttp2;
|
||||
this._ctime = Date.now();
|
||||
this._retryTimeoutID = null;
|
||||
};
|
||||
|
||||
SubscriptionListener.prototype = {
|
||||
onStartRequest() {},
|
||||
|
||||
onDataAvailable() {},
|
||||
|
||||
onStopRequest(aRequest, aStatus) {
|
||||
lazy.console.debug("SubscriptionListener: onStopRequest()");
|
||||
|
||||
// Check if pushService is still active.
|
||||
if (!this._service.hasmainPushService()) {
|
||||
this._reject(new Error("Push service unavailable"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Components.isSuccessCode(aStatus)) {
|
||||
this._reject(new Error("Error listening for messages: " + aStatus));
|
||||
return;
|
||||
}
|
||||
|
||||
var statusCode = aRequest.QueryInterface(Ci.nsIHttpChannel).responseStatus;
|
||||
|
||||
if (Math.floor(statusCode / 100) == 5) {
|
||||
if (this._subInfo.retries < prefs.getIntPref("http2.maxRetries")) {
|
||||
this._subInfo.retries++;
|
||||
var retryAfter = retryAfterParser(aRequest);
|
||||
this._retryTimeoutID = setTimeout(_ => {
|
||||
this._reject({
|
||||
retry: true,
|
||||
subInfo: this._subInfo,
|
||||
});
|
||||
this._service.removeListenerPendingRetry(this);
|
||||
this._retryTimeoutID = null;
|
||||
}, retryAfter);
|
||||
this._service.addListenerPendingRetry(this);
|
||||
} else {
|
||||
this._reject(new Error("Unexpected server response: " + statusCode));
|
||||
}
|
||||
return;
|
||||
} else if (statusCode != 201) {
|
||||
this._reject(new Error("Unexpected server response: " + statusCode));
|
||||
return;
|
||||
}
|
||||
|
||||
var subscriptionUri;
|
||||
try {
|
||||
subscriptionUri = aRequest.getResponseHeader("location");
|
||||
} catch (err) {
|
||||
this._reject(new Error("Missing Location header"));
|
||||
return;
|
||||
}
|
||||
|
||||
lazy.console.debug("onStopRequest: subscriptionUri", subscriptionUri);
|
||||
|
||||
var linkList;
|
||||
try {
|
||||
linkList = aRequest.getResponseHeader("link");
|
||||
} catch (err) {
|
||||
this._reject(new Error("Missing Link header"));
|
||||
return;
|
||||
}
|
||||
|
||||
var linkParserResult;
|
||||
try {
|
||||
linkParserResult = linkParser(linkList, this._serverURI);
|
||||
} catch (e) {
|
||||
this._reject(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!subscriptionUri) {
|
||||
this._reject(new Error("Invalid Location header"));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Services.io.newURI(subscriptionUri);
|
||||
} catch (e) {
|
||||
lazy.console.error(
|
||||
"onStopRequest: Invalid subscription URI",
|
||||
subscriptionUri
|
||||
);
|
||||
this._reject(
|
||||
new Error("Invalid subscription endpoint: " + subscriptionUri)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let reply = new PushRecordHttp2({
|
||||
subscriptionUri,
|
||||
pushEndpoint: linkParserResult.pushEndpoint,
|
||||
pushReceiptEndpoint: linkParserResult.pushReceiptEndpoint,
|
||||
scope: this._subInfo.record.scope,
|
||||
originAttributes: this._subInfo.record.originAttributes,
|
||||
systemRecord: this._subInfo.record.systemRecord,
|
||||
appServerKey: this._subInfo.record.appServerKey,
|
||||
ctime: Date.now(),
|
||||
});
|
||||
|
||||
this._resolve(reply);
|
||||
},
|
||||
|
||||
abortRetry() {
|
||||
if (this._retryTimeoutID != null) {
|
||||
clearTimeout(this._retryTimeoutID);
|
||||
this._retryTimeoutID = null;
|
||||
} else {
|
||||
lazy.console.debug(
|
||||
"SubscriptionListener.abortRetry: aborting non-existent retry?"
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function retryAfterParser(aRequest) {
|
||||
let retryAfter = 0;
|
||||
try {
|
||||
let retryField = aRequest.getResponseHeader("retry-after");
|
||||
if (isNaN(retryField)) {
|
||||
retryAfter = Date.parse(retryField) - new Date().getTime();
|
||||
} else {
|
||||
retryAfter = parseInt(retryField, 10) * 1000;
|
||||
}
|
||||
retryAfter = retryAfter > 0 ? retryAfter : 0;
|
||||
} catch (e) {}
|
||||
|
||||
return retryAfter;
|
||||
}
|
||||
|
||||
function linkParser(linkHeader, serverURI) {
|
||||
let linkList = linkHeader.split(",");
|
||||
if (linkList.length < 1) {
|
||||
throw new Error("Invalid Link header");
|
||||
}
|
||||
|
||||
let pushEndpoint;
|
||||
let pushReceiptEndpoint;
|
||||
|
||||
linkList.forEach(link => {
|
||||
let linkElems = link.split(";");
|
||||
|
||||
if (linkElems.length == 2) {
|
||||
if (linkElems[1].trim() === 'rel="urn:ietf:params:push"') {
|
||||
pushEndpoint = linkElems[0].substring(
|
||||
linkElems[0].indexOf("<") + 1,
|
||||
linkElems[0].indexOf(">")
|
||||
);
|
||||
} else if (linkElems[1].trim() === 'rel="urn:ietf:params:push:receipt"') {
|
||||
pushReceiptEndpoint = linkElems[0].substring(
|
||||
linkElems[0].indexOf("<") + 1,
|
||||
linkElems[0].indexOf(">")
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
lazy.console.debug("linkParser: pushEndpoint", pushEndpoint);
|
||||
lazy.console.debug("linkParser: pushReceiptEndpoint", pushReceiptEndpoint);
|
||||
// Missing pushReceiptEndpoint is allowed.
|
||||
if (!pushEndpoint) {
|
||||
throw new Error("Missing push endpoint");
|
||||
}
|
||||
|
||||
const pushURI = Services.io.newURI(pushEndpoint, null, serverURI);
|
||||
let pushReceiptURI;
|
||||
if (pushReceiptEndpoint) {
|
||||
pushReceiptURI = Services.io.newURI(pushReceiptEndpoint, null, serverURI);
|
||||
}
|
||||
|
||||
return {
|
||||
pushEndpoint: pushURI.spec,
|
||||
pushReceiptEndpoint: pushReceiptURI ? pushReceiptURI.spec : "",
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of the WebPush.
|
||||
*/
|
||||
export var PushServiceHttp2 = {
|
||||
_mainPushService: null,
|
||||
_serverURI: null,
|
||||
|
||||
// Keep information about all connections, e.g. the channel, listener...
|
||||
_conns: {},
|
||||
_started: false,
|
||||
|
||||
// Set of SubscriptionListeners that are pending a subscription retry attempt.
|
||||
_listenersPendingRetry: new Set(),
|
||||
|
||||
newPushDB() {
|
||||
return new PushDB(
|
||||
kPUSHHTTP2DB_DB_NAME,
|
||||
kPUSHHTTP2DB_DB_VERSION,
|
||||
kPUSHHTTP2DB_STORE_NAME,
|
||||
"subscriptionUri",
|
||||
PushRecordHttp2
|
||||
);
|
||||
},
|
||||
|
||||
hasmainPushService() {
|
||||
return this._mainPushService !== null;
|
||||
},
|
||||
|
||||
async connect() {
|
||||
let subscriptions = await this._mainPushService.getAllUnexpired();
|
||||
this.startConnections(subscriptions);
|
||||
},
|
||||
|
||||
async sendSubscribeBroadcast() {
|
||||
// Not implemented yet
|
||||
},
|
||||
|
||||
isConnected() {
|
||||
return this._mainPushService != null;
|
||||
},
|
||||
|
||||
disconnect() {
|
||||
this._shutdownConnections(false);
|
||||
},
|
||||
|
||||
_makeChannel(aUri) {
|
||||
var chan = NetUtil.newChannel({
|
||||
uri: aUri,
|
||||
loadUsingSystemPrincipal: true,
|
||||
}).QueryInterface(Ci.nsIHttpChannel);
|
||||
|
||||
var loadGroup = Cc["@mozilla.org/network/load-group;1"].createInstance(
|
||||
Ci.nsILoadGroup
|
||||
);
|
||||
chan.loadGroup = loadGroup;
|
||||
return chan;
|
||||
},
|
||||
|
||||
/**
|
||||
* Subscribe new resource.
|
||||
*/
|
||||
register(aRecord) {
|
||||
lazy.console.debug("subscribeResource()");
|
||||
|
||||
return this._subscribeResourceInternal({
|
||||
record: aRecord,
|
||||
retries: 0,
|
||||
}).then(result =>
|
||||
PushCrypto.generateKeys().then(([publicKey, privateKey]) => {
|
||||
result.p256dhPublicKey = publicKey;
|
||||
result.p256dhPrivateKey = privateKey;
|
||||
result.authenticationSecret = PushCrypto.generateAuthenticationSecret();
|
||||
this._conns[result.subscriptionUri] = {
|
||||
channel: null,
|
||||
listener: null,
|
||||
countUnableToConnect: 0,
|
||||
lastStartListening: 0,
|
||||
retryTimerID: 0,
|
||||
};
|
||||
this._listenForMsgs(result.subscriptionUri);
|
||||
return result;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
_subscribeResourceInternal(aSubInfo) {
|
||||
lazy.console.debug("subscribeResourceInternal()");
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
var listener = new SubscriptionListener(
|
||||
aSubInfo,
|
||||
resolve,
|
||||
reject,
|
||||
this._serverURI,
|
||||
this
|
||||
);
|
||||
|
||||
var chan = this._makeChannel(this._serverURI.spec);
|
||||
chan.requestMethod = "POST";
|
||||
chan.asyncOpen(listener);
|
||||
}).catch(err => {
|
||||
if ("retry" in err) {
|
||||
return this._subscribeResourceInternal(err.subInfo);
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
},
|
||||
|
||||
_deleteResource(aUri) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var chan = this._makeChannel(aUri);
|
||||
chan.requestMethod = "DELETE";
|
||||
chan.asyncOpen(new PushServiceDelete(resolve, reject));
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Unsubscribe the resource with a subscription uri aSubscriptionUri.
|
||||
* We can't do anything about it if it fails, so we don't listen for response.
|
||||
*/
|
||||
_unsubscribeResource(aSubscriptionUri) {
|
||||
lazy.console.debug("unsubscribeResource()");
|
||||
|
||||
return this._deleteResource(aSubscriptionUri);
|
||||
},
|
||||
|
||||
/**
|
||||
* Start listening for messages.
|
||||
*/
|
||||
_listenForMsgs(aSubscriptionUri) {
|
||||
lazy.console.debug("listenForMsgs()", aSubscriptionUri);
|
||||
if (!this._conns[aSubscriptionUri]) {
|
||||
lazy.console.warn(
|
||||
"listenForMsgs: We do not have this subscription",
|
||||
aSubscriptionUri
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var chan = this._makeChannel(aSubscriptionUri);
|
||||
var conn = {};
|
||||
conn.channel = chan;
|
||||
var listener = new PushSubscriptionListener(this, aSubscriptionUri);
|
||||
conn.listener = listener;
|
||||
|
||||
chan.notificationCallbacks = listener;
|
||||
|
||||
try {
|
||||
chan.asyncOpen(listener);
|
||||
} catch (e) {
|
||||
lazy.console.error(
|
||||
"listenForMsgs: Error connecting to push server.",
|
||||
"asyncOpen failed",
|
||||
e
|
||||
);
|
||||
conn.listener.disconnect();
|
||||
chan.cancel(Cr.NS_ERROR_ABORT);
|
||||
this._retryAfterBackoff(aSubscriptionUri, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
this._conns[aSubscriptionUri].lastStartListening = Date.now();
|
||||
this._conns[aSubscriptionUri].channel = conn.channel;
|
||||
this._conns[aSubscriptionUri].listener = conn.listener;
|
||||
},
|
||||
|
||||
_ackMsgRecv(aAckUri) {
|
||||
lazy.console.debug("ackMsgRecv()", aAckUri);
|
||||
return this._deleteResource(aAckUri);
|
||||
},
|
||||
|
||||
init(aOptions, aMainPushService, aServerURL) {
|
||||
lazy.console.debug("init()");
|
||||
this._mainPushService = aMainPushService;
|
||||
this._serverURI = aServerURL;
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
_retryAfterBackoff(aSubscriptionUri, retryAfter) {
|
||||
lazy.console.debug("retryAfterBackoff()");
|
||||
|
||||
var resetRetryCount = prefs.getIntPref("http2.reset_retry_count_after_ms");
|
||||
// If it was running for some time, reset retry counter.
|
||||
if (
|
||||
Date.now() - this._conns[aSubscriptionUri].lastStartListening >
|
||||
resetRetryCount
|
||||
) {
|
||||
this._conns[aSubscriptionUri].countUnableToConnect = 0;
|
||||
}
|
||||
|
||||
let maxRetries = prefs.getIntPref("http2.maxRetries");
|
||||
if (this._conns[aSubscriptionUri].countUnableToConnect >= maxRetries) {
|
||||
this._shutdownSubscription(aSubscriptionUri);
|
||||
this._resubscribe(aSubscriptionUri);
|
||||
return;
|
||||
}
|
||||
|
||||
if (retryAfter !== -1) {
|
||||
// This is a 5xx response.
|
||||
this._conns[aSubscriptionUri].countUnableToConnect++;
|
||||
this._conns[aSubscriptionUri].retryTimerID = setTimeout(
|
||||
_ => this._listenForMsgs(aSubscriptionUri),
|
||||
retryAfter
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
retryAfter =
|
||||
prefs.getIntPref("http2.retryInterval") *
|
||||
Math.pow(2, this._conns[aSubscriptionUri].countUnableToConnect);
|
||||
|
||||
retryAfter = retryAfter * (0.8 + Math.random() * 0.4); // add +/-20%.
|
||||
|
||||
this._conns[aSubscriptionUri].countUnableToConnect++;
|
||||
this._conns[aSubscriptionUri].retryTimerID = setTimeout(
|
||||
_ => this._listenForMsgs(aSubscriptionUri),
|
||||
retryAfter
|
||||
);
|
||||
|
||||
lazy.console.debug("retryAfterBackoff: Retry in", retryAfter);
|
||||
},
|
||||
|
||||
// Close connections.
|
||||
_shutdownConnections(deleteInfo) {
|
||||
lazy.console.debug("shutdownConnections()");
|
||||
|
||||
for (let subscriptionUri in this._conns) {
|
||||
if (this._conns[subscriptionUri]) {
|
||||
if (this._conns[subscriptionUri].listener) {
|
||||
this._conns[subscriptionUri].listener._pushService = null;
|
||||
}
|
||||
|
||||
if (this._conns[subscriptionUri].channel) {
|
||||
try {
|
||||
this._conns[subscriptionUri].channel.cancel(Cr.NS_ERROR_ABORT);
|
||||
} catch (e) {}
|
||||
}
|
||||
this._conns[subscriptionUri].listener = null;
|
||||
this._conns[subscriptionUri].channel = null;
|
||||
|
||||
if (this._conns[subscriptionUri].retryTimerID > 0) {
|
||||
clearTimeout(this._conns[subscriptionUri].retryTimerID);
|
||||
}
|
||||
|
||||
if (deleteInfo) {
|
||||
delete this._conns[subscriptionUri];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Start listening if subscriptions present.
|
||||
startConnections(aSubscriptions) {
|
||||
lazy.console.debug("startConnections()", aSubscriptions.length);
|
||||
|
||||
for (let i = 0; i < aSubscriptions.length; i++) {
|
||||
let record = aSubscriptions[i];
|
||||
this._mainPushService.ensureCrypto(record).then(
|
||||
record => {
|
||||
this._startSingleConnection(record);
|
||||
},
|
||||
error => {
|
||||
lazy.console.error(
|
||||
"startConnections: Error updating record",
|
||||
record.keyID,
|
||||
error
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
_startSingleConnection(record) {
|
||||
lazy.console.debug("_startSingleConnection()");
|
||||
if (typeof this._conns[record.subscriptionUri] != "object") {
|
||||
this._conns[record.subscriptionUri] = {
|
||||
channel: null,
|
||||
listener: null,
|
||||
countUnableToConnect: 0,
|
||||
retryTimerID: 0,
|
||||
};
|
||||
}
|
||||
if (!this._conns[record.subscriptionUri].conn) {
|
||||
this._listenForMsgs(record.subscriptionUri);
|
||||
}
|
||||
},
|
||||
|
||||
// Close connection and notify apps that subscription are gone.
|
||||
_shutdownSubscription(aSubscriptionUri) {
|
||||
lazy.console.debug("shutdownSubscriptions()");
|
||||
|
||||
if (typeof this._conns[aSubscriptionUri] == "object") {
|
||||
if (this._conns[aSubscriptionUri].listener) {
|
||||
this._conns[aSubscriptionUri].listener._pushService = null;
|
||||
}
|
||||
|
||||
if (this._conns[aSubscriptionUri].channel) {
|
||||
try {
|
||||
this._conns[aSubscriptionUri].channel.cancel(Cr.NS_ERROR_ABORT);
|
||||
} catch (e) {}
|
||||
}
|
||||
delete this._conns[aSubscriptionUri];
|
||||
}
|
||||
},
|
||||
|
||||
uninit() {
|
||||
lazy.console.debug("uninit()");
|
||||
this._abortPendingSubscriptionRetries();
|
||||
this._shutdownConnections(true);
|
||||
this._mainPushService = null;
|
||||
},
|
||||
|
||||
_abortPendingSubscriptionRetries() {
|
||||
this._listenersPendingRetry.forEach(listener => listener.abortRetry());
|
||||
this._listenersPendingRetry.clear();
|
||||
},
|
||||
|
||||
unregister(aRecord) {
|
||||
this._shutdownSubscription(aRecord.subscriptionUri);
|
||||
return this._unsubscribeResource(aRecord.subscriptionUri);
|
||||
},
|
||||
|
||||
reportDeliveryError(messageID, reason) {
|
||||
lazy.console.warn(
|
||||
"reportDeliveryError: Ignoring message delivery error",
|
||||
messageID,
|
||||
reason
|
||||
);
|
||||
},
|
||||
|
||||
/** Push server has deleted subscription.
|
||||
* Re-subscribe - if it succeeds send update db record and send
|
||||
* pushsubscriptionchange,
|
||||
* - on error delete record and send pushsubscriptionchange
|
||||
* TODO: maybe pushsubscriptionerror will be included.
|
||||
*/
|
||||
_resubscribe(aSubscriptionUri) {
|
||||
this._mainPushService.getByKeyID(aSubscriptionUri).then(record =>
|
||||
this.register(record).then(
|
||||
recordNew => {
|
||||
if (this._mainPushService) {
|
||||
this._mainPushService
|
||||
.updateRegistrationAndNotifyApp(aSubscriptionUri, recordNew)
|
||||
.catch(console.error);
|
||||
}
|
||||
},
|
||||
() => {
|
||||
if (this._mainPushService) {
|
||||
this._mainPushService
|
||||
.dropRegistrationAndNotifyApp(aSubscriptionUri)
|
||||
.catch(console.error);
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
connOnStop(aRequest, aSuccess, aSubscriptionUri) {
|
||||
lazy.console.debug("connOnStop() succeeded", aSuccess);
|
||||
|
||||
var conn = this._conns[aSubscriptionUri];
|
||||
if (!conn) {
|
||||
// there is no connection description that means that we closed
|
||||
// connection, so do nothing. But we should have already deleted
|
||||
// the listener.
|
||||
return;
|
||||
}
|
||||
|
||||
conn.channel = null;
|
||||
conn.listener = null;
|
||||
|
||||
if (!aSuccess) {
|
||||
this._retryAfterBackoff(aSubscriptionUri, -1);
|
||||
} else if (Math.floor(aRequest.responseStatus / 100) == 5) {
|
||||
var retryAfter = retryAfterParser(aRequest);
|
||||
this._retryAfterBackoff(aSubscriptionUri, retryAfter);
|
||||
} else if (Math.floor(aRequest.responseStatus / 100) == 4) {
|
||||
this._shutdownSubscription(aSubscriptionUri);
|
||||
this._resubscribe(aSubscriptionUri);
|
||||
} else if (Math.floor(aRequest.responseStatus / 100) == 2) {
|
||||
// This should be 204
|
||||
setTimeout(_ => this._listenForMsgs(aSubscriptionUri), 0);
|
||||
} else {
|
||||
this._retryAfterBackoff(aSubscriptionUri, -1);
|
||||
}
|
||||
},
|
||||
|
||||
addListenerPendingRetry(aListener) {
|
||||
this._listenersPendingRetry.add(aListener);
|
||||
},
|
||||
|
||||
removeListenerPendingRetry(aListener) {
|
||||
if (!this._listenersPendingRetry.remove(aListener)) {
|
||||
lazy.console.debug("removeListenerPendingRetry: listener not in list?");
|
||||
}
|
||||
},
|
||||
|
||||
_pushChannelOnStop(aUri, aAckUri, aHeaders, aMessage) {
|
||||
lazy.console.debug("pushChannelOnStop()");
|
||||
|
||||
this._mainPushService
|
||||
.receivedPushMessage(aUri, "", aHeaders, aMessage, record => {
|
||||
// Always update the stored record.
|
||||
return record;
|
||||
})
|
||||
.then(_ => this._ackMsgRecv(aAckUri))
|
||||
.catch(err => {
|
||||
lazy.console.error("pushChannelOnStop: Error receiving message", err);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
function PushRecordHttp2(record) {
|
||||
PushRecord.call(this, record);
|
||||
this.subscriptionUri = record.subscriptionUri;
|
||||
this.pushReceiptEndpoint = record.pushReceiptEndpoint;
|
||||
}
|
||||
|
||||
PushRecordHttp2.prototype = Object.create(PushRecord.prototype, {
|
||||
keyID: {
|
||||
get() {
|
||||
return this.subscriptionUri;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
PushRecordHttp2.prototype.toSubscription = function () {
|
||||
let subscription = PushRecord.prototype.toSubscription.call(this);
|
||||
subscription.pushReceiptEndpoint = this.pushReceiptEndpoint;
|
||||
return subscription;
|
||||
};
|
@ -23,7 +23,6 @@ EXTRA_JS_MODULES += [
|
||||
if CONFIG["MOZ_BUILD_APP"] != "mobile/android":
|
||||
# Everything but GeckoView.
|
||||
EXTRA_JS_MODULES += [
|
||||
"PushServiceHttp2.sys.mjs",
|
||||
"PushServiceWebSocket.sys.mjs",
|
||||
]
|
||||
|
||||
|
@ -1,44 +0,0 @@
|
||||
const { NetUtil } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/NetUtil.sys.mjs"
|
||||
);
|
||||
|
||||
// Returns the test H/2 server port, throwing if it's missing or invalid.
|
||||
function getTestServerPort() {
|
||||
let portEnv = Services.env.get("MOZHTTP2_PORT");
|
||||
let port = parseInt(portEnv, 10);
|
||||
if (!Number.isFinite(port) || port < 1 || port > 65535) {
|
||||
throw new Error(`Invalid port in MOZHTTP2_PORT env var: ${portEnv}`);
|
||||
}
|
||||
info(`Using HTTP/2 server on port ${port}`);
|
||||
return port;
|
||||
}
|
||||
|
||||
function readFile(file) {
|
||||
let fstream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
|
||||
Ci.nsIFileInputStream
|
||||
);
|
||||
fstream.init(file, -1, 0, 0);
|
||||
let data = NetUtil.readInputStreamToString(fstream, fstream.available());
|
||||
fstream.close();
|
||||
return data;
|
||||
}
|
||||
|
||||
function addCertFromFile(certdb, filename, trustString) {
|
||||
let certFile = do_get_file(filename, false);
|
||||
let pem = readFile(certFile)
|
||||
.replace(/-----BEGIN CERTIFICATE-----/, "")
|
||||
.replace(/-----END CERTIFICATE-----/, "")
|
||||
.replace(/[\r\n]/g, "");
|
||||
certdb.addCertFromBase64(pem, trustString);
|
||||
}
|
||||
|
||||
function trustHttp2CA() {
|
||||
let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
|
||||
Ci.nsIX509CertDB
|
||||
);
|
||||
addCertFromFile(
|
||||
certdb,
|
||||
"../../../../netwerk/test/unit/http2-ca.pem",
|
||||
"CTu,u,u"
|
||||
);
|
||||
}
|
@ -15,8 +15,7 @@ ChromeUtils.defineESModuleGetters(this, {
|
||||
Preferences: "resource://gre/modules/Preferences.sys.mjs",
|
||||
PushCrypto: "resource://gre/modules/PushCrypto.sys.mjs",
|
||||
PushService: "resource://gre/modules/PushService.sys.mjs",
|
||||
PushServiceHttp2: "resource://gre/modules/PushService.sys.mjs",
|
||||
PushServiceWebSocket: "resource://gre/modules/PushService.sys.mjs",
|
||||
PushServiceWebSocket: "resource://gre/modules/PushServiceWebSocket.sys.mjs",
|
||||
pushBroadcastService: "resource://gre/modules/PushBroadcastService.sys.mjs",
|
||||
});
|
||||
|
||||
@ -169,9 +168,6 @@ function setPrefs(prefs = {}) {
|
||||
retryBaseInterval: 5000,
|
||||
pingInterval: 30 * 60 * 1000,
|
||||
// Misc. defaults.
|
||||
"http2.maxRetries": 2,
|
||||
"http2.retryInterval": 500,
|
||||
"http2.reset_retry_count_after_ms": 60000,
|
||||
maxQuotaPerSubscription: 16,
|
||||
quotaUpdateDelay: 3000,
|
||||
"testing.notifyWorkers": false,
|
||||
|
@ -1,129 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { HttpServer } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/httpd.sys.mjs"
|
||||
);
|
||||
|
||||
var httpServer = null;
|
||||
|
||||
ChromeUtils.defineLazyGetter(this, "serverPort", function () {
|
||||
return httpServer.identity.primaryPort;
|
||||
});
|
||||
|
||||
var retries = 0;
|
||||
|
||||
function subscribe5xxCodeHandler(metadata, response) {
|
||||
if (retries == 0) {
|
||||
ok(true, "Subscribe 5xx code");
|
||||
do_test_finished();
|
||||
response.setHeader("Retry-After", "1");
|
||||
response.setStatusLine(metadata.httpVersion, 500, "Retry");
|
||||
} else {
|
||||
ok(true, "Subscribed");
|
||||
do_test_finished();
|
||||
response.setHeader(
|
||||
"Location",
|
||||
"http://localhost:" + serverPort + "/subscription"
|
||||
);
|
||||
response.setHeader(
|
||||
"Link",
|
||||
'</pushEndpoint>; rel="urn:ietf:params:push", ' +
|
||||
'</receiptPushEndpoint>; rel="urn:ietf:params:push:receipt"'
|
||||
);
|
||||
response.setStatusLine(metadata.httpVersion, 201, "OK");
|
||||
}
|
||||
retries++;
|
||||
}
|
||||
|
||||
function listenSuccessHandler(metadata, response) {
|
||||
Assert.ok(true, "New listener point");
|
||||
Assert.equal(retries, 2, "Should try 2 times.");
|
||||
do_test_finished();
|
||||
response.setHeader("Retry-After", "10");
|
||||
response.setStatusLine(metadata.httpVersion, 500, "Retry");
|
||||
}
|
||||
|
||||
httpServer = new HttpServer();
|
||||
httpServer.registerPathHandler("/subscribe5xxCode", subscribe5xxCodeHandler);
|
||||
httpServer.registerPathHandler("/subscription", listenSuccessHandler);
|
||||
httpServer.start(-1);
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
"testing.allowInsecureServerURL": true,
|
||||
"http2.retryInterval": 1000,
|
||||
"http2.maxRetries": 2,
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(async function test1() {
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
registerCleanupFunction(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
do_test_pending();
|
||||
do_test_pending();
|
||||
do_test_pending();
|
||||
do_test_pending();
|
||||
|
||||
var serverURL = "http://localhost:" + httpServer.identity.primaryPort;
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe5xxCode",
|
||||
db,
|
||||
});
|
||||
|
||||
let originAttributes = ChromeUtils.originAttributesToSuffix({
|
||||
inIsolatedMozBrowser: false,
|
||||
});
|
||||
let newRecord = await PushService.register({
|
||||
scope: "https://example.com/retry5xxCode",
|
||||
originAttributes,
|
||||
});
|
||||
|
||||
var subscriptionUri = serverURL + "/subscription";
|
||||
var pushEndpoint = serverURL + "/pushEndpoint";
|
||||
var pushReceiptEndpoint = serverURL + "/receiptPushEndpoint";
|
||||
equal(
|
||||
newRecord.endpoint,
|
||||
pushEndpoint,
|
||||
"Wrong push endpoint in registration record"
|
||||
);
|
||||
|
||||
equal(
|
||||
newRecord.pushReceiptEndpoint,
|
||||
pushReceiptEndpoint,
|
||||
"Wrong push endpoint receipt in registration record"
|
||||
);
|
||||
|
||||
let record = await db.getByKeyID(subscriptionUri);
|
||||
equal(
|
||||
record.subscriptionUri,
|
||||
subscriptionUri,
|
||||
"Wrong subscription ID in database record"
|
||||
);
|
||||
equal(
|
||||
record.pushEndpoint,
|
||||
pushEndpoint,
|
||||
"Wrong push endpoint in database record"
|
||||
);
|
||||
equal(
|
||||
record.pushReceiptEndpoint,
|
||||
pushReceiptEndpoint,
|
||||
"Wrong push endpoint receipt in database record"
|
||||
);
|
||||
equal(
|
||||
record.scope,
|
||||
"https://example.com/retry5xxCode",
|
||||
"Wrong scope in database record"
|
||||
);
|
||||
|
||||
httpServer.stop(do_test_finished);
|
||||
});
|
@ -1,38 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(async function test_registrations_error() {
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
registerCleanupFunction(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
PushService.init({
|
||||
serverURI: "https://push.example.org/",
|
||||
db: makeStub(db, {
|
||||
getByIdentifiers() {
|
||||
return Promise.reject("Database error");
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
await Assert.rejects(
|
||||
PushService.registration({
|
||||
scope: "https://example.net/1",
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix({
|
||||
inIsolatedMozBrowser: false,
|
||||
}),
|
||||
}),
|
||||
function (error) {
|
||||
return error == "Database error";
|
||||
},
|
||||
"Wrong message"
|
||||
);
|
||||
});
|
@ -1,77 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
var serverPort = -1;
|
||||
|
||||
function run_test() {
|
||||
serverPort = getTestServerPort();
|
||||
|
||||
do_get_profile();
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(async function test_pushNotifications() {
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
registerCleanupFunction(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
var serverURL = "https://localhost:" + serverPort;
|
||||
|
||||
let records = [
|
||||
{
|
||||
subscriptionUri: serverURL + "/subscriptionA",
|
||||
pushEndpoint: serverURL + "/pushEndpointA",
|
||||
pushReceiptEndpoint: serverURL + "/pushReceiptEndpointA",
|
||||
scope: "https://example.net/a",
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix({
|
||||
inIsolatedMozBrowser: false,
|
||||
}),
|
||||
quota: Infinity,
|
||||
},
|
||||
{
|
||||
subscriptionUri: serverURL + "/subscriptionB",
|
||||
pushEndpoint: serverURL + "/pushEndpointB",
|
||||
pushReceiptEndpoint: serverURL + "/pushReceiptEndpointB",
|
||||
scope: "https://example.net/b",
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix({
|
||||
inIsolatedMozBrowser: false,
|
||||
}),
|
||||
quota: Infinity,
|
||||
},
|
||||
{
|
||||
subscriptionUri: serverURL + "/subscriptionC",
|
||||
pushEndpoint: serverURL + "/pushEndpointC",
|
||||
pushReceiptEndpoint: serverURL + "/pushReceiptEndpointC",
|
||||
scope: "https://example.net/c",
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix({
|
||||
inIsolatedMozBrowser: false,
|
||||
}),
|
||||
quota: Infinity,
|
||||
},
|
||||
];
|
||||
|
||||
for (let record of records) {
|
||||
await db.put(record);
|
||||
}
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL,
|
||||
db,
|
||||
});
|
||||
|
||||
let registration = await PushService.registration({
|
||||
scope: "https://example.net/a",
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix({
|
||||
inIsolatedMozBrowser: false,
|
||||
}),
|
||||
});
|
||||
equal(
|
||||
registration.endpoint,
|
||||
serverURL + "/pushEndpointA",
|
||||
"Wrong push endpoint for scope"
|
||||
);
|
||||
});
|
@ -1,113 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { HttpServer } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/httpd.sys.mjs"
|
||||
);
|
||||
|
||||
var httpServer = null;
|
||||
|
||||
ChromeUtils.defineLazyGetter(this, "serverPort", function () {
|
||||
return httpServer.identity.primaryPort;
|
||||
});
|
||||
|
||||
var handlerDone;
|
||||
var handlerPromise = new Promise(r => (handlerDone = after(3, r)));
|
||||
|
||||
function listen4xxCodeHandler(metadata, response) {
|
||||
ok(true, "Listener point error");
|
||||
handlerDone();
|
||||
response.setStatusLine(metadata.httpVersion, 410, "GONE");
|
||||
}
|
||||
|
||||
function resubscribeHandler(metadata, response) {
|
||||
ok(true, "Ask for new subscription");
|
||||
handlerDone();
|
||||
response.setHeader(
|
||||
"Location",
|
||||
"http://localhost:" + serverPort + "/newSubscription"
|
||||
);
|
||||
response.setHeader(
|
||||
"Link",
|
||||
'</newPushEndpoint>; rel="urn:ietf:params:push", ' +
|
||||
'</newReceiptPushEndpoint>; rel="urn:ietf:params:push:receipt"'
|
||||
);
|
||||
response.setStatusLine(metadata.httpVersion, 201, "OK");
|
||||
}
|
||||
|
||||
function listenSuccessHandler(metadata, response) {
|
||||
Assert.ok(true, "New listener point");
|
||||
httpServer.stop(handlerDone);
|
||||
response.setStatusLine(metadata.httpVersion, 204, "Try again");
|
||||
}
|
||||
|
||||
httpServer = new HttpServer();
|
||||
httpServer.registerPathHandler("/subscription4xxCode", listen4xxCodeHandler);
|
||||
httpServer.registerPathHandler("/subscribe", resubscribeHandler);
|
||||
httpServer.registerPathHandler("/newSubscription", listenSuccessHandler);
|
||||
httpServer.start(-1);
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
|
||||
setPrefs({
|
||||
"testing.allowInsecureServerURL": true,
|
||||
"testing.notifyWorkers": false,
|
||||
"testing.notifyAllObservers": true,
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(async function test1() {
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
registerCleanupFunction(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
var serverURL = "http://localhost:" + httpServer.identity.primaryPort;
|
||||
|
||||
let records = [
|
||||
{
|
||||
subscriptionUri: serverURL + "/subscription4xxCode",
|
||||
pushEndpoint: serverURL + "/pushEndpoint",
|
||||
pushReceiptEndpoint: serverURL + "/pushReceiptEndpoint",
|
||||
scope: "https://example.com/page",
|
||||
originAttributes: "",
|
||||
quota: Infinity,
|
||||
},
|
||||
];
|
||||
|
||||
for (let record of records) {
|
||||
await db.put(record);
|
||||
}
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe",
|
||||
db,
|
||||
});
|
||||
|
||||
await handlerPromise;
|
||||
|
||||
let record = await db.getByIdentifiers({
|
||||
scope: "https://example.com/page",
|
||||
originAttributes: "",
|
||||
});
|
||||
equal(
|
||||
record.keyID,
|
||||
serverURL + "/newSubscription",
|
||||
"Should update subscription URL"
|
||||
);
|
||||
equal(
|
||||
record.pushEndpoint,
|
||||
serverURL + "/newPushEndpoint",
|
||||
"Should update push endpoint"
|
||||
);
|
||||
equal(
|
||||
record.pushReceiptEndpoint,
|
||||
serverURL + "/newReceiptPushEndpoint",
|
||||
"Should update push receipt endpoint"
|
||||
);
|
||||
});
|
@ -1,116 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { HttpServer } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/httpd.sys.mjs"
|
||||
);
|
||||
|
||||
var httpServer = null;
|
||||
|
||||
ChromeUtils.defineLazyGetter(this, "serverPort", function () {
|
||||
return httpServer.identity.primaryPort;
|
||||
});
|
||||
|
||||
var retries = 0;
|
||||
var handlerDone;
|
||||
var handlerPromise = new Promise(r => (handlerDone = after(5, r)));
|
||||
|
||||
function listen5xxCodeHandler(metadata, response) {
|
||||
ok(true, "Listener 5xx code");
|
||||
handlerDone();
|
||||
retries++;
|
||||
response.setHeader("Retry-After", "1");
|
||||
response.setStatusLine(metadata.httpVersion, 500, "Retry");
|
||||
}
|
||||
|
||||
function resubscribeHandler(metadata, response) {
|
||||
ok(true, "Ask for new subscription");
|
||||
Assert.equal(retries, 3, "Should retry 2 times.");
|
||||
handlerDone();
|
||||
response.setHeader(
|
||||
"Location",
|
||||
"http://localhost:" + serverPort + "/newSubscription"
|
||||
);
|
||||
response.setHeader(
|
||||
"Link",
|
||||
'</newPushEndpoint>; rel="urn:ietf:params:push", ' +
|
||||
'</newReceiptPushEndpoint>; rel="urn:ietf:params:push:receipt"'
|
||||
);
|
||||
response.setStatusLine(metadata.httpVersion, 201, "OK");
|
||||
}
|
||||
|
||||
function listenSuccessHandler(metadata, response) {
|
||||
Assert.ok(true, "New listener point");
|
||||
httpServer.stop(handlerDone);
|
||||
response.setStatusLine(metadata.httpVersion, 204, "Try again");
|
||||
}
|
||||
|
||||
httpServer = new HttpServer();
|
||||
httpServer.registerPathHandler("/subscription5xxCode", listen5xxCodeHandler);
|
||||
httpServer.registerPathHandler("/subscribe", resubscribeHandler);
|
||||
httpServer.registerPathHandler("/newSubscription", listenSuccessHandler);
|
||||
httpServer.start(-1);
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
"testing.allowInsecureServerURL": true,
|
||||
"http2.retryInterval": 1000,
|
||||
"http2.maxRetries": 2,
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(async function test1() {
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
registerCleanupFunction(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
var serverURL = "http://localhost:" + httpServer.identity.primaryPort;
|
||||
|
||||
let records = [
|
||||
{
|
||||
subscriptionUri: serverURL + "/subscription5xxCode",
|
||||
pushEndpoint: serverURL + "/pushEndpoint",
|
||||
pushReceiptEndpoint: serverURL + "/pushReceiptEndpoint",
|
||||
scope: "https://example.com/page",
|
||||
originAttributes: "",
|
||||
quota: Infinity,
|
||||
},
|
||||
];
|
||||
|
||||
for (let record of records) {
|
||||
await db.put(record);
|
||||
}
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe",
|
||||
db,
|
||||
});
|
||||
|
||||
await handlerPromise;
|
||||
|
||||
let record = await db.getByIdentifiers({
|
||||
scope: "https://example.com/page",
|
||||
originAttributes: "",
|
||||
});
|
||||
equal(
|
||||
record.keyID,
|
||||
serverURL + "/newSubscription",
|
||||
"Should update subscription URL"
|
||||
);
|
||||
equal(
|
||||
record.pushEndpoint,
|
||||
serverURL + "/newPushEndpoint",
|
||||
"Should update push endpoint"
|
||||
);
|
||||
equal(
|
||||
record.pushReceiptEndpoint,
|
||||
serverURL + "/newReceiptPushEndpoint",
|
||||
"Should update push receipt endpoint"
|
||||
);
|
||||
});
|
@ -1,116 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { HttpServer } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/httpd.sys.mjs"
|
||||
);
|
||||
|
||||
var httpServer = null;
|
||||
|
||||
ChromeUtils.defineLazyGetter(this, "serverPort", function () {
|
||||
return httpServer.identity.primaryPort;
|
||||
});
|
||||
|
||||
var handlerDone;
|
||||
var handlerPromise = new Promise(r => (handlerDone = after(2, r)));
|
||||
|
||||
function resubscribeHandler(metadata, response) {
|
||||
ok(true, "Ask for new subscription");
|
||||
handlerDone();
|
||||
response.setHeader(
|
||||
"Location",
|
||||
"http://localhost:" + serverPort + "/newSubscription"
|
||||
);
|
||||
response.setHeader(
|
||||
"Link",
|
||||
'</newPushEndpoint>; rel="urn:ietf:params:push", ' +
|
||||
'</newReceiptPushEndpoint>; rel="urn:ietf:params:push:receipt"'
|
||||
);
|
||||
response.setStatusLine(metadata.httpVersion, 201, "OK");
|
||||
}
|
||||
|
||||
function listenSuccessHandler(metadata, response) {
|
||||
Assert.ok(true, "New listener point");
|
||||
httpServer.stop(handlerDone);
|
||||
response.setStatusLine(metadata.httpVersion, 204, "Try again");
|
||||
}
|
||||
|
||||
httpServer = new HttpServer();
|
||||
httpServer.registerPathHandler("/subscribe", resubscribeHandler);
|
||||
httpServer.registerPathHandler("/newSubscription", listenSuccessHandler);
|
||||
httpServer.start(-1);
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
"testing.allowInsecureServerURL": true,
|
||||
"http2.retryInterval": 1000,
|
||||
"http2.maxRetries": 2,
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(async function test1() {
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
registerCleanupFunction(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
var serverURL = "http://localhost:" + httpServer.identity.primaryPort;
|
||||
|
||||
let records = [
|
||||
{
|
||||
subscriptionUri: "http://localhost/subscriptionNotExist",
|
||||
pushEndpoint: serverURL + "/pushEndpoint",
|
||||
pushReceiptEndpoint: serverURL + "/pushReceiptEndpoint",
|
||||
scope: "https://example.com/page",
|
||||
p256dhPublicKey:
|
||||
"BPCd4gNQkjwRah61LpdALdzZKLLnU5UAwDztQ5_h0QsT26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA",
|
||||
p256dhPrivateKey: {
|
||||
crv: "P-256",
|
||||
d: "1jUPhzVsRkzV0vIzwL4ZEsOlKdNOWm7TmaTfzitJkgM",
|
||||
ext: true,
|
||||
key_ops: ["deriveBits"],
|
||||
kty: "EC",
|
||||
x: "8J3iA1CSPBFqHrUul0At3NkosudTlQDAPO1Dn-HRCxM",
|
||||
y: "26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA",
|
||||
},
|
||||
originAttributes: "",
|
||||
quota: Infinity,
|
||||
},
|
||||
];
|
||||
|
||||
for (let record of records) {
|
||||
await db.put(record);
|
||||
}
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe",
|
||||
db,
|
||||
});
|
||||
|
||||
await handlerPromise;
|
||||
|
||||
let record = await db.getByIdentifiers({
|
||||
scope: "https://example.com/page",
|
||||
originAttributes: "",
|
||||
});
|
||||
equal(
|
||||
record.keyID,
|
||||
serverURL + "/newSubscription",
|
||||
"Should update subscription URL"
|
||||
);
|
||||
equal(
|
||||
record.pushEndpoint,
|
||||
serverURL + "/newPushEndpoint",
|
||||
"Should update push endpoint"
|
||||
);
|
||||
equal(
|
||||
record.pushReceiptEndpoint,
|
||||
serverURL + "/newReceiptPushEndpoint",
|
||||
"Should update push receipt endpoint"
|
||||
);
|
||||
});
|
@ -1,78 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
var pushEnabled;
|
||||
var pushConnectionEnabled;
|
||||
|
||||
var serverPort = -1;
|
||||
|
||||
function run_test() {
|
||||
serverPort = getTestServerPort();
|
||||
|
||||
do_get_profile();
|
||||
pushEnabled = Services.prefs.getBoolPref("dom.push.enabled");
|
||||
pushConnectionEnabled = Services.prefs.getBoolPref(
|
||||
"dom.push.connection.enabled"
|
||||
);
|
||||
|
||||
// Set to allow the cert presented by our H2 server
|
||||
var oldPref = Services.prefs.getIntPref(
|
||||
"network.http.speculative-parallel-limit"
|
||||
);
|
||||
Services.prefs.setIntPref("network.http.speculative-parallel-limit", 0);
|
||||
Services.prefs.setBoolPref("dom.push.enabled", true);
|
||||
Services.prefs.setBoolPref("dom.push.connection.enabled", true);
|
||||
|
||||
trustHttp2CA();
|
||||
|
||||
Services.prefs.setIntPref("network.http.speculative-parallel-limit", oldPref);
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(async function test_pushUnsubscriptionSuccess() {
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
registerCleanupFunction(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
var serverURL = "https://localhost:" + serverPort;
|
||||
|
||||
await db.put({
|
||||
subscriptionUri: serverURL + "/subscriptionUnsubscriptionSuccess",
|
||||
pushEndpoint: serverURL + "/pushEndpointUnsubscriptionSuccess",
|
||||
pushReceiptEndpoint:
|
||||
serverURL + "/receiptPushEndpointUnsubscriptionSuccess",
|
||||
scope: "https://example.com/page/unregister-success",
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix({
|
||||
inIsolatedMozBrowser: false,
|
||||
}),
|
||||
quota: Infinity,
|
||||
});
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL,
|
||||
db,
|
||||
});
|
||||
|
||||
await PushService.unregister({
|
||||
scope: "https://example.com/page/unregister-success",
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix({
|
||||
inIsolatedMozBrowser: false,
|
||||
}),
|
||||
});
|
||||
let record = await db.getByKeyID(
|
||||
serverURL + "/subscriptionUnsubscriptionSuccess"
|
||||
);
|
||||
ok(!record, "Unregister did not remove record");
|
||||
});
|
||||
|
||||
add_task(async function test_complete() {
|
||||
Services.prefs.setBoolPref("dom.push.enabled", pushEnabled);
|
||||
Services.prefs.setBoolPref(
|
||||
"dom.push.connection.enabled",
|
||||
pushConnectionEnabled
|
||||
);
|
||||
});
|
@ -1,72 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { HttpServer } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/httpd.sys.mjs"
|
||||
);
|
||||
|
||||
var httpServer = null;
|
||||
|
||||
function listenHandler(metadata, response) {
|
||||
Assert.ok(true, "Start listening");
|
||||
httpServer.stop(do_test_finished);
|
||||
response.setHeader("Retry-After", "10");
|
||||
response.setStatusLine(metadata.httpVersion, 500, "Retry");
|
||||
}
|
||||
|
||||
httpServer = new HttpServer();
|
||||
httpServer.registerPathHandler("/subscriptionNoKey", listenHandler);
|
||||
httpServer.start(-1);
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
"testing.allowInsecureServerURL": true,
|
||||
"http2.retryInterval": 1000,
|
||||
"http2.maxRetries": 2,
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(async function test1() {
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
registerCleanupFunction(() => {
|
||||
return db.drop().then(() => db.close());
|
||||
});
|
||||
|
||||
do_test_pending();
|
||||
|
||||
var serverURL = "http://localhost:" + httpServer.identity.primaryPort;
|
||||
|
||||
let record = {
|
||||
subscriptionUri: serverURL + "/subscriptionNoKey",
|
||||
pushEndpoint: serverURL + "/pushEndpoint",
|
||||
pushReceiptEndpoint: serverURL + "/pushReceiptEndpoint",
|
||||
scope: "https://example.com/page",
|
||||
originAttributes: "",
|
||||
quota: Infinity,
|
||||
systemRecord: true,
|
||||
};
|
||||
|
||||
await db.put(record);
|
||||
|
||||
let notifyPromise = promiseObserverNotification(
|
||||
PushServiceComponent.subscriptionChangeTopic,
|
||||
_ => true
|
||||
);
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe",
|
||||
db,
|
||||
});
|
||||
|
||||
await notifyPromise;
|
||||
|
||||
let aRecord = await db.getByKeyID(serverURL + "/subscriptionNoKey");
|
||||
ok(aRecord, "The record should still be there");
|
||||
ok(aRecord.p256dhPublicKey, "There should be a public key");
|
||||
ok(aRecord.p256dhPrivateKey, "There should be a private key");
|
||||
});
|
@ -1,5 +1,5 @@
|
||||
[DEFAULT]
|
||||
head = "head.js head-http2.js"
|
||||
head = "head.js"
|
||||
# Push notifications and alarms are currently disabled on Android.
|
||||
skip-if = ["os == 'android'"]
|
||||
support-files = ["broadcast_handler.sys.mjs"]
|
||||
@ -55,8 +55,6 @@ run-sequentially = "This will delete all existing push subscriptions."
|
||||
|
||||
["test_record.js"]
|
||||
|
||||
["test_register_5xxCode_http2.js"]
|
||||
|
||||
["test_register_case.js"]
|
||||
|
||||
["test_register_flush.js"]
|
||||
@ -89,18 +87,10 @@ run-sequentially = "This will delete all existing push subscriptions."
|
||||
|
||||
["test_registration_success.js"]
|
||||
|
||||
["test_resubscribe_4xxCode_http2.js"]
|
||||
|
||||
["test_resubscribe_5xxCode_http2.js"]
|
||||
|
||||
["test_resubscribe_listening_for_msg_error_http2.js"]
|
||||
|
||||
["test_retry_ws.js"]
|
||||
|
||||
["test_service_child.js"]
|
||||
|
||||
#http2 test
|
||||
|
||||
["test_service_parent.js"]
|
||||
|
||||
["test_unregister_empty_scope.js"]
|
||||
@ -115,7 +105,5 @@ run-sequentially = "very high failure rate in parallel"
|
||||
|
||||
["test_unregister_success.js"]
|
||||
|
||||
["test_updateRecordNoEncryptionKeys_http2.js"]
|
||||
|
||||
["test_updateRecordNoEncryptionKeys_ws.js"]
|
||||
skip-if = ["os == 'linux'"] # Bug 1265233
|
||||
|
@ -3333,11 +3333,6 @@ pref("dom.push.pingInterval", 1800000); // 30 minutes
|
||||
// How long before we timeout
|
||||
pref("dom.push.requestTimeout", 10000);
|
||||
|
||||
// WebPush prefs:
|
||||
pref("dom.push.http2.reset_retry_count_after_ms", 60000);
|
||||
pref("dom.push.http2.maxRetries", 2);
|
||||
pref("dom.push.http2.retryInterval", 5000);
|
||||
|
||||
// How long must we wait before declaring that a window is a "ghost" (i.e., a
|
||||
// likely leak)? This should be longer than it usually takes for an eligible
|
||||
// window to be collected via the GC/CC.
|
||||
|
Loading…
Reference in New Issue
Block a user