Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Kind can be one of:
# - breaking-change: a change to previously-documented behavior
# - deprecation: functionality that is being removed in a later release
# - bug-fix: fixes a problem in a previous version
# - enhancement: extends functionality but does not break or fix existing behavior
# - feature: new functionality
# - known-issue: problems that we are aware of in a given version
# - security: impacts on the security of a product or a user’s deployment.
# - upgrade: important information for someone upgrading from a prior version
# - other: does not fit into any of the other categories
kind: bug-fix

# Change summary; a 80ish characters long description of the change.
summary: Fix checkin endpoint compression support

# Long description; in case the summary is not enough to describe the change
# this field accommodate a description without length limits.
# NOTE: This field will be rendered only for breaking-change and known-issue kinds at the moment.
description: |
Adds support for gzip compressed requests to the checkin endpoint.

# Affected component; usually one of "elastic-agent", "fleet-server", "filebeat", "metricbeat", "auditbeat", "all", etc.
component: fleet-server

# PR URL; optional; the PR number that added the changeset.
# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added.
# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number.
# Please provide it if you are adding a fragment for a different PR.
pr: https://github.com/elastic/fleet-server/pull/6491

# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of).
# If not present is automatically filled by the tooling with the issue linked to the PR number.
#issue: https://github.com/owner/repo/1234
15 changes: 14 additions & 1 deletion internal/pkg/api/handleCheckin.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,20 @@ func (ct *CheckinT) validateRequest(zlog zerolog.Logger, w http.ResponseWriter,
}
readCounter := datacounter.NewReaderCounter(body)

// Decompress the body when the client signals Content-Encoding: gzip.
var bodyReader io.Reader = readCounter
if r.Header.Get("Content-Encoding") == kEncodingGzip {
gr, err := gzip.NewReader(readCounter)
if err != nil {
return validatedCheckin{}, &BadRequestErr{msg: "unable to create gzip reader for request body", nextErr: err}
}
defer gr.Close()
bodyReader = gr
}

var val validatedCheckin
var req CheckinRequest
decoder := json.NewDecoder(readCounter)
decoder := json.NewDecoder(bodyReader)
if err := decoder.Decode(&req); err != nil {
return val, &BadRequestErr{msg: "unable to decode checkin request", nextErr: err}
}
Expand Down Expand Up @@ -666,6 +677,8 @@ func (ct *CheckinT) writeResponse(zlog zerolog.Logger, w http.ResponseWriter, r
return err
}

// acceptsEncoding reports whether the request includes the passed encoding.
// Only an exact match is checked as that is all the agent will send.
func acceptsEncoding(r *http.Request, encoding string) bool {
for _, v := range r.Header.Values("Accept-Encoding") {
if v == encoding {
Expand Down
40 changes: 40 additions & 0 deletions internal/pkg/api/handleCheckin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
package api

import (
"bytes"
"compress/flate"
"compress/gzip"
"context"
"encoding/json"
"errors"
Expand Down Expand Up @@ -1168,6 +1170,44 @@ func TestValidateCheckinRequest(t *testing.T) {
rawMeta: []byte(`{"elastic": {"agent": {"id": "testid", "fips": true}}}`),
},
},
{
name: "gzip-compressed request body is decompressed before JSON decoding",
req: func() *http.Request {
var buf bytes.Buffer
gz := gzip.NewWriter(&buf)
_, _ = gz.Write([]byte(`{"status": "online", "message": "test message"}`))
_ = gz.Close()
return &http.Request{
Header: http.Header{"Content-Encoding": []string{"gzip"}},
Body: io.NopCloser(&buf),
}
}(),
cfg: &config.Server{
Limits: config.ServerLimits{
CheckinLimit: config.Limit{
MaxBody: 0,
},
},
},
expErr: nil,
expValid: validatedCheckin{},
},
{
name: "invalid gzip request body returns bad request error",
req: &http.Request{
Header: http.Header{"Content-Encoding": []string{"gzip"}},
Body: io.NopCloser(strings.NewReader(`not gzip data`)),
},
cfg: &config.Server{
Limits: config.ServerLimits{
CheckinLimit: config.Limit{
MaxBody: 0,
},
},
},
expErr: &BadRequestErr{msg: "unable to create gzip reader for request body", nextErr: gzip.ErrHeader},
expValid: validatedCheckin{},
},
}

for _, tc := range tests {
Expand Down
35 changes: 32 additions & 3 deletions internal/pkg/api/openapi.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion internal/pkg/server/fleet_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const (
serverVersion = "8.0.0"
localhost = "localhost"

testWaitServerUp = 3 * time.Second
testWaitServerUp = 10 * time.Second

enrollBody = `{
"type": "PERMANENT",
Expand Down Expand Up @@ -244,6 +244,7 @@ func startTestServer(t *testing.T, ctx context.Context, policyD model.PolicyData
}

func (s *tserver) waitServerUp(ctx context.Context, dur time.Duration) error {
zlog := zerolog.Ctx(ctx)
ctx, cancel := context.WithTimeout(ctx, dur)
defer cancel()

Expand All @@ -270,6 +271,7 @@ func (s *tserver) waitServerUp(ctx context.Context, dur time.Duration) error {
return false, err
}

zlog.Info().Msgf("test wait for fleet-server up status: %s", status.Status)
return status.Status == "HEALTHY", nil
}

Expand Down
26 changes: 20 additions & 6 deletions model/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1489,11 +1489,18 @@ paths:
- name: Accept-Encoding
in: header
description: |
If the agent is able to accept encoded responses.
Used to indicate if GZIP compression may be used by the server.
The elastic-agent does not use the accept-encoding header.
Indicates encodings the agent can accept. The only encoding supported by the server currently is gzip.
Comma-separated directive lists and RFC 7231 quality values (e.g. "gzip;q=1.0, deflate;q=0.5") are both accepted.
schema:
type: string
- name: Content-Encoding
in: header
description: |
Signals that the request body has been compressed. Server currently only supports gzip compression.
schema:
type: string
enum:
- gzip
- $ref: "#/components/parameters/userAgent"
- $ref: "#/components/parameters/requestId"
- $ref: "#/components/parameters/apiVersion"
Expand Down Expand Up @@ -1529,12 +1536,19 @@ paths:
description: Agent checkin successful. May include actions.
headers:
Content-Encoding:
description: Responses may be compressed if the accept encoding indicates it. Currently not used by the agent.
description: |
Present when fleet-server has gzip-compressed the response body.
Compression is applied when all three conditions are met: the serialised
response exceeds the configured compression threshold, the server compression
level is not NoCompression, and the request's Accept-Encoding header
advertises gzip support.
schema:
type: string
enum:
- gzip
examples:
gzip:
description: Response is gzip encoded as the request headers allowed it.
description: Response body is gzip-compressed.
value: gzip
Elastic-Api-Version:
$ref: "#/components/headers/apiVersion"
Expand Down Expand Up @@ -2020,4 +2034,4 @@ paths:
"500":
$ref: "#/components/responses/internalServerError"
"503":
$ref: "#/components/responses/unavailable"
$ref: "#/components/responses/unavailable"
29 changes: 20 additions & 9 deletions pkg/api/client.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 13 additions & 3 deletions pkg/api/types.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.