update
Some checks failed
Pipeline: Test, Lint, Build / Get version info (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test JS code (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint i18n files (push) Has been cancelled
Pipeline: Test, Lint, Build / Check Docker configuration (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (darwin/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (darwin/arm64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/386) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v5) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v6) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v7) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (windows/386) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (windows/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Push to GHCR (push) Has been cancelled
Pipeline: Test, Lint, Build / Push to Docker Hub (push) Has been cancelled
Pipeline: Test, Lint, Build / Cleanup digest artifacts (push) Has been cancelled
Pipeline: Test, Lint, Build / Build Windows installers (push) Has been cancelled
Pipeline: Test, Lint, Build / Package/Release (push) Has been cancelled
Pipeline: Test, Lint, Build / Upload Linux PKG (push) Has been cancelled
Close stale issues and PRs / stale (push) Has been cancelled
POEditor import / update-translations (push) Has been cancelled
Some checks failed
Pipeline: Test, Lint, Build / Get version info (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test Go code (push) Has been cancelled
Pipeline: Test, Lint, Build / Test JS code (push) Has been cancelled
Pipeline: Test, Lint, Build / Lint i18n files (push) Has been cancelled
Pipeline: Test, Lint, Build / Check Docker configuration (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (darwin/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (darwin/arm64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/386) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v5) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v6) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm/v7) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (linux/arm64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (windows/386) (push) Has been cancelled
Pipeline: Test, Lint, Build / Build (windows/amd64) (push) Has been cancelled
Pipeline: Test, Lint, Build / Push to GHCR (push) Has been cancelled
Pipeline: Test, Lint, Build / Push to Docker Hub (push) Has been cancelled
Pipeline: Test, Lint, Build / Cleanup digest artifacts (push) Has been cancelled
Pipeline: Test, Lint, Build / Build Windows installers (push) Has been cancelled
Pipeline: Test, Lint, Build / Package/Release (push) Has been cancelled
Pipeline: Test, Lint, Build / Upload Linux PKG (push) Has been cancelled
Close stale issues and PRs / stale (push) Has been cancelled
POEditor import / update-translations (push) Has been cancelled
This commit is contained in:
114
plugins/host_http.go
Normal file
114
plugins/host_http.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/navidrome/navidrome/log"
|
||||
hosthttp "github.com/navidrome/navidrome/plugins/host/http"
|
||||
)
|
||||
|
||||
type httpServiceImpl struct {
|
||||
pluginID string
|
||||
permissions *httpPermissions
|
||||
}
|
||||
|
||||
const defaultTimeout = 10 * time.Second
|
||||
|
||||
func (s *httpServiceImpl) Get(ctx context.Context, req *hosthttp.HttpRequest) (*hosthttp.HttpResponse, error) {
|
||||
return s.doHttp(ctx, http.MethodGet, req)
|
||||
}
|
||||
|
||||
func (s *httpServiceImpl) Post(ctx context.Context, req *hosthttp.HttpRequest) (*hosthttp.HttpResponse, error) {
|
||||
return s.doHttp(ctx, http.MethodPost, req)
|
||||
}
|
||||
|
||||
func (s *httpServiceImpl) Put(ctx context.Context, req *hosthttp.HttpRequest) (*hosthttp.HttpResponse, error) {
|
||||
return s.doHttp(ctx, http.MethodPut, req)
|
||||
}
|
||||
|
||||
func (s *httpServiceImpl) Delete(ctx context.Context, req *hosthttp.HttpRequest) (*hosthttp.HttpResponse, error) {
|
||||
return s.doHttp(ctx, http.MethodDelete, req)
|
||||
}
|
||||
|
||||
func (s *httpServiceImpl) Patch(ctx context.Context, req *hosthttp.HttpRequest) (*hosthttp.HttpResponse, error) {
|
||||
return s.doHttp(ctx, http.MethodPatch, req)
|
||||
}
|
||||
|
||||
func (s *httpServiceImpl) Head(ctx context.Context, req *hosthttp.HttpRequest) (*hosthttp.HttpResponse, error) {
|
||||
return s.doHttp(ctx, http.MethodHead, req)
|
||||
}
|
||||
|
||||
func (s *httpServiceImpl) Options(ctx context.Context, req *hosthttp.HttpRequest) (*hosthttp.HttpResponse, error) {
|
||||
return s.doHttp(ctx, http.MethodOptions, req)
|
||||
}
|
||||
|
||||
func (s *httpServiceImpl) doHttp(ctx context.Context, method string, req *hosthttp.HttpRequest) (*hosthttp.HttpResponse, error) {
|
||||
// Check permissions if they exist
|
||||
if s.permissions != nil {
|
||||
if err := s.permissions.IsRequestAllowed(req.Url, method); err != nil {
|
||||
log.Warn(ctx, "HTTP request blocked by permissions", "plugin", s.pluginID, "url", req.Url, "method", method, err)
|
||||
return &hosthttp.HttpResponse{Error: "Request blocked by plugin permissions: " + err.Error()}, nil
|
||||
}
|
||||
}
|
||||
client := &http.Client{
|
||||
Timeout: cmp.Or(time.Duration(req.TimeoutMs)*time.Millisecond, defaultTimeout),
|
||||
}
|
||||
|
||||
// Configure redirect policy based on permissions
|
||||
if s.permissions != nil {
|
||||
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
// Enforce maximum redirect limit
|
||||
if len(via) >= httpMaxRedirects {
|
||||
log.Warn(ctx, "HTTP redirect limit exceeded", "plugin", s.pluginID, "url", req.URL.String(), "redirectCount", len(via))
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
|
||||
// Check if redirect destination is allowed
|
||||
if err := s.permissions.IsRequestAllowed(req.URL.String(), req.Method); err != nil {
|
||||
log.Warn(ctx, "HTTP redirect blocked by permissions", "plugin", s.pluginID, "url", req.URL.String(), "method", req.Method, err)
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
|
||||
return nil // Allow redirect
|
||||
}
|
||||
}
|
||||
var body io.Reader
|
||||
if method == http.MethodPost || method == http.MethodPut || method == http.MethodPatch {
|
||||
body = bytes.NewReader(req.Body)
|
||||
}
|
||||
httpReq, err := http.NewRequestWithContext(ctx, method, req.Url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range req.Headers {
|
||||
httpReq.Header.Set(k, v)
|
||||
}
|
||||
resp, err := client.Do(httpReq)
|
||||
if err != nil {
|
||||
log.Trace(ctx, "HttpService request error", "method", method, "url", req.Url, "headers", req.Headers, err)
|
||||
return &hosthttp.HttpResponse{Error: err.Error()}, nil
|
||||
}
|
||||
log.Trace(ctx, "HttpService request", "method", method, "url", req.Url, "headers", req.Headers, "resp.status", resp.StatusCode)
|
||||
defer resp.Body.Close()
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Trace(ctx, "HttpService request error", "method", method, "url", req.Url, "headers", req.Headers, "resp.status", resp.StatusCode, err)
|
||||
return &hosthttp.HttpResponse{Error: err.Error()}, nil
|
||||
}
|
||||
headers := map[string]string{}
|
||||
for k, v := range resp.Header {
|
||||
if len(v) > 0 {
|
||||
headers[k] = v[0]
|
||||
}
|
||||
}
|
||||
return &hosthttp.HttpResponse{
|
||||
Status: int32(resp.StatusCode),
|
||||
Body: respBody,
|
||||
Headers: headers,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user