createSocketURL.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /**
  2. * @param {{ protocol?: string, auth?: string, hostname?: string, port?: string, pathname?: string, search?: string, hash?: string, slashes?: boolean }} objURL
  3. * @returns {string}
  4. */
  5. function format(objURL) {
  6. var protocol = objURL.protocol || "";
  7. if (protocol && protocol.substr(-1) !== ":") {
  8. protocol += ":";
  9. }
  10. var auth = objURL.auth || "";
  11. if (auth) {
  12. auth = encodeURIComponent(auth);
  13. auth = auth.replace(/%3A/i, ":");
  14. auth += "@";
  15. }
  16. var host = "";
  17. if (objURL.hostname) {
  18. host = auth + (objURL.hostname.indexOf(":") === -1 ? objURL.hostname : "[".concat(objURL.hostname, "]"));
  19. if (objURL.port) {
  20. host += ":".concat(objURL.port);
  21. }
  22. }
  23. var pathname = objURL.pathname || "";
  24. if (objURL.slashes) {
  25. host = "//".concat(host || "");
  26. if (pathname && pathname.charAt(0) !== "/") {
  27. pathname = "/".concat(pathname);
  28. }
  29. } else if (!host) {
  30. host = "";
  31. }
  32. var search = objURL.search || "";
  33. if (search && search.charAt(0) !== "?") {
  34. search = "?".concat(search);
  35. }
  36. var hash = objURL.hash || "";
  37. if (hash && hash.charAt(0) !== "#") {
  38. hash = "#".concat(hash);
  39. }
  40. pathname = pathname.replace(/[?#]/g,
  41. /**
  42. * @param {string} match
  43. * @returns {string}
  44. */
  45. function (match) {
  46. return encodeURIComponent(match);
  47. });
  48. search = search.replace("#", "%23");
  49. return "".concat(protocol).concat(host).concat(pathname).concat(search).concat(hash);
  50. }
  51. /**
  52. * @param {URL & { fromCurrentScript?: boolean }} parsedURL
  53. * @returns {string}
  54. */
  55. function createSocketURL(parsedURL) {
  56. var hostname = parsedURL.hostname; // Node.js module parses it as `::`
  57. // `new URL(urlString, [baseURLString])` parses it as '[::]'
  58. var isInAddrAny = hostname === "0.0.0.0" || hostname === "::" || hostname === "[::]"; // why do we need this check?
  59. // hostname n/a for file protocol (example, when using electron, ionic)
  60. // see: https://github.com/webpack/webpack-dev-server/pull/384
  61. if (isInAddrAny && self.location.hostname && self.location.protocol.indexOf("http") === 0) {
  62. hostname = self.location.hostname;
  63. }
  64. var socketURLProtocol = parsedURL.protocol || self.location.protocol; // When https is used in the app, secure web sockets are always necessary because the browser doesn't accept non-secure web sockets.
  65. if (socketURLProtocol === "auto:" || hostname && isInAddrAny && self.location.protocol === "https:") {
  66. socketURLProtocol = self.location.protocol;
  67. }
  68. socketURLProtocol = socketURLProtocol.replace(/^(?:http|.+-extension|file)/i, "ws");
  69. var socketURLAuth = ""; // `new URL(urlString, [baseURLstring])` doesn't have `auth` property
  70. // Parse authentication credentials in case we need them
  71. if (parsedURL.username) {
  72. socketURLAuth = parsedURL.username; // Since HTTP basic authentication does not allow empty username,
  73. // we only include password if the username is not empty.
  74. if (parsedURL.password) {
  75. // Result: <username>:<password>
  76. socketURLAuth = socketURLAuth.concat(":", parsedURL.password);
  77. }
  78. } // In case the host is a raw IPv6 address, it can be enclosed in
  79. // the brackets as the brackets are needed in the final URL string.
  80. // Need to remove those as url.format blindly adds its own set of brackets
  81. // if the host string contains colons. That would lead to non-working
  82. // double brackets (e.g. [[::]]) host
  83. //
  84. // All of these web socket url params are optionally passed in through resourceQuery,
  85. // so we need to fall back to the default if they are not provided
  86. var socketURLHostname = (hostname || self.location.hostname || "localhost").replace(/^\[(.*)\]$/, "$1");
  87. var socketURLPort = parsedURL.port;
  88. if (!socketURLPort || socketURLPort === "0") {
  89. socketURLPort = self.location.port;
  90. } // If path is provided it'll be passed in via the resourceQuery as a
  91. // query param so it has to be parsed out of the querystring in order for the
  92. // client to open the socket to the correct location.
  93. var socketURLPathname = "/ws";
  94. if (parsedURL.pathname && !parsedURL.fromCurrentScript) {
  95. socketURLPathname = parsedURL.pathname;
  96. }
  97. return format({
  98. protocol: socketURLProtocol,
  99. auth: socketURLAuth,
  100. hostname: socketURLHostname,
  101. port: socketURLPort,
  102. pathname: socketURLPathname,
  103. slashes: true
  104. });
  105. }
  106. export default createSocketURL;