Skip to content

Commit abe9f37

Browse files
authored
pkg/httpserver: allow rules to exclude parameters (#59)
1 parent e56c6e8 commit abe9f37

File tree

4 files changed

+39
-2
lines changed

4 files changed

+39
-2
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1212

1313
### Removed
1414

15+
## [0.11.0]
16+
17+
### Added
18+
19+
- Added ability to specify disallowed parameters: [#59](https://github.com/elastic/stream/pull/59)
20+
1521
## [0.10.0]
1622

1723
### Added

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ The rules will be defined in order, and will only match if all criteria is true
7171
- `path`: the path to match. It can use [gorilla/mux](https://pkg.go.dev/github.com/gorilla/mux#pkg-overview) parameters patterns.
7272
- `methods`: a list of methods to match with the rule.
7373
- `user` and `password`: username and password for basic auth matching.
74-
- `query_params`: Key-Value definitions of the query parameters to match. It can use [gorilla/mux](https://pkg.go.dev/github.com/gorilla/mux#Route.Queries) parameters patterns for the values. Web form params will also be added and compared against this for simplicity.
74+
- `query_params`: Key-Value definitions of the query parameters to match. It can use [gorilla/mux](https://pkg.go.dev/github.com/gorilla/mux#Route.Queries) parameters patterns for the values. Web form params will also be added and compared against this for simplicity. If a key is given an empty value, requests with this parameter will not satisfy the rule.
7575
- `request_headers`: Key-Value definitions of the headers to match. Any headers outside of this list will be ignored. The matches can be defined [as regular expressions](https://pkg.go.dev/github.com/gorilla/mux#Route.HeadersRegexp).
7676
- `request_body`: a string defining the expected body to match for the request. If the string is quoted with slashes, the leading and trailing slash are stripped and the resulting string is interpreted as a regular expression.
7777
- `responses`: a list of zero or more responses to return on matches. If more than one are set, they will be returned in rolling sequence.
@@ -127,4 +127,4 @@ The emulator does not require authentication.
127127

128128
- `gcs-bucket`: The name of the GCS bucket that should be created, should not already exist.
129129
- `gcs-object`: The name of the GCS object that will be populated with the collected data, using the configured GCS bucket.
130-
- `gcs-projectid`: The related projectID used when creating the bucket, this is required to be changed from the default value when not using an emulator.
130+
- `gcs-projectid`: The related projectID used when creating the bucket, this is required to be changed from the default value when not using an emulator.

pkg/httpserver/httpserver.go

+13
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,24 @@ func newHandlerFromConfig(config *config, logger *zap.SugaredLogger) (http.Handl
160160

161161
route.Methods(rule.Methods...)
162162

163+
exclude := make(map[string]bool)
163164
for key, vals := range rule.QueryParams {
165+
if len(vals) == 0 { // Cannot use nil since ucfg interprets null as an empty slice instead of nil.
166+
exclude[key] = true
167+
continue
168+
}
164169
for _, v := range vals {
165170
route.Queries(key, v)
166171
}
167172
}
173+
route.MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool {
174+
for key := range exclude {
175+
if r.URL.Query().Has(key) {
176+
return false
177+
}
178+
}
179+
return true
180+
})
168181

169182
for key, vals := range rule.RequestHeaders {
170183
for _, v := range vals {

pkg/httpserver/httpserver_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ func TestHTTPServer(t *testing.T) {
3030
password: passwd
3131
query_params:
3232
p1: ["v1"]
33+
p2: null
3334
request_headers:
3435
accept: ["application/json"]
3536
@@ -124,4 +125,21 @@ func TestHTTPServer(t *testing.T) {
124125
assert.JSONEq(t, "2", string(body))
125126
assert.Equal(t, "text/plain", resp.Header.Get("content-type"))
126127
})
128+
129+
t.Run("request has rejected parameter", func(t *testing.T) {
130+
req, err := http.NewRequest("GET", "http://"+addr+"/path1/test?p1=v1&p2=bad", nil)
131+
require.NoError(t, err)
132+
133+
resp, err := http.DefaultClient.Do(req)
134+
require.NoError(t, err)
135+
resp.Body.Close()
136+
137+
assert.Equal(t, 404, resp.StatusCode) // must fail because p2 is present
138+
139+
body, err := ioutil.ReadAll(resp.Body)
140+
require.NoError(t, err)
141+
resp.Body.Close()
142+
143+
assert.Equal(t, []byte{}, body)
144+
})
127145
}

0 commit comments

Comments
 (0)