Add SOAP action support.
This commit is contained in:
parent
489c180de9
commit
1b8d66b887
@ -19,7 +19,16 @@ func (i indentLevel) String() string {
|
||||
func displayDevice(indent indentLevel, device *goupnp.Device) {
|
||||
fmt.Println(indent.String(), device)
|
||||
for _, srv := range device.Services {
|
||||
fmt.Println((indent + 1).String(), srv)
|
||||
fmt.Println((indent + 1).String(), srv, srv.SCPDURL.URL.String(), srv.ControlURL.URL.String())
|
||||
fmt.Println(goupnp.ServiceTypeWANPPPConnection, srv.ServiceType)
|
||||
if srv.ServiceType == goupnp.ServiceTypeWANPPPConnection {
|
||||
results, err := goupnp.PerformSoapAction(goupnp.ServiceTypeWANPPPConnection, "GetExternalIPAddress", &srv.ControlURL.URL, nil)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Println(results)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, subdev := range device.Devices {
|
||||
displayDevice(indent+1, subdev)
|
||||
|
@ -11,11 +11,11 @@ import (
|
||||
// Non-exhaustive set of UPnP service types.
|
||||
const (
|
||||
ServiceTypeLayer3Forwarding = "urn:schemas-upnp-org:service:Layer3Forwarding:1"
|
||||
ServiceTypeWANCommonInterfaceConfig = "urn:schemas-upnp-org:WANCommonInterfaceConfig:1"
|
||||
ServiceTypeWANCommonInterfaceConfig = "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"
|
||||
// WANPPPConnection is typically useful with regard to the external IP and
|
||||
// port forwarding.
|
||||
// http://upnp.org/specs/gw/UPnP-gw-WANPPPConnection-v1-Service.pdf
|
||||
ServiceTypeWANPPPConnection = "urn:schemas-upnp-org:WANPPPConnection:1"
|
||||
ServiceTypeWANPPPConnection = "urn:schemas-upnp-org:service:WANPPPConnection:1"
|
||||
)
|
||||
|
||||
// Non-exhaustive set of UPnP device types.
|
||||
|
107
soap.go
Normal file
107
soap.go
Normal file
@ -0,0 +1,107 @@
|
||||
// Definition for the SOAP structure required for UPnP's SOAP usage.
|
||||
|
||||
package goupnp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
const (
|
||||
SoapEncodingStyle = "http://schemas.xmlsoap.org/soap/encoding/"
|
||||
)
|
||||
|
||||
type NameValue struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
// NewSoapAction creates a SoapEnvelope with the given action and arguments.
|
||||
func newSoapAction(actionNamespace, actionName string, arguments []NameValue) *SoapEnvelope {
|
||||
env := &SoapEnvelope{
|
||||
EncodingStyle: SoapEncodingStyle,
|
||||
Body: SoapBody{
|
||||
Action: SoapAction{
|
||||
XMLName: xml.Name{actionNamespace, actionName},
|
||||
Arguments: make([]SoapArgument, len(arguments)),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i := range arguments {
|
||||
env.Body.Action.Arguments[i].XMLName.Local = arguments[i].Name
|
||||
env.Body.Action.Arguments[i].Value = arguments[i].Value
|
||||
}
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
// PerformSoapAction makes a SOAP request, with the given action.
|
||||
func PerformSoapAction(actionNamespace, actionName string, url *url.URL, arguments []NameValue) ([]NameValue, error) {
|
||||
requestEnv := newSoapAction(actionNamespace, actionName, arguments)
|
||||
requestBytes, err := xml.Marshal(requestEnv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := http.Client{}
|
||||
response, err := client.Do(&http.Request{
|
||||
Method: "POST",
|
||||
URL: url,
|
||||
Header: http.Header{
|
||||
"SOAPACTION": []string{actionNamespace + "#" + actionName},
|
||||
"CONTENT-TYPE": []string{"text/xml; charset=\"utf-8\""},
|
||||
},
|
||||
Body: ioutil.NopCloser(bytes.NewBuffer(requestBytes)),
|
||||
// Set ContentLength to avoid chunked encoding - some servers might not support it.
|
||||
ContentLength: int64(len(requestBytes)),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if response.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("goupnp: SOAP request got %s response from %s", response.Status, url.String())
|
||||
}
|
||||
|
||||
decoder := xml.NewDecoder(response.Body)
|
||||
fmt.Println(response.Header)
|
||||
var responseEnv SoapEnvelope
|
||||
if err := decoder.Decode(&responseEnv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
results := make([]NameValue, len(responseEnv.Body.Action.Arguments))
|
||||
for i, soapArg := range responseEnv.Body.Action.Arguments {
|
||||
results[i] = NameValue{
|
||||
Name: soapArg.XMLName.Local,
|
||||
Value: soapArg.Value,
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
type SoapEnvelope struct {
|
||||
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
|
||||
EncodingStyle string `xml:"http://schemas.xmlsoap.org/soap/envelope/ encodingStyle,attr"`
|
||||
Body SoapBody `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
|
||||
}
|
||||
|
||||
type SoapBody struct {
|
||||
Action SoapAction `xml:",any"`
|
||||
}
|
||||
|
||||
type SoapAction struct {
|
||||
XMLName xml.Name
|
||||
Arguments []SoapArgument `xml:",any"`
|
||||
}
|
||||
|
||||
type SoapArgument struct {
|
||||
XMLName xml.Name
|
||||
Value string `xml:",chardata"`
|
||||
}
|
Loading…
Reference in New Issue
Block a user