惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

U
Unit 42
P
Proofpoint News Feed
The Last Watchdog
The Last Watchdog
S
Secure Thoughts
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
N
News | PayPal Newsroom
Application and Cybersecurity Blog
Application and Cybersecurity Blog
O
OpenAI News
S
Security @ Cisco Blogs
宝玉的分享
宝玉的分享
Hacker News: Ask HN
Hacker News: Ask HN
T
Troy Hunt's Blog
Google Online Security Blog
Google Online Security Blog
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
TaoSecurity Blog
TaoSecurity Blog
Help Net Security
Help Net Security
Latest news
Latest news
NISL@THU
NISL@THU
S
Security Affairs
博客园_首页
C
CXSECURITY Database RSS Feed - CXSecurity.com
博客园 - 聂微东
AI
AI
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Recent Announcements
Recent Announcements
P
Privacy & Cybersecurity Law Blog
小众软件
小众软件
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Hugging Face - Blog
Hugging Face - Blog
博客园 - 司徒正美
AWS News Blog
AWS News Blog
W
WeLiveSecurity
Google DeepMind News
Google DeepMind News
I
InfoQ
Schneier on Security
Schneier on Security
Recent Commits to openclaw:main
Recent Commits to openclaw:main
T
The Exploit Database - CXSecurity.com
IT之家
IT之家
T
Threatpost
Scott Helme
Scott Helme
L
LINUX DO - 热门话题
腾讯CDC
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
N
News and Events Feed by Topic
L
LINUX DO - 最新话题
F
Full Disclosure
大猫的无限游戏
大猫的无限游戏
H
Heimdal Security Blog
S
SegmentFault 最新的问题

hayato

Chromium's DOM Deep Dive: Understanding Shadow DOM and Tree Structures - hayato Introducing the Task Scheduler in Chromium's Network Stack: A 0.3% Improvement in Loading Speed - hayato 出版のお知らせ 「普通の人が資産運用で99点をとる方法とその考え方」 - hayato 普通の人が資産運用で99点をとる方法とその考え方 - hayato Imperative Shadow DOM Distribution API - hayato Google にソフトウェアエンジニアとして入社して 10 年と 10 日がたちました - hayato Web のつくりかた: ソフトウェアエンジニアリングと雰囲気で理解する Web 標準とブラウザのつくりかた - hayato ブラウザのしくみ: データ構造とアルゴリズムと雰囲気で理解する DOM と Shadow DOM - hayato Rust が最強のプログラミング言語である証明 - hayato 六本木ではたらくソフトウェアエンジニアへのよくある質問とその答え (FAQ) (2015 - 2017) - hayato What's New in Shadow DOM v1 (by examples) - hayato Simple timer application for the pomodoro technique - hayato Google の面接試験で出る (はずがない)「海賊と金貨」問題を空気を読まないでプログラムで解いてみる - hayato hayato.io (4topcoder) - hayato SRM432 - hayato SRM405 - hayato SRM386 - hayato SRM385 - hayato SRM384 - ゲーム - hayato SRM369 - hayato SRM368 - hayato SRM366 - typo 2 連発 - hayato Python - generator で素数生成 - hayato New Launguage - 新言語追加の予定? - hayato lxml で HTML スクレーピング - hayato SRM358 - Today is a Bad Day (-50). - hayato SRM349 FizzBuzz 問題が簡単すぎるそんなあなたへ。「赤いマーブル問題」。 - hayato Suffix Array - hayato google-code-prettify を導入してみる - hayato Subset の生成 - hayato 2006 年のまとめ - SRM332 - hayato Google Code Jam 2006 - Championship Round - hayato Blogger Beta に移行 - hayato Google Code Jam 2006 - Round1 に挑戦 - Generics と AutoBoxing - hayato Google Code Jam 2006 - Qualification Round 通過 - hayato Google Code Jam 2006 - Qualification Round に挑戦 - hayato Google Code Jam 2006 - Practice1 - hayato Google Code Jam 2006 - 開催決定 - hayato Stay tuned... Global Google Code Jam 2006 - 秋に開催? - hayato SRM308 - Hard DP - hayato SRM304 - 条件付確率 - hayato JavaWorld に寄稿しました - hayato 2006 TopCoder Open Final Round - hayato IBM Rational Software Development Forum Tokyo で講演します - hayato SRM298 - ひとふで書き - hayato SRM293 - ビンゴ - hayato 2006 TopCoder Open - Round1 撃墜 - hayato 2006 TopCoder Open - Odds 発表 - hayato 2006 TopCoder Open Qualification Round - hayato SRM291 - OutOfMemory - hayato デブサミ 2006 で講演します - hayato SRM287 - 気分は中学生?連立2元方程式 - hayato SRM281 - 2006 年はポイントゼロスタート - hayato SRM280 - Challenge 祭り - hayato SRM277 - 初賞金げっと - hayato マラソン・マッチ - hayato reStructuredText で blogger - hayato 2006 TopCoder Open - NSA がスポンサーに - hayato SRM276 - お買い物は楽しく DP で - hayato SRM273 - bit 演算は int で行われるのね - hayato SRM272 - Division1 - 4 度目の正直 - hayato JavaOne Tokyo で講演します - hayato SRM270 - Integer == Integer - hayato SRM268 - Division2 脱出 - hayato SRM266 - NASA, we have a problem... - hayato SRM265 - Division2 落ち... - hayato SRM262 - 初 Division1 は Typo に終わる - hayato SRM260 - TopCoder への挑戦のはじまり - hayato
Resource loading with Content Addressable Bundles - hayato
Authors · 2021-03-11 · via hayato

This proposal was merged into wicg/webpackage repo.

Last update: Mar 2021

This is a strawperson proposal at very early stage, aiming to load multiple resources efficiently, using a Content-Addressable Bundle, which can link to other bundles.

  • Hayato Ito (hayato@google.com)

Participate

Introduction

  • Loading many unbundled resources is still slower in 2021. We concluded that bundling was necessary in 2018, and our latest local measurement still suggests that.

  • With JS bundlers (e.g. webpack), execution needs to wait for the full bytes to come. Ideally loading multiple subresources should be able to utilize full streaming and parallelization, but that's not possible if all resources are bundled as one javascript. (For JS modules execution still needs to be waited for the entire tree due to the current deterministic execution model)

There is a proposal which aims to address the above issues, called Subresource loading with Web Bundles, a new way to load a large number of resources efficiently using a format that allows multiple resources to be bundled, e.g. Web Bundles.

With the Subresource loading with Web Bundles proposal, developers will write

<link
  rel="webbundle"
  href="https://example.com/dir/subresources.wbn"
  resources="https://example.com/dir/a.js https://example.com/dir/b.js https://example.com/dir/c.png"
/>

to tell the browser that subresources specified in resources attribute can be found within the https://example.com/dir/subresources.wbn bundle. The browsers will fetch a bundle, and load resources from the bundle.

However, currently, there is no mechanism to fetch the partial content of the bundle. Browsers will fetch the entire bundle even if only one of the resources in the bundle is updated in a server side.

This explainer proposes a new approach on the top of Subresource loading with Web Bundles, aiming more flexibility of how resources are grouped together as bundles. This allows web developers to split code into various bundles which can then be loaded on demand or in parallel, and also provide a new capability to express a dependency between bundles, which can be used for a browser to improve a loading performance.

Goals

  • This proposal aims to support Code Splitting, as webpack or other bundlers already support as a user-land solution. Smaller bundles, if used correctly, can have a major impact on load time.

  • Non-opinionated about bundle granularity. There are trade-offs how a site composes their resources into bundles in order to balance various factors like total bytes transferred, loading latency, or cache granularity. Instead of a all-or-nothing bundle, this proposal aims to provide a way to express a dependency graph of bundles. The use of bundlers is an established practice in Web development. Bundlers, such as webpack, skypack, would know much about which resources should be grouped as a bundle, and might want to express their intent as a dependency graph of bundles, considering various trade-offs. They wouldn't want to lose this information in building an all-or-nothing bundle, and a browser wants to know it to improve a loading performance.

  • Bundles can be served from a static content server. The proposal doesn't require any smart server, such as dynamically assembling resources into a bundle. Bundles should be statistically generated by a bundler and can be copied to a static content server. This proposals assumes that this is an important for a wide adoption.

  • The proposal aims to give a browser an opportunity to improve their cache efficiency by introducing immutability to a bundle. If a bundle's URL doesn't change, we assume the bundle's contents are exactly same. This is not an effort by a convention. The proposal aims to force immutability by introducing a Content-Addressable Hash, which is conceptually similar to a Git's commit ID you might be familiar with. Content-Addressability gives web developers reproducible builds as well as giving a browser an opportunity to improve their cache efficiency.

  • URL integrity. Brave raised the concern that bundling systems could be used in a way where URLs are "rotated" between different requests, making URLs less meaningful/stable. Content-Addressability prevents this kind of undesired behaviors.

Non-Goals

  • Although this proposal aims to give a browser an opportunity to improve cache efficiency by utilizing immutability and a dependency graph of bundles, this proposal doesn't define any normative procedure how a browser should cache resources. That's up-to a browser, as of now.

  • This proposal's current scope is to serve static contents which don't contain any personal information. Although nothing prevents us from including such personal information, this proposal currently assumes every resources are publicly viewable ones.

  • Dynamic bundling use case. This use case requires a server side support. That's out of scope in this proposal.

  • A fine-grained cache control for each individual resource in a bundle. That's out of scope in this proposal. This proposal assumes that a granularity of a cache can be controlled by a granularity of bundles, if used correctly, at some level. If you want a fine-grained cache control for an individual resource in a bundle, this proposal doesn't meet your requirements.

Web Bundle format

TODO(hayato): Define a format using CDDL. For a while, this section explains a non-normative conceptual format.

Bundle's URL should be <main-resource-url>.<hash>.wbn.

  • Example: https://example.com/app/index.js.abcd.wbn
  • We'll explain how a hash is calculated later.

This proposal extends the WebBundle Format with a new version number. The bundle should have the following fields:

  1. bundle's hash: abcd

    Note: This proposal has not decided which hash functions we should use. This section writes down a hash as just 4 characters, however, a hash would be much longer in real cases, like 40 characters used in a Git commit ID.

  2. bundle's main-resource URL: https://example.com/app/index.js

    Note: The current WebBundle format also defines a main-resource URL. Now, a main resource URL must be a part of the bundle's URL, "<main-resource-url>.<hash>.wbn".

  3. Resources section, which is conceptually as follows:

URLtyperesponse headersbodyhash
./index.jsinline<encoded response header><body>b0b0
./foo.jsinline<encoded response header><body>c0c0
./bar.jsinline<encoded response header><body>d0d0
https://cdn.example.com/date-util.js.f1f2.wbnexternalNANAf1f2
  • A resource's URL is either relative or absolute. If a URL is relative, we call it an inline resource. If a URL is absolute, we call it an external resource.

  • A relative URL must be resolved with base of <main-resource-url>. e.g.

    ./foo.js is resolved as https://example.com/app/foo.js.

    Note: The motivation of using a relative URL for inline resources is to make it clear that path restriction is always met.

  • Inline resources must have response headers and body.

  • External resources must not have response headers nor body (Marked as NA in the table).

  • Each resource must have a hash.

  • Bundle's main resource must be there and must be an inline resource.

  • External resource's URL must be formatted as <external-main-resource>.<hash>.wbn, which is pointing to an external bundle.

    Note that an external resource entry tells a browser that an external resource can be found in a linked external bundle. In the example, https://cdn.example.com/date-util.js can be loaded from https://cdn.example.com/date-util.js.f1f2.wbn.

  • An external resource's URL can be cross-origin to the containing bundle.

  • A bundle can have any number of inline resources and any number of external resource entries.

A hash must be calculated as follows:

  • For an inline resource:

    resource's hash := hash(hash(`<resource-url>`) + hash(`<canonicalized response headers>`) + hash(`<body>`))
    
  • For an external resource:

    resource's hash := external bundle's hash
    
  • For a bundle:

    bundle's hash := hash(main-resource-hash + resource0-hash + resource1-hash + ... (for every resources in a bundle))
    

    (This implies we calculate a hash recursively if a bundle contains an external resource)

    Note: A hash should be changed if a main resource is changed.

We call a bundle which satisfies the above requirements, Content Addressable Bundles ; In short, CAB.

These requirements imply that a dependency graph of bundles forms a DAG (directed acyclic graph). If it has a cycle, a hash can't be calculated.

To distinguish CAB and other existing Web Bundles which are not CAB, we may call the former immutable bundles, and the latter mutable bundles in order to make the difference clear in some contexts. Unless otherwise noted, a bundle means a CAB, an immutable bundle, in this proposal.

Web APIs

Declarative form

Note: Declarative syntax is tentative. We borrow <link>-based API from Subresource loading with web bundles proposal for the purpose of the explanation.

Example

The page (https://example.com/app/index.html):

<link rel=webbundle
      href=https://example.com/app/index.js.abcd.wbn
      resources="index.js" >

<script type="module" src="index.js" />

The bundle (https://example.com/app/index.js.abcd.wbn):

URLtyperesponse headersbodyhash
./index.jsinline<encoded response header><body>b0b0
./foo.jsinline<encoded response header><body>c0c0
./bar.jsinline<encoded response header><body>d0d0
https://cdn.example.com/date-util.js.f1f2.wbnexternalNANAf1f2

Another bundle (https://cdn.example.com/date-util.js.f1f2.wbn) (which is linked from the index.js.abcd.wbn):

URLtyperesponse headersbodyhash
./date-util.jsinline<encoded response header><body>0808
./string-util.jsinline<encoded response header><body>0909
https://cdn.example.com/common-util.js.3b3b.wbnexternalNANA3b3b

For illustration purposes, the dependency graph of resources is:

toppage
└── index.js
    ├── foo.js
    │   └── https://cdn.example.com/date-util.js
    │       ├── string-util.js
    │       └── https://cdn.example.com/common-util.js
    │           └── ...
    └── bar.js

These resources are bundled into three bundles, as follows:

toppage
└── https://example.com/app/index.js.abcd.wbn
    ├── index.js (main-resurce)
    ├── foo.js
    ├── bar.js
    └── https://cdn.example.com/date-util.js.f1f2.wbn
        ├── date-util.js (main-resurce)
        ├── string-util.js
        └── https://cdn.example.com/common-util.3b3b.js
            ├── commons-util.js (main-resource)
            └── ...

How to Load

  1. When the HTML page is parsed, a browser records that index.js can be loaded from the bundle, https://example.com/app/index.js.abcd.wbn.

  2. The browser starts to fetch https://example.com/app/index.js.abcd.wbn.

    1. (This is asynchronously done) The browser must parse the index section of the fetched bundle, and record a list of resource's URLs, foo.js, bar.js, and https://cdn.example.com/date-util.js, as resources which must be loaded from the bundle.

    Note a relative URL is resolved based on the bundle's URL. For an external resource, we use a filename convention. If the URL ends with ".<hash>.wbn", the browser removes this suffix and then record the URL.

  3. The browser sees <script type="module" src="index.js" /> tag. Since index.js is already noted, the browser must load it from the bundle.

  4. Suppose that index.js, which is an inline resource in the bundle, has the following content:

    import * as foo from "./foo.js";
    import * as bar from "./bar.js";
    ...
    

    The browser must load ./foo.js and ./bar.js from the bundle since these resources are already recorded.

  5. Suppose that foo.js, which is an inline resource in the bundle, has the following content:

    import * as date from "https://cdn.example.com/date-util.js";
    ...
    

    The browser knows https://cdn.example.com/date-util.js is an external resource and it should be loaded from https://cdn.example.com/date-util.js.f1f2.wbn. The browser must start to fetch https://cdn.example.com/date-util.js.f1f2.wbn.

  6. The browser must parse the index section of the bundle, date-util.js.f1f2.wbn, and record a list of resource's URLs, as the browser did for index.js.abcd.wbn.

  7. After recorded, the browser loads the main resource, date-util.js, from the bundle. If date-util.js depends on ./string-util.js, string-util.js is loaded from the bundle.

  8. Continue processing...

Notes:

  • The browser must validate a fetched bundle's hash integrity, and let a resource loading fail if the bundle is invalid.
  • The browser can cache a bundle by its Content-Addressable hash, and free to load a bundle from the cache, instead of from the network.
  • The browser may prefetch an external resource bundle as long as a semantics doesn't change. The browser should be careful not to record resource entries listed in a prefetched bundle too early.

In the previous example, we use <link>-based API to declare a starting node and its index information, as a bootstrap, however, if a browser supports Navigate to a bundle feature, we don't need such a declarative form in HTML.

For example, given that a bundle, https://example.com/app/index.html.0101.wbn, whose main resource is HTML file:

URLtyperesponse headersbodyhash
./index.html (main-resource)inline<encoded response header><body>0101
./index.jsinline<encoded response header><body>b0b0
./foo.jsinline<encoded response header><body>c0c0
./bar.jsinline<encoded response header><body>d0d0
https://cdn.example.com/date-util.js.f1f2.wbnexternalNANAf1f2

If a browser supports entering https://example.com/app/index.html.0101.wbn URL directly in its address bar, ./index.html doesn't have to declare resources. The browser can know them from the bundle's index section, before starting to parse index.html file.

Cache strategy

It's up-to browsers how to cache bundles. This explainer doesn't define any formal procedure, however, there are several possible approaches:

  • The browser might want to store a dependency graph of bundles in its cache storage, using some preferable data structures so that the browser can traverse a DAG efficiently.

  • If the browser wants to prefetch external resource bundles, the browser can traverse a DAG and start to prefetch a missing node.

    For example, when the page declares a bundle-A, the browser can know its hash immediately, and start to traverse a DAG, starting bunble-A node, which can be looked up by its hash in the browsers' cache storage.

    For example, if the browser has the following DAG, the browser starts to prefetch bundle-D immediately.

    bundle-A
    ├── bundle-B
    └── bundle-C
        ├── bundle-D  (missing in the cache storage)
        └── bundle-E
    
  • If the browser wants to save the cache storage space, the browser might want to store only index sections of bundles, which are good enough to traverse DAG, and doesn't store the bodies of inline resources because body might be space-consuming.

    For example, given that browser has the following dependency graph of bundles, where the bodies of bundle-C, bundle-D, bundle-E are missing, the browser can start to fetch them again, possibly in parallel, without waiting fetching bundle-C.

    bundle-A
    ├── bundle-B
    └── bundle-C (body is missing)
        ├── bundle-D (body is missing)
        └── bundle-E (body is missing)
    
  • Individual inline resources in a bundle must not interfere a browser's HTTP Cache of its resolved URL.

    For example, an inline resource, ./index.js, in the bundle, https://example.com/app/index.js.abcd.wbn, and https://example.com/app/index.js must be treated separately, in terms of caching.

Key scenarios

TODO(hayato): [Description of the end-user scenario]

Considered alternatives

TODO(hayato): Mention scopes.

FAQ

What happens if a bundle doesn't declare a dependency to a resource which is used from its inline resources?

Nothing is wrong if you know what you are doing.

However, this proposal strongly suggests that a bundle should be self-contained as much as possible; Include every resources as inline resources, or declare external dependencies explicitly in the bundle in order to achieve reproducibility.

In the above example, https://example.com/app/index.js.abcd.wbn declares a dependency to the specific version of date-util.js with https://cdn.example.com/date-util.js.f1f2.wbn.

It's totally fine to use an external file directly without any declaration. For example, index.js might use an external module, hello.js, as follows:

import * as hello from "https://cdn.example.com/hello.js";

However, we no longer guarantee reproducibility. If https://cdn.example.com/hello.js changes its semantics, the web site, a user of index.js.abcd.wbn, might break. Especially, this can be problematic if an external dependency's content is out of control.

If you can control an undeclared external resource's API stability somehow, this might not be a big issue. For example, index.js can hard-code the specific version of hello.js, such as:

import * as hello from "https://cdn.example.com/hello@4.12.js";

However, this proposal suggests that this kind of hard-coding should be avoided, and such a dependency information should be specified outside of source code as much as possible.

We can see many similar patterns in other package management systems, such as node (package.json), Rust (Cargo.toml), deno (deps.ts).

This proposal aims to support this pattern by making it possible to declare dependencies inside of a bundle.

Any recommendations for bundle's granularity? How should we group resources into bundles?

That's totally up-to you. There are various factors and trade-off to make a decision.

For example, if you want to make an initial loading faster, with a cold start, you probably want to make the size of a bootstrap bundle smaller; let it include only minimum inline resources which are required for first contentful paint.

toppage
└── https://example.com/app/index.js.abcd.wbn
    ├── index.js (main-resurce, which should be small)
    ├── support-file-for-initial-loading.js (which should be small)
    ├── ...
    ├── https://example.com/lazy-load-1.js.0088.wbn (the size might be big)
    │   └── ...
    ├── https://example.com/lazy-load-2.js.0089.wbn (the size might be big)
    │   └── ...
    └── https://example.com/lazy-load-3.js.008a.wbn (the size might be big)
        └── ...

As another example, if you have:

  • resources which wouldn't be updated frequently
  • and resources which would be updated frequently, such as daily update.

In this case, you might want to group these resources separately in order to reduce the total bytes transferred in each day.

For example, a site may serve the following bundles today:

toppage
   https://example.com/app/index.js.abcd.wbn
     index.js (main-resurce)
     https://example.com/daily-hot-contents.00a0.wbn
       ...
     https://example.com/cold-contents.js.00a1.wbn
       ...

If the daily-hot-contents is updated in the next day, a site re-packages their resources as follows:

toppage
└── https://example.com/app/index.js.c0e3.wbn (changed)
    ├── index.js (main-resurce)
    ├── https://example.com/daily-host-contents.00c0.wbn (changed)
    │   └── ...
    └── https://example.com/cold-contents.js.00a1.wbn (didn't change)
        └── ...

Then, the total byte transferred in the next day is nearly the sum of index.js and https://example.com/daily-host-contents.00c0.wbn because the browser probably can load cold-contents.js.00a1.wbn, whose hash didn't change, from its cache.

I would like to separate code into bundles for various reasons, but I'm afraid this causes too many round-trips if bundles are nested and discovered one bye one. Is there any good way to avoid it?

A possible workaround as a user-land solution would be to declare indirect dependent bundles as well as direct dependent bundles.

In the example case, the top page may declare:

<!-- This is mandarory -->
<link rel=webbundle href=https://example.com/app/index.js.abcd.wbn resources="index.js" >
<!-- The followings are optional, as a hint for a browser to prefetch -->
<link rel=webbundle href=https://cdn.example.com/date-util.js.f1f2.wbn>
<link rel=webbundle href=https://cdn.example.com/common-util.js.3b3b.wbn>

The second and third link elements are not mandatory because they are not a direct dependency, but declaring them should guarantee that a browser prefetch these bundles in parallel, instead of on-demand basis.

Nothing yet.

In the future, we hope that js bundlers provide an option to use Content Addressable Bundles as its output format.

Also, it would be nice that CDN supports a CAB format to serve their contents so that we can link to their CAB from our application bundles.

How can mutable bundles and immutable bundles (CAB) interact each other?

TODO(hayato): We might want to backport a declarative dependency capability to mutable bundles as well as CAB.

We might want to let them refer to each other, with some restrictions:

  • Any bundles can refer to immutable bundles.
  • Only mutable bundles can refer to mutable bundles.

We'll explore this problem space, and update this section.

Stakeholder Feedback / Opposition

Not yet.

References & acknowledgements