-
Notifications
You must be signed in to change notification settings - Fork 118
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 test for HTTP1 request with large header #658
Changes from 6 commits
0aecc2b
c43bccc
fb64416
cb026ca
a7f64d4
3f8f55f
efa86b5
5f5dc1e
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 |
---|---|---|
|
@@ -35,8 +35,8 @@ final class HTTP1ClientChannelHandler: ChannelDuplexHandler { | |
didSet { | ||
if let newRequest = self.request { | ||
var requestLogger = newRequest.logger | ||
requestLogger[metadataKey: "ahc-connection-id"] = "\(self.connection.id)" | ||
requestLogger[metadataKey: "ahc-el"] = "\(self.connection.channel.eventLoop)" | ||
requestLogger[metadataKey: "ahc-connection-id"] = self.connectionIdLoggerMetadata | ||
requestLogger[metadataKey: "ahc-el"] = "\(self.eventLoop)" | ||
self.logger = requestLogger | ||
|
||
if let idleReadTimeout = newRequest.requestOptions.idleReadTimeout { | ||
|
@@ -59,15 +59,15 @@ final class HTTP1ClientChannelHandler: ChannelDuplexHandler { | |
|
||
private let backgroundLogger: Logger | ||
private var logger: Logger | ||
private let eventLoop: EventLoop | ||
private let connectionIdLoggerMetadata: Logger.MetadataValue | ||
|
||
let connection: HTTP1Connection | ||
let eventLoop: EventLoop | ||
|
||
init(connection: HTTP1Connection, eventLoop: EventLoop, logger: Logger) { | ||
self.connection = connection | ||
var onRequestCompleted: () -> Void = {} | ||
init(eventLoop: EventLoop, backgroundLogger: Logger, connectionIdLoggerMetadata: Logger.MetadataValue) { | ||
self.eventLoop = eventLoop | ||
self.backgroundLogger = logger | ||
self.logger = self.backgroundLogger | ||
self.backgroundLogger = backgroundLogger | ||
self.logger = backgroundLogger | ||
self.connectionIdLoggerMetadata = connectionIdLoggerMetadata | ||
} | ||
|
||
func handlerAdded(context: ChannelHandlerContext) { | ||
|
@@ -108,6 +108,7 @@ final class HTTP1ClientChannelHandler: ChannelDuplexHandler { | |
|
||
let action = self.state.writabilityChanged(writable: context.channel.isWritable) | ||
self.run(action, context: context) | ||
context.fireChannelWritabilityChanged() | ||
} | ||
|
||
func channelRead(context: ChannelHandlerContext, data: NIOAny) { | ||
|
@@ -156,6 +157,7 @@ final class HTTP1ClientChannelHandler: ChannelDuplexHandler { | |
metadata: req.requestFramingMetadata | ||
) | ||
self.run(action, context: context) | ||
promise?.succeed(()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't seem appropriate. We should presumably send the promise on with the appropriate data, rather than silently succeeding it here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch. I have reverted it now. I though I needed it because |
||
} | ||
|
||
func read(context: ChannelHandlerContext) { | ||
|
@@ -274,7 +276,7 @@ final class HTTP1ClientChannelHandler: ChannelDuplexHandler { | |
if shouldClose { | ||
context.close(promise: nil) | ||
} else { | ||
self.connection.taskCompleted() | ||
self.onRequestCompleted() | ||
} | ||
|
||
oldRequest.succeedRequest(buffer) | ||
|
@@ -286,7 +288,7 @@ final class HTTP1ClientChannelHandler: ChannelDuplexHandler { | |
|
||
context.writeAndFlush(self.wrapOutboundOut(.end(nil)), promise: writePromise) | ||
case .informConnectionIsIdle: | ||
self.connection.taskCompleted() | ||
self.onRequestCompleted() | ||
oldRequest.succeedRequest(buffer) | ||
} | ||
|
||
|
@@ -303,7 +305,7 @@ final class HTTP1ClientChannelHandler: ChannelDuplexHandler { | |
oldRequest.fail(error) | ||
|
||
case .informConnectionIsIdle: | ||
self.connection.taskCompleted() | ||
self.onRequestCompleted() | ||
oldRequest.fail(error) | ||
|
||
case .failWritePromise(let writePromise): | ||
|
@@ -328,6 +330,7 @@ final class HTTP1ClientChannelHandler: ChannelDuplexHandler { | |
// we must check if the request is still present here. | ||
guard let request = self.request else { return } | ||
request.requestHeadSent() | ||
|
||
request.resumeRequestBodyStream() | ||
} else { | ||
context.write(self.wrapOutboundOut(.head(head)), promise: nil) | ||
|
@@ -434,6 +437,11 @@ final class HTTP1ClientChannelHandler: ChannelDuplexHandler { | |
} | ||
} | ||
|
||
#if swift(>=5.6) | ||
@available(*, unavailable) | ||
extension HTTP1ClientChannelHandler: Sendable {} | ||
#endif | ||
|
||
extension HTTP1ClientChannelHandler: HTTPRequestExecutor { | ||
func writeRequestBodyPart(_ data: IOData, request: HTTPExecutableRequest, promise: EventLoopPromise<Void>?) { | ||
if self.eventLoop.inEventLoop { | ||
|
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.
Why are we adding this indirection here?
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.
We need it to break the dependency between
HTTP1Connection
andHTTP1ClientChannelHandler
.HTTP1ClientChannelHandler
only needs a reference toHTTP1Connection
to get the connection id for logging and to signal that the last request is complete. Theconnection.id
is now passed asconnectionIdLoggerMetadata: Logger.MetadataValue
in the init.connection.taskComplete()
is replaced withonRequestCompleted()
indirection.I think in the long run we should replace this with a write promise we pass together with the request here:
async-http-client/Sources/AsyncHTTPClient/ConnectionPool/HTTP1/HTTP1Connection.swift
Line 101 in 817d9aa
This will cost us an additional allocation per request but this should be fine.
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.
Yes, this was my reaction: having unstructured callbacks like this is a bit of a worry for me. Promises are a substantial improvement.
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.
Is there any reason not to make that change soon?
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.
HTTP2 currently uses the same state machine and doesn't need to store separate promise because it creates a new stream and therefore a new channel for each request. It uses the
channel.closeFuture
to detect that the request is done (well it will soon #657). So it doesn't need a separate promise but it also doesn't hurt either.At the end nothing technically is blocking this, it's just work that need to be 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.
Ok, I'm asking only because I suspect this mystery-meat callback will live here forever if we don't make the change now.
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.
So I have tried to do that. The problem I run into is that it is currently not really used like a promise and may never be called. It is used to signal that the connection is again able to receive further request which it may never be for various reasons. This case is currently handled by closing the channel instead of calling this callback. At the end it just calls down to
connection.delegate.http1ConnectionReleased(connection)
I think changing and testing this is quite some effort which I would like not to do right now.
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 have at least renamed it to
onConnectionIdle
to better explain what it actually does: 5f5dc1e