Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | 6x 6x 6x 6x 119x 119x 119x 119x 119x 51942x 51942x 51942x 51940x 51940x 51942x 113x 1x 1x 113x 118x 118x 118x 118x 118x 51943x 2x 51941x 51941x 51941x 51941x 51941x 51941x 51941x 51941x 5x 5x 5x 51941x 51936x 51936x 51936x 51936x 1x 51935x 51935x 51941x 51941x 6x 6x 6x 6x 51936x 51936x 1x 2x 5x 1x 6x 6x 6x 6x 6x | const assert = require('assert'); const util = require('util'); const stream = require('stream'); const { InternalZyspawnError } = require('./error'); /** * Transform a stream of bytes into a stream of String (break by newline). */ class LineTransform extends stream.Transform { constructor(options) { options = options || {}; options.readableObjectMode = true; options.decodeStrings = false; super(options); this._buffer = ""; } _transform(data, encoding, callback) { this._buffer += data; let end = this._buffer.indexOf('\n'); if (end != -1) { this.push(this._buffer.substring(0, end)); this._buffer = this._buffer.substring(end+1); } callback(); } _flush(callback) { if (this._buffer.length > 0) { this.push(this._buffer); this._buffer = ""; } callback(); } } /** * Wraps the logic of two-way communication through two pipes (one to * send and another to receive). Messages are expected to be strings of * JSON objects and seperated by newlines. * * Timeout is also supported. Note that a timeout of one message will * lead to errors in all subsequent message sending. This is because * the sender cannot distinguish a late response for a timeout message * from the response for the current message (unless we specify an ID * for each message, which adds more complexity). */ class Port { /** * @param {stream.Writable} sender A raw stream to send data to * @param {stream.Readable} receiver A raw stream to receive data from */ constructor(sender, receiver) { this._in = receiver.pipe(new LineTransform()); this._out = sender; this._busy = false; this._waiting_jobs = []; this._broken = false; } /** * Send an object and get a response. * @param {Number} timeout Maximum waiting time (0 means no timeout) * @param {Object} obj The object to send * @param {Function(Error, Object)} callback Called when response received * or any error happens */ send(obj, timeout, callback) { if (this._broken) { callback(new PortBrokenError()); } else { this._waiting_jobs.push({obj: obj, timeout: timeout, callback: callback}); Eif (!this._busy) { this._start_next_job(); } } } /** * Check if this Port is busy (waiting for some response). */ isBusy() { return this._busy; } /** * Check if this Port is broken (unable to send/receive objects) */ isBroken() { return this._broken; } _start_next_job() { assert(!this._busy); assert(this._waiting_jobs.length != 0); this._busy = true; let job = this._waiting_jobs.shift(); let timer, dataReceivedCallback; timer = job.timeout === 0 ? null : setTimeout(() => { // clean up this._in.removeListener('data', dataReceivedCallback); this._busy = false; this._break(job, new PortTimeoutError(job.timeout)); }, job.timeout); dataReceivedCallback = (response) => { // clean up Eif (job.timeout !== 0) clearTimeout(timer); this._busy = false; let obj = parseJSON(response); if (obj instanceof SyntaxError) { this._break(job, new PortParseError(response)); } else { Iif (this._waiting_jobs.length != 0) this._start_next_job(); job.callback(null, obj); } }; this._in.once('data', dataReceivedCallback); this._out.write(JSON.stringify(job.obj) + '\n'); } _break(current_job, err) { this._broken = true; current_job.callback(err); this._waiting_jobs.forEach((job) => { job.callback(new PortBrokenError()); }); this._waiting_jobs.length = 0; } } function parseJSON(str) { try { return JSON.parse(str); } catch (error) { return error; } } class PortBrokenError extends InternalZyspawnError { constructor() { super("Error occurs in previous messages"); } } class PortTimeoutError extends InternalZyspawnError { constructor(timeout) { super(`Response not received in specified time limit: ${timeout}`); } } class PortParseError extends InternalZyspawnError { constructor(response) { super(`Bad response format: ${response}`); } } module.exports.LineTransform = LineTransform; module.exports.Port = Port; module.exports.PortBrorkenError = PortBrokenError; module.exports.PortTimeoutError = PortTimeoutError; module.exports.PortParseError = PortParseError; |