sendfile 0.3.1 → 0.4
raw patch · 10 files changed
+201/−140 lines, 10 filesdep +network-bytestringdep ~Win32
Dependencies added: network-bytestring
Dependency ranges changed: Win32
Files
- csrc/sendfile_linux.c +0/−9
- csrc/sendfile_win32.c +0/−16
- sendfile.cabal +11/−9
- src/Network/Socket/SendFile.hs +44/−13
- src/Network/Socket/SendFile/FreeBSD.hs +17/−0
- src/Network/Socket/SendFile/Internal.hs +58/−10
- src/Network/Socket/SendFile/Linux.hs +17/−0
- src/Network/Socket/SendFile/Linux.hsc +0/−27
- src/Network/Socket/SendFile/Portable.hs +0/−13
- src/Network/Socket/SendFile/Win32.hsc +54/−43
− csrc/sendfile_linux.c
@@ -1,9 +0,0 @@--/* LINUX */-#include <stdlib.h>-#include <sys/sendfile.h>--ssize_t c_sendfile_linux(const int out_fd, const int in_fd, const size_t count) {- return sendfile(out_fd, in_fd, NULL, count);-}-
− csrc/sendfile_win32.c
@@ -1,16 +0,0 @@--/* WIN32 */-#include <windows.h>-#include <mswsock.h>-#include <io.h>--BOOL c_TransmitFile(SOCKET out_fd, HANDLE in_hdl, DWORD count) {- /* http://msdn.microsoft.com/en-us/library/ms740565(VS.85).aspx */- return TransmitFile(out_fd, in_hdl, count, 0, NULL, NULL, TF_USE_KERNEL_APC);-}--long c_get_osfhandle(int fd) {- /* http://support.microsoft.com/kb/99173 - MAY BE IMPORTANT */- /* http://msdn.microsoft.com/en-us/library/ks2530z6.aspx */- return _get_osfhandle(fd);-}
sendfile.cabal view
@@ -1,10 +1,10 @@ name: sendfile-version: 0.3.1-stability: experimental+version: 0.4+stability: provisional synopsis: A portable sendfile library description: A library which exposes zero-copy sendfile functionality in a portable way. If a platform does not support sendfile, a fallback implementation in haskell is provided. .- Currently supported platforms: Windows 2000+ (Native), Linux 2.6+ (Native), Everything else (Haskell).+ Currently supported platforms: Windows 2000+ (Native), Linux 2.6+ (Native), FreeBSD (Untested!), Everything else (Haskell). license: BSD3 license-file: LICENSE@@ -33,19 +33,21 @@ if os(windows) && !flag(portable) cpp-options: -DWIN32_SENDFILE- build-depends: Win32- c-sources: csrc/sendfile_win32.c+ build-depends: Win32 >= 2.2.0.0 && < 2.3 extra-libraries: mswsock other-modules: Network.Socket.SendFile.Win32 else if os(linux) && !flag(portable) cpp-options: -DLINUX_SENDFILE- c-sources: csrc/sendfile_linux.c other-modules: Network.Socket.SendFile.Linux else- cpp-options: -DPORTABLE_SENDFILE- other-modules: Network.Socket.SendFile.Portable- build-depends: bytestring >= 0.9.1.4 && < 0.10+ if os(freebsd) && !flag(portable)+ cpp-options: -DFREEBSD_SENDFILE+ other-modules: Network.Socket.SendFile.FreeBSD+ else+ cpp-options: -DPORTABLE_SENDFILE+ build-depends: bytestring >= 0.9.1.4 && < 0.10,+ network-bytestring >= 0.1.1.3 && < 0.2 source-repository head type: darcs
src/Network/Socket/SendFile.hs view
@@ -1,28 +1,59 @@--- | A cross-platform wrapper for sendfile -- this implements an available operating-system call if supported, otherwise it falls back to a portable haskell implementation. SendFile will flush the output Handle before transmission.+-- | A cross-platform wrapper for sendfile -- this implements an available operating-system call if supported, otherwise it falls back to a portable haskell implementation. ----- Keep in mind that the input should only be a `Handle` derived from a network socket (by using socketToHandle). Using something other than a network socket for the input Handle will result in undefined behavior. Furthermore, for consistent read/write behavior, both the output and input handles should be opened in Binary mode rather than Text mode (especially if you are using hSeek).+-- Two interfaces are provided for both the unsafe and safe sets of functions. The first interface accepts an output socket\/handle and the path of the file you want to send; sendFile and unsafeSendFile comprise this interface. The second interface accepts an output socket\/handle, a handle to the file you want to send, and the number of bytes you want to send; sendFile' and unsafeSendFile' comprise this interface.+--+-- For consistent read/write behavior with either sendFile' or unsafeSendFile', the input handle should be opened in Binary mode rather than Text mode (especially if you are using hSeek before sending).+--+ module Network.Socket.SendFile (+ -- * Safe functions (recommended) sendFile, sendFile',+ -- * Unsafe functions (not recommended)+ -- | With unsafeSendFile and unsafeSendFile' the output should only be a `Handle` derived from a network socket (by using socketToHandle). These functions are unsafe because you cannot know whether or not a Handle is actually a `Socket` at compile-time. If you try to use a `Handle` for the output which is not in fact a socket, you will get a runtime error.+ --+ -- These functions are provided for convenience given that the current high-level interface in the Network package gives you a `Handle` when you use accept. They may be deprecated in the future.+ + unsafeSendFile,+ unsafeSendFile',+ -- * Utility functions sendFileMode ) where+import qualified Network.Socket.SendFile.Internal (sendFile, sendFile', sendFileMode, unsafeSendFile, unsafeSendFile') -import qualified Network.Socket.SendFile.Internal (sendFile, sendFile', sendFileMode) import System.IO (Handle)+import Network.Socket (Socket) --- | The simplest interface. Simply give it an output `Handle` and the `FilePath` to the input file.-sendFile :: Handle -- ^ The output handle- -> FilePath -- ^ The path where the input file resides- -> IO ()+-- | The simplest interface. Simply give it an output `Socket` and the `FilePath` to the input file.+sendFile+ :: Socket -- ^ The output socket+ -> FilePath -- ^ The path where the input file resides+ -> IO () sendFile = Network.Socket.SendFile.Internal.sendFile --- | A more powerful interface than sendFile, sendFile' accepts a `Handle` for the input file instead of a `FilePath` and the number of bytes you would like to read; this number must be a positive integer. This unlocks the full potential `Handle`(s). For instance, if you wanted to start reading from a particular offset, you could utilize `hSeek`. If you need the file size you can use 'hFileSize'.-sendFile' :: Handle -- ^ The output handle- -> Handle -- ^ The input handle- -> Integer -- ^ The number of bytes to read- -> IO ()+-- | A more powerful interface than sendFile, sendFile' accepts a `Handle` for the input file instead of a `FilePath` and the number of bytes you would like to read; this number must be a positive integer. This unlocks the full potential `Handle`(s). For instance, if you wanted to start reading from a particular offset in the file you could utilize `hSeek`; if you needed the file size you could use 'hFileSize'.+sendFile'+ :: Socket -- ^ The output socket+ -> Handle -- ^ The input file handle+ -> Integer -- ^ The number of bytes to send+ -> IO () -- sendFile' = Network.Socket.SendFile.Internal.sendFile' --- | Returns the mode that sendfile was compiled with. Mainly for debugging use. Possible values are 'WIN32_SENDFILE', 'LINUX_SENDFILE', and 'PORTABLE_SENDFILE'.+-- | The unsafe version of sendFile which accepts a `Handle` instead of a `Socket` for the output. It will flush the output handle before sending any file data.+unsafeSendFile+ :: Handle -- ^ The output handle+ -> FilePath -- ^ The path where the input file resides+ -> IO ()+unsafeSendFile = Network.Socket.SendFile.Internal.unsafeSendFile++-- | The unsafe version of sendFile' which accepts a `Handle` instead of a `Socket` for the output. It will flush the output handle before sending any file data.+unsafeSendFile'+ :: Handle -- ^ The output handle+ -> Handle -- ^ The input file handle+ -> Integer -- ^ The number of bytes to send+ -> IO ()+unsafeSendFile' = Network.Socket.SendFile.Internal.unsafeSendFile'++-- | Returns the mode that sendfile was compiled with. Mainly for debugging use. Possible values are 'WIN32_SENDFILE', 'LINUX_SENDFILE', 'FREEBSD_SENDFILE', and 'PORTABLE_SENDFILE'. sendFileMode :: String -- ^ The mode that sendfile was compiled with sendFileMode = Network.Socket.SendFile.Internal.sendFileMode
+ src/Network/Socket/SendFile/FreeBSD.hs view
@@ -0,0 +1,17 @@+{-# LANGUAGE ForeignFunctionInterface #-}+-- | FreeBSD system-dependent code for 'sendfile'.+module Network.Socket.SendFile.FreeBSD (_sendFile) where+import Foreign.C.Error (throwErrnoIfMinus1_)+import Foreign.C.Types (CInt, CSize)+import Foreign.Ptr (Ptr, nullPtr)+import System.Posix.Types (COff, Fd)++_sendFile :: Fd -> Fd -> Integer -> IO ()+_sendFile out_fd in_fd count =+ throwErrnoIfMinus1_+ "Network.Socket.SendFile.FreeBSD.sendFile'"+ (c_sendfile_freebsd in_fd out_fd 0 (fromIntegral count) nullPtr nullPtr 0)++foreign import ccall unsafe "sys/uio.h sendfile" c_sendfile_freebsd+ :: Fd -> Fd -> COff -> CSize -> Ptr () -> Ptr COff -> CInt -> IO CInt+
src/Network/Socket/SendFile/Internal.hs view
@@ -1,16 +1,24 @@ {-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-unused-imports #-} module Network.Socket.SendFile.Internal ( sendFile, sendFile',- sendFileMode+ sendFileMode,+ unsafeSendFile,+ unsafeSendFile', ) where +import Network.Socket (Socket(..), fdSocket) import System.IO ( Handle, IOMode(..), hFileSize,+ hFlush, withBinaryFile )+import System.Posix.Types (Fd(..))+import GHC.Handle (withHandle_)+import GHC.IOBase (haFD) #if defined(WIN32_SENDFILE) import Network.Socket.SendFile.Win32 (_sendFile)@@ -26,20 +34,60 @@ sendFileMode = "LINUX_SENDFILE" #endif +#if defined(FREEBSD_SENDFILE)+import Network.Socket.SendFile.FreeBSD (_sendFile)++sendFileMode :: String+sendFileMode = "FREEBSD_SENDFILE"+#endif+ #if defined(PORTABLE_SENDFILE)-import Network.Socket.SendFile.Portable (_sendFile)+import Data.ByteString.Char8 (hGet, hPutStr)+import Network.Socket.ByteString (sendAll) sendFileMode :: String sendFileMode = "PORTABLE_SENDFILE"++sendFile' :: Socket -> Handle -> Integer -> IO ()+sendFile' = wrapSendFile' $ \outs inp count -> do+ sendAll outs =<< hGet inp (fromIntegral count)+ return ()++unsafeSendFile' :: Handle -> Handle -> Integer -> IO ()+unsafeSendFile' = wrapSendFile' $ \outp inp count -> do+ hPutStr outp =<< hGet inp (fromIntegral count)+ hFlush outp -- match the behavior that all data is "flushed to the os" of native implementations+#else+sendFile' :: Socket -> Handle -> Integer -> IO ()+sendFile' outs inp count =+ withHandle_ "Network.Socket.SendFile.sendFile'" inp $ \inp' -> do+ let out_fd = Fd (fdSocket outs)+ let in_fd = Fd (haFD inp')+ wrapSendFile' _sendFile out_fd in_fd count++unsafeSendFile' :: Handle -> Handle -> Integer -> IO ()+unsafeSendFile' outp inp count = do+ hFlush outp -- flush outp before sending+ withHandle_ "Network.Socket.SendFile.unsafeSendFile'" outp $ \outp' -> do+ withHandle_ "Network.Socket.SendFile.unsafeSendFile'" inp $ \inp' -> do+ let out_fd = Fd (haFD outp')+ let in_fd = Fd (haFD inp')+ wrapSendFile' _sendFile out_fd in_fd count #endif -sendFile :: Handle -> FilePath -> IO ()-sendFile outp infp = withBinaryFile infp ReadMode $ \inp -> do+-- | wraps sendFile' to check arguments+wrapSendFile' :: (a -> b -> Integer -> IO ()) -> a -> b -> Integer -> IO ()+wrapSendFile' fun outp inp count+ | count < 0 = error "SendFile - count must be a positive integer"+ | count == 0 = return () -- Send nothing -- why do the work? Also, Windows treats '0' as 'send the whole file'.+ | otherwise = fun outp inp count++sendFile :: Socket -> FilePath -> IO ()+sendFile outs infp = withBinaryFile infp ReadMode $ \inp -> do count <- hFileSize inp- sendFile' outp inp count+ sendFile' outs inp count -sendFile' :: Handle -> Handle -> Integer -> IO ()-sendFile' outp inp count- | count < 0 = error "sendFile' - count must be a positive integer"- | count == 0 = return () -- Send nothing -- why do the work? Also, Windows treats '0' as 'send the whole file'.- | otherwise = _sendFile outp inp count+unsafeSendFile :: Handle -> FilePath -> IO ()+unsafeSendFile outp infp = withBinaryFile infp ReadMode $ \inp -> do+ count <- hFileSize inp+ unsafeSendFile' outp inp count
+ src/Network/Socket/SendFile/Linux.hs view
@@ -0,0 +1,17 @@+{-# LANGUAGE ForeignFunctionInterface #-}+-- | Linux system-dependent code for 'sendfile'.+module Network.Socket.SendFile.Linux (_sendFile) where+import Foreign.C.Error (throwErrnoIfMinus1_)+import Foreign.C.Types (CSize)+import Foreign.Ptr (Ptr, nullPtr)+import System.Posix.Types (COff, Fd)++_sendFile :: Fd -> Fd -> Integer -> IO ()+_sendFile out_fd in_fd count =+ throwErrnoIfMinus1_+ "Network.Socket.SendFile.Linux.sendFile'"+ (c_sendfile_linux out_fd in_fd nullPtr (fromIntegral count))++foreign import ccall unsafe "sys/sendfile.h sendfile" c_sendfile_linux+ :: Fd -> Fd -> Ptr COff -> CSize -> IO CSize+
− src/Network/Socket/SendFile/Linux.hsc
@@ -1,27 +0,0 @@-{-# LANGUAGE ForeignFunctionInterface #-} --- | Linux system-dependent code for 'sendfile'. -module Network.Socket.SendFile.Linux (_sendFile) where-import Data.Int-import Data.Word -import Foreign.C.Error (throwErrno) -import GHC.IOBase (FD, haFD) -import GHC.Handle (withHandle_)-import System.IO (Handle, hFlush) --#include <sys/sendfile.h>- -_sendFile :: Handle -> Handle -> Integer -> IO () -_sendFile outp inp count = do - -- flush outp before sending - hFlush outp - withHandle_ "Network.Socket.SendFile.Linux.sendFile'" outp $ \outp' -> do - withHandle_ "Network.Socket.SendFile.Linux.sendFile'" inp $ \inp' -> do - let out_fd = haFD outp'- let in_fd = haFD inp' - res <- c_sendfile_linux out_fd in_fd (fromIntegral count) - if res == -1 - then throwErrno "Network.Socket.SendFile.Linux.sendFile'" - else return () - -foreign import ccall unsafe - c_sendfile_linux :: FD -> FD -> (#type size_t) -> IO (#type ssize_t)
− src/Network/Socket/SendFile/Portable.hs
@@ -1,13 +0,0 @@--- | Portable implementation of sendfile. -module Network.Socket.SendFile.Portable (_sendFile) where - -import Data.ByteString.Char8 -import Prelude hiding (readFile) -import System.IO (Handle, hFlush) - - --- FIXME: possibly immature / inefficient implementation -_sendFile :: Handle -> Handle -> Integer -> IO () -_sendFile outp inp count = do - hPutStr outp =<< hGet inp (fromIntegral count) - hFlush outp -- match the behavior that all data is "flushed to the os" of native implementations
src/Network/Socket/SendFile/Win32.hsc view
@@ -1,43 +1,54 @@-{-# LANGUAGE ForeignFunctionInterface #-} --- | Win32 system-dependent code for 'TransmitFile'. -module Network.Socket.SendFile.Win32 (_sendFile) where -import Data.Int -import Data.Word -import Foreign.C.Error (throwErrno) -import Foreign.Ptr (IntPtr, intPtrToPtr) -import GHC.Handle (withHandle_) -import GHC.IOBase (FD, haFD) -import System.IO (Handle, hFlush) -import System.Win32.Types - -#include <windows.h> - -_sendFile :: Handle -> Handle -> Integer -> IO () -_sendFile outp inp count = do - hFlush outp -- flush the buffer before invoking transmitFile - withHandle_ "Network.Socket.SendFile.Win32.sendFile'" outp $ \outp' -> do - withHandle_ "Network.Socket.SendFile.Win32.sendFile'" inp $ \inp' -> do - let out_fd = haFD outp' - in_hdl <- get_osfhandle (haFD inp') - transmitFile (fromIntegral out_fd) in_hdl (fromIntegral count) - -get_osfhandle :: FD -- ^ User file descriptor. - -> IO HANDLE -- ^ The operating-system file handle. -get_osfhandle fd = do - res <- c_get_osfhandle fd - if res == (#const INVALID_HANDLE_VALUE) - then throwErrno "Network.Socket.SendFile.Win32.get_osfhandle" - else return (intPtrToPtr res) - -transmitFile :: FD -- ^ A handle to a connected socket. - -> HANDLE -- ^ A handle to the open file that the TransmitFile function transmits. - -> DWORD -- ^ The number of bytes in the file to transmit. - -> IO () -transmitFile out_fd in_hdl count = - failIf_ (== 0) "Network.Socket.SendFile.Win32.transmitFile" (c_TransmitFile out_fd in_hdl count) - -foreign import ccall unsafe - c_get_osfhandle :: FD -> IO IntPtr - -foreign import ccall unsafe - c_TransmitFile :: FD -> HANDLE -> DWORD -> IO (#type BOOL) +{-# LANGUAGE ForeignFunctionInterface #-}+-- | Win32 system-dependent code for 'TransmitFile'.+module Network.Socket.SendFile.Win32 (_sendFile) where+import Foreign.C.Error (throwErrnoIf)+import Foreign.Ptr (IntPtr, Ptr, intPtrToPtr, nullPtr)+import Foreign.C.Types (CInt)+import System.Posix.Types (Fd)+import System.Win32.Types (DWORD, HANDLE, failIfZero)++#include <windows.h>+#include <mswsock.h>++type SOCKET = Fd++_sendFile :: Fd -> Fd -> Integer -> IO ()+_sendFile out_fd in_fd count = do+ in_hdl <- get_osfhandle in_fd+ transmitFile out_fd in_hdl (fromIntegral count)++get_osfhandle :: Fd -- ^ User file descriptor.+ -> IO HANDLE -- ^ The operating-system file handle.+get_osfhandle fd = do+ res <- throwErrnoIf+ (== (#const INVALID_HANDLE_VALUE))+ "Network.Socket.SendFile.Win32.get_osfhandle"+ (c_get_osfhandle fd)+ return (intPtrToPtr res)++transmitFile :: SOCKET -- ^ A handle to a connected socket.+ -> HANDLE -- ^ A handle to the open file that the TransmitFile function transmits.+ -> DWORD -- ^ The number of bytes in the file to transmit.+ -> IO ()+transmitFile out_fd in_hdl count = do+ failIfZero+ "Network.Socket.SendFile.Win32.transmitFile"+ (c_TransmitFile out_fd in_hdl count 0 nullPtr nullPtr (#const TF_USE_KERNEL_APC))+ return ()++-- http://support.microsoft.com/kb/99173 - MAY BE IMPORTANT+-- http://msdn.microsoft.com/en-us/library/ks2530z6.aspx+foreign import ccall unsafe "io.h _get_osfhandle"+ c_get_osfhandle :: Fd+ -> IO IntPtr++-- http://msdn.microsoft.com/en-us/library/ms740565(VS.85).aspx+foreign import stdcall unsafe "mswsock.h TransmitFile"+ c_TransmitFile :: SOCKET+ -> HANDLE+ -> DWORD+ -> DWORD+ -> Ptr ()+ -> Ptr ()+ -> DWORD+ -> IO CInt