Coverage Report

Created: 2026-03-09 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fluent-bit/src/flb_http_client_http1.c
Line
Count
Source
1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*  Fluent Bit
4
 *  ==========
5
 *  Copyright (C) 2015-2026 The Fluent Bit Authors
6
 *
7
 *  Licensed under the Apache License, Version 2.0 (the "License");
8
 *  you may not use this file except in compliance with the License.
9
 *  You may obtain a copy of the License at
10
 *
11
 *      http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 *  Unless required by applicable law or agreed to in writing, software
14
 *  distributed under the License is distributed on an "AS IS" BASIS,
15
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 *  See the License for the specific language governing permissions and
17
 *  limitations under the License.
18
 */
19
20
#define _GNU_SOURCE
21
#include <string.h>
22
23
#include <fluent-bit/flb_info.h>
24
#include <fluent-bit/flb_kv.h>
25
#include <fluent-bit/flb_log.h>
26
#include <fluent-bit/flb_mem.h>
27
#include <fluent-bit/flb_http_common.h>
28
#include <fluent-bit/flb_http_client.h>
29
#include <fluent-bit/flb_http_client_debug.h>
30
#include <fluent-bit/flb_utils.h>
31
#include <fluent-bit/flb_base64.h>
32
#include <fluent-bit/tls/flb_tls.h>
33
34
static inline size_t http2_lower_value(size_t left_value, size_t right_value);
35
36
37
static int compose_request_line(cfl_sds_t *output_buffer,
38
                                struct flb_http_request *request);
39
40
41
static int compose_header_line(cfl_sds_t *output_buffer,
42
                               char *name,
43
                               size_t name_length,
44
                               char *value,
45
                               size_t value_length);
46
47
static int parse_term(char **term_start,
48
                      size_t *term_length,
49
                      char **next_term,
50
                      char *current_term,
51
                      char delimiter_character,
52
                      int trim_leading_spaces,
53
                      int trim_trailing_spaces);
54
55
static int parse_headers(struct flb_http_response *response, char *headers);
56
57
58
int flb_http1_client_session_init(struct flb_http1_client_session *session)
59
0
{
60
0
    return 0;
61
0
}
62
63
void flb_http1_client_session_destroy(struct flb_http1_client_session *session)
64
0
{
65
0
    if (session != NULL) {
66
0
        if (session->initialized) {
67
0
            session->initialized = FLB_FALSE;
68
0
        }
69
0
    }
70
0
}
71
72
static int cfl_sds_shift_left(cfl_sds_t *buffer, size_t positions)
73
0
{
74
0
    size_t buffer_length;
75
0
    size_t remainder;
76
77
0
    buffer_length = cfl_sds_len(*buffer);
78
0
    remainder = 0;
79
80
0
    if (positions < buffer_length) {
81
0
        remainder = buffer_length - positions;
82
83
0
        memmove(*buffer,
84
0
                &((*buffer)[positions]),
85
0
                remainder);
86
0
    }
87
88
0
    cfl_sds_len_set(*buffer, remainder);
89
90
0
    (*buffer)[remainder] = '\0';
91
92
0
    return 0;
93
0
}
94
95
static int flb_http1_client_session_process_headers(struct flb_http_client_session *session,
96
                                                    struct flb_http_response *response)
97
0
{
98
0
    char   *header_block_begining;
99
0
    size_t  header_block_length;
100
0
    size_t  status_line_length;
101
0
    char   *header_block_end;
102
0
    char   *status_line;
103
0
    int     result;
104
105
0
    status_line = session->incoming_data;
106
107
    /* We need at least 8 characters to differentiate
108
    * HTTP/1.x from HTTP/0.9
109
    */
110
0
    if (cfl_sds_len(status_line) < 9) {
111
0
        return 0;
112
0
    }
113
114
0
    if (strncasecmp(status_line, "HTTP/1.1 ", 9) == 0) {
115
0
        response->protocol_version = HTTP_PROTOCOL_VERSION_11;
116
0
    }
117
0
    else if (strncasecmp(status_line, "HTTP/1.0 ", 9) == 0) {
118
0
        response->protocol_version = HTTP_PROTOCOL_VERSION_11;
119
0
    }
120
0
    else {
121
0
        response->protocol_version = HTTP_PROTOCOL_VERSION_09;
122
0
    }
123
124
0
    if (response->protocol_version == HTTP_PROTOCOL_VERSION_09) {
125
0
        flb_http_response_set_status(response, 200);
126
127
0
        flb_http_response_set_message(response, "");
128
129
0
        response->stream->status = HTTP_STREAM_STATUS_RECEIVING_DATA;
130
0
    }
131
0
    else {
132
0
        header_block_begining = strstr(status_line, "\r\n");
133
134
0
        if (header_block_begining == NULL) {
135
0
            return 0;
136
0
        }
137
138
0
        status_line_length = (size_t) (((uintptr_t) header_block_begining) -
139
0
                                       ((uintptr_t) status_line));
140
141
0
        header_block_begining = &header_block_begining[2];
142
143
0
        header_block_end = strstr(header_block_begining, "\r\n\r\n");
144
145
0
        if (header_block_end == NULL) {
146
0
            return 0;
147
0
        }
148
149
0
        header_block_length = (size_t) ((uintptr_t) header_block_end -
150
0
                                        (uintptr_t) header_block_begining);
151
152
0
        if (response->status <= 0) {
153
0
            response->status = (int) strtoul(&status_line[9], NULL, 10);
154
155
0
            if (response->status < 100 || response->status > 599) {
156
0
                response->stream->status = HTTP_STREAM_STATUS_ERROR;
157
158
0
                return -1;
159
0
            }
160
161
0
            result = parse_headers(response, header_block_begining);
162
163
0
            if (result != 0) {
164
0
                response->stream->status = HTTP_STREAM_STATUS_ERROR;
165
166
0
                return -1;
167
0
            }
168
0
        }
169
170
0
        cfl_sds_shift_left(&session->incoming_data,
171
0
                           status_line_length + 2 +
172
0
                           header_block_length + 4);
173
174
0
        response->stream->status = HTTP_STREAM_STATUS_RECEIVING_DATA;
175
0
    }
176
177
0
    return 0;
178
0
}
179
180
static int flb_http1_client_session_process_data(struct flb_http_client_session *session,
181
                                                 struct flb_http_response *response)
182
0
{
183
0
    char                           *body;
184
0
    int                             body_streaming_flag;
185
0
    int                             chunked_transfer;
186
0
    char                           *transfer_encoding;
187
0
    size_t                          body_length;
188
0
    size_t                         body_remainder;
189
0
    int     result;
190
0
    size_t                         chunk_length_length;
191
0
    char                          *chunk_length_end;
192
0
    char                          *chunk_header;
193
0
    char                          *chunk_data;
194
0
    size_t                         chunk_length;
195
0
    size_t                         required_size;
196
197
0
    body = session->incoming_data;
198
199
0
    body_streaming_flag = (session->parent->flags &
200
0
                           FLB_HTTP_CLIENT_FLAG_STREAM_BODY) != 0;
201
202
0
    chunked_transfer = FLB_FALSE;
203
204
0
    if (response->content_length == 0) {
205
0
        transfer_encoding = flb_http_response_get_header(response, "transfer-encoding");
206
207
0
        if (transfer_encoding != NULL) {
208
0
            if (strncasecmp(transfer_encoding, "chunked", 7) == 0) {
209
0
                chunked_transfer = FLB_TRUE;
210
0
            }
211
0
        }
212
0
    }
213
214
0
    body_length = cfl_sds_len(body);
215
216
0
    if (chunked_transfer == FLB_FALSE) {
217
0
        if (response->content_length > 0) {
218
0
            if ((response->body_read_offset + body_length) <
219
0
                response->content_length) {
220
0
                if (body_streaming_flag == FLB_FALSE) {
221
0
                    return 0;
222
0
                }
223
224
0
                response->body_read_offset += body_length;
225
226
0
                result = flb_http_response_append_to_body(
227
0
                            response,
228
0
                            (unsigned char *) body,
229
0
                            body_length);
230
0
            }
231
0
            else {
232
0
                result = flb_http_response_append_to_body(
233
0
                            response,
234
0
                            (unsigned char *) body,
235
0
                            body_length);
236
237
0
                response->stream->status = HTTP_STREAM_STATUS_READY;
238
0
            }
239
240
0
            cfl_sds_shift_left(&session->incoming_data, body_length);
241
0
        }
242
0
        else {
243
0
            response->stream->status = HTTP_STREAM_STATUS_READY;
244
0
        }
245
0
    }
246
0
    else {
247
0
        body_remainder = body_length;
248
249
0
        while (body_remainder > 0) {
250
0
            chunk_header = session->incoming_data;
251
252
0
            if (strchr(chunk_header, '\r') == NULL) {
253
0
                return 0;
254
0
            }
255
256
0
            errno = 0;
257
258
0
            chunk_length = strtoull(chunk_header, &chunk_length_end, 16);
259
260
0
            if (errno != 0) {
261
0
                response->stream->status = HTTP_STREAM_STATUS_ERROR;
262
263
0
                return -1;
264
0
            }
265
266
0
            chunk_length_length = (size_t) ((uintptr_t) chunk_length_end -
267
0
                                            (uintptr_t) chunk_header);
268
269
0
            required_size = chunk_length_length + 2 + chunk_length + 2;
270
271
0
            if (body_remainder < required_size) {
272
0
                return 0;
273
0
            }
274
275
0
            chunk_data = chunk_header + chunk_length_length + 2;
276
277
0
            if (chunk_length > 0) {
278
0
                result = flb_http_response_append_to_body(
279
0
                            response,
280
0
                            (unsigned char *) chunk_data,
281
0
                            chunk_length);
282
283
0
                if (result != 0) {
284
0
                    response->stream->status = HTTP_STREAM_STATUS_ERROR;
285
286
0
                    return -1;
287
0
                }
288
0
            }
289
0
            else {
290
0
                response->stream->status = HTTP_STREAM_STATUS_READY;
291
0
            }
292
293
0
            cfl_sds_shift_left(&session->incoming_data, required_size);
294
295
0
            response->body_read_offset += required_size;
296
0
            body_remainder -= required_size;
297
0
        }
298
0
    }
299
300
0
    return 0;
301
0
}
302
303
int flb_http1_client_session_ingest(struct flb_http1_client_session *session,
304
                                    unsigned char *buffer,
305
                                    size_t length)
306
0
{
307
0
    cfl_sds_t                      resized_buffer;
308
0
    int                             result;
309
0
    struct flb_http_stream         *stream;
310
311
0
    stream = cfl_list_entry_first(&session->parent->streams,
312
0
                                  struct flb_http_stream,
313
0
                                  _head);
314
315
0
    resized_buffer = cfl_sds_cat(session->parent->incoming_data,
316
0
                                 (const char *) buffer,
317
0
                                 length);
318
319
0
    if (resized_buffer == NULL) {
320
0
        return -1;
321
0
    }
322
323
0
    session->parent->incoming_data = resized_buffer;
324
0
    result = 0;
325
326
0
    if (stream->response.stream->status == HTTP_STREAM_STATUS_RECEIVING_HEADERS) {
327
0
        result = flb_http1_client_session_process_headers(
328
0
                    session->parent,
329
0
                    &stream->response);
330
0
    }
331
332
0
    if (result == 0 &&
333
0
        stream->response.stream->status == HTTP_STREAM_STATUS_RECEIVING_DATA) {
334
0
        result = flb_http1_client_session_process_data(
335
0
                    session->parent,
336
0
                    &stream->response);
337
0
    }
338
339
0
    return result;
340
0
}
341
342
int flb_http1_request_begin(struct flb_http_request *request)
343
0
{
344
0
    return 0;
345
0
}
346
347
348
int flb_http1_request_commit(struct flb_http_request *request)
349
0
{
350
0
    char                             content_length_string[16];
351
0
    struct mk_list                  *header_iterator;
352
0
    cfl_sds_t                        request_buffer;
353
0
    struct flb_http_client_session  *parent_session;
354
0
    struct flb_hash_table_entry     *header_entry;
355
0
    cfl_sds_t                        sds_result;
356
0
    struct flb_http1_client_session *session;
357
0
    struct flb_http_stream          *stream;
358
0
    int                              result;
359
360
0
    parent_session = (struct flb_http_client_session *) request->stream->parent;
361
362
0
    if (parent_session == NULL) {
363
0
        return -1;
364
0
    }
365
366
0
    session = &parent_session->http1;
367
368
0
    if (session == NULL) {
369
0
        return -1;
370
0
    }
371
372
0
    stream  = (struct flb_http_stream *) request->stream;
373
374
0
    if (stream == NULL) {
375
0
        return -2;
376
0
    }
377
378
0
    request_buffer = cfl_sds_create_size(128);
379
380
0
    if (request_buffer == NULL) {
381
0
        return -3;
382
0
    }
383
384
0
    result = compose_request_line(&request_buffer, request);
385
386
0
    if (result != 0) {
387
0
        cfl_sds_destroy(request_buffer);
388
389
0
        return -4;
390
0
    }
391
392
0
    if (request->protocol_version == HTTP_PROTOCOL_VERSION_11) {
393
0
        if(request->host != NULL) {
394
0
            result = compose_header_line(&request_buffer,
395
0
                                        "Host", 0,
396
0
                                        request->host, 0);
397
398
0
            if (result != 0) {
399
0
                cfl_sds_destroy(request_buffer);
400
401
0
                return -7;
402
0
            }
403
0
        }
404
0
    }
405
406
0
    if(request->user_agent != NULL) {
407
0
        result = compose_header_line(&request_buffer,
408
0
                                     "User-agent", 0,
409
0
                                     request->user_agent, 0);
410
411
0
        if (result != 0) {
412
0
            cfl_sds_destroy(request_buffer);
413
414
0
            return -8;
415
0
        }
416
0
    }
417
418
0
    if(request->content_type != NULL) {
419
0
        result = compose_header_line(&request_buffer,
420
0
                                     "Content-Type", 0,
421
0
                                     request->content_type, 0);
422
423
0
        if (result != 0) {
424
0
            cfl_sds_destroy(request_buffer);
425
426
0
            return -9;
427
0
        }
428
0
    }
429
430
0
    if (request->method == HTTP_METHOD_POST ||
431
0
        request->method == HTTP_METHOD_PUT) {
432
0
        snprintf(content_length_string,
433
0
                 sizeof(content_length_string) - 1,
434
0
                 "%zu",
435
0
                 request->content_length);
436
437
0
        content_length_string[sizeof(content_length_string) - 1] = '\0';
438
439
0
        result = compose_header_line(&request_buffer,
440
0
                                     "Content-Length", 0,
441
0
                                     content_length_string, 0);
442
443
0
        if (result != 0) {
444
0
            cfl_sds_destroy(request_buffer);
445
446
0
            return -7;
447
0
        }
448
0
    }
449
450
0
    mk_list_foreach(header_iterator, &request->headers->entries) {
451
0
        header_entry = mk_list_entry(header_iterator,
452
0
                                     struct flb_hash_table_entry,
453
0
                                     _head_parent);
454
455
0
        if (header_entry == NULL) {
456
0
            cfl_sds_destroy(request_buffer);
457
458
0
            return -5;
459
0
        }
460
461
0
        result = compose_header_line(&request_buffer,
462
0
                                      header_entry->key,
463
0
                                      header_entry->key_len,
464
0
                                      header_entry->val,
465
0
                                      header_entry->val_size);
466
467
0
        if (result != 0) {
468
0
            cfl_sds_destroy(request_buffer);
469
470
0
            return -6;
471
0
        }
472
0
    }
473
474
0
    sds_result = cfl_sds_cat(request_buffer, "\r\n", 2);
475
476
0
    if (sds_result == NULL) {
477
0
        cfl_sds_destroy(request_buffer);
478
479
0
        return -7;
480
0
    }
481
482
0
    request_buffer = sds_result;
483
484
0
    if (request->body != NULL) {
485
0
        sds_result = cfl_sds_cat(request_buffer,
486
0
                                 request->body,
487
0
                                 cfl_sds_len(request->body));
488
489
0
        if (sds_result == NULL) {
490
0
            cfl_sds_destroy(request_buffer);
491
492
0
            return -8;
493
0
        }
494
495
0
        request_buffer = sds_result;
496
0
    }
497
498
0
    sds_result = cfl_sds_cat(session->parent->outgoing_data,
499
0
                             request_buffer,
500
0
                             cfl_sds_len(request_buffer));
501
502
0
    cfl_sds_destroy(request_buffer);
503
504
0
    if (sds_result == NULL) {
505
0
        return -9;
506
0
    }
507
508
0
    session->parent->outgoing_data = sds_result;
509
510
0
    return 0;
511
0
}
512
513
static int compose_request_line(cfl_sds_t *output_buffer,
514
                                struct flb_http_request *request)
515
0
{
516
0
    const char *protocol_version_string;
517
0
    const char *method_name;
518
0
    cfl_sds_t   sds_result;
519
520
0
    sds_result = NULL;
521
522
0
    if (request->protocol_version == HTTP_PROTOCOL_VERSION_11) {
523
0
        protocol_version_string = " HTTP/1.1";
524
0
    }
525
0
    else if (request->protocol_version == HTTP_PROTOCOL_VERSION_10) {
526
0
        protocol_version_string = " HTTP/1.0";
527
0
    }
528
0
    else if (request->protocol_version == HTTP_PROTOCOL_VERSION_09) {
529
0
        protocol_version_string = "";
530
0
    }
531
0
    else {
532
0
        return -1;
533
0
    }
534
535
0
    method_name = flb_http_get_method_string_from_id(request->method);
536
537
0
    if (method_name == NULL) {
538
0
        return -1;
539
0
    }
540
541
0
    if (request->method == HTTP_METHOD_CONNECT) {
542
0
        sds_result = cfl_sds_printf(output_buffer,
543
0
                                    "CONNECT %s:%u%s\r\n",
544
0
                                    request->host,
545
0
                                    request->port,
546
0
                                    protocol_version_string);
547
0
    }
548
0
    else {
549
0
        if (request->query_string != NULL) {
550
0
            sds_result = cfl_sds_printf(output_buffer,
551
0
                                        "%s %s?%s%s\r\n",
552
0
                                        method_name,
553
0
                                        request->path,
554
0
                                        request->query_string,
555
0
                                        protocol_version_string);
556
0
        }
557
0
        else {
558
0
            sds_result = cfl_sds_printf(output_buffer,
559
0
                                        "%s %s%s\r\n",
560
0
                                        method_name,
561
0
                                        request->path,
562
0
                                        protocol_version_string);
563
0
        }
564
0
    }
565
566
0
    if (sds_result == NULL) {
567
0
        return -1;
568
0
    }
569
570
0
    *output_buffer = sds_result;
571
572
0
    return 0;
573
0
}
574
575
static int compose_header_line(cfl_sds_t *output_buffer,
576
                               char *name,
577
                               size_t name_length,
578
                               char *value,
579
                               size_t value_length)
580
0
{
581
0
    cfl_sds_t sds_result;
582
583
0
    if (name_length == 0) {
584
0
        name_length = strlen(name);
585
0
    }
586
587
0
    if (value_length == 0) {
588
0
        value_length = strlen(value);
589
0
    }
590
591
0
    sds_result = cfl_sds_printf(output_buffer,
592
0
                                "%.*s: %.*s\r\n",
593
0
                                (int) name_length,
594
0
                                name,
595
0
                                (int) value_length,
596
0
                                value);
597
598
0
    if (sds_result == NULL) {
599
0
        return -1;
600
0
    }
601
602
0
    return 0;
603
0
}
604
605
606
607
608
static int parse_term(char **term_start,
609
                      size_t *term_length,
610
                      char **next_term,
611
                      char *current_term,
612
                      char delimiter_character,
613
                      int trim_leading_spaces,
614
                      int trim_trailing_spaces)
615
0
{
616
0
    char  *term_delimiter;
617
618
0
    if (trim_leading_spaces) {
619
0
        while (current_term[0] == ' ') {
620
0
            current_term++;
621
0
        }
622
0
    }
623
624
0
    if (current_term[0] == '\0') {
625
0
        return -1;
626
0
    }
627
628
0
    term_delimiter = strchr(current_term, delimiter_character);
629
630
0
    if (term_delimiter == NULL) {
631
0
        return -1;
632
0
    }
633
634
0
    *term_start = current_term;
635
0
    *term_length = (size_t) ((uintptr_t) term_delimiter -
636
0
                             (uintptr_t) current_term);
637
638
0
    *next_term = &term_delimiter[1];
639
640
0
    if (trim_trailing_spaces) {
641
0
        while (*term_length > 0 && current_term[*term_length - 1] == ' ') {
642
0
            (*term_length)--;
643
0
        }
644
0
    }
645
646
0
    return 0;
647
0
}
648
649
static int parse_headers(struct flb_http_response *response, char *headers)
650
0
{
651
0
    char    temporary_buffer[21];
652
0
    char    *current_term;
653
0
    size_t   value_length;
654
0
    size_t   name_length;
655
0
    int      result;
656
0
    char    *value;
657
0
    char    *name;
658
659
0
    current_term = headers;
660
661
0
    while (current_term != NULL && current_term[0] != '\r') {
662
0
        result = parse_term(&name,
663
0
                            &name_length,
664
0
                            &current_term,
665
0
                            current_term,
666
0
                            ':',
667
0
                            FLB_TRUE,
668
0
                            FLB_TRUE);
669
670
0
        if (result != 0) {
671
0
            return -1;
672
0
        }
673
674
0
        result = parse_term(&value,
675
0
                            &value_length,
676
0
                            &current_term,
677
0
                            current_term,
678
0
                            '\r',
679
0
                            FLB_TRUE,
680
0
                            FLB_TRUE);
681
682
0
        if (result != 0) {
683
0
            return -1;
684
0
        }
685
686
0
        if (flb_http_server_strncasecmp((uint8_t *) name,
687
0
                                        name_length,
688
0
                                        "content-type", 0) == 0) {
689
0
            response->content_type = \
690
0
                cfl_sds_create_len((const char *) value,
691
0
                                    value_length);
692
693
0
            if (response->content_type == NULL) {
694
0
                return -1;
695
0
            }
696
0
        }
697
0
        else if (flb_http_server_strncasecmp((uint8_t *) name,
698
0
                                             name_length,
699
0
                                             "content-length", 0) == 0) {
700
0
            strncpy(temporary_buffer,
701
0
                    (const char *) value,
702
0
                    http2_lower_value(sizeof(temporary_buffer), value_length + 1));
703
704
0
            temporary_buffer[sizeof(temporary_buffer) - 1] = '\0';
705
706
0
            response->content_length = strtoull(temporary_buffer, NULL, 10);
707
0
        }
708
709
0
        result = flb_http_response_set_header(response,
710
0
                                              name,
711
0
                                              name_length,
712
0
                                              (void *) value,
713
0
                                              value_length);
714
715
0
        if (result != 0) {
716
0
            return -1;
717
0
        }
718
719
0
        current_term = &current_term[1];
720
0
    }
721
722
0
    return 0;
723
0
}
724
725
static inline size_t http2_lower_value(size_t left_value, size_t right_value)
726
0
{
727
0
    if (left_value < right_value) {
728
0
        return left_value;
729
0
    }
730
731
0
    return right_value;
732
0
}