123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- 'use strict'
- const { connect } = require('node:net')
- const { kClose, kDestroy } = require('../core/symbols')
- const { InvalidArgumentError } = require('../core/errors')
- const util = require('../core/util')
- const Client = require('./client')
- const DispatcherBase = require('./dispatcher-base')
- class H2CClient extends DispatcherBase {
- #client = null
- constructor (origin, clientOpts) {
- super()
- if (typeof origin === 'string') {
- origin = new URL(origin)
- }
- if (origin.protocol !== 'http:') {
- throw new InvalidArgumentError(
- 'h2c-client: Only h2c protocol is supported'
- )
- }
- const { connect, maxConcurrentStreams, pipelining, ...opts } =
- clientOpts ?? {}
- let defaultMaxConcurrentStreams = 100
- let defaultPipelining = 100
- if (
- maxConcurrentStreams != null &&
- Number.isInteger(maxConcurrentStreams) &&
- maxConcurrentStreams > 0
- ) {
- defaultMaxConcurrentStreams = maxConcurrentStreams
- }
- if (pipelining != null && Number.isInteger(pipelining) && pipelining > 0) {
- defaultPipelining = pipelining
- }
- if (defaultPipelining > defaultMaxConcurrentStreams) {
- throw new InvalidArgumentError(
- 'h2c-client: pipelining cannot be greater than maxConcurrentStreams'
- )
- }
- this.#client = new Client(origin, {
- ...opts,
- connect: this.#buildConnector(connect),
- maxConcurrentStreams: defaultMaxConcurrentStreams,
- pipelining: defaultPipelining,
- allowH2: true
- })
- }
- #buildConnector (connectOpts) {
- return (opts, callback) => {
- const timeout = connectOpts?.connectOpts ?? 10e3
- const { hostname, port, pathname } = opts
- const socket = connect({
- ...opts,
- host: hostname,
- port,
- pathname
- })
- // Set TCP keep alive options on the socket here instead of in connect() for the case of assigning the socket
- if (opts.keepAlive == null || opts.keepAlive) {
- const keepAliveInitialDelay =
- opts.keepAliveInitialDelay == null ? 60e3 : opts.keepAliveInitialDelay
- socket.setKeepAlive(true, keepAliveInitialDelay)
- }
- socket.alpnProtocol = 'h2'
- const clearConnectTimeout = util.setupConnectTimeout(
- new WeakRef(socket),
- { timeout, hostname, port }
- )
- socket
- .setNoDelay(true)
- .once('connect', function () {
- queueMicrotask(clearConnectTimeout)
- if (callback) {
- const cb = callback
- callback = null
- cb(null, this)
- }
- })
- .on('error', function (err) {
- queueMicrotask(clearConnectTimeout)
- if (callback) {
- const cb = callback
- callback = null
- cb(err)
- }
- })
- return socket
- }
- }
- dispatch (opts, handler) {
- return this.#client.dispatch(opts, handler)
- }
- async [kClose] () {
- await this.#client.close()
- }
- async [kDestroy] () {
- await this.#client.destroy()
- }
- }
- module.exports = H2CClient
|