feat(plugins): add SubsonicAPI CallRaw, with support for raw=true binary response for host functions (#4982)

* feat: implement raw binary framing for host function responses

Signed-off-by: Deluan <deluan@navidrome.org>

* feat: add CallRaw method for Subsonic API to handle binary responses

Signed-off-by: Deluan <deluan@navidrome.org>

* test: add tests for raw=true methods and binary framing generation

Signed-off-by: Deluan <deluan@navidrome.org>

* fix: improve error message for malformed raw responses to indicate incomplete header

Signed-off-by: Deluan <deluan@navidrome.org>

* fix: add wasm_import_module attribute for raw methods and improve content-type handling

Signed-off-by: Deluan <deluan@navidrome.org>

---------

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan Quintão
2026-02-04 15:48:08 -05:00
committed by GitHub
parent 19ea338bed
commit e8863ed147
23 changed files with 1223 additions and 40 deletions
@@ -8,6 +8,9 @@
package {{.Package}}
import (
{{- if .Service.HasRawMethods}}
"encoding/binary"
{{- end}}
"encoding/json"
{{- if .Service.HasErrors}}
"errors"
@@ -49,7 +52,7 @@ type {{requestType .}} struct {
{{- end}}
}
{{- end}}
{{- if not .IsErrorOnly}}
{{- if and (not .IsErrorOnly) (not .Raw)}}
type {{responseType .}} struct {
{{- range .Returns}}
@@ -95,7 +98,27 @@ func {{$.Service.Name}}{{.Name}}({{range $i, $p := .Params}}{{if $i}}, {{end}}{{
// Read the response from memory
responseMem := pdk.FindMemory(responsePtr)
responseBytes := responseMem.ReadBytes()
{{- if .IsErrorOnly}}
{{- if .Raw}}
// Parse binary-framed response
if len(responseBytes) == 0 {
return "", nil, errors.New("empty response from host")
}
if responseBytes[0] == 0x01 { // error
return "", nil, errors.New(string(responseBytes[1:]))
}
if responseBytes[0] != 0x00 {
return "", nil, errors.New("unknown response status")
}
if len(responseBytes) < 5 {
return "", nil, errors.New("malformed raw response: incomplete header")
}
ctLen := binary.BigEndian.Uint32(responseBytes[1:5])
if uint32(len(responseBytes)) < 5+ctLen {
return "", nil, errors.New("malformed raw response: content-type overflow")
}
return string(responseBytes[5 : 5+ctLen]), responseBytes[5+ctLen:], nil
{{- else if .IsErrorOnly}}
// Parse error-only response
var response struct {