mirror of
https://github.com/navidrome/navidrome.git
synced 2026-05-03 06:51:16 +00:00
fix(radio): use sentinel errors for Radio Browser query validation - #5239
Export ErrQueryTooShort and ErrQueryTooLong; native API uses errors.Is instead of matching error strings. Signed-off-by: elpatron <m.busche@gmail.com>
This commit is contained in:
parent
276cb06f5f
commit
78b6a899ee
@ -87,7 +87,7 @@ func (api *Router) searchRadioBrowser() http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
stations, err := radiobrowser.Search(r.Context(), q, limit)
|
stations, err := radiobrowser.Search(r.Context(), q, limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "too short") || strings.Contains(err.Error(), "too long") {
|
if errors.Is(err, radiobrowser.ErrQueryTooShort) || errors.Is(err, radiobrowser.ErrQueryTooLong) {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ package radiobrowser
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@ -39,6 +40,12 @@ type apiStation struct {
|
|||||||
StationUUID string `json:"stationuuid"`
|
StationUUID string `json:"stationuuid"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sentinel errors for query validation. Use errors.Is to detect them.
|
||||||
|
var (
|
||||||
|
ErrQueryTooShort = errors.New("query too short")
|
||||||
|
ErrQueryTooLong = errors.New("query too long")
|
||||||
|
)
|
||||||
|
|
||||||
var fallbackAPIHosts = []string{
|
var fallbackAPIHosts = []string{
|
||||||
"de1.api.radio-browser.info",
|
"de1.api.radio-browser.info",
|
||||||
"nl1.api.radio-browser.info",
|
"nl1.api.radio-browser.info",
|
||||||
@ -87,10 +94,10 @@ func shuffleHosts(hosts []string) []string {
|
|||||||
func Search(ctx context.Context, rawQuery string, limit int) ([]Station, error) {
|
func Search(ctx context.Context, rawQuery string, limit int) ([]Station, error) {
|
||||||
q := strings.TrimSpace(rawQuery)
|
q := strings.TrimSpace(rawQuery)
|
||||||
if len(q) < minQueryLen {
|
if len(q) < minQueryLen {
|
||||||
return nil, fmt.Errorf("query too short (min %d characters)", minQueryLen)
|
return nil, fmt.Errorf("query too short (min %d characters): %w", minQueryLen, ErrQueryTooShort)
|
||||||
}
|
}
|
||||||
if len(q) > maxQueryLen {
|
if len(q) > maxQueryLen {
|
||||||
return nil, fmt.Errorf("query too long (max %d characters)", maxQueryLen)
|
return nil, fmt.Errorf("query too long (max %d characters): %w", maxQueryLen, ErrQueryTooLong)
|
||||||
}
|
}
|
||||||
if limit <= 0 {
|
if limit <= 0 {
|
||||||
limit = defaultLimit
|
limit = defaultLimit
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
package radiobrowser
|
package radiobrowser
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func TestNormalizeStations(t *testing.T) {
|
func TestNormalizeStations(t *testing.T) {
|
||||||
raw := []apiStation{
|
raw := []apiStation{
|
||||||
@ -19,3 +24,16 @@ func TestNormalizeStations(t *testing.T) {
|
|||||||
t.Fatalf("second stream: %q", got[1].StreamURL)
|
t.Fatalf("second stream: %q", got[1].StreamURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSearchSentinelErrors(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
_, err := Search(ctx, "x", 10)
|
||||||
|
if !errors.Is(err, ErrQueryTooShort) {
|
||||||
|
t.Fatalf("short query: want ErrQueryTooShort, got %v", err)
|
||||||
|
}
|
||||||
|
long := strings.Repeat("a", maxQueryLen+1)
|
||||||
|
_, err = Search(ctx, long, 10)
|
||||||
|
if !errors.Is(err, ErrQueryTooLong) {
|
||||||
|
t.Fatalf("long query: want ErrQueryTooLong, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user