on e235cd3 help sign in
.azure/azure-linux-template.yml+12−6
since splitsnapshots
unifiedsplit
@@ -5,6 +5,12 @@ jobs:
5 vmImage: ${{ parameters.vmImage }}
5 vmImage: ${{ parameters.vmImage }}
6 strategy:
6 strategy:
7 matrix:
7 matrix:
8+ stack-def:
9+ BUILD: stack
10+ stack-lts-9:
11+ BUILD: stack
12+ STACK_YAML: stack-lts-9.yaml
13+ ARGS: "--resolver lts-9"
8 stack-lts-11:
14 stack-lts-11:
9 BUILD: stack
15 BUILD: stack
10 STACK_YAML: stack-lts-11.yaml
16 STACK_YAML: stack-lts-11.yaml
@@ -16,10 +22,10 @@ jobs:
16 stack-lts-14:
22 stack-lts-14:
17 BUILD: stack
23 BUILD: stack
18 ARGS: "--resolver lts-14"
24 ARGS: "--resolver lts-14"
19− stack-nightly:
25+ cabal-8.0.2:
20− BUILD: stack
26+ BUILD: cabal
21− STACK_YAML: stack-nightly.yaml
27+ GHCVER: 8.0.2
22− ARGS: "--resolver nightly"
28+ CABALVER: 1.24
23 cabal-8.2.2:
29 cabal-8.2.2:
24 BUILD: cabal
30 BUILD: cabal
25 GHCVER: 8.4.4
31 GHCVER: 8.4.4
@@ -28,9 +34,9 @@ jobs:
28 BUILD: cabal
34 BUILD: cabal
29 GHCVER: 8.4.4
35 GHCVER: 8.4.4
30 CABALVER: 2.2
36 CABALVER: 2.2
31− cabal-8.6.5:
37+ cabal-8.6.4:
32 BUILD: cabal
38 BUILD: cabal
33− GHCVER: 8.6.5
39+ GHCVER: 8.6.4
34 CABALVER: 2.4
40 CABALVER: 2.4
35 maxParallel: 6
41 maxParallel: 6
36 steps:
42 steps:
.azure/azure-osx-template.yml+6
@@ -5,6 +5,12 @@ jobs:
5 vmImage: ${{ parameters.vmImage }}
5 vmImage: ${{ parameters.vmImage }}
6 strategy:
6 strategy:
7 matrix:
7 matrix:
8+ stack-def:
9+ BUILD: stack
10+ stack-lts-9:
11+ BUILD: stack
12+ STACK_YAML: stack-lts-9.yaml
13+ ARGS: "--resolver lts-9"
8 stack-lts-11:
14 stack-lts-11:
9 BUILD: stack
15 BUILD: stack
10 STACK_YAML: stack-lts-11.yaml
16 STACK_YAML: stack-lts-11.yaml
.azure/azure-windows-template.yml+1−2
@@ -16,8 +16,7 @@ jobs:
16 unzip -o /usr/bin/stack.zip -d /usr/bin/
16 unzip -o /usr/bin/stack.zip -d /usr/bin/
17 stack setup $ARGS > /dev/null
17 stack setup $ARGS > /dev/null
18 stack --install-ghc $ARGS test --bench --only-dependencies
18 stack --install-ghc $ARGS test --bench --only-dependencies
19− stack $ARGS test --bench --no-run-benchmarks --no-run-tests
19+ stack $ARGS test --bench --no-run-benchmarks
20− stack $ARGS test --bench --no-run-benchmarks --jobs 1 # non-parallel test execution
21 env:
20 env:
22 OS_NAME: ${{ parameters.os }}
21 OS_NAME: ${{ parameters.os }}
23 displayName: 'Installation ${{parameters.os}} & Test'
22 displayName: 'Installation ${{parameters.os}} & Test'
.gitignore−1
@@ -9,4 +9,3 @@ tarballs/
9cabal.sandbox.config
9cabal.sandbox.config
10.stack-work/
10.stack-work/
11*~
11*~
12−stack*.yaml.lock
stack-lts-11.yaml+6−1
@@ -18,10 +18,15 @@ extra-deps:
18- http2-2.0.0
18- http2-2.0.0
19- network-byte-order-0.1.1.0
19- network-byte-order-0.1.1.0
20- tls-1.5.3
20- tls-1.5.3
21−- tls-session-manager-0.0.4
22- x509-1.7.5
21- x509-1.7.5
23nix:
22nix:
24 enable: false
23 enable: false
25 packages:
24 packages:
26 - fcgi
25 - fcgi
27 - zlib
26 - zlib
27+image:
28+ containers:
29+ - base: "fpco/pid1"
30+ name: "yesodweb/warp"
31+ executables:
32+ - warp
stack-lts-12.yaml+6−1
@@ -18,9 +18,14 @@ nix:
18 packages:
18 packages:
19 - fcgi
19 - fcgi
20 - zlib
20 - zlib
21+image:
22+ containers:
23+ - base: "fpco/pid1"
24+ name: "yesodweb/warp"
25+ executables:
26+ - warp
21extra-deps:
27extra-deps:
22- http2-2.0.0
28- http2-2.0.0
23- network-byte-order-0.1.1.0
29- network-byte-order-0.1.1.0
24- tls-1.5.3
30- tls-1.5.3
25−- tls-session-manager-0.0.4
26- x509-1.7.5
31- x509-1.7.5
stack-lts-9.yamlnew file+36
@@ -0,0 +1,36 @@
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+- tls-1.5.3
22+- x509-1.7.5
23+- cryptonite-0.26
24+- basement-0.0.11
25+- memory-0.15.0
26+nix:
27+ enable: false
28+ packages:
29+ - fcgi
30+ - zlib
31+image:
32+ containers:
33+ - base: "fpco/pid1"
34+ name: "yesodweb/warp"
35+ executables:
36+ - 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−2
@@ -1,4 +1,4 @@
1−resolver: lts-14.22
1+resolver: lts-14.20
2packages:
2packages:
3- ./auto-update
3- ./auto-update
4- ./mime-types
4- ./mime-types
@@ -18,8 +18,13 @@ nix:
18 packages:
18 packages:
19 - fcgi
19 - fcgi
20 - zlib
20 - zlib
21+image:
22+ containers:
23+ - base: "fpco/pid1"
24+ name: "yesodweb/warp"
25+ executables:
26+ - warp
21extra-deps:
27extra-deps:
22- http2-2.0.0
28- http2-2.0.0
23- network-byte-order-0.1.1.0
29- network-byte-order-0.1.1.0
24- tls-1.5.3
30- 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−
11## 3.1.6.3
1## 3.1.6.3
12
2
13* The executable warp obeys `-h` option properly for host
3* 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
164 -- Didn't match, but we have a hash value. Send the file contents
164 -- Didn't match, but we have a hash value. Send the file contents
165 -- with an ETag header.
165 -- with an ETag header.
166 --
166 --
167− -- RFC7232 (HTTP 1.1):
167+ -- Note: It would be arguably better to next check
168− -- > A recipient MUST ignore If-Modified-Since if the request contains an
168+ -- if-modified-since and return a 304 if that indicates a match as
169− -- > If-None-Match header field; the condition in If-None-Match is
169+ -- well. However, the circumstances under which such a situation
170− -- > considered to be a more accurate replacement for the condition in
170+ -- could arise would be very anomalous, and should likely warrant a
171− -- > If-Modified-Since, and the two are only combined for the sake of
171+ -- new file being sent anyway.
172− -- > interoperating with older intermediaries that might not implement
173− -- > If-None-Match.
174 (Just hash, _) -> respond [("ETag", hash)]
172 (Just hash, _) -> respond [("ETag", hash)]
175
173
176 -- No hash value available, fall back to last modified support.
174 -- No hash value available, fall back to last modified support.
@@ -245,7 +243,7 @@ staticAppPieces ss rawPieces req sendResponse = liftIO $ do
245 response :: StaticResponse -> IO W.ResponseReceived
243 response :: StaticResponse -> IO W.ResponseReceived
246 response (FileResponse file ch) = do
244 response (FileResponse file ch) = do
247 mimetype <- ssGetMimeType ss file
245 mimetype <- ssGetMimeType ss file
248− -- let filesize = fileGetSize file
246+ let filesize = fileGetSize file
249 let headers = ("Content-Type", mimetype)
247 let headers = ("Content-Type", mimetype)
250 -- Let Warp provide the content-length, since it takes
248 -- Let Warp provide the content-length, since it takes
251 -- range requests into account
249 -- range requests into account
@@ -269,13 +267,13 @@ staticAppPieces ss rawPieces req sendResponse = liftIO $ do
269 Just hash -> replace "etag" (Just hash) (W.queryString req)
267 Just hash -> replace "etag" (Just hash) (W.queryString req)
270 Nothing -> remove "etag" (W.queryString req)
268 Nothing -> remove "etag" (W.queryString req)
271
269
272− sendResponse $ W.responseLBS H.status302
270+ sendResponse $ W.responseLBS H.status301
273 [ ("Content-Type", "text/plain")
271 [ ("Content-Type", "text/plain")
274 , ("Location", S8.append loc $ H.renderQuery True qString)
272 , ("Location", S8.append loc $ H.renderQuery True qString)
275 ] "Redirect"
273 ] "Redirect"
276
274
277 response (RawRedirect path) =
275 response (RawRedirect path) =
278− sendResponse $ W.responseLBS H.status302
276+ sendResponse $ W.responseLBS H.status301
279 [ ("Content-Type", "text/plain")
277 [ ("Content-Type", "text/plain")
280 , ("Location", path)
278 , ("Location", path)
281 ] "Redirect"
279 ] "Redirect"
wai-app-static/WaiAppStatic/CmdLine.hs−1
@@ -39,7 +39,6 @@ data Args = Args
39 }
39 }
40
40
41#if MIN_VERSION_optparse_applicative(0, 10, 0)
41#if MIN_VERSION_optparse_applicative(0, 10, 0)
42−option' :: Mod OptionFields Int -> Parser Int
43option' = option auto
42option' = option auto
44#else
43#else
45option' = option
44option' = option
wai-app-static/WaiAppStatic/Listing.hs+1−1
@@ -64,7 +64,7 @@ defaultListing pieces (Folder contents) = do
64
64
65 -- Add a link to the root of the tree
65 -- Add a link to the root of the tree
66 showFolder' :: Bool -> Pieces -> H.Html
66 showFolder' :: Bool -> Pieces -> H.Html
67− showFolder' hasTrailingSlash pieces' = showFolder hasTrailingSlash (unsafeToPiece "root" : pieces')
67+ showFolder' hasTrailingSlash pieces = showFolder hasTrailingSlash (unsafeToPiece "root" : pieces)
68
68
69 showFolder :: Bool -> Pieces -> H.Html
69 showFolder :: Bool -> Pieces -> H.Html
70 showFolder _ [] = "/" -- won't happen
70 showFolder _ [] = "/" -- won't happen
wai-app-static/WaiAppStatic/Storage/Embedded/TH.hs+1
@@ -7,6 +7,7 @@ module WaiAppStatic.Storage.Embedded.TH(
7
7
8import Data.ByteString.Builder.Extra (byteStringInsert)
8import Data.ByteString.Builder.Extra (byteStringInsert)
9import Codec.Compression.GZip (compress)
9import Codec.Compression.GZip (compress)
10+import Control.Applicative
10import Data.ByteString.Unsafe (unsafePackAddressLen)
11import Data.ByteString.Unsafe (unsafePackAddressLen)
11import Data.Either (lefts, rights)
12import Data.Either (lefts, rights)
12import GHC.Exts (Int(..))
13import GHC.Exts (Int(..))
wai-app-static/WaiAppStatic/Types.hs−2
@@ -59,8 +59,6 @@ toPieces :: [Text] -> Maybe Pieces
59toPieces = mapM toPiece
59toPieces = mapM toPiece
60
60
61-- | Request coming from a user. Corresponds to @pathInfo@.
61-- | Request coming from a user. Corresponds to @pathInfo@.
62−--
63−-- The root path is the empty list.
64type Pieces = [Piece]
62type Pieces = [Piece]
65
63
66-- | Values for the max-age component of the cache-control response header.
64-- | Values for the max-age component of the cache-control response header.
wai-app-static/test/WaiAppStaticTest.hs+8−8
@@ -61,18 +61,18 @@ spec = do
61 assertStatus 404 =<<
61 assertStatus 404 =<<
62 request (setRawPathInfo defRequest "doesNotExist")
62 request (setRawPathInfo defRequest "doesNotExist")
63
63
64− it "302 redirect when multiple slashes" $ webApp $ do
64+ it "301 redirect when multiple slashes" $ webApp $ do
65 req <- request (setRawPathInfo defRequest "a//b/c")
65 req <- request (setRawPathInfo defRequest "a//b/c")
66− assertStatus 302 req
66+ assertStatus 301 req
67 assertHeader "Location" "../../a/b/c" req
67 assertHeader "Location" "../../a/b/c" req
68
68
69 let absoluteApp = flip runSession $ staticApp $ (defaultWebAppSettings "test") {
69 let absoluteApp = flip runSession $ staticApp $ (defaultWebAppSettings "test") {
70 ssMkRedirect = \_ u -> S8.append "http://www.example.com" u
70 ssMkRedirect = \_ u -> S8.append "http://www.example.com" u
71 }
71 }
72− it "302 redirect when multiple slashes" $ absoluteApp $
72+ it "301 redirect when multiple slashes" $ absoluteApp $
73 flip mapM_ ["/a//b/c", "a//b/c"] $ \path -> do
73 flip mapM_ ["/a//b/c", "a//b/c"] $ \path -> do
74 req <- request (setRawPathInfo defRequest path)
74 req <- request (setRawPathInfo defRequest path)
75− assertStatus 302 req
75+ assertStatus 301 req
76 assertHeader "Location" "http://www.example.com/a/b/c" req
76 assertHeader "Location" "http://www.example.com/a/b/c" req
77
77
78 describe "webApp when requesting a static asset" $ do
78 describe "webApp when requesting a static asset" $ do
@@ -131,10 +131,10 @@ spec = do
131 assertStatus 304 req
131 assertStatus 304 req
132 assertNoHeader "Cache-Control" req
132 assertNoHeader "Cache-Control" req
133
133
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
135 it "works at the root" $ fileServerApp $ do
135 it "works at the root" $ fileServerApp $ do
136 req <- request (setRawPathInfo defRequest "/a")
136 req <- request (setRawPathInfo defRequest "/a")
137− assertStatus 302 req
137+ assertStatus 301 req
138 assertHeader "Location" "/a/" req
138 assertHeader "Location" "/a/" req
139
139
140 it "works when an index.html is delivered" $ do
140 it "works when an index.html is delivered" $ do
@@ -144,7 +144,7 @@ spec = do
144 inTempDirectory $ fileServerAppWithSettings settings $ do
144 inTempDirectory $ fileServerAppWithSettings settings $ do
145 liftIO $ touch "foo/index.html"
145 liftIO $ touch "foo/index.html"
146 req <- request (setRawPathInfo defRequest "/foo")
146 req <- request (setRawPathInfo defRequest "/foo")
147− assertStatus 302 req
147+ assertStatus 301 req
148 assertHeader "Location" "/foo/" req
148 assertHeader "Location" "/foo/" req
149
149
150 let urlMapApp = flip runSession $ \req send ->
150 let urlMapApp = flip runSession $ \req send ->
@@ -158,7 +158,7 @@ spec = do
158 "urlMapApp: only works at subPath"
158 "urlMapApp: only works at subPath"
159 it "works with subpath at the root of the file server" $ urlMapApp $ do
159 it "works with subpath at the root of the file server" $ urlMapApp $ do
160 req <- request (setRawPathInfo defRequest "/subPath")
160 req <- request (setRawPathInfo defRequest "/subPath")
161− assertStatus 302 req
161+ assertStatus 301 req
162 assertHeader "Location" "/subPath/" req
162 assertHeader "Location" "/subPath/" req
163
163
164 context "with defaultWebAppSettings" $ do
164 context "with defaultWebAppSettings" $ do
wai-app-static/wai-app-static.cabal+1−1
@@ -1,5 +1,5 @@
1name: wai-app-static
1name: wai-app-static
2−version: 3.1.7.1
2+version: 3.1.6.3
3license: MIT
3license: MIT
4license-file: LICENSE
4license-file: LICENSE
5author: Michael Snoyman <michael@snoyman.com>
5author: Michael Snoyman <michael@snoyman.com>
wai-conduit/Network/Wai/Conduit.hs+6−5
@@ -17,16 +17,17 @@ import qualified Data.ByteString as S
17import Control.Monad (unless)
17import Control.Monad (unless)
18import Network.HTTP.Types
18import Network.HTTP.Types
19import Data.ByteString.Builder (Builder)
19import Data.ByteString.Builder (Builder)
20+import Data.IORef
20import qualified Data.Conduit.List as CL
21import qualified Data.Conduit.List as CL
21
22
22-- | Stream the request body.
23-- | Stream the request body.
23--
24--
24-- Since 3.0.0
25-- Since 3.0.0
25−sourceRequestBody :: MonadIO m => Request -> ConduitT () ByteString m ()
26+sourceRequestBody :: MonadIO m => Request -> Source m ByteString
26sourceRequestBody req =
27sourceRequestBody req =
27 loop
28 loop
28 where
29 where
29− go = liftIO (getRequestBodyChunk req)
30+ go = liftIO (requestBody req)
30
31
31 loop = do
32 loop = do
32 bs <- go
33 bs <- go
@@ -37,9 +38,9 @@ sourceRequestBody req =
37-- | Create an HTTP response out of a @Source@.
38-- | Create an HTTP response out of a @Source@.
38--
39--
39-- Since 3.0.0
40-- Since 3.0.0
40−responseSource :: Status -> ResponseHeaders -> ConduitT () (Flush Builder) IO () -> Response
41+responseSource :: Status -> ResponseHeaders -> Source IO (Flush Builder) -> Response
41responseSource s hs src = responseStream s hs $ \send flush ->
42responseSource s hs src = responseStream s hs $ \send flush ->
42− runConduit $ src .| CL.mapM_ (\mbuilder ->
43+ src $$ CL.mapM_ (\mbuilder ->
43 case mbuilder of
44 case mbuilder of
44 Chunk b -> send b
45 Chunk b -> send b
45 Flush -> flush)
46 Flush -> flush)
@@ -49,7 +50,7 @@ responseSource s hs src = responseStream s hs $ \send flush ->
49--
50--
50-- Since 3.0.0
51-- Since 3.0.0
51responseRawSource :: (MonadIO m, MonadIO n)
52responseRawSource :: (MonadIO m, MonadIO n)
52− => (ConduitT () ByteString m () -> ConduitT ByteString Void n () -> IO ())
53+ => (Source m ByteString -> Sink ByteString n () -> IO ())
53 -> Response
54 -> Response
54 -> Response
55 -> Response
55responseRawSource app =
56responseRawSource app =
wai-extra/ChangeLog.md−4
@@ -1,9 +1,5 @@
1# Changelog for wai-extra
1# Changelog for wai-extra
2
2
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
3## 3.0.29
8
4
9* Export `Network.Wai.EventSource.eventStreamAppRaw` [#786](https://github.com/yesodweb/wai/pull/786)
5* Export `Network.Wai.EventSource.eventStreamAppRaw` [#786](https://github.com/yesodweb/wai/pull/786)
wai-extra/Network/Wai/Test.hs+14−22
@@ -99,10 +99,6 @@ runSession session app = ST.evalStateT (runReaderT session app) initState
99data SRequest = SRequest
99data SRequest = SRequest
100 { simpleRequest :: Request
100 { simpleRequest :: Request
101 , simpleRequestBody :: L.ByteString
101 , 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'.
106 }
102 }
107data SResponse = SResponse
103data SResponse = SResponse
108 { simpleStatus :: H.Status
104 { simpleStatus :: H.Status
@@ -110,16 +106,8 @@ data SResponse = SResponse
110 , simpleBody :: L.ByteString
106 , simpleBody :: L.ByteString
111 }
107 }
112 deriving (Show, Eq)
108 deriving (Show, Eq)
113−
114request :: Request -> Session SResponse
109request :: Request -> Session SResponse
115−request req = do
110+request = srequest . flip SRequest L.empty
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
123
111
124-- | Set whole path (request path + query string).
112-- | Set whole path (request path + query string).
125setPath :: Request -> S8.ByteString -> Request
113setPath :: Request -> S8.ByteString -> Request
@@ -178,18 +166,22 @@ extractSetCookieFromSResponse response = do
178 (Map.fromList [(Cookie.setCookieName c, c) | c <- newClientCookies ]))
166 (Map.fromList [(Cookie.setCookieName c, c) | c <- newClientCookies ]))
179 return response
167 return response
180
168
181−-- | Similar to 'request', but allows setting the request body as a plain
182−-- 'L.ByteString'.
183srequest :: SRequest -> Session SResponse
169srequest :: SRequest -> Session SResponse
184srequest (SRequest req bod) = do
170srequest (SRequest req bod) = do
171+ app <- ask
185 refChunks <- liftIO $ newIORef $ L.toChunks bod
172 refChunks <- liftIO $ newIORef $ L.toChunks bod
186− request $
187− req
173+ let req' = req
188− { requestBody = atomicModifyIORef refChunks $ \bss ->
174+ { requestBody = atomicModifyIORef refChunks $ \bss ->
189− case bss of
175+ case bss of
190− [] -> ([], S.empty)
176+ [] -> ([], S.empty)
191− x:y -> (y, x)
177+ x:y -> (y, x)
192− }
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
193
185
194runResponse :: IORef SResponse -> Response -> IO ResponseReceived
186runResponse :: IORef SResponse -> Response -> IO ResponseReceived
195runResponse ref res = do
187runResponse ref res = do
wai-extra/example/Main.hs+2−2
@@ -42,10 +42,10 @@ eventRaw = handle 0
42 where
42 where
43 handle counter emit flush = do
43 handle counter emit flush = do
44 threadDelay 1000000
44 threadDelay 1000000
45− _ <- emit $ ServerEvent (Just $ string8 "raw")
45+ emit $ ServerEvent (Just $ string8 "raw")
46 Nothing
46 Nothing
47 [string8 . show $ counter]
47 [string8 . show $ counter]
48− _ <- flush
48+ flush
49 handle (counter + 1) emit flush
49 handle (counter + 1) emit flush
50
50
51main :: IO ()
51main :: IO ()
wai-extra/test/Network/Wai/TestSpec.hs+11−41
@@ -3,8 +3,6 @@ module Network.Wai.TestSpec (main, spec) where
3
3
4import Control.Monad (void)
4import Control.Monad (void)
5
5
6−import qualified Data.IORef as IORef
7−
8import qualified Data.Text.Encoding as TE
6import qualified Data.Text.Encoding as TE
9
7
10import Data.Time.Calendar (fromGregorian)
8import Data.Time.Calendar (fromGregorian)
@@ -55,53 +53,25 @@ spec = do
55 it "sets rawQueryString to empty string" $ do
53 it "sets rawQueryString to empty string" $ do
56 rawQueryString (setPath defaultRequest "/foo/bar/baz") `shouldBe` ""
54 rawQueryString (setPath defaultRequest "/foo/bar/baz") `shouldBe` ""
57
55
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−
74 describe "request" $ do
56 describe "request" $ do
75
57
76− let echoApp req respond = do
58+ let simpleApp _req respond =
77− reqBody <- L8.fromStrict <$> getRequestBodyChunk req
78− let reqHeaders = requestHeaders req
79 respond $
59 respond $
80 responseLBS
60 responseLBS
81 status200
61 status200
82− reqHeaders
62+ [("foo", "bar")]
83− reqBody
63+ "simple"
84
64
85− it "returns the status code of an echo app on default request" $ do
65+ it "returns the status code of a simple app on default request" $ do
86− sresp <- runSession (request defaultRequest) echoApp
66+ sresp <- runSession (request defaultRequest) simpleApp
87 simpleStatus sresp `shouldBe` status200
67 simpleStatus sresp `shouldBe` status200
88
68
89− it "returns the response body of an echo app" $ do
69+ it "returns the response body of a simple app on default request" $ do
90− bodyRef <- IORef.newIORef "request body"
91− let getBodyChunk = IORef.atomicModifyIORef bodyRef $ \leftover -> ("", leftover)
92− sresp <- flip runSession echoApp $
93− request $
94− defaultRequest
70+ sresp <- runSession (request defaultRequest) simpleApp
95− { requestBody = getBodyChunk
96− }
97− simpleBody sresp `shouldBe` "request body"
71+ simpleBody sresp `shouldBe` "simple"
98−
72+
99− it "returns the response headers of an echo app" $ do
73+ it "returns the response headers of a simple app on default request" $ do
100− sresp <- flip runSession echoApp $
101− request $
102− defaultRequest
74+ sresp <- runSession (request defaultRequest) simpleApp
103− { requestHeaders = [("foo", "bar")]
104− }
105 simpleHeaders sresp `shouldBe` [("foo", "bar")]
75 simpleHeaders sresp `shouldBe` [("foo", "bar")]
106
76
107 let cookieApp req respond =
77 let cookieApp req respond =
wai-extra/wai-extra.cabal+1−1
@@ -1,5 +1,5 @@
1Name: wai-extra
1Name: wai-extra
2−Version: 3.0.29.1
2+Version: 3.0.29
3Synopsis: Provides some basic WAI handlers and middleware.
3Synopsis: Provides some basic WAI handlers and middleware.
4description:
4description:
5 Provides basic WAI handler and middleware functionality:
5 Provides basic WAI handler and middleware functionality:
wai-websockets/server.lhs−6
@@ -8,8 +8,6 @@ this example, keep the [reference](http://jaspervdj.be/websockets/reference)
8nearby to check out the functions we use.
8nearby to check out the functions we use.
9
9
10> {-# LANGUAGE OverloadedStrings, TemplateHaskell #-}
10> {-# LANGUAGE OverloadedStrings, TemplateHaskell #-}
11−> {-# LANGUAGE CPP #-}
12−> {-# OPTIONS_GHC -Wno-unused-top-binds #-} -- for numClients
13> import Data.Char (isPunctuation, isSpace)
11> import Data.Char (isPunctuation, isSpace)
14> import Data.Monoid (mappend)
12> import Data.Monoid (mappend)
15> import Data.Text (Text)
13> import Data.Text (Text)
@@ -101,11 +99,7 @@ stays alive on some browsers.
101
99
102> application state pending = do
100> application state pending = do
103> conn <- WS.acceptRequest pending
101> conn <- WS.acceptRequest pending
104−#if __GLASGOW_HASKELL__ >= 806
105−> WS.withPingThread conn 30 (return ()) $ do
106−#else
107> WS.forkPingThread conn 30
102> WS.forkPingThread conn 30
108−#endif
109
103
110When a client is successfully connected, we read the first message. This should
104When a client is successfully connected, we read the first message. This should
111be in the format of "Hi! I am Jasper", where Jasper is the requested username.
105be in the format of "Hi! I am Jasper", where Jasper is the requested username.
wai-websockets/wai-websockets.cabal−1
@@ -45,7 +45,6 @@ Executable wai-websockets-example
45 , text
45 , text
46 , file-embed
46 , file-embed
47 , http-types
47 , http-types
48− other-modules: Network.Wai.Handler.WebSockets
49 else
48 else
50 buildable: False
49 buildable: False
51
50
warp-tls/ChangeLog.md−14
@@ -1,17 +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
1## 3.2.8
16
2
17* Using gracefullClose of network 3.1.1 or later if available.
3* Using gracefullClose of network 3.1.1 or later if available.
warp-tls/Network/Wai/Handler/WarpTLS.hs+1−1
@@ -361,7 +361,7 @@ httpOverTls TLSSettings{..} _set s bs0 params = do
361 backend recvN = TLS.Backend {
361 backend recvN = TLS.Backend {
362 TLS.backendFlush = return ()
362 TLS.backendFlush = return ()
363#if MIN_VERSION_network(3,1,1)
363#if MIN_VERSION_network(3,1,1)
364− , TLS.backendClose = gracefulClose s 5000 `E.catch` \(SomeException _) -> return ()
364+ , TLS.backendClose = gracefulClose s 5000
365#else
365#else
366 , TLS.backendClose = close s
366 , TLS.backendClose = close s
367#endif
367#endif
warp-tls/warp-tls.cabal+2−2
@@ -1,5 +1,5 @@
1Name: warp-tls
1Name: warp-tls
2−Version: 3.2.11
2+Version: 3.2.10
3Synopsis: HTTP over TLS support for Warp via the TLS package
3Synopsis: HTTP over TLS support for Warp via the TLS package
4License: MIT
4License: MIT
5License-file: LICENSE
5License-file: LICENSE
@@ -27,7 +27,7 @@ Library
27 , cryptonite >= 0.12
27 , cryptonite >= 0.12
28 , network >= 2.2.1
28 , network >= 2.2.1
29 , streaming-commons
29 , streaming-commons
30− , tls-session-manager >= 0.0.4
30+ , tls-session-manager >= 0.0.0.1
31 Exposed-modules: Network.Wai.Handler.WarpTLS
31 Exposed-modules: Network.Wai.Handler.WarpTLS
32 ghc-options: -Wall
32 ghc-options: -Wall
33 if os(windows)
33 if os(windows)
warp/ChangeLog.md−23
@@ -1,26 +1,3 @@
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
1## 3.3.5
25
2
26* New APIs: setGracefulCloseTimeout1 and setGracefulCloseTimeout2.
3* New APIs: setGracefulCloseTimeout1 and setGracefulCloseTimeout2.
warp/Network/Wai/Handler/Warp.hs+1−9
@@ -74,7 +74,6 @@ module Network.Wai.Handler.Warp (
74 , setGracefulShutdownTimeout
74 , setGracefulShutdownTimeout
75 , setGracefulCloseTimeout1
75 , setGracefulCloseTimeout1
76 , setGracefulCloseTimeout2
76 , setGracefulCloseTimeout2
77− , setMaxTotalHeaderLength
78 -- ** Getters
77 -- ** Getters
79 , getPort
78 , getPort
80 , getHost
79 , getHost
@@ -124,7 +123,7 @@ module Network.Wai.Handler.Warp (
124 , defaultPushPromise
123 , defaultPushPromise
125 ) where
124 ) where
126
125
127−import Control.Exception (SomeException, throwIO)
126+import Control.Exception (SomeException, throwIO, throw)
128import Data.Streaming.Network (HostPreference)
127import Data.Streaming.Network (HostPreference)
129import qualified Data.Vault.Lazy as Vault
128import qualified Data.Vault.Lazy as Vault
130import Data.X509
129import Data.X509
@@ -437,13 +436,6 @@ setGracefulShutdownTimeout :: Maybe Int
437 -> Settings -> Settings
436 -> Settings -> Settings
438setGracefulShutdownTimeout time y = y { settingsGracefulShutdownTimeout = time }
437setGracefulShutdownTimeout time y = y { settingsGracefulShutdownTimeout = time }
439
438
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−
447-- | Explicitly pause the slowloris timeout.
439-- | Explicitly pause the slowloris timeout.
448--
440--
449-- This is useful for cases where you partially consume a request body. For
441-- This is useful for cases where you partially consume a request body. For
warp/Network/Wai/Handler/Warp/File.hs+4−7
@@ -34,15 +34,12 @@ data RspFileInfo = WithoutBody H.Status
34----------------------------------------------------------------
34----------------------------------------------------------------
35
35
36conditionalRequest :: I.FileInfo
36conditionalRequest :: I.FileInfo
37− -> H.ResponseHeaders
37+ -> H.ResponseHeaders -> IndexedHeader
38− -> IndexedHeader -- ^ Response
39− -> IndexedHeader -- ^ Request
40 -> RspFileInfo
38 -> RspFileInfo
41−conditionalRequest finfo hs0 rspidx reqidx = case condition of
39+conditionalRequest finfo hs0 reqidx = case condition of
42 nobody@(WithoutBody _) -> nobody
40 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
46 in WithBody s hs off len
43 in WithBody s hs off len
47 where
44 where
48 !mtime = I.fileInfoTime finfo
45 !mtime = I.fileInfoTime finfo
warp/Network/Wai/Handler/Warp/HTTP2.hs+3−3
@@ -24,14 +24,14 @@ import Network.Wai.Handler.Warp.Types
24
24
25----------------------------------------------------------------
25----------------------------------------------------------------
26
26
27−http2 :: Connection -> Transport -> InternalInfo -> SockAddr -> S.Settings -> (BufSize -> IO ByteString) -> (ByteString -> IO ()) -> Application -> IO ()
27+http2 :: Connection -> Transport -> InternalInfo -> SockAddr -> S.Settings -> (BufSize -> IO ByteString) -> Application -> IO ()
28−http2 conn transport ii addr settings readN send app =
28+http2 conn transport ii addr settings readN app =
29 H2.run conf http2server
29 H2.run conf http2server
30 where
30 where
31 conf = H2.Config {
31 conf = H2.Config {
32 confWriteBuffer = connWriteBuffer conn
32 confWriteBuffer = connWriteBuffer conn
33 , confBufferSize = connBufferSize conn
33 , confBufferSize = connBufferSize conn
34− , confSendAll = send
34+ , confSendAll = connSendAll conn
35 , confReadN = readN
35 , confReadN = readN
36 , confPositionReadMaker = pReadMaker ii
36 , confPositionReadMaker = pReadMaker ii
37 }
37 }
warp/Network/Wai/Handler/Warp/HTTP2/Response.hs+1−2
@@ -72,8 +72,7 @@ responseFile _ rsphdr isHead path Nothing ii reqhdr = do
72 Left (_ex :: E.IOException) -> return $ response404 rsphdr
72 Left (_ex :: E.IOException) -> return $ response404 rsphdr
73 Right finfo -> do
73 Right finfo -> do
74 let reqidx = indexRequestHeader reqhdr
74 let reqidx = indexRequestHeader reqhdr
75− rspidx = indexResponseHeader rsphdr
76− case conditionalRequest finfo rsphdr rspidx reqidx of
75+ case conditionalRequest finfo rsphdr reqidx of
77 WithoutBody s -> return $ responseNoBody s rsphdr
76 WithoutBody s -> return $ responseNoBody s rsphdr
78 WithBody s rsphdr' off bytes -> do
77 WithBody s rsphdr' off bytes -> do
79 let !off' = fromIntegral off
78 let !off' = fromIntegral off
warp/Network/Wai/Handler/Warp/Header.hs−2
@@ -69,7 +69,6 @@ indexResponseHeader hdr = traverseHeader hdr responseMaxIndex responseKeyIndex
69data ResponseHeaderIndex = ResContentLength
69data ResponseHeaderIndex = ResContentLength
70 | ResServer
70 | ResServer
71 | ResDate
71 | ResDate
72− | ResLastModified
73 deriving (Enum,Bounded)
72 deriving (Enum,Bounded)
74
73
75-- | The size for 'IndexedHeader' for HTTP Response.
74-- | The size for 'IndexedHeader' for HTTP Response.
@@ -80,7 +79,6 @@ responseKeyIndex :: HeaderName -> Int
80responseKeyIndex hn = case BS.length bs of
79responseKeyIndex hn = case BS.length bs of
81 4 -> if bs == "date" then fromEnum ResDate else -1
80 4 -> if bs == "date" then fromEnum ResDate else -1
82 6 -> if bs == "server" then fromEnum ResServer else -1
81 6 -> if bs == "server" then fromEnum ResServer else -1
83− 13 -> if bs == "last-modified" then fromEnum ResLastModified else -1
84 14 -> if bs == "content-length" then fromEnum ResContentLength else -1
82 14 -> if bs == "content-length" then fromEnum ResContentLength else -1
85 _ -> -1
83 _ -> -1
86 where
84 where
warp/Network/Wai/Handler/Warp/Recv.hs+1−13
@@ -17,9 +17,7 @@ import Foreign.C.Types
17import Foreign.ForeignPtr (withForeignPtr)
17import Foreign.ForeignPtr (withForeignPtr)
18import Foreign.Ptr (Ptr, castPtr, plusPtr)
18import Foreign.Ptr (Ptr, castPtr, plusPtr)
19import GHC.Conc (threadWaitRead)
19import GHC.Conc (threadWaitRead)
20−import qualified GHC.IO.Exception as E
21import Network.Socket (Socket)
20import Network.Socket (Socket)
22−import qualified System.IO.Error as E
23#if MIN_VERSION_network(3,1,0)
21#if MIN_VERSION_network(3,1,0)
24import Network.Socket (withFdSocket)
22import Network.Socket (withFdSocket)
25#else
23#else
@@ -96,11 +94,8 @@ spell init0 siz0 recv recvBuf
96 siz' = siz - len
94 siz' = siz - len
97 loop bss' siz'
95 loop bss' siz'
98
96
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.
102receive :: Socket -> BufferPool -> Recv
97receive :: Socket -> BufferPool -> Recv
103−receive sock pool = E.handle handler $ withBufferPool pool $ \ (ptr, size) -> do
98+receive sock pool = withBufferPool pool $ \ (ptr, size) -> do
104#if MIN_VERSION_network(3,1,0)
99#if MIN_VERSION_network(3,1,0)
105 withFdSocket sock $ \fd -> do
100 withFdSocket sock $ \fd -> do
106#elif MIN_VERSION_network(3,0,0)
101#elif MIN_VERSION_network(3,0,0)
@@ -110,11 +105,6 @@ receive sock pool = E.handle handler $ withBufferPool pool $ \ (ptr, size) -> do
110#endif
105#endif
111 let size' = fromIntegral size
106 let size' = fromIntegral size
112 fromIntegral <$> receiveloop fd ptr size'
107 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
118
108
119receiveBuf :: Socket -> RecvBuf
109receiveBuf :: Socket -> RecvBuf
120receiveBuf sock buf0 siz0 = do
110receiveBuf sock buf0 siz0 = do
@@ -153,8 +143,6 @@ receiveloop sock ptr size = do
153 else
143 else
154 return bytes
144 return bytes
155
145
156−#ifndef mingw32_HOST_OS
157-- fixme: the type of the return value
146-- fixme: the type of the return value
158foreign import ccall unsafe "recv"
147foreign import ccall unsafe "recv"
159 c_recv :: CInt -> Ptr CChar -> CSize -> CInt -> IO CInt
148 c_recv :: CInt -> Ptr CChar -> CSize -> CInt -> IO CInt
160−#endif
warp/Network/Wai/Handler/Warp/Request.hs+17−11
@@ -38,7 +38,13 @@ import Network.Wai.Handler.Warp.Header
38import Network.Wai.Handler.Warp.Imports hiding (readInt, lines)
38import Network.Wai.Handler.Warp.Imports hiding (readInt, lines)
39import Network.Wai.Handler.Warp.ReadInt
39import Network.Wai.Handler.Warp.ReadInt
40import Network.Wai.Handler.Warp.RequestHeader
40import Network.Wai.Handler.Warp.RequestHeader
41−import Network.Wai.Handler.Warp.Settings (Settings, settingsNoParsePath, settingsMaxTotalHeaderLength)
41+import Network.Wai.Handler.Warp.Settings (Settings, settingsNoParsePath)
42+
43+----------------------------------------------------------------
44+
45+-- FIXME come up with good values here
46+maxTotalHeaderLength :: Int
47+maxTotalHeaderLength = 50 * 1024
42
48
43----------------------------------------------------------------
49----------------------------------------------------------------
44
50
@@ -62,7 +68,7 @@ recvRequest :: Bool -- ^ first request on this connection?
62 -- Body producing action used for flushing the request body
68 -- Body producing action used for flushing the request body
63
69
64recvRequest firstRequest settings conn ii th addr src transport = do
70recvRequest firstRequest settings conn ii th addr src transport = do
65− hdrlines <- headerLines (settingsMaxTotalHeaderLength settings) firstRequest src
71+ hdrlines <- headerLines firstRequest src
66 (method, unparsedPath, path, query, httpversion, hdr) <- parseHeaderLines hdrlines
72 (method, unparsedPath, path, query, httpversion, hdr) <- parseHeaderLines hdrlines
67 let idxhdr = indexRequestHeader hdr
73 let idxhdr = indexRequestHeader hdr
68 expect = idxhdr ! fromEnum ReqExpect
74 expect = idxhdr ! fromEnum ReqExpect
@@ -101,8 +107,8 @@ recvRequest firstRequest settings conn ii th addr src transport = do
101
107
102----------------------------------------------------------------
108----------------------------------------------------------------
103
109
104−headerLines :: Int -> Bool -> Source -> IO [ByteString]
110+headerLines :: Bool -> Source -> IO [ByteString]
105−headerLines maxTotalHeaderLength firstRequest src = do
111+headerLines firstRequest src = do
106 bs <- readSource src
112 bs <- readSource src
107 if S.null bs
113 if S.null bs
108 -- When we're working on a keep-alive connection and trying to
114 -- When we're working on a keep-alive connection and trying to
@@ -110,7 +116,7 @@ headerLines maxTotalHeaderLength firstRequest src = do
110 -- lack of data as a real exception. See the http1 function in
116 -- lack of data as a real exception. See the http1 function in
111 -- the Run module for more details.
117 -- the Run module for more details.
112 then if firstRequest then throwIO ConnectionClosedByPeer else throwIO NoKeepAliveRequest
118 then if firstRequest then throwIO ConnectionClosedByPeer else throwIO NoKeepAliveRequest
113− else push maxTotalHeaderLength src (THStatus 0 id id) bs
119+ else push src (THStatus 0 id id) bs
114
120
115data NoKeepAliveRequest = NoKeepAliveRequest
121data NoKeepAliveRequest = NoKeepAliveRequest
116 deriving (Show, Typeable)
122 deriving (Show, Typeable)
@@ -220,8 +226,8 @@ close :: Sink ByteString IO a
220close = throwIO IncompleteHeaders
226close = throwIO IncompleteHeaders
221-}
227-}
222
228
223−push :: Int -> Source -> THStatus -> ByteString -> IO [ByteString]
229+push :: Source -> THStatus -> ByteString -> IO [ByteString]
224−push maxTotalHeaderLength src (THStatus len lines prepend) bs'
230+push src (THStatus len lines prepend) bs'
225 -- Too many bytes
231 -- Too many bytes
226 | len > maxTotalHeaderLength = throwIO OverLargeHeader
232 | len > maxTotalHeaderLength = throwIO OverLargeHeader
227 | otherwise = push' mnl
233 | otherwise = push' mnl
@@ -249,13 +255,13 @@ push maxTotalHeaderLength src (THStatus len lines prepend) bs'
249 push' Nothing = do
255 push' Nothing = do
250 bst <- readSource' src
256 bst <- readSource' src
251 when (S.null bst) $ throwIO IncompleteHeaders
257 when (S.null bst) $ throwIO IncompleteHeaders
252− push maxTotalHeaderLength src status bst
258+ push src status bst
253 where
259 where
254 len' = len + bsLen
260 len' = len + bsLen
255 prepend' = S.append bs
261 prepend' = S.append bs
256 status = THStatus len' lines prepend'
262 status = THStatus len' lines prepend'
257 -- Found a newline, but next line continues as a multiline header
263 -- Found a newline, but next line continues as a multiline header
258− push' (Just (end, True)) = push maxTotalHeaderLength src status rest
264+ push' (Just (end, True)) = push src status rest
259 where
265 where
260 rest = S.drop (end + 1) bs
266 rest = S.drop (end + 1) bs
261 prepend' = S.append (SU.unsafeTake (checkCR bs end) bs)
267 prepend' = S.append (SU.unsafeTake (checkCR bs end) bs)
@@ -274,12 +280,12 @@ push maxTotalHeaderLength src (THStatus len lines prepend) bs'
274 in if start < bsLen then
280 in if start < bsLen then
275 -- more bytes in this chunk, push again
281 -- more bytes in this chunk, push again
276 let bs'' = SU.unsafeDrop start bs
282 let bs'' = SU.unsafeDrop start bs
277− in push maxTotalHeaderLength src status bs''
283+ in push src status bs''
278 else do
284 else do
279 -- no more bytes in this chunk, ask for more
285 -- no more bytes in this chunk, ask for more
280 bst <- readSource' src
286 bst <- readSource' src
281 when (S.null bs) $ throwIO IncompleteHeaders
287 when (S.null bs) $ throwIO IncompleteHeaders
282− push maxTotalHeaderLength src status bst
288+ push src status bst
283 where
289 where
284 start = end + 1 -- start of next chunk
290 start = end + 1 -- start of next chunk
285 line = SU.unsafeTake (checkCR bs end) bs
291 line = SU.unsafeTake (checkCR bs end) bs
warp/Network/Wai/Handler/Warp/Response.hs+16−19
@@ -116,14 +116,14 @@ sendResponse settings conn ii th req reqidxhdr src response = do
116 -- and status, the response to HEAD is processed here.
116 -- and status, the response to HEAD is processed here.
117 --
117 --
118 -- See definition of rsp below for proper body stripping.
118 -- 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
120 case ms of
120 case ms of
121 Nothing -> return ()
121 Nothing -> return ()
122 Just realStatus -> logger req realStatus mlen
122 Just realStatus -> logger req realStatus mlen
123 T.tickle th
123 T.tickle th
124 return ret
124 return ret
125 else do
125 else do
126− _ <- sendRsp conn ii ver s hs rspidxhdr RspNoBody
126+ _ <- sendRsp conn ii ver s hs RspNoBody
127 logger req s Nothing
127 logger req s Nothing
128 T.tickle th
128 T.tickle th
129 return isPersist
129 return isPersist
@@ -196,13 +196,12 @@ sendRsp :: Connection
196 -> H.HttpVersion
196 -> H.HttpVersion
197 -> H.Status
197 -> H.Status
198 -> H.ResponseHeaders
198 -> H.ResponseHeaders
199− -> IndexedHeader -- Response
200 -> Rsp
199 -> Rsp
201 -> IO (Maybe H.Status, Maybe Integer)
200 -> IO (Maybe H.Status, Maybe Integer)
202
201
203----------------------------------------------------------------
202----------------------------------------------------------------
204
203
205−sendRsp conn _ ver s hs _ RspNoBody = do
204+sendRsp conn _ ver s hs RspNoBody = do
206 -- Not adding Content-Length.
205 -- Not adding Content-Length.
207 -- User agents treats it as Content-Length: 0.
206 -- User agents treats it as Content-Length: 0.
208 composeHeader ver s hs >>= connSendAll conn
207 composeHeader ver s hs >>= connSendAll conn
@@ -210,7 +209,7 @@ sendRsp conn _ ver s hs _ RspNoBody = do
210
209
211----------------------------------------------------------------
210----------------------------------------------------------------
212
211
213−sendRsp conn _ ver s hs _ (RspBuilder body needsChunked) = do
212+sendRsp conn _ ver s hs (RspBuilder body needsChunked) = do
214 header <- composeHeaderBuilder ver s hs needsChunked
213 header <- composeHeaderBuilder ver s hs needsChunked
215 let hdrBdy
214 let hdrBdy
216 | needsChunked = header <> chunkedTransferEncoding body
215 | needsChunked = header <> chunkedTransferEncoding body
@@ -223,7 +222,7 @@ sendRsp conn _ ver s hs _ (RspBuilder body needsChunked) = do
223
222
224----------------------------------------------------------------
223----------------------------------------------------------------
225
224
226−sendRsp conn _ ver s hs _ (RspStream streamingBody needsChunked th) = do
225+sendRsp conn _ ver s hs (RspStream streamingBody needsChunked th) = do
227 header <- composeHeaderBuilder ver s hs needsChunked
226 header <- composeHeaderBuilder ver s hs needsChunked
228 (recv, finish) <- newByteStringBuilderRecv $ reuseBufferStrategy
227 (recv, finish) <- newByteStringBuilderRecv $ reuseBufferStrategy
229 $ toBuilderBuffer (connWriteBuffer conn) (connBufferSize conn)
228 $ toBuilderBuffer (connWriteBuffer conn) (connBufferSize conn)
@@ -247,7 +246,7 @@ sendRsp conn _ ver s hs _ (RspStream streamingBody needsChunked th) = do
247
246
248----------------------------------------------------------------
247----------------------------------------------------------------
249
248
250−sendRsp conn _ _ _ _ _ (RspRaw withApp src tickle) = do
249+sendRsp conn _ _ _ _ (RspRaw withApp src tickle) = do
251 withApp recv send
250 withApp recv send
252 return (Nothing, Nothing)
251 return (Nothing, Nothing)
253 where
252 where
@@ -261,8 +260,8 @@ sendRsp conn _ _ _ _ _ (RspRaw withApp src tickle) = do
261
260
262-- Sophisticated WAI applications.
261-- Sophisticated WAI applications.
263-- We respect s0. s0 MUST be a proper value.
262-- We respect s0. s0 MUST be a proper value.
264−sendRsp conn ii ver s0 hs0 rspidxhdr (RspFile path (Just part) _ isHead hook) =
263+sendRsp conn ii ver s0 hs0 (RspFile path (Just part) _ isHead hook) =
265− sendRspFile2XX conn ii ver s0 hs rspidxhdr path beg len isHead hook
264+ sendRspFile2XX conn ii ver s0 hs path beg len isHead hook
266 where
265 where
267 beg = filePartOffset part
266 beg = filePartOffset part
268 len = filePartByteCount part
267 len = filePartByteCount part
@@ -272,17 +271,17 @@ sendRsp conn ii ver s0 hs0 rspidxhdr (RspFile path (Just part) _ isHead hook) =
272
271
273-- Simple WAI applications.
272-- Simple WAI applications.
274-- Status is ignored
273-- 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
276 efinfo <- E.try $ getFileInfo ii path
275 efinfo <- E.try $ getFileInfo ii path
277 case efinfo of
276 case efinfo of
278 Left (_ex :: E.IOException) ->
277 Left (_ex :: E.IOException) ->
279#ifdef WARP_DEBUG
278#ifdef WARP_DEBUG
280 print _ex >>
279 print _ex >>
281#endif
280#endif
282− sendRspFile404 conn ii ver hs0 rspidxhdr
281+ sendRspFile404 conn ii ver hs0
283− Right finfo -> case conditionalRequest finfo hs0 rspidxhdr reqidxhdr of
282+ Right finfo -> case conditionalRequest finfo hs0 idxhdr of
284− WithoutBody s -> sendRsp conn ii ver s hs0 rspidxhdr RspNoBody
283+ WithoutBody s -> sendRsp conn ii ver s hs0 RspNoBody
285− WithBody s hs beg len -> sendRspFile2XX conn ii ver s hs rspidxhdr path beg len isHead hook
284+ WithBody s hs beg len -> sendRspFile2XX conn ii ver s hs path beg len isHead hook
286
285
287----------------------------------------------------------------
286----------------------------------------------------------------
288
287
@@ -291,15 +290,14 @@ sendRspFile2XX :: Connection
291 -> H.HttpVersion
290 -> H.HttpVersion
292 -> H.Status
291 -> H.Status
293 -> H.ResponseHeaders
292 -> H.ResponseHeaders
294− -> IndexedHeader
295 -> FilePath
293 -> FilePath
296 -> Integer
294 -> Integer
297 -> Integer
295 -> Integer
298 -> Bool
296 -> Bool
299 -> IO ()
297 -> IO ()
300 -> IO (Maybe H.Status, Maybe Integer)
298 -> IO (Maybe H.Status, Maybe Integer)
301−sendRspFile2XX conn ii ver s hs rspidxhdr path beg len isHead hook
299+sendRspFile2XX conn ii ver s hs path beg len isHead hook
302− | isHead = sendRsp conn ii ver s hs rspidxhdr RspNoBody
300+ | isHead = sendRsp conn ii ver s hs RspNoBody
303 | otherwise = do
301 | otherwise = do
304 lheader <- composeHeader ver s hs
302 lheader <- composeHeader ver s hs
305 (mfd, fresher) <- getFd ii path
303 (mfd, fresher) <- getFd ii path
@@ -312,9 +310,8 @@ sendRspFile404 :: Connection
312 -> InternalInfo
310 -> InternalInfo
313 -> H.HttpVersion
311 -> H.HttpVersion
314 -> H.ResponseHeaders
312 -> H.ResponseHeaders
315− -> IndexedHeader
316 -> IO (Maybe H.Status, Maybe Integer)
313 -> 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)
318 where
315 where
319 s = H.notFound404
316 s = H.notFound404
320 hs = replaceHeader H.hContentType "text/plain; charset=utf-8" hs0
317 hs = replaceHeader H.hContentType "text/plain; charset=utf-8" hs0
warp/Network/Wai/Handler/Warp/Run.hs+21−21
@@ -17,7 +17,7 @@ import qualified Control.Concurrent as Conc (yield)
17import Control.Exception as E
17import Control.Exception as E
18import qualified Data.ByteString as S
18import qualified Data.ByteString as S
19import Data.Char (chr)
19import Data.Char (chr)
20−import Data.IORef (IORef, newIORef, readIORef, writeIORef)
20+import Data.IORef (IORef, newIORef, readIORef, writeIORef, atomicModifyIORef')
21import Data.Streaming.Network (bindPortTCP)
21import Data.Streaming.Network (bindPortTCP)
22import Foreign.C.Error (Errno(..), eCONNABORTED)
22import Foreign.C.Error (Errno(..), eCONNABORTED)
23import GHC.IO.Exception (IOException(..))
23import GHC.IO.Exception (IOException(..))
@@ -59,11 +59,7 @@ import Network.Socket (fdSocket)
59
59
60-- | Creating 'Connection' for plain HTTP based on a given socket.
60-- | Creating 'Connection' for plain HTTP based on a given socket.
61socketConnection :: Settings -> Socket -> IO Connection
61socketConnection :: Settings -> Socket -> IO Connection
62−#if MIN_VERSION_network(3,1,1)
63socketConnection set s = do
62socketConnection set s = do
64−#else
65−socketConnection _ s = do
66−#endif
67 bufferPool <- newBufferPool
63 bufferPool <- newBufferPool
68 writeBuf <- allocateBuffer bufferSize
64 writeBuf <- allocateBuffer bufferSize
69 let sendall = Sock.sendAll s
65 let sendall = Sock.sendAll s
@@ -80,7 +76,7 @@ socketConnection _ s = do
80 if tm == 0 then
76 if tm == 0 then
81 close s
77 close s
82 else
78 else
83− gracefulClose s tm `E.catch` \(E.SomeException _) -> return ()
79+ gracefulClose s tm
84#else
80#else
85 , connClose = close s
81 , connClose = close s
86#endif
82#endif
@@ -286,7 +282,10 @@ fork :: Settings
286fork set mkConn addr app counter ii = settingsFork set $ \unmask ->
282fork set mkConn addr app counter ii = settingsFork set $ \unmask ->
287 -- Call the user-supplied on exception code if any
283 -- Call the user-supplied on exception code if any
288 -- exceptions are thrown.
284 -- exceptions are thrown.
289− handle (settingsOnException set Nothing) $
285+ handle (settingsOnException set Nothing) .
286+ -- Allocate a new IORef indicating whether the connection has been
287+ -- closed, to avoid double-freeing a connection
288+ withClosedRef $ \ref ->
290 -- Run the connection maker to get a new connection, and ensure
289 -- Run the connection maker to get a new connection, and ensure
291 -- that the connection is closed. If the mkConn call throws an
290 -- that the connection is closed. If the mkConn call throws an
292 -- exception, we will leak the connection. If the mkConn call is
291 -- exception, we will leak the connection. If the mkConn call is
@@ -297,13 +296,21 @@ fork set mkConn addr app counter ii = settingsFork set $ \unmask ->
297 -- We grab the connection before registering timeouts since the
296 -- We grab the connection before registering timeouts since the
298 -- timeouts will be useless during connection creation, due to the
297 -- timeouts will be useless during connection creation, due to the
299 -- fact that async exceptions are still masked.
298 -- fact that async exceptions are still masked.
300− bracket mkConn cleanUp (serve unmask)
299+ bracket mkConn (cleanUp ref) (serve unmask ref)
301 where
300 where
301+ withClosedRef inner = newIORef False >>= inner
302+
303+ closeConn ref conn = do
304+ isClosed <- atomicModifyIORef' ref $ \x -> (True, x)
305+ unless isClosed $ connClose conn
306+
302− cleanUp (conn, _) = connClose conn `finally` connFree conn
307+ cleanUp ref (conn, _) = closeConn ref conn `finally` connFree conn
303
308
304 -- We need to register a timeout handler for this thread, and
309 -- We need to register a timeout handler for this thread, and
305− -- cancel that handler as soon as we exit.
310+ -- cancel that handler as soon as we exit. We additionally close
311+ -- the connection immediately in case the child thread catches the
312+ -- async exception or performs some long-running cleanup action.
306− serve unmask (conn, transport) = bracket register cancel $ \th -> do
313+ serve unmask ref (conn, transport) = bracket register cancel $ \th -> do
307 -- We now have fully registered a connection close handler in
314 -- We now have fully registered a connection close handler in
308 -- the case of all exceptions, so it is safe to once again
315 -- the case of all exceptions, so it is safe to once again
309 -- allow async exceptions.
316 -- allow async exceptions.
@@ -315,7 +322,8 @@ fork set mkConn addr app counter ii = settingsFork set $ \unmask ->
315 -- above ensures the connection is closed.
322 -- above ensures the connection is closed.
316 when goingon $ serveConnection conn ii th addr transport set app
323 when goingon $ serveConnection conn ii th addr transport set app
317 where
324 where
318− register = T.registerKillThread (timeoutManager ii) (connClose conn)
325+ register = T.registerKillThread (timeoutManager ii)
326+ (closeConn ref conn)
319 cancel = T.cancel
327 cancel = T.cancel
320
328
321 onOpen adr = increase counter >> settingsOnOpen set adr
329 onOpen adr = increase counter >> settingsOnOpen set adr
@@ -342,19 +350,11 @@ serveConnection conn ii th origAddr transport settings app = do
342 istatus <- newIORef False
350 istatus <- newIORef False
343 if settingsHTTP2Enabled settings && h2 then do
351 if settingsHTTP2Enabled settings && h2 then do
344 rawRecvN <- makeReceiveN bs (connRecv conn) (connRecvBuf conn)
352 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.
352 let recvN = wrappedRecvN th istatus (settingsSlowlorisSize settings) rawRecvN
353 let recvN = wrappedRecvN th istatus (settingsSlowlorisSize settings) rawRecvN
353− sendBS x = connSendAll conn x >> T.tickle th
354 -- fixme: origAddr
354 -- fixme: origAddr
355 checkTLS
355 checkTLS
356 setConnHTTP2 conn True
356 setConnHTTP2 conn True
357− http2 conn transport ii origAddr settings recvN sendBS app
357+ http2 conn transport ii origAddr settings recvN app
358 else do
358 else do
359 src <- mkSource (wrappedRecv conn th istatus (settingsSlowlorisSize settings))
359 src <- mkSource (wrappedRecv conn th istatus (settingsSlowlorisSize settings))
360 writeIORef istatus True
360 writeIORef istatus True
warp/Network/Wai/Handler/Warp/Settings.hs−5
@@ -128,10 +128,6 @@ data Settings = Settings
128 -- Default: 2000.
128 -- Default: 2000.
129 --
129 --
130 -- Since 3.3.5
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
135 }
131 }
136
132
137-- | Specify usage of the PROXY protocol.
133-- | Specify usage of the PROXY protocol.
@@ -170,7 +166,6 @@ defaultSettings = Settings
170 , settingsGracefulShutdownTimeout = Nothing
166 , settingsGracefulShutdownTimeout = Nothing
171 , settingsGracefulCloseTimeout1 = 0
167 , settingsGracefulCloseTimeout1 = 0
172 , settingsGracefulCloseTimeout2 = 2000
168 , settingsGracefulCloseTimeout2 = 2000
173− , settingsMaxTotalHeaderLength = 50 * 1024
174 }
169 }
175
170
176-- | Apply the logic provided by 'defaultOnException' to determine if an
171-- | Apply the logic provided by 'defaultOnException' to determine if an
warp/test/FileSpec.hs+1−1
@@ -21,7 +21,7 @@ testFileRange desc reqhs file ans = it desc $ do
21 let WithBody s hs off len = ans
21 let WithBody s hs off len = ans
22 hs' = ("Last-Modified",fileInfoDate finfo) : hs
22 hs' = ("Last-Modified",fileInfoDate finfo) : hs
23 ans' = WithBody s hs' off len
23 ans' = WithBody s hs' off len
24− conditionalRequest finfo [] (indexResponseHeader hs) (indexRequestHeader reqhs) `shouldBe` ans'
24+ conditionalRequest finfo [] (indexRequestHeader reqhs) `shouldBe` ans'
25
25
26spec :: Spec
26spec :: Spec
27spec = do
27spec = do
warp/test/RequestSpec.hs+7−11
@@ -7,7 +7,6 @@ module RequestSpec (main, spec) where
7import Network.Wai.Handler.Warp.File (parseByteRanges)
7import Network.Wai.Handler.Warp.File (parseByteRanges)
8import Network.Wai.Handler.Warp.Request
8import Network.Wai.Handler.Warp.Request
9import Network.Wai.Handler.Warp.Types
9import Network.Wai.Handler.Warp.Types
10−import Network.Wai.Handler.Warp.Settings (settingsMaxTotalHeaderLength, defaultSettings)
11import Test.Hspec
10import Test.Hspec
12import Test.Hspec.QuickCheck
11import Test.Hspec.QuickCheck
13import qualified Data.ByteString as S
12import qualified Data.ByteString as S
@@ -18,9 +17,6 @@ import Data.IORef
18main :: IO ()
17main :: IO ()
19main = hspec spec
18main = hspec spec
20
19
21−defaultMaxTotalHeaderLength :: Int
22−defaultMaxTotalHeaderLength = settingsMaxTotalHeaderLength defaultSettings
23−
24spec :: Spec
20spec :: Spec
25spec = do
21spec = do
26 describe "headerLines" $ do
22 describe "headerLines" $ do
@@ -67,29 +63,29 @@ spec = do
67 describe "headerLines" $ do
63 describe "headerLines" $ do
68 it "can handle a nomarl case" $ do
64 it "can handle a nomarl case" $ do
69 src <- mkSourceFunc ["Status: 200\r\nContent-Type: text/plain\r\n\r\n"] >>= mkSource
65 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
71 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
67 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
72
68
73 it "can handle a nasty case (1)" $ do
69 it "can handle a nasty case (1)" $ do
74 src <- mkSourceFunc ["Status: 200", "\r\nContent-Type: text/plain", "\r\n\r\n"] >>= mkSource
70 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
76 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
72 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
77
73
78 it "can handle a nasty case (1)" $ do
74 it "can handle a nasty case (1)" $ do
79 src <- mkSourceFunc ["Status: 200", "\r", "\nContent-Type: text/plain", "\r", "\n\r\n"] >>= mkSource
75 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
81 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
77 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
82
78
83 it "can handle a nasty case (1)" $ do
79 it "can handle a nasty case (1)" $ do
84 src <- mkSourceFunc ["Status: 200", "\r", "\n", "Content-Type: text/plain", "\r", "\n", "\r", "\n"] >>= mkSource
80 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
86 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
82 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
87
83
88 it "can handle an illegal case (1)" $ do
84 it "can handle an illegal case (1)" $ do
89 src <- mkSourceFunc ["\nStatus:", "\n 200", "\nContent-Type: text/plain", "\r\n\r\n"] >>= mkSource
85 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
91 x `shouldBe` []
87 x `shouldBe` []
92− y <- headerLines defaultMaxTotalHeaderLength True src
88+ y <- headerLines True src
93 y `shouldBe` ["Status: 200", "Content-Type: text/plain"]
89 y `shouldBe` ["Status: 200", "Content-Type: text/plain"]
94
90
95 where
91 where
@@ -115,7 +111,7 @@ headerLinesList' orig = do
115 writeIORef ref z
111 writeIORef ref z
116 return y
112 return y
117 src' <- mkSource src
113 src' <- mkSource src
118− res <- headerLines defaultMaxTotalHeaderLength True src'
114+ res <- headerLines True src'
119 return (res, src')
115 return (res, src')
120
116
121consumeLen :: Int -> Source -> IO S8.ByteString
117consumeLen :: Int -> Source -> IO S8.ByteString
warp/warp.cabal+1−1
@@ -1,5 +1,5 @@
1Name: warp
1Name: warp
2−Version: 3.3.9
2+Version: 3.3.6
3Synopsis: A fast, light-weight web server for WAI applications.
3Synopsis: A fast, light-weight web server for WAI applications.
4License: MIT
4License: MIT
5License-file: LICENSE
5License-file: LICENSE