001package io.prometheus.metrics.config;
002
003import io.prometheus.metrics.annotations.StableApi;
004import javax.annotation.Nullable;
005
006@StableApi
007public enum EscapingScheme {
008  /** NO_ESCAPING indicates that a name will not be escaped. */
009  ALLOW_UTF8("allow-utf-8"),
010
011  /** UNDERSCORE_ESCAPING replaces all legacy-invalid characters with underscores. */
012  UNDERSCORE_ESCAPING("underscores"),
013
014  /**
015   * DOTS_ESCAPING is similar to UNDERSCORE_ESCAPING, except that dots are converted to `_dot_` and
016   * pre-existing underscores are converted to `__`.
017   */
018  DOTS_ESCAPING("dots"),
019
020  /**
021   * VALUE_ENCODING_ESCAPING prepends the name with `U__` and replaces all invalid characters with
022   * the Unicode value, surrounded by underscores. Single underscores are replaced with double
023   * underscores.
024   */
025  VALUE_ENCODING_ESCAPING("values");
026
027  private static final String ESCAPING_KEY = "escaping";
028
029  /** Default escaping scheme for names when not specified. */
030  public static final EscapingScheme DEFAULT = UNDERSCORE_ESCAPING;
031
032  public final String getValue() {
033    return value;
034  }
035
036  private final String value;
037
038  EscapingScheme(String value) {
039    this.value = value;
040  }
041
042  /**
043   * fromAcceptHeader returns an EscapingScheme depending on the Accept header. Iff the header
044   * contains an escaping=allow-utf-8 term, it will select NO_ESCAPING. If a valid "escaping" term
045   * exists, that will be used. Otherwise, the global default will be returned.
046   */
047  public static EscapingScheme fromAcceptHeader(@Nullable String acceptHeader) {
048    if (acceptHeader != null) {
049      for (String p : acceptHeader.split(";")) {
050        String[] toks = p.split("=");
051        if (toks.length != 2) {
052          continue;
053        }
054        String key = toks[0].trim();
055        String value = toks[1].trim();
056        if (key.equals(ESCAPING_KEY)) {
057          try {
058            return EscapingScheme.forString(value);
059          } catch (IllegalArgumentException e) {
060            // If the escaping parameter is unknown, ignore it.
061            return DEFAULT;
062          }
063        }
064      }
065    }
066    return DEFAULT;
067  }
068
069  static EscapingScheme forString(String value) {
070    switch (value) {
071      case "allow-utf-8":
072        return ALLOW_UTF8;
073      case "underscores":
074        return UNDERSCORE_ESCAPING;
075      case "dots":
076        return DOTS_ESCAPING;
077      case "values":
078        return VALUE_ENCODING_ESCAPING;
079      default:
080        throw new IllegalArgumentException("Unknown escaping scheme: " + value);
081    }
082  }
083
084  public String toHeaderFormat() {
085    return "; " + ESCAPING_KEY + "=" + value;
086  }
087}