Skip to content
This repository has been archived by the owner on Aug 1, 2024. It is now read-only.

Commit

Permalink
Enforce a default size limit (4KB) for messages sent as part of the f…
Browse files Browse the repository at this point in the history
…ast handshake (via a GET).

RELNOTES: enforce a default size limit (4KB) for WebChannel messages sent as part of the fast handshake, which uses GET to enable QUIC 0-RTT.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=239863911
  • Loading branch information
John Plaisted committed Mar 25, 2019
1 parent 43e8f8b commit 1f02db3
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 24 deletions.
4 changes: 2 additions & 2 deletions closure/goog/deps.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 13 additions & 11 deletions closure/goog/labs/net/webchannel.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ goog.net.WebChannel.FailureRecovery = function() {};
* testUrl: the test URL for detecting connectivity during the initial
* handshake. This parameter defaults to "/<channel_url>/test".
*
* sendRawJson: whether to bypass v8 encoding of client-sent messages. Will be
* deprecated after v9 wire protocol is introduced. Only safe to set if the
* server is known to support this feature.
* sendRawJson: whether to bypass v8 encoding of client-sent messages.
* This defaults to false now due to legacy servers. New applications should
* always configure this option to true.
*
* httpSessionIdParam: the URL parameter name that contains the session id (
* for sticky routing of HTTP requests). When this param is specified, a server
Expand All @@ -156,14 +156,16 @@ goog.net.WebChannel.FailureRecovery = function() {};
* This defaults to false. Long-polling may be necessary when a (MITM) proxy
* is buffering data sent by the server.
*
* fastHandshake: experimental feature to enable true 0-RTT message delivery,
* e.g. by leveraging QUIC 0-RTT (which requires GET to be used). This option
* defaults to false. When this option is enabled, backgroundChannelTest will
* be forced to true. Note it is allowed to send messages before Open event is
* received, after a channel has been connected. In order to enable 0-RTT,
* messages need be encoded as part of URL and therefore there needs be a size
* limit (e.g. 16KB) for messages that need be sent immediately
* as part of the handshake.
* fastHandshake: enable true 0-RTT message delivery, including
* leveraging QUIC 0-RTT (which requires GET to be used). This option
* defaults to false. Note it is allowed to send messages before Open event is
* received, after a channel has been opened. In order to enable 0-RTT,
* messages will be encoded as part of URL and therefore there needs be a size
* limit for those initial messages that are sent immediately as part of the
* GET handshake request. With sendRawJson=true, this limit is currently set
* to 4K chars and data beyond this limit will be buffered till the handshake
* (1-RTT) finishes. With sendRawJson=false, it's up to the application
* to limit the amount of data that is sent as part of the handshake.
*
* disableRedact: whether to disable logging redact. By default, redact is
* enabled to remove any message payload or user-provided info
Expand Down
70 changes: 64 additions & 6 deletions closure/goog/labs/net/webchannel/webchannelbase.js
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,15 @@ WebChannelBase.ChannelType_ = {
WebChannelBase.MAX_MAPS_PER_REQUEST_ = 1000;


/**
* The maximum number of utf-8 chars that can be sent in one GET to enable 0-RTT
* handshake.
*
* @const @private {number}
*/
WebChannelBase.MAX_CHARS_PER_GET_ = 4 * 1024;


/**
* A guess at a cutoff at which to no longer assume the backchannel is dead
* when we are slow to receive data. Number in bytes.
Expand All @@ -565,6 +574,14 @@ WebChannelBase.MAX_MAPS_PER_REQUEST_ = 1000;
WebChannelBase.OUTSTANDING_DATA_BACKCHANNEL_RETRY_CUTOFF = 37500;


/**
* @return {number} The server version or 0 if undefined
*/
WebChannelBase.prototype.getServerVersion = function() {
return this.serverVersion_;
};


/**
* @return {!ForwardChannelRequestPool} The forward channel request pool.
*/
Expand Down Expand Up @@ -1269,7 +1286,11 @@ WebChannelBase.prototype.open_ = function() {
request.setExtraHeaders(extraHeaders);
}

var requestText = this.dequeueOutgoingMaps_(request);
var requestText = this.dequeueOutgoingMaps_(
request,
this.fastHandshake_ ? this.getMaxNumMessagesForFastHandshake_() :
WebChannelBase.MAX_MAPS_PER_REQUEST_);

var uri = this.forwardChannelUri_.clone();
uri.setParameterValue('RID', rid);

Expand Down Expand Up @@ -1308,6 +1329,37 @@ WebChannelBase.prototype.open_ = function() {
};


/**
* @return {number} The number of raw JSON messages to be encoded
* with the fast-handshake (GET) request, including zero. If messages are not
* encoded as raw JSON data, return WebChannelBase.MAX_MAPS_PER_REQUEST_
* @private
*/
WebChannelBase.prototype.getMaxNumMessagesForFastHandshake_ = function() {
var total = 0;
for (var i = 0; i < this.outgoingMaps_.length; i++) {
var map = this.outgoingMaps_[i];
var size = map.getRawDataSize();
if (size === undefined) {
break;
}
total += size;

if (total > WebChannelBase.MAX_CHARS_PER_GET_) {
return i;
}

if (total === WebChannelBase.MAX_CHARS_PER_GET_ ||
i === this.outgoingMaps_.length - 1) {
return i + 1;
}
}

return WebChannelBase.MAX_MAPS_PER_REQUEST_;
};



/**
* Makes a forward channel request using XMLHTTP.
* @param {!ChannelRequest=} opt_retryRequest A failed request to retry.
Expand Down Expand Up @@ -1346,7 +1398,8 @@ WebChannelBase.prototype.makeForwardChannelRequest_ = function(
if (opt_retryRequest) {
this.requeuePendingMaps_(opt_retryRequest);
}
requestText = this.dequeueOutgoingMaps_(request);
requestText =
this.dequeueOutgoingMaps_(request, WebChannelBase.MAX_MAPS_PER_REQUEST_);

// Randomize from 50%-100% of the forward channel timeout to avoid
// a big hit if servers happen to die at once.
Expand Down Expand Up @@ -1378,14 +1431,15 @@ WebChannelBase.prototype.addAdditionalParams_ = function(uri) {

/**
* Returns the request text from the outgoing maps and resets it.
* @param {!ChannelRequest=} request The new request for sending the messages.
* @param {!ChannelRequest} request The new request for sending the messages.
* @param {number} maxNum The maximum number of messages to be encoded
* @return {string} The encoded request text created from all the currently
* queued outgoing maps.
* @private
*/
WebChannelBase.prototype.dequeueOutgoingMaps_ = function(request) {
var count =
Math.min(this.outgoingMaps_.length, WebChannelBase.MAX_MAPS_PER_REQUEST_);
WebChannelBase.prototype.dequeueOutgoingMaps_ = function(request, maxNum) {
var count = Math.min(this.outgoingMaps_.length, maxNum);

var badMapHandler = this.handler_ ?
goog.bind(this.handler_.badMapError, this.handler_, this) :
null;
Expand Down Expand Up @@ -1969,6 +2023,10 @@ WebChannelBase.prototype.onInput_ = function(respArray, request) {
}

this.startBackchannelAfterHandshake_(request);

if (this.outgoingMaps_.length > 0) {
this.ensureForwardChannel_();
}
} else if (nextArray[0] == 'stop' || nextArray[0] == 'close') {
// treat close also as an abort
this.signalError_(WebChannelBase.Error.STOP);
Expand Down
6 changes: 4 additions & 2 deletions closure/goog/labs/net/webchannel/webchannelbasetransport.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ goog.require('goog.events.EventTarget');
goog.require('goog.json');
goog.require('goog.labs.net.webChannel.ChannelRequest');
goog.require('goog.labs.net.webChannel.WebChannelBase');
goog.require('goog.labs.net.webChannel.Wire');
goog.require('goog.log');
goog.require('goog.net.WebChannel');
goog.require('goog.net.WebChannelTransport');
Expand Down Expand Up @@ -58,6 +59,7 @@ goog.labs.net.webChannel.WebChannelBaseTransport = function() {
goog.scope(function() {
var WebChannelBaseTransport = goog.labs.net.webChannel.WebChannelBaseTransport;
var WebChannelBase = goog.labs.net.webChannel.WebChannelBase;
var Wire = goog.labs.net.webChannel.Wire;


/**
Expand Down Expand Up @@ -275,11 +277,11 @@ WebChannelBaseTransport.Channel.prototype.send = function(message) {

if (goog.isString(message)) {
var rawJson = {};
rawJson['__data__'] = message;
rawJson[Wire.RAW_DATA_KEY] = message;
this.channel_.sendMap(rawJson);
} else if (this.sendRawJson_) {
var rawJson = {};
rawJson['__data__'] = goog.json.serialize(message);
rawJson[Wire.RAW_DATA_KEY] = goog.json.serialize(message);
this.channel_.sendMap(rawJson);
} else {
this.channel_.sendMap(message);
Expand Down
73 changes: 70 additions & 3 deletions closure/goog/labs/net/webchannel/webchannelbasetransport_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ goog.require('goog.json');
goog.require('goog.labs.net.webChannel.ChannelRequest');
goog.require('goog.labs.net.webChannel.WebChannelBase');
goog.require('goog.labs.net.webChannel.WebChannelBaseTransport');
goog.require('goog.labs.net.webChannel.Wire');
goog.require('goog.net.WebChannel');
goog.require('goog.testing.PropertyReplacer');
goog.require('goog.testing.jsunit');
Expand Down Expand Up @@ -248,11 +249,53 @@ function testOpenWithCorsEnabled() {
assertTrue(webChannel.channel_.supportsCrossDomainXhrs_);
}

function testSendRawJson() {
function testSendRawJsonDefaultValue() {
var channelMsg;
stubs.set(
goog.labs.net.webChannel.WebChannelBase.prototype, 'sendMap',
function(message) { channelMsg = message; });
function(message) {
channelMsg = message;
});

var webChannelTransport =
new goog.labs.net.webChannel.WebChannelBaseTransport();
webChannel = webChannelTransport.createWebChannel(channelUrl);
webChannel.open();

webChannel.send({foo: 'bar'});
assertEquals('bar', channelMsg.foo);
}

function testSendRawJsonUndefinedValue() {
var channelMsg;
stubs.set(
goog.labs.net.webChannel.WebChannelBase.prototype, 'sendMap',
function(message) {
channelMsg = message;
});

var webChannelTransport =
new goog.labs.net.webChannel.WebChannelBaseTransport();
var options = {};
webChannel = webChannelTransport.createWebChannel(channelUrl, options);
webChannel.open();

webChannel.send({foo: 'bar'});
assertEquals('bar', channelMsg.foo);
}

function testSendRawJsonExplicitTrueValue() {
var channelMsg;
stubs.set(
goog.labs.net.webChannel.WebChannelBase.prototype, 'sendMap',
function(message) {
channelMsg = message;
});
stubs.set(
goog.labs.net.webChannel.WebChannelBase.prototype, 'getServerVersion',
function() {
return 12;
});

var webChannelTransport =
new goog.labs.net.webChannel.WebChannelBaseTransport();
Expand All @@ -262,10 +305,34 @@ function testSendRawJson() {

webChannel.send({foo: 'bar'});

var receivedMsg = goog.json.parse(channelMsg['__data__']);
var receivedMsg =
goog.json.parse(channelMsg[goog.labs.net.webChannel.Wire.RAW_DATA_KEY]);
assertEquals('bar', receivedMsg.foo);
}

function testSendRawJsonExplicitFalseValue() {
var channelMsg;
stubs.set(
goog.labs.net.webChannel.WebChannelBase.prototype, 'sendMap',
function(message) {
channelMsg = message;
});
stubs.set(
goog.labs.net.webChannel.WebChannelBase.prototype, 'getServerVersion',
function() {
return 12;
});

var webChannelTransport =
new goog.labs.net.webChannel.WebChannelBaseTransport();
var options = {'sendRawJson': false};
webChannel = webChannelTransport.createWebChannel(channelUrl, options);
webChannel.open();

webChannel.send({foo: 'bar'});
assertEquals('bar', channelMsg.foo);
}

function testOpenThenCloseChannel() {
var webChannelTransport =
new goog.labs.net.webChannel.WebChannelBaseTransport();
Expand Down
23 changes: 23 additions & 0 deletions closure/goog/labs/net/webchannel/wire.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ var Wire = goog.labs.net.webChannel.Wire;
Wire.LATEST_CHANNEL_VERSION = 8;


/**
* The JSON field key for the raw data wrapper object.
* @type {string}
*/
Wire.RAW_DATA_KEY = '__data__';



/**
* Simple container class for a (mapId, map) pair.
Expand Down Expand Up @@ -73,4 +80,20 @@ Wire.QueuedMap = function(mapId, map, opt_context) {
*/
this.context = opt_context || null;
};


/**
* @return {number|undefined} the size of the raw JSON message or
* undefined if the message is not encoded as a raw JSON message
*/
Wire.QueuedMap.prototype.getRawDataSize = function() {
if (Wire.RAW_DATA_KEY in this.map) {
var data = this.map[Wire.RAW_DATA_KEY];
if (goog.isString(data)) {
return data.length;
}
}

return undefined;
};
}); // goog.scope

0 comments on commit 1f02db3

Please sign in to comment.