/src/mhd2/src/mhd2/mhd_send.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) 2017-2024 Karlson2k (Evgeny Grin), Full re-write of buffering |
5 | | and pushing, many bugs fixes, optimisations, |
6 | | sendfile() porting |
7 | | Copyright (C) 2019 ng0 <ng0@n0.is>, Initial version of send() wrappers |
8 | | |
9 | | GNU libmicrohttpd is free software; you can redistribute it and/or |
10 | | modify it under the terms of the GNU Lesser General Public |
11 | | License as published by the Free Software Foundation; either |
12 | | version 2.1 of the License, or (at your option) any later version. |
13 | | |
14 | | GNU libmicrohttpd is distributed in the hope that it will be useful, |
15 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | | Lesser General Public License for more details. |
18 | | |
19 | | Alternatively, you can redistribute GNU libmicrohttpd and/or |
20 | | modify it under the terms of the GNU General Public License as |
21 | | published by the Free Software Foundation; either version 2 of |
22 | | the License, or (at your option) any later version, together |
23 | | with the eCos exception, as follows: |
24 | | |
25 | | As a special exception, if other files instantiate templates or |
26 | | use macros or inline functions from this file, or you compile this |
27 | | file and link it with other works to produce a work based on this |
28 | | file, this file does not by itself cause the resulting work to be |
29 | | covered by the GNU General Public License. However the source code |
30 | | for this file must still be made available in accordance with |
31 | | section (3) of the GNU General Public License v2. |
32 | | |
33 | | This exception does not invalidate any other reasons why a work |
34 | | based on this file might be covered by the GNU General Public |
35 | | License. |
36 | | |
37 | | You should have received copies of the GNU Lesser General Public |
38 | | License and the GNU General Public License along with this library; |
39 | | if not, see <https://www.gnu.org/licenses/>. |
40 | | */ |
41 | | |
42 | | /** |
43 | | * @file src/mhd2/mhd_send.c |
44 | | * @brief Implementation of send() wrappers and helper functions. |
45 | | * @author Karlson2k (Evgeny Grin) |
46 | | * @author ng0 (N. Gillmann) |
47 | | * @author Christian Grothoff |
48 | | */ |
49 | | |
50 | | /* Worth considering for future improvements and additions: |
51 | | * NetBSD has no sendfile or sendfile64. The way to work |
52 | | * with this seems to be to mmap the file and write(2) as |
53 | | * large a chunk as possible to the socket. Alternatively, |
54 | | * use madvise(..., MADV_SEQUENTIAL). */ |
55 | | |
56 | | #include "mhd_sys_options.h" |
57 | | |
58 | | #include <string.h> |
59 | | |
60 | | #include "mhd_send.h" |
61 | | #include "sys_sockets_headers.h" |
62 | | #include "sys_ip_headers.h" |
63 | | #include "mhd_sockets_macros.h" |
64 | | #include "daemon_logger.h" |
65 | | #include "mhd_socket_error_funcs.h" |
66 | | |
67 | | #include "mhd_daemon.h" |
68 | | #include "mhd_connection.h" |
69 | | #include "mhd_response.h" |
70 | | |
71 | | #include "mhd_iovec.h" |
72 | | #ifdef HAVE_LINUX_SENDFILE |
73 | | # include <sys/sendfile.h> |
74 | | #endif /* HAVE_LINUX_SENDFILE */ |
75 | | |
76 | | #if defined(HAVE_FREEBSD_SENDFILE) || defined(HAVE_DARWIN_SENDFILE) |
77 | | # include <sys/types.h> |
78 | | # include <sys/socket.h> |
79 | | # include <sys/uio.h> |
80 | | #endif /* HAVE_FREEBSD_SENDFILE || HAVE_DARWIN_SENDFILE */ |
81 | | #ifdef HAVE_SYS_PARAM_H |
82 | | /* For FreeBSD version identification */ |
83 | | # include <sys/param.h> |
84 | | #endif /* HAVE_SYS_PARAM_H */ |
85 | | #ifdef HAVE_SYSCONF |
86 | | # include <unistd.h> |
87 | | #endif /* HAVE_SYSCONF */ |
88 | | #include "mhd_assert.h" |
89 | | |
90 | | #include "mhd_limits.h" |
91 | | |
92 | | #ifdef MHD_SUPPORT_HTTPS |
93 | | # include "mhd_tls_funcs.h" |
94 | | #endif |
95 | | |
96 | | #if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) || \ |
97 | | defined(MHD_SOCKETS_KIND_WINSOCK) |
98 | | # define mhd_USE_VECT_SEND 1 |
99 | | #endif /* HAVE_SENDMSG || HAVE_WRITEV || MHD_SOCKETS_KIND_WINSOCK */ |
100 | | |
101 | | |
102 | | #ifdef mhd_USE_VECT_SEND |
103 | | # if (! defined(HAVE_SENDMSG) || ! defined(HAVE_DCLR_MSG_NOSIGNAL)) && \ |
104 | | defined(mhd_SEND_SPIPE_SUPPRESS_POSSIBLE) && \ |
105 | | defined(mhd_SEND_SPIPE_SUPPRESS_NEEDED) |
106 | | # define mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED 1 |
107 | | # endif /* (!HAVE_SENDMSG || !HAVE_DCLR_MSG_NOSIGNAL) && |
108 | | mhd_SEND_SPIPE_SUPPRESS_POSSIBLE && mhd_SEND_SPIPE_SUPPRESS_NEEDED */ |
109 | | #endif /* mhd_USE_VECT_SEND */ |
110 | | |
111 | | /** |
112 | | * sendfile() chuck size |
113 | | */ |
114 | 0 | #define mhd_SENFILE_CHUNK_SIZE (0x20000) |
115 | | |
116 | | /** |
117 | | * sendfile() chuck size for thread-per-connection |
118 | | */ |
119 | 0 | #define mhd_SENFILE_CHUNK_SIZE_FOR_THR_P_C (0x200000) |
120 | | |
121 | | #if defined(HAVE_FREEBSD_SENDFILE) && defined(SF_FLAGS) |
122 | | /** |
123 | | * FreeBSD sendfile() flags |
124 | | */ |
125 | | static int freebsd_sendfile_flags_; |
126 | | |
127 | | /** |
128 | | * FreeBSD sendfile() flags for thread-per-connection |
129 | | */ |
130 | | static int freebsd_sendfile_flags_thd_p_c_; |
131 | | |
132 | | |
133 | | /** |
134 | | * Initialises variables for FreeBSD's sendfile() |
135 | | */ |
136 | | static void |
137 | | freebsd_sendfile_init_ (void) |
138 | | { |
139 | | long sys_page_size = sysconf (_SC_PAGESIZE); |
140 | | if (0 >= sys_page_size) |
141 | | { /* Failed to get page size. */ |
142 | | freebsd_sendfile_flags_ = SF_NODISKIO; |
143 | | freebsd_sendfile_flags_thd_p_c_ = SF_NODISKIO; |
144 | | } |
145 | | else |
146 | | { |
147 | | freebsd_sendfile_flags_ = |
148 | | SF_FLAGS ((uint_least16_t) \ |
149 | | ((mhd_SENFILE_CHUNK_SIZE + sys_page_size - 1) / sys_page_size) \ |
150 | | & 0xFFFFU, SF_NODISKIO); |
151 | | freebsd_sendfile_flags_thd_p_c_ = |
152 | | SF_FLAGS ((uint_least16_t) \ |
153 | | ((mhd_SENFILE_CHUNK_SIZE_FOR_THR_P_C + sys_page_size - 1) \ |
154 | | / sys_page_size) & 0xFFFFU, SF_NODISKIO); |
155 | | } |
156 | | } |
157 | | |
158 | | |
159 | | #else /* ! HAVE_FREEBSD_SENDFILE || ! SF_FLAGS */ |
160 | 3 | # define freebsd_sendfile_init_() (void) 0 |
161 | | #endif /* HAVE_FREEBSD_SENDFILE */ |
162 | | |
163 | | |
164 | | #if defined(HAVE_SYSCONF) && defined(_SC_IOV_MAX) |
165 | | /** |
166 | | * Current IOV_MAX system value |
167 | | */ |
168 | | static unsigned long mhd_iov_max_ = 0; |
169 | | |
170 | | static void |
171 | | iov_max_init_ (void) |
172 | 3 | { |
173 | 3 | long res = sysconf (_SC_IOV_MAX); |
174 | 3 | if (res >= 0) |
175 | 3 | mhd_iov_max_ = (unsigned long) res; |
176 | 0 | else |
177 | 0 | { |
178 | 0 | # if defined(IOV_MAX) |
179 | 0 | mhd_iov_max_ = IOV_MAX; |
180 | | # else /* ! IOV_MAX */ |
181 | | mhd_iov_max_ = 8; /* Should be the safe limit */ |
182 | | # endif /* ! IOV_MAX */ |
183 | 0 | } |
184 | 3 | } |
185 | | |
186 | | |
187 | | /** |
188 | | * IOV_MAX (run-time) value |
189 | | */ |
190 | 0 | # define mhd_IOV_MAX mhd_iov_max_ |
191 | | #else /* ! HAVE_SYSCONF || ! _SC_IOV_MAX */ |
192 | | # define iov_max_init_() ((void) 0) |
193 | | # if defined(IOV_MAX) |
194 | | |
195 | | /** |
196 | | * IOV_MAX (static) value |
197 | | */ |
198 | | # define mhd_IOV_MAX IOV_MAX |
199 | | # endif /* IOV_MAX */ |
200 | | #endif /* ! HAVE_SYSCONF || ! _SC_IOV_MAX */ |
201 | | |
202 | | |
203 | | void |
204 | | mhd_send_init_once (void) |
205 | 3 | { |
206 | | /* FreeBSD 11 and later allow to specify read-ahead size |
207 | | * and handles SF_NODISKIO differently. |
208 | | * SF_FLAGS defined only on FreeBSD 11 and later. */ |
209 | 3 | freebsd_sendfile_init_ (); |
210 | | |
211 | 3 | iov_max_init_ (); |
212 | 3 | } |
213 | | |
214 | | |
215 | | MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool |
216 | | mhd_connection_set_nodelay_state (struct MHD_Connection *connection, |
217 | | bool nodelay_state) |
218 | 3 | { |
219 | 3 | #ifdef HAVE_DCLR_TCP_NODELAY |
220 | 3 | static const mhd_SCKT_OPT_BOOL off_val = 0; |
221 | 3 | static const mhd_SCKT_OPT_BOOL on_val = 1; |
222 | 3 | int err_code; |
223 | | |
224 | 3 | if (mhd_T_IS_YES (connection->sk.props.is_nonip)) |
225 | 1 | return false; |
226 | | |
227 | 2 | if (0 == mhd_setsockopt (connection->sk.fd, |
228 | 2 | IPPROTO_TCP, |
229 | 2 | TCP_NODELAY, |
230 | 2 | (const void *) (nodelay_state ? &on_val : &off_val), |
231 | 2 | sizeof (off_val))) |
232 | 0 | { |
233 | 0 | connection->sk.state.nodelay = nodelay_state ? mhd_T_YES : mhd_T_NO; |
234 | 0 | return true; |
235 | 0 | } |
236 | | |
237 | 2 | err_code = mhd_SCKT_GET_LERR (); |
238 | 2 | if ((mhd_T_IS_NOT_YES (connection->sk.props.is_nonip)) && |
239 | 2 | (mhd_SCKT_ERR_IS_EINVAL (err_code) || |
240 | 2 | mhd_SCKT_ERR_IS_NOPROTOOPT (err_code) || |
241 | 2 | mhd_SCKT_ERR_IS_NOTSOCK (err_code))) |
242 | 2 | { |
243 | 2 | connection->sk.props.is_nonip = mhd_T_YES; |
244 | 2 | } |
245 | 0 | else |
246 | 0 | { |
247 | 0 | mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_TCP_NODELAY_FAILED, \ |
248 | 0 | "Failed to set required TCP_NODELAY option for the socket."); |
249 | 0 | } |
250 | | #else /* ! TCP_NODELAY */ |
251 | | (void) nodelay_state; /* Mute compiler warnings */ |
252 | | connection->sk.state.nodelay = mhd_T_NO; |
253 | | #endif /* ! TCP_NODELAY */ |
254 | 2 | return false; |
255 | 2 | } |
256 | | |
257 | | |
258 | | MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool |
259 | | mhd_connection_set_cork_state (struct MHD_Connection *connection, |
260 | | bool cork_state) |
261 | 0 | { |
262 | 0 | #if defined(mhd_TCP_CORK_NOPUSH) |
263 | 0 | static const mhd_SCKT_OPT_BOOL off_val = 0; |
264 | 0 | static const mhd_SCKT_OPT_BOOL on_val = 1; |
265 | 0 | int err_code; |
266 | |
|
267 | 0 | if (mhd_T_IS_YES (connection->sk.props.is_nonip)) |
268 | 0 | return false; |
269 | | |
270 | 0 | if (0 == mhd_setsockopt (connection->sk.fd, |
271 | 0 | IPPROTO_TCP, |
272 | 0 | mhd_TCP_CORK_NOPUSH, |
273 | 0 | (const void *) (cork_state ? &on_val : &off_val), |
274 | 0 | sizeof (off_val))) |
275 | 0 | { |
276 | 0 | connection->sk.state.corked = cork_state ? mhd_T_YES : mhd_T_NO; |
277 | 0 | return true; |
278 | 0 | } |
279 | | |
280 | 0 | err_code = mhd_SCKT_GET_LERR (); |
281 | 0 | if ((mhd_T_IS_NOT_YES (connection->sk.props.is_nonip)) && |
282 | 0 | (mhd_SCKT_ERR_IS_EINVAL (err_code) || |
283 | 0 | mhd_SCKT_ERR_IS_NOPROTOOPT (err_code) || |
284 | 0 | mhd_SCKT_ERR_IS_NOTSOCK (err_code))) |
285 | 0 | { |
286 | 0 | connection->sk.props.is_nonip = mhd_T_YES; |
287 | 0 | } |
288 | 0 | else |
289 | 0 | { |
290 | 0 | # ifdef TCP_CORK |
291 | 0 | mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_TCP_CORK_NOPUSH_FAILED, \ |
292 | 0 | "Failed to set required TCP_CORK option for the socket."); |
293 | | # else |
294 | | mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_TCP_CORK_NOPUSH_FAILED, \ |
295 | | "Failed to set required TCP_NOPUSH option for the socket."); |
296 | | # endif |
297 | 0 | } |
298 | |
|
299 | | #else /* ! mhd_TCP_CORK_NOPUSH */ |
300 | | (void) cork_state; /* Mute compiler warnings. */ |
301 | | connection->sk.state.corked = mhd_T_NO; |
302 | | #endif /* ! mhd_TCP_CORK_NOPUSH */ |
303 | 0 | return false; |
304 | 0 | } |
305 | | |
306 | | |
307 | | /** |
308 | | * Handle pre-send setsockopt calls. |
309 | | * |
310 | | * @param connection the MHD_Connection structure |
311 | | * @param plain_send set to true if plain send() or sendmsg() will be called, |
312 | | * set to false if TLS socket send(), sendfile() or |
313 | | * writev() will be called. |
314 | | * @param push_data whether to push data to the network from buffers after |
315 | | * the next call of send function. |
316 | | */ |
317 | | static void |
318 | | pre_send_setopt (struct MHD_Connection *connection, |
319 | | bool plain_send, |
320 | | bool push_data) |
321 | 0 | { |
322 | | /* Try to buffer data if not sending the final piece. |
323 | | * Final piece is indicated by push_data == true. */ |
324 | 0 | const bool buffer_data = (! push_data); |
325 | |
|
326 | 0 | if (mhd_T_IS_YES (connection->sk.props.is_nonip)) |
327 | 0 | return; |
328 | | |
329 | | // TODO: support inheriting of TCP_NODELAY and TCP_NOPUSH |
330 | | |
331 | | /* The goal is to minimise the total number of additional sys-calls |
332 | | * before and after send(). |
333 | | * The following tricky (over-)complicated algorithm typically use zero, |
334 | | * one or two additional sys-calls (depending on OS) for each response. */ |
335 | | |
336 | 0 | if (buffer_data) |
337 | 0 | { |
338 | | /* Need to buffer data if possible. */ |
339 | 0 | #ifdef mhd_USE_MSG_MORE |
340 | 0 | if (plain_send) |
341 | 0 | return; /* Data is buffered by send() with MSG_MORE flag. |
342 | | * No need to check or change anything. */ |
343 | | #else /* ! mhd_USE_MSG_MORE */ |
344 | | (void) plain_send; /* Mute compiler warning. */ |
345 | | #endif /* ! mhd_USE_MSG_MORE */ |
346 | | |
347 | 0 | #ifdef mhd_TCP_CORK_NOPUSH |
348 | 0 | if (mhd_T_IS_YES (connection->sk.state.corked)) |
349 | 0 | return; /* The connection was already corked. */ |
350 | | |
351 | | /* Prefer 'cork' over 'no delay' as the 'cork' buffers better, regardless |
352 | | * of the number of received ACKs. */ |
353 | 0 | if (mhd_connection_set_cork_state (connection, true)) |
354 | 0 | return; /* The connection has been corked. */ |
355 | | |
356 | | /* Failed to cork the connection. |
357 | | * Really unlikely to happen on TCP connections. */ |
358 | 0 | #endif /* mhd_TCP_CORK_NOPUSH */ |
359 | 0 | if (mhd_T_IS_NO (connection->sk.state.nodelay)) |
360 | 0 | return; /* TCP_NODELAY was not set for the socket. |
361 | | * Nagle's algorithm will buffer some data. */ |
362 | | |
363 | | /* Try to reset TCP_NODELAY state for the socket. |
364 | | * Ignore possible error as no other options exist to |
365 | | * buffer data. */ |
366 | 0 | mhd_connection_set_nodelay_state (connection, false); |
367 | | /* TCP_NODELAY has been (hopefully) reset for the socket. |
368 | | * Nagle's algorithm will buffer some data. */ |
369 | 0 | return; |
370 | 0 | } |
371 | | |
372 | | /* Need to push data after the next send() */ |
373 | | /* If additional sys-call is required, prefer to make it only after the send() |
374 | | * (if possible) as this send() may consume only part of the prepared data and |
375 | | * more send() calls will be used. */ |
376 | 0 | #ifdef mhd_TCP_CORK_NOPUSH |
377 | 0 | # ifdef mhd_CORK_RESET_PUSH_DATA |
378 | 0 | # ifdef mhd_CORK_RESET_PUSH_DATA_ALWAYS |
379 | | /* Data can be pushed immediately by uncorking socket regardless of |
380 | | * cork state before. */ |
381 | | /* This is typical for Linux, no other kernel with |
382 | | * such behaviour are known so far. */ |
383 | | |
384 | | /* No need to check the current state of TCP_CORK / TCP_NOPUSH |
385 | | * as reset of cork will push the data anyway. */ |
386 | 0 | return; /* Data may be pushed by resetting of |
387 | | * TCP_CORK / TCP_NOPUSH after send() */ |
388 | | # else /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */ |
389 | | /* Reset of TCP_CORK / TCP_NOPUSH will push the data |
390 | | * only if socket is corked. */ |
391 | | |
392 | | # ifdef mhd_NODELAY_SET_PUSH_DATA_ALWAYS |
393 | | /* Data can be pushed immediately by setting TCP_NODELAY regardless |
394 | | * of TCP_NODDELAY or corking state before. */ |
395 | | |
396 | | /* Dead code currently, no known kernels with such behaviour and without |
397 | | * pushing by uncorking. */ |
398 | | return; /* Data may be pushed by setting of TCP_NODELAY after send(). |
399 | | No need to make extra sys-calls before send().*/ |
400 | | # else /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */ |
401 | | |
402 | | /* These next comment blocks are just generic description for the possible |
403 | | * choices for the code below. */ |
404 | | # ifdef mhd_NODELAY_SET_PUSH_DATA |
405 | | /* Setting of TCP_NODELAY will push the data only if |
406 | | * both TCP_NODELAY and TCP_CORK / TCP_NOPUSH were not set. */ |
407 | | |
408 | | /* Data can be pushed immediately by uncorking socket if |
409 | | * socket was corked before or by setting TCP_NODELAY if |
410 | | * socket was not corked and TCP_NODELAY was not set before. */ |
411 | | |
412 | | /* This combination not possible currently as Linux is the only kernel that |
413 | | * pushes data by setting of TCP_NODELAY and Linux pushes data always |
414 | | * by TCP_NODELAY, regardless previous TCP_NODELAY state. */ |
415 | | # else /* ! mhd_NODELAY_SET_PUSH_DATA */ |
416 | | /* Data can be pushed immediately by uncorking socket or |
417 | | * can be pushed by send() on uncorked socket if |
418 | | * TCP_NODELAY was set *before*. */ |
419 | | |
420 | | /* This is typical modern FreeBSD and OpenBSD behaviour. */ |
421 | | # endif /* ! mhd_NODELAY_SET_PUSH_DATA */ |
422 | | |
423 | | if (mhd_T_IS_YES (connection->sk.state.corked)) |
424 | | return; /* Socket is corked. Data can be pushed by resetting of |
425 | | * TCP_CORK / TCP_NOPUSH after send() */ |
426 | | else if (mhd_T_IS_NO (connection->sk.state.corked)) |
427 | | { |
428 | | /* The socket is not corked. */ |
429 | | if (mhd_T_IS_YES (connection->sk.state.nodelay)) |
430 | | return; /* TCP_NODELAY was already set, |
431 | | * data will be pushed automatically by the next send() */ |
432 | | # ifdef mhd_NODELAY_SET_PUSH_DATA |
433 | | else if (mhd_T_IS_MAYBE (connection->sk.state.nodelay)) |
434 | | { |
435 | | /* Setting TCP_NODELAY may push data NOW. |
436 | | * Cork socket here and uncork after send(). */ |
437 | | if (mhd_connection_set_cork_state (connection, true)) |
438 | | return; /* The connection has been corked. |
439 | | * Data can be pushed by resetting of |
440 | | * TCP_CORK / TCP_NOPUSH after send() */ |
441 | | else |
442 | | { |
443 | | /* The socket cannot be corked. |
444 | | * Really unlikely to happen on TCP connections */ |
445 | | /* Have to set TCP_NODELAY. |
446 | | * If TCP_NODELAY real system state was OFF then |
447 | | * already buffered data may be pushed NOW, but it is unlikely |
448 | | * to happen as this is only a backup solution when corking has failed. |
449 | | * Ignore possible error here as no other options exist to |
450 | | * push data. */ |
451 | | mhd_connection_set_nodelay_state (connection, true); |
452 | | /* TCP_NODELAY has been (hopefully) set for the socket. |
453 | | * The data will be pushed by the next send(). */ |
454 | | return; |
455 | | } |
456 | | } |
457 | | # endif /* mhd_NODELAY_SET_PUSH_DATA */ |
458 | | else |
459 | | { |
460 | | # ifdef mhd_NODELAY_SET_PUSH_DATA |
461 | | /* The socket is not corked and TCP_NODELAY is switched off. */ |
462 | | # else /* ! mhd_NODELAY_SET_PUSH_DATA */ |
463 | | /* The socket is not corked and TCP_NODELAY is not set or unknown. */ |
464 | | # endif /* ! mhd_NODELAY_SET_PUSH_DATA */ |
465 | | |
466 | | /* At least one additional sys-call before send() is required. */ |
467 | | /* Setting TCP_NODELAY is optimal here as data will be pushed |
468 | | * automatically by the next send() and no additional |
469 | | * sys-call are needed after the send(). */ |
470 | | if (mhd_connection_set_nodelay_state (connection, true)) |
471 | | return; |
472 | | else |
473 | | { |
474 | | /* Failed to set TCP_NODELAY for the socket. |
475 | | * Really unlikely to happen on TCP connections. */ |
476 | | /* Cork the socket here and make additional sys-call |
477 | | * to uncork the socket after send(). This will push the data. */ |
478 | | /* Ignore possible error here as no other options exist to |
479 | | * push data. */ |
480 | | mhd_connection_set_cork_state (connection, true); |
481 | | /* The connection has been (hopefully) corked. |
482 | | * Data can be pushed by resetting of TCP_CORK / TCP_NOPUSH |
483 | | * after send() */ |
484 | | return; |
485 | | } |
486 | | } |
487 | | } |
488 | | /* Corked state is unknown. Need to make a sys-call here otherwise |
489 | | * data may not be pushed. */ |
490 | | if (mhd_connection_set_cork_state (connection, true)) |
491 | | return; /* The connection has been corked. |
492 | | * Data can be pushed by resetting of |
493 | | * TCP_CORK / TCP_NOPUSH after send() */ |
494 | | /* The socket cannot be corked. |
495 | | * Really unlikely to happen on TCP connections */ |
496 | | if (mhd_T_IS_YES (connection->sk.state.nodelay)) |
497 | | return; /* TCP_NODELAY was already set, |
498 | | * data will be pushed by the next send() */ |
499 | | |
500 | | /* Have to set TCP_NODELAY. */ |
501 | | # ifdef mhd_NODELAY_SET_PUSH_DATA |
502 | | /* If TCP_NODELAY state was unknown (external connection) then |
503 | | * already buffered data may be pushed here, but this is unlikely |
504 | | * to happen as it is only a backup solution when corking has failed. */ |
505 | | # endif /* mhd_NODELAY_SET_PUSH_DATA */ |
506 | | /* Ignore possible error here as no other options exist to |
507 | | * push data. */ |
508 | | mhd_connection_set_nodelay_state (connection, true); |
509 | | /* TCP_NODELAY has been (hopefully) set for the socket. |
510 | | * The data will be pushed by the next send(). */ |
511 | | return; |
512 | | # endif /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */ |
513 | | # endif /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */ |
514 | | # else /* ! mhd_CORK_RESET_PUSH_DATA */ |
515 | | |
516 | | # ifndef mhd_NODELAY_SET_PUSH_DATA |
517 | | /* Neither uncorking the socket or setting TCP_NODELAY |
518 | | * push the data immediately. */ |
519 | | /* The only way to push the data is to use send() on uncorked |
520 | | * socket with TCP_NODELAY switched on . */ |
521 | | |
522 | | /* This is old FreeBSD and Darwin behaviour. */ |
523 | | |
524 | | /* Uncork socket if socket wasn't uncorked. */ |
525 | | if (mhd_T_IS_NOT_NO (connection->sk.state.corked)) |
526 | | mhd_connection_set_cork_state (connection, false); |
527 | | |
528 | | /* Set TCP_NODELAY if it wasn't set. */ |
529 | | if (mhd_T_IS_NOT_YES (connection->sk.state.nodelay)) |
530 | | mhd_connection_set_nodelay_state (connection, true); |
531 | | |
532 | | return; |
533 | | # else /* mhd_NODELAY_SET_PUSH_DATA */ |
534 | | /* Setting TCP_NODELAY push the data immediately. */ |
535 | | |
536 | | /* Dead code currently as Linux kernel is only kernel which push by |
537 | | * setting TCP_NODELAY. The same kernel push data by resetting TCP_CORK. */ |
538 | | # ifdef mhd_NODELAY_SET_PUSH_DATA_ALWAYS |
539 | | return; /* Data may be pushed by setting of TCP_NODELAY after send(). |
540 | | No need to make extra sys-calls before send().*/ |
541 | | # else /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */ |
542 | | /* Cannot set TCP_NODELAY here as it would push data NOW. |
543 | | * Set TCP_NODELAY after the send(), together if uncorking if necessary. */ |
544 | | # endif /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */ |
545 | | # endif /* mhd_NODELAY_SET_PUSH_DATA */ |
546 | | # endif /* ! mhd_CORK_RESET_PUSH_DATA */ |
547 | | #else /* ! mhd_TCP_CORK_NOPUSH */ |
548 | | /* Buffering of data is controlled only by |
549 | | * Nagel's algorithm. */ |
550 | | /* Set TCP_NODELAY if it wasn't set. */ |
551 | | if (mhd_T_IS_NOT_YES (connection->sk.state.nodelay)) |
552 | | mhd_connection_set_nodelay_state (connection, true); |
553 | | #endif /* ! mhd_TCP_CORK_NOPUSH */ |
554 | 0 | } |
555 | | |
556 | | |
557 | | #ifndef mhd_CORK_RESET_PUSH_DATA_ALWAYS |
558 | | /** |
559 | | * Send zero-sized data |
560 | | * |
561 | | * This function use send of zero-sized data to kick data from the socket |
562 | | * buffers to the network. The socket must not be corked and must have |
563 | | * TCP_NODELAY switched on. |
564 | | * Used only as last resort option, when other options are failed due to |
565 | | * some errors. |
566 | | * Should not be called on typical data processing. |
567 | | * @return true if succeed, false if failed |
568 | | */ |
569 | | static bool |
570 | | zero_send (struct MHD_Connection *connection) |
571 | | { |
572 | | static const int dummy = 0; |
573 | | |
574 | | if (mhd_T_IS_YES (connection->sk.props.is_nonip)) |
575 | | return false; |
576 | | mhd_assert (mhd_T_IS_NO (connection->sk.state.corked)); |
577 | | mhd_assert (mhd_T_IS_YES (connection->sk.state.nodelay)); |
578 | | if (0 == mhd_sys_send (connection->sk.fd, &dummy, 0)) |
579 | | return true; |
580 | | mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_ZERO_SEND_FAILED, \ |
581 | | "Failed to push the data by zero-sized send."); |
582 | | return false; |
583 | | } |
584 | | |
585 | | |
586 | | #endif /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */ |
587 | | |
588 | | /** |
589 | | * Handle post-send setsockopt calls. |
590 | | * |
591 | | * @param connection the MHD_Connection structure |
592 | | * @param plain_send_next set to true if plain send() or sendmsg() will be |
593 | | * called next, |
594 | | * set to false if TLS socket send(), sendfile() or |
595 | | * writev() will be called next. |
596 | | * @param push_data whether to push data to the network from buffers |
597 | | */ |
598 | | static void |
599 | | post_send_setopt (struct MHD_Connection *connection, |
600 | | bool plain_send_next, |
601 | | bool push_data) |
602 | 0 | { |
603 | | /* Try to buffer data if not sending the final piece. |
604 | | * Final piece is indicated by push_data == true. */ |
605 | 0 | const bool buffer_data = (! push_data); |
606 | |
|
607 | 0 | if (mhd_T_IS_YES (connection->sk.props.is_nonip)) |
608 | 0 | return; |
609 | 0 | if (buffer_data) |
610 | 0 | return; /* Nothing to do after the send(). */ |
611 | | |
612 | | #ifndef mhd_USE_MSG_MORE |
613 | | (void) plain_send_next; /* Mute compiler warning */ |
614 | | #endif /* ! mhd_USE_MSG_MORE */ |
615 | | |
616 | | /* Need to push data. */ |
617 | 0 | #ifdef mhd_TCP_CORK_NOPUSH |
618 | 0 | if (mhd_T_IS_YES (connection->sk.state.nodelay) && \ |
619 | 0 | mhd_T_IS_NO (connection->sk.state.corked)) |
620 | 0 | return; /* Data has been already pushed by last send(). */ |
621 | | |
622 | 0 | # ifdef mhd_CORK_RESET_PUSH_DATA_ALWAYS |
623 | 0 | # ifdef mhd_NODELAY_SET_PUSH_DATA_ALWAYS |
624 | 0 | # ifdef mhd_USE_MSG_MORE |
625 | | /* This is Linux kernel. |
626 | | * The socket is corked (or unknown) or 'no delay' is not set (or unknown). |
627 | | * There are options: |
628 | | * * Push the data by setting of TCP_NODELAY (without change |
629 | | * of the cork on the socket), |
630 | | * * Push the data by resetting of TCP_CORK. |
631 | | * The optimal choice depends on the next final send functions |
632 | | * used on the same socket. |
633 | | * |
634 | | * In general on Linux kernel TCP_NODELAY always enabled is preferred, |
635 | | * as buffering is controlled by MSG_MORE or cork/uncork. |
636 | | * |
637 | | * If next send function will not support MSG_MORE (like sendfile() |
638 | | * or TLS-connection) than push data by setting TCP_NODELAY |
639 | | * so the socket may remain corked (no additional sys-call before |
640 | | * next send()). |
641 | | * |
642 | | * If send()/sendmsg() will be used next than push data by |
643 | | * resetting of TCP_CORK so next final send without MSG_MORE will push |
644 | | * data to the network (without additional sys-call to push data). */ |
645 | | |
646 | 0 | if (mhd_T_IS_NOT_YES (connection->sk.state.nodelay) || |
647 | 0 | (! plain_send_next)) |
648 | 0 | { |
649 | 0 | if (mhd_connection_set_nodelay_state (connection, true)) |
650 | 0 | return; /* Data has been pushed by TCP_NODELAY. */ |
651 | | /* Failed to set TCP_NODELAY for the socket. |
652 | | * Really unlikely to happen on TCP connections. */ |
653 | 0 | if (mhd_connection_set_cork_state (connection, false)) |
654 | 0 | return; /* Data has been pushed by uncorking the socket. */ |
655 | | /* Failed to uncork the socket. |
656 | | * Really unlikely to happen on TCP connections. */ |
657 | | |
658 | | /* The socket cannot be uncorked, no way to push data */ |
659 | 0 | } |
660 | 0 | else |
661 | 0 | { |
662 | 0 | if (mhd_connection_set_cork_state (connection, false)) |
663 | 0 | return; /* Data has been pushed by uncorking the socket. */ |
664 | | /* Failed to uncork the socket. |
665 | | * Really unlikely to happen on TCP connections. */ |
666 | 0 | if (mhd_connection_set_nodelay_state (connection, true)) |
667 | 0 | return; /* Data has been pushed by TCP_NODELAY. */ |
668 | | /* Failed to set TCP_NODELAY for the socket. |
669 | | * Really unlikely to happen on TCP connections. */ |
670 | | |
671 | | /* The socket cannot be uncorked, no way to push data */ |
672 | 0 | } |
673 | | # else /* ! mhd_USE_MSG_MORE */ |
674 | | /* Push data by setting TCP_NODELAY here as uncorking here |
675 | | * would require corking the socket before sending the next response. */ |
676 | | if (mhd_connection_set_nodelay_state (connection, true)) |
677 | | return; /* Data was pushed by TCP_NODELAY. */ |
678 | | /* Failed to set TCP_NODELAY for the socket. |
679 | | * Really unlikely to happen on TCP connections. */ |
680 | | if (mhd_connection_set_cork_state (connection, false)) |
681 | | return; /* Data was pushed by uncorking the socket. */ |
682 | | /* Failed to uncork the socket. |
683 | | * Really unlikely to happen on TCP connections. */ |
684 | | |
685 | | /* The socket remains corked, no way to push data */ |
686 | | # endif /* ! mhd_USE_MSG_MORE */ |
687 | | # else /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */ |
688 | | if (mhd_connection_set_cork_state (connection, false)) |
689 | | return; /* Data was pushed by uncorking the socket. */ |
690 | | /* Failed to uncork the socket. |
691 | | * Really unlikely to happen on TCP connections. */ |
692 | | |
693 | | /* Socket remains corked, no way to push data */ |
694 | | # endif /* ! mhd_NODELAY_SET_PUSH_DATA_ALWAYS */ |
695 | | # else /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */ |
696 | | /* This is old FreeBSD or Darwin kernel. */ |
697 | | |
698 | | if (mhd_T_IS_NO (connection->sk.state.corked)) |
699 | | { |
700 | | mhd_assert (mhd_T_IS_NOT_YES (connection->sk.state.nodelay)); |
701 | | |
702 | | /* Unlikely to reach this code. |
703 | | * TCP_NODELAY should be turned on before send(). */ |
704 | | if (mhd_connection_set_nodelay_state (connection, true)) |
705 | | { |
706 | | /* TCP_NODELAY has been set on uncorked socket. |
707 | | * Use zero-send to push the data. */ |
708 | | if (zero_send (connection)) |
709 | | return; /* The data has been pushed by zero-send. */ |
710 | | } |
711 | | |
712 | | /* Failed to push the data by all means. */ |
713 | | /* There is nothing left to try. */ |
714 | | } |
715 | | else |
716 | | { |
717 | | #ifdef mhd_CORK_RESET_PUSH_DATA |
718 | | enum mhd_Tristate old_cork_state = connection->sk.state.corked; |
719 | | #endif /* mhd_CORK_RESET_PUSH_DATA */ |
720 | | /* The socket is corked or cork state is unknown. */ |
721 | | |
722 | | if (mhd_connection_set_cork_state (connection, false)) |
723 | | { |
724 | | #ifdef mhd_CORK_RESET_PUSH_DATA |
725 | | /* Modern FreeBSD or OpenBSD kernel */ |
726 | | if (mhd_T_IS_YES (old_cork_state)) |
727 | | return; /* Data has been pushed by uncorking the socket. */ |
728 | | #endif /* mhd_CORK_RESET_PUSH_DATA */ |
729 | | |
730 | | /* Unlikely to reach this code. |
731 | | * The data should be pushed by uncorking (FreeBSD) or |
732 | | * the socket should be uncorked before send(). */ |
733 | | if (mhd_T_IS_YES (connection->sk.state.nodelay) || |
734 | | (mhd_connection_set_nodelay_state (connection, true))) |
735 | | { |
736 | | /* TCP_NODELAY is turned ON on uncorked socket. |
737 | | * Use zero-send to push the data. */ |
738 | | if (zero_send (connection)) |
739 | | return; /* The data has been pushed by zero-send. */ |
740 | | } |
741 | | } |
742 | | /* Data cannot be pushed. */ |
743 | | } |
744 | | #endif /* ! mhd_CORK_RESET_PUSH_DATA_ALWAYS */ |
745 | | #else /* ! mhd_TCP_CORK_NOPUSH */ |
746 | | /* Corking is not supported. Buffering is controlled |
747 | | * by TCP_NODELAY only. */ |
748 | | mhd_assert (mhd_T_IS_NOT_YES (connection->sk.state.corked)); |
749 | | if (mhd_T_IS_YES (connection->sk.state.nodelay)) |
750 | | return; /* Data was already pushed by send(). */ |
751 | | |
752 | | /* Unlikely to reach this code. |
753 | | * TCP_NODELAY should be turned on before send(). */ |
754 | | if (mhd_connection_set_nodelay_state (connection, true)) |
755 | | { |
756 | | /* TCP_NODELAY has been set. |
757 | | * Use zero-send to try to push the data. */ |
758 | | if (zero_send (connection)) |
759 | | return; /* The data has been pushed by zero-send. */ |
760 | | } |
761 | | |
762 | | /* Failed to push the data. */ |
763 | | #endif /* ! mhd_TCP_CORK_NOPUSH */ |
764 | 0 | mhd_LOG_MSG (connection->daemon, MHD_SC_SOCKET_FLUSH_LAST_PART_FAILED, \ |
765 | 0 | "Failed to force flush the last part of the response header " \ |
766 | 0 | "or the response content that might have been buffered by " \ |
767 | 0 | "the kernel. The client may experience some delay (usually " \ |
768 | 0 | "in range 200ms - 5 sec)."); |
769 | 0 | return; |
770 | 0 | } |
771 | | |
772 | | |
773 | | static MHD_FN_PAR_NONNULL_ALL_ |
774 | | MHD_FN_PAR_IN_SIZE_ (3,2) |
775 | | MHD_FN_PAR_OUT_ (5) enum mhd_SocketError |
776 | | mhd_send_plain (struct MHD_Connection *restrict c, |
777 | | size_t buf_size, |
778 | | const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], |
779 | | bool push_data, |
780 | | size_t *restrict sent) |
781 | 0 | { |
782 | | /* plaintext transmission */ |
783 | 0 | ssize_t res; |
784 | 0 | bool full_buf_sent; |
785 | |
|
786 | 0 | mhd_assert (! mhd_C_HAS_TLS (c)); |
787 | 0 | mhd_assert (! mhd_D_HAS_TLS (c->daemon)); |
788 | |
|
789 | 0 | if (buf_size > MHD_SCKT_SEND_MAX_SIZE_) |
790 | 0 | { |
791 | 0 | buf_size = MHD_SCKT_SEND_MAX_SIZE_; /* send() return value limit */ |
792 | 0 | push_data = false; /* Incomplete send */ |
793 | 0 | } |
794 | |
|
795 | 0 | pre_send_setopt (c, true, push_data); |
796 | 0 | #ifdef mhd_USE_MSG_MORE |
797 | 0 | res = mhd_sys_send4 (c->sk.fd, |
798 | 0 | buf, |
799 | 0 | buf_size, |
800 | 0 | push_data ? 0 : MSG_MORE); |
801 | | #else |
802 | | res = mhd_sys_send4 (c->sk.fd, |
803 | | buf, |
804 | | buf_size, |
805 | | 0); |
806 | | #endif |
807 | |
|
808 | 0 | if (0 >= res) |
809 | 0 | { |
810 | 0 | enum mhd_SocketError err; |
811 | |
|
812 | 0 | err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ()); |
813 | |
|
814 | 0 | if (mhd_SOCKET_ERR_AGAIN == err) |
815 | 0 | c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ |
816 | 0 | (((unsigned int) c->sk.ready) |
817 | 0 | & (~(enum mhd_SocketNetState) |
818 | 0 | mhd_SOCKET_NET_STATE_SEND_READY)); |
819 | |
|
820 | 0 | return err; |
821 | 0 | } |
822 | 0 | *sent = (size_t) res; |
823 | |
|
824 | 0 | full_buf_sent = (buf_size == (size_t) res); |
825 | |
|
826 | 0 | if (! full_buf_sent || ! c->sk.props.is_nonblck) |
827 | 0 | c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ |
828 | 0 | (((unsigned int) c->sk.ready) |
829 | 0 | & (~(enum mhd_SocketNetState) |
830 | 0 | mhd_SOCKET_NET_STATE_SEND_READY)); |
831 | | |
832 | | /* If there is a need to push the data from network buffers |
833 | | * call post_send_setopt(). */ |
834 | | /* It's unknown whether sendfile() (or other send function without |
835 | | * MSG_MORE support) will be used for the next reply so assume |
836 | | * that next sending will be the same, like this call. */ |
837 | 0 | if (push_data && full_buf_sent) |
838 | 0 | post_send_setopt (c, true, push_data); |
839 | |
|
840 | 0 | return mhd_SOCKET_ERR_NO_ERROR; |
841 | 0 | } |
842 | | |
843 | | |
844 | | #ifdef MHD_SUPPORT_HTTPS |
845 | | |
846 | | static MHD_FN_PAR_NONNULL_ALL_ |
847 | | MHD_FN_PAR_IN_SIZE_ (3,2) |
848 | | MHD_FN_PAR_OUT_ (5) enum mhd_SocketError |
849 | | mhd_send_tls (struct MHD_Connection *restrict c, |
850 | | size_t buf_size, |
851 | | const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], |
852 | | bool push_data, |
853 | | size_t *restrict sent) |
854 | 0 | { |
855 | | /* TLS connection */ |
856 | 0 | enum mhd_SocketError res; |
857 | |
|
858 | 0 | mhd_assert (mhd_C_HAS_TLS (c)); |
859 | 0 | mhd_assert (mhd_D_HAS_TLS (c->daemon)); |
860 | 0 | mhd_assert (0 != buf_size); |
861 | |
|
862 | 0 | pre_send_setopt (c, false, push_data); |
863 | |
|
864 | 0 | res = mhd_tls_conn_send (c->tls, |
865 | 0 | buf_size, |
866 | 0 | buf, |
867 | 0 | sent); |
868 | |
|
869 | 0 | if (mhd_SOCKET_ERR_NO_ERROR != res) |
870 | 0 | { |
871 | 0 | if (mhd_SOCKET_ERR_AGAIN == res) |
872 | 0 | c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ |
873 | 0 | (((unsigned int) c->sk.ready) |
874 | 0 | & (~(enum mhd_SocketNetState) |
875 | 0 | mhd_SOCKET_NET_STATE_SEND_READY)); |
876 | 0 | return res; |
877 | 0 | } |
878 | | |
879 | 0 | if (! c->sk.props.is_nonblck) |
880 | 0 | c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ |
881 | 0 | (((unsigned int) c->sk.ready) |
882 | 0 | & (~(enum mhd_SocketNetState) |
883 | 0 | mhd_SOCKET_NET_STATE_SEND_READY)); |
884 | | |
885 | | /* If there is a need to push the data from network buffers |
886 | | * call post_send_setopt(). */ |
887 | 0 | if (push_data && (buf_size == *sent)) |
888 | 0 | post_send_setopt (c, false, true); |
889 | |
|
890 | 0 | return mhd_SOCKET_ERR_NO_ERROR; |
891 | 0 | } |
892 | | |
893 | | |
894 | | #endif /* MHD_SUPPORT_HTTPS */ |
895 | | |
896 | | MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ |
897 | | MHD_FN_PAR_IN_SIZE_ (3,2) |
898 | | MHD_FN_PAR_OUT_ (5) enum mhd_SocketError |
899 | | mhd_send_data (struct MHD_Connection *restrict connection, |
900 | | size_t buf_size, |
901 | | const char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], |
902 | | bool push_data, |
903 | | size_t *restrict sent) |
904 | 0 | { |
905 | 0 | mhd_assert (MHD_INVALID_SOCKET != connection->sk.fd); |
906 | 0 | mhd_assert (mhd_HTTP_STAGE_CLOSED != connection->stage); |
907 | |
|
908 | 0 | #ifdef MHD_SUPPORT_HTTPS |
909 | 0 | if (mhd_C_HAS_TLS (connection)) |
910 | 0 | return mhd_send_tls (connection, |
911 | 0 | buf_size, |
912 | 0 | buf, |
913 | 0 | push_data, |
914 | 0 | sent); |
915 | 0 | #endif /* MHD_SUPPORT_HTTPS */ |
916 | | |
917 | 0 | return mhd_send_plain (connection, |
918 | 0 | buf_size, |
919 | 0 | buf, |
920 | 0 | push_data, |
921 | 0 | sent); |
922 | 0 | } |
923 | | |
924 | | |
925 | | MHD_INTERNAL |
926 | | MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (3) |
927 | | MHD_FN_PAR_IN_SIZE_ (3,2) MHD_FN_PAR_IN_SIZE_ (6,5) enum mhd_SocketError |
928 | | mhd_send_hdr_and_body (struct MHD_Connection *restrict connection, |
929 | | size_t header_size, |
930 | | const char *restrict header, |
931 | | bool never_push_hdr, |
932 | | size_t body_size, |
933 | | const char *restrict body, |
934 | | bool complete_response, |
935 | | size_t *restrict sent) |
936 | 0 | { |
937 | 0 | mhd_iov_ret_type res; |
938 | 0 | bool send_error; |
939 | 0 | bool push_hdr; |
940 | 0 | bool push_body; |
941 | 0 | MHD_Socket s = connection->sk.fd; |
942 | 0 | #ifdef mhd_USE_VECT_SEND |
943 | 0 | #if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) |
944 | 0 | struct iovec vector[2]; |
945 | 0 | #ifdef HAVE_SENDMSG |
946 | 0 | struct msghdr msg; |
947 | 0 | #endif /* HAVE_SENDMSG */ |
948 | 0 | #endif /* HAVE_SENDMSG || HAVE_WRITEV */ |
949 | | #ifdef _WIN32 |
950 | | WSABUF vector[2]; |
951 | | DWORD vec_sent; |
952 | | #endif /* _WIN32 */ |
953 | 0 | bool no_vec; /* Is vector-send() disallowed? */ |
954 | |
|
955 | 0 | no_vec = false; |
956 | 0 | no_vec = no_vec || (mhd_C_HAS_TLS (connection)); |
957 | | #if (! defined(HAVE_SENDMSG) || ! defined(HAVE_DCLR_MSG_NOSIGNAL) ) && \ |
958 | | defined(mhd_SEND_SPIPE_SUPPRESS_POSSIBLE) && \ |
959 | | defined(mhd_SEND_SPIPE_SUPPRESS_NEEDED) |
960 | | no_vec = no_vec || (! connection->daemon->sigpipe_blocked && |
961 | | ! connection->sk.props.has_spipe_supp); |
962 | | #endif /* (!HAVE_SENDMSG || ! HAVE_DCLR_MSG_NOSIGNAL) && |
963 | | mhd_SEND_SPIPE_SUPPRESS_POSSIBLE && |
964 | | mhd_SEND_SPIPE_SUPPRESS_NEEDED */ |
965 | 0 | #endif /* mhd_USE_VECT_SEND */ |
966 | |
|
967 | 0 | mhd_assert ( (NULL != body) || (0 == body_size) ); |
968 | |
|
969 | 0 | mhd_assert (MHD_INVALID_SOCKET != s); |
970 | 0 | mhd_assert (mhd_HTTP_STAGE_CLOSED != connection->stage); |
971 | |
|
972 | 0 | push_body = complete_response; |
973 | |
|
974 | 0 | if (! never_push_hdr) |
975 | 0 | { |
976 | 0 | if (! complete_response) |
977 | 0 | push_hdr = true; /* Push the header as the client may react |
978 | | * on header alone while the body data is |
979 | | * being prepared. */ |
980 | 0 | else |
981 | 0 | { |
982 | 0 | if (1400 > (header_size + body_size)) |
983 | 0 | push_hdr = false; /* Do not push the header as complete |
984 | | * reply is already ready and the whole |
985 | | * reply most probably will fit into |
986 | | * the single IP packet. */ |
987 | 0 | else |
988 | 0 | push_hdr = true; /* Push header alone so client may react |
989 | | * on it while reply body is being delivered. */ |
990 | 0 | } |
991 | 0 | } |
992 | 0 | else |
993 | 0 | push_hdr = false; |
994 | |
|
995 | 0 | if (complete_response && (0 == body_size)) |
996 | 0 | push_hdr = true; /* The header alone is equal to the whole response. */ |
997 | |
|
998 | | #ifndef mhd_USE_VECT_SEND |
999 | | no_vec = (no_vec || true); |
1000 | | #else /* mhd_USE_VECT_SEND */ |
1001 | 0 | no_vec = (no_vec || (0 == body_size)); |
1002 | 0 | no_vec = (no_vec || ((sizeof(mhd_iov_elmn_size) <= sizeof(size_t)) && |
1003 | 0 | (((size_t) mhd_IOV_ELMN_MAX_SIZE) < header_size))); |
1004 | 0 | #endif /* mhd_USE_VECT_SEND */ |
1005 | | |
1006 | |
|
1007 | 0 | if (no_vec) |
1008 | 0 | { |
1009 | 0 | enum mhd_SocketError ret; |
1010 | 0 | ret = mhd_send_data (connection, |
1011 | 0 | header_size, |
1012 | 0 | header, |
1013 | 0 | push_hdr, |
1014 | 0 | sent); |
1015 | |
|
1016 | 0 | if ((mhd_SOCKET_ERR_NO_ERROR == ret) && |
1017 | 0 | (header_size == *sent) && |
1018 | 0 | (0 != body_size) && |
1019 | 0 | (header_size < header_size + body_size) && |
1020 | 0 | (connection->sk.props.is_nonblck)) |
1021 | 0 | { |
1022 | 0 | size_t sent_b; |
1023 | | /* The header has been sent completely. |
1024 | | * Try to send the reply body without waiting for |
1025 | | * the next round. */ |
1026 | |
|
1027 | 0 | ret = mhd_send_data (connection, |
1028 | 0 | body_size, |
1029 | 0 | body, |
1030 | 0 | push_body, |
1031 | 0 | &sent_b); |
1032 | |
|
1033 | 0 | if (mhd_SOCKET_ERR_NO_ERROR == ret) |
1034 | 0 | *sent += sent_b; |
1035 | 0 | else if (mhd_SOCKET_ERR_IS_HARD (ret)) |
1036 | 0 | return ret; /* Unrecoverable error */ |
1037 | | |
1038 | 0 | return mhd_SOCKET_ERR_NO_ERROR; /* The header has been sent successfully */ |
1039 | 0 | } |
1040 | 0 | return ret; |
1041 | 0 | } |
1042 | 0 | #ifdef mhd_USE_VECT_SEND |
1043 | | |
1044 | 0 | mhd_assert (! mhd_C_HAS_TLS (connection)); |
1045 | |
|
1046 | 0 | if (header_size > (header_size + body_size)) |
1047 | 0 | { |
1048 | | /* Return value limit */ |
1049 | 0 | body_size = SIZE_MAX - header_size; |
1050 | 0 | complete_response = false; |
1051 | 0 | push_body = complete_response; |
1052 | 0 | } |
1053 | 0 | if (((mhd_iov_ret_type) (header_size + body_size)) < 0 || |
1054 | 0 | ((size_t) (mhd_iov_ret_type) (header_size + body_size)) != |
1055 | 0 | (header_size + body_size)) |
1056 | 0 | { |
1057 | | /* Send sys-call total amount limit */ |
1058 | 0 | body_size = mhd_IOV_RET_MAX_SIZE - header_size; |
1059 | 0 | complete_response = false; |
1060 | 0 | push_body = complete_response; |
1061 | 0 | } |
1062 | |
|
1063 | 0 | pre_send_setopt (connection, |
1064 | 0 | #ifdef HAVE_SENDMSG |
1065 | 0 | true, |
1066 | | #else /* ! HAVE_SENDMSG */ |
1067 | | false, |
1068 | | #endif /* ! HAVE_SENDMSG */ |
1069 | 0 | push_hdr || push_body); |
1070 | 0 | send_error = false; |
1071 | 0 | #if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) |
1072 | 0 | vector[0].iov_base = mhd_DROP_CONST (header); |
1073 | 0 | vector[0].iov_len = header_size; |
1074 | 0 | vector[1].iov_base = mhd_DROP_CONST (body); |
1075 | 0 | vector[1].iov_len = body_size; |
1076 | |
|
1077 | 0 | #if defined(HAVE_SENDMSG) |
1078 | 0 | memset (&msg, 0, sizeof(msg)); |
1079 | 0 | msg.msg_name = NULL; |
1080 | 0 | msg.msg_namelen = 0; |
1081 | 0 | msg.msg_iov = vector; |
1082 | 0 | msg.msg_iovlen = 2; |
1083 | 0 | msg.msg_control = NULL; |
1084 | 0 | msg.msg_controllen = 0; |
1085 | 0 | msg.msg_flags = 0; |
1086 | |
|
1087 | 0 | res = sendmsg (s, &msg, mhd_MSG_NOSIGNAL |
1088 | 0 | # ifdef mhd_USE_MSG_MORE |
1089 | 0 | | ((push_hdr || push_body) ? 0 : mhd_MSG_MORE) |
1090 | 0 | # endif |
1091 | 0 | ); |
1092 | | #elif defined(HAVE_WRITEV) |
1093 | | res = writev (s, vector, 2); |
1094 | | #endif /* HAVE_WRITEV */ |
1095 | 0 | if (0 < res) |
1096 | 0 | *sent = (size_t) res; |
1097 | 0 | else |
1098 | 0 | send_error = true; |
1099 | 0 | #endif /* HAVE_SENDMSG || HAVE_WRITEV */ |
1100 | | #ifdef _WIN32 |
1101 | | if (((mhd_iov_elmn_size) body_size) != body_size) |
1102 | | { |
1103 | | /* Send item size limit */ |
1104 | | body_size = mhd_IOV_ELMN_MAX_SIZE; |
1105 | | complete_response = false; |
1106 | | push_body = complete_response; |
1107 | | } |
1108 | | vector[0].buf = (char *) mhd_DROP_CONST (header); |
1109 | | vector[0].len = (unsigned long) header_size; |
1110 | | vector[1].buf = (char *) mhd_DROP_CONST (body); |
1111 | | vector[1].len = (unsigned long) body_size; |
1112 | | |
1113 | | res = WSASend (s, vector, 2, &vec_sent, 0, NULL, NULL); |
1114 | | if (0 == res) |
1115 | | *sent = (size_t) vec_sent; |
1116 | | else |
1117 | | send_error = true; |
1118 | | #endif /* _WIN32 */ |
1119 | |
|
1120 | 0 | if (send_error) |
1121 | 0 | { |
1122 | 0 | enum mhd_SocketError err; |
1123 | |
|
1124 | 0 | err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ()); |
1125 | |
|
1126 | 0 | if (mhd_SOCKET_ERR_AGAIN == err) |
1127 | 0 | connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ |
1128 | 0 | (((unsigned int) connection->sk.ready) |
1129 | 0 | & (~(enum mhd_SocketNetState) |
1130 | 0 | mhd_SOCKET_NET_STATE_SEND_READY)); |
1131 | |
|
1132 | 0 | return err; |
1133 | 0 | } |
1134 | 0 | if (((header_size + body_size) > *sent) |
1135 | 0 | || ! connection->sk.props.is_nonblck) |
1136 | 0 | connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ |
1137 | 0 | (((unsigned int) connection->sk.ready) |
1138 | 0 | & (~(enum mhd_SocketNetState) |
1139 | 0 | mhd_SOCKET_NET_STATE_SEND_READY)); |
1140 | | |
1141 | | /* If there is a need to push the data from network buffers |
1142 | | * call post_send_setopt(). */ |
1143 | 0 | if ( (push_body) && |
1144 | 0 | ((header_size + body_size) == *sent) ) |
1145 | 0 | { |
1146 | | /* Complete reply has been sent. */ |
1147 | | /* If TLS connection is used then next final send() will be |
1148 | | * without MSG_MORE support. If non-TLS connection is used |
1149 | | * it's unknown whether next 'send' will be plain send() / sendmsg() or |
1150 | | * sendfile() will be used so assume that next final send() will be |
1151 | | * the same, like for this response. */ |
1152 | 0 | post_send_setopt (connection, |
1153 | 0 | #ifdef HAVE_SENDMSG |
1154 | 0 | true, /* Assume the same type of the send function */ |
1155 | | #else /* ! HAVE_SENDMSG */ |
1156 | | false, /* Assume the same type of the send function */ |
1157 | | #endif /* ! HAVE_SENDMSG */ |
1158 | 0 | true); |
1159 | 0 | } |
1160 | 0 | else if ( (push_hdr) && |
1161 | 0 | (header_size <= *sent)) |
1162 | 0 | { |
1163 | | /* The header has been sent completely and there is a |
1164 | | * need to push the header data. */ |
1165 | | /* Luckily the type of send function will be used next is known. */ |
1166 | 0 | post_send_setopt (connection, |
1167 | 0 | true, |
1168 | 0 | true); |
1169 | 0 | } |
1170 | |
|
1171 | 0 | return mhd_SOCKET_ERR_NO_ERROR; |
1172 | | #else /* ! mhd_USE_VECT_SEND */ |
1173 | | mhd_assert (0 && "Should be unreachable"); |
1174 | | return mhd_SOCKET_ERR_INTERNAL; /* Unreachable. Mute warnings. */ |
1175 | | #endif /* ! mhd_USE_VECT_SEND */ |
1176 | 0 | } |
1177 | | |
1178 | | |
1179 | | #if defined(mhd_USE_SENDFILE) |
1180 | | |
1181 | | #if defined(HAVE_LINUX_SENDFILE) && defined(HAVE_SENDFILE64) |
1182 | 0 | # define mhd_off_t off64_t |
1183 | | #else |
1184 | | # define mhd_off_t off_t |
1185 | | #endif |
1186 | | |
1187 | | MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ |
1188 | | MHD_FN_PAR_OUT_ (2) enum mhd_SocketError |
1189 | | mhd_send_sendfile (struct MHD_Connection *restrict c, |
1190 | | size_t *restrict sent) |
1191 | 0 | { |
1192 | 0 | enum mhd_SocketError ret; |
1193 | 0 | const bool used_thr_p_c = |
1194 | 0 | mhd_D_HAS_THR_PER_CONN (c->daemon); |
1195 | 0 | const size_t chunk_size = |
1196 | 0 | used_thr_p_c ? |
1197 | 0 | mhd_SENFILE_CHUNK_SIZE_FOR_THR_P_C : mhd_SENFILE_CHUNK_SIZE; |
1198 | 0 | const int file_fd = c->rp.response->cntn.file.fd; |
1199 | 0 | mhd_off_t offset; |
1200 | 0 | size_t send_size = 0; /* Initialised to mute compiler warning */ |
1201 | 0 | size_t sent_bytes; |
1202 | 0 | bool push_data; |
1203 | 0 | bool fallback_to_filereader; |
1204 | 0 | mhd_assert (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc); |
1205 | 0 | mhd_assert (MHD_SIZE_UNKNOWN != c->rp.response->cntn_size); |
1206 | 0 | mhd_assert (chunk_size <= (size_t) SSIZE_MAX); |
1207 | 0 | mhd_assert (! mhd_C_HAS_TLS (c)); |
1208 | |
|
1209 | 0 | mhd_assert (0 == send_size); /* Mute analyser warning */ |
1210 | 0 | push_data = true; |
1211 | 0 | if (1) |
1212 | 0 | { |
1213 | 0 | bool too_large; |
1214 | 0 | uint_fast64_t left; |
1215 | |
|
1216 | 0 | offset = (mhd_off_t) |
1217 | 0 | (c->rp.rsp_cntn_read_pos + c->rp.response->cntn.file.offset); |
1218 | 0 | too_large = (((uint_fast64_t) offset) < c->rp.rsp_cntn_read_pos); |
1219 | 0 | too_large = too_large || |
1220 | 0 | (((uint_fast64_t) offset) != |
1221 | 0 | (c->rp.rsp_cntn_read_pos + c->rp.response->cntn.file.offset)); |
1222 | 0 | too_large = too_large || (0 > offset); |
1223 | 0 | if (too_large) |
1224 | 0 | { /* Retry to send with file reader and standard 'send()'. */ |
1225 | 0 | c->rp.response->cntn.file.use_sf = false; |
1226 | 0 | return mhd_SOCKET_ERR_INTR; |
1227 | 0 | } |
1228 | | |
1229 | 0 | left = c->rp.response->cntn_size - c->rp.rsp_cntn_read_pos; |
1230 | | |
1231 | | /* Do not allow system to stick sending on single fast connection: |
1232 | | * use 128KiB chunks (2MiB for thread-per-connection). */ |
1233 | 0 | if (chunk_size < left) /* This also limit to SSIZE_MAX automatically */ |
1234 | 0 | { |
1235 | 0 | send_size = chunk_size; |
1236 | 0 | push_data = false; /* No need to push data, there is more to send. */ |
1237 | 0 | } |
1238 | 0 | else |
1239 | 0 | send_size = (size_t) left; |
1240 | 0 | } |
1241 | 0 | mhd_assert (0 != send_size); |
1242 | |
|
1243 | 0 | pre_send_setopt (c, false, push_data); |
1244 | |
|
1245 | 0 | sent_bytes = 0; |
1246 | 0 | ret = mhd_SOCKET_ERR_NO_ERROR; |
1247 | 0 | fallback_to_filereader = false; |
1248 | 0 | #if defined(HAVE_LINUX_SENDFILE) |
1249 | 0 | if (1) |
1250 | 0 | { |
1251 | 0 | ssize_t res; |
1252 | | #ifndef HAVE_SENDFILE64 |
1253 | | ret = sendfile (c->sk.fd, |
1254 | | file_fd, |
1255 | | &offset, |
1256 | | send_size); |
1257 | | #else /* HAVE_SENDFILE64 */ |
1258 | 0 | res = sendfile64 (c->sk.fd, |
1259 | 0 | file_fd, |
1260 | 0 | &offset, |
1261 | 0 | send_size); |
1262 | 0 | #endif /* HAVE_SENDFILE64 */ |
1263 | 0 | if (0 > res) |
1264 | 0 | { |
1265 | 0 | const int sk_err = mhd_SCKT_GET_LERR (); |
1266 | |
|
1267 | 0 | if ((EINVAL == sk_err) || |
1268 | 0 | (EOVERFLOW == sk_err) || |
1269 | 0 | #ifdef EIO |
1270 | 0 | (EIO == sk_err) || |
1271 | 0 | #endif |
1272 | 0 | #ifdef EAFNOSUPPORT |
1273 | 0 | (EAFNOSUPPORT == sk_err) || |
1274 | 0 | #endif |
1275 | 0 | (EOPNOTSUPP == sk_err)) |
1276 | 0 | fallback_to_filereader = true; |
1277 | 0 | else |
1278 | 0 | ret = mhd_socket_error_get_from_sys_err (sk_err); |
1279 | 0 | } |
1280 | 0 | else |
1281 | 0 | sent_bytes = (size_t) res; |
1282 | 0 | } |
1283 | | #elif defined(HAVE_FREEBSD_SENDFILE) |
1284 | | if (1) |
1285 | | { |
1286 | | off_t sent_bytes_offt = 0; |
1287 | | int flags = 0; |
1288 | | bool sent_something = false; |
1289 | | #ifdef SF_FLAGS |
1290 | | flags = used_thr_p_c ? |
1291 | | freebsd_sendfile_flags_thd_p_c_ : freebsd_sendfile_flags_; |
1292 | | #endif /* SF_FLAGS */ |
1293 | | if (0 != sendfile (file_fd, |
1294 | | c->sk.fd, |
1295 | | offset, |
1296 | | send_size, |
1297 | | NULL, |
1298 | | &sent_bytes_offt, |
1299 | | flags)) |
1300 | | { |
1301 | | const int sk_err = mhd_SCKT_GET_LERR (); |
1302 | | |
1303 | | sent_something = |
1304 | | (((EAGAIN == sk_err) || (EBUSY == sk_err) || (EINTR == sk_err)) && |
1305 | | (0 != sent_bytes_offt)); |
1306 | | |
1307 | | if (! sent_something) |
1308 | | { |
1309 | | enum mhd_SocketError err; |
1310 | | if ((EINVAL == sk_err) || |
1311 | | (EIO == sk_err) || |
1312 | | (EOPNOTSUPP == sk_err)) |
1313 | | fallback_to_filereader = true; |
1314 | | else |
1315 | | ret = mhd_socket_error_get_from_sys_err (sk_err); |
1316 | | } |
1317 | | } |
1318 | | else |
1319 | | sent_something = true; |
1320 | | |
1321 | | if (sent_something) |
1322 | | { |
1323 | | mhd_assert (0 <= sent_bytes_offt); |
1324 | | mhd_assert (SIZE_MAX >= sent_bytes_offt); |
1325 | | sent_bytes = (size_t) sent_bytes_offt; |
1326 | | } |
1327 | | } |
1328 | | #elif defined(HAVE_DARWIN_SENDFILE) |
1329 | | if (1) |
1330 | | { |
1331 | | off_t len; |
1332 | | bool sent_something; |
1333 | | |
1334 | | sent_something = false; |
1335 | | len = (off_t) send_size; /* chunk always fit */ |
1336 | | |
1337 | | if (0 != sendfile (file_fd, |
1338 | | c->sk.fd, |
1339 | | offset, |
1340 | | &len, |
1341 | | NULL, |
1342 | | 0)) |
1343 | | { |
1344 | | const int sk_err = mhd_SCKT_GET_LERR (); |
1345 | | |
1346 | | sent_something = |
1347 | | ((EAGAIN == sk_err) || (EINTR == sk_err)) && |
1348 | | (0 != len); |
1349 | | |
1350 | | if (! sent_something) |
1351 | | { |
1352 | | if ((ENOTSUP == sk_err) || |
1353 | | (EOPNOTSUPP == sk_err)) |
1354 | | fallback_to_filereader = true; |
1355 | | else |
1356 | | ret = mhd_socket_error_get_from_sys_err (sk_err); |
1357 | | } |
1358 | | } |
1359 | | else |
1360 | | sent_something = true; |
1361 | | |
1362 | | if (sent_something) |
1363 | | { |
1364 | | mhd_assert (0 <= len); |
1365 | | mhd_assert (SIZE_MAX >= len); |
1366 | | sent_bytes = (size_t) len; |
1367 | | } |
1368 | | } |
1369 | | #else |
1370 | | #error No sendfile() function |
1371 | | #endif |
1372 | |
|
1373 | 0 | mhd_assert (send_size >= sent_bytes); |
1374 | | |
1375 | | /* Some platforms indicate "beyond of the end of the file" by returning |
1376 | | * success with zero bytes. Let filereader to re-detect this kind of error. */ |
1377 | 0 | if ((fallback_to_filereader) || |
1378 | 0 | ((mhd_SOCKET_ERR_NO_ERROR == ret) && (0 == sent_bytes))) |
1379 | 0 | { /* Retry to send with file reader and standard 'send()'. */ |
1380 | 0 | c->rp.response->cntn.file.use_sf = false; |
1381 | 0 | return mhd_SOCKET_ERR_INTR; |
1382 | 0 | } |
1383 | | |
1384 | 0 | if ((mhd_SOCKET_ERR_AGAIN == ret) || ! c->sk.props.is_nonblck || |
1385 | 0 | ((mhd_SOCKET_ERR_NO_ERROR == ret) && (send_size > sent_bytes))) |
1386 | 0 | c->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ |
1387 | 0 | (((unsigned int) c->sk.ready) |
1388 | 0 | & (~(enum mhd_SocketNetState) |
1389 | 0 | mhd_SOCKET_NET_STATE_SEND_READY)); |
1390 | |
|
1391 | 0 | if (mhd_SOCKET_ERR_NO_ERROR != ret) |
1392 | 0 | return ret; |
1393 | | |
1394 | | /* If there is a need to push the data from network buffers |
1395 | | * call post_send_setopt(). */ |
1396 | | /* It's unknown whether sendfile() will be used in the next |
1397 | | * response so assume that next response will be the same. */ |
1398 | 0 | if ((push_data) && |
1399 | 0 | (send_size == sent_bytes)) |
1400 | 0 | post_send_setopt (c, true, push_data); |
1401 | |
|
1402 | 0 | *sent = sent_bytes; |
1403 | 0 | return ret; |
1404 | 0 | } |
1405 | | |
1406 | | |
1407 | | #endif /* mhd_USE_SENDFILE */ |
1408 | | |
1409 | | #if defined(mhd_USE_VECT_SEND) |
1410 | | |
1411 | | |
1412 | | /** |
1413 | | * Function sends iov data by system sendmsg or writev function. |
1414 | | * |
1415 | | * Connection must be in non-TLS (non-HTTPS) mode. |
1416 | | * |
1417 | | * @param connection the MHD connection structure |
1418 | | * @param r_iov the pointer to iov data structure with tracking |
1419 | | * @param push_data set to true to force push the data to the network from |
1420 | | * system buffers (usually set for the last piece of data), |
1421 | | * set to false to prefer holding incomplete network packets |
1422 | | * (more data will be send for the same reply). |
1423 | | * @param[out] sent the pointer to get amount of actually sent bytes |
1424 | | * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets |
1425 | | * the sent size) or socket error |
1426 | | */ |
1427 | | static MHD_FN_PAR_NONNULL_ALL_ |
1428 | | MHD_FN_PAR_OUT_ (4) enum mhd_SocketError |
1429 | | send_iov_nontls (struct MHD_Connection *restrict connection, |
1430 | | struct mhd_iovec_track *const restrict r_iov, |
1431 | | bool push_data, |
1432 | | size_t *restrict sent) |
1433 | 0 | { |
1434 | 0 | bool send_error; |
1435 | 0 | size_t items_to_send; |
1436 | 0 | #ifndef MHD_SOCKETS_KIND_WINSOCK |
1437 | 0 | ssize_t res; |
1438 | 0 | #endif |
1439 | 0 | #ifdef HAVE_SENDMSG |
1440 | 0 | struct msghdr msg; |
1441 | | #elif defined(MHD_SOCKETS_KIND_WINSOCK) |
1442 | | DWORD bytes_sent; |
1443 | | DWORD cnt_w; |
1444 | | #endif /* MHD_SOCKETS_KIND_WINSOCK */ |
1445 | |
|
1446 | 0 | mhd_assert (! mhd_C_HAS_TLS (connection)); |
1447 | 0 | mhd_assert (! mhd_D_HAS_TLS (connection->daemon)); |
1448 | |
|
1449 | 0 | mhd_assert (MHD_INVALID_SOCKET != connection->sk.fd); |
1450 | 0 | mhd_assert (mhd_HTTP_STAGE_CLOSED != connection->stage); |
1451 | |
|
1452 | 0 | send_error = false; |
1453 | 0 | items_to_send = r_iov->cnt - r_iov->sent; |
1454 | 0 | #ifdef mhd_IOV_MAX |
1455 | 0 | if (mhd_IOV_MAX < items_to_send) |
1456 | 0 | { |
1457 | 0 | mhd_assert (0 < mhd_IOV_MAX); |
1458 | 0 | if (0 == mhd_IOV_MAX) |
1459 | 0 | return mhd_SOCKET_ERR_INTERNAL; /* Should never happen */ |
1460 | 0 | items_to_send = mhd_IOV_MAX; |
1461 | 0 | push_data = false; /* Incomplete response */ |
1462 | 0 | } |
1463 | 0 | #endif /* mhd_IOV_MAX */ |
1464 | 0 | #ifdef HAVE_SENDMSG |
1465 | 0 | memset (&msg, 0, sizeof(struct msghdr)); |
1466 | 0 | msg.msg_name = NULL; |
1467 | 0 | msg.msg_namelen = 0; |
1468 | 0 | msg.msg_iov = r_iov->iov + r_iov->sent; |
1469 | 0 | msg.msg_iovlen = items_to_send; |
1470 | 0 | msg.msg_control = NULL; |
1471 | 0 | msg.msg_controllen = 0; |
1472 | 0 | msg.msg_flags = 0; |
1473 | |
|
1474 | 0 | pre_send_setopt (connection, true, push_data); |
1475 | 0 | res = sendmsg (connection->sk.fd, &msg, |
1476 | 0 | mhd_MSG_NOSIGNAL |
1477 | 0 | # ifdef mhd_USE_MSG_MORE |
1478 | 0 | | (push_data ? 0 : MSG_MORE) |
1479 | 0 | # endif |
1480 | 0 | ); |
1481 | 0 | if (0 < res) |
1482 | 0 | *sent = (size_t) res; |
1483 | 0 | else |
1484 | 0 | send_error = true; |
1485 | | #elif defined(HAVE_WRITEV) |
1486 | | pre_send_setopt (connection, false, push_data); |
1487 | | res = writev (connection->sk.fd, r_iov->iov + r_iov->sent, |
1488 | | items_to_send); |
1489 | | if (0 < res) |
1490 | | *sent = (size_t) res; |
1491 | | else |
1492 | | send_error = true; |
1493 | | #elif defined(MHD_SOCKETS_KIND_WINSOCK) |
1494 | | #ifdef _WIN64 |
1495 | | if (items_to_send > ULONG_MAX) |
1496 | | { |
1497 | | cnt_w = ULONG_MAX; |
1498 | | push_data = false; /* Incomplete response */ |
1499 | | } |
1500 | | else |
1501 | | cnt_w = (DWORD) items_to_send; |
1502 | | #else /* ! _WIN64 */ |
1503 | | cnt_w = (DWORD) items_to_send; |
1504 | | #endif /* ! _WIN64 */ |
1505 | | pre_send_setopt (connection, true, push_data); |
1506 | | if (0 == WSASend (connection->sk.fd, |
1507 | | (LPWSABUF) (r_iov->iov + r_iov->sent), |
1508 | | cnt_w, |
1509 | | &bytes_sent, 0, NULL, NULL)) |
1510 | | *sent = (size_t) bytes_sent; |
1511 | | else |
1512 | | send_error = true; |
1513 | | #else /* !HAVE_SENDMSG && !HAVE_WRITEV && !MHD_SOCKETS_KIND_WINSOCK */ |
1514 | | #error No vector-send function available |
1515 | | #endif |
1516 | |
|
1517 | 0 | if (send_error) |
1518 | 0 | { |
1519 | 0 | enum mhd_SocketError err; |
1520 | |
|
1521 | 0 | err = mhd_socket_error_get_from_sys_err (mhd_SCKT_GET_LERR ()); |
1522 | |
|
1523 | 0 | if (mhd_SOCKET_ERR_AGAIN == err) |
1524 | 0 | connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ |
1525 | 0 | (((unsigned int) connection->sk.ready) |
1526 | 0 | & (~(enum mhd_SocketNetState) |
1527 | 0 | mhd_SOCKET_NET_STATE_SEND_READY)); |
1528 | |
|
1529 | 0 | return err; |
1530 | 0 | } |
1531 | | |
1532 | | /* Some data has been sent */ |
1533 | 0 | if (1) |
1534 | 0 | { |
1535 | 0 | size_t track_sent = (size_t) *sent; |
1536 | |
|
1537 | 0 | if (! connection->sk.props.is_nonblck) |
1538 | 0 | connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ |
1539 | 0 | (((unsigned int) connection->sk.ready) |
1540 | 0 | & (~(enum mhd_SocketNetState) |
1541 | 0 | mhd_SOCKET_NET_STATE_SEND_READY)); |
1542 | | |
1543 | | /* Adjust the internal tracking information for the iovec to |
1544 | | * take this last send into account. */ |
1545 | 0 | while ((0 != track_sent) && (r_iov->iov[r_iov->sent].iov_len <= track_sent)) |
1546 | 0 | { |
1547 | 0 | track_sent -= r_iov->iov[r_iov->sent].iov_len; |
1548 | 0 | r_iov->sent++; /* The iov element has been completely sent */ |
1549 | 0 | mhd_assert ((r_iov->cnt > r_iov->sent) || (0 == track_sent)); |
1550 | 0 | } |
1551 | |
|
1552 | 0 | if (r_iov->cnt == r_iov->sent) |
1553 | 0 | post_send_setopt (connection, true, push_data); |
1554 | 0 | else |
1555 | 0 | { |
1556 | 0 | connection->sk.ready = (enum mhd_SocketNetState) /* Clear 'send-ready' */ |
1557 | 0 | (((unsigned int) connection->sk.ready) |
1558 | 0 | & (~(enum mhd_SocketNetState) |
1559 | 0 | mhd_SOCKET_NET_STATE_SEND_READY)); |
1560 | 0 | if (0 != track_sent) |
1561 | 0 | { |
1562 | 0 | mhd_assert (r_iov->cnt > r_iov->sent); |
1563 | | /* The last iov element has been partially sent */ |
1564 | 0 | r_iov->iov[r_iov->sent].iov_base = |
1565 | 0 | (mhd_IOV_ELMN_PTR_TYPE) |
1566 | 0 | (((uint8_t *) r_iov->iov[r_iov->sent].iov_base) |
1567 | 0 | + track_sent); |
1568 | 0 | r_iov->iov[r_iov->sent].iov_len -= (mhd_iov_elmn_size) track_sent; |
1569 | 0 | } |
1570 | 0 | } |
1571 | 0 | } |
1572 | |
|
1573 | 0 | return mhd_SOCKET_ERR_NO_ERROR; |
1574 | 0 | } |
1575 | | |
1576 | | |
1577 | | #endif /* mhd_USE_VECT_SEND */ |
1578 | | |
1579 | | #if ! defined(mhd_USE_VECT_SEND) || defined(MHD_SUPPORT_HTTPS) || \ |
1580 | | defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED) |
1581 | | |
1582 | | |
1583 | | /** |
1584 | | * Function sends iov data by sending buffers one-by-one by standard |
1585 | | * data send function. |
1586 | | * |
1587 | | * Connection could be in HTTPS or non-HTTPS mode. |
1588 | | * |
1589 | | * @param connection the MHD connection structure |
1590 | | * @param r_iov the pointer to iov data structure with tracking |
1591 | | * @param push_data set to true to force push the data to the network from |
1592 | | * system buffers (usually set for the last piece of data), |
1593 | | * set to false to prefer holding incomplete network packets |
1594 | | * (more data will be send for the same reply). |
1595 | | * @param[out] sent the pointer to get amount of actually sent bytes |
1596 | | * @return mhd_SOCKET_ERR_NO_ERROR if send succeed (the @a sent gets |
1597 | | * the sent size) or socket error |
1598 | | */ |
1599 | | static MHD_FN_PAR_NONNULL_ALL_ |
1600 | | MHD_FN_PAR_OUT_ (4) enum mhd_SocketError |
1601 | | send_iov_emu (struct MHD_Connection *restrict connection, |
1602 | | struct mhd_iovec_track *const restrict r_iov, |
1603 | | bool push_data, |
1604 | | size_t *restrict sent) |
1605 | 0 | { |
1606 | 0 | const bool non_blk = connection->sk.props.is_nonblck; |
1607 | 0 | size_t total_sent; |
1608 | 0 | size_t max_elelements_to_sent; |
1609 | |
|
1610 | 0 | mhd_assert (NULL != r_iov->iov); |
1611 | 0 | total_sent = 0; |
1612 | 0 | max_elelements_to_sent = 8; /* Do not make too many sys-calls for just one connection */ |
1613 | 0 | do |
1614 | 0 | { |
1615 | 0 | enum mhd_SocketError res; |
1616 | 0 | size_t sent_el_size; |
1617 | |
|
1618 | 0 | if (total_sent > (size_t) (r_iov->iov[r_iov->sent].iov_len + total_sent)) |
1619 | 0 | break; /* return value would overflow */ |
1620 | | |
1621 | 0 | res = mhd_send_data (connection, |
1622 | 0 | r_iov->iov[r_iov->sent].iov_len, |
1623 | 0 | (const char *) r_iov->iov[r_iov->sent].iov_base, |
1624 | 0 | push_data && (r_iov->cnt == r_iov->sent + 1), |
1625 | 0 | &sent_el_size); |
1626 | 0 | if (mhd_SOCKET_ERR_NO_ERROR == res) |
1627 | 0 | { |
1628 | | /* Result is an error */ |
1629 | 0 | if (0 == total_sent) |
1630 | 0 | return res; /* Nothing was sent, return error as is */ |
1631 | | |
1632 | 0 | if (mhd_SOCKET_ERR_IS_HARD (res)) |
1633 | 0 | return res; /* Any kind of a hard error */ |
1634 | | |
1635 | 0 | break; /* Return the amount of the sent data */ |
1636 | 0 | } |
1637 | | |
1638 | 0 | total_sent += sent_el_size; |
1639 | |
|
1640 | 0 | if (r_iov->iov[r_iov->sent].iov_len != sent_el_size) |
1641 | 0 | { |
1642 | | /* Incomplete buffer has been sent. |
1643 | | * Adjust buffer of the last element. */ |
1644 | 0 | r_iov->iov[r_iov->sent].iov_base = |
1645 | 0 | (mhd_IOV_ELMN_PTR_TYPE) |
1646 | 0 | (((uint8_t *) r_iov->iov[r_iov->sent].iov_base) + sent_el_size); |
1647 | 0 | r_iov->iov[r_iov->sent].iov_len -= (mhd_iov_elmn_size) sent_el_size; |
1648 | |
|
1649 | 0 | break; /* Return the amount of the sent data */ |
1650 | 0 | } |
1651 | | /* The iov element has been completely sent */ |
1652 | 0 | r_iov->sent++; |
1653 | 0 | } while ((r_iov->cnt > r_iov->sent) && 0 != (--max_elelements_to_sent) && |
1654 | 0 | (non_blk)); |
1655 | | |
1656 | 0 | mhd_assert (0 != total_sent); |
1657 | 0 | *sent = total_sent; |
1658 | 0 | return mhd_SOCKET_ERR_NO_ERROR; |
1659 | 0 | } |
1660 | | |
1661 | | |
1662 | | #endif /* !mhd_USE_VECT_SEND || MHD_SUPPORT_HTTPS |
1663 | | || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */ |
1664 | | |
1665 | | MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ |
1666 | | MHD_FN_PAR_OUT_ (4) enum mhd_SocketError |
1667 | | mhd_send_iovec (struct MHD_Connection *restrict connection, |
1668 | | struct mhd_iovec_track *const restrict r_iov, |
1669 | | bool push_data, |
1670 | | size_t *restrict sent) |
1671 | 0 | { |
1672 | 0 | #ifdef mhd_USE_VECT_SEND |
1673 | 0 | #if defined(MHD_SUPPORT_HTTPS) || \ |
1674 | 0 | defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED) |
1675 | 0 | bool use_iov_send = true; |
1676 | 0 | #endif /* MHD_SUPPORT_HTTPS || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */ |
1677 | 0 | #endif /* mhd_USE_VECT_SEND */ |
1678 | |
|
1679 | 0 | mhd_assert (NULL != connection->rp.resp_iov.iov); |
1680 | 0 | mhd_assert (mhd_RESPONSE_CONTENT_DATA_IOVEC == \ |
1681 | 0 | connection->rp.response->cntn_dtype); |
1682 | 0 | mhd_assert (connection->rp.resp_iov.cnt > connection->rp.resp_iov.sent); |
1683 | 0 | #ifdef mhd_USE_VECT_SEND |
1684 | 0 | #if defined(MHD_SUPPORT_HTTPS) || \ |
1685 | 0 | defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED) |
1686 | 0 | use_iov_send = use_iov_send && |
1687 | 0 | (! mhd_C_HAS_TLS (connection)); |
1688 | | #ifdef mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED |
1689 | | use_iov_send = use_iov_send && (connection->daemon->sigpipe_blocked || |
1690 | | connection->sk.props.has_spipe_supp); |
1691 | | #endif /* mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */ |
1692 | 0 | if (use_iov_send) |
1693 | 0 | #endif /* MHD_SUPPORT_HTTPS || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */ |
1694 | 0 | return send_iov_nontls (connection, r_iov, push_data, sent); |
1695 | 0 | #endif /* mhd_USE_VECT_SEND */ |
1696 | | |
1697 | 0 | #if ! defined(mhd_USE_VECT_SEND) || defined(MHD_SUPPORT_HTTPS) || \ |
1698 | 0 | defined(mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED) |
1699 | 0 | return send_iov_emu (connection, r_iov, push_data, sent); |
1700 | 0 | #endif /* !mhd_USE_VECT_SEND || MHD_SUPPORT_HTTPS |
1701 | | || mhd_VECT_SEND_NEEDS_SPIPE_SUPPRESSED */ |
1702 | 0 | } |