Commit Graph
100 Commits
Author SHA1 Message Date
Muki Kiboigo ddfa034310 add credentials_mode for proper CORS credentials handling 2026-09-04 06:59:39 -07:00
Muki Kiboigo f31b32ac4e don't store network in CorsGate 2026-09-04 06:59:38 -07:00
Muki Kiboigo cf37a94f16 get rid of blocked on CorsGate check result 2026-09-04 06:59:38 -07:00
Muki Kiboigo 4e79f24814 basic validation without preflighting 2026-09-04 06:59:38 -07:00
Muki Kiboigo ec45dccacf add obey_cors option 2026-09-04 06:59:38 -07:00
Muki Kiboigo 212c806be5 add basic CorsGate scaffold 2026-09-04 06:59:37 -07:00
Muki Kiboigo b114382c44 add origin field to HttpClient Request 2026-09-04 06:59:37 -07:00
Karl Seguin 396cffa1c1 Merge pull request #3391 from lightpanda-io/resource-timing
webapi: resource-timing
2026-09-04 18:03:17 +08:00
Karl Seguin 1d66b2fbc8 Merge pull request #3398 from lightpanda-io/link-ownership
chore: Move ownership of the Inbox from HttpClient to WebSocker Driver
2026-09-04 03:04:57 +08:00
Karl Seguin 87320a506d chore: Move ownership of the Inbox from HttpClient to WebSocker Driver
Currently, the HttpClient owns the inbox and its borrowed by the Link. This is
a bit backwards, but it also means that we can't eagerly create a Link: the
Link needs the inbox, so it needs the HttpClient, which is created by the
Browser (which creates an Isolate).

Remember, the Inbox is one of the few things shared between the main thread
and the worker, so either end can own it and the other can borrow it.

This switches the ownership so that the HttpClient now borrows the Inbox from
the Server's side of the Link (the WebSocket).

The main goal of this change is to prepare for more advanced HTTP WebDriver
flows. The more we can create _without_ a Browser, the fewer edge cases we have
to deal with (Browser because it's expensive and has to be created on the
Worker thread due to how V8::Isolate works).
2026-09-03 16:51:12 +08:00
Karl Seguin 872fd7d5ff webapi: resource-timing
Adds resource timing, e.g. `performance.getEntriesByType("resource")`.

The `resource-timing` WPT category is currently at 4.6%, and this is a first
step at improving it. It also hopefully fixes https://github.com/lightpanda-io/browser/issues/3359

This is more complicated than I thought because there's a "Timing-Allow-Origin"
header that a server can include which hides some of the data if the request
doesn't come from the listed origin. And that, of course, interacts with
redirects.

(The DOMException change is seemingly random, but it came up in one of the WPT
cases I was looking at).
2026-09-02 20:39:51 +08:00
Karl Seguin 7d39a4fbaf http: handle redirects better
Our redirect handling was "optimized" to re-use the same easy connection, at the
cost of circumventing the entire pipeline. A redirect would not check the new
target's robots.txt, rate limit, CORS, Web Auth, ...

The new code simply restarts the pipeline (with a discriminator tag so that
things like CDP can tell the difference between a redirected request and a new
one).
2026-09-02 12:41:53 +08:00
Karl Seguin 2474a2c2bc Merge pull request #3368 from lightpanda-io/window-stop
webapi: window.stop()
2026-09-02 09:06:07 +08:00
Karl Seguin ed88bd763f complete merge 2026-09-02 08:17:15 +08:00
Karl Seguin e25d2da0c0 chore: Simplify making HTTP requests.
If you look at https://github.com/lightpanda-io/browser/pull/3293, you'll see
a relatively contained change that has to touch over 20 files. The issue is that
every HttpClient.newRequest needs to provide a lot of data. But `newRequest`
takes a 2nd parameter: the HttpClient.Owner. If we make that Owner a little
smarter, we can start to remove some of the individual fields needed in
newRequest. For example, we can still allow a callsite to pass frame_id but,
by default, we can use the owner's frame_id (which is what we want in most
cases).
2026-09-02 07:55:53 +08:00
Halil Durak 87308b5e23 compute site-for-cookies from ancestor chain of a Frame
The site for cookies were computed from the immediate parent `Frame`, which would allow sending a cookie that's `SameSite=Strict` from 2 levels deep under. Directly from RFC6265bis, this PR essentially implements (except for step 4, we skip host-less ancestors):

   Given a Document (document), the following algorithm returns its
   "site for cookies":

   1.  Let top-document be the active document in document's navigable's
       top-level traversable.

   2.  Let top-origin be the origin of top-document's URI if top-
       document's sandboxed origin browsing context flag is set, and
       top-document's origin otherwise.

   3.  Let documents be a list consisting of the active documents of
       document's inclusive ancestor navigables.

   4.  For each item in documents:

       1.  Let origin be the origin of item's URI if item's sandboxed
           origin browsing context flag is set, and item's origin
           otherwise.

       2.  If origin is not same-site with top-origin, return an origin
           set to an opaque origin.

   5.  Return top-origin.
2026-09-02 07:19:35 +08:00
Karl Seguin 76c8f4bb6e webapi: window.stop()
window.stop() is less destructive than other mechanisms we have. For one, it
seems largely isolated to pending or inflight HTTP requests. For anther, it
keeps the page intact.

To achieve this, HttpClient gains an `cancelRequests` which is a gentler version
of `abortOwner`. It cancels inflight/pending HTTP requests, which results in
error callbacks (not shutdown callbacks) firing.

Just like https://github.com/lightpanda-io/browser/pull/3189 I ran into the
problem that I couldn't distinguish between an HTTP request that was canceled
because of user-action (e.g. calling window.stop(), or xhr.abort()) and an HTTP
request that was internally aborted. These now have distinct errors/flows so
that we can present the correct state. Most places that aborted now all
transfer.cancel() which results in a distinct `error.TransferCanceled` (some
places still abort -> `error.Abort`). It should be possible to revisit 3189 now.

The CDP "Page.stopLoading" now hooks into this new behavior. Fixes
https://github.com/lightpanda-io/browser/issues/3351
2026-09-01 14:55:19 +08:00
Karl Seguin a68697921e cdp: remove race between terminate/disconnect
A client that disconnects might get treated as a harsher terminate failure (e.g.
watchdog). This doesn't have a huge impact, but it makes the CI flaky and it
produces more logs than is necessary.

In a terminate state, the driver will now check its inbox to see if this is a
client disconnection.
2026-09-01 12:11:07 +08:00
Karl Seguin 7c51dfff05 bidi: add skeleton for bidi
1. Abstract "Driver". Non-CDP things that referenced *CDP now reference a Driver
2. Move the NodeRegistry out of CDP. This created an artificial link between
   agent / mcp and CDP
3. Add BiDi driver with enough to navigate to a page
2026-08-31 22:44:59 +08:00
Karl Seguin 7dd4496b0f cli: add --http-version 1.1 / auto flag
https://github.com/lightpanda-io/browser/issues/3348

When set to 1.1, libcurl is configured to only offer HTTP 1.1. By default, or
when set to "auto", it's up to libcurl to decide how to connect. This maps to
libcurl's CURL_HTTP_VERSION_1_1 and CURL_HTTP_VERSION_NONE.

LP.configureCDP now takes an `httpVersion` field which can be "1.1" or "auto"
to control that specific browser session. Ideally this is called prior to any
navigation.
2026-08-31 16:16:47 +08:00
muki 57b86112b3 Merge pull request #3240 from lightpanda-io/xhr-async-flag
XHR Sync Request
2026-08-26 17:11:48 -07:00
Karl Seguin fce38a1aa8 Merge pull request #3289 from lightpanda-io/fix-double-releases
fix: Fix double-release and other (edge-case) memory issues
2026-08-27 07:34:45 +08:00
Karl Seguin cfd921ecab fix: Fix double-release and other (edge-case) memory issues
Our `Arena` from the `ArenaPool` now tracks if it's already been released. On
a subsequent release, it panics then and there. Without this, the code will
almost certainly panic anyways, but it will panic in a seemingly unrelated
place. Hopefully this makes identifying future cases of this easier (since we'll
get the stack trace of the re-releaser).

Fix 3 separate memory issues, all edge cases.

1 - an XHR error handler that re-open/sends would incorrectly null the new
    transfer (maybe this isn't that odd, maybe it's a common retry-on-error).

2 - On a curl_easy_pause error (from WebSocket.zig) we now unqueue the just-
    queued message, because the error will errdefer the message arena to be
    cleaned up.

3 - ScriptManager now cleans up after itself on a failure prior to submit()
    being called.
2026-08-26 18:24:12 +08:00
Pierre Tachoire 467167d6e6 http: strip Authorization on cross-origin redirects 2026-08-26 10:00:11 +02:00
Muki Kiboigo 234d47d0e2 syncRequest -> submitSync on Transfer 2026-08-25 17:43:03 -07:00
Halil Durak 87a87ba62e HttpClient: remove pending_low_queue, separate keys for headers_only in cache, more eager connection reuse 2026-08-25 14:20:38 +03:00
Halil Durak c1079444c4 addHeader -> setHeader + enable low prio for images 2026-08-25 14:18:46 +03:00
Halil Durak e58f99da18 add --load-resources CLI arg, supporting image param 2026-08-25 14:18:45 +03:00
Karl Seguin a6618fff63 http: Guarantee Transfer contract of single outcome callback
HttpClient guarantees that only one of done/error/shtudown callback will be
called. But that guarantee had a hole when a done/error callback would trigger
JS that caused an HttpClient.abort (e.g. by scheduling a navigation).

So far, this isn't a problem. fetch/XHR guard against this themselves, and
ScriptManager doesn't care of it happens.

But https://github.com/lightpanda-io/browser/pull/3230 doesn't guard against it
and would crash if it happens: its doneCallback frees `self` but can re-enter
shutdown which needs `self`. While 3230 could fix this issue, I'd prefer to
bake this contract into the HttpClient. Transfers now have an
`_outcome_delivered: bool` which guarantees that only one of done/error/shutdown
is called.
2026-08-22 11:09:52 +08:00
Karl Seguin 57bcdfed10 chore: Simplify Network and HttpClient init
Remove app field from Network
2026-08-22 07:05:05 +08:00
Karl Seguin decedca6e7 chore: Network cleanup
Network has accumulated a bit of bagged. It knows a lot about certificates, it
knows a lot of the cache. I have plans to expand Network, and wanted to clean it
up.

1 - certificate logic moved to dedicated Certificates.zig
2 - Removed CurlDebugAllocator
    - this makes setup easier, to Updater can just init libcurl directly
3 - Change Updater to be a single function
4 - Cache initialization is don in the Cache
5 - ?Cache => Cache{.kind = .noop}
2026-08-22 07:04:58 +08:00
Karl Seguin 9f4b39d723 Merge pull request #3239 from lightpanda-io/ratelimiter
http: Add optional rate limit
2026-08-22 06:58:52 +08:00
Karl Seguin 09edfd3ce5 http: Add optional rate limit
Add an optional per-host rate limit. This currently only applies to the top-
level navigation. This makes it simpler to implement and simpler to reason
about. The full load of a page is only ever delayed at its head, not
sporadically through the page loading.

The use-case where a RateLimiter is most useful is when the browser is crawling
multiple pages of the same site, and in that case, the top-level rate limit
still applies some degree of limit to any linked resources (e.g. a JS on a
different host).

`--http-nav-delay` is the delay, in milliseconds, to apply to top level
navigates per host. Currently defaults to 0 (disabled).

`--http-nav-burst` is the burst allowed per host. Defaults to 1 (has no impact
when `--http-nav-delay` is disabled).
2026-08-20 16:03:42 +08:00
Karl Seguin b70f7a325e webapi: Blob / File conformance
1-
Per spec, dictionary objects should be loaded in lexicographical order (a). So
the following always pushes `endings` before `type`

```js
  new Blob([], {
    get type()    { order.push('type'); return ''; },
    get endings() { order.push('endings'); return 'transparent'; },
  });
```

This requirement is now enforced by a comptime check. So, a lot of files were
updated to satisfy this requirement.

2-
Blob parts now work with the @@iterator protocol. This is done more generically
(via `value.iterator())` but is currently only used by Blob (and File). Other
types that need this (e.g. URLSearchParams) can be done in a follow up.

3-
Add support for Blob.textStream

4-
Reject XHR/Fetch requests on blob URLs if the method isn't GET

5-
Strip #hash from blob URL when doing lookup

All of this fixes ~175 FileAPI WPT cases, though it's possible #1 (the
dictionary ordering) helps with a few others.

(a) https://webidl.spec.whatwg.org/#js-dictionary
2026-08-20 13:57:51 +08:00
Pierre Tachoire 067709bb58 cdp: add obeyRobots to lp.configureCDP 2026-08-18 17:07:18 +02:00
Karl Seguin d3c26f589e Merge pull request #3219 from lightpanda-io/sec-ch-ua-full-version
send Sec-Ch-Ua-Full-Version-List header
2026-08-18 20:00:44 +08:00
Pierre Tachoire 9ff1ad8306 fix header key case for Sec-Ch-Ua* 2026-08-18 12:25:40 +02:00
Pierre Tachoire a961156283 send Sec-Ch-Ua-Full-Version-List header
Add Sec-Ch-Ua-Full-Version-List header with the full LP version

```
Sec-Ch-Ua-Full-Version-List: "Lightpanda";v="1.0.0-dev.8713+33867c727"
```
2026-08-18 09:38:00 +02:00
Karl Seguin 5c0aa90ea4 Merge pull request #3187 from lightpanda-io/http-header-option
add --http-header option to send headers on every HTTP requests
2026-08-18 15:24:10 +08:00
Pierre Tachoire 33867c7275 Merge pull request #3116 from lightpanda-io/nikneym/adblocker-init
Initial implementation for easy list parsing and AdBlocker
2026-08-18 08:50:42 +02:00
Pierre Tachoire a6afdce444 add --http-header option to send headers on every HTTP requests 2026-08-17 17:18:13 +02:00
Karl Seguin 737f69ee44 http: improve header overwrite/enforcement
Builds ontop of https://github.com/lightpanda-io/browser/pull/3200 to centralize
header enforcement and standardize merge vs overwrite header logic.

The API is still a `setHeader` and `appendHeader`, with a source, but set/append
both are thin wrappers around private `putHeader`. putHeader blocks overwriting
restricted headers (user-agent). The `source` acts as a priority (ordered enum)
which further restricts the header AND depending on whether set or append were
called, controls if the value is overwritten or appended to.

3200 had an always-append which can cause problems, e.g. a script setting
Accept-Language: fr would have results in the value being appended to the
default, e.g.: 'en-US,en;q=0.9, fr'.
2026-08-15 12:09:08 +08:00
Pierre Tachoire dd83300095 htttp: add Transfer.appendHeader
and make transfer.addHeader private.
2026-08-14 16:33:49 +02:00
Pierre Tachoire 6e72441001 make Sec-Ch-Ua not modifiable via cdp 2026-08-14 16:05:19 +02:00
Pierre Tachoire 80e45ab016 apply user-agent validation rules on request intercepted 2026-08-14 12:26:33 +02:00
Muki Kiboigo fb5a9b6a35 get rid of allocator stored in RobotsGate 2026-08-13 08:20:15 -07:00
Halil Durak c4b0b7cc33 HttpClient: check the adblocker inside isUrlBlocked` 2026-08-13 16:39:16 +03:00
Halil Durak c111fc5675 load and parse block list once and share between HTTP clients 2026-08-13 16:39:14 +03:00
Halil Durak 835cf8917b update tests 2026-08-13 16:39:13 +03:00
Halil Durak b1b9a64573 CLI option for adblock list passing + integrate adblocker to http client 2026-08-13 16:39:13 +03:00