Example #1
0
        public void Location_ReadAndWriteProperty_CallsForwardedToHttpGeneralHeaders()
        {
            Assert.Null(headers.Location);

            Uri expected = new Uri("http://example.com/path/");

            headers.Location = expected;
            Assert.Equal(expected, headers.Location);

            headers.Location = null;
            Assert.Null(headers.Location);
            Assert.False(headers.Contains("Location"),
                         "Header store should not contain a header 'Location' after setting it to null.");
        }
        public static IEnumerable <string> GetValuesSafe(this HttpResponseHeaders headers, string name)
        {
            IEnumerable <string> result = (headers?.Contains(name) ?? false) ? headers.GetValues(name) : emptyResult;

            return(result);
        }
 /// <summary>
 /// Except for the standard hop-by-hop headers (Keep-Alive, Transfer-Encoding, TE, Connection, Trailer, Upgrade,
 /// Proxy-Authorization and Proxy-Authenticate), any hop-by-hop headers used by the message must be listed in the
 /// Connection header, so that the first proxy knows it has to consume them and not forward them further.
 /// Standard hop-by-hop headers can be listed too (it is often the case of Keep-Alive, but this is not mandatory).
 /// See <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection">here</a> for more information.
 /// </summary>
 private static ICollection <string> GetNonStandardHopByHopHeaders(HttpResponseHeaders headers)
 => headers.Contains(HeaderNames.Connection) ? headers.Connection : ImmutableList <string> .Empty;
Example #4
0
        protected override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            //Get rate limiter for the application
            RateLimiter appRateLimiter = _rateLimiter.Get("App");

            //Get rate limiter for the method
            RateLimiter methodRateLimiter = _rateLimiter.Get("METHOD_" + request.Properties["Method"]);

            //Get limit for service
            DateTime serviceLimiter = _serviceLimiter.ContainsKey("EUW1") ? _serviceLimiter["EUW1"] : DateTime.MinValue;

            int nbSeconds;
            int secondsToWait = 0;

            if (appRateLimiter.IsLimited(out nbSeconds) && nbSeconds > 0)
            {
                secondsToWait = Math.Max(secondsToWait, nbSeconds);
                //_logger.Debug("App limited for " + nbSeconds);
            }

            if (methodRateLimiter.IsLimited(out nbSeconds) && nbSeconds > 0)
            {
                secondsToWait = Math.Max(secondsToWait, nbSeconds);
                //_logger.Debug("Method limited for " + nbSeconds);
            }

            if (serviceLimiter > DateTime.Now)
            {
                secondsToWait = Math.Max(secondsToWait, nbSeconds);
                //_logger.Debug("Service limited for " + nbSeconds);
            }

            if (secondsToWait > 0)
            {
                return(new HttpResponseMessage((HttpStatusCode)429)
                {
                    Content = new StringContent(secondsToWait.ToString())
                });
            }

            //We want the response to be gziped so we save bandwith
            request.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));

            var key = request.Headers.GetValues("X-Riot-Token").First();

            _logger.InfoFormat("[KEY {1}] Query {0}", request.RequestUri, key.Substring(key.Length - 5));


            HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

            HttpResponseHeaders headers = response.Headers;

            //Read limits values
            if (headers.Contains("X-App-Rate-Limit"))
            {
                appRateLimiter.Read(headers.GetValues("X-App-Rate-Limit").First(), headers.GetValues("X-App-Rate-Limit-Count").First());
            }

            if (headers.Contains("X-Method-Rate-Limit"))
            {
                methodRateLimiter.Read(headers.GetValues("X-Method-Rate-Limit").First(), headers.GetValues("X-Method-Rate-Limit-Count").First());
            }

            //still limited
            if ((int)response.StatusCode == 429)
            {
                _logger.Info("Aie");

                string rateLimitType = response.Headers.GetValues("X-Rate-Limit-Type").FirstOrDefault();
                string retryAfter    = response.Headers.GetValues("Retry-After").FirstOrDefault();

                //underlying service rate limited
                if (retryAfter == null)
                {
                    _logger.Info("Underlying service limited");
                    await Task.Delay(1000, cancellationToken);

                    return(await SendAsync(request, cancellationToken));
                }


                //riot API service limited
                int retryAfterNbSeconds = Math.Max(0, int.Parse(retryAfter));
                _logger.Info("Service limited for " + retryAfter);

                _serviceLimiter["EUW1"] = DateTime.Now.AddSeconds(retryAfterNbSeconds);

                return(new HttpResponseMessage((HttpStatusCode)429)
                {
                    Content = new StringContent(retryAfterNbSeconds.ToString())
                });
            }

            return(response);
        }
Example #5
0
        /// <summary>
        /// Extracts rate limit headers from the given HTTP response headers.
        /// Returns null if no rate limit headers are present.
        /// </summary>
        public static RateLimitHeaders ParseOrNull(HttpResponseHeaders headers)
        {
            bool isGlobal = headers.Contains("X-RateLimit-Global");

            int?retryAfterHeader = null;

            IEnumerable <string> retryAfterValues;

            if (headers.TryGetValues("Retry-After", out retryAfterValues))
            {
                string retryAfterStr = retryAfterValues.FirstOrDefault();

                int retryAfter;
                if (!string.IsNullOrWhiteSpace(retryAfterStr) && int.TryParse(retryAfterStr, out retryAfter))
                {
                    retryAfterHeader = retryAfter;
                }
            }

            if (!isGlobal)
            {
                int?   limitHeader = null, remainingHeader = null;
                double?resetTimeHeader = null;
                string bucket          = null;

                IEnumerable <string> limitValues;
                if (headers.TryGetValues("X-RateLimit-Limit", out limitValues))
                {
                    string limit = limitValues.FirstOrDefault();

                    int limitInt;
                    if (!string.IsNullOrWhiteSpace(limit) && int.TryParse(limit, out limitInt))
                    {
                        limitHeader = limitInt;
                    }
                }

                IEnumerable <string> remainingValues;
                if (headers.TryGetValues("X-RateLimit-Remaining", out remainingValues))
                {
                    string remainingStr = remainingValues.FirstOrDefault();

                    int remaining;
                    if (!string.IsNullOrWhiteSpace(remainingStr) && int.TryParse(remainingStr, out remaining))
                    {
                        remainingHeader = remaining;
                    }
                }

                IEnumerable <string> resetValues;
                if (headers.TryGetValues("X-RateLimit-Reset", out resetValues))
                {
                    string resetTimeStr = resetValues.FirstOrDefault();

                    double resetTime;
                    if (!string.IsNullOrWhiteSpace(resetTimeStr) && double.TryParse(resetTimeStr, out resetTime))
                    {
                        resetTimeHeader = resetTime;
                    }
                }

                IEnumerable <string> bucketValues;
                if (headers.TryGetValues("X-RateLimit-Bucket", out bucketValues))
                {
                    bucket = bucketValues.FirstOrDefault();
                }

                if (limitHeader.HasValue && remainingHeader.HasValue && resetTimeHeader.HasValue)
                {
                    return(new RateLimitHeaders(isGlobal,
                                                limitHeader.Value, remainingHeader.Value, resetTimeHeader.Value, retryAfterHeader, bucket));
                }
                else
                {
                    return(null);
                }
            }
            else
            {
                return(new RateLimitHeaders(isGlobal, retryAfterHeader));
            }
        }
        public void Test()
        {
            var metrics = _fixture.AgentLog.GetMetrics().ToList();
            var calleeTransactionEvent = _fixture.AgentLog.TryGetTransactionEvent("WebTransaction/MVC/DefaultController/Index");

            Assert.NotNull(calleeTransactionEvent);

            var callerTransactionTrace = _fixture.AgentLog.TryGetTransactionSample("WebTransaction/MVC/DefaultController/Chained");

            Assert.NotNull(callerTransactionTrace);

            var crossProcessId = _fixture.AgentLog.GetCrossProcessId();

            // Note: we are checking the metrics that are generated by the *Caller* as a result of receiving a CAT response.
            var expectedMetrics = new List <Assertions.ExpectedMetric>
            {
                new Assertions.ExpectedMetric {
                    metricName = @"External/all", callCount = 1
                },
                new Assertions.ExpectedMetric {
                    metricName = @"External/allWeb", callCount = 1
                },
                new Assertions.ExpectedMetric {
                    metricName = $@"External/{_fixture.RemoteApplication.DestinationServerName}/all", callCount = 1
                },
                new Assertions.ExpectedMetric {
                    metricName = $@"External/{_fixture.RemoteApplication.DestinationServerName}/Stream/GET", callCount = 1
                },
                new Assertions.ExpectedMetric {
                    metricName = $@"External/{_fixture.RemoteApplication.DestinationServerName}/Stream/GET", metricScope = @"WebTransaction/MVC/DefaultController/Chained", callCount = 1
                }
            };
            var unexpectedMetrics = new List <Assertions.ExpectedMetric>
            {
                new Assertions.ExpectedMetric {
                    metricName = $@"ExternalApp/{_fixture.RemoteApplication.DestinationServerName}/{crossProcessId}/all", callCount = 1
                },
                new Assertions.ExpectedMetric {
                    metricName = $@"ExternalTransaction/{_fixture.RemoteApplication.DestinationServerName}/{crossProcessId}/WebTransaction/MVC/DefaultController/Index"
                },
                new Assertions.ExpectedMetric {
                    metricName = $@"ExternalTransaction/{_fixture.RemoteApplication.DestinationServerName}/{crossProcessId}/WebTransaction/MVC/DefaultController/Index", metricScope = @"WebTransaction/MVC/DefaultController/Chained"
                },
                new Assertions.ExpectedMetric {
                    metricName = @"ClientApplication/[^/]+/all", IsRegexName = true
                }
            };
            var expectedCallerTraceSegmentRegexes = new List <string>
            {
                "External/[^/]+/Stream/GET"
            };

            NrAssert.Multiple
            (
                () => Assert.False(_responseHeaders.Contains(@"X-NewRelic-App-Data")),

                () => Assertions.MetricsExist(expectedMetrics, metrics),
                () => Assertions.MetricsDoNotExist(unexpectedMetrics, metrics),

                // Note: It is difficult (perhaps impossible) to ensure that a transaction trace is generate for the chained request. This is because only one transaction trace is collected per harvest, and the chained requests will both be eligible.

                // calleeTransactionEvent attributes
                () => Assertions.TransactionEventDoesNotHaveAttributes(Expectations.UnexpectedTransactionTraceIntrinsicAttributesCatDisabled, TransactionEventAttributeType.Intrinsic, calleeTransactionEvent),

                // callerTransactionTrace segments
                () => Assertions.TransactionTraceSegmentsExist(expectedCallerTraceSegmentRegexes, callerTransactionTrace, true)
            );
        }
Example #7
0
        public void Headers_Invalid()
        {
            HttpResponseMessage message = new HttpResponseMessage();
            HttpResponseHeaders headers = message.Headers;

            try {
                headers.Add("age", "");
                Assert.Fail("#1");
            } catch (FormatException) {
            }

            try {
                headers.Add(null, "");
                Assert.Fail("#2");
            } catch (ArgumentException) {
            }

            try {
                headers.Add("mm", null as IEnumerable <string>);
                Assert.Fail("#2b");
            } catch (ArgumentNullException) {
            }

            try {
                headers.Add("Allow", "audio");
                Assert.Fail("#2c");
            } catch (InvalidOperationException) {
            }

            Assert.IsFalse(headers.TryAddWithoutValidation("Allow", ""), "#3");

            Assert.IsFalse(headers.TryAddWithoutValidation(null, ""), "#4");

            try {
                headers.Contains(null);
                Assert.Fail("#5");
            } catch (ArgumentException) {
            }

            try {
                headers.GetValues(null);
                Assert.Fail("#6a");
            } catch (ArgumentException) {
            }

            try {
                headers.GetValues("bbbb");
                Assert.Fail("#6b");
            } catch (InvalidOperationException) {
            }

            try {
                headers.Add("location", new[] { "google.com", "xamarin.com" });
                Assert.Fail("#7a");
            } catch (FormatException) {
            }

            headers.TryAddWithoutValidation("location", "*****@*****.**");
            try {
                headers.Add("location", "w3.org");
                Assert.Fail("#7b");
            } catch (FormatException) {
            }
        }