42
42
#undef MONGOC_LOG_DOMAIN
43
43
#define MONGOC_LOG_DOMAIN "stream-tls"
44
44
45
+ #define MONGOC_STREAM_TLS_BUFFER_SIZE 4096
46
+
45
47
46
48
/**
47
49
* mongoc_stream_tls_t:
@@ -448,6 +450,53 @@ _mongoc_stream_tls_flush (mongoc_stream_t *stream)
448
450
}
449
451
450
452
453
+ static ssize_t
454
+ _mongoc_stream_tls_write (mongoc_stream_tls_t * tls ,
455
+ char * buf ,
456
+ size_t buf_len )
457
+ {
458
+ ssize_t ret ;
459
+
460
+ int64_t now ;
461
+ int64_t expire = 0 ;
462
+
463
+ BSON_ASSERT (tls );
464
+ BSON_ASSERT (buf );
465
+ BSON_ASSERT (buf_len );
466
+
467
+ if (tls -> timeout_msec >= 0 ) {
468
+ expire = bson_get_monotonic_time () + (tls -> timeout_msec * 1000UL );
469
+ }
470
+
471
+ ret = BIO_write (tls -> bio , buf , buf_len );
472
+
473
+ if (ret < 0 ) {
474
+ return ret ;
475
+ }
476
+
477
+ if (expire ) {
478
+ now = bson_get_monotonic_time ();
479
+
480
+ if ((expire - now ) < 0 ) {
481
+ if (ret < buf_len ) {
482
+ mongoc_counter_streams_timeout_inc ();
483
+ #ifdef _WIN32
484
+ errno = WSAETIMEDOUT ;
485
+ #else
486
+ errno = ETIMEDOUT ;
487
+ #endif
488
+ }
489
+
490
+ tls -> timeout_msec = 0 ;
491
+ } else {
492
+ tls -> timeout_msec = (expire - now ) / 1000L ;
493
+ }
494
+ }
495
+
496
+ return ret ;
497
+ }
498
+
499
+
451
500
/*
452
501
*--------------------------------------------------------------------------
453
502
*
@@ -473,62 +522,112 @@ _mongoc_stream_tls_writev (mongoc_stream_t *stream,
473
522
int32_t timeout_msec )
474
523
{
475
524
mongoc_stream_tls_t * tls = (mongoc_stream_tls_t * )stream ;
525
+ char buf [MONGOC_STREAM_TLS_BUFFER_SIZE ];
526
+
476
527
ssize_t ret = 0 ;
528
+ ssize_t child_ret ;
477
529
size_t i ;
478
530
size_t iov_pos = 0 ;
479
- int write_ret ;
480
531
481
- int64_t now ;
482
- int64_t expire = 0 ;
532
+ /* There's a bit of a dance to coalesce vectorized writes into
533
+ * MONGOC_STREAM_TLS_BUFFER_SIZE'd writes to avoid lots of small tls
534
+ * packets.
535
+ *
536
+ * The basic idea is that we want to combine writes in the buffer if they're
537
+ * smaller than the buffer, flushing as it gets full. For larger writes, or
538
+ * the last write in the iovec array, we want to ignore the buffer and just
539
+ * write immediately. We take care of doing buffer writes by re-invoking
540
+ * ourself with a single iovec_t, pointing at our stack buffer.
541
+ */
542
+ char * buf_head = buf ;
543
+ char * buf_tail = buf ;
544
+ char * buf_end = buf + MONGOC_STREAM_TLS_BUFFER_SIZE ;
545
+ size_t bytes ;
546
+
547
+ char * to_write = NULL ;
548
+ size_t to_write_len ;
483
549
484
550
BSON_ASSERT (tls );
485
551
BSON_ASSERT (iov );
486
552
BSON_ASSERT (iovcnt );
487
553
488
554
tls -> timeout_msec = timeout_msec ;
489
555
490
- if (timeout_msec >= 0 ) {
491
- expire = bson_get_monotonic_time () + (timeout_msec * 1000UL );
492
- }
493
-
494
556
for (i = 0 ; i < iovcnt ; i ++ ) {
495
557
iov_pos = 0 ;
496
558
497
559
while (iov_pos < iov [i ].iov_len ) {
498
- write_ret = BIO_write (tls -> bio , (char * )iov [i ].iov_base + iov_pos ,
499
- (int )(iov [i ].iov_len - iov_pos ));
560
+ if (buf_head != buf_tail ||
561
+ ((i + 1 < iovcnt ) &&
562
+ ((buf_end - buf_tail ) > (iov [i ].iov_len - iov_pos )))) {
563
+ /* If we have either of:
564
+ * - buffered bytes already
565
+ * - another iovec to send after this one and we don't have more
566
+ * bytes to send than the size of the buffer.
567
+ *
568
+ * copy into the buffer */
569
+
570
+ bytes = BSON_MIN (iov [i ].iov_len - iov_pos , buf_end - buf_tail );
571
+
572
+ memcpy (buf_tail , iov [i ].iov_base + iov_pos , bytes );
573
+ buf_tail += bytes ;
574
+ iov_pos += bytes ;
575
+
576
+ if (buf_tail == buf_end ) {
577
+ /* If we're full, request send */
578
+
579
+ to_write = buf_head ;
580
+ to_write_len = buf_tail - buf_head ;
581
+
582
+ buf_tail = buf_head = buf ;
583
+ }
584
+ } else {
585
+ /* Didn't buffer, so just write it through */
586
+
587
+ to_write = (char * )iov [i ].iov_base + iov_pos ;
588
+ to_write_len = iov [i ].iov_len - iov_pos ;
500
589
501
- if (write_ret < 0 ) {
502
- return write_ret ;
590
+ iov_pos += to_write_len ;
503
591
}
504
592
505
- if (expire ) {
506
- now = bson_get_monotonic_time ();
593
+ if (to_write ) {
594
+ /* We get here if we buffered some bytes and filled the buffer, or
595
+ * if we didn't buffer and have to send out of the iovec */
507
596
508
- if ((expire - now ) < 0 ) {
509
- if (write_ret == 0 ) {
510
- mongoc_counter_streams_timeout_inc ();
511
- #ifdef _WIN32
512
- errno = WSAETIMEDOUT ;
513
- #else
514
- errno = ETIMEDOUT ;
515
- #endif
516
- return -1 ;
517
- }
597
+ child_ret = _mongoc_stream_tls_write (tls , to_write , to_write_len );
518
598
519
- tls -> timeout_msec = 0 ;
520
- } else {
521
- tls -> timeout_msec = (expire - now ) / 1000L ;
599
+ if (child_ret < 0 ) {
600
+ /* Buffer write failed, just return the error */
601
+ return child_ret ;
602
+ }
603
+
604
+ ret += child_ret ;
605
+
606
+ if (child_ret < to_write_len ) {
607
+ /* we timed out, so send back what we could send */
608
+
609
+ return ret ;
522
610
}
611
+
612
+ to_write = NULL ;
523
613
}
614
+ }
615
+ }
616
+
617
+ if (buf_head != buf_tail ) {
618
+ /* If we have any bytes buffered, send */
524
619
525
- ret += write_ret ;
526
- iov_pos += write_ret ;
620
+ child_ret = _mongoc_stream_tls_write (tls , buf_head , buf_tail - buf_head );
621
+
622
+ if (child_ret < 0 ) {
623
+ return child_ret ;
527
624
}
625
+
626
+ ret += child_ret ;
528
627
}
529
628
530
629
if (ret >= 0 ) {
531
- mongoc_counter_streams_egress_add (ret );
630
+ mongoc_counter_streams_egress_add (ret );
532
631
}
533
632
534
633
return ret ;
0 commit comments