Skip to content

Commit

Permalink
feat: add a system property to enable redirect handling #1323
Browse files Browse the repository at this point in the history
  • Loading branch information
Ronald Holshausen committed Mar 13, 2021
1 parent da1b926 commit 9b5c579
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 54 deletions.
1 change: 1 addition & 0 deletions provider/gradle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ The following project properties can be specified with `-Pproperty=value` on the
|`pact.pactbroker.httpclient.usePreemptiveAuthentication`|Enables preemptive authentication with the pact broker when set to `true`|
|`pact.provider.tag`|Sets the provider tag to push before publishing verification results (can use a comma separated list)|
|`pact.content_type.override.<TYPE>.<SUBTYPE>=<VAL>` where `<VAL>` may be `text`, `json` or `binary`|Overrides the handling of a particular content type [4.1.3+]|
|`pact.verifier.enableRedirectHandling`|Enables automatically handling redirects [4.1.8+]|

## Specifying the provider hostname at runtime

Expand Down
1 change: 1 addition & 0 deletions provider/maven/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ The following plugin properties can be specified with `-Dproperty=value` on the
|`pact.pactbroker.httpclient.usePreemptiveAuthentication`|Enables preemptive authentication with the pact broker when set to `true`|
|`pact.consumer.tags`|Overrides the tags used when publishing pacts [version 4.0.7+]|
|`pact.content_type.override.<TYPE>.<SUBTYPE>=text\|json\|binary`|Overrides the handling of a particular content type [version 4.1.3+]|
|`pact.verifier.enableRedirectHandling`|Enables automatically handling redirects [4.1.8+]|

Example in the configuration section:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.apache.http.conn.ssl.SSLConnectionSocketFactory
import org.apache.http.impl.client.CloseableHttpClient
import org.apache.http.impl.client.HttpClientBuilder
import org.apache.http.impl.client.HttpClients
import org.apache.http.impl.client.LaxRedirectStrategy
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager
import org.apache.http.ssl.SSLContextBuilder
import org.apache.http.ssl.TrustStrategy
Expand All @@ -21,66 +22,82 @@ import java.security.cert.X509Certificate
*/
class HttpClientFactory : IHttpClientFactory {

override fun newClient(provider: IProviderInfo): CloseableHttpClient {
return if (provider.createClient != null) {
if (provider.createClient is Closure<*>) {
(provider.createClient as Closure<*>).call(provider) as CloseableHttpClient
} else {
val binding = Binding()
binding.setVariable("provider", provider)
val shell = GroovyShell(binding)
shell.evaluate(provider.createClient.toString()) as CloseableHttpClient
}
} else if (provider.insecure) {
createInsecure()
} else if (provider.trustStore != null && provider.trustStorePassword != null) {
createWithTrustStore(provider)
} else {
HttpClients.custom().useSystemProperties().disableRedirectHandling().build()
}
override fun newClient(provider: IProviderInfo): CloseableHttpClient {
return if (provider.createClient != null) {
if (provider.createClient is Closure<*>) {
(provider.createClient as Closure<*>).call(provider) as CloseableHttpClient
} else {
val binding = Binding()
binding.setVariable("provider", provider)
val shell = GroovyShell(binding)
shell.evaluate(provider.createClient.toString()) as CloseableHttpClient
}
} else if (provider.insecure) {
createInsecure()
} else if (provider.trustStore != null && provider.trustStorePassword != null) {
createWithTrustStore(provider)
} else {
val builder = HttpClients.custom().useSystemProperties()
val enableRedirectHandling = System.getProperty("pact.verifier.enableRedirectHandling")
if (enableRedirectHandling.isNullOrEmpty() || enableRedirectHandling != "true") {
builder.disableRedirectHandling()
} else {
builder.setRedirectStrategy(LaxRedirectStrategy())
}
builder.build()
}
}

private fun createWithTrustStore(provider: IProviderInfo): CloseableHttpClient {
val password = provider.trustStorePassword.orEmpty().toCharArray()
return HttpClients
.custom()
.useSystemProperties()
.disableRedirectHandling()
.setSslcontext(SSLContextBuilder().loadTrustMaterial(provider.trustStore, password).build())
.build()
private fun createWithTrustStore(provider: IProviderInfo): CloseableHttpClient {
val password = provider.trustStorePassword.orEmpty().toCharArray()
val builder = HttpClients
.custom()
.useSystemProperties()
.setSslcontext(SSLContextBuilder().loadTrustMaterial(provider.trustStore, password).build())
val enableRedirectHandling = System.getProperty("pact.verifier.enableRedirectHandling")
if (enableRedirectHandling.isNullOrEmpty() || enableRedirectHandling != "true") {
builder.disableRedirectHandling()
} else {
builder.setRedirectStrategy(LaxRedirectStrategy())
}
return builder.build()
}

private fun createInsecure(): CloseableHttpClient {
val b = HttpClientBuilder.create()
.useSystemProperties()
.disableRedirectHandling()
private fun createInsecure(): CloseableHttpClient {
val b = HttpClientBuilder.create().useSystemProperties()
val enableRedirectHandling = System.getProperty("pact.verifier.enableRedirectHandling")
if (enableRedirectHandling.isNullOrEmpty() || enableRedirectHandling != "true") {
b.disableRedirectHandling()
} else {
b.setRedirectStrategy(LaxRedirectStrategy())
}

// setup a Trust Strategy that allows all certificates.
//
val trustStratergy = TrustStrategy { _: Array<X509Certificate>, _: String -> true }
val sslContext = SSLContextBuilder().loadTrustMaterial(null, trustStratergy).build()
b.setSslcontext(sslContext)
// don't check Hostnames, either.
// -- use SSLConnectionSocketFactory.getDefaultHostnameVerifier(), if you don't want to weaken
val hostnameVerifier = AllowAllHostnameVerifier()
// setup a Trust Strategy that allows all certificates.
//
val trustStratergy = TrustStrategy { _: Array<X509Certificate>, _: String -> true }
val sslContext = SSLContextBuilder().loadTrustMaterial(null, trustStratergy).build()
b.setSslcontext(sslContext)
// don't check Hostnames, either.
// -- use SSLConnectionSocketFactory.getDefaultHostnameVerifier(), if you don't want to weaken
val hostnameVerifier = AllowAllHostnameVerifier()

// here's the special part:
// -- need to create an SSL Socket Factory, to use our weakened "trust strategy";
// -- and create a Registry, to register it.
//
val sslSocketFactory = SSLConnectionSocketFactory(sslContext, hostnameVerifier)
val socketFactoryRegistry = RegistryBuilder.create<ConnectionSocketFactory>()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslSocketFactory)
.build()
// here's the special part:
// -- need to create an SSL Socket Factory, to use our weakened "trust strategy";
// -- and create a Registry, to register it.
//
val sslSocketFactory = SSLConnectionSocketFactory(sslContext, hostnameVerifier)
val socketFactoryRegistry = RegistryBuilder.create<ConnectionSocketFactory>()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslSocketFactory)
.build()

// now, we create connection-manager using our Registry.
// -- allows multi-threaded use
val connMgr = PoolingHttpClientConnectionManager(socketFactoryRegistry)
b.setConnectionManager(connMgr)
// now, we create connection-manager using our Registry.
// -- allows multi-threaded use
val connMgr = PoolingHttpClientConnectionManager(socketFactoryRegistry)
b.setConnectionManager(connMgr)

// finally, build the HttpClient;
// -- done!
return b.build()
}
// finally, build the HttpClient;
// -- done!
return b.build()
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package au.com.dius.pact.provider

import org.apache.http.impl.client.CloseableHttpClient
import org.apache.http.impl.client.LaxRedirectStrategy
import org.apache.http.impl.execchain.RedirectExec
import spock.lang.Issue
import spock.lang.Specification
import spock.util.environment.RestoreSystemProperties

class HttpClientFactorySpec extends Specification {

Expand Down Expand Up @@ -29,4 +33,44 @@ class HttpClientFactorySpec extends Specification {
new HttpClientFactory().newClient(provider) != null
}

@Issue('#1323')
@RestoreSystemProperties
def 'if pact.verifier.enableRedirectHandling is set, add a redirect handler'() {
given:
def provider = new ProviderInfo()
System.setProperty('pact.verifier.enableRedirectHandling', 'true')

when:
def client = new HttpClientFactory().newClient(provider)

then:
client.execChain instanceof RedirectExec
client.execChain.redirectStrategy instanceof LaxRedirectStrategy
}

@Issue('#1323')
@RestoreSystemProperties
def 'if pact.verifier.enableRedirectHandling is not set to true, do not add a redirect handler'() {
given:
def provider = new ProviderInfo()
System.setProperty('pact.verifier.enableRedirectHandling', 'false')

when:
def client = new HttpClientFactory().newClient(provider)

then:
!(client.execChain instanceof RedirectExec)
}

@Issue('#1323')
def 'if pact.verifier.enableRedirectHandling is not set, do not add a redirect handler'() {
given:
def provider = new ProviderInfo()

when:
def client = new HttpClientFactory().newClient(provider)

then:
!(client.execChain instanceof RedirectExec)
}
}

0 comments on commit 9b5c579

Please sign in to comment.