in iothub/device/src/ModuleClient.cs [581:892]
public Task SetDesiredPropertyUpdateCallbackAsync(DesiredPropertyUpdateCallback callback, object userContext) =>
InternalClient.SetDesiredPropertyUpdateCallbackAsync(callback, userContext);
/// <summary>
/// Set a callback that will be called whenever the client receives a state update
/// (desired or reported) from the service.
/// Set callback value to null to clear.
/// </summary>
/// <remarks>
/// This has the side-effect of subscribing to the PATCH topic on the service.
/// </remarks>
/// <param name="callback">Callback to call after the state update has been received and applied.</param>
/// <param name="userContext">Context object that will be passed into callback.</param>
/// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
/// <exception cref="OperationCanceledException">Thrown when the operation has been canceled.</exception>
/// <exception cref="ObjectDisposedException">When the client has been disposed.</exception>
public Task SetDesiredPropertyUpdateCallbackAsync(DesiredPropertyUpdateCallback callback, object userContext, CancellationToken cancellationToken) =>
InternalClient.SetDesiredPropertyUpdateCallbackAsync(callback, userContext, cancellationToken);
/// <summary>
/// Retrieve a module twin object for the current module.
/// </summary>
/// <returns>The module twin object for the current module</returns>
/// <exception cref="ObjectDisposedException">When the client has been disposed.</exception>
public Task<Twin> GetTwinAsync() => InternalClient.GetTwinAsync();
/// <summary>
/// Retrieve a module twin object for the current module.
/// </summary>
/// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
/// <exception cref="OperationCanceledException">Thrown when the operation has been canceled.</exception>
/// <exception cref="ObjectDisposedException">When the client has been disposed.</exception>
/// <returns>The module twin object for the current module</returns>
public Task<Twin> GetTwinAsync(CancellationToken cancellationToken) => InternalClient.GetTwinAsync(cancellationToken);
/// <summary>
/// Push reported property changes up to the service.
/// </summary>
/// <param name="reportedProperties">Reported properties to push.</param>
/// <exception cref="ObjectDisposedException">When the client has been disposed.</exception>
public Task UpdateReportedPropertiesAsync(TwinCollection reportedProperties) =>
InternalClient.UpdateReportedPropertiesAsync(reportedProperties);
/// <summary>
/// Push reported property changes up to the service.
/// </summary>
/// <param name="reportedProperties">Reported properties to push.</param>
/// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
/// <exception cref="OperationCanceledException">Thrown when the operation has been canceled.</exception>
/// <exception cref="ObjectDisposedException">When the client has been disposed.</exception>
public Task UpdateReportedPropertiesAsync(TwinCollection reportedProperties, CancellationToken cancellationToken) =>
InternalClient.UpdateReportedPropertiesAsync(reportedProperties, cancellationToken);
#region Module Specific API
// APIs that are available only in module client
/// <summary>
/// Sends an event to IoT hub.
/// </summary>
/// <remarks>
/// In case of a transient issue, retrying the operation should work. In case of a non-transient issue, inspect the error details and take steps accordingly.
/// Please note that the above list is not exhaustive.
/// </remarks>
/// <param name="outputName">The output target for sending the given message.</param>
/// <param name="message">The message to send.</param>
/// <exception cref="ArgumentNullException">Thrown when a required parameter is null.</exception>
/// <exception cref="TimeoutException">Thrown if the service does not respond to the request within the timeout specified for the operation.
/// The timeout values are largely transport protocol specific. Check the corresponding transport settings to see if they can be configured.
/// The operation timeout for the client can be set using <see cref="OperationTimeoutInMilliseconds"/>.</exception>
/// <exception cref="IotHubCommunicationException">Thrown if the client encounters a transient retryable exception. </exception>
/// <exception cref="SocketException">Thrown if a socket error occurs.</exception>
/// <exception cref="WebSocketException">Thrown if an error occurs when performing an operation on a WebSocket connection.</exception>
/// <exception cref="IOException">Thrown if an I/O error occurs.</exception>
/// <exception cref="ClosedChannelException">Thrown if the MQTT transport layer closes unexpectedly.</exception>
/// <exception cref="IotHubException">Thrown if an error occurs when communicating with IoT hub service.
/// If <see cref="IotHubException.IsTransient"/> is set to <c>true</c> then it is a transient exception.
/// If <see cref="IotHubException.IsTransient"/> is set to <c>false</c> then it is a non-transient exception.</exception>
/// <exception cref="ObjectDisposedException">When the client has been disposed.</exception>
/// <returns>The message containing the event</returns>
public Task SendEventAsync(string outputName, Message message) =>
InternalClient.SendEventAsync(outputName, message);
/// <summary>
/// Sends an event to IoT hub.
/// </summary>
/// <remarks>
/// In case of a transient issue, retrying the operation should work. In case of a non-transient issue, inspect the error details and take steps accordingly.
/// Please note that the above list is not exhaustive.
/// </remarks>
/// <param name="outputName">The output target for sending the given message.</param>
/// <param name="message">The message to send.</param>
/// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
/// <exception cref="ArgumentNullException">Thrown when a required parameter is null.</exception>
/// <exception cref="OperationCanceledException">Thrown if the service does not respond to the request before the expiration of the passed <see cref="CancellationToken"/>.
/// If a cancellation token is not supplied to the operation call, a cancellation token with an expiration time of 4 minutes is used.
/// </exception>
/// <exception cref="IotHubCommunicationException">Thrown if the client encounters a transient retryable exception. </exception>
/// <exception cref="SocketException">Thrown if a socket error occurs.</exception>
/// <exception cref="WebSocketException">Thrown if an error occurs when performing an operation on a WebSocket connection.</exception>
/// <exception cref="IOException">Thrown if an I/O error occurs.</exception>
/// <exception cref="ClosedChannelException">Thrown if the MQTT transport layer closes unexpectedly.</exception>
/// <exception cref="IotHubException">Thrown if an error occurs when communicating with IoT hub service.
/// If <see cref="IotHubException.IsTransient"/> is set to <c>true</c> then it is a transient exception.
/// If <see cref="IotHubException.IsTransient"/> is set to <c>false</c> then it is a non-transient exception.</exception>
/// <exception cref="ObjectDisposedException">When the client has been disposed.</exception>
/// <returns>The message containing the event</returns>
public Task SendEventAsync(string outputName, Message message, CancellationToken cancellationToken) =>
InternalClient.SendEventAsync(outputName, message, cancellationToken);
/// <summary>
/// Sends a batch of events to IoT hub. Use AMQP or HTTPs for a true batch operation.
/// </summary>
/// <remarks>
/// MQTT will just send the messages one after the other as it does not support true batching.
/// For more information on IoT Edge module routing <see href="https://docs.microsoft.com/azure/iot-edge/module-composition?view=iotedge-2018-06#declare-routes"/>
/// </remarks>
/// <param name="outputName">The output target for sending the given message.</param>
/// <param name="messages">A list of one or more messages to send.</param>
/// <exception cref="OperationCanceledException">Thrown when the operation has been canceled.</exception>
/// <exception cref="ObjectDisposedException">When the client has been disposed.</exception>
/// <returns>The task containing the event</returns>
public Task SendEventBatchAsync(string outputName, IEnumerable<Message> messages) =>
InternalClient.SendEventBatchAsync(outputName, messages);
/// <summary>
/// Sends a batch of events to IoT hub. Use AMQP or HTTPs for a true batch operation. MQTT will just send the messages one after the other.
/// For more information on IoT Edge module routing <see href="https://docs.microsoft.com/azure/iot-edge/module-composition?view=iotedge-2018-06#declare-routes"/>
/// </summary>
/// <param name="outputName">The output target for sending the given message.</param>
/// <param name="messages">A list of one or more messages to send.</param>
/// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
/// <exception cref="OperationCanceledException">Thrown when the operation has been canceled.</exception>
/// <exception cref="ObjectDisposedException">When the client has been disposed.</exception>
/// <returns>The task containing the event</returns>
public Task SendEventBatchAsync(string outputName, IEnumerable<Message> messages, CancellationToken cancellationToken) =>
InternalClient.SendEventBatchAsync(outputName, messages, cancellationToken);
/// <summary>
/// Sets a new delegate for the particular input. If a delegate is already associated with
/// the input, it will be replaced with the new delegate.
/// </summary>
/// <param name="inputName">The name of the input to associate with the delegate.</param>
/// <param name="messageHandler">The delegate to be used when a message is sent to the particular inputName.</param>
/// <param name="userContext">generic parameter to be interpreted by the client code.</param>
/// <exception cref="OperationCanceledException">Thrown when the operation has been canceled.</exception>
/// <exception cref="ObjectDisposedException">When the client has been disposed.</exception>
/// <returns>The task containing the event</returns>
public Task SetInputMessageHandlerAsync(string inputName, MessageHandler messageHandler, object userContext) =>
InternalClient.SetInputMessageHandlerAsync(inputName, messageHandler, userContext, _isAnEdgeModule);
/// <summary>
/// Sets a new delegate for the particular input. If a delegate is already associated with
/// the input, it will be replaced with the new delegate.
/// </summary>
/// <param name="inputName">The name of the input to associate with the delegate.</param>
/// <param name="messageHandler">The delegate to be used when a message is sent to the particular inputName.</param>
/// <param name="userContext">generic parameter to be interpreted by the client code.</param>
/// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
/// <exception cref="OperationCanceledException">Thrown when the operation has been canceled.</exception>
/// <exception cref="ObjectDisposedException">When the client has been disposed.</exception>
/// <returns>The task containing the event</returns>
public Task SetInputMessageHandlerAsync(string inputName, MessageHandler messageHandler, object userContext, CancellationToken cancellationToken) =>
InternalClient.SetInputMessageHandlerAsync(inputName, messageHandler, userContext, _isAnEdgeModule, cancellationToken);
/// <summary>
/// Sets a new default delegate which applies to all endpoints.
/// </summary>
/// <remarks>
/// If a delegate is already associated with the input, it will be called, else the default delegate will be called.
/// If a default delegate was set previously, it will be overwritten.
/// </remarks>
/// <param name="messageHandler">The delegate to be called when a message is sent to any input.</param>
/// <param name="userContext">generic parameter to be interpreted by the client code.</param>
/// <exception cref="OperationCanceledException">Thrown when the operation has been canceled.</exception>
/// <exception cref="ObjectDisposedException">When the client has been disposed.</exception>
/// <returns>The task containing the event</returns>
public Task SetMessageHandlerAsync(MessageHandler messageHandler, object userContext) =>
InternalClient.SetMessageHandlerAsync(messageHandler, userContext, _isAnEdgeModule);
/// <summary>
/// Sets a new default delegate which applies to all endpoints. If a delegate is already associated with
/// the input, it will be called, else the default delegate will be called. If a default delegate was set previously,
/// it will be overwritten.
/// </summary>
/// <param name="messageHandler">The delegate to be called when a message is sent to any input.</param>
/// <param name="userContext">generic parameter to be interpreted by the client code.</param>
/// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
/// <exception cref="OperationCanceledException">Thrown when the operation has been canceled.</exception>
/// <exception cref="ObjectDisposedException">When the client has been disposed.</exception>
/// <returns>The task containing the event</returns>
public Task SetMessageHandlerAsync(MessageHandler messageHandler, object userContext, CancellationToken cancellationToken) =>
InternalClient.SetMessageHandlerAsync(messageHandler, userContext, _isAnEdgeModule, cancellationToken);
/// <summary>
/// Interactively invokes a method from an edge module to an edge device.
/// Both the edge module and the edge device need to be connected to the same edge hub.
/// </summary>
/// <param name="deviceId">The unique identifier of the edge device to invoke the method on.</param>
/// <param name="methodRequest">The details of the method to invoke.</param>
/// <exception cref="ObjectDisposedException">When the client has been disposed.</exception>
/// <returns>The result of the method invocation.</returns>
public Task<MethodResponse> InvokeMethodAsync(string deviceId, MethodRequest methodRequest) =>
InvokeMethodAsync(deviceId, methodRequest, CancellationToken.None);
/// <summary>
/// Interactively invokes a method from an edge module to an edge device.
/// Both the edge module and the edge device need to be connected to the same edge hub.
/// </summary>
/// <param name="deviceId">The unique identifier of the edge device to invoke the method on.</param>
/// <param name="methodRequest">The details of the method to invoke.</param>
/// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
/// <exception cref="OperationCanceledException">Thrown when the operation has been canceled.</exception>
/// <exception cref="ObjectDisposedException">When the client has been disposed.</exception>
/// <returns>The result of the method invocation.</returns>
public Task<MethodResponse> InvokeMethodAsync(string deviceId, MethodRequest methodRequest, CancellationToken cancellationToken) =>
InvokeMethodAsync(GetDeviceMethodUri(deviceId), methodRequest, cancellationToken);
/// <summary>
/// Interactively invokes a method from an edge module to a different edge module.
/// Both of the edge modules need to be connected to the same edge hub.
/// </summary>
/// <param name="deviceId">The unique identifier of the device.</param>
/// <param name="moduleId">The unique identifier of the edge module to invoke the method on.</param>
/// <param name="methodRequest">The details of the method to invoke.</param>
/// <exception cref="OperationCanceledException">Thrown when the operation has been canceled.</exception>
/// <exception cref="ObjectDisposedException">When the client has been disposed.</exception>
/// <returns>The result of the method invocation.</returns>
public Task<MethodResponse> InvokeMethodAsync(string deviceId, string moduleId, MethodRequest methodRequest) =>
InvokeMethodAsync(deviceId, moduleId, methodRequest, CancellationToken.None);
/// <summary>
/// Interactively invokes a method from an edge module to a different edge module.
/// Both of the edge modules need to be connected to the same edge hub.
/// </summary>
/// <param name="deviceId">The unique identifier of the device.</param>
/// <param name="moduleId">The unique identifier of the edge module to invoke the method on.</param>
/// <param name="methodRequest">The details of the method to invoke.</param>
/// <param name="cancellationToken">A cancellation token to cancel the operation.</param>
/// <exception cref="OperationCanceledException">Thrown when the operation has been canceled.</exception>
/// <exception cref="ObjectDisposedException">When the client has been disposed.</exception>
/// <returns>The result of the method invocation.</returns>
public Task<MethodResponse> InvokeMethodAsync(string deviceId, string moduleId, MethodRequest methodRequest, CancellationToken cancellationToken) =>
InvokeMethodAsync(GetModuleMethodUri(deviceId, moduleId), methodRequest, cancellationToken);
private async Task<MethodResponse> InvokeMethodAsync(Uri uri, MethodRequest methodRequest, CancellationToken cancellationToken)
{
if (InternalClient.IsDisposed)
{
throw new ObjectDisposedException("IoT client", DefaultDelegatingHandler.ClientDisposedMessage);
}
methodRequest.ThrowIfNull(nameof(methodRequest));
if (_httpTransportHandler == null)
{
Func<object, X509Certificate, X509Chain, SslPolicyErrors, bool> customCertificateValidation = _certValidator.GetCustomCertificateValidation();
// The HTTP message handlers created in this block are disposed when this client is
// disposed.
#pragma warning disable CA2000 // Dispose objects before losing scope
#if !NET451
var httpMessageHandler = new HttpClientHandler();
#else
var httpMessageHandler = new WebRequestHandler();
#endif
#pragma warning restore CA2000 // Dispose objects before losing scope
if (customCertificateValidation != null)
{
TlsVersions.Instance.SetLegacyAcceptableVersions();
#if !NET451
httpMessageHandler.ServerCertificateCustomValidationCallback = customCertificateValidation;
httpMessageHandler.SslProtocols = TlsVersions.Instance.Preferred;
#else
httpMessageHandler.ServerCertificateValidationCallback = (sender, certificate, chain, errors) =>
{
return customCertificateValidation(sender, certificate, chain, errors);
};
#endif
}
// We need to add the certificate to the httpTransport if DeviceAuthenticationWithX509Certificate
if (InternalClient.Certificate != null)
{
httpMessageHandler.ClientCertificates.Add(InternalClient.Certificate);
}
// Note that this client is ignoring any HttpTransportSettings that the user may have
// provided. This is because the kinds of settings a user would want to override
// aren't as applicable for this particular operation.
var transportSettings = new Http1TransportSettings()
{
HttpClient = new HttpClient(httpMessageHandler, true)
};
var context = new PipelineContext
{
ProductInfo = new ProductInfo
{
Extra = InternalClient.ProductInfo
}
};
_httpTransportHandler = new HttpTransportHandler(context, InternalClient.IotHubConnectionString, transportSettings);
}
var methodInvokeRequest = new MethodInvokeRequest(methodRequest.Name, methodRequest.DataAsJson, methodRequest.ResponseTimeout, methodRequest.ConnectionTimeout);
MethodInvokeResponse result = await _httpTransportHandler.InvokeMethodAsync(methodInvokeRequest, uri, cancellationToken).ConfigureAwait(false);
return new MethodResponse(Encoding.UTF8.GetBytes(result.GetPayloadAsJson()), result.Status);
}