Coverage Report

Created: 2023-11-19 07:36

/src/fluent-bit/plugins/in_http/http_prot.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-2022 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_version.h>
22
#include <fluent-bit/flb_error.h>
23
#include <fluent-bit/flb_pack.h>
24
25
#include <monkey/monkey.h>
26
#include <monkey/mk_core.h>
27
28
#include "http.h"
29
#include "http_conn.h"
30
31
0
#define HTTP_CONTENT_JSON       0
32
0
#define HTTP_CONTENT_URLENCODED 1
33
34
static inline char hex2nibble(char c)
35
0
{
36
0
    if ((c >= 0x30) && (c <= '9')) {
37
0
        return c - 0x30;
38
0
    }
39
    // 0x30-0x39 are digits, 0x41-0x46 A-F,
40
    // so there is a gap at 0x40
41
0
    if ((c >= 'A') && (c <= 'F')) {
42
0
        return (c - 'A') + 10;
43
0
    }
44
0
    if ((c >= 'a') && (c <= 'f')) {
45
0
        return (c - 'a') + 10;
46
0
    }
47
0
    return 0;
48
0
}
49
50
static int sds_uri_decode(flb_sds_t s)
51
0
{
52
0
    char buf[1024];
53
0
    char *optr;
54
0
    char *iptr;
55
56
57
0
    for (optr = buf, iptr = s; iptr < s + flb_sds_len(s) && optr-buf < sizeof(buf); iptr++) {
58
0
        if (*iptr == '%') {
59
0
            if (iptr+2 > (s + flb_sds_len(s))) {
60
0
                return -1;
61
0
            }
62
0
            *optr++ = hex2nibble(*(iptr+1)) << 4 | hex2nibble(*(iptr+2));
63
0
            iptr+=2;
64
0
        } else if (*iptr == '+') {
65
0
            *optr++ = ' ';
66
0
        } else {
67
0
            *optr++ = *iptr;
68
0
        }
69
0
    }
70
71
0
    memcpy(s, buf, optr-buf);
72
0
    s[optr-buf] = '\0';
73
0
    flb_sds_len_set(s, (optr-buf));
74
75
0
    return 0;
76
0
}
77
78
static int send_response(struct http_conn *conn, int http_status, char *message)
79
0
{
80
0
    struct flb_http *context;
81
0
    size_t           sent;
82
0
    int              len;
83
0
    flb_sds_t        out;
84
85
0
    context = (struct flb_http *) conn->ctx;
86
87
0
    out = flb_sds_create_size(256);
88
0
    if (!out) {
89
0
        return -1;
90
0
    }
91
92
0
    if (message) {
93
0
        len = strlen(message);
94
0
    }
95
0
    else {
96
0
        len = 0;
97
0
    }
98
99
0
    if (http_status == 201) {
100
0
        flb_sds_printf(&out,
101
0
                       "HTTP/1.1 201 Created \r\n"
102
0
                       "Server: Fluent Bit v%s\r\n"
103
0
                       "%s"
104
0
                       "Content-Length: 0\r\n\r\n",
105
0
                       FLB_VERSION_STR,
106
0
                       context->success_headers_str);
107
0
    }
108
0
    else if (http_status == 200) {
109
0
        flb_sds_printf(&out,
110
0
                       "HTTP/1.1 200 OK\r\n"
111
0
                       "Server: Fluent Bit v%s\r\n"
112
0
                       "%s"
113
0
                       "Content-Length: 0\r\n\r\n",
114
0
                       FLB_VERSION_STR,
115
0
                       context->success_headers_str);
116
0
    }
117
0
    else if (http_status == 204) {
118
0
        flb_sds_printf(&out,
119
0
                       "HTTP/1.1 204 No Content\r\n"
120
0
                       "Server: Fluent Bit v%s\r\n"
121
0
                       "%s"
122
0
                       "\r\n\r\n",
123
0
                       FLB_VERSION_STR,
124
0
                       context->success_headers_str);
125
0
    }
126
0
    else if (http_status == 400) {
127
0
        flb_sds_printf(&out,
128
0
                       "HTTP/1.1 400 Forbidden\r\n"
129
0
                       "Server: Fluent Bit v%s\r\n"
130
0
                       "Content-Length: %i\r\n\r\n%s",
131
0
                       FLB_VERSION_STR,
132
0
                       len, message);
133
0
    }
134
135
    /* We should check this operations result */
136
0
    flb_io_net_write(conn->connection,
137
0
                     (void *) out,
138
0
                     flb_sds_len(out),
139
0
                     &sent);
140
141
0
    flb_sds_destroy(out);
142
143
0
    return 0;
144
0
}
145
146
/* implements functionality to get tag from key in record */
147
static flb_sds_t tag_key(struct flb_http *ctx, msgpack_object *map)
148
0
{
149
0
    size_t map_size = map->via.map.size;
150
0
    msgpack_object_kv *kv;
151
0
    msgpack_object  key;
152
0
    msgpack_object  val;
153
0
    char *key_str = NULL;
154
0
    char *val_str = NULL;
155
0
    size_t key_str_size = 0;
156
0
    size_t val_str_size = 0;
157
0
    int j;
158
0
    int check = FLB_FALSE;
159
0
    int found = FLB_FALSE;
160
0
    flb_sds_t tag;
161
162
0
    kv = map->via.map.ptr;
163
164
0
    for(j=0; j < map_size; j++) {
165
0
        check = FLB_FALSE;
166
0
        found = FLB_FALSE;
167
0
        key = (kv+j)->key;
168
0
        if (key.type == MSGPACK_OBJECT_BIN) {
169
0
            key_str  = (char *) key.via.bin.ptr;
170
0
            key_str_size = key.via.bin.size;
171
0
            check = FLB_TRUE;
172
0
        }
173
0
        if (key.type == MSGPACK_OBJECT_STR) {
174
0
            key_str  = (char *) key.via.str.ptr;
175
0
            key_str_size = key.via.str.size;
176
0
            check = FLB_TRUE;
177
0
        }
178
179
0
        if (check == FLB_TRUE) {
180
0
            if (strncmp(ctx->tag_key, key_str, key_str_size) == 0) {
181
0
                val = (kv+j)->val;
182
0
                if (val.type == MSGPACK_OBJECT_BIN) {
183
0
                    val_str  = (char *) val.via.bin.ptr;
184
0
                    val_str_size = val.via.str.size;
185
0
                    found = FLB_TRUE;
186
0
                    break;
187
0
                }
188
0
                if (val.type == MSGPACK_OBJECT_STR) {
189
0
                    val_str  = (char *) val.via.str.ptr;
190
0
                    val_str_size = val.via.str.size;
191
0
                    found = FLB_TRUE;
192
0
                    break;
193
0
                }
194
0
            }
195
0
        }
196
0
    }
197
198
0
    if (found == FLB_TRUE) {
199
0
        tag = flb_sds_create_len(val_str, val_str_size);
200
0
        if (!tag) {
201
0
            flb_errno();
202
0
            return NULL;
203
0
        }
204
0
        return tag;
205
0
    }
206
        
207
        
208
0
    flb_plg_error(ctx->ins, "Could not find tag_key %s in record", ctx->tag_key);
209
0
    return NULL;
210
0
}
211
212
int process_pack(struct flb_http *ctx, flb_sds_t tag, char *buf, size_t size)
213
0
{
214
0
    int ret;
215
0
    size_t off = 0;
216
0
    msgpack_unpacked result;
217
0
    struct flb_time tm;
218
0
    int i = 0;
219
0
    msgpack_object *obj;
220
0
    msgpack_object record;
221
0
    flb_sds_t tag_from_record = NULL;
222
223
0
    flb_time_get(&tm);
224
225
0
    msgpack_unpacked_init(&result);
226
0
    while (msgpack_unpack_next(&result, buf, size, &off) == MSGPACK_UNPACK_SUCCESS) {
227
0
        if (result.data.type == MSGPACK_OBJECT_MAP) {
228
0
            tag_from_record = NULL;
229
0
            if (ctx->tag_key) {
230
0
                obj = &result.data;
231
0
                tag_from_record = tag_key(ctx, obj);
232
0
            }
233
234
0
            ret = flb_log_event_encoder_begin_record(&ctx->log_encoder);
235
236
0
            if (ret == FLB_EVENT_ENCODER_SUCCESS) {
237
0
                ret = flb_log_event_encoder_set_timestamp(
238
0
                        &ctx->log_encoder,
239
0
                        &tm);
240
0
            }
241
242
0
            if (ret == FLB_EVENT_ENCODER_SUCCESS) {
243
0
                ret = flb_log_event_encoder_set_body_from_msgpack_object(
244
0
                        &ctx->log_encoder,
245
0
                        &result.data);
246
0
            }
247
248
0
            if (ret == FLB_EVENT_ENCODER_SUCCESS) {
249
0
                ret = flb_log_event_encoder_commit_record(&ctx->log_encoder);
250
0
            }
251
252
0
            if (ret == FLB_EVENT_ENCODER_SUCCESS) {
253
0
                if (tag_from_record) {
254
0
                    flb_input_log_append(ctx->ins,
255
0
                                         tag_from_record,
256
0
                                         flb_sds_len(tag_from_record),
257
0
                                         ctx->log_encoder.output_buffer,
258
0
                                         ctx->log_encoder.output_length);
259
260
0
                    flb_sds_destroy(tag_from_record);
261
0
                }
262
0
                else if (tag) {
263
0
                    flb_input_log_append(ctx->ins, tag, flb_sds_len(tag),
264
0
                                         ctx->log_encoder.output_buffer,
265
0
                                         ctx->log_encoder.output_length);
266
0
                }
267
0
                else {
268
                    /* use default plugin Tag (it internal name, e.g: http.0 */
269
0
                    flb_input_log_append(ctx->ins, NULL, 0,
270
0
                                         ctx->log_encoder.output_buffer,
271
0
                                         ctx->log_encoder.output_length);
272
0
                }
273
0
            }
274
0
            else {
275
0
                flb_plg_error(ctx->ins, "Error encoding record : %d", ret);
276
0
            }
277
278
0
            flb_log_event_encoder_reset(&ctx->log_encoder);
279
0
        } 
280
0
        else if (result.data.type == MSGPACK_OBJECT_ARRAY) {
281
0
            obj = &result.data;
282
0
            for (i = 0; i < obj->via.array.size; i++)
283
0
            {
284
0
                record = obj->via.array.ptr[i];
285
286
0
                tag_from_record = NULL;
287
0
                if (ctx->tag_key) {
288
0
                    tag_from_record = tag_key(ctx, &record);
289
0
                }
290
291
0
                ret = flb_log_event_encoder_begin_record(&ctx->log_encoder);
292
293
0
                if (ret == FLB_EVENT_ENCODER_SUCCESS) {
294
0
                    ret = flb_log_event_encoder_set_timestamp(
295
0
                            &ctx->log_encoder,
296
0
                            &tm);
297
0
                }
298
299
0
                if (ret == FLB_EVENT_ENCODER_SUCCESS) {
300
0
                    ret = flb_log_event_encoder_set_body_from_msgpack_object(
301
0
                            &ctx->log_encoder,
302
0
                            &record);
303
0
                }
304
305
0
                if (ret == FLB_EVENT_ENCODER_SUCCESS) {
306
0
                    ret = flb_log_event_encoder_commit_record(&ctx->log_encoder);
307
0
                }
308
309
0
                if (ret == FLB_EVENT_ENCODER_SUCCESS) {
310
0
                    if (tag_from_record) {
311
0
                        flb_input_log_append(ctx->ins,
312
0
                                             tag_from_record,
313
0
                                             flb_sds_len(tag_from_record),
314
0
                                             ctx->log_encoder.output_buffer,
315
0
                                             ctx->log_encoder.output_length);
316
317
0
                        flb_sds_destroy(tag_from_record);
318
0
                    }
319
0
                    else if (tag) {
320
0
                        flb_input_log_append(ctx->ins, tag, flb_sds_len(tag),
321
0
                                             ctx->log_encoder.output_buffer,
322
0
                                             ctx->log_encoder.output_length);
323
0
                    }
324
0
                    else {
325
                        /* use default plugin Tag (it internal name, e.g: http.0 */
326
0
                        flb_input_log_append(ctx->ins, NULL, 0,
327
0
                                             ctx->log_encoder.output_buffer,
328
0
                                             ctx->log_encoder.output_length);
329
0
                    }
330
0
                }
331
0
                else {
332
0
                    flb_plg_error(ctx->ins, "Error encoding record : %d", ret);
333
0
                }
334
335
                /* TODO : Optimize this
336
                 *
337
                 * This is wasteful, considering that we are emitting a series
338
                 * of records we should start and commit each one and then
339
                 * emit them all at once after the loop.
340
                 */
341
342
0
                flb_log_event_encoder_reset(&ctx->log_encoder);
343
0
            }
344
345
0
            break;
346
0
        } 
347
0
        else {
348
0
            flb_plg_error(ctx->ins, "skip record from invalid type: %i",
349
0
                         result.data.type);
350
351
0
            msgpack_unpacked_destroy(&result);
352
353
0
            return -1;
354
0
        }
355
0
    }
356
357
0
    msgpack_unpacked_destroy(&result);
358
359
0
    return 0;
360
0
}
361
362
static ssize_t parse_payload_json(struct flb_http *ctx, flb_sds_t tag,
363
                                  char *payload, size_t size)
364
0
{
365
0
    int ret;
366
0
    int out_size;
367
0
    char *pack;
368
0
    struct flb_pack_state pack_state;
369
370
    /* Initialize packer */
371
0
    flb_pack_state_init(&pack_state);
372
373
    /* Pack JSON as msgpack */
374
0
    ret = flb_pack_json_state(payload, size,
375
0
                              &pack, &out_size, &pack_state);
376
0
    flb_pack_state_reset(&pack_state);
377
378
    /* Handle exceptions */
379
0
    if (ret == FLB_ERR_JSON_PART) {
380
0
        flb_plg_warn(ctx->ins, "JSON data is incomplete, skipping");
381
0
        return -1;
382
0
    }
383
0
    else if (ret == FLB_ERR_JSON_INVAL) {
384
0
        flb_plg_warn(ctx->ins, "invalid JSON message, skipping");
385
0
        return -1;
386
0
    }
387
0
    else if (ret == -1) {
388
0
        return -1;
389
0
    }
390
391
    /* Process the packaged JSON and return the last byte used */
392
0
    process_pack(ctx, tag, pack, out_size);
393
0
    flb_free(pack);
394
395
0
    return 0;
396
0
}
397
398
static ssize_t parse_payload_urlencoded(struct flb_http *ctx, flb_sds_t tag,
399
                                  char *payload, size_t size)
400
0
{
401
0
    struct mk_list *kvs;
402
0
    struct mk_list *head = NULL;
403
0
    struct flb_split_entry *cur = NULL;
404
0
    char **keys = NULL;
405
0
    char **vals = NULL;
406
0
    char *sep;
407
0
    char *start;
408
0
    int idx = 0;
409
0
    int ret = -1;
410
0
    msgpack_packer pck;
411
0
    msgpack_sbuffer sbuf;
412
413
414
    /* initialize buffers */
415
0
    msgpack_sbuffer_init(&sbuf);
416
0
    msgpack_packer_init(&pck, &sbuf, msgpack_sbuffer_write);
417
418
0
    kvs = flb_utils_split(payload, '&', -1 );
419
0
    if (kvs == NULL) {
420
0
        goto split_error;
421
0
    }
422
423
0
    keys = flb_calloc(mk_list_size(kvs), sizeof(char *));
424
0
    if (keys == NULL) {
425
0
        goto keys_calloc_error;
426
0
    }
427
428
0
    vals = flb_calloc(mk_list_size(kvs), sizeof(char *));
429
0
    if (vals == NULL) {
430
0
        goto vals_calloc_error;
431
0
    }
432
433
0
    mk_list_foreach(head, kvs) {
434
0
        cur = mk_list_entry(head, struct flb_split_entry, _head);
435
0
        if (cur->value[0] == '\n') {
436
0
            start = &cur->value[1];
437
0
        } else {
438
0
            start = cur->value;
439
0
        }
440
0
        sep = strchr(start, '=');
441
0
        if (sep == NULL) {
442
0
            vals[idx] = NULL;
443
0
            continue;
444
0
        }
445
0
        *sep++ = '\0';
446
447
0
        keys[idx] = flb_sds_create_len(start, strlen(start));
448
0
        vals[idx] = flb_sds_create_len(sep, strlen(sep));
449
450
0
        flb_sds_trim(keys[idx]);
451
0
        flb_sds_trim(vals[idx]);
452
0
        idx++;
453
0
    }
454
455
0
    msgpack_pack_map(&pck, mk_list_size(kvs));
456
0
    for (idx = 0; idx < mk_list_size(kvs); idx++) {
457
0
        msgpack_pack_str(&pck, flb_sds_len(keys[idx]));
458
0
        msgpack_pack_str_body(&pck, keys[idx], flb_sds_len(keys[idx]));
459
460
0
        if (sds_uri_decode(vals[idx]) != 0) {
461
0
            goto decode_error;
462
0
        } else {
463
0
            msgpack_pack_str(&pck, flb_sds_len(vals[idx]));
464
0
            msgpack_pack_str_body(&pck, vals[idx], strlen(vals[idx]));            
465
0
        }
466
0
    }
467
468
0
    ret = process_pack(ctx, tag, sbuf.data, sbuf.size);
469
470
0
decode_error:
471
0
    for (idx = 0; idx < mk_list_size(kvs); idx++) {
472
0
        if (keys[idx]) {
473
0
            flb_sds_destroy(keys[idx]);
474
0
        }
475
0
        if (vals[idx]) {
476
0
            flb_sds_destroy(vals[idx]);
477
0
        }
478
0
    }
479
0
    flb_free(vals);
480
0
vals_calloc_error:
481
0
    flb_free(keys);
482
0
keys_calloc_error:
483
0
    flb_utils_split_free(kvs);
484
0
split_error:
485
0
    msgpack_sbuffer_destroy(&sbuf);
486
0
    return ret;
487
0
}
488
489
static int process_payload(struct flb_http *ctx, struct http_conn *conn,
490
                           flb_sds_t tag,
491
                           struct mk_http_session *session,
492
                           struct mk_http_request *request)
493
0
{
494
0
    int type = -1;
495
0
    struct mk_http_header *header;
496
497
0
    header = &session->parser.headers[MK_HEADER_CONTENT_TYPE];
498
0
    if (header->key.data == NULL) {
499
0
        send_response(conn, 400, "error: header 'Content-Type' is not set\n");
500
0
        return -1;
501
0
    }
502
503
0
    if (header->val.len == 16 &&
504
0
        strncasecmp(header->val.data, "application/json", 16) == 0) {
505
0
        type = HTTP_CONTENT_JSON;
506
0
    }
507
508
0
    if (header->val.len == 33 &&
509
0
        strncasecmp(header->val.data, "application/x-www-form-urlencoded", 33) == 0) {
510
0
        type = HTTP_CONTENT_URLENCODED;
511
0
    }
512
513
0
    if (type == -1) {
514
0
        send_response(conn, 400, "error: invalid 'Content-Type'\n");
515
0
        return -1;
516
0
    }
517
518
0
    if (request->data.len <= 0) {
519
0
        send_response(conn, 400, "error: no payload found\n");
520
0
        return -1;
521
0
    }
522
523
0
    if (type == HTTP_CONTENT_JSON) {
524
0
        parse_payload_json(ctx, tag, request->data.data, request->data.len);
525
0
    } else if (type == HTTP_CONTENT_URLENCODED) {
526
0
        parse_payload_urlencoded(ctx, tag, request->data.data, request->data.len);
527
0
    }
528
529
0
    return 0;
530
0
}
531
532
static inline int mk_http_point_header(mk_ptr_t *h,
533
                                       struct mk_http_parser *parser, int key)
534
0
{
535
0
    struct mk_http_header *header;
536
537
0
    header = &parser->headers[key];
538
0
    if (header->type == key) {
539
0
        h->data = header->val.data;
540
0
        h->len  = header->val.len;
541
0
        return 0;
542
0
    }
543
0
    else {
544
0
        h->data = NULL;
545
0
        h->len  = -1;
546
0
    }
547
548
0
    return -1;
549
0
}
550
551
/*
552
 * Handle an incoming request. It perform extra checks over the request, if
553
 * everything is OK, it enqueue the incoming payload.
554
 */
555
int http_prot_handle(struct flb_http *ctx, struct http_conn *conn,
556
                     struct mk_http_session *session,
557
                     struct mk_http_request *request)
558
0
{
559
0
    int i;
560
0
    int ret;
561
0
    int len;
562
0
    char *uri;
563
0
    char *qs;
564
0
    off_t diff;
565
0
    flb_sds_t tag;
566
0
    struct mk_http_header *header;
567
568
0
    if (request->uri.data[0] != '/') {
569
0
        send_response(conn, 400, "error: invalid request\n");
570
0
        return -1;
571
0
    }
572
573
    /* Decode URI */
574
0
    uri = mk_utils_url_decode(request->uri);
575
0
    if (!uri) {
576
0
        uri = mk_mem_alloc_z(request->uri.len + 1);
577
0
        if (!uri) {
578
0
            return -1;
579
0
        }
580
0
        memcpy(uri, request->uri.data, request->uri.len);
581
0
        uri[request->uri.len] = '\0';
582
0
    }
583
584
    /* Try to match a query string so we can remove it */
585
0
    qs = strchr(uri, '?');
586
0
    if (qs) {
587
        /* remove the query string part */
588
0
        diff = qs - uri;
589
0
        uri[diff] = '\0';
590
0
    }
591
592
    /* Compose the query string using the URI */
593
0
    len = strlen(uri);
594
595
0
    if (len == 1) {
596
0
        tag = NULL; /* use default tag */
597
0
    }
598
0
    else {
599
0
        tag = flb_sds_create_size(len);
600
0
        if (!tag) {
601
0
            mk_mem_free(uri);
602
0
            return -1;
603
0
        }
604
605
        /* New tag skipping the URI '/' */
606
0
        flb_sds_cat(tag, uri + 1, len - 1);
607
608
        /* Sanitize, only allow alphanum chars */
609
0
        for (i = 0; i < flb_sds_len(tag); i++) {
610
0
            if (!isalnum(tag[i]) && tag[i] != '_' && tag[i] != '.') {
611
0
                tag[i] = '_';
612
0
            }
613
0
        }
614
0
    }
615
616
0
    mk_mem_free(uri);
617
618
    /* Check if we have a Host header: Hostname ; port */
619
0
    mk_http_point_header(&request->host, &session->parser, MK_HEADER_HOST);
620
621
    /* Header: Connection */
622
0
    mk_http_point_header(&request->connection, &session->parser,
623
0
                         MK_HEADER_CONNECTION);
624
625
    /* HTTP/1.1 needs Host header */
626
0
    if (!request->host.data && request->protocol == MK_HTTP_PROTOCOL_11) {
627
0
        flb_sds_destroy(tag);
628
0
        return -1;
629
0
    }
630
631
    /* Should we close the session after this request ? */
632
0
    mk_http_keepalive_check(session, request, ctx->server);
633
634
    /* Content Length */
635
0
    header = &session->parser.headers[MK_HEADER_CONTENT_LENGTH];
636
0
    if (header->type == MK_HEADER_CONTENT_LENGTH) {
637
0
        request->_content_length.data = header->val.data;
638
0
        request->_content_length.len  = header->val.len;
639
0
    }
640
0
    else {
641
0
        request->_content_length.data = NULL;
642
0
    }
643
644
0
    if (request->method != MK_METHOD_POST) {
645
0
        flb_sds_destroy(tag);
646
0
        send_response(conn, 400, "error: invalid HTTP method\n");
647
0
        return -1;
648
0
    }
649
650
0
    ret = process_payload(ctx, conn, tag, session, request);
651
0
    flb_sds_destroy(tag);
652
0
    send_response(conn, ctx->successful_response_code, NULL);
653
0
    return ret;
654
0
}
655
656
/*
657
 * Handle an incoming request which has resulted in an http parser error.
658
 */
659
int http_prot_handle_error(struct flb_http *ctx, struct http_conn *conn,
660
                           struct mk_http_session *session,
661
                           struct mk_http_request *request)
662
0
{
663
0
    send_response(conn, 400, "error: invalid request\n");
664
0
    return -1;
665
0
}