菜单
开源

Cookies

HTTP Cookie 用于网站和应用在用户设备上存储一些状态信息。通过 Set-Cookie HTTP 头部,服务器告诉客户端希望在用户机器上存储哪些信息。

用户浏览器存储 cookie 数据,并将其与服务器的主机名关联。每次对该主机名进行后续请求时,浏览器都会在 Cookie 头部中包含存储的 cookie 数据。

然后,您可以控制更具体的规则,决定是否发送 cookie 数据,包括将其限制在特定的子域名或路径。您还可以设置 cookie 的过期日期,并告诉浏览器仅通过加密 (SSL/TLS) 连接发送它。

在 k6 中使用 Cookies

在大多数情况下,k6 会透明地管理 cookie 的接收、存储和传输,如描述所示。对您基于 cookie 的网站或应用进行测试将会*正常工作*,而无需您进行任何特殊操作。

然而,在某些情况下,您可能希望对 cookie 有更多控制。k6 提供了多种选项来实现此目的。您可以:

以下部分介绍如何使用 Cookie API。

设置简单的 cookies

为了模拟 cookie 之前已被浏览器设置,并且现在应该包含在对服务器的后续请求中,请将 cookie 包含在 cookies 请求参数中

JavaScript
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

JavaScript
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

JavaScript
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 属性

JavaScript
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 对象包含以下属性

属性类型描述
namestringcookie 的名称
valuestringcookie 的值
domainstring决定此 cookie 应发送到哪些主机名的 domain
pathstring限制 cookie 仅在请求路径与此值匹配时发送
expiresstringcookie 何时过期,这需要采用 RFC1123 格式,例如:Mon, 02 Jan 2006 15:04:05 MST
max_agenumber用于与 expires 相同目的,但定义为 cookie 有效的秒数
secureboolean如果为 true,cookie 将仅通过加密 (SSL/TLS) 连接发送
http_onlyboolean如果为 true,cookie 在浏览器环境中不会暴露给 JavaScript

要查看特定 URL 的 cookie jar 中设置和存储了哪些 cookie,请使用 cookie jar 对象的 cookieForURL() 方法

JavaScript
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 中。例如

JavaScript
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',
  });
}

除了每个 VU 的 cookie jar 之外,您还可以创建本地 cookie jar,以在每个请求的基础上覆盖每个 VU 的 cookie jar

JavaScript
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',
  });
}

示例

JavaScript
// 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]);
  });
}