Compare commits

..

No commits in common. "11e9df2080ac8bbaf8648142a52a8f00f752bb8f" and "00a824fe48a337c1f1f39c340e1b9704362256f5" have entirely different histories.

25 changed files with 151 additions and 693 deletions

4
go.mod
View File

@ -1,5 +1,5 @@
module git.cyrilix.bzh/cyrilix/goupnp module git.cyrilix.bzh/cyrilix/goupnp
go 1.22 go 1.19
require golang.org/x/sync v0.7.0 require golang.org/x/sync v0.1.0

4
go.sum
View File

@ -1,2 +1,2 @@
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

View File

@ -1,6 +1,4 @@
go 1.22 go 1.18
toolchain go1.22.5
use ( use (
. .

View File

@ -1,6 +0,0 @@
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=

View File

@ -85,10 +85,7 @@ 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
} }
@ -151,10 +148,6 @@ 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()
@ -164,7 +157,7 @@ func requestXml(ctx context.Context, url string, defaultSpace string, doc interf
return err return err
} }
resp, err := HTTPClientDefault.Do(req) resp, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
return err return err
} }

View File

@ -3,7 +3,6 @@ package httpu
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"context"
"errors" "errors"
"fmt" "fmt"
"log" "log"
@ -27,27 +26,6 @@ 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 {
@ -56,7 +34,6 @@ 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.
@ -98,25 +75,6 @@ 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()
@ -143,28 +101,10 @@ func (httpu *HTTPUClient) DoWithContext(
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 return nil, err
ctx := req.Context()
deadline, ok := ctx.Deadline()
if ok {
if err = httpu.conn.SetDeadline(deadline); err != nil {
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++ {
if n, err := httpu.conn.WriteTo(requestBuf.Bytes(), destAddr); err != nil { if n, err := httpu.conn.WriteTo(requestBuf.Bytes(), destAddr); err != nil {

View File

@ -49,14 +49,14 @@ 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,
) error { ) error {
tasks := &errgroup.Group{} tasks := &errgroup.Group{}
for _, d := range mc.delegates { for _, d := range mc.delegates {
d := d // copy for closure d := d // copy for closure
tasks.Go(func() error { tasks.Go(func() error {
responses, err := d.Do(req, timeout, numSends) responses, err := d.Do(req, timeout, numSends)
if err != nil { if err != nil {
@ -68,65 +68,3 @@ 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()
}

View File

@ -7,7 +7,6 @@ import (
"net" "net"
"net/http" "net/http"
"regexp" "regexp"
"sync"
) )
const ( const (
@ -74,25 +73,20 @@ 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 := bufPool.Get().([]byte) buf := make([]byte, maxMessageBytes)
n, peerAddr, err := l.ReadFrom(buf) n, peerAddr, err := l.ReadFrom(buf)
if err != nil { if err != nil {
return err return err
} }
go func() { buf = buf[:n]
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.
reqBuf := trailingWhitespaceRx.ReplaceAllLiteral(buf[:n], crlf) buf = trailingWhitespaceRx.ReplaceAllLiteral(buf, crlf)
req, err := http.ReadRequest(bufio.NewReader(bytes.NewBuffer(reqBuf))) req, err := http.ReadRequest(bufio.NewReader(bytes.NewBuffer(buf)))
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
@ -100,7 +94,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)
} }
} }

View File

@ -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.ClientInterfaceCtx, func(), error) { func httpuClient() (httpu.ClientInterface, 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.ClientInterfaceCtx, 0, len(addrs)) delegates := make([]httpu.ClientInterface, 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.ClientInterfaceCtx, func(), error) {
} }
} }
return httpu.NewMultiClientCtx(delegates), closer, nil return httpu.NewMultiClient(delegates), closer, nil
} }
// localIPv2MCastAddrs returns the set of IPv4 addresses on multicast-able // localIPv2MCastAddrs returns the set of IPv4 addresses on multicast-able

View File

@ -194,13 +194,9 @@ 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"`
} }

View File

@ -2,12 +2,10 @@ package soap
import ( import (
"bytes" "bytes"
"errors"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"reflect" "reflect"
"strings"
"testing" "testing"
) )
@ -89,75 +87,6 @@ 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()

View File

@ -35,15 +35,6 @@ 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
@ -58,64 +49,8 @@ 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: request timeout must be at least 1s") return nil, errors.New("ssdp: maxWaitSeconds must be >= 1")
} }
req := (&http.Request{ req := (&http.Request{
@ -132,13 +67,11 @@ func prepareRequest(ctx context.Context, searchTarget string, maxWaitSeconds int
"ST": []string{searchTarget}, "ST": []string{searchTarget},
}, },
}).WithContext(ctx) }).WithContext(ctx)
return req, nil allResponses, err := httpu.Do(req, time.Duration(maxWaitSeconds)*time.Second+100*time.Millisecond, numSends)
} 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)

View File

@ -23,9 +23,7 @@ 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"
"golang.org/x/exp/maps" "github.com/huin/goupnp/v2alpha/soap/types"
soaptypes "github.com/huin/goupnp/v2alpha/soap/types"
) )
var ( var (
@ -33,9 +31,7 @@ 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", "", upnpresourcesZip = flag.String("upnpresources_zip", "", "Path to upnpresources.zip.")
"Path to upnpresources.zip, downloaded from "+
"https://openconnectivity.org/upnp-specs/upnpresources.zip.")
) )
const soapActionInterface = "SOAPActionInterface" const soapActionInterface = "SOAPActionInterface"
@ -54,14 +50,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)
@ -70,7 +66,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,
@ -81,7 +77,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 {
@ -95,7 +91,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 := soaptypes.TypeMap().Clone() typeMap := types.TypeMap().Clone()
typeMap[soapActionInterface] = typedesc.TypeDesc{ typeMap[soapActionInterface] = typedesc.TypeDesc{
GoType: reflect.TypeOf((*soap.Action)(nil)).Elem(), GoType: reflect.TypeOf((*soap.Action)(nil)).Elem(),
} }
@ -162,8 +158,7 @@ func processService(
return fmt.Errorf("transforming service description: %w", err) return fmt.Errorf("transforming service description: %w", err)
} }
imps := newImports() imps, err := accumulateImports(sd, typeMap)
types, err := accumulateTypes(sd, typeMap, imps)
if err != nil { if err != nil {
return err return err
} }
@ -172,7 +167,6 @@ 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 {
@ -225,44 +219,14 @@ 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 {
@ -275,41 +239,17 @@ 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
} }
// accumulateTypes creates type information, and adds any required imports for func accumulateImports(srvDesc *srvdesc.SCPD, typeMap typedesc.TypeMap) (*imports, error) {
// them. typeNames := make(map[string]bool)
func accumulateTypes( typeNames[soapActionInterface] = true
srvDesc *srvdesc.SCPD,
typeMap typedesc.TypeMap,
imps *imports,
) (*types, error) {
typeNames := make(map[string]struct{})
typeNames[soapActionInterface] = struct{}{}
var stringVarDefs []stringVarDef err := visitTypesSCPD(srvDesc, func(typeName string) {
sortedVarNames := maps.Keys(srvDesc.VariableByName) typeNames[typeName] = true
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
@ -317,7 +257,7 @@ func accumulateTypes(
// 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]struct{}) paths := make(map[string]bool)
for typeName := range typeNames { for typeName := range typeNames {
t, ok := typeMap[typeName] t, ok := typeMap[typeName]
if !ok { if !ok {
@ -325,17 +265,29 @@ func accumulateTypes(
} }
pkgPath := t.GoType.PkgPath() pkgPath := t.GoType.PkgPath()
if pkgPath == "" { if pkgPath == "" {
// Builtin type, no import needed. // Builtin type, ignore.
continue continue
} }
paths[pkgPath] = struct{}{} paths[pkgPath] = true
}
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 in deterministic order. // Generate import aliases.
index := 1
aliasByPath := make(map[string]string, len(paths))
importLines := make([]importItem, 0, len(paths))
for _, path := range sortedPaths { for _, path := range sortedPaths {
imps.getAliasForPath(path) alias := fmt.Sprintf("pkg%d", index)
index++
importLines = append(importLines, importItem{
Alias: alias,
Path: path,
})
aliasByPath[path] = alias
} }
// Populate typeByName. // Populate typeByName.
@ -343,27 +295,28 @@ func accumulateTypes(
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 pkgPath == "" { if alias == "" {
// 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 = imps.getAliasForPath(pkgPath) + "." + td.Name td.Ref = alias + "." + td.Name
} }
typeByName[typeName] = td typeByName[typeName] = td
} }
return &types{ return &imports{
TypeByName: typeByName, TypeByName: typeByName,
StringVarDefs: stringVarDefs, ImportLines: importLines,
}, nil }, nil
} }
type typeVisitor func(sv *srvdesc.StateVariable) type typeVisitor func(typeName string)
// 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.`
@ -382,14 +335,14 @@ func visitTypesAction(action *srvdesc.Action, visitor typeVisitor) error {
if err != nil { if err != nil {
return err return err
} }
visitor(sv) visitor(sv.DataType)
} }
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) visitor(sv.DataType)
} }
return nil return nil
} }

View File

@ -10,9 +10,9 @@ import (
) )
var ( var (
ErrBadDescription = errors.New("bad XML description") BadDescriptionError = errors.New("bad XML description")
ErrMissingDefinition = errors.New("missing definition") MissingDefinitionError = errors.New("missing definition")
ErrUnsupportedDescription = errors.New("unsupported XML description") UnsupportedDescriptionError = 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",
ErrBadDescription, sv.Name) BadDescriptionError, 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",
ErrBadDescription, action.Name) BadDescriptionError, 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", ErrBadDescription) return nil, fmt.Errorf("%w: empty action name", BadDescriptionError)
} }
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",
ErrBadDescription, xmlArg.Name, xmlArg.Direction) BadDescriptionError, 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", ErrBadDescription) return nil, fmt.Errorf("%w: empty argument name", BadDescriptionError)
} }
if xmlArg.RelatedStateVariable == "" { if xmlArg.RelatedStateVariable == "" {
return nil, fmt.Errorf("%w: empty related state variable", ErrBadDescription) return nil, fmt.Errorf("%w: empty related state variable", BadDescriptionError)
} }
return &Argument{ return &Argument{
Action: action, Action: action,
@ -133,32 +133,25 @@ 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", ErrMissingDefinition, arg.RelatedStateVariableName) return nil, fmt.Errorf("%w: state variable %q", MissingDefinitionError, 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", ErrBadDescription) return nil, fmt.Errorf("%w: empty state variable name", BadDescriptionError)
} }
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",
ErrUnsupportedDescription, xmlSV.DataType.Type) UnsupportedDescriptionError, 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
} }

View File

@ -2,8 +2,6 @@ module github.com/huin/goupnp/v2alpha
go 1.18 go 1.18
require github.com/google/go-cmp v0.5.8 require github.com/google/go-cmp v0.5.7
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

View File

@ -2,9 +2,5 @@ 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=

View File

@ -8,52 +8,15 @@ 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 ( var _ HttpClient = &http.Client{}
// ErrSOAP can be used with errors.Is.
ErrSOAP = errors.New("SOAP error")
)
// SOAPError describes an error from this package, potentially including a // HttpClient defines the interface required of an HTTP client. It is a subset of *http.Client.
// lower-level cause. type HttpClient interface {
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)
} }
@ -62,21 +25,22 @@ 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
@ -115,10 +79,8 @@ func (c *Client) Do(
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return &SOAPError{ return fmt.Errorf("SOAP request got HTTP %s (%d)",
description: fmt.Sprintf("SOAP request got HTTP %s (%d)", resp.Status, resp.StatusCode)
resp.Status, resp.StatusCode),
}
} }
return ParseResponseAction(resp, actionOut) return ParseResponseAction(resp, actionOut)
@ -148,10 +110,7 @@ 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 &SOAPError{ return fmt.Errorf("encoding envelope: %w", err)
description: "encoding envelope",
cause: err,
}
} }
req.Body = io.NopCloser(buf) req.Body = io.NopCloser(buf)
@ -172,39 +131,31 @@ func ParseResponseAction(
actionOut *envelope.Action, actionOut *envelope.Action,
) error { ) error {
if resp.Body == nil { if resp.Body == nil {
return &SOAPError{description: "missing HTTP response body"} return errors.New("missing 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 &SOAPError{ return fmt.Errorf("reading response body: %w", err)
description: "reading HTTP response body",
cause: err,
}
} }
if err := envelope.Read(buf, actionOut); err != nil { if err := envelope.Read(buf, actionOut); err != nil {
if errors.Is(err, envelope.ErrFault) { if _, ok := err.(*envelope.Fault); ok {
// Parsed cleanly, got SOAP fault. // Parsed cleanly, got SOAP fault.
return &SOAPError{ return err
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 (total %d bytes): ", dispLen, buf.Len()) truncMessage = fmt.Sprintf("first %d bytes: ", dispLen)
}
return &SOAPError{
description: fmt.Sprintf(
"parsing SOAP response from HTTP body (%s%q)",
truncMessage, buf.Bytes()[:dispLen],
),
cause: err,
} }
return fmt.Errorf(
"parsing response body (%s%q): %w",
truncMessage, buf.Bytes()[:dispLen],
err,
)
} }
return nil return nil

View File

@ -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",

View File

@ -34,7 +34,6 @@ 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
} }
@ -65,7 +64,6 @@ 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
} }
@ -96,7 +94,6 @@ 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
} }
@ -130,9 +127,7 @@ 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
} }
@ -163,7 +158,6 @@ 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
} }
@ -194,7 +188,6 @@ 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
} }
@ -225,7 +218,6 @@ 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
} }
@ -256,7 +248,6 @@ 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
} }
@ -287,7 +278,6 @@ 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
} }
@ -318,7 +308,6 @@ 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
} }
@ -349,7 +338,6 @@ 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
} }
@ -377,9 +365,7 @@ 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
} }
@ -410,7 +396,6 @@ 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
} }
@ -441,7 +426,6 @@ 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
} }
@ -472,7 +456,6 @@ 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
} }
@ -503,7 +486,6 @@ 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
} }
@ -534,7 +516,6 @@ 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
} }
@ -565,7 +546,6 @@ 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
} }
@ -596,7 +576,6 @@ 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
} }

View File

@ -8,35 +8,6 @@ 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.
@ -63,22 +34,14 @@ 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 pkg2.UI2
// NewExternalPort relates to state variable ExternalPort. NewProtocol string
NewExternalPort pkg2.UI2 NewInternalPort pkg2.UI2
// NewProtocol relates to state variable PortMappingProtocol (2 standard allowed values). NewInternalClient string
NewProtocol string NewEnabled pkg2.Boolean
// NewInternalPort relates to state variable InternalPort.
NewInternalPort pkg2.UI2
// NewInternalClient relates to state variable InternalClient.
NewInternalClient string
// NewEnabled relates to state variable PortMappingEnabled.
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
} }
// AddPortMappingResponse contains the "out" args for the "AddPortMapping" action. // AddPortMappingResponse contains the "out" args for the "AddPortMapping" action.
@ -108,9 +71,7 @@ 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
} }
@ -141,12 +102,9 @@ 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
} }
// DeletePortMappingResponse contains the "out" args for the "DeletePortMapping" action. // DeletePortMappingResponse contains the "out" args for the "DeletePortMapping" action.
@ -207,7 +165,6 @@ 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
} }
@ -238,9 +195,7 @@ 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
} }
@ -271,7 +226,6 @@ 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
} }
@ -299,28 +253,19 @@ 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 pkg2.UI2
// NewExternalPort relates to state variable ExternalPort. NewProtocol string
NewExternalPort pkg2.UI2 NewInternalPort pkg2.UI2
// NewProtocol relates to state variable PortMappingProtocol (2 standard allowed values). NewInternalClient string
NewProtocol string NewEnabled pkg2.Boolean
// NewInternalPort relates to state variable InternalPort.
NewInternalPort pkg2.UI2
// NewInternalClient relates to state variable InternalClient.
NewInternalClient string
// NewEnabled relates to state variable PortMappingEnabled.
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
} }
// GetIdleDisconnectTime provides request and response for the action. // GetIdleDisconnectTime provides request and response for the action.
@ -350,7 +295,6 @@ 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
} }
@ -381,9 +325,7 @@ 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
} }
@ -414,10 +356,8 @@ 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
} }
// GetPPPAuthenticationProtocol provides request and response for the action. // GetPPPAuthenticationProtocol provides request and response for the action.
@ -447,7 +387,6 @@ 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
} }
@ -478,7 +417,6 @@ 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
} }
@ -509,7 +447,6 @@ 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
} }
@ -540,7 +477,6 @@ 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
} }
@ -568,26 +504,18 @@ 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 string
// NewInternalClient relates to state variable InternalClient. NewEnabled pkg2.Boolean
NewInternalClient string
// NewEnabled relates to state variable PortMappingEnabled.
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
} }
// GetStatusInfo provides request and response for the action. // GetStatusInfo provides request and response for the action.
@ -617,12 +545,9 @@ 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
} }
// GetUserName provides request and response for the action. // GetUserName provides request and response for the action.
@ -652,7 +577,6 @@ 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
} }
@ -683,7 +607,6 @@ 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
} }
@ -767,7 +690,6 @@ 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
} }
@ -798,7 +720,6 @@ 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
} }
@ -829,7 +750,6 @@ 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
} }
@ -860,7 +780,6 @@ 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
} }

View File

@ -1,6 +1,5 @@
{{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}}
// //
@ -14,28 +13,15 @@ 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 "Types" $Types}} {{- template "action" args "Action" . "Imps" $Imps}}
{{end}} {{end}}
{{- end}} {{- end}}
{{define "action"}} {{define "action"}}
{{- $Imps := .Imps}} {{- $Imps := .Imps}}
{{- $Types := .Types}} {{- $soapActionType := index $Imps.TypeByName "SOAPActionInterface"}}
{{- $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.
@ -57,23 +43,18 @@ 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 "Types" $Types}} {{- template "args" args "Args" .Action.InArgs "Imps" $Imps}}
// {{.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 "Types" $Types}} {{- template "args" args "Args" .Action.OutArgs "Imps" $Imps}}
{{- end}} {{- end}}
{{define "args"}} {{define "args"}}
{{- $Imps := .Imps -}} {{- $Imps := .Imps -}}
{{- $Types := .Types -}}
{ {{- with .Args}} { {{- with .Args}}
{{- range .}} {{- range .}}
{{- $fieldType := index $Types.TypeByName .RelatedStateVariable.DataType}} {{- $fieldType := index $Imps.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 -}} }

View File

@ -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(error) cancel func()
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 := withCancelCause(ctx) ctx, cancel := context.WithCancel(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.err) g.cancel()
} }
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.err) g.cancel()
} }
}) })
} }
@ -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.err) g.cancel()
} }
}) })
} }

View File

@ -1,13 +0,0 @@
// 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)
}

View File

@ -1,14 +0,0 @@
// 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
View File

@ -1,3 +1,3 @@
# golang.org/x/sync v0.5.0 # golang.org/x/sync v0.1.0
## explicit; go 1.18 ## explicit
golang.org/x/sync/errgroup golang.org/x/sync/errgroup