aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd/mhd_send.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/microhttpd/mhd_send.c')
-rw-r--r--src/microhttpd/mhd_send.c709
1 files changed, 562 insertions, 147 deletions
diff --git a/src/microhttpd/mhd_send.c b/src/microhttpd/mhd_send.c
index eab145bc..018ba657 100644
--- a/src/microhttpd/mhd_send.c
+++ b/src/microhttpd/mhd_send.c
@@ -1,6 +1,8 @@
1/* 1/*
2 This file is part of libmicrohttpd 2 This file is part of libmicrohttpd
3 Copyright (C) 2019 ng0 <ng0@n0.is> 3 Copyright (C) 2017,2020 Karlson2k (Evgeny Grin), Full re-write of buffering and
4 pushing, many bugs fixes, optimisations, sendfile() porting
5 Copyright (C) 2019 ng0 <ng0@n0.is>, Initial version of send() wrappers
4 6
5 This library is free software; you can redistribute it and/or 7 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public 8 modify it under the terms of the GNU Lesser General Public
@@ -21,9 +23,9 @@
21/** 23/**
22 * @file microhttpd/mhd_send.c 24 * @file microhttpd/mhd_send.c
23 * @brief Implementation of send() wrappers. 25 * @brief Implementation of send() wrappers.
26 * @author Karlson2k (Evgeny Grin)
24 * @author ng0 (N. Gillmann) 27 * @author ng0 (N. Gillmann)
25 * @author Christian Grothoff 28 * @author Christian Grothoff
26 * @author Evgeny Grin
27 */ 29 */
28 30
29/* Worth considering for future improvements and additions: 31/* Worth considering for future improvements and additions:
@@ -32,10 +34,6 @@
32 * large a chunk as possible to the socket. Alternatively, 34 * large a chunk as possible to the socket. Alternatively,
33 * use madvise(..., MADV_SEQUENTIAL). */ 35 * use madvise(..., MADV_SEQUENTIAL). */
34 36
35/* Functions to be used in: send_param_adapter, MHD_send_
36 * and every place where sendfile(), sendfile64(), setsockopt()
37 * are used. */
38
39#include "mhd_send.h" 37#include "mhd_send.h"
40#ifdef MHD_LINUX_SOLARIS_SENDFILE 38#ifdef MHD_LINUX_SOLARIS_SENDFILE
41#include <sys/sendfile.h> 39#include <sys/sendfile.h>
@@ -51,6 +49,7 @@
51#endif /* HAVE_SYS_PARAM_H */ 49#endif /* HAVE_SYS_PARAM_H */
52#include "mhd_assert.h" 50#include "mhd_assert.h"
53 51
52#include "mhd_limits.h"
54 53
55/** 54/**
56 * sendfile() chuck size 55 * sendfile() chuck size
@@ -107,6 +106,121 @@ MHD_send_init_static_vars_ (void)
107 106
108 107
109/** 108/**
109 * Set required TCP_NODELAY state for connection socket
110 *
111 * The function automatically updates sk_nodelay state.
112 * @param connection the connection to manipulate
113 * @param nodelay_state the requested new state of socket
114 * @return true if succeed, false if failed
115 */
116static bool
117connection_set_nodelay_state_ (struct MHD_Connection *connection,
118 bool nodelay_state)
119{
120 const MHD_SCKT_OPT_BOOL_ off_val = 0;
121 const MHD_SCKT_OPT_BOOL_ on_val = 1;
122 int err_code;
123
124 if (0 == setsockopt (connection->socket_fd,
125 IPPROTO_TCP,
126 TCP_NODELAY,
127 (const void *) (nodelay_state ? &on_val : &off_val),
128 sizeof (off_val)))
129 {
130 connection->sk_nodelay = nodelay_state;
131 return true;
132 }
133 err_code = MHD_socket_get_error_ ();
134 switch (err_code)
135 {
136 case ENOTSOCK:
137 /* FIXME: Could be we are talking to a pipe, maybe remember this
138 and avoid all setsockopt() in the future? */
139 break;
140 case EBADF:
141 /* FIXME: should we die hard here? */
142 case EINVAL:
143 /* FIXME: optlen invalid, should at least log this, maybe die */
144 case EFAULT:
145 /* wopsie, should at least log this, FIXME: maybe die */
146 case ENOPROTOOPT:
147 /* optlen unknown, should at least log this */
148 default:
149#ifdef HAVE_MESSAGES
150 MHD_DLOG (connection->daemon,
151 _ ("Setting %s option to %s state failed: %s\n"),
152 "TCP_NODELAY",
153 nodelay_state ? _ ("ON") : _ ("OFF"),
154 MHD_socket_strerr_ (err_code));
155#endif /* HAVE_MESSAGES */
156 break;
157 }
158 return false;
159}
160
161
162#if defined(MHD_TCP_CORK_NOPUSH)
163/**
164 * Set required cork state for connection socket
165 *
166 * The function automatically updates sk_corked state.
167 * @param connection the connection to manipulate
168 * @param cork_state the requested new state of socket
169 * @return true if succeed, false if failed
170 */
171static bool
172connection_set_cork_state_ (struct MHD_Connection *connection,
173 bool cork_state)
174{
175 const MHD_SCKT_OPT_BOOL_ off_val = 0;
176 const MHD_SCKT_OPT_BOOL_ on_val = 1;
177 int err_code;
178
179 if (0 == setsockopt (connection->socket_fd,
180 IPPROTO_TCP,
181 MHD_TCP_CORK_NOPUSH,
182 (const void *) (cork_state ? &on_val : &off_val),
183 sizeof (off_val)))
184 {
185 connection->sk_corked = cork_state;
186 return true;
187 }
188 err_code = MHD_socket_get_error_ ();
189 switch (err_code)
190 {
191 case ENOTSOCK:
192 /* FIXME: Could be we are talking to a pipe, maybe remember this
193 and avoid all setsockopt() in the future? */
194 break;
195 case EBADF:
196 /* FIXME: should we die hard here? */
197 case EINVAL:
198 /* FIXME: optlen invalid, should at least log this, maybe die */
199 case EFAULT:
200 /* wopsie, should at least log this, FIXME: maybe die */
201 case ENOPROTOOPT:
202 /* optlen unknown, should at least log this */
203 default:
204#ifdef HAVE_MESSAGES
205 MHD_DLOG (connection->daemon,
206 _ ("Setting %s option to %s state failed: %s\n"),
207#ifdef TCP_CORK
208 "TCP_CORK",
209#else /* ! TCP_CORK */
210 "TCP_NOPUSH",
211#endif /* ! TCP_CORK */
212 cork_state ? _ ("ON") : _ ("OFF"),
213 MHD_socket_strerr_ (err_code));
214#endif /* HAVE_MESSAGES */
215 break;
216 }
217 return false;
218}
219
220
221#endif /* MHD_TCP_CORK_NOPUSH */
222
223/**
110 * Handle pre-send setsockopt calls. 224 * Handle pre-send setsockopt calls.
111 * 225 *
112 * @param connection the MHD_Connection structure 226 * @param connection the MHD_Connection structure
@@ -126,96 +240,259 @@ pre_send_setopt (struct MHD_Connection *connection,
126 * Final piece is indicated by push_data == true. */ 240 * Final piece is indicated by push_data == true. */
127 const bool buffer_data = (! push_data); 241 const bool buffer_data = (! push_data);
128 242
129#ifdef MHD_USE_MSG_MORE 243 /* The goal is to minimise the total number of additional sys-calls
130 if (plain_send) 244 * before and after send().
245 * The following tricky (over-)complicated algorithm typically use zero,
246 * one or two additional sys-calls (depending on OS) for each response. */
247
248 if (buffer_data)
131 { 249 {
132 /* MSG_MORE is used, no need for extra syscalls! */ 250 /* Need to buffer data if possible. */
133 return; 251#ifdef MHD_USE_MSG_MORE
134 } 252 if (plain_send)
253 return; /* Data is buffered by send() with MSG_MORE flag.
254 * No need to check or change anything. */
135#else /* ! MHD_USE_MSG_MORE */ 255#else /* ! MHD_USE_MSG_MORE */
136 (void) plain_send; /* Mute compiler warning. */ 256 (void) plain_send; /* Mute compiler warning. */
137#endif /* ! MHD_USE_MSG_MORE */ 257#endif /* ! MHD_USE_MSG_MORE */
138 258
139#if defined(MHD_TCP_CORK_NOPUSH) 259#ifdef MHD_TCP_CORK_NOPUSH
140 /* If connection is already in required corked state, do nothing. */ 260 if (_MHD_ON == connection->sk_corked)
141 if (connection->sk_corked == buffer_data) 261 return; /* The connection was already corked. */
142 return; 262
143 if (push_data) 263 if (connection_set_cork_state_ (connection, true))
144 return; /* nothing to do *pre* syscall! BUG: to be fixed */ 264 return; /* The connection has been corked. */
145 ret = MHD_socket_cork_ (connection->socket_fd, 265
146 buffer_data); 266 /* Failed to cork the connection.
147 if (0 != ret) 267 * Really unlikely to happen on TCP connections. */
148 { 268#endif /* MHD_TCP_CORK_NOPUSH */
149 connection->sk_corked = buffer_data; 269 if (_MHD_OFF == connection->sk_nodelay)
270 return; /* TCP_NODELAY was not set for the socket.
271 * Nagle's algorithm will buffer some data. */
272
273 /* Try to reset TCP_NODELAY state for the socket.
274 * Ignore possible error as no other options exist to
275 * buffer data. */
276 connection_set_nodelay_state_ (connection, false);
277 /* TCP_NODELAY has been (hopefully) reset for the socket.
278 * Nagle's algorithm will buffer some data. */
150 return; 279 return;
151 } 280 }
152 switch (errno) 281
153 { 282 /* Need to push data after send() */
154 case ENOTSOCK: 283 /* Prefer to make additional sys-call after the send()
155 /* FIXME: Could be we are talking to a pipe, maybe remember this 284 * as the next send() may consume only part of the
156 and avoid all setsockopt() in the future? */ 285 * prepared data and additional send() may be required. */
157 break; 286#ifdef MHD_TCP_CORK_NOPUSH
158 case EBADF: 287#ifdef _MHD_CORK_RESET_PUSH_DATA
159 /* FIXME: should we die hard here? */ 288#ifdef _MHD_CORK_RESET_PUSH_DATA_ALWAYS
160 break; 289 /* Data can be pushed immediately by uncorking socket regardless of
161 case EINVAL: 290 * cork state before. */
162 /* FIXME: optlen invalid, should at least log this, maybe die */ 291 /* This is typical for Linux, no other kernel with
163#ifdef HAVE_MESSAGES 292 * such behavior are known so far. */
164 MHD_DLOG (connection->daemon, 293
165 _ ("optlen invalid: %s\n"), 294 /* No need to check the current state of TCP_CORK / TCP_NOPUSH
166 MHD_socket_last_strerr_ ()); 295 * as reset of cork will push the data anyway. */
167#endif 296 return; /* Data may be pushed by resetting of
168 break; 297 * TCP_CORK / TCP_NOPUSH after send() */
169 case EFAULT: 298#else /* ! _MHD_CORK_RESET_PUSH_DATA_ALWAYS */
170 /* wopsie, should at least log this, FIXME: maybe die */ 299 /* Reset of TCP_CORK / TCP_NOPUSH will push the data
171#ifdef HAVE_MESSAGES 300 * only if socket is corked. */
172 MHD_DLOG (connection->daemon, 301
173 _ ( 302#ifdef _MHD_NODELAY_SET_PUSH_DATA_ALWAYS
174 "The address pointed to by optval is not a valid part of the process address space: %s\n"), 303 /* Data can be pushed immediately by setting TCP_NODELAY regardless
175 MHD_socket_last_strerr_ ()); 304 * of TCP_NODDELAY or corking state before. */
176#endif 305
177 break; 306 /* Dead code currently, no known kernels with such behavior. */
178 case ENOPROTOOPT: 307 return; /* Data may be pushed by setting of TCP_NODELAY after send().
179 /* optlen unknown, should at least log this */ 308 No need to make extra sys-calls before send().*/
180#ifdef HAVE_MESSAGES 309#else /* ! _MHD_NODELAY_SET_PUSH_DATA_ALWAYS */
181 MHD_DLOG (connection->daemon, 310
182 _ ("The option is unknown: %s\n"), 311#ifdef _MHD_NODELAY_SET_PUSH_DATA
183 MHD_socket_last_strerr_ ()); 312 /* Setting of TCP_NODELAY will push the data only if
184#endif 313 * both TCP_NODELAY and TCP_CORK / TCP_NOPUSH were not set. */
185 break; 314
186 default: 315 /* Data can be pushed immediately by uncorking socket if
187 /* any others? man page does not list more... */ 316 * socket was corked before or by setting TCP_NODELAY if
188 break; 317 * socket was not corked and TCP_NODELAY was not set before. */
189 } 318
190#else 319 /* Dead code currently as Linux is the only kernel that push
191 /* CORK/NOPUSH do not exist on this platform, 320 * data by setting of TCP_NODELAY and Linux push data always. */
192 Turning on/off of Naggle's algorithm 321#else /* ! _MHD_NODELAY_SET_PUSH_DATA */
193 (otherwise we keep it always off) */ 322 /* Data can be pushed immediately by uncorking socket or
194 if (connection->sk_nodelay == push_data) 323 * can be pushed by send() on uncorked socket if
324 * TCP_NODELAY was set *before*. */
325
326 /* This is typical FreeBSD behavior. */
327#endif /* ! _MHD_NODELAY_SET_PUSH_DATA */
328
329 if (_MHD_ON == connection->sk_corked)
330 return; /* Socket is corked. Data can be pushed by resetting of
331 * TCP_CORK / TCP_NOPUSH after send() */
332 else if (_MHD_OFF == connection->sk_corked)
195 { 333 {
196 /* nothing to do, success! */ 334 /* The socket is not corked. */
197 return; 335 if (_MHD_ON == connection->sk_nodelay)
336 return; /* TCP_NODELAY was already set,
337 * data will be pushed automatically by the next send() */
338#ifdef _MHD_NODELAY_SET_PUSH_DATA
339 else if (_MHD_UNKNOWN == connection->sk_nodelay)
340 {
341 /* Setting TCP_NODELAY may push data.
342 * Cork socket here and uncork after send(). */
343 if (connection_set_cork_state_ (connection, true))
344 return; /* The connection has been corked.
345 * Data can be pushed by resetting of
346 * TCP_CORK / TCP_NOPUSH after send() */
347 else
348 {
349 /* The socket cannot be corked.
350 * Really unlikely to happen on TCP connections */
351 /* Have to set TCP_NODELAY.
352 * If TCP_NODELAY real system state was OFF then
353 * already buffered data may be pushed here, but this is unlikely
354 * to happen as it is only a backup solution when corking has failed.
355 * Ignore possible error here as no other options exist to
356 * push data. */
357 connection_set_nodelay_state_ (connection, true);
358 /* TCP_NODELAY has been (hopefully) set for the socket.
359 * The data will be pushed by the next send(). */
360 return;
361 }
362 }
363#endif /* _MHD_NODELAY_SET_PUSH_DATA */
364 else
365 {
366#ifdef _MHD_NODELAY_SET_PUSH_DATA
367 /* TCP_NODELAY was switched off and
368 * the socket is not corked. */
369#else /* ! _MHD_NODELAY_SET_PUSH_DATA */
370 /* Socket is not corked and TCP_NODELAY was not set or unknown. */
371#endif /* ! _MHD_NODELAY_SET_PUSH_DATA */
372
373 /* At least one additional sys-call is required. */
374 /* Setting TCP_NODELAY is optimal here as data will be pushed
375 * automatically by the next send() and no additional
376 * sys-call are needed after the send(). */
377 if (connection_set_nodelay_state_ (connection, true))
378 return;
379 else
380 {
381 /* Failed to set TCP_NODELAY for the socket.
382 * Really unlikely to happen on TCP connections. */
383 /* Cork the socket here and make additional sys-call
384 * to uncork the socket after send(). */
385 /* Ignore possible error here as no other options exist to
386 * push data. */
387 connection_set_cork_state_ (connection, true);
388 /* The connection has been (hopefully) corked.
389 * Data can be pushed by resetting of TCP_CORK / TCP_NOPUSH
390 * after send() */
391 return;
392 }
393 }
198 } 394 }
199 if (0 == MHD_socket_set_nodelay_ (connection->socket_fd, 395 /* Corked state is unknown. Need to make sys-call here otherwise
200 (push_data))) 396 * data may not be pushed. */
201 connection->sk_nodelay = push_data; 397 if (connection_set_cork_state_ (connection, true))
202#endif 398 return; /* The connection has been corked.
399 * Data can be pushed by resetting of
400 * TCP_CORK / TCP_NOPUSH after send() */
401 /* The socket cannot be corked.
402 * Really unlikely to happen on TCP connections */
403 if (_MHD_ON == connection->sk_nodelay)
404 return; /* TCP_NODELAY was already set,
405 * data will be pushed by the next send() */
406 /* Have to set TCP_NODELAY. */
407#ifdef _MHD_NODELAY_SET_PUSH_DATA
408 /* If TCP_NODELAY state was unknown (external connection) then
409 * already buffered data may be pushed here, but this is unlikely
410 * to happen as it is only a backup solution when corking has failed. */
411#endif /* _MHD_NODELAY_SET_PUSH_DATA */
412 /* Ignore possible error here as no other options exist to
413 * push data. */
414 connection_set_nodelay_state_ (connection, true);
415 /* TCP_NODELAY has been (hopefully) set for the socket.
416 * The data will be pushed by the next send(). */
417 return;
418#endif /* ! _MHD_NODELAY_SET_PUSH_DATA_ALWAYS */
419#endif /* ! _MHD_CORK_RESET_PUSH_DATA_ALWAYS */
420#else /* ! _MHD_CORK_RESET_PUSH_DATA */
421 /* Neither uncorking the socket or setting TCP_NODELAY
422 * push the data immediately. */
423 /* The only way to push the data is to use send() on uncorked
424 * socket with TCP_NODELAY switched on . */
425
426 /* This is a typical *BSD (except FreeBSD) and Darwin behavior. */
427
428 /* Uncork socket if socket wasn't uncorked. */
429 if (_MHD_OFF != connection->sk_corked)
430 connection_set_cork_state_ (connection, false);
431
432 /* Set TCP_NODELAY if it wasn't set. */
433 if (_MHD_ON != connection->sk_nodelay)
434 connection_set_nodelay_state_ (connection, true);
435
436 return;
437#endif /* ! _MHD_CORK_RESET_PUSH_DATA */
438#else /* ! MHD_TCP_CORK_NOPUSH */
439 /* Buffering of data is controlled only by
440 * Nagel's algorithm. */
441 /* Set TCP_NODELAY if it wasn't set. */
442 if (_MHD_ON != connection->sk_nodelay)
443 connection_set_nodelay_state_ (connection, true);
444#endif /* ! MHD_TCP_CORK_NOPUSH */
445}
446
447
448#ifndef _MHD_CORK_RESET_PUSH_DATA_ALWAYS
449/**
450 * Send zero-sized data
451 *
452 * This function use send of zero-sized data to kick data from the socket
453 * buffers to the network. The socket must not be corked and must have
454 * TCP_NODELAY switched on.
455 * Used only as last resort option, when other options are failed due to
456 * some errors.
457 * Should not be called on typical data processing.
458 * @return true if succeed, false if failed
459 */
460static bool
461zero_send_ (struct MHD_Connection *connection)
462{
463 int dummy;
464 mhd_assert (_MHD_OFF == connection->sk_corked);
465 mhd_assert (_MHD_ON == connection->sk_nodelay);
466
467 dummy = 0; /* Mute compiler and analyzer warnings */
468
469 if (0 == MHD_send_ (connection->socket_fd, &dummy, 0))
470 return true;
471
472#ifdef HAVE_MESSAGES
473 MHD_DLOG (connection->daemon,
474 _ ("Zero-send failed: %s\n"),
475 MHD_socket_last_strerr_ () );
476#endif /* HAVE_MESSAGES */
477 return false;
203} 478}
204 479
205 480
481#endif /* ! _MHD_CORK_RESET_PUSH_DATA_ALWAYS */
482
206/** 483/**
207 * Handle post-send setsockopt calls. 484 * Handle post-send setsockopt calls.
208 * 485 *
209 * @param connection the MHD_Connection structure 486 * @param connection the MHD_Connection structure
210 * @param plain_send set to true if plain send() or sendmsg() have been 487 * @param plain_send_next set to true if plain send() or sendmsg() will be
211 * called, 488 * called next,
212 * set to false if TLS socket send(), sendfile() or 489 * set to false if TLS socket send(), sendfile() or
213 * writev() has been called. 490 * writev() will be called next.
214 * @param push_data whether to push data to the network from buffers 491 * @param push_data whether to push data to the network from buffers
215 */ 492 */
216static void 493static void
217post_send_setopt (struct MHD_Connection *connection, 494post_send_setopt (struct MHD_Connection *connection,
218 bool plain_send, 495 bool plain_send_next,
219 bool push_data) 496 bool push_data)
220{ 497{
221 int ret; 498 int ret;
@@ -223,70 +500,163 @@ post_send_setopt (struct MHD_Connection *connection,
223 * Final piece is indicated by push_data == true. */ 500 * Final piece is indicated by push_data == true. */
224 const bool buffer_data = (! push_data); 501 const bool buffer_data = (! push_data);
225 502
503 if (buffer_data)
504 return; /* Nothing to do after send(). */
505
506#ifndef MHD_USE_MSG_MORE
507 (void) plain_send_next; /* Mute compiler warning */
508#endif /* ! MHD_USE_MSG_MORE */
509
510 /* Need to push data. */
511#ifdef MHD_TCP_CORK_NOPUSH
512#ifdef _MHD_CORK_RESET_PUSH_DATA_ALWAYS
513#ifdef _MHD_NODELAY_SET_PUSH_DATA_ALWAYS
226#ifdef MHD_USE_MSG_MORE 514#ifdef MHD_USE_MSG_MORE
227 if (plain_send) 515 if (_MHD_OFF == connection->sk_corked)
228 { 516 {
229 /* MSG_MORE is used, no need for extra syscalls! */ 517 if (_MHD_ON == connection->sk_nodelay)
230 return; 518 return; /* Data was already pushed by send(). */
519 }
520 /* This is Linux kernel. There are options:
521 * * Push the data by setting of TCP_NODELAY (without change
522 * of the cork on the socket),
523 * * Push the data by resetting of TCP_CORK.
524 * The optimal choice depends on the next final send functions
525 * used on the same socket. If TCP_NODELAY wasn't set then push
526 * data by setting TCP_NODELAY (TCP_NODELAY will not be removed
527 * and is needed to push the data by send() without MSG_MORE).
528 * If send()/sendmsg() will be used next than push data by
529 * reseting of TCP_CORK so next send without MSG_MORE will push
530 * data to the network (without additional sys-call to push data).
531 * If next final send function will not support MSG_MORE (like
532 * sendfile() or TLS-connection) than push data by setting
533 * TCP_NODELAY so socket will remain corked (no additional
534 * sys-call before next send()). */
535 if ((_MHD_ON != connection->sk_nodelay) ||
536 (! plain_send_next))
537 {
538 if (connection_set_nodelay_state_ (connection, true))
539 return; /* Data has been pushed by TCP_NODELAY. */
540 /* Failed to set TCP_NODELAY for the socket.
541 * Really unlikely to happen on TCP connections. */
542 if (connection_set_cork_state_ (connection, false))
543 return; /* Data has been pushed by uncorking the socket. */
544 /* Failed to uncork the socket.
545 * Really unlikely to happen on TCP connections. */
546
547 /* The socket cannot be uncorked, no way to push data */
548 }
549 else
550 {
551 if (connection_set_cork_state_ (connection, false))
552 return; /* Data has been pushed by uncorking the socket. */
553 /* Failed to uncork the socket.
554 * Really unlikely to happen on TCP connections. */
555 if (connection_set_nodelay_state_ (connection, true))
556 return; /* Data has been pushed by TCP_NODELAY. */
557 /* Failed to set TCP_NODELAY for the socket.
558 * Really unlikely to happen on TCP connections. */
559
560 /* The socket cannot be uncorked, no way to push data */
231 } 561 }
232#else /* ! MHD_USE_MSG_MORE */ 562#else /* ! MHD_USE_MSG_MORE */
233 (void) plain_send; /* Mute compiler warning. */ 563 /* Use setting of TCP_NODELAY here to avoid sys-call
564 * for corking the socket during sending of the next response. */
565 if (connection_set_nodelay_state_ (connection, true))
566 return; /* Data was pushed by TCP_NODELAY. */
567 /* Failed to set TCP_NODELAY for the socket.
568 * Really unlikely to happen on TCP connections. */
569 if (connection_set_cork_state_ (connection, false))
570 return; /* Data was pushed by uncorking the socket. */
571 /* Failed to uncork the socket.
572 * Really unlikely to happen on TCP connections. */
573
574 /* The socket remains corked, no way to push data */
234#endif /* ! MHD_USE_MSG_MORE */ 575#endif /* ! MHD_USE_MSG_MORE */
576#else /* ! _MHD_NODELAY_SET_PUSH_DATA_ALWAYS */
577 if (connection_set_cork_state_ (connection, false))
578 return; /* Data was pushed by uncorking the socket. */
579 /* Failed to uncork the socket.
580 * Really unlikely to happen on TCP connections. */
581 return; /* Socket remains corked, no way to push data */
582#endif /* ! _MHD_NODELAY_SET_PUSH_DATA_ALWAYS */
583#else /* ! _MHD_CORK_RESET_PUSH_DATA_ALWAYS */
584 /* This is a typical *BSD or Darwin kernel. */
235 585
236#if defined(MHD_TCP_CORK_NOPUSH) 586 if (_MHD_OFF == connection->sk_corked)
237 /* If connection is already in required corked state, do nothing. */
238 if (connection->sk_corked == buffer_data)
239 return;
240 if (buffer_data)
241 return; /* nothing to do *post* syscall (in fact, we should never
242 get here, as sk_corked should have succeeded in the
243 pre-syscall) */
244 ret = MHD_socket_cork_ (connection->socket_fd,
245 buffer_data);
246 if (0 != ret)
247 { 587 {
248 connection->sk_corked = buffer_data; 588 if (_MHD_ON == connection->sk_nodelay)
249 return; 589 return; /* Data was already pushed by send(). */
590
591 /* Unlikely to reach this code.
592 * TCP_NODELAY should be turned on before send(). */
593 if (connection_set_nodelay_state_ (connection, true))
594 {
595 /* TCP_NODELAY has been set on uncorked socket.
596 * Use zero-send to push the data. */
597 if (zero_send_ (connection))
598 return; /* The data has been pushed by zero-send. */
599 }
600
601 /* Failed to push the data by all means. */
602 /* There is nothing left to try. */
250 } 603 }
251 switch (errno) 604 else
252 { 605 {
253 case ENOTSOCK: 606#ifdef _MHD_CORK_RESET_PUSH_DATA
254 /* FIXME: Could be we are talking to a pipe, maybe remember this 607 enum MHD_tristate old_cork_state = connection->sk_corked;
255 and avoid all setsockopt() in the future? */ 608#endif /* _MHD_CORK_RESET_PUSH_DATA */
256 break; 609 /* The socket is corked or cork state is unknown. */
257 case EBADF: 610
258 /* FIXME: should we die hard here? */ 611 if (connection_set_cork_state_ (connection, false))
259 break; 612 {
260 case EINVAL: 613#ifdef _MHD_CORK_RESET_PUSH_DATA
261 /* FIXME: optlen invalid, should at least log this, maybe die */ 614 /* FreeBSD kernel */
262#ifdef HAVE_MESSAGES 615 if (_MHD_OFF == old_cork_state)
263 MHD_DLOG (connection->daemon, 616 return; /* Data has been pushed by uncorking the socket. */
264 _ ("optlen invalid: %s\n"), 617#endif /* _MHD_CORK_RESET_PUSH_DATA */
265 MHD_socket_last_strerr_ ()); 618
266#endif 619 /* Unlikely to reach this code.
267 break; 620 * The data should be pushed by uncorking (FreeBSD) or
268 case EFAULT: 621 * the socket should be uncorked before send(). */
269 /* wopsie, should at least log this, FIXME: maybe die */ 622 if ((_MHD_ON == connection->sk_nodelay) ||
270#ifdef HAVE_MESSAGES 623 (connection_set_nodelay_state_ (connection, true)))
271 MHD_DLOG (connection->daemon, 624 {
272 _ ( 625 /* TCP_NODELAY is turned ON on uncorked socket.
273 "The address pointed to by optval is not a valid part of the process address space: %s\n"), 626 * Use zero-send to push the data. */
274 MHD_socket_last_strerr_ ()); 627 if (zero_send_ (connection))
275#endif 628 return; /* The data has been pushed by zero-send. */
276 break; 629 }
277 case ENOPROTOOPT: 630 }
278 /* optlen unknown, should at least log this */ 631 /* The socket remains corked. Data cannot be pushed. */
279#ifdef HAVE_MESSAGES
280 MHD_DLOG (connection->daemon,
281 _ ("The option is unknown: %s\n"),
282 MHD_socket_last_strerr_ ());
283#endif
284 break;
285 default:
286 /* any others? man page does not list more... */
287 break;
288 } 632 }
289#endif 633#endif /* ! _MHD_CORK_RESET_PUSH_DATA_ALWAYS */
634#else /* ! MHD_TCP_CORK_NOPUSH */
635 /* Corking is not supported. Buffering is controlled
636 * by TCP_NODELAY only. */
637 mhd_assert (_MHD_ON != connection->sk_corked);
638 if (_MHD_ON == connection->sk_nodelay)
639 return; /* Data was already pushed by send(). */
640
641 /* Unlikely to reach this code.
642 * TCP_NODELAY should be turned on before send(). */
643 if (connection_set_nodelay_state_ (connection, true))
644 {
645 /* TCP_NODELAY has been set.
646 * Use zero-send to push the data. */
647 if (zero_send_ (connection))
648 return; /* The data has been pushed by zero-send. */
649 }
650
651 /* Failed to push the data. */
652#endif /* ! MHD_TCP_CORK_NOPUSH */
653#ifdef HAVE_MESSAGES
654 MHD_DLOG (connection->daemon,
655 _ ("Failed to put the data from buffers to the network. "
656 "Client may experience some delay "
657 "(usually in range 200ms - 5 sec).\n"));
658#endif /* HAVE_MESSAGES */
659 return;
290} 660}
291 661
292 662
@@ -422,8 +792,16 @@ MHD_send_on_connection_ (struct MHD_Connection *connection,
422 connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY; 792 connection->epoll_state &= ~MHD_EPOLL_STATE_WRITE_READY;
423#endif /* EPOLL_SUPPORT */ 793#endif /* EPOLL_SUPPORT */
424 } 794 }
425 post_send_setopt (connection, tls_conn, 795
426 (push_data && (buffer_size == (size_t) ret)) ); 796 /* If there is a need to push the data from network buffers
797 * call post_send_setopt(). */
798 /* If TLS connection is used then next final send() will be
799 * without MSG_MORE support. If non-TLS connection is used
800 * it's unknown whether sendfile() will be used or not so
801 * assume that next call will be the same, like this call. */
802 if ( (push_data) &&
803 (buffer_size == (size_t) ret) )
804 post_send_setopt (connection, (! tls_conn), push_data);
427 805
428 return ret; 806 return ret;
429} 807}
@@ -455,7 +833,9 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection,
455{ 833{
456 MHD_socket s = connection->socket_fd; 834 MHD_socket s = connection->socket_fd;
457 ssize_t ret; 835 ssize_t ret;
836#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
458 struct iovec vector[2]; 837 struct iovec vector[2];
838#endif /* HAVE_SENDMSG || HAVE_WRITEV */
459#ifdef HTTPS_SUPPORT 839#ifdef HTTPS_SUPPORT
460 const bool tls_conn = (connection->daemon->options & MHD_USE_TLS); 840 const bool tls_conn = (connection->daemon->options & MHD_USE_TLS);
461#else /* ! HTTPS_SUPPORT */ 841#else /* ! HTTPS_SUPPORT */
@@ -477,7 +857,13 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection,
477#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) 857#if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV)
478 /* Since we generally give the fully answer, we do not want 858 /* Since we generally give the fully answer, we do not want
479 corking to happen */ 859 corking to happen */
480 pre_send_setopt (connection, (! tls_conn), true); 860 pre_send_setopt (connection,
861#if HAVE_SENDMSG
862 true,
863#elif HAVE_WRITEV
864 false,
865#endif /* HAVE_WRITEV */
866 true);
481 867
482 vector[0].iov_base = (void *) header; 868 vector[0].iov_base = (void *) header;
483 vector[0].iov_len = header_size; 869 vector[0].iov_len = header_size;
@@ -509,10 +895,21 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection,
509 } 895 }
510#endif 896#endif
511 897
512 /* Only if we succeeded sending the full buffer, we need to make sure that 898 /* If there is a need to push the data from network buffers
513 the OS flushes at the end */ 899 * call post_send_setopt(). */
514 post_send_setopt (connection, (! tls_conn), 900 /* If TLS connection is used then next final send() will be
515 (header_size + buffer_size == (size_t) ret)); 901 * without MSG_MORE support. If non-TLS connection is used
902 * it's unknown whether sendfile() will be used or not so
903 * assume that next final send() will be the same, like for
904 * this response. */
905 if ((header_size + buffer_size) == (size_t) ret)
906 post_send_setopt (connection,
907#if HAVE_SENDMSG
908 true,
909#elif HAVE_WRITEV
910 false,
911#endif /* HAVE_WRITEV */
912 true);
516 913
517 return ret; 914 return ret;
518 915
@@ -563,22 +960,36 @@ MHD_send_sendfile_ (struct MHD_Connection *connection)
563 const size_t chunk_size = used_thr_p_c ? MHD_SENFILE_CHUNK_THR_P_C_ : 960 const size_t chunk_size = used_thr_p_c ? MHD_SENFILE_CHUNK_THR_P_C_ :
564 MHD_SENFILE_CHUNK_; 961 MHD_SENFILE_CHUNK_;
565 size_t send_size = 0; 962 size_t send_size = 0;
963 bool push_data;
566 mhd_assert (MHD_resp_sender_sendfile == connection->resp_sender); 964 mhd_assert (MHD_resp_sender_sendfile == connection->resp_sender);
567 mhd_assert (0 == (connection->daemon->options & MHD_USE_TLS)); 965 mhd_assert (0 == (connection->daemon->options & MHD_USE_TLS));
568 966
569 pre_send_setopt (connection, false, true);
570
571 offsetu64 = connection->response_write_position 967 offsetu64 = connection->response_write_position
572 + connection->response->fd_off; 968 + connection->response->fd_off;
573 left = connection->response->total_size - connection->response_write_position;
574 /* Do not allow system to stick sending on single fast connection:
575 * use 128KiB chunks (2MiB for thread-per-connection). */
576 send_size = (left > chunk_size) ? chunk_size : (size_t) left;
577 if (max_off_t < offsetu64) 969 if (max_off_t < offsetu64)
578 { /* Retry to send with standard 'send()'. */ 970 { /* Retry to send with standard 'send()'. */
579 connection->resp_sender = MHD_resp_sender_std; 971 connection->resp_sender = MHD_resp_sender_std;
580 return MHD_ERR_AGAIN_; 972 return MHD_ERR_AGAIN_;
581 } 973 }
974
975 left = connection->response->total_size - connection->response_write_position;
976
977 if ( (uint64_t) SSIZE_MAX > left)
978 left = SSIZE_MAX;
979 /* Do not allow system to stick sending on single fast connection:
980 * use 128KiB chunks (2MiB for thread-per-connection). */
981 if (chunk_size < left)
982 {
983 send_size = chunk_size;
984 push_data = false; /* No need to push data, there is more to send, */
985 }
986 else
987 {
988 send_size = (size_t) left;
989 push_data = true; /* Final piece of data, need to push to the network. */
990 }
991 pre_send_setopt (connection, false, push_data);
992
582#ifdef MHD_LINUX_SOLARIS_SENDFILE 993#ifdef MHD_LINUX_SOLARIS_SENDFILE
583#ifndef HAVE_SENDFILE64 994#ifndef HAVE_SENDFILE64
584 offset = (off_t) offsetu64; 995 offset = (off_t) offsetu64;
@@ -708,9 +1119,13 @@ MHD_send_sendfile_ (struct MHD_Connection *connection)
708 ret = (ssize_t) len; 1119 ret = (ssize_t) len;
709#endif /* HAVE_FREEBSD_SENDFILE */ 1120#endif /* HAVE_FREEBSD_SENDFILE */
710 1121
711 /* Make sure we send the data without delay ONLY if we 1122 /* If there is a need to push the data from network buffers
712 provided the complete response (not on partial write) */ 1123 * call post_send_setopt(). */
713 post_send_setopt (connection, false, (left == (uint64_t) ret)); 1124 /* It's unknown whether sendfile() will be used in the next
1125 * response so assume that next response will be the same. */
1126 if ( (push_data) &&
1127 (send_size == (size_t) ret) )
1128 post_send_setopt (connection, false, push_data);
714 1129
715 return ret; 1130 return ret;
716} 1131}