Unsure of how to pass Map[String, String] headers from session feeder into request

I have a json feeder that is generated from live traffic which contains headers and request urls. I cannot figure out how to get the headers to be picked up from the session and placed into the request.

My first approach was:

scenario(name)
      .feed(feeder)
      .exec(
        http("API call")
          .get("#{Url}")
          .headers("#{Header}")
          .check(status.is(200))
      )
      .inject(
        rampUsersPerSec(usersPerSec) to maxUsers during(rampSec seconds),
        constantUsersPerSec(maxUsers) during(constSec seconds))
      .protocols(httpConf)

However, I quickly saw that the session ETL will not work for Headers(). I have not seen a way to access the session while inside the call to headers, so I haven’t been able to do .headers(session("Header").

Then I tried

scenario(name)
      .feed(feeder)
      .exec{session =>
        val url = session("Url").as[String]
        val headers = session("Header").as[Map[String, String]]
        http("API call")
          .get(url)
          .headers(headers)
          .check(status.is(200))
        session
      }
      .inject(
        rampUsersPerSec(usersPerSec) to maxUsers during(rampSec seconds),
        constantUsersPerSec(maxUsers) during(constSec seconds))
      .protocols(httpConf)

However this results in no requests being sent. I assume it’s because I return the original session from the exec call instead of a new session modified by the call to http (I’m new to scala, not sure of the terminology here). I have not figured out how to return a new session with the http request instead of the old one.

Is there a modification I can make to either of these approaches that will work? Is there something else that I’ve not considered? I’d appreciate any advice on how to achieve this. Thanks!

What you did in the second try is return a session, the http().get().header… will just create a HttpRequestBuilder and do nothing with it. If you really want to go down the rabbit hole and end up shooting yourself in the foot you can try TrieMap Scala Standard Library 2.12.8 - scala.collection.concurrent.TrieMap , your milage will definitely vary.

Simplest is to have the headers set like the following

.header("foo", "#{fooval}")
.header("bar", "#{barval}")

Thanks for the reply!

Do you know what I can do for header values that may or may not be present?

This code fails if sometimes-header is not present.

.header("sometimes-header", "#{Header.sometimes-header}")

It seems like I cannot do this either

.header("sometimes-header", session("Header").as[Map[String,String]].getOrElse("sometimes-header", "default-value"))

The map is also immutable, so I cannot insert an item into it either.

Not sure why you you can’t do the #2 option, try this, you are not mutating the Map object, you are getting the Map Object stored at “Header” in the session HashMap, then doing a getOrElse

.header("sometimes-header", session => session("Header").as[Map[String,String]].getOrElse("sometimes-header", "default-value"))

Thanks for the suggestion. That gives me the following error:

type mismatch;
  found   : Object
  required: io.gatling.commons.validation.Validation[String]
        .header("sometimes-header", session => session("Header").as[Map[String,String]].getOrElse("sometimes-header", "default-value"))

Can I perform a cast here to resolve it? Apologies if this is a basic question.

Not header. headers.

Finally I got to my workspace. What you had an issue was getOrElse was returning a Success(String). Here is a solution I tested and works

  val getBooks = http("getListOfBooks")
    .get("/list")
    .header("Authorization", "#{auth}")
    .queryParam("limit", "#{limit}")
    .header("sometimes-header", session => session("Header").as[Map[String,String]].getOrElse("sometimes-header", "default-value").toString) // intelliJ sensed that .toString is not necessary but it actually is :/

val scn = scenario("test")
    .exec(_.setAll(("auth", "foobar"), ("limit", 10), ("Header", Map[String,String]())))
    .exec(getBooks)
    .exec(_.set("Header", Map("sometimes-header" -> "present-value"))) //this sets the value of sometimes-header
    .exec(getBooks)

  setUp(scn.inject(atOnceUsers(1))).protocols(httpProtocol)

The first execution uses the default value since we have an empty Map,

=========================
HTTP request:
GET https://localhost:8443/list?limit=10
headers:
	accept-language: en-US,en;q=0.5
	accept-encoding: gzip, deflate
	sometimes-header: default-value
	accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
	Authorization: foobar
	user-agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:101.0) Gecko/20100101 Firefox/101.0
	host: localhost:8443
=========================

The second time we set a value of the sometimes-header and vola we use that

=========================
HTTP request:
GET https://localhost:8443/list?limit=10
headers:
	accept-language: en-US,en;q=0.5
	accept-encoding: gzip, deflate
	sometimes-header: present-value
	accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
	Authorization: foobar
	user-agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:101.0) Gecko/20100101 Firefox/101.0
	host: localhost:8443
=========================
1 Like