diff --git a/.changeset/fix-blob-worker-socket-url.md b/.changeset/fix-blob-worker-socket-url.md new file mode 100644 index 0000000000..40d2aace81 --- /dev/null +++ b/.changeset/fix-blob-worker-socket-url.md @@ -0,0 +1,5 @@ +--- +"webpack-dev-server": patch +--- + +Connect the client to the page's host when it runs in a worker started from a `blob:` URL, instead of `0.0.0.0` or an insecure `ws:` socket on HTTPS pages. diff --git a/client-src/index.js b/client-src/index.js index 59aafb8036..4fb4e80ab4 100644 --- a/client-src/index.js +++ b/client-src/index.js @@ -724,6 +724,13 @@ const formatURL = (objURL) => { * @returns {string} socket URL */ const createSocketURL = (parsedURL) => { + // Blob workers have an empty hostname; their origin is the page that created them. + const pageLocation = + self.location.protocol === "blob:" && + self.location.origin && + self.location.origin !== "null" + ? new URL(self.location.origin) + : self.location; let { hostname } = parsedURL; // Node.js module parses it as `::` @@ -736,20 +743,20 @@ const createSocketURL = (parsedURL) => { // see: https://github.com/webpack/webpack-dev-server/pull/384 if ( isInAddrAny && - self.location.hostname && - self.location.protocol.indexOf("http") === 0 + pageLocation.hostname && + pageLocation.protocol.indexOf("http") === 0 ) { - hostname = self.location.hostname; + hostname = pageLocation.hostname; } - let socketURLProtocol = parsedURL.protocol || self.location.protocol; + let socketURLProtocol = parsedURL.protocol || pageLocation.protocol; // When https is used in the app, secure web sockets are always necessary because the browser doesn't accept non-secure web sockets. if ( socketURLProtocol === "auto:" || - (hostname && isInAddrAny && self.location.protocol === "https:") + (hostname && isInAddrAny && pageLocation.protocol === "https:") ) { - socketURLProtocol = self.location.protocol; + socketURLProtocol = pageLocation.protocol; } socketURLProtocol = socketURLProtocol.replace( @@ -796,14 +803,14 @@ const createSocketURL = (parsedURL) => { // so we need to fall back to the default if they are not provided const socketURLHostname = ( hostname || - self.location.hostname || + pageLocation.hostname || "localhost" ).replace(/^\[(.*)\]$/, "$1"); let socketURLPort = parsedURL.port; if (!socketURLPort || socketURLPort === "0") { - socketURLPort = self.location.port; + socketURLPort = pageLocation.port; } // If path is provided it'll be passed in via the resourceQuery as a diff --git a/test/client/utils/createSocketURL.test.js b/test/client/utils/createSocketURL.test.js index 20a4f63b11..398c62877f 100644 --- a/test/client/utils/createSocketURL.test.js +++ b/test/client/utils/createSocketURL.test.js @@ -116,6 +116,26 @@ describe("'createSocketURL' function", () => { [null, "file:///home/user/project/index.html", "ws://localhost/ws"], [null, "chrome-extension://localhost/", "ws://localhost/ws"], [null, "file://localhost/", "ws://localhost/ws"], + [ + "?protocol=ws:&hostname=0.0.0.0&port=0", + "blob:https://example.com/worker-id", + "wss://example.com/ws", + ], + [ + "?protocol=ws:&hostname=0.0.0.0&port=0", + "blob:http://localhost:8080/worker-id", + "ws://localhost:8080/ws", + ], + [ + "?hostname=0.0.0.0", + "blob:https://example.com:8443/worker-id", + "wss://example.com:8443/ws", + ], + [ + "?hostname=example.com&port=9000", + "blob:https://localhost/worker-id", + "wss://example.com:9000/ws", + ], ]; for (const [__resourceQuery, location, expected] of samples) {