예제 #1
0
        /// <summary>
        /// Removes a record from cache and registry and invalidates results cache.
        /// </summary>
        /// <param name="record"></param>
        /// <param name="ct"></param>
        /// <returns></returns>
        public async Task RemoveAsync(INameRecord record, CancellationToken ct)
        {
            _cache.TryRemove(record.Id, out record);
            await RemoveRecordAsync(record, ct);

            base.Invalidate();
        }
예제 #2
0
        /// <summary>
        /// Creates a device in the IoT Hub device registry
        /// </summary>
        /// <param name="record"></param>
        /// <param name="ct"></param>
        /// <returns></returns>
        internal async Task <IoTHubRecord> AddRecordAsync(INameRecord record,
                                                          CancellationToken ct)
        {
            try {
                await _http.CallAsync(CreateUri("/devices/" + record.Id.ToLowerInvariant()), Http.Put,
                                      async h => {
                    h.Add(HttpRequestHeader.Authorization.ToString(),
                          await GetSasTokenAsync(3600).ConfigureAwait(false));
                    h.Add(HttpRequestHeader.UserAgent.ToString(), _clientId);
                }, (sc, h) => {
                    if (sc == HttpStatusCode.Conflict || sc == HttpStatusCode.PreconditionFailed)
                    {
                        throw new TransientException();
                    }
                }, ct,
                                      @"{""deviceId"": """ + record.Id + @"""}", "application/json").ConfigureAwait(false);

                ProxyEventSource.Log.RecordAdded(this, record);
                return(new IoTHubRecord(record));
            }
            catch (TransientException) {
                // Retrieve the twin object and update it
                var result = await GetRecordAsync(record, ct);

                if (result == null)
                {
                    return(null);
                }
                result.Assign(record);
                return(result);
            }
            catch (Exception e) {
                throw ProxyEventSource.Log.Rethrow(e, this);
            }
        }
        /// <summary>
        /// Check whether the record is connected
        /// </summary>
        /// <param name="record"></param>
        /// <param name="ct"></param>
        /// <returns></returns>
        internal async Task <bool> IsConnectedAsync(INameRecord record,
                                                    CancellationToken ct)
        {
            try {
                var stream = await _http.StreamAsync(
                    CreateUri("/devices/" + record.Id), Http.Get,
                    async h => {
                    h.Add(HttpRequestHeader.Authorization.ToString(),
                          await IoTHubService.GetSasTokenAsync(_hubConnectionString,
                                                               3600).ConfigureAwait(false));
                    h.Add(HttpRequestHeader.UserAgent.ToString(), _clientId);
                }, (sc, h) => {
                    if (sc == HttpStatusCode.NotFound)
                    {
                        throw new TransientException();
                    }
                }, ct).ConfigureAwait(false);

                using (stream)
                    using (var sr = new StreamReader(stream))
                        using (JsonReader reader = new JsonTextReader(sr)) {
                            var response = (JObject)JToken.ReadFrom(reader);

                            return(response["connectionState"].ToString().Equals(
                                       "Connected", StringComparison.CurrentCultureIgnoreCase));
                        }
            }
            catch (TransientException) {
                return(false);
            }
            catch (Exception e) {
                throw ProxyEventSource.Log.Rethrow(e, this);
            }
        }
        /// <summary>
        /// Retrieves a single device twin based record from IoT Hub
        /// </summary>
        /// <param name="record"></param>
        /// <param name="ct"></param>
        /// <returns></returns>
        internal async Task <IoTHubRecord> GetRecordAsync(INameRecord record,
                                                          CancellationToken ct)
        {
            try {
                var stream = await _http.StreamAsync(
                    CreateUri("/twins/" + record.Id), Http.Get,
                    async h => {
                    h.Add(HttpRequestHeader.Authorization.ToString(),
                          await IoTHubService.GetSasTokenAsync(_hubConnectionString,
                                                               3600).ConfigureAwait(false));
                    h.Add(HttpRequestHeader.UserAgent.ToString(), _clientId);
                }, (sc, h) => {
                    if (sc == HttpStatusCode.NotFound)
                    {
                        throw new TransientException();
                    }
                }, ct).ConfigureAwait(false);

                using (stream)
                    using (var sr = new StreamReader(stream))
                        using (JsonReader reader = new JsonTextReader(sr)) {
                            return(new IoTHubRecord((JObject)JToken.ReadFrom(reader)));
                        }
            }
            catch (TransientException) {
                ProxyEventSource.Log.RecordRemoved(this, record);
                return(null);
            }
            catch (Exception e) {
                throw ProxyEventSource.Log.Rethrow(e, this);
            }
        }
예제 #5
0
 /// <summary>
 /// Copy constructor for record
 /// </summary>
 /// <param name="record"></param>
 public Record(INameRecord record)
 {
     Address = record.Address;
     Type    = record.Type;
     Id      = record.Id;
     Name    = record.Name;
 }
 /// <summary>
 /// Matches proxy records using optional host as guidance for limiting results.
 /// </summary>
 /// <param name="record"></param>
 /// <param name="host"></param>
 /// <returns></returns>
 public static bool IsProxyForHost(this INameRecord record, SocketAddress host)
 {
     if (!record.Type.HasFlag(NameRecordType.Proxy))
     {
         return(false);
     }
     if (host is ProxySocketAddress address)
     {
         if (!string.IsNullOrEmpty(address.Domain))
         {
             if (string.IsNullOrEmpty(record.Domain))
             {
                 // Proxy without domain, do not use it.
                 return(false);
             }
             if (address.Domain == record.Domain)
             {
                 // Same domain
                 return(true);
             }
             // If record domain is a super domain of the address one, also match ok
             return(address.Domain.EndsWith("." + record.Domain,
                                            StringComparison.CurrentCultureIgnoreCase));
         }
         // No domain to match, thus match all.
     }
     return(true);
 }
예제 #7
0
 /// <summary>
 /// Invoke method and return response
 /// </summary>
 /// <param name="record"></param>
 /// <param name="request"></param>
 /// <param name="timeout"></param>
 /// <param name="ct"></param>
 /// <returns></returns>
 internal async Task <Message> InvokeDeviceMethodAsync(INameRecord record, Message request,
                                                       TimeSpan timeout, CancellationToken ct)
 {
     request.Proxy    = record.Address;
     request.DeviceId = record.Id;
     return(await InvokeDeviceMethodAsync(request, timeout, ct).ConfigureAwait(false));
 }
 /// <summary>
 /// Invoke method on proxy
 /// </summary>
 /// <param name="proxy"></param>
 /// <param name="request"></param>
 /// <param name="ct"></param>
 /// <returns></returns>
 public async Task <Message> CallAsync(INameRecord proxy, Message request, TimeSpan timeout,
                                       CancellationToken ct)
 {
     using (var invoker = new IoTHubInvoker(_hubConnectionString)) {
         return(await invoker.CallAsync(proxy, request, timeout, ct));
     }
 }
예제 #9
0
 /// <summary>
 /// Copy constructor for record
 /// </summary>
 /// <param name="record"></param>
 public NameRecord(INameRecord record)
 {
     Address = record.Address;
     Type    = record.Type;
     Id      = record.Id;
     Name    = record.Name;
     ReferenceSet.AddRange(record.References);
 }
 /// <summary>
 /// Match record against address and type
 /// </summary>
 /// <param name="record"></param>
 /// <param name="address"></param>
 /// <param name="type"></param>
 /// <returns></returns>
 public static bool Matches(this INameRecord record, Reference address, NameRecordType type)
 {
     if (!record.Type.HasFlag(type))
     {
         return(false);
     }
     return(address == Reference.All || record.Address.Equals(address));
 }
예제 #11
0
 /// <summary>
 /// Invoke method and return response
 /// </summary>
 /// <param name="record"></param>
 /// <param name="request"></param>
 /// <param name="timeout"></param>
 /// <param name="ct"></param>
 /// <returns></returns>
 public async Task <Message> CallAsync(INameRecord record, Message request,
                                       TimeSpan timeout, CancellationToken ct)
 {
     using (var message = request.Clone()) {
         message.Proxy    = record.Address;
         message.DeviceId = record.Id;
         return(await CallAsync(message, timeout, ct).ConfigureAwait(false));
     }
 }
예제 #12
0
 /// <summary>
 /// Constructor creating a method based polled stream.
 /// </summary>
 /// <param name="iothub"></param>
 /// <param name="streamId"></param>
 /// <param name="remoteId"></param>
 /// <param name="link"></param>
 /// <param name="connectionString"></param>
 public IoTHubStream(IoTHubService iothub, Reference streamId,
                     Reference remoteId, INameRecord link, ConnectionString connectionString)
 {
     _iotHub          = iothub;
     _streamId        = streamId;
     _remoteId        = remoteId;
     _link            = link;
     ConnectionString = connectionString;
 }
        /// <summary>
        /// Constructor for proxy link object
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="proxy"></param>
        /// <param name="remoteId"></param>
        /// <param name="localAddress"></param>
        /// <param name="peerAddress"></param>
        internal ProxyLink(ProxySocket socket, INameRecord proxy, Reference remoteId,
                           SocketAddress localAddress, SocketAddress peerAddress)
        {
            _socket = socket ?? throw new ArgumentNullException(nameof(socket));

            Proxy        = proxy ?? throw new ArgumentNullException(nameof(proxy));
            RemoteId     = remoteId ?? throw new ArgumentNullException(nameof(remoteId));
            LocalAddress = localAddress ?? throw new ArgumentNullException(nameof(localAddress));
            PeerAddress  = peerAddress ?? throw new ArgumentNullException(nameof(peerAddress));
        }
예제 #14
0
 /// <summary>
 /// Comparison
 /// </summary>
 /// <param name="that"></param>
 /// <returns></returns>
 public bool Equals(INameRecord that)
 {
     return
         (IsEqual(Address, that.Address) &&
          IsEqual(Type, that.Type) &&
          IsEqual(Id, that.Id) &&
          IsEqual(Name, that.Name) &&
          ReferenceSet.SetEquals(that.References)
         );
 }
예제 #15
0
 /// <summary>
 /// Copy constructor for record
 /// </summary>
 /// <param name="record"></param>
 public NameRecord(INameRecord record)
 {
     Address      = record.Address;
     Type         = record.Type;
     Id           = record.Id;
     Name         = record.Name;
     Domain       = record.Domain;
     LastActivity = record.LastActivity;
     ReferenceSet.AddRange(record.References);
 }
예제 #16
0
 /// <summary>
 /// Constructor creating a method based polled stream.
 /// </summary>
 /// <param name="streamId"></param>
 /// <param name="remoteId"></param>
 /// <param name="link"></param>
 /// <param name="connectionString"></param>
 public IoTHubStream(ConnectionString hubConnectionString, Reference streamId,
                     Reference remoteId, INameRecord link, ConnectionString connectionString)
 {
     _hubConnectionString = hubConnectionString ??
                            throw new ArgumentNullException(nameof(hubConnectionString));
     _streamId        = streamId;
     _remoteId        = remoteId;
     _link            = link;
     ConnectionString = connectionString;
 }
 /// <summary>
 /// Match record against name and type
 /// </summary>
 /// <param name="record"></param>
 /// <param name="name"></param>
 /// <param name="type"></param>
 /// <returns></returns>
 public static bool Matches(this INameRecord record, string name, NameRecordType type)
 {
     if (!record.Type.HasFlag(type))
     {
         return(false);
     }
     return(name == null ||
            (record.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase) ||
             record.Id.Equals(name, StringComparison.CurrentCultureIgnoreCase)));
 }
예제 #18
0
 /// <summary>
 /// Constructor for proxy link object
 /// </summary>
 /// <param name="socket">Owning proxy link</param>
 /// <param name="remoteId">Remote endpoint id assigned by proxy</param>
 /// <param name="localAddress">Remote local address</param>
 /// <param name="peerAddress">Remote peer address</param>
 /// <returns></returns>
 internal ProxyLink(ProxySocket socket, INameRecord proxy, Reference remoteId,
                    SocketAddress localAddress, SocketAddress peerAddress)
 {
     _streamId    = new Reference();
     _socket      = socket;
     Proxy        = proxy;
     RemoteId     = remoteId;
     LocalAddress = localAddress;
     PeerAddress  = peerAddress;
 }
예제 #19
0
 /// <summary>
 /// Invoke method and return response
 /// </summary>
 /// <param name="record"></param>
 /// <param name="request"></param>
 /// <param name="timeout"></param>
 /// <param name="ct"></param>
 /// <returns></returns>
 private async Task <Message> TryInvokeDeviceMethodAsync(INameRecord record, Message request,
                                                         TimeSpan timeout, CancellationToken ct)
 {
     try {
         return(await InvokeDeviceMethodAsync(
                    record, request, timeout, ct).ConfigureAwait(false));
     }
     catch {
         return(null);
     }
 }
        /// <summary>
        /// Create stream connection through iot hub methods.
        /// </summary>
        /// <param name="streamId">Local reference address of the stream</param>
        /// <param name="remoteId">Remote reference of link</param>
        /// <param name="proxy">The proxy server</param>
        /// <returns></returns>
        public Task <IConnection> CreateConnectionAsync(Reference streamId,
                                                        Reference remoteId, INameRecord proxy, CodecId encoding)
        {
            IConnection conn = new IoTHubStream(_hubConnectionString, streamId, remoteId, proxy, null);

            // TODO: Revisit:  At this point we could either a) look up a host from the registry
            // then use it to create a dedicated stream with connection string or b) create an
            // adhoc dr stream record in the registry.

            return(Task.FromResult(conn));
        }
예제 #21
0
        /// <summary>
        /// Link one remote endpoint
        /// </summary>
        /// <param name="proxy"></param>
        /// <param name="ct"></param>
        /// <returns></returns>
        private async Task <IProxyLink> CreateLinkAsync(INameRecord proxy,
                                                        CancellationToken ct)
        {
            ProxyEventSource.Log.LinkCreate(this, proxy.Name, Info.Address);
            // Create link, i.e. perform bind, connect, listen, etc. on proxy
            Message response = await Provider.ControlChannel.CallAsync(proxy,
                                                                       new Message(Id, Reference.Null, new LinkRequest {
                Properties = Info
            }), ct);

            if (response == null || response.Error != (int)SocketError.Success)
            {
                ProxyEventSource.Log.LinkFailure(this, proxy.Name, Info, response, null);
                return(null);
            }

            var linkResponse = response.Content as LinkResponse;

            if (linkResponse == null)
            {
                ProxyEventSource.Log.LinkFailure(this, proxy.Name, Info, response, null);
                return(null);
            }

            // now create local link and open link for streaming
            var link = new ProxyLink(this, proxy, linkResponse.LinkId,
                                     linkResponse.LocalAddress, linkResponse.PeerAddress);

            try {
                // Broker connection string to proxy
                var openRequest = await link.BeginOpenAsync(ct).ConfigureAwait(false);

                ProxyEventSource.Log.LinkOpen(this, proxy.Name, Info.Address);

                await Provider.ControlChannel.CallAsync(proxy,
                                                        new Message(Id, linkResponse.LinkId, openRequest), ct).ConfigureAwait(false);

                // Wait until remote side opens stream connection
                bool success = await link.TryCompleteOpenAsync(ct).ConfigureAwait(false);

                if (success)
                {
                    ProxyEventSource.Log.LinkComplete(this, proxy.Name, Info.Address);
                    return(link);
                }
            }
            catch (Exception e) {
                // Try to close remote side
                await link.CloseAsync(CancellationToken.None).ConfigureAwait(false);

                ProxyEventSource.Log.LinkFailure(this, proxy.Name, Info.Address, null, e);
            }
            return(null);
        }
예제 #22
0
 /// <summary>
 /// Comparison
 /// </summary>
 /// <param name="that"></param>
 /// <returns></returns>
 public bool Equals(INameRecord that)
 {
     return
         (IsEqual(Address, that.Address) &&
          IsEqual(Type, that.Type) &&
          IsEqual(Id, that.Id) &&
          IsEqual(Name, that.Name) &&
          IsEqual(Domain, that.Domain) &&
          IsEqual(LastActivity, that.LastActivity) &&
          ReferenceSet.SetEquals(that.References)
         );
 }
예제 #23
0
        /// <summary>
        /// Create stream connection through iot hub methods.
        /// </summary>
        /// <param name="streamId">Local reference id of the stream</param>
        /// <param name="remoteId">Remote reference of link</param>
        /// <param name="proxy">The proxy server</param>
        /// <returns></returns>
        public Task <IConnection> CreateConnectionAsync(Reference streamId,
                                                        Reference remoteId, INameRecord proxy)
        {
            IConnection conn = new IoTHubStream(this, streamId, remoteId, proxy,
                                                null);

            // TODO: Revisit:  At this point we could either a) look up a host from the registry
            // then use it to create a dedicated stream with connection string or b) create an
            // adhoc dr stream entry in the registry.  However, due to overall IoTHub performance
            // characteristic there is at this point no performance benefit from doing so.

            return(Task.FromResult(conn));
        }
예제 #24
0
        /// <summary>
        /// Registers a new record with device registry.  Host records are
        /// synchronized lazily at every cache refresh to avoid spamming the
        /// registry...
        /// </summary>
        /// <param name="proxy"></param>
        /// <param name="name"></param>
        /// <param name="ct"></param>
        /// <returns></returns>
        public async Task AddOrUpdateAsync(INameRecord record, CancellationToken ct)
        {
            if (NameRecordType.Host == (record.Type & NameRecordType.Host))
            {
                _cache.AddOrUpdate(record.Id, s => record, (s, r) => r.Assign(record));
            }
            else
            {
                await UpdateRecordAsync(record, ct);

                base.Invalidate();
            }
        }
        /// <summary>
        /// Returns a new connection
        /// </summary>
        /// <param name="streamId">Local reference id of the stream</param>
        /// <param name="remoteId">Remote reference of link</param>
        /// <param name="proxy">The proxy server</param>
        /// <returns></returns>
        public Task <IConnection> CreateConnectionAsync(Reference streamId,
                                                        Reference remoteId, INameRecord proxy)
        {
            var uri = new UriBuilder(_uri);

            uri.Scheme = "wss";
            uri.Path   = streamId.ToString();
            var connection = new WebSocketMessageStream(
                this, streamId, new ConnectionString(uri.Uri, "proxy", "secret"));

            _streamMap.AddOrUpdate(streamId, connection, (r, s) => connection);
            return(Task.FromResult((IConnection)connection));
        }
        /// <summary>
        /// Creates new connection
        /// </summary>
        /// <param name="streamId">Local reference id of the stream</param>
        /// <param name="remoteId">Remote reference of link</param>
        /// <param name="proxy">The proxy server</param>
        /// <param name="encoding">The encoding to use</param>
        /// <returns></returns>
        public Task <IConnection> CreateConnectionAsync(Reference streamId,
                                                        Reference remoteId, INameRecord proxy, CodecId encoding)
        {
            var uri = new UriBuilder(_uri)
            {
                Scheme = _secure ? "wss" : "ws",
                Path   = streamId.ToString()
            };
            var connection = new WebSocketConnection(this, streamId, remoteId, encoding,
                                                     ConnectionString.CreateWithEndpointAndToken(uri.Uri, "proxy", "secret"));

            _connectionMap.AddOrUpdate(streamId, connection, (r, s) => connection);
            return(Task.FromResult((IConnection)connection));
        }
예제 #27
0
 /// <summary>
 /// Comparison
 /// </summary>
 /// <param name="that"></param>
 /// <returns></returns>
 public bool Equals(INameRecord that)
 {
     if (that == null)
     {
         return(false);
     }
     return
         (Address.Equals(that.Address) &&
          Type.Equals(that.Type) &&
          Id.Equals(that.Id) &&
          Name.Equals(that.Name, StringComparison.CurrentCultureIgnoreCase) &&
          References.SameAs(that.References)
         );
 }
예제 #28
0
        /// <summary>
        /// Copies members from passed in record
        /// </summary>
        /// <param name="record"></param>
        public INameRecord Assign(INameRecord record)
        {
            if (!Id.Equals(record.Id))
            {
                return(record);
            }

            Type = record.Type;
            Name = record.Name;

            ReferenceSet.Clear();
            ReferenceSet.AddRange(record.References);
            return(this);
        }
예제 #29
0
 /// <summary>
 /// Invoke method on proxy
 /// </summary>
 /// <param name="proxy"></param>
 /// <param name="request"></param>
 /// <param name="ct"></param>
 /// <returns></returns>
 public async Task <Message> CallAsync(INameRecord proxy, Message request,
                                       CancellationToken ct)
 {
     try {
         return(await InvokeDeviceMethodAsync(proxy, request, TimeSpan.FromMinutes(3),
                                              ct).ConfigureAwait(false));
     }
     catch (OperationCanceledException) {
     }
     catch (Exception e) {
         ProxyEventSource.Log.HandledExceptionAsError(this, e);
     }
     return(null);
 }
        /// <summary>
        /// Creates connection
        /// </summary>
        /// <param name="streamId">Local reference id of the stream</param>
        /// <param name="remoteId">Remote reference of link</param>
        /// <param name="proxy">The proxy server</param>
        /// <returns></returns>
        public async Task <IConnection> CreateConnectionAsync(Reference streamId,
                                                              Reference remoteId, INameRecord proxy)
        {
            var uri = new UriBuilder(_uri);

            uri.Scheme = "http";
            var token = await _tokenProvider.GetTokenAsync(uri.ToString(),
                                                           TimeSpan.FromHours(24)).ConfigureAwait(false);

            var connection = new RelayStream(
                this, streamId, new ConnectionString(_uri, "proxy", token.TokenString));

            _streamMap.AddOrUpdate(streamId, connection, (r, s) => connection);
            return(connection);
        }