fix(webdav): read ahead for mounted media ranges

This commit is contained in:
saltbo
2026-05-12 22:42:43 -04:00
parent ea27161690
commit bdd2d526cc
2 changed files with 37 additions and 1 deletions
+28
View File
@@ -489,6 +489,34 @@ describe('WebDAV API', () => {
expect(S3Service.prototype.getObjectBytes).not.toHaveBeenCalled()
})
it('expands tiny WebDAVFS ranges for mounted media reads', async () => {
const { app, db, auth } = await createTestApp()
await authedHeaders(app)
await seedStorage(db)
const workspace = await org(db)
const account = await userAccount(db)
const key = await apiKey(auth, account.id, { webdav: ['read'] })
await file(db, workspace.id, { id: 'mounted-media', name: 'audio.mp3', size: 2 * 1024 * 1024 })
vi.mocked(S3Service.prototype.getObjectBody).mockResolvedValueOnce(streamBody('chunk'))
const partial = await app.request(`/dav/${workspace.slug}/audio.mp3`, {
method: 'GET',
headers: basicHeaders(account.email, key, {
Range: 'bytes=419430-419430',
'User-Agent': 'WebDAVFS/3.0.0 (03008000) Darwin/24.6.0 (arm64)',
}),
})
expect(partial.status).toBe(206)
expect(partial.headers.get('Content-Range')).toBe('bytes 419430-1468005/2097152')
expect(partial.headers.get('Content-Length')).toBe('1048576')
expect(S3Service.prototype.getObjectBody).toHaveBeenCalledWith(
expect.objectContaining({ id: storage.id }),
'objects/mounted-media.txt',
'bytes=419430-1468005',
)
})
it('GET ignores unsupported ranges and honors If-Range validators', async () => {
const { app, db, auth } = await createTestApp()
await authedHeaders(app)
+9 -1
View File
@@ -261,6 +261,8 @@ interface ByteRange {
end: number
}
const WEBDAVFS_READ_AHEAD_BYTES = 1024 * 1024
type RangeRequest = { action: 'none' | 'ignore' } | { action: 'serve'; range: ByteRange } | { action: 'reject' }
function parseRangeRequest(header: string | undefined, size: number): RangeRequest {
@@ -298,6 +300,12 @@ function rangeNotSatisfiable(size: number): Response {
return new Response(null, { status: 416, headers: { 'Content-Range': `bytes */${size}` } })
}
function webDavFsReadAheadRange(c: DavContext, range: ByteRange, size: number): ByteRange {
if (!c.req.header('User-Agent')?.startsWith('WebDAVFS/')) return range
if (range.end - range.start + 1 >= WEBDAVFS_READ_AHEAD_BYTES) return range
return { start: range.start, end: Math.min(size - 1, range.start + WEBDAVFS_READ_AHEAD_BYTES - 1) }
}
function ifRangeMatches(header: string | undefined, matter: NonNullable<WebDavTarget['matter']>): boolean {
if (!header) return true
const value = header.trim()
@@ -622,7 +630,7 @@ async function readFile(c: DavContext, auth: DavAuth): Promise<Response> {
if (rangeRequest.action === 'reject') return rangeNotSatisfiable(size)
if (rangeRequest.action !== 'serve') throw new Error('Unexpected range request action')
const range = rangeRequest.range
const range = webDavFsReadAheadRange(c, rangeRequest.range, size)
const body = await s3.getObjectBody(storage, matter.object, `bytes=${range.start}-${range.end}`)
headers.set('Content-Length', String(range.end - range.start + 1))
headers.set('Content-Range', `bytes ${range.start}-${range.end}/${size}`)