Пример #1
0
        // This routine is the client-side complement of
        //    Tug.Server.Controllers.ModelResultExt#Model()
        //
        // This routine returns an IDisposable that should be cleaned up by the caller
        // after they are done working with the dscResp instance to release any possible
        // temporary resources that were created to satisfy the model binding
        protected async Task <IDisposable> ExtractToResponseModel(HttpResponseMessage respMessage, DscResponse dscResp)
        {
            // We keep track of any disposables that we have to create during
            // the course of binding from the response the model instance and
            // then we'll clean all those up when the disposer is invoked
            var disposables = new List <IDisposable>();
            var disposer    = new DisposableAction(
                x => { foreach (var d in (List <IDisposable>)x)
                       {
                           d.Dispose();
                       }
                },
                disposables);

            PropertyInfo toResultProperty = null; // Used to detect more than one result property

            foreach (var pi in dscResp.GetType().GetProperties())
            {
                var toHeader = pi.GetCustomAttribute(typeof(ToHeaderAttribute))
                               as ToHeaderAttribute;
                if (toHeader != null)
                {
                    var headerName = toHeader.Name;
                    if (string.IsNullOrEmpty(headerName))
                    {
                        headerName = pi.Name;
                    }

                    if (LOG.IsEnabled(LogLevel.Debug))
                    {
                        LOG.LogDebug("Extracting Header[{headerName}]", headerName);
                    }

                    if (respMessage.Headers.Contains(headerName))
                    {
                        object headerValue = string.Join(",",
                                                         respMessage.Headers.GetValues(headerName));
                        if (pi.PropertyType != typeof(string))
                        {
                            headerValue = ConvertTo(pi.PropertyType, headerValue);
                        }
                        pi.SetValue(dscResp, headerValue);
                    }
                    continue;
                }

                var toResult = pi.GetCustomAttribute(typeof(ToResultAttribute))
                               as ToResultAttribute;
                if (toResult != null && respMessage.Content != null)
                {
                    if (toResultProperty != null)
                    {
                        throw new InvalidOperationException("multiple Result-mapping attributes found");
                    }
                    toResultProperty = pi;

                    object resultValue  = null;
                    var    contentType  = respMessage.Content.Headers.ContentType;
                    var    toResultType = pi.PropertyType;
                    if (toResultType.IsAssignableFrom(typeof(IActionResult)))
                    {
                        resultValue = new FileContentResult(
                            await respMessage.Content.ReadAsByteArrayAsync(),
                            contentType.ToString());
                    }
                    else if (toResultType.IsAssignableFrom(typeof(byte[])))
                    {
                        resultValue = await respMessage.Content.ReadAsByteArrayAsync();
                    }
                    else if (toResultType.IsAssignableFrom(typeof(Stream)))
                    {
                        // We'll remember the stream to properly clean it up
                        var stream = await respMessage.Content.ReadAsStreamAsync();

                        disposables.Add(stream);
                        resultValue = stream;
                    }
                    else if (toResultType.IsAssignableFrom(typeof(FileInfo)))
                    {
                        // If the result type is a FileInfo, we write out the
                        // response body content to a temp file, and then register
                        // an IDisposable that will clean up the temp file
                        var filePath = Path.GetTempFileName();
                        var fileInfo = new FileInfo(filePath);
                        disposables.Add(new DisposableAction(
                                            x => { ((FileInfo)x).Delete(); },
                                            fileInfo));
                        resultValue = fileInfo;
                    }
                    else if (toResultType.IsAssignableFrom(typeof(string)))
                    {
                        resultValue = await respMessage.Content.ReadAsStringAsync();
                    }
                    else
                    {
                        // Assume the result is model object that we can construct from
                        // a JSON representation encoded in the result content
                        var json = await respMessage.Content.ReadAsStringAsync();

                        resultValue = JsonConvert.DeserializeObject(json, toResultType, _jsonSerSettings);
                    }

                    if (resultValue != null)
                    {
                        pi.SetValue(dscResp, resultValue);
                    }
                }
            }

            return(disposer);
        }
Пример #2
0
        protected async Task <IDisposable> SendDscAsync(DscPullConfig.ServerConfig server, HttpMethod verb, string route,
                                                        DscRequest dscRequ, DscResponse dscResp)
        {
            if (LOG.IsEnabled(LogLevel.Trace))
            {
                LOG.LogTrace(nameof(SendDscAsync));
            }

            AssertInit();

            dscRequ.ProtocolVersionHeader = "2.0";

            var routeExpanded = route;

            if (dscRequ is DscAgentRequest)
            {
                var dscAgentRequ = (DscAgentRequest)dscRequ;
                routeExpanded = route.Replace("{AgentId}", dscAgentRequ.AgentId.ToString());
            }

            var requUrl = new Uri(server.ServerUrl, routeExpanded);

            if (LOG.IsEnabled(LogLevel.Debug))
            {
                LOG.LogDebug("Computed request URL:  [{url}]", requUrl);
            }

            var httpHandler = new HttpClientHandler();

            if (server.Proxy != null)
            {
                if (LOG.IsEnabled(LogLevel.Debug))
                {
                    LOG.LogDebug("Enabling Proxy:  [{proxy}] supported=[{supported}]",
                                 server.Proxy, httpHandler.SupportsProxy);
                }
                httpHandler.UseProxy = true;
                httpHandler.Proxy    = server.Proxy;
            }

            var requMessage = new HttpRequestMessage
            {
                Method     = verb,
                RequestUri = requUrl,
            };

            // By default, we only send JSON unless something else was specified
            var contentType = dscRequ.ContentTypeHeader;

            if (string.IsNullOrEmpty(contentType))
            {
                contentType = DscContentTypes.JSON;
            }
            requMessage.Headers.Accept.Clear();
            requMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(contentType));

            ExtractFromRequestModel(dscRequ, requMessage);

            // See if we need to add RegKey authorization data
            if (!string.IsNullOrEmpty(server.RegistrationKey) &&
                dscRequ.MsDateHeader == COMPUTE_MS_DATE_HEADER &&
                requMessage.Content != null)
            {
                LOG.LogInformation("Computing RegKey Authorization");

                // Shhh!  This is the super-secret formula for computing an
                // Authorization challenge when using Reg Key Authentication
                // Details can be found at /references/regkey-authorization.md
                var msDate = DateTime.UtcNow.ToString(DscRequest.X_MS_DATE_FORMAT);
                requMessage.Headers.Remove(DscRequest.X_MS_DATE_HEADER);
                requMessage.Headers.Add(DscRequest.X_MS_DATE_HEADER, msDate);
                dscRequ.MsDateHeader = null;

                var macKey = Encoding.UTF8.GetBytes(server.RegistrationKey);

                using (var sha = SHA256.Create())
                    using (var mac = new HMACSHA256(macKey))
                        using (var ms = new MemoryStream())
                        {
                            await requMessage.Content.CopyToAsync(ms);

                            var body = ms.ToArray();
                            LOG.LogDebug("Computing hash over body content");
                            LOG.LogDebug("BODY:-----------------------------");
                            LOG.LogDebug($"<{Encoding.UTF8.GetString(body)}>");
                            LOG.LogDebug("-----------------------------:BODY");

                            var digest = sha.ComputeHash(body);
                            var digB64 = Convert.ToBase64String(digest);
                            LOG.LogDebug("  * digB64=" + digB64);
                            var concat = $"{digB64}\n{msDate}";
                            LOG.LogDebug("  * concat=" + concat);
                            var macSig = mac.ComputeHash(Encoding.UTF8.GetBytes(concat));
                            var sigB64 = Convert.ToBase64String(macSig);

                            requMessage.Headers.Authorization = new AuthenticationHeaderValue("Shared", sigB64);
                        }
            }

            // TODO:  Eventually we'll address this improper usage pattern as described here:
            //    https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
            HttpResponseMessage respMessage = null;

            using (var http = _clientFactory(httpHandler))
            {
                respMessage = await http.SendAsync(requMessage);
            }

            respMessage.EnsureSuccessStatusCode();

            if (dscResp == null)
            {
                return(null);
            }
            else
            {
                return(await ExtractToResponseModel(respMessage, dscResp));
            }
        }