- Outside of components/ipfs
- chrome/browser gains a dependency on components/ipfs
- Other apps may opt in as well.
- In ChromeContentBrowserClient::WillCreateURLLoaderRequestInterceptors
- Instantiate an ipfs::Interceptor and add it to the list.
- ipfs:// and ipns:// URLs should be recongized in the omnibar
- New component "ipfs" contains classes:
- URLLoaderRequestInterceptor
- Checks the scheme against ipfs/ipns to decide whether to intercept
- Creates Loader for each IPFS/IPNS request
- Gateways - maintains a list of known http gateways
- Some are hard-coded
- An embedder has the option to add more
- Possible future work
- Browser settings for end-users to manage gateway list
- Zeroconf
- Maintains dynamic scoring of gateways based on performance
- get_list() generates an ordered (prioritized) list to be used for a given request
- The specific algo could be configured and/or extended
- A URLLoader for ipfs:// and ipns:// URLs
- Currently most of the actual mechanism is planned to go here
- Could be further decomposed
- Data validation utilities (*)
- IPFS validation
- Parse the CID to get hash & hash algo
- Hash the block and check that they match
- IPNS validation
- Parse the name as CIDv1, if fails assume DNSLink
- Fetch the IPNS name record, see https://gist.github.com/hacdias/f8935decee0c6a57513f2221c5a2fd7c for examples.
- DNSLink URLs
- Attempt to fetch the TXT record by standard Chromium means
- Do the substitution as described here
Navigation Request Flow
Top-Level
graph TD;
start(["ChromeContentBrowserClient::WillCreateURLLoaderRequestInterceptors()"])
style start fill:#9CF,color:black
start --"(creates)"----> inter[[ipfs::Interceptor]]
prev[[Previous Interceptor]] --> req0[/"URLRequest(1)"/]
style req0 fill:#FFE,color:black
req0 --> inter
inter --> is_ipfs{"Is scheme ip_s?"}
is_ipfs --NO--> req1[/"URLRequest(1)"/]
style req1 fill:#FFE,color:black
req1 --> next[["Next Interceptor(not handled)"]]
is_ipfs --"YES (creates)"--> loader[["ipfs::Loader(URLLoader)"]]
state[["Gateways"]] --> gwlist[/"Prioritized list of gateways"/] --> loader
style gwlist fill:#FFE,color:black
req2[/"URLRequest(2)"/] --"Maybe modified, but not scheme"--> loader
style req2 fill:#FFE,color:black
errored(["Call back to client code with error"])
style errored fill:#9CF,color:black
loader --> is_ipns{"URL Scheme"} --IPNS--> parse_ipns[/"Attempt to parse name as CIDV1"/] --FAIL--> dns_txt[/"Attempt to fetch dnslink= from TXT"/] --FAIL--> errored
dns_txt --PASS--> dns_sub["Replace URL prefix based on value"] --> is_ipns
parse_ipns --PASS--> detnrurl["Set 'Accept: application/vnd.ipfs.ipns-record'"] --> gwreq>"Gateway Request (see next chart)"]
style gwreq fill:#FDC,color:black
gwreq --> validate --> sub["Substitute into URL"] --> is_ipns
is_ipns --IPFS--> hdr["Set 'Accept: application/vnd.ipld.raw'"] --> gwreq0>"Gateway Request"]
style gwreq0 fill:#FDC,color:black
gwreq0 --> v["validate"] --> store["Store/append the block if we're at the end of the path"] --> links{"Are there links in the block?"} --YES--> filtr["If URL contains path, only follow appropriate DAG links"] --"For Each"--> hdr
gwreq --ALL FAIL--> errored
gwreq0 --ALL FAIL--> errored
links --NO--> reconstitute --> successed(["Call back with status 200 and response body"])
style successed fill:#9CF,color:black
Gateway Request
The class maintains:
- Pending requests: A list where each member has:
- URL suffix (everything after the gateway)
- enum/callback for what to do with the response
- duplicate count: how many gateways it's been requested from already
- A list of gateways marked or segregated based on:
- GOOD/BAD - whether the gateway has returned a successful http response during this class's lifetime yet.
- BUSY/FREE - whether an HTTP request to that gateway is outstanding
- For each gateway, a set of TaskFailed requests (URL suffixes)
graph TD;
start(["URL suffix from above flow"]) --> add[("Add to startup_pending_ requests")]
style start fill:#9CF,color:black
add --> selreq["Select the startup_pending_ request with the lowest dup count"] --> incdup["Increase its dup count"]
incdup --> goodfree{"Is there a gateway that is both GOOD and FREE"}
goodfree --NO--> badfree{"Are there any FREE that have not already TaskFailed this request?"}
badfree --NO--> anybusy{"Are there any BUSY gateways?"}
anybusy --YES--> wait(("Wait for startup_pending_ requests to finish (return flow control)"))
badfree --YES--> selbf["Select the one with the fewest TaskFailed requests, initial parallel tiebreaks"]
selbf --> mark_busy["Mark the gateway as BUSY"]
goodfree --YES--> selbf
mark_busy --> send["Create and send an HTTP URLRequest"]
send -.callback.-> resp
send --> conc{"Do we have the max concurrent requests already?"} --NO--> selreq
conc --YES--> wait
resp[/"URL response"/] --> markfree["Mark the gateway as FREE"] --> success{"status=200 + body?"}
style resp fill:#FFE,color:black
success --YES--> cancel["Cancel identical requests"] --> markgood["Mark the gateway as GOOD"]
markgood --> incprio["Indicate to Gateways that this gateway's score/parallel should be a bit higher"]
incprio ----> successed(["Return response (see previous diagram)"])
style successed fill:#9CF,color:black
success --NO--> addbad["Add the URL suffix to this gateway's set of failures."]
addbad --> decprio[Indicate to Gateways that this gateway's score/parallel should be a bit lower] --> selreq
anybusy --NO---> all_failed(["As all gateways have TaskFailed on a given request, report that failure"])
style all_failed fill:#9CF,color:black
Class Diagram
classDiagram
class ChromeContentBrowserClient {
WillCreateURLLoaderRequestInterceptors()
...
}
class ipfs_Loader {
pending_requests
gateway_list
...
}
class Gateways {
get_list()
increase_priority()
decrease_priority()
}
ChromeContentBrowserClient ..> ipfs_Interceptor
ipfs_Interceptor ..> Gateways
ipfs_Interceptor ..> ipfs_Loader
ipfs_Loader ..> Gateways