@@ -1,74 +1,59 @@
2{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE RankNTypes #-}
2{-# LANGUAGE RankNTypes #-}
4{-# LANGUAGE RecordWildCards #-}
3{-# LANGUAGE RecordWildCards #-}
5{-# LANGUAGE DeriveDataTypeable #-}
4{-# LANGUAGE DeriveDataTypeable #-}
6{-# LANGUAGE PatternGuards #-}
5{-# LANGUAGE PatternGuards #-}
8−-- | HTTP over TLS support for Warp via the TLS package.
8+-- | HTTP over SSL/TLS support for Warp via the TLS package.
10−-- If HTTP\/2 is negotiated by ALPN, HTTP\/2 over TLS is used.
11−-- Otherwise HTTP\/1.1 over TLS is used.
13−-- Support for SSL is now obsoleted.
15module Network.Wai.Handler.WarpTLS (
10module Network.Wai.Handler.WarpTLS (
16 -- * Settings
11 -- * Settings
17 TLSSettings
12 TLSSettings
19− -- * Smart constructors
23− , tlsSettingsChainMemory
25 , certFile
13 , certFile
27 , tlsLogging
16 , tlsLogging
28 , tlsAllowedVersions
17 , tlsAllowedVersions
29 , tlsCiphers
18 , tlsCiphers
30 , tlsWantClientCert
19 , tlsWantClientCert
31 , tlsServerHooks
20 , tlsServerHooks
32− , tlsServerDHEParams
21+ , defaultTlsSettings
33− , tlsSessionManagerConfig
22+ , tlsSettings
34− , onInsecure
23+ , tlsSettingsMemory
25+ , tlsSettingsChainMemory
35 , OnInsecure (..)
26 , OnInsecure (..)
36 -- * Runner
27 -- * Runner
38 , runTLSSocket
29 , runTLSSocket
39 -- * Exception
30 -- * Exception
40 , WarpTLSException (..)
31 , WarpTLSException (..)
34+#if __GLASGOW_HASKELL__ < 709
45−import Control.Applicative ((<|>))
35+import Control.Applicative ((<$>))
46−import Control.Exception (Exception, throwIO, bracket, finally, handle, fromException, try, IOException, onException, SomeException(..), handleJust)
37+import Control.Exception (Exception, throwIO, bracket, finally, handle, fromException, try, IOException, onException)
47−import qualified Control.Exception as E
48−import Control.Monad (void, guard)
38+import Control.Monad (void)
39+import qualified Crypto.Random.AESCtr
40+import qualified Data.ByteString as B
49import qualified Data.ByteString as S
41import qualified Data.ByteString as S
50import qualified Data.ByteString.Lazy as L
42import qualified Data.ByteString.Lazy as L
51import Data.Default.Class (def)
43import Data.Default.Class (def)
52import qualified Data.IORef as I
44import qualified Data.IORef as I
53import Data.Streaming.Network (bindPortTCP, safeRecv)
45import Data.Streaming.Network (bindPortTCP, safeRecv)
54import Data.Typeable (Typeable)
46import Data.Typeable (Typeable)
55−import Network.Socket (Socket, close, withSocketsDo, SockAddr, accept)
47+import Network.Socket (Socket, sClose, withSocketsDo, SockAddr, accept)
56−#if MIN_VERSION_network(3,1,1)
57−import Network.Socket (gracefulClose)
59import Network.Socket.ByteString (sendAll)
48import Network.Socket.ByteString (sendAll)
60import qualified Network.TLS as TLS
49import qualified Network.TLS as TLS
61−import qualified Crypto.PubKey.DH as DH
62import qualified Network.TLS.Extra as TLSExtra
50import qualified Network.TLS.Extra as TLSExtra
63−import qualified Network.TLS.SessionManager as SM
64import Network.Wai (Application)
51import Network.Wai (Application)
65import Network.Wai.Handler.Warp
52import Network.Wai.Handler.Warp
66−import Network.Wai.Handler.Warp.Internal
67import System.IO.Error (isEOFError)
53import System.IO.Error (isEOFError)
69----------------------------------------------------------------
55----------------------------------------------------------------
71−-- | Settings for WarpTLS.
72data TLSSettings = TLSSettings {
57data TLSSettings = TLSSettings {
73 certFile :: FilePath
58 certFile :: FilePath
74 -- ^ File containing the certificate.
59 -- ^ File containing the certificate.
@@ -80,10 +65,8 @@ data TLSSettings = TLSSettings {
80 , chainCertsMemory :: [S.ByteString]
65 , chainCertsMemory :: [S.ByteString]
81 , keyMemory :: Maybe S.ByteString
66 , keyMemory :: Maybe S.ByteString
82 , onInsecure :: OnInsecure
67 , onInsecure :: OnInsecure
83− -- ^ Do we allow insecure connections with this server as well?
68+ -- ^ Do we allow insecure connections with this server as well? Default
84− --
69+ -- is a simple text response stating that a secure connection is required.
85− -- >>> onInsecure defaultTlsSettings
86− -- DenyInsecure "This server only accepts secure HTTPS connections."
88 -- Since 1.4.0
71 -- Since 1.4.0
89 , tlsLogging :: TLS.Logging
72 , tlsLogging :: TLS.Logging
@@ -93,44 +76,23 @@ data TLSSettings = TLSSettings {
94 -- Since 1.4.0
77 -- Since 1.4.0
95 , tlsAllowedVersions :: [TLS.Version]
78 , tlsAllowedVersions :: [TLS.Version]
96−#if MIN_VERSION_tls(1,5,0)
97− -- ^ The TLS versions this server accepts.
99− -- >>> tlsAllowedVersions defaultTlsSettings
100− -- [TLS13,TLS12,TLS11,TLS10]
104 -- ^ The TLS versions this server accepts.
79 -- ^ The TLS versions this server accepts.
106− -- >>> tlsAllowedVersions defaultTlsSettings
81+ -- Default: '[TLS.TLS10,TLS.TLS11,TLS.TLS12]'.
107− -- [TLS12,TLS11,TLS10]
109 -- Since 1.4.2
83 -- Since 1.4.2
111 , tlsCiphers :: [TLS.Cipher]
84 , tlsCiphers :: [TLS.Cipher]
112−#if MIN_VERSION_tls(1,5,0)
113− -- ^ The TLS ciphers this server accepts.
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]
120 -- ^ The TLS ciphers this server accepts.
85 -- ^ The TLS ciphers this server accepts.
122− -- >>> tlsCiphers defaultTlsSettings
87+ -- Default: '[TLSExtra.cipher_AES128_SHA1, TLSExtra.cipher_AES256_SHA1, TLSEtra.cipher_RC4_128_MD5, TLSExtra.cipher_RC4_128_SHA1]'
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]
125 -- Since 1.4.2
89 -- Since 1.4.2
127 , tlsWantClientCert :: Bool
90 , tlsWantClientCert :: Bool
128 -- ^ Whether or not to demand a certificate from the client. If this
91 -- ^ Whether or not to demand a certificate from the client. If this
129 -- is set to True, you must handle received certificates in a server hook
92 -- is set to True, you must handle received certificates in a server hook
130 -- or all connections will fail.
93 -- or all connections will fail.
132− -- >>> tlsWantClientCert defaultTlsSettings
133− -- False
95+ -- Default: False
135 -- Since 3.0.2
97 -- Since 3.0.2
136 , tlsServerHooks :: TLS.ServerHooks
98 , tlsServerHooks :: TLS.ServerHooks
@@ -141,25 +103,9 @@ data TLSSettings = TLSSettings {
141 -- Default: def
103 -- Default: def
143 -- Since 3.0.2
105 -- Since 3.0.2
144− , tlsServerDHEParams :: Maybe DH.Params
145− -- ^ Configuration for ServerDHEParams
146− -- more function lives in `cryptonite` package
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'.
162−-- | Default 'TLSSettings'. Use this to create 'TLSSettings' with the field record name (aka accessors).
108+-- | Default 'TLSSettings'. Use this to create 'TLSSettings' with the field record name.
163defaultTlsSettings :: TLSSettings
109defaultTlsSettings :: TLSSettings
164defaultTlsSettings = TLSSettings {
110defaultTlsSettings = TLSSettings {
165 certFile = "certificate.pem"
111 certFile = "certificate.pem"
@@ -170,32 +116,39 @@ defaultTlsSettings = TLSSettings {
170 , keyMemory = Nothing
116 , keyMemory = Nothing
171 , onInsecure = DenyInsecure "This server only accepts secure HTTPS connections."
117 , onInsecure = DenyInsecure "This server only accepts secure HTTPS connections."
172 , tlsLogging = def
118 , tlsLogging = def
173−#if MIN_VERSION_tls(1,5,0)
174− , tlsAllowedVersions = [TLS.TLS13,TLS.TLS12,TLS.TLS11,TLS.TLS10]
119+ , tlsAllowedVersions = [TLS.TLS10,TLS.TLS11,TLS.TLS12]
176− , tlsAllowedVersions = [TLS.TLS12,TLS.TLS11,TLS.TLS10]
178 , tlsCiphers = ciphers
120 , tlsCiphers = ciphers
179 , tlsWantClientCert = False
121 , tlsWantClientCert = False
180 , tlsServerHooks = def
122 , tlsServerHooks = def
181− , tlsServerDHEParams = Nothing
182− , tlsSessionManagerConfig = Nothing
185-- taken from stunnel example in tls-extra
125-- taken from stunnel example in tls-extra
186ciphers :: [TLS.Cipher]
126ciphers :: [TLS.Cipher]
187−ciphers = TLSExtra.ciphersuite_strong
127+ciphers =
128+ [ TLSExtra.cipher_ECDHE_RSA_AES128GCM_SHA256
129+ , TLSExtra.cipher_DHE_RSA_AES128GCM_SHA256
130+ , TLSExtra.cipher_DHE_RSA_AES256_SHA256
131+ , TLSExtra.cipher_DHE_RSA_AES128_SHA256
132+ , TLSExtra.cipher_DHE_RSA_AES256_SHA1
133+ , TLSExtra.cipher_DHE_RSA_AES128_SHA1
134+ , TLSExtra.cipher_DHE_DSS_AES128_SHA1
135+ , TLSExtra.cipher_DHE_DSS_AES256_SHA1
136+ , TLSExtra.cipher_DHE_DSS_RC4_SHA1
137+ , TLSExtra.cipher_AES128_SHA1
138+ , TLSExtra.cipher_AES256_SHA1
139+ , TLSExtra.cipher_RC4_128_MD5
140+ , TLSExtra.cipher_RC4_128_SHA1
189----------------------------------------------------------------
143----------------------------------------------------------------
191-- | An action when a plain HTTP comes to HTTP over TLS/SSL port.
145-- | An action when a plain HTTP comes to HTTP over TLS/SSL port.
192data OnInsecure = DenyInsecure L.ByteString
146data OnInsecure = DenyInsecure L.ByteString
193 | AllowInsecure
147 | AllowInsecure
196----------------------------------------------------------------
149----------------------------------------------------------------
198−-- | A smart constructor for 'TLSSettings' based on 'defaultTlsSettings'.
151+-- | A smart constructor for 'TLSSettings'.
199tlsSettings :: FilePath -- ^ Certificate file
152tlsSettings :: FilePath -- ^ Certificate file
200 -> FilePath -- ^ Key file
153 -> FilePath -- ^ Key file
201 -> TLSSettings
154 -> TLSSettings
@@ -205,7 +158,7 @@ tlsSettings cert key = defaultTlsSettings {
207-- | A smart constructor for 'TLSSettings' that allows specifying
160-- | A smart constructor for 'TLSSettings' that allows specifying
208−-- chain certificates based on 'defaultTlsSettings'.
161+-- chain certificates.
210-- Since 3.0.3
163-- Since 3.0.3
211tlsSettingsChain
164tlsSettingsChain
@@ -220,7 +173,7 @@ tlsSettingsChain cert chainCerts key = defaultTlsSettings {
222-- | A smart constructor for 'TLSSettings', but uses in-memory representations
175-- | A smart constructor for 'TLSSettings', but uses in-memory representations
223−-- of the certificate and key based on 'defaultTlsSettings'.
176+-- of the certificate and key
225-- Since 3.0.1
178-- Since 3.0.1
226tlsSettingsMemory
179tlsSettingsMemory
@@ -233,7 +186,7 @@ tlsSettingsMemory cert key = defaultTlsSettings
235-- | A smart constructor for 'TLSSettings', but uses in-memory representations
188-- | A smart constructor for 'TLSSettings', but uses in-memory representations
236−-- of the certificate and key based on 'defaultTlsSettings'.
189+-- of the certificate and key
238-- Since 3.0.3
191-- Since 3.0.3
239tlsSettingsChainMemory
192tlsSettingsChainMemory
@@ -249,12 +202,13 @@ tlsSettingsChainMemory cert chainCerts key = defaultTlsSettings
250----------------------------------------------------------------
203----------------------------------------------------------------
252-- | Running 'Application' with 'TLSSettings' and 'Settings'.
206-- | Running 'Application' with 'TLSSettings' and 'Settings'.
253runTLS :: TLSSettings -> Settings -> Application -> IO ()
207runTLS :: TLSSettings -> Settings -> Application -> IO ()
254runTLS tset set app = withSocketsDo $
208runTLS tset set app = withSocketsDo $
256 (bindPortTCP (getPort set) (getHost set))
210 (bindPortTCP (getPort set) (getHost set))
258 (\sock -> runTLSSocket tset set sock app)
212 (\sock -> runTLSSocket tset set sock app)
260----------------------------------------------------------------
214----------------------------------------------------------------
@@ -272,186 +226,89 @@ runTLSSocket tlsset@TLSSettings{..} set sock app = do
272 key <- maybe (S.readFile keyFile) return mkey
226 key <- maybe (S.readFile keyFile) return mkey
273 either error return $
227 either error return $
274 TLS.credentialLoadX509ChainFromMemory cert chainCertsMemory key
228 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
229+ runTLSSocket' tlsset set credential sock app
280−runTLSSocket' :: TLSSettings -> Settings -> TLS.Credential -> TLS.SessionManager -> Socket -> Application -> IO ()
231+runTLSSocket' :: TLSSettings -> Settings -> TLS.Credential -> Socket -> Application -> IO ()
281−runTLSSocket' tlsset@TLSSettings{..} set credential mgr sock app =
232+runTLSSocket' tlsset@TLSSettings{..} set credential sock app =
282 runSettingsConnectionMakerSecure set get app
233 runSettingsConnectionMakerSecure set get app
284− get = getter tlsset set sock params
235+ get = getter tlsset sock params
285− params = def { -- TLS.ServerParams
236+ params = def {
286 TLS.serverWantClientCert = tlsWantClientCert
237 TLS.serverWantClientCert = tlsWantClientCert
287− , TLS.serverCACertificates = []
238+ , TLS.serverSupported = def {
288− , TLS.serverDHEParams = tlsServerDHEParams
239+ TLS.supportedVersions = tlsAllowedVersions
240+ , TLS.supportedCiphers = tlsCiphers
242+ , TLS.serverShared = def {
243+ TLS.sharedCredentials = TLS.Credentials [credential]
289− , TLS.serverHooks = hooks
245+ , TLS.serverHooks = tlsServerHooks
290− , TLS.serverShared = shared
291− , TLS.serverSupported = supported
292−#if MIN_VERSION_tls(1,5,0)
293− , TLS.serverEarlyDataSize = 2018
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)
302− TLS.sharedCredentials = TLS.Credentials [credential]
303− , TLS.sharedSessionManager = mgr
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]
318−alpn :: [S.ByteString] -> IO S.ByteString
320− | "h2" `elem` xs = return "h2"
321− | otherwise = return "http/1.1"
323----------------------------------------------------------------
248----------------------------------------------------------------
325−getter :: TLS.TLSParams params => TLSSettings -> Settings -> Socket -> params -> IO (IO (Connection, Transport), SockAddr)
250+getter :: TLS.TLSParams params => TLSSettings -> Socket -> params -> IO (IO (Connection, Transport), SockAddr)
326−getter tlsset@TLSSettings{..} set sock params = do
251+getter tlsset@TLSSettings{..} sock params = do
328− (s, sa) <- windowsThreadBlockHack $ accept sock
330 (s, sa) <- accept sock
252 (s, sa) <- accept sock
332− setSocketCloseOnExec s
333− return (mkConn tlsset set s params, sa)
253+ return (mkConn tlsset s params, sa)
335−mkConn :: TLS.TLSParams params => TLSSettings -> Settings -> Socket -> params -> IO (Connection, Transport)
255+mkConn :: TLS.TLSParams params => TLSSettings -> Socket -> params -> IO (Connection, Transport)
336−mkConn tlsset set s params = switch `onException` close s
256+mkConn tlsset s params = do
339− firstBS <- safeRecv s 4096
257+ firstBS <- safeRecv s 4096
258+ cachedRef <- I.newIORef firstBS
340− if not (S.null firstBS) && S.head firstBS == 0x16 then
259+ (if not (B.null firstBS) && B.head firstBS == 0x16 then
341− httpOverTls tlsset set s firstBS params
260+ httpOverTls tlsset s cachedRef params
343− plainHTTP tlsset set s firstBS
262+ plainHTTP tlsset s cachedRef) `onException` sClose s
345----------------------------------------------------------------
264----------------------------------------------------------------
347−httpOverTls :: TLS.TLSParams params => TLSSettings -> Settings -> Socket -> S.ByteString -> params -> IO (Connection, Transport)
266+httpOverTls :: TLS.TLSParams params => TLSSettings -> Socket -> I.IORef B.ByteString -> params -> IO (Connection, Transport)
348−httpOverTls TLSSettings{..} _set s bs0 params = do
267+httpOverTls TLSSettings{..} s cachedRef params = do
349− recvN <- makePlainReceiveN s bs0
268+ gen <- Crypto.Random.AESCtr.makeSystem
350− ctx <- TLS.contextNew (backend recvN) params
269+ ctx <- TLS.contextNew backend params gen
351 TLS.contextHookSetLogging ctx tlsLogging
270 TLS.contextHookSetLogging ctx tlsLogging
352 TLS.handshake ctx
271 TLS.handshake ctx
353− h2 <- (== Just "h2") <$> TLS.getNegotiatedProtocol ctx
354− isH2 <- I.newIORef h2
355 writeBuf <- allocateBuffer bufferSize
272 writeBuf <- allocateBuffer bufferSize
356− -- Creating a cache for leftover input data.
357− ref <- I.newIORef ""
358 tls <- getTLSinfo ctx
273 tls <- getTLSinfo ctx
359− return (conn ctx writeBuf ref isH2, tls)
274+ return (conn ctx writeBuf, tls)
361− backend recvN = TLS.Backend {
276+ backend = TLS.Backend {
362 TLS.backendFlush = return ()
277 TLS.backendFlush = return ()
363−#if MIN_VERSION_network(3,1,1)
278+ , TLS.backendClose = sClose s
364− , TLS.backendClose = gracefulClose s 5000 `E.catch` \(SomeException _) -> return ()
366− , TLS.backendClose = close s
368− , TLS.backendSend = sendAll' s
279+ , TLS.backendSend = sendAll s
369− , TLS.backendRecv = recvN
280+ , TLS.backendRecv = recvTLS cachedRef s
371− sendAll' sock bs = sendAll sock bs `E.catch` \(SomeException _) ->
372− throwIO ConnectionClosedByPeer
373− conn ctx writeBuf ref isH2 = Connection {
282+ conn ctx writeBuf = Connection {
374 connSendMany = TLS.sendData ctx . L.fromChunks
283 connSendMany = TLS.sendData ctx . L.fromChunks
375 , connSendAll = sendall
284 , connSendAll = sendall
376 , connSendFile = sendfile
285 , connSendFile = sendfile
377− , connClose = close'
286+ , connClose = close
378− , connFree = freeBuffer writeBuf
379− , connRecv = recv ref
287+ , connRecv = recv
380− , connRecvBuf = recvBuf ref
381 , connWriteBuffer = writeBuf
288 , connWriteBuffer = writeBuf
382 , connBufferSize = bufferSize
289 , connBufferSize = bufferSize
386 sendall = TLS.sendData ctx . L.fromChunks . return
292 sendall = TLS.sendData ctx . L.fromChunks . return
387 sendfile fid offset len hook headers =
293 sendfile fid offset len hook headers =
388 readSendFile writeBuf bufferSize sendall fid offset len hook headers
294 readSendFile writeBuf bufferSize sendall fid offset len hook headers
390− close' = void (tryIO sendBye) `finally`
296+ close = freeBuffer writeBuf `finally`
391− TLS.contextClose ctx
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.
397− (\e -> guard (e == ConnectionClosedByPeer) >> return e)
399− (TLS.bye ctx)
297+ void (tryIO $ TLS.bye ctx) `finally`
400−
298+ TLS.contextClose ctx
401− -- TLS version of recv with a cache for leftover input data.
299+
402− -- The cache is shared with recvBuf.
404− cached <- I.readIORef cref
405− if cached /= "" then do
406− I.writeIORef cref ""
411− -- TLS version of recv (decrypting) without a cache.
412− recv' = handle onEOF go
300+ recv = handle onEOF go
415− | Just TLS.Error_EOF <- fromException e = return S.empty
303+ | Just TLS.Error_EOF <- fromException e = return B.empty
416− | Just ioe <- fromException e, isEOFError ioe = return S.empty | otherwise = throwIO e
304+ | Just ioe <- fromException e, isEOFError ioe = return B.empty | otherwise = throwIO e
418 x <- TLS.recvData ctx
306 x <- TLS.recvData ctx
419− if S.null x then
307+ if B.null x then
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
431−fill :: S.ByteString -> Buffer -> BufSize -> Recv -> IO (Bool,S.ByteString)
432−fill bs0 buf0 siz0 recv
434− let (bs, leftover) = S.splitAt siz0 bs0
436− return (True, leftover)
438− buf <- copy buf0 bs0
439− loop buf (siz0 - len0)
442− loop _ 0 = return (True, "")
445− let len = S.length bs
446− if len == 0 then return (False, "")
447− else if (len <= siz) then do
449− loop buf' (siz - len)
451− let (bs1,bs2) = S.splitAt siz bs
455getTLSinfo :: TLS.Context -> IO Transport
312getTLSinfo :: TLS.Context -> IO Transport
456getTLSinfo ctx = do
313getTLSinfo ctx = do
457 proto <- TLS.getNegotiatedProtocol ctx
314 proto <- TLS.getNegotiatedProtocol ctx
@@ -465,16 +322,11 @@ getTLSinfo ctx = do
465 TLS.TLS10 -> (3,1)
322 TLS.TLS10 -> (3,1)
466 TLS.TLS11 -> (3,2)
323 TLS.TLS11 -> (3,2)
467 TLS.TLS12 -> (3,3)
324 TLS.TLS12 -> (3,3)
468−#if MIN_VERSION_tls(1,5,0)
471− clientCert <- TLS.getClientCertificateChain ctx
472 return TLS {
325 return TLS {
473 tlsMajorVersion = major
326 tlsMajorVersion = major
474 , tlsMinorVersion = minor
327 , tlsMinorVersion = minor
475 , tlsNegotiatedProtocol = proto
328 , tlsNegotiatedProtocol = proto
476 , tlsChiperID = TLS.cipherID infoCipher
329 , tlsChiperID = TLS.cipherID infoCipher
477− , tlsClientCertificate = clientCert
480tryIO :: IO a -> IO (Either IOException a)
332tryIO :: IO a -> IO (Either IOException a)
@@ -482,46 +334,52 @@ tryIO = try
483----------------------------------------------------------------
335----------------------------------------------------------------
485−plainHTTP :: TLSSettings -> Settings -> Socket -> S.ByteString -> IO (Connection, Transport)
337+plainHTTP :: TLSSettings -> Socket -> I.IORef B.ByteString -> IO (Connection, Transport)
486−plainHTTP TLSSettings{..} set s bs0 = case onInsecure of
338+plainHTTP TLSSettings{..} s cachedRef = case onInsecure of
487 AllowInsecure -> do
339 AllowInsecure -> do
488− conn' <- socketConnection set s
340+ conn' <- socketConnection s
489− cachedRef <- I.newIORef bs0
490 let conn'' = conn'
341 let conn'' = conn'
491 { connRecv = recvPlain cachedRef (connRecv conn')
342 { connRecv = recvPlain cachedRef (connRecv conn')
493 return (conn'', TCP)
344 return (conn'', TCP)
494 DenyInsecure lbs -> do
345 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.
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"
346+ sendAll s "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n"
509 mapM_ (sendAll s) $ L.toChunks lbs
347 mapM_ (sendAll s) $ L.toChunks lbs
510− close s
348+ sClose s
511 throwIO InsecureConnectionDenied
349 throwIO InsecureConnectionDenied
513----------------------------------------------------------------
351----------------------------------------------------------------
353+recvTLS :: I.IORef B.ByteString -> Socket -> Int -> IO B.ByteString
354+recvTLS cachedRef s size = do
355+ cached <- I.readIORef cachedRef
358+ loop bs | B.length bs >= size = do
359+ let (x, y) = B.splitAt size bs
360+ I.writeIORef cachedRef y
363+ bs2 <- safeRecv s 4096
364+ if B.null bs2 then do
365+ -- FIXME does this deserve an exception being thrown?
366+ I.writeIORef cachedRef B.empty
369+ loop $ B.append bs1 bs2
371+----------------------------------------------------------------
515-- | Modify the given receive function to first check the given @IORef@ for a
373-- | Modify the given receive function to first check the given @IORef@ for a
516-- chunk of data. If present, takes the chunk of data from the @IORef@ and
374-- chunk of data. If present, takes the chunk of data from the @IORef@ and
517-- empties out the @IORef@. Otherwise, calls the supplied receive function.
375-- empties out the @IORef@. Otherwise, calls the supplied receive function.
518−recvPlain :: I.IORef S.ByteString -> IO S.ByteString -> IO S.ByteString
376+recvPlain :: I.IORef B.ByteString -> IO B.ByteString -> IO B.ByteString
519recvPlain ref fallback = do
377recvPlain ref fallback = do
520 bs <- I.readIORef ref
378 bs <- I.readIORef ref
521− if S.null bs
379+ if B.null bs
522 then fallback
380 then fallback
524− I.writeIORef ref S.empty
382+ I.writeIORef ref B.empty
525 return bs
383 return bs
527----------------------------------------------------------------
385----------------------------------------------------------------