Coverage Report

Created: 2025-09-04 07:51

/src/fluent-bit/lib/monkey/mk_server/mk_header.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
/*  Monkey HTTP Server
4
 *  ==================
5
 *  Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
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 <monkey/monkey.h>
21
#include <monkey/mk_server.h>
22
#include <monkey/mk_header.h>
23
#include <monkey/mk_core.h>
24
#include <monkey/mk_http_status.h>
25
#include <monkey/mk_config.h>
26
#include <monkey/mk_socket.h>
27
#include <monkey/mk_utils.h>
28
#include <monkey/mk_clock.h>
29
#include <monkey/mk_cache.h>
30
#include <monkey/mk_http.h>
31
#include <monkey/mk_vhost.h>
32
#include <monkey/mk_tls.h>
33
34
#define MK_HEADER_SHORT_DATE       "Date: "
35
#define MK_HEADER_SHORT_LOCATION   "Location: "
36
#define MK_HEADER_SHORT_CT         "Content-Type: "
37
#define MK_HEADER_ACCEPT_RANGES    "Accept-Ranges: bytes" MK_CRLF
38
#define MK_HEADER_ALLOWED_METHODS  "Allow: "
39
#define MK_HEADER_CONN_KA          "Connection: Keep-Alive" MK_CRLF
40
#define MK_HEADER_CONN_CLOSE       "Connection: Close" MK_CRLF
41
#define MK_HEADER_CONN_UPGRADE     "Connection: Upgrade" MK_CRLF
42
#define MK_HEADER_CONTENT_LENGTH   "Content-Length: "
43
#define MK_HEADER_CONTENT_ENCODING "Content-Encoding: "
44
#define MK_HEADER_TE_CHUNKED       "Transfer-Encoding: chunked" MK_CRLF
45
#define MK_HEADER_LAST_MODIFIED    "Last-Modified: "
46
#define MK_HEADER_UPGRADE_H2C      "Upgrade: h2c" MK_CRLF
47
48
const mk_ptr_t mk_header_short_date = mk_ptr_init(MK_HEADER_SHORT_DATE);
49
const mk_ptr_t mk_header_short_location = mk_ptr_init(MK_HEADER_SHORT_LOCATION);
50
const mk_ptr_t mk_header_short_ct = mk_ptr_init(MK_HEADER_SHORT_CT);
51
const mk_ptr_t mk_header_allow = mk_ptr_init(MK_HEADER_ALLOWED_METHODS);
52
53
const mk_ptr_t mk_header_conn_ka = mk_ptr_init(MK_HEADER_CONN_KA);
54
const mk_ptr_t mk_header_conn_close = mk_ptr_init(MK_HEADER_CONN_CLOSE);
55
const mk_ptr_t mk_header_conn_upgrade = mk_ptr_init(MK_HEADER_CONN_UPGRADE);
56
const mk_ptr_t mk_header_content_length = mk_ptr_init(MK_HEADER_CONTENT_LENGTH);
57
const mk_ptr_t mk_header_content_encoding = mk_ptr_init(MK_HEADER_CONTENT_ENCODING);
58
const mk_ptr_t mk_header_accept_ranges = mk_ptr_init(MK_HEADER_ACCEPT_RANGES);
59
const mk_ptr_t mk_header_te_chunked = mk_ptr_init(MK_HEADER_TE_CHUNKED);
60
const mk_ptr_t mk_header_last_modified = mk_ptr_init(MK_HEADER_LAST_MODIFIED);
61
const mk_ptr_t mk_header_upgrade_h2c = mk_ptr_init(MK_HEADER_UPGRADE_H2C);
62
63
#define status_entry(num, str) {num, sizeof(str) - 1, str}
64
65
static const struct header_status_response status_response[] = {
66
67
    /*
68
     * The most used first:
69
     *
70
     *  - HTTP/1.1 200 OK
71
     *  - HTTP/1.1 404 Not Found
72
     */
73
    status_entry(MK_HTTP_OK, MK_RH_HTTP_OK),
74
    status_entry(MK_CLIENT_NOT_FOUND, MK_RH_CLIENT_NOT_FOUND),
75
76
    /* Informational */
77
    status_entry(MK_INFO_CONTINUE, MK_RH_INFO_CONTINUE),
78
    status_entry(MK_INFO_SWITCH_PROTOCOL, MK_RH_INFO_SWITCH_PROTOCOL),
79
80
    /* Successful */
81
    status_entry(MK_HTTP_CREATED, MK_RH_HTTP_CREATED),
82
    status_entry(MK_HTTP_ACCEPTED, MK_RH_HTTP_ACCEPTED),
83
    status_entry(MK_HTTP_NON_AUTH_INFO, MK_RH_HTTP_NON_AUTH_INFO),
84
    status_entry(MK_HTTP_NOCONTENT, MK_RH_HTTP_NOCONTENT),
85
    status_entry(MK_HTTP_RESET, MK_RH_HTTP_RESET),
86
    status_entry(MK_HTTP_PARTIAL, MK_RH_HTTP_PARTIAL),
87
88
    /* Redirections */
89
    status_entry(MK_REDIR_MULTIPLE, MK_RH_REDIR_MULTIPLE),
90
    status_entry(MK_REDIR_MOVED, MK_RH_REDIR_MOVED),
91
    status_entry(MK_REDIR_MOVED_T, MK_RH_REDIR_MOVED_T),
92
    status_entry(MK_REDIR_SEE_OTHER, MK_RH_REDIR_SEE_OTHER),
93
    status_entry(MK_NOT_MODIFIED, MK_RH_NOT_MODIFIED),
94
    status_entry(MK_REDIR_USE_PROXY, MK_RH_REDIR_USE_PROXY),
95
96
    /* Client side errors */
97
    status_entry(MK_CLIENT_BAD_REQUEST, MK_RH_CLIENT_BAD_REQUEST),
98
    status_entry(MK_CLIENT_UNAUTH, MK_RH_CLIENT_UNAUTH),
99
    status_entry(MK_CLIENT_PAYMENT_REQ, MK_RH_CLIENT_PAYMENT_REQ),
100
    status_entry(MK_CLIENT_FORBIDDEN, MK_RH_CLIENT_FORBIDDEN),
101
    status_entry(MK_CLIENT_METHOD_NOT_ALLOWED, MK_RH_CLIENT_METHOD_NOT_ALLOWED),
102
    status_entry(MK_CLIENT_NOT_ACCEPTABLE, MK_RH_CLIENT_NOT_ACCEPTABLE),
103
    status_entry(MK_CLIENT_PROXY_AUTH, MK_RH_CLIENT_PROXY_AUTH),
104
    status_entry(MK_CLIENT_REQUEST_TIMEOUT, MK_RH_CLIENT_REQUEST_TIMEOUT),
105
    status_entry(MK_CLIENT_CONFLICT, MK_RH_CLIENT_CONFLICT),
106
    status_entry(MK_CLIENT_GONE, MK_RH_CLIENT_GONE),
107
    status_entry(MK_CLIENT_LENGTH_REQUIRED, MK_RH_CLIENT_LENGTH_REQUIRED),
108
    status_entry(MK_CLIENT_PRECOND_FAILED, MK_RH_CLIENT_PRECOND_FAILED),
109
    status_entry(MK_CLIENT_REQUEST_ENTITY_TOO_LARGE,
110
                 MK_RH_CLIENT_REQUEST_ENTITY_TOO_LARGE),
111
    status_entry(MK_CLIENT_REQUEST_URI_TOO_LONG,
112
                 MK_RH_CLIENT_REQUEST_URI_TOO_LONG),
113
    status_entry(MK_CLIENT_UNSUPPORTED_MEDIA, MK_RH_CLIENT_UNSUPPORTED_MEDIA),
114
    status_entry(MK_CLIENT_REQUESTED_RANGE_NOT_SATISF,
115
                 MK_RH_CLIENT_REQUESTED_RANGE_NOT_SATISF),
116
117
    /* Server side errors */
118
    status_entry(MK_SERVER_INTERNAL_ERROR, MK_RH_SERVER_INTERNAL_ERROR),
119
    status_entry(MK_SERVER_NOT_IMPLEMENTED, MK_RH_SERVER_NOT_IMPLEMENTED),
120
    status_entry(MK_SERVER_BAD_GATEWAY, MK_RH_SERVER_BAD_GATEWAY),
121
    status_entry(MK_SERVER_SERVICE_UNAV, MK_RH_SERVER_SERVICE_UNAV),
122
    status_entry(MK_SERVER_GATEWAY_TIMEOUT, MK_RH_SERVER_GATEWAY_TIMEOUT),
123
    status_entry(MK_SERVER_HTTP_VERSION_UNSUP, MK_RH_SERVER_HTTP_VERSION_UNSUP)
124
};
125
126
static const int status_response_len =
127
    (sizeof(status_response)/(sizeof(status_response[0])));
128
129
static void mk_header_cb_finished(struct mk_stream_input *in)
130
0
{
131
0
    struct mk_iov *iov = in->buffer;
132
133
0
    mk_iov_free_marked(iov);
134
135
#if defined(__APPLE__)
136
        /*
137
         * Disable TCP_CORK right away, according to:
138
         *
139
         *  ---
140
         *  commit 81e8b869d70f9da93ddfbfb17ec7f12ce3c28fc6
141
         *  Author: Sonny Karlsson <ksonny@lotrax.org>
142
         *  Date:   Sat Oct 18 12:11:49 2014 +0200
143
         *
144
         *  http: Remove cork before first call to sendfile().
145
         *
146
         *  This removes a large delay on Mac OS X when headers and file content
147
         *  does not fill a single frame.
148
         *  Deactivating TCP_NOPUSH does not cause pending frames to be sent until
149
         *  the next write operation.
150
         *  ---
151
         */
152
153
    mk_server_cork_flag(in->stream->channel->fd, TCP_CORK_OFF);
154
#endif
155
0
}
156
157
static void cb_stream_iov_extended_free(struct mk_stream_input *in)
158
0
{
159
0
    struct mk_iov *iov;
160
161
0
    iov = in->buffer;
162
0
    mk_iov_free(iov);
163
0
}
164
165
/* Send response headers */
166
int mk_header_prepare(struct mk_http_session *cs, struct mk_http_request *sr,
167
                      struct mk_server *server)
168
0
{
169
0
    int i = 0;
170
0
    unsigned long len = 0;
171
0
    char *buffer = 0;
172
0
    mk_ptr_t response;
173
0
    struct response_headers *sh;
174
0
    struct mk_iov *iov;
175
176
0
    sh = &sr->headers;
177
0
    iov = &sh->headers_iov;
178
179
    /* HTTP Status Code */
180
0
    if (sh->status == MK_CUSTOM_STATUS) {
181
0
        response.data = sh->custom_status.data;
182
0
        response.len = sh->custom_status.len;
183
0
    }
184
0
    else {
185
0
        for (i = 0; i < status_response_len; i++) {
186
0
            if (status_response[i].status == sh->status) {
187
0
                response.data = status_response[i].response;
188
0
                response.len  = status_response[i].length;
189
0
                break;
190
0
            }
191
0
        }
192
0
    }
193
194
    /* Invalid status set */
195
0
    mk_bug(i == status_response_len);
196
197
0
    mk_iov_add(iov, response.data, response.len, MK_FALSE);
198
199
    /*
200
     * Preset headers (mk_clock.c):
201
     *
202
     * - Server
203
     * - Date
204
     */
205
0
    mk_iov_add(iov,
206
0
               server->clock_context->headers_preset.data,
207
0
               server->clock_context->headers_preset.len,
208
0
               MK_FALSE);
209
210
    /* Last-Modified */
211
0
    if (sh->last_modified > 0) {
212
0
        mk_ptr_t *lm = MK_TLS_GET(mk_tls_cache_header_lm);
213
0
        lm->len = mk_utils_utime2gmt(&lm->data, sh->last_modified);
214
215
0
        mk_iov_add(iov,
216
0
                   mk_header_last_modified.data,
217
0
                   mk_header_last_modified.len,
218
0
                   MK_FALSE);
219
0
        mk_iov_add(iov,
220
0
                   lm->data,
221
0
                   lm->len,
222
0
                   MK_FALSE);
223
0
    }
224
225
    /* Connection */
226
0
    if (sh->connection == 0) {
227
0
        if (cs->close_now == MK_FALSE) {
228
0
            if (sr->connection.len > 0) {
229
0
                if (sr->protocol != MK_HTTP_PROTOCOL_11) {
230
0
                    mk_iov_add(iov,
231
0
                               mk_header_conn_ka.data,
232
0
                               mk_header_conn_ka.len,
233
0
                               MK_FALSE);
234
0
                }
235
0
            }
236
0
        }
237
0
        else {
238
0
            mk_iov_add(iov,
239
0
                       mk_header_conn_close.data,
240
0
                       mk_header_conn_close.len,
241
0
                       MK_FALSE);
242
0
        }
243
0
    }
244
0
    else if (sh->connection == MK_HEADER_CONN_UPGRADED) {
245
0
             mk_iov_add(iov,
246
0
                        mk_header_conn_upgrade.data,
247
0
                        mk_header_conn_upgrade.len,
248
0
                        MK_FALSE);
249
0
    }
250
251
    /* Location */
252
0
    if (sh->location != NULL) {
253
0
        mk_iov_add(iov,
254
0
                   mk_header_short_location.data,
255
0
                   mk_header_short_location.len,
256
0
                   MK_FALSE);
257
258
0
        mk_iov_add(iov,
259
0
                   sh->location,
260
0
                   strlen(sh->location),
261
0
                   MK_TRUE);
262
0
    }
263
264
    /* allowed methods */
265
0
    if (sh->allow_methods.len > 0) {
266
0
        mk_iov_add(iov,
267
0
                   mk_header_allow.data,
268
0
                   mk_header_allow.len,
269
0
                   MK_FALSE);
270
0
        mk_iov_add(iov,
271
0
                   sh->allow_methods.data,
272
0
                   sh->allow_methods.len,
273
0
                   MK_FALSE);
274
0
    }
275
276
    /* Content type */
277
0
    if (sh->content_type.len > 0) {
278
0
        mk_iov_add(iov,
279
0
                   sh->content_type.data,
280
0
                   sh->content_type.len,
281
0
                   MK_FALSE);
282
0
    }
283
284
    /*
285
     * Transfer Encoding: the transfer encoding header is just sent when
286
     * the response has some content defined by the HTTP status response
287
     */
288
0
    switch (sh->transfer_encoding) {
289
0
    case MK_HEADER_TE_TYPE_CHUNKED:
290
0
        mk_iov_add(iov,
291
0
                   mk_header_te_chunked.data,
292
0
                   mk_header_te_chunked.len,
293
0
                   MK_FALSE);
294
0
        break;
295
0
    }
296
297
    /* E-Tag */
298
0
    if (sh->etag_len > 0) {
299
0
        mk_iov_add(iov, sh->etag_buf, sh->etag_len, MK_FALSE);
300
0
    }
301
302
    /* Content-Encoding */
303
0
    if (sh->content_encoding.len > 0) {
304
0
        mk_iov_add(iov, mk_header_content_encoding.data,
305
0
                   mk_header_content_encoding.len,
306
0
                   MK_FALSE);
307
0
        mk_iov_add(iov, sh->content_encoding.data,
308
0
                   sh->content_encoding.len,
309
0
                   MK_FALSE);
310
0
    }
311
312
    /* Content-Length */
313
0
    if (sh->content_length >= 0 && sh->transfer_encoding != 0) {
314
        /* Map content length to MK_POINTER */
315
0
        mk_ptr_t *cl = MK_TLS_GET(mk_tls_cache_header_cl);
316
0
        mk_string_itop(sh->content_length, cl);
317
318
        /* Set headers */
319
0
        mk_iov_add(iov,
320
0
                   mk_header_content_length.data,
321
0
                   mk_header_content_length.len,
322
0
                   MK_FALSE);
323
0
        mk_iov_add(iov,
324
0
                   cl->data,
325
0
                   cl->len,
326
0
                   MK_FALSE);
327
0
    }
328
329
0
    if ((sh->content_length != 0 && (sh->ranges[0] >= 0 || sh->ranges[1] >= 0)) &&
330
0
        server->resume == MK_TRUE) {
331
0
        buffer = 0;
332
333
        /* yyy- */
334
0
        if (sh->ranges[0] >= 0 && sh->ranges[1] == -1) {
335
0
            mk_string_build(&buffer,
336
0
                            &len,
337
0
                            "%s bytes %d-%ld/%ld\r\n",
338
0
                            RH_CONTENT_RANGE,
339
0
                            sh->ranges[0],
340
0
                            (sh->real_length - 1), sh->real_length);
341
0
            mk_iov_add(iov, buffer, len, MK_TRUE);
342
0
        }
343
344
        /* yyy-xxx */
345
0
        if (sh->ranges[0] >= 0 && sh->ranges[1] >= 0) {
346
0
            mk_string_build(&buffer,
347
0
                            &len,
348
0
                            "%s bytes %d-%d/%ld\r\n",
349
0
                            RH_CONTENT_RANGE,
350
0
                            sh->ranges[0], sh->ranges[1], sh->real_length);
351
352
0
            mk_iov_add(iov, buffer, len, MK_TRUE);
353
0
        }
354
355
        /* -xxx */
356
0
        if (sh->ranges[0] == -1 && sh->ranges[1] > 0) {
357
0
            mk_string_build(&buffer,
358
0
                            &len,
359
0
                            "%s bytes %ld-%ld/%ld\r\n",
360
0
                            RH_CONTENT_RANGE,
361
0
                            (sh->real_length - sh->ranges[1]),
362
0
                            (sh->real_length - 1), sh->real_length);
363
0
            mk_iov_add(iov, buffer, len, MK_TRUE);
364
0
        }
365
0
    }
366
367
0
    if (sh->upgrade == MK_HEADER_UPGRADED_H2C) {
368
0
        mk_iov_add(iov, mk_header_upgrade_h2c.data, mk_header_upgrade_h2c.len,
369
0
                   MK_FALSE);
370
0
    }
371
372
373
0
    if (sh->cgi == SH_NOCGI || sh->breakline == MK_HEADER_BREAKLINE) {
374
0
        if (!sr->headers._extra_rows) {
375
0
            mk_iov_add(iov, mk_iov_crlf.data, mk_iov_crlf.len,
376
0
                       MK_FALSE);
377
0
        }
378
0
        else {
379
0
            mk_iov_add(sr->headers._extra_rows, mk_iov_crlf.data,
380
0
                       mk_iov_crlf.len, MK_FALSE);
381
0
        }
382
0
    }
383
384
    /*
385
     * Configure the Stream to dispatch the headers
386
     */
387
388
    /* Set the IOV input stream */
389
0
    sr->in_headers.buffer      = iov;
390
0
    sr->in_headers.bytes_total = iov->total_len;
391
0
    sr->in_headers.cb_finished = mk_header_cb_finished;
392
393
0
    if (sr->headers._extra_rows) {
394
        /* Our main sr->stream contains the main headers (header_iov)
395
         * and 'may' have already some linked data. If we have some
396
         * extra headers rows we need to link this IOV right after
397
         * the main header_iov.
398
         */
399
0
        struct mk_stream_input *in = &sr->in_headers_extra;
400
0
        in->type        = MK_STREAM_IOV;
401
0
        in->dynamic     = MK_FALSE;
402
0
        in->cb_consumed = NULL;
403
0
        in->cb_finished = cb_stream_iov_extended_free;
404
0
        in->stream      = &sr->stream;
405
0
        in->buffer      = sr->headers._extra_rows;
406
0
        in->bytes_total = sr->headers._extra_rows->total_len;
407
408
0
        mk_list_add_after(&sr->in_headers_extra._head,
409
0
                          &sr->in_headers._head,
410
0
                          &sr->stream.inputs);
411
0
    }
412
413
0
    sh->sent = MK_TRUE;
414
415
0
    return 0;
416
0
}
417
418
void mk_header_set_http_status(struct mk_http_request *sr, int status)
419
0
{
420
0
    mk_bug(!sr);
421
0
    sr->headers.status = status;
422
423
0
    MK_TRACE("Set HTTP status = %i", status);
424
0
}
425
426
void mk_header_response_reset(struct response_headers *header)
427
0
{
428
0
    struct mk_iov *iov;
429
430
0
    header->status = -1;
431
0
    header->sent = MK_FALSE;
432
0
    header->ranges[0] = -1;
433
0
    header->ranges[1] = -1;
434
0
    header->content_length = -1;
435
0
    header->connection = 0;
436
0
    header->transfer_encoding = -1;
437
0
    header->last_modified = -1;
438
0
    header->upgrade = -1;
439
0
    header->cgi = SH_NOCGI;
440
0
    mk_ptr_reset(&header->content_type);
441
0
    mk_ptr_reset(&header->content_encoding);
442
0
    header->location = NULL;
443
0
    header->_extra_rows = NULL;
444
0
    header->allow_methods.len = 0;
445
446
    /* Initialize headers IOV */
447
0
    iov = &header->headers_iov;
448
0
    iov->io          = (struct iovec *) &header->__iov_io;
449
0
    iov->buf_to_free = (void *) &header->__iov_buf;
450
0
    mk_iov_init(&header->headers_iov, MK_HEADER_IOV, 0);
451
0
}