1'use strict';
2const common = require('../common');
3const assert = require('assert');
4const fs = require('fs').promises;
5const vm = require('vm');
6const { MessageChannel, moveMessagePortToContext } = require('worker_threads');
7const { once } = require('events');
8
9(async function() {
10  const fh = await fs.open(__filename);
11
12  const { port1, port2 } = new MessageChannel();
13
14  assert.throws(() => {
15    port1.postMessage(fh);
16  }, {
17    code: 'ERR_MISSING_TRANSFERABLE_IN_TRANSFER_LIST'
18  });
19
20  // Check that transferring FileHandle instances works.
21  assert.notStrictEqual(fh.fd, -1);
22  port1.postMessage(fh, [ fh ]);
23  assert.strictEqual(fh.fd, -1);
24
25  const [ fh2 ] = await once(port2, 'message');
26  assert.strictEqual(Object.getPrototypeOf(fh2), Object.getPrototypeOf(fh));
27
28  assert.deepStrictEqual(await fh2.readFile(), await fs.readFile(__filename));
29  await fh2.close();
30
31  assert.rejects(() => fh.readFile(), { code: 'EBADF' });
32})().then(common.mustCall());
33
34(async function() {
35  // Check that there is no crash if the message is never read.
36  const fh = await fs.open(__filename);
37
38  const { port1 } = new MessageChannel();
39
40  assert.notStrictEqual(fh.fd, -1);
41  port1.postMessage(fh, [ fh ]);
42  assert.strictEqual(fh.fd, -1);
43})().then(common.mustCall());
44
45(async function() {
46  // Check that in the case of a context mismatch the message is discarded.
47  const fh = await fs.open(__filename);
48
49  const { port1, port2 } = new MessageChannel();
50
51  const ctx = vm.createContext();
52  const port2moved = moveMessagePortToContext(port2, ctx);
53  port2moved.onmessage = common.mustCall((msgEvent) => {
54    assert.strictEqual(msgEvent.data, 'second message');
55    port1.close();
56  });
57  // TODO(addaleax): Switch this to a 'messageerror' event once MessagePort
58  // implements EventTarget fully and in a cross-context manner.
59  port2moved.onmessageerror = common.mustCall((event) => {
60    assert.strictEqual(event.data.code,
61                       'ERR_MESSAGE_TARGET_CONTEXT_UNAVAILABLE');
62  });
63  port2moved.start();
64
65  assert.notStrictEqual(fh.fd, -1);
66  port1.postMessage(fh, [ fh ]);
67  assert.strictEqual(fh.fd, -1);
68
69  port1.postMessage('second message');
70})().then(common.mustCall());
71
72(async function() {
73  // Check that a FileHandle with a read in progress cannot be transferred.
74  const fh = await fs.open(__filename);
75
76  const { port1 } = new MessageChannel();
77
78  const readPromise = fh.readFile();
79  assert.throws(() => {
80    port1.postMessage(fh, [fh]);
81  }, {
82    message: 'Cannot transfer FileHandle while in use',
83    name: 'DataCloneError'
84  });
85
86  assert.deepStrictEqual(await readPromise, await fs.readFile(__filename));
87})().then(common.mustCall());
88
89(async function() {
90  // Check that filehandles with a close in progress cannot be transferred.
91  const fh = await fs.open(__filename);
92
93  const { port1 } = new MessageChannel();
94
95  const closePromise = fh.close();
96  assert.throws(() => {
97    port1.postMessage(fh, [fh]);
98  }, {
99    message: 'Cannot transfer FileHandle while in use',
100    name: 'DataCloneError'
101  });
102  await closePromise;
103})().then(common.mustCall());
104