Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 352
Enabling baggage cache to support limits and non-ascii characters#8713
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
0ac63beeb3ae1503d4b1ce111266576aab8f9c53d2a1e5ffbFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -24,42 +24,41 @@ public class BaggagePropagator implements Propagator { | ||
| private static final Logger LOG = LoggerFactory.getLogger(BaggagePropagator.class); | ||
| private static final PercentEscaper UTF_ESCAPER = PercentEscaper.create(); | ||
| static final String BAGGAGE_KEY = "baggage"; | ||
| private final Config config; | ||
| private final boolean injectBaggage; | ||
| private final boolean extractBaggage; | ||
| private final int maxItems; | ||
| private final int maxBytes; | ||
| public BaggagePropagator(Config config) { | ||
| this.injectBaggage = config.isBaggageInject(); | ||
| this.extractBaggage = config.isBaggageExtract(); | ||
| this.config = config; | ||
| this( | ||
| config.isBaggageInject(), | ||
| config.isBaggageInject(), | ||
| config.getTraceBaggageMaxItems(), | ||
| config.getTraceBaggageMaxBytes()); | ||
| } | ||
| // use primarily for testing purposes | ||
| public BaggagePropagator(boolean injectBaggage, boolean extractBaggage) { | ||
| BaggagePropagator(boolean injectBaggage, boolean extractBaggage, int maxItems, int maxBytes) { | ||
| this.injectBaggage = injectBaggage; | ||
| this.extractBaggage = extractBaggage; | ||
| this.config = Config.get(); | ||
| this.maxItems = maxItems; | ||
| this.maxBytes = maxBytes; | ||
| } | ||
| @Override | ||
| public <C> void inject(Context context, C carrier, CarrierSetter<C> setter) { | ||
| int maxItems = this.config.getTraceBaggageMaxItems(); | ||
| int maxBytes = this.config.getTraceBaggageMaxBytes(); | ||
| //noinspection ConstantValue | ||
| Baggage baggage; | ||
| if (!this.injectBaggage | ||
| || maxItems == 0 | ||
| || maxBytes == 0 | ||
| || this.maxItems == 0 | ||
| || this.maxBytes == 0 | ||
| || context == null | ||
| || carrier == null | ||
| || setter == null) { | ||
| return; | ||
| } | ||
| Baggage baggage = Baggage.fromContext(context); | ||
| if (baggage == null) { | ||
| || setter == null | ||
| || (baggage = Baggage.fromContext(context)) == null) { | ||
| return; | ||
| } | ||
| // Inject cached header if any as optimized path | ||
| String headerValue = baggage.getW3cHeader(); | ||
| if (headerValue != null) { | ||
| setter.set(carrier, BAGGAGE_KEY, headerValue); | ||
| @@ -86,25 +85,25 @@ public <C> void inject(Context context, C carrier, CarrierSetter<C> setter) { | ||
| processedItems++; | ||
| // reached the max number of baggage items allowed | ||
| if (processedItems == maxItems) { | ||
| if (processedItems == this.maxItems) { | ||
| break; | ||
| } | ||
| // Drop newest k/v pair if adding it leads to exceeding the limit | ||
| if (currentBytes + escapedKey.size + escapedVal.size + extraBytes > maxBytes) { | ||
| if (currentBytes + escapedKey.size + escapedVal.size + extraBytes > this.maxBytes) { | ||
| baggageText.setLength(currentBytes); | ||
| break; | ||
| } | ||
| currentBytes += escapedKey.size + escapedVal.size + extraBytes; | ||
| } | ||
| headerValue = baggageText.toString(); | ||
| // Save header as cache to re-inject it later if baggage did not change | ||
| baggage.setW3cHeader(headerValue); | ||
| setter.set(carrier, BAGGAGE_KEY, headerValue); | ||
| } | ||
| @Override | ||
| public <C> Context extract(Context context, C carrier, CarrierVisitor<C> visitor) { | ||
| //noinspection ConstantValue | ||
| if (!this.extractBaggage || context == null || carrier == null || visitor == null) { | ||
| return context; | ||
| } | ||
| @@ -113,12 +112,11 @@ public <C> Context extract(Context context, C carrier, CarrierVisitor<C> visitor | ||
| return baggageExtractor.extracted == null ? context : context.with(baggageExtractor.extracted); | ||
| } | ||
| private static class BaggageExtractor implements BiConsumer<String, String> { | ||
| private class BaggageExtractor implements BiConsumer<String, String> { | ||
| private static final char KEY_VALUE_SEPARATOR = '='; | ||
| private static final char PAIR_SEPARATOR = ','; | ||
| private Baggage extracted; | ||
| private BaggageExtractor() {} | ||
| private String w3cHeader; | ||
| /** URL decode value */ | ||
| private String decode(final String value) { | ||
| @@ -134,6 +132,7 @@ private String decode(final String value) { | ||
| private Map<String, String> parseBaggageHeaders(String input) { | ||
| Map<String, String> baggage = new HashMap<>(); | ||
| int start = 0; | ||
| boolean truncatedCache = false; | ||
| int pairSeparatorInd = input.indexOf(PAIR_SEPARATOR); | ||
| pairSeparatorInd = pairSeparatorInd == -1 ? input.length() : pairSeparatorInd; | ||
| int kvSeparatorInd = input.indexOf(KEY_VALUE_SEPARATOR); | ||
| @@ -152,11 +151,29 @@ private Map<String, String> parseBaggageHeaders(String input) { | ||
| } | ||
| baggage.put(key, value); | ||
| // need to percent-encode non-ascii headers we pass down | ||
| if (UTF_ESCAPER.keyNeedsEncoding(key) || UTF_ESCAPER.valNeedsEncoding(value)) { | ||
| truncatedCache = true; | ||
| this.w3cHeader = null; | ||
| } else if (!truncatedCache && (end > maxBytes || baggage.size() > maxItems)) { | ||
| if (start == 0) { // if we go out of range after first k/v pair, there is no cache | ||
| this.w3cHeader = null; | ||
| } else { | ||
| this.w3cHeader = input.substring(0, start - 1); // -1 to ignore the k/v separator | ||
| } | ||
| truncatedCache = true; | ||
Collaborator There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even if we don't cache here, we still need to maintain all of the raw baggage data so we can't break. | ||
| } | ||
| kvSeparatorInd = input.indexOf(KEY_VALUE_SEPARATOR, pairSeparatorInd + 1); | ||
| pairSeparatorInd = input.indexOf(PAIR_SEPARATOR, pairSeparatorInd + 1); | ||
| pairSeparatorInd = pairSeparatorInd == -1 ? input.length() : pairSeparatorInd; | ||
| start = end + 1; | ||
| } | ||
| if (!truncatedCache) { | ||
| this.w3cHeader = input; | ||
| } | ||
| return baggage; | ||
| } | ||
| @@ -166,7 +183,7 @@ public void accept(String key, String value) { | ||
| if (BAGGAGE_KEY.equalsIgnoreCase(key)) { | ||
| Map<String, String> baggage = parseBaggageHeaders(value); | ||
| if (!baggage.isEmpty()) { | ||
| this.extracted = Baggage.create(baggage, value); | ||
| this.extracted = Baggage.create(baggage, this.w3cHeader); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It’s not strictly truncated, it’s invalid in this case as non ascii characters are not allowed in HTTP headers. I though the RFC asked to drop the header and not parse it if it was invalid.
From RFC encoding:
From RFC error handling:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would argue that this isn't an invalid case and should be something that we can support since HTTP clients technically can send non-ascii characters as headers and we do support non-ascii characters as baggage. I would say that the invalid case is more for parsing issues with not being able to figure out what baggage should be used.