on master help sign in
stack.yaml+1−1
since splitsnapshots
unifiedsplit
@@ -1,4 +1,4 @@
1−resolver: lts-14.20
1+resolver: lts-14.22
2packages:
2packages:
3- ./auto-update
3- ./auto-update
4- ./mime-types
4- ./mime-types
wai-app-static/ChangeLog.md+4
@@ -1,5 +1,9 @@
1# wai-app-static changelog
1# wai-app-static changelog
2
2
3+## 3.1.7.1
4+
5+* Update the test suite too
6+
3## 3.1.7
7## 3.1.7
4
8
5* Use 302 instead of 301 redirect, to avoid caching the presence of an index.html file
9* Use 302 instead of 301 redirect, to avoid caching the presence of an index.html file
wai-app-static/Network/Wai/Application/Static.hs+8−6
@@ -164,11 +164,13 @@ 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− -- Note: It would be arguably better to next check
167+ -- RFC7232 (HTTP 1.1):
168− -- if-modified-since and return a 304 if that indicates a match as
168+ -- > A recipient MUST ignore If-Modified-Since if the request contains an
169− -- well. However, the circumstances under which such a situation
169+ -- > If-None-Match header field; the condition in If-None-Match is
170− -- could arise would be very anomalous, and should likely warrant a
170+ -- > considered to be a more accurate replacement for the condition in
171− -- new file being sent anyway.
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.
172 (Just hash, _) -> respond [("ETag", hash)]
174 (Just hash, _) -> respond [("ETag", hash)]
173
175
174 -- No hash value available, fall back to last modified support.
176 -- No hash value available, fall back to last modified support.
@@ -243,7 +245,7 @@ staticAppPieces ss rawPieces req sendResponse = liftIO $ do
243 response :: StaticResponse -> IO W.ResponseReceived
245 response :: StaticResponse -> IO W.ResponseReceived
244 response (FileResponse file ch) = do
246 response (FileResponse file ch) = do
245 mimetype <- ssGetMimeType ss file
247 mimetype <- ssGetMimeType ss file
246− let filesize = fileGetSize file
248+ -- let filesize = fileGetSize file
247 let headers = ("Content-Type", mimetype)
249 let headers = ("Content-Type", mimetype)
248 -- Let Warp provide the content-length, since it takes
250 -- Let Warp provide the content-length, since it takes
249 -- range requests into account
251 -- range requests into account
wai-app-static/WaiAppStatic/CmdLine.hs+1
@@ -39,6 +39,7 @@ 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
42option' = option auto
43option' = option auto
43#else
44#else
44option' = option
45option' = 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,7 +7,6 @@ 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
11import Data.ByteString.Unsafe (unsafePackAddressLen)
10import Data.ByteString.Unsafe (unsafePackAddressLen)
12import Data.Either (lefts, rights)
11import Data.Either (lefts, rights)
13import GHC.Exts (Int(..))
12import GHC.Exts (Int(..))
wai-app-static/WaiAppStatic/Types.hs+2
@@ -59,6 +59,8 @@ 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.
62type Pieces = [Piece]
64type Pieces = [Piece]
63
65
64-- | Values for the max-age component of the cache-control response header.
66-- | 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 "301 redirect when multiple slashes" $ webApp $ do
64+ it "302 redirect when multiple slashes" $ webApp $ do
65 req <- request (setRawPathInfo defRequest "a//b/c")
65 req <- request (setRawPathInfo defRequest "a//b/c")
66− assertStatus 301 req
66+ assertStatus 302 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 "301 redirect when multiple slashes" $ absoluteApp $
72+ it "302 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 301 req
75+ assertStatus 302 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 "301 redirect to add a trailing slash on directories if missing" $ do
134+ context "302 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 301 req
137+ assertStatus 302 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 301 req
147+ assertStatus 302 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 301 req
161+ assertStatus 302 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
2+version: 3.1.7.1
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+5−6
@@ -17,17 +17,16 @@ 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
21import qualified Data.Conduit.List as CL
20import qualified Data.Conduit.List as CL
22
21
23-- | Stream the request body.
22-- | Stream the request body.
24--
23--
25-- Since 3.0.0
24-- Since 3.0.0
26−sourceRequestBody :: MonadIO m => Request -> Source m ByteString
25+sourceRequestBody :: MonadIO m => Request -> ConduitT () ByteString m ()
27sourceRequestBody req =
26sourceRequestBody req =
28 loop
27 loop
29 where
28 where
30− go = liftIO (requestBody req)
29+ go = liftIO (getRequestBodyChunk req)
31
30
32 loop = do
31 loop = do
33 bs <- go
32 bs <- go
@@ -38,9 +37,9 @@ sourceRequestBody req =
38-- | Create an HTTP response out of a @Source@.
37-- | Create an HTTP response out of a @Source@.
39--
38--
40-- Since 3.0.0
39-- Since 3.0.0
41−responseSource :: Status -> ResponseHeaders -> Source IO (Flush Builder) -> Response
40+responseSource :: Status -> ResponseHeaders -> ConduitT () (Flush Builder) IO () -> Response
42responseSource s hs src = responseStream s hs $ \send flush ->
41responseSource s hs src = responseStream s hs $ \send flush ->
43− src $$ CL.mapM_ (\mbuilder ->
42+ runConduit $ src .| CL.mapM_ (\mbuilder ->
44 case mbuilder of
43 case mbuilder of
45 Chunk b -> send b
44 Chunk b -> send b
46 Flush -> flush)
45 Flush -> flush)
@@ -50,7 +49,7 @@ responseSource s hs src = responseStream s hs $ \send flush ->
50--
49--
51-- Since 3.0.0
50-- Since 3.0.0
52responseRawSource :: (MonadIO m, MonadIO n)
51responseRawSource :: (MonadIO m, MonadIO n)
53− => (Source m ByteString -> Sink ByteString n () -> IO ())
52+ => (ConduitT () ByteString m () -> ConduitT ByteString Void n () -> IO ())
54 -> Response
53 -> Response
55 -> Response
54 -> Response
56responseRawSource app =
55responseRawSource app =
wai-extra/ChangeLog.md+4
@@ -1,5 +1,9 @@
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+
3## 3.0.29
7## 3.0.29
4
8
5* Export `Network.Wai.EventSource.eventStreamAppRaw` [#786](https://github.com/yesodweb/wai/pull/786)
9* Export `Network.Wai.EventSource.eventStreamAppRaw` [#786](https://github.com/yesodweb/wai/pull/786)
wai-extra/Network/Wai/Test.hs+22−14
@@ -99,6 +99,10 @@ 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'.
102 }
106 }
103data SResponse = SResponse
107data SResponse = SResponse
104 { simpleStatus :: H.Status
108 { simpleStatus :: H.Status
@@ -106,8 +110,16 @@ data SResponse = SResponse
106 , simpleBody :: L.ByteString
110 , simpleBody :: L.ByteString
107 }
111 }
108 deriving (Show, Eq)
112 deriving (Show, Eq)
113+
109request :: Request -> Session SResponse
114request :: Request -> Session SResponse
110−request = srequest . flip SRequest L.empty
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
111
123
112-- | Set whole path (request path + query string).
124-- | Set whole path (request path + query string).
113setPath :: Request -> S8.ByteString -> Request
125setPath :: Request -> S8.ByteString -> Request
@@ -166,22 +178,18 @@ extractSetCookieFromSResponse response = do
166 (Map.fromList [(Cookie.setCookieName c, c) | c <- newClientCookies ]))
178 (Map.fromList [(Cookie.setCookieName c, c) | c <- newClientCookies ]))
167 return response
179 return response
168
180
181+-- | Similar to 'request', but allows setting the request body as a plain
182+-- 'L.ByteString'.
169srequest :: SRequest -> Session SResponse
183srequest :: SRequest -> Session SResponse
170srequest (SRequest req bod) = do
184srequest (SRequest req bod) = do
171− app <- ask
172 refChunks <- liftIO $ newIORef $ L.toChunks bod
185 refChunks <- liftIO $ newIORef $ L.toChunks bod
186+ request $
173− let req' = req
187+ req
174− { requestBody = atomicModifyIORef refChunks $ \bss ->
188+ { requestBody = atomicModifyIORef refChunks $ \bss ->
175− case bss of
189+ case bss of
176− [] -> ([], S.empty)
190+ [] -> ([], S.empty)
177− x:y -> (y, x)
191+ x:y -> (y, x)
178− }
192+ }
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
185
193
186runResponse :: IORef SResponse -> Response -> IO ResponseReceived
194runResponse :: IORef SResponse -> Response -> IO ResponseReceived
187runResponse ref res = do
195runResponse 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+41−11
@@ -3,6 +3,8 @@ 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+
6import qualified Data.Text.Encoding as TE
8import qualified Data.Text.Encoding as TE
7
9
8import Data.Time.Calendar (fromGregorian)
10import Data.Time.Calendar (fromGregorian)
@@ -53,25 +55,53 @@ spec = do
53 it "sets rawQueryString to empty string" $ do
55 it "sets rawQueryString to empty string" $ do
54 rawQueryString (setPath defaultRequest "/foo/bar/baz") `shouldBe` ""
56 rawQueryString (setPath defaultRequest "/foo/bar/baz") `shouldBe` ""
55
57
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+
56 describe "request" $ do
74 describe "request" $ do
57
75
58− let simpleApp _req respond =
76+ let echoApp req respond = do
77+ reqBody <- L8.fromStrict <$> getRequestBodyChunk req
78+ let reqHeaders = requestHeaders req
59 respond $
79 respond $
60 responseLBS
80 responseLBS
61 status200
81 status200
62− [("foo", "bar")]
82+ reqHeaders
63− "simple"
83+ reqBody
64
84
65− it "returns the status code of a simple app on default request" $ do
85+ it "returns the status code of an echo app on default request" $ do
66− sresp <- runSession (request defaultRequest) simpleApp
86+ sresp <- runSession (request defaultRequest) echoApp
67 simpleStatus sresp `shouldBe` status200
87 simpleStatus sresp `shouldBe` status200
68
88
69− it "returns the response body of a simple app on default request" $ do
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 $
70− sresp <- runSession (request defaultRequest) simpleApp
94+ defaultRequest
95+ { requestBody = getBodyChunk
96+ }
71− simpleBody sresp `shouldBe` "simple"
97+ simpleBody sresp `shouldBe` "request body"
72−
98+
73− it "returns the response headers of a simple app on default request" $ do
99+ it "returns the response headers of an echo app" $ do
100+ sresp <- flip runSession echoApp $
101+ request $
74− sresp <- runSession (request defaultRequest) simpleApp
102+ defaultRequest
103+ { requestHeaders = [("foo", "bar")]
104+ }
75 simpleHeaders sresp `shouldBe` [("foo", "bar")]
105 simpleHeaders sresp `shouldBe` [("foo", "bar")]
76
106
77 let cookieApp req respond =
107 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
2+Version: 3.0.29.1
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,6 +8,8 @@ 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
11> import Data.Char (isPunctuation, isSpace)
13> import Data.Char (isPunctuation, isSpace)
12> import Data.Monoid (mappend)
14> import Data.Monoid (mappend)
13> import Data.Text (Text)
15> import Data.Text (Text)
@@ -99,7 +101,11 @@ stays alive on some browsers.
99
101
100> application state pending = do
102> application state pending = do
101> conn <- WS.acceptRequest pending
103> conn <- WS.acceptRequest pending
104+#if __GLASGOW_HASKELL__ >= 806
105+> WS.withPingThread conn 30 (return ()) $ do
106+#else
102> WS.forkPingThread conn 30
107> WS.forkPingThread conn 30
108+#endif
103
109
104When a client is successfully connected, we read the first message. This should
110When a client is successfully connected, we read the first message. This should
105be in the format of "Hi! I am Jasper", where Jasper is the requested username.
111be in the format of "Hi! I am Jasper", where Jasper is the requested username.
wai-websockets/wai-websockets.cabal+1
@@ -45,6 +45,7 @@ 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
48 else
49 else
49 buildable: False
50 buildable: False
50
51
warp-tls/ChangeLog.md+4
@@ -1,3 +1,7 @@
1+## 3.2.11
2+
3+* Ignoring an exception from shutdown (gracefulClose).
4+
1## 3.2.10
5## 3.2.10
2
6
3* Passing client certificate, if any, to Warp
7* Passing client certificate, if any, to Warp
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
364+ , TLS.backendClose = gracefulClose s 5000 `E.catch` \(SomeException _) -> return ()
365#else
365#else
366 , TLS.backendClose = close s
366 , TLS.backendClose = close s
367#endif
367#endif
warp-tls/warp-tls.cabal+1−1
@@ -1,5 +1,5 @@
1Name: warp-tls
1Name: warp-tls
2−Version: 3.2.10
2+Version: 3.2.11
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
warp/ChangeLog.md+16
@@ -1,3 +1,19 @@
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+
1## 3.3.6
17## 3.3.6
2
18
3* Fixing a bug of thread killed in the case of event source with
19* Fixing a bug of thread killed in the case of event source with
warp/Network/Wai/Handler/Warp.hs+9−1
@@ -74,6 +74,7 @@ module Network.Wai.Handler.Warp (
74 , setGracefulShutdownTimeout
74 , setGracefulShutdownTimeout
75 , setGracefulCloseTimeout1
75 , setGracefulCloseTimeout1
76 , setGracefulCloseTimeout2
76 , setGracefulCloseTimeout2
77+ , setMaxTotalHeaderLength
77 -- ** Getters
78 -- ** Getters
78 , getPort
79 , getPort
79 , getHost
80 , getHost
@@ -123,7 +124,7 @@ module Network.Wai.Handler.Warp (
123 , defaultPushPromise
124 , defaultPushPromise
124 ) where
125 ) where
125
126
126−import Control.Exception (SomeException, throwIO, throw)
127+import Control.Exception (SomeException, throwIO)
127import Data.Streaming.Network (HostPreference)
128import Data.Streaming.Network (HostPreference)
128import qualified Data.Vault.Lazy as Vault
129import qualified Data.Vault.Lazy as Vault
129import Data.X509
130import Data.X509
@@ -436,6 +437,13 @@ setGracefulShutdownTimeout :: Maybe Int
436 -> Settings -> Settings
437 -> Settings -> Settings
437setGracefulShutdownTimeout time y = y { settingsGracefulShutdownTimeout = time }
438setGracefulShutdownTimeout time y = y { settingsGracefulShutdownTimeout = time }
438
439
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+
439-- | Explicitly pause the slowloris timeout.
447-- | Explicitly pause the slowloris timeout.
440--
448--
441-- This is useful for cases where you partially consume a request body. For
449-- This is useful for cases where you partially consume a request body. For
warp/Network/Wai/Handler/Warp/File.hs+7−4
@@ -34,12 +34,15 @@ data RspFileInfo = WithoutBody H.Status
34----------------------------------------------------------------
34----------------------------------------------------------------
35
35
36conditionalRequest :: I.FileInfo
36conditionalRequest :: I.FileInfo
37− -> H.ResponseHeaders -> IndexedHeader
37+ -> H.ResponseHeaders
38+ -> IndexedHeader -- ^ Response
39+ -> IndexedHeader -- ^ Request
38 -> RspFileInfo
40 -> RspFileInfo
39−conditionalRequest finfo hs0 reqidx = case condition of
41+conditionalRequest finfo hs0 rspidx reqidx = case condition of
40 nobody@(WithoutBody _) -> nobody
42 nobody@(WithoutBody _) -> nobody
43+ WithBody s _ off len -> let !hs1 = addContentHeaders hs0 off len size
44+ !hasLM = isJust $ rspidx ! fromEnum ResLastModified
41− WithBody s _ off len -> let !hs = (H.hLastModified,date) :
45+ !hs = [ (H.hLastModified,date) | not hasLM ] ++ hs1
42− addContentHeaders hs0 off len size
43 in WithBody s hs off len
46 in WithBody s hs off len
44 where
47 where
45 !mtime = I.fileInfoTime finfo
48 !mtime = I.fileInfoTime finfo
warp/Network/Wai/Handler/Warp/HTTP2/Response.hs+2−1
@@ -72,7 +72,8 @@ 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
75− case conditionalRequest finfo rsphdr reqidx of
76+ case conditionalRequest finfo rsphdr rspidx reqidx of
76 WithoutBody s -> return $ responseNoBody s rsphdr
77 WithoutBody s -> return $ responseNoBody s rsphdr
77 WithBody s rsphdr' off bytes -> do
78 WithBody s rsphdr' off bytes -> do
78 let !off' = fromIntegral off
79 let !off' = fromIntegral off
warp/Network/Wai/Handler/Warp/Header.hs+2
@@ -69,6 +69,7 @@ indexResponseHeader hdr = traverseHeader hdr responseMaxIndex responseKeyIndex
69data ResponseHeaderIndex = ResContentLength
69data ResponseHeaderIndex = ResContentLength
70 | ResServer
70 | ResServer
71 | ResDate
71 | ResDate
72+ | ResLastModified
72 deriving (Enum,Bounded)
73 deriving (Enum,Bounded)
73
74
74-- | The size for 'IndexedHeader' for HTTP Response.
75-- | The size for 'IndexedHeader' for HTTP Response.
@@ -79,6 +80,7 @@ responseKeyIndex :: HeaderName -> Int
79responseKeyIndex hn = case BS.length bs of
80responseKeyIndex hn = case BS.length bs of
80 4 -> if bs == "date" then fromEnum ResDate else -1
81 4 -> if bs == "date" then fromEnum ResDate else -1
81 6 -> if bs == "server" then fromEnum ResServer else -1
82 6 -> if bs == "server" then fromEnum ResServer else -1
83+ 13 -> if bs == "last-modified" then fromEnum ResLastModified else -1
82 14 -> if bs == "content-length" then fromEnum ResContentLength else -1
84 14 -> if bs == "content-length" then fromEnum ResContentLength else -1
83 _ -> -1
85 _ -> -1
84 where
86 where
warp/Network/Wai/Handler/Warp/Recv.hs+13−1
@@ -17,7 +17,9 @@ 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
20import Network.Socket (Socket)
21import Network.Socket (Socket)
22+import qualified System.IO.Error as E
21#if MIN_VERSION_network(3,1,0)
23#if MIN_VERSION_network(3,1,0)
22import Network.Socket (withFdSocket)
24import Network.Socket (withFdSocket)
23#else
25#else
@@ -94,8 +96,11 @@ spell init0 siz0 recv recvBuf
94 siz' = siz - len
96 siz' = siz - len
95 loop bss' siz'
97 loop bss' siz'
96
98
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.
97receive :: Socket -> BufferPool -> Recv
102receive :: Socket -> BufferPool -> Recv
98−receive sock pool = withBufferPool pool $ \ (ptr, size) -> do
103+receive sock pool = E.handle handler $ withBufferPool pool $ \ (ptr, size) -> do
99#if MIN_VERSION_network(3,1,0)
104#if MIN_VERSION_network(3,1,0)
100 withFdSocket sock $ \fd -> do
105 withFdSocket sock $ \fd -> do
101#elif MIN_VERSION_network(3,0,0)
106#elif MIN_VERSION_network(3,0,0)
@@ -105,6 +110,11 @@ receive sock pool = withBufferPool pool $ \ (ptr, size) -> do
105#endif
110#endif
106 let size' = fromIntegral size
111 let size' = fromIntegral size
107 fromIntegral <$> receiveloop fd ptr size'
112 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
108
118
109receiveBuf :: Socket -> RecvBuf
119receiveBuf :: Socket -> RecvBuf
110receiveBuf sock buf0 siz0 = do
120receiveBuf sock buf0 siz0 = do
@@ -143,6 +153,8 @@ receiveloop sock ptr size = do
143 else
153 else
144 return bytes
154 return bytes
145
155
156+#ifndef mingw32_HOST_OS
146-- fixme: the type of the return value
157-- fixme: the type of the return value
147foreign import ccall unsafe "recv"
158foreign import ccall unsafe "recv"
148 c_recv :: CInt -> Ptr CChar -> CSize -> CInt -> IO CInt
159 c_recv :: CInt -> Ptr CChar -> CSize -> CInt -> IO CInt
160+#endif
warp/Network/Wai/Handler/Warp/Request.hs+11−17
@@ -38,13 +38,7 @@ 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)
41+import Network.Wai.Handler.Warp.Settings (Settings, settingsNoParsePath, settingsMaxTotalHeaderLength)
42−
43−----------------------------------------------------------------
44−
45−-- FIXME come up with good values here
46−maxTotalHeaderLength :: Int
47−maxTotalHeaderLength = 50 * 1024
48
42
49----------------------------------------------------------------
43----------------------------------------------------------------
50
44
@@ -68,7 +62,7 @@ recvRequest :: Bool -- ^ first request on this connection?
68 -- Body producing action used for flushing the request body
62 -- Body producing action used for flushing the request body
69
63
70recvRequest firstRequest settings conn ii th addr src transport = do
64recvRequest firstRequest settings conn ii th addr src transport = do
71− hdrlines <- headerLines firstRequest src
65+ hdrlines <- headerLines (settingsMaxTotalHeaderLength settings) firstRequest src
72 (method, unparsedPath, path, query, httpversion, hdr) <- parseHeaderLines hdrlines
66 (method, unparsedPath, path, query, httpversion, hdr) <- parseHeaderLines hdrlines
73 let idxhdr = indexRequestHeader hdr
67 let idxhdr = indexRequestHeader hdr
74 expect = idxhdr ! fromEnum ReqExpect
68 expect = idxhdr ! fromEnum ReqExpect
@@ -107,8 +101,8 @@ recvRequest firstRequest settings conn ii th addr src transport = do
107
101
108----------------------------------------------------------------
102----------------------------------------------------------------
109
103
110−headerLines :: Bool -> Source -> IO [ByteString]
104+headerLines :: Int -> Bool -> Source -> IO [ByteString]
111−headerLines firstRequest src = do
105+headerLines maxTotalHeaderLength firstRequest src = do
112 bs <- readSource src
106 bs <- readSource src
113 if S.null bs
107 if S.null bs
114 -- When we're working on a keep-alive connection and trying to
108 -- When we're working on a keep-alive connection and trying to
@@ -116,7 +110,7 @@ headerLines firstRequest src = do
116 -- lack of data as a real exception. See the http1 function in
110 -- lack of data as a real exception. See the http1 function in
117 -- the Run module for more details.
111 -- the Run module for more details.
118 then if firstRequest then throwIO ConnectionClosedByPeer else throwIO NoKeepAliveRequest
112 then if firstRequest then throwIO ConnectionClosedByPeer else throwIO NoKeepAliveRequest
119− else push src (THStatus 0 id id) bs
113+ else push maxTotalHeaderLength src (THStatus 0 id id) bs
120
114
121data NoKeepAliveRequest = NoKeepAliveRequest
115data NoKeepAliveRequest = NoKeepAliveRequest
122 deriving (Show, Typeable)
116 deriving (Show, Typeable)
@@ -226,8 +220,8 @@ close :: Sink ByteString IO a
226close = throwIO IncompleteHeaders
220close = throwIO IncompleteHeaders
227-}
221-}
228
222
229−push :: Source -> THStatus -> ByteString -> IO [ByteString]
223+push :: Int -> Source -> THStatus -> ByteString -> IO [ByteString]
230−push src (THStatus len lines prepend) bs'
224+push maxTotalHeaderLength src (THStatus len lines prepend) bs'
231 -- Too many bytes
225 -- Too many bytes
232 | len > maxTotalHeaderLength = throwIO OverLargeHeader
226 | len > maxTotalHeaderLength = throwIO OverLargeHeader
233 | otherwise = push' mnl
227 | otherwise = push' mnl
@@ -255,13 +249,13 @@ push src (THStatus len lines prepend) bs'
255 push' Nothing = do
249 push' Nothing = do
256 bst <- readSource' src
250 bst <- readSource' src
257 when (S.null bst) $ throwIO IncompleteHeaders
251 when (S.null bst) $ throwIO IncompleteHeaders
258− push src status bst
252+ push maxTotalHeaderLength src status bst
259 where
253 where
260 len' = len + bsLen
254 len' = len + bsLen
261 prepend' = S.append bs
255 prepend' = S.append bs
262 status = THStatus len' lines prepend'
256 status = THStatus len' lines prepend'
263 -- Found a newline, but next line continues as a multiline header
257 -- Found a newline, but next line continues as a multiline header
264− push' (Just (end, True)) = push src status rest
258+ push' (Just (end, True)) = push maxTotalHeaderLength src status rest
265 where
259 where
266 rest = S.drop (end + 1) bs
260 rest = S.drop (end + 1) bs
267 prepend' = S.append (SU.unsafeTake (checkCR bs end) bs)
261 prepend' = S.append (SU.unsafeTake (checkCR bs end) bs)
@@ -280,12 +274,12 @@ push src (THStatus len lines prepend) bs'
280 in if start < bsLen then
274 in if start < bsLen then
281 -- more bytes in this chunk, push again
275 -- more bytes in this chunk, push again
282 let bs'' = SU.unsafeDrop start bs
276 let bs'' = SU.unsafeDrop start bs
283− in push src status bs''
277+ in push maxTotalHeaderLength src status bs''
284 else do
278 else do
285 -- no more bytes in this chunk, ask for more
279 -- no more bytes in this chunk, ask for more
286 bst <- readSource' src
280 bst <- readSource' src
287 when (S.null bs) $ throwIO IncompleteHeaders
281 when (S.null bs) $ throwIO IncompleteHeaders
288− push src status bst
282+ push maxTotalHeaderLength src status bst
289 where
283 where
290 start = end + 1 -- start of next chunk
284 start = end + 1 -- start of next chunk
291 line = SU.unsafeTake (checkCR bs end) bs
285 line = SU.unsafeTake (checkCR bs end) bs
warp/Network/Wai/Handler/Warp/Response.hs+19−16
@@ -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 rsp
119+ (ms, mlen) <- sendRsp conn ii ver s hs rspidxhdr 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 RspNoBody
126+ _ <- sendRsp conn ii ver s hs rspidxhdr 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,12 +196,13 @@ sendRsp :: Connection
196 -> H.HttpVersion
196 -> H.HttpVersion
197 -> H.Status
197 -> H.Status
198 -> H.ResponseHeaders
198 -> H.ResponseHeaders
199+ -> IndexedHeader -- Response
199 -> Rsp
200 -> Rsp
200 -> IO (Maybe H.Status, Maybe Integer)
201 -> IO (Maybe H.Status, Maybe Integer)
201
202
202----------------------------------------------------------------
203----------------------------------------------------------------
203
204
204−sendRsp conn _ ver s hs RspNoBody = do
205+sendRsp conn _ ver s hs _ RspNoBody = do
205 -- Not adding Content-Length.
206 -- Not adding Content-Length.
206 -- User agents treats it as Content-Length: 0.
207 -- User agents treats it as Content-Length: 0.
207 composeHeader ver s hs >>= connSendAll conn
208 composeHeader ver s hs >>= connSendAll conn
@@ -209,7 +210,7 @@ sendRsp conn _ ver s hs RspNoBody = do
209
210
210----------------------------------------------------------------
211----------------------------------------------------------------
211
212
212−sendRsp conn _ ver s hs (RspBuilder body needsChunked) = do
213+sendRsp conn _ ver s hs _ (RspBuilder body needsChunked) = do
213 header <- composeHeaderBuilder ver s hs needsChunked
214 header <- composeHeaderBuilder ver s hs needsChunked
214 let hdrBdy
215 let hdrBdy
215 | needsChunked = header <> chunkedTransferEncoding body
216 | needsChunked = header <> chunkedTransferEncoding body
@@ -222,7 +223,7 @@ sendRsp conn _ ver s hs (RspBuilder body needsChunked) = do
222
223
223----------------------------------------------------------------
224----------------------------------------------------------------
224
225
225−sendRsp conn _ ver s hs (RspStream streamingBody needsChunked th) = do
226+sendRsp conn _ ver s hs _ (RspStream streamingBody needsChunked th) = do
226 header <- composeHeaderBuilder ver s hs needsChunked
227 header <- composeHeaderBuilder ver s hs needsChunked
227 (recv, finish) <- newByteStringBuilderRecv $ reuseBufferStrategy
228 (recv, finish) <- newByteStringBuilderRecv $ reuseBufferStrategy
228 $ toBuilderBuffer (connWriteBuffer conn) (connBufferSize conn)
229 $ toBuilderBuffer (connWriteBuffer conn) (connBufferSize conn)
@@ -246,7 +247,7 @@ sendRsp conn _ ver s hs (RspStream streamingBody needsChunked th) = do
246
247
247----------------------------------------------------------------
248----------------------------------------------------------------
248
249
249−sendRsp conn _ _ _ _ (RspRaw withApp src tickle) = do
250+sendRsp conn _ _ _ _ _ (RspRaw withApp src tickle) = do
250 withApp recv send
251 withApp recv send
251 return (Nothing, Nothing)
252 return (Nothing, Nothing)
252 where
253 where
@@ -260,8 +261,8 @@ sendRsp conn _ _ _ _ (RspRaw withApp src tickle) = do
260
261
261-- Sophisticated WAI applications.
262-- Sophisticated WAI applications.
262-- We respect s0. s0 MUST be a proper value.
263-- We respect s0. s0 MUST be a proper value.
263−sendRsp conn ii ver s0 hs0 (RspFile path (Just part) _ isHead hook) =
264+sendRsp conn ii ver s0 hs0 rspidxhdr (RspFile path (Just part) _ isHead hook) =
264− sendRspFile2XX conn ii ver s0 hs path beg len isHead hook
265+ sendRspFile2XX conn ii ver s0 hs rspidxhdr path beg len isHead hook
265 where
266 where
266 beg = filePartOffset part
267 beg = filePartOffset part
267 len = filePartByteCount part
268 len = filePartByteCount part
@@ -271,17 +272,17 @@ sendRsp conn ii ver s0 hs0 (RspFile path (Just part) _ isHead hook) =
271
272
272-- Simple WAI applications.
273-- Simple WAI applications.
273-- Status is ignored
274-- Status is ignored
274−sendRsp conn ii ver _ hs0 (RspFile path Nothing idxhdr isHead hook) = do
275+sendRsp conn ii ver _ hs0 rspidxhdr (RspFile path Nothing reqidxhdr isHead hook) = do
275 efinfo <- E.try $ getFileInfo ii path
276 efinfo <- E.try $ getFileInfo ii path
276 case efinfo of
277 case efinfo of
277 Left (_ex :: E.IOException) ->
278 Left (_ex :: E.IOException) ->
278#ifdef WARP_DEBUG
279#ifdef WARP_DEBUG
279 print _ex >>
280 print _ex >>
280#endif
281#endif
281− sendRspFile404 conn ii ver hs0
282+ sendRspFile404 conn ii ver hs0 rspidxhdr
282− Right finfo -> case conditionalRequest finfo hs0 idxhdr of
283+ Right finfo -> case conditionalRequest finfo hs0 rspidxhdr reqidxhdr of
283− WithoutBody s -> sendRsp conn ii ver s hs0 RspNoBody
284+ WithoutBody s -> sendRsp conn ii ver s hs0 rspidxhdr RspNoBody
284− WithBody s hs beg len -> sendRspFile2XX conn ii ver s hs path beg len isHead hook
285+ WithBody s hs beg len -> sendRspFile2XX conn ii ver s hs rspidxhdr path beg len isHead hook
285
286
286----------------------------------------------------------------
287----------------------------------------------------------------
287
288
@@ -290,14 +291,15 @@ sendRspFile2XX :: Connection
290 -> H.HttpVersion
291 -> H.HttpVersion
291 -> H.Status
292 -> H.Status
292 -> H.ResponseHeaders
293 -> H.ResponseHeaders
294+ -> IndexedHeader
293 -> FilePath
295 -> FilePath
294 -> Integer
296 -> Integer
295 -> Integer
297 -> Integer
296 -> Bool
298 -> Bool
297 -> IO ()
299 -> IO ()
298 -> IO (Maybe H.Status, Maybe Integer)
300 -> IO (Maybe H.Status, Maybe Integer)
299−sendRspFile2XX conn ii ver s hs path beg len isHead hook
301+sendRspFile2XX conn ii ver s hs rspidxhdr path beg len isHead hook
300− | isHead = sendRsp conn ii ver s hs RspNoBody
302+ | isHead = sendRsp conn ii ver s hs rspidxhdr RspNoBody
301 | otherwise = do
303 | otherwise = do
302 lheader <- composeHeader ver s hs
304 lheader <- composeHeader ver s hs
303 (mfd, fresher) <- getFd ii path
305 (mfd, fresher) <- getFd ii path
@@ -310,8 +312,9 @@ sendRspFile404 :: Connection
310 -> InternalInfo
312 -> InternalInfo
311 -> H.HttpVersion
313 -> H.HttpVersion
312 -> H.ResponseHeaders
314 -> H.ResponseHeaders
315+ -> IndexedHeader
313 -> IO (Maybe H.Status, Maybe Integer)
316 -> IO (Maybe H.Status, Maybe Integer)
314−sendRspFile404 conn ii ver hs0 = sendRsp conn ii ver s hs (RspBuilder body True)
317+sendRspFile404 conn ii ver hs0 rspidxhdr = sendRsp conn ii ver s hs rspidxhdr (RspBuilder body True)
315 where
318 where
316 s = H.notFound404
319 s = H.notFound404
317 hs = replaceHeader H.hContentType "text/plain; charset=utf-8" hs0
320 hs = replaceHeader H.hContentType "text/plain; charset=utf-8" hs0
warp/Network/Wai/Handler/Warp/Run.hs+12−20
@@ -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, atomicModifyIORef')
20+import Data.IORef (IORef, newIORef, readIORef, writeIORef)
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,7 +59,11 @@ 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)
62socketConnection set s = do
63socketConnection set s = do
64+#else
65+socketConnection _ s = do
66+#endif
63 bufferPool <- newBufferPool
67 bufferPool <- newBufferPool
64 writeBuf <- allocateBuffer bufferSize
68 writeBuf <- allocateBuffer bufferSize
65 let sendall = Sock.sendAll s
69 let sendall = Sock.sendAll s
@@ -76,7 +80,7 @@ socketConnection set s = do
76 if tm == 0 then
80 if tm == 0 then
77 close s
81 close s
78 else
82 else
79− gracefulClose s tm
83+ gracefulClose s tm `E.catch` \(E.SomeException _) -> return ()
80#else
84#else
81 , connClose = close s
85 , connClose = close s
82#endif
86#endif
@@ -282,10 +286,7 @@ fork :: Settings
282fork set mkConn addr app counter ii = settingsFork set $ \unmask ->
286fork set mkConn addr app counter ii = settingsFork set $ \unmask ->
283 -- Call the user-supplied on exception code if any
287 -- Call the user-supplied on exception code if any
284 -- exceptions are thrown.
288 -- exceptions are thrown.
285− handle (settingsOnException set Nothing) .
289+ 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 ->
289 -- Run the connection maker to get a new connection, and ensure
290 -- Run the connection maker to get a new connection, and ensure
290 -- that the connection is closed. If the mkConn call throws an
291 -- that the connection is closed. If the mkConn call throws an
291 -- exception, we will leak the connection. If the mkConn call is
292 -- exception, we will leak the connection. If the mkConn call is
@@ -296,21 +297,13 @@ fork set mkConn addr app counter ii = settingsFork set $ \unmask ->
296 -- We grab the connection before registering timeouts since the
297 -- We grab the connection before registering timeouts since the
297 -- timeouts will be useless during connection creation, due to the
298 -- timeouts will be useless during connection creation, due to the
298 -- fact that async exceptions are still masked.
299 -- fact that async exceptions are still masked.
299− bracket mkConn (cleanUp ref) (serve unmask ref)
300+ bracket mkConn cleanUp (serve unmask)
300 where
301 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−
307− cleanUp ref (conn, _) = closeConn ref conn `finally` connFree conn
302+ cleanUp (conn, _) = connClose conn `finally` connFree conn
308
303
309 -- We need to register a timeout handler for this thread, and
304 -- We need to register a timeout handler for this thread, and
310− -- cancel that handler as soon as we exit. We additionally close
305+ -- cancel that handler as soon as we exit.
311− -- the connection immediately in case the child thread catches the
312− -- async exception or performs some long-running cleanup action.
313− serve unmask ref (conn, transport) = bracket register cancel $ \th -> do
306+ serve unmask (conn, transport) = bracket register cancel $ \th -> do
314 -- We now have fully registered a connection close handler in
307 -- We now have fully registered a connection close handler in
315 -- the case of all exceptions, so it is safe to once again
308 -- the case of all exceptions, so it is safe to once again
316 -- allow async exceptions.
309 -- allow async exceptions.
@@ -322,8 +315,7 @@ fork set mkConn addr app counter ii = settingsFork set $ \unmask ->
322 -- above ensures the connection is closed.
315 -- above ensures the connection is closed.
323 when goingon $ serveConnection conn ii th addr transport set app
316 when goingon $ serveConnection conn ii th addr transport set app
324 where
317 where
325− register = T.registerKillThread (timeoutManager ii)
318+ register = T.registerKillThread (timeoutManager ii) (connClose conn)
326− (closeConn ref conn)
327 cancel = T.cancel
319 cancel = T.cancel
328
320
329 onOpen adr = increase counter >> settingsOnOpen set adr
321 onOpen adr = increase counter >> settingsOnOpen set adr
warp/Network/Wai/Handler/Warp/Settings.hs+5
@@ -128,6 +128,10 @@ 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
131 }
135 }
132
136
133-- | Specify usage of the PROXY protocol.
137-- | Specify usage of the PROXY protocol.
@@ -166,6 +170,7 @@ defaultSettings = Settings
166 , settingsGracefulShutdownTimeout = Nothing
170 , settingsGracefulShutdownTimeout = Nothing
167 , settingsGracefulCloseTimeout1 = 0
171 , settingsGracefulCloseTimeout1 = 0
168 , settingsGracefulCloseTimeout2 = 2000
172 , settingsGracefulCloseTimeout2 = 2000
173+ , settingsMaxTotalHeaderLength = 50 * 1024
169 }
174 }
170
175
171-- | Apply the logic provided by 'defaultOnException' to determine if an
176-- | 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 [] (indexRequestHeader reqhs) `shouldBe` ans'
24+ conditionalRequest finfo [] (indexResponseHeader hs) (indexRequestHeader reqhs) `shouldBe` ans'
25
25
26spec :: Spec
26spec :: Spec
27spec = do
27spec = do
warp/test/RequestSpec.hs+11−7
@@ -7,6 +7,7 @@ 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)
10import Test.Hspec
11import Test.Hspec
11import Test.Hspec.QuickCheck
12import Test.Hspec.QuickCheck
12import qualified Data.ByteString as S
13import qualified Data.ByteString as S
@@ -17,6 +18,9 @@ import Data.IORef
17main :: IO ()
18main :: IO ()
18main = hspec spec
19main = hspec spec
19
20
21+defaultMaxTotalHeaderLength :: Int
22+defaultMaxTotalHeaderLength = settingsMaxTotalHeaderLength defaultSettings
23+
20spec :: Spec
24spec :: Spec
21spec = do
25spec = do
22 describe "headerLines" $ do
26 describe "headerLines" $ do
@@ -63,29 +67,29 @@ spec = do
63 describe "headerLines" $ do
67 describe "headerLines" $ do
64 it "can handle a nomarl case" $ do
68 it "can handle a nomarl case" $ do
65 src <- mkSourceFunc ["Status: 200\r\nContent-Type: text/plain\r\n\r\n"] >>= mkSource
69 src <- mkSourceFunc ["Status: 200\r\nContent-Type: text/plain\r\n\r\n"] >>= mkSource
66− x <- headerLines True src
70+ x <- headerLines defaultMaxTotalHeaderLength True src
67 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
71 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
68
72
69 it "can handle a nasty case (1)" $ do
73 it "can handle a nasty case (1)" $ do
70 src <- mkSourceFunc ["Status: 200", "\r\nContent-Type: text/plain", "\r\n\r\n"] >>= mkSource
74 src <- mkSourceFunc ["Status: 200", "\r\nContent-Type: text/plain", "\r\n\r\n"] >>= mkSource
71− x <- headerLines True src
75+ x <- headerLines defaultMaxTotalHeaderLength True src
72 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
76 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
73
77
74 it "can handle a nasty case (1)" $ do
78 it "can handle a nasty case (1)" $ do
75 src <- mkSourceFunc ["Status: 200", "\r", "\nContent-Type: text/plain", "\r", "\n\r\n"] >>= mkSource
79 src <- mkSourceFunc ["Status: 200", "\r", "\nContent-Type: text/plain", "\r", "\n\r\n"] >>= mkSource
76− x <- headerLines True src
80+ x <- headerLines defaultMaxTotalHeaderLength True src
77 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
81 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
78
82
79 it "can handle a nasty case (1)" $ do
83 it "can handle a nasty case (1)" $ do
80 src <- mkSourceFunc ["Status: 200", "\r", "\n", "Content-Type: text/plain", "\r", "\n", "\r", "\n"] >>= mkSource
84 src <- mkSourceFunc ["Status: 200", "\r", "\n", "Content-Type: text/plain", "\r", "\n", "\r", "\n"] >>= mkSource
81− x <- headerLines True src
85+ x <- headerLines defaultMaxTotalHeaderLength True src
82 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
86 x `shouldBe` ["Status: 200", "Content-Type: text/plain"]
83
87
84 it "can handle an illegal case (1)" $ do
88 it "can handle an illegal case (1)" $ do
85 src <- mkSourceFunc ["\nStatus:", "\n 200", "\nContent-Type: text/plain", "\r\n\r\n"] >>= mkSource
89 src <- mkSourceFunc ["\nStatus:", "\n 200", "\nContent-Type: text/plain", "\r\n\r\n"] >>= mkSource
86− x <- headerLines True src
90+ x <- headerLines defaultMaxTotalHeaderLength True src
87 x `shouldBe` []
91 x `shouldBe` []
88− y <- headerLines True src
92+ y <- headerLines defaultMaxTotalHeaderLength True src
89 y `shouldBe` ["Status: 200", "Content-Type: text/plain"]
93 y `shouldBe` ["Status: 200", "Content-Type: text/plain"]
90
94
91 where
95 where
@@ -111,7 +115,7 @@ headerLinesList' orig = do
111 writeIORef ref z
115 writeIORef ref z
112 return y
116 return y
113 src' <- mkSource src
117 src' <- mkSource src
114− res <- headerLines True src'
118+ res <- headerLines defaultMaxTotalHeaderLength True src'
115 return (res, src')
119 return (res, src')
116
120
117consumeLen :: Int -> Source -> IO S8.ByteString
121consumeLen :: Int -> Source -> IO S8.ByteString
warp/warp.cabal+1−1
@@ -1,5 +1,5 @@
1Name: warp
1Name: warp
2−Version: 3.3.6
2+Version: 3.3.9
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