From 09da1d39b9bc2945389d00ce91167be53692f637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Ferrand?= Date: Tue, 21 Jan 2025 12:39:07 +0100 Subject: [PATCH 1/2] feat: Add support for specifying URL Query params in HTTP resource --- docs/gossfile.md | 2 ++ resource/http.go | 66 +++++++++++++++++++++++----------------------- system/http.go | 68 +++++++++++++++++++++++++++--------------------- util/config.go | 1 + 4 files changed, 76 insertions(+), 61 deletions(-) diff --git a/docs/gossfile.md b/docs/gossfile.md index a463c2ce..c407787c 100644 --- a/docs/gossfile.md +++ b/docs/gossfile.md @@ -335,6 +335,8 @@ http: timeout: 1000 request-headers: # Set request header values - "Content-Type: text/html" + request-query-params: + foo: 'bar' headers: [] # Check http response headers for these patterns (e.g. "Content-Type: text/html") request-body: '{"key": "value"}' # request body body: [] # Check http response content for these patterns diff --git a/resource/http.go b/resource/http.go index bbd82009..279a9ddd 100644 --- a/resource/http.go +++ b/resource/http.go @@ -10,26 +10,27 @@ import ( ) type HTTP struct { - Title string `json:"title,omitempty" yaml:"title,omitempty"` - Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - id string `json:"-" yaml:"-"` - URL string `json:"url,omitempty" yaml:"url,omitempty"` - Method string `json:"method,omitempty" yaml:"method,omitempty"` - Status matcher `json:"status" yaml:"status"` - AllowInsecure bool `json:"allow-insecure" yaml:"allow-insecure"` - NoFollowRedirects bool `json:"no-follow-redirects" yaml:"no-follow-redirects"` - Timeout int `json:"timeout,omitempty" yaml:"timeout,omitempty"` - RequestHeader []string `json:"request-headers,omitempty" yaml:"request-headers,omitempty"` - RequestBody string `json:"request-body,omitempty" yaml:"request-body,omitempty"` - Headers matcher `json:"headers,omitempty" yaml:"headers,omitempty"` - Body matcher `json:"body,omitempty" yaml:"body,omitempty"` - Username string `json:"username,omitempty" yaml:"username,omitempty"` - Password string `json:"password,omitempty" yaml:"password,omitempty"` - CAFile string `json:"ca-file,omitempty" yaml:"ca-file,omitempty"` - CertFile string `json:"cert-file,omitempty" yaml:"cert-file,omitempty"` - KeyFile string `json:"key-file,omitempty" yaml:"key-file,omitempty"` - Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` - Proxy string `json:"proxy,omitempty" yaml:"proxy,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` + id string `json:"-" yaml:"-"` + URL string `json:"url,omitempty" yaml:"url,omitempty"` + Method string `json:"method,omitempty" yaml:"method,omitempty"` + Status matcher `json:"status" yaml:"status"` + AllowInsecure bool `json:"allow-insecure" yaml:"allow-insecure"` + NoFollowRedirects bool `json:"no-follow-redirects" yaml:"no-follow-redirects"` + Timeout int `json:"timeout,omitempty" yaml:"timeout,omitempty"` + RequestHeader []string `json:"request-headers,omitempty" yaml:"request-headers,omitempty"` + RequestBody string `json:"request-body,omitempty" yaml:"request-body,omitempty"` + RequestQueryParams map[string]string `json:"request-query-params,omitempty" yaml:"request-query-params,omitempty"` + Headers matcher `json:"headers,omitempty" yaml:"headers,omitempty"` + Body matcher `json:"body,omitempty" yaml:"body,omitempty"` + Username string `json:"username,omitempty" yaml:"username,omitempty"` + Password string `json:"password,omitempty" yaml:"password,omitempty"` + CAFile string `json:"ca-file,omitempty" yaml:"ca-file,omitempty"` + CertFile string `json:"cert-file,omitempty" yaml:"cert-file,omitempty"` + KeyFile string `json:"key-file,omitempty" yaml:"key-file,omitempty"` + Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` + Proxy string `json:"proxy,omitempty" yaml:"proxy,omitempty"` } const ( @@ -75,7 +76,7 @@ func (u *HTTP) Validate(sys *system.System) []TestResult { KeyFile: u.KeyFile, NoFollowRedirects: u.NoFollowRedirects, Timeout: time.Duration(u.Timeout) * time.Millisecond, Username: u.Username, Password: u.Password, Proxy: u.Proxy, - RequestHeader: u.RequestHeader, RequestBody: u.RequestBody, Method: u.Method}) + RequestHeader: u.RequestHeader, RequestBody: u.RequestBody, RequestQueryParams: u.RequestQueryParams, Method: u.Method}) sysHTTP.SetAllowInsecure(u.AllowInsecure) sysHTTP.SetNoFollowRedirects(u.NoFollowRedirects) @@ -98,17 +99,18 @@ func NewHTTP(sysHTTP system.HTTP, config util.Config) (*HTTP, error) { http := sysHTTP.HTTP() status, err := sysHTTP.Status() u := &HTTP{ - id: http, - Status: status, - RequestHeader: []string{}, - Headers: nil, - Body: []string{}, - AllowInsecure: config.AllowInsecure, - NoFollowRedirects: config.NoFollowRedirects, - Timeout: config.TimeOutMilliSeconds(), - Username: config.Username, - Password: config.Password, - Proxy: config.Proxy, + id: http, + Status: status, + RequestHeader: []string{}, + RequestQueryParams: nil, + Headers: nil, + Body: []string{}, + AllowInsecure: config.AllowInsecure, + NoFollowRedirects: config.NoFollowRedirects, + Timeout: config.TimeOutMilliSeconds(), + Username: config.Username, + Password: config.Password, + Proxy: config.Proxy, } return u, err } diff --git a/system/http.go b/system/http.go index d5e5abec..07d1edda 100644 --- a/system/http.go +++ b/system/http.go @@ -30,22 +30,23 @@ type HTTP interface { } type DefHTTP struct { - http string - allowInsecure bool - noFollowRedirects bool - resp *http.Response - RequestHeader http.Header - RequestBody string - Timeout int - loaded bool - err error - Username string - Password string - CAFile string - CertFile string - KeyFile string - Method string - Proxy string + http string + allowInsecure bool + noFollowRedirects bool + resp *http.Response + RequestHeader http.Header + RequestBody string + RequestQueryParams map[string]string + Timeout int + loaded bool + err error + Username string + Password string + CAFile string + CertFile string + KeyFile string + Method string + Proxy string } func NewDefHTTP(_ context.Context, httpStr string, system *System, config util.Config) HTTP { @@ -60,19 +61,20 @@ func NewDefHTTP(_ context.Context, httpStr string, system *System, config util.C headers.Add(str[0], str[1]) } return &DefHTTP{ - http: httpStr, - allowInsecure: config.AllowInsecure, - Method: config.Method, - noFollowRedirects: config.NoFollowRedirects, - RequestHeader: headers, - RequestBody: config.RequestBody, - Timeout: config.TimeOutMilliSeconds(), - Username: config.Username, - Password: config.Password, - CAFile: config.CAFile, - CertFile: config.CertFile, - KeyFile: config.KeyFile, - Proxy: config.Proxy, + http: httpStr, + allowInsecure: config.AllowInsecure, + Method: config.Method, + noFollowRedirects: config.NoFollowRedirects, + RequestHeader: headers, + RequestBody: config.RequestBody, + RequestQueryParams: config.RequestQueryParams, + Timeout: config.TimeOutMilliSeconds(), + Username: config.Username, + Password: config.Password, + CAFile: config.CAFile, + CertFile: config.CertFile, + KeyFile: config.KeyFile, + Proxy: config.Proxy, } } @@ -162,6 +164,14 @@ func (u *DefHTTP) setupReal() error { req.Host = host } + if u.RequestQueryParams != nil { + qParams := req.URL.Query() + for k, v := range u.RequestQueryParams { + qParams.Add(k, v) + } + req.URL.RawQuery = qParams.Encode() + } + if u.Username != "" || u.Password != "" { req.SetBasicAuth(u.Username, u.Password) } diff --git a/util/config.go b/util/config.go index 3be5fa84..6a1a2256 100644 --- a/util/config.go +++ b/util/config.go @@ -42,6 +42,7 @@ type Config struct { RequestBody string Proxy string RequestHeader []string + RequestQueryParams map[string]string RetryTimeout time.Duration RunLevel string Server string From 366954041edf3108cc199ec55e4a7ebaa8d55988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Ferrand?= Date: Wed, 14 May 2025 07:18:02 +0200 Subject: [PATCH 2/2] feat: Adapt to @aelsabbahy map[string][]string suggestion * See https://github.com/goss-org/goss/pull/1013/files#r2070757415 --- docs/gossfile.md | 3 ++- resource/http.go | 42 +++++++++++++++++++++--------------------- system/http.go | 8 +++++--- util/config.go | 2 +- 4 files changed, 29 insertions(+), 26 deletions(-) diff --git a/docs/gossfile.md b/docs/gossfile.md index c407787c..d4b4f3d3 100644 --- a/docs/gossfile.md +++ b/docs/gossfile.md @@ -336,7 +336,8 @@ http: request-headers: # Set request header values - "Content-Type: text/html" request-query-params: - foo: 'bar' + foo: + - 'bar' headers: [] # Check http response headers for these patterns (e.g. "Content-Type: text/html") request-body: '{"key": "value"}' # request body body: [] # Check http response content for these patterns diff --git a/resource/http.go b/resource/http.go index 279a9ddd..d35a07cb 100644 --- a/resource/http.go +++ b/resource/http.go @@ -10,27 +10,27 @@ import ( ) type HTTP struct { - Title string `json:"title,omitempty" yaml:"title,omitempty"` - Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - id string `json:"-" yaml:"-"` - URL string `json:"url,omitempty" yaml:"url,omitempty"` - Method string `json:"method,omitempty" yaml:"method,omitempty"` - Status matcher `json:"status" yaml:"status"` - AllowInsecure bool `json:"allow-insecure" yaml:"allow-insecure"` - NoFollowRedirects bool `json:"no-follow-redirects" yaml:"no-follow-redirects"` - Timeout int `json:"timeout,omitempty" yaml:"timeout,omitempty"` - RequestHeader []string `json:"request-headers,omitempty" yaml:"request-headers,omitempty"` - RequestBody string `json:"request-body,omitempty" yaml:"request-body,omitempty"` - RequestQueryParams map[string]string `json:"request-query-params,omitempty" yaml:"request-query-params,omitempty"` - Headers matcher `json:"headers,omitempty" yaml:"headers,omitempty"` - Body matcher `json:"body,omitempty" yaml:"body,omitempty"` - Username string `json:"username,omitempty" yaml:"username,omitempty"` - Password string `json:"password,omitempty" yaml:"password,omitempty"` - CAFile string `json:"ca-file,omitempty" yaml:"ca-file,omitempty"` - CertFile string `json:"cert-file,omitempty" yaml:"cert-file,omitempty"` - KeyFile string `json:"key-file,omitempty" yaml:"key-file,omitempty"` - Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` - Proxy string `json:"proxy,omitempty" yaml:"proxy,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` + id string `json:"-" yaml:"-"` + URL string `json:"url,omitempty" yaml:"url,omitempty"` + Method string `json:"method,omitempty" yaml:"method,omitempty"` + Status matcher `json:"status" yaml:"status"` + AllowInsecure bool `json:"allow-insecure" yaml:"allow-insecure"` + NoFollowRedirects bool `json:"no-follow-redirects" yaml:"no-follow-redirects"` + Timeout int `json:"timeout,omitempty" yaml:"timeout,omitempty"` + RequestHeader []string `json:"request-headers,omitempty" yaml:"request-headers,omitempty"` + RequestBody string `json:"request-body,omitempty" yaml:"request-body,omitempty"` + RequestQueryParams map[string][]string `json:"request-query-params,omitempty" yaml:"request-query-params,omitempty"` + Headers matcher `json:"headers,omitempty" yaml:"headers,omitempty"` + Body matcher `json:"body,omitempty" yaml:"body,omitempty"` + Username string `json:"username,omitempty" yaml:"username,omitempty"` + Password string `json:"password,omitempty" yaml:"password,omitempty"` + CAFile string `json:"ca-file,omitempty" yaml:"ca-file,omitempty"` + CertFile string `json:"cert-file,omitempty" yaml:"cert-file,omitempty"` + KeyFile string `json:"key-file,omitempty" yaml:"key-file,omitempty"` + Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` + Proxy string `json:"proxy,omitempty" yaml:"proxy,omitempty"` } const ( diff --git a/system/http.go b/system/http.go index 07d1edda..5058a600 100644 --- a/system/http.go +++ b/system/http.go @@ -36,7 +36,7 @@ type DefHTTP struct { resp *http.Response RequestHeader http.Header RequestBody string - RequestQueryParams map[string]string + RequestQueryParams map[string][]string Timeout int loaded bool err error @@ -166,8 +166,10 @@ func (u *DefHTTP) setupReal() error { if u.RequestQueryParams != nil { qParams := req.URL.Query() - for k, v := range u.RequestQueryParams { - qParams.Add(k, v) + for k, params := range u.RequestQueryParams { + for _, param := range params { + qParams.Add(k, param) + } } req.URL.RawQuery = qParams.Encode() } diff --git a/util/config.go b/util/config.go index 6a1a2256..f9a701a6 100644 --- a/util/config.go +++ b/util/config.go @@ -42,7 +42,7 @@ type Config struct { RequestBody string Proxy string RequestHeader []string - RequestQueryParams map[string]string + RequestQueryParams map[string][]string RetryTimeout time.Duration RunLevel string Server string