/src/mhd2/src/mhd2/response_from.c
Line | Count | Source |
1 | | /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ |
2 | | /* |
3 | | This file is part of GNU libmicrohttpd. |
4 | | Copyright (C) 2021-2024 Evgeny Grin (Karlson2k) |
5 | | |
6 | | GNU libmicrohttpd is free software; you can redistribute it and/or |
7 | | modify it under the terms of the GNU Lesser General Public |
8 | | License as published by the Free Software Foundation; either |
9 | | version 2.1 of the License, or (at your option) any later version. |
10 | | |
11 | | GNU libmicrohttpd is distributed in the hope that it will be useful, |
12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | Lesser General Public License for more details. |
15 | | |
16 | | Alternatively, you can redistribute GNU libmicrohttpd and/or |
17 | | modify it under the terms of the GNU General Public License as |
18 | | published by the Free Software Foundation; either version 2 of |
19 | | the License, or (at your option) any later version, together |
20 | | with the eCos exception, as follows: |
21 | | |
22 | | As a special exception, if other files instantiate templates or |
23 | | use macros or inline functions from this file, or you compile this |
24 | | file and link it with other works to produce a work based on this |
25 | | file, this file does not by itself cause the resulting work to be |
26 | | covered by the GNU General Public License. However the source code |
27 | | for this file must still be made available in accordance with |
28 | | section (3) of the GNU General Public License v2. |
29 | | |
30 | | This exception does not invalidate any other reasons why a work |
31 | | based on this file might be covered by the GNU General Public |
32 | | License. |
33 | | |
34 | | You should have received copies of the GNU Lesser General Public |
35 | | License and the GNU General Public License along with this library; |
36 | | if not, see <https://www.gnu.org/licenses/>. |
37 | | */ |
38 | | |
39 | | /** |
40 | | * @file src/mhd2/response_from.c |
41 | | * @brief The definitions of MHD_response_from_X() functions and related |
42 | | * internal functions |
43 | | * @author Karlson2k (Evgeny Grin) |
44 | | */ |
45 | | |
46 | | #include "mhd_sys_options.h" |
47 | | |
48 | | #include "response_from.h" |
49 | | |
50 | | #include <string.h> |
51 | | |
52 | | #include "sys_bool_type.h" |
53 | | #include "sys_base_types.h" |
54 | | |
55 | | #include "compat_calloc.h" |
56 | | #include "sys_malloc.h" |
57 | | #include "sys_file_fd.h" |
58 | | |
59 | | #include "mhd_public_api.h" |
60 | | |
61 | | #include "mhd_locks.h" |
62 | | #include "mhd_response.h" |
63 | | #include "response_options.h" |
64 | | |
65 | | #include "mhd_assert.h" |
66 | | |
67 | | #include "mhd_limits.h" |
68 | | |
69 | | static struct MHD_Response * |
70 | | response_create_basic (enum MHD_HTTP_StatusCode sc, |
71 | | uint_fast64_t cntn_size, |
72 | | MHD_FreeCallback free_cb, |
73 | | void *free_cb_cls) |
74 | 2.10k | { |
75 | 2.10k | struct MHD_Response *restrict r; |
76 | 2.10k | struct ResponseOptions *restrict s; |
77 | | |
78 | 2.10k | if ((100 > sc) || (999 < sc)) |
79 | 0 | return NULL; |
80 | | |
81 | 2.10k | r = (struct MHD_Response *) |
82 | 2.10k | mhd_calloc (1, |
83 | 2.10k | sizeof(struct MHD_Response)); |
84 | 2.10k | if (NULL != r) |
85 | 2.10k | { |
86 | 2.10k | s = (struct ResponseOptions *) |
87 | 2.10k | mhd_calloc (1, |
88 | 2.10k | sizeof(struct ResponseOptions)); |
89 | 2.10k | if (NULL != s) |
90 | 2.10k | { |
91 | | #ifndef HAVE_NULL_PTR_ALL_ZEROS |
92 | | mhd_DLINKEDL_INIT_LIST (r, headers); |
93 | | #ifdef MHD_SUPPORT_AUTH_DIGEST |
94 | | mhd_DLINKEDL_INIT_LIST (r, auth_d_hdrs); |
95 | | #endif |
96 | | r->free.cb = NULL; |
97 | | r->free.cls = NULL; |
98 | | r->special_resp.spec_hdr = NULL; |
99 | | |
100 | | s->termination_callback.v_ended_cb = NULL; |
101 | | s->termination_callback.v_ended_cb_cls = NULL; |
102 | | #endif /* ! HAVE_NULL_PTR_ALL_ZEROS */ |
103 | | |
104 | 2.10k | r->sc = sc; |
105 | 2.10k | r->cntn_size = cntn_size; |
106 | 2.10k | r->free.cb = free_cb; |
107 | 2.10k | r->free.cls = free_cb_cls; |
108 | 2.10k | r->settings = s; |
109 | | |
110 | 2.10k | return r; /* Success exit point */ |
111 | 2.10k | } |
112 | 0 | free (r); |
113 | 0 | } |
114 | 0 | return NULL; /* Failure exit point */ |
115 | 2.10k | } |
116 | | |
117 | | |
118 | | MHD_INTERNAL |
119 | | MHD_FN_PAR_NONNULL_ (1) void |
120 | | mhd_response_deinit_content_data (struct MHD_Response *restrict r) |
121 | 2.10k | { |
122 | 2.10k | mhd_assert (mhd_RESPONSE_CONTENT_DATA_INVALID != r->cntn_dtype); |
123 | 2.10k | if (mhd_RESPONSE_CONTENT_DATA_IOVEC == r->cntn_dtype) |
124 | 0 | free (r->cntn.iovec.iov); |
125 | 2.10k | else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype) |
126 | 1.16k | close (r->cntn.file.fd); |
127 | | /* For #mhd_RESPONSE_CONTENT_DATA_BUFFER clean-up performed by callback |
128 | | for both modes: internal copy and external cleanup */ |
129 | 2.10k | if (NULL != r->free.cb) |
130 | 0 | r->free.cb (r->free.cls); |
131 | 2.10k | } |
132 | | |
133 | | |
134 | | MHD_EXTERN_ struct MHD_Response * |
135 | | MHD_response_from_callback (enum MHD_HTTP_StatusCode sc, |
136 | | uint_fast64_t size, |
137 | | MHD_DynamicContentCreator dyn_cont, |
138 | | void *dyn_cont_cls, |
139 | | MHD_FreeCallback dyn_cont_fc) |
140 | 0 | { |
141 | 0 | struct MHD_Response *restrict res; |
142 | 0 | res = response_create_basic (sc, size, dyn_cont_fc, dyn_cont_cls); |
143 | 0 | if (NULL != res) |
144 | 0 | { |
145 | 0 | res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_CALLBACK; |
146 | 0 | res->cntn.dyn.cb = dyn_cont; |
147 | 0 | res->cntn.dyn.cls = dyn_cont_cls; |
148 | 0 | } |
149 | 0 | return res; |
150 | 0 | } |
151 | | |
152 | | |
153 | | static const unsigned char empty_buf[1] = { 0 }; |
154 | | |
155 | | MHD_EXTERN_ |
156 | | MHD_FN_PAR_IN_SIZE_ (3,2) struct MHD_Response * |
157 | | MHD_response_from_buffer ( |
158 | | enum MHD_HTTP_StatusCode sc, |
159 | | size_t buffer_size, |
160 | | const char *buffer, |
161 | | MHD_FreeCallback free_cb, |
162 | | void *free_cb_cls) |
163 | 941 | { |
164 | 941 | struct MHD_Response *restrict res; |
165 | | |
166 | 941 | #if SIZEOF_SIZE_T >= 8 |
167 | 941 | if (MHD_SIZE_UNKNOWN == buffer_size) |
168 | 0 | return NULL; |
169 | 941 | #endif /* SIZEOF_SIZE_T >= 8 */ |
170 | | |
171 | 941 | res = response_create_basic (sc, buffer_size, free_cb, free_cb_cls); |
172 | 941 | if (NULL != res) |
173 | 941 | { |
174 | 941 | res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER; |
175 | 941 | res->cntn.buf = (0 != buffer_size) ? |
176 | 895 | (const unsigned char *) buffer : empty_buf; |
177 | 941 | } |
178 | 941 | return res; |
179 | 941 | } |
180 | | |
181 | | |
182 | | static void |
183 | | response_cntn_free_buf (void *ptr) |
184 | 0 | { |
185 | 0 | free (ptr); |
186 | 0 | } |
187 | | |
188 | | |
189 | | MHD_EXTERN_ |
190 | | MHD_FN_PAR_IN_SIZE_ (3,2) struct MHD_Response * |
191 | | MHD_response_from_buffer_copy ( |
192 | | enum MHD_HTTP_StatusCode sc, |
193 | | size_t buffer_size, |
194 | | const char buffer[MHD_FN_PAR_DYN_ARR_SIZE_ (buffer_size)]) |
195 | 0 | { |
196 | 0 | struct MHD_Response *restrict res; |
197 | 0 | const unsigned char *buf_copy; |
198 | 0 | unsigned char *new_buf; |
199 | |
|
200 | 0 | #if SIZEOF_SIZE_T >= 8 |
201 | 0 | if (MHD_SIZE_UNKNOWN == buffer_size) |
202 | 0 | return NULL; |
203 | 0 | #endif /* SIZEOF_SIZE_T >= 8 */ |
204 | | |
205 | 0 | if (0 != buffer_size) |
206 | 0 | { |
207 | 0 | new_buf = (unsigned char *) malloc (buffer_size); |
208 | 0 | if (NULL == new_buf) |
209 | 0 | return NULL; |
210 | 0 | memcpy (new_buf, buffer, buffer_size); |
211 | 0 | res = response_create_basic (sc, buffer_size, |
212 | 0 | response_cntn_free_buf, new_buf); |
213 | 0 | buf_copy = new_buf; |
214 | 0 | } |
215 | 0 | else |
216 | 0 | { |
217 | 0 | new_buf = NULL; |
218 | 0 | buf_copy = empty_buf; |
219 | 0 | res = response_create_basic (sc, 0, (MHD_FreeCallback) NULL, NULL); |
220 | 0 | } |
221 | | |
222 | 0 | if (NULL != res) |
223 | 0 | { |
224 | 0 | res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER; |
225 | 0 | res->cntn.buf = buf_copy; |
226 | 0 | return res; /* Success exit point */ |
227 | 0 | } |
228 | | |
229 | | /* Cleanup path */ |
230 | 0 | if (NULL != new_buf) |
231 | 0 | free (new_buf); |
232 | 0 | return NULL; |
233 | 0 | } |
234 | | |
235 | | |
236 | | MHD_EXTERN_ struct MHD_Response * |
237 | | MHD_response_from_iovec ( |
238 | | enum MHD_HTTP_StatusCode sc, |
239 | | unsigned int iov_count, |
240 | | const struct MHD_IoVec iov[MHD_FN_PAR_DYN_ARR_SIZE_ (iov_count)], |
241 | | MHD_FreeCallback free_cb, |
242 | | void *free_cb_cls) |
243 | 0 | { |
244 | 0 | unsigned int i; |
245 | 0 | size_t i_cp = 0; /**< Index in the copy of iov */ |
246 | 0 | uint_fast64_t total_size = 0; |
247 | | |
248 | | /* Calculate final size, number of valid elements, and check 'iov' */ |
249 | 0 | for (i = 0; i < iov_count; ++i) |
250 | 0 | { |
251 | 0 | if (0 == iov[i].iov_len) |
252 | 0 | continue; /* skip zero-sized elements */ |
253 | 0 | if (NULL == iov[i].iov_base) |
254 | 0 | return NULL; /* NULL pointer with non-zero size */ |
255 | | |
256 | 0 | total_size += iov[i].iov_len; |
257 | 0 | if ((total_size < iov[i].iov_len) || (0 > (ssize_t) total_size) |
258 | 0 | || (((size_t) total_size) != total_size)) |
259 | 0 | return NULL; /* Larger than send function may report as success */ |
260 | 0 | #if defined(MHD_SOCKETS_KIND_POSIX) || ! defined(_WIN64) |
261 | 0 | i_cp++; |
262 | | #else /* ! MHD_SOCKETS_KIND_POSIX && _WIN64 */ |
263 | | if (1) |
264 | | { |
265 | | size_t i_add; |
266 | | |
267 | | i_add = (size_t) (iov[i].iov_len / mhd_IOV_ELMN_MAX_SIZE); |
268 | | if (0 != iov[i].iov_len % mhd_IOV_ELMN_MAX_SIZE) |
269 | | i_add++; |
270 | | i_cp += i_add; |
271 | | if (i_cp < i_add) |
272 | | return NULL; /* Counter overflow */ |
273 | | } |
274 | | #endif /* ! MHD_SOCKETS_KIND_POSIX && _WIN64 */ |
275 | 0 | } |
276 | 0 | if (0 == total_size) |
277 | 0 | { |
278 | 0 | struct MHD_Response *restrict res; |
279 | |
|
280 | 0 | res = response_create_basic (sc, 0, free_cb, free_cb_cls); |
281 | 0 | if (NULL != res) |
282 | 0 | { |
283 | 0 | res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER; |
284 | 0 | res->cntn.buf = empty_buf; |
285 | 0 | } |
286 | 0 | return res; |
287 | 0 | } |
288 | 0 | if (MHD_SIZE_UNKNOWN == total_size) |
289 | 0 | return NULL; |
290 | | |
291 | 0 | mhd_assert (0 < i_cp); |
292 | 0 | if (1) |
293 | 0 | { /* for local variables local scope only */ |
294 | 0 | struct MHD_Response *restrict res; |
295 | 0 | mhd_iovec *iov_copy; |
296 | 0 | size_t num_copy_elements = i_cp; |
297 | |
|
298 | 0 | iov_copy = (mhd_iovec *) |
299 | 0 | mhd_calloc (num_copy_elements, |
300 | 0 | sizeof(mhd_iovec)); |
301 | 0 | if (NULL == iov_copy) |
302 | 0 | return NULL; |
303 | | |
304 | 0 | i_cp = 0; |
305 | 0 | for (i = 0; i < iov_count; ++i) |
306 | 0 | { |
307 | 0 | size_t element_size = iov[i].iov_len; |
308 | 0 | const unsigned char *buf = (const unsigned char *) iov[i].iov_base; |
309 | |
|
310 | 0 | if (0 == element_size) |
311 | 0 | continue; /* skip zero-sized elements */ |
312 | | #if defined(MHD_SOCKETS_KIND_WINSOCK) && defined(_WIN64) |
313 | | while (mhd_IOV_ELMN_MAX_SIZE < element_size) |
314 | | { |
315 | | iov_copy[i_cp].iov_base = |
316 | | (mhd_IOV_ELMN_PTR_TYPE) mhd_DROP_CONST (buf); |
317 | | iov_copy[i_cp].iov_len = mhd_IOV_ELMN_MAX_SIZE; |
318 | | buf += mhd_IOV_ELMN_MAX_SIZE; |
319 | | element_size -= mhd_IOV_ELMN_MAX_SIZE; |
320 | | i_cp++; |
321 | | } |
322 | | #endif /* MHD_SOCKETS_KIND_WINSOCK && _WIN64 */ |
323 | 0 | iov_copy[i_cp].iov_base = (mhd_IOV_ELMN_PTR_TYPE) mhd_DROP_CONST (buf); |
324 | 0 | iov_copy[i_cp].iov_len = (mhd_iov_elmn_size) element_size; |
325 | 0 | i_cp++; |
326 | 0 | } |
327 | 0 | mhd_assert (num_copy_elements == i_cp); |
328 | 0 | mhd_assert (0 < i_cp); |
329 | |
|
330 | 0 | res = response_create_basic (sc, total_size, free_cb, free_cb_cls); |
331 | 0 | if (NULL != res) |
332 | 0 | { |
333 | 0 | res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_IOVEC; |
334 | 0 | res->cntn.iovec.iov = iov_copy; |
335 | 0 | res->cntn.iovec.cnt = i_cp; |
336 | 0 | return res; /* Success exit point */ |
337 | 0 | } |
338 | | |
339 | | /* Below is a cleanup path */ |
340 | 0 | free (iov_copy); |
341 | 0 | } |
342 | 0 | return NULL; |
343 | 0 | } |
344 | | |
345 | | |
346 | | MHD_EXTERN_ |
347 | | MHD_FN_PAR_FD_READ_ (2) struct MHD_Response * |
348 | | MHD_response_from_fd (enum MHD_HTTP_StatusCode sc, |
349 | | int fd, |
350 | | uint_fast64_t offset, |
351 | | uint_fast64_t size) |
352 | 220 | { |
353 | 220 | struct MHD_Response *restrict res; |
354 | 220 | if (offset == MHD_SIZE_UNKNOWN) |
355 | 0 | return NULL; |
356 | 220 | if (size != MHD_SIZE_UNKNOWN) |
357 | 220 | { |
358 | 220 | if (size > ((size + offset) & 0xFFFFFFFFFFFFFFFFU)) |
359 | 0 | return NULL; |
360 | 220 | } |
361 | 220 | res = response_create_basic (sc, size, (MHD_FreeCallback) NULL, NULL); |
362 | 220 | if (NULL != res) |
363 | 220 | { |
364 | 220 | res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_FILE; |
365 | 220 | res->cntn.file.fd = fd; |
366 | 220 | res->cntn.file.offset = offset; |
367 | 220 | #ifdef mhd_USE_SENDFILE |
368 | 220 | res->cntn.file.use_sf = (size < MHD_SIZE_UNKNOWN); |
369 | 220 | #endif |
370 | 220 | res->cntn.file.is_pipe = false; /* Not necessary */ |
371 | 220 | } |
372 | 220 | return res; |
373 | 220 | } |
374 | | |
375 | | |
376 | | MHD_EXTERN_ |
377 | | MHD_FN_PAR_FD_READ_ (2) struct MHD_Response * |
378 | | MHD_response_from_pipe (enum MHD_HTTP_StatusCode sc, |
379 | | int fd) |
380 | 941 | { |
381 | 941 | struct MHD_Response *restrict res; |
382 | 941 | res = response_create_basic (sc, |
383 | 941 | MHD_SIZE_UNKNOWN, |
384 | 941 | (MHD_FreeCallback) NULL, |
385 | 941 | NULL); |
386 | 941 | if (NULL != res) |
387 | 941 | { |
388 | 941 | res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_FILE; |
389 | 941 | res->cntn.file.fd = fd; |
390 | 941 | res->cntn.file.offset = 0; /* Not necessary */ |
391 | 941 | #ifdef mhd_USE_SENDFILE |
392 | 941 | res->cntn.file.use_sf = false; /* Not necessary */ |
393 | 941 | #endif |
394 | 941 | res->cntn.file.is_pipe = true; |
395 | 941 | } |
396 | 941 | return res; |
397 | 941 | } |
398 | | |
399 | | |
400 | | /** |
401 | | * Create special internal response for sending error reply |
402 | | * @param sc the HTTP status code |
403 | | * @param cntn_len the length of the @a cntn |
404 | | * @param cntn the content of the response, could be NULL |
405 | | * @param spec_hdr_len the length of the @a spec_hdr |
406 | | * @param spec_hdr the special header line, without last CRLF, |
407 | | * if not NULL it will be deallocated by free(). |
408 | | * @return the pointer to the created object if succeed, |
409 | | * NULL otherwise |
410 | | * |
411 | | */ |
412 | | MHD_INTERNAL |
413 | | MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (5) struct MHD_Response * |
414 | | mhd_response_special_for_error (unsigned int sc, |
415 | | size_t cntn_len, |
416 | | const char *cntn, |
417 | | size_t spec_hdr_len, |
418 | | char *spec_hdr) |
419 | 0 | { |
420 | 0 | struct MHD_Response *restrict res; |
421 | |
|
422 | 0 | mhd_assert (100 <= sc); |
423 | 0 | mhd_assert (600 > sc); |
424 | 0 | mhd_assert ((NULL != cntn) || (0 == cntn_len)); |
425 | 0 | mhd_assert ((NULL != spec_hdr) || (0 == spec_hdr_len)); |
426 | |
|
427 | 0 | res = (struct MHD_Response *) |
428 | 0 | mhd_calloc (1, |
429 | 0 | sizeof(struct MHD_Response)); |
430 | 0 | if (NULL == res) |
431 | 0 | return NULL; |
432 | | |
433 | | #ifndef HAVE_NULL_PTR_ALL_ZEROS |
434 | | mhd_DLINKEDL_INIT_LIST (res, headers); |
435 | | res->free.cb = NULL; |
436 | | res->free.cls = NULL; |
437 | | res->special_resp.spec_hdr = NULL; |
438 | | #endif /* ! HAVE_NULL_PTR_ALL_ZEROS */ |
439 | 0 | res->sc = (enum MHD_HTTP_StatusCode) sc; |
440 | 0 | res->cntn_size = cntn_len; |
441 | 0 | res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER; |
442 | 0 | res->cntn.buf = (const unsigned char *) ((0 != cntn_len) ? cntn : ""); |
443 | 0 | res->cfg.close_forced = true; |
444 | 0 | res->cfg.int_err_resp = true; |
445 | 0 | res->special_resp.spec_hdr_len = spec_hdr_len; |
446 | 0 | res->special_resp.spec_hdr = spec_hdr; |
447 | 0 | res->frozen = true; |
448 | |
|
449 | 0 | return res; |
450 | 0 | } |