You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何处理Ktor HTTPS服务器的非SSL客户端机器人连接问题

解决Ktor HTTPS服务器非SSL机器人连接的日志问题

问题根源

你遇到的TLS handshake failed日志是Netty在SSL握手阶段抛出的,这发生在Ktor的请求处理流程(包括Request Validation插件)之前——也就是说,这些非法请求根本没到Ktor的业务逻辑层,所以你配置的Request Validation完全不会触发。

解决方案

方案1:精准抑制特定日志(最简单)

直接修改logback.xml,针对产生WARN日志的特定Netty Logger设置更高的级别(比如ERROR或OFF),就能阻止这些日志输出:

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{dd-MM-YYYY HH:mm:ss.SSS} [%thread] [%-5level] %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="trace">
        <appender-ref ref="STDOUT"/>
    </root>
    <logger name="org.eclipse.jetty" level="WARN"/>
    <logger name="io.netty" level="WARN"/>
    <!-- 新增:抑制特定SSL握手失败的WARN日志 -->
    <logger name="io.netty.handler.ssl.ApplicationProtocolNegotiationHandler" level="OFF"/>
    <logger name="io.ktor" level="WARN"/>
</configuration>

方案2:自定义Netty Channel处理异常(更灵活)

如果不想完全关闭日志,而是想静默处理这类异常,可以在Ktor的Netty引擎配置中添加自定义的ChannelHandler,捕获NotSslRecordException并直接关闭连接,不传播异常:

fun main() {
    embeddedServer(Netty, port = 1234, sslPort = 1234) {
        engine {
            pipeline.addLast("ssl-exception-handler", object : ChannelDuplexHandler() {
                override fun exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable) {
                    if (cause is NotSslRecordException) {
                        // 静默关闭连接,不记录日志
                        ctx.close()
                    } else {
                        // 其他异常正常传播
                        super.exceptionCaught(ctx, cause)
                    }
                }
            })
        }
        module()
    }.start(wait = true)
}

方案3:修改Netty的SSL Handler配置

另一种方式是自定义Netty的SslHandler,让它忽略NotSslRecordException:

fun createSslContext(): SslContext {
    // 替换为你的SSL上下文创建逻辑(加载证书、密钥等)
    val originalContext = SslContextBuilder.forServer(File("cert.pem"), File("key.pem"))
        .build()
    
    return object : SslContext(originalContext.context()) {
        override fun newHandler(allocator: ByteBufAllocator, peerHost: String?, peerPort: Int): SslHandler {
            val originalHandler = super.newHandler(allocator, peerHost, peerPort)
            return object : SslHandler(originalHandler.engine()) {
                override fun exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable) {
                    if (cause is NotSslRecordException) {
                        ctx.close()
                    } else {
                        super.exceptionCaught(ctx, cause)
                    }
                }
            }
        }
    }
}

// 在Ktor中使用自定义SSL上下文
embeddedServer(Netty, port = 1234, sslPort = 1234) {
    configureSsl {
        sslContext = createSslContext()
    }
    module()
}.start(wait = true)

总结

  • 方案1最快捷,直接通过日志配置解决,不需要修改业务代码;
  • 方案2和3更灵活,可以针对这类异常做自定义处理(比如统计请求量但不打WARN日志)。

内容的提问来源于stack exchange,提问作者Cool_Coder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 22:05:54