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