Clone header before creating exporter HTTP request

Behind http.Header is just a map and it's not safe for concurrent use.
Before this change, a panic might have occurred when doing asynchronous
HTTP exports (applies to both *http_post and *http_json_map exporters).
Cloning the header before adding it to the HTTP request has fixed this
issue.

Slightly improved the test that found this data race.
This commit is contained in:
ionutboangiu
2023-11-07 11:17:05 -05:00
committed by Dan Christian Bogos
parent c7e1f8f036
commit 29f58debc9
3 changed files with 27 additions and 6 deletions

View File

@@ -96,7 +96,7 @@ func (httpEE *HTTPjsonMapEE) GetMetrics() *utils.SafeMapStorage { return httpEE.
func (httpEE *HTTPjsonMapEE) PrepareMap(mp *utils.CGREvent) (any, error) {
body, err := json.Marshal(mp.Event)
return &HTTPPosterRequest{
Header: httpEE.hdr,
Header: httpEE.hdr.Clone(),
Body: body,
}, err
}
@@ -111,7 +111,7 @@ func (httpEE *HTTPjsonMapEE) PrepareOrderMap(mp *utils.OrderedNavigableMap) (any
}
body, err := json.Marshal(valMp)
return &HTTPPosterRequest{
Header: httpEE.hdr,
Header: httpEE.hdr.Clone(),
Body: body,
}, err
}

View File

@@ -49,6 +49,7 @@ type HTTPPostEE struct {
hdr http.Header
}
type HTTPPosterRequest struct {
Header http.Header
Body any
@@ -99,7 +100,7 @@ func (httpPost *HTTPPostEE) PrepareMap(mp *utils.CGREvent) (any, error) {
urlVals.Set(k, utils.IfaceAsString(v))
}
return &HTTPPosterRequest{
Header: httpPost.hdr,
Header: httpPost.hdr.Clone(),
Body: urlVals,
}, nil
}
@@ -113,7 +114,7 @@ func (httpPost *HTTPPostEE) PrepareOrderMap(mp *utils.OrderedNavigableMap) (any,
urlVals.Set(strings.Join(path, utils.NestingSep), nmIt.String())
}
return &HTTPPosterRequest{
Header: httpPost.hdr,
Header: httpPost.hdr.Clone(),
Body: urlVals,
}, nil
}

View File

@@ -130,7 +130,7 @@ func TestHttpPostSync(t *testing.T) {
t.Error(err)
}
vals, err := exp.PrepareMap(&utils.CGREvent{
req1, err := exp.PrepareMap(&utils.CGREvent{
Event: map[string]any{
"Account": "1001",
"Destination": "1002",
@@ -139,9 +139,29 @@ func TestHttpPostSync(t *testing.T) {
if err != nil {
t.Fatal(err)
}
req2, err := exp.PrepareMap(&utils.CGREvent{
Event: map[string]any{
"Account": "1001",
"Destination": "1003",
},
})
if err != nil {
t.Fatal(err)
}
req3, err := exp.PrepareMap(&utils.CGREvent{
Event: map[string]any{
"Account": "1003",
"Destination": "1001",
},
})
if err != nil {
t.Fatal(err)
}
requests := []any{req1, req2, req3}
for i := 0; i < 3; i++ {
go exp.ExportEvent(vals, "")
go exp.ExportEvent(requests[i], "")
}
select {