HTTP Request
Learn about get put post delete head patch options method, and configure the query parameters, HTTP checks, multipart and request body
HTTP support has a dedicated DSL, whose entry point is the http(requestName: Expression[String])
method.
This request name is important because it will act as a key when computing stats for the reports. If the same name appears in multiple places in a Simulation, Gatling will consider those requests are of the same type and their statistics will be aggregated.
HTTP requests have to be passed to the exec()
method in order to be attached to the scenario and be executed.
// embedded style
scenario("MyScenario")
.exec(http("RequestName").get("url"))
// non embedded style
val request = http("RequestName").get("url")
scenario("MyScenario")
.exec(request)
Method and URL
HTTP protocol requires 2 mandatory parameters: the method and the URL.
Gatling provides built-ins for the most common methods. Those are simply the method name in minor case:
get(url: Expression[String])
put(url: Expression[String])
post(url: Expression[String])
delete(url: Expression[String])
head(url: Expression[String])
patch(url: Expression[String])
options(url: Expression[String])
Gatling also supports custom methods (e.g. you can use the method PURGE to purge Nginx cache):
httpRequest(method: String, url: Expression[String])
This is how an HTTP request is declared:
// general structure of an HTTP request
http(requestName).method(url)
// concrete examples
http("Retrieve home page").get("https://github.com/gatling/gatling")
http("Login").post("https://github.com/session")
http("Nginx cache purge").httpRequest("PURGE", "http://myNginx.com")
Query Parameters
Frameworks and developers often pass additional information in the query, which is the part of the url after the ?
. A query is composed of key=value pairs, separated by &
. Those are named query parameters.
For example, https://github.com/gatling/gatling/issues?milestone=1&state=open
contains 2 query parameters:
milestone=1
: the key is milestone and its value is 1state=open
: the key is state and its value is open
In order to set the query parameters of an HTTP request, you can:
- either pass the full query in the url, e.g.:
http("Getting issues")
.get("https://github.com/gatling/gatling/issues?milestone=1&state=open")
- or pass query parameters one by one to the method named
queryParam(key: Expression[String], value: Expression[Any])
, e.g.:
http("Getting issues")
.get("https://github.com/gatling/gatling/issues")
.queryParam("milestone", "1")
.queryParam("state", "open")
Of course, you can use Gatling Expression Language (EL) to make those values dynamic based on data in the virtual user’s session:
http("Value from session example")
.get("https://github.com/gatling/gatling")
.queryParam("myKey", "${sessionKey}")
If you’d like to specify a query parameter without value, you have to use queryParam("key", "")
:
// GET https://github.com/gatling/gatling?myKey
http("Empty value example")
.get("https://github.com/gatling/gatling")
.queryParam("myKey", "")
If you’d like to pass multiple values for your parameter, but all at once, you can use multivaluedQueryParam(key: Expression[String], values: Expression[Seq[Any]])
:
http("Request with multivaluedQueryParam")
.get("myUrl")
.multivaluedQueryParam("multi1", "${foo}") // where foo is the name of a Seq Session attribute
.multivaluedQueryParam("multi2", session => Seq("foo", "bar"))
If you want to add multiple query parameters at once, there are two suitable methods:
queryParamSeq(seq: Expression[Seq[(String, Any)]])
http("Getting issues")
.get("https://github.com/gatling/gatling/issues")
.queryParamSeq(Seq(("milestone", "1"), ("state", "open")))
queryParamMap(map: Expression[Map[String, Any]])
http("Getting issues")
.get("https://github.com/gatling/gatling/issues")
.queryParamMap(Map("milestone" -> "1", "state" -> "open"))
Expression[T]
, i.e. ‘key’ parameter is an Expression[String]
and so on, if you have more specific needs you can also provide an arbitrary Expression[T]
, i.e. a Session => Validation[T]
function.
This function will be evaluated against the user session every time this one pass through it.
For a deeper look at Expression
see dedicated section here.Headers
HTTP protocol uses headers to exchange information between client and server that is not part of the message (stored in the body of the request, if there is one).
Gatling HTTP allows you to specify any header you want to with the header(name: String, value: Expression[String])
and headers(newHeaders: Map[String, String])
methods.
Here are some examples:
// Defining a map of headers before the scenario allows you to reuse these in several requests
val sentHeaders = Map("Content-Type" -> "application/javascript", "Accept" -> "text/html")
http("Custom headers")
.post("myUrl")
// Adds several headers at once
.headers(sentHeaders)
// Adds another header to the request
.header("Keep-Alive", "150")
// Overrides the Content-Type header
.header("Content-Type", "application/json")
HttpHeaderNames.ContentType
.
You can find a list of the predefined constants here.http("foo").get("bar").asJson
is equivalent to:
http("foo")
.get("bar")
.header(HttpHeaderNames.ContentType, HttpHeaderValues.ApplicationJson)
.header(HttpHeaderNames.Accept, HttpHeaderValues.ApplicationJson)
http("foo").get("bar").asXml
is equivalent to:
http("foo")
.get("bar")
.header(HttpHeaderNames.ContentType, HttpHeaderValues.ApplicationXml)
.header(HttpHeaderNames.Accept, HttpHeaderValues.ApplicationXml)
HttpProtocol
.Signature Calculator
You might want to edit the HTTP requests before they’re being sent over the wire, based on other request information: url, headers and/or body. For example, you might want to generate some HMAC header.
This can only happen after Gatling has resolved the request, e.g. computed the body based on a template.
Gatling provides the SignatureCalculator
API:
public interface SignatureCalculator {
void sign(Request request) throws Exception;
}
request
is the mutable object that’s been computed so far.
You can typically use its attributes to compute a new header that you will add to the existing headers.
The proper method signature for setting a SignatureCalculator
is:
.sign(calculator: Expression[SignatureCalculator])
but you can pass a static SignatureCalculator
instead of an Expression
and Gatling DSL will automatically lift it for you.
Gatling also provides a built-in for OAuth1:
.signWithOAuth1(consumerKey: Expression[String],
clientSharedSecret: Expression[String],
token: Expression[String],
tokenSecret: Expression[String])
Authentication
You can set the authentication methods at request level with these methods:
basicAuth(username: Expression[String], password: Expression[String])
digestAuth(username: Expression[String], password: Expression[String])
http("My BASIC secured request").get("http://my.secured.uri").basicAuth("myUser", "myPassword")
http("My DIGEST secured request").get("http://my.secured.uri").digestAuth("myUser", "myPassword")
HttpProtocol
.Outgoing Proxy
You can tell Gatling to use a proxy to send the HTTP requests. You can optionally set a different port for HTTPS and credentials:
http("Getting issues")
.get("https://github.com/gatling/gatling/issues")
.proxy(Proxy("myHttpProxyHost", 8080).httpsPort(8143).credentials("myUsername", "myPassword")) // HTTP proxy
.proxy(Proxy("mySocks4proxyHost", 8080).socks4) // SOCKS 4 proxy
.proxy(Proxy("mySocks5proxyHost", 8080).socks5) // SOCKS 5 proxy
HttpProtocol
.Virtual Host
You can tell Gatling to override the default computed virtual host with the method virtualHost(virtualHost: Expression[String])
:
// GET https://mobile.github.com/gatling/gatling instead of GET https://www.github.com/gatling/gatling
http("Getting issues")
.get("https://www.github.com/gatling/gatling/issues")
.virtualHost("mobile")
HttpProtocol
.HTTP Checks
You can add checks on a request:
http("Getting issues")
.get("https://www.github.com/gatling/gatling/issues")
.check(myCheck)
For more information, see the HTTP Checks reference section.
For a given request, you can also disable common checks that were defined on the HttpProtocol
with ignoreDefaultChecks
:
http("Getting issues")
.get("https://www.github.com/gatling/gatling/issues")
.ignoreDefaultChecks
FollowRedirect
For a given request, you can use disableFollowRedirect
, just like it can be done globally on the HttpProtocol
:
http("Getting issues")
.get("https://www.github.com/gatling/gatling/issues")
.disableFollowRedirect
Url Encoding
Url components are supposed to be urlencoded. Gatling will encode them for you, there might be some corner cases where already encoded components might be encoded twice.
If you know that your urls are already properly encoded, you can disable this feature with .disableUrlEncoding
.
Silencing
See silencing protocol section for more details.
You can then make the request silent:
http("Getting issues")
.get("https://www.github.com/gatling/gatling/issues")
.silent
You might also want to do the exact opposite, typically on a given resource while resources have been globally turned silent at protocol level:
.resources(
http("Gatling Logo")
.get("https://gatling.io/assets/images/img1.png")
.notSilent
)
Form Parameters
Requests can have parameters defined in their body. This is typically used for form submission, where all the values are stored as POST parameters in the body of the request.
To add such parameters to a POST request, you must use the method formParam(key: Expression[String], value: Expression[Any])
which is actually the same as queryParam
in terms of usage (it has the same signatures).
http("My Form Data")
.post("my.form-action.uri")
.formParam("myKey", "myValue")
As for queryParam
you have two methods to add multiple parameters at once:
formParamSeq(seq: Expression[Seq[(String, Any)]])
:
http("My Form Data")
.post("my.form-action.uri")
.formParamSeq(Seq(("myKey", "myValue"), ("anotherKey", "anotherValue")))
formParamMap(map: Expression[Map[String, Any]])
:
http("My Form Data")
.post("my.form-action.uri")
.formParamMap(Map("myKey" -> "myValue", "anotherKey" -> "anotherValue"))
If you’d like to pass multiple values for your parameter, but all at once, you can use multivaluedFormParam(key: Expression[String], values: Expression[Seq[Any]])
:
http("Request with multivaluedFormParam")
.post("myUrl")
.multivaluedFormParam("multi1", "${foo}") // where foo is the name of a Seq Session attribute
.multivaluedFormParam("multi2", session => List("foo", "bar"))
The method formParam
can also take directly an HttpParam
instance, if you want to build it by hand.
form(seq: Expression[Map[String, Any])
:
Param
http("My Form Data")
.post("my.form-action.uri")
.formParam("myKey", "myValue")
Typically used after capturing a whole form with a form
check.
You can override the form field values with the formParam
and the likes.
Content-Type
header for you if you didn’t specify one.
It will use application/x-www-form-urlencoded
except if there’s also some body parts, in which case it will set multipart/form-data
.File Based Request Bodies
Gatling provides various ways of sending files.
When using the bundle distribution, files must be in the user-files/resources
directory. This location can be overridden, see [configuration`.
When using a build tool such as maven, files must be in src/main/resources
or src/test/resources
.
Multipart Form
This applies only for POST requests. When you find forms asking for text values and a file to upload (usually an email attachment), your browser will send a multipart encoded request.
To define such a request, you have to add the parameters as stated above, and the file to be uploaded at the same time with the following method: formUpload(name: Expression[String], filePath: Expression[String])
.
The Content-Type
header will be set to multipart/form-data
and the file added in addition to the parameters.
One can call formUpload()
multiple times in order to upload multiple files.
http("My Multipart Request")
.post("my.form-action.uri")
.formParam("myKey", "myValue")
.formUpload("myKey2", "myAttachment.txt")
Content-Type
header to multipart/form-data
if you didn’t specify one.application/octet-stream
and the character set defaults to the one configured in gatling.conf
(UTF-8
by default).
Don’t forget to override them when needed.
Then, directly use a body part, e.g. .bodyPart(RawFileBodyPart("file", data.xls").contentType("application/vnd.ms-excel").fileName("data.xls")).asMultipartForm
.asMultipartForm
.
It is equivalent to header(HttpHeaderNames.ContentType, HttpHeaderValues.MultipartFormData)
.
If you use formUpload
the header is automatically set for you.Request Body
You can add a full body to an HTTP request with the dedicated method body(body)
, where body can be:
RawFileBody(path: Expression[String])
where path is the location of a file that will be uploaded as is
RawFileBody
lets you pass a raw file that will be sent as is.
Over regular HTTP, Gatling can optimise sending such a body and directly stream from the file to the socket, without copying in memory.
Of course, this optimisation is disabled over HTTPS, as bytes have to be encoded, i.e. loaded in memory.:
// myFileBody.json is a file that contains
// { "myContent": "myHardCodedValue" }
.body(RawFileBody("myFileBody.json")).asJson
ElFileBody(path: Expression[String])
where path is the location of a file whose content will be parsed and resolved with Gatling EL engine
Here, the file content is parsed and turned into a Gatling EL expression. Of course, it can’t be binary.:
// myFileBody.json is a file that contains
// { "myContent": "${myDynamicValue}" }
.body(ElFileBody("myFileBody.json")).asJson
StringBody(string: Expression[String])
Here, you can pass a raw String, a Gatling EL String, or an Expression function.:
.body(StringBody("""{ "myContent": "myHardCodedValue" }""")).asJson
.body(StringBody("""{ "myContent": "${myDynamicValue}" }""")).asJson
.body(StringBody(session => """{ "myContent": """" + someGenerator(session) + """" }""")).asJson
ByteArrayBody(bytes: Expression[Array[Byte]])
Here, you can pass bytes instead of text.
InputStreamBody(stream: Expression[InputStream])
Here, you can pass a Stream.
PebbleStringBody(template: String)
andPebbleFileBody(path: Expression[String])
Gatling Expression Language is definitively the most optimized templating engine for Gatling, in terms of raw performance. However, it’s a bit limited in terms of logic you can implement in there. If you want loops and conditional blocks, you can use Gatling’s Pebble based templating engine.
.body(PebbleStringBody("""{ "myContent": "{% if myCondition %}{{myDynamicValue}}{% endif %}" }""")).asJson
// myFileBody.json is a file that contains
// { "myContent": "{myDynamicValue}" }
.body(PebbleFileBody("myFileBody.json")).asJson
PebbleFileBody
.Extensions
s with registerPebbleExtensions(extensions: Extension*)
. This can only be do once, and must be done prior to loading any Pebble template.Note that one can take full advantage of Scala 2.10 macros for writing template directly in Scala compiled code instead of relying on a templating engine. See Scala 2.10 string interpolation and Fastring.
For example:
object Templates {
val template: Expression[String] = (session: Session) =>
for {
foo <- session("foo").validate[String]
bar <- session("bar").validate[String]
} yield s"""{ foo: $foo, bar: $bar }"""
}
Multipart Request
You can add a multipart body to an HTTP request and add parts with the dedicated method bodyPart(bodyPart)
, where bodyPart can be:
RawFileBodyPart(path: Expression[String])
RawFileBodyPart(name: Expression[String], path: Expression[String])
where path is the location of a file that will be uploaded as is.
Similar to RawFileBody.
ElFileBodyPart(path: Expression[String])
ElFileBodyPart(name: Expression[String], path: Expression[String])
where path is the location of a file whose content will be parsed and resolved with Gatling EL engine.
Similar to ElFileBody.
StringBodyPart(string: Expression[String])
StringBodyPart(name: Expression[String], string: Expression[String])
Similar to StringBody.
ByteArrayBodyPart(bytes: Expression[Array[Byte])
ByteArrayBodyPart(name: Expression[String], bytes: Expression[Array[Byte])
Similar to ByteArrayBody.
Once bootstrapped, BodyPart has the following methods for setting additional optional information:
contentType(contentType: String)
charset(charset: String)
, part of ofContent-Type
header. If not set, defaults to the one fromgatling.conf
file.fileName(fileName: Expression[String])
, part of the Content-Disposition header.dispositionType(contentId: String)
, part of theContent-Disposition
header. If not set, defaults toform-data
.contentId(contentId: Expression[String])
transferEncoding(transferEncoding: String)
header(name: String, value: Expression[String])
, let you define additional part headers
Request Body Processor
You might want to process the request body before it’s being sent to the wire.
processRequestBody(processor: Body => Body)
: takes a Body => Body
Gatling ships two built-ins:
gzipBody
: compress the request body with GZIPstreamBody
: turn the body into a stream
Response Transformers
Similarly, one might want to process the response before it’s passed to the checks pipeline:
transformResponse(responseTransformer: (Session => Response) => Validation[Response])
The example below shows how to decode some Base64 encoded response body:
import java.util.Base64
import io.gatling.http.response._
import java.nio.charset.StandardCharsets.UTF_8
// ignore when response status code is not 200
.transformResponse {
(session, response) =>
if (response.status.code == 200) {
response.copy(body = new ByteArrayResponseBody(Base64.getDecoder.decode(response.body.string), UTF_8))
} else {
response
}
}
Resources
Gatling allow to fetch resources in parallel in order to emulate the behavior of a real web browser.
At the request level you can use the resources(res: AbstractHttpRequestBuilder[_]*)
method.
For example:
http("Getting issues")
.get("https://www.github.com/gatling/gatling/issues")
.resources(
http("api.js").get("https://collector-cdn.github.com/assets/api.js"),
http("ga.js").get("https://ssl.google-analytics.com/ga.js")
)