-
Notifications
You must be signed in to change notification settings - Fork 133
check body length #255
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
check body length #255
Changes from 1 commit
be4f9aa
a7c4977
3006123
34ad4a6
044765e
97a8e96
7f31443
715ab14
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -651,6 +651,8 @@ internal class TaskHandler<Delegate: HTTPClientResponseDelegate>: RemovableChann | |
let logger: Logger // We are okay to store the logger here because a TaskHandler is just for one request. | ||
|
||
var state: State = .idle | ||
var expectedBodyLength: Int? = nil | ||
var actualBodyLength: Int = 0 | ||
var pendingRead = false | ||
var mayRead = true | ||
var closing = false { | ||
|
@@ -794,11 +796,19 @@ extension TaskHandler: ChannelDuplexHandler { | |
assert(head.version == HTTPVersion(major: 1, minor: 1), | ||
"Sending a request in HTTP version \(head.version) which is unsupported by the above `if`") | ||
|
||
self.expectedBodyLength = head.headers[canonicalForm: "content-length"].first.flatMap { Int($0) } | ||
|
||
context.write(wrapOutboundOut(.head(head))).map { | ||
self.callOutToDelegateFireAndForget(value: head, self.delegate.didSendRequestHead) | ||
}.flatMap { | ||
self.writeBody(request: request, context: context) | ||
}.flatMap { | ||
if let expectedBodyLength = self.expectedBodyLength, expectedBodyLength != self.actualBodyLength { | ||
weissi marked this conversation as resolved.
Show resolved
Hide resolved
weissi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.state = .end | ||
weissi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
let error = HTTPClientError.bodyLengthMismatch | ||
self.failTaskAndNotifyDelegate(error: error, self.delegate.didReceiveError) | ||
return context.eventLoop.makeFailedFuture(error) | ||
} | ||
context.eventLoop.assertInEventLoop() | ||
return context.writeAndFlush(self.wrapOutboundOut(.end(nil))) | ||
}.map { | ||
|
@@ -836,6 +846,7 @@ extension TaskHandler: ChannelDuplexHandler { | |
} | ||
|
||
return promise.futureResult.map { | ||
self.actualBodyLength += part.readableBytes | ||
|
||
self.callOutToDelegateFireAndForget(value: part, self.delegate.didSendRequestPart) | ||
} | ||
}) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2051,4 +2051,35 @@ class HTTPClientTests: XCTestCase { | |
|
||
XCTAssertNoThrow(try future.wait()) | ||
} | ||
|
||
func testContentLengthTooLongFails() { | ||
let url = self.defaultHTTPBinURLPrefix + "/post" | ||
XCTAssertThrowsError( | ||
try self.defaultClient.execute(request: | ||
Request(url: url, | ||
body: .stream(length: 10) { streamWriter in | ||
streamWriter.write(.byteBuffer(ByteBuffer(string: "1"))) | ||
})).wait()) { error in | ||
XCTAssertEqual(error as! HTTPClientError, HTTPClientError.bodyLengthMismatch) | ||
} | ||
// Quickly try another request and check that it works. | ||
XCTAssertNoThrow(try self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "/get").wait()) | ||
|
||
} | ||
|
||
// currently gets stuck because of #250 the server just never replies | ||
func testContentLengthTooShortFails() { | ||
let url = self.defaultHTTPBinURLPrefix + "/post" | ||
let tooLong = "XBAD BAD BAD NOT HTTP/1.1\r\n\r\n" | ||
XCTAssertThrowsError( | ||
try self.defaultClient.execute(request: | ||
Request(url: url, | ||
body: .stream(length: 1) { streamWriter in | ||
streamWriter.write(.byteBuffer(ByteBuffer(string: tooLong))) | ||
})).wait()) { error in | ||
XCTAssertEqual(error as! HTTPClientError, HTTPClientError.bodyLengthMismatch) | ||
} | ||
// Quickly try another request and check that it works. If we by accident wrote some extra bytes into the | ||
// stream (and reuse the connection) that could cause problems. | ||
XCTAssertNoThrow(try self.defaultClient.get(url: self.defaultHTTPBinURLPrefix + "/get").wait()) | ||
|
||
} | ||
} |
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 error if there's more than one CL header
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.
shouldn't
validateHeaders
do that?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 can add a return from
validate
with body length, as an alternativeUh oh!
There was an error while loading. Please reload this page.
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 happy if we have a test that proves that we error if the user sends two
content-length
s. In this code, I think we should just add anto be sure. That makes the code much easier to read because you can immediately dismiss that thought if you see the assertion.
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.
it's even better, we remove all content length headers and insert them ourselves :) except one case, I'll add a test for that (and an assert).
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.
That will happen only if the stream has no length is not specified. In all other cases we actually ignore the user-provided headers, you think we should fail on those?
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.
@artemredkin wait, why are we looking for the first CL header then if this code is only run if the user specified no CL headers?
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 code is run always, if the content-length was not specified, then
expectedLength
will benil
and final check will not be executedThere 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.
alright, let's just add the assertion&test (if not present for all the possible cases) in case somebody modifies the sanitisation 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.
done!