Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions providers/flipt/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/open-feature/go-sdk v1.15.1 h1:TC3FtHtOKlGlIbSf3SEpxXVhgTd/bCbuc39XHIyltkw=
github.com/open-feature/go-sdk v1.15.1/go.mod h1:2WAFYzt8rLYavcubpCoiym3iSCXiHdPB6DxtMkv2wyo=
github.com/open-feature/go-sdk v1.16.0 h1:5NCHYv5slvNBIZhYXAzAufo0OI59OACZ5tczVqSE+Tg=
github.com/open-feature/go-sdk v1.16.0/go.mod h1:EIF40QcoYT1VbQkMPy2ZJH4kvZeY+qGUXAorzSWgKSo=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
Expand Down Expand Up @@ -51,16 +49,12 @@ go.opentelemetry.io/otel/sdk/metric v1.33.0 h1:Gs5VK9/WUJhNXZgn8MR6ITatvAmKeIuCt
go.opentelemetry.io/otel/sdk/metric v1.33.0/go.mod h1:dL5ykHZmm1B1nVRk9dDjChwDmt81MjVp3gLkQRwKf/Q=
go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
google.golang.org/genproto/googleapis/api v0.0.0-20241230172942-26aa7a208def h1:0Km0hi+g2KXbXL0+riZzSCKz23f4MmwicuEb00JeonI=
Expand Down
16 changes: 15 additions & 1 deletion providers/flipt/pkg/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"net/http"
"strconv"

"github.com/open-feature/go-sdk-contrib/providers/flipt/pkg/service/transport"
Expand All @@ -25,11 +26,19 @@ type Config struct {
TokenProvider sdk.ClientTokenProvider
Namespace string
GRPCDialOptions []grpc.DialOption
httpClient *http.Client
}

// Option is a configuration option for the provider.
type Option func(*Provider)

// WithHTTPClient returns an [Option] that specifies the HTTP client to use as the basis of communications.
func WithHTTPClient(client *http.Client) Option {
return func(p *Provider) {
p.config.httpClient = client
}
}

// WithAddress sets the address for the remote Flipt gRPC or HTTP API.
func WithAddress(address string) Option {
return func(p *Provider) {
Expand Down Expand Up @@ -86,14 +95,19 @@ func NewProvider(opts ...Option) *Provider {
Address: "http://localhost:8080",
Namespace: "default",
GRPCDialOptions: []grpc.DialOption{},
httpClient: http.DefaultClient,
}}

for _, opt := range opts {
opt(p)
}

if p.svc == nil {
topts := []transport.Option{transport.WithAddress(p.config.Address), transport.WithCertificatePath(p.config.CertificatePath)}
topts := []transport.Option{
transport.WithAddress(p.config.Address),
transport.WithHTTPClient(p.config.httpClient),
transport.WithCertificatePath(p.config.CertificatePath),
}
if p.config.TokenProvider != nil {
topts = append(topts, transport.WithClientTokenProvider(p.config.TokenProvider))
}
Expand Down
7 changes: 7 additions & 0 deletions providers/flipt/pkg/provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package flipt
import (
"encoding/json"
"errors"
"net/http"
"testing"

of "github.com/open-feature/go-sdk/openfeature"
Expand Down Expand Up @@ -672,3 +673,9 @@ func TestObjectEvaluation(t *testing.T) {
})
}
}

func TestWithHTTPClientOption(t *testing.T) {
client := &http.Client{}
p := NewProvider(WithHTTPClient(client))
assert.Equal(t, client, p.config.httpClient)
}
12 changes: 11 additions & 1 deletion providers/flipt/pkg/service/transport/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"crypto/x509"
"errors"
"fmt"
"net/http"
"net/url"
"os"
"strings"
Expand Down Expand Up @@ -40,11 +41,19 @@ type Service struct {
once sync.Once
tokenProvider sdk.ClientTokenProvider
grpcDialOptions []grpc.DialOption
httpClient *http.Client
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The httpClient field in the Service struct is not protected by a mutex. If multiple goroutines access the Service concurrently, they could potentially modify the httpClient simultaneously, leading to a race condition. To prevent this, consider adding a mutex to protect access to the httpClient field.

type Service struct {
    // ... other fields
    httpClient        *http.Client
    httpClientMutex   sync.RWMutex // Add this mutex
}

Then, use the mutex to protect access to the httpClient in the WithHTTPClient option and the instance method.

}

// Option is a service option.
type Option func(*Service)

// WithHTTPClient returns an [Option] that specifies the HTTP client to use as the basis of communications.
func WithHTTPClient(client *http.Client) Option {
return func(s *Service) {
s.httpClient = client
Comment on lines +51 to +53
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

To ensure thread-safe access to the httpClient, acquire a write lock on the httpClientMutex before modifying the httpClient and release it afterwards.

Consider the following code suggestion to address this issue.

func WithHTTPClient(client *http.Client) Option {
	return func(s *Service) {
		// Acquire a write lock before modifying the httpClient.
		// Release the lock after the modification is complete.
		// This ensures that only one goroutine can modify the httpClient at a time.
		// Prevents race conditions.
		 s.httpClientMutex.Lock()
		 s.httpClient = client
		 s.httpClientMutex.Unlock()
	}
}

}
}

// WithAddress sets the address for the remote Flipt gRPC API.
func WithAddress(address string) Option {
return func(s *Service) {
Expand Down Expand Up @@ -91,6 +100,7 @@ func New(opts ...Option) *Service {
grpcDialOptions: []grpc.DialOption{
grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
},
httpClient: http.DefaultClient,
}

for _, opt := range opts {
Expand Down Expand Up @@ -158,7 +168,7 @@ func (s *Service) instance() (offlipt.Client, error) {
opts = append(opts, sdk.WithClientTokenProvider(s.tokenProvider))
}

hclient := sdk.New(sdkhttp.NewTransport(s.address), opts...)
hclient := sdk.New(sdkhttp.NewTransport(s.address, sdkhttp.WithHTTPClient(s.httpClient)), opts...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

To ensure thread-safe access to the httpClient, acquire a read lock on the httpClientMutex before accessing the httpClient and release it afterwards.

Consider the following code suggestion to address this issue.

		// Acquire a read lock before accessing the httpClient.
		// Release the lock after the access is complete.
		// This allows multiple goroutines to read the httpClient concurrently,
		// but prevents them from modifying it while it's being read.
		 s.httpClientMutex.RLock()
		 hclient := sdk.New(sdkhttp.NewTransport(s.address, sdkhttp.WithHTTPClient(s.httpClient)), opts...)
		 s.httpClientMutex.RUnlock()
		
		if u.Scheme == "https" || u.Scheme == "http" {

if u.Scheme == "https" || u.Scheme == "http" {
s.client = &fclient{
hclient.Flipt(),
Expand Down
7 changes: 7 additions & 0 deletions providers/flipt/pkg/service/transport/service_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package transport

import (
"net/http"
"testing"

of "github.com/open-feature/go-sdk/openfeature"
Expand Down Expand Up @@ -288,3 +289,9 @@ func TestGRPCToOpenFeatureError(t *testing.T) {
})
}
}

func TestWithHTTPClientOption(t *testing.T) {
client := &http.Client{}
p := New(WithHTTPClient(client))
assert.Equal(t, client, p.httpClient)
}
Loading