/// <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); } }
/// <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); } }
/// <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); }