@@ -1,74 +1,62 @@
1−{-# LANGUAGE CPP #-}
21{-# LANGUAGE OverloadedStrings #-}
32{-# LANGUAGE RankNTypes #-}
43{-# LANGUAGE RecordWildCards #-}
54{-# LANGUAGE DeriveDataTypeable #-}
65{-# LANGUAGE PatternGuards #-}
6+{-# LANGUAGE CPP #-}
77
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.
8+-- | HTTP over SSL/TLS support for Warp via the TLS package.
149
1510module Network.Wai.Handler.WarpTLS (
1611 -- * Settings
1712 TLSSettings
18− , defaultTlsSettings
19− -- * Smart constructors
20− , tlsSettings
21− , tlsSettingsMemory
22− , tlsSettingsChain
23− , tlsSettingsChainMemory
24− -- * Accessors
2513 , certFile
2614 , keyFile
15+ , onInsecure
2716 , tlsLogging
2817 , tlsAllowedVersions
2918 , tlsCiphers
3019 , tlsWantClientCert
3120 , tlsServerHooks
32− , tlsServerDHEParams
33− , tlsSessionManagerConfig
34− , onInsecure
21+ , defaultTlsSettings
22+ , tlsSettings
23+ , tlsSettingsMemory
24+ , tlsSettingsChain
25+ , tlsSettingsChainMemory
3526 , OnInsecure (..)
3627 -- * Runner
3728 , runTLS
3829 , runTLSSocket
3930 -- * Exception
4031 , WarpTLSException (..)
41− , DH.Params
42− , DH.generateParams
4332 ) where
4433
45−import Control.Applicative ((<|>))
46−import Control.Exception (Exception, throwIO, bracket, finally, handle, fromException, try, IOException, onException, SomeException(..), handleJust)
47−import qualified Control.Exception as E
48−import Control.Monad (void, guard)
34+#if __GLASGOW_HASKELL__ < 709
35+import Control.Applicative ((<$>))
36+#endif
37+import Control.Exception (Exception, throwIO, bracket, finally, handle, fromException, try, IOException, onException)
38+import Control.Monad (unless, void)
39+import qualified Crypto.Random.AESCtr
40+import qualified Data.ByteString as B
4941import qualified Data.ByteString as S
5042import qualified Data.ByteString.Lazy as L
43+import Data.ByteString.Lazy.Internal (defaultChunkSize)
5144import Data.Default.Class (def)
5245import qualified Data.IORef as I
5346import Data.Streaming.Network (bindPortTCP, safeRecv)
5447import Data.Typeable (Typeable)
55−import Network.Socket (Socket, close, withSocketsDo, SockAddr, accept)
56−#if MIN_VERSION_network(3,1,1)
57−import Network.Socket (gracefulClose)
58−#endif
48+import Network.Socket (Socket, sClose, withSocketsDo, SockAddr, accept)
5949import Network.Socket.ByteString (sendAll)
6050import qualified Network.TLS as TLS
61−import qualified Crypto.PubKey.DH as DH
6251import qualified Network.TLS.Extra as TLSExtra
63−import qualified Network.TLS.SessionManager as SM
6452import Network.Wai (Application)
6553import Network.Wai.Handler.Warp
66−import Network.Wai.Handler.Warp.Internal
54+import Network.Wai.Handler.Warp.Buffer (allocateBuffer, bufferSize, freeBuffer)
55+import qualified System.IO as IO
6756import System.IO.Error (isEOFError)
6857
6958----------------------------------------------------------------
7059
71−-- | Settings for WarpTLS.
7260data TLSSettings = TLSSettings {
7361 certFile :: FilePath
7462 -- ^ File containing the certificate.
@@ -80,10 +68,8 @@ data TLSSettings = TLSSettings {
8068 , chainCertsMemory :: [S.ByteString]
8169 , keyMemory :: Maybe S.ByteString
8270 , onInsecure :: OnInsecure
83− -- ^ Do we allow insecure connections with this server as well?
84− --
85− -- >>> onInsecure defaultTlsSettings
86− -- DenyInsecure "This server only accepts secure HTTPS connections."
71+ -- ^ Do we allow insecure connections with this server as well? Default
72+ -- is a simple text response stating that a secure connection is required.
8773 --
8874 -- Since 1.4.0
8975 , tlsLogging :: TLS.Logging
@@ -93,44 +79,23 @@ data TLSSettings = TLSSettings {
9379 --
9480 -- Since 1.4.0
9581 , tlsAllowedVersions :: [TLS.Version]
96−#if MIN_VERSION_tls(1,5,0)
97− -- ^ The TLS versions this server accepts.
98− --
99− -- >>> tlsAllowedVersions defaultTlsSettings
100− -- [TLS13,TLS12,TLS11,TLS10]
101− --
102− -- Since 1.4.2
103−#else
10482 -- ^ The TLS versions this server accepts.
10583 --
106− -- >>> tlsAllowedVersions defaultTlsSettings
107− -- [TLS12,TLS11,TLS10]
84+ -- Default: '[TLS.TLS10,TLS.TLS11,TLS.TLS12]'.
10885 --
10986 -- Since 1.4.2
110−#endif
11187 , tlsCiphers :: [TLS.Cipher]
112−#if MIN_VERSION_tls(1,5,0)
113− -- ^ The TLS ciphers this server accepts.
114− --
115− -- >>> tlsCiphers defaultTlsSettings
116− -- [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]
117− --
118− -- Since 1.4.2
119−#else
12088 -- ^ The TLS ciphers this server accepts.
12189 --
122− -- >>> tlsCiphers defaultTlsSettings
123− -- [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]
90+ -- Default: '[TLSExtra.cipher_AES128_SHA1, TLSExtra.cipher_AES256_SHA1, TLSEtra.cipher_RC4_128_MD5, TLSExtra.cipher_RC4_128_SHA1]'
12491 --
12592 -- Since 1.4.2
126−#endif
12793 , tlsWantClientCert :: Bool
12894 -- ^ Whether or not to demand a certificate from the client. If this
12995 -- is set to True, you must handle received certificates in a server hook
13096 -- or all connections will fail.
13197 --
132− -- >>> tlsWantClientCert defaultTlsSettings
133− -- False
98+ -- Default: False
13499 --
135100 -- Since 3.0.2
136101 , tlsServerHooks :: TLS.ServerHooks
@@ -141,25 +106,9 @@ data TLSSettings = TLSSettings {
141106 -- Default: def
142107 --
143108 -- Since 3.0.2
144− , tlsServerDHEParams :: Maybe DH.Params
145− -- ^ Configuration for ServerDHEParams
146− -- more function lives in `cryptonite` package
147− --
148− -- Default: Nothing
149− --
150− -- Since 3.2.2
151− , tlsSessionManagerConfig :: Maybe SM.Config
152− -- ^ Configuration for in-memory TLS session manager.
153− -- If Nothing, 'TLS.noSessionManager' is used.
154− -- Otherwise, an in-memory TLS session manager is created
155− -- according to 'Config'.
156− --
157− -- Default: Nothing
158− --
159− -- Since 3.2.4
160109 }
161110
162−-- | Default 'TLSSettings'. Use this to create 'TLSSettings' with the field record name (aka accessors).
111+-- | Default 'TLSSettings'. Use this to create 'TLSSettings' with the field record name.
163112defaultTlsSettings :: TLSSettings
164113defaultTlsSettings = TLSSettings {
165114 certFile = "certificate.pem"
@@ -170,32 +119,39 @@ defaultTlsSettings = TLSSettings {
170119 , keyMemory = Nothing
171120 , onInsecure = DenyInsecure "This server only accepts secure HTTPS connections."
172121 , tlsLogging = def
173−#if MIN_VERSION_tls(1,5,0)
174− , tlsAllowedVersions = [TLS.TLS13,TLS.TLS12,TLS.TLS11,TLS.TLS10]
175−#else
176− , tlsAllowedVersions = [TLS.TLS12,TLS.TLS11,TLS.TLS10]
177−#endif
122+ , tlsAllowedVersions = [TLS.TLS10,TLS.TLS11,TLS.TLS12]
178123 , tlsCiphers = ciphers
179124 , tlsWantClientCert = False
180125 , tlsServerHooks = def
181− , tlsServerDHEParams = Nothing
182− , tlsSessionManagerConfig = Nothing
183126 }
184127
185128-- taken from stunnel example in tls-extra
186129ciphers :: [TLS.Cipher]
187−ciphers = TLSExtra.ciphersuite_strong
130+ciphers =
131+ [ TLSExtra.cipher_ECDHE_RSA_AES128GCM_SHA256
132+ , TLSExtra.cipher_DHE_RSA_AES128GCM_SHA256
133+ , TLSExtra.cipher_DHE_RSA_AES256_SHA256
134+ , TLSExtra.cipher_DHE_RSA_AES128_SHA256
135+ , TLSExtra.cipher_DHE_RSA_AES256_SHA1
136+ , TLSExtra.cipher_DHE_RSA_AES128_SHA1
137+ , TLSExtra.cipher_DHE_DSS_AES128_SHA1
138+ , TLSExtra.cipher_DHE_DSS_AES256_SHA1
139+ , TLSExtra.cipher_DHE_DSS_RC4_SHA1
140+ , TLSExtra.cipher_AES128_SHA1
141+ , TLSExtra.cipher_AES256_SHA1
142+ , TLSExtra.cipher_RC4_128_MD5
143+ , TLSExtra.cipher_RC4_128_SHA1
144+ ]
188145
189146----------------------------------------------------------------
190147
191148-- | An action when a plain HTTP comes to HTTP over TLS/SSL port.
192149data OnInsecure = DenyInsecure L.ByteString
193150 | AllowInsecure
194− deriving (Show)
195151
196152----------------------------------------------------------------
197153
198−-- | A smart constructor for 'TLSSettings' based on 'defaultTlsSettings'.
154+-- | A smart constructor for 'TLSSettings'.
199155tlsSettings :: FilePath -- ^ Certificate file
200156 -> FilePath -- ^ Key file
201157 -> TLSSettings
@@ -205,7 +161,7 @@ tlsSettings cert key = defaultTlsSettings {
205161 }
206162
207163-- | A smart constructor for 'TLSSettings' that allows specifying
208−-- chain certificates based on 'defaultTlsSettings'.
164+-- chain certificates.
209165--
210166-- Since 3.0.3
211167tlsSettingsChain
@@ -220,7 +176,7 @@ tlsSettingsChain cert chainCerts key = defaultTlsSettings {
220176 }
221177
222178-- | A smart constructor for 'TLSSettings', but uses in-memory representations
223−-- of the certificate and key based on 'defaultTlsSettings'.
179+-- of the certificate and key
224180--
225181-- Since 3.0.1
226182tlsSettingsMemory
@@ -233,7 +189,7 @@ tlsSettingsMemory cert key = defaultTlsSettings
233189 }
234190
235191-- | A smart constructor for 'TLSSettings', but uses in-memory representations
236−-- of the certificate and key based on 'defaultTlsSettings'.
192+-- of the certificate and key
237193--
238194-- Since 3.0.3
239195tlsSettingsChainMemory
@@ -249,12 +205,13 @@ tlsSettingsChainMemory cert chainCerts key = defaultTlsSettings
249205
250206----------------------------------------------------------------
251207
208+
252209-- | Running 'Application' with 'TLSSettings' and 'Settings'.
253210runTLS :: TLSSettings -> Settings -> Application -> IO ()
254211runTLS tset set app = withSocketsDo $
255212 bracket
256213 (bindPortTCP (getPort set) (getHost set))
257− close
214+ sClose
258215 (\sock -> runTLSSocket tset set sock app)
259216
260217----------------------------------------------------------------
@@ -272,186 +229,104 @@ runTLSSocket tlsset@TLSSettings{..} set sock app = do
272229 key <- maybe (S.readFile keyFile) return mkey
273230 either error return $
274231 TLS.credentialLoadX509ChainFromMemory cert chainCertsMemory key
275− mgr <- case tlsSessionManagerConfig of
276− Nothing -> return TLS.noSessionManager
277− Just config -> SM.newSessionManager config
278− runTLSSocket' tlsset set credential mgr sock app
232+ runTLSSocket' tlsset set credential sock app
279233
280−runTLSSocket' :: TLSSettings -> Settings -> TLS.Credential -> TLS.SessionManager -> Socket -> Application -> IO ()
281−runTLSSocket' tlsset@TLSSettings{..} set credential mgr sock app =
234+runTLSSocket' :: TLSSettings -> Settings -> TLS.Credential -> Socket -> Application -> IO ()
235+runTLSSocket' tlsset@TLSSettings{..} set credential sock app =
282236 runSettingsConnectionMakerSecure set get app
283237 where
284− get = getter tlsset set sock params
285− params = def { -- TLS.ServerParams
238+ get = getter tlsset sock params
239+ params = def {
286240 TLS.serverWantClientCert = tlsWantClientCert
287− , TLS.serverCACertificates = []
288− , TLS.serverDHEParams = tlsServerDHEParams
289− , TLS.serverHooks = hooks
290− , TLS.serverShared = shared
291− , TLS.serverSupported = supported
292−#if MIN_VERSION_tls(1,5,0)
293− , TLS.serverEarlyDataSize = 2018
294−#endif
295− }
296− -- Adding alpn to user's tlsServerHooks.
297− hooks = tlsServerHooks {
298− TLS.onALPNClientSuggest = TLS.onALPNClientSuggest tlsServerHooks <|>
299− (if settingsHTTP2Enabled set then Just alpn else Nothing)
241+ , TLS.serverSupported = def {
242+ TLS.supportedVersions = tlsAllowedVersions
243+ , TLS.supportedCiphers = tlsCiphers
244+ }
245+ , TLS.serverShared = def {
246+ TLS.sharedCredentials = TLS.Credentials [credential]
247+ }
248+ , TLS.serverHooks = tlsServerHooks
300249 }
301− shared = def {
302− TLS.sharedCredentials = TLS.Credentials [credential]
303− , TLS.sharedSessionManager = mgr
304− }
305− supported = def { -- TLS.Supported
306− TLS.supportedVersions = tlsAllowedVersions
307− , TLS.supportedCiphers = tlsCiphers
308− , TLS.supportedCompressions = [TLS.nullCompression]
309− , TLS.supportedSecureRenegotiation = True
310− , TLS.supportedClientInitiatedRenegotiation = False
311− , TLS.supportedSession = True
312− , TLS.supportedFallbackScsv = True
313−#if MIN_VERSION_tls(1,5,0)
314− , TLS.supportedGroups = [TLS.X25519,TLS.P256,TLS.P384]
315−#endif
316− }
317−
318−alpn :: [S.ByteString] -> IO S.ByteString
319−alpn xs
320− | "h2" `elem` xs = return "h2"
321− | otherwise = return "http/1.1"
322250
323251----------------------------------------------------------------
324252
325−getter :: TLS.TLSParams params => TLSSettings -> Settings -> Socket -> params -> IO (IO (Connection, Transport), SockAddr)
326−getter tlsset@TLSSettings{..} set sock params = do
327−#if WINDOWS
328− (s, sa) <- windowsThreadBlockHack $ accept sock
329−#else
253+getter :: TLS.TLSParams params => TLSSettings -> Socket -> params -> IO (IO (Connection, Transport), SockAddr)
254+getter tlsset@TLSSettings{..} sock params = do
330255 (s, sa) <- accept sock
331−#endif
332− setSocketCloseOnExec s
333− return (mkConn tlsset set s params, sa)
256+ return (mkConn tlsset s params, sa)
334257
335−mkConn :: TLS.TLSParams params => TLSSettings -> Settings -> Socket -> params -> IO (Connection, Transport)
336−mkConn tlsset set s params = switch `onException` close s
337− where
338− switch = do
339− firstBS <- safeRecv s 4096
340− if not (S.null firstBS) && S.head firstBS == 0x16 then
341− httpOverTls tlsset set s firstBS params
342− else
343− plainHTTP tlsset set s firstBS
258+mkConn :: TLS.TLSParams params => TLSSettings -> Socket -> params -> IO (Connection, Transport)
259+mkConn tlsset s params = do
260+ firstBS <- safeRecv s 4096
261+ cachedRef <- I.newIORef firstBS
262+ (if not (B.null firstBS) && B.head firstBS == 0x16 then
263+ httpOverTls tlsset s cachedRef params
264+ else
265+ plainHTTP tlsset s cachedRef) `onException` sClose s
344266
345267----------------------------------------------------------------
346268
347−httpOverTls :: TLS.TLSParams params => TLSSettings -> Settings -> Socket -> S.ByteString -> params -> IO (Connection, Transport)
348−httpOverTls TLSSettings{..} _set s bs0 params = do
349− recvN <- makePlainReceiveN s bs0
350− ctx <- TLS.contextNew (backend recvN) params
269+httpOverTls :: TLS.TLSParams params => TLSSettings -> Socket -> I.IORef B.ByteString -> params -> IO (Connection, Transport)
270+httpOverTls TLSSettings{..} s cachedRef params = do
271+ gen <- Crypto.Random.AESCtr.makeSystem
272+ ctx <- TLS.contextNew backend params gen
351273 TLS.contextHookSetLogging ctx tlsLogging
352274 TLS.handshake ctx
353− h2 <- (== Just "h2") <$> TLS.getNegotiatedProtocol ctx
354− isH2 <- I.newIORef h2
275+ readBuf <- allocateBuffer bufferSize
355276 writeBuf <- allocateBuffer bufferSize
356− -- Creating a cache for leftover input data.
357− ref <- I.newIORef ""
358277 tls <- getTLSinfo ctx
359− return (conn ctx writeBuf ref isH2, tls)
278+ return (conn ctx readBuf writeBuf, tls)
360279 where
361− backend recvN = TLS.Backend {
280+ backend = TLS.Backend {
362281 TLS.backendFlush = return ()
363−#if MIN_VERSION_network(3,1,1)
364− , TLS.backendClose = gracefulClose s 5000 `E.catch` \(SomeException _) -> return ()
365−#else
366− , TLS.backendClose = close s
367−#endif
368− , TLS.backendSend = sendAll' s
369− , TLS.backendRecv = recvN
282+ , TLS.backendClose = sClose s
283+ , TLS.backendSend = sendAll s
284+ , TLS.backendRecv = recvTLS cachedRef s
370285 }
371− sendAll' sock bs = sendAll sock bs `E.catch` \(SomeException _) ->
372− throwIO ConnectionClosedByPeer
373− conn ctx writeBuf ref isH2 = Connection {
286+ conn ctx readBuf writeBuf = Connection {
374287 connSendMany = TLS.sendData ctx . L.fromChunks
375− , connSendAll = sendall
288+ , connSendAll = TLS.sendData ctx . L.fromChunks . return
376289 , connSendFile = sendfile
377− , connClose = close'
378− , connFree = freeBuffer writeBuf
379− , connRecv = recv ref
380− , connRecvBuf = recvBuf ref
290+ , connClose = close
291+ , connRecv = recv
292+ , connSendFileOverride = NotOverride
293+ , connReadBuffer = readBuf
381294 , connWriteBuffer = writeBuf
382295 , connBufferSize = bufferSize
383− , connHTTP2 = isH2
384296 }
385297 where
386− sendall = TLS.sendData ctx . L.fromChunks . return
387− sendfile fid offset len hook headers =
388− readSendFile writeBuf bufferSize sendall fid offset len hook headers
389−
390− close' = void (tryIO sendBye) `finally`
391− TLS.contextClose ctx
392−
393− sendBye =
394− -- It's fine if the connection was closed by the other side before
395− -- receiving close_notify, see RFC 5246 section 7.2.1.
396− handleJust
397− (\e -> guard (e == ConnectionClosedByPeer) >> return e)
398− (const (return ()))
399− (TLS.bye ctx)
400−
401− -- TLS version of recv with a cache for leftover input data.
402− -- The cache is shared with recvBuf.
403− recv cref = do
404− cached <- I.readIORef cref
405− if cached /= "" then do
406− I.writeIORef cref ""
407− return cached
408− else
409− recv'
410−
411− -- TLS version of recv (decrypting) without a cache.
412− recv' = handle onEOF go
298+ sendfile fp offset len tickle' headers = do
299+ TLS.sendData ctx $ L.fromChunks headers
300+ IO.withBinaryFile fp IO.ReadMode $ \h -> do
301+ IO.hSeek h IO.AbsoluteSeek offset
302+ loop h $ fromIntegral len
303+ where
304+ loop _ remaining | remaining <= 0 = return ()
305+ loop h remaining = do
306+ bs <- B.hGetSome h defaultChunkSize
307+ unless (B.null bs) $ do
308+ let x = B.take remaining bs
309+ TLS.sendData ctx $ L.fromChunks [x]
310+ void $ tickle' -- This tickles the Timer. It MUST be called per chunk.
311+ loop h $ remaining - B.length x
312+
313+ close = freeBuffer readBuf `finally`
314+ freeBuffer writeBuf `finally`
315+ void (tryIO $ TLS.bye ctx) `finally`
316+ TLS.contextClose ctx
317+
318+ recv = handle onEOF go
413319 where
414320 onEOF e
415− | Just TLS.Error_EOF <- fromException e = return S.empty
416− | Just ioe <- fromException e, isEOFError ioe = return S.empty | otherwise = throwIO e
321+ | Just TLS.Error_EOF <- fromException e = return B.empty
322+ | Just ioe <- fromException e, isEOFError ioe = return B.empty | otherwise = throwIO e
417323 go = do
418324 x <- TLS.recvData ctx
419− if S.null x then
325+ if B.null x then
420326 go
421327 else
422328 return x
423329
424− -- TLS version of recvBuf with a cache for leftover input data.
425− recvBuf cref buf siz = do
426− cached <- I.readIORef cref
427− (ret, leftover) <- fill cached buf siz recv'
428− I.writeIORef cref leftover
429− return ret
430−
431−fill :: S.ByteString -> Buffer -> BufSize -> Recv -> IO (Bool,S.ByteString)
432−fill bs0 buf0 siz0 recv
433− | siz0 <= len0 = do
434− let (bs, leftover) = S.splitAt siz0 bs0
435− void $ copy buf0 bs
436− return (True, leftover)
437− | otherwise = do
438− buf <- copy buf0 bs0
439− loop buf (siz0 - len0)
440− where
441− len0 = S.length bs0
442− loop _ 0 = return (True, "")
443− loop buf siz = do
444− bs <- recv
445− let len = S.length bs
446− if len == 0 then return (False, "")
447− else if (len <= siz) then do
448− buf' <- copy buf bs
449− loop buf' (siz - len)
450− else do
451− let (bs1,bs2) = S.splitAt siz bs
452− void $ copy buf bs1
453− return (True, bs2)
454−
455330getTLSinfo :: TLS.Context -> IO Transport
456331getTLSinfo ctx = do
457332 proto <- TLS.getNegotiatedProtocol ctx
@@ -465,16 +340,11 @@ getTLSinfo ctx = do
465340 TLS.TLS10 -> (3,1)
466341 TLS.TLS11 -> (3,2)
467342 TLS.TLS12 -> (3,3)
468−#if MIN_VERSION_tls(1,5,0)
469− TLS.TLS13 -> (3,4)
470−#endif
471− clientCert <- TLS.getClientCertificateChain ctx
472343 return TLS {
473344 tlsMajorVersion = major
474345 , tlsMinorVersion = minor
475346 , tlsNegotiatedProtocol = proto
476347 , tlsChiperID = TLS.cipherID infoCipher
477− , tlsClientCertificate = clientCert
478348 }
479349
480350tryIO :: IO a -> IO (Either IOException a)
@@ -482,46 +352,52 @@ tryIO = try
482352
483353----------------------------------------------------------------
484354
485−plainHTTP :: TLSSettings -> Settings -> Socket -> S.ByteString -> IO (Connection, Transport)
486−plainHTTP TLSSettings{..} set s bs0 = case onInsecure of
355+plainHTTP :: TLSSettings -> Socket -> I.IORef B.ByteString -> IO (Connection, Transport)
356+plainHTTP TLSSettings{..} s cachedRef = case onInsecure of
487357 AllowInsecure -> do
488− conn' <- socketConnection set s
489− cachedRef <- I.newIORef bs0
358+ conn' <- socketConnection s
490359 let conn'' = conn'
491360 { connRecv = recvPlain cachedRef (connRecv conn')
492361 }
493362 return (conn'', TCP)
494363 DenyInsecure lbs -> do
495− -- Listening port 443 but TLS records do not arrive.
496− -- We want to let the browser know that TLS is required.
497− -- So, we use 426.
498− -- http://tools.ietf.org/html/rfc2817#section-4.2
499− -- https://tools.ietf.org/html/rfc7231#section-6.5.15
500− -- FIXME: should we distinguish HTTP/1.1 and HTTP/2?
501− -- In the case of HTTP/2, should we send
502− -- GOAWAY + INADEQUATE_SECURITY?
503− -- FIXME: Content-Length:
504− -- FIXME: TLS/<version>
505− sendAll s "HTTP/1.1 426 Upgrade Required\
506− \r\nUpgrade: TLS/1.0, HTTP/1.1\
507− \r\nConnection: Upgrade\
508− \r\nContent-Type: text/plain\r\n\r\n"
364+ sendAll s "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n"
509365 mapM_ (sendAll s) $ L.toChunks lbs
510− close s
366+ sClose s
511367 throwIO InsecureConnectionDenied
512368
513369----------------------------------------------------------------
514370
371+recvTLS :: I.IORef B.ByteString -> Socket -> Int -> IO B.ByteString
372+recvTLS cachedRef s size = do
373+ cached <- I.readIORef cachedRef
374+ loop cached
375+ where
376+ loop bs | B.length bs >= size = do
377+ let (x, y) = B.splitAt size bs
378+ I.writeIORef cachedRef y
379+ return x
380+ loop bs1 = do
381+ bs2 <- safeRecv s 4096
382+ if B.null bs2 then do
383+ -- FIXME does this deserve an exception being thrown?
384+ I.writeIORef cachedRef B.empty
385+ return bs1
386+ else
387+ loop $ B.append bs1 bs2
388+
389+----------------------------------------------------------------
390+
515391-- | Modify the given receive function to first check the given @IORef@ for a
516392-- chunk of data. If present, takes the chunk of data from the @IORef@ and
517393-- empties out the @IORef@. Otherwise, calls the supplied receive function.
518−recvPlain :: I.IORef S.ByteString -> IO S.ByteString -> IO S.ByteString
394+recvPlain :: I.IORef B.ByteString -> IO B.ByteString -> IO B.ByteString
519395recvPlain ref fallback = do
520396 bs <- I.readIORef ref
521− if S.null bs
397+ if B.null bs
522398 then fallback
523399 else do
524− I.writeIORef ref S.empty
400+ I.writeIORef ref B.empty
525401 return bs
526402
527403----------------------------------------------------------------