diff --git a/plugins/host/httpclient.go b/plugins/host/http.go similarity index 100% rename from plugins/host/httpclient.go rename to plugins/host/http.go diff --git a/plugins/host/httpclient_gen.go b/plugins/host/http_gen.go similarity index 100% rename from plugins/host/httpclient_gen.go rename to plugins/host/http_gen.go diff --git a/plugins/pdk/go/go.mod b/plugins/pdk/go/go.mod index 3916cd74..4d5fcddf 100644 --- a/plugins/pdk/go/go.mod +++ b/plugins/pdk/go/go.mod @@ -6,3 +6,10 @@ require ( github.com/extism/go-pdk v1.1.3 github.com/stretchr/testify v1.11.1 ) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/plugins/pdk/go/host/nd_host_httpclient.go b/plugins/pdk/go/host/nd_host_http.go similarity index 90% rename from plugins/pdk/go/host/nd_host_httpclient.go rename to plugins/pdk/go/host/nd_host_http.go index 8bd96035..d77db476 100644 --- a/plugins/pdk/go/host/nd_host_httpclient.go +++ b/plugins/pdk/go/host/nd_host_http.go @@ -14,6 +14,7 @@ import ( "github.com/navidrome/navidrome/plugins/pdk/go/pdk" ) +// HTTPRequest represents the HTTPRequest data structure. // HTTPRequest represents an outbound HTTP request from a plugin. type HTTPRequest struct { Method string `json:"method"` @@ -23,6 +24,7 @@ type HTTPRequest struct { TimeoutMs int32 `json:"timeoutMs"` } +// HTTPResponse represents the HTTPResponse data structure. // HTTPResponse represents the response from an outbound HTTP request. type HTTPResponse struct { StatusCode int32 `json:"statusCode"` @@ -35,11 +37,11 @@ type HTTPResponse struct { //go:wasmimport extism:host/user http_send func http_send(uint64) uint64 -type httpSendRequest struct { +type hTTPSendRequest struct { Request HTTPRequest `json:"request"` } -type httpSendResponse struct { +type hTTPSendResponse struct { Result *HTTPResponse `json:"result,omitempty"` Error string `json:"error,omitempty"` } @@ -55,7 +57,7 @@ type httpSendResponse struct { // Successful HTTP calls (including 4xx/5xx status codes) return a non-nil response with nil error. func HTTPSend(request HTTPRequest) (*HTTPResponse, error) { // Marshal request to JSON - req := httpSendRequest{ + req := hTTPSendRequest{ Request: request, } reqBytes, err := json.Marshal(req) @@ -73,7 +75,7 @@ func HTTPSend(request HTTPRequest) (*HTTPResponse, error) { responseBytes := responseMem.ReadBytes() // Parse the response - var response httpSendResponse + var response hTTPSendResponse if err := json.Unmarshal(responseBytes, &response); err != nil { return nil, err } diff --git a/plugins/pdk/go/host/nd_host_httpclient_stub.go b/plugins/pdk/go/host/nd_host_http_stub.go similarity index 94% rename from plugins/pdk/go/host/nd_host_httpclient_stub.go rename to plugins/pdk/go/host/nd_host_http_stub.go index 05306939..b5d1eee7 100644 --- a/plugins/pdk/go/host/nd_host_httpclient_stub.go +++ b/plugins/pdk/go/host/nd_host_http_stub.go @@ -10,6 +10,7 @@ package host import "github.com/stretchr/testify/mock" +// HTTPRequest represents the HTTPRequest data structure. // HTTPRequest represents an outbound HTTP request from a plugin. type HTTPRequest struct { Method string `json:"method"` @@ -19,6 +20,7 @@ type HTTPRequest struct { TimeoutMs int32 `json:"timeoutMs"` } +// HTTPResponse represents the HTTPResponse data structure. // HTTPResponse represents the response from an outbound HTTP request. type HTTPResponse struct { StatusCode int32 `json:"statusCode"` diff --git a/plugins/pdk/python/host/nd_host_http.py b/plugins/pdk/python/host/nd_host_http.py new file mode 100644 index 00000000..8f196f98 --- /dev/null +++ b/plugins/pdk/python/host/nd_host_http.py @@ -0,0 +1,59 @@ +# Code generated by ndpgen. DO NOT EDIT. +# +# This file contains client wrappers for the HTTP host service. +# It is intended for use in Navidrome plugins built with extism-py. +# +# IMPORTANT: Due to a limitation in extism-py, you cannot import this file directly. +# The @extism.import_fn decorators are only detected when defined in the plugin's +# main __init__.py file. Copy the needed functions from this file into your plugin. + +from dataclasses import dataclass +from typing import Any + +import extism +import json + + +class HostFunctionError(Exception): + """Raised when a host function returns an error.""" + pass + + +@extism.import_fn("extism:host/user", "http_send") +def _http_send(offset: int) -> int: + """Raw host function - do not call directly.""" + ... + + +def http_send(request: Any) -> Any: + """Send executes an HTTP request and returns the response. + +Parameters: + - request: The HTTP request to execute, including method, URL, headers, body, and timeout + +Returns the HTTP response with status code, headers, and body. +Network errors, timeouts, and permission failures are returned as Go errors. +Successful HTTP calls (including 4xx/5xx status codes) return a non-nil response with nil error. + + Args: + request: Any parameter. + + Returns: + Any: The result value. + + Raises: + HostFunctionError: If the host function returns an error. + """ + request = { + "request": request, + } + request_bytes = json.dumps(request).encode("utf-8") + request_mem = extism.memory.alloc(request_bytes) + response_offset = _http_send(request_mem.offset) + response_mem = extism.memory.find(response_offset) + response = json.loads(extism.memory.string(response_mem)) + + if response.get("error"): + raise HostFunctionError(response["error"]) + + return response.get("result", None) diff --git a/plugins/pdk/rust/nd-pdk-host/src/nd_host_http.rs b/plugins/pdk/rust/nd-pdk-host/src/nd_host_http.rs index c73241c8..b8aaad1e 100644 --- a/plugins/pdk/rust/nd-pdk-host/src/nd_host_http.rs +++ b/plugins/pdk/rust/nd-pdk-host/src/nd_host_http.rs @@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize}; /// HTTPRequest represents an outbound HTTP request from a plugin. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct HttpRequest { +pub struct HTTPRequest { pub method: String, pub url: String, #[serde(default)] @@ -23,7 +23,7 @@ pub struct HttpRequest { /// HTTPResponse represents the response from an outbound HTTP request. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct HttpResponse { +pub struct HTTPResponse { pub status_code: i32, #[serde(default)] pub headers: std::collections::HashMap, @@ -34,14 +34,14 @@ pub struct HttpResponse { #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] struct HTTPSendRequest { - request: HttpRequest, + request: HTTPRequest, } #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] struct HTTPSendResponse { #[serde(default)] - result: Option, + result: Option, #[serde(default)] error: Option, } @@ -52,23 +52,23 @@ extern "ExtismHost" { } /// Send executes an HTTP request and returns the response. -/// +/// /// Parameters: /// - request: The HTTP request to execute, including method, URL, headers, body, and timeout -/// +/// /// Returns the HTTP response with status code, headers, and body. -/// Network errors, timeouts, and permission failures are returned as errors. +/// Network errors, timeouts, and permission failures are returned as Go errors. /// Successful HTTP calls (including 4xx/5xx status codes) return a non-nil response with nil error. /// /// # Arguments -/// * `request` - HttpRequest parameter. +/// * `request` - HTTPRequest parameter. /// /// # Returns /// The result value. /// /// # Errors /// Returns an error if the host function call fails. -pub fn send(request: HttpRequest) -> Result, Error> { +pub fn send(request: HTTPRequest) -> Result, Error> { let response = unsafe { http_send(Json(HTTPSendRequest { request: request,