k0001 / network-simple

Haskell library abstracting common network sockets usage patterns.
BSD 3-Clause "New" or "Revised" License
33 stars 9 forks source link

recv buffer keeping connection open despite closeSock call #27

Closed ancarda closed 6 years ago

ancarda commented 6 years ago

I'm having some trouble using this package when the server side tries to close the socket. I've called closeSock to close a TCP connection but have found the connection stays open due to a previous recv function call.

When the client side sends enough bytes specified by recv, the connection is closed right away, but without sending those bytes, the connection will hang forever. The client can later send bytes and the connection will close as expected.

Below is a minimal example that recreates the issue:

module Main where

import Network.Simple.TCP
import Data.Maybe
import Data.ByteString.Char8 (pack)

main = do
  serve (Host "127.0.0.1") "8080" $ \(sock, remoteAddr) -> do
    send sock $ pack "Type 4 bytes.\n";

    x <- recv sock 4
    let input = fromMaybe (pack "") x
    send sock input

    send sock $ pack "\nClosing Connection.\n"
    closeSock sock

Please observe if you connect and send 4 or more bytes, only 4 bytes will be echoed back, and the server will close the connection. When less than 4 bytes are specified, the connection will remain open -- despite closeSock being called -- until the remaining bytes are sent.

$ nc 127.0.0.1 8080
Type 4 bytes.
test
test
Closing Connection.

$ nc 127.0.0.1 8080
Type 4 bytes.
huh
huh

Closing Connection.
^C (manually killed)

$ nc 127.0.0.1 8080
Type 4 bytes.
huh
huh

Closing Connection.
? (process closed by itself)

Am I misunderstanding how to use this package or is this a bug?

Wizek commented 6 years ago

I also remember running into this some time ago, and being confused. Any ideas, @k0001?

k0001 commented 6 years ago

Thanks for reporting this. I'll try to look into it later today.

One thing that I immediately notice is that one is not supposed to manually close the socket within the body of 'serve' or similar functions. This is done automatically as soon as the body returns.

On Sat, Jul 21, 2018, 18:52 Milán Nagy notifications@github.com wrote:

I also remember running into this some time ago, and being confused. Any ideas, @k0001 https://github.com/k0001?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/k0001/network-simple/issues/27#issuecomment-406805550, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAM5klkAFcjHP0m8JIohh5XsThf_fB6ks5uI04ygaJpZM4VZqu9 .

MathiasVP commented 6 years ago

I'm also currently running into this problem (even though I'm not explicitly calling closeSock, but instead relying on connect / serve to close it for me upon exit) :( Any updates on this?

ancarda commented 6 years ago

Good timing to ask, I had an idea earlier today which I will explore tonight if I get the chance.

k0001 commented 6 years ago

I'm investigating this at the moment. Thanks for the excellent issue report.

k0001 commented 6 years ago

There is a known bug in network-2.6.3.4 when closing a socket. Are you using this version by any chance?

ancarda commented 6 years ago

The only thing I have in my cabal file is network-simple >= 0.4.1 -- do you know how I can check the dependency for network?

k0001 commented 6 years ago

You can check with 'ghc-pkg list | grep network' within your development environment.

ancarda commented 6 years ago

I am using these versions:

    network-2.7.0.2
    network-simple-0.4.1

I had a thought about this earlier today (I just got back from work), and I suspect most, or possibly all, of what I am seeing is due to how netcat (nc) works. If I adjust the server to not receive any input, netcat will still hang even though the connection closes immediately after sending "Type 4 bytes". When using nc -d (disable stdin), netcat will exit as soon as the connection is closed.

That could explain what I'm seeing:

If you kill the server process while nc -d is running, it'll exit right away; this essentially simulates what a close would do; send a TCP RST, or otherwise signal to the client the connection is closing. If you don't have -d, netcat is waiting for stdin; as it, unlike telnet, uses line buffering.

If you compare to using telnet; it works as expected:

$ telnet 127.0.0.1 8080
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Type 4 bytes.
abcd 
abcd
Closing Connection.
Connection closed by foreign host.

$ telnet 127.0.0.1 8080
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Type 4 bytes.
ab
ab

Closing Connection.
Connection closed by foreign host.

Note even though I only typed 2 bytes, the connection closed right after sending some input to the server.

I'm going to close this issue as I think this is working as expected. I hope this is useful, @MathiasVP.

k0001 commented 6 years ago

I tend to agree with @ancarda. This behavior seems to be specific to nc.

Somewhat related: I pushed some changes to master improving error handling and connection shutdown in general throughout the library. I will give a test to these changes soon and then upload a new version of the library to Hackage. Notice that even after these improvements, the nc situation is the same.

k0001 commented 6 years ago

Unrelated update: I just pushed network-simple-0.4.2 to Hackage, it mostly improves internal error handling.