@@ -2,11 +2,10 @@ package tech.httptoolkit.javaagent
22
33import net.bytebuddy.agent.builder.AgentBuilder
44import net.bytebuddy.asm.Advice
5+ import net.bytebuddy.description.method.MethodDescription
56import net.bytebuddy.dynamic.DynamicType
67import net.bytebuddy.matcher.ElementMatchers.*
7- import tech.httptoolkit.javaagent.advice.apacheclient.ApacheSetSslSocketFactoryAdvice
8- import tech.httptoolkit.javaagent.advice.apacheclient.ApacheV4ReturnProxyRouteAdvice
9- import tech.httptoolkit.javaagent.advice.apacheclient.ApacheV5ReturnProxyRouteAdvice
8+ import tech.httptoolkit.javaagent.advice.apacheclient.*
109
1110// For both v4 & v5 we override all implementations of the RoutePlanner interface, and we redefine all routes
1211// to go via our proxy instead of their existing configuration.
@@ -68,3 +67,71 @@ class ApacheSslSocketFactoryTransformer(logger: TransformationLogger) : Matching
6867 );
6968 }
7069}
70+
71+ // Meanwhile, for V3 we need to do something totally different: we patch HostConfiguration to apply a proxy to
72+ // all new configurations (and ignore changes), we patch HttpMethodDirector to update existing configurations
73+ // as they're used, and we patch Protocol to change the SslSocketFactory on all secure protocols.
74+
75+ class ApacheHostConfigurationTransformer (logger : TransformationLogger ) : MatchingAgentTransformer(logger) {
76+ override fun register (builder : AgentBuilder ): AgentBuilder {
77+ return builder
78+ .type(
79+ named(" org.apache.commons.httpclient.HostConfiguration" )
80+ ).transform(this )
81+ }
82+
83+ override fun transform (builder : DynamicType .Builder <* >): DynamicType .Builder <* > {
84+ return builder
85+ // Override the proxy field value for all new configurations, and for any attempts to call
86+ // setProxy/ProxyHost. We don't no-op these, because we want to call them ourselves later on
87+ // existing configs to reset them - we don't just want to ignore this.
88+ .visit(
89+ Advice .to(ApacheOverrideProxyHostFieldAdvice ::class .java)
90+ .on(isConstructor<MethodDescription >()
91+ .or (hasMethodName(" setProxy" ))
92+ .or (hasMethodName(" setProxyHost" ))
93+ )
94+ )
95+ }
96+ }
97+
98+ // Whenever an HttpMethodDirector is used, we reset the proxy in the passed configuration. This uses the above
99+ // hooks, which ensure that setProxyHost(anything) automatically loads & sets our intercepted proxy.
100+ // We *don't* want to reset all proxy hosts in all existing configurations, because that's a) quite tricky and
101+ // b) some are used as keys in existing direct connections in pools, and we don't want to match those later.
102+ class ApacheHttpMethodDirectorTransformer (logger : TransformationLogger ) : MatchingAgentTransformer(logger) {
103+ override fun register (builder : AgentBuilder ): AgentBuilder {
104+ return builder
105+ .type(
106+ named(" org.apache.commons.httpclient.HttpMethodDirector" )
107+ ).transform(this )
108+ }
109+
110+ override fun transform (builder : DynamicType .Builder <* >): DynamicType .Builder <* > {
111+ return builder
112+ .visit(
113+ Advice .to(ApacheSetConfigProxyHostAdvice ::class .java)
114+ .on(hasMethodName(" executeMethod" ))
115+ )
116+ }
117+ }
118+
119+ // Every v3 configuration has a protocol, and each one can build sockets in its own unique way. Here, we patch
120+ // all of them so that all _secure_ protocols trust only our certificate, and nothing else. This would
121+ // be an issue for a generic TCP client, but for HTTPS we know we should be the only authority present.
122+ class ApacheProtocolTransformer (logger : TransformationLogger ) : MatchingAgentTransformer(logger) {
123+ override fun register (builder : AgentBuilder ): AgentBuilder {
124+ return builder
125+ .type(
126+ named(" org.apache.commons.httpclient.protocol.Protocol" )
127+ ).transform(this )
128+ }
129+
130+ override fun transform (builder : DynamicType .Builder <* >): DynamicType .Builder <* > {
131+ return builder
132+ .visit(
133+ Advice .to(ApacheReturnCustomSslProtocolSocketFactoryAdvice ::class .java)
134+ .on(hasMethodName(" getSocketFactory" ))
135+ )
136+ }
137+ }
0 commit comments