<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Protocols on Grafana Labs</title><link>https://grafana.com/docs/k6/v2.3.x/using-k6/protocols/</link><description>Recent content in Protocols on Grafana Labs</description><generator>Hugo -- gohugo.io</generator><language>en</language><atom:link href="/docs/k6/v2.3.x/using-k6/protocols/index.xml" rel="self" type="application/rss+xml"/><item><title>HTTP/2</title><link>https://grafana.com/docs/k6/v2.3.x/using-k6/protocols/http-2/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/using-k6/protocols/http-2/</guid><content><![CDATA[&lt;h1 id=&#34;http2&#34;&gt;HTTP/2&lt;/h1&gt;
&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;
&lt;p&gt;HTTP/2.0 is the latest version of the HTTP protocol.
It improves significantly upon HTTP/1.
Most importantly, it introduces a binary wire protocol with multiplexed streams over a single TCP connection.
This solves a long-standing performance issue with HTTP/1.1: &lt;a href=&#34;https://en.wikipedia.org/wiki/Head-of-line_blocking&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;head-of-line blocking&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Well, it at least &lt;em&gt;partially&lt;/em&gt; solves it.
The TCP congestion control mechanisms can interfere with the intended independent nature of the multiplexed streams in cases of lost/dropped packets and retransmission/reassembly.
The full solution is to run HTTP/2.0 over UDP, as Google implemented with &lt;a href=&#34;https://en.wikipedia.org/wiki/QUIC&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;QUIC&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;additional-features-of-http20&#34;&gt;Additional features of HTTP/2.0&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Builtin compression of HTTP headers&lt;/li&gt;
&lt;li&gt;Server push&lt;/li&gt;
&lt;li&gt;Pipelining of requests&lt;/li&gt;
&lt;li&gt;Prioritization of requests&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;load-testing-http2-with-k6&#34;&gt;Load testing HTTP/2 with k6&lt;/h2&gt;
&lt;p&gt;When you make HTTP requests in k6, k6 automatically upgrades the connection to HTTP/2.0 if the server supports it, just like your web browser would.&lt;/p&gt;
&lt;p&gt;To check what protocol was used for a particular request, refer to the &lt;code&gt;proto&lt;/code&gt; property of the response object.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import http from &amp;#39;k6/http&amp;#39;;
import { check, sleep } from &amp;#39;k6&amp;#39;;

export default function () {
  const res = http.get(&amp;#39;https://quickpizza.grafana.com&amp;#39;);
  check(res, {
    &amp;#39;protocol is HTTP/2&amp;#39;: (r) =&amp;gt; r.proto === &amp;#39;HTTP/2.0&amp;#39;,
  });
  sleep(1);
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To see the values that the &lt;code&gt;r.proto&lt;/code&gt; field can have, refer to the documentation for 
    &lt;a href=&#34;/docs/k6/v2.3.x/javascript-api/k6-http/response/&#34;&gt;k6 HTTP&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="http2">HTTP/2&lt;/h1>
&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>HTTP/2.0 is the latest version of the HTTP protocol.
It improves significantly upon HTTP/1.
Most importantly, it introduces a binary wire protocol with multiplexed streams over a single TCP connection.
This solves a long-standing performance issue with HTTP/1.1: &lt;a href="https://en.wikipedia.org/wiki/Head-of-line_blocking" target="_blank" rel="noopener noreferrer">head-of-line blocking&lt;/a>.&lt;/p></description></item><item><title>WebSockets</title><link>https://grafana.com/docs/k6/v2.3.x/using-k6/protocols/websockets/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/using-k6/protocols/websockets/</guid><content><![CDATA[&lt;h1 id=&#34;websockets&#34;&gt;WebSockets&lt;/h1&gt;
&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/WebSocket&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;WebSocket&lt;/a&gt; is a protocol that provides full-duplex communication channels over a single TCP connection.
It is commonly used by single-page apps (SPAs) and mobile apps, to add server-push based functionality, which usually improves performance in polling-based solutions.&lt;/p&gt;
&lt;h2 id=&#34;load-testing-websockets-with-k6&#34;&gt;Load testing WebSockets with k6&lt;/h2&gt;


&lt;div class=&#34;admonition admonition-note&#34;&gt;&lt;blockquote&gt;&lt;p class=&#34;title text-uppercase&#34;&gt;Note&lt;/p&gt;&lt;p&gt;The 
    &lt;a href=&#34;/docs/k6/v2.3.x/javascript-api/k6-websockets/&#34;&gt;&lt;code&gt;k6/websockets&lt;/code&gt;&lt;/a&gt; module provides a more standard API than &lt;code&gt;k6/ws&lt;/code&gt;. It implements &lt;a href=&#34;https://websockets.spec.whatwg.org/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;the WebSockets API living standard&lt;/a&gt; and uses a global event loop instead of a local one for better performance.&lt;/p&gt;
&lt;p&gt;We recommend using &lt;code&gt;k6/websockets&lt;/code&gt; for new tests. The &lt;code&gt;k6/experimental/websockets&lt;/code&gt; module has been deprecated.&lt;/p&gt;&lt;/blockquote&gt;&lt;/div&gt;

&lt;p&gt;Comparing HTTP-based tests to WebSocket ones, you&amp;rsquo;ll find differences in both structure and inner workings.
The primary difference is that instead of continuously looping the main function (&lt;code&gt;export default function () { ... }&lt;/code&gt;) over and over, each VU is now runs an asynchronous event loop.&lt;/p&gt;
&lt;p&gt;The basic structure of a WebSocket test looks like this:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import ws from &amp;#39;k6/ws&amp;#39;;
import { check } from &amp;#39;k6&amp;#39;;

export default function () {
  const url = &amp;#39;wss://echo.websocket.org&amp;#39;;
  const params = { tags: { my_tag: &amp;#39;hello&amp;#39; } };

  const res = ws.connect(url, params, function (socket) {
    socket.on(&amp;#39;open&amp;#39;, () =&amp;gt; console.log(&amp;#39;connected&amp;#39;));
    socket.on(&amp;#39;message&amp;#39;, (data) =&amp;gt; console.log(&amp;#39;Message received: &amp;#39;, data));
    socket.on(&amp;#39;close&amp;#39;, () =&amp;gt; console.log(&amp;#39;disconnected&amp;#39;));
  });

  check(res, { &amp;#39;status is 101&amp;#39;: (r) =&amp;gt; r &amp;amp;&amp;amp; r.status === 101 });
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In this example, the 
    &lt;a href=&#34;/docs/k6/v2.3.x/javascript-api/k6-ws/connect/&#34;&gt;connect()&lt;/a&gt; method takes a &amp;ldquo;run&amp;rdquo; function as its third parameter.
That function should accept a 
    &lt;a href=&#34;/docs/k6/v2.3.x/javascript-api/k6-ws/socket/&#34;&gt;Socket&lt;/a&gt; object as its only parameter.
The run function forms the basis of the asynchronous event loop.&lt;/p&gt;
&lt;p&gt;When the WebSocket connection is created, the run function will be immediately called, all code inside it will be executed (usually code to set up event handlers), and then blocked until the WebSocket connection is closed (by the remote host or by using 
    &lt;a href=&#34;/docs/k6/v2.3.x/javascript-api/k6-ws/socket/socket-close/&#34;&gt;socket.close()&lt;/a&gt;).&lt;/p&gt;
&lt;h2 id=&#34;error-handling&#34;&gt;Error handling&lt;/h2&gt;
&lt;p&gt;To catch errors happen during the life of a WebSocket connection, attach a handler to the &amp;ldquo;error&amp;rdquo; event:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import ws from &amp;#39;k6/ws&amp;#39;;
import { check } from &amp;#39;k6&amp;#39;;

export default function () {
  const url = &amp;#39;wss://echo.websocket.org&amp;#39;;
  const params = { tags: { my_tag: &amp;#39;hello&amp;#39; } };

  const res = ws.connect(url, params, function (socket) {
    socket.on(&amp;#39;open&amp;#39;, function open() {
      // ...
    });

    socket.on(&amp;#39;error&amp;#39;, function (e) {
      if (e.error() != &amp;#39;websocket: close sent&amp;#39;) {
        console.log(&amp;#39;An unexpected error occured: &amp;#39;, e.error());
      }
    });
  });

  check(res, { &amp;#39;status is 101&amp;#39;: (r) =&amp;gt; r &amp;amp;&amp;amp; r.status === 101 });
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&#34;timers&#34;&gt;Timers&lt;/h2&gt;
&lt;p&gt;To schedule a recurring action, use the 
    &lt;a href=&#34;/docs/k6/v2.3.x/javascript-api/k6-ws/socket/#section-socketsetinterval&#34;&gt;socket.setInterval&lt;/a&gt; to specify a function to call at a particular interval.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import ws from &amp;#39;k6/ws&amp;#39;;
import { check } from &amp;#39;k6&amp;#39;;

export default function () {
  const url = &amp;#39;wss://echo.websocket.org&amp;#39;;
  const params = { tags: { my_tag: &amp;#39;hello&amp;#39; } };

  const res = ws.connect(url, params, function (socket) {
    socket.on(&amp;#39;open&amp;#39;, function open() {
      console.log(&amp;#39;connected&amp;#39;);

      socket.setInterval(function timeout() {
        socket.ping();
        console.log(&amp;#39;Pinging every 1sec (setInterval test)&amp;#39;);
      }, 1000);
    });

    socket.on(&amp;#39;ping&amp;#39;, () =&amp;gt; console.log(&amp;#39;PING!&amp;#39;));
    socket.on(&amp;#39;pong&amp;#39;, () =&amp;gt; console.log(&amp;#39;PONG!&amp;#39;));
    socket.on(&amp;#39;close&amp;#39;, () =&amp;gt; console.log(&amp;#39;disconnected&amp;#39;));
  });

  check(res, { &amp;#39;status is 101&amp;#39;: (r) =&amp;gt; r &amp;amp;&amp;amp; r.status === 101 });
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&#34;timeouts&#34;&gt;Timeouts&lt;/h2&gt;
&lt;p&gt;To add a timeout to the WebSocket connection, pass both a handler function and a timeout value (in milliseconds) to the 
    &lt;a href=&#34;/docs/k6/v2.3.x/javascript-api/k6-ws/socket/socket-settimeout/&#34;&gt;socket.setTimeout&lt;/a&gt; function.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import ws from &amp;#39;k6/ws&amp;#39;;
import { check } from &amp;#39;k6&amp;#39;;

export default function () {
  const url = &amp;#39;wss://echo.websocket.org&amp;#39;;
  const params = { tags: { my_tag: &amp;#39;hello&amp;#39; } };

  const res = ws.connect(url, params, function (socket) {
    socket.on(&amp;#39;open&amp;#39;, () =&amp;gt; console.log(&amp;#39;connected&amp;#39;));
    socket.on(&amp;#39;close&amp;#39;, () =&amp;gt; console.log(&amp;#39;disconnected&amp;#39;));

    socket.setTimeout(function () {
      console.log(&amp;#39;2 seconds passed, closing the socket&amp;#39;);
      socket.close();
    }, 2000);
  });

  check(res, { &amp;#39;status is 101&amp;#39;: (r) =&amp;gt; r &amp;amp;&amp;amp; r.status === 101 });
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In the preceding example, the timeout will close the WebSocket connection after 2 seconds.&lt;/p&gt;
&lt;h2 id=&#34;multiple-event-handlers&#34;&gt;Multiple event handlers&lt;/h2&gt;
&lt;p&gt;You can attach multiple handler functions to an event:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import ws from &amp;#39;k6/ws&amp;#39;;
import { check } from &amp;#39;k6&amp;#39;;

export default function () {
  const url = &amp;#39;wss://echo.websocket.org&amp;#39;;
  const params = { tags: { my_tag: &amp;#39;hello&amp;#39; } };

  const response = ws.connect(url, params, function (socket) {
    socket.on(&amp;#39;open&amp;#39;, function open() {
      console.log(&amp;#39;connected&amp;#39;);
      socket.send(Date.now());

      socket.setInterval(function timeout() {
        socket.ping();
        console.log(&amp;#39;Pinging every 1sec (setInterval test)&amp;#39;);
      }, 1000);
    });

    socket.on(&amp;#39;ping&amp;#39;, () =&amp;gt; console.log(&amp;#39;PING!&amp;#39;));
    socket.on(&amp;#39;pong&amp;#39;, () =&amp;gt; console.log(&amp;#39;PONG!&amp;#39;));
    socket.on(&amp;#39;pong&amp;#39;, () =&amp;gt; {
      // Multiple event handlers on the same event
      console.log(&amp;#39;OTHER PONG!&amp;#39;);
    });

    socket.on(&amp;#39;close&amp;#39;, () =&amp;gt; console.log(&amp;#39;disconnected&amp;#39;));

    socket.on(&amp;#39;error&amp;#39;, (e) =&amp;gt; {
      if (e.error() != &amp;#39;websocket: close sent&amp;#39;) {
        console.log(&amp;#39;An unexpected error occurred: &amp;#39;, e.error());
      }
    });

    socket.setTimeout(function () {
      console.log(&amp;#39;2 seconds passed, closing the socket&amp;#39;);
      socket.close();
    }, 2000);
  });

  check(response, { &amp;#39;status is 101&amp;#39;: (r) =&amp;gt; r &amp;amp;&amp;amp; r.status === 101 });
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
]]></content><description>&lt;h1 id="websockets">WebSockets&lt;/h1>
&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>&lt;a href="https://en.wikipedia.org/wiki/WebSocket" target="_blank" rel="noopener noreferrer">WebSocket&lt;/a> is a protocol that provides full-duplex communication channels over a single TCP connection.
It is commonly used by single-page apps (SPAs) and mobile apps, to add server-push based functionality, which usually improves performance in polling-based solutions.&lt;/p></description></item><item><title>gRPC</title><link>https://grafana.com/docs/k6/v2.3.x/using-k6/protocols/grpc/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/using-k6/protocols/grpc/</guid><content><![CDATA[&lt;!-- md-k6:skipall --&gt;
&lt;h1 id=&#34;grpc&#34;&gt;gRPC&lt;/h1&gt;
&lt;h2 id=&#34;overview&#34;&gt;Overview&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://grpc.io/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;gRPC&lt;/a&gt; is a lightweight, open-source RPC framework.
It was originally developed by Google, with version 1.0 released in August 2016.
Since then, it&amp;rsquo;s gained much attention and wide adoption.&lt;/p&gt;
&lt;p&gt;Whereas JSON transmits as human-readable text, gRPC is binary.
The binary format makes data transfer faster and more compact.
In the benchmarks we&amp;rsquo;ve seen, gRPC has proved much faster than REST, gRPC&amp;rsquo;s more traditional, JSON-based counterpart.
The messages and services used for gRPC are described in &lt;code&gt;.proto&lt;/code&gt; files, containing definitions for &lt;a href=&#34;https://en.wikipedia.org/wiki/Protocol_Buffers&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Protocol Buffers&lt;/a&gt; (protobuf).&lt;/p&gt;
&lt;h2 id=&#34;load-testing-grpc-services-with-k6&#34;&gt;Load testing gRPC services with k6&lt;/h2&gt;
&lt;p&gt;Starting on k6 v0.49.0, k6 supports unary gRPC requests and streaming as part of the &lt;code&gt;k6/net/grpc&lt;/code&gt; core module.&lt;/p&gt;
&lt;h3 id=&#34;grpc-definitions&#34;&gt;gRPC definitions&lt;/h3&gt;
&lt;p&gt;Before interacting with a gRPC service, k6 needs to learn the definitions of the messages and services.&lt;/p&gt;
&lt;p&gt;One way to do that is to explicitly use the &lt;code&gt;Client.load()&lt;/code&gt; method and load the client definitions from the local file system. The method accepts a list of import paths and a list of &lt;code&gt;.proto&lt;/code&gt; files. k6 then loads all the definitions from the files and their dependencies.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { Client } from &amp;#39;k6/net/grpc&amp;#39;;

const client = new Client();
client.load([&amp;#39;definitions&amp;#39;], &amp;#39;hello.proto&amp;#39;);&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Alternatively, you can dynamically load the definitions by using the gRPC reflection protocol. To enable reflection, you can pass the &lt;code&gt;reflect: true&lt;/code&gt; option to &lt;code&gt;Client.connect()&lt;/code&gt;. k6 then loads all the definitions from the server and their dependencies.&lt;/p&gt;
&lt;p&gt;This option is only possible if the server has been instrumented with reflection support.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { Client } from &amp;#39;k6/net/grpc&amp;#39;;

const client = new Client();
client.connect(&amp;#39;127.0.0.1:10000&amp;#39;, { reflect: true });&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;unary-grpc-requests&#34;&gt;Unary gRPC requests&lt;/h3&gt;
&lt;p&gt;Unary calls work the same way as regular HTTP requests. A single request is sent to a server, and the server replies with a single response.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { Client, StatusOK } from &amp;#39;k6/net/grpc&amp;#39;;
import { check, sleep } from &amp;#39;k6&amp;#39;;

const client = new Client();
client.load([&amp;#39;definitions&amp;#39;], &amp;#39;hello.proto&amp;#39;);

export default () =&amp;gt; {
  client.connect(&amp;#39;127.0.0.1:10000&amp;#39;, {});

  const data = { greeting: &amp;#39;Bert&amp;#39; };
  const response = client.invoke(&amp;#39;hello.HelloService/SayHello&amp;#39;, data);

  check(response, {
    &amp;#39;status is OK&amp;#39;: (r) =&amp;gt; r &amp;amp;&amp;amp; r.status === StatusOK,
  });

  console.log(JSON.stringify(response.message));

  client.close();
  sleep(1);
};&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;streaming-requires-async-functions&#34;&gt;Streaming requires async functions&lt;/h3&gt;
&lt;p&gt;gRPC streaming relies on event handlers (&lt;code&gt;stream.on&lt;/code&gt;) to process messages.
In JavaScript, event handlers can only run when the call stack is empty.
If your default function is synchronous, event handlers can only fire after it returns.&lt;/p&gt;
&lt;p&gt;To let event handlers run during test execution, declare the default function as &lt;code&gt;async&lt;/code&gt; and use a &lt;code&gt;Promise&lt;/code&gt; to wait for the stream to complete.
All of the streaming examples on this page follow this pattern.&lt;/p&gt;
&lt;h3 id=&#34;server-grpc-streaming&#34;&gt;Server gRPC streaming&lt;/h3&gt;
&lt;p&gt;In server streaming mode, the client sends a single request to the server, and the server replies with multiple responses.&lt;/p&gt;
&lt;p&gt;The following example demonstrates server streaming:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { Client, Stream } from &amp;#39;k6/net/grpc&amp;#39;;

const COORD_FACTOR = 1e7;

const client = new Client();

export default async function () {
  if (__ITER == 0) {
    client.connect(&amp;#39;127.0.0.1:10000&amp;#39;, { plaintext: true, reflect: true });
  }

  const stream = new Stream(client, &amp;#39;main.FeatureExplorer/ListFeatures&amp;#39;, null);

  await new Promise((resolve, reject) =&amp;gt; {
    stream.on(&amp;#39;data&amp;#39;, function (feature) {
      console.log(
        `Found feature called &amp;#34;${feature.name}&amp;#34; at ${feature.location.latitude / COORD_FACTOR}, ${
          feature.location.longitude / COORD_FACTOR
        }`
      );
    });

    stream.on(&amp;#39;error&amp;#39;, function (e) {
      reject(e);
    });

    stream.on(&amp;#39;end&amp;#39;, function () {
      client.close();
      console.log(&amp;#39;All done&amp;#39;);
      resolve();
    });

    stream.write({
      lo: {
        latitude: 400000000,
        longitude: -750000000,
      },
      hi: {
        latitude: 420000000,
        longitude: -730000000,
      },
    });
  });
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In this example, k6 connects to a gRPC server, creates a stream, and sends a message with latitude and longitude coordinates.
The &lt;code&gt;async&lt;/code&gt; default function wraps the stream logic in a &lt;code&gt;Promise&lt;/code&gt;, which lets event handlers fire while the function awaits.
When the server finishes sending data, the &lt;code&gt;end&lt;/code&gt; handler closes the client and resolves the promise.&lt;/p&gt;
&lt;h3 id=&#34;client-grpc-streaming&#34;&gt;Client gRPC streaming&lt;/h3&gt;
&lt;p&gt;The client streaming mode is the opposite of the server streaming mode. The client sends multiple requests to the server, and the server replies with a single response.&lt;/p&gt;
&lt;p&gt;The following example demonstrates client streaming:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { Client, Stream } from &amp;#39;k6/net/grpc&amp;#39;;

const COORD_FACTOR = 1e7;
const client = new Client();

const points = [
  {
    location: { latitude: 407838351, longitude: -746143763 },
    name: &amp;#39;Patriots Path, Mendham, NJ 07945, USA&amp;#39;,
  },
  {
    location: { latitude: 408122808, longitude: -743999179 },
    name: &amp;#39;101 New Jersey 10, Whippany, NJ 07981, USA&amp;#39;,
  },
  {
    location: { latitude: 413628156, longitude: -749015468 },
    name: &amp;#39;U.S. 6, Shohola, PA 18458, USA&amp;#39;,
  },
  {
    location: { latitude: 419999544, longitude: -740371136 },
    name: &amp;#39;5 Conners Road, Kingston, NY 12401, USA&amp;#39;,
  },
  {
    location: { latitude: 414008389, longitude: -743951297 },
    name: &amp;#39;Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA&amp;#39;,
  },
];

export default async function () {
  if (__ITER == 0) {
    client.connect(&amp;#39;127.0.0.1:10000&amp;#39;, { plaintext: true, reflect: true });
  }

  const stream = new Stream(client, &amp;#39;main.RouteGuide/RecordRoute&amp;#39;);

  await new Promise((resolve, reject) =&amp;gt; {
    stream.on(&amp;#39;data&amp;#39;, (stats) =&amp;gt; {
      console.log(`Finished trip with ${stats.pointCount} points`);
      console.log(`Passed ${stats.featureCount} features`);
      console.log(`Travelled ${stats.distance} meters`);
      console.log(`It took ${stats.elapsedTime} seconds`);
    });

    stream.on(&amp;#39;error&amp;#39;, (e) =&amp;gt; {
      reject(e);
    });

    stream.on(&amp;#39;end&amp;#39;, () =&amp;gt; {
      client.close();
      console.log(&amp;#39;All done&amp;#39;);
      resolve();
    });

    for (let i = 0; i &amp;lt; 3; i&amp;#43;&amp;#43;) {
      const point = points[Math.floor(Math.random() * points.length)];
      console.log(
        `Visiting point ${point.name} ${point.location.latitude / COORD_FACTOR}, ${
          point.location.longitude / COORD_FACTOR
        }`
      );
      stream.write(point.location);
    }

    stream.end();
  });
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In this example, k6 connects to a gRPC server, creates a stream, and sends three random points.
The code wraps the stream logic in a &lt;code&gt;Promise&lt;/code&gt;, the same pattern as the server streaming example.
The server responds with trip statistics, and the &lt;code&gt;end&lt;/code&gt; handler closes the client and resolves the promise.&lt;/p&gt;
&lt;h3 id=&#34;bidirectional-grpc-streaming&#34;&gt;Bidirectional gRPC streaming&lt;/h3&gt;
&lt;p&gt;In bi-directional streaming mode, the client and the server may send multiple messages.&lt;/p&gt;
&lt;p&gt;From the API perspective, it combines the client and server streaming modes, so the code is similar to the examples above.&lt;/p&gt;
&lt;h3 id=&#34;streaming-error-handling&#34;&gt;Streaming error handling&lt;/h3&gt;
&lt;p&gt;To catch errors that occur during streaming, you can use the &lt;code&gt;error&lt;/code&gt; event handler.&lt;/p&gt;
&lt;p&gt;The handler receives 
    &lt;a href=&#34;/docs/k6/v2.3.x/javascript-api/k6-net-grpc/stream/stream-error/&#34;&gt;an error object&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { Client, Stream } from &amp;#39;k6/net/grpc&amp;#39;;

const client = new Client();
const stream = new Stream(client, &amp;#39;main.RouteGuide/RecordRoute&amp;#39;);

stream.on(&amp;#39;error&amp;#39;, function (e) {
  // An error has occurred and the stream has been closed.
  console.log(&amp;#39;Error: &amp;#39; &amp;#43; JSON.stringify(e));
});&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;protocol-buffers-json-mapping&#34;&gt;Protocol Buffers JSON Mapping&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s important to note how k6 handles requests and messages. First, it tries to marshal the request/message in JSON format. Then, k6 uses the &lt;a href=&#34;https://pkg.go.dev/google.golang.org/protobuf/encoding/protojson&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;protojson&lt;/a&gt; package to encode or decode to a Protobuf message.&lt;/p&gt;
&lt;p&gt;A limitation during this process is that the object you pass as a request/message must be serializable. That means structs like &lt;code&gt;Map&lt;/code&gt; don&amp;rsquo;t work.&lt;/p&gt;
&lt;p&gt;The benefit of using &lt;code&gt;protojson&lt;/code&gt; is the canonical JSON encoding support. The &lt;a href=&#34;https://protobuf.dev/programming-guides/proto3/#json&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Protocol Buffers documentation&lt;/a&gt; describes this mapping.
Also note that according to &lt;a href=&#34;https://protobuf.dev/programming-guides/json/&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;ProtoJSON docs&lt;/a&gt;, the JSON value of a non-numeric float (e.g. &lt;code&gt;NaN&lt;/code&gt; or &lt;code&gt;Infinity&lt;/code&gt;) will be their equivalent string representation.&lt;/p&gt;
&lt;h4 id=&#34;examples&#34;&gt;Examples&lt;/h4&gt;
&lt;p&gt;For instance, if you import &lt;code&gt;&amp;quot;google/protobuf/wrappers.proto&amp;quot;&lt;/code&gt; and your proto-definitions look like this:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;proto&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-proto&#34;&gt;syntax = &amp;#34;proto3&amp;#34;;

package testing;

import &amp;#34;google/protobuf/wrappers.proto&amp;#34;;

service Service {
   rpc SayHey(google.protobuf.StringValue) returns (google.protobuf.StringValue);
   rpc DoubleInteger(google.protobuf.Int64Value) returns (google.protobuf.Int64Value);
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;When passing a message, you should use a string or an integer, not an object. As a result, you will receive a type that has already been marshaled.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { Client } from &amp;#39;k6/net/grpc&amp;#39;;
const client = new Client();

// an example of passing a string
const respString = client.invoke(&amp;#39;testing.Service/SayHey&amp;#39;, &amp;#39;John&amp;#39;);
if (respString.message !== &amp;#39;hey John&amp;#39;) {
  throw new Error(&amp;#34;expected to get &amp;#39;hey John&amp;#39;, but got a &amp;#34; &amp;#43; respString.message);
}

// an example of passing an integer
const respInt = client.invoke(&amp;#39;testing.Service/DoubleInteger&amp;#39;, &amp;#39;3&amp;#39;);
if (respInt.message !== &amp;#39;6&amp;#39;) {
  throw new Error(&amp;#34;expected to get &amp;#39;6&amp;#39;, but got a &amp;#34; &amp;#43; respInt.message);
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Another example could be usage of &lt;code&gt;oneof&lt;/code&gt;. Let&amp;rsquo;s say you have a proto-definition like this:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;proto&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-proto&#34;&gt;syntax = &amp;#34;proto3&amp;#34;;

package testing;

service Service {
   rpc Test(Foo)  returns (Foo) {}
}

message Foo {
   oneof Bar {
     string code = 1;
     uint32 id = 2;
   }
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In this case, you should pass an object either with &lt;code&gt;code&lt;/code&gt; or &lt;code&gt;id&lt;/code&gt; fields.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import { Client } from &amp;#39;k6/net/grpc&amp;#39;;
const client = new Client();

// calling RPC with filled code field
const respWithCode = client.invoke(&amp;#39;testing.Service/Test&amp;#39;, { code: &amp;#39;abc-123&amp;#39; });

// calling RPC with filled id field
const respWithID = client.invoke(&amp;#39;testing.Service/Test&amp;#39;, { id: 123 });&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h2 id=&#34;health-checking-protocol&#34;&gt;Health Checking protocol&lt;/h2&gt;
&lt;p&gt;There is a dedicated support for the &lt;a href=&#34;https://grpc.io/docs/guides/health-checking&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Health Checking&lt;/a&gt; protocol, which provides a standard way to check if a gRPC service is healthy and ready to serve requests. It eliminates the need to use the generic &lt;code&gt;invoke&lt;/code&gt; method for health checks.&lt;/p&gt;
&lt;p&gt;The 
    &lt;a href=&#34;/docs/k6/v2.3.x/javascript-api/k6-net-grpc/client/client-health-check/&#34;&gt;Client.healthCheck()&lt;/a&gt; method implements this protocol and returns the health status of the specified service. If no service name is provided, it checks the overall server health.&lt;/p&gt;
]]></content><description>&lt;!-- md-k6:skipall -->
&lt;h1 id="grpc">gRPC&lt;/h1>
&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>&lt;a href="https://grpc.io/" target="_blank" rel="noopener noreferrer">gRPC&lt;/a> is a lightweight, open-source RPC framework.
It was originally developed by Google, with version 1.0 released in August 2016.
Since then, it&amp;rsquo;s gained much attention and wide adoption.&lt;/p></description></item><item><title>SSL/TLS</title><link>https://grafana.com/docs/k6/v2.3.x/using-k6/protocols/ssl-tls/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/using-k6/protocols/ssl-tls/</guid><content><![CDATA[&lt;h1 id=&#34;ssltls&#34;&gt;SSL/TLS&lt;/h1&gt;
&lt;p&gt;Transport Layer Security (TLS), the successor of Secure Socket Layer (SSL), is the mechanism through
which encrypted connections can be established between clients and servers on the web and through
which data can flow with integrity intact.&lt;/p&gt;
&lt;p&gt;Simplified, TLS is made possible by using a Public Key Infrastructure (PKI) involving private
cryptographic keys, certificates of ownership (public, signed with private key), and certificate
authorities (CAs, issuing and asserting the validity of issued certificates).&lt;/p&gt;
&lt;p&gt;The entire system depends on the functioning operation of the CAs.
The system trusts that the cryptographic keys that CAs use to sign certificates of ownership to site/domain owners are kept secret.
It also depends on the domain owners&amp;rsquo; ability to keep their private cryptographic keys secret.&lt;/p&gt;
&lt;p&gt;If they fail to do so, they have to report the leak to the CA that issued the certificate.
After this disclosure, the CA can revoke the certificate and, in doing so, let browsers and other clients know of the changes through
the Online Certificate Status Protocol (OCSP).&lt;/p&gt;
&lt;h2 id=&#34;tls-in-k6&#34;&gt;TLS in k6&lt;/h2&gt;
&lt;p&gt;By default and without any special configuration, k6 connects and talks to servers over TLS.
You just need to make sure that you specify your request URLs with the &lt;code&gt;https&lt;/code&gt; scheme.&lt;/p&gt;
&lt;p&gt;K6 supports the following TLS functionalities.
Each is worth discussing in more detail:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
    &lt;a href=&#34;/docs/k6/v2.3.x/using-k6/protocols/ssl-tls/ssl-tls-client-certificates/&#34;&gt;TLS client certificates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
    &lt;a href=&#34;/docs/k6/v2.3.x/using-k6/protocols/ssl-tls/ssl-tls-version-and-ciphers/&#34;&gt;TLS version and ciphers&lt;/a&gt; (restricting as
well as checking what was used for a HTTP request by checking response object properties)&lt;/li&gt;
&lt;li&gt;
    &lt;a href=&#34;/docs/k6/v2.3.x/using-k6/protocols/ssl-tls/online-certificate-status-protocol--ocsp/&#34;&gt;Online Certificate Status Protocol (OCSP)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
    &lt;a href=&#34;/docs/k6/v2.3.x/using-k6/k6-options/reference/#tls-aia-fetch&#34;&gt;AIA fetching for missing intermediates&lt;/a&gt; (opt-in via &lt;code&gt;tlsAIAFetch&lt;/code&gt;, for servers that rely on Authority Information Access rather than bundling the full chain)&lt;/li&gt;
&lt;li&gt;
    &lt;a href=&#34;/docs/k6/v2.3.x/javascript-api/k6-http/response/&#34;&gt;Response.timings.tls_handshaking&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
]]></content><description>&lt;h1 id="ssltls">SSL/TLS&lt;/h1>
&lt;p>Transport Layer Security (TLS), the successor of Secure Socket Layer (SSL), is the mechanism through
which encrypted connections can be established between clients and servers on the web and through
which data can flow with integrity intact.&lt;/p></description></item></channel></rss>