Skip to content

Conversation

@Lusitaniae
Copy link

@Lusitaniae Lusitaniae commented Dec 29, 2025

It allows the prometheus exporter to serve compressed responses when the appropriate Accept Encoding header is set by the client.

Closes #11320


Testing

fluent-bit.conf

[SERVICE]
    Flush        1
    Daemon       Off
    Log_Level    info
    Parsers_File parsers.conf

    HTTP_Server  On
    HTTP_Listen  0.0.0.0
    HTTP_Port    2020

[INPUT]
    Name        dummy
    Dummy       {"message":"dummy", "kubernetes":{"namespace_name": "default", "docker_id": "abc123", "pod_name": "pod1", "container_name": "mycontainer", "pod_id": "def456", "labels":{"app": "app1"}}, "duration": 20, "color": "red", "shape": "circle"}
    Tag         dummy.log

[INPUT]
    Name        dummy
    Dummy       {"message":"hello", "kubernetes":{"namespace_name": "default", "docker_id": "abc123", "pod_name": "pod1", "container_name": "mycontainer", "pod_id": "def456", "labels":{"app": "app1"}}, "duration": 60, "color": "blue", "shape": "square"}
    Tag         dummy.log2

[FILTER]
    name               log_to_metrics
    match              dummy.log*
    tag                test_metric
    metric_mode        counter
    metric_name        count_all_dummy_messages
    metric_description This metric counts dummy messages

[OUTPUT]
    name    prometheus_exporter
    match   *
    host    0.0.0.0
    port    9999

test requests

i@i:~/workspace/triton/fluent-bit/build$ curl localhost:9999/metrics -H "Accept-Encoding: gzip" -v --compressed
* Host localhost:9999 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:9999...
* connect to ::1 port 9999 from ::1 port 49998 failed: Connection refused
*   Trying 127.0.0.1:9999...
* Connected to localhost (127.0.0.1) port 9999
> GET /metrics HTTP/1.1
> Host: localhost:9999
> User-Agent: curl/8.5.0
> Accept: */*
> Accept-Encoding: gzip
> 
< HTTP/1.1 200 OK
< Server: Monkey/1.8.6
< Date: Mon, 29 Dec 2025 10:42:38 GMT
< Transfer-Encoding: chunked
< Content-Type: text/plain; version=0.0.4
< Content-Encoding: gzip
< 
# HELP log_metric_counter_count_all_dummy_messages This metric counts dummy messages
# TYPE log_metric_counter_count_all_dummy_messages counter
log_metric_counter_count_all_dummy_messages 21
# HELP log_metric_counter_count_all_dummy_messages This metric counts dummy messages
# TYPE log_metric_counter_count_all_dummy_messages counter
log_metric_counter_count_all_dummy_messages 22
* Connection #0 to host localhost left intact
i@i:~/workspace/triton/fluent-bit/build$ curl localhost:9999/metrics -v
* Host localhost:9999 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:9999...
* connect to ::1 port 9999 from ::1 port 50006 failed: Connection refused
*   Trying 127.0.0.1:9999...
* Connected to localhost (127.0.0.1) port 9999
> GET /metrics HTTP/1.1
> Host: localhost:9999
> User-Agent: curl/8.5.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Server: Monkey/1.8.6
< Date: Mon, 29 Dec 2025 10:42:41 GMT
< Transfer-Encoding: chunked
< Content-Type: text/plain; version=0.0.4
< 
# HELP log_metric_counter_count_all_dummy_messages This metric counts dummy messages
# TYPE log_metric_counter_count_all_dummy_messages counter
log_metric_counter_count_all_dummy_messages 27
# HELP log_metric_counter_count_all_dummy_messages This metric counts dummy messages
# TYPE log_metric_counter_count_all_dummy_messages counter
log_metric_counter_count_all_dummy_messages 28
* Connection #0 to host localhost left intact

valgrind

valgrind --leak-check=full bin/fluent-bit --config fluent-bit.conf 
==378238== Memcheck, a memory error detector
==378238== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==378238== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==378238== Command: bin/fluent-bit --config fluent-bit.conf
==378238== 
Fluent Bit v4.2.3
* Copyright (C) 2015-2025 The Fluent Bit Authors
* Fluent Bit is a CNCF graduated project under the Fluent organization
* https://fluentbit.io

______ _                  _    ______ _ _             ___   _____ 
|  ___| |                | |   | ___ (_) |           /   | / __  \
| |_  | |_   _  ___ _ __ | |_  | |_/ /_| |_  __   __/ /| | `' / /'
|  _| | | | | |/ _ \ '_ \| __| | ___ \ | __| \ \ / / /_| |   / /  
| |   | | |_| |  __/ | | | |_  | |_/ / | |_   \ V /\___  |_./ /___
\_|   |_|\__,_|\___|_| |_|\__| \____/|_|\__|   \_/     |_(_)_____/
                                                                  
             Fluent Bit v4.2 – Direct Routes Ahead
         Celebrating 10 Years of Open, Fluent Innovation!

[2025/12/29 17:42:26.859033176] [error] [/home/i/workspace/triton/fluent-bit/src/config_format/flb_cf_fluentbit.c:289 errno=2] No such file or directory
[2025/12/29 17:42:26.873744505] [error] file=/home/i/workspace/triton/fluent-bit/build/parsers.conf
[2025/12/29 17:42:26.942204959] [ info] [fluent bit] version=4.2.3, commit=7b0c1aebb6, pid=378238
[2025/12/29 17:42:26.954875222] [ info] [storage] ver=1.5.4, type=memory, sync=normal, checksum=off, max_chunks_up=128
[2025/12/29 17:42:26.955273358] [ info] [simd    ] disabled
[2025/12/29 17:42:26.955536705] [ info] [cmetrics] version=1.0.6
[2025/12/29 17:42:26.955774331] [ info] [ctraces ] version=0.6.6
[2025/12/29 17:42:26.969658549] [ info] [input:dummy:dummy.0] initializing
[2025/12/29 17:42:26.970305614] [ info] [input:dummy:dummy.0] storage_strategy='memory' (memory only)
[2025/12/29 17:42:26.990696434] [ info] [input:dummy:dummy.1] initializing
[2025/12/29 17:42:26.990853866] [ info] [input:dummy:dummy.1] storage_strategy='memory' (memory only)
[2025/12/29 17:42:27.4379214] [ info] [input:emitter:emitter_for_log_to_metrics.0] initializing
[2025/12/29 17:42:27.4518352] [ info] [input:emitter:emitter_for_log_to_metrics.0] storage_strategy='memory' (memory only)
[2025/12/29 17:42:27.95259058] [ info] [output:prometheus_exporter:prometheus_exporter.0] listening iface=0.0.0.0 tcp_port=9999
[2025/12/29 17:42:27.158827405] [ info] [http_server] listen iface=0.0.0.0 tcp_port=2020
[2025/12/29 17:42:27.160001703] [ info] [sp] stream processor started
[2025/12/29 17:42:27.162278621] [ info] [engine] Shutdown Grace Period=5, Shutdown Input Grace Period=2
==378238== Warning: client switching stacks?  SP change: 0x63ac198 --> 0x59ebdb0
==378238==          to suppress, use: --max-stackframe=10224616 or greater
==378238== Warning: client switching stacks?  SP change: 0x59ebc98 --> 0x63ac198
==378238==          to suppress, use: --max-stackframe=10224896 or greater
==378238== Warning: client switching stacks?  SP change: 0x63ac4f8 --> 0x59ebc98
==378238==          to suppress, use: --max-stackframe=10225760 or greater
==378238==          further instances of this message will not be shown.
^C[2025/12/29 17:42:55] [engine] caught signal (SIGINT)
[2025/12/29 17:42:55.552831886] [ warn] [engine] service will shutdown in max 5 seconds
[2025/12/29 17:42:55.554362770] [ info] [engine] pausing all inputs..
[2025/12/29 17:42:55.556146059] [ info] [input] pausing dummy.0
[2025/12/29 17:42:55.559915898] [ info] [input] pausing dummy.1
[2025/12/29 17:42:55.560153776] [ info] [input] pausing emitter_for_log_to_metrics.0
[2025/12/29 17:42:56.448458227] [ info] [engine] service has stopped (0 pending tasks)
[2025/12/29 17:42:56.448966294] [ info] [input] pausing dummy.0
[2025/12/29 17:42:56.449161069] [ info] [input] pausing dummy.1
[2025/12/29 17:42:56.449256903] [ info] [input] pausing emitter_for_log_to_metrics.0
==378238== 
==378238== HEAP SUMMARY:
==378238==     in use at exit: 0 bytes in 0 blocks
==378238==   total heap usage: 77,249 allocs, 77,249 frees, 31,956,228 bytes allocated
==378238== 
==378238== All heap blocks were freed -- no leaks are possible
==378238== 
==378238== For lists of detected and suppressed errors, rerun with: -s
==378238== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Fluent Bit is licensed under Apache 2.0, by submitting this pull request I understand that this code will be released under the terms of that license.

Summary by CodeRabbit

  • New Features

    • Metrics endpoint now negotiates gzip per request and sends compressed responses when clients request gzip, reducing bandwidth.
  • Bug Fixes / Reliability

    • Returns 404 when metrics are unavailable.
    • Gracefully falls back to uncompressed responses if compression fails, ensuring consistent delivery.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 29, 2025

📝 Walkthrough

Walkthrough

Per-request gzip negotiation was added to the Prometheus exporter: the handler checks Accept-Encoding for gzip, attempts to compress metrics with flb_gzip_compress(), sets Content-Encoding: gzip on success, falls back to uncompressed output on failure, and returns 404 if no metrics buffer exists.

Changes

Cohort / File(s) Summary
Prometheus metrics endpoint (gzip support)
plugins/out_prometheus_exporter/prom_http.c
Added client_accepts_gzip() to parse Accept-Encoding (token-delimited, case-insensitive). cb_metrics() now returns 404 when metrics are absent, negotiates gzip per-request, calls flb_gzip_compress() when appropriate, sets Content-Encoding: gzip on successful compression, falls back on failure, and manages compressed buffer allocation/free. Added includes: flb_gzip.h, monkey/mk_http_parser.h, string.h.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Server as cb_metrics()
    participant Parser as client_accepts_gzip()
    participant Compressor as flb_gzip_compress()
    participant Response

    Client->>Server: GET /metrics (Accept-Encoding: ...)
    Server->>Server: Check metrics buffer exists
    alt no metrics
        Server->>Response: Respond 404
        Response-->>Client: 404
    else metrics present
        Server->>Parser: Inspect Accept-Encoding header
        Parser-->>Server: gzip accepted? (true/false)
        alt gzip accepted
            Server->>Compressor: Compress metrics payload
            alt compression success
                Compressor-->>Server: compressed payload
                Note right of Server `#D5F5E3`: Set Content-Encoding: gzip
                Server->>Response: Send compressed payload
            else compression fails
                Compressor-->>Server: error
                Server->>Response: Send uncompressed payload
            end
        else gzip not accepted
            Server->>Response: Send uncompressed payload
        end
        Response-->>Client: 200 + headers + body
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I sniff the headers, find gzip in the breeze,
I press the bytes until they fold with ease,
If packing trips, I hop back to plain,
Tiny parcels sent across the lane,
A happy hop — metrics snug and neat.

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding gzip compression support to the Prometheus exporter, which is the central focus of the changeset.
Linked Issues check ✅ Passed The PR implements all coding requirements from issue #11320: detects Accept-Encoding header, conditionally compresses metrics with gzip, and sets appropriate Content-Encoding header.
Out of Scope Changes check ✅ Passed All changes are within scope: gzip compression handling in prom_http.c directly addresses the linked issue's requirement to support compressed responses.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
plugins/out_prometheus_exporter/prom_http.c (2)

22-24: Consider removing unused string.h include.

The flb_gzip.h and mk_http_parser.h includes are necessary for the new functionality. However, string.h doesn't appear to be used by the new code—no new calls to strcpy, strcmp, strlen, or similar functions are introduced.

🔎 Proposed fix
 #include <fluent-bit/flb_gzip.h>
 #include <monkey/mk_http_parser.h>
-#include <string.h>

163-220: Token parsing looks correct; consider handling q=0 quality value (optional).

The token boundary detection logic is sound. However, per RFC 7231, a quality value of q=0 explicitly means the encoding is not acceptable (e.g., gzip;q=0, deflate). The current implementation would still return 1 for such cases.

This is a minor RFC compliance gap that's unlikely to affect real Prometheus scrapers, but worth noting.

If you want full compliance, you could extend the check to parse the quality value after detecting "gzip":

/* After finding "gzip" token, check for q=0 */
const char *semi = p + 4;
while (semi < end && (*semi == ' ' || *semi == '\t')) semi++;
if (semi < end && *semi == ';') {
    /* Parse quality value - skip if q=0 */
    // ... (parse "q=" and value)
}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b0c1ae and b68c4ee.

📒 Files selected for processing (1)
  • plugins/out_prometheus_exporter/prom_http.c
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-29T06:25:27.250Z
Learnt from: shadowshot-x
Repo: fluent/fluent-bit PR: 10794
File: tests/internal/aws_compress.c:93-107
Timestamp: 2025-08-29T06:25:27.250Z
Learning: In Fluent Bit, ZSTD compression is enabled by default and is treated as a core dependency, not requiring conditional compilation guards like `#ifdef FLB_HAVE_ZSTD`. Unlike some other optional components such as ARROW/PARQUET (which use `#ifdef FLB_HAVE_ARROW` guards), ZSTD support is always available and doesn't need build-time conditionals. ZSTD headers are included directly without guards across multiple plugins and core components.

Applied to files:

  • plugins/out_prometheus_exporter/prom_http.c
📚 Learning: 2025-08-29T06:25:02.561Z
Learnt from: shadowshot-x
Repo: fluent/fluent-bit PR: 10794
File: tests/internal/aws_compress.c:7-7
Timestamp: 2025-08-29T06:25:02.561Z
Learning: In Fluent Bit, ZSTD (zstandard) compression library is bundled directly in the source tree at `lib/zstd-1.5.7` and is built unconditionally as a static library. Unlike optional external dependencies, ZSTD does not use conditional compilation guards like `FLB_HAVE_ZSTD` and is always available. Headers like `<fluent-bit/flb_zstd.h>` can be included directly without guards.

Applied to files:

  • plugins/out_prometheus_exporter/prom_http.c
🧬 Code graph analysis (1)
plugins/out_prometheus_exporter/prom_http.c (3)
lib/monkey/mk_server/mk_http.c (1)
  • mk_http_header_get (1499-1528)
src/flb_gzip.c (1)
  • flb_gzip_compress (157-252)
include/fluent-bit/flb_mem.h (1)
  • flb_free (126-128)
🔇 Additional comments (1)
plugins/out_prometheus_exporter/prom_http.c (1)

241-269: Compression logic and memory handling look correct.

The fallback to uncompressed on compression failure is a good resilience pattern. Memory management is correct: flb_gzip_compress allocates internally and the caller frees with flb_free().

Note: The check on lines 251-254 is defensive—flb_gzip_compress doesn't set *out_data on failure paths, so compressed_data should remain NULL when ret != 0. The defensive check is fine to keep.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
plugins/out_prometheus_exporter/prom_http.c (1)

163-220: Consider simplifying token parsing with standard library functions.

The manual case-insensitive search and boundary validation work correctly but could be more maintainable. Consider using strncasecmp() or similar standard functions to simplify the logic, though the current implementation handles edge cases properly (e.g., preventing matches within words like "notgzip").

Alternative approach using standard functions
/* Check if client accepts gzip encoding */
static int client_accepts_gzip(mk_request_t *request)
{
    struct mk_http_header *header;
    char *p, *token;
    char *saveptr = NULL;
    char buf[256];
    
    /* Get Accept-Encoding header */
    header = mk_http_header_get(MK_HEADER_ACCEPT_ENCODING, request, NULL, 0);
    if (!header || header->val.len == 0 || header->val.len >= sizeof(buf)) {
        return 0;
    }
    
    /* Copy header value for tokenization */
    memcpy(buf, header->val.data, header->val.len);
    buf[header->val.len] = '\0';
    
    /* Parse comma-separated encoding list */
    token = strtok_r(buf, ",", &saveptr);
    while (token) {
        /* Skip leading whitespace */
        while (*token == ' ' || *token == '\t') token++;
        
        /* Check for gzip (case-insensitive) */
        if (strncasecmp(token, "gzip", 4) == 0) {
            char c = token[4];
            if (c == '\0' || c == ' ' || c == '\t' || c == ';') {
                return 1;
            }
        }
        token = strtok_r(NULL, ",", &saveptr);
    }
    
    return 0;
}

Note: This assumes a reasonable header length limit. Adjust if needed.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b68c4ee and 833ab79.

📒 Files selected for processing (1)
  • plugins/out_prometheus_exporter/prom_http.c
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: shadowshot-x
Repo: fluent/fluent-bit PR: 10794
File: tests/internal/aws_compress.c:93-107
Timestamp: 2025-08-29T06:25:27.250Z
Learning: In Fluent Bit, ZSTD compression is enabled by default and is treated as a core dependency, not requiring conditional compilation guards like `#ifdef FLB_HAVE_ZSTD`. Unlike some other optional components such as ARROW/PARQUET (which use `#ifdef FLB_HAVE_ARROW` guards), ZSTD support is always available and doesn't need build-time conditionals. ZSTD headers are included directly without guards across multiple plugins and core components.
📚 Learning: 2025-08-29T06:25:27.250Z
Learnt from: shadowshot-x
Repo: fluent/fluent-bit PR: 10794
File: tests/internal/aws_compress.c:93-107
Timestamp: 2025-08-29T06:25:27.250Z
Learning: In Fluent Bit, ZSTD compression is enabled by default and is treated as a core dependency, not requiring conditional compilation guards like `#ifdef FLB_HAVE_ZSTD`. Unlike some other optional components such as ARROW/PARQUET (which use `#ifdef FLB_HAVE_ARROW` guards), ZSTD support is always available and doesn't need build-time conditionals. ZSTD headers are included directly without guards across multiple plugins and core components.

Applied to files:

  • plugins/out_prometheus_exporter/prom_http.c
📚 Learning: 2025-08-29T06:25:02.561Z
Learnt from: shadowshot-x
Repo: fluent/fluent-bit PR: 10794
File: tests/internal/aws_compress.c:7-7
Timestamp: 2025-08-29T06:25:02.561Z
Learning: In Fluent Bit, ZSTD (zstandard) compression library is bundled directly in the source tree at `lib/zstd-1.5.7` and is built unconditionally as a static library. Unlike optional external dependencies, ZSTD does not use conditional compilation guards like `FLB_HAVE_ZSTD` and is always available. Headers like `<fluent-bit/flb_zstd.h>` can be included directly without guards.

Applied to files:

  • plugins/out_prometheus_exporter/prom_http.c
🔇 Additional comments (3)
plugins/out_prometheus_exporter/prom_http.c (3)

226-256: LGTM - Solid compression implementation with proper fallback.

The compression logic is well-structured:

  • Proper initialization of compressed_data to NULL enables safe cleanup
  • Client negotiation via client_accepts_gzip() respects HTTP standards
  • Graceful fallback to uncompressed response on compression failure
  • Correct memory management in error paths

The per-request compression is appropriate given this is an opt-in feature controlled by the client.


22-22: No changes needed to the gzip includes.

GZIP compression is unconditionally available in Fluent Bit and included without guards throughout the codebase (e.g., src/flb_http_common.c, src/http_server/flb_http_server.c, tests/runtime/in_forward.c). The direct include of flb_gzip.h is correct and consistent with existing patterns.

Likely an incorrect or invalid review comment.


258-273: No changes needed. The code at lines 258–273 is correct.

Lines 260 and 269 do not have trailing whitespace—these are clean blank lines. Additionally, the buf->users reference counting is thread-safe by design: buffers are stored in thread-local storage (via pthread_getspecific), so each Monkey HTTP worker thread maintains its own separate metrics list. The counter is only modified and read within the same thread context, eliminating race conditions.

Likely an incorrect or invalid review comment.

@edsiper
Copy link
Member

edsiper commented Jan 5, 2026

@codex review

@edsiper edsiper added this to the Fluent Bit v4.2.3 milestone Jan 5, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e71090557d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +210 to +213
}
}
/* Found valid "gzip" token */
return 1;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Respect q values in Accept-Encoding before enabling gzip

In client_accepts_gzip the code returns true as soon as it finds a gzip token in the Accept-Encoding header, but it never inspects the q quality parameter. A client that explicitly disables gzip (e.g. Accept-Encoding: gzip;q=0) will still receive a compressed metrics response and be unable to read it. Consider parsing the quality value and skipping gzip when it is zero.

Useful? React with 👍 / 👎.

@Lusitaniae
Copy link
Author

force pushed to add filter_log_to_metrics: prefix to commits

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
plugins/out_prometheus_exporter/prom_http.c (1)

257-268: LGTM! Conditional Content-Encoding header and memory cleanup.

The response handling correctly:

  • Sets Content-Encoding: gzip only when compressed data is sent.
  • Sends either compressed or uncompressed payload as appropriate.
  • Frees compressed_data immediately after sending (line 264), preventing leaks.

Minor: trailing whitespace on lines 259 and 268 (after the semicolon and closing brace, respectively). This is a formatting nit and may be caught by linters.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e710905 and 4eae4dc.

📒 Files selected for processing (1)
  • plugins/out_prometheus_exporter/prom_http.c
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: shadowshot-x
Repo: fluent/fluent-bit PR: 10794
File: tests/internal/aws_compress.c:93-107
Timestamp: 2025-08-29T06:25:27.250Z
Learning: In Fluent Bit, ZSTD compression is enabled by default and is treated as a core dependency, not requiring conditional compilation guards like `#ifdef FLB_HAVE_ZSTD`. Unlike some other optional components, ZSTD support is always available and doesn't need build-time conditionals.
Learnt from: shadowshot-x
Repo: fluent/fluent-bit PR: 10794
File: tests/internal/aws_compress.c:93-107
Timestamp: 2025-08-29T06:25:27.250Z
Learning: In Fluent Bit, ZSTD compression is enabled by default and is treated as a core dependency, not requiring conditional compilation guards like `#ifdef FLB_HAVE_ZSTD`. Unlike some other optional components such as ARROW/PARQUET (which use `#ifdef FLB_HAVE_ARROW` guards), ZSTD support is always available and doesn't need build-time conditionals. ZSTD headers are included directly without guards across multiple plugins and core components.
📚 Learning: 2025-08-29T06:25:02.561Z
Learnt from: shadowshot-x
Repo: fluent/fluent-bit PR: 10794
File: tests/internal/aws_compress.c:7-7
Timestamp: 2025-08-29T06:25:02.561Z
Learning: In Fluent Bit, ZSTD (zstandard) compression library is bundled directly in the source tree at `lib/zstd-1.5.7` and is built unconditionally as a static library. Unlike optional external dependencies, ZSTD does not use conditional compilation guards like `FLB_HAVE_ZSTD` and is always available. Headers like `<fluent-bit/flb_zstd.h>` can be included directly without guards.

Applied to files:

  • plugins/out_prometheus_exporter/prom_http.c
📚 Learning: 2025-08-29T06:25:27.250Z
Learnt from: shadowshot-x
Repo: fluent/fluent-bit PR: 10794
File: tests/internal/aws_compress.c:93-107
Timestamp: 2025-08-29T06:25:27.250Z
Learning: In Fluent Bit, ZSTD compression is enabled by default and is treated as a core dependency, not requiring conditional compilation guards like `#ifdef FLB_HAVE_ZSTD`. Unlike some other optional components such as ARROW/PARQUET (which use `#ifdef FLB_HAVE_ARROW` guards), ZSTD support is always available and doesn't need build-time conditionals. ZSTD headers are included directly without guards across multiple plugins and core components.

Applied to files:

  • plugins/out_prometheus_exporter/prom_http.c
🧬 Code graph analysis (1)
plugins/out_prometheus_exporter/prom_http.c (2)
lib/monkey/mk_server/mk_http.c (1)
  • mk_http_header_get (1499-1528)
src/flb_gzip.c (1)
  • flb_gzip_compress (157-252)
🔇 Additional comments (3)
plugins/out_prometheus_exporter/prom_http.c (3)

22-23: LGTM! Necessary includes for gzip compression.

The includes are appropriate for the new functionality: flb_gzip.h provides the compression API and mk_http_parser.h provides the HTTP header access.


231-236: Good: Return 404 when no metrics available.

Returning a 404 status when the buffer is unavailable is the correct behavior for this endpoint.


240-255: LGTM! Compression with proper fallback.

The compression flow is well-designed:

  • Checks client capability before attempting compression.
  • Calls flb_gzip_compress() which allocates the output buffer.
  • Falls back gracefully to uncompressed output if compression fails.
  • Frees allocated memory if compression fails partway.

Memory management is correct based on the flb_gzip_compress contract (caller owns the allocated buffer).

Comment on lines +162 to +219
/* Check if client accepts gzip encoding */
static int client_accepts_gzip(mk_request_t *request)
{
struct mk_http_header *header;
const char *accept_encoding;
size_t len;
const char *p;
const char *end;

/* Get Accept-Encoding header */
header = mk_http_header_get(MK_HEADER_ACCEPT_ENCODING, request, NULL, 0);
if (!header || header->val.len == 0) {
return 0;
}

accept_encoding = header->val.data;
len = header->val.len;

/* Check if "gzip" is present in Accept-Encoding header (case-insensitive) */
if (len < 4) {
return 0;
}

p = accept_encoding;
end = accept_encoding + len;

/* Search for "gzip" token */
while (p < end - 3) {
/* Case-insensitive comparison for "gzip" */
if ((p[0] == 'g' || p[0] == 'G') &&
(p[1] == 'z' || p[1] == 'Z') &&
(p[2] == 'i' || p[2] == 'I') &&
(p[3] == 'p' || p[3] == 'P')) {
/* Check if it's a complete token (not part of another word) */
/* Check character before "gzip" */
if (p > accept_encoding) {
char prev = p[-1];
if (prev != ',' && prev != ' ' && prev != '\t') {
p++;
continue;
}
}
/* Check character after "gzip" */
if (p + 4 < end) {
char next = p[4];
if (next != ',' && next != ' ' && next != '\t' && next != ';') {
p++;
continue;
}
}
/* Found valid "gzip" token */
return 1;
}
p++;
}

return 0;
}
Copy link

@coderabbitai coderabbitai bot Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Quality values (q=0) still not respected in Accept-Encoding.

The function correctly identifies the presence of "gzip" with token-boundary checks, but it does not parse quality values. A client that sends Accept-Encoding: gzip;q=0 (explicitly disabling gzip) will still receive compressed content. Per RFC 7231, q=0 means "not acceptable."

Consider parsing and respecting the quality parameter or at minimum checking for q=0 after detecting the gzip token.

🔎 Example logic to reject gzip when q=0

After finding the gzip token (line 213), scan forward for a semicolon and parse the q-value:

             }
             /* Found valid "gzip" token */
+            /* Check for q=0 (explicitly disabled) */
+            const char *q_check = p + 4;
+            while (q_check < end && (*q_check == ' ' || *q_check == '\t')) {
+                q_check++;
+            }
+            if (q_check < end && *q_check == ';') {
+                q_check++;
+                while (q_check < end - 2 && (*q_check == ' ' || *q_check == '\t')) {
+                    q_check++;
+                }
+                if (q_check < end - 2 && q_check[0] == 'q' && q_check[1] == '=' && q_check[2] == '0') {
+                    return 0;  /* q=0 means not acceptable */
+                }
+            }
             return 1;
         }

Note: A full RFC 7231-compliant parser would be more robust, but this addresses the most critical case.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much do we care about this?

I'm guessing there's very limited number of prom metric clients, which wouldn't require handling edge cases

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Lusitaniae, that's a fair point. You're absolutely right that the Prometheus ecosystem has a relatively small and well-defined set of clients—mostly Prometheus servers scraping endpoints, Grafana, and similar monitoring tools.

In practice, q=0 for gzip is extremely rare. HTTP clients typically either:

  • Send Accept-Encoding: gzip (or include it in a list)
  • Don't send it at all
  • Very rarely explicitly disable it with q=0

The current implementation handles the common cases correctly and will work fine with all mainstream Prometheus clients. The q=0 edge case is more of a theoretical RFC compliance issue than a practical concern.

My take: Given the specific use case (Prometheus metrics), the current implementation is pragmatic and sufficient. If you're comfortable with the trade-off of not handling q=0, I'd say ship it as-is. The benefit of perfect RFC compliance is minimal compared to the added complexity and maintenance burden.

If issues arise in the wild (which I doubt), you can always revisit it later.

🐰


🧠 Learnings used
Learnt from: shadowshot-x
Repo: fluent/fluent-bit PR: 10794
File: tests/internal/aws_compress.c:93-107
Timestamp: 2025-08-29T06:25:27.250Z
Learning: In Fluent Bit, ZSTD compression is enabled by default and is treated as a core dependency, not requiring conditional compilation guards like `#ifdef FLB_HAVE_ZSTD`. Unlike some other optional components such as ARROW/PARQUET (which use `#ifdef FLB_HAVE_ARROW` guards), ZSTD support is always available and doesn't need build-time conditionals. ZSTD headers are included directly without guards across multiple plugins and core components.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prometheus exporter should support compressed responses

2 participants