-
Notifications
You must be signed in to change notification settings - Fork 191
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add the possibility to set the maximum number of header fields #549
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Two pieces of feedback:
- Can you update the version number in the cabal file too?
- It's slightly surprising that the new config value is a
Maybe Int
but it comes with a default of 100 and doesn't appear to have a way to set it toNothing
. Not a strong comment, but something more consistent might make sense.
Thanks for having a look :)
done
I used the same strategy of
I think we introduced some extra complexity, not really required. The main purpose of configuring those parameters to If we would like to continue to use a cc: @tfausak |
I don't remember the reasons behind the max header length decisions. I'm fine with any approach. Even if we went over to @tfausak any opinion from you on which way to go? |
I like the idea of removing There may be no need for a special "unlimited" value. You could just set it to |
I had a look here https://www.rfc-editor.org/rfc/rfc2616#page-31, and it seems that header fields can also be omitted, so We could also use [1] marinelli/http-client@set-max-headers...marinelli:http-client:use-ints |
Technically the RFC allows zero headers, but practically the
|
I don't know, it's a bit confusing to me to assign to |
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! I like removing the Maybe
from both fields.
case fmap unMaxHeaderLength mhl of | ||
Nothing -> pure () | ||
Just n -> when (total' > n) $ throwHttp OverlongHeaders | ||
when (total >= unMaxHeaderLength mhl && total /= 0) $ do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to compare against the new total, not the old one. And it should be checking to see if the MaxHeaderLength
is 0, not the total.
when (total >= unMaxHeaderLength mhl && total /= 0) $ do | |
let n = unMaxHeaderLength mhl | |
total' = total + fromIntegral (S.length bs) | |
when (total' >= n && n /= 0) $ do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know why I made this change :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #549 (comment)
parseHeaders 100 _ = throwHttp OverlongHeaders | ||
guardMaxNumberHeaders :: Word -> IO () | ||
guardMaxNumberHeaders count = | ||
when (count >= unMaxNumberHeaders mnh && count /= 0) $ do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same deal here:
when (count >= unMaxNumberHeaders mnh && count /= 0) $ do | |
let n = unMaxNumberHeaders mnh | |
in when (count >= n && n /= 0) $ do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #549 (comment)
, managerMaxHeaderLength :: Maybe MaxHeaderLength | ||
, managerMaxHeaderLength :: MaxHeaderLength | ||
-- ^ Configure the maximum size, in bytes, of an HTTP header field. | ||
-- Set it to 0 to remove this limit (eg: for debugging purposes). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's necessary to use 0 as a sentinel value. maxBound :: Word32
is more than 4 GB, and maxBound :: Word64
is unfathomably large. In other words, using maxBound
would effectively disable these checks anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, and this is why I just removed the n /= 0
condition from the previous predicates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #549 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's avoid any backwards-incompatible changes to the library, including to the internal modules. We can implement new features using the preferred approach though.
http-client/Network/HTTP/Client.hs
Outdated
@@ -322,9 +323,14 @@ managerSetProxy :: ProxyOverride -> ManagerSettings -> ManagerSettings | |||
managerSetProxy po = managerSetInsecureProxy po . managerSetSecureProxy po | |||
|
|||
-- @since 0.7.17 | |||
managerSetMaxHeaderLength :: Int -> ManagerSettings -> ManagerSettings | |||
managerSetMaxHeaderLength :: Word -> ManagerSettings -> ManagerSettings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a breaking change, I do not want to include it. It's not worth the ecosystem churn for a small improvement in type correctness.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, I actually thought about it, and I didn't change it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But... are you talking about managerSetMaxHeaderLength
only? I would just revert to use Word
for both managerSetMaxHeaderLength
and managerSetMaxNumberHeaders
, and also the newtypes used internally for distinguishing those values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I missed your main comment above. Personally I would avoid using Int
in one place and Word
in an other, it would be confusing to me: why one max value is an int an an other a word?
If we want to refine a bit the types of the interface, let's do that in a new version like 0.8.0, we could put there multiple breaking changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not in favor of a breaking change for this topic. I'll leave the choice to you: inconsistency in the API, or using Int
in both places. My only stance is avoiding breaking changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the end I reverted to the first original implementation, in this way we have similar settings for managerMaxHeaderLength
and managerMaxNumberHeaders
(we could also rename it to managerMaxNumberHeaderFields
). I prefer to have a more uniform interface, it improves readability.
-- | ||
-- @since 0.5.0 | ||
| TooManyHeaders |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The introduction of this error might be considered a breaking change, but it also simplified debugging the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is acceptable, we've documented that adding extra constructors to error types is something end users should expect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I renamed it to TooManyHeaderFields
, it's more clear.
http-conduit/test/main.hs
Outdated
@@ -261,6 +262,18 @@ main = do | |||
let Just req1 = parseUrlThrow $ "http://127.0.0.1:" ++ show port | |||
_ <- httpLbs req1 manager | |||
return () | |||
it "too many header fields" $ tooManyHeaderFields $ \port -> do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just wrote those tests here, but we could move the entire DOS protection tests in the http-client
package, we do not really need a data stream to raise the OverlongHeaders
exception.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved the tests for TooManyHeaderFields
in the http-client
package. I tried to move also the ones for OverlongHeaders
, without success. I didn't find the correct way to simulate it (something similar to what I did for TooManyHeaderFields
).
f57e83d
to
a104b38
Compare
http-client/Network/HTTP/Client.hs
Outdated
@@ -324,7 +325,12 @@ managerSetProxy po = managerSetInsecureProxy po . managerSetSecureProxy po | |||
-- @since 0.7.17 | |||
managerSetMaxHeaderLength :: Int -> ManagerSettings -> ManagerSettings | |||
managerSetMaxHeaderLength l manager = manager | |||
{ managerMaxHeaderLength = Just $ MaxHeaderLength l } | |||
{ managerMaxHeaderLength = MaxHeaderLength l } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should also not change, yes it's an internal module, but there's no need to break end-user code for this.
-- | ||
-- @since 0.5.0 | ||
| TooManyHeaders |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is acceptable, we've documented that adding extra constructors to error types is something end users should expect.
This PR adds the possibility to configure the maximum number of HTTP header fields in a message.
This is similar to what it was done for configuring the maximum size of an HTTP header field (#514).