Simple email application for Android. Original source code: https://framagit.org/dystopia-project/simple-email
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.

153 lines
4.0 KiB

  1. 'use strict';
  2. const net = require('net');
  3. class TimeoutError extends Error {
  4. constructor(threshold, event) {
  5. super(`Timeout awaiting '${event}' for ${threshold}ms`);
  6. this.name = 'TimeoutError';
  7. this.code = 'ETIMEDOUT';
  8. this.event = event;
  9. }
  10. }
  11. const reentry = Symbol('reentry');
  12. const noop = () => {};
  13. module.exports = (request, delays, options) => {
  14. /* istanbul ignore next: this makes sure timed-out isn't called twice */
  15. if (request[reentry]) {
  16. return;
  17. }
  18. request[reentry] = true;
  19. let stopNewTimeouts = false;
  20. const addTimeout = (delay, callback, ...args) => {
  21. // An error had been thrown before. Going further would result in uncaught errors.
  22. // See https://github.com/sindresorhus/got/issues/631#issuecomment-435675051
  23. if (stopNewTimeouts) {
  24. return noop;
  25. }
  26. // Event loop order is timers, poll, immediates.
  27. // The timed event may emit during the current tick poll phase, so
  28. // defer calling the handler until the poll phase completes.
  29. let immediate;
  30. const timeout = setTimeout(() => {
  31. immediate = setImmediate(callback, delay, ...args);
  32. /* istanbul ignore next: added in node v9.7.0 */
  33. if (immediate.unref) {
  34. immediate.unref();
  35. }
  36. }, delay);
  37. /* istanbul ignore next: in order to support electron renderer */
  38. if (timeout.unref) {
  39. timeout.unref();
  40. }
  41. const cancel = () => {
  42. clearTimeout(timeout);
  43. clearImmediate(immediate);
  44. };
  45. cancelers.push(cancel);
  46. return cancel;
  47. };
  48. const {host, hostname} = options;
  49. const timeoutHandler = (delay, event) => {
  50. request.emit('error', new TimeoutError(delay, event));
  51. request.once('error', () => {}); // Ignore the `socket hung up` error made by request.abort()
  52. request.abort();
  53. };
  54. const cancelers = [];
  55. const cancelTimeouts = () => {
  56. stopNewTimeouts = true;
  57. cancelers.forEach(cancelTimeout => cancelTimeout());
  58. };
  59. request.once('error', cancelTimeouts);
  60. request.once('response', response => {
  61. response.once('end', cancelTimeouts);
  62. });
  63. if (delays.request !== undefined) {
  64. addTimeout(delays.request, timeoutHandler, 'request');
  65. }
  66. if (delays.socket !== undefined) {
  67. request.setTimeout(delays.socket, () => {
  68. timeoutHandler(delays.socket, 'socket');
  69. });
  70. }
  71. if (delays.lookup !== undefined && !request.socketPath && !net.isIP(hostname || host)) {
  72. request.once('socket', socket => {
  73. /* istanbul ignore next: hard to test */
  74. if (socket.connecting) {
  75. const cancelTimeout = addTimeout(delays.lookup, timeoutHandler, 'lookup');
  76. socket.once('lookup', cancelTimeout);
  77. }
  78. });
  79. }
  80. if (delays.connect !== undefined) {
  81. request.once('socket', socket => {
  82. /* istanbul ignore next: hard to test */
  83. if (socket.connecting) {
  84. const timeConnect = () => addTimeout(delays.connect, timeoutHandler, 'connect');
  85. if (request.socketPath || net.isIP(hostname || host)) {
  86. socket.once('connect', timeConnect());
  87. } else {
  88. socket.once('lookup', error => {
  89. if (error === null) {
  90. socket.once('connect', timeConnect());
  91. }
  92. });
  93. }
  94. }
  95. });
  96. }
  97. if (delays.secureConnect !== undefined && options.protocol === 'https:') {
  98. request.once('socket', socket => {
  99. /* istanbul ignore next: hard to test */
  100. if (socket.connecting) {
  101. socket.once('connect', () => {
  102. const cancelTimeout = addTimeout(delays.secureConnect, timeoutHandler, 'secureConnect');
  103. socket.once('secureConnect', cancelTimeout);
  104. });
  105. }
  106. });
  107. }
  108. if (delays.send !== undefined) {
  109. request.once('socket', socket => {
  110. const timeRequest = () => addTimeout(delays.send, timeoutHandler, 'send');
  111. /* istanbul ignore next: hard to test */
  112. if (socket.connecting) {
  113. socket.once('connect', () => {
  114. request.once('upload-complete', timeRequest());
  115. });
  116. } else {
  117. request.once('upload-complete', timeRequest());
  118. }
  119. });
  120. }
  121. if (delays.response !== undefined) {
  122. request.once('upload-complete', () => {
  123. const cancelTimeout = addTimeout(delays.response, timeoutHandler, 'response');
  124. request.once('response', cancelTimeout);
  125. });
  126. }
  127. };
  128. module.exports.TimeoutError = TimeoutError;