1'use strict'; 2 3const { 4 JSONParse, 5 JSONStringify, 6 Symbol, 7} = primordials; 8const { Buffer } = require('buffer'); 9const { StringDecoder } = require('string_decoder'); 10const v8 = require('v8'); 11const { isArrayBufferView } = require('internal/util/types'); 12const assert = require('internal/assert'); 13 14const kMessageBuffer = Symbol('kMessageBuffer'); 15const kJSONBuffer = Symbol('kJSONBuffer'); 16const kStringDecoder = Symbol('kStringDecoder'); 17 18// Extend V8's serializer APIs to give more JSON-like behaviour in 19// some cases; in particular, for native objects this serializes them the same 20// way that JSON does rather than throwing an exception. 21const kArrayBufferViewTag = 0; 22const kNotArrayBufferViewTag = 1; 23class ChildProcessSerializer extends v8.DefaultSerializer { 24 _writeHostObject(object) { 25 if (isArrayBufferView(object)) { 26 this.writeUint32(kArrayBufferViewTag); 27 return super._writeHostObject(object); 28 } 29 this.writeUint32(kNotArrayBufferViewTag); 30 this.writeValue({ ...object }); 31 } 32} 33 34class ChildProcessDeserializer extends v8.DefaultDeserializer { 35 _readHostObject() { 36 const tag = this.readUint32(); 37 if (tag === kArrayBufferViewTag) 38 return super._readHostObject(); 39 40 assert(tag === kNotArrayBufferViewTag); 41 return this.readValue(); 42 } 43} 44 45// Messages are parsed in either of the following formats: 46// - Newline-delimited JSON, or 47// - V8-serialized buffers, prefixed with their length as a big endian uint32 48// (aka 'advanced') 49const advanced = { 50 initMessageChannel(channel) { 51 channel[kMessageBuffer] = Buffer.alloc(0); 52 channel.buffering = false; 53 }, 54 55 *parseChannelMessages(channel, readData) { 56 if (readData.length === 0) return; 57 58 let messageBuffer = Buffer.concat([channel[kMessageBuffer], readData]); 59 while (messageBuffer.length > 4) { 60 const size = messageBuffer.readUInt32BE(); 61 if (messageBuffer.length < 4 + size) { 62 break; 63 } 64 65 const deserializer = new ChildProcessDeserializer( 66 messageBuffer.subarray(4, 4 + size)); 67 messageBuffer = messageBuffer.subarray(4 + size); 68 69 deserializer.readHeader(); 70 yield deserializer.readValue(); 71 } 72 channel[kMessageBuffer] = messageBuffer; 73 channel.buffering = messageBuffer.length > 0; 74 }, 75 76 writeChannelMessage(channel, req, message, handle) { 77 const ser = new ChildProcessSerializer(); 78 ser.writeHeader(); 79 ser.writeValue(message); 80 const serializedMessage = ser.releaseBuffer(); 81 const sizeBuffer = Buffer.allocUnsafe(4); 82 sizeBuffer.writeUInt32BE(serializedMessage.length); 83 return channel.writeBuffer(req, Buffer.concat([ 84 sizeBuffer, 85 serializedMessage 86 ]), handle); 87 }, 88}; 89 90const json = { 91 initMessageChannel(channel) { 92 channel[kJSONBuffer] = ''; 93 channel[kStringDecoder] = undefined; 94 }, 95 96 *parseChannelMessages(channel, readData) { 97 if (readData.length === 0) return; 98 99 if (channel[kStringDecoder] === undefined) 100 channel[kStringDecoder] = new StringDecoder('utf8'); 101 const chunks = channel[kStringDecoder].write(readData).split('\n'); 102 const numCompleteChunks = chunks.length - 1; 103 // Last line does not have trailing linebreak 104 const incompleteChunk = chunks[numCompleteChunks]; 105 if (numCompleteChunks === 0) { 106 channel[kJSONBuffer] += incompleteChunk; 107 } else { 108 chunks[0] = channel[kJSONBuffer] + chunks[0]; 109 for (let i = 0; i < numCompleteChunks; i++) 110 yield JSONParse(chunks[i]); 111 channel[kJSONBuffer] = incompleteChunk; 112 } 113 channel.buffering = channel[kJSONBuffer].length !== 0; 114 }, 115 116 writeChannelMessage(channel, req, message, handle) { 117 const string = JSONStringify(message) + '\n'; 118 return channel.writeUtf8String(req, string, handle); 119 }, 120}; 121 122module.exports = { advanced, json }; 123