Streaming with range requests and path safety, cover art discovery/resize cache
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
package streaming
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func setupFolder(t *testing.T) (root string) {
|
||||
t.Helper()
|
||||
root = t.TempDir()
|
||||
if err := os.MkdirAll(filepath.Join(root, "Artist", "Album"), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(root, "Artist", "Album", "song.mp3"), []byte("0123456789"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
func TestResolvePathAllowsFileWithinFolder(t *testing.T) {
|
||||
root := setupFolder(t)
|
||||
got, err := ResolvePath(root, "Artist/Album/song.mp3")
|
||||
if err != nil {
|
||||
t.Fatalf("ResolvePath: %v", err)
|
||||
}
|
||||
want, _ := filepath.EvalSymlinks(filepath.Join(root, "Artist", "Album", "song.mp3"))
|
||||
if got != want {
|
||||
t.Errorf("ResolvePath = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolvePathRejectsTraversal(t *testing.T) {
|
||||
root := setupFolder(t)
|
||||
// A secret file just outside the music folder root.
|
||||
if err := os.WriteFile(filepath.Join(filepath.Dir(root), "secret.txt"), []byte("nope"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err := ResolvePath(root, "../secret.txt")
|
||||
if err == nil {
|
||||
t.Fatal("expected ResolvePath to reject a path escaping the folder root")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolvePathRejectsDeepTraversal(t *testing.T) {
|
||||
root := setupFolder(t)
|
||||
_, err := ResolvePath(root, "Artist/../../../../../../etc/passwd")
|
||||
if err == nil {
|
||||
t.Fatal("expected ResolvePath to reject a deep traversal attempt")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeFileSupportsRangeRequests(t *testing.T) {
|
||||
root := setupFolder(t)
|
||||
path := filepath.Join(root, "Artist", "Album", "song.mp3")
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/stream/x", nil)
|
||||
req.Header.Set("Range", "bytes=2-5")
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
if err := ServeFile(rec, req, path, "song.mp3"); err != nil {
|
||||
t.Fatalf("ServeFile: %v", err)
|
||||
}
|
||||
|
||||
if rec.Code != http.StatusPartialContent {
|
||||
t.Fatalf("status = %d, want 206", rec.Code)
|
||||
}
|
||||
if got := rec.Body.String(); got != "2345" {
|
||||
t.Errorf("body = %q, want %q", got, "2345")
|
||||
}
|
||||
if cr := rec.Header().Get("Content-Range"); cr == "" {
|
||||
t.Error("expected Content-Range header on a range response")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeFileFullBody(t *testing.T) {
|
||||
root := setupFolder(t)
|
||||
path := filepath.Join(root, "Artist", "Album", "song.mp3")
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/stream/x", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
if err := ServeFile(rec, req, path, "song.mp3"); err != nil {
|
||||
t.Fatalf("ServeFile: %v", err)
|
||||
}
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("status = %d, want 200", rec.Code)
|
||||
}
|
||||
if rec.Body.String() != "0123456789" {
|
||||
t.Errorf("body = %q", rec.Body.String())
|
||||
}
|
||||
if rec.Header().Get("Accept-Ranges") != "bytes" {
|
||||
t.Error("expected Accept-Ranges: bytes header")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeFileRejectsDirectory(t *testing.T) {
|
||||
root := setupFolder(t)
|
||||
req := httptest.NewRequest(http.MethodGet, "/stream/x", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
if err := ServeFile(rec, req, filepath.Join(root, "Artist", "Album"), "Album"); err == nil {
|
||||
t.Fatal("expected error serving a directory")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMimeType(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
"song.mp3": "audio/mpeg",
|
||||
"song.flac": "audio/flac",
|
||||
"song.opus": "audio/opus",
|
||||
"song.m4a": "audio/mp4",
|
||||
"song.xyz": "application/octet-stream",
|
||||
}
|
||||
for path, want := range cases {
|
||||
if got := MimeType(path); got != want {
|
||||
t.Errorf("MimeType(%q) = %q, want %q", path, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user