From aa1bc013ff54eb118855365945aae4218d099003 Mon Sep 17 00:00:00 2001 From: Wilfried Goesgens Date: Sat, 8 Oct 2011 23:59:09 +0200 Subject: [PATCH] Add support for sending / reading to files; so far we just implement the linux sendfile facility --- libcitadel/configure.in | 2 + libcitadel/lib/libcitadel.h | 17 ++++++ libcitadel/lib/stringbuf.c | 118 ++++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) diff --git a/libcitadel/configure.in b/libcitadel/configure.in index eb3f4c0ed..d61a071c3 100755 --- a/libcitadel/configure.in +++ b/libcitadel/configure.in @@ -74,6 +74,8 @@ fi AC_CHECK_HEADER(CUnit/CUnit.h, [AC_DEFINE(ENABLE_TESTS, [], [whether we should compile the test-suite])]) +AC_CHECK_HEADER(sys/sendfile.h, [AC_DEFINE(LINUX_SENDFILE, [], [whether we have the linux sendfile api])]) +dnl TODO: we might need to check for the actual syntax.... AC_CHECK_HEADERS(iconv.h) diff --git a/libcitadel/lib/libcitadel.h b/libcitadel/lib/libcitadel.h index ac102b1b2..0ee29b20c 100644 --- a/libcitadel/lib/libcitadel.h +++ b/libcitadel/lib/libcitadel.h @@ -236,6 +236,23 @@ typedef struct _file_buffer { int nBlobBytesWanted; } IOBuffer; + +typedef struct __fd_iobuffer { + IOBuffer *IOB; + int OtherFD; + long TotalSendSize; + long TotalSentAlready; + long ChunkSize; + long ChunkSendRemain; + StrBuf *ChunkBuffer; /* just used if we don't have sendfile */ +} FDIOBuffer; + + +void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize); +int FileSendChunked(FDIOBuffer *FDB, const char **Err); +int FileRecvChunked(FDIOBuffer *FDB, const char **Err); +eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error); + long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB); int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB); diff --git a/libcitadel/lib/stringbuf.c b/libcitadel/lib/stringbuf.c index c8f8a6434..08e904b31 100644 --- a/libcitadel/lib/stringbuf.c +++ b/libcitadel/lib/stringbuf.c @@ -28,6 +28,9 @@ #include #define SHOW_ME_VAPPEND_PRINTF #include +#ifndef LINUX_SENDFILE +#include +#endif #include "libcitadel.h" #ifdef HAVE_ICONV @@ -38,6 +41,10 @@ #include #endif +#ifdef LINUX_SENDFILE +#include +#endif + #ifdef HAVE_ZLIB #include int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen, @@ -3782,6 +3789,117 @@ long IOBufferStrLength(IOBuffer *FB) return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf); } + + +void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize) +{ + memset(FDB, 0, sizeof(FDIOBuffer)); + FDB->ChunkSize = + FDB->TotalSendSize = TotalSendSize; + FDB->IOB = IO; +#ifndef LINUX_SENDFILE + FDB->ChunkBuffer = NewStrBuf(); +#endif + FDB->OtherFD = FD; +} + +int FileSendChunked(FDIOBuffer *FDB, const char **Err) +{ + +#ifdef LINUX_SENDFILE + ssize_t sent; + sent = sendfile(FDB->IOB->fd, FDB->OtherFD, &FDB->TotalSentAlready, FDB->ChunkSendRemain); + if (sent == -1) + { + *Err = strerror(errno); + return sent; + } + FDB->ChunkSendRemain -= sent; + return FDB->ChunkSendRemain; +#else +#endif + return 0; +} + +int FileRecvChunked(FDIOBuffer *FDB, const char **Err) +{ + +#ifdef LINUX_SENDFILE + ssize_t sent; + sent = sendfile(FDB->OtherFD, FDB->IOB->fd, &FDB->TotalSentAlready, FDB->ChunkSendRemain); + if (sent == -1) + { + *Err = strerror(errno); + return sent; + } + FDB->ChunkSendRemain -= sent; + return FDB->ChunkSendRemain; +#else +#endif + return 0; +} + +eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error) +{ + int IsNonBlock; + int fdflags; + long rlen; + long should_write; + int nSuccessLess = 0; + struct timeval tv; + fd_set rfds; + + fdflags = fcntl(FDB->OtherFD, F_GETFL); + IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK; + + while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) && + (FDB->ChunkSendRemain > 0)) + { + if (IsNonBlock){ + tv.tv_sec = 1; /* selectresolution; */ + tv.tv_usec = 0; + + FD_ZERO(&rfds); + FD_SET(FDB->OtherFD, &rfds); + if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) { + *Error = strerror(errno); + return eReadFail; + } + } + if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) { + nSuccessLess ++; + continue; + } + + should_write = FDB->IOB->Buf->BufUsed - + (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf); + if (should_write > FDB->ChunkSendRemain) + should_write = FDB->ChunkSendRemain; + + rlen = write(FDB->OtherFD, + FDB->IOB->ReadWritePointer, + should_write); + if (rlen < 1) { + *Error = strerror(errno); + + return eReadFail; + } + FDB->TotalSentAlready += rlen; + FDB->IOB->ReadWritePointer += rlen; + FDB->ChunkSendRemain -= rlen; + } + if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed) + { + FlushStrBuf(FDB->IOB->Buf); + FDB->IOB->ReadWritePointer = NULL; + } + + if (FDB->ChunkSendRemain == 0) + return eReadSuccess; + else + return eMustReadMore; +} + /******************************************************************************* * File I/O; Prefer buffered read since its faster! * *******************************************************************************/ -- 2.30.2