on master help sign in
Fix for #647 (Payload Too Large in WAI is 500 instead of 413)
302726bvlatkoB
wai-extra/ChangeLog.md+6
@@ -1,5 +1,11 @@
1# Changelog for wai-extra
1# Changelog for wai-extra
2
2
3+## 3.0.26
4+
5+* Throw 413 for too large payload
6+* Throw 431 for too large headers
7+ [#741](https://github.com/yesodweb/wai/pull/741)
8+
3## 3.0.25
9## 3.0.25
4
10
5* Supporting `network` version 3.0.
11* Supporting `network` version 3.0.
wai-extra/Network/Wai/Parse.hs+11−4
@@ -48,6 +48,7 @@ module Network.Wai.Parse
48#endif
48#endif
49 ) where
49 ) where
50
50
51+import qualified Control.Exception as E
51import qualified Data.ByteString as S
52import qualified Data.ByteString as S
52import qualified Data.ByteString.Lazy as L
53import qualified Data.ByteString.Lazy as L
53import qualified Data.ByteString.Char8 as S8
54import qualified Data.ByteString.Char8 as S8
@@ -67,6 +68,7 @@ import Control.Monad (when, unless, guard)
67import Control.Monad.Trans.Resource (allocate, release, register, InternalState, runInternalState)
68import Control.Monad.Trans.Resource (allocate, release, register, InternalState, runInternalState)
68import Data.IORef
69import Data.IORef
69import Network.HTTP.Types (hContentType)
70import Network.HTTP.Types (hContentType)
71+import Network.HTTP2( HTTP2Error (..), ErrorCodeId (..) )
70import Data.CaseInsensitive (mk)
72import Data.CaseInsensitive (mk)
71
73
72import Prelude hiding (lines)
74import Prelude hiding (lines)
@@ -426,6 +428,8 @@ conduitRequestBodyEx o backend (Multipart bound) rbody add =
426
428
427
429
428-- | Take one header or subheader line.
430-- | Take one header or subheader line.
431+-- Since: 3.0.26
432+-- Throw 431 if headers too large.
429takeLine :: Maybe Int -> Source -> IO (Maybe S.ByteString)
433takeLine :: Maybe Int -> Source -> IO (Maybe S.ByteString)
430takeLine maxlen src =
434takeLine maxlen src =
431 go ""
435 go ""
@@ -434,7 +438,8 @@ takeLine maxlen src =
434 bs <- readSource src
438 bs <- readSource src
435 case maxlen of
439 case maxlen of
436 Just maxlen' -> when (S.length front > maxlen') $
440 Just maxlen' -> when (S.length front > maxlen') $
437− error "Header line length exceeds allowed maximum."
441+ E.throwIO $ ConnectionError (UnknownErrorCode 431)
442+ "Request Header Fields Too Large"
438 Nothing -> return ()
443 Nothing -> return ()
439 if S.null bs
444 if S.null bs
440 then close front
445 then close front
@@ -450,7 +455,8 @@ takeLine maxlen src =
450 let res = front `S.append` x
455 let res = front `S.append` x
451 case maxlen of
456 case maxlen of
452 Just maxlen' -> when (S.length res > maxlen') $
457 Just maxlen' -> when (S.length res > maxlen') $
453− error "Header line length exceeds allowed maximum."
458+ E.throwIO $ ConnectionError (UnknownErrorCode 431)
459+ "Request Header Fields Too Large"
454 Nothing -> return ()
460 Nothing -> return ()
455 return $ Just $ killCR $ res
461 return $ Just $ killCR $ res
456
462
@@ -639,8 +645,9 @@ wrapTillBound bound src max' = do
639 cur <- atomicModifyIORef' sref $ \ cur ->
645 cur <- atomicModifyIORef' sref $ \ cur ->
640 let new = cur + fromIntegral (S.length bs) in (new, new)
646 let new = cur + fromIntegral (S.length bs) in (new, new)
641 case max' of
647 case max' of
642− Just max'' | cur > max'' -> error "Maximum size exceeded"
648+ Just max'' | cur > max'' ->
649+ E.throwIO $ ConnectionError (UnknownErrorCode 413) "Payload Too Large"
643− _ -> return ()
650+ _ -> return ()
644 if S.null bs
651 if S.null bs
645 then do
652 then do
646 writeIORef ref $ WTBDone False
653 writeIORef ref $ WTBDone False
wai-extra/test/Network/Wai/ParseSpec.hs+14−5
@@ -13,6 +13,7 @@ import qualified Data.ByteString.Lazy as L
13import qualified Data.Text as TS
13import qualified Data.Text as TS
14import qualified Data.Text.Encoding as TE
14import qualified Data.Text.Encoding as TE
15import Control.Monad.Trans.Resource (withInternalState, runResourceT)
15import Control.Monad.Trans.Resource (withInternalState, runResourceT)
16+import Network.HTTP2( HTTP2Error (..), ErrorCodeId (..) )
16
17
17import Network.Wai
18import Network.Wai
18import Network.Wai.Test
19import Network.Wai.Test
@@ -94,6 +95,9 @@ caseParseRequestBody = do
94 let expectedfile3 = [("yaml", FileInfo "README" "application/octet-stream" "Photo blog using Hack.\n")]
95 let expectedfile3 = [("yaml", FileInfo "README" "application/octet-stream" "Photo blog using Hack.\n")]
95 let expected3 = (expectedsmap3, expectedfile3)
96 let expected3 = (expectedsmap3, expectedfile3)
96
97
98+ let unknownErrorException c (ConnectionError (UnknownErrorCode code) _) = c == code
99+ unknownErrorException _ _ = False
100+
97
101
98 let def = defaultParseRequestBodyOptions
102 let def = defaultParseRequestBodyOptions
99 it "parsing actual post multipart/form-data" $ do
103 it "parsing actual post multipart/form-data" $ do
@@ -119,17 +123,21 @@ caseParseRequestBody = do
119
123
120 it "exceeding file size" $ do
124 it "exceeding file size" $ do
121 SRequest req4 _bod4 <- toRequest'' ctype3 content3
125 SRequest req4 _bod4 <- toRequest'' ctype3 content3
122− (parseRequestBodyEx ( setMaxRequestFileSize 2 def ) lbsBackEnd req4) `shouldThrow` anyErrorCall
126+ (parseRequestBodyEx ( setMaxRequestFileSize 2 def ) lbsBackEnd req4)
127+ `shouldThrow` unknownErrorException 413
123
128
124 it "exceeding total file size" $ do
129 it "exceeding total file size" $ do
125 SRequest req4 _bod4 <- toRequest'' ctype3 content3
130 SRequest req4 _bod4 <- toRequest'' ctype3 content3
126− (parseRequestBodyEx ( setMaxRequestFilesSize 20 def ) lbsBackEnd req4) `shouldThrow` anyErrorCall
131+ (parseRequestBodyEx ( setMaxRequestFilesSize 20 def ) lbsBackEnd req4)
132+ `shouldThrow` unknownErrorException 413
127 SRequest req5 _bod5 <- toRequest'' ctype3 content5
133 SRequest req5 _bod5 <- toRequest'' ctype3 content5
128− (parseRequestBodyEx ( setMaxRequestFilesSize 20 def ) lbsBackEnd req5) `shouldThrow` anyErrorCall
134+ (parseRequestBodyEx ( setMaxRequestFilesSize 20 def ) lbsBackEnd req5)
135+ `shouldThrow` unknownErrorException 413
129
136
130 it "exceeding max parm value size" $ do
137 it "exceeding max parm value size" $ do
131 SRequest req4 _bod4 <- toRequest'' ctype2 content2
138 SRequest req4 _bod4 <- toRequest'' ctype2 content2
132− (parseRequestBodyEx ( setMaxRequestParmsSize 10 def ) lbsBackEnd req4) `shouldThrow` anyErrorCall
139+ (parseRequestBodyEx ( setMaxRequestParmsSize 10 def ) lbsBackEnd req4)
140+ `shouldThrow` unknownErrorException 413
133
141
134 it "exceeding max header lines" $ do
142 it "exceeding max header lines" $ do
135 SRequest req4 _bod4 <- toRequest'' ctype2 content2
143 SRequest req4 _bod4 <- toRequest'' ctype2 content2
@@ -137,7 +145,8 @@ caseParseRequestBody = do
137
145
138 it "exceeding header line size" $ do
146 it "exceeding header line size" $ do
139 SRequest req4 _bod4 <- toRequest'' ctype3 content4
147 SRequest req4 _bod4 <- toRequest'' ctype3 content4
140− (parseRequestBodyEx ( setMaxHeaderLineLength 8190 def ) lbsBackEnd req4) `shouldThrow` anyErrorCall
148+ (parseRequestBodyEx ( setMaxHeaderLineLength 8190 def ) lbsBackEnd req4)
149+ `shouldThrow` unknownErrorException 431
141
150
142 it "Testing parseRequestBodyEx with application/x-www-form-urlencoded" $ do
151 it "Testing parseRequestBodyEx with application/x-www-form-urlencoded" $ do
143 let content = "thisisalongparameterkey=andthisbeanevenlongerparametervaluehelloworldhowareyou"
152 let content = "thisisalongparameterkey=andthisbeanevenlongerparametervaluehelloworldhowareyou"
wai-extra/wai-extra.cabal+3−1
@@ -1,5 +1,5 @@
1Name: wai-extra
1Name: wai-extra
2−Version: 3.0.25
2+Version: 3.0.26
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:
@@ -115,6 +115,7 @@ Library
115 , zlib
115 , zlib
116 , aeson
116 , aeson
117 , iproute
117 , iproute
118+ , http2
118
119
119 if os(windows)
120 if os(windows)
120 cpp-options: -DWINDOWS
121 cpp-options: -DWINDOWS
@@ -202,6 +203,7 @@ test-suite spec
202 , cookie
203 , cookie
203 , time
204 , time
204 , case-insensitive
205 , case-insensitive
206+ , http2
205 ghc-options: -Wall
207 ghc-options: -Wall
206 default-language: Haskell2010
208 default-language: Haskell2010
207
209
warp/ChangeLog.md+4
@@ -4,6 +4,10 @@
4 value of the `PORT` environment variable
4 value of the `PORT` environment variable
5 [#736](https://github.com/yesodweb/wai/pull/736)
5 [#736](https://github.com/yesodweb/wai/pull/736)
6
6
7+* Throw 413 for too large payload
8+* Throw 431 for too large headers
9+ [#741](https://github.com/yesodweb/wai/pull/741)
10+
7## 3.2.26
11## 3.2.26
8
12
9* Support network package version 3
13* Support network package version 3
warp/Network/Wai/Handler/Warp/Settings.hs+22−4
@@ -8,12 +8,14 @@ import Control.Concurrent (forkIOWithUnmask)
8import Control.Exception
8import Control.Exception
9import Data.ByteString.Builder (byteString)
9import Data.ByteString.Builder (byteString)
10import qualified Data.ByteString.Char8 as C8
10import qualified Data.ByteString.Char8 as C8
11+import Data.ByteString.Lazy (fromStrict)
11import Data.Streaming.Network (HostPreference)
12import Data.Streaming.Network (HostPreference)
12import qualified Data.Text as T
13import qualified Data.Text as T
13import qualified Data.Text.IO as TIO
14import qualified Data.Text.IO as TIO
14import Data.Version (showVersion)
15import Data.Version (showVersion)
15import GHC.IO.Exception (IOErrorType(..))
16import GHC.IO.Exception (IOErrorType(..))
16import qualified Network.HTTP.Types as H
17import qualified Network.HTTP.Types as H
18+import Network.HTTP2( HTTP2Error (..), ErrorCodeId (..) )
17import Network.Socket (SockAddr)
19import Network.Socket (SockAddr)
18import Network.Wai
20import Network.Wai
19import qualified Paths_warp
21import qualified Paths_warp
@@ -168,13 +170,29 @@ defaultOnException _ e =
168 when (defaultShouldDisplayException e)
170 when (defaultShouldDisplayException e)
169 $ TIO.hPutStrLn stderr $ T.pack $ show e
171 $ TIO.hPutStrLn stderr $ T.pack $ show e
170
172
173+-- | Sending 400 for bad requests.
171−-- | Sending 400 for bad requests. Sending 500 for internal server errors.
174+-- Sending 500 for internal server errors.
172−--
173-- Since: 3.1.0
175-- Since: 3.1.0
176+-- Sending 413 for too large payload.
177+-- Sending 431 for too large headers.
178+-- Since 3.2.27
174defaultOnExceptionResponse :: SomeException -> Response
179defaultOnExceptionResponse :: SomeException -> Response
175defaultOnExceptionResponse e
180defaultOnExceptionResponse e
181+ | Just (_ :: InvalidRequest) <-
176− | Just (_ :: InvalidRequest) <- fromException e = responseLBS H.badRequest400 [(H.hContentType, "text/plain; charset=utf-8")] "Bad Request"
182+ fromException e = responseLBS H.badRequest400
183+ [(H.hContentType, "text/plain; charset=utf-8")]
184+ "Bad Request"
185+ | Just (ConnectionError (UnknownErrorCode 413) t) <-
186+ fromException e = responseLBS H.status413
187+ [(H.hContentType, "text/plain; charset=utf-8")]
188+ (fromStrict t)
189+ | Just (ConnectionError (UnknownErrorCode 431) t) <-
190+ fromException e = responseLBS H.status431
191+ [(H.hContentType, "text/plain; charset=utf-8")]
192+ (fromStrict t)
177− | otherwise = responseLBS H.internalServerError500 [(H.hContentType, "text/plain; charset=utf-8")] "Something went wrong"
193+ | otherwise = responseLBS H.internalServerError500
194+ [(H.hContentType, "text/plain; charset=utf-8")]
195+ "Something went wrong"
178
196
179-- | Exception handler for the debugging purpose.
197-- | Exception handler for the debugging purpose.
180-- 500, text/plain, a showed exception.
198-- 500, text/plain, a showed exception.
warp/warp.cabal+1−1
@@ -1,5 +1,5 @@
1Name: warp
1Name: warp
2−Version: 3.2.26
2+Version: 3.2.27
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