1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 /** @suppress {duplicate} */
6 var remoting
= remoting
|| {};
13 * A class that writes log events to our back-end using XHR.
14 * If a log request fails due to network failure, it will be stored to
15 * |storage| for retrying in the future by calling flush().
17 * @param {!string} url
18 * @param {!StorageArea} storage
19 * @param {!string} storageKey
22 remoting
.XhrEventWriter = function(url
, storage
, storageKey
) {
26 this.storage_
= storage
;
27 /** @private @const */
28 this.storageKey_
= storageKey
;
30 this.pendingRequests_
= new Map();
31 /** @private {base.Deferred} */
32 this.pendingFlush_
= null;
36 * @return {Promise} A promise that resolves when initialization is completed.
38 remoting
.XhrEventWriter
.prototype.loadPendingRequests = function() {
40 var deferred
= new base
.Deferred();
41 this.storage_
.get(this.storageKey_
, function(entry
) {
43 that
.pendingRequests_
= new Map(entry
[that
.storageKey_
]);
45 that
.pendingRequests_
= new Map();
47 deferred
.resolve(that
.pendingRequests_
);
49 return deferred
.promise();
53 * @param {Object} event The event to be written to the server.
54 * @return {Promise} A promise that resolves on success.
56 remoting
.XhrEventWriter
.prototype.write = function(event
) {
57 console
.log('Writing Event - ' + JSON
.stringify(event
));
58 this.markPending_(event
);
63 * @return {Promise} A promise that resolves on success.
65 remoting
.XhrEventWriter
.prototype.flush = function() {
66 if (!this.pendingFlush_
) {
68 this.pendingFlush_
= new base
.Deferred();
70 var onFailure = function(/** * */e
) {
71 that
.pendingFlush_
.reject(e
);
72 that
.pendingFlush_
= null;
75 var flushAll = function() {
76 if (that
.pendingRequests_
.size
> 0) {
77 that
.doFlush_().then(flushAll
, onFailure
);
79 that
.pendingFlush_
.resolve();
80 that
.pendingFlush_
= null;
84 // Ensures that |this.pendingFlush_| won't be set to null
85 // in the same stack frame.
86 Promise
.resolve().then(flushAll
);
89 return this.pendingFlush_
.promise();
93 * @return {Promise} A promise that resolves on success.
96 remoting
.XhrEventWriter
.prototype.doFlush_ = function() {
100 // Map.forEach enumerates the entires of the map in insertion order.
101 this.pendingRequests_
.forEach(
102 function(/** Object */ event
, /** string */ requestId
) {
103 requestIds
.push(requestId
);
107 return this.doXhr_(requestIds
, {'event': payLoad
});
111 * @return {Promise} A promise that resolves when the pending requests are
114 remoting
.XhrEventWriter
.prototype.writeToStorage = function() {
115 var deferred
= new base
.Deferred();
117 this.pendingRequests_
.forEach(
118 function(/** Object */ request
, /** string */ id
) {
119 map
.push([id
, request
]);
123 entry
[this.storageKey_
] = map
;
124 this.storage_
.set(entry
, deferred
.resolve
.bind(deferred
));
125 return deferred
.promise();
129 * @param {Array<string>} requestIds
130 * @param {Object} event
134 remoting
.XhrEventWriter
.prototype.doXhr_ = function(requestIds
, event
) {
136 var XHR_RETRY_ATTEMPTS
= 20;
137 var xhr
= new remoting
.AutoRetryXhr(
138 {method
: 'POST', url
: this.url_
, jsonContent
: event
, useIdentity
: true},
140 return xhr
.start().then(function(response
) {
141 var error
= remoting
.Error
.fromHttpStatus(response
.status
);
142 // Only store requests that are failed with NETWORK_FAILURE, so that
143 // malformed requests won't be stuck in the client forever.
144 if (!error
.hasTag(remoting
.Error
.Tag
.NETWORK_FAILURE
)) {
145 requestIds
.forEach(function(/** string */ requestId
) {
146 that
.pendingRequests_
.delete(requestId
);
149 if (!error
.isNone()) {
156 * @param {number} length
157 * @return {string} A random string of length |length|
159 function randomString(length
) {
160 var random
= new Uint8Array(length
);
161 window
.crypto
.getRandomValues(random
);
162 return window
.btoa(String
.fromCharCode
.apply(null, random
));
166 * @param {Object} event
169 remoting
.XhrEventWriter
.prototype.markPending_ = function(event
) {
170 var requestId
= Date
.now() + '_' + randomString(16);
171 console
.assert(!this.pendingRequests_
.has(requestId
),
172 'There is already an event with id ' + requestId
+ '.');
173 this.pendingRequests_
.set(requestId
, event
);