index.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.CloudflareSocket = void 0;
  4. const events_1 = require("events");
  5. /**
  6. * Wrapper around the Cloudflare built-in socket that can be used by the `Connection`.
  7. */
  8. class CloudflareSocket extends events_1.EventEmitter {
  9. constructor(ssl) {
  10. super();
  11. this.ssl = ssl;
  12. this.writable = false;
  13. this.destroyed = false;
  14. this._upgrading = false;
  15. this._upgraded = false;
  16. this._cfSocket = null;
  17. this._cfWriter = null;
  18. this._cfReader = null;
  19. }
  20. setNoDelay() {
  21. return this;
  22. }
  23. setKeepAlive() {
  24. return this;
  25. }
  26. ref() {
  27. return this;
  28. }
  29. unref() {
  30. return this;
  31. }
  32. async connect(port, host, connectListener) {
  33. try {
  34. log('connecting');
  35. if (connectListener)
  36. this.once('connect', connectListener);
  37. const options = this.ssl ? { secureTransport: 'starttls' } : {};
  38. const mod = await import('cloudflare:sockets');
  39. const connect = mod.connect;
  40. this._cfSocket = connect(`${host}:${port}`, options);
  41. this._cfWriter = this._cfSocket.writable.getWriter();
  42. this._addClosedHandler();
  43. this._cfReader = this._cfSocket.readable.getReader();
  44. if (this.ssl) {
  45. this._listenOnce().catch((e) => this.emit('error', e));
  46. }
  47. else {
  48. this._listen().catch((e) => this.emit('error', e));
  49. }
  50. await this._cfWriter.ready;
  51. log('socket ready');
  52. this.writable = true;
  53. this.emit('connect');
  54. return this;
  55. }
  56. catch (e) {
  57. this.emit('error', e);
  58. }
  59. }
  60. async _listen() {
  61. // eslint-disable-next-line no-constant-condition
  62. while (true) {
  63. log('awaiting receive from CF socket');
  64. const { done, value } = await this._cfReader.read();
  65. log('CF socket received:', done, value);
  66. if (done) {
  67. log('done');
  68. break;
  69. }
  70. this.emit('data', Buffer.from(value));
  71. }
  72. }
  73. async _listenOnce() {
  74. log('awaiting first receive from CF socket');
  75. const { done, value } = await this._cfReader.read();
  76. log('First CF socket received:', done, value);
  77. this.emit('data', Buffer.from(value));
  78. }
  79. write(data, encoding = 'utf8', callback = () => { }) {
  80. if (data.length === 0)
  81. return callback();
  82. if (typeof data === 'string')
  83. data = Buffer.from(data, encoding);
  84. log('sending data direct:', data);
  85. this._cfWriter.write(data).then(() => {
  86. log('data sent');
  87. callback();
  88. }, (err) => {
  89. log('send error', err);
  90. callback(err);
  91. });
  92. return true;
  93. }
  94. end(data = Buffer.alloc(0), encoding = 'utf8', callback = () => { }) {
  95. log('ending CF socket');
  96. this.write(data, encoding, (err) => {
  97. this._cfSocket.close();
  98. if (callback)
  99. callback(err);
  100. });
  101. return this;
  102. }
  103. destroy(reason) {
  104. log('destroying CF socket', reason);
  105. this.destroyed = true;
  106. return this.end();
  107. }
  108. startTls(options) {
  109. if (this._upgraded) {
  110. // Don't try to upgrade again.
  111. this.emit('error', 'Cannot call `startTls()` more than once on a socket');
  112. return;
  113. }
  114. this._cfWriter.releaseLock();
  115. this._cfReader.releaseLock();
  116. this._upgrading = true;
  117. this._cfSocket = this._cfSocket.startTls(options);
  118. this._cfWriter = this._cfSocket.writable.getWriter();
  119. this._cfReader = this._cfSocket.readable.getReader();
  120. this._addClosedHandler();
  121. this._listen().catch((e) => this.emit('error', e));
  122. }
  123. _addClosedHandler() {
  124. this._cfSocket.closed.then(() => {
  125. if (!this._upgrading) {
  126. log('CF socket closed');
  127. this._cfSocket = null;
  128. this.emit('close');
  129. }
  130. else {
  131. this._upgrading = false;
  132. this._upgraded = true;
  133. }
  134. }).catch((e) => this.emit('error', e));
  135. }
  136. }
  137. exports.CloudflareSocket = CloudflareSocket;
  138. const debug = false;
  139. function dump(data) {
  140. if (data instanceof Uint8Array || data instanceof ArrayBuffer) {
  141. const hex = Buffer.from(data).toString('hex');
  142. const str = new TextDecoder().decode(data);
  143. return `\n>>> STR: "${str.replace(/\n/g, '\\n')}"\n>>> HEX: ${hex}\n`;
  144. }
  145. else {
  146. return data;
  147. }
  148. }
  149. function log(...args) {
  150. debug && console.log(...args.map(dump));
  151. }
  152. //# sourceMappingURL=index.js.map