Skip to content

Commit 4a444de

Browse files
committed
feat: Allow setting of application name to the User-Agent header value
1 parent e52048f commit 4a444de

11 files changed

+118
-9
lines changed

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
## [unreleased]
2+
### Features
3+
- [#358](https://github.com/influxdata/influxdb-client-go/pull/358):
4+
- Added possibility to set an application name, which will be part of the User-Agent HTTP header
5+
- Set using `Options.SetApplicationName`
6+
- Error message is written to log if an application name is not set.
7+
- Added example how to fully override `User-Agent` header using `Doer` interface
8+
29
### Bug fixes
310
- [#359](https://github.com/influxdata/influxdb-client-go/pull/359) `WriteAPIBlocking.Flush()` correctly returns nil error.
411

api/http/options.go

+13
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ type Options struct {
2323
tlsConfig *tls.Config
2424
// HTTP request timeout in sec. Default 20
2525
httpRequestTimeout uint
26+
// Application name in the User-Agent HTTP header string
27+
appName string
2628
}
2729

2830
// HTTPClient returns the http.Client that is configured to be used
@@ -119,6 +121,17 @@ func (o *Options) SetHTTPRequestTimeout(httpRequestTimeout uint) *Options {
119121
return o
120122
}
121123

124+
// ApplicationName returns application name used in the User-Agent HTTP header
125+
func (o *Options) ApplicationName() string {
126+
return o.appName
127+
}
128+
129+
// SetApplicationName sets an application name to the User-Agent HTTP header
130+
func (o *Options) SetApplicationName(appName string) *Options {
131+
o.appName = appName
132+
return o
133+
}
134+
122135
// DefaultOptions returns Options object with default values
123136
func DefaultOptions() *Options {
124137
return &Options{httpRequestTimeout: 20}

api/http/options_test.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ func TestDefaultOptions(t *testing.T) {
2020
assert.Equal(t, uint(20), opts.HTTPRequestTimeout())
2121
assert.NotNil(t, opts.HTTPClient())
2222
assert.True(t, opts.OwnHTTPClient())
23+
assert.EqualValues(t, "", opts.ApplicationName())
2324
}
2425

2526
func TestOptionsSetting(t *testing.T) {
@@ -28,9 +29,11 @@ func TestOptionsSetting(t *testing.T) {
2829
}
2930
opts := http.DefaultOptions().
3031
SetTLSConfig(tlsConfig).
31-
SetHTTPRequestTimeout(50)
32+
SetHTTPRequestTimeout(50).
33+
SetApplicationName("Monitor/1.1")
3234
assert.Equal(t, tlsConfig, opts.TLSConfig())
3335
assert.Equal(t, uint(50), opts.HTTPRequestTimeout())
36+
assert.EqualValues(t, "Monitor/1.1", opts.ApplicationName())
3437
if client := opts.HTTPClient(); assert.NotNil(t, client) {
3538
assert.Equal(t, 50*time.Second, client.Timeout)
3639
assert.Equal(t, tlsConfig, client.Transport.(*nethttp.Transport).TLSClientConfig)

api/http/service.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ type service struct {
5656
serverURL string
5757
authorization string
5858
client Doer
59+
userAgent string
5960
}
6061

6162
// NewService creates instance of http Service with given parameters
@@ -73,6 +74,7 @@ func NewService(serverURL, authorization string, httpOptions *Options) Service {
7374
serverURL: serverURL,
7475
authorization: authorization,
7576
client: httpOptions.HTTPDoer(),
77+
userAgent: http2.FormatUserAgent(httpOptions.ApplicationName()),
7678
}
7779
}
7880

@@ -128,7 +130,7 @@ func (s *service) DoHTTPRequestWithResponse(req *http.Request, requestCallback R
128130
req.Header.Set("Authorization", s.authorization)
129131
}
130132
if req.Header.Get("User-Agent") == "" {
131-
req.Header.Set("User-Agent", http2.UserAgent)
133+
req.Header.Set("User-Agent", s.userAgent)
132134
}
133135
if requestCallback != nil {
134136
requestCallback(req)

api/writeAPIBlocking.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,8 @@ func (w *writeAPIBlocking) write(ctx context.Context, line string) error {
8686
w.batch = append(w.batch, line)
8787
if len(w.batch) == int(w.writeOptions.BatchSize()) {
8888
return w.flush(ctx)
89-
} else {
90-
return nil
9189
}
90+
return nil
9291
}
9392
err := w.service.WriteBatch(ctx, iwrite.NewBatch(line, w.writeOptions.MaxRetryTime()))
9493
if err != nil {

client.go

+3
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ func NewClientWithOptions(serverURL string, authToken string, options *Options)
145145
}
146146
ilog.Infof("Using URL '%s'%s", serverURL, tokenStr)
147147
}
148+
if options.ApplicationName() == "" {
149+
ilog.Error("Application name is not set")
150+
}
148151
return client
149152
}
150153

client_test.go

+57-1
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@ package influxdb2
77
import (
88
"context"
99
"fmt"
10+
"log"
1011
"net/http"
1112
"net/http/httptest"
13+
"runtime"
14+
"strings"
1215
"testing"
1316
"time"
1417

18+
ihttp "github.com/influxdata/influxdb-client-go/v2/api/http"
1519
"github.com/influxdata/influxdb-client-go/v2/domain"
1620
http2 "github.com/influxdata/influxdb-client-go/v2/internal/http"
1721
iwrite "github.com/influxdata/influxdb-client-go/v2/internal/write"
@@ -76,24 +80,76 @@ func TestWriteAPIManagement(t *testing.T) {
7680
assert.Len(t, c.syncWriteAPIs, 0)
7781
}
7882

83+
func TestUserAgentBase(t *testing.T) {
84+
ua := fmt.Sprintf("influxdb-client-go/%s (%s; %s)", Version, runtime.GOOS, runtime.GOARCH)
85+
assert.Equal(t, ua, http2.UserAgentBase)
86+
87+
}
88+
89+
type doer struct {
90+
userAgent string
91+
doer ihttp.Doer
92+
}
93+
94+
func (d *doer) Do(req *http.Request) (*http.Response, error) {
95+
req.Header.Set("User-Agent", d.userAgent)
96+
return d.doer.Do(req)
97+
}
98+
7999
func TestUserAgent(t *testing.T) {
100+
ua := http2.UserAgentBase
80101
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
81102
<-time.After(100 * time.Millisecond)
82-
if r.Header.Get("User-Agent") == http2.UserAgent {
103+
if r.Header.Get("User-Agent") == ua {
83104
w.WriteHeader(http.StatusNoContent)
84105
} else {
85106
w.WriteHeader(http.StatusNotFound)
86107
}
87108
}))
88109

89110
defer server.Close()
111+
var sb strings.Builder
112+
log.SetOutput(&sb)
113+
log.SetFlags(0)
90114
c := NewClient(server.URL, "x")
115+
assert.True(t, strings.Contains(sb.String(), "Application name is not set"))
91116
up, err := c.Ping(context.Background())
92117
require.NoError(t, err)
93118
assert.True(t, up)
94119

95120
err = c.WriteAPIBlocking("o", "b").WriteRecord(context.Background(), "a,a=a a=1i")
96121
assert.NoError(t, err)
122+
123+
c.Close()
124+
sb.Reset()
125+
// Test setting application name
126+
c = NewClientWithOptions(server.URL, "x", DefaultOptions().SetApplicationName("Monitor/1.1"))
127+
ua = fmt.Sprintf("influxdb-client-go/%s (%s; %s) Monitor/1.1", Version, runtime.GOOS, runtime.GOARCH)
128+
assert.False(t, strings.Contains(sb.String(), "Application name is not set"))
129+
up, err = c.Ping(context.Background())
130+
require.NoError(t, err)
131+
assert.True(t, up)
132+
133+
err = c.WriteAPIBlocking("o", "b").WriteRecord(context.Background(), "a,a=a a=1i")
134+
assert.NoError(t, err)
135+
c.Close()
136+
137+
ua = "Monitor/1.1"
138+
opts := DefaultOptions()
139+
opts.HTTPOptions().SetHTTPDoer(&doer{
140+
userAgent: ua,
141+
doer: http.DefaultClient,
142+
})
143+
144+
//Create client with custom user agent setter
145+
c = NewClientWithOptions(server.URL, "x", opts)
146+
up, err = c.Ping(context.Background())
147+
require.NoError(t, err)
148+
assert.True(t, up)
149+
150+
err = c.WriteAPIBlocking("o", "b").WriteRecord(context.Background(), "a,a=a a=1i")
151+
assert.NoError(t, err)
152+
c.Close()
97153
}
98154

99155
func TestServerError429(t *testing.T) {

internal/http/userAgent.go

+14-2
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,17 @@
55
// Package http hold internal HTTP related stuff
66
package http
77

8-
// UserAgent keeps once created User-Agent string
9-
var UserAgent string
8+
import (
9+
"fmt"
10+
)
11+
12+
// UserAgentBase keeps once created base User-Agent string
13+
var UserAgentBase string
14+
15+
// FormatUserAgent creates User-Agent header value for application name
16+
func FormatUserAgent(appName string) string {
17+
if appName != "" {
18+
return fmt.Sprintf("%s %s", UserAgentBase, appName)
19+
}
20+
return UserAgentBase
21+
}

options.go

+11
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,17 @@ func (o *Options) AddDefaultTag(key, value string) *Options {
219219
return o
220220
}
221221

222+
// ApplicationName returns application name used in the User-Agent HTTP header
223+
func (o *Options) ApplicationName() string {
224+
return o.HTTPOptions().ApplicationName()
225+
}
226+
227+
// SetApplicationName sets an application name to the User-Agent HTTP header
228+
func (o *Options) SetApplicationName(appName string) *Options {
229+
o.HTTPOptions().SetApplicationName(appName)
230+
return o
231+
}
232+
222233
// DefaultOptions returns Options object with default values
223234
func DefaultOptions() *Options {
224235
return &Options{logLevel: 0, writeOptions: write.DefaultOptions(), httpOptions: http.DefaultOptions()}

options_test.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ func TestDefaultOptions(t *testing.T) {
3333
assert.EqualValues(t, (*tls.Config)(nil), opts.TLSConfig())
3434
assert.EqualValues(t, 20, opts.HTTPRequestTimeout())
3535
assert.EqualValues(t, 0, opts.LogLevel())
36+
assert.EqualValues(t, "", opts.ApplicationName())
3637
}
3738

3839
func TestSettingsOptions(t *testing.T) {
@@ -53,7 +54,8 @@ func TestSettingsOptions(t *testing.T) {
5354
SetTLSConfig(tlsConfig).
5455
SetHTTPRequestTimeout(50).
5556
SetLogLevel(3).
56-
AddDefaultTag("t", "a")
57+
AddDefaultTag("t", "a").
58+
SetApplicationName("Monitor/1.1")
5759
assert.EqualValues(t, 5, opts.BatchSize())
5860
assert.EqualValues(t, true, opts.UseGZip())
5961
assert.EqualValues(t, 5_000, opts.FlushInterval())
@@ -66,6 +68,7 @@ func TestSettingsOptions(t *testing.T) {
6668
assert.EqualValues(t, 5, opts.ExponentialBase())
6769
assert.EqualValues(t, tlsConfig, opts.TLSConfig())
6870
assert.EqualValues(t, 50, opts.HTTPRequestTimeout())
71+
assert.EqualValues(t, "Monitor/1.1", opts.ApplicationName())
6972
if client := opts.HTTPClient(); assert.NotNil(t, client) {
7073
assert.EqualValues(t, 50*time.Second, client.Timeout)
7174
assert.Equal(t, tlsConfig, client.Transport.(*http.Transport).TLSClientConfig)

version.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@ const (
1717
)
1818

1919
func init() {
20-
http.UserAgent = fmt.Sprintf("influxdb-client-go/%s (%s; %s)", Version, runtime.GOOS, runtime.GOARCH)
20+
http.UserAgentBase = fmt.Sprintf("influxdb-client-go/%s (%s; %s)", Version, runtime.GOOS, runtime.GOARCH)
2121
}

0 commit comments

Comments
 (0)