@@ -2,7 +2,6 @@ package org.hildan.chrome.devtools.protocol
2
2
3
3
import io.ktor.client.*
4
4
import io.ktor.client.call.*
5
- import io.ktor.client.plugins.*
6
5
import io.ktor.client.plugins.contentnegotiation.*
7
6
import io.ktor.client.plugins.websocket.*
8
7
import io.ktor.client.request.*
@@ -14,20 +13,13 @@ import kotlinx.serialization.Serializable
14
13
import kotlinx.serialization.json.Json
15
14
import org.hildan.chrome.devtools.sessions.*
16
15
17
- private val DEFAULT_HTTP_CLIENT by lazy { createHttpClient(overrideHostHeader = false ) }
16
+ private val DEFAULT_HTTP_CLIENT by lazy { createHttpClient() }
18
17
19
- private val DEFAULT_HTTP_CLIENT_WITH_HOST_OVERRIDE by lazy { createHttpClient(overrideHostHeader = true ) }
20
-
21
- private fun createHttpClient (overrideHostHeader : Boolean ) = HttpClient {
18
+ private fun createHttpClient () = HttpClient {
22
19
install(ContentNegotiation ) {
23
20
json(Json { ignoreUnknownKeys = true })
24
21
}
25
22
install(WebSockets )
26
- if (overrideHostHeader) {
27
- install(DefaultRequest ) {
28
- headers[" Host" ] = " localhost"
29
- }
30
- }
31
23
}
32
24
33
25
/* *
@@ -42,14 +34,12 @@ private fun createHttpClient(overrideHostHeader: Boolean) = HttpClient {
42
34
*
43
35
* ## Host override
44
36
*
45
- * Chrome doesn't accept a `Host` header that is not an IP nor `localhost`, but in some environments it might be hard
46
- * to provide this (e.g. docker services in a docker swarm, communicating using service names).
37
+ * Chrome doesn't accept a `Host` header that is not an IP nor `localhost`, but in some environments the URL has to
38
+ * have a named host (e.g. docker services in a docker swarm, communicating using service names).
47
39
*
48
- * To work around this problem, simply set [overrideHostHeader] to true.
49
- * This overrides the `Host` header to "localhost" in the HTTP requests to the Chrome debugger to make it happy, and
50
- * also replaces the host in subsequent web socket URLs (returned by Chrome) by the initial host provided in
51
- * [remoteDebugUrl].
52
- * This is necessary because Chrome uses the `Host` header to build these URLs, and it would be incorrect to keep this.
40
+ * Enabling [overrideHostHeader] works around this problem by setting the `Host` header to "localhost" in the HTTP
41
+ * requests to the Chrome debugger. It also restores the original host in URLs that are returned by Chrome
42
+ * (Chrome uses the `Host` header to build the URLs that it returns, so they would contain `localhost` otherwise).
53
43
*/
54
44
class ChromeDPClient (
55
45
/* *
@@ -74,7 +64,7 @@ class ChromeDPClient(
74
64
* You should only need to override it to work around an issue in the client's configuration/behaviour, or if you
75
65
* want to also reuse your own client here.
76
66
*/
77
- private val httpClient : HttpClient = if (overrideHostHeader) DEFAULT_HTTP_CLIENT_WITH_HOST_OVERRIDE else DEFAULT_HTTP_CLIENT ,
67
+ private val httpClient : HttpClient = DEFAULT_HTTP_CLIENT ,
78
68
) {
79
69
init {
80
70
require(remoteDebugUrl.startsWith(" http://" ) || remoteDebugUrl.startsWith(" https://" )) {
@@ -86,19 +76,17 @@ class ChromeDPClient(
86
76
/* *
87
77
* Fetches the browser version metadata via the debugger's HTTP API.
88
78
*/
89
- suspend fun version (): ChromeVersion =
90
- httpClient.get(" $remoteDebugUrl /json/version" ).body<ChromeVersion >().fixHost()
79
+ suspend fun version (): ChromeVersion = httpGet(" /json/version" ).body<ChromeVersion >().fixHost()
91
80
92
81
/* *
93
82
* Fetches the current Chrome DevTools Protocol definition, as a JSON string.
94
83
*/
95
- suspend fun protocolJson (): String = httpClient.get( " $remoteDebugUrl /json/protocol" ).bodyAsText()
84
+ suspend fun protocolJson (): String = httpGet( " /json/protocol" ).bodyAsText()
96
85
97
86
/* *
98
87
* Fetches the list of all available web socket targets (e.g. browser tabs).
99
88
*/
100
- suspend fun targets (): List <ChromeDPTarget > =
101
- httpClient.get(" $remoteDebugUrl /json/list" ).body<List <ChromeDPTarget >>().map { it.fixHost() }
89
+ suspend fun targets (): List <ChromeDPTarget > = httpGet(" /json/list" ).body<List <ChromeDPTarget >>().map { it.fixHost() }
102
90
103
91
/* *
104
92
* Opens a new tab, and returns the websocket target data for the new tab.
@@ -114,17 +102,17 @@ class ChromeDPClient(
114
102
),
115
103
)
116
104
suspend fun newTab (url : String = "about : blank"): ChromeDPTarget =
117
- httpClient.put( " $remoteDebugUrl /json/new?$url " ).body<ChromeDPTarget >().fixHost()
105
+ httpPut( " /json/new?$url " ).body<ChromeDPTarget >().fixHost()
118
106
119
107
/* *
120
108
* Brings the page identified by the given [targetId] into the foreground (activates a tab).
121
109
*/
122
- suspend fun activateTab (targetId : String ): String = httpClient.get( " $remoteDebugUrl /json/activate/$targetId " ).body()
110
+ suspend fun activateTab (targetId : String ): String = httpGet( " /json/activate/$targetId " ).body()
123
111
124
112
/* *
125
113
* Closes the page identified by [targetId].
126
114
*/
127
- suspend fun closeTab (targetId : String ): String = httpClient.get( " $remoteDebugUrl /json/close/$targetId " ).body()
115
+ suspend fun closeTab (targetId : String ): String = httpGet( " /json/close/$targetId " ).body()
128
116
129
117
/* *
130
118
* Closes all targets.
@@ -156,6 +144,18 @@ class ChromeDPClient(
156
144
return httpClient.chromeWebSocket(browserDebuggerUrl)
157
145
}
158
146
147
+ private suspend fun httpPut (endpoint : String ): HttpResponse = httpClient.put(remoteDebugUrl + endpoint) {
148
+ if (overrideHostHeader) {
149
+ headers[" Host" ] = " localhost"
150
+ }
151
+ }
152
+
153
+ private suspend fun httpGet (endpoint : String ): HttpResponse = httpClient.get(remoteDebugUrl + endpoint) {
154
+ if (overrideHostHeader) {
155
+ headers[" Host" ] = " localhost"
156
+ }
157
+ }
158
+
159
159
private fun ChromeVersion.fixHost () = when {
160
160
overrideHostHeader -> copy(webSocketDebuggerUrl = webSocketDebuggerUrl.fixHost())
161
161
else -> this
0 commit comments