• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Flags: --experimental-network-imports --dns-result-order=ipv4first
2import * as common from '../common/index.mjs';
3import { path, readKey } from '../common/fixtures.mjs';
4import { pathToFileURL } from 'url';
5import assert from 'assert';
6import http from 'http';
7import os from 'os';
8import util from 'util';
9
10if (!common.hasCrypto) {
11  common.skip('missing crypto');
12}
13
14const https = (await import('https')).default;
15
16const createHTTPServer = http.createServer;
17
18// Needed to deal w/ test certs
19process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
20const options = {
21  key: readKey('agent1-key.pem'),
22  cert: readKey('agent1-cert.pem')
23};
24
25const createHTTPSServer = https.createServer.bind(null, options);
26
27
28const testListeningOptions = [
29  {
30    hostname: 'localhost',
31    listenOptions: {
32      host: '127.0.0.1'
33    }
34  },
35];
36
37const internalInterfaces = Object.values(os.networkInterfaces()).flat().filter(
38  (iface) => iface?.internal && iface.address && !iface.scopeid
39);
40for (const iface of internalInterfaces) {
41  testListeningOptions.push({
42    hostname: iface?.family === 'IPv6' ? `[${iface?.address}]` : iface?.address,
43    listenOptions: {
44      host: iface?.address,
45      ipv6Only: iface?.family === 'IPv6'
46    }
47  });
48}
49
50for (const { protocol, createServer } of [
51  { protocol: 'http:', createServer: createHTTPServer },
52  { protocol: 'https:', createServer: createHTTPSServer },
53]) {
54  const body = `
55    export default (a) => () => a;
56    export let url = import.meta.url;
57  `;
58
59  const base = 'http://127.0.0.1';
60  for (const { hostname, listenOptions } of testListeningOptions) {
61    const host = new URL(base);
62    host.protocol = protocol;
63    host.hostname = hostname;
64    // /not-found is a 404
65    // ?redirect causes a redirect, no body. JSON.parse({status:number,location:string})
66    // ?mime sets the content-type, string
67    // ?body sets the body, string
68    const server = createServer(function(_req, res) {
69      const url = new URL(_req.url, host);
70      const redirect = url.searchParams.get('redirect');
71      if (url.pathname === '/not-found') {
72        res.writeHead(404);
73        res.end();
74        return;
75      }
76      if (redirect) {
77        const { status, location } = JSON.parse(redirect);
78        res.writeHead(status, {
79          location
80        });
81        res.end();
82        return;
83      }
84      res.writeHead(200, {
85        'content-type': url.searchParams.get('mime') || 'text/javascript'
86      });
87      res.end(url.searchParams.get('body') || body);
88    });
89
90    const listen = util.promisify(server.listen.bind(server));
91    await listen({
92      ...listenOptions,
93      port: 0
94    });
95    const url = new URL(host);
96    url.port = server?.address()?.port;
97
98    const ns = await import(url.href);
99    assert.strict.deepStrictEqual(Object.keys(ns), ['default', 'url']);
100    const obj = {};
101    assert.strict.equal(ns.default(obj)(), obj);
102    assert.strict.equal(ns.url, url.href);
103
104    // Redirects have same import.meta.url but different cache
105    // entry on Web
106    const redirect = new URL(url.href);
107    redirect.searchParams.set('redirect', JSON.stringify({
108      status: 302,
109      location: url.href
110    }));
111    const redirectedNS = await import(redirect.href);
112    assert.strict.deepStrictEqual(
113      Object.keys(redirectedNS),
114      ['default', 'url']
115    );
116    assert.strict.notEqual(redirectedNS.default, ns.default);
117    assert.strict.equal(redirectedNS.url, url.href);
118
119    // Redirects have the same import.meta.url but different cache
120    // entry on Web
121    const relativeAfterRedirect = new URL(url.href + 'foo/index.js');
122    const redirected = new URL(url.href + 'bar/index.js');
123    redirected.searchParams.set('body', 'export let relativeDepURL = (await import("./baz.js")).url');
124    relativeAfterRedirect.searchParams.set('redirect', JSON.stringify({
125      status: 302,
126      location: redirected.href
127    }));
128    const relativeAfterRedirectedNS = await import(relativeAfterRedirect.href);
129    assert.strict.equal(
130      relativeAfterRedirectedNS.relativeDepURL,
131      url.href + 'bar/baz.js'
132    );
133
134    const crossProtocolRedirect = new URL(url.href);
135    crossProtocolRedirect.searchParams.set('redirect', JSON.stringify({
136      status: 302,
137      location: 'data:text/javascript,'
138    }));
139    await assert.rejects(
140      import(crossProtocolRedirect.href),
141      { code: 'ERR_NETWORK_IMPORT_DISALLOWED' }
142    );
143
144    const deps = new URL(url.href);
145    deps.searchParams.set('body', `
146      export {data} from 'data:text/javascript,export let data = 1';
147      import * as http from ${JSON.stringify(url.href)};
148      export {http};
149    `);
150    const depsNS = await import(deps.href);
151    assert.strict.deepStrictEqual(Object.keys(depsNS), ['data', 'http']);
152    assert.strict.equal(depsNS.data, 1);
153    assert.strict.equal(depsNS.http, ns);
154
155    const relativeDeps = new URL(url.href);
156    relativeDeps.searchParams.set('body', `
157      import * as http from "./";
158      export {http};
159    `);
160    const relativeDepsNS = await import(relativeDeps.href);
161    assert.strict.deepStrictEqual(Object.keys(relativeDepsNS), ['http']);
162    assert.strict.equal(relativeDepsNS.http, ns);
163    const fileDep = new URL(url.href);
164    const { href } = pathToFileURL(path('/es-modules/message.mjs'));
165    fileDep.searchParams.set('body', `
166      import ${JSON.stringify(href)};
167      export default 1;`);
168    await assert.rejects(
169      import(fileDep.href),
170      { code: 'ERR_NETWORK_IMPORT_DISALLOWED' }
171    );
172
173    const builtinDep = new URL(url.href);
174    builtinDep.searchParams.set('body', `
175      import 'node:fs';
176      export default 1;
177    `);
178    await assert.rejects(
179      import(builtinDep.href),
180      { code: 'ERR_NETWORK_IMPORT_DISALLOWED' }
181    );
182
183    const unprefixedBuiltinDep = new URL(url.href);
184    unprefixedBuiltinDep.searchParams.set('body', `
185      import 'fs';
186      export default 1;
187    `);
188    await assert.rejects(
189      import(unprefixedBuiltinDep.href),
190      { code: 'ERR_NETWORK_IMPORT_DISALLOWED' }
191    );
192
193    const unsupportedMIME = new URL(url.href);
194    unsupportedMIME.searchParams.set('mime', 'application/node');
195    unsupportedMIME.searchParams.set('body', '');
196    await assert.rejects(
197      import(unsupportedMIME.href),
198      { code: 'ERR_UNKNOWN_MODULE_FORMAT' }
199    );
200    const notFound = new URL(url.href);
201    notFound.pathname = '/not-found';
202    await assert.rejects(
203      import(notFound.href),
204      { code: 'ERR_MODULE_NOT_FOUND' },
205    );
206
207    server.close();
208  }
209}
210