createSocketUrl.js 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. 'use strict';
  2. /* global self */
  3. var url = require('url');
  4. var getCurrentScriptSource = require('./getCurrentScriptSource');
  5. function createSocketUrl(resourceQuery, currentLocation) {
  6. var urlParts;
  7. if (typeof resourceQuery === 'string' && resourceQuery !== '') {
  8. // If this bundle is inlined, use the resource query to get the correct url.
  9. // format is like `?http://0.0.0.0:8096&sockPort=8097&sockHost=localhost`
  10. urlParts = url.parse(resourceQuery // strip leading `?` from query string to get a valid URL
  11. .substr(1) // replace first `&` with `?` to have a valid query string
  12. .replace('&', '?'), true);
  13. } else {
  14. // Else, get the url from the <script> this file was called with.
  15. var scriptHost = getCurrentScriptSource();
  16. urlParts = url.parse(scriptHost || '/', true, true);
  17. } // Use parameter to allow passing location in unit tests
  18. if (typeof currentLocation === 'string' && currentLocation !== '') {
  19. currentLocation = url.parse(currentLocation);
  20. } else {
  21. currentLocation = self.location;
  22. }
  23. return getSocketUrl(urlParts, currentLocation);
  24. }
  25. /*
  26. * Gets socket URL based on Script Source/Location
  27. * (scriptSrc: URL, location: URL) -> URL
  28. */
  29. function getSocketUrl(urlParts, loc) {
  30. var auth = urlParts.auth,
  31. query = urlParts.query;
  32. var hostname = urlParts.hostname,
  33. protocol = urlParts.protocol,
  34. port = urlParts.port;
  35. if (!port || port === '0') {
  36. port = loc.port;
  37. } // check ipv4 and ipv6 `all hostname`
  38. // why do we need this check?
  39. // hostname n/a for file protocol (example, when using electron, ionic)
  40. // see: https://github.com/webpack/webpack-dev-server/pull/384
  41. if ((hostname === '0.0.0.0' || hostname === '::') && loc.hostname && loc.protocol.indexOf('http') === 0) {
  42. hostname = loc.hostname;
  43. } // `hostname` can be empty when the script path is relative. In that case, specifying
  44. // a protocol would result in an invalid URL.
  45. // When https is used in the app, secure websockets are always necessary
  46. // because the browser doesn't accept non-secure websockets.
  47. if (hostname && hostname !== '127.0.0.1' && (loc.protocol === 'https:' || urlParts.hostname === '0.0.0.0')) {
  48. protocol = loc.protocol;
  49. } // all of these sock url params are optionally passed in through
  50. // resourceQuery, so we need to fall back to the default if
  51. // they are not provided
  52. var sockHost = query.sockHost || hostname;
  53. var sockPath = query.sockPath || '/sockjs-node';
  54. var sockPort = query.sockPort || port;
  55. if (sockPort === 'location') {
  56. sockPort = loc.port;
  57. }
  58. return url.format({
  59. protocol: protocol,
  60. auth: auth,
  61. hostname: sockHost,
  62. port: sockPort,
  63. // If sockPath is provided it'll be passed in via the resourceQuery as a
  64. // query param so it has to be parsed out of the querystring in order for the
  65. // client to open the socket to the correct location.
  66. pathname: sockPath
  67. });
  68. }
  69. module.exports = createSocketUrl;