Skip to content
This repository was archived by the owner on Jun 8, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 113 additions & 101 deletions Open.Nat/Upnp/SoapClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

using Open.Nat.Utils;
using System;
using System.Collections.Generic;
using System.Diagnostics;
Expand All @@ -36,16 +37,16 @@

namespace Open.Nat
{
internal class SoapClient
{
private readonly string _serviceType;
private readonly Uri _url;
internal class SoapClient
{
private readonly string _serviceType;
private readonly Uri _url;

public SoapClient(Uri url, string serviceType)
{
_url = url;
_serviceType = serviceType;
}
public SoapClient(Uri url, string serviceType)
{
_url = url;
_serviceType = serviceType;
}

#if NET35
public Task<XmlDocument> InvokeAsync(string operationName, IDictionary<string, object> args)
Expand Down Expand Up @@ -100,38 +101,38 @@ public Task<XmlDocument> InvokeAsync(string operationName, IDictionary<string, o
});
}
#else
public async Task<XmlDocument> InvokeAsync(string operationName, IDictionary<string, object> args)
{
NatDiscoverer.TraceSource.TraceEvent(TraceEventType.Verbose, 0, "SOAPACTION: **{0}** url:{1}", operationName,
_url);
byte[] messageBody = BuildMessageBody(operationName, args);
HttpWebRequest request = BuildHttpWebRequest(operationName, messageBody);
public async Task<XmlDocument> InvokeAsync(string operationName, IDictionary<string, object> args)
{
NatDiscoverer.TraceSource.TraceEvent(TraceEventType.Verbose, 0, "SOAPACTION: **{0}** url:{1}", operationName,
_url);
byte[] messageBody = BuildMessageBody(operationName, args);
HttpWebRequest request = BuildHttpWebRequest(operationName, messageBody);

if (messageBody.Length > 0)
{
using (var stream = await request.GetRequestStreamAsync())
{
await stream.WriteAsync(messageBody, 0, messageBody.Length);
}
}
if (messageBody.Length > 0)
{
using (var stream = await request.GetRequestStreamAsync())
{
await stream.WriteAsync(messageBody, 0, messageBody.Length);
}
}

using(var response = await GetWebResponse(request))
{
var stream = response.GetResponseStream();
var contentLength = response.ContentLength;
using (var response = await GetWebResponse(request))
{
var stream = response.GetResponseStream();
var contentLength = response.ContentLength;

var reader = new StreamReader(stream, Encoding.UTF8);
var reader = new StreamReader(stream, Encoding.UTF8);

var responseBody = contentLength != -1
? reader.ReadAsMany((int) contentLength)
: reader.ReadToEnd();
var responseBody = contentLength != -1
? reader.ReadAsMany((int)contentLength)
: reader.ReadToEnd();

var responseXml = GetXmlDocument(responseBody);
var responseXml = GetXmlDocument(responseBody);

response.Close();
return responseXml;
}
}
response.Close();
return responseXml;
}
}
#endif

#if NET35
Expand Down Expand Up @@ -169,85 +170,96 @@ private static Task<WebResponse> GetWebResponse(WebRequest request)
});
}
#else
private static async Task<WebResponse> GetWebResponse(WebRequest request)
{
WebResponse response;
try
{
response = await request.GetResponseAsync();
}
catch (WebException ex)
{
NatDiscoverer.TraceSource.TraceEvent(TraceEventType.Verbose, 0, "WebException status: {0}", ex.Status);
private static async Task<WebResponse> GetWebResponse(WebRequest request)
{
WebResponse response;
try
{
response = await request.GetResponseAsync();
}
catch (WebException ex)
{
NatDiscoverer.TraceSource.TraceEvent(TraceEventType.Verbose, 0, "WebException status: {0}", ex.Status);

// Even if the request "failed" we need to continue reading the response from the router
response = ex.Response as HttpWebResponse;
// Even if the request "failed" we need to continue reading the response from the router
response = ex.Response as HttpWebResponse;

if (response == null)
throw;
}
return response;
}
if (response == null)
throw;
}
return response;
}
#endif

private HttpWebRequest BuildHttpWebRequest(string operationName, byte[] messageBody)
{
private HttpWebRequest BuildHttpWebRequest(string operationName, byte[] messageBody)
{
#if NET35
var request = (HttpWebRequest)WebRequest.Create(_url);
#else
var request = WebRequest.CreateHttp(_url);
var request = WebRequest.CreateHttp(_url);
#endif
request.KeepAlive = false;
request.Method = "POST";
request.ContentType = "text/xml; charset=\"utf-8\"";
request.Headers.Add("SOAPACTION", "\"" + _serviceType + "#" + operationName + "\"");
request.ContentLength = messageBody.Length;
return request;
}
request.KeepAlive = false;
request.Method = "POST";
request.ContentType = "text/xml; charset=\"utf-8\"";
request.Headers.Add("SOAPACTION", "\"" + _serviceType + "#" + operationName + "\"");
request.ContentLength = messageBody.Length;
return request;
}

private byte[] BuildMessageBody(string operationName, IEnumerable<KeyValuePair<string, object>> args)
{
var sb = new StringBuilder();
sb.AppendLine("<s:Envelope ");
sb.AppendLine(" xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" ");
sb.AppendLine(" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">");
sb.AppendLine(" <s:Body>");
sb.AppendLine(" <u:" + operationName + " xmlns:u=\"" + _serviceType + "\">");
foreach (var a in args)
{
sb.AppendLine(" <" + a.Key + ">" + Convert.ToString(a.Value, CultureInfo.InvariantCulture) +
"</" + a.Key + ">");
}
sb.AppendLine(" </u:" + operationName + ">");
sb.AppendLine(" </s:Body>");
sb.Append("</s:Envelope>\r\n\r\n");
string requestBody = sb.ToString();
private byte[] BuildMessageBody(string operationName, IEnumerable<KeyValuePair<string, object>> args)
{
var sb = new StringBuilder();
sb.AppendLine("<s:Envelope ");
sb.AppendLine(" xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" ");
sb.AppendLine(" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">");
sb.AppendLine(" <s:Body>");
sb.AppendLine(" <u:" + operationName + " xmlns:u=\"" + _serviceType + "\">");
foreach (var a in args)
{
sb.AppendLine(" <" + a.Key + ">" + Convert.ToString(a.Value, CultureInfo.InvariantCulture) +
"</" + a.Key + ">");
}
sb.AppendLine(" </u:" + operationName + ">");
sb.AppendLine(" </s:Body>");
sb.Append("</s:Envelope>\r\n\r\n");
string requestBody = sb.ToString();

byte[] messageBody = Encoding.UTF8.GetBytes(requestBody);
return messageBody;
}
byte[] messageBody = Encoding.UTF8.GetBytes(requestBody);
return messageBody;
}

private XmlDocument GetXmlDocument(string response)
{
XmlNode node;
var doc = new XmlDocument();
doc.LoadXml(response);
private XmlDocument GetXmlDocument(string response)
{
XmlNode node;
var doc = new XmlDocument();

var nsm = new XmlNamespaceManager(doc.NameTable);
// Filter out illegal hex characters
response = response.Replace("\x00", "");

// Error messages should be found under this namespace
nsm.AddNamespace("errorNs", "urn:schemas-upnp-org:control-1-0");
try
{
doc.LoadXml(response);
}
catch (XmlException)
{
return doc;
}

// Check to see if we have a fault code message.
if ((node = doc.SelectSingleNode("//errorNs:UPnPError", nsm)) != null)
{
int code = Convert.ToInt32(node.GetXmlElementText("errorCode"), CultureInfo.InvariantCulture);
string errorMessage = node.GetXmlElementText("errorDescription");
NatDiscoverer.TraceSource.LogWarn("Server failed with error: {0} - {1}", code, errorMessage);
throw new MappingException(code, errorMessage);
}
var nsm = new XmlNamespaceManager(doc.NameTable);

return doc;
}
}
// Error messages should be found under this namespace
nsm.AddNamespace("errorNs", "urn:schemas-upnp-org:control-1-0");

// Check to see if we have a fault code message.
if ((node = doc.SelectSingleNode("//errorNs:UPnPError", nsm)) != null)
{
int code = Convert.ToInt32(node.GetXmlElementText("errorCode"), CultureInfo.InvariantCulture);
string errorMessage = node.GetXmlElementText("errorDescription");
NatDiscoverer.TraceSource.LogWarn("Server failed with error: {0} - {1}", code, errorMessage);
throw new MappingException(code, errorMessage);
}

return doc;
}
}
}
Loading