Skip to content
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

CIO server HTTPS support on JVM #2929

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

olme04
Copy link
Contributor

@olme04 olme04 commented Mar 30, 2022

Subsystem
Server, CIO, Network

Motivation
Fixes https://youtrack.jetbrains.com/issue/KTOR-694

Solution
Sses JDK SSLEngine for TLS implementation
Also, supports client TLS, but not used for now

TODO:

  • one test failed - HttpServerCommonTestSuite.testProxyHeaders - not sure what is the reason

@e5l
Copy link
Member

e5l commented May 17, 2022

Please change the target branch to 2.1

@@ -145,7 +145,7 @@ public class CIOApplicationEngine(

try {
environment.connectors.forEach { connectorSpec ->
if (connectorSpec.type == ConnectorType.HTTPS) {
if (connectorSpec.type == ConnectorType.HTTPS && !PlatformUtils.IS_JVM) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would extract a condition to check if the platform supports tls

@@ -50,3 +51,8 @@ public suspend fun Socket.tls(
*/
public actual suspend fun Socket.tls(coroutineContext: CoroutineContext, block: TLSConfigBuilder.() -> Unit): Socket =
tls(coroutineContext, TLSConfigBuilder().apply(block).build())

@InternalAPI
public fun Socket.tls(coroutineContext: CoroutineContext, engine: SSLEngine): Socket {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add KDoc

private val wrapLock = Mutex()
private var wrapDestination = bufferAllocator.allocatePacket(0)

suspend fun wrapAndWrite(wrapSource: ByteBuffer): SSLEngineResult = wrapLock.withLock {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you tell me why do we need a lock here?

}

@Suppress("BlockingMethodInNonBlockingContext")
private suspend fun wrapAndWriteX(wrapSource: ByteBuffer): SSLEngineResult {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrapAndWriteSuspend

return result
}

suspend fun close(cause: Throwable?): SSLEngineResult = wrapLock.withLock {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suspend close is not a good idea, I would try avoiding this

}
val reader = socket.openReadChannel()
repeat(3) {
println("CCC: ${assertNotNull(reader.readUTF8Line())}")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please drop printlns

while (true) {
when (status) {
SSLEngineResult.HandshakeStatus.NEED_TASK -> {
coroutineScope {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coroutineScope will create a new job for every NEED_TASK status.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can remove this, but delegatedTask is potentially blocking task, and then, we should make sure, that when creating TLS socket, we don't provide Dispatchers.Default or other non IO dispatcher to it constructor.

coroutineScope {
while (true) {
val task = engine.delegatedTask ?: break
launch(Dispatchers.IO) { task.run() }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

}

private suspend fun doClose(cause: Throwable?) = lock.withLock {
if (closed.compareAndSet(expect = false, update = true)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (!closed.compareAndSet(expect = false, update = true)) return

buffer: ByteBuffer,
flip: Boolean,
allocate: (length: Int) -> ByteBuffer
): ByteBuffer {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we just make large enough buffers upfront and use a regular pool? the maximum packet size is limited by the protocol

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sslengine max buffer size is 16K, and we need up to 4 such buffers for every TLS socket implementation, so 64K per socket.
For client side - it's ok, as there will not so much active sockets at the same time, but for server side it can cause memory issues with a lot of clients.
Note: most of the time, those 16K are not fully used, and buffer of smaller size is enough to perform TLS operations

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants