oven-sh / bun

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one
https://bun.sh
Other
73.54k stars 2.71k forks source link

Bun does not support streaming videos with range requests #10440

Open gal-yedidovich opened 5 months ago

gal-yedidovich commented 5 months ago

What version of Bun is running?

1.1.4

What platform is your computer?

Darwin 23.4.0 arm64 arm

What steps can reproduce the bug?

I created a sample http server using express that streams videos with range requests, but it seems that bun is not able to handle it and the request is loading indefinitely.

here is an express sample project code: server.js

import express from 'express'
import fs from 'fs'

console.log('Starting server...')
const port = 3000
const app = express()

app.use(express.static('public'))

app.get('/video', function (req, res) {
    try {
        const range = req.headers.range
        if (!range) {
            res.status(416).send('Requires Range header')
            return
        }

        const videoPath = 'local-file-path.mp4'

        const size = fs.statSync(videoPath).size
        const CHUNK_SIZE = 2 * (10 ** 6) // 2MB each chunk
        const start = Number(range.replace(/\D/g, '')) // starting point from the request range
        const end = Math.min(start + CHUNK_SIZE, size - 1) // ending point
        const contentLength = end - start + 1

        res.writeHead(206, {
            'Content-Range': `bytes ${start}-${end}/${size}`,
            'Accept-Ranges': 'bytes',
            'Content-Length': contentLength,
            'Content-Type': 'video/mp4',
            'Access-Control-Allow-Origin': '*',
        })

        const videoStream = fs.createReadStream(videoPath, { start, end })
        videoStream.pipe(res)
    } catch (e) {
        console.error('failed to get video:', e)
        res.status(500)
    }
})

app.listen(port, function () {
    console.log(`Listening on port ${port}...`)
})

also, index.html:

<!DOCTYPE html>
<html lang="en">
<body>
<video style="width: 100vw; height: 100vh" controls autofocus>
    <source src="/video">
</video>
</body>
</html>

Using Bun.file

I tried also using the standard Bun way with Bun.file('path'), it seems to download the entire file instead of serving the requested range.

What is the expected behavior?

I'm expecting bun to support every use case of video streaming

  1. supporting express library
  2. supporting with Bun.file('path')

What do you see instead?

  1. with express example: The request is indefinite, and does not ever ends
  2. using Bun.file (with Bun.serve): The video file is served from beginning before video reaching the requested start-range

Additional information

I also tried it with Elysia to be sure there is no workaround with a supporting framework, I got the similar result as using only Bun.serve with Bun.file

server.ts: (serving identical index.html for the client)

import { Elysia } from 'elysia'
import { staticPlugin } from '@elysiajs/static'

const app = new Elysia()
    .get('/', Bun.file('public/index.html')
    .get('/video', Bun.file('local-file-path.mp4'))
    .listen(3000)

console.log(
    `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`,
)
7f8ddd commented 5 months ago

Sadly there is no way to slice a Bun.file (yet)

Jarred-Sumner commented 5 months ago

I'm pretty sure we have tests that verify this works

Jarred-Sumner commented 5 months ago

oh this is using express, that makes more sense

gal-yedidovich commented 5 months ago

@Jarred-Sumner

I'm pretty sure we have tests that verify this works

Can you direct me to an example?

slmjkdbtl commented 4 months ago

I think it's this example?