fix(audio): give ffprobe its own 45s deadline, a timeout is the file's fault (#49)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,8 +22,14 @@ type FFprobe struct {
|
||||
Bin string
|
||||
// MaxOutput bounds how much of ffprobe's stdout is read; zero means DefaultMaxOutput.
|
||||
MaxOutput int
|
||||
// Timeout bounds one probe; zero means DefaultProbeTimeout. A probe is milliseconds of
|
||||
// work on a real file, so one that runs this long is the FILE's doing (#49).
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// DefaultProbeTimeout is the probe deadline when FFprobe.Timeout is unset.
|
||||
const DefaultProbeTimeout = 45 * time.Second
|
||||
|
||||
// DefaultMaxOutput is far above the narrowed probe output of a legitimate file (a few
|
||||
// hundred bytes) and far below what a hostile one can make ffprobe print.
|
||||
const DefaultMaxOutput = 1 << 20
|
||||
@@ -102,7 +108,13 @@ func (f *FFprobe) Probe(ctx context.Context, path string) (uploads.Probe, error)
|
||||
}
|
||||
args := append([]string{"-v", "error"}, inputRestrictions...)
|
||||
args = append(args, "-print_format", "json", "-show_entries", probeEntries, path)
|
||||
cmd := exec.CommandContext(ctx, bin, args...)
|
||||
timeout := f.Timeout
|
||||
if timeout <= 0 {
|
||||
timeout = DefaultProbeTimeout
|
||||
}
|
||||
probeCtx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(probeCtx, bin, args...)
|
||||
cmd.WaitDelay = killGrace
|
||||
maxOut := f.MaxOutput
|
||||
if maxOut <= 0 {
|
||||
@@ -111,9 +123,12 @@ func (f *FFprobe) Probe(ctx context.Context, path string) (uploads.Probe, error)
|
||||
stdout, stderr := &capWriter{max: maxOut}, &headWriter{max: maxStderr}
|
||||
cmd.Stdout, cmd.Stderr = stdout, stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
if ctxErr := ctx.Err(); ctxErr != nil {
|
||||
if ctxErr := ctx.Err(); ctxErr != nil { // the caller gave up: not a verdict on the file
|
||||
return uploads.Probe{}, fmt.Errorf("audio: ffprobe: %w", ctxErr)
|
||||
}
|
||||
if probeCtx.Err() != nil { // our own deadline passed while the caller was still waiting
|
||||
return uploads.Probe{}, fmt.Errorf("%w: ffprobe did not finish within %s", uploads.ErrUnreadable, timeout)
|
||||
}
|
||||
if errors.Is(err, errOutputCap) {
|
||||
return uploads.Probe{}, fmt.Errorf("%w: ffprobe printed more than %d bytes", uploads.ErrUnreadable, maxOut)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user