pub struct GooseUser {
pub started: Instant,
pub scenarios_index: usize,
pub client: Client,
pub base_url: Url,
pub config: GooseConfiguration,
pub logger: Option<Sender<Option<GooseLog>>>,
pub throttle: Option<Sender<bool>>,
pub is_throttled: bool,
pub metrics_channel: Option<Sender<GooseMetric>>,
pub shutdown_channel: Option<Sender<usize>>,
pub weighted_users_index: usize,
pub load_test_hash: u64,
/* private fields */
}
Expand description
An individual user state, repeatedly running all Transaction
s
in a specific Scenario
.
Fields
started: Instant
The Instant when this GooseUser
client started.
scenarios_index: usize
An index into the internal GooseAttack
.scenarios
vector, indicating which Scenario
is running.
client: Client
Client used to make requests, managing sessions and cookies.
base_url: Url
The base URL to prepend to all relative paths.
config: GooseConfiguration
A local copy of the global GooseConfiguration
.
logger: Option<Sender<Option<GooseLog>>>
Channel to logger.
throttle: Option<Sender<bool>>
Channel to throttle.
is_throttled: bool
Normal transactions are optionally throttled,
test_start
and
test_stop
transactions are not.
metrics_channel: Option<Sender<GooseMetric>>
Channel for sending metrics to the parent for aggregation.
shutdown_channel: Option<Sender<usize>>
Channel for notifying the parent when thread shuts down.
weighted_users_index: usize
An index into the internal GooseAttack
.weighted_users
vector, indicating which weighted GooseUser
is running.
load_test_hash: u64
Load test hash.
Implementations
sourceimpl GooseUser
impl GooseUser
sourcepub fn new(
scenarios_index: usize,
base_url: Url,
configuration: &GooseConfiguration,
load_test_hash: u64
) -> Result<Self, GooseError>
pub fn new(
scenarios_index: usize,
base_url: Url,
configuration: &GooseConfiguration,
load_test_hash: u64
) -> Result<Self, GooseError>
Create a new user state.
sourcepub fn single(
base_url: Url,
configuration: &GooseConfiguration
) -> Result<Self, GooseError>
pub fn single(
base_url: Url,
configuration: &GooseConfiguration
) -> Result<Self, GooseError>
Create a new single-use user.
sourcepub fn get_iterations(&self) -> usize
pub fn get_iterations(&self) -> usize
Returns the number of iterations this GooseUser has run through it’s
assigned Scenario
.
sourcepub fn get_session_data<T: GooseUserData>(&self) -> Option<&T>
pub fn get_session_data<T: GooseUserData>(&self) -> Option<&T>
Returns an optional reference to per-GooseUser
session data.
Leaves the session data in-place, returning an optional reference to the
original session data if existing and of the correct type. Returns None
if no session data has been set or the session data set is not of type T
.
Example
use goose::prelude::*;
struct Foo(String);
let mut transaction = transaction!(get_session_data_function);
/// A very simple transaction that makes a GET request.
async fn get_session_data_function(user: &mut GooseUser) -> TransactionResult {
let foo = user.get_session_data::<Foo>().expect("Missing session data!");
println!("Session data: {}", foo.0);
Ok(())
}
sourcepub fn get_session_data_unchecked<T: GooseUserData>(&self) -> &T
pub fn get_session_data_unchecked<T: GooseUserData>(&self) -> &T
Returns a reference to per-GooseUser
session data, without doing any
validation that the session data exists and is of the correct type.
Leaves the session data in-place, returning a reference to the original
session data. Calling this method on a GooseUser
object without
session data or with a different type T
will panic.
For a safe alternative see GooseUser::get_session_data
.
Example
use goose::prelude::*;
struct Foo(String);
let mut transaction = transaction!(get_session_data_unchecked_function);
/// A very simple transaction that makes a GET request.
async fn get_session_data_unchecked_function(user: &mut GooseUser) -> TransactionResult {
let foo = user.get_session_data_unchecked::<Foo>();
println!("Session data: {}", foo.0);
Ok(())
}
sourcepub fn get_session_data_mut<T: GooseUserData>(&mut self) -> Option<&mut T>
pub fn get_session_data_mut<T: GooseUserData>(&mut self) -> Option<&mut T>
Returns an optional mutable reference to per-GooseUser
session data.
Leaves the session data in-place, returning an optional mutable reference
to the original session data if existing and of the correct type. Returns
None
if no session data has been set or the session data set is not of
type T
.
Example
use goose::prelude::*;
struct Foo(String);
let mut transaction = transaction!(get_session_data_mut_function);
/// A very simple transaction that makes a GET request.
async fn get_session_data_mut_function(user: &mut GooseUser) -> TransactionResult {
let foo = user.get_session_data_mut::<Foo>().expect("Missing session data!");
foo.0 = "Bar".to_owned();
Ok(())
}
sourcepub fn get_session_data_unchecked_mut<T: GooseUserData>(&mut self) -> &mut T
pub fn get_session_data_unchecked_mut<T: GooseUserData>(&mut self) -> &mut T
Returns a mutable reference to per-GooseUser
session data, without
doing any validation that the session data exists and is of the correct
type.
Leaves the session data in-place, returning a mutable reference to the
original session data. Calling this method on a GooseUser
object
without session data or with a different type T
will panic.
For a safe alternative see GooseUser::get_session_data_mut
.
Example
use goose::prelude::*;
struct Foo(String);
let mut transaction = transaction!(get_session_data_unchecked_mut_function);
/// A very simple transaction that makes a GET request.
async fn get_session_data_unchecked_mut_function(user: &mut GooseUser) -> TransactionResult {
let foo = user.get_session_data_unchecked_mut::<Foo>();
foo.0 = "Bar".to_owned();
Ok(())
}
sourcepub fn set_session_data<T: GooseUserData>(&mut self, data: T)
pub fn set_session_data<T: GooseUserData>(&mut self, data: T)
Sets session data for the current GooseUser
.
If session data already exists for the current GooseUser
, it will be
replaced. Session data must be of a type implementing the
GooseUserData
trait.
Example
use goose::prelude::*;
struct Foo(String);
let mut transaction = transaction!(set_session_data_function);
/// A very simple transaction that makes a GET request.
async fn set_session_data_function(user: &mut GooseUser) -> TransactionResult {
user.set_session_data(Foo("Foo".to_string()));
Ok(())
}
sourcepub fn build_url(&self, path: &str) -> Result<String, TransactionError>
pub fn build_url(&self, path: &str) -> Result<String, TransactionError>
A helper that prepends a base_url
to all relative paths.
A base_url
is determined per user thread, using the following order
of precedence:
--host
(host specified on the command line when running load test)Scenario
.host
(default host defined for the current scenario)GooseDefault::Host
(default host defined for the current load test)
sourcepub async fn get(
&mut self,
path: &str
) -> Result<GooseResponse, TransactionError>
pub async fn get(
&mut self,
path: &str
) -> Result<GooseResponse, TransactionError>
A helper to make a GET
request of a path and collect relevant metrics.
Automatically prepends the correct host.
Calls to get()
return a GooseResponse
object which
contains a copy of the request you made (GooseRequestMetric
),
and the response (reqwest::Response
).
If you need to set headers, change timeouts, or otherwise make use of the
reqwest::RequestBuilder
object, refer to GooseUser::get_request_builder
.
Example
GET a URL.
use goose::prelude::*;
let mut transaction = transaction!(get_function);
/// A very simple transaction that makes a GET request.
async fn get_function(user: &mut GooseUser) -> TransactionResult {
let _goose = user.get("path/to/foo/").await?;
Ok(())
}
sourcepub async fn get_named(
&mut self,
path: &str,
name: &str
) -> Result<GooseResponse, TransactionError>
pub async fn get_named(
&mut self,
path: &str,
name: &str
) -> Result<GooseResponse, TransactionError>
A helper to make a named GET
request of a path and collect relevant metrics.
Automatically prepends the correct host.
Calls to get_named()
return a GooseResponse
object which
contains a copy of the request you made (GooseRequestMetric
),
and the response (reqwest::Response
).
If you need to set headers, change timeouts, or otherwise make use of the
reqwest::RequestBuilder
object, refer to GooseUser::get_request_builder
.
Example
GET a URL and name the request in collected metrics.
use goose::prelude::*;
let mut transaction = transaction!(get_function);
/// A very simple transaction that makes a GET request, naming it for metrics.
async fn get_function(user: &mut GooseUser) -> TransactionResult {
let _goose = user.get_named("path/to/foo/", "foo").await?;
Ok(())
}
sourcepub async fn post<T: Into<Body>>(
&mut self,
path: &str,
body: T
) -> Result<GooseResponse, TransactionError>
pub async fn post<T: Into<Body>>(
&mut self,
path: &str,
body: T
) -> Result<GooseResponse, TransactionError>
A helper to make a POST
request of a path and collect relevant metrics.
Automatically prepends the correct host.
Calls to post()
return a GooseResponse
object which
contains a copy of the request you made (GooseRequestMetric
),
and the response (reqwest::Response
).
If you need to set headers, change timeouts, or otherwise make use of the
reqwest::RequestBuilder
object, refer to GooseUser::get_request_builder
.
Example
POST an arbitrary body.
use goose::prelude::*;
let mut transaction = transaction!(post_function);
/// A very simple transaction that makes a POST request.
async fn post_function(user: &mut GooseUser) -> TransactionResult {
let _goose = user.post("path/to/foo/", "BODY BEING POSTED").await?;
Ok(())
}
sourcepub async fn post_form<T: Serialize + ?Sized>(
&mut self,
path: &str,
form: &T
) -> Result<GooseResponse, TransactionError>
pub async fn post_form<T: Serialize + ?Sized>(
&mut self,
path: &str,
form: &T
) -> Result<GooseResponse, TransactionError>
A helper to make a POST
request of a form on a path and collect relevant metrics.
Automatically prepends the correct host.
Calls to post_form()
return a GooseResponse
object which
contains a copy of the request you made (GooseRequestMetric
),
and the response (reqwest::Response
).
If you need to set headers, change timeouts, or otherwise make use of the
reqwest::RequestBuilder
object, refer to GooseUser::get_request_builder
.
Example
POST a form.
use goose::prelude::*;
let mut transaction = transaction!(post_function);
/// A very simple transaction that POSTs form parameters.
async fn post_function(user: &mut GooseUser) -> TransactionResult {
let params = [("foo", "bar"), ("foo2", "bar2")];
let _goose = user.post_form("path/to/foo/", ¶ms).await?;
Ok(())
}
sourcepub async fn post_json<T: Serialize + ?Sized>(
&mut self,
path: &str,
json: &T
) -> Result<GooseResponse, TransactionError>
pub async fn post_json<T: Serialize + ?Sized>(
&mut self,
path: &str,
json: &T
) -> Result<GooseResponse, TransactionError>
A helper to make a POST
request of json on a path and collect relevant metrics.
Automatically prepends the correct host.
Calls to post_json()
return a GooseResponse
object which
contains a copy of the request you made (GooseRequestMetric
),
and the response (reqwest::Response
).
If you need to set headers, change timeouts, or otherwise make use of the
reqwest::RequestBuilder
object, refer to GooseUser::get_request_builder
.
Example
POST an arbitrary JSON object.
use goose::prelude::*;
let mut transaction = transaction!(post_function);
/// A very simple transaction that POSTs an arbitrary json object.
async fn post_function(user: &mut GooseUser) -> TransactionResult {
let json = &serde_json::json!({
"foo": "bar",
"foo2": "bar2"
});
let _goose = user.post_json("path/to/foo/", &json).await?;
Ok(())
}
sourcepub async fn head(
&mut self,
path: &str
) -> Result<GooseResponse, TransactionError>
pub async fn head(
&mut self,
path: &str
) -> Result<GooseResponse, TransactionError>
A helper to make a HEAD
request of a path and collect relevant metrics.
Automatically prepends the correct host.
Calls to head()
return a GooseResponse
object which
contains a copy of the request you made (GooseRequestMetric
),
and the response (reqwest::Response
).
If you need to set headers, change timeouts, or otherwise make use of the
reqwest::RequestBuilder
object, refer to GooseUser::get_request_builder
.
Example
Make a HEAD request.
use goose::prelude::*;
let mut transaction = transaction!(head_function);
/// A very simple transaction that makes a HEAD request.
async fn head_function(user: &mut GooseUser) -> TransactionResult {
let _goose = user.head("path/to/foo/").await?;
Ok(())
}
sourcepub async fn delete(
&mut self,
path: &str
) -> Result<GooseResponse, TransactionError>
pub async fn delete(
&mut self,
path: &str
) -> Result<GooseResponse, TransactionError>
A helper to make a DELETE
request of a path and collect relevant metrics.
Automatically prepends the correct host.
Calls to delete()
return a GooseResponse
object which
contains a copy of the request you made (GooseRequestMetric
),
and the response (reqwest::Response
).
If you need to set headers, change timeouts, or otherwise make use of the
reqwest::RequestBuilder
object, refer to GooseUser::get_request_builder
.
Example
Make a DELETE request.
use goose::prelude::*;
let mut transaction = transaction!(delete_function);
/// A very simple transaction that makes a DELETE request.
async fn delete_function(user: &mut GooseUser) -> TransactionResult {
let _goose = user.delete("path/to/foo/").await?;
Ok(())
}
sourcepub fn get_request_builder(
&self,
method: &GooseMethod,
path: &str
) -> Result<RequestBuilder, TransactionError>
pub fn get_request_builder(
&self,
method: &GooseMethod,
path: &str
) -> Result<RequestBuilder, TransactionError>
Used to get a reqwest::RequestBuilder
object. If no reqwest::RequestBuilder
is
already defined in the GooseRequest
passed to GooseUser::request
it will automatically
invoke this function.
The HTTP request method must be defined as a GooseMethod
, and the path that will be requested
must be defined as a &str
.
It is possible to use this function to directly interact with the reqwest::RequestBuilder
object and the GooseRequest
object during load tests. In the following example, we set a
timeout on the Request, and tell Goose to expect a 404 HTTP response status code.
Example
Request a non-existent page, timing out after 500 milliseconds.
use goose::prelude::*;
let mut transaction = transaction!(test_404);
async fn test_404(user: &mut GooseUser) -> TransactionResult {
use std::time::Duration;
// Manually interact with the Reqwest RequestBuilder object.
let request_builder = user.get_request_builder(&GooseMethod::Get, "no/such/path")?
// Configure the request to timeout if it takes longer than 500 milliseconds.
.timeout(Duration::from_millis(500));
// Manually build a GooseRequest.
let goose_request = GooseRequest::builder()
// Manually add our custom RequestBuilder object.
.set_request_builder(request_builder)
// Tell Goose to expect a 404 status code.
.expect_status_code(404)
// Turn the GooseRequestBuilder object into a GooseRequest.
.build();
// Finally make the actual request with our custom GooseRequest object.
let _goose = user.request(goose_request).await?;
Ok(())
}
sourcepub async fn request<'a>(
&mut self,
request: GooseRequest<'_>
) -> Result<GooseResponse, TransactionError>
pub async fn request<'a>(
&mut self,
request: GooseRequest<'_>
) -> Result<GooseResponse, TransactionError>
Makes a request for the provided GooseRequest
object, and if metrics are enabled
captures relevant metrics.
Calls to request()
return a Result
containing a GooseResponse
on success, and a
flume::SendError
<bool>
,
on failure. Failure only happens when --throttle-requests
is enabled and the load test
completes. The GooseResponse
object contains a copy of
the request you made (GooseRequestMetric
), and the
response (reqwest::Response
).
Example
Make a GET request.
use goose::prelude::*;
let mut transaction = transaction!(get_function);
/// A simple transaction that makes a GET request.
async fn get_function(user: &mut GooseUser) -> TransactionResult {
let goose_request = GooseRequest::builder()
// Goose will prepend a host name to this path.
.path("path/to/loadtest")
// GET is the default method, this is not necessary.
.method(GooseMethod::Get)
// Assemble the `GooseRequestBuilder` into a `GooseRequest.
.build();
let goose = user.request(goose_request).await?;
// Do stuff with goose.request and/or goose.response here.
Ok(())
}
sourcepub fn set_success(&self, request: &mut GooseRequestMetric) -> TransactionResult
pub fn set_success(&self, request: &mut GooseRequestMetric) -> TransactionResult
Manually mark a request as a success.
Goose determines if a request was successful based on the the HTTP response status
code. By default, it uses reqwest::StatusCode::is_success
. If an alternative
HTTP response code is expected, use GooseRequestBuilder::expect_status_code
. If
validation requires additional logic, you can use set_success().
A copy of your original request is returned with the response, and a mutable copy must be included when setting a request as a success.
Example
use goose::prelude::*;
let mut transaction = transaction!(get_function);
/// A simple transaction that makes a GET request.
async fn get_function(user: &mut GooseUser) -> TransactionResult {
let mut goose = user.get("404").await?;
if let Ok(response) = &goose.response {
// We expect a 404 here.
if response.status() == 404 {
return user.set_success(&mut goose.request);
}
}
Err(TransactionError::RequestFailed {
raw_request: goose.request.clone(),
})
}
sourcepub fn set_failure(
&self,
tag: &str,
request: &mut GooseRequestMetric,
headers: Option<&HeaderMap>,
body: Option<&str>
) -> TransactionResult
pub fn set_failure(
&self,
tag: &str,
request: &mut GooseRequestMetric,
headers: Option<&HeaderMap>,
body: Option<&str>
) -> TransactionResult
Manually mark a request as a failure.
By default, Goose will consider any response with a 2xx status code as a success. You may require more advanced logic, in which a 2xx status code is actually a failure. A copy of your original request is returned with the response, and a mutable copy must be included when setting a request as a failure.
Calls to set_failure
must include four parameters. The first, tag
, is an
arbitrary string identifying the reason for the failure, used when logging. The
second, request
, is a mutable reference to the
(GooseRequestMetric
) object of the request being
identified as a failure (the contained success
field will be set to false
,
and the update
field will be set to true
). The last two parameters, header
and body
, are optional and used to provide more detail in logs.
The value of tag
will normally be collected into the errors summary table if
metrics are being displayed. However, if set_failure
is called multiple times,
or is called on a request that was already an error, only the first error will
be collected.
This also calls GooseUser::log_debug
.
Example
use goose::prelude::*;
let mut transaction = transaction!(loadtest_index_page);
async fn loadtest_index_page(user: &mut GooseUser) -> TransactionResult {
let mut goose = user.get("").await?;
if let Ok(response) = goose.response {
// We only need to check pages that returned a success status code.
if response.status().is_success() {
match response.text().await {
Ok(text) => {
// If the expected string doesn't exist, this page load
// was a failure.
if !text.contains("this string must exist") {
// As this is a named request, pass in the name not the URL
return user.set_failure("string missing", &mut goose.request, None, None);
}
}
// Empty page, this is a failure.
Err(_) => {
return user.set_failure("empty page", &mut goose.request, None, None);
}
}
}
};
Ok(())
}
sourcepub fn log_debug(
&self,
tag: &str,
request: Option<&GooseRequestMetric>,
headers: Option<&HeaderMap>,
body: Option<&str>
) -> TransactionResult
pub fn log_debug(
&self,
tag: &str,
request: Option<&GooseRequestMetric>,
headers: Option<&HeaderMap>,
body: Option<&str>
) -> TransactionResult
Write to debug_file
if enabled.
This function provides a mechanism for optional debug logging when a load test is running. This can be especially helpful when writing a load test. Each entry must include a tag, which is an arbitrary string identifying the debug message. It may also optionally include references to the GooseRequestMetric made, the headers returned by the server, and the response body returned by the server,
As the response body can be large, the --no-debug-body
option (or
GooseDefault::NoDebugBody
default)
can be set to prevent the debug log from including the response body. When this option
is enabled, the body will always show up as null
in the debug log.
Calls to GooseUser::set_failure
automatically invoke log_debug
.
To enable the debug log, a load test must be run with the --debug-log-file=foo
option set, where foo
is either a relative or an absolute path of the log file
to create. Any existing file will be overwritten.
In the following example, we are logging debug messages whenever there are errors.
Example
use goose::prelude::*;
let mut transaction = transaction!(loadtest_index_page);
async fn loadtest_index_page(user: &mut GooseUser) -> TransactionResult {
let mut goose = user.get("").await?;
match goose.response {
Ok(response) => {
// Grab a copy of the headers so we can include them when logging errors.
let headers = &response.headers().clone();
// We only need to check pages that returned a success status code.
if !response.status().is_success() {
match response.text().await {
Ok(html) => {
// Server returned an error code, log everything.
user.log_debug(
"error loading /",
Some(&goose.request),
Some(headers),
Some(&html),
);
},
Err(e) => {
// No body was returned, log everything else.
user.log_debug(
&format!("error loading /: {}", e),
Some(&goose.request),
Some(headers),
None,
);
}
}
}
},
// No response from server.
Err(e) => {
user.log_debug(
"no response from server when loading /",
Some(&goose.request),
None,
None,
);
}
}
Ok(())
}
sourcepub async fn set_client_builder(
&mut self,
builder: ClientBuilder
) -> Result<(), TransactionError>
pub async fn set_client_builder(
&mut self,
builder: ClientBuilder
) -> Result<(), TransactionError>
Manually build a
reqwest::Client
.
By default, Goose configures the following options when building a
reqwest::Client
:
- reports itself as the
user_agent
requesting web pages (iegoose/0.15.2
); - stores cookies, generally necessary if you aim to simulate logged in users;
- enables
gzip
compression; - sets a 60 second
timeout
all on all requests.
Default configuration:
use reqwest::Client;
use core::time::Duration;
static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
let builder = Client::builder()
.user_agent(APP_USER_AGENT)
.cookie_store(true)
.gzip(true)
.timeout(Duration::from_secs(60));
Alternatively, you can use this function to manually build a
reqwest::Client
.
with custom configuration. Available options are found in the
reqwest::ClientBuilder
documentation.
When manually building a
reqwest::Client
,
there are a few things to be aware of:
- Manually building a client in
test_start
will only affect requests made during test setup; - Manually building a client in
test_stop
will only affect requests made during test teardown; - A manually built client is specific to a single Goose thread – if you are
generating a large load test with many users, each will need to manually build their
own client (typically you’d do this in a Transaction that is registered with
Transaction::set_on_start()
in each Scenario requiring a custom client; - Manually building a client will completely replace the automatically built client with a brand new one, so any configuration, cookies or headers set in the previously built client will be gone;
- You must include all desired configuration, as you are completely replacing Goose
defaults. For example, if you want Goose clients to store cookies, you will have to
include
.cookie_store(true)
.
In the following example, the Goose client is configured with a different user agent, sets a default header on every request, stores cookies, supports gzip compression, and times out requests after 30 seconds.
Example
use goose::prelude::*;
use core::time::Duration;
transaction!(setup_custom_client).set_on_start();
async fn setup_custom_client(user: &mut GooseUser) -> TransactionResult {
use reqwest::{Client, header};
// Build a custom HeaderMap to include with all requests made by this client.
let mut headers = header::HeaderMap::new();
headers.insert("X-Custom-Header", header::HeaderValue::from_str("custom value").unwrap());
// Build a custom client.
let builder = Client::builder()
.default_headers(headers)
.user_agent("custom user agent")
.cookie_store(true)
.gzip(true)
.timeout(Duration::from_secs(30));
// Assign the custom client to this GooseUser.
user.set_client_builder(builder).await?;
Ok(())
}
Alternative Compression Algorithms
Reqwest also supports
brotli
and
deflate
compression.
To enable either, you must enable the features in your load test’s Cargo.toml
, for example:
reqwest = { version = "^0.11.4", default-features = false, features = [
"brotli",
"cookies",
"deflate",
"gzip",
"json",
] }
Once enabled, you can add .brotli(true)
and/or .deflate(true)
to your custom
reqwest::Client::builder()
, following the documentation above.
Custom Cookies
Custom cookies can also be manually set when building a custom reqwest::Client
. This requires
loading the GooseUser::base_url
being load tested in order to properly build the cookie. Then
a custom reqwest::cookie::Jar
is created and the custom cookie is added with
reqwest::cookie::Jar::add_cookie_str
. Finally, the new cookie jar must be specified as the
reqwest::ClientBuilder::cookie_provider
for the custom client.
Example
use reqwest::{cookie::Jar, Client};
use std::sync::Arc;
use goose::prelude::*;
transaction!(custom_cookie_with_custom_client).set_on_start();
async fn custom_cookie_with_custom_client(user: &mut GooseUser) -> TransactionResult {
// Prepare the contents of a custom cookie.
let cookie = "my-custom-cookie=custom-value";
// Pre-load one or more cookies into a custom cookie jar to use with this client.
let jar = Jar::default();
jar.add_cookie_str(
cookie,
&user.base_url,
);
// Build a custom client.
let builder = Client::builder()
.user_agent("example-loadtest")
.cookie_store(true)
.cookie_provider(Arc::new(jar))
.gzip(true);
// Assign the custom client to this GooseUser.
user.set_client_builder(builder).await?;
Ok(())
}
sourcepub fn set_base_url(&mut self, host: &str) -> Result<(), TransactionError>
pub fn set_base_url(&mut self, host: &str) -> Result<(), TransactionError>
Some websites use multiple domains to serve traffic, redirecting depending on
the user’s roll. For this reason, Goose needs to respect a redirect of the
base_url
and subsequent paths should be built from the redirect domain.
For example, if the base_url
(ie --host
) is set to foo.example.com
and the
load test requests /login
, thereby loading http://foo.example.com/login
and
this request gets redirected by the server to http://foo-secure.example.com/
,
subsequent requests made by this user need to be against the new
foo-secure.example.com domain
. (Further, if the base_url
is again redirected,
such as when loading http://foo-secure.example.com/logout
, the user should
again follow for subsequent requests, perhaps in this case back to
foo.example.com
.)
Load tests can also request absolute URLs, and if these URLs are redirected
it does not affect the base_url
of the load test. For example, if
foo.example.com
is the base url, and the load test requests
http://bar.example.com
(a different domain) and this request gets redirected
to http://other.example.com
, subsequent relative requests would still be made
against foo.example.com
.
This functionality is used internally by Goose to follow redirects of the
base_url
when --sticky-follow
is specified at run time, or
set_default
(
GooseDefault::StickyFollow
, true)
is enabled. It is also
available to be manually invoked from a load test such as in the following
example.
Example
use goose::prelude::*;
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), GooseError> {
let _goose_metrics = GooseAttack::initialize()?
.register_scenario(scenario!("LoadtestTransactions")
.set_host("http://foo.example.com/")
.set_wait_time(Duration::from_secs(0), Duration::from_secs(3))?
.register_transaction(transaction!(transaction_foo).set_weight(10)?)
.register_transaction(transaction!(transaction_bar))
)
// Set a default run time so this test runs to completion.
.set_default(GooseDefault::RunTime, 1)?
.execute()
.await?;
Ok(())
}
async fn transaction_foo(user: &mut GooseUser) -> TransactionResult {
let _goose = user.get("").await?;
Ok(())
}
async fn transaction_bar(user: &mut GooseUser) -> TransactionResult {
// Before this transaction runs, all requests are being made against
// http://foo.example.com, after this transaction runs all subsequent
// requests are made against http://bar.example.com/.
user.set_base_url("http://bar.example.com/");
let _goose = user.get("").await?;
Ok(())
}
Auto Trait Implementations
impl !RefUnwindSafe for GooseUser
impl Send for GooseUser
impl Sync for GooseUser
impl Unpin for GooseUser
impl !UnwindSafe for GooseUser
Blanket Implementations
sourceimpl<T> BorrowMut<T> for T where
T: ?Sized,
impl<T> BorrowMut<T> for T where
T: ?Sized,
const: unstable · sourcefn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
sourceimpl<T> Downcast for T where
T: Any,
impl<T> Downcast for T where
T: Any,
sourcefn into_any(self: Box<T, Global>) -> Box<dyn Any + 'static, Global>
fn into_any(self: Box<T, Global>) -> Box<dyn Any + 'static, Global>
Convert Box<dyn Trait>
(where Trait: Downcast
) to Box<dyn Any>
. Box<dyn Any>
can
then be further downcast
into Box<ConcreteType>
where ConcreteType
implements Trait
. Read more
sourcefn into_any_rc(self: Rc<T>) -> Rc<dyn Any + 'static>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any + 'static>
Convert Rc<Trait>
(where Trait: Downcast
) to Rc<Any>
. Rc<Any>
can then be
further downcast
into Rc<ConcreteType>
where ConcreteType
implements Trait
. Read more
sourcefn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Convert &Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &Any
’s vtable from &Trait
’s. Read more
sourcefn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert &mut Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &mut Any
’s vtable from &mut Trait
’s. Read more
sourceimpl<T> DowncastSync for T where
T: Any + Send + Sync,
impl<T> DowncastSync for T where
T: Any + Send + Sync,
sourceimpl<T> Instrument for T
impl<T> Instrument for T
sourcefn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
sourcefn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
impl<V, T> VZip<V> for T where
V: MultiLane<T>,
impl<V, T> VZip<V> for T where
V: MultiLane<T>,
fn vzip(self) -> V
sourceimpl<T> WithSubscriber for T
impl<T> WithSubscriber for T
sourcefn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> where
S: Into<Dispatch>,
fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> where
S: Into<Dispatch>,
Attaches the provided Subscriber
to this type, returning a
WithDispatch
wrapper. Read more
sourcefn with_current_subscriber(self) -> WithDispatch<Self>
fn with_current_subscriber(self) -> WithDispatch<Self>
Attaches the current default Subscriber
to this type, returning a
WithDispatch
wrapper. Read more