server_selector.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. /*
  2. HTML5 Speedtest v4.7.2 MPOT - Server selector
  3. by Federico Dossena
  4. https://github.com/adolfintel/speedtest/
  5. GNU LGPLv3 License
  6. */
  7. //pings the specified URL, then calls the function result. Result will receive a parameter which is either the time it took to ping the URL, or -1 if something went wrong.
  8. var PING_TIMEOUT = 1000;
  9. var USE_PING_TIMEOUT = true; //will be disabled on unsupported browsers
  10. if (/MSIE.(\d+\.\d+)/i.test(navigator.userAgent)) {
  11. //IE11 doesn't support XHR timeout
  12. USE_PING_TIMEOUT = false;
  13. }
  14. function ping(url, result) {
  15. var xhr = new XMLHttpRequest();
  16. var t = new Date().getTime();
  17. xhr.onload = function() {
  18. if (xhr.responseText.length == 0) {
  19. //we expect an empty response
  20. var instspd = new Date().getTime() - t; //rough timing estimate
  21. try {
  22. //try to get more accurate timing using performance API
  23. var p = performance.getEntries();
  24. p = p[p.length - 1];
  25. var d = p.responseStart - p.requestStart;
  26. if (d <= 0) d = p.duration;
  27. if (d > 0 && d < instspd) instspd = d;
  28. } catch (e) {}
  29. result(instspd);
  30. } else result(-1);
  31. }.bind(this);
  32. xhr.onerror = function() {
  33. result(-1);
  34. }.bind(this);
  35. xhr.open("GET", url);
  36. if (USE_PING_TIMEOUT) {
  37. try {
  38. xhr.timeout = PING_TIMEOUT;
  39. xhr.ontimeout = xhr.onerror;
  40. } catch (e) {}
  41. }
  42. xhr.send();
  43. }
  44. //this function repeatedly pings a server to get a good estimate of the ping. When it's done, it calls the done function without parameters. At the end of the execution, the server will have a new parameter called pingT, which is either the best ping we got from the server or -1 if something went wrong.
  45. var PINGS = 3, //up to 3 pings are performed, unless the server is down...
  46. SLOW_THRESHOLD = 500; //...or one of the pings is above this threshold
  47. function checkServer(server, done) {
  48. var i = 0;
  49. server.pingT = -1;
  50. if (server.server.indexOf(location.protocol) == -1) done();
  51. else {
  52. var nextPing = function() {
  53. if (i++ == PINGS) {
  54. done();
  55. return;
  56. }
  57. ping(
  58. server.server + server.pingURL,
  59. function(t) {
  60. if (t >= 0) {
  61. if (t < server.pingT || server.pingT == -1) server.pingT = t;
  62. if (t < SLOW_THRESHOLD) nextPing();
  63. else done();
  64. } else done();
  65. }.bind(this)
  66. );
  67. }.bind(this);
  68. nextPing();
  69. }
  70. }
  71. /*this function goes through a list of servers, each with this format:
  72. {
  73. name: "User friendly name",
  74. server:"http://yourBackend.com/", <---- make sure there's a / at the end!
  75. dlURL:"garbage.php" <----- path to garbage.php or its replacement on the server
  76. ulURL:"empty.php" <----- path to empty.php or its replacement on the server
  77. pingURL:"empty.php" <----- path to empty.php or its replacement on the server. This is used to ping the server by this selector
  78. getIpURL:"getIP.php" <----- path to getIP.php or its replacement on the server
  79. }
  80. For each server, the ping is measured, then the server with the function result is called with the best server, or null if all the servers were down.
  81. */
  82. function selectServer(serverList, result) {
  83. var i = 0;
  84. var done = function() {
  85. var bestServer = null;
  86. for (var i = 0; i < serverList.length; i++) {
  87. if (serverList[i].pingT != -1 && (bestServer == null || serverList[i].pingT < bestServer.pingT)) bestServer = serverList[i];
  88. }
  89. result(bestServer);
  90. }.bind(this);
  91. var nextServer = function() {
  92. if (i == serverList.length) {
  93. done();
  94. return;
  95. }
  96. checkServer(serverList[i++], nextServer);
  97. }.bind(this);
  98. nextServer();
  99. }
  100. /*
  101. this function is a faster version of selectServer that tests multiple servers concurrently. Useful for large server lists
  102. */
  103. var CONCURRENCY = 4; //4 seems to be the safest value
  104. function fastSelectServer(serverList, result) {
  105. var serverLists = [];
  106. for (var i = 0; i < CONCURRENCY; i++) {
  107. serverLists[i] = [];
  108. }
  109. for (var i = 0; i < serverList.length; i++) {
  110. serverLists[i % CONCURRENCY].push(serverList[i]);
  111. }
  112. var completed = 0;
  113. var bestServer = null;
  114. for (var i = 0; i < CONCURRENCY; i++) {
  115. selectServer(
  116. serverLists[i],
  117. function(server) {
  118. if (server != null) {
  119. if (bestServer == null || server.pingT < bestServer.pingT) bestServer = server;
  120. }
  121. completed++;
  122. if (completed == CONCURRENCY) result(bestServer);
  123. }.bind(this)
  124. );
  125. }
  126. }