/// <summary> /// Encodes a call of the specified <paramref name="action"/> with the given <paramref name="inParamValues"/> and /// returns the resulting SOAP XML string. /// </summary> /// <param name="action">Action to be called.</param> /// <param name="inParamValues">List of parameter values which must match the action's signature. /// Can be <c>null</c> if the parameter list is empty.</param> /// <param name="upnpVersion">UPnP version to use for the encoding.</param> /// <returns>XML string which contains the SOAP document.</returns> public static string EncodeCall(CpAction action, IList<object> inParamValues, UPnPVersion upnpVersion) { bool targetSupportsUPnP11 = upnpVersion.VerMin >= 1; StringBuilder result = new StringBuilder(5000); using (StringWriterWithEncoding stringWriter = new StringWriterWithEncoding(result, UPnPConsts.UTF8_NO_BOM)) using (XmlWriter writer = XmlWriter.Create(stringWriter, UPnPConfiguration.DEFAULT_XML_WRITER_SETTINGS)) { SoapHelper.WriteSoapEnvelopeStart(writer, true); writer.WriteStartElement("u", action.Name, action.ParentService.ServiceTypeVersion_URN); // Check input parameters IList<CpArgument> formalArguments = action.InArguments; if (inParamValues == null) inParamValues = EMPTY_OBJECT_LIST; if (inParamValues.Count != formalArguments.Count) throw new ArgumentException("Invalid argument count"); for (int i = 0; i < formalArguments.Count; i++) { CpArgument argument = formalArguments[i]; object value = inParamValues[i]; writer.WriteStartElement(argument.Name); argument.SoapSerializeArgument(value, !targetSupportsUPnP11, writer); writer.WriteEndElement(); // argument.Name } SoapHelper.WriteSoapEnvelopeEndAndClose(writer); } return result.ToString(); }
protected CpStateVariable _relatedStateVariable; // References the related state variable in our parent action's parent service public CpArgument(CpAction parentAction, string name, CpStateVariable relatedStateVariable, ArgumentDirection direction, bool isReturnValue) { _parentAction = parentAction; _name = name; _relatedStateVariable = relatedStateVariable; _direction = direction; _isReturnValue = isReturnValue; }
internal static CpArgument CreateArgument(CpAction parentAction, CpService parentService, XPathNavigator argumentNav, IXmlNamespaceResolver nsmgr) { string name = ParserHelper.SelectText(argumentNav, "s:name/text()", nsmgr); string relatedStateVariableName = ParserHelper.SelectText(argumentNav, "s:relatedStateVariable/text()", nsmgr); CpStateVariable relatedStateVariable; if (!parentService.StateVariables.TryGetValue(relatedStateVariableName, out relatedStateVariable)) { throw new ArgumentException("Related state variable '{0}' is not present in service", relatedStateVariableName); } string direction = ParserHelper.SelectText(argumentNav, "s:direction/text()", nsmgr); XPathNodeIterator retValIt = argumentNav.Select("s:retval", nsmgr); CpArgument result = new CpArgument(parentAction, name, relatedStateVariable, ParseArgumentDirection(direction), retValIt.MoveNext()); return(result); }
internal static CpAction ConnectAction(DeviceConnection connection, CpService parentService, XPathNavigator actionNav, IXmlNamespaceResolver nsmgr) { using (connection.CPData.Lock.EnterWrite()) { string name = ParserHelper.SelectText(actionNav, "s:name/text()", nsmgr); CpAction result = new CpAction(connection, parentService, name); XPathNodeIterator argumentIt = actionNav.Select("s:argumentList/s:argument", nsmgr); while (argumentIt.MoveNext()) { CpArgument argument = CpArgument.CreateArgument(result, parentService, argumentIt.Current, nsmgr); if (argument.Direction == ArgumentDirection.In) { result.AddInAgrument(argument); } else { result.AddOutAgrument(argument); } } return(result); } }
/// <summary> /// Takes the XML document provided by the given <paramref name="body"/> stream, parses it in the given /// <paramref name="contentEncoding"/> and provides the action result to the appropriate receiver. /// </summary> /// <param name="body">Body stream of the SOAP XML action result message.</param> /// <param name="contentEncoding">Encoding of the body stream.</param> /// <param name="action">Action which was called before.</param> /// <param name="clientState">State object which was given in the action call and which will be returned to the client.</param> /// <param name="upnpVersion">UPnP version of the UPnP server.</param> public static void HandleResult(Stream body, Encoding contentEncoding, CpAction action, object clientState, UPnPVersion upnpVersion) { bool sourceSupportsUPnP11 = upnpVersion.VerMin >= 1; IList<object> outParameterValues; try { if (!body.CanRead) { UPnPConfiguration.LOGGER.Error("SOAPHandler: Empty action result document"); action.ActionErrorResultPresent(new UPnPError(501, "Invalid server result"), clientState); return; } using (TextReader textReader = new StreamReader(body, contentEncoding)) outParameterValues = ParseResult(textReader, action, sourceSupportsUPnP11); } catch (Exception e) { UPnPConfiguration.LOGGER.Error("SOAPHandler: Error parsing action result document", e); action.ActionErrorResultPresent(new UPnPError(501, "Invalid server result"), clientState); return; } try { // Invoke action result action.ActionResultPresent(outParameterValues, clientState); } catch (Exception e) { UPnPConfiguration.LOGGER.Error("UPnP subsystem: Error invoking action '{0}'", e, action.FullQualifiedName); } }
protected static IList<object> ParseResult(TextReader textReader, CpAction action, bool sourceSupportsUPnP11) { object[] outParameterValues = new object[action.OutArguments.Count]; using(XmlReader reader = XmlReader.Create(textReader, UPnPConfiguration.DEFAULT_XML_READER_SETTINGS)) { reader.MoveToContent(); // Parse SOAP envelope reader.ReadStartElement("Envelope", UPnPConsts.NS_SOAP_ENVELOPE); reader.ReadStartElement("Body", UPnPConsts.NS_SOAP_ENVELOPE); // Reader is positioned at the action element string serviceTypeVersion_URN = reader.NamespaceURI; string type; int version; // Parse service and action if (!ParserHelper.TryParseTypeVersion_URN(serviceTypeVersion_URN, out type, out version)) throw new ArgumentException("Invalid service type or version"); string actionName = reader.LocalName; if (!actionName.EndsWith("Response") || actionName.Substring(0, actionName.Length - "Response".Length) != action.Name) throw new ArgumentException("Invalid action name in result message"); // UPnP spec says we have to be able to handle return values being out // of order to support UPnP 1.0 devices. See UPnP-arch-DeviceArchitecture-v1.1 // section 2.5.4. We need a dictionary to make this easy. IDictionary<string, int> formalArgIdxDictionary = new Dictionary<string, int>(); for (int i = 0; i < action.OutArguments.Count; i++) formalArgIdxDictionary.Add(action.OutArguments[i].Name, i); int outArgCount = 0; if (!SoapHelper.ReadEmptyStartElement(reader)) // Parse and check output parameters while (reader.NodeType != XmlNodeType.EndElement) { string argumentName = reader.Name; // Arguments don't have a namespace, so take full name int formalArgumentIndex; if (!formalArgIdxDictionary.TryGetValue(argumentName, out formalArgumentIndex)) throw new ArgumentException("Invalid argument name"); CpArgument formalArgument = action.OutArguments[formalArgumentIndex]; // Get the argument value and store it in the correct position in the return list. object value = null; if (!SoapHelper.ReadNull(reader)) formalArgument.SoapParseArgument(reader, !sourceSupportsUPnP11, out value); outParameterValues[formalArgumentIndex] = value; outArgCount++; // Don't allow duplicates of the same argument. formalArgIdxDictionary.Remove(formalArgument.Name); } if (outArgCount != action.OutArguments.Count) // Too few arguments throw new ArgumentException("Invalid out argument count"); } return outParameterValues; }
public static void ActionFailed(CpAction action, object clientState, string errorDescription) { action.ActionErrorResultPresent(new UPnPError(501, errorDescription), clientState); }
public static void HandleErrorResult(TextReader textReader, CpAction action, object clientState) { try { uint errorCode; string errorDescription; ParseFaultDocument(textReader, out errorCode, out errorDescription); action.ActionErrorResultPresent(new UPnPError(errorCode, errorDescription), clientState); } catch (Exception e) { UPnPConfiguration.LOGGER.Error("SOAPHandler: Error parsing action error result document", e); ActionFailed(action, clientState, "Invalid server result"); } }
protected HttpWebRequest CreateActionCallRequest(ServiceDescriptor sd, CpAction action) { LinkData preferredLink = sd.RootDescriptor.SSDPRootEntry.PreferredLink; HttpWebRequest request = (HttpWebRequest) WebRequest.Create(new Uri( new Uri(preferredLink.DescriptionLocation), sd.ControlURL)); NetworkUtils.SetLocalEndpoint(request, preferredLink.Endpoint.EndPointIPAddress); request.Method = "POST"; request.KeepAlive = _useHttpKeepAlive; request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore); request.ServicePoint.Expect100Continue = false; request.AllowAutoRedirect = true; request.UserAgent = UPnPConfiguration.UPnPMachineInfoHeader; request.ContentType = "text/xml; charset=\"utf-8\""; request.Headers.Add("SOAPACTION", '"' + action.Action_URN + '"'); request.Headers.Add("Accept-Encoding", CompressionHelper.GetAcceptedEncodings()); return request; }
internal void OnActionCalled(CpAction action, IList<object> inParams, object clientState) { if (!action.IsConnected) throw new UPnPDisconnectedException("Action '{0}' is not connected to a UPnP network action", action.FullQualifiedName); CpService service = action.ParentService; ServiceDescriptor sd = GetServiceDescriptor(service); string message = SOAPHandler.EncodeCall(action, inParams, _rootDescriptor.SSDPRootEntry.UPnPVersion); HttpWebRequest request = CreateActionCallRequest(sd, action); ActionCallState state = new ActionCallState(action, clientState, request); state.SetRequestMessage(message); lock (_cpData.SyncObj) _pendingCalls.Add(state); IAsyncResult result = state.Request.BeginGetResponse(OnCallResponseReceived, state); NetworkHelper.AddTimeout(request, result, PENDING_ACTION_CALL_TIMEOUT * 1000); }
/// <summary> /// Adds the specified <paramref name="action"/> instance to match to this service template. /// </summary> /// <param name="action">Action template to be added.</param> internal void AddAction(CpAction action) { _actions.Add(action.Name, action); }
internal static CpAction ConnectAction(DeviceConnection connection, CpService parentService, XPathNavigator actionNav, IXmlNamespaceResolver nsmgr) { lock (connection.CPData.SyncObj) { string name = ParserHelper.SelectText(actionNav, "s:name/text()", nsmgr); CpAction result = new CpAction(connection, parentService, name); XPathNodeIterator argumentIt = actionNav.Select("s:argumentList/s:argument", nsmgr); while (argumentIt.MoveNext()) { CpArgument argument = CpArgument.CreateArgument(result, parentService, argumentIt.Current, nsmgr); if (argument.Direction == ArgumentDirection.In) result.AddInAgrument(argument); else result.AddOutAgrument(argument); } return result; } }
public CpArgument(CpAction parentAction, string name, CpStateVariable relatedStateVariable, ArgumentDirection direction) : this(parentAction, name, relatedStateVariable, direction, false) { }
public ActionCallState(CpAction action, object clientState, HttpWebRequest request) : base(request) { _action = action; _clientState = clientState; }
internal static CpArgument CreateArgument(CpAction parentAction, CpService parentService, XPathNavigator argumentNav, IXmlNamespaceResolver nsmgr) { string name = ParserHelper.SelectText(argumentNav, "s:name/text()", nsmgr); string relatedStateVariableName = ParserHelper.SelectText(argumentNav, "s:relatedStateVariable/text()", nsmgr); CpStateVariable relatedStateVariable; if (!parentService.StateVariables.TryGetValue(relatedStateVariableName, out relatedStateVariable)) throw new ArgumentException("Related state variable '{0}' is not present in service", relatedStateVariableName); string direction = ParserHelper.SelectText(argumentNav, "s:direction/text()", nsmgr); XPathNodeIterator retValIt = argumentNav.Select("s:retval", nsmgr); CpArgument result = new CpArgument(parentAction, name, relatedStateVariable, ParseArgumentDirection(direction), retValIt.MoveNext()); return result; }
protected static IList<object> ParseResult(TextReader textReader, CpAction action, bool sourceSupportsUPnP11) { IList<object> outParameterValues = new List<object>(); using(XmlReader reader = XmlReader.Create(textReader, UPnPConfiguration.DEFAULT_XML_READER_SETTINGS)) { reader.MoveToContent(); // Parse SOAP envelope reader.ReadStartElement("Envelope", UPnPConsts.NS_SOAP_ENVELOPE); reader.ReadStartElement("Body", UPnPConsts.NS_SOAP_ENVELOPE); // Reader is positioned at the action element string serviceTypeVersion_URN = reader.NamespaceURI; string type; int version; // Parse service and action if (!ParserHelper.TryParseTypeVersion_URN(serviceTypeVersion_URN, out type, out version)) throw new ArgumentException("Invalid service type or version"); string actionName = reader.LocalName; if (!actionName.EndsWith("Response") || actionName.Substring(0, actionName.Length - "Response".Length) != action.Name) throw new ArgumentException("Invalid action name in result message"); IEnumerator<CpArgument> formalArgumentEnumer = action.OutArguments.GetEnumerator(); if (!SoapHelper.ReadEmptyStartElement(reader)) // Parse and check output parameters while (reader.NodeType != XmlNodeType.EndElement) { string argumentName = reader.Name; // Arguments don't have a namespace, so take full name if (!formalArgumentEnumer.MoveNext()) // Too many arguments throw new ArgumentException("Invalid out argument count"); if (formalArgumentEnumer.Current.Name != argumentName) throw new ArgumentException("Invalid argument name"); object value; if (SoapHelper.ReadNull(reader)) value = null; else formalArgumentEnumer.Current.SoapParseArgument(reader, !sourceSupportsUPnP11, out value); outParameterValues.Add(value); } if (formalArgumentEnumer.MoveNext()) // Too few arguments throw new ArgumentException("Invalid out argument count"); } return outParameterValues; }
protected static HttpWebRequest CreateActionCallRequest(ServiceDescriptor sd, CpAction action) { LinkData preferredLink = sd.RootDescriptor.SSDPRootEntry.PreferredLink; HttpWebRequest request = (HttpWebRequest) WebRequest.Create(new Uri( new Uri(preferredLink.DescriptionLocation), sd.ControlURL)); NetworkUtils.SetLocalEndpoint(request, preferredLink.Endpoint.EndPointIPAddress); request.Method = "POST"; request.KeepAlive = true; request.AllowAutoRedirect = true; request.UserAgent = UPnPConfiguration.UPnPMachineInfoHeader; request.ContentType = "text/xml; charset=\"utf-8\""; request.Headers.Add("SOAPACTION", action.Action_URN); return request; }