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 5968346

Browse files
alessio-peruginicmaglie
andauthored
settings: add network.connection_timeout and network.cloud_api.skip_board_detection_calls (#2770)
* settings: add `connection_timeout` * add test * add docs * Added gRPC flag to ignore Cloud API detection in BoardList* operations * Set default duration to 60 seconds * Updated JSON schema for configuration * Added option to disable call to Arduino Cloud API for board detection * Adjusted unit-tests --------- Co-authored-by: Cristian Maglie <c.maglie@arduino.cc>
1 parent 84fc413 commit 5968346

File tree

10 files changed

+270
-90
lines changed

10 files changed

+270
-90
lines changed

‎commands/service_board_list.go‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ func identifyViaCloudAPI(props *properties.Map, settings *configuration.Settings
139139
}
140140

141141
// identify returns a list of boards checking first the installed platforms or the Cloud API
142-
func identify(pme *packagemanager.Explorer, port *discovery.Port, settings *configuration.Settings) ([]*rpc.BoardListItem, error) {
142+
func identify(pme *packagemanager.Explorer, port *discovery.Port, settings *configuration.Settings, skipCloudAPIbool) ([]*rpc.BoardListItem, error) {
143143
boards := []*rpc.BoardListItem{}
144144
if port.Properties == nil {
145145
return boards, nil
@@ -170,7 +170,7 @@ func identify(pme *packagemanager.Explorer, port *discovery.Port, settings *conf
170170

171171
// if installed cores didn't recognize the board, try querying
172172
// the builder API if the board is a USB device port
173-
if len(boards) == 0 {
173+
if len(boards) == 0 &&!skipCloudAPI&&!settings.SkipCloudApiForBoardDetection() {
174174
items, err := identifyViaCloudAPI(port.Properties, settings)
175175
if err != nil {
176176
// this is bad, but keep going
@@ -225,7 +225,7 @@ func (s *arduinoCoreServerImpl) BoardList(ctx context.Context, req *rpc.BoardLis
225225

226226
ports := []*rpc.DetectedPort{}
227227
for _, port := range dm.List() {
228-
boards, err := identify(pme, port, s.settings)
228+
boards, err := identify(pme, port, s.settings, req.GetSkipCloudApiForBoardDetection())
229229
if err != nil {
230230
warnings = append(warnings, err.Error())
231231
}
@@ -298,7 +298,7 @@ func (s *arduinoCoreServerImpl) BoardListWatch(req *rpc.BoardListWatchRequest, s
298298

299299
boardsError := ""
300300
if event.Type == "add" {
301-
boards, err := identify(pme, event.Port, s.settings)
301+
boards, err := identify(pme, event.Port, s.settings, req.GetSkipCloudApiForBoardDetection())
302302
if err != nil {
303303
boardsError = err.Error()
304304
}

‎commands/service_board_list_test.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ func TestBoardIdentifySorting(t *testing.T) {
157157
defer release()
158158

159159
settings := configuration.NewSettings()
160-
res, err := identify(pme, &discovery.Port{Properties: idPrefs}, settings)
160+
res, err := identify(pme, &discovery.Port{Properties: idPrefs}, settings, true)
161161
require.NoError(t, err)
162162
require.NotNil(t, res)
163163
require.Len(t, res, 4)

‎commands/service_settings_test.go‎

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,12 +224,73 @@ func TestDelete(t *testing.T) {
224224
srv := NewArduinoCoreServer()
225225
loadConfig(t, srv, paths.New("testdata", "arduino-cli.yml"))
226226

227-
_, err := srv.SettingsGetValue(context.Background(), &rpc.SettingsGetValueRequest{Key: "network"})
227+
// Check loaded config
228+
resp, err := srv.ConfigurationSave(context.Background(), &rpc.ConfigurationSaveRequest{
229+
SettingsFormat: "yaml",
230+
})
231+
require.NoError(t, err)
232+
require.YAMLEq(t, `
233+
board_manager:
234+
additional_urls:
235+
- http://foobar.com
236+
- http://example.com
237+
238+
daemon:
239+
port: "50051"
240+
241+
directories:
242+
data: /home/massi/.arduino15
243+
downloads: /home/massi/.arduino15/staging
244+
245+
logging:
246+
file: ""
247+
format: text
248+
level: info
249+
250+
network:
251+
proxy: "123"
252+
`, resp.GetEncodedSettings())
253+
254+
// Check default and setted values
255+
res, err := srv.SettingsGetValue(context.Background(), &rpc.SettingsGetValueRequest{Key: "network"})
256+
require.NoError(t, err)
257+
require.Equal(t, `{"proxy":"123"}`, res.GetEncodedValue())
258+
// Maybe should be like this?
259+
// require.Equal(t, `{"proxy":"123","connection_timeout":"1m0s"}`, res.GetEncodedValue())
260+
res, err = srv.SettingsGetValue(context.Background(), &rpc.SettingsGetValueRequest{Key: "network.connection_timeout"})
261+
require.Equal(t, `"1m0s"`, res.GetEncodedValue()) // default value
228262
require.NoError(t, err)
229263

264+
// Run deletion
230265
_, err = srv.SettingsSetValue(context.Background(), &rpc.SettingsSetValueRequest{Key: "network", EncodedValue: ""})
231266
require.NoError(t, err)
267+
resp, err = srv.ConfigurationSave(context.Background(), &rpc.ConfigurationSaveRequest{
268+
SettingsFormat: "yaml",
269+
})
270+
require.NoError(t, err)
271+
require.YAMLEq(t, `
272+
board_manager:
273+
additional_urls:
274+
- http://foobar.com
275+
- http://example.com
232276
233-
_, err = srv.SettingsGetValue(context.Background(), &rpc.SettingsGetValueRequest{Key: "network"})
234-
require.Error(t, err)
277+
daemon:
278+
port: "50051"
279+
280+
directories:
281+
data: /home/massi/.arduino15
282+
downloads: /home/massi/.arduino15/staging
283+
284+
logging:
285+
file: ""
286+
format: text
287+
level: info
288+
`, resp.GetEncodedSettings())
289+
// Check default and setted values
290+
res, err = srv.SettingsGetValue(context.Background(), &rpc.SettingsGetValueRequest{Key: "network"})
291+
require.NoError(t, err)
292+
require.Equal(t, `{"connection_timeout":"1m0s"}`, res.GetEncodedValue())
293+
res, err = srv.SettingsGetValue(context.Background(), &rpc.SettingsGetValueRequest{Key: "network.connection_timeout"})
294+
require.Equal(t, `"1m0s"`, res.GetEncodedValue()) // default value
295+
require.NoError(t, err)
235296
}

‎docs/configuration.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@
4545
[time.ParseDuration()](https://pkg.go.dev/time#ParseDuration), defaults to `720h` (30 days).
4646
- `network` - configuration options related to the network connection.
4747
- `proxy` - URL of the proxy server.
48+
- `connection_timeout` - network connection timeout, the value format must be a valid input for
49+
[time.ParseDuration()](https://pkg.go.dev/time#ParseDuration), defaults to `60s` (60 seconds). `0` means it will
50+
wait indefinitely.
51+
- `cloud_api.skip_board_detection_calls` - if set to `true` it will make the Arduino CLI skip network calls to Arduino
52+
Cloud API to help detection of an unknown board.
4853

4954
### Default directories
5055

‎internal/cli/configuration/configuration.schema.json‎

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,8 @@
3838
},
3939
"ttl": {
4040
"description": "cache expiration time of build folders. If the cache is hit by a compilation the corresponding build files lifetime is renewed. The value format must be a valid input for time.ParseDuration(), defaults to `720h` (30 days)",
41-
"oneOf": [
42-
{
43-
"type": "integer",
44-
"minimum": 0
45-
},
46-
{
47-
"type": "string",
48-
"pattern": "^\\+?([0-9]?\\.?[0-9]+(([nuμm]?s)|m|h))+$"
49-
}
50-
]
41+
"type": "string",
42+
"pattern": "^[+-]?(([0-9]+(\\.[0-9]*)?|(\\.[0-9]+))(ns|us|μs|μs|ms|s|m|h))+$"
5143
}
5244
},
5345
"type": "object"
@@ -146,6 +138,35 @@
146138
},
147139
"type": "object"
148140
},
141+
"network": {
142+
"description": "settings related to network connections.",
143+
"type": "object",
144+
"properties": {
145+
"proxy": {
146+
"description": "proxy settings for network connections.",
147+
"type": "string"
148+
},
149+
"user_agent_ext": {
150+
"description": "extra string to append to the user agent string in HTTP requests.",
151+
"type": "string"
152+
},
153+
"connection_timeout": {
154+
"description": "timeout for network connections, defaults to '30s'",
155+
"type": "string",
156+
"pattern": "^[+-]?(([0-9]+(\\.[0-9]*)?|(\\.[0-9]+))(ns|us|μs|μs|ms|s|m|h))+$"
157+
},
158+
"cloud_api": {
159+
"description": "settings related to the Arduino Cloud API.",
160+
"type": "object",
161+
"properties": {
162+
"skip_board_detection_calls": {
163+
"description": "do not call the Arduino Cloud API to detect an unknown board",
164+
"type": "boolean"
165+
}
166+
}
167+
}
168+
}
169+
},
149170
"output": {
150171
"description": "settings related to text output.",
151172
"properties": {

‎internal/cli/configuration/defaults.go‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ func SetDefaults(settings *Settings) {
7171
// network settings
7272
setKeyTypeSchema("network.proxy", "")
7373
setKeyTypeSchema("network.user_agent_ext", "")
74+
setDefaultValueAndKeyTypeSchema("network.connection_timeout", (time.Second * 60).String())
75+
// network: Arduino Cloud API settings
76+
setKeyTypeSchema("network.cloud_api.skip_board_detection_calls", false)
7477

7578
// locale
7679
setKeyTypeSchema("locale", "")

‎internal/cli/configuration/network.go‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"net/url"
2323
"os"
2424
"runtime"
25+
"time"
2526

2627
"github.com/arduino/arduino-cli/commands/cmderrors"
2728
"github.com/arduino/arduino-cli/internal/i18n"
@@ -58,6 +59,19 @@ func (settings *Settings) ExtraUserAgent() string {
5859
return settings.GetString("network.user_agent_ext")
5960
}
6061

62+
// ConnectionTimeout returns the network connection timeout
63+
func (settings *Settings) ConnectionTimeout() time.Duration {
64+
if timeout, ok, _ := settings.GetDurationOk("network.connection_timeout"); ok {
65+
return timeout
66+
}
67+
return settings.Defaults.GetDuration("network.connection_timeout")
68+
}
69+
70+
// SkipCloudApiForBoardDetection returns whether the cloud API should be ignored for board detection
71+
func (settings *Settings) SkipCloudApiForBoardDetection() bool {
72+
return settings.GetBool("network.cloud_api.skip_board_detection_calls")
73+
}
74+
6175
// NetworkProxy returns the proxy configuration (mainly used by HTTP clients)
6276
func (settings *Settings) NetworkProxy() (*url.URL, error) {
6377
if proxyConfig, ok, _ := settings.GetStringOk("network.proxy"); !ok {
@@ -82,6 +96,7 @@ func (settings *Settings) NewHttpClient() (*http.Client, error) {
8296
},
8397
userAgent: settings.UserAgent(),
8498
},
99+
Timeout: settings.ConnectionTimeout(),
85100
}, nil
86101
}
87102

‎internal/cli/configuration/network_test.go‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"net/http"
2222
"net/http/httptest"
2323
"testing"
24+
"time"
2425

2526
"github.com/arduino/arduino-cli/internal/cli/configuration"
2627
"github.com/stretchr/testify/require"
@@ -68,3 +69,41 @@ func TestProxy(t *testing.T) {
6869
require.NoError(t, err)
6970
require.Equal(t, http.StatusNoContent, response.StatusCode)
7071
}
72+
73+
func TestConnectionTimeout(t *testing.T) {
74+
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
75+
time.Sleep(5 * time.Second)
76+
w.WriteHeader(http.StatusNoContent)
77+
}))
78+
defer ts.Close()
79+
80+
doRequest := func(timeout int) (*http.Response, time.Duration, error) {
81+
settings := configuration.NewSettings()
82+
require.NoError(t, settings.Set("network.proxy", ts.URL))
83+
if timeout != 0 {
84+
require.NoError(t, settings.Set("network.connection_timeout", "2s"))
85+
}
86+
client, err := settings.NewHttpClient()
87+
require.NoError(t, err)
88+
89+
request, err := http.NewRequest("GET", "http://arduino.cc", nil)
90+
require.NoError(t, err)
91+
92+
start := time.Now()
93+
resp, err := client.Do(request)
94+
duration := time.Since(start)
95+
96+
return resp, duration, err
97+
}
98+
99+
// Using default timeout (0), will wait indefinitely (in our case up to 5s)
100+
response, elapsed, err := doRequest(0)
101+
require.NoError(t, err)
102+
require.Equal(t, http.StatusNoContent, response.StatusCode)
103+
require.True(t, elapsed.Seconds() >= 4 && elapsed.Seconds() <= 6) // Adding some tolerance just in case...
104+
105+
// Setting a timeout of 1 seconds, will drop the connection after 1s
106+
_, elapsed, err = doRequest(1)
107+
require.Error(t, err)
108+
require.True(t, elapsed.Seconds() >= 0.5 && elapsed.Seconds() <= 3) // Adding some tolerance just in case...
109+
}

0 commit comments

Comments
(0)

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