diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index a6dcf61ed591..3caf843b13df 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -117,6 +117,7 @@ https://github.com/elastic/beats/compare/v8.2.0\...main[Check the HEAD diff] - add documentation for decode_xml_wineventlog processor field mappings. {pull}32456[32456] - httpjson input: Add request tracing logger. {issue}32402[32402] {pull}32412[32412] - Add cloudflare R2 to provider list in AWS S3 input. {pull}32620[32620] +- Add support for single string containing multiple relation-types in getRFC5988Link. {pull}32811[32811] *Auditbeat* diff --git a/x-pack/filebeat/input/httpjson/value_tpl.go b/x-pack/filebeat/input/httpjson/value_tpl.go index 2f2d382480d8..5e739122b652 100644 --- a/x-pack/filebeat/input/httpjson/value_tpl.go +++ b/x-pack/filebeat/input/httpjson/value_tpl.go @@ -214,8 +214,8 @@ func parseTimestampNano(ns int64) time.Time { var regexpLinkRel = regexp.MustCompile(`<(.*)>;.*\srel\="?([^;"]*)`) -func getRFC5988Link(rel string, links []string) string { - for _, link := range links { +func getMatchLink(rel string, linksSplit []string) string { + for _, link := range linksSplit { if !regexpLinkRel.MatchString(link) { continue } @@ -231,10 +231,17 @@ func getRFC5988Link(rel string, links []string) string { return matches[1] } - return "" } +func getRFC5988Link(rel string, links []string) string { + if len(links) == 1 && strings.Count(links[0], "rel=") > 1 { + linksSplit := strings.Split(links[0], ",") + return getMatchLink(rel, linksSplit) + } + return getMatchLink(rel, links) +} + func toInt(v interface{}) int64 { vv := reflect.ValueOf(v) switch vv.Kind() { diff --git a/x-pack/filebeat/input/httpjson/value_tpl_test.go b/x-pack/filebeat/input/httpjson/value_tpl_test.go index 7dfbd3e829f0..5bdc92f939c7 100644 --- a/x-pack/filebeat/input/httpjson/value_tpl_test.go +++ b/x-pack/filebeat/input/httpjson/value_tpl_test.go @@ -189,7 +189,24 @@ func TestValueTpl(t *testing.T) { expectedVal: "2020-11-05 13:25:32 +0000 UTC", }, { - name: "func getRFC5988Link", + name: "func getRFC5988Link single rel matches", + value: `[[ getRFC5988Link "next" .last_response.header.Link ]]`, + paramCtx: &transformContext{ + firstEvent: &mapstr.M{}, + lastEvent: &mapstr.M{}, + lastResponse: newTestResponse( + nil, + http.Header{"Link": []string{ + `; title="Page 3"; rel="next"`, + }}, + "", + ), + }, + paramTr: transformable{}, + expectedVal: "https://example.com/api/v1/users?after=00ubfjQEMYBLRUWIEDKK", + }, + { + name: "func getRFC5988Link multiple rel as separate strings matches", value: `[[ getRFC5988Link "previous" .last_response.header.Link ]]`, paramCtx: &transformContext{ firstEvent: &mapstr.M{}, @@ -206,6 +223,60 @@ func TestValueTpl(t *testing.T) { paramTr: transformable{}, expectedVal: "https://example.com/api/v1/users?before=00ubfjQEMYBLRUWIEDKK", }, + { + name: "func getRFC5988Link multiple rel as separate strings in random order matches", + value: `[[ getRFC5988Link "previous" .last_response.header.Link ]]`, + paramCtx: &transformContext{ + firstEvent: &mapstr.M{}, + lastEvent: &mapstr.M{}, + lastResponse: newTestResponse( + nil, + http.Header{"Link": []string{ + `; title="Page 1"; rel="previous"`, + `; title="Page 3"; rel="next"`, + }}, + "", + ), + }, + paramTr: transformable{}, + expectedVal: "https://example.com/api/v1/users?before=00ubfjQEMYBLRUWIEDKK", + }, + { + name: "func getRFC5988Link multiple rel as single string matches", + value: `[[ getRFC5988Link "previous" .last_response.header.Link ]]`, + paramCtx: &transformContext{ + firstEvent: &mapstr.M{}, + lastEvent: &mapstr.M{}, + lastResponse: newTestResponse( + nil, + http.Header{"Link": []string{ + `; title="Page 1"; rel="previous", + ; title="Page 3"; rel="next"`, + }}, + "", + ), + }, + paramTr: transformable{}, + expectedVal: "https://example.com/api/v1/users?before=00ubfjQEMYBLRUWIEDKK", + }, + { + name: "func getRFC5988Link multiple rel as single string in random order matches", + value: `[[ getRFC5988Link "next" .last_response.header.Link ]]`, + paramCtx: &transformContext{ + firstEvent: &mapstr.M{}, + lastEvent: &mapstr.M{}, + lastResponse: newTestResponse( + nil, + http.Header{"Link": []string{ + `; title="Page 1"; rel="previous", + ; title="Page 3"; rel="next"`, + }}, + "", + ), + }, + paramTr: transformable{}, + expectedVal: "https://example.com/api/v1/users?after=00ubfjQEMYBLRUWIEDKK", + }, { name: "func getRFC5988Link does not match", value: `[[ getRFC5988Link "previous" .last_response.header.Link ]]`,