Giter VIP home page Giter VIP logo

hs-connection's Introduction

haskell Connection library

Simple network library for all your connection need.

Features:

  • Really simple to use
  • SSL/TLS
  • SOCKS

Usage

Connect to www.example.com on port 4567 (without socks or tls), then send a byte, receive a single byte, print it, and close the connection:

import qualified Data.ByteString as B
import Network.Connection
import Data.Default

main = do
    ctx <- initConnectionContext
    con <- connectTo ctx $ ConnectionParams
                              { connectionHostname  = "www.example.com"
                              , connectionPort      = 4567
                              , connectionUseSecure = Nothing
                              , connectionUseSocks  = Nothing
                              }
    connectionPut con (B.singleton 0xa)
    r <- connectionGet con 1
    putStrLn $ show r
    connectionClose con

Using a socks proxy is easy, we just need replacing the connectionSocks parameter, for example connecting to the same host, but using a socks proxy at localhost:1080:

con <- connectTo ctx $ ConnectionParams
                       { connectionHostname  = "www.example.com"
                       , connectionPort      = 4567
                       , connectionUseSecure = Nothing
                       , connectionUseSocks  = Just $ SockSettingsSimple "localhost" 1080
                       }

Connecting to a SSL style socket is equally easy, and need to set the UseSecure fields in ConnectionParams:

con <- connectTo ctx $ ConnectionParams
                       { connectionHostname  = "www.example.com"
                       , connectionPort      = 4567
                       , connectionUseSecure = Just def
                       , connectionUseSocks  = Nothing
                       }

And finally, you can start TLS in the middle of an insecure connection. This is great for protocol using STARTTLS (e.g. IMAP, SMTP):

{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString as B
import Data.ByteString.Char8 ()
import Network.Connection
import Data.Default

main = do
    ctx <- initConnectionContext
    con <- connectTo ctx $ ConnectionParams
                              { connectionHostname  = "www.example.com"
                              , connectionPort      = 4567
                              , connectionUseSecure = Nothing
                              , connectionUseSocks  = Nothing
                              }
    -- talk to the other side with no TLS: says hello and starttls
    connectionPut con "HELLO\n"
    connectionPut con "STARTTLS\n"

    -- switch to TLS
    connectionSetSecure ctx con def

    -- the connection is from now on using TLS, we can send secret for example
    connectionPut con "PASSWORD 123\n"
    connectionClose con

hs-connection's People

Contributors

agrafix avatar alainodea avatar blackgnezdo avatar dmjio avatar dpwright avatar erikd avatar felixonmars avatar joeyadams avatar mb720 avatar snoyberg avatar vincenthz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hs-connection's Issues

Add connectionGet' that works like B.hGet

Here is an implementation I am using as part of in my changes to support TLS in the amqp package:

connectionGet' :: Conn.Connection -> Int -> IO BC.ByteString
connectionGet' conn x = do
  bs <- Conn.connectionGet conn x
  let diff = BS.length bs - x
  if BS.length bs == 0 || diff == 0
    then do
      return bs
    else do
      next <- connectionGet' conn diff
      return $ BC.append bs next

Relevant qualified imports:

import qualified Network.Connection as Conn
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BC

Export `globalCertificateStore` field of `ConnectionContext`

I would like to initialize the connection with system certificates and add my own certificates in addition. This is possible because CertificateStore has the Semigroup instance, so I'm able to combine multiple stores. However, it seems like there's no way to easily add your certificates when using initConnectionContext and connectTo functions.

Export Network.TLS.Struct?

Maybe some refactoring should be done, because a lot of this module really doesn't need to see the light of day, but the Handshake type in particular is necessary for defining a handshake hook (i.e. with hookRecvHandshake).

Usage with Handle -> IO ()?

Hello,

This appears to be a very useful package. But, I'm new to Haskell (like many people, LOL) and don't understand enough yet, so hope you can provide a bit of help. I'm trying to create an SSL connection with an IRC server - such a useful protocol for learning purposes.

With plain text connections, most examples show something roughly similar to the following (example taken from https://wiki.haskell.org/Roll_your_own_IRC_bot):

import Network
import System.IO

server = "irc.freenode.org"
port   = 6667

main = do
    h <- connectTo server (PortNumber (fromIntegral port))
    hSetBuffering h NoBuffering
    t <- hGetContents h
    print t

But with Connections, I don't understand, because I can't work with handles, can I? Anyway, hope you can help newbies like myself understand a bit more. Thanks.

FEATURE: Listen!

In order to be a reasonable replacement for secure-sockets, one thing we need is a 'listen' functionality - could you summarize the missing pieces?

Some TLS connections fail

Consider the following example:

import Network.Connection
import Data.Default

main :: IO ()
main = do
    context <- initConnectionContext
    conn <- connectTo context $ ConnectionParams "httpbin.org" 443 (Just def) Nothing
    return ()

This example errors out with the following message:

test.hs: HandshakeFailed (Error_Packet_unexpected "Alert [(AlertLevel_Fatal,BadRecordMac)]" " expected: change cipher")

I get the same error message when trying to connection to https://www.fpcomplete.com/ (which is using Amazon ELB's SSL capabilities), but the connection works to https://www.google.com/. However, all of these connections work correctly when using http-conduit.

Don't get an exception on a timeout

I have used Network.Connection to connect to an IRC network. An exception is raised when the remote host closes the connection but I never get an exception on a timeout. For example if I turn off wifi, it never throws any kind of exception.

Need away to set socket options on a connection backed by a socket

I'm running into an issue, where I have a connection that goes idle for an extended period, that results in a router silently disconnecting me form the target.
I don't think I can practically manually send keep alive packets to the server
So I would like to be able to set the SO_KEEPALIVE , TCP_KEEPIDLE , TCP_KEEPINTVL, TCP_KEEPCNT Socket options on the socket.
This may already be possible and I'm missing it.

Is there a simple way to use connectionGet in a blocking way?

I am in the midst of porting the amqp package from using GHC.IO.Handle to using Network.Connection.Connection. The motivation for this is to gain transparent SSL/TLS support to allow for encrypted AMQP communications from Haskell.

The trouble is that my Connnection-based implementation doesn't work. I was running into some fairly surprising (to me) differences when I packet trace the alternate implementations.

It became evident that Network.Connection.connectionGet and GHC.IO.Handle.hGet are very different (non-blocking vs blocking):

http://hackage.haskell.org/package/connection-0.1.3.1/docs/Network-Connection.html#v:connectionGet http://hackage.haskell.org/package/bytestring-0.10.4.0/docs/Data-ByteString-Lazy.html#v:hGet

Network.Connection.connectionGet acts like GHC.IO.Handle.hGetNonBlocking.

I was replacing GHC.IO.Handle.hGet with Network.Connection.connectionGet thinking it was a drop-in replacement which it isn't.

How do I use Network.Connection.connectionGet in a blocking manner like Data.ByteString.Lazy.hGet?

I posted this to StackOverflow as well:
http://stackoverflow.com/questions/20414948/how-do-i-use-network-connection-connectionget-in-a-blocking-manner-like-data-byt

Unclear semantics regarding EOF and zero-length chunks

Two questions regarding the behavior of connectionGetChunk:

  • How is EOF indicated? Does it return a zero-length string, or throw an exception?
  • Will it ever return an empty string before EOF?

Based on the documentation and a quick test, here's how hGetSome seems to behave:

  • On EOF, hGetSome returns an empty string every time it is called. No exception is thrown. (Note that hGetLine does throw an exception on EOF).
  • hGetSome will only return an empty string on EOF.

However, I don't know about the TLS recvData function. http-conduit ignores null chunks, which would produce an infinite loop if recvData had the same EOF semantics as hGetSome.

https://badssl.com/ test failure

When trying to connect to "expired.badssl.com" and "self-signed.badssl.com" I see relevant exceptions. However, connecting to "wrong.host.badssl.com" unexpectedly succeeds. I haven't figured out yet how to enable hostname verification or to understand if it's even implemented somewhere.

import Network.Connection
import Data.Default

main = do
    ctx <- initConnectionContext
    con <- connectTo ctx $ ConnectionParams
        -- { connectionHostname  = "expired.badssl.com"
        -- { connectionHostname  = "wrong.host.badssl.com"
        { connectionHostname  = "self-signed.badssl.com"
        , connectionPort      = 443
        , connectionUseSecure = Just def
        , connectionUseSocks  = Nothing
        }
    connectionClose con

IPv6?

Is there a way to specify that I do not want to use IPv6?

mock Connection

I would like to run integration tests that don't depend on the network. It appears to me that if I can mock the Connection than I can mock http-client. Do you have any insight on how to initialize a mock connection?

From a quick review of the code, my first try would be use connectFromHandle but give a mock Handle, although now I have to figure out if it is possible to create a mock Handle.

connection-0.2.5 fails to build with GHC 9.0.2

connection-0.2.5$ cabal build -w ghc-9.0.2
Resolving dependencies...
...
Building library for connection-0.2.5..
[1 of 2] Compiling Network.Connection.Types ( ... )

Network/Connection/Types.hs:19:1: warning: [-Wdeprecations]
    Module ‘Network.BSD’ is deprecated:
      This platform dependent module is no longer supported.
   |
19 | import Network.BSD (HostName)
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[2 of 2] Compiling Network.Connection ( ... )

Network/Connection.hs:101:43: error:
    • Couldn't match expected type ‘crypton-x509-store-1.6.9:Data.X509.CertificateStore.CertificateStore’
                  with actual type ‘Data.X509.CertificateStore.CertificateStore’
      NB: ‘crypton-x509-store-1.6.9:Data.X509.CertificateStore.CertificateStore’
            is defined in ‘Data.X509.CertificateStore’
                in package ‘crypton-x509-store-1.6.9’
          ‘Data.X509.CertificateStore.CertificateStore’
            is defined in ‘Data.X509.CertificateStore’
                in package ‘x509-store-1.6.9’
    • In the ‘sharedCAStore’ field of a record
      In the ‘clientShared’ field of a record
      In the expression:
        (TLS.defaultParamsClient (fst cid) portString)
          {TLS.clientSupported = def
                                   {TLS.supportedCiphers = TLS.ciphersuite_all},
           TLS.clientShared = def
                                {TLS.sharedCAStore = globalCertificateStore cg,
                                 TLS.sharedValidationCache = validationCache}}
    |
101 |             { TLS.sharedCAStore         = globalCertificateStore cg
    |                                           ^^^^^^^^^^^^^^^^^^^^^^^^^
Error: cabal: Failed to build connection-0.2.5.

Probably the package is missing som upper bounds, which could be supplied via a hackage revision. Here is the dependencies used in this build attempt (as resolved by Cabal, excluding package shipped with GHC 9.0.2):

asn1-encoding-0.9.6
asn1-parse-0.9.5
asn1-types-0.3.4
async-2.2.4
base-orphans-0.9.0
basement-0.0.15
byteable-0.1.1
cereal-0.5.8.3
crypton-0.31
crypton-x509-1.7.6
crypton-x509-store-1.6.9
crypton-x509-validation-1.6.12
cryptonite-0.30
data-array-byte-0.1.0.1
data-default-class-0.1.2.0
hashable-1.4.2.0
hourglass-0.2.12
hsc2hs-0.68.9
memory-0.18.0
network-2.8.0.1
old-locale-1.0.0.7
old-time-1.1.0.3
pem-0.2.4
socks-0.5.4
tls-1.7.0
unix-time-0.4.9
x509-1.7.7
x509-store-1.6.9
x509-system-1.6.7
x509-validation-1.6.12

Connection-0.2.6 breaks Amazonka (stackage lts-6.12 on up)

I tracked this down over the last full day of pulling my hair. It was not unlike the bug seen in hs-tls 1.3.4 where you would get bad mac every once in a while.

With connection-0.2.5 I can pound away on amazonka. With connection-0.2.6 I get random bad mac errors.

user@computer:~/src/github.com/fpco/ops% time stack exec -- ops aws s3 purge 222b3ebc-70a0-11e6-80be-0f425a3fa624                               
.....................ops: TransportError (TlsExceptionHostPort (Terminated True "received fatal error: BadRecordMac" (Error_Protocol ("remote side fatal error",True,BadRecordMac))) "s3.amazonaws.com" 443)
stack exec -- ops aws s3 purge 222b3ebc-70a0-11e6-80be-0f425a3fa624  3.27s user 0.99s system 20% cpu 21.052 total
user@computer:~/src/github.com/fpco/ops% time stack exec -- ops aws s3 purge 222b3ebc-70a0-11e6-80be-0f425a3fa624
........................................................ops: TransportError (TlsExceptionHostPort (Terminated True "received fatal error: BadRecordMac" (Error_Protocol ("remote side fatal error",True,BadRecordMac))) "s3.amazonaws.com" 443)
stack exec -- ops aws s3 purge 222b3ebc-70a0-11e6-80be-0f425a3fa624  5.08s user 1.31s system 11% cpu 56.224 total
user@computer:~/src/github.com/fpco/ops% time stack exec -- ops aws s3 purge 222b3ebc-70a0-11e6-80be-0f425a3fa624
........................ops: TransportError (TlsExceptionHostPort (Terminated True "received fatal error: BadRecordMac" (Error_Protocol ("remote side fatal error",True,BadRecordMac))) "s3.amazonaws.com" 443)
stack exec -- ops aws s3 purge 222b3ebc-70a0-11e6-80be-0f425a3fa624  3.28s user 0.95s system 17% cpu 24.207 total
user@computer:~/src/github.com/fpco/ops% time stack exec -- ops aws s3 purge 222b3ebc-70a0-11e6-80be-0f425a3fa624
............................ops: TransportError (TlsExceptionHostPort (Terminated True "received fatal error: BadRecordMac" (Error_Protocol ("remote side fatal error",True,BadRecordMac))) "s3.amazonaws.com" 443)
stack exec -- ops aws s3 purge 222b3ebc-70a0-11e6-80be-0f425a3fa624  3.15s user 0.87s system 14% cpu 27.544 total
user@computer:~/src/github.com/fpco/ops% time stack exec -- ops aws s3 purge 222b3ebc-70a0-11e6-80be-0f425a3fa624
.........................ops: TransportError (TlsExceptionHostPort (Terminated True "received fatal error: BadRecordMac" (Error_Protocol ("remote side fatal error",True,BadRecordMac))) "s3.amazonaws.com" 443)
stack exec -- ops aws s3 purge 222b3ebc-70a0-11e6-80be-0f425a3fa624  3.27s user 0.75s system 15% cpu 25.356 total
user@computer:~/src/github.com/fpco/ops% time stack exec -- ops aws s3 purge 222b3ebc-70a0-11e6-80be-0f425a3fa624
.................................ops: TransportError (TlsExceptionHostPort (Terminated True "received fatal error: BadRecordMac" (Error_Protocol ("remote side fatal error",True,BadRecordMac))) "s3.amazonaws.com" 443)
stack exec -- ops aws s3 purge 222b3ebc-70a0-11e6-80be-0f425a3fa624  3.18s user 1.00s system 12% cpu 33.218 total
user@computer:~/src/github.com/fpco/ops% time stack exec -- ops aws s3 purge 222b3ebc-70a0-11e6-80be-0f425a3fa624
..................................ops: TransportError (TlsExceptionHostPort (Terminated True "received fatal error: BadRecordMac" (Error_Protocol ("remote side fatal error",True,BadRecordMac))) "s3.amazonaws.com" 443)
stack exec -- ops aws s3 purge 222b3ebc-70a0-11e6-80be-0f425a3fa624  2.50s user 1.00s system 10% cpu 34.065 total
user@computer:~/src/github.com/fpco/ops% time stack exec -- ops aws s3 purge 222b3ebc-70a0-11e6-80be-0f425a3fa624
........................ops: TransportError (TlsExceptionHostPort (Terminated True "received fatal error: BadRecordMac" (Error_Protocol ("remote side fatal error",True,BadRecordMac))) "s3.amazonaws.com" 443)
stack exec -- ops aws s3 purge 222b3ebc-70a0-11e6-80be-0f425a3fa624  2.58s user 0.75s system 13% cpu 24.302 total
user@computer:~/src/github.com/fpco/ops% time stack exec -- ops aws s3 purge 222b3ebc-70a0-11e6-80be-0f425a3fa624
........................ops: TransportError (TlsExceptionHostPort (Terminated True "received fatal error: BadRecordMac" (Error_Protocol ("remote side fatal error",True,BadRecordMac))) "s3.amazonaws.com" 443)
stack exec -- ops aws s3 purge 222b3ebc-70a0-11e6-80be-0f425a3fa624  2.22s user 0.84s system 12% cpu 24.030 total

API for getting connection info

I'd like to be able to get information about an open connection. Specifically I'd want to retrieve information about the TLS handshake that occurred for TLS connections.

Ambiguous docs on re-using TLS sessions

Hello! I'm trying to implement TLS session re-use in one of our open source projects (channable/vaultenv#37), but am having a few issues understanding the current status of support from this library.

The docs for settingDisableSession currently read "Disable session management. TLS/SSL connections will always re-established their context. Not Implemented Yet."

To me, it seems like it would mean "Disabling session management isn't implemented, sessions will always be re-used", but this code suggests that the actual re-use isn't implemented (since the default of the tls package is noSessionManager).

Because of the double negative in the docs, the wording is a bit ambiguous.

Questions:

  • What is the best way to currently get TLS session re-use using hs-connection? Could we just override the default settings to use connectionSessionManager from this package? Or does it have problems and do we need to use another implementation (such as https://hackage.haskell.org/package/tls-session-manager-0.0.0.2/docs/Network-TLS-SessionManager.html or roll our own?)
  • Are you open to a PR which updates the docs with a pointer to an alternative implementation or rephrases the current wording to be more clear?

Thanks for your work you put in this package, by the way 😄

Wait for data with a timeout

I am looking for hWaitForInput-like function to implement IMAP IDLE over secure connection.
Is there a function to wait for data with a timeout?

Potential open socket leak when TLS handshake fails

The connectTo function in Network.Connection calls connectFromSocket after opening a connection to the server:

connectTo :: ConnectionContext -- ^ The global context of this connection.
          -> ConnectionParams  -- ^ The parameters for this connection (where to connect, and such).
          -> IO Connection     -- ^ The new established connection on success.
connectTo cg cParams = do
    conFct <- getConFct (connectionUseSocks cParams)
    h      <- conFct (connectionHostname cParams) (N.PortNumber $ connectionPort cParams)
    connectFromSocket cg h cParams

The h handle is I believe leaked when connectFromSocket throws an exception. This happens, for example, when TLS handshakes fail:

connectFromSocket :: ConnectionContext
                  -> Socket
                  -> ConnectionParams
                  -> IO Connection
connectFromSocket cg sock p = withSecurity (connectionUseSecure p)
    where withSecurity Nothing            = connectionNew cid $ ConnectionSocket sock
          withSecurity (Just tlsSettings) =
                tlsEstablish sock (makeTLSParams cg cid tlsSettings) >>=
                        connectionNew cid . ConnectionTLS
...
tlsEstablish :: TLS.HasBackend backend => backend -> TLS.ClientParams -> IO TLS.Context
tlsEstablish handle tlsParams = do
    ctx <- TLS.contextNew handle tlsParams
    TLS.handshake ctx
    return ctx

because TLS.handshake throws exceptions on failure and does not return the ctx that might otherwise be used to close the connection.

TLSSettingsSimple is too simple, TLSSettings is unwieldy

Hello

When trying to communicated with my crummy server, I have to force the use of TLS10. Since it is not possible to do so with TLSSettingsSimple, I use the full TLSSettings (the one with a ClientParams). However, to make it work, there's quite a lot of setup that I could only discover by reading the source code, to find out how a ClientParams is created from a TLSSettingsSimple.

The result to for supportedVersions(and disable certificate validation) looks like the following, which is kind of horrible:

        tlsSettings = TLSSettings clientParams
        clientParams = (defaultParamsClient "" "")
                         { clientSupported = def { supportedVersions = [TLS10], supportedCiphers = ciphersuite_all}
                         , clientShared = def {
                             --sharedCAStore = globalCertificateStore context
                             sharedValidationCache = validationCache
                                              }}
        validationCache = TLS.ValidationCache (\_ _ _ -> return TLS.ValidationCachePass)
                                    (\_ _ _ -> return ())

I was thinking about adding an extra field with type ClientParams -> ClientParams to TLSSettingsSimple, for extra customization. If you consider it a good idea, I'll send a pull request.

Thanks a lot!

Library does not support asynchronous I/O (send and receive block each other)

Currently, Network.Connection uses a single MVar lock on the backend, which is held during a send or receive (withBackend doWrite and withBackend getMoreData). This means that if one thread is waiting for data with connectionGet, another thread cannot send data until the other host responds.

Network.TLS will likely need to be tweaked to support concurrent send and receive, though. recvData appears to call sendPacket in several circumstances (e.g. renegotiation). If recvData and sendData are called concurrently, sendPacket may be called concurrently. sendPacket looks like this:

sendPacket :: MonadIO m => Context -> Packet -> m ()
sendPacket ctx pkt = do
        ...
        dataToSend <- usingState_ ctx $ writePacket pkt
        ...
        liftIO $ contextSend ctx dataToSend

usingState_ updates an MVar holding the connection state (for both sending and receiving), and contextSend sends the bytes (no locking beyond what the backend does). One possible interleaving is:

A: compute dataToSend
B: compute dataToSend
B: call contextSend
A: call contextSend

This interleaving sends the packets in a different order than they were computed. Assuming that one thread is calling recvData and another is caling sendData, is this okay? Or do the packets to send need to be serialized somehow?

Error compiling connection-0.3.0

Issue

When I try to compile connection (a dependency of xmobar, which is what I am trying to install), I get a compilation error.

Machine info

$ uname -a
Linux kuro 4.14.83-gentoo #6 SMP Fri Feb 15 20:45:56 -00 2019 x86_64 Intel(R) Core(TM) i7-4600U CPU @ 2.10GHz GenuineIntel GNU/Linux
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 8.0.2
$ cabal --version
cabal-install version 1.24.0.2
compiled using version 1.24.2.0 of the Cabal library

Build log

Build log ( /home/lattis/.cabal/logs/connection-0.3.0.log ):
cabal: Entering directory '/tmp/cabal-tmp-18567/connection-0.3.0'
Configuring connection-0.3.0...
Building connection-0.3.0...
Preprocessing library connection-0.3.0...
[1 of 2] Compiling Network.Connection.Types ( Network/Connection/Types.hs, dist/build/Network/Connection/Types.o )
[2 of 2] Compiling Network.Connection ( Network/Connection.hs, dist/build/Network/Connection.o )

Network/Connection.hs:213:22: error:
    • No instance for (Read PortNumber) arising from a use of ‘reads’
    • In the expression: reads portS
      In the expression:
        case reads portS of {
          [(sPort, "")] -> Just (sHost, sPort)
          _ -> Nothing }
      In a case alternative:
          (sHost, ':' : portS)
            -> case reads portS of {
                 [(sPort, "")] -> Just (sHost, sPort)
                 _ -> Nothing }
cabal: Leaving directory '/tmp/cabal-tmp-18567/connection-0.3.0'

Steps to reproduce

$ cabal install xmobar

connectionInfo

Possibly add & export a function to Network.Connection that allows the user of the library to query information about an established TLS connection. Something along the lines of (using LambdaCase)

connectionInfo :: Connection -> IO (Maybe TLS.Information)
connectionInfo c = do
    tryReadMVar (connectionBackend c) >>= \case
     Nothing -> return Nothing
     Just connBackend -> do
          case connBackend of
            ConnectionTLS ctx -> TLS.contextGetInformation ctx
            _ -> return Nothing

HostNotResolved can never happen?

HostNotResolved is supposed to be "Exception raised when there's no resolution for a specific host".

Looking at connectTo we can see it's going to be thrown when getAddrInfo returns an empty list.

Problem is getAddrInfo never returns an empty list, it throws instead:

If the query fails, this function throws an IO exception instead of returning an empty list.

connectTo hangs forever

Hi,

when using connectTo in a simple test application like the following, it hangs forever. Running with strace shows that something weird is going on with polling. In a bigger application of mine it all works though, so it's probably a race condition somewhere or something else is needed? This reliably always fails on two different Linux systems here.

{-# LANGUAGE OverloadedStrings #-}                                                                           

import Network.Connection
import Network.Socket (PortNumber (PortNum))

main :: IO ()
main = do
    ctx <- initConnectionContext
    let params = ConnectionParams "google.com" (PortNum 80) Nothing Nothing

    c <- connectTo ctx params

    connectionClose c

Confusing docs on connectionGetLine

The docs for connectionGetLine are reproduced here:

-- | Get the next line, using ASCII LF as the line terminator.
--
-- This throws an 'isEOFError' exception on end of input, and LineTooLong when
-- the number of bytes gathered is over the limit without a line terminator.
--
-- The actual line returned can be bigger than the limit specified, provided
-- that the last chunk returned by the underlaying backend contains a LF.
-- In another world only when we need more input and limit is reached that the
-- LineTooLong exception will be raised.
--
-- An end of file will be considered as a line terminator too, if line is
-- not empty.

When I read this initially, I was confused about what happens if the input ends. Reading the source, I see that there's an throwEOF conn loc that is passed to more, but this is never actually called. Instead, the throwEOF invocation that could happen here happens from connectionGetChunkBase, which occurs when the connectionBuffer conn MVar value is a Nothing. The comment on that field says:

    , connectionBuffer  :: MVar (Maybe ByteString) -- ^ this is set to 'Nothing' on EOF

If that's Just, then we check if it is empty. If it is, then we try to get more data. If it's still empty after getting more data, we close the connection and return f applied to the empty bytestring. If it's got more data, then you update the buffer with the remainder.

So, as far as I can tell:

  • If Connection is already done (eg hit EOF), then connectionGetLine throws an EOF error.
  • If Connection encounters an EOF condition (no more bytes from upstream, even after requesting more) then it returns the ByteString as if it found a \n and closes the MVar.
  • If Connection encounters a \n it returns the bytestring accumulated so far.
  • If Connection gets too many bytes then it throws the line error.

I'll try to come up with a patch to make the docs easier to understand and maybe remove the unused throwEOF parameter to more.

Specify whether IPv6 addresses are bracketed in HostName (and export HostName)

type HostName is documented as:

-- | Hostname This could either be a name string (punycode encoded) or an ipv4/ipv6
type HostName = String

It is unclear whether IPv6 addresses are expected in URI hostname form ([::1]) or as plain IPv6 addresses (::1).

  • please specify which format is expected
  • please export HostName from NetworkConnection so it show up in the documentation

fail to build

Preprocessing library connection-0.2.2...
[1 of 2] Compiling Network.Connection.Types ( Network/Connection/Types.hs, dist/build/Network/Connection/Types.o )
[2 of 2] Compiling Network.Connection ( Network/Connection.hs, dist/build/Network/Connection.o )

Network/Connection.hs:156:21:
Ambiguous occurrence E.try' It could refer to eitherE.try',
imported qualified from Control.Exception' at Network/Connection.hs:50:1-39 (and originally defined inControl.Exception.Base')
or E.try', imported qualified fromSystem.IO.Error' at Network/Connection.hs:51:1-37
cabal: Error: some packages failed to install:

sessionResumeOnlyOnce

TLS v1.5.0 introduced a new field sessionResumeOnlyOnce. connection should catch up.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.