Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit c5cfe4a

Browse files
MatteoPologrutocmaglie
andauthored
Add metadata retrieved from the context to the user agent when a new HTTP client is created (#2789)
* Set the extra user agent when a new rpc instance is created * Add integration test * Moved user-agent extraction deep in configuration.HttpClient This allows the extraction of the user-agent in a single place. Also it forces the context passing on all operations that requires access to network. * Updated integration test * Restore previous user-agent for package manager * Apply review suggestions to the integration test --------- Co-authored-by: Cristian Maglie <c.maglie@arduino.cc>
1 parent c562ff3 commit c5cfe4a

File tree

11 files changed

+115
-37
lines changed

11 files changed

+115
-37
lines changed

‎commands/instances.go‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func (s *arduinoCoreServerImpl) Create(ctx context.Context, req *rpc.CreateReque
8989
}
9090
}
9191

92-
config, err := s.settings.DownloaderConfig()
92+
config, err := s.settings.DownloaderConfig(ctx)
9393
if err != nil {
9494
return nil, err
9595
}
@@ -377,7 +377,7 @@ func (s *arduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCor
377377
responseError(err.GRPCStatus())
378378
continue
379379
}
380-
config, err := s.settings.DownloaderConfig()
380+
config, err := s.settings.DownloaderConfig(ctx)
381381
if err != nil {
382382
taskCallback(&rpc.TaskProgress{Name: i18n.Tr("Error downloading library %s", libraryRef)})
383383
e := &cmderrors.FailedLibraryInstallError{Cause: err}
@@ -498,7 +498,7 @@ func (s *arduinoCoreServerImpl) UpdateLibrariesIndex(req *rpc.UpdateLibrariesInd
498498
}
499499

500500
// Perform index update
501-
config, err := s.settings.DownloaderConfig()
501+
config, err := s.settings.DownloaderConfig(stream.Context())
502502
if err != nil {
503503
return err
504504
}
@@ -608,7 +608,7 @@ func (s *arduinoCoreServerImpl) UpdateIndex(req *rpc.UpdateIndexRequest, stream
608608
}
609609
}
610610

611-
config, err := s.settings.DownloaderConfig()
611+
config, err := s.settings.DownloaderConfig(stream.Context())
612612
if err != nil {
613613
downloadCB.Start(u, i18n.Tr("Downloading index: %s", filepath.Base(URL.Path)))
614614
downloadCB.End(false, i18n.Tr("Invalid network configuration: %s", err))

‎commands/service_board_identify.go‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func (s *arduinoCoreServerImpl) BoardIdentify(ctx context.Context, req *rpc.Boar
4848
defer release()
4949

5050
props := properties.NewFromHashmap(req.GetProperties())
51-
res, err := identify(pme, props, s.settings, !req.GetUseCloudApiForUnknownBoardDetection())
51+
res, err := identify(ctx, pme, props, s.settings, !req.GetUseCloudApiForUnknownBoardDetection())
5252
if err != nil {
5353
return nil, err
5454
}
@@ -58,7 +58,7 @@ func (s *arduinoCoreServerImpl) BoardIdentify(ctx context.Context, req *rpc.Boar
5858
}
5959

6060
// identify returns a list of boards checking first the installed platforms or the Cloud API
61-
func identify(pme *packagemanager.Explorer, properties *properties.Map, settings *configuration.Settings, skipCloudAPI bool) ([]*rpc.BoardListItem, error) {
61+
func identify(ctx context.Context, pme *packagemanager.Explorer, properties *properties.Map, settings *configuration.Settings, skipCloudAPI bool) ([]*rpc.BoardListItem, error) {
6262
if properties == nil {
6363
return nil, nil
6464
}
@@ -90,7 +90,7 @@ func identify(pme *packagemanager.Explorer, properties *properties.Map, settings
9090
// if installed cores didn't recognize the board, try querying
9191
// the builder API if the board is a USB device port
9292
if len(boards) == 0 && !skipCloudAPI && !settings.SkipCloudApiForBoardDetection() {
93-
items, err := identifyViaCloudAPI(properties, settings)
93+
items, err := identifyViaCloudAPI(ctx, properties, settings)
9494
if err != nil {
9595
// this is bad, but keep going
9696
logrus.WithError(err).Debug("Error querying builder API")
@@ -119,22 +119,22 @@ func identify(pme *packagemanager.Explorer, properties *properties.Map, settings
119119
return boards, nil
120120
}
121121

122-
func identifyViaCloudAPI(props *properties.Map, settings *configuration.Settings) ([]*rpc.BoardListItem, error) {
122+
func identifyViaCloudAPI(ctx context.Context, props *properties.Map, settings *configuration.Settings) ([]*rpc.BoardListItem, error) {
123123
// If the port is not USB do not try identification via cloud
124124
if !props.ContainsKey("vid") || !props.ContainsKey("pid") {
125125
return nil, nil
126126
}
127127

128128
logrus.Debug("Querying builder API for board identification...")
129-
return cachedAPIByVidPid(props.Get("vid"), props.Get("pid"), settings)
129+
return cachedAPIByVidPid(ctx, props.Get("vid"), props.Get("pid"), settings)
130130
}
131131

132132
var (
133133
vidPidURL = "https://builder.arduino.cc/v3/boards/byVidPid"
134134
validVidPid = regexp.MustCompile(`0[xX][a-fA-F\d]{4}`)
135135
)
136136

137-
func cachedAPIByVidPid(vid, pid string, settings *configuration.Settings) ([]*rpc.BoardListItem, error) {
137+
func cachedAPIByVidPid(ctx context.Context, vid, pid string, settings *configuration.Settings) ([]*rpc.BoardListItem, error) {
138138
var resp []*rpc.BoardListItem
139139

140140
cacheKey := fmt.Sprintf("cache.builder-api.v3/boards/byvid/pid/%s/%s", vid, pid)
@@ -148,7 +148,7 @@ func cachedAPIByVidPid(vid, pid string, settings *configuration.Settings) ([]*rp
148148
}
149149
}
150150

151-
resp, err := apiByVidPid(vid, pid, settings) // Perform API requrest
151+
resp, err := apiByVidPid(ctx, vid, pid, settings) // Perform API requrest
152152

153153
if err == nil {
154154
if cachedResp, err := json.Marshal(resp); err == nil {
@@ -160,7 +160,7 @@ func cachedAPIByVidPid(vid, pid string, settings *configuration.Settings) ([]*rp
160160
return resp, err
161161
}
162162

163-
func apiByVidPid(vid, pid string, settings *configuration.Settings) ([]*rpc.BoardListItem, error) {
163+
func apiByVidPid(ctx context.Context, vid, pid string, settings *configuration.Settings) ([]*rpc.BoardListItem, error) {
164164
// ensure vid and pid are valid before hitting the API
165165
if !validVidPid.MatchString(vid) {
166166
return nil, errors.New(i18n.Tr("Invalid vid value: '%s'", vid))
@@ -173,7 +173,7 @@ func apiByVidPid(vid, pid string, settings *configuration.Settings) ([]*rpc.Boar
173173
req, _ := http.NewRequest("GET", url, nil)
174174
req.Header.Set("Content-Type", "application/json")
175175

176-
httpClient, err := settings.NewHttpClient()
176+
httpClient, err := settings.NewHttpClient(ctx)
177177
if err != nil {
178178
return nil, fmt.Errorf("%s: %w", i18n.Tr("failed to initialize http client"), err)
179179
}

‎commands/service_board_identify_test.go‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package commands
1717

1818
import (
19+
"context"
1920
"fmt"
2021
"net/http"
2122
"net/http/httptest"
@@ -48,15 +49,15 @@ func TestGetByVidPid(t *testing.T) {
4849

4950
vidPidURL = ts.URL
5051
settings := configuration.NewSettings()
51-
res, err := apiByVidPid("0xf420", "0XF069", settings)
52+
res, err := apiByVidPid(context.Background(), "0xf420", "0XF069", settings)
5253
require.Nil(t, err)
5354
require.Len(t, res, 1)
5455
require.Equal(t, "Arduino/Genuino MKR1000", res[0].GetName())
5556
require.Equal(t, "arduino:samd:mkr1000", res[0].GetFqbn())
5657

5758
// wrong vid (too long), wrong pid (not an hex value)
5859

59-
_, err = apiByVidPid("0xfffff", "0xDEFG", settings)
60+
_, err = apiByVidPid(context.Background(), "0xfffff", "0xDEFG", settings)
6061
require.NotNil(t, err)
6162
}
6263

@@ -69,7 +70,7 @@ func TestGetByVidPidNotFound(t *testing.T) {
6970
defer ts.Close()
7071

7172
vidPidURL = ts.URL
72-
res, err := apiByVidPid("0x0420", "0x0069", settings)
73+
res, err := apiByVidPid(context.Background(), "0x0420", "0x0069", settings)
7374
require.NoError(t, err)
7475
require.Empty(t, res)
7576
}
@@ -84,7 +85,7 @@ func TestGetByVidPid5xx(t *testing.T) {
8485
defer ts.Close()
8586

8687
vidPidURL = ts.URL
87-
res, err := apiByVidPid("0x0420", "0x0069", settings)
88+
res, err := apiByVidPid(context.Background(), "0x0420", "0x0069", settings)
8889
require.NotNil(t, err)
8990
require.Equal(t, "the server responded with status 500 Internal Server Error", err.Error())
9091
require.Len(t, res, 0)
@@ -99,15 +100,15 @@ func TestGetByVidPidMalformedResponse(t *testing.T) {
99100
defer ts.Close()
100101

101102
vidPidURL = ts.URL
102-
res, err := apiByVidPid("0x0420", "0x0069", settings)
103+
res, err := apiByVidPid(context.Background(), "0x0420", "0x0069", settings)
103104
require.NotNil(t, err)
104105
require.Equal(t, "wrong format in server response", err.Error())
105106
require.Len(t, res, 0)
106107
}
107108

108109
func TestBoardDetectionViaAPIWithNonUSBPort(t *testing.T) {
109110
settings := configuration.NewSettings()
110-
items, err := identifyViaCloudAPI(properties.NewMap(), settings)
111+
items, err := identifyViaCloudAPI(context.Background(), properties.NewMap(), settings)
111112
require.NoError(t, err)
112113
require.Empty(t, items)
113114
}
@@ -156,7 +157,7 @@ func TestBoardIdentifySorting(t *testing.T) {
156157
defer release()
157158

158159
settings := configuration.NewSettings()
159-
res, err := identify(pme, idPrefs, settings, true)
160+
res, err := identify(context.Background(), pme, idPrefs, settings, true)
160161
require.NoError(t, err)
161162
require.NotNil(t, res)
162163
require.Len(t, res, 4)

‎commands/service_check_for_updates.go‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func (s *arduinoCoreServerImpl) CheckForArduinoCLIUpdates(ctx context.Context, r
4343
inventory.WriteStore()
4444
}()
4545

46-
latestVersion, err := semver.Parse(s.getLatestRelease())
46+
latestVersion, err := semver.Parse(s.getLatestRelease(ctx))
4747
if err != nil {
4848
return nil, err
4949
}
@@ -82,8 +82,8 @@ func (s *arduinoCoreServerImpl) shouldCheckForUpdate(currentVersion *semver.Vers
8282

8383
// getLatestRelease queries the official Arduino download server for the latest release,
8484
// if there are no errors or issues a version string is returned, in all other case an empty string.
85-
func (s *arduinoCoreServerImpl) getLatestRelease() string {
86-
client, err := s.settings.NewHttpClient()
85+
func (s *arduinoCoreServerImpl) getLatestRelease(ctx context.Context) string {
86+
client, err := s.settings.NewHttpClient(ctx)
8787
if err != nil {
8888
return ""
8989
}

‎commands/service_library_download.go‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,15 @@ func (s *arduinoCoreServerImpl) LibraryDownload(req *rpc.LibraryDownloadRequest,
8282
})
8383
}
8484

85-
func downloadLibrary(ctx context.Context, downloadsDir *paths.Path, libRelease *librariesindex.Release,
86-
downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB, queryParameter string, settings *configuration.Settings) error {
87-
85+
func downloadLibrary(
86+
ctx context.Context,
87+
downloadsDir *paths.Path, libRelease *librariesindex.Release,
88+
downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB,
89+
queryParameter string,
90+
settings *configuration.Settings,
91+
) error {
8892
taskCB(&rpc.TaskProgress{Name: i18n.Tr("Downloading %s", libRelease)})
89-
config, err := settings.DownloaderConfig()
93+
config, err := settings.DownloaderConfig(ctx)
9094
if err != nil {
9195
return &cmderrors.FailedDownloadError{Message: i18n.Tr("Can't download library"), Cause: err}
9296
}

‎internal/arduino/resources/helpers_test.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func TestDownloadApplyUserAgentHeaderUsingConfig(t *testing.T) {
5555

5656
settings := configuration.NewSettings()
5757
settings.Set("network.user_agent_ext", goldUserAgentValue)
58-
config, err := settings.DownloaderConfig()
58+
config, err := settings.DownloaderConfig(context.Background())
5959
require.NoError(t, err)
6060
err = r.Download(context.Background(), tmp, config, "", func(progress *rpc.DownloadProgress) {}, "")
6161
require.NoError(t, err)

‎internal/cli/configuration/network.go‎

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,21 @@
1616
package configuration
1717

1818
import (
19+
"context"
1920
"errors"
2021
"fmt"
2122
"net/http"
2223
"net/url"
2324
"os"
2425
"runtime"
26+
"strings"
2527
"time"
2628

2729
"github.com/arduino/arduino-cli/commands/cmderrors"
2830
"github.com/arduino/arduino-cli/internal/i18n"
2931
"github.com/arduino/arduino-cli/internal/version"
3032
"go.bug.st/downloader/v2"
33+
"google.golang.org/grpc/metadata"
3134
)
3235

3336
// UserAgent returns the user agent (mainly used by HTTP clients)
@@ -84,17 +87,23 @@ func (settings *Settings) NetworkProxy() (*url.URL, error) {
8487
}
8588

8689
// NewHttpClient returns a new http client for use in the arduino-cli
87-
func (settings *Settings) NewHttpClient() (*http.Client, error) {
90+
func (settings *Settings) NewHttpClient(ctx context.Context) (*http.Client, error) {
8891
proxy, err := settings.NetworkProxy()
8992
if err != nil {
9093
return nil, err
9194
}
95+
userAgent := settings.UserAgent()
96+
if md, ok := metadata.FromIncomingContext(ctx); ok {
97+
if extraUserAgent := strings.Join(md.Get("user-agent"), " "); extraUserAgent != "" {
98+
userAgent += " " + extraUserAgent
99+
}
100+
}
92101
return &http.Client{
93102
Transport: &httpClientRoundTripper{
94103
transport: &http.Transport{
95104
Proxy: http.ProxyURL(proxy),
96105
},
97-
userAgent: settings.UserAgent(),
106+
userAgent: userAgent,
98107
},
99108
Timeout: settings.ConnectionTimeout(),
100109
}, nil
@@ -111,8 +120,8 @@ func (h *httpClientRoundTripper) RoundTrip(req *http.Request) (*http.Response, e
111120
}
112121

113122
// DownloaderConfig returns the downloader configuration based on current settings.
114-
func (settings *Settings) DownloaderConfig() (downloader.Config, error) {
115-
httpClient, err := settings.NewHttpClient()
123+
func (settings *Settings) DownloaderConfig(ctx context.Context) (downloader.Config, error) {
124+
httpClient, err := settings.NewHttpClient(ctx)
116125
if err != nil {
117126
return downloader.Config{}, &cmderrors.InvalidArgumentError{
118127
Message: i18n.Tr("Could not connect via HTTP"),

‎internal/cli/configuration/network_test.go‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package configuration_test
1717

1818
import (
19+
"context"
1920
"fmt"
2021
"io"
2122
"net/http"
@@ -35,7 +36,7 @@ func TestUserAgentHeader(t *testing.T) {
3536

3637
settings := configuration.NewSettings()
3738
require.NoError(t, settings.Set("network.user_agent_ext", "test-user-agent"))
38-
client, err := settings.NewHttpClient()
39+
client, err := settings.NewHttpClient(context.Background())
3940
require.NoError(t, err)
4041

4142
request, err := http.NewRequest("GET", ts.URL, nil)
@@ -59,7 +60,7 @@ func TestProxy(t *testing.T) {
5960

6061
settings := configuration.NewSettings()
6162
settings.Set("network.proxy", ts.URL)
62-
client, err := settings.NewHttpClient()
63+
client, err := settings.NewHttpClient(context.Background())
6364
require.NoError(t, err)
6465

6566
request, err := http.NewRequest("GET", "http://arduino.cc", nil)
@@ -83,7 +84,7 @@ func TestConnectionTimeout(t *testing.T) {
8384
if timeout != 0 {
8485
require.NoError(t, settings.Set("network.connection_timeout", "2s"))
8586
}
86-
client, err := settings.NewHttpClient()
87+
client, err := settings.NewHttpClient(context.Background())
8788
require.NoError(t, err)
8889

8990
request, err := http.NewRequest("GET", "http://arduino.cc", nil)

‎internal/integrationtest/arduino-cli.go‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,11 @@ func (cli *ArduinoCLI) StartDaemon(verbose bool) string {
450450
for retries := 5; retries > 0; retries-- {
451451
time.Sleep(time.Second)
452452

453-
conn, err := grpc.NewClient(cli.daemonAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
453+
conn, err := grpc.NewClient(
454+
cli.daemonAddr,
455+
grpc.WithTransportCredentials(insecure.NewCredentials()),
456+
grpc.WithUserAgent("cli-test/0.0.0"),
457+
)
454458
if err != nil {
455459
connErr = err
456460
continue

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /