geomyidae

A small C-based gopherd. (gopher://bitreich.org/1/scm/geomyidae)
git clone git://r-36.net/geomyidae
Log | Files | Refs | README | LICENSE

commit d8b18c7bfa0d75f38bbceefde217c83813473e5f
parent 1c6dfdef1faabdb80161e5490526491e2a02c28c
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
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:
handlr.c | 14+++-----------
ind.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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 <stdlib.h> #include <netdb.h> #include <sys/socket.h> +#include <sys/stat.h> #include <netinet/in.h> +#include <netinet/tcp.h> #include <arpa/inet.h> +/* for sendfile(2) */ +#ifdef __linux__ +#include <sys/sendfile.h> +#elif defined(__FreeBSD__) || defined(__DragonFly__) +#include <sys/types.h> +#include <sys/uio.h> +#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);