Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add multiple inbound transports #433

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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.
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'])
})
})
})