diff --git a/.idea/libraries/ktor_server_jetty.xml b/.idea/libraries/ktor_server_jetty.xml
new file mode 100644
index 000000000000..5ae8a4cf89ab
--- /dev/null
+++ b/.idea/libraries/ktor_server_jetty.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityLibraryLicenses.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityLibraryLicenses.groovy
index d49cffe0b368..d0e729b79bd4 100644
--- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityLibraryLicenses.groovy
+++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityLibraryLicenses.groovy
@@ -440,6 +440,11 @@ class CommunityLibraryLicenses {
license: "Apache 2.0",
licenseUrl: "http://www.apache.org/licenses/LICENSE-2.0",
url: "https://github.com/ktorio/ktor"),
+ new LibraryLicense(name: "ktor-server-jetty",
+ libraryName: "ktor-server-jetty",
+ license: "Apache 2.0",
+ licenseUrl: "http://www.apache.org/licenses/LICENSE-2.0",
+ url: "https://github.com/ktorio/ktor"),
new LibraryLicense(name: "ktor-websockets",
libraryName: "ktor-websockets",
license: "Apache 2.0",
diff --git a/plugins/space/intellij.space.iml b/plugins/space/intellij.space.iml
index 12075c0c4365..340cf24b9b1d 100644
--- a/plugins/space/intellij.space.iml
+++ b/plugins/space/intellij.space.iml
@@ -107,6 +107,7 @@
+
diff --git a/plugins/space/src/main/kotlin/com/intellij/space/auth/SpaceOAuthRedirectHandler.kt b/plugins/space/src/main/kotlin/com/intellij/space/auth/SpaceOAuthRedirectHandler.kt
index c5b1ae45d6cc..6cf40169b247 100644
--- a/plugins/space/src/main/kotlin/com/intellij/space/auth/SpaceOAuthRedirectHandler.kt
+++ b/plugins/space/src/main/kotlin/com/intellij/space/auth/SpaceOAuthRedirectHandler.kt
@@ -1,14 +1,19 @@
package com.intellij.space.auth
+import com.intellij.openapi.application.ApplicationManager
import com.intellij.util.concurrency.AppExecutorUtil
-import io.ktor.network.selector.ActorSelectorManager
-import io.ktor.network.sockets.ServerSocket
-import io.ktor.network.sockets.aSocket
-import io.ktor.network.sockets.openReadChannel
-import io.ktor.network.sockets.openWriteChannel
-import io.ktor.utils.io.readUTF8Line
-import io.ktor.utils.io.writeStringUtf8
-import kotlinx.coroutines.*
+import io.ktor.application.call
+import io.ktor.http.ContentType
+import io.ktor.request.uri
+import io.ktor.response.respondText
+import io.ktor.routing.get
+import io.ktor.routing.routing
+import io.ktor.server.engine.embeddedServer
+import io.ktor.server.jetty.Jetty
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Runnable
+import kotlinx.coroutines.asCoroutineDispatcher
import libraries.coroutines.extra.Lifetime
import libraries.coroutines.extra.launch
import java.io.IOException
@@ -16,51 +21,51 @@ import java.io.IOException
private val ioDispatcher = AppExecutorUtil.getAppExecutorService().asCoroutineDispatcher()
internal suspend fun startRedirectHandling(lifetime: Lifetime, ports: Collection): SpaceRedirectHandlingInfo? {
- val redirectUrl = CompletableDeferred()
- val freePort = occupyAvailablePort(lifetime, ports) { serverSocket ->
- val socket = serverSocket.accept()
- launch(lifetime, ioDispatcher) {
- val response = ""
- val output = socket.openWriteChannel(autoFlush = true)
- output.writeStringUtf8(
- """
- HTTP/1.1 200 OK
- Content-Type: text/html
- Content-Length: ${response.length}
-
- $response
- """.trimIndent()
- )
- }
-
- val input = socket.openReadChannel()
- val line = input.readUTF8Line()!!.substringAfter("/").substringBefore(" ")
- redirectUrl.complete(line)
- } ?: return null
- return SpaceRedirectHandlingInfo(freePort, redirectUrl)
-}
-
-private suspend fun occupyAvailablePort(lifetime: Lifetime, ports: Collection, withFreePort: suspend (ServerSocket) -> Unit): Int? {
- val reservedPort = CompletableDeferred()
+ val info = CompletableDeferred()
launch(lifetime, ioDispatcher) {
for (port in ports.distinct().shuffled()) {
try {
- aSocket(ActorSelectorManager(ioDispatcher)).tcp().bind(port = port).use { socket ->
- reservedPort.complete(port)
- withFreePort(socket)
- }
+ val redirectUrl = startAuthServerAsync(lifetime, port)
+ info.complete(SpaceRedirectHandlingInfo(port, redirectUrl))
break
}
catch (e: IOException) {
- if (reservedPort.isCompleted) {
- throw e
- }
// check next port
}
}
- reservedPort.complete(null)
+ info.complete(null)
}
- return reservedPort.await()
+ return info.await()
+}
+
+private fun startAuthServerAsync(lifetime: Lifetime, port: Int): Deferred {
+ val redirectUrl = CompletableDeferred()
+ val server = embeddedServer(Jetty, port = port, host = "localhost") {
+ routing {
+ get("/auth") {
+ call.respondText(
+ "",
+ contentType = ContentType.Text.Html
+ )
+ redirectUrl.complete(call.request.uri)
+ }
+ }
+ }
+ server.start(wait = false)
+
+ fun stopServer() {
+ ApplicationManager.getApplication().executeOnPooledThread(Runnable {
+ server.stop(1000, 1000)
+ })
+ }
+ lifetime.addOrCallImmediately {
+ stopServer()
+ }
+ redirectUrl.invokeOnCompletion {
+ stopServer()
+ }
+
+ return redirectUrl
}
internal data class SpaceRedirectHandlingInfo(val port: Int, val redirectUrl: Deferred)
\ No newline at end of file