diff --git a/.chloggen/splunkhecreceiver-align-error-message.yaml b/.chloggen/splunkhecreceiver-align-error-message.yaml new file mode 100644 index 000000000000..293c8345b820 --- /dev/null +++ b/.chloggen/splunkhecreceiver-align-error-message.yaml @@ -0,0 +1,11 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# 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: align error message with splunk enterprise to include No Data, Invalid Data Format, Event field is required, and Event field cannot be blank + +# One or more tracking issues related to the change +issues: [19219] diff --git a/receiver/splunkhecreceiver/receiver.go b/receiver/splunkhecreceiver/receiver.go index 75b0dd13bd63..b9752346da3a 100644 --- a/receiver/splunkhecreceiver/receiver.go +++ b/receiver/splunkhecreceiver/receiver.go @@ -45,12 +45,16 @@ const ( responseOK = "OK" 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" 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. gzipEncoding = "gzip" httpContentEncodingHeader = "Content-Encoding" @@ -64,13 +68,17 @@ var ( errInvalidEncoding = errors.New("invalid encoding") okRespBody = initJSONResponse(responseOK) - invalidMethodRespBody = initJSONResponse(responseInvalidMethod) + 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) ) // splunkReceiver implements the receiver.Metrics for Splunk HEC metric protocol. @@ -246,6 +254,7 @@ func (r *splunkReceiver) handleRawReq(resp http.ResponseWriter, req *http.Reques if req.ContentLength == 0 { r.obsrecv.EndLogsOp(ctx, typeStr, 0, nil) + r.failRequest(ctx, resp, http.StatusBadRequest, noDataRespBody, 0, nil) return } @@ -311,9 +320,7 @@ func (r *splunkReceiver) handleReq(resp http.ResponseWriter, req *http.Request) } if req.ContentLength == 0 { - if _, err := resp.Write(okRespBody); err != nil { - r.failRequest(ctx, resp, http.StatusInternalServerError, errInternalServerError, 0, err) - } + r.failRequest(ctx, resp, http.StatusBadRequest, noDataRespBody, 0, nil) return } @@ -325,7 +332,17 @@ func (r *splunkReceiver) handleReq(resp http.ResponseWriter, req *http.Request) var msg splunk.Event err := dec.Decode(&msg) if err != nil { - r.failRequest(ctx, resp, http.StatusBadRequest, errUnmarshalBodyRespBody, len(events), err) + r.failRequest(ctx, resp, http.StatusBadRequest, invalidFormatRespBody, len(events), err) + return + } + + if msg.Event == nil { + r.failRequest(ctx, resp, http.StatusBadRequest, eventRequiredRespBody, len(events), nil) + return + } + + if msg.Event == "" { + r.failRequest(ctx, resp, http.StatusBadRequest, eventBlankRespBody, len(events), nil) return } diff --git a/receiver/splunkhecreceiver/receiver_test.go b/receiver/splunkhecreceiver/receiver_test.go index 8469bd85f6f0..1c1db2ec2eea 100644 --- a/receiver/splunkhecreceiver/receiver_test.go +++ b/receiver/splunkhecreceiver/receiver_test.go @@ -190,7 +190,9 @@ func Test_splunkhecReceiver_handleReq(t *testing.T) { { name: "incorrect_content_type", req: func() *http.Request { - req := httptest.NewRequest("POST", "http://localhost/foo", nil) + msgBytes, err := json.Marshal(splunkMsg) + require.NoError(t, err) + req := httptest.NewRequest("POST", "http://localhost/foo", bytes.NewReader(msgBytes)) req.Header.Set("Content-Type", "application/not-json") return req }(), @@ -234,7 +236,7 @@ func Test_splunkhecReceiver_handleReq(t *testing.T) { }(), assertResponse: func(t *testing.T, status int, body string) { assert.Equal(t, http.StatusBadRequest, status) - assert.Equal(t, responseErrUnmarshalBody, body) + assert.Equal(t, responseInvalidDataFormat, body) }, }, { @@ -244,8 +246,51 @@ func Test_splunkhecReceiver_handleReq(t *testing.T) { return req }(), assertResponse: func(t *testing.T, status int, body string) { - assert.Equal(t, http.StatusOK, status) - assert.Equal(t, responseOK, body) + assert.Equal(t, http.StatusBadRequest, status) + assert.Equal(t, responseNoData, body) + }, + }, + { + name: "invalid_data_format", + req: func() *http.Request { + msgBytes, err := json.Marshal(`{"foo":"bar"}`) + require.NoError(t, err) + req := httptest.NewRequest("POST", "http://localhost/foo", bytes.NewReader(msgBytes)) + return req + }(), + assertResponse: func(t *testing.T, status int, body string) { + assert.Equal(t, http.StatusBadRequest, status) + assert.Equal(t, responseInvalidDataFormat, body) + }, + }, + { + name: "event_required_error", + req: func() *http.Request { + nilEventMsg := buildSplunkHecMsg(currentTime, 3) + nilEventMsg.Event = nil + msgBytes, err := json.Marshal(nilEventMsg) + require.NoError(t, err) + req := httptest.NewRequest("POST", "http://localhost/foo", bytes.NewReader(msgBytes)) + return req + }(), + assertResponse: func(t *testing.T, status int, body string) { + assert.Equal(t, http.StatusBadRequest, status) + assert.Equal(t, responseErrEventRequired, body) + }, + }, + { + name: "event_cannot_be_blank_error", + req: func() *http.Request { + blankEventMsg := buildSplunkHecMsg(currentTime, 3) + blankEventMsg.Event = "" + msgBytes, err := json.Marshal(blankEventMsg) + require.NoError(t, err) + req := httptest.NewRequest("POST", "http://localhost/foo", bytes.NewReader(msgBytes)) + return req + }(), + assertResponse: func(t *testing.T, status int, body string) { + assert.Equal(t, http.StatusBadRequest, status) + assert.Equal(t, responseErrEventBlank, body) }, }, { @@ -864,7 +909,7 @@ func Test_splunkhecReceiver_handleRawReq(t *testing.T) { { name: "incorrect_content_type", req: func() *http.Request { - req := httptest.NewRequest("POST", "http://localhost/foo", nil) + req := httptest.NewRequest("POST", "http://localhost/foo", strings.NewReader("foo\nbar")) req.Header.Set("Content-Type", "application/not-json") return req }(), @@ -891,7 +936,8 @@ func Test_splunkhecReceiver_handleRawReq(t *testing.T) { return req }(), assertResponse: func(t *testing.T, status int, body string) { - assert.Equal(t, http.StatusOK, status) + assert.Equal(t, http.StatusBadRequest, status) + assert.Equal(t, responseNoData, body) }, },