POST Request failing with String index out of range

Hi,

I am trying my hand with Gatling and getting “String index out of range: -1” on the last request (highlighted in Blue below). The request body for this request has to be formatted dynamically based on the response from the previous request (number of fields varies every time). So, I am using .formParamMap(paramMap) and paramMap is built before the request that is failing is sent (in bold below). Am I missing something here?

Gatling Scala script:

class PlanitChallengeSimulation extends Simulation {

val httpProtocol = http
.baseUrl(“http://test.url.com”)
.inferHtmlResources()
.userAgentHeader(“Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36”)

val headers_0 = Map(
“Accept” → “text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9”,
“Accept-Encoding” → “gzip, deflate”,
“Accept-Language” → “en-US,en;q=0.9”,
“Proxy-Connection” → “keep-alive”,
“Upgrade-Insecure-Requests” → “1”)

val headers_1 = Map(
“Accept” → “text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9”,
“Accept-Encoding” → “gzip, deflate”,
“Accept-Language” → “en-US,en;q=0.9”,
“Origin” → “http://challenge.planittesting.com”,
“Proxy-Connection” → “keep-alive”,
“Upgrade-Insecure-Requests” → “1”)

val headers_4 = Map(
“Accept” → “application/json, text/javascript, /; q=0.01”,
“Accept-Encoding” → “gzip, deflate”,
“Accept-Language” → “en-US,en;q=0.9”,
“Proxy-Connection” → “keep-alive”,
“X-Requested-With” → “XMLHttpRequest”)

val headers_7 = Map(
“Client-Request-Id” → “{B8BF2DB6-E7C9-4D5E-A3B9-BEB14F5FD0A9}”,
“Content-Type” → “text/xml”,
“Pragma” → “no-cache”,
“Proxy-Connection” → “Keep-Alive”,
“User-Agent” → “Microsoft Office/16.0 (Windows NT 10.0; Microsoft Outlook 16.0.11929; Pro)”)

val uri2 = “http://autodiscover.planittesting.com/autodiscover/autodiscover.xml

var paramMap = Map.empty[String,String] // Define param map

val scn = scenario(“PlanitChallengeSimulation”)
.exec(http(“request_0”)
.get("/")
.headers(headers_0)
.check(regex(“VIEWSTATE” value="(.?)"").find.saveAs(“cVIEWSTATE”)))
.pause(3)
.exec(http(“request_1”)
.post("/challenge1.php")
.headers(headers_1)
.formParam("__EVENTTARGET", “”)
.formParam("__EVENTARGUMENT", “”)
.formParam("__VIEWSTATE", “${cVIEWSTATE}”)
.check(regex(“name=”(.
?)">[\n\r\t ].glyphicon-ok").find.saveAs(“cCHLG1”))
.check(regex(“name=”(.
?)">[\n\r\t ]
.glyphicon-remove").findAll.saveAs(“cCHLG1S”))
.check(regex("tick at the end: [\n\r\t ]
(.)[\n\r\t ]").find.saveAs(“cVALUE1”))
.check(regex(“Challenge”)))

.pause(3)
.exec(http(“request_2”)
.post("/challenge2.php")
.headers(headers_1)
.formParam(“EPwcsaQq2e”, “”)
.formParam(“qKg0uqsl63”, “”)
.formParam("${cCHLG1}", “${cVALUE1}”)
.formParam(“53rRq7OF0d”, “”)
.check(regex(“input.name="(.?)”").findAll.saveAs(“cCHLG2S”))
.check(regex("class=“glyphicon (.?)"").findAll.saveAs(“cGLYPHS”))
.check(regex("tick at the end:[\n\r\t ]
(.*)”).find.saveAs(“cVALUE2”))
.check(regex(“Challenge”)))
.exec(session => {

val chlg2s = session(“cCHLG2S”).as[List[String]]
val glyphs = session(“cGLYPHS”).as[List[String]]
var chlg2: String = “NOT FOUND”
glyphs.indices.foreach { i =>
if (glyphs(i) == “glyphicon-ok”) {
chlg2 = chlg2s(i).toString
}
}
session.set(“cCHLG2”, chlg2)

})
//.exec { session => println(session(“cCHLG2”).as[String]); session}
.pause(3)

.exec(http(“request_3”)
.post("/challenge3.php")
.headers(headers_1)
.formParam(“MP4pIbAtZm”, “”)
.formParam(“qBx2IxEwQk”, “”)
.formParam("${cCHLG2}", “${cVALUE2}”)
.formParam(“VOV5EhHJpK”, “”)
.formParam(“FV6lAdeYK2”, “”)
.formParam(“eawSZdQg5J”, “”)
.formParam(“MTVUO3PukB”, “”)
.formParam(“526EcNJR5X”, “”)
.formParam(“2Ymp9rAV2Q”, “”)
.formParam(“Zxi42xGv12”, “”)
.formParam(“aQ9UrWOVKw”, “”)
.formParam(“uBcpwGR2BZ”, “”)
.check(regex(“Challenge”)))
.pause(3)
.exec(http(“request_4”)
.get("/assets/js/challenge3.js.php")
.headers(headers_4)
.check(regex("&requestid=(.?)&").find.saveAs(“cREQID1”)))
.pause(3)
.exec(http(“request_4a”)
.get("/challenge3_driver.php?type=session&requestid=${cREQID1}&ts=")
.headers(headers_4)
.check(regex("“pSession”:"(.
?)",").find.saveAs(“cPSESSION”))
.check(regex("“requestid”:"(.*?)",").find.saveAs(“cREQID2”)))
//.check(bodyString.saveAs(“responseBody”)))
.pause(3)

.exec(http(“request_5”)
.get("/challenge3_driver.php?type=input&session=${cPSESSION}&requestid=${cREQID2}&ts=")
.headers(headers_4)
.check(regex("“value”:"(.?)",").find.saveAs(“cVALUE3”))
.check(regex("“input”:"(.
?)",").find.saveAs(“cCHLG3”)))
//.exec { session => println(session(“cCHLG3”).as[String]); session}
//.exec { session => println(session(“cVALUE3”).as[String]); session}
.pause(3)
.exec(http(“request_6”)
.post("/challenge4.php")
.headers(headers_1)
.formParam("${cCHLG3}", “${cVALUE3}”)
//.check(bodyString.saveAs(“responseBody”))
.check(regex(“input.name="(.?)”").findAll.saveAs(“cCHLG4S”))
.check(regex(“div id=“numbers”.[\r\n\t ](.?)[\r\n\t ]<”).find.transform(.split(" ").toList.map(.toInt).sortWith(<)).saveAs(“cNUMS”))
.check(regex(“Challenge 4”)))
.exec { session => println(session(“cCHLG4S”).as[String]); session}
.exec { session => println(session(“cNUMS”).as[String]); session}
//.exec { session => println(session(“responseBody”).as[String]); session}
.exec(session => {

val chlg4s = session(“cCHLG4S”).as[List[String]]
val iNumList = session(“cNUMS”).as[List[Int]]
session.set(“cNUMLIST”, iNumList) // Save int numList to session

for (i ← 0 until iNumList.length) {
println(s"$i is ${chlg4s(i)} ${iNumList(i)}")
paramMap += (chlg4s(i) → iNumList(i).toString) // Add field name and value to params map
}

session
})
//.exec { session => println(session); session}

.pause(3)
.exec(session => {
println(s"paramMap = $paramMap.as[Map[String,String]]}")
println(s"headers_1 = $headers_1.as[Map[String,String]]}")
session
})

.exec(http(“request_7”)
.post("/challenge5.php")
.headers(headers_1)
.formParamMap(paramMap)
.check(bodyString.saveAs(“responseBody”)))
.exec { session => println(session(“responseBody”).as[String]); session}

.exec { session => println(session); session}

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

Stack trace:

14:41:27.542 [INFO ] i.g.h.e.GatlingHttpListener - Request ‘request_7’ failed for user 1
java.lang.StringIndexOutOfBoundsException: String index out of range: -1
at java.lang.AbstractStringBuilder.setLength(Unknown Source)
at java.lang.StringBuilder.setLength(Unknown Source)
at io.gatling.http.client.body.form.FormUrlEncodedRequestBody.encode(FormUrlEncodedRequestBody.java:57)
at io.gatling.http.client.body.form.FormUrlEncodedRequestBody.build(FormUrlEncodedRequestBody.java:45)
at io.gatling.http.client.impl.request.WritableRequestBuilder.buildRequestWithBody(WritableRequestBuilder.java:68)
at io.gatling.http.client.impl.request.WritableRequestBuilder.buildRequest(WritableRequestBuilder.java:118)
at io.gatling.http.client.impl.HttpAppHandler.write(HttpAppHandler.java:105)
at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:716)
at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:708)
at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:791)
at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:701)
at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:696)
at io.netty.channel.DefaultChannelPipeline.write(DefaultChannelPipeline.java:1022)
at io.netty.channel.AbstractChannel.write(AbstractChannel.java:283)
at io.gatling.http.client.impl.DefaultHttpClient.sendTxWithChannel(DefaultHttpClient.java:438)
at io.gatling.http.client.impl.DefaultHttpClient.sendTx(DefaultHttpClient.java:356)
at io.gatling.http.client.impl.DefaultHttpClient.lambda$sendRequest$0(DefaultHttpClient.java:265)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:510)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:518)
at io.netty.util.concurrent.SingleThreadEventExecutor$6.run(SingleThreadEventExecutor.java:1044)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Unknown Source)
14:41:27.545 [INFO ] i.g.h.e.r.DefaultStatsProcessor - Request ‘request_7’ failed for user 1: j.l.StringIndexOutOfBoundsException: String index out of range: -1
14:41:27.546 [TRACE] i.g.h.e.r.DefaultStatsProcessor -

You’re doing it wrong. You should be using a global Map, this reference would be shared amongst ALL virtual users.
If you really want to compute user specific data, store it and fetch it later, you should store in the Session, not in a global reference.

But then, in your case, you could directly compute everything on spot:

.formParamMap { session =>
// return a Map computed based on session’s content
}