base1: Break input into small blocks
The WebSocket protocol only allows messages up to 128K. Therefore data larger than that size, such as file write data, or input into processes' stdin need to be broken into smaller chunks. Lets not rely on the caller of various cockpit.xxxx() functions to do that. We know the places where it's necessary. Issue #12338 Closes #13149
This commit is contained in:
parent
3c8d134f89
commit
d1ea17988c
|
@ -95,6 +95,32 @@ function invoke_functions(functions, self, args) {
|
|||
}
|
||||
}
|
||||
|
||||
function iterate_data(data, callback, batch) {
|
||||
var binary = false;
|
||||
var i, n;
|
||||
var len = 0;
|
||||
|
||||
if (!batch)
|
||||
batch = 64 * 1024;
|
||||
|
||||
if (data) {
|
||||
if (data.byteLength) {
|
||||
len = data.byteLength;
|
||||
binary = true;
|
||||
} else if (data.length) {
|
||||
len = data.length;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i += batch) {
|
||||
n = Math.min(len - i, batch);
|
||||
if (binary)
|
||||
callback(new window.Uint8Array(data.buffer, i, n));
|
||||
else
|
||||
callback(data.substr(i, n));
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------
|
||||
* Channels
|
||||
*
|
||||
|
@ -2818,7 +2844,9 @@ function factory() {
|
|||
ret.input = function(message, stream) {
|
||||
if (message !== null && message !== undefined) {
|
||||
spawn_debug("process input:", message);
|
||||
channel.send(message);
|
||||
iterate_data(message, function(data) {
|
||||
channel.send(data);
|
||||
});
|
||||
}
|
||||
if (!stream)
|
||||
channel.control({ command: "done" });
|
||||
|
@ -3740,26 +3768,9 @@ function factory() {
|
|||
}
|
||||
});
|
||||
|
||||
var len = 0;
|
||||
var binary = false;
|
||||
if (file_content) {
|
||||
if (file_content.byteLength) {
|
||||
len = file_content.byteLength;
|
||||
binary = true;
|
||||
} else if (file_content.length) {
|
||||
len = file_content.length;
|
||||
}
|
||||
}
|
||||
|
||||
var i, n;
|
||||
var batch = 16 * 1024;
|
||||
for (i = 0; i < len; i += batch) {
|
||||
n = Math.min(len - i, batch);
|
||||
if (binary)
|
||||
replace_channel.send(new window.Uint8Array(file_content.buffer, i, n));
|
||||
else
|
||||
replace_channel.send(file_content.substr(i, n));
|
||||
}
|
||||
iterate_data(file_content, function(data) {
|
||||
replace_channel.send(data);
|
||||
});
|
||||
|
||||
replace_channel.control({ command: "done" });
|
||||
return dfd.promise;
|
||||
|
@ -4150,7 +4161,9 @@ function factory() {
|
|||
if (input !== undefined) {
|
||||
if (input !== "") {
|
||||
http_debug("http input:", input);
|
||||
channel.send(input);
|
||||
iterate_data(input, function(data) {
|
||||
channel.send(data);
|
||||
});
|
||||
}
|
||||
http_debug("http done");
|
||||
channel.control({ command: "done" });
|
||||
|
@ -4227,7 +4240,9 @@ function factory() {
|
|||
ret.input = function(message, stream) {
|
||||
if (message !== null && message !== undefined) {
|
||||
http_debug("http input:", message);
|
||||
channel.send(message);
|
||||
iterate_data(message, function(data) {
|
||||
channel.send(data);
|
||||
});
|
||||
}
|
||||
if (!stream) {
|
||||
http_debug("http done");
|
||||
|
|
|
@ -27,12 +27,8 @@ function MockPeer() {
|
|||
/* nada */
|
||||
};
|
||||
|
||||
/* get event: triggered when we receive a get request */
|
||||
this.onget = function(event, channel, request) {
|
||||
if (event.isDefaultPrevented())
|
||||
return false;
|
||||
if (request.path == "/")
|
||||
this.reply(channel, request, { key: "value" });
|
||||
this.oncontrol = function(event, channel, options) {
|
||||
/* nada */
|
||||
};
|
||||
|
||||
/* send a message from peer back to channel */
|
||||
|
@ -81,6 +77,13 @@ function MockPeer() {
|
|||
window.setTimeout(function() { $(peer).trigger("recv", [channel, payload]) }, 5);
|
||||
};
|
||||
|
||||
this.control = function(options) {
|
||||
console.assert(typeof command === 'string');
|
||||
console.assert(options !== null && typeof options === 'object');
|
||||
console.assert(arguments.length == 1);
|
||||
window.setTimeout(function() { $(peer).trigger("control", [channel, options]) }, 5);
|
||||
};
|
||||
|
||||
this.close = function(options) {
|
||||
console.assert(arguments.length <= 1);
|
||||
this.valid = false;
|
||||
|
@ -157,6 +160,69 @@ QUnit.test("simple request", function (assert) {
|
|||
});
|
||||
});
|
||||
|
||||
QUnit.test("input large", function (assert) {
|
||||
const done = assert.async();
|
||||
assert.expect(25);
|
||||
|
||||
var str = new Array(128 * 1024).join('abcdef12345');
|
||||
var output = "";
|
||||
var count = 0;
|
||||
|
||||
var peer = new MockPeer();
|
||||
$(peer).on("recv", function(event, channel, payload) {
|
||||
assert.ok(typeof (payload) == "string", "got payload");
|
||||
output += payload;
|
||||
count += 1;
|
||||
});
|
||||
$(peer).on("control", function(event, channel, options) {
|
||||
if (options.command == "done")
|
||||
this.close(channel);
|
||||
});
|
||||
|
||||
cockpit.spawn(["/path/to/command"])
|
||||
.input(str)
|
||||
.always(function() {
|
||||
assert.equal(this.state(), "resolved", "didn't fail");
|
||||
assert.equal(str, output, "right output");
|
||||
assert.ok(count > 1, "broken into multiple blocks");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test("binary large", function (assert) {
|
||||
const done = assert.async();
|
||||
assert.expect(10);
|
||||
|
||||
var data = new Uint8Array(249 * 1023);
|
||||
var i;
|
||||
var len = data.byteLength;
|
||||
for (i = 0; i < len; i++)
|
||||
data[i] = i % 233;
|
||||
|
||||
var count = 0;
|
||||
|
||||
var peer = new MockPeer();
|
||||
$(peer).on("recv", function(event, channel, payload) {
|
||||
console.log(typeof (payload), payload.constructor);
|
||||
assert.equal(typeof (payload), "object", "got payload");
|
||||
assert.equal(payload.constructor, Uint8Array, "right binary array");
|
||||
count += 1;
|
||||
});
|
||||
$(peer).on("control", function(event, channel, options) {
|
||||
console.log("control", options);
|
||||
if (options.command == "done")
|
||||
this.close(channel);
|
||||
});
|
||||
|
||||
cockpit.spawn(["/ptah/to/command"])
|
||||
.input(data)
|
||||
.always(function() {
|
||||
assert.equal(this.state(), "resolved", "didn't fail");
|
||||
assert.ok(count > 1, "broken into multiple blocks");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test("string command", function (assert) {
|
||||
const done = assert.async();
|
||||
assert.expect(2);
|
||||
|
|
Loading…
Reference in New Issue