async-http-client
async-http-client copied to clipboard
for debugging purposes, we should offer an option to switch on plain text .pcap recording
for debugging purposes, we should offer an option to switch on plain text .pcap recording.
It should be very clear that it's for debugging because NIOWritePCAPHandler.SynchronisedFileSink blocks the event loop (we could write an async file sink ofc)...
A pretty terrible patch that can serve as example (but not the final impl ofc) is here:
diff --git a/Package.swift b/Package.swift
index bce6b69..0575c2d 100644
--- a/Package.swift
+++ b/Package.swift
@@ -31,7 +31,7 @@ let package = Package(
.target(
name: "AsyncHTTPClient",
dependencies: ["NIO", "NIOHTTP1", "NIOSSL", "NIOConcurrencyHelpers", "NIOHTTPCompression",
- "NIOFoundationCompat", "NIOTransportServices", "Logging"]
+ "NIOFoundationCompat", "NIOTransportServices", "Logging", "NIOExtras"]
),
.testTarget(
name: "AsyncHTTPClientTests",
diff --git a/Sources/AsyncHTTPClient/ConnectionPool.swift b/Sources/AsyncHTTPClient/ConnectionPool.swift
index 46e0b61..03abfc3 100644
--- a/Sources/AsyncHTTPClient/ConnectionPool.swift
+++ b/Sources/AsyncHTTPClient/ConnectionPool.swift
@@ -20,6 +20,7 @@ import NIOHTTP1
import NIOHTTPCompression
import NIOTLS
import NIOTransportServices
+import NIOExtras
/// A connection pool that manages and creates new connections to hosts respecting the specified preferences
///
@@ -257,6 +258,10 @@ struct ConnectionKey: Hashable {
}
}
+let fileSink = try? NIOWritePCAPHandler.SynchronizedFileSink.fileSinkWritingToFile(path: "/tmp/ahc-\(getpid()).pcap") { error in
+ print("AHC ERROR: something went wrong creating the file sink: \(error)")
+}
+
/// A connection provider of `HTTP/1.1` connections with a given `Key` (host, scheme, port)
///
/// On top of enabling connection reuse this provider it also facilitates the creation
@@ -529,9 +534,22 @@ class HTTP1ConnectionProvider {
let requiresSSLHandler = self.configuration.proxy != nil && self.key.scheme.requiresTLS
let handshakePromise = channel.eventLoop.makePromise(of: Void.self)
+
channel.pipeline.addSSLHandlerIfNeeded(for: self.key, tlsConfiguration: self.configuration.tlsConfiguration, addSSLClient: requiresSSLHandler, handshakePromise: handshakePromise)
+ func addWritePCAP() -> EventLoopFuture<Void> {
+ if let fileSink = fileSink {
+ return channel.pipeline.addHandler(NIOWritePCAPHandler(mode: .client, fileSink: fileSink.write(buffer:)),
+ position: .last)
+ } else {
+ return channel.eventLoop.makeSucceededFuture(())
+ }
+
+ }
+
return handshakePromise.futureResult.flatMap {
+ addWritePCAP()
+ }.flatMap {
channel.pipeline.addHTTPClientHandlers(leftOverBytesStrategy: .forwardBytes)
}.flatMap {
#if canImport(Network)
more hacky but newer patch for this
diff --git a/Package.swift b/Package.swift
index f72de84..d572ea0 100644
--- a/Package.swift
+++ b/Package.swift
@@ -39,6 +39,7 @@ let package = Package(
.product(name: "NIO", package: "swift-nio"),
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOPosix", package: "swift-nio"),
+ .product(name: "NIOExtras", package: "swift-nio-extras"),
.product(name: "NIOHTTP1", package: "swift-nio"),
.product(name: "NIOConcurrencyHelpers", package: "swift-nio"),
.product(name: "NIOHTTP2", package: "swift-nio-http2"),
diff --git a/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionPool+Factory.swift b/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionPool+Factory.swift
index 1444df9..bd57a18 100644
--- a/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionPool+Factory.swift
+++ b/Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionPool+Factory.swift
@@ -20,6 +20,8 @@ import NIOPosix
import NIOSOCKS
import NIOSSL
import NIOTLS
+import NIOExtras
+import Foundation
#if canImport(Network)
import NIOTransportServices
#endif
@@ -448,6 +450,17 @@ extension HTTPConnectionPool.ConnectionFactory {
.tlsOptions(options)
.channelInitializer { channel in
do {
+ let fileSink = try! NIOWritePCAPHandler.SynchronizedFileSink.fileSinkWritingToFile(path: "/tmp/packets-nw-\(sslServerHostname ?? "unknown")-\(UUID())-\(getpid())-\(Date()).pcap",
+ errorHandler: { error in
+ logger.error("ouch: \(error)")
+ })
+
+ try sync.addHandler(NIOWritePCAPHandler(mode: .client,
+ fileSink: fileSink.write(buffer:)))
+ channel.closeFuture.whenComplete { _ in
+ try! fileSink.syncClose()
+ }
+
try channel.pipeline.syncOperations.addHandler(HTTPClient.NWErrorHandler())
try channel.pipeline.syncOperations.addHandler(NWWaitingHandler(requester: requester, connectionID: connectionID))
// we don't need to set a TLS deadline for NIOTS connections, since the
@@ -485,6 +498,16 @@ extension HTTPConnectionPool.ConnectionFactory {
try sync.addHandler(sslHandler)
try sync.addHandler(tlsEventHandler)
+ let fileSink = try! NIOWritePCAPHandler.SynchronizedFileSink.fileSinkWritingToFile(path: "/tmp/packets-\(sslServerHostname ?? "unknown")-\(UUID())-\(getpid())-\(Date()).pcap",
+ errorHandler: { error in
+ logger.error("ouch: \(error)")
+ })
+
+ try sync.addHandler(NIOWritePCAPHandler(mode: .client,
+ fileSink: fileSink.write(buffer:)))
+ channel.closeFuture.whenComplete { _ in
+ try! fileSink.syncClose()
+ }
return channel.eventLoop.makeSucceededVoidFuture()
} catch {
return channel.eventLoop.makeFailedFuture(error)