on 562a22e help sign in
57 files changed+412−755
since splitsnapshots
unifiedsplit
.azure/azure-linux-template.yml+22−8 .azure/azure-osx-template.yml+12−2 .azure/azure-windows-template.yml+3−4 .gitignore−1 auto-update/Control/Debounce.hs+15−4 auto-update/Control/Debounce/Internal.hs+17−28 stack-lts-11.yaml+6−3 stack-lts-12.yaml+6−3 stack-lts-6.yaml+35 stack-lts-9.yaml+31 stack-nightly.yaml−20 stack.yaml+7−3 wai-app-static/ChangeLog.md−10 wai-app-static/Network/Wai/Application/Static.hs+8−10 wai-app-static/WaiAppStatic/CmdLine.hs−1 wai-app-static/WaiAppStatic/Listing.hs+1−1 wai-app-static/WaiAppStatic/Storage/Embedded/TH.hs+1 wai-app-static/WaiAppStatic/Types.hs−2 wai-app-static/test/WaiAppStaticTest.hs+9−9 wai-app-static/wai-app-static.cabal+1−1 wai-conduit/Network/Wai/Conduit.hs+6−5 wai-extra/ChangeLog.md−16 wai-extra/Network/Wai/EventSource.hs+1−19 wai-extra/Network/Wai/EventSource/EventStream.hs+1−1 wai-extra/Network/Wai/Middleware/Gzip.hs−3 wai-extra/Network/Wai/Middleware/RequestLogger.hs+6−56 wai-extra/Network/Wai/Middleware/RequestLogger/JSON.hs+1−34 wai-extra/Network/Wai/Middleware/Rewrite.hs+1−1 wai-extra/Network/Wai/Test.hs+14−22 wai-extra/example/Main.hs−11 wai-extra/test/Network/Wai/TestSpec.hs+11−41 wai-extra/wai-extra.cabal+3−3 wai-http2-extra/wai-http2-extra.cabal+2−9 wai-websockets/server.lhs+1−7 wai-websockets/wai-websockets.cabal−1 warp-tls/ChangeLog.md−22 warp-tls/Network/Wai/Handler/WarpTLS.hs+15−27 warp-tls/warp-tls.cabal+5−5 warp/ChangeLog.md+1−50 warp/Network/Wai/Handler/Warp.hs+1−51 warp/Network/Wai/Handler/Warp/File.hs+4−7 warp/Network/Wai/Handler/Warp/HTTP2.hs+16−25 warp/Network/Wai/Handler/Warp/HTTP2/File.hs+2−6 warp/Network/Wai/Handler/Warp/HTTP2/Request.hs+6−7 warp/Network/Wai/Handler/Warp/HTTP2/Response.hs+1−2 warp/Network/Wai/Handler/Warp/Header.hs−2 warp/Network/Wai/Handler/Warp/Recv.hs+4−25 warp/Network/Wai/Handler/Warp/Request.hs+19−21 warp/Network/Wai/Handler/Warp/Response.hs+16−19 warp/Network/Wai/Handler/Warp/Run.hs+25−41 warp/Network/Wai/Handler/Warp/Settings.hs+5−31 warp/Network/Wai/Handler/Warp/Types.hs−10 warp/test/FileSpec.hs+1−1 warp/test/RequestSpec.hs+7−11 warp/test/ResponseSpec.hs+15−11 warp/test/RunSpec.hs+47−38 warp/warp.cabal+1−4
.azure/azure-linux-template.yml+22−8
@@ -5,6 +5,16 @@ jobs:
55 vmImage: ${{ parameters.vmImage }}
66 strategy:
77 matrix:
8+ stack-def:
9+ BUILD: stack
10+ stack-lts-6:
11+ BUILD: stack
12+ STACK_YAML: stack-lts-6.yaml
13+ ARGS: "--resolver lts-6"
14+ stack-lts-9:
15+ BUILD: stack
16+ STACK_YAML: stack-lts-9.yaml
17+ ARGS: "--resolver lts-9"
818 stack-lts-11:
919 BUILD: stack
1020 STACK_YAML: stack-lts-11.yaml
@@ -13,13 +23,17 @@ jobs:
1323 BUILD: stack
1424 STACK_YAML: stack-lts-12.yaml
1525 ARGS: "--resolver lts-12"
16− stack-lts-14:
26+ stack-lts-13:
1727 BUILD: stack
18− ARGS: "--resolver lts-14"
19− stack-nightly:
20− BUILD: stack
21− STACK_YAML: stack-nightly.yaml
22− ARGS: "--resolver nightly"
28+ ARGS: "--resolver lts-13"
29+ cabal-7.10.3:
30+ BUILD: cabal
31+ GHCVER: 7.10.3
32+ CABALVER: 1.22
33+ cabal-8.0.2:
34+ BUILD: cabal
35+ GHCVER: 8.0.2
36+ CABALVER: 1.24
2337 cabal-8.2.2:
2438 BUILD: cabal
2539 GHCVER: 8.4.4
@@ -28,9 +42,9 @@ jobs:
2842 BUILD: cabal
2943 GHCVER: 8.4.4
3044 CABALVER: 2.2
31− cabal-8.6.5:
45+ cabal-8.6.4:
3246 BUILD: cabal
33− GHCVER: 8.6.5
47+ GHCVER: 8.6.4
3448 CABALVER: 2.4
3549 maxParallel: 6
3650 steps:
.azure/azure-osx-template.yml+12−2
@@ -5,6 +5,16 @@ jobs:
55 vmImage: ${{ parameters.vmImage }}
66 strategy:
77 matrix:
8+ stack-def:
9+ BUILD: stack
10+ stack-lts-6:
11+ BUILD: stack
12+ STACK_YAML: stack-lts-6.yaml
13+ ARGS: "--resolver lts-6"
14+ stack-lts-9:
15+ BUILD: stack
16+ STACK_YAML: stack-lts-9.yaml
17+ ARGS: "--resolver lts-9"
818 stack-lts-11:
919 BUILD: stack
1020 STACK_YAML: stack-lts-11.yaml
@@ -13,9 +23,9 @@ jobs:
1323 BUILD: stack
1424 STACK_YAML: stack-lts-12.yaml
1525 ARGS: "--resolver lts-12"
16− stack-lts-14:
26+ stack-lts-13:
1727 BUILD: stack
18− ARGS: "--resolver lts-14"
28+ ARGS: "--resolver lts-13"
1929 maxParallel: 3
2030 steps:
2131 - script: |
.azure/azure-windows-template.yml+3−4
@@ -5,9 +5,9 @@ jobs:
55 vmImage: ${{ parameters.vmImage }}
66 strategy:
77 matrix:
8− stack-lts-14:
8+ stack-lts-13:
99 BUILD: stack
10− ARGS: "--resolver lts-14"
10+ ARGS: "--resolver lts-13"
1111 maxParallel: 3
1212 steps:
1313 - bash: |
@@ -16,8 +16,7 @@ jobs:
1616 unzip -o /usr/bin/stack.zip -d /usr/bin/
1717 stack setup $ARGS > /dev/null
1818 stack --install-ghc $ARGS test --bench --only-dependencies
19− stack $ARGS test --bench --no-run-benchmarks --no-run-tests
20− stack $ARGS test --bench --no-run-benchmarks --jobs 1 # non-parallel test execution
19+ stack $ARGS test --bench --no-run-benchmarks
2120 env:
2221 OS_NAME: ${{ parameters.os }}
2322 displayName: 'Installation ${{parameters.os}} & Test'
.gitignore−1
@@ -9,4 +9,3 @@ tarballs/
99cabal.sandbox.config
1010.stack-work/
1111*~
12−stack*.yaml.lock
auto-update/Control/Debounce.hs+15−4
@@ -11,7 +11,7 @@
1111-- printString <- 'mkDebounce' 'defaultDebounceSettings'
1212-- { 'debounceAction' = putStrLn "Running action"
1313-- , 'debounceFreq' = 5000000 -- 5 seconds
14−-- , 'debounceEdge' = 'DI.trailingEdge' -- Trigger on the trailing edge
14+-- , 'debounceEdge' = 'trailingEdge' -- Trigger on the trailing edge
1515-- }
1616-- @
1717--
@@ -32,8 +32,8 @@ module Control.Debounce
3232 , DI.debounceFreq
3333 , DI.debounceAction
3434 , DI.debounceEdge
35− , DI.leadingEdge
36− , DI.trailingEdge
35+ , leadingEdge
36+ , trailingEdge
3737 -- * Creation
3838 , mkDebounce
3939 ) where
@@ -48,7 +48,7 @@ defaultDebounceSettings :: DI.DebounceSettings
4848defaultDebounceSettings = DI.DebounceSettings
4949 { DI.debounceFreq = 1000000
5050 , DI.debounceAction = return ()
51− , DI.debounceEdge = DI.leadingEdge
51+ , DI.debounceEdge = leadingEdge
5252 }
5353
5454-- | Generate an action which will trigger the debounced action to be performed.
@@ -58,3 +58,14 @@ mkDebounce :: DI.DebounceSettings -> IO (IO ())
5858mkDebounce settings = do
5959 baton <- newEmptyMVar
6060 DI.mkDebounceInternal baton threadDelay settings
61+
62+-- | Perform the action immediately, and then begin a cooldown period.
63+-- If the trigger happens again during the cooldown, wait until the end of the cooldown
64+-- and then perform the action again, then enter a new cooldown period.
65+leadingEdge :: DI.DebounceEdge
66+leadingEdge = DI.Leading
67+
68+-- | Start a cooldown period and perform the action when the period ends. If another trigger
69+-- happens during the cooldown, it has no effect.
70+trailingEdge :: DI.DebounceEdge
71+trailingEdge = DI.Trailing
auto-update/Control/Debounce/Internal.hs+17−28
@@ -4,15 +4,14 @@
44module Control.Debounce.Internal (
55 DebounceSettings(..)
66 , DebounceEdge(..)
7− , leadingEdge
8− , trailingEdge
97 , mkDebounceInternal
108 ) where
119
1210import Control.Concurrent (forkIO)
1311import Control.Concurrent.MVar (takeMVar, tryPutMVar, tryTakeMVar, MVar)
1412import Control.Exception (SomeException, handle, mask_)
15−import Control.Monad (forever, void)
13+import Control.Monad (forever, void, when)
14+import Data.Maybe (isJust)
1615
1716-- | Settings to control how debouncing should work.
1817--
@@ -43,7 +42,7 @@ data DebounceSettings = DebounceSettings
4342 -- ^ Whether to perform the action on the leading edge or trailing edge of
4443 -- the timeout.
4544 --
46− -- Default: 'trailingEdge'.
45+ -- Default: trailingEdge.
4746 --
4847 -- @since 0.1.6
4948 }
@@ -62,37 +61,27 @@ data DebounceEdge =
6261 -- happens during the cooldown, it has no effect.
6362 deriving (Show, Eq)
6463
65−
66−-- | Perform the action immediately, and then begin a cooldown period.
67−-- If the trigger happens again during the cooldown, wait until the end of the cooldown
68−-- and then perform the action again, then enter a new cooldown period.
69−--
70−-- @since 0.1.6
71−leadingEdge :: DebounceEdge
72−leadingEdge = Leading
73−
74−-- | Start a cooldown period and perform the action when the period ends. If another trigger
75−-- happens during the cooldown, it has no effect.
76−--
77−-- @since 0.1.6
78−trailingEdge :: DebounceEdge
79−trailingEdge = Trailing
80−
8164mkDebounceInternal :: MVar () -> (Int -> IO ()) -> DebounceSettings -> IO (IO ())
8265mkDebounceInternal baton delayFn (DebounceSettings freq action edge) = do
8366 mask_ $ void $ forkIO $ forever $ do
8467 takeMVar baton
8568 case edge of
86− Leading -> do
87− ignoreExc action
88− delayFn freq
89− Trailing -> do
90− delayFn freq
91− -- Empty the baton of any other activations during the interval
92− void $ tryTakeMVar baton
93− ignoreExc action
69+ Leading -> ignoreExc action >> runDelay
70+ Trailing -> runDelay
9471
9572 return $ void $ tryPutMVar baton ()
9673
74+ where runDelay = do
75+ delayFn freq
76+
77+ case edge of
78+ Trailing -> do
79+ -- Empty the baton of any other activations during the interval
80+ void $ tryTakeMVar baton
81+ ignoreExc action
82+ Leading ->
83+ -- We already fired at the beginning of the interval so do nothing
84+ return ()
85+
9786ignoreExc :: IO () -> IO ()
9887ignoreExc = handle $ \(_ :: SomeException) -> return ()
stack-lts-11.yaml+6−3
@@ -17,11 +17,14 @@ extra-deps:
1717- streaming-commons-0.2.1.0@sha256:163af02944f374f316e35257c1afbffefd2c2852a85a151d1ea0ad22152cf28d,4990
1818- http2-2.0.0
1919- network-byte-order-0.1.1.0
20−- tls-1.5.3
21−- tls-session-manager-0.0.4
22−- x509-1.7.5
2320nix:
2421 enable: false
2522 packages:
2623 - fcgi
2724 - zlib
25+image:
26+ containers:
27+ - base: "fpco/pid1"
28+ name: "yesodweb/warp"
29+ executables:
30+ - warp
stack-lts-12.yaml+6−3
@@ -18,9 +18,12 @@ nix:
1818 packages:
1919 - fcgi
2020 - zlib
21+image:
22+ containers:
23+ - base: "fpco/pid1"
24+ name: "yesodweb/warp"
25+ executables:
26+ - warp
2127extra-deps:
2228- http2-2.0.0
2329- network-byte-order-0.1.1.0
24−- tls-1.5.3
25−- tls-session-manager-0.0.4
26−- x509-1.7.5
stack-lts-6.yamlnew file+35
@@ -0,0 +1,35 @@
1+resolver: lts-6.35
2+packages:
3+- ./auto-update
4+- ./mime-types
5+- ./wai
6+- ./wai-extra
7+- ./warp
8+- ./warp-tls
9+- ./wai-app-static
10+- ./wai-websockets
11+- ./wai-conduit
12+- ./time-manager
13+extra-deps:
14+- bsb-http-chunked-0.0.0.2@sha256:0571ef33ec5530104503d32eaf24f2d7c221f350e5fc475a6dd8e84311bc0ca1,1792
15+- streaming-commons-0.2.1.0@sha256:163af02944f374f316e35257c1afbffefd2c2852a85a151d1ea0ad22152cf28d,4990
16+- tls-session-manager-0.0.0.2@sha256:e3d3f8a026196df46746a19d3d7d31211b2fca3aefed9074e951b2dcc1b8833c,872
17+- wai-logger-2.3.2@sha256:7f5dbc72c0753bd3e0e73f8f542d9d159d1d70f8ff3a3ec16d0b74b500534fc6,1572
18+- gauge-0.2.3@sha256:ce41cb66ebf730a5050181db0338b1a4b3f76ac71331bf5070089bcd4352ff46,3777
19+- psqueues-0.2.7.0@sha256:189970837d0aae01f48cd2a8aac6ce314191939e118cc0488aee1fb12da1956a,4757
20+- basement-0.0.7@rev:0
21+- http-client-0.5.13.1@rev:0
22+- http-types-0.12.3@rev:0
23+- http2-2.0.0
24+- network-byte-order-0.1.1.0
25+nix:
26+ enable: false
27+ packages:
28+ - fcgi
29+ - zlib
30+image:
31+ containers:
32+ - base: "fpco/pid1"
33+ name: "yesodweb/warp"
34+ executables:
35+ - warp
stack-lts-9.yamlnew file+31
@@ -0,0 +1,31 @@
1+resolver: lts-9.21
2+packages:
3+- ./auto-update
4+- ./mime-types
5+- ./wai
6+- ./wai-extra
7+- ./warp
8+- ./warp-tls
9+- ./wai-app-static
10+- ./wai-websockets
11+- ./wai-conduit
12+- ./time-manager
13+extra-deps:
14+- bsb-http-chunked-0.0.0.2@sha256:0571ef33ec5530104503d32eaf24f2d7c221f350e5fc475a6dd8e84311bc0ca1,1792
15+- streaming-commons-0.2.1.0@sha256:163af02944f374f316e35257c1afbffefd2c2852a85a151d1ea0ad22152cf28d,4990
16+- wai-logger-2.3.2@sha256:7f5dbc72c0753bd3e0e73f8f542d9d159d1d70f8ff3a3ec16d0b74b500534fc6,1572
17+- http-client-0.5.13.1@rev:0
18+- http-types-0.12.3@rev:0
19+- http2-2.0.0
20+- network-byte-order-0.1.1.0
21+nix:
22+ enable: false
23+ packages:
24+ - fcgi
25+ - zlib
26+image:
27+ containers:
28+ - base: "fpco/pid1"
29+ name: "yesodweb/warp"
30+ executables:
31+ - warp
stack-nightly.yamldeleted−20
@@ -1,20 +0,0 @@
1−resolver: nightly-2020-01-08
2−packages:
3−- ./auto-update
4−- ./mime-types
5−- ./wai
6−- ./wai-extra
7−- ./warp
8−- ./warp-tls
9−- ./wai-app-static
10−- ./wai-websockets
11−- ./wai-conduit
12−- ./time-manager
13−flags:
14− wai-extra:
15− build-example: true
16−nix:
17− enable: false
18− packages:
19− - fcgi
20− - zlib
stack.yaml+7−3
@@ -1,4 +1,4 @@
1−resolver: lts-14.22
1+resolver: lts-13.14
22packages:
33- ./auto-update
44- ./mime-types
@@ -18,8 +18,12 @@ nix:
1818 packages:
1919 - fcgi
2020 - zlib
21+image:
22+ containers:
23+ - base: "fpco/pid1"
24+ name: "yesodweb/warp"
25+ executables:
26+ - warp
2127extra-deps:
2228- http2-2.0.0
2329- network-byte-order-0.1.1.0
24−- tls-1.5.3
25−- tls-session-manager-0.0.4
wai-app-static/ChangeLog.md−10
@@ -1,13 +1,3 @@
1−# wai-app-static changelog
2−
3−## 3.1.7.1
4−
5−* Update the test suite too
6−
7−## 3.1.7
8−
9−* Use 302 instead of 301 redirect, to avoid caching the presence of an index.html file
10−
111## 3.1.6.3
122
133* The executable warp obeys `-h` option properly for host
wai-app-static/Network/Wai/Application/Static.hs+8−10
@@ -164,13 +164,11 @@ serveFile StaticSettings {..} req file
164164 -- Didn't match, but we have a hash value. Send the file contents
165165 -- with an ETag header.
166166 --
167− -- RFC7232 (HTTP 1.1):
168− -- > A recipient MUST ignore If-Modified-Since if the request contains an
169− -- > If-None-Match header field; the condition in If-None-Match is
170− -- > considered to be a more accurate replacement for the condition in
171− -- > If-Modified-Since, and the two are only combined for the sake of
172− -- > interoperating with older intermediaries that might not implement
173− -- > If-None-Match.
167+ -- Note: It would be arguably better to next check
168+ -- if-modified-since and return a 304 if that indicates a match as
169+ -- well. However, the circumstances under which such a situation
170+ -- could arise would be very anomolous, and should likely warrant a
171+ -- new file being sent anyway.
174172 (Just hash, _) -> respond [("ETag", hash)]
175173
176174 -- No hash value available, fall back to last modified support.
@@ -245,7 +243,7 @@ staticAppPieces ss rawPieces req sendResponse = liftIO $ do
245243 response :: StaticResponse -> IO W.ResponseReceived
246244 response (FileResponse file ch) = do
247245 mimetype <- ssGetMimeType ss file
248− -- let filesize = fileGetSize file
246+ let filesize = fileGetSize file
249247 let headers = ("Content-Type", mimetype)
250248 -- Let Warp provide the content-length, since it takes
251249 -- range requests into account
@@ -269,13 +267,13 @@ staticAppPieces ss rawPieces req sendResponse = liftIO $ do
269267 Just hash -> replace "etag" (Just hash) (W.queryString req)
270268 Nothing -> remove "etag" (W.queryString req)
271269
272− sendResponse $ W.responseLBS H.status302
270+ sendResponse $ W.responseLBS H.status301
273271 [ ("Content-Type", "text/plain")
274272 , ("Location", S8.append loc $ H.renderQuery True qString)
275273 ] "Redirect"
276274
277275 response (RawRedirect path) =
278− sendResponse $ W.responseLBS H.status302
276+ sendResponse $ W.responseLBS H.status301
279277 [ ("Content-Type", "text/plain")
280278 , ("Location", path)
281279 ] "Redirect"
wai-app-static/WaiAppStatic/CmdLine.hs−1
@@ -39,7 +39,6 @@ data Args = Args
3939 }
4040
4141#if MIN_VERSION_optparse_applicative(0, 10, 0)
42−option' :: Mod OptionFields Int -> Parser Int
4342option' = option auto
4443#else
4544option' = option
wai-app-static/WaiAppStatic/Listing.hs+1−1
@@ -64,7 +64,7 @@ defaultListing pieces (Folder contents) = do
6464
6565 -- Add a link to the root of the tree
6666 showFolder' :: Bool -> Pieces -> H.Html
67− showFolder' hasTrailingSlash pieces' = showFolder hasTrailingSlash (unsafeToPiece "root" : pieces')
67+ showFolder' hasTrailingSlash pieces = showFolder hasTrailingSlash (unsafeToPiece "root" : pieces)
6868
6969 showFolder :: Bool -> Pieces -> H.Html
7070 showFolder _ [] = "/" -- won't happen
wai-app-static/WaiAppStatic/Storage/Embedded/TH.hs+1
@@ -7,6 +7,7 @@ module WaiAppStatic.Storage.Embedded.TH(
77
88import Data.ByteString.Builder.Extra (byteStringInsert)
99import Codec.Compression.GZip (compress)
10+import Control.Applicative
1011import Data.ByteString.Unsafe (unsafePackAddressLen)
1112import Data.Either (lefts, rights)
1213import GHC.Exts (Int(..))
wai-app-static/WaiAppStatic/Types.hs−2
@@ -59,8 +59,6 @@ toPieces :: [Text] -> Maybe Pieces
5959toPieces = mapM toPiece
6060
6161-- | Request coming from a user. Corresponds to @pathInfo@.
62−--
63−-- The root path is the empty list.
6462type Pieces = [Piece]
6563
6664-- | Values for the max-age component of the cache-control response header.
wai-app-static/test/WaiAppStaticTest.hs+9−9
@@ -57,22 +57,22 @@ spec = do
5757 assertStatus 200 =<<
5858 request (setRawPathInfo defRequest path)
5959
60− it "404 for non-existent files" $ webApp $
60+ it "404 for non-existant files" $ webApp $
6161 assertStatus 404 =<<
6262 request (setRawPathInfo defRequest "doesNotExist")
6363
64− it "302 redirect when multiple slashes" $ webApp $ do
64+ it "301 redirect when multiple slashes" $ webApp $ do
6565 req <- request (setRawPathInfo defRequest "a//b/c")
66− assertStatus 302 req
66+ assertStatus 301 req
6767 assertHeader "Location" "../../a/b/c" req
6868
6969 let absoluteApp = flip runSession $ staticApp $ (defaultWebAppSettings "test") {
7070 ssMkRedirect = \_ u -> S8.append "http://www.example.com" u
7171 }
72− it "302 redirect when multiple slashes" $ absoluteApp $
72+ it "301 redirect when multiple slashes" $ absoluteApp $
7373 flip mapM_ ["/a//b/c", "a//b/c"] $ \path -> do
7474 req <- request (setRawPathInfo defRequest path)
75− assertStatus 302 req
75+ assertStatus 301 req
7676 assertHeader "Location" "http://www.example.com/a/b/c" req
7777
7878 describe "webApp when requesting a static asset" $ do
@@ -131,10 +131,10 @@ spec = do
131131 assertStatus 304 req
132132 assertNoHeader "Cache-Control" req
133133
134− context "302 redirect to add a trailing slash on directories if missing" $ do
134+ context "301 redirect to add a trailing slash on directories if missing" $ do
135135 it "works at the root" $ fileServerApp $ do
136136 req <- request (setRawPathInfo defRequest "/a")
137− assertStatus 302 req
137+ assertStatus 301 req
138138 assertHeader "Location" "/a/" req
139139
140140 it "works when an index.html is delivered" $ do
@@ -144,7 +144,7 @@ spec = do
144144 inTempDirectory $ fileServerAppWithSettings settings $ do
145145 liftIO $ touch "foo/index.html"
146146 req <- request (setRawPathInfo defRequest "/foo")
147− assertStatus 302 req
147+ assertStatus 301 req
148148 assertHeader "Location" "/foo/" req
149149
150150 let urlMapApp = flip runSession $ \req send ->
@@ -158,7 +158,7 @@ spec = do
158158 "urlMapApp: only works at subPath"
159159 it "works with subpath at the root of the file server" $ urlMapApp $ do
160160 req <- request (setRawPathInfo defRequest "/subPath")
161− assertStatus 302 req
161+ assertStatus 301 req
162162 assertHeader "Location" "/subPath/" req
163163
164164 context "with defaultWebAppSettings" $ do
wai-app-static/wai-app-static.cabal+1−1
@@ -1,5 +1,5 @@
11name: wai-app-static
2−version: 3.1.7.1
2+version: 3.1.6.3
33license: MIT
44license-file: LICENSE
55author: Michael Snoyman <michael@snoyman.com>
wai-conduit/Network/Wai/Conduit.hs+6−5
@@ -17,16 +17,17 @@ import qualified Data.ByteString as S
1717import Control.Monad (unless)
1818import Network.HTTP.Types
1919import Data.ByteString.Builder (Builder)
20+import Data.IORef
2021import qualified Data.Conduit.List as CL
2122
2223-- | Stream the request body.
2324--
2425-- Since 3.0.0
25−sourceRequestBody :: MonadIO m => Request -> ConduitT () ByteString m ()
26+sourceRequestBody :: MonadIO m => Request -> Source m ByteString
2627sourceRequestBody req =
2728 loop
2829 where
29− go = liftIO (getRequestBodyChunk req)
30+ go = liftIO (requestBody req)
3031
3132 loop = do
3233 bs <- go
@@ -37,9 +38,9 @@ sourceRequestBody req =
3738-- | Create an HTTP response out of a @Source@.
3839--
3940-- Since 3.0.0
40−responseSource :: Status -> ResponseHeaders -> ConduitT () (Flush Builder) IO () -> Response
41+responseSource :: Status -> ResponseHeaders -> Source IO (Flush Builder) -> Response
4142responseSource s hs src = responseStream s hs $ \send flush ->
42− runConduit $ src .| CL.mapM_ (\mbuilder ->
43+ src $$ CL.mapM_ (\mbuilder ->
4344 case mbuilder of
4445 Chunk b -> send b
4546 Flush -> flush)
@@ -49,7 +50,7 @@ responseSource s hs src = responseStream s hs $ \send flush ->
4950--
5051-- Since 3.0.0
5152responseRawSource :: (MonadIO m, MonadIO n)
52− => (ConduitT () ByteString m () -> ConduitT ByteString Void n () -> IO ())
53+ => (Source m ByteString -> Sink ByteString n () -> IO ())
5354 -> Response
5455 -> Response
5556responseRawSource app =
wai-extra/ChangeLog.md−16
@@ -1,21 +1,5 @@
11# Changelog for wai-extra
22
3−## 3.0.29.1
4−
5−* Fix `Network.Wai.Test.request` always sending an empty request body [#794](https://github.com/yesodweb/wai/pull/794)
6−
7−## 3.0.29
8−
9−* Export `Network.Wai.EventSource.eventStreamAppRaw` [#786](https://github.com/yesodweb/wai/pull/786)
10−
11−## 3.0.28
12−
13−* Add `Network.Wai.EventSource.eventStreamAppRaw` [#767](https://github.com/yesodweb/wai/pull/767)
14−
15−## 3.0.27
16−
17−* Add custom request log formatter which includes response headers [#762](https://github.com/yesodweb/wai/pull/762)
18−
193## 3.0.26.1
204
215* When available, supply the response size to custom loggers
wai-extra/Network/Wai/EventSource.hs+1−19
@@ -5,14 +5,11 @@
55 headers:
66
77 > [ ("X-Accel-Buffering", "no"), ("Cache-Control", "no-cache")]
8−
9− There is a small example using these functions in the @example@ directory.
108-}
119module Network.Wai.EventSource (
1210 ServerEvent(..),
1311 eventSourceAppChan,
14− eventSourceAppIO,
15− eventStreamAppRaw
12+ eventSourceAppIO
1613 ) where
1714
1815import Data.Function (fix)
@@ -42,18 +39,3 @@ eventSourceAppIO src _ sendResponse =
4239 case eventToBuilder se of
4340 Nothing -> return ()
4441 Just b -> sendChunk b >> flush >> loop
45−
46−-- | Make a new WAI EventSource application with a handler that emits events.
47−--
48−-- @since 3.0.28
49−eventStreamAppRaw :: ((ServerEvent -> IO()) -> IO () -> IO ()) -> Application
50−eventStreamAppRaw handler _ sendResponse =
51− sendResponse $ responseStream
52− status200
53− [(hContentType, "text/event-stream")]
54− $ \sendChunk flush -> handler (sendEvent sendChunk) flush
55− where
56− sendEvent sendChunk event =
57− case eventToBuilder event of
58− Nothing -> return ()
59− Just b -> sendChunk b
wai-extra/Network/Wai/EventSource/EventStream.hs+1−1
@@ -1,5 +1,5 @@
11{-# LANGUAGE CPP #-}
2−{- code adapted by Mathias Billman originally from Chris Smith https://github.com/cdsmith/gloss-web -}
2+{- code adapted by Mathias Billman originaly from Chris Smith https://github.com/cdsmith/gloss-web -}
33
44{-|
55 Internal module, usually you don't need to use it.
wai-extra/Network/Wai/Middleware/Gzip.hs−3
@@ -89,9 +89,6 @@ defaultCheckMime bs =
8989-- if gzip is supported.
9090--
9191-- File responses will be compressed according to the 'GzipFiles' setting.
92−--
93−-- Will only be applied based on the 'gzipCheckMime' setting. For default
94−-- behavior, see 'defaultCheckMime'.
9592gzip :: GzipSettings -> Middleware
9693gzip set app env sendResponse = app env $ \res ->
9794 case res of
wai-extra/Network/Wai/Middleware/RequestLogger.hs+6−56
@@ -14,7 +14,6 @@ module Network.Wai.Middleware.RequestLogger
1414 , OutputFormat (..)
1515 , OutputFormatter
1616 , OutputFormatterWithDetails
17− , OutputFormatterWithDetailsAndHeaders
1817 , Destination (..)
1918 , Callback
2019 , IPAddrSource (..)
@@ -50,42 +49,14 @@ import Network.Wai.Middleware.RequestLogger.Internal
5049import Network.Wai.Header (contentLength)
5150import Data.Text.Encoding (decodeUtf8')
5251
53−data OutputFormat
54− = Apache IPAddrSource
55− | Detailed Bool -- ^ use colors?
56− | CustomOutputFormat OutputFormatter
57− | CustomOutputFormatWithDetails OutputFormatterWithDetails
58− | CustomOutputFormatWithDetailsAndHeaders OutputFormatterWithDetailsAndHeaders
52+data OutputFormat = Apache IPAddrSource
53+ | Detailed Bool -- ^ use colors?
54+ | CustomOutputFormat OutputFormatter
55+ | CustomOutputFormatWithDetails OutputFormatterWithDetails
5956
6057type OutputFormatter = ZonedDate -> Request -> Status -> Maybe Integer -> LogStr
61−
62−type OutputFormatterWithDetails
63− = ZonedDate
64− -> Request
65− -> Status
66− -> Maybe Integer
67− -> NominalDiffTime
68− -> [S8.ByteString]
69− -> B.Builder
70− -> LogStr
71−
72−-- | Same as @OutputFormatterWithDetails@ but with response headers included
73−--
74−-- This is useful if you wish to include arbitrary application data in your
75−-- logs, e.g., an authenticated user ID, which you would set in a response
76−-- header in your application and retrieve in the log formatter.
77−--
78−-- @since 3.0.27
79−type OutputFormatterWithDetailsAndHeaders
80− = ZonedDate -- ^ When the log message was generated
81− -> Request -- ^ The WAI request
82− -> Status -- ^ HTTP status code
83− -> Maybe Integer -- ^ Response size
84− -> NominalDiffTime -- ^ Duration of the request
85− -> [S8.ByteString] -- ^ The request body
86− -> B.Builder -- ^ Raw response
87− -> [Header] -- ^ The response headers
88− -> LogStr
58+type OutputFormatterWithDetails =
59+ ZonedDate -> Request -> Status -> Maybe Integer -> NominalDiffTime -> [S8.ByteString] -> B.Builder -> LogStr
8960
9061data Destination = Handle Handle
9162 | Logger LoggerSet
@@ -137,9 +108,6 @@ mkRequestLogger RequestLoggerSettings{..} = do
137108 CustomOutputFormatWithDetails formatter -> do
138109 getdate <- getDateGetter flusher
139110 return $ customMiddlewareWithDetails callbackAndFlush getdate formatter
140− CustomOutputFormatWithDetailsAndHeaders formatter -> do
141− getdate <- getDateGetter flusher
142− return $ customMiddlewareWithDetailsAndHeaders callbackAndFlush getdate formatter
143111
144112apacheMiddleware :: ApacheLoggerActions -> Middleware
145113apacheMiddleware ala app req sendResponse = app req $ \res -> do
@@ -170,24 +138,6 @@ customMiddlewareWithDetails cb getdate formatter app req sendResponse = do
170138 readIORef builderIO
171139 return rspRcv
172140
173−customMiddlewareWithDetailsAndHeaders :: Callback -> IO ZonedDate -> OutputFormatterWithDetailsAndHeaders -> Middleware
174−customMiddlewareWithDetailsAndHeaders cb getdate formatter app req sendResponse = do
175− (req', reqBody) <- getRequestBody req
176− t0 <- getCurrentTime
177− app req' $ \res -> do
178− t1 <- getCurrentTime
179− date <- liftIO getdate
180− let msize = contentLength (responseHeaders res)
181− builderIO <- newIORef $ B.byteString ""
182− res' <- recordChunks builderIO res
183− rspRcv <- sendResponse res'
184− _ <- do
185− rawResponse <- readIORef builderIO
186− let status = responseStatus res'
187− duration = t1 `diffUTCTime` t0
188− resHeaders = responseHeaders res'
189− liftIO . cb $ formatter date req' status msize duration reqBody rawResponse resHeaders
190− return rspRcv
191141-- | Production request logger middleware.
192142--
193143-- This uses the 'Apache' logging format, and takes IP addresses for clients from
wai-extra/Network/Wai/Middleware/RequestLogger/JSON.hs+1−34
@@ -1,10 +1,7 @@
11{-# LANGUAGE RecordWildCards #-}
22{-# LANGUAGE CPP #-}
33
4−module Network.Wai.Middleware.RequestLogger.JSON
5− ( formatAsJSON
6− , formatAsJSONWithHeaders
7− ) where
4+module Network.Wai.Middleware.RequestLogger.JSON (formatAsJSON) where
85
96import qualified Data.ByteString.Builder as BB (toLazyByteString)
107import Data.ByteString.Lazy (toStrict)
@@ -43,30 +40,6 @@ formatAsJSON date req status responseSize duration reqBody response =
4340 , "time" .= decodeUtf8With lenientDecode date
4441 ]) <> "\n"
4542
46−-- | Same as @formatAsJSON@ but with response headers included
47−--
48−-- This is useful for passing arbitrary data from your application out to the
49−-- WAI layer for it to be logged, but you may need to be careful to
50−-- subsequently redact any headers which may contain sensitive data.
51−--
52−-- @since 3.0.27
53−formatAsJSONWithHeaders :: OutputFormatterWithDetailsAndHeaders
54−formatAsJSONWithHeaders date req status resSize duration reqBody res resHeaders =
55− toLogStr (encode $
56− object
57− [ "request" .= requestToJSON duration req reqBody
58− , "response" .= object
59− [ "status" .= statusCode status
60− , "size" .= resSize
61− , "headers" .= responseHeadersToJSON resHeaders
62− , "body" .=
63− if statusCode status >= 400
64− then Just . decodeUtf8With lenientDecode . toStrict . BB.toLazyByteString $ res
65− else Nothing
66− ]
67− , "time" .= decodeUtf8With lenientDecode date
68− ]) <> "\n"
69−
7043word32ToHostAddress :: Word32 -> Text
7144word32ToHostAddress = T.intercalate "." . map (T.pack . show) . fromIPv4 . fromHostAddress
7245
@@ -117,12 +90,6 @@ requestHeadersToJSON = toJSON . map hToJ where
11790 hToJ ("Cookie", _) = toJSON ("Cookie" :: Text, "-RDCT-" :: Text)
11891 hToJ hd = headerToJSON hd
11992
120−responseHeadersToJSON :: [Header] -> Value
121−responseHeadersToJSON = toJSON . map hToJ where
122− -- Redact cookies
123− hToJ ("Set-Cookie", _) = toJSON ("Set-Cookie" :: Text, "-RDCT-" :: Text)
124− hToJ hd = headerToJSON hd
125−
12693headerToJSON :: Header -> Value
12794headerToJSON (headerName, header) = toJSON (decodeUtf8With lenientDecode . original $ headerName, decodeUtf8With lenientDecode header)
12895
wai-extra/Network/Wai/Middleware/Rewrite.hs+1−1
@@ -169,7 +169,7 @@ import Control.Applicative
169169--
170170-- as the following example demonstrates.
171171--
172−-- Old versions of the library could only handle path pieces, not queries.
172+-- Old versions of the libary could only handle path pieces, not queries.
173173-- This could have been supplied to 'rewritePure'.
174174--
175175-- @
wai-extra/Network/Wai/Test.hs+14−22
@@ -99,10 +99,6 @@ runSession session app = ST.evalStateT (runReaderT session app) initState
9999data SRequest = SRequest
100100 { simpleRequest :: Request
101101 , simpleRequestBody :: L.ByteString
102− -- ^ Request body that will override the one set in 'simpleRequest'.
103− --
104− -- This is usually simpler than setting the body as a stateful IO-action
105− -- in 'simpleRequest'.
106102 }
107103data SResponse = SResponse
108104 { simpleStatus :: H.Status
@@ -110,16 +106,8 @@ data SResponse = SResponse
110106 , simpleBody :: L.ByteString
111107 }
112108 deriving (Show, Eq)
113−
114109request :: Request -> Session SResponse
115−request req = do
116− app <- ask
117− req' <- addCookiesToRequest req
118− response <- liftIO $ do
119− ref <- newIORef $ error "runResponse gave no result"
120− ResponseReceived <- app req' (runResponse ref)
121− readIORef ref
122− extractSetCookieFromSResponse response
110+request = srequest . flip SRequest L.empty
123111
124112-- | Set whole path (request path + query string).
125113setPath :: Request -> S8.ByteString -> Request
@@ -178,18 +166,22 @@ extractSetCookieFromSResponse response = do
178166 (Map.fromList [(Cookie.setCookieName c, c) | c <- newClientCookies ]))
179167 return response
180168
181−-- | Similar to 'request', but allows setting the request body as a plain
182−-- 'L.ByteString'.
183169srequest :: SRequest -> Session SResponse
184170srequest (SRequest req bod) = do
171+ app <- ask
185172 refChunks <- liftIO $ newIORef $ L.toChunks bod
186− request $
187− req
188− { requestBody = atomicModifyIORef refChunks $ \bss ->
189− case bss of
190− [] -> ([], S.empty)
191− x:y -> (y, x)
192− }
173+ let req' = req
174+ { requestBody = atomicModifyIORef refChunks $ \bss ->
175+ case bss of
176+ [] -> ([], S.empty)
177+ x:y -> (y, x)
178+ }
179+ req'' <- addCookiesToRequest req'
180+ response <- liftIO $ do
181+ ref <- newIORef $ error "runResponse gave no result"
182+ ResponseReceived <- app req'' (runResponse ref)
183+ readIORef ref
184+ extractSetCookieFromSResponse response
193185
194186runResponse :: IORef SResponse -> Response -> IO ResponseReceived
195187runResponse ref res = do
wai-extra/example/Main.hs−11
@@ -37,17 +37,6 @@ eventIO = do
3737 Nothing
3838 [string8 . show $ time]
3939
40−eventRaw :: (ServerEvent -> IO ()) -> IO () -> IO ()
41−eventRaw = handle 0
42− where
43− handle counter emit flush = do
44− threadDelay 1000000
45− _ <- emit $ ServerEvent (Just $ string8 "raw")
46− Nothing
47− [string8 . show $ counter]
48− _ <- flush
49− handle (counter + 1) emit flush
50−
5140main :: IO ()
5241main = do
5342 chan <- newChan
wai-extra/test/Network/Wai/TestSpec.hs+11−41
@@ -3,8 +3,6 @@ module Network.Wai.TestSpec (main, spec) where
33
44import Control.Monad (void)
55
6−import qualified Data.IORef as IORef
7−
86import qualified Data.Text.Encoding as TE
97
108import Data.Time.Calendar (fromGregorian)
@@ -55,53 +53,25 @@ spec = do
5553 it "sets rawQueryString to empty string" $ do
5654 rawQueryString (setPath defaultRequest "/foo/bar/baz") `shouldBe` ""
5755
58− describe "srequest" $ do
59−
60− let echoApp req respond = do
61− reqBody <- L8.fromStrict <$> getRequestBodyChunk req
62− let reqHeaders = requestHeaders req
63− respond $
64− responseLBS
65− status200
66− reqHeaders
67− reqBody
68−
69− it "returns the response body of an echo app" $ do
70− sresp <- flip runSession echoApp $
71− srequest $ SRequest defaultRequest "request body"
72− simpleBody sresp `shouldBe` "request body"
73−
7456 describe "request" $ do
7557
76− let echoApp req respond = do
77− reqBody <- L8.fromStrict <$> getRequestBodyChunk req
78− let reqHeaders = requestHeaders req
58+ let simpleApp _req respond =
7959 respond $
8060 responseLBS
8161 status200
82− reqHeaders
83− reqBody
62+ [("foo", "bar")]
63+ "simple"
8464
85− it "returns the status code of an echo app on default request" $ do
86− sresp <- runSession (request defaultRequest) echoApp
65+ it "returns the status code of a simple app on default request" $ do
66+ sresp <- runSession (request defaultRequest) simpleApp
8767 simpleStatus sresp `shouldBe` status200
8868
89− it "returns the response body of an echo app" $ do
90− bodyRef <- IORef.newIORef "request body"
91− let getBodyChunk = IORef.atomicModifyIORef bodyRef $ \leftover -> ("", leftover)
92− sresp <- flip runSession echoApp $
93− request $
94− defaultRequest
95− { requestBody = getBodyChunk
96− }
97− simpleBody sresp `shouldBe` "request body"
98−
99− it "returns the response headers of an echo app" $ do
100− sresp <- flip runSession echoApp $
101− request $
102− defaultRequest
103− { requestHeaders = [("foo", "bar")]
104− }
69+ it "returns the response body of a simple app on default request" $ do
70+ sresp <- runSession (request defaultRequest) simpleApp
71+ simpleBody sresp `shouldBe` "simple"
72+
73+ it "returns the response headers of a simple app on default request" $ do
74+ sresp <- runSession (request defaultRequest) simpleApp
10575 simpleHeaders sresp `shouldBe` [("foo", "bar")]
10676
10777 let cookieApp req respond =
wai-extra/wai-extra.cabal+3−3
@@ -1,5 +1,5 @@
11Name: wai-extra
2−Version: 3.0.29.1
2+Version: 3.0.26.1
33Synopsis: Provides some basic WAI handlers and middleware.
44description:
55 Provides basic WAI handler and middleware functionality:
@@ -99,8 +99,8 @@ Library
9999 , text >= 0.7
100100 , case-insensitive >= 0.2
101101 , data-default-class
102− , fast-logger >= 2.4.5
103− , wai-logger >= 2.3.2
102+ , fast-logger >= 2.4.5 && < 2.5
103+ , wai-logger >= 2.3.2 && < 2.4
104104 , ansi-terminal
105105 , resourcet >= 0.4.6 && < 1.3
106106 , void >= 0.5
wai-http2-extra/wai-http2-extra.cabal+2−9
@@ -1,5 +1,5 @@
11Name: wai-http2-extra
2−Version: 0.1.2
2+Version: 0.1.1
33Synopsis: WAI utilities for HTTP/2
44License: MIT
55License-file: LICENSE
@@ -8,7 +8,7 @@ Maintainer: kazu@iij.ad.jp
88Homepage: http://github.com/yesodweb/wai
99Category: Web
1010Build-Type: Simple
11−Cabal-Version: >=1.10
11+Cabal-Version: >=1.8
1212Stability: Stable
1313Description: WAI utilities for HTTP/2
1414
@@ -24,9 +24,6 @@ Library
2424 Exposed-modules: Network.Wai.Middleware.Push.Referer
2525 Other-modules: Network.Wai.Middleware.Push.Referer.LimitMultiMap
2626 Ghc-Options: -Wall
27− if impl(ghc >= 8)
28− default-extensions: Strict StrictData
29− default-language: Haskell2010
3027
3128Test-Suite doctest
3229 Type: exitcode-stdio-1.0
@@ -35,10 +32,6 @@ Test-Suite doctest
3532 Main-Is: doctests.hs
3633 Build-Depends: base
3734 , doctest >= 0.10.1
38− if impl(ghc >= 8)
39− default-extensions: Strict StrictData
40− default-language: Haskell2010
41−
4235Source-Repository head
4336 Type: git
4437 Location: git://github.com/yesodweb/wai.git
wai-websockets/server.lhs+1−7
@@ -8,8 +8,6 @@ this example, keep the [reference](http://jaspervdj.be/websockets/reference)
88nearby to check out the functions we use.
99
1010> {-# LANGUAGE OverloadedStrings, TemplateHaskell #-}
11−> {-# LANGUAGE CPP #-}
12−> {-# OPTIONS_GHC -Wno-unused-top-binds #-} -- for numClients
1311> import Data.Char (isPunctuation, isSpace)
1412> import Data.Monoid (mappend)
1513> import Data.Text (Text)
@@ -101,13 +99,9 @@ stays alive on some browsers.
10199
102100> application state pending = do
103101> conn <- WS.acceptRequest pending
104−#if __GLASGOW_HASKELL__ >= 806
105−> WS.withPingThread conn 30 (return ()) $ do
106−#else
107102> WS.forkPingThread conn 30
108−#endif
109103
110−When a client is successfully connected, we read the first message. This should
104+When a client is succesfully connected, we read the first message. This should
111105be in the format of "Hi! I am Jasper", where Jasper is the requested username.
112106
113107> msg <- WS.receiveData conn
wai-websockets/wai-websockets.cabal−1
@@ -45,7 +45,6 @@ Executable wai-websockets-example
4545 , text
4646 , file-embed
4747 , http-types
48− other-modules: Network.Wai.Handler.WebSockets
4948 else
5049 buildable: False
5150
warp-tls/ChangeLog.md−22
@@ -1,25 +1,3 @@
1−## 3.2.11
2−
3−* Ignoring an exception from shutdown (gracefulClose).
4−
5−## 3.2.10
6−
7−* Passing client certificate, if any, to Warp
8− [#783](https://github.com/yesodweb/wai/pull/783)
9−
10−## 3.2.9
11−
12−* Cooperating setGracefulCloseTimeout1 and setGracefulCloseTimeout2 of Warp.
13− [#782](https://github.com/yesodweb/wai/pull/782)
14−
15−## 3.2.8
16−
17−* Using gracefullClose of network 3.1.1 or later if available.
18−
19−## 3.2.7
20−
21−* Relaxing version constraint.
22−
231## 3.2.6
242
253* Using the Strict and StrictData language extensions for GHC >8.
warp-tls/Network/Wai/Handler/WarpTLS.hs+15−27
@@ -53,9 +53,6 @@ import qualified Data.IORef as I
5353import Data.Streaming.Network (bindPortTCP, safeRecv)
5454import Data.Typeable (Typeable)
5555import Network.Socket (Socket, close, withSocketsDo, SockAddr, accept)
56−#if MIN_VERSION_network(3,1,1)
57−import Network.Socket (gracefulClose)
58−#endif
5956import Network.Socket.ByteString (sendAll)
6057import qualified Network.TLS as TLS
6158import qualified Crypto.PubKey.DH as DH
@@ -281,7 +278,7 @@ runTLSSocket' :: TLSSettings -> Settings -> TLS.Credential -> TLS.SessionManager
281278runTLSSocket' tlsset@TLSSettings{..} set credential mgr sock app =
282279 runSettingsConnectionMakerSecure set get app
283280 where
284− get = getter tlsset set sock params
281+ get = getter tlsset sock params
285282 params = def { -- TLS.ServerParams
286283 TLS.serverWantClientCert = tlsWantClientCert
287284 , TLS.serverCACertificates = []
@@ -322,55 +319,49 @@ alpn xs
322319
323320----------------------------------------------------------------
324321
325−getter :: TLS.TLSParams params => TLSSettings -> Settings -> Socket -> params -> IO (IO (Connection, Transport), SockAddr)
326−getter tlsset@TLSSettings{..} set sock params = do
322+getter :: TLS.TLSParams params => TLSSettings -> Socket -> params -> IO (IO (Connection, Transport), SockAddr)
323+getter tlsset@TLSSettings{..} sock params = do
327324#if WINDOWS
328325 (s, sa) <- windowsThreadBlockHack $ accept sock
329326#else
330327 (s, sa) <- accept sock
331328#endif
332329 setSocketCloseOnExec s
333− return (mkConn tlsset set s params, sa)
330+ return (mkConn tlsset s params, sa)
334331
335−mkConn :: TLS.TLSParams params => TLSSettings -> Settings -> Socket -> params -> IO (Connection, Transport)
336−mkConn tlsset set s params = switch `onException` close s
332+mkConn :: TLS.TLSParams params => TLSSettings -> Socket -> params -> IO (Connection, Transport)
333+mkConn tlsset s params = switch `onException` close s
337334 where
338335 switch = do
339336 firstBS <- safeRecv s 4096
340337 if not (S.null firstBS) && S.head firstBS == 0x16 then
341− httpOverTls tlsset set s firstBS params
338+ httpOverTls tlsset s firstBS params
342339 else
343− plainHTTP tlsset set s firstBS
340+ plainHTTP tlsset s firstBS
344341
345342----------------------------------------------------------------
346343
347−httpOverTls :: TLS.TLSParams params => TLSSettings -> Settings -> Socket -> S.ByteString -> params -> IO (Connection, Transport)
348−httpOverTls TLSSettings{..} _set s bs0 params = do
344+httpOverTls :: TLS.TLSParams params => TLSSettings -> Socket -> S.ByteString -> params -> IO (Connection, Transport)
345+httpOverTls TLSSettings{..} s bs0 params = do
349346 recvN <- makePlainReceiveN s bs0
350347 ctx <- TLS.contextNew (backend recvN) params
351348 TLS.contextHookSetLogging ctx tlsLogging
352349 TLS.handshake ctx
353− h2 <- (== Just "h2") <$> TLS.getNegotiatedProtocol ctx
354− isH2 <- I.newIORef h2
355350 writeBuf <- allocateBuffer bufferSize
356351 -- Creating a cache for leftover input data.
357352 ref <- I.newIORef ""
358353 tls <- getTLSinfo ctx
359− return (conn ctx writeBuf ref isH2, tls)
354+ return (conn ctx writeBuf ref, tls)
360355 where
361356 backend recvN = TLS.Backend {
362357 TLS.backendFlush = return ()
363−#if MIN_VERSION_network(3,1,1)
364− , TLS.backendClose = gracefulClose s 5000 `E.catch` \(SomeException _) -> return ()
365−#else
366358 , TLS.backendClose = close s
367−#endif
368359 , TLS.backendSend = sendAll' s
369360 , TLS.backendRecv = recvN
370361 }
371362 sendAll' sock bs = sendAll sock bs `E.catch` \(SomeException _) ->
372363 throwIO ConnectionClosedByPeer
373− conn ctx writeBuf ref isH2 = Connection {
364+ conn ctx writeBuf ref = Connection {
374365 connSendMany = TLS.sendData ctx . L.fromChunks
375366 , connSendAll = sendall
376367 , connSendFile = sendfile
@@ -380,7 +371,6 @@ httpOverTls TLSSettings{..} _set s bs0 params = do
380371 , connRecvBuf = recvBuf ref
381372 , connWriteBuffer = writeBuf
382373 , connBufferSize = bufferSize
383− , connHTTP2 = isH2
384374 }
385375 where
386376 sendall = TLS.sendData ctx . L.fromChunks . return
@@ -468,13 +458,11 @@ getTLSinfo ctx = do
468458#if MIN_VERSION_tls(1,5,0)
469459 TLS.TLS13 -> (3,4)
470460#endif
471− clientCert <- TLS.getClientCertificateChain ctx
472461 return TLS {
473462 tlsMajorVersion = major
474463 , tlsMinorVersion = minor
475464 , tlsNegotiatedProtocol = proto
476465 , tlsChiperID = TLS.cipherID infoCipher
477− , tlsClientCertificate = clientCert
478466 }
479467
480468tryIO :: IO a -> IO (Either IOException a)
@@ -482,10 +470,10 @@ tryIO = try
482470
483471----------------------------------------------------------------
484472
485−plainHTTP :: TLSSettings -> Settings -> Socket -> S.ByteString -> IO (Connection, Transport)
486−plainHTTP TLSSettings{..} set s bs0 = case onInsecure of
473+plainHTTP :: TLSSettings -> Socket -> S.ByteString -> IO (Connection, Transport)
474+plainHTTP TLSSettings{..} s bs0 = case onInsecure of
487475 AllowInsecure -> do
488− conn' <- socketConnection set s
476+ conn' <- socketConnection s
489477 cachedRef <- I.newIORef bs0
490478 let conn'' = conn'
491479 { connRecv = recvPlain cachedRef (connRecv conn')
warp-tls/warp-tls.cabal+5−5
@@ -1,5 +1,5 @@
11Name: warp-tls
2−Version: 3.2.11
2+Version: 3.2.7
33Synopsis: HTTP over TLS support for Warp via the TLS package
44License: MIT
55License-file: LICENSE
@@ -18,16 +18,16 @@ description: SSLv1 and SSLv2 are obsoleted by IETF.
1818extra-source-files: ChangeLog.md README.md
1919
2020Library
21− Build-Depends: base >= 4.10 && < 5
21+ Build-Depends: base >= 4.8 && < 5
2222 , bytestring >= 0.9
2323 , wai >= 3.2 && < 3.3
24− , warp >= 3.3.6 && < 3.4
24+ , warp >= 3.2.17 && < 3.4
2525 , data-default-class >= 0.0.1
26− , tls >= 1.5.3
26+ , tls >= 1.3.10
2727 , cryptonite >= 0.12
2828 , network >= 2.2.1
2929 , streaming-commons
30− , tls-session-manager >= 0.0.4
30+ , tls-session-manager >= 0.0.0.1
3131 Exposed-modules: Network.Wai.Handler.WarpTLS
3232 ghc-options: -Wall
3333 if os(windows)
warp/ChangeLog.md+1−50
@@ -1,56 +1,7 @@
1−## 3.3.9
2−
3−* Don't insert Last-Modified: if exists.
4− [#791](https://github.com/yesodweb/wai/pull/791)
5−
6−## 3.3.8
7−
8−* Maximum header size is configurable.
9− [#781](https://github.com/yesodweb/wai/pull/781)
10−* Ignoring an exception from shutdown (gracefulClose).
11−
12−## 3.3.7
13−
14−* InvalidArgument (Bad file descriptor) is ignored in `receive`.
15− [#787](https://github.com/yesodweb/wai/pull/787)
16−
17−## 3.3.6
18−
19−* Fixing a bug of thread killed in the case of event source with
20− HTTP/2 (fixing #692 and #785)
21−* New APIs: clientCertificate to get client's certificate
22− [#783](https://github.com/yesodweb/wai/pull/783)
23−
24−## 3.3.5
25−
26−* New APIs: setGracefulCloseTimeout1 and setGracefulCloseTimeout2.
27− For HTTP/1.x, connections are closed immediately by default.
28− gracefullClose is used for HTTP/2 by default.
29− [#782](https://github.com/yesodweb/wai/pull/782)
30−
31−## 3.3.4
32−
33−* Setting isSecure of HTTP/2 correctly.
34−
35−## 3.3.3
36−
37−* Calling setOnException in HTTP/2.
38− [#771](https://github.com/yesodweb/wai/pull/771)
39−
40−## 3.3.2
41−
42−* Fixing a bug of HTTP/2 without fd cache.
43−
44−## 3.3.1
45−
46−* Using gracefullClose of network 3.1.1 or later if available.
47−* If the first line of an HTTP request is really invalid,
48− don't send an error response
49−
501## 3.3.0
512
523* Switching from the original implementation to HTTP/2 server library.
53− [#754](https://github.com/yesodweb/wai/pull/754)
4+ [#752](https://github.com/yesodweb/wai/pull/752)
545* Breaking change: The type of `http2dataTrailers` is now
556 `HTTP2Data -> TrailersMaker`.
567
warp/Network/Wai/Handler/Warp.hs+1−51
@@ -72,9 +72,6 @@ module Network.Wai.Handler.Warp (
7272 , setLogger
7373 , setServerPushLogger
7474 , setGracefulShutdownTimeout
75− , setGracefulCloseTimeout1
76− , setGracefulCloseTimeout2
77− , setMaxTotalHeaderLength
7875 -- ** Getters
7976 , getPort
8077 , getHost
@@ -82,8 +79,6 @@ module Network.Wai.Handler.Warp (
8279 , getOnClose
8380 , getOnException
8481 , getGracefulShutdownTimeout
85− , getGracefulCloseTimeout1
86− , getGracefulCloseTimeout2
8782 -- ** Exception handler
8883 , defaultOnException
8984 , defaultShouldDisplayException
@@ -98,7 +93,6 @@ module Network.Wai.Handler.Warp (
9893 , pauseTimeout
9994 , FileInfo(..)
10095 , getFileInfo
101− , clientCertificate
10296 , withApplication
10397 , withApplicationSettings
10498 , testWithApplication
@@ -127,7 +121,6 @@ module Network.Wai.Handler.Warp (
127121import Control.Exception (SomeException, throwIO)
128122import Data.Streaming.Network (HostPreference)
129123import qualified Data.Vault.Lazy as Vault
130−import Data.X509
131124import qualified Network.HTTP.Types as H
132125import Network.Socket (SockAddr)
133126import Network.Wai (Request, Response, vault)
@@ -385,7 +378,7 @@ setProxyProtocolRequired y = y { settingsProxyProtocol = ProxyProtocolRequired }
385378--
386379-- WARNING: This is contrary to the PROXY protocol specification and
387380-- using it can indicate a security problem with your
388−-- architecture if the web server is directly accessible
381+-- architecture if the web server is directly accessable
389382-- to the public, since it would allow easy IP address
390383-- spoofing. However, it can be useful in some cases,
391384-- such as if a load balancer health check uses regular
@@ -437,13 +430,6 @@ setGracefulShutdownTimeout :: Maybe Int
437430 -> Settings -> Settings
438431setGracefulShutdownTimeout time y = y { settingsGracefulShutdownTimeout = time }
439432
440−-- | Set the maximum header size that Warp will tolerate when using HTTP/1.x.
441−--
442−-- Since 3.3.8
443−setMaxTotalHeaderLength :: Int -> Settings -> Settings
444−setMaxTotalHeaderLength maxTotalHeaderLength settings = settings
445− { settingsMaxTotalHeaderLength = maxTotalHeaderLength }
446−
447433-- | Explicitly pause the slowloris timeout.
448434--
449435-- This is useful for cases where you partially consume a request body. For
@@ -471,39 +457,3 @@ pauseTimeout = fromMaybe (return ()) . Vault.lookup pauseTimeoutKey . vault
471457-- Since 3.1.10
472458getFileInfo :: Request -> FilePath -> IO FileInfo
473459getFileInfo = fromMaybe (\_ -> throwIO (userError "getFileInfo")) . Vault.lookup getFileInfoKey . vault
474−
475−-- | A timeout to limit the time (in milliseconds) waiting for
476−-- FIN for HTTP/1.x. 0 means uses immediate close.
477−-- Default: 0.
478−--
479−-- Since 3.3.5
480−setGracefulCloseTimeout1 :: Int -> Settings -> Settings
481−setGracefulCloseTimeout1 x y = y { settingsGracefulCloseTimeout1 = x }
482−
483−-- | A timeout to limit the time (in milliseconds) waiting for
484−-- FIN for HTTP/1.x. 0 means uses immediate close.
485−--
486−-- Since 3.3.5
487−getGracefulCloseTimeout1 :: Settings -> Int
488−getGracefulCloseTimeout1 = settingsGracefulCloseTimeout1
489−
490−-- | A timeout to limit the time (in milliseconds) waiting for
491−-- FIN for HTTP/2. 0 means uses immediate close.
492−-- Default: 2000.
493−--
494−-- Since 3.3.5
495−setGracefulCloseTimeout2 :: Int -> Settings -> Settings
496−setGracefulCloseTimeout2 x y = y { settingsGracefulCloseTimeout2 = x }
497−
498−-- | A timeout to limit the time (in milliseconds) waiting for
499−-- FIN for HTTP/2. 0 means uses immediate close.
500−--
501−-- Since 3.3.5
502−getGracefulCloseTimeout2 :: Settings -> Int
503−getGracefulCloseTimeout2 = settingsGracefulCloseTimeout2
504−
505−-- | Getting information of client certificate.
506−--
507−-- Since 3.3.5
508−clientCertificate :: Request -> Maybe CertificateChain
509−clientCertificate = join . Vault.lookup getClientCertificateKey . vault
warp/Network/Wai/Handler/Warp/File.hs+4−7
@@ -34,15 +34,12 @@ data RspFileInfo = WithoutBody H.Status
3434----------------------------------------------------------------
3535
3636conditionalRequest :: I.FileInfo
37− -> H.ResponseHeaders
38− -> IndexedHeader -- ^ Response
39− -> IndexedHeader -- ^ Request
37+ -> H.ResponseHeaders -> IndexedHeader
4038 -> RspFileInfo
41−conditionalRequest finfo hs0 rspidx reqidx = case condition of
39+conditionalRequest finfo hs0 reqidx = case condition of
4240 nobody@(WithoutBody _) -> nobody
43− WithBody s _ off len -> let !hs1 = addContentHeaders hs0 off len size
44− !hasLM = isJust $ rspidx ! fromEnum ResLastModified
45− !hs = [ (H.hLastModified,date) | not hasLM ] ++ hs1
41+ WithBody s _ off len -> let !hs = (H.hLastModified,date) :
42+ addContentHeaders hs0 off len size
4643 in WithBody s hs off len
4744 where
4845 !mtime = I.fileInfoTime finfo
warp/Network/Wai/Handler/Warp/HTTP2.hs+16−25
@@ -6,7 +6,6 @@
66
77module Network.Wai.Handler.Warp.HTTP2 (http2) where
88
9−import qualified Data.IORef as I
109import qualified Control.Exception as E
1110import qualified Network.HTTP2.Server as H2
1211import Network.Socket (SockAddr)
@@ -24,46 +23,38 @@ import Network.Wai.Handler.Warp.Types
2423
2524----------------------------------------------------------------
2625
27−http2 :: Connection -> Transport -> InternalInfo -> SockAddr -> S.Settings -> (BufSize -> IO ByteString) -> (ByteString -> IO ()) -> Application -> IO ()
28−http2 conn transport ii addr settings readN send app =
26+http2 :: Connection -> InternalInfo -> SockAddr -> S.Settings -> (BufSize -> IO ByteString) -> Application -> IO ()
27+http2 conn ii addr settings readN app =
2928 H2.run conf http2server
3029 where
3130 conf = H2.Config {
3231 confWriteBuffer = connWriteBuffer conn
3332 , confBufferSize = connBufferSize conn
34− , confSendAll = send
33+ , confSendAll = connSendAll conn
3534 , confReadN = readN
3635 , confPositionReadMaker = pReadMaker ii
3736 }
3837
3938 http2server h2req aux response = do
4039 req <- toWAIRequest h2req aux
41− ref <- I.newIORef Nothing
42− eResponseReceived <- E.try $ app req $ \rsp -> do
40+ ResponseReceived <- app req $ \rsp -> do
4341 h2rsp <- fromResponse settings ii req rsp
4442 pps <- fromPushPromises ii req
45− I.writeIORef ref $ Just (h2rsp, pps)
46− _ <- response h2rsp pps
43+ ex <- E.try $ response h2rsp pps
44+ case ex of
45+ Right () -> do
46+ logResponse h2rsp req
47+ mapM_ (logPushPromise req) pps
48+ Left e@(E.SomeException _)
49+ -- killed by the local worker manager
50+ | Just E.ThreadKilled <- E.fromException e -> return ()
51+ -- killed by the local timeout manager
52+ | Just T.TimeoutThread <- E.fromException e -> return ()
53+ | otherwise -> S.settingsOnException settings (Just req) e
4754 return ResponseReceived
48− case eResponseReceived of
49− Right ResponseReceived -> do
50− Just (h2rsp, pps) <- I.readIORef ref
51− logResponse h2rsp req
52− mapM_ (logPushPromise req) pps
53− Left e@(E.SomeException _)
54− -- killed by the local worker manager
55− | Just E.ThreadKilled <- E.fromException e -> return ()
56− -- killed by the local timeout manager
57− | Just T.TimeoutThread <- E.fromException e -> return ()
58− | otherwise -> do
59− S.settingsOnException settings (Just req) e
60− let ersp = S.settingsOnExceptionResponse settings e
61− h2rsp' <- fromResponse settings ii req ersp
62− _ <- response h2rsp' []
63− logResponse h2rsp' req
6455 return ()
6556
66− toWAIRequest h2req aux = toRequest ii settings addr hdr bdylen bdy th transport
57+ toWAIRequest h2req aux = toRequest ii settings addr hdr bdylen bdy th
6758 where
6859 !hdr = H2.requestHeaders h2req
6960 !bdy = H2.getRequestBodyChunk h2req
warp/Network/Wai/Handler/Warp/HTTP2/File.hs+2−6
@@ -15,12 +15,8 @@ import Network.Wai.Handler.Warp.SendFile (positionRead)
1515
1616pReadMaker :: InternalInfo -> PositionReadMaker
1717pReadMaker ii path = do
18− (mfd, refresh) <- getFd ii path
19− case mfd of
20− Just fd -> return (pread fd, Refresher refresh)
21− Nothing -> do
22− fd <- openFile path
23− return (pread fd, Closer $ closeFile fd)
18+ (Just fd, refresh) <- getFd ii path
19+ return (pread fd, Refresher refresh)
2420 where
2521 pread :: Fd -> PositionRead
2622 pread fd off bytes buf = fromIntegral <$> positionRead fd buf bytes' off'
warp/Network/Wai/Handler/Warp/HTTP2/Request.hs+6−7
@@ -22,23 +22,23 @@ import qualified System.TimeManager as T
2222
2323import Network.Wai.Handler.Warp.HTTP2.Types
2424import Network.Wai.Handler.Warp.Imports
25−import Network.Wai.Handler.Warp.Request (getFileInfoKey, pauseTimeoutKey, getClientCertificateKey)
25+import Network.Wai.Handler.Warp.Request (getFileInfoKey, pauseTimeoutKey)
2626import qualified Network.Wai.Handler.Warp.Settings as S (Settings, settingsNoParsePath)
2727import Network.Wai.Handler.Warp.Types
2828
29−type ToReq = (TokenHeaderList,ValueTable) -> Maybe Int -> IO ByteString -> T.Handle -> Transport -> IO Request
29+type ToReq = (TokenHeaderList,ValueTable) -> Maybe Int -> IO ByteString -> T.Handle -> IO Request
3030
3131----------------------------------------------------------------
3232
3333toRequest :: InternalInfo -> S.Settings -> SockAddr -> ToReq
34−toRequest ii settings addr ht bodylen body th transport = do
34+toRequest ii settings addr ht bodylen body th = do
3535 ref <- newIORef Nothing
36− toRequest' ii settings addr ref ht bodylen body th transport
36+ toRequest' ii settings addr ref ht bodylen body th
3737
3838toRequest' :: InternalInfo -> S.Settings -> SockAddr
3939 -> IORef (Maybe HTTP2Data)
4040 -> ToReq
41−toRequest' ii settings addr ref (reqths,reqvt) bodylen body th transport = return req
41+toRequest' ii settings addr ref (reqths,reqvt) bodylen body th = return req
4242 where
4343 !req = Request {
4444 requestMethod = colonMethod
@@ -48,7 +48,7 @@ toRequest' ii settings addr ref (reqths,reqvt) bodylen body th transport = retur
4848 , rawQueryString = query
4949 , queryString = H.parseQuery query
5050 , requestHeaders = headers
51− , isSecure = isTransportSecure transport
51+ , isSecure = True
5252 , remoteHost = addr
5353 , requestBody = body
5454 , vault = vaultValue
@@ -83,7 +83,6 @@ toRequest' ii settings addr ref (reqths,reqvt) bodylen body th transport = retur
8383 $ Vault.insert setHTTP2DataKey (writeIORef ref)
8484 $ Vault.insert modifyHTTP2DataKey (modifyIORef' ref)
8585 $ Vault.insert pauseTimeoutKey (T.pause th)
86− $ Vault.insert getClientCertificateKey (tlsClientCertificate transport)
8786 Vault.empty
8887
8988getHTTP2DataKey :: Vault.Key (IO (Maybe HTTP2Data))
warp/Network/Wai/Handler/Warp/HTTP2/Response.hs+1−2
@@ -72,8 +72,7 @@ responseFile _ rsphdr isHead path Nothing ii reqhdr = do
7272 Left (_ex :: E.IOException) -> return $ response404 rsphdr
7373 Right finfo -> do
7474 let reqidx = indexRequestHeader reqhdr
75− rspidx = indexResponseHeader rsphdr
76− case conditionalRequest finfo rsphdr rspidx reqidx of
75+ case conditionalRequest finfo rsphdr reqidx of
7776 WithoutBody s -> return $ responseNoBody s rsphdr
7877 WithBody s rsphdr' off bytes -> do
7978 let !off' = fromIntegral off
warp/Network/Wai/Handler/Warp/Header.hs−2
@@ -69,7 +69,6 @@ indexResponseHeader hdr = traverseHeader hdr responseMaxIndex responseKeyIndex
6969data ResponseHeaderIndex = ResContentLength
7070 | ResServer
7171 | ResDate
72− | ResLastModified
7372 deriving (Enum,Bounded)
7473
7574-- | The size for 'IndexedHeader' for HTTP Response.
@@ -80,7 +79,6 @@ responseKeyIndex :: HeaderName -> Int
8079responseKeyIndex hn = case BS.length bs of
8180 4 -> if bs == "date" then fromEnum ResDate else -1
8281 6 -> if bs == "server" then fromEnum ResServer else -1
83− 13 -> if bs == "last-modified" then fromEnum ResLastModified else -1
8482 14 -> if bs == "content-length" then fromEnum ResContentLength else -1
8583 _ -> -1
8684 where
warp/Network/Wai/Handler/Warp/Recv.hs+4−25
@@ -17,14 +17,7 @@ import Foreign.C.Types
1717import Foreign.ForeignPtr (withForeignPtr)
1818import Foreign.Ptr (Ptr, castPtr, plusPtr)
1919import GHC.Conc (threadWaitRead)
20−import qualified GHC.IO.Exception as E
21−import Network.Socket (Socket)
22−import qualified System.IO.Error as E
23−#if MIN_VERSION_network(3,1,0)
24−import Network.Socket (withFdSocket)
25−#else
26−import Network.Socket (fdSocket)
27−#endif
20+import Network.Socket (Socket, fdSocket)
2821import System.Posix.Types (Fd(..))
2922
3023import Network.Wai.Handler.Warp.Buffer
@@ -96,31 +89,19 @@ spell init0 siz0 recv recvBuf
9689 siz' = siz - len
9790 loop bss' siz'
9891
99−-- The timeout manager may close the socket.
100−-- In that case, an error of "Bad file descriptor" occurs.
101−-- We ignores it because we expect TimeoutThread.
10292receive :: Socket -> BufferPool -> Recv
103−receive sock pool = E.handle handler $ withBufferPool pool $ \ (ptr, size) -> do
104−#if MIN_VERSION_network(3,1,0)
105− withFdSocket sock $ \fd -> do
106−#elif MIN_VERSION_network(3,0,0)
93+receive sock pool = withBufferPool pool $ \ (ptr, size) -> do
94+#if MIN_VERSION_network(3,0,0)
10795 fd <- fdSocket sock
10896#else
10997 let fd = fdSocket sock
11098#endif
11199 let size' = fromIntegral size
112100 fromIntegral <$> receiveloop fd ptr size'
113− where
114− handler :: E.IOException -> IO ByteString
115− handler e
116− | E.ioeGetErrorType e == E.InvalidArgument = return ""
117− | otherwise = E.throwIO e
118101
119102receiveBuf :: Socket -> RecvBuf
120103receiveBuf sock buf0 siz0 = do
121−#if MIN_VERSION_network(3,1,0)
122− withFdSocket sock $ \fd -> do
123−#elif MIN_VERSION_network(3,0,0)
104+#if MIN_VERSION_network(3,0,0)
124105 fd <- fdSocket sock
125106#else
126107 let fd = fdSocket sock
@@ -153,8 +134,6 @@ receiveloop sock ptr size = do
153134 else
154135 return bytes
155136
156−#ifndef mingw32_HOST_OS
157137-- fixme: the type of the return value
158138foreign import ccall unsafe "recv"
159139 c_recv :: CInt -> Ptr CChar -> CSize -> CInt -> IO CInt
160−#endif
warp/Network/Wai/Handler/Warp/Request.hs+19−21
@@ -9,7 +9,6 @@ module Network.Wai.Handler.Warp.Request (
99 , headerLines
1010 , pauseTimeoutKey
1111 , getFileInfoKey
12− , getClientCertificateKey
1312 , NoKeepAliveRequest (..)
1413 ) where
1514
@@ -22,7 +21,6 @@ import qualified Data.CaseInsensitive as CI
2221import qualified Data.IORef as I
2322import Data.Typeable (Typeable)
2423import qualified Data.Vault.Lazy as Vault
25−import Data.X509
2624import qualified Network.HTTP.Types as H
2725import Network.Socket (SockAddr)
2826import Network.Wai
@@ -38,7 +36,13 @@ import Network.Wai.Handler.Warp.Header
3836import Network.Wai.Handler.Warp.Imports hiding (readInt, lines)
3937import Network.Wai.Handler.Warp.ReadInt
4038import Network.Wai.Handler.Warp.RequestHeader
41−import Network.Wai.Handler.Warp.Settings (Settings, settingsNoParsePath, settingsMaxTotalHeaderLength)
39+import Network.Wai.Handler.Warp.Settings (Settings, settingsNoParsePath)
40+
41+----------------------------------------------------------------
42+
43+-- FIXME come up with good values here
44+maxTotalHeaderLength :: Int
45+maxTotalHeaderLength = 50 * 1024
4246
4347----------------------------------------------------------------
4448
@@ -51,7 +55,6 @@ recvRequest :: Bool -- ^ first request on this connection?
5155 -> Timeout.Handle
5256 -> SockAddr -- ^ Peer's address.
5357 -> Source -- ^ Where HTTP request comes from.
54− -> Transport
5558 -> IO (Request
5659 ,Maybe (I.IORef Int)
5760 ,IndexedHeader
@@ -61,8 +64,8 @@ recvRequest :: Bool -- ^ first request on this connection?
6164 -- 'IndexedHeader' of HTTP request for internal use,
6265 -- Body producing action used for flushing the request body
6366
64−recvRequest firstRequest settings conn ii th addr src transport = do
65− hdrlines <- headerLines (settingsMaxTotalHeaderLength settings) firstRequest src
67+recvRequest firstRequest settings conn ii th addr src = do
68+ hdrlines <- headerLines firstRequest src
6669 (method, unparsedPath, path, query, httpversion, hdr) <- parseHeaderLines hdrlines
6770 let idxhdr = indexRequestHeader hdr
6871 expect = idxhdr ! fromEnum ReqExpect
@@ -72,7 +75,6 @@ recvRequest firstRequest settings conn ii th addr src transport = do
7275 rawPath = if settingsNoParsePath settings then unparsedPath else path
7376 vaultValue = Vault.insert pauseTimeoutKey (Timeout.pause th)
7477 $ Vault.insert getFileInfoKey (getFileInfo ii)
75− $ Vault.insert getClientCertificateKey (tlsClientCertificate transport)
7678 Vault.empty
7779 (rbody, remainingRef, bodyLength) <- bodyAndSource src cl te
7880 -- body producing function which will produce '100-continue', if needed
@@ -87,7 +89,7 @@ recvRequest firstRequest settings conn ii th addr src transport = do
8789 , rawQueryString = query
8890 , queryString = H.parseQuery query
8991 , requestHeaders = hdr
90− , isSecure = isTransportSecure transport
92+ , isSecure = False
9193 , remoteHost = addr
9294 , requestBody = rbody'
9395 , vault = vaultValue
@@ -101,8 +103,8 @@ recvRequest firstRequest settings conn ii th addr src transport = do
101103
102104----------------------------------------------------------------
103105
104−headerLines :: Int -> Bool -> Source -> IO [ByteString]
105−headerLines maxTotalHeaderLength firstRequest src = do
106+headerLines :: Bool -> Source -> IO [ByteString]
107+headerLines firstRequest src = do
106108 bs <- readSource src
107109 if S.null bs
108110 -- When we're working on a keep-alive connection and trying to
@@ -110,7 +112,7 @@ headerLines maxTotalHeaderLength firstRequest src = do
110112 -- lack of data as a real exception. See the http1 function in
111113 -- the Run module for more details.
112114 then if firstRequest then throwIO ConnectionClosedByPeer else throwIO NoKeepAliveRequest
113− else push maxTotalHeaderLength src (THStatus 0 id id) bs
115+ else push src (THStatus 0 id id) bs
114116
115117data NoKeepAliveRequest = NoKeepAliveRequest
116118 deriving (Show, Typeable)
@@ -220,8 +222,8 @@ close :: Sink ByteString IO a
220222close = throwIO IncompleteHeaders
221223-}
222224
223−push :: Int -> Source -> THStatus -> ByteString -> IO [ByteString]
224−push maxTotalHeaderLength src (THStatus len lines prepend) bs'
225+push :: Source -> THStatus -> ByteString -> IO [ByteString]
226+push src (THStatus len lines prepend) bs'
225227 -- Too many bytes
226228 | len > maxTotalHeaderLength = throwIO OverLargeHeader
227229 | otherwise = push' mnl
@@ -249,13 +251,13 @@ push maxTotalHeaderLength src (THStatus len lines prepend) bs'
249251 push' Nothing = do
250252 bst <- readSource' src
251253 when (S.null bst) $ throwIO IncompleteHeaders
252− push maxTotalHeaderLength src status bst
254+ push src status bst
253255 where
254256 len' = len + bsLen
255257 prepend' = S.append bs
256258 status = THStatus len' lines prepend'
257259 -- Found a newline, but next line continues as a multiline header
258− push' (Just (end, True)) = push maxTotalHeaderLength src status rest
260+ push' (Just (end, True)) = push src status rest
259261 where
260262 rest = S.drop (end + 1) bs
261263 prepend' = S.append (SU.unsafeTake (checkCR bs end) bs)
@@ -274,12 +276,12 @@ push maxTotalHeaderLength src (THStatus len lines prepend) bs'
274276 in if start < bsLen then
275277 -- more bytes in this chunk, push again
276278 let bs'' = SU.unsafeDrop start bs
277− in push maxTotalHeaderLength src status bs''
279+ in push src status bs''
278280 else do
279281 -- no more bytes in this chunk, ask for more
280282 bst <- readSource' src
281283 when (S.null bs) $ throwIO IncompleteHeaders
282− push maxTotalHeaderLength src status bst
284+ push src status bst
283285 where
284286 start = end + 1 -- start of next chunk
285287 line = SU.unsafeTake (checkCR bs end) bs
@@ -297,7 +299,3 @@ pauseTimeoutKey = unsafePerformIO Vault.newKey
297299getFileInfoKey :: Vault.Key (FilePath -> IO FileInfo)
298300getFileInfoKey = unsafePerformIO Vault.newKey
299301{-# NOINLINE getFileInfoKey #-}
300−
301−getClientCertificateKey :: Vault.Key (Maybe CertificateChain)
302−getClientCertificateKey = unsafePerformIO Vault.newKey
303−{-# NOINLINE getClientCertificateKey #-}
warp/Network/Wai/Handler/Warp/Response.hs+16−19
@@ -116,14 +116,14 @@ sendResponse settings conn ii th req reqidxhdr src response = do
116116 -- and status, the response to HEAD is processed here.
117117 --
118118 -- See definition of rsp below for proper body stripping.
119− (ms, mlen) <- sendRsp conn ii ver s hs rspidxhdr rsp
119+ (ms, mlen) <- sendRsp conn ii ver s hs rsp
120120 case ms of
121121 Nothing -> return ()
122122 Just realStatus -> logger req realStatus mlen
123123 T.tickle th
124124 return ret
125125 else do
126− _ <- sendRsp conn ii ver s hs rspidxhdr RspNoBody
126+ _ <- sendRsp conn ii ver s hs RspNoBody
127127 logger req s Nothing
128128 T.tickle th
129129 return isPersist
@@ -196,13 +196,12 @@ sendRsp :: Connection
196196 -> H.HttpVersion
197197 -> H.Status
198198 -> H.ResponseHeaders
199− -> IndexedHeader -- Response
200199 -> Rsp
201200 -> IO (Maybe H.Status, Maybe Integer)
202201
203202----------------------------------------------------------------
204203
205−sendRsp conn _ ver s hs _ RspNoBody = do
204+sendRsp conn _ ver s hs RspNoBody = do
206205 -- Not adding Content-Length.
207206 -- User agents treats it as Content-Length: 0.
208207 composeHeader ver s hs >>= connSendAll conn
@@ -210,7 +209,7 @@ sendRsp conn _ ver s hs _ RspNoBody = do
210209
211210----------------------------------------------------------------
212211
213−sendRsp conn _ ver s hs _ (RspBuilder body needsChunked) = do
212+sendRsp conn _ ver s hs (RspBuilder body needsChunked) = do
214213 header <- composeHeaderBuilder ver s hs needsChunked
215214 let hdrBdy
216215 | needsChunked = header <> chunkedTransferEncoding body
@@ -223,7 +222,7 @@ sendRsp conn _ ver s hs _ (RspBuilder body needsChunked) = do
223222
224223----------------------------------------------------------------
225224
226−sendRsp conn _ ver s hs _ (RspStream streamingBody needsChunked th) = do
225+sendRsp conn _ ver s hs (RspStream streamingBody needsChunked th) = do
227226 header <- composeHeaderBuilder ver s hs needsChunked
228227 (recv, finish) <- newByteStringBuilderRecv $ reuseBufferStrategy
229228 $ toBuilderBuffer (connWriteBuffer conn) (connBufferSize conn)
@@ -247,7 +246,7 @@ sendRsp conn _ ver s hs _ (RspStream streamingBody needsChunked th) = do
247246
248247----------------------------------------------------------------
249248
250−sendRsp conn _ _ _ _ _ (RspRaw withApp src tickle) = do
249+sendRsp conn _ _ _ _ (RspRaw withApp src tickle) = do
251250 withApp recv send
252251 return (Nothing, Nothing)
253252 where
@@ -261,8 +260,8 @@ sendRsp conn _ _ _ _ _ (RspRaw withApp src tickle) = do
261260
262261-- Sophisticated WAI applications.
263262-- We respect s0. s0 MUST be a proper value.
264−sendRsp conn ii ver s0 hs0 rspidxhdr (RspFile path (Just part) _ isHead hook) =
265− sendRspFile2XX conn ii ver s0 hs rspidxhdr path beg len isHead hook
263+sendRsp conn ii ver s0 hs0 (RspFile path (Just part) _ isHead hook) =
264+ sendRspFile2XX conn ii ver s0 hs path beg len isHead hook
266265 where
267266 beg = filePartOffset part
268267 len = filePartByteCount part
@@ -272,17 +271,17 @@ sendRsp conn ii ver s0 hs0 rspidxhdr (RspFile path (Just part) _ isHead hook) =
272271
273272-- Simple WAI applications.
274273-- Status is ignored
275−sendRsp conn ii ver _ hs0 rspidxhdr (RspFile path Nothing reqidxhdr isHead hook) = do
274+sendRsp conn ii ver _ hs0 (RspFile path Nothing idxhdr isHead hook) = do
276275 efinfo <- E.try $ getFileInfo ii path
277276 case efinfo of
278277 Left (_ex :: E.IOException) ->
279278#ifdef WARP_DEBUG
280279 print _ex >>
281280#endif
282− sendRspFile404 conn ii ver hs0 rspidxhdr
283− Right finfo -> case conditionalRequest finfo hs0 rspidxhdr reqidxhdr of
284− WithoutBody s -> sendRsp conn ii ver s hs0 rspidxhdr RspNoBody
285− WithBody s hs beg len -> sendRspFile2XX conn ii ver s hs rspidxhdr path beg len isHead hook
281+ sendRspFile404 conn ii ver hs0
282+ Right finfo -> case conditionalRequest finfo hs0 idxhdr of
283+ WithoutBody s -> sendRsp conn ii ver s hs0 RspNoBody
284+ WithBody s hs beg len -> sendRspFile2XX conn ii ver s hs path beg len isHead hook
286285
287286----------------------------------------------------------------
288287
@@ -291,15 +290,14 @@ sendRspFile2XX :: Connection
291290 -> H.HttpVersion
292291 -> H.Status
293292 -> H.ResponseHeaders
294− -> IndexedHeader
295293 -> FilePath
296294 -> Integer
297295 -> Integer
298296 -> Bool
299297 -> IO ()
300298 -> IO (Maybe H.Status, Maybe Integer)
301−sendRspFile2XX conn ii ver s hs rspidxhdr path beg len isHead hook
302− | isHead = sendRsp conn ii ver s hs rspidxhdr RspNoBody
299+sendRspFile2XX conn ii ver s hs path beg len isHead hook
300+ | isHead = sendRsp conn ii ver s hs RspNoBody
303301 | otherwise = do
304302 lheader <- composeHeader ver s hs
305303 (mfd, fresher) <- getFd ii path
@@ -312,9 +310,8 @@ sendRspFile404 :: Connection
312310 -> InternalInfo
313311 -> H.HttpVersion
314312 -> H.ResponseHeaders
315− -> IndexedHeader
316313 -> IO (Maybe H.Status, Maybe Integer)
317−sendRspFile404 conn ii ver hs0 rspidxhdr = sendRsp conn ii ver s hs rspidxhdr (RspBuilder body True)
314+sendRspFile404 conn ii ver hs0 = sendRsp conn ii ver s hs (RspBuilder body True)
318315 where
319316 s = H.notFound404
320317 hs = replaceHeader H.hContentType "text/plain; charset=utf-8" hs0
warp/Network/Wai/Handler/Warp/Run.hs+25−41
@@ -17,15 +17,12 @@ import qualified Control.Concurrent as Conc (yield)
1717import Control.Exception as E
1818import qualified Data.ByteString as S
1919import Data.Char (chr)
20−import Data.IORef (IORef, newIORef, readIORef, writeIORef)
20+import Data.IORef (IORef, newIORef, readIORef, writeIORef, atomicModifyIORef')
2121import Data.Streaming.Network (bindPortTCP)
2222import Foreign.C.Error (Errno(..), eCONNABORTED)
2323import GHC.IO.Exception (IOException(..))
2424import qualified Network.HTTP2 as H2
2525import Network.Socket (Socket, close, accept, withSocketsDo, SockAddr(SockAddrInet, SockAddrInet6), setSocketOption, SocketOption(..))
26−#if MIN_VERSION_network(3,1,1)
27−import Network.Socket (gracefulClose)
28−#endif
2926import qualified Network.Socket.ByteString as Sock
3027import Network.Wai
3128import Network.Wai.Internal (ResponseReceived (ResponseReceived))
@@ -58,38 +55,21 @@ import Network.Socket (fdSocket)
5855#endif
5956
6057-- | Creating 'Connection' for plain HTTP based on a given socket.
61−socketConnection :: Settings -> Socket -> IO Connection
62−#if MIN_VERSION_network(3,1,1)
63−socketConnection set s = do
64−#else
65−socketConnection _ s = do
66−#endif
58+socketConnection :: Socket -> IO Connection
59+socketConnection s = do
6760 bufferPool <- newBufferPool
6861 writeBuf <- allocateBuffer bufferSize
6962 let sendall = Sock.sendAll s
70− isH2 <- newIORef False -- HTTP/1.x
7163 return Connection {
7264 connSendMany = Sock.sendMany s
7365 , connSendAll = sendall
7466 , connSendFile = sendFile s writeBuf bufferSize sendall
75−#if MIN_VERSION_network(3,1,1)
76− , connClose = do
77− h2 <- readIORef isH2
78− let tm = if h2 then settingsGracefulCloseTimeout2 set
79− else settingsGracefulCloseTimeout1 set
80− if tm == 0 then
81− close s
82− else
83− gracefulClose s tm `E.catch` \(E.SomeException _) -> return ()
84−#else
8567 , connClose = close s
86−#endif
8768 , connFree = freeBuffer writeBuf
8869 , connRecv = receive s bufferPool
8970 , connRecvBuf = receiveBuf s
9071 , connWriteBuffer = writeBuf
9172 , connBufferSize = bufferSize
92− , connHTTP2 = isH2
9373 }
9474
9575-- | Run an 'Application' on the given port.
@@ -151,7 +131,7 @@ runSettingsSocket set socket app = do
151131 setSocketCloseOnExec s
152132 -- NoDelay causes an error for AF_UNIX.
153133 setSocketOption s NoDelay 1 `E.catch` \(E.SomeException _) -> return ()
154− conn <- socketConnection set s
134+ conn <- socketConnection s
155135 return (conn, sa)
156136
157137 closeListenSocket = close socket
@@ -286,7 +266,10 @@ fork :: Settings
286266fork set mkConn addr app counter ii = settingsFork set $ \unmask ->
287267 -- Call the user-supplied on exception code if any
288268 -- exceptions are thrown.
289− handle (settingsOnException set Nothing) $
269+ handle (settingsOnException set Nothing) .
270+ -- Allocate a new IORef indicating whether the connection has been
271+ -- closed, to avoid double-freeing a connection
272+ withClosedRef $ \ref ->
290273 -- Run the connection maker to get a new connection, and ensure
291274 -- that the connection is closed. If the mkConn call throws an
292275 -- exception, we will leak the connection. If the mkConn call is
@@ -297,13 +280,21 @@ fork set mkConn addr app counter ii = settingsFork set $ \unmask ->
297280 -- We grab the connection before registering timeouts since the
298281 -- timeouts will be useless during connection creation, due to the
299282 -- fact that async exceptions are still masked.
300− bracket mkConn cleanUp (serve unmask)
283+ bracket mkConn (cleanUp ref) (serve unmask ref)
301284 where
302− cleanUp (conn, _) = connClose conn `finally` connFree conn
285+ withClosedRef inner = newIORef False >>= inner
286+
287+ closeConn ref conn = do
288+ isClosed <- atomicModifyIORef' ref $ \x -> (True, x)
289+ unless isClosed $ connClose conn
290+
291+ cleanUp ref (conn, _) = closeConn ref conn `finally` connFree conn
303292
304293 -- We need to register a timeout handler for this thread, and
305− -- cancel that handler as soon as we exit.
306− serve unmask (conn, transport) = bracket register cancel $ \th -> do
294+ -- cancel that handler as soon as we exit. We additionally close
295+ -- the connection immediately in case the child thread catches the
296+ -- async exception or performs some long-running cleanup action.
297+ serve unmask ref (conn, transport) = bracket register cancel $ \th -> do
307298 -- We now have fully registered a connection close handler in
308299 -- the case of all exceptions, so it is safe to once again
309300 -- allow async exceptions.
@@ -315,7 +306,8 @@ fork set mkConn addr app counter ii = settingsFork set $ \unmask ->
315306 -- above ensures the connection is closed.
316307 when goingon $ serveConnection conn ii th addr transport set app
317308 where
318− register = T.registerKillThread (timeoutManager ii) (connClose conn)
309+ register = T.registerKillThread (timeoutManager ii)
310+ (closeConn ref conn)
319311 cancel = T.cancel
320312
321313 onOpen adr = increase counter >> settingsOnOpen set adr
@@ -342,19 +334,10 @@ serveConnection conn ii th origAddr transport settings app = do
342334 istatus <- newIORef False
343335 if settingsHTTP2Enabled settings && h2 then do
344336 rawRecvN <- makeReceiveN bs (connRecv conn) (connRecvBuf conn)
345− -- This thread becomes the sender in http2 library.
346− -- In the case of event source, one request comes and one
347− -- worker gets busy. But it is likely that the receiver does
348− -- not receive any data at all while the sender is sending
349− -- output data from the worker. It's not good enough to tickle
350− -- the time handler in the receiver only. So, we should tickle
351− -- the time handler in both the receiver and the sender.
352337 let recvN = wrappedRecvN th istatus (settingsSlowlorisSize settings) rawRecvN
353− sendBS x = connSendAll conn x >> T.tickle th
354338 -- fixme: origAddr
355339 checkTLS
356− setConnHTTP2 conn True
357− http2 conn transport ii origAddr settings recvN sendBS app
340+ http2 conn ii origAddr settings recvN app
358341 else do
359342 src <- mkSource (wrappedRecv conn th istatus (settingsSlowlorisSize settings))
360343 writeIORef istatus True
@@ -429,7 +412,8 @@ serveConnection conn ii th origAddr transport settings app = do
429412 errorResponse e = settingsOnExceptionResponse settings e
430413
431414 http1 firstRequest addr istatus src = do
432− (req, mremainingRef, idxhdr, nextBodyFlush) <- recvRequest firstRequest settings conn ii th addr src transport
415+ (req', mremainingRef, idxhdr, nextBodyFlush) <- recvRequest firstRequest settings conn ii th addr src
416+ let req = req' { isSecure = isTransportSecure transport }
433417 keepAlive <- processRequest istatus src req mremainingRef idxhdr nextBodyFlush
434418 `E.catch` \e -> do
435419 settingsOnException settings (Just req) e
warp/Network/Wai/Handler/Warp/Settings.hs+5−31
@@ -76,13 +76,6 @@ data Settings = Settings
7676 --
7777 -- Since 2.0.3
7878 , settingsInstallShutdownHandler :: IO () -> IO ()
79− -- ^ An action to install a handler (e.g. Unix signal handler)
80− -- to close a listen socket.
81− -- The first argument is an action to close the listen socket.
82− --
83− -- Default: no action
84− --
85− -- Since 3.0.1
8679 , settingsServerName :: ByteString
8780 -- ^ Default server name if application does not set one.
8881 --
@@ -94,44 +87,28 @@ data Settings = Settings
9487 , settingsProxyProtocol :: ProxyProtocol
9588 -- ^ Specify usage of the PROXY protocol.
9689 --
97− -- Since 3.0.5
90+ -- Since 3.0.5.
9891 , settingsSlowlorisSize :: Int
9992 -- ^ Size of bytes read to prevent Slowloris protection. Default value: 2048
10093 --
101− -- Since 3.1.2
94+ -- Since 3.1.2.
10295 , settingsHTTP2Enabled :: Bool
10396 -- ^ Whether to enable HTTP2 ALPN/upgrades. Default: True
10497 --
105− -- Since 3.1.7
98+ -- Since 3.1.7.
10699 , settingsLogger :: Request -> H.Status -> Maybe Integer -> IO ()
107100 -- ^ A log function. Default: no action.
108101 --
109− -- Since 3.1.10
102+ -- Since 3.X.X.
110103 , settingsServerPushLogger :: Request -> ByteString -> Integer -> IO ()
111104 -- ^ A HTTP/2 server push log function. Default: no action.
112105 --
113− -- Since 3.2.7
106+ -- Since 3.X.X.
114107 , settingsGracefulShutdownTimeout :: Maybe Int
115108 -- ^ An optional timeout to limit the time (in seconds) waiting for
116109 -- a graceful shutdown of the web server.
117110 --
118111 -- Since 3.2.8
119− , settingsGracefulCloseTimeout1 :: Int
120− -- ^ A timeout to limit the time (in milliseconds) waiting for
121− -- FIN for HTTP/1.x. 0 means uses immediate close.
122− -- Default: 0.
123− --
124− -- Since 3.3.5
125− , settingsGracefulCloseTimeout2 :: Int
126− -- ^ A timeout to limit the time (in milliseconds) waiting for
127− -- FIN for HTTP/2. 0 means uses immediate close.
128− -- Default: 2000.
129− --
130− -- Since 3.3.5
131− , settingsMaxTotalHeaderLength :: Int
132− -- ^ Determines the maximum header size that Warp will tolerate when using HTTP/1.x.
133− --
134− -- Since 3.3.8
135112 }
136113
137114-- | Specify usage of the PROXY protocol.
@@ -168,9 +145,6 @@ defaultSettings = Settings
168145 , settingsLogger = \_ _ _ -> return ()
169146 , settingsServerPushLogger = \_ _ _ -> return ()
170147 , settingsGracefulShutdownTimeout = Nothing
171− , settingsGracefulCloseTimeout1 = 0
172− , settingsGracefulCloseTimeout2 = 2000
173− , settingsMaxTotalHeaderLength = 50 * 1024
174148 }
175149
176150-- | Apply the logic provided by 'defaultOnException' to determine if an
warp/Network/Wai/Handler/Warp/Types.hs−10
@@ -8,7 +8,6 @@ import Control.Exception
88import qualified Data.ByteString as S
99import Data.IORef (IORef, readIORef, writeIORef, newIORef)
1010import Data.Typeable (Typeable)
11−import Data.X509
1211import Foreign.Ptr (Ptr)
1312import System.Posix.Types (Fd)
1413import qualified System.TimeManager as T
@@ -123,16 +122,8 @@ data Connection = Connection {
123122 , connWriteBuffer :: Buffer
124123 -- | The size of the write buffer.
125124 , connBufferSize :: BufSize
126− -- | Is this connection HTTP/2?
127− , connHTTP2 :: IORef Bool
128125 }
129126
130−getConnHTTP2 :: Connection -> IO Bool
131−getConnHTTP2 conn = readIORef (connHTTP2 conn)
132−
133−setConnHTTP2 :: Connection -> Bool -> IO ()
134−setConnHTTP2 conn b = writeIORef (connHTTP2 conn) b
135−
136127----------------------------------------------------------------
137128
138129data InternalInfo = InternalInfo {
@@ -180,7 +171,6 @@ data Transport = TCP -- ^ Plain channel: TCP
180171 , tlsMinorVersion :: Int
181172 , tlsNegotiatedProtocol :: Maybe ByteString -- ^ The result of Application Layer Protocol Negociation in RFC 7301
182173 , tlsChiperID :: Word16
183− , tlsClientCertificate :: Maybe CertificateChain
184174 } -- ^ Encrypted channel: TLS or SSL
185175
186176isTransportSecure :: Transport -> Bool
warp/test/FileSpec.hs+1−1
@@ -21,7 +21,7 @@ testFileRange desc reqhs file ans = it desc $ do
2121 let WithBody s hs off len = ans
2222 hs' = ("Last-Modified",fileInfoDate finfo) : hs
2323 ans' = WithBody s hs' off len
24− conditionalRequest finfo [] (indexResponseHeader hs) (indexRequestHeader reqhs) `shouldBe` ans'
24+ conditionalRequest finfo [] (indexRequestHeader reqhs) `shouldBe` ans'
2525
2626spec :: Spec
2727spec = do
warp/test/RequestSpec.hs+7−11
@@ -7,7 +7,6 @@ module RequestSpec (main, spec) where
77import Network.Wai.Handler.Warp.File (parseByteRanges)
88import Network.Wai.Handler.Warp.Request
99import Network.Wai.Handler.Warp.Types
10−import Network.Wai.Handler.Warp.Settings (settingsMaxTotalHeaderLength, defaultSettings)
1110import Test.Hspec
1211import Test.Hspec.QuickCheck
1312import qualified Data.ByteString as S
@@ -18,9 +17,6 @@ import Data.IORef
1817main :: IO ()
1918main = hspec spec
2019
21−defaultMaxTotalHeaderLength :: Int
22−defaultMaxTotalHeaderLength = settingsMaxTotalHeaderLength defaultSettings
23−
2420spec :: Spec
2521spec = do
2622 describe "headerLines" $ do
@@ -67,29 +63,29 @@ spec = do
6763 describe "headerLines" $ do
6864 it "can handle a nomarl case" $ do
6965 src <- mkSourceFunc ["Status: 200\r\nContent-Type: text/plain\r\n\r\n"] >>= mkSource
70− x <- headerLines defaultMaxTotalHeaderLength True src
66+ x <- headerLines True src
7167 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
7268
7369 it "can handle a nasty case (1)" $ do
7470 src <- mkSourceFunc ["Status: 200", "\r\nContent-Type: text/plain", "\r\n\r\n"] >>= mkSource
75− x <- headerLines defaultMaxTotalHeaderLength True src
71+ x <- headerLines True src
7672 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
7773
7874 it "can handle a nasty case (1)" $ do
7975 src <- mkSourceFunc ["Status: 200", "\r", "\nContent-Type: text/plain", "\r", "\n\r\n"] >>= mkSource
80− x <- headerLines defaultMaxTotalHeaderLength True src
76+ x <- headerLines True src
8177 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
8278
8379 it "can handle a nasty case (1)" $ do
8480 src <- mkSourceFunc ["Status: 200", "\r", "\n", "Content-Type: text/plain", "\r", "\n", "\r", "\n"] >>= mkSource
85− x <- headerLines defaultMaxTotalHeaderLength True src
81+ x <- headerLines True src
8682 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
8783
8884 it "can handle an illegal case (1)" $ do
8985 src <- mkSourceFunc ["\nStatus:", "\n 200", "\nContent-Type: text/plain", "\r\n\r\n"] >>= mkSource
90− x <- headerLines defaultMaxTotalHeaderLength True src
86+ x <- headerLines True src
9187 x `shouldBe` []
92− y <- headerLines defaultMaxTotalHeaderLength True src
88+ y <- headerLines True src
9389 y `shouldBe` ["Status: 200", "Content-Type: text/plain"]
9490
9591 where
@@ -115,7 +111,7 @@ headerLinesList' orig = do
115111 writeIORef ref z
116112 return y
117113 src' <- mkSource src
118− res <- headerLines defaultMaxTotalHeaderLength True src'
114+ res <- headerLines True src'
119115 return (res, src')
120116
121117consumeLen :: Int -> Source -> IO S8.ByteString
warp/test/ResponseSpec.hs+15−11
@@ -10,7 +10,7 @@ import Network.HTTP.Types
1010import Network.Wai hiding (responseHeaders)
1111import Network.Wai.Handler.Warp
1212import Network.Wai.Handler.Warp.Response
13−import RunSpec (withApp, msWrite, msRead, withMySocket)
13+import RunSpec (withApp, msClose, msWrite, msRead, connectTo)
1414import Test.Hspec
1515
1616main :: IO ()
@@ -20,13 +20,15 @@ testRange :: S.ByteString -- ^ range value
2020 -> String -- ^ expected output
2121 -> Maybe String -- ^ expected content-range value
2222 -> Spec
23−testRange range out crange = it title $ withApp defaultSettings app $ withMySocket $ \ms -> do
24− msWrite ms "GET / HTTP/1.0\r\n"
25− msWrite ms "Range: bytes="
26− msWrite ms range
27− msWrite ms "\r\n\r\n"
23+testRange range out crange = it title $ withApp defaultSettings app $ \port -> do
24+ handle <- connectTo port
25+ msWrite handle "GET / HTTP/1.0\r\n"
26+ msWrite handle "Range: bytes="
27+ msWrite handle range
28+ msWrite handle "\r\n\r\n"
2829 threadDelay 10000
29− bss <- fmap (lines . filter (/= '\r') . S8.unpack) $ msRead ms 1024
30+ bss <- fmap (lines . filter (/= '\r') . S8.unpack) $ msRead handle 1024
31+ msClose handle
3032 last bss `shouldBe` out
3133 let hs = mapMaybe toHeader bss
3234 lookup "Content-Range" hs `shouldBe` fmap ("bytes " ++) crange
@@ -44,10 +46,12 @@ testPartial :: Integer -- ^ file size
4446 -> Integer -- ^ byte count
4547 -> String -- ^ expected output
4648 -> Spec
47−testPartial size offset count out = it title $ withApp defaultSettings app $ withMySocket $ \ms -> do
48− msWrite ms "GET / HTTP/1.0\r\n\r\n"
49+testPartial size offset count out = it title $ withApp defaultSettings app $ \port -> do
50+ handle <- connectTo port
51+ msWrite handle "GET / HTTP/1.0\r\n\r\n"
4952 threadDelay 10000
50− bss <- fmap (lines . filter (/= '\r') . S8.unpack) $ msRead ms 1024
53+ bss <- fmap (lines . filter (/= '\r') . S8.unpack) $ msRead handle 1024
54+ msClose handle
5155 out `shouldBe` last bss
5256 let hs = mapMaybe toHeader bss
5357 lookup "Content-Length" hs `shouldBe` Just (show $ length $ last bss)
@@ -83,7 +87,7 @@ spec = do
8387 it "discards empty lines" $ do
8488 sanitizeHeaderValue "foo\r\n\r\nbar" `shouldBe` "foo\r\n bar"
8589
86− context "when sanitizing single occurrences of \n" $ do
90+ context "when sanitizing single occurences of \n" $ do
8791 it "replaces \n with \r\n" $ do
8892 sanitizeHeaderValue "foo\n bar" `shouldBe` "foo\r\n bar"
8993
warp/test/RunSpec.hs+47−38
@@ -1,7 +1,7 @@
11{-# LANGUAGE OverloadedStrings #-}
22{-# LANGUAGE ScopedTypeVariables #-}
33
4−module RunSpec (main, spec, withApp, MySocket, msWrite, msRead, withMySocket) where
4+module RunSpec (main, spec, withApp, connectTo, MySocket, msWrite, msRead, msClose) where
55
66import Control.Concurrent (forkIO, killThread, threadDelay)
77import Control.Concurrent.MVar (newEmptyMVar, takeMVar, putMVar)
@@ -37,7 +37,7 @@ type CounterApplication = Counter -> Application
3737
3838data MySocket = MySocket
3939 { msSocket :: !Socket
40− , msBuffer :: !(I.IORef ByteString)
40+ , _msBuffer :: !(I.IORef ByteString)
4141 }
4242
4343msWrite :: MySocket -> ByteString -> IO ()
@@ -70,16 +70,9 @@ msClose :: MySocket -> IO ()
7070msClose = Network.Socket.close . msSocket
7171
7272connectTo :: Int -> IO MySocket
73−connectTo port = do
74− s <- fst <$> getSocketTCP "127.0.0.1" port
75− ref <- I.newIORef mempty
76− return MySocket {
77− msSocket = s
78− , msBuffer = ref
79− }
80−
81−withMySocket :: (MySocket -> IO a) -> Int -> IO a
82−withMySocket body port = bracket (connectTo port) msClose body
73+connectTo port = MySocket
74+ <$> (fst <$> getSocketTCP "127.0.0.1" port)
75+ <*> I.newIORef mempty
8376
8477incr :: MonadIO m => Counter -> m ()
8578incr icount = liftIO $ I.atomicModifyIORef icount $ \ecount ->
@@ -157,7 +150,8 @@ runTest :: Int -- ^ expected number of requests
157150 -> IO ()
158151runTest expected app chunks = do
159152 ref <- I.newIORef (Right 0)
160− withApp defaultSettings (app ref) $ withMySocket $ \ms -> do
153+ withApp defaultSettings (app ref) $ \port -> do
154+ ms <- connectTo port
161155 forM_ chunks $ \chunk -> msWrite ms chunk
162156 _ <- timeout 100000 $ replicateM_ expected $ msRead ms 4096
163157 res <- I.readIORef ref
@@ -174,9 +168,10 @@ runTerminateTest :: InvalidRequest
174168runTerminateTest expected input = do
175169 ref <- I.newIORef Nothing
176170 let onExc _ = I.writeIORef ref . Just
177− withApp (setOnException onExc defaultSettings) dummyApp $ withMySocket $ \ms -> do
178− msWrite ms input
179− msClose ms -- explicitly
171+ withApp (setOnException onExc defaultSettings) dummyApp $ \port -> do
172+ handle <- connectTo port
173+ msWrite handle input
174+ msClose handle
180175 threadDelay 1000
181176 res <- I.readIORef ref
182177 show res `shouldBe` show (Just expected)
@@ -247,11 +242,13 @@ spec = do
247242 let app req f = do
248243 liftIO $ I.writeIORef iheaders $ requestHeaders req
249244 f $ responseLBS status200 [] ""
250− withApp defaultSettings app $ withMySocket $ \ms -> do
245+ withApp defaultSettings app $ \port -> do
246+ handle <- connectTo port
251247 let input = S.concat
252248 [ "GET / HTTP/1.1\r\nfoo: bar\r\n baz\r\n\tbin\r\n\r\n"
253249 ]
254− msWrite ms input
250+ msWrite handle input
251+ msClose handle
255252 threadDelay 1000
256253 headers <- I.readIORef iheaders
257254 headers `shouldBe`
@@ -262,11 +259,13 @@ spec = do
262259 let app req f = do
263260 liftIO $ I.writeIORef iheaders $ requestHeaders req
264261 f $ responseLBS status200 [] ""
265− withApp defaultSettings app $ withMySocket $ \ms -> do
262+ withApp defaultSettings app $ \port -> do
263+ handle <- connectTo port
266264 let input = S.concat
267265 [ "GET / HTTP/1.1\r\nfoo:bar\r\n\r\n"
268266 ]
269− msWrite ms input
267+ msWrite handle input
268+ msClose handle
270269 threadDelay 1000
271270 headers <- I.readIORef iheaders
272271 headers `shouldBe`
@@ -282,14 +281,16 @@ spec = do
282281 liftIO $ I.atomicModifyIORef ifront $ \front -> (front . (S.concat bss:), ())
283282 atomically $ modifyTVar countVar (+ 1)
284283 f $ responseLBS status200 [] ""
285− withApp defaultSettings app $ withMySocket $ \ms -> do
284+ withApp defaultSettings app $ \port -> do
285+ handle <- connectTo port
286286 let input = S.concat
287287 [ "POST / HTTP/1.1\r\nTransfer-Encoding: Chunked\r\n\r\n"
288288 , "c\r\nHello World\n\r\n3\r\nBye\r\n0\r\n\r\n"
289289 , "POST / HTTP/1.1\r\nTransfer-Encoding: Chunked\r\n\r\n"
290290 , "b\r\nHello World\r\n0\r\n\r\n"
291291 ]
292− msWrite ms input
292+ msWrite handle input
293+ msClose handle
293294 atomically $ do
294295 count <- readTVar countVar
295296 check $ count == 2
@@ -306,12 +307,14 @@ spec = do
306307 I.atomicModifyIORef ifront $ \front -> (front . (S.concat bss:), ())
307308 atomically $ modifyTVar countVar (+ 1)
308309 f $ responseLBS status200 [] ""
309− withApp defaultSettings app $ withMySocket $ \ms -> do
310+ withApp defaultSettings app $ \port -> do
311+ handle <- connectTo port
310312 let input = concat $ replicate 2 $
311313 ["POST / HTTP/1.1\r\nTransfer-Encoding: Chunked\r\n\r\n"] ++
312314 (replicate 50 "5\r\n12345\r\n") ++
313315 ["0\r\n\r\n"]
314− mapM_ (msWrite ms) input
316+ mapM_ (msWrite handle) input
317+ msClose handle
315318 atomically $ do
316319 count <- readTVar countVar
317320 check $ count == 2
@@ -327,14 +330,16 @@ spec = do
327330 liftIO $ I.atomicModifyIORef ifront $ \front -> (front . (S.concat bss:), ())
328331 atomically $ modifyTVar countVar (+ 1)
329332 f $ responseLBS status200 [] ""
330− withApp defaultSettings app $ withMySocket $ \ms -> do
333+ withApp defaultSettings app $ \port -> do
334+ handle <- connectTo port
331335 let input = S.concat
332336 [ "POST / HTTP/1.1\r\nTransfer-Encoding: Chunked\r\n\r\n"
333337 , "c\r\nHello World\n\r\n3\r\nBye\r\n0\r\n"
334338 , "POST / HTTP/1.1\r\nTransfer-Encoding: Chunked\r\n\r\n"
335339 , "b\r\nHello World\r\n0\r\n\r\n"
336340 ]
337− mapM_ (msWrite ms) $ map S.singleton $ S.unpack input
341+ mapM_ (msWrite handle) $ map S.singleton $ S.unpack input
342+ msClose handle
338343 atomically $ do
339344 count <- readTVar countVar
340345 check $ count == 2
@@ -355,18 +360,20 @@ spec = do
355360 E.throwIO (e :: E.SomeException)
356361 liftIO $ I.atomicModifyIORef ifront $ \front -> (front . (S.concat bss:), ())
357362 f $ responseLBS status200 [] ""
358− withApp (setTimeout 1 defaultSettings) app $ withMySocket $ \ms -> do
363+ withApp (setTimeout 1 defaultSettings) app $ \port -> do
359364 let bs1 = S.replicate 2048 88
360365 bs2 = "This is short"
361366 bs = S.append bs1 bs2
362− msWrite ms "POST / HTTP/1.1\r\n"
363− msWrite ms "content-length: "
364− msWrite ms $ S8.pack $ show $ S.length bs
365− msWrite ms "\r\n\r\n"
367+ handle <- connectTo port
368+ msWrite handle "POST / HTTP/1.1\r\n"
369+ msWrite handle "content-length: "
370+ msWrite handle $ S8.pack $ show $ S.length bs
371+ msWrite handle "\r\n\r\n"
366372 threadDelay 100000
367− msWrite ms bs1
373+ msWrite handle bs1
368374 threadDelay 100000
369− msWrite ms bs2
375+ msWrite handle bs2
376+ msClose handle
370377 threadDelay 5000000
371378 front <- I.readIORef ifront
372379 S.concat (front []) `shouldBe` bs
@@ -382,7 +389,8 @@ spec = do
382389 loop
383390 loop
384391 doubleBS = S.concatMap $ \w -> S.pack [w, w]
385− withApp defaultSettings app $ withMySocket $ \ms -> do
392+ withApp defaultSettings app $ \port -> do
393+ ms <- connectTo port
386394 msWrite ms "POST / HTTP/1.1\r\n\r\n12345"
387395 timeout 100000 (msRead ms 10) >>= (`shouldBe` Just "1122334455")
388396 msWrite ms "67890"
@@ -410,14 +418,15 @@ spec = do
410418 atomically $ modifyTVar countVar (+ 1)
411419 loop
412420 loop
413− withApp defaultSettings app $ withMySocket $ \ms -> do
414− msWrite ms "POST / HTTP/1.1\r\ntransfer-encoding: chunked\r\n\r\n"
421+ withApp defaultSettings app $ \port -> do
422+ (sock, _addr) <- getSocketTCP "127.0.0.1" port
423+ sendAll sock "POST / HTTP/1.1\r\ntransfer-encoding: chunked\r\n\r\n"
415424 threadDelay 10000
416− msWrite ms "5\r\nhello\r\n0\r\n\r\n"
425+ sendAll sock "5\r\nhello\r\n0\r\n\r\n"
417426 atomically $ do
418427 count <- readTVar countVar
419428 check $ count >= 1
420− bs <- safeRecv (msSocket ms) 4096 -- must not use msRead
429+ bs <- safeRecv sock 4096
421430 S.takeWhile (/= 13) bs `shouldBe` "HTTP/1.1 200 OK"
422431
423432 it "streaming response with length" $ do
warp/warp.cabal+1−4
@@ -1,5 +1,5 @@
11Name: warp
2−Version: 3.3.9
2+Version: 3.3.0
33Synopsis: A fast, light-weight web server for WAI applications.
44License: MIT
55License-file: LICENSE
@@ -56,7 +56,6 @@ Library
5656 , vault >= 0.3
5757 , wai >= 3.2 && < 3.3
5858 , word8
59− , x509
6059 if impl(ghc < 8)
6160 Build-Depends: semigroups
6261 if flag(network-bytestring)
@@ -212,7 +211,6 @@ Test-Suite spec
212211 , vault
213212 , wai >= 3.2 && < 3.3
214213 , word8
215− , x509
216214 -- Build-Tool-Depends: hspec-discover:hspec-discover
217215 if impl(ghc < 8)
218216 Build-Depends: semigroups
@@ -251,7 +249,6 @@ Benchmark parser
251249 , network
252250 , time-manager
253251 , unix-compat
254− , x509
255252 if impl(ghc < 8)
256253 Build-Depends: semigroups
257254