• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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