Skip to content

Commit

Permalink
feat: add multiple inbound transports (#433)
Browse files Browse the repository at this point in the history
* feat: add multiple inbound transports
* chore: update mediator sample

Signed-off-by: Timo Glastra <timo@animo.id>
  • Loading branch information
TimoGlastra committed Aug 21, 2021
1 parent 895f7d0 commit 56cb9f2
Show file tree
Hide file tree
Showing 39 changed files with 272 additions and 295 deletions.
22 changes: 5 additions & 17 deletions docker/docker-compose-mediators-ngrok.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,15 @@ version: '3'
# This file extends docker-compose-mediators.yml

services:
http-mediator:
mediator:
environment:
NGROK_NAME: http-mediator-ngrok
NGROK_NAME: mediator-ngrok
entrypoint: ./scripts/ngrok-wait.sh
depends_on: [http-mediator-ngrok]
depends_on: [mediator-ngrok]

http-mediator-ngrok:
mediator-ngrok:
image: wernight/ngrok
command: ngrok http -bind-tls=true --log stdout http-mediator:3001
networks:
- hyperledger

ws-mediator:
environment:
NGROK_NAME: ws-mediator-ngrok
entrypoint: ./scripts/ngrok-wait.sh
depends_on: [ws-mediator-ngrok]

ws-mediator-ngrok:
image: wernight/ngrok
command: ngrok http -bind-tls=true --log stdout ws-mediator:3002
command: ngrok http -bind-tls=true --log stdout mediator:3001
networks:
- hyperledger

Expand Down
15 changes: 2 additions & 13 deletions docker/docker-compose-mediators.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,16 @@
version: '3'

services:
http-mediator:
mediator:
build: ..
image: aries-framework-javascript
container_name: afj-http-mediator
container_name: afj-mediator
command: yarn run-mediator
platform: linux/amd64
networks:
- hyperledger
ports:
- 3001:3001

ws-mediator:
build: ..
image: aries-framework-javascript
container_name: afj-ws-mediator
command: yarn run-mediator-ws
platform: linux/amd64
networks:
- hyperledger
ports:
- 3002:3002

networks:
hyperledger:
14 changes: 7 additions & 7 deletions docs/getting-started/1-transports.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Transports

An agent needs an inbound and outbound transporter. At this current time, the outbound transporter is already built-in and can be used. The inbound transporter is a tad bit more complicated and has to be added manually.
An agent needs an inbound and outbound transport. At this current time, the outbound transport is already built-in and can be used. The inbound transport is a tad bit more complicated and has to be added manually.

- [Aries RFC 0025: DIComm Transports](https://github.com/hyperledger/aries-rfcs/blob/master/features/0025-didcomm-transports/README.md)
- [Aries RFC 0005: DID Communication](https://github.com/hyperledger/aries-rfcs/blob/master/concepts/0005-didcomm/README.md)
Expand All @@ -10,19 +10,19 @@ An agent needs an inbound and outbound transporter. At this current time, the ou
Outbound transports allow you to send messages to other agents. Currently, only a single outbound transport can be used. See [Issue 268: Add support for multiple transports](https://github.com/hyperledger/aries-framework-javascript/issues/268) for progress on supporting multiple outbound transports.

```ts
import { HttpOutboundTransporter, WsOutboundTransporter Agent } from '@aries-framework/core'
import { HttpOutboundTransport, WsOutboundTransport, Agent } from '@aries-framework/core'

const agent = new Agent({
/* config */
})

// Use HTTP as outbound transporter
const httpOutboundTransporter = new HttpOutboundTransporter()
agent.registerOutboundTransporter(httpOutboundTransporter)
// Use HTTP as outbound transport
const httpOutboundTransport = new HttpOutboundTransport()
agent.registerOutboundTransport(httpOutboundTransport)

// Or use WebSocket instead
const wsOutboundTransporter = new WsOutboundTransporter()
agent.registerOutboundTransporter(wsOutboundTransporter)
const wsOutboundTransport = new WsOutboundTransport()
agent.registerOutboundTransport(wsOutboundTransport)
```

## Inbound Transport
Expand Down
8 changes: 4 additions & 4 deletions docs/getting-started/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ In most applications, the client communicates with the server using http protoco

On the other hand, agents communicate using [DIDComm](https://github.com/hyperledger/aries-rfcs/tree/master/concepts/0005-didcomm) communication protocols. While protocols have much more to talk about the most important concern here is how the communication flow goes. For the sake of demonstration, lets assume 2 agents want to communicate, Agent Alice and Agent Bob.

1. Agent Alice will send a connection request to Agent Bob either directly or through a mediator (another routing agent) using outbound transporter
2. Agent Bob receives the message (through inbound transporter) and process the message
1. Agent Alice will send a connection request to Agent Bob either directly or through a mediator (another routing agent) using outbound transport
2. Agent Bob receives the message (through inbound transport) and process the message
3. Agent Bob sends the response in a new request (using outbound TP) sent back to agent Alice
4. Agent Alice receives the message through the inbound TP
5. Agent Alice process the message (under the hood through Aries) and raises an event with attached data relevant to communication context
Expand All @@ -37,8 +37,8 @@ A callback method passed to the agent event handler to be called on different ev
- A recent connection with other agent has changed state
- A credential received or has a state changed

## [Transporters](transports.md)
## [Transports](transports.md)

Services that will handle the outbound and inbound transports. Remember we mentioned that unlike http request which happens on one channel, the connection here has two ways one outgoing using the outbound transporter and one incoming using the inbound transporter
Services that will handle the outbound and inbound transports. Remember we mentioned that unlike http request which happens on one channel, the connection here has two ways one outgoing using the outbound transport and one incoming using the inbound transport

Those are the main components that you as a developer will need to care about.
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@
"lint": "eslint --ignore-path .gitignore .",
"validate": "yarn lint && yarn check-types && yarn check-format",
"prepare": "husky install",
"run-mediator": "ts-node ./samples/mediator.ts",
"run-mediator-ws": "ts-node ./samples/mediator-ws.ts"
"run-mediator": "ts-node ./samples/mediator.ts"
},
"devDependencies": {
"@types/cors": "^2.8.10",
Expand Down
33 changes: 19 additions & 14 deletions packages/core/src/agent/Agent.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Logger } from '../logger'
import type { InboundTransporter } from '../transport/InboundTransporter'
import type { OutboundTransporter } from '../transport/OutboundTransporter'
import type { InboundTransport } from '../transport/InboundTransport'
import type { OutboundTransport } from '../transport/OutboundTransport'
import type { InitConfig } from '../types'
import type { Wallet } from '../wallet/Wallet'
import type { AgentDependencies } from './AgentDependencies'
Expand Down Expand Up @@ -43,7 +43,6 @@ export class Agent {
protected messageReceiver: MessageReceiver
protected transportService: TransportService
protected messageSender: MessageSender
public inboundTransporter?: InboundTransporter
private _isInitialized = false
public messageSubscription: Subscription

Expand Down Expand Up @@ -114,16 +113,20 @@ export class Agent {
.subscribe()
}

public setInboundTransporter(inboundTransporter: InboundTransporter) {
this.inboundTransporter = inboundTransporter
public registerInboundTransport(inboundTransport: InboundTransport) {
this.messageReceiver.registerInboundTransport(inboundTransport)
}

public registerOutboundTransporter(outboundTransporter: OutboundTransporter) {
this.messageSender.registerOutboundTransporter(outboundTransporter)
public get inboundTransports() {
return this.messageReceiver.inboundTransports
}

public get outboundTransporters() {
return this.messageSender.outboundTransporters
public registerOutboundTransport(outboundTransport: OutboundTransport) {
this.messageSender.registerOutboundTransport(outboundTransport)
}

public get outboundTransports() {
return this.messageSender.outboundTransports
}

public get events() {
Expand Down Expand Up @@ -158,11 +161,11 @@ export class Agent {
await this.wallet.initPublicDid({ seed: publicDidSeed })
}

if (this.inboundTransporter) {
await this.inboundTransporter.start(this)
for (const transport of this.inboundTransports) {
transport.start(this)
}

for (const transport of this.messageSender.outboundTransporters) {
for (const transport of this.outboundTransports) {
transport.start(this)
}

Expand All @@ -184,10 +187,12 @@ export class Agent {
this.agentConfig.stop$.next(true)

// Stop transports
for (const transport of this.messageSender.outboundTransporters) {
for (const transport of this.outboundTransports) {
transport.stop()
}
for (const transport of this.inboundTransports) {
transport.stop()
}
await this.inboundTransporter?.stop()

// close/delete wallet if still initialized
if (this.wallet.isInitialized) {
Expand Down
13 changes: 6 additions & 7 deletions packages/core/src/agent/AgentConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,14 @@ export class AgentConfig {
return this.initConfig.mediatorPickupStrategy ?? MediatorPickupStrategy.Explicit
}

public getEndpoint() {
// If we have an endpoint set, use it
if (this.initConfig.endpoint) {
return this.initConfig.endpoint
public get endpoints(): [string, ...string[]] {
// if endpoints is not set, return queue endpoint
// https://github.com/hyperledger/aries-rfcs/issues/405#issuecomment-582612875
if (!this.initConfig.endpoints || this.initConfig.endpoints.length === 0) {
return [DID_COMM_TRANSPORT_QUEUE]
}

// Otherwise, return didcomm:transport/queue
// https://github.com/hyperledger/aries-rfcs/issues/405#issuecomment-582612875
return DID_COMM_TRANSPORT_QUEUE
return this.initConfig.endpoints as [string, ...string[]]
}

public get mediatorConnectionsInvite() {
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/agent/MessageReceiver.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Logger } from '../logger'
import type { InboundTransport } from '../transport'
import type { UnpackedMessageContext, UnpackedMessage, WireMessage } from '../types'
import type { AgentMessage } from './AgentMessage'
import type { TransportSession } from './TransportService'
Expand All @@ -24,6 +25,7 @@ export class MessageReceiver {
private connectionService: ConnectionService
private dispatcher: Dispatcher
private logger: Logger
public readonly inboundTransports: InboundTransport[] = []

public constructor(
config: AgentConfig,
Expand All @@ -40,6 +42,10 @@ export class MessageReceiver {
this.logger = this.config.logger
}

public registerInboundTransport(inboundTransport: InboundTransport) {
this.inboundTransports.push(inboundTransport)
}

/**
* Receive and handle an inbound DIDComm message. It will unpack the message, transform it
* to it's corresponding message class and finally dispatch it to the dispatcher.
Expand Down
22 changes: 9 additions & 13 deletions packages/core/src/agent/MessageSender.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { DidCommService, ConnectionRecord } from '../modules/connections'
import type { OutboundTransporter } from '../transport/OutboundTransporter'
import type { OutboundTransport } from '../transport/OutboundTransport'
import type { OutboundMessage, OutboundPackage, WireMessage } from '../types'
import type { AgentMessage } from './AgentMessage'
import type { EnvelopeKeys } from './EnvelopeService'
Expand Down Expand Up @@ -27,7 +27,7 @@ export class MessageSender {
private transportService: TransportService
private messageRepository: MessageRepository
private logger: Logger
private outboundTransports: OutboundTransporter[] = []
public readonly outboundTransports: OutboundTransport[] = []

public constructor(
envelopeService: EnvelopeService,
Expand All @@ -42,12 +42,8 @@ export class MessageSender {
this.outboundTransports = []
}

public registerOutboundTransporter(outboundTransporter: OutboundTransporter) {
this.outboundTransports.push(outboundTransporter)
}

public get outboundTransporters() {
return this.outboundTransports
public registerOutboundTransport(outboundTransport: OutboundTransport) {
this.outboundTransports.push(outboundTransport)
}

public async packMessage({
Expand Down Expand Up @@ -103,15 +99,15 @@ export class MessageSender {
// Retrieve DIDComm services
const { services, queueService } = await this.retrieveServicesByConnection(connection, options?.transportPriority)

if (this.outboundTransporters.length === 0 && !queueService) {
throw new AriesFrameworkError('Agent has no outbound transporter!')
if (this.outboundTransports.length === 0 && !queueService) {
throw new AriesFrameworkError('Agent has no outbound transport!')
}

// Loop trough all available services and try to send the message
for await (const service of services) {
this.logger.debug(`Sending outbound message to service:`, { service })
try {
for (const transport of this.outboundTransporters) {
for (const transport of this.outboundTransports) {
if (transport.supportedSchemes.includes(service.protocolScheme)) {
await transport.sendMessage({
payload: packedMessage,
Expand Down Expand Up @@ -235,7 +231,7 @@ export class MessageSender {
returnRoute?: boolean
}) {
if (this.outboundTransports.length === 0) {
throw new AriesFrameworkError('Agent has no outbound transporter!')
throw new AriesFrameworkError('Agent has no outbound transport!')
}

this.logger.debug(`Sending outbound message to service:`, { messageId: message.id, service })
Expand All @@ -253,7 +249,7 @@ export class MessageSender {

const outboundPackage = await this.packMessage({ message, keys, endpoint: service.serviceEndpoint })
outboundPackage.endpoint = service.serviceEndpoint
for (const transport of this.outboundTransporters) {
for (const transport of this.outboundTransports) {
if (transport.supportedSchemes.includes(service.protocolScheme)) {
await transport.sendMessage(outboundPackage)
break
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/agent/__tests__/AgentConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ describe('AgentConfig', () => {
const endpoint = 'https://local-url.com'

const agentConfig = getAgentConfig('AgentConfig Test', {
endpoint,
endpoints: [endpoint],
})

expect(agentConfig.getEndpoint()).toBe(endpoint)
expect(agentConfig.endpoints).toEqual([endpoint])
})

it("should return 'didcomm:transport/queue' if no inbound connection or config endpoint or host/port is available", () => {
it("should return ['didcomm:transport/queue'] if no inbound connection or config endpoint or host/port is available", () => {
const agentConfig = getAgentConfig('AgentConfig Test')

expect(agentConfig.getEndpoint()).toBe('didcomm:transport/queue')
expect(agentConfig.endpoints).toStrictEqual(['didcomm:transport/queue'])
})
})
})

0 comments on commit 56cb9f2

Please sign in to comment.