Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,28 @@ function processLines(buffer, onChunk) {
return buffer;
}

// Parse the docker multiplexed stream format, https://docs.docker.com/reference/api/engine/version/v1.43/#stream-format
function parse_multiplexed_stream(buffer) {
const output = [];
// Only three stream types are defined: 0=stdin, 1=stdout, 2=stderr.
const decoders = [new TextDecoder(), new TextDecoder(), new TextDecoder()];
Comment thread
jjm2473 marked this conversation as resolved.

while (buffer.length > 0) {
if (buffer.length < 8) break; // Not enough data for header
let stream_type = buffer[0];
let payload_length = ((buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]) >>> 0; // Convert to unsigned
if (buffer.length < 8 + payload_length) break; // Not enough data for payload
if (stream_type <= 2) {
let payload = buffer.subarray(8, 8 + payload_length);
output.push({ type: stream_type, payload: decoders[stream_type].decode(payload, { stream: true }) });
} else {
console.warn(`Unknown stream type ${stream_type} in multiplexed stream, ignoring`);
}
buffer = buffer.subarray(8 + payload_length);
}

return output;
}

function call_docker(method, path, options = {}) {
return loadPromise.then(() => {
Expand Down Expand Up @@ -223,6 +245,17 @@ function call_docker(method, path, options = {}) {
headersObj[key] = value;
}

if ("application/vnd.docker.multiplexed-stream" === headersObj['content-type']) {
return response.arrayBuffer().then(buffer => {
const parsed = parse_multiplexed_stream(new Uint8Array(buffer));
return {
code: response.status,
body: parsed,
headers: headersObj,
};
});
}

return response.text().then(text => {
const safeText = (typeof text === 'string') ? text : '';
let parsed = safeText || text;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1685,7 +1685,12 @@ return dm2.dv.extend({
return false;
}

const logText = response?.body || _('No logs available');
let logText = response?.body;
if ("application/vnd.docker.multiplexed-stream" === response?.headers?.['content-type']) {
logText = (response?.body || []).map(frame => frame.payload).join('');
}
logText = logText || _('No logs available');

// Convert ANSI codes to HTML and set innerHTML
logsDiv.innerHTML = dm2.ansiToHtml(logText);
logsDiv.scrollTop = logsDiv.scrollHeight;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,23 @@ function coerce_values_to_string(obj) {
return obj;
};

// Parse the docker multiplexed stream format, https://docs.docker.com/reference/api/engine/version/v1.43/#stream-format
function parse_multiplexed_stream(buffer) {
let output = [];

while (length(buffer) > 0) {
if (length(buffer) < 8) break; // Not enough data for header
let stream_type = ord(buffer, 0);
let payload_length = ord(buffer, 4) << 24 | ord(buffer, 5) << 16 | ord(buffer, 6) << 8 | ord(buffer, 7);
if (length(buffer) < 8 + payload_length) break; // Not enough data for payload
let payload = substr(buffer, 8, payload_length);
push(output, { type: stream_type, payload: payload });
buffer = substr(buffer, 8 + payload_length);
}

return output;
};

function call_docker(method, path, options) {
options = options || {};
const headers = options.headers || {};
Expand Down Expand Up @@ -277,6 +294,8 @@ function call_docker(method, path, options) {
} else {
response_body = data;
}
} else if (response_headers['content-type'] === 'application/vnd.docker.multiplexed-stream' && response_body) {
response_body = parse_multiplexed_stream(response_body);
}

return {
Expand Down
Loading