Skip to content

Commit

Permalink
[receiver/splunkhec] Fix the double encoding of JSON responses (#27606)
Browse files Browse the repository at this point in the history
**Description:**
We would double-encode JSON objects as strings. We typically have
encoded strings as JSON strings as static responses to issues
encountered during data processing. Along the way, we adopted JSON
objects to match the Splunk Enterprise API. However, we didn't stop
encoding as JSON strings, resulting in double encoded JSON strings.

**Link to tracking Issue:**
Fixes #27604 

**Testing:**
Unit test changes.
  • Loading branch information
atoulme authored Oct 27, 2023
1 parent c9b9e19 commit 7772e02
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 84 deletions.
27 changes: 27 additions & 0 deletions .chloggen/json_error_codes.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: bug_fix

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: splunkhecreceiver

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Do not encode JSON response objects as string.

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [27604]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: []
47 changes: 19 additions & 28 deletions receiver/splunkhecreceiver/receiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@ const (

responseOK = `{"text": "Success", "code": 0}`
responseHecHealthy = `{"text": "HEC is healthy", "code": 17}`
responseInvalidMethod = `Only "POST" method is supported`
responseInvalidEncoding = `"Content-Encoding" must be "gzip" or empty`
responseInvalidMethod = `"Only \"POST\" method is supported"`
responseInvalidEncoding = `"\"Content-Encoding\" must be \"gzip\" or empty"`
responseInvalidDataFormat = `{"text":"Invalid data format","code":6}`
responseErrEventRequired = `{"text":"Event field is required","code":12}`
responseErrEventBlank = `{"text":"Event field cannot be blank","code":13}`
responseErrGzipReader = "Error on gzip body"
responseErrUnmarshalBody = "Failed to unmarshal message body"
responseErrInternalServerError = "Internal Server Error"
responseErrUnsupportedMetricEvent = "Unsupported metric event"
responseErrUnsupportedLogEvent = "Unsupported log event"
responseErrGzipReader = `"Error on gzip body"`
responseErrUnmarshalBody = `"Failed to unmarshal message body"`
responseErrInternalServerError = `"Internal Server Error"`
responseErrUnsupportedMetricEvent = `"Unsupported metric event"`
responseErrUnsupportedLogEvent = `"Unsupported log event"`
responseErrHandlingIndexedFields = `{"text":"Error in handling indexed fields","code":15,"invalid-event-number":%d}`
responseNoData = `{"text":"No data","code":5}`
// Centralizing some HTTP and related string constants.
Expand All @@ -58,18 +58,18 @@ var (
errInvalidMethod = errors.New("invalid http method")
errInvalidEncoding = errors.New("invalid encoding")

okRespBody = initJSONResponse(responseOK)
eventRequiredRespBody = initJSONResponse(responseErrEventRequired)
eventBlankRespBody = initJSONResponse(responseErrEventBlank)
invalidEncodingRespBody = initJSONResponse(responseInvalidEncoding)
invalidFormatRespBody = initJSONResponse(responseInvalidDataFormat)
invalidMethodRespBody = initJSONResponse(responseInvalidMethod)
errGzipReaderRespBody = initJSONResponse(responseErrGzipReader)
errUnmarshalBodyRespBody = initJSONResponse(responseErrUnmarshalBody)
errInternalServerError = initJSONResponse(responseErrInternalServerError)
errUnsupportedMetricEvent = initJSONResponse(responseErrUnsupportedMetricEvent)
errUnsupportedLogEvent = initJSONResponse(responseErrUnsupportedLogEvent)
noDataRespBody = initJSONResponse(responseNoData)
okRespBody = []byte(responseOK)
eventRequiredRespBody = []byte(responseErrEventRequired)
eventBlankRespBody = []byte(responseErrEventBlank)
invalidEncodingRespBody = []byte(responseInvalidEncoding)
invalidFormatRespBody = []byte(responseInvalidDataFormat)
invalidMethodRespBody = []byte(responseInvalidMethod)
errGzipReaderRespBody = []byte(responseErrGzipReader)
errUnmarshalBodyRespBody = []byte(responseErrUnmarshalBody)
errInternalServerError = []byte(responseErrInternalServerError)
errUnsupportedMetricEvent = []byte(responseErrUnsupportedMetricEvent)
errUnsupportedLogEvent = []byte(responseErrUnsupportedLogEvent)
noDataRespBody = []byte(responseNoData)
)

// splunkReceiver implements the receiver.Metrics for Splunk HEC metric protocol.
Expand Down Expand Up @@ -464,15 +464,6 @@ func (r *splunkReceiver) handleHealthReq(writer http.ResponseWriter, _ *http.Req
_, _ = writer.Write([]byte(responseHecHealthy))
}

func initJSONResponse(s string) []byte {
respBody, err := jsoniter.Marshal(s)
if err != nil {
// This is to be used in initialization so panic here is fine.
panic(err)
}
return respBody
}

func isFlatJSONField(field interface{}) bool {
switch value := field.(type) {
case map[string]interface{}:
Expand Down
Loading

0 comments on commit 7772e02

Please sign in to comment.