mirror of
https://github.com/saltbo/zpan.git
synced 2026-09-01 15:49:00 +08:00
fix(webdav): read ahead for mounted media ranges
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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}`)
|
||||
|
||||
Reference in New Issue
Block a user