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;
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); }
/// <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) ); }
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) { } }