Skip to content

Commit

Permalink
feat(@vates/nbd-client): use secure connect eve,t
Browse files Browse the repository at this point in the history
  • Loading branch information
fbeauchamp committed Oct 12, 2022
1 parent 76bd00b commit cc3f2c0
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 54 deletions.
25 changes: 11 additions & 14 deletions @vates/nbd-client/.USAGE.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
### `new NdbClient({address, exportname, secure = true, port = 10809})`

create a new nbd client
### `new NdbClient({address, exportname, secure = true, port = 10809})`

create a new nbd client

```js
import NbdClient from '@vates/nbd-client'
const client = new NbdClient({
address: 'MY_NBD_HOST',
exportname: 'MY_SECRET_EXPORT',
cert: 'Server certificate', // optional, will use encrypted link if provided
})

import NbdClient from '@vates/nbd-client'
const client = new NbdClient({
address: 'MY_NBD_HOST',
exportname: 'MY_SECRET_EXPORT',
cert: 'Server certificate', // optional, will use encrypted link if provided
})

await client.connect()
const block = await client.readBlock(blockIndex, BlockSize)
await client.disconnect()

await client.connect()
const block = await client.readBlock(blockIndex, BlockSize)
await client.disconnect()
```
92 changes: 52 additions & 40 deletions @vates/nbd-client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,17 @@ module.exports = class NbdClient {

async #tlsConnect() {
return new Promise((resolve, reject) => {
this.#serverSocket = connect(
{
socket: this.#serverSocket,
rejectUnauthorized: false,
cert: this.#serverCert,
},
resolve
)
this.#serverSocket.once('error', reject)
// @todo : why is this never called ?
this.#serverSocket.once('connect', () => {
this.#serverSocket = connect({
socket: this.#serverSocket,
rejectUnauthorized: false,
cert: this.#serverCert,
})
this.#serverSocket.once('secureConnect', () => {
this.#serverSocket.removeListener('error', reject)
resolve()
})

this.#serverSocket.once('error', reject)
})
}

Expand All @@ -85,6 +82,11 @@ module.exports = class NbdClient {
// to tls during the handshake
await this.#unsecureConnect()
await this.#handshake()

// reset internal state if we reconnected a nbd client
this.#commandQueryBacklog = new Map()
this.#nbCommands = 0
this.#waitingForResponse = false
}

async disconnect() {
Expand Down Expand Up @@ -163,10 +165,10 @@ module.exports = class NbdClient {

// when one read fail ,stop everything
async #rejectAll(error) {
this.this.#commandQueryBacklog.forEach(({ reject }, blockQueryId) => {
this.#commandQueryBacklog.forEach(({ reject }) => {
reject(error)
this.#commandQueryBacklog.delete(blockQueryId)
})
await this.disconnect()
}

async #readBlockResponse() {
Expand All @@ -175,32 +177,39 @@ module.exports = class NbdClient {
if (this.#waitingForResponse) {
return
}
this.#waitingForResponse = true
this.#nbCommands--
const magic = await this.#readInt32()

if (magic !== NBD_REPLY_MAGIC) {
return this.#rejectAll(new Error(`magic number for block answer is wrong : ${magic} ${NBD_REPLY_MAGIC}`))
}

const error = await this.#readInt32()
if (error !== 0) {
// @todo use error code from constants.mjs
return this.#rejectAll(new Error(`GOT ERROR CODE : ${error}`))
}

const blockQueryId = await this.#readInt64()
const query = this.#commandQueryBacklog.get(blockQueryId)
if (!query) {
return this.#rejectAll(new Error(` no query associated with id ${blockQueryId}`))
}
this.#commandQueryBacklog.delete(blockQueryId)
const data = await this.#read(query.size)
query.resolve(data)

this.#waitingForResponse = false
if (this.#nbCommands > 0) {
await this.#readBlockResponse()
try {
this.#waitingForResponse = true
this.#nbCommands--
const magic = await this.#readInt32()

if (magic !== NBD_REPLY_MAGIC) {
throw new Error(`magic number for block answer is wrong : ${magic} ${NBD_REPLY_MAGIC}`)
}

const error = await this.#readInt32()
if (error !== 0) {
// @todo use error code from constants.mjs
throw new Error(`GOT ERROR CODE : ${error}`)
}

const blockQueryId = await this.#readInt64()
const query = this.#commandQueryBacklog.get(blockQueryId)
if (!query) {
throw new Error(` no query associated with id ${blockQueryId}`)
}
this.#commandQueryBacklog.delete(blockQueryId)
const data = await this.#read(query.size)
query.resolve(data)
this.#waitingForResponse = false
if (this.#nbCommands > 0) {
await this.#readBlockResponse()
}
} catch (error) {
// reject all the promises
// we don't need to call readBlockResponse on failure
// since we will empty the backlog
await this.#rejectAll(error)
}
}

Expand All @@ -227,8 +236,11 @@ module.exports = class NbdClient {
reject,
})
// really send the command to the server
this.#write(buffer).then(() => this.#readBlockResponse())
// it can never reject
this.#write(buffer).catch(reject)

// #readBlockResponse never throws directly
// but if it fails it will reject all the promises in the backlog
this.#readBlockResponse()
})
}
}

0 comments on commit cc3f2c0

Please sign in to comment.