public override async Task RegisterAsync()

in provisioning/transport/http/src/ProvisioningTransportHandlerHttp.cs [60:287]


        public override async Task<DeviceRegistrationResult> RegisterAsync(
            ProvisioningTransportRegisterMessage message,
            CancellationToken cancellationToken)
        {
            if (Logging.IsEnabled)
                Logging.Enter(this, $"{nameof(ProvisioningTransportHandlerHttp)}.{nameof(RegisterAsync)}");

            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            cancellationToken.ThrowIfCancellationRequested();

            try
            {
                HttpAuthStrategy authStrategy;

                switch (message.Security)
                {
                    case SecurityProviderTpm _:
                        authStrategy = new HttpAuthStrategyTpm((SecurityProviderTpm)message.Security);
                        break;

                    case SecurityProviderX509 _:
                        authStrategy = new HttpAuthStrategyX509((SecurityProviderX509)message.Security);
                        break;

                    case SecurityProviderSymmetricKey _:
                        authStrategy = new HttpAuthStrategySymmetricKey((SecurityProviderSymmetricKey)message.Security);
                        break;

                    default:
                        if (Logging.IsEnabled)
                            Logging.Error(this, $"Invalid {nameof(SecurityProvider)} type.");

                        throw new NotSupportedException(
                            $"{nameof(message.Security)} must be of type {nameof(SecurityProviderTpm)}, {nameof(SecurityProviderX509)} or {nameof(SecurityProviderSymmetricKey)}");
                }

                Logging.Associate(authStrategy, this);

                using var httpClientHandler = new HttpClientHandler()
                {
                    // Cannot specify a specific protocol here, as desired due to an error:
                    //   ProvisioningDeviceClient_ValidRegistrationId_AmqpWithProxy_SymmetricKey_RegisterOk_GroupEnrollment failing for me with System.PlatformNotSupportedException: Operation is not supported on this platform.
                    // When revisiting TLS12 work for DPS, we should figure out why. Perhaps the service needs to support it.

                    //SslProtocols = TlsVersions.Preferred,
                };

                if (Proxy != DefaultWebProxySettings.Instance)
                {
                    httpClientHandler.UseProxy = Proxy != null;
                    httpClientHandler.Proxy = Proxy;
                    if (Logging.IsEnabled)
                        Logging.Info(this, $"{nameof(RegisterAsync)} Setting HttpClientHandler.Proxy");
                }

                var builder = new UriBuilder
                {
                    Scheme = Uri.UriSchemeHttps,
                    Host = message.GlobalDeviceEndpoint,
                    Port = Port,
                };

                using DeviceProvisioningServiceRuntimeClient client = authStrategy.CreateClient(builder.Uri, httpClientHandler);
                client.HttpClient.DefaultRequestHeaders.Add("User-Agent", message.ProductInfo);
                if (Logging.IsEnabled)
                    Logging.Info(this, $"Uri: {builder.Uri}; User-Agent: {message.ProductInfo}");

                DeviceRegistration deviceRegistration = authStrategy.CreateDeviceRegistration();
                if (message.Payload != null
                    && message.Payload.Length > 0)
                {
                    deviceRegistration.Payload = new JRaw(message.Payload);
                }
                string registrationId = message.Security.GetRegistrationID();

                RegistrationOperationStatus operation = await client.RuntimeRegistration
                    .RegisterDeviceAsync(
                        registrationId,
                        message.IdScope,
                        deviceRegistration,
                        cancellationToken: cancellationToken)
                    .ConfigureAwait(false);

                int attempts = 0;
                string operationId = operation.OperationId;

                if (Logging.IsEnabled)
                    Logging.RegisterDevice(
                        this,
                        registrationId,
                        message.IdScope,
                        deviceRegistration.Tpm == null ? "X509" : "TPM",
                        operation.OperationId,
                        operation.RetryAfter,
                        operation.Status);

                // Poll with operationId until registration complete.
                while (string.CompareOrdinal(operation.Status, RegistrationOperationStatus.OperationStatusAssigning) == 0
                    || string.CompareOrdinal(operation.Status, RegistrationOperationStatus.OperationStatusUnassigned) == 0)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    TimeSpan? serviceRecommendedDelay = operation.RetryAfter;
                    if (serviceRecommendedDelay != null
                        && serviceRecommendedDelay?.TotalSeconds < s_defaultOperationPoolingIntervalMilliseconds.TotalSeconds)
                    {
                        if (Logging.IsEnabled)
                            Logging.Error(
                                this,
                                $"Service recommended unexpected retryAfter of {operation.RetryAfter?.TotalSeconds}, defaulting to delay of {s_defaultOperationPoolingIntervalMilliseconds}",
                                nameof(RegisterAsync));

                        serviceRecommendedDelay = s_defaultOperationPoolingIntervalMilliseconds;
                    }

                    await Task
                        .Delay(serviceRecommendedDelay ?? RetryJitter.GenerateDelayWithJitterForRetry(s_defaultOperationPoolingIntervalMilliseconds), cancellationToken)
                        .ConfigureAwait(false);

                    try
                    {
                        operation = await client
                            .RuntimeRegistration.OperationStatusLookupAsync(
                                registrationId,
                                operationId,
                                message.IdScope,
                                cancellationToken: cancellationToken)
                            .ConfigureAwait(false);
                    }
                    catch (HttpOperationException ex)
                    {
                        bool isTransient = ex.Response.StatusCode >= HttpStatusCode.InternalServerError
                            || (int)ex.Response.StatusCode == 429;

                        try
                        {
                            ProvisioningErrorDetailsHttp errorDetails = JsonConvert.DeserializeObject<ProvisioningErrorDetailsHttp>(ex.Response.Content, JsonSerializerSettingsInitializer.GetJsonSerializerSettings());

                            if (isTransient)
                            {
                                serviceRecommendedDelay = errorDetails.RetryAfter;
                            }
                            else
                            {
                                if (Logging.IsEnabled)
                                    Logging.Error(this, $"{nameof(ProvisioningTransportHandlerHttp)} threw exception {ex}", nameof(RegisterAsync));

                                throw new ProvisioningTransportException(ex.Response.Content, ex, isTransient, errorDetails);
                            }
                        }
                        catch (JsonException jex)
                        {
                            if (Logging.IsEnabled)
                                Logging.Error(
                                    this,
                                    $"{nameof(ProvisioningTransportHandlerHttp)} server returned malformed error response." +
                                    $"Parsing error: {jex}. Server response: {ex.Response.Content}",
                                    nameof(RegisterAsync));

                            throw new ProvisioningTransportException(
                                $"HTTP transport exception: malformed server error message: '{ex.Response.Content}'",
                                jex,
                                false);
                        }
                    }

                    if (Logging.IsEnabled)
                        Logging.OperationStatusLookup(
                            this,
                            registrationId,
                            operation.OperationId,
                            operation.RetryAfter,
                            operation.Status,
                            attempts);

                    ++attempts;
                }

                if (string.CompareOrdinal(operation.Status, RegistrationOperationStatus.OperationStatusAssigned) == 0)
                {
                    authStrategy.SaveCredentials(operation);
                }

                return ConvertToProvisioningRegistrationResult(operation.RegistrationState);
            }
            catch (HttpOperationException ex)
            {
                if (Logging.IsEnabled)
                    Logging.Error(this, $"{nameof(ProvisioningTransportHandlerHttp)} threw exception {ex}", nameof(RegisterAsync));

                bool isTransient = ex.Response.StatusCode >= HttpStatusCode.InternalServerError
                    || (int)ex.Response.StatusCode == 429;

                try
                {
                    ProvisioningErrorDetailsHttp errorDetails = JsonConvert.DeserializeObject<ProvisioningErrorDetailsHttp>(ex.Response.Content, JsonSerializerSettingsInitializer.GetJsonSerializerSettings());
                    throw new ProvisioningTransportException(ex.Response.Content, ex, isTransient, errorDetails);
                }
                catch (JsonException jex)
                {
                    if (Logging.IsEnabled)
                        Logging.Error(
                            this,
                            $"{nameof(ProvisioningTransportHandlerHttp)} server returned malformed error response. Parsing error: {jex}. Server response: {ex.Response.Content}",
                            nameof(RegisterAsync));

                    throw new ProvisioningTransportException(
                        $"HTTP transport exception: malformed server error message: '{ex.Response.Content}'",
                        jex,
                        false);
                }
            }
            catch (Exception ex) when (!(ex is ProvisioningTransportException))
            {
                if (Logging.IsEnabled)
                    Logging.Error(this, $"{nameof(ProvisioningTransportHandlerHttp)} threw exception {ex}", nameof(RegisterAsync));

                throw new ProvisioningTransportException($"HTTP transport exception", ex, true);
            }
            finally
            {
                if (Logging.IsEnabled)
                    Logging.Exit(this, $"{nameof(ProvisioningTransportHandlerHttp)}.{nameof(RegisterAsync)}");
            }
        }