scalasoapnettyscalaxbbetamax

Stubbing SOAP requests in Scala


I use scalaxb to generate models and client part of the SOAP interface. For testing I use Betamax, which can also be used in Scala. However, scalaxb uses Netty as a transport, which ignores proxy settings set up by Betamax. How would you cope with this situation?

scalaxb uses cake pattern, so the service is built from 3 parts like in the following example:

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent._
import scala.concurrent.duration._

val service = (new stockquote.StockQuoteSoap12Bindings with
  scalaxb.SoapClientsAsync with
  scalaxb.DispatchHttpClientsAsync {}).service
val fresponse = service.getQuote(Some("GOOG"))
val response = Await.result(fresponse, 5 seconds)
println(response)

And tests:

import co.freeside.betamax.{TapeMode, Recorder}
import co.freeside.betamax.proxy.jetty.ProxyServer
import dispatch._
import org.scalatest.{Tag, FunSuite}
import scala.concurrent.duration._

import scala.concurrent.{Await, Future}

class StockquoteSpec extends FunSuite with Betamax {

  testWithBetamax("stockquote", Some(TapeMode.READ_WRITE))("stockquote") { 
    val fresponse = service.getQuote(Some("GOOG"))
    val response = Await.result(fresponse, 5 seconds)
    println(response)
  }
}

trait Betamax {

  protected def test(testName: String, testTags: Tag*)(testFun: => Unit)

  def testWithBetamax(tape: String, mode: Option[TapeMode] = None)(testName: String, testTags: Tag*)(testFun: => Unit) = {
    test(testName, testTags: _*) {
      val recorder = new Recorder
      val proxyServer = new ProxyServer(recorder)
      recorder.insertTape(tape)
      recorder.getTape.setMode(mode.getOrElse(recorder.getDefaultMode()))
      proxyServer.start()
      try {
        testFun
      } finally {
        recorder.ejectTape()
        proxyServer.stop()
      }
    }
  }
}

Versions:


Solution

  • It is indeed possible to use proxy with Netty. Although Netty does not read system properties for proxy settings, the settings can be injected using ProxyServerSelector. It is created in build method of AsyncHttpClientConfig:

    if (proxyServerSelector == null && useProxySelector) {
        proxyServerSelector = ProxyUtils.getJdkDefaultProxyServerSelector();
    }
    
    if (proxyServerSelector == null && useProxyProperties) {
        proxyServerSelector = ProxyUtils.createProxyServerSelector(System.getProperties());
    }
    
    if (proxyServerSelector == null) {
        proxyServerSelector = ProxyServerSelector.NO_PROXY_SELECTOR;
    }
    

    The only obstacle is that scalaxb uses default config with useProxyProperties=false. You can override it with custom MyDispatchHttpClientsAsync that you can use when creating the service:

    val service = (new stockquote.StockQuoteSoap12Bindings with
      scalaxb.SoapClientsAsync with
      MyDispatchHttpClientsAsync {}).service
    

    And the source code of MyDispatchHttpClientsAsync (the key point is calling setUseProxyProperties(true)):

    import com.ning.http.client.providers.netty.NettyAsyncHttpProvider
    import com.ning.http.client.{AsyncHttpClientConfig, AsyncHttpClient}
    import scalaxb.HttpClientsAsync
    
    /**
     * @author miso
     */
    trait MyDispatchHttpClientsAsync extends HttpClientsAsync {
      lazy val httpClient = new DispatchHttpClient {}
    
      trait DispatchHttpClient extends HttpClient {
        import dispatch._, Defaults._
    
        // Keep it lazy. See https://github.com/eed3si9n/scalaxb/pull/279
        lazy val http = new Http(new AsyncHttpClient(new NettyAsyncHttpProvider(new AsyncHttpClientConfig.Builder().setUseProxyProperties(true).build())))
    //    lazy val http = Http.configure(_.setUseProxyProperties(true)) // Maybe later. See https://github.com/eed3si9n/scalaxb/issues/312
    
        def request(in: String, address: java.net.URI, headers: Map[String, String]): concurrent.Future[String] = {
          val req = url(address.toString).setBodyEncoding("UTF-8") <:< headers << in
          http(req > as.String)
        }
      }
    }