optimize binary file transfers: use sendfile(2) syscall if supported - geomyidae - A small C-based gopherd.
git clone git://bitreich.org/geomyidae/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/geomyidae/
Log
Files
Refs
Tags
README
LICENSE
---
commit d8b18c7bfa0d75f38bbceefde217c83813473e5f
parent 1c6dfdef1faabdb80161e5490526491e2a02c28c
Author: Hiltjo Posthuma 
Date:   Sat, 23 Sep 2017 16:08:28 +0200

optimize binary file transfers: use sendfile(2) syscall if supported

The OS supporting sendfile(2) are (for now): Linux, FreeBSD, DragonFlyBSD.

When the file is empty or has no filesize information (such as block,
character devices etc) fallback to the normal read/write loop in userland.
A platform with no sendfile(2) support always uses this loop.

Optimize the buffer size in the normal read/write loop by using the block size
information, fallback to BUFSIZ.

Set socket options TCP_CORK (Linux), TCP_NOPUSH (BSDs) and TCP_NODELAY to
optimize packet transfers for large file transfers.

Tested on Linux (glibc and musl), FreeBSD, DragonFlyBSD and OpenBSD.

Signed-off-by: Christoph Lohmann <20h@r-36.net>

Diffstat:
  M handlr.c                            |      14 +++-----------
  M ind.c                               |      68 +++++++++++++++++++++++++++++++
  M ind.h                               |       1 +

3 files changed, 72 insertions(+), 11 deletions(-)
---
diff --git a/handlr.c b/handlr.c
@@ -104,8 +104,7 @@ void
 handlebin(int sock, char *file, char *port, char *base, char *args,
                 char *sear, char *ohost)
 {
-        char sendb[1024];
-        int len, fd, sent;
+        int fd;
 
         USED(port);
         USED(base);
@@ -115,15 +114,8 @@ handlebin(int sock, char *file, char *port, char *base, char *args,
 
         fd = open(file, O_RDONLY);
         if(fd >= 0) {
-                while((len = read(fd, sendb, sizeof(sendb))) > 0) {
-                        while(len > 0) {
-                                if ((sent = send(sock, sendb, len, 0)) < 0) {
-                                        close(fd);
-                                        return;
-                                }
-                                len -= sent;
-                        }
-                }
+                if(xsendfile(fd, sock) < 0)
+                        perror("sendfile");
                 close(fd);
         }
 }
diff --git a/ind.c b/ind.c
@@ -12,9 +12,19 @@
 #include 
 #include 
 #include 
+#include 
 #include 
+#include 
 #include 
 
+/* for sendfile(2) */
+#ifdef __linux__
+#include 
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+#include 
+#include 
+#endif
+
 #include "ind.h"
 #include "handlr.h"
 
@@ -42,6 +52,64 @@ filetype type[] = {
         {nil, nil, nil},
 };
 
+int
+xsendfile(int fd, int sock)
+{
+        struct stat st;
+        char *sendb;
+        size_t bufsiz = BUFSIZ, count = 0;
+        int len, sent, optval;
+
+#ifdef TCP_CORK
+        optval = 1;
+        setsockopt(sock, IPPROTO_TCP, TCP_CORK, &optval, sizeof(int));
+#endif
+
+#ifdef TCP_NOPUSH
+        optval = 1;
+        setsockopt(sock, IPPROTO_TCP, TCP_NOPUSH, &optval, sizeof(int));
+#endif
+
+#ifdef TCP_NODELAY
+        optval = 0;
+        setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(int));
+#endif
+
+        if(fstat(fd, &st) >= 0) {
+                if((bufsiz = st.st_blksize) < BUFSIZ)
+                        bufsiz = BUFSIZ;
+                count = st.st_size;
+        }
+
+#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__DragonFly__)
+        count = 0;
+#endif
+
+        if (count == 0) {
+                sendb = xmalloc(bufsiz);
+                while((len = read(fd, sendb, bufsiz)) > 0) {
+                        while(len > 0) {
+                                if ((sent = send(sock, sendb, len, 0)) < 0) {
+                                        close(fd);
+                                        free(sendb);
+                                        return -1;
+                                }
+                                len -= sent;
+                        }
+                }
+                free(sendb);
+                return 0;
+        }
+
+#ifdef __linux__
+        return sendfile(sock, fd, NULL, count);
+#endif
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+        return sendfile(fd, sock, 0, count, NULL, NULL, 0);
+#endif
+        return -1;
+}
+
 void *
 xcalloc(size_t nmemb, size_t size)
 {
diff --git a/ind.h b/ind.h
@@ -35,6 +35,7 @@ void *xcalloc(size_t, size_t);
 void *xmalloc(size_t);
 void *xrealloc(void *, size_t);
 char *xstrdup(const char *str);
+int xsendfile(int, int);
 Indexs *scanfile(char *fname);
 Elems *getadv(char *str);
 int printelem(int fd, Elems *el, char *addr, char *port);