@@ -10,7 +10,8 @@ namespace NewRelic.Agent.Extensions.Helpers;
1010/// Parses request-queue-time headers set by upstream load balancers or ingress controllers
1111/// (X-Request-Start, X-Queue-Start) and returns the time the request spent queued before
1212/// reaching the application. Unit auto-detection handles ns/us/ms/s epoch timestamps.
13- /// Values earlier than 2000-01-01 are rejected. Future timestamps (clock skew) yield null.
13+ /// Future timestamps (clock skew) yield null, as do queue times beyond a sanity cap
14+ /// (a misdetected unit or a stale/garbage timestamp produces an implausibly large delta).
1415/// Parsing avoids regex and per-call allocations because it runs on every web request.
1516/// </summary>
1617public static class QueueTimeHeaderParser
@@ -19,14 +20,18 @@ public static class QueueTimeHeaderParser
1920 private const string HeaderQueueStart = "X-Queue-Start" ;
2021
2122 private static readonly DateTime _epoch = new DateTime ( 1970 , 1 , 1 , 0 , 0 , 0 , DateTimeKind . Utc ) ;
22- private static readonly DateTime _floor = new DateTime ( 2000 , 1 , 1 , 0 , 0 , 0 , DateTimeKind . Utc ) ;
23+
24+ // Upper bound on a plausible front-end queue (load balancer + network + host accept).
25+ // A genuine wait is seconds, not minutes; a larger delta means the timestamp was
26+ // misdetected, stale, or garbage, so we omit rather than report a bogus queue time.
27+ private static readonly TimeSpan _maxQueueTime = TimeSpan . FromMinutes ( 10 ) ;
2328
2429 /// <summary>
2530 /// Reads X-Request-Start and X-Queue-Start headers via <paramref name="getHeader"/>,
2631 /// parses Unix-epoch timestamps (auto-detects ns/us/ms/s by magnitude), and returns
2732 /// the queue time as <c>nowUtc - earliestStartTime</c>. Returns null when no valid
28- /// header is found, all candidates predate 2000-01-01, or the start time is in the
29- /// future relative to <paramref name="nowUtc"/> (clock skew guard).
33+ /// header is found, the start time is in the future relative to <paramref name="nowUtc"/>
34+ /// (clock skew guard), or the computed queue time exceeds the sanity cap .
3035 /// </summary>
3136 /// <param name="getHeader">Delegate that returns a header value by name, or null/empty if absent.</param>
3237 /// <param name="nowUtc">The current UTC time used to compute elapsed queue time.</param>
@@ -38,7 +43,11 @@ public static class QueueTimeHeaderParser
3843 if ( earliest == null || earliest . Value > nowUtc )
3944 return null ;
4045
41- return nowUtc - earliest . Value ;
46+ var queueTime = nowUtc - earliest . Value ;
47+ if ( queueTime > _maxQueueTime )
48+ return null ;
49+
50+ return queueTime ;
4251 }
4352
4453 private static DateTime ? SelectEarlier ( DateTime ? current , string raw )
@@ -76,15 +85,19 @@ public static class QueueTimeHeaderParser
7685 if ( double . IsNaN ( value ) || double . IsInfinity ( value ) )
7786 return null ;
7887
88+ // Auto-detect the timestamp unit by magnitude and normalize to seconds. A current
89+ // Unix epoch is ~1.7e9 in seconds (10 digits), ~1.7e12 in ms (13), ~1.7e15 in us (16),
90+ // and ~1.7e18 in ns (19). Each threshold sits an order of magnitude below its bucket
91+ // so the correct unit wins; values below 1e12 are taken as already being in seconds.
7992 double seconds ;
8093 if ( value >= 1e18 )
81- seconds = value / 1e9 ;
94+ seconds = value / 1e9 ; // nanoseconds -> seconds
8295 else if ( value >= 1e15 )
83- seconds = value / 1e6 ;
96+ seconds = value / 1e6 ; // microseconds -> seconds
8497 else if ( value >= 1e12 )
85- seconds = value / 1e3 ;
98+ seconds = value / 1e3 ; // milliseconds -> seconds
8699 else
87- seconds = value ;
100+ seconds = value ; // already seconds
88101
89102 DateTime startTime ;
90103 try
@@ -96,9 +109,6 @@ public static class QueueTimeHeaderParser
96109 return null ;
97110 }
98111
99- if ( startTime < _floor )
100- return null ;
101-
102112 return startTime ;
103113 }
104114
0 commit comments