You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.js 3.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. 'use strict';
  2. /**
  3. * Module dependencies.
  4. */
  5. var co = require('co');
  6. var vm = require('vm');
  7. var parse = require('url').parse;
  8. var thunkify = require('thunkify');
  9. var degenerator = require('degenerator');
  10. /**
  11. * Built-in PAC functions.
  12. */
  13. var dateRange = require('./dateRange');
  14. var dnsDomainIs = require('./dnsDomainIs');
  15. var dnsDomainLevels = require('./dnsDomainLevels');
  16. var dnsResolve = require('./dnsResolve');
  17. var isInNet = require('./isInNet');
  18. var isPlainHostName = require('./isPlainHostName');
  19. var isResolvable = require('./isResolvable');
  20. var localHostOrDomainIs = require('./localHostOrDomainIs');
  21. var myIpAddress = require('./myIpAddress');
  22. var shExpMatch = require('./shExpMatch');
  23. var timeRange = require('./timeRange');
  24. var weekdayRange = require('./weekdayRange');
  25. /**
  26. * Module exports.
  27. */
  28. module.exports = generate;
  29. /**
  30. * Returns an asyncronous `FindProxyForURL` function from the
  31. * given JS string (from a PAC file).
  32. *
  33. * @param {String} str JS string
  34. * @param {Object} opts optional "options" object
  35. * @return {Function} async resolver function
  36. */
  37. function generate (_str, opts) {
  38. var i;
  39. var str = String(_str)
  40. // the sandbox to use for the vm
  41. var sandbox = {
  42. dateRange: dateRange,
  43. dnsDomainIs: dnsDomainIs,
  44. dnsDomainLevels: dnsDomainLevels,
  45. dnsResolve: dnsResolve,
  46. isInNet: isInNet,
  47. isPlainHostName: isPlainHostName,
  48. isResolvable: isResolvable,
  49. localHostOrDomainIs: localHostOrDomainIs,
  50. myIpAddress: myIpAddress,
  51. shExpMatch: shExpMatch,
  52. timeRange: timeRange,
  53. weekdayRange: weekdayRange
  54. };
  55. // copy the properties from the user-provided `sandbox` onto ours
  56. if (opts && opts.sandbox) {
  57. for (i in opts.sandbox) {
  58. sandbox[i] = opts.sandbox[i];
  59. }
  60. }
  61. // construct the array of async function names to add `yield` calls to.
  62. // user-provided async functions added to the `sandbox` must have an
  63. // `async = true` property set on the function instance
  64. var names = [];
  65. for (i in sandbox) {
  66. if (sandbox[i].async) {
  67. names.push(i);
  68. sandbox[i] = thunkify(sandbox[i]);
  69. }
  70. }
  71. //console.log(names);
  72. // convert the JS FindProxyForURL function into a generator function
  73. var js = degenerator(str, names);
  74. // filename of the pac file for the vm
  75. var filename = (opts && opts.filename) || 'proxy.pac';
  76. // evaluate the JS string and extract the FindProxyForURL generator function
  77. var fn = vm.runInNewContext(js + ';FindProxyForURL', sandbox, filename);
  78. if ('function' != typeof fn) {
  79. throw new TypeError('PAC file JavaScript contents must define a `FindProxyForURL` function');
  80. }
  81. // return the async resolver function
  82. var resolver = co.wrap(fn);
  83. return function FindProxyForURL (url, _host, _callback) {
  84. let host
  85. let callback
  86. switch (arguments.length) {
  87. case 3:
  88. host = _host
  89. callback = _callback
  90. break;
  91. case 2:
  92. if (typeof _host === 'function') {
  93. callback = _host
  94. } else {
  95. host = _host
  96. }
  97. break;
  98. }
  99. if (!host) {
  100. host = parse(url).hostname;
  101. }
  102. const promise = resolver(url, host, callback);
  103. if (typeof callback === 'function') {
  104. toCallback(promise, callback)
  105. } else {
  106. return promise
  107. }
  108. };
  109. }
  110. function toCallback (promise, callback) {
  111. let called = false
  112. function resolve(rtn) {
  113. if (called) return
  114. called = true
  115. callback(null, rtn)
  116. }
  117. function reject(err) {
  118. if (called) return
  119. called = true
  120. callback(err)
  121. }
  122. promise.then(resolve, reject)
  123. }