メインコンテンツへスキップ

セキュリティ・キャンプ2025 Y2 CDN自作ゼミ 応募課題さらし

·22729 文字·107 分·
応募課題 コードリーディング Chromium C++ C言語
著者
on-keyday
隠者
このブログは拙速にとりあえずの出来で公開しているものです。 レイアウトが崩れまくっている箇所がございますが修正途中なのでご了承ください セキュキャンが終わる頃までにはどうにかします…

はじめに
#

2025年度セキュリティ・キャンプのY2 CDN自作ゼミに受かったため 周りがやっているからという安易すぎる理由に基づいて応募課題晒しをしてみるものである。受かっただけで完走してないのにええんか? 本来は6/25ころに公開を考えていたのだがライセンス問題を考えた結果その解決策を出すのに時間がかかったためこの時期になっている。

https://www.ipa.go.jp/jinzai/security-camp/2025/camp/zenkoku/index.html

おそらく文章の拙さと内容のまとまりの無さと考えている感のなさについてはセキュキャン参加者の中では随一である自覚があるため、 「なんだこいつ、こんな拙い文章で良く受かったな」とか「ほぼコードのコピペじゃねーか」とか思われる可能性があることをあらかじめご了承いただきたい。 ほんとなんで通ったんだ…文章力見てるわけじゃないからか…

真面目な話するとこれは送ったほぼそのままを晒しているものであり、そしてあまり読みやすさというのには配慮されていない面が存在している。また当時の筆者の認識誤りや用語の誤用を含んでいる箇所などもあり、それを承知の上で読んでいただきたい。 特に元の文章はテキスト形式でありそのためブログに移すためにソースコードなどを適宜コードブロックに差し替えているが何分長すぎて変換しきれていない箇所が多いためしばしばレイアウト崩れを起こしている。こちらは適宜修正しているので何卒ご了承いただきたい。

あとLLMに書かせた内容についてのノートを以下においておく。しかし筆者の伝え方が悪く謎に美化されているため実際の記事内容や筆者の力量に対する期待値は、以下記述の1/100くらいに調整して読んでもらいたい。

この記事の主な部分ははChromiumとLinuxカーネルのソースコードリーディングに関する、非常に詳細かつ網羅的な記録です。ChromeのOmnibox(アドレスバー)でのURL入力から、ネットワークリクエストがOSの低レベルAPIに到達するまでの全プロセスを、ソースコードレベルで追跡しています。

応募課題として作成された性質上、一般的なブログ記事のような「読みやすさ」よりも、情報の正確性と網羅性を重視しています。そのため、膨大な情報量を含み、読了にはかなりの時間を要する可能性があります。

こんな方におすすめです。

Chromiumやブラウザの内部実装に深く興味を持つエンジニア

Linuxカーネルのネットワークスタックやシステムコールに興味がある方

低レイヤーのシステムプログラミングやデバッグ手法に関心がある方

巨大プロジェクトのソースコードを読み解く具体的なプロセスを知りたい方

応募課題本文
#

応募課題の質問文全体ははこちらにあるので適宜参照してください。

https://www.ipa.go.jp/jinzai/security-camp/2025/camp/zenkoku/sbn8o1000000c4oy-att/kadai_y2.txt

【問1:プログラミング自己アピール】
#

もうだいぶ前になりますがQUICというネットワークプロトコルスタックを0から作っていたことがあります。 一応それに付随して一応http1からhttp3までとそれを動かすサーバーも作ったりしました。(現在はクライアント側の統合に苦戦して以来止まっております)

https://github.com/on-keyday/utils/tree/main/src/include/fnet

https://github.com/on-keyday/utils/tree/main/src/include/fnet/quic

https://github.com/on-keyday/utils/tree/main/src/tool/server

QUICに至るまでの経緯

  1. 「かんたんVisualC++」という本に乗っていたsocketプログラミングがネットワークプログラミングに触れた始まり
  2. その後HTTP/1.1というのを知って実装しようと思いたちなんとか実装
  3. 暗号化が必要だとかでOpenSSLを触りSSL_readとかSSL_writeとかを使って実装してみたりする
  4. 暗号化通信できたぜと調子に乗ってあちこちのサイトに接続してみるってのをやっていたら一部のサイトがHTTP/1.1で繋がらないといったことでHTTP2の存在を知りそしてわからないわからないと言いながらなんとかHTTP2を実装
  5. 悦に浸っていたら今度はHTTP3があると言われそれの下ではQUICなるものが動いていると聞いてここまで来たらHTTP3を実装しようと思いたつ
  6. QUICの実装を始め,1年くらいかけてなんとかinitial connectionを確立させその後作っていって正常に通信ができるようにしさらにHTTP3まで実装した

動機はHTTP/1.1を作ったならHTTP/2も作りたいしHTTP2ができてHTTP3があるならそれも作りたいくらいの認識で特に意図があったわけではないです。あとそれらを書いていた時期は個人的に辛い時期だったので現実逃避のためにこれらに一生懸命になっていたという面もあります。

正直他の実装を見てみるとあまり筋が良くなかったかなぁと思う面もあります(特にメモリ管理周りが酷いなぁと思ってます) BoringSSL及びOpenSSLと統合したのですが統合過程でしばしば暗号化関連のロジックがバグるなどしておりそれを確認するためにBoringSSLなどのソースコードを見に行くなどしていたおかげでとりあえずソースコードを見ようという精神が身につきました。 技術的に自信があると言っていいのかわからないのですができる限り0コピーでパース等のロジックを設計しようとしていました。

当時ネットワークプロトコルをどう実装するかとかなんにも知らなかったのでRFCだけで実装するもんだと思い込んでRFCだけで途中まで頑張ってましたが流石に無理があったのでquic-goを参考に実装しました。 特に輻輳制御関連のアルゴリズムについてはほぼほぼquic-goのパクリです… https://www.rfc-editor.org/rfc/rfc8999.html https://www.rfc-editor.org/rfc/rfc9000.html https://www.rfc-editor.org/rfc/rfc9001.html https://www.rfc-editor.org/rfc/rfc9002.html https://datatracker.ietf.org/doc/html/rfc9221 https://github.com/quic-go/quic-go

他にも 一応DNSとかもclient側だけ実装してみたりしましたが結構拙いです。 https://github.com/on-keyday/utils/blob/main/src/include/fnet/dns/dns.h https://github.com/on-keyday/utils/blob/main/src/test/fnet/test_fnet_dns.cpp

一応Rustとかを書いたりもします https://github.com/on-keyday/brstack

低レイヤ的なものは少し触ったことがある程度です… https://github.com/on-keyday/utils/tree/main/src/lib/low/kernel https://github.com/on-keyday/utils/blob/main/src/include/jit/x64.h

その後SecHack365でQUICを実装していたときにネットワークプロトコルのバイナリフォーマットのシリアライザ/デシリアライザを書くのが大変すぎるということで バイナリエンコーダー/デコーダージェネレーターbrgenというものを作りました。 https://github.com/on-keyday/brgen 現在はジェネレーターを拡張をしようと頑張っています https://github.com/on-keyday/rebrgen

あとGo言語を主に使って自宅サーバーを作っていたりします(ただソースコードを公開していないので表に出ているのはブログ部分しかありません…) cloudflare tunnel経由で公開しています。 一応アクセス制御システム的なのとあとwebsocket上で動く隠しトンネル的なのとか自動再起動システムとかをGoとかで書いたりしたのですが残念ながら公開していないのでなんとも言えません 去年あたりについてはこれの開発を頑張りすぎたせいで去年のやつはあまり公開できるような成果物がない状況です… https://www.on-keyday.net/static/blog/

勉強のためにlinuxカーネルのネットワークスタックを読んだりはしましたが 最近はあまり低レイヤ的なものに触れていないなぁというのは感じています。

【問2:インターネットって何?】
#

以下時間がなくて各章完結していないものが多い。途中まで調べてどうにか頑張った結果である。特に行きについてはある程度調べられたものの帰りがけについての調査は不足している面が多い。また主要な流れを軽くなぞっただけであるため例えばキャッシュをどのように適用しているかとかどのようなヘッダを送っているかとかあたりにはあまり触れられていない。 又質問の意図として「まず通常の「ウェブサーバ」と手元の端末の間で、どのような処理・データのやりとりが、どのようなプロトコルで行われているのかを把握する」というのがあったためプロトコル部分等について重点的に探索等している結果ブラウザのレスポンス解析、DOM構築、JS、レンダリングプロセスと言ったところは後回しにされ結果時間が足りず触れられていない。またどのようなデータのやり取りかというのもあまり詳細に触れられていない部分があることをあらかじめ申し上げておく。

まず問「「ブラウザのURLバーに、 https://www.shonenjump.com/j/weeklyshonenjump/ を入力して決定キーを押した時になにが起こるか」を調べて、可能な限り詳しく説明してください。」に対する一番表層的な回答を「https://www.shonenjump.com/j/weeklyshonenjump/ の内容が表示される」だとしその裏で起こっていることについて深堀りしていく。

LLMはGoogle Gemini 2.5 Flashを適宜質問するなどの用途に使った。

  1. https://g.co/gemini/share/882b30e41c21
  2. https://g.co/gemini/share/19ce66d38f9d
  3. https://g.co/gemini/share/6fdeb5775f36
  4. https://g.co/gemini/share/da86b7f3f9b7
  5. https://g.co/gemini/share/3f468f2c30f9
  6. https://g.co/gemini/share/8d831a015c92
  7. https://g.co/gemini/share/f6104e624254
  8. https://g.co/gemini/share/0053cacc477e
  9. https://g.co/gemini/share/0c080baf3a7c
  10. https://g.co/gemini/share/26b398539a5d
  11. https://g.co/gemini/share/c179cc2ca524
  12. https://g.co/gemini/share/3f2c34623e99

0. 周辺調査
#

本題に入る前に自宅から宛先までどのような技術でつながっているかを確認する。

まずブラウザのDevToolsでみる限りレスポンスヘッダの表記よりApache httpdが動いていると推定される。 https://github.com/apache/httpd

content-type: text/html
date: Thu, 08 May 2025 10:48:03 GMT
server: Apache
x-frame-options: SAMEORIGIN

またprotocolフィールドの値からh2(HTTP/2 over TLS)プロトコルが動いていることが確認できる。 証明書情報からはLet’s Encryptの使用が確認できる。公開鍵は2025/06/03時点では4096ビットのRSA鍵、署名アルゴリズムはSHA-256 with RSAであった。 Wiresharkで確認したところTLS1.3を使用しているようであることがわかった。

Transport Layer Security
    TLSv1.3 Record Layer: Handshake Protocol: Client Hello
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 1819
        Handshake Protocol: Client Hello
            Handshake Type: Client Hello (1)
            Length: 1815
            Version: TLS 1.2 (0x0303)
                [Expert Info (Chat/Deprecated): This legacy_version field MUST be ignored. The supported_versions extension is present and MUST be used instead.]
            Random: 5f93f1e5ec50af0229e4cd2521f7a28ef4feed41b2ee6d0ddc326c0191e91417
            Session ID Length: 32
            Session ID: 9a212a8de0a2d77cb24a60c9c1f7fd425b30a0b151de06be0c8608df86a42bd9
            Cipher Suites Length: 32
            Cipher Suites (16 suites)
            Compression Methods Length: 1
            Compression Methods (1 method)
            Extensions Length: 1710
            Extension: Reserved (GREASE) (len=0)
            Extension: ec_point_formats (len=2)
            Extension: supported_groups (len=12)
            Extension: compress_certificate (len=3)
            Extension: status_request (len=5)
            Extension: server_name (len=23) name=www.shonenjump.com
            Extension: encrypted_client_hello (len=282)
            Extension: key_share (len=1263) Unknown (4588), x25519
            Extension: renegotiation_info (len=1)
            Extension: psk_key_exchange_modes (len=2)
            Extension: extended_master_secret (len=0)
            Extension: signature_algorithms (len=18)
            Extension: supported_versions (len=7) TLS 1.3, TLS 1.2
            Extension: session_ticket (len=0)
            Extension: signed_certificate_timestamp (len=0)
            Extension: Unknown type 17613 (len=5)
            Extension: application_layer_protocol_negotiation (len=14)
            Extension: Reserved (GREASE) (len=1)
            [JA4: t13d1516h2_8daaf6152771_d8a2da3f94cd]
            [JA4_r: t13d1516h2_002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0005,000a,000b,000d,0012,0017,001b,0023,002b,002d,0033,44cd,fe0d,ff01_0403,0804,0401,0503,0805,0501,0806,0601]
            [JA3 Fullstring: 771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,11-10-27-5-0-65037-51-65281-45-23-13-43-35-18-17613-16,4588-29-23-24,0]
            [JA3: 9170266a63037876ae8ab3c90f39f6a0]

また宛先ipアドレス202.218.223.232から取得されるwhois情報はImpress Holdingsとの情報が出てきたことから集英社からImpress HDへ何らかの委託が行われている可能性がある。 https://www.impressholdings.com/business/contents/ https://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%B3%E3%83%97%E3%83%AC%E3%82%B9

$ whois 202.218.223.232
[ JPNIC database provides information regarding IP address and ASN. Its use   ]
[ is restricted to network administration purposes. For further information,  ]
[ use 'whois -h whois.nic.ad.jp help'. To only display English output,        ]
[ add '/e' at the end of command, e.g. 'whois -h whois.nic.ad.jp xxx/e'.      ]

Network Information:
a. [Network Number]             202.218.223.128/25
b. [Network Name]               IMPRESSGROUP
g. [Organization]               Impress Holdings, Inc.
m. [Administrative Contact]     MY7594JP
n. [Technical Contact]          CK637JP
o. [Abuse]
p. [Nameserver]
[Assigned Date]                 2006/08/10
[Return Date]
[Last Update]                   2012/12/05 08:30:31(JST)

Less Specific Info.
----------
IDC Frontier Inc.
                     [Allocation]                 202.218.77.0-202.218.255.255
IDC Frontier Inc.
        SUBA-032-223 [Sub Allocation]                         202.218.223.0/24

More Specific Info.
----------
No match!!

なお確認のためimpress社のほうも確認してみたところ

$ whois impress.co.jp
[ JPRS database provides information on network administration. Its use is    ]
[ restricted to network administration purposes. For further information,     ]
[ use 'whois -h whois.jprs.jp help'. To suppress Japanese output, add'/e'     ]
[ at the end of command, e.g. 'whois -h whois.jprs.jp xxx/e'.                 ]
Domain Information:
a. [Domain Name]                IMPRESS.CO.JP
g. [Organization]               Impress Holdings, Inc.
l. [Organization Type]          Corporation
m. [Administrative Contact]     KA19179JP
n. [Technical Contact]          KA19179JP
p. [Name Server]                ns-468.awsdns-58.com
p. [Name Server]                ns-1109.awsdns-10.org
p. [Name Server]                ns-674.awsdns-20.net
p. [Name Server]                ns-1841.awsdns-38.co.uk
s. [Signing Key]
[State]                         Connected (2026/03/31)
[Registered Date]
[Connected Date]                2011/07/07
[Last Update]                   2025/04/01 01:02:07 (JST)
$ whois 202.218.128.207
[ JPNIC database provides information regarding IP address and ASN. Its use   ]
[ is restricted to network administration purposes. For further information,  ]
[ use 'whois -h whois.nic.ad.jp help'. To only display English output,        ]
[ add '/e' at the end of command, e.g. 'whois -h whois.nic.ad.jp xxx/e'.      ]

Network Information:
a. [Network Number]             202.218.128.128/25
b. [Network Name]               IMPRESSEXTRA
g. [Organization]               Impress Holdings, Inc.
m. [Administrative Contact]     MY7594JP
n. [Technical Contact]          CK637JP
o. [Abuse]
p. [Nameserver]
[Assigned Date]                 2006/08/24
[Return Date]
[Last Update]                   2012/12/05 08:30:30(JST)

Less Specific Info.
----------
IDC Frontier Inc.
                     [Allocation]                 202.218.77.0-202.218.255.255
IDC Frontier Inc.
        SUBA-032-128 [Sub Allocation]                         202.218.128.0/24

More Specific Info.
----------
No match!!

似たような情報が出てきていたため蓋然性は高いのではないかと推測する。 またIR情報も確認してみたところ https://www.impressholdings.com/pdf.php?irpeid=493 2025年現在でも集英社がインプレスHDの主要な顧客となっているため蓋然性は高いと思われる。 以上のように調べるなどした後最終的に以下のサイトを発見しImpressHDのグループ会社、株式会社ICEによって運用されていることが明確になった。 https://www.ice-inc.co.jp/solution/233/

なおRDAPでも確認してみたがこれ以上に詳細にはならなかったので割愛する。 https://jpnic.rdap.apnic.net/ip/202.218.223.232

次にドメイン名でwhoisをかける。

$ whois shonenjump.com
   Domain Name: SHONENJUMP.COM
   Registry Domain ID: 87648335_DOMAIN_COM-VRSN
   Registrar WHOIS Server: whois.jprs.jp
   Registrar URL: http://jprs.jp/registrar/
   Updated Date: 2025-02-28T07:31:22Z
   Creation Date: 2002-06-18T17:40:06Z
   Registry Expiry Date: 2027-03-02T04:59:59Z
   Registrar: Japan Registry Services Co., Ltd.
   Registrar IANA ID: 1485
   Registrar Abuse Contact Email: [email protected]
   Registrar Abuse Contact Phone: +81.352158457
   Domain Status: ok https://icann.org/epp#ok
   Name Server: NS-1230.AWSDNS-25.ORG
   Name Server: NS-1592.AWSDNS-07.CO.UK
   Name Server: NS-509.AWSDNS-63.COM
   Name Server: NS-816.AWSDNS-38.NET
   DNSSEC: unsigned
   URL of the ICANN Whois Inaccuracy Complaint Form: https://www.icann.org/wicf/
>>> Last update of whois database: 2025-05-08T11:00:59Z <<<

For more information on Whois status codes, please visit https://icann.org/epp

NOTICE: The expiration date displayed in this record is the date the
registrar's sponsorship of the domain name registration in the registry is
currently set to expire. This date does not necessarily reflect the expiration
date of the domain name registrant's agreement with the sponsoring
registrar.  Users may consult the sponsoring registrar's Whois database to
view the registrar's reported date of expiration for this registration.

TERMS OF USE: You are not authorized to access or query our Whois
database through the use of electronic processes that are high-volume and
automated except as reasonably necessary to register domain names or
modify existing registrations; the Data in VeriSign Global Registry
Services' ("VeriSign") Whois database is provided by VeriSign for
information purposes only, and to assist persons in obtaining information
about or related to a domain name registration record. VeriSign does not
guarantee its accuracy. By submitting a Whois query, you agree to abide
by the following terms of use: You agree that you may use this Data only
for lawful purposes and that under no circumstances will you use this Data
to: (1) allow, enable, or otherwise support the transmission of mass
unsolicited, commercial advertising or solicitations via e-mail, telephone,
or facsimile; or (2) enable high volume, automated, electronic processes
that apply to VeriSign (or its computer systems). The compilation,
repackaging, dissemination or other use of this Data is expressly
prohibited without the prior written consent of VeriSign. You agree not to
use electronic processes that are automated and high-volume to access or
query the Whois database except as reasonably necessary to register
domain names or modify existing registrations. VeriSign reserves the right
to restrict your access to the Whois database in its sole discretion to ensure
operational stability.  VeriSign may restrict or terminate your access to the
Whois database for failure to abide by these terms of use. VeriSign
reserves the right to modify these terms at any time.

The Registry database contains ONLY .COM, .NET, .EDU domains and
Registrars.
Domain Name: SHONENJUMP.COM
Registry Domain ID: 87648335_DOMAIN_COM-VRSN
Registrar WHOIS Server: whois.jprs.jp
Registrar URL: https://jprs.jp/registrar/
Updated Date: 2025-02-28T07:31:23Z
Creation Date: 2002-06-18T17:40:06Z
Registrar Registration Expiration Date: 2027-03-02T04:59:59Z
Registrar: Japan Registry Services Co.,Ltd.(JPRS)
Registrar IANA ID: 1485
Registrar Abuse Contact Email: [email protected]
Registrar Abuse Contact Phone: +81.352158457
Domain Status: ok  https://icann.org/epp#ok
Registry Registrant ID: Not Available From Registry
Registrant Name: SHUEISHA Inc.
Registrant Street: 2-5-10,Hitotsubashi
Registrant City: Chiyoda-ku
Registrant State/Province: Tokyo
Registrant Postal Code: 101-8050
Registrant Country: JP
Registrant Phone: +81.332306291
Registrant Fax: +81.332389256
Registrant Email: [email protected]
Registry Admin ID: Not Available From Registry
Admin Name: SUURI-KEIKAKU CO. LTD.
Admin Street: 2-4-6 Hitotsubashi
Admin City: Chiyoda-ku
Admin State/Province: Tokyo
Admin Postal Code: 101-0003
Admin Country: JP
Admin Phone: +81.352109611
Admin Fax: +81.332216336
Admin Email: [email protected]
Registry Tech ID: Not Available From Registry
Tech Name: Impress Professional Works, Inc.
Tech Street: 1-105 Kanda Jinbo-cho
Tech City: Chiyoda-ku
Tech State/Province: Tokyo
Tech Postal Code: 101-0051
Tech Country: JP
Tech Phone: +81.368375010
Tech Email: [email protected]
Name Server: NS-509.AWSDNS-63.COM
Name Server: NS-816.AWSDNS-38.NET
Name Server: NS-1592.AWSDNS-07.CO.UK
Name Server: NS-1230.AWSDNS-25.ORG
DNSSEC: unsigned
URL of the ICANN Whois Inaccuracy Complaint Form: https://www.icann.org/wicf/
>>> Last update of WHOIS database: 2025-05-08T11:01:15Z <<<

For more information on Whois status codes, please visit https://icann.org/epp

JPRS WHOIS LEGAL DISCLAIMER:

The WHOIS service (the "Service") offered by Japan Registry Services Co.,Ltd. (JPRS) and the access to the records in the JPRS WHOIS database (the "Database") are provided for information purposes and query-based public access.

Neither JPRS nor any of its officers, directors, employees or agents makes any warranty as to the results to be obtained from use of the Service. JPRS specifically disclaims all warranties of any kind, whether express, implied or statutory, including, but not limited to, any warranties of title, non-infringement, merchantability or fitness for a particular purpose.
In no event shall JPRS nor anyone else involved in creating, supporting, producing or delivering the Service be liable to you for any lost profits or costs, even if JPRS or such person has been advised of the possibility of such damages.

JPRS allows you to use the Service only for lawful purposes and that, under no circumstances shall you use the Service to: (a) allow, enable or otherwise support the transmission by e-mail, telephone, postal mail, facsimile or other means of mass unsolicited, commercial advertising or solicitations to entities other than the data recipient's own existing customers; or (b) enable high volume, automated, electronic processes that send queries or data to the systems of any registry operator or ICANN-accredited registrar, except as reasonably necessary to register domain names or modify existing registrations.

By submitting the query you agree to abide by these terms and further agree that JPRS may take measures to limit the use of the Service in order to protect the privacy of its registrants or the integrity of the Database.

JPRS reserves the right to modify or change these terms at any time without prior or subsequent notification of any kind.

For further information, use 'whois -h whois.jprs.jp help'. To express Japanese output, add '/j' at the end of command, e.g. 'whois -h whois.jprs.jp xxx/j'.

DNSにはAWSのもの(Route53)を使っていると推測される。これはImpress社 https://aws.amazon.com/jp/route53/ またレジストラはJPRSでありドメイン名の所有権は集英社にあるようである。

またAS番号を調べるために以下のツールを使用したところ https://develop.tools/ip-asn/

AS4694 (JP)との回答が帰ってきた。 自環境のASも調べたところ AS17676 (JP)との回答が帰ってきた。

peering dbを見て見たところ以下の情報が見つかった。 AS4694について https://www.peeringdb.com/net/4936 AS17676について https://www.peeringdb.com/net/2606

また我が家からtracerouteしてみたところ以下のようになった。

$ traceroute www.shonenjump.com
traceroute to www.shonenjump.com (202.218.223.232), 30 hops max, 60 byte packets
 1  _gateway (192.168.3.1)  1.193 ms  1.290 ms  1.390 ms
 2  softbank221110187118.bbtec.net (221.110.187.118)  118.194 ms  118.178 ms  118.165 ms
 3  softbank221110187117.bbtec.net (221.110.187.117)  118.153 ms  118.141 ms  118.127 ms
 4  10.0.7.46 (10.0.7.46)  120.626 ms  120.612 ms  120.599 ms
 5  101.203.70.90 (101.203.70.90)  120.632 ms  120.572 ms  120.603 ms
 6  * * *
 7  * * *
 8  * * *
 9  www.shonenjump.com (202.218.223.232)  21.598 ms  21.839 ms  21.797 ms

また* * *になる直前の101.203.70.90から推定して 以下のexchange pointに近いのではないかという推定をした。

BBIX Tokyo
17676
200G
RS PEER
BFD Support
101.203.90.81
2001:de8:c::1:7676:13
BBIX Tokyo
17676
200G
RS PEER
BFD Support
101.203.90.80
2001:de8:c::1:7676:12
BBIX Tokyo
17676
1T
RS PEER
BFD Support
101.203.88.26
2001:de8:c::1:7676:7
BBIX Tokyo
17676
1T
RS PEER
BFD Support
101.203.88.25
2001:de8:c::1:7676:6
BBIX Tokyo
17676
1T
RS PEER
BFD Support
101.203.88.16
2001:de8:c::1:7676:3
BBIX Tokyo
17676
1T
RS PEER
BFD Support
101.203.88.11
2001:de8:c::1:7676:2

また宛先側AS4694側にも以下の記述があったため

BBIX Tokyo
4694
300G
RS PEER
BFD Support
101.203.88.56
2001:de8:c::4694:1

以上の事実からBBIX Tokyoを通って接続されていると推定される。 https://ja.wikipedia.org/wiki/BBIX なお我が家のルーターはSoftbankBBでありIPv4 over IPv6のような形になっているためtraceourteのhop 1と2の間にIPv6のexit nodeがあると推測される。 (以下記事では https://datatracker.ietf.org/doc/html/rfc2473 だと推定されているが定かではない) https://scrapbox.io/rokoucha/IPv6%E9%AB%98%E9%80%9F%E3%83%8F%E3%82%A4%E3%83%96%E3%83%AA%E3%83%83%E3%83%89_IPv6_IPoE_+_IPv4_%E3%81%AF%E3%81%A9%E3%81%AE%E3%82%88%E3%81%86%E3%81%AB%E3%81%97%E3%81%A6_IPv4_over_IPv6_%E3%82%92%E5%AE%9F%E7%8F%BE%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E3%81%AE%E3%81%8B

また宛先側AS4694の情報からImpress HoldingsがIDC Frontier社の提供しているサービスを利用していると推定し 導入事例を検索したところ見つかった。(なお後から確認したらwhois情報の時点でIDC Frontier社の情報があったことに気づいた) https://www.idcf.jp/case/impressholdings/ また以下ページをたどることでおおよそここまでの推測が合っていることが推定される。 https://www.idcf.jp/datacenter/ https://www.idcf.jp/network/ix-connectivity.html

IDC FrontierのCDNサービスを利用している可能性についても検討してみたがそうであるならばfastlyを基盤に使っていると書いてあるとおり fastlyのipレンジになるはずであるがそうなってはいないため違うと思われる。 画像ファイル(JPEG)のExif情報が削除されていないなどがあるため少なくとも画像圧縮のようなサービスを使っている可能性も低いと見込まれる。 あるいはImage::ExifTool 10.8との表記があったためなにかしらオリジン側で圧縮をしている可能性はある。 https://www.idcf.jp/company/ https://www.idcf.jp/cloud/cache/?bid=lp_cdn-smb https://www.exiftool.org/ancient_history.html https://github.com/exiftool/exiftool/tree/10.80

ではこれらの推測情報等を踏まえて以下流れを追っていく。

1. デバイスドライバ/OS
#

私の環境がWindowsであるのでクライアント環境はWindowsと仮定する

まず物理的にEnterキーを入力されたあとデバイスドライバを経由してWindowsのWndProcに渡されるまでであるが あいにくwindowsのデバイスドライバについては詳しくなく、他の部分に多くの時間を取られたため推測しか言えない状態ではあるがおそらくDriverMainから始まるコードがありなんらか割り込みベクタのようなものを登録しそしてキーボードからの割り込みがあったらそれをWindows WDKにあるAPIを使って送りつけているのであろうとは思われる。

https://qiita.com/spc_canbe/items/a6653534088823e7eb7b#%E3%83%89%E3%83%A9%E3%82%A4%E3%83%90%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%81%AE%E3%83%93%E3%83%AB%E3%83%89 https://github.com/Microsoft/Windows-driver-samples

https://github.com/microsoft/Windows-driver-samples/blob/02b59d168de5812730fda9e0642302d1e3b25877/input/kbfiltr/sys/kbfiltr.c#L766

2. ブラウザプロセス
#

残念ながら自環境はブラウザのデバッグを快適にできるほどの性能はなくまた 金欠につきクラウド環境を借りるわけにもいかずさらには構築にかける時間もなかったので動かしてデバッグはできそうにはなかった。 そのためChromium Code Searchを使ってUI入力部分からSocket APIを使用した送信部分までをトレースした。動かしてデバッグをできたわけでもないため間違っているところがある可能性はある。

LICENSE: https://github.com/chromium/chromium/blob/main/LICENSE Copyright 2015 The Chromium Authors

要約
#

非常に長いため呼び出し階層の要約を書く。完全なコールスタックではない。 まずUI部分でWinodwsのWndProcから入って変換がされていき最終的にOmniboxViewViewsに流れ着き

OmniboxViewViews::HandleKeyEvent 
-> OmniboxEditModel::OpenSelection 
-> OmniboxEditModel::AcceptInput 
-> OmniboxAction::Execute 
-> OmniboxAction::OpenURL 
-> ChromeOmniboxClient::OnAutocompleteAccept 
-> chrome::OpenCurrentURL 
-> chrome::Navigate 
-> chrome::LoadURLInContents 
-> NavigationControllerImpl::LoadURLWithParams 
-> NavigationControllerImpl::NavigateWithoutEntry 
   -> NavigationControllerImpl::CreateNavigationRequestFromLoadParams
-> Navigator::Navigate
  -> FrameTreeNode::TakeNavigationRequest
-> NavigationRequest::BeginNavigation
-> NavigationRequest::BeginNavigationImpl
-> NavigationRequest::WillStartRequest
-> NavigationThrottleRunner::ProcessNavigationEvent
-> NavigationThrottleRunner::ProcessInternal
-> NavigationThrottleRunner::InformRegistry 
-> NavigationThrottleRegistryImpl::OnEventProcessed
-> NavigationRequest::OnNavigationEventProcessed
-> NavigationRequest::OnWillStartRequestProcessed
-> NavigationRequest::OnStartChecksComplete
-> NavigationURLLoaderImpl::Start
-> NavigationURLLoaderImpl::Restart
-> NavigationURLLoaderImpl::MaybeStartLoader
-> NavigationURLLoaderImpl::StartNonInterceptedRequest
-> NavigationURLLoaderImpl::CreateThrottlingLoaderAndStart
-> blink::ThrottlingURLLoader::CreateLoaderAndStart
-> ThrottlingURLLoader::Start
-> ThrottlingURLLoader::StartNow
(IPC経由と推定される)
-> PrefetchMatchingURLLoaderFactory::CreateLoaderAndStart
-> CorsURLLoaderFactory::CreateLoaderAndStart
-> CorsURLLoader::Start
-> CorsURLLoader::StartRequest
-> CorsURLLoader::StartNetworkRequest
-> URLLoaderFactory::CreateLoaderAndStart
-> URLLoaderFactory::CreateLoaderAndStartWithSyncClient
-> URLLoader::ProcessOutboundTrustTokenInterceptor
(ここ非同期.???)
-> URLLoader::OnDoneBeginningTrustTokenOperation
-> URLLoader::ProcessOutboundSharedStorageInterceptor
-> URLLoader::ScheduleStart
-> URLRequest::Start
-> URLRequest::StartJob
-> URLRequestHttpJob::Start
-> URLRequestHttpJob::OnGotFirstPartySetMetadata
-> URLRequestHttpJob::StartTransaction
-> URLRequestHttpJob::StartTransactionInternal
  -> HttpNetworkTransaction::Start
  -> HttpNetworkTransaction::DoLoop 
    -> HttpNetworkTransaction::DoCreateStream
      -> HttpStreamFactory::RequestStream
      -> HttpStreamFactory::RequestStreamInternal
      -> HttpStreamFactory::JobController::Start
      -> HttpStreamFactory::JobController::RunLoop
      -> HttpStreamFactory::JobController::DoLoop
      -> HttpStreamFactory::JobController::DoCreateJobs
      -> HttpStreamFactory::Job::Start
      -> HttpStreamFactory::Job::StartInternal
      -> HttpStreamFactory::Job::RunLoop
      -> HttpStreamFactory::Job::DoLoop
        -> HttpStreamFactory::Job::DoInitConnection
          -> HttpStreamFactory::Job::DoInitConnectionImpl
          -> InitSocketHandleForHttpRequest
          -> InitSocketPoolHelper
          -> ClientSocketHandle::Init
          -> TransportClientSocketPool::RequestSocket
          -> TransportClientSocketPool::RequestSocketInternal
          -> ConnectJob::Connect
          -> SSLConnectJob::ConnectInternal
          -> SSLConnectJob::DoLoop
            -> SSLConnectJob::DoTransportConnect
              -> TransportConnectJob::ConnectInternal
              -> TransportConnectJob::DoLoop
                -> TransportConnectJob::DoResolveHost
                  -> HostResolverManager::RequestImpl::Start
                  -> HostResolverManager::RequestImpl::DoLoop
                    -> HostResolverManager::RequestImpl::DoStartJob
                      -> HostResolverManager::CreateAndStartJob
                      -> HostResolverManager::Job::RunNextTask
                        -> HostResolverManager::Job::StartSystemTask
                          -> HostResolverSystemTask::Start
                          -> HostResolverSystemTask::StartLookupAttempt
                          -> PostSystemDnsResolutionTaskAndReply
                          -> ResolveOnWorkerThread
                          -> SystemHostResolverCall
                          -> AddressInfo::Get
                          -> AddrInfoGetter::getaddrinfo
                          -> ::getaddrinfo (Windows API)
                        -> HostResolverManager::Job::StartDnsTask
                          -> HostResolverDnsTask::StartNextTransaction
                          -> HostResolverDnsTask::CreateAndStartTransaction
                          -> DnsTransactionImpl::Start
                          -> DnsTransactionImpl::PrepareSearch
                          -> DnsTransactionImpl::StartQuery
                          -> DnsTransactionImpl::MakeAttempt
                          -> DnsTransactionImpl::MakeUdpAttempt
                          -> DnsUDPAttempt::Start
                          -> DnsUDPAttempt::DoLoop
                            -> DnsUDPAttempt::DoSendQuery
                              -> UDPClientSocket::Write
                              -> UDPSocketWin::Write
                              -> UDPSocketWin::SendToOrWrite
                              -> InternalSendToOverlapped
                              -> WSASendTo (Windows API)
                              (IOCPで返ってきて)
                              -> DnsUDPAttempt::OnIOComplete -> DnsUDPAttempt::DoLoop
                            -> DnsUDPAttempt::DoReadResponse
                              -> UDPClientSocket::Read
                              -> UDPSocketWin::Read
                              -> UDPSocketWin::RecvFrom
                              -> InternalRecvFromOverlapped
                              -> WSARecvFrom (Windows API)
                -> TransportConnectJob::DoTransportConnect
                  -> TransportConnectSubJob::Start
                    -> TransportConnectSubJob::DoLoop
                      -> TransportConnectSubJob::DoEndpointLockComplete
                      -> TCPClientSocket::Connect
                      -> TCPClientSocket::DoConnectLoop
                      -> TCPClientSocket::DoConnect
                      -> TCPClientSocket::ConnectInternal
                      -> TCPSocketWin::Connect
                      -> TCPSocketWin::DoConnect
                      -> connect (Windows API)
            -> SSLConnectJob::DoSSLConnect
              -> SSLClientSocketImpl::Connect
              -> SSLClientSocketImpl::Init
              -> SSLClientSocketImpl::DoHandshakeLoop
                -> SSL_do_handshake
                -> ssl_run_handshake
                  -> ssl_handle_open_record
                  -> ssl_read_buffer_extended_to
                  -> tls_read_buffer_extended_to
                  -> BIO_read
                    -> SocketBIOAdapter::BIOReadWrapper
                    -> SocketBIOAdapter::BIORead
                    -> TCPClientSocket::ReadIfReady
                    -> TCPClientSocket::ReadCommon
                    -> TcpSocketIoCompletionPortWin::ReadIfReady
                    -> TcpSocketIoCompletionPortWin::HandleReadRequest
                    -> WSARecv (Windows API)
                  -> ssl_client_handshake
                  -> do_start_connect
                    -> ssl_add_client_hello
                  -> do_read_server_hello
                    -> tls_get_message
                     -> parse_message
                  -> do_tls13
                    -> do_read_server_hello (TLS1.3)
                    -> do_read_encrypted_extensions
                    -> do_read_server_certificate
                    -> do_read_server_certificate_verify
                    -> do_read_server_finished
                -> tls_flush
                -> BIO_write
                  -> SocketBIOAdapter::BIOWriteWrapper
                  -> SocketBIOAdapter::BIOWrite
                  -> SocketBIOAdapter::SocketWrite
                  -> TCPClientSocket::Write
                  -> TcpSocketIoCompletionPortWin::Write
                  -> WSASend (Windows API)
        -> HttpStreamFactory::Job::DoInitConnectionComplete
        -> HttpStreamFactory::Job::DoCreateStream
          -> SpdySessionPool::CreateAvailableSessionFromSocketHandle (Spdyだけど中身がHTTP/2という紛らわしい(昔の名称そのまま使ってる...))
            -> SpdySession::InitializeWithSocketHandle
            -> SpdySession::InitializeInternal
            -> SpdySession::SendInitialData
            -> SpdySession::EnqueueSessionWrite
            -> SpdySession::EnqueueWrite
            -> SpdySession::MaybePostWriteLoop
            (非同期)
            -> SpdySession::PumpWriteLoop
            -> SpdySession::DoWriteLoop
            -> SpdySession::DoWrite
          -> HttpStreamFactory::Job::SetSpdyHttpStreamOrBidirectionalStreamImpl
    -> HttpNetworkTransaction::DoBuildRequest
    -> HttpNetworkTransaction::DoSendRequest
      -> SpdyHttpStream::SendRequest
        -> CreateSpdyHeadersFromHttpRequest
      -> SpdyStream::SendRequestHeaders
      (EnqueueStreamWriteを経由してEnqueueWriteに行った後SpdySession::DoWrite内で)
      -> SpdyStream::HeadersBufferProducer::ProduceBuffer
      -> SpdyStream::ProduceHeadersFrame
      -> SpdySession::CreateHeaders
      -> BufferedSpdyFramer::SerializeFrame
      -> SpdyFramer::SerializeFrame
      -> SpdyHeadersIR::Visit
      -> FrameSerializationVisitor::VisitHeaders
      -> SpdyFramer::SerializeHeaders
        -> SpdyFramer::SerializeHeadersBuilderHelper
          -> HpackEncoder::EncodeHeaderBlock (HPACK)
    -> HttpNetworkTransaction::DoReadHeaders
      (SpdySession::InitializeInternalで非同期でタスクとして開始されていたPumpReadLoopにて)
      SpdySession::PumpReadLoop
      -> SpdySession::DoReadLoop
        -> SpdySession::DoRead (TcpClientSocket::ReadIfReadyへ)
        -> SpdySession::DoReadComplete
        -> BufferedSpdyFramer::ProcessInput
          -> Http2DecoderAdapter::ProcessInput
          -> Http2FrameDecoder::DecodeFrame
          -> Http2FrameDecoder::StartDecodingPayload
            -> HeadersPayloadDecoder::StartDecodingPayload
              -> HeadersPayloadDecoder::ResumeDecodingPayload
              -> Http2DecoderAdapter::OnHpackFragment
              -> HpackDecoderAdapter::HandleControlFrameHeadersData
              -> HpackDecoder::DecodeFragment
              -> HpackBlockDecoder::Decode
                -> HpackEntryDecoder::Start
                -> HpackEntryDecoder::Resume
                  -> HpackEntryDecoder::DispatchOnType
                  -> HpackDecoderState::OnIndexedHeader (例示としてindexのパターン)
                  -> HpackDecoderAdapter::ListenerAdapter::OnHeader
                  -> HeaderCoalescer::OnHeader
                  -> HeaderCoalescer::AddHeader
            -> Http2DecoderAdapter::OnHeadersEnd
            -> Http2DecoderAdapter::CommonHpackFragmentEnd
            -> BufferedSpdyFramer::OnHeaderFrameEnd
            -> SpdySession::OnHeaders
            -> SpdyStream::OnHeadersReceived
            -> SpdyStream::SaveResponseHeaders
            -> SpdyHttpStream::OnHeadersReceived
            -> SpdyHttpStream::DoResponseCallback 
            -> HttpNetworkTransaction::OnIOComplete (HttpNetworkTransaction::DoLoopに戻る)
    -> HttpNetworkTransaction::DoReadBody
    -> HttpNetworkTransaction::DoReadBodyComplete
    -> HttpNetworkTransaction::DoCallback
(HttpNetworkTransaction::DoCallback経由)
-> URLRequestHttpJob::OnStartCompleted
-> URLRequestHttpJob::SaveCookiesAndNotifyHeadersComplete
-> URLRequestHttpJob::NotifyHeadersComplete
-> URLRequestJob::NotifyHeadersComplete
-> URLRequestJob::NotifyFinalHeadersReceived
-> URLRequest::NotifyResponseStarted
-> URLRequest::NotifyRequestCompleted
(ここで制限時間切れ)

探査記録
#

WinMain https://source.chromium.org/chromium/chromium/src/+/main:chrome/app/chrome_exe_main_win.cc;l=200;bpv=1;bpt=1?q=main&ss=chromium%2Fchromium%2Fsrc:chrome%2Fapp%2F chrome.dllのChromeMainに入る。 https://source.chromium.org/chromium/chromium/src/+/main:chrome/app/chrome_main.cc;l=107;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 ContentMain https://source.chromium.org/chromium/chromium/src/+/main:content/app/content_main.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;l=369 ContentMainRunnerImpl::Run https://source.chromium.org/chromium/chromium/src/+/main:content/app/content_main_runner_impl.cc;l=1135;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 ContentMainRunnerImpl::RunBrowser https://source.chromium.org/chromium/chromium/src/+/main:content/app/content_main_runner_impl.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1;l=1202 RunBrowserProcessMain https://source.chromium.org/chromium/chromium/src/+/main:content/app/content_main_runner_impl.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1;l=723 BrowserMain https://source.chromium.org/chromium/chromium/src/+/main:content/browser/browser_main.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1;l=19 BrowserMainRunnerImpl::Run https://source.chromium.org/chromium/chromium/src/+/main:content/browser/browser_main_runner_impl.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1;l=153 BrowserMainLoop::RunMainMessageLoop https://source.chromium.org/chromium/chromium/src/+/main:content/browser/browser_main_loop.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1;l=1076 RunLoop::Run https://source.chromium.org/chromium/chromium/src/+/main:base/run_loop.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1;l=105 ThreadControllerWithMessagePumpImpl::Run https://source.chromium.org/chromium/chromium/src/+/main:base/task/sequence_manager/thread_controller_with_message_pump_impl.cc;l=594;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 MessagePumpWin::Run https://source.chromium.org/chromium/chromium/src/+/main:base/message_loop/message_pump_win.cc;l=78;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 MessagePumpForUI::DoRunLoop https://source.chromium.org/chromium/chromium/src/+/main:base/message_loop/message_pump_win.cc;l=218;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 MessagePumpForUI::ProcessNextWindowsMessage https://source.chromium.org/chromium/chromium/src/+/main:base/message_loop/message_pump_win.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1;l=534

ここで::PeekMessageに到達 https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-peekmessagea https://source.chromium.org/chromium/chromium/src/+/main:base/message_loop/message_pump_win.cc;l=586;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

そしてここで::DispatchMessageされている。 https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-dispatchmessage https://source.chromium.org/chromium/chromium/src/+/main:base/message_loop/message_pump_win.cc;l=628;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

キー入力をするとDispatchMessageからやってきてまずWndProcにWM_KEYDOWN入る。 https://source.chromium.org/chromium/chromium/src/+/main:ui/gfx/win/window_impl.cc;l=290;drc=2e87e014afa5464f87909bfa795ab10834459ed7;bpv=0;bpt=1 OnWndProc https://source.chromium.org/chromium/chromium/src/+/main:ui/gfx/win/window_impl.cc;l=277;drc=2e87e014afa5464f87909bfa795ab10834459ed7;bpv=0;bpt=1 おそらくここで適宜されたWM_KEYDOWNに対応するOnKeyEventに入る https://source.chromium.org/chromium/chromium/src/+/main:ui/platform_window/win/win_window.h;l=80;drc=2e87e014afa5464f87909bfa795ab10834459ed7 KeyEventに変換されDispatchEventされる。 https://source.chromium.org/chromium/chromium/src/+/main:ui/platform_window/win/win_window.cc;l=270;drc=2e87e014afa5464f87909bfa795ab10834459ed7 ここからauraのコードに入る WindowTreeHostPlatformの実装(ui::PlatformWindowDelegateの実装,WindowTreeHostの実装,後で重要になるui::EventSourceの実装) https://source.chromium.org/chromium/chromium/src/+/main:ui/aura/window_tree_host_platform.h;l=28;drc=2e87e014afa5464f87909bfa795ab10834459ed7 WindowTreeHostPlatform::DispatchEventでSendEventToSinkが呼ばれる https://source.chromium.org/chromium/chromium/src/+/main:ui/aura/window_tree_host_platform.cc;l=295;drc=2e87e014afa5464f87909bfa795ab10834459ed7 EventSourece::SendEventToSink https://source.chromium.org/chromium/chromium/src/+/main:ui/events/event_source.cc;drc=2e87e014afa5464f87909bfa795ab10834459ed7;l=112 最終的にEventSkin::OnEventFromSource

EventDispatchDetails EventSource::DeliverEventToSink(Event* event) {
  EventSink* sink = GetEventSink();
  CHECK(sink);
  return sink->OnEventFromSource(event);
}

EventSinkはここからくると思われる https://source.chromium.org/chromium/chromium/src/+/main:ui/views/widget/widget.cc;l=2328;drc=2e87e014afa5464f87909bfa795ab10834459ed7

ui::EventSink* Widget::GetEventSink() {
  return root_view_.get();
}

root_view_の実装はinternal::RootViewでそれから派生したBrowserRootView https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/frame/browser_root_view.h;l=30;drc=2e87e014afa5464f87909bfa795ab10834459ed7;bpv=1;bpt=1 RootViewはViewから派生。Viewはui::EventProcessorから派生。EventProcessorがEventSinkから派生している。 EventProcessorで実際の処理のようだ。 https://source.chromium.org/chromium/chromium/src/+/main:ui/events/event_processor.cc;l=16;drc=2e87e014afa5464f87909bfa795ab10834459ed7 EventDispatcherDelegate::DispatchEvent https://source.chromium.org/chromium/chromium/src/+/main:ui/events/event_dispatcher.cc;drc=2e87e014afa5464f87909bfa795ab10834459ed7;l=78 EventDispatcherDelegate::DispatchEventToTarget https://source.chromium.org/chromium/chromium/src/+/main:ui/events/event_dispatcher.cc;l=78;drc=2e87e014afa5464f87909bfa795ab10834459ed7 void EventDispatcher::ProcessEvent(EventTarget* target, Event* event) https://source.chromium.org/chromium/chromium/src/+/main:ui/events/event_dispatcher.cc;drc=2e87e014afa5464f87909bfa795ab10834459ed7;l=113 void EventDispatcher::DispatchEvent(EventHandler* handler, Event* event) https://source.chromium.org/chromium/chromium/src/+/main:ui/events/event_dispatcher.cc;drc=2e87e014afa5464f87909bfa795ab10834459ed7;l=180 void EventHandler::OnEvent(Event* event) https://source.chromium.org/chromium/chromium/src/+/main:ui/events/event_handler.cc;drc=2e87e014afa5464f87909bfa795ab10834459ed7;l=26

OmniboxViewViewsはview::TextFieldを継承しており https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/omnibox/omnibox_view_views.h;l=60?q=OmniboxViewViews&ss=chromium%2Fchromium%2Fsrc view::TextFieldはview::Viewを継承しており https://source.chromium.org/chromium/chromium/src/+/main:ui/views/controls/textfield/textfield.h;l=72;drc=2e87e014afa5464f87909bfa795ab10834459ed7 view::Viewはui::EventHandlerを継承している。 https://source.chromium.org/chromium/chromium/src/+/main:ui/views/view.h;drc=2e87e014afa5464f87909bfa795ab10834459ed7;l=312 void View::OnKeyEvent(ui::KeyEvent* event)でOnKeyPressedまたはOnKeyReleasedに変換されている https://source.chromium.org/chromium/chromium/src/+/main:ui/views/view.cc;l=1640;drc=2e87e014afa5464f87909bfa795ab10834459ed7

まずbool Textfield::OnKeyPressed(const ui::KeyEvent& event)に入る https://source.chromium.org/chromium/chromium/src/+/main:ui/views/controls/textfield/textfield.cc;l=788;drc=467a8e68f685f9cfa47ee3fbfca20c22f7f6e893;bpv=1;bpt=1

通常のキー入力の場合はExecuteTextEditCommandに入る。 void OmniboxViewViews::ExecuteTextEditCommand(ui::TextEditCommand command) https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/omnibox/omnibox_view_views.cc;l=1585;drc=2e87e014afa5464f87909bfa795ab10834459ed7 void Textfield::ExecuteTextEditCommand(ui::TextEditCommand command) に処理が移譲 https://source.chromium.org/chromium/chromium/src/+/main:ui/views/controls/textfield/textfield.cc;l=2191;drc=467a8e68f685f9cfa47ee3fbfca20c22f7f6e893

EnterKeyの場合はoverrideされた bool OmniboxViewViews::HandleKeyEvent(views::Textfield* textfield,const ui::KeyEvent& event) でハンドリングされる。 https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/omnibox/omnibox_view_views.cc;l=1658;drc=2e87e014afa5464f87909bfa795ab10834459ed7

    case ui::VKEY_RETURN: {
      WindowOpenDisposition disposition = WindowOpenDisposition::CURRENT_TAB;
      if ((alt && !shift) || (shift && command)) {
        disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
      } else if (alt || command) {
        disposition = WindowOpenDisposition::NEW_BACKGROUND_TAB;
      } else if (shift) {
        disposition = WindowOpenDisposition::NEW_WINDOW;
      }
      // According to unit tests and comments, holding control when pressing
      // enter has special behavior handled by `AcceptInput` so in this case
      // the user is selecting their input (possibly with modification like
      // appending ".com") and not the row match. This is indicated with an
      // explicit `kNoMatch` line selection.
      if (model()->PopupIsOpen() && !control) {
        model()->OpenSelection(model()->GetPopupSelection(), event.time_stamp(),
                               disposition);
      } else {
        model()->OpenSelection(
            OmniboxPopupSelection(OmniboxPopupSelection::kNoMatch),
            event.time_stamp(), disposition);
      }
      return true;
    }

ここでOpenSelectionが呼ばれる。 https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/omnibox/omnibox_view_views.cc;l=1687;drc=2e87e014afa5464f87909bfa795ab10834459ed7

void OmniboxEditModel::OpenSelection(OmniboxPopupSelection selection, base::TimeTicks timestamp, WindowOpenDisposition disposition)内でAcceptInput呼び出し https://source.chromium.org/chromium/chromium/src/+/main:components/omnibox/browser/omnibox_edit_model.cc;l=937;drc=2e87e014afa5464f87909bfa795ab10834459ed7 void OmniboxEditModel::AcceptInput(WindowOpenDisposition disposition, base::TimeTicks match_selection_timestamp) https://source.chromium.org/chromium/chromium/src/+/main:components/omnibox/browser/omnibox_edit_model.cc;l=2506;drc=2e87e014afa5464f87909bfa795ab10834459ed7;bpv=1;bpt=1?q=AcceptInput&sq=&ss=chromium%2Fchromium%2Fsrc omniboxのisURLの判定 https://source.chromium.org/chromium/chromium/src/+/main:components/omnibox/browser/omnibox_edit_model.cc;drc=2e87e014afa5464f87909bfa795ab10834459ed7;bpv=0;l=571 2. https://source.chromium.org/chromium/chromium/src/+/main:components/omnibox/browser/autocomplete_match_type.h;drc=2e87e014afa5464f87909bfa795ab10834459ed7;bpv=0;l=12

void OmniboxEditModel::OpenMatch(OmniboxPopupSelection selection,
                                 AutocompleteMatch match,
                                 WindowOpenDisposition disposition,
                                 const GURL& alternate_nav_url,
                                 const std::u16string& pasted_text,
                                 base::TimeTicks match_selection_timestamp) {
  // If the user is executing an action, this will be non-null and some match
  // opening and metrics behavior will be adjusted accordingly.
  OmniboxAction* action = nullptr;
  if (selection.state == OmniboxPopupSelection::NORMAL &&
      match.takeover_action) {
    DCHECK(match_selection_timestamp != base::TimeTicks());
    action = match.takeover_action.get();
  } else if (selection.IsAction()) {
    DCHECK_LT(selection.action_index, match.actions.size());
    action = match.actions[selection.action_index].get();
  }

  // Invalid URLs such as chrome://history can end up here, but that's okay
  // if the user is executing an action instead of navigating to the URL.
  if (!match.destination_url.is_valid() && !action) {
    return;
  }

  // NULL_RESULT_MESSAGE matches are informational only and cannot be acted
  // upon. Immediately return when attempting to open one.
  if (match.type == AutocompleteMatchType::NULL_RESULT_MESSAGE) {
    return;
  }

  // Switch the window disposition to SWITCH_TO_TAB for open tab matches that
  // originated while in keyword mode.  This is to support the keyword mode
  // starter pack's tab search (@tabs) feature, which should open all
  // suggestions in the existing open tab.
  bool is_open_tab_match =
      match.from_keyword &&
      match.provider->type() == AutocompleteProvider::TYPE_OPEN_TAB;
  // Also switch the window disposition for tab switch actions. The action
  // itself will already open with SWITCH_TO_TAB disposition, but the change
  // is needed earlier for metrics.
  bool is_tab_switch_action =
      action && action->ActionId() == OmniboxActionId::TAB_SWITCH;
  if (is_open_tab_match || is_tab_switch_action) {
    disposition = WindowOpenDisposition::SWITCH_TO_TAB;
  }

  TRACE_EVENT("omnibox", "OmniboxEditModel::OpenMatch", "match", match,
              "disposition", disposition, "altenate_nav_url", alternate_nav_url,
              "pasted_text", pasted_text);
  const base::TimeTicks& now(base::TimeTicks::Now());
  base::TimeDelta elapsed_time_since_user_first_modified_omnibox(
      now - time_user_first_modified_omnibox_);
  autocomplete_controller()
      ->UpdateMatchDestinationURLWithAdditionalSearchboxStats(
          elapsed_time_since_user_first_modified_omnibox, &match);

  GURL destination_url = action ? action->getUrl() : match.destination_url;

  // Save the result of the interaction, but do not record the histogram yet.
  focus_resulted_in_navigation_ = true;

  RecordActionShownForAllActions(autocomplete_controller()->result(),
                                 selection);
  HistoryFuzzyProvider::RecordOpenMatchMetrics(
      autocomplete_controller()->result(), match);

  std::u16string input_text(pasted_text);
  if (input_text.empty()) {
    input_text = user_input_in_progress_ ? user_text_ : url_for_editing_;
  }
  // Create a dummy AutocompleteInput for use in calling VerbatimMatchForInput()
  // to create an alternate navigational match.
  AutocompleteInput alternate_input(
      input_text, GetPageClassification(),
      controller_->client()->GetSchemeClassifier(),
      controller_->client()->ShouldDefaultTypedNavigationsToHttps(), 0, false);
  // Somehow we can occasionally get here with no active tab.  It's not
  // clear why this happens.
  alternate_input.set_current_url(controller_->client()->GetURL());
  alternate_input.set_current_title(controller_->client()->GetTitle());

  base::TimeDelta elapsed_time_since_last_change_to_default_match(
      now - autocomplete_controller()->last_time_default_match_changed());
  DCHECK(match.provider);
  // These elapsed times don't really make sense for matches that come from
  // omnibox focus (because the user did not modify the omnibox), so for those
  // we set the elapsed times to something that will be ignored by
  // metrics_log.cc.  They also don't necessarily make sense if the omnibox
  // dropdown is closed or the user used paste-and-go.  (In most
  // cases when this happens, the user never modified the omnibox.)
  const bool popup_open = PopupIsOpen();
  const base::TimeDelta default_time_delta = base::Milliseconds(-1);
  if (input_.IsZeroSuggest() || !pasted_text.empty()) {
    elapsed_time_since_user_first_modified_omnibox = default_time_delta;
    elapsed_time_since_last_change_to_default_match = default_time_delta;
  }

  base::TimeDelta elapsed_time_since_user_focused_omnibox = default_time_delta;
  if (!last_omnibox_focus_.is_null()) {
    elapsed_time_since_user_focused_omnibox = now - last_omnibox_focus_;
    // Only record focus to open time when a focus actually happened (as
    // opposed to, say, dragging a link onto the omnibox).
    LogFocusToOpenTime(elapsed_time_since_user_focused_omnibox,
                       input_.IsZeroSuggest(), GetPageClassification(), match,
                       selection.IsAction() ? selection.action_index : -1);
  }

  // In some unusual cases, we ignore autocomplete_controller()->result() and
  // instead log a fake result set with a single element (|match|) and
  // selected_index of 0. For these cases:
  //  1. If the popup is closed (there is no result set). This doesn't apply
  //  for WebUI searchboxes since they don't have an associated popup.
  //  2. If the index is out of bounds. This should only happen if
  //  |selection.line| is
  //     kNoMatch, which can happen if the default search provider is disabled.
  //  3. If this is paste-and-go (meaning the contents of the dropdown
  //     are ignored regardless).
  const bool dropdown_ignored =
      (!popup_open && !omnibox::IsWebUISearchbox(GetPageClassification())) ||
      selection.line >= autocomplete_controller()->result().size() ||
      !pasted_text.empty();
  ACMatches fake_single_entry_matches;
  fake_single_entry_matches.push_back(match);
  AutocompleteResult fake_single_entry_result;
  fake_single_entry_result.AppendMatches(fake_single_entry_matches);

  std::u16string user_text =
      input_.IsZeroSuggest() ? std::u16string() : input_text;
  size_t completed_length = match.allowed_to_be_default_match
                                ? match.inline_autocompletion.length()
                                : std::u16string::npos;
  bool is_incognito = autocomplete_controller()
                          ->autocomplete_provider_client()
                          ->IsOffTheRecord();
  OmniboxLog log(
      user_text, just_deleted_text_, input_.type(), is_keyword_selected(),
      keyword_mode_entry_method_, popup_open,
      dropdown_ignored ? OmniboxPopupSelection(0) : selection, disposition,
      !pasted_text.empty(),
      SessionID::InvalidValue(),  // don't know tab ID; set later if appropriate
      GetPageClassification(), elapsed_time_since_user_first_modified_omnibox,
      completed_length, elapsed_time_since_last_change_to_default_match,
      dropdown_ignored ? fake_single_entry_result
                       : autocomplete_controller()->result(),
      destination_url, is_incognito,
      match.zero_prefix_suggestions_shown_in_session,
      match.zero_prefix_search_suggestions_shown_in_session,
      match.zero_prefix_url_suggestions_shown_in_session,
      match.typed_search_suggestions_shown_in_session,
      match.typed_url_suggestions_shown_in_session);
// Check disabled on iOS as the platform shows a default suggestion on focus
// (crbug.com/40061502).
#if !BUILDFLAG(IS_IOS)
  DCHECK(dropdown_ignored ||
         (log.elapsed_time_since_user_first_modified_omnibox >=
          log.elapsed_time_since_last_change_to_default_match))
      << "We should've got the notification that the user modified the "
      << "omnibox text at same time or before the most recent time the "
      << "default match changed.";
#endif
  log.elapsed_time_since_user_focused_omnibox =
      elapsed_time_since_user_focused_omnibox;
  log.ukm_source_id = controller_->client()->GetUKMSourceId();

  if ((disposition == WindowOpenDisposition::CURRENT_TAB) &&
      controller_->client()->CurrentPageExists()) {
    // If we know the destination is being opened in the current tab,
    // we can easily get the tab ID.  (If it's being opened in a new
    // tab, we don't know the tab ID yet.)
    log.tab_id = controller_->client()->GetSessionID();
  }
  autocomplete_controller()->AddProviderAndTriggeringLogs(&log);

  base::UmaHistogramEnumeration("Omnibox.SuggestionUsed.RichAutocompletion",
                                match.rich_autocompletion_triggered);
  size_t ipv4_parts_count =
      CountNumberOfIPv4Parts(user_text, destination_url, completed_length);
  // The histogram is collected to decide if shortened IPv4 addresses
  // like 127.1 should be deprecated.
  // Only valid IP addresses manually inputted by the user will be counted.
  if (ipv4_parts_count > 0) {
    base::UmaHistogramCounts100("Omnibox.IPv4AddressPartsCount",
                                ipv4_parts_count);
  }

  controller_->client()->OnURLOpenedFromOmnibox(&log);
  OmniboxEventGlobalTracker::GetInstance()->OnURLOpened(&log);

  LOCAL_HISTOGRAM_BOOLEAN("Omnibox.EventCount", true);
  omnibox::answer_data_parser::LogAnswerUsed(match.answer_type);

  TemplateURLService* service = controller_->client()->GetTemplateURLService();
  TemplateURL* template_url = match.GetTemplateURL(service, false);
  if (template_url) {
    // |match| is a Search navigation or a URL navigation in keyword mode; log
    // search engine usage metrics.
    AutocompleteMatch::LogSearchEngineUsed(match, service);

    if (ui::PageTransitionTypeIncludingQualifiersIs(
            match.transition, ui::PAGE_TRANSITION_KEYWORD) ||
        match.provider->type() ==
            AutocompleteProvider::TYPE_UNSCOPED_EXTENSION) {
      // User is in keyword mode or accepted an unscoped extension suggestion,
      // increment usage count for the keyword.
      base::RecordAction(base::UserMetricsAction("AcceptedKeyword"));
      EmitAcceptedKeywordSuggestionHistogram(keyword_mode_entry_method_,
                                             template_url);
      controller_->client()->GetTemplateURLService()->IncrementUsageCount(
          template_url);

      // Notify the extension of the selected input, but ignore if the selection
      // corresponds to an action created by an extension in unscoped mode.
      if (template_url->type() == TemplateURL::OMNIBOX_API_EXTENSION &&
          !action) {
        controller_->client()->ProcessExtensionMatch(input_text, template_url,
                                                     match, disposition);
        if (disposition != WindowOpenDisposition::NEW_BACKGROUND_TAB && view_) {
          base::AutoReset<bool> tmp(&in_revert_, true);
          view_->RevertAll();
        }
        // Avoid calling `OmniboxClient::OnAutocompleteAccept()`. The extension
        // was notfied of the accepted input and will handle the navigation.
        return;
      }
    } else {
      DCHECK(ui::PageTransitionTypeIncludingQualifiersIs(
                 match.transition, ui::PAGE_TRANSITION_GENERATED) ||
             ui::PageTransitionTypeIncludingQualifiersIs(
                 match.transition, ui::PAGE_TRANSITION_RELOAD));
      // NOTE: We purposefully don't increment the usage count of the default
      // search engine here like we do for explicit keywords above; see comments
      // in template_url.h.
    }
  } else {
    // |match| is a URL navigation, not a search.
    // For logging the below histogram, only record uses that depend on the
    // omnibox suggestion system, i.e., TYPED navigations.  That is, exclude
    // omnibox URL interactions that are treated as reloads or link-following
    // (i.e., cut-and-paste of URLs) or paste-and-go.
    if (ui::PageTransitionTypeIncludingQualifiersIs(
            match.transition, ui::PAGE_TRANSITION_TYPED) &&
        pasted_text.empty()) {
      navigation_metrics::RecordOmniboxURLNavigation(destination_url);
    }

    // The following histograms should be recorded for both TYPED and pasted
    // URLs, but should still exclude reloads.
    if (ui::PageTransitionTypeIncludingQualifiersIs(
            match.transition, ui::PAGE_TRANSITION_TYPED) ||
        ui::PageTransitionTypeIncludingQualifiersIs(match.transition,
                                                    ui::PAGE_TRANSITION_LINK)) {
      net::cookie_util::RecordCookiePortOmniboxHistograms(destination_url);
    }
  }

  if (action) {
    OmniboxAction::ExecutionContext context(
        *(autocomplete_controller()->autocomplete_provider_client()),
        base::BindOnce(&OmniboxClient::OnAutocompleteAccept,
                       controller_->client()->AsWeakPtr()),
        match_selection_timestamp, disposition);
    action->Execute(context);

    // Actions aren't generally able to change omnibox state, but it may be
    // worth considering an extension to OmniboxAction::ExecutionContext
    // if more action types want to enter keyword modes, close the popup, etc.
    // Note: The CONTEXTUAL_SEARCH_OPEN_LENS action does not enter the omnibox
    // into '@page' keyword mode and the omnibox is committed and closed as
    // normal in that case, with the lens UI managing its own state, so there's
    // no need for the omnibox to close lens.
    if (action->ActionId() ==
            OmniboxActionId::CONTEXTUAL_SEARCH_ASK_ABOUT_PAGE ||
        action->ActionId() ==
            OmniboxActionId::CONTEXTUAL_SEARCH_SELECT_REGION) {
      if (const TemplateURL* page_turl =
              controller_->client()
                  ->GetTemplateURLService()
                  ->FindStarterPackTemplateURL(
                      TemplateURLStarterPackData::kPage)) {
        EnterKeywordMode(
            OmniboxEventProto::SELECT_SUGGESTION, page_turl,
            l10n_util::GetStringUTF16(IDS_OMNIBOX_PAGE_SCOPE_PLACEHOLDER_TEXT));
        if (action->ActionId() ==
                OmniboxActionId::CONTEXTUAL_SEARCH_SELECT_REGION &&
            view_) {
          view_->CloseOmniboxPopup();
        }
        close_lens_ = true;
        return;
      }
    } else if (action->ActionId() ==
               OmniboxActionId::CONTEXTUAL_SEARCH_FULFILLMENT) {
      // Lens fulfills matches in the side panel, and should not be closed
      // by the omnibox after opening this match.
      close_lens_ = false;
    }
  }

  if (disposition != WindowOpenDisposition::NEW_BACKGROUND_TAB && view_) {
    base::AutoReset<bool> tmp(&in_revert_, true);
    view_->RevertAll();  // Revert the box to its unedited state.
  }

  if (!action) {
    // Track whether the destination URL sends us to a search results page
    // using the default search provider.
    TemplateURLService* template_url_service =
        controller_->client()->GetTemplateURLService();
    if (template_url_service &&
        template_url_service->IsSearchResultsPageFromDefaultSearchProvider(
            match.destination_url)) {
      base::RecordAction(
          base::UserMetricsAction("OmniboxDestinationURLIsSearchOnDSP"));
      base::UmaHistogramBoolean("Omnibox.Search.OffTheRecord", is_incognito);
    }

    BookmarkModel* bookmark_model = controller_->client()->GetBookmarkModel();
    if (bookmark_model && bookmark_model->IsBookmarked(destination_url)) {
      controller_->client()->OnBookmarkLaunched();
    }

    // This block should be the last call in OpenMatch, because controller_ is
    // not guaranteed to exist after client()->OnAutocompleteAccept is called.
    if (destination_url.is_valid()) {
      // This calls RevertAll again.
      base::AutoReset<bool> tmp(&in_revert_, true);

      controller_->client()->OnAutocompleteAccept(
          destination_url, match.post_content.get(), disposition,
          ui::PageTransitionFromInt(match.transition |
                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
          match.type, match_selection_timestamp,
          input_.added_default_scheme_to_typed_url(),
          input_.typed_url_had_http_scheme() &&
              match.type == AutocompleteMatchType::URL_WHAT_YOU_TYPED,
          input_text, match,
          VerbatimMatchForInput(
              autocomplete_controller()->history_url_provider(),
              autocomplete_controller()->autocomplete_provider_client(),
              alternate_input, alternate_nav_url, false));
    }
  }
}

https://source.chromium.org/chromium/chromium/src/+/main:components/omnibox/browser/omnibox_edit_model.cc;drc=467a8e68f685f9cfa47ee3fbfca20c22f7f6e893;l=2588

void OmniboxAction::Execute(OmniboxAction::ExecutionContext& context) https://source.chromium.org/chromium/chromium/src/+/main:components/omnibox/browser/actions/omnibox_action.cc;drc=467a8e68f685f9cfa47ee3fbfca20c22f7f6e893;l=96

void OmniboxAction::OpenURL(OmniboxAction::ExecutionContext& context,
                            const GURL& url) const {
  // Set `match_type` as if the user just typed |url| verbatim.
  // `destination_url_entered_without_scheme` is used to determine whether
  // navigations typed without a scheme and upgraded to HTTPS should fall back
  // to HTTP. The URL might have been entered without a scheme, but Action
  // destination URLs don't need a fallback so it's fine to pass false here.
  std::move(context.open_url_callback_)
      .Run(url, nullptr, context.disposition_, ui::PAGE_TRANSITION_GENERATED,
           /*match_type=*/AutocompleteMatchType::URL_WHAT_YOU_TYPED,
           context.match_selection_timestamp_,
           /*destination_url_entered_without_scheme=*/false,
           /*destination_url_entered_with_http_scheme=*/false, u"",
           AutocompleteMatch(), AutocompleteMatch());
}

https://source.chromium.org/chromium/chromium/src/+/main:components/omnibox/browser/actions/omnibox_action.cc;drc=467a8e68f685f9cfa47ee3fbfca20c22f7f6e893;l=136

context.open_url_callback_の実態は以下を継承したもの

 virtual void OnAutocompleteAccept(
      const GURL& destination_url,
      TemplateURLRef::PostContent* post_content,
      WindowOpenDisposition disposition,
      ui::PageTransition transition,
      AutocompleteMatchType::Type match_type,
      base::TimeTicks match_selection_timestamp,
      bool destination_url_entered_without_scheme,
      bool destination_url_entered_with_http_scheme,
      const std::u16string& text,
      const AutocompleteMatch& match,
      const AutocompleteMatch& alternative_nav_match) = 0;

https://source.chromium.org/chromium/chromium/src/+/main:components/omnibox/browser/omnibox_client.h;drc=467a8e68f685f9cfa47ee3fbfca20c22f7f6e893;l=257

実態はおそらくこれ

void ChromeOmniboxClient::OnAutocompleteAccept(
    const GURL& destination_url,
    TemplateURLRef::PostContent* post_content,
    WindowOpenDisposition disposition,
    ui::PageTransition transition,
    AutocompleteMatchType::Type match_type,
    base::TimeTicks match_selection_timestamp,
    bool destination_url_entered_without_scheme,
    bool destination_url_entered_with_http_scheme,
    const std::u16string& text,
    const AutocompleteMatch& match,
    const AutocompleteMatch& alternative_nav_match) {
  TRACE_EVENT("omnibox", "ChromeOmniboxClient::OnAutocompleteAccept", "text",
              text, "match", match, "alternative_nav_match",
              alternative_nav_match);

  // Store the details necessary to open the omnibox match via browser commands.
  location_bar_->set_navigation_params(LocationBar::NavigationParams(
      destination_url, disposition, transition, match_selection_timestamp,
      destination_url_entered_without_scheme,
      destination_url_entered_with_http_scheme, match.extra_headers));

  if (browser_) {
    auto navigation = chrome::OpenCurrentURL(browser_);
    ChromeOmniboxNavigationObserver::Create(navigation.get(), profile_, text,
                                            match, alternative_nav_match);
  }

#if BUILDFLAG(ENABLE_EXTENSIONS)
  extensions::MaybeShowExtensionControlledSearchNotification(
      location_bar_->GetWebContents(), match_type);
#endif
}

https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/omnibox/chrome_omnibox_client.cc;l=725;bpv=1;bpt=1?q=OmniboxClient::OnAutocompleteAccept&ss=chromium%2Fchromium%2Fsrc

base::WeakPtr<content::NavigationHandle> OpenCurrentURL(Browser* browser) {
  base::RecordAction(UserMetricsAction("LoadURL"));
  // TODO(crbug.com/40820294): Eliminate extra checks once source of
  //  bad pointer dereference is identified. See also TODO comment below.
  CHECK(browser);
  BrowserWindow* window = browser->window();
  CHECK(window);
  LocationBar* location_bar = window->GetLocationBar();
  if (!location_bar) {
    return nullptr;
  }

  GURL url(location_bar->navigation_params().destination_url);
  TRACE_EVENT1("navigation", "chrome::OpenCurrentURL", "url", url);

  if (ShouldInterceptChromeURLNavigationInIncognito(browser, url)) {
    ProcessInterceptedChromeURLNavigationInIncognito(browser, url);
    return nullptr;
  }

  NavigateParams params(browser, url,
                        location_bar->navigation_params().transition);
  params.disposition = location_bar->navigation_params().disposition;
  // Use ADD_INHERIT_OPENER so that all pages opened by the omnibox at least
  // inherit the opener. In some cases the tabstrip will determine the group
  // should be inherited, in which case the group is inherited instead of the
  // opener.
  params.tabstrip_add_types =
      AddTabTypes::ADD_FORCE_INDEX | AddTabTypes::ADD_INHERIT_OPENER;
  params.input_start =
      location_bar->navigation_params().match_selection_timestamp;
  params.is_using_https_as_default_scheme =
      location_bar->navigation_params().url_typed_without_scheme;
  params.url_typed_with_http_scheme =
      location_bar->navigation_params().url_typed_with_http_scheme;
  params.extra_headers = location_bar->navigation_params().extra_headers;
  auto result = Navigate(&params);

#if BUILDFLAG(ENABLE_EXTENSIONS)
  DCHECK(extensions::ExtensionSystem::Get(browser->profile())
             ->extension_service());
  // TODO(crbug.com/40820294): Eliminate extra checks once source of
  //  bad pointer dereference is identified. See also TODO comment above.
  extensions::ExtensionRegistry* extension_registry =
      extensions::ExtensionRegistry::Get(browser->profile());
  CHECK(extension_registry);
  const extensions::Extension* extension =
      extension_registry->enabled_extensions().GetAppByURL(url);
  if (extension) {
    extensions::RecordAppLaunchType(extension_misc::APP_LAUNCH_OMNIBOX_LOCATION,
                                    extension->GetType());
  }
#endif
  return result;
}

https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/browser_commands.cc;drc=467a8e68f685f9cfa47ee3fbfca20c22f7f6e893;bpv=1;bpt=1;l=842

そしてNavigate関数が呼ばれる。

base::WeakPtr<content::NavigationHandle> Navigate(NavigateParams* params) {
  TRACE_EVENT1("navigation", "chrome::Navigate", "disposition",
               params->disposition);
  Browser* source_browser = params->browser;
  if (source_browser) {
    params->initiating_profile = source_browser->profile();
  }
  DCHECK(params->initiating_profile);

  // If the created window is a partitioned popin, a valid source exists, and
  // the disposition is NEW_POPUP then the resulting popup should be tab-modal.
  // See: https://explainers-by-googlers.github.io/partitioned-popins/
  params->is_tab_modal_popup_deprecated |=
      params->window_features.is_partitioned_popin && params->source_contents &&
      params->disposition == WindowOpenDisposition::NEW_POPUP;

#if BUILDFLAG(IS_CHROMEOS)
  if (params->initiating_profile->IsOffTheRecord() &&
      params->initiating_profile->GetOTRProfileID().IsCaptivePortal() &&
      params->disposition != WindowOpenDisposition::NEW_POPUP &&
      params->disposition != WindowOpenDisposition::CURRENT_TAB &&
      !IncognitoModeForced(params->initiating_profile)) {
    // Navigation outside of the current tab or the initial popup window from a
    // captive portal signin window should be prevented.
    params->disposition = WindowOpenDisposition::CURRENT_TAB;
  }
#endif

  if (params->initiating_profile->ShutdownStarted()) {
    // Don't navigate when the profile is shutting down.
    return nullptr;
  }

  // Block navigation requests when in locked fullscreen mode. We allow
  // navigation requests in the webapp when locked for OnTask (only relevant for
  // non-web browser scenarios).
  // TODO(b/365146870): Remove once we consolidate locked fullscreen with
  // OnTask.
  if (source_browser) {
    bool should_block_navigation =
        platform_util::IsBrowserLockedFullscreen(source_browser);
#if BUILDFLAG(IS_CHROMEOS)
    if (source_browser->IsLockedForOnTask()) {
      should_block_navigation = false;
    }
#endif  // BUILDFLAG(IS_CHROMEOS)
    if (should_block_navigation) {
      return nullptr;
    }
  }

  // Open System Apps in their standalone window if necessary.
  // TODO(crbug.com/40136163): Remove this code after we integrate with intent
  // handling.
#if BUILDFLAG(IS_CHROMEOS)
  const std::optional<ash::SystemWebAppType> capturing_system_app_type =
      ash::GetCapturingSystemAppForURL(params->initiating_profile, params->url);
  if (capturing_system_app_type &&
      (!params->browser ||
       !ash::IsBrowserForSystemWebApp(params->browser,
                                      capturing_system_app_type.value()))) {
    ash::SystemAppLaunchParams swa_params;
    swa_params.url = params->url;
    ash::LaunchSystemWebAppAsync(params->initiating_profile,
                                 capturing_system_app_type.value(), swa_params);

    // It's okay to early return here, because LaunchSystemWebAppAsync uses a
    // different logic to choose (and create if necessary) a browser window for
    // system apps.
    //
    // It's okay to skip the checks and cleanups below. The link captured system
    // app will either open in its own browser window, or navigate an existing
    // browser window exclusively used by this app. For the initiating browser,
    // the navigation should appear to be cancelled.
    return nullptr;
  }
#endif  // BUILDFLAG(IS_CHROMEOS)

#if !BUILDFLAG(IS_ANDROID)
  // Force isolated PWAs to open in an app window.
  params->force_open_pwa_window =
      content::SiteIsolationPolicy::ShouldUrlUseApplicationIsolationLevel(
          params->initiating_profile, params->url);
  params->open_pwa_window_if_possible |= params->force_open_pwa_window;
#endif

  if (!AdjustNavigateParamsForURL(params)) {
    return nullptr;
  }

  // Picture-in-picture browser windows must have a source contents in order for
  // the window to function correctly. If we have no source contents to work
  // with (e.g. if an extension popup attempts to open a PiP window), we should
  // cancel the navigation.  The source URL must also be of a type that's
  // allowed to open document PiP.  See `PictureInPictureWindowManager` for
  // details on what's allowed.
  if (params->disposition == WindowOpenDisposition::NEW_PICTURE_IN_PICTURE) {
    const GURL& url = params->source_contents
                          ? params->source_contents->GetLastCommittedURL()
                          : GURL();
    if (!PictureInPictureWindowManager::IsSupportedForDocumentPictureInPicture(
            url)) {
      return nullptr;
    }
  }

  // If no source WebContents was specified, we use the selected one from the
  // target browser. This must happen before GetBrowserAndTabForDisposition()
  // has a chance to replace |params->browser| with another one, but after the
  // above check that relies on the original source_contents value.
  if (!params->source_contents && params->browser) {
    params->source_contents =
        params->browser->tab_strip_model()->GetActiveWebContents();
  }

  WebContents* contents_to_navigate_or_insert =
      params->contents_to_insert.get();
  if (params->switch_to_singleton_tab) {
    DCHECK_EQ(params->disposition, WindowOpenDisposition::SINGLETON_TAB);
    contents_to_navigate_or_insert = params->switch_to_singleton_tab;
  }

#if !BUILDFLAG(IS_ANDROID)
  // If this is a Picture in Picture window, then notify the pip manager about
  // it. This enables the opener and pip window to stay connected, so that (for
  // example), the pip window does not outlive the opener.
  //
  // We do this before creating the browser window, so that the browser can talk
  // to the PictureInPictureWindowManager.  Otherwise, the manager has no idea
  // that there's a pip window.
  if (params->disposition == WindowOpenDisposition::NEW_PICTURE_IN_PICTURE) {
    // Picture in picture windows may not be opened by other picture in
    // picture windows, or without an opener.
    if (!params->browser || params->browser->is_type_picture_in_picture()) {
      params->browser = nullptr;
      return nullptr;
    }

    PictureInPictureWindowManager::GetInstance()->EnterDocumentPictureInPicture(
        params->source_contents, contents_to_navigate_or_insert);
  }
#endif  // !BUILDFLAG(IS_ANDROID)

  // TODO(crbug.com/364657540): Revisit integration with web_application system
  // later if needed.
  int singleton_index;

#if !BUILDFLAG(IS_ANDROID)
  std::unique_ptr<web_app::NavigationCapturingProcess> app_navigation =
      web_app::NavigationCapturingProcess::MaybeHandleAppNavigation(*params);
  std::optional<std::tuple<Browser*, int>> app_browser_tab_override;
  if (app_navigation) {
    app_browser_tab_override =
        app_navigation->GetInitialBrowserAndTabOverrideForNavigation(*params);
  }
  std::tie(params->browser, singleton_index) =
      app_browser_tab_override.has_value()
          ? *app_browser_tab_override
          : GetBrowserAndTabForDisposition(*params);
#else  // !BUILDFLAG(IS_ANDROID)
  std::tie(params->browser, singleton_index) =
      GetBrowserAndTabForDisposition(*params);
#endif

  if (!params->browser) {
    return nullptr;
  }

  // Trying to open a background tab when in a non-tabbed app browser results in
  // focusing a regular browser window and opening a tab in the background
  // of that window. Change the disposition to NEW_FOREGROUND_TAB so that
  // the new tab is focused.
  if (source_browser && source_browser->is_type_app() &&
      params->disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB &&
      !(source_browser->app_controller() &&
        source_browser->app_controller()->has_tab_strip())) {
    params->disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
  }

  if (singleton_index != -1) {
    contents_to_navigate_or_insert =
        params->browser->tab_strip_model()->GetWebContentsAt(singleton_index);
  } else if (params->disposition == WindowOpenDisposition::SWITCH_TO_TAB) {
    // The user is trying to open a tab that no longer exists. If we open a new
    // tab, it could leave orphaned NTPs around, but always overwriting the
    // current tab could could clobber state that the user was trying to
    // preserve. Fallback to the behavior used for singletons: overwrite the
    // current tab if it's the NTP, otherwise open a new tab.
    params->disposition = WindowOpenDisposition::SINGLETON_TAB;
    ShowSingletonTabOverwritingNTP(params);
    return nullptr;
  }
  if (params->force_open_pwa_window) {
    CHECK(web_app::AppBrowserController::IsWebApp(params->browser));
  }
#if BUILDFLAG(IS_CHROMEOS)
  if (source_browser && source_browser != params->browser) {
    // When the newly created browser was spawned by a browser which visits
    // another user's desktop, it should be shown on the same desktop as the
    // originating one. (This is part of the desktop separation per profile).
    auto* window_manager = MultiUserWindowManagerHelper::GetWindowManager();
    // Some unit tests have no client instantiated.
    if (window_manager) {
      aura::Window* src_window = source_browser->window()->GetNativeWindow();
      aura::Window* new_window = params->browser->window()->GetNativeWindow();
      const AccountId& src_account_id =
          window_manager->GetUserPresentingWindow(src_window);
      if (src_account_id !=
          window_manager->GetUserPresentingWindow(new_window)) {
        // Once the window gets presented, it should be shown on the same
        // desktop as the desktop of the creating browser. Note that this
        // command will not show the window if it wasn't shown yet by the
        // browser creation.
        window_manager->ShowWindowForUser(new_window, src_account_id);
      }
    }
  }
#endif

  // Navigate() must not return early after this point.

  if (GetSourceProfile(params) != params->browser->profile()) {
    // A tab is being opened from a link from a different profile, we must reset
    // source information that may cause state to be shared.
    params->opener = nullptr;
    params->source_contents = nullptr;
    params->source_site_instance = nullptr;
    params->referrer = content::Referrer();
  }

  // Make sure the Browser is shown if params call for it.
  ScopedBrowserShower shower(params, &contents_to_navigate_or_insert);
  if (params->is_tab_modal_popup_deprecated) {
    shower.set_source_contents(params->source_contents);
  }

  // Some dispositions need coercion to base types.
  NormalizeDisposition(params);

  // If a new window has been created, it needs to be shown.
  if (params->window_action == NavigateParams::NO_ACTION &&
      source_browser != params->browser &&
      params->browser->tab_strip_model()->empty()) {
    params->window_action = NavigateParams::SHOW_WINDOW;
  }

  // If we create a popup window from a non user-gesture, don't activate it.
  if (params->window_action == NavigateParams::SHOW_WINDOW &&
      params->disposition == WindowOpenDisposition::NEW_POPUP &&
      params->user_gesture == false) {
    params->window_action = NavigateParams::SHOW_WINDOW_INACTIVE;
  }

  // Determine if the navigation was user initiated. If it was, we need to
  // inform the target WebContents, and we may need to update the UI.
  bool user_initiated =
      params->transition & ui::PAGE_TRANSITION_FROM_ADDRESS_BAR ||
      !ui::PageTransitionIsWebTriggerable(params->transition);

  base::WeakPtr<content::NavigationHandle> navigation_handle;

  std::unique_ptr<tabs::TabModel> tab_to_insert;
  if (params->contents_to_insert) {
    tab_to_insert =
        std::make_unique<tabs::TabModel>(std::move(params->contents_to_insert),
                                         params->browser->tab_strip_model());
  }

  // If no target WebContents was specified (and we didn't seek and find a
  // singleton), we need to construct one if we are supposed to target a new
  // tab.
  if (!contents_to_navigate_or_insert) {
    DCHECK(!params->url.is_empty());
    if (params->disposition != WindowOpenDisposition::CURRENT_TAB) {
      tab_to_insert = std::make_unique<tabs::TabModel>(
          CreateTargetContents(*params, params->url),
          params->browser->tab_strip_model());
      contents_to_navigate_or_insert = tab_to_insert->GetContents();

      apps::SetAppIdForWebContents(params->browser->profile(),
                                   contents_to_navigate_or_insert,
                                   params->app_id);
#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
      captive_portal::CaptivePortalTabHelper::FromWebContents(
          contents_to_navigate_or_insert)
          ->set_window_type(params->captive_portal_window_type);
#endif
    } else {
      // ... otherwise if we're loading in the current tab, thnave target is the
      // same as the source.
      DCHECK(params->source_contents);
      contents_to_navigate_or_insert = params->source_contents;
    }

    // Try to handle non-navigational URLs that popup dialogs and such, these
    // should not actually navigate.
    if (!HandleNonNavigationAboutURL(params->url)) {
      // Perform the actual navigation, tracking whether it came from the
      // renderer.
      navigation_handle = LoadURLInContents(contents_to_navigate_or_insert,
                                            params->url, params);
    }
  } else {
    // |contents_to_navigate_or_insert| was specified non-NULL, and so we assume
    // it has already been navigated appropriately. We need to do nothing more
    // other than add it to the appropriate tabstrip.
  }

  // If the user navigated from the omnibox, and the selected tab is going to
  // lose focus, then make sure the focus for the source tab goes away from the
  // omnibox.
  if (params->source_contents &&
      (params->disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
       params->disposition == WindowOpenDisposition::NEW_WINDOW) &&
      (params->tabstrip_add_types & AddTabTypes::ADD_INHERIT_OPENER)) {
    params->source_contents->Focus();
  }

  if (tab_to_insert) {
    // Save data needed for link capturing into apps that cannot otherwise be
    // inferred later in the navigation. These are only needed when the
    // navigation happens in a different tab to the link click.
    apps::SetLinkCapturingSourceDisposition(tab_to_insert->GetContents(),
                                            params->disposition);
  }

  if (params->source_contents == contents_to_navigate_or_insert) {
    // The navigation occurred in the source tab.
    params->browser->UpdateUIForNavigationInTab(
        contents_to_navigate_or_insert, params->transition,
        params->window_action, user_initiated);
  } else if (singleton_index == -1) {
    if (source_browser != params->browser) {
      params->tabstrip_index = params->browser->tab_strip_model()->count();
    }

    // If some non-default value is set for the index, we should tell the
    // TabStripModel to respect it.
    if (params->tabstrip_index != -1) {
      params->tabstrip_add_types |= AddTabTypes::ADD_FORCE_INDEX;
    }

    // Maybe notify that an open operation has been done from a gesture.
    // TODO(crbug.com/40719979): preferably pipe this information through the
    // TabStripModel instead. See bug for deeper discussion.
    if (params->user_gesture && source_browser == params->browser) {
      params->browser->window()->LinkOpeningFromGesture(params->disposition);
    }

    DCHECK(tab_to_insert);
    // The navigation should insert a new tab into the target Browser.
    params->browser->tab_strip_model()->AddTab(
        std::move(tab_to_insert), params->tabstrip_index, params->transition,
        params->tabstrip_add_types, params->group);
  }

  if (singleton_index >= 0) {
    // If switching browsers, make sure it is shown.
    if (params->disposition == WindowOpenDisposition::SWITCH_TO_TAB &&
        params->browser != source_browser) {
      params->window_action = NavigateParams::SHOW_WINDOW;
    }

    if (contents_to_navigate_or_insert->IsCrashed()) {
      contents_to_navigate_or_insert->GetController().Reload(
          content::ReloadType::NORMAL, true);
    } else if (params->path_behavior == NavigateParams::IGNORE_AND_NAVIGATE &&
               contents_to_navigate_or_insert->GetURL() != params->url) {
      navigation_handle = LoadURLInContents(contents_to_navigate_or_insert,
                                            params->url, params);
    }

    // If the singleton tab isn't already selected, select it.
    if (params->source_contents != contents_to_navigate_or_insert) {
      // Use the index before the potential close below, because it could
      // make the index refer to a different tab.
      auto gesture_type = user_initiated
                              ? TabStripUserGestureDetails::GestureType::kOther
                              : TabStripUserGestureDetails::GestureType::kNone;
      bool should_close_this_tab = false;
      if (params->disposition == WindowOpenDisposition::SWITCH_TO_TAB) {
        // Close orphaned NTP (and the like) with no history when the user
        // switches away from them.
        if (params->source_contents) {
          if (params->source_contents->GetController().CanGoBack() ||
              (params->source_contents->GetLastCommittedURL().spec() !=
                   chrome::kChromeUINewTabURL &&
               params->source_contents->GetLastCommittedURL().spec() !=
                   url::kAboutBlankURL)) {
            // Blur location bar before state save in ActivateTabAt() below.
            params->source_contents->Focus();
          } else {
            should_close_this_tab = true;
          }
        }
      }
      params->browser->tab_strip_model()->ActivateTabAt(
          singleton_index, TabStripUserGestureDetails(gesture_type));
      // Close tab after switch so index remains correct.
      if (should_close_this_tab) {
        params->source_contents->Close();
      }
    }
  }

  params->navigated_or_inserted_contents = contents_to_navigate_or_insert;

// At this point, the `params->navigated_or_inserted_contents` is guaranteed to
// be non null, so perform tasks if the navigation has been captured by a web
// app, like enqueueing launch params.
#if !BUILDFLAG(IS_ANDROID)
  if (app_navigation) {
    web_app::NavigationCapturingProcess::AfterWebContentsCreation(
        std::move(app_navigation), *params->navigated_or_inserted_contents,
        navigation_handle.get());
  }
#endif  // !BUILDFLAG(IS_ANDROID)
  return navigation_handle;
}

https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/browser_navigator.cc;drc=467a8e68f685f9cfa47ee3fbfca20c22f7f6e893;bpv=1;bpt=1;l=586

おそらくLoadURLInContentsが呼ばれる

base::WeakPtr<content::NavigationHandle> LoadURLInContents(
    WebContents* target_contents,
    const GURL& url,
    NavigateParams* params) {
  NavigationController::LoadURLParams load_url_params(url);
  load_url_params.initiator_frame_token = params->initiator_frame_token;
  load_url_params.initiator_process_id = params->initiator_process_id;
  load_url_params.initiator_origin = params->initiator_origin;
  load_url_params.initiator_base_url = params->initiator_base_url;
  load_url_params.source_site_instance = params->source_site_instance;
  load_url_params.referrer = params->referrer;
  load_url_params.frame_name = params->frame_name;
  load_url_params.frame_tree_node_id = params->frame_tree_node_id;
  load_url_params.redirect_chain = params->redirect_chain;
  load_url_params.transition_type = params->transition;
  load_url_params.extra_headers = params->extra_headers;
  load_url_params.should_replace_current_entry =
      params->should_replace_current_entry;
  load_url_params.is_renderer_initiated = params->is_renderer_initiated;
  load_url_params.started_from_context_menu = params->started_from_context_menu;
  load_url_params.has_user_gesture = params->user_gesture;
  load_url_params.blob_url_loader_factory = params->blob_url_loader_factory;
  load_url_params.input_start = params->input_start;
  load_url_params.was_activated = params->was_activated;
  load_url_params.href_translate = params->href_translate;
  load_url_params.reload_type = params->reload_type;
  load_url_params.impression = params->impression;
  load_url_params.suggested_system_entropy = params->suggested_system_entropy;

  // |frame_tree_node_id| is invalid for main frame navigations.
  if (params->frame_tree_node_id.is_null()) {
    bool force_no_https_upgrade =
        params->url_typed_with_http_scheme ||
        params->captive_portal_window_type !=
            captive_portal::CaptivePortalWindowType::kNone;
    std::unique_ptr<ChromeNavigationUIData> navigation_ui_data =
        ChromeNavigationUIData::CreateForMainFrameNavigation(
            target_contents, params->is_using_https_as_default_scheme,
            force_no_https_upgrade);
    navigation_ui_data->set_navigation_initiated_from_sync(
        params->navigation_initiated_from_sync);
    load_url_params.navigation_ui_data = std::move(navigation_ui_data);
  }

  if (params->post_data) {
    load_url_params.load_type = NavigationController::LOAD_TYPE_HTTP_POST;
    load_url_params.post_data = params->post_data;
  }

  return target_contents->GetController().LoadURLWithParams(load_url_params);
}

https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/browser_navigator.cc;l=454;drc=467a8e68f685f9cfa47ee3fbfca20c22f7f6e893;bpv=1;bpt=1

LoadURLWithParamsで


base::WeakPtr<NavigationHandle> NavigationControllerImpl::LoadURLWithParams(
    const LoadURLParams& params) {
  if (params.is_renderer_initiated)
    DCHECK(params.initiator_origin.has_value());

  TRACE_EVENT1("browser,navigation",
               "NavigationControllerImpl::LoadURLWithParams", "url",
               params.url.possibly_invalid_spec());
  bool is_explicit_navigation =
      GetContentClient()->browser()->IsExplicitNavigation(
          params.transition_type);
  if (HandleDebugURL(params.url, params.transition_type,
                     is_explicit_navigation)) {
    // If Telemetry is running, allow the URL load to proceed as if it's
    // unhandled, otherwise Telemetry can't tell if Navigation completed.
    if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
            switches::kEnableGpuBenchmarking)) {
      return nullptr;
    }
  }

  // Checks based on params.load_type.
  switch (params.load_type) {
    case LOAD_TYPE_DEFAULT:
    case LOAD_TYPE_HTTP_POST:
#if BUILDFLAG(IS_ANDROID)
    case LOAD_TYPE_PDF_ANDROID:
#endif
      break;
    case LOAD_TYPE_DATA:
      if (!params.url.SchemeIs(url::kDataScheme)) {
        NOTREACHED() << "Data load must use data scheme.";
      }
      break;
  }

  // The user initiated a load, we don't need to reload anymore.
  needs_reload_ = false;

  return NavigateWithoutEntry(params);
}

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/navigation_controller_impl.cc;l=1347;drc=467a8e68f685f9cfa47ee3fbfca20c22f7f6e893;bpv=1;bpt=1

base::WeakPtr<NavigationHandle> NavigationControllerImpl::NavigateWithoutEntry(
    const LoadURLParams& params) 

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/navigation_controller_impl.cc;drc=467a8e68f685f9cfa47ee3fbfca20c22f7f6e893;bpv=1;bpt=1;l=3662

NavigationRequestに変換

std::unique_ptr<NavigationRequest> request =
      CreateNavigationRequestFromLoadParams(
          node, params, override_user_agent, should_replace_current_entry,
          params.has_user_gesture, network::mojom::SourceLocation::New(),
          reload_type, pending_entry_, pending_entry_->GetFrameEntry(node),
          actual_navigation_start_time, navigation_start_time);

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/navigation_controller_impl.cc;l=3800;drc=467a8e68f685f9cfa47ee3fbfca20c22f7f6e893;bpv=1;bpt=1

void Navigator::Navigate(std::unique_ptr request, ReloadType reload_type)を実行 https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/navigator.cc;drc=467a8e68f685f9cfa47ee3fbfca20c22f7f6e893;bpv=1;bpt=1;l=815

frame_tree_node->TakeNavigationRequest(std::move(request));の呼び出し https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/navigator.cc;l=937;drc=467a8e68f685f9cfa47ee3fbfca20c22f7f6e893;bpv=1;bpt=1 void FrameTreeNode::TakeNavigationRequest( std::unique_ptr navigation_request) https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/frame_tree_node.cc;drc=467a8e68f685f9cfa47ee3fbfca20c22f7f6e893;bpv=1;bpt=1;l=578

最終的にここにNavigationRequestが設定されてpendingされる。(おそらく非同期?) https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/frame_tree_node.cc;l=621;drc=467a8e68f685f9cfa47ee3fbfca20c22f7f6e893;bpv=1;bpt=1

NavigationURLLoaderがこの設定されたやつを制御するっぽい? https://source.chromium.org/chromium/chromium/src/+/main:content/browser/loader/navigation_url_loader.h;drc=467a8e68f685f9cfa47ee3fbfca20c22f7f6e893;bpv=1;bpt=1;l=40

ここでFrameTreeNode::TakeNavigationRequestで取得したNavigationRequestを使ってリクエスト実行開始 https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/navigator.cc;l=950;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

NavigationRequest::BeginNavigation() https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/navigation_request.cc;l=2383;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

NavigationRequest::BeginNavigationImpl() https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/navigation_request.cc;l=2738;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

NavigationRequest::WillStartRequest() https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/navigation_request.cc;l=7976;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

NavigationThrottleRunner::ProcessNavigationEvent https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/navigation_throttle_runner.cc;l=128;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

NavigationThrottleRunner::ProcessInternal() https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/navigation_throttle_runner.cc;l=220;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/navigation_throttle_runner.cc;l=249;drc=8fd12b9c0e73b29ea33990af0ef603e1db43e4c3;bpv=1;bpt=1

void NavigationThrottleRunner::InformRegistry(
    const NavigationThrottle::ThrottleCheckResult& result) {
  // Now that the event has executed, reset the current event to kNoEvent since
  // we're no longer processing any event. Do it before the call to the
  // delegate, as it might lead to the deletion of this
  // NavigationThrottleRunner.
  NavigationThrottleEvent event = current_event_;
  current_event_ = NavigationThrottleEvent::kNoEvent;
  registry_->OnEventProcessed(event, result);
  // DO NOT ADD CODE AFTER THIS. The NavigationThrottleRunner might have been
  // deleted by the previous call.
}

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/navigation_throttle_registry_impl.cc;l=179;drc=8fd12b9c0e73b29ea33990af0ef603e1db43e4c3;bpv=1;bpt=1

void NavigationThrottleRegistryImpl::OnEventProcessed(
    NavigationThrottleEvent event,
    NavigationThrottle::ThrottleCheckResult result) {
  navigation_request_->OnNavigationEventProcessed(event, result);
}

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/navigation_request.cc;l=7704;drc=8fd12b9c0e73b29ea33990af0ef603e1db43e4c3;bpv=1;bpt=1

void NavigationRequest::OnNavigationEventProcessed(
    NavigationThrottleEvent event,
    NavigationThrottle::ThrottleCheckResult result) {

NavigationRequest::OnWillStartRequestProcessed https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/navigation_request.cc;l=7706;drc=736622ed7d9cf605750afa417b3f4e681eef686c?q=OnWillStartRequestProcessed&ss=chromium%2Fchromium%2Fsrc

void NavigationRequest::OnWillStartRequestProcessed(
    NavigationThrottle::ThrottleCheckResult result) {
  DCHECK_EQ(WILL_START_REQUEST, state_);
  DCHECK_NE(NavigationThrottle::BLOCK_RESPONSE, result.action());
  DCHECK(processing_navigation_throttle_);
  processing_navigation_throttle_ = false;
  if (result.action() != NavigationThrottle::PROCEED)
    SetState(CANCELING);

  if (complete_callback_for_testing_ &&
      std::move(complete_callback_for_testing_).Run(result)) {
    return;
  }

  if (MaybeEvictFromBackForwardCacheBySubframeNavigation(
          frame_tree_node_->current_frame_host())) {
    // DO NOT ADD CODE AFTER THIS, as the NavigationRequest might have been
    // deleted by the previous calls.
    return;
  }

  OnStartChecksComplete(result);

  // DO NOT ADD CODE AFTER THIS, as the NavigationRequest might have been
  // deleted by the previous calls.
}
void NavigationRequest::OnStartChecksComplete(
    NavigationThrottle::ThrottleCheckResult result)

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/navigation_request.cc;l=5444;drc=736622ed7d9cf605750afa417b3f4e681eef686c?q=OnWillStartRequestProcessed&ss=chromium%2Fchromium%2Fsrc

commit_params_->navigation_timing->fetch_start = base::TimeTicks::Now();

ここでスタートしているようだ… https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/navigation_request.cc;l=5592;drc=736622ed7d9cf605750afa417b3f4e681eef686c?q=OnWillStartRequestProcessed&ss=chromium%2Fchromium%2Fsrc

loader_->Start();

実装はおそらくここと思われる。

void NavigationURLLoaderImpl::Start()

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/loader/navigation_url_loader_impl.cc;l=590;drc=736622ed7d9cf605750afa417b3f4e681eef686c;bpv=1;bpt=1

void NavigationURLLoaderImpl::Restart() 

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/loader/navigation_url_loader_impl.cc;drc=736622ed7d9cf605750afa417b3f4e681eef686c;bpv=1;bpt=1;l=736

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/loader/navigation_url_loader_impl.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=778?q=SingleRequestURLLoaderFactory&ss=chromium%2Fchromium%2Fsrc&start=1

void NavigationURLLoaderImpl::MaybeStartLoader(
    size_t next_interceptor_index,
    std::optional<NavigationLoaderInterceptor::Result> interceptor_result) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  DCHECK(started_);

  if (interceptor_result) {
    subresource_loader_params_ =
        std::move(interceptor_result->subresource_loader_params);
    if (!interceptor_result->single_request_factory) {
      // Skip the subsequent interceptors and start with the default behavior.
      //
      // Here `subresource_loader_params_` can still have non-default values
      // e.g. when there's a controlling service worker that doesn't have a
      // fetch event handler so it doesn't intercept requests.
      StartNonInterceptedRequest(
          std::move(interceptor_result->response_head_update_params));
      return;
    }

    // Intercept the request with `interceptor_result->single_request_factory`.
    StartInterceptedRequest(
        std::move(interceptor_result->single_request_factory));
    return;
  }

  subresource_loader_params_ = {};

  if (next_interceptor_index >= interceptors_.size()) {
    // All interceptors have been checked and none has elected to handle the
    // request. Start with the default behavior.
    StartNonInterceptedRequest(ResponseHeadUpdateParams());
    return;
  }

  // Fallback to the next interceptor.
  auto* next_interceptor = interceptors_[next_interceptor_index].get();
  next_interceptor->MaybeCreateLoader(
      *resource_request_, browser_context_,
      base::BindOnce(&NavigationURLLoaderImpl::MaybeStartLoader,
                     weak_factory_.GetWeakPtr(), next_interceptor_index + 1),
      base::BindOnce(&NavigationURLLoaderImpl::FallbackToNonInterceptedRequest,
                     weak_factory_.GetWeakPtr()));
}

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/loader/navigation_url_loader_impl.cc;l=809;drc=736622ed7d9cf605750afa417b3f4e681eef686c;bpv=1;bpt=1

// All interceptors have been checked and none has elected to handle the
// request. Start with the default behavior.
StartNonInterceptedRequest(ResponseHeadUpdateParams());
return;

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/loader/navigation_url_loader_impl.cc;drc=736622ed7d9cf605750afa417b3f4e681eef686c;bpv=1;bpt=1;l=850

void NavigationURLLoaderImpl::StartNonInterceptedRequest(
    ResponseHeadUpdateParams head_update_params) {
  // If we already have the default `url_loader_` we must come here after a
  // redirect. No interceptors wanted to intercept the redirected request, so
  // let the loader just follow the redirect.
  if (url_loader_) {
    DCHECK(!redirect_info_.new_url.is_empty());
    url_loader_->FollowRedirect(
        std::move(url_loader_removed_headers_),
        std::move(url_loader_modified_headers_),
        std::move(url_loader_modified_cors_exempt_headers_));
    return;
  }

  head_update_params_ = std::move(head_update_params);
  scoped_refptr<network::SharedURLLoaderFactory> factory;
  if (network::IsURLHandledByNetworkService(resource_request_->url)) {
    factory = network_loader_factory_;
    default_loader_used_ = true;
  } else {
    factory = GetOrCreateNonNetworkLoaderFactory();
  }

  response_loader_receiver_.reset();
  CreateThrottlingLoaderAndStart(std::move(factory),
                                 /*additional_throttles=*/{});
}

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/loader/navigation_url_loader_impl.cc;drc=736622ed7d9cf605750afa417b3f4e681eef686c;bpv=1;bpt=1;l=1018

void NavigationURLLoaderImpl::CreateThrottlingLoaderAndStart(
    scoped_refptr<network::SharedURLLoaderFactory> factory,
    std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
        additional_throttles) {
  TRACE_EVENT_WITH_FLOW0(
      "navigation", "NavigationURLLoaderImpl::CreateThrottlingLoaderAndStart",
      TRACE_ID_LOCAL(this),
      TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
  CHECK(!url_loader_);

  std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles =
      CreateURLLoaderThrottles();
  for (auto&& throttle : additional_throttles) {
    throttles.push_back(std::move(throttle));
  }

  uint32_t options =
      GetURLLoaderOptions(resource_request_->is_outermost_main_frame);

  url_loader_ = blink::ThrottlingURLLoader::CreateLoaderAndStart(
      std::move(factory), std::move(throttles), global_request_id_.request_id,
      options, resource_request_.get(), /*client=*/this,
      kNavigationUrlLoaderTrafficAnnotation,
      GetUIThreadTaskRunner({BrowserTaskType::kNavigationNetworkResponse}),
      /*cors_exempt_header_list=*/std::nullopt,
      /*client_receiver_delegate=*/nullptr,
      &request_info_->common_params->initiator_origin_trial_features);
}

https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/common/loader/throttling_url_loader.cc;drc=736622ed7d9cf605750afa417b3f4e681eef686c;bpv=1;bpt=1;l=261

// static
std::unique_ptr<ThrottlingURLLoader> ThrottlingURLLoader::CreateLoaderAndStart(
    scoped_refptr<network::SharedURLLoaderFactory> factory,
    std::vector<std::unique_ptr<URLLoaderThrottle>> throttles,
    int32_t request_id,
    uint32_t options,
    network::ResourceRequest* url_request,
    network::mojom::URLLoaderClient* client,
    const net::NetworkTrafficAnnotationTag& traffic_annotation,
    scoped_refptr<base::SequencedTaskRunner> task_runner,
    std::optional<std::vector<std::string>> cors_exempt_header_list,
    ClientReceiverDelegate* client_receiver_delegate,
    const std::vector<int>* initiator_origin_trial_features) {
  DCHECK(url_request);
  std::unique_ptr<ThrottlingURLLoader> loader(
      new ThrottlingURLLoader(std::move(throttles), client, traffic_annotation,
                              client_receiver_delegate));
  loader->Start(std::move(factory), request_id, options, url_request,
                std::move(task_runner), std::move(cors_exempt_header_list),
                initiator_origin_trial_features);
  return loader;
}

https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/common/loader/throttling_url_loader.cc;drc=736622ed7d9cf605750afa417b3f4e681eef686c;bpv=1;bpt=1;l=404

void ThrottlingURLLoader::Start(
    scoped_refptr<network::SharedURLLoaderFactory> factory,
    int32_t request_id,
    uint32_t options,
    network::ResourceRequest* url_request,
    scoped_refptr<base::SequencedTaskRunner> task_runner,
    std::optional<std::vector<std::string>> cors_exempt_header_list,
    const std::vector<int>* initiator_origin_trial_features) {
  TRACE_EVENT_WITH_FLOW1("loading", "ThrottlingURLLoader::Start",
                         TRACE_ID_LOCAL(this),
                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
                         "request_id", request_id);
  DCHECK_EQ(DEFERRED_NONE, deferred_stage_);
  DCHECK(!loader_completed_);

  bool deferred = false;
  DCHECK(deferring_throttles_.empty());
  if (!throttles_.empty()) {
    original_url_ = url_request->url;
    for (auto& entry : throttles_) {
      auto* throttle = entry.throttle.get();
      bool throttle_deferred = false;

#if DCHECK_IS_ON()
      std::set<std::string> initial_headers, initial_cors_exempt_headers;
      if (cors_exempt_header_list) {
        for (auto& header : url_request->headers.GetHeaderVector())
          initial_headers.insert(header.key);

        for (auto& header : url_request->cors_exempt_headers.GetHeaderVector())
          initial_cors_exempt_headers.insert(header.key);
      }
#endif

      base::Time start = base::Time::Now();
      throttle->WillStartRequest(url_request, &throttle_deferred);
      RecordExecutionTimeHistogram(GetStageNameForHistogram(DEFERRED_START),
                                   start);

#if DCHECK_IS_ON()
      if (cors_exempt_header_list) {
        CheckThrottleWillNotCauseCorsPreflight(
            initial_headers, initial_cors_exempt_headers, url_request->headers,
            url_request->cors_exempt_headers, *cors_exempt_header_list);
      }
#endif

      if (original_url_ != url_request->url) {
        DCHECK(throttle_will_start_redirect_url_.is_empty())
            << "ThrottlingURLLoader doesn't support multiple throttles "
               "changing the URL.";
        if (original_url_.SchemeIsHTTPOrHTTPS() &&
            !url_request->url.SchemeIsHTTPOrHTTPS()) {
          NOTREACHED() << "A URLLoaderThrottle can't redirect from http(s) to "
                       << "a non http(s) scheme.";
        } else {
          throttle_will_start_redirect_url_ = url_request->url;
        }
        // Restore the original URL so that all throttles see the same original
        // URL.
        url_request->url = original_url_;
      }
      if (!HandleThrottleResult(throttle, throttle_deferred, &deferred))
        return;
    }
  }

  if (initiator_origin_trial_features &&
      base::Contains(
          *initiator_origin_trial_features,
          static_cast<int>(
              mojom::OriginTrialFeature::kDeviceBoundSessionCredentials))) {
    url_request->allows_device_bound_session_registration = true;
  }

  start_info_ = std::make_unique<StartInfo>(factory, request_id, options,
                                            url_request, std::move(task_runner),
                                            std::move(cors_exempt_header_list));

  if (deferred)
    deferred_stage_ = DEFERRED_START;
  else
    StartNow();
}

https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/common/loader/throttling_url_loader.cc;l=404;drc=736622ed7d9cf605750afa417b3f4e681eef686c;bpv=1;bpt=1

void ThrottlingURLLoader::StartNow() {
  TRACE_EVENT_WITH_FLOW0("loading", "ThrottlingURLLoader::StartNow",
                         TRACE_ID_LOCAL(this),
                         TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT);
  DCHECK(start_info_);
  if (!throttle_will_start_redirect_url_.is_empty()) {
    auto first_party_url_policy =
        start_info_->url_request.update_first_party_url_on_redirect
            ? net::RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT
            : net::RedirectInfo::FirstPartyURLPolicy::NEVER_CHANGE_URL;

    net::RedirectInfo redirect_info = net::RedirectInfo::ComputeRedirectInfo(
        start_info_->url_request.method, start_info_->url_request.url,
        start_info_->url_request.site_for_cookies, first_party_url_policy,
        start_info_->url_request.referrer_policy,
        start_info_->url_request.referrer.spec(),
        // Use status code 307 to preserve the method, so POST requests work.
        net::HTTP_TEMPORARY_REDIRECT, throttle_will_start_redirect_url_,
        std::nullopt, false, false, false);

    // Set Critical-CH restart info and clear for next redirect.
    redirect_info.critical_ch_restart_time = critical_ch_restart_time_;
    critical_ch_restart_time_ = base::TimeTicks();

    bool should_clear_upload = false;
    net::RedirectUtil::UpdateHttpRequest(
        start_info_->url_request.url, start_info_->url_request.method,
        redirect_info, std::nullopt, std::nullopt,
        &start_info_->url_request.headers, &should_clear_upload);

    if (should_clear_upload) {
      start_info_->url_request.request_body = nullptr;
    }

    // Set the new URL in the ResourceRequest struct so that it is the URL
    // that's requested.
    start_info_->url_request.url = throttle_will_start_redirect_url_;

    auto response_head = network::mojom::URLResponseHead::New();
    std::string header_string = base::StringPrintf(
        "HTTP/1.1 %i Internal Redirect\n"
        "Location: %s",
        net::HTTP_TEMPORARY_REDIRECT,
        throttle_will_start_redirect_url_.spec().c_str());

    response_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
        net::HttpUtil::AssembleRawHeaders(header_string));
    response_head->encoded_data_length = header_string.size();
    start_info_->task_runner->PostTask(
        FROM_HERE,
        base::BindOnce(&ThrottlingURLLoader::OnReceiveRedirect,
                       weak_factory_.GetWeakPtr(), std::move(redirect_info),
                       std::move(response_head)));
    return;
  }

  if (start_info_->url_request.keepalive) {
    base::UmaHistogramBoolean("FetchKeepAlive.Renderer.Total.Started", true);
  }
  DCHECK(start_info_->url_loader_factory);
  start_info_->url_loader_factory->CreateLoaderAndStart(
      url_loader_.BindNewPipeAndPassReceiver(start_info_->task_runner),
      start_info_->request_id, start_info_->options, start_info_->url_request,
      client_receiver_.BindNewPipeAndPassRemote(start_info_->task_runner),
      net::MutableNetworkTrafficAnnotationTag(traffic_annotation_));

  // TODO(https://crbug.com/919736): Remove this call.
  client_receiver_.internal_state()->EnableBatchDispatch();

  client_receiver_.set_disconnect_handler(base::BindOnce(
      &ThrottlingURLLoader::OnClientConnectionError, base::Unretained(this)));

  if (priority_info_) {
    auto priority_info = std::move(priority_info_);
    url_loader_->SetPriority(priority_info->priority,
                             priority_info->intra_priority_value);
  }

  // Initialize with the request URL, may be updated when on redirects
  response_url_ = start_info_->url_request.url;
}

以下のところでリクエスト開始のようである。 https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/common/loader/throttling_url_loader.cc;l=549;drc=736622ed7d9cf605750afa417b3f4e681eef686c;bpv=1;bpt=1

 start_info_->url_loader_factory->CreateLoaderAndStart(
      url_loader_.BindNewPipeAndPassReceiver(start_info_->task_runner),
      start_info_->request_id, start_info_->options, start_info_->url_request,
      client_receiver_.BindNewPipeAndPassRemote(start_info_->task_runner),
      net::MutableNetworkTrafficAnnotationTag(traffic_annotation_));

このurl_loader_factoryの出どころはここで https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/common/loader/throttling_url_loader.cc;l=479-481;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133

 start_info_ = std::make_unique<StartInfo>(factory, request_id, options,
                                            url_request, std::move(task_runner),
                                            std::move(cors_exempt_header_list));

さらにこのfactory引数自体の出どころは以下に遡れる。 https://source.chromium.org/chromium/chromium/src/+/main:content/browser/loader/navigation_url_loader_impl.cc;l=867;drc=736622ed7d9cf605750afa417b3f4e681eef686c

factory = network_loader_factory_;

さらにnetwork_loader_factory_の出どころは以下 https://source.chromium.org/chromium/chromium/src/+/main:content/browser/loader/navigation_url_loader_impl.cc;l=1721;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1

NavigationURLLoaderImpl::NavigationURLLoaderImpl(
    BrowserContext* browser_context,
    StoragePartition* storage_partition,
    std::unique_ptr<NavigationRequestInfo> request_info,
    std::unique_ptr<NavigationUIData> navigation_ui_data,
    ServiceWorkerMainResourceHandle* service_worker_handle,
    scoped_refptr<PrefetchedSignedExchangeCache>
        prefetched_signed_exchange_cache,
    NavigationURLLoaderDelegate* delegate,
    mojo::PendingRemote<network::mojom::CookieAccessObserver> cookie_observer,
    mojo::PendingRemote<network::mojom::TrustTokenAccessObserver>
        trust_token_observer,
    mojo::PendingRemote<network::mojom::SharedDictionaryAccessObserver>
        shared_dictionary_observer,
    mojo::PendingRemote<network::mojom::URLLoaderNetworkServiceObserver>
        url_loader_network_observer,
    mojo::PendingRemote<network::mojom::DevToolsObserver> devtools_observer,
    mojo::PendingRemote<network::mojom::DeviceBoundSessionAccessObserver>
        device_bound_session_observer,
    std::vector<std::unique_ptr<NavigationLoaderInterceptor>>
        initial_interceptors)
    : delegate_(delegate),
      browser_context_(browser_context),
      storage_partition_(static_cast<StoragePartitionImpl*>(storage_partition)),
      service_worker_handle_(service_worker_handle),
      request_info_(std::move(request_info)),
      url_(request_info_->common_params->url),
      frame_tree_node_id_(request_info_->frame_tree_node_id),
      global_request_id_(GlobalRequestID::MakeBrowserInitiated()),
      web_contents_getter_(
          base::BindRepeating(&WebContents::FromFrameTreeNodeId,
                              frame_tree_node_id_)),
      navigation_ui_data_(std::move(navigation_ui_data)),
      interceptors_(std::move(initial_interceptors)),
      prefetched_signed_exchange_cache_(
          std::move(prefetched_signed_exchange_cache)),
      loader_creation_time_(base::TimeTicks::Now()),
      ukm_source_id_(FrameTreeNode::GloballyFindByID(frame_tree_node_id_)
                         ->navigation_request()
                         ->GetNextPageUkmSourceId()) {
  TRACE_EVENT_WITH_FLOW0("navigation",
                         "NavigationURLLoaderImpl::NavigationURLLoaderImpl",
                         TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_OUT);
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP1(
      "navigation", "Navigation timeToResponseStarted", TRACE_ID_LOCAL(this),
      request_info_->common_params->navigation_start, "FrameTreeNode id",
      frame_tree_node_id_);

  mojo::PendingRemote<network::mojom::AcceptCHFrameObserver>
      accept_ch_frame_observer;
  // Use |kNavigationNetworkResponse| thread runner. Messages received related
  // to AcceptCHFrame are not order dependent and can restart the navigation,
  // blocking navigation when they do.
  accept_ch_frame_observers_.Add(
      this, accept_ch_frame_observer.InitWithNewPipeAndPassReceiver(),
      GetUIThreadTaskRunner({BrowserTaskType::kNavigationNetworkResponse}));

  FrameTreeNode* frame_tree_node =
      FrameTreeNode::GloballyFindByID(frame_tree_node_id_);
  DCHECK(frame_tree_node);
  DCHECK(frame_tree_node->navigation_request());

  resource_request_ = CreateResourceRequest(
      *request_info_, frame_tree_node, std::move(cookie_observer),
      std::move(trust_token_observer), std::move(shared_dictionary_observer),
      std::move(url_loader_network_observer), std::move(devtools_observer),
      std::move(device_bound_session_observer),
      std::move(accept_ch_frame_observer));

  network_loader_factory_ = CreateNetworkLoaderFactory(
      browser_context_, storage_partition_, frame_tree_node,
      ukm::SourceIdObj::FromInt64(ukm_source_id_), &bypass_redirect_checks_);
}

CreateNetworkLoaderFactory関数 https://source.chromium.org/chromium/chromium/src/+/main:content/browser/loader/navigation_url_loader_impl.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=1804

scoped_refptr<network::SharedURLLoaderFactory>
NavigationURLLoaderImpl::CreateNetworkLoaderFactory(
    BrowserContext* browser_context,
    StoragePartitionImpl* storage_partition,
    FrameTreeNode* frame_tree_node,
    const ukm::SourceIdObj& ukm_id,
    bool* bypass_redirect_checks) {
  mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient>
      header_client;

  // The embedder may want to proxy all network-bound URLLoaderFactory
  // receivers that it can. If it elects to do so, those proxies will be
  // connected when loader is created if the request type supports proxying.
  network::URLLoaderFactoryBuilder factory_builder;
  // Here we give nullptr for `factory_override`, because CORS is no-op for
  // navigations.
  GetContentClient()->browser()->WillCreateURLLoaderFactory(
      browser_context, frame_tree_node->current_frame_host(),
      frame_tree_node->current_frame_host()->GetProcess()->GetDeprecatedID(),
      ContentBrowserClient::URLLoaderFactoryType::kNavigation, url::Origin(),
      net::IsolationInfo(),
      frame_tree_node->navigation_request()->GetNavigationId(), ukm_id,
      factory_builder, &header_client, bypass_redirect_checks,
      /*disable_secure_dns=*/nullptr, /*factory_override=*/nullptr,
      GetUIThreadTaskRunner({BrowserTaskType::kNavigationNetworkResponse}));

  auto devtools_params =
      devtools_instrumentation::WillCreateURLLoaderFactoryParams::ForFrame(
          frame_tree_node->current_frame_host());
  devtools_params.Run(/*is_navigation=*/true,
                      /*is_download=*/false, factory_builder,
                      /*factory_override=*/nullptr);
  net::CookieSettingOverrides devtools_cookie_overrides;
  devtools_instrumentation::ApplyNetworkCookieControlsOverrides(
      devtools_params.agent_host(), devtools_cookie_overrides);

  net::CookieSettingOverrides cookie_overrides;
  if (ShouldAllowSameSiteNoneCookiesInSandbox(*frame_tree_node)) {
    // Include a CookieSettingOverride in the UrlLoaderFactoryParams for the
    // frame's SharedURLLoaderFactory if the frame contains the
    // `allow-same-site-none-cookies` value in its sandbox policy.
    cookie_overrides.Put(
        net::CookieSettingOverride::kAllowSameSiteNoneCookiesInSandbox);
  }

  if (header_client) {
    return base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>(
        CreateURLLoaderFactoryWithHeaderClient(
            std::move(header_client), std::move(factory_builder),
            storage_partition, std::move(devtools_cookie_overrides),
            std::move(cookie_overrides)));
  } else {
    if (!devtools_cookie_overrides.empty() || !cookie_overrides.empty()) {
      network::mojom::URLLoaderFactoryParamsPtr params =
          storage_partition->CreateURLLoaderFactoryParams();
      params->devtools_cookie_setting_overrides =
          std::move(devtools_cookie_overrides);
      params->cookie_setting_overrides = std::move(cookie_overrides);
      return std::move(factory_builder)
          .Finish(storage_partition->GetNetworkContext(), std::move(params));
    }
    return std::move(factory_builder)
        .Finish(storage_partition->GetURLLoaderFactoryForBrowserProcess());
  }
}

GetURLLoaderFactoryForBrowserProcess https://source.chromium.org/chromium/chromium/src/+/main:content/browser/storage_partition_impl.cc;l=1602;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133

scoped_refptr<network::SharedURLLoaderFactory>
StoragePartitionImpl::GetURLLoaderFactoryForBrowserProcess() {
  CHECK(shared_url_loader_factory_for_browser_process_);
  return shared_url_loader_factory_for_browser_process_->factory();
}

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/storage_partition_impl.cc;l=1432;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1

shared_url_loader_factory_for_browser_process_ = std::make_unique<
      ReconnectableURLLoaderFactoryForIOThreadWrapper>(base::BindRepeating(
      &StoragePartitionImpl::CreateURLLoaderFactoryForBrowserProcessInternal,
      GetWeakPtr()));
  shared_url_loader_factory_for_browser_process_->factory_for_io_thread()
      ->Initialize();

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/storage_partition_impl.cc;l=3679;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1?q=StoragePartitionImpl::CreateURLLoaderFactoryForBrowserProcessInternal&ss=chromium%2Fchromium%2Fsrc

void StoragePartitionImpl::CreateURLLoaderFactoryForBrowserProcessInternal(
    mojo::PendingRemote<network::mojom::URLLoaderFactory>* out_factory) {
  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  network::URLLoaderFactoryBuilder factory_builder;
  if (url_loader_factory::GetTestingInterceptor()) {
    url_loader_factory::GetTestingInterceptor().Run(
        network::mojom::kBrowserProcessId, factory_builder);
  }
  *out_factory =
      std::move(factory_builder)
          .Finish<mojo::PendingRemote<network::mojom::URLLoaderFactory>>(
              GetNetworkContext(), CreateURLLoaderFactoryParams());
}

https://source.chromium.org/chromium/chromium/src/+/main:services/network/public/cpp/url_loader_factory_builder.h;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=81

 // This `Finish()` variant returns the resulting factory as a
  // mojom::URLLoaderFactory-ish `OutType`.
  // See `WrapAs` methods for supported `OutType`.
  template <typename OutType = scoped_refptr<SharedURLLoaderFactory>,
            typename... Args>
  [[nodiscard]] OutType Finish(Args... terminal_args) && {
    if (IsEmpty()) {
      // The builder has no interceptors, so just forward the terminal.
      return WrapAs<OutType>(std::forward<Args>(terminal_args)...);
    }
    // Connect `head_` -(interceptors)-> `tail_` -> the terminal, and return the
    // head.
    ConnectTerminal(std::move(tail_), std::forward<Args>(terminal_args)...);
    return WrapAs<OutType>(std::move(head_));
  }

https://source.chromium.org/chromium/chromium/src/+/main:services/network/public/cpp/url_loader_factory_builder.h;l=123;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1

  template <typename OutType>
  static OutType WrapAs(mojom::NetworkContext* context,
                        mojom::URLLoaderFactoryParamsPtr factory_param) {
    mojo::PendingRemote<mojom::URLLoaderFactory> remote;
    context->CreateURLLoaderFactory(remote.InitWithNewPipeAndPassReceiver(),
                                    std::move(factory_param));
    return WrapAs<OutType>(std::move(remote));
  }

https://source.chromium.org/chromium/chromium/src/+/main:services/network/network_context.cc;l=1009;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1

void NetworkContext::CreateURLLoaderFactory(
    mojo::PendingReceiver<mojom::URLLoaderFactory> receiver,
    mojom::URLLoaderFactoryParamsPtr params) {
  scoped_refptr<ResourceSchedulerClient> resource_scheduler_client =
      base::MakeRefCounted<ResourceSchedulerClient>(
          ResourceScheduler::ClientId::Create(params->top_frame_id),
          IsBrowserInitiated(params->process_id == mojom::kBrowserProcessId),
          resource_scheduler_.get(),
          url_request_context_->network_quality_estimator());
  CreateURLLoaderFactory(std::move(receiver), std::move(params),
                         std::move(resource_scheduler_client));
}

https://source.chromium.org/chromium/chromium/src/+/main:services/network/network_context.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=963

void NetworkContext::CreateURLLoaderFactory(
    mojo::PendingReceiver<mojom::URLLoaderFactory> receiver,
    mojom::URLLoaderFactoryParamsPtr params,
    scoped_refptr<ResourceSchedulerClient> resource_scheduler_client) {
  url_loader_factories_.emplace(
      std::make_unique<PrefetchMatchingURLLoaderFactory>(
          this, std::move(params), std::move(resource_scheduler_client),
          std::move(receiver), &cors_origin_access_list_,
          prefetch_cache_.get()));
}

https://source.chromium.org/chromium/chromium/src/+/main:services/network/prefetch_matching_url_loader_factory.cc;l=62?q=PrefetchMatchingURLLoaderFactory&ss=chromium%2Fchromium%2Fsrc

void PrefetchMatchingURLLoaderFactory::CreateLoaderAndStart(
    mojo::PendingReceiver<mojom::URLLoader> loader,
    int32_t request_id,
    uint32_t options,
    ResourceRequest& request,
    mojo::PendingRemote<mojom::URLLoaderClient> client,
    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
  // If we don't think the request should be permitted from a render process, we
  // don't try to match it and instead let CorsURLLoaderFactory deal with the
  // issue.
  if (cache_ && IsRequestSafeForMatching(request)) {
    PrefetchURLLoaderClient* prefetch_client = cache_->Lookup(
        next_->isolation_info().network_isolation_key(), request.url);
    if (prefetch_client) {
      // A prefetch exists with the same NIK and URL.
      if (prefetch_client->Matches(request)) {
        if (use_matches_) {
          // The match has succeeded, and we are going to hand-off the
          // in-progress prefetch to the renderer.

          // TODO(crbug.com/342445996): Check that `options` is compatible with
          // the prefetch and whether anything needs to be done to the ongoing
          // request to match it.
          prefetch_client->Consume(std::move(loader), std::move(client));
          return;
        }
        // The match has succeeded, but the kNetworkContextPrefetchUseMatches
        // feature is not enabled. Try to make it possible for the render
        // process to reuse the cache entry.

        // Give the real URLLoader time to reach the cache, then cancel the
        // prefetch.
        cache_->DelayedErase(prefetch_client);
      } else {
        // There was an entry with the same NIK and URL, but it failed to match
        // on some other field. It is unlikely the renderer will subsequently
        // issue a matching request for the same URL, so erase the cache entry
        // to save resources.
        cache_->Erase(prefetch_client);
      }
    }
  }

  next_->CreateLoaderAndStart(std::move(loader), request_id, options, request,
                              std::move(client), traffic_annotation);
}

https://source.chromium.org/chromium/chromium/src/+/main:services/network/prefetch_matching_url_loader_factory.cc;l=62?q=PrefetchMatchingURLLoaderFactory&ss=chromium%2Fchromium%2Fsrc

PrefetchMatchingURLLoaderFactory::PrefetchMatchingURLLoaderFactory(
    NetworkContext* context,
    mojom::URLLoaderFactoryParamsPtr params,
    scoped_refptr<ResourceSchedulerClient> resource_scheduler_client,
    mojo::PendingReceiver<mojom::URLLoaderFactory> receiver,
    const cors::OriginAccessList* origin_access_list,
    PrefetchCache* cache)
    : ignore_factory_reset_(params->ignore_factory_reset),
      next_(std::make_unique<cors::CorsURLLoaderFactory>(
          context,
          std::move(params),
          std::move(resource_scheduler_client),
          origin_access_list,
          this)),
      context_(context),
      cache_(cache),
      use_matches_(base::FeatureList::IsEnabled(
          features::kNetworkContextPrefetchUseMatches)) {
  receivers_.Add(this, std::move(receiver));
  // This use of base::Unretained() is safe because `receivers_` won't call the
  // disconnect handler after it has been destroyed.
  receivers_.set_disconnect_handler(base::BindRepeating(
      &PrefetchMatchingURLLoaderFactory::OnDisconnect, base::Unretained(this)));
}

https://source.chromium.org/chromium/chromium/src/+/main:services/network/cors/cors_url_loader_factory.cc;l=212?q=CorsURLLoaderFactory::CorsURLLoaderFactory&ss=chromium%2Fchromium%2Fsrc

CorsURLLoaderFactory::CorsURLLoaderFactory(
    NetworkContext* context,
    mojom::URLLoaderFactoryParamsPtr params,
    scoped_refptr<ResourceSchedulerClient> resource_scheduler_client,
    const OriginAccessList* origin_access_list,
    PrefetchMatchingURLLoaderFactory* owner)
    : context_(context),
      is_trusted_(params->is_trusted),
      disable_web_security_(params->disable_web_security),
      process_id_(params->process_id),
      request_initiator_origin_lock_(params->request_initiator_origin_lock),
      ignore_isolated_world_origin_(params->ignore_isolated_world_origin),
      trust_token_issuance_policy_(params->trust_token_issuance_policy),
      trust_token_redemption_policy_(params->trust_token_redemption_policy),
      isolation_info_(params->isolation_info),
      automatically_assign_isolation_info_(
          params->automatically_assign_isolation_info),
      debug_tag_(params->debug_tag),
      cross_origin_embedder_policy_(
          params->client_security_state
              ? params->client_security_state->cross_origin_embedder_policy
              : CrossOriginEmbedderPolicy()),
      coep_reporter_(std::move(params->coep_reporter)),
      dip_reporter_(std::move(params->dip_reporter)),
      client_security_state_(params->client_security_state.Clone()),
      url_loader_network_service_observer_(
          std::move(params->url_loader_network_observer)),
      shared_dictionary_observer_(
          std::move(params->shared_dictionary_observer)),
      require_cross_site_request_for_cookies_(
          params->require_cross_site_request_for_cookies),
      factory_cookie_setting_overrides_(params->cookie_setting_overrides),
      devtools_cookie_setting_overrides_(
          params->devtools_cookie_setting_overrides),
      origin_access_list_(origin_access_list),
      owner_(owner) {
  TRACE_EVENT("loading", "CorsURLLoaderFactory::CorsURLLoaderFactory",
              perfetto::Flow::FromPointer(this));
  DCHECK(context_);
  DCHECK(origin_access_list_);
  DCHECK_NE(mojom::kInvalidProcessId, process_id_);
  DCHECK_EQ(net::IsolationInfo::RequestType::kOther,
            params->isolation_info.request_type());
  if (context_->url_request_context()->bound_network() !=
      net::handles::kInvalidNetworkHandle) {
    // CorsURLLoaderFactories bound to a network allow CORS preflight load
    // options (see CorsURLLoaderFactory::IsCorsPreflighLoadOptionAllowed for
    // the rationale). To prevent security issues, such a factory must not be
    // issuing CORS preflight requests itself. This is, for example, to prevent
    // compromised renderers from using a CorsURLLoaderFactory that doesn't
    // trigger CORS preflight requests, but also does not fail requests which
    // represent CORS preflights. In other words, these two things are mutually
    // exclusive and a CorsURLLoaderFactory must either:
    // 1. Perform CORS, but fail loads that represent CORS preflight requests
    // 2. Allow loads that represent CORS preflight requests, but do not perform
    //    CORS
    CHECK(disable_web_security_);
  }
  if (params->automatically_assign_isolation_info) {
    DCHECK(params->isolation_info.IsEmpty());
    // Only the browser process is currently permitted to use automatically
    // assigned IsolationInfo, to prevent cross-site information leaks.
    DCHECK_EQ(mojom::kBrowserProcessId, process_id_);
  }

  if (context_->GetSharedDictionaryManager() && client_security_state_ &&
      client_security_state_->is_web_secure_context) {
    std::optional<net::SharedDictionaryIsolationKey> isolation_key =
        net::SharedDictionaryIsolationKey::MaybeCreate(params->isolation_info);
    if (isolation_key) {
      shared_dictionary_storage_ =
          context_->GetSharedDictionaryManager()->GetStorage(*isolation_key);
    }
  }

  auto factory_override = std::move(params->factory_override);
  auto network_loader_factory = std::make_unique<network::URLLoaderFactory>(
      context, std::move(params), std::move(resource_scheduler_client), this);

  if (factory_override) {
    DCHECK(factory_override->overriding_factory);
    factory_override_ = std::make_unique<FactoryOverride>(
        std::move(factory_override), std::move(network_loader_factory));
  } else {
    network_loader_factory_ = std::move(network_loader_factory);
  }
}

https://source.chromium.org/chromium/chromium/src/+/main:services/network/url_loader_factory.cc;l=85?q=URLLoaderFactory::URLLoaderFactory&ss=chromium%2Fchromium%2Fsrc

URLLoaderFactory::URLLoaderFactory(
    NetworkContext* context,
    mojom::URLLoaderFactoryParamsPtr params,
    scoped_refptr<ResourceSchedulerClient> resource_scheduler_client,
    cors::CorsURLLoaderFactory* cors_url_loader_factory)
    : context_(context),
      params_(std::move(params)),
      resource_scheduler_client_(std::move(resource_scheduler_client)),
      header_client_(std::move(params_->header_client)),
      cors_url_loader_factory_(cors_url_loader_factory),
      cookie_observer_(std::move(params_->cookie_observer)),
      trust_token_observer_(std::move(params_->trust_token_observer)),
      devtools_observer_(std::move(params_->devtools_observer)),
      device_bound_session_observer_(
          params_->device_bound_session_observer
              ? base::MakeRefCounted<
                    RefCountedDeviceBoundSessionAccessObserverRemote>(
                    mojo::Remote<mojom::DeviceBoundSessionAccessObserver>(
                        std::move(params_->device_bound_session_observer)))
              : nullptr) {
  DCHECK(context);
  DCHECK_NE(mojom::kInvalidProcessId, params_->process_id);
  DCHECK(!params_->factory_override);
  // Only non-navigation IsolationInfos should be bound to URLLoaderFactories.
  DCHECK_EQ(net::IsolationInfo::RequestType::kOther,
            params_->isolation_info.request_type());
  DCHECK(!params_->automatically_assign_isolation_info ||
         params_->isolation_info.IsEmpty());
  DCHECK(cors_url_loader_factory_);

  if (!params_->top_frame_id) {
    params_->top_frame_id = base::UnguessableToken::Create();
  }

  if (context_->network_service()) {
    context_->network_service()->keepalive_statistics_recorder()->Register(
        *params_->top_frame_id);
  }
}

https://source.chromium.org/chromium/chromium/src/+/main:services/network/cors/cors_url_loader_factory.cc;l=357?q=CorsURLLoaderFactory::CorsURLLoaderFactory&ss=chromium%2Fchromium%2Fsrc

void CorsURLLoaderFactory::CreateLoaderAndStart(
    mojo::PendingReceiver<mojom::URLLoader> receiver,
    int32_t request_id,
    uint32_t options,
    ResourceRequest& resource_request,
    mojo::PendingRemote<mojom::URLLoaderClient> client,
    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
  TRACE_EVENT("loading", "CorsURLLoaderFactory::CreateLoaderAndStart",
              perfetto::Flow::FromPointer(this));

  std::optional<base::ElapsedTimer> timer;
  std::optional<base::ElapsedThreadTimer> thread_timer;

  if (metrics_subsampler_.ShouldSample(0.001)) {
    timer.emplace();
    if (base::ThreadTicks::IsSupported()) {
      thread_timer.emplace();
    }
  }

  debug::ScopedResourceRequestCrashKeys request_crash_keys(resource_request);
  SCOPED_CRASH_KEY_NUMBER("net", "traffic_annotation_hash",
                          traffic_annotation.unique_id_hash_code);
  SCOPED_CRASH_KEY_STRING64("network", "factory_debug_tag", debug_tag_);

  if (!IsValidRequest(resource_request, options)) {
    mojo::Remote<mojom::URLLoaderClient>(std::move(client))
        ->OnComplete(URLLoaderCompletionStatus(net::ERR_INVALID_ARGUMENT));
    return;
  }

  if (resource_request.destination ==
      network::mojom::RequestDestination::kWebBundle) {
    DCHECK(resource_request.web_bundle_token_params.has_value());

    mojo::PendingRemote<mojom::DevToolsObserver> devtools_observer;
    if (resource_request.devtools_request_id.has_value()) {
      devtools_observer = GetDevToolsObserver(resource_request);
    }

    base::WeakPtr<WebBundleURLLoaderFactory> web_bundle_url_loader_factory =
        context_->GetWebBundleManager().CreateWebBundleURLLoaderFactory(
            resource_request.url, *resource_request.web_bundle_token_params,
            process_id_, std::move(devtools_observer),
            resource_request.devtools_request_id, cross_origin_embedder_policy_,
            coep_reporter());
    client = web_bundle_url_loader_factory->MaybeWrapURLLoaderClient(
        std::move(client));
    if (!client) {
      return;
    }
  }

  mojom::URLLoaderFactory* const inner_url_loader_factory =
      factory_override_ ? factory_override_->get()
                        : network_loader_factory_.get();
  DCHECK(inner_url_loader_factory);

  const net::IsolationInfo* isolation_info_ptr = &isolation_info_;
  auto isolation_info = url_loader_util::GetIsolationInfo(
      isolation_info_, automatically_assign_isolation_info_, resource_request);
  if (isolation_info.has_value()) {
    isolation_info_ptr = &isolation_info.value();
  }

  // Check if the initiator's network access has been revoked.
  // This check is only relevant if there is a partition nonce in the
  // isolation info. (All requests originating from a fenced frame have a
  // nonce specified.)
  if (isolation_info.has_value() && isolation_info->nonce().has_value() &&
      !context_->IsNetworkForNonceAndUrlAllowed(*isolation_info->nonce(),
                                                resource_request.url)) {
    mojo::Remote<mojom::URLLoaderClient>(std::move(client))
        ->OnComplete(
            URLLoaderCompletionStatus(net::ERR_NETWORK_ACCESS_REVOKED));
    return;
  }

  if (!disable_web_security_) {
    mojo::PendingRemote<mojom::DevToolsObserver> devtools_observer;
    const bool always_clone = !base::FeatureList::IsEnabled(
        network::features::kCloneDevToolsConnectionOnlyIfRequested);
    if (always_clone || resource_request.devtools_request_id.has_value()) {
      devtools_observer = GetDevToolsObserver(resource_request);
    }

    scoped_refptr<SharedDictionaryStorage> shared_dictionary_storage =
        shared_dictionary_storage_;
    if (context_->GetSharedDictionaryManager() &&
        IsTrustedNavigationRequestFromSecureContext(resource_request)) {
      // For trusted navigation requests, we need to get a storage using
      // `isolation_info_ptr`.
      std::optional<net::SharedDictionaryIsolationKey> isolation_key =
          net::SharedDictionaryIsolationKey::MaybeCreate(*isolation_info_ptr);
      if (isolation_key) {
        shared_dictionary_storage =
            context_->GetSharedDictionaryManager()->GetStorage(*isolation_key);
      }
    }

    std::unique_ptr<CorsURLLoader> loader;
    if (base::FeatureList::IsEnabled(
            network::features::kAvoidResourceRequestCopies)) {
      loader = std::make_unique<CorsURLLoader>(
          std::move(receiver), process_id_, request_id, options,
          base::BindOnce(&CorsURLLoaderFactory::DestroyCorsURLLoader,
                         base::Unretained(this)),
          std::move(resource_request), ignore_isolated_world_origin_,
          factory_override_ &&
              factory_override_->ShouldSkipCorsEnabledSchemeCheck(),
          std::move(client), traffic_annotation, inner_url_loader_factory,
          factory_override_ ? nullptr : network_loader_factory_.get(),
          origin_access_list_, GetAllowAnyCorsExemptHeaderForBrowser(),
          *isolation_info_ptr, std::move(devtools_observer),
          client_security_state_.get(), &url_loader_network_service_observer_,
          cross_origin_embedder_policy_, shared_dictionary_storage,
          shared_dictionary_observer_ ? shared_dictionary_observer_.get()
                                      : nullptr,
          context_, factory_cookie_setting_overrides_,
          devtools_cookie_setting_overrides_);
    } else {
      loader = std::make_unique<CorsURLLoader>(
          std::move(receiver), process_id_, request_id, options,
          base::BindOnce(&CorsURLLoaderFactory::DestroyCorsURLLoader,
                         base::Unretained(this)),
          resource_request, ignore_isolated_world_origin_,
          factory_override_ &&
              factory_override_->ShouldSkipCorsEnabledSchemeCheck(),
          std::move(client), traffic_annotation, inner_url_loader_factory,
          factory_override_ ? nullptr : network_loader_factory_.get(),
          origin_access_list_, GetAllowAnyCorsExemptHeaderForBrowser(),
          *isolation_info_ptr, std::move(devtools_observer),
          client_security_state_.get(), &url_loader_network_service_observer_,
          cross_origin_embedder_policy_, shared_dictionary_storage,
          shared_dictionary_observer_ ? shared_dictionary_observer_.get()
                                      : nullptr,
          context_, factory_cookie_setting_overrides_,
          devtools_cookie_setting_overrides_);
    }
    auto* raw_loader = loader.get();
    OnCorsURLLoaderCreated(std::move(loader));
    raw_loader->Start();
  } else {
    inner_url_loader_factory->CreateLoaderAndStart(
        std::move(receiver), request_id, options, resource_request,
        std::move(client), traffic_annotation);
  }

  if (timer.has_value()) {
    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
        "NetworkService.CorsURLLoaderFactory.CreateLoaderAndStart2.Duration",
        timer->Elapsed(), base::Microseconds(1), base::Milliseconds(16), 100);
  }
  if (thread_timer.has_value()) {
    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
        "NetworkService.CorsURLLoaderFactory.CreateLoaderAndStart2."
        "ThreadDuration",
        thread_timer->Elapsed(), base::Microseconds(1), base::Milliseconds(16),
        100);
  }
}

このメソッドでraw_loader->Start()を呼んでいる箇所がある

https://source.chromium.org/chromium/chromium/src/+/main:services/network/cors/cors_url_loader.cc;l=293;bpv=1;bpt=1?q=CorsURLLoader::CorsURLLoader&ss=chromium%2Fchromium%2Fsrc

CorsURLLoader::CorsURLLoader(
    mojo::PendingReceiver<mojom::URLLoader> loader_receiver,
    int32_t process_id,
    int32_t request_id,
    uint32_t options,
    DeleteCallback delete_callback,
    ResourceRequest resource_request,
    bool ignore_isolated_world_origin,
    bool skip_cors_enabled_scheme_check,
    mojo::PendingRemote<mojom::URLLoaderClient> client,
    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
    mojom::URLLoaderFactory* network_loader_factory,
    URLLoaderFactory* sync_network_loader_factory,
    const OriginAccessList* origin_access_list,
    bool allow_any_cors_exempt_header,
    const net::IsolationInfo& isolation_info,
    mojo::PendingRemote<mojom::DevToolsObserver> devtools_observer,
    const mojom::ClientSecurityState* factory_client_security_state,
    mojo::Remote<mojom::URLLoaderNetworkServiceObserver>*
        url_loader_network_service_observer,
    const CrossOriginEmbedderPolicy& cross_origin_embedder_policy,
    scoped_refptr<SharedDictionaryStorage> shared_dictionary_storage,
    raw_ptr<mojom::SharedDictionaryAccessObserver> shared_dictionary_observer,
    NetworkContext* context,
    net::CookieSettingOverrides factory_cookie_setting_overrides,
    net::CookieSettingOverrides devtools_cookie_setting_overrides)
    : receiver_(this, std::move(loader_receiver)),
      process_id_(process_id),
      request_id_(request_id),
      options_(options),
      delete_callback_(std::move(delete_callback)),
      network_loader_factory_(network_loader_factory),
      sync_network_loader_factory_(sync_network_loader_factory),
      request_(std::move(resource_request)),
      forwarding_client_(std::move(client)),
      traffic_annotation_(traffic_annotation),
      origin_access_list_(origin_access_list),
      skip_cors_enabled_scheme_check_(skip_cors_enabled_scheme_check),
      allow_any_cors_exempt_header_(allow_any_cors_exempt_header),
      isolation_info_(isolation_info),
      factory_client_security_state_(factory_client_security_state),
      url_loader_network_service_observer_(url_loader_network_service_observer),
      cross_origin_embedder_policy_(cross_origin_embedder_policy),
      devtools_observer_(std::move(devtools_observer)),
      weak_devtools_observer_factory_(&devtools_observer_),
      // CORS preflight related events are logged in a series of URL_REQUEST
      // logs.
      net_log_(net::NetLogWithSource::Make(net::NetLog::Get(),
                                           net::NetLogSourceType::URL_REQUEST)),
      context_(context),
      shared_dictionary_storage_(std::move(shared_dictionary_storage)),
      shared_dictionary_observer_(shared_dictionary_observer),
      factory_cookie_setting_overrides_(factory_cookie_setting_overrides),
      devtools_cookie_setting_overrides_(devtools_cookie_setting_overrides) {
  TRACE_EVENT("loading", "CorsURLLoader::CorsURLLoader",
              net::NetLogWithSourceToFlow(net_log_), "url", request_.url.spec(),
              "process_id", process_id_, "request_id", request_id_,
              "traffic_annotation_id", traffic_annotation_.unique_id_hash_code);
  CHECK(url_loader_network_service_observer_ != nullptr);
  if (ignore_isolated_world_origin)
    request_.isolated_world_origin = std::nullopt;

  receiver_.set_disconnect_handler(
      base::BindOnce(&CorsURLLoader::OnMojoDisconnect, base::Unretained(this)));
  request_.net_log_create_info = net_log_.source();
  DCHECK(network_loader_factory_);
  DCHECK(origin_access_list_);
  SetCorsFlagIfNeeded();

  if (shared_dictionary_storage_) {
    if (request_.mode != mojom::RequestMode::kNoCors) {
      request_.load_flags |= net::LOAD_CAN_USE_SHARED_DICTIONARY;
    } else if (request_.request_initiator &&
               request_.request_initiator->IsSameOriginWith(request_.url)) {
      // For no-cors mode requests, we can use shared dictionaries only for same
      // origin requests. When redirected to another origin,
      // net::URLRequest::Redirect() disables the LOAD_CAN_USE_SHARED_DICTIONARY
      // flag.
      request_.load_flags |= net::LOAD_CAN_USE_SHARED_DICTIONARY;
      request_.load_flags |=
          net::LOAD_DISABLE_SHARED_DICTIONARY_AFTER_CROSS_ORIGIN_REDIRECT;
    }
    // This is intended to load the dictionary as soon as possible. Without
    // this, the dictionary will be loaded from the disk when
    // `HttpNetworkTransaction` builds the request header just before sending it
    // to the server.
    shared_dictionary_storage_->GetDictionary(
        request_.url, request_.destination,
        base::BindOnce(
            [](base::WeakPtr<CorsURLLoader> loader,
               scoped_refptr<net::SharedDictionary> shared_dictionary) {
              if (loader) {
                loader->shared_dictionary_ = std::move(shared_dictionary);
              }
            },
            weak_factory_.GetWeakPtr()));
  }
}

https://source.chromium.org/chromium/chromium/src/+/main:services/network/cors/cors_url_loader.cc;l=293?q=CorsURLLoader::CorsURLLoader&ss=chromium%2Fchromium%2Fsrc

void CorsURLLoader::Start() {
  TRACE_EVENT("loading", "CorsURLLoader::Start",
              net::NetLogWithSourceToFlow(net_log_));
  if (fetch_cors_flag_ && IsCorsEnabledRequestMode(request_.mode)) {
    // Username and password should be stripped in a CORS-enabled request.
    if (request_.url.has_username() || request_.url.has_password()) {
      GURL::Replacements replacements;
      replacements.SetUsernameStr("");
      replacements.SetPasswordStr("");
      request_.url = request_.url.ReplaceComponents(replacements);
    }
  }

  last_response_url_ = request_.url;

  net_log_.BeginEvent(net::NetLogEventType::CORS_REQUEST,
                      [&] { return NetLogCorsURLLoaderStartParams(request_); });
  StartRequest();
}

StartRequest https://source.chromium.org/chromium/chromium/src/+/main:services/network/cors/cors_url_loader.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;l=885?q=CorsURLLoader::CorsURLLoader&ss=chromium%2Fchromium%2Fsrc

void CorsURLLoader::StartRequest() {
  TRACE_EVENT("loading", "CorsURLLoader::StartRequest",
              net::NetLogWithSourceToFlow(net_log_));
  // All results should be reported to `forwarding_client_` as part of a
  // `URLResponseHead`, then `pna_preflight_result_` reset to `kNone`.
  CHECK_EQ(pna_preflight_result_,
           mojom::PrivateNetworkAccessPreflightResult::kNone);

  if (fetch_cors_flag_ && !skip_cors_enabled_scheme_check_ &&
      !base::Contains(url::GetCorsEnabledSchemes(), request_.url.scheme())) {
    HandleComplete(URLLoaderCompletionStatus(
        CorsErrorStatus(mojom::CorsError::kCorsDisabledScheme)));
    return;
  }

  auto should_include_origin_header = [&]() -> bool {
    if (!request_.request_initiator) {
      return false;
    }

    if (request_.credentials_mode == mojom::CredentialsMode::kInclude &&
        GetStorageAccessStatus() ==
            net::cookie_util::StorageAccessStatus::kInactive) {
      // Lower layers will add the Sec-Fetch-Storage-Access header, and the
      // server may respond with a "retry" header. The server needs to know the
      // origin in that event.
      return true;
    }

    // If the `CORS flag` is set, `httpRequest`’s method is neither `GET` nor
    // `HEAD`, or `httpRequest`’s mode is "websocket", then append
    // `Origin`/the result of serializing a request origin with `httpRequest`,
    // to `httpRequest`’s header list.
    //
    // We exclude navigation requests to keep the existing behavior.
    // TODO(yhirano): Reconsider this.
    if (request_.mode == network::mojom::RequestMode::kNavigate) {
      return false;
    }
    if (fetch_cors_flag_) {
      return true;
    }
    return request_.method != net::HttpRequestHeaders::kGetMethod &&
           request_.method != net::HttpRequestHeaders::kHeadMethod;
  };

  if (should_include_origin_header()) {
    if (tainted_) {
      request_.headers.SetHeader(net::HttpRequestHeaders::kOrigin,
                                 url::Origin().Serialize());
    } else {
      request_.headers.SetHeader(net::HttpRequestHeaders::kOrigin,
                                 request_.request_initiator->Serialize());
    }
  }

  if (fetch_cors_flag_ && request_.mode == mojom::RequestMode::kSameOrigin) {
    DCHECK(request_.request_initiator);
    HandleComplete(URLLoaderCompletionStatus(
        CorsErrorStatus(mojom::CorsError::kDisallowedByMode)));
    return;
  }

  response_tainting_ = CalculateResponseTainting(
      request_.url, request_.mode, request_.request_initiator,
      request_.isolated_world_origin, fetch_cors_flag_, tainted_,
      *origin_access_list_);

  // Note that even when `needs_preflight` holds we might not make a preflight
  // request. This happens when `fetch_cors_flag_` is false, e.g. when the
  // origin of the url is equal to the origin of the request, and the preflight
  // reason is not `kPrivateNetworkAccess`. In the case of a private network
  // access we always send a preflight, even for CORS-disabled requests.
  //
  // See the first step of the HTTP-no-service-worker fetch algorithm defined in
  // the Private Network Access spec:
  // https://wicg.github.io/private-network-access/#http-no-service-worker-fetch
  std::optional<PreflightRequiredReason> needs_preflight =
      NeedsPreflight(request_);
  bool preflight_required =
      needs_preflight.has_value() &&
      (fetch_cors_flag_ ||
       *needs_preflight == PreflightRequiredReason::kPrivateNetworkAccess);
  net_log_.AddEvent(net::NetLogEventType::CHECK_CORS_PREFLIGHT_REQUIRED, [&] {
    return NetLogPreflightRequiredParams(needs_preflight);
  });

  has_authorization_covered_by_wildcard_ = false;
  if (!preflight_required) {
    StartNetworkRequest();
    return;
  }

  preflight_mode_.Clear();
  if (fetch_cors_flag_ && NeedsCorsPreflight(request_).has_value()) {
    preflight_mode_.Put(PreflightController::PreflightType::kCors);
  }
  if (NeedsPrivateNetworkAccessPreflight(request_).has_value()) {
    preflight_mode_.Put(
        PreflightController::PreflightType::kPrivateNetworkAccess);
  }
  CHECK(!preflight_mode_.empty());

  // Since we're doing a preflight, we won't reuse the original request. Cancel
  // it now to free up the socket.
  network_loader_.reset();

  mojo::PendingRemote<mojom::URLLoaderNetworkServiceObserver> remote_observer;

  if (needs_preflight.has_value() &&
      *needs_preflight == PreflightRequiredReason::kPrivateNetworkAccess) {
    // TODO(crbug.com/40229602): Create a base function and clean up all
    // need_pna_permission check in the code base.
    const mojom::ClientSecurityState* state = GetClientSecurityState();
    const bool needs_pna_permission =
        state && PrivateNetworkAccessChecker::NeedPermission(
                     request_.url, state->is_web_secure_context,
                     request_.required_ip_address_space);
    if (needs_pna_permission &&
        url_loader_network_service_observer_->is_bound()) {
      // Fail the request if `targetAddressSpace` on fetch option is not the
      // same as the real target address space.
      if (request_.required_ip_address_space !=
          request_.target_ip_address_space) {
        HandleComplete(URLLoaderCompletionStatus(
            CorsErrorStatus(mojom::CorsError::kInvalidPrivateNetworkAccess)));
        return;
      }
      (*url_loader_network_service_observer_)
          ->Clone(remote_observer.InitWithNewPipeAndPassReceiver());
    }
  }
  context_->cors_preflight_controller()->PerformPreflightCheck(
      base::BindOnce(&CorsURLLoader::OnPreflightRequestComplete,
                     weak_factory_.GetWeakPtr()),
      request_,
      PreflightController::WithTrustedHeaderClient(
          options_ & mojom::kURLLoadOptionUseHeaderClient),
      context_->cors_non_wildcard_request_headers_support(),
      GetPrivateNetworkAccessPreflightBehavior(
          request_.required_ip_address_space),
      tainted_, net::NetworkTrafficAnnotationTag(traffic_annotation_),
      network_loader_factory_, isolation_info_, CloneClientSecurityState(),
      weak_devtools_observer_factory_.GetWeakPtr(), net_log_,
      context_->acam_preflight_spec_conformant(), std::move(remote_observer),
      preflight_mode_);
}

StartNetworkRequest関数(!prefilight_required) https://source.chromium.org/chromium/chromium/src/+/main:services/network/cors/cors_url_loader.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;l=1163?q=CorsURLLoader::CorsURLLoader&ss=chromium%2Fchromium%2Fsrc

void CorsURLLoader::StartNetworkRequest() {
  TRACE_EVENT("loading", "CorsURLLoader::StartNetworkRequest",
              net::NetLogWithSourceToFlow(net_log_));
  // Here we overwrite the credentials mode sent to URLLoader because
  // network::URLLoader doesn't understand |kSameOrigin|.
  // TODO(crbug.com/40619226): Fix this.
  auto original_credentials_mode = request_.credentials_mode;
  if (original_credentials_mode == mojom::CredentialsMode::kSameOrigin) {
    request_.credentials_mode =
        CalculateCredentialsFlag(original_credentials_mode, response_tainting_)
            ? mojom::CredentialsMode::kInclude
            : mojom::CredentialsMode::kOmit;
  }

  // Binding |this| as an unretained pointer is safe because
  // |network_client_receiver_| shares this object's lifetime.
  network_loader_.reset();

  network_loader_start_time_ = base::TimeTicks::Now();

  if (sync_network_loader_factory_) {
    sync_network_loader_factory_->CreateLoaderAndStartWithSyncClient(
        network_loader_.BindNewPipeAndPassReceiver(), request_id_, options_,
        request_, network_client_receiver_.BindNewPipeAndPassRemote(),
        sync_client_receiver_factory_.GetWeakPtr(), traffic_annotation_);
  } else {
    network_loader_factory_->CreateLoaderAndStart(
        network_loader_.BindNewPipeAndPassReceiver(), request_id_, options_,
        request_, network_client_receiver_.BindNewPipeAndPassRemote(),
        traffic_annotation_);
  }
  network_client_receiver_.set_disconnect_handler(base::BindOnce(
      &CorsURLLoader::OnNetworkClientMojoDisconnect, base::Unretained(this)));

  request_.credentials_mode = original_credentials_mode;
}

https://source.chromium.org/chromium/chromium/src/+/main:services/network/url_loader_factory.cc;l=132;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1

void URLLoaderFactory::CreateLoaderAndStart(
    mojo::PendingReceiver<mojom::URLLoader> receiver,
    int32_t request_id,
    uint32_t options,
    const ResourceRequest& resource_request,
    mojo::PendingRemote<mojom::URLLoaderClient> client,
    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
  CreateLoaderAndStartWithSyncClient(std::move(receiver), request_id, options,
                                     resource_request, std::move(client),
                                     /* sync_client= */ nullptr,
                                     traffic_annotation);
}

https://source.chromium.org/chromium/chromium/src/+/main:services/network/url_loader_factory.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=200

void URLLoaderFactory::CreateLoaderAndStartWithSyncClient(
    mojo::PendingReceiver<mojom::URLLoader> receiver,
    int32_t request_id,
    uint32_t options,
    const ResourceRequest& resource_request,
    mojo::PendingRemote<mojom::URLLoaderClient> client,
    base::WeakPtr<mojom::URLLoaderClient> sync_client,
    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
  // Requests with |trusted_params| when params_->is_trusted is not set should
  // have been rejected at the CorsURLLoader layer.
  DCHECK(!resource_request.trusted_params || params_->is_trusted);

  if (resource_request.web_bundle_token_params.has_value() &&
      resource_request.destination !=
          network::mojom::RequestDestination::kWebBundle) {
    mojo::Remote<mojom::TrustedHeaderClient> trusted_header_client;
    if (header_client_ && (options & mojom::kURLLoadOptionUseHeaderClient)) {
      // CORS preflight request must not come here.
      DCHECK(!(options & mojom::kURLLoadOptionAsCorsPreflight));
      header_client_->OnLoaderCreated(
          request_id, trusted_header_client.BindNewPipeAndPassReceiver());
    }

    // Load a subresource from a WebBundle.
    context_->GetWebBundleManager().StartSubresourceRequest(
        std::move(receiver), resource_request, std::move(client),
        params_->process_id, std::move(trusted_header_client));
    return;
  }

  base::WeakPtr<KeepaliveStatisticsRecorder> keepalive_statistics_recorder;
  if (context_->network_service()) {
    keepalive_statistics_recorder = context_->network_service()
                                        ->keepalive_statistics_recorder()
                                        ->AsWeakPtr();
  }

  bool exhausted = false;
  if (!context_->CanCreateLoader(params_->process_id)) {
    exhausted = true;
  }

  int keepalive_request_size = 0;
  if (resource_request.keepalive) {
    base::UmaHistogramEnumeration(
        "FetchKeepAlive.Requests2.Network",
        internal::FetchKeepAliveRequestNetworkMetricType::kOnCreate);
  }
  if (resource_request.keepalive && keepalive_statistics_recorder) {
    const size_t url_size = resource_request.url.spec().size();
    size_t headers_size = 0;

    net::HttpRequestHeaders merged_headers = resource_request.headers;
    merged_headers.MergeFrom(resource_request.cors_exempt_headers);

    for (const auto& pair : merged_headers.GetHeaderVector()) {
      headers_size += (pair.key.size() + pair.value.size());
    }

    keepalive_request_size = url_size + headers_size;

    const auto& top_frame_id = *params_->top_frame_id;
    const auto& recorder = *keepalive_statistics_recorder;

    if (!exhausted) {
      if (recorder.num_inflight_requests() >= kMaxKeepaliveConnections ||
          recorder.NumInflightRequestsPerTopLevelFrame(top_frame_id) >=
              kMaxKeepaliveConnectionsPerTopLevelFrame ||
          recorder.GetTotalRequestSizePerTopLevelFrame(top_frame_id) +
                  keepalive_request_size >
              kMaxTotalKeepaliveRequestSize) {
        exhausted = true;
      }
    }
  }

  if (exhausted) {
    URLLoaderCompletionStatus status;
    status.error_code = net::ERR_INSUFFICIENT_RESOURCES;
    status.exists_in_cache = false;
    status.completion_time = base::TimeTicks::Now();
    mojo::Remote<mojom::URLLoaderClient>(std::move(client))->OnComplete(status);
    return;
  }

  std::unique_ptr<TrustTokenRequestHelperFactory> trust_token_factory;
  if (resource_request.trust_token_params) {
    trust_token_factory = std::make_unique<TrustTokenRequestHelperFactory>(
        context_->trust_token_store(),
        context_->network_service()->trust_token_key_commitments(),
        // It's safe to use Unretained because |context_| is guaranteed to
        // outlive the URLLoader that will own this
        // TrustTokenRequestHelperFactory.
        base::BindRepeating(&NetworkContext::client,
                            base::Unretained(context_)),
        // It's safe to access cookie manager for |context_| here because
        // NetworkContext::CookieManager outlives the URLLoaders associated with
        // the NetworkContext.
        base::BindRepeating(
            [](NetworkContext* context, const GURL& resource_request_url,
               const GURL& top_frame_origin) {
              // Private state tokens will be blocked if the user has either
              // disabled the anti-abuse content setting or blocked the top
              // level site or issuer from storing data through the cookie
              // content settings.
              return (
                  // PST is not disabled through settings.
                  !context->are_trust_tokens_blocked() &&
                  // and top frame is not blocked.
                  context->cookie_manager()
                      ->cookie_settings()
                      .ArePrivateStateTokensAllowed(top_frame_origin) &&
                  // and issuer is not blocked.
                  context->cookie_manager()
                      ->cookie_settings()
                      .ArePrivateStateTokensAllowed(resource_request_url));
            },
            base::Unretained(context_), resource_request.url,
            params_->isolation_info.top_frame_origin()
                .value_or(url::Origin())
                .GetURL()));
  }

  std::unique_ptr<SharedDictionaryAccessChecker> shared_dictionary_checker;
  if (context_->GetSharedDictionaryManager()) {
    if (resource_request.trusted_params &&
        resource_request.trusted_params->shared_dictionary_observer) {
      shared_dictionary_checker =
          std::make_unique<SharedDictionaryAccessChecker>(
              *context_, std::move(const_cast<mojo::PendingRemote<
                                       mojom::SharedDictionaryAccessObserver>&>(
                             resource_request.trusted_params
                                 ->shared_dictionary_observer)));
    } else {
      shared_dictionary_checker =
          std::make_unique<SharedDictionaryAccessChecker>(
              *context_,
              cors_url_loader_factory_->GetSharedDictionaryAccessObserver());
    }
  }

  auto cookie_observer = CreateObserverWrapper<mojom::CookieAccessObserver>(
      resource_request.trusted_params,
      &ResourceRequest::TrustedParams::cookie_observer, cookie_observer_);
  auto trust_token_observer =
      CreateObserverWrapper<mojom::TrustTokenAccessObserver>(
          resource_request.trusted_params,
          &ResourceRequest::TrustedParams::trust_token_observer,
          trust_token_observer_);
  auto url_loader_network_observer =
      CreateObserverWrapper<mojom::URLLoaderNetworkServiceObserver>(
          resource_request.trusted_params,
          &ResourceRequest::TrustedParams::url_loader_network_observer,
          GetURLLoaderNetworkServiceObserver());
  auto devtools_observer = CreateObserverWrapper<mojom::DevToolsObserver>(
      resource_request.trusted_params,
      &ResourceRequest::TrustedParams::devtools_observer, devtools_observer_);
  auto device_bound_session_observer =
      CreateObserverWrapper<mojom::DeviceBoundSessionAccessObserver>(
          resource_request.trusted_params,
          &ResourceRequest::TrustedParams::device_bound_session_observer,
          device_bound_session_observer_
              ? device_bound_session_observer_->data.get()
              : nullptr);

  mojo::PendingRemote<mojom::AcceptCHFrameObserver> accept_ch_frame_observer;
  if (resource_request.trusted_params &&
      resource_request.trusted_params->accept_ch_frame_observer) {
    accept_ch_frame_observer = std::move(
        const_cast<mojo::PendingRemote<mojom::AcceptCHFrameObserver>&>(
            resource_request.trusted_params->accept_ch_frame_observer));
  }

  auto loader = std::make_unique<URLLoader>(
      *this,
      base::BindOnce(&cors::CorsURLLoaderFactory::DestroyURLLoader,
                     base::Unretained(cors_url_loader_factory_)),
      std::move(receiver), options, resource_request, std::move(client),
      std::move(sync_client),
      static_cast<net::NetworkTrafficAnnotationTag>(traffic_annotation),
      request_id, keepalive_request_size,
      std::move(keepalive_statistics_recorder), std::move(trust_token_factory),
      context_->GetSharedDictionaryManager(),
      std::move(shared_dictionary_checker), std::move(cookie_observer),
      std::move(trust_token_observer), std::move(url_loader_network_observer),
      std::move(devtools_observer), std::move(device_bound_session_observer),
      std::move(accept_ch_frame_observer),
      resource_request.shared_storage_writable_eligible,
      *context_->GetSharedResourceChecker());

  cors_url_loader_factory_->OnURLLoaderCreated(std::move(loader));
}

https://source.chromium.org/chromium/chromium/src/+/main:services/network/cors/cors_url_loader_factory.h;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;l=164

  template <class T>
  void OnLoaderCreated(
      std::unique_ptr<T> loader,
      std::set<std::unique_ptr<T>, base::UniquePtrComparator>& loaders) {
    context_->LoaderCreated(process_id_);
    loaders.insert(std::move(loader));
  }

URLLoaderFactoryはURLLoaderContextを継承している。 https://source.chromium.org/chromium/chromium/src/+/main:services/network/url_loader_factory.h;l=50;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1

https://source.chromium.org/chromium/chromium/src/+/main:services/network/url_loader.cc;l=340?q=URLLoader::URLLoader&ss=chromium%2Fchromium%2Fsrc

URLLoader::URLLoader(
    URLLoaderContext& context,
    DeleteCallback delete_callback,
    mojo::PendingReceiver<mojom::URLLoader> url_loader_receiver,
    int32_t options,
    const ResourceRequest& request,
    mojo::PendingRemote<mojom::URLLoaderClient> url_loader_client,
    base::WeakPtr<mojom::URLLoaderClient> sync_url_loader_client,
    const net::NetworkTrafficAnnotationTag& traffic_annotation,
    base::StrictNumeric<int32_t> request_id,
    int keepalive_request_size,
    base::WeakPtr<KeepaliveStatisticsRecorder> keepalive_statistics_recorder,
    std::unique_ptr<TrustTokenRequestHelperFactory> trust_token_helper_factory,
    SharedDictionaryManager* shared_dictionary_manager,
    std::unique_ptr<SharedDictionaryAccessChecker> shared_dictionary_checker,
    ObserverWrapper<mojom::CookieAccessObserver> cookie_observer,
    ObserverWrapper<mojom::TrustTokenAccessObserver> trust_token_observer,
    ObserverWrapper<mojom::URLLoaderNetworkServiceObserver>
        url_loader_network_observer,
    ObserverWrapper<mojom::DevToolsObserver> devtools_observer,
    ObserverWrapper<mojom::DeviceBoundSessionAccessObserver>
        device_bound_session_observer,
    mojo::PendingRemote<mojom::AcceptCHFrameObserver> accept_ch_frame_observer,
    bool shared_storage_writable_eligible,
    SharedResourceChecker& shared_resource_checker)
    : url_request_context_(context.GetUrlRequestContext()),
      network_context_client_(context.GetNetworkContextClient()),
      delete_callback_(std::move(delete_callback)),
      resource_type_(request.resource_type),
      is_load_timing_enabled_(request.enable_load_timing),
      factory_params_(context.GetFactoryParams()),
      coep_reporter_(context.GetCoepReporter()),
      dip_reporter_(context.GetDipReporter()),
      request_id_(request_id),
      keepalive_request_size_(keepalive_request_size),
      keepalive_(request.keepalive),
      client_security_state_(
          request.trusted_params
              ? request.trusted_params->client_security_state.Clone()
              : nullptr),
      do_not_prompt_for_login_(request.do_not_prompt_for_login),
      receiver_(this, std::move(url_loader_receiver)),
      url_loader_client_(std::move(url_loader_client),
                         std::move(sync_url_loader_client)),
      writable_handle_watcher_(FROM_HERE,
                               mojo::SimpleWatcher::ArmingPolicy::MANUAL,
                               base::SequencedTaskRunner::GetCurrentDefault()),
      peer_closed_handle_watcher_(
          FROM_HERE,
          mojo::SimpleWatcher::ArmingPolicy::MANUAL,
          base::SequencedTaskRunner::GetCurrentDefault()),
      per_factory_orb_state_(context.GetMutableOrbState()),
      devtools_request_id_(request.devtools_request_id),
      options_(PopulateOptions(options,
                               factory_params_->is_orb_enabled,
                               !!devtools_request_id())),
      request_mode_(request.mode),
      request_credentials_mode_(request.credentials_mode),
      has_user_activation_(request.trusted_params &&
                           request.trusted_params->has_user_activation),
      request_destination_(request.destination),
      expected_public_keys_(request.expected_public_keys),
      resource_scheduler_client_(context.GetResourceSchedulerClient()),
      keepalive_statistics_recorder_(std::move(keepalive_statistics_recorder)),
      fetch_window_id_(request.fetch_window_id),
      private_network_access_interceptor_(request,
                                          GetClientSecurityState(),
                                          options_),
      trust_token_interceptor_(TrustTokenUrlLoaderInterceptor::MaybeCreate(
          std::move(trust_token_helper_factory))),
      shared_dictionary_checker_(std::move(shared_dictionary_checker)),
      origin_access_list_(context.GetOriginAccessList()),
      cookie_observer_(std::move(cookie_observer)),
      trust_token_observer_(std::move(trust_token_observer)),
      url_loader_network_observer_(std::move(url_loader_network_observer)),
      devtools_observer_(std::move(devtools_observer)),
      device_bound_session_observer_(std::move(device_bound_session_observer)),
      device_bound_session_observer_shared_remote_(
          MaybeInitializeDeviceBoundSessionAccessObserverSharedRemote(
              device_bound_session_observer_,
              context)),
      shared_storage_request_helper_(
          std::make_unique<SharedStorageRequestHelper>(
              shared_storage_writable_eligible,
              url_loader_network_observer_.get())),
      ad_auction_event_record_request_helper_(
          request.attribution_reporting_eligibility,
          url_loader_network_observer_.get()),
      has_fetch_streaming_upload_body_(
          url_loader_util::HasFetchStreamingUploadBody(request)),
      accept_ch_frame_interceptor_(AcceptCHFrameInterceptor::MaybeCreate(
          std::move(accept_ch_frame_observer))),
      allow_cookies_from_browser_(
          request.trusted_params &&
          request.trusted_params->allow_cookies_from_browser),
      cookies_from_browser_(allow_cookies_from_browser_
                                ? url_loader_util::GetCookiesFromHeaders(
                                      request.headers,
                                      request.cors_exempt_headers)
                                : std::string()),
      include_request_cookies_with_response_(
          request.trusted_params &&
          request.trusted_params->include_request_cookies_with_response),
      include_load_timing_internal_info_with_response_(
          request.trusted_params.has_value()),
      provide_data_use_updates_(context.DataUseUpdatesEnabled()),
      partial_decoder_decoding_buffer_size_(net::kMaxBytesToSniff),
      permissions_policy_(request.permissions_policy) {
  DCHECK(delete_callback_);

  if (options_ & mojom::kURLLoadOptionReadAndDiscardBody) {
    if (!factory_params_->is_orb_enabled) {
      discard_buffer_ =
          base::MakeRefCounted<net::IOBufferWithSize>(kDiscardBufferSize);
    }
  }

  mojom::TrustedURLLoaderHeaderClient* url_loader_header_client =
      context.GetUrlLoaderHeaderClient();
  if (url_loader_header_client &&
      (options_ & mojom::kURLLoadOptionUseHeaderClient)) {
    if (options_ & mojom::kURLLoadOptionAsCorsPreflight) {
      url_loader_header_client->OnLoaderForCorsPreflightCreated(
          request, header_client_.BindNewPipeAndPassReceiver());
    } else {
      url_loader_header_client->OnLoaderCreated(
          request_id_, header_client_.BindNewPipeAndPassReceiver());
    }
    // Make sure the loader dies if |header_client_| has an error, otherwise
    // requests can hang.
    header_client_.set_disconnect_handler(
        base::BindOnce(&URLLoader::OnMojoDisconnect, base::Unretained(this)));
  }
  receiver_.set_disconnect_handler(
      base::BindOnce(&URLLoader::OnMojoDisconnect, base::Unretained(this)));
  url_request_ = url_request_context_->CreateRequest(
      request.url, request.priority, this, traffic_annotation,
      /*is_for_websockets=*/false, request.net_log_create_info);

  TRACE_EVENT("loading", "URLLoader::URLLoader",
              net::NetLogWithSourceToFlow(url_request_->net_log()));
  // Set up UserData (pointing to `this`) first.
  url_request_->SetUserData(kUserDataKey,
                            std::make_unique<UnownedPointer>(this));
  // Configure the main request parameters. This must happen after setting
  // UserData, as `ConfigureUrlRequest` might internally retrieve data (e.g.,
  // PermissionsPolicy) via the `url_request_`'s UserData pointer.
  url_loader_util::ConfigureUrlRequest(request, *factory_params_,
                                       *origin_access_list_, *url_request_,
                                       shared_resource_checker);
  if (context.ShouldRequireIsolationInfo()) {
    DCHECK(!url_request_->isolation_info().IsEmpty());
  }
  SetUpUrlRequestCallbacks(shared_dictionary_manager);

  throttling_token_ = network::ScopedThrottlingToken::MaybeCreate(
      url_request_->net_log().source().id, request.throttling_profile_id);

  if (keepalive_ && keepalive_statistics_recorder_) {
    keepalive_statistics_recorder_->OnLoadStarted(
        *factory_params_->top_frame_id, keepalive_request_size_);
  }

  if (request.net_log_reference_info) {
    // Log source object that created the request, if available.
    url_request_->net_log().AddEventReferencingSource(
        net::NetLogEventType::CREATED_BY,
        request.net_log_reference_info.value());
  }

  // Resolve elements from request_body and prepare upload data.
  if (request.request_body.get()) {
    OpenFilesForUpload(request);
    return;
  }

  ProcessOutboundTrustTokenInterceptor(request);
}

https://source.chromium.org/chromium/chromium/src/+/main:services/network/url_loader.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;l=701?q=URLLoader::URLLoader&ss=chromium%2Fchromium%2Fsrc

void URLLoader::ProcessOutboundTrustTokenInterceptor(
    const ResourceRequest& request) {
  // If no Trust Token parameters are specified, proceed to the next
  // interceptor.
  if (!request.trust_token_params) {
    ProcessOutboundSharedStorageInterceptor();
    return;
  }
  // If trust_token_params exist, the interceptor MUST have been created in the
  // URLLoader constructor.
  CHECK(trust_token_interceptor_);

  // Ask the interceptor if any special load flags are needed.
  url_request_->SetLoadFlags(url_request_->load_flags() |
                             trust_token_interceptor_->GetAdditionalLoadFlags(
                                 request.trust_token_params.value()));

  // Delegate the Begin phase of the Trust Token operation to the interceptor.
  // The interceptor will asynchronously handle helper creation, calling Begin,
  // and determining the outcome.
  trust_token_interceptor_->BeginOperation(
      request.trust_token_params->operation, url_request_->url(),
      url_request_->isolation_info().top_frame_origin().value_or(url::Origin()),
      url_request_->extra_request_headers(), request.trust_token_params.value(),
      url_request_->net_log(),
      // Provide a getter for the TrustTokenAccessObserver.
      base::BindOnce(
          [](base::WeakPtr<URLLoader> weak_ptr)
              -> mojom::TrustTokenAccessObserver* {
            return weak_ptr ? weak_ptr->trust_token_observer_.get() : nullptr;
          },
          weak_ptr_factory_.GetWeakPtr()),
      // Provide a getter for the DevTools reporting callback.
      base::BindOnce(
          [](base::WeakPtr<URLLoader> weak_ptr)
              -> base::OnceCallback<void(mojom::TrustTokenOperationResultPtr)> {
            if (weak_ptr && weak_ptr->devtools_observer_.get() &&
                weak_ptr->devtools_request_id_) {
              return base::BindOnce(
                  &mojom::DevToolsObserver::OnTrustTokenOperationDone,
                  base::Unretained(weak_ptr->devtools_observer_.get()),
                  *weak_ptr->devtools_request_id_);
            }
            return base::OnceCallback<void(
                mojom::TrustTokenOperationResultPtr)>();
          },
          weak_ptr_factory_.GetWeakPtr()),
      base::BindOnce(&URLLoader::OnDoneBeginningTrustTokenOperation,
                     weak_ptr_factory_.GetWeakPtr()));
}

https://source.chromium.org/chromium/chromium/src/+/main:services/network/url_loader.cc;l=678;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133?q=URLLoader::URLLoader&ss=chromium%2Fchromium%2Fsrc

void URLLoader::OnDoneBeginningTrustTokenOperation(
    base::expected<net::HttpRequestHeaders, net::Error> result) {
  // If `result` does not have a value, the operation failed or completed
  // locally.
  if (!result.has_value()) {
    // Defer calling NotifyCompleted to make sure the URLLoader
    // finishes initializing before getting deleted.
    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE,
        base::BindOnce(&URLLoader::NotifyCompleted,
                       weak_ptr_factory_.GetWeakPtr(), result.error()));
    return;
  }
  // Operation succeeded and returned headers to add/overwrite.
  // Apply the headers provided by the interceptor.
  for (const auto& header_pair : result->GetHeaderVector()) {
    url_request_->SetExtraRequestHeaderByName(
        header_pair.key, header_pair.value, /*overwrite=*/true);
  }
  // Trust Token outbound processing is done, proceed to the next interceptor.
  ProcessOutboundSharedStorageInterceptor();
}



void URLLoader::ProcessOutboundSharedStorageInterceptor() {
  DCHECK(shared_storage_request_helper_);
  shared_storage_request_helper_->ProcessOutgoingRequest(*url_request_);
  ScheduleStart();
}

void URLLoader::ScheduleStart() {
  TRACE_EVENT("loading", "URLLoader::ScheduleStart",
              net::NetLogWithSourceToFlow(url_request_->net_log()));
  bool defer = false;
  if (resource_scheduler_client_) {
    resource_scheduler_request_handle_ =
        resource_scheduler_client_->ScheduleRequest(
            !(options_ & mojom::kURLLoadOptionSynchronous), url_request_.get());
    resource_scheduler_request_handle_->set_resume_callback(
        base::BindOnce(&URLLoader::ResumeStart, base::Unretained(this)));
    resource_scheduler_request_handle_->WillStartRequest(&defer);
  }
  if (defer)
    url_request_->LogBlockedBy("ResourceScheduler");
  else
    url_request_->Start();
}

https://source.chromium.org/chromium/chromium/src/+/main:net/url_request/url_request.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;l=586

void URLRequest::Start() {
  DCHECK(delegate_);

  // We do not support credentials with a non-general
  // NetworkIsolationPartition.
  CHECK(isolation_info_.GetNetworkIsolationPartition() ==
            NetworkIsolationPartition::kGeneral ||
        !allow_credentials());

  if (status_ != OK)
    return;

  if (context_->require_network_anonymization_key()) {
    DCHECK(!isolation_info_.IsEmpty());
  }

  // Some values can be NULL, but the job factory must not be.
  DCHECK(context_->job_factory());

  // Anything that sets |blocked_by_| before start should have cleaned up after
  // itself.
  DCHECK(blocked_by_.empty());

  g_url_requests_started = true;
  response_info_.request_time = base::Time::Now();

  load_timing_info_ = LoadTimingInfo();
  load_timing_info_.request_start_time = response_info_.request_time;
  load_timing_info_.request_start = base::TimeTicks::Now();

  if (network_delegate()) {
    OnCallToDelegate(NetLogEventType::NETWORK_DELEGATE_BEFORE_URL_REQUEST);
    int error = network_delegate()->NotifyBeforeURLRequest(
        this,
        base::BindOnce(&URLRequest::BeforeRequestComplete,
                       base::Unretained(this)),
        &delegate_redirect_url_);
    // If ERR_IO_PENDING is returned, the delegate will invoke
    // |BeforeRequestComplete| later.
    if (error != ERR_IO_PENDING)
      BeforeRequestComplete(error);
    return;
  }

  StartJob(context_->job_factory()->CreateJob(this));
}

https://source.chromium.org/chromium/chromium/src/+/main:net/url_request/url_request.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;l=690

void URLRequest::StartJob(std::unique_ptr<URLRequestJob> job) {
  DCHECK(!is_pending_);
  DCHECK(!job_);
  if (is_created_from_network_anonymization_key_) {
    DCHECK(load_flags() & LOAD_DISABLE_CACHE);
    DCHECK(!allow_credentials_);
  }

  net_log_.BeginEvent(NetLogEventType::URL_REQUEST_START_JOB, [&] {
    return NetLogURLRequestStartParams(
        url(), method_, load_flags(), isolation_info_, site_for_cookies_,
        initiator_,
        upload_data_stream_ ? upload_data_stream_->identifier() : -1);
  });

  job_ = std::move(job);
  job_->SetExtraRequestHeaders(extra_request_headers_);
  job_->SetPriority(priority_);
  job_->SetRequestHeadersCallback(request_headers_callback_);
  job_->SetEarlyResponseHeadersCallback(early_response_headers_callback_);
  if (is_shared_dictionary_read_allowed_callback_) {
    job_->SetIsSharedDictionaryReadAllowedCallback(
        is_shared_dictionary_read_allowed_callback_);
  }
  job_->SetResponseHeadersCallback(response_headers_callback_);
  if (shared_dictionary_getter_) {
    job_->SetSharedDictionaryGetter(shared_dictionary_getter_);
  }

  if (upload_data_stream_.get())
    job_->SetUpload(upload_data_stream_.get());

  is_pending_ = true;
  is_redirecting_ = false;
  deferred_redirect_info_.reset();

  response_info_.was_cached = false;

  maybe_sent_cookies_.clear();
  maybe_stored_cookies_.clear();

  GURL referrer_url(referrer_);
  bool same_origin_for_metrics;

  if (referrer_url !=
      URLRequestJob::ComputeReferrerForPolicy(
          referrer_policy_, referrer_url, url(), &same_origin_for_metrics)) {
    if (!network_delegate() ||
        !network_delegate()->CancelURLRequestWithPolicyViolatingReferrerHeader(
            *this, url(), referrer_url)) {
      referrer_.clear();
    } else {
      // We need to clear the referrer anyway to avoid an infinite recursion
      // when starting the error job.
      referrer_.clear();
      net_log_.AddEventWithStringParams(NetLogEventType::CANCELLED, "source",
                                        "delegate");
      RestartWithJob(
          std::make_unique<URLRequestErrorJob>(this, ERR_BLOCKED_BY_CLIENT));
      return;
    }
  }

  RecordReferrerGranularityMetrics(same_origin_for_metrics);

  // Start() always completes asynchronously.
  //
  // Status is generally set by URLRequestJob itself, but Start() calls
  // directly into the URLRequestJob subclass, so URLRequestJob can't set it
  // here.
  // TODO(mmenke):  Make the URLRequest manage its own status.
  status_ = ERR_IO_PENDING;
  job_->Start();
}

https://source.chromium.org/chromium/chromium/src/+/main:net/url_request/url_request_http_job.cc;l=429;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1

void URLRequestHttpJob::Start() {
  DCHECK(!transaction_.get());

  request_info_.url = request_->url();
  request_info_.method = request_->method();

  request_info_.network_isolation_key =
      request_->isolation_info().network_isolation_key();
  request_info_.network_anonymization_key =
      request_->isolation_info().network_anonymization_key();
  request_info_.possibly_top_frame_origin =
      request_->isolation_info().top_frame_origin();
  request_info_.frame_origin = request_->isolation_info().frame_origin();
  request_info_.is_subframe_document_resource =
      request_->isolation_info().request_type() ==
      net::IsolationInfo::RequestType::kSubFrame;
  request_info_.is_main_frame_navigation =
      request_->isolation_info().IsMainFrameRequest();
  request_info_.initiator = request_->initiator();
  request_info_.load_flags = request_->load_flags();
  request_info_.priority_incremental = request_->priority_incremental();
  request_info_.secure_dns_policy = request_->secure_dns_policy();
  request_info_.traffic_annotation =
      net::MutableNetworkTrafficAnnotationTag(request_->traffic_annotation());
  request_info_.socket_tag = request_->socket_tag();
  request_info_.idempotency = request_->GetIdempotency();
#if BUILDFLAG(ENABLE_REPORTING)
  request_info_.reporting_upload_depth = request_->reporting_upload_depth();
#endif
  request_info_.is_shared_resource = request_->is_shared_resource();

  CookieStore* cookie_store = request()->context()->cookie_store();
  const CookieAccessDelegate* delegate =
      cookie_store ? cookie_store->cookie_access_delegate() : nullptr;

  request_->net_log().BeginEvent(NetLogEventType::FIRST_PARTY_SETS_METADATA);

  std::optional<
      std::pair<FirstPartySetMetadata, FirstPartySetsCacheFilter::MatchInfo>>
      maybe_metadata = cookie_util::ComputeFirstPartySetMetadataMaybeAsync(
          SchemefulSite(request()->url()), request()->isolation_info(),
          delegate,
          base::BindOnce(&URLRequestHttpJob::OnGotFirstPartySetMetadata,
                         weak_factory_.GetWeakPtr()));

  if (maybe_metadata.has_value()) {
    auto [metadata, match_info] = std::move(maybe_metadata).value();
    OnGotFirstPartySetMetadata(std::move(metadata), std::move(match_info));
  }
}

https://source.chromium.org/chromium/chromium/src/+/main:net/url_request/url_request_http_job.cc;l=489;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1

void URLRequestHttpJob::OnGotFirstPartySetMetadata(
    FirstPartySetMetadata first_party_set_metadata,
    FirstPartySetsCacheFilter::MatchInfo match_info) {
  first_party_set_metadata_ = std::move(first_party_set_metadata);
  request_info_.fps_cache_filter = match_info.clear_at_run_id;
  request_info_.browser_run_id = match_info.browser_run_id;

  request_->net_log().EndEvent(
      NetLogEventType::FIRST_PARTY_SETS_METADATA, [&]() {
        return FirstPartySetMetadataNetLogParams(
            first_party_set_metadata_,
            base::OptionalToPtr(request_info_.fps_cache_filter));
      });

  // Privacy mode could still be disabled in SetCookieHeaderAndStart if we are
  // going to send previously saved cookies.
  request_info_.privacy_mode = DeterminePrivacyMode();
  request()->net_log().AddEventWithStringParams(
      NetLogEventType::COMPUTED_PRIVACY_MODE, "privacy_mode",
      PrivacyModeToDebugString(request_info_.privacy_mode));

  // Strip Referer from request_info_.extra_headers to prevent, e.g., plugins
  // from overriding headers that are controlled using other means. Otherwise a
  // plugin could set a referrer although sending the referrer is inhibited.
  request_info_.extra_headers.RemoveHeader(HttpRequestHeaders::kReferer);

  // URLRequest::SetReferrer ensures that we do not send username and password
  // fields in the referrer.
  GURL referrer(request_->referrer());

  // Our consumer should have made sure that this is a safe referrer (e.g. via
  // URLRequestJob::ComputeReferrerForPolicy).
  if (referrer.is_valid()) {
    std::string referer_value = referrer.spec();
    request_info_.extra_headers.SetHeader(HttpRequestHeaders::kReferer,
                                          referer_value);
  }

  request_info_.extra_headers.SetHeaderIfMissing(
      HttpRequestHeaders::kUserAgent,
      http_user_agent_settings_ ?
          http_user_agent_settings_->GetUserAgent() : std::string());

  AddExtraHeaders();

  if (ShouldAddCookieHeader()) {
    AddCookieHeaderAndStart();
  } else {
    StartTransaction();
  }
}

https://source.chromium.org/chromium/chromium/src/+/main:net/url_request/url_request_http_job.cc;l=669;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1

void URLRequestHttpJob::StartTransaction() {
  DCHECK(!override_response_info_);

  NetworkDelegate* network_delegate = request()->network_delegate();
  if (network_delegate) {
    OnCallToDelegate(
        NetLogEventType::NETWORK_DELEGATE_BEFORE_START_TRANSACTION);
    int rv = network_delegate->NotifyBeforeStartTransaction(
        request_, request_info_.extra_headers,
        base::BindOnce(&URLRequestHttpJob::NotifyBeforeStartTransactionCallback,
                       weak_factory_.GetWeakPtr()));
    // If an extension blocks the request, we rely on the callback to
    // MaybeStartTransactionInternal().
    if (rv == ERR_IO_PENDING)
      return;
    MaybeStartTransactionInternal(rv);
    return;
  }
  StartTransactionInternal();
}

https://source.chromium.org/chromium/chromium/src/+/main:net/url_request/url_request_http_job.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=697

void URLRequestHttpJob::StartTransactionInternal() {
  DCHECK(!override_response_headers_);

  // NOTE: This method assumes that request_info_ is already setup properly.

  // If we already have a transaction, then we should restart the transaction
  // with auth provided by auth_credentials_.

  int rv = OK;

  // Notify NetworkQualityEstimator.
  NetworkQualityEstimator* network_quality_estimator =
      request()->context()->network_quality_estimator();
  if (network_quality_estimator)
    network_quality_estimator->NotifyStartTransaction(*request_);

  if (transaction_.get()) {
    rv = transaction_->RestartWithAuth(
        auth_credentials_, base::BindOnce(&URLRequestHttpJob::OnStartCompleted,
                                          base::Unretained(this)));
    auth_credentials_ = AuthCredentials();
  } else {
    DCHECK(request_->context()->http_transaction_factory());
    transaction_ =
        request_->context()->http_transaction_factory()->CreateTransaction(
            priority_);
    CHECK(transaction_);

    if (request_info_.url.SchemeIsWSOrWSS()) {
      base::SupportsUserData::Data* data =
          request_->GetUserData(kWebSocketHandshakeUserDataKey);
      if (data) {
        transaction_->SetWebSocketHandshakeStreamCreateHelper(
            static_cast<WebSocketHandshakeStreamBase::CreateHelper*>(data));
      } else {
        rv = ERR_DISALLOWED_URL_SCHEME;
      }
    }

    if (rv == OK && request_info_.method == "CONNECT") {
      // CONNECT has different kinds of targets than other methods (RFC 9110,
      // section 9.3.6), which are incompatible with URLRequest.
      rv = ERR_METHOD_NOT_SUPPORTED;
    }

    if (rv == OK) {
      transaction_->SetConnectedCallback(base::BindRepeating(
          &URLRequestHttpJob::NotifyConnectedCallback, base::Unretained(this)));
      transaction_->SetRequestHeadersCallback(request_headers_callback_);
      transaction_->SetEarlyResponseHeadersCallback(
          early_response_headers_callback_);
      transaction_->SetResponseHeadersCallback(response_headers_callback_);
      if (is_shared_dictionary_read_allowed_callback_) {
        transaction_->SetIsSharedDictionaryReadAllowedCallback(
            is_shared_dictionary_read_allowed_callback_);
      }

      rv = transaction_->Start(
          &request_info_,
          base::BindOnce(&URLRequestHttpJob::OnStartCompleted,
                         base::Unretained(this)),
          request_->net_log());
      start_time_ = base::TimeTicks::Now();
    }
  }

  if (rv == ERR_IO_PENDING)
    return;

  // The transaction started synchronously, but we need to notify the
  // URLRequest delegate via the message loop.
  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
      FROM_HERE, base::BindOnce(&URLRequestHttpJob::OnStartCompleted,
                                weak_factory_.GetWeakPtr(), rv));
}

https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_network_transaction.cc;l=359;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1

int HttpNetworkTransaction::Start(const HttpRequestInfo* request_info,
                                  CompletionOnceCallback callback,
                                  const NetLogWithSource& net_log) {
  TRACE_EVENT("net", "HttpNetworkTransaction::Start",
              NetLogWithSourceToFlow(net_log), "url", request_info->url);

  if (session_->power_suspended()) {
    return ERR_NETWORK_IO_SUSPENDED;
  }

  if (request_info->load_flags & LOAD_ONLY_FROM_CACHE) {
    return ERR_CACHE_MISS;
  }

  DCHECK(request_info->traffic_annotation.is_valid());
  DCHECK(request_info->IsConsistent());
  net_log_ = net_log;
  request_ = request_info;
  url_ = request_->url;
  network_anonymization_key_ = request_->network_anonymization_key;
  start_timeticks_ = base::TimeTicks::Now();
#if BUILDFLAG(ENABLE_REPORTING)
  // Store values for later use in NEL report generation.
  request_method_ = request_->method;
  if (std::optional<std::string> header =
          request_->extra_headers.GetHeader(HttpRequestHeaders::kReferer);
      header) {
    request_referrer_.swap(header.value());
  }
  if (std::optional<std::string> header =
          request_->extra_headers.GetHeader(HttpRequestHeaders::kUserAgent);
      header) {
    request_user_agent_.swap(header.value());
  }
  request_reporting_upload_depth_ = request_->reporting_upload_depth;
#endif  // BUILDFLAG(ENABLE_REPORTING)

  if (request_->idempotency == IDEMPOTENT ||
      (request_->idempotency == DEFAULT_IDEMPOTENCY &&
       HttpUtil::IsMethodSafe(request_info->method))) {
    can_send_early_data_ = true;
  }

  if (request_->load_flags & LOAD_PREFETCH) {
    response_.unused_since_prefetch = true;
  }

  if (request_->load_flags & LOAD_RESTRICTED_PREFETCH_FOR_MAIN_FRAME) {
    DCHECK(response_.unused_since_prefetch);
    response_.restricted_prefetch = true;
  }

  next_state_ = STATE_CREATE_STREAM;
  int rv = DoLoop(OK);
  if (rv == ERR_IO_PENDING)
    callback_ = std::move(callback);

  // This always returns ERR_IO_PENDING because DoCreateStream() does, but
  // GenerateNetworkErrorLoggingReportIfError() should be called here if any
  // other Error can be returned.
  DCHECK_EQ(rv, ERR_IO_PENDING);
  return rv;
}

https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_network_transaction.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=985

int HttpNetworkTransaction::DoLoop(int result) {
  DCHECK(next_state_ != STATE_NONE);

  int rv = result;
  do {
    State state = next_state_;
    next_state_ = STATE_NONE;
    switch (state) {
      case STATE_CREATE_STREAM:
        DCHECK_EQ(OK, rv);
        rv = DoCreateStream();
        break;
      case STATE_CREATE_STREAM_COMPLETE:
        rv = DoCreateStreamComplete(rv);
        break;
      case STATE_CONNECTED_CALLBACK:
        rv = DoConnectedCallback();
        break;
      case STATE_CONNECTED_CALLBACK_COMPLETE:
        rv = DoConnectedCallbackComplete(rv);
        break;
      case STATE_INIT_STREAM:
        DCHECK_EQ(OK, rv);
        rv = DoInitStream();
        break;
      case STATE_INIT_STREAM_COMPLETE:
        rv = DoInitStreamComplete(rv);
        break;
      case STATE_GENERATE_PROXY_AUTH_TOKEN:
        DCHECK_EQ(OK, rv);
        rv = DoGenerateProxyAuthToken();
        break;
      case STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE:
        rv = DoGenerateProxyAuthTokenComplete(rv);
        break;
      case STATE_GENERATE_SERVER_AUTH_TOKEN:
        DCHECK_EQ(OK, rv);
        rv = DoGenerateServerAuthToken();
        break;
      case STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE:
        rv = DoGenerateServerAuthTokenComplete(rv);
        break;
      case STATE_INIT_REQUEST_BODY:
        DCHECK_EQ(OK, rv);
        rv = DoInitRequestBody();
        break;
      case STATE_INIT_REQUEST_BODY_COMPLETE:
        rv = DoInitRequestBodyComplete(rv);
        break;
      case STATE_BUILD_REQUEST:
        DCHECK_EQ(OK, rv);
        net_log_.BeginEvent(NetLogEventType::HTTP_TRANSACTION_SEND_REQUEST);
        rv = DoBuildRequest();
        break;
      case STATE_BUILD_REQUEST_COMPLETE:
        rv = DoBuildRequestComplete(rv);
        break;
      case STATE_SEND_REQUEST:
        DCHECK_EQ(OK, rv);
        rv = DoSendRequest();
        break;
      case STATE_SEND_REQUEST_COMPLETE:
        rv = DoSendRequestComplete(rv);
        net_log_.EndEventWithNetErrorCode(
            NetLogEventType::HTTP_TRANSACTION_SEND_REQUEST, rv);
        break;
      case STATE_READ_HEADERS:
        DCHECK_EQ(OK, rv);
        net_log_.BeginEvent(NetLogEventType::HTTP_TRANSACTION_READ_HEADERS);
        rv = DoReadHeaders();
        break;
      case STATE_READ_HEADERS_COMPLETE:
        rv = DoReadHeadersComplete(rv);
        net_log_.EndEventWithNetErrorCode(
            NetLogEventType::HTTP_TRANSACTION_READ_HEADERS, rv);
        break;
      case STATE_READ_BODY:
        DCHECK_EQ(OK, rv);
        net_log_.BeginEvent(NetLogEventType::HTTP_TRANSACTION_READ_BODY);
        rv = DoReadBody();
        break;
      case STATE_READ_BODY_COMPLETE:
        rv = DoReadBodyComplete(rv);
        net_log_.EndEventWithNetErrorCode(
            NetLogEventType::HTTP_TRANSACTION_READ_BODY, rv);
        break;
      case STATE_DRAIN_BODY_FOR_AUTH_RESTART:
        DCHECK_EQ(OK, rv);
        net_log_.BeginEvent(
            NetLogEventType::HTTP_TRANSACTION_DRAIN_BODY_FOR_AUTH_RESTART);
        rv = DoDrainBodyForAuthRestart();
        break;
      case STATE_DRAIN_BODY_FOR_AUTH_RESTART_COMPLETE:
        rv = DoDrainBodyForAuthRestartComplete(rv);
        net_log_.EndEventWithNetErrorCode(
            NetLogEventType::HTTP_TRANSACTION_DRAIN_BODY_FOR_AUTH_RESTART, rv);
        break;
      default:
        NOTREACHED() << "bad state";
    }
  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);

  return rv;
}

https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_network_transaction.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=1090

int HttpNetworkTransaction::DoCreateStream() {
  TRACE_EVENT("net", "HttpNetworkTransaction::CreateStream",
              NetLogWithSourceToFlow(net_log_), "retry_attempts",
              retry_attempts_, "num_restarts", num_restarts_);
  response_.network_accessed = true;

  next_state_ = STATE_CREATE_STREAM_COMPLETE;
  // IP based pooling is only disabled on a retry after 421 Misdirected Request
  // is received. Alternative Services are also disabled in this case (though
  // they can also be disabled when retrying after a QUIC error).
  if (!enable_ip_based_pooling_) {
    DCHECK(!enable_alternative_services_);
  }

  create_stream_start_time_ = base::TimeTicks::Now();
  // Reset `create_stream_end_time__` to prevent an inconsistent state in
  // case that `DoCreateStream` is called multiple times.
  create_stream_end_time_ = base::TimeTicks();

  if (ForWebSocketHandshake()) {
    stream_request_ =
        session_->http_stream_factory()->RequestWebSocketHandshakeStream(
            *request_, priority_, /*allowed_bad_certs=*/observed_bad_certs_,
            this, websocket_handshake_stream_base_create_helper_,
            enable_ip_based_pooling_, enable_alternative_services_, net_log_);
  } else {
    stream_request_ = session_->http_stream_factory()->RequestStream(
        *request_, priority_, /*allowed_bad_certs=*/observed_bad_certs_, this,
        enable_ip_based_pooling_, enable_alternative_services_, net_log_);
  }
  DCHECK(stream_request_.get());
  return ERR_IO_PENDING;
}

https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_stream_factory.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=167

std::unique_ptr<HttpStreamRequest> HttpStreamFactory::RequestStream(
    const HttpRequestInfo& request_info,
    RequestPriority priority,
    const std::vector<SSLConfig::CertAndStatus>& allowed_bad_certs,
    HttpStreamRequest::Delegate* delegate,
    bool enable_ip_based_pooling,
    bool enable_alternative_services,
    const NetLogWithSource& net_log) {
  return RequestStreamInternal(request_info, priority, allowed_bad_certs,
                               delegate, nullptr,
                               HttpStreamRequest::HTTP_STREAM,
                               /*is_websocket=*/false, enable_ip_based_pooling,
                               enable_alternative_services, net_log);
}

https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_stream_factory.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=218

std::unique_ptr<HttpStreamRequest> HttpStreamFactory::RequestStreamInternal(
    const HttpRequestInfo& request_info,
    RequestPriority priority,
    const std::vector<SSLConfig::CertAndStatus>& allowed_bad_certs,
    HttpStreamRequest::Delegate* delegate,
    WebSocketHandshakeStreamBase::CreateHelper*
        websocket_handshake_stream_create_helper,
    HttpStreamRequest::StreamType stream_type,
    bool is_websocket,
    bool enable_ip_based_pooling,
    bool enable_alternative_services,
    const NetLogWithSource& net_log) {
  // This is only needed in the non-preconnect path, as preconnects do not
  // require a NetworkIsolationKey.
  DCHECK(request_info.IsConsistent());

  auto job_controller = std::make_unique<JobController>(
      this, delegate, session_, job_factory_.get(), request_info,
      /* is_preconnect = */ false, is_websocket, enable_ip_based_pooling,
      enable_alternative_services,
      session_->context()
          .quic_context->params()
          ->delay_main_job_with_available_spdy_session,
      allowed_bad_certs);
  JobController* job_controller_raw_ptr = job_controller.get();
  job_controller_set_.insert(std::move(job_controller));
  return job_controller_raw_ptr->Start(delegate,
                                       websocket_handshake_stream_create_helper,
                                       net_log, stream_type, priority);
}

https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_stream_factory_job_controller.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=206

std::unique_ptr<HttpStreamRequest> HttpStreamFactory::JobController::Start(
    HttpStreamRequest::Delegate* delegate,
    WebSocketHandshakeStreamBase::CreateHelper*
        websocket_handshake_stream_create_helper,
    const NetLogWithSource& source_net_log,
    HttpStreamRequest::StreamType stream_type,
    RequestPriority priority) {
  DCHECK(!request_);

  stream_type_ = stream_type;
  priority_ = priority;

  auto request = std::make_unique<HttpStreamRequest>(
      this, websocket_handshake_stream_create_helper, source_net_log,
      stream_type);
  // Keep a raw pointer but release ownership of HttpStreamRequest instance.
  request_ = request.get();

  // Associates |net_log_| with |source_net_log|.
  source_net_log.AddEventReferencingSource(
      NetLogEventType::HTTP_STREAM_JOB_CONTROLLER_BOUND, net_log_.source());
  net_log_.AddEventReferencingSource(
      NetLogEventType::HTTP_STREAM_JOB_CONTROLLER_BOUND,
      source_net_log.source());

  RunLoop(OK);

  return request;
}

https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_stream_factory_job_controller.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=747

void HttpStreamFactory::JobController::RunLoop(int result) {
  int rv = DoLoop(result);
  if (rv == ERR_IO_PENDING) {
    return;
  }
  if (rv != OK) {
    // DoLoop can only fail during proxy resolution step which happens before
    // any jobs are created. Notify |request_| of the failure one message loop
    // iteration later to avoid re-entrancy.
    DCHECK(!main_job_);
    DCHECK(!alternative_job_);
    DCHECK(!dns_alpn_h3_job_);
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE,
        base::BindOnce(&HttpStreamFactory::JobController::NotifyRequestFailed,
                       ptr_factory_.GetWeakPtr(), rv));
  }
}

int HttpStreamFactory::JobController::DoLoop(int rv) {
  DCHECK_NE(next_state_, STATE_NONE);
  do {
    State state = next_state_;
    next_state_ = STATE_NONE;
    switch (state) {
      case STATE_RESOLVE_PROXY:
        DCHECK_EQ(OK, rv);
        rv = DoResolveProxy();
        break;
      case STATE_RESOLVE_PROXY_COMPLETE:
        rv = DoResolveProxyComplete(rv);
        break;
      case STATE_CREATE_JOBS:
        DCHECK_EQ(OK, rv);
        rv = DoCreateJobs();
        break;
      default:
        NOTREACHED() << "bad state";
    }
  } while (next_state_ != STATE_NONE && rv != ERR_IO_PENDING);
  return rv;
}

https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_stream_factory_job_controller.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=840

int HttpStreamFactory::JobController::DoCreateJobs() {
  DCHECK(!main_job_);
  DCHECK(!alternative_job_);
  DCHECK(origin_url_.is_valid());
  DCHECK(origin_url_.IsStandard());

  url::SchemeHostPort destination(origin_url_);
  DCHECK(destination.IsValid());
  ConvertWsToHttp(destination);

  // Create an alternative job if alternative service is set up for this domain.
  // This is applicable even if the connection will be made via a proxy.
  alternative_service_info_ = GetAlternativeServiceInfoFor(
      http_request_info_url_, request_info_, delegate_, stream_type_);

  if (session_->host_resolver()->IsHappyEyeballsV3Enabled() &&
      proxy_info_.is_direct() && !is_websocket_) {
    SwitchToHttpStreamPool();
    return OK;
  }

  quic::ParsedQuicVersion quic_version = quic::ParsedQuicVersion::Unsupported();
  if (alternative_service_info_.protocol() == NextProto::kProtoQUIC) {
    quic_version =
        SelectQuicVersion(alternative_service_info_.advertised_versions());
    DCHECK_NE(quic_version, quic::ParsedQuicVersion::Unsupported());
  }

  // Getting ALPN for H3 from DNS has a lot of preconditions. Among them:
  // - proxied connections perform DNS on the proxy, so they can't get supported
  //   ALPNs from DNS
  const bool dns_alpn_h3_job_enabled =
      !session_->ShouldForceQuic(destination, proxy_info_, is_websocket_) &&
      enable_alternative_services_ &&
      session_->params().use_dns_https_svcb_alpn &&
      base::EqualsCaseInsensitiveASCII(origin_url_.scheme(),
                                       url::kHttpsScheme) &&
      session_->IsQuicEnabled() && proxy_info_.is_direct() &&
      !session_->http_server_properties()->IsAlternativeServiceBroken(
          GetAlternativeServiceForDnsJob(origin_url_),
          request_info_.network_anonymization_key);

  if (is_preconnect_) {
    // Due to how the socket pools handle priorities and idle sockets, only IDLE
    // priority currently makes sense for preconnects. The priority for
    // preconnects is currently ignored (see RequestSocketsForPool()), but could
    // be used at some point for proxy resolution or something.
    // Note: When `dns_alpn_h3_job_enabled` is true, we create a
    // PRECONNECT_DNS_ALPN_H3 job. If no matching HTTPS DNS ALPN records are
    // received, the PRECONNECT_DNS_ALPN_H3 job will fail with
    // ERR_DNS_NO_MATCHING_SUPPORTED_ALPN, and `preconnect_backup_job_` will
    // be started in OnPreconnectsComplete().
    std::unique_ptr<Job> preconnect_job = job_factory_->CreateJob(
        this, dns_alpn_h3_job_enabled ? PRECONNECT_DNS_ALPN_H3 : PRECONNECT,
        session_, request_info_, IDLE, proxy_info_, allowed_bad_certs_,
        destination, origin_url_, is_websocket_, enable_ip_based_pooling_,
        net_log_.net_log(), NextProto::kProtoUnknown,
        quic::ParsedQuicVersion::Unsupported(), management_config_);
    // When there is an valid alternative service info, and `preconnect_job`
    // has no existing QUIC session, create a job for the alternative service.
    if (alternative_service_info_.protocol() != NextProto::kProtoUnknown &&
        !preconnect_job->HasAvailableQuicSession()) {
      GURL alternative_url = CreateAltSvcUrl(
          origin_url_, alternative_service_info_.GetHostPortPair());
      RewriteUrlWithHostMappingRules(alternative_url);

      url::SchemeHostPort alternative_destination =
          url::SchemeHostPort(alternative_url);
      ConvertWsToHttp(alternative_destination);

      main_job_ = job_factory_->CreateJob(
          this, PRECONNECT, session_, request_info_, IDLE, proxy_info_,
          allowed_bad_certs_, std::move(alternative_destination), origin_url_,
          is_websocket_, enable_ip_based_pooling_, session_->net_log(),
          alternative_service_info_.protocol(), quic_version,
          management_config_);
    } else {
      main_job_ = std::move(preconnect_job);

      if (dns_alpn_h3_job_enabled) {
        preconnect_backup_job_ = job_factory_->CreateJob(
            this, PRECONNECT, session_, request_info_, IDLE, proxy_info_,
            allowed_bad_certs_, std::move(destination), origin_url_,
            is_websocket_, enable_ip_based_pooling_, net_log_.net_log(),
            NextProto::kProtoUnknown, quic::ParsedQuicVersion::Unsupported(),
            management_config_);
      }
    }
    main_job_->Preconnect(num_streams_);
    return OK;
  }
  main_job_ = job_factory_->CreateJob(
      this, MAIN, session_, request_info_, priority_, proxy_info_,
      allowed_bad_certs_, std::move(destination), origin_url_, is_websocket_,
      enable_ip_based_pooling_, net_log_.net_log(), NextProto::kProtoUnknown,
      quic::ParsedQuicVersion::Unsupported(), management_config_);

  // Alternative Service can only be set for HTTPS requests while Alternative
  // Proxy is set for HTTP requests.
  // The main job may use HTTP/3 if the origin is specified in
  // `--origin-to-force-quic-on` switch. In that case, do not create
  // `alternative_job_` and `dns_alpn_h3_job_`.
  if ((alternative_service_info_.protocol() != NextProto::kProtoUnknown) &&
      !main_job_->using_quic()) {
    DCHECK(origin_url_.SchemeIs(url::kHttpsScheme));
    DCHECK(!is_websocket_);
    DVLOG(1) << "Selected alternative service (host: "
             << alternative_service_info_.GetHostPortPair().host()
             << " port: " << alternative_service_info_.GetHostPortPair().port()
             << " version: " << quic_version << ")";

    GURL alternative_url = CreateAltSvcUrl(
        origin_url_, alternative_service_info_.GetHostPortPair());
    RewriteUrlWithHostMappingRules(alternative_url);

    url::SchemeHostPort alternative_destination =
        url::SchemeHostPort(alternative_url);
    ConvertWsToHttp(alternative_destination);

    alternative_job_ = job_factory_->CreateJob(
        this, ALTERNATIVE, session_, request_info_, priority_, proxy_info_,
        allowed_bad_certs_, std::move(alternative_destination), origin_url_,
        is_websocket_, enable_ip_based_pooling_, net_log_.net_log(),
        alternative_service_info_.protocol(), quic_version, management_config_);
  }

  if (dns_alpn_h3_job_enabled && !main_job_->using_quic()) {
    DCHECK(!is_websocket_);
    url::SchemeHostPort dns_alpn_h3_destination =
        url::SchemeHostPort(origin_url_);
    dns_alpn_h3_job_ = job_factory_->CreateJob(
        this, DNS_ALPN_H3, session_, request_info_, priority_, proxy_info_,
        allowed_bad_certs_, std::move(dns_alpn_h3_destination), origin_url_,
        is_websocket_, enable_ip_based_pooling_, net_log_.net_log(),
        NextProto::kProtoUnknown, quic::ParsedQuicVersion::Unsupported(),
        management_config_);
  }

  ClearInappropriateJobs();

  if (main_job_ && (alternative_job_ ||
                    (dns_alpn_h3_job_ &&
                     (!main_job_->TargettedSocketGroupHasActiveSocket() &&
                      !main_job_->HasAvailableSpdySession())))) {
    // We don't block |main_job_| when |alternative_job_| doesn't exists and
    // |dns_alpn_h3_job_| exists and an active socket is available for
    // |main_job_|. This is intended to make the fallback logic faster.
    main_job_is_blocked_ = true;
  }

  if (alternative_job_) {
    alternative_job_->Start(request_->stream_type());
  }

  if (dns_alpn_h3_job_) {
    dns_alpn_h3_job_->Start(request_->stream_type());
  }

  if (main_job_) {
    main_job_->Start(request_->stream_type());
  }
  return OK;
}

https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_stream_factory_job.cc;l=238;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1

void HttpStreamFactory::Job::Start(HttpStreamRequest::StreamType stream_type) {
  started_ = true;
  stream_type_ = stream_type;

  const NetLogWithSource* delegate_net_log = delegate_->GetNetLog();
  if (delegate_net_log) {
    net_log_.BeginEvent(NetLogEventType::HTTP_STREAM_JOB, [&] {
      base::Value::Dict dict;
      const auto& source = delegate_net_log->source();
      if (source.IsValid()) {
        source.AddToEventParameters(dict);
      }
      dict.Set("logical_destination",
               url::SchemeHostPort(origin_url_).Serialize());
      dict.Set("destination", destination_.Serialize());
      dict.Set("expect_spdy", expect_spdy_);
      dict.Set("using_quic", using_quic_);
      dict.Set("priority", RequestPriorityToString(priority_));
      dict.Set("type", NetLogHttpStreamJobType(job_type_));
      return dict;
    });
    delegate_net_log->AddEventReferencingSource(
        NetLogEventType::HTTP_STREAM_REQUEST_STARTED_JOB, net_log_.source());
  }

  StartInternal();
}

https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_stream_factory_job.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=518

void HttpStreamFactory::Job::RunLoop(int result) {
  result = DoLoop(result);

  if (result == ERR_IO_PENDING) {
    return;
  }

  // Stop watching for new SpdySessions, to avoid receiving a new SPDY session
  // while doing anything other than waiting to establish a connection.
  spdy_session_request_.reset();

  // Record histograms which are required for the end of session creation.
  RecordCompletionHistograms(result);

  if ((job_type_ == PRECONNECT) || (job_type_ == PRECONNECT_DNS_ALPN_H3)) {
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE,
        base::BindOnce(&HttpStreamFactory::Job::OnPreconnectsComplete,
                       ptr_factory_.GetWeakPtr(), result));
    return;
  }

  if (IsCertificateError(result)) {
    // Retrieve SSL information from the socket.
    SSLInfo ssl_info;
    GetSSLInfo(&ssl_info);

    next_state_ = STATE_WAITING_USER_ACTION;
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE,
        base::BindOnce(&HttpStreamFactory::Job::OnCertificateErrorCallback,
                       ptr_factory_.GetWeakPtr(), result, ssl_info));
    return;
  }

  switch (result) {
    case ERR_SSL_CLIENT_AUTH_CERT_NEEDED:
      base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
          FROM_HERE,
          base::BindOnce(
              &Job::OnNeedsClientAuthCallback, ptr_factory_.GetWeakPtr(),
              base::RetainedRef(connection_->ssl_cert_request_info())));
      return;

    case OK:
      next_state_ = STATE_DONE;
      if (is_websocket_) {
        DCHECK(websocket_stream_);
        base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
            FROM_HERE,
            base::BindOnce(&Job::OnWebSocketHandshakeStreamReadyCallback,
                           ptr_factory_.GetWeakPtr()));
      } else if (stream_type_ == HttpStreamRequest::BIDIRECTIONAL_STREAM) {
        if (!bidirectional_stream_impl_) {
          base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
              FROM_HERE, base::BindOnce(&Job::OnStreamFailedCallback,
                                        ptr_factory_.GetWeakPtr(), ERR_FAILED));
        } else {
          base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
              FROM_HERE,
              base::BindOnce(&Job::OnBidirectionalStreamImplReadyCallback,
                             ptr_factory_.GetWeakPtr()));
        }
      } else {
        DCHECK(stream_.get());
        base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
            FROM_HERE, base::BindOnce(&Job::OnStreamReadyCallback,
                                      ptr_factory_.GetWeakPtr()));
      }
      return;

    default:
      base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
          FROM_HERE, base::BindOnce(&Job::OnStreamFailedCallback,
                                    ptr_factory_.GetWeakPtr(), result));
      return;
  }
}

int HttpStreamFactory::Job::DoLoop(int result) {
  DCHECK_NE(next_state_, STATE_NONE);
  int rv = result;
  do {
    State state = next_state_;
    next_state_ = STATE_NONE;
    switch (state) {
      case STATE_START:
        DCHECK_EQ(OK, rv);
        rv = DoStart();
        break;
      case STATE_WAIT:
        DCHECK_EQ(OK, rv);
        rv = DoWait();
        break;
      case STATE_WAIT_COMPLETE:
        rv = DoWaitComplete(rv);
        break;
      case STATE_INIT_CONNECTION:
        DCHECK_EQ(OK, rv);
        rv = DoInitConnection();
        break;
      case STATE_INIT_CONNECTION_COMPLETE:
        rv = DoInitConnectionComplete(rv);
        break;
      case STATE_WAITING_USER_ACTION:
        rv = DoWaitingUserAction(rv);
        break;
      case STATE_CREATE_STREAM:
        DCHECK_EQ(OK, rv);
        rv = DoCreateStream();
        break;
      case STATE_CREATE_STREAM_COMPLETE:
        rv = DoCreateStreamComplete(rv);
        break;
      default:
        NOTREACHED() << "bad state";
    }
  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
  return rv;
}

int HttpStreamFactory::Job::StartInternal() {
  CHECK_EQ(STATE_NONE, next_state_);
  next_state_ = STATE_START;
  RunLoop(OK);
  return ERR_IO_PENDING;
}

https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_stream_factory_job.cc;l=687;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1

int HttpStreamFactory::Job::DoInitConnection() {
  net_log_.BeginEvent(NetLogEventType::HTTP_STREAM_JOB_INIT_CONNECTION);
  int result = DoInitConnectionImpl();
  if (!expect_on_quic_session_created_ && !expect_on_quic_host_resolution_) {
    delegate_->OnConnectionInitialized(this, result);
  }
  return result;
}

int HttpStreamFactory::Job::DoInitConnectionImpl() {
  DCHECK(!connection_->is_initialized());

  if (using_quic_ && !proxy_info_.is_direct() &&
      !proxy_info_.proxy_chain().Last().is_quic()) {
    // QUIC can not be spoken to non-QUIC proxies.  This error should not be
    // user visible, because the non-alternative Job should be resumed.
    return ERR_NO_SUPPORTED_PROXIES;
  }

  DCHECK(proxy_info_.proxy_chain().IsValid());
  next_state_ = STATE_INIT_CONNECTION_COMPLETE;

  if (using_quic_) {
    // TODO(mmenke): Clean this up. `disable_cert_verification_network_fetches`
    // is enabled in ConnectJobFactory for H1/H2 connections. Also need to add
    // it to the SpdySessionKey for H2 connections.
    SSLConfig server_ssl_config;
    server_ssl_config.disable_cert_verification_network_fetches =
        disable_cert_verification_network_fetches();
    return DoInitConnectionImplQuic(server_ssl_config.GetCertVerifyFlags());
  }

  // Check first if there is a pushed stream matching the request, or an HTTP/2
  // connection this request can pool to.  If so, then go straight to using
  // that.
  if (CanUseExistingSpdySession()) {
    if (!existing_spdy_session_) {
      if (!spdy_session_request_) {
        // If not currently watching for an H2 session, use
        // SpdySessionPool::RequestSession() to check for a session, and start
        // watching for one.
        bool should_throttle_connect = ShouldThrottleConnectForSpdy();
        base::RepeatingClosure resume_callback =
            should_throttle_connect
                ? base::BindRepeating(
                      &HttpStreamFactory::Job::ResumeInitConnection,
                      ptr_factory_.GetWeakPtr())
                : base::RepeatingClosure();

        bool is_blocking_request_for_session;
        existing_spdy_session_ = session_->spdy_session_pool()->RequestSession(
            spdy_session_key_, enable_ip_based_pooling_, is_websocket_,
            net_log_, resume_callback, this, &spdy_session_request_,
            &is_blocking_request_for_session);
        if (!existing_spdy_session_ && should_throttle_connect &&
            !is_blocking_request_for_session) {
          net_log_.AddEvent(NetLogEventType::HTTP_STREAM_JOB_THROTTLED);
          next_state_ = STATE_INIT_CONNECTION;
          base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
              FROM_HERE, resume_callback, base::Milliseconds(kHTTP2ThrottleMs));
          return ERR_IO_PENDING;
        }
      } else if (enable_ip_based_pooling_) {
        // If already watching for an H2 session, still need to check for an
        // existing connection that can be reused through IP pooling, as those
        // don't post session available notifications.
        //
        // TODO(mmenke):  Make sessions created through IP pooling invoke the
        // callback.
        existing_spdy_session_ =
            session_->spdy_session_pool()->FindAvailableSession(
                spdy_session_key_, enable_ip_based_pooling_, is_websocket_,
                net_log_);
      }
    }
    if (existing_spdy_session_) {
      // Stop watching for SpdySessions.
      spdy_session_request_.reset();

      // If we're preconnecting, but we already have a SpdySession, we don't
      // actually need to preconnect any sockets, so we're done.
      if (job_type_ == PRECONNECT) {
        return OK;
      }
      negotiated_protocol_ = NextProto::kProtoHTTP2;
      next_state_ = STATE_CREATE_STREAM;
      return OK;
    }
  }

  establishing_tunnel_ = !UsingHttpProxyWithoutTunnel();

  if (job_type_ == PRECONNECT) {
    DCHECK(!is_websocket_);
    DCHECK(request_info_.socket_tag == SocketTag());

    // The lifeime of the preconnect tasks is not controlled by |connection_|.
    // It may outlives |this|. So we can't use |io_callback_| which holds
    // base::Unretained(this).
    auto callback =
        base::BindOnce(&Job::OnIOComplete, ptr_factory_.GetWeakPtr());

    // TODO(crbug.com/391578657): Check proxy info for did try IPP proxy to
    // populate `fail_if_alias_requires_proxy_override` and pass into method for
    // Preconnect.
    return PreconnectSocketsForHttpRequest(
        destination_, request_info_.load_flags, priority_, session_,
        proxy_info_, allowed_bad_certs_, request_info_.privacy_mode,
        request_info_.network_anonymization_key,
        request_info_.secure_dns_policy, net_log_, num_streams_,
        /*fail_if_alias_requires_proxy_override_=*/false, std::move(callback));
  }

  // TODO(crbug.com/383134117): Check proxy info for did try IPP proxy to
  // populate `fail_if_alias_requires_proxy_override` and pass into
  // `InitSocketHandleForWebSocketRequest` and `InitSocketHandleForHttpRequest`
  ClientSocketPool::ProxyAuthCallback proxy_auth_callback =
      base::BindRepeating(&HttpStreamFactory::Job::OnNeedsProxyAuthCallback,
                          base::Unretained(this));
  if (is_websocket_) {
    DCHECK(request_info_.socket_tag == SocketTag());
    DCHECK_EQ(SecureDnsPolicy::kAllow, request_info_.secure_dns_policy);
    return InitSocketHandleForWebSocketRequest(
        destination_, request_info_.load_flags, priority_, session_,
        proxy_info_, allowed_bad_certs_, request_info_.privacy_mode,
        request_info_.network_anonymization_key, net_log_, connection_.get(),
        io_callback_, proxy_auth_callback,
        /*fail_if_alias_requires_proxy_override_=*/false);
  }

  return InitSocketHandleForHttpRequest(
      destination_, request_info_.load_flags, priority_, session_, proxy_info_,
      allowed_bad_certs_, request_info_.privacy_mode,
      request_info_.network_anonymization_key, request_info_.secure_dns_policy,
      request_info_.socket_tag, net_log_, connection_.get(), io_callback_,
      proxy_auth_callback, /*fail_if_alias_requires_proxy_override_=*/false);
}

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/client_socket_pool_manager.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;l=213 int InitSocketHandleForHttpRequest( url::SchemeHostPort endpoint, int request_load_flags, RequestPriority request_priority, HttpNetworkSession* session, const ProxyInfo& proxy_info, const std::vectorSSLConfig::CertAndStatus & allowed_bad_certs, PrivacyMode privacy_mode, NetworkAnonymizationKey network_anonymization_key, SecureDnsPolicy secure_dns_policy, const SocketTag& socket_tag, const NetLogWithSource& net_log, ClientSocketHandle* socket_handle, CompletionOnceCallback callback, const ClientSocketPool::ProxyAuthCallback& proxy_auth_callback, bool fail_if_alias_requires_proxy_override) { DCHECK(socket_handle); return InitSocketPoolHelper( std::move(endpoint), request_load_flags, request_priority, session, proxy_info, allowed_bad_certs, privacy_mode, std::move(network_anonymization_key), secure_dns_policy, socket_tag, net_log, 0, socket_handle, HttpNetworkSession::NORMAL_SOCKET_POOL, std::move(callback), proxy_auth_callback, fail_if_alias_requires_proxy_override); }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/client_socket_pool_manager.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;l=84 int InitSocketPoolHelper( url::SchemeHostPort endpoint, int request_load_flags, RequestPriority request_priority, HttpNetworkSession* session, const ProxyInfo& proxy_info, const std::vectorSSLConfig::CertAndStatus & allowed_bad_certs, PrivacyMode privacy_mode, NetworkAnonymizationKey network_anonymization_key, SecureDnsPolicy secure_dns_policy, const SocketTag& socket_tag, const NetLogWithSource& net_log, int num_preconnect_streams, ClientSocketHandle* socket_handle, HttpNetworkSession::SocketPoolType socket_pool_type, CompletionOnceCallback callback, const ClientSocketPool::ProxyAuthCallback& proxy_auth_callback, bool fail_if_alias_requires_proxy_override) { DCHECK(endpoint.IsValid());

session->ApplyTestingFixedPort(endpoint);

bool disable_cert_network_fetches = !!(request_load_flags & LOAD_DISABLE_CERT_NETWORK_FETCHES); ClientSocketPool::GroupId connection_group( std::move(endpoint), privacy_mode, std::move(network_anonymization_key), secure_dns_policy, disable_cert_network_fetches); scoped_refptrClientSocketPool::SocketParams socket_params = CreateSocketParams(connection_group, allowed_bad_certs);

ClientSocketPool* pool = session->GetSocketPool(socket_pool_type, proxy_info.proxy_chain()); ClientSocketPool::RespectLimits respect_limits = ClientSocketPool::RespectLimits::ENABLED; if ((request_load_flags & LOAD_IGNORE_LIMITS) != 0) respect_limits = ClientSocketPool::RespectLimits::DISABLED;

std::optional proxy_annotation = proxy_info.is_direct() ? std::nullopt : std::optional( proxy_info.traffic_annotation()); if (num_preconnect_streams) { return pool->RequestSockets(connection_group, std::move(socket_params), proxy_annotation, num_preconnect_streams, fail_if_alias_requires_proxy_override, std::move(callback), net_log); }

return socket_handle->Init( connection_group, std::move(socket_params), proxy_annotation, request_priority, socket_tag, respect_limits, std::move(callback), proxy_auth_callback, fail_if_alias_requires_proxy_override, pool, net_log); }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/client_socket_handle.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;l=31 int ClientSocketHandle::Init( const ClientSocketPool::GroupId& group_id, scoped_refptrClientSocketPool::SocketParams socket_params, const std::optional& proxy_annotation_tag, RequestPriority priority, const SocketTag& socket_tag, ClientSocketPool::RespectLimits respect_limits, CompletionOnceCallback callback, const ClientSocketPool::ProxyAuthCallback& proxy_auth_callback, bool fail_if_alias_requires_proxy_override, ClientSocketPool* pool, const NetLogWithSource& net_log) { requesting_source_ = net_log.source();

CHECK(group_id.destination().IsValid()); ResetInternal(true /* cancel /, false / cancel_connect_job */); ResetErrorState(); pool_ = pool; group_id_ = group_id; CompletionOnceCallback io_complete_callback = base::BindOnce(&ClientSocketHandle::OnIOComplete, base::Unretained(this)); int rv = pool_->RequestSocket( group_id, std::move(socket_params), proxy_annotation_tag, priority, socket_tag, respect_limits, this, std::move(io_complete_callback), proxy_auth_callback, fail_if_alias_requires_proxy_override, net_log); if (rv == ERR_IO_PENDING) { callback_ = std::move(callback); } else { HandleInitCompletion(rv); } return rv; }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/transport_client_socket_pool.cc;l=249;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1 int TransportClientSocketPool::RequestSocket( const GroupId& group_id, scoped_refptr params, const std::optional& proxy_annotation_tag, RequestPriority priority, const SocketTag& socket_tag, RespectLimits respect_limits, ClientSocketHandle* handle, CompletionOnceCallback callback, const ProxyAuthCallback& proxy_auth_callback, bool fail_if_alias_requires_proxy_override, const NetLogWithSource& net_log) { CHECK(callback); CHECK(handle);

NetLogTcpClientSocketPoolRequestedSocket(net_log, group_id);

std::unique_ptr request = std::make_unique( handle, std::move(callback), proxy_auth_callback, fail_if_alias_requires_proxy_override, priority, socket_tag, respect_limits, NORMAL, std::move(params), proxy_annotation_tag, net_log);

// Cleanup any timed-out idle sockets. CleanupIdleSockets(false, nullptr /* net_log_reason_utf8 */);

request->net_log().BeginEvent(NetLogEventType::SOCKET_POOL);

int rv = RequestSocketInternal(group_id, request, /preconnect_done_closure=/base::OnceClosure()); if (rv != ERR_IO_PENDING) { if (rv == OK) { request->handle()->socket()->ApplySocketTag(request->socket_tag()); } request->net_log().EndEventWithNetErrorCode(NetLogEventType::SOCKET_POOL, rv); CHECK(!request->handle()->is_initialized()); request.reset(); } else { Group group = GetOrCreateGroup( group_id, !request->fail_if_alias_requires_proxy_override()); group->InsertUnboundRequest(std::move(request)); // Have to do this asynchronously, as closing sockets in higher level pools // call back in to |this|, which will cause all sorts of fun and exciting // re-entrancy issues if the socket pool is doing something else at the // time. if (group->CanUseAdditionalSocketSlot(max_sockets_per_group_)) { base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, base::BindOnce( &TransportClientSocketPool::TryToCloseSocketsInLayeredPools, weak_factory_.GetWeakPtr())); } } return rv; }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/transport_client_socket_pool.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=391 int TransportClientSocketPool::RequestSocketInternal( const GroupId& group_id, const Request& request, base::OnceClosure preconnect_done_closure) { #if DCHECK_IS_ON() DCHECK(!request_in_process_); base::AutoReset auto_reset(&request_in_process_, true); #endif // DCHECK_IS_ON()

ClientSocketHandle* const handle = request.handle(); const bool preconnecting = !handle; DCHECK_EQ(preconnecting, !!preconnect_done_closure);

Group* group = nullptr; auto group_it = group_map_.find(group_id); if (group_it != group_map_.end()) { group = group_it->second;

if (!(request.flags() & NO_IDLE_SOCKETS)) {
  // Try to reuse a socket.
  if (AssignIdleSocketToRequest(request, group))
    return OK;
}

// If there are more ConnectJobs than pending requests, don't need to do
// anything.  Can just wait for the extra job to connect, and then assign it
// to the request.
if (!preconnecting && group->TryToUseNeverAssignedConnectJob())
  return ERR_IO_PENDING;

// Can we make another active socket now?
if (!group->HasAvailableSocketSlot(max_sockets_per_group_) &&
    request.respect_limits() == RespectLimits::ENABLED) {
  // TODO(willchan): Consider whether or not we need to close a socket in a
  // higher layered group. I don't think this makes sense since we would
  // just reuse that socket then if we needed one and wouldn't make it down
  // to this layer.
  request.net_log().AddEvent(
      NetLogEventType::SOCKET_POOL_STALLED_MAX_SOCKETS_PER_GROUP);
  return preconnecting ? ERR_PRECONNECT_MAX_SOCKET_LIMIT : ERR_IO_PENDING;
}

}

if (ReachedMaxSocketsLimit() && request.respect_limits() == RespectLimits::ENABLED) { // NOTE(mmenke): Wonder if we really need different code for each case // here. Only reason for them now seems to be preconnects. if (idle_socket_count_ > 0) { // There’s an idle socket in this pool. Either that’s because there’s // still one in this group, but we got here due to preconnecting // bypassing idle sockets, or because there’s an idle socket in another // group. bool closed = CloseOneIdleSocketExceptInGroup(group); if (preconnecting && !closed) return ERR_PRECONNECT_MAX_SOCKET_LIMIT; } else { // We could check if we really have a stalled group here, but it // requires a scan of all groups, so just flip a flag here, and do the // check later. request.net_log().AddEvent( NetLogEventType::SOCKET_POOL_STALLED_MAX_SOCKETS); return preconnecting ? ERR_PRECONNECT_MAX_SOCKET_LIMIT : ERR_IO_PENDING; } }

// We couldn’t find a socket to reuse, and there’s space to allocate one, // so allocate and connect a new one. group = GetOrCreateGroup(group_id, !request.fail_if_alias_requires_proxy_override()); std::unique_ptr connect_job( CreateConnectJob(group_id, request.socket_params(), proxy_chain_, request.proxy_annotation_tag(), request.priority(), request.socket_tag(), group)); connect_job->net_log().AddEvent( NetLogEventType::SOCKET_POOL_CONNECT_JOB_CREATED, [&] { return NetLogCreateConnectJobParams(false /* backup_job */, &group_id); });

int rv = connect_job->Connect(); if (rv == ERR_IO_PENDING) { if (preconnect_done_closure) { DCHECK(preconnecting); connect_job->set_done_closure(std::move(preconnect_done_closure)); } // If we didn’t have any sockets in this group, set a timer for potentially // creating a new one. If the SYN is lost, this backup socket may complete // before the slow socket, improving end user latency. if (connect_backup_jobs_enabled_ && group->IsEmpty()) group->StartBackupJobTimer(group_id); group->AddJob(std::move(connect_job), preconnecting); connecting_socket_count_++; return rv; }

LogBoundConnectJobToRequest(connect_job->net_log().source(), request); if (preconnecting) { if (rv == OK) AddIdleSocket(connect_job->PassSocket(), group); } else { DCHECK(handle); if (rv != OK) handle->SetAdditionalErrorState(connect_job.get()); std::unique_ptr socket = connect_job->PassSocket(); if (socket) { HandOutSocket(std::move(socket), StreamSocketHandle::SocketReuseType::kUnused, connect_job->connect_timing(), handle, /time_idle=/base::TimeDelta(), group, request.net_log()); } } if (group->IsEmpty()) RemoveGroup(group_id);

return rv; }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/connect_job.cc;l=130;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1 int ConnectJob::Connect() { if (!timeout_duration_.is_zero()) { timer_.Start(FROM_HERE, timeout_duration_, this, &ConnectJob::OnTimeout); }

LogConnectStart();

int rv = ConnectInternal();

if (rv != ERR_IO_PENDING) { LogConnectCompletion(rv); delegate_ = nullptr; }

return rv; }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/ssl_connect_job.cc;l=488;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1

int SSLConnectJob::ConnectInternal() { next_state_ = GetInitialState(params_->GetConnectionType()); return DoLoop(OK); }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/ssl_connect_job.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=201 int SSLConnectJob::DoLoop(int result) { TRACE_EVENT0(NetTracingCategory(), “SSLConnectJob::DoLoop”); DCHECK_NE(next_state_, STATE_NONE);

int rv = result; do { State state = next_state_; next_state_ = STATE_NONE; switch (state) { case STATE_TRANSPORT_CONNECT: DCHECK_EQ(OK, rv); rv = DoTransportConnect(); break; case STATE_TRANSPORT_CONNECT_COMPLETE: rv = DoTransportConnectComplete(rv); break; case STATE_SOCKS_CONNECT: DCHECK_EQ(OK, rv); rv = DoSOCKSConnect(); break; case STATE_SOCKS_CONNECT_COMPLETE: rv = DoSOCKSConnectComplete(rv); break; case STATE_TUNNEL_CONNECT: DCHECK_EQ(OK, rv); rv = DoTunnelConnect(); break; case STATE_TUNNEL_CONNECT_COMPLETE: rv = DoTunnelConnectComplete(rv); break; case STATE_SSL_CONNECT: DCHECK_EQ(OK, rv); rv = DoSSLConnect(); break; case STATE_SSL_CONNECT_COMPLETE: rv = DoSSLConnectComplete(rv); break; default: NOTREACHED() « “bad state”; } } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);

return rv; }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/ssl_connect_job.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=246 int SSLConnectJob::DoTransportConnect() { DCHECK(!nested_connect_job_); DCHECK(params_->GetDirectConnectionParams()); DCHECK(!TimerIsRunning());

next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE; // If this is an ECH retry, connect to the same server as before. std::optionalTransportConnectJob::EndpointResultOverride endpoint_result_override; if (ech_retry_configs_) { DCHECK(ssl_client_context()->config().ech_enabled); DCHECK(endpoint_result_); endpoint_result_override.emplace(*endpoint_result_, dns_aliases_); } nested_connect_job_ = std::make_unique( priority(), socket_tag(), common_connect_job_params(), params_->GetDirectConnectionParams(), this, &net_log(), std::move(endpoint_result_override)); return nested_connect_job_->Connect(); }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/transport_connect_job.cc;l=513;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1 int TransportConnectJob::ConnectInternal() { next_state_ = STATE_RESOLVE_HOST; return DoLoop(OK); }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/transport_connect_job.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=209 int TransportConnectJob::DoLoop(int result) { DCHECK_NE(next_state_, STATE_NONE);

int rv = result; do { State state = next_state_; next_state_ = STATE_NONE; switch (state) { case STATE_RESOLVE_HOST: DCHECK_EQ(OK, rv); rv = DoResolveHost(); break; case STATE_RESOLVE_HOST_COMPLETE: rv = DoResolveHostComplete(rv); break; case STATE_RESOLVE_HOST_CALLBACK_COMPLETE: DCHECK_EQ(OK, rv); rv = DoResolveHostCallbackComplete(); break; case STATE_TRANSPORT_CONNECT: DCHECK_EQ(OK, rv); rv = DoTransportConnect(); break; case STATE_TRANSPORT_CONNECT_COMPLETE: rv = DoTransportConnectComplete(rv); break; default: NOTREACHED(); } } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);

return rv; }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/transport_connect_job.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=243 int TransportConnectJob::DoResolveHost() { connect_timing_.domain_lookup_start = base::TimeTicks::Now();

if (has_dns_override_) { DCHECK_EQ(1u, endpoint_results_.size()); connect_timing_.domain_lookup_end = connect_timing_.domain_lookup_start; next_state_ = STATE_TRANSPORT_CONNECT; return OK; }

next_state_ = STATE_RESOLVE_HOST_COMPLETE;

HostResolver::ResolveHostParameters parameters; parameters.initial_priority = priority(); parameters.secure_dns_policy = params_->secure_dns_policy(); if (std::holds_alternativeurl::SchemeHostPort (params_->destination())) { request_ = host_resolver()->CreateRequest( std::geturl::SchemeHostPort (params_->destination()), params_->network_anonymization_key(), net_log(), parameters); } else { request_ = host_resolver()->CreateRequest( std::get(params_->destination()), params_->network_anonymization_key(), net_log(), parameters); }

return request_->Start(base::BindOnce(&TransportConnectJob::OnIOComplete, base::Unretained(this))); }

https://source.chromium.org/chromium/chromium/src/+/main:net/dns/host_resolver_manager_request_impl.cc;l=73;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1 int HostResolverManager::RequestImpl::Start(CompletionOnceCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(callback); // Start() may only be called once per request. CHECK(!job_.has_value()); DCHECK(!complete_); DCHECK(!callback_); // Parent HostResolver must still be alive to call Start(). DCHECK(resolver_);

if (!resolve_context_) { complete_ = true; resolver_.reset(); set_error_info(ERR_CONTEXT_SHUT_DOWN, false); return ERR_NAME_NOT_RESOLVED; }

LogStartRequest();

next_state_ = STATE_IPV6_REACHABILITY; callback_ = std::move(callback);

int rv = OK; rv = DoLoop(rv); return rv; }

https://source.chromium.org/chromium/chromium/src/+/main:net/dns/host_resolver_manager_request_impl.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=244 int HostResolverManager::RequestImpl::DoLoop(int rv) { do { ResolveState state = next_state_; next_state_ = STATE_NONE; switch (state) { case STATE_IPV6_REACHABILITY: rv = DoIPv6Reachability(); break; case STATE_GET_PARAMETERS: DCHECK_EQ(OK, rv); rv = DoGetParameters(); break; case STATE_GET_PARAMETERS_COMPLETE: rv = DoGetParametersComplete(rv); break; case STATE_RESOLVE_LOCALLY: rv = DoResolveLocally(); break; case STATE_START_JOB: rv = DoStartJob(); break; case STATE_FINISH_REQUEST: rv = DoFinishRequest(rv); break; default: NOTREACHED() « “next_state_: " « next_state_; } } while (next_state_ != STATE_NONE && rv != ERR_IO_PENDING);

return rv; }

https://source.chromium.org/chromium/chromium/src/+/main:net/dns/host_resolver_manager_request_impl.cc;l=355;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1 int HostResolverManager::RequestImpl::DoStartJob() { resolver_->CreateAndStartJob(std::move(job_key_), std::move(tasks_), this); DCHECK(!complete_); resolver_.reset(); return ERR_IO_PENDING; }

https://source.chromium.org/chromium/chromium/src/+/main:net/dns/host_resolver_manager.cc;l=951;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1 void HostResolverManager::CreateAndStartJob(JobKey key, std::deque tasks, RequestImpl* request) { DCHECK(!tasks.empty());

auto jobit = jobs_.find(key); Job* job; if (jobit == jobs_.end()) { job = AddJobWithoutRequest(key, request->parameters().cache_usage, request->host_cache(), std::move(tasks), request->priority(), request->source_net_log()); job->AddRequest(request); job->RunNextTask(); } else { job = jobit->second.get(); job->AddRequest(request); } }

https://source.chromium.org/chromium/chromium/src/+/main:net/dns/host_resolver_manager_job.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=401 void HostResolverManager::Job::RunNextTask() { // If there are no tasks left to try, cache any stored results and complete // the request with the last stored result. All stored results should be // errors. if (tasks_.empty()) { // If there are no stored results, complete with an error. if (completion_results_.size() == 0) { CompleteRequestsWithError(ERR_NAME_NOT_RESOLVED, /task_type=/std::nullopt); return; }

// Cache all but the last result here. The last result will be cached
// as part of CompleteRequests.
for (size_t i = 0; i < completion_results_.size() - 1; ++i) {
  const auto& result = completion_results_[i];
  DCHECK_NE(OK, result.entry.error());
  MaybeCacheResult(result.entry, result.ttl, result.secure);
}
const auto& last_result = completion_results_.back();
DCHECK_NE(OK, last_result.entry.error());
CompleteRequests(last_result.entry, last_result.ttl, true /* allow_cache */,
                 last_result.secure,
                 last_result.secure ? TaskType::SECURE_DNS : TaskType::DNS);
return;

}

TaskType next_task = tasks_.front();

// Schedule insecure DnsTasks and HostResolverSystemTasks with the // dispatcher. if (!dispatched_ && (next_task == TaskType::DNS || next_task == TaskType::SYSTEM || next_task == TaskType::MDNS)) { dispatched_ = true; job_running_ = false; Schedule(false); DCHECK(is_running() || is_queued());

// Check for queue overflow.
PrioritizedDispatcher& dispatcher = *resolver_->dispatcher_;
if (dispatcher.num_queued_jobs() > resolver_->max_queued_jobs_) {
  Job* evicted = static_cast<Job*>(dispatcher.EvictOldestLowest());
  DCHECK(evicted);
  evicted->OnEvicted();
}
return;

}

if (start_time_ == base::TimeTicks()) { net_log_.AddEvent(NetLogEventType::HOST_RESOLVER_MANAGER_JOB_STARTED); start_time_ = tick_clock_->NowTicks(); } tasks_.pop_front(); job_running_ = true;

switch (next_task) { case TaskType::SYSTEM: StartSystemTask(); break; case TaskType::DNS: StartDnsTask(false /* secure /); break; case TaskType::SECURE_DNS: StartDnsTask(true / secure */); break; case TaskType::MDNS: StartMdnsTask(); break; case TaskType::INSECURE_CACHE_LOOKUP: InsecureCacheLookup(); break; case TaskType::NAT64: StartNat64Task(); break; case TaskType::SECURE_CACHE_LOOKUP: case TaskType::CACHE_LOOKUP: case TaskType::CONFIG_PRESET: case TaskType::HOSTS: // These task types should have been handled synchronously in // ResolveLocally() prior to Job creation. NOTREACHED(); } }

まずはとりあえずSystemTaskの方から向かってみる。 https://source.chromium.org/chromium/chromium/src/+/main:net/dns/host_resolver_manager_job.cc;l=621;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1?q=StartSystemTask&sq=&ss=chromium%2Fchromium%2Fsrc void HostResolverManager::Job::StartSystemTask() { DCHECK(dispatched_); DCHECK_EQ(1, num_occupied_job_slots_); DCHECK(HasAddressType(key_.query_types));

std::optionalHostResolverSystemTask::CacheParams cache_params; if (key_.resolve_context->host_resolver_cache()) { cache_params.emplace(*key_.resolve_context->host_resolver_cache(), key_.network_anonymization_key); }

system_task_ = HostResolverSystemTask::Create( std::string(key_.host.GetHostnameWithoutBrackets()), HostResolver::DnsQueryTypeSetToAddressFamily(key_.query_types), key_.flags, resolver_->host_resolver_system_params_, net_log_, key_.GetTargetNetwork(), std::move(cache_params));

// Start() could be called from within Resolve(), hence it must NOT directly // call OnSystemTaskComplete, for example, on synchronous failure. system_task_->Start(base::BindOnce(&Job::OnSystemTaskComplete, base::Unretained(this), tick_clock_->NowTicks())); }

https://source.chromium.org/chromium/chromium/src/+/main:net/dns/host_resolver_system_task.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=299 void HostResolverSystemTask::Start(SystemDnsResultsCallback results_cb) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(results_cb); DCHECK(!results_cb_); results_cb_ = std::move(results_cb); net_log_.BeginEvent(NetLogEventType::HOST_RESOLVER_SYSTEM_TASK); StartLookupAttempt(); }

void HostResolverSystemTask::StartLookupAttempt() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!was_completed()); ++attempt_number_;

net_log_.AddEventWithIntParams( NetLogEventType::HOST_RESOLVER_MANAGER_ATTEMPT_STARTED, “attempt_number”, attempt_number_);

// If the results aren’t received within a given time, RetryIfNotComplete // will start a new attempt if none of the outstanding attempts have // completed yet. // Use a WeakPtr to avoid keeping the HostResolverSystemTask alive after // completion or cancellation. if (attempt_number_ <= params_.max_retry_attempts) { base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( FROM_HERE, base::BindOnce(&HostResolverSystemTask::StartLookupAttempt, weak_ptr_factory_.GetWeakPtr()), params_.unresponsive_delay * std::pow(params_.retry_factor, attempt_number_ - 1)); }

auto lookup_complete_cb = base::BindOnce(&HostResolverSystemTask::OnLookupComplete, weak_ptr_factory_.GetWeakPtr(), attempt_number_);

// If a hook has been installed, call it instead of posting a resolution task // to a worker thread. if (GetSystemDnsResolverOverride()) { GetSystemDnsResolverOverride().Run(hostname_, address_family_, flags_, std::move(lookup_complete_cb), network_); // Do not add code below. lookup_complete_cb may have already deleted // this. } else { base::OnceCallback<int(AddressList * addrlist, int* os_error)> resolve_cb = base::BindOnce(&ResolveOnWorkerThread, params_.resolver_proc, hostname_, address_family_, flags_, network_); PostSystemDnsResolutionTaskAndReply(std::move(resolve_cb), std::move(lookup_complete_cb)); } }

https://source.chromium.org/chromium/chromium/src/+/main:net/dns/host_resolver_system_task.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=74 // Posts a synchronous callback to a thread pool task runner created with // MayBlock, USER_BLOCKING, and CONTINUE_ON_SHUTDOWN. This task runner can be // overridden by assigning to GetSystemDnsResolutionTaskRunnerOverride(). // results_cb will be called later on the current sequence with the results of // the DNS resolution. void PostSystemDnsResolutionTaskAndReply( base::OnceCallback<int(AddressList* addrlist, int* os_error)> system_dns_resolution_callback, SystemDnsResultsCallback results_cb) { auto addr_list = std::make_uniquenet::AddressList (); net::AddressList* addr_list_ptr = addr_list.get(); auto os_error = std::make_unique(); int* os_error_ptr = os_error.get();

// This callback owns |addr_list| and |os_error| and just calls |results_cb| // with the results. auto call_with_results_cb = base::BindOnce( [](SystemDnsResultsCallback results_cb, std::unique_ptrnet::AddressList addr_list, std::unique_ptr os_error, int net_error) { std::move(results_cb).Run(std::move(*addr_list), *os_error, net_error); }, std::move(results_cb), std::move(addr_list), std::move(os_error));

scoped_refptrbase::TaskRunner system_dns_resolution_task_runner = GetSystemDnsResolutionTaskRunnerOverride(); if (!system_dns_resolution_task_runner) { // In production this will run on every call, otherwise some tests will // leave a stale task runner around after tearing down their task // environment. This should not be less performant than the regular // base::ThreadPool::PostTask(). system_dns_resolution_task_runner = base::ThreadPool::CreateTaskRunner( {base::MayBlock(), base::TaskPriority::USER_BLOCKING, base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}); } system_dns_resolution_task_runner->PostTaskAndReplyWithResult( FROM_HERE, base::BindOnce(std::move(system_dns_resolution_callback), addr_list_ptr, os_error_ptr), std::move(call_with_results_cb)); }

https://source.chromium.org/chromium/chromium/src/+/main:net/dns/host_resolver_system_task.cc;l=111?q=PostSystemDnsResolutionTaskAndReply&ss=chromium%2Fchromium%2Fsrc int ResolveOnWorkerThread(scoped_refptr resolver_proc, std::optionalstd::string hostname, AddressFamily address_family, HostResolverFlags flags, handles::NetworkHandle network, AddressList* addrlist, int* os_error) { std::string hostname_str = hostname ? *std::move(hostname) : GetHostName(); if (resolver_proc) { return resolver_proc->Resolve(hostname_str, address_family, flags, addrlist, os_error, network); } else { return SystemHostResolverCall(hostname_str, address_family, flags, addrlist, os_error, network); } }

https://source.chromium.org/chromium/chromium/src/+/main:net/dns/host_resolver_system_task.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=490 int SystemHostResolverCall(const std::string& host, AddressFamily address_family, HostResolverFlags host_resolver_flags, AddressList* addrlist, int* os_error_opt, handles::NetworkHandle network) { struct addrinfo hints = {0}; hints.ai_family = AddressFamilyToAF(address_family);

#if BUILDFLAG(IS_WIN) // DO NOT USE AI_ADDRCONFIG ON WINDOWS. // // The following comment in <winsock2.h> is the best documentation I found // on AI_ADDRCONFIG for Windows: // Flags used in “hints” argument to getaddrinfo() // - AI_ADDRCONFIG is supported starting with Vista // - default is AI_ADDRCONFIG ON whether the flag is set or not // because the performance penalty in not having ADDRCONFIG in // the multi-protocol stack environment is severe; // this defaulting may be disabled by specifying the AI_ALL flag, // in that case AI_ADDRCONFIG must be EXPLICITLY specified to // enable ADDRCONFIG behavior // // Not only is AI_ADDRCONFIG unnecessary, but it can be harmful. If the // computer is not connected to a network, AI_ADDRCONFIG causes getaddrinfo // to fail with WSANO_DATA (11004) for “localhost”, probably because of the // following note on AI_ADDRCONFIG in the MSDN getaddrinfo page: // The IPv4 or IPv6 loopback address is not considered a valid global // address. // See http://crbug.com/5234 . // // OpenBSD does not support it, either. hints.ai_flags = 0; #else // On other operating systems, AI_ADDRCONFIG may reduce the amount of // unnecessary DNS lookups, e.g. getaddrinfo() will not send a request for // AAAA records if the current machine has no IPv6 addresses configured and // therefore could not use the resulting AAAA record anyway. On some ancient // routers, AAAA DNS queries won’t be handled correctly and will cause // multiple retransmitions and large latency spikes. hints.ai_flags = AI_ADDRCONFIG; #endif

// On Linux AI_ADDRCONFIG doesn’t consider loopback addresses, even if only // loopback addresses are configured. So don’t use it when there are only // loopback addresses. See loopback_only.h and // https://fedoraproject.org/wiki/QA/Networking/NameResolution/ADDRCONFIG for // a description of some of the issues AI_ADDRCONFIG can cause. if (host_resolver_flags & HOST_RESOLVER_LOOPBACK_ONLY) { hints.ai_flags &= ~AI_ADDRCONFIG; }

if (host_resolver_flags & HOST_RESOLVER_CANONNAME) hints.ai_flags |= AI_CANONNAME;

#if BUILDFLAG(IS_WIN) // See crbug.com/1176970. Flag not documented (other than the declaration // comment in ws2def.h) but confirmed by Microsoft to work for this purpose // and be safe. if (host_resolver_flags & HOST_RESOLVER_AVOID_MULTICAST) hints.ai_flags |= AI_DNS_ONLY; #endif // BUILDFLAG(IS_WIN)

// Restrict result set to only this socket type to avoid duplicates. hints.ai_socktype = SOCK_STREAM;

// This function can block for a long time. Use ScopedBlockingCall to increase // the current thread pool’s capacity and thus avoid reducing CPU usage by the // current process during that time. base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::BlockingType::WILL_BLOCK); DnsReloaderMaybeReload();

auto [ai, err, os_error] = AddressInfo::Get(host, hints, nullptr, network); bool should_retry = false; // If the lookup was restricted (either by address family, or address // detection), and the results where all localhost of a single family, // maybe we should retry. There were several bugs related to these // issues, for example http://crbug.com/42058 and http://crbug.com/49024 if ((hints.ai_family != AF_UNSPEC || hints.ai_flags & AI_ADDRCONFIG) && ai && ai->IsAllLocalhostOfOneFamily()) { if (host_resolver_flags & HOST_RESOLVER_DEFAULT_FAMILY_SET_DUE_TO_NO_IPV6) { hints.ai_family = AF_UNSPEC; should_retry = true; } if (hints.ai_flags & AI_ADDRCONFIG) { hints.ai_flags &= ~AI_ADDRCONFIG; should_retry = true; } } if (should_retry) { std::tie(ai, err, os_error) = AddressInfo::Get(host, hints, nullptr, network); }

if (os_error_opt) *os_error_opt = os_error;

if (!ai) return err;

*addrlist = ai->CreateAddressList(); return OK; }

https://source.chromium.org/chromium/chromium/src/+/main:net/dns/address_info.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=56 AddressInfo::AddressInfoAndResult AddressInfo::Get( const std::string& host, const addrinfo& hints, std::unique_ptr getter, handles::NetworkHandle network) { if (getter == nullptr) getter = std::make_unique(); int err = OK; int os_error = 0; std::unique_ptr<addrinfo, FreeAddrInfoFunc> ai = getter->getaddrinfo(host, &hints, &os_error, network);

if (!ai) { err = ERR_NAME_NOT_RESOLVED;

// If the call to getaddrinfo() failed because of a system error, report
// it separately from ERR_NAME_NOT_RESOLVED.

#if BUILDFLAG(IS_WIN) if (os_error != WSAHOST_NOT_FOUND && os_error != WSANO_DATA) err = ERR_NAME_RESOLUTION_FAILED; #elif BUILDFLAG(IS_ANDROID) // Workaround for Android’s getaddrinfo leaving ai==nullptr without an // error. // http://crbug.com/134142 err = ERR_NAME_NOT_RESOLVED; #elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_FREEBSD) if (os_error != EAI_NONAME && os_error != EAI_NODATA) err = ERR_NAME_RESOLUTION_FAILED; #endif

return AddressInfoAndResult(std::optional<AddressInfo>(), err, os_error);

}

return AddressInfoAndResult( std::optional(AddressInfo(std::move(ai), std::move(getter))), OK, 0); }

getaddrinfo関数の呼び出しに到達。 https://learn.microsoft.com/ja-jp/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfo 本仮定ではクライアント環境がWindowsであるためこれ以上はリバースエンジニアリングでもしないと遡れないが下手に触ってライセンスの問題を踏みたくないためこれ以上はクライアントサイドではいかない。 https://source.chromium.org/chromium/chromium/src/+/main:net/dns/address_info.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=178 std::unique_ptr<addrinfo, FreeAddrInfoFunc> AddrInfoGetter::getaddrinfo( const std::string& host, const addrinfo* hints, int* out_os_error, handles::NetworkHandle network) { addrinfo* ai; // We wrap freeaddrinfo() in a lambda just in case some operating systems use // a different signature for it. FreeAddrInfoFunc deleter = [](addrinfo* ai) { ::freeaddrinfo(ai); };

std::unique_ptr<addrinfo, FreeAddrInfoFunc> rv = {nullptr, deleter};

if (network != handles::kInvalidNetworkHandle) { // Currently, only Android supports lookups for a specific network. #if BUILDFLAG(IS_ANDROID) *out_os_error = android::GetAddrInfoForNetwork(network, host.c_str(), nullptr, hints, &ai); #elif BUILDFLAG(IS_WIN) *out_os_error = WSAEOPNOTSUPP; return rv; #else errno = ENOSYS; *out_os_error = EAI_SYSTEM; return rv; #endif // BUILDFLAG(IS_ANDROID) } else { *out_os_error = ::getaddrinfo(host.c_str(), nullptr, hints, &ai); }

if (*out_os_error) { #if BUILDFLAG(IS_WIN) *out_os_error = WSAGetLastError(); #endif return rv; }

rv.reset(ai); return rv; }

getaddrinfo実装は少なくとも同一プロセス内では様々なキャッシュ戦略を用いているため常にDNS Queryを飛ばしているわけではなく従って複数回アクセスしようとしても観測できないことも多いため、代わりにcurlを使ってみたところ Wiresharkで観測できた限りでは以下のようなリクエストを

Domain Name System (query)
    Transaction ID: 0xa855
    Flags: 0x0100 Standard query
    Questions: 1
    Answer RRs: 0
    Authority RRs: 0
    Additional RRs: 0
    Queries
        www.shonenjump.com: type A, class IN
            Name: www.shonenjump.com
            [Name Length: 18]
            [Label Count: 3]
            Type: A (1) (Host Address)
            Class: IN (0x0001)
    [Response In: 15886]

Domain Name System (response)
    Transaction ID: 0xa855
    Flags: 0x8180 Standard query response, No error
    Questions: 1
    Answer RRs: 1
    Authority RRs: 4
    Additional RRs: 8
    Queries
        www.shonenjump.com: type A, class IN
            Name: www.shonenjump.com
            [Name Length: 18]
            [Label Count: 3]
            Type: A (1) (Host Address)
            Class: IN (0x0001)
    Answers
        www.shonenjump.com: type A, class IN, addr 202.218.223.232
            Name: www.shonenjump.com
            Type: A (1) (Host Address)
            Class: IN (0x0001)
            Time to live: 105 (1 minute, 45 seconds)
            Data length: 4
            Address: 202.218.223.232
    Authoritative nameservers
        shonenjump.com: type NS, class IN, ns ns-816.awsdns-38.net
            Name: shonenjump.com
            Type: NS (2) (authoritative Name Server)
            Class: IN (0x0001)
            Time to live: 49170 (13 hours, 39 minutes, 30 seconds)
            Data length: 22
            Name Server: ns-816.awsdns-38.net
        shonenjump.com: type NS, class IN, ns ns-509.awsdns-63.com
            Name: shonenjump.com
            Type: NS (2) (authoritative Name Server)
            Class: IN (0x0001)
            Time to live: 49170 (13 hours, 39 minutes, 30 seconds)
            Data length: 19
            Name Server: ns-509.awsdns-63.com
        shonenjump.com: type NS, class IN, ns ns-1592.awsdns-07.co.uk
            Name: shonenjump.com
            Type: NS (2) (authoritative Name Server)
            Class: IN (0x0001)
            Time to live: 49170 (13 hours, 39 minutes, 30 seconds)
            Data length: 25
            Name Server: ns-1592.awsdns-07.co.uk
        shonenjump.com: type NS, class IN, ns ns-1230.awsdns-25.org
            Name: shonenjump.com
            Type: NS (2) (authoritative Name Server)
            Class: IN (0x0001)
            Time to live: 49170 (13 hours, 39 minutes, 30 seconds)
            Data length: 23
            Name Server: ns-1230.awsdns-25.org
    Additional records
        ns-509.awsdns-63.com: type AAAA, class IN, addr 2600:9000:5301:fd00::1
            Name: ns-509.awsdns-63.com
            Type: AAAA (28) (IP6 Address)
            Class: IN (0x0001)
            Time to live: 36894 (10 hours, 14 minutes, 54 seconds)
            Data length: 16
            AAAA Address: 2600:9000:5301:fd00::1
        ns-816.awsdns-38.net: type AAAA, class IN, addr 2600:9000:5303:3000::1
            Name: ns-816.awsdns-38.net
            Type: AAAA (28) (IP6 Address)
            Class: IN (0x0001)
            Time to live: 37691 (10 hours, 28 minutes, 11 seconds)
            Data length: 16
            AAAA Address: 2600:9000:5303:3000::1
        ns-1230.awsdns-25.org: type AAAA, class IN, addr 2600:9000:5304:ce00::1
            Name: ns-1230.awsdns-25.org
            Type: AAAA (28) (IP6 Address)
            Class: IN (0x0001)
            Time to live: 43955 (12 hours, 12 minutes, 35 seconds)
            Data length: 16
            AAAA Address: 2600:9000:5304:ce00::1
        ns-1592.awsdns-07.co.uk: type AAAA, class IN, addr 2600:9000:5306:3800::1
            Name: ns-1592.awsdns-07.co.uk
            Type: AAAA (28) (IP6 Address)
            Class: IN (0x0001)
            Time to live: 36677 (10 hours, 11 minutes, 17 seconds)
            Data length: 16
            AAAA Address: 2600:9000:5306:3800::1
        ns-509.awsdns-63.com: type A, class IN, addr 205.251.193.253
            Name: ns-509.awsdns-63.com
            Type: A (1) (Host Address)
            Class: IN (0x0001)
            Time to live: 36894 (10 hours, 14 minutes, 54 seconds)
            Data length: 4
            Address: 205.251.193.253
        ns-816.awsdns-38.net: type A, class IN, addr 205.251.195.48
            Name: ns-816.awsdns-38.net
            Type: A (1) (Host Address)
            Class: IN (0x0001)
            Time to live: 37691 (10 hours, 28 minutes, 11 seconds)
            Data length: 4
            Address: 205.251.195.48
        ns-1230.awsdns-25.org: type A, class IN, addr 205.251.196.206
            Name: ns-1230.awsdns-25.org
            Type: A (1) (Host Address)
            Class: IN (0x0001)
            Time to live: 43955 (12 hours, 12 minutes, 35 seconds)
            Data length: 4
            Address: 205.251.196.206
        ns-1592.awsdns-07.co.uk: type A, class IN, addr 205.251.198.56
            Name: ns-1592.awsdns-07.co.uk
            Type: A (1) (Host Address)
            Class: IN (0x0001)
            Time to live: 36677 (10 hours, 11 minutes, 17 seconds)
            Data length: 4
            Address: 205.251.198.56
    [Request In: 15885]
    [Time: 0.015522000 seconds]

推測通りAWSのRoute 56を使っているものと思われる。

とはいえ近年のブラウザは独自のDNS実装を使っていることも多いためそちらも眺めていく。 https://source.chromium.org/chromium/chromium/src/+/main:net/dns/host_resolver_manager_job.cc;l=707;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133?q=StartDnsTask&sq=&ss=chromium%2Fchromium%2Fsrc void HostResolverManager::Job::StartDnsTask(bool secure) { DCHECK_EQ(secure, !dispatched_); DCHECK_EQ(dispatched_ ? 1 : 0, num_occupied_job_slots_); DCHECK(!resolver_->ShouldForceSystemResolverDueToTestOverride());

// Need to create the task even if we’re going to post a failure instead of // running it, as a “started” job needs a task to be properly cleaned up. dns_task_ = std::make_unique( resolver_->dns_client_.get(), key_.host, key_.network_anonymization_key, key_.query_types, &key_.resolve_context, secure, key_.secure_dns_mode, this, net_log_, tick_clock_, !tasks_.empty() / fallback_available */, https_svcb_options_); if (resolver_->IsHappyEyeballsV3Enabled()) { dns_task_results_manager_ = std::make_unique( this, key_.host, key_.query_types, net_log_); } dns_task_->StartNextTransaction(); // Schedule a second transaction, if needed. DoH queries can bypass the // dispatcher and start all of their transactions immediately. if (secure) { while (dns_task_->num_additional_transactions_needed() >= 1) { dns_task_->StartNextTransaction(); } DCHECK_EQ(dns_task_->num_additional_transactions_needed(), 0); } else if (dns_task_->num_additional_transactions_needed() >= 1) { Schedule(true); } }

https://source.chromium.org/chromium/chromium/src/+/main:net/dns/host_resolver_dns_task.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;l=310 void HostResolverDnsTask::StartNextTransaction() { DCHECK_GE(num_additional_transactions_needed(), 1);

if (!any_transaction_started_) { net_log_.BeginEvent(NetLogEventType::HOST_RESOLVER_DNS_TASK, [&] { return NetLogDnsTaskCreationParams(); }); } any_transaction_started_ = true;

TransactionInfo transaction_info = std::move(transactions_needed_.front()); transactions_needed_.pop_front();

DCHECK(IsAddressType(transaction_info.type) || secure_ || client_->CanQueryAdditionalTypesViaInsecureDns());

// Record how long this transaction has been waiting to be created. base::TimeDelta time_queued = tick_clock_->NowTicks() - task_start_time_; UMA_HISTOGRAM_LONG_TIMES_100(“Net.DNS.JobQueueTime.PerTransaction”, time_queued); delegate_->AddTransactionTimeQueued(time_queued);

CreateAndStartTransaction(std::move(transaction_info)); }

https://source.chromium.org/chromium/chromium/src/+/main:net/dns/host_resolver_dns_task.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;l=429 void HostResolverDnsTask::CreateAndStartTransaction( TransactionInfo transaction_info) { DCHECK(!transaction_info.transaction); DCHECK_NE(DnsQueryType::UNSPECIFIED, transaction_info.type);

std::string transaction_hostname(host_.GetHostnameWithoutBrackets());

// For HTTPS, prepend “.https.” for any non-default port. uint16_t request_port = 0; if (transaction_info.type == DnsQueryType::HTTPS && host.HasScheme()) { const auto& scheme_host_port = host.AsSchemeHostPort(); transaction_hostname = dns_util::GetNameForHttpsQuery(scheme_host_port, &request_port); }

transaction_info.transaction = client_->GetTransactionFactory()->CreateTransaction( std::move(transaction_hostname), DnsQueryTypeToQtype(transaction_info.type), net_log_, secure_, secure_dns_mode_, &resolve_context_, fallback_available_ / fast_timeout */); transaction_info.transaction->SetRequestPriority(delegate_->priority());

auto transaction_info_it = transactions_in_progress_.insert(std::move(transaction_info)).first;

// Safe to pass transaction_info_it because it is only modified/removed // after async completion of this call or by destruction (which cancels the // transaction and prevents callback because it owns the DnsTransaction // object). transaction_info_it->transaction->Start(base::BindOnce( &HostResolverDnsTask::OnDnsTransactionComplete, base::Unretained(this), transaction_info_it, request_port)); }

https://source.chromium.org/chromium/chromium/src/+/main:net/dns/dns_transaction.cc;l=1226;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1 void Start(ResponseCallback callback) override { DCHECK(!callback.is_null()); DCHECK(callback_.is_null()); DCHECK(attempts_.empty());

callback_ = std::move(callback);

net_log_.BeginEvent(NetLogEventType::DNS_TRANSACTION,
                    [&] { return NetLogStartParams(hostname_, qtype_); });
time_from_start_ = std::make_unique<base::ElapsedTimer>();
AttemptResult result(PrepareSearch(), nullptr);
if (result.rv == OK) {
  qnames_initial_size_ = qnames_.size();
  result = ProcessAttemptResult(StartQuery());
}

// Must always return result asynchronously, to avoid reentrancy.
if (result.rv != ERR_IO_PENDING) {
  // Clear all other non-completed attempts. They are no longer needed and
  // they may interfere with this posted result.
  ClearAttempts(result.attempt);
  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
      FROM_HERE, base::BindOnce(&DnsTransactionImpl::DoCallback,
                                weak_ptr_factory_.GetWeakPtr(), result));
}

}

クエリの構築 https://source.chromium.org/chromium/chromium/src/+/main:net/dns/dns_transaction.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=1278 // Prepares |qnames_| according to the DnsConfig. int PrepareSearch() { const DnsConfig& config = session_->config();

std::optional<std::vector<uint8_t>> labeled_qname =
    dns_names_util::DottedNameToNetwork(
        hostname_,
        /*require_valid_internet_hostname=*/true);
if (!labeled_qname.has_value())
  return ERR_INVALID_ARGUMENT;

if (hostname_.back() == '.') {
  // It's a fully-qualified name, no suffix search.
  qnames_.push_back(std::move(labeled_qname).value());
  return OK;
}

int ndots = CountLabels(labeled_qname.value()) - 1;

if (ndots > 0 && !config.append_to_multi_label_name) {
  qnames_.push_back(std::move(labeled_qname).value());
  return OK;
}

// Set true when `labeled_qname` is put on the list.
bool had_qname = false;

if (ndots >= config.ndots) {
  qnames_.push_back(labeled_qname.value());
  had_qname = true;
}

for (const auto& suffix : config.search) {
  std::optional<std::vector<uint8_t>> qname =
      dns_names_util::DottedNameToNetwork(
          hostname_ + "." + suffix,
          /*require_valid_internet_hostname=*/true);
  // Ignore invalid (too long) combinations.
  if (!qname.has_value())
    continue;
  if (qname.value().size() == labeled_qname.value().size()) {
    if (had_qname)
      continue;
    had_qname = true;
  }
  qnames_.push_back(std::move(qname).value());
}

if (ndots > 0 && !had_qname)
  qnames_.push_back(std::move(labeled_qname).value());

return qnames_.empty() ? ERR_DNS_SEARCH_EMPTY : OK;

}

DNSクエリの開始 https://source.chromium.org/chromium/chromium/src/+/main:net/dns/dns_transaction.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=1533 AttemptResult StartQuery() { std::optionalstd::string dotted_qname = dns_names_util::NetworkToDottedName(qnames_.front()); net_log_.BeginEventWithStringParams( NetLogEventType::DNS_TRANSACTION_QUERY, “qname”, dotted_qname.value_or(”???MALFORMED_NAME???"));

attempts_.clear();
had_tcp_retry_ = false;
if (secure_) {
  dns_server_iterator_ = resolve_context_->GetDohIterator(
      session_->config(), secure_dns_mode_, session_.get());
} else {
  dns_server_iterator_ = resolve_context_->GetClassicDnsIterator(
      session_->config(), session_.get());
}
DCHECK(dns_server_iterator_);
// Check for available server before starting as DoH servers might be
// unavailable.
if (!dns_server_iterator_->AttemptAvailable())
  return AttemptResult(ERR_BLOCKED_BY_CLIENT, nullptr);

return MakeAttempt();

}

https://source.chromium.org/chromium/chromium/src/+/main:net/dns/dns_transaction.cc;l=1356;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1 AttemptResult MakeAttempt() { DCHECK(MoreAttemptsAllowed());

DnsConfig config = session_->config();
if (secure_) {
  DCHECK(!config.doh_config.servers().empty());
  RecordAttemptUma(DnsAttemptType::kHttp);
  return MakeHTTPAttempt();
}

DCHECK_GT(config.nameservers.size(), 0u);
return MakeClassicDnsAttempt();

}

AttemptResult MakeClassicDnsAttempt() { uint16_t id = session_->NextQueryId(); std::unique_ptr query; if (attempts_.empty()) { query = std::make_unique(id, qnames_.front(), qtype_, opt_rdata_); } else { query = attempts_[0]->GetQuery()->CloneWithNewId(id); } DCHECK(dns_server_iterator_->AttemptAvailable()); size_t server_index = dns_server_iterator_->GetNextAttemptIndex();

size_t attempt_number = attempts_.size();
AttemptResult result;
if (session_->udp_tracker()->low_entropy()) {
  result = MakeTcpAttempt(server_index, std::move(query));
  RecordAttemptUma(DnsAttemptType::kTcpLowEntropy);
} else {
  result = MakeUdpAttempt(server_index, std::move(query));
  RecordAttemptUma(DnsAttemptType::kUdp);
}

if (result.rv == ERR_IO_PENDING) {
  base::TimeDelta fallback_period =
      resolve_context_->NextClassicFallbackPeriod(
          server_index, attempt_number, session_.get());
  timer_.Start(FROM_HERE, fallback_period, this,
               &DnsTransactionImpl::OnFallbackPeriodExpired);
}

return result;

}

// Makes another attempt at the current name, |qnames_.front()|, using the // next nameserver. AttemptResult MakeUdpAttempt(size_t server_index, std::unique_ptr query) { DCHECK(!secure_); DCHECK(!session_->udp_tracker()->low_entropy());

const DnsConfig& config = session_->config();
DCHECK_LT(server_index, config.nameservers.size());
size_t attempt_number = attempts_.size();

std::unique_ptr<DatagramClientSocket> socket =
    resolve_context_->url_request_context()
        ->GetNetworkSessionContext()
        ->client_socket_factory->CreateDatagramClientSocket(
            DatagramSocket::RANDOM_BIND, net_log_.net_log(),
            net_log_.source());

attempts_.push_back(std::make_unique<DnsUDPAttempt>(
    server_index, std::move(socket), config.nameservers[server_index],
    std::move(query), session_->udp_tracker()));
++attempts_count_;

DnsAttempt* attempt = attempts_.back().get();
net_log_.AddEventReferencingSource(NetLogEventType::DNS_TRANSACTION_ATTEMPT,
                                   attempt->GetSocketNetLog().source());

int rv = attempt->Start(base::BindOnce(
    &DnsTransactionImpl::OnAttemptComplete, base::Unretained(this),
    attempt_number, true /* record_rtt */, base::TimeTicks::Now()));
return AttemptResult(rv, attempt);

}

ここのコンストラクタでDNSパケットの構築が行われている。 https://source.chromium.org/chromium/chromium/src/+/main:net/dns/dns_query.cc;l=107;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1 // DNS query consists of a 12-byte header followed by a question section. // For details, see RFC 1035 section 4.1.1. This header template sets RD // bit, which directs the name server to pursue query recursively, and sets // the QDCOUNT to 1, meaning the question section has a single entry. DnsQuery::DnsQuery(uint16_t id, base::span qname, uint16_t qtype, const OptRecordRdata* opt_rdata, PaddingStrategy padding_strategy) : qname_size_(qname.size()) { #if DCHECK_IS_ON() std::optionalstd::string dotted_name = dns_names_util::NetworkToDottedName(qname); DCHECK(dotted_name && !dotted_name.value().empty()); #endif // DCHECK_IS_ON()

size_t buffer_size = kHeaderSize + QuestionSize(qname_size_); std::unique_ptr merged_opt_rdata = AddPaddingIfNecessary(opt_rdata, padding_strategy, buffer_size); if (merged_opt_rdata) buffer_size += OptRecordSize(merged_opt_rdata.get());

io_buffer_ = base::MakeRefCounted(buffer_size);

dns_protocol::Header* header = header_in_io_buffer(); *header = {}; header->id = base::HostToNet16(id); header->flags = base::HostToNet16(dns_protocol::kFlagRD); header->qdcount = base::HostToNet16(1);

// Write question section after the header. auto writer = base::SpanWriter(io_buffer_->span().subspan(kHeaderSize)); writer.Write(qname); writer.WriteU16BigEndian(qtype); writer.WriteU16BigEndian(dns_protocol::kClassIN);

if (merged_opt_rdata) { DCHECK_NE(merged_opt_rdata->OptCount(), 0u);

header->arcount = base::HostToNet16(1);
// Write OPT pseudo-resource record.
writer.WriteU8BigEndian(0);  // empty domain name (root domain)
writer.WriteU16BigEndian(OptRecordRdata::kType);  // type
writer.WriteU16BigEndian(kMaxUdpPayloadSize);     // class
// ttl (next 3 fields)
writer.WriteU8BigEndian(0);  // rcode does not apply to requests
writer.WriteU8BigEndian(0);  // version
// TODO(robpercival): Set "DNSSEC OK" flag if/when DNSSEC is supported:
// https://tools.ietf.org/html/rfc3225#section-3
writer.WriteU16BigEndian(0);  // flags

// rdata
writer.WriteU16BigEndian(merged_opt_rdata->buf().size());  // rdata length
writer.Write(base::as_byte_span(merged_opt_rdata->buf()));

} }

https://source.chromium.org/chromium/chromium/src/+/main:net/dns/dns_transaction.cc;l=226;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1

int Start(CompletionOnceCallback callback) override { DCHECK_EQ(STATE_NONE, next_state_); callback_ = std::move(callback); start_time_ = base::TimeTicks::Now(); next_state_ = STATE_CONNECT_COMPLETE;

int rv = socket_->ConnectAsync(
    server_,
    base::BindOnce(&DnsUDPAttempt::OnIOComplete, base::Unretained(this)));
if (rv == ERR_IO_PENDING) {
  return rv;
}
return DoLoop(rv);

}

https://source.chromium.org/chromium/chromium/src/+/main:net/dns/dns_transaction.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=270 int DoLoop(int result) { CHECK_NE(STATE_NONE, next_state_); int rv = result; do { State state = next_state_; next_state_ = STATE_NONE; switch (state) { case STATE_CONNECT_COMPLETE: rv = DoConnectComplete(rv); break; case STATE_SEND_QUERY: rv = DoSendQuery(rv); break; case STATE_SEND_QUERY_COMPLETE: rv = DoSendQueryComplete(rv); break; case STATE_READ_RESPONSE: rv = DoReadResponse(); break; case STATE_READ_RESPONSE_COMPLETE: rv = DoReadResponseComplete(rv); break; default: NOTREACHED(); } } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);

if (rv != ERR_IO_PENDING)
  DCHECK_EQ(STATE_NONE, next_state_);

return rv;

}

ここでDNSパケットがソケットに渡されている。 https://source.chromium.org/chromium/chromium/src/+/main:net/dns/dns_transaction.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=316 int DoSendQuery(int rv) { DCHECK_NE(ERR_IO_PENDING, rv); if (rv < 0) return rv; next_state_ = STATE_SEND_QUERY_COMPLETE; return socket_->Write( query_->io_buffer(), query_->io_buffer()->size(), base::BindOnce(&DnsUDPAttempt::OnIOComplete, base::Unretained(this)), kTrafficAnnotation); }

おそらくこれ https://source.chromium.org/chromium/chromium/src/+/main:net/socket/udp_client_socket.cc;l=193;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1 int UDPClientSocket::Write( IOBuffer* buf, int buf_len, CompletionOnceCallback callback, const NetworkTrafficAnnotationTag& traffic_annotation) { return socket_.Write(buf, buf_len, std::move(callback), traffic_annotation); }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/udp_socket_win.cc;l=438;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1 int UDPSocketWin::Write( IOBuffer* buf, int buf_len, CompletionOnceCallback callback, const NetworkTrafficAnnotationTag& /* traffic_annotation */) { return SendToOrWrite(buf, buf_len, remote_address_.get(), std::move(callback)); }

int UDPSocketWin::SendTo(IOBuffer* buf, int buf_len, const IPEndPoint& address, CompletionOnceCallback callback) { if (dscp_manager_) { // Alert DscpManager in case this is a new remote address. Failure to // apply Dscp code is never fatal. int rv = dscp_manager_->PrepareForSend(address); if (rv != OK) net_log_.AddEventWithNetErrorCode(NetLogEventType::UDP_SEND_ERROR, rv); } return SendToOrWrite(buf, buf_len, &address, std::move(callback)); }

int UDPSocketWin::SendToOrWrite(IOBuffer* buf, int buf_len, const IPEndPoint* address, CompletionOnceCallback callback) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK_NE(INVALID_SOCKET, socket_); CHECK(write_callback_.is_null()); DCHECK(!callback.is_null()); // Synchronous operation not supported. DCHECK_GT(buf_len, 0); DCHECK(!send_to_address_.get());

int nwrite = core_ ? InternalSendToOverlapped(buf, buf_len, address) : InternalSendToNonBlocking(buf, buf_len, address); if (nwrite != ERR_IO_PENDING) return nwrite;

if (address) send_to_address_ = std::make_unique(*address); write_callback_ = std::move(callback); return ERR_IO_PENDING; }

WSASendToの呼び出しにまで到着 https://learn.microsoft.com/ja-jp/windows/win32/api/winsock2/nf-winsock2-wsasendto https://source.chromium.org/chromium/chromium/src/+/main:net/socket/udp_socket_win.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=1018 int UDPSocketWin::InternalSendToOverlapped(IOBuffer* buf, int buf_len, const IPEndPoint* address) { DCHECK(!core_->write_iobuffer_.get()); SockaddrStorage storage; struct sockaddr* addr = storage.addr(); // Convert address. if (!address) { addr = nullptr; storage.addr_len = 0; } else { if (!address->ToSockAddr(addr, &storage.addr_len)) { int result = ERR_ADDRESS_INVALID; LogWrite(result, nullptr, nullptr); return result; } }

WSABUF write_buffer; write_buffer.buf = buf->data(); write_buffer.len = buf_len;

DWORD flags = 0; DWORD num; int rv; if (send_ecn_ != ECN_NOT_ECT) { WSABUF control_buffer; char raw_control_buffer[WSA_CMSG_SPACE(sizeof(int))]; control_buffer.buf = raw_control_buffer; control_buffer.len = sizeof(raw_control_buffer); WSAMSG message; bool temp_address = !remote_address_.get(); if (temp_address) { remote_address_ = std::make_unique(*address); } PopulateWSAMSG(message, storage, &write_buffer, control_buffer, true); if (temp_address) { remote_address_.reset(); } rv = wsa_send_msg_(socket_, &message, flags, &num, &core_->write_overlapped_, nullptr); } else { rv = WSASendTo(socket_, &write_buffer, 1, &num, flags, addr, storage.addr_len, &core_->write_overlapped_, nullptr); } if (rv == 0) { if (ResetEventIfSignaled(core_->write_overlapped_.hEvent)) { int result = num; LogWrite(result, buf->data(), address); return result; } } else { int os_error = WSAGetLastError(); if (os_error != WSA_IO_PENDING) { int result = MapSystemError(os_error); LogWrite(result, nullptr, nullptr); return result; } }

core_->WatchForWrite(); core_->write_iobuffer_ = buf; return ERR_IO_PENDING; }

そしておそらくだが

https://source.chromium.org/chromium/chromium/src/+/main:net/dns/dns_transaction.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;l=370 int DoReadResponse() { next_state_ = STATE_READ_RESPONSE_COMPLETE; response_ = std::make_unique(); return socket_->Read( response_->io_buffer(), response_->io_buffer_size(), base::BindOnce(&DnsUDPAttempt::OnIOComplete, base::Unretained(this))); }

int DoReadResponseComplete(int rv) { DCHECK_NE(ERR_IO_PENDING, rv); if (rv < 0) return rv; read_size_ = rv;

bool parse_result = response_->InitParse(rv, *query_);
if (response_->id())
  udp_tracker_->RecordResponseId(query_->id(), response_->id().value());

if (!parse_result)
  return ERR_DNS_MALFORMED_RESPONSE;
if (response_->flags() & dns_protocol::kFlagTC)
  return ERR_DNS_SERVER_REQUIRES_TCP;
if (response_->rcode() == dns_protocol::kRcodeNXDOMAIN)
  return ERR_NAME_NOT_RESOLVED;
if (response_->rcode() != dns_protocol::kRcodeNOERROR)
  return ERR_DNS_SERVER_FAILED;

return OK;

}

void OnIOComplete(int rv) { rv = DoLoop(rv); if (rv != ERR_IO_PENDING) std::move(callback_).Run(rv); }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/udp_client_socket.cc;l=187;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1 int UDPClientSocket::Read(IOBuffer* buf, int buf_len, CompletionOnceCallback callback) { return socket_.Read(buf, buf_len, std::move(callback)); }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/udp_socket_win.cc;l=411;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1 int UDPSocketWin::Read(IOBuffer* buf, int buf_len, CompletionOnceCallback callback) { return RecvFrom(buf, buf_len, nullptr, std::move(callback)); }

int UDPSocketWin::RecvFrom(IOBuffer* buf, int buf_len, IPEndPoint* address, CompletionOnceCallback callback) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); DCHECK_NE(INVALID_SOCKET, socket_); CHECK(read_callback_.is_null()); DCHECK(!recv_from_address_); DCHECK(!callback.is_null()); // Synchronous operation not supported. DCHECK_GT(buf_len, 0);

int nread = core_ ? InternalRecvFromOverlapped(buf, buf_len, address) : InternalRecvFromNonBlocking(buf, buf_len, address); if (nread != ERR_IO_PENDING) return nread;

read_callback_ = std::move(callback); recv_from_address_ = address; return ERR_IO_PENDING; }

WSARecvまで到着 https://learn.microsoft.com/ja-jp/windows/win32/api/winsock2/nf-winsock2-wsarecv https://source.chromium.org/chromium/chromium/src/+/main:net/socket/udp_socket_win.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=952 int UDPSocketWin::InternalRecvFromOverlapped(IOBuffer* buf, int buf_len, IPEndPoint* address) { DCHECK(!core_->read_iobuffer_.get()); DCHECK(!core_->read_message_.get()); SockaddrStorage& storage = core_->recv_addr_storage_; storage.addr_len = sizeof(storage.addr_storage);

WSABUF read_buffer; read_buffer.buf = buf->data(); read_buffer.len = buf_len;

DWORD flags = 0; DWORD num; CHECK_NE(INVALID_SOCKET, socket_); int rv; std::unique_ptr message; if (report_ecn_) { WSABUF control_buffer; control_buffer.buf = core_->read_control_buffer_; control_buffer.len = sizeof(core_->read_control_buffer_); message = std::make_unique(); PopulateWSAMSG(*message, storage, &read_buffer, control_buffer, false); rv = wsa_recv_msg_(socket_, message.get(), &num, &core_->read_overlapped_, nullptr); if (rv == 0) { SetLastTosFromWSAMSG(message); } } else { rv = WSARecvFrom(socket_, &read_buffer, 1, &num, &flags, storage.addr(), &storage.addr_len, &core_->read_overlapped_, nullptr); } if (rv == 0) { if (ResetEventIfSignaled(core_->read_overlapped_.hEvent)) { int result = num; // Convert address. IPEndPoint address_storage; IPEndPoint address_to_log = nullptr; if (result >= 0) { if (address_storage.FromSockAddr(core_->recv_addr_storage_.addr(), core_->recv_addr_storage_.addr_len)) { if (address) { *address = address_storage; } address_to_log = &address_storage; } else { result = ERR_ADDRESS_INVALID; } } LogRead(result, buf->data(), address_to_log); return result; } } else { int os_error = WSAGetLastError(); if (os_error != WSA_IO_PENDING) { int result = MapSystemError(os_error); LogRead(result, nullptr, nullptr); return result; } } core_->WatchForRead(); core_->read_iobuffer_ = buf; core_->read_message_ = std::move(message); return ERR_IO_PENDING; }

https://source.chromium.org/chromium/chromium/src/+/main:net/dns/dns_response.h;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;l=125 bool DnsResponse::InitParse(size_t nbytes, const DnsQuery& query) { const std::string_view question = query.question();

// Response includes question, it should be at least that size. if (nbytes < kHeaderSize + question.size() || nbytes > io_buffer_size_) { return false; }

// At this point, it has been validated that the response is at least large // enough to read the ID field. id_available_ = true;

// Match the query id. DCHECK(id()); if (id().value() != query.id()) return false;

// Not a response? if ((base::NetToHost16(header()->flags) & dns_protocol::kFlagResponse) == 0) return false;

// Match question count. if (base::NetToHost16(header()->qdcount) != 1) return false;

base::span subspan = io_buffer_->span().subspan(kHeaderSize, question.size()); // Match the question section. if (question != base::as_string_view(subspan)) { return false; }

std::optionalstd::string dotted_qname = dns_names_util::NetworkToDottedName(query.qname()); if (!dotted_qname.has_value()) return false; dotted_qnames_.push_back(std::move(dotted_qname).value()); qtypes_.push_back(query.qtype());

size_t num_records = base::NetToHost16(header()->ancount) + base::NetToHost16(header()->nscount) + base::NetToHost16(header()->arcount);

// Construct the parser. Only allow parsing up to num_records records. If // more records are present in the buffer, it’s just garbage extra data after // the formal end of the response and should be ignored. parser_ = DnsRecordParser(io_buffer_->first(nbytes), kHeaderSize + question.size(), num_records); return true; }

TransportConnectJob::DoLoopまで戻り、名前解決が完了してTransportConnectJob::DoTransportConnectに入ったところから行う。

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/transport_connect_job.cc;l=417;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c int TransportConnectJob::DoTransportConnect() { next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;

const HostResolverEndpointResult& endpoint = GetEndpointResultForCurrentSubJobs(); std::vector ipv4_addresses, ipv6_addresses; for (const auto& ip_endpoint : endpoint.ip_endpoints) { switch (ip_endpoint.GetFamily()) { case ADDRESS_FAMILY_IPV4: ipv4_addresses.push_back(ip_endpoint); break;

  case ADDRESS_FAMILY_IPV6:
    ipv6_addresses.push_back(ip_endpoint);
    break;

  default:
    DVLOG(1) << "Unexpected ADDRESS_FAMILY: " << ip_endpoint.GetFamily();
    break;
}

}

if (!ipv4_addresses.empty()) { ipv4_job_ = std::make_unique( std::move(ipv4_addresses), this, SUB_JOB_IPV4); }

if (!ipv6_addresses.empty()) { ipv6_job_ = std::make_unique( std::move(ipv6_addresses), this, SUB_JOB_IPV6); int result = ipv6_job_->Start(); if (result != ERR_IO_PENDING) return HandleSubJobComplete(result, ipv6_job_.get()); if (ipv4_job_) { // This use of base::Unretained is safe because |fallback_timer_| is // owned by this object. fallback_timer_.Start( FROM_HERE, kIPv6FallbackTime, base::BindOnce(&TransportConnectJob::StartIPv4JobAsync, base::Unretained(this))); } return ERR_IO_PENDING; }

DCHECK(!ipv6_job_); DCHECK(ipv4_job_); int result = ipv4_job_->Start(); if (result != ERR_IO_PENDING) return HandleSubJobComplete(result, ipv4_job_.get()); return ERR_IO_PENDING; }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/transport_connect_sub_job.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;l=125 // Start connecting. int TransportConnectSubJob::Start() { DCHECK_EQ(STATE_NONE, next_state_); next_state_ = STATE_OBTAIN_LOCK; return DoLoop(OK); }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/transport_connect_sub_job.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;l=163 int TransportConnectSubJob::DoLoop(int result) { DCHECK_NE(next_state_, STATE_NONE);

int rv = result; do { State state = next_state_; next_state_ = STATE_NONE; switch (state) { case STATE_OBTAIN_LOCK: DCHECK_EQ(OK, rv); rv = DoEndpointLock(); break; case STATE_OBTAIN_LOCK_COMPLETE: DCHECK_EQ(OK, rv); rv = DoEndpointLockComplete(); break; case STATE_TRANSPORT_CONNECT_COMPLETE: rv = DoTransportConnectComplete(rv); break; default: NOTREACHED(); } } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE && next_state_ != STATE_DONE);

return rv; }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/transport_connect_sub_job.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;l=244 int TransportConnectSubJob::DoEndpointLockComplete() { next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE; AddressList one_address(CurrentAddress());

// Create a SocketPerformanceWatcher, and pass the ownership. std::unique_ptr socket_performance_watcher; if (auto* factory = parent_job_->socket_performance_watcher_factory(); factory != nullptr) { socket_performance_watcher = factory->CreateSocketPerformanceWatcher( SocketPerformanceWatcherFactory::PROTOCOL_TCP, CurrentAddress().address()); }

const NetLogWithSource& net_log = parent_job_->net_log(); transport_socket_ = parent_job_->client_socket_factory()->CreateTransportClientSocket( one_address, std::move(socket_performance_watcher), parent_job_->network_quality_estimator(), net_log.net_log(), net_log.source());

net_log.AddEvent(NetLogEventType::TRANSPORT_CONNECT_JOB_CONNECT_ATTEMPT, [&] { auto dict = base::Value::Dict().Set(“address”, CurrentAddress().ToString()); transport_socket_->NetLog().source().AddToEventParameters(dict); return dict; });

// If websocket_endpoint_lock_manager_ is non-null, this class now owns an // endpoint lock. Wrap socket in a WebSocketStreamSocket to take ownership // of the lock and release it when the socket goes out of scope. This must // happen before any early returns in this method. if (parent_job_->websocket_endpoint_lock_manager()) { transport_socket_ = std::make_unique( std::move(transport_socket_), parent_job_->websocket_endpoint_lock_manager(), CurrentAddress()); }

transport_socket_->ApplySocketTag(parent_job_->socket_tag());

// This use of base::Unretained() is safe because transport_socket_ is // destroyed in the destructor. return transport_socket_->Connect(base::BindOnce( &TransportConnectSubJob::OnIOComplete, base::Unretained(this))); }

TCPClientSocket::Connect https://source.chromium.org/chromium/chromium/src/+/main:net/socket/tcp_client_socket.cc;l=126;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 int TCPClientSocket::Connect(CompletionOnceCallback callback) { DCHECK(!callback.is_null());

// If connecting or already connected, then just return OK. if (socket_->IsValid() && current_address_index_ >= 0) return OK;

DCHECK(!read_callback_); DCHECK(!write_callback_);

if (was_disconnected_on_suspend_) { Disconnect(); was_disconnected_on_suspend_ = false; }

socket_->StartLoggingMultipleConnectAttempts(addresses_);

// We will try to connect to each address in addresses_. Start with the // first one in the list. next_connect_state_ = CONNECT_STATE_CONNECT; current_address_index_ = 0;

int rv = DoConnectLoop(OK); if (rv == ERR_IO_PENDING) { connect_callback_ = std::move(callback); } else { socket_->EndLoggingMultipleConnectAttempts(rv); }

return rv; }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/tcp_client_socket.cc;l=321;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 int TCPClientSocket::DoConnectLoop(int result) { DCHECK_NE(next_connect_state_, CONNECT_STATE_NONE);

int rv = result; do { ConnectState state = next_connect_state_; next_connect_state_ = CONNECT_STATE_NONE; switch (state) { case CONNECT_STATE_CONNECT: DCHECK_EQ(OK, rv); rv = DoConnect(); break; case CONNECT_STATE_CONNECT_COMPLETE: rv = DoConnectComplete(rv); break; default: NOTREACHED() « “bad state " « state; } } while (rv != ERR_IO_PENDING && next_connect_state_ != CONNECT_STATE_NONE);

return rv; }

TCPClientSocket::DoConnect https://source.chromium.org/chromium/chromium/src/+/main:net/socket/tcp_client_socket.cc;l=231;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

TCPClientSocket::ConnectInternal https://source.chromium.org/chromium/chromium/src/+/main:net/socket/tcp_client_socket.cc;l=286;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

TCPSocketWin::Connect https://source.chromium.org/chromium/chromium/src/+/main:net/socket/tcp_socket_win.cc;l=586;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c

TCPSocketWin::DoConnect ここでconnect関数に到達。 https://learn.microsoft.com/ja-jp/windows/win32/api/winsock2/nf-winsock2-connect https://source.chromium.org/chromium/chromium/src/+/main:net/socket/tcp_socket_win.cc;l=1055;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c

Chromeとかならconnectexとか使ってんのかなと思っていたが普通にconnectだった(TCP fastopenを狙いでもしない限りはただただめんどくさいだけかもしれないというのはある…) https://learn.microsoft.com/ja-jp/windows/win32/api/mswsock/nc-mswsock-lpfn_connectex

SSLConnectJob::DoLoopまで戻った後DoSSLConnectに入るところへ行く。 https://source.chromium.org/chromium/chromium/src/+/main:net/socket/ssl_connect_job.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;l=336 int SSLConnectJob::DoSSLConnect() { TRACE_EVENT0(NetTracingCategory(), “SSLConnectJob::DoSSLConnect”); DCHECK(!TimerIsRunning());

next_state_ = STATE_SSL_CONNECT_COMPLETE;

// Set the timeout to just the time allowed for the SSL handshake. ResetTimer(kSSLHandshakeTimeout);

// Get the transport’s connect start and DNS times. const LoadTimingInfo::ConnectTiming& socket_connect_timing = nested_connect_job_->connect_timing();

// Overwriting |connect_start| serves two purposes - it adjusts timing so // |connect_start| doesn’t include dns times, and it adjusts the time so // as not to include time spent waiting for an idle socket. connect_timing_.connect_start = socket_connect_timing.connect_start; connect_timing_.domain_lookup_start = socket_connect_timing.domain_lookup_start; connect_timing_.domain_lookup_end = socket_connect_timing.domain_lookup_end;

ssl_negotiation_started_ = true; connect_timing_.ssl_start = base::TimeTicks::Now();

// Save the HostResolverEndpointResult. nested_connect_job_ is destroyed // at the end of this function. endpoint_result_ = nested_connect_job_->GetHostResolverEndpointResult();

SSLConfig ssl_config = params_->ssl_config(); ssl_config.ignore_certificate_errors = *common_connect_job_params()->ignore_certificate_errors; ssl_config.network_anonymization_key = params_->network_anonymization_key();

if (ssl_client_context()->config().ech_enabled) { if (ech_retry_configs_) { ssl_config.ech_config_list = *ech_retry_configs_; } else if (endpoint_result_) { ssl_config.ech_config_list = endpoint_result_->metadata.ech_config_list; } if (!ssl_config.ech_config_list.empty()) { // Overriding the DNS lookup only works for direct connections. We // currently do not support ECH with other connection types. DCHECK_EQ(params_->GetConnectionType(), SSLSocketParams::DIRECT); } }

if (base::FeatureList::IsEnabled(features::kTLSTrustAnchorIDs) && endpoint_result_ && !endpoint_result_->metadata.trust_anchor_ids.empty() && !ssl_client_context()->config().trust_anchor_ids.empty()) { ssl_config.trust_anchor_ids = SSLConfig::SelectTrustAnchorIDs( endpoint_result_->metadata.trust_anchor_ids, ssl_client_context()->config().trust_anchor_ids); }

net_log().AddEvent(NetLogEventType::SSL_CONNECT_JOB_SSL_CONNECT, [&] { base::Value::Dict dict; dict.Set(“ech_enabled”, ssl_client_context()->config().ech_enabled); dict.Set(“ech_config_list”, NetLogBinaryValue(ssl_config.ech_config_list)); return dict; }); ssl_socket_ = client_socket_factory()->CreateSSLClientSocket( ssl_client_context(), std::move(nested_socket_), params_->host_and_port(), ssl_config); nested_connect_job_.reset(); return ssl_socket_->Connect(callback_); }

https://source.chromium.org/chromium/chromium/src/+/main:net/socket/ssl_client_socket_impl.cc;l=336;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 int SSLClientSocketImpl::Connect(CompletionOnceCallback callback) {

int SSLClientSocketImpl::Init() https://source.chromium.org/chromium/chromium/src/+/main:net/socket/ssl_client_socket_impl.cc;l=638;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 ここでSSL_newなどで初期化した後 stream_socketをBIO経由でSSL部分と接続させている。

BIO_METHODの定義がここにあり https://source.chromium.org/chromium/chromium/src/+/main:net/socket/socket_bio_adapter.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1;l=446 SocketBIOAdapter::BIOWriteWrapper https://source.chromium.org/chromium/chromium/src/+/main:net/socket/socket_bio_adapter.cc;l=401;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 int SocketBIOAdapter::BIOWrite(base::span in) https://source.chromium.org/chromium/chromium/src/+/main:net/socket/socket_bio_adapter.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1;l=215 void SocketBIOAdapter::SocketWrite() https://source.chromium.org/chromium/chromium/src/+/main:net/socket/socket_bio_adapter.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1;l=294 そして TCPClientSocket::Write https://source.chromium.org/chromium/chromium/src/+/main:net/socket/tcp_client_socket.cc;l=422;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 おそらくTcpSocketIoCompletionPortWin::Write WSASendにたどり着く https://learn.microsoft.com/ja-jp/windows/win32/api/winsock2/nf-winsock2-wsasend https://source.chromium.org/chromium/chromium/src/+/main:net/socket/tcp_socket_io_completion_port_win.cc;l=300;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

BoringSSLの関数が出てきた… SSL_set_connect_state https://source.chromium.org/chromium/chromium/src/+/main:net/socket/ssl_client_socket_impl.cc;l=355;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

SSLClientSocketImpl::DoHandshakeLoop https://source.chromium.org/chromium/chromium/src/+/main:net/socket/ssl_client_socket_impl.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1;l=1217

SSL_do_handshake https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/ssl_lib.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1;l=716

ssl_run_handshake https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/handshake.cc;l=495;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

ssl_client_handshake https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/handshake_client.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1;l=1817

do_start_connect https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/handshake_client.cc;l=343;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/encrypted_client_hello.cc;l=779;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/encrypted_client_hello.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1;l=722 なかなか興味深いことにencrypted client helloに対応するなどしている…ただし現段階ではECHによる接続確立ではないようなので setup_ech_greaseの方にフォールバックしているようだ bool ssl_encrypt_client_hello(SSL_HANDSHAKE *hs, Span enc) {

硬直化(Ossification)対策 https://datatracker.ietf.org/doc/id/draft-ietf-tls-esni-08.html#name-grease-extensions https://asnokaze.hatenablog.com/entry/2020/02/01/013058

bool ssl_add_client_hello(SSL_HANDSHAKE *hs) こちらが実際のclient_helloを追加する箇所。 https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/handshake_client.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1;l=218

ssl_write_client_hello_without_extensions https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/handshake_client.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;l=180

bool ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, CBB *out_encoded, bool *out_needs_psk_binder, ssl_client_hello_type_t type, size_t header_len) https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/extensions.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;l=3875

最終的にRecordに乗せている(もしくはpending) https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/s3_both.cc;l=105;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/s3_both.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;l=40 https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/tls_record.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;l=406

一旦tls_flushでフラッシュされる。 https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/internal.h;l=4038;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 BIO_writeでデータが書き込まれ https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/s3_both.cc;l=233;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 BIO_flushは今回はスタブである https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/s3_both.cc;l=245;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 https://source.chromium.org/chromium/chromium/src/+/main:net/socket/socket_bio_adapter.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;l=432

do_read_server_hello(SSL_HANDSHAKE *hs) https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/handshake_client.cc;l=573;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c

このメソッド自体はconsumeはしない bool tls_get_message(const SSL *ssl, SSLMessage *out) https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/s3_both.cc;l=404;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

parse_message https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/s3_both.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1;l=376

tls_read_buffer_extend_to及びBIO_read呼び出しまでのフロー ssl_run_handshake https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/handshake.cc;l=565;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1 ssl_handle_open_record https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/ssl_buffer.cc;l=205;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1 ssl_read_buffer_extended_to https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/ssl_buffer.cc;l=164;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1 tls_read_buffer_extended_to https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/ssl_buffer.cc;l=152;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

SocketBIOAdapter::BIOReadWrapper https://source.chromium.org/chromium/chromium/src/+/main:net/socket/socket_bio_adapter.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=417 SocketBIOAdapter::BIORead https://source.chromium.org/chromium/chromium/src/+/main:net/socket/socket_bio_adapter.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=105 TCPClientSocket::ReadIfReady https://source.chromium.org/chromium/chromium/src/+/main:net/socket/tcp_client_socket.cc;l=410;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1 TCPClientSocket::ReadCommon https://source.chromium.org/chromium/chromium/src/+/main:net/socket/tcp_client_socket.cc;l=179;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1 TcpSocketIoCompletionPortWin::ReadIfReady https://source.chromium.org/chromium/chromium/src/+/main:net/socket/tcp_socket_io_completion_port_win.cc;l=256;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1 TcpSocketIoCompletionPortWin::HandleReadRequest WSARecvに到達 https://learn.microsoft.com/ja-jp/windows/win32/api/winsock2/nf-winsock2-wsarecv https://source.chromium.org/chromium/chromium/src/+/main:net/socket/tcp_socket_io_completion_port_win.cc;l=509;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

do_read_server_hello state_tls13に移行 TLS1.3はTLS1.2から大幅にプロトコル変更がされているため個別のメソッドになっているものと思われる。 https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/handshake_client.cc;l=655;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c

do_tls13でtls13_client_handshake呼び出し https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/handshake_client.cc;l=822;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) https://datatracker.ietf.org/doc/rfc9383/ https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/tls13_client.cc;l=371;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

ここらへんから鍵共有をしてhandshake secretの導出をしている。 https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/tls13_client.cc;l=544;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

static enum ssl_hs_wait_t do_read_encrypted_extensions(SSL_HANDSHAKE *hs) https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/tls13_client.cc;l=593;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

ALPNのネゴシエーションはこのあたり https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/extensions.cc;l=4246;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/extensions.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1;l=4117 https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/extensions.cc;l=4175;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/extensions.cc;l=1372;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 static bool ext_alpn_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert, CBS *contents) { SSL *const ssl = hs->ssl; if (contents == NULL) { if (SSL_is_quic(ssl)) { // ALPN is required when QUIC is used. OPENSSL_PUT_ERROR(SSL, SSL_R_NO_APPLICATION_PROTOCOL); *out_alert = SSL_AD_NO_APPLICATION_PROTOCOL; return false; } return true; }

assert(!ssl->s3->initial_handshake_complete); assert(!hs->config->alpn_client_proto_list.empty());

if (hs->next_proto_neg_seen) { // NPN and ALPN may not be negotiated in the same connection. *out_alert = SSL_AD_ILLEGAL_PARAMETER; OPENSSL_PUT_ERROR(SSL, SSL_R_NEGOTIATED_BOTH_NPN_AND_ALPN); return false; }

// The extension data consists of a ProtocolNameList which must have // exactly one ProtocolName. Each of these is length-prefixed. CBS protocol_name_list, protocol_name; if (!CBS_get_u16_length_prefixed(contents, &protocol_name_list) || // CBS_len(contents) != 0 || // !CBS_get_u8_length_prefixed(&protocol_name_list, &protocol_name) || // // Empty protocol names are forbidden. CBS_len(&protocol_name) == 0 || // CBS_len(&protocol_name_list) != 0) { return false; }

if (!ssl_is_alpn_protocol_allowed(hs, protocol_name)) { OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ALPN_PROTOCOL); *out_alert = SSL_AD_ILLEGAL_PARAMETER; return false; }

if (!ssl->s3->alpn_selected.CopyFrom(protocol_name)) { *out_alert = SSL_AD_INTERNAL_ERROR; return false; }

return true; }

サーバー証明書受信及び証明書チェーンの構築 static enum ssl_hs_wait_t do_read_server_certificate(SSL_HANDSHAKE *hs) https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/tls13_client.cc;l=760;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/tls13_both.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;l=102

証明書チェーンの検証 static enum ssl_hs_wait_t do_read_server_certificate_verify(SSL_HANDSHAKE *hs) enum ssl_verify_result_t ssl_verify_peer_cert(SSL_HANDSHAKE *hs) https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/handshake.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;l=229 https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/tls13_client.cc;l=770;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1 公開鍵署名の検証 bool tls13_process_certificate_verify(SSL_HANDSHAKE *hs, const SSLMessage &msg) https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/tls13_both.cc;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1;l=334

ServerのFinishedメッセージのトランスクリプトハッシュの検証とアプリケーション鍵の導出 static enum ssl_hs_wait_t do_read_server_finished(SSL_HANDSHAKE *hs) https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/tls13_client.cc;l=787;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

クライアントのEncrypted Extension送信 static enum ssl_hs_wait_t do_send_client_encrypted_extensions( SSL_HANDSHAKE *hs) https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/tls13_client.cc;l=864;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

クライアントのFinishedメッセージ生成とアプリケーション鍵の設定 static enum ssl_hs_wait_t do_complete_second_flight(SSL_HANDSHAKE *hs) https://source.chromium.org/chromium/chromium/src/+/main:third_party/boringssl/src/ssl/tls13_client.cc;l=1009;drc=d9942f1cda698ea5524c9d8b7ba9f48f508bf41c;bpv=1;bpt=1

ハンドシェイクが完了する

HttpStreamFactory::Job::DoInitConnectionが終わったところまで戻りHttpStreamFactory::Job::DoInitConnectionCompleteへ入る。

HttpStreamFactory::Job::DoInitConnectionComplete(int result) ここでプロトコルが決定される。今回はHTTP/2となるはずである。 https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_stream_factory_job.cc;l=954;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf // Determine the protocol (HTTP/1.1, HTTP/2, or HTTP/3). This covers both the // origin and some proxy cases. First, if the URL is HTTPS (or WSS), we may // negotiate HTTP/2 or HTTP/3 with the origin. Second, non-tunneled requests // (i.e. HTTP URLs) through an HTTPS or QUIC proxy work by sending the request // to the proxy directly. In that case, this logic also handles the proxy’s // negotiated protocol. HTTPS requests are always tunneled, so at most one of // these applies. // // Tunneled requests may also negotiate ALPN at the proxy, but // HttpProxyConnectJob handles ALPN. The resulting StreamSocket will not // report an ALPN protocol. if (result == OK) { if (using_quic_) { // TODO(davidben): Record these values consistently between QUIC and TCP // below. In the QUIC case, we only record it for origin connections. In // the TCP case, we also record it for non-tunneled, proxied requests. if (using_ssl_) { negotiated_protocol_ = NextProto::kProtoQUIC; } } else if (connection_->socket()->GetNegotiatedProtocol() != NextProto::kProtoUnknown) { // Only connections that use TLS (either to the origin or via a GET to a // secure proxy) can negotiate ALPN. bool get_to_secure_proxy = IsGetToProxy(proxy_info_.proxy_chain(), origin_url_) && proxy_info_.proxy_chain().Last().is_secure_http_like(); DCHECK(using_ssl_ || get_to_secure_proxy); negotiated_protocol_ = connection_->socket()->GetNegotiatedProtocol(); net_log_.AddEvent(NetLogEventType::HTTP_STREAM_REQUEST_PROTO, [&] { return NetLogHttpStreamProtoParams(negotiated_protocol_); }); if (using_spdy()) { if (is_websocket_) { // WebSocket is not supported over a fresh HTTP/2 connection. This // should not be reachable. For the origin, we do not request HTTP/2 // on fresh WebSockets connections, because not all HTTP/2 servers // implement RFC 8441. For proxies, WebSockets are always tunneled. // // TODO(davidben): This isn’t a CHECK() because, previously, it was // reachable in https://crbug.com/828865 . However, if reachable, it // means a bug in the socket pools. The socket pools have since been // cleaned up, so this may no longer be reachable. Restore the CHECK // and see if this is still needed. return ERR_NOT_IMPLEMENTED; } } } }

SSLClientSocketImpl::GetNegotiatedProtocol https://source.chromium.org/chromium/chromium/src/+/main:net/socket/ssl_client_socket_impl.cc;l=462;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1 SSLClientSocketImpl::DoHandshakeComplete内でALPNからプロトコルが決定される。 https://source.chromium.org/chromium/chromium/src/+/main:net/socket/ssl_client_socket_impl.cc;l=927;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1 NextProto NextProtoFromString(std::string_view proto_string) https://source.chromium.org/chromium/chromium/src/+/main:net/socket/next_proto.cc;l=11;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

HttpStreamFactory::Job::DoCreateStream() https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_stream_factory_job.cc;l=1094;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

紛らわしいことにChrome内部ではHTTP/2は相変わらずspdyという名前で扱われているようだ… https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_stream_factory_job.cc;l=354;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1 bool HttpStreamFactory::Job::using_spdy() const { return negotiated_protocol_ == NextProto::kProtoHTTP2; }

SpdySessionPool::CreateAvailableSessionFromSocketHandle https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_session_pool.cc;l=151;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf SpdySession::InitializeWithSocketHandle https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_session.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=883 void SpdySession::InitializeInternal(SpdySessionPool* pool) https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_session.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=1601 void SpdySession::SendInitialData() ここでConnection PrefaceとかSETTINGS Frameの送信 https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_session.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=2188 Connection Preface (PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n) https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/core/spdy_protocol.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=25 SpdySession::EnqueueSessionWrite https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_session.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=2475 SpdySession::EnqueueWrite https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_session.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=2517 SpdySession::MaybePostWriteLoop 非同期化 https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_session.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=2024 SpdySession::PumpWriteLoop(WriteState expected_write_state, int result) https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_session.cc;l=2072;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1 int SpdySession::DoWriteLoop(WriteState expected_write_state, int result) https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_session.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=2035 SpdySession::DoWrite() そしてここからSocketの書き込み要求(SSL Socket) https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_session.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=2072

SpdyWriteQueue::Dequeue 優先度付きqueueが使われている。 https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_write_queue.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=83

HttpStreamFactory::Job::SetSpdyHttpStreamOrBidirectionalStreamImpl https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_stream_factory_job.cc;l=1057;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

SpdyHttpStreamを設定する。 https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_stream_factory_job.cc;l=1089;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf stream_ = std::make_unique(session, net_log_.source(), std::move(dns_aliases));

HttpNetworkTransaction::DoLoopまで戻ってProxy関係のやつを飛ばしてHttpNetworkTransaction::DoBuildRequestから入る。 https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_network_transaction.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=1489

HttpNetworkTransaction::BuildRequestHeaders https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_network_transaction.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=1387

HttpNetworkTransaction::DoSendRequest() https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_network_transaction.cc;l=1526;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

int SpdyHttpStream::SendRequest(const HttpRequestHeaders& request_headers, HttpResponseInfo* response, CompletionOnceCallback callback) { https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_http_stream.cc;l=205;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

ここでHostとかConnectionとかHTTP/1.1特有のヘッダをフィルタリングし 小文字か処理とか:methodとか:authorityとか:pathとか:schemeとかのついかをしたりしている void CreateSpdyHeadersFromHttpRequest(const HttpRequestInfo& info, std::optional priority, const HttpRequestHeaders& request_headers, quiche::HttpHeaderBlock* headers) https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_http_utils.cc;l=198;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

AddUniqueSpdyHeader経由でquiche::HttpHeaderBlock::InsertResult HttpHeaderBlock::insert

https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/common/http/http_header_block.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=248

https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_stream.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=619 SpdyStream::SendRequestHeaders(quiche::HttpHeaderBlock request_headers, SpdySendStatus send_status)

SpdyStream::HeadersBufferProducer::ProduceBuffer https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_stream.cc;l=83;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1 SpdyStream::ProduceHeadersFrame https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_stream.cc;l=137;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1 SpdySession::CreateHeaders https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_session.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=1061 std::unique_ptrspdy::SpdySerializedFrame SpdySession::CreateHeaders( spdy::SpdyStreamId stream_id, RequestPriority priority, spdy::SpdyControlFlags flags, quiche::HttpHeaderBlock block, NetLogSource source_dependency)

BufferedSpdyFramer::SerializeFrame https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/buffered_spdy_framer.h;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=245

SpdyFramer::SerializeFrame https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/core/spdy_framer.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=953

void SpdyHeadersIR::Visit(SpdyFrameVisitor* visitor) const https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/core/spdy_protocol.cc;l=444;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

FrameSerializationVisitor::VisitHeaders void VisitHeaders(const SpdyHeadersIR& headers) override https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/core/spdy_framer.cc;l=826;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

実際のシリアライズ処理 bool SpdyFramer::SerializeHeaders(const SpdyHeadersIR& headers, ZeroCopyOutputBuffer* output) https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/core/spdy_framer.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=1088

SpdyFramer::SerializeHeadersBuilderHelper https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/core/spdy_framer.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=543

https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/hpack/hpack_encoder.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=96

再びHttpNetworkTransaction::DoLoopに戻った後HttpNetworkTransaction::DoReadHeaders https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_network_transaction.cc;l=1549;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf

SpdyHttpStream::ReadResponseHeaders 基本的に完了通知のみである。 https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_http_stream.cc;l=89;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1 int SpdyHttpStream::ReadResponseHeaders(CompletionOnceCallback callback) { CHECK(!callback.is_null()); if (stream_closed_) return closed_stream_status_;

CHECK(stream_);

// Check if we already have the response headers. If so, return synchronously. if (response_headers_complete_) { CHECK(!stream_->IsIdle()); return OK; }

// Still waiting for the response, return IO_PENDING. CHECK(response_callback_.is_null()); response_callback_ = std::move(callback); return ERR_IO_PENDING; }

SpdySession::PumpReadLoop(SpdySession::InitInternal内で非同期タスクとして生成) https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_session.cc;l=1883;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1 SpdySession::DoReadLoop https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_session.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=1891

SpdySession::DoRead https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_session.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=1942 SpdySession::DoReadComplete https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_session.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=1942 BufferedSpdyFramer::ProcessInput https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/buffered_spdy_framer.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=225

size_t Http2DecoderAdapter::ProcessInput(const char* data, size_t len) { https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/core/http2_frame_decoder_adapter.cc;l=267;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

DecodeStatus Http2FrameDecoder::DecodeFrame(DecodeBuffer* db) { https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/decoder/http2_frame_decoder.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=52

Http2FrameDecoder::StartDecodingPayload(DecodeBuffer* db) https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/decoder/http2_frame_decoder.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=87

HeadersPayloadDecoder::StartDecodingPayload https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/decoder/payload_decoders/headers_payload_decoder.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=43

ステートマシンベースでどこでも中断できるようになっている… DecodeStatus HeadersPayloadDecoder::ResumeDecodingPayload( FrameDecoderState* state, DecodeBuffer* db) https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/decoder/payload_decoders/headers_payload_decoder.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=100

HPACKのデータを受信したらこれが呼ばれる。 void Http2DecoderAdapter::OnHpackFragment(const char* data, size_t len) https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/core/http2_frame_decoder_adapter.cc;l=474;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

HpackDecoderAdapter::HandleControlFrameHeadersData https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/hpack/hpack_decoder_adapter.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=47

bool HpackDecoder::DecodeFragment(DecodeBuffer* db) { https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder.cc;l=55;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

DecodeStatus HpackBlockDecoder::Decode(DecodeBuffer* db) https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_decoder.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=17

HpackEntryDecoder::Start(DecodeBuffer* db, HpackEntryDecoderListener* listener) https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder.cc;l=65;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

ものすごく長いswitch文による最適化が行われている… DecodeStatus HpackEntryTypeDecoder::Start(DecodeBuffer* db) https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_type_decoder.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=35

HpackEntryDecoder::ResumeでListenerにDispatch https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder.cc;l=123;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

HpackEntryDecoder::DispatchOnType https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder.cc;l=223;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

HpackDecoderState::OnIndexedHeader(size_t index) https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_state.cc;l=81;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

void HpackDecoderAdapter::ListenerAdapter::OnHeader(absl::string_view name, absl::string_view value) https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/http2/hpack/hpack_decoder_adapter.cc;l=131;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

void HeaderCoalescer::OnHeader(std::string_view key, std::string_view value) https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/header_coalescer.cc;l=50;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

bool HeaderCoalescer::AddHeader(std::string_view key, std::string_view value) https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/header_coalescer.cc;l=64;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

void HttpHeaderBlock::AppendValueOrAddHeader(const absl::string_view key, const absl::string_view value) https://source.chromium.org/chromium/chromium/src/+/main:net/third_party/quiche/src/quiche/common/http/http_header_block.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=287

https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/buffered_spdy_framer.cc;l=109;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1 spdy::SpdyHeadersHandlerInterface* BufferedSpdyFramer::OnHeaderFrameStart( spdy::SpdyStreamId stream_id)

void BufferedSpdyFramer::OnHeaderFrameEnd(spdy::SpdyStreamId stream_id) https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/buffered_spdy_framer.cc;l=113;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

void SpdySession::OnHeaders(spdy::SpdyStreamId stream_id, bool has_priority, int weight, spdy::SpdyStreamId parent_stream_id, bool exclusive, bool fin, quiche::HttpHeaderBlock headers, base::TimeTicks recv_first_byte_time) https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_session.cc;l=3035;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

Status Codeのパースなどしている。 void SpdyStream::OnHeadersReceived( const quiche::HttpHeaderBlock& response_headers, base::Time response_time, base::TimeTicks recv_first_byte_time) { https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_stream.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=329

void SpdyStream::SaveResponseHeaders( const quiche::HttpHeaderBlock& response_headers, int status) https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_stream.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=802

void SpdyHttpStream::OnHeadersReceived( const quiche::HttpHeaderBlock& response_headers) https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_http_stream.cc;l=294;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

最終的にここでSpdyHttpStream::ReadResponseHeadersにて設定された完了コールバックが呼ばれHttpNetworkTransaction::OnIOComplete経由でHttpNetworkTransaction::DoLoopに戻って処理が進行する https://source.chromium.org/chromium/chromium/src/+/main:net/spdy/spdy_http_stream.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=564 void SpdyHttpStream::DoResponseCallback(int rv) { CHECK_NE(rv, ERR_IO_PENDING); CHECK(!response_callback_.is_null());

// Since Run may result in being called back, reset response_callback_ in // advance. std::move(response_callback_).Run(rv); }

HttpNetworkTransaction::DoLoopに戻った後 HttpNetworkTransaction::DoReadBody https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_network_transaction.cc;l=1549;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

HttpNetworkTransaction::DoReadBodyComplete https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_network_transaction.cc;l=1764;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1

最終的にHttpNetworkTransaction::DoCallbackで表に戻される。 https://source.chromium.org/chromium/chromium/src/+/main:net/http/http_network_transaction.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;bpv=1;bpt=1;l=965

そしてURLRequestHttpJob::OnStartCompletedが呼ばれる https://source.chromium.org/chromium/chromium/src/+/main:net/url_request/url_request_http_job.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=1249

URLRequestHttpJob::SaveCookiesAndNotifyHeadersComplete(int result) https://source.chromium.org/chromium/chromium/src/+/main:net/url_request/url_request_http_job.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=1008

URLRequestHttpJob::NotifyHeadersComplete() https://source.chromium.org/chromium/chromium/src/+/main:net/url_request/url_request_http_job.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=600

URLRequestJob::NotifyHeadersComplete() https://source.chromium.org/chromium/chromium/src/+/main:net/url_request/url_request_job.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=416

URLRequestJob::NotifyFinalHeadersReceived() https://source.chromium.org/chromium/chromium/src/+/main:net/url_request/url_request_job.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=484

void URLRequest::NotifyResponseStarted(int net_error) https://source.chromium.org/chromium/chromium/src/+/main:net/url_request/url_request.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=917

void URLRequest::NotifyRequestCompleted() https://source.chromium.org/chromium/chromium/src/+/main:net/url_request/url_request.cc;drc=f0cb0ae3f9b142a11fdc5efc77e27a5d53b6b6cf;l=1274

IPC経由でやっているっぽいことを見つけるまで
#

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/storage_partition_impl.cc;l=1585;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133?q=StoragePartitionImpl::CreateURLLoaderFactoryForBrowserProcessInternal&ss=chromium%2Fchromium%2Fsrc

network::mojom::NetworkContext* StoragePartitionImpl::GetNetworkContext() {
  DCHECK(initialized_);
  if (!network_context_owner_->network_context.is_bound()) {
    InitNetworkContext();
  }
  return network_context_owner_->network_context.get();
}

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/storage_partition_impl.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;l=3571?q=StoragePartitionImpl::CreateURLLoaderFactoryForBrowserProcessInternal&ss=chromium%2Fchromium%2Fsrc

void StoragePartitionImpl::InitNetworkContext() {
  network::mojom::NetworkContextParamsPtr context_params =
      network::mojom::NetworkContextParams::New();
  cert_verifier::mojom::CertVerifierCreationParamsPtr
      cert_verifier_creation_params =
          cert_verifier::mojom::CertVerifierCreationParams::New();
  GetContentClient()->browser()->ConfigureNetworkContextParams(
      browser_context_, is_in_memory(), relative_partition_path_,
      context_params.get(), cert_verifier_creation_params.get());
  // Should be initialized with existing per-profile CORS access lists.
  DCHECK(context_params->cors_origin_access_list.empty())
      << "NetworkContextParams::cors_origin_access_list should be populated "
         "via SharedCorsOriginAccessList";
  context_params->cors_origin_access_list =
      browser_context_->GetSharedCorsOriginAccessList()
          ->GetOriginAccessList()
          .CreateCorsOriginAccessPatternsList();
  devtools_instrumentation::ApplyNetworkContextParamsOverrides(
      browser_context_, context_params.get());
  DCHECK(!context_params->cert_verifier_params)
      << "`cert_verifier_params` should not be set in the "
         "NetworkContextParams, as they will be replaced with a new pipe to "
         "the CertVerifierService.";

  cert_verifier_service_updater_.reset();
  context_params->cert_verifier_params = GetCertVerifierParamsWithUpdater(
      std::move(cert_verifier_creation_params),
      cert_verifier_service_updater_.BindNewPipeAndPassReceiver());

  // This mechanisms should be used only for legacy internal headers. You can
  // find a recommended alternative approach on URLRequest::cors_exempt_headers
  // at services/network/public/mojom/url_loader.mojom.
  context_params->cors_exempt_header_list.push_back(blink::kPurposeHeaderName);
  context_params->cors_exempt_header_list.push_back(
      GetCorsExemptRequestedWithHeaderName());
  variations::UpdateCorsExemptHeaderForVariations(context_params.get());

  cors_exempt_header_list_ = context_params->cors_exempt_header_list;

  if (base::FeatureList::IsEnabled(
          network::features::kCompressionDictionaryTransportBackend) &&
      GetContentClient()->browser()->AllowCompressionDictionaryTransport(
          browser_context_)) {
    context_params->shared_dictionary_enabled = true;
    if (!is_in_memory()) {
      // Some callers may already initialize NetworkContextFilePaths, and we
      // don't want to overwrite them.
      if (!context_params->file_paths) {
        context_params->file_paths =
            network::mojom::NetworkContextFilePaths::New();
      }
      context_params->file_paths->shared_dictionary_directory =
          partition_path_.Append(FILE_PATH_LITERAL("Shared Dictionary"));
    }
    if (context_params->shared_dictionary_cache_max_size == 0u) {
      CalculateAndSetSharedDictionaryCacheMaxSize(
          GetWeakPtr(), is_in_memory() ? base::FilePath() : partition_path_);
    }
  }

  if (cookie_deprecation_label_manager_) {
    context_params->cookie_deprecation_label =
        cookie_deprecation_label_manager_->GetValue().value_or("");
  }

  network_context_owner_->network_context.reset();
  CreateNetworkContextInNetworkService(
      network_context_owner_->network_context.BindNewPipeAndPassReceiver(),
      std::move(context_params));
  DCHECK(network_context_owner_->network_context);

  // Restore the saved network revocation nonces. This allows fenced frames'
  // untrusted network access states to be persisted in case of a
  // `NetworkService` crash.
  std::vector<base::UnguessableToken> nonces(
      std::begin(network_revocation_nonces_),
      std::end(network_revocation_nonces_));
  network_context_owner_->network_context->RevokeNetworkForNonces(
      nonces, base::NullCallback());

  network_context_client_receiver_.reset();
  network_context_owner_->network_context->SetClient(
      network_context_client_receiver_.BindNewPipeAndPassRemote());
  network_context_owner_->network_context.set_disconnect_handler(base::BindOnce(
      &StoragePartitionImpl::InitNetworkContext, weak_factory_.GetWeakPtr()));
}

https://source.chromium.org/chromium/chromium/src/+/main:services/network/network_context.cc;l=1003

void NetworkContext::SetClient(
    mojo::PendingRemote<mojom::NetworkContextClient> client) {
  client_.reset();
  client_.Bind(std::move(client));
}

global関数CreateNetworkContextInNetworkServiceにて https://source.chromium.org/chromium/chromium/src/+/main:content/browser/network_service_instance_impl.cc;l=916?q=CreateNetworkContextInNetworkService&ss=chromium%2Fchromium%2Fsrc

void CreateNetworkContextInNetworkService(
    mojo::PendingReceiver<network::mojom::NetworkContext> context,
    network::mojom::NetworkContextParamsPtr params) {
  TRACE_EVENT0("loading", "CreateNetworkContextInNetworkService");
  DCHECK(!BrowserThread::IsThreadInitialized(BrowserThread::UI) ||
         BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (params->http_cache_enabled && params->file_paths &&
      params->file_paths->http_cache_directory) {
    params->file_paths->http_cache_directory =
        params->file_paths->http_cache_directory->path().Append(
            kCacheDataDirectoryName);
  }

  const bool has_valid_http_cache_path =
      params->http_cache_enabled && params->file_paths &&
      params->file_paths->http_cache_directory &&
      !params->file_paths->http_cache_directory->path().empty();
  const bool brokering_is_enabled =
      IsOutOfProcessNetworkService() &&
      base::FeatureList::IsEnabled(
          features::kBrokerFileOperationsOnDiskCacheInNetworkService);
  if (has_valid_http_cache_path && brokering_is_enabled) {
    mojo::MakeSelfOwnedReceiver(
        std::make_unique<HttpCacheBackendFileOperationsFactory>(
            params->file_paths->http_cache_directory->path()),
        params->http_cache_file_operations_factory
            .InitWithNewPipeAndPassReceiver());
  }

#if BUILDFLAG(IS_ANDROID)
  // On Android, if a cookie_manager pending receiver was passed then migration
  // should not be attempted as the cookie file is already being accessed by the
  // browser instance.
  if (params->cookie_manager) {
    if (params->file_paths) {
      // No migration should ever be attempted under this configuration.
      DCHECK(!params->file_paths->unsandboxed_data_path);
    }
    CreateNetworkContextInternal(
        std::move(context), std::move(params),
        SandboxGrantResult::kDidNotAttemptToGrantSandboxAccess);
    return;
  }

  // Note: This logic is duplicated from MaybeGrantAccessToDataPath to this fast
  // path. This should be kept in sync if there are any changes to the logic.
  SandboxGrantResult grant_result = SandboxGrantResult::kNoMigrationRequested;
  if (!params->file_paths) {
    // No file paths (e.g. in-memory context) so nothing to do.
    grant_result = SandboxGrantResult::kDidNotAttemptToGrantSandboxAccess;
  } else {
    // If no `unsandboxed_data_path` is supplied, it means this is network
    // context has been created by Android Webview, which does not understand
    // the concept of `unsandboxed_data_path`. In this case, `data_directory`
    // should always be used, if present.
    if (!params->file_paths->unsandboxed_data_path)
      grant_result = SandboxGrantResult::kDidNotAttemptToGrantSandboxAccess;
  }
  // Create network context immediately without thread hops.
  CreateNetworkContextInternal(std::move(context), std::move(params),
                               grant_result);
#else
  // Restrict disk access to a certain path (on another thread) and continue
  // with network context creation.
  GrantSandboxAccessOnThreadPool(
      std::move(params),
      base::BindOnce(&CreateNetworkContextInternal, std::move(context)));
#endif  // BUILDFLAG(IS_ANDROID)
}

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/network_service_instance_impl.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;l=235?q=CreateNetworkContextInNetworkService&ss=chromium%2Fchromium%2Fsrc

void CreateNetworkContextInternal(
    mojo::PendingReceiver<network::mojom::NetworkContext> context,
    network::mojom::NetworkContextParamsPtr params,
    SandboxGrantResult grant_access_result) {
  TRACE_EVENT0("loading", "CreateNetworkContextInternal");
  // These two histograms are logged from elsewhere, so don't log them twice.
  DCHECK(grant_access_result !=
         SandboxGrantResult::kFailedToCreateCacheDirectory);
  DCHECK(grant_access_result !=
         SandboxGrantResult::kFailedToGrantSandboxAccessToCache);
  base::UmaHistogramEnumeration("NetworkService.GrantSandboxResult",
                                grant_access_result);

  if (grant_access_result != SandboxGrantResult::kSuccess &&
      grant_access_result !=
          SandboxGrantResult::kDidNotAttemptToGrantSandboxAccess &&
      grant_access_result != SandboxGrantResult::kNoMigrationRequested &&
      grant_access_result != SandboxGrantResult::kMigrationAlreadySucceeded) {
    PLOG(ERROR) << "Encountered error while migrating network context data or "
                   "granting sandbox access for "
                << (params->file_paths
                        ? params->file_paths->data_directory.path()
                        : base::FilePath())
                << ". Result: " << static_cast<int>(grant_access_result);
  }

  if (!IsSafeToUseDataPath(grant_access_result)) {
    // Unsafe to use new `data_directory`. This means that a migration was
    // attempted, and `unsandboxed_data_path` contains the still-valid set of
    // data. Swap the parameters to instruct the network service to use this
    // path for the network context. This of course will mean that if the
    // network service is running sandboxed then this data might not be
    // accessible, but does provide a pathway to user recovery, as the sandbox
    // can just be disabled in this case.
    DCHECK(params->file_paths->unsandboxed_data_path.has_value());
    params->file_paths->data_directory =
        *params->file_paths->unsandboxed_data_path;
  }

  if (network::TransferableDirectory::IsOpenForTransferRequired()) {
    if (params->file_paths) {
      if (params->file_paths->http_cache_directory) {
        params->file_paths->http_cache_directory->OpenForTransfer();
      }
      if (params->file_paths->shared_dictionary_directory) {
        params->file_paths->shared_dictionary_directory->OpenForTransfer();
      }
      params->file_paths->data_directory.OpenForTransfer();
    }
  }

  // This might recreate g_client if the network service needed to be restarted.
  auto* network_service = GetNetworkService();

#if BUILDFLAG(IS_WIN)
  // If the browser has started shutting down, it is possible that either a)
  // `g_client` was never created if shutdown started before the network service
  // was created, or b) the network service might have crashed meaning
  // `g_client` is the client for the already-crashed Network Service, and a new
  // network service never started. It's not safe to bind the socket broker in
  // either of these cases so skip the binding since the browser is shutting
  // down anyway.
  if (!GetContentClient()->browser()->IsShuttingDown() &&
      GetContentClient()->browser()->ShouldSandboxNetworkService() &&
      !params->socket_brokers) {
    params->socket_brokers = network::mojom::SocketBrokerRemotes::New();
    params->socket_brokers->client = g_client->BindSocketBroker();
    params->socket_brokers->server = g_client->BindSocketBroker();
  }
#endif  // BUILDFLAG(IS_WIN)

  network_service->CreateNetworkContext(std::move(context), std::move(params));
}

https://source.chromium.org/chromium/chromium/src/+/main:services/network/network_service.cc;l=710?q=CreateNetworkContext&ss=chromium%2Fchromium%2Fsrc&start=41

void NetworkService::CreateNetworkContext(
    mojo::PendingReceiver<mojom::NetworkContext> receiver,
    mojom::NetworkContextParamsPtr params) {
  owned_network_contexts_.emplace(std::make_unique<NetworkContext>(
      this, std::move(receiver), std::move(params),
      base::BindOnce(&NetworkService::OnNetworkContextConnectionClosed,
                     base::Unretained(this))));
}

https://source.chromium.org/chromium/chromium/src/+/main:services/network/network_context.h;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=152

// A NetworkContext creates and manages access to a URLRequestContext.
//
// When the network service is enabled, NetworkContexts are created through
// NetworkService's mojo interface and are owned jointly by the NetworkService
// and the mojo::Remote<NetworkContext> used to talk to them, and the
// NetworkContext is destroyed when either one is torn down.
#if BUILDFLAG(ENABLE_REPORTING)
class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext
    : public mojom::NetworkContext,
      public net::ReportingCacheObserver {
#else
class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext
    : public mojom::NetworkContext {
#endif  // BUILDFLAG(ENABLE_REPORTING)

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/network_service_instance_impl.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;l=572?q=CreateNetworkContextInNetworkService&ss=chromium%2Fchromium%2Fsrc

network::mojom::NetworkService* GetNetworkService() {
  if (!g_network_service_remote)
    g_network_service_remote = new mojo::Remote<network::mojom::NetworkService>;
  if (!g_network_service_remote->is_bound() ||
      !g_network_service_remote->is_connected()) {
    bool service_was_bound = g_network_service_remote->is_bound();
    g_network_service_remote->reset();
    if (GetContentClient()->browser()->IsShuttingDown()) {
      // This happens at system shutdown, since in other scenarios the network
      // process would only be torn down once the message loop stopped running.
      // We don't want to start the network service again so just create message
      // pipe that's not bound to stop consumers from requesting creation of the
      // service.
      auto receiver = g_network_service_remote->BindNewPipeAndPassReceiver();
      auto leaked_pipe = receiver.PassPipe().release();
    } else {
      if (!g_force_create_network_service_directly) {
        mojo::PendingReceiver<network::mojom::NetworkService> receiver =
            g_network_service_remote->BindNewPipeAndPassReceiver();
        g_network_service_remote->set_disconnect_handler(
            base::BindOnce(&OnNetworkServiceProcessGone, /*crashed=*/true));
        if (IsInProcessNetworkService()) {
          CreateInProcessNetworkService(std::move(receiver));
        } else {
          if (service_was_bound)
            LOG(ERROR) << "Network service crashed, restarting service.";
          ServiceProcessHost::Launch(std::move(receiver),
                                     ServiceProcessHost::Options()
                                         .WithDisplayName(u"Network Service")
                                         .Pass());
        }
      } else {
        DCHECK(IsInProcessNetworkService())
            << "If the network service is created directly, the test must not "
               "request an out of process network service.";
        // This should only be reached in unit tests.
        if (BrowserThread::CurrentlyOn(BrowserThread::IO)) {
          CreateNetworkServiceOnIOForTesting(
              g_network_service_remote->BindNewPipeAndPassReceiver(),
              /*completion_event=*/nullptr);
        } else {
          base::WaitableEvent event;
          GetIOThreadTaskRunner({})->PostTask(
              FROM_HERE,
              base::BindOnce(
                  CreateNetworkServiceOnIOForTesting,
                  g_network_service_remote->BindNewPipeAndPassReceiver(),
                  base::Unretained(&event)));
          event.Wait();
        }
      }

      delete g_client;  // In case we're recreating the network service.
      g_client = new NetworkServiceClient();

      (*g_network_service_remote)->SetParams(CreateNetworkServiceParams());
      g_client->OnNetworkServiceInitialized(g_network_service_remote->get());

      g_network_service_is_responding = false;
      g_network_service_remote->QueryVersion(base::BindOnce(
          [](uint32_t) { g_network_service_is_responding = true; }));

      const base::CommandLine* command_line =
          base::CommandLine::ForCurrentProcess();
      if (command_line->HasSwitch(network::switches::kLogNetLog)) {
        base::FilePath log_path =
            command_line->GetSwitchValuePath(network::switches::kLogNetLog);
        if (log_path.empty()) {
          log_path = GetContentClient()->browser()->GetNetLogDefaultDirectory();
          if (!log_path.empty())
            log_path = log_path.Append(FILE_PATH_LITERAL("netlog.json"));
        }

        base::File file = NetworkServiceInstancePrivate::BlockingOpenFile(
            log_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
        if (!file.IsValid()) {
          LOG(ERROR) << "Failed opening NetLog: " << log_path.value();
        } else {
          (*g_network_service_remote)
              ->StartNetLog(
                  std::move(file),
                  GetNetLogMaximumFileSizeFromCommandLine(*command_line),
                  GetNetCaptureModeFromCommandLine(*command_line),
                  GetContentClient()->browser()->GetNetLogConstants(),
                  GetNetLogDurationFromCommandLine(*command_line));
        }
      }

      base::FilePath ssl_key_log_path;
      if (command_line->HasSwitch(network::switches::kSSLKeyLogFile)) {
        UMA_HISTOGRAM_ENUMERATION(kSSLKeyLogFileHistogram,
                                  SSLKeyLogFileAction::kSwitchFound);
        ssl_key_log_path =
            command_line->GetSwitchValuePath(network::switches::kSSLKeyLogFile);
        LOG_IF(WARNING, ssl_key_log_path.empty())
            << "ssl-key-log-file argument missing";
      } else {
        std::unique_ptr<base::Environment> env(base::Environment::Create());
        std::optional<std::string> env_str = env->GetVar("SSLKEYLOGFILE");
        if (env_str.has_value()) {
          UMA_HISTOGRAM_ENUMERATION(kSSLKeyLogFileHistogram,
                                    SSLKeyLogFileAction::kEnvVarFound);
#if BUILDFLAG(IS_WIN)
          // base::Environment returns environment variables in UTF-8 on
          // Windows.
          ssl_key_log_path = base::FilePath(base::UTF8ToWide(*env_str));
#else
          ssl_key_log_path = base::FilePath(*env_str);
#endif
        }
      }

      if (!ssl_key_log_path.empty()) {
        base::File file = NetworkServiceInstancePrivate::BlockingOpenFile(
            ssl_key_log_path,
            base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND);
        if (!file.IsValid()) {
          LOG(ERROR) << "Failed opening SSL key log file: "
                     << ssl_key_log_path.value();
        } else {
          UMA_HISTOGRAM_ENUMERATION(kSSLKeyLogFileHistogram,
                                    SSLKeyLogFileAction::kLogFileEnabled);
          (*g_network_service_remote)->SetSSLKeyLogFile(std::move(file));
        }
      }

      if (FirstPartySetsHandlerImpl::GetInstance()->IsEnabled()) {
        if (std::optional<net::GlobalFirstPartySets> sets =
                FirstPartySetsHandlerImpl::GetInstance()->GetSets(
                    base::BindOnce([](net::GlobalFirstPartySets sets) {
                      GetNetworkService()->SetFirstPartySets(std::move(sets));
                    }));
            sets.has_value()) {
          g_network_service_remote->get()->SetFirstPartySets(
              std::move(sets.value()));
        }
      }

      GetContentClient()->browser()->OnNetworkServiceCreated(
          g_network_service_remote->get());
    }
  }
  return g_network_service_remote->get();
}

おそらく以下でNetwork Serviceの実行が開始される

   ServiceProcessHost::Launch(std::move(receiver),
                                     ServiceProcessHost::Options()
                                         .WithDisplayName(u"Network Service")
                                         .Pass());

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/loader/reconnectable_url_loader_factory.cc;l=293;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1

ReconnectableURLLoaderFactoryForIOThreadWrapper::
    ReconnectableURLLoaderFactoryForIOThreadWrapper(
        CreateCallback create_url_loader_factory_callback)
    : factory_(base::MakeRefCounted<ReconnectableURLLoaderFactory>(
          create_url_loader_factory_callback)),
      factory_for_io_thread_(
          base::MakeRefCounted<ReconnectableURLLoaderFactoryForIOThread>(
              create_url_loader_factory_callback)) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
}

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/loader/reconnectable_url_loader_factory.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;l=177

void ReconnectableURLLoaderFactoryForIOThread::Initialize() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  // Create a mojo::PendingRemote<URLLoaderFactory> synchronously and push it to
  // the IO thread. If the pipe errors out later due to a network service crash,
  // the pipe is created on the IO thread, and the request send back to the UI
  // thread.
  // TODO(mmenke):  Is one less thread hop on startup worth the extra complexity
  // of two different pipe creation paths?
  mojo::PendingRemote<network::mojom::URLLoaderFactory> network_factory;
  HandleNetworkFactoryRequestOnUIThread(
      network_factory.InitWithNewPipeAndPassReceiver());

  GetIOThreadTaskRunner({})->PostTask(
      FROM_HERE,
      base::BindOnce(
          &ReconnectableURLLoaderFactoryForIOThread::InitializeOnIOThread, this,
          std::move(network_factory)));
}

IOスレッド側 https://source.chromium.org/chromium/chromium/src/+/main:content/browser/loader/reconnectable_url_loader_factory.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;l=251

void ReconnectableURLLoaderFactoryForIOThread::InitializeOnIOThread(
    mojo::PendingRemote<network::mojom::URLLoaderFactory> network_factory) {
  ReinitializeOnIOThread(mojo::Remote<network::mojom::URLLoaderFactory>(
      std::move(network_factory)));
}

void ReconnectableURLLoaderFactoryForIOThread::ReinitializeOnIOThread(
    mojo::Remote<network::mojom::URLLoaderFactory> network_factory) {
  DCHECK_CURRENTLY_ON(BrowserThread::IO);
  DCHECK(network_factory.is_bound());
  // Set a disconnect handler so that connection errors on the pipes are
  // noticed, but the class doesn't actually do anything when the error is
  // observed - instead, a new pipe is created in GetURLLoaderFactory() as
  // needed. This is to avoid incrementing the reference count of |this| in the
  // callback, as that could result in increasing the reference count from 0 to
  // 1 while there's a pending task to delete |this|. See
  // https://crbug.com/870942 for more details.
  network_factory.set_disconnect_handler(base::DoNothing());
  url_loader_factory_ = std::move(network_factory);
}

これは先程のReconnectableURLLoaderFactoryForIOThread::InitializeにてUIスレッド側から呼ばれていたやつ

void ReconnectableURLLoaderFactoryForIOThread::
    HandleNetworkFactoryRequestOnUIThread(
        mojo::PendingReceiver<network::mojom::URLLoaderFactory>
            network_factory_receiver) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  mojo::PendingRemote<network::mojom::URLLoaderFactory> factory_remote;
  create_url_loader_factory_callback_.Run(&factory_remote);

  if (!factory_remote) {
    // The underlying URLLoaderFactory has went away while
    // `ReconnectableURLLoaderFactoryForIOThread` is still held by consumers.
    return;
  }

  CHECK(mojo::FusePipes(std::move(network_factory_receiver),
                        std::move(factory_remote)));
}

https://source.chromium.org/chromium/chromium/src/+/main:services/network/public/cpp/single_request_url_loader_factory.cc;l=29?q=CreateLoaderAndStart&ss=chromium%2Fchromium%2Fsrc&start=61

 void CreateLoaderAndStart(
      mojo::PendingReceiver<mojom::URLLoader> loader,
      int32_t request_id,
      uint32_t options,
      const ResourceRequest& request,
      mojo::PendingRemote<mojom::URLLoaderClient> client,
      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
    if (!handler_task_runner_->RunsTasksInCurrentSequence()) {
      handler_task_runner_->PostTask(
          FROM_HERE,
          base::BindOnce(&HandlerState::CreateLoaderAndStart, this,
                         std::move(loader), request_id, options, request,
                         std::move(client), traffic_annotation));
      return;
    }

    DCHECK(handler_);
    std::move(handler_).Run(std::move(loader), request_id, options, request,
                            std::move(client), traffic_annotation);
  }

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/loader/reconnectable_url_loader_factory.cc;l=23;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1

void ReconnectableURLLoaderFactory::CreateLoaderAndStart(
    mojo::PendingReceiver<network::mojom::URLLoader> receiver,
    int32_t request_id,
    uint32_t options,
    const network::ResourceRequest& url_request,
    mojo::PendingRemote<network::mojom::URLLoaderClient> client,
    const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
  if (network::mojom::URLLoaderFactory* factory = GetURLLoaderFactory()) {
    factory->CreateLoaderAndStart(std::move(receiver), request_id, options,
                                  url_request, std::move(client),
                                  traffic_annotation);
  }
}

https://source.chromium.org/chromium/chromium/src/+/main:content/browser/loader/reconnectable_url_loader_factory.cc;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133;bpv=1;bpt=1;l=52

network::mojom::URLLoaderFactory*
ReconnectableURLLoaderFactory::GetURLLoaderFactory() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  // Create the URLLoaderFactory as needed, but make sure not to reuse a
  // previously created one if the test override has changed.
  if (url_loader_factory_ && url_loader_factory_.is_connected() &&
      is_test_url_loader_factory_ ==
          !!url_loader_factory::GetTestingInterceptor()) {
    return url_loader_factory_.get();
  }

  is_test_url_loader_factory_ = !!url_loader_factory::GetTestingInterceptor();
  url_loader_factory_.reset();

  mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory;
  create_url_loader_factory_callback_.Run(&url_loader_factory);
  if (!url_loader_factory) {
    return nullptr;
  }

  url_loader_factory_.Bind(std::move(url_loader_factory));
  return url_loader_factory_.get();
}
create_url_loader_factory_callback_ == StoragePartitionImpl::CreateURLLoaderFactoryForBrowserProcessInternal

handler_の設定箇所はおそらくこれ? https://source.chromium.org/chromium/chromium/src/+/main:services/network/public/cpp/single_request_url_loader_factory.cc;l=89-101;drc=4f289372eacd2b226e9d2830cc4a87b4ad621133

SingleRequestURLLoaderFactory::SingleRequestURLLoaderFactory(
    RequestHandler handler)
    : state_(base::MakeRefCounted<HandlerState>(base::BindOnce(
          [](RequestHandler handler,
             mojo::PendingReceiver<network::mojom::URLLoader> loader,
             int32_t request_id,
             uint32_t options,
             const network::ResourceRequest& request,
             mojo::PendingRemote<network::mojom::URLLoaderClient> client,
             const net::MutableNetworkTrafficAnnotationTag&
                 traffic_annotation) {
            std::move(handler).Run(request, std::move(loader),
                                   std::move(client));
          },
          std::move(handler)))) {}

さらにここから呼ばれている? https://source.chromium.org/chromium/chromium/src/+/main:content/browser/loader/navigation_url_loader_impl.cc;l=153?q=SingleRequestURLLoaderFactory&ss=chromium%2Fchromium%2Fsrc&start=1

  void MaybeCreateLoader(
      const network::ResourceRequest& tentative_resource_request,
      BrowserContext* browser_context,
      LoaderCallback callback,
      FallbackCallback fallback_callback) override {
    browser_interceptor_->MaybeCreateLoader(
        tentative_resource_request, browser_context,
        base::BindOnce(
            [](LoaderCallback callback,
               URLLoaderRequestInterceptor::RequestHandler handler) {
              if (handler) {
                std::move(callback).Run(NavigationLoaderInterceptor::Result(
                    base::MakeRefCounted<
                        network::SingleRequestURLLoaderFactory>(
                        std::move(handler)),
                    /*subresource_loader_params=*/{}));
              } else {
                std::move(callback).Run(std::nullopt);
              }
            },
            std::move(callback)));
  }

3. クライアントOS層(Windows)
#

今回の仮定ではクライアントがWindowsのPCであるが残念ながらWindowsはOSSではないため直接的にソースコードを見ることは不可能である。 しかし例えば以下のスライドや記事等に示されるようにリバースエンジニアリングの知見からTCP/IPプロトコル・スタックが動いていることが推察できる。 https://i.blackhat.com/EU-23/Presentations/EU-23-Quan-Breaking-Theoretical-Limits_REV2.pdf https://doar-e.github.io/blog/2021/04/15/reverse-engineering-tcpipsys-mechanics-of-a-packet-of-the-death-cve-2021-24086/

https://ja.wikipedia.org/wiki/Network_Driver_Interface_Specification

残念ながらWindowsのカーネルデバッグとかについてはあまり詳しくないのとVirtualBox環境でWinDbgで動かすのを試みようとしたもののうまく繋がらずであったため深堀りができていない https://learn.microsoft.com/ja-jp/windows-hardware/drivers/debugger/getting-started-with-windbg--kernel-mode-

4. Wifiルーターまで
#

自環境においては最初のルーターまではWi-Fiによる接続が行われている。 プロトコルとしてはWi-Fi6(ieee802.11ax)が使われており 暗号化方式はWPA2パーソナル(事前鍵共有方式)である。

基本的な中身としてはデータリンク層(MPDU/PSDU)においては以下のようにデータが格納されており

https://www.n-study.com/wlan-detail/802-11-frame-type-subtype/ https://github.com/on-keyday/brgen/blob/main/example/ieee802_11.bgn

enum FrameType:
    :u2
    Management = 0
    Control = 1
    Data = 2
    Extension = 3


format FrameControl: # msb to lsb
    protocol_version :u2
    frame_type: FrameType
    sub_type: u4
    to_ds: u1
    from_ds: u1
    more_frag: u1
    retry: u1
    power_mgmt: u1
    more_data: u1
    protected_frame: u1
    order: u1

format IEEE802_11FCS:
    frame_control: u16
    duration_id: u16
    addr1 : [6]u8
    addr2 : [6]u8
    addr3 : [6]u8
    sequence_control: u16
    addr4 : [6]u8
    payload: [..]u8
    fcs: u32

そして最下層のPPDU部分が

https://www.rfwireless-world.com/terminology/wlan-802-11ax-frame-structure-ppdu-formats

L-STF L-LTF L-SIGのレガシープリアンブルに続き

HE SU PPDU RL-SIG HE-SIGA HE-STF HE-LTF1 HE SIG-B HE

(途中)

なお残念ながらIEEE802.11axの規格そのものは手に入れられなかった。

5. Wifiルーターから宛先まで
#

Wifiルーターは光BBユニットでありそれをNTTのRT-500KIルーターに繋ぐ形となっている。

光BBユニット https://www.softbank.jp/internet/support/connect/hikari/router/bbu24-menu/ RT-500KIルーター https://web116.jp/shop/hikari_r/guide/500ki/index/

推測でしかないがSoftbankBBがBBIX社の技術を使っていると仮定すると OCX光を使っているのではないかと思われるが実際のところがどうなのかはいまいちわからない。 OCX 光(BBIX社の提供するVNEサービス) https://ocx-hikari.net/technical OCX 光の公式ページではIP in IPを利用していると言っているがおそらく4in6(RFC2473) https://en.wikipedia.org/wiki/IP_in_IP https://en.wikipedia.org/wiki/4in6 ただし、他のVNEでは明示的にRTシリーズルーターをサポートする旨が書いてあったのに対して OCX光には直接的な記述はなかった。 https://www.mfeed.ad.jp/transix/overview.html https://www.ntt.com/content/dam/nttcom/hq/jp/business/services/network/internet-connect/ocn-business/option/v-access-ipoe/pdf/bocn_vc_router.pdf また実際の自宅のルーターにspliterなり線をつなぐなりして確認できているわけではないため あくまでも推測に過ぎない。

https://github.com/v6pc/v6mig-prov/blob/1.1/spec.md https://www.nextech.co.jp/business/nt4ov6kit/ https://qiita.com/metastable-void/items/74b49f3efc72eb6ca623 https://www.ntt-west.co.jp/ngn/business/index.html

RT-500KIからはGE-PON FA C ONU タイプDに繋がれており光回線の速度自体は最大1Gbps相当ということになっている https://www.fujitsu.com/jp/products/network/carrier-router/archives/a-gepon/spec-2132/ そこでEthernetと光回線の変換が行われてNTT西日本のNGN網へ接続されている。

今回の宛先はIPv4でルート指定されたが途中まではIPv6で運ばれているためその基盤部分のルートについても調査を試みた。

RT-500KIにもWifiルーター機能があったためそのネットワークに直接繋いでIPv6レイヤーでtracertをしてみたところ

$ tracert -w 300  google.com

Tracing route to google.com [2404:6800:4004:808::200e]
over a maximum of 30 hops:

  1     1 ms     1 ms     1 ms  (redacted for privacy)
  2     *        *        *     Request timed out.
  3     *        *        *     Request timed out.
  4     *        *        *     Request timed out.
  5    14 ms    13 ms    14 ms  2400:2000:bb1a:3308::1
  6    14 ms    14 ms    13 ms  2400:2000:2:0:1a:0:2:21
  7    15 ms    13 ms    15 ms  2400:2000:0:201:1:5169:1011:2
  8    18 ms    15 ms    15 ms  2001:4860:0:1::86eb
  9    14 ms    14 ms    13 ms  2001:4860:0:1::86e6
 10    28 ms    23 ms    23 ms  2001:4860::c:4001:389
 11    22 ms    22 ms    22 ms  2001:4860::9:4001:388
 12    25 ms    26 ms    22 ms  2001:4860:0:1::659
 13    22 ms    22 ms    21 ms  nrt20s08-in-x0e.1e100.net [2404:6800:4004:808::200e]

Trace complete.

そして光BBユニットを経由してtracertしてみたところ。

$ tracert -w 300  google.com

Tracing route to google.com [2404:6800:4004:818::200e]
over a maximum of 30 hops:

  1     1 ms    <1 ms     2 ms  (redacted for privacy)
  2     2 ms     2 ms     2 ms  (redacted for privacy)
  3     *        *        *     Request timed out.
  4     *        *        *     Request timed out.
  5     *        *        *     Request timed out.
  6     *        *        *     Request timed out.
  7    16 ms    16 ms    15 ms  2400:2000:2:0:1a:0:2:21
  8    15 ms    15 ms    15 ms  2400:2000:0:201:1:5169:1011:2
  9    15 ms    15 ms    15 ms  2001:4860:0:1::39bd
 10    16 ms    16 ms    15 ms  2001:4860:0:1::17d4
 11    24 ms    24 ms    24 ms  2001:4860::c:4002:2166
 12    24 ms    23 ms    23 ms  2001:4860::9:4001:388
 13    22 ms    22 ms    23 ms  2001:4860:0:1::6ec5
 14    23 ms    22 ms    22 ms  nrt13s50-in-x0e.1e100.net [2404:6800:4004:818::200e]

Trace complete.

おそらくルートとして必ずBBIXのルートを通るように制御をかけられている。 また2400:2000:bb1a:3308::1が光BBユニットがあるときには非表示になっているためここでなんらかの特殊な処理、 例えばIPv4 over IPv6を引き剥がすなどがされているのかもしれない。(これは完全なる推測である) おそらく最初の空白地帯はNGN網内部であろうと推定される。

他NGN網とVNEのつながりについては以下の記事があり、おそらくVNEまでのルートについては送信元のグローバルIPアドレスベースでどのVNEのネットワークに行くかが決定されているようだ。 https://www.geekpage.jp/blog/?id=2013/1/11/1 https://dforest.watch.impress.co.jp/library/p/proipv6/11948/ProfessionalIPv62ndedition-2023.pdf

ここではIPv4パケットがRFC2473の方式で行っているものと仮定し事前調査のtracerouteの通りのルートを通って202.218.223.232まで到達したものとする。 おそらく各ルーター内では FIBを元にルーティングするとかあるいはFIBを設定するまでのところでもBGPなりあるいは管理者の設定したポリシーに基づく設定なりいろいろやってはいるであろうし IPv4 over IPv6 Gatewayの存在があるのでそれの挙動もあるであろうし あるいは帰りのパケットが行きとは全く別の経路を辿っているとかもありそうではあるし 他考慮漏れしている事項はいろいろあると思うが観測しようがないので割愛する。

6. 宛先のOSレイヤー(Linux)
#

LLMに聞いて確認程度なので正確性については疑問が残るがコード量的に Linuxのライセンス GPL 2.0に従うとすると この箇所についてはソースコード公開義務があるかもしれないとなっているため別記事に移動

Linuxレイヤーの部分

7. Apache
#

Apache httpdが使われていると仮定する。観測できる範囲では前段に何が使われているかと行った情報は一切わからないためApacheが全部処理していると仮定する。またmpm_eventを使っていると仮定する。 https://github.com/apache/httpd https://github.com/apache/apr

またLLMに分析させた結果 ServerTokens Prod でサーバーバージョンを開示しないようにされておりまたmod_headersで Header always append X-Frame-Options SAMEORIGIN のようにクリックジャッキング攻撃を防ぐための設定などがされている。

参照: https://qiita.com/bezeklik/items/1c4145652661cf5b2271

まずepoll_waitしているところから https://github.com/apache/apr/blob/5a6d5173c8b88767616e80ab33a711c4e0543e04/poll/unix/epoll.c#L271

epoll_waitはlistener_threadでapr_pollset_poll経由で呼び出されている。 https://github.com/apache/httpd/blob/af61c91eb967f28d22627090f58a287a7a5366b7/server/mpm/event/event.c#L2100

acceptする https://github.com/apache/httpd/blob/af61c91eb967f28d22627090f58a287a7a5366b7/server/mpm/event/event.c#L2259

ap_unixd_accept https://github.com/apache/httpd/blob/af61c91eb967f28d22627090f58a287a7a5366b7/os/unix/unixd.c#L293

apr_socket_acceptでaccept/accept4システムコール呼び出し及びユーザー空間のapr_socket構造体を作成 https://github.com/apache/apr/blob/5a6d5173c8b88767616e80ab33a711c4e0543e04/network_io/unix/sockets.c#L268

push2workerでworkerスレッドに送信 https://github.com/apache/httpd/blob/af61c91eb967f28d22627090f58a287a7a5366b7/server/mpm/event/event.c#L1496

https://github.com/apache/httpd/blob/af61c91eb967f28d22627090f58a287a7a5366b7/server/mpm_fdqueue.c#L382

/**
 * Push a new socket onto the queue.
 *
 * precondition: ap_queue_info_wait_for_idler has already been called
 *               to reserve an idle worker thread
 */
apr_status_t ap_queue_push_socket(fd_queue_t *queue,
                                  apr_socket_t *sd, void *sd_baton,
                                  apr_pool_t *p)

worker_threadで取得する https://github.com/apache/httpd/blob/af61c91eb967f28d22627090f58a287a7a5366b7/server/mpm/event/event.c#L2512

        rv = ap_queue_pop_something(worker_queue, &csd, (void **)&cs,
                                    &ptrans, &te);

process_socketで処理開始 https://github.com/apache/httpd/blob/af61c91eb967f28d22627090f58a287a7a5366b7/server/mpm/event/event.c#L1048

/*
 * process one connection in the worker
 */
static void process_socket(apr_thread_t *thd, apr_pool_t * p, apr_socket_t * sock,
                          event_conn_state_t * cs, int my_child_num,
                          int my_thread_num)

https://github.com/apache/httpd/blob/af61c91eb967f28d22627090f58a287a7a5366b7/server/core.c#L5675

ap_pre_connection(conn_rec *c, void *csd)

https://github.com/apache/httpd/blob/af61c91eb967f28d22627090f58a287a7a5366b7/server/core.c#L5626

static int core_pre_connection(conn_rec *c, void *csd)

https://stackoverflow.com/questions/22167234/where-the-function-ap-run-pre-connection-in-apache-httpd-source-code#22171266

alpn callback処理 https://github.com/apache/httpd/blob/af61c91eb967f28d22627090f58a287a7a5366b7/modules/ssl/ssl_engine_kernel.c#L2826

https://github.com/apache/httpd/blob/af61c91eb967f28d22627090f58a287a7a5366b7/server/protocol.c#L2409

AP_DECLARE(const char *) ap_select_protocol(conn_rec *c, request_rec *r, 
                                            server_rec *s,
                                            const apr_array_header_t *choices)

現在のconenctionのprotocolが異なる場合は変更する https://github.com/apache/httpd/blob/af61c91eb967f28d22627090f58a287a7a5366b7/modules/ssl/ssl_engine_kernel.c#L2843

https://github.com/apache/httpd/blob/af61c91eb967f28d22627090f58a287a7a5366b7/modules/http2/h2_switch.c#L154

static int h2_protocol_switch(conn_rec *c, request_rec *r, server_rec *s,
                              const char *protocol)

https://github.com/apache/httpd/blob/af61c91eb967f28d22627090f58a287a7a5366b7/modules/http2/h2_c1.c#L238

static int h2_c1_hook_process_connection(conn_rec* c)

(時間切れ)

【問3:自由アピール】
#

技術同人誌を書いたりしています。 https://techbookfest.org/product/rg6Y8rm2N2nVD4bkqBVv6i?productVariantID=iEp1TjzPyCrQramkN4Sx25

正直高速化とかキャッシュ戦略とかという領域が自分はあまりわかっていないというのを感じるのでそこらへんの実装とかがあるのであればぜひやりたいです。あとはDNSによる負荷分散とかあるいはanycastとかQUICとかを使った配信高速化みたいなのがあるのかどこまでやるのかなというのが楽しみです。

【問4:事前学習について】
#

事前学習に同意する

まとめ
#

とにかくソースコードを読んでコピペすればどうにか受かるようなので(違

ぜひソースコードを読みましょう

正解を求めているわけではないらしいのでこんな拙くてほとんどソースコードが本文みたいなのでも 通してくれることもあるということがわかったので来年以降応募する方は一つの参考程度にしてもらえればと…

https://www.ipa.go.jp/jinzai/security-camp/2025/camp/zenkoku/message.html

真面目なこと言うと

情報セキュリティに対する向き合い方(知識を深めたい意欲や、技術力を伸ばしたい熱意、課題に取り組む姿勢、考察の過程など)

を見ていると書いてあるのでこれくらいやれば文章拙くても意欲が示せますよという例ではあるのかもしれない….