Compare commits
16 Commits
feat/openh
...
main
Author | SHA1 | Date | |
---|---|---|---|
11e9df2080 | |||
7a4ff9bdbd | |||
e42f04b51d | |||
|
00783e79ec | ||
|
8ca2329ddb | ||
|
c99b664f99 | ||
|
dc178c5d44 | ||
|
15a204aa25 | ||
|
e5bb4e5154 | ||
|
1270e56d5f | ||
|
51ba21d432 | ||
|
fe0b17f589 | ||
|
8e5cccc9ac | ||
|
d2cb593349 | ||
|
9278656124 | ||
|
62bd5c75d8 |
4
go.mod
4
go.mod
@ -1,5 +1,5 @@
|
|||||||
module git.cyrilix.bzh/cyrilix/goupnp
|
module git.cyrilix.bzh/cyrilix/goupnp
|
||||||
|
|
||||||
go 1.19
|
go 1.22
|
||||||
|
|
||||||
require golang.org/x/sync v0.1.0
|
require golang.org/x/sync v0.7.0
|
||||||
|
4
go.sum
4
go.sum
@ -1,2 +1,2 @@
|
|||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
6
go.work.sum
Normal file
6
go.work.sum
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
|
||||||
|
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||||
|
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||||
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
||||||
|
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
11
goupnp.go
11
goupnp.go
@ -85,7 +85,10 @@ func DiscoverDevicesCtx(ctx context.Context, searchTarget string) ([]MaybeRootDe
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer hcCleanup()
|
defer hcCleanup()
|
||||||
responses, err := ssdp.SSDPRawSearchCtx(ctx, hc, string(searchTarget), 2, 3)
|
|
||||||
|
searchCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
responses, err := ssdp.RawSearch(searchCtx, hc, string(searchTarget), 3)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -148,6 +151,10 @@ func DeviceByURL(loc *url.URL) (*RootDevice, error) {
|
|||||||
// but should not be changed after requesting clients.
|
// but should not be changed after requesting clients.
|
||||||
var CharsetReaderDefault func(charset string, input io.Reader) (io.Reader, error)
|
var CharsetReaderDefault func(charset string, input io.Reader) (io.Reader, error)
|
||||||
|
|
||||||
|
// HTTPClient specifies the http.Client object used when fetching the XML from the UPnP server.
|
||||||
|
// HTTPClient defaults the http.DefaultClient. This may be overridden by the importing application.
|
||||||
|
var HTTPClientDefault = http.DefaultClient
|
||||||
|
|
||||||
func requestXml(ctx context.Context, url string, defaultSpace string, doc interface{}) error {
|
func requestXml(ctx context.Context, url string, defaultSpace string, doc interface{}) error {
|
||||||
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@ -157,7 +164,7 @@ func requestXml(ctx context.Context, url string, defaultSpace string, doc interf
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
resp, err := HTTPClientDefault.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package httpu
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
@ -26,6 +27,27 @@ type ClientInterface interface {
|
|||||||
) ([]*http.Response, error)
|
) ([]*http.Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClientInterfaceCtx is the equivalent of ClientInterface, except with methods
|
||||||
|
// taking a context.Context parameter.
|
||||||
|
type ClientInterfaceCtx interface {
|
||||||
|
// DoWithContext performs a request. If the input request has a
|
||||||
|
// deadline, then that value will be used as the timeout for how long
|
||||||
|
// to wait before returning the responses that were received. If the
|
||||||
|
// request's context is canceled, this method will return immediately.
|
||||||
|
//
|
||||||
|
// If the request's context is never canceled, and does not have a
|
||||||
|
// deadline, then this function WILL NEVER RETURN. You MUST set an
|
||||||
|
// appropriate deadline on the context, or otherwise cancel it when you
|
||||||
|
// want to finish an operation.
|
||||||
|
//
|
||||||
|
// An error is only returned for failing to send the request. Failures
|
||||||
|
// in receipt simply do not add to the resulting responses.
|
||||||
|
DoWithContext(
|
||||||
|
req *http.Request,
|
||||||
|
numSends int,
|
||||||
|
) ([]*http.Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
// HTTPUClient is a client for dealing with HTTPU (HTTP over UDP). Its typical
|
// HTTPUClient is a client for dealing with HTTPU (HTTP over UDP). Its typical
|
||||||
// function is for HTTPMU, and particularly SSDP.
|
// function is for HTTPMU, and particularly SSDP.
|
||||||
type HTTPUClient struct {
|
type HTTPUClient struct {
|
||||||
@ -34,6 +56,7 @@ type HTTPUClient struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var _ ClientInterface = &HTTPUClient{}
|
var _ ClientInterface = &HTTPUClient{}
|
||||||
|
var _ ClientInterfaceCtx = &HTTPUClient{}
|
||||||
|
|
||||||
// NewHTTPUClient creates a new HTTPUClient, opening up a new UDP socket for the
|
// NewHTTPUClient creates a new HTTPUClient, opening up a new UDP socket for the
|
||||||
// purpose.
|
// purpose.
|
||||||
@ -75,6 +98,25 @@ func (httpu *HTTPUClient) Do(
|
|||||||
req *http.Request,
|
req *http.Request,
|
||||||
timeout time.Duration,
|
timeout time.Duration,
|
||||||
numSends int,
|
numSends int,
|
||||||
|
) ([]*http.Response, error) {
|
||||||
|
ctx := req.Context()
|
||||||
|
if timeout > 0 {
|
||||||
|
var cancel func()
|
||||||
|
ctx, cancel = context.WithTimeout(ctx, timeout)
|
||||||
|
defer cancel()
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpu.DoWithContext(req, numSends)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DoWithContext implements ClientInterfaceCtx.DoWithContext.
|
||||||
|
//
|
||||||
|
// Make sure to read the documentation on the ClientInterfaceCtx interface
|
||||||
|
// regarding cancellation!
|
||||||
|
func (httpu *HTTPUClient) DoWithContext(
|
||||||
|
req *http.Request,
|
||||||
|
numSends int,
|
||||||
) ([]*http.Response, error) {
|
) ([]*http.Response, error) {
|
||||||
httpu.connLock.Lock()
|
httpu.connLock.Lock()
|
||||||
defer httpu.connLock.Unlock()
|
defer httpu.connLock.Unlock()
|
||||||
@ -101,9 +143,27 @@ func (httpu *HTTPUClient) Do(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err = httpu.conn.SetDeadline(time.Now().Add(timeout)); err != nil {
|
|
||||||
|
// Handle context deadline/timeout
|
||||||
|
ctx := req.Context()
|
||||||
|
deadline, ok := ctx.Deadline()
|
||||||
|
if ok {
|
||||||
|
if err = httpu.conn.SetDeadline(deadline); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle context cancelation
|
||||||
|
done := make(chan struct{})
|
||||||
|
defer close(done)
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
// if context is cancelled, stop any connections by setting time in the past.
|
||||||
|
httpu.conn.SetDeadline(time.Now().Add(-time.Second))
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Send request.
|
// Send request.
|
||||||
for i := 0; i < numSends; i++ {
|
for i := 0; i < numSends; i++ {
|
||||||
|
@ -49,7 +49,7 @@ func (mc *MultiClient) Do(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mc *MultiClient) sendRequests(
|
func (mc *MultiClient) sendRequests(
|
||||||
results chan<-[]*http.Response,
|
results chan<- []*http.Response,
|
||||||
req *http.Request,
|
req *http.Request,
|
||||||
timeout time.Duration,
|
timeout time.Duration,
|
||||||
numSends int,
|
numSends int,
|
||||||
@ -68,3 +68,65 @@ func (mc *MultiClient) sendRequests(
|
|||||||
}
|
}
|
||||||
return tasks.Wait()
|
return tasks.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MultiClientCtx dispatches requests out to all the delegated clients.
|
||||||
|
type MultiClientCtx struct {
|
||||||
|
// The HTTPU clients to delegate to.
|
||||||
|
delegates []ClientInterfaceCtx
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ClientInterfaceCtx = &MultiClientCtx{}
|
||||||
|
|
||||||
|
// NewMultiClient creates a new MultiClient that delegates to all the given
|
||||||
|
// clients.
|
||||||
|
func NewMultiClientCtx(delegates []ClientInterfaceCtx) *MultiClientCtx {
|
||||||
|
return &MultiClientCtx{
|
||||||
|
delegates: delegates,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DoWithContext implements ClientInterfaceCtx.DoWithContext.
|
||||||
|
func (mc *MultiClientCtx) DoWithContext(
|
||||||
|
req *http.Request,
|
||||||
|
numSends int,
|
||||||
|
) ([]*http.Response, error) {
|
||||||
|
tasks, ctx := errgroup.WithContext(req.Context())
|
||||||
|
req = req.WithContext(ctx) // so we cancel if the errgroup errors
|
||||||
|
results := make(chan []*http.Response)
|
||||||
|
|
||||||
|
// For each client, send the request to it and collect results.
|
||||||
|
tasks.Go(func() error {
|
||||||
|
defer close(results)
|
||||||
|
return mc.sendRequestsCtx(results, req, numSends)
|
||||||
|
})
|
||||||
|
|
||||||
|
var responses []*http.Response
|
||||||
|
tasks.Go(func() error {
|
||||||
|
for rs := range results {
|
||||||
|
responses = append(responses, rs...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return responses, tasks.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *MultiClientCtx) sendRequestsCtx(
|
||||||
|
results chan<- []*http.Response,
|
||||||
|
req *http.Request,
|
||||||
|
numSends int,
|
||||||
|
) error {
|
||||||
|
tasks := &errgroup.Group{}
|
||||||
|
for _, d := range mc.delegates {
|
||||||
|
d := d // copy for closure
|
||||||
|
tasks.Go(func() error {
|
||||||
|
responses, err := d.DoWithContext(req, numSends)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
results <- responses
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return tasks.Wait()
|
||||||
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -73,20 +74,25 @@ func (srv *Server) Serve(l net.PacketConn) error {
|
|||||||
if srv.MaxMessageBytes != 0 {
|
if srv.MaxMessageBytes != 0 {
|
||||||
maxMessageBytes = srv.MaxMessageBytes
|
maxMessageBytes = srv.MaxMessageBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bufPool := &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return make([]byte, maxMessageBytes)
|
||||||
|
},
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
buf := make([]byte, maxMessageBytes)
|
buf := bufPool.Get().([]byte)
|
||||||
n, peerAddr, err := l.ReadFrom(buf)
|
n, peerAddr, err := l.ReadFrom(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
buf = buf[:n]
|
go func() {
|
||||||
|
defer bufPool.Put(buf)
|
||||||
go func(buf []byte, peerAddr net.Addr) {
|
|
||||||
// At least one router's UPnP implementation has added a trailing space
|
// At least one router's UPnP implementation has added a trailing space
|
||||||
// after "HTTP/1.1" - trim it.
|
// after "HTTP/1.1" - trim it.
|
||||||
buf = trailingWhitespaceRx.ReplaceAllLiteral(buf, crlf)
|
reqBuf := trailingWhitespaceRx.ReplaceAllLiteral(buf[:n], crlf)
|
||||||
|
|
||||||
req, err := http.ReadRequest(bufio.NewReader(bytes.NewBuffer(buf)))
|
req, err := http.ReadRequest(bufio.NewReader(bytes.NewBuffer(reqBuf)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("httpu: Failed to parse request: %v", err)
|
log.Printf("httpu: Failed to parse request: %v", err)
|
||||||
return
|
return
|
||||||
@ -94,7 +100,7 @@ func (srv *Server) Serve(l net.PacketConn) error {
|
|||||||
req.RemoteAddr = peerAddr.String()
|
req.RemoteAddr = peerAddr.String()
|
||||||
srv.Handler.ServeMessage(req)
|
srv.Handler.ServeMessage(req)
|
||||||
// No need to call req.Body.Close - underlying reader is bytes.Buffer.
|
// No need to call req.Body.Close - underlying reader is bytes.Buffer.
|
||||||
}(buf, peerAddr)
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,14 +10,14 @@ import (
|
|||||||
// httpuClient creates a HTTPU client that multiplexes to all multicast-capable
|
// httpuClient creates a HTTPU client that multiplexes to all multicast-capable
|
||||||
// IPv4 addresses on the host. Returns a function to clean up once the client is
|
// IPv4 addresses on the host. Returns a function to clean up once the client is
|
||||||
// no longer required.
|
// no longer required.
|
||||||
func httpuClient() (httpu.ClientInterface, func(), error) {
|
func httpuClient() (httpu.ClientInterfaceCtx, func(), error) {
|
||||||
addrs, err := localIPv4MCastAddrs()
|
addrs, err := localIPv4MCastAddrs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, ctxError(err, "requesting host IPv4 addresses")
|
return nil, nil, ctxError(err, "requesting host IPv4 addresses")
|
||||||
}
|
}
|
||||||
|
|
||||||
closers := make([]io.Closer, 0, len(addrs))
|
closers := make([]io.Closer, 0, len(addrs))
|
||||||
delegates := make([]httpu.ClientInterface, 0, len(addrs))
|
delegates := make([]httpu.ClientInterfaceCtx, 0, len(addrs))
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
c, err := httpu.NewHTTPUClientAddr(addr)
|
c, err := httpu.NewHTTPUClientAddr(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -34,7 +34,7 @@ func httpuClient() (httpu.ClientInterface, func(), error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return httpu.NewMultiClient(delegates), closer, nil
|
return httpu.NewMultiClientCtx(delegates), closer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// localIPv2MCastAddrs returns the set of IPv4 addresses on multicast-able
|
// localIPv2MCastAddrs returns the set of IPv4 addresses on multicast-able
|
||||||
|
@ -194,9 +194,13 @@ type soapBody struct {
|
|||||||
|
|
||||||
// SOAPFaultError implements error, and contains SOAP fault information.
|
// SOAPFaultError implements error, and contains SOAP fault information.
|
||||||
type SOAPFaultError struct {
|
type SOAPFaultError struct {
|
||||||
FaultCode string `xml:"faultCode"`
|
FaultCode string `xml:"faultcode"`
|
||||||
FaultString string `xml:"faultString"`
|
FaultString string `xml:"faultstring"`
|
||||||
Detail struct {
|
Detail struct {
|
||||||
|
UPnPError struct {
|
||||||
|
Errorcode int `xml:"errorCode"`
|
||||||
|
ErrorDescription string `xml:"errorDescription"`
|
||||||
|
} `xml:"UPnPError"`
|
||||||
Raw []byte `xml:",innerxml"`
|
Raw []byte `xml:",innerxml"`
|
||||||
} `xml:"detail"`
|
} `xml:"detail"`
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,12 @@ package soap
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -87,6 +89,75 @@ func TestActionInputs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUPnPError(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
url, err := url.Parse("http://example.com/soap")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
body := `
|
||||||
|
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
||||||
|
<s:Body>
|
||||||
|
<s:Fault>
|
||||||
|
<faultcode>s:Client</faultcode>
|
||||||
|
<faultstring>UPnPError</faultstring>
|
||||||
|
<detail>
|
||||||
|
<UPnPError xmlns="urn:schemas-upnp-org:control-1-0">
|
||||||
|
<errorCode>725</errorCode>
|
||||||
|
<errorDescription>OnlyPermanentLeasesSupported</errorDescription>
|
||||||
|
</UPnPError>
|
||||||
|
</detail>
|
||||||
|
</s:Fault>
|
||||||
|
</s:Body>
|
||||||
|
</s:Envelope>`
|
||||||
|
rt := &capturingRoundTripper{
|
||||||
|
resp: &http.Response{
|
||||||
|
StatusCode: 500,
|
||||||
|
ContentLength: int64(len(body)),
|
||||||
|
Body: ioutil.NopCloser(bytes.NewBufferString(body)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
client := SOAPClient{
|
||||||
|
EndpointURL: *url,
|
||||||
|
HTTPClient: http.Client{
|
||||||
|
Transport: rt,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.PerformAction("mynamespace", "myaction", nil, nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error, got nil")
|
||||||
|
}
|
||||||
|
if testing.Verbose() {
|
||||||
|
t.Logf("%+v\n", err)
|
||||||
|
}
|
||||||
|
soapErr := &SOAPFaultError{}
|
||||||
|
if ok := errors.As(err, &soapErr); !ok {
|
||||||
|
t.Fatal("expected *SOAPFaultError")
|
||||||
|
}
|
||||||
|
if soapErr.FaultCode != "s:Client" {
|
||||||
|
t.Fatalf("unexpected FaultCode: %s", soapErr.FaultCode)
|
||||||
|
}
|
||||||
|
if soapErr.FaultString != "UPnPError" {
|
||||||
|
t.Fatalf("unexpected FaultString: %s", soapErr.FaultString)
|
||||||
|
}
|
||||||
|
if soapErr.Detail.UPnPError.Errorcode != 725 {
|
||||||
|
t.Fatalf("unexpected UPnPError Errorcode: %d", soapErr.Detail.UPnPError.Errorcode)
|
||||||
|
}
|
||||||
|
if soapErr.Detail.UPnPError.ErrorDescription != "OnlyPermanentLeasesSupported" {
|
||||||
|
t.Fatalf("unexpected UPnPError ErrorDescription: %s",
|
||||||
|
soapErr.Detail.UPnPError.ErrorDescription)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.EqualFold(string(soapErr.Detail.Raw), `
|
||||||
|
<UPnPError xmlns="urn:schemas-upnp-org:control-1-0">
|
||||||
|
<errorCode>725</errorCode>
|
||||||
|
<errorDescription>OnlyPermanentLeasesSupported</errorDescription>
|
||||||
|
</UPnPError>
|
||||||
|
`) {
|
||||||
|
t.Fatalf("unexpected Detail.Raw, got:\n%s", string(soapErr.Detail.Raw))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestEscapeXMLText(t *testing.T) {
|
func TestEscapeXMLText(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
77
ssdp/ssdp.go
77
ssdp/ssdp.go
@ -35,6 +35,15 @@ type HTTPUClient interface {
|
|||||||
) ([]*http.Response, error)
|
) ([]*http.Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTPUClientCtx is an optional interface that will be used to perform
|
||||||
|
// HTTP-over-UDP requests if the client implements it.
|
||||||
|
type HTTPUClientCtx interface {
|
||||||
|
DoWithContext(
|
||||||
|
req *http.Request,
|
||||||
|
numSends int,
|
||||||
|
) ([]*http.Response, error)
|
||||||
|
}
|
||||||
|
|
||||||
// SSDPRawSearchCtx performs a fairly raw SSDP search request, and returns the
|
// SSDPRawSearchCtx performs a fairly raw SSDP search request, and returns the
|
||||||
// unique response(s) that it receives. Each response has the requested
|
// unique response(s) that it receives. Each response has the requested
|
||||||
// searchTarget, a USN, and a valid location. maxWaitSeconds states how long to
|
// searchTarget, a USN, and a valid location. maxWaitSeconds states how long to
|
||||||
@ -49,8 +58,64 @@ func SSDPRawSearchCtx(
|
|||||||
maxWaitSeconds int,
|
maxWaitSeconds int,
|
||||||
numSends int,
|
numSends int,
|
||||||
) ([]*http.Response, error) {
|
) ([]*http.Response, error) {
|
||||||
|
req, err := prepareRequest(ctx, searchTarget, maxWaitSeconds)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allResponses, err := httpu.Do(req, time.Duration(maxWaitSeconds)*time.Second+100*time.Millisecond, numSends)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return processSSDPResponses(searchTarget, allResponses)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawSearch performs a fairly raw SSDP search request, and returns the
|
||||||
|
// unique response(s) that it receives. Each response has the requested
|
||||||
|
// searchTarget, a USN, and a valid location. If the provided context times out
|
||||||
|
// or is canceled, the search will be aborted. numSends is the number of
|
||||||
|
// requests to send - 3 is a reasonable value for this.
|
||||||
|
//
|
||||||
|
// The provided context should have a deadline, since the SSDP protocol
|
||||||
|
// requires the max wait time be included in search requests. If the context
|
||||||
|
// has no deadline, then a default deadline of 3 seconds will be applied.
|
||||||
|
func RawSearch(
|
||||||
|
ctx context.Context,
|
||||||
|
httpu HTTPUClientCtx,
|
||||||
|
searchTarget string,
|
||||||
|
numSends int,
|
||||||
|
) ([]*http.Response, error) {
|
||||||
|
// We need a timeout value to include in the SSDP request; get it by
|
||||||
|
// checking the deadline on the context.
|
||||||
|
var maxWaitSeconds int
|
||||||
|
if deadline, ok := ctx.Deadline(); ok {
|
||||||
|
maxWaitSeconds = int(deadline.Sub(time.Now()) / time.Second)
|
||||||
|
} else {
|
||||||
|
// Pick a default timeout of 3 seconds if none was provided.
|
||||||
|
maxWaitSeconds = 3
|
||||||
|
|
||||||
|
var cancel func()
|
||||||
|
ctx, cancel = context.WithTimeout(ctx, time.Duration(maxWaitSeconds)*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := prepareRequest(ctx, searchTarget, maxWaitSeconds)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
allResponses, err := httpu.DoWithContext(req, numSends)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return processSSDPResponses(searchTarget, allResponses)
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareRequest checks the provided parameters and constructs a SSDP search
|
||||||
|
// request to be sent.
|
||||||
|
func prepareRequest(ctx context.Context, searchTarget string, maxWaitSeconds int) (*http.Request, error) {
|
||||||
if maxWaitSeconds < 1 {
|
if maxWaitSeconds < 1 {
|
||||||
return nil, errors.New("ssdp: maxWaitSeconds must be >= 1")
|
return nil, errors.New("ssdp: request timeout must be at least 1s")
|
||||||
}
|
}
|
||||||
|
|
||||||
req := (&http.Request{
|
req := (&http.Request{
|
||||||
@ -67,11 +132,13 @@ func SSDPRawSearchCtx(
|
|||||||
"ST": []string{searchTarget},
|
"ST": []string{searchTarget},
|
||||||
},
|
},
|
||||||
}).WithContext(ctx)
|
}).WithContext(ctx)
|
||||||
allResponses, err := httpu.Do(req, time.Duration(maxWaitSeconds)*time.Second+100*time.Millisecond, numSends)
|
return req, nil
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
|
func processSSDPResponses(
|
||||||
|
searchTarget string,
|
||||||
|
allResponses []*http.Response,
|
||||||
|
) ([]*http.Response, error) {
|
||||||
isExactSearch := searchTarget != SSDPAll && searchTarget != UPNPRootDevice
|
isExactSearch := searchTarget != SSDPAll && searchTarget != UPNPRootDevice
|
||||||
|
|
||||||
seenIDs := make(map[string]bool)
|
seenIDs := make(map[string]bool)
|
||||||
|
@ -23,7 +23,9 @@ import (
|
|||||||
"github.com/huin/goupnp/v2alpha/description/typedesc"
|
"github.com/huin/goupnp/v2alpha/description/typedesc"
|
||||||
"github.com/huin/goupnp/v2alpha/description/xmlsrvdesc"
|
"github.com/huin/goupnp/v2alpha/description/xmlsrvdesc"
|
||||||
"github.com/huin/goupnp/v2alpha/soap"
|
"github.com/huin/goupnp/v2alpha/soap"
|
||||||
"github.com/huin/goupnp/v2alpha/soap/types"
|
"golang.org/x/exp/maps"
|
||||||
|
|
||||||
|
soaptypes "github.com/huin/goupnp/v2alpha/soap/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -31,7 +33,9 @@ var (
|
|||||||
outputDir = flag.String("output_dir", "", "Path to directory to write output in.")
|
outputDir = flag.String("output_dir", "", "Path to directory to write output in.")
|
||||||
srvManifests = flag.String("srv_manifests", "", "Path to srvmanifests.toml")
|
srvManifests = flag.String("srv_manifests", "", "Path to srvmanifests.toml")
|
||||||
srvTemplate = flag.String("srv_template", "", "Path to srv.gotemplate.")
|
srvTemplate = flag.String("srv_template", "", "Path to srv.gotemplate.")
|
||||||
upnpresourcesZip = flag.String("upnpresources_zip", "", "Path to upnpresources.zip.")
|
upnpresourcesZip = flag.String("upnpresources_zip", "",
|
||||||
|
"Path to upnpresources.zip, downloaded from "+
|
||||||
|
"https://openconnectivity.org/upnp-specs/upnpresources.zip.")
|
||||||
)
|
)
|
||||||
|
|
||||||
const soapActionInterface = "SOAPActionInterface"
|
const soapActionInterface = "SOAPActionInterface"
|
||||||
@ -50,14 +54,14 @@ func run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if *outputDir == "" {
|
if *outputDir == "" {
|
||||||
return errors.New("-output_dir is a required flag.")
|
return errors.New("-output_dir is a required flag")
|
||||||
}
|
}
|
||||||
if err := os.MkdirAll(*outputDir, 0); err != nil {
|
if err := os.MkdirAll(*outputDir, 0); err != nil {
|
||||||
return fmt.Errorf("creating output_dir %q: %w", *outputDir, err)
|
return fmt.Errorf("creating output_dir %q: %w", *outputDir, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *srvManifests == "" {
|
if *srvManifests == "" {
|
||||||
return errors.New("-srv_manifests is a required flag.")
|
return errors.New("-srv_manifests is a required flag")
|
||||||
}
|
}
|
||||||
var manifests DCPSpecManifests
|
var manifests DCPSpecManifests
|
||||||
_, err := toml.DecodeFile(*srvManifests, &manifests)
|
_, err := toml.DecodeFile(*srvManifests, &manifests)
|
||||||
@ -66,7 +70,7 @@ func run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if *srvTemplate == "" {
|
if *srvTemplate == "" {
|
||||||
return errors.New("-srv_template is a required flag.")
|
return errors.New("-srv_template is a required flag")
|
||||||
}
|
}
|
||||||
tmpl, err := template.New(filepath.Base(*srvTemplate)).Funcs(template.FuncMap{
|
tmpl, err := template.New(filepath.Base(*srvTemplate)).Funcs(template.FuncMap{
|
||||||
"args": tmplfuncs.Args,
|
"args": tmplfuncs.Args,
|
||||||
@ -77,7 +81,7 @@ func run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if *upnpresourcesZip == "" {
|
if *upnpresourcesZip == "" {
|
||||||
return errors.New("-upnpresources_zip is a required flag.")
|
return errors.New("-upnpresources_zip is a required flag")
|
||||||
}
|
}
|
||||||
f, err := os.Open(*upnpresourcesZip)
|
f, err := os.Open(*upnpresourcesZip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -91,7 +95,7 @@ func run() error {
|
|||||||
|
|
||||||
// Use default type map for now. Addtional types could be use instead or
|
// Use default type map for now. Addtional types could be use instead or
|
||||||
// as well as necessary for extended types.
|
// as well as necessary for extended types.
|
||||||
typeMap := types.TypeMap().Clone()
|
typeMap := soaptypes.TypeMap().Clone()
|
||||||
typeMap[soapActionInterface] = typedesc.TypeDesc{
|
typeMap[soapActionInterface] = typedesc.TypeDesc{
|
||||||
GoType: reflect.TypeOf((*soap.Action)(nil)).Elem(),
|
GoType: reflect.TypeOf((*soap.Action)(nil)).Elem(),
|
||||||
}
|
}
|
||||||
@ -158,7 +162,8 @@ func processService(
|
|||||||
return fmt.Errorf("transforming service description: %w", err)
|
return fmt.Errorf("transforming service description: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
imps, err := accumulateImports(sd, typeMap)
|
imps := newImports()
|
||||||
|
types, err := accumulateTypes(sd, typeMap, imps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -167,6 +172,7 @@ func processService(
|
|||||||
err = tmpl.ExecuteTemplate(buf, "service", tmplArgs{
|
err = tmpl.ExecuteTemplate(buf, "service", tmplArgs{
|
||||||
Manifest: srvManifest,
|
Manifest: srvManifest,
|
||||||
Imps: imps,
|
Imps: imps,
|
||||||
|
Types: types,
|
||||||
SCPD: sd,
|
SCPD: sd,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -219,14 +225,44 @@ type ServiceManifest struct {
|
|||||||
type tmplArgs struct {
|
type tmplArgs struct {
|
||||||
Manifest *ServiceManifest
|
Manifest *ServiceManifest
|
||||||
Imps *imports
|
Imps *imports
|
||||||
|
Types *types
|
||||||
SCPD *srvdesc.SCPD
|
SCPD *srvdesc.SCPD
|
||||||
}
|
}
|
||||||
|
|
||||||
type imports struct {
|
type imports struct {
|
||||||
// Maps from a type name like "ui4" to the `alias.name` for the import.
|
|
||||||
TypeByName map[string]typeDesc
|
|
||||||
// Each required import line, ordered by path.
|
// Each required import line, ordered by path.
|
||||||
ImportLines []importItem
|
ImportLines []importItem
|
||||||
|
// aliasByPath maps from import path to its imported alias.
|
||||||
|
aliasByPath map[string]string
|
||||||
|
// nextAlias is the number for the next import alias.
|
||||||
|
nextAlias int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newImports() *imports {
|
||||||
|
return &imports{
|
||||||
|
aliasByPath: make(map[string]string),
|
||||||
|
nextAlias: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (imps *imports) getAliasForPath(path string) string {
|
||||||
|
if alias, ok := imps.aliasByPath[path]; ok {
|
||||||
|
return alias
|
||||||
|
}
|
||||||
|
alias := fmt.Sprintf("pkg%d", imps.nextAlias)
|
||||||
|
imps.nextAlias++
|
||||||
|
imps.ImportLines = append(imps.ImportLines, importItem{
|
||||||
|
Alias: alias,
|
||||||
|
Path: path,
|
||||||
|
})
|
||||||
|
imps.aliasByPath[path] = alias
|
||||||
|
return alias
|
||||||
|
}
|
||||||
|
|
||||||
|
type types struct {
|
||||||
|
// Maps from a type name like "ui4" to the `alias.name` for the import.
|
||||||
|
TypeByName map[string]typeDesc
|
||||||
|
StringVarDefs []stringVarDef
|
||||||
}
|
}
|
||||||
|
|
||||||
type typeDesc struct {
|
type typeDesc struct {
|
||||||
@ -239,17 +275,41 @@ type typeDesc struct {
|
|||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type stringVarDef struct {
|
||||||
|
Name string
|
||||||
|
AllowedValues []string
|
||||||
|
}
|
||||||
|
|
||||||
type importItem struct {
|
type importItem struct {
|
||||||
Alias string
|
Alias string
|
||||||
Path string
|
Path string
|
||||||
}
|
}
|
||||||
|
|
||||||
func accumulateImports(srvDesc *srvdesc.SCPD, typeMap typedesc.TypeMap) (*imports, error) {
|
// accumulateTypes creates type information, and adds any required imports for
|
||||||
typeNames := make(map[string]bool)
|
// them.
|
||||||
typeNames[soapActionInterface] = true
|
func accumulateTypes(
|
||||||
|
srvDesc *srvdesc.SCPD,
|
||||||
|
typeMap typedesc.TypeMap,
|
||||||
|
imps *imports,
|
||||||
|
) (*types, error) {
|
||||||
|
typeNames := make(map[string]struct{})
|
||||||
|
typeNames[soapActionInterface] = struct{}{}
|
||||||
|
|
||||||
err := visitTypesSCPD(srvDesc, func(typeName string) {
|
var stringVarDefs []stringVarDef
|
||||||
typeNames[typeName] = true
|
sortedVarNames := maps.Keys(srvDesc.VariableByName)
|
||||||
|
sort.Strings(sortedVarNames)
|
||||||
|
for _, svName := range sortedVarNames {
|
||||||
|
sv := srvDesc.VariableByName[svName]
|
||||||
|
if sv.DataType == "string" && len(sv.AllowedValues) > 0 {
|
||||||
|
stringVarDefs = append(stringVarDefs, stringVarDef{
|
||||||
|
Name: svName,
|
||||||
|
AllowedValues: srvDesc.VariableByName[svName].AllowedValues,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := visitTypesSCPD(srvDesc, func(sv *srvdesc.StateVariable) {
|
||||||
|
typeNames[sv.DataType] = struct{}{}
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -257,7 +317,7 @@ func accumulateImports(srvDesc *srvdesc.SCPD, typeMap typedesc.TypeMap) (*import
|
|||||||
|
|
||||||
// Have sorted list of import package paths. Partly for aesthetics of generated code, but also
|
// Have sorted list of import package paths. Partly for aesthetics of generated code, but also
|
||||||
// to have stable-generated aliases.
|
// to have stable-generated aliases.
|
||||||
paths := make(map[string]bool)
|
paths := make(map[string]struct{})
|
||||||
for typeName := range typeNames {
|
for typeName := range typeNames {
|
||||||
t, ok := typeMap[typeName]
|
t, ok := typeMap[typeName]
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -265,29 +325,17 @@ func accumulateImports(srvDesc *srvdesc.SCPD, typeMap typedesc.TypeMap) (*import
|
|||||||
}
|
}
|
||||||
pkgPath := t.GoType.PkgPath()
|
pkgPath := t.GoType.PkgPath()
|
||||||
if pkgPath == "" {
|
if pkgPath == "" {
|
||||||
// Builtin type, ignore.
|
// Builtin type, no import needed.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
paths[pkgPath] = true
|
paths[pkgPath] = struct{}{}
|
||||||
}
|
|
||||||
sortedPaths := make([]string, 0, len(paths))
|
|
||||||
for path := range paths {
|
|
||||||
sortedPaths = append(sortedPaths, path)
|
|
||||||
}
|
}
|
||||||
|
sortedPaths := maps.Keys(paths)
|
||||||
sort.Strings(sortedPaths)
|
sort.Strings(sortedPaths)
|
||||||
|
|
||||||
// Generate import aliases.
|
// Generate import aliases in deterministic order.
|
||||||
index := 1
|
|
||||||
aliasByPath := make(map[string]string, len(paths))
|
|
||||||
importLines := make([]importItem, 0, len(paths))
|
|
||||||
for _, path := range sortedPaths {
|
for _, path := range sortedPaths {
|
||||||
alias := fmt.Sprintf("pkg%d", index)
|
imps.getAliasForPath(path)
|
||||||
index++
|
|
||||||
importLines = append(importLines, importItem{
|
|
||||||
Alias: alias,
|
|
||||||
Path: path,
|
|
||||||
})
|
|
||||||
aliasByPath[path] = alias
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate typeByName.
|
// Populate typeByName.
|
||||||
@ -295,28 +343,27 @@ func accumulateImports(srvDesc *srvdesc.SCPD, typeMap typedesc.TypeMap) (*import
|
|||||||
for typeName := range typeNames {
|
for typeName := range typeNames {
|
||||||
goType := typeMap[typeName]
|
goType := typeMap[typeName]
|
||||||
pkgPath := goType.GoType.PkgPath()
|
pkgPath := goType.GoType.PkgPath()
|
||||||
alias := aliasByPath[pkgPath]
|
|
||||||
td := typeDesc{
|
td := typeDesc{
|
||||||
Name: goType.GoType.Name(),
|
Name: goType.GoType.Name(),
|
||||||
}
|
}
|
||||||
if alias == "" {
|
if pkgPath == "" {
|
||||||
// Builtin type.
|
// Builtin type.
|
||||||
td.AbsRef = td.Name
|
td.AbsRef = td.Name
|
||||||
td.Ref = td.Name
|
td.Ref = td.Name
|
||||||
} else {
|
} else {
|
||||||
td.AbsRef = strconv.Quote(pkgPath) + "." + td.Name
|
td.AbsRef = strconv.Quote(pkgPath) + "." + td.Name
|
||||||
td.Ref = alias + "." + td.Name
|
td.Ref = imps.getAliasForPath(pkgPath) + "." + td.Name
|
||||||
}
|
}
|
||||||
typeByName[typeName] = td
|
typeByName[typeName] = td
|
||||||
}
|
}
|
||||||
|
|
||||||
return &imports{
|
return &types{
|
||||||
TypeByName: typeByName,
|
TypeByName: typeByName,
|
||||||
ImportLines: importLines,
|
StringVarDefs: stringVarDefs,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type typeVisitor func(typeName string)
|
type typeVisitor func(sv *srvdesc.StateVariable)
|
||||||
|
|
||||||
// visitTypesSCPD calls `visitor` with each data type name (e.g. "ui4") referenced
|
// visitTypesSCPD calls `visitor` with each data type name (e.g. "ui4") referenced
|
||||||
// by action arguments.`
|
// by action arguments.`
|
||||||
@ -335,14 +382,14 @@ func visitTypesAction(action *srvdesc.Action, visitor typeVisitor) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
visitor(sv.DataType)
|
visitor(sv)
|
||||||
}
|
}
|
||||||
for _, arg := range action.OutArgs {
|
for _, arg := range action.OutArgs {
|
||||||
sv, err := arg.RelatedStateVariable()
|
sv, err := arg.RelatedStateVariable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
visitor(sv.DataType)
|
visitor(sv)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
BadDescriptionError = errors.New("bad XML description")
|
ErrBadDescription = errors.New("bad XML description")
|
||||||
MissingDefinitionError = errors.New("missing definition")
|
ErrMissingDefinition = errors.New("missing definition")
|
||||||
UnsupportedDescriptionError = errors.New("unsupported XML description")
|
ErrUnsupportedDescription = errors.New("unsupported XML description")
|
||||||
)
|
)
|
||||||
|
|
||||||
// SCPD is the top level service description.
|
// SCPD is the top level service description.
|
||||||
@ -37,7 +37,7 @@ func FromXML(xmlDesc *xmlsrvdesc.SCPD) (*SCPD, error) {
|
|||||||
}
|
}
|
||||||
if _, exists := stateVariables[sv.Name]; exists {
|
if _, exists := stateVariables[sv.Name]; exists {
|
||||||
return nil, fmt.Errorf("%w: multiple state variables with name %q",
|
return nil, fmt.Errorf("%w: multiple state variables with name %q",
|
||||||
BadDescriptionError, sv.Name)
|
ErrBadDescription, sv.Name)
|
||||||
}
|
}
|
||||||
stateVariables[sv.Name] = sv
|
stateVariables[sv.Name] = sv
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@ func FromXML(xmlDesc *xmlsrvdesc.SCPD) (*SCPD, error) {
|
|||||||
}
|
}
|
||||||
if _, exists := actions[action.Name]; exists {
|
if _, exists := actions[action.Name]; exists {
|
||||||
return nil, fmt.Errorf("%w: multiple actions with name %q",
|
return nil, fmt.Errorf("%w: multiple actions with name %q",
|
||||||
BadDescriptionError, action.Name)
|
ErrBadDescription, action.Name)
|
||||||
}
|
}
|
||||||
actions[action.Name] = action
|
actions[action.Name] = action
|
||||||
}
|
}
|
||||||
@ -79,7 +79,7 @@ type Action struct {
|
|||||||
// actionFromXML creates an Action from the given XML description.
|
// actionFromXML creates an Action from the given XML description.
|
||||||
func actionFromXML(xmlAction *xmlsrvdesc.Action, scpd *SCPD) (*Action, error) {
|
func actionFromXML(xmlAction *xmlsrvdesc.Action, scpd *SCPD) (*Action, error) {
|
||||||
if xmlAction.Name == "" {
|
if xmlAction.Name == "" {
|
||||||
return nil, fmt.Errorf("%w: empty action name", BadDescriptionError)
|
return nil, fmt.Errorf("%w: empty action name", ErrBadDescription)
|
||||||
}
|
}
|
||||||
action := &Action{
|
action := &Action{
|
||||||
SCPD: scpd,
|
SCPD: scpd,
|
||||||
@ -99,7 +99,7 @@ func actionFromXML(xmlAction *xmlsrvdesc.Action, scpd *SCPD) (*Action, error) {
|
|||||||
outArgs = append(outArgs, arg)
|
outArgs = append(outArgs, arg)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("%w: argument %q has invalid direction %q",
|
return nil, fmt.Errorf("%w: argument %q has invalid direction %q",
|
||||||
BadDescriptionError, xmlArg.Name, xmlArg.Direction)
|
ErrBadDescription, xmlArg.Name, xmlArg.Direction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
action.InArgs = inArgs
|
action.InArgs = inArgs
|
||||||
@ -117,10 +117,10 @@ type Argument struct {
|
|||||||
// argumentFromXML creates an Argument from the XML description.
|
// argumentFromXML creates an Argument from the XML description.
|
||||||
func argumentFromXML(xmlArg *xmlsrvdesc.Argument, action *Action) (*Argument, error) {
|
func argumentFromXML(xmlArg *xmlsrvdesc.Argument, action *Action) (*Argument, error) {
|
||||||
if xmlArg.Name == "" {
|
if xmlArg.Name == "" {
|
||||||
return nil, fmt.Errorf("%w: empty argument name", BadDescriptionError)
|
return nil, fmt.Errorf("%w: empty argument name", ErrBadDescription)
|
||||||
}
|
}
|
||||||
if xmlArg.RelatedStateVariable == "" {
|
if xmlArg.RelatedStateVariable == "" {
|
||||||
return nil, fmt.Errorf("%w: empty related state variable", BadDescriptionError)
|
return nil, fmt.Errorf("%w: empty related state variable", ErrBadDescription)
|
||||||
}
|
}
|
||||||
return &Argument{
|
return &Argument{
|
||||||
Action: action,
|
Action: action,
|
||||||
@ -133,25 +133,32 @@ func (arg *Argument) RelatedStateVariable() (*StateVariable, error) {
|
|||||||
if v, ok := arg.Action.SCPD.VariableByName[arg.RelatedStateVariableName]; ok {
|
if v, ok := arg.Action.SCPD.VariableByName[arg.RelatedStateVariableName]; ok {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("%w: state variable %q", MissingDefinitionError, arg.RelatedStateVariableName)
|
return nil, fmt.Errorf("%w: state variable %q", ErrMissingDefinition, arg.RelatedStateVariableName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// StateVariable description data.
|
// StateVariable description data.
|
||||||
type StateVariable struct {
|
type StateVariable struct {
|
||||||
Name string
|
Name string
|
||||||
DataType string
|
DataType string
|
||||||
|
|
||||||
|
AllowedValues []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func stateVariableFromXML(xmlSV *xmlsrvdesc.StateVariable) (*StateVariable, error) {
|
func stateVariableFromXML(xmlSV *xmlsrvdesc.StateVariable) (*StateVariable, error) {
|
||||||
if xmlSV.Name == "" {
|
if xmlSV.Name == "" {
|
||||||
return nil, fmt.Errorf("%w: empty state variable name", BadDescriptionError)
|
return nil, fmt.Errorf("%w: empty state variable name", ErrBadDescription)
|
||||||
}
|
}
|
||||||
if xmlSV.DataType.Type != "" {
|
if xmlSV.DataType.Type != "" {
|
||||||
return nil, fmt.Errorf("%w: unsupported data type %q",
|
return nil, fmt.Errorf("%w: unsupported data type %q",
|
||||||
UnsupportedDescriptionError, xmlSV.DataType.Type)
|
ErrUnsupportedDescription, xmlSV.DataType.Type)
|
||||||
|
}
|
||||||
|
if xmlSV.DataType.Name != "string" && len(xmlSV.AllowedValues) > 0 {
|
||||||
|
return nil, fmt.Errorf("%w: allowedValueList is currently unsupported for type %q",
|
||||||
|
ErrUnsupportedDescription, xmlSV.DataType.Name)
|
||||||
}
|
}
|
||||||
return &StateVariable{
|
return &StateVariable{
|
||||||
Name: xmlSV.Name,
|
Name: xmlSV.Name,
|
||||||
DataType: xmlSV.DataType.Name,
|
DataType: xmlSV.DataType.Name,
|
||||||
|
AllowedValues: xmlSV.AllowedValues,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ module github.com/huin/goupnp/v2alpha
|
|||||||
|
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
require github.com/google/go-cmp v0.5.7
|
require github.com/google/go-cmp v0.5.8
|
||||||
|
|
||||||
require github.com/BurntSushi/toml v1.1.0
|
require github.com/BurntSushi/toml v1.1.0
|
||||||
|
|
||||||
|
require golang.org/x/exp v0.0.0-20230307190834-24139beb5833 // indirect
|
||||||
|
@ -2,5 +2,9 @@ github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I
|
|||||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
||||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||||
|
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||||
|
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
golang.org/x/exp v0.0.0-20230307190834-24139beb5833 h1:SChBja7BCQewoTAU7IgvucQKMIXrEpFxNMs0spT3/5s=
|
||||||
|
golang.org/x/exp v0.0.0-20230307190834-24139beb5833/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
@ -8,15 +8,52 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/huin/goupnp/v2alpha/soap"
|
"github.com/huin/goupnp/v2alpha/soap"
|
||||||
"github.com/huin/goupnp/v2alpha/soap/envelope"
|
"github.com/huin/goupnp/v2alpha/soap/envelope"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ HttpClient = &http.Client{}
|
var (
|
||||||
|
// ErrSOAP can be used with errors.Is.
|
||||||
|
ErrSOAP = errors.New("SOAP error")
|
||||||
|
)
|
||||||
|
|
||||||
// HttpClient defines the interface required of an HTTP client. It is a subset of *http.Client.
|
// SOAPError describes an error from this package, potentially including a
|
||||||
type HttpClient interface {
|
// lower-level cause.
|
||||||
|
type SOAPError struct {
|
||||||
|
// description describes the error from the SOAP perspective.
|
||||||
|
description string
|
||||||
|
// cause may be nil.
|
||||||
|
cause error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (se *SOAPError) Error() string {
|
||||||
|
b := &strings.Builder{}
|
||||||
|
b.WriteString("SOAP error")
|
||||||
|
if se.description != "" {
|
||||||
|
b.WriteString(": ")
|
||||||
|
b.WriteString(se.description)
|
||||||
|
}
|
||||||
|
if se.cause != nil {
|
||||||
|
b.WriteString(": ")
|
||||||
|
b.WriteString(se.cause.Error())
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (se *SOAPError) Is(target error) bool {
|
||||||
|
return target == ErrSOAP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (se *SOAPError) Unwrap() error {
|
||||||
|
return se.cause
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ HTTPClient = &http.Client{}
|
||||||
|
|
||||||
|
// HTTPClient defines the interface required of an HTTP client. It is a subset of *http.Client.
|
||||||
|
type HTTPClient interface {
|
||||||
Do(req *http.Request) (*http.Response, error)
|
Do(req *http.Request) (*http.Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,22 +62,21 @@ type Option func(*options)
|
|||||||
|
|
||||||
// WithHTTPClient specifies an *http.Client to use instead of
|
// WithHTTPClient specifies an *http.Client to use instead of
|
||||||
// http.DefaultClient.
|
// http.DefaultClient.
|
||||||
func WithHTTPClient(httpClient HttpClient) Option {
|
func WithHTTPClient(httpClient HTTPClient) Option {
|
||||||
return func(o *options) {
|
return func(o *options) {
|
||||||
o.httpClient = httpClient
|
o.httpClient = httpClient
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type options struct {
|
type options struct {
|
||||||
httpClient HttpClient
|
httpClient HTTPClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client is a SOAP client, attached to a specific SOAP endpoint.
|
// Client is a SOAP client, attached to a specific SOAP endpoint.
|
||||||
// the zero value is not usable, use NewClient() to create an instance.
|
// the zero value is not usable, use NewClient() to create an instance.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
httpClient HttpClient
|
httpClient HTTPClient
|
||||||
endpointURL string
|
endpointURL string
|
||||||
maxErrorResponseBytes int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new SOAP client, which will POST its requests to the
|
// New creates a new SOAP client, which will POST its requests to the
|
||||||
@ -79,8 +115,10 @@ func (c *Client) Do(
|
|||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return fmt.Errorf("SOAP request got HTTP %s (%d)",
|
return &SOAPError{
|
||||||
resp.Status, resp.StatusCode)
|
description: fmt.Sprintf("SOAP request got HTTP %s (%d)",
|
||||||
|
resp.Status, resp.StatusCode),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseResponseAction(resp, actionOut)
|
return ParseResponseAction(resp, actionOut)
|
||||||
@ -110,7 +148,10 @@ func SetRequestAction(
|
|||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
err := envelope.Write(buf, actionIn)
|
err := envelope.Write(buf, actionIn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("encoding envelope: %w", err)
|
return &SOAPError{
|
||||||
|
description: "encoding envelope",
|
||||||
|
cause: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Body = io.NopCloser(buf)
|
req.Body = io.NopCloser(buf)
|
||||||
@ -131,31 +172,39 @@ func ParseResponseAction(
|
|||||||
actionOut *envelope.Action,
|
actionOut *envelope.Action,
|
||||||
) error {
|
) error {
|
||||||
if resp.Body == nil {
|
if resp.Body == nil {
|
||||||
return errors.New("missing response body")
|
return &SOAPError{description: "missing HTTP response body"}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
if _, err := io.Copy(buf, resp.Body); err != nil {
|
if _, err := io.Copy(buf, resp.Body); err != nil {
|
||||||
return fmt.Errorf("reading response body: %w", err)
|
return &SOAPError{
|
||||||
|
description: "reading HTTP response body",
|
||||||
|
cause: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := envelope.Read(buf, actionOut); err != nil {
|
if err := envelope.Read(buf, actionOut); err != nil {
|
||||||
if _, ok := err.(*envelope.Fault); ok {
|
if errors.Is(err, envelope.ErrFault) {
|
||||||
// Parsed cleanly, got SOAP fault.
|
// Parsed cleanly, got SOAP fault.
|
||||||
return err
|
return &SOAPError{
|
||||||
|
description: "SOAP fault",
|
||||||
|
cause: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Parsing problem, provide some information for context.
|
// Parsing problem, provide some information for context.
|
||||||
dispLen := buf.Len()
|
dispLen := buf.Len()
|
||||||
truncMessage := ""
|
truncMessage := ""
|
||||||
if dispLen > 1024 {
|
if dispLen > 1024 {
|
||||||
dispLen = 1024
|
dispLen = 1024
|
||||||
truncMessage = fmt.Sprintf("first %d bytes: ", dispLen)
|
truncMessage = fmt.Sprintf("first %d bytes (total %d bytes): ", dispLen, buf.Len())
|
||||||
}
|
}
|
||||||
return fmt.Errorf(
|
return &SOAPError{
|
||||||
"parsing response body (%s%q): %w",
|
description: fmt.Sprintf(
|
||||||
|
"parsing SOAP response from HTTP body (%s%q)",
|
||||||
truncMessage, buf.Bytes()[:dispLen],
|
truncMessage, buf.Bytes()[:dispLen],
|
||||||
err,
|
),
|
||||||
)
|
cause: err,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -321,9 +321,9 @@ var _ SOAPValue = &Fixed14_4{}
|
|||||||
|
|
||||||
// Fixed14_4FromParts creates a Fixed14_4 from components.
|
// Fixed14_4FromParts creates a Fixed14_4 from components.
|
||||||
// Bounds:
|
// Bounds:
|
||||||
// * Both intPart and fracPart must have the same sign.
|
// - Both intPart and fracPart must have the same sign.
|
||||||
// * -1e14 < intPart < 1e14
|
// - -1e14 < intPart < 1e14
|
||||||
// * -1e4 < fracPart < 1e4
|
// - -1e4 < fracPart < 1e4
|
||||||
func Fixed14_4FromParts(intPart int64, fracPart int16) (Fixed14_4, error) {
|
func Fixed14_4FromParts(intPart int64, fracPart int16) (Fixed14_4, error) {
|
||||||
var v Fixed14_4
|
var v Fixed14_4
|
||||||
err := v.SetParts(intPart, fracPart)
|
err := v.SetParts(intPart, fracPart)
|
||||||
@ -332,9 +332,9 @@ func Fixed14_4FromParts(intPart int64, fracPart int16) (Fixed14_4, error) {
|
|||||||
|
|
||||||
// SetFromParts sets the value based on the integer component and the fractional component.
|
// SetFromParts sets the value based on the integer component and the fractional component.
|
||||||
// Bounds:
|
// Bounds:
|
||||||
// * Both intPart and fracPart must have the same sign.
|
// - Both intPart and fracPart must have the same sign.
|
||||||
// * -1e14 < intPart < 1e14
|
// - -1e14 < intPart < 1e14
|
||||||
// * -1e4 < fracPart < 1e4
|
// - -1e4 < fracPart < 1e4
|
||||||
func (v *Fixed14_4) SetParts(intPart int64, fracPart int16) error {
|
func (v *Fixed14_4) SetParts(intPart int64, fracPart int16) error {
|
||||||
if (intPart < 0) != (fracPart < 0) {
|
if (intPart < 0) != (fracPart < 0) {
|
||||||
return fmt.Errorf("want intPart and fracPart with same sign, got %d and %d",
|
return fmt.Errorf("want intPart and fracPart with same sign, got %d and %d",
|
||||||
|
@ -34,6 +34,7 @@ func (a *DeleteDNSServer) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// DeleteDNSServerRequest contains the "in" args for the "DeleteDNSServer" action.
|
// DeleteDNSServerRequest contains the "in" args for the "DeleteDNSServer" action.
|
||||||
type DeleteDNSServerRequest struct {
|
type DeleteDNSServerRequest struct {
|
||||||
|
// NewDNSServers relates to state variable DNSServers.
|
||||||
NewDNSServers string
|
NewDNSServers string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +65,7 @@ func (a *DeleteIPRouter) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// DeleteIPRouterRequest contains the "in" args for the "DeleteIPRouter" action.
|
// DeleteIPRouterRequest contains the "in" args for the "DeleteIPRouter" action.
|
||||||
type DeleteIPRouterRequest struct {
|
type DeleteIPRouterRequest struct {
|
||||||
|
// NewIPRouters relates to state variable IPRouters.
|
||||||
NewIPRouters string
|
NewIPRouters string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +96,7 @@ func (a *DeleteReservedAddress) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// DeleteReservedAddressRequest contains the "in" args for the "DeleteReservedAddress" action.
|
// DeleteReservedAddressRequest contains the "in" args for the "DeleteReservedAddress" action.
|
||||||
type DeleteReservedAddressRequest struct {
|
type DeleteReservedAddressRequest struct {
|
||||||
|
// NewReservedAddresses relates to state variable ReservedAddresses.
|
||||||
NewReservedAddresses string
|
NewReservedAddresses string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +130,9 @@ type GetAddressRangeRequest struct{}
|
|||||||
|
|
||||||
// GetAddressRangeResponse contains the "out" args for the "GetAddressRange" action.
|
// GetAddressRangeResponse contains the "out" args for the "GetAddressRange" action.
|
||||||
type GetAddressRangeResponse struct {
|
type GetAddressRangeResponse struct {
|
||||||
|
// NewMinAddress relates to state variable MinAddress.
|
||||||
NewMinAddress string
|
NewMinAddress string
|
||||||
|
// NewMaxAddress relates to state variable MaxAddress.
|
||||||
NewMaxAddress string
|
NewMaxAddress string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,6 +163,7 @@ type GetDHCPRelayRequest struct{}
|
|||||||
|
|
||||||
// GetDHCPRelayResponse contains the "out" args for the "GetDHCPRelay" action.
|
// GetDHCPRelayResponse contains the "out" args for the "GetDHCPRelay" action.
|
||||||
type GetDHCPRelayResponse struct {
|
type GetDHCPRelayResponse struct {
|
||||||
|
// NewDHCPRelay relates to state variable DHCPRelay.
|
||||||
NewDHCPRelay pkg2.Boolean
|
NewDHCPRelay pkg2.Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,6 +194,7 @@ type GetDHCPServerConfigurableRequest struct{}
|
|||||||
|
|
||||||
// GetDHCPServerConfigurableResponse contains the "out" args for the "GetDHCPServerConfigurable" action.
|
// GetDHCPServerConfigurableResponse contains the "out" args for the "GetDHCPServerConfigurable" action.
|
||||||
type GetDHCPServerConfigurableResponse struct {
|
type GetDHCPServerConfigurableResponse struct {
|
||||||
|
// NewDHCPServerConfigurable relates to state variable DHCPServerConfigurable.
|
||||||
NewDHCPServerConfigurable pkg2.Boolean
|
NewDHCPServerConfigurable pkg2.Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,6 +225,7 @@ type GetDNSServersRequest struct{}
|
|||||||
|
|
||||||
// GetDNSServersResponse contains the "out" args for the "GetDNSServers" action.
|
// GetDNSServersResponse contains the "out" args for the "GetDNSServers" action.
|
||||||
type GetDNSServersResponse struct {
|
type GetDNSServersResponse struct {
|
||||||
|
// NewDNSServers relates to state variable DNSServers.
|
||||||
NewDNSServers string
|
NewDNSServers string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,6 +256,7 @@ type GetDomainNameRequest struct{}
|
|||||||
|
|
||||||
// GetDomainNameResponse contains the "out" args for the "GetDomainName" action.
|
// GetDomainNameResponse contains the "out" args for the "GetDomainName" action.
|
||||||
type GetDomainNameResponse struct {
|
type GetDomainNameResponse struct {
|
||||||
|
// NewDomainName relates to state variable DomainName.
|
||||||
NewDomainName string
|
NewDomainName string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,6 +287,7 @@ type GetIPRoutersListRequest struct{}
|
|||||||
|
|
||||||
// GetIPRoutersListResponse contains the "out" args for the "GetIPRoutersList" action.
|
// GetIPRoutersListResponse contains the "out" args for the "GetIPRoutersList" action.
|
||||||
type GetIPRoutersListResponse struct {
|
type GetIPRoutersListResponse struct {
|
||||||
|
// NewIPRouters relates to state variable IPRouters.
|
||||||
NewIPRouters string
|
NewIPRouters string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,6 +318,7 @@ type GetReservedAddressesRequest struct{}
|
|||||||
|
|
||||||
// GetReservedAddressesResponse contains the "out" args for the "GetReservedAddresses" action.
|
// GetReservedAddressesResponse contains the "out" args for the "GetReservedAddresses" action.
|
||||||
type GetReservedAddressesResponse struct {
|
type GetReservedAddressesResponse struct {
|
||||||
|
// NewReservedAddresses relates to state variable ReservedAddresses.
|
||||||
NewReservedAddresses string
|
NewReservedAddresses string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,6 +349,7 @@ type GetSubnetMaskRequest struct{}
|
|||||||
|
|
||||||
// GetSubnetMaskResponse contains the "out" args for the "GetSubnetMask" action.
|
// GetSubnetMaskResponse contains the "out" args for the "GetSubnetMask" action.
|
||||||
type GetSubnetMaskResponse struct {
|
type GetSubnetMaskResponse struct {
|
||||||
|
// NewSubnetMask relates to state variable SubnetMask.
|
||||||
NewSubnetMask string
|
NewSubnetMask string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,7 +377,9 @@ func (a *SetAddressRange) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// SetAddressRangeRequest contains the "in" args for the "SetAddressRange" action.
|
// SetAddressRangeRequest contains the "in" args for the "SetAddressRange" action.
|
||||||
type SetAddressRangeRequest struct {
|
type SetAddressRangeRequest struct {
|
||||||
|
// NewMinAddress relates to state variable MinAddress.
|
||||||
NewMinAddress string
|
NewMinAddress string
|
||||||
|
// NewMaxAddress relates to state variable MaxAddress.
|
||||||
NewMaxAddress string
|
NewMaxAddress string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,6 +410,7 @@ func (a *SetDHCPRelay) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// SetDHCPRelayRequest contains the "in" args for the "SetDHCPRelay" action.
|
// SetDHCPRelayRequest contains the "in" args for the "SetDHCPRelay" action.
|
||||||
type SetDHCPRelayRequest struct {
|
type SetDHCPRelayRequest struct {
|
||||||
|
// NewDHCPRelay relates to state variable DHCPRelay.
|
||||||
NewDHCPRelay pkg2.Boolean
|
NewDHCPRelay pkg2.Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,6 +441,7 @@ func (a *SetDHCPServerConfigurable) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// SetDHCPServerConfigurableRequest contains the "in" args for the "SetDHCPServerConfigurable" action.
|
// SetDHCPServerConfigurableRequest contains the "in" args for the "SetDHCPServerConfigurable" action.
|
||||||
type SetDHCPServerConfigurableRequest struct {
|
type SetDHCPServerConfigurableRequest struct {
|
||||||
|
// NewDHCPServerConfigurable relates to state variable DHCPServerConfigurable.
|
||||||
NewDHCPServerConfigurable pkg2.Boolean
|
NewDHCPServerConfigurable pkg2.Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,6 +472,7 @@ func (a *SetDNSServer) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// SetDNSServerRequest contains the "in" args for the "SetDNSServer" action.
|
// SetDNSServerRequest contains the "in" args for the "SetDNSServer" action.
|
||||||
type SetDNSServerRequest struct {
|
type SetDNSServerRequest struct {
|
||||||
|
// NewDNSServers relates to state variable DNSServers.
|
||||||
NewDNSServers string
|
NewDNSServers string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -486,6 +503,7 @@ func (a *SetDomainName) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// SetDomainNameRequest contains the "in" args for the "SetDomainName" action.
|
// SetDomainNameRequest contains the "in" args for the "SetDomainName" action.
|
||||||
type SetDomainNameRequest struct {
|
type SetDomainNameRequest struct {
|
||||||
|
// NewDomainName relates to state variable DomainName.
|
||||||
NewDomainName string
|
NewDomainName string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,6 +534,7 @@ func (a *SetIPRouter) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// SetIPRouterRequest contains the "in" args for the "SetIPRouter" action.
|
// SetIPRouterRequest contains the "in" args for the "SetIPRouter" action.
|
||||||
type SetIPRouterRequest struct {
|
type SetIPRouterRequest struct {
|
||||||
|
// NewIPRouters relates to state variable IPRouters.
|
||||||
NewIPRouters string
|
NewIPRouters string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,6 +565,7 @@ func (a *SetReservedAddress) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// SetReservedAddressRequest contains the "in" args for the "SetReservedAddress" action.
|
// SetReservedAddressRequest contains the "in" args for the "SetReservedAddress" action.
|
||||||
type SetReservedAddressRequest struct {
|
type SetReservedAddressRequest struct {
|
||||||
|
// NewReservedAddresses relates to state variable ReservedAddresses.
|
||||||
NewReservedAddresses string
|
NewReservedAddresses string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -576,6 +596,7 @@ func (a *SetSubnetMask) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// SetSubnetMaskRequest contains the "in" args for the "SetSubnetMask" action.
|
// SetSubnetMaskRequest contains the "in" args for the "SetSubnetMask" action.
|
||||||
type SetSubnetMaskRequest struct {
|
type SetSubnetMaskRequest struct {
|
||||||
|
// NewSubnetMask relates to state variable SubnetMask.
|
||||||
NewSubnetMask string
|
NewSubnetMask string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,35 @@ import (
|
|||||||
pkg2 "github.com/huin/goupnp/v2alpha/soap/types"
|
pkg2 "github.com/huin/goupnp/v2alpha/soap/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Allowed values for state variable ConnectionStatus.
|
||||||
|
const (
|
||||||
|
ConnectionStatus_Unconfigured = "Unconfigured"
|
||||||
|
ConnectionStatus_Connected = "Connected"
|
||||||
|
ConnectionStatus_Disconnected = "Disconnected"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Allowed values for state variable LastConnectionError.
|
||||||
|
const (
|
||||||
|
LastConnectionError_ERROR_NONE = "ERROR_NONE"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Allowed values for state variable PortMappingProtocol.
|
||||||
|
const (
|
||||||
|
PortMappingProtocol_TCP = "TCP"
|
||||||
|
PortMappingProtocol_UDP = "UDP"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Allowed values for state variable PossibleConnectionTypes.
|
||||||
|
const (
|
||||||
|
PossibleConnectionTypes_Unconfigured = "Unconfigured"
|
||||||
|
PossibleConnectionTypes_IP_Routed = "IP_Routed"
|
||||||
|
PossibleConnectionTypes_DHCP_Spoofed = "DHCP_Spoofed"
|
||||||
|
PossibleConnectionTypes_PPPoE_Bridged = "PPPoE_Bridged"
|
||||||
|
PossibleConnectionTypes_PPTP_Relay = "PPTP_Relay"
|
||||||
|
PossibleConnectionTypes_L2TP_Relay = "L2TP_Relay"
|
||||||
|
PossibleConnectionTypes_PPPoE_Relay = "PPPoE_Relay"
|
||||||
|
)
|
||||||
|
|
||||||
const ServiceType = "urn:schemas-upnp-org:service:WANPPPConnection:1"
|
const ServiceType = "urn:schemas-upnp-org:service:WANPPPConnection:1"
|
||||||
|
|
||||||
// AddPortMapping provides request and response for the action.
|
// AddPortMapping provides request and response for the action.
|
||||||
@ -34,13 +63,21 @@ func (a *AddPortMapping) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// AddPortMappingRequest contains the "in" args for the "AddPortMapping" action.
|
// AddPortMappingRequest contains the "in" args for the "AddPortMapping" action.
|
||||||
type AddPortMappingRequest struct {
|
type AddPortMappingRequest struct {
|
||||||
|
// NewRemoteHost relates to state variable RemoteHost.
|
||||||
NewRemoteHost string
|
NewRemoteHost string
|
||||||
|
// NewExternalPort relates to state variable ExternalPort.
|
||||||
NewExternalPort pkg2.UI2
|
NewExternalPort pkg2.UI2
|
||||||
|
// NewProtocol relates to state variable PortMappingProtocol (2 standard allowed values).
|
||||||
NewProtocol string
|
NewProtocol string
|
||||||
|
// NewInternalPort relates to state variable InternalPort.
|
||||||
NewInternalPort pkg2.UI2
|
NewInternalPort pkg2.UI2
|
||||||
|
// NewInternalClient relates to state variable InternalClient.
|
||||||
NewInternalClient string
|
NewInternalClient string
|
||||||
|
// NewEnabled relates to state variable PortMappingEnabled.
|
||||||
NewEnabled pkg2.Boolean
|
NewEnabled pkg2.Boolean
|
||||||
|
// NewPortMappingDescription relates to state variable PortMappingDescription.
|
||||||
NewPortMappingDescription string
|
NewPortMappingDescription string
|
||||||
|
// NewLeaseDuration relates to state variable PortMappingLeaseDuration.
|
||||||
NewLeaseDuration pkg2.UI4
|
NewLeaseDuration pkg2.UI4
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +108,9 @@ func (a *ConfigureConnection) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// ConfigureConnectionRequest contains the "in" args for the "ConfigureConnection" action.
|
// ConfigureConnectionRequest contains the "in" args for the "ConfigureConnection" action.
|
||||||
type ConfigureConnectionRequest struct {
|
type ConfigureConnectionRequest struct {
|
||||||
|
// NewUserName relates to state variable UserName.
|
||||||
NewUserName string
|
NewUserName string
|
||||||
|
// NewPassword relates to state variable Password.
|
||||||
NewPassword string
|
NewPassword string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,8 +141,11 @@ func (a *DeletePortMapping) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// DeletePortMappingRequest contains the "in" args for the "DeletePortMapping" action.
|
// DeletePortMappingRequest contains the "in" args for the "DeletePortMapping" action.
|
||||||
type DeletePortMappingRequest struct {
|
type DeletePortMappingRequest struct {
|
||||||
|
// NewRemoteHost relates to state variable RemoteHost.
|
||||||
NewRemoteHost string
|
NewRemoteHost string
|
||||||
|
// NewExternalPort relates to state variable ExternalPort.
|
||||||
NewExternalPort pkg2.UI2
|
NewExternalPort pkg2.UI2
|
||||||
|
// NewProtocol relates to state variable PortMappingProtocol (2 standard allowed values).
|
||||||
NewProtocol string
|
NewProtocol string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,6 +207,7 @@ type GetAutoDisconnectTimeRequest struct{}
|
|||||||
|
|
||||||
// GetAutoDisconnectTimeResponse contains the "out" args for the "GetAutoDisconnectTime" action.
|
// GetAutoDisconnectTimeResponse contains the "out" args for the "GetAutoDisconnectTime" action.
|
||||||
type GetAutoDisconnectTimeResponse struct {
|
type GetAutoDisconnectTimeResponse struct {
|
||||||
|
// NewAutoDisconnectTime relates to state variable AutoDisconnectTime.
|
||||||
NewAutoDisconnectTime pkg2.UI4
|
NewAutoDisconnectTime pkg2.UI4
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,7 +238,9 @@ type GetConnectionTypeInfoRequest struct{}
|
|||||||
|
|
||||||
// GetConnectionTypeInfoResponse contains the "out" args for the "GetConnectionTypeInfo" action.
|
// GetConnectionTypeInfoResponse contains the "out" args for the "GetConnectionTypeInfo" action.
|
||||||
type GetConnectionTypeInfoResponse struct {
|
type GetConnectionTypeInfoResponse struct {
|
||||||
|
// NewConnectionType relates to state variable ConnectionType.
|
||||||
NewConnectionType string
|
NewConnectionType string
|
||||||
|
// NewPossibleConnectionTypes relates to state variable PossibleConnectionTypes (7 standard allowed values).
|
||||||
NewPossibleConnectionTypes string
|
NewPossibleConnectionTypes string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,6 +271,7 @@ type GetExternalIPAddressRequest struct{}
|
|||||||
|
|
||||||
// GetExternalIPAddressResponse contains the "out" args for the "GetExternalIPAddress" action.
|
// GetExternalIPAddressResponse contains the "out" args for the "GetExternalIPAddress" action.
|
||||||
type GetExternalIPAddressResponse struct {
|
type GetExternalIPAddressResponse struct {
|
||||||
|
// NewExternalIPAddress relates to state variable ExternalIPAddress.
|
||||||
NewExternalIPAddress string
|
NewExternalIPAddress string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,18 +299,27 @@ func (a *GetGenericPortMappingEntry) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// GetGenericPortMappingEntryRequest contains the "in" args for the "GetGenericPortMappingEntry" action.
|
// GetGenericPortMappingEntryRequest contains the "in" args for the "GetGenericPortMappingEntry" action.
|
||||||
type GetGenericPortMappingEntryRequest struct {
|
type GetGenericPortMappingEntryRequest struct {
|
||||||
|
// NewPortMappingIndex relates to state variable PortMappingNumberOfEntries.
|
||||||
NewPortMappingIndex pkg2.UI2
|
NewPortMappingIndex pkg2.UI2
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGenericPortMappingEntryResponse contains the "out" args for the "GetGenericPortMappingEntry" action.
|
// GetGenericPortMappingEntryResponse contains the "out" args for the "GetGenericPortMappingEntry" action.
|
||||||
type GetGenericPortMappingEntryResponse struct {
|
type GetGenericPortMappingEntryResponse struct {
|
||||||
|
// NewRemoteHost relates to state variable RemoteHost.
|
||||||
NewRemoteHost string
|
NewRemoteHost string
|
||||||
|
// NewExternalPort relates to state variable ExternalPort.
|
||||||
NewExternalPort pkg2.UI2
|
NewExternalPort pkg2.UI2
|
||||||
|
// NewProtocol relates to state variable PortMappingProtocol (2 standard allowed values).
|
||||||
NewProtocol string
|
NewProtocol string
|
||||||
|
// NewInternalPort relates to state variable InternalPort.
|
||||||
NewInternalPort pkg2.UI2
|
NewInternalPort pkg2.UI2
|
||||||
|
// NewInternalClient relates to state variable InternalClient.
|
||||||
NewInternalClient string
|
NewInternalClient string
|
||||||
|
// NewEnabled relates to state variable PortMappingEnabled.
|
||||||
NewEnabled pkg2.Boolean
|
NewEnabled pkg2.Boolean
|
||||||
|
// NewPortMappingDescription relates to state variable PortMappingDescription.
|
||||||
NewPortMappingDescription string
|
NewPortMappingDescription string
|
||||||
|
// NewLeaseDuration relates to state variable PortMappingLeaseDuration.
|
||||||
NewLeaseDuration pkg2.UI4
|
NewLeaseDuration pkg2.UI4
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,6 +350,7 @@ type GetIdleDisconnectTimeRequest struct{}
|
|||||||
|
|
||||||
// GetIdleDisconnectTimeResponse contains the "out" args for the "GetIdleDisconnectTime" action.
|
// GetIdleDisconnectTimeResponse contains the "out" args for the "GetIdleDisconnectTime" action.
|
||||||
type GetIdleDisconnectTimeResponse struct {
|
type GetIdleDisconnectTimeResponse struct {
|
||||||
|
// NewIdleDisconnectTime relates to state variable IdleDisconnectTime.
|
||||||
NewIdleDisconnectTime pkg2.UI4
|
NewIdleDisconnectTime pkg2.UI4
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,7 +381,9 @@ type GetLinkLayerMaxBitRatesRequest struct{}
|
|||||||
|
|
||||||
// GetLinkLayerMaxBitRatesResponse contains the "out" args for the "GetLinkLayerMaxBitRates" action.
|
// GetLinkLayerMaxBitRatesResponse contains the "out" args for the "GetLinkLayerMaxBitRates" action.
|
||||||
type GetLinkLayerMaxBitRatesResponse struct {
|
type GetLinkLayerMaxBitRatesResponse struct {
|
||||||
|
// NewUpstreamMaxBitRate relates to state variable UpstreamMaxBitRate.
|
||||||
NewUpstreamMaxBitRate pkg2.UI4
|
NewUpstreamMaxBitRate pkg2.UI4
|
||||||
|
// NewDownstreamMaxBitRate relates to state variable DownstreamMaxBitRate.
|
||||||
NewDownstreamMaxBitRate pkg2.UI4
|
NewDownstreamMaxBitRate pkg2.UI4
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,7 +414,9 @@ type GetNATRSIPStatusRequest struct{}
|
|||||||
|
|
||||||
// GetNATRSIPStatusResponse contains the "out" args for the "GetNATRSIPStatus" action.
|
// GetNATRSIPStatusResponse contains the "out" args for the "GetNATRSIPStatus" action.
|
||||||
type GetNATRSIPStatusResponse struct {
|
type GetNATRSIPStatusResponse struct {
|
||||||
|
// NewRSIPAvailable relates to state variable RSIPAvailable.
|
||||||
NewRSIPAvailable pkg2.Boolean
|
NewRSIPAvailable pkg2.Boolean
|
||||||
|
// NewNATEnabled relates to state variable NATEnabled.
|
||||||
NewNATEnabled pkg2.Boolean
|
NewNATEnabled pkg2.Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,6 +447,7 @@ type GetPPPAuthenticationProtocolRequest struct{}
|
|||||||
|
|
||||||
// GetPPPAuthenticationProtocolResponse contains the "out" args for the "GetPPPAuthenticationProtocol" action.
|
// GetPPPAuthenticationProtocolResponse contains the "out" args for the "GetPPPAuthenticationProtocol" action.
|
||||||
type GetPPPAuthenticationProtocolResponse struct {
|
type GetPPPAuthenticationProtocolResponse struct {
|
||||||
|
// NewPPPAuthenticationProtocol relates to state variable PPPAuthenticationProtocol.
|
||||||
NewPPPAuthenticationProtocol string
|
NewPPPAuthenticationProtocol string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,6 +478,7 @@ type GetPPPCompressionProtocolRequest struct{}
|
|||||||
|
|
||||||
// GetPPPCompressionProtocolResponse contains the "out" args for the "GetPPPCompressionProtocol" action.
|
// GetPPPCompressionProtocolResponse contains the "out" args for the "GetPPPCompressionProtocol" action.
|
||||||
type GetPPPCompressionProtocolResponse struct {
|
type GetPPPCompressionProtocolResponse struct {
|
||||||
|
// NewPPPCompressionProtocol relates to state variable PPPCompressionProtocol.
|
||||||
NewPPPCompressionProtocol string
|
NewPPPCompressionProtocol string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,6 +509,7 @@ type GetPPPEncryptionProtocolRequest struct{}
|
|||||||
|
|
||||||
// GetPPPEncryptionProtocolResponse contains the "out" args for the "GetPPPEncryptionProtocol" action.
|
// GetPPPEncryptionProtocolResponse contains the "out" args for the "GetPPPEncryptionProtocol" action.
|
||||||
type GetPPPEncryptionProtocolResponse struct {
|
type GetPPPEncryptionProtocolResponse struct {
|
||||||
|
// NewPPPEncryptionProtocol relates to state variable PPPEncryptionProtocol.
|
||||||
NewPPPEncryptionProtocol string
|
NewPPPEncryptionProtocol string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -477,6 +540,7 @@ type GetPasswordRequest struct{}
|
|||||||
|
|
||||||
// GetPasswordResponse contains the "out" args for the "GetPassword" action.
|
// GetPasswordResponse contains the "out" args for the "GetPassword" action.
|
||||||
type GetPasswordResponse struct {
|
type GetPasswordResponse struct {
|
||||||
|
// NewPassword relates to state variable Password.
|
||||||
NewPassword string
|
NewPassword string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,17 +568,25 @@ func (a *GetSpecificPortMappingEntry) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// GetSpecificPortMappingEntryRequest contains the "in" args for the "GetSpecificPortMappingEntry" action.
|
// GetSpecificPortMappingEntryRequest contains the "in" args for the "GetSpecificPortMappingEntry" action.
|
||||||
type GetSpecificPortMappingEntryRequest struct {
|
type GetSpecificPortMappingEntryRequest struct {
|
||||||
|
// NewRemoteHost relates to state variable RemoteHost.
|
||||||
NewRemoteHost string
|
NewRemoteHost string
|
||||||
|
// NewExternalPort relates to state variable ExternalPort.
|
||||||
NewExternalPort pkg2.UI2
|
NewExternalPort pkg2.UI2
|
||||||
|
// NewProtocol relates to state variable PortMappingProtocol (2 standard allowed values).
|
||||||
NewProtocol string
|
NewProtocol string
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSpecificPortMappingEntryResponse contains the "out" args for the "GetSpecificPortMappingEntry" action.
|
// GetSpecificPortMappingEntryResponse contains the "out" args for the "GetSpecificPortMappingEntry" action.
|
||||||
type GetSpecificPortMappingEntryResponse struct {
|
type GetSpecificPortMappingEntryResponse struct {
|
||||||
|
// NewInternalPort relates to state variable InternalPort.
|
||||||
NewInternalPort pkg2.UI2
|
NewInternalPort pkg2.UI2
|
||||||
|
// NewInternalClient relates to state variable InternalClient.
|
||||||
NewInternalClient string
|
NewInternalClient string
|
||||||
|
// NewEnabled relates to state variable PortMappingEnabled.
|
||||||
NewEnabled pkg2.Boolean
|
NewEnabled pkg2.Boolean
|
||||||
|
// NewPortMappingDescription relates to state variable PortMappingDescription.
|
||||||
NewPortMappingDescription string
|
NewPortMappingDescription string
|
||||||
|
// NewLeaseDuration relates to state variable PortMappingLeaseDuration.
|
||||||
NewLeaseDuration pkg2.UI4
|
NewLeaseDuration pkg2.UI4
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -545,8 +617,11 @@ type GetStatusInfoRequest struct{}
|
|||||||
|
|
||||||
// GetStatusInfoResponse contains the "out" args for the "GetStatusInfo" action.
|
// GetStatusInfoResponse contains the "out" args for the "GetStatusInfo" action.
|
||||||
type GetStatusInfoResponse struct {
|
type GetStatusInfoResponse struct {
|
||||||
|
// NewConnectionStatus relates to state variable ConnectionStatus (3 standard allowed values).
|
||||||
NewConnectionStatus string
|
NewConnectionStatus string
|
||||||
|
// NewLastConnectionError relates to state variable LastConnectionError (1 standard allowed values).
|
||||||
NewLastConnectionError string
|
NewLastConnectionError string
|
||||||
|
// NewUptime relates to state variable Uptime.
|
||||||
NewUptime pkg2.UI4
|
NewUptime pkg2.UI4
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -577,6 +652,7 @@ type GetUserNameRequest struct{}
|
|||||||
|
|
||||||
// GetUserNameResponse contains the "out" args for the "GetUserName" action.
|
// GetUserNameResponse contains the "out" args for the "GetUserName" action.
|
||||||
type GetUserNameResponse struct {
|
type GetUserNameResponse struct {
|
||||||
|
// NewUserName relates to state variable UserName.
|
||||||
NewUserName string
|
NewUserName string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -607,6 +683,7 @@ type GetWarnDisconnectDelayRequest struct{}
|
|||||||
|
|
||||||
// GetWarnDisconnectDelayResponse contains the "out" args for the "GetWarnDisconnectDelay" action.
|
// GetWarnDisconnectDelayResponse contains the "out" args for the "GetWarnDisconnectDelay" action.
|
||||||
type GetWarnDisconnectDelayResponse struct {
|
type GetWarnDisconnectDelayResponse struct {
|
||||||
|
// NewWarnDisconnectDelay relates to state variable WarnDisconnectDelay.
|
||||||
NewWarnDisconnectDelay pkg2.UI4
|
NewWarnDisconnectDelay pkg2.UI4
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -690,6 +767,7 @@ func (a *SetAutoDisconnectTime) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// SetAutoDisconnectTimeRequest contains the "in" args for the "SetAutoDisconnectTime" action.
|
// SetAutoDisconnectTimeRequest contains the "in" args for the "SetAutoDisconnectTime" action.
|
||||||
type SetAutoDisconnectTimeRequest struct {
|
type SetAutoDisconnectTimeRequest struct {
|
||||||
|
// NewAutoDisconnectTime relates to state variable AutoDisconnectTime.
|
||||||
NewAutoDisconnectTime pkg2.UI4
|
NewAutoDisconnectTime pkg2.UI4
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -720,6 +798,7 @@ func (a *SetConnectionType) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// SetConnectionTypeRequest contains the "in" args for the "SetConnectionType" action.
|
// SetConnectionTypeRequest contains the "in" args for the "SetConnectionType" action.
|
||||||
type SetConnectionTypeRequest struct {
|
type SetConnectionTypeRequest struct {
|
||||||
|
// NewConnectionType relates to state variable ConnectionType.
|
||||||
NewConnectionType string
|
NewConnectionType string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -750,6 +829,7 @@ func (a *SetIdleDisconnectTime) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// SetIdleDisconnectTimeRequest contains the "in" args for the "SetIdleDisconnectTime" action.
|
// SetIdleDisconnectTimeRequest contains the "in" args for the "SetIdleDisconnectTime" action.
|
||||||
type SetIdleDisconnectTimeRequest struct {
|
type SetIdleDisconnectTimeRequest struct {
|
||||||
|
// NewIdleDisconnectTime relates to state variable IdleDisconnectTime.
|
||||||
NewIdleDisconnectTime pkg2.UI4
|
NewIdleDisconnectTime pkg2.UI4
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -780,6 +860,7 @@ func (a *SetWarnDisconnectDelay) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// SetWarnDisconnectDelayRequest contains the "in" args for the "SetWarnDisconnectDelay" action.
|
// SetWarnDisconnectDelayRequest contains the "in" args for the "SetWarnDisconnectDelay" action.
|
||||||
type SetWarnDisconnectDelayRequest struct {
|
type SetWarnDisconnectDelayRequest struct {
|
||||||
|
// NewWarnDisconnectDelay relates to state variable WarnDisconnectDelay.
|
||||||
NewWarnDisconnectDelay pkg2.UI4
|
NewWarnDisconnectDelay pkg2.UI4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{{define "service"}}
|
{{define "service"}}
|
||||||
{{- $Imps := .Imps -}}
|
{{- $Imps := .Imps -}}
|
||||||
|
{{- $Types := .Types -}}
|
||||||
// Package {{.Manifest.Package}} provides types for the {{quote .Manifest.ServiceType}} service.
|
// Package {{.Manifest.Package}} provides types for the {{quote .Manifest.ServiceType}} service.
|
||||||
{{- with .Manifest.DocumentURL}}
|
{{- with .Manifest.DocumentURL}}
|
||||||
//
|
//
|
||||||
@ -13,15 +14,28 @@ import (
|
|||||||
{{- end}}
|
{{- end}}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
{{range .Types.StringVarDefs}}
|
||||||
|
{{- $Name := .Name}}
|
||||||
|
{{- with .AllowedValues}}
|
||||||
|
// Allowed values for state variable {{$Name}}.
|
||||||
|
const (
|
||||||
|
{{- range .}}
|
||||||
|
{{$Name}}_{{.}} = "{{.}}"
|
||||||
|
{{- end}}
|
||||||
|
)
|
||||||
|
{{- end}}
|
||||||
|
{{- end}}
|
||||||
|
|
||||||
const ServiceType = {{quote .Manifest.ServiceType}}
|
const ServiceType = {{quote .Manifest.ServiceType}}
|
||||||
{{range .SCPD.SortedActions}}
|
{{range .SCPD.SortedActions}}
|
||||||
{{- template "action" args "Action" . "Imps" $Imps}}
|
{{- template "action" args "Action" . "Imps" $Imps "Types" $Types}}
|
||||||
{{end}}
|
{{end}}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
|
|
||||||
{{define "action"}}
|
{{define "action"}}
|
||||||
{{- $Imps := .Imps}}
|
{{- $Imps := .Imps}}
|
||||||
{{- $soapActionType := index $Imps.TypeByName "SOAPActionInterface"}}
|
{{- $Types := .Types}}
|
||||||
|
{{- $soapActionType := index $Types.TypeByName "SOAPActionInterface"}}
|
||||||
// {{.Action.Name}} provides request and response for the action.
|
// {{.Action.Name}} provides request and response for the action.
|
||||||
//
|
//
|
||||||
// ServiceType implements {{$soapActionType.AbsRef}}, self-describing the SOAP action.
|
// ServiceType implements {{$soapActionType.AbsRef}}, self-describing the SOAP action.
|
||||||
@ -43,18 +57,23 @@ func (a *{{.Action.Name}}) RefResponse() any { return &a.Response }
|
|||||||
|
|
||||||
// {{.Action.Name}}Request contains the "in" args for the {{quote .Action.Name}} action.
|
// {{.Action.Name}}Request contains the "in" args for the {{quote .Action.Name}} action.
|
||||||
type {{.Action.Name}}Request struct
|
type {{.Action.Name}}Request struct
|
||||||
{{- template "args" args "Args" .Action.InArgs "Imps" $Imps}}
|
{{- template "args" args "Args" .Action.InArgs "Imps" $Imps "Types" $Types}}
|
||||||
|
|
||||||
// {{.Action.Name}}Response contains the "out" args for the {{quote .Action.Name}} action.
|
// {{.Action.Name}}Response contains the "out" args for the {{quote .Action.Name}} action.
|
||||||
type {{.Action.Name}}Response struct
|
type {{.Action.Name}}Response struct
|
||||||
{{- template "args" args "Args" .Action.OutArgs "Imps" $Imps}}
|
{{- template "args" args "Args" .Action.OutArgs "Imps" $Imps "Types" $Types}}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
|
|
||||||
{{define "args"}}
|
{{define "args"}}
|
||||||
{{- $Imps := .Imps -}}
|
{{- $Imps := .Imps -}}
|
||||||
|
{{- $Types := .Types -}}
|
||||||
{ {{- with .Args}}
|
{ {{- with .Args}}
|
||||||
{{- range .}}
|
{{- range .}}
|
||||||
{{- $fieldType := index $Imps.TypeByName .RelatedStateVariable.DataType}}
|
{{- $fieldType := index $Types.TypeByName .RelatedStateVariable.DataType}}
|
||||||
|
// {{.Name}} relates to state variable {{.RelatedStateVariable.Name}}
|
||||||
|
{{- with .RelatedStateVariable.AllowedValues}}
|
||||||
|
{{- ""}} ({{len .}} standard allowed values)
|
||||||
|
{{- end }}.
|
||||||
{{.Name}} {{$fieldType.Ref}}
|
{{.Name}} {{$fieldType.Ref}}
|
||||||
{{- end}}
|
{{- end}}
|
||||||
{{end -}} }
|
{{end -}} }
|
||||||
|
10
vendor/golang.org/x/sync/errgroup/errgroup.go
generated
vendored
10
vendor/golang.org/x/sync/errgroup/errgroup.go
generated
vendored
@ -20,7 +20,7 @@ type token struct{}
|
|||||||
// A zero Group is valid, has no limit on the number of active goroutines,
|
// A zero Group is valid, has no limit on the number of active goroutines,
|
||||||
// and does not cancel on error.
|
// and does not cancel on error.
|
||||||
type Group struct {
|
type Group struct {
|
||||||
cancel func()
|
cancel func(error)
|
||||||
|
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ func (g *Group) done() {
|
|||||||
// returns a non-nil error or the first time Wait returns, whichever occurs
|
// returns a non-nil error or the first time Wait returns, whichever occurs
|
||||||
// first.
|
// first.
|
||||||
func WithContext(ctx context.Context) (*Group, context.Context) {
|
func WithContext(ctx context.Context) (*Group, context.Context) {
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := withCancelCause(ctx)
|
||||||
return &Group{cancel: cancel}, ctx
|
return &Group{cancel: cancel}, ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ func WithContext(ctx context.Context) (*Group, context.Context) {
|
|||||||
func (g *Group) Wait() error {
|
func (g *Group) Wait() error {
|
||||||
g.wg.Wait()
|
g.wg.Wait()
|
||||||
if g.cancel != nil {
|
if g.cancel != nil {
|
||||||
g.cancel()
|
g.cancel(g.err)
|
||||||
}
|
}
|
||||||
return g.err
|
return g.err
|
||||||
}
|
}
|
||||||
@ -76,7 +76,7 @@ func (g *Group) Go(f func() error) {
|
|||||||
g.errOnce.Do(func() {
|
g.errOnce.Do(func() {
|
||||||
g.err = err
|
g.err = err
|
||||||
if g.cancel != nil {
|
if g.cancel != nil {
|
||||||
g.cancel()
|
g.cancel(g.err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -105,7 +105,7 @@ func (g *Group) TryGo(f func() error) bool {
|
|||||||
g.errOnce.Do(func() {
|
g.errOnce.Do(func() {
|
||||||
g.err = err
|
g.err = err
|
||||||
if g.cancel != nil {
|
if g.cancel != nil {
|
||||||
g.cancel()
|
g.cancel(g.err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
13
vendor/golang.org/x/sync/errgroup/go120.go
generated
vendored
Normal file
13
vendor/golang.org/x/sync/errgroup/go120.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright 2023 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build go1.20
|
||||||
|
|
||||||
|
package errgroup
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
func withCancelCause(parent context.Context) (context.Context, func(error)) {
|
||||||
|
return context.WithCancelCause(parent)
|
||||||
|
}
|
14
vendor/golang.org/x/sync/errgroup/pre_go120.go
generated
vendored
Normal file
14
vendor/golang.org/x/sync/errgroup/pre_go120.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Copyright 2023 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !go1.20
|
||||||
|
|
||||||
|
package errgroup
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
func withCancelCause(parent context.Context) (context.Context, func(error)) {
|
||||||
|
ctx, cancel := context.WithCancel(parent)
|
||||||
|
return ctx, func(error) { cancel() }
|
||||||
|
}
|
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
@ -1,3 +1,3 @@
|
|||||||
# golang.org/x/sync v0.1.0
|
# golang.org/x/sync v0.5.0
|
||||||
## explicit
|
## explicit; go 1.18
|
||||||
golang.org/x/sync/errgroup
|
golang.org/x/sync/errgroup
|
||||||
|
Loading…
Reference in New Issue
Block a user