Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test and remove warnings of undefined behaviour #375

Merged
merged 14 commits into from
Feb 6, 2019
4 changes: 0 additions & 4 deletions Network/Socket/Buffer.hs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ socket2FD s = do
-- | Send data to the socket. The socket must be connected to a remote
-- socket. Returns the number of bytes sent. Applications are
-- responsible for ensuring that all data has been sent.
--
-- Sending data to closed socket may lead to undefined behaviour.
sendBuf :: Socket -- Bound/Connected Socket
-> Ptr Word8 -- Pointer to the data to send
-> Int -- Length of the buffer
Expand Down Expand Up @@ -115,8 +113,6 @@ recvBufFrom s ptr nbytes
-- The return value is the length of received data. Zero means
-- EOF. Historical note: Version 2.8.x.y or earlier,
-- an EOF error was thrown. This was changed in version 3.0.
--
-- Receiving data from closed socket may lead to undefined behaviour.
recvBuf :: Socket -> Ptr Word8 -> Int -> IO Int
recvBuf s ptr nbytes
| nbytes <= 0 = ioError (mkInvalidRecvArgError "Network.Socket.recvBuf")
Expand Down
6 changes: 0 additions & 6 deletions Network/Socket/ByteString.hs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,6 @@ import Network.Socket.Types
-- explicitly, so the socket need not be in a connected state.
-- Returns the number of bytes sent. Applications are responsible for
-- ensuring that all data has been sent.
--
-- Sending data to closed socket may lead to undefined behaviour.
sendTo :: Socket -> ByteString -> SockAddr -> IO Int
sendTo = G.sendTo

Expand All @@ -79,17 +77,13 @@ sendTo = G.sendTo
-- data has been sent or an error occurs. On error, an exception is
-- raised, and there is no way to determine how much data, if any, was
-- successfully sent.
--
-- Sending data to closed socket may lead to undefined behaviour.
sendAllTo :: Socket -> ByteString -> SockAddr -> IO ()
sendAllTo = G.sendAllTo

-- | Receive data from the socket. The socket need not be in a
-- connected state. Returns @(bytes, address)@ where @bytes@ is a
-- 'ByteString' representing the data received and @address@ is a
-- 'SockAddr' representing the address of the sending socket.
--
-- Receiving data from closed socket may lead to undefined behaviour.
recvFrom :: Socket -> Int -> IO (ByteString, SockAddr)
recvFrom = G.recvFrom

16 changes: 0 additions & 16 deletions Network/Socket/ByteString/IO.hsc
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ import Network.Socket.ByteString.MsgHdr (MsgHdr(..))
-- | Send data to the socket. The socket must be connected to a
-- remote socket. Returns the number of bytes sent. Applications are
-- responsible for ensuring that all data has been sent.
--
-- Sending data to closed socket may lead to undefined behaviour.
send :: Socket -- ^ Connected socket
-> ByteString -- ^ Data to send
-> IO Int -- ^ Number of bytes sent
Expand All @@ -92,8 +90,6 @@ waitWhen0 _ _ = return ()
-- until either all data has been sent or an error occurs. On error,
-- an exception is raised, and there is no way to determine how much
-- data, if any, was successfully sent.
--
-- Sending data to closed socket may lead to undefined behaviour.
sendAll :: Socket -- ^ Connected socket
-> ByteString -- ^ Data to send
-> IO ()
Expand All @@ -107,8 +103,6 @@ sendAll s bs = do
-- explicitly, so the socket need not be in a connected state.
-- Returns the number of bytes sent. Applications are responsible for
-- ensuring that all data has been sent.
--
-- Sending data to closed socket may lead to undefined behaviour.
sendTo :: SocketAddress sa =>
Socket -- ^ Socket
-> ByteString -- ^ Data to send
Expand All @@ -123,8 +117,6 @@ sendTo s xs sa =
-- data has been sent or an error occurs. On error, an exception is
-- raised, and there is no way to determine how much data, if any, was
-- successfully sent.
--
-- Sending data to closed socket may lead to undefined behaviour.
sendAllTo :: SocketAddress sa =>
Socket -- ^ Socket
-> ByteString -- ^ Data to send
Expand All @@ -142,8 +134,6 @@ sendAllTo s xs sa = do
-- sent or an error occurs. On error, an exception is raised, and
-- there is no way to determine how much data, if any, was
-- successfully sent.
--
-- Sending data to closed socket may lead to undefined behaviour.
sendMany :: Socket -- ^ Connected socket
-> [ByteString] -- ^ Data to send
-> IO ()
Expand All @@ -170,8 +160,6 @@ sendMany s = sendAll s . B.concat
-- continues to send data until either all data has been sent or an
-- error occurs. On error, an exception is raised, and there is no
-- way to determine how much data, if any, was successfully sent.
--
-- Sending data to closed socket may lead to undefined behaviour.
sendManyTo :: Socket -- ^ Socket
-> [ByteString] -- ^ Data to send
-> SockAddr -- ^ Recipient address
Expand Down Expand Up @@ -211,8 +199,6 @@ sendManyTo s cs = sendAllTo s (B.concat cs)
--
-- For TCP sockets, a zero length return value means the peer has
-- closed its half side of the connection.
--
-- Receiving data from closed socket may lead to undefined behaviour.
recv :: Socket -- ^ Connected socket
-> Int -- ^ Maximum number of bytes to receive
-> IO ByteString -- ^ Data received
Expand All @@ -226,8 +212,6 @@ recv s nbytes
-- 'SockAddr' representing the address of the sending socket.
--
-- If the first return value is zero, it means EOF.
--
-- Receiving data from closed socket may lead to undefined behaviour.
recvFrom :: SocketAddress sa =>
Socket -- ^ Socket
-> Int -- ^ Maximum number of bytes to receive
Expand Down
7 changes: 4 additions & 3 deletions Network/Socket/ByteString/Lazy.hs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import Data.ByteString.Lazy.Internal (ByteString(..), defaultChunkSize
import Network.Socket (ShutdownCmd (..), shutdown)
import Prelude hiding (getContents)
import System.IO.Unsafe (unsafeInterleaveIO)
import Control.Exception (onException)

#if defined(mingw32_HOST_OS)
import Network.Socket.ByteString.Lazy.Windows (send, sendAll)
Expand Down Expand Up @@ -64,7 +65,9 @@ getContents s = loop
loop = unsafeInterleaveIO $ do
sbs <- N.recv s defaultChunkSize
if S.null sbs
then shutdown s ShutdownReceive >> return Empty
then do
shutdown s ShutdownReceive `onException` return ()
eborden marked this conversation as resolved.
Show resolved Hide resolved
return Empty
else Chunk sbs <$> loop

-- | Receive data from the socket. The socket must be in a connected
Expand All @@ -74,8 +77,6 @@ getContents s = loop
-- until a message arrives.
--
-- If there is no more data to be received, returns an empty 'ByteString'.
--
-- Receiving data from closed socket may lead to undefined behaviour.
recv
:: Socket -- ^ Connected socket
-> Int64 -- ^ Maximum number of bytes to receive
Expand Down
6 changes: 0 additions & 6 deletions Network/Socket/Types.hsc
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,6 @@ invalidateSocket (Socket ref _) errorAction normalAction = do
-- | Close the socket. This function does not throw exceptions even if
-- the underlying system call returns errors.
--
-- Sending data to or receiving data from closed socket
-- may lead to undefined behaviour.
--
-- If multiple threads use the same socket and one uses 'fdSocket' and
-- the other use 'close', unexpected behavior may happen.
-- For more information, please refer to the documentation of 'fdSocket'.
Expand All @@ -156,9 +153,6 @@ close s = invalidateSocket s (\_ -> return ()) $ \oldfd -> do

-- | Close the socket. This function throws exceptions if
-- the underlying system call returns errors.
--
-- Sending data to or receiving data from closed socket
-- may lead to undefined behaviour.
close' :: Socket -> IO ()
close' s = invalidateSocket s (\_ -> return ()) $ \oldfd -> do
-- closeFdWith avoids the deadlock of IO manager.
Expand Down
7 changes: 5 additions & 2 deletions network.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,11 @@ test-suite spec
default-language: Haskell2010
hs-source-dirs: tests
main-is: Spec.hs
other-modules: RegressionSpec
SimpleSpec
other-modules:
Network.Test.Common
Network.SocketSpec
Network.Socket.ByteStringSpec
Network.Socket.ByteString.LazySpec
type: exitcode-stdio-1.0
ghc-options: -Wall -threaded
-- NB: make sure to versions of hspec and hspec-discover
Expand Down
85 changes: 85 additions & 0 deletions tests/Network/Socket/ByteString/LazySpec.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}

module Network.Socket.ByteString.LazySpec (main, spec) where

import Prelude hiding (getContents)

import qualified Data.ByteString.Lazy as L
import Network.Socket
import Network.Socket.ByteString.Lazy
import Network.Test.Common
import Control.Monad

import Test.Hspec

main :: IO ()
main = hspec spec

spec :: Spec
spec = do
describe "send" $ do
it "works well" $ do
let server sock = recv sock 1024 `shouldReturn` lazyTestMsg
client sock = send sock lazyTestMsg
tcpTest client server

it "throws when closed" $ do
let server _ = return ()
client sock = do
close sock
send sock lazyTestMsg `shouldThrow` anyException
tcpTest client server

describe "sendAll" $ do
it "works well" $ do
let server sock = recv sock 1024 `shouldReturn` lazyTestMsg
client sock = sendAll sock lazyTestMsg
tcpTest client server

it "throws when closed" $ do
let server _ = return ()
client sock = do
close sock
sendAll sock lazyTestMsg `shouldThrow` anyException
tcpTest client server

describe "getContents" $ do
it "works well" $ do
let server sock = getContents sock `shouldReturn` lazyTestMsg
client sock = do
void $ send sock lazyTestMsg
shutdown sock ShutdownSend
tcpTest client server

it "returns empty string at EOF" $ do
let client s = getContents s `shouldReturn` L.empty
server s = shutdown s ShutdownSend
tcpTest client server

describe "recv" $ do
it "works well" $ do
let server sock = recv sock 1024 `shouldReturn` lazyTestMsg
client sock = send sock lazyTestMsg
tcpTest client server

it "throws when closed" $ do
let server sock = do
close sock
recv sock 1024 `shouldThrow` anyException
client sock = send sock lazyTestMsg
tcpTest client server

it "can treat overflow" $ do
let server sock = do
seg1 <- recv sock (L.length lazyTestMsg - 3)
seg2 <- recv sock 1024
let msg = L.append seg1 seg2
msg `shouldBe` lazyTestMsg
client sock = send sock lazyTestMsg
tcpTest client server

it "returns empty string at EOF" $ do
let client s = recv s 4096 `shouldReturn` L.empty
server s = shutdown s ShutdownSend
tcpTest client server
Loading