I'm making test-script for the micro-service which authorize by HMAC Authentication. The script is written by gatling-3.1 I'm facing a problem the process of generating HMAC signature is redundant.
I referred sample code in Github like this: https://github.com/gatling/gatling/blob/ace52b37bc60e5ebc168385694a283c4851cb3f9/gatling-http/src/test/scala/io/gatling/http/compile/HttpCompileTest.scala#L229-L245
I made code and confirmed that calculated signature is added to Authorization header in each request. But I don't know how to generate signature by module, As a result, my code still be redundant.
Please pay attention to (1) and (2).
package computerdatabase
class BasicSimulation extends Simulation {
val httpProtocol = http
.baseUrl("http://computer-database.gatling.io") // Here is the root for all relative URLs
val scn = scenario("Scenario Name") // A scenario is a chain of requests and pauses
.exec(http("request_1")
.get("/")
//---------- (1) Calculate signature in "request_1" by customized logic ----------
.sign(new SignatureCalculator {
override def sign(request: Request): Unit = {
import java.util.Base64
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
val mac = Mac.getInstance("HmacSHA256")
mac.init(new SecretKeySpec("THE_SECRET_KEY".getBytes("UTF-8"), "HmacSHA256"))
val rawSignature = mac.doFinal("".getBytes("UTF-8"))
val authorization = Base64.getEncoder.encodeToString(rawSignature)
request.getHeaders.add("Authorization", authorization)
}
})
//---------- (1) End ----------
)
.pause(7) // Note that Gatling has recorded real time pauses
.exec(http("request_2")
.get("/computers?f=macbook")
//---------- (2) Calculate signature in "request_2". Same logic as (1)...It is redundant----------
.sign(new SignatureCalculator {
override def sign(request: Request): Unit = {
import java.util.Base64
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
val mac = Mac.getInstance("HmacSHA256")
mac.init(new SecretKeySpec("THE_SECRET_KEY".getBytes("UTF-8"), "HmacSHA256"))
val rawSignature = mac.doFinal("".getBytes("UTF-8"))
val authorization = Base64.getEncoder.encodeToString(rawSignature)
request.getHeaders.add("Authorization", authorization)
}
})
//---------- (2) End ----------
)
setUp(scn.inject(atOnceUsers(1)).protocols(httpProtocol))
}
I want to calculate signature of request_1 and request_2 using a module for maintainability. Please let me know if you know.
Solution: Thank you for George. I could remove redundant code as below.
package computerdatabase
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._
object Hmac {
val signatureCalculator = new SignatureCalculator {
override def sign(request: Request): Unit = {
import java.util.Base64
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
val mac = Mac.getInstance("HmacSHA256")
mac.init(new SecretKeySpec("THE_SECRET_KEY".getBytes("UTF-8"), "HmacSHA256"))
val rawSignature = mac.doFinal("".getBytes("UTF-8"))
val authorization = Base64.getEncoder.encodeToString(rawSignature)
request.getHeaders.add("Authorization", authorization)
}
}
}
Just use a variable to hold your SignatureCalculator
.
val signatureCalculator = new SignatureCalculator {
// ...
Or use the lambda syntax.
val signatureCalculator: SignatureCalculator = { request =>
Then you can do
.exec(http("request_2")
.get("/computers?f=macbook")
.sign(signatureCalculator)
)
If your secret key is different for the virtual users, you will have to make it an Expression
. To depend on the session attribute, you can write something like:
val signatureCalculator: Expression[SignatureCalculator] = { session =>
session("secretKey")
.validate[String]
.map { secretKey =>
{ request =>
// ...
}
}
}