Coverage Report

Created: 2025-07-04 07:08

/src/fluent-bit/plugins/in_http/http_conn.c
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*  Fluent Bit
4
 *  ==========
5
 *  Copyright (C) 2015-2024 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
#include <fluent-bit/flb_input_plugin.h>
21
#include <fluent-bit/flb_engine.h>
22
23
#include "http.h"
24
#include "http_conn.h"
25
#include "http_prot.h"
26
27
static void http_conn_request_init(struct mk_http_session *session,
28
                                   struct mk_http_request *request);
29
30
static int http_conn_buffer_realloc(struct flb_http *ctx, struct http_conn *conn, size_t size)
31
0
{
32
0
    char *tmp;
33
34
    /* Perform realloc */
35
0
    tmp = flb_realloc(conn->buf_data, size);
36
0
    if (!tmp) {
37
0
        flb_errno();
38
0
        flb_plg_error(ctx->ins, "could not perform realloc for size %zu", size);
39
0
        return -1;
40
0
    }
41
42
    /* Update buffer info */
43
0
    conn->buf_data = tmp;
44
0
    conn->buf_size = size;
45
46
    /* Keep NULL termination */
47
0
    conn->buf_data[conn->buf_len] = '\0';
48
49
    /* Reset parser state */
50
0
    mk_http_parser_init(&conn->session.parser);
51
52
0
    return 0;
53
0
}
54
55
static int http_conn_event(void *data)
56
0
{
57
0
    int status;
58
0
    size_t size;
59
0
    size_t sent;
60
0
    ssize_t available;
61
0
    ssize_t bytes;
62
0
    size_t request_len;
63
0
    struct flb_connection *connection;
64
0
    struct http_conn *conn;
65
0
    struct mk_event *event;
66
0
    struct flb_http *ctx;
67
0
    char *request_too_large = "HTTP/1.1 413 Request Entity Too Large\r\n" \
68
0
                              "Content-Length: 0\r\n" \
69
0
                              "Connection: close\r\n\r\n";
70
71
0
    connection = (struct flb_connection *) data;
72
73
0
    conn = connection->user_data;
74
75
0
    ctx = conn->ctx;
76
77
0
    event = &connection->event;
78
79
0
    if (event->mask & MK_EVENT_READ) {
80
0
        available = (conn->buf_size - conn->buf_len) - 1;
81
0
        if (available < 1) {
82
0
            if (conn->buf_size + ctx->buffer_chunk_size > ctx->buffer_max_size) {
83
0
                flb_plg_trace(ctx->ins,
84
0
                              "fd=%i incoming data exceed limit (%zu KB)",
85
0
                              event->fd, (ctx->buffer_max_size / 1024));
86
87
0
                flb_io_net_write(conn->connection,
88
0
                                 (void *) request_too_large, strlen(request_too_large), &sent);
89
0
                http_conn_del(conn);
90
0
                return -1;
91
0
            }
92
93
0
            size = conn->buf_size + ctx->buffer_chunk_size;
94
0
            if (http_conn_buffer_realloc(ctx, conn, size) == -1) {
95
0
                flb_errno();
96
0
                http_conn_del(conn);
97
0
                return -1;
98
0
            }
99
0
            flb_plg_trace(ctx->ins, "fd=%i buffer realloc %i -> %zu",
100
0
                          event->fd, conn->buf_size, size);
101
102
0
            available = (conn->buf_size - conn->buf_len) - 1;
103
0
        }
104
105
        /* Read data */
106
0
        bytes = flb_io_net_read(connection,
107
0
                                (void *) &conn->buf_data[conn->buf_len],
108
0
                                available);
109
110
0
        if (bytes <= 0) {
111
0
            flb_plg_trace(ctx->ins, "fd=%i closed connection", event->fd);
112
0
            http_conn_del(conn);
113
0
            return -1;
114
0
        }
115
116
0
        flb_plg_trace(ctx->ins, "read()=%zi pre_len=%i now_len=%zi",
117
0
                      bytes, conn->buf_len, conn->buf_len + bytes);
118
0
        conn->buf_len += bytes;
119
0
        conn->buf_data[conn->buf_len] = '\0';
120
121
0
        status = mk_http_parser(&conn->request, &conn->session.parser,
122
0
                                conn->buf_data, conn->buf_len, conn->session.server);
123
124
0
        if (status == MK_HTTP_PARSER_OK) {
125
            /* Do more logic parsing and checks for this request */
126
0
            http_prot_handle(ctx, conn, &conn->session, &conn->request);
127
128
            /*
129
             * Evict the processed request from the connection buffer and reinitialize
130
             * the HTTP parser.
131
             */
132
133
            /* Use the last parser position as the request length */
134
0
            request_len = mk_http_parser_request_size(&conn->session.parser,
135
0
                                                      conn->buf_data,
136
0
                                                      conn->buf_len);
137
138
0
            if (request_len == -1 || (request_len > conn->buf_len)) {
139
                /* Unexpected but let's make sure things are safe */
140
0
                conn->buf_len = 0;
141
0
                flb_plg_debug(ctx->ins, "request length exceeds buffer length, closing connection");
142
0
                http_conn_del(conn);
143
0
                return -1;
144
0
            }
145
146
            /* If we have extra bytes in our bytes, adjust the extra bytes */
147
0
            if (0 < (conn->buf_len - request_len)) {
148
0
                memmove(conn->buf_data, &conn->buf_data[request_len],
149
0
                        conn->buf_len - request_len);
150
151
0
                conn->buf_data[conn->buf_len - request_len] = '\0';
152
0
                conn->buf_len -= request_len;
153
0
            }
154
0
            else {
155
0
                memset(conn->buf_data, 0, request_len);
156
0
                conn->buf_len = 0;
157
0
            }
158
159
            /* Reinitialize the parser so the next request is properly
160
                * handled, the additional memset intends to wipe any left over data
161
                * from the headers parsed in the previous request.
162
                */
163
0
            memset(&conn->session.parser, 0, sizeof(struct mk_http_parser));
164
0
            mk_http_parser_init(&conn->session.parser);
165
0
            http_conn_request_init(&conn->session, &conn->request);
166
0
        }
167
0
        else if (status == MK_HTTP_PARSER_ERROR) {
168
0
            http_prot_handle_error(ctx, conn, &conn->session, &conn->request);
169
170
            /* Reinitialize the parser so the next request is properly
171
             * handled, the additional memset intends to wipe any left over data
172
             * from the headers parsed in the previous request.
173
             */
174
0
            memset(&conn->session.parser, 0, sizeof(struct mk_http_parser));
175
0
            mk_http_parser_init(&conn->session.parser);
176
0
            http_conn_request_init(&conn->session, &conn->request);
177
0
        }
178
179
        /* FIXME: add Protocol handler here */
180
0
        return bytes;
181
0
    }
182
183
0
    if (event->mask & MK_EVENT_CLOSE) {
184
0
        flb_plg_trace(ctx->ins, "fd=%i hangup", event->fd);
185
0
        http_conn_del(conn);
186
0
        return -1;
187
0
    }
188
189
0
    return 0;
190
191
0
}
192
193
static void http_conn_session_init(struct mk_http_session *session,
194
                                   struct mk_server *server,
195
                                   int client_fd)
196
0
{
197
    /* Alloc memory for node */
198
0
    session->_sched_init = MK_TRUE;
199
0
    session->pipelined   = MK_FALSE;
200
0
    session->counter_connections = 0;
201
0
    session->close_now = MK_FALSE;
202
0
    session->status = MK_REQUEST_STATUS_INCOMPLETE;
203
0
    session->server = server;
204
0
    session->socket = client_fd;
205
206
    /* creation time in unix time */
207
0
    session->init_time = time(NULL);
208
209
0
    session->channel = mk_channel_new(MK_CHANNEL_SOCKET, session->socket);
210
0
    session->channel->io = session->server->network;
211
212
    /* Init session request list */
213
0
    mk_list_init(&session->request_list);
214
215
    /* Initialize the parser */
216
0
    mk_http_parser_init(&session->parser);
217
0
}
218
219
static void http_conn_request_init(struct mk_http_session *session,
220
                                   struct mk_http_request *request)
221
0
{
222
0
    memset(request, 0, sizeof(struct mk_http_request));
223
224
0
    mk_http_request_init(session, request, session->server);
225
226
0
    request->in_headers.type        = MK_STREAM_IOV;
227
0
    request->in_headers.dynamic     = MK_FALSE;
228
0
    request->in_headers.cb_consumed = NULL;
229
0
    request->in_headers.cb_finished = NULL;
230
0
    request->in_headers.stream      = &request->stream;
231
232
0
    mk_list_add(&request->in_headers._head, &request->stream.inputs);
233
234
0
    request->session = session;
235
0
}
236
237
struct http_conn *http_conn_add(struct flb_connection *connection,
238
                                struct flb_http *ctx)
239
0
{
240
0
    struct http_conn *conn;
241
0
    int               ret;
242
243
0
    conn = flb_calloc(1, sizeof(struct http_conn));
244
0
    if (!conn) {
245
0
        flb_errno();
246
0
        return NULL;
247
0
    }
248
249
0
    conn->connection = connection;
250
251
    /* Set data for the event-loop */
252
0
    MK_EVENT_NEW(&connection->event);
253
254
0
    connection->user_data     = conn;
255
0
    connection->event.type    = FLB_ENGINE_EV_CUSTOM;
256
0
    connection->event.handler = http_conn_event;
257
258
    /* Connection info */
259
0
    conn->ctx     = ctx;
260
0
    conn->buf_len = 0;
261
262
0
    conn->buf_data = flb_malloc(ctx->buffer_chunk_size);
263
0
    if (!conn->buf_data) {
264
0
        flb_errno();
265
266
0
        flb_plg_error(ctx->ins, "could not allocate new connection");
267
0
        flb_free(conn);
268
269
0
        return NULL;
270
0
    }
271
0
    conn->buf_size = ctx->buffer_chunk_size;
272
273
    /* Register instance into the event loop */
274
0
    ret = mk_event_add(flb_engine_evl_get(),
275
0
                       connection->fd,
276
0
                       FLB_ENGINE_EV_CUSTOM,
277
0
                       MK_EVENT_READ,
278
0
                       &connection->event);
279
0
    if (ret == -1) {
280
0
        flb_plg_error(ctx->ins, "could not register new connection");
281
282
0
        flb_free(conn->buf_data);
283
0
        flb_free(conn);
284
285
0
        return NULL;
286
0
    }
287
288
    /* Initialize HTTP Session: this is a custom context for Monkey HTTP */
289
0
    http_conn_session_init(&conn->session, ctx->server, conn->connection->fd);
290
291
    /* Initialize HTTP Request: this is the initial request and it will be reinitialized
292
     * automatically after the request is handled so it can be used for the next one.
293
     */
294
0
    http_conn_request_init(&conn->session, &conn->request);
295
296
    /* Link connection node to parent context list */
297
0
    mk_list_add(&conn->_head, &ctx->connections);
298
299
0
    return conn;
300
0
}
301
302
int http_conn_del(struct http_conn *conn)
303
0
{
304
0
    if (conn->session.channel != NULL) {
305
0
        mk_channel_release(conn->session.channel);
306
0
    }
307
308
    /* The downstream unregisters the file descriptor from the event-loop
309
     * so there's nothing to be done by the plugin
310
     */
311
0
    flb_downstream_conn_release(conn->connection);
312
313
0
    mk_list_del(&conn->_head);
314
315
0
    flb_free(conn->buf_data);
316
0
    flb_free(conn);
317
318
0
    return 0;
319
0
}
320
321
void http_conn_release_all(struct flb_http *ctx)
322
0
{
323
0
    struct mk_list *tmp;
324
0
    struct mk_list *head;
325
0
    struct http_conn *conn;
326
327
0
    mk_list_foreach_safe(head, tmp, &ctx->connections) {
328
0
        conn = mk_list_entry(head, struct http_conn, _head);
329
0
        http_conn_del(conn);
330
0
    }
331
0
}