Example #1
0
        /// <summary>
        /// Set the HTTP headers in the response using values from
        /// the supplied flow data.
        /// If the supplied headers already have values in the response
        /// then they will be amended rather than replaced.
        /// </summary>
        /// <param name="context">
        /// The <see cref="HttpContext"/> to set the response headers in
        /// </param>
        /// <param name="flowData">
        /// The flow data containing the headers to set.
        /// </param>
        public static void SetHeaders(HttpContext context,
                                      IFlowData flowData)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
            if (flowData == null)
            {
                throw new ArgumentNullException(nameof(flowData));
            }

            var element = flowData.Pipeline.GetElement <ISetHeadersElement>();

            foreach (var header in flowData.GetFromElement(element)
                     .ResponseHeaderDictionary)
            {
                if (context.Response.Headers.ContainsKey(header.Key))
                {
                    context.Response.Headers.Append(header.Key, header.Value);
                }
                else
                {
                    context.Response.Headers.Add(header.Key, header.Value);
                }
            }
        }
Example #2
0
        public void ValidateData(IFlowData data, bool validEvidence = true)
        {
            var elementData = data.GetFromElement(_engine);
            var dict        = elementData.AsDictionary();

            foreach (var property in _engine.Properties
                     .Where(p => p.Available &&
                            // The JavascriptImageOptimiser property is deprecated.
                            // It exists in the meta-data but is never populated
                            // so we need to ignore it here.
                            p.Name.Equals("JavascriptImageOptimiser", StringComparison.OrdinalIgnoreCase) == false))
            {
                Assert.IsTrue(dict.ContainsKey(property.Name));
                IAspectPropertyValue value = dict[property.Name] as IAspectPropertyValue;
                if (validEvidence)
                {
                    Assert.IsTrue(value.HasValue);
                }
                else
                {
                    if (property.Category.Equals("Device Metrics"))
                    {
                        Assert.IsTrue(value.HasValue);
                    }
                    else
                    {
                        Assert.IsFalse(value.HasValue);
                        if (EvidenceContainsUserAgent(data) == false)
                        {
                            Assert.AreEqual("The evidence required to determine" +
                                            " this property was not supplied. The most" +
                                            " common evidence passed to this engine is" +
                                            " 'header.user-agent'.", value.NoValueMessage);
                        }
                        else
                        {
                            Assert.AreEqual("No matching profiles could be " +
                                            "found for the supplied evidence. A 'best " +
                                            "guess' can be returned by configuring more " +
                                            "lenient matching rules. See " +
                                            "https://51degrees.com/documentation/_device_detection__features__false_positive_control.html", value.NoValueMessage);
                        }
                    }
                }
            }
            Assert.IsTrue(string.IsNullOrEmpty(elementData.DeviceId.Value) == false);
            if (validEvidence == false)
            {
                Assert.AreEqual("0-0-0-0", elementData.DeviceId.Value);
            }

            var validKeys = data.GetEvidence().AsDictionary().Keys.Where(
                k => _engine.EvidenceKeyFilter.Include(k)).Count();

            Assert.AreEqual(validKeys, elementData.UserAgents.Value.Count);
        }
Example #3
0
        public void ValidateProfileIds(IFlowData data, string[] profileIds)
        {
            var elementData     = data.GetFromElement(_engine);
            var matchedProfiles = elementData.DeviceId.Value.Split('-');

            foreach (var profileId in profileIds)
            {
                Assert.IsTrue(matchedProfiles.Contains(profileId),
                              $"The profile '{profileId}' was not set in the result.");
            }
        }
        /// <summary>
        /// Retrieve the raw JSON response from the
        /// <see cref="CloudRequestEngine"/> in this pipeline, extract
        /// the data for this specific engine and populate the
        /// <code>TData</code> instance accordingly.
        /// </summary>
        /// <param name="data">
        /// The <see cref="IFlowData"/> to get the raw JSON data from.
        /// </param>
        /// <param name="aspectData">
        /// The <code>TData</code> instance to populate with values.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Thrown if a required parameter is null.
        /// </exception>
        protected override void ProcessEngine(IFlowData data, T aspectData)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            CloudRequestData requestData;

            // Get requestData from CloudRequestEngine. If requestData does not
            // exist in the element data TypedKeyMap then the engine either
            // does not exist in the Pipeline or is not run before this engine.
            try
            {
                requestData = data.GetFromElement(RequestEngine.GetInstance());
            }
            catch (KeyNotFoundException ex)
            {
                throw new PipelineConfigurationException(
                          $"The '{GetType().Name}' requires a 'CloudRequestEngine' " +
                          $"before it in the Pipeline. This engine will be unable " +
                          $"to produce results until this is corrected.", ex);
            }

            // Check the requestData ProcessStarted flag which informs whether
            // the cloud request engine process method was called.
            if (requestData?.ProcessStarted == false)
            {
                throw new PipelineConfigurationException(
                          $"The '{GetType().Name}' requires a 'CloudRequestEngine' " +
                          $"before it in the Pipeline. This engine will be unable " +
                          $"to produce results until this is corrected.");
            }

            var json = requestData?.JsonResponse;

            // If the JSON is empty or null then do not Process the CloudAspectEngine.
            // Empty or null JSON indicates that an error has occurred in the
            // CloudRequestEngine. The error will have been reported by the
            // CloudRequestEngine so just log a warning that this
            // CloudAspectEngine did not process.
            if (string.IsNullOrEmpty(json) == false)
            {
                ProcessCloudEngine(data, aspectData, json);
            }
            else
            {
                Logger.LogInformation($"The '{GetType().Name}' did not process " +
                                      $"as the JSON response from the CloudRequestEngine was null " +
                                      $"or empty. Please refer to errors generated by the " +
                                      $"CloudRequestEngine in the logs as this indicates an error " +
                                      $"occurred there.");
            }
        }
        /// <summary>
        /// Retrieve the raw JSON response from the
        /// <see cref="CloudRequestEngine"/> in this pipeline, extract
        /// the data for this specific engine and populate the
        /// <code>TData</code> instance accordingly.
        /// </summary>
        /// <param name="data">
        /// The <see cref="IFlowData"/> to get the raw JSON data from.
        /// </param>
        /// <param name="aspectData">
        /// The <code>TData</code> instance to populate with values.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Thrown if a required parameter is null.
        /// </exception>
        protected override void ProcessEngine(IFlowData data, TData aspectData)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            var requestData = data.GetFromElement(RequestEngine.GetInstance());
            var json        = requestData?.JsonResponse;

            if (string.IsNullOrEmpty(json))
            {
                throw new PipelineConfigurationException(
                          $"Json response from cloud request engine is null. " +
                          $"This is probably because there is not a " +
                          $"'CloudRequestEngine' before the '{GetType().Name}' " +
                          $"in the Pipeline. This engine will be unable " +
                          $"to produce results until this is corrected.");
            }
            else
            {
                // Extract data from json to the aspectData instance.
                var dictionary = JsonConvert.DeserializeObject <Dictionary <string, object> >(json);
                // Access the data relating to this engine.
                var propertyKeyed = dictionary[ElementDataKey] as JObject;
                // Access the 'Profiles' property
                foreach (var entry in propertyKeyed["profiles"])
                {
                    // Iterate through the devices, parsing each one and
                    // adding it to the result.
                    var propertyValues = JsonConvert.DeserializeObject <Dictionary <string, object> >(entry.ToString(),
                                                                                                      new JsonSerializerSettings()
                    {
                        Converters = JSON_CONVERTERS,
                    });
                    var device = CreateProfileData();
                    // Get the meta-data for properties on device instances.
                    var propertyMetaData = Properties
                                           .Single(p => p.Name == "Profiles").ItemProperties;

                    var deviceData = CreateAPVDictionary(
                        propertyValues,
                        propertyMetaData);

                    device.PopulateFromDictionary(deviceData);
                    //device.SetNoValueReasons(nullReasons);
                    aspectData.AddProfile(device);
                }
            }
        }
Example #6
0
        /// <summary>
        /// Private method that checks if the result is already in the cache
        /// or not.
        /// If it is then the result is added to 'data', if not then
        /// <see cref="ProcessEngine(IFlowData, T)"/> is called to do so.
        /// </summary>
        /// <param name="data">
        /// The <see cref="IFlowData"/> instance that provides the evidence
        /// and holds the result.
        /// </param>
        private void ProcessWithCache(IFlowData data)
        {
            T cacheResult = default(T);

            // If there is a cache then check if the result
            // is already in there.
            if (_cache != null)
            {
                try
                {
                    cacheResult = (T)_cache[data];
                }
                catch (InvalidCastException) { }
            }
            // If we don't have a result from the cache then
            // run through the normal processing.
            if (cacheResult == null)
            {
                // If the flow data already contains an entry for this
                // element's key then use it. Otherwise, create a new
                // aspect data instance and add it to the flow data.
                T aspectData =
                    data.GetOrAdd(ElementDataKeyTyped, CreateElementData);
                if (aspectData.Engines.Contains(this) == false)
                {
                    (aspectData as AspectDataBase).AddEngine(this);
                }

                // Start the engine processing
                if (LazyLoadingConfiguration != null)
                {
                    // If lazy loading is configured then create a task
                    // to do the processing and assign the task to the
                    // aspect data property.
                    var task = Task.Run(() =>
                    {
                        ProcessEngine(data, aspectData);
                    });
                    (aspectData as AspectDataBase).AddProcessTask(task);
                }
                else
                {
                    // If not lazy loading, just start processing.
                    ProcessEngine(data, aspectData);
                }
                // If there is a cache then add the result
                // of processing to the cache.
                if (_cache != null)
                {
                    _cache.Put(data, data.GetFromElement(this));
                }
            }
            else
            {
                // We have a result from the cache so add it
                // into the flow data.
                data.GetOrAdd(ElementDataKeyTyped, (f) =>
                {
                    return(cacheResult);
                });
            }
        }
        private void ServeContent(IContextAdapter context, IFlowData flowData, ContentType contentType)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
            if (flowData == null)
            {
                throw new ArgumentNullException(nameof(flowData));
            }

            context.Response.Clear();
            context.Response.ClearHeaders();

            // Get the hash code.
            var hash = flowData.GenerateKey(_pipeline.EvidenceKeyFilter).GetHashCode();

            if (int.TryParse(context.Request.GetHeaderValue("If-None-Match"),
                             out int previousHash) &&
                hash == previousHash)
            {
                // The response hasn't changed so respond with a 304.
                context.Response.StatusCode = 304;
            }
            else
            {
                // Otherwise, return the requested content to the client.
                string content = null;
                switch (contentType)
                {
                case ContentType.JavaScript:
                    var jsElement = flowData.Pipeline.GetElement <JavaScriptBuilderElement>();
                    if (jsElement == null)
                    {
                        throw new PipelineConfigurationException(
                                  Messages.ExceptionNoJavaScriptBuilder);
                    }
                    var jsData = flowData.GetFromElement(jsElement);
                    content = jsData?.JavaScript;
                    break;

                case ContentType.Json:
                    var jsonElement = flowData.Pipeline.GetElement <JsonBuilderElement>();
                    if (jsonElement == null)
                    {
                        throw new PipelineConfigurationException(
                                  Messages.ExceptionNoJsonBuilder);
                    }
                    var jsonData = flowData.GetFromElement(jsonElement);
                    content = jsonData?.Json;
                    break;

                default:
                    break;
                }

                int length = 0;
                if (content != null && content.Length > 0)
                {
                    length = Encoding.UTF8.GetBytes(content).Length;
                }

                context.Response.StatusCode = 200;
                SetHeaders(context,
                           hash.ToString(CultureInfo.InvariantCulture),
                           length,
                           contentType == ContentType.JavaScript ? "x-javascript" : "json");

                context.Response.Write(content);
            }
        }