LCOV - code coverage report
Current view: top level - lib - lwan-io-wrappers.c (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 45 128 35.2 %
Date: 2023-04-18 16:19:03 Functions: 3 5 60.0 %

          Line data    Source code
       1             : /*
       2             :  * lwan - web server
       3             :  * Copyright (c) 2013 L. A. F. Pereira <l@tia.mat.br>
       4             :  *
       5             :  * This program is free software; you can redistribute it and/or
       6             :  * modify it under the terms of the GNU General Public License
       7             :  * as published by the Free Software Foundation; either version 2
       8             :  * of the License, or any later version.
       9             :  *
      10             :  * This program is distributed in the hope that it will be useful,
      11             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :  * GNU General Public License for more details.
      14             :  *
      15             :  * You should have received a copy of the GNU General Public License
      16             :  * along with this program; if not, write to the Free Software
      17             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
      18             :  * USA.
      19             :  */
      20             : 
      21             : #include <assert.h>
      22             : #include <errno.h>
      23             : #include <fcntl.h>
      24             : #include <sys/sendfile.h>
      25             : #include <sys/socket.h>
      26             : #include <sys/types.h>
      27             : #include <unistd.h>
      28             : 
      29             : #include "lwan-io-wrappers.h"
      30             : #include "lwan-private.h"
      31             : 
      32             : static const int MAX_FAILED_TRIES = 5;
      33             : 
      34             : ssize_t
      35          18 : lwan_writev(struct lwan_request *request, struct iovec *iov, int iov_count)
      36             : {
      37          18 :     ssize_t total_written = 0;
      38          18 :     int curr_iov = 0;
      39          18 :     int flags = (request->conn->flags & CONN_CORK) ? MSG_MORE : 0;
      40             : 
      41          18 :     for (int tries = MAX_FAILED_TRIES; tries;) {
      42          18 :         const int remaining_len = (int)(iov_count - curr_iov);
      43             :         ssize_t written;
      44             : 
      45          18 :         if (remaining_len == 1) {
      46           0 :             const struct iovec *vec = &iov[curr_iov];
      47          18 :             return lwan_send(request, vec->iov_base, vec->iov_len, flags);
      48             :         }
      49             : 
      50          18 :         struct msghdr hdr = {
      51          18 :             .msg_iov = iov + curr_iov,
      52          18 :             .msg_iovlen = (size_t)remaining_len,
      53             :         };
      54          18 :         written = sendmsg(request->fd, &hdr, flags);
      55             : 
      56          18 :         if (UNLIKELY(written < 0)) {
      57             :             /* FIXME: Consider short writes as another try as well? */
      58           0 :             tries--;
      59             : 
      60           0 :             switch (errno) {
      61           0 :             case EAGAIN:
      62             :             case EINTR:
      63           0 :                 goto try_again;
      64           0 :             default:
      65           0 :                 goto out;
      66             :             }
      67             :         }
      68             : 
      69          18 :         total_written += written;
      70             : 
      71          68 :         while (curr_iov < iov_count &&
      72          50 :                written >= (ssize_t)iov[curr_iov].iov_len) {
      73          50 :             written -= (ssize_t)iov[curr_iov].iov_len;
      74          50 :             curr_iov++;
      75             :         }
      76             : 
      77          18 :         if (curr_iov == iov_count)
      78          18 :             return total_written;
      79             : 
      80           0 :         iov[curr_iov].iov_base = (char *)iov[curr_iov].iov_base + written;
      81           0 :         iov[curr_iov].iov_len -= (size_t)written;
      82             : 
      83           0 :     try_again:
      84           0 :         coro_yield(request->conn->coro, CONN_CORO_WANT_WRITE);
      85             :     }
      86             : 
      87           0 : out:
      88           0 :     coro_yield(request->conn->coro, CONN_CORO_ABORT);
      89           0 :     __builtin_unreachable();
      90             : }
      91             : 
      92             : ssize_t
      93           0 : lwan_readv(struct lwan_request *request, struct iovec *iov, int iov_count)
      94             : {
      95           0 :     ssize_t total_bytes_read = 0;
      96           0 :     int curr_iov = 0;
      97             : 
      98           0 :     for (int tries = MAX_FAILED_TRIES; tries;) {
      99             :         ssize_t bytes_read =
     100           0 :             readv(request->fd, iov + curr_iov, iov_count - curr_iov);
     101           0 :         if (UNLIKELY(bytes_read < 0)) {
     102             :             /* FIXME: Consider short reads as another try as well? */
     103           0 :             tries--;
     104             : 
     105           0 :             switch (errno) {
     106           0 :             case EAGAIN:
     107             :             case EINTR:
     108           0 :                 goto try_again;
     109           0 :             default:
     110           0 :                 goto out;
     111             :             }
     112             :         }
     113             : 
     114           0 :         total_bytes_read += bytes_read;
     115             : 
     116           0 :         while (curr_iov < iov_count &&
     117           0 :                bytes_read >= (ssize_t)iov[curr_iov].iov_len) {
     118           0 :             bytes_read -= (ssize_t)iov[curr_iov].iov_len;
     119           0 :             curr_iov++;
     120             :         }
     121             : 
     122           0 :         if (curr_iov == iov_count)
     123           0 :             return total_bytes_read;
     124             : 
     125           0 :         iov[curr_iov].iov_base = (char *)iov[curr_iov].iov_base + bytes_read;
     126           0 :         iov[curr_iov].iov_len -= (size_t)bytes_read;
     127             : 
     128           0 :     try_again:
     129           0 :         coro_yield(request->conn->coro, CONN_CORO_WANT_READ);
     130             :     }
     131             : 
     132           0 : out:
     133           0 :     coro_yield(request->conn->coro, CONN_CORO_ABORT);
     134           0 :     __builtin_unreachable();
     135             : }
     136             : 
     137         506 : ssize_t lwan_send(struct lwan_request *request,
     138             :                   const void *buf,
     139             :                   size_t count,
     140             :                   int flags)
     141             : {
     142         506 :     ssize_t total_sent = 0;
     143             : 
     144         506 :     if (request->conn->flags & CONN_CORK)
     145         254 :         flags |= MSG_MORE;
     146             : 
     147         506 :     for (int tries = MAX_FAILED_TRIES; tries;) {
     148         506 :         ssize_t written = send(request->fd, buf, count, flags);
     149         503 :         if (UNLIKELY(written < 0)) {
     150           0 :             tries--;
     151             : 
     152           0 :             switch (errno) {
     153           0 :             case EAGAIN:
     154             :             case EINTR:
     155           0 :                 goto try_again;
     156           0 :             default:
     157           0 :                 goto out;
     158             :             }
     159             :         }
     160             : 
     161         503 :         total_sent += written;
     162         503 :         if ((size_t)total_sent == count)
     163         503 :             return total_sent;
     164           0 :         if ((size_t)total_sent < count)
     165           0 :             buf = (char *)buf + written;
     166             : 
     167           0 :     try_again:
     168           0 :         coro_yield(request->conn->coro, CONN_CORO_WANT_WRITE);
     169             :     }
     170             : 
     171           0 : out:
     172           0 :     coro_yield(request->conn->coro, CONN_CORO_ABORT);
     173           0 :     __builtin_unreachable();
     174             : }
     175             : 
     176             : ssize_t
     177           0 : lwan_recv(struct lwan_request *request, void *buf, size_t count, int flags)
     178             : {
     179           0 :     ssize_t total_recv = 0;
     180             : 
     181           0 :     for (int tries = MAX_FAILED_TRIES; tries;) {
     182           0 :         ssize_t recvd = recv(request->fd, buf, count, flags);
     183           0 :         if (UNLIKELY(recvd < 0)) {
     184           0 :             tries--;
     185             : 
     186           0 :             switch (errno) {
     187           0 :             case EAGAIN:
     188           0 :                 if (flags & MSG_DONTWAIT)
     189           0 :                     return total_recv;
     190             :                 /* Fallthrough */
     191             :             case EINTR:
     192           0 :                 goto try_again;
     193           0 :             default:
     194           0 :                 goto out;
     195             :             }
     196             :         }
     197             : 
     198           0 :         total_recv += recvd;
     199           0 :         if ((size_t)total_recv == count)
     200           0 :             return total_recv;
     201           0 :         if ((size_t)total_recv < count)
     202           0 :             buf = (char *)buf + recvd;
     203             : 
     204           0 :     try_again:
     205           0 :         coro_yield(request->conn->coro, CONN_CORO_WANT_READ);
     206             :     }
     207             : 
     208           0 : out:
     209           0 :     coro_yield(request->conn->coro, CONN_CORO_ABORT);
     210           0 :     __builtin_unreachable();
     211             : }
     212             : 
     213             : #if defined(__linux__)
     214           5 : void lwan_sendfile(struct lwan_request *request,
     215             :                    int in_fd,
     216             :                    off_t offset,
     217             :                    size_t count,
     218             :                    const char *header,
     219             :                    size_t header_len)
     220             : {
     221             :     /* Clamp each chunk to 2^21 bytes[1] to balance throughput and
     222             :      * scalability.  This used to be capped to 2^14 bytes, as that's the
     223             :      * maximum TLS record size[2], but was found to be hurtful for
     224             :      * performance[2], so use the same default value that Nginx uses.
     225             :      *
     226             :      * First chunk is clamped to 2^21 - header_len, because the header is
     227             :      * sent using MSG_MORE.  Subsequent chunks are sized 2^21 bytes.  (Do
     228             :      * this regardless of this connection being TLS or not for simplicity.)
     229             :      *
     230             :      * [1] https://www.kernel.org/doc/html/v5.12/networking/tls.html#sending-tls-application-data
     231             :      * [2] https://github.com/lpereira/lwan/issues/334
     232             :      */
     233           5 :     size_t chunk_size = LWAN_MIN(count, (1ul << 21) - header_len);
     234           5 :     size_t to_be_written = count;
     235             : 
     236           5 :     assert(header_len < (1ul << 21));
     237             : 
     238           5 :     lwan_send(request, header, header_len, MSG_MORE);
     239             : 
     240           0 :     while (true) {
     241           5 :         ssize_t written = sendfile(request->fd, in_fd, &offset, chunk_size);
     242           5 :         if (written < 0) {
     243           0 :             switch (errno) {
     244           0 :             case EAGAIN:
     245             :             case EINTR:
     246           0 :                 goto try_again;
     247           0 :             default:
     248           0 :                 coro_yield(request->conn->coro, CONN_CORO_ABORT);
     249           0 :                 __builtin_unreachable();
     250             :             }
     251             :         }
     252             : 
     253           5 :         to_be_written -= (size_t)written;
     254           5 :         if (!to_be_written)
     255           4 :             break;
     256             : 
     257           1 :         chunk_size = LWAN_MIN(to_be_written, 1ul << 21);
     258           1 :         lwan_readahead_queue(in_fd, offset, chunk_size);
     259             : 
     260           1 :     try_again:
     261           1 :         coro_yield(request->conn->coro, CONN_CORO_WANT_WRITE);
     262             :     }
     263           4 : }
     264             : #elif defined(__FreeBSD__) || defined(__APPLE__)
     265             : void lwan_sendfile(struct lwan_request *request,
     266             :                    int in_fd,
     267             :                    off_t offset,
     268             :                    size_t count,
     269             :                    const char *header,
     270             :                    size_t header_len)
     271             : {
     272             :     struct sf_hdtr headers = {.headers =
     273             :                                   (struct iovec[]){{.iov_base = (void *)header,
     274             :                                                     .iov_len = header_len}},
     275             :                               .hdr_cnt = 1};
     276             :     off_t sbytes = (off_t)count;
     277             : 
     278             :     if (!count) {
     279             :         /* FreeBSD's sendfile() won't send the headers when count is 0. Why? */
     280             :         return (void)lwan_writev(request, headers.headers, headers.hdr_cnt);
     281             :     }
     282             : 
     283             :     while (true) {
     284             :         int r;
     285             : 
     286             : #ifdef __APPLE__
     287             :         r = sendfile(in_fd, request->fd, offset, &sbytes, &headers, 0);
     288             : #else
     289             :         r = sendfile(in_fd, request->fd, offset, count, &headers, &sbytes,
     290             :                      SF_MNOWAIT);
     291             : #endif
     292             : 
     293             :         if (UNLIKELY(r < 0)) {
     294             :             switch (errno) {
     295             :             case EAGAIN:
     296             :             case EBUSY:
     297             :             case EINTR:
     298             :                 goto try_again;
     299             :             default:
     300             :                 coro_yield(request->conn->coro, CONN_CORO_ABORT);
     301             :                 __builtin_unreachable();
     302             :             }
     303             :         }
     304             : 
     305             :         count -= (size_t)sbytes;
     306             :         if (!count)
     307             :             break;
     308             : 
     309             :     try_again:
     310             :         coro_yield(request->conn->coro, CONN_CORO_WANT_WRITE);
     311             :     }
     312             : }
     313             : #else
     314             : static size_t try_pread_file(struct lwan_request *request,
     315             :                              int fd,
     316             :                              void *buffer,
     317             :                              size_t len,
     318             :                              off_t offset)
     319             : {
     320             :     size_t total_read = 0;
     321             : 
     322             :     for (int tries = MAX_FAILED_TRIES; tries;) {
     323             :         ssize_t r = pread(fd, buffer, len, offset);
     324             : 
     325             :         if (UNLIKELY(r < 0)) {
     326             :             tries--;
     327             : 
     328             :             switch (errno) {
     329             :             case EAGAIN:
     330             :             case EINTR:
     331             :                 /* fd is a file, re-read -- but give other coros some time, too */
     332             :                 coro_yield(request->conn->coro, CONN_CORO_YIELD);
     333             :                 continue;
     334             :             default:
     335             :                 break;
     336             :             }
     337             :         }
     338             : 
     339             :         total_read += (size_t)r;
     340             : 
     341             :         if (r == 0 || total_read == len)
     342             :             return total_read;
     343             : 
     344             :         offset += r;
     345             :     }
     346             : 
     347             :     coro_yield(request->conn->coro, CONN_CORO_ABORT);
     348             :     __builtin_unreachable();
     349             : }
     350             : 
     351             : void lwan_sendfile(struct lwan_request *request,
     352             :                    int in_fd,
     353             :                    off_t offset,
     354             :                    size_t count,
     355             :                    const char *header,
     356             :                    size_t header_len)
     357             : {
     358             :     unsigned char buffer[512];
     359             : 
     360             :     lwan_send(request, header, header_len, MSG_MORE);
     361             : 
     362             :     while (count) {
     363             :         size_t bytes_read = try_pread_file(
     364             :             request, in_fd, buffer, LWAN_MIN(count, sizeof(buffer)), offset);
     365             :         lwan_send(request, buffer, bytes_read, 0);
     366             :         count -= bytes_read;
     367             :         offset += bytes_read;
     368             :     }
     369             : }
     370             : #endif

Generated by: LCOV version 1.15-2-gb9d6727