/// <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);
            }
        }
Example #3
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>
        /// Returns the query results async
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="continuation"></param>
        /// <param name="ct"></param>
        /// <returns></returns>
        private async Task <Tuple <string, IEnumerable <IoTHubRecord> > > PagedLookupAsync(
            string sql, string continuation, CancellationToken ct)
        {
            if (string.IsNullOrEmpty(sql))
            {
                throw ProxyEventSource.Log.ArgumentNull("sql");
            }
            try {
                var uri = new UriBuilder {
                    Scheme = "https",
                    Host   = _hubConnectionString.HostName,
                    Path   = "/devices/query",
                    Query  = "api-version=" + _apiVersion
                };
                var stream = await _http.StreamAsync(
                    CreateUri("/devices/query"), Http.Post,
                    async h => {
                    h.Add(HttpRequestHeader.Authorization.ToString(),
                          await IoTHubService.GetSasTokenAsync(_hubConnectionString,
                                                               3600).ConfigureAwait(false));
                    h.Add(HttpRequestHeader.UserAgent.ToString(), _clientId);

                    // Add previous continuation if any provided
                    if (!string.IsNullOrEmpty(continuation))
                    {
                        h.Add("x-ms-continuation", continuation);
                    }
                }, (s, h) => {
                    // get continuation returned if any
                    if (h.TryGetValues("x-ms-continuation", out var values))
                    {
                        continuation = values.FirstOrDefault();
                    }
                    else
                    {
                        continuation = null;
                    }
                }, ct,
                    @"{""query"":""" + sql + @"""}", "application/json").ConfigureAwait(false);

                using (stream)
                    using (var sr = new StreamReader(stream))
                        using (JsonReader reader = new JsonTextReader(sr)) {
                            var results = JToken.ReadFrom(reader);
                            return(Tuple.Create(continuation, results.Select(j => new IoTHubRecord((JObject)j))));
                        }
            }
            catch (Exception e) {
                throw ProxyEventSource.Log.Rethrow(e, this);
            }
        }
 /// <summary>
 /// Initialize default provider
 /// </summary>
 /// <param name="iothub"></param>
 public DefaultProvider(ConnectionString iothub)
 {
     if (iothub == null)
     {
         throw new ArgumentException("You must provide the iothubowner connection " +
                                     "string, which can be obtained from the Azure IoT Hub portal. You must " +
                                     "then either initialize the DefaultProvider class, or set the _HUB_CS " +
                                     "environment variable.");
     }
     try {
         _iothub = new IoTHubService(iothub);
     }
     catch (Exception e) {
         throw ProxyEventSource.Log.Rethrow(e, this);
     }
 }
 /// <summary>
 /// Initialize default provider
 /// </summary>
 /// <param name="iothub"></param>
 public DefaultProvider(string iothub)
 {
     if (string.IsNullOrEmpty(iothub))
     {
         iothub = Environment.GetEnvironmentVariable("_HUB_CS");
     }
     if (string.IsNullOrEmpty(iothub))
     {
         throw ProxyEventSource.Log.ArgumentNull(nameof(iothub), this);
     }
     try {
         _iothub = new IoTHubService(ConnectionString.Parse(iothub));
     }
     catch (Exception e) {
         throw ProxyEventSource.Log.Rethrow(e, this);
     }
 }
 /// <summary>
 /// Deletes an record in the IoT Hub device registry
 /// </summary>
 /// <param name="record"></param>
 /// <param name="ct"></param>
 /// <returns></returns>
 internal async Task RemoveRecordAsync(INameRecord record,
                                       CancellationToken ct)
 {
     try {
         await _http.CallAsync(
             CreateUri("/devices/" + record.Id), Http.Delete,
             async h => {
             h.Add(HttpRequestHeader.Authorization.ToString(),
                   await IoTHubService.GetSasTokenAsync(_hubConnectionString,
                                                        3600).ConfigureAwait(false));
             h.Add(HttpRequestHeader.UserAgent.ToString(), _clientId);
             h.IfMatch.Add(new EntityTagHeaderValue(@"""*"""));
         },
             (sc, h) => { }, ct).ConfigureAwait(false);
     }
     catch (Exception e) {
         ProxyEventSource.Log.HandledExceptionAsInformation(this, e);
     }
 }
        /// <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), Http.Put,
                                      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.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).ConfigureAwait(false);

                if (result == null)
                {
                    return(null);
                }
                result.Assign(record);
                return(result);
            }
            catch (Exception e) {
                throw ProxyEventSource.Log.Rethrow(e, this);
            }
        }
Example #9
0
        /// <summary>
        /// Makes a method request from a message
        /// </summary>
        /// <param name="message"></param>
        /// <param name="timeout"></param>
        /// <param name="ct"></param>
        /// <returns></returns>
        public async Task <Message> CallAsync(Message message,
                                              TimeSpan timeout, CancellationToken ct)
        {
#if LOG_MESSAGES
            var sw = System.Diagnostics.Stopwatch.StartNew();
#endif
#endif
            if (string.IsNullOrEmpty(message.DeviceId))
            {
                throw ProxyEventSource.Log.ArgumentNull("deviceId");
            }
            var buffer  = new MemoryStream();
            var request = MethodCallRequest.Create(message, timeout);
            request.Encode(buffer, CodecId.Json);

            var uri = new UriBuilder {
                Scheme = "https",
                Host   = ConnectionString.HostName,
                Path   = $"/twins/{WebUtility.UrlEncode(message.DeviceId)}/methods",
                Query  = "api-version=" + IoTHubService._apiVersion
            };
            MethodCallResponse response;
            try {
                var stream = await _http.StreamAsync(uri.Uri, Http.Post, async h => {
                    h.Add(HttpRequestHeader.Authorization.ToString(),
                          await IoTHubService.GetSasTokenAsync(ConnectionString,
                                                               3600).ConfigureAwait(false));
                    h.Add(HttpRequestHeader.UserAgent.ToString(), IoTHubService._clientId);
                }, (s, h) => { }, ct, Encoding.UTF8.GetString(buffer.ToArray()),
                                                     "application/json").ConfigureAwait(false);

                using (stream) {
                    response = Serializable.Decode <MethodCallResponse>(stream, CodecId.Json);
                }
            }
            catch (HttpResponseException hex) {
                ProxyEventSource.Log.HandledExceptionAsError(this, hex);
                if (hex.StatusCode == HttpStatusCode.NotFound)
                {
                    throw new ProxyNotFound(hex);
                }
                if (hex.StatusCode == HttpStatusCode.Forbidden)
                {
                    throw new ProxyPermission(hex);
                }
                if (hex.StatusCode == HttpStatusCode.GatewayTimeout)
                {
                    throw new ProxyTimeout(hex.Message, hex);
                }
                throw new ProxyException(
                          $"Remote proxy device method communication failure: {hex.StatusCode}.", hex);
            }
            catch (OperationCanceledException) {
                throw;
            }
            catch (Exception ex) {
                ProxyEventSource.Log.HandledExceptionAsError(this, ex);
                throw new ProxyException($"Unknown proxy failure.", ex);
            }
#if LOG_MESSAGES
            finally {
                System.Diagnostics.Trace.TraceInformation(
                    $" > {sw.Elapsed} < for InvokeDeviceMethodAsync({message})");
            }
#endif
            if (response.Status != 200)
            {
                throw ProxyEventSource.Log.Rethrow(
                          new ProxyException($"Unexpected status {response.Status} returned by proxy."));
            }
            return(response.Payload);
        }
        /// <summary>
        /// Updates a record in the device registry
        /// </summary>
        /// <param name="record"></param>
        /// <param name="ct"></param>
        /// <returns></returns>
        private async Task <IoTHubRecord> UpdateRecordAsync(
            INameRecord record, CancellationToken ct)
        {
            var hubRecord = record as IoTHubRecord;

            //
            // If the record is a generic record, add it first and retrieve the resulting
            // twin record from the registry.  Then assign the generic record to it and
            // see if anything needs patching...
            //
            if (hubRecord == null)
            {
                // Create and convert generic record into hub record
                hubRecord = await AddRecordAsync(record, ct).ConfigureAwait(false);

                if (hubRecord == null)
                {
                    return(null);
                }
            }

            var json = hubRecord.Patch;

            if (string.IsNullOrEmpty(json))
            {
                // Nothing to patch...
                return(hubRecord);
            }

            //
            // If we logged changes to the record use the resulting patch json to patch
            // up the twin record.  If we do not find the record anymore as part of
            // patching it, then it was deleted, in which case return null.  Otherwise
            // return the returned patched up twin record
            //
            ProxyEventSource.Log.PatchingRecord(this, record, json);
            try {
                var stream = await _http.StreamAsync(
                    CreateUri("/twins/" + hubRecord.Id), Http.Patch,
                    async h => {
                    h.Add(HttpRequestHeader.Authorization.ToString(),
                          await IoTHubService.GetSasTokenAsync(_hubConnectionString,
                                                               3600).ConfigureAwait(false));
                    h.Add(HttpRequestHeader.UserAgent.ToString(), _clientId);
                    h.IfMatch.Add(new EntityTagHeaderValue(@"""*"""));
                },
                    (sc, h) => {
                    if (sc == HttpStatusCode.NotFound)
                    {
                        throw new TransientException();
                    }
                }, ct, json, "application/json").ConfigureAwait(false);

                using (stream)
                    using (var sr = new StreamReader(stream))
                        using (JsonReader reader = new JsonTextReader(sr)) {
                            hubRecord = new IoTHubRecord(new IoTHubRecord((JObject)JToken.ReadFrom(reader)));
                        }
            }
            catch (TransientException) {
                ProxyEventSource.Log.RecordRemoved(this, record);
                hubRecord = null;
            }
            catch (Exception e) {
                throw ProxyEventSource.Log.Rethrow(e, this);
            }
            ProxyEventSource.Log.RecordPatched(this, record, json);
            return(hubRecord);
        }