forked from httptoolkit/jvm-http-proxy-agent
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAgentMain.kt
178 lines (151 loc) · 6.68 KB
/
AgentMain.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
@file:JvmName("HttpProxyAgent")
package tech.httptoolkit.javaagent
import net.bytebuddy.ByteBuddy
import net.bytebuddy.agent.builder.AgentBuilder
import net.bytebuddy.asm.Advice
import net.bytebuddy.description.type.TypeDescription
import net.bytebuddy.dynamic.ClassFileLocator
import net.bytebuddy.dynamic.DynamicType
import net.bytebuddy.dynamic.scaffold.TypeValidation
import net.bytebuddy.matcher.ElementMatchers.none
import net.bytebuddy.pool.TypePool
import net.bytebuddy.utility.JavaModule
import java.lang.instrument.Instrumentation
import javax.net.ssl.SSLContext
import java.net.*
import java.security.ProtectionDomain
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.TrustManagerFactory
lateinit var InterceptedSslContext: SSLContext
private set
lateinit var InterceptedTrustManagerFactory: TrustManagerFactory
private set
lateinit var AgentProxyHost: String
private set
var AgentProxyPort = -1
private set
lateinit var AgentProxySelector: ProxySelector
private set
// If attached at startup with a -javaagent argument, use either arguments or env
fun premain(arguments: String?, instrumentation: Instrumentation) {
val config = try {
getConfigFromArg(arguments!!)
} catch (e: Throwable) {
// If that fails for any reason (any kind of parse error at all), try to
// use our env variables instead
getConfigFromEnv()
}
interceptAllHttps(config, instrumentation)
}
// If attached after startup, pull config from the passed arguments
fun agentmain(arguments: String?, instrumentation: Instrumentation) {
if (arguments.isNullOrEmpty()) {
throw Error("Can't attach proxy agent without configuration arguments")
}
// If attached as a test, we don't intercept anything, we're just checking that it's
// possible to attach in the first place with the current VM.
if (arguments == "attach-test") {
println("Agent attach test successful")
return
};
val config = getConfigFromArg(arguments)
interceptAllHttps(config, instrumentation)
}
fun interceptAllHttps(config: Config, instrumentation: Instrumentation) {
val (certPath, proxyHost, proxyPort) = config
InterceptedTrustManagerFactory = buildTrustManagerFactoryForCertificate(certPath)
InterceptedSslContext = buildSslContextForCertificate(InterceptedTrustManagerFactory)
AgentProxyHost = proxyHost
AgentProxyPort = proxyPort
// Reconfigure the JVM default settings:
setDefaultProxy(proxyHost, proxyPort)
setDefaultSslContext(InterceptedSslContext)
val debugMode = !System.getenv("DEBUG_JVM_HTTP_PROXY_AGENT").isNullOrEmpty()
val logger = TransformationLogger(debugMode)
// Disabling type validation allows us to intercept non-Java types, e.g. Kotlin
// in OkHttp. See https://github.com/raphw/byte-buddy/issues/764
var agentBuilder = AgentBuilder.Default(
ByteBuddy().with(TypeValidation.DISABLED)
)
.ignore(none())
.with(AgentBuilder.TypeStrategy.Default.REDEFINE)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.disableClassFormatChanges()
.with(logger)
arrayOf(
OkHttpClientV3Transformer(logger),
OkHttpClientV2Transformer(logger),
ApacheClientRoutingV4Transformer(logger),
ApacheClientRoutingV5Transformer(logger),
ApacheSslSocketFactoryTransformer(logger),
ApacheClientTlsStrategyTransformer(logger),
ApacheHostConfigurationTransformer(logger),
ApacheHttpMethodDirectorTransformer(logger),
ApacheProtocolTransformer(logger),
JavaClientTransformer(logger),
UrlConnectionTransformer(logger),
HttpsUrlConnectionTransformer(logger),
ProxySelectorTransformer(logger),
SslContextTransformer(logger),
JettyClientTransformer(logger),
AsyncHttpClientConfigTransformer(logger),
AsyncHttpChannelManagerTransformer(logger),
ReactorNettyClientConfigTransformer(logger),
ReactorNettyProxyProviderTransformer(logger),
ReactorNettyOverrideRequestAddressTransformer(logger),
ReactorNettyHttpClientSecureTransformer(logger),
KtorClientEngineConfigTransformer(logger),
KtorCioEngineTransformer(logger),
KtorClientTlsTransformer(logger),
AkkaHttpTransformer(logger),
AkkaPoolSettingsTransformer(logger),
AkkaPoolTransformer(logger),
AkkaGatewayTransformer(logger),
VertxHttpClientTransformer(logger),
VertxNetClientOptionsTransformer(logger),
).forEach { matchingAgentTransformer ->
agentBuilder = matchingAgentTransformer.register(agentBuilder)
}
agentBuilder.installOn(instrumentation)
System.err.println("HTTP Toolkit interception active")
}
abstract class MatchingAgentTransformer(private val logger: TransformationLogger) : AgentBuilder.Transformer {
abstract fun register(builder: AgentBuilder): AgentBuilder
abstract fun transform(builder: DynamicType.Builder<*>, loadAdvice: (String) -> Advice): DynamicType.Builder<*>
override fun transform(
builder: DynamicType.Builder<*>,
typeDescription: TypeDescription,
classLoader: ClassLoader?,
module: JavaModule?,
protectionDomain: ProtectionDomain?
): DynamicType.Builder<*> {
logger.beforeTransformation(typeDescription)
return transform(builder) { adviceName ->
val locator = if (classLoader != null) {
ClassFileLocator.Compound(
ClassFileLocator.ForClassLoader.of(classLoader),
ClassFileLocator.ForClassLoader.of(ByteBuddy::class.java.classLoader)
)
} else {
ClassFileLocator.ForClassLoader.of(ByteBuddy::class.java.classLoader)
}
Advice.to(TypePool.Default.of(locator).describe(adviceName).resolve(), locator)
}
}
}
private fun setDefaultProxy(proxyHost: String, proxyPort: Int) {
System.setProperty("http.proxyHost", proxyHost)
System.setProperty("http.proxyPort", proxyPort.toString())
System.setProperty("https.proxyHost", proxyHost)
System.setProperty("https.proxyPort", proxyPort.toString())
// We back up the properties in our namespace too, in case anybody manually overrides the above:
System.setProperty("tech.httptoolkit.proxyHost", proxyHost)
System.setProperty("tech.httptoolkit.proxyPort", proxyPort.toString())
val proxySelector = ConstantProxySelector(proxyHost, proxyPort)
AgentProxySelector = proxySelector
ProxySelector.setDefault(proxySelector)
}
private fun setDefaultSslContext(context: SSLContext) {
SSLContext.setDefault(context)
HttpsURLConnection.setDefaultSSLSocketFactory(context.socketFactory)
}