Coverage Report

Created: 2025-11-11 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/civetweb/src/response.inl
Line
Count
Source
1
/* response.inl
2
 *
3
 * Bufferring for HTTP headers for HTTP response.
4
 * This function are only intended to be used at the server side.
5
 * Optional for HTTP/1.0 and HTTP/1.1, mandatory for HTTP/2.
6
 *
7
 * This file is part of the CivetWeb project.
8
 */
9
10
#if defined(NO_RESPONSE_BUFFERING) && defined(USE_HTTP2)
11
#error "HTTP2 works only if NO_RESPONSE_BUFFERING is not set"
12
#endif
13
14
15
/* Internal function to free header list */
16
static void
17
free_buffered_response_header_list(struct mg_connection *conn)
18
0
{
19
0
#if !defined(NO_RESPONSE_BUFFERING)
20
0
  while (conn->response_info.num_headers > 0) {
21
0
    conn->response_info.num_headers--;
22
0
    mg_free((void *)conn->response_info
23
0
                .http_headers[conn->response_info.num_headers]
24
0
                .name);
25
0
    conn->response_info.http_headers[conn->response_info.num_headers].name =
26
0
        0;
27
0
    mg_free((void *)conn->response_info
28
0
                .http_headers[conn->response_info.num_headers]
29
0
                .value);
30
0
    conn->response_info.http_headers[conn->response_info.num_headers]
31
0
        .value = 0;
32
0
  }
33
#else
34
  (void)conn; /* Nothing to do */
35
#endif
36
0
}
37
38
39
/* Send first line of HTTP/1.x response */
40
static int
41
send_http1_response_status_line(struct mg_connection *conn)
42
0
{
43
0
  const char *status_txt;
44
0
  const char *http_version = conn->request_info.http_version;
45
0
  int status_code = conn->status_code;
46
47
0
  if ((status_code < 100) || (status_code > 999)) {
48
    /* Set invalid status code to "500 Internal Server Error" */
49
0
    status_code = 500;
50
0
  }
51
0
  if (!http_version) {
52
0
    http_version = "1.0";
53
0
  }
54
55
  /* mg_get_response_code_text will never return NULL */
56
0
  status_txt = mg_get_response_code_text(conn, conn->status_code);
57
58
0
  if (mg_printf(
59
0
          conn, "HTTP/%s %i %s\r\n", http_version, status_code, status_txt)
60
0
      < 10) {
61
    /* Network sending failed */
62
0
    return 0;
63
0
  }
64
0
  return 1;
65
0
}
66
67
68
/* Initialize a new HTTP response
69
 * Parameters:
70
 *   conn: Current connection handle.
71
 *   status: HTTP status code (e.g., 200 for "OK").
72
 * Return:
73
 *   0:    ok
74
 *  -1:    parameter error
75
 *  -2:    invalid connection type
76
 *  -3:    invalid connection status
77
 *  -4:    network error (only if built with NO_RESPONSE_BUFFERING)
78
 */
79
int
80
mg_response_header_start(struct mg_connection *conn, int status)
81
0
{
82
0
  int ret = 0;
83
0
  if ((conn == NULL) || (status < 100) || (status > 999)) {
84
    /* Parameter error */
85
0
    return -1;
86
0
  }
87
0
  if ((conn->connection_type != CONNECTION_TYPE_REQUEST)
88
0
      || (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) {
89
    /* Only allowed in server context */
90
0
    return -2;
91
0
  }
92
0
  if (conn->request_state != 0) {
93
    /* only allowed if nothing was sent up to now */
94
0
    return -3;
95
0
  }
96
0
  conn->status_code = status;
97
0
  conn->request_state = 1;
98
99
  /* Buffered response is stored, unbuffered response will be sent directly,
100
   * but we can only send HTTP/1.x response here */
101
0
#if !defined(NO_RESPONSE_BUFFERING)
102
0
  free_buffered_response_header_list(conn);
103
#else
104
  if (!send_http1_response_status_line(conn)) {
105
    ret = -4;
106
  };
107
  conn->request_state = 1; /* Reset from 10 to 1 */
108
#endif
109
110
0
  return ret;
111
0
}
112
113
114
/* Add a new HTTP response header line
115
 * Parameters:
116
 *   conn: Current connection handle.
117
 *   header: Header name.
118
 *   value: Header value.
119
 *   value_len: Length of header value, excluding the terminating zero.
120
 *              Use -1 for "strlen(value)".
121
 * Return:
122
 *    0:    ok
123
 *   -1:    parameter error
124
 *   -2:    invalid connection type
125
 *   -3:    invalid connection status
126
 *   -4:    too many headers
127
 *   -5:    out of memory
128
 */
129
int
130
mg_response_header_add(struct mg_connection *conn,
131
                       const char *header,
132
                       const char *value,
133
                       int value_len)
134
0
{
135
0
#if !defined(NO_RESPONSE_BUFFERING)
136
0
  int hidx;
137
0
#endif
138
139
0
  if ((conn == NULL) || (header == NULL) || (value == NULL)) {
140
    /* Parameter error */
141
0
    return -1;
142
0
  }
143
0
  if ((conn->connection_type != CONNECTION_TYPE_REQUEST)
144
0
      || (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) {
145
    /* Only allowed in server context */
146
0
    return -2;
147
0
  }
148
0
  if (conn->request_state != 1) {
149
    /* only allowed if mg_response_header_start has been called before */
150
0
    return -3;
151
0
  }
152
153
0
#if !defined(NO_RESPONSE_BUFFERING)
154
0
  hidx = conn->response_info.num_headers;
155
0
  if (hidx >= MG_MAX_HEADERS) {
156
    /* Too many headers */
157
0
    return -4;
158
0
  }
159
160
  /* Alloc new element */
161
0
  conn->response_info.http_headers[hidx].name =
162
0
      mg_strdup_ctx(header, conn->phys_ctx);
163
0
  if (value_len >= 0) {
164
0
    char *hbuf =
165
0
        (char *)mg_malloc_ctx((unsigned)value_len + 1, conn->phys_ctx);
166
0
    if (hbuf) {
167
0
      memcpy(hbuf, value, (unsigned)value_len);
168
0
      hbuf[value_len] = 0;
169
0
    }
170
0
    conn->response_info.http_headers[hidx].value = hbuf;
171
0
  } else {
172
0
    conn->response_info.http_headers[hidx].value =
173
0
        mg_strdup_ctx(value, conn->phys_ctx);
174
0
  }
175
176
0
  if ((conn->response_info.http_headers[hidx].name == 0)
177
0
      || (conn->response_info.http_headers[hidx].value == 0)) {
178
    /* Out of memory */
179
0
    mg_free((void *)conn->response_info.http_headers[hidx].name);
180
0
    conn->response_info.http_headers[hidx].name = 0;
181
0
    mg_free((void *)conn->response_info.http_headers[hidx].value);
182
0
    conn->response_info.http_headers[hidx].value = 0;
183
0
    return -5;
184
0
  }
185
186
  /* OK, header stored */
187
0
  conn->response_info.num_headers++;
188
189
#else
190
  if (value_len >= 0) {
191
    mg_printf(conn, "%s: %.*s\r\n", header, (int)value_len, value);
192
  } else {
193
    mg_printf(conn, "%s: %s\r\n", header, value);
194
  }
195
  conn->request_state = 1; /* Reset from 10 to 1 */
196
#endif
197
198
0
  return 0;
199
0
}
200
201
202
/* forward */
203
static int parse_http_headers(char **buf, struct mg_header hdr[MG_MAX_HEADERS]);
204
205
206
/* Add a complete header string (key + value).
207
 * Parameters:
208
 *   conn: Current connection handle.
209
 *   http1_headers: Header line(s) in the form "name: value".
210
 * Return:
211
 *  >=0:   no error, number of header lines added
212
 *  -1:    parameter error
213
 *  -2:    invalid connection type
214
 *  -3:    invalid connection status
215
 *  -4:    too many headers
216
 *  -5:    out of memory
217
 */
218
int
219
mg_response_header_add_lines(struct mg_connection *conn,
220
                             const char *http1_headers)
221
0
{
222
0
  struct mg_header add_hdr[MG_MAX_HEADERS];
223
0
  int num_hdr, i, ret;
224
0
  char *workbuffer, *parse;
225
226
  /* We need to work on a copy of the work buffer, sice parse_http_headers
227
   * will modify */
228
0
  workbuffer = mg_strdup_ctx(http1_headers, conn->phys_ctx);
229
0
  if (!workbuffer) {
230
    /* Out of memory */
231
0
    return -5;
232
0
  }
233
234
  /* Call existing method to split header buffer */
235
0
  parse = workbuffer;
236
0
  num_hdr = parse_http_headers(&parse, add_hdr);
237
0
  ret = num_hdr;
238
239
0
  for (i = 0; i < num_hdr; i++) {
240
0
    int lret =
241
0
        mg_response_header_add(conn, add_hdr[i].name, add_hdr[i].value, -1);
242
0
    if ((ret > 0) && (lret < 0)) {
243
      /* Store error return value */
244
0
      ret = lret;
245
0
    }
246
0
  }
247
248
  /* mg_response_header_add created a copy, so we can free the original */
249
0
  mg_free(workbuffer);
250
0
  return ret;
251
0
}
252
253
254
#if defined(USE_HTTP2)
255
static int http2_send_response_headers(struct mg_connection *conn);
256
#endif
257
258
259
/* Send http response
260
 * Parameters:
261
 *   conn: Current connection handle.
262
 * Return:
263
 *   0:    ok
264
 *  -1:    parameter error
265
 *  -2:    invalid connection type
266
 *  -3:    invalid connection status
267
 *  -4:    network send failed
268
 */
269
int
270
mg_response_header_send(struct mg_connection *conn)
271
0
{
272
0
#if !defined(NO_RESPONSE_BUFFERING)
273
0
  int i;
274
0
  int has_date = 0;
275
0
  int has_connection = 0;
276
0
#endif
277
278
0
  if (conn == NULL) {
279
    /* Parameter error */
280
0
    return -1;
281
0
  }
282
0
  if ((conn->connection_type != CONNECTION_TYPE_REQUEST)
283
0
      || (conn->protocol_type == PROTOCOL_TYPE_WEBSOCKET)) {
284
    /* Only allowed in server context */
285
0
    return -2;
286
0
  }
287
0
  if (conn->request_state != 1) {
288
    /* only allowed if mg_response_header_start has been called before */
289
0
    return -3;
290
0
  }
291
292
  /* State: 2 */
293
0
  conn->request_state = 2;
294
295
0
#if !defined(NO_RESPONSE_BUFFERING)
296
#if defined(USE_HTTP2)
297
  if (conn->protocol_type == PROTOCOL_TYPE_HTTP2) {
298
    int ret = http2_send_response_headers(conn);
299
    free_buffered_response_header_list(conn);
300
    return (ret ? 0 : -4);
301
  }
302
#endif
303
304
  /* Send */
305
0
  if (!send_http1_response_status_line(conn)) {
306
0
    free_buffered_response_header_list(conn);
307
0
    return -4;
308
0
  };
309
0
  for (i = 0; i < conn->response_info.num_headers; i++) {
310
0
    mg_printf(conn,
311
0
              "%s: %s\r\n",
312
0
              conn->response_info.http_headers[i].name,
313
0
              conn->response_info.http_headers[i].value);
314
315
    /* Check for some special headers */
316
0
    if (!mg_strcasecmp("Date", conn->response_info.http_headers[i].name)) {
317
0
      has_date = 1;
318
0
    }
319
0
    if (!mg_strcasecmp("Connection",
320
0
                       conn->response_info.http_headers[i].name)) {
321
0
      has_connection = 1;
322
0
    }
323
0
  }
324
325
0
  if (!has_date) {
326
0
    time_t curtime = time(NULL);
327
0
    char date[64];
328
0
    gmt_time_string(date, sizeof(date), &curtime);
329
0
    mg_printf(conn, "Date: %s\r\n", date);
330
0
  }
331
0
  if (!has_connection) {
332
0
    mg_printf(conn, "Connection: %s\r\n", suggest_connection_header(conn));
333
0
  }
334
0
#endif
335
336
0
  mg_write(conn, "\r\n", 2);
337
0
  conn->request_state = 3;
338
339
  /* ok */
340
0
  free_buffered_response_header_list(conn);
341
0
  return 0;
342
0
}