Skip to content

Commit dcd8786

Browse files
committed
Added readFile/writeFile chunks + unmount + tests for both
1 parent 2aced54 commit dcd8786

File tree

2 files changed

+136
-22
lines changed

2 files changed

+136
-22
lines changed

lib/drives/index.js

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ const {
1414
toStat,
1515
toMount,
1616
fromMount,
17-
toDriveStats
17+
toDriveStats,
18+
toChunks
1819
} = require('hyperdrive-daemon-client/lib/common')
1920
const { rpc } = require('hyperdrive-daemon-client')
2021

@@ -316,43 +317,63 @@ function createDriveHandlers (driveManager) {
316317
return rsp
317318
},
318319

320+
createReadStream: async (call) => {
321+
322+
},
323+
319324
readFile: async (call) => {
320325
const id = call.request.getId()
321326
const path = call.request.getPath()
322327

323328
if (!id) throw new Error('A readFile request must specify a session ID.')
324-
if (!path) throw new Error('A writeFile request must specify a path.')
329+
if (!path) throw new Error('A readFile request must specify a path.')
325330
const drive = driveManager.driveForSession(id)
326331

327-
return new Promise((resolve, reject) => {
332+
const content = await new Promise((resolve, reject) => {
328333
drive.readFile(path, (err, content) => {
329334
if (err) return reject(err)
330-
331-
const rsp = new rpc.drive.messages.ReadFileResponse()
332-
rsp.setContent(content)
333-
334-
return resolve(rsp)
335+
return resolve(content)
335336
})
336337
})
338+
339+
const chunks = toChunks(content)
340+
for (const chunk of chunks) {
341+
const rsp = new rpc.drive.messages.ReadFileResponse()
342+
rsp.setChunk(chunk)
343+
call.write(rsp)
344+
}
345+
call.end()
337346
},
338347

339-
writeFile: async (call) => {
340-
const id = call.request.getId()
341-
const path = call.request.getPath()
342-
const contents = Buffer.from(call.request.getContent())
348+
createWriteStream: async (call) => {
343349

344-
if (!id) throw new Error('A writeFile request must specify a session ID.')
345-
if (!path) throw new Error('A writeFile request must specify a path.')
346-
if (!contents) throw new Error('A writeFile request must specify contents.')
347-
const drive = driveManager.driveForSession(id)
350+
},
348351

352+
writeFile: async (call) => {
349353
return new Promise((resolve, reject) => {
350-
drive.writeFile(path, contents, (err) => {
351-
if (err) return reject(err)
352-
const rsp = new rpc.drive.messages.WriteFileResponse()
353-
return resolve(rsp)
354+
call.once('data', req => {
355+
const id = req.getId()
356+
const path = req.getPath()
357+
358+
if (!id) throw new Error('A writeFile request must specify a session ID.')
359+
if (!path) throw new Error('A writeFile request must specify a path.')
360+
const drive = driveManager.driveForSession(id)
361+
362+
return loadContent(resolve, reject, path, drive)
354363
})
355364
})
365+
366+
function loadContent (resolve, reject, path, drive) {
367+
return collectStream(call, (err, reqs) => {
368+
if (err) return reject(err)
369+
const chunks = reqs.map(req => Buffer.from(req.getChunk()))
370+
return drive.writeFile(path, Buffer.concat(chunks), err => {
371+
if (err) return reject(err)
372+
const rsp = new rpc.drive.messages.WriteFileResponse()
373+
return resolve(rsp)
374+
})
375+
})
376+
}
356377
},
357378

358379
stat: async (call) => {
@@ -454,8 +475,10 @@ function createDriveHandlers (driveManager) {
454475

455476
mount: async (call) => {
456477
const id = call.request.getId()
457-
const path = call.request.getPath()
458-
const opts = fromMount(call.request.getOpts())
478+
const mountInfo = call.request.getInfo()
479+
480+
const path = mountInfo.getPath()
481+
const opts = fromMount(mountInfo.getOpts())
459482

460483
if (!id) throw new Error('A mount request must specify a session ID.')
461484
if (!path) throw new Error('A mount request must specify a path.')
@@ -471,6 +494,23 @@ function createDriveHandlers (driveManager) {
471494
})
472495
},
473496

497+
unmount: async (call) => {
498+
const id = call.request.getId()
499+
const path = call.request.getPath()
500+
501+
if (!id) throw new Error('An unmount request must specify a session ID.')
502+
if (!path) throw new Error('An unmount request must specify a path.')
503+
const drive = driveManager.driveForSession(id)
504+
505+
return new Promise((resolve, reject) => {
506+
drive.unmount(path, err => {
507+
if (err) return reject(err)
508+
const rsp = new rpc.drive.messages.UnmountDriveResponse()
509+
return resolve(rsp)
510+
})
511+
})
512+
},
513+
474514
watch: async (call) => {
475515
const id = call.request.getId()
476516
var path = call.request.getPath()
@@ -487,7 +527,11 @@ function createDriveHandlers (driveManager) {
487527
call.on('finish', onclose)
488528
call.on('error', onclose)
489529

530+
var closed = false
490531
function onclose (err) {
532+
if (closed) return
533+
closed = true
534+
491535
watcher.destroy()
492536
log.debug({ id, path }, 'unregistering watcher')
493537
if (err && !call.destroyed) call.destroy(err)

test/hyperdrive.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,30 @@ test('can write/read a file from a remote hyperdrive', async t => {
2424
t.end()
2525
})
2626

27+
test('can write/read a large file from a remote hyperdrive', async t => {
28+
const { client, cleanup } = await createOne()
29+
30+
const content = Buffer.alloc(3.9e6 * 10.11).fill('abcdefghi')
31+
32+
try {
33+
const { opts, id } = await client.drive.get()
34+
t.true(opts.key)
35+
t.same(id, 1)
36+
37+
await client.drive.writeFile(id, 'hello', content)
38+
39+
const contents = await client.drive.readFile(id, 'hello')
40+
t.same(contents, content)
41+
42+
await client.drive.close(id)
43+
} catch (err) {
44+
t.fail(err)
45+
}
46+
47+
await cleanup()
48+
t.end()
49+
})
50+
2751
test('can stat a file from a remote hyperdrive', async t => {
2852
const { client, cleanup } = await createOne()
2953

@@ -143,3 +167,49 @@ test('can mount a drive within a remote hyperdrive', async t => {
143167
await cleanup()
144168
t.end()
145169
})
170+
171+
test.only('can unmount a drive within a remote hyperdrive', async t => {
172+
const { client, cleanup } = await createOne()
173+
174+
try {
175+
const { opts: opts1, id: id1 } = await client.drive.get()
176+
t.true(opts1.key)
177+
t.same(id1, 1)
178+
179+
const { opts: opts2, id: id2 } = await client.drive.get()
180+
t.true(opts2.key)
181+
t.same(id2, 2)
182+
t.notEqual(opts1.key, opts2.key)
183+
184+
const noVersion = { ...opts2, version: null }
185+
186+
await client.drive.mount(id1, 'a', noVersion)
187+
188+
await client.drive.writeFile(id1, 'a/hello', 'world')
189+
await client.drive.writeFile(id1, 'a/goodbye', 'dog')
190+
await client.drive.writeFile(id1, 'adios', 'amigo')
191+
await client.drive.writeFile(id2, 'hamster', 'wheel')
192+
193+
t.same(await client.drive.readFile(id1, 'adios'), Buffer.from('amigo'))
194+
t.same(await client.drive.readFile(id1, 'a/hello'), Buffer.from('world'))
195+
t.same(await client.drive.readFile(id2, 'hello'), Buffer.from('world'))
196+
t.same(await client.drive.readFile(id2, 'hamster'), Buffer.from('wheel'))
197+
198+
await client.drive.unmount(id1, 'a')
199+
try {
200+
await client.drive.readFile(id1, 'a/hello')
201+
} catch (err) {
202+
t.true(err)
203+
t.same(err.code, 2)
204+
}
205+
206+
await client.drive.close(id1)
207+
await client.drive.close(id2)
208+
} catch (err) {
209+
t.fail(err)
210+
}
211+
212+
await cleanup()
213+
t.end()
214+
215+
})

0 commit comments

Comments
 (0)