Skip to content
This repository has been archived by the owner on Feb 12, 2024. It is now read-only.

feat: allow passing a http.Agent to the grpc client #3477

Merged
merged 3 commits into from
Jan 14, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/ipfs-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ An optional object which may have the following keys:
| ---- | ---- | ------- | ----------- |
| grpc | `Multiaddr` or `string` or `URL` | `undefined` | The address of a [ipfs-grpc-server][] to connect to |
| http | `Multiaddr` or `string` or `URL` | `undefined` | The address of a [ipfs-http-server][] to connect to |
| agent | [http.Agent](https://nodejs.org/api/http.html#http_class_http_agent) | `undefined` | A http.Agent used to control HTTP client behaviour (node.js only) |

### Returns

Expand Down
1 change: 1 addition & 0 deletions packages/ipfs-grpc-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ An optional object which may have the following keys:
| Name | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| url | `Multiaddr` or `string` or `URL` | `undefined` | The address of a [ipfs-grpc-server][] to connect to |
| agent | [http.Agent](https://nodejs.org/api/http.html#http_class_http_agent) | `undefined` | A http.Agent used to control HTTP client behaviour (node.js only) |

### Returns

Expand Down
3 changes: 2 additions & 1 deletion packages/ipfs-grpc-client/src/core-api/add-all.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ module.exports = function grpcAddAll (grpc, service, opts = {}) {
} = bidiToDuplex(grpc, service, {
host: opts.url,
debug: Boolean(process.env.DEBUG),
metadata: options
metadata: options,
agent: opts.agent
})

sendFiles(stream, sink)
Expand Down
3 changes: 2 additions & 1 deletion packages/ipfs-grpc-client/src/core-api/files/ls.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ module.exports = function grpcMfsLs (grpc, service, opts = {}) {
for await (const result of serverStreamToIterator(grpc, service, request, {
host: opts.url,
debug: Boolean(process.env.DEBUG),
metadata: options
metadata: options,
agent: opts.agent
})) {
yield {
name: result.name,
Expand Down
3 changes: 2 additions & 1 deletion packages/ipfs-grpc-client/src/core-api/files/write.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ module.exports = function grpcMfsWrite (grpc, service, opts = {}) {
await clientStreamToPromise(grpc, service, stream(path, content), {
host: opts.url,
debug: Boolean(process.env.DEBUG),
metadata: options
metadata: options,
agent: opts.agent
})
}

Expand Down
3 changes: 2 additions & 1 deletion packages/ipfs-grpc-client/src/core-api/id.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ module.exports = function grpcId (grpc, service, opts = {}) {

const res = await unaryToPromise(grpc, service, request, {
host: opts.url,
metadata: toHeaders(options)
metadata: toHeaders(options),
agent: opts.agent
})

return {
Expand Down
31 changes: 26 additions & 5 deletions packages/ipfs-grpc-client/src/grpc/transport.node.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,32 @@ const WebsocketSignal = {

const finishSendFrame = new Uint8Array([1])

function WebsocketTransport () {
return (opts) => {
return websocketRequest(opts)
/**
* @param {object} options
* @param {import('http').Agent} [options.agent] - http.Agent used to control HTTP client behaviour
*/
function WebsocketTransport (options) {
/**
* @param {import('@improbable-eng/grpc-web').grpc.TransportOptions} opts
*/
const websocketTransportFactory = (opts) => {
return websocketRequest({
...options,
...opts
})
}

return websocketTransportFactory
}

/**
* @typedef {object} NodeTransportOptions
* @property {import('http').Agent} [options.agent]
*
* @typedef {NodeTransportOptions & import('@improbable-eng/grpc-web').grpc.TransportOptions} WebSocketTransportOptions
*
* @param {WebSocketTransportOptions} options
*/
function websocketRequest (options) {
const webSocketAddress = constructWebSocketAddress(options.url)

Expand Down Expand Up @@ -54,7 +74,7 @@ function websocketRequest (options) {
}
},
start: (metadata) => {
ws = new WebSocket(webSocketAddress, ['grpc-websockets'])
ws = new WebSocket(webSocketAddress, ['grpc-websockets'], options)
ws.binaryType = 'arraybuffer'
ws.onopen = function () {
options.debug && debug('websocketRequest.onopen')
Expand Down Expand Up @@ -93,7 +113,8 @@ function constructWebSocketAddress (url) {
} else if (url.substr(0, 7) === 'http://') {
return `ws://${url.substr(7)}`
}
throw new Error('Websocket transport constructed with non-https:// or http:// host.')

throw new Error('Websocket transport url must start with ws:// or wss:// or http:// or https://')
}

function headersToBytes (headers) {
Expand Down
9 changes: 6 additions & 3 deletions packages/ipfs-grpc-client/src/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
'use strict'

const transport = require('./grpc/transport')
const toUrlString = require('ipfs-core-utils/src/to-url-string')
const loadServices = require('./utils/load-services')
const { grpc } = require('@improbable-eng/grpc-web')
grpc.setDefaultTransport(transport())

const service = loadServices()

Expand All @@ -21,7 +19,12 @@ function normaliseUrls (opts) {
})
}

module.exports = function createClient (opts = {}) {
/**
* @param {object} opts
* @param {string} opts.url - The URL to connect to as a URL or Multiaddr
* @param {import('http').Agent} [opts.agent] - http.Agent used to control HTTP client behaviour (node.js only)
*/
module.exports = function createClient (opts = { url: '' }) {
opts.url = toUrlString(opts.url)

// @improbable-eng/grpc-web requires http:// protocol URLs, not ws://
Expand Down
9 changes: 8 additions & 1 deletion packages/ipfs-grpc-client/src/utils/bidi-to-duplex.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const pushable = require('it-pushable')
const errCode = require('err-code')
const toHeaders = require('./to-headers')
const transport = require('../grpc/transport')

async function sendMessages (service, client, source) {
for await (const obj of source) {
Expand All @@ -23,6 +24,7 @@ async function sendMessages (service, client, source) {
* @param {string} options.host - The remote host
* @param {boolean} [options.debug] - Whether to print debug messages
* @param {object} [options.metadata] - Metadata sent as headers
* @param {import('http').Agent} [options.agent] - http.Agent used to control HTTP client behaviour (node.js only)
* @returns {{ source: AsyncIterable<object>, sink: { push: Function, end: Function } }}
**/
module.exports = function bidiToDuplex (grpc, service, options) {
Expand All @@ -32,7 +34,12 @@ module.exports = function bidiToDuplex (grpc, service, options) {
// @ts-ignore
const sink = pushable()

const client = grpc.client(service, options)
const client = grpc.client(service, {
...options,
transport: transport({
agent: options.agent
})
})
client.onMessage(message => {
sink.push(message)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const bidiToDuplex = require('./bidi-to-duplex')
* @param {string} options.host - The remote host
* @param {boolean} [options.debug] - Whether to print debug messages
* @param {object} [options.metadata] - Metadata sent as headers
* @param {import('http').Agent} [options.agent] - http.Agent used to control HTTP client behaviour (node.js only)
* @returns {Promise<Object>} - A promise that resolves to a response object
**/
module.exports = async function clientStreamToPromise (grpc, service, source, options) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const bidiToDuplex = require('./bidi-to-duplex')
* @param {string} options.host - The remote host
* @param {boolean} [options.debug] - Whether to print debug messages
* @param {object} [options.metadata] - Metadata sent as headers
* @param {import('http').Agent} [options.agent] - http.Agent used to control HTTP client behaviour (node.js only)
* @returns {AsyncIterable<object>}
**/
module.exports = function serverStreamToIterator (grpc, service, request, options) {
Expand Down
1 change: 1 addition & 0 deletions packages/ipfs-grpc-client/src/utils/unary-to-promise.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const bidiToDuplex = require('./bidi-to-duplex')
* @param {string} options.host - The remote host
* @param {boolean} [options.debug] - Whether to print debug messages
* @param {object} [options.metadata] - Metadata sent as headers
* @param {import('http').Agent} [options.agent] - http.Agent used to control HTTP client behaviour (node.js only)
* @returns {Promise<Object>} - A promise that resolves to a response object
**/
module.exports = function unaryToPromise (grpc, service, request, options) {
Expand Down
49 changes: 49 additions & 0 deletions packages/ipfs-grpc-client/test/agent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* eslint-env mocha */
'use strict'

const grpcClient = require('../src')
const WebSocket = require('ws')

function startServer () {
return new Promise((resolve) => {
const wss = new WebSocket.Server({ port: 0 })

wss.on('listening', () => {
resolve({
port: wss.address().port,
close: () => wss.close()
})
})

wss.on('connection', (ws) => {
ws.once('message', () => {
ws.send('')
ws.end()
})
})
})
}

describe('agent', function () {
it('uses the passed agent', async () => {
const server = await startServer()

try {
await new Promise((resolve) => {
const ipfs = grpcClient({
url: `http://localhost:${server.port}`,
agent: {
addRequest () {
// an agent method was invoked
resolve()
}
}
})

ipfs.id().catch(() => {})
})
} finally {
server.close()
}
})
})
3 changes: 3 additions & 0 deletions packages/ipfs-grpc-client/test/node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
'use strict'

require('./agent')
5 changes: 5 additions & 0 deletions packages/ipfs-http-client/test/node/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ describe('agent', function () {

break
}

if (i === 4) {
// should have first two responses by now
expect(responses).to.have.lengthOf(2)
}
}

// wait for the final request to arrive
Expand Down