- signed the Network.Rest Assembly

- corrected bugs on Bonjour
- added a MEF Bonjour catalog (not found yet how a service could import someting, so a service can only be imported)
- added a resolve with timeout in the service resolver interface
master
neonp_cp 2009-02-04 14:11:27 -08:00
parent d71c0b856d
commit 6e72f8a9c3
37 changed files with 947 additions and 42 deletions

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?><ItemProperties xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Properties><Property><Name>svn:mime-type</Name><Value>application/octet-stream</Value></Property></Properties></ItemProperties>

View File

@ -17,6 +17,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Network.Rest", "Rest\Networ
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Network.UPnP.DLNA", "DLNA\Network.UPnP.DLNA.csproj", "{8BF16D75-B05B-48AF-A72D-5CDDC861DFD6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MEF.Bonjour", "MEF.Bonjour\MEF.Bonjour.csproj", "{94D114DE-DA48-4060-9B7A-3059BF75E87D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MEF.Bonjour.Tests", "MEF.Bonjour.Tests\MEF.Bonjour.Tests.csproj", "{770EE8FF-624F-445F-A6ED-B29E2DAFA591}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -55,6 +59,14 @@ Global
{8BF16D75-B05B-48AF-A72D-5CDDC861DFD6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8BF16D75-B05B-48AF-A72D-5CDDC861DFD6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8BF16D75-B05B-48AF-A72D-5CDDC861DFD6}.Release|Any CPU.Build.0 = Release|Any CPU
{94D114DE-DA48-4060-9B7A-3059BF75E87D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{94D114DE-DA48-4060-9B7A-3059BF75E87D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{94D114DE-DA48-4060-9B7A-3059BF75E87D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{94D114DE-DA48-4060-9B7A-3059BF75E87D}.Release|Any CPU.Build.0 = Release|Any CPU
{770EE8FF-624F-445F-A6ED-B29E2DAFA591}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{770EE8FF-624F-445F-A6ED-B29E2DAFA591}.Debug|Any CPU.Build.0 = Debug|Any CPU
{770EE8FF-624F-445F-A6ED-B29E2DAFA591}.Release|Any CPU.ActiveCfg = Release|Any CPU
{770EE8FF-624F-445F-A6ED-B29E2DAFA591}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -18,7 +18,20 @@ namespace Network.Bonjour
addresses = new List<Network.Dns.EndPoint>();
}
public DomainName HostName { get; set; }
private DomainName hostName;
public DomainName HostName
{
get
{
return hostName;
}
set
{
hostName = value;
if (value != null && needsToBeResolvedLater)
Resolve();
}
}
protected DateTime expiration;
public State State { get; set; }
public bool IsOutDated { get { return DateTime.Now > expiration; } }
@ -32,12 +45,19 @@ namespace Network.Bonjour
MDnsClient resolver;
MDnsClient publisher;
AutoResetEvent resolved = new AutoResetEvent(false);
bool needsToBeResolvedLater = false;
public void Resolve()
{
if (resolver != null)
resolver.Stop();
if (HostName == null)
{
needsToBeResolvedLater = true;
return;
}
needsToBeResolvedLater = false;
resolver = MDnsClient.CreateAndResolve(HostName);
resolver.AnswerReceived += client_AnswerReceived;
resolver.Start();
@ -169,10 +189,10 @@ namespace Network.Bonjour
foreach (Network.Dns.EndPoint ep in Addresses)
{
foreach (var address in ep.Addresses)
m.Additionals.Add(new Answer() { Class = Class.IN, DomainName = HostName, Ttl = 120, Type = address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork ? Network.Dns.Type.A : Network.Dns.Type.AAAA, ResponseData = new HostAddress() { Address = address } });
m.Additionals.Add(new Answer() { Class = Class.IN, DomainName = Name + "." + Protocol, Ttl = 120, Type = Network.Dns.Type.SRV, ResponseData = new Srv() { Port = ep.Port, Target = ep.DomainName } });
m.Additionals.Add(new Answer() { Class = Class.IN, DomainName = Name + "." + Protocol, Ttl = 120, Type = Network.Dns.Type.TXT, ResponseData = new Txt() { Properties = properties } });
m.Authorities.Add(new Answer() { Class = Class.IN, DomainName = Protocol, Ttl = 120, Type = Network.Dns.Type.PTR, ResponseData = new Ptr() { DomainName = Name + "." + Protocol } });
m.Additionals.Add(new Answer() { Class = Class.IN, DomainName = HostName, Ttl = 500, Type = address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork ? Network.Dns.Type.A : Network.Dns.Type.AAAA, ResponseData = new HostAddress() { Address = address } });
m.Additionals.Add(new Answer() { Class = Class.IN, DomainName = Name + "." + Protocol, Ttl = 500, Type = Network.Dns.Type.SRV, ResponseData = new Srv() { Port = ep.Port, Target = ep.DomainName } });
m.Additionals.Add(new Answer() { Class = Class.IN, DomainName = Name + "." + Protocol, Ttl = 500, Type = Network.Dns.Type.TXT, ResponseData = new Txt() { Properties = properties } });
m.Authorities.Add(new Answer() { Class = Class.IN, DomainName = Protocol, Ttl = 500, Type = Network.Dns.Type.PTR, ResponseData = new Ptr() { DomainName = Name + "." + Protocol } });
}
publisher.Send(m, m.From);
@ -299,5 +319,17 @@ namespace Network.Bonjour
}
#endregion
#region IEnumerable<KeyValuePair<string,string>> Members
public IEnumerable<KeyValuePair<string, string>> Properties
{
get
{
return properties;
}
}
#endregion
}
}

View File

@ -48,10 +48,16 @@ namespace Network.Bonjour
{
client = MDnsClient.CreateAndResolve(protocol);
client.AnswerReceived += client_AnswerReceived;
client.Start();
}
else
client.Resolve(protocol);
if (!client.IsStarted)
client.Start();
}
public IList<IService> Resolve(string protocol, TimeSpan timeout, int minCountServices, int maxCountServices)
{
return new ResolverHelper().Resolve(this, protocol, timeout, minCountServices, maxCountServices);
}
void client_AnswerReceived(Message m)

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using Network.Rest;
using System.Net;
namespace Network.UPnP.DLNA
{
@ -10,11 +11,11 @@ namespace Network.UPnP.DLNA
{
public Command(string connectionString) : base(connectionString) { }
protected override Network.Rest.HttpRequest BuildRequest()
protected override HttpRequest BuildRequest()
{
HttpRequest request = base.BuildRequest();
request.Headers["User-Agent"] = "Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)";
request.Headers["Content-Type"] = "text/xml; charset=\"utf-8\"";
request.ContentType = "text/xml; charset=\"utf-8\"";
request.Headers["Connection"] = "Close";
request.Headers["Cache-Control"] = "no-cache";
request.Headers["Pragma"] = "no-cache";

View File

@ -5,6 +5,7 @@ using System.Text;
using Network.Rest;
using System.IO;
using System.Xml.Linq;
using System.Net;
namespace DLNA.ContentDirectory
{
@ -12,6 +13,11 @@ namespace DLNA.ContentDirectory
{
public Browse(string connectionString) : base(connectionString) { }
public override void Initialize(HttpRequest request)
{
throw new NotImplementedException();
}
protected override HttpRequest GetRequest()
{
HttpRequest request = BuildRequest();
@ -31,7 +37,7 @@ namespace DLNA.ContentDirectory
new XElement(contentDirectory + "StartingIndex", new XAttribute(dt + "dt", "ui4"), "0"),
new XElement(contentDirectory + "RequestedCount", new XAttribute(dt + "dt", "ui4"), "200"),
new XElement(contentDirectory + "SortCriteria", new XAttribute(dt + "dt", "string"), "")))));
request.Headers["Content-Length"] = (request.Body.Length).ToString();
//request.Headers["Content-Length"] = (request.ContentType.Length).ToString();
return request;
}
}

View File

@ -45,6 +45,7 @@ namespace Network.Dns
bytes.AddRange(DomainName.ToBytes());
bytes.AddRange(Message.ToBytes((ushort)Type));
bytes.AddRange(Message.ToBytes((ushort)Class));
//bytes.AddRange(Message.ToBytes((ushort)(((ushort)(ushort.MaxValue >> 15 << 15)) + (ushort)Class)));
return bytes.ToArray();
}

View File

@ -103,8 +103,9 @@ namespace Network.Dns
public override byte[] ToBytes()
{
List<byte> bytes = new List<byte>();
bytes.AddRange(Message.ToBytes((ushort)4));
bytes.AddRange(Address.GetAddressBytes());
byte[] address = Address.GetAddressBytes();
bytes.AddRange(Message.ToBytes((ushort)address.Length));
bytes.AddRange(address);
return bytes.ToArray();
}

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{770EE8FF-624F-445F-A6ED-B29E2DAFA591}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MEF.Bonjour.Tests</RootNamespace>
<AssemblyName>MEF.Bonjour.Tests</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<StartupObject>MEF.Bonjour.Tests.Program</StartupObject>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition, Version=2009.1.23.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\bin\System.ComponentModel.Composition.dll</HintPath>
</Reference>
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Bonjour.NET\Network.Bonjour.csproj">
<Project>{CA2E95A3-CF9D-4524-85FC-12CAD9D81479}</Project>
<Name>Network.Bonjour</Name>
</ProjectReference>
<ProjectReference Include="..\MEF.Bonjour\MEF.Bonjour.csproj">
<Project>{94D114DE-DA48-4060-9B7A-3059BF75E87D}</Project>
<Name>MEF.Bonjour</Name>
</ProjectReference>
<ProjectReference Include="..\SD\Network.ZeroConf.csproj">
<Project>{2B2D41E0-DBC7-4075-873C-394D02448281}</Project>
<Name>Network.ZeroConf</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Text;
using Network.ZeroConf;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace MEF.Bonjour.Tests
{
public class Program
{
public static void Main()
{
new Program().Run();
}
[Import("_touch-able._tcp")]
private IService service;
public void Run()
{
using (Catalog catalog = new Catalog())
{
CompositionContainer container = new CompositionContainer(catalog);
CompositionBatch batch = new CompositionBatch();
batch.AddPart(this);
container.Compose(batch);
Console.WriteLine(service.Name);
//foreach (IService service in services)
// Console.WriteLine(service.Name);
}
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("MEF.Bonjour.Tests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("MEF.Bonjour.Tests")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2009")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("57230306-094c-4477-a104-d1e08436e8b7")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

86
MEF.Bonjour/Catalog.cs Normal file
View File

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.ComponentModel.Composition.Primitives;
using System.ComponentModel.Composition.Hosting;
using Network.Bonjour;
using Network.ZeroConf;
using System.Linq.Expressions;
namespace MEF.Bonjour
{
public class Catalog : ComposablePartCatalog
{
public TimeSpan TimeOut { get; set; }
BonjourServiceResolver resolver = new BonjourServiceResolver();
public Catalog(TimeSpan timeOut)
{
resolver.ServiceFound += new Network.ZeroConf.ObjectEvent<Network.ZeroConf.IService>(resolver_ServiceFound);
resolver.ServiceRemoved += new Network.ZeroConf.ObjectEvent<Network.ZeroConf.IService>(resolver_ServiceRemoved);
TimeOut = timeOut;
}
public Catalog()
: this(new TimeSpan(0, 0, 30))
{
}
private IDictionary<IService, ServicePartDefinition> parts = new Dictionary<IService, ServicePartDefinition>();
void resolver_ServiceRemoved(Network.ZeroConf.IService item)
{
if (parts.ContainsKey(item))
parts.Remove(item);
}
void resolver_ServiceFound(Network.ZeroConf.IService item)
{
if (!parts.ContainsKey(item))
parts.Add(item, new ServicePartDefinition(item));
}
public override IEnumerable<TempTuple<ComposablePartDefinition, ExportDefinition>> GetExports(ImportDefinition definition)
{
LambdaExpression lambda = definition.Constraint;
if (lambda.Body is BinaryExpression)
{
BinaryExpression binaryExpression = (BinaryExpression)lambda.Body;
if (binaryExpression.Left is MemberExpression && ((MemberExpression)binaryExpression.Left).Member.Name == "ContractName")
{
IList<IService> services = resolver.Resolve((string)((ConstantExpression)binaryExpression.Right).Value, TimeOut, definition.Cardinality == ImportCardinality.ZeroOrOne ? 0 : 1, definition.Cardinality == ImportCardinality.ZeroOrMore ? int.MaxValue : 1);
foreach (IService service in services)
resolver_ServiceFound(service);
}
}
var exports = new List<TempTuple<ComposablePartDefinition, ExportDefinition>>();
foreach (var part in this.Parts.ToArray())
{
foreach (var export in part.ExportDefinitions)
{
if (definition.Constraint.Compile().Invoke(export))
{
exports.Add(new TempTuple<ComposablePartDefinition, ExportDefinition>(part, export));
}
}
}
return exports;
}
public override System.Linq.IQueryable<ComposablePartDefinition> Parts
{
get { return parts.Values.AsQueryable().OfType<ComposablePartDefinition>(); }
}
protected override void Dispose(bool disposing)
{
resolver.ServiceFound -= resolver_ServiceFound;
resolver.ServiceRemoved -= resolver_ServiceRemoved;
if (disposing)
resolver.Dispose();
base.Dispose(disposing);
}
}
}

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{94D114DE-DA48-4060-9B7A-3059BF75E87D}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MEF.Bonjour</RootNamespace>
<AssemblyName>MEF.Bonjour</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition, Version=2009.1.23.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\bin\System.ComponentModel.Composition.dll</HintPath>
</Reference>
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Catalog.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ServicePart.cs" />
<Compile Include="ServicePartDefinition.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Bonjour.NET\Network.Bonjour.csproj">
<Project>{CA2E95A3-CF9D-4524-85FC-12CAD9D81479}</Project>
<Name>Network.Bonjour</Name>
</ProjectReference>
<ProjectReference Include="..\DnsResolver\Network.Dns.csproj">
<Project>{36528547-53DC-46CD-8C97-1BAE69262B00}</Project>
<Name>Network.Dns</Name>
</ProjectReference>
<ProjectReference Include="..\SD\Network.ZeroConf.csproj">
<Project>{2B2D41E0-DBC7-4075-873C-394D02448281}</Project>
<Name>Network.ZeroConf</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("MEF.Bonjour")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("MEF.Bonjour")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2009")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("70f443ff-2ed4-4d49-8f25-d17616114d0b")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition.Primitives;
using Network.Bonjour;
using Network.ZeroConf;
namespace MEF.Bonjour
{
public class ServicePart : ComposablePart
{
IService service;
public ServicePart(IService service)
{
this.service = service;
}
public override IEnumerable<ExportDefinition> ExportDefinitions
{
get { return ExportDefinition(service); }
}
internal static IEnumerable<ExportDefinition> ExportDefinition(IService service)
{
Dictionary<string, object> properties = new Dictionary<string, object>();
foreach (KeyValuePair<string, string> property in service.Properties)
properties.Add(property.Key, property.Value);
if (service.Protocol.EndsWith("."))
service.Protocol = service.Protocol.Substring(0, service.Protocol.Length - 1);
if (service.Protocol.EndsWith(".local"))
service.Protocol = service.Protocol.Substring(0, service.Protocol.Length - 6);
string[] protocols = new string[] { service.Protocol, service.Protocol + ".", service.Protocol + ".local", service.Protocol + ".local." };
foreach (string protocol in protocols)
{
ExportDefinition ed = new ExportDefinition(protocol, properties);
yield return ed;
}
}
public override object GetExportedObject(ExportDefinition definition)
{
return service;
}
public override IEnumerable<ImportDefinition> ImportDefinitions
{
get { return new ImportDefinition[] { }; }
}
public override void SetImport(ImportDefinition definition, IEnumerable<Export> exports)
{
}
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition.Primitives;
using Network.ZeroConf;
namespace MEF.Bonjour
{
public class ServicePartDefinition : ComposablePartDefinition
{
IService service;
public ServicePartDefinition(IService service)
{
this.service = service;
}
public override ComposablePart CreatePart()
{
return new ServicePart(service);
}
public override IEnumerable<ExportDefinition> ExportDefinitions
{
get { return ServicePart.ExportDefinition(service); }
}
public override IEnumerable<ImportDefinition> ImportDefinitions
{
get { return null; }
}
}
}

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?><ItemProperties xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Properties><Property><Name>svn:mime-type</Name><Value>application/octet-stream</Value></Property></Properties></ItemProperties>

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Net;
namespace Network.Rest
{
@ -12,6 +13,12 @@ namespace Network.Rest
Uri = new Uri(connectionString);
}
public Command(HttpRequest request)
{
}
public abstract void Initialize(HttpRequest request);
public Uri Uri { get; protected set; }
protected string Method { get; set; }
@ -19,6 +26,8 @@ namespace Network.Rest
protected virtual HttpRequest BuildRequest()
{
HttpRequest request = new HttpRequest(Uri);
request.KeepAlive = true;
request.ContentType = "text/xml";
request.Method = Method;
return request;
}
@ -28,12 +37,21 @@ namespace Network.Rest
public virtual HttpResponse GetHttpResponse()
{
HttpRequest request = GetRequest();
return request.GetResponse();
return (HttpResponse)request.GetResponse();
}
XmlDocument doc = new XmlDocument();
bool docLoaded = false;
public XmlDocument GetResponse()
{
return GetHttpResponse().Document;
if (!docLoaded)
{
HttpResponse response = GetHttpResponse();
docLoaded = true;
doc.Load(response.Body);
}
return doc;
}
}
}

19
Rest/CommandDescriptor.cs Normal file
View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Network.Rest
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class CommandDescriptorAttribute : Attribute
{
public string Method { get; set; }
public string UriRegex { get; set; }
public CommandDescriptorAttribute(string method)
: base()
{
Method = method;
}
}
}

54
Rest/CommandFactory.cs Normal file
View File

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace Network.Rest
{
public class CommandFactory
{
private IDictionary<string, IDictionary<Regex, Type>> registeredCommands = new Dictionary<string, IDictionary<Regex, Type>>();
public void RegisterCommand<T>()
where T : Command
{
foreach (CommandDescriptorAttribute attribute in typeof(T).GetCustomAttributes(typeof(CommandDescriptorAttribute), true))
{
IDictionary<Regex, Type> dict;
if (!registeredCommands.ContainsKey(attribute.Method))
{
dict = new Dictionary<Regex, Type>();
registeredCommands.Add(attribute.Method, dict);
}
else
dict = registeredCommands[attribute.Method];
if (string.IsNullOrEmpty(attribute.UriRegex))
dict.Add(new Regex("^/.*"), typeof(T));
}
}
public Command GetCommand(HttpRequest request)
{
Command cmd = GetCommandInternal(request);
cmd.Initialize(request);
return cmd;
}
internal Command GetCommandInternal(HttpRequest request)
{
IDictionary<Regex, Type> possibleCommands = registeredCommands[request.Method];
foreach (KeyValuePair<Regex, Type> command in possibleCommands)
{
if (command.Key.IsMatch(request.Uri))
return (Command)Activator.CreateInstance(command.Value, request);
}
throw new NotSupportedException("No command have been registered for this kind of request");
}
public T GetCommand<T>(HttpRequest request)
where T : Command
{
return GetCommand(request) as T;
}
}
}

View File

@ -35,7 +35,19 @@ namespace Network.Rest
public Encoding Encoding { get; set; }
public virtual int ContentLength
{
get
{
if (Headers.ContainsKey("Content-Length"))
return int.Parse(Headers["Content-Length"]);
return 0;
}
set
{
Headers["Content-Length"] = value.ToString();
}
}
public string Host
{

View File

@ -32,9 +32,36 @@ namespace Network.Rest
request.Uri = firstLine[1];
request.HttpVersion = HttpVersion.HTTP11;
request.ReadHeaders(reader);
request.Host = request.Host.Trim();
return request;
}
public override int ContentLength
{
get
{
if (Body.Length > 0 && Headers.ContainsKey("Content-Length"))
return (int)Body.Length;
return base.ContentLength;
}
set
{
base.ContentLength = value;
}
}
public bool KeepAlive
{
get { return Headers["KeepAlive"] == "true"; }
set { Headers["KeepAlive"] = value.ToString(); }
}
public string ContentType
{
get { return (string)Headers["Content-Type"]; }
set { Headers["Content-Type"] = value; }
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
@ -77,12 +104,12 @@ namespace Network.Rest
public HttpResponse GetResponse()
{
Uri uri = new Uri("http://" + Host + Uri);
if (Protocol == TransportProtocol.TCP)
{
TcpClient client = new TcpClient();
Uri uri = new Uri(Uri);
client.Connect(uri.Host, uri.Port != 0 ? uri.Port : 80);
Uri = Uri.Substring(Uri.IndexOf('/', Uri.IndexOf(uri.Host)));
//Uri = Uri.Substring(Uri.IndexOf('/', Uri.IndexOf(uri.Host)));
byte[] requestBytes = GetBytes();
Uri = uri.ToString();
client.GetStream().Write(requestBytes, 0, requestBytes.Length);
@ -99,12 +126,9 @@ namespace Network.Rest
if (Protocol == TransportProtocol.UDP)
{
UdpClient client = new UdpClient();
Uri uri = new Uri(Uri);
client.Connect(uri.Host, uri.Port != 0 ? uri.Port : 80);
byte[] requestBytes = GetBytes();
client.Send(requestBytes, requestBytes.Length);
byte[] responseBytes = new byte[client.Available - requestBytes.Length];
MemoryStream stream = new MemoryStream();
System.Net.IPEndPoint ep = new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0);
return HttpResponse.FromBytes(client.Receive(ref ep));
}

View File

@ -11,6 +11,7 @@ namespace Network.Rest
{
public HttpResponse()
{
ResponseCode = HttpStatusCode.OK;
}
public static HttpResponse FromBytes(byte[] bytes)
@ -50,8 +51,7 @@ namespace Network.Rest
response.ResponseMessage = string.Join(" ", firstLine, 2, firstLine.Length - 2);
response.ReadHeaders(reader);
StreamWriter sw = new StreamWriter(response.Body);
while (!string.IsNullOrEmpty(line = reader.ReadLine()))
sw.WriteLine(line);
sw.Write(reader.ReadToEnd());
response.Body.Seek(0, SeekOrigin.Begin);
return response;
}

View File

@ -16,6 +16,16 @@
</StartupObject>
<TargetFrameworkSubset>
</TargetFrameworkSubset>
<SccProjectName>
</SccProjectName>
<SccLocalPath>
</SccLocalPath>
<SccAuxPath>
</SccAuxPath>
<SccProvider>
</SccProvider>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>rest.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -35,16 +45,26 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Build.Framework" />
<Reference Include="Microsoft.Build.Utilities" />
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Command.cs" />
<Compile Include="CommandDescriptor.cs" />
<Compile Include="CommandFactory.cs" />
<Compile Include="HttpMessage.cs" />
<Compile Include="HttpResponse.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="HttpRequest.cs" />
<Compile Include="RestServer.cs" />
<Compile Include="RestTask.cs" />
<Compile Include="UdpRestServer.cs" />
</ItemGroup>
<ItemGroup>
<None Include="rest.snk" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

65
Rest/RestServer.cs Normal file
View File

@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
namespace Network.Rest
{
public abstract class RestServer
{
public ushort Port { get; private set; }
public RestServer(ushort port)
{
Port = port;
}
public bool IsStarted { get; private set; }
public event EventHandler Started;
public event EventHandler Stopped;
public event EventHandler<RequestEventArgs> RequestReceived;
public void Start()
{
OnStart();
IsStarted = true;
}
public void Stop()
{
OnStop();
IsStarted = false;
}
protected virtual void OnStart()
{
if (Started != null)
Started(this, EventArgs.Empty);
}
protected virtual void OnStop()
{
if (Stopped != null)
Stopped(this, EventArgs.Empty);
}
public void OnRequestReceived(RequestEventArgs rea)
{
if (RequestReceived != null)
RequestReceived(this, rea);
}
}
public class RequestEventArgs : EventArgs
{
public RequestEventArgs()
{
Response = new HttpResponse();
}
public HttpRequest Request { get; set; }
public HttpResponse Response { get; set; }
public IPEndPoint Host { get; set; }
}
}

42
Rest/RestTask.cs Normal file
View File

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using System.Net;
namespace Network.Rest
{
public class RestTask : Task
{
public RestTask() { }
[Required]
public string ConnectionString { get; set; }
public string Command { get; set; }
protected HttpResponse ExecuteRequest()
{
Command command = (Command)Activator.CreateInstance(Type.GetType(Command), ConnectionString);
BuildCommand(command);
return command.GetHttpResponse();
}
protected virtual void BuildCommand(Command command)
{
}
public override bool Execute()
{
//return ExecuteRequest().ResponseCode == System.Net.HttpStatusCode.OK;
try
{
ExecuteRequest();
return true;
}
catch { return false; }
}
}
}

89
Rest/UdpRestServer.cs Normal file
View File

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.IO;
namespace Network.Rest
{
public class UdpRestServer : RestServer
{
public UdpClient client;
public UdpRestServer(ushort port)
: base(port)
{
client = new UdpClient(port);
}
public UdpRestServer(IPAddress hostAddress, ushort port)
: base(port)
{
if (!IsMulticast(hostAddress))
client = new UdpClient(new IPEndPoint(hostAddress, port));
else
{
client = new UdpClient(port);
client.JoinMulticastGroup(hostAddress);
}
}
private bool IsMulticast(IPAddress hostAddress)
{
if (hostAddress.IsIPv6Multicast)
return true;
byte[] addressBytes = hostAddress.GetAddressBytes();
if (addressBytes[0] >= 224 && addressBytes[0] <= 239)
return true;
return false;
}
protected override void OnStart()
{
client.BeginReceive(ReceiveRequest, null);
base.OnStart();
}
private void ReceiveRequest(IAsyncResult result)
{
RequestEventArgs rea = new RequestEventArgs();
IPEndPoint ep = new IPEndPoint(IPAddress.Any, Port);
byte[] requestBytes = client.EndReceive(result, ref ep);
if (IsStarted)
client.BeginReceive(ReceiveRequest, null);
HttpRequest request = HttpRequest.FromBytes(requestBytes);
rea.Request = request;
rea.Host = ep;
try
{
OnRequestReceived(rea);
Reply(ep, rea.Response);
}
catch (NotImplementedException e)
{
ReplyError(ep, e, HttpStatusCode.NotImplemented);
}
catch (Exception e)
{
ReplyError(ep, e, HttpStatusCode.BadRequest);
}
}
private void ReplyError(IPEndPoint ep, Exception e, HttpStatusCode code)
{
HttpResponse response = new HttpResponse();
StreamWriter sw = new StreamWriter(response.Body);
sw.Write(e.ToString());
response.ResponseCode = HttpStatusCode.NotImplemented;
response.ResponseMessage = e.Message;
Reply(ep, response);
}
private void Reply(IPEndPoint ep, HttpResponse response)
{
byte[] responseBytes = response.GetBytes();
client.Send(responseBytes, responseBytes.Length, ep);
}
}
}

BIN
Rest/rest.snk Normal file

Binary file not shown.

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Network.ZeroConf
{
@ -46,6 +47,11 @@ namespace Network.ZeroConf
resolvers.ForEach(delegate(IServiceResolver resolver) { resolver.Resolve(protocol); });
}
public IList<IService> Resolve(string protocol, TimeSpan timeout, int minCountServices, int maxCountServices)
{
return new ResolverHelper().Resolve(this, protocol, timeout, 0, 10);
}
#endregion
#region IDisposable Members

View File

@ -7,11 +7,13 @@ namespace Network.ZeroConf
{
public interface IService : IExpirable
{
DomainName HostName { get; }
DomainName HostName { get; set; }
IList<EndPoint> Addresses { get; }
void AddAddress(EndPoint ep);
string Protocol { get; set; }
string Name { get; set; }
string this[string key] { get; set; }
IEnumerable<KeyValuePair<string, string>> Properties { get; }
State State { get; }
bool IsOutDated { get; }
void Publish();

View File

@ -12,5 +12,6 @@ namespace Network.ZeroConf
event ObjectEvent<IService> ServiceFound;
event ObjectEvent<IService> ServiceRemoved;
void Resolve(string protocol);
IList<IService> Resolve(string protocol, TimeSpan timeout, int minCountServices, int maxCountServices);
}
}

View File

@ -44,6 +44,7 @@
<Compile Include="IService.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="IServiceResolver.cs" />
<Compile Include="ResolverHelper.cs" />
<Compile Include="TtlCollection.cs" />
</ItemGroup>
<ItemGroup>

37
SD/ResolverHelper.cs Normal file
View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Network.ZeroConf
{
public class ResolverHelper
{
private ManualResetEvent syncLock = new ManualResetEvent(false);
private IList<IService> services = new List<IService>();
public IList<IService> Resolve(IServiceResolver resolver, string protocol, TimeSpan timeout, int minServiceCountFound, int maxServiceCountFound)
{
resolver.ServiceFound += new ObjectEvent<IService>(resolver_ServiceFound);
resolver.ServiceRemoved += new ObjectEvent<IService>(resolver_ServiceRemoved);
resolver.Resolve(protocol);
for (int i = minServiceCountFound; i <= maxServiceCountFound; i++)
syncLock.WaitOne(timeout);
resolver.ServiceFound -= resolver_ServiceFound;
resolver.ServiceRemoved -= resolver_ServiceRemoved;
return services;
}
void resolver_ServiceRemoved(IService item)
{
services.Remove(item);
}
void resolver_ServiceFound(IService item)
{
services.Add(item);
syncLock.Set();
}
}
}

Binary file not shown.

View File

@ -20,7 +20,9 @@ namespace Network.UPnP
public Network.Dns.DomainName HostName { get; set; }
public IList<Network.Dns.EndPoint> Addresses { get; set; }
public IList<Network.Dns.EndPoint> Addresses { get; private set; }
public void AddAddress(Network.Dns.EndPoint ep) { Addresses.Add(ep); }
public string Protocol
{
@ -39,7 +41,7 @@ namespace Network.UPnP
get
{
//if (properties.ContainsKey(key))
return properties[key];
return properties[key];
//return Txt.False;
}
set
@ -193,5 +195,17 @@ namespace Network.UPnP
}
#endregion
#region IEnumerable<KeyValuePair<string,string>> Members
public IEnumerable<KeyValuePair<string, string>> Properties
{
get
{
return properties;
}
}
#endregion
}
}

View File

@ -94,5 +94,15 @@ namespace Network.UPnP
}
#endregion
#region IServiceResolver Members
public IList<IService> Resolve(string protocol, TimeSpan timeout, int minCountServices, int maxCountServices)
{
return new ResolverHelper().Resolve(this, protocol, timeout, minCountServices, maxCountServices);
}
#endregion
}
}

View File

@ -13,24 +13,24 @@ namespace mDNSReader
{
static void Main(string[] args)
{
//BonjourServiceResolver bsr = new BonjourServiceResolver();
//bsr.ServiceFound += new Network.ZeroConf.ObjectEvent<Network.ZeroConf.IService>(bsr_ServiceFound);
//bsr.Resolve("_http._tcp.");
Service s = new Service();
s.AddAddress(new Network.Dns.EndPoint() { DomainName = "ASPERGE.local.", Port = 50508, Addresses = new List<IPAddress>() { IPAddress.Parse("192.168.1.19") } });
s.Protocol = "_touch-remote._tcp.local.";
s.Name = "MyName";
s.HostName = "ASPERGE.local.";
s["DvNm"] = "PC Remote";
s["RemV"] = "10000";
s["DvTy"] = "iPod";
s["RemN"] = "Remote";
s["txtvers"] = "1";
s["Pair"] = "0000000000000001";
s.Publish();
Thread.Sleep(3600000);
s.Stop();
BonjourServiceResolver bsr = new BonjourServiceResolver();
bsr.ServiceFound += new Network.ZeroConf.ObjectEvent<Network.ZeroConf.IService>(bsr_ServiceFound);
bsr.Resolve("_touch-able._tcp.");
Console.ReadLine();
//Service s = new Service();
//s.AddAddress(new Network.Dns.EndPoint() { DomainName = "ASPERGE.local.", Port = 50508, Addresses = new List<IPAddress>() { IPAddress.Parse("192.168.1.19") } });
//s.Protocol = "_touch-remote._tcp.local.";
//s.Name = "MyName";
//s.HostName = "ASPERGE.local.";
//s["DvNm"] = "PC Remote";
//s["RemV"] = "10000";
//s["DvTy"] = "iPod";
//s["RemN"] = "Remote";
//s["txtvers"] = "1";
//s["Pair"] = "0000000000000001";
//s.Publish();
//Thread.Sleep(3600000);
//s.Stop();
}
static void bsr_ServiceFound(Network.ZeroConf.IService item)