buffer.callback = function(data) { var ret = 0; var pos = 0; var parts; /* Look for end of headers first */ if (headers === null) { pos = sequence_find(data, [ 13, 10, 13, 10 ]); if (pos == -1) return ret; if (data.subarray) headers = cockpit.utf8_decoder().decode(data.subarray(0, pos)); else headers = cockpit.utf8_decoder().decode(data.slice(0, pos)); docker_debug(container_id + ": console headers: ", headers); parts = headers.split("\r\n", 1)[0].split(" "); if (parts[1] != "200") { failure(parts.slice(2).join(" ")); buffer.callback = null; return; } else if (data.subarray) { data = data.subarray(pos + 4); ret += pos + 4; } else { data = data.slice(pos + 4); ret += pos + 4; } } /* We need at least two bytes to determine stream type */ if (tty === undefined || tty === null) { if (data.length < 2) return ret; tty = !((data[0] === 0 || data[0] === 1 || data[0] === 2) && data[1] === 0); docker_debug(container_id + ": mode tty: " + tty); } if (tty) view = new DockerTerminal(self, channel); else view = new DockerLogs(self, channel); self.typeable(want_typeable); self.connected = true; buffer.callback = view.process; var consumed = view.process(data); return ret + consumed; };
function DockerTerminal(parent, channel) { var self = this; var term = new Terminal({ cols: 80, rows: 24, screenKeys: true, inlineStyle: false, useFocus: false, }); var enable_input = true; var decoder = cockpit.utf8_decoder(); var encoder = cockpit.utf8_encoder(); /* term.js wants the parent element to build its terminal inside of */ parent.empty(); term.open(parent[0]); /* Shows and hides the cursor */ self.typeable = function typeable(yes) { term.cursorHidden = !yes; term.refresh(term.y, term.y); enable_input = yes; }; self.focus = function focus() { term.focus(); }; /* Allows caller to cleanup nicely */ self.close = function close() { term.destroy(); }; if (typeof channel == "string") { term.write('\x1b[31m' + channel + '\x1b[m\r\n'); self.close = function() { }; self.typeable(false); return self; } $(channel) .on("close", function(ev, options) { var problem = options.problem || "disconnected"; term.write('\x1b[31m' + problem + '\x1b[m\r\n'); self.typeable(false); $(channel).off("message"); channel = null; }); self.process = function process(buffer) { term.write(decoder.decode(buffer)); return buffer.length; }; term.on('data', function(data) { /* Send typed input back through channel */ if (enable_input && channel) channel.send(encoder.encode(data)); }); return self; }
function DockerLogs(parent, channel) { var self = this; var pre = $("<pre>"); parent.empty(); parent.append(pre); var wait; var writing = []; function write(data) { writing.push(data); if (!wait) { wait = window.setTimeout(function() { wait = null; var at_bottom = pre[0].scrollHeight - pre.scrollTop() <= pre.outerHeight(); var span = $("<span>").text(writing.join("")); writing.length = 0; pre.append(span); if (at_bottom) pre.scrollTop(pre.prop("scrollHeight")); }, 50); } } /* Just display the failure */ if (typeof channel == "string") { write(channel); self.close = function() { }; return self; } channel.control({ batch: 16384, latency: 50 }); var decoder = cockpit.utf8_decoder(false); self.process = function process(buffer) { var at = 0; var size, block; var length = buffer.length; while (true) { if (length < at + 8) return at; /* more data */ size = ((buffer[at + 4] & 0xFF) << 24) | ((buffer[at + 5] & 0xFF) << 16) | ((buffer[at + 6] & 0xFF) << 8) | (buffer[at + 7] & 0xFF); if (length < at + 8 + size) return at; /* more data */ /* Output the data */ if (buffer.subarray) block = buffer.subarray(at + 8, at + 8 + size); else block = buffer.slice(at + 8, at + 8 + size); write(decoder.decode(block, { stream: true })); at += 8 + size; } }; self.focus = function() { /* Nothing to do */ }; self.close = function() { /* Nothing to do */ }; /* * A raw channel over which we speak Docker's even stranger /logs * protocol. It starts with a HTTP GET, and then quickly * degenerates into a stream with framing. */ $(channel) .on("close", function(ev, options) { write(options.reason || "disconnected"); $(channel).off(); channel = null; }); return self; }
export function decode_filename(encoded) { return cockpit.utf8_decoder().decode(cockpit.base64_decode(encoded).slice(0, -1)); }