@@ -2,23 +2,42 @@ package featureflags
22
33import (
44 "context"
5+ "flag"
56 "mime"
67 "net/http"
78 "strings"
89
910 "connectrpc.com/connect"
11+ "github.com/go-kit/log"
1012 "github.com/go-kit/log/level"
1113 "github.com/grafana/dskit/middleware"
12- "github.com/grafana/pyroscope/pkg/util"
1314 "google.golang.org/grpc"
1415 "google.golang.org/grpc/metadata"
1516)
1617
1718const (
1819 // Capability names - update parseClientCapabilities below when new capabilities added
1920 allowUtf8LabelNamesCapabilityName string = "allow-utf8-labelnames"
21+
22+ // Config
23+ clientCapabilityPrefix = "client-capability."
24+ allowUtf8LabelNames = clientCapabilityPrefix + allowUtf8LabelNamesCapabilityName
2025)
2126
27+ type ClientCapabilityConfig struct {
28+ AllowUtf8LabelNames bool `yaml:"allow_utf_8_label_names" category:"experimental"`
29+ }
30+
31+ func (cfg * ClientCapabilityConfig ) RegisterFlags (fs * flag.FlagSet ) {
32+ fs .BoolVar (
33+ & cfg .AllowUtf8LabelNames ,
34+ allowUtf8LabelNames ,
35+ false ,
36+ "Enable reading and writing utf-8 label names. To use this feature, API calls must " +
37+ "include `allow-utf8-labelnames=true` in the `Accept` header." ,
38+ )
39+ }
40+
2241// Define a custom context key type to avoid collisions
2342type contextKey struct {}
2443
@@ -35,7 +54,7 @@ func GetClientCapabilities(ctx context.Context) (ClientCapabilities, bool) {
3554 return value , ok
3655}
3756
38- func ClientCapabilitiesGRPCMiddleware () grpc.UnaryServerInterceptor {
57+ func ClientCapabilitiesGRPCMiddleware (cfg * ClientCapabilityConfig , logger log. Logger ) grpc.UnaryServerInterceptor {
3958 return func (
4059 ctx context.Context ,
4160 req interface {},
@@ -56,7 +75,7 @@ func ClientCapabilitiesGRPCMiddleware() grpc.UnaryServerInterceptor {
5675 }
5776
5877 // Reuse existing HTTP header parsing
59- clientCapabilities , err := parseClientCapabilities (httpHeader )
78+ clientCapabilities , err := parseClientCapabilities (httpHeader , cfg , logger )
6079 if err != nil {
6180 return nil , connect .NewError (connect .CodeInvalidArgument , err )
6281 }
@@ -68,10 +87,10 @@ func ClientCapabilitiesGRPCMiddleware() grpc.UnaryServerInterceptor {
6887
6988// ClientCapabilitiesHttpMiddleware creates middleware that extracts and parses the
7089// `Accept` header for capabilities the client supports
71- func ClientCapabilitiesHttpMiddleware () middleware.Interface {
90+ func ClientCapabilitiesHttpMiddleware (cfg * ClientCapabilityConfig , logger log. Logger ) middleware.Interface {
7291 return middleware .Func (func (next http.Handler ) http.Handler {
7392 return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
74- clientCapabilities , err := parseClientCapabilities (r .Header )
93+ clientCapabilities , err := parseClientCapabilities (r .Header , cfg , logger )
7594 if err != nil {
7695 http .Error (w , "Invalid header format: " + err .Error (), http .StatusBadRequest )
7796 return
@@ -83,7 +102,7 @@ func ClientCapabilitiesHttpMiddleware() middleware.Interface {
83102 })
84103}
85104
86- func parseClientCapabilities (header http.Header ) (ClientCapabilities , error ) {
105+ func parseClientCapabilities (header http.Header , cfg * ClientCapabilityConfig , logger log. Logger ) (ClientCapabilities , error ) {
87106 acceptHeaderValues := header .Values ("Accept" )
88107
89108 var capabilities ClientCapabilities
@@ -100,10 +119,16 @@ func parseClientCapabilities(header http.Header) (ClientCapabilities, error) {
100119 switch k {
101120 case allowUtf8LabelNamesCapabilityName :
102121 if v == "true" {
103- capabilities .AllowUtf8LabelNames = true
122+ if ! cfg .AllowUtf8LabelNames {
123+ level .Warn (logger ).Log (
124+ "msg" , "client requested capability that is not enabled on server" ,
125+ "capability" , allowUtf8LabelNamesCapabilityName )
126+ } else {
127+ capabilities .AllowUtf8LabelNames = true
128+ }
104129 }
105130 default :
106- level .Debug (util . Logger ).Log (
131+ level .Debug (logger ).Log (
107132 "msg" , "unknown capability parsed from Accept header" ,
108133 "acceptHeaderKey" , k ,
109134 "acceptHeaderValue" , v )
0 commit comments