I'm working on my HTTP/HTTPS proxy application, which can encrypt stream between local and remote proxy server, like the following.
Client <----> Proxy <==== Encrypted Stream Over Internet ====> Remote Proxy <----> Host
After some modification on the example ConnectHandler Example, I've made it work on HTTPS request, but had problem on the HTTP request. The following is some piece of my ChannelInboundHandler for HTTP requests.
final class HTTPChannelHandler<ChannelHandler: ChannelDuplexHandler & RemovableChannelHandler> where ChannelHandler.InboundIn == HTTPServerRequestPart, ChannelHandler.InboundOut == HTTPClientRequestPart, ChannelHandler.OutboundIn == HTTPClientResponsePart, ChannelHandler.OutboundOut == HTTPServerResponsePart {
typealias InboundIn = HTTPServerRequestPart
typealias InboundOut = HTTPClientRequestPart
typealias OutboundIn = HTTPClientResponsePart
typealias OutboundOut = HTTPServerResponsePart
}
extension HTTPChannelHandler: RemovableChannelHandler {
func removeHandler(context: ChannelHandlerContext, removalToken: ChannelHandlerContext.RemovalToken) {
removeHandler(context: context)
context.leavePipeline(removalToken: removalToken)
}
private func removeHandler(context: ChannelHandlerContext) {
guard let channelHandler = self.channelHandler else { return }
if case let .pendingConnection(head) = self.state {
self.state = .connected
context.fireChannelRead(channelHandler.wrapInboundOut(.head(head)))
if let body = self.bufferedBody {
context.fireChannelRead(channelHandler.wrapInboundOut(.body(.byteBuffer(body))))
}
if let bufferedEnd = self.bufferedEnd {
context.fireChannelRead(channelHandler.wrapInboundOut(.end(bufferedEnd)))
self.bufferedEnd = nil
}
context.fireChannelReadComplete()
}
}
}
The GlueHandler
is from the example but have a cryptor in between, and works on ByteBuffer instead of NIOAny
extension GlueHandler: ChannelDuplexHandler {
typealias InboundIn = ByteBuffer
typealias OutboundIn = ByteBuffer
typealias OutboundOut = ByteBuffer
}
extension GlueHandler {
private func partnerWrite(_ data: NIOAny, promise: EventLoopPromise<Void>? = nil) {
if isLocalChannel {
var buffer = self.unwrapInboundIn(data)
// encrypt buffer and context.wrtie
} else {
var buffer = self.unwrapInboundIn(data)
// decrypt buffer and context.wrtie
}
}
}
The pipeline like the following
[I] ↓↑ [O]
ByteToMessageHandler<HTTPDecoder<HTTPPart<HTTPRequestHead, ByteBuffer>, HTTPPart<HTTPResponseHead, IOData>>> ↓↑ [handler0]
↓↑ HTTPResponseEncoder [handler1]
ConnectHandler ↓↑ ConnectHandler [handler2]
↓↑
GlueHandler ↓↑ GlueHandler [handler3]
But I've got the fatal error
NIOCore/NIOAny.swift:101: Fatal error: tried to decode as type ByteBuffer but found HTTPPart<HTTPRequestHead, IOData> with contents other(NIOHTTP1.HTTPPart<NIOHTTP1.HTTPRequestHead, NIOCore.IOData>.head(HTTPRequestHead { method: GET, uri: "/get", version: HTTP/1.1, headers: [("Host", "httpbin.org"), ("User-Agent", "curl/8.4.0"), ("Accept", "*/*"), ("Proxy-Connection", "Keep-Alive")] }))
I know the InboundOut of my HTTPChannelHandler is HTTPClientRequestPart, but the InboundIn of GlueHandler is ByteBuffer, so there is a type mismatch. I've tried to add a HTTPRequestEncoder after HTTPChannelHandler, but looks HTTPRequestEncoder only works on Outbound. I want to know how to fix this, is there somthing I did wrong?