Hi, I'm newer to kotlin, and I'm having trouble se...
# help
r
Hi, I'm newer to kotlin, and I'm having trouble setting up wiremock with junit 5 to work in proxy mode AND to fail on unmatched requests. I'm looking for behavior similar to how js mock service worker / ruby webmock defaults are setup where any request to an outside host that isn't stubbed by default will throw an exception / failure. Right now, my setup will just literally proxy the request to the host even if I'm not stubbing it.
Copy code
package test

import kotlin.test.Test
import com.github.tomakehurst.wiremock.client.WireMock.*
import com.github.tomakehurst.wiremock.junit5.WireMockExtension
import com.marcinziolo.kotlin.wiremock.contains
import com.marcinziolo.kotlin.wiremock.get
import com.marcinziolo.kotlin.wiremock.returns
import org.apache.http.client.methods.HttpGet
import org.apache.http.impl.client.HttpClientBuilder
import org.junit.jupiter.api.extension.RegisterExtension

class WireMockTest {
    companion object {
        @JvmField
        @RegisterExtension
        val wm = WireMockExtension.newInstance()
            .proxyMode(true)
            .failOnUnmatchedRequests(true)
            .build()
    }


    @Test
    fun can_get_initialized_secrets() {
        wm.get {
            url contains "/test"
        }.returns { ok() }

        val client = HttpClientBuilder.create().useSystemProperties().build()
        val resp = client.execute(HttpGet("<http://www.example.com/doesnotexist>"))
        println(resp.statusLine)
    }
}
t
Can you share the details of your setup - your startup call/parameters and stubs in particular? Generally the way WireMock works is that it’ll try to match a stub and will proxy if it can’t. Or if you’ve got proxying turned off, it’ll just return a 404 with an error report when it can’t match.
r
just updated with my code snippet
t
Read my mind 🙂
r
my (limited) understanding of wiremock is that, if proxy mode is turned off, it won't even try to stub out requests to external hosts since those won't even be routed to the wiremock internal server though right?
t
It doesn’t look like your HTTP client is set up to use WireMock as a proxy.
You need to tell it explicitly to proxy through WireMock, then what you’ve got should work
r
It's definitely proxying though. I can see it in the logs that wire mock is accepting the request and proxying to the server
What i'm trying to figure out is, can I get wiremock to fail the request since it's not stubbed explicitly
(oh! and thank you so much for your help / quick responses btw!)
oh, let me take back that it's definitely proxying, i could be misreading the logs
but, if I change the request to use
<http://www.example.com/test|www.example.com/test>
, wiremock does return the stubbed response
so that makes me think that wiremock is intercepting this
t
It seems unlikely it’s proxying given what you’ve shown, but depends what’s in your system properties
Try this:
Copy code
val wm = WireMockExtension.newInstance()
          .options(wireMockConfig()
                  .enableBrowserProxying(true)
                  .proxyPassThrough(false)
          )
          .failOnUnmatchedRequests(true)
          .build()
r
holy cow
that sounds promising
one sec
I think that disabled the proxying all together in my use case. I'm getting the same http response (when I'd expect to get an assertion error because no stub matched), and I'm also not getting the logs I used to get that seemed to show that wiremock accepted the request and was proxying it through
t
What that should do is return stubs when they match via the proxy and the default 404 if not, rather than proxying onward to the target. Is that not what you wanted?
r
that's def what I want, but I don't think that's what i'm getting. Troubleshooting
kk, so I don't think it's quite doing what i'm expecting. I changed the example a bit to use http://httpbin.org/get Scenario 1: proxyMode = false (works as expected)
Copy code
val wm = WireMockExtension.newInstance()
//            .options(WireMockConfiguration.wireMockConfig()
//                .enableBrowserProxying(true)
//                .proxyPassThrough(true)
//            )
            .proxyMode(false)
            .failOnUnmatchedRequests(true)
            .build()
If i use this config (which should disable all proxying and any request I send shouldn't interact with wiremock at all) and then stub/call like this:
Copy code
wm.get {
  url contains "get"
}.returns { statusCode = 201 }

val client = HttpClientBuilder.create().useSystemProperties().build()
val resp = client.execute(HttpGet("<http://httpbin.org/get>"))
println(resp.statusLine)
println(resp.entity.content.readAllBytes().toString(Charsets.UTF_8))
I'll see the actual response which is good:
Copy code
HTTP/1.1 200 OK
{
  "args": {}, 
  "headers": {
    "Accept-Encoding": "gzip,deflate", 
    "Host": "<http://httpbin.org|httpbin.org>", 
    "User-Agent": "Apache-HttpClient/4.5.14 (Java/21.0.3)", 
    "X-Amzn-Trace-Id": "Root=1-66799f0a-0d6d0e1e08c6c00f24cdb810"
  }, 
  "origin": "184.165.15.27", 
  "url": "<http://httpbin.org/get>"
}
Scenario 2: proxyMode = true (stubbing works, but non stubs flow through)
Copy code
val wm = WireMockExtension.newInstance()
//            .options(WireMockConfiguration.wireMockConfig()
//                .enableBrowserProxying(true)
//                .proxyPassThrough(true)
//            )
            .proxyMode(true)
            .failOnUnmatchedRequests(true)
            .build()

// ...

wm.get {
  url contains "get"
}.returns { statusCode = 201 }

val client = HttpClientBuilder.create().useSystemProperties().build()
val resp = client.execute(HttpGet("<http://httpbin.org/get>"))
println(resp.statusLine)
println(resp.entity.content.readAllBytes().toString(Charsets.UTF_8))
I'll see the 201 response in this case which is expected. But if I changed to a different path, i'd just get the response from origin back instead of a failure. Scenario 3: proxyPassThrough = false (does not work as expected)
Copy code
val wm = WireMockExtension.newInstance()
            .options(WireMockConfiguration.wireMockConfig()
                .enableBrowserProxying(true)
                .proxyPassThrough(false)
            )
//            .proxyMode(false)
            .failOnUnmatchedRequests(true)
            .build()

wm.get {
            url contains "get"
        }.returns { statusCode = 201 }

        val client = HttpClientBuilder.create().useSystemProperties().build()
        val resp = client.execute(HttpGet("<http://httpbin.org/get>"))
        println(resp.statusLine)
        println(resp.entity.content.readAllBytes().toString(Charsets.UTF_8))
With this config, I get the same response as scenario 1 unfortunately, it seems like no proxying is happening at all and wiremock isn't involved in the request. If I changed
proxyPassThrough(true)
nothing changes as well.
t
How does your HTTP client know to proxy through WireMock? I still don’t think it is. Unless you’ve got the http.proxy system property pointing to localhost 8080?
r
ya, good point, unclear, i'm not explicitly configuring anything that i can tell. https://wiremock.org/docs/junit-jupiter/#programmatic
I'm following the instructions from the docs here which mention that useSystemProperties sets up the auto proxy config
regardless, since i'm new the java ecosystem, maybe you could point me in the right direction for my problem. Given a library that you don't have good control over how the internal http requests are created, how would you go about stubbing out that functionality? Historically, i'd reach for a tool like webmock / msw from ruby/js ecosystems, but I'm wondering if there's a better idiomatic JVM way to approach this problem
t
It’s been quite a long time since I touched this bit of the code so I’m having to remind myself exactly how it works. Agree that comment in the docs makes it look like it should auto-configure.
Do you always plan to proxy through to HTTP or will you need HTTPS?
r
the specific requests are https
but since https seemed like more configuration, i figured i'd try to just get http working first, then try and get https working in an example, and then move onto my actual use case haha
t
OK, auto-configuration would have been impossible in that case anyway because you’ve got to tell your client to trust WireMock’s fake certificates.
👍 1
I suggest explicitly setting WireMock’s host/port as the HTTP client’s proxy and also telling it to trust any certificate it’s given
I’d give you the incantation to do that, but I’m about to go into a series of calls so you might need to look at Stackoverflow
r
ah ah
ok, the library i'm using accepts http proxy port and hostname
t
OK, so if you set those explicitly your example should work for HTTP
Then if you want to do HTTPS you’ll additionally need to give it an SSL context that trusts everything
(or there’s a more complex way where you only trust our fake stuff, but that’s usually overkill)
r
ya, makes sense
thank you so much for your help!
t
No worries
I’ll look at clarifying that doc a bit
r
so, if i'm using the http client to "proxy" through wiremock's server, i wouldn't need to configure proxying with wiremock right?
t
In fact if you wouldn’t mind opening an issue on GH about the doc that’d be much appreciated, otherwise there’s a risk I’ll forget!
r
ya def, i'll make one!
thank you so much for your help!
t
You also need to enable WireMock’s prpxying
WireMock will only try to act as a proxy if you tell it to explicitly
So you need both WireMock and client to have it enabled
Ah, I remember why that comment is in the docs. It’s supposed to be paired with something like:
Copy code
JvmProxyConfigurer.configureFor(8080);
That might also be worth a try, although again won’t work for HTTPS unless you also do the trust thing.
r
ah kk
t
Hmmm..so the docs are correct actually.
JvmProxyConfigurer.configureFor(8080);
is called automatically when
.proxyMode(true)
is set.
r
kk, that makes sense because the proxying was working for me