2022-02-21 16:28:04 +01:00
|
|
|
|
|
|
|
'use strict'
|
|
|
|
|
|
|
|
/* eslint-env mocha */
|
|
|
|
|
|
|
|
const mockConnection = require('../../utils/mockConnection')
|
|
|
|
const { expect } = require('aegir/utils/chai')
|
|
|
|
const peerUtils = require('../../utils/creators/peer')
|
|
|
|
const { handleHopProtocol } = require('../../../src/circuit/v2/hop')
|
|
|
|
const StreamHandler = require('../../../src/circuit/v2/stream-handler')
|
|
|
|
const multicodec = require('../../../src/circuit/multicodec')
|
|
|
|
const { Status, HopMessage } = require('../../../src/circuit/v2/protocol')
|
|
|
|
const { Multiaddr } = require('multiaddr')
|
|
|
|
const sinon = require('sinon')
|
|
|
|
|
|
|
|
describe('Circuit v2 - hop protocol', function () {
|
|
|
|
it('error on unknow message type', async function () {
|
|
|
|
const conn = await mockConnection()
|
|
|
|
const { stream } = await conn.newStream([multicodec.protocolIDv2Hop])
|
|
|
|
const streamHandler = new StreamHandler({ stream })
|
|
|
|
await handleHopProtocol({
|
|
|
|
connection: conn,
|
|
|
|
streamHandler,
|
|
|
|
request: {
|
|
|
|
type: 'not_existing'
|
|
|
|
}
|
|
|
|
})
|
|
|
|
const msg = HopMessage.decode(await streamHandler.read())
|
|
|
|
expect(msg.type).to.be.equal(HopMessage.Type.STATUS)
|
|
|
|
expect(msg.status).to.be.equal(Status.MALFORMED_MESSAGE)
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('reserve', function () {
|
|
|
|
let srcPeer, relayPeer, conn, streamHandler, reservationStore
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
[srcPeer, relayPeer] = await peerUtils.createPeerId({ number: 2 })
|
|
|
|
conn = await mockConnection({ localPeer: srcPeer, remotePeer: relayPeer })
|
|
|
|
const { stream } = await conn.newStream([multicodec.protocolIDv2Hop])
|
|
|
|
streamHandler = new StreamHandler({ stream })
|
|
|
|
reservationStore = {
|
|
|
|
reserve: sinon.stub(),
|
|
|
|
removeReservation: sinon.stub()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
this.afterEach(async function () {
|
|
|
|
streamHandler.close()
|
|
|
|
await conn.close()
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should reserve slot', async function () {
|
|
|
|
const expire = 123
|
|
|
|
reservationStore.reserve.resolves({ status: Status.OK, expire })
|
|
|
|
await handleHopProtocol({
|
|
|
|
request: {
|
|
|
|
type: HopMessage.Type.RESERVE
|
|
|
|
},
|
|
|
|
connection: conn,
|
|
|
|
streamHandler,
|
|
|
|
relayPeer,
|
|
|
|
relayAddrs: [new Multiaddr('/ip4/127.0.0.1/udp/1234')],
|
|
|
|
reservationStore
|
|
|
|
})
|
|
|
|
expect(reservationStore.reserve.calledOnceWith(conn.remotePeer, conn.remoteAddr)).to.be.true()
|
|
|
|
const response = HopMessage.decode(await streamHandler.read())
|
|
|
|
expect(response.type).to.be.equal(HopMessage.Type.STATUS)
|
|
|
|
expect(response.limit).to.be.null()
|
|
|
|
expect(response.status).to.be.equal(Status.OK)
|
2022-03-23 14:41:13 +01:00
|
|
|
expect(response.reservation.expire).to.be.equal(expire)
|
2022-02-21 16:28:04 +01:00
|
|
|
expect(response.reservation.voucher).to.not.be.null()
|
|
|
|
expect(response.reservation.addrs.length).to.be.greaterThan(0)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should fail to reserve slot - acl denied', async function () {
|
|
|
|
await handleHopProtocol({
|
|
|
|
request: {
|
|
|
|
type: HopMessage.Type.RESERVE
|
|
|
|
},
|
|
|
|
connection: conn,
|
|
|
|
streamHandler,
|
|
|
|
relayPeer,
|
|
|
|
relayAddrs: [new Multiaddr('/ip4/127.0.0.1/udp/1234')],
|
|
|
|
reservationStore,
|
|
|
|
acl: { allowReserve: function () { return false } }
|
|
|
|
})
|
|
|
|
expect(reservationStore.reserve.notCalled).to.be.true()
|
|
|
|
const response = HopMessage.decode(await streamHandler.read())
|
|
|
|
expect(response.type).to.be.equal(HopMessage.Type.STATUS)
|
|
|
|
expect(response.limit).to.be.null()
|
|
|
|
expect(response.status).to.be.equal(Status.PERMISSION_DENIED)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should fail to reserve slot - resource exceeded', async function () {
|
|
|
|
reservationStore.reserve.resolves({ status: Status.RESOURCE_LIMIT_EXCEEDED })
|
|
|
|
await handleHopProtocol({
|
|
|
|
request: {
|
|
|
|
type: HopMessage.Type.RESERVE
|
|
|
|
},
|
|
|
|
connection: conn,
|
|
|
|
streamHandler,
|
|
|
|
relayPeer,
|
|
|
|
relayAddrs: [new Multiaddr('/ip4/127.0.0.1/udp/1234')],
|
|
|
|
reservationStore
|
|
|
|
})
|
|
|
|
expect(reservationStore.reserve.calledOnce).to.be.true()
|
|
|
|
const response = HopMessage.decode(await streamHandler.read())
|
|
|
|
expect(response.type).to.be.equal(HopMessage.Type.STATUS)
|
|
|
|
expect(response.limit).to.be.null()
|
|
|
|
expect(response.status).to.be.equal(Status.RESOURCE_LIMIT_EXCEEDED)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should fail to reserve slot - failed to write response', async function () {
|
|
|
|
reservationStore.reserve.resolves({ status: Status.OK, expire: 123 })
|
|
|
|
reservationStore.removeReservation.resolves()
|
|
|
|
const backup = streamHandler.write
|
|
|
|
streamHandler.write = function () { throw new Error('connection reset') }
|
|
|
|
await handleHopProtocol({
|
|
|
|
request: {
|
|
|
|
type: HopMessage.Type.RESERVE
|
|
|
|
},
|
|
|
|
connection: conn,
|
|
|
|
streamHandler,
|
|
|
|
relayPeer,
|
|
|
|
relayAddrs: [new Multiaddr('/ip4/127.0.0.1/udp/1234')],
|
|
|
|
reservationStore
|
|
|
|
})
|
|
|
|
expect(reservationStore.reserve.calledOnce).to.be.true()
|
|
|
|
expect(reservationStore.removeReservation.calledOnce).to.be.true()
|
|
|
|
streamHandler.write = backup
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('connect', function () {
|
|
|
|
let srcPeer, relayPeer, dstPeer, conn, streamHandler, reservationStore, circuit
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
[srcPeer, relayPeer, dstPeer] = await peerUtils.createPeerId({ number: 3 })
|
|
|
|
conn = await mockConnection({ localPeer: srcPeer, remotePeer: relayPeer })
|
|
|
|
const { stream } = await conn.newStream([multicodec.protocolIDv2Hop])
|
|
|
|
streamHandler = new StreamHandler({ stream })
|
|
|
|
reservationStore = {
|
|
|
|
reserve: sinon.stub(),
|
|
|
|
removeReservation: sinon.stub(),
|
|
|
|
hasReservation: sinon.stub()
|
|
|
|
}
|
|
|
|
circuit = {
|
|
|
|
_connectionManager: {
|
|
|
|
get: sinon.stub()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
this.afterEach(async function () {
|
|
|
|
streamHandler.close()
|
|
|
|
await conn.close()
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should succeed to connect', async function () {
|
|
|
|
reservationStore.hasReservation.resolves(Status.OK)
|
|
|
|
const dstConn = await mockConnection({ localPeer: dstPeer, remotePeer: relayPeer })
|
|
|
|
circuit._connectionManager.get.returns(dstConn)
|
|
|
|
await handleHopProtocol({
|
|
|
|
connection: conn,
|
|
|
|
streamHandler,
|
|
|
|
request: {
|
|
|
|
type: HopMessage.Type.CONNECT,
|
|
|
|
peer: {
|
|
|
|
id: dstPeer.id,
|
|
|
|
addrs: []
|
|
|
|
}
|
|
|
|
},
|
|
|
|
reservationStore,
|
|
|
|
circuit
|
|
|
|
})
|
|
|
|
const response = HopMessage.decode(await streamHandler.read())
|
|
|
|
expect(response.type).to.be.equal(HopMessage.Type.STATUS)
|
|
|
|
expect(response.status).to.be.equal(Status.OK)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should succeed to connect', async function () {
|
|
|
|
reservationStore.hasReservation.resolves(Status.OK)
|
|
|
|
const dstConn = await mockConnection({ localPeer: dstPeer, remotePeer: relayPeer })
|
|
|
|
circuit._connectionManager.get.returns(dstConn)
|
|
|
|
await handleHopProtocol({
|
|
|
|
connection: conn,
|
|
|
|
streamHandler,
|
|
|
|
request: {
|
|
|
|
type: HopMessage.Type.CONNECT,
|
|
|
|
peer: {
|
|
|
|
id: dstPeer.id,
|
|
|
|
addrs: []
|
|
|
|
}
|
|
|
|
},
|
|
|
|
reservationStore,
|
|
|
|
circuit
|
|
|
|
})
|
|
|
|
const response = HopMessage.decode(await streamHandler.read())
|
|
|
|
expect(response.type).to.be.equal(HopMessage.Type.STATUS)
|
|
|
|
expect(response.status).to.be.equal(Status.OK)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should fail to connect - invalid request', async function () {
|
|
|
|
await handleHopProtocol({
|
|
|
|
connection: conn,
|
|
|
|
streamHandler,
|
|
|
|
request: {
|
|
|
|
type: HopMessage.Type.CONNECT,
|
|
|
|
peer: {
|
|
|
|
}
|
|
|
|
},
|
|
|
|
reservationStore,
|
|
|
|
circuit
|
|
|
|
})
|
|
|
|
const response = HopMessage.decode(await streamHandler.read())
|
|
|
|
expect(response.type).to.be.equal(HopMessage.Type.STATUS)
|
|
|
|
expect(response.status).to.be.equal(Status.MALFORMED_MESSAGE)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should failed to connect - acl denied', async function () {
|
|
|
|
const acl = {
|
|
|
|
allowConnect: function () { return Status.PERMISSION_DENIED }
|
|
|
|
}
|
|
|
|
await handleHopProtocol({
|
|
|
|
connection: conn,
|
|
|
|
streamHandler,
|
|
|
|
request: {
|
|
|
|
type: HopMessage.Type.CONNECT,
|
|
|
|
peer: {
|
|
|
|
id: dstPeer.id,
|
|
|
|
addrs: []
|
|
|
|
}
|
|
|
|
},
|
|
|
|
reservationStore,
|
|
|
|
circuit,
|
|
|
|
acl
|
|
|
|
})
|
|
|
|
const response = HopMessage.decode(await streamHandler.read())
|
|
|
|
expect(response.type).to.be.equal(HopMessage.Type.STATUS)
|
|
|
|
expect(response.status).to.be.equal(Status.PERMISSION_DENIED)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should fail to connect - no reservation', async function () {
|
|
|
|
reservationStore.hasReservation.resolves(Status.NO_RESERVATION)
|
|
|
|
await handleHopProtocol({
|
|
|
|
connection: conn,
|
|
|
|
streamHandler,
|
|
|
|
request: {
|
|
|
|
type: HopMessage.Type.CONNECT,
|
|
|
|
peer: {
|
|
|
|
id: dstPeer.id,
|
|
|
|
addrs: []
|
|
|
|
}
|
|
|
|
},
|
|
|
|
reservationStore,
|
|
|
|
circuit
|
|
|
|
})
|
|
|
|
const response = HopMessage.decode(await streamHandler.read())
|
|
|
|
expect(response.type).to.be.equal(HopMessage.Type.STATUS)
|
|
|
|
expect(response.status).to.be.equal(Status.NO_RESERVATION)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should fail to connect - no connection', async function () {
|
|
|
|
reservationStore.hasReservation.resolves(Status.OK)
|
|
|
|
await handleHopProtocol({
|
|
|
|
connection: conn,
|
|
|
|
streamHandler,
|
|
|
|
request: {
|
|
|
|
type: HopMessage.Type.CONNECT,
|
|
|
|
peer: {
|
|
|
|
id: dstPeer.id,
|
|
|
|
addrs: []
|
|
|
|
}
|
|
|
|
},
|
|
|
|
reservationStore,
|
|
|
|
circuit
|
|
|
|
})
|
|
|
|
const response = HopMessage.decode(await streamHandler.read())
|
|
|
|
expect(response.type).to.be.equal(HopMessage.Type.STATUS)
|
|
|
|
expect(response.status).to.be.equal(Status.NO_RESERVATION)
|
|
|
|
expect(circuit._connectionManager.get.calledOnce).to.be.true()
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|