Skip to content

browser: add proxy option to browser contexts - #5924

Merged
szkiba merged 3 commits into
grafana:masterfrom
nightt5879:nightt5879/fix-browser-context-proxy
Jun 10, 2026
Merged

szkiba merged 3 commits into
grafana:masterfrom
nightt5879:nightt5879/fix-browser-context-proxy

Conversation

@nightt5879

Copy link
Copy Markdown
Contributor

What?

Adds a proxy option to browser context options so callers can configure Chromium context-level proxy settings with:

const context = await browser.newContext({
  proxy: {
    server: 'http://proxy.test:8080',
    bypass: 'localhost,127.0.0.1',
  },
});

The option is wired to CDP Target.createBrowserContext through proxyServer and proxyBypassList, and invalid proxy options now fail early when proxy.server is missing.

Why?

Browser traffic currently cannot be routed through a proxy via the browser API. Environment proxy variables only affect k6's DevTools WebSocket connection, and global Chromium launch args are not equivalent to a browser context option.

This makes proxy configuration explicit in browser context options and keeps the behavior scoped to the created browser context.

Checklist

  • I have performed a self-review of my code.
  • I have commented on my code, particularly in hard-to-understand areas.
  • I have added tests for my changes.
  • I have run linter and tests locally (make check) and all pass.

Targeted tests run locally:

go test ./internal/js/modules/k6/browser/common ./internal/js/modules/k6/browser/browser

Checklist: Documentation (only for k6 maintainers and if relevant)

Please do not merge this PR until the following items are filled out.

  • I have added the correct milestone and labels to the PR.
  • I have updated the release notes: link
  • I have updated or added an issue to the k6-documentation: grafana/k6-docs#NUMBER if applicable
  • I have updated or added an issue to the TypeScript definitions: grafana/k6-DefinitelyTyped#NUMBER if applicable

Related PR(s)/Issue(s)

Closes #4432

@cla-assistant

cla-assistant Bot commented May 3, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@nightt5879
nightt5879 marked this pull request as ready for review May 12, 2026 03:20
@nightt5879
nightt5879 requested a review from a team as a code owner May 12, 2026 03:20
@nightt5879
nightt5879 requested review from inancgumus and szkiba and removed request for a team May 12, 2026 03:20
@inancgumus
inancgumus requested a review from Copilot May 13, 2026 18:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for configuring a Chromium context-level proxy via the k6 browser API by introducing a proxy option on browser.newContext() / browser.newPage() context options and wiring it to CDP Target.createBrowserContext parameters.

Changes:

  • Introduces proxy configuration (server, bypass) on BrowserContextOptions, including early validation that proxy.server is required when proxy is provided.
  • Wires proxyServer and proxyBypassList into the CDP Target.createBrowserContext call when creating a new browser context.
  • Adds unit tests covering proxy mapping/parsing, CDP parameter wiring, and validation errors.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
internal/js/modules/k6/browser/common/browser.go Validates proxy options and forwards proxy settings to CDP context creation.
internal/js/modules/k6/browser/common/browser_test.go Adds unit tests ensuring proxy fields are sent (and omitted) correctly and invalid proxy config fails early.
internal/js/modules/k6/browser/common/browser_context_options.go Extends context options with ProxyOptions and adds validation for required server.
internal/js/modules/k6/browser/browser/browser_mapping.go Validates parsed proxy options when mapping JS values into Go options.
internal/js/modules/k6/browser/browser/browser_context_options_test.go Adds JS-side parsing tests for proxy and integrates it into the “all options” test.

ðŸ’Ą Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@nightt5879
nightt5879 temporarily deployed to azure-trusted-signing May 13, 2026 18:11 — with GitHub Actions Inactive
@nightt5879
nightt5879 temporarily deployed to azure-trusted-signing May 13, 2026 18:14 — with GitHub Actions Inactive

@inancgumus inancgumus left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution 🙇 Current tests only verify internals. Please remove all current tests and instead add an integration test in the tests/ pkg that runs a fake proxy server to test the behavior of using a proxy server + the data transferred with and without the proxy options set. LMK if you have any questions.


const browserContextID cdp.BrowserContextID = "42"

ctx, cancel := context.WithCancel(context.Background())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use t.Context() in all tests.

@nightt5879

Copy link
Copy Markdown
Contributor Author

Addressed in 38d0792.

Changes:

  • removed the proxy-specific internal unit tests
  • added TestBrowserContextProxy under internal/js/modules/k6/browser/tests
  • the new integration test runs a fake HTTP proxy and verifies both direct and proxied browser context requests, including the request data forwarded through the proxy

Ran:

  • go test ./internal/js/modules/k6/browser/tests -run TestBrowserContextProxy -count=1 -v -timeout 90s
  • go test ./internal/js/modules/k6/browser/common ./internal/js/modules/k6/browser/browser -count=1
  • go test ./internal/js/modules/k6/browser/tests -run 'TestBrowserContextProxy|TestBrowserContextOptions' -count=1 -timeout 120s

Comment thread internal/js/modules/k6/browser/tests/browser_context_proxy_test.go Outdated
}
w.WriteHeader(resp.StatusCode)
_, err = io.Copy(w, resp.Body)
require.NoError(t, err)

@inancgumus inancgumus May 14, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same: require.NoError in handler goroutine.

ThroughProxy bool
}

func TestBrowserContextProxy(t *testing.T) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing validation error test. Old unit tests covered "proxy without server" — now untested. Add a case: browser.newContext({ proxy: { bypass: 'foo' } }) asserting the error.

@nightt5879
nightt5879 temporarily deployed to azure-trusted-signing May 14, 2026 17:21 — with GitHub Actions Inactive
@nightt5879
nightt5879 temporarily deployed to azure-trusted-signing May 14, 2026 17:22 — with GitHub Actions Inactive
@nightt5879

Copy link
Copy Markdown
Contributor Author

Addressed the latest review feedback in b81ec5c. The review threads are resolved now; could you take another look when you have a chance?

This comment was marked as duplicate.

@inancgumus
inancgumus temporarily deployed to azure-trusted-signing June 2, 2026 19:52 — with GitHub Actions Inactive
@inancgumus
inancgumus temporarily deployed to azure-trusted-signing June 2, 2026 19:55 — with GitHub Actions Inactive
@inancgumus

Copy link
Copy Markdown
Contributor

Thanks, @nightt5879. Please check if the test errors are introduced by your changes or not.

@nightt5879

nightt5879 commented Jun 5, 2026 •

Copy link
Copy Markdown
Contributor Author

I checked the failing CI jobs to confirm whether they look related to this PR.

The previous red jobs were the Test workflow test-tip matrix jobs on Ubuntu/ARM/Windows. They were running Go tip:

go version go1.27-devel_d7a37379 Tue Jun 2 11:49:53 2026 -0700

The failures I found were in existing HTTP/TLS/HTTP2-related tests, for example:

  • TestForceHTTP1Feature in go.k6.io/k6/v2/internal/js
  • TestLoad in go.k6.io/k6/v2/internal/loader
  • TestTLS13Support and cookie-related cases in go.k6.io/k6/v2/js/modules/k6/http
  • TestHTTP2ConnectionError, TestHTTP2GoAwayError, TestX509HostnameError, and TestHTTP2StreamError in go.k6.io/k6/v2/lib/netext/httpext

The common error pattern was around TLS/HTTP2 behavior under gotip, e.g. remote error: tls: no application protocol, or different HTTP2 error strings/codes than the tests expected.

This PR only changes the browser context proxy implementation/tests, and in the same failed job the browser packages themselves passed, for example:

ok go.k6.io/k6/v2/internal/js/modules/k6/browser/browser
ok go.k6.io/k6/v2/internal/js/modules/k6/browser/chromium
ok go.k6.io/k6/v2/internal/js/modules/k6/browser/common
ok go.k6.io/k6/v2/internal/js/modules/k6/browser/storage

So from the logs, I don't think those CI failures were introduced by this PR. They look like gotip / Go HTTP-TLS behavior changes affecting existing tests outside the browser proxy changes.

Update after syncing with current master: the PR head is now 004f004ea6206c287cbef04ede2c3268c96630b6. The new GitHub Actions runs for Test, Browser tests, Build, Lint, etc. are currently action_required with no jobs started, so there are no fresh failure logs yet. The checks that did run on the updated head are passing:

  • license/cla
  • semgrep-cloud-platform/scan

Once the Actions workflows are approved/rerun, I can re-check the new CI result.

@nightt5879
nightt5879 temporarily deployed to azure-trusted-signing June 5, 2026 20:23 — with GitHub Actions Inactive
@nightt5879
nightt5879 temporarily deployed to azure-trusted-signing June 5, 2026 20:24 — with GitHub Actions Inactive
@inancgumus
inancgumus temporarily deployed to azure-trusted-signing June 9, 2026 16:44 — with GitHub Actions Inactive
@inancgumus
inancgumus temporarily deployed to azure-trusted-signing June 9, 2026 16:46 — with GitHub Actions Inactive
@inancgumus

Copy link
Copy Markdown
Contributor

cc: @szkiba I've approved this PR.

@szkiba
szkiba merged commit 4273f1e into grafana:master Jun 10, 2026
51 of 54 checks passed
social4hyq pushed a commit to social4hyq/homebrew-core that referenced this pull request Sep 20, 2026
k6 2.1.0

Created-by: HarmonybrewBot
Commit-by: HarmonybrewBot
Merged-by: HarmonybrewBot
Description: Created by `brew bump`

---

Created with `brew bump-formula-pr`.<details>
  <summary>release notes</summary>
  <pre>k6 `v2.1.0` is here 🎉  

This release includes:
- An opt-in feature-flag system — `--features`, the `K6_FEATURES` environment variable, and a `k6 features` discovery command — shipping with experimental native histograms for trend metrics as its first flag.
- A context-level `proxy` option for browser contexts.
- Subcommand discovery in `k6 x`, so binaries can report which extension commands they expose.

## Breaking changes

There are no breaking changes in this release.

## New features

### `k6 cloud test list` command [#6007](grafana/k6#6007)

A new `k6 cloud test` command group has been added, with a `k6 cloud test list` subcommand that lists the load tests of a Grafana Cloud k6 project. It complements the `k6 cloud project list` command introduced in v2.0.0.

The project to list tests for is resolved in the following order:

1. The `--project-id` flag.
2. The `K6_CLOUD_PROJECT_ID` environment variable (cloud config `projectID`).
3. The default project of the configured stack, populated by `k6 cloud login`.

Output defaults to a human-readable table. Pass `--json` to emit a JSON array instead, mirroring the format established by `k6 cloud project list`.

```shell
k6 cloud test list
k6 cloud test list --project-id 12345
k6 cloud test list --json
```

### Feature flags and experimental native histograms [#6055](grafana/k6#6055), [#6056](grafana/k6#6056)

k6 now has an opt-in feature-flag mechanism for trialing new, not-yet-stable behavior without affecting existing runs. Flags can be enabled on `k6 run` and `k6 cloud run` through the `--features` flag (comma-separated or repeated), the `K6_FEATURES` environment variable, or the `features` key in `config.json`. Enabled flags are surfaced as metric tags and propagated into archives and cloud workers so a run behaves consistently wherever it executes.

Use `k6 features` (or `k6 features --json`) to discover the available flags and their lifecycle:

```console
$ k6 features
FEATURE             LIFECYCLE      DESCRIPTION
native-histograms   Experimental   Use native histograms for trend metrics
```

The first flag shipped is `native-histograms`, an experimental flag that makes k6 use native histograms for trend metrics:

```shell
k6 run --features native-histograms script.js
# or
K6_FEATURES=native-histograms k6 run script.js
```

### Subcommand discovery in `k6 x` [#5972](grafana/k6#5972)

Running `k6 x` now lists the available subcommands — both the ones baked into the binary and those advertised by the extension registry (official and community). Tab-completion surfaces the same set once the catalog has been cached locally by a prior `k6 x` run, so completion never blocks on the network.

```text
$ k6 x
...
Available Commands:
  agent       Bootstrap an AI-assisted k6 testing workflow in any editor
  docs        CLI k6 docs for AI agents and users
  explore     Explore k6 extensions for Automatic Resolution
  mcp         An MCP server for k6 for AI agents
```

This makes a k6 binary self-describing — particularly useful for AI agents driving k6, which previously had no way to introspect which extension subcommands were available.

### Browser context proxy option [#5924](grafana/k6#5924)

Browser contexts can now be configured with a context-level `proxy` option, letting you route a context's traffic through a proxy without launching a custom-built binary or relying on environment proxy variables (which only affected k6's DevTools WebSocket connection). The option is wired to Chromium through `Target.createBrowserContext`, and invalid proxy configuration now fails early when `proxy.server` is missing. Thanks, @nightt5879!

```js
const context = await browser.newContext({
  proxy: {
    server: 'http://proxy.test:8080',
    bypass: 'localhost,127.0.0.1',
  },
});
```

### Browser `locator.isInViewport()` [#6023](grafana/k6#6023)

A new `locator.isInViewport()` method reports whether an element intersects the browser viewport. It accepts an optional `ratio` (0 to 1) that sets how much of the element must be visible, defaulting to `0` so any visible pixel counts, matching Playwright's `toBeInViewport` semantics. The call waits for the element to attach, honoring the `timeout` option, then measures the intersection once. Thanks, @Anuragp22!

```js
const button = page.locator('button#submit');
if (await button.isInViewport()) {
  await button.click();
}
```

### Basic auth for the OpenTelemetry HTTP exporter [#5997](grafana/k6#5997)

The OpenTelemetry output's HTTP exporter can now send HTTP Basic Auth credentials. Set them through the `K6_OTEL_HTTP_EXPORTER_USERNAME` and `K6_OTEL_HTTP_EXPORTER_PASSWORD` environment variables, or the `username` and `password` keys in the output config.

```shell
K6_OTEL_HTTP_EXPORTER_USERNAME=user \
K6_OTEL_HTTP_EXPORTER_PASSWORD=secret \
k6 run --out opentelemetry script.js
```

### `single()` selection helper in `k6/html` [#6002](grafana/k6#6002)

`Selection.single(selector)` returns at most one matching element, backed by goquery's `Single` matcher for a faster lookup than `find()` when you only need the first match. Thanks, @rohan-patnaik!

```js
import { parseHTML } from 'k6/html';

const doc = parseHTML(content);
const title = doc.single('h1').text();
```

## UX improvements and enhancements

- [#5971](grafana/k6#5971) Tags browser API failures with `module=browser` so that browser errors surfaced in Grafana Cloud Logs can be filtered separately from other log sources.

## Bug fixes

- [#5794](grafana/k6#5794) Makes the `--vus` flag work as a standalone execution shortcut instead of being silently ignored when a script defines scenarios. Running `k6 run script.js --vus N` now creates a `shared-iterations` scenario with `N` VUs and `N` iterations, overriding any script-defined scenarios with a warning — consistent with how `--iterations`, `--duration`, and `--stages` already behave. Thanks, @Reranko05!
- [#6013](grafana/k6#6013) Rejects invalid threshold percentiles. A percentile aggregation value outside the 0 to 100 range (or `NaN`) now fails parsing with a clear message instead of being silently accepted. Thanks, @immanuwell!
- [#6011](grafana/k6#6011) Writes the on-disk k6 config file with owner-only permissions (`0o600`, inside a `0o700` directory). The file can hold the Grafana Cloud API token (`collectors.cloud.token`), so tightening it keeps other local users on shared hosts (CI runners, multi-user boxes, sidecar containers) from reading the token. Existing configs are upgraded on the next write, for example the next `k6 cloud login`.

## Maintenance and internal improvements

- [#6033](grafana/k6#6033) Moves Docker Hub image publishing to a Google Artifact Registry mirror.
- [#5953](grafana/k6#5953) Installs `s3cmd` via `apt` instead of `pip` to fix the `k6packager` image build.
- [#6052](grafana/k6#6052) Fixes the browser end-to-end test workflow.
- [#5984](grafana/k6#5984) Routes per-test logger output to the test instance instead of the global `logrus`, improving test isolation.
- [#5985](grafana/k6#5985) Fixes the flaky `TestPageScreenshotFullpage` browser test.
- [#5981](grafana/k6#5981) Lets Renovate track the Go minor version in the `setup-go` workflows.
- [#5968](grafana/k6#5968) Honors the caller-pinned ref in the shared lint action.
- [#6041](grafana/k6#6041), [#6054](grafana/k6#6054) Aligns the Go module directive and toolchain (1.25.0 / 1.25.11).
- [#5967](grafana/k6#5967) Updates the release notes template after v2.0.0.
- [#6015](grafana/k6#6015) Updates `golang.org/x/net` to `v0.55.0` [security].
- [#6026](grafana/k6#6026) Updates `golang.org/x/crypto` to `v0.52.0` [security].
- [#5962](grafana/k6#5962), [#6028](grafana/k6#6028) Updates `google.golang.org/grpc` to `v1.81.1`.
- [#5960](grafana/k6#5960) Updates `grafana/shared-workflows/get-vault-secrets` to `v1.3.2`.
- [#5958](grafana/k6#5958) Updates `grafana/shared-workflows/azure-trusted-signing` to `v1.0.2`.
- [#5957](grafana/k6#5957) Updates `github/codeql-action` to `v4.35.4`.
- [#5959](grafana/k6#5959) Updates `grafana/shared-workflows/dockerhub-login` to `v1.0.4`.
- [#6009](grafana/k6#6009) Moves the browser `PageScreenshotOptions` parsing into the mapping layer.
- [#5964](grafana/k6#5964) Adds the k6 feature-flags specification under `openspec/`.
- [#6067](grafana/k6#6067) Forces the legacy `x/net/http2` implementation on the `gotip` CI test job.
- [#6065](grafana/k6#6065) Lets Renovate update the `Dockerfile` on the v1.x branch.
- [#6031](grafana/k6#6031) Updates `go.opentelemetry.io/otel` to `v1.44.0`.
- [#6086](grafana/k6#6086) Updates `golang.org/x` dependencies (`crypto` to `v0.53.0`, `net` to `v0.56.0`, `term` to `v0.44.0`).
- [#6061](grafana/k6#6061) Updates `golang.org/x/sync` to `v0.21.0`.
- [#6030](grafana/k6#6030) Updates `github.com/tidwall/gjson` to `v1.19.0`.
- [#6029](https:

See merge request: Harmonybrew/homebrew-core!13169

This branch was previously deployed

1 inactive deployment
azure-trusted-signing — e6652455 Deployed Jun 9, 2026 by inancgumus via sign-packages #9624
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unable to set up proxy

4 participants