1{-# LANGUAGE CPP #-}
2{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE RankNTypes #-}
4{-# LANGUAGE RecordWildCards #-}
5{-# LANGUAGE DeriveDataTypeable #-}
6{-# LANGUAGE PatternGuards #-}
7
8-- | HTTP over TLS support for Warp via the TLS package.
9--
10--   If HTTP\/2 is negotiated by ALPN, HTTP\/2 over TLS is used.
11--   Otherwise HTTP\/1.1 over TLS is used.
12--
13--   Support for SSL is now obsoleted.
14
15module Network.Wai.Handler.WarpTLS (
16    -- * Runner
17      runTLS
18    , runTLSSocket
19    -- * Settings
20    , TLSSettings
21    , defaultTlsSettings
22    -- * Smart constructors
23    -- ** From files
24    , tlsSettings
25    , tlsSettingsChain
26    -- ** From memory
27    , tlsSettingsMemory
28    , tlsSettingsChainMemory
29    -- ** From references
30    , tlsSettingsRef
31    , tlsSettingsChainRef
32    -- * Accessors
33    , tlsCredentials
34    , tlsLogging
35    , tlsAllowedVersions
36    , tlsCiphers
37    , tlsWantClientCert
38    , tlsServerHooks
39    , tlsServerDHEParams
40    , tlsSessionManagerConfig
41    , tlsSessionManager
42    , onInsecure
43    , OnInsecure (..)
44    -- * Exception
45    , WarpTLSException (..)
46    -- * DH parameters (re-exports)
47    --
48    -- | This custom DH parameters are not necessary anymore because
49    --   pre-defined DH parameters are supported in the TLS package.
50    , DH.Params
51    , DH.generateParams
52    ) where
53
54import Control.Applicative ((<|>))
55import Control.Exception (Exception, throwIO, bracket, finally, handle, fromException, try, IOException, onException, SomeException(..), handleJust)
56import qualified Control.Exception as E
57import Control.Monad (void, guard)
58import qualified Data.ByteString as S
59import qualified Data.ByteString.Lazy as L
60import Data.Default.Class (def)
61import qualified Data.IORef as I
62import Data.Streaming.Network (bindPortTCP, safeRecv)
63import Data.Typeable (Typeable)
64import GHC.IO.Exception (IOErrorType(..))
65import Network.Socket (Socket, close, withSocketsDo, SockAddr, accept)
66#if MIN_VERSION_network(3,1,1)
67import Network.Socket (gracefulClose)
68#endif
69import Network.Socket.ByteString (sendAll)
70import qualified Network.TLS as TLS
71import qualified Crypto.PubKey.DH as DH
72import qualified Network.TLS.Extra as TLSExtra
73import qualified Network.TLS.SessionManager as SM
74import Network.Wai (Application)
75import Network.Wai.Handler.Warp
76import Network.Wai.Handler.Warp.Internal
77import System.IO.Error (isEOFError, ioeGetErrorType)
78
79----------------------------------------------------------------
80
81-- | Determines where to load the certificate, chain
82-- certificates, and key from.
83data CertSettings
84  = CertFromFile !FilePath ![FilePath] !FilePath
85  | CertFromMemory !S.ByteString ![S.ByteString] !S.ByteString
86  | CertFromRef !(I.IORef S.ByteString) ![I.IORef S.ByteString] !(I.IORef S.ByteString)
87
88-- | The default 'CertSettings'.
89defaultCertSettings :: CertSettings
90defaultCertSettings = CertFromFile "certificate.pem" [] "key.pem"
91
92----------------------------------------------------------------
93
94-- | Settings for WarpTLS.
95data TLSSettings = TLSSettings {
96    certSettings :: CertSettings
97    -- ^ Where are the certificate, chain certificates, and key
98    -- loaded from?
99    --
100    -- >>> certSettings defaultTlsSettings
101    -- tlsSettings "certificate.pem" "key.pem"
102    --
103    -- @since 3.3.0
104  , onInsecure :: OnInsecure
105    -- ^ Do we allow insecure connections with this server as well?
106    --
107    -- >>> onInsecure defaultTlsSettings
108    -- DenyInsecure "This server only accepts secure HTTPS connections."
109    --
110    -- Since 1.4.0
111  , tlsLogging :: TLS.Logging
112    -- ^ The level of logging to turn on.
113    --
114    -- Default: 'TLS.defaultLogging'.
115    --
116    -- Since 1.4.0
117  , tlsAllowedVersions :: [TLS.Version]
118#if MIN_VERSION_tls(1,5,0)
119    -- ^ The TLS versions this server accepts.
120    --
121    -- >>> tlsAllowedVersions defaultTlsSettings
122    -- [TLS13,TLS12,TLS11,TLS10]
123    --
124    -- Since 1.4.2
125#else
126    -- ^ The TLS versions this server accepts.
127    --
128    -- >>> tlsAllowedVersions defaultTlsSettings
129    -- [TLS12,TLS11,TLS10]
130    --
131    -- Since 1.4.2
132#endif
133  , tlsCiphers :: [TLS.Cipher]
134#if MIN_VERSION_tls(1,5,0)
135    -- ^ The TLS ciphers this server accepts.
136    --
137    -- >>> tlsCiphers defaultTlsSettings
138    -- [ECDHE-ECDSA-AES256GCM-SHA384,ECDHE-ECDSA-AES128GCM-SHA256,ECDHE-RSA-AES256GCM-SHA384,ECDHE-RSA-AES128GCM-SHA256,DHE-RSA-AES256GCM-SHA384,DHE-RSA-AES128GCM-SHA256,ECDHE-ECDSA-AES256CBC-SHA384,ECDHE-RSA-AES256CBC-SHA384,DHE-RSA-AES256-SHA256,ECDHE-ECDSA-AES256CBC-SHA,ECDHE-RSA-AES256CBC-SHA,DHE-RSA-AES256-SHA1,RSA-AES256GCM-SHA384,RSA-AES256-SHA256,RSA-AES256-SHA1,AES128GCM-SHA256,AES256GCM-SHA384]
139    --
140    -- Since 1.4.2
141#else
142    -- ^ The TLS ciphers this server accepts.
143    --
144    -- >>> tlsCiphers defaultTlsSettings
145    -- [ECDHE-ECDSA-AES256GCM-SHA384,ECDHE-ECDSA-AES128GCM-SHA256,ECDHE-RSA-AES256GCM-SHA384,ECDHE-RSA-AES128GCM-SHA256,DHE-RSA-AES256GCM-SHA384,DHE-RSA-AES128GCM-SHA256,ECDHE-ECDSA-AES256CBC-SHA384,ECDHE-RSA-AES256CBC-SHA384,DHE-RSA-AES256-SHA256,ECDHE-ECDSA-AES256CBC-SHA,ECDHE-RSA-AES256CBC-SHA,DHE-RSA-AES256-SHA1,RSA-AES256GCM-SHA384,RSA-AES256-SHA256,RSA-AES256-SHA1]
146    --
147    -- Since 1.4.2
148#endif
149  , tlsWantClientCert :: Bool
150    -- ^ Whether or not to demand a certificate from the client.  If this
151    -- is set to True, you must handle received certificates in a server hook
152    -- or all connections will fail.
153    --
154    -- >>> tlsWantClientCert defaultTlsSettings
155    -- False
156    --
157    -- Since 3.0.2
158  , tlsServerHooks :: TLS.ServerHooks
159    -- ^ The server-side hooks called by the tls package, including actions
160    -- to take when a client certificate is received.  See the "Network.TLS"
161    -- module for details.
162    --
163    -- Default: def
164    --
165    -- Since 3.0.2
166  , tlsServerDHEParams :: Maybe DH.Params
167    -- ^ Configuration for ServerDHEParams
168    -- more function lives in `cryptonite` package
169    --
170    -- Default: Nothing
171    --
172    -- Since 3.2.2
173  , tlsSessionManagerConfig :: Maybe SM.Config
174    -- ^ Configuration for in-memory TLS session manager.
175    -- If Nothing, 'TLS.noSessionManager' is used.
176    -- Otherwise, an in-memory TLS session manager is created
177    -- according to 'Config'.
178    --
179    -- Default: Nothing
180    --
181    -- Since 3.2.4
182  , tlsCredentials :: Maybe TLS.Credentials
183    -- ^ Specifying 'TLS.Credentials' directly.  If this value is
184    --   specified, other fields such as 'certFile' are ignored.
185    --
186    --   Since 3.2.12
187  , tlsSessionManager :: Maybe TLS.SessionManager
188    -- ^ Specifying 'TLS.SessionManager' directly. If this value is
189    --   specified, 'tlsSessionManagerConfig' is ignored.
190    --
191    --   Since 3.2.12
192  }
193
194-- | Default 'TLSSettings'. Use this to create 'TLSSettings' with the field record name (aka accessors).
195defaultTlsSettings :: TLSSettings
196defaultTlsSettings = TLSSettings {
197    certSettings = defaultCertSettings
198  , onInsecure = DenyInsecure "This server only accepts secure HTTPS connections."
199  , tlsLogging = def
200#if MIN_VERSION_tls(1,5,0)
201  , tlsAllowedVersions = [TLS.TLS13,TLS.TLS12,TLS.TLS11,TLS.TLS10]
202#else
203  , tlsAllowedVersions = [TLS.TLS12,TLS.TLS11,TLS.TLS10]
204#endif
205  , tlsCiphers = ciphers
206  , tlsWantClientCert = False
207  , tlsServerHooks = def
208  , tlsServerDHEParams = Nothing
209  , tlsSessionManagerConfig = Nothing
210  , tlsCredentials = Nothing
211  , tlsSessionManager = Nothing
212  }
213
214-- taken from stunnel example in tls-extra
215ciphers :: [TLS.Cipher]
216ciphers = TLSExtra.ciphersuite_strong
217
218----------------------------------------------------------------
219
220-- | An action when a plain HTTP comes to HTTP over TLS/SSL port.
221data OnInsecure = DenyInsecure L.ByteString
222                | AllowInsecure
223                deriving (Show)
224
225----------------------------------------------------------------
226
227-- | A smart constructor for 'TLSSettings' based on 'defaultTlsSettings'.
228tlsSettings :: FilePath -- ^ Certificate file
229            -> FilePath -- ^ Key file
230            -> TLSSettings
231tlsSettings cert key = defaultTlsSettings {
232    certSettings = CertFromFile cert [] key
233  }
234
235-- | A smart constructor for 'TLSSettings' that allows specifying
236-- chain certificates based on 'defaultTlsSettings'.
237--
238-- Since 3.0.3
239tlsSettingsChain
240            :: FilePath -- ^ Certificate file
241            -> [FilePath] -- ^ Chain certificate files
242            -> FilePath -- ^ Key file
243            -> TLSSettings
244tlsSettingsChain cert chainCerts key = defaultTlsSettings {
245    certSettings = CertFromFile cert chainCerts key
246  }
247
248-- | A smart constructor for 'TLSSettings', but uses in-memory representations
249-- of the certificate and key based on 'defaultTlsSettings'.
250--
251-- Since 3.0.1
252tlsSettingsMemory
253    :: S.ByteString -- ^ Certificate bytes
254    -> S.ByteString -- ^ Key bytes
255    -> TLSSettings
256tlsSettingsMemory cert key = defaultTlsSettings {
257    certSettings = CertFromMemory cert [] key
258  }
259
260-- | A smart constructor for 'TLSSettings', but uses in-memory representations
261-- of the certificate and key based on 'defaultTlsSettings'.
262--
263-- Since 3.0.3
264tlsSettingsChainMemory
265    :: S.ByteString -- ^ Certificate bytes
266    -> [S.ByteString] -- ^ Chain certificate bytes
267    -> S.ByteString -- ^ Key bytes
268    -> TLSSettings
269tlsSettingsChainMemory cert chainCerts key = defaultTlsSettings {
270    certSettings = CertFromMemory cert chainCerts key
271  }
272
273-- | A smart constructor for 'TLSSettings', but uses references to in-memory
274-- representations of the certificate and key based on 'defaultTlsSettings'.
275--
276-- @since 3.3.0
277tlsSettingsRef
278    :: I.IORef S.ByteString -- ^ Reference to certificate bytes
279    -> I.IORef (S.ByteString) -- ^ Reference to key bytes
280    -> TLSSettings
281tlsSettingsRef cert key = defaultTlsSettings {
282    certSettings = CertFromRef cert [] key
283  }
284
285-- | A smart constructor for 'TLSSettings', but uses references to in-memory
286-- representations of the certificate and key based on 'defaultTlsSettings'.
287--
288-- @since 3.3.0
289tlsSettingsChainRef
290    :: I.IORef S.ByteString -- ^ Reference to certificate bytes
291    -> [I.IORef S.ByteString] -- ^ Reference to chain certificate bytes
292    -> I.IORef (S.ByteString) -- ^ Reference to key bytes
293    -> TLSSettings
294tlsSettingsChainRef cert chainCerts key = defaultTlsSettings {
295    certSettings = CertFromRef cert chainCerts key
296  }
297
298----------------------------------------------------------------
299
300-- | Running 'Application' with 'TLSSettings' and 'Settings'.
301runTLS :: TLSSettings -> Settings -> Application -> IO ()
302runTLS tset set app = withSocketsDo $
303    bracket
304        (bindPortTCP (getPort set) (getHost set))
305        close
306        (\sock -> runTLSSocket tset set sock app)
307
308----------------------------------------------------------------
309
310loadCredentials :: TLSSettings -> IO TLS.Credentials
311loadCredentials TLSSettings{ tlsCredentials = Just creds } = return creds
312loadCredentials TLSSettings{..} = case certSettings of
313  CertFromFile cert chainFiles key -> do
314    cred <- either error id <$> TLS.credentialLoadX509Chain cert chainFiles key
315    return $ TLS.Credentials [cred]
316  CertFromRef certRef chainCertsRef keyRef -> do
317    cert <- I.readIORef certRef
318    chainCerts <- mapM I.readIORef chainCertsRef
319    key <- I.readIORef keyRef
320    cred <- either error return $ TLS.credentialLoadX509ChainFromMemory cert chainCerts key
321    return $ TLS.Credentials [cred]
322  CertFromMemory certMemory chainCertsMemory keyMemory -> do
323    cred <- either error return $ TLS.credentialLoadX509ChainFromMemory certMemory chainCertsMemory keyMemory
324    return $ TLS.Credentials [cred]
325
326getSessionManager :: TLSSettings -> IO TLS.SessionManager
327getSessionManager TLSSettings{ tlsSessionManager = Just mgr } = return mgr
328getSessionManager TLSSettings{..} = case tlsSessionManagerConfig of
329      Nothing     -> return TLS.noSessionManager
330      Just config -> SM.newSessionManager config
331
332-- | Running 'Application' with 'TLSSettings' and 'Settings' using
333--   specified 'Socket'.
334runTLSSocket :: TLSSettings -> Settings -> Socket -> Application -> IO ()
335runTLSSocket tlsset set sock app = do
336    credentials <- loadCredentials tlsset
337    mgr <- getSessionManager tlsset
338    runTLSSocket' tlsset set credentials mgr sock app
339
340runTLSSocket' :: TLSSettings -> Settings -> TLS.Credentials -> TLS.SessionManager -> Socket -> Application -> IO ()
341runTLSSocket' tlsset@TLSSettings{..} set credentials mgr sock app =
342    runSettingsConnectionMakerSecure set get app
343  where
344    get = getter tlsset set sock params
345    params = def { -- TLS.ServerParams
346        TLS.serverWantClientCert = tlsWantClientCert
347      , TLS.serverCACertificates = []
348      , TLS.serverDHEParams      = tlsServerDHEParams
349      , TLS.serverHooks          = hooks
350      , TLS.serverShared         = shared
351      , TLS.serverSupported      = supported
352#if MIN_VERSION_tls(1,5,0)
353      , TLS.serverEarlyDataSize  = 2018
354#endif
355      }
356    -- Adding alpn to user's tlsServerHooks.
357    hooks = tlsServerHooks {
358        TLS.onALPNClientSuggest = TLS.onALPNClientSuggest tlsServerHooks <|>
359          (if settingsHTTP2Enabled set then Just alpn else Nothing)
360      }
361    shared = def {
362        TLS.sharedCredentials    = credentials
363      , TLS.sharedSessionManager = mgr
364      }
365    supported = def { -- TLS.Supported
366        TLS.supportedVersions       = tlsAllowedVersions
367      , TLS.supportedCiphers        = tlsCiphers
368      , TLS.supportedCompressions   = [TLS.nullCompression]
369      , TLS.supportedSecureRenegotiation = True
370      , TLS.supportedClientInitiatedRenegotiation = False
371      , TLS.supportedSession             = True
372      , TLS.supportedFallbackScsv        = True
373#if MIN_VERSION_tls(1,5,0)
374      , TLS.supportedGroups              = [TLS.X25519,TLS.P256,TLS.P384]
375#endif
376      }
377
378alpn :: [S.ByteString] -> IO S.ByteString
379alpn xs
380  | "h2"    `elem` xs = return "h2"
381  | otherwise         = return "http/1.1"
382
383----------------------------------------------------------------
384
385getter :: TLS.TLSParams params => TLSSettings -> Settings -> Socket -> params -> IO (IO (Connection, Transport), SockAddr)
386getter tlsset set sock params = do
387#if WINDOWS
388    (s, sa) <- windowsThreadBlockHack $ accept sock
389#else
390    (s, sa) <- accept sock
391#endif
392    setSocketCloseOnExec s
393    return (mkConn tlsset set s params, sa)
394
395mkConn :: TLS.TLSParams params => TLSSettings -> Settings -> Socket -> params -> IO (Connection, Transport)
396mkConn tlsset set s params = switch `onException` close s
397  where
398    switch = do
399        firstBS <- safeRecv s 4096
400        if not (S.null firstBS) && S.head firstBS == 0x16 then
401            httpOverTls tlsset set s firstBS params
402          else
403            plainHTTP tlsset set s firstBS
404
405----------------------------------------------------------------
406
407httpOverTls :: TLS.TLSParams params => TLSSettings -> Settings -> Socket -> S.ByteString -> params -> IO (Connection, Transport)
408httpOverTls TLSSettings{..} _set s bs0 params = do
409    recvN <- makePlainReceiveN s bs0
410    ctx <- TLS.contextNew (backend recvN) params
411    TLS.contextHookSetLogging ctx tlsLogging
412    TLS.handshake ctx
413    h2 <- (== Just "h2") <$> TLS.getNegotiatedProtocol ctx
414    isH2 <- I.newIORef h2
415    writeBuf <- allocateBuffer bufferSize
416    -- Creating a cache for leftover input data.
417    ref <- I.newIORef ""
418    tls <- getTLSinfo ctx
419    return (conn ctx writeBuf ref isH2, tls)
420  where
421    backend recvN = TLS.Backend {
422        TLS.backendFlush = return ()
423#if MIN_VERSION_network(3,1,1)
424      , TLS.backendClose = gracefulClose s 5000 `E.catch` \(SomeException _) -> return ()
425#else
426      , TLS.backendClose = close s
427#endif
428      , TLS.backendSend  = sendAll' s
429      , TLS.backendRecv  = recvN
430      }
431    sendAll' sock bs = E.handleJust
432      (\ e -> if ioeGetErrorType e == ResourceVanished
433        then Just ConnectionClosedByPeer
434        else Nothing)
435      throwIO
436      $ sendAll sock bs
437    conn ctx writeBuf ref isH2 = Connection {
438        connSendMany         = TLS.sendData ctx . L.fromChunks
439      , connSendAll          = sendall
440      , connSendFile         = sendfile
441      , connClose            = close'
442      , connFree             = freeBuffer writeBuf
443      , connRecv             = recv ref
444      , connRecvBuf          = recvBuf ref
445      , connWriteBuffer      = writeBuf
446      , connBufferSize       = bufferSize
447      , connHTTP2            = isH2
448      }
449      where
450        sendall = TLS.sendData ctx . L.fromChunks . return
451        sendfile fid offset len hook headers =
452            readSendFile writeBuf bufferSize sendall fid offset len hook headers
453
454        close' = void (tryIO sendBye) `finally`
455                 TLS.contextClose ctx
456
457        sendBye =
458          -- It's fine if the connection was closed by the other side before
459          -- receiving close_notify, see RFC 5246 section 7.2.1.
460          handleJust
461            (\e -> guard (e == ConnectionClosedByPeer) >> return e)
462            (const (return ()))
463            (TLS.bye ctx)
464
465        -- TLS version of recv with a cache for leftover input data.
466        -- The cache is shared with recvBuf.
467        recv cref = do
468            cached <- I.readIORef cref
469            if cached /= "" then do
470                I.writeIORef cref ""
471                return cached
472              else
473                recv'
474
475        -- TLS version of recv (decrypting) without a cache.
476        recv' = handle onEOF go
477          where
478            onEOF e
479              | Just TLS.Error_EOF <- fromException e       = return S.empty
480              | Just ioe <- fromException e, isEOFError ioe = return S.empty                  | otherwise                                   = throwIO e
481            go = do
482                x <- TLS.recvData ctx
483                if S.null x then
484                    go
485                  else
486                    return x
487
488        -- TLS version of recvBuf with a cache for leftover input data.
489        recvBuf cref buf siz = do
490            cached <- I.readIORef cref
491            (ret, leftover) <- fill cached buf siz recv'
492            I.writeIORef cref leftover
493            return ret
494
495fill :: S.ByteString -> Buffer -> BufSize -> Recv -> IO (Bool,S.ByteString)
496fill bs0 buf0 siz0 recv
497  | siz0 <= len0 = do
498      let (bs, leftover) = S.splitAt siz0 bs0
499      void $ copy buf0 bs
500      return (True, leftover)
501  | otherwise = do
502      buf <- copy buf0 bs0
503      loop buf (siz0 - len0)
504  where
505    len0 = S.length bs0
506    loop _   0   = return (True, "")
507    loop buf siz = do
508      bs <- recv
509      let len = S.length bs
510      if len == 0 then return (False, "")
511        else if (len <= siz) then do
512          buf' <- copy buf bs
513          loop buf' (siz - len)
514        else do
515          let (bs1,bs2) = S.splitAt siz bs
516          void $ copy buf bs1
517          return (True, bs2)
518
519getTLSinfo :: TLS.Context -> IO Transport
520getTLSinfo ctx = do
521    proto <- TLS.getNegotiatedProtocol ctx
522    minfo <- TLS.contextGetInformation ctx
523    case minfo of
524        Nothing   -> return TCP
525        Just TLS.Information{..} -> do
526            let (major, minor) = case infoVersion of
527                    TLS.SSL2  -> (2,0)
528                    TLS.SSL3  -> (3,0)
529                    TLS.TLS10 -> (3,1)
530                    TLS.TLS11 -> (3,2)
531                    TLS.TLS12 -> (3,3)
532#if MIN_VERSION_tls(1,5,0)
533                    TLS.TLS13 -> (3,4)
534#endif
535            clientCert <- TLS.getClientCertificateChain ctx
536            return TLS {
537                tlsMajorVersion = major
538              , tlsMinorVersion = minor
539              , tlsNegotiatedProtocol = proto
540              , tlsChiperID = TLS.cipherID infoCipher
541              , tlsClientCertificate = clientCert
542              }
543
544tryIO :: IO a -> IO (Either IOException a)
545tryIO = try
546
547----------------------------------------------------------------
548
549plainHTTP :: TLSSettings -> Settings -> Socket -> S.ByteString -> IO (Connection, Transport)
550plainHTTP TLSSettings{..} set s bs0 = case onInsecure of
551    AllowInsecure -> do
552        conn' <- socketConnection set s
553        cachedRef <- I.newIORef bs0
554        let conn'' = conn'
555                { connRecv = recvPlain cachedRef (connRecv conn')
556                }
557        return (conn'', TCP)
558    DenyInsecure lbs -> do
559        -- Listening port 443 but TLS records do not arrive.
560        -- We want to let the browser know that TLS is required.
561        -- So, we use 426.
562        --     http://tools.ietf.org/html/rfc2817#section-4.2
563        --     https://tools.ietf.org/html/rfc7231#section-6.5.15
564        -- FIXME: should we distinguish HTTP/1.1 and HTTP/2?
565        --        In the case of HTTP/2, should we send
566        --        GOAWAY + INADEQUATE_SECURITY?
567        -- FIXME: Content-Length:
568        -- FIXME: TLS/<version>
569        sendAll s "HTTP/1.1 426 Upgrade Required\
570        \r\nUpgrade: TLS/1.0, HTTP/1.1\
571        \r\nConnection: Upgrade\
572        \r\nContent-Type: text/plain\r\n\r\n"
573        mapM_ (sendAll s) $ L.toChunks lbs
574        close s
575        throwIO InsecureConnectionDenied
576
577----------------------------------------------------------------
578
579-- | Modify the given receive function to first check the given @IORef@ for a
580-- chunk of data. If present, takes the chunk of data from the @IORef@ and
581-- empties out the @IORef@. Otherwise, calls the supplied receive function.
582recvPlain :: I.IORef S.ByteString -> IO S.ByteString -> IO S.ByteString
583recvPlain ref fallback = do
584    bs <- I.readIORef ref
585    if S.null bs
586        then fallback
587        else do
588            I.writeIORef ref S.empty
589            return bs
590
591----------------------------------------------------------------
592
593data WarpTLSException = InsecureConnectionDenied
594    deriving (Show, Typeable)
595instance Exception WarpTLSException
596