parent
869ffc8afd
commit
1c96d42a4b
|
|
@ -1 +1 @@
|
||||||
1016
|
1017
|
||||||
|
|
|
||||||
|
|
@ -648,10 +648,10 @@ index 0000000000000000000000000000000000000000..673e93b0278a3502d94006696cea7e6e
|
||||||
+
|
+
|
||||||
diff --git a/testing/juggler/NetworkObserver.js b/testing/juggler/NetworkObserver.js
|
diff --git a/testing/juggler/NetworkObserver.js b/testing/juggler/NetworkObserver.js
|
||||||
new file mode 100644
|
new file mode 100644
|
||||||
index 0000000000000000000000000000000000000000..2afbc74a4170233e76dadd7e7b294ca30a73e723
|
index 0000000000000000000000000000000000000000..e38c9b37b531de4eac67c2a138b68a34053b155b
|
||||||
--- /dev/null
|
--- /dev/null
|
||||||
+++ b/testing/juggler/NetworkObserver.js
|
+++ b/testing/juggler/NetworkObserver.js
|
||||||
@@ -0,0 +1,450 @@
|
@@ -0,0 +1,674 @@
|
||||||
+"use strict";
|
+"use strict";
|
||||||
+
|
+
|
||||||
+const {EventEmitter} = ChromeUtils.import('resource://gre/modules/EventEmitter.jsm');
|
+const {EventEmitter} = ChromeUtils.import('resource://gre/modules/EventEmitter.jsm');
|
||||||
|
|
@ -701,11 +701,14 @@ index 0000000000000000000000000000000000000000..2afbc74a4170233e76dadd7e7b294ca3
|
||||||
+ this._activityDistributor = Cc["@mozilla.org/network/http-activity-distributor;1"].getService(Ci.nsIHttpActivityDistributor);
|
+ this._activityDistributor = Cc["@mozilla.org/network/http-activity-distributor;1"].getService(Ci.nsIHttpActivityDistributor);
|
||||||
+ this._activityDistributor.addObserver(this);
|
+ this._activityDistributor.addObserver(this);
|
||||||
+
|
+
|
||||||
+ this._redirectMap = new Map();
|
+ this._redirectMap = new Map(); // oldId => newId
|
||||||
|
+ this._resumedRequestIdToHeaders = new Map(); // requestId => { headers }
|
||||||
|
+ this._postResumeChannelIdToRequestId = new Map(); // post-resume channel id => pre-resume request id
|
||||||
|
+
|
||||||
+ this._channelSink = {
|
+ this._channelSink = {
|
||||||
+ QueryInterface: ChromeUtils.generateQI([Ci.nsIChannelEventSink]),
|
+ QueryInterface: ChromeUtils.generateQI([Ci.nsIChannelEventSink]),
|
||||||
+ asyncOnChannelRedirect: (oldChannel, newChannel, flags, callback) => {
|
+ asyncOnChannelRedirect: (oldChannel, newChannel, flags, callback) => {
|
||||||
+ this._onRedirect(oldChannel, newChannel);
|
+ this._onRedirect(oldChannel, newChannel, flags);
|
||||||
+ callback.onRedirectVerifyCallback(Cr.NS_OK);
|
+ callback.onRedirectVerifyCallback(Cr.NS_OK);
|
||||||
+ },
|
+ },
|
||||||
+ };
|
+ };
|
||||||
|
|
@ -718,10 +721,10 @@ index 0000000000000000000000000000000000000000..2afbc74a4170233e76dadd7e7b294ca3
|
||||||
+ registrar.registerFactory(SINK_CLASS_ID, SINK_CLASS_DESCRIPTION, SINK_CONTRACT_ID, this._channelSinkFactory);
|
+ registrar.registerFactory(SINK_CLASS_ID, SINK_CLASS_DESCRIPTION, SINK_CONTRACT_ID, this._channelSinkFactory);
|
||||||
+ Services.catMan.addCategoryEntry(SINK_CATEGORY_NAME, SINK_CONTRACT_ID, SINK_CONTRACT_ID, false, true);
|
+ Services.catMan.addCategoryEntry(SINK_CATEGORY_NAME, SINK_CONTRACT_ID, SINK_CONTRACT_ID, false, true);
|
||||||
+
|
+
|
||||||
+ // Request interception state.
|
+ this._browserInterceptors = new Map(); // Browser => (requestId => interceptor).
|
||||||
+ this._browserSuspendedChannels = new Map();
|
|
||||||
+ this._extraHTTPHeaders = new Map();
|
+ this._extraHTTPHeaders = new Map();
|
||||||
+ this._browserResponseStorages = new Map();
|
+ this._browserResponseStorages = new Map();
|
||||||
|
+ this._browserAuthCredentials = new Map();
|
||||||
+
|
+
|
||||||
+ this._eventListeners = [
|
+ this._eventListeners = [
|
||||||
+ helper.addObserver(this._onRequest.bind(this), 'http-on-modify-request'),
|
+ helper.addObserver(this._onRequest.bind(this), 'http-on-modify-request'),
|
||||||
|
|
@ -739,36 +742,32 @@ index 0000000000000000000000000000000000000000..2afbc74a4170233e76dadd7e7b294ca3
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ enableRequestInterception(browser) {
|
+ enableRequestInterception(browser) {
|
||||||
+ if (!this._browserSuspendedChannels.has(browser))
|
+ if (!this._browserInterceptors.has(browser))
|
||||||
+ this._browserSuspendedChannels.set(browser, new Map());
|
+ this._browserInterceptors.set(browser, new Map());
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ disableRequestInterception(browser) {
|
+ disableRequestInterception(browser) {
|
||||||
+ const suspendedChannels = this._browserSuspendedChannels.get(browser);
|
+ const interceptors = this._browserInterceptors.get(browser);
|
||||||
+ if (!suspendedChannels)
|
+ if (!interceptors)
|
||||||
+ return;
|
+ return;
|
||||||
+ this._browserSuspendedChannels.delete(browser);
|
+ this._browserInterceptors.delete(browser);
|
||||||
+ for (const channel of suspendedChannels.values())
|
+ for (const interceptor of interceptors.values())
|
||||||
+ channel.resume();
|
+ interceptor._resume();
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ resumeSuspendedRequest(browser, requestId, headers) {
|
+ _takeInterceptor(browser, requestId) {
|
||||||
+ const suspendedChannels = this._browserSuspendedChannels.get(browser);
|
+ const interceptors = this._browserInterceptors.get(browser);
|
||||||
+ if (!suspendedChannels)
|
+ if (!interceptors)
|
||||||
+ throw new Error(`Request interception is not enabled`);
|
+ throw new Error(`Request interception is not enabled`);
|
||||||
+ const httpChannel = suspendedChannels.get(requestId);
|
+ const interceptor = interceptors.get(requestId);
|
||||||
+ if (!httpChannel)
|
+ if (!interceptor)
|
||||||
+ throw new Error(`Cannot find request "${requestId}"`);
|
+ throw new Error(`Cannot find request "${requestId}"`);
|
||||||
+ if (headers) {
|
+ interceptors.delete(requestId);
|
||||||
+ // 1. Clear all previous headers.
|
+ return interceptor;
|
||||||
+ for (const header of requestHeaders(httpChannel))
|
+ }
|
||||||
+ httpChannel.setRequestHeader(header.name, '', false /* merge */);
|
+
|
||||||
+ // 2. Set new headers.
|
+ resumeInterceptedRequest(browser, requestId, headers) {
|
||||||
+ for (const header of headers)
|
+ this._takeInterceptor(browser, requestId)._resume(headers);
|
||||||
+ httpChannel.setRequestHeader(header.name, header.value, false /* merge */);
|
|
||||||
+ }
|
|
||||||
+ suspendedChannels.delete(requestId);
|
|
||||||
+ httpChannel.resume();
|
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ getResponseBody(browser, requestId) {
|
+ getResponseBody(browser, requestId) {
|
||||||
|
|
@ -778,30 +777,50 @@ index 0000000000000000000000000000000000000000..2afbc74a4170233e76dadd7e7b294ca3
|
||||||
+ return responseStorage.getBase64EncodedResponse(requestId);
|
+ return responseStorage.getBase64EncodedResponse(requestId);
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ abortSuspendedRequest(browser, aRequestId) {
|
+ fulfillInterceptedRequest(browser, requestId, status, statusText, headers, base64body) {
|
||||||
+ const suspendedChannels = this._browserSuspendedChannels.get(browser);
|
+ this._takeInterceptor(browser, requestId)._fulfill(status, statusText, headers, base64body);
|
||||||
+ if (!suspendedChannels)
|
|
||||||
+ throw new Error(`Request interception is not enabled`);
|
|
||||||
+ const httpChannel = suspendedChannels.get(aRequestId);
|
|
||||||
+ if (!httpChannel)
|
|
||||||
+ throw new Error(`Cannot find request "${aRequestId}"`);
|
|
||||||
+ suspendedChannels.delete(aRequestId);
|
|
||||||
+ httpChannel.cancel(Cr.NS_ERROR_FAILURE);
|
|
||||||
+ httpChannel.resume();
|
|
||||||
+ this.emit('requestfailed', httpChannel, {
|
|
||||||
+ requestId: requestId(httpChannel),
|
|
||||||
+ errorCode: helper.getNetworkErrorStatusText(httpChannel.status),
|
|
||||||
+ });
|
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ _onRedirect(oldChannel, newChannel) {
|
+ abortInterceptedRequest(browser, requestId, errorCode) {
|
||||||
+ if (!(oldChannel instanceof Ci.nsIHttpChannel))
|
+ this._takeInterceptor(browser, requestId)._abort(errorCode);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ setAuthCredentials(browser, username, password) {
|
||||||
|
+ this._browserAuthCredentials.set(browser, { username, password });
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ _requestId(httpChannel) {
|
||||||
|
+ const id = httpChannel.channelId + '';
|
||||||
|
+ return this._postResumeChannelIdToRequestId.get(id) || id;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ _onRedirect(oldChannel, newChannel, flags) {
|
||||||
|
+ if (!(oldChannel instanceof Ci.nsIHttpChannel) || !(newChannel instanceof Ci.nsIHttpChannel))
|
||||||
+ return;
|
+ return;
|
||||||
+ const httpChannel = oldChannel.QueryInterface(Ci.nsIHttpChannel);
|
+ const oldHttpChannel = oldChannel.QueryInterface(Ci.nsIHttpChannel);
|
||||||
+ const loadContext = getLoadContext(httpChannel);
|
+ const newHttpChannel = newChannel.QueryInterface(Ci.nsIHttpChannel);
|
||||||
+ if (!loadContext || !this._browserSessionCount.has(loadContext.topFrameElement))
|
+ const browser = this._getBrowserForChannel(oldHttpChannel);
|
||||||
|
+ if (!browser)
|
||||||
+ return;
|
+ return;
|
||||||
+ this._redirectMap.set(newChannel, oldChannel);
|
+ const oldRequestId = this._requestId(oldHttpChannel);
|
||||||
|
+ const newRequestId = this._requestId(newHttpChannel);
|
||||||
|
+ if (this._resumedRequestIdToHeaders.has(oldRequestId)) {
|
||||||
|
+ // When we call resetInterception on a request, we get a new "redirected" request for it.
|
||||||
|
+ const { headers } = this._resumedRequestIdToHeaders.get(oldRequestId);
|
||||||
|
+ if (headers) {
|
||||||
|
+ // Apply new request headers from interception resume.
|
||||||
|
+ for (const header of requestHeaders(newChannel))
|
||||||
|
+ newChannel.setRequestHeader(header.name, '', false /* merge */);
|
||||||
|
+ for (const header of headers)
|
||||||
|
+ newChannel.setRequestHeader(header.name, header.value, false /* merge */);
|
||||||
|
+ }
|
||||||
|
+ // Use the old request id for the new "redirected" request for protocol consistency.
|
||||||
|
+ this._resumedRequestIdToHeaders.delete(oldRequestId);
|
||||||
|
+ this._postResumeChannelIdToRequestId.set(newRequestId, oldRequestId);
|
||||||
|
+ } else if (!(flags & Ci.nsIChannelEventSink.REDIRECT_INTERNAL)) {
|
||||||
|
+ // Regular (non-internal) redirect.
|
||||||
|
+ this._redirectMap.set(newRequestId, oldRequestId);
|
||||||
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ observeActivity(channel, activityType, activitySubtype, timestamp, extraSizeData, extraStringData) {
|
+ observeActivity(channel, activityType, activitySubtype, timestamp, extraSizeData, extraStringData) {
|
||||||
|
|
@ -810,56 +829,130 @@ index 0000000000000000000000000000000000000000..2afbc74a4170233e76dadd7e7b294ca3
|
||||||
+ if (!(channel instanceof Ci.nsIHttpChannel))
|
+ if (!(channel instanceof Ci.nsIHttpChannel))
|
||||||
+ return;
|
+ return;
|
||||||
+ const httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
|
+ const httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
|
||||||
+ const loadContext = getLoadContext(httpChannel);
|
+ const browser = this._getBrowserForChannel(httpChannel);
|
||||||
+ if (!loadContext || !this._browserSessionCount.has(loadContext.topFrameElement))
|
+ if (!browser)
|
||||||
+ return;
|
+ return;
|
||||||
+ if (activitySubtype !== Ci.nsIHttpActivityObserver.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE)
|
+ if (activitySubtype !== Ci.nsIHttpActivityObserver.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE)
|
||||||
+ return;
|
+ return;
|
||||||
+ this.emit('requestfinished', httpChannel, {
|
+ if (this._isResumedChannel(httpChannel))
|
||||||
+ requestId: requestId(httpChannel),
|
+ return;
|
||||||
+ });
|
+ this._sendOnRequestFinished(httpChannel);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ _getBrowserForChannel(httpChannel) {
|
||||||
|
+ let loadContext = null;
|
||||||
|
+ try {
|
||||||
|
+ if (httpChannel.notificationCallbacks)
|
||||||
|
+ loadContext = httpChannel.notificationCallbacks.getInterface(Ci.nsILoadContext);
|
||||||
|
+ } catch (e) {}
|
||||||
|
+ try {
|
||||||
|
+ if (!loadContext && httpChannel.loadGroup)
|
||||||
|
+ loadContext = httpChannel.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
|
||||||
|
+ } catch (e) { }
|
||||||
|
+ if (!loadContext || !this._browserSessionCount.has(loadContext.topFrameElement))
|
||||||
|
+ return;
|
||||||
|
+ return loadContext.topFrameElement;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ _isResumedChannel(httpChannel) {
|
||||||
|
+ return this._postResumeChannelIdToRequestId.has(httpChannel.channelId + '');
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ _onRequest(channel, topic) {
|
+ _onRequest(channel, topic) {
|
||||||
+ if (!(channel instanceof Ci.nsIHttpChannel))
|
+ if (!(channel instanceof Ci.nsIHttpChannel))
|
||||||
+ return;
|
+ return;
|
||||||
+ const httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
|
+ const httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
|
||||||
+ const loadContext = getLoadContext(httpChannel);
|
+ const browser = this._getBrowserForChannel(httpChannel);
|
||||||
+ if (!loadContext || !this._browserSessionCount.has(loadContext.topFrameElement))
|
+ if (!browser)
|
||||||
+ return;
|
+ return;
|
||||||
+ const extraHeaders = this._extraHTTPHeaders.get(loadContext.topFrameElement);
|
+ if (this._isResumedChannel(httpChannel)) {
|
||||||
|
+ // Ignore onRequest for resumed requests, but listen to their response.
|
||||||
|
+ new ResponseBodyListener(this, browser, httpChannel);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+ const extraHeaders = this._extraHTTPHeaders.get(browser);
|
||||||
+ if (extraHeaders) {
|
+ if (extraHeaders) {
|
||||||
+ for (const header of extraHeaders)
|
+ for (const header of extraHeaders)
|
||||||
+ httpChannel.setRequestHeader(header.name, header.value, false /* merge */);
|
+ httpChannel.setRequestHeader(header.name, header.value, false /* merge */);
|
||||||
+ }
|
+ }
|
||||||
+ const causeType = httpChannel.loadInfo ? httpChannel.loadInfo.externalContentPolicyType : Ci.nsIContentPolicy.TYPE_OTHER;
|
+ const requestId = this._requestId(httpChannel);
|
||||||
+ const suspendedChannels = this._browserSuspendedChannels.get(loadContext.topFrameElement);
|
+ const isRedirect = this._redirectMap.has(requestId);
|
||||||
+ if (suspendedChannels) {
|
+ const interceptors = this._browserInterceptors.get(browser);
|
||||||
+ httpChannel.suspend();
|
+ if (!interceptors) {
|
||||||
+ suspendedChannels.set(requestId(httpChannel), httpChannel);
|
+ new NotificationCallbacks(this, browser, httpChannel, false);
|
||||||
|
+ this._sendOnRequest(httpChannel, false);
|
||||||
|
+ new ResponseBodyListener(this, browser, httpChannel);
|
||||||
|
+ } else if (isRedirect) {
|
||||||
|
+ // We pretend that redirect is interceptable in the protocol, although it's actually not
|
||||||
|
+ // and therefore we do not instantiate the interceptor.
|
||||||
|
+ // TODO: look into REDIRECT_MODE_MANUAL.
|
||||||
|
+ interceptors.set(requestId, {
|
||||||
|
+ _resume: () => {},
|
||||||
|
+ _abort: () => {},
|
||||||
|
+ _fulfill: () => {},
|
||||||
|
+ });
|
||||||
|
+ new NotificationCallbacks(this, browser, httpChannel, false);
|
||||||
|
+ this._sendOnRequest(httpChannel, true);
|
||||||
|
+ new ResponseBodyListener(this, browser, httpChannel);
|
||||||
|
+ } else {
|
||||||
|
+ new NotificationCallbacks(this, browser, httpChannel, true);
|
||||||
|
+ // We'll issue onRequest once it's intercepted.
|
||||||
+ }
|
+ }
|
||||||
+ const oldChannel = this._redirectMap.get(httpChannel);
|
+ }
|
||||||
+ this._redirectMap.delete(httpChannel);
|
|
||||||
+
|
+
|
||||||
+ // Install response body hooks.
|
+ _onIntercepted(httpChannel, interceptor) {
|
||||||
+ new ResponseBodyListener(this, loadContext.topFrameElement, httpChannel);
|
+ const browser = this._getBrowserForChannel(httpChannel);
|
||||||
|
+ if (!browser) {
|
||||||
|
+ interceptor._resume();
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+ const interceptors = this._browserInterceptors.get(browser);
|
||||||
|
+ this._sendOnRequest(httpChannel, !!interceptors);
|
||||||
|
+ if (interceptors)
|
||||||
|
+ interceptors.set(this._requestId(httpChannel), interceptor);
|
||||||
|
+ else
|
||||||
|
+ interceptor._resume();
|
||||||
|
+ }
|
||||||
+
|
+
|
||||||
|
+ _sendOnRequest(httpChannel, isIntercepted) {
|
||||||
|
+ const browser = this._getBrowserForChannel(httpChannel);
|
||||||
|
+ if (!browser)
|
||||||
|
+ return;
|
||||||
|
+ const causeType = httpChannel.loadInfo ? httpChannel.loadInfo.externalContentPolicyType : Ci.nsIContentPolicy.TYPE_OTHER;
|
||||||
|
+ const requestId = this._requestId(httpChannel);
|
||||||
|
+ const redirectedFrom = this._redirectMap.get(requestId);
|
||||||
|
+ this._redirectMap.delete(requestId);
|
||||||
+ this.emit('request', httpChannel, {
|
+ this.emit('request', httpChannel, {
|
||||||
+ url: httpChannel.URI.spec,
|
+ url: httpChannel.URI.spec,
|
||||||
+ suspended: suspendedChannels ? true : undefined,
|
+ isIntercepted,
|
||||||
+ requestId: requestId(httpChannel),
|
+ requestId,
|
||||||
+ redirectedFrom: oldChannel ? requestId(oldChannel) : undefined,
|
+ redirectedFrom,
|
||||||
+ postData: readRequestPostData(httpChannel),
|
+ postData: readRequestPostData(httpChannel),
|
||||||
+ headers: requestHeaders(httpChannel),
|
+ headers: requestHeaders(httpChannel),
|
||||||
+ method: httpChannel.requestMethod,
|
+ method: httpChannel.requestMethod,
|
||||||
+ navigationId: httpChannel.isMainDocumentChannel ? requestId(httpChannel) : undefined,
|
+ navigationId: httpChannel.isMainDocumentChannel ? this._requestId(httpChannel) : undefined,
|
||||||
+ cause: causeTypeToString(causeType),
|
+ cause: causeTypeToString(causeType),
|
||||||
+ });
|
+ });
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
|
+ _sendOnRequestFinished(httpChannel) {
|
||||||
|
+ this.emit('requestfinished', httpChannel, {
|
||||||
|
+ requestId: this._requestId(httpChannel),
|
||||||
|
+ });
|
||||||
|
+ this._postResumeChannelIdToRequestId.delete(httpChannel.channelId + '');
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ _sendOnRequestFailed(httpChannel, error) {
|
||||||
|
+ this.emit('requestfailed', httpChannel, {
|
||||||
|
+ requestId: this._requestId(httpChannel),
|
||||||
|
+ errorCode: helper.getNetworkErrorStatusText(error),
|
||||||
|
+ });
|
||||||
|
+ this._postResumeChannelIdToRequestId.delete(httpChannel.channelId + '');
|
||||||
|
+ }
|
||||||
|
+
|
||||||
+ _onResponse(fromCache, httpChannel, topic) {
|
+ _onResponse(fromCache, httpChannel, topic) {
|
||||||
+ const loadContext = getLoadContext(httpChannel);
|
+ const browser = this._getBrowserForChannel(httpChannel);
|
||||||
+ if (!loadContext || !this._browserSessionCount.has(loadContext.topFrameElement))
|
+ if (!browser)
|
||||||
+ return;
|
+ return;
|
||||||
+ httpChannel.QueryInterface(Ci.nsIHttpChannelInternal);
|
+ httpChannel.QueryInterface(Ci.nsIHttpChannelInternal);
|
||||||
+ const headers = [];
|
+ const headers = [];
|
||||||
|
|
@ -876,7 +969,7 @@ index 0000000000000000000000000000000000000000..2afbc74a4170233e76dadd7e7b294ca3
|
||||||
+ // remoteAddress is not defined for cached requests.
|
+ // remoteAddress is not defined for cached requests.
|
||||||
+ }
|
+ }
|
||||||
+ this.emit('response', httpChannel, {
|
+ this.emit('response', httpChannel, {
|
||||||
+ requestId: requestId(httpChannel),
|
+ requestId: this._requestId(httpChannel),
|
||||||
+ securityDetails: getSecurityDetails(httpChannel),
|
+ securityDetails: getSecurityDetails(httpChannel),
|
||||||
+ fromCache,
|
+ fromCache,
|
||||||
+ headers,
|
+ headers,
|
||||||
|
|
@ -892,16 +985,14 @@ index 0000000000000000000000000000000000000000..2afbc74a4170233e76dadd7e7b294ca3
|
||||||
+ if (!responseStorage)
|
+ if (!responseStorage)
|
||||||
+ return;
|
+ return;
|
||||||
+ responseStorage.addResponseBody(httpChannel, body);
|
+ responseStorage.addResponseBody(httpChannel, body);
|
||||||
+ this.emit('requestfinished', httpChannel, {
|
+ this._sendOnRequestFinished(httpChannel);
|
||||||
+ requestId: requestId(httpChannel),
|
|
||||||
+ });
|
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ startTrackingBrowserNetwork(browser) {
|
+ startTrackingBrowserNetwork(browser) {
|
||||||
+ const value = this._browserSessionCount.get(browser) || 0;
|
+ const value = this._browserSessionCount.get(browser) || 0;
|
||||||
+ this._browserSessionCount.set(browser, value + 1);
|
+ this._browserSessionCount.set(browser, value + 1);
|
||||||
+ if (value === 0)
|
+ if (value === 0)
|
||||||
+ this._browserResponseStorages.set(browser, new ResponseStorage(MAX_RESPONSE_STORAGE_SIZE, MAX_RESPONSE_STORAGE_SIZE / 10));
|
+ this._browserResponseStorages.set(browser, new ResponseStorage(this, MAX_RESPONSE_STORAGE_SIZE, MAX_RESPONSE_STORAGE_SIZE / 10));
|
||||||
+ return () => this.stopTrackingBrowserNetwork(browser);
|
+ return () => this.stopTrackingBrowserNetwork(browser);
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
|
|
@ -912,6 +1003,8 @@ index 0000000000000000000000000000000000000000..2afbc74a4170233e76dadd7e7b294ca3
|
||||||
+ } else {
|
+ } else {
|
||||||
+ this._browserSessionCount.delete(browser);
|
+ this._browserSessionCount.delete(browser);
|
||||||
+ this._browserResponseStorages.delete(browser);
|
+ this._browserResponseStorages.delete(browser);
|
||||||
|
+ this._browserAuthCredentials.delete(browser);
|
||||||
|
+ this._browserInterceptors.delete(browser);
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
|
|
@ -982,23 +1075,6 @@ index 0000000000000000000000000000000000000000..2afbc74a4170233e76dadd7e7b294ca3
|
||||||
+ return text;
|
+ return text;
|
||||||
+}
|
+}
|
||||||
+
|
+
|
||||||
+function getLoadContext(httpChannel) {
|
|
||||||
+ let loadContext = null;
|
|
||||||
+ try {
|
|
||||||
+ if (httpChannel.notificationCallbacks)
|
|
||||||
+ loadContext = httpChannel.notificationCallbacks.getInterface(Ci.nsILoadContext);
|
|
||||||
+ } catch (e) {}
|
|
||||||
+ try {
|
|
||||||
+ if (!loadContext && httpChannel.loadGroup)
|
|
||||||
+ loadContext = httpChannel.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
|
|
||||||
+ } catch (e) { }
|
|
||||||
+ return loadContext;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+function requestId(httpChannel) {
|
|
||||||
+ return httpChannel.channelId + '';
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+function requestHeaders(httpChannel) {
|
+function requestHeaders(httpChannel) {
|
||||||
+ const headers = [];
|
+ const headers = [];
|
||||||
+ httpChannel.visitRequestHeaders({
|
+ httpChannel.visitRequestHeaders({
|
||||||
|
|
@ -1016,7 +1092,8 @@ index 0000000000000000000000000000000000000000..2afbc74a4170233e76dadd7e7b294ca3
|
||||||
+}
|
+}
|
||||||
+
|
+
|
||||||
+class ResponseStorage {
|
+class ResponseStorage {
|
||||||
+ constructor(maxTotalSize, maxResponseSize) {
|
+ constructor(networkObserver, maxTotalSize, maxResponseSize) {
|
||||||
|
+ this._networkObserver = networkObserver;
|
||||||
+ this._totalSize = 0;
|
+ this._totalSize = 0;
|
||||||
+ this._maxResponseSize = maxResponseSize;
|
+ this._maxResponseSize = maxResponseSize;
|
||||||
+ this._maxTotalSize = maxTotalSize;
|
+ this._maxTotalSize = maxTotalSize;
|
||||||
|
|
@ -1036,7 +1113,7 @@ index 0000000000000000000000000000000000000000..2afbc74a4170233e76dadd7e7b294ca3
|
||||||
+ const encodingHeader = httpChannel.getResponseHeader("Content-Encoding");
|
+ const encodingHeader = httpChannel.getResponseHeader("Content-Encoding");
|
||||||
+ encodings = encodingHeader.split(/\s*\t*,\s*\t*/);
|
+ encodings = encodingHeader.split(/\s*\t*,\s*\t*/);
|
||||||
+ }
|
+ }
|
||||||
+ this._responses.set(requestId(httpChannel), {body, encodings});
|
+ this._responses.set(this._networkObserver._requestId(httpChannel), {body, encodings});
|
||||||
+ this._totalSize += body.length;
|
+ this._totalSize += body.length;
|
||||||
+ if (this._totalSize > this._maxTotalSize) {
|
+ if (this._totalSize > this._maxTotalSize) {
|
||||||
+ for (let [requestId, response] of this._responses) {
|
+ for (let [requestId, response] of this._responses) {
|
||||||
|
|
@ -1100,6 +1177,153 @@ index 0000000000000000000000000000000000000000..2afbc74a4170233e76dadd7e7b294ca3
|
||||||
+ }
|
+ }
|
||||||
+}
|
+}
|
||||||
+
|
+
|
||||||
|
+class NotificationCallbacks {
|
||||||
|
+ constructor(networkObserver, browser, httpChannel, shouldIntercept) {
|
||||||
|
+ this._networkObserver = networkObserver;
|
||||||
|
+ this._browser = browser;
|
||||||
|
+ this._shouldIntercept = shouldIntercept;
|
||||||
|
+ this._httpChannel = httpChannel;
|
||||||
|
+ this._previousCallbacks = httpChannel.notificationCallbacks;
|
||||||
|
+ httpChannel.notificationCallbacks = this;
|
||||||
|
+
|
||||||
|
+ const qis = [
|
||||||
|
+ Ci.nsIAuthPrompt2,
|
||||||
|
+ Ci.nsIAuthPromptProvider,
|
||||||
|
+ Ci.nsIInterfaceRequestor,
|
||||||
|
+ ];
|
||||||
|
+ if (shouldIntercept)
|
||||||
|
+ qis.push(Ci.nsINetworkInterceptController);
|
||||||
|
+ this.QueryInterface = ChromeUtils.generateQI(qis);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ getInterface(iid) {
|
||||||
|
+ if (iid.equals(Ci.nsIAuthPrompt2) || iid.equals(Ci.nsIAuthPromptProvider))
|
||||||
|
+ return this;
|
||||||
|
+ if (this._shouldIntercept && iid.equals(Ci.nsINetworkInterceptController))
|
||||||
|
+ return this;
|
||||||
|
+ if (iid.equals(Ci.nsIAuthPrompt)) // Block nsIAuthPrompt - we want nsIAuthPrompt2 to be used instead.
|
||||||
|
+ throw Cr.NS_ERROR_NO_INTERFACE;
|
||||||
|
+ if (this._previousCallbacks)
|
||||||
|
+ return this._previousCallbacks.getInterface(iid);
|
||||||
|
+ throw Cr.NS_ERROR_NO_INTERFACE;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ _forward(iid, method, args) {
|
||||||
|
+ if (!this._previousCallbacks)
|
||||||
|
+ return;
|
||||||
|
+ try {
|
||||||
|
+ const impl = this._previousCallbacks.getInterface(iid);
|
||||||
|
+ impl[method].apply(impl, args);
|
||||||
|
+ } catch (e) {
|
||||||
|
+ if (e.result != Cr.NS_ERROR_NO_INTERFACE)
|
||||||
|
+ throw e;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // nsIAuthPromptProvider
|
||||||
|
+ getAuthPrompt(aPromptReason, iid) {
|
||||||
|
+ return this;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // nsIAuthPrompt2
|
||||||
|
+ asyncPromptAuth(aChannel, aCallback, aContext, level, authInfo) {
|
||||||
|
+ let canceled = false;
|
||||||
|
+ Promise.resolve().then(() => {
|
||||||
|
+ if (canceled)
|
||||||
|
+ return;
|
||||||
|
+ const hasAuth = this.promptAuth(aChannel, level, authInfo);
|
||||||
|
+ if (hasAuth)
|
||||||
|
+ aCallback.onAuthAvailable(aContext, authInfo);
|
||||||
|
+ else
|
||||||
|
+ aCallback.onAuthCancelled(aContext, true);
|
||||||
|
+ });
|
||||||
|
+ return {
|
||||||
|
+ QueryInterface: ChromeUtils.generateQI([Ci.nsICancelable]),
|
||||||
|
+ cancel: () => {
|
||||||
|
+ aCallback.onAuthCancelled(aContext, false);
|
||||||
|
+ canceled = true;
|
||||||
|
+ }
|
||||||
|
+ };
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // nsIAuthPrompt2
|
||||||
|
+ promptAuth(aChannel, level, authInfo) {
|
||||||
|
+ if (authInfo.flags & Ci.nsIAuthInformation.PREVIOUS_FAILED)
|
||||||
|
+ return false;
|
||||||
|
+ const credentials = this._networkObserver._browserAuthCredentials.get(this._browser);
|
||||||
|
+ if (!credentials || credentials.username === null)
|
||||||
|
+ return false;
|
||||||
|
+ authInfo.username = credentials.username;
|
||||||
|
+ authInfo.password = credentials.password;
|
||||||
|
+ return true;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // nsINetworkInterceptController
|
||||||
|
+ shouldPrepareForIntercept(aURI, channel) {
|
||||||
|
+ if (!(channel instanceof Ci.nsIHttpChannel))
|
||||||
|
+ return false;
|
||||||
|
+ const httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
|
||||||
|
+ return httpChannel.channelId === this._httpChannel.channelId;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // nsINetworkInterceptController
|
||||||
|
+ channelIntercepted(intercepted) {
|
||||||
|
+ this._intercepted = intercepted.QueryInterface(Ci.nsIInterceptedChannel);
|
||||||
|
+ const httpChannel = this._intercepted.channel.QueryInterface(Ci.nsIHttpChannel);
|
||||||
|
+ this._networkObserver._onIntercepted(httpChannel, this);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ _resume(headers) {
|
||||||
|
+ this._networkObserver._resumedRequestIdToHeaders.set(this._networkObserver._requestId(this._httpChannel), { headers });
|
||||||
|
+ this._intercepted.resetInterception();
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ _fulfill(status, statusText, headers, base64body) {
|
||||||
|
+ this._intercepted.synthesizeStatus(status, statusText);
|
||||||
|
+ for (const header of headers)
|
||||||
|
+ this._intercepted.synthesizeHeader(header.name, header.value);
|
||||||
|
+ const synthesized = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
|
||||||
|
+ if (base64body)
|
||||||
|
+ synthesized.data = atob(base64body);
|
||||||
|
+ else
|
||||||
|
+ synthesized.data = '';
|
||||||
|
+ this._intercepted.startSynthesizedResponse(synthesized, null, null, '', false);
|
||||||
|
+ this._intercepted.finishSynthesizedResponse();
|
||||||
|
+ this._networkObserver.emit('response', this._httpChannel, {
|
||||||
|
+ requestId: this._networkObserver._requestId(this._httpChannel),
|
||||||
|
+ securityDetails: null,
|
||||||
|
+ fromCache: false,
|
||||||
|
+ headers,
|
||||||
|
+ status,
|
||||||
|
+ statusText,
|
||||||
|
+ });
|
||||||
|
+ this._networkObserver._sendOnRequestFinished(this._httpChannel);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ _abort(errorCode) {
|
||||||
|
+ const error = errorMap[errorCode] || Cr.NS_ERROR_FAILURE;
|
||||||
|
+ this._intercepted.cancelInterception(error);
|
||||||
|
+ this._networkObserver._sendOnRequestFailed(this._httpChannel, error);
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+const errorMap = {
|
||||||
|
+ 'aborted': Cr.NS_ERROR_ABORT,
|
||||||
|
+ 'accessdenied': Cr.NS_ERROR_PORT_ACCESS_NOT_ALLOWED,
|
||||||
|
+ 'addressunreachable': Cr.NS_ERROR_UNKNOWN_HOST,
|
||||||
|
+ 'blockedbyclient': Cr.NS_ERROR_FAILURE,
|
||||||
|
+ 'blockedbyresponse': Cr.NS_ERROR_FAILURE,
|
||||||
|
+ 'connectionaborted': Cr.NS_ERROR_NET_INTERRUPT,
|
||||||
|
+ 'connectionclosed': Cr.NS_ERROR_FAILURE,
|
||||||
|
+ 'connectionfailed': Cr.NS_ERROR_FAILURE,
|
||||||
|
+ 'connectionrefused': Cr.NS_ERROR_CONNECTION_REFUSED,
|
||||||
|
+ 'connectionreset': Cr.NS_ERROR_NET_RESET,
|
||||||
|
+ 'internetdisconnected': Cr.NS_ERROR_OFFLINE,
|
||||||
|
+ 'namenotresolved': Cr.NS_ERROR_UNKNOWN_HOST,
|
||||||
|
+ 'timedout': Cr.NS_ERROR_NET_TIMEOUT,
|
||||||
|
+ 'failed': Cr.NS_ERROR_FAILURE,
|
||||||
|
+};
|
||||||
|
+
|
||||||
+var EXPORTED_SYMBOLS = ['NetworkObserver'];
|
+var EXPORTED_SYMBOLS = ['NetworkObserver'];
|
||||||
+this.NetworkObserver = NetworkObserver;
|
+this.NetworkObserver = NetworkObserver;
|
||||||
diff --git a/testing/juggler/TargetRegistry.js b/testing/juggler/TargetRegistry.js
|
diff --git a/testing/juggler/TargetRegistry.js b/testing/juggler/TargetRegistry.js
|
||||||
|
|
@ -3915,10 +4139,10 @@ index 0000000000000000000000000000000000000000..956988738079272be8d3998dcbbaa91a
|
||||||
+
|
+
|
||||||
diff --git a/testing/juggler/protocol/NetworkHandler.js b/testing/juggler/protocol/NetworkHandler.js
|
diff --git a/testing/juggler/protocol/NetworkHandler.js b/testing/juggler/protocol/NetworkHandler.js
|
||||||
new file mode 100644
|
new file mode 100644
|
||||||
index 0000000000000000000000000000000000000000..f5e7e919594b3778fd3046bf69d34878cccefa64
|
index 0000000000000000000000000000000000000000..22e7b4f9397e592f26ce447aafd6318398ad5b48
|
||||||
--- /dev/null
|
--- /dev/null
|
||||||
+++ b/testing/juggler/protocol/NetworkHandler.js
|
+++ b/testing/juggler/protocol/NetworkHandler.js
|
||||||
@@ -0,0 +1,154 @@
|
@@ -0,0 +1,166 @@
|
||||||
+"use strict";
|
+"use strict";
|
||||||
+
|
+
|
||||||
+const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
+const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||||
|
|
@ -3943,6 +4167,7 @@ index 0000000000000000000000000000000000000000..f5e7e919594b3778fd3046bf69d34878
|
||||||
+ this._requestInterception = false;
|
+ this._requestInterception = false;
|
||||||
+ this._eventListeners = [];
|
+ this._eventListeners = [];
|
||||||
+ this._pendingRequstWillBeSentEvents = new Set();
|
+ this._pendingRequstWillBeSentEvents = new Set();
|
||||||
|
+ this._requestIdToFrameId = new Map();
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ async enable() {
|
+ async enable() {
|
||||||
|
|
@ -3976,12 +4201,20 @@ index 0000000000000000000000000000000000000000..f5e7e919594b3778fd3046bf69d34878
|
||||||
+ await Promise.all(Array.from(this._pendingRequstWillBeSentEvents));
|
+ await Promise.all(Array.from(this._pendingRequstWillBeSentEvents));
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ async resumeSuspendedRequest({requestId, headers}) {
|
+ async resumeInterceptedRequest({requestId, headers}) {
|
||||||
+ this._networkObserver.resumeSuspendedRequest(this._browser, requestId, headers);
|
+ this._networkObserver.resumeInterceptedRequest(this._browser, requestId, headers);
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ async abortSuspendedRequest({requestId}) {
|
+ async abortInterceptedRequest({requestId, errorCode}) {
|
||||||
+ this._networkObserver.abortSuspendedRequest(this._browser, requestId);
|
+ this._networkObserver.abortInterceptedRequest(this._browser, requestId, errorCode);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ async fulfillInterceptedRequest({requestId, status, statusText, headers, base64body}) {
|
||||||
|
+ this._networkObserver.fulfillInterceptedRequest(this._browser, requestId, status, statusText, headers, base64body);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ async setAuthCredentials({username, password}) {
|
||||||
|
+ this._networkObserver.setAuthCredentials(this._browser, username, password);
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ dispose() {
|
+ dispose() {
|
||||||
|
|
@ -4042,9 +4275,12 @@ index 0000000000000000000000000000000000000000..f5e7e919594b3778fd3046bf69d34878
|
||||||
+ return;
|
+ return;
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
|
+ // Inherit frameId for redirects when details are not available.
|
||||||
|
+ const frameId = details ? details.frameId : (eventDetails.redirectedFrom ? this._requestIdToFrameId.get(eventDetails.redirectedFrom) : undefined);
|
||||||
|
+ this._requestIdToFrameId.set(eventDetails.requestId, frameId);
|
||||||
+ const activity = this._ensureHTTPActivity(eventDetails.requestId);
|
+ const activity = this._ensureHTTPActivity(eventDetails.requestId);
|
||||||
+ activity.request = {
|
+ activity.request = {
|
||||||
+ frameId: details ? details.frameId : undefined,
|
+ frameId,
|
||||||
+ ...eventDetails,
|
+ ...eventDetails,
|
||||||
+ };
|
+ };
|
||||||
+ this._reportHTTPAcitivityEvents(activity);
|
+ this._reportHTTPAcitivityEvents(activity);
|
||||||
|
|
@ -4515,10 +4751,10 @@ index 0000000000000000000000000000000000000000..78b6601b91d0b7fcda61114e6846aa07
|
||||||
+this.EXPORTED_SYMBOLS = ['t', 'checkScheme'];
|
+this.EXPORTED_SYMBOLS = ['t', 'checkScheme'];
|
||||||
diff --git a/testing/juggler/protocol/Protocol.js b/testing/juggler/protocol/Protocol.js
|
diff --git a/testing/juggler/protocol/Protocol.js b/testing/juggler/protocol/Protocol.js
|
||||||
new file mode 100644
|
new file mode 100644
|
||||||
index 0000000000000000000000000000000000000000..1eecb6120f101cb7506fcf8d40c177089e62671b
|
index 0000000000000000000000000000000000000000..a0913f7728931a938b850083213560a511b624a8
|
||||||
--- /dev/null
|
--- /dev/null
|
||||||
+++ b/testing/juggler/protocol/Protocol.js
|
+++ b/testing/juggler/protocol/Protocol.js
|
||||||
@@ -0,0 +1,731 @@
|
@@ -0,0 +1,747 @@
|
||||||
+const {t, checkScheme} = ChromeUtils.import('chrome://juggler/content/protocol/PrimitiveTypes.js');
|
+const {t, checkScheme} = ChromeUtils.import('chrome://juggler/content/protocol/PrimitiveTypes.js');
|
||||||
+
|
+
|
||||||
+// Protocol-specific types.
|
+// Protocol-specific types.
|
||||||
|
|
@ -4815,7 +5051,7 @@ index 0000000000000000000000000000000000000000..1eecb6120f101cb7506fcf8d40c17708
|
||||||
+ redirectedFrom: t.Optional(t.String),
|
+ redirectedFrom: t.Optional(t.String),
|
||||||
+ postData: t.Optional(t.String),
|
+ postData: t.Optional(t.String),
|
||||||
+ headers: t.Array(networkTypes.HTTPHeader),
|
+ headers: t.Array(networkTypes.HTTPHeader),
|
||||||
+ suspended: t.Optional(t.Boolean),
|
+ isIntercepted: t.Boolean,
|
||||||
+ url: t.String,
|
+ url: t.String,
|
||||||
+ method: t.String,
|
+ method: t.String,
|
||||||
+ navigationId: t.Optional(t.String),
|
+ navigationId: t.Optional(t.String),
|
||||||
|
|
@ -4851,17 +5087,27 @@ index 0000000000000000000000000000000000000000..1eecb6120f101cb7506fcf8d40c17708
|
||||||
+ headers: t.Array(networkTypes.HTTPHeader),
|
+ headers: t.Array(networkTypes.HTTPHeader),
|
||||||
+ },
|
+ },
|
||||||
+ },
|
+ },
|
||||||
+ 'abortSuspendedRequest': {
|
+ 'abortInterceptedRequest': {
|
||||||
+ params: {
|
+ params: {
|
||||||
+ requestId: t.String,
|
+ requestId: t.String,
|
||||||
|
+ errorCode: t.String,
|
||||||
+ },
|
+ },
|
||||||
+ },
|
+ },
|
||||||
+ 'resumeSuspendedRequest': {
|
+ 'resumeInterceptedRequest': {
|
||||||
+ params: {
|
+ params: {
|
||||||
+ requestId: t.String,
|
+ requestId: t.String,
|
||||||
+ headers: t.Optional(t.Array(networkTypes.HTTPHeader)),
|
+ headers: t.Optional(t.Array(networkTypes.HTTPHeader)),
|
||||||
+ },
|
+ },
|
||||||
+ },
|
+ },
|
||||||
|
+ 'fulfillInterceptedRequest': {
|
||||||
|
+ params: {
|
||||||
|
+ requestId: t.String,
|
||||||
|
+ status: t.Number,
|
||||||
|
+ statusText: t.String,
|
||||||
|
+ headers: t.Array(networkTypes.HTTPHeader),
|
||||||
|
+ base64body: t.Optional(t.String), // base64-encoded
|
||||||
|
+ },
|
||||||
|
+ },
|
||||||
+ 'getResponseBody': {
|
+ 'getResponseBody': {
|
||||||
+ params: {
|
+ params: {
|
||||||
+ requestId: t.String,
|
+ requestId: t.String,
|
||||||
|
|
@ -4871,6 +5117,12 @@ index 0000000000000000000000000000000000000000..1eecb6120f101cb7506fcf8d40c17708
|
||||||
+ evicted: t.Optional(t.Boolean),
|
+ evicted: t.Optional(t.Boolean),
|
||||||
+ },
|
+ },
|
||||||
+ },
|
+ },
|
||||||
|
+ 'setAuthCredentials': {
|
||||||
|
+ params: {
|
||||||
|
+ username: t.Nullable(t.String),
|
||||||
|
+ password: t.Nullable(t.String),
|
||||||
|
+ },
|
||||||
|
+ },
|
||||||
+ },
|
+ },
|
||||||
+};
|
+};
|
||||||
+
|
+
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue