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