Cookies
HTTP Cookie 用于网站和应用在用户设备上存储一些状态信息。通过 Set-Cookie HTTP 头部,服务器告诉客户端希望在用户机器上存储哪些信息。
用户浏览器存储 cookie 数据,并将其与服务器的主机名关联。每次对该主机名进行后续请求时,浏览器都会在 Cookie 头部中包含存储的 cookie 数据。
然后,您可以控制更具体的规则,决定是否发送 cookie 数据,包括将其限制在特定的子域名或路径。您还可以设置 cookie 的过期日期,并告诉浏览器仅通过加密 (SSL/TLS) 连接发送它。
在 k6 中使用 Cookies
在大多数情况下,k6 会透明地管理 cookie 的接收、存储和传输,如描述所示。对您基于 cookie 的网站或应用进行测试将会*正常工作*,而无需您进行任何特殊操作。
然而,在某些情况下,您可能希望对 cookie 有更多控制。k6 提供了多种选项来实现此目的。您可以:
- 直接操作 HTTP 头部,
- 使用更符合人体工程学的 cookie API。
以下部分介绍如何使用 Cookie API。
设置简单的 cookies
为了模拟 cookie 之前已被浏览器设置,并且现在应该包含在对服务器的后续请求中,请将 cookie 包含在 cookies 请求参数中
import http from 'k6/http';
export default function () {
http.get('https://quickpizza.grafana.com/api/cookies', {
cookies: {
my_cookie: 'hello world',
},
});
}
这仅适用于当前请求中的 cookie。它不会发送到任何后续请求。要将 cookie 发送到后续请求,请将其添加到 cookie jar 中。默认情况下,k6 为每个 VU 提供一个 cookie jar,您可以与之交互来设置和检查 cookie
import http from 'k6/http';
export default function () {
const jar = http.cookieJar();
jar.set('https://quickpizza.grafana.com/api/cookies', 'my_cookie', 'hello world');
http.get('https://quickpizza.grafana.com/api/cookies');
}
每个 VU 的 cookie jar 存储所有从服务器的 Set-Cookie 头部接收到的 cookie。您还可以创建“本地 cookie jar”,它们会覆盖每个 VU 的 cookie jar(将在后续部分中介绍)。
您还可以覆盖已经是每个 VU 的 cookie jar 一部分的 cookie
import http from 'k6/http';
import { check } from 'k6';
export default function () {
const jar = http.cookieJar();
jar.set('https://quickpizza.grafana.com/api/cookies', 'my_cookie', 'hello world');
const cookies = {
my_cookie: {
value: 'hello world 2',
replace: true,
},
};
const res = http.get('https://quickpizza.grafana.com/api/cookies', {
cookies,
});
check(res.json(), {
'cookie has correct value': (b) => b.cookies.my_cookie == 'hello world 2',
});
}
访问 cookies
要查看特定响应设置了哪些 cookie,请查看响应对象的 cookies 属性
import http from 'k6/http';
import { check } from 'k6';
export default function () {
const res = http.post('https://quickpizza.grafana.com/api/cookies?my_cookie=hello%20world', {
redirects: 0,
});
check(res, {
"has cookie 'my_cookie'": (r) => r.cookies.my_cookie.length > 0,
'cookie has correct value': (r) => r.cookies.my_cookie[0].value === 'hello world',
});
}
响应对象的 cookies 属性是一个映射,其中键是 cookie 名称,值是响应 cookie 对象的数组。此数组可以支持具有相同名称但不同 domain 或 path 属性的多个 cookie,如 RFC6265 中所述。
响应 cookie 对象的属性
响应 cookie 对象包含以下属性
属性 | 类型 | 描述 |
---|---|---|
name | string | cookie 的名称 |
value | string | cookie 的值 |
domain | string | 决定此 cookie 应发送到哪些主机名的 domain |
path | string | 限制 cookie 仅在请求路径与此值匹配时发送 |
expires | string | cookie 何时过期,这需要采用 RFC1123 格式,例如:Mon, 02 Jan 2006 15:04:05 MST |
max_age | number | 用于与 expires 相同目的,但定义为 cookie 有效的秒数 |
secure | boolean | 如果为 true,cookie 将仅通过加密 (SSL/TLS) 连接发送 |
http_only | boolean | 如果为 true,cookie 在浏览器环境中不会暴露给 JavaScript |
检查 cookie jar 中的 cookie
要查看特定 URL 的 cookie jar 中设置和存储了哪些 cookie,请使用 cookie jar 对象的 cookieForURL() 方法
import http from 'k6/http';
import { check } from 'k6';
export default function () {
const res = http.post('https://quickpizza.grafana.com/api/cookies?my_cookie=hello%20world', {
redirects: 0,
});
const jar = http.cookieJar();
const cookies = jar.cookiesForURL('https://quickpizza.grafana.com/api/cookies');
check(res, {
"has cookie 'my_cookie'": (r) => cookies.my_cookie.length > 0,
'cookie has correct value': (r) => cookies.my_cookie[0] === 'hello world',
});
}
jar 的 cookiesForURL() 方法返回的 cookies 对象是一个映射,其中键是 cookie 名称,值是 cookie 值(字符串)的数组。它是一个数组,用于支持具有相同名称(但不同 domain 和/或 path 属性)的多个 cookie,这是 RFC6265 的一部分。
设置带属性的高级 cookies
要设置更严格控制 cookie 行为的 cookie,我们必须将 cookie 添加到 cookie jar 中。例如
import http from 'k6/http';
import { check } from 'k6';
export default function () {
const jar = http.cookieJar();
jar.set('https://quickpizza.grafana.com/api/cookies', 'my_cookie', 'hello world', {
domain: 'quickpizza.grafana.com',
path: '/api/cookies',
secure: true,
max_age: 600,
});
const res = http.get('https://quickpizza.grafana.com/api/cookies');
check(res, {
'has status 200': (r) => r.status === 200,
'cookie has correct value': (r) => r.json().cookies.my_cookie == 'hello world',
});
}
本地 cookie jar
除了每个 VU 的 cookie jar 之外,您还可以创建本地 cookie jar,以在每个请求的基础上覆盖每个 VU 的 cookie jar
import http from 'k6/http';
import { check } from 'k6';
export default function () {
const jar = new http.CookieJar();
// Add cookie to local jar
const cookieOptions = {
domain: 'quickpizza.grafana.com',
path: '/api/cookies',
secure: true,
max_age: 600,
};
jar.set('https://quickpizza.grafana.com/api/cookies', 'my_cookie', 'hello world', cookieOptions);
// Override per-VU jar with local jar for the following request
const res = http.get('https://quickpizza.grafana.com/api/cookies', { jar });
check(res, {
'has status 200': (r) => r.status === 200,
'cookie has correct value': (r) => r.json().cookies.my_cookie == 'hello world',
});
}
示例
// Example showing two methods how to log all cookies (with attributes) from a HTTP response.
import http from 'k6/http';
function logCookie(c) {
// Here we log the name and value of the cookie along with additional attributes.
// For full list of attributes see:
// https://grafana.org.cn/docs/k6/using-k6/cookies#properties-of-a-response-cookie-object
const output = `
${c.name}: ${c.value}
tdomain: ${c.domain}
tpath: ${c.path}
texpires: ${c.expires}
thttpOnly: ${c.http_only}
`;
console.log(output);
}
export default function () {
const res = http.get('https://www.google.com/');
// Method 1: Use for-loop and check for non-inherited properties
for (const name in res.cookies) {
if (res.cookies.hasOwnProperty(name) !== undefined) {
logCookie(res.cookies[name][0]);
}
}
// Method 2: Use ES6 Map to loop over Object entries
new Map(Object.entries(res.cookies)).forEach((v, k) => {
logCookie(v[0]);
});
}