Skip to content

Commit

Permalink
Remove Krossbow and use Ktor directly
Browse files Browse the repository at this point in the history
Resolves:
#190
  • Loading branch information
joffrey-bion committed Aug 10, 2022
1 parent b802acc commit 75433a6
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 34 deletions.
2 changes: 0 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ repositories {
}

dependencies {
api("org.hildan.krossbow:krossbow-websocket-core:4.1.0")
implementation("org.hildan.krossbow:krossbow-websocket-builtin:4.1.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.plugins.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.websocket.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
Expand All @@ -14,10 +15,6 @@ import kotlinx.serialization.json.Json
import org.hildan.chrome.devtools.targets.ChromeBrowserSession
import org.hildan.chrome.devtools.targets.ChromePageSession
import org.hildan.chrome.devtools.targets.attachToPage
import org.hildan.krossbow.websocket.WebSocketClient
import org.hildan.krossbow.websocket.builtin.builtIn

private val DEFAULT_WEBSOCKET_CLIENT by lazy { WebSocketClient.builtIn() }

private val DEFAULT_HTTP_CLIENT by lazy { createHttpClient(overrideHostHeader = false) }

Expand All @@ -41,7 +38,7 @@ private fun createHttpClient(overrideHostHeader: Boolean) = HttpClient {
* the browser and its targets to make use of the full Chrome Devtools Protocol API.
*
* **Note:** if you already know the browser target's web socket URL, you don't need to create a `ChromeDPClient`.
* Instead, use a [WebSocketClient] and [WebSocketClient.connectToChrome][connectToChrome] instead.
* Instead, you can directly use [HttpClient.chromeWebSocket].
*
* ## Host override
*
Expand All @@ -64,11 +61,6 @@ class ChromeDPClient(
* Enables override of the `Host` header to `localhost` (see section about Host override in [ChromeDPClient] doc).
*/
private val overrideHostHeader: Boolean = false,
/**
* This parameter should usually be left to its default value.
* Only use this to work around an issue in the client's configuration/behaviour.
*/
private val webSocketClient: WebSocketClient = DEFAULT_WEBSOCKET_CLIENT,
/**
* This parameter should usually be left to its default value.
* Only use this to work around an issue in the client's configuration/behaviour.
Expand Down Expand Up @@ -124,7 +116,7 @@ class ChromeDPClient(
*/
suspend fun webSocket(): ChromeBrowserSession {
val browserDebuggerUrl = version().webSocketDebuggerUrl
return webSocketClient.connectToChrome(browserDebuggerUrl)
return httpClient.chromeWebSocket(browserDebuggerUrl)
}

private fun ChromeVersion.fixHost() = when {
Expand Down Expand Up @@ -177,16 +169,7 @@ data class ChromeDPTarget(
val description: String,
val devtoolsFrontendUrl: String,
val webSocketDebuggerUrl: String,
) {
/**
* Attaches to this target via a new web socket connection to this target's debugger URL.
* This establishes a new protocol session to this target.
*/
@Deprecated("this method provides no way of closing the created websocket connection, prefer using " +
"ChromeDPClient.webSocket and using the richer websocket API to interact with targets (ChromeBrowserSession.target)")
suspend fun attach(webSocketClient: WebSocketClient = DEFAULT_WEBSOCKET_CLIENT): ChromePageSession =
webSocketClient.connectToChrome(webSocketDebuggerUrl).attachToPage(id)
}
)

/**
* Connects to the Chrome debugger at the given [webSocketDebuggerUrl].
Expand All @@ -197,7 +180,7 @@ data class ChromeDPTarget(
* [ChromeBrowserSession.attachToPage] or
* [ChromeBrowserSession.attachToNewPage][org.hildan.chrome.devtools.targets.attachToNewPage].
*/
suspend fun WebSocketClient.connectToChrome(webSocketDebuggerUrl: String): ChromeBrowserSession {
val connection = connect(webSocketDebuggerUrl).chromeDp()
suspend fun HttpClient.chromeWebSocket(webSocketDebuggerUrl: String): ChromeBrowserSession {
val connection = webSocketSession(webSocketDebuggerUrl).chromeDp()
return ChromeBrowserSession(connection.withSession(sessionId = null))
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,33 @@
package org.hildan.chrome.devtools.protocol

import io.ktor.websocket.*
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.hildan.krossbow.websocket.WebSocketConnection
import org.hildan.krossbow.websocket.WebSocketFrame

private val json = Json { ignoreUnknownKeys = true }

/**
* Wraps this [WebSocketConnection] to provide Chrome DevTools Protocol capabilities.
* Wraps this [WebSocketSession] to provide Chrome DevTools Protocol capabilities.
*
* The returned [ChromeDPConnection] can be used to send requests and listen to events.
*/
internal fun WebSocketConnection.chromeDp(): ChromeDPConnection = ChromeDPConnection(this)
internal fun WebSocketSession.chromeDp(): ChromeDPConnection = ChromeDPConnection(this)

/**
* A connection to Chrome, providing communication primitives for the Chrome DevTools protocol.
*
* It encodes/decodes ChromeDP frames, and handles sharing of incoming events.
*/
internal class ChromeDPConnection(
private val webSocket: WebSocketConnection
private val webSocket: WebSocketSession,
) {
private val coroutineScope = CoroutineScope(CoroutineName("ChromeDP-frame-decoder"))

private val frames = webSocket.incomingFrames
.filterIsInstance<WebSocketFrame.Text>()
.map { frame -> json.decodeFromString(InboundFrameSerializer, frame.text) }
private val frames = webSocket.incoming.receiveAsFlow()
.filterIsInstance<Frame.Text>()
.map { frame -> json.decodeFromString(InboundFrameSerializer, frame.readText()) }
.shareIn(
scope = coroutineScope,
started = SharingStarted.Eagerly,
Expand All @@ -40,7 +39,7 @@ internal class ChromeDPConnection(
* Throws [RequestFailed] in case of error.
*/
suspend fun request(request: RequestFrame): ResponseFrame {
val resultFrame = frames.onSubscription { webSocket.sendText(json.encodeToString(request)) }
val resultFrame = frames.onSubscription { webSocket.send(json.encodeToString(request)) }
.filterIsInstance<ResultFrame>()
.filter { it.matchesRequest(request) }
.first() // a shared flow never completes anyway, so this will never throw (but can hang forever)
Expand Down

0 comments on commit 75433a6

Please sign in to comment.