diff --git a/.github/tour.sh b/.github/tour.sh index e516f1ef..7c03223a 100755 --- a/.github/tour.sh +++ b/.github/tour.sh @@ -1,6 +1,6 @@ #!/bin/bash -ex echo Clone tester repo. -git clone https://github.com/John-LittleBearLabs/ipfs_client_clitester.git +git clone --single-branch --branch simplify https://github.com/John-LittleBearLabs/ipfs_client_clitester.git echo Install dependencies. sudo apt-get update @@ -38,8 +38,12 @@ do done function url_case() { - echo "url_case(" "${@}" ")" - if timeout 360 ./tester_build/clitester warning "${1}://${2}" + echo "namespace =${1}" + echo "path remaining =${2}" + echo "output hash =${3}" + echo "test case description =${4}" + echo "log level =${5-trace}" + if timeout 360 ./tester_build/clitester "${5-trace}" "${1}://${2}" then echo clitester exited with successful status else @@ -47,7 +51,12 @@ function url_case() { exit 7 fi n=`sed 's,[^A-Za-z0-9\.],_,g' <<< ${2}` - if cat "_${1}_${n}" | md5sum | cut -d ' ' -f 1 > actual + if [ ! -f "_${1}_${n}" ] + then + echo "Failed to produce expected file _${1}_${n} for test case " "${@}" + ls -lrth + exit 7 + elif cat "_${1}_${n}" | md5sum | cut -d ' ' -f 1 > actual then ls -lrth _ip?s_* if [ $# -ge 3 ] @@ -57,6 +66,7 @@ function url_case() { then echo good else + xxd "_${1}_${n}" echo "Got wrong result: " "${@}" exit 8 fi @@ -74,7 +84,7 @@ url_case ipfs bafkqacdjmrsw45djor4q ff483d1ff591898a9942916050d2ca3f 'Identity ( url_case ipfs baguqeerah2nswg7r2pvlpbnsz5y4c4pr4wsgbzixdl632w5qxvedqzryf54q 7750fd7b0928f007e1d181763c0dbdb5 'A DAG-JSON document. The block itself md5s to b92348005af4ae4795e6f312844fb359, but the response we are hashing here is an HTML preview page. This does mean this test breaks if you make the preview less ugly.' url_case ipns en.wikipedia-on-ipfs.org/I/HFE_Too_Slow_1.JPG.webp 09c09b2654e8529740b5a7625e39e0c8 'An image fetched through DNSLink and HAMT sharded directories.' -url_case ipfs bafybeieb33pqideyl5ncd33kho622thym5rqv6sujrmelcuhkjlf2hdpu4/Big%20Buck%20Bunny.webm 06d51286e56badb4455594ebed6daba2 'A large UnixFS file - several hundred blocks.' +echo 'Skip as it takes too long.' url_case ipfs bafybeieb33pqideyl5ncd33kho622thym5rqv6sujrmelcuhkjlf2hdpu4/Big%20Buck%20Bunny.webm 06d51286e56badb4455594ebed6daba2 'A large UnixFS file - several hundred blocks.' error url_case ipns k51qzi5uqu5dijv526o4z2z10ejylnel0bfvrtw53itcmsecffo8yf0zb4g9gi/symlinks/relative_link.txt cfe9b69523140b5b5e63874a8e4997e4 'A relative symlink resolves successfully to the file pointed to.' echo Stop test server. diff --git a/chromium_edits/121.0.6147.0/chrome/browser/flag-metadata.json.patch b/chromium_edits/121.0.6147.0/chrome/browser/flag-metadata.json.patch deleted file mode 100644 index 476c7cb6..00000000 --- a/chromium_edits/121.0.6147.0/chrome/browser/flag-metadata.json.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json -index f8609c4f6e389..7f329f8ab9bfb 100644 ---- a/chrome/browser/flag-metadata.json -+++ b/chrome/browser/flag-metadata.json -@@ -2889,6 +2889,11 @@ - "owners": [ "hanxi@chromium.org", "wychen@chromium.org" ], - "expiry_milestone": 130 - }, -+ { -+ "name": "enable-ipfs", -+ "owners": [ "//components/ipfs/OWNERS" ], -+ "expiry_milestone": 150 -+ }, - { - "name": "enable-isolated-sandboxed-iframes", - "owners": [ "wjmaclean@chromium.org", "alexmos@chromium.org", "creis@chromium.org" ], diff --git a/chromium_edits/121.0.6147.0/chrome/browser/flag_descriptions.h.patch b/chromium_edits/121.0.6147.0/chrome/browser/flag_descriptions.h.patch deleted file mode 100644 index 449fbccc..00000000 --- a/chromium_edits/121.0.6147.0/chrome/browser/flag_descriptions.h.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h -index ca4c8f927ba26..ab6f0ee6cb103 100644 ---- a/chrome/browser/flag_descriptions.h -+++ b/chrome/browser/flag_descriptions.h -@@ -22,6 +22,7 @@ - #include "pdf/buildflags.h" - #include "printing/buildflags/buildflags.h" - #include "third_party/blink/public/common/buildflags.h" -+#include "third_party/ipfs_client/ipfs_buildflags.h" - - // This file declares strings used in chrome://flags. These messages are not - // translated, because instead of end-users they target Chromium developers and -@@ -172,6 +173,11 @@ extern const char kDownloadWarningImprovementsDescription[]; - extern const char kEnableBenchmarkingName[]; - extern const char kEnableBenchmarkingDescription[]; - -+#if BUILDFLAG(ENABLE_IPFS) -+extern const char kEnableIpfsName[]; -+extern const char kEnableIpfsDescription[]; -+#endif -+ - #if BUILDFLAG(USE_FONTATIONS_BACKEND) - extern const char kFontationsFontBackendName[]; - extern const char kFontationsFontBackendDescription[]; diff --git a/chromium_edits/121.0.6147.0/url/url_canon_ipfs.cc b/chromium_edits/121.0.6147.0/url/url_canon_ipfs.cc deleted file mode 100644 index c25912fa..00000000 --- a/chromium_edits/121.0.6147.0/url/url_canon_ipfs.cc +++ /dev/null @@ -1,64 +0,0 @@ -#include "url_canon_internal.h" - -#include -#include - -#include - -namespace m = libp2p::multi; -using Cid = m::ContentIdentifier; -using CidCodec = m::ContentIdentifierCodec; - -bool url::CanonicalizeIpfsURL(const char* spec, - int spec_len, - const Parsed& parsed, - SchemeType scheme_type, - CharsetConverter* charset_converter, - CanonOutput* output, - Parsed* output_parsed) { - if (spec_len < 1 || !spec) { - return false; - } - if (parsed.host.len < 1) { - return false; - } - std::string cid_str{spec + parsed.host.begin, - static_cast(parsed.host.len)}; - auto maybe_cid = CidCodec::fromString(cid_str); - if (!maybe_cid.has_value()) { - auto e = libp2p::multi::Stringify(maybe_cid.error()); - std::ostringstream err; - err << e << ' ' - << std::string_view{spec, static_cast(spec_len)}; - maybe_cid = ipfs::id_cid::forText(err.str()); - } - auto cid = maybe_cid.value(); - if (cid.version == Cid::Version::V0) { - cid = Cid{Cid::Version::V1, cid.content_type, cid.content_address}; - } - auto as_str = CidCodec::toString(cid); - if (!as_str.has_value()) { - return false; - } - std::string stdurl{spec, static_cast(parsed.host.begin)}; - stdurl.append(as_str.value()); - stdurl.append(spec + parsed.host.end(), spec_len - parsed.host.end()); - spec = stdurl.data(); - spec_len = static_cast(stdurl.size()); - Parsed parsed_input; - ParseStandardURL(spec, spec_len, &parsed_input); - return CanonicalizeStandardURL(spec, spec_len, parsed_input, scheme_type, - charset_converter, output, output_parsed); -} -bool url::CanonicalizeIpfsURL(const char16_t* spec, - int spec_len, - const Parsed& parsed, - SchemeType scheme_type, - CharsetConverter* query_converter, - CanonOutput* output, - Parsed* new_parsed) { - RawCanonOutput<2048> as8; - ConvertUTF16ToUTF8(spec, spec_len, &as8); - return CanonicalizeIpfsURL(as8.data(), as8.length(), parsed, scheme_type, - query_converter, output, new_parsed); -} diff --git a/chromium_edits/121.0.6147.0/url/url_util.cc.patch b/chromium_edits/121.0.6147.0/url/url_util.cc.patch deleted file mode 100644 index 0332e847..00000000 --- a/chromium_edits/121.0.6147.0/url/url_util.cc.patch +++ /dev/null @@ -1,17 +0,0 @@ -diff --git a/url/url_util.cc b/url/url_util.cc -index 9258cfcfada47..daf10e4c3b741 100644 ---- a/url/url_util.cc -+++ b/url/url_util.cc -@@ -277,6 +277,12 @@ bool DoCanonicalize(const CHAR* spec, - charset_converter, output, - output_parsed); - -+ } else if (DoCompareSchemeComponent(spec, scheme, "ipfs")) { -+ // Switch multibase away from case-sensitive ones before continuing canonicalization. -+ ParseStandardURL(spec, spec_len, &parsed_input); -+ success = CanonicalizeIpfsURL(spec, spec_len, parsed_input, scheme_type, -+ charset_converter, output, output_parsed); -+ - } else if (DoIsStandard(spec, scheme, &scheme_type)) { - // All "normal" URLs. - ParseStandardURL(spec, spec_len, &parsed_input); diff --git a/chromium_edits/121.0.6156.3/chrome/browser/BUILD.gn.patch b/chromium_edits/121.0.6156.3/chrome/browser/BUILD.gn.patch deleted file mode 100644 index 9af9be4d..00000000 --- a/chromium_edits/121.0.6156.3/chrome/browser/BUILD.gn.patch +++ /dev/null @@ -1,23 +0,0 @@ -diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn -index 4ec6cf79062ea..dc9d3637464bb 100644 ---- a/chrome/browser/BUILD.gn -+++ b/chrome/browser/BUILD.gn -@@ -40,6 +40,7 @@ import("//rlz/buildflags/buildflags.gni") - import("//sandbox/features.gni") - import("//testing/libfuzzer/fuzzer_test.gni") - import("//third_party/blink/public/public_features.gni") -+import("//third_party/ipfs_client/args.gni") - import("//third_party/protobuf/proto_library.gni") - import("//third_party/webrtc/webrtc.gni") - import("//third_party/widevine/cdm/widevine.gni") -@@ -2630,6 +2631,10 @@ static_library("browser") { - ] - } - -+ if (enable_ipfs) { -+ deps += [ "//components/ipfs" ] -+ } -+ - if (is_chromeos_ash) { - deps += [ "//chrome/browser/screen_ai:screen_ai_dlc_installer" ] - } diff --git a/chromium_edits/121.0.6156.3/url/BUILD.gn.patch b/chromium_edits/121.0.6156.3/url/BUILD.gn.patch deleted file mode 100644 index 63fb8f8b..00000000 --- a/chromium_edits/121.0.6156.3/url/BUILD.gn.patch +++ /dev/null @@ -1,32 +0,0 @@ -diff --git a/url/BUILD.gn b/url/BUILD.gn -index c525c166979d6..ce2b1ae43c0a7 100644 ---- a/url/BUILD.gn -+++ b/url/BUILD.gn -@@ -5,6 +5,7 @@ - import("//build/buildflag_header.gni") - import("//testing/libfuzzer/fuzzer_test.gni") - import("//testing/test.gni") -+import("//third_party/ipfs_client/args.gni") - import("features.gni") - - import("//build/config/cronet/config.gni") -@@ -67,6 +68,7 @@ component("url") { - public_deps = [ - "//base", - "//build:robolectric_buildflags", -+ "//third_party/ipfs_client:ipfs_buildflags", - ] - - configs += [ "//build/config/compiler:wexit_time_destructors" ] -@@ -89,6 +91,11 @@ component("url") { - public_configs = [ "//third_party/jdk" ] - } - -+ if (enable_ipfs) { -+ sources += [ "url_canon_ipfs.cc" ] -+ deps += [ "//third_party/ipfs_client:ipfs_client" ] -+ } -+ - if (is_win) { - # Don't conflict with Windows' "url.dll". - output_name = "url_lib" diff --git a/chromium_edits/121.0.6156.3/url/url_canon.h.patch b/chromium_edits/121.0.6156.3/url/url_canon.h.patch deleted file mode 100644 index 24ae1ba4..00000000 --- a/chromium_edits/121.0.6156.3/url/url_canon.h.patch +++ /dev/null @@ -1,28 +0,0 @@ -diff --git a/url/url_canon.h b/url/url_canon.h -index d3a7fabf09fa8..06db17242248f 100644 ---- a/url/url_canon.h -+++ b/url/url_canon.h -@@ -697,6 +697,23 @@ bool CanonicalizeMailtoURL(const char16_t* spec, - CanonOutput* output, - Parsed* new_parsed); - -+COMPONENT_EXPORT(URL) -+bool CanonicalizeIpfsURL(const char* spec, -+ int spec_len, -+ const Parsed& parsed, -+ SchemeType scheme_type, -+ CharsetConverter* query_converter, -+ CanonOutput* output, -+ Parsed* new_parsed); -+COMPONENT_EXPORT(URL) -+bool CanonicalizeIpfsURL(const char16_t* spec, -+ int spec_len, -+ const Parsed& parsed, -+ SchemeType scheme_type, -+ CharsetConverter* query_converter, -+ CanonOutput* output, -+ Parsed* new_parsed); -+ - // Part replacer -------------------------------------------------------------- - - // Internal structure used for storing separate strings for each component. diff --git a/chromium_edits/121.0.6156.3/url/url_util.cc.patch b/chromium_edits/121.0.6156.3/url/url_util.cc.patch deleted file mode 100644 index 0332e847..00000000 --- a/chromium_edits/121.0.6156.3/url/url_util.cc.patch +++ /dev/null @@ -1,17 +0,0 @@ -diff --git a/url/url_util.cc b/url/url_util.cc -index 9258cfcfada47..daf10e4c3b741 100644 ---- a/url/url_util.cc -+++ b/url/url_util.cc -@@ -277,6 +277,12 @@ bool DoCanonicalize(const CHAR* spec, - charset_converter, output, - output_parsed); - -+ } else if (DoCompareSchemeComponent(spec, scheme, "ipfs")) { -+ // Switch multibase away from case-sensitive ones before continuing canonicalization. -+ ParseStandardURL(spec, spec_len, &parsed_input); -+ success = CanonicalizeIpfsURL(spec, spec_len, parsed_input, scheme_type, -+ charset_converter, output, output_parsed); -+ - } else if (DoIsStandard(spec, scheme, &scheme_type)) { - // All "normal" URLs. - ParseStandardURL(spec, spec_len, &parsed_input); diff --git a/chromium_edits/121.0.6167.8/chrome/browser/about_flags.cc.patch b/chromium_edits/121.0.6167.8/chrome/browser/about_flags.cc.patch deleted file mode 100644 index 6f181024..00000000 --- a/chromium_edits/121.0.6167.8/chrome/browser/about_flags.cc.patch +++ /dev/null @@ -1,38 +0,0 @@ -diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc -index b3954a5fec88e..162b64711b3ca 100644 ---- a/chrome/browser/about_flags.cc -+++ b/chrome/browser/about_flags.cc -@@ -212,6 +212,7 @@ - #include "third_party/blink/public/common/features_generated.h" - #include "third_party/blink/public/common/forcedark/forcedark_switches.h" - #include "third_party/blink/public/common/switches.h" -+#include "third_party/ipfs_client/ipfs_buildflags.h" - #include "ui/accessibility/accessibility_features.h" - #include "ui/accessibility/accessibility_switches.h" - #include "ui/base/ui_base_features.h" -@@ -312,6 +313,10 @@ - #include "extensions/common/switches.h" - #endif // BUILDFLAG(ENABLE_EXTENSIONS) - -+#if BUILDFLAG(ENABLE_IPFS) -+#include "components/ipfs/ipfs_features.h" -+#endif -+ - #if BUILDFLAG(ENABLE_PDF) - #include "pdf/pdf_features.h" - #endif -@@ -9846,6 +9851,14 @@ const FeatureEntry kFeatureEntries[] = { - flag_descriptions::kOmitCorsClientCertDescription, kOsAll, - FEATURE_VALUE_TYPE(network::features::kOmitCorsClientCert)}, - -+#if BUILDFLAG(ENABLE_IPFS) -+ {"enable-ipfs", -+ flag_descriptions::kEnableIpfsName, -+ flag_descriptions::kEnableIpfsDescription, -+ kOsMac | kOsWin | kOsLinux,//TODO: These are the only variants currently getting built, but that is not likely to remain the case -+ FEATURE_VALUE_TYPE(ipfs::kEnableIpfs)}, -+#endif -+ - {"use-idna2008-non-transitional", - flag_descriptions::kUseIDNA2008NonTransitionalName, - flag_descriptions::kUseIDNA2008NonTransitionalDescription, kOsAll, diff --git a/chromium_edits/121.0.6167.8/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc.patch b/chromium_edits/121.0.6167.8/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc.patch deleted file mode 100644 index 4d172311..00000000 --- a/chromium_edits/121.0.6167.8/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc.patch +++ /dev/null @@ -1,50 +0,0 @@ -diff --git a/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc b/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc -index 4c88614c68c25..f8bb12a3b0c2e 100644 ---- a/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc -+++ b/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc -@@ -10,6 +10,8 @@ - #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" - #include "chrome/browser/external_protocol/external_protocol_handler.h" - #include "chrome/browser/profiles/profile.h" -+#include "third_party/ipfs_client/ipfs_buildflags.h" -+ - #if BUILDFLAG(IS_ANDROID) - #include "chrome/browser/profiles/profile_android.h" - #endif -@@ -18,6 +20,9 @@ - #include "chrome/browser/ui/android/omnibox/jni_headers/ChromeAutocompleteSchemeClassifier_jni.h" - #endif - #include "components/custom_handlers/protocol_handler_registry.h" -+#if BUILDFLAG(ENABLE_IPFS) -+#include "components/ipfs/ipfs_features.h" -+#endif - #include "content/public/common/url_constants.h" - #include "url/url_util.h" - -@@ -55,12 +60,20 @@ ChromeAutocompleteSchemeClassifier::GetInputTypeForScheme( - if (scheme.empty()) { - return metrics::OmniboxInputType::EMPTY; - } -- if (base::IsStringASCII(scheme) && -- (ProfileIOData::IsHandledProtocol(scheme) || -- base::EqualsCaseInsensitiveASCII(scheme, content::kViewSourceScheme) || -- base::EqualsCaseInsensitiveASCII(scheme, url::kJavaScriptScheme) || -- base::EqualsCaseInsensitiveASCII(scheme, url::kDataScheme))) { -- return metrics::OmniboxInputType::URL; -+ if (base::IsStringASCII(scheme)) { -+ if (ProfileIOData::IsHandledProtocol(scheme) || -+ base::EqualsCaseInsensitiveASCII(scheme, content::kViewSourceScheme) || -+ base::EqualsCaseInsensitiveASCII(scheme, url::kJavaScriptScheme) || -+ base::EqualsCaseInsensitiveASCII(scheme, url::kDataScheme)) { -+ return metrics::OmniboxInputType::URL; -+ } -+#if BUILDFLAG(ENABLE_IPFS) -+ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs) && -+ (base::EqualsCaseInsensitiveASCII(scheme, "ipfs") || base::EqualsCaseInsensitiveASCII(scheme, "ipns")) -+ ) { -+ return metrics::OmniboxInputType::URL; -+ } -+#endif - } - - // Also check for schemes registered via registerProtocolHandler(), which diff --git a/chromium_edits/121.0.6167.8/chrome/browser/chrome_content_browser_client.cc.patch b/chromium_edits/121.0.6167.8/chrome/browser/chrome_content_browser_client.cc.patch deleted file mode 100644 index f212961c..00000000 --- a/chromium_edits/121.0.6167.8/chrome/browser/chrome_content_browser_client.cc.patch +++ /dev/null @@ -1,63 +0,0 @@ -diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc -index a9b87ac2fcd74..987686c621664 100644 ---- a/chrome/browser/chrome_content_browser_client.cc -+++ b/chrome/browser/chrome_content_browser_client.cc -@@ -377,6 +377,7 @@ - #include "third_party/blink/public/common/switches.h" - #include "third_party/blink/public/mojom/browsing_topics/browsing_topics.mojom.h" - #include "third_party/blink/public/public_buildflags.h" -+#include "third_party/ipfs_client/ipfs_buildflags.h" - #include "third_party/widevine/cdm/buildflags.h" - #include "ui/base/clipboard/clipboard_format_type.h" - #include "ui/base/l10n/l10n_util.h" -@@ -499,6 +500,12 @@ - #include "chrome/browser/fuchsia/chrome_browser_main_parts_fuchsia.h" - #endif - -+#if BUILDFLAG(ENABLE_IPFS) -+#include "components/ipfs/interceptor.h" -+#include "components/ipfs/ipfs_features.h" -+#include "components/ipfs/url_loader_factory.h" -+#endif -+ - #if BUILDFLAG(IS_CHROMEOS) - #include "base/debug/leak_annotations.h" - #include "chrome/browser/apps/app_service/app_install/app_install_navigation_throttle.h" -@@ -6157,12 +6164,23 @@ void ChromeContentBrowserClient:: - const absl::optional& request_initiator_origin, - NonNetworkURLLoaderFactoryMap* factories) { - #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(ENABLE_EXTENSIONS) || \ -- !BUILDFLAG(IS_ANDROID) -+ !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_IPFS) - content::RenderFrameHost* frame_host = - RenderFrameHost::FromID(render_process_id, render_frame_id); - WebContents* web_contents = WebContents::FromRenderFrameHost(frame_host); - #endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(ENABLE_EXTENSIONS) || \ -- // !BUILDFLAG(IS_ANDROID) -+ // !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_IPFS) -+#if BUILDFLAG(ENABLE_IPFS) -+ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { -+ network::mojom::URLLoaderFactory* default_factory = g_browser_process->system_network_context_manager()->GetURLLoaderFactory(); -+ ipfs::IpfsURLLoaderFactory::Create( -+ factories, -+ web_contents->GetBrowserContext(), -+ default_factory, -+ GetSystemNetworkContext() -+ ); -+ } -+#endif // BUILDFLAG(ENABLE_IPFS) - - #if BUILDFLAG(IS_CHROMEOS_ASH) - if (web_contents) { -@@ -6304,6 +6322,11 @@ ChromeContentBrowserClient::WillCreateURLLoaderRequestInterceptors( - scoped_refptr navigation_response_task_runner) { - std::vector> - interceptors; -+#if BUILDFLAG(ENABLE_IPFS) -+ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { -+ interceptors.push_back(std::make_unique(g_browser_process->system_network_context_manager()->GetURLLoaderFactory(), GetSystemNetworkContext())); -+ } -+#endif - #if BUILDFLAG(ENABLE_OFFLINE_PAGES) - interceptors.push_back( - std::make_unique( diff --git a/chromium_edits/121.0.6167.8/chrome/browser/flag_descriptions.cc.patch b/chromium_edits/121.0.6167.8/chrome/browser/flag_descriptions.cc.patch deleted file mode 100644 index acd0588e..00000000 --- a/chromium_edits/121.0.6167.8/chrome/browser/flag_descriptions.cc.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc -index 879831885f22f..03877bb2c3254 100644 ---- a/chrome/browser/flag_descriptions.cc -+++ b/chrome/browser/flag_descriptions.cc -@@ -288,6 +288,11 @@ const char kEnableBenchmarkingDescription[] = - "after 3 restarts. On the third restart, the flag will appear to be off " - "but the effect is still active."; - -+#if BUILDFLAG(ENABLE_IPFS) -+extern const char kEnableIpfsName[] = "Enable IPFS"; -+extern const char kEnableIpfsDescription[] = "Enable ipfs:// and ipns:// URLs"; -+#endif -+ - const char kPreloadingOnPerformancePageName[] = - "Preloading Settings on Performance Page"; - const char kPreloadingOnPerformancePageDescription[] = diff --git a/chromium_edits/121.0.6167.8/chrome/common/chrome_content_client.cc.patch b/chromium_edits/121.0.6167.8/chrome/common/chrome_content_client.cc.patch deleted file mode 100644 index 5bd09f16..00000000 --- a/chromium_edits/121.0.6167.8/chrome/common/chrome_content_client.cc.patch +++ /dev/null @@ -1,17 +0,0 @@ -diff --git a/chrome/common/chrome_content_client.cc b/chrome/common/chrome_content_client.cc -index 246ec9c5c911f..5d66d133a7907 100644 ---- a/chrome/common/chrome_content_client.cc -+++ b/chrome/common/chrome_content_client.cc -@@ -296,6 +296,12 @@ void ChromeContentClient::AddAdditionalSchemes(Schemes* schemes) { - #if BUILDFLAG(IS_ANDROID) - schemes->local_schemes.push_back(url::kContentScheme); - #endif -+ for ( const char* ip_s : {"ipfs", "ipns"} ) { -+ schemes->standard_schemes.push_back(ip_s); -+ schemes->cors_enabled_schemes.push_back(ip_s); -+ schemes->secure_schemes.push_back(ip_s); -+ schemes->csp_bypassing_schemes.push_back(ip_s); -+ } - } - - std::u16string ChromeContentClient::GetLocalizedString(int message_id) { diff --git a/chromium_edits/121.0.6167.8/components/cbor/reader.cc.patch b/chromium_edits/121.0.6167.8/components/cbor/reader.cc.patch deleted file mode 100644 index aed86452..00000000 --- a/chromium_edits/121.0.6167.8/components/cbor/reader.cc.patch +++ /dev/null @@ -1,44 +0,0 @@ -diff --git a/components/cbor/reader.cc b/components/cbor/reader.cc -index 306ba52fa4944..6b13b3a679a65 100644 ---- a/components/cbor/reader.cc -+++ b/components/cbor/reader.cc -@@ -22,7 +22,7 @@ - namespace cbor { - - namespace constants { --const char kUnsupportedMajorType[] = "Unsupported major type."; -+const char kUnsupportedMajorType[] = "Unsupported major type operation."; - } - - namespace { -@@ -156,7 +156,11 @@ absl::optional Reader::DecodeCompleteDataItem(const Config& config, - case Value::Type::FLOAT_VALUE: - // Floating point values also go here since they are also type 7. - return DecodeToSimpleValueOrFloat(*header, config); -- case Value::Type::TAG: // We explicitly don't support TAG. -+ case Value::Type::TAG: -+ if (config.parse_tags) { -+ return ReadTagContent(*header, config, max_nesting_level); -+ } -+ break; - case Value::Type::NONE: - case Value::Type::INVALID_UTF8: - break; -@@ -347,6 +351,17 @@ absl::optional Reader::ReadByteStringContent( - return Value(std::move(cbor_byte_string)); - } - -+absl::optional Reader::ReadTagContent( -+ const Reader::DataItemHeader& header, -+ const Config& config, -+ int max_nesting_level) { -+ auto tagged_content = DecodeCompleteDataItem(config, max_nesting_level); -+ if (tagged_content.has_value()) { -+ tagged_content.value().SetTag(header.value); -+ } -+ return tagged_content; -+} -+ - absl::optional Reader::ReadArrayContent( - const Reader::DataItemHeader& header, - const Config& config, diff --git a/chromium_edits/121.0.6167.8/components/cbor/reader.h.patch b/chromium_edits/121.0.6167.8/components/cbor/reader.h.patch deleted file mode 100644 index fb821165..00000000 --- a/chromium_edits/121.0.6167.8/components/cbor/reader.h.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/components/cbor/reader.h b/components/cbor/reader.h -index f0b43a5517528..a57e277a1bc66 100644 ---- a/components/cbor/reader.h -+++ b/components/cbor/reader.h -@@ -130,6 +130,11 @@ class CBOR_EXPORT Reader { - // during decoding will set raise the `UNSUPPORTED_FLOATING_POINT_VALUE` - // error. - bool allow_floating_point = false; -+ -+ // If the parser encounters a TAG element, should it be parsed out and -+ // the tag value saved (true), or should the entire node and its content -+ // be discarded (false) -+ bool parse_tags = false; - }; - - Reader(const Reader&) = delete; -@@ -204,6 +209,9 @@ class CBOR_EXPORT Reader { - absl::optional ReadMapContent(const DataItemHeader& header, - const Config& config, - int max_nesting_level); -+ absl::optional ReadTagContent(const DataItemHeader& header, -+ const Config& config, -+ int max_nesting_level); - absl::optional ReadByte(); - absl::optional> ReadBytes(uint64_t num_bytes); - bool IsKeyInOrder(const Value& new_key, diff --git a/chromium_edits/121.0.6167.8/components/cbor/reader_unittest.cc.patch b/chromium_edits/121.0.6167.8/components/cbor/reader_unittest.cc.patch deleted file mode 100644 index 3f657dc3..00000000 --- a/chromium_edits/121.0.6167.8/components/cbor/reader_unittest.cc.patch +++ /dev/null @@ -1,47 +0,0 @@ -diff --git a/components/cbor/reader_unittest.cc b/components/cbor/reader_unittest.cc -index 83d44a48d6dfa..a6ec5299b3241 100644 ---- a/components/cbor/reader_unittest.cc -+++ b/components/cbor/reader_unittest.cc -@@ -1451,5 +1451,42 @@ TEST(CBORReaderTest, AllowInvalidUTF8) { - EXPECT_FALSE(cbor); - EXPECT_EQ(Reader::DecoderError::INVALID_UTF8, error); - } -+TEST(CBORReaderTest, RejectsTagUnderDefaultConfig) { -+ static const uint8_t kTaggedCbor[] = { -+ 0xd8, 0x2a, 0x58, 0x25, 0x00, 0x01, 0x71, 0x12, 0x20, 0x69, 0xea, 0x07, -+ 0x40, 0xf9, 0x80, 0x7a, 0x28, 0xf4, 0xd9, 0x32, 0xc6, 0x2e, 0x7c, 0x1c, -+ 0x83, 0xbe, 0x05, 0x5e, 0x55, 0x07, 0x2c, 0x90, 0x26, 0x6a, 0xb3, 0xe7, -+ 0x9d, 0xf6, 0x3a, 0x36, 0x5b -+ }; -+ Reader::Config config; -+ absl::optional cbor = Reader::Read(kTaggedCbor, config); -+ EXPECT_FALSE(cbor.has_value()); -+} -+TEST(CBORReaderTest, ReadsTagWhenConfiguredToDoSo) { -+ static const uint8_t kTaggedCbor[] = { -+ 0xd8, 0x2a, 0x58, 0x25, 0x00, 0x01, 0x71, 0x12, 0x20, 0x69, 0xea, 0x07, -+ 0x40, 0xf9, 0x80, 0x7a, 0x28, 0xf4, 0xd9, 0x32, 0xc6, 0x2e, 0x7c, 0x1c, -+ 0x83, 0xbe, 0x05, 0x5e, 0x55, 0x07, 0x2c, 0x90, 0x26, 0x6a, 0xb3, 0xe7, -+ 0x9d, 0xf6, 0x3a, 0x36, 0x5b -+ }; -+ Reader::Config config; -+ config.parse_tags = true; -+ absl::optional cbor = Reader::Read(kTaggedCbor, config); -+ EXPECT_TRUE(cbor.has_value()); -+ auto& v = cbor.value(); -+ EXPECT_TRUE(v.has_tag()); -+ EXPECT_EQ(v.GetTag(),42UL); -+ EXPECT_TRUE(v.is_bytestring()); -+ EXPECT_EQ(v.type(), Value::Type::BYTE_STRING); -+ auto& bytes = v.GetBytestring(); -+ EXPECT_EQ(bytes.size(), 37UL); -+ EXPECT_EQ(bytes.at(0), 0x00);//identity multibase (e.g. not base-encoded, bytes are themselves) -+ EXPECT_EQ(bytes.at(1), 0x01);//CID version 1 -+ EXPECT_EQ(bytes.at(2), 0x71);//codec = dag-cbor -+ EXPECT_EQ(bytes.at(3), 0x12);//multihash = 18 = sha2-256 -+ EXPECT_EQ(bytes.at(4), 0x20);//hash length = 32 bytes -+ EXPECT_EQ(bytes.at(5), 0x69);//first byte of hash digest -+ EXPECT_EQ(bytes.at(36),0x5b);//last byte of hash digest -+} - - } // namespace cbor diff --git a/chromium_edits/121.0.6167.8/components/cbor/values.cc.patch b/chromium_edits/121.0.6167.8/components/cbor/values.cc.patch deleted file mode 100644 index ddbab2b3..00000000 --- a/chromium_edits/121.0.6167.8/components/cbor/values.cc.patch +++ /dev/null @@ -1,145 +0,0 @@ -diff --git a/components/cbor/values.cc b/components/cbor/values.cc -index 02498209c820e..34055aef24cfe 100644 ---- a/components/cbor/values.cc -+++ b/components/cbor/values.cc -@@ -66,32 +66,34 @@ Value::Value(Type type) : type_(type) { - NOTREACHED(); - } - --Value::Value(SimpleValue in_simple) -- : type_(Type::SIMPLE_VALUE), simple_value_(in_simple) { -+Value::Value(SimpleValue in_simple, uint64_t tag) -+ : type_(Type::SIMPLE_VALUE), simple_value_(in_simple), tag_(tag) { - CHECK(static_cast(in_simple) >= 20 && static_cast(in_simple) <= 23); - } - --Value::Value(bool boolean_value) : type_(Type::SIMPLE_VALUE) { -+Value::Value(bool boolean_value, uint64_t tag) : type_(Type::SIMPLE_VALUE), tag_(tag) { - simple_value_ = boolean_value ? Value::SimpleValue::TRUE_VALUE - : Value::SimpleValue::FALSE_VALUE; - } - --Value::Value(double float_value) -- : type_(Type::FLOAT_VALUE), float_value_(float_value) {} -+Value::Value(double float_value, uint64_t tag) -+ : type_(Type::FLOAT_VALUE), float_value_(float_value), tag_(tag) {} - --Value::Value(int integer_value) -- : Value(base::checked_cast(integer_value)) {} -+Value::Value(int integer_value, uint64_t tag) -+ : Value(base::checked_cast(integer_value), tag) {} - --Value::Value(int64_t integer_value) : integer_value_(integer_value) { -+Value::Value(int64_t integer_value, uint64_t tag) : integer_value_(integer_value), tag_(tag) { - type_ = integer_value >= 0 ? Type::UNSIGNED : Type::NEGATIVE; - } - --Value::Value(base::span in_bytes) -+Value::Value(base::span in_bytes, uint64_t tag) - : type_(Type::BYTE_STRING), -- bytestring_value_(in_bytes.begin(), in_bytes.end()) {} -+ bytestring_value_(in_bytes.begin(), in_bytes.end()), -+ tag_(tag) -+ {} - --Value::Value(base::span in_bytes, Type type) -- : type_(type), bytestring_value_(in_bytes.begin(), in_bytes.end()) { -+Value::Value(base::span in_bytes, Type type, uint64_t tag) -+ : type_(type), bytestring_value_(in_bytes.begin(), in_bytes.end()), tag_(tag) { - DCHECK(type_ == Type::BYTE_STRING || type_ == Type::INVALID_UTF8); - } - -@@ -117,7 +119,8 @@ Value::Value(std::string&& in_string, Type type) noexcept : type_(type) { - } - } - --Value::Value(base::StringPiece in_string, Type type) : type_(type) { -+Value::Value(base::StringPiece in_string, Type type, uint64_t tag) -+: type_(type), tag_(tag) { - switch (type_) { - case Type::STRING: - new (&string_value_) std::string(); -@@ -133,16 +136,18 @@ Value::Value(base::StringPiece in_string, Type type) : type_(type) { - } - } - --Value::Value(const ArrayValue& in_array) : type_(Type::ARRAY), array_value_() { -+Value::Value(const ArrayValue& in_array, uint64_t tag) -+: type_(Type::ARRAY), array_value_(), tag_(tag) { - array_value_.reserve(in_array.size()); - for (const auto& val : in_array) - array_value_.emplace_back(val.Clone()); - } - --Value::Value(ArrayValue&& in_array) noexcept -- : type_(Type::ARRAY), array_value_(std::move(in_array)) {} -+Value::Value(ArrayValue&& in_array, uint64_t tag) noexcept -+ : type_(Type::ARRAY), array_value_(std::move(in_array)), tag_(tag) {} - --Value::Value(const MapValue& in_map) : type_(Type::MAP), map_value_() { -+Value::Value(const MapValue& in_map, uint64_t tag) -+: type_(Type::MAP), map_value_(), tag_(tag) { - map_value_.reserve(in_map.size()); - for (const auto& it : in_map) - map_value_.emplace_hint(map_value_.end(), it.first.Clone(), -@@ -168,31 +173,36 @@ Value Value::Clone() const { - case Type::NONE: - return Value(); - case Type::INVALID_UTF8: -- return Value(bytestring_value_, Type::INVALID_UTF8); -+ return Value(bytestring_value_, Type::INVALID_UTF8, tag_); - case Type::UNSIGNED: - case Type::NEGATIVE: -- return Value(integer_value_); -+ return Value(integer_value_, tag_); - case Type::BYTE_STRING: -- return Value(bytestring_value_); -+ return Value(bytestring_value_, tag_); - case Type::STRING: -- return Value(string_value_); -+ return Value(string_value_, Type::STRING, tag_); - case Type::ARRAY: -- return Value(array_value_); -+ return Value(array_value_, tag_); - case Type::MAP: -- return Value(map_value_); -+ return Value(map_value_, tag_); - case Type::TAG: - NOTREACHED() << constants::kUnsupportedMajorType; - return Value(); - case Type::SIMPLE_VALUE: -- return Value(simple_value_); -+ return Value(simple_value_, tag_); - case Type::FLOAT_VALUE: -- return Value(float_value_); -+ return Value(float_value_, tag_); - } - - NOTREACHED(); - return Value(); - } - -+Value& Value::SetTag(uint64_t tag) noexcept { -+ tag_ = tag; -+ return *this; -+} -+ - Value::SimpleValue Value::GetSimpleValue() const { - CHECK(is_simple()); - return simple_value_; -@@ -258,9 +268,14 @@ const Value::BinaryValue& Value::GetInvalidUTF8() const { - return bytestring_value_; - } - -+uint64_t Value::GetTag() const { -+ CHECK(has_tag()); -+ return tag_; -+} -+ - void Value::InternalMoveConstructFrom(Value&& that) { - type_ = that.type_; -- -+ tag_ = that.tag_; - switch (type_) { - case Type::UNSIGNED: - case Type::NEGATIVE: diff --git a/chromium_edits/121.0.6167.8/components/cbor/values.h.patch b/chromium_edits/121.0.6167.8/components/cbor/values.h.patch deleted file mode 100644 index ca39df01..00000000 --- a/chromium_edits/121.0.6167.8/components/cbor/values.h.patch +++ /dev/null @@ -1,79 +0,0 @@ -diff --git a/components/cbor/values.h b/components/cbor/values.h -index d81ef5607c55a..10216a8dcdc57 100644 ---- a/components/cbor/values.h -+++ b/components/cbor/values.h -@@ -127,28 +127,29 @@ class CBOR_EXPORT Value { - - explicit Value(Type type); - -- explicit Value(SimpleValue in_simple); -- explicit Value(bool boolean_value); -- explicit Value(double in_float); -+ explicit Value(SimpleValue in_simple, uint64_t tag = NO_TAG); -+ explicit Value(bool boolean_value, uint64_t tag = NO_TAG); -+ explicit Value(double in_float, uint64_t tag = NO_TAG); - -- explicit Value(int integer_value); -- explicit Value(int64_t integer_value); -+ explicit Value(int integer_value, uint64_t tag = NO_TAG); -+ explicit Value(int64_t integer_value, uint64_t tag = NO_TAG); - explicit Value(uint64_t integer_value) = delete; - -- explicit Value(base::span in_bytes); -+ explicit Value(base::span in_bytes, uint64_t tag = NO_TAG); - explicit Value(BinaryValue&& in_bytes) noexcept; - - explicit Value(const char* in_string, Type type = Type::STRING); - explicit Value(std::string&& in_string, Type type = Type::STRING) noexcept; -- explicit Value(base::StringPiece in_string, Type type = Type::STRING); -+ explicit Value(base::StringPiece in_string, Type type = Type::STRING, uint64_t tag = NO_TAG); - -- explicit Value(const ArrayValue& in_array); -- explicit Value(ArrayValue&& in_array) noexcept; -+ explicit Value(const ArrayValue& in_array, uint64_t tag = NO_TAG); -+ explicit Value(ArrayValue&& in_array, uint64_t tag = NO_TAG) noexcept; - -- explicit Value(const MapValue& in_map); -+ explicit Value(const MapValue& in_map, uint64_t tag = NO_TAG); - explicit Value(MapValue&& in_map) noexcept; - - Value& operator=(Value&& that) noexcept; -+ Value& SetTag(uint64_t) noexcept; - - Value(const Value&) = delete; - Value& operator=(const Value&) = delete; -@@ -179,6 +180,7 @@ class CBOR_EXPORT Value { - bool is_string() const { return type() == Type::STRING; } - bool is_array() const { return type() == Type::ARRAY; } - bool is_map() const { return type() == Type::MAP; } -+ bool has_tag() const { return tag_ != NO_TAG; } - - // These will all fatally assert if the type doesn't match. - SimpleValue GetSimpleValue() const; -@@ -194,12 +196,13 @@ class CBOR_EXPORT Value { - const ArrayValue& GetArray() const; - const MapValue& GetMap() const; - const BinaryValue& GetInvalidUTF8() const; -+ uint64_t GetTag() const; - - private: - friend class Reader; - // This constructor allows INVALID_UTF8 values to be created, which only - // |Reader| and InvalidUTF8StringValueForTesting() may do. -- Value(base::span in_bytes, Type type); -+ Value(base::span in_bytes, Type type, uint64_t tag = NO_TAG); - - Type type_; - -@@ -213,6 +216,11 @@ class CBOR_EXPORT Value { - MapValue map_value_; - }; - -+ //This value specified as Invalid, -+ // used here to represent absence of TAG -+ constexpr static uint64_t NO_TAG = 0xFFFF; -+ uint64_t tag_ = NO_TAG; -+ - void InternalMoveConstructFrom(Value&& that); - void InternalCleanup(); - }; diff --git a/chromium_edits/121.0.6167.8/components/cbor/writer.cc.patch b/chromium_edits/121.0.6167.8/components/cbor/writer.cc.patch deleted file mode 100644 index 21fe28ce..00000000 --- a/chromium_edits/121.0.6167.8/components/cbor/writer.cc.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/components/cbor/writer.cc b/components/cbor/writer.cc -index bb22754d36a07..aae4027836377 100644 ---- a/components/cbor/writer.cc -+++ b/components/cbor/writer.cc -@@ -47,6 +47,9 @@ bool Writer::EncodeCBOR(const Value& node, - if (max_nesting_level < 0) - return false; - -+ if (node.has_tag()) { -+ StartItem(Value::Type::TAG, node.GetTag()); -+ } - switch (node.type()) { - case Value::Type::NONE: { - StartItem(Value::Type::BYTE_STRING, 0); diff --git a/chromium_edits/121.0.6167.8/components/cbor/writer_unittest.cc.patch b/chromium_edits/121.0.6167.8/components/cbor/writer_unittest.cc.patch deleted file mode 100644 index 240fee83..00000000 --- a/chromium_edits/121.0.6167.8/components/cbor/writer_unittest.cc.patch +++ /dev/null @@ -1,36 +0,0 @@ -diff --git a/components/cbor/writer_unittest.cc b/components/cbor/writer_unittest.cc -index e3bffe20734bc..0ed569ae164a0 100644 ---- a/components/cbor/writer_unittest.cc -+++ b/components/cbor/writer_unittest.cc -@@ -522,4 +522,31 @@ TEST(CBORWriterTest, OverlyNestedCBOR) { - EXPECT_FALSE(Writer::Write(Value(map), 4).has_value()); - } - -+TEST(CBORWriterTest, CanWriteTag) { -+ std::array content{ -+ 0x00, 0x01, 0x71, 0x12, 0x20, -+ 0x69, 0xea, 0x07, 0x40, 0xf9, -+ 0x80, 0x7a, 0x28, 0xf4, 0xd9, -+ 0x32, 0xc6, 0x2e, 0x7c, 0x1c, -+ 0x83, 0xbe, 0x05, 0x5e, 0x55, -+ 0x07, 0x2c, 0x90, 0x26, 0x6a, -+ 0xb3, 0xe7, 0x9d, 0xf6, 0x3a, -+ 0x36, 0x5b -+ }; -+ Value to_write(content); -+ to_write.SetTag(42); -+ auto result = Writer::Write(to_write); -+ EXPECT_TRUE(result.has_value()); -+ auto& bytes = result.value(); -+ EXPECT_EQ(bytes.size(), 41UL); -+ EXPECT_EQ(bytes.at(0), 0xd8); -+ EXPECT_EQ(bytes.at(1), 0x2a); -+ EXPECT_EQ(bytes.at(2), 0x58); -+ EXPECT_EQ(bytes.at(3), 0x25); -+ for (auto i = 0UL; i < content.size(); ++i) { -+ ASSERT_LT(i + 4UL, bytes.size()); -+ ASSERT_EQ(content.at(i), bytes.at(i+4UL)); -+ } -+} -+ - } // namespace cbor diff --git a/chromium_edits/121.0.6167.8/components/open_from_clipboard/clipboard_recent_content_generic.cc.patch b/chromium_edits/121.0.6167.8/components/open_from_clipboard/clipboard_recent_content_generic.cc.patch deleted file mode 100644 index 891b53df..00000000 --- a/chromium_edits/121.0.6167.8/components/open_from_clipboard/clipboard_recent_content_generic.cc.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/components/open_from_clipboard/clipboard_recent_content_generic.cc b/components/open_from_clipboard/clipboard_recent_content_generic.cc -index 4dcafecbc66c6..d205209c08162 100644 ---- a/components/open_from_clipboard/clipboard_recent_content_generic.cc -+++ b/components/open_from_clipboard/clipboard_recent_content_generic.cc -@@ -20,7 +20,7 @@ - namespace { - // Schemes appropriate for suggestion by ClipboardRecentContent. - const char* kAuthorizedSchemes[] = { -- url::kAboutScheme, url::kDataScheme, url::kHttpScheme, url::kHttpsScheme, -+ url::kAboutScheme, url::kDataScheme, url::kHttpScheme, url::kHttpsScheme, "ipfs", "ipns" - // TODO(mpearson): add support for chrome:// URLs. Right now the scheme - // for that lives in content and is accessible via - // GetEmbedderRepresentationOfAboutScheme() or content::kChromeUIScheme diff --git a/chromium_edits/121.0.6167.8/net/dns/dns_config_service_linux.cc.patch b/chromium_edits/121.0.6167.8/net/dns/dns_config_service_linux.cc.patch deleted file mode 100644 index a5e9863f..00000000 --- a/chromium_edits/121.0.6167.8/net/dns/dns_config_service_linux.cc.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff --git a/net/dns/dns_config_service_linux.cc b/net/dns/dns_config_service_linux.cc -index 5273da5190277..12b28b86a4c00 100644 ---- a/net/dns/dns_config_service_linux.cc -+++ b/net/dns/dns_config_service_linux.cc -@@ -272,11 +272,11 @@ bool IsNsswitchConfigCompatible( - // Ignore any entries after `kDns` because Chrome will fallback to the - // system resolver if a result was not found in DNS. - return true; -- -+ case NsswitchReader::Service::kResolve: -+ break; - case NsswitchReader::Service::kMdns: - case NsswitchReader::Service::kMdns4: - case NsswitchReader::Service::kMdns6: -- case NsswitchReader::Service::kResolve: - case NsswitchReader::Service::kNis: - RecordIncompatibleNsswitchReason( - IncompatibleNsswitchReason::kIncompatibleService, diff --git a/chromium_edits/121.0.6167.8/third_party/blink/renderer/platform/weborigin/scheme_registry.cc.patch b/chromium_edits/121.0.6167.8/third_party/blink/renderer/platform/weborigin/scheme_registry.cc.patch deleted file mode 100644 index 119d72b2..00000000 --- a/chromium_edits/121.0.6167.8/third_party/blink/renderer/platform/weborigin/scheme_registry.cc.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/third_party/blink/renderer/platform/weborigin/scheme_registry.cc b/third_party/blink/renderer/platform/weborigin/scheme_registry.cc -index 4eadf46ea0c24..d62fc7fb14e01 100644 ---- a/third_party/blink/renderer/platform/weborigin/scheme_registry.cc -+++ b/third_party/blink/renderer/platform/weborigin/scheme_registry.cc -@@ -67,7 +67,7 @@ class URLSchemesRegistry final { - // is considered secure. Additional checks are performed to ensure that - // other http pages are filtered out. - service_worker_schemes({"http", "https"}), -- fetch_api_schemes({"http", "https"}), -+ fetch_api_schemes({"http", "https", "ipfs", "ipns"}), - allowed_in_referrer_schemes({"http", "https"}) { - for (auto& scheme : url::GetCorsEnabledSchemes()) - cors_enabled_schemes.insert(scheme.c_str()); diff --git a/chromium_edits/121.0.6167.8/url/url_util.cc.patch b/chromium_edits/121.0.6167.8/url/url_util.cc.patch deleted file mode 100644 index 0332e847..00000000 --- a/chromium_edits/121.0.6167.8/url/url_util.cc.patch +++ /dev/null @@ -1,17 +0,0 @@ -diff --git a/url/url_util.cc b/url/url_util.cc -index 9258cfcfada47..daf10e4c3b741 100644 ---- a/url/url_util.cc -+++ b/url/url_util.cc -@@ -277,6 +277,12 @@ bool DoCanonicalize(const CHAR* spec, - charset_converter, output, - output_parsed); - -+ } else if (DoCompareSchemeComponent(spec, scheme, "ipfs")) { -+ // Switch multibase away from case-sensitive ones before continuing canonicalization. -+ ParseStandardURL(spec, spec_len, &parsed_input); -+ success = CanonicalizeIpfsURL(spec, spec_len, parsed_input, scheme_type, -+ charset_converter, output, output_parsed); -+ - } else if (DoIsStandard(spec, scheme, &scheme_type)) { - // All "normal" URLs. - ParseStandardURL(spec, spec_len, &parsed_input); diff --git a/chromium_edits/121.0.6147.0/chrome/browser/BUILD.gn.patch b/chromium_edits/122.0.6226.2/chrome/browser/BUILD.gn.patch similarity index 72% rename from chromium_edits/121.0.6147.0/chrome/browser/BUILD.gn.patch rename to chromium_edits/122.0.6226.2/chrome/browser/BUILD.gn.patch index c04a0748..289c0b9c 100644 --- a/chromium_edits/121.0.6147.0/chrome/browser/BUILD.gn.patch +++ b/chromium_edits/122.0.6226.2/chrome/browser/BUILD.gn.patch @@ -1,8 +1,8 @@ diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn -index 8704f445741b4..2c0cae5d77b6d 100644 +index 516f8d3bc275f..3188eaaf003a1 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn -@@ -40,6 +40,7 @@ import("//rlz/buildflags/buildflags.gni") +@@ -39,6 +39,7 @@ import("//rlz/buildflags/buildflags.gni") import("//sandbox/features.gni") import("//testing/libfuzzer/fuzzer_test.gni") import("//third_party/blink/public/public_features.gni") @@ -10,11 +10,15 @@ index 8704f445741b4..2c0cae5d77b6d 100644 import("//third_party/protobuf/proto_library.gni") import("//third_party/webrtc/webrtc.gni") import("//third_party/widevine/cdm/widevine.gni") -@@ -2634,6 +2635,10 @@ static_library("browser") { +@@ -2618,6 +2619,14 @@ static_library("browser") { ] } + if (enable_ipfs) { ++ sources += [ ++ "ipfs_extra_parts.cc", ++ "ipfs_extra_parts.h", ++ ] + deps += [ "//components/ipfs" ] + } + diff --git a/chromium_edits/121.0.6156.3/chrome/browser/about_flags.cc.patch b/chromium_edits/122.0.6226.2/chrome/browser/about_flags.cc.patch similarity index 88% rename from chromium_edits/121.0.6156.3/chrome/browser/about_flags.cc.patch rename to chromium_edits/122.0.6226.2/chrome/browser/about_flags.cc.patch index a5bf782d..004c6748 100644 --- a/chromium_edits/121.0.6156.3/chrome/browser/about_flags.cc.patch +++ b/chromium_edits/122.0.6226.2/chrome/browser/about_flags.cc.patch @@ -1,16 +1,16 @@ diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc -index af66a784e629e..36d2a2de39e38 100644 +index cba0c0e2bc1c2..91e14f34800de 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc -@@ -209,6 +209,7 @@ +@@ -214,6 +214,7 @@ #include "third_party/blink/public/common/features_generated.h" #include "third_party/blink/public/common/forcedark/forcedark_switches.h" #include "third_party/blink/public/common/switches.h" +#include "third_party/ipfs_client/ipfs_buildflags.h" #include "ui/accessibility/accessibility_features.h" #include "ui/accessibility/accessibility_switches.h" - #include "ui/base/ui_base_features.h" -@@ -309,6 +310,10 @@ + #include "ui/base/ozone_buildflags.h" +@@ -310,6 +311,10 @@ #include "extensions/common/switches.h" #endif // BUILDFLAG(ENABLE_EXTENSIONS) @@ -21,7 +21,7 @@ index af66a784e629e..36d2a2de39e38 100644 #if BUILDFLAG(ENABLE_PDF) #include "pdf/pdf_features.h" #endif -@@ -9855,6 +9860,14 @@ const FeatureEntry kFeatureEntries[] = { +@@ -9379,6 +9384,14 @@ const FeatureEntry kFeatureEntries[] = { flag_descriptions::kOmitCorsClientCertDescription, kOsAll, FEATURE_VALUE_TYPE(network::features::kOmitCorsClientCert)}, diff --git a/chromium_edits/121.0.6147.0/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc.patch b/chromium_edits/122.0.6226.2/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc.patch similarity index 100% rename from chromium_edits/121.0.6147.0/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc.patch rename to chromium_edits/122.0.6226.2/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc.patch diff --git a/chromium_edits/121.0.6147.0/chrome/browser/chrome_content_browser_client.cc.patch b/chromium_edits/122.0.6226.2/chrome/browser/chrome_content_browser_client.cc.patch similarity index 75% rename from chromium_edits/121.0.6147.0/chrome/browser/chrome_content_browser_client.cc.patch rename to chromium_edits/122.0.6226.2/chrome/browser/chrome_content_browser_client.cc.patch index 8e038617..f6f88ae7 100644 --- a/chromium_edits/121.0.6147.0/chrome/browser/chrome_content_browser_client.cc.patch +++ b/chromium_edits/122.0.6226.2/chrome/browser/chrome_content_browser_client.cc.patch @@ -1,8 +1,8 @@ diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc -index 69237f32680f9..be754dca72ffb 100644 +index 23ee785c34a30..8550b43c61f2f 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc -@@ -374,6 +374,7 @@ +@@ -376,6 +376,7 @@ #include "third_party/blink/public/common/switches.h" #include "third_party/blink/public/mojom/browsing_topics/browsing_topics.mojom.h" #include "third_party/blink/public/public_buildflags.h" @@ -10,11 +10,12 @@ index 69237f32680f9..be754dca72ffb 100644 #include "third_party/widevine/cdm/buildflags.h" #include "ui/base/clipboard/clipboard_format_type.h" #include "ui/base/l10n/l10n_util.h" -@@ -496,6 +497,12 @@ +@@ -499,6 +500,13 @@ #include "chrome/browser/fuchsia/chrome_browser_main_parts_fuchsia.h" #endif +#if BUILDFLAG(ENABLE_IPFS) ++#include "chrome/browser/ipfs_extra_parts.h" +#include "components/ipfs/interceptor.h" +#include "components/ipfs/ipfs_features.h" +#include "components/ipfs/url_loader_factory.h" @@ -23,7 +24,19 @@ index 69237f32680f9..be754dca72ffb 100644 #if BUILDFLAG(IS_CHROMEOS) #include "base/debug/leak_annotations.h" #include "chrome/browser/apps/app_service/app_install/app_install_navigation_throttle.h" -@@ -6130,12 +6137,23 @@ void ChromeContentBrowserClient:: +@@ -1711,6 +1719,11 @@ ChromeContentBrowserClient::CreateBrowserMainParts(bool is_integration_test) { + main_parts->AddParts( + std::make_unique()); + ++#if BUILDFLAG(ENABLE_IPFS) ++ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { ++ main_parts->AddParts(std::make_unique()); ++ } ++#endif + return main_parts; + } + +@@ -6057,12 +6070,25 @@ void ChromeContentBrowserClient:: const absl::optional& request_initiator_origin, NonNetworkURLLoaderFactoryMap* factories) { #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(ENABLE_EXTENSIONS) || \ @@ -33,23 +46,24 @@ index 69237f32680f9..be754dca72ffb 100644 RenderFrameHost::FromID(render_process_id, render_frame_id); WebContents* web_contents = WebContents::FromRenderFrameHost(frame_host); #endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(ENABLE_EXTENSIONS) || \ -- // !BUILDFLAG(IS_ANDROID) -+ // !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_IPFS) + // !BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(ENABLE_IPFS) + if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { + network::mojom::URLLoaderFactory* default_factory = g_browser_process->system_network_context_manager()->GetURLLoaderFactory(); ++ auto* context = web_contents->GetBrowserContext(); + ipfs::IpfsURLLoaderFactory::Create( + factories, -+ web_contents->GetBrowserContext(), ++ context, + default_factory, -+ GetSystemNetworkContext() ++ GetSystemNetworkContext(), ++ Profile::FromBrowserContext(context)->GetPrefs() + ); + } +#endif // BUILDFLAG(ENABLE_IPFS) #if BUILDFLAG(IS_CHROMEOS_ASH) if (web_contents) { -@@ -6277,6 +6295,11 @@ ChromeContentBrowserClient::WillCreateURLLoaderRequestInterceptors( +@@ -6204,6 +6230,11 @@ ChromeContentBrowserClient::WillCreateURLLoaderRequestInterceptors( scoped_refptr navigation_response_task_runner) { std::vector> interceptors; diff --git a/chromium_edits/121.0.6156.3/chrome/browser/flag-metadata.json.patch b/chromium_edits/122.0.6226.2/chrome/browser/flag-metadata.json.patch similarity index 88% rename from chromium_edits/121.0.6156.3/chrome/browser/flag-metadata.json.patch rename to chromium_edits/122.0.6226.2/chrome/browser/flag-metadata.json.patch index 16bf283e..8ad099bd 100644 --- a/chromium_edits/121.0.6156.3/chrome/browser/flag-metadata.json.patch +++ b/chromium_edits/122.0.6226.2/chrome/browser/flag-metadata.json.patch @@ -1,8 +1,8 @@ diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json -index 4c152d2b20941..d348a33e0c1b5 100644 +index ae98bbb22d81a..c85d48a4a77c1 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json -@@ -2922,6 +2922,11 @@ +@@ -2946,6 +2946,11 @@ "owners": [ "hanxi@chromium.org", "wychen@chromium.org" ], "expiry_milestone": 130 }, diff --git a/chromium_edits/121.0.6156.3/chrome/browser/flag_descriptions.cc.patch b/chromium_edits/122.0.6226.2/chrome/browser/flag_descriptions.cc.patch similarity index 94% rename from chromium_edits/121.0.6156.3/chrome/browser/flag_descriptions.cc.patch rename to chromium_edits/122.0.6226.2/chrome/browser/flag_descriptions.cc.patch index 3ef7683d..2cd5dcc0 100644 --- a/chromium_edits/121.0.6156.3/chrome/browser/flag_descriptions.cc.patch +++ b/chromium_edits/122.0.6226.2/chrome/browser/flag_descriptions.cc.patch @@ -1,5 +1,5 @@ diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc -index 9f35b91158262..0dc974246fe82 100644 +index ec5799f66a720..7be72282040c1 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc @@ -284,6 +284,11 @@ const char kEnableBenchmarkingDescription[] = diff --git a/chromium_edits/121.0.6167.8/chrome/browser/flag_descriptions.h.patch b/chromium_edits/122.0.6226.2/chrome/browser/flag_descriptions.h.patch similarity index 89% rename from chromium_edits/121.0.6167.8/chrome/browser/flag_descriptions.h.patch rename to chromium_edits/122.0.6226.2/chrome/browser/flag_descriptions.h.patch index 7b22a391..18c0b4cc 100644 --- a/chromium_edits/121.0.6167.8/chrome/browser/flag_descriptions.h.patch +++ b/chromium_edits/122.0.6226.2/chrome/browser/flag_descriptions.h.patch @@ -1,5 +1,5 @@ diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h -index 067e8d3c0b793..95ceadb06622a 100644 +index 3224ecf17e6c8..14976704c1345 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h @@ -23,6 +23,7 @@ @@ -10,7 +10,7 @@ index 067e8d3c0b793..95ceadb06622a 100644 // This file declares strings used in chrome://flags. These messages are not // translated, because instead of end-users they target Chromium developers and -@@ -179,6 +180,11 @@ extern const char kDownloadWarningImprovementsDescription[]; +@@ -176,6 +177,11 @@ extern const char kDownloadWarningImprovementsDescription[]; extern const char kEnableBenchmarkingName[]; extern const char kEnableBenchmarkingDescription[]; diff --git a/chromium_edits/122.0.6226.2/chrome/browser/ipfs_extra_parts.cc b/chromium_edits/122.0.6226.2/chrome/browser/ipfs_extra_parts.cc new file mode 100644 index 00000000..90d2596f --- /dev/null +++ b/chromium_edits/122.0.6226.2/chrome/browser/ipfs_extra_parts.cc @@ -0,0 +1,10 @@ +#include "ipfs_extra_parts.h" + +#include "profiles/profile.h" + +#include + +void IpfsExtraParts::PostProfileInit(Profile* profile, bool /* is_initial_profile */ ) { + DCHECK(profile); + ipfs::InterRequestState::CreateForBrowserContext(profile, profile->GetPrefs()); +} diff --git a/chromium_edits/122.0.6226.2/chrome/browser/ipfs_extra_parts.h b/chromium_edits/122.0.6226.2/chrome/browser/ipfs_extra_parts.h new file mode 100644 index 00000000..2059c437 --- /dev/null +++ b/chromium_edits/122.0.6226.2/chrome/browser/ipfs_extra_parts.h @@ -0,0 +1,10 @@ +#ifndef IPFS_EXTRA_PART_H_ +#define IPFS_EXTRA_PART_H_ + +#include + +class IpfsExtraParts : public ChromeBrowserMainExtraParts { + void PostProfileInit(Profile* profile, bool is_initial_profile) override; +}; + +#endif // IPFS_EXTRA_PART_H_ diff --git a/chromium_edits/122.0.6226.2/chrome/browser/prefs/browser_prefs.cc.patch b/chromium_edits/122.0.6226.2/chrome/browser/prefs/browser_prefs.cc.patch new file mode 100644 index 00000000..08688d58 --- /dev/null +++ b/chromium_edits/122.0.6226.2/chrome/browser/prefs/browser_prefs.cc.patch @@ -0,0 +1,36 @@ +diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc +index e4e82350ade4a..7023d6c5e07c3 100644 +--- a/chrome/browser/prefs/browser_prefs.cc ++++ b/chrome/browser/prefs/browser_prefs.cc +@@ -189,6 +189,7 @@ + #include "printing/buildflags/buildflags.h" + #include "rlz/buildflags/buildflags.h" + #include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/ipfs_client/ipfs_buildflags.h" + + #if BUILDFLAG(ENABLE_BACKGROUND_MODE) + #include "chrome/browser/background/background_mode_manager.h" +@@ -233,6 +234,11 @@ + #include "chrome/browser/pdf/pdf_pref_names.h" + #endif // BUILDFLAG(ENABLE_PDF) + ++#if BUILDFLAG(ENABLE_IPFS) ++#include "components/ipfs/ipfs_features.h" ++#include "components/ipfs/preferences.h" ++#endif ++ + #if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) + #include "chrome/browser/screen_ai/pref_names.h" + #endif +@@ -1685,6 +1691,11 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry, + IncognitoModePrefs::RegisterProfilePrefs(registry); + invalidation::PerUserTopicSubscriptionManager::RegisterProfilePrefs(registry); + invalidation::InvalidatorRegistrarWithMemory::RegisterProfilePrefs(registry); ++#if BUILDFLAG(ENABLE_IPFS) ++ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { ++ ipfs::RegisterPreferences(registry); ++ } ++#endif + language::LanguagePrefs::RegisterProfilePrefs(registry); + login_detection::prefs::RegisterProfilePrefs(registry); + lookalikes::RegisterProfilePrefs(registry); diff --git a/chromium_edits/121.0.6147.0/chrome/common/chrome_content_client.cc.patch b/chromium_edits/122.0.6226.2/chrome/common/chrome_content_client.cc.patch similarity index 100% rename from chromium_edits/121.0.6147.0/chrome/common/chrome_content_client.cc.patch rename to chromium_edits/122.0.6226.2/chrome/common/chrome_content_client.cc.patch diff --git a/chromium_edits/121.0.6147.0/components/cbor/reader.cc.patch b/chromium_edits/122.0.6226.2/components/cbor/reader.cc.patch similarity index 100% rename from chromium_edits/121.0.6147.0/components/cbor/reader.cc.patch rename to chromium_edits/122.0.6226.2/components/cbor/reader.cc.patch diff --git a/chromium_edits/121.0.6147.0/components/cbor/reader.h.patch b/chromium_edits/122.0.6226.2/components/cbor/reader.h.patch similarity index 100% rename from chromium_edits/121.0.6147.0/components/cbor/reader.h.patch rename to chromium_edits/122.0.6226.2/components/cbor/reader.h.patch diff --git a/chromium_edits/121.0.6147.0/components/cbor/reader_unittest.cc.patch b/chromium_edits/122.0.6226.2/components/cbor/reader_unittest.cc.patch similarity index 100% rename from chromium_edits/121.0.6147.0/components/cbor/reader_unittest.cc.patch rename to chromium_edits/122.0.6226.2/components/cbor/reader_unittest.cc.patch diff --git a/chromium_edits/121.0.6147.0/components/cbor/values.cc.patch b/chromium_edits/122.0.6226.2/components/cbor/values.cc.patch similarity index 100% rename from chromium_edits/121.0.6147.0/components/cbor/values.cc.patch rename to chromium_edits/122.0.6226.2/components/cbor/values.cc.patch diff --git a/chromium_edits/121.0.6147.0/components/cbor/values.h.patch b/chromium_edits/122.0.6226.2/components/cbor/values.h.patch similarity index 100% rename from chromium_edits/121.0.6147.0/components/cbor/values.h.patch rename to chromium_edits/122.0.6226.2/components/cbor/values.h.patch diff --git a/chromium_edits/121.0.6147.0/components/cbor/writer.cc.patch b/chromium_edits/122.0.6226.2/components/cbor/writer.cc.patch similarity index 100% rename from chromium_edits/121.0.6147.0/components/cbor/writer.cc.patch rename to chromium_edits/122.0.6226.2/components/cbor/writer.cc.patch diff --git a/chromium_edits/121.0.6147.0/components/cbor/writer_unittest.cc.patch b/chromium_edits/122.0.6226.2/components/cbor/writer_unittest.cc.patch similarity index 100% rename from chromium_edits/121.0.6147.0/components/cbor/writer_unittest.cc.patch rename to chromium_edits/122.0.6226.2/components/cbor/writer_unittest.cc.patch diff --git a/chromium_edits/121.0.6147.0/components/open_from_clipboard/clipboard_recent_content_generic.cc.patch b/chromium_edits/122.0.6226.2/components/open_from_clipboard/clipboard_recent_content_generic.cc.patch similarity index 100% rename from chromium_edits/121.0.6147.0/components/open_from_clipboard/clipboard_recent_content_generic.cc.patch rename to chromium_edits/122.0.6226.2/components/open_from_clipboard/clipboard_recent_content_generic.cc.patch diff --git a/chromium_edits/121.0.6147.0/net/dns/dns_config_service_linux.cc.patch b/chromium_edits/122.0.6226.2/net/dns/dns_config_service_linux.cc.patch similarity index 100% rename from chromium_edits/121.0.6147.0/net/dns/dns_config_service_linux.cc.patch rename to chromium_edits/122.0.6226.2/net/dns/dns_config_service_linux.cc.patch diff --git a/chromium_edits/121.0.6147.0/third_party/blink/renderer/platform/weborigin/scheme_registry.cc.patch b/chromium_edits/122.0.6226.2/third_party/blink/renderer/platform/weborigin/scheme_registry.cc.patch similarity index 100% rename from chromium_edits/121.0.6147.0/third_party/blink/renderer/platform/weborigin/scheme_registry.cc.patch rename to chromium_edits/122.0.6226.2/third_party/blink/renderer/platform/weborigin/scheme_registry.cc.patch diff --git a/chromium_edits/121.0.6167.8/url/BUILD.gn.patch b/chromium_edits/122.0.6226.2/url/BUILD.gn.patch similarity index 87% rename from chromium_edits/121.0.6167.8/url/BUILD.gn.patch rename to chromium_edits/122.0.6226.2/url/BUILD.gn.patch index 63fb8f8b..cc358ad4 100644 --- a/chromium_edits/121.0.6167.8/url/BUILD.gn.patch +++ b/chromium_edits/122.0.6226.2/url/BUILD.gn.patch @@ -1,5 +1,5 @@ diff --git a/url/BUILD.gn b/url/BUILD.gn -index c525c166979d6..ce2b1ae43c0a7 100644 +index b5edb89f7698f..d299856674d7d 100644 --- a/url/BUILD.gn +++ b/url/BUILD.gn @@ -5,6 +5,7 @@ @@ -10,7 +10,7 @@ index c525c166979d6..ce2b1ae43c0a7 100644 import("features.gni") import("//build/config/cronet/config.gni") -@@ -67,6 +68,7 @@ component("url") { +@@ -68,6 +69,7 @@ component("url") { public_deps = [ "//base", "//build:robolectric_buildflags", @@ -18,7 +18,7 @@ index c525c166979d6..ce2b1ae43c0a7 100644 ] configs += [ "//build/config/compiler:wexit_time_destructors" ] -@@ -89,6 +91,11 @@ component("url") { +@@ -90,6 +92,11 @@ component("url") { public_configs = [ "//third_party/jdk" ] } diff --git a/chromium_edits/121.0.6167.8/url/url_canon.h.patch b/chromium_edits/122.0.6226.2/url/url_canon.h.patch similarity index 91% rename from chromium_edits/121.0.6167.8/url/url_canon.h.patch rename to chromium_edits/122.0.6226.2/url/url_canon.h.patch index 24ae1ba4..7ffd3a2f 100644 --- a/chromium_edits/121.0.6167.8/url/url_canon.h.patch +++ b/chromium_edits/122.0.6226.2/url/url_canon.h.patch @@ -1,8 +1,8 @@ diff --git a/url/url_canon.h b/url/url_canon.h -index d3a7fabf09fa8..06db17242248f 100644 +index 8c48f9825d8cf..b9ad961e1b123 100644 --- a/url/url_canon.h +++ b/url/url_canon.h -@@ -697,6 +697,23 @@ bool CanonicalizeMailtoURL(const char16_t* spec, +@@ -804,6 +804,23 @@ bool CanonicalizeMailtoURL(const char16_t* spec, CanonOutput* output, Parsed* new_parsed); diff --git a/chromium_edits/121.0.6156.3/url/url_canon_ipfs.cc b/chromium_edits/122.0.6226.2/url/url_canon_ipfs.cc similarity index 62% rename from chromium_edits/121.0.6156.3/url/url_canon_ipfs.cc rename to chromium_edits/122.0.6226.2/url/url_canon_ipfs.cc index da3a5f03..d7c9fdc7 100644 --- a/chromium_edits/121.0.6156.3/url/url_canon_ipfs.cc +++ b/chromium_edits/122.0.6226.2/url/url_canon_ipfs.cc @@ -1,14 +1,10 @@ #include "url_canon_internal.h" -#include +#include #include #include -namespace m = libp2p::multi; -using Cid = m::ContentIdentifier; -using CidCodec = m::ContentIdentifierCodec; - bool url::CanonicalizeIpfsURL(const char* spec, int spec_len, const Parsed& parsed, @@ -22,37 +18,24 @@ bool url::CanonicalizeIpfsURL(const char* spec, if ( parsed.host.len < 1 ) { return false; } - std::string cid_str{ spec + parsed.host.begin, static_cast(parsed.host.len) }; - auto maybe_cid = CidCodec::fromString(cid_str); - if ( !maybe_cid.has_value() ) { - auto e = libp2p::multi::Stringify(maybe_cid.error()); - std::ostringstream err; - err << e << ' ' - << std::string_view{spec,static_cast(spec_len)}; - maybe_cid = ipfs::id_cid::forText( err.str() ); - } - auto cid = maybe_cid.value(); - if ( cid.version == Cid::Version::V0 ) { - //TODO dcheck content_type == DAG_PB && content_address.getType() == sha256 - cid = Cid{ - Cid::Version::V1, - cid.content_type, - cid.content_address - }; + std::string_view cid_str{ spec + parsed.host.begin, static_cast(parsed.host.len) }; + auto cid = ipfs::Cid(cid_str); + if ( !cid.valid() ) { + cid = ipfs::id_cid::forText( std::string{cid_str} + " is not a valid CID." ); } - auto as_str = CidCodec::toString(cid); - if ( !as_str.has_value() ) { + auto as_str = cid.to_string(); + if ( as_str.empty() ) { return false; } std::string stdurl{ spec, static_cast(parsed.host.begin) }; - stdurl.append( as_str.value() ); + stdurl.append( as_str ); stdurl.append( spec + parsed.host.end(), spec_len - parsed.host.end() ); spec = stdurl.data(); spec_len = static_cast(stdurl.size()); Parsed parsed_input; ParseStandardURL(spec, spec_len, &parsed_input); return CanonicalizeStandardURL( - spec, spec_len, + spec, parsed_input, scheme_type, charset_converter, diff --git a/chromium_edits/122.0.6226.2/url/url_util.cc.patch b/chromium_edits/122.0.6226.2/url/url_util.cc.patch new file mode 100644 index 00000000..814f4b82 --- /dev/null +++ b/chromium_edits/122.0.6226.2/url/url_util.cc.patch @@ -0,0 +1,22 @@ +diff --git a/url/url_util.cc b/url/url_util.cc +index 6f83f33c01c6b..a248e11c49445 100644 +--- a/url/url_util.cc ++++ b/url/url_util.cc +@@ -273,8 +273,15 @@ bool DoCanonicalize(const CHAR* spec, + } else if (DoCompareSchemeComponent(spec, scheme, url::kFileSystemScheme)) { + // Filesystem URLs are special. + ParseFileSystemURL(spec, spec_len, &parsed_input); +- success = CanonicalizeFileSystemURL(spec, parsed_input, charset_converter, +- output, output_parsed); ++ success = CanonicalizeFileSystemURL(spec, parsed_input, ++ charset_converter, output, ++ output_parsed); ++ ++ } else if (DoCompareSchemeComponent(spec, scheme, "ipfs")) { ++ // Switch multibase away from case-sensitive ones before continuing canonicalization. ++ ParseStandardURL(spec, spec_len, &parsed_input); ++ success = CanonicalizeIpfsURL(spec, spec_len, parsed_input, scheme_type, ++ charset_converter, output, output_parsed); + + } else if (DoIsStandard(spec, scheme, &scheme_type)) { + // All "normal" URLs. diff --git a/chromium_edits/121.0.6167.8/chrome/browser/BUILD.gn.patch b/chromium_edits/122.0.6236.2/chrome/browser/BUILD.gn.patch similarity index 72% rename from chromium_edits/121.0.6167.8/chrome/browser/BUILD.gn.patch rename to chromium_edits/122.0.6236.2/chrome/browser/BUILD.gn.patch index 68259c43..eb61fe43 100644 --- a/chromium_edits/121.0.6167.8/chrome/browser/BUILD.gn.patch +++ b/chromium_edits/122.0.6236.2/chrome/browser/BUILD.gn.patch @@ -1,8 +1,8 @@ diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn -index 6572022524828..d5956c31511df 100644 +index 9e7f057a1c35d..846fe6d119c66 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn -@@ -40,6 +40,7 @@ import("//rlz/buildflags/buildflags.gni") +@@ -39,6 +39,7 @@ import("//rlz/buildflags/buildflags.gni") import("//sandbox/features.gni") import("//testing/libfuzzer/fuzzer_test.gni") import("//third_party/blink/public/public_features.gni") @@ -10,11 +10,15 @@ index 6572022524828..d5956c31511df 100644 import("//third_party/protobuf/proto_library.gni") import("//third_party/webrtc/webrtc.gni") import("//third_party/widevine/cdm/widevine.gni") -@@ -2632,6 +2633,10 @@ static_library("browser") { +@@ -2619,6 +2620,14 @@ static_library("browser") { ] } + if (enable_ipfs) { ++ sources += [ ++ "ipfs_extra_parts.cc", ++ "ipfs_extra_parts.h", ++ ] + deps += [ "//components/ipfs" ] + } + diff --git a/chromium_edits/121.0.6147.0/chrome/browser/about_flags.cc.patch b/chromium_edits/122.0.6236.2/chrome/browser/about_flags.cc.patch similarity index 88% rename from chromium_edits/121.0.6147.0/chrome/browser/about_flags.cc.patch rename to chromium_edits/122.0.6236.2/chrome/browser/about_flags.cc.patch index 91648f23..bbc45911 100644 --- a/chromium_edits/121.0.6147.0/chrome/browser/about_flags.cc.patch +++ b/chromium_edits/122.0.6236.2/chrome/browser/about_flags.cc.patch @@ -1,16 +1,16 @@ diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc -index 680632024bb1b..27ea3c7c74620 100644 +index 61699cdf19305..5b99fd9811eb0 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc -@@ -209,6 +209,7 @@ +@@ -214,6 +214,7 @@ #include "third_party/blink/public/common/features_generated.h" #include "third_party/blink/public/common/forcedark/forcedark_switches.h" #include "third_party/blink/public/common/switches.h" +#include "third_party/ipfs_client/ipfs_buildflags.h" #include "ui/accessibility/accessibility_features.h" #include "ui/accessibility/accessibility_switches.h" - #include "ui/base/ui_base_features.h" -@@ -309,6 +310,10 @@ + #include "ui/base/ozone_buildflags.h" +@@ -310,6 +311,10 @@ #include "extensions/common/switches.h" #endif // BUILDFLAG(ENABLE_EXTENSIONS) @@ -21,7 +21,7 @@ index 680632024bb1b..27ea3c7c74620 100644 #if BUILDFLAG(ENABLE_PDF) #include "pdf/pdf_features.h" #endif -@@ -9912,6 +9917,14 @@ const FeatureEntry kFeatureEntries[] = { +@@ -9413,6 +9418,14 @@ const FeatureEntry kFeatureEntries[] = { flag_descriptions::kOmitCorsClientCertDescription, kOsAll, FEATURE_VALUE_TYPE(network::features::kOmitCorsClientCert)}, diff --git a/chromium_edits/121.0.6156.3/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc.patch b/chromium_edits/122.0.6236.2/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc.patch similarity index 100% rename from chromium_edits/121.0.6156.3/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc.patch rename to chromium_edits/122.0.6236.2/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc.patch diff --git a/chromium_edits/121.0.6156.3/chrome/browser/chrome_content_browser_client.cc.patch b/chromium_edits/122.0.6236.2/chrome/browser/chrome_content_browser_client.cc.patch similarity index 76% rename from chromium_edits/121.0.6156.3/chrome/browser/chrome_content_browser_client.cc.patch rename to chromium_edits/122.0.6236.2/chrome/browser/chrome_content_browser_client.cc.patch index 1f6d393d..a991fd2e 100644 --- a/chromium_edits/121.0.6156.3/chrome/browser/chrome_content_browser_client.cc.patch +++ b/chromium_edits/122.0.6236.2/chrome/browser/chrome_content_browser_client.cc.patch @@ -1,5 +1,5 @@ diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc -index 5746f7c712514..2c56ac60c0e74 100644 +index a9f0eb5ab9151..8956d3ca5519e 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc @@ -374,6 +374,7 @@ @@ -10,11 +10,12 @@ index 5746f7c712514..2c56ac60c0e74 100644 #include "third_party/widevine/cdm/buildflags.h" #include "ui/base/clipboard/clipboard_format_type.h" #include "ui/base/l10n/l10n_util.h" -@@ -496,6 +497,12 @@ +@@ -497,6 +498,13 @@ #include "chrome/browser/fuchsia/chrome_browser_main_parts_fuchsia.h" #endif +#if BUILDFLAG(ENABLE_IPFS) ++#include "chrome/browser/ipfs_extra_parts.h" +#include "components/ipfs/interceptor.h" +#include "components/ipfs/ipfs_features.h" +#include "components/ipfs/url_loader_factory.h" @@ -23,7 +24,19 @@ index 5746f7c712514..2c56ac60c0e74 100644 #if BUILDFLAG(IS_CHROMEOS) #include "base/debug/leak_annotations.h" #include "chrome/browser/apps/app_service/app_install/app_install_navigation_throttle.h" -@@ -6130,12 +6137,23 @@ void ChromeContentBrowserClient:: +@@ -1709,6 +1717,11 @@ ChromeContentBrowserClient::CreateBrowserMainParts(bool is_integration_test) { + main_parts->AddParts( + std::make_unique()); + ++#if BUILDFLAG(ENABLE_IPFS) ++ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { ++ main_parts->AddParts(std::make_unique()); ++ } ++#endif + return main_parts; + } + +@@ -6049,12 +6062,25 @@ void ChromeContentBrowserClient:: const absl::optional& request_initiator_origin, NonNetworkURLLoaderFactoryMap* factories) { #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(ENABLE_EXTENSIONS) || \ @@ -33,23 +46,24 @@ index 5746f7c712514..2c56ac60c0e74 100644 RenderFrameHost::FromID(render_process_id, render_frame_id); WebContents* web_contents = WebContents::FromRenderFrameHost(frame_host); #endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(ENABLE_EXTENSIONS) || \ -- // !BUILDFLAG(IS_ANDROID) -+ // !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_IPFS) + // !BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(ENABLE_IPFS) + if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { + network::mojom::URLLoaderFactory* default_factory = g_browser_process->system_network_context_manager()->GetURLLoaderFactory(); ++ auto* context = web_contents->GetBrowserContext(); + ipfs::IpfsURLLoaderFactory::Create( + factories, -+ web_contents->GetBrowserContext(), ++ context, + default_factory, -+ GetSystemNetworkContext() ++ GetSystemNetworkContext(), ++ Profile::FromBrowserContext(context)->GetPrefs() + ); + } +#endif // BUILDFLAG(ENABLE_IPFS) #if BUILDFLAG(IS_CHROMEOS_ASH) if (web_contents) { -@@ -6277,6 +6295,11 @@ ChromeContentBrowserClient::WillCreateURLLoaderRequestInterceptors( +@@ -6196,6 +6222,11 @@ ChromeContentBrowserClient::WillCreateURLLoaderRequestInterceptors( scoped_refptr navigation_response_task_runner) { std::vector> interceptors; diff --git a/chromium_edits/121.0.6167.8/chrome/browser/flag-metadata.json.patch b/chromium_edits/122.0.6236.2/chrome/browser/flag-metadata.json.patch similarity index 88% rename from chromium_edits/121.0.6167.8/chrome/browser/flag-metadata.json.patch rename to chromium_edits/122.0.6236.2/chrome/browser/flag-metadata.json.patch index 834d2929..7827ee94 100644 --- a/chromium_edits/121.0.6167.8/chrome/browser/flag-metadata.json.patch +++ b/chromium_edits/122.0.6236.2/chrome/browser/flag-metadata.json.patch @@ -1,8 +1,8 @@ diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json -index 0cdc387bf20b7..10e6b164bfdbf 100644 +index 91740e0cc9e2b..916f584f37a54 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json -@@ -2944,6 +2944,11 @@ +@@ -2943,6 +2943,11 @@ "owners": [ "hanxi@chromium.org", "wychen@chromium.org" ], "expiry_milestone": 130 }, diff --git a/chromium_edits/121.0.6147.0/chrome/browser/flag_descriptions.cc.patch b/chromium_edits/122.0.6236.2/chrome/browser/flag_descriptions.cc.patch similarity index 85% rename from chromium_edits/121.0.6147.0/chrome/browser/flag_descriptions.cc.patch rename to chromium_edits/122.0.6236.2/chrome/browser/flag_descriptions.cc.patch index 96f7d80e..9a74068e 100644 --- a/chromium_edits/121.0.6147.0/chrome/browser/flag_descriptions.cc.patch +++ b/chromium_edits/122.0.6236.2/chrome/browser/flag_descriptions.cc.patch @@ -1,8 +1,8 @@ diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc -index 4f92be79c3e35..780bdad269134 100644 +index bddee4563e255..9e84641ac307f 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc -@@ -274,6 +274,11 @@ const char kEnableBenchmarkingDescription[] = +@@ -284,6 +284,11 @@ const char kEnableBenchmarkingDescription[] = "after 3 restarts. On the third restart, the flag will appear to be off " "but the effect is still active."; diff --git a/chromium_edits/121.0.6156.3/chrome/browser/flag_descriptions.h.patch b/chromium_edits/122.0.6236.2/chrome/browser/flag_descriptions.h.patch similarity index 87% rename from chromium_edits/121.0.6156.3/chrome/browser/flag_descriptions.h.patch rename to chromium_edits/122.0.6236.2/chrome/browser/flag_descriptions.h.patch index b3836144..e3bc6464 100644 --- a/chromium_edits/121.0.6156.3/chrome/browser/flag_descriptions.h.patch +++ b/chromium_edits/122.0.6236.2/chrome/browser/flag_descriptions.h.patch @@ -1,8 +1,8 @@ diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h -index afe0f24f278d3..3e0fe1ba84040 100644 +index dc09da6e1897a..f139e88d67ae1 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h -@@ -22,6 +22,7 @@ +@@ -23,6 +23,7 @@ #include "pdf/buildflags.h" #include "printing/buildflags/buildflags.h" #include "third_party/blink/public/common/buildflags.h" @@ -10,7 +10,7 @@ index afe0f24f278d3..3e0fe1ba84040 100644 // This file declares strings used in chrome://flags. These messages are not // translated, because instead of end-users they target Chromium developers and -@@ -175,6 +176,11 @@ extern const char kDownloadWarningImprovementsDescription[]; +@@ -176,6 +177,11 @@ extern const char kDownloadWarningImprovementsDescription[]; extern const char kEnableBenchmarkingName[]; extern const char kEnableBenchmarkingDescription[]; diff --git a/chromium_edits/122.0.6236.2/chrome/browser/ipfs_extra_parts.cc b/chromium_edits/122.0.6236.2/chrome/browser/ipfs_extra_parts.cc new file mode 100644 index 00000000..90d2596f --- /dev/null +++ b/chromium_edits/122.0.6236.2/chrome/browser/ipfs_extra_parts.cc @@ -0,0 +1,10 @@ +#include "ipfs_extra_parts.h" + +#include "profiles/profile.h" + +#include + +void IpfsExtraParts::PostProfileInit(Profile* profile, bool /* is_initial_profile */ ) { + DCHECK(profile); + ipfs::InterRequestState::CreateForBrowserContext(profile, profile->GetPrefs()); +} diff --git a/chromium_edits/122.0.6236.2/chrome/browser/ipfs_extra_parts.h b/chromium_edits/122.0.6236.2/chrome/browser/ipfs_extra_parts.h new file mode 100644 index 00000000..2059c437 --- /dev/null +++ b/chromium_edits/122.0.6236.2/chrome/browser/ipfs_extra_parts.h @@ -0,0 +1,10 @@ +#ifndef IPFS_EXTRA_PART_H_ +#define IPFS_EXTRA_PART_H_ + +#include + +class IpfsExtraParts : public ChromeBrowserMainExtraParts { + void PostProfileInit(Profile* profile, bool is_initial_profile) override; +}; + +#endif // IPFS_EXTRA_PART_H_ diff --git a/chromium_edits/122.0.6236.2/chrome/browser/prefs/browser_prefs.cc.patch b/chromium_edits/122.0.6236.2/chrome/browser/prefs/browser_prefs.cc.patch new file mode 100644 index 00000000..ddda775c --- /dev/null +++ b/chromium_edits/122.0.6236.2/chrome/browser/prefs/browser_prefs.cc.patch @@ -0,0 +1,36 @@ +diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc +index e406d936c277c..0aff4e87587ae 100644 +--- a/chrome/browser/prefs/browser_prefs.cc ++++ b/chrome/browser/prefs/browser_prefs.cc +@@ -189,6 +189,7 @@ + #include "printing/buildflags/buildflags.h" + #include "rlz/buildflags/buildflags.h" + #include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/ipfs_client/ipfs_buildflags.h" + + #if BUILDFLAG(ENABLE_BACKGROUND_MODE) + #include "chrome/browser/background/background_mode_manager.h" +@@ -233,6 +234,11 @@ + #include "chrome/browser/pdf/pdf_pref_names.h" + #endif // BUILDFLAG(ENABLE_PDF) + ++#if BUILDFLAG(ENABLE_IPFS) ++#include "components/ipfs/ipfs_features.h" ++#include "components/ipfs/preferences.h" ++#endif ++ + #if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) + #include "chrome/browser/screen_ai/pref_names.h" + #endif +@@ -1658,6 +1664,11 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry, + IncognitoModePrefs::RegisterProfilePrefs(registry); + invalidation::PerUserTopicSubscriptionManager::RegisterProfilePrefs(registry); + invalidation::InvalidatorRegistrarWithMemory::RegisterProfilePrefs(registry); ++#if BUILDFLAG(ENABLE_IPFS) ++ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { ++ ipfs::RegisterPreferences(registry); ++ } ++#endif + language::LanguagePrefs::RegisterProfilePrefs(registry); + login_detection::prefs::RegisterProfilePrefs(registry); + lookalikes::RegisterProfilePrefs(registry); diff --git a/chromium_edits/121.0.6156.3/chrome/common/chrome_content_client.cc.patch b/chromium_edits/122.0.6236.2/chrome/common/chrome_content_client.cc.patch similarity index 100% rename from chromium_edits/121.0.6156.3/chrome/common/chrome_content_client.cc.patch rename to chromium_edits/122.0.6236.2/chrome/common/chrome_content_client.cc.patch diff --git a/chromium_edits/121.0.6156.3/components/cbor/reader.cc.patch b/chromium_edits/122.0.6236.2/components/cbor/reader.cc.patch similarity index 100% rename from chromium_edits/121.0.6156.3/components/cbor/reader.cc.patch rename to chromium_edits/122.0.6236.2/components/cbor/reader.cc.patch diff --git a/chromium_edits/121.0.6156.3/components/cbor/reader.h.patch b/chromium_edits/122.0.6236.2/components/cbor/reader.h.patch similarity index 100% rename from chromium_edits/121.0.6156.3/components/cbor/reader.h.patch rename to chromium_edits/122.0.6236.2/components/cbor/reader.h.patch diff --git a/chromium_edits/121.0.6156.3/components/cbor/reader_unittest.cc.patch b/chromium_edits/122.0.6236.2/components/cbor/reader_unittest.cc.patch similarity index 100% rename from chromium_edits/121.0.6156.3/components/cbor/reader_unittest.cc.patch rename to chromium_edits/122.0.6236.2/components/cbor/reader_unittest.cc.patch diff --git a/chromium_edits/121.0.6156.3/components/cbor/values.cc.patch b/chromium_edits/122.0.6236.2/components/cbor/values.cc.patch similarity index 100% rename from chromium_edits/121.0.6156.3/components/cbor/values.cc.patch rename to chromium_edits/122.0.6236.2/components/cbor/values.cc.patch diff --git a/chromium_edits/121.0.6156.3/components/cbor/values.h.patch b/chromium_edits/122.0.6236.2/components/cbor/values.h.patch similarity index 100% rename from chromium_edits/121.0.6156.3/components/cbor/values.h.patch rename to chromium_edits/122.0.6236.2/components/cbor/values.h.patch diff --git a/chromium_edits/121.0.6156.3/components/cbor/writer.cc.patch b/chromium_edits/122.0.6236.2/components/cbor/writer.cc.patch similarity index 100% rename from chromium_edits/121.0.6156.3/components/cbor/writer.cc.patch rename to chromium_edits/122.0.6236.2/components/cbor/writer.cc.patch diff --git a/chromium_edits/121.0.6156.3/components/cbor/writer_unittest.cc.patch b/chromium_edits/122.0.6236.2/components/cbor/writer_unittest.cc.patch similarity index 100% rename from chromium_edits/121.0.6156.3/components/cbor/writer_unittest.cc.patch rename to chromium_edits/122.0.6236.2/components/cbor/writer_unittest.cc.patch diff --git a/chromium_edits/121.0.6156.3/components/open_from_clipboard/clipboard_recent_content_generic.cc.patch b/chromium_edits/122.0.6236.2/components/open_from_clipboard/clipboard_recent_content_generic.cc.patch similarity index 100% rename from chromium_edits/121.0.6156.3/components/open_from_clipboard/clipboard_recent_content_generic.cc.patch rename to chromium_edits/122.0.6236.2/components/open_from_clipboard/clipboard_recent_content_generic.cc.patch diff --git a/chromium_edits/121.0.6156.3/net/dns/dns_config_service_linux.cc.patch b/chromium_edits/122.0.6236.2/net/dns/dns_config_service_linux.cc.patch similarity index 100% rename from chromium_edits/121.0.6156.3/net/dns/dns_config_service_linux.cc.patch rename to chromium_edits/122.0.6236.2/net/dns/dns_config_service_linux.cc.patch diff --git a/chromium_edits/121.0.6156.3/third_party/blink/renderer/platform/weborigin/scheme_registry.cc.patch b/chromium_edits/122.0.6236.2/third_party/blink/renderer/platform/weborigin/scheme_registry.cc.patch similarity index 100% rename from chromium_edits/121.0.6156.3/third_party/blink/renderer/platform/weborigin/scheme_registry.cc.patch rename to chromium_edits/122.0.6236.2/third_party/blink/renderer/platform/weborigin/scheme_registry.cc.patch diff --git a/chromium_edits/121.0.6147.0/url/BUILD.gn.patch b/chromium_edits/122.0.6236.2/url/BUILD.gn.patch similarity index 87% rename from chromium_edits/121.0.6147.0/url/BUILD.gn.patch rename to chromium_edits/122.0.6236.2/url/BUILD.gn.patch index 63fb8f8b..cc358ad4 100644 --- a/chromium_edits/121.0.6147.0/url/BUILD.gn.patch +++ b/chromium_edits/122.0.6236.2/url/BUILD.gn.patch @@ -1,5 +1,5 @@ diff --git a/url/BUILD.gn b/url/BUILD.gn -index c525c166979d6..ce2b1ae43c0a7 100644 +index b5edb89f7698f..d299856674d7d 100644 --- a/url/BUILD.gn +++ b/url/BUILD.gn @@ -5,6 +5,7 @@ @@ -10,7 +10,7 @@ index c525c166979d6..ce2b1ae43c0a7 100644 import("features.gni") import("//build/config/cronet/config.gni") -@@ -67,6 +68,7 @@ component("url") { +@@ -68,6 +69,7 @@ component("url") { public_deps = [ "//base", "//build:robolectric_buildflags", @@ -18,7 +18,7 @@ index c525c166979d6..ce2b1ae43c0a7 100644 ] configs += [ "//build/config/compiler:wexit_time_destructors" ] -@@ -89,6 +91,11 @@ component("url") { +@@ -90,6 +92,11 @@ component("url") { public_configs = [ "//third_party/jdk" ] } diff --git a/chromium_edits/121.0.6147.0/url/url_canon.h.patch b/chromium_edits/122.0.6236.2/url/url_canon.h.patch similarity index 91% rename from chromium_edits/121.0.6147.0/url/url_canon.h.patch rename to chromium_edits/122.0.6236.2/url/url_canon.h.patch index 24ae1ba4..7ffd3a2f 100644 --- a/chromium_edits/121.0.6147.0/url/url_canon.h.patch +++ b/chromium_edits/122.0.6236.2/url/url_canon.h.patch @@ -1,8 +1,8 @@ diff --git a/url/url_canon.h b/url/url_canon.h -index d3a7fabf09fa8..06db17242248f 100644 +index 8c48f9825d8cf..b9ad961e1b123 100644 --- a/url/url_canon.h +++ b/url/url_canon.h -@@ -697,6 +697,23 @@ bool CanonicalizeMailtoURL(const char16_t* spec, +@@ -804,6 +804,23 @@ bool CanonicalizeMailtoURL(const char16_t* spec, CanonOutput* output, Parsed* new_parsed); diff --git a/chromium_edits/121.0.6167.8/url/url_canon_ipfs.cc b/chromium_edits/122.0.6236.2/url/url_canon_ipfs.cc similarity index 62% rename from chromium_edits/121.0.6167.8/url/url_canon_ipfs.cc rename to chromium_edits/122.0.6236.2/url/url_canon_ipfs.cc index da3a5f03..d7c9fdc7 100644 --- a/chromium_edits/121.0.6167.8/url/url_canon_ipfs.cc +++ b/chromium_edits/122.0.6236.2/url/url_canon_ipfs.cc @@ -1,14 +1,10 @@ #include "url_canon_internal.h" -#include +#include #include #include -namespace m = libp2p::multi; -using Cid = m::ContentIdentifier; -using CidCodec = m::ContentIdentifierCodec; - bool url::CanonicalizeIpfsURL(const char* spec, int spec_len, const Parsed& parsed, @@ -22,37 +18,24 @@ bool url::CanonicalizeIpfsURL(const char* spec, if ( parsed.host.len < 1 ) { return false; } - std::string cid_str{ spec + parsed.host.begin, static_cast(parsed.host.len) }; - auto maybe_cid = CidCodec::fromString(cid_str); - if ( !maybe_cid.has_value() ) { - auto e = libp2p::multi::Stringify(maybe_cid.error()); - std::ostringstream err; - err << e << ' ' - << std::string_view{spec,static_cast(spec_len)}; - maybe_cid = ipfs::id_cid::forText( err.str() ); - } - auto cid = maybe_cid.value(); - if ( cid.version == Cid::Version::V0 ) { - //TODO dcheck content_type == DAG_PB && content_address.getType() == sha256 - cid = Cid{ - Cid::Version::V1, - cid.content_type, - cid.content_address - }; + std::string_view cid_str{ spec + parsed.host.begin, static_cast(parsed.host.len) }; + auto cid = ipfs::Cid(cid_str); + if ( !cid.valid() ) { + cid = ipfs::id_cid::forText( std::string{cid_str} + " is not a valid CID." ); } - auto as_str = CidCodec::toString(cid); - if ( !as_str.has_value() ) { + auto as_str = cid.to_string(); + if ( as_str.empty() ) { return false; } std::string stdurl{ spec, static_cast(parsed.host.begin) }; - stdurl.append( as_str.value() ); + stdurl.append( as_str ); stdurl.append( spec + parsed.host.end(), spec_len - parsed.host.end() ); spec = stdurl.data(); spec_len = static_cast(stdurl.size()); Parsed parsed_input; ParseStandardURL(spec, spec_len, &parsed_input); return CanonicalizeStandardURL( - spec, spec_len, + spec, parsed_input, scheme_type, charset_converter, diff --git a/chromium_edits/122.0.6236.2/url/url_util.cc.patch b/chromium_edits/122.0.6236.2/url/url_util.cc.patch new file mode 100644 index 00000000..814f4b82 --- /dev/null +++ b/chromium_edits/122.0.6236.2/url/url_util.cc.patch @@ -0,0 +1,22 @@ +diff --git a/url/url_util.cc b/url/url_util.cc +index 6f83f33c01c6b..a248e11c49445 100644 +--- a/url/url_util.cc ++++ b/url/url_util.cc +@@ -273,8 +273,15 @@ bool DoCanonicalize(const CHAR* spec, + } else if (DoCompareSchemeComponent(spec, scheme, url::kFileSystemScheme)) { + // Filesystem URLs are special. + ParseFileSystemURL(spec, spec_len, &parsed_input); +- success = CanonicalizeFileSystemURL(spec, parsed_input, charset_converter, +- output, output_parsed); ++ success = CanonicalizeFileSystemURL(spec, parsed_input, ++ charset_converter, output, ++ output_parsed); ++ ++ } else if (DoCompareSchemeComponent(spec, scheme, "ipfs")) { ++ // Switch multibase away from case-sensitive ones before continuing canonicalization. ++ ParseStandardURL(spec, spec_len, &parsed_input); ++ success = CanonicalizeIpfsURL(spec, spec_len, parsed_input, scheme_type, ++ charset_converter, output, output_parsed); + + } else if (DoIsStandard(spec, scheme, &scheme_type)) { + // All "normal" URLs. diff --git a/cmake/GNU.cmake b/cmake/GNU.cmake index 6545949f..5eaf8f05 100644 --- a/cmake/GNU.cmake +++ b/cmake/GNU.cmake @@ -1,2 +1,3 @@ set(CHROMIUM_COMPILER_FLAGS -nostdinc++ -fno-rtti) set(WARNING_FLAGS -Wall -Wextra -Wpedantic -Werror=switch-enum -Werror=return-type -Werror=reorder -Werror=implicit-fallthrough -fmax-errors=3) +set(coverage_debug_flags -fno-inline -g3 -ggdb3 -O0 ) diff --git a/cmake/patch.py b/cmake/patch.py index 4d98c109..b4d280d0 100755 --- a/cmake/patch.py +++ b/cmake/patch.py @@ -212,11 +212,7 @@ def recommend(self) -> str: raise EnvironmentError(f"Can't find an appropriate tag for {osname()}, anymore!") def available(self): - for f in listdir(self.pdir): - if splitext(f)[1] != '.patch': - print(f"Warning: {f} does not belong in {self.pdir}. Removing.", file=stderr) - remove(join(self.pdir, f)) - return map(lambda p: splitext(p)[0], listdir(self.pdir)) + return listdir(self.edir) def distances(self, frm, ref): a = int(self.git(['rev-list', '--count', frm+'..'+ref], Result.Output)) @@ -258,7 +254,7 @@ def electron_version(self, branch='main'): def unavailable(self): avail = list(map(as_int, self.available())) version_set = {} - fudge = 59893 + fudge = 59895 def check(version, version_set, s): i = as_int(version) by = (fudge,0) @@ -348,6 +344,10 @@ def list_ood(self, to_check: list[str], sense: bool): if len(rels) > 1: print(f'Prev {chan:9}{os:7}', rels[1][1]) print("Electron's main branch:", p.electron_version()) + elif argv[1] == 'available': + pr = Patcher('/mnt/big/lbl/code/chromium/src', 'git', 'Debug') + print(list(pr.available())) + print(pr.edir) elif argv[1] == 'old': pr = Patcher('/mnt/big/lbl/code/chromium/src', 'git', 'Debug') if len(argv) > 2: diff --git a/component/block_http_request.cc b/component/block_http_request.cc index 76244b0a..a2fff2d5 100644 --- a/component/block_http_request.cc +++ b/component/block_http_request.cc @@ -62,8 +62,6 @@ void Self::OnResponse(std::shared_ptr, status = 200; break; case net::Error::ERR_TIMED_OUT: - VLOG(2) << "HTTP request timed out: " << inf_.url << " after " - << inf_.timeout_seconds << "s."; status = 408; break; default: diff --git a/component/chromium_ipfs_context.cc b/component/chromium_ipfs_context.cc index d4b180a9..db708ea9 100644 --- a/component/chromium_ipfs_context.cc +++ b/component/chromium_ipfs_context.cc @@ -121,6 +121,9 @@ auto Self::GetGateway(std::size_t index) const -> std::optional { } return std::nullopt; } +void Self ::AddGateway(std::string_view prefix) { + rates_.SetRate(prefix, 120U); +} Self::ChromiumIpfsContext(InterRequestState& state, PrefService* prefs) : state_{state}, rates_{prefs} {} diff --git a/component/chromium_ipfs_context.h b/component/chromium_ipfs_context.h index b274746e..e48e64ef 100644 --- a/component/chromium_ipfs_context.h +++ b/component/chromium_ipfs_context.h @@ -54,6 +54,7 @@ class ChromiumIpfsContext final : public ContextApi { std::optional GetGateway(std::size_t index) const override; unsigned int GetGatewayRate(std::string_view) override; void SetGatewayRate(std::string_view, unsigned int) override; + void AddGateway(std::string_view) override; public: ChromiumIpfsContext(InterRequestState&, PrefService* prefs); diff --git a/component/patches/121.0.6147.0.patch b/component/patches/121.0.6147.0.patch deleted file mode 100644 index 586c4e7e..00000000 --- a/component/patches/121.0.6147.0.patch +++ /dev/null @@ -1,837 +0,0 @@ -diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn -index 8704f445741b4..2c0cae5d77b6d 100644 ---- a/chrome/browser/BUILD.gn -+++ b/chrome/browser/BUILD.gn -@@ -40,6 +40,7 @@ import("//rlz/buildflags/buildflags.gni") - import("//sandbox/features.gni") - import("//testing/libfuzzer/fuzzer_test.gni") - import("//third_party/blink/public/public_features.gni") -+import("//third_party/ipfs_client/args.gni") - import("//third_party/protobuf/proto_library.gni") - import("//third_party/webrtc/webrtc.gni") - import("//third_party/widevine/cdm/widevine.gni") -@@ -2634,6 +2635,10 @@ static_library("browser") { - ] - } - -+ if (enable_ipfs) { -+ deps += [ "//components/ipfs" ] -+ } -+ - if (is_chromeos_ash) { - deps += [ "//chrome/browser/screen_ai:screen_ai_dlc_installer" ] - } -diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc -index 680632024bb1b..27ea3c7c74620 100644 ---- a/chrome/browser/about_flags.cc -+++ b/chrome/browser/about_flags.cc -@@ -209,6 +209,7 @@ - #include "third_party/blink/public/common/features_generated.h" - #include "third_party/blink/public/common/forcedark/forcedark_switches.h" - #include "third_party/blink/public/common/switches.h" -+#include "third_party/ipfs_client/ipfs_buildflags.h" - #include "ui/accessibility/accessibility_features.h" - #include "ui/accessibility/accessibility_switches.h" - #include "ui/base/ui_base_features.h" -@@ -309,6 +310,10 @@ - #include "extensions/common/switches.h" - #endif // BUILDFLAG(ENABLE_EXTENSIONS) - -+#if BUILDFLAG(ENABLE_IPFS) -+#include "components/ipfs/ipfs_features.h" -+#endif -+ - #if BUILDFLAG(ENABLE_PDF) - #include "pdf/pdf_features.h" - #endif -@@ -9912,6 +9917,14 @@ const FeatureEntry kFeatureEntries[] = { - flag_descriptions::kOmitCorsClientCertDescription, kOsAll, - FEATURE_VALUE_TYPE(network::features::kOmitCorsClientCert)}, - -+#if BUILDFLAG(ENABLE_IPFS) -+ {"enable-ipfs", -+ flag_descriptions::kEnableIpfsName, -+ flag_descriptions::kEnableIpfsDescription, -+ kOsMac | kOsWin | kOsLinux,//TODO: These are the only variants currently getting built, but that is not likely to remain the case -+ FEATURE_VALUE_TYPE(ipfs::kEnableIpfs)}, -+#endif -+ - {"use-idna2008-non-transitional", - flag_descriptions::kUseIDNA2008NonTransitionalName, - flag_descriptions::kUseIDNA2008NonTransitionalDescription, kOsAll, -diff --git a/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc b/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc -index 4c88614c68c25..f8bb12a3b0c2e 100644 ---- a/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc -+++ b/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc -@@ -10,6 +10,8 @@ - #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" - #include "chrome/browser/external_protocol/external_protocol_handler.h" - #include "chrome/browser/profiles/profile.h" -+#include "third_party/ipfs_client/ipfs_buildflags.h" -+ - #if BUILDFLAG(IS_ANDROID) - #include "chrome/browser/profiles/profile_android.h" - #endif -@@ -18,6 +20,9 @@ - #include "chrome/browser/ui/android/omnibox/jni_headers/ChromeAutocompleteSchemeClassifier_jni.h" - #endif - #include "components/custom_handlers/protocol_handler_registry.h" -+#if BUILDFLAG(ENABLE_IPFS) -+#include "components/ipfs/ipfs_features.h" -+#endif - #include "content/public/common/url_constants.h" - #include "url/url_util.h" - -@@ -55,12 +60,20 @@ ChromeAutocompleteSchemeClassifier::GetInputTypeForScheme( - if (scheme.empty()) { - return metrics::OmniboxInputType::EMPTY; - } -- if (base::IsStringASCII(scheme) && -- (ProfileIOData::IsHandledProtocol(scheme) || -- base::EqualsCaseInsensitiveASCII(scheme, content::kViewSourceScheme) || -- base::EqualsCaseInsensitiveASCII(scheme, url::kJavaScriptScheme) || -- base::EqualsCaseInsensitiveASCII(scheme, url::kDataScheme))) { -- return metrics::OmniboxInputType::URL; -+ if (base::IsStringASCII(scheme)) { -+ if (ProfileIOData::IsHandledProtocol(scheme) || -+ base::EqualsCaseInsensitiveASCII(scheme, content::kViewSourceScheme) || -+ base::EqualsCaseInsensitiveASCII(scheme, url::kJavaScriptScheme) || -+ base::EqualsCaseInsensitiveASCII(scheme, url::kDataScheme)) { -+ return metrics::OmniboxInputType::URL; -+ } -+#if BUILDFLAG(ENABLE_IPFS) -+ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs) && -+ (base::EqualsCaseInsensitiveASCII(scheme, "ipfs") || base::EqualsCaseInsensitiveASCII(scheme, "ipns")) -+ ) { -+ return metrics::OmniboxInputType::URL; -+ } -+#endif - } - - // Also check for schemes registered via registerProtocolHandler(), which -diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc -index 69237f32680f9..be754dca72ffb 100644 ---- a/chrome/browser/chrome_content_browser_client.cc -+++ b/chrome/browser/chrome_content_browser_client.cc -@@ -374,6 +374,7 @@ - #include "third_party/blink/public/common/switches.h" - #include "third_party/blink/public/mojom/browsing_topics/browsing_topics.mojom.h" - #include "third_party/blink/public/public_buildflags.h" -+#include "third_party/ipfs_client/ipfs_buildflags.h" - #include "third_party/widevine/cdm/buildflags.h" - #include "ui/base/clipboard/clipboard_format_type.h" - #include "ui/base/l10n/l10n_util.h" -@@ -496,6 +497,12 @@ - #include "chrome/browser/fuchsia/chrome_browser_main_parts_fuchsia.h" - #endif - -+#if BUILDFLAG(ENABLE_IPFS) -+#include "components/ipfs/interceptor.h" -+#include "components/ipfs/ipfs_features.h" -+#include "components/ipfs/url_loader_factory.h" -+#endif -+ - #if BUILDFLAG(IS_CHROMEOS) - #include "base/debug/leak_annotations.h" - #include "chrome/browser/apps/app_service/app_install/app_install_navigation_throttle.h" -@@ -6130,12 +6137,23 @@ void ChromeContentBrowserClient:: - const absl::optional& request_initiator_origin, - NonNetworkURLLoaderFactoryMap* factories) { - #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(ENABLE_EXTENSIONS) || \ -- !BUILDFLAG(IS_ANDROID) -+ !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_IPFS) - content::RenderFrameHost* frame_host = - RenderFrameHost::FromID(render_process_id, render_frame_id); - WebContents* web_contents = WebContents::FromRenderFrameHost(frame_host); - #endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(ENABLE_EXTENSIONS) || \ -- // !BUILDFLAG(IS_ANDROID) -+ // !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_IPFS) -+#if BUILDFLAG(ENABLE_IPFS) -+ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { -+ network::mojom::URLLoaderFactory* default_factory = g_browser_process->system_network_context_manager()->GetURLLoaderFactory(); -+ ipfs::IpfsURLLoaderFactory::Create( -+ factories, -+ web_contents->GetBrowserContext(), -+ default_factory, -+ GetSystemNetworkContext() -+ ); -+ } -+#endif // BUILDFLAG(ENABLE_IPFS) - - #if BUILDFLAG(IS_CHROMEOS_ASH) - if (web_contents) { -@@ -6277,6 +6295,11 @@ ChromeContentBrowserClient::WillCreateURLLoaderRequestInterceptors( - scoped_refptr navigation_response_task_runner) { - std::vector> - interceptors; -+#if BUILDFLAG(ENABLE_IPFS) -+ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { -+ interceptors.push_back(std::make_unique(g_browser_process->system_network_context_manager()->GetURLLoaderFactory(), GetSystemNetworkContext())); -+ } -+#endif - #if BUILDFLAG(ENABLE_OFFLINE_PAGES) - interceptors.push_back( - std::make_unique( -diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json -index f8609c4f6e389..7f329f8ab9bfb 100644 ---- a/chrome/browser/flag-metadata.json -+++ b/chrome/browser/flag-metadata.json -@@ -2889,6 +2889,11 @@ - "owners": [ "hanxi@chromium.org", "wychen@chromium.org" ], - "expiry_milestone": 130 - }, -+ { -+ "name": "enable-ipfs", -+ "owners": [ "//components/ipfs/OWNERS" ], -+ "expiry_milestone": 150 -+ }, - { - "name": "enable-isolated-sandboxed-iframes", - "owners": [ "wjmaclean@chromium.org", "alexmos@chromium.org", "creis@chromium.org" ], -diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc -index 4f92be79c3e35..780bdad269134 100644 ---- a/chrome/browser/flag_descriptions.cc -+++ b/chrome/browser/flag_descriptions.cc -@@ -274,6 +274,11 @@ const char kEnableBenchmarkingDescription[] = - "after 3 restarts. On the third restart, the flag will appear to be off " - "but the effect is still active."; - -+#if BUILDFLAG(ENABLE_IPFS) -+extern const char kEnableIpfsName[] = "Enable IPFS"; -+extern const char kEnableIpfsDescription[] = "Enable ipfs:// and ipns:// URLs"; -+#endif -+ - const char kPreloadingOnPerformancePageName[] = - "Preloading Settings on Performance Page"; - const char kPreloadingOnPerformancePageDescription[] = -diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h -index ca4c8f927ba26..ab6f0ee6cb103 100644 ---- a/chrome/browser/flag_descriptions.h -+++ b/chrome/browser/flag_descriptions.h -@@ -22,6 +22,7 @@ - #include "pdf/buildflags.h" - #include "printing/buildflags/buildflags.h" - #include "third_party/blink/public/common/buildflags.h" -+#include "third_party/ipfs_client/ipfs_buildflags.h" - - // This file declares strings used in chrome://flags. These messages are not - // translated, because instead of end-users they target Chromium developers and -@@ -172,6 +173,11 @@ extern const char kDownloadWarningImprovementsDescription[]; - extern const char kEnableBenchmarkingName[]; - extern const char kEnableBenchmarkingDescription[]; - -+#if BUILDFLAG(ENABLE_IPFS) -+extern const char kEnableIpfsName[]; -+extern const char kEnableIpfsDescription[]; -+#endif -+ - #if BUILDFLAG(USE_FONTATIONS_BACKEND) - extern const char kFontationsFontBackendName[]; - extern const char kFontationsFontBackendDescription[]; -diff --git a/chrome/common/chrome_content_client.cc b/chrome/common/chrome_content_client.cc -index 246ec9c5c911f..5d66d133a7907 100644 ---- a/chrome/common/chrome_content_client.cc -+++ b/chrome/common/chrome_content_client.cc -@@ -296,6 +296,12 @@ void ChromeContentClient::AddAdditionalSchemes(Schemes* schemes) { - #if BUILDFLAG(IS_ANDROID) - schemes->local_schemes.push_back(url::kContentScheme); - #endif -+ for ( const char* ip_s : {"ipfs", "ipns"} ) { -+ schemes->standard_schemes.push_back(ip_s); -+ schemes->cors_enabled_schemes.push_back(ip_s); -+ schemes->secure_schemes.push_back(ip_s); -+ schemes->csp_bypassing_schemes.push_back(ip_s); -+ } - } - - std::u16string ChromeContentClient::GetLocalizedString(int message_id) { -diff --git a/components/cbor/reader.cc b/components/cbor/reader.cc -index 306ba52fa4944..6b13b3a679a65 100644 ---- a/components/cbor/reader.cc -+++ b/components/cbor/reader.cc -@@ -22,7 +22,7 @@ - namespace cbor { - - namespace constants { --const char kUnsupportedMajorType[] = "Unsupported major type."; -+const char kUnsupportedMajorType[] = "Unsupported major type operation."; - } - - namespace { -@@ -156,7 +156,11 @@ absl::optional Reader::DecodeCompleteDataItem(const Config& config, - case Value::Type::FLOAT_VALUE: - // Floating point values also go here since they are also type 7. - return DecodeToSimpleValueOrFloat(*header, config); -- case Value::Type::TAG: // We explicitly don't support TAG. -+ case Value::Type::TAG: -+ if (config.parse_tags) { -+ return ReadTagContent(*header, config, max_nesting_level); -+ } -+ break; - case Value::Type::NONE: - case Value::Type::INVALID_UTF8: - break; -@@ -347,6 +351,17 @@ absl::optional Reader::ReadByteStringContent( - return Value(std::move(cbor_byte_string)); - } - -+absl::optional Reader::ReadTagContent( -+ const Reader::DataItemHeader& header, -+ const Config& config, -+ int max_nesting_level) { -+ auto tagged_content = DecodeCompleteDataItem(config, max_nesting_level); -+ if (tagged_content.has_value()) { -+ tagged_content.value().SetTag(header.value); -+ } -+ return tagged_content; -+} -+ - absl::optional Reader::ReadArrayContent( - const Reader::DataItemHeader& header, - const Config& config, -diff --git a/components/cbor/reader.h b/components/cbor/reader.h -index f0b43a5517528..a57e277a1bc66 100644 ---- a/components/cbor/reader.h -+++ b/components/cbor/reader.h -@@ -130,6 +130,11 @@ class CBOR_EXPORT Reader { - // during decoding will set raise the `UNSUPPORTED_FLOATING_POINT_VALUE` - // error. - bool allow_floating_point = false; -+ -+ // If the parser encounters a TAG element, should it be parsed out and -+ // the tag value saved (true), or should the entire node and its content -+ // be discarded (false) -+ bool parse_tags = false; - }; - - Reader(const Reader&) = delete; -@@ -204,6 +209,9 @@ class CBOR_EXPORT Reader { - absl::optional ReadMapContent(const DataItemHeader& header, - const Config& config, - int max_nesting_level); -+ absl::optional ReadTagContent(const DataItemHeader& header, -+ const Config& config, -+ int max_nesting_level); - absl::optional ReadByte(); - absl::optional> ReadBytes(uint64_t num_bytes); - bool IsKeyInOrder(const Value& new_key, -diff --git a/components/cbor/reader_unittest.cc b/components/cbor/reader_unittest.cc -index 83d44a48d6dfa..a6ec5299b3241 100644 ---- a/components/cbor/reader_unittest.cc -+++ b/components/cbor/reader_unittest.cc -@@ -1451,5 +1451,42 @@ TEST(CBORReaderTest, AllowInvalidUTF8) { - EXPECT_FALSE(cbor); - EXPECT_EQ(Reader::DecoderError::INVALID_UTF8, error); - } -+TEST(CBORReaderTest, RejectsTagUnderDefaultConfig) { -+ static const uint8_t kTaggedCbor[] = { -+ 0xd8, 0x2a, 0x58, 0x25, 0x00, 0x01, 0x71, 0x12, 0x20, 0x69, 0xea, 0x07, -+ 0x40, 0xf9, 0x80, 0x7a, 0x28, 0xf4, 0xd9, 0x32, 0xc6, 0x2e, 0x7c, 0x1c, -+ 0x83, 0xbe, 0x05, 0x5e, 0x55, 0x07, 0x2c, 0x90, 0x26, 0x6a, 0xb3, 0xe7, -+ 0x9d, 0xf6, 0x3a, 0x36, 0x5b -+ }; -+ Reader::Config config; -+ absl::optional cbor = Reader::Read(kTaggedCbor, config); -+ EXPECT_FALSE(cbor.has_value()); -+} -+TEST(CBORReaderTest, ReadsTagWhenConfiguredToDoSo) { -+ static const uint8_t kTaggedCbor[] = { -+ 0xd8, 0x2a, 0x58, 0x25, 0x00, 0x01, 0x71, 0x12, 0x20, 0x69, 0xea, 0x07, -+ 0x40, 0xf9, 0x80, 0x7a, 0x28, 0xf4, 0xd9, 0x32, 0xc6, 0x2e, 0x7c, 0x1c, -+ 0x83, 0xbe, 0x05, 0x5e, 0x55, 0x07, 0x2c, 0x90, 0x26, 0x6a, 0xb3, 0xe7, -+ 0x9d, 0xf6, 0x3a, 0x36, 0x5b -+ }; -+ Reader::Config config; -+ config.parse_tags = true; -+ absl::optional cbor = Reader::Read(kTaggedCbor, config); -+ EXPECT_TRUE(cbor.has_value()); -+ auto& v = cbor.value(); -+ EXPECT_TRUE(v.has_tag()); -+ EXPECT_EQ(v.GetTag(),42UL); -+ EXPECT_TRUE(v.is_bytestring()); -+ EXPECT_EQ(v.type(), Value::Type::BYTE_STRING); -+ auto& bytes = v.GetBytestring(); -+ EXPECT_EQ(bytes.size(), 37UL); -+ EXPECT_EQ(bytes.at(0), 0x00);//identity multibase (e.g. not base-encoded, bytes are themselves) -+ EXPECT_EQ(bytes.at(1), 0x01);//CID version 1 -+ EXPECT_EQ(bytes.at(2), 0x71);//codec = dag-cbor -+ EXPECT_EQ(bytes.at(3), 0x12);//multihash = 18 = sha2-256 -+ EXPECT_EQ(bytes.at(4), 0x20);//hash length = 32 bytes -+ EXPECT_EQ(bytes.at(5), 0x69);//first byte of hash digest -+ EXPECT_EQ(bytes.at(36),0x5b);//last byte of hash digest -+} - - } // namespace cbor -diff --git a/components/cbor/values.cc b/components/cbor/values.cc -index 02498209c820e..34055aef24cfe 100644 ---- a/components/cbor/values.cc -+++ b/components/cbor/values.cc -@@ -66,32 +66,34 @@ Value::Value(Type type) : type_(type) { - NOTREACHED(); - } - --Value::Value(SimpleValue in_simple) -- : type_(Type::SIMPLE_VALUE), simple_value_(in_simple) { -+Value::Value(SimpleValue in_simple, uint64_t tag) -+ : type_(Type::SIMPLE_VALUE), simple_value_(in_simple), tag_(tag) { - CHECK(static_cast(in_simple) >= 20 && static_cast(in_simple) <= 23); - } - --Value::Value(bool boolean_value) : type_(Type::SIMPLE_VALUE) { -+Value::Value(bool boolean_value, uint64_t tag) : type_(Type::SIMPLE_VALUE), tag_(tag) { - simple_value_ = boolean_value ? Value::SimpleValue::TRUE_VALUE - : Value::SimpleValue::FALSE_VALUE; - } - --Value::Value(double float_value) -- : type_(Type::FLOAT_VALUE), float_value_(float_value) {} -+Value::Value(double float_value, uint64_t tag) -+ : type_(Type::FLOAT_VALUE), float_value_(float_value), tag_(tag) {} - --Value::Value(int integer_value) -- : Value(base::checked_cast(integer_value)) {} -+Value::Value(int integer_value, uint64_t tag) -+ : Value(base::checked_cast(integer_value), tag) {} - --Value::Value(int64_t integer_value) : integer_value_(integer_value) { -+Value::Value(int64_t integer_value, uint64_t tag) : integer_value_(integer_value), tag_(tag) { - type_ = integer_value >= 0 ? Type::UNSIGNED : Type::NEGATIVE; - } - --Value::Value(base::span in_bytes) -+Value::Value(base::span in_bytes, uint64_t tag) - : type_(Type::BYTE_STRING), -- bytestring_value_(in_bytes.begin(), in_bytes.end()) {} -+ bytestring_value_(in_bytes.begin(), in_bytes.end()), -+ tag_(tag) -+ {} - --Value::Value(base::span in_bytes, Type type) -- : type_(type), bytestring_value_(in_bytes.begin(), in_bytes.end()) { -+Value::Value(base::span in_bytes, Type type, uint64_t tag) -+ : type_(type), bytestring_value_(in_bytes.begin(), in_bytes.end()), tag_(tag) { - DCHECK(type_ == Type::BYTE_STRING || type_ == Type::INVALID_UTF8); - } - -@@ -117,7 +119,8 @@ Value::Value(std::string&& in_string, Type type) noexcept : type_(type) { - } - } - --Value::Value(base::StringPiece in_string, Type type) : type_(type) { -+Value::Value(base::StringPiece in_string, Type type, uint64_t tag) -+: type_(type), tag_(tag) { - switch (type_) { - case Type::STRING: - new (&string_value_) std::string(); -@@ -133,16 +136,18 @@ Value::Value(base::StringPiece in_string, Type type) : type_(type) { - } - } - --Value::Value(const ArrayValue& in_array) : type_(Type::ARRAY), array_value_() { -+Value::Value(const ArrayValue& in_array, uint64_t tag) -+: type_(Type::ARRAY), array_value_(), tag_(tag) { - array_value_.reserve(in_array.size()); - for (const auto& val : in_array) - array_value_.emplace_back(val.Clone()); - } - --Value::Value(ArrayValue&& in_array) noexcept -- : type_(Type::ARRAY), array_value_(std::move(in_array)) {} -+Value::Value(ArrayValue&& in_array, uint64_t tag) noexcept -+ : type_(Type::ARRAY), array_value_(std::move(in_array)), tag_(tag) {} - --Value::Value(const MapValue& in_map) : type_(Type::MAP), map_value_() { -+Value::Value(const MapValue& in_map, uint64_t tag) -+: type_(Type::MAP), map_value_(), tag_(tag) { - map_value_.reserve(in_map.size()); - for (const auto& it : in_map) - map_value_.emplace_hint(map_value_.end(), it.first.Clone(), -@@ -168,31 +173,36 @@ Value Value::Clone() const { - case Type::NONE: - return Value(); - case Type::INVALID_UTF8: -- return Value(bytestring_value_, Type::INVALID_UTF8); -+ return Value(bytestring_value_, Type::INVALID_UTF8, tag_); - case Type::UNSIGNED: - case Type::NEGATIVE: -- return Value(integer_value_); -+ return Value(integer_value_, tag_); - case Type::BYTE_STRING: -- return Value(bytestring_value_); -+ return Value(bytestring_value_, tag_); - case Type::STRING: -- return Value(string_value_); -+ return Value(string_value_, Type::STRING, tag_); - case Type::ARRAY: -- return Value(array_value_); -+ return Value(array_value_, tag_); - case Type::MAP: -- return Value(map_value_); -+ return Value(map_value_, tag_); - case Type::TAG: - NOTREACHED() << constants::kUnsupportedMajorType; - return Value(); - case Type::SIMPLE_VALUE: -- return Value(simple_value_); -+ return Value(simple_value_, tag_); - case Type::FLOAT_VALUE: -- return Value(float_value_); -+ return Value(float_value_, tag_); - } - - NOTREACHED(); - return Value(); - } - -+Value& Value::SetTag(uint64_t tag) noexcept { -+ tag_ = tag; -+ return *this; -+} -+ - Value::SimpleValue Value::GetSimpleValue() const { - CHECK(is_simple()); - return simple_value_; -@@ -258,9 +268,14 @@ const Value::BinaryValue& Value::GetInvalidUTF8() const { - return bytestring_value_; - } - -+uint64_t Value::GetTag() const { -+ CHECK(has_tag()); -+ return tag_; -+} -+ - void Value::InternalMoveConstructFrom(Value&& that) { - type_ = that.type_; -- -+ tag_ = that.tag_; - switch (type_) { - case Type::UNSIGNED: - case Type::NEGATIVE: -diff --git a/components/cbor/values.h b/components/cbor/values.h -index d81ef5607c55a..10216a8dcdc57 100644 ---- a/components/cbor/values.h -+++ b/components/cbor/values.h -@@ -127,28 +127,29 @@ class CBOR_EXPORT Value { - - explicit Value(Type type); - -- explicit Value(SimpleValue in_simple); -- explicit Value(bool boolean_value); -- explicit Value(double in_float); -+ explicit Value(SimpleValue in_simple, uint64_t tag = NO_TAG); -+ explicit Value(bool boolean_value, uint64_t tag = NO_TAG); -+ explicit Value(double in_float, uint64_t tag = NO_TAG); - -- explicit Value(int integer_value); -- explicit Value(int64_t integer_value); -+ explicit Value(int integer_value, uint64_t tag = NO_TAG); -+ explicit Value(int64_t integer_value, uint64_t tag = NO_TAG); - explicit Value(uint64_t integer_value) = delete; - -- explicit Value(base::span in_bytes); -+ explicit Value(base::span in_bytes, uint64_t tag = NO_TAG); - explicit Value(BinaryValue&& in_bytes) noexcept; - - explicit Value(const char* in_string, Type type = Type::STRING); - explicit Value(std::string&& in_string, Type type = Type::STRING) noexcept; -- explicit Value(base::StringPiece in_string, Type type = Type::STRING); -+ explicit Value(base::StringPiece in_string, Type type = Type::STRING, uint64_t tag = NO_TAG); - -- explicit Value(const ArrayValue& in_array); -- explicit Value(ArrayValue&& in_array) noexcept; -+ explicit Value(const ArrayValue& in_array, uint64_t tag = NO_TAG); -+ explicit Value(ArrayValue&& in_array, uint64_t tag = NO_TAG) noexcept; - -- explicit Value(const MapValue& in_map); -+ explicit Value(const MapValue& in_map, uint64_t tag = NO_TAG); - explicit Value(MapValue&& in_map) noexcept; - - Value& operator=(Value&& that) noexcept; -+ Value& SetTag(uint64_t) noexcept; - - Value(const Value&) = delete; - Value& operator=(const Value&) = delete; -@@ -179,6 +180,7 @@ class CBOR_EXPORT Value { - bool is_string() const { return type() == Type::STRING; } - bool is_array() const { return type() == Type::ARRAY; } - bool is_map() const { return type() == Type::MAP; } -+ bool has_tag() const { return tag_ != NO_TAG; } - - // These will all fatally assert if the type doesn't match. - SimpleValue GetSimpleValue() const; -@@ -194,12 +196,13 @@ class CBOR_EXPORT Value { - const ArrayValue& GetArray() const; - const MapValue& GetMap() const; - const BinaryValue& GetInvalidUTF8() const; -+ uint64_t GetTag() const; - - private: - friend class Reader; - // This constructor allows INVALID_UTF8 values to be created, which only - // |Reader| and InvalidUTF8StringValueForTesting() may do. -- Value(base::span in_bytes, Type type); -+ Value(base::span in_bytes, Type type, uint64_t tag = NO_TAG); - - Type type_; - -@@ -213,6 +216,11 @@ class CBOR_EXPORT Value { - MapValue map_value_; - }; - -+ //This value specified as Invalid, -+ // used here to represent absence of TAG -+ constexpr static uint64_t NO_TAG = 0xFFFF; -+ uint64_t tag_ = NO_TAG; -+ - void InternalMoveConstructFrom(Value&& that); - void InternalCleanup(); - }; -diff --git a/components/cbor/writer.cc b/components/cbor/writer.cc -index bb22754d36a07..aae4027836377 100644 ---- a/components/cbor/writer.cc -+++ b/components/cbor/writer.cc -@@ -47,6 +47,9 @@ bool Writer::EncodeCBOR(const Value& node, - if (max_nesting_level < 0) - return false; - -+ if (node.has_tag()) { -+ StartItem(Value::Type::TAG, node.GetTag()); -+ } - switch (node.type()) { - case Value::Type::NONE: { - StartItem(Value::Type::BYTE_STRING, 0); -diff --git a/components/cbor/writer_unittest.cc b/components/cbor/writer_unittest.cc -index e3bffe20734bc..0ed569ae164a0 100644 ---- a/components/cbor/writer_unittest.cc -+++ b/components/cbor/writer_unittest.cc -@@ -522,4 +522,31 @@ TEST(CBORWriterTest, OverlyNestedCBOR) { - EXPECT_FALSE(Writer::Write(Value(map), 4).has_value()); - } - -+TEST(CBORWriterTest, CanWriteTag) { -+ std::array content{ -+ 0x00, 0x01, 0x71, 0x12, 0x20, -+ 0x69, 0xea, 0x07, 0x40, 0xf9, -+ 0x80, 0x7a, 0x28, 0xf4, 0xd9, -+ 0x32, 0xc6, 0x2e, 0x7c, 0x1c, -+ 0x83, 0xbe, 0x05, 0x5e, 0x55, -+ 0x07, 0x2c, 0x90, 0x26, 0x6a, -+ 0xb3, 0xe7, 0x9d, 0xf6, 0x3a, -+ 0x36, 0x5b -+ }; -+ Value to_write(content); -+ to_write.SetTag(42); -+ auto result = Writer::Write(to_write); -+ EXPECT_TRUE(result.has_value()); -+ auto& bytes = result.value(); -+ EXPECT_EQ(bytes.size(), 41UL); -+ EXPECT_EQ(bytes.at(0), 0xd8); -+ EXPECT_EQ(bytes.at(1), 0x2a); -+ EXPECT_EQ(bytes.at(2), 0x58); -+ EXPECT_EQ(bytes.at(3), 0x25); -+ for (auto i = 0UL; i < content.size(); ++i) { -+ ASSERT_LT(i + 4UL, bytes.size()); -+ ASSERT_EQ(content.at(i), bytes.at(i+4UL)); -+ } -+} -+ - } // namespace cbor -diff --git a/components/open_from_clipboard/clipboard_recent_content_generic.cc b/components/open_from_clipboard/clipboard_recent_content_generic.cc -index 4dcafecbc66c6..d205209c08162 100644 ---- a/components/open_from_clipboard/clipboard_recent_content_generic.cc -+++ b/components/open_from_clipboard/clipboard_recent_content_generic.cc -@@ -20,7 +20,7 @@ - namespace { - // Schemes appropriate for suggestion by ClipboardRecentContent. - const char* kAuthorizedSchemes[] = { -- url::kAboutScheme, url::kDataScheme, url::kHttpScheme, url::kHttpsScheme, -+ url::kAboutScheme, url::kDataScheme, url::kHttpScheme, url::kHttpsScheme, "ipfs", "ipns" - // TODO(mpearson): add support for chrome:// URLs. Right now the scheme - // for that lives in content and is accessible via - // GetEmbedderRepresentationOfAboutScheme() or content::kChromeUIScheme -diff --git a/net/dns/dns_config_service_linux.cc b/net/dns/dns_config_service_linux.cc -index 5273da5190277..12b28b86a4c00 100644 ---- a/net/dns/dns_config_service_linux.cc -+++ b/net/dns/dns_config_service_linux.cc -@@ -272,11 +272,11 @@ bool IsNsswitchConfigCompatible( - // Ignore any entries after `kDns` because Chrome will fallback to the - // system resolver if a result was not found in DNS. - return true; -- -+ case NsswitchReader::Service::kResolve: -+ break; - case NsswitchReader::Service::kMdns: - case NsswitchReader::Service::kMdns4: - case NsswitchReader::Service::kMdns6: -- case NsswitchReader::Service::kResolve: - case NsswitchReader::Service::kNis: - RecordIncompatibleNsswitchReason( - IncompatibleNsswitchReason::kIncompatibleService, -diff --git a/third_party/blink/renderer/platform/weborigin/scheme_registry.cc b/third_party/blink/renderer/platform/weborigin/scheme_registry.cc -index 4eadf46ea0c24..d62fc7fb14e01 100644 ---- a/third_party/blink/renderer/platform/weborigin/scheme_registry.cc -+++ b/third_party/blink/renderer/platform/weborigin/scheme_registry.cc -@@ -67,7 +67,7 @@ class URLSchemesRegistry final { - // is considered secure. Additional checks are performed to ensure that - // other http pages are filtered out. - service_worker_schemes({"http", "https"}), -- fetch_api_schemes({"http", "https"}), -+ fetch_api_schemes({"http", "https", "ipfs", "ipns"}), - allowed_in_referrer_schemes({"http", "https"}) { - for (auto& scheme : url::GetCorsEnabledSchemes()) - cors_enabled_schemes.insert(scheme.c_str()); -diff --git a/url/BUILD.gn b/url/BUILD.gn -index c525c166979d6..ce2b1ae43c0a7 100644 ---- a/url/BUILD.gn -+++ b/url/BUILD.gn -@@ -5,6 +5,7 @@ - import("//build/buildflag_header.gni") - import("//testing/libfuzzer/fuzzer_test.gni") - import("//testing/test.gni") -+import("//third_party/ipfs_client/args.gni") - import("features.gni") - - import("//build/config/cronet/config.gni") -@@ -67,6 +68,7 @@ component("url") { - public_deps = [ - "//base", - "//build:robolectric_buildflags", -+ "//third_party/ipfs_client:ipfs_buildflags", - ] - - configs += [ "//build/config/compiler:wexit_time_destructors" ] -@@ -89,6 +91,11 @@ component("url") { - public_configs = [ "//third_party/jdk" ] - } - -+ if (enable_ipfs) { -+ sources += [ "url_canon_ipfs.cc" ] -+ deps += [ "//third_party/ipfs_client:ipfs_client" ] -+ } -+ - if (is_win) { - # Don't conflict with Windows' "url.dll". - output_name = "url_lib" -diff --git a/url/url_canon.h b/url/url_canon.h -index d3a7fabf09fa8..06db17242248f 100644 ---- a/url/url_canon.h -+++ b/url/url_canon.h -@@ -697,6 +697,23 @@ bool CanonicalizeMailtoURL(const char16_t* spec, - CanonOutput* output, - Parsed* new_parsed); - -+COMPONENT_EXPORT(URL) -+bool CanonicalizeIpfsURL(const char* spec, -+ int spec_len, -+ const Parsed& parsed, -+ SchemeType scheme_type, -+ CharsetConverter* query_converter, -+ CanonOutput* output, -+ Parsed* new_parsed); -+COMPONENT_EXPORT(URL) -+bool CanonicalizeIpfsURL(const char16_t* spec, -+ int spec_len, -+ const Parsed& parsed, -+ SchemeType scheme_type, -+ CharsetConverter* query_converter, -+ CanonOutput* output, -+ Parsed* new_parsed); -+ - // Part replacer -------------------------------------------------------------- - - // Internal structure used for storing separate strings for each component. -diff --git a/url/url_canon_ipfs.cc b/url/url_canon_ipfs.cc -new file mode 100644 -index 0000000000000..da3a5f032b5e8 ---- /dev/null -+++ b/url/url_canon_ipfs.cc -@@ -0,0 +1,72 @@ -+#include "url_canon_internal.h" -+ -+#include -+#include -+ -+#include -+ -+namespace m = libp2p::multi; -+using Cid = m::ContentIdentifier; -+using CidCodec = m::ContentIdentifierCodec; -+ -+bool url::CanonicalizeIpfsURL(const char* spec, -+ int spec_len, -+ const Parsed& parsed, -+ SchemeType scheme_type, -+ CharsetConverter* charset_converter, -+ CanonOutput* output, -+ Parsed* output_parsed) { -+ if ( spec_len < 1 || !spec ) { -+ return false; -+ } -+ if ( parsed.host.len < 1 ) { -+ return false; -+ } -+ std::string cid_str{ spec + parsed.host.begin, static_cast(parsed.host.len) }; -+ auto maybe_cid = CidCodec::fromString(cid_str); -+ if ( !maybe_cid.has_value() ) { -+ auto e = libp2p::multi::Stringify(maybe_cid.error()); -+ std::ostringstream err; -+ err << e << ' ' -+ << std::string_view{spec,static_cast(spec_len)}; -+ maybe_cid = ipfs::id_cid::forText( err.str() ); -+ } -+ auto cid = maybe_cid.value(); -+ if ( cid.version == Cid::Version::V0 ) { -+ //TODO dcheck content_type == DAG_PB && content_address.getType() == sha256 -+ cid = Cid{ -+ Cid::Version::V1, -+ cid.content_type, -+ cid.content_address -+ }; -+ } -+ auto as_str = CidCodec::toString(cid); -+ if ( !as_str.has_value() ) { -+ return false; -+ } -+ std::string stdurl{ spec, static_cast(parsed.host.begin) }; -+ stdurl.append( as_str.value() ); -+ stdurl.append( spec + parsed.host.end(), spec_len - parsed.host.end() ); -+ spec = stdurl.data(); -+ spec_len = static_cast(stdurl.size()); -+ Parsed parsed_input; -+ ParseStandardURL(spec, spec_len, &parsed_input); -+ return CanonicalizeStandardURL( -+ spec, spec_len, -+ parsed_input, -+ scheme_type, -+ charset_converter, -+ output, output_parsed -+ ); -+} -+bool url::CanonicalizeIpfsURL(const char16_t* spec, -+ int spec_len, -+ const Parsed& parsed, -+ SchemeType scheme_type, -+ CharsetConverter* query_converter, -+ CanonOutput* output, -+ Parsed* new_parsed) { -+ RawCanonOutput<2048> as8; -+ ConvertUTF16ToUTF8(spec, spec_len, &as8); -+ return CanonicalizeIpfsURL(as8.data(), as8.length(), parsed, scheme_type, query_converter, output, new_parsed); -+} -diff --git a/url/url_util.cc b/url/url_util.cc -index 9258cfcfada47..daf10e4c3b741 100644 ---- a/url/url_util.cc -+++ b/url/url_util.cc -@@ -277,6 +277,12 @@ bool DoCanonicalize(const CHAR* spec, - charset_converter, output, - output_parsed); - -+ } else if (DoCompareSchemeComponent(spec, scheme, "ipfs")) { -+ // Switch multibase away from case-sensitive ones before continuing canonicalization. -+ ParseStandardURL(spec, spec_len, &parsed_input); -+ success = CanonicalizeIpfsURL(spec, spec_len, parsed_input, scheme_type, -+ charset_converter, output, output_parsed); -+ - } else if (DoIsStandard(spec, scheme, &scheme_type)) { - // All "normal" URLs. - ParseStandardURL(spec, spec_len, &parsed_input); diff --git a/component/patches/121.0.6167.8.patch b/component/patches/122.0.6226.2.patch similarity index 87% rename from component/patches/121.0.6167.8.patch rename to component/patches/122.0.6226.2.patch index 5c183d79..d09aad3b 100644 --- a/component/patches/121.0.6167.8.patch +++ b/component/patches/122.0.6226.2.patch @@ -1,8 +1,8 @@ diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn -index 6572022524828..d5956c31511df 100644 +index 516f8d3bc275f..3188eaaf003a1 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn -@@ -40,6 +40,7 @@ import("//rlz/buildflags/buildflags.gni") +@@ -39,6 +39,7 @@ import("//rlz/buildflags/buildflags.gni") import("//sandbox/features.gni") import("//testing/libfuzzer/fuzzer_test.gni") import("//third_party/blink/public/public_features.gni") @@ -10,11 +10,15 @@ index 6572022524828..d5956c31511df 100644 import("//third_party/protobuf/proto_library.gni") import("//third_party/webrtc/webrtc.gni") import("//third_party/widevine/cdm/widevine.gni") -@@ -2632,6 +2633,10 @@ static_library("browser") { +@@ -2618,6 +2619,14 @@ static_library("browser") { ] } + if (enable_ipfs) { ++ sources += [ ++ "ipfs_extra_parts.cc", ++ "ipfs_extra_parts.h", ++ ] + deps += [ "//components/ipfs" ] + } + @@ -22,18 +26,18 @@ index 6572022524828..d5956c31511df 100644 deps += [ "//chrome/browser/screen_ai:screen_ai_dlc_installer" ] } diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc -index b3954a5fec88e..162b64711b3ca 100644 +index cba0c0e2bc1c2..91e14f34800de 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc -@@ -212,6 +212,7 @@ +@@ -214,6 +214,7 @@ #include "third_party/blink/public/common/features_generated.h" #include "third_party/blink/public/common/forcedark/forcedark_switches.h" #include "third_party/blink/public/common/switches.h" +#include "third_party/ipfs_client/ipfs_buildflags.h" #include "ui/accessibility/accessibility_features.h" #include "ui/accessibility/accessibility_switches.h" - #include "ui/base/ui_base_features.h" -@@ -312,6 +313,10 @@ + #include "ui/base/ozone_buildflags.h" +@@ -310,6 +311,10 @@ #include "extensions/common/switches.h" #endif // BUILDFLAG(ENABLE_EXTENSIONS) @@ -44,7 +48,7 @@ index b3954a5fec88e..162b64711b3ca 100644 #if BUILDFLAG(ENABLE_PDF) #include "pdf/pdf_features.h" #endif -@@ -9846,6 +9851,14 @@ const FeatureEntry kFeatureEntries[] = { +@@ -9379,6 +9384,14 @@ const FeatureEntry kFeatureEntries[] = { flag_descriptions::kOmitCorsClientCertDescription, kOsAll, FEATURE_VALUE_TYPE(network::features::kOmitCorsClientCert)}, @@ -110,10 +114,10 @@ index 4c88614c68c25..f8bb12a3b0c2e 100644 // Also check for schemes registered via registerProtocolHandler(), which diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc -index a9b87ac2fcd74..987686c621664 100644 +index 23ee785c34a30..8550b43c61f2f 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc -@@ -377,6 +377,7 @@ +@@ -376,6 +376,7 @@ #include "third_party/blink/public/common/switches.h" #include "third_party/blink/public/mojom/browsing_topics/browsing_topics.mojom.h" #include "third_party/blink/public/public_buildflags.h" @@ -121,11 +125,12 @@ index a9b87ac2fcd74..987686c621664 100644 #include "third_party/widevine/cdm/buildflags.h" #include "ui/base/clipboard/clipboard_format_type.h" #include "ui/base/l10n/l10n_util.h" -@@ -499,6 +500,12 @@ +@@ -499,6 +500,13 @@ #include "chrome/browser/fuchsia/chrome_browser_main_parts_fuchsia.h" #endif +#if BUILDFLAG(ENABLE_IPFS) ++#include "chrome/browser/ipfs_extra_parts.h" +#include "components/ipfs/interceptor.h" +#include "components/ipfs/ipfs_features.h" +#include "components/ipfs/url_loader_factory.h" @@ -134,7 +139,19 @@ index a9b87ac2fcd74..987686c621664 100644 #if BUILDFLAG(IS_CHROMEOS) #include "base/debug/leak_annotations.h" #include "chrome/browser/apps/app_service/app_install/app_install_navigation_throttle.h" -@@ -6157,12 +6164,23 @@ void ChromeContentBrowserClient:: +@@ -1711,6 +1719,11 @@ ChromeContentBrowserClient::CreateBrowserMainParts(bool is_integration_test) { + main_parts->AddParts( + std::make_unique()); + ++#if BUILDFLAG(ENABLE_IPFS) ++ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { ++ main_parts->AddParts(std::make_unique()); ++ } ++#endif + return main_parts; + } + +@@ -6057,12 +6070,25 @@ void ChromeContentBrowserClient:: const absl::optional& request_initiator_origin, NonNetworkURLLoaderFactoryMap* factories) { #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(ENABLE_EXTENSIONS) || \ @@ -144,23 +161,24 @@ index a9b87ac2fcd74..987686c621664 100644 RenderFrameHost::FromID(render_process_id, render_frame_id); WebContents* web_contents = WebContents::FromRenderFrameHost(frame_host); #endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(ENABLE_EXTENSIONS) || \ -- // !BUILDFLAG(IS_ANDROID) -+ // !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_IPFS) + // !BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(ENABLE_IPFS) + if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { + network::mojom::URLLoaderFactory* default_factory = g_browser_process->system_network_context_manager()->GetURLLoaderFactory(); ++ auto* context = web_contents->GetBrowserContext(); + ipfs::IpfsURLLoaderFactory::Create( + factories, -+ web_contents->GetBrowserContext(), ++ context, + default_factory, -+ GetSystemNetworkContext() ++ GetSystemNetworkContext(), ++ Profile::FromBrowserContext(context)->GetPrefs() + ); + } +#endif // BUILDFLAG(ENABLE_IPFS) #if BUILDFLAG(IS_CHROMEOS_ASH) if (web_contents) { -@@ -6304,6 +6322,11 @@ ChromeContentBrowserClient::WillCreateURLLoaderRequestInterceptors( +@@ -6204,6 +6230,11 @@ ChromeContentBrowserClient::WillCreateURLLoaderRequestInterceptors( scoped_refptr navigation_response_task_runner) { std::vector> interceptors; @@ -173,10 +191,10 @@ index a9b87ac2fcd74..987686c621664 100644 interceptors.push_back( std::make_unique( diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json -index 0cdc387bf20b7..10e6b164bfdbf 100644 +index ae98bbb22d81a..c85d48a4a77c1 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json -@@ -2944,6 +2944,11 @@ +@@ -2946,6 +2946,11 @@ "owners": [ "hanxi@chromium.org", "wychen@chromium.org" ], "expiry_milestone": 130 }, @@ -189,10 +207,10 @@ index 0cdc387bf20b7..10e6b164bfdbf 100644 "name": "enable-isolated-sandboxed-iframes", "owners": [ "wjmaclean@chromium.org", "alexmos@chromium.org", "creis@chromium.org" ], diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc -index 879831885f22f..03877bb2c3254 100644 +index ec5799f66a720..7be72282040c1 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc -@@ -288,6 +288,11 @@ const char kEnableBenchmarkingDescription[] = +@@ -284,6 +284,11 @@ const char kEnableBenchmarkingDescription[] = "after 3 restarts. On the third restart, the flag will appear to be off " "but the effect is still active."; @@ -205,7 +223,7 @@ index 879831885f22f..03877bb2c3254 100644 "Preloading Settings on Performance Page"; const char kPreloadingOnPerformancePageDescription[] = diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h -index 067e8d3c0b793..95ceadb06622a 100644 +index 3224ecf17e6c8..14976704c1345 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h @@ -23,6 +23,7 @@ @@ -216,7 +234,7 @@ index 067e8d3c0b793..95ceadb06622a 100644 // This file declares strings used in chrome://flags. These messages are not // translated, because instead of end-users they target Chromium developers and -@@ -179,6 +180,11 @@ extern const char kDownloadWarningImprovementsDescription[]; +@@ -176,6 +177,11 @@ extern const char kDownloadWarningImprovementsDescription[]; extern const char kEnableBenchmarkingName[]; extern const char kEnableBenchmarkingDescription[]; @@ -228,6 +246,42 @@ index 067e8d3c0b793..95ceadb06622a 100644 #if BUILDFLAG(USE_FONTATIONS_BACKEND) extern const char kFontationsFontBackendName[]; extern const char kFontationsFontBackendDescription[]; +diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc +index e4e82350ade4a..7023d6c5e07c3 100644 +--- a/chrome/browser/prefs/browser_prefs.cc ++++ b/chrome/browser/prefs/browser_prefs.cc +@@ -189,6 +189,7 @@ + #include "printing/buildflags/buildflags.h" + #include "rlz/buildflags/buildflags.h" + #include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/ipfs_client/ipfs_buildflags.h" + + #if BUILDFLAG(ENABLE_BACKGROUND_MODE) + #include "chrome/browser/background/background_mode_manager.h" +@@ -233,6 +234,11 @@ + #include "chrome/browser/pdf/pdf_pref_names.h" + #endif // BUILDFLAG(ENABLE_PDF) + ++#if BUILDFLAG(ENABLE_IPFS) ++#include "components/ipfs/ipfs_features.h" ++#include "components/ipfs/preferences.h" ++#endif ++ + #if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) + #include "chrome/browser/screen_ai/pref_names.h" + #endif +@@ -1685,6 +1691,11 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry, + IncognitoModePrefs::RegisterProfilePrefs(registry); + invalidation::PerUserTopicSubscriptionManager::RegisterProfilePrefs(registry); + invalidation::InvalidatorRegistrarWithMemory::RegisterProfilePrefs(registry); ++#if BUILDFLAG(ENABLE_IPFS) ++ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { ++ ipfs::RegisterPreferences(registry); ++ } ++#endif + language::LanguagePrefs::RegisterProfilePrefs(registry); + login_detection::prefs::RegisterProfilePrefs(registry); + lookalikes::RegisterProfilePrefs(registry); diff --git a/chrome/common/chrome_content_client.cc b/chrome/common/chrome_content_client.cc index 246ec9c5c911f..5d66d133a7907 100644 --- a/chrome/common/chrome_content_client.cc @@ -681,7 +735,7 @@ index 4eadf46ea0c24..d62fc7fb14e01 100644 for (auto& scheme : url::GetCorsEnabledSchemes()) cors_enabled_schemes.insert(scheme.c_str()); diff --git a/url/BUILD.gn b/url/BUILD.gn -index c525c166979d6..ce2b1ae43c0a7 100644 +index b5edb89f7698f..d299856674d7d 100644 --- a/url/BUILD.gn +++ b/url/BUILD.gn @@ -5,6 +5,7 @@ @@ -692,7 +746,7 @@ index c525c166979d6..ce2b1ae43c0a7 100644 import("features.gni") import("//build/config/cronet/config.gni") -@@ -67,6 +68,7 @@ component("url") { +@@ -68,6 +69,7 @@ component("url") { public_deps = [ "//base", "//build:robolectric_buildflags", @@ -700,7 +754,7 @@ index c525c166979d6..ce2b1ae43c0a7 100644 ] configs += [ "//build/config/compiler:wexit_time_destructors" ] -@@ -89,6 +91,11 @@ component("url") { +@@ -90,6 +92,11 @@ component("url") { public_configs = [ "//third_party/jdk" ] } @@ -713,10 +767,10 @@ index c525c166979d6..ce2b1ae43c0a7 100644 # Don't conflict with Windows' "url.dll". output_name = "url_lib" diff --git a/url/url_canon.h b/url/url_canon.h -index d3a7fabf09fa8..06db17242248f 100644 +index 8c48f9825d8cf..b9ad961e1b123 100644 --- a/url/url_canon.h +++ b/url/url_canon.h -@@ -697,6 +697,23 @@ bool CanonicalizeMailtoURL(const char16_t* spec, +@@ -804,6 +804,23 @@ bool CanonicalizeMailtoURL(const char16_t* spec, CanonOutput* output, Parsed* new_parsed); @@ -742,21 +796,17 @@ index d3a7fabf09fa8..06db17242248f 100644 // Internal structure used for storing separate strings for each component. diff --git a/url/url_canon_ipfs.cc b/url/url_canon_ipfs.cc new file mode 100644 -index 0000000000000..da3a5f032b5e8 +index 0000000000000..d7c9fdc78eb91 --- /dev/null +++ b/url/url_canon_ipfs.cc -@@ -0,0 +1,72 @@ +@@ -0,0 +1,55 @@ +#include "url_canon_internal.h" + -+#include ++#include +#include + +#include + -+namespace m = libp2p::multi; -+using Cid = m::ContentIdentifier; -+using CidCodec = m::ContentIdentifierCodec; -+ +bool url::CanonicalizeIpfsURL(const char* spec, + int spec_len, + const Parsed& parsed, @@ -770,37 +820,24 @@ index 0000000000000..da3a5f032b5e8 + if ( parsed.host.len < 1 ) { + return false; + } -+ std::string cid_str{ spec + parsed.host.begin, static_cast(parsed.host.len) }; -+ auto maybe_cid = CidCodec::fromString(cid_str); -+ if ( !maybe_cid.has_value() ) { -+ auto e = libp2p::multi::Stringify(maybe_cid.error()); -+ std::ostringstream err; -+ err << e << ' ' -+ << std::string_view{spec,static_cast(spec_len)}; -+ maybe_cid = ipfs::id_cid::forText( err.str() ); -+ } -+ auto cid = maybe_cid.value(); -+ if ( cid.version == Cid::Version::V0 ) { -+ //TODO dcheck content_type == DAG_PB && content_address.getType() == sha256 -+ cid = Cid{ -+ Cid::Version::V1, -+ cid.content_type, -+ cid.content_address -+ }; ++ std::string_view cid_str{ spec + parsed.host.begin, static_cast(parsed.host.len) }; ++ auto cid = ipfs::Cid(cid_str); ++ if ( !cid.valid() ) { ++ cid = ipfs::id_cid::forText( std::string{cid_str} + " is not a valid CID." ); + } -+ auto as_str = CidCodec::toString(cid); -+ if ( !as_str.has_value() ) { ++ auto as_str = cid.to_string(); ++ if ( as_str.empty() ) { + return false; + } + std::string stdurl{ spec, static_cast(parsed.host.begin) }; -+ stdurl.append( as_str.value() ); ++ stdurl.append( as_str ); + stdurl.append( spec + parsed.host.end(), spec_len - parsed.host.end() ); + spec = stdurl.data(); + spec_len = static_cast(stdurl.size()); + Parsed parsed_input; + ParseStandardURL(spec, spec_len, &parsed_input); + return CanonicalizeStandardURL( -+ spec, spec_len, ++ spec, + parsed_input, + scheme_type, + charset_converter, @@ -819,19 +856,25 @@ index 0000000000000..da3a5f032b5e8 + return CanonicalizeIpfsURL(as8.data(), as8.length(), parsed, scheme_type, query_converter, output, new_parsed); +} diff --git a/url/url_util.cc b/url/url_util.cc -index 9258cfcfada47..daf10e4c3b741 100644 +index 6f83f33c01c6b..a248e11c49445 100644 --- a/url/url_util.cc +++ b/url/url_util.cc -@@ -277,6 +277,12 @@ bool DoCanonicalize(const CHAR* spec, - charset_converter, output, - output_parsed); - +@@ -273,8 +273,15 @@ bool DoCanonicalize(const CHAR* spec, + } else if (DoCompareSchemeComponent(spec, scheme, url::kFileSystemScheme)) { + // Filesystem URLs are special. + ParseFileSystemURL(spec, spec_len, &parsed_input); +- success = CanonicalizeFileSystemURL(spec, parsed_input, charset_converter, +- output, output_parsed); ++ success = CanonicalizeFileSystemURL(spec, parsed_input, ++ charset_converter, output, ++ output_parsed); ++ + } else if (DoCompareSchemeComponent(spec, scheme, "ipfs")) { + // Switch multibase away from case-sensitive ones before continuing canonicalization. + ParseStandardURL(spec, spec_len, &parsed_input); + success = CanonicalizeIpfsURL(spec, spec_len, parsed_input, scheme_type, + charset_converter, output, output_parsed); -+ + } else if (DoIsStandard(spec, scheme, &scheme_type)) { // All "normal" URLs. - ParseStandardURL(spec, spec_len, &parsed_input); + diff --git a/component/patches/121.0.6156.3.patch b/component/patches/122.0.6236.2.patch similarity index 88% rename from component/patches/121.0.6156.3.patch rename to component/patches/122.0.6236.2.patch index eacc5115..d16ef2fc 100644 --- a/component/patches/121.0.6156.3.patch +++ b/component/patches/122.0.6236.2.patch @@ -1,8 +1,8 @@ diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn -index 4ec6cf79062ea..dc9d3637464bb 100644 +index 9e7f057a1c35d..846fe6d119c66 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn -@@ -40,6 +40,7 @@ import("//rlz/buildflags/buildflags.gni") +@@ -39,6 +39,7 @@ import("//rlz/buildflags/buildflags.gni") import("//sandbox/features.gni") import("//testing/libfuzzer/fuzzer_test.gni") import("//third_party/blink/public/public_features.gni") @@ -10,11 +10,15 @@ index 4ec6cf79062ea..dc9d3637464bb 100644 import("//third_party/protobuf/proto_library.gni") import("//third_party/webrtc/webrtc.gni") import("//third_party/widevine/cdm/widevine.gni") -@@ -2630,6 +2631,10 @@ static_library("browser") { +@@ -2619,6 +2620,14 @@ static_library("browser") { ] } + if (enable_ipfs) { ++ sources += [ ++ "ipfs_extra_parts.cc", ++ "ipfs_extra_parts.h", ++ ] + deps += [ "//components/ipfs" ] + } + @@ -22,18 +26,18 @@ index 4ec6cf79062ea..dc9d3637464bb 100644 deps += [ "//chrome/browser/screen_ai:screen_ai_dlc_installer" ] } diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc -index af66a784e629e..36d2a2de39e38 100644 +index 61699cdf19305..5b99fd9811eb0 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc -@@ -209,6 +209,7 @@ +@@ -214,6 +214,7 @@ #include "third_party/blink/public/common/features_generated.h" #include "third_party/blink/public/common/forcedark/forcedark_switches.h" #include "third_party/blink/public/common/switches.h" +#include "third_party/ipfs_client/ipfs_buildflags.h" #include "ui/accessibility/accessibility_features.h" #include "ui/accessibility/accessibility_switches.h" - #include "ui/base/ui_base_features.h" -@@ -309,6 +310,10 @@ + #include "ui/base/ozone_buildflags.h" +@@ -310,6 +311,10 @@ #include "extensions/common/switches.h" #endif // BUILDFLAG(ENABLE_EXTENSIONS) @@ -44,7 +48,7 @@ index af66a784e629e..36d2a2de39e38 100644 #if BUILDFLAG(ENABLE_PDF) #include "pdf/pdf_features.h" #endif -@@ -9855,6 +9860,14 @@ const FeatureEntry kFeatureEntries[] = { +@@ -9413,6 +9418,14 @@ const FeatureEntry kFeatureEntries[] = { flag_descriptions::kOmitCorsClientCertDescription, kOsAll, FEATURE_VALUE_TYPE(network::features::kOmitCorsClientCert)}, @@ -110,7 +114,7 @@ index 4c88614c68c25..f8bb12a3b0c2e 100644 // Also check for schemes registered via registerProtocolHandler(), which diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc -index 5746f7c712514..2c56ac60c0e74 100644 +index a9f0eb5ab9151..8956d3ca5519e 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc @@ -374,6 +374,7 @@ @@ -121,11 +125,12 @@ index 5746f7c712514..2c56ac60c0e74 100644 #include "third_party/widevine/cdm/buildflags.h" #include "ui/base/clipboard/clipboard_format_type.h" #include "ui/base/l10n/l10n_util.h" -@@ -496,6 +497,12 @@ +@@ -497,6 +498,13 @@ #include "chrome/browser/fuchsia/chrome_browser_main_parts_fuchsia.h" #endif +#if BUILDFLAG(ENABLE_IPFS) ++#include "chrome/browser/ipfs_extra_parts.h" +#include "components/ipfs/interceptor.h" +#include "components/ipfs/ipfs_features.h" +#include "components/ipfs/url_loader_factory.h" @@ -134,7 +139,19 @@ index 5746f7c712514..2c56ac60c0e74 100644 #if BUILDFLAG(IS_CHROMEOS) #include "base/debug/leak_annotations.h" #include "chrome/browser/apps/app_service/app_install/app_install_navigation_throttle.h" -@@ -6130,12 +6137,23 @@ void ChromeContentBrowserClient:: +@@ -1709,6 +1717,11 @@ ChromeContentBrowserClient::CreateBrowserMainParts(bool is_integration_test) { + main_parts->AddParts( + std::make_unique()); + ++#if BUILDFLAG(ENABLE_IPFS) ++ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { ++ main_parts->AddParts(std::make_unique()); ++ } ++#endif + return main_parts; + } + +@@ -6049,12 +6062,25 @@ void ChromeContentBrowserClient:: const absl::optional& request_initiator_origin, NonNetworkURLLoaderFactoryMap* factories) { #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(ENABLE_EXTENSIONS) || \ @@ -144,23 +161,24 @@ index 5746f7c712514..2c56ac60c0e74 100644 RenderFrameHost::FromID(render_process_id, render_frame_id); WebContents* web_contents = WebContents::FromRenderFrameHost(frame_host); #endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(ENABLE_EXTENSIONS) || \ -- // !BUILDFLAG(IS_ANDROID) -+ // !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_IPFS) + // !BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(ENABLE_IPFS) + if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { + network::mojom::URLLoaderFactory* default_factory = g_browser_process->system_network_context_manager()->GetURLLoaderFactory(); ++ auto* context = web_contents->GetBrowserContext(); + ipfs::IpfsURLLoaderFactory::Create( + factories, -+ web_contents->GetBrowserContext(), ++ context, + default_factory, -+ GetSystemNetworkContext() ++ GetSystemNetworkContext(), ++ Profile::FromBrowserContext(context)->GetPrefs() + ); + } +#endif // BUILDFLAG(ENABLE_IPFS) #if BUILDFLAG(IS_CHROMEOS_ASH) if (web_contents) { -@@ -6277,6 +6295,11 @@ ChromeContentBrowserClient::WillCreateURLLoaderRequestInterceptors( +@@ -6196,6 +6222,11 @@ ChromeContentBrowserClient::WillCreateURLLoaderRequestInterceptors( scoped_refptr navigation_response_task_runner) { std::vector> interceptors; @@ -173,10 +191,10 @@ index 5746f7c712514..2c56ac60c0e74 100644 interceptors.push_back( std::make_unique( diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json -index 4c152d2b20941..d348a33e0c1b5 100644 +index 91740e0cc9e2b..916f584f37a54 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json -@@ -2922,6 +2922,11 @@ +@@ -2943,6 +2943,11 @@ "owners": [ "hanxi@chromium.org", "wychen@chromium.org" ], "expiry_milestone": 130 }, @@ -189,7 +207,7 @@ index 4c152d2b20941..d348a33e0c1b5 100644 "name": "enable-isolated-sandboxed-iframes", "owners": [ "wjmaclean@chromium.org", "alexmos@chromium.org", "creis@chromium.org" ], diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc -index 9f35b91158262..0dc974246fe82 100644 +index bddee4563e255..9e84641ac307f 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc @@ -284,6 +284,11 @@ const char kEnableBenchmarkingDescription[] = @@ -205,10 +223,10 @@ index 9f35b91158262..0dc974246fe82 100644 "Preloading Settings on Performance Page"; const char kPreloadingOnPerformancePageDescription[] = diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h -index afe0f24f278d3..3e0fe1ba84040 100644 +index dc09da6e1897a..f139e88d67ae1 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h -@@ -22,6 +22,7 @@ +@@ -23,6 +23,7 @@ #include "pdf/buildflags.h" #include "printing/buildflags/buildflags.h" #include "third_party/blink/public/common/buildflags.h" @@ -216,7 +234,7 @@ index afe0f24f278d3..3e0fe1ba84040 100644 // This file declares strings used in chrome://flags. These messages are not // translated, because instead of end-users they target Chromium developers and -@@ -175,6 +176,11 @@ extern const char kDownloadWarningImprovementsDescription[]; +@@ -176,6 +177,11 @@ extern const char kDownloadWarningImprovementsDescription[]; extern const char kEnableBenchmarkingName[]; extern const char kEnableBenchmarkingDescription[]; @@ -228,6 +246,42 @@ index afe0f24f278d3..3e0fe1ba84040 100644 #if BUILDFLAG(USE_FONTATIONS_BACKEND) extern const char kFontationsFontBackendName[]; extern const char kFontationsFontBackendDescription[]; +diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc +index e406d936c277c..0aff4e87587ae 100644 +--- a/chrome/browser/prefs/browser_prefs.cc ++++ b/chrome/browser/prefs/browser_prefs.cc +@@ -189,6 +189,7 @@ + #include "printing/buildflags/buildflags.h" + #include "rlz/buildflags/buildflags.h" + #include "third_party/abseil-cpp/absl/types/optional.h" ++#include "third_party/ipfs_client/ipfs_buildflags.h" + + #if BUILDFLAG(ENABLE_BACKGROUND_MODE) + #include "chrome/browser/background/background_mode_manager.h" +@@ -233,6 +234,11 @@ + #include "chrome/browser/pdf/pdf_pref_names.h" + #endif // BUILDFLAG(ENABLE_PDF) + ++#if BUILDFLAG(ENABLE_IPFS) ++#include "components/ipfs/ipfs_features.h" ++#include "components/ipfs/preferences.h" ++#endif ++ + #if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) + #include "chrome/browser/screen_ai/pref_names.h" + #endif +@@ -1658,6 +1664,11 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry, + IncognitoModePrefs::RegisterProfilePrefs(registry); + invalidation::PerUserTopicSubscriptionManager::RegisterProfilePrefs(registry); + invalidation::InvalidatorRegistrarWithMemory::RegisterProfilePrefs(registry); ++#if BUILDFLAG(ENABLE_IPFS) ++ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { ++ ipfs::RegisterPreferences(registry); ++ } ++#endif + language::LanguagePrefs::RegisterProfilePrefs(registry); + login_detection::prefs::RegisterProfilePrefs(registry); + lookalikes::RegisterProfilePrefs(registry); diff --git a/chrome/common/chrome_content_client.cc b/chrome/common/chrome_content_client.cc index 246ec9c5c911f..5d66d133a7907 100644 --- a/chrome/common/chrome_content_client.cc @@ -681,7 +735,7 @@ index 4eadf46ea0c24..d62fc7fb14e01 100644 for (auto& scheme : url::GetCorsEnabledSchemes()) cors_enabled_schemes.insert(scheme.c_str()); diff --git a/url/BUILD.gn b/url/BUILD.gn -index c525c166979d6..ce2b1ae43c0a7 100644 +index b5edb89f7698f..d299856674d7d 100644 --- a/url/BUILD.gn +++ b/url/BUILD.gn @@ -5,6 +5,7 @@ @@ -692,7 +746,7 @@ index c525c166979d6..ce2b1ae43c0a7 100644 import("features.gni") import("//build/config/cronet/config.gni") -@@ -67,6 +68,7 @@ component("url") { +@@ -68,6 +69,7 @@ component("url") { public_deps = [ "//base", "//build:robolectric_buildflags", @@ -700,7 +754,7 @@ index c525c166979d6..ce2b1ae43c0a7 100644 ] configs += [ "//build/config/compiler:wexit_time_destructors" ] -@@ -89,6 +91,11 @@ component("url") { +@@ -90,6 +92,11 @@ component("url") { public_configs = [ "//third_party/jdk" ] } @@ -713,10 +767,10 @@ index c525c166979d6..ce2b1ae43c0a7 100644 # Don't conflict with Windows' "url.dll". output_name = "url_lib" diff --git a/url/url_canon.h b/url/url_canon.h -index d3a7fabf09fa8..06db17242248f 100644 +index 8c48f9825d8cf..b9ad961e1b123 100644 --- a/url/url_canon.h +++ b/url/url_canon.h -@@ -697,6 +697,23 @@ bool CanonicalizeMailtoURL(const char16_t* spec, +@@ -804,6 +804,23 @@ bool CanonicalizeMailtoURL(const char16_t* spec, CanonOutput* output, Parsed* new_parsed); @@ -742,21 +796,17 @@ index d3a7fabf09fa8..06db17242248f 100644 // Internal structure used for storing separate strings for each component. diff --git a/url/url_canon_ipfs.cc b/url/url_canon_ipfs.cc new file mode 100644 -index 0000000000000..da3a5f032b5e8 +index 0000000000000..d7c9fdc78eb91 --- /dev/null +++ b/url/url_canon_ipfs.cc -@@ -0,0 +1,72 @@ +@@ -0,0 +1,55 @@ +#include "url_canon_internal.h" + -+#include ++#include +#include + +#include + -+namespace m = libp2p::multi; -+using Cid = m::ContentIdentifier; -+using CidCodec = m::ContentIdentifierCodec; -+ +bool url::CanonicalizeIpfsURL(const char* spec, + int spec_len, + const Parsed& parsed, @@ -770,37 +820,24 @@ index 0000000000000..da3a5f032b5e8 + if ( parsed.host.len < 1 ) { + return false; + } -+ std::string cid_str{ spec + parsed.host.begin, static_cast(parsed.host.len) }; -+ auto maybe_cid = CidCodec::fromString(cid_str); -+ if ( !maybe_cid.has_value() ) { -+ auto e = libp2p::multi::Stringify(maybe_cid.error()); -+ std::ostringstream err; -+ err << e << ' ' -+ << std::string_view{spec,static_cast(spec_len)}; -+ maybe_cid = ipfs::id_cid::forText( err.str() ); -+ } -+ auto cid = maybe_cid.value(); -+ if ( cid.version == Cid::Version::V0 ) { -+ //TODO dcheck content_type == DAG_PB && content_address.getType() == sha256 -+ cid = Cid{ -+ Cid::Version::V1, -+ cid.content_type, -+ cid.content_address -+ }; ++ std::string_view cid_str{ spec + parsed.host.begin, static_cast(parsed.host.len) }; ++ auto cid = ipfs::Cid(cid_str); ++ if ( !cid.valid() ) { ++ cid = ipfs::id_cid::forText( std::string{cid_str} + " is not a valid CID." ); + } -+ auto as_str = CidCodec::toString(cid); -+ if ( !as_str.has_value() ) { ++ auto as_str = cid.to_string(); ++ if ( as_str.empty() ) { + return false; + } + std::string stdurl{ spec, static_cast(parsed.host.begin) }; -+ stdurl.append( as_str.value() ); ++ stdurl.append( as_str ); + stdurl.append( spec + parsed.host.end(), spec_len - parsed.host.end() ); + spec = stdurl.data(); + spec_len = static_cast(stdurl.size()); + Parsed parsed_input; + ParseStandardURL(spec, spec_len, &parsed_input); + return CanonicalizeStandardURL( -+ spec, spec_len, ++ spec, + parsed_input, + scheme_type, + charset_converter, @@ -819,19 +856,25 @@ index 0000000000000..da3a5f032b5e8 + return CanonicalizeIpfsURL(as8.data(), as8.length(), parsed, scheme_type, query_converter, output, new_parsed); +} diff --git a/url/url_util.cc b/url/url_util.cc -index 9258cfcfada47..daf10e4c3b741 100644 +index 6f83f33c01c6b..a248e11c49445 100644 --- a/url/url_util.cc +++ b/url/url_util.cc -@@ -277,6 +277,12 @@ bool DoCanonicalize(const CHAR* spec, - charset_converter, output, - output_parsed); - +@@ -273,8 +273,15 @@ bool DoCanonicalize(const CHAR* spec, + } else if (DoCompareSchemeComponent(spec, scheme, url::kFileSystemScheme)) { + // Filesystem URLs are special. + ParseFileSystemURL(spec, spec_len, &parsed_input); +- success = CanonicalizeFileSystemURL(spec, parsed_input, charset_converter, +- output, output_parsed); ++ success = CanonicalizeFileSystemURL(spec, parsed_input, ++ charset_converter, output, ++ output_parsed); ++ + } else if (DoCompareSchemeComponent(spec, scheme, "ipfs")) { + // Switch multibase away from case-sensitive ones before continuing canonicalization. + ParseStandardURL(spec, spec_len, &parsed_input); + success = CanonicalizeIpfsURL(spec, spec_len, parsed_input, scheme_type, + charset_converter, output, output_parsed); -+ + } else if (DoIsStandard(spec, scheme, &scheme_type)) { // All "normal" URLs. - ParseStandardURL(spec, spec_len, &parsed_input); + diff --git a/component/patches/2f84c59146681c9207a4610d12669dd4a0603af2.patch b/component/patches/2f84c59146681c9207a4610d12669dd4a0603af2.patch deleted file mode 100644 index 68d44d66..00000000 --- a/component/patches/2f84c59146681c9207a4610d12669dd4a0603af2.patch +++ /dev/null @@ -1,11532 +0,0 @@ -diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn -index a188528a9e262..88df13b162858 100644 ---- a/chrome/browser/BUILD.gn -+++ b/chrome/browser/BUILD.gn -@@ -40,6 +40,7 @@ import("//rlz/buildflags/buildflags.gni") - import("//sandbox/features.gni") - import("//testing/libfuzzer/fuzzer_test.gni") - import("//third_party/blink/public/public_features.gni") -+import("//third_party/ipfs_client/args.gni") - import("//third_party/protobuf/proto_library.gni") - import("//third_party/webrtc/webrtc.gni") - import("//third_party/widevine/cdm/widevine.gni") -@@ -1912,7 +1913,6 @@ static_library("browser") { - "user_education/user_education_service_factory.h", - ] - } -- - configs += [ - "//build/config/compiler:wexit_time_destructors", - "//build/config:precompiled_headers", -@@ -2604,6 +2604,14 @@ static_library("browser") { - ] - } - -+ if (enable_ipfs) { -+ sources += [ -+ "ipfs_extra_parts.cc", -+ "ipfs_extra_parts.h", -+ ] -+ deps += [ "//components/ipfs" ] -+ } -+ - if (is_chromeos_ash) { - deps += [ "//chrome/browser/screen_ai:screen_ai_dlc_installer" ] - } -diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc -index a7907d8b188d8..68a96934ccf48 100644 ---- a/chrome/browser/about_flags.cc -+++ b/chrome/browser/about_flags.cc -@@ -213,6 +213,7 @@ - #include "third_party/blink/public/common/features_generated.h" - #include "third_party/blink/public/common/forcedark/forcedark_switches.h" - #include "third_party/blink/public/common/switches.h" -+#include "third_party/ipfs_client/ipfs_buildflags.h" - #include "ui/accessibility/accessibility_features.h" - #include "ui/accessibility/accessibility_switches.h" - #include "ui/base/ui_base_features.h" -@@ -308,6 +309,10 @@ - #include "extensions/common/switches.h" - #endif // BUILDFLAG(ENABLE_EXTENSIONS) - -+#if BUILDFLAG(ENABLE_IPFS) -+#include "components/ipfs/ipfs_features.h" -+#endif -+ - #if BUILDFLAG(ENABLE_PDF) - #include "pdf/pdf_features.h" - #endif -@@ -9731,6 +9736,14 @@ const FeatureEntry kFeatureEntries[] = { - flag_descriptions::kOmitCorsClientCertDescription, kOsAll, - FEATURE_VALUE_TYPE(network::features::kOmitCorsClientCert)}, - -+#if BUILDFLAG(ENABLE_IPFS) -+ {"enable-ipfs", -+ flag_descriptions::kEnableIpfsName, -+ flag_descriptions::kEnableIpfsDescription, -+ kOsMac | kOsWin | kOsLinux,//TODO: These are the only variants currently getting built, but that is not likely to remain the case -+ FEATURE_VALUE_TYPE(ipfs::kEnableIpfs)}, -+#endif -+ - {"use-idna2008-non-transitional", - flag_descriptions::kUseIDNA2008NonTransitionalName, - flag_descriptions::kUseIDNA2008NonTransitionalDescription, kOsAll, -diff --git a/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc b/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc -index 4c88614c68c25..f8bb12a3b0c2e 100644 ---- a/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc -+++ b/chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.cc -@@ -10,6 +10,8 @@ - #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" - #include "chrome/browser/external_protocol/external_protocol_handler.h" - #include "chrome/browser/profiles/profile.h" -+#include "third_party/ipfs_client/ipfs_buildflags.h" -+ - #if BUILDFLAG(IS_ANDROID) - #include "chrome/browser/profiles/profile_android.h" - #endif -@@ -18,6 +20,9 @@ - #include "chrome/browser/ui/android/omnibox/jni_headers/ChromeAutocompleteSchemeClassifier_jni.h" - #endif - #include "components/custom_handlers/protocol_handler_registry.h" -+#if BUILDFLAG(ENABLE_IPFS) -+#include "components/ipfs/ipfs_features.h" -+#endif - #include "content/public/common/url_constants.h" - #include "url/url_util.h" - -@@ -55,12 +60,20 @@ ChromeAutocompleteSchemeClassifier::GetInputTypeForScheme( - if (scheme.empty()) { - return metrics::OmniboxInputType::EMPTY; - } -- if (base::IsStringASCII(scheme) && -- (ProfileIOData::IsHandledProtocol(scheme) || -- base::EqualsCaseInsensitiveASCII(scheme, content::kViewSourceScheme) || -- base::EqualsCaseInsensitiveASCII(scheme, url::kJavaScriptScheme) || -- base::EqualsCaseInsensitiveASCII(scheme, url::kDataScheme))) { -- return metrics::OmniboxInputType::URL; -+ if (base::IsStringASCII(scheme)) { -+ if (ProfileIOData::IsHandledProtocol(scheme) || -+ base::EqualsCaseInsensitiveASCII(scheme, content::kViewSourceScheme) || -+ base::EqualsCaseInsensitiveASCII(scheme, url::kJavaScriptScheme) || -+ base::EqualsCaseInsensitiveASCII(scheme, url::kDataScheme)) { -+ return metrics::OmniboxInputType::URL; -+ } -+#if BUILDFLAG(ENABLE_IPFS) -+ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs) && -+ (base::EqualsCaseInsensitiveASCII(scheme, "ipfs") || base::EqualsCaseInsensitiveASCII(scheme, "ipns")) -+ ) { -+ return metrics::OmniboxInputType::URL; -+ } -+#endif - } - - // Also check for schemes registered via registerProtocolHandler(), which -diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc -index d3d67d83a514e..a5b1ef6339211 100644 ---- a/chrome/browser/chrome_content_browser_client.cc -+++ b/chrome/browser/chrome_content_browser_client.cc -@@ -378,6 +378,7 @@ - #include "third_party/blink/public/common/switches.h" - #include "third_party/blink/public/mojom/browsing_topics/browsing_topics.mojom.h" - #include "third_party/blink/public/public_buildflags.h" -+#include "third_party/ipfs_client/ipfs_buildflags.h" - #include "third_party/widevine/cdm/buildflags.h" - #include "ui/base/clipboard/clipboard_format_type.h" - #include "ui/base/l10n/l10n_util.h" -@@ -500,6 +501,13 @@ - #include "chrome/browser/fuchsia/chrome_browser_main_parts_fuchsia.h" - #endif - -+#if BUILDFLAG(ENABLE_IPFS) -+#include "chrome/browser/ipfs_extra_parts.h" -+#include "components/ipfs/interceptor.h" -+#include "components/ipfs/ipfs_features.h" -+#include "components/ipfs/url_loader_factory.h" -+#endif -+ - #if BUILDFLAG(IS_CHROMEOS) - #include "base/debug/leak_annotations.h" - #include "chrome/browser/apps/app_service/app_install/app_install_navigation_throttle.h" -@@ -1712,6 +1720,11 @@ ChromeContentBrowserClient::CreateBrowserMainParts(bool is_integration_test) { - main_parts->AddParts( - std::make_unique()); - -+#if BUILDFLAG(ENABLE_IPFS) -+ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { -+ main_parts->AddParts(std::make_unique()); -+ } -+#endif - return main_parts; - } - -@@ -6084,12 +6097,25 @@ void ChromeContentBrowserClient:: - const absl::optional& request_initiator_origin, - NonNetworkURLLoaderFactoryMap* factories) { - #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(ENABLE_EXTENSIONS) || \ -- !BUILDFLAG(IS_ANDROID) -+ !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_IPFS) - content::RenderFrameHost* frame_host = - RenderFrameHost::FromID(render_process_id, render_frame_id); - WebContents* web_contents = WebContents::FromRenderFrameHost(frame_host); - #endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(ENABLE_EXTENSIONS) || \ -- // !BUILDFLAG(IS_ANDROID) -+ // !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_IPFS) -+#if BUILDFLAG(ENABLE_IPFS) -+ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { -+ network::mojom::URLLoaderFactory* default_factory = g_browser_process->system_network_context_manager()->GetURLLoaderFactory(); -+ auto* context = web_contents->GetBrowserContext(); -+ ipfs::IpfsURLLoaderFactory::Create( -+ factories, -+ context, -+ default_factory, -+ GetSystemNetworkContext(), -+ Profile::FromBrowserContext(context)->GetPrefs() -+ ); -+ } -+#endif // BUILDFLAG(ENABLE_IPFS) - - #if BUILDFLAG(IS_CHROMEOS_ASH) - if (web_contents) { -@@ -6231,6 +6257,11 @@ ChromeContentBrowserClient::WillCreateURLLoaderRequestInterceptors( - scoped_refptr navigation_response_task_runner) { - std::vector> - interceptors; -+#if BUILDFLAG(ENABLE_IPFS) -+ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { -+ interceptors.push_back(std::make_unique(g_browser_process->system_network_context_manager()->GetURLLoaderFactory(), GetSystemNetworkContext())); -+ } -+#endif - #if BUILDFLAG(ENABLE_OFFLINE_PAGES) - interceptors.push_back( - std::make_unique( -diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json -index 0b51e78fcb8b9..9571b2c92c57f 100644 ---- a/chrome/browser/flag-metadata.json -+++ b/chrome/browser/flag-metadata.json -@@ -2948,6 +2948,11 @@ - "owners": [ "hanxi@chromium.org", "wychen@chromium.org" ], - "expiry_milestone": 130 - }, -+ { -+ "name": "enable-ipfs", -+ "owners": [ "//components/ipfs/OWNERS" ], -+ "expiry_milestone": 150 -+ }, - { - "name": "enable-isolated-sandboxed-iframes", - "owners": [ "wjmaclean@chromium.org", "alexmos@chromium.org", "creis@chromium.org" ], -diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc -index b2992e30f9811..f92d8a322b634 100644 ---- a/chrome/browser/flag_descriptions.cc -+++ b/chrome/browser/flag_descriptions.cc -@@ -288,6 +288,11 @@ const char kEnableBenchmarkingDescription[] = - "after 3 restarts. On the third restart, the flag will appear to be off " - "but the effect is still active."; - -+#if BUILDFLAG(ENABLE_IPFS) -+extern const char kEnableIpfsName[] = "Enable IPFS"; -+extern const char kEnableIpfsDescription[] = "Enable ipfs:// and ipns:// URLs"; -+#endif -+ - const char kPreloadingOnPerformancePageName[] = - "Preloading Settings on Performance Page"; - const char kPreloadingOnPerformancePageDescription[] = -diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h -index ad76d832395a1..438facecff519 100644 ---- a/chrome/browser/flag_descriptions.h -+++ b/chrome/browser/flag_descriptions.h -@@ -23,6 +23,7 @@ - #include "pdf/buildflags.h" - #include "printing/buildflags/buildflags.h" - #include "third_party/blink/public/common/buildflags.h" -+#include "third_party/ipfs_client/ipfs_buildflags.h" - - // This file declares strings used in chrome://flags. These messages are not - // translated, because instead of end-users they target Chromium developers and -@@ -179,6 +180,11 @@ extern const char kDownloadWarningImprovementsDescription[]; - extern const char kEnableBenchmarkingName[]; - extern const char kEnableBenchmarkingDescription[]; - -+#if BUILDFLAG(ENABLE_IPFS) -+extern const char kEnableIpfsName[]; -+extern const char kEnableIpfsDescription[]; -+#endif -+ - #if BUILDFLAG(USE_FONTATIONS_BACKEND) - extern const char kFontationsFontBackendName[]; - extern const char kFontationsFontBackendDescription[]; -diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc -index fc9fcf1ff478a..800961b3c8767 100644 ---- a/chrome/browser/prefs/browser_prefs.cc -+++ b/chrome/browser/prefs/browser_prefs.cc -@@ -190,6 +190,7 @@ - #include "printing/buildflags/buildflags.h" - #include "rlz/buildflags/buildflags.h" - #include "third_party/abseil-cpp/absl/types/optional.h" -+#include "third_party/ipfs_client/ipfs_buildflags.h" - - #if BUILDFLAG(ENABLE_BACKGROUND_MODE) - #include "chrome/browser/background/background_mode_manager.h" -@@ -241,6 +242,11 @@ - #include "chrome/browser/pdf/pdf_pref_names.h" - #endif // BUILDFLAG(ENABLE_PDF) - -+#if BUILDFLAG(ENABLE_IPFS) -+#include "components/ipfs/ipfs_features.h" -+#include "components/ipfs/preferences.h" -+#endif -+ - #if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE) - #include "chrome/browser/screen_ai/pref_names.h" - #endif -@@ -1658,6 +1664,11 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry, - IncognitoModePrefs::RegisterProfilePrefs(registry); - invalidation::PerUserTopicSubscriptionManager::RegisterProfilePrefs(registry); - invalidation::InvalidatorRegistrarWithMemory::RegisterProfilePrefs(registry); -+#if BUILDFLAG(ENABLE_IPFS) -+ if (base::FeatureList::IsEnabled(ipfs::kEnableIpfs)) { -+ ipfs::RegisterPreferences(registry); -+ } -+#endif - language::LanguagePrefs::RegisterProfilePrefs(registry); - login_detection::prefs::RegisterProfilePrefs(registry); - lookalikes::RegisterProfilePrefs(registry); -diff --git a/chrome/common/chrome_content_client.cc b/chrome/common/chrome_content_client.cc -index 246ec9c5c911f..5d66d133a7907 100644 ---- a/chrome/common/chrome_content_client.cc -+++ b/chrome/common/chrome_content_client.cc -@@ -296,6 +296,12 @@ void ChromeContentClient::AddAdditionalSchemes(Schemes* schemes) { - #if BUILDFLAG(IS_ANDROID) - schemes->local_schemes.push_back(url::kContentScheme); - #endif -+ for ( const char* ip_s : {"ipfs", "ipns"} ) { -+ schemes->standard_schemes.push_back(ip_s); -+ schemes->cors_enabled_schemes.push_back(ip_s); -+ schemes->secure_schemes.push_back(ip_s); -+ schemes->csp_bypassing_schemes.push_back(ip_s); -+ } - } - - std::u16string ChromeContentClient::GetLocalizedString(int message_id) { -diff --git a/components/cbor/reader.cc b/components/cbor/reader.cc -index 306ba52fa4944..6b13b3a679a65 100644 ---- a/components/cbor/reader.cc -+++ b/components/cbor/reader.cc -@@ -22,7 +22,7 @@ - namespace cbor { - - namespace constants { --const char kUnsupportedMajorType[] = "Unsupported major type."; -+const char kUnsupportedMajorType[] = "Unsupported major type operation."; - } - - namespace { -@@ -156,7 +156,11 @@ absl::optional Reader::DecodeCompleteDataItem(const Config& config, - case Value::Type::FLOAT_VALUE: - // Floating point values also go here since they are also type 7. - return DecodeToSimpleValueOrFloat(*header, config); -- case Value::Type::TAG: // We explicitly don't support TAG. -+ case Value::Type::TAG: -+ if (config.parse_tags) { -+ return ReadTagContent(*header, config, max_nesting_level); -+ } -+ break; - case Value::Type::NONE: - case Value::Type::INVALID_UTF8: - break; -@@ -347,6 +351,17 @@ absl::optional Reader::ReadByteStringContent( - return Value(std::move(cbor_byte_string)); - } - -+absl::optional Reader::ReadTagContent( -+ const Reader::DataItemHeader& header, -+ const Config& config, -+ int max_nesting_level) { -+ auto tagged_content = DecodeCompleteDataItem(config, max_nesting_level); -+ if (tagged_content.has_value()) { -+ tagged_content.value().SetTag(header.value); -+ } -+ return tagged_content; -+} -+ - absl::optional Reader::ReadArrayContent( - const Reader::DataItemHeader& header, - const Config& config, -diff --git a/components/cbor/reader.h b/components/cbor/reader.h -index f0b43a5517528..a57e277a1bc66 100644 ---- a/components/cbor/reader.h -+++ b/components/cbor/reader.h -@@ -130,6 +130,11 @@ class CBOR_EXPORT Reader { - // during decoding will set raise the `UNSUPPORTED_FLOATING_POINT_VALUE` - // error. - bool allow_floating_point = false; -+ -+ // If the parser encounters a TAG element, should it be parsed out and -+ // the tag value saved (true), or should the entire node and its content -+ // be discarded (false) -+ bool parse_tags = false; - }; - - Reader(const Reader&) = delete; -@@ -204,6 +209,9 @@ class CBOR_EXPORT Reader { - absl::optional ReadMapContent(const DataItemHeader& header, - const Config& config, - int max_nesting_level); -+ absl::optional ReadTagContent(const DataItemHeader& header, -+ const Config& config, -+ int max_nesting_level); - absl::optional ReadByte(); - absl::optional> ReadBytes(uint64_t num_bytes); - bool IsKeyInOrder(const Value& new_key, -diff --git a/components/cbor/reader_unittest.cc b/components/cbor/reader_unittest.cc -index 83d44a48d6dfa..a6ec5299b3241 100644 ---- a/components/cbor/reader_unittest.cc -+++ b/components/cbor/reader_unittest.cc -@@ -1451,5 +1451,42 @@ TEST(CBORReaderTest, AllowInvalidUTF8) { - EXPECT_FALSE(cbor); - EXPECT_EQ(Reader::DecoderError::INVALID_UTF8, error); - } -+TEST(CBORReaderTest, RejectsTagUnderDefaultConfig) { -+ static const uint8_t kTaggedCbor[] = { -+ 0xd8, 0x2a, 0x58, 0x25, 0x00, 0x01, 0x71, 0x12, 0x20, 0x69, 0xea, 0x07, -+ 0x40, 0xf9, 0x80, 0x7a, 0x28, 0xf4, 0xd9, 0x32, 0xc6, 0x2e, 0x7c, 0x1c, -+ 0x83, 0xbe, 0x05, 0x5e, 0x55, 0x07, 0x2c, 0x90, 0x26, 0x6a, 0xb3, 0xe7, -+ 0x9d, 0xf6, 0x3a, 0x36, 0x5b -+ }; -+ Reader::Config config; -+ absl::optional cbor = Reader::Read(kTaggedCbor, config); -+ EXPECT_FALSE(cbor.has_value()); -+} -+TEST(CBORReaderTest, ReadsTagWhenConfiguredToDoSo) { -+ static const uint8_t kTaggedCbor[] = { -+ 0xd8, 0x2a, 0x58, 0x25, 0x00, 0x01, 0x71, 0x12, 0x20, 0x69, 0xea, 0x07, -+ 0x40, 0xf9, 0x80, 0x7a, 0x28, 0xf4, 0xd9, 0x32, 0xc6, 0x2e, 0x7c, 0x1c, -+ 0x83, 0xbe, 0x05, 0x5e, 0x55, 0x07, 0x2c, 0x90, 0x26, 0x6a, 0xb3, 0xe7, -+ 0x9d, 0xf6, 0x3a, 0x36, 0x5b -+ }; -+ Reader::Config config; -+ config.parse_tags = true; -+ absl::optional cbor = Reader::Read(kTaggedCbor, config); -+ EXPECT_TRUE(cbor.has_value()); -+ auto& v = cbor.value(); -+ EXPECT_TRUE(v.has_tag()); -+ EXPECT_EQ(v.GetTag(),42UL); -+ EXPECT_TRUE(v.is_bytestring()); -+ EXPECT_EQ(v.type(), Value::Type::BYTE_STRING); -+ auto& bytes = v.GetBytestring(); -+ EXPECT_EQ(bytes.size(), 37UL); -+ EXPECT_EQ(bytes.at(0), 0x00);//identity multibase (e.g. not base-encoded, bytes are themselves) -+ EXPECT_EQ(bytes.at(1), 0x01);//CID version 1 -+ EXPECT_EQ(bytes.at(2), 0x71);//codec = dag-cbor -+ EXPECT_EQ(bytes.at(3), 0x12);//multihash = 18 = sha2-256 -+ EXPECT_EQ(bytes.at(4), 0x20);//hash length = 32 bytes -+ EXPECT_EQ(bytes.at(5), 0x69);//first byte of hash digest -+ EXPECT_EQ(bytes.at(36),0x5b);//last byte of hash digest -+} - - } // namespace cbor -diff --git a/components/cbor/values.cc b/components/cbor/values.cc -index 02498209c820e..34055aef24cfe 100644 ---- a/components/cbor/values.cc -+++ b/components/cbor/values.cc -@@ -66,32 +66,34 @@ Value::Value(Type type) : type_(type) { - NOTREACHED(); - } - --Value::Value(SimpleValue in_simple) -- : type_(Type::SIMPLE_VALUE), simple_value_(in_simple) { -+Value::Value(SimpleValue in_simple, uint64_t tag) -+ : type_(Type::SIMPLE_VALUE), simple_value_(in_simple), tag_(tag) { - CHECK(static_cast(in_simple) >= 20 && static_cast(in_simple) <= 23); - } - --Value::Value(bool boolean_value) : type_(Type::SIMPLE_VALUE) { -+Value::Value(bool boolean_value, uint64_t tag) : type_(Type::SIMPLE_VALUE), tag_(tag) { - simple_value_ = boolean_value ? Value::SimpleValue::TRUE_VALUE - : Value::SimpleValue::FALSE_VALUE; - } - --Value::Value(double float_value) -- : type_(Type::FLOAT_VALUE), float_value_(float_value) {} -+Value::Value(double float_value, uint64_t tag) -+ : type_(Type::FLOAT_VALUE), float_value_(float_value), tag_(tag) {} - --Value::Value(int integer_value) -- : Value(base::checked_cast(integer_value)) {} -+Value::Value(int integer_value, uint64_t tag) -+ : Value(base::checked_cast(integer_value), tag) {} - --Value::Value(int64_t integer_value) : integer_value_(integer_value) { -+Value::Value(int64_t integer_value, uint64_t tag) : integer_value_(integer_value), tag_(tag) { - type_ = integer_value >= 0 ? Type::UNSIGNED : Type::NEGATIVE; - } - --Value::Value(base::span in_bytes) -+Value::Value(base::span in_bytes, uint64_t tag) - : type_(Type::BYTE_STRING), -- bytestring_value_(in_bytes.begin(), in_bytes.end()) {} -+ bytestring_value_(in_bytes.begin(), in_bytes.end()), -+ tag_(tag) -+ {} - --Value::Value(base::span in_bytes, Type type) -- : type_(type), bytestring_value_(in_bytes.begin(), in_bytes.end()) { -+Value::Value(base::span in_bytes, Type type, uint64_t tag) -+ : type_(type), bytestring_value_(in_bytes.begin(), in_bytes.end()), tag_(tag) { - DCHECK(type_ == Type::BYTE_STRING || type_ == Type::INVALID_UTF8); - } - -@@ -117,7 +119,8 @@ Value::Value(std::string&& in_string, Type type) noexcept : type_(type) { - } - } - --Value::Value(base::StringPiece in_string, Type type) : type_(type) { -+Value::Value(base::StringPiece in_string, Type type, uint64_t tag) -+: type_(type), tag_(tag) { - switch (type_) { - case Type::STRING: - new (&string_value_) std::string(); -@@ -133,16 +136,18 @@ Value::Value(base::StringPiece in_string, Type type) : type_(type) { - } - } - --Value::Value(const ArrayValue& in_array) : type_(Type::ARRAY), array_value_() { -+Value::Value(const ArrayValue& in_array, uint64_t tag) -+: type_(Type::ARRAY), array_value_(), tag_(tag) { - array_value_.reserve(in_array.size()); - for (const auto& val : in_array) - array_value_.emplace_back(val.Clone()); - } - --Value::Value(ArrayValue&& in_array) noexcept -- : type_(Type::ARRAY), array_value_(std::move(in_array)) {} -+Value::Value(ArrayValue&& in_array, uint64_t tag) noexcept -+ : type_(Type::ARRAY), array_value_(std::move(in_array)), tag_(tag) {} - --Value::Value(const MapValue& in_map) : type_(Type::MAP), map_value_() { -+Value::Value(const MapValue& in_map, uint64_t tag) -+: type_(Type::MAP), map_value_(), tag_(tag) { - map_value_.reserve(in_map.size()); - for (const auto& it : in_map) - map_value_.emplace_hint(map_value_.end(), it.first.Clone(), -@@ -168,31 +173,36 @@ Value Value::Clone() const { - case Type::NONE: - return Value(); - case Type::INVALID_UTF8: -- return Value(bytestring_value_, Type::INVALID_UTF8); -+ return Value(bytestring_value_, Type::INVALID_UTF8, tag_); - case Type::UNSIGNED: - case Type::NEGATIVE: -- return Value(integer_value_); -+ return Value(integer_value_, tag_); - case Type::BYTE_STRING: -- return Value(bytestring_value_); -+ return Value(bytestring_value_, tag_); - case Type::STRING: -- return Value(string_value_); -+ return Value(string_value_, Type::STRING, tag_); - case Type::ARRAY: -- return Value(array_value_); -+ return Value(array_value_, tag_); - case Type::MAP: -- return Value(map_value_); -+ return Value(map_value_, tag_); - case Type::TAG: - NOTREACHED() << constants::kUnsupportedMajorType; - return Value(); - case Type::SIMPLE_VALUE: -- return Value(simple_value_); -+ return Value(simple_value_, tag_); - case Type::FLOAT_VALUE: -- return Value(float_value_); -+ return Value(float_value_, tag_); - } - - NOTREACHED(); - return Value(); - } - -+Value& Value::SetTag(uint64_t tag) noexcept { -+ tag_ = tag; -+ return *this; -+} -+ - Value::SimpleValue Value::GetSimpleValue() const { - CHECK(is_simple()); - return simple_value_; -@@ -258,9 +268,14 @@ const Value::BinaryValue& Value::GetInvalidUTF8() const { - return bytestring_value_; - } - -+uint64_t Value::GetTag() const { -+ CHECK(has_tag()); -+ return tag_; -+} -+ - void Value::InternalMoveConstructFrom(Value&& that) { - type_ = that.type_; -- -+ tag_ = that.tag_; - switch (type_) { - case Type::UNSIGNED: - case Type::NEGATIVE: -diff --git a/components/cbor/values.h b/components/cbor/values.h -index d81ef5607c55a..10216a8dcdc57 100644 ---- a/components/cbor/values.h -+++ b/components/cbor/values.h -@@ -127,28 +127,29 @@ class CBOR_EXPORT Value { - - explicit Value(Type type); - -- explicit Value(SimpleValue in_simple); -- explicit Value(bool boolean_value); -- explicit Value(double in_float); -+ explicit Value(SimpleValue in_simple, uint64_t tag = NO_TAG); -+ explicit Value(bool boolean_value, uint64_t tag = NO_TAG); -+ explicit Value(double in_float, uint64_t tag = NO_TAG); - -- explicit Value(int integer_value); -- explicit Value(int64_t integer_value); -+ explicit Value(int integer_value, uint64_t tag = NO_TAG); -+ explicit Value(int64_t integer_value, uint64_t tag = NO_TAG); - explicit Value(uint64_t integer_value) = delete; - -- explicit Value(base::span in_bytes); -+ explicit Value(base::span in_bytes, uint64_t tag = NO_TAG); - explicit Value(BinaryValue&& in_bytes) noexcept; - - explicit Value(const char* in_string, Type type = Type::STRING); - explicit Value(std::string&& in_string, Type type = Type::STRING) noexcept; -- explicit Value(base::StringPiece in_string, Type type = Type::STRING); -+ explicit Value(base::StringPiece in_string, Type type = Type::STRING, uint64_t tag = NO_TAG); - -- explicit Value(const ArrayValue& in_array); -- explicit Value(ArrayValue&& in_array) noexcept; -+ explicit Value(const ArrayValue& in_array, uint64_t tag = NO_TAG); -+ explicit Value(ArrayValue&& in_array, uint64_t tag = NO_TAG) noexcept; - -- explicit Value(const MapValue& in_map); -+ explicit Value(const MapValue& in_map, uint64_t tag = NO_TAG); - explicit Value(MapValue&& in_map) noexcept; - - Value& operator=(Value&& that) noexcept; -+ Value& SetTag(uint64_t) noexcept; - - Value(const Value&) = delete; - Value& operator=(const Value&) = delete; -@@ -179,6 +180,7 @@ class CBOR_EXPORT Value { - bool is_string() const { return type() == Type::STRING; } - bool is_array() const { return type() == Type::ARRAY; } - bool is_map() const { return type() == Type::MAP; } -+ bool has_tag() const { return tag_ != NO_TAG; } - - // These will all fatally assert if the type doesn't match. - SimpleValue GetSimpleValue() const; -@@ -194,12 +196,13 @@ class CBOR_EXPORT Value { - const ArrayValue& GetArray() const; - const MapValue& GetMap() const; - const BinaryValue& GetInvalidUTF8() const; -+ uint64_t GetTag() const; - - private: - friend class Reader; - // This constructor allows INVALID_UTF8 values to be created, which only - // |Reader| and InvalidUTF8StringValueForTesting() may do. -- Value(base::span in_bytes, Type type); -+ Value(base::span in_bytes, Type type, uint64_t tag = NO_TAG); - - Type type_; - -@@ -213,6 +216,11 @@ class CBOR_EXPORT Value { - MapValue map_value_; - }; - -+ //This value specified as Invalid, -+ // used here to represent absence of TAG -+ constexpr static uint64_t NO_TAG = 0xFFFF; -+ uint64_t tag_ = NO_TAG; -+ - void InternalMoveConstructFrom(Value&& that); - void InternalCleanup(); - }; -diff --git a/components/cbor/writer.cc b/components/cbor/writer.cc -index bb22754d36a07..aae4027836377 100644 ---- a/components/cbor/writer.cc -+++ b/components/cbor/writer.cc -@@ -47,6 +47,9 @@ bool Writer::EncodeCBOR(const Value& node, - if (max_nesting_level < 0) - return false; - -+ if (node.has_tag()) { -+ StartItem(Value::Type::TAG, node.GetTag()); -+ } - switch (node.type()) { - case Value::Type::NONE: { - StartItem(Value::Type::BYTE_STRING, 0); -diff --git a/components/cbor/writer_unittest.cc b/components/cbor/writer_unittest.cc -index e3bffe20734bc..0ed569ae164a0 100644 ---- a/components/cbor/writer_unittest.cc -+++ b/components/cbor/writer_unittest.cc -@@ -522,4 +522,31 @@ TEST(CBORWriterTest, OverlyNestedCBOR) { - EXPECT_FALSE(Writer::Write(Value(map), 4).has_value()); - } - -+TEST(CBORWriterTest, CanWriteTag) { -+ std::array content{ -+ 0x00, 0x01, 0x71, 0x12, 0x20, -+ 0x69, 0xea, 0x07, 0x40, 0xf9, -+ 0x80, 0x7a, 0x28, 0xf4, 0xd9, -+ 0x32, 0xc6, 0x2e, 0x7c, 0x1c, -+ 0x83, 0xbe, 0x05, 0x5e, 0x55, -+ 0x07, 0x2c, 0x90, 0x26, 0x6a, -+ 0xb3, 0xe7, 0x9d, 0xf6, 0x3a, -+ 0x36, 0x5b -+ }; -+ Value to_write(content); -+ to_write.SetTag(42); -+ auto result = Writer::Write(to_write); -+ EXPECT_TRUE(result.has_value()); -+ auto& bytes = result.value(); -+ EXPECT_EQ(bytes.size(), 41UL); -+ EXPECT_EQ(bytes.at(0), 0xd8); -+ EXPECT_EQ(bytes.at(1), 0x2a); -+ EXPECT_EQ(bytes.at(2), 0x58); -+ EXPECT_EQ(bytes.at(3), 0x25); -+ for (auto i = 0UL; i < content.size(); ++i) { -+ ASSERT_LT(i + 4UL, bytes.size()); -+ ASSERT_EQ(content.at(i), bytes.at(i+4UL)); -+ } -+} -+ - } // namespace cbor -diff --git a/components/ipfs/BUILD.gn b/components/ipfs/BUILD.gn -new file mode 100644 -index 0000000000000..572e93e493e7a ---- /dev/null -+++ b/components/ipfs/BUILD.gn -@@ -0,0 +1,62 @@ -+import("//testing/test.gni") -+import("//third_party/ipfs_client/args.gni") -+ -+if (enable_ipfs) { -+ -+ component("ipfs") { -+ sources = [ -+ "block_http_request.cc", -+ "block_http_request.h", -+ "cache_requestor.cc", -+ "cache_requestor.h", -+ "chromium_cbor_adapter.cc", -+ "chromium_cbor_adapter.h", -+ "chromium_ipfs_context.cc", -+ "chromium_ipfs_context.h", -+ "chromium_json_adapter.cc", -+ "chromium_json_adapter.h", -+ "crypto_api.cc", -+ "crypto_api.h", -+ "dns_txt_request.cc", -+ "dns_txt_request.h", -+ "export.h", -+ "inter_request_state.cc", -+ "inter_request_state.h", -+ "interceptor.cc", -+ "interceptor.h", -+ "ipfs_features.cc", -+ "ipfs_features.h", -+ "ipfs_url_loader.cc", -+ "ipfs_url_loader.h", -+ "preferences.cc", -+ "preferences.h", -+ "url_loader_factory.cc", -+ "url_loader_factory.h", -+ ] -+ defines = [ ] -+ include_dirs = [ -+ ".", -+ "ipfs_client", -+ "ipfs_client/unix_fs", -+ ] -+ deps = [ -+ "//content", -+ "//crypto", -+ "//base", -+ "//components/cbor", -+ "//components/prefs", -+ "//components/webcrypto:webcrypto", -+ "//mojo/public/cpp/bindings", -+ "//services/network:network_service", -+ "//services/network/public/cpp:cpp", -+ "//services/network/public/mojom:url_loader_base", -+ "//url", -+ "//third_party/blink/public:blink", -+ ] -+ public_deps = [ -+ "//third_party/ipfs_client", -+ ] -+ defines = [ "IS_IPFS_IMPL" ] -+ } -+ -+} -diff --git a/components/ipfs/README.md b/components/ipfs/README.md -new file mode 100644 -index 0000000000000..1333ed77b7e1e ---- /dev/null -+++ b/components/ipfs/README.md -@@ -0,0 +1 @@ -+TODO -diff --git a/components/ipfs/block_http_request.cc b/components/ipfs/block_http_request.cc -new file mode 100644 -index 0000000000000..c48ddd8f77c8d ---- /dev/null -+++ b/components/ipfs/block_http_request.cc -@@ -0,0 +1,102 @@ -+#include "block_http_request.h" -+ -+#include -+#include -+#include -+ -+using Self = ipfs::BlockHttpRequest; -+ -+namespace { -+constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation = -+ net::DefineNetworkTrafficAnnotation("ipfs_gateway_request", R"( -+ semantics { -+ sender: "IPFS component" -+ description: -+ "Sends a request to an IPFS gateway." -+ trigger: -+ "Processing of an ipfs:// or ipns:// URL." -+ data: "None" -+ destination: WEBSITE -+ } -+ policy { -+ cookies_allowed: NO -+ setting: "EnableIpfs" -+ } -+ )"); -+} -+ -+Self::BlockHttpRequest(ipfs::HttpRequestDescription req_inf, -+ HttpCompleteCallback cb) -+ : inf_{req_inf}, callback_{cb} {} -+Self::~BlockHttpRequest() noexcept {} -+ -+void Self::send(raw_ptr loader_factory) { -+ auto req = std::make_unique(); -+ req->url = GURL{inf_.url}; -+ req->priority = net::HIGHEST; // TODO -+ if (!inf_.accept.empty()) { -+ req->headers.SetHeader("Accept", inf_.accept); -+ } -+ using L = network::SimpleURLLoader; -+ loader_ = L::Create(std::move(req), kTrafficAnnotation, FROM_HERE); -+ loader_->SetTimeoutDuration(base::Seconds(inf_.timeout_seconds)); -+ loader_->SetAllowHttpErrorResults(true); -+ loader_->SetOnResponseStartedCallback( -+ base::BindOnce(&Self::OnResponseHead, base::Unretained(this))); -+ auto bound = base::BindOnce(&Self::OnResponse, base::Unretained(this), -+ shared_from_this()); -+ DCHECK(loader_factory); -+ if (auto sz = inf_.max_response_size) { -+ loader_->DownloadToString(loader_factory, std::move(bound), sz.value()); -+ } else { -+ loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(loader_factory, -+ std::move(bound)); -+ } -+} -+void Self::OnResponse(std::shared_ptr, -+ std::unique_ptr body) { -+ DCHECK(loader_); -+ int status; -+ switch (loader_->NetError()) { -+ case net::Error::OK: -+ status = 200; -+ break; -+ case net::Error::ERR_TIMED_OUT: -+ VLOG(2) << "HTTP request timed out: " << inf_.url << " after " -+ << inf_.timeout_seconds << "s."; -+ status = 408; -+ break; -+ default: -+ VLOG(2) << "NetErr " << loader_->NetError() << " for " << inf_.url; -+ status = 500; -+ } -+ // auto sz = body ? body->size() : 0UL; -+ auto const* head = loader_->ResponseInfo(); -+ if (head) { -+ OnResponseHead({}, *head); -+ } -+ auto sp = status_line_.find(' '); -+ if (sp < status_line_.size()) { -+ VLOG(2) << "HTTP response status='" << status_line_ << "'."; -+ status = std::atoi(status_line_.c_str() + sp + 1); -+ } -+ if (body) { -+ callback_(status, *body, header_accessor_); -+ } else { -+ callback_(status, "", header_accessor_); -+ } -+} -+void Self::OnResponseHead( -+ GURL const&, -+ network::mojom::URLResponseHead const& response_head) { -+ if (!response_head.headers) { -+ return; -+ } -+ auto head = response_head.headers; -+ status_line_ = head->GetStatusLine(); -+ header_accessor_ = [head](std::string_view k) { -+ std::string val; -+ head->EnumerateHeader(nullptr, k, &val); -+ return val; -+ }; -+} -diff --git a/components/ipfs/block_http_request.h b/components/ipfs/block_http_request.h -new file mode 100644 -index 0000000000000..a34d88b7a54cf ---- /dev/null -+++ b/components/ipfs/block_http_request.h -@@ -0,0 +1,46 @@ -+#ifndef IPFS_BLOCK_HTTP_REQUEST_H_ -+#define IPFS_BLOCK_HTTP_REQUEST_H_ -+ -+#include -+ -+#include -+#include -+ -+namespace network { -+struct ResourceRequest; -+class SimpleURLLoader; -+} // namespace network -+namespace network::mojom { -+class URLLoaderFactory; -+class URLResponseHead; -+} // namespace network::mojom -+class GURL; -+ -+namespace ipfs { -+class BlockHttpRequest : public std::enable_shared_from_this { -+ // TODO ween oneself off of SimpleURLLoader -+ // std::array buffer_; -+ std::unique_ptr loader_; -+ -+ public: -+ using HttpCompleteCallback = ipfs::ContextApi::HttpCompleteCallback; -+ BlockHttpRequest(ipfs::HttpRequestDescription, HttpCompleteCallback); -+ ~BlockHttpRequest() noexcept; -+ -+ void send(raw_ptr); -+ -+ private: -+ ipfs::HttpRequestDescription const inf_; -+ HttpCompleteCallback callback_; -+ std::string status_line_; -+ ContextApi::HeaderAccess header_accessor_ = [](auto) { -+ return std::string{}; -+ }; -+ -+ void OnResponseHead(GURL const&, network::mojom::URLResponseHead const&); -+ void OnResponse(std::shared_ptr, -+ std::unique_ptr body); -+}; -+} // namespace ipfs -+ -+#endif // IPFS_BLOCK_HTTP_REQUEST_H_ -diff --git a/components/ipfs/cache_requestor.cc b/components/ipfs/cache_requestor.cc -new file mode 100644 -index 0000000000000..ce446b608080a ---- /dev/null -+++ b/components/ipfs/cache_requestor.cc -@@ -0,0 +1,219 @@ -+#include "cache_requestor.h" -+ -+#include "chromium_ipfs_context.h" -+#include "inter_request_state.h" -+ -+#include -+#include -+ -+using Self = ipfs::CacheRequestor; -+namespace dc = disk_cache; -+ -+std::string_view Self::name() const { -+ return "Disk Cache"; -+} -+Self::CacheRequestor(InterRequestState& state, base::FilePath base) -+ : state_{state} { -+ if (!base.empty()) { -+ path_ = base.AppendASCII("IpfsBlockCache"); -+ } -+ Start(); -+} -+void Self::Start() { -+ if (pending_) { -+ return; -+ } -+ auto result = dc::CreateCacheBackend( -+ net::CacheType::DISK_CACHE, net::CACHE_BACKEND_DEFAULT, {}, path_, 0, -+ dc::ResetHandling::kNeverReset, -+ // dc::ResetHandling::kResetOnError, -+ nullptr, base::BindOnce(&Self::Assign, base::Unretained(this))); -+ LOG(INFO) << "Start(" << result.net_error << ')' << result.net_error; -+ pending_ = result.net_error == net::ERR_IO_PENDING; -+ if (!pending_) { -+ Assign(std::move(result)); -+ } -+} -+Self::~CacheRequestor() noexcept = default; -+ -+void Self::Assign(dc::BackendResult res) { -+ pending_ = false; -+ if (res.net_error == net::OK) { -+ LOG(INFO) << "Initialized disk cache"; -+ cache_.swap(res.backend); -+ } else { -+ LOG(ERROR) << "Trouble opening " << name() << ": " << res.net_error; -+ Start(); -+ } -+} -+auto Self::handle(RequestPtr req) -> HandleOutcome { -+ if (pending_) { -+ return HandleOutcome::NOT_HANDLED; -+ } -+ Task task; -+ task.key = req->main_param; -+ task.request = req; -+ StartFetch(task, net::MAXIMUM_PRIORITY); -+ return HandleOutcome::PENDING; -+} -+void Self::StartFetch(Task& task, net::RequestPriority priority) { -+ if (pending_) { -+ Start(); -+ Miss(task); -+ return; -+ } -+ auto bound = base::BindOnce(&Self::OnOpen, base::Unretained(this), task); -+ auto res = cache_->OpenEntry(task.key, priority, std::move(bound)); -+ if (res.net_error() != net::ERR_IO_PENDING) { -+ OnOpen(task, std::move(res)); -+ } -+} -+void Self::Miss(Task& task) { -+ if (task.request) { -+ VLOG(2) << "Cache miss on " << task.request->debug_string(); -+ auto req = task.request; -+ task.request->Hook([this, req](std::string_view bytes) { -+ Store(req->main_param, "TODO", std::string{bytes}); -+ }); -+ forward(req); -+ } -+} -+namespace { -+std::shared_ptr GetEntry(dc::EntryResult& result) { -+ auto* e = result.ReleaseEntry(); -+ auto deleter = [](auto e) { -+ if (e) { -+ e->Close(); -+ } -+ }; -+ return {e, deleter}; -+} -+} // namespace -+ -+void Self::OnOpen(Task task, dc::EntryResult res) { -+ VLOG(2) << "OnOpen(" << res.net_error() << ")"; -+ if (res.net_error() != net::OK) { -+ VLOG(2) << "Failed to find " << task.key << " in " << name(); -+ Miss(task); -+ return; -+ } -+ task.entry = GetEntry(res); -+ DCHECK(task.entry); -+ task.buf = base::MakeRefCounted(2 * 1024 * 1024); -+ DCHECK(task.buf); -+ auto bound = -+ base::BindOnce(&Self::OnHeaderRead, base::Unretained(this), task); -+ auto code = task.entry->ReadData(0, 0, task.buf.get(), task.buf->size(), -+ std::move(bound)); -+ if (code != net::ERR_IO_PENDING) { -+ OnHeaderRead(task, code); -+ } -+} -+void Self::OnHeaderRead(Task task, int code) { -+ if (code <= 0) { -+ LOG(ERROR) << "Failed to read headers for entry " << task.key << " in " -+ << name() << " " << code; -+ // Miss(task); -+ // return; -+ } -+ task.header.assign(task.buf->data(), static_cast(code)); -+ auto bound = base::BindOnce(&Self::OnBodyRead, base::Unretained(this), task); -+ code = task.entry->ReadData(1, 0, task.buf.get(), task.buf->size(), -+ std::move(bound)); -+ if (code != net::ERR_IO_PENDING) { -+ OnBodyRead(task, code); -+ } -+} -+void Self::OnBodyRead(Task task, int code) { -+ if (code <= 0) { -+ LOG(INFO) << "Failed to read body for entry " << task.key << " in " -+ << name(); -+ Miss(task); -+ return; -+ } -+ task.body.assign(task.buf->data(), static_cast(code)); -+ if (task.request) { -+ task.SetHeaders(name()); -+ if (task.request->RespondSuccessfully(task.body, api_)) { -+ VLOG(2) << "Cache hit on " << task.key << " for " -+ << task.request->debug_string(); -+ } else { -+ LOG(ERROR) << "Had a BAD cached response for " << task.key; -+ Expire(task.key); -+ Miss(task); -+ } -+ } -+} -+void Self::Store(std::string cid, std::string headers, std::string body) { -+ VLOG(1) << "Store(" << name() << ',' << cid << ',' << headers.size() << ',' -+ << body.size() << ')'; -+ auto bound = base::BindOnce(&Self::OnEntryCreated, base::Unretained(this), -+ cid, headers, body); -+ auto res = cache_->OpenOrCreateEntry(cid, net::LOW, std::move(bound)); -+ if (res.net_error() != net::ERR_IO_PENDING) { -+ OnEntryCreated(cid, headers, body, std::move(res)); -+ } -+} -+void Self::OnEntryCreated(std::string cid, -+ std::string headers, -+ std::string body, -+ disk_cache::EntryResult result) { -+ if (result.opened()) { -+ VLOG(1) << "No need to write an entry for " << cid << " in " << name() -+ << " as it is already there and immutable."; -+ } else if (result.net_error() == net::OK) { -+ auto entry = GetEntry(result); -+ auto buf = base::MakeRefCounted(headers); -+ DCHECK(buf); -+ auto bound = base::BindOnce(&Self::OnHeaderWritten, base::Unretained(this), -+ buf, body, entry); -+ auto code = -+ entry->WriteData(0, 0, buf.get(), buf->size(), std::move(bound), true); -+ if (code != net::ERR_IO_PENDING) { -+ OnHeaderWritten(buf, body, entry, code); -+ } -+ } else { -+ LOG(ERROR) << "Failed to create an entry for " << cid << " in " << name(); -+ } -+} -+void Self::OnHeaderWritten(scoped_refptr buf, -+ std::string body, -+ std::shared_ptr entry, -+ int code) { -+ if (code < 0) { -+ LOG(ERROR) << "Failed to write header info for " << entry->GetKey() -+ << " in " << name(); -+ return; -+ } -+ buf = base::MakeRefCounted(body); -+ DCHECK(buf); -+ auto f = [](scoped_refptr, int c) { -+ VLOG(1) << "body write " << c; -+ }; -+ auto bound = base::BindOnce(f, buf); -+ entry->WriteData(1, 0, buf.get(), buf->size(), std::move(bound), true); -+} -+ -+void Self::Task::SetHeaders(std::string_view source) { -+ auto heads = base::MakeRefCounted(header); -+ DCHECK(heads); -+ std::string value{"blockcache-"}; -+ value.append(key); -+ value.append(";desc=\"Load from local browser block cache\";dur="); -+ auto dur = base::TimeTicks::Now() - start; -+ value.append(std::to_string(dur.InMillisecondsRoundedUp())); -+ heads->SetHeader("Server-Timing", value); -+ VLOG(2) << "From cache: Server-Timing: " << value << "; Block-Cache-" << key -+ << ": " << source; -+ heads->SetHeader("Block-Cache-" + key, {source.data(), source.size()}); -+ header = heads->raw_headers(); -+} -+void Self::Expire(std::string const& key) { -+ if (cache_ && !pending_) { -+ cache_->DoomEntry(key, net::RequestPriority::LOWEST, base::DoNothing()); -+ } -+} -+ -+Self::Task::Task() = default; -+Self::Task::Task(Task const&) = default; -+Self::Task::~Task() noexcept = default; -diff --git a/components/ipfs/cache_requestor.h b/components/ipfs/cache_requestor.h -new file mode 100644 -index 0000000000000..b8c31d371ecb4 ---- /dev/null -+++ b/components/ipfs/cache_requestor.h -@@ -0,0 +1,71 @@ -+#ifndef CACHE_REQUESTOR_H_ -+#define CACHE_REQUESTOR_H_ -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#include -+ -+#include -+ -+namespace ipfs { -+ -+class BlockStorage; -+class InterRequestState; -+ -+class CacheRequestor : public gw::Requestor { -+ public: -+ CacheRequestor(InterRequestState&, base::FilePath); -+ ~CacheRequestor() noexcept override; -+ void Store(std::string cid, std::string headers, std::string body); -+ void Expire(std::string const& key); -+ -+ std::string_view name() const override; -+ -+ private: -+ struct Task { -+ Task(); -+ Task(Task const&); -+ ~Task() noexcept; -+ std::string key; -+ base::TimeTicks start = base::TimeTicks::Now(); -+ std::string header; -+ std::string body; -+ scoped_refptr buf; -+ std::shared_ptr entry; -+ gw::RequestPtr request; -+ -+ void SetHeaders(std::string_view); -+ }; -+ raw_ref state_; -+ std::unique_ptr cache_; -+ bool pending_ = false; -+ base::FilePath path_; -+ -+ void Start(); -+ -+ void StartFetch(Task& t, net::RequestPriority priority); -+ void Assign(disk_cache::BackendResult); -+ void OnOpen(Task, disk_cache::EntryResult); -+ void OnHeaderRead(Task, int); -+ void OnBodyRead(Task, int); -+ -+ void OnEntryCreated(std::string c, -+ std::string h, -+ std::string b, -+ disk_cache::EntryResult); -+ void OnHeaderWritten(scoped_refptr buf, -+ std::string body, -+ std::shared_ptr entry, -+ int); -+ void Miss(Task&); -+ HandleOutcome handle(RequestPtr) override; -+}; -+} // namespace ipfs -+ -+#endif // CACHE_REQUESTOR_H_ -diff --git a/components/ipfs/chromium_cbor_adapter.cc b/components/ipfs/chromium_cbor_adapter.cc -new file mode 100644 -index 0000000000000..d7d43b81be96b ---- /dev/null -+++ b/components/ipfs/chromium_cbor_adapter.cc -@@ -0,0 +1,91 @@ -+#include "chromium_cbor_adapter.h" -+ -+#include -+ -+using Self = ipfs::ChromiumCborAdapter; -+ -+bool Self::is_map() const { -+ return cbor_.is_map(); -+} -+bool Self::is_array() const { -+ return cbor_.is_array(); -+} -+auto Self::at(std::string_view key) const -> std::unique_ptr { -+ if (is_map()) { -+ auto& m = cbor_.GetMap(); -+ auto it = m.find(cbor::Value{base::StringPiece{key}}); -+ if (m.end() != it) { -+ return std::make_unique(it->second.Clone()); -+ } -+ } -+ return {}; -+} -+std::optional Self::as_unsigned() const { -+ if (cbor_.is_unsigned()) { -+ return cbor_.GetUnsigned(); -+ } -+ return std::nullopt; -+} -+std::optional Self::as_signed() const { -+ if (cbor_.is_integer()) { -+ return cbor_.GetInteger(); -+ } -+ return {}; -+} -+std::optional Self::as_float() const { -+ return {}; -+} -+ -+std::optional Self::as_string() const { -+ if (cbor_.is_string()) { -+ return cbor_.GetString(); -+ } -+ return std::nullopt; -+} -+auto Self::as_bytes() const -> std::optional> { -+ if (cbor_.is_bytestring()) { -+ return cbor_.GetBytestring(); -+ } -+ return std::nullopt; -+} -+auto Self::as_link() const -> std::optional { -+ VLOG(1) << "Trying to do an as_link(" << static_cast(cbor_.type()) << ',' << std::boolalpha << cbor_.has_tag() << ")"; -+ if (!cbor_.has_tag() || cbor_.GetTag() != 42UL || !cbor_.is_bytestring()) { -+ VLOG(1) << "This is not a link."; -+ return std::nullopt; -+ } -+ auto& bytes = cbor_.GetBytestring(); -+ auto* byte_ptr = reinterpret_cast(bytes.data()) + 1; -+ auto result = Cid(ByteView{byte_ptr, bytes.size() - 1UL}); -+ if (result.valid()) { -+ return result; -+ } else { -+ LOG(ERROR) << "Unable to decode bytes from DAG-CBOR Link as CID."; -+ return std::nullopt; -+ } -+} -+std::optional Self::as_bool() const { -+ if (cbor_.is_bool()) { -+ return cbor_.GetBool(); -+ } -+ return std::nullopt; -+} -+void Self::iterate_map(MapElementCallback cb) const { -+ auto& m = cbor_.GetMap(); -+ for (auto& [k,v] : m) { -+ cb(k.GetString(), Self{v}); -+ } -+} -+void Self::iterate_array(ArrayElementCallback cb) const { -+ auto& a = cbor_.GetArray(); -+ for (auto& e : a) { -+ cb(Self{e}); -+ } -+} -+ -+Self::ChromiumCborAdapter(cbor::Value const& v) : cbor_{v.Clone()} {} -+Self::ChromiumCborAdapter(cbor::Value&& v) : cbor_{std::move(v)} {} -+Self::ChromiumCborAdapter(ChromiumCborAdapter const& rhs) -+ : cbor_{rhs.cbor_.Clone()} {} -+ -+Self::~ChromiumCborAdapter() noexcept {} -diff --git a/components/ipfs/chromium_cbor_adapter.h b/components/ipfs/chromium_cbor_adapter.h -new file mode 100644 -index 0000000000000..65c2d746e6630 ---- /dev/null -+++ b/components/ipfs/chromium_cbor_adapter.h -@@ -0,0 +1,33 @@ -+#ifndef IPFS_CHROMIUM_CBOR_ADAPTER_H_ -+#define IPFS_CHROMIUM_CBOR_ADAPTER_H_ -+ -+#include -+ -+#include -+ -+namespace ipfs { -+class ChromiumCborAdapter final : public DagCborValue { -+ cbor::Value cbor_; -+ -+ std::unique_ptr at(std::string_view) const override; -+ std::optional as_unsigned() const override; -+ std::optional as_signed() const override; -+ std::optional as_float() const override; -+ std::optional as_string() const override; -+ std::optional> as_bytes() const override; -+ std::optional as_link() const override; -+ std::optional as_bool() const override; -+ bool is_map() const override; -+ bool is_array() const override; -+ void iterate_map(MapElementCallback) const override; -+ void iterate_array(ArrayElementCallback) const override; -+ -+ public: -+ ChromiumCborAdapter(cbor::Value&&); -+ ChromiumCborAdapter(cbor::Value const&); -+ ChromiumCborAdapter(ChromiumCborAdapter const& rhs); -+ ~ChromiumCborAdapter() noexcept override; -+}; -+} // namespace ipfs -+ -+#endif // IPFS_CHROMIUM_CBOR_ADAPTER_H_ -diff --git a/components/ipfs/chromium_ipfs_context.cc b/components/ipfs/chromium_ipfs_context.cc -new file mode 100644 -index 0000000000000..9346fc1ec3bf9 ---- /dev/null -+++ b/components/ipfs/chromium_ipfs_context.cc -@@ -0,0 +1,125 @@ -+#include "chromium_ipfs_context.h" -+ -+#include "block_http_request.h" -+#include "chromium_cbor_adapter.h" -+#include "chromium_json_adapter.h" -+#include "crypto_api.h" -+#include "inter_request_state.h" -+#include "preferences.h" -+ -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include -+ -+#include -+#include -+ -+#include -+ -+#include -+#include -+ -+ -+using Self = ipfs::ChromiumIpfsContext; -+ -+void Self::SetLoaderFactory(network::mojom::URLLoaderFactory& lf) { -+ loader_factory_ = &lf; -+} -+ -+std::string Self::MimeType(std::string extension, -+ std::string_view content, -+ std::string const& url) const { -+ std::string result; -+ auto fp_ext = base::FilePath::FromUTF8Unsafe(extension).value(); -+ VLOG(2) << "extension=" << extension << "content.size()=" << content.size() -+ << "(as-if) url for mime type:" << url; -+ if (extension.empty()) { -+ result.clear(); -+ } else if (net::GetWellKnownMimeTypeFromExtension(fp_ext, &result)) { -+ VLOG(2) << "Got " << result << " from extension " << extension << " for " -+ << url; -+ } else { -+ result.clear(); -+ } -+ auto head_size = std::min(content.size(), 999'999UL); -+ if (net::SniffMimeType({content.data(), head_size}, GURL{url}, result, -+ net::ForceSniffFileUrlsForHtml::kDisabled, &result)) { -+ VLOG(2) << "Got " << result << " from content of " << url; -+ } -+ if (result.empty() || result == "application/octet-stream") { -+ net::SniffMimeTypeFromLocalData({content.data(), head_size}, &result); -+ VLOG(2) << "Falling all the way back to content type " << result; -+ } -+ return result; -+} -+std::string Self::UnescapeUrlComponent(std::string_view comp) const { -+ using Rule = base::UnescapeRule; -+ auto rules = Rule::PATH_SEPARATORS | -+ Rule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS | Rule::SPACES; -+ auto result = base::UnescapeURLComponent({comp.data(), comp.size()}, rules); -+ return result; -+} -+void Self::SendDnsTextRequest(std::string host, -+ DnsTextResultsCallback res, -+ DnsTextCompleteCallback don) { -+ if (dns_reqs_.find(host) != dns_reqs_.end()) { -+ LOG(ERROR) << "Requested resolution of DNSLink host " << host -+ << " multiple times."; -+ } -+ auto don_wrap = [don, this, host]() { -+ don(); -+ LOG(INFO) << "Finished resolving " << host << " via DNSLink"; -+ dns_reqs_.erase(host); -+ }; -+ auto* nc = state_->network_context(); -+ dns_reqs_[host] = std::make_unique(host, res, don_wrap, nc); -+} -+void Self::SendHttpRequest(HttpRequestDescription req_inf, -+ HttpCompleteCallback cb) const { -+ DCHECK(loader_factory_); -+ auto ptr = std::make_shared(req_inf, cb); -+ ptr->send(loader_factory_); -+} -+bool Self::VerifyKeySignature(SigningKeyType t, -+ ByteView signature, -+ ByteView data, -+ ByteView key_bytes) const { -+ return crypto_api::VerifySignature(static_cast(t), signature, -+ data, key_bytes); -+} -+auto Self::ParseCbor(ipfs::ContextApi::ByteView bytes) const -+ -> std::unique_ptr { -+ cbor::Reader::Config cfg; -+ cfg.parse_tags = true; -+ auto parsed = cbor::Reader::Read(as_octets(bytes), cfg); -+ if (parsed.has_value()) { -+ return std::make_unique(std::move(parsed.value())); -+ } -+ LOG(ERROR) << "Failed to parse CBOR."; -+ return {}; -+} -+auto Self::ParseJson(std::string_view j_str) const -+ -> std::unique_ptr { -+ auto d = base::JSONReader::Read(j_str, base::JSON_ALLOW_TRAILING_COMMAS); -+ if (d) { -+ return std::make_unique(std::move(d.value())); -+ } -+ return {}; -+} -+ -+Self::ChromiumIpfsContext(InterRequestState& state, PrefService* prefs) -+ : state_{state} { -+ auto l = GetGatewayList(prefs); -+ for (auto gs : l) { -+ LOG(INFO) << "From pref: " << gs.prefix << '=' << gs.strength; -+ } -+} -+Self::~ChromiumIpfsContext() noexcept { -+ LOG(WARNING) << "API dtor - are all URIs loaded?"; -+} -+ -diff --git a/components/ipfs/chromium_ipfs_context.h b/components/ipfs/chromium_ipfs_context.h -new file mode 100644 -index 0000000000000..80f914a5394d8 ---- /dev/null -+++ b/components/ipfs/chromium_ipfs_context.h -@@ -0,0 +1,60 @@ -+#ifndef IPFS_CHROMIUM_IPFS_CONTEXT_H_ -+#define IPFS_CHROMIUM_IPFS_CONTEXT_H_ -+ -+#include "dns_txt_request.h" -+ -+#include -+#include -+ -+#include -+#include -+ -+#include -+ -+#include -+ -+class PrefService; -+ -+namespace network { -+class SimpleURLLoader; -+namespace mojom { -+class URLLoaderFactory; -+} -+} // namespace network -+ -+namespace ipfs { -+class InterRequestState; -+class IpfsRequest; -+class NetworkRequestor; -+ -+class ChromiumIpfsContext final : public ContextApi { -+ raw_ptr loader_factory_ = nullptr; -+ raw_ref state_; -+ std::map> dns_reqs_; -+ -+ std::string MimeType(std::string extension, -+ std::string_view content, -+ std::string const& url) const override; -+ std::string UnescapeUrlComponent(std::string_view) const override; -+ void SendDnsTextRequest(std::string, -+ DnsTextResultsCallback, -+ DnsTextCompleteCallback) override; -+ void SendHttpRequest(HttpRequestDescription req_inf, -+ HttpCompleteCallback cb) const override; -+ bool VerifyKeySignature(SigningKeyType, -+ ByteView signature, -+ ByteView data, -+ ByteView key_bytes) const override; -+ -+ std::unique_ptr ParseCbor(ByteView) const override; -+ std::unique_ptr ParseJson(std::string_view) const override; -+ -+ public: -+ ChromiumIpfsContext(InterRequestState&, PrefService* prefs); -+ ~ChromiumIpfsContext() noexcept override; -+ void SetLoaderFactory(network::mojom::URLLoaderFactory&); -+}; -+ -+} // namespace ipfs -+ -+#endif // IPFS_CHROMIUM_IPFS_CONTEXT_H_ -diff --git a/components/ipfs/chromium_json_adapter.cc b/components/ipfs/chromium_json_adapter.cc -new file mode 100644 -index 0000000000000..92c1c19aa35ce ---- /dev/null -+++ b/components/ipfs/chromium_json_adapter.cc -@@ -0,0 +1,48 @@ -+#include "chromium_json_adapter.h" -+ -+using Self = ipfs::ChromiumJsonAdapter; -+ -+Self::ChromiumJsonAdapter(base::Value d) : data_(std::move(d)) {} -+Self::~ChromiumJsonAdapter() noexcept {} -+std::string Self::pretty_print() const { -+ return data_.DebugString(); -+} -+std::optional Self::get_if_string() const { -+ auto* s = data_.GetIfString(); -+ if (s) { -+ return *s; -+ } else { -+ return std::nullopt; -+ } -+} -+auto Self::operator[](std::string_view k) const -+ -> std::unique_ptr { -+ if (auto* m = data_.GetIfDict()) { -+ if (auto* v = m->Find(k)) { -+ return std::make_unique(v->Clone()); -+ } -+ } -+ return {}; -+} -+bool Self::iterate_list(std::function cb) const { -+ auto* l = data_.GetIfList(); -+ if (!l) { -+ return false; -+ } -+ for (auto& v : *l) { -+ Self wrap(v.Clone()); -+ cb(wrap); -+ } -+ return true; -+} -+std::optional> Self::object_keys() const { -+ auto* m = data_.GetIfDict(); -+ if (!m) { -+ return std::nullopt; -+ } -+ std::vector rv; -+ for (auto [k, v] : *m) { -+ rv.push_back(k); -+ } -+ return rv; -+} -\ No newline at end of file -diff --git a/components/ipfs/chromium_json_adapter.h b/components/ipfs/chromium_json_adapter.h -new file mode 100644 -index 0000000000000..8e5e26aa3150e ---- /dev/null -+++ b/components/ipfs/chromium_json_adapter.h -@@ -0,0 +1,22 @@ -+#ifndef IPFS_CHROMIUM_JSON_ADAPTER_H_ -+#define IPFS_CHROMIUM_JSON_ADAPTER_H_ -+ -+#include -+#include -+ -+namespace ipfs { -+class ChromiumJsonAdapter final : public ipfs::DagJsonValue { -+ base::Value data_; -+ std::string pretty_print() const override; -+ std::unique_ptr operator[](std::string_view) const override; -+ std::optional get_if_string() const override; -+ std::optional> object_keys() const override; -+ bool iterate_list(std::function) const override; -+ -+ public: -+ ChromiumJsonAdapter(base::Value); -+ ~ChromiumJsonAdapter() noexcept override; -+}; -+} // namespace ipfs -+ -+#endif // IPFS_CHROMIUM_JSON_ADAPTER_H_ -diff --git a/components/ipfs/crypto_api.cc b/components/ipfs/crypto_api.cc -new file mode 100644 -index 0000000000000..d15a63f1f577c ---- /dev/null -+++ b/components/ipfs/crypto_api.cc -@@ -0,0 +1,62 @@ -+#include "crypto_api.h" -+ -+#include "base/logging.h" -+#include "components/webcrypto/algorithm_implementations.h" -+#include "components/webcrypto/status.h" -+#include "third_party/blink/public/platform/web_crypto_key.h" -+#include "third_party/boringssl/src/include/openssl/evp.h" -+ -+namespace { -+int ToEvpKeyType(ipfs::ipns::KeyType t) { -+ using ipfs::ipns::KeyType; -+ switch (t) { -+ case KeyType::ECDSA: -+ LOG(ERROR) << "TODO Check on ECDSA key type translation."; -+ return EVP_PKEY_EC; -+ case KeyType::Ed25519: -+ return EVP_PKEY_ED25519; -+ case KeyType::RSA: -+ return EVP_PKEY_RSA; -+ case KeyType::Secp256k1: -+ LOG(ERROR) << "TODO Check on Secp256k1 key type translation."; -+ return EVP_PKEY_DSA; -+ default: -+ LOG(ERROR) << "Invalid key type: " << static_cast(t); -+ return EVP_PKEY_NONE; -+ } -+} -+} // namespace -+ -+namespace cpto = ipfs::crypto_api; -+ -+bool cpto::VerifySignature(ipfs::ipns::KeyType key_type, -+ ipfs::ByteView signature, -+ ipfs::ByteView data, -+ ipfs::ByteView key_bytes) { -+ auto* key_p = reinterpret_cast(key_bytes.data()); -+ auto* data_p = reinterpret_cast(data.data()); -+ auto* sig_p = reinterpret_cast(signature.data()); -+ auto kt = ToEvpKeyType(key_type); -+ std::clog << "data:"; -+ for (auto b : data) { -+ std::clog << ' ' << std::hex << static_cast(b); -+ } -+ std::clog << ' ' << data.size() << " bytes.\n"; -+ bssl::UniquePtr pkey(EVP_PKEY_new_raw_public_key( -+ kt, /*engine*/ nullptr, key_p, key_bytes.size())); -+ bssl::ScopedEVP_MD_CTX ctx; -+ if (!EVP_DigestVerifyInit(ctx.get(), /*pctx=*/nullptr, /*type=*/nullptr, -+ /*e=*/nullptr, pkey.get())) { -+ LOG(ERROR) << "EVP_DigestVerifyInit failed"; -+ return false; -+ } -+ // auto* prefix = reinterpret_cast( -+ // "\x69\x70\x6e\x73\x2d\x73\x69\x67\x6e\x61\x74\x75\x72\x65\x3a"); -+ // std::basic_string to_verify = prefix; -+ // to_verify.append(data_p, data.size()); -+ auto result = -+ EVP_DigestVerify(ctx.get(), sig_p, signature.size(), data_p, data.size()); -+ // to_verify.data(), to_verify.size()); -+ LOG(INFO) << "EVP_DigestVerify returned " << result; -+ return result == 1; -+} -diff --git a/components/ipfs/crypto_api.h b/components/ipfs/crypto_api.h -new file mode 100644 -index 0000000000000..1363bb1fec6df ---- /dev/null -+++ b/components/ipfs/crypto_api.h -@@ -0,0 +1,22 @@ -+#ifndef IPFS_VALIDATE_SIGNATURE_H_ -+#define IPFS_VALIDATE_SIGNATURE_H_ -+ -+#include "components/webcrypto/algorithm_implementation.h" -+ -+#include "third_party/ipfs_client/keys.pb.h" -+ -+#include -+ -+namespace ipfs::crypto_api { -+/* -+using Algo = std::pair>; -+Algo GetAlgo(ipfs::ipns::KeyType); -+*/ -+bool VerifySignature(ipfs::ipns::KeyType, -+ ByteView signature, -+ ByteView data, -+ ByteView key); -+} // namespace ipfs::crypto_api -+ -+#endif // IPFS_VALIDATE_SIGNATURE_H_ -diff --git a/components/ipfs/dns_txt_request.cc b/components/ipfs/dns_txt_request.cc -new file mode 100644 -index 0000000000000..c7e8e667d5f05 ---- /dev/null -+++ b/components/ipfs/dns_txt_request.cc -@@ -0,0 +1,39 @@ -+#include "dns_txt_request.h" -+ -+#include -+#include -+ -+namespace moj = network::mojom; -+using Self = ipfs::DnsTxtRequest; -+ -+Self::DnsTxtRequest(std::string host, -+ ipfs::ContextApi::DnsTextResultsCallback res, -+ ipfs::ContextApi::DnsTextCompleteCallback don, -+ moj::NetworkContext* network_context) -+ : results_callback_{res}, completion_callback_{don} { -+ auto params = moj::ResolveHostParameters::New(); -+ params->dns_query_type = net::DnsQueryType::TXT; -+ params->initial_priority = net::RequestPriority::HIGHEST; -+ params->source = net::HostResolverSource::ANY; -+ params->cache_usage = moj::ResolveHostParameters_CacheUsage::STALE_ALLOWED; -+ params->secure_dns_policy = moj::SecureDnsPolicy::ALLOW; -+ params->purpose = moj::ResolveHostParameters::Purpose::kUnspecified; -+ LOG(INFO) << "Querying DNS for TXT records on " << host; -+ auto hrh = moj::HostResolverHost::NewHostPortPair({host, 0}); -+ auto nak = net::NetworkAnonymizationKey::CreateTransient(); -+ network_context->ResolveHost(std::move(hrh), nak, std::move(params), -+ recv_.BindNewPipeAndPassRemote()); -+} -+Self::~DnsTxtRequest() {} -+ -+void Self::OnTextResults(std::vector const& results) { -+ LOG(INFO) << "Hit " << results.size() << " DNS TXT results."; -+ results_callback_(results); -+} -+void Self::OnComplete(int32_t result, -+ const ::net::ResolveErrorInfo&, -+ const absl::optional<::net::AddressList>&, -+ const absl::optional&) { -+ LOG(INFO) << "DNS Results done with code: " << result; -+ completion_callback_(); -+} -diff --git a/components/ipfs/dns_txt_request.h b/components/ipfs/dns_txt_request.h -new file mode 100644 -index 0000000000000..a6c72e467d11f ---- /dev/null -+++ b/components/ipfs/dns_txt_request.h -@@ -0,0 +1,36 @@ -+#ifndef IPFS_DNS_TXT_REQUEST_H_ -+#define IPFS_DNS_TXT_REQUEST_H_ -+ -+#include -+ -+#include -+#include -+ -+namespace network::mojom { -+class NetworkContext; -+} -+ -+namespace ipfs { -+class DnsTxtRequest final : public network::ResolveHostClientBase { -+ ipfs::ContextApi::DnsTextResultsCallback results_callback_; -+ ipfs::ContextApi::DnsTextCompleteCallback completion_callback_; -+ mojo::Receiver recv_{this}; -+ -+ using Endpoints = std::vector<::net::HostResolverEndpointResult>; -+ void OnTextResults(std::vector const&) override; -+ void OnComplete(int32_t result, -+ ::net::ResolveErrorInfo const&, -+ absl::optional<::net::AddressList> const&, -+ absl::optional const&) override; -+ -+ public: -+ DnsTxtRequest(std::string, -+ ipfs::ContextApi::DnsTextResultsCallback, -+ ipfs::ContextApi::DnsTextCompleteCallback, -+ network::mojom::NetworkContext*); -+ DnsTxtRequest(DnsTxtRequest&&) = delete; -+ ~DnsTxtRequest() noexcept override; -+}; -+} // namespace ipfs -+ -+#endif // IPFS_DNS_TXT_REQUEST_H_ -diff --git a/components/ipfs/export.h b/components/ipfs/export.h -new file mode 100644 -index 0000000000000..8161da33aca16 ---- /dev/null -+++ b/components/ipfs/export.h -@@ -0,0 +1,16 @@ -+#ifndef IPFS_EXPORT_H_ -+#define IPFS_EXPORT_H_ -+ -+#if __has_include() -+#include -+#else -+ -+#ifndef IS_IPFS_IMPL -+#if !defined(COMPONENT_EXPORT) -+#define COMPONENT_EXPORT(IPFS) -+#endif -+#endif -+ -+#endif -+ -+#endif // IPFS_EXPORT_H_ -diff --git a/components/ipfs/inter_request_state.cc b/components/ipfs/inter_request_state.cc -new file mode 100644 -index 0000000000000..b20a013a9d646 ---- /dev/null -+++ b/components/ipfs/inter_request_state.cc -@@ -0,0 +1,72 @@ -+#include "inter_request_state.h" -+ -+#include "chromium_ipfs_context.h" -+#include "preferences.h" -+ -+#include -+#include "content/public/browser/browser_context.h" -+ -+#include -+#include -+#include -+#include -+ -+using Self = ipfs::InterRequestState; -+ -+namespace { -+constexpr char user_data_key[] = "ipfs_request_userdata"; -+} -+ -+void Self::CreateForBrowserContext(content::BrowserContext* c, PrefService* p) { -+ DCHECK(c); -+ DCHECK(p); -+ LOG(INFO) << "Creating new IPFS state for this browser context."; -+ auto owned = std::make_unique(c->GetPath(), p); -+ c->SetUserData(user_data_key, std::move(owned)); -+} -+auto Self::FromBrowserContext(content::BrowserContext* context) -+ -> InterRequestState& { -+ static ipfs::InterRequestState static_state({}, {}); -+ if (!context) { -+ LOG(WARNING) << "No browser context! Using a default IPFS state."; -+ return static_state; -+ } -+ base::SupportsUserData::Data* existing = context->GetUserData(user_data_key); -+ if (existing) { -+ VLOG(2) << "Re-using existing IPFS state."; -+ return *static_cast(existing); -+ } else { -+ LOG(ERROR) << "Browser context has no IPFS state! It must be set earlier!"; -+ return static_state; -+ } -+} -+std::shared_ptr Self::api() { -+ return api_; -+} -+auto Self::cache() -> std::shared_ptr& { -+ if (!cache_) { -+ cache_ = std::make_shared(*this, disk_path_); -+ } -+ return cache_; -+} -+auto Self::orchestrator() -> Orchestrator& { -+ if (!orc_) { -+ auto rtor = -+ gw::default_requestor(Gateways::DefaultGateways(), cache(), api()); -+ orc_ = std::make_shared(rtor, api()); -+ } -+ return *orc_; -+} -+void Self::network_context(network::mojom::NetworkContext* val) { -+ network_context_ = val; -+} -+network::mojom::NetworkContext* Self::network_context() const { -+ return network_context_; -+} -+Self::InterRequestState(base::FilePath p, PrefService* prefs) -+ : api_{std::make_shared(*this, prefs)}, -+ disk_path_{p} {} -+Self::~InterRequestState() noexcept { -+ network_context_ = nullptr; -+ cache_.reset(); -+} -diff --git a/components/ipfs/inter_request_state.h b/components/ipfs/inter_request_state.h -new file mode 100644 -index 0000000000000..3471cfe5d15c9 ---- /dev/null -+++ b/components/ipfs/inter_request_state.h -@@ -0,0 +1,51 @@ -+#ifndef IPFS_INTER_REQUEST_STATE_H_ -+#define IPFS_INTER_REQUEST_STATE_H_ -+ -+#include "cache_requestor.h" -+ -+#include "ipfs_client/gateways.h" -+#include "ipfs_client/ipns_names.h" -+#include "ipfs_client/orchestrator.h" -+ -+#include "base/supports_user_data.h" -+#include "services/network/network_context.h" -+ -+class PrefService; -+ -+namespace content { -+class BrowserContext; -+} -+ -+namespace ipfs { -+class Scheduler; -+class ChromiumIpfsContext; -+class COMPONENT_EXPORT(IPFS) InterRequestState -+ : public base::SupportsUserData::Data { -+ IpnsNames names_; -+ std::shared_ptr api_; -+ std::time_t last_discovery_ = 0; -+ std::shared_ptr cache_; -+ base::FilePath const disk_path_; -+ std::shared_ptr orc_; // TODO - map of origin to Orchestrator -+ raw_ptr network_context_; -+ -+ std::shared_ptr& cache(); -+ -+ public: -+ InterRequestState(base::FilePath, PrefService*); -+ ~InterRequestState() noexcept override; -+ -+ IpnsNames& names() { return names_; } -+ Scheduler& scheduler(); -+ std::shared_ptr api(); -+ std::array,2> serialized_caches(); -+ Orchestrator& orchestrator(); -+ void network_context(network::mojom::NetworkContext*); -+ network::mojom::NetworkContext* network_context() const; -+ -+ static void CreateForBrowserContext(content::BrowserContext*, PrefService*); -+ static InterRequestState& FromBrowserContext(content::BrowserContext*); -+}; -+} // namespace ipfs -+ -+#endif // IPFS_INTER_REQUEST_STATE_H_ -diff --git a/components/ipfs/interceptor.cc b/components/ipfs/interceptor.cc -new file mode 100644 -index 0000000000000..39b5de32b87ef ---- /dev/null -+++ b/components/ipfs/interceptor.cc -@@ -0,0 +1,36 @@ -+#include "interceptor.h" -+ -+#include "inter_request_state.h" -+#include "ipfs_url_loader.h" -+ -+#include "base/logging.h" -+#include "services/network/public/cpp/resource_request.h" -+#include "services/network/public/mojom/url_response_head.mojom.h" -+#include "services/network/url_loader_factory.h" -+#include "url/url_util.h" -+ -+using Interceptor = ipfs::Interceptor; -+ -+Interceptor::Interceptor(network::mojom::URLLoaderFactory* handles_http, -+ network::mojom::NetworkContext* network_context) -+ : loader_factory_{handles_http}, network_context_{network_context} {} -+ -+void Interceptor::MaybeCreateLoader(network::ResourceRequest const& req, -+ content::BrowserContext* context, -+ LoaderCallback loader_callback) { -+ auto& state = InterRequestState::FromBrowserContext(context); -+ state.network_context(network_context_); -+ if (req.url.SchemeIs("ipfs") || req.url.SchemeIs("ipns")) { -+ auto hdr_str = req.headers.ToString(); -+ std::replace(hdr_str.begin(), hdr_str.end(), '\r', ' '); -+ VLOG(1) << req.url.spec() << " getting intercepted! Headers: \n" << hdr_str; -+ DCHECK(context); -+ auto loader = -+ std::make_shared(*loader_factory_, state); -+ std::move(loader_callback) -+ .Run(base::BindOnce(&ipfs::IpfsUrlLoader::StartRequest, loader)); -+ -+ } else { -+ std::move(loader_callback).Run({}); // SEP -+ } -+} -diff --git a/components/ipfs/interceptor.h b/components/ipfs/interceptor.h -new file mode 100644 -index 0000000000000..0321ea5481864 ---- /dev/null -+++ b/components/ipfs/interceptor.h -@@ -0,0 +1,30 @@ -+#ifndef IPFS_INTERCEPTOR_H_ -+#define IPFS_INTERCEPTOR_H_ -+ -+#include "content/public/browser/url_loader_request_interceptor.h" -+ -+class PrefService; -+namespace network::mojom { -+class URLLoaderFactory; -+class NetworkContext; -+} // namespace network::mojom -+ -+namespace ipfs { -+ -+class COMPONENT_EXPORT(IPFS) Interceptor final -+ : public content::URLLoaderRequestInterceptor { -+ raw_ptr loader_factory_; -+ raw_ptr network_context_; -+ raw_ptr pref_svc_; -+ -+ void MaybeCreateLoader(network::ResourceRequest const&, -+ content::BrowserContext*, -+ LoaderCallback) override; -+ -+ public: -+ Interceptor(network::mojom::URLLoaderFactory* handles_http, -+ network::mojom::NetworkContext*); -+}; -+} // namespace ipfs -+ -+#endif // IPFS_INTERCEPTOR_H_ -diff --git a/components/ipfs/ipfs_features.cc b/components/ipfs/ipfs_features.cc -new file mode 100644 -index 0000000000000..a0a729d5aa8e6 ---- /dev/null -+++ b/components/ipfs/ipfs_features.cc -@@ -0,0 +1,7 @@ -+#include "ipfs_features.h" -+ -+namespace ipfs { -+ -+BASE_FEATURE(kEnableIpfs, "EnableIpfs", base::FEATURE_DISABLED_BY_DEFAULT); -+ -+} -diff --git a/components/ipfs/ipfs_features.h b/components/ipfs/ipfs_features.h -new file mode 100644 -index 0000000000000..2e54462b135a9 ---- /dev/null -+++ b/components/ipfs/ipfs_features.h -@@ -0,0 +1,13 @@ -+#ifndef IPFS_IPFS_FEATURES_H_ -+#define IPFS_IPFS_FEATURES_H_ -+ -+#include "base/component_export.h" -+#include "base/feature_list.h" -+ -+namespace ipfs { -+ -+COMPONENT_EXPORT(IPFS) BASE_DECLARE_FEATURE(kEnableIpfs); -+ -+} // namespace ipfs -+ -+#endif // IPFS_IPFS_FEATURES_H_ -diff --git a/components/ipfs/ipfs_url_loader.cc b/components/ipfs/ipfs_url_loader.cc -new file mode 100644 -index 0000000000000..afc97dc425b4f ---- /dev/null -+++ b/components/ipfs/ipfs_url_loader.cc -@@ -0,0 +1,194 @@ -+#include "ipfs_url_loader.h" -+ -+#include "chromium_ipfs_context.h" -+#include "inter_request_state.h" -+ -+#include "ipfs_client/gateways.h" -+#include "ipfs_client/ipfs_request.h" -+ -+#include "base/debug/stack_trace.h" -+#include "base/notreached.h" -+#include "base/strings/stringprintf.h" -+#include "base/threading/platform_thread.h" -+#include "net/http/http_status_code.h" -+#include "services/network/public/cpp/parsed_headers.h" -+#include "services/network/public/cpp/simple_url_loader.h" -+#include "services/network/public/mojom/url_loader_factory.mojom.h" -+#include "services/network/public/mojom/url_response_head.mojom.h" -+#include "services/network/url_loader_factory.h" -+ -+#include -+ -+ipfs::IpfsUrlLoader::IpfsUrlLoader( -+ network::mojom::URLLoaderFactory& handles_http, -+ InterRequestState& state) -+ : state_{state}, lower_loader_factory_{handles_http}, api_{state_->api()} {} -+ipfs::IpfsUrlLoader::~IpfsUrlLoader() noexcept { -+ if (!complete_) { -+ LOG(ERROR) << "Premature IPFS URLLoader dtor, uri was '" << original_url_ -+ << "' " << base::debug::StackTrace(); -+ } -+} -+ -+void ipfs::IpfsUrlLoader::FollowRedirect( -+ std::vector const& // removed_headers -+ , -+ net::HttpRequestHeaders const& // modified_headers -+ , -+ net::HttpRequestHeaders const& // modified_cors_exempt_headers -+ , -+ absl::optional<::GURL> const& // new_url -+) { -+ NOTIMPLEMENTED(); -+} -+ -+void ipfs::IpfsUrlLoader::SetPriority(net::RequestPriority priority, -+ int32_t intra_prio_val) { -+ VLOG(1) << "TODO SetPriority(" << priority << ',' << intra_prio_val << ')'; -+} -+ -+void ipfs::IpfsUrlLoader::PauseReadingBodyFromNet() { -+ NOTIMPLEMENTED(); -+} -+ -+void ipfs::IpfsUrlLoader::ResumeReadingBodyFromNet() { -+ NOTIMPLEMENTED(); -+} -+ -+void ipfs::IpfsUrlLoader::StartRequest( -+ std::shared_ptr me, -+ network::ResourceRequest const& resource_request, -+ mojo::PendingReceiver receiver, -+ mojo::PendingRemote client) { -+ DCHECK(!me->receiver_.is_bound()); -+ DCHECK(!me->client_.is_bound()); -+ me->receiver_.Bind(std::move(receiver)); -+ me->client_.Bind(std::move(client)); -+ if (me->original_url_.empty()) { -+ me->original_url_ = resource_request.url.spec(); -+ } -+ if (resource_request.url.SchemeIs("ipfs") || -+ resource_request.url.SchemeIs("ipns")) { -+ auto ns = resource_request.url.scheme(); -+ auto cid_str = resource_request.url.host(); -+ auto path = resource_request.url.path(); -+ auto abs_path = "/" + ns + "/" + cid_str + path; -+ VLOG(1) << resource_request.url.spec() << " -> " << abs_path; -+ me->root_ = cid_str; -+ me->api_->SetLoaderFactory(*(me->lower_loader_factory_)); -+ auto whendone = [me](IpfsRequest const& req, ipfs::Response const& res) { -+ VLOG(1) << "whendone(" << req.path().to_string() << ',' << res.status_ -+ << ',' << res.body_.size() << "B mime=" << res.mime_ << ')'; -+ if (!res.body_.empty()) { -+ me->ReceiveBlockBytes(res.body_); -+ } -+ me->status_ = res.status_; -+ me->resp_loc_ = res.location_; -+ if (res.status_ == Response::IMMUTABLY_GONE.status_) { -+ auto p = req.path(); -+ p.pop(); -+ std::string cid{p.pop()}; -+ me->DoesNotExist(cid, p.to_string()); -+ } else { -+ me->BlocksComplete(res.mime_); -+ } -+ DCHECK(me->complete_); -+ }; -+ auto req = std::make_shared(abs_path, whendone); -+ me->state_->orchestrator().build_response(req); -+ } else { -+ LOG(ERROR) << "Wrong scheme: " << resource_request.url.scheme(); -+ } -+} -+ -+void ipfs::IpfsUrlLoader::OverrideUrl(GURL u) { -+ original_url_ = u.spec(); -+} -+void ipfs::IpfsUrlLoader::AddHeader(std::string_view a, std::string_view b) { -+ VLOG(1) << "AddHeader(" << a << ',' << b << ')'; -+ additional_outgoing_headers_.emplace_back(a, b); -+} -+ -+void ipfs::IpfsUrlLoader::BlocksComplete(std::string mime_type) { -+ VLOG(1) << "Resolved from unix-fs dag a file of type: " << mime_type -+ << " will report it as " << original_url_; -+ if (complete_) { -+ return; -+ } -+ auto result = -+ mojo::CreateDataPipe(partial_block_.size(), pipe_prod_, pipe_cons_); -+ if (result) { -+ LOG(ERROR) << " ERROR: TaskFailed to create data pipe: " << result; -+ return; -+ } -+ complete_ = true; -+ auto head = network::mojom::URLResponseHead::New(); -+ if (mime_type.size()) { -+ head->mime_type = mime_type; -+ } -+ std::uint32_t byte_count = partial_block_.size(); -+ VLOG(1) << "Calling WriteData(" << byte_count << ")"; -+ pipe_prod_->WriteData(partial_block_.data(), &byte_count, -+ MOJO_BEGIN_WRITE_DATA_FLAG_ALL_OR_NONE); -+ VLOG(1) << "Called WriteData(" << byte_count << ")"; -+ head->content_length = byte_count; -+ head->headers = -+ net::HttpResponseHeaders::TryToCreate("access-control-allow-origin: *"); -+ if (resp_loc_.size()) { -+ head->headers->AddHeader("Location", resp_loc_); -+ } -+ if (!head->headers) { -+ LOG(ERROR) << "\n\tFailed to create headers!\n"; -+ return; -+ } -+ auto* reason = -+ net::GetHttpReasonPhrase(static_cast(status_)); -+ auto status_line = base::StringPrintf("HTTP/1.1 %d %s", status_, reason); -+ VLOG(1) << "Returning with status line '" << status_line << "'.\n"; -+ head->headers->ReplaceStatusLine(status_line); -+ if (mime_type.size()) { -+ head->headers->SetHeader("Content-Type", mime_type); -+ } -+ head->headers->SetHeader("Access-Control-Allow-Origin", "*"); -+ head->was_fetched_via_spdy = false; -+ for (auto& [n, v] : additional_outgoing_headers_) { -+ VLOG(1) << "Appending 'additional' header:" << n << '=' << v << '.'; -+ head->headers->AddHeader(n, v); -+ } -+ VLOG(1) << "Calling PopulateParsedHeaders"; -+ head->parsed_headers = -+ network::PopulateParsedHeaders(head->headers.get(), GURL{original_url_}); -+ VLOG(1) << "Sending response for " << original_url_ << " with mime type " -+ << head->mime_type << " and status line " << status_line; -+ if (status_ / 100 == 3 && resp_loc_.size()) { -+ auto ri = net::RedirectInfo::ComputeRedirectInfo( -+ "GET", GURL{original_url_}, net::SiteForCookies{}, -+ net::RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT, -+ net::ReferrerPolicy::NO_REFERRER, "", status_, GURL{resp_loc_}, -+ std::nullopt, false); -+ client_->OnReceiveRedirect(ri, std::move(head)); -+ } else { -+ client_->OnReceiveResponse(std::move(head), std::move(pipe_cons_), -+ absl::nullopt); -+ } -+ client_->OnComplete(network::URLLoaderCompletionStatus{}); -+ stepper_.reset(); -+} -+ -+void ipfs::IpfsUrlLoader::DoesNotExist(std::string_view cid, -+ std::string_view path) { -+ LOG(ERROR) << "Immutable data 404 for " << cid << '/' << path; -+ complete_ = true; -+ client_->OnComplete( -+ network::URLLoaderCompletionStatus{net::ERR_FILE_NOT_FOUND}); -+ stepper_.reset(); -+} -+void ipfs::IpfsUrlLoader::NotHere(std::string_view cid, std::string_view path) { -+ LOG(INFO) << "TODO " << __func__ << '(' << cid << ',' << path << ')'; -+} -+ -+void ipfs::IpfsUrlLoader::ReceiveBlockBytes(std::string_view content) { -+ partial_block_.append(content); -+ VLOG(2) << "Recived a block of size " << content.size() << " now have " -+ << partial_block_.size() << " bytes."; -+} -diff --git a/components/ipfs/ipfs_url_loader.h b/components/ipfs/ipfs_url_loader.h -new file mode 100644 -index 0000000000000..dc324f7b11f2d ---- /dev/null -+++ b/components/ipfs/ipfs_url_loader.h -@@ -0,0 +1,96 @@ -+#ifndef COMPONENTS_IPFS_URL_LOADER_H_ -+#define COMPONENTS_IPFS_URL_LOADER_H_ 1 -+ -+#include "base/debug/debugging_buildflags.h" -+#include "base/timer/timer.h" -+#include "mojo/public/cpp/bindings/receiver_set.h" -+#include "mojo/public/cpp/system/data_pipe.h" -+#include "net/http/http_request_headers.h" -+#include "services/network/public/cpp/resolve_host_client_base.h" -+#include "services/network/public/cpp/resource_request.h" -+#include "services/network/public/mojom/url_loader.mojom.h" -+ -+#include -+ -+namespace ipfs { -+class ChromiumIpfsContext; -+} // namespace ipfs -+ -+namespace network::mojom { -+class URLLoaderFactory; -+class HostResolver; -+class NetworkContext; -+} // namespace network::mojom -+namespace network { -+class SimpleURLLoader; -+} -+ -+namespace ipfs { -+class InterRequestState; -+ -+class IpfsUrlLoader final : public network::mojom::URLLoader { -+ void FollowRedirect( -+ std::vector const& removed_headers, -+ net::HttpRequestHeaders const& modified_headers, -+ net::HttpRequestHeaders const& modified_cors_exempt_headers, -+ absl::optional<::GURL> const& new_url) override; -+ void SetPriority(net::RequestPriority priority, -+ int32_t intra_priority_value) override; -+ void PauseReadingBodyFromNet() override; -+ void ResumeReadingBodyFromNet() override; -+ -+ public: -+ explicit IpfsUrlLoader(network::mojom::URLLoaderFactory& handles_http, -+ InterRequestState& state); -+ ~IpfsUrlLoader() noexcept override; -+ -+ using ptr = std::shared_ptr; -+ -+ // Passed as the RequestHandler for -+ // Interceptor::MaybeCreateLoader. -+ static void StartRequest( -+ ptr, -+ network::ResourceRequest const& resource_request, -+ mojo::PendingReceiver receiver, -+ mojo::PendingRemote client); -+ -+ void OverrideUrl(GURL); -+ void AddHeader(std::string_view,std::string_view); -+ void extra(std::shared_ptr xtra) { extra_ = xtra; } -+ -+ private: -+ using RequestHandle = std::unique_ptr; -+ -+ raw_ref state_; -+ mojo::Receiver receiver_{this}; -+ mojo::Remote client_; -+ raw_ref lower_loader_factory_; -+ mojo::ScopedDataPipeProducerHandle pipe_prod_ = {}; -+ mojo::ScopedDataPipeConsumerHandle pipe_cons_ = {}; -+ bool complete_ = false; -+ std::shared_ptr api_; -+ std::string original_url_; -+ std::string partial_block_; -+ std::vector> additional_outgoing_headers_; -+ std::shared_ptr extra_; -+ std::unique_ptr stepper_; -+ std::string root_; -+ int status_ = 200; -+ std::string resp_loc_; -+ -+ void CreateBlockRequest(std::string cid); -+ -+ void ReceiveBlockBytes(std::string_view); -+ void BlocksComplete(std::string mime_type); -+ void DoesNotExist(std::string_view cid, std::string_view path); -+ void NotHere(std::string_view cid, std::string_view path); -+ -+ void StartUnixFsProc(ptr, std::string_view); -+ void AppendGatewayHeaders(std::vector const& cids, net::HttpResponseHeaders&); -+ void AppendGatewayInfoHeader(std::string const&, net::HttpResponseHeaders&); -+ void TakeStep(); -+}; -+ -+} // namespace ipfs -+ -+#endif -diff --git a/components/ipfs/url_loader_factory.cc b/components/ipfs/url_loader_factory.cc -new file mode 100644 -index 0000000000000..9a80284098748 ---- /dev/null -+++ b/components/ipfs/url_loader_factory.cc -@@ -0,0 +1,56 @@ -+#include "url_loader_factory.h" -+ -+#include "inter_request_state.h" -+#include "ipfs_url_loader.h" -+ -+void ipfs::IpfsURLLoaderFactory::Create( -+ NonNetworkURLLoaderFactoryMap* in_out, -+ content::BrowserContext* context, -+ URLLoaderFactory* default_factory, -+ network::mojom::NetworkContext* net_ctxt, -+ PrefService* pref_svc) { -+ for (char const* scheme : {"ipfs", "ipns"}) { -+ mojo::PendingRemote pending; -+ new IpfsURLLoaderFactory(scheme, pending.InitWithNewPipeAndPassReceiver(), -+ context, default_factory, net_ctxt, pref_svc); -+ in_out->emplace(scheme, std::move(pending)); -+ } -+} -+ -+ipfs::IpfsURLLoaderFactory::IpfsURLLoaderFactory( -+ std::string scheme, -+ mojo::PendingReceiver factory_receiver, -+ content::BrowserContext* context, -+ URLLoaderFactory* default_factory, -+ network::mojom::NetworkContext* net_ctxt, -+ PrefService* pref_svc) -+ : network::SelfDeletingURLLoaderFactory(std::move(factory_receiver)), -+ scheme_{scheme}, -+ context_{context}, -+ default_factory_{default_factory}, -+ network_context_{net_ctxt}, -+ pref_svc_{pref_svc} {} -+ -+ipfs::IpfsURLLoaderFactory::~IpfsURLLoaderFactory() noexcept { -+ context_ = nullptr; -+ default_factory_ = nullptr; -+ network_context_ = nullptr; -+} -+ -+void ipfs::IpfsURLLoaderFactory::CreateLoaderAndStart( -+ mojo::PendingReceiver loader, -+ int32_t /*request_id*/, -+ uint32_t /*options*/, -+ network::ResourceRequest const& request, -+ mojo::PendingRemote client, -+ net::MutableNetworkTrafficAnnotationTag const& // traffic_annotation -+) { -+ VLOG(2) << "IPFS subresource: case=" << scheme_ -+ << " url=" << request.url.spec(); -+ DCHECK(default_factory_); -+ if (scheme_ == "ipfs" || scheme_ == "ipns") { -+ auto ptr = std::make_shared( -+ *default_factory_, InterRequestState::FromBrowserContext(context_)); -+ ptr->StartRequest(ptr, request, std::move(loader), std::move(client)); -+ } -+} -diff --git a/components/ipfs/url_loader_factory.h b/components/ipfs/url_loader_factory.h -new file mode 100644 -index 0000000000000..01cd66ea6ed8f ---- /dev/null -+++ b/components/ipfs/url_loader_factory.h -@@ -0,0 +1,58 @@ -+#ifndef IPFS_URL_LOADER_FACTORY_H_ -+#define IPFS_URL_LOADER_FACTORY_H_ -+ -+#include "services/network/public/cpp/self_deleting_url_loader_factory.h" -+#include "services/network/public/mojom/url_loader_factory.mojom.h" -+ -+#include -+ -+class PrefService; -+namespace content { -+class BrowserContext; -+} -+namespace network { -+namespace mojom { -+class NetworkContext; -+} -+} // namespace network -+ -+namespace ipfs { -+using NonNetworkURLLoaderFactoryMap = -+ std::map>; -+ -+class COMPONENT_EXPORT(IPFS) IpfsURLLoaderFactory -+ : public network::SelfDeletingURLLoaderFactory { -+ public: -+ static void Create(NonNetworkURLLoaderFactoryMap* in_out, -+ content::BrowserContext*, -+ URLLoaderFactory*, -+ network::mojom::NetworkContext*, -+ PrefService*); -+ -+ private: -+ IpfsURLLoaderFactory(std::string, -+ mojo::PendingReceiver, -+ content::BrowserContext*, -+ network::mojom::URLLoaderFactory*, -+ network::mojom::NetworkContext*, -+ PrefService*); -+ ~IpfsURLLoaderFactory() noexcept override; -+ void CreateLoaderAndStart( -+ mojo::PendingReceiver loader, -+ int32_t request_id, -+ uint32_t options, -+ network::ResourceRequest const& request, -+ mojo::PendingRemote client, -+ net::MutableNetworkTrafficAnnotationTag const& traffic_annotation) -+ override; -+ -+ std::string scheme_; -+ raw_ptr context_; -+ raw_ptr default_factory_; -+ raw_ptr network_context_; -+ raw_ptr pref_svc_; -+}; -+} // namespace ipfs -+ -+#endif // IPFS_URL_LOADER_FACTORY_H_ -diff --git a/components/open_from_clipboard/clipboard_recent_content_generic.cc b/components/open_from_clipboard/clipboard_recent_content_generic.cc -index 4dcafecbc66c6..d205209c08162 100644 ---- a/components/open_from_clipboard/clipboard_recent_content_generic.cc -+++ b/components/open_from_clipboard/clipboard_recent_content_generic.cc -@@ -20,7 +20,7 @@ - namespace { - // Schemes appropriate for suggestion by ClipboardRecentContent. - const char* kAuthorizedSchemes[] = { -- url::kAboutScheme, url::kDataScheme, url::kHttpScheme, url::kHttpsScheme, -+ url::kAboutScheme, url::kDataScheme, url::kHttpScheme, url::kHttpsScheme, "ipfs", "ipns" - // TODO(mpearson): add support for chrome:// URLs. Right now the scheme - // for that lives in content and is accessible via - // GetEmbedderRepresentationOfAboutScheme() or content::kChromeUIScheme -diff --git a/net/dns/dns_config_service_linux.cc b/net/dns/dns_config_service_linux.cc -index 5273da5190277..12b28b86a4c00 100644 ---- a/net/dns/dns_config_service_linux.cc -+++ b/net/dns/dns_config_service_linux.cc -@@ -272,11 +272,11 @@ bool IsNsswitchConfigCompatible( - // Ignore any entries after `kDns` because Chrome will fallback to the - // system resolver if a result was not found in DNS. - return true; -- -+ case NsswitchReader::Service::kResolve: -+ break; - case NsswitchReader::Service::kMdns: - case NsswitchReader::Service::kMdns4: - case NsswitchReader::Service::kMdns6: -- case NsswitchReader::Service::kResolve: - case NsswitchReader::Service::kNis: - RecordIncompatibleNsswitchReason( - IncompatibleNsswitchReason::kIncompatibleService, -diff --git a/third_party/blink/renderer/platform/weborigin/scheme_registry.cc b/third_party/blink/renderer/platform/weborigin/scheme_registry.cc -index 4eadf46ea0c24..d62fc7fb14e01 100644 ---- a/third_party/blink/renderer/platform/weborigin/scheme_registry.cc -+++ b/third_party/blink/renderer/platform/weborigin/scheme_registry.cc -@@ -67,7 +67,7 @@ class URLSchemesRegistry final { - // is considered secure. Additional checks are performed to ensure that - // other http pages are filtered out. - service_worker_schemes({"http", "https"}), -- fetch_api_schemes({"http", "https"}), -+ fetch_api_schemes({"http", "https", "ipfs", "ipns"}), - allowed_in_referrer_schemes({"http", "https"}) { - for (auto& scheme : url::GetCorsEnabledSchemes()) - cors_enabled_schemes.insert(scheme.c_str()); -diff --git a/third_party/ipfs_client/BUILD.gn b/third_party/ipfs_client/BUILD.gn -new file mode 100644 -index 0000000000000..9eb6d56505851 ---- /dev/null -+++ b/third_party/ipfs_client/BUILD.gn -@@ -0,0 +1,200 @@ -+import("args.gni") -+import("//build/buildflag_header.gni") -+ -+buildflag_header("ipfs_buildflags") { -+ header = "ipfs_buildflags.h" -+ flags = [ "ENABLE_IPFS=$enable_ipfs" ] -+} -+ -+config("external_config") { -+ include_dirs = [ -+ "include", -+ ] -+} -+ -+if (enable_ipfs) { -+ cxx_sources = [ -+ "include/ipfs_client/block_requestor.h", -+ "include/ipfs_client/block_storage.h", -+ "include/ipfs_client/cid.h", -+ "include/ipfs_client/context_api.h", -+ "include/ipfs_client/crypto/hasher.h", -+ "include/ipfs_client/dag_cbor_value.h", -+ "include/ipfs_client/dag_json_value.h", -+ "include/ipfs_client/gateways.h", -+ "include/ipfs_client/gw/block_request_splitter.h", -+ "include/ipfs_client/gw/default_requestor.h", -+ "include/ipfs_client/gw/dnslink_requestor.h", -+ "include/ipfs_client/gw/gateway_request.h", -+ "include/ipfs_client/gw/inline_request_handler.h", -+ "include/ipfs_client/gw/requestor.h", -+ "include/ipfs_client/gw/terminating_requestor.h", -+ "include/ipfs_client/http_request_description.h", -+ "include/ipfs_client/identity_cid.h", -+ "include/ipfs_client/ipfs_request.h", -+ "include/ipfs_client/ipld/dag_node.h", -+ "include/ipfs_client/ipld/link.h", -+ "include/ipfs_client/ipld/resolution_state.h", -+ "include/ipfs_client/ipns_cbor_entry.h", -+ "include/ipfs_client/ipns_names.h", -+ "include/ipfs_client/ipns_record.h", -+ "include/ipfs_client/json_cbor_adapter.h", -+ "include/ipfs_client/logger.h", -+ "include/ipfs_client/multi_base.h", -+ "include/ipfs_client/multi_hash.h", -+ "include/ipfs_client/multicodec.h", -+ "include/ipfs_client/orchestrator.h", -+ "include/ipfs_client/pb_dag.h", -+ "include/ipfs_client/response.h", -+ "include/ipfs_client/signing_key_type.h", -+ "include/ipfs_client/url_spec.h", -+ "include/libp2p/common/types.hpp", -+ "include/libp2p/crypto/key.h", -+ "include/libp2p/crypto/protobuf/protobuf_key.hpp", -+ "include/libp2p/multi/multibase_codec.hpp", -+ "include/libp2p/multi/multibase_codec/codecs/base16.h", -+ "include/libp2p/multi/multibase_codec/codecs/base32.hpp", -+ "include/libp2p/multi/multibase_codec/codecs/base36.hpp", -+ "include/libp2p/multi/multibase_codec/codecs/base_error.hpp", -+ "include/libp2p/multi/multicodec_type.hpp", -+ "include/libp2p/multi/uvarint.hpp", -+ "include/multibase/algorithm.h", -+ "include/multibase/basic_algorithm.h", -+ "include/multibase/encoding.h", -+ "include/smhasher/MurmurHash3.h", -+ "include/vocab/byte.h", -+ "include/vocab/byte_view.h", -+ "include/vocab/endian.h", -+ "include/vocab/expected.h", -+ "include/vocab/flat_mapset.h", -+ "include/vocab/html_escape.h", -+ "include/vocab/i128.h", -+ "include/vocab/raw_ptr.h", -+ "include/vocab/slash_delimited.h", -+ "include/vocab/span.h", -+ "include/vocab/stringify.h", -+ "src/ipfs_client/bases/b16_upper.h", -+ "src/ipfs_client/bases/b32.h", -+ "src/ipfs_client/block_requestor.cc", -+ "src/ipfs_client/car.cc", -+ "src/ipfs_client/car.h", -+ "src/ipfs_client/cid.cc", -+ "src/ipfs_client/context_api.cc", -+ "src/ipfs_client/crypto/openssl_sha2_256.cc", -+ "src/ipfs_client/crypto/openssl_sha2_256.h", -+ "src/ipfs_client/dag_cbor_value.cc", -+ "src/ipfs_client/dag_json_value.cc", -+ "src/ipfs_client/gateways.cc", -+ "src/ipfs_client/generated_directory_listing.cc", -+ "src/ipfs_client/generated_directory_listing.h", -+ "src/ipfs_client/gw/block_request_splitter.cc", -+ "src/ipfs_client/gw/default_requestor.cc", -+ "src/ipfs_client/gw/dnslink_requestor.cc", -+ "src/ipfs_client/gw/gateway_http_requestor.cc", -+ "src/ipfs_client/gw/gateway_http_requestor.h", -+ "src/ipfs_client/gw/gateway_request.cc", -+ "src/ipfs_client/gw/inline_request_handler.cc", -+ "src/ipfs_client/gw/requestor.cc", -+ "src/ipfs_client/gw/requestor_pool.cc", -+ "src/ipfs_client/gw/requestor_pool.h", -+ "src/ipfs_client/gw/terminating_requestor.cc", -+ "src/ipfs_client/http_request_description.cc", -+ "src/ipfs_client/identity_cid.cc", -+ "src/ipfs_client/ipfs_request.cc", -+ "src/ipfs_client/ipld/chunk.cc", -+ "src/ipfs_client/ipld/chunk.h", -+ "src/ipfs_client/ipld/dag_cbor_node.cc", -+ "src/ipfs_client/ipld/dag_cbor_node.h", -+ "src/ipfs_client/ipld/dag_json_node.cc", -+ "src/ipfs_client/ipld/dag_json_node.h", -+ "src/ipfs_client/ipld/dag_node.cc", -+ "src/ipfs_client/ipld/directory_shard.cc", -+ "src/ipfs_client/ipld/directory_shard.h", -+ "src/ipfs_client/ipld/ipns_name.cc", -+ "src/ipfs_client/ipld/ipns_name.h", -+ "src/ipfs_client/ipld/link.cc", -+ "src/ipfs_client/ipld/resolution_state.cc", -+ "src/ipfs_client/ipld/root.cc", -+ "src/ipfs_client/ipld/root.h", -+ "src/ipfs_client/ipld/small_directory.cc", -+ "src/ipfs_client/ipld/small_directory.h", -+ "src/ipfs_client/ipld/symlink.cc", -+ "src/ipfs_client/ipld/symlink.h", -+ "src/ipfs_client/ipld/unixfs_file.cc", -+ "src/ipfs_client/ipld/unixfs_file.h", -+ "src/ipfs_client/ipns_names.cc", -+ "src/ipfs_client/ipns_record.cc", -+ "src/ipfs_client/logger.cc", -+ "src/ipfs_client/multi_base.cc", -+ "src/ipfs_client/multi_hash.cc", -+ "src/ipfs_client/multicodec.cc", -+ "src/ipfs_client/orchestrator.cc", -+ "src/ipfs_client/path2url.cc", -+ "src/ipfs_client/path2url.h", -+ "src/ipfs_client/pb_dag.cc", -+ "src/ipfs_client/redirects.cc", -+ "src/ipfs_client/redirects.h", -+ "src/ipfs_client/response.cc", -+ "src/ipfs_client/signing_key_type.cc", -+ "src/libp2p/crypto/protobuf_key.hpp", -+ "src/libp2p/multi/multibase_codec/codecs/base16.cc", -+ "src/libp2p/multi/multibase_codec/codecs/base32.cc", -+ "src/libp2p/multi/multibase_codec/codecs/base36.cc", -+ "src/libp2p/multi/uvarint.cc", -+ "src/log_macros.h", -+ "src/smhasher/MurmurHash3.cc", -+ "src/vocab/byte_view.cc", -+ "src/vocab/slash_delimited.cc", -+ ] -+ static_library("ipfs_client") { -+ if (is_nacl) { -+ sources = cxx_sources - [ -+ "src/ipfs_client/dag_block.cc", -+ "src/ipfs_client/gw/gateway_request.cc", -+ "src/ipfs_client/gw/gateway_http_requestor.cc", -+ "src/ipfs_client/gw/requestor.cc", -+ "src/ipfs_client/ipld/dag_node.cc", -+ "src/ipfs_client/ipns_names.cc", -+ "src/ipfs_client/ipns_record.cc", -+ "src/ipfs_client/logger.cc", -+ "src/ipfs_client/signing_key_type.cc", -+ ] -+ } else { -+ sources = cxx_sources -+ } -+ include_dirs = [ -+ "include", -+ "src", -+ "..", -+ "../boringssl/src/include" -+ ] -+ public_configs = [ -+ ":external_config" -+ ] -+ public_deps = [ -+ "//third_party/abseil-cpp:absl", -+ "//base", -+ ] -+ deps = [ -+ "//third_party/abseil-cpp:absl", -+ "//base", -+ ] -+ if (!is_nacl) { -+ public_deps += [ -+ ":protos", -+ "//third_party/protobuf:protobuf_lite", -+ ] -+ } -+ } -+} -+ -+import("//third_party/protobuf/proto_library.gni") -+ -+proto_library("protos") { -+ sources = [ -+ "ipns_record.proto", -+ "keys.proto", -+ "pb_dag.proto", -+ "unix_fs.proto", -+ ] -+} -diff --git a/third_party/ipfs_client/README.chromium b/third_party/ipfs_client/README.chromium -new file mode 100644 -index 0000000000000..e69de29bb2d1d -diff --git a/third_party/ipfs_client/README.md b/third_party/ipfs_client/README.md -new file mode 100644 -index 0000000000000..0e6ffadd2ebbc ---- /dev/null -+++ b/third_party/ipfs_client/README.md -@@ -0,0 +1,6 @@ -+# ipfs-client -+ -+## TODO -+ -+Need to fill out this README to explain how to use ipfs-client in other contexts. -+ -diff --git a/third_party/ipfs_client/args.gni b/third_party/ipfs_client/args.gni -new file mode 100644 -index 0000000000000..bb13519b23e89 ---- /dev/null -+++ b/third_party/ipfs_client/args.gni -@@ -0,0 +1,3 @@ -+declare_args() { -+ enable_ipfs = false -+} -diff --git a/third_party/ipfs_client/conanfile.py b/third_party/ipfs_client/conanfile.py -new file mode 100644 -index 0000000000000..289e3b48f8ad1 ---- /dev/null -+++ b/third_party/ipfs_client/conanfile.py -@@ -0,0 +1,79 @@ -+from conan import ConanFile -+from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps, cmake_layout -+from shutil import copyfile, which -+import sys -+from os.path import dirname, isfile, join, realpath -+ -+here = realpath(dirname(__file__)) -+sys.path.append(realpath(join(here, '..', 'cmake'))) -+sys.path.append(here) -+ -+try: -+ import version -+ VERSION = version.deduce() -+except ImportError: -+ VERSION = open(join(here,'version.txt'), 'r').read().strip() -+ -+ -+class IpfsChromium(ConanFile): -+ name = "ipfs_client" -+ version = VERSION -+ settings = "os", "compiler", "build_type", "arch" -+ # generators = "CMakeDeps", 'CMakeToolchain' -+ _PB = 'protobuf/3.20.0' -+ require_transitively = [ -+ 'abseil/20230125.3', -+ 'boost/1.81.0', -+ 'bzip2/1.0.8', -+ 'c-ares/1.22.1', -+ 'nlohmann_json/3.11.2', -+ 'openssl/1.1.1t', -+ _PB, -+ ] -+ # default_options = {"boost/*:header_only": True} -+ default_options = { -+ "boost/*:bzip2": True, -+ "boost/*:with_stacktrace_backtrace": True -+ } -+ tool_requires = [ -+ 'cmake/3.22.6', -+ 'ninja/1.11.1', -+ _PB, -+ ] -+ extensions = ['h', 'cc', 'hpp', 'proto'] -+ exports_sources = [ '*.txt' ] + [f'**/*.{e}' for e in extensions] -+ exports = 'version.txt' -+ package_type = 'static-library' -+ -+ -+ def generate(self): -+ tc = CMakeToolchain(self, 'Ninja') -+ tc.generate() -+ d = CMakeDeps(self) -+ d.generate() -+ -+ def build(self): -+ cmake = CMake(self) -+ cmake.configure(variables={ -+ "CXX_VERSION": 20, -+ "INSIDE_CONAN": True -+ }) -+ cmake.build(build_tool_args=['--verbose']) -+ -+ def package(self): -+ cmake = CMake(self) -+ cmake.install() -+ print(self.cpp_info.objects) -+ -+ def package_info(self): -+ self.cpp_info.libs = ["ipfs_client"] -+ -+ def build_requirements(self): -+ if not which("doxygen"): -+ self.tool_requires("doxygen/1.9.4") -+ def layout(self): -+ cmake_layout(self) -+ -+ def requirements(self): -+ for l in self.require_transitively: -+ self.requires(l, transitive_headers=True) -diff --git a/third_party/ipfs_client/include/ipfs_client/block_requestor.h b/third_party/ipfs_client/include/ipfs_client/block_requestor.h -new file mode 100644 -index 0000000000000..42ae26e519760 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/block_requestor.h -@@ -0,0 +1,48 @@ -+#ifndef BLOCK_REQUESTOR_H_ -+#define BLOCK_REQUESTOR_H_ -+ -+#include -+ -+#include -+#include -+#include -+ -+namespace ipfs { -+ -+/*! -+ * \brief The urgency of a gateway request -+ * \details Determines how many gateways should be involved, and how burdened a -+ * gateway should be before not also taking this one on concurrently. Zero is -+ * a special value that indicates the block isn't actually required now, but -+ * rather might be required soonish (prefetch). There are some cases of -+ * special handling for that. -+ */ -+using Priority = std::uint_least16_t; -+ -+class DagListener; -+ -+/*! -+ * \brief Interface for classes that can asynchronously fetch a block for a CID -+ * \details This is one of the interfaces using code is meant to implement. -+ * Common usages: -+ * * A class that requests blocks from gateways -+ * * A cache that must act asynchronously (perhaps on-disk) -+ * * ChainedRequestors : a chain-of-responsibility combining multiple -+ */ -+class BlockRequestor { -+ public: -+ /** -+ * \brief Request a single block from gateway(s). -+ * \param cid - MB-MH string representation of the Content IDentifier -+ * \param dl - Someone who may be interested -+ * \param priority - Urgency of the request -+ * \note The DagListener is mostly about lifetime extension, since it's -+ * waiting on something which is waiting on this -+ */ -+ virtual void RequestByCid(std::string cid, -+ std::shared_ptr dl, -+ Priority priority) = 0; -+}; -+} // namespace ipfs -+ -+#endif // BLOCK_REQUESTOR_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/block_storage.h b/third_party/ipfs_client/include/ipfs_client/block_storage.h -new file mode 100644 -index 0000000000000..525bae463f50d ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/block_storage.h -@@ -0,0 +1,144 @@ -+#ifndef IPFS_BLOCKS_H_ -+#define IPFS_BLOCKS_H_ -+ -+#include "pb_dag.h" -+#include "vocab/flat_mapset.h" -+ -+#include -+#include -+#include -+ -+namespace libp2p::multi { -+struct ContentIdentifier; -+} -+ -+namespace ipfs { -+class DagListener; -+class ContextApi; -+ -+class UnixFsPathResolver; -+ -+/*! -+ * \brief Immediate access to recently-accessed blocks -+ * \details Blocks are held in-memory, using pretty standard containers, as -+ * already-parsed ipfs::Block objects. -+ */ -+class BlockStorage { -+ public: -+ BlockStorage(); -+ -+ BlockStorage(BlockStorage const&) = delete; -+ -+ ~BlockStorage() noexcept; -+ -+ /*! -+ * \brief Store a Block for later access. -+ * \param cid_str - The string representation of cid -+ * \param cid - The Content IDentifier -+ * \param headers - Associated HTTP headers -+ * \param body - The raw bytes of the block -+ * \param block - The block being stored -+ * \return Whether this block is now stored in *this -+ */ -+ bool Store(std::string cid_str, -+ Cid const& cid, -+ std::string headers, -+ std::string const& body, -+ PbDag&& block); -+ -+ /*! -+ * \name Store (Convenience) -+ * Convenience functions for -+ * ipfs::BlockStorage::Store(std::string,Cid const&,std::string,std::string -+ * const&,Block&&) -+ */ -+ ///@{ -+ bool Store(std::string headers, std::string const& body, PbDag&& block); -+ bool Store(std::string const& cid, std::string headers, std::string body); -+ bool Store(std::string cid_str, -+ Cid const& cid, -+ std::string headers, -+ std::string body); -+ bool Store(Cid const& cid, -+ std::string headers, -+ std::string const& body, -+ PbDag&&); -+ ///@} -+ -+ /*! -+ * \brief Get a block! -+ * \details cid must match string-wise exactly: same multibase & all. -+ * For identity codecs, returns the data even if not stored. -+ * \param cid - String representation of the CID for the block. -+ * \return Non-owning pointer if found, nullptr -+ * otherwise -+ */ -+ PbDag const* Get(std::string const& cid); -+ -+ /*! -+ * \brief Get HTTP headers associated with the block -+ * \param cid - String representation of the CID for the block. -+ * \return nullptr iff ! Get(cid) ; -+ * Empty string if the headers have never been set ; -+ * Otherwise, application-specific std::string (as-stored) -+ */ -+ std::string const* GetHeaders(std::string const& cid); -+ -+ /*! -+ * \brief Indicate that a particular path resolver is waiting on a CID to -+ * become available -+ */ -+ void AddListening(UnixFsPathResolver*); -+ -+ /*! -+ * \brief Indicate that a particular path resolver is no longer waiting -+ */ -+ void StopListening(UnixFsPathResolver*); -+ -+ /*! -+ * \brief Normally called internally -+ * \details Checks to see if any listening path resolver appears to be waiting -+ * on a CID which is now available. -+ */ -+ void CheckListening(); -+ -+ /*! -+ * \brief Type for callbacks about new blocks -+ * \details The parameters to the hook are -+ * * CID string -+ * * HTTP headers -+ * * raw bytes of the block -+ */ -+ using SerializedStorageHook = -+ std::function; -+ -+ /*! -+ * \brief Register a callback that will be called when any new block goes into -+ * storage -+ */ -+ void AddStorageHook(SerializedStorageHook); -+ -+ private: -+ struct Record { -+ Record(); -+ ~Record() noexcept; -+ std::time_t last_access = 0L; -+ std::string cid_str = {}; -+ PbDag block = {}; -+ std::string headers = {}; -+ }; -+ std::list records_ = std::list(0xFFUL); -+ using Iter = decltype(records_)::iterator; -+ flat_map cid2record_; -+ flat_set listening_; -+ bool checking_ = false; -+ std::vector hooks_; -+ -+ Record const* GetInternal(std::string const&); -+ Record* FindFree(std::time_t); -+ Record* Allocate(); -+ Record* StoreIdentity(std::string const&, Cid const&); -+}; -+} // namespace ipfs -+ -+#endif // IPFS_BLOCKS_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/cid.h b/third_party/ipfs_client/include/ipfs_client/cid.h -new file mode 100644 -index 0000000000000..d957d23e5e7e4 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/cid.h -@@ -0,0 +1,38 @@ -+#ifndef IPFS_CID_H_ -+#define IPFS_CID_H_ -+ -+#include "multi_hash.h" -+#include "multicodec.h" -+ -+#include -+ -+#include -+#include -+ -+namespace ipfs { -+class Cid { -+ MultiCodec codec_ = MultiCodec::INVALID; -+ MultiHash hash_; -+ -+ public: -+ Cid() = default; -+ Cid(MultiCodec, MultiHash); -+ explicit Cid(std::string_view); -+ explicit Cid(ByteView); -+ bool ReadStart(ByteView&); -+ -+ bool valid() const; -+ MultiCodec codec() const { return codec_; } -+ MultiHash const& multi_hash() const { return hash_; } -+ ByteView hash() const; -+ HashType hash_type() const; -+ -+ std::string to_string() const; -+ -+ constexpr static std::size_t MinSerializedLength = -+ 1 /*cid version*/ + 1 /*codec*/ + 1 /*hash type*/ + -+ 1 /*hash len, could be zero*/; -+}; -+} // namespace ipfs -+ -+#endif // IPFS_CID_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/context_api.h b/third_party/ipfs_client/include/ipfs_client/context_api.h -new file mode 100644 -index 0000000000000..da524bb9a86a5 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/context_api.h -@@ -0,0 +1,86 @@ -+#ifndef IPFS_CONTEXT_API_H_ -+#define IPFS_CONTEXT_API_H_ -+ -+#include "crypto/hasher.h" -+#include "dag_cbor_value.h" -+#include "http_request_description.h" -+#include "ipns_cbor_entry.h" -+#include "multi_hash.h" -+#include "signing_key_type.h" -+ -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+namespace ipfs { -+class IpfsRequest; -+class DagJsonValue; -+ -+/** -+ * \brief Interface that provides functionality from whatever -+ * environment you're using this library in. -+ * \note A user of this library must implement this, but will probably do so -+ * only once. -+ */ -+class ContextApi : public std::enable_shared_from_this { -+ public: -+ ContextApi(); -+ virtual ~ContextApi() noexcept {} -+ -+ using HttpRequestDescription = ::ipfs::HttpRequestDescription; -+ using HeaderAccess = std::function; -+ using HttpCompleteCallback = -+ std::function; -+ virtual void SendHttpRequest(HttpRequestDescription, -+ HttpCompleteCallback cb) const = 0; -+ -+ using DnsTextResultsCallback = -+ std::function const&)>; -+ using DnsTextCompleteCallback = std::function; -+ virtual void SendDnsTextRequest(std::string hostname, -+ DnsTextResultsCallback, -+ DnsTextCompleteCallback) = 0; -+ -+ /*! -+ * \brief Determine a mime type for a given file. -+ * \param extension - "File extension" not including ., e.g. "html" -+ * \param content - The content of the resource or a large prefix thereof -+ * \param url - A URL it was fetched from (of any sort, ipfs:// is fine) -+ */ -+ virtual std::string MimeType(std::string extension, -+ std::string_view content, -+ std::string const& url) const = 0; -+ -+ /*! -+ * \brief Remove URL escaping, e.g. %20 -+ * \param url_comp - a single component of the URL, e.g. a element of the path -+ * not including / -+ * \return The unescaped string -+ */ -+ virtual std::string UnescapeUrlComponent(std::string_view url_comp) const = 0; -+ -+ virtual std::unique_ptr ParseCbor(ByteView) const = 0; -+ virtual std::unique_ptr ParseJson(std::string_view) const = 0; -+ -+ using IpnsCborEntry = ::ipfs::IpnsCborEntry; -+ -+ using SigningKeyType = ::ipfs::SigningKeyType; -+ using ByteView = ::ipfs::ByteView; -+ virtual bool VerifyKeySignature(SigningKeyType, -+ ByteView signature, -+ ByteView data, -+ ByteView key_bytes) const = 0; -+ -+ std::optional> Hash(HashType, ByteView data); -+ -+ protected: -+ std::unordered_map> hashers_; -+}; -+ -+} // namespace ipfs -+ -+#endif -diff --git a/third_party/ipfs_client/include/ipfs_client/crypto/hasher.h b/third_party/ipfs_client/include/ipfs_client/crypto/hasher.h -new file mode 100644 -index 0000000000000..5222d622ce998 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/crypto/hasher.h -@@ -0,0 +1,18 @@ -+#ifndef IPFS_HASHER_H_ -+#define IPFS_HASHER_H_ -+ -+#include -+ -+#include -+#include -+ -+namespace ipfs::crypto { -+class Hasher { -+ public: -+ virtual ~Hasher() noexcept {} -+ -+ virtual std::optional> hash(ByteView) = 0; -+}; -+} // namespace ipfs::crypto -+ -+#endif // IPFS_HASHER_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/dag_cbor_value.h b/third_party/ipfs_client/include/ipfs_client/dag_cbor_value.h -new file mode 100644 -index 0000000000000..71cb538776361 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/dag_cbor_value.h -@@ -0,0 +1,35 @@ -+#ifndef IPFS_DAG_CBOR_VALUE_H_ -+#define IPFS_DAG_CBOR_VALUE_H_ -+ -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+namespace ipfs { -+class DagCborValue { -+ public: -+ virtual std::unique_ptr at(std::string_view) const = 0; -+ virtual std::optional as_unsigned() const = 0; -+ virtual std::optional as_signed() const = 0; -+ virtual std::optional as_float() const = 0; -+ virtual std::optional as_string() const = 0; -+ virtual std::optional> as_bytes() const = 0; -+ virtual std::optional as_bool() const = 0; -+ virtual std::optional as_link() const = 0; -+ virtual bool is_map() const = 0; -+ virtual bool is_array() const = 0; -+ using MapElementCallback = std::function; -+ using ArrayElementCallback = std::function; -+ virtual void iterate_map(MapElementCallback) const = 0; -+ virtual void iterate_array(ArrayElementCallback) const = 0; -+ std::string html() const; -+ void html(std::ostream&) const; -+ virtual ~DagCborValue() noexcept {} -+}; -+} -+ -+#endif // IPFS_DAG_CBOR_VALUE_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/dag_json_value.h b/third_party/ipfs_client/include/ipfs_client/dag_json_value.h -new file mode 100644 -index 0000000000000..32e170c439438 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/dag_json_value.h -@@ -0,0 +1,26 @@ -+#ifndef IPFS_DAG_JSON_VALUE_H_ -+#define IPFS_DAG_JSON_VALUE_H_ -+ -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+namespace ipfs { -+class DagJsonValue { -+ public: -+ virtual std::string pretty_print() const = 0; -+ virtual std::unique_ptr operator[](std::string_view) const = 0; -+ virtual std::optional get_if_string() const = 0; -+ virtual std::optional> object_keys() const = 0; -+ virtual bool iterate_list(std::function) const = 0; -+ virtual ~DagJsonValue() noexcept; -+ -+ std::optional get_if_link() const; -+}; -+} // namespace ipfs -+ -+#endif // IPFS_DAG_JSON_VALUE_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/gateways.h b/third_party/ipfs_client/include/ipfs_client/gateways.h -new file mode 100644 -index 0000000000000..0063b52525df6 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/gateways.h -@@ -0,0 +1,66 @@ -+#ifndef CHROMIUM_IPFS_GATEWAYS_H_ -+#define CHROMIUM_IPFS_GATEWAYS_H_ -+ -+#include "vocab/flat_mapset.h" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+namespace ipfs { -+struct GatewaySpec { -+ std::string prefix; -+ unsigned strength; -+ bool operator<(GatewaySpec const& r) const { -+ if (strength == r.strength) { -+ return prefix < r.prefix; -+ } -+ return strength > r.strength; -+ } -+}; -+using GatewayList = std::vector; -+class ContextApi; -+ -+/*! -+ * \brief All known IPFS gateways -+ */ -+class Gateways { -+ flat_map known_gateways_; -+ std::default_random_engine random_engine_; -+ std::geometric_distribution dist_; -+ int up_log_ = 1; -+ -+ public: -+ /*! -+ * \brief The hard-coded list of gateways at startup -+ */ -+ static GatewayList DefaultGateways(); -+ -+ Gateways(); -+ ~Gateways(); -+ GatewayList GenerateList(); ///< Get a sorted list of gateways for requesting -+ -+ /*! -+ * \brief Good gateway, handle more! -+ * \param prefix - identify the gateway by its URL prefix -+ */ -+ void promote(std::string const& prefix); -+ -+ /*! -+ * \brief Bad gateway, move toward the back of the line. -+ * \param prefix - identify the gateway by its URL prefix -+ */ -+ void demote(std::string const& prefix); -+ -+ /*! -+ * \brief Bulk load a bunch of new gateways -+ * \param prefices - list of URL gateways by prefix -+ */ -+ void AddGateways(std::vector prefices); -+}; -+} // namespace ipfs -+ -+#endif // CHROMIUM_IPFS_GATEWAYS_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/gw/block_request_splitter.h b/third_party/ipfs_client/include/ipfs_client/gw/block_request_splitter.h -new file mode 100644 -index 0000000000000..0f308a996d360 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/gw/block_request_splitter.h -@@ -0,0 +1,17 @@ -+#ifndef IPFS_BLOCK_REQUEST_SPLITTER_H_ -+#define IPFS_BLOCK_REQUEST_SPLITTER_H_ -+ -+#include "requestor.h" -+ -+namespace ipfs { -+class ContextApi; -+} -+ -+namespace ipfs::gw { -+class BlockRequestSplitter final : public Requestor { -+ HandleOutcome handle(RequestPtr) override; -+ std::string_view name() const override; -+}; -+} // namespace ipfs::gw -+ -+#endif // IPFS_BLOCK_REQUEST_SPLITTER_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/gw/default_requestor.h b/third_party/ipfs_client/include/ipfs_client/gw/default_requestor.h -new file mode 100644 -index 0000000000000..06b5970e1d103 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/gw/default_requestor.h -@@ -0,0 +1,14 @@ -+#ifndef IPFS_DEFAULT_REQUESTOR_LIST_H_ -+#define IPFS_DEFAULT_REQUESTOR_LIST_H_ -+ -+#include "requestor.h" -+ -+#include -+ -+namespace ipfs::gw { -+std::shared_ptr default_requestor(GatewayList, -+ std::shared_ptr early, -+ std::shared_ptr); -+} -+ -+#endif // IPFS_DEFAULT_REQUESTOR_LIST_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/gw/dnslink_requestor.h b/third_party/ipfs_client/include/ipfs_client/gw/dnslink_requestor.h -new file mode 100644 -index 0000000000000..4910fe61976c8 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/gw/dnslink_requestor.h -@@ -0,0 +1,20 @@ -+#ifndef IPFS_DNSLINK_REQUESTOR_H_ -+#define IPFS_DNSLINK_REQUESTOR_H_ -+ -+#include "requestor.h" -+ -+namespace ipfs { -+class ContextApi; -+} -+ -+namespace ipfs::gw { -+class DnsLinkRequestor final : public Requestor { -+ public: -+ explicit DnsLinkRequestor(std::shared_ptr); -+ -+ HandleOutcome handle(RequestPtr) override; -+ std::string_view name() const override; -+}; -+} // namespace ipfs::gw -+ -+#endif // IPFS_DNSLINK_REQUESTOR_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/gw/gateway_request.h b/third_party/ipfs_client/include/ipfs_client/gw/gateway_request.h -new file mode 100644 -index 0000000000000..efded265680b8 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/gw/gateway_request.h -@@ -0,0 +1,77 @@ -+#ifndef IPFS_TRUSTLESS_REQUEST_H_ -+#define IPFS_TRUSTLESS_REQUEST_H_ -+ -+#include -+#include -+ -+#include -+ -+#include -+#include -+#include -+#include -+ -+namespace ipfs { -+class IpfsRequest; -+class Orchestrator; -+namespace ipld { -+class DagNode; -+} -+} // namespace ipfs -+ -+namespace ipfs::gw { -+class Requestor; -+ -+enum class Type : char { -+ Block, -+ Car, -+ Ipns, -+ DnsLink, -+ Providers, -+ Identity, -+ Zombie -+}; -+std::string_view name(Type); -+ -+constexpr std::size_t BLOCK_RESPONSE_BUFFER_SIZE = 2 * 1024 * 1024; -+ -+class GatewayRequest { -+ std::shared_ptr orchestrator_; -+ std::vector> bytes_received_hooks; -+ -+ void ParseNodes(std::string_view, ContextApi* api); -+ -+ public: -+ Type type; -+ std::string main_param; ///< CID, IPNS name, hostname -+ std::string path; ///< For CAR requests -+ std::shared_ptr dependent; -+ std::optional cid; -+ short parallel = 0; -+ std::string affinity; -+ -+ std::string url_suffix() const; -+ std::string_view accept() const; -+ std::string_view identity_data() const; -+ short timeout_seconds() const; -+ bool is_http() const; -+ std::optional max_response_size() const; -+ std::optional describe_http() const; -+ std::string debug_string() const; -+ void orchestrator(std::shared_ptr const&); -+ -+ bool RespondSuccessfully(std::string_view, -+ std::shared_ptr const& api); -+ void Hook(std::function); -+ bool PartiallyRedundant() const; -+ -+ static std::shared_ptr fromIpfsPath(SlashDelimited); -+}; -+ -+} // namespace ipfs::gw -+ -+inline std::ostream& operator<<(std::ostream& s, ipfs::gw::Type t) { -+ return s << name(t); -+} -+ -+#endif // IPFS_TRUSTLESS_REQUEST_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/gw/inline_request_handler.h b/third_party/ipfs_client/include/ipfs_client/gw/inline_request_handler.h -new file mode 100644 -index 0000000000000..0301c561c5735 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/gw/inline_request_handler.h -@@ -0,0 +1,14 @@ -+#ifndef IPFS_INLINE_REQUEST_HANDLER_H_ -+#define IPFS_INLINE_REQUEST_HANDLER_H_ -+ -+#include "requestor.h" -+ -+namespace ipfs::gw { -+class InlineRequestHandler final : public Requestor { -+ public: -+ HandleOutcome handle(RequestPtr) override; -+ std::string_view name() const override; -+}; -+} // namespace ipfs::gw -+ -+#endif // IPFS_INLINE_REQUEST_HANDLER_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/gw/requestor.h b/third_party/ipfs_client/include/ipfs_client/gw/requestor.h -new file mode 100644 -index 0000000000000..634c36730b1ea ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/gw/requestor.h -@@ -0,0 +1,55 @@ -+#ifndef IPFS_REQUESTOR_H_ -+#define IPFS_REQUESTOR_H_ -+ -+#include -+#include -+#include -+ -+namespace ipfs::ipld { -+class DagNode; -+} -+namespace ipfs { -+class ContextApi; -+struct Response; -+} // namespace ipfs -+ -+namespace ipfs::gw { -+class GatewayRequest; -+using RequestPtr = std::shared_ptr; -+ -+class Requestor : public std::enable_shared_from_this { -+ protected: -+ Requestor() {} -+ -+ friend class RequestorPool; -+ enum class HandleOutcome : char { -+ NOT_HANDLED = 'N', -+ PENDING = 'P', -+ DONE = 'D', -+ PARALLEL = 'L', -+ MAYBE_LATER = 'M' -+ }; -+ virtual HandleOutcome handle(RequestPtr) = 0; -+ -+ void definitive_failure(RequestPtr) const; -+ void forward(RequestPtr) const; -+ -+ std::shared_ptr api_; -+ -+ public: -+ using RequestPtr = ::ipfs::gw::RequestPtr; -+ virtual std::string_view name() const = 0; -+ -+ virtual ~Requestor() noexcept {} -+ void request(std::shared_ptr); -+ Requestor& or_else(std::shared_ptr p); -+ void api(std::shared_ptr); -+ -+ void TestAccess(void*); -+ -+ private: -+ std::shared_ptr next_; -+}; -+} // namespace ipfs::gw -+ -+#endif // IPFS_REQUESTOR_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/gw/terminating_requestor.h b/third_party/ipfs_client/include/ipfs_client/gw/terminating_requestor.h -new file mode 100644 -index 0000000000000..3fe7a01e752f5 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/gw/terminating_requestor.h -@@ -0,0 +1,15 @@ -+#ifndef IPFS_TERMINATING_REQUESTOR_H_ -+#define IPFS_TERMINATING_REQUESTOR_H_ -+ -+#include "requestor.h" -+ -+namespace ipfs::gw { -+class TerminatingRequestor : public Requestor { -+ public: -+ using HandleOutcome = Requestor::HandleOutcome; -+ std::string_view name() const override; -+ HandleOutcome handle(RequestPtr) override; -+}; -+} // namespace ipfs::gw -+ -+#endif // IPFS_TERMINATING_REQUESTOR_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/http_request_description.h b/third_party/ipfs_client/include/ipfs_client/http_request_description.h -new file mode 100644 -index 0000000000000..f3f07d58ea199 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/http_request_description.h -@@ -0,0 +1,20 @@ -+#ifndef IPFS_HTTP_REQUEST_DESCRIPTION_H_ -+#define IPFS_HTTP_REQUEST_DESCRIPTION_H_ -+ -+#include -+#include -+ -+#include -+ -+namespace ipfs { -+struct HttpRequestDescription { -+ std::string url; -+ int timeout_seconds; -+ std::string accept; -+ std::optional max_response_size; -+ bool operator==(HttpRequestDescription const&) const; -+ bool operator<(HttpRequestDescription const&) const; -+}; -+} // namespace ipfs -+ -+#endif // IPFS_HTTP_REQUEST_DESCRIPTION_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/identity_cid.h b/third_party/ipfs_client/include/ipfs_client/identity_cid.h -new file mode 100644 -index 0000000000000..29efd30d1c6b2 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/identity_cid.h -@@ -0,0 +1,14 @@ -+#ifndef IPFS_IDENTITY_CID_H_ -+#define IPFS_IDENTITY_CID_H_ 1 -+ -+#include -+ -+#include -+ -+namespace ipfs { -+namespace id_cid { -+ipfs::Cid forText(std::string_view); -+} // namespace id_cid -+} // namespace ipfs -+ -+#endif -diff --git a/third_party/ipfs_client/include/ipfs_client/ipfs_request.h b/third_party/ipfs_client/include/ipfs_client/ipfs_request.h -new file mode 100644 -index 0000000000000..eda8bdfa7010b ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/ipfs_request.h -@@ -0,0 +1,33 @@ -+#ifndef IPFS_IPFS_REQUEST_H_ -+#define IPFS_IPFS_REQUEST_H_ -+ -+#include -+ -+#include -+#include -+#include -+ -+namespace ipfs { -+struct Response; -+class IpfsRequest { -+ public: -+ using Finisher = std::function; -+ -+ private: -+ std::string path_; -+ Finisher callback_; -+ std::size_t waiting_ = 0UL; -+ -+ public: -+ IpfsRequest(std::string path, Finisher); -+ SlashDelimited path() const { return SlashDelimited{path_}; } -+ void finish(Response& r); -+ void till_next(std::size_t); -+ bool ready_after(); -+ void new_path(std::string_view); -+ -+ static std::shared_ptr fromUrl(std::string url, Finisher); -+}; -+} // namespace ipfs -+ -+#endif // IPFS_IPFS_REQUEST_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/ipld/dag_node.h b/third_party/ipfs_client/include/ipfs_client/ipld/dag_node.h -new file mode 100644 -index 0000000000000..1c66f4fd1c755 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/ipld/dag_node.h -@@ -0,0 +1,102 @@ -+#ifndef IPFS_DAG_NODE_H_ -+#define IPFS_DAG_NODE_H_ -+ -+#include "link.h" -+#include "resolution_state.h" -+ -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+namespace ipfs { -+class PbDag; -+class ContextApi; -+struct ValidatedIpns; -+} // namespace ipfs -+namespace libp2p::multi { -+struct ContentIdentifier; -+} -+namespace ipfs::ipld { -+ -+using NodePtr = std::shared_ptr; -+class DirShard; -+ -+struct MoreDataNeeded { -+ MoreDataNeeded(std::string one) : ipfs_abs_paths_{{one}} {} -+ template -+ MoreDataNeeded(Range const& many) -+ : ipfs_abs_paths_(many.begin(), many.end()) {} -+ std::vector ipfs_abs_paths_; -+ bool insist_on_car = false; -+}; -+enum class ProvenAbsent {}; -+struct PathChange { -+ std::string new_path; -+}; -+ -+using ResolveResult = -+ std::variant; -+/** -+ * @brief A block, an IPNS record, etc. -+ */ -+class DagNode : public std::enable_shared_from_this { -+ Link* FindChild(std::string_view); -+ static void Descend(ResolutionState&); -+ -+ protected: -+ std::vector> links_; -+ std::shared_ptr api_; -+ -+ ///< When the next path element is what's needed, and it should already be a -+ ///< link known about... -+ ResolveResult CallChild(ResolutionState&); -+ -+ ///< As before, but it might be possible to create on the fly if not known -+ ResolveResult CallChild(ResolutionState&, -+ std::function gen_child); -+ -+ ///< When the child's name is not the next element in the path, but it must be -+ ///< known about. e.g. index.html for a path ending in a directory -+ ResolveResult CallChild(ResolutionState&, std::string_view link_key); -+ -+ ///< Add the link if not present, then CallChild(ResolutionState) -+ ResolveResult CallChild(ResolutionState&, -+ std::string_view link_key, -+ std::string_view block_key); -+ -+ public: -+ virtual ResolveResult resolve(ResolutionState& params) = 0; -+ ResolveResult resolve(SlashDelimited initial_path, BlockLookup); -+ -+ static NodePtr fromBytes(std::shared_ptr const& api, -+ Cid const&, -+ ByteView bytes); -+ static NodePtr fromBytes(std::shared_ptr const& api, -+ Cid const&, -+ std::string_view bytes); -+ static NodePtr fromBlock(PbDag const&); -+ static NodePtr fromIpnsRecord(ValidatedIpns const&); -+ -+ virtual ~DagNode() noexcept {} -+ -+ virtual NodePtr rooted(); -+ virtual NodePtr deroot(); -+ virtual DirShard* as_hamt(); // Wish I had access to dynamic_cast -+ -+ void set_api(std::shared_ptr); -+}; -+} // namespace ipfs::ipld -+ -+std::ostream& operator<<(std::ostream&, ipfs::ipld::PathChange const&); -+ -+#endif // IPFS_DAG_NODE_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/ipld/link.h b/third_party/ipfs_client/include/ipfs_client/ipld/link.h -new file mode 100644 -index 0000000000000..a0d290b25dd3d ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/ipld/link.h -@@ -0,0 +1,22 @@ -+#ifndef IPFS_LINK_H_ -+#define IPFS_LINK_H_ -+ -+#include -+#include -+ -+namespace ipfs::ipld { -+ -+class DagNode; -+using Ptr = std::shared_ptr; -+ -+class Link { -+ public: -+ std::string cid; -+ Ptr node; -+ -+ Link(std::string); -+ explicit Link(std::string, std::shared_ptr); -+}; -+} // namespace ipfs::ipld -+ -+#endif // IPFS_LINK_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/ipld/resolution_state.h b/third_party/ipfs_client/include/ipfs_client/ipld/resolution_state.h -new file mode 100644 -index 0000000000000..82e330cea4355 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/ipld/resolution_state.h -@@ -0,0 +1,36 @@ -+#ifndef IPFS_RESOLUTION_STATE_H_ -+#define IPFS_RESOLUTION_STATE_H_ -+ -+#include -+ -+#include -+#include -+ -+namespace ipfs { -+class ContextApi; -+} -+ -+namespace ipfs::ipld { -+class DagNode; -+using NodePtr = std::shared_ptr; -+using BlockLookup = std::function; -+ -+class ResolutionState { -+ friend class DagNode; -+ std::string resolved_path_components; -+ SlashDelimited unresolved_path; -+ BlockLookup get_available_block; -+ -+ public: -+ SlashDelimited MyPath() const; -+ SlashDelimited PathToResolve() const; -+ bool IsFinalComponent() const; -+ std::string NextComponent(ContextApi const*) const; -+ NodePtr GetBlock(std::string const& block_key) const; -+ -+ ResolutionState WithPath(std::string_view) const; -+ ResolutionState RestartResolvedPath() const; -+}; -+} // namespace ipfs::ipld -+ -+#endif // IPFS_RESOLUTION_STATE_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/ipns_cbor_entry.h b/third_party/ipfs_client/include/ipfs_client/ipns_cbor_entry.h -new file mode 100644 -index 0000000000000..230339793543c ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/ipns_cbor_entry.h -@@ -0,0 +1,21 @@ -+#ifndef IPFS_IPNS_CBOR_ENTRY_H_ -+#define IPFS_IPNS_CBOR_ENTRY_H_ -+ -+#include -+#include -+ -+namespace ipfs { -+/*! -+ * \brief Parsed out data contained in the CBOR data of an IPNS record. -+ */ -+struct IpnsCborEntry { -+ std::string value; ///< The "value" (target) the name points at -+ std::string validity; ///< Value to compare for validity (i.e. expiration) -+ std::uint64_t validityType; ///< Way to deterimine current validity -+ std::uint64_t sequence; ///< Distinguish other IPNS records for the same name -+ std::uint64_t ttl; ///< Recommended caching time -+}; -+ -+} // namespace ipfs -+ -+#endif // IPFS_IPNS_CBOR_ENTRY_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/ipns_names.h b/third_party/ipfs_client/include/ipfs_client/ipns_names.h -new file mode 100644 -index 0000000000000..b611365b87874 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/ipns_names.h -@@ -0,0 +1,69 @@ -+#ifndef IPNS_NAME_RESOLVER_H_ -+#define IPNS_NAME_RESOLVER_H_ -+ -+#include -+#include -+ -+#include -+ -+namespace ipfs { -+ -+/*! -+ * \brief Fast synchronous access to IPNS & DNSLink name resolution -+ */ -+class IpnsNames { -+ flat_map names_; -+ -+ public: -+ IpnsNames(); -+ ~IpnsNames(); -+ -+ /*! -+ * \brief Get the already-known "value"/target of a given name -+ * \param name - either a mb-mf IPNS (key) name, or a host with DNSLink -+ * \return -+ * * if resolution is incomplete: "" -+ * * if it is known not to resolve: kNoSuchName -+ * * otherwise an IPFS path witout leading /, e.g.: -+ * - ipfs/bafybeicfqz46dj67nkhxaylqd5sknnidsr4oaw4hhsjrgdmcwt73sow2d4/ -+ * - ipns/k51qzi5uqu5dlvj2baxnqndepeb86cbk3ng7n3i46uzyxzyqj2xjonzllnv0v8 -+ */ -+ std::string_view NameResolvedTo(std::string_view name) const; -+ -+ /*! -+ * \brief Store an IPNS record that already validated for this name -+ * \param name - The name that resolves with this -+ * \param rec - The record modulo validation bits -+ */ -+ void AssignName(std::string const& name, ValidatedIpns rec); -+ -+ /*! -+ * \brief Assign a target path to a DNSLink host -+ * \param host - The original host NOT including a "_dnslink." prefix -+ * \param target - an IPFS path witout leading / -+ */ -+ void AssignDnsLink(std::string const& host, std::string_view target); -+ -+ /*! -+ * \brief Store the definitive absence of a resolution -+ * \details This is useful because code will check resolution here before -+ * trying to resolve it fresh again, and you can stop that if you know -+ * it will never work. -+ */ -+ void NoSuchName(std::string const& name); -+ -+ /*! -+ * \brief Fetch the all the stored IPNS record data -+ * \param name - the IPNS name it was stored with -+ * \return nullptr if missing, otherwise non-owning pointer to record -+ */ -+ ValidatedIpns const* Entry(std::string const& name); -+ -+ /*! -+ * \brief A special value constant -+ */ -+ static constexpr std::string_view kNoSuchName{"NO_SUCH_NAME"}; -+}; -+} // namespace ipfs -+ -+#endif // IPNS_NAME_RESOLVER_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/ipns_record.h b/third_party/ipfs_client/include/ipfs_client/ipns_record.h -new file mode 100644 -index 0000000000000..a6bd168a4af60 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/ipns_record.h -@@ -0,0 +1,75 @@ -+#ifndef IPFS_IPNS_RECORD_H_ -+#define IPFS_IPNS_RECORD_H_ -+ -+#include -+ -+#include -+ -+#if __has_include() -+#include -+#else -+#include "ipfs_client/keys.pb.h" -+#endif -+ -+#include -+#include -+ -+namespace libp2p::peer { -+class PeerId; -+} -+namespace libp2p::multi { -+struct ContentIdentifier; -+} -+ -+namespace ipfs { -+ -+class Cid; -+class ContextApi; -+ -+constexpr static std::size_t MAX_IPNS_PB_SERIALIZED_SIZE = 10 * 1024; -+ -+std::optional ValidateIpnsRecord(ByteView top_level_bytes, -+ Cid const& name, -+ ContextApi&); -+ -+/*! -+ * \brief Data from IPNS record modulo the verification parts -+ */ -+struct ValidatedIpns { -+ std::string value; ///< The path the record claims the IPNS name points to -+ std::time_t use_until; ///< An expiration timestamp -+ std::time_t cache_until; ///< Inspired by TTL -+ -+ /*! -+ * \brief The version of the record -+ * \details Higher sequence numbers obsolete lower ones -+ */ -+ std::uint64_t sequence; -+ std::int64_t resolution_ms; ///< How long it took to fetch the record -+ -+ /*! -+ * \brief When the record was fetched -+ */ -+ std::time_t fetch_time = std::time(nullptr); -+ std::string gateway_source; ///< Who gave us this record? -+ -+ ValidatedIpns(); ///< Create an invalid default object -+ ValidatedIpns(IpnsCborEntry const&); -+ ValidatedIpns(ValidatedIpns&&); -+ ValidatedIpns(ValidatedIpns const&); -+ ValidatedIpns& operator=(ValidatedIpns const&); -+ -+ std::string Serialize() const; ///< Turn into a well-defined list of bytes -+ -+ /*! -+ * \brief Create a ValidatedIpns from untyped bytes -+ * \param bytes - Output from a former call to Serialize() -+ * \note Is used by disk cache -+ * \return Recreation of the old object -+ */ -+ static ValidatedIpns Deserialize(std::string bytes); -+}; -+ -+} // namespace ipfs -+ -+#endif // IPFS_IPNS_RECORD_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/json_cbor_adapter.h b/third_party/ipfs_client/include/ipfs_client/json_cbor_adapter.h -new file mode 100644 -index 0000000000000..5ed52ad465b0c ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/json_cbor_adapter.h -@@ -0,0 +1,155 @@ -+#ifndef IPFS_JSON_CBOR_ADAPTER_H_ -+#define IPFS_JSON_CBOR_ADAPTER_H_ -+ -+#include -+#include -+ -+#include -+#include -+ -+#if __has_include() -+ -+#include -+#define HAS_JSON_CBOR_ADAPTER 1 -+ -+namespace ipfs { -+// LCOV_EXCL_START -+class JsonCborAdapter final : public DagCborValue, public DagJsonValue { -+ nlohmann::json data_; -+ -+ public: -+ using Cid = ipfs::Cid; -+ JsonCborAdapter(nlohmann::json data) : data_{data} { -+ if (data_.is_array() && data_.size() == 1UL) { -+ data_ = data_[0]; -+ } -+ } -+ std::unique_ptr at(std::string_view k) const override { -+ if (data_.is_object() && data_.contains(k)) { -+ return std::make_unique(data_.at(k)); -+ } -+ return {}; -+ } -+ std::unique_ptr operator[](std::string_view k) const override { -+ if (data_.is_object() && data_.contains(k)) { -+ return std::make_unique(data_[k]); -+ } -+ return {}; -+ } -+ std::optional as_unsigned() const override { -+ if (data_.is_number_unsigned()) { -+ return data_.get(); -+ } -+ return std::nullopt; -+ } -+ std::optional as_signed() const { -+ if (data_.is_number_integer()) { -+ return data_.get(); -+ } else if (auto ui = as_unsigned()) { -+ if (*ui <= std::numeric_limits::max()) { -+ return static_cast(*ui); -+ } -+ } -+ return std::nullopt; -+ } -+ std::optional as_float() const override { -+ if (data_.is_number_float()) { -+ return data_.get(); -+ } -+ return std::nullopt; -+ } -+ std::optional as_string() const override { -+ if (data_.is_string()) { -+ return data_.get(); -+ } -+ return std::nullopt; -+ } -+ std::optional get_if_string() const override { -+ return as_string(); -+ } -+ std::optional as_bool() const override { -+ if (data_.is_boolean()) { -+ return data_.get(); -+ } -+ return std::nullopt; -+ } -+ std::optional> as_bytes() const override { -+ if (data_.is_binary()) { -+ return data_.get_binary(); -+ } -+ return std::nullopt; -+ } -+ std::optional as_link() const override { -+ if (!data_.is_binary()) { -+ return std::nullopt; -+ } -+ auto& bin = data_.get_binary(); -+ if (!bin.has_subtype() || bin.subtype() != 42) { -+ return std::nullopt; -+ } -+ if (bin.size() < 6) { -+ return std::nullopt; -+ } -+ if (bin[0]) { -+ return std::nullopt; -+ } -+ auto p = reinterpret_cast(bin.data()) + 1UL; -+ Cid from_binary(ByteView{p, bin.size() - 1UL}); -+ if (from_binary.valid()) { -+ return from_binary; -+ } else { -+ return std::nullopt; -+ } -+ } -+ bool is_map() const override {return data_.is_object();} -+ bool is_array() const override {return data_.is_array();} -+ void iterate_map(MapElementCallback cb) const override { -+ if (!is_map()) { -+ return; -+ } -+ for (auto& [k,v] : data_.items()) { -+ JsonCborAdapter el(v); -+ cb(k, el); -+ } -+ } -+ void iterate_array(ArrayElementCallback cb) const override { -+ if (!is_array()) { -+ return; -+ } -+ for (auto& v : data_) { -+ JsonCborAdapter el(v); -+ cb(el); -+ } -+ } -+ std::string pretty_print() const override { -+ std::ostringstream result; -+ result << std::setw(2) << data_; -+ return result.str(); -+ } -+ std::optional> object_keys() const override { -+ if (!data_.is_object()) { -+ return std::nullopt; -+ } -+ std::vector rv; -+ for (auto& [k, v] : data_.items()) { -+ rv.push_back(k); -+ } -+ return rv; -+ } -+ bool iterate_list( -+ std::function cb) const override { -+ if (!data_.is_array()) { -+ return false; -+ } -+ for (auto& v : data_) { -+ JsonCborAdapter wrap(v); -+ cb(wrap); -+ } -+ return true; -+ } -+}; -+} // namespace ipfs -+ -+#endif -+ -+#endif // IPFS_JSON_CBOR_ADAPTER_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/logger.h b/third_party/ipfs_client/include/ipfs_client/logger.h -new file mode 100644 -index 0000000000000..35191ac5f832c ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/logger.h -@@ -0,0 +1,34 @@ -+#ifndef IPFS_LOGGER_H_ -+#define IPFS_LOGGER_H_ -+ -+#include -+ -+namespace ipfs::log { -+ -+enum class Level { -+ TRACE = -2, -+ DEBUG = -1, -+ INFO = 0, -+ WARN = 1, -+ ERROR = 2, -+ FATAL = 3, -+ OFF -+}; -+ -+void SetLevel(Level); -+ -+using Handler = void (*)(std::string const&, char const*, int, Level); -+void SetHandler(Handler); -+ -+void DefaultHandler(std::string const& message, -+ char const* source_file, -+ int source_line, -+ Level for_prefix); -+ -+std::string_view LevelDescriptor(Level); -+ -+bool IsInitialized(); -+ -+} // namespace ipfs::log -+ -+#endif // LOGGER_H -diff --git a/third_party/ipfs_client/include/ipfs_client/multi_base.h b/third_party/ipfs_client/include/ipfs_client/multi_base.h -new file mode 100644 -index 0000000000000..8c09b97345635 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/multi_base.h -@@ -0,0 +1,42 @@ -+#ifndef IPFS_MB_PREFIXES_H_ -+#define IPFS_MB_PREFIXES_H_ -+ -+#include -+ -+#include -+#include -+#include -+#include -+ -+namespace ipfs::mb { -+ -+// https://github.com/multiformats/multibase/blob/master/multibase.csv -+enum class Code : char { -+ IDENTITY = '\0', -+ UNSUPPORTED = '1', -+ BASE16_LOWER = 'f', -+ BASE16_UPPER = 'F', -+ BASE32_LOWER = 'b', -+ BASE32_UPPER = 'B', -+ BASE36_LOWER = 'k', -+ BASE36_UPPER = 'K', -+ BASE58_BTC = 'z', -+ BASE64 = 'm' -+}; -+Code CodeFromPrefix(char c); -+std::string_view GetName(Code); -+ -+using Decoder = std::vector (*)(std::string_view); -+using Encoder = std::string (*)(ByteView); -+struct Codec { -+ Decoder const decode; -+ Encoder const encode; -+ std::string_view const name; -+ static Codec const* Get(Code); -+}; -+ -+std::string encode(Code, ByteView); -+std::optional> decode(std::string_view mb_str); -+} // namespace ipfs::mb -+ -+#endif // IPFS_MB_PREFIXES_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/multi_hash.h b/third_party/ipfs_client/include/ipfs_client/multi_hash.h -new file mode 100644 -index 0000000000000..6ed78f5e674dc ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/multi_hash.h -@@ -0,0 +1,32 @@ -+#ifndef IPFS_MULTI_HASH_H_ -+#define IPFS_MULTI_HASH_H_ -+ -+#include -+ -+#include -+ -+namespace ipfs { -+enum class HashType { INVALID = -1, IDENTITY = 0, SHA2_256 = 0X12 }; -+constexpr std::uint16_t MaximumHashLength = 127; -+ -+HashType Validate(HashType); -+std::string_view GetName(HashType); -+class MultiHash { -+ public: -+ MultiHash() = default; -+ explicit MultiHash(ByteView); -+ explicit MultiHash(HashType, ByteView digest); -+ -+ bool ReadPrefix(ByteView&); -+ -+ bool valid() const; -+ HashType type() const { return type_; } -+ ByteView digest() const { return hash_; } -+ -+ private: -+ HashType type_ = HashType::INVALID; -+ std::vector hash_; -+}; -+} // namespace ipfs -+ -+#endif // IPFS_MULTI_HASH_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/multicodec.h b/third_party/ipfs_client/include/ipfs_client/multicodec.h -new file mode 100644 -index 0000000000000..bf8d89b6c27e2 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/multicodec.h -@@ -0,0 +1,23 @@ -+#ifndef IPFS_MUTLICODEC_H_ -+#define IPFS_MUTLICODEC_H_ -+ -+#include -+#include -+ -+#include -+ -+namespace ipfs { -+enum class MultiCodec : std::uint32_t { -+ INVALID = std::numeric_limits::max(), -+ IDENTITY = 0x00, -+ RAW = 0x55, -+ DAG_PB = 0x70, -+ DAG_CBOR = 0x71, -+ LIBP2P_KEY = 0x72, -+ DAG_JSON = 0x0129, -+}; -+MultiCodec Validate(MultiCodec); -+std::string_view GetName(MultiCodec); -+} // namespace ipfs -+ -+#endif // IPFS_MUTLICODEC_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/orchestrator.h b/third_party/ipfs_client/include/ipfs_client/orchestrator.h -new file mode 100644 -index 0000000000000..f204dde799b3e ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/orchestrator.h -@@ -0,0 +1,45 @@ -+#ifndef IPFS_ORCHESTRATOR_H_ -+#define IPFS_ORCHESTRATOR_H_ -+ -+#include "ipfs_client/ipld/dag_node.h" -+ -+#include -+#include -+ -+#include -+#include -+ -+namespace ipfs { -+ -+class ContextApi; -+ -+class Orchestrator : public std::enable_shared_from_this { -+ public: -+ using GatewayAccess = -+ std::function)>; -+ using MimeDetection = std::function< -+ std::string(std::string, std::string_view, std::string const&)>; -+ explicit Orchestrator(std::shared_ptr requestor, -+ std::shared_ptr = {}); -+ void build_response(std::shared_ptr); -+ bool add_node(std::string key, ipld::NodePtr); -+ bool has_key(std::string const& k) const; -+ -+ private: -+ flat_map dags_; -+ // GatewayAccess gw_requestor_; -+ std::shared_ptr api_; -+ std::shared_ptr requestor_; -+ -+ void from_tree(std::shared_ptr, -+ ipld::NodePtr&, -+ SlashDelimited, -+ std::string const&); -+ bool gw_request(std::shared_ptr, -+ SlashDelimited path, -+ std::string const& aff); -+ std::string sniff(SlashDelimited, std::string const&) const; -+}; -+} // namespace ipfs::ipld -+ -+#endif // IPFS_ORCHESTRATOR_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/response.h b/third_party/ipfs_client/include/ipfs_client/response.h -new file mode 100644 -index 0000000000000..3c277994d8b9c ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/response.h -@@ -0,0 +1,27 @@ -+#ifndef IPFS_RESPONSE_H_ -+#define IPFS_RESPONSE_H_ -+ -+#include -+ -+#include -+#include -+#include -+ -+namespace ipfs { -+ -+struct Response { -+ std::string mime_; -+ std::uint16_t status_; -+ std::string body_; -+ std::string location_; -+ -+ static Response PLAIN_NOT_FOUND; -+ static Response IMMUTABLY_GONE; -+ static Response HOST_NOT_FOUND; -+ -+ constexpr static std::uint16_t HOST_NOT_FOUND_STATUS = 503; -+}; -+ -+} // namespace ipfs -+ -+#endif // IPFS_RESPONSE_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/signing_key_type.h b/third_party/ipfs_client/include/ipfs_client/signing_key_type.h -new file mode 100644 -index 0000000000000..4a74ad0f6967b ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/signing_key_type.h -@@ -0,0 +1,14 @@ -+#ifndef IPFS_SIGNING_KEY_TYPE_H_ -+#define IPFS_SIGNING_KEY_TYPE_H_ -+ -+namespace ipfs { -+enum class SigningKeyType : int { -+ RSA, -+ Ed25519, -+ Secp256k1, -+ ECDSA, -+ KeyTypeCount -+}; -+} -+ -+#endif // IPFS_SIGNING_KEY_TYPE_H_ -diff --git a/third_party/ipfs_client/include/ipfs_client/url_spec.h b/third_party/ipfs_client/include/ipfs_client/url_spec.h -new file mode 100644 -index 0000000000000..a61aec25d5968 ---- /dev/null -+++ b/third_party/ipfs_client/include/ipfs_client/url_spec.h -@@ -0,0 +1,24 @@ -+#ifndef IPFS_URL_SPEC_H_ -+#define IPFS_URL_SPEC_H_ -+ -+// TODO - Give more thought to how this interplays with gw::Request -+ -+#include -+#include -+ -+namespace ipfs { -+struct UrlSpec { -+ std::string suffix; -+ std::string_view accept; -+ -+ bool operator<(UrlSpec const& rhs) const { -+ if (suffix != rhs.suffix) { -+ return suffix < rhs.suffix; -+ } -+ return accept < rhs.accept; -+ } -+ bool none() const { return suffix.empty(); } -+}; -+} // namespace ipfs -+ -+#endif // IPFS_URL_SPEC_H_ -diff --git a/third_party/ipfs_client/include/libp2p/common/types.hpp b/third_party/ipfs_client/include/libp2p/common/types.hpp -new file mode 100644 -index 0000000000000..a112d1bf5d3db ---- /dev/null -+++ b/third_party/ipfs_client/include/libp2p/common/types.hpp -@@ -0,0 +1,39 @@ -+/** -+ * Copyright Soramitsu Co., Ltd. All Rights Reserved. -+ * SPDX-License-Identifier: Apache-2.0 -+ */ -+ -+#ifndef LIBP2P_P2P_COMMON_TYPES_HPP -+#define LIBP2P_P2P_COMMON_TYPES_HPP -+ -+#include "vocab/byte_view.h" -+ -+#include -+#include -+#include -+#include -+ -+namespace libp2p::common { -+/** -+ * Sequence of bytes -+ */ -+using ByteArray = std::vector; -+// using ByteArray = std::string; -+ -+template -+void append(Collection& c, Item&& g) { -+ c.insert(c.end(), g.begin(), g.end()); -+} -+ -+template -+void append(Collection& c, char g) { -+ c.push_back(g); -+} -+ -+/// Hash256 as a sequence of 32 bytes -+using Hash256 = std::array; -+/// Hash512 as a sequence of 64 bytes -+using Hash512 = std::array; -+} // namespace libp2p::common -+ -+#endif // LIBP2P_P2P_COMMON_TYPES_HPP -diff --git a/third_party/ipfs_client/include/libp2p/crypto/key.h b/third_party/ipfs_client/include/libp2p/crypto/key.h -new file mode 100644 -index 0000000000000..8198e41122fdd ---- /dev/null -+++ b/third_party/ipfs_client/include/libp2p/crypto/key.h -@@ -0,0 +1,100 @@ -+/** -+ * Copyright Soramitsu Co., Ltd. All Rights Reserved. -+ * SPDX-License-Identifier: Apache-2.0 -+ */ -+ -+#ifndef LIBP2P_LIBP2P_CRYPTO_KEY_HPP -+#define LIBP2P_LIBP2P_CRYPTO_KEY_HPP -+ -+#include -+ -+#include "libp2p/common/types.hpp" -+ -+namespace libp2p::crypto { -+ -+using Buffer = libp2p::common::ByteArray; -+ -+struct Key { -+ /** -+ * Supported types of all keys -+ */ -+ enum class Type { -+ UNSPECIFIED = 100, -+ RSA = 0, -+ Ed25519 = 1, -+ Secp256k1 = 2, -+ ECDSA = 3 -+ }; -+ -+ Key(Type, std::vector); -+ ~Key() noexcept; -+ Type type = Type::UNSPECIFIED; ///< key type -+ std::vector data{}; ///< key content -+}; -+ -+inline bool operator==(const Key& lhs, const Key& rhs) { -+ return lhs.type == rhs.type && lhs.data == rhs.data; -+} -+ -+inline bool operator!=(const Key& lhs, const Key& rhs) { -+ return !(lhs == rhs); -+} -+ -+struct PublicKey : public Key {}; -+ -+struct PrivateKey : public Key {}; -+ -+struct KeyPair { -+ PublicKey publicKey; -+ PrivateKey privateKey; -+}; -+ -+using Signature = std::vector; -+ -+inline bool operator==(const KeyPair& a, const KeyPair& b) { -+ return a.publicKey == b.publicKey && a.privateKey == b.privateKey; -+} -+ -+/** -+ * Result of ephemeral key generation -+ * -+struct EphemeralKeyPair { -+ Buffer ephemeral_public_key; -+ std::function(Buffer)> shared_secret_generator; -+}; -+*/ -+ -+/** -+ * Type of the stretched key -+ * -+struct StretchedKey { -+ Buffer iv; -+ Buffer cipher_key; -+ Buffer mac_key; -+}; -+*/ -+} // namespace libp2p::crypto -+ -+namespace std { -+template <> -+struct hash { -+ size_t operator()(const libp2p::crypto::Key& x) const; -+}; -+ -+template <> -+struct hash { -+ size_t operator()(const libp2p::crypto::PrivateKey& x) const; -+}; -+ -+template <> -+struct hash { -+ size_t operator()(const libp2p::crypto::PublicKey& x) const; -+}; -+ -+template <> -+struct hash { -+ size_t operator()(const libp2p::crypto::KeyPair& x) const; -+}; -+} // namespace std -+ -+#endif // LIBP2P_LIBP2P_CRYPTO_KEY_HPP -diff --git a/third_party/ipfs_client/include/libp2p/crypto/protobuf/protobuf_key.hpp b/third_party/ipfs_client/include/libp2p/crypto/protobuf/protobuf_key.hpp -new file mode 100644 -index 0000000000000..1a0d7ae7a2d4e ---- /dev/null -+++ b/third_party/ipfs_client/include/libp2p/crypto/protobuf/protobuf_key.hpp -@@ -0,0 +1,29 @@ -+/** -+ * Copyright Soramitsu Co., Ltd. All Rights Reserved. -+ * SPDX-License-Identifier: Apache-2.0 -+ */ -+ -+#ifndef KAGOME_PROTOBUF_KEY_HPP -+#define KAGOME_PROTOBUF_KEY_HPP -+ -+// #include -+ -+#include -+ -+#include -+ -+namespace libp2p::crypto { -+/** -+ * Strict type for key, which is encoded into Protobuf format -+ */ -+struct ProtobufKey { //: public boost::equality_comparable { -+ explicit ProtobufKey(std::vector key); -+ ~ProtobufKey() noexcept; -+ -+ std::vector key; -+ -+ bool operator==(const ProtobufKey& other) const { return key == other.key; } -+}; -+} // namespace libp2p::crypto -+ -+#endif // KAGOME_PROTOBUF_KEY_HPP -diff --git a/third_party/ipfs_client/include/libp2p/multi/multibase_codec.hpp b/third_party/ipfs_client/include/libp2p/multi/multibase_codec.hpp -new file mode 100644 -index 0000000000000..c7b9cbd1f7d40 ---- /dev/null -+++ b/third_party/ipfs_client/include/libp2p/multi/multibase_codec.hpp -@@ -0,0 +1,65 @@ -+/** -+ * Copyright Soramitsu Co., Ltd. All Rights Reserved. -+ * SPDX-License-Identifier: Apache-2.0 -+ */ -+ -+#ifndef LIBP2P_MULTIBASE_HPP -+#define LIBP2P_MULTIBASE_HPP -+ -+#include "vocab/expected.h" -+ -+#include -+#include -+#include -+ -+#include -+ -+namespace libp2p::multi { -+/** -+ * Allows to distinguish between different base-encoded binaries -+ * See more: https://github.com/multiformats/multibase -+ */ -+class MultibaseCodec { -+ public: -+ enum class Error { UNSUPPORTED_BASE = 1, INPUT_TOO_SHORT, BASE_CODEC_ERROR }; -+ -+ using ByteBuffer = common::ByteArray; -+ using FactoryResult = ipfs::expected; -+ -+ virtual ~MultibaseCodec() = default; -+ /** -+ * Encodings, supported by this Multibase -+ * @sa https://github.com/multiformats/multibase#multibase-table -+ */ -+ enum class Encoding : char { -+ BASE16_LOWER = 'f', -+ BASE16_UPPER = 'F', -+ BASE32_LOWER = 'b', -+ BASE32_UPPER = 'B', -+ BASE36 = 'k', -+ BASE58 = 'z', -+ BASE64 = 'm' -+ }; -+ -+ /** -+ * Encode the incoming bytes -+ * @param bytes to be encoded -+ * @param encoding - base of the desired encoding -+ * @return encoded string WITH an encoding prefix -+ */ -+ virtual std::string encode(const ByteBuffer& bytes, -+ Encoding encoding) const = 0; -+ -+ /** -+ * Decode the incoming string -+ * @param string to be decoded -+ * @return bytes, if decoding was successful, error otherwise -+ */ -+ virtual FactoryResult decode(std::string_view string) const = 0; -+}; -+ -+bool case_critical(MultibaseCodec::Encoding); -+ -+} // namespace libp2p::multi -+ -+#endif // LIBP2P_MULTIBASE_HPP -diff --git a/third_party/ipfs_client/include/libp2p/multi/multibase_codec/codecs/base16.h b/third_party/ipfs_client/include/libp2p/multi/multibase_codec/codecs/base16.h -new file mode 100644 -index 0000000000000..72a74237eb2ee ---- /dev/null -+++ b/third_party/ipfs_client/include/libp2p/multi/multibase_codec/codecs/base16.h -@@ -0,0 +1,24 @@ -+#ifndef IPFS_BASE32_H_ -+#define IPFS_BASE32_H_ -+ -+#include "base_error.hpp" -+ -+#include -+#include -+ -+#include -+ -+#include -+ -+namespace ipfs::base16 { -+std::string encodeLower(ByteView bytes); -+std::string encodeUpper(ByteView bytes); -+ -+using libp2p::common::ByteArray; -+using libp2p::multi::detail::BaseError; -+using Decoded = ipfs::expected; -+Decoded decode(std::string_view string); -+ -+} // namespace ipfs::base16 -+ -+#endif // IPFS_BASE32_H_ -diff --git a/third_party/ipfs_client/include/libp2p/multi/multibase_codec/codecs/base32.hpp b/third_party/ipfs_client/include/libp2p/multi/multibase_codec/codecs/base32.hpp -new file mode 100644 -index 0000000000000..c24dc59d54121 ---- /dev/null -+++ b/third_party/ipfs_client/include/libp2p/multi/multibase_codec/codecs/base32.hpp -@@ -0,0 +1,52 @@ -+/** -+ * Copyright Soramitsu Co., Ltd. All Rights Reserved. -+ * SPDX-License-Identifier: Apache-2.0 -+ */ -+ -+#ifndef LIBP2P_BASE32_HPP -+#define LIBP2P_BASE32_HPP -+ -+#include "base_error.hpp" -+ -+#include -+#include -+ -+/** -+ * Encode/decode to/from base32 format -+ * Implementation is taken from -+ * https://github.com/mjg59/tpmtotp/blob/master/base32.c -+ */ -+namespace libp2p::multi::detail { -+ -+/** -+ * Encode bytes to base32 uppercase string -+ * @param bytes to be encoded -+ * @return encoded string -+ */ -+std::string encodeBase32Upper(ipfs::ByteView bytes); -+/** -+ * Encode bytes to base32 lowercase string -+ * @param bytes to be encoded -+ * @return encoded string -+ */ -+std::string encodeBase32Lower(ipfs::ByteView bytes); -+ -+/** -+ * Decode base32 uppercase to bytes -+ * @param string to be decoded -+ * @return decoded bytes in case of success -+ */ -+ipfs::expected decodeBase32Upper( -+ std::string_view string); -+ -+/** -+ * Decode base32 lowercase string to bytes -+ * @param string to be decoded -+ * @return decoded bytes in case of success -+ */ -+ipfs::expected decodeBase32Lower( -+ std::string_view string); -+ -+} // namespace libp2p::multi::detail -+ -+#endif // LIBP2P_BASE32_HPP -diff --git a/third_party/ipfs_client/include/libp2p/multi/multibase_codec/codecs/base36.hpp b/third_party/ipfs_client/include/libp2p/multi/multibase_codec/codecs/base36.hpp -new file mode 100644 -index 0000000000000..20006df216d51 ---- /dev/null -+++ b/third_party/ipfs_client/include/libp2p/multi/multibase_codec/codecs/base36.hpp -@@ -0,0 +1,42 @@ -+/** -+ * Copyright Soramitsu Co., Ltd. All Rights Reserved. -+ * SPDX-License-Identifier: Apache-2.0 -+ */ -+ -+#ifndef LIBP2P_BASE36_HPP -+#define LIBP2P_BASE36_HPP -+ -+#include "base_error.hpp" -+ -+#include -+#include -+ -+/** -+ * Encode/decode to/from base36 format -+ */ -+namespace libp2p::multi::detail { -+ -+/** -+ * Encode bytes to base36 uppercase string -+ * @param bytes to be encoded -+ * @return encoded string -+ */ -+std::string encodeBase36Upper(ipfs::ByteView bytes); -+/** -+ * Encode bytes to base36 lowercase string -+ * @param bytes to be encoded -+ * @return encoded string -+ */ -+std::string encodeBase36Lower(ipfs::ByteView bytes); -+ -+/** -+ * Decode base36 (case-insensitively) to bytes -+ * @param string to be decoded -+ * @return decoded bytes in case of success -+ */ -+ipfs::expected decodeBase36( -+ std::string_view string); -+ -+} // namespace libp2p::multi::detail -+ -+#endif // LIBP2P_BASE36_HPP -diff --git a/third_party/ipfs_client/include/libp2p/multi/multibase_codec/codecs/base_error.hpp b/third_party/ipfs_client/include/libp2p/multi/multibase_codec/codecs/base_error.hpp -new file mode 100644 -index 0000000000000..a0ab1b6c54be5 ---- /dev/null -+++ b/third_party/ipfs_client/include/libp2p/multi/multibase_codec/codecs/base_error.hpp -@@ -0,0 +1,24 @@ -+/** -+ * Copyright Soramitsu Co., Ltd. All Rights Reserved. -+ * SPDX-License-Identifier: Apache-2.0 -+ */ -+ -+#ifndef LIBP2P_BASE_ERROR_HPP -+#define LIBP2P_BASE_ERROR_HPP -+ -+namespace libp2p::multi::detail { -+ -+enum class BaseError { -+ INVALID_BASE58_INPUT = 1, -+ INVALID_BASE64_INPUT, -+ INVALID_BASE32_INPUT, -+ INVALID_BASE36_INPUT, -+ NON_UPPERCASE_INPUT, -+ NON_LOWERCASE_INPUT, -+ UNIMPLEMENTED_MULTIBASE, -+ INVALID_BASE16_INPUT -+}; -+ -+} -+ -+#endif // LIBP2P_BASE_ERROR_HPP -diff --git a/third_party/ipfs_client/include/libp2p/multi/multicodec_type.hpp b/third_party/ipfs_client/include/libp2p/multi/multicodec_type.hpp -new file mode 100644 -index 0000000000000..bda027bb29567 ---- /dev/null -+++ b/third_party/ipfs_client/include/libp2p/multi/multicodec_type.hpp -@@ -0,0 +1,78 @@ -+/** -+ * Copyright Soramitsu Co., Ltd. All Rights Reserved. -+ * SPDX-License-Identifier: Apache-2.0 -+ */ -+ -+#ifndef LIBP2P_MULTICODECTYPE_HPP -+#define LIBP2P_MULTICODECTYPE_HPP -+ -+#include -+ -+namespace libp2p::multi { -+ -+/** -+ * LibP2P uses "protocol tables" to agree upon the mapping from one multicodec -+ * code. These tables can be application specific, though, like with other -+ * multiformats, there is a globally agreed upon table with common protocols -+ * and formats. -+ */ -+class MulticodecType { -+ public: -+ enum class Code { -+ IDENTITY = 0x00, -+ SHA1 = 0x11, -+ SHA2_256 = 0x12, -+ SHA2_512 = 0x13, -+ SHA3_512 = 0x14, -+ SHA3_384 = 0x15, -+ SHA3_256 = 0x16, -+ SHA3_224 = 0x17, -+ RAW = 0x55, -+ DAG_PB = 0x70, -+ DAG_CBOR = 0x71, -+ LIBP2P_KEY = 0x72, -+ DAG_JSON = 0x0129, -+ FILECOIN_COMMITMENT_UNSEALED = 0xf101, -+ FILECOIN_COMMITMENT_SEALED = 0xf102, -+ }; -+ -+ constexpr static std::string_view getName(Code code) { -+ switch (code) { -+ case Code::IDENTITY: -+ return "identity"; -+ case Code::SHA1: -+ return "sha1"; -+ case Code::SHA2_256: -+ return "sha2-256"; -+ case Code::SHA2_512: -+ return "sha2-512"; -+ case Code::SHA3_224: -+ return "sha3-224"; -+ case Code::SHA3_256: -+ return "sha3-256"; -+ case Code::SHA3_384: -+ return "sha3-384"; -+ case Code::SHA3_512: -+ return "sha3-512"; -+ case Code::RAW: -+ return "raw"; -+ case Code::DAG_PB: -+ return "dag-pb"; -+ case Code::DAG_CBOR: -+ return "dag-cbor"; -+ case Code::DAG_JSON: -+ return "dag-json"; -+ case Code::LIBP2P_KEY: -+ return "libp2p-key"; -+ case Code::FILECOIN_COMMITMENT_UNSEALED: -+ return "fil-commitment-unsealed"; -+ case Code::FILECOIN_COMMITMENT_SEALED: -+ return "fil-commitment-sealed"; -+ } -+ return "unknown"; -+ } -+}; -+ -+} // namespace libp2p::multi -+ -+#endif // LIBP2P_MULTICODECTYPE_HPP -diff --git a/third_party/ipfs_client/include/libp2p/multi/uvarint.hpp b/third_party/ipfs_client/include/libp2p/multi/uvarint.hpp -new file mode 100644 -index 0000000000000..4dd452abffba4 ---- /dev/null -+++ b/third_party/ipfs_client/include/libp2p/multi/uvarint.hpp -@@ -0,0 +1,98 @@ -+/** -+ * Copyright Soramitsu Co., Ltd. All Rights Reserved. -+ * SPDX-License-Identifier: Apache-2.0 -+ */ -+ -+#ifndef LIBP2P_VARINT_HPP -+#define LIBP2P_VARINT_HPP -+ -+#include "vocab/byte_view.h" -+ -+#include -+ -+#include -+#include -+#include -+ -+namespace libp2p::multi { -+ -+/** -+ * @class Encodes and decodes unsigned integers into and from -+ * variable-length byte arrays using LEB128 algorithm. -+ */ -+class UVarint { -+ public: -+ /** -+ * Constructs a varint from an unsigned integer 'number' -+ * @param number -+ */ -+ explicit UVarint(uint64_t number); -+ -+ /** -+ * Constructs a varint from an array of raw bytes, which are -+ * meant to be an already encoded unsigned varint -+ * @param varint_bytes an array of bytes representing an unsigned varint -+ */ -+ explicit UVarint(ipfs::ByteView varint_bytes); -+ -+ /** -+ * Constructs a varint from an array of raw bytes, which beginning may or -+ * may not be an encoded varint -+ * @param varint_bytes an array of bytes, possibly representing an unsigned -+ * varint -+ */ -+ static std::optional create(ipfs::ByteView varint_bytes); -+ -+ /** -+ * Converts a varint back to a usual unsigned integer. -+ * @return an integer previously encoded to the varint -+ */ -+ uint64_t toUInt64() const; -+ -+ /** -+ * @return an array view to raw bytes of the stored varint -+ */ -+ ipfs::ByteView toBytes() const; -+ -+ std::vector const& toVector() const; -+ -+ std::string toHex() const; -+ -+ /** -+ * Assigns the varint to an unsigned integer, encoding the latter -+ * @param n the integer to encode and store -+ * @return this varint -+ */ -+ UVarint& operator=(uint64_t n); -+ -+ bool operator==(const UVarint& r) const; -+ bool operator!=(const UVarint& r) const; -+ bool operator<(const UVarint& r) const; -+ -+ /** -+ * @return the number of bytes currently stored in a varint -+ */ -+ size_t size() const; -+ -+ /** -+ * @param varint_bytes an array with a raw byte representation of a varint -+ * @return the size of the varint stored in the array, if its content is a -+ * valid varint. Otherwise, the result is undefined -+ */ -+ static size_t calculateSize(ipfs::ByteView varint_bytes); -+ -+ UVarint() = delete; -+ UVarint(UVarint const&); -+ UVarint& operator=(UVarint const&); -+ ~UVarint() noexcept; -+ -+ private: -+ /// private ctor for unsafe creation -+ UVarint(ipfs::ByteView varint_bytes, size_t varint_size); -+ -+ std::vector bytes_{}; -+}; -+ -+} // namespace libp2p::multi -+ -+#endif // LIBP2P_VARINT_HPP -diff --git a/third_party/ipfs_client/include/multibase/algorithm.h b/third_party/ipfs_client/include/multibase/algorithm.h -new file mode 100644 -index 0000000000000..2cea1cabd296e ---- /dev/null -+++ b/third_party/ipfs_client/include/multibase/algorithm.h -@@ -0,0 +1,27 @@ -+#pragma once -+ -+#include -+ -+namespace multibase { -+ -+class algorithm { -+ public: -+ /** Tag identifying algorithms which operate on blocks */ -+ class block_tag {}; -+ -+ /** Tag identifying algorithms which operate on continuous data */ -+ class stream_tag {}; -+ -+ virtual ~algorithm() = default; -+ -+ /** Returns the input size required to decode a single block */ -+ virtual std::size_t block_size() { return 0; } -+ -+ /** Returns the size of a processed block */ -+ virtual std::size_t output_size() { return 0; } -+ -+ /** Processes an input block returning any intermediate result */ -+ virtual std::string process(std::string_view input) = 0; -+}; -+ -+} // namespace multibase -diff --git a/third_party/ipfs_client/include/multibase/basic_algorithm.h b/third_party/ipfs_client/include/multibase/basic_algorithm.h -new file mode 100644 -index 0000000000000..5da225c885fd4 ---- /dev/null -+++ b/third_party/ipfs_client/include/multibase/basic_algorithm.h -@@ -0,0 +1,322 @@ -+/* From: https://github.com/lockblox/multibase -+ * Copyright (c) 2018 markovchainy -+ * MIT License -+ */ -+#pragma once -+ -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+namespace multibase { -+ -+template -+struct traits { -+ static const std::array charset; -+ static const char name[]; -+ static const char padding = 0; -+ using execution_style = algorithm::block_tag; -+}; -+ -+/** Template implementation of base encoding which computes a lookup table at -+ * compile time and avoids the virtual algorithm lookup penalty */ -+template > -+class basic_algorithm { -+ public: -+ class encoder : public algorithm { -+ public: -+ size_t output_size() override; -+ size_t block_size() override; -+ std::string process(std::string_view input) override; -+ -+ private: -+ constexpr size_t input_size() { return ratio.den; } -+ }; -+ -+ class decoder : public algorithm { -+ public: -+ size_t output_size() override; -+ size_t block_size() override; -+ std::string process(std::string_view input) override; -+ -+ private: -+ constexpr size_t input_size() { return ratio.num; } -+ }; -+ -+ private: -+ constexpr static auto first = Traits::charset.cbegin(); -+ constexpr static auto last = Traits::charset.cend(); -+ using CharsetT = decltype(Traits::charset); -+ using value_type = typename CharsetT::value_type; -+ using iterator = typename CharsetT::const_iterator; -+ -+ /** Find a value at compile time */ -+ constexpr static iterator find(iterator b, iterator e, -+ value_type const& v) noexcept { -+ return (b != e && *b != v) ? find(++b, e, v) : b; -+ } -+ -+ /** Determine the character encoding for a given value -+ @return character encoding, or xFF if none such encoding exists */ -+ constexpr static unsigned char getval(unsigned char p) noexcept { -+ return find(first, last, p) == last -+ ? static_cast(255) -+ : static_cast( -+ std::distance(first, find(first, last, p))); -+ } -+ -+ /** Compute base-2 logarithm */ -+ constexpr static std::intmax_t log2(std::intmax_t n) noexcept { -+ return (n == 1) ? 0 : ((n < 2) ? 1 : 1 + log2(n / 2)); -+ } -+ -+ /** encoding as determined by size of character set */ -+ constexpr static auto radix = sizeof(Traits::charset) / sizeof(value_type); -+ /** Ratio of encoded characters per byte */ -+ constexpr static auto ratio = std::ratio{}; -+ /** Map from value to corresponding character in base encoding */ -+ static const std::array valset; -+ -+ constexpr static auto base = T; -+}; -+ -+template -+const std::array basic_algorithm::valset = { -+ getval(0), getval(1), getval(2), getval(3), getval(4), -+ getval(5), getval(6), getval(7), getval(8), getval(9), -+ getval(10), getval(11), getval(12), getval(13), getval(14), -+ getval(15), getval(16), getval(17), getval(18), getval(19), -+ getval(20), getval(21), getval(22), getval(23), getval(24), -+ getval(25), getval(26), getval(27), getval(28), getval(29), -+ getval(30), getval(31), getval(32), getval(33), getval(34), -+ getval(35), getval(36), getval(37), getval(38), getval(39), -+ getval(40), getval(41), getval(42), getval(43), getval(44), -+ getval(45), getval(46), getval(47), getval(48), getval(49), -+ getval(50), getval(51), getval(52), getval(53), getval(54), -+ getval(55), getval(56), getval(57), getval(58), getval(59), -+ getval(60), getval(61), getval(62), getval(63), getval(64), -+ getval(65), getval(66), getval(67), getval(68), getval(69), -+ getval(70), getval(71), getval(72), getval(73), getval(74), -+ getval(75), getval(76), getval(77), getval(78), getval(79), -+ getval(80), getval(81), getval(82), getval(83), getval(84), -+ getval(85), getval(86), getval(87), getval(88), getval(89), -+ getval(90), getval(91), getval(92), getval(93), getval(94), -+ getval(95), getval(96), getval(97), getval(98), getval(99), -+ getval(100), getval(101), getval(102), getval(103), getval(104), -+ getval(105), getval(106), getval(107), getval(108), getval(109), -+ getval(110), getval(111), getval(112), getval(113), getval(114), -+ getval(115), getval(116), getval(117), getval(118), getval(119), -+ getval(120), getval(121), getval(122), getval(123), getval(124), -+ getval(125), getval(126), getval(127), getval(128), getval(129), -+ getval(130), getval(131), getval(132), getval(133), getval(134), -+ getval(135), getval(136), getval(137), getval(138), getval(139), -+ getval(140), getval(141), getval(142), getval(143), getval(144), -+ getval(145), getval(146), getval(147), getval(148), getval(149), -+ getval(150), getval(151), getval(152), getval(153), getval(154), -+ getval(155), getval(156), getval(157), getval(158), getval(159), -+ getval(160), getval(161), getval(162), getval(163), getval(164), -+ getval(165), getval(166), getval(167), getval(168), getval(169), -+ getval(170), getval(171), getval(172), getval(173), getval(174), -+ getval(175), getval(176), getval(177), getval(178), getval(179), -+ getval(180), getval(181), getval(182), getval(183), getval(184), -+ getval(185), getval(186), getval(187), getval(188), getval(189), -+ getval(190), getval(191), getval(192), getval(193), getval(194), -+ getval(195), getval(196), getval(197), getval(198), getval(199), -+ getval(200), getval(201), getval(202), getval(203), getval(204), -+ getval(205), getval(206), getval(207), getval(208), getval(209), -+ getval(210), getval(211), getval(212), getval(213), getval(214), -+ getval(215), getval(216), getval(217), getval(218), getval(219), -+ getval(220), getval(221), getval(222), getval(223), getval(224), -+ getval(225), getval(226), getval(227), getval(228), getval(229), -+ getval(230), getval(231), getval(232), getval(233), getval(234), -+ getval(235), getval(236), getval(237), getval(238), getval(239), -+ getval(240), getval(241), getval(242), getval(243), getval(244), -+ getval(245), getval(246), getval(247), getval(248), getval(249), -+ getval(250), getval(251), getval(252), getval(253), getval(254), -+ getval(255)}; -+ -+template -+std::string basic_algorithm::encoder::process( -+ std::string_view input) { -+ std::string output; -+ std::size_t isize = input.size(); -+ auto partial_blocks = static_cast(input.size()) / input_size(); -+ auto num_blocks = static_cast(partial_blocks); -+ auto osize = static_cast(std::ceil(partial_blocks * output_size())); -+ if constexpr (std::is_same_v) { -+ num_blocks = static_cast(std::ceil(partial_blocks)); -+ isize = input_size() * num_blocks; -+ } -+ output.resize(std::max(osize, (output_size() * num_blocks))); -+ auto input_it = std::begin(input); -+ int length = 0; -+ for (std::size_t i = 0; i < isize; ++i, ++input_it) { -+ int carry = i >= input.size() ? 0 : static_cast(*input_it); -+ int j = 0; -+ for (auto oi = output.rbegin(); -+ (oi != output.rend()) && (carry != 0 || j < length); ++oi, ++j) { -+ carry += 256 * (*oi); -+ auto byte = (unsigned char*)(&(*oi)); -+ *byte = carry % radix; -+ carry /= radix; -+ } -+ length = j; -+ } -+ std::transform(output.rbegin(), output.rend(), output.rbegin(), -+ [](auto c) { return Traits::charset[c]; }); -+ if constexpr (Traits::padding == 0) { -+ output.resize(osize); -+ } else { -+ auto pad_size = output.size() - osize; -+ output.replace(osize, pad_size, pad_size, Traits::padding); -+ } -+ if constexpr (std::is_same_v) { -+ output.erase(0, output.size() % output_size() ? output.size() - length : 0); -+ } -+ return output; -+} -+ -+template -+std::size_t basic_algorithm::encoder::block_size() { -+ return std::is_same_v -+ ? input_size() -+ : 0; -+} -+ -+template -+std::size_t basic_algorithm::encoder::output_size() { -+ return ratio.num; -+} -+ -+template -+std::size_t basic_algorithm::decoder::block_size() { -+ return std::is_same_v -+ ? input_size() -+ : 0; -+} -+ -+template -+std::size_t basic_algorithm::decoder::output_size() { -+ return ratio.den; -+} -+ -+template -+std::string basic_algorithm::decoder::process( -+ std::string_view input) { -+ std::string output; -+ auto end = std::find(input.begin(), input.end(), Traits::padding); -+ size_t input_size = std::distance(input.begin(), end); -+ auto partial_blocks = static_cast(input_size) / this->input_size(); -+ auto output_size = static_cast(this->output_size() * partial_blocks); -+ if constexpr (std::is_same_v) { -+ std::size_t num_blocks = 0; -+ auto input_size_float = static_cast(input.size()); -+ num_blocks = -+ static_cast(std::ceil(input_size_float / this->input_size())); -+ output.resize(this->output_size() * num_blocks); -+ input_size = this->input_size() * num_blocks; -+ } else { -+ output.resize(output_size); -+ } -+ auto input_it = input.begin(); -+ for (size_t i = 0; i < input_size; ++i, ++input_it) { -+ int carry = i > input.size() || *input_it == Traits::padding -+ ? 0 -+ : valset[(unsigned char)(*input_it)]; -+ if (carry == 255) { -+ // throw std::invalid_argument(std::string{"Invalid input character -+ // "} + *input_it); -+ return {}; -+ } -+ auto j = output.size(); -+ while (carry != 0 || j > 0) { -+ auto index = j - 1; -+ carry += radix * static_cast(output[index]); -+ output[index] = static_cast(carry % 256); -+ carry /= 256; -+ if (carry > 0 && index == 0) { -+ output.insert(0, 1, 0); -+ } else { -+ j = index; -+ } -+ } -+ } -+ if constexpr (std::is_same_v) { -+ output.erase(output_size, output.size()); -+ } -+ return output; -+} -+ -+template <> -+struct traits { -+ constexpr static const std::array charset = { -+ '0', '1', '2', '3', '4', '5', '6', '7', -+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; -+ constexpr static const char name[] = "base_16"; -+ using execution_style = algorithm::block_tag; -+ constexpr static const char padding = 0; -+}; -+using base_16 = basic_algorithm; -+ -+template <> -+struct traits { -+ constexpr static const std::array charset = { -+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', -+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', -+ 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', -+ 'u', 'v', 'w', 'x', 'y', 'z'}; -+ constexpr static const char name[] = "base_36"; -+ using execution_style = algorithm::stream_tag; -+ constexpr static const char padding = 0; -+}; -+using base_36_btc = basic_algorithm; -+ -+ -+template <> -+struct traits { -+ constexpr static const std::array charset = { -+ '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', -+ 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', -+ 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm', -+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; -+ constexpr static const char name[] = "base_58_btc"; -+ using execution_style = algorithm::stream_tag; -+ constexpr static const char padding = 0; -+}; -+using base_58_btc = basic_algorithm; -+ -+template <> -+struct traits { -+ constexpr static const std::array charset = { -+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', -+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', -+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', -+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', -+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; -+ constexpr static const char name[] = "base_64_pad"; -+ using execution_style = algorithm::block_tag; -+ constexpr static const char padding = '='; -+}; -+using base_64_pad = basic_algorithm; -+ -+template <> -+struct traits { -+ constexpr static const std::array charset = { -+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', -+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', -+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', -+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', -+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; -+ constexpr static const char name[] = "base_64"; -+ using base_64 = basic_algorithm; -+ using execution_style = algorithm::block_tag; -+ constexpr static const char padding = 0; -+}; -+using base_64 = basic_algorithm; -+ -+} // namespace multibase -diff --git a/third_party/ipfs_client/include/multibase/encoding.h b/third_party/ipfs_client/include/multibase/encoding.h -new file mode 100644 -index 0000000000000..7675ca6e8445a ---- /dev/null -+++ b/third_party/ipfs_client/include/multibase/encoding.h -@@ -0,0 +1,21 @@ -+#pragma once -+#include -+#include -+ -+namespace multibase { -+ -+enum class encoding : unsigned char { -+ base_unknown = '?', -+ base_256 = 0, -+ base_16 = 'f', -+ base_16_upper = 'F', -+ base_32 = 'b', -+ base_32_upper = 'B', -+ base_36 = 'k', -+ base_58_btc = 'Z', -+ base_64 = 'm', -+ base_64_pad = 'M' -+ -+}; -+ -+} // namespace multibase -diff --git a/third_party/ipfs_client/include/smhasher/MurmurHash3.h b/third_party/ipfs_client/include/smhasher/MurmurHash3.h -new file mode 100644 -index 0000000000000..e1c6d34976c6a ---- /dev/null -+++ b/third_party/ipfs_client/include/smhasher/MurmurHash3.h -@@ -0,0 +1,37 @@ -+//----------------------------------------------------------------------------- -+// MurmurHash3 was written by Austin Appleby, and is placed in the public -+// domain. The author hereby disclaims copyright to this source code. -+ -+#ifndef _MURMURHASH3_H_ -+#define _MURMURHASH3_H_ -+ -+//----------------------------------------------------------------------------- -+// Platform-specific functions and macros -+ -+// Microsoft Visual Studio -+ -+#if defined(_MSC_VER) && (_MSC_VER < 1600) -+ -+typedef unsigned char uint8_t; -+typedef unsigned int uint32_t; -+typedef unsigned __int64 uint64_t; -+ -+// Other compilers -+ -+#else // defined(_MSC_VER) -+ -+#include -+ -+#endif // !defined(_MSC_VER) -+ -+//----------------------------------------------------------------------------- -+ -+void MurmurHash3_x86_32 ( const void * key, int len, uint32_t seed, void * out ); -+ -+void MurmurHash3_x86_128 ( const void * key, int len, uint32_t seed, void * out ); -+ -+void MurmurHash3_x64_128 ( const void * key, int len, uint32_t seed, void * out ); -+ -+//----------------------------------------------------------------------------- -+ -+#endif // _MURMURHASH3_H_ -diff --git a/third_party/ipfs_client/include/vocab/byte.h b/third_party/ipfs_client/include/vocab/byte.h -new file mode 100644 -index 0000000000000..17477c11c2a6f ---- /dev/null -+++ b/third_party/ipfs_client/include/vocab/byte.h -@@ -0,0 +1,43 @@ -+#ifndef IPFS_BYTE_H_ -+#define IPFS_BYTE_H_ -+ -+#include -+#include -+ -+#include -+#include -+#include -+ -+#ifdef __cpp_lib_byte -+ -+namespace ipfs { -+using Byte = std::byte; -+} // namespace ipfs -+ -+#else -+namespace ipfs { -+enum class Byte : std::uint_least8_t {}; -+} // namespace ipfs -+#endif -+ -+namespace { -+[[maybe_unused]] std::ostream& operator<<(std::ostream& str, ipfs::Byte b) { -+ return str << std::hex << std::setw(2) << std::setfill('0') -+ << static_cast(b); -+} -+} // namespace -+ -+namespace { -+// libc++ provides this, but for some reason libstdc++ does not -+[[maybe_unused]] std::uint8_t to_integer(ipfs::Byte b) { -+ return static_cast(b); -+} -+} // namespace -+ -+namespace ipfs { -+inline bool operator==(Byte a, Byte b) { -+ return to_integer(a) == to_integer(b); -+} -+} // namespace ipfs -+ -+#endif // IPFS_BYTE_H_ -diff --git a/third_party/ipfs_client/include/vocab/byte_view.h b/third_party/ipfs_client/include/vocab/byte_view.h -new file mode 100644 -index 0000000000000..69858d1972a30 ---- /dev/null -+++ b/third_party/ipfs_client/include/vocab/byte_view.h -@@ -0,0 +1,24 @@ -+#ifndef CHROMIUM_IPFS_BYTE_VIEW_H -+#define CHROMIUM_IPFS_BYTE_VIEW_H -+ -+#include "byte.h" -+#include "span.h" -+ -+#include -+ -+namespace ipfs { -+using ByteView = span; -+ -+// ByteView is a view over arbitrary opaque byte -+// Cast it to a view over 8-bit unsigned integers for inspection -+inline span as_octets(ByteView bytes) { -+ return {reinterpret_cast(bytes.data()), bytes.size()}; -+} -+template -+inline ByteView as_bytes(ContiguousBytes const& b) { -+ auto p = reinterpret_cast(b.data()); -+ return ByteView{p, b.size()}; -+} -+} // namespace ipfs -+ -+#endif // CHROMIUM_IPFS_BYTE_VIEW_H -diff --git a/third_party/ipfs_client/include/vocab/endian.h b/third_party/ipfs_client/include/vocab/endian.h -new file mode 100644 -index 0000000000000..2423006c7c02b ---- /dev/null -+++ b/third_party/ipfs_client/include/vocab/endian.h -@@ -0,0 +1,21 @@ -+#ifndef IPFS_ENDIAN_H_ -+#define IPFS_ENDIAN_H_ -+ -+#if __has_include() -+#include -+#endif -+#if __has_include() -+#include -+#endif -+ -+#ifdef htobe64 -+// Good -+#elif __has_include() -+#include -+#define htobe64 absl::ghtonll -+#elif __has_include() -+#include -+#define htobe64 native_to_big -+#endif -+ -+#endif // IPFS_ENDIAN_H_ -diff --git a/third_party/ipfs_client/include/vocab/expected.h b/third_party/ipfs_client/include/vocab/expected.h -new file mode 100644 -index 0000000000000..2006f2bf01397 ---- /dev/null -+++ b/third_party/ipfs_client/include/vocab/expected.h -@@ -0,0 +1,44 @@ -+#ifndef IPFS_EXPECTED_H_ -+#define IPFS_EXPECTED_H_ -+ -+// std::expected isn't available until C++23 and we need to support C++17 -+// boost::outcome isn't available inside the Chromium tree -+// absl::StatusOr doesn't allow templating or extending the error type, and -+// translating the specific error codes into generic ones isn't great. -+ -+#if __has_include("base/types/expected.h") -+#include "base/types/expected.h" -+namespace ipfs { -+template -+using expected = base::expected; -+template -+using unexpected = base::unexpected; -+} // namespace ipfs -+#elif __has_cpp_attribute(__cpp_lib_expected) -+ -+#include -+namespace ipfs { -+template -+using expected = std::expected; -+template -+using unexpected = std::unexpected; -+} // namespace ipfs -+ -+#elif __has_include() -+ -+// If the API differences between std::expected and boost::outcome::checked -+// become a problem, consider wrapping as proposed in the FAQ: -+// https://www.boost.org/doc/libs/master/libs/outcome/doc/html/faq.html#how-far-away-from-the-proposed-std-expected-t-e-is-outcome-s-checked-t-e -+#include -+namespace ipfs { -+template -+using expected = boost::outcome_v2::checked; -+template -+using unexpected = Error; -+} // namespace ipfs -+ -+#else -+#error Get an expected implementation -+#endif -+ -+#endif // IPFS_EXPECTED_H_ -diff --git a/third_party/ipfs_client/include/vocab/flat_mapset.h b/third_party/ipfs_client/include/vocab/flat_mapset.h -new file mode 100644 -index 0000000000000..1630e3f9ca358 ---- /dev/null -+++ b/third_party/ipfs_client/include/vocab/flat_mapset.h -@@ -0,0 +1,39 @@ -+#ifndef CHROMIUM_IPFS_VOCAB_MAP_SET_H_ -+#define CHROMIUM_IPFS_VOCAB_MAP_SET_H_ -+ -+#if __has_include("base/containers/flat_map.h") // Chromium -+ -+#include "base/containers/flat_map.h" -+#include "base/containers/flat_set.h" -+#include "base/debug/debugging_buildflags.h" -+namespace ipfs { -+using base::flat_map; -+using base::flat_set; -+} // namespace ipfs -+ -+#elif __has_cpp_attribute(__cpp_lib_flat_map) && \ -+ __has_cpp_attribute(__cpp_lib_flat_set) -+ -+#include -+#include -+namespace ipfs { -+using std::flat_map; -+using std::flat_set; -+} // namespace ipfs -+ -+#elif __has_include() //Boost -+#include -+#include -+namespace ipfs { -+using boost::container::flat_map; -+using boost::container::flat_set; -+} // namespace ipfs -+ -+#else -+ -+#error \ -+ "Provide an implementation for flat_map and flat_set, or install boost or have a Chromium tree or use a newer C++ version." -+ -+#endif -+ -+#endif // CHROMIUM_IPFS_VOCAB_MAP_SET_H_ -diff --git a/third_party/ipfs_client/include/vocab/html_escape.h b/third_party/ipfs_client/include/vocab/html_escape.h -new file mode 100644 -index 0000000000000..60339ad7d45bd ---- /dev/null -+++ b/third_party/ipfs_client/include/vocab/html_escape.h -@@ -0,0 +1,23 @@ -+#ifndef IPFS_HTML_ESCAPE_H_ -+#define IPFS_HTML_ESCAPE_H_ -+ -+#include -+ -+constexpr inline std::string_view html_escape(char& c) { -+ switch (c) { -+ case '"': -+ return """; -+ case '\'': -+ return "'"; -+ case '<': -+ return "<"; -+ case '>': -+ return ">"; -+ case '&': -+ return "&"; -+ default: -+ return {&c, 1UL}; -+ } -+} -+ -+#endif // IPFS_HTML_ESCAPE_H_ -diff --git a/third_party/ipfs_client/include/vocab/i128.h b/third_party/ipfs_client/include/vocab/i128.h -new file mode 100644 -index 0000000000000..4aa36cc09877f ---- /dev/null -+++ b/third_party/ipfs_client/include/vocab/i128.h -@@ -0,0 +1,16 @@ -+#ifndef IPFS_I128_H_ -+#define IPFS_I128_H_ -+ -+#if __has_include() -+#include -+namespace ipfs { -+using Int_128 = absl::int128; -+} -+#else -+namespace ipfs { -+// TODO Check if available, if not use boost multiprecision -+using Int_128 = __int128; -+} // namespace ipfs -+#endif -+ -+#endif // IPFS_I128_H_ -diff --git a/third_party/ipfs_client/include/vocab/raw_ptr.h b/third_party/ipfs_client/include/vocab/raw_ptr.h -new file mode 100644 -index 0000000000000..25405d3ea30ba ---- /dev/null -+++ b/third_party/ipfs_client/include/vocab/raw_ptr.h -@@ -0,0 +1,64 @@ -+#ifndef IPFS_OBSERVER_PTR_H_ -+#define IPFS_OBSERVER_PTR_H_ -+ -+#if __has_include("base/memory/raw_ptr.h") -+#include "base/memory/raw_ptr.h" -+ -+namespace ipfs { -+template -+using raw_ptr = base::raw_ptr; -+} -+ -+#elif defined(__has_cpp_attribute) && \ -+ __has_cpp_attribute(__cpp_lib_experimental_observer_ptr) -+#include -+ -+namespace ipfs { -+template -+using raw_ptr = std::experimental::observer_ptr; -+} -+ -+#else -+ -+#include -+ -+namespace ipfs { -+ -+/*! -+ * \brief Just an observing (non-owning) pointer. -+ */ -+template -+class raw_ptr { -+ T* ptr_; -+ -+ public: -+ // Chromium's raw_ptr has a default ctor whose semantics depend on build -+ // config. For components/ipfs purposes, there is no reason to ever default -+ // construct. Set it to nullptr. We have time needed to read_start a word. -+ raw_ptr() = delete; -+ -+ raw_ptr(T* p) : ptr_{p} {} -+ raw_ptr(raw_ptr&&) = default; -+ raw_ptr(raw_ptr const&) = default; -+ -+ raw_ptr& operator=(raw_ptr const&) = default; -+ -+ T* get() { return ptr_; } -+ T const* get() const { return ptr_; } -+ explicit operator bool() const { return !!ptr_; } -+ T* operator->() { return ptr_; } -+ T const* operator->() const { return ptr_; } -+ raw_ptr& operator=(T* p) { -+ ptr_ = p; -+ return *this; -+ } -+ T& operator*() { -+ assert(ptr_); -+ return *ptr_; -+ } -+}; -+} // namespace ipfs -+ -+#endif -+ -+#endif // IPFS_OBSERVER_PTR_H_ -diff --git a/third_party/ipfs_client/include/vocab/slash_delimited.h b/third_party/ipfs_client/include/vocab/slash_delimited.h -new file mode 100644 -index 0000000000000..53fd142465028 ---- /dev/null -+++ b/third_party/ipfs_client/include/vocab/slash_delimited.h -@@ -0,0 +1,35 @@ -+#ifndef IPFS_SLASH_DELIMITED_H_ -+#define IPFS_SLASH_DELIMITED_H_ -+ -+#include -+#include -+#include -+ -+namespace google::protobuf::internal { -+class LogMessage; -+} -+ -+namespace ipfs { -+struct SlashDelimited { -+ std::string_view remainder_; -+ -+ public: -+ SlashDelimited() : remainder_{""} {} -+ explicit SlashDelimited(std::string_view unowned); -+ explicit operator bool() const; -+ std::string_view pop(); -+ std::string_view pop_all(); -+ std::string_view pop_n(std::size_t); -+ std::string_view peek_back() const; -+ std::string pop_back(); -+ std::string to_string() const { return std::string{remainder_}; } -+ std::string_view to_view() const { return remainder_; } -+}; -+} // namespace ipfs -+ -+std::ostream& operator<<(std::ostream&, ipfs::SlashDelimited const&); -+google::protobuf::internal::LogMessage& operator<<( -+ google::protobuf::internal::LogMessage&, -+ ipfs::SlashDelimited const&); -+ -+#endif // IPFS_SLASH_DELIMITED_H_ -diff --git a/third_party/ipfs_client/include/vocab/span.h b/third_party/ipfs_client/include/vocab/span.h -new file mode 100644 -index 0000000000000..f9c05d2a7dd61 ---- /dev/null -+++ b/third_party/ipfs_client/include/vocab/span.h -@@ -0,0 +1,65 @@ -+#ifndef IPFS_SPAN_H_ -+#define IPFS_SPAN_H_ -+ -+#if __cpp_lib_span -+#include -+ -+namespace ipfs { -+template -+using span = std::span; -+} // namespace ipfs -+ -+#elif __has_include("base/containers/span.h") -+ -+#include "base/containers/span.h" -+namespace ipfs { -+template -+using span = base::span; -+} // namespace ipfs -+ -+#elif __has_include() -+ -+#include -+namespace ipfs { -+template -+using span = absl::Span; -+} // namespace ipfs -+ -+#elif __has_include() -+ -+#include -+namespace ipfs { -+template -+using span = boost::span; -+} // namespace ipfs -+ -+#elif __has_include() -+ -+// Prior to Boost 1.78, span did not exist in core yet -+#include -+#include -+namespace ipfs { -+template -+class span : public boost::beast::span { -+ public: -+ span(Value* d, std::size_t n) : boost::beast::span{d, n} {} -+ -+ template -+ span(std::vector const& v) -+ : boost::beast::span{v.data(), v.size()} {} -+ -+ span subspan(std::size_t off) const { -+ return span{this->data() + off, this->size() - off}; -+ } -+ Value& operator[](std::size_t i) { return this->data()[i]; } -+}; -+} // namespace ipfs -+ -+#else -+ -+#error \ -+ "No good implementation of span available. Implement one, move to a newer C++, or provide Boost or Abseil." -+ -+#endif -+ -+#endif // IPFS_SPAN_H_ -diff --git a/third_party/ipfs_client/include/vocab/stringify.h b/third_party/ipfs_client/include/vocab/stringify.h -new file mode 100644 -index 0000000000000..8572ebfef7165 ---- /dev/null -+++ b/third_party/ipfs_client/include/vocab/stringify.h -@@ -0,0 +1,17 @@ -+#ifndef IPFS_STRINGIFY_H_ -+#define IPFS_STRINGIFY_H_ -+ -+#include -+ -+namespace ipfs { -+namespace { -+template -+std::string Stringify(T const& t) { -+ std::ostringstream oss; -+ oss << t; -+ return oss.str(); -+} -+} // namespace -+} // namespace ipfs -+ -+#endif // IPFS_STRINGIFY_H_ -diff --git a/third_party/ipfs_client/ipns_record.proto b/third_party/ipfs_client/ipns_record.proto -new file mode 100644 -index 0000000000000..6018931b7466f ---- /dev/null -+++ b/third_party/ipfs_client/ipns_record.proto -@@ -0,0 +1,38 @@ -+syntax = "proto2"; -+option optimize_for = LITE_RUNTIME; -+package ipfs.ipns; -+ -+message IpnsEntry { -+ enum ValidityType { -+ // setting an EOL says "this record is valid until..." -+ EOL = 0; -+ } -+ -+ // deserialized copy of data[value] -+ optional bytes value = 1; -+ -+ // legacy field, verify 'signatureV2' instead -+ optional bytes signatureV1 = 2; -+ -+ // deserialized copies of data[validityType] and data[validity] -+ optional ValidityType validityType = 3; -+ optional bytes validity = 4; -+ -+ // deserialized copy of data[sequence] -+ optional uint64 sequence = 5; -+ -+ // record TTL in nanoseconds, a deserialized copy of data[ttl] -+ optional uint64 ttl = 6; -+ -+ // in order for nodes to properly validate a record upon receipt, they need the public -+ // key associated with it. For old RSA keys, its easiest if we just send this as part of -+ // the record itself. For newer Ed25519 keys, the public key can be embedded in the -+ // IPNS Name itself, making this field unnecessary. -+ optional bytes pubKey = 7; -+ -+ // the signature of the IPNS record -+ optional bytes signatureV2 = 8; -+ -+ // extensible record data in DAG-CBOR format -+ optional bytes data = 9; -+} -diff --git a/third_party/ipfs_client/keys.proto b/third_party/ipfs_client/keys.proto -new file mode 100644 -index 0000000000000..a6f4f75ddba93 ---- /dev/null -+++ b/third_party/ipfs_client/keys.proto -@@ -0,0 +1,22 @@ -+syntax = "proto2"; -+option optimize_for = LITE_RUNTIME; -+package ipfs.ipns; -+ -+enum KeyType { -+ RSA = 0; -+ Ed25519 = 1; -+ Secp256k1 = 2; -+ ECDSA = 3; -+} -+ -+// PublicKey -+message PublicKey { -+ required KeyType Type = 1; -+ required bytes Data = 2; -+} -+ -+// PrivateKey -+message PrivateKey { -+ required KeyType Type = 1; -+ required bytes Data = 2; -+} -diff --git a/third_party/ipfs_client/pb_dag.proto b/third_party/ipfs_client/pb_dag.proto -new file mode 100644 -index 0000000000000..5cd027631c6de ---- /dev/null -+++ b/third_party/ipfs_client/pb_dag.proto -@@ -0,0 +1,23 @@ -+syntax = "proto2"; -+option optimize_for = LITE_RUNTIME; -+package ipfs.pb_dag; -+ -+message PBLink { -+ // binary CID (with no multibase prefix) of the target object -+ optional bytes Hash = 1; -+ -+ // UTF-8 string name -+ optional string Name = 2; -+ -+ // cumulative size of target object -+ optional uint64 Tsize = 3; -+} -+ -+message PBNode { -+ // refs to other objects -+ repeated PBLink Links = 2; -+ -+ // opaque user data -+ optional bytes Data = 1; -+} -+ -diff --git a/third_party/ipfs_client/src/ipfs_client/bases/b16_upper.h b/third_party/ipfs_client/src/ipfs_client/bases/b16_upper.h -new file mode 100644 -index 0000000000000..9d0056ecb98b9 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/bases/b16_upper.h -@@ -0,0 +1,23 @@ -+#ifndef IPFS_B16_UPPER_H_ -+#define IPFS_B16_UPPER_H_ -+ -+#include -+ -+namespace multibase { -+template <> -+struct traits<::multibase::encoding::base_16_upper> { -+ constexpr static const std::array charset = { -+ '0', '1', '2', '3', '4', '5', '6', '7', -+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; -+ constexpr static const char name[] = "BASE_16"; -+ using execution_style = multibase::algorithm::block_tag; -+ constexpr static const char padding = 0; -+}; -+} // namespace multibase -+ -+namespace ipfs::mb { -+using base_16_upper = -+ multibase::basic_algorithm; -+} // namespace ipfs::mb -+ -+#endif // IPFS_B16_UPPER_H_ -diff --git a/third_party/ipfs_client/src/ipfs_client/bases/b32.h b/third_party/ipfs_client/src/ipfs_client/bases/b32.h -new file mode 100644 -index 0000000000000..9dac14db53ac3 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/bases/b32.h -@@ -0,0 +1,35 @@ -+#ifndef IPFS_B32_UPPER_H_ -+#define IPFS_B32_UPPER_H_ -+ -+#include -+ -+namespace multibase { -+template <> -+struct traits<::multibase::encoding::base_32> { -+ constexpr static const std::array charset = { -+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', -+ 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', -+ 'w', 'x', 'y', 'z', '2', '3', '4', '5', '6', '7'}; -+ constexpr static const char name[] = "base_32"; -+ using execution_style = multibase::algorithm::block_tag; -+ constexpr static const char padding = 0; -+}; -+template <> -+struct traits<::multibase::encoding::base_32_upper> { -+ constexpr static const std::array charset = { -+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', -+ 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', -+ 'W', 'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7'}; -+ constexpr static const char name[] = "base_32_upper"; -+ using execution_style = multibase::algorithm::block_tag; -+ constexpr static const char padding = 0; -+}; -+} // namespace multibase -+ -+namespace ipfs::mb { -+using base_32 = multibase::basic_algorithm; -+using base_32_upper = -+ multibase::basic_algorithm; -+} // namespace ipfs::mb -+ -+#endif // IPFS_B32_UPPER_H_ -diff --git a/third_party/ipfs_client/src/ipfs_client/block_requestor.cc b/third_party/ipfs_client/src/ipfs_client/block_requestor.cc -new file mode 100644 -index 0000000000000..8a63e6f7ae0cc ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/block_requestor.cc -@@ -0,0 +1 @@ -+#include -diff --git a/third_party/ipfs_client/src/ipfs_client/car.cc b/third_party/ipfs_client/src/ipfs_client/car.cc -new file mode 100644 -index 0000000000000..e36442347415f ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/car.cc -@@ -0,0 +1,132 @@ -+#include "car.h" -+ -+#include -+ -+#include -+ -+#include "log_macros.h" -+ -+#include -+ -+using Self = ipfs::Car; -+using Byte = ipfs::Byte; -+using ByteView = ipfs::ByteView; -+using VarInt = libp2p::multi::UVarint; -+ -+namespace { -+short ReadHeader(ByteView&, ipfs::ContextApi const&); -+std::pair GetV1PayloadPos(ByteView); -+} // namespace -+ -+Self::Car(ByteView bytes, ContextApi const& api) { -+ auto after_header = bytes; -+ auto version = ReadHeader(after_header, api); -+ switch (version) { -+ case 0: -+ LOG(ERROR) << "Problem parsing CAR header."; -+ break; -+ case 1: -+ LOG(INFO) << "Reading CARv1"; -+ data_ = after_header; -+ break; -+ case 2: { -+ auto [off, siz] = GetV1PayloadPos(after_header); -+ LOG(INFO) << "CARv2 carries a payload of " << siz << "B @ " << off; -+ // TODO validate off and siz are sane, e.g. not pointing back into pragma -+ // or whatever -+ data_ = bytes.subspan(off, siz); -+ ReadHeader(data_, api); -+ break; -+ } -+ default: -+ LOG(ERROR) << "Unsupported CAR format version " << version; -+ } -+} -+auto Self::NextBlock() -> std::optional { -+ auto len = VarInt::create(data_); -+ if (!len) { -+ return std::nullopt; -+ } -+ data_ = data_.subspan(len->size()); -+ if (len->toUInt64() > data_.size()) { -+ LOG(ERROR) << "Length prefix claims cid+block is " << len->toUInt64() -+ << " bytes, but I only have " << data_.size() -+ << " bytes left in the CAR payload."; -+ data_ = {}; -+ return std::nullopt; -+ } -+ Block rv; -+ rv.bytes = data_.subspan(0U, len->toUInt64()); -+ data_ = data_.subspan(len->toUInt64()); -+ if (rv.cid.ReadStart(rv.bytes)) { -+ // TODO : check hash -+ return rv; -+ } -+ return std::nullopt; -+} -+ -+namespace { -+// https://ipld.io/specs/transport/car/carv2/ -+short ReadHeader(ByteView& bytes, ipfs::ContextApi const& api) { -+ auto header_len = VarInt::create(bytes); -+ if (!header_len || -+ header_len->toUInt64() + header_len->size() > bytes.size()) { -+ return 0; -+ } -+ bytes = bytes.subspan(header_len->size()); -+ auto header_bytes = bytes.subspan(0UL, header_len->toUInt64()); -+ auto header = api.ParseCbor(header_bytes); -+ if (!header) { -+ return 0; -+ } -+ auto version_node = header->at("version"); -+ if (!version_node) { -+ return 0; -+ } -+ auto version = version_node->as_unsigned(); -+ if (version) { -+ bytes = bytes.subspan(header_len->toUInt64()); -+ return version.value(); -+ } -+ return 0; -+} -+std::uint64_t read_le_u64(ByteView bytes, unsigned& off) { -+ auto b = bytes.subspan(off, off + 8); -+ off += 8U; -+ auto shift_in = [](std::uint64_t i, Byte y) { -+ return (i << 8) | static_cast(y); -+ }; -+ return std::accumulate(b.rbegin(), b.rend(), 0UL, shift_in); -+} -+std::pair GetV1PayloadPos(ByteView bytes) { -+ // Following the 11 byte pragma, the CARv2 [header] is a fixed-length sequence -+ // of 40 bytes, broken into the following sections: -+ if (bytes.size() < 40) { -+ return {}; -+ } -+ -+ // Characteristics: A 128-bit (16-byte) bitfield used to describe certain -+ // features of the enclosed data. -+ auto reading_off = 16U; -+ -+ // Data offset: A 64-bit (8-byte) unsigned -+ // little-endian integer indicating the byte-offset from the beginning of the -+ // CARv2 [pragma] to the first byte of the CARv1 data payload. -+ auto data_offset = read_le_u64(bytes, reading_off); -+ -+ // Data size: A 64-bit -+ // (8-byte) unsigned little-endian integer indicating the byte-length of the -+ // CARv1 data payload. -+ auto data_size = read_le_u64(bytes, reading_off); -+ -+ // Index offset: A 64-bit (8-byte) unsigned little-endian -+ // integer indicating the byte-offset from the beginning of the CARv2 to the -+ // first byte of the index payload. This value may be 0 to indicate the -+ // absence of index data. -+ reading_off += 8; // Ignoring index and therefore index offset -+ -+ assert(reading_off == 40UL); -+ -+ return {data_offset, data_size}; -+} -+} // namespace -\ No newline at end of file -diff --git a/third_party/ipfs_client/src/ipfs_client/car.h b/third_party/ipfs_client/src/ipfs_client/car.h -new file mode 100644 -index 0000000000000..619ac48ed8cd3 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/car.h -@@ -0,0 +1,26 @@ -+#ifndef IPFS_CAR_H_ -+#define IPFS_CAR_H_ -+ -+#include -+#include -+ -+#include -+#include -+ -+namespace ipfs { -+class ContextApi; -+class Car { -+ public: -+ Car(ByteView, ContextApi const&); -+ struct Block { -+ Cid cid; -+ ByteView bytes; -+ }; -+ std::optional NextBlock(); -+ -+ private: -+ ByteView data_; -+}; -+} // namespace ipfs -+ -+#endif // IPFS_CAR_H_ -diff --git a/third_party/ipfs_client/src/ipfs_client/cid.cc b/third_party/ipfs_client/src/ipfs_client/cid.cc -new file mode 100644 -index 0000000000000..b20686086bca6 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/cid.cc -@@ -0,0 +1,86 @@ -+#include -+ -+#include -+#include -+ -+#include "log_macros.h" -+ -+using Self = ipfs::Cid; -+using VarInt = libp2p::multi::UVarint; -+ -+Self::Cid(ipfs::MultiCodec cdc, ipfs::MultiHash hsh) -+ : codec_{cdc}, hash_{hsh} {} -+ -+Self::Cid(ipfs::ByteView bytes) { -+ ReadStart(bytes); -+} -+ -+Self::Cid(std::string_view s) { -+ if (s.size() == 46 && s[0] == 'Q' && s[1] == 'm') { -+ auto bytes = mb::Codec::Get(mb::Code::BASE58_BTC)->decode(s); -+ auto view = ByteView{bytes}; -+ ReadStart(view); -+ } else if (auto bytes = mb::decode(s)) { -+ if (bytes->size() > 4) { -+ auto view = ByteView{bytes.value()}; -+ ReadStart(view); -+ } -+ } else { -+ LOG(WARNING) << "Failed to decode the multibase for a CID: " << s; -+ } -+} -+ -+bool Self::ReadStart(ByteView& bytes) { -+ if (bytes.size() >= 34 && bytes[0] == ipfs::Byte{0x12} && -+ bytes[1] == ipfs::Byte{0x20}) { -+ hash_ = MultiHash{bytes}; -+ codec_ = hash_.valid() ? MultiCodec::DAG_PB : MultiCodec::INVALID; -+ bytes = bytes.subspan(34); -+ return true; -+ } -+ auto version = VarInt::create(bytes); -+ if (!version) { -+ return false; -+ } -+ if (version->toUInt64() != 1U) { -+ LOG(ERROR) << "CID version " << version->toUInt64() << " not supported."; -+ return false; -+ } -+ bytes = bytes.subspan(version->size()); -+ auto codec = VarInt::create(bytes); -+ if (!codec) { -+ return false; -+ } -+ auto cdc = static_cast(codec->toUInt64()); -+ codec_ = Validate(cdc); -+ bytes = bytes.subspan(codec->size()); -+ return hash_.ReadPrefix(bytes); -+} -+ -+bool Self::valid() const { -+ return codec_ != MultiCodec::INVALID && hash_.valid(); -+} -+ -+auto Self::hash() const -> ByteView { -+ return hash_.digest(); -+} -+auto Self::hash_type() const -> HashType { -+ return multi_hash().type(); -+} -+ -+std::string Self::to_string() const { -+ std::vector binary; -+ auto append_varint = [&binary](auto x) { -+ auto i = static_cast(x); -+ VarInt v{i}; -+ auto b = v.toBytes(); -+ binary.insert(binary.end(), b.begin(), b.end()); -+ }; -+ append_varint(1); // CID version 1 -+ append_varint(codec()); -+ append_varint(hash_type()); -+ append_varint(hash().size()); -+ auto h = hash(); -+ binary.insert(binary.end(), h.begin(), h.end()); -+ return mb::encode(mb::Code::BASE32_LOWER, binary); -+} -diff --git a/third_party/ipfs_client/src/ipfs_client/context_api.cc b/third_party/ipfs_client/src/ipfs_client/context_api.cc -new file mode 100644 -index 0000000000000..972e3df2fa320 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/context_api.cc -@@ -0,0 +1,21 @@ -+#include -+ -+#include "crypto/openssl_sha2_256.h" -+ -+using Self = ipfs::ContextApi; -+ -+Self::ContextApi() { -+#if HAS_OPENSSL_SHA -+ hashers_.emplace(HashType::SHA2_256, -+ std::make_unique()); -+#endif -+} -+ -+auto Self::Hash(HashType ht, ByteView data) -+ -> std::optional> { -+ auto it = hashers_.find(ht); -+ if (hashers_.end() == it || !(it->second)) { -+ return std::nullopt; -+ } -+ return it->second->hash(data); -+} -diff --git a/third_party/ipfs_client/src/ipfs_client/crypto/openssl_sha2_256.cc b/third_party/ipfs_client/src/ipfs_client/crypto/openssl_sha2_256.cc -new file mode 100644 -index 0000000000000..ff5c7a24d23bb ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/crypto/openssl_sha2_256.cc -@@ -0,0 +1,32 @@ -+#include "openssl_sha2_256.h" -+ -+using Self = ipfs::crypto::OpensslSha2_256; -+ -+#include "log_macros.h" -+ -+#if HAS_OPENSSL_SHA -+ -+#include -+ -+Self::~OpensslSha2_256() {} -+auto Self::hash(ipfs::ByteView data) -> std::optional> { -+ SHA256_CTX ctx; -+ if (1 != SHA256_Init(&ctx)) { -+ LOG(ERROR) << "Failed to initialize SHA256"; -+ return std::nullopt; -+ } -+ if (1 != SHA256_Update(&ctx, data.data(), data.size())) { -+ LOG(ERROR) << "Failure injesting data into SHA256."; -+ return {}; -+ } -+ std::vector rv(SHA256_DIGEST_LENGTH, Byte{}); -+ auto p = reinterpret_cast(rv.data()); -+ if (1 == SHA256_Final(p, &ctx)) { -+ return rv; -+ } else { -+ LOG(ERROR) << "Error calculating sha2-256 hash."; -+ return std::nullopt; -+ } -+} -+ -+#endif -diff --git a/third_party/ipfs_client/src/ipfs_client/crypto/openssl_sha2_256.h b/third_party/ipfs_client/src/ipfs_client/crypto/openssl_sha2_256.h -new file mode 100644 -index 0000000000000..c4e7bb975d366 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/crypto/openssl_sha2_256.h -@@ -0,0 +1,18 @@ -+#ifndef IPFS_OPENSSL_SHA2_256_H_ -+#define IPFS_OPENSSL_SHA2_256_H_ -+ -+#if __has_include() -+#define HAS_OPENSSL_SHA 1 -+#endif -+ -+#include -+ -+namespace ipfs::crypto { -+class OpensslSha2_256 final : public Hasher { -+ public: -+ ~OpensslSha2_256() noexcept override; -+ std::optional> hash(ByteView) override; -+}; -+} // namespace ipfs::crypto -+ -+#endif // IPFS_OPENSSL_SHA2_256_H_ -diff --git a/third_party/ipfs_client/src/ipfs_client/dag_cbor_value.cc b/third_party/ipfs_client/src/ipfs_client/dag_cbor_value.cc -new file mode 100644 -index 0000000000000..20a6fc713ad4e ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/dag_cbor_value.cc -@@ -0,0 +1,69 @@ -+#include -+ -+#include -+ -+#include "log_macros.h" -+ -+#include -+ -+using Self = ipfs::DagCborValue; -+ -+void Self::html(std::ostream& str) const { -+ if (auto u = as_unsigned()) { -+ str << "" << *u << "\n"; -+ } else if (auto si = as_signed()) { -+ str << "" << *si << "\n"; -+ } else if (auto fl = as_float()) { -+ str << "" << *si << "\n"; -+ } else if (auto s = as_string()) { -+ str << "

""; -+ for (auto c : *s) { -+ str << html_escape(c); -+ } -+ str << ""

\n"; -+ } else if (auto cid = as_link()) { -+ auto cs = cid.value().to_string(); -+ if (cs.size()) { -+ str << "" << cs -+ << "\n"; -+ } else { -+ str << "\n"; -+ } -+ } else if (auto bin = as_bytes()) { -+ str << "

0x"; -+ for (auto b : *bin) { -+ str << ' ' << std::hex << std::setw(2) << std::setfill('0') -+ << static_cast(b); -+ } -+ str << "

\n"; -+ } else if (is_array()) { -+ str << "
    \n"; -+ iterate_array([&str](auto& v) { -+ str << "
  1. \n"; -+ v.html(str); -+ str << "
  2. \n"; -+ }); -+ str << "
\n"; -+ } else if (is_map()) { -+ str << "\n"; -+ iterate_map([&str](auto k, auto& v) { -+ str << " \n"; -+ }); -+ str << "
" << k << "\n"; -+ v.html(str); -+ str << "
\n"; -+ } else if (auto bul = as_bool()) { -+ auto val = (bul.value() ? "True" : "False"); -+ str << " " << val << "\n"; -+ } else { -+ str << "\n"; -+ } -+} -+ -+std::string Self::html() const { -+ std::ostringstream oss; -+ oss << "DAG-CBOR Preview\n"; -+ html(oss); -+ oss << ""; -+ return oss.str(); -+} -diff --git a/third_party/ipfs_client/src/ipfs_client/dag_json_value.cc b/third_party/ipfs_client/src/ipfs_client/dag_json_value.cc -new file mode 100644 -index 0000000000000..12a493cbd92cb ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/dag_json_value.cc -@@ -0,0 +1,22 @@ -+#include -+ -+#include -+ -+using Self = ipfs::DagJsonValue; -+ -+Self::~DagJsonValue() noexcept {} -+auto Self::get_if_link() const -> std::optional { -+ auto slash = (*this)["/"]; -+ if (!slash) { -+ return std::nullopt; -+ } -+ auto str = slash->get_if_string(); -+ if (!str) { -+ return std::nullopt; -+ } -+ auto cid = Cid(*str); -+ if (cid.valid()) { -+ return cid; -+ } -+ return std::nullopt; -+} -\ No newline at end of file -diff --git a/third_party/ipfs_client/src/ipfs_client/gateways.cc b/third_party/ipfs_client/src/ipfs_client/gateways.cc -new file mode 100644 -index 0000000000000..fe49949b10537 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/gateways.cc -@@ -0,0 +1,121 @@ -+#include -+ -+#include -+ -+#include "log_macros.h" -+ -+#include -+#include -+#include -+ -+using namespace std::string_literals; -+ -+ipfs::Gateways::Gateways() -+ : random_engine_{std::random_device{}()}, dist_{0.01} { -+ auto gws = DefaultGateways(); -+ for (auto [k, v] : gws) { -+ known_gateways_[k] = v; -+ } -+} -+ipfs::Gateways::~Gateways() {} -+ -+auto ipfs::Gateways::GenerateList() -> GatewayList { -+ GatewayList result; -+ for (auto [k, v] : known_gateways_) { -+ result.push_back({k, v + dist_(random_engine_)}); -+ } -+ std::sort(result.begin(), result.end()); -+ return result; -+} -+ -+void ipfs::Gateways::promote(std::string const& key) { -+ auto it = known_gateways_.find(key); -+ if (known_gateways_.end() == it) { -+ LOG(ERROR) << "Can't promote (" << key -+ << ") because I don't know that one."; -+ } else { -+ auto l = known_gateways_.at(key)++; -+ if (l % (++up_log_ / 2) <= 9) { -+ LOG(INFO) << "Promote(" << key << ")"; -+ } -+ } -+} -+void ipfs::Gateways::demote(std::string const& key) { -+ auto it = known_gateways_.find(key); -+ if (known_gateways_.end() == it) { -+ VLOG(2) << "Can't demote " << key << " as I don't have that gateway."; -+ } else if (it->second) { -+ if (it->second-- % 3 == 0) { -+ LOG(INFO) << "Demote(" << key << ") to " << it->second; -+ } -+ } else { -+ LOG(INFO) << "Demoted(" << key << ") for the last time - dropping."; -+ known_gateways_.erase(it); -+ } -+} -+ -+void ipfs::Gateways::AddGateways(std::vector v) { -+ LOG(INFO) << "AddGateways(" << v.size() << ')'; -+ for (auto& ip : v) { -+ if (ip.empty()) { -+ LOG(ERROR) << "ERROR: Attempted to add empty string as gateway!"; -+ continue; -+ } -+ std::string prefix; -+ if (ip.find("://") == std::string::npos) { -+ prefix = "http://"; -+ prefix.append(ip); -+ } else { -+ prefix = ip; -+ } -+ if (prefix.back() != '/') { -+ prefix.push_back('/'); -+ } -+ if (known_gateways_.insert({prefix, 99}).second) { -+ VLOG(1) << "Adding discovered gateway " << prefix; -+ } -+ } -+} -+ -+auto ipfs::Gateways::DefaultGateways() -> GatewayList { -+ auto* ovr = std::getenv("IPFS_GATEWAY"); -+ if (ovr && *ovr) { -+ std::istringstream user_override{ovr}; -+ GatewayList result; -+ std::string gw; -+ while (user_override >> gw) { -+ if ( gw.empty() ) { -+ continue; -+ } -+ if ( gw.back() != '/' ) { -+ gw.push_back('/'); -+ } -+ result.push_back( {gw, 0} ); -+ } -+ auto N = static_cast(result.size()); -+ for (auto i = 0; i < N; ++i) { -+ auto& r = result[i]; -+ r.strength = N - i; -+ LOG(INFO) << "User-specified gateway: " << r.prefix << '=' << r.strength; -+ } -+ return result; -+ } -+ return {{"http://localhost:8080/"s, 929}, -+ {"https://jcsl.hopto.org/"s, 863}, -+ {"https://human.mypinata.cloud/"s, 798}, -+ {"https://ipfs.io/"s, 753}, -+ {"https://gateway.ipfs.io/"s, 678}, -+ {"https://dweb.link/"s, 598}, -+ {"https://gateway.pinata.cloud/"s, 519}, -+ {"https://ipfs.joaoleitao.org/"s, 434}, -+ {"https://ipfs.runfission.com/"s, 371}, -+ {"https://nftstorage.link/"s, 307}, -+ {"https://w3s.link/"s, 243}, -+ {"https://ipfs.fleek.co/"s, 203}, -+ {"https://ipfs.jpu.jp/"s, 162}, -+ {"https://permaweb.eu.org/"s, 121}, -+ {"https://jorropo.net/"s, 76}, -+ {"https://hardbin.com/"s, 39}, -+ {"https://ipfs.soul-network.com/"s, 1}, -+ {"https://storry.tv/"s, 0}}; -+} -diff --git a/third_party/ipfs_client/src/ipfs_client/generated_directory_listing.cc b/third_party/ipfs_client/src/ipfs_client/generated_directory_listing.cc -new file mode 100644 -index 0000000000000..5a7c4fce733d6 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/generated_directory_listing.cc -@@ -0,0 +1,45 @@ -+#include "generated_directory_listing.h" -+ -+#include "log_macros.h" -+ -+ipfs::GeneratedDirectoryListing::GeneratedDirectoryListing( -+ std::string_view base_path) -+ : html_("\n "), base_path_(base_path) { -+ if (base_path.empty() || base_path[0] != '/') { -+ base_path_.insert(0UL, 1UL, '/'); -+ } -+ if (base_path_.back() != '/') { -+ base_path_.push_back('/'); -+ } -+ html_.append(base_path_) -+ .append(" (directory listing)\n") -+ .append(" \n") -+ .append("
    \n"); -+ if (base_path.find_first_not_of("/") < base_path.size()) { -+ std::string_view dotdotpath{base_path_}; -+ dotdotpath.remove_suffix(1); // Remove that trailing / -+ auto last_slash = dotdotpath.find_last_of("/"); -+ dotdotpath = dotdotpath.substr(0, last_slash + 1UL); -+ AddLink("..", dotdotpath); -+ } -+} -+ -+void ipfs::GeneratedDirectoryListing::AddEntry(std::string_view name) { -+ auto path = base_path_; -+ path.append(name); -+ AddLink(name, path); -+} -+void ipfs::GeneratedDirectoryListing::AddLink(std::string_view name, -+ std::string_view path) { -+ html_.append("
  • \n") -+ .append(" ") -+ .append(name) -+ .append("\n") -+ .append("
  • \n"); -+} -+ -+std::string const& ipfs::GeneratedDirectoryListing::Finish() { -+ return html_.append("
\n").append(" \n").append("\n"); -+} -\ No newline at end of file -diff --git a/third_party/ipfs_client/src/ipfs_client/generated_directory_listing.h b/third_party/ipfs_client/src/ipfs_client/generated_directory_listing.h -new file mode 100644 -index 0000000000000..8daa0ec01cb9e ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/generated_directory_listing.h -@@ -0,0 +1,41 @@ -+#ifndef IPFS_GENERATED_DIRECTORY_LISTING_H_ -+#define IPFS_GENERATED_DIRECTORY_LISTING_H_ -+ -+#include -+#include -+ -+namespace ipfs { -+ -+/*! -+ * \brief An index.html listing out a directory node's content -+ */ -+class GeneratedDirectoryListing { -+ public: -+ -+ /*! -+ * \brief Get the HTML preamble going -+ * \param base_path - The path _to_ this directory -+ */ -+ GeneratedDirectoryListing(std::string_view base_path); -+ -+ /*! -+ * \brief Add an entry to the list -+ * \param name - The directory's way of referring to that CID -+ */ -+ void AddEntry(std::string_view name); -+ -+ /*! -+ * \brief Finish up all the HTML stuff at the end. -+ * \return The generated HTML -+ */ -+ std::string const& Finish(); -+ -+ private: -+ std::string html_; -+ std::string base_path_; -+ -+ void AddLink(std::string_view name, std::string_view path); -+}; -+} // namespace ipfs -+ -+#endif // IPFS_GENERATED_DIRECTORY_LISTING_H_ -diff --git a/third_party/ipfs_client/src/ipfs_client/gw/block_request_splitter.cc b/third_party/ipfs_client/src/ipfs_client/gw/block_request_splitter.cc -new file mode 100644 -index 0000000000000..3ded788f6bdf1 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/gw/block_request_splitter.cc -@@ -0,0 +1,30 @@ -+#include -+ -+#include -+ -+using Self = ipfs::gw::BlockRequestSplitter; -+ -+std::string_view Self::name() const { -+ return "BlockRequestSplitter"; -+} -+auto Self::handle(ipfs::gw::RequestPtr r) -> HandleOutcome { -+ if (r->type != Type::Car) { -+ return HandleOutcome::NOT_HANDLED; -+ } -+ { -+ auto br = std::make_shared(*r); -+ br->type = Type::Block; -+ br->path.clear(); -+ forward(br); -+ } -+ /* -+ { -+ auto pr = std::make_shared(*r); -+ pr->type = Type::Providers; -+ pr->path.clear(); -+ pr->affinity.clear(); -+ forward(pr); -+ } -+ */ -+ return HandleOutcome::NOT_HANDLED; -+} -\ No newline at end of file -diff --git a/third_party/ipfs_client/src/ipfs_client/gw/default_requestor.cc b/third_party/ipfs_client/src/ipfs_client/gw/default_requestor.cc -new file mode 100644 -index 0000000000000..46dbf88033b7c ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/gw/default_requestor.cc -@@ -0,0 +1,30 @@ -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+auto ipfs::gw::default_requestor(ipfs::GatewayList gws, -+ std::shared_ptr early, -+ std::shared_ptr api) -+ -> std::shared_ptr { -+ auto result = std::make_shared(); -+ result->or_else(std::make_shared()); -+ if (early) { -+ result->or_else(early); -+ early->api(api); -+ } -+ auto pool = std::make_shared(); -+ result->or_else(std::make_shared(api)) -+ .or_else(pool) -+ .or_else(std::make_shared()); -+ for (auto& gw : gws) { -+ auto gwr = -+ std::make_shared(gw.prefix, gw.strength, api); -+ pool->add(gwr); -+ } -+ return result; -+} -diff --git a/third_party/ipfs_client/src/ipfs_client/gw/dnslink_requestor.cc b/third_party/ipfs_client/src/ipfs_client/gw/dnslink_requestor.cc -new file mode 100644 -index 0000000000000..731b750ffd43a ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/gw/dnslink_requestor.cc -@@ -0,0 +1,69 @@ -+#include -+ -+#include "ipfs_client/ipld/ipns_name.h" -+ -+#include -+#include -+ -+#include -+#include -+#include -+ -+#include "log_macros.h" -+ -+#include -+ -+using Self = ipfs::gw::DnsLinkRequestor; -+using namespace std::literals; -+ -+Self::DnsLinkRequestor(std::shared_ptr api) { -+ api_ = api; -+} -+std::string_view Self::name() const { -+ return "DNSLink requestor"; -+} -+namespace { -+bool parse_results(ipfs::gw::RequestPtr req, -+ std::vector const& results, -+ std::shared_ptr const&); -+} -+auto Self::handle(ipfs::gw::RequestPtr req) -> HandleOutcome { -+ if (req->type != Type::DnsLink) { -+ return HandleOutcome::NOT_HANDLED; -+ } -+ // std::function requires target be copy-constructible -+ auto success = std::make_shared(); -+ *success = false; -+ auto a = api_; -+ auto res = [req, success, a](std::vector const& results) { -+ *success = *success || parse_results(req, results, a); -+ }; -+ auto don = [success, req]() { -+ LOG(INFO) << "DNSLink request completed for " << req->main_param -+ << " success=" << *success; -+ if (!*success) { -+ req->dependent->finish(ipfs::Response::HOST_NOT_FOUND); -+ } -+ }; -+ api_->SendDnsTextRequest("_dnslink." + req->main_param, res, std::move(don)); -+ return HandleOutcome::PENDING; -+} -+namespace { -+bool parse_results(ipfs::gw::RequestPtr req, -+ std::vector const& results, -+ std::shared_ptr const& api) { -+ constexpr auto prefix = "dnslink="sv; -+ LOG(INFO) << "Scanning " << results.size() << " DNS TXT records for " -+ << req->main_param << " looking for dnslink..."; -+ for (auto& result : results) { -+ if (starts_with(result, prefix)) { -+ LOG(INFO) << "DNSLink result=" << result; -+ req->RespondSuccessfully(result.substr(prefix.size()), api); -+ return true; -+ } else { -+ LOG(INFO) << "Irrelevant TXT result, ignored: " << result; -+ } -+ } -+ return false; -+} -+} // namespace -diff --git a/third_party/ipfs_client/src/ipfs_client/gw/gateway_http_requestor.cc b/third_party/ipfs_client/src/ipfs_client/gw/gateway_http_requestor.cc -new file mode 100644 -index 0000000000000..94ab27fc14228 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/gw/gateway_http_requestor.cc -@@ -0,0 +1,141 @@ -+#include "gateway_http_requestor.h" -+ -+#include -+#include -+ -+#include -+#include -+#include -+ -+#include "log_macros.h" -+ -+using Self = ipfs::gw::GatewayHttpRequestor; -+using ReqTyp = ipfs::gw::Type; -+ -+std::string_view Self::name() const { -+ return "simplistic HTTP requestor"; -+} -+auto Self::handle(ipfs::gw::RequestPtr r) -> HandleOutcome { -+ DCHECK(r); -+ DCHECK(r->dependent); -+ DCHECK_GT(prefix_.size(), 0UL); -+ if (!r->is_http()) { -+ LOG(ERROR) << name() << " only handles HTTP requests"; -+ return HandleOutcome::NOT_HANDLED; -+ } -+ auto req_key = r->url_suffix().append(r->accept()); -+ if (seen_[req_key] > 0xFD) { -+ return HandleOutcome::NOT_HANDLED; -+ } -+ if (target(*r) <= r->parallel + pending_ + seen_[req_key]) { -+ return HandleOutcome::MAYBE_LATER; -+ } -+ auto desc = r->describe_http(); -+ if (!desc.has_value() || desc.value().url.empty()) { -+ LOG(ERROR) -+ << r->debug_string() -+ << " is HTTP but can't describe the HTTP request that would happen?"; -+ return HandleOutcome::NOT_HANDLED; -+ } -+ if (prefix_.back() == '/' && desc.value().url[0] == '/') { -+ desc.value().url.insert(0, prefix_, 0UL, prefix_.size() - 1UL); -+ } else { -+ desc.value().url.insert(0, prefix_); -+ } -+ desc.value().timeout_seconds += extra_seconds_; -+ auto cb = [this, r, desc, req_key](std::int16_t status, std::string_view body, -+ ContextApi::HeaderAccess ha) { -+ if (r->parallel) { -+ r->parallel--; -+ } -+ if (pending_) { -+ pending_--; -+ } -+ if (r->type == Type::Zombie) { -+ return; -+ } else if (status == 408 || status == 504) { -+ // Timeouts -+ extra_seconds_++; -+ forward(r); -+ return; -+ } else if (status / 100 == 2) { -+ auto ct = ha("content-type"); -+ std::transform(ct.begin(), ct.end(), ct.begin(), ::tolower); -+ if (ct.empty()) { -+ LOG(ERROR) << "No content-type header?"; -+ } -+ if (ct.size() && desc->accept.size() && -+ ct.find(desc->accept) == std::string::npos) { -+ LOG(WARNING) << "Requested with Accept: " << desc->accept -+ << " but received response with content-type: " << ct; -+ LOG(INFO) << "Demote(" << prefix_ << ')'; -+ } else if (!r->RespondSuccessfully(body, api_)) { -+ LOG(ERROR) << "Got an unuseful response from " << prefix_ -+ << " forwarding request " << r->debug_string() -+ << " to next requestor."; -+ } else { -+ // Good cases -+ if (typ_good_.insert(r->type).second) { -+ VLOG(1) << prefix_ << " OK with requests of type " -+ << static_cast(r->type); -+ } else if (typ_bad_.erase(r->type)) { -+ VLOG(1) << prefix_ << " truly OK with requests of type " -+ << static_cast(r->type); -+ } -+ if (aff_good_.insert(r->affinity).second) { -+ VLOG(1) << prefix_ << " likes requests in the neighborhood of " -+ << r->affinity; -+ } else if (aff_bad_.erase(r->affinity)) { -+ VLOG(1) << prefix_ << " truly OK with affinity " << r->affinity; -+ } -+ VLOG(2) << prefix_ << " had a success on " << r->debug_string(); -+ LOG(INFO) << "Promote(" << prefix_ << ')'; -+ ++strength_; -+ return; -+ } -+ } else if (status / 100 == 4) { -+ seen_[req_key] += 9; -+ } -+ seen_[req_key] += 9; -+ LOG(INFO) << "Demote(" << prefix_ << ')'; -+ if (strength_ > 0) { -+ --strength_; -+ } -+ aff_bad_.insert(r->affinity); -+ typ_bad_.insert(r->type); -+ forward(r); -+ }; -+ DCHECK(api_); -+ api_->SendHttpRequest(desc.value(), cb); -+ seen_[req_key]++; -+ pending_++; -+ return HandleOutcome::PENDING; -+} -+ -+Self::GatewayHttpRequestor(std::string gateway_prefix, -+ int strength, -+ std::shared_ptr api) -+ : prefix_{gateway_prefix}, strength_{strength} { -+ api_ = api; -+} -+Self::~GatewayHttpRequestor() {} -+ -+int Self::target(GatewayRequest const& r) const { -+ int result = (strength_ - pending_) / 2; -+ if (!pending_) { -+ ++result; -+ } -+ if (typ_good_.count(r.type)) { -+ result += 3; -+ } -+ if (!typ_bad_.count(r.type)) { -+ result += 2; -+ } -+ if (aff_good_.count(r.affinity)) { -+ result += 5; -+ } -+ if (aff_bad_.count(r.affinity) == 0UL) { -+ result += 4; -+ } -+ return result; -+} -diff --git a/third_party/ipfs_client/src/ipfs_client/gw/gateway_http_requestor.h b/third_party/ipfs_client/src/ipfs_client/gw/gateway_http_requestor.h -new file mode 100644 -index 0000000000000..8c61bef879db8 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/gw/gateway_http_requestor.h -@@ -0,0 +1,34 @@ -+#ifndef IPFS_GATEWAY_HTTP_REQUESTOR_H_ -+#define IPFS_GATEWAY_HTTP_REQUESTOR_H_ -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+namespace ipfs::gw { -+class GatewayHttpRequestor final : public Requestor { -+ std::string prefix_; -+ int strength_; -+ std::unordered_map seen_; -+ std::set aff_good_, aff_bad_; -+ std::set typ_good_, typ_bad_; -+ int pending_ = 0; -+ int extra_seconds_ = 0; -+ -+ HandleOutcome handle(RequestPtr) override; -+ std::string_view name() const override; -+ int target(GatewayRequest const&) const; -+ -+ public: -+ GatewayHttpRequestor(std::string gateway_prefix, -+ int strength, -+ std::shared_ptr); -+ ~GatewayHttpRequestor() noexcept override; -+}; -+} // namespace ipfs::gw -+ -+#endif // IPFS_GATEWAY_HTTP_REQUESTOR_H_ -diff --git a/third_party/ipfs_client/src/ipfs_client/gw/gateway_request.cc b/third_party/ipfs_client/src/ipfs_client/gw/gateway_request.cc -new file mode 100644 -index 0000000000000..1b251b1fc57a8 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/gw/gateway_request.cc -@@ -0,0 +1,305 @@ -+#include -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "log_macros.h" -+ -+#include -+#include -+ -+using namespace std::literals; -+ -+using Self = ipfs::gw::GatewayRequest; -+ -+std::shared_ptr Self::fromIpfsPath(ipfs::SlashDelimited p) { -+ auto name_space = p.pop(); -+ auto r = std::make_shared(); -+ r->main_param = p.pop(); -+ Cid cid(r->main_param); -+ if (cid.valid()) { -+ r->cid = std::move(cid); -+ } else { -+ r->cid = std::nullopt; -+ } -+ if (name_space == "ipfs") { -+ if (!r->cid.has_value()) { -+ LOG(ERROR) << "IPFS request with invalid/unsupported CID " -+ << r->main_param; -+ return {}; -+ } -+ if (r->cid.value().hash_type() == HashType::IDENTITY) { -+ r->type = Type::Identity; -+ } else { -+ r->path = p.pop_all(); -+ r->type = r->path.empty() ? Type::Block : Type::Car; -+ } -+ } else if (name_space == "ipns") { -+ r->path = p.pop_all(); -+ if (Cid(r->main_param).valid()) { -+ r->type = Type::Ipns; -+ } else { -+ r->type = Type::DnsLink; -+ } -+ } else { -+ LOG(FATAL) << "Unsupported namespace in ipfs path: /" << name_space << '/' -+ << p.pop_all(); -+ } -+ return r; -+} -+ -+std::string Self::url_suffix() const { -+ switch (type) { -+ case Type::Block: -+ return "/ipfs/" + main_param; -+ case Type::Car: -+ return "/ipfs/" + main_param + "/" + path + "?dag-scope=entity"; -+ case Type::Ipns: -+ return "/ipns/" + main_param; -+ case Type::Providers: -+ return "/routing/v1/providers/" + main_param; -+ case Type::DnsLink: -+ LOG(FATAL) << "Don't try to use HTTP(s) for DNS TXT records."; -+ return {}; -+ case Type::Identity: -+ case Type::Zombie: -+ return {}; -+ } -+ LOG(FATAL) << "Unhandled gateway request type: " << static_cast(type); -+ return {}; -+} -+std::string_view Self::accept() const { -+ switch (type) { -+ case Type::Block: -+ return "application/vnd.ipld.raw"sv; -+ case Type::Ipns: -+ return "application/vnd.ipfs.ipns-record"sv; -+ case Type::Car: -+ return "application/vnd.ipld.car"sv; -+ case Type::Providers: -+ return "application/json"sv; -+ case Type::DnsLink: -+ // TODO : not sure this advice is 100% good, actually. -+ // If the user's system setup allows for text records to actually work, -+ // it would be good to respect their autonomy and try to follow the -+ // system's DNS setup. However, it's extremely easy to get yourself in a -+ // situation where Chromium _cannot_ access text records. If you're in -+ // that scenario, it might be better to try to use an IPFS gateway with -+ // DNSLink capability. -+ LOG(FATAL) << "Don't try to use HTTP(s) for DNS TXT records."; -+ return {}; -+ case Type::Identity: -+ case Type::Zombie: -+ return {}; -+ } -+ LOG(FATAL) << "Invalid gateway request type: " << static_cast(type); -+ return {}; -+} -+short Self::timeout_seconds() const { -+ switch (type) { -+ case Type::DnsLink: -+ return 16; -+ case Type::Block: -+ return 39; -+ case Type::Providers: -+ return 64; -+ case Type::Car: -+ return 128; -+ case Type::Ipns: -+ return 256; -+ case Type::Identity: -+ case Type::Zombie: -+ return 0; -+ } -+ LOG(FATAL) << "timeout_seconds() called for unsupported gateway request type " -+ << static_cast(type); -+ return 0; -+} -+ -+auto Self::identity_data() const -> std::string_view { -+ if (type != Type::Identity) { -+ return ""; -+ } -+ auto hash = cid.value().hash(); -+ auto d = reinterpret_cast(hash.data()); -+ return std::string_view{d, hash.size()}; -+} -+ -+bool Self::is_http() const { -+ return type != Type::DnsLink && type != Type::Identity; -+} -+auto Self::describe_http() const -> std::optional { -+ if (!is_http()) { -+ return {}; -+ } -+ return HttpRequestDescription{url_suffix(), timeout_seconds(), -+ std::string{accept()}, max_response_size()}; -+} -+std::optional Self::max_response_size() const { -+ switch (type) { -+ case Type::Identity: -+ return 0; -+ case Type::DnsLink: -+ return std::nullopt; -+ case Type::Ipns: -+ return MAX_IPNS_PB_SERIALIZED_SIZE; -+ case Type::Block: -+ return BLOCK_RESPONSE_BUFFER_SIZE; -+ case Type::Car: { -+ // There could be an unlimited number of blocks in the CAR -+ // The _floor_ is the number of path components. -+ // But one path component could be a HAMT sharded directory that we may -+ // need to pass through several layers on. -+ // And the final path component could be a UnixFS file with an unlimited -+ // number of blocks in it. -+ return std::nullopt; -+ } -+ case Type::Zombie: -+ return 0; -+ case Type::Providers: -+ // This one's tricky. -+ // One could easily guess a pracitical limit to the size of a Peer, -+ // and the spec says it SHOULD be limited to 100 peers. -+ // But there's no guaranteed limits. A peer could have an unlimited -+ // number of multiaddrs. And they're allowed to throw in arbitrary -+ // fields I'm supposed to ignore. So in theory it could be infinitely -+ // large. -+ return std::nullopt; -+ } -+ LOG(ERROR) << "Invalid gateway request type " << static_cast(type); -+ return std::nullopt; -+} -+std::string_view ipfs::gw::name(ipfs::gw::Type t) { -+ using ipfs::gw::Type; -+ switch (t) { -+ case Type::Block: -+ return "Block"; -+ case Type::Car: -+ return "Car"; -+ case Type::Ipns: -+ return "Ipns"; -+ case Type::DnsLink: -+ return "DnsLink"; -+ case Type::Providers: -+ return "Providers"; -+ case Type::Identity: -+ return "Identity"; -+ case Type::Zombie: -+ return "CompletedRequest"; -+ } -+ static std::array buf; -+ std::sprintf(buf.data(), "InvalidType %d", static_cast(t)); -+ return buf.data(); -+} -+std::string Self::debug_string() const { -+ std::ostringstream oss; -+ oss << "Request{Type=" << type << ' ' << main_param; -+ if (!path.empty()) { -+ oss << ' ' << path; -+ } -+ if (dependent) { -+ oss << " for=" << dependent->path().to_string(); -+ } -+ oss << " plel=" << parallel << '}'; -+ return oss.str(); -+} -+bool Self::RespondSuccessfully(std::string_view bytes, -+ std::shared_ptr const& api) { -+ using namespace ipfs::ipld; -+ bool success = false; -+ switch (type) { -+ case Type::Block: { -+ DCHECK(cid.has_value()); -+ if (!cid.has_value()) { -+ LOG(ERROR) << "Your CID doesn't even have a value!"; -+ return false; -+ } -+ DCHECK(api); -+ auto node = DagNode::fromBytes(api, cid.value(), bytes); -+ success = orchestrator_->add_node(main_param, node); -+ } break; -+ case Type::Identity: -+ success = orchestrator_->add_node( -+ main_param, std::make_shared(std::string{bytes})); -+ break; -+ case Type::Ipns: -+ if (cid.has_value()) { -+ DCHECK(api); -+ auto byte_ptr = reinterpret_cast(bytes.data()); -+ auto rec = ipfs::ValidateIpnsRecord({byte_ptr, bytes.size()}, -+ cid.value(), *api); -+ if (rec.has_value()) { -+ auto node = DagNode::fromIpnsRecord(rec.value()); -+ success = orchestrator_->add_node(main_param, node); -+ } else { -+ LOG(ERROR) << "IPNS record failed to validate!"; -+ return false; -+ } -+ } -+ break; -+ case Type::DnsLink: -+ LOG(INFO) << "Resolved " << debug_string() << " to " << bytes; -+ if (orchestrator_) { -+ success = orchestrator_->add_node( -+ main_param, std::make_shared(bytes)); -+ } else { -+ LOG(FATAL) << "I have no orchestrator!!"; -+ } -+ break; -+ case Type::Car: { -+ DCHECK(api); -+ Car car(as_bytes(bytes), *api); -+ auto added = 0; -+ while (auto block = car.NextBlock()) { -+ auto cid_s = block->cid.to_string(); -+ auto n = DagNode::fromBytes(api, block->cid, block->bytes); -+ if (!n) { -+ LOG(ERROR) << "Unable to handle block from CAR: " << cid_s; -+ } else if (orchestrator_->add_node(cid_s, n)) { -+ ++added; -+ } else { -+ LOG(INFO) << "Did not add node from CAR: " << cid_s; -+ } -+ } -+ LOG(INFO) << "Added " << added << " nodes from a CAR."; -+ success = added > 0; -+ break; -+ } -+ case Type::Providers: -+ LOG(WARNING) << "TODO - handle responses to providers requests."; -+ break; -+ case Type::Zombie: -+ LOG(WARNING) << "Responding to a zombie is ill-advised."; -+ break; -+ default: -+ LOG(ERROR) << "TODO " << static_cast(type); -+ } -+ if (success) { -+ for (auto& hook : bytes_received_hooks) { -+ hook(bytes); -+ } -+ bytes_received_hooks.clear(); -+ orchestrator_->build_response(dependent); -+ } -+ return success; -+} -+void Self::Hook(std::function f) { -+ bytes_received_hooks.push_back(f); -+} -+void Self::orchestrator(std::shared_ptr const& orc) { -+ orchestrator_ = orc; -+} -+bool Self::PartiallyRedundant() const { -+ if (!orchestrator_) { -+ return false; -+ } -+ return orchestrator_->has_key(main_param); -+} -diff --git a/third_party/ipfs_client/src/ipfs_client/gw/inline_request_handler.cc b/third_party/ipfs_client/src/ipfs_client/gw/inline_request_handler.cc -new file mode 100644 -index 0000000000000..435142a1a74ee ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/gw/inline_request_handler.cc -@@ -0,0 +1,23 @@ -+#include -+ -+#include -+#include -+#include -+ -+#include "log_macros.h" -+ -+using Self = ipfs::gw::InlineRequestHandler; -+ -+std::string_view Self::name() const { -+ return "InlineRequestHandler"; -+} -+auto Self::handle(ipfs::gw::RequestPtr req) -> HandleOutcome { -+ if (req->type != gw::Type::Identity) { -+ VLOG(2) << ipfs::gw::name(req->type); -+ return HandleOutcome::NOT_HANDLED; -+ } -+ std::string data{req->identity_data()}; -+ LOG(INFO) << "Responding to inline CID without using network."; -+ req->RespondSuccessfully(data, api_); -+ return HandleOutcome::DONE; -+} -diff --git a/third_party/ipfs_client/src/ipfs_client/gw/requestor.cc b/third_party/ipfs_client/src/ipfs_client/gw/requestor.cc -new file mode 100644 -index 0000000000000..72e6746c7a807 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/gw/requestor.cc -@@ -0,0 +1,70 @@ -+#include -+ -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include "log_macros.h" -+ -+using Self = ipfs::gw::Requestor; -+using ReqPtr = std::shared_ptr; -+ -+Self& Self::or_else(std::shared_ptr p) { -+ if (next_) { -+ next_->or_else(p); -+ } else { -+ VLOG(2) << name() << " is followed by " << p->name(); -+ next_ = p; -+ } -+ if (api_ && !p->api_) { -+ VLOG(1) << name() << " granting context to " << p->name(); -+ p->api_ = api_; -+ } -+ return *this; -+} -+ -+void Self::request(ReqPtr req) { -+ if (!req || req->type == Type::Zombie) { -+ return; -+ } -+ switch (handle(req)) { -+ case HandleOutcome::MAYBE_LATER: -+ // TODO -+ forward(req); -+ break; -+ case HandleOutcome::PARALLEL: -+ case HandleOutcome::NOT_HANDLED: -+ if (next_) { -+ next_->request(req); -+ } else { -+ LOG(ERROR) << "Ran out of Requestors in the chain while looking for " -+ "one that can handle " -+ << req->debug_string(); -+ definitive_failure(req); -+ } -+ break; -+ case HandleOutcome::PENDING: -+ break; -+ case HandleOutcome::DONE: -+ VLOG(2) << req->debug_string() << " finished synchronously: " << name(); -+ break; -+ } -+} -+void Self::definitive_failure(ipfs::gw::RequestPtr r) const { -+ DCHECK(r); -+ DCHECK(r->dependent); -+ r->dependent->finish(Response::PLAIN_NOT_FOUND); -+} -+ -+void Self::forward(ipfs::gw::RequestPtr req) const { -+ if (next_) { -+ next_->request(req); -+ } -+} -+void Self::api(std::shared_ptr a) { -+ api_ = a; -+} -\ No newline at end of file -diff --git a/third_party/ipfs_client/src/ipfs_client/gw/requestor_pool.cc b/third_party/ipfs_client/src/ipfs_client/gw/requestor_pool.cc -new file mode 100644 -index 0000000000000..b337dda1b1529 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/gw/requestor_pool.cc -@@ -0,0 +1,73 @@ -+#include "requestor_pool.h" -+ -+#include -+ -+#include "log_macros.h" -+ -+using Self = ipfs::gw::RequestorPool; -+ -+std::string_view Self::name() const { -+ return "requestor pool"; -+} -+Self& Self::add(std::shared_ptr r) { -+ if (api_ && !(r->api_)) { -+ r->api_ = api_; -+ } -+ pool_.push_back(r); -+ r->or_else(shared_from_this()); -+ return *this; -+} -+auto Self::handle(ipfs::gw::RequestPtr req) -> HandleOutcome { -+ auto now = std::time(nullptr); -+ for (auto i = 0UL; i * 2 < waiting_.size(); ++i) { -+ auto& t = waiting_.front().when; -+ if (t != now) { -+ auto to_pop = waiting_.front(); -+ waiting_.pop(); -+ check(to_pop); -+ } -+ } -+ return check({req, 0UL, 0L}); -+} -+auto Self::check(Waiting w) -> HandleOutcome { -+ using O = HandleOutcome; -+ auto next_retry = pool_.size(); -+ auto req = w.req; -+ if (req->PartiallyRedundant()) { -+ return O::DONE; -+ } -+ for (auto i = w.at_idx; i < pool_.size(); ++i) { -+ if (req->type == Type::Zombie) { -+ return O::DONE; -+ } -+ auto& tor = pool_[i]; -+ switch (tor->handle(req)) { -+ case O::DONE: -+ LOG(INFO) << "RequestorPool::handle returning DONE because a member of " -+ "the pool's handle returned DONE."; -+ return O::DONE; -+ case O::PENDING: -+ case O::PARALLEL: -+ req->parallel++; -+ break; -+ case O::MAYBE_LATER: -+ if (next_retry == pool_.size()) { -+ next_retry = i; -+ } -+ break; -+ case O::NOT_HANDLED: -+ break; -+ } -+ } -+ if (req->parallel > 0) { -+ return O::PENDING; -+ } -+ if (next_retry < pool_.size()) { -+ w.when = std::time(nullptr); -+ waiting_.emplace(w); -+ return O::PENDING; -+ } -+ VLOG(1) << "Have exhausted all requestors in pool looking for " -+ << req->debug_string(); -+ return O::NOT_HANDLED; -+} -diff --git a/third_party/ipfs_client/src/ipfs_client/gw/requestor_pool.h b/third_party/ipfs_client/src/ipfs_client/gw/requestor_pool.h -new file mode 100644 -index 0000000000000..86104319a32eb ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/gw/requestor_pool.h -@@ -0,0 +1,32 @@ -+#ifndef IPFS_REQUESTOR_POOL_H_ -+#define IPFS_REQUESTOR_POOL_H_ -+ -+#include -+ -+#include -+ -+#include -+#include -+#include -+ -+namespace ipfs::gw { -+class RequestorPool : public Requestor { -+ std::string_view name() const override; -+ HandleOutcome handle(RequestPtr) override; -+ -+ std::vector> pool_; -+ struct Waiting { -+ RequestPtr req; -+ std::size_t at_idx; -+ std::time_t when; -+ }; -+ std::queue waiting_; -+ -+ HandleOutcome check(Waiting); -+ -+ public: -+ RequestorPool& add(std::shared_ptr); -+}; -+} // namespace ipfs::gw -+ -+#endif // IPFS_REQUESTOR_POOL_H_ -diff --git a/third_party/ipfs_client/src/ipfs_client/gw/terminating_requestor.cc b/third_party/ipfs_client/src/ipfs_client/gw/terminating_requestor.cc -new file mode 100644 -index 0000000000000..791ffd849ddd4 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/gw/terminating_requestor.cc -@@ -0,0 +1,23 @@ -+#include "ipfs_client/gw/terminating_requestor.h" -+ -+#include -+ -+#include "log_macros.h" -+ -+using Self = ipfs::gw::TerminatingRequestor; -+ -+std::string_view Self::name() const { -+ return "Terminating requestor"; -+} -+auto Self::handle(ipfs::gw::RequestPtr r) -> HandleOutcome { -+ if (r->type == Type::Zombie) { -+ return HandleOutcome::DONE; -+ } else if (r->parallel) { -+ return HandleOutcome::PENDING; -+ } else { -+ VLOG(2) << "Out of options, giving up on gateway request " -+ << r->debug_string(); -+ definitive_failure(r); -+ return HandleOutcome::DONE; -+ } -+} -diff --git a/third_party/ipfs_client/src/ipfs_client/http_request_description.cc b/third_party/ipfs_client/src/ipfs_client/http_request_description.cc -new file mode 100644 -index 0000000000000..19b29d0ccde51 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/http_request_description.cc -@@ -0,0 +1,12 @@ -+#include -+ -+using Self = ipfs::HttpRequestDescription; -+ -+bool Self::operator==(HttpRequestDescription const& r) const { -+ // The concept of identity does NOT involve feedback-looping timeout fudge -+ // Nor is the acceptable size of a response necessary to distinguish. -+ return url == r.url && accept == r.accept; -+} -+bool Self::operator<(HttpRequestDescription const& r) const { -+ return url == r.url ? accept < r.accept : url < r.url; -+} -\ No newline at end of file -diff --git a/third_party/ipfs_client/src/ipfs_client/identity_cid.cc b/third_party/ipfs_client/src/ipfs_client/identity_cid.cc -new file mode 100644 -index 0000000000000..9ea2421d5cdf3 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/identity_cid.cc -@@ -0,0 +1,19 @@ -+#include -+ -+#include -+ -+namespace Self = ipfs::id_cid; -+ -+auto Self::forText(std::string_view txt) -> Cid { -+ txt = txt.substr(0UL, MaximumHashLength); -+ auto p = reinterpret_cast(txt.data()); -+ auto b = ByteView{p, txt.size()}; -+ MultiHash mh(HashType::IDENTITY, b); -+ if (mh.valid()) { -+ return Cid{MultiCodec::RAW, mh}; -+ } else { -+ LOG(FATAL) -+ << "We really shouldn't be able to fail to 'hash' using identity."; -+ return forText("Unreachable"); -+ } -+} -\ No newline at end of file -diff --git a/third_party/ipfs_client/src/ipfs_client/ipfs_request.cc b/third_party/ipfs_client/src/ipfs_client/ipfs_request.cc -new file mode 100644 -index 0000000000000..e4f3e1e4b47e3 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipfs_request.cc -@@ -0,0 +1,43 @@ -+#include -+ -+#include -+ -+#include "log_macros.h" -+ -+#include -+ -+using Self = ipfs::IpfsRequest; -+ -+// Self::IpfsRequest(std::string path_p) -+// : path_{path_p}, callback_([](auto&, auto&) {}) {} -+Self::IpfsRequest(std::string path_p, Finisher f) -+ : path_{path_p}, callback_{f} {} -+ -+std::shared_ptr Self::fromUrl(std::string url, ipfs::IpfsRequest::Finisher f) { -+ url.erase(4UL, 2UL ); -+ url.insert(0UL, 1UL, '/'); -+ return std::make_shared(std::move(url), std::move(f)); -+} -+ -+void Self::till_next(std::size_t w) { -+ waiting_ = w; -+} -+void Self::finish(ipfs::Response& r) { -+ VLOG(2) << "IpfsRequest::finish(" << waiting_ << ',' << r.status_ << ");"; -+ if (waiting_) { -+ if (--waiting_) { -+ return; -+ } -+ } -+ callback_(*this, r); -+ // TODO - cancel other gw req pointing into this -+ callback_ = [](auto& q, auto&) { -+ VLOG(2) << "IPFS request " << q.path().pop_all() << " satisfied multiply"; -+ }; -+} -+bool Self::ready_after() { -+ return waiting_ == 0 || 0 == --waiting_; -+} -+void Self::new_path(std::string_view sv) { -+ path_.assign(sv); -+} -\ No newline at end of file -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/chunk.cc b/third_party/ipfs_client/src/ipfs_client/ipld/chunk.cc -new file mode 100644 -index 0000000000000..e5540b080e4b3 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/chunk.cc -@@ -0,0 +1,19 @@ -+#include "chunk.h" -+ -+#include "log_macros.h" -+ -+using Chunk = ipfs::ipld::Chunk; -+ -+Chunk::Chunk(std::string data) : data_{data} {} -+Chunk::~Chunk() {} -+ -+auto Chunk::resolve(ResolutionState& params) -> ResolveResult { -+ if (params.IsFinalComponent()) { -+ return Response{"", 200, data_, params.MyPath().to_string()}; -+ } else { -+ LOG(ERROR) << "Can't resolve a path (" << params.MyPath() -+ << ") inside of a file chunk!"; -+ return ProvenAbsent{}; -+ } -+} -+ -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/chunk.h b/third_party/ipfs_client/src/ipfs_client/ipld/chunk.h -new file mode 100644 -index 0000000000000..b846cc5379171 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/chunk.h -@@ -0,0 +1,18 @@ -+#ifndef IPFS_CHUNK_H_ -+#define IPFS_CHUNK_H_ -+ -+#include -+ -+namespace ipfs::ipld { -+class Chunk : public DagNode { -+ std::string const data_; -+ -+ ResolveResult resolve(ResolutionState&) override; -+ -+ public: -+ explicit Chunk(std::string); -+ virtual ~Chunk() noexcept; -+}; -+} // namespace ipfs::ipld -+ -+#endif // IPFS_CHUNK_H_ -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/dag_cbor_node.cc b/third_party/ipfs_client/src/ipfs_client/ipld/dag_cbor_node.cc -new file mode 100644 -index 0000000000000..064a89373f743 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/dag_cbor_node.cc -@@ -0,0 +1,25 @@ -+#include "dag_cbor_node.h" -+ -+#include "log_macros.h" -+ -+using Self = ipfs::ipld::DagCborNode; -+ -+auto Self::resolve(ResolutionState& params) -> ResolveResult { -+ if (auto cid = doc_->as_link()) { -+ auto cid_str = cid.value().to_string(); -+ return CallChild(params, "", cid_str); -+ } -+ if (params.IsFinalComponent()) { -+ return Response{"text/html", 200, doc_->html(), -+ params.PathToResolve().to_string()}; -+ } -+ return CallChild(params, [this](std::string_view element_name) -> NodePtr { -+ if (auto child = doc_->at(element_name)) { -+ return std::make_shared(std::move(child)); -+ } -+ return {}; -+ }); -+} -+ -+Self::DagCborNode(std::unique_ptr p) : doc_{std::move(p)} {} -+Self::~DagCborNode() {} -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/dag_cbor_node.h b/third_party/ipfs_client/src/ipfs_client/ipld/dag_cbor_node.h -new file mode 100644 -index 0000000000000..c9ba53331674a ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/dag_cbor_node.h -@@ -0,0 +1,22 @@ -+#ifndef IPFS_DAG_CBOR_NODE_H_ -+#define IPFS_DAG_CBOR_NODE_H_ -+ -+#include -+ -+#include -+ -+namespace ipfs::ipld { -+class DagCborNode final : public DagNode { -+ ResolveResult resolve(ResolutionState&) override; -+ -+ public: -+ using Data = DagCborValue; -+ explicit DagCborNode(std::unique_ptr); -+ ~DagCborNode() noexcept override; -+ -+ private: -+ std::unique_ptr doc_; -+}; -+} -+ -+#endif // IPFS_DAG_CBOR_NODE_H_ -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/dag_json_node.cc b/third_party/ipfs_client/src/ipfs_client/ipld/dag_json_node.cc -new file mode 100644 -index 0000000000000..dfb3f38b0a0e1 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/dag_json_node.cc -@@ -0,0 +1,95 @@ -+#include "dag_json_node.h" -+ -+#include -+ -+#include -+ -+using Self = ipfs::ipld::DagJsonNode; -+ -+Self::DagJsonNode(std::unique_ptr j) : data_(std::move(j)) { -+ auto cid = data_->get_if_link(); -+ if (!cid) { -+ return; -+ } -+ auto cid_str = cid->to_string(); -+ if (cid_str.size()) { -+ links_.emplace_back("", Link(cid_str)); -+ } -+} -+Self::~DagJsonNode() noexcept {} -+ -+auto Self::resolve(ResolutionState& params) -> ResolveResult { -+ auto respond_as_link = CallChild(params, ""); -+ if (!std::get_if(&respond_as_link)) { -+ return respond_as_link; -+ } -+ if (params.IsFinalComponent()) { -+ return Response{"text/html", 200, html(), params.MyPath().to_string()}; -+ } -+ return CallChild(params, [this](std::string_view name) -> NodePtr { -+ auto child_data = (*data_)[name]; -+ if (child_data) { -+ return std::make_shared(std::move(child_data)); -+ } -+ return {}; -+ }); -+} -+ -+auto Self::is_link() -> Link* { -+ if (links_.size() == 1UL && links_.front().first.empty()) { -+ return &links_.front().second; -+ } else { -+ return nullptr; -+ } -+} -+namespace { -+void write_body(std::ostream& str, ipfs::DagJsonValue const& val) { -+ if (auto link = val.get_if_link()) { -+ auto cid_str = link.value().to_string(); -+ str << "" << cid_str << "\n"; -+ } else if (auto keys = val.object_keys()) { -+ str << "{\n"; -+ for (auto& key : keys.value()) { -+ str << " \n \n" -+ << " \n \n \n"; -+ } -+ str << "
  ""; -+ for (auto c : key) { -+ str << html_escape(c); -+ } -+ str << "":\n"; -+ auto child = val[key]; -+ write_body(str, *child); -+ str << ",
}\n"; -+ } else if (val.iterate_list([](auto&) {})) { -+ str << "[\n"; -+ val.iterate_list([&str](auto& child) { -+ str << " \n \n \n \n"; -+ }); -+ str << "
  \n"; -+ write_body(str, child); -+ str << "
]\n"; -+ } else { -+ auto plain = val.pretty_print(); -+ // str << "

"; -+ for (auto c : plain) { -+ if (c == '\n') { -+ str << "
\n"; -+ } else { -+ str << html_escape(c); -+ } -+ } -+ // str << "

\n"; -+ } -+} -+} // namespace -+std::string const& Self::html() { -+ if (html_.empty()) { -+ std::ostringstream html; -+ html << "Preview of DAG-JSON\n"; -+ write_body(html, *data_); -+ html << "\n"; -+ html_ = html.str(); -+ } -+ return html_; -+} -\ No newline at end of file -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/dag_json_node.h b/third_party/ipfs_client/src/ipfs_client/ipld/dag_json_node.h -new file mode 100644 -index 0000000000000..1d8012a007487 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/dag_json_node.h -@@ -0,0 +1,22 @@ -+#ifndef IPFS_DAG_JSON_NODE_H_ -+#define IPFS_DAG_JSON_NODE_H_ -+ -+#include -+#include -+ -+namespace ipfs::ipld { -+class DagJsonNode final : public DagNode { -+ std::unique_ptr data_; -+ std::string html_; -+ ResolveResult resolve(ResolutionState& params) override; -+ Link* is_link(); -+ std::string const& html(); -+ -+ public: -+ DagJsonNode(std::unique_ptr); -+ ~DagJsonNode() noexcept override; -+}; -+ -+} // namespace ipfs::ipld -+ -+#endif // IPFS_DAG_JSON_NODE_H_ -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/dag_node.cc b/third_party/ipfs_client/src/ipfs_client/ipld/dag_node.cc -new file mode 100644 -index 0000000000000..4d11f6f23bc78 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/dag_node.cc -@@ -0,0 +1,248 @@ -+#include -+ -+#include "chunk.h" -+#include "dag_cbor_node.h" -+#include "dag_json_node.h" -+#include "directory_shard.h" -+#include "ipns_name.h" -+#include "root.h" -+#include "small_directory.h" -+#include "symlink.h" -+#include "unixfs_file.h" -+ -+#include -+#include -+#include -+ -+#include "log_macros.h" -+ -+#include -+#include -+ -+using Node = ipfs::ipld::DagNode; -+ -+std::shared_ptr Node::fromBytes(std::shared_ptr const& api, -+ Cid const& cid, -+ std::string_view bytes) { -+ return fromBytes(api, cid, as_bytes(bytes)); -+} -+auto Node::fromBytes(std::shared_ptr const& api, -+ ipfs::Cid const& cid, -+ ipfs::ByteView bytes) -> NodePtr { -+ std::shared_ptr result = nullptr; -+ auto hash = api->Hash(cid.hash_type(), bytes); -+ if (!hash.has_value()) { -+ LOG(ERROR) << "Could not hash response for " << cid.to_string(); -+ return {}; -+ } -+ if (hash.value().size() != cid.hash().size()) { -+ return {}; -+ } -+ for (auto i = 0U; i < hash.value().size(); ++i) { -+ auto e = cid.hash()[i]; -+ auto a = hash.value().at(i); -+ if (e != a) { -+ return {}; -+ } -+ } -+ auto required = cid.hash(); -+ auto calculated = hash.value(); -+ if (!std::equal(required.begin(), required.end(), calculated.begin(), -+ calculated.end())) { -+ LOG(ERROR) << "Hash of response did not match the one in the CID " -+ << cid.to_string(); -+ return {}; -+ } -+ switch (cid.codec()) { -+ case MultiCodec::DAG_CBOR: { -+ auto p = reinterpret_cast(bytes.data()); -+ auto cbor = api->ParseCbor({p, bytes.size()}); -+ if (cbor) { -+ result = std::make_shared(std::move(cbor)); -+ } else { -+ LOG(ERROR) << "CBOR node " << cid.to_string() -+ << " does not parse as CBOR."; -+ } -+ } break; -+ case MultiCodec::DAG_JSON: { -+ auto p = reinterpret_cast(bytes.data()); -+ auto json = api->ParseJson({p, bytes.size()}); -+ if (json) { -+ result = std::make_shared(std::move(json)); -+ } else { -+ LOG(ERROR) << "JSON node " << cid.to_string() -+ << " does not parse as JSON."; -+ } -+ } break; -+ case MultiCodec::RAW: -+ case MultiCodec::DAG_PB: { -+ ipfs::PbDag b{cid, bytes}; -+ if (b.valid()) { -+ result = fromBlock(b); -+ } else { -+ std::ostringstream hex; -+ for (auto byt : bytes) { -+ hex << ' ' << std::hex -+ << static_cast(static_cast(byt)); -+ } -+ LOG(ERROR) -+ << "Have a response that did not parse as a valid block, cid: " -+ << cid.to_string() << " contents: " << bytes.size() -+ << " bytes = " << hex.str(); -+ } -+ } break; -+ case MultiCodec::INVALID: -+ case MultiCodec::IDENTITY: -+ case MultiCodec::LIBP2P_KEY: -+ default: -+ LOG(ERROR) << "Response for unhandled CID Codec: " -+ << GetName(cid.codec()); -+ } -+ if (result) { -+ result->set_api(api); -+ } -+ return result; -+} -+std::shared_ptr Node::fromBlock(ipfs::PbDag const& block) { -+ std::shared_ptr result; -+ switch (block.type()) { -+ case PbDag::Type::FileChunk: -+ return std::make_shared(block.chunk_data()); -+ case PbDag::Type::NonFs: -+ return std::make_shared(block.unparsed()); -+ case PbDag::Type::Symlink: -+ return std::make_shared(block.chunk_data()); -+ case PbDag::Type::Directory: -+ result = std::make_shared(); -+ break; -+ case PbDag::Type::File: -+ case PbDag::Type::Raw: -+ result = std::make_shared(); -+ break; -+ case PbDag::Type::HAMTShard: -+ if (block.fsdata().has_fanout()) { -+ result = std::make_shared(block.fsdata().fanout()); -+ } else { -+ result = std::make_shared(); -+ } -+ break; -+ case PbDag::Type::Metadata: -+ LOG(ERROR) << "Metadata blocks unhandled."; -+ return result; -+ case PbDag::Type::Invalid: -+ LOG(ERROR) << "Invalid block."; -+ return result; -+ default: -+ LOG(FATAL) << "TODO " << static_cast(block.type()); -+ } -+ auto add_link = [&result](auto& n, auto c) { -+ result->links_.emplace_back(n, c); -+ return true; -+ }; -+ block.List(add_link); -+ return result; -+} -+ -+auto Node::fromIpnsRecord(ipfs::ValidatedIpns const& v) -> NodePtr { -+ return std::make_shared(v.value); -+} -+ -+std::shared_ptr Node::deroot() { -+ return shared_from_this(); -+} -+std::shared_ptr Node::rooted() { -+ return std::make_shared(shared_from_this()); -+} -+auto Node::as_hamt() -> DirShard* { -+ return nullptr; -+} -+void Node::set_api(std::shared_ptr api) { -+ api_ = api; -+} -+auto Node::resolve(SlashDelimited initial_path, BlockLookup blu) -+ -> ResolveResult { -+ ResolutionState state; -+ state.resolved_path_components = ""; -+ state.unresolved_path = initial_path; -+ state.get_available_block = blu; -+ return resolve(state); -+} -+auto Node::CallChild(ipfs::ipld::ResolutionState& state) -> ResolveResult { -+ return CallChild(state, state.NextComponent(api_.get())); -+} -+auto Node::CallChild(ipfs::ipld::ResolutionState& state, -+ std::string_view link_key, -+ std::string_view block_key) -> ResolveResult { -+ auto child = FindChild(link_key); -+ if (!child) { -+ links_.emplace_back(link_key, Link{std::string{block_key}, {}}); -+ } -+ return CallChild(state, link_key); -+} -+auto Node::CallChild(ResolutionState& state, std::string_view link_key) -+ -> ResolveResult { -+ auto* child = FindChild(link_key); -+ if (!child) { -+ return ProvenAbsent{}; -+ } -+ auto& node = child->node; -+ if (!node) { -+ node = state.GetBlock(child->cid); -+ } -+ if (node) { -+ Descend(state); -+ return node->resolve(state); -+ } else { -+ std::string needed{"/ipfs/"}; -+ needed.append(child->cid); -+ auto more = state.unresolved_path.to_view(); -+ if (more.size()) { -+ if (more.front() != '/') { -+ needed.push_back('/'); -+ } -+ needed.append(more); -+ } -+ return MoreDataNeeded{needed}; -+ } -+} -+auto Node::CallChild(ResolutionState& state, -+ std::function gen_child) -+ -> ResolveResult { -+ auto link_key = state.NextComponent(api_.get()); -+ auto child = FindChild(link_key); -+ if (!child) { -+ links_.emplace_back(link_key, Link{{}, {}}); -+ child = &links_.back().second; -+ } -+ auto& node = child->node; -+ if (!node) { -+ node = gen_child(link_key); -+ if (!node) { -+ return ProvenAbsent{}; -+ } -+ } -+ Descend(state); -+ return node->resolve(state); -+} -+auto Node::FindChild(std::string_view link_key) -> Link* { -+ for (auto& [name, link] : links_) { -+ if (name == link_key) { -+ return &link; -+ } -+ } -+ return nullptr; -+} -+void Node::Descend(ResolutionState& state) { -+ auto next = state.unresolved_path.pop(); -+ if (next.empty()) { -+ return; -+ } -+ if (!state.resolved_path_components.ends_with('/')) { -+ state.resolved_path_components.push_back('/'); -+ } -+ state.resolved_path_components.append(next); -+} -+ -+std::ostream& operator<<(std::ostream& s, ipfs::ipld::PathChange const& c) { -+ return s << "PathChange{" << c.new_path << '}'; -+} -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/directory_shard.cc b/third_party/ipfs_client/src/ipfs_client/ipld/directory_shard.cc -new file mode 100644 -index 0000000000000..7839e62b66247 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/directory_shard.cc -@@ -0,0 +1,101 @@ -+#include "directory_shard.h" -+ -+#include "log_macros.h" -+ -+#include -+#include -+ -+#include -+ -+#include -+#include -+#include -+ -+using namespace std::literals; -+ -+using Self = ipfs::ipld::DirShard; -+ -+auto Self::resolve(ResolutionState& parms) -> ResolveResult { -+ if (parms.IsFinalComponent()) { -+ auto index_parm = parms.WithPath("index.html"sv); -+ auto result = resolve(index_parm); -+ // TODO generate index.html if not present -+ auto resp = std::get_if(&result); -+ if (resp) { -+ resp->mime_ = "text/html"; -+ } -+ return result; -+ } -+ std::string name{parms.NextComponent(api_.get())}; -+ auto hash = hexhash(name); -+ return resolve_internal(hash.begin(), hash.end(), name, parms); -+} -+auto Self::resolve_internal(ipfs::ipld::DirShard::HashIter hash_b, -+ ipfs::ipld::DirShard::HashIter hash_e, -+ std::string_view human_name, -+ ResolutionState& parms) -> ResolveResult { -+ auto hash_chunk = hash_b == hash_e ? std::string{} : *hash_b; -+ for (auto& [name, link] : links_) { -+ if (!starts_with(name, hash_chunk)) { -+ continue; -+ } -+ if (ends_with(name, human_name)) { -+ VLOG(2) << "Found " << human_name << ", leaving HAMT sharded directory " -+ << name << "->" << link.cid; -+ return CallChild(parms, name); -+ } -+ auto node = parms.GetBlock(link.cid); -+ if (!node) { -+ // Unfortunately we can't really append more path and do a full Car -+ // request -+ // The gateway would hash whatever we gave it and compare it to a -+ // partially-consumed hash -+ return MoreDataNeeded{{"/ipfs/" + link.cid}}; -+ } -+ auto downcast = node->as_hamt(); -+ if (downcast) { -+ if (hash_b == hash_e) { -+ LOG(ERROR) << "Ran out of hash bits."; -+ return ProvenAbsent{}; -+ } -+ VLOG(2) << "Found hash chunk, continuing to next level of HAMT sharded " -+ "directory " -+ << name << "->" << link.cid; -+ return downcast->resolve_internal(std::next(hash_b), hash_e, human_name, -+ parms); -+ } else { -+ return ProvenAbsent{}; -+ } -+ } -+ return ProvenAbsent{}; -+} -+std::vector Self::hexhash(std::string_view path_element) const { -+ auto hex_width = 0U; -+ for (auto x = fanout_; (x >>= 4); ++hex_width) -+ ; -+ std::array digest = {0U, 0U}; -+ MurmurHash3_x64_128(path_element.data(), path_element.size(), 0, -+ digest.data()); -+ std::vector result; -+ for (auto d : digest) { -+ auto hash_bits = htobe64(d); -+ while (hash_bits) { -+ // 2. Pop the log2(fanout_) lowest bits from the path component hash -+ // digest,... -+ auto popped = hash_bits % fanout_; -+ hash_bits /= fanout_; -+ std::ostringstream oss; -+ // ... then hex encode (using 0-F) using little endian those bits ... -+ oss << std::setfill('0') << std::setw(hex_width) << std::uppercase -+ << std::hex << popped; -+ result.push_back(oss.str()); -+ } -+ } -+ return result; -+} -+ -+Self::DirShard(std::uint64_t fanout) : fanout_{fanout} {} -+Self::~DirShard() {} -+Self* Self::as_hamt() { -+ return this; -+} -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/directory_shard.h b/third_party/ipfs_client/src/ipfs_client/ipld/directory_shard.h -new file mode 100644 -index 0000000000000..c1cb811355922 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/directory_shard.h -@@ -0,0 +1,26 @@ -+#ifndef IPFS_DIRECTORY_SHARD_H_ -+#define IPFS_DIRECTORY_SHARD_H_ 1 -+ -+#include -+ -+namespace ipfs::ipld { -+class DirShard : public DagNode { -+ std::uint64_t const fanout_; -+ -+ ResolveResult resolve(ResolutionState&) override; -+ DirShard* as_hamt() override; -+ -+ std::vector hexhash(std::string_view path_element) const; -+ using HashIter = std::vector::const_iterator; -+ ResolveResult resolve_internal(HashIter, -+ HashIter, -+ std::string_view, -+ ResolutionState&); -+ -+ public: -+ explicit DirShard(std::uint64_t fanout = 256UL); -+ virtual ~DirShard() noexcept; -+}; -+} // namespace ipfs::ipld -+ -+#endif // IPFS_DIRECTORY_SHARD_H_ -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/ipns_name.cc b/third_party/ipfs_client/src/ipfs_client/ipld/ipns_name.cc -new file mode 100644 -index 0000000000000..f38200923f49f ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/ipns_name.cc -@@ -0,0 +1,25 @@ -+#include "ipns_name.h" -+ -+#include "log_macros.h" -+ -+using Self = ipfs::ipld::IpnsName; -+ -+Self::IpnsName(std::string_view target_abs_path) -+ : target_path_{target_abs_path} {} -+ -+auto Self::resolve(ResolutionState& params) -> ResolveResult { -+ // Can't use PathChange, as the target is truly absolute (rootless) -+ SlashDelimited t{target_path_}; -+ t.pop(); // Discard namespace, though realistically it's going to be ipfs -+ // basically all the time -+ auto name = t.pop(); -+ if (t) { -+ LOG(WARNING) << "Odd case: name points at /ns/root/MORE/PATH (" -+ << target_path_ << "): " << params.MyPath(); -+ auto path = t.to_string() + "/" + params.PathToResolve().to_string(); -+ auto altered = params.WithPath(path); -+ return CallChild(altered, "", name); -+ } else { -+ return CallChild(params, "", name); -+ } -+} -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/ipns_name.h b/third_party/ipfs_client/src/ipfs_client/ipld/ipns_name.h -new file mode 100644 -index 0000000000000..8b50d6e86e397 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/ipns_name.h -@@ -0,0 +1,18 @@ -+#ifndef IPFS_IPLD_IPNS_NAME_H_ -+#define IPFS_IPLD_IPNS_NAME_H_ -+ -+#include "ipfs_client/ipld/dag_node.h" -+ -+namespace ipfs::ipld { -+class IpnsName : public DagNode { -+ std::string const target_path_; -+ -+ ResolveResult resolve(ResolutionState& params) override; -+ -+ public: -+ IpnsName(std::string_view target_abs_path); -+ virtual ~IpnsName() noexcept {} -+}; -+} // namespace ipfs::ipld -+ -+#endif // IPFS_IPLD_IPNS_NAME_H_ -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/link.cc b/third_party/ipfs_client/src/ipfs_client/ipld/link.cc -new file mode 100644 -index 0000000000000..f9dad98e58840 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/link.cc -@@ -0,0 +1,6 @@ -+#include "ipfs_client/ipld/link.h" -+ -+using Self = ipfs::ipld::Link; -+ -+Self::Link(std::string cid_s) : cid{cid_s} {} -+Self::Link(std::string s, std::shared_ptr n) : cid{s}, node{n} {} -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/resolution_state.cc b/third_party/ipfs_client/src/ipfs_client/ipld/resolution_state.cc -new file mode 100644 -index 0000000000000..7b51513d83d6f ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/resolution_state.cc -@@ -0,0 +1,36 @@ -+#include -+ -+#include -+ -+using Self = ipfs::ipld::ResolutionState; -+ -+bool Self::IsFinalComponent() const { -+ return !unresolved_path; -+} -+auto Self::PathToResolve() const -> SlashDelimited { -+ return unresolved_path; -+} -+auto Self::MyPath() const -> SlashDelimited { -+ return SlashDelimited{resolved_path_components}; -+} -+std::string Self::NextComponent(ContextApi const* api) const { -+ auto copy = unresolved_path; -+ if (api) { -+ return api->UnescapeUrlComponent(copy.pop()); -+ } else { -+ return std::string{copy.pop()}; -+ } -+} -+auto Self::GetBlock(std::string const& block_key) const -> NodePtr { -+ return get_available_block(block_key); -+} -+Self Self::WithPath(std::string_view p) const { -+ auto rv = *this; -+ rv.unresolved_path = SlashDelimited{p}; -+ return rv; -+} -+auto Self::RestartResolvedPath() const -> ResolutionState { -+ auto rv = *this; -+ rv.resolved_path_components.clear(); -+ return rv; -+} -\ No newline at end of file -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/root.cc b/third_party/ipfs_client/src/ipfs_client/ipld/root.cc -new file mode 100644 -index 0000000000000..fd5af1b2891b2 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/root.cc -@@ -0,0 +1,90 @@ -+#include "root.h" -+ -+#include "log_macros.h" -+ -+using namespace std::literals; -+ -+using Self = ipfs::ipld::Root; -+using Ptr = std::shared_ptr; -+ -+Self::Root(std::shared_ptr under) { -+ links_.push_back({{}, Link{{}, under}}); -+} -+Self::~Root() {} -+ -+Ptr Self::deroot() { -+ return links_.at(0).second.node; -+} -+Ptr Self::rooted() { -+ return shared_from_this(); -+} -+ -+auto Self::resolve(ResolutionState& params) -> ResolveResult { -+ auto location = params.PathToResolve().to_string(); -+ auto result = deroot()->resolve(params); -+ if (auto pc = std::get_if(&result)) { -+ auto lower = params.WithPath(pc->new_path); -+ result = resolve(lower); -+ location.assign(lower.MyPath().to_view()); -+ } else if (std::get_if(&result)) { -+ if (params.NextComponent(api_.get()) == "_redirects") { -+ return result; -+ } -+ if (!redirects_.has_value()) { -+ auto redirects_path = params.WithPath("_redirects"); -+ result = resolve(redirects_path); -+ auto redirect_resp = std::get_if(&result); -+ if (redirect_resp && redirect_resp->status_ == 200) { -+ redirects_ = redirects::File(redirect_resp->body_); -+ } else { -+ // Either this is ProvenAbsent, in which case this will be interpreted -+ // as the original ProvenAbsent Or it's MoreDataNeeded but for -+ // _redirects, which is what we need now -+ return result; -+ } -+ } -+ if (redirects_.has_value() && redirects_.value().valid()) { -+ Response* resp = nullptr; -+ auto status = redirects_.value().rewrite(location); -+ if (location.find("://") < location.size()) { -+ LOG(INFO) << "_redirects file sent us to a whole URL, scheme-and-all: " -+ << location << " status=" << status; -+ return Response{"", status, "", location}; -+ } -+ auto lower_parm = params.WithPath(location).RestartResolvedPath(); -+ switch (status / 100) { -+ case 0: // no rewrites available -+ break; -+ case 2: -+ result = deroot()->resolve(lower_parm); -+ location.assign(lower_parm.MyPath().to_view()); -+ break; -+ case 3: -+ // Let the redirect happen -+ return Response{"", status, "", location}; -+ case 4: { -+ result = deroot()->resolve(lower_parm); -+ location.assign(lower_parm.MyPath().to_view()); -+ if (std::get_if(&result)) { -+ return Response{"", 500, "", location}; -+ } -+ resp = std::get_if(&result); -+ if (resp) { -+ resp->status_ = status; -+ return *resp; -+ } -+ break; // MoreDataNeeded to fetch e.g. custom 404 page -+ } -+ default: -+ LOG(ERROR) << "Unsupported status came back from _redirects file: " -+ << status; -+ return ProvenAbsent{}; -+ } -+ } -+ } -+ auto resp = std::get_if(&result); -+ if (resp && resp->location_.empty()) { -+ resp->location_ = location; -+ } -+ return result; -+} -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/root.h b/third_party/ipfs_client/src/ipfs_client/ipld/root.h -new file mode 100644 -index 0000000000000..b57951b42f7f1 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/root.h -@@ -0,0 +1,23 @@ -+#ifndef IPFS_ROOT_H_ -+#define IPFS_ROOT_H_ -+ -+#include -+#include -+ -+#include -+ -+namespace ipfs::ipld { -+class Root : public DagNode { -+ std::optional redirects_; -+ -+ ResolveResult resolve(ResolutionState& params) override; -+ std::shared_ptr rooted() override; -+ std::shared_ptr deroot() override; -+ -+ public: -+ Root(std::shared_ptr); -+ virtual ~Root() noexcept; -+}; -+} // namespace ipfs::ipld -+ -+#endif // IPFS_ROOT_H_ -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/small_directory.cc b/third_party/ipfs_client/src/ipfs_client/ipld/small_directory.cc -new file mode 100644 -index 0000000000000..b8613663932ca ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/small_directory.cc -@@ -0,0 +1,35 @@ -+#include "small_directory.h" -+ -+#include -+#include "ipfs_client/generated_directory_listing.h" -+#include "ipfs_client/path2url.h" -+ -+#include "log_macros.h" -+ -+#include -+ -+using namespace std::literals; -+ -+using Self = ipfs::ipld::SmallDirectory; -+ -+auto Self::resolve(ResolutionState& params) -> ResolveResult { -+ if (params.IsFinalComponent()) { -+ LOG(INFO) << "Directory listing requested for " << params.MyPath(); -+ auto result = CallChild(params, "index.html"); -+ if (auto resp = std::get_if(&result)) { -+ resp->mime_ = "text/html"; -+ } -+ if (!std::get_if(&result)) { -+ return result; -+ } -+ auto dir_path = params.MyPath().to_view(); -+ GeneratedDirectoryListing index_html{dir_path}; -+ for (auto& [name, link] : links_) { -+ index_html.AddEntry(name); -+ } -+ return Response{"text/html", 200, index_html.Finish(), ""}; -+ } -+ return CallChild(params); -+} -+ -+Self::~SmallDirectory() {} -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/small_directory.h b/third_party/ipfs_client/src/ipfs_client/ipld/small_directory.h -new file mode 100644 -index 0000000000000..a076122c5041f ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/small_directory.h -@@ -0,0 +1,17 @@ -+#ifndef IPFS_UNIXFS_DIRECTORY_H_ -+#define IPFS_UNIXFS_DIRECTORY_H_ -+ -+#include "ipfs_client/ipld/link.h" -+ -+#include -+ -+namespace ipfs::ipld { -+class SmallDirectory : public DagNode { -+ ResolveResult resolve(ResolutionState&) override; -+ -+ public: -+ virtual ~SmallDirectory() noexcept; -+}; -+} // namespace ipfs::ipld -+ -+#endif // IPFS_UNIXFS_DIRECTORY_H_ -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/symlink.cc b/third_party/ipfs_client/src/ipfs_client/ipld/symlink.cc -new file mode 100644 -index 0000000000000..b35725ad7f703 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/symlink.cc -@@ -0,0 +1,37 @@ -+#include "symlink.h" -+ -+#include "log_macros.h" -+ -+using Self = ipfs::ipld::Symlink; -+ -+Self::Symlink(std::string target) : target_{target} {} -+ -+Self::~Symlink() {} -+ -+auto Self::resolve(ResolutionState& params) -> ResolveResult { -+ std::string result; -+ if (!is_absolute()) { -+ auto left_path = params.MyPath(); -+ left_path.pop_n(2); // Returning a path relative to content root. -+ left_path.pop_back(); // Because the final component refers to this -+ // symlink, which is getting replaced with target -+ result.assign(left_path.to_view()); -+ } -+ result.append("/").append(target_); -+ if (!params.IsFinalComponent()) { -+ result.append("/").append(params.PathToResolve().to_string()); -+ } -+ std::size_t i; -+ while ((i = result.find("//")) != std::string::npos) { -+ result.erase(i, 1); -+ } -+ if (result.ends_with('/')) { -+ result.resize(result.size() - 1); -+ } -+ LOG(INFO) << "symlink: '" << params.MyPath() << "' -> '" << result << "'."; -+ return PathChange{result}; -+} -+ -+bool Self::is_absolute() const { -+ return target_.at(0) == '/'; -+} -\ No newline at end of file -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/symlink.h b/third_party/ipfs_client/src/ipfs_client/ipld/symlink.h -new file mode 100644 -index 0000000000000..937f0d248c25d ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/symlink.h -@@ -0,0 +1,20 @@ -+#ifndef IPFS_SYMLINK_H_ -+#define IPFS_SYMLINK_H_ -+ -+#include -+ -+namespace ipfs::ipld { -+class Symlink : public DagNode { -+ std::string const target_; -+ -+ ResolveResult resolve(ResolutionState& params) override; -+ -+ bool is_absolute() const; -+ -+ public: -+ Symlink(std::string target); -+ ~Symlink() noexcept override; -+}; -+} // namespace ipfs::ipld -+ -+#endif // IPFS_SYMLINK_H_ -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/unixfs_file.cc b/third_party/ipfs_client/src/ipfs_client/ipld/unixfs_file.cc -new file mode 100644 -index 0000000000000..784a1df367152 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/unixfs_file.cc -@@ -0,0 +1,50 @@ -+#include "unixfs_file.h" -+ -+#include "log_macros.h" -+ -+using namespace std::literals; -+ -+using Self = ipfs::ipld::UnixfsFile; -+ -+auto Self::resolve(ResolutionState& params) -> ResolveResult { -+ if (!params.IsFinalComponent()) { -+ LOG(ERROR) << "Can't path through a file, (at " << params.MyPath() -+ << ") but given the path " << params.PathToResolve(); -+ return ProvenAbsent{}; -+ } -+ std::vector missing; -+ std::string body; -+ for (auto& child : links_) { -+ auto& link = child.second; -+ if (!link.node) { -+ link.node = params.GetBlock(link.cid); -+ } -+ if (link.node) { -+ auto recurse = link.node->resolve(params); -+ auto mdn = std::get_if(&recurse); -+ if (mdn) { -+ missing.insert(missing.end(), mdn->ipfs_abs_paths_.begin(), -+ mdn->ipfs_abs_paths_.end()); -+ continue; -+ } -+ if (missing.empty()) { -+ body.append(std::get(recurse).body_); -+ } -+ } else { -+ missing.push_back("/ipfs/" + link.cid); -+ } -+ } -+ if (missing.empty()) { -+ return Response{ -+ "", -+ 200, -+ body, -+ params.MyPath().to_string(), -+ }; -+ } -+ auto result = MoreDataNeeded{missing}; -+ result.insist_on_car = true; -+ return result; -+} -+ -+Self::~UnixfsFile() {} -diff --git a/third_party/ipfs_client/src/ipfs_client/ipld/unixfs_file.h b/third_party/ipfs_client/src/ipfs_client/ipld/unixfs_file.h -new file mode 100644 -index 0000000000000..3447e949d330e ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipld/unixfs_file.h -@@ -0,0 +1,15 @@ -+#ifndef IPFS_UNIXFS_FILE_H_ -+#define IPFS_UNIXFS_FILE_H_ -+ -+#include -+ -+namespace ipfs::ipld { -+class UnixfsFile : public DagNode { -+ ResolveResult resolve(ResolutionState&) override; -+ -+ public: -+ virtual ~UnixfsFile() noexcept; -+}; -+} // namespace ipfs::ipld -+ -+#endif // IPFS_UNIXFS_FILE_H_ -diff --git a/third_party/ipfs_client/src/ipfs_client/ipns_names.cc b/third_party/ipfs_client/src/ipfs_client/ipns_names.cc -new file mode 100644 -index 0000000000000..6eccfaa7dc51b ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipns_names.cc -@@ -0,0 +1,101 @@ -+#include -+ -+#include -+ -+#include "log_macros.h" -+ -+using Self = ipfs::IpnsNames; -+ -+void Self::NoSuchName(std::string const& name) { -+ names_[name]; // If it already exists, leave it. -+} -+void Self::AssignName(std::string const& name, ValidatedIpns entry) { -+ auto& res = entry.value; -+ if (res.size() && res.front() == '/') { -+ res.erase(0, 1); -+ } -+ auto endofcid = res.find_first_of("/?#", 6); -+ using namespace libp2p::multi; -+ auto cid_str = res.substr(5, endofcid); -+ LOG(INFO) << "IPNS points to CID " << cid_str; -+ auto cid = Cid(cid_str); -+ if (cid.valid()) { -+ auto desensitized = res.substr(0, 5); -+ desensitized.append(cid_str); -+ if (endofcid < res.size()) { -+ auto extra = res.substr(endofcid); -+ LOG(INFO) << name << " resolution contains oddity '" << extra; -+ desensitized.append(extra); -+ } -+ LOG(INFO) << name << " now resolves to (desensitized)" << desensitized; -+ entry.value = desensitized; -+ } else { -+ LOG(INFO) << name << " now resolves to (extra level)" << res; -+ } -+ auto it = names_.find(name); -+ if (it == names_.end()) { -+ names_.emplace(name, std::move(entry)); -+ } else if (it->second.sequence < entry.sequence) { -+ LOG(INFO) << "Updating IPNS record for " << name << " from sequence " -+ << it->second.sequence << " where it pointed to " -+ << it->second.value << " to sequence " << entry.sequence -+ << " where it points to " << entry.value; -+ it->second = std::move(entry); -+ } else { -+ LOG(INFO) << "Discarding redundant IPNS record for " << name; -+ } -+} -+void Self::AssignDnsLink(std::string const& name, std::string_view target) { -+ ValidatedIpns v; -+ v.value.assign(target); -+ auto t = std::time(nullptr); -+ v.use_until = v.cache_until = t + 300; -+ AssignName(name, std::move(v)); -+} -+ -+std::string_view Self::NameResolvedTo(std::string_view original_name) const { -+ std::string name{original_name}; -+ std::string_view prev = ""; -+ auto trailer = names_.end(); -+ auto trail_step = false; -+ auto now = std::time(nullptr); -+ while (true) { -+ auto it = names_.find(name); -+ if (names_.end() == it) { -+ LOG(INFO) << "Host not in immediate access map: " << name << " (" -+ << std::string{original_name} << ')'; -+ return prev; -+ } else if (it == trailer) { -+ LOG(ERROR) << "Host cycle found in IPNS: " << std::string{original_name} -+ << ' ' << name; -+ return ""; -+ } -+ auto& target = it->second.value; -+ if (target.empty()) { -+ return kNoSuchName; -+ } -+ if (target.at(2) == 'f') { -+ return target; -+ } -+ if (it->second.use_until < now) { -+ return prev; -+ } -+ if (trail_step) { -+ if (trailer == names_.end()) { -+ trailer = names_.find(name); -+ } else { -+ trailer = names_.find(trailer->second.value.substr(5)); -+ } -+ } -+ trail_step = !trail_step; -+ prev = it->second.value; -+ name.assign(prev, 5); -+ } -+} -+auto Self::Entry(std::string const& name) -> ValidatedIpns const* { -+ auto it = names_.find(name); -+ return it == names_.end() ? nullptr : &(it->second); -+} -+ -+Self::IpnsNames() {} -+Self::~IpnsNames() {} -diff --git a/third_party/ipfs_client/src/ipfs_client/ipns_record.cc b/third_party/ipfs_client/src/ipfs_client/ipns_record.cc -new file mode 100644 -index 0000000000000..86be780f9066f ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/ipns_record.cc -@@ -0,0 +1,244 @@ -+#include -+ -+#include -+#include -+#include -+ -+#include "log_macros.h" -+ -+#include -+#include -+ -+#if __has_include() -+#include -+#else -+#include "ipfs_client/ipns_record.pb.h" -+#endif -+ -+namespace { -+bool matches(ipfs::MultiHash const& hash, -+ ipfs::ByteView pubkey_bytes, -+ ipfs::ContextApi& api) { -+ auto result = api.Hash(hash.type(), pubkey_bytes); -+ if (!result.has_value()) { -+ return false; -+ } -+ return std::equal(result->begin(), result->end(), hash.digest().begin(), -+ hash.digest().end()); -+} -+} // namespace -+ -+namespace { -+void assign(std::string& out, -+ ipfs::DagCborValue& top, -+ std::string_view key) { -+ auto p = top.at(key); -+ if (!p) { -+ out.assign("Key '").append(key).append("' not present in IPNS CBOR!"); -+ } else { -+ // YEP! as_bytes() . There are only 2 string values here, they are logically -+ // text, but they are defined in the spec to be bytes. -+ auto o = p->as_bytes(); -+ if (o.has_value()) { -+ auto chars = reinterpret_cast(o.value().data()); -+ out.assign(chars, o.value().size()); -+ } else { -+ out.assign("Key '").append(key).append( -+ "' was not a string in IPNS CBOR!"); -+ } -+ } -+} -+void assign(std::uint64_t& out, -+ ipfs::DagCborValue& top, -+ std::string_view key) { -+ auto p = top.at(key); -+ if (!p) { -+ LOG(ERROR) << "Key '" << key << "' is not present in IPNS CBOR!"; -+ out = std::numeric_limits::max(); -+ } else { -+ auto o = p->as_unsigned(); -+ if (o.has_value()) { -+ out = o.value(); -+ } else { -+ LOG(ERROR) << "Key '" << key -+ << "' is not an unsigned integer in IPNS CBOR!"; -+ out = std::numeric_limits::max(); -+ } -+ } -+} -+} // namespace -+ -+auto ipfs::ValidateIpnsRecord(ipfs::ByteView top_level_bytes, -+ Cid const& name, -+ ContextApi& api) -> std::optional { -+ DCHECK_EQ(name.codec(), MultiCodec::LIBP2P_KEY); -+ if (name.codec() != MultiCodec::LIBP2P_KEY) { -+ return {}; -+ } -+ // https://github.com/ipfs/specs/blob/main/ipns/IPNS.md#record-verification -+ -+ // Before parsing the protobuf, confirm that the serialized IpnsEntry bytes -+ // sum to less than or equal to the size limit. -+ if (top_level_bytes.size() > MAX_IPNS_PB_SERIALIZED_SIZE) { -+ LOG(ERROR) << "IPNS record too large: " << top_level_bytes.size(); -+ return {}; -+ } -+ -+ ipfs::ipns::IpnsEntry entry; -+ if (!entry.ParseFromArray(top_level_bytes.data(), top_level_bytes.size())) { -+ LOG(ERROR) << "Failed to parse top-level bytes as a protobuf"; -+ return {}; -+ } -+ -+ // Confirm IpnsEntry.signatureV2 and IpnsEntry.data are present and are not -+ // empty -+ if (!entry.has_signaturev2()) { -+ LOG(ERROR) << "IPNS record contains no .signatureV2!"; -+ return {}; -+ } -+ if (!entry.has_data() || entry.data().empty()) { -+ LOG(ERROR) << "IPNS record has no .data"; -+ return {}; -+ } -+ -+ // The only supported value is 0, which indicates the validity field contains -+ // the expiration date after which the IPNS record becomes invalid. -+ DCHECK_EQ(entry.validitytype(), 0); -+ -+ auto parsed = -+ api.ParseCbor({reinterpret_cast(entry.data().data()), -+ entry.data().size()}); -+ if (!parsed) { -+ LOG(ERROR) << "CBOR parsing failed."; -+ return {}; -+ } -+ IpnsCborEntry result; -+ assign(result.value, *parsed, "Value"); -+ if (entry.has_value() && result.value != entry.value()) { -+ LOG(ERROR) << "Mismatch on Value field in IPNS record... CBOR(v2): '" -+ << result.value << "' but PB(v1): '" << entry.value() -+ << "' : " << parsed->html(); -+ return {}; -+ } -+ ipfs::ByteView public_key; -+ if (entry.has_pubkey()) { -+ public_key = ipfs::ByteView{ -+ reinterpret_cast(entry.pubkey().data()), -+ entry.pubkey().size()}; -+ if (!matches(name.multi_hash(), public_key, api)) { -+ LOG(ERROR) << "Given IPNS record contains a pubkey that does not match " -+ "the hash from the IPNS name that fetched it!"; -+ return {}; -+ } -+ } else if (name.hash_type() == HashType::IDENTITY) { -+ public_key = name.hash(); -+ } else { -+ LOG(ERROR) << "IPNS record contains no public key, and the IPNS name " -+ << name.to_string() -+ << " is a true hash, not identity. Validation impossible."; -+ return {}; -+ } -+ ipfs::ipns::PublicKey pk; -+ auto* pkbp = reinterpret_cast(public_key.data()); -+ if (!pk.ParseFromArray(pkbp, public_key.size())) { -+ LOG(ERROR) << "Failed to parse public key bytes"; -+ return {}; -+ } -+ LOG(INFO) << "Record contains a public key of type " << pk.type() -+ << " and points to " << entry.value(); -+ auto& signature_str = entry.signaturev2(); -+ ByteView signature{reinterpret_cast(signature_str.data()), -+ signature_str.size()}; -+ // https://specs.ipfs.tech/ipns/ipns-record/#record-verification -+ // Create bytes for signature verification by concatenating -+ // ipto_hex(ns-signature:// prefix (bytes in hex: -+ // 69706e732d7369676e61747572653a) with raw CBOR bytes from IpnsEntry.data -+ auto bytes_str = entry.data(); -+ bytes_str.insert( -+ 0, "\x69\x70\x6e\x73\x2d\x73\x69\x67\x6e\x61\x74\x75\x72\x65\x3a"); -+ ByteView bytes{reinterpret_cast(bytes_str.data()), -+ bytes_str.size()}; -+ ByteView key_bytes{reinterpret_cast(pk.data().data()), -+ pk.data().size()}; -+ if (!api.VerifyKeySignature(static_cast(pk.type()), signature, -+ bytes, key_bytes)) { -+ LOG(ERROR) << "Verification failed!!"; -+ return {}; -+ } -+ // TODO check expiration date -+ if (entry.has_value() && entry.value() != result.value) { -+ LOG(ERROR) << "IPNS " << name.to_string() << " has different values for V1(" -+ << entry.value() << ") and V2(" << result.value << ')'; -+ return {}; -+ } -+ assign(result.validity, *parsed, "Validity"); -+ if (entry.has_validity() && entry.validity() != result.validity) { -+ LOG(ERROR) << "IPNS " << name.to_string() -+ << " has different validity for V1(" << entry.validity() -+ << ") and V2(" << result.validity << ')'; -+ return {}; -+ } -+ assign(result.validityType, *parsed, "ValidityType"); -+ if (entry.has_validitytype() && -+ entry.validitytype() != static_cast(result.validityType)) { -+ LOG(ERROR) << "IPNS " << name.to_string() -+ << " has different validity types for V1(" -+ << entry.validitytype() << ") and V2(" << result.validityType -+ << ')'; -+ return {}; -+ } -+ assign(result.sequence, *parsed, "Sequence"); -+ if (entry.has_sequence() && entry.sequence() != result.sequence) { -+ LOG(ERROR) << "IPNS " << name.to_string() -+ << " has different validity types for V1(" << entry.sequence() -+ << ") and V2(" << result.sequence << ')'; -+ return {}; -+ } -+ assign(result.ttl, *parsed, "TTL"); -+ if (entry.has_ttl() && entry.ttl() != result.ttl) { -+ LOG(ERROR) << "IPNS " << name.to_string() -+ << " has different validity types for V1(" << entry.ttl() -+ << ") and V2(" << result.ttl << ')'; -+ return {}; -+ } -+ LOG(INFO) << "IPNS record verification passes for " << name.to_string() -+ << " sequence: " << result.sequence << " points at " -+ << result.value; -+ return result; -+} -+ -+ipfs::ValidatedIpns::ValidatedIpns() = default; -+ipfs::ValidatedIpns::ValidatedIpns(ValidatedIpns&&) = default; -+ipfs::ValidatedIpns::ValidatedIpns(ValidatedIpns const&) = default; -+auto ipfs::ValidatedIpns::operator=(ValidatedIpns const&) -+ -> ValidatedIpns& = default; -+ipfs::ValidatedIpns::ValidatedIpns(IpnsCborEntry const& e) -+ : value{e.value}, sequence{e.sequence} { -+ std::istringstream ss{e.validity}; -+ std::tm t = {}; -+ ss >> std::get_time(&t, "%Y-%m-%dT%H:%M:%S"); -+ long ttl = (e.ttl / 1'000'000'000UL) + 1; -+#ifdef _MSC_VER -+ use_until = _mkgmtime(&t); -+#else -+ use_until = timegm(&t); -+#endif -+ cache_until = std::time(nullptr) + ttl; -+} -+ -+std::string ipfs::ValidatedIpns::Serialize() const { -+ DCHECK_EQ(value.find(' '), std::string::npos); -+ DCHECK_EQ(gateway_source.find(' '), std::string::npos); -+ std::ostringstream ss; -+ ss << std::hex << sequence << ' ' << use_until << ' ' << cache_until << ' ' -+ << fetch_time << ' ' << resolution_ms << ' ' << value << ' ' -+ << gateway_source; -+ return ss.str(); -+} -+auto ipfs::ValidatedIpns::Deserialize(std::string s) -> ValidatedIpns { -+ std::istringstream ss(s); -+ ValidatedIpns e; -+ ss >> std::hex >> e.sequence >> e.use_until >> e.cache_until >> -+ e.fetch_time >> e.resolution_ms >> e.value >> e.gateway_source; -+ return e; -+} -diff --git a/third_party/ipfs_client/src/ipfs_client/logger.cc b/third_party/ipfs_client/src/ipfs_client/logger.cc -new file mode 100644 -index 0000000000000..8c093bfca89aa ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/logger.cc -@@ -0,0 +1,76 @@ -+#include -+ -+#include -+ -+#include -+ -+namespace lg = ipfs::log; -+ -+namespace { -+lg::Level current_level = lg::Level::WARN; -+lg::Handler current_handler = nullptr; -+ -+void CheckLevel(google::protobuf::LogLevel lv, -+ char const* f, -+ int l, -+ std::string const& m) { -+ auto lev = static_cast(lv); -+ if (lev < static_cast(current_level)) { -+ return; -+ } -+ if (!current_handler) { -+ return; -+ } -+ current_handler(m, f, l, static_cast(lev)); -+} -+} // namespace -+ -+void lg::SetLevel(Level lev) { -+ IsInitialized(); -+ current_level = lev; -+} -+ -+void lg::SetHandler(Handler h) { -+ current_handler = h; -+ google::protobuf::SetLogHandler(&CheckLevel); -+} -+ -+void lg::DefaultHandler(std::string const& message, -+ char const* source_file, -+ int source_line, -+ Level lev) { -+ std::clog << source_file << ':' << source_line << ": " << LevelDescriptor(lev) -+ << ": " << message << '\n'; -+ if (lev == Level::FATAL) { -+ std::abort(); -+ } -+} -+ -+std::string_view lg::LevelDescriptor(Level l) { -+ switch (l) { -+ case Level::TRACE: -+ return "trace"; -+ case Level::DEBUG: -+ return "debug"; -+ case Level::INFO: -+ return "note"; // The next 3 are gcc- & clang-inspired -+ case Level::WARN: -+ return "warning"; -+ case Level::ERROR: -+ return "error"; -+ case Level::FATAL: -+ return " ### FATAL ERROR ### "; -+ case Level::OFF: -+ return "off"; -+ default: -+ return "Unknown log level used: possible corruption?"; -+ } -+} -+ -+bool lg::IsInitialized() { -+ if (current_handler) { -+ return true; -+ } -+ SetHandler(&DefaultHandler); -+ return false; -+} -diff --git a/third_party/ipfs_client/src/ipfs_client/multi_base.cc b/third_party/ipfs_client/src/ipfs_client/multi_base.cc -new file mode 100644 -index 0000000000000..58c9a0f18d100 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/multi_base.cc -@@ -0,0 +1,133 @@ -+#include "ipfs_client/multi_base.h" -+ -+#include "bases/b16_upper.h" -+#include "bases/b32.h" -+ -+#include -+ -+#include "log_macros.h" -+ -+using namespace std::literals; -+ -+namespace imb = ipfs::mb; -+namespace { -+constexpr std::string_view UnsupportedMultibase = "unsupported-multibase"; -+ -+template -+std::string encode_adapt(ipfs::ByteView bytes) { -+ auto p = reinterpret_cast(bytes.data()); -+ typename Target::encoder target; -+ return target.process({p, bytes.size()}); -+} -+enum class EncodedCase { lower, UPPER, Sensitive }; -+template -+std::vector decode_adapt(std::string_view encoded_sv) { -+ typename Target::decoder target; -+ std::string encoded_s{encoded_sv}; -+ switch (ec) { -+ case EncodedCase::lower: -+ for (auto& c : encoded_s) { -+ if (c >= 'A' && c <= 'Z') { -+ c = c - 'A' + 'a'; -+ } -+ } -+ break; -+ case EncodedCase::UPPER: -+ for (auto& c : encoded_s) { -+ if (c >= 'a' && c <= 'z') { -+ c = c - 'a' + 'A'; -+ } -+ } -+ break; -+ case EncodedCase::Sensitive: -+ break; -+ } -+ auto s = target.process(encoded_s); -+ auto b = reinterpret_cast(s.data()); -+ auto e = b + s.size(); -+ return std::vector(b, e); -+} -+template -+constexpr imb::Codec adapt(std::string_view name) { -+ return imb::Codec{&decode_adapt, -+ &encode_adapt, name}; -+} -+} // namespace -+ -+auto imb::Codec::Get(Code c) -> Codec const* { -+ switch (c) { -+ case Code::IDENTITY: -+ return nullptr; -+ case Code::UNSUPPORTED: -+ return nullptr; -+ case Code::BASE16_LOWER: { -+ static auto b16 = -+ adapt("base16"sv); -+ return &b16; -+ } -+ case Code::BASE16_UPPER: { -+ static auto b16u = -+ adapt("base16upper"sv); -+ return &b16u; -+ } -+ case Code::BASE32_LOWER: { -+ static auto b32 = adapt("base32"sv); -+ return &b32; -+ } -+ case Code::BASE32_UPPER: { -+ static auto b32u = -+ adapt("base32upper"sv); -+ return &b32u; -+ } -+ case Code::BASE36_LOWER: { -+ static auto b36 = -+ adapt("base36"sv); -+ return &b36; -+ } -+ case Code::BASE36_UPPER: -+ return nullptr; -+ case Code::BASE58_BTC: { -+ static auto b58 = -+ adapt("base58btc"sv); -+ return &b58; -+ } -+ case Code::BASE64: -+ return nullptr; -+ } -+ return nullptr; -+} -+std::string_view imb::GetName(Code c) { -+ if (auto codec = Codec::Get(c)) { -+ return codec->name; -+ } -+ return UnsupportedMultibase; -+} -+auto imb::CodeFromPrefix(char ch) -> Code { -+ auto c = static_cast(ch); -+ return Codec::Get(c) ? Code::UNSUPPORTED : c; -+} -+auto imb::decode(std::string_view mb_str) -> std::optional> { -+ if (mb_str.empty()) { -+ return std::nullopt; -+ } -+ if (auto* codec = Codec::Get(static_cast(mb_str[0]))) { -+ return codec->decode(mb_str.substr(1)); -+ } else { -+ return std::nullopt; -+ } -+} -+std::string imb::encode(Code c, ByteView bs) { -+ if (auto codec = Codec::Get(c)) { -+ auto rv = codec->encode(bs); -+ if (rv.size() >= bs.size()) { -+ rv.insert(0UL, 1UL, static_cast(c)); -+ return rv; -+ } else { -+ LOG(ERROR) << "Error encoding into base " << codec->name; -+ } -+ } else { -+ LOG(ERROR) << "Can't encode to multibase " << static_cast(c) -+ << " because I can't find a codec??"; -+ } -+ return {}; -+} -\ No newline at end of file -diff --git a/third_party/ipfs_client/src/ipfs_client/multi_hash.cc b/third_party/ipfs_client/src/ipfs_client/multi_hash.cc -new file mode 100644 -index 0000000000000..20cf5b19a16c8 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/multi_hash.cc -@@ -0,0 +1,56 @@ -+#include -+ -+#include -+ -+using Self = ipfs::MultiHash; -+using VarInt = libp2p::multi::UVarint; -+ -+Self::MultiHash(ipfs::HashType t, ipfs::ByteView digest) -+ : type_{t}, hash_(digest.begin(), digest.end()) {} -+ -+Self::MultiHash(ipfs::ByteView bytes) { -+ ReadPrefix(bytes); -+} -+bool Self::ReadPrefix(ipfs::ByteView& bytes) { -+ auto i = VarInt::create(bytes); -+ if (!i) { -+ return false; -+ } -+ bytes = bytes.subspan(i->size()); -+ auto type = Validate(static_cast(i->toUInt64())); -+ i = VarInt::create(bytes); -+ if (!i) { -+ return false; -+ } -+ auto length = i->toUInt64(); -+ if (length > bytes.size()) { -+ return false; -+ } -+ bytes = bytes.subspan(i->size()); -+ hash_.assign(bytes.begin(), std::next(bytes.begin(), length)); -+ bytes = bytes.subspan(length); -+ type_ = type; -+ return true; -+} -+bool Self::valid() const { -+ return type_ != HashType::INVALID && hash_.size() > 0UL; -+} -+namespace { -+constexpr std::string_view InvalidHashTypeName; -+} -+std::string_view ipfs::GetName(HashType t) { -+ switch (t) { -+ case HashType::INVALID: -+ return InvalidHashTypeName; -+ case HashType::IDENTITY: -+ return "identity"; -+ case HashType::SHA2_256: -+ return "sha2-256"; -+ } -+ // Don't use default: -> let it fall through. We want compiler warnings about -+ // unhandled cases. -+ return InvalidHashTypeName; -+} -+auto ipfs::Validate(HashType t) -> HashType { -+ return GetName(t) == InvalidHashTypeName ? HashType::INVALID : t; -+} -\ No newline at end of file -diff --git a/third_party/ipfs_client/src/ipfs_client/multicodec.cc b/third_party/ipfs_client/src/ipfs_client/multicodec.cc -new file mode 100644 -index 0000000000000..68cad03ea5862 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/multicodec.cc -@@ -0,0 +1,33 @@ -+#include -+ -+using Cdc = ipfs::MultiCodec; -+ -+namespace { -+constexpr std::string_view InvalidMulticodecLabel{"invalid-multicodec"}; -+} -+ -+std::string_view ipfs::GetName(Cdc c) { -+ switch (c) { -+ case Cdc::INVALID: -+ return InvalidMulticodecLabel; -+ case Cdc::IDENTITY: -+ return "identity"; -+ case Cdc::RAW: -+ return "raw"; -+ case Cdc::DAG_PB: -+ return "dag-pb"; -+ case Cdc::DAG_CBOR: -+ return "dag-cbor"; -+ case Cdc::LIBP2P_KEY: -+ return "libp2p-key"; -+ case Cdc::DAG_JSON: -+ return "dag-json"; -+ } -+ return InvalidMulticodecLabel; -+} -+Cdc ipfs::Validate(Cdc c) { -+ if (GetName(c) == InvalidMulticodecLabel) { -+ return Cdc::INVALID; -+ } -+ return c; -+} -\ No newline at end of file -diff --git a/third_party/ipfs_client/src/ipfs_client/orchestrator.cc b/third_party/ipfs_client/src/ipfs_client/orchestrator.cc -new file mode 100644 -index 0000000000000..3392ae1126e39 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/orchestrator.cc -@@ -0,0 +1,146 @@ -+#include "ipfs_client/orchestrator.h" -+ -+#include -+#include -+#include -+ -+#include "log_macros.h" -+#include "path2url.h" -+ -+using namespace std::literals; -+ -+using Self = ipfs::Orchestrator; -+ -+Self::Orchestrator(std::shared_ptr requestor, -+ std::shared_ptr api) -+ // : gw_requestor_{ga}, api_{api}, requestor_{requestor} { -+ : api_{api}, requestor_{requestor} { -+ DCHECK(requestor); -+} -+ -+void Self::build_response(std::shared_ptr req) { -+ if (!req || !req->ready_after()) { -+ return; -+ } -+ auto req_path = req->path(); -+ VLOG(2) << "build_response(" << req_path.to_string() << ')'; -+ req_path.pop(); // namespace -+ std::string affinity{req_path.pop()}; -+ auto it = dags_.find(affinity); -+ if (dags_.end() == it) { -+ if (gw_request(req, req->path(), affinity)) { -+ build_response(req); -+ } -+ } else { -+ VLOG(2) << "Requesting root " << affinity << " resolve path " -+ << req_path.to_string(); -+ auto root = it->second->rooted(); -+ if (root != it->second) { -+ it->second = root; -+ } -+ from_tree(req, root, req_path, affinity); -+ } -+} -+void Self::from_tree(std::shared_ptr req, -+ ipfs::ipld::NodePtr& node, -+ SlashDelimited relative_path, -+ std::string const& affinity) { -+ auto root = node->rooted(); -+ auto block_look_up = [this](auto& k) { -+ auto i = dags_.find(k); -+ return i == dags_.end() ? ipld::NodePtr{} : i->second; -+ }; -+ auto start = std::string{req->path().pop_n(2)}; -+ auto result = root->resolve(relative_path, block_look_up); -+ auto response = std::get_if(&result); -+ if (response) { -+ VLOG(2) << "Tree gave us a response: status=" << response->status_ -+ << " mime=" << response->mime_ -+ << " location=" << response->location_ << " body is " -+ << response->body_.size() << " bytes."; -+ if (response->mime_.empty() && !response->body_.empty()) { -+ if (response->location_.empty()) { -+ LOG(INFO) << "Request for " << req->path() -+ << " returned no location, so sniffing from request path and " -+ "body of " -+ << response->body_.size() << "B."; -+ response->mime_ = sniff(req->path(), response->body_); -+ } else { -+ std::string hit_path{req->path().pop_n(2)}; -+ if (!hit_path.ends_with('/') && -+ !(response->location_.starts_with('/'))) { -+ hit_path.push_back('/'); -+ } -+ hit_path.append(response->location_); -+ LOG(INFO) << "Request for " << req->path() << " returned a location of " -+ << response->location_ << " and a body of " -+ << response->body_.size() << " bytes, sniffing mime from " -+ << hit_path; -+ response->mime_ = sniff(SlashDelimited{hit_path}, response->body_); -+ } -+ } -+ req->finish(*response); -+ } else if (std::holds_alternative(result)) { -+ auto& np = std::get(result); -+ LOG(INFO) << "Symlink converts request to " << req->path().to_string() -+ << " into " << np.new_path -+ << ". TODO - check for infinite loops."; -+ req->new_path(np.new_path); -+ build_response(req); -+ } else if (std::get_if(&result)) { -+ req->finish(Response::IMMUTABLY_GONE); -+ } else { -+ auto& mps = std::get(result).ipfs_abs_paths_; -+ req->till_next(mps.size()); -+ if (std::any_of(mps.begin(), mps.end(), [this, &req, &affinity](auto& p) { -+ return gw_request(req, SlashDelimited{p}, affinity); -+ })) { -+ from_tree(req, node, relative_path, affinity); -+ } -+ } -+} -+bool Self::gw_request(std::shared_ptr ir, -+ ipfs::SlashDelimited path, -+ std::string const& aff) { -+ VLOG(1) << "Seeking " << path.to_string(); -+ auto req = gw::GatewayRequest::fromIpfsPath(path); -+ if (req) { -+ req->dependent = ir; -+ req->orchestrator(shared_from_this()); -+ req->affinity = aff; -+ requestor_->request(req); -+ } else { -+ LOG(ERROR) << "Failed to create a request for " << path.to_string(); -+ } -+ return false; -+} -+ -+bool Self::add_node(std::string key, ipfs::ipld::NodePtr p) { -+ if (p) { -+ if (dags_.insert({key, p}).second) { -+ p->set_api(api_); -+ } -+ return true; -+ } else { -+ LOG(INFO) << "NULL block attempted to be added for " << key; -+ } -+ return false; -+} -+ -+std::string Self::sniff(ipfs::SlashDelimited p, std::string const& body) const { -+ auto fake_url = path2url(p.to_string()); -+ auto file_name = p.peek_back(); -+ auto dot = file_name.find_last_of('.'); -+ std::string ext = ""; -+ if (dot < file_name.size()) { -+ ext.assign(file_name, dot + 1); -+ } -+ auto result = api_->MimeType(ext, body, fake_url); -+ LOG(INFO) << "Deduced mime from (ext=" << ext << " body of " << body.size() -+ << " bytes, 'url'=" << fake_url << ")=" << result; -+ return result; -+} -+ -+bool Self::has_key(std::string const& k) const { -+ return dags_.count(k); -+} -\ No newline at end of file -diff --git a/third_party/ipfs_client/src/ipfs_client/path2url.cc b/third_party/ipfs_client/src/ipfs_client/path2url.cc -new file mode 100644 -index 0000000000000..0d7cf305a47b4 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/path2url.cc -@@ -0,0 +1,16 @@ -+#include "path2url.h" -+ -+#include "log_macros.h" -+ -+std::string ipfs::path2url(std::string p) { -+ while (!p.empty() && p[0] == '/') { -+ p.erase(0UL, 1UL); -+ } -+ DCHECK_EQ(p.at(0), 'i'); -+ DCHECK_EQ(p.at(1), 'p'); -+ DCHECK(p.at(2) == 'f' || p.at(2) == 'n'); -+ DCHECK_EQ(p.at(3), 's'); -+ DCHECK_EQ(p.at(4), '/'); -+ p.insert(4, ":/"); -+ return p; -+} -diff --git a/third_party/ipfs_client/src/ipfs_client/path2url.h b/third_party/ipfs_client/src/ipfs_client/path2url.h -new file mode 100644 -index 0000000000000..683e92d759b4e ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/path2url.h -@@ -0,0 +1,10 @@ -+#ifndef IPFS_PATH2URL_H_ -+#define IPFS_PATH2URL_H_ -+ -+#include -+ -+namespace ipfs { -+std::string path2url(std::string path_as_string); -+} -+ -+#endif // IPFS_PATH2URL_H_ -diff --git a/third_party/ipfs_client/src/ipfs_client/redirects.cc b/third_party/ipfs_client/src/ipfs_client/redirects.cc -new file mode 100644 -index 0000000000000..b2395dcae75c0 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/redirects.cc -@@ -0,0 +1,259 @@ -+#include "redirects.h" -+ -+#include "log_macros.h" -+ -+#include -+ -+#include -+#include -+ -+namespace r = ipfs::redirects; -+using namespace std::literals; -+ -+namespace { -+// 2.4.4 Max File Size -+// The file size must not exceed 64 KiB. -+constexpr std::size_t MAX_SIZE = 64UL * 1024UL * 1024UL; -+ -+// Not including \n which terminates lines -+constexpr std::string_view WHITESPACE = " \t\f\r\v\n"; -+ -+// https://specs.ipfs.tech/http-gateways/web-redirects-file/#status -+constexpr int DEFAULT_STATUS = 301; -+// https://specs.ipfs.tech/http-gateways/web-redirects-file/#error-handling -+constexpr int PARSE_ERROR_STATUS = 500; -+} // namespace -+ -+r::Directive::Directive(std::string_view from, std::string_view to, int status) -+ : to_{to}, status_{status} { -+ SlashDelimited comp_str_s{from}; -+ std::unordered_set placeholders; -+ while (comp_str_s) { -+ auto comp_str = comp_str_s.pop(); -+ if (comp_str.empty()) { -+ LOG(ERROR) << "Got empty slash-delimited component. Should not have."; -+ return; -+ } else if (comp_str == "*") { -+ components_.emplace_back(ComponentType::SPLAT, comp_str); -+ } else if (comp_str[0] == ':') { -+ if (placeholders.insert(comp_str).second) { -+ components_.emplace_back(ComponentType::PLACEHOLDER, comp_str); -+ } else { -+ to_.assign("ERROR: Duplicate placeholder ").append(comp_str); -+ return; -+ } -+ } else { -+ components_.emplace_back(ComponentType::LITERAL, comp_str); -+ } -+ } -+} -+std::uint16_t r::Directive::rewrite(std::string& path) const { -+ auto input = SlashDelimited{path}; -+ auto result = to_; -+ auto replace = [&result](std::string_view ph, std::string_view val) { -+ std::size_t pos; -+ while ((pos = result.find(ph)) < result.size()) { -+ result.replace(pos, ph.size(), val); -+ } -+ }; -+ for (auto [type, comp_str] : components_) { -+ if (!input) { -+ VLOG(2) << "Ran out of input in [" << path -+ << "] before running out of pattern components to match against " -+ "(was looking for [" -+ << comp_str << "]. Not a match."; -+ return 0; -+ } -+ if (type == ComponentType::LITERAL) { -+ if (comp_str != input.pop()) { -+ return 0; -+ } -+ } else if (type == ComponentType::PLACEHOLDER) { -+ replace(comp_str, input.pop()); -+ } else { -+ replace(":splat"sv, input.pop_all()); -+ } -+ } -+ if (input) { -+ return 0; -+ } else { -+ path = result; -+ return status_; -+ } -+} -+std::string r::Directive::error() const { -+ if (starts_with(to_, "ERROR: ")) { -+ return to_; -+ } -+ if (status_ < 200 || status_ > 451) { -+ return "UNSUPPORTED STATUS " + std::to_string(status_); -+ } -+ if (components_.empty()) { -+ return "Empty directive pattern"; -+ } -+ if (to_.empty()) { -+ return "Empty redirect target location"; -+ } -+ if (to_.at(0) != '/' && to_.find("://") == std::string::npos) { -+ return "Location must begin with / or be a URL"; -+ } -+ return {}; -+} -+ -+std::uint16_t r::File::rewrite(std::string& missing_path) const { -+ for (auto& directive : directives_) { -+ auto status = directive.rewrite(missing_path); -+ if (status) { -+ return status; -+ } -+ } -+ return 0; -+} -+r::File::File(std::string_view to_parse) { -+ if (to_parse.size() > MAX_SIZE) { -+ error_ = "INPUT FILE TOO LARGE " + std::to_string(to_parse.size()); -+ return; -+ } -+ for (auto line_number = 1; valid() && to_parse.size(); ++line_number) { -+ auto line_end = to_parse.find('\n'); -+ auto line = to_parse.substr(0UL, line_end); -+ if (!parse_line(line, line_number)) { -+ LOG(INFO) << "Line #" << line_number << " ignored: [" << line << ']'; -+ } else if (directives_.empty()) { -+ LOG(ERROR) << "Expected to have a directive after parsing line #" -+ << line_number << ": " << line; -+ } else if (directives_.back().valid()) { -+ VLOG(1) << "Line #" << line_number << " parsed. " << line; -+ } else { -+ error_ = "FAILURE PARSING LINE # " + std::to_string(line_number); -+ error_.append(": ") -+ .append(directives_.back().error()) -+ .append(" [") -+ .append(line) -+ .push_back(']'); -+ LOG(ERROR) << error_; -+ return; -+ } -+ if (line_end < to_parse.size()) { -+ to_parse.remove_prefix(line_end + 1); -+ } else { -+ break; -+ } -+ } -+ if (directives_.empty()) { -+ error_ = "No redirection directives in _redirects"; -+ LOG(ERROR) << error_; -+ } -+} -+ -+namespace { -+std::pair parse_status(std::string_view line, -+ std::size_t col); -+} -+bool r::File::parse_line(std::string_view line, int line_number) { -+ if (line.empty()) { -+ // empty line is not a directive -+ return false; -+ } -+ auto bpos = line.find_first_not_of(WHITESPACE); -+ if (bpos == std::string_view::npos) { -+ // effectively empty line -+ return false; -+ } else if (line[bpos] == '#') { -+ // https://specs.ipfs.tech/http-gateways/web-redirects-file/#comments -+ return false; -+ } -+ auto epos = line.find_first_of(WHITESPACE, bpos); -+ if (epos == std::string_view::npos) { -+ error_ = "Parsing _redirects file: line # " + std::to_string(line_number); -+ error_ -+ .append(" , expected at least 2 tokens (from and to) for directive: [") -+ .append(line) -+ .append("], but didn't even get whitespace to end from"); -+ return false; -+ } -+ auto from = line.substr(bpos, epos - bpos); -+ bpos = line.find_first_not_of(WHITESPACE, epos); -+ if (bpos == std::string_view::npos) { -+ error_ = "Parsing _redirects file: line # " + std::to_string(line_number); -+ error_ -+ .append(" , expected at least 2 tokens (from and to) for directive: [") -+ .append(line) -+ .append("], but didn't get a to"); -+ return false; -+ } -+ epos = line.find_first_of(WHITESPACE, bpos); -+ auto to = line.substr(bpos, epos - bpos); -+ auto [status, err] = parse_status(line, epos); -+ if (err.empty()) { -+ directives_.emplace_back(from, to, status); -+ return true; -+ } else { -+ error_ = err; -+ LOG(ERROR) << "Error parsing status on line #" << line_number << " [" -+ << line << "]."; -+ return false; -+ } -+} -+ -+namespace { -+ -+std::pair parse_status(std::string_view line, -+ std::size_t col) { -+ if (col >= line.size()) { -+ VLOG(2) << " No status specified, using default."; -+ return {DEFAULT_STATUS, ""}; -+ } -+ auto b = line.find_first_not_of(WHITESPACE, col); -+ if (b >= line.size()) { -+ VLOG(2) -+ << " No status specified (line ended in whitespace), using default."; -+ return {DEFAULT_STATUS, ""}; -+ } -+ auto status_str = line.substr(b); -+ if (status_str.size() < 3) { -+ return {PARSE_ERROR_STATUS, -+ " Not enough characters for a valid status string: [" + -+ std::string{status_str} + "]."}; -+ } -+ auto good = [](int i) { return std::make_pair(i, ""s); }; -+ auto unsupported = [status_str]() { -+ return std::make_pair( -+ PARSE_ERROR_STATUS, -+ "Unsupported status specified in directive:" + std::string{status_str}); -+ }; -+ /* -+ * 200 - OK treated as a rewrite, without changing the URL in the browser. -+ * 301 - Permanent Redirect (default) -+ * 302 - Found (commonly used for Temporary Redirect) -+ * 303 - See Other (replacing PUT and POST with GET) -+ * 307 - Temporary Redirect (explicitly preserving body and HTTP method) -+ * 308 - Permanent Redirect (preserving body & method of original request) -+ * 404 - Not Found (Useful for a pretty 404 page) -+ * 410 - Gone -+ * 451 - Unavailable For Legal Reasons -+ */ -+ switch (status_str[0]) { -+ case '2': -+ return status_str == "200" ? good(200) : unsupported(); -+ case '3': -+ if (status_str[1] != '0') { -+ return unsupported(); -+ } -+ return good(300 + status_str[2] - '0'); -+ case '4': -+ switch (status_str[1]) { -+ case '0': -+ return status_str[2] == '4' ? good(404) : unsupported(); -+ case '1': -+ return status_str[2] == '0' ? good(410) : unsupported(); -+ case '5': -+ return status_str[2] == '1' ? good(451) : unsupported(); -+ default: -+ return unsupported(); -+ } -+ default: -+ return unsupported(); -+ } -+} -+} // namespace -diff --git a/third_party/ipfs_client/src/ipfs_client/redirects.h b/third_party/ipfs_client/src/ipfs_client/redirects.h -new file mode 100644 -index 0000000000000..e0b333f1de2f1 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/redirects.h -@@ -0,0 +1,41 @@ -+#ifndef IPFS_REDIRECTS_H_ -+#define IPFS_REDIRECTS_H_ -+ -+#include -+ -+#include -+#include -+#include -+ -+namespace ipfs { -+namespace redirects { -+class Directive { -+ enum class ComponentType { LITERAL, PLACEHOLDER, SPLAT }; -+ std::vector> components_; -+ std::string to_; -+ int const status_; -+ -+ public: -+ Directive(std::string_view, std::string_view, int); -+ std::uint16_t rewrite(std::string&) const; -+ std::string error() const; -+ bool valid() const { return error().empty(); } -+}; -+class File { -+ std::vector directives_; -+ std::string error_; -+ -+ public: -+ File(std::string_view to_parse); -+ -+ bool valid() const { return error().empty(); } -+ std::string const& error() const { return error_; } -+ std::uint16_t rewrite(std::string& missing_path) const; -+ -+ private: -+ bool parse_line(std::string_view, int); -+}; -+} // namespace redirects -+} // namespace ipfs -+ -+#endif // IPFS_REDIRECTS_H_ -diff --git a/third_party/ipfs_client/src/ipfs_client/response.cc b/third_party/ipfs_client/src/ipfs_client/response.cc -new file mode 100644 -index 0000000000000..411d87d1354e4 ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/response.cc -@@ -0,0 +1,16 @@ -+#include "ipfs_client/response.h" -+ -+using Self = ipfs::Response; -+ -+Self Self::PLAIN_NOT_FOUND{"text/html", static_cast(404), -+ std::string{}, std::string{}}; -+Self Self::IMMUTABLY_GONE{"text/plain", 410, -+ "Using immutable data it has been proven the " -+ "resource does not exist anywhere.", -+ std::string{}}; -+ -+Self Self::HOST_NOT_FOUND{ -+ "text/plain", Self::HOST_NOT_FOUND_STATUS, -+ "either a hostname didn't resolve a DNS TXT records for dnslink=, or we " -+ "can't find a gateway with the necessary IPNS record", -+ std::string{}}; -diff --git a/third_party/ipfs_client/src/ipfs_client/signing_key_type.cc b/third_party/ipfs_client/src/ipfs_client/signing_key_type.cc -new file mode 100644 -index 0000000000000..b6489a47b130f ---- /dev/null -+++ b/third_party/ipfs_client/src/ipfs_client/signing_key_type.cc -@@ -0,0 +1,15 @@ -+#include -+ -+#include -+ -+using T = ipfs::SigningKeyType; -+namespace n = ipfs::ipns; -+ -+// It is critically important that these 2 enumerations remain in-synch. -+// However, some headers that reference SigningKeyType need to be able to -+// compile without access to protobuf. -+static_assert(static_cast(T::RSA) == n::RSA); -+static_assert(static_cast(T::Ed25519) == n::Ed25519); -+static_assert(static_cast(T::Secp256k1) == n::Secp256k1); -+static_assert(static_cast(T::ECDSA) == n::ECDSA); -+static_assert(static_cast(T::KeyTypeCount) == n::KeyType_ARRAYSIZE); -diff --git a/third_party/ipfs_client/src/libp2p/crypto/protobuf_key.hpp b/third_party/ipfs_client/src/libp2p/crypto/protobuf_key.hpp -new file mode 100644 -index 0000000000000..459426f8c58a2 ---- /dev/null -+++ b/third_party/ipfs_client/src/libp2p/crypto/protobuf_key.hpp -@@ -0,0 +1,29 @@ -+/** -+ * Copyright Soramitsu Co., Ltd. All Rights Reserved. -+ * SPDX-License-Identifier: Apache-2.0 -+ */ -+ -+#ifndef KAGOME_PROTOBUF_KEY_HPP -+#define KAGOME_PROTOBUF_KEY_HPP -+ -+#include -+#include -+ -+#include -+ -+namespace libp2p::crypto { -+ /** -+ * Strict type for key, which is encoded into Protobuf format -+ */ -+ struct ProtobufKey : public boost::equality_comparable { -+ explicit ProtobufKey(std::vector key) : key{std::move(key)} {} -+ -+ std::vector key; -+ -+ bool operator==(const ProtobufKey &other) const { -+ return key == other.key; -+ } -+ }; -+} // namespace libp2p::crypto -+ -+#endif // KAGOME_PROTOBUF_KEY_HPP -diff --git a/third_party/ipfs_client/src/libp2p/multi/multibase_codec/codecs/base16.cc b/third_party/ipfs_client/src/libp2p/multi/multibase_codec/codecs/base16.cc -new file mode 100644 -index 0000000000000..b032cccbbdc1c ---- /dev/null -+++ b/third_party/ipfs_client/src/libp2p/multi/multibase_codec/codecs/base16.cc -@@ -0,0 +1,104 @@ -+#include -+ -+namespace b16 = ipfs::base16; -+ -+namespace { -+std::uint8_t to_i(char c); -+template -+char to_c(std::uint8_t n) { -+ if (n < 10) { -+ return n + '0'; -+ } else { -+ return n - 10 + a; -+ } -+} -+template -+std::string encode(ipfs::ByteView bytes) { -+ std::string result; -+ result.reserve(bytes.size() * 2); -+ for (auto b : bytes) { -+ auto i = to_integer(b); -+ result.push_back(to_c(i >> 4)); -+ result.push_back(to_c(i & 0xF)); -+ } -+ return result; -+} -+} // namespace -+ -+std::string b16::encodeLower(ByteView bytes) { -+ return encode<'a'>(bytes); -+} -+std::string b16::encodeUpper(ByteView bytes) { -+ return encode<'A'>(bytes); -+} -+auto b16::decode(std::string_view s) -> Decoded { -+ ByteArray result(s.size() / 2, ipfs::Byte{}); -+ for (auto i = 0U; i + 1U < s.size(); i += 2U) { -+ auto a = to_i(s[i]); -+ auto b = to_i(s[i + 1]); -+ if (a > 0xF || b > 0xF) { -+ return ipfs::unexpected{BaseError::INVALID_BASE16_INPUT}; -+ } -+ result[i / 2] = ipfs::Byte{static_cast((a << 4) | b)}; -+ } -+ if (s.size() % 2) { -+ auto a = to_i(s.back()); -+ if (a <= 0xF) { -+ result.push_back(ipfs::Byte{a}); -+ } -+ } -+ return result; -+} -+ -+namespace { -+std::uint8_t to_i(char c) { -+ switch (c) { -+ case '0': -+ return 0; -+ case '1': -+ return 1; -+ case '2': -+ return 2; -+ case '3': -+ return 3; -+ case '4': -+ return 4; -+ case '5': -+ return 5; -+ case '6': -+ return 6; -+ case '7': -+ return 7; -+ case '8': -+ return 8; -+ case '9': -+ return 9; -+ case 'a': -+ return 10; -+ case 'b': -+ return 11; -+ case 'c': -+ return 12; -+ case 'd': -+ return 13; -+ case 'e': -+ return 14; -+ case 'f': -+ return 15; -+ case 'A': -+ return 10; -+ case 'B': -+ return 11; -+ case 'C': -+ return 12; -+ case 'D': -+ return 13; -+ case 'E': -+ return 14; -+ case 'F': -+ return 15; -+ default: -+ return 0xFF; -+ } -+} -+} // namespace -diff --git a/third_party/ipfs_client/src/libp2p/multi/multibase_codec/codecs/base32.cc b/third_party/ipfs_client/src/libp2p/multi/multibase_codec/codecs/base32.cc -new file mode 100644 -index 0000000000000..36030d0b445fb ---- /dev/null -+++ b/third_party/ipfs_client/src/libp2p/multi/multibase_codec/codecs/base32.cc -@@ -0,0 +1,200 @@ -+/** -+ * Copyright Soramitsu Co., Ltd. All Rights Reserved. -+ * SPDX-License-Identifier: Apache-2.0 -+ */ -+ -+/** -+ * base32 (de)coder implementation as specified by RFC4648. -+ * -+ * Copyright (c) 2010 Adrien Kunysz -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a copy -+ * of this software and associated documentation files (the "Software"), to deal -+ * in the Software without restriction, including without limitation the rights -+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -+ * copies of the Software, and to permit persons to whom the Software is -+ * furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -+ * THE SOFTWARE. -+ **/ -+ -+#include "libp2p/multi/multibase_codec/codecs/base32.hpp" -+#include "libp2p/multi/multibase_codec/codecs/base_error.hpp" -+ -+#include -+ -+namespace libp2p::multi::detail { -+const std::string kUpperBase32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; -+const std::string kLowerBase32Alphabet = "abcdefghijklmnopqrstuvwxyz234567"; -+ -+enum Base32Mode { -+ LOWER, -+ UPPER, -+}; -+ -+int get_byte(int block) { -+ return block * 5 / 8; -+} -+ -+int get_bit(int block) { -+ return 8 - 5 - block * 5 % 8; -+} -+ -+char encode_char(unsigned char c, Base32Mode mode) { -+ if (mode == Base32Mode::UPPER) { -+ return kUpperBase32Alphabet[c & 0x1F]; // 0001 1111 -+ } -+ return kLowerBase32Alphabet[c & 0x1F]; -+} -+ -+unsigned char shift_right(uint8_t byte, int8_t offset) { -+ if (offset > 0) { -+ return byte >> offset; -+ } -+ -+ return byte << -offset; -+} -+ -+unsigned char shift_left(uint8_t byte, int8_t offset) { -+ return shift_right(byte, -offset); -+} -+ -+int encode_sequence(ipfs::span plain, -+ ipfs::span coded, -+ Base32Mode mode) { -+ for (int block = 0; block < 8; block++) { -+ int byte = get_byte(block); -+ int bit = get_bit(block); -+ -+ if (byte >= static_cast(plain.size())) { -+ return block; -+ } -+ -+ unsigned char c = shift_right(plain[byte], bit); -+ -+ if (bit < 0 && byte < static_cast(plain.size()) - 1L) { -+ c |= shift_right(plain[byte + 1], 8 + bit); -+ } -+ coded[block] = encode_char(c, mode); -+ } -+ return 8; -+} -+ -+std::string encodeBase32(ipfs::ByteView bytes, Base32Mode mode) { -+ std::string result; -+ if (bytes.size() % 5 == 0) { -+ result = std::string(bytes.size() / 5 * 8, ' '); -+ } else { -+ result = std::string((bytes.size() / 5 + 1) * 8, ' '); -+ } -+ -+ for (size_t i = 0, j = 0; i < bytes.size(); i += 5, j += 8) { -+ int n = encode_sequence( -+ ipfs::span(reinterpret_cast(&bytes[i]), -+ std::min(bytes.size() - i, 5)), -+ ipfs::span(&result[j], 8U), mode); -+ if (n < 8) { -+ result.erase(result.end() - (8 - n), result.end()); -+ } -+ } -+ -+ return result; -+} -+ -+std::string encodeBase32Upper(ipfs::ByteView bytes) { -+ return encodeBase32(bytes, Base32Mode::UPPER); -+} -+ -+std::string encodeBase32Lower(ipfs::ByteView bytes) { -+ return encodeBase32(bytes, Base32Mode::LOWER); -+} -+ -+int decode_char(unsigned char c, Base32Mode mode) { -+ char decoded_ch = -1; -+ -+ if (mode == Base32Mode::UPPER) { -+ if (c >= 'A' && c <= 'Z') { -+ decoded_ch = c - 'A'; // NOLINT -+ } -+ } else { -+ if (c >= 'a' && c <= 'z') { -+ decoded_ch = c - 'a'; // NOLINT -+ } -+ } -+ if (c >= '2' && c <= '7') { -+ decoded_ch = c - '2' + 26; // NOLINT -+ } -+ -+ return decoded_ch; -+} -+ -+ipfs::expected decode_sequence(ipfs::span coded, -+ ipfs::span plain, -+ Base32Mode mode) { -+ plain[0] = 0; -+ for (int block = 0; block < 8; block++) { -+ int bit = get_bit(block); -+ int byte = get_byte(block); -+ -+ if (block >= static_cast(coded.size())) { -+ return byte; -+ } -+ int c = decode_char(coded[block], mode); -+ if (c < 0) { -+ // return absl::InvalidArgumentError("INVALID_BASE32_INPUT"); -+ return ipfs::unexpected{BaseError::INVALID_BASE32_INPUT}; -+ } -+ -+ plain[byte] |= shift_left(c, bit); -+ if (bit < 0) { -+ plain[byte + 1] = shift_left(c, 8 + bit); -+ } -+ } -+ return 5; -+} -+ -+ipfs::expected decodeBase32( -+ std::string_view string, -+ Base32Mode mode) { -+ common::ByteArray result; -+ if (string.size() % 8 == 0) { -+ result = common::ByteArray(string.size() / 8 * 5, ipfs::Byte{0}); -+ } else { -+ result = common::ByteArray((string.size() / 8 + 1) * 5, ipfs::Byte{0}); -+ } -+ -+ for (size_t i = 0, j = 0; i < string.size(); i += 8, j += 5) { -+ auto n = decode_sequence( -+ ipfs::span(&string[i], -+ std::min(string.size() - i, 8)), -+ ipfs::span(reinterpret_cast(&result[j]), 5U), mode); -+ if (!n.has_value()) { -+ return ipfs::unexpected{n.error()}; -+ } -+ if (n.value() < 5) { -+ result.erase(result.end() - (5 - n.value()), result.end()); -+ } -+ } -+ return result; -+} -+ -+ipfs::expected decodeBase32Upper( -+ std::string_view string) { -+ return decodeBase32(string, Base32Mode::UPPER); -+} -+ -+ipfs::expected decodeBase32Lower( -+ std::string_view string) { -+ return decodeBase32(string, Base32Mode::LOWER); -+} -+ -+} // namespace libp2p::multi::detail -diff --git a/third_party/ipfs_client/src/libp2p/multi/multibase_codec/codecs/base36.cc b/third_party/ipfs_client/src/libp2p/multi/multibase_codec/codecs/base36.cc -new file mode 100644 -index 0000000000000..2f508979a004c ---- /dev/null -+++ b/third_party/ipfs_client/src/libp2p/multi/multibase_codec/codecs/base36.cc -@@ -0,0 +1,58 @@ -+#include -+ -+#include -+ -+#include -+ -+#include -+ -+namespace det = libp2p::multi::detail; -+ -+namespace { -+constexpr double kLengthRatio = 0.646240625; // log(36)/log(256) -+ -+std::int_least16_t digit_value(char digit) { -+ if (digit < '0') { -+ return -1; -+ } else if (digit <= '9') { -+ return digit - '0'; -+ } else if (digit < 'A') { -+ return -2; -+ } else if (digit <= 'Z') { -+ return (digit - 'A') + 10; -+ } else if (digit < 'a') { -+ return -3; -+ } else if (digit <= 'z') { -+ return (digit - 'a') + 10; -+ } else { -+ return -4; -+ } -+} -+int operator*(int a, ipfs::Byte b) { -+ return a * static_cast(b); -+} -+} // namespace -+ -+std::string det::encodeBase36Lower(ipfs::ByteView) { -+ std::abort(); -+} -+ -+auto det::decodeBase36(std::string_view str_b36) -+ -> ipfs::expected { -+ common::ByteArray out; -+ out.resize(std::ceil(static_cast(str_b36.size()) * kLengthRatio), -+ ipfs::Byte{}); -+ for (auto digit : str_b36) { // chunk) { -+ int val = digit_value(digit); -+ if (val < 0) { -+ return ipfs::unexpected{BaseError::INVALID_BASE36_INPUT}; -+ } -+ auto mod_byte = [&val](auto& b) { -+ val += 36 * b; -+ b = static_cast(val & 0xFF); -+ val >>= 8; -+ }; -+ std::for_each(out.rbegin(), out.rend(), mod_byte); -+ } -+ return out; -+} -diff --git a/third_party/ipfs_client/src/libp2p/multi/uvarint.cc b/third_party/ipfs_client/src/libp2p/multi/uvarint.cc -new file mode 100644 -index 0000000000000..2e6ed6eb0bada ---- /dev/null -+++ b/third_party/ipfs_client/src/libp2p/multi/uvarint.cc -@@ -0,0 +1,107 @@ -+/** -+ * Copyright Soramitsu Co., Ltd. All Rights Reserved. -+ * SPDX-License-Identifier: Apache-2.0 -+ */ -+ -+#include -+ -+namespace libp2p::multi { -+ -+UVarint::UVarint(UVarint const& rhs) : bytes_(rhs.bytes_) {} -+UVarint::UVarint(uint64_t number) { -+ do { -+ auto byte = static_cast(number) & ipfs::Byte{0x7f}; -+ number >>= 7; -+ if (number != 0) { -+ byte |= ipfs::Byte{0x80}; -+ } -+ bytes_.push_back(byte); -+ } while (number != 0); -+} -+ -+UVarint::UVarint(ipfs::ByteView varint_bytes) { -+ auto size = calculateSize(varint_bytes); -+ if (size <= varint_bytes.size()) { -+ bytes_.assign(varint_bytes.begin(), varint_bytes.begin() + size); -+ } -+} -+ -+UVarint::UVarint(ipfs::ByteView varint_bytes, size_t varint_size) -+ : bytes_(varint_bytes.begin(), varint_bytes.begin() + varint_size) {} -+ -+std::optional UVarint::create(ipfs::ByteView varint_bytes) { -+ size_t size = calculateSize(varint_bytes); -+ if (size > 0 && size <= varint_bytes.size()) { -+ return UVarint{varint_bytes, size}; -+ } -+ return {}; -+} -+ -+uint64_t UVarint::toUInt64() const { -+ uint64_t res = 0; -+ size_t index = 0; -+ for (const auto& byte : bytes_) { -+ res += static_cast((byte & ipfs::Byte{0x7f})) << index; -+ index += 7; -+ } -+ return res; -+} -+ -+ipfs::ByteView UVarint::toBytes() const { -+ return ipfs::ByteView{bytes_.data(), bytes_.size()}; -+} -+ -+std::vector const& UVarint::toVector() const { -+ return bytes_; -+} -+ -+size_t UVarint::size() const { -+ return bytes_.size(); -+} -+ -+UVarint& UVarint::operator=(UVarint const& rhs) { -+ bytes_ = rhs.bytes_; // actually OK even if &rhs == this -+ return *this; -+} -+UVarint& UVarint::operator=(uint64_t n) { -+ *this = UVarint(n); -+ return *this; -+} -+ -+bool UVarint::operator==(const UVarint& r) const { -+ return std::equal(bytes_.begin(), bytes_.end(), r.bytes_.begin(), -+ r.bytes_.end()); -+} -+ -+bool UVarint::operator!=(const UVarint& r) const { -+ return !(*this == r); -+} -+ -+bool UVarint::operator<(const UVarint& r) const { -+ return toUInt64() < r.toUInt64(); -+} -+ -+size_t UVarint::calculateSize(ipfs::ByteView varint_bytes) { -+ size_t size = 0; -+ size_t shift = 0; -+ constexpr size_t capacity = sizeof(uint64_t) * 8; -+ bool last_byte_found = false; -+ for (const auto& byte : varint_bytes) { -+ ++size; -+ std::uint_least64_t slice = to_integer(byte) & 0x7f; -+ if (shift >= capacity || ((slice << shift) >> shift) != slice) { -+ size = 0; -+ break; -+ } -+ if ((byte & ipfs::Byte{0x80}) == ipfs::Byte{0}) { -+ last_byte_found = true; -+ break; -+ } -+ shift += 7; -+ } -+ return last_byte_found ? size : 0; -+} -+ -+UVarint::~UVarint() noexcept {} -+ -+} // namespace libp2p::multi -diff --git a/third_party/ipfs_client/src/log_macros.h b/third_party/ipfs_client/src/log_macros.h -new file mode 100644 -index 0000000000000..e406429d0f280 ---- /dev/null -+++ b/third_party/ipfs_client/src/log_macros.h -@@ -0,0 +1,53 @@ -+#ifndef IPFS_LOG_MACROS_H_ -+#define IPFS_LOG_MACROS_H_ -+ -+#include -+ -+#if __has_include("base/logging.h") //In Chromium -+ -+#include "base/logging.h" -+#include "base/check_op.h" -+ -+#else // Not in Chromium -+ -+#include -+ -+#include -+ -+#define DCHECK_EQ GOOGLE_DCHECK_EQ -+#define DCHECK_GT GOOGLE_DCHECK_GT -+#define DCHECK GOOGLE_DCHECK -+#define LOG GOOGLE_LOG -+ -+#define VLOG(X) \ -+ ::google::protobuf::internal::LogFinisher() = \ -+ ::google::protobuf::internal::LogMessage( \ -+ static_cast<::google::protobuf::LogLevel>( \ -+ ::google::protobuf::LOGLEVEL_INFO - X), \ -+ __FILE__, __LINE__) -+ -+#pragma GCC diagnostic push -+#pragma GCC diagnostic ignored "-Wunused-variable" -+namespace { -+static bool is_logging_initialized = ::ipfs::log::IsInitialized(); -+} -+#pragma GCC diagnostic pop -+ -+#endif //Chromium in-tree check -+ -+#define L_VAR(X) LOG(INFO) << "VAR " << #X << "='" << (X) << '\''; -+ -+inline bool starts_with(std::string_view full_text, std::string_view prefix) { -+ if (prefix.size() > full_text.size()) { -+ return false; -+ } -+ return full_text.substr(0UL, prefix.size()) == prefix; -+} -+inline bool ends_with(std::string_view full_text, std::string_view suffix) { -+ if (suffix.size() > full_text.size()) { -+ return false; -+ } -+ return full_text.substr(full_text.size() - suffix.size()) == suffix; -+} -+ -+#endif // IPFS_LOG_MACROS_H_ -diff --git a/third_party/ipfs_client/src/smhasher/MurmurHash3.cc b/third_party/ipfs_client/src/smhasher/MurmurHash3.cc -new file mode 100644 -index 0000000000000..677aedf1d7a55 ---- /dev/null -+++ b/third_party/ipfs_client/src/smhasher/MurmurHash3.cc -@@ -0,0 +1,424 @@ -+//----------------------------------------------------------------------------- -+// MurmurHash3 was written by Austin Appleby, and is placed in the public -+// domain. The author hereby disclaims copyright to this source code. -+ -+// Note - The x86 and x64 versions do _not_ produce the same results, as the -+// algorithms are optimized for their respective platforms. You can still -+// compile and run any of them on any platform, but your performance with the -+// non-native version will be less than optimal. -+ -+#include "smhasher/MurmurHash3.h" -+#ifdef __GNUG__ -+#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" -+#endif -+ -+#ifdef __clang__ -+#pragma clang diagnostic ignored "-Wimplicit-fallthrough" -+#endif -+//----------------------------------------------------------------------------- -+// Platform-specific functions and macros -+ -+// Microsoft Visual Studio -+ -+#if defined(_MSC_VER) -+ -+#define FORCE_INLINE __forceinline -+ -+#include -+ -+#define ROTL32(x, y) _rotl(x, y) -+#define ROTL64(x, y) _rotl64(x, y) -+ -+#define BIG_CONSTANT(x) (x) -+ -+// Other compilers -+ -+#else // defined(_MSC_VER) -+ -+#define FORCE_INLINE inline __attribute__((always_inline)) -+ -+inline uint32_t rotl32(uint32_t x, int8_t r) { -+ return (x << r) | (x >> (32 - r)); -+} -+ -+inline uint64_t rotl64(uint64_t x, int8_t r) { -+ return (x << r) | (x >> (64 - r)); -+} -+ -+#define ROTL32(x, y) rotl32(x, y) -+#define ROTL64(x, y) rotl64(x, y) -+ -+#define BIG_CONSTANT(x) (x##LLU) -+ -+#endif // !defined(_MSC_VER) -+ -+//----------------------------------------------------------------------------- -+// Block read - if your platform needs to do endian-swapping or can only -+// handle aligned reads, do the conversion here -+ -+FORCE_INLINE uint32_t getblock32(const uint32_t* p, int i) { -+ return p[i]; -+} -+ -+FORCE_INLINE uint64_t getblock64(const uint64_t* p, int i) { -+ return p[i]; -+} -+ -+//----------------------------------------------------------------------------- -+// Finalization mix - force all bits of a hash block to avalanche -+ -+FORCE_INLINE uint32_t fmix32(uint32_t h) { -+ h ^= h >> 16; -+ h *= 0x85ebca6b; -+ h ^= h >> 13; -+ h *= 0xc2b2ae35; -+ h ^= h >> 16; -+ -+ return h; -+} -+ -+//---------- -+ -+FORCE_INLINE uint64_t fmix64(uint64_t k) { -+ k ^= k >> 33; -+ k *= BIG_CONSTANT(0xff51afd7ed558ccd); -+ k ^= k >> 33; -+ k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53); -+ k ^= k >> 33; -+ -+ return k; -+} -+ -+//----------------------------------------------------------------------------- -+ -+void MurmurHash3_x86_32(const void* key, int len, uint32_t seed, void* out) { -+ const uint8_t* data = (const uint8_t*)key; -+ const int nblocks = len / 4; -+ -+ uint32_t h1 = seed; -+ -+ const uint32_t c1 = 0xcc9e2d51; -+ const uint32_t c2 = 0x1b873593; -+ -+ //---------- -+ // body -+ -+ const uint32_t* blocks = (const uint32_t*)(data + nblocks * 4); -+ -+ for (int i = -nblocks; i; i++) { -+ uint32_t k1 = getblock32(blocks, i); -+ -+ k1 *= c1; -+ k1 = ROTL32(k1, 15); -+ k1 *= c2; -+ -+ h1 ^= k1; -+ h1 = ROTL32(h1, 13); -+ h1 = h1 * 5 + 0xe6546b64; -+ } -+ -+ //---------- -+ // tail -+ -+ const uint8_t* tail = (const uint8_t*)(data + nblocks * 4); -+ -+ uint32_t k1 = 0; -+ -+ switch (len & 3) { -+ case 3: -+ k1 ^= tail[2] << 16; -+ case 2: -+ k1 ^= tail[1] << 8; -+ case 1: -+ k1 ^= tail[0]; -+ k1 *= c1; -+ k1 = ROTL32(k1, 15); -+ k1 *= c2; -+ h1 ^= k1; -+ }; -+ -+ //---------- -+ // finalization -+ -+ h1 ^= len; -+ -+ h1 = fmix32(h1); -+ -+ *(uint32_t*)out = h1; -+} -+ -+//----------------------------------------------------------------------------- -+ -+void MurmurHash3_x86_128(const void* key, -+ const int len, -+ uint32_t seed, -+ void* out) { -+ const uint8_t* data = (const uint8_t*)key; -+ const int nblocks = len / 16; -+ -+ uint32_t h1 = seed; -+ uint32_t h2 = seed; -+ uint32_t h3 = seed; -+ uint32_t h4 = seed; -+ -+ const uint32_t c1 = 0x239b961b; -+ const uint32_t c2 = 0xab0e9789; -+ const uint32_t c3 = 0x38b34ae5; -+ const uint32_t c4 = 0xa1e38b93; -+ -+ //---------- -+ // body -+ -+ const uint32_t* blocks = (const uint32_t*)(data + nblocks * 16); -+ -+ for (int i = -nblocks; i; i++) { -+ uint32_t k1 = getblock32(blocks, i * 4 + 0); -+ uint32_t k2 = getblock32(blocks, i * 4 + 1); -+ uint32_t k3 = getblock32(blocks, i * 4 + 2); -+ uint32_t k4 = getblock32(blocks, i * 4 + 3); -+ -+ k1 *= c1; -+ k1 = ROTL32(k1, 15); -+ k1 *= c2; -+ h1 ^= k1; -+ -+ h1 = ROTL32(h1, 19); -+ h1 += h2; -+ h1 = h1 * 5 + 0x561ccd1b; -+ -+ k2 *= c2; -+ k2 = ROTL32(k2, 16); -+ k2 *= c3; -+ h2 ^= k2; -+ -+ h2 = ROTL32(h2, 17); -+ h2 += h3; -+ h2 = h2 * 5 + 0x0bcaa747; -+ -+ k3 *= c3; -+ k3 = ROTL32(k3, 17); -+ k3 *= c4; -+ h3 ^= k3; -+ -+ h3 = ROTL32(h3, 15); -+ h3 += h4; -+ h3 = h3 * 5 + 0x96cd1c35; -+ -+ k4 *= c4; -+ k4 = ROTL32(k4, 18); -+ k4 *= c1; -+ h4 ^= k4; -+ -+ h4 = ROTL32(h4, 13); -+ h4 += h1; -+ h4 = h4 * 5 + 0x32ac3b17; -+ } -+ -+ //---------- -+ // tail -+ -+ const uint8_t* tail = (const uint8_t*)(data + nblocks * 16); -+ -+ uint32_t k1 = 0; -+ uint32_t k2 = 0; -+ uint32_t k3 = 0; -+ uint32_t k4 = 0; -+ -+ switch (len & 15) { -+ case 15: -+ k4 ^= tail[14] << 16; -+ case 14: -+ k4 ^= tail[13] << 8; -+ case 13: -+ k4 ^= tail[12] << 0; -+ k4 *= c4; -+ k4 = ROTL32(k4, 18); -+ k4 *= c1; -+ h4 ^= k4; -+ -+ case 12: -+ k3 ^= tail[11] << 24; -+ case 11: -+ k3 ^= tail[10] << 16; -+ case 10: -+ k3 ^= tail[9] << 8; -+ case 9: -+ k3 ^= tail[8] << 0; -+ k3 *= c3; -+ k3 = ROTL32(k3, 17); -+ k3 *= c4; -+ h3 ^= k3; -+ -+ case 8: -+ k2 ^= tail[7] << 24; -+ case 7: -+ k2 ^= tail[6] << 16; -+ case 6: -+ k2 ^= tail[5] << 8; -+ case 5: -+ k2 ^= tail[4] << 0; -+ k2 *= c2; -+ k2 = ROTL32(k2, 16); -+ k2 *= c3; -+ h2 ^= k2; -+ -+ case 4: -+ k1 ^= tail[3] << 24; -+ case 3: -+ k1 ^= tail[2] << 16; -+ case 2: -+ k1 ^= tail[1] << 8; -+ case 1: -+ k1 ^= tail[0] << 0; -+ k1 *= c1; -+ k1 = ROTL32(k1, 15); -+ k1 *= c2; -+ h1 ^= k1; -+ }; -+ -+ //---------- -+ // finalization -+ -+ h1 ^= len; -+ h2 ^= len; -+ h3 ^= len; -+ h4 ^= len; -+ -+ h1 += h2; -+ h1 += h3; -+ h1 += h4; -+ h2 += h1; -+ h3 += h1; -+ h4 += h1; -+ -+ h1 = fmix32(h1); -+ h2 = fmix32(h2); -+ h3 = fmix32(h3); -+ h4 = fmix32(h4); -+ -+ h1 += h2; -+ h1 += h3; -+ h1 += h4; -+ h2 += h1; -+ h3 += h1; -+ h4 += h1; -+ -+ ((uint32_t*)out)[0] = h1; -+ ((uint32_t*)out)[1] = h2; -+ ((uint32_t*)out)[2] = h3; -+ ((uint32_t*)out)[3] = h4; -+} -+ -+//----------------------------------------------------------------------------- -+ -+void MurmurHash3_x64_128(const void* key, -+ const int len, -+ const uint32_t seed, -+ void* out) { -+ const uint8_t* data = (const uint8_t*)key; -+ const int nblocks = len / 16; -+ -+ uint64_t h1 = seed; -+ uint64_t h2 = seed; -+ -+ const uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5); -+ const uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f); -+ -+ //---------- -+ // body -+ -+ const uint64_t* blocks = (const uint64_t*)(data); -+ -+ for (int i = 0; i < nblocks; i++) { -+ uint64_t k1 = getblock64(blocks, i * 2 + 0); -+ uint64_t k2 = getblock64(blocks, i * 2 + 1); -+ -+ k1 *= c1; -+ k1 = ROTL64(k1, 31); -+ k1 *= c2; -+ h1 ^= k1; -+ -+ h1 = ROTL64(h1, 27); -+ h1 += h2; -+ h1 = h1 * 5 + 0x52dce729; -+ -+ k2 *= c2; -+ k2 = ROTL64(k2, 33); -+ k2 *= c1; -+ h2 ^= k2; -+ -+ h2 = ROTL64(h2, 31); -+ h2 += h1; -+ h2 = h2 * 5 + 0x38495ab5; -+ } -+ -+ //---------- -+ // tail -+ -+ const uint8_t* tail = (const uint8_t*)(data + nblocks * 16); -+ -+ uint64_t k1 = 0; -+ uint64_t k2 = 0; -+ -+ switch (len & 15) { -+ case 15: -+ k2 ^= ((uint64_t)tail[14]) << 48; -+ case 14: -+ k2 ^= ((uint64_t)tail[13]) << 40; -+ case 13: -+ k2 ^= ((uint64_t)tail[12]) << 32; -+ case 12: -+ k2 ^= ((uint64_t)tail[11]) << 24; -+ case 11: -+ k2 ^= ((uint64_t)tail[10]) << 16; -+ case 10: -+ k2 ^= ((uint64_t)tail[9]) << 8; -+ case 9: -+ k2 ^= ((uint64_t)tail[8]) << 0; -+ k2 *= c2; -+ k2 = ROTL64(k2, 33); -+ k2 *= c1; -+ h2 ^= k2; -+ -+ case 8: -+ k1 ^= ((uint64_t)tail[7]) << 56; -+ case 7: -+ k1 ^= ((uint64_t)tail[6]) << 48; -+ case 6: -+ k1 ^= ((uint64_t)tail[5]) << 40; -+ case 5: -+ k1 ^= ((uint64_t)tail[4]) << 32; -+ case 4: -+ k1 ^= ((uint64_t)tail[3]) << 24; -+ case 3: -+ k1 ^= ((uint64_t)tail[2]) << 16; -+ case 2: -+ k1 ^= ((uint64_t)tail[1]) << 8; -+ case 1: -+ k1 ^= ((uint64_t)tail[0]) << 0; -+ k1 *= c1; -+ k1 = ROTL64(k1, 31); -+ k1 *= c2; -+ h1 ^= k1; -+ }; -+ -+ //---------- -+ // finalization -+ -+ h1 ^= len; -+ h2 ^= len; -+ -+ h1 += h2; -+ h2 += h1; -+ -+ h1 = fmix64(h1); -+ h2 = fmix64(h2); -+ -+ h1 += h2; -+ h2 += h1; -+ -+ ((uint64_t*)out)[0] = h1; -+ ((uint64_t*)out)[1] = h2; -+} -+ -+//----------------------------------------------------------------------------- -diff --git a/third_party/ipfs_client/src/vocab/byte_view.cc b/third_party/ipfs_client/src/vocab/byte_view.cc -new file mode 100644 -index 0000000000000..f71dcaa0181f1 ---- /dev/null -+++ b/third_party/ipfs_client/src/vocab/byte_view.cc -@@ -0,0 +1,2 @@ -+#include "vocab/byte_view.h" -+ -diff --git a/third_party/ipfs_client/src/vocab/slash_delimited.cc b/third_party/ipfs_client/src/vocab/slash_delimited.cc -new file mode 100644 -index 0000000000000..c81ae5823c867 ---- /dev/null -+++ b/third_party/ipfs_client/src/vocab/slash_delimited.cc -@@ -0,0 +1,117 @@ -+#include -+ -+#include "log_macros.h" -+ -+#include -+ -+#if __has_include() -+#include -+#define HAS_STRINGPIECE 1 -+#endif -+ -+using Self = ipfs::SlashDelimited; -+ -+Self::SlashDelimited(std::string_view unowned) : remainder_{unowned} {} -+ -+Self::operator bool() const { -+ return remainder_.find_first_not_of("/") < remainder_.size(); -+} -+std::string_view Self::pop() { -+ if (remainder_.empty()) { -+ return remainder_; -+ } -+ auto slash = remainder_.find('/'); -+ if (slash == std::string_view::npos) { -+ return pop_all(); -+ } -+ auto result = remainder_.substr(0UL, slash); -+ remainder_.remove_prefix(slash + 1); -+ if (result.empty()) { -+ return pop(); -+ } else { -+ return result; -+ } -+} -+std::string_view Self::pop_all() { -+ auto result = remainder_; -+ remainder_ = ""; -+ return result; -+} -+std::string_view Self::pop_n(std::size_t n) { -+ std::size_t a = 0UL; -+ while (n) { -+ auto slash = remainder_.find('/', a); -+ auto non_slash = remainder_.find_first_not_of("/", a); -+ if (slash == std::string_view::npos) { -+ auto result = remainder_; -+ remainder_ = ""; -+ return result; -+ } -+ if (non_slash < slash) { -+ --n; -+ } -+ a = slash + 1UL; -+ } -+ auto result = remainder_.substr(0UL, a - 1); -+ remainder_.remove_prefix(a); -+ return result; -+} -+std::string_view Self::peek_back() const { -+ auto s = remainder_; -+ while (!s.empty() && s.back() == '/') { -+ s.remove_suffix(1); -+ } -+ if (s.empty()) { -+ return s; -+ } -+ auto last_slash = s.find_last_of('/'); -+ if (last_slash < remainder_.size()) { -+ return remainder_.substr(last_slash + 1); -+ } else { -+ return s; -+ } -+} -+std::string Self::pop_back() { -+ auto non_slash = remainder_.find_last_not_of('/'); -+ if (non_slash == std::string_view::npos) { -+ return ""; -+ } -+ auto slash = remainder_.find_last_of('/', non_slash); -+ std::string rv; -+ if (slash == std::string_view::npos) { -+ rv = remainder_; -+ remainder_ = ""; -+ } else { -+ rv = remainder_.substr(slash + 1, non_slash - slash); -+ remainder_ = remainder_.substr(0UL, slash); -+ } -+ return rv; -+} -+ -+std::ostream& operator<<(std::ostream& str, ipfs::SlashDelimited const& sd) { -+ return str << sd.to_view(); -+} -+ -+#if __has_include() -+#include -+ -+using namespace google::protobuf::internal; -+using namespace google::protobuf; -+ -+#if PROTOBUF_VERSION >= 3020000 -+#include -+LogMessage& operator<<(LogMessage& str, ipfs::SlashDelimited const& sd) { -+ return str << sd.to_view(); -+} -+#elif __has_include() -+#include -+LogMessage& operator<<(LogMessage& str, ipfs::SlashDelimited const& sd) { -+ return str << StringPiece{sd.to_view()}; -+} -+#else -+LogMessage& operator<<(LogMessage& str, ipfs::SlashDelimited const& sd) { -+ return str << std::string{sd.to_view()}; -+} -+#endif -+ -+#endif -diff --git a/third_party/ipfs_client/unix_fs.proto b/third_party/ipfs_client/unix_fs.proto -new file mode 100644 -index 0000000000000..9d117a4d66bdf ---- /dev/null -+++ b/third_party/ipfs_client/unix_fs.proto -@@ -0,0 +1,32 @@ -+syntax = "proto2"; -+option optimize_for = LITE_RUNTIME; -+package ipfs.unix_fs; -+ -+message Data { -+ enum DataType { -+ Raw = 0; -+ Directory = 1; -+ File = 2; -+ Metadata = 3; -+ Symlink = 4; -+ HAMTShard = 5; -+ } -+ -+ required DataType Type = 1; -+ optional bytes Data = 2; -+ optional uint64 filesize = 3; -+ repeated uint64 blocksizes = 4; -+ optional uint64 hashType = 5; -+ optional uint64 fanout = 6; -+ optional uint32 mode = 7; -+ optional UnixTime mtime = 8; -+} -+ -+message Metadata { -+ optional string MimeType = 1; -+} -+ -+message UnixTime { -+ required int64 Seconds = 1; -+ optional fixed32 FractionalNanoseconds = 2; -+} -diff --git a/url/BUILD.gn b/url/BUILD.gn -index c525c166979d6..ce2b1ae43c0a7 100644 ---- a/url/BUILD.gn -+++ b/url/BUILD.gn -@@ -5,6 +5,7 @@ - import("//build/buildflag_header.gni") - import("//testing/libfuzzer/fuzzer_test.gni") - import("//testing/test.gni") -+import("//third_party/ipfs_client/args.gni") - import("features.gni") - - import("//build/config/cronet/config.gni") -@@ -67,6 +68,7 @@ component("url") { - public_deps = [ - "//base", - "//build:robolectric_buildflags", -+ "//third_party/ipfs_client:ipfs_buildflags", - ] - - configs += [ "//build/config/compiler:wexit_time_destructors" ] -@@ -89,6 +91,11 @@ component("url") { - public_configs = [ "//third_party/jdk" ] - } - -+ if (enable_ipfs) { -+ sources += [ "url_canon_ipfs.cc" ] -+ deps += [ "//third_party/ipfs_client:ipfs_client" ] -+ } -+ - if (is_win) { - # Don't conflict with Windows' "url.dll". - output_name = "url_lib" -diff --git a/url/url_canon.h b/url/url_canon.h -index 913b3685c6fec..3c3c55e580564 100644 ---- a/url/url_canon.h -+++ b/url/url_canon.h -@@ -792,6 +792,23 @@ bool CanonicalizeMailtoURL(const char16_t* spec, - CanonOutput* output, - Parsed* new_parsed); - -+COMPONENT_EXPORT(URL) -+bool CanonicalizeIpfsURL(const char* spec, -+ int spec_len, -+ const Parsed& parsed, -+ SchemeType scheme_type, -+ CharsetConverter* query_converter, -+ CanonOutput* output, -+ Parsed* new_parsed); -+COMPONENT_EXPORT(URL) -+bool CanonicalizeIpfsURL(const char16_t* spec, -+ int spec_len, -+ const Parsed& parsed, -+ SchemeType scheme_type, -+ CharsetConverter* query_converter, -+ CanonOutput* output, -+ Parsed* new_parsed); -+ - // Part replacer -------------------------------------------------------------- - - // Internal structure used for storing separate strings for each component. -diff --git a/url/url_canon_ipfs.cc b/url/url_canon_ipfs.cc -new file mode 100644 -index 0000000000000..9511e3f5e6f5c ---- /dev/null -+++ b/url/url_canon_ipfs.cc -@@ -0,0 +1,55 @@ -+#include "url_canon_internal.h" -+ -+#include -+#include -+ -+#include -+ -+bool url::CanonicalizeIpfsURL(const char* spec, -+ int spec_len, -+ const Parsed& parsed, -+ SchemeType scheme_type, -+ CharsetConverter* charset_converter, -+ CanonOutput* output, -+ Parsed* output_parsed) { -+ if ( spec_len < 1 || !spec ) { -+ return false; -+ } -+ if ( parsed.host.len < 1 ) { -+ return false; -+ } -+ std::string_view cid_str{ spec + parsed.host.begin, static_cast(parsed.host.len) }; -+ auto cid = ipfs::Cid(cid_str); -+ if ( !cid.valid() ) { -+ cid = ipfs::id_cid::forText( std::string{cid_str} + " is not a valid CID." ); -+ } -+ auto as_str = cid.to_string(); -+ if ( as_str.empty() ) { -+ return false; -+ } -+ std::string stdurl{ spec, static_cast(parsed.host.begin) }; -+ stdurl.append( as_str ); -+ stdurl.append( spec + parsed.host.end(), spec_len - parsed.host.end() ); -+ spec = stdurl.data(); -+ spec_len = static_cast(stdurl.size()); -+ Parsed parsed_input; -+ ParseStandardURL(spec, spec_len, &parsed_input); -+ return CanonicalizeStandardURL( -+ spec, spec_len, -+ parsed_input, -+ scheme_type, -+ charset_converter, -+ output, output_parsed -+ ); -+} -+bool url::CanonicalizeIpfsURL(const char16_t* spec, -+ int spec_len, -+ const Parsed& parsed, -+ SchemeType scheme_type, -+ CharsetConverter* query_converter, -+ CanonOutput* output, -+ Parsed* new_parsed) { -+ RawCanonOutput<2048> as8; -+ ConvertUTF16ToUTF8(spec, spec_len, &as8); -+ return CanonicalizeIpfsURL(as8.data(), as8.length(), parsed, scheme_type, query_converter, output, new_parsed); -+} -diff --git a/url/url_util.cc b/url/url_util.cc -index 9258cfcfada47..daf10e4c3b741 100644 ---- a/url/url_util.cc -+++ b/url/url_util.cc -@@ -277,6 +277,12 @@ bool DoCanonicalize(const CHAR* spec, - charset_converter, output, - output_parsed); - -+ } else if (DoCompareSchemeComponent(spec, scheme, "ipfs")) { -+ // Switch multibase away from case-sensitive ones before continuing canonicalization. -+ ParseStandardURL(spec, spec_len, &parsed_input); -+ success = CanonicalizeIpfsURL(spec, spec_len, parsed_input, scheme_type, -+ charset_converter, output, output_parsed); -+ - } else if (DoIsStandard(spec, scheme, &scheme_type)) { - // All "normal" URLs. - ParseStandardURL(spec, spec_len, &parsed_input); - diff --git a/library/BUILD.gn.in b/library/BUILD.gn.in index 61d4338a..15c2434e 100644 --- a/library/BUILD.gn.in +++ b/library/BUILD.gn.in @@ -19,13 +19,9 @@ if (enable_ipfs) { static_library("ipfs_client") { if (is_nacl) { sources = cxx_sources - [ - "src/ipfs_client/gw/gateway_request.cc", - "src/ipfs_client/gw/gateway_http_requestor.cc", - "src/ipfs_client/gw/requestor.cc", "src/ipfs_client/ipld/dag_node.cc", "src/ipfs_client/ipns_names.cc", "src/ipfs_client/ipns_record.cc", - "src/ipfs_client/logger.cc", "src/ipfs_client/pb_dag.cc", "src/ipfs_client/signing_key_type.cc", ] diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 66bdec9b..183dde2b 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -145,13 +145,6 @@ add_custom_target(upload_library WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" ) -if(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU) - include(CodeCoverage OPTIONAL RESULT_VARIABLE INCLUDED_COVERAGE) - if(INCLUDED_COVERAGE) - append_coverage_compiler_flags_to_target(ipfs_client_covered) - endif() -endif() - file(GLOB_RECURSE unit_test_sources CONFIGURE_DEPENDS @@ -216,14 +209,34 @@ if(GTest_FOUND AND IN_WORKSPACE) COMMAND "${CMAKE_COMMAND}" -E echo "Tests up-to-date" ) endif() - if(LCOV_PATH) - setup_target_for_coverage_lcov( - NAME cov - DEPENDENCIES run_tests - EXECUTABLE $ - ) - else() - message(WARNING "Cannot provide coverage report without lcov") + if(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU) + include(CodeCoverage OPTIONAL RESULT_VARIABLE INCLUDED_COVERAGE) + if(INCLUDED_COVERAGE) + append_coverage_compiler_flags_to_target(ipfs_client_covered) + append_coverage_compiler_flags_to_target(unit_test_runner) + if(CMAKE_BUILD_TYPE STREQUAL Debug) + target_compile_options(ipfs_client_covered + PRIVATE + ${coverage_debug_flags} + ) + endif() + if(CMAKE_BUILD_TYPE STREQUAL Debug) + target_compile_options(unit_test_runner + PRIVATE + ${coverage_debug_flags} + ) + endif() + endif() + if(LCOV_PATH) + setup_target_for_coverage_lcov( + NAME cov + DEPENDENCIES run_tests + EXECUTABLE $ + EXCLUDE ${unit_test_sources} + ) + else() + message(WARNING "Cannot provide coverage report without lcov") + endif() endif() else() message(WARNING "Not running tests - no gtest available.") diff --git a/library/include/ipfs_client/context_api.h b/library/include/ipfs_client/context_api.h index dc46f903..f269f6bd 100644 --- a/library/include/ipfs_client/context_api.h +++ b/library/include/ipfs_client/context_api.h @@ -79,8 +79,9 @@ class ContextApi : public std::enable_shared_from_this { std::optional> Hash(HashType, ByteView data); virtual std::optional GetGateway(std::size_t index) const = 0; - virtual unsigned GetGatewayRate(std::string_view); + virtual unsigned GetGatewayRate(std::string_view) = 0; virtual void SetGatewayRate(std::string_view, unsigned); + virtual void AddGateway(std::string_view) = 0; protected: std::unordered_map> hashers_; diff --git a/library/include/ipfs_client/gw/block_request_splitter.h b/library/include/ipfs_client/gw/block_request_splitter.h index 0f308a99..e49b88ef 100644 --- a/library/include/ipfs_client/gw/block_request_splitter.h +++ b/library/include/ipfs_client/gw/block_request_splitter.h @@ -3,12 +3,17 @@ #include "requestor.h" +#include +#include + namespace ipfs { class ContextApi; } namespace ipfs::gw { class BlockRequestSplitter final : public Requestor { + std::array recent_provider_requests; + std::size_t old_provider_request = 0UL; HandleOutcome handle(RequestPtr) override; std::string_view name() const override; }; diff --git a/library/include/ipfs_client/ipfs_request.h b/library/include/ipfs_client/ipfs_request.h index eda8bdfa..0b699906 100644 --- a/library/include/ipfs_client/ipfs_request.h +++ b/library/include/ipfs_client/ipfs_request.h @@ -16,14 +16,12 @@ class IpfsRequest { private: std::string path_; Finisher callback_; - std::size_t waiting_ = 0UL; public: IpfsRequest(std::string path, Finisher); SlashDelimited path() const { return SlashDelimited{path_}; } void finish(Response& r); void till_next(std::size_t); - bool ready_after(); void new_path(std::string_view); static std::shared_ptr fromUrl(std::string url, Finisher); diff --git a/library/include/ipfs_client/pb_dag.h b/library/include/ipfs_client/pb_dag.h index d6cb392f..48355152 100644 --- a/library/include/ipfs_client/pb_dag.h +++ b/library/include/ipfs_client/pb_dag.h @@ -1,19 +1,13 @@ #ifndef IPFS_PB_DAG_H_ #define IPFS_PB_DAG_H_ -#if __has_include() -#include -#include -#else -#include "ipfs_client/pb_dag.pb.h" -#include "ipfs_client/unix_fs.pb.h" -#endif - #include "cid.h" #include +#include #include +#include #include namespace ipfs { @@ -47,7 +41,7 @@ class PbDag { PbDag(Cid const& cid, std::string_view bytes); PbDag(PbDag const&); - PbDag& operator=(PbDag const&) = default; +// PbDag& operator=(PbDag const&) = default; PbDag(); ///< Construct an invalid block @@ -78,12 +72,6 @@ class PbDag { std::string const& unparsed() const; ///< Original bytes (with protobuf bits) - /*! - * \brief Accessor for all UnixFS data as a protobuf object - * \deprecated - */ - unix_fs::Data const& fsdata() const { return fsdata_; } - Cid const& cid() const; ///< Getter for Content IDentifier bool cid_matches_data(ContextApi&) const; ///< Basic validation @@ -92,29 +80,12 @@ class PbDag { std::vector binary_hash(ContextApi&, HashType = HashType::INVALID) const; - /*! - * \brief Iterate through the links of this UnixFS node - * \param foo - Called for each link with (name, cid) - * should return converts-to-bool - * name is convertable from std::string const& - * cid is convertable from std::string&& - */ - template - void List(Functor foo) const { - for (auto& link : node_.links()) { - // protobuf uses string for binary data, too - auto hash = ipfs::ByteView{ - reinterpret_cast(link.hash().data()), - link.hash().size()}; - if (!foo(link.name(), LinkCid(hash))) { - break; - } - } - } + void List(std::function) const; + std::optional Fanout() const; private: - pb_dag::PBNode node_; - unix_fs::Data fsdata_; + struct Data; + std::unique_ptr pimpl_; bool valid_ = false; bool fs_node_ = false; std::string mime_ = {}; diff --git a/library/include/ipfs_client/test_context.h b/library/include/ipfs_client/test_context.h index 9216fb0c..9e4e2325 100644 --- a/library/include/ipfs_client/test_context.h +++ b/library/include/ipfs_client/test_context.h @@ -121,7 +121,6 @@ class TestContext final : public ContextApi { GOOGLE_LOG(ERROR) << "TODO\n"; return true; } - std::optional GetGateway(std::size_t) const; std::vector gateways_; boost::asio::io_context& io_; @@ -135,14 +134,19 @@ class TestContext final : public ContextApi { TestContext(boost::asio::io_context& io); ~TestContext() noexcept override; void DnsResults(std::string&, ares_txt_reply&); + std::optional GetGateway(std::size_t) const; + void AddGateway(std::string_view) override; + void SetGatewayRate(std::string_view, unsigned); + unsigned int GetGatewayRate(std::string_view) override; + std::vector::iterator FindGateway(std::string_view); }; -inline std::shared_ptr start_default( - boost::asio::io_context& io) { +inline std::pair, std::shared_ptr> +start_default(boost::asio::io_context& io) { auto api = std::make_shared(io); auto gl = Gateways::DefaultGateways(); auto rtor = gw::default_requestor(gl, {}, api); auto orc = std::make_shared(rtor, api); - return orc; + return {api, orc}; } } // namespace ipfs diff --git a/library/include/vocab/html_escape.h b/library/include/vocab/html_escape.h index 60339ad7..30cdf3af 100644 --- a/library/include/vocab/html_escape.h +++ b/library/include/vocab/html_escape.h @@ -3,7 +3,7 @@ #include -constexpr inline std::string_view html_escape(char& c) { +constexpr std::string_view html_escape(char& c) { switch (c) { case '"': return """; diff --git a/library/src/ipfs_client/context_api.cc b/library/src/ipfs_client/context_api.cc index f58a062d..b9fc82ae 100644 --- a/library/src/ipfs_client/context_api.cc +++ b/library/src/ipfs_client/context_api.cc @@ -20,7 +20,7 @@ auto Self::Hash(HashType ht, ByteView data) return it->second->hash(data); } -unsigned int Self::GetGatewayRate(std::string_view) { - return 120; -} void Self::SetGatewayRate(std::string_view, unsigned int) {} +void Self::AddGateway(std::string_view gw) { + SetGatewayRate(gw, 60U); +} \ No newline at end of file diff --git a/library/src/ipfs_client/crypto/openssl_sha2_256.cc b/library/src/ipfs_client/crypto/openssl_sha2_256.cc index ff5c7a24..162818f9 100644 --- a/library/src/ipfs_client/crypto/openssl_sha2_256.cc +++ b/library/src/ipfs_client/crypto/openssl_sha2_256.cc @@ -6,9 +6,10 @@ using Self = ipfs::crypto::OpensslSha2_256; #if HAS_OPENSSL_SHA +#include #include -Self::~OpensslSha2_256() {} +Self::~OpensslSha2_256() noexcept {} auto Self::hash(ipfs::ByteView data) -> std::optional> { SHA256_CTX ctx; if (1 != SHA256_Init(&ctx)) { diff --git a/library/src/ipfs_client/gateways.cc b/library/src/ipfs_client/gateways.cc index a5b7bf83..31a72ace 100644 --- a/library/src/ipfs_client/gateways.cc +++ b/library/src/ipfs_client/gateways.cc @@ -100,19 +100,26 @@ auto ipfs::Gateways::DefaultGateways() -> GatewayList { } return result; } - return {{"http://localhost:8080/"s, 854}, - {"https://jcsl.hopto.org/"s, 791}, - {"https://ipfs.io/"s, 720}, - {"https://human.mypinata.cloud/"s, 622}, + return {{"http://localhost:8080/"s, 5994}, + {"https://jcsl.hopto.org/"s, 751}, + {"https://ipfs.io/"s, 722}, {"https://gateway.ipfs.io/"s, 606}, - {"https://dweb.link/"s, 373}, - {"https://gateway.pinata.cloud/"s, 324}, - {"https://ipfs.runfission.com/"s, 289}, - {"https://ipfs.joaoleitao.org/"s, 259}, - {"https://nftstorage.link/"s, 175}, - {"https://w3s.link/"s, 91}, - {"https://permaweb.eu.org/"s, 63}, - {"https://ipfs.fleek.co/"s, 27}, + {"https://human.mypinata.cloud/"s, 377}, + {"https://dag.w3s.link/", 235}, + {"https://ipfs.runfission.com/"s, 142}, + {"https://dweb.link/"s, 103}, + {"https://gateway.pinata.cloud/"s, 62}, + {"https://permaweb.eu.org/"s, 60}, + {"https://cesginc.com/", 59}, + {"https://http.f02620.devtty.eu/", 47}, + {"https://ipfs.joaoleitao.org/"s, 46}, + {"https://f010479.twinquasar.io/", 35}, + {"https://ipfs.omnicloudstorage.com:9443/", 28}, + {"http://f02095132.datasetcreators.com/", 23}, + {"https://data.filstorage.io/", 11}, + {"https://nftstorage.link/"s, 7}, + {"https://ipfs.fleek.co/"s, 6}, + {"https://w3s.link/"s, 5}, {"https://hardbin.com/"s, 4}, {"https://ipfs.jpu.jp/"s, 3}, {"https://ipfs.soul-network.com/"s, 2}, diff --git a/library/src/ipfs_client/gateways_unittest.cc b/library/src/ipfs_client/gateways_unittest.cc index 1786f812..273795b6 100644 --- a/library/src/ipfs_client/gateways_unittest.cc +++ b/library/src/ipfs_client/gateways_unittest.cc @@ -8,7 +8,7 @@ TEST(GatewaysTest, DefaultListMeetsBasicGuidelines) { EXPECT_GT(dg.size(), 9U); EXPECT_EQ(dg.at(0).prefix, "http://localhost:8080/"); for (auto i = 1U; i < dg.size(); ++i) { - EXPECT_EQ(dg.at(i).prefix.substr(0, 8), std::string{"https://"}); + EXPECT_EQ(dg.at(i).prefix.substr(0, 4), std::string{"http"}); EXPECT_EQ(dg.at(i).prefix.back(), '/'); // I considered asserting the number of /s and :s, but that _is_ allowed to // change diff --git a/library/src/ipfs_client/gw/block_request_splitter.cc b/library/src/ipfs_client/gw/block_request_splitter.cc index 3ded788f..7769b3ed 100644 --- a/library/src/ipfs_client/gw/block_request_splitter.cc +++ b/library/src/ipfs_client/gw/block_request_splitter.cc @@ -2,6 +2,8 @@ #include +#include + using Self = ipfs::gw::BlockRequestSplitter; std::string_view Self::name() const { @@ -17,14 +19,17 @@ auto Self::handle(ipfs::gw::RequestPtr r) -> HandleOutcome { br->path.clear(); forward(br); } - /* - { + auto it = std::find(recent_provider_requests.begin(), + recent_provider_requests.end(), r->main_param); + if (recent_provider_requests.end() == it) { + auto i = old_provider_request % recent_provider_requests.size(); + recent_provider_requests[i] = r->main_param; + ++old_provider_request; auto pr = std::make_shared(*r); pr->type = Type::Providers; pr->path.clear(); pr->affinity.clear(); forward(pr); } - */ return HandleOutcome::NOT_HANDLED; } \ No newline at end of file diff --git a/library/src/ipfs_client/gw/block_request_splitter_unittest.cc b/library/src/ipfs_client/gw/block_request_splitter_unittest.cc index 5f742282..8ff6eaf5 100644 --- a/library/src/ipfs_client/gw/block_request_splitter_unittest.cc +++ b/library/src/ipfs_client/gw/block_request_splitter_unittest.cc @@ -20,10 +20,7 @@ struct Recording : public g::Requestor { }; } // namespace -TEST(BlockRequestSplitterTest, not_yet_implemented) { - // Until the gateway requestors know how to handle things like Car and - // Providers, Don't bother forwarding them. When you DO make those changes, - // change BRS, which will force you to change this test. +TEST(BlockRequestSplitterTest, split2three) { Tested tested; auto rec = std::make_shared(); tested.or_else(rec); @@ -33,20 +30,19 @@ TEST(BlockRequestSplitterTest, not_yet_implemented) { req->path = "path"; req->parallel = 123; tested.request(req); - EXPECT_EQ(rec->requests_received.size(), 2U); + EXPECT_EQ(rec->requests_received.size(), 3U); EXPECT_TRUE(rec->requests_received.at(0)->type == g::Type::Block) << static_cast(rec->requests_received.at(0)->type); - // EXPECT_TRUE(rec->requests_received.at(1)->type == g::Type::Providers) << - // static_cast(rec->requests_received.at(0)->type); - // EXPECT_TRUE(rec->requests_received.at(2)->type == g::Type::Car) << - // static_cast(rec->requests_received.at(0)->type); EXPECT_EQ(rec->requests_received.at(0)->main_param, "cid"); - // EXPECT_EQ(rec->requests_received.at(1)->main_param, "cid"); - // EXPECT_EQ(rec->requests_received.at(2)->main_param, "cid"); - // EXPECT_EQ(rec->requests_received.at(0)->path, ""); EXPECT_EQ(rec->requests_received.at(0)->path, ""); - EXPECT_EQ(rec->requests_received.at(1)->path, "path"); - EXPECT_EQ(rec->requests_received.at(0)->parallel, 123); - // EXPECT_EQ(rec->requests_received.at(1)->parallel, 123); - // EXPECT_EQ(rec->requests_received.at(2)->parallel, 123); + + EXPECT_TRUE(rec->requests_received.at(1)->type == g::Type::Providers) + << static_cast(rec->requests_received.at(2)->type); + EXPECT_EQ(rec->requests_received.at(1)->main_param, "cid"); + EXPECT_EQ(rec->requests_received.at(1)->path, ""); + + EXPECT_TRUE(rec->requests_received.at(2)->type == g::Type::Car) + << static_cast(rec->requests_received.at(2)->type); + EXPECT_EQ(rec->requests_received.at(2)->main_param, "cid"); + EXPECT_EQ(rec->requests_received.at(2)->path, "path"); } diff --git a/library/src/ipfs_client/gw/default_requestor.cc b/library/src/ipfs_client/gw/default_requestor.cc index e58c4dc4..e9cb18b5 100644 --- a/library/src/ipfs_client/gw/default_requestor.cc +++ b/library/src/ipfs_client/gw/default_requestor.cc @@ -2,7 +2,6 @@ #include #include -#include #include #include #include diff --git a/library/src/ipfs_client/gw/gateway_http_requestor.h b/library/src/ipfs_client/gw/gateway_http_requestor.h deleted file mode 100644 index 8c61bef8..00000000 --- a/library/src/ipfs_client/gw/gateway_http_requestor.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef IPFS_GATEWAY_HTTP_REQUESTOR_H_ -#define IPFS_GATEWAY_HTTP_REQUESTOR_H_ - -#include -#include -#include - -#include -#include -#include - -namespace ipfs::gw { -class GatewayHttpRequestor final : public Requestor { - std::string prefix_; - int strength_; - std::unordered_map seen_; - std::set aff_good_, aff_bad_; - std::set typ_good_, typ_bad_; - int pending_ = 0; - int extra_seconds_ = 0; - - HandleOutcome handle(RequestPtr) override; - std::string_view name() const override; - int target(GatewayRequest const&) const; - - public: - GatewayHttpRequestor(std::string gateway_prefix, - int strength, - std::shared_ptr); - ~GatewayHttpRequestor() noexcept override; -}; -} // namespace ipfs::gw - -#endif // IPFS_GATEWAY_HTTP_REQUESTOR_H_ diff --git a/library/src/ipfs_client/gw/gateway_request.cc b/library/src/ipfs_client/gw/gateway_request.cc index a4335a3c..8d05f34f 100644 --- a/library/src/ipfs_client/gw/gateway_request.cc +++ b/library/src/ipfs_client/gw/gateway_request.cc @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -106,15 +107,15 @@ std::string_view Self::accept() const { short Self::timeout_seconds() const { switch (type) { case Type::DnsLink: - return 32; + return 16; case Type::Block: - return 64; + return 32; case Type::Providers: - return 128; - case Type::Car: - return 256; + return 33; case Type::Ipns: - return 512; + return 64; + case Type::Car: + return 65; case Type::Identity: case Type::Zombie: return 0; @@ -185,11 +186,11 @@ std::optional Self::max_response_size() const { return 0; case Type::Providers: // This one's tricky. - // One could easily guess a pracitical limit to the size of a Peer, - // and the spec says it SHOULD be limited to 100 peers. + // One could easily guess a practical limit to the size of a Peer's + // json, and the spec says it SHOULD be limited to 100 peers. // But there's no guaranteed limits. A peer could have an unlimited // number of multiaddrs. And they're allowed to throw in arbitrary - // fields I'm supposed to ignore. So in theory it could be infinitely + // fields I'm supposed to ignore. So in theory it could be arbitrarily // large. return std::nullopt; } @@ -297,7 +298,7 @@ bool Self::RespondSuccessfully(std::string_view bytes, break; } case Type::Providers: - LOG(WARNING) << "TODO - handle responses to providers requests."; + providers::ProcessResponse(bytes, *api); break; case Type::Zombie: LOG(WARNING) << "Responding to a zombie is ill-advised."; @@ -306,13 +307,15 @@ bool Self::RespondSuccessfully(std::string_view bytes, LOG(ERROR) << "TODO " << static_cast(type); } if (success) { + LOG(INFO) << "Request " << this->debug_string() + << " was successful. Calling hooks and finishing."; for (auto& hook : bytes_received_hooks) { hook(bytes); } bytes_received_hooks.clear(); orchestrator_->build_response(dependent); + type = Type::Zombie; } - type = Type::Zombie; return success; } void Self::Hook(std::function f) { @@ -327,3 +330,5 @@ bool Self::PartiallyRedundant() const { } return orchestrator_->has_key(main_param); } + +#include diff --git a/library/src/ipfs_client/gw/gateway_state.cc b/library/src/ipfs_client/gw/gateway_state.cc index 6e0f5951..8064960b 100644 --- a/library/src/ipfs_client/gw/gateway_state.cc +++ b/library/src/ipfs_client/gw/gateway_state.cc @@ -41,12 +41,14 @@ unsigned int& Self::current_bucket() { return sent_counts[last_hist_update % sent_counts.size()]; } void Self::hit(GatewayRequest const& req) { - std::clog << "GatewayState::hit " << static_cast(this) << ' ' - << static_cast(&req) << std::endl; request_type_success.at(static_cast(req.type))++; affinity_success[req.affinity]++; } bool Self::miss(GatewayRequest const& req) { request_type_success.at(static_cast(req.type))--; + slowness++; return affinity_success[req.affinity]-- >= 0; } +void Self::timed_out() { + slowness += 10; +} \ No newline at end of file diff --git a/library/src/ipfs_client/gw/gateway_state.h b/library/src/ipfs_client/gw/gateway_state.h index 7adf7072..4d21c909 100644 --- a/library/src/ipfs_client/gw/gateway_state.h +++ b/library/src/ipfs_client/gw/gateway_state.h @@ -18,6 +18,7 @@ class GatewayState { std::size_t total_sent = 0UL; std::time_t last_hist_update; unsigned& current_bucket(); + long slowness = 0; public: GatewayState(); @@ -28,6 +29,8 @@ class GatewayState { void just_sent_one(); void hit(GatewayRequest const&); bool miss(GatewayRequest const&); + void timed_out(); + long extra_ms() { return slowness; } }; } // namespace ipfs::gw diff --git a/library/src/ipfs_client/gw/multi_gateway_requestor.cc b/library/src/ipfs_client/gw/multi_gateway_requestor.cc index 2d1aa39f..5cb4a68d 100644 --- a/library/src/ipfs_client/gw/multi_gateway_requestor.cc +++ b/library/src/ipfs_client/gw/multi_gateway_requestor.cc @@ -32,9 +32,11 @@ bool Self::Process(RequestPtr const& req) { return false; } auto state_iter = state_.begin(); - auto config_idx = 0UL; - std::vector> candidates; + auto config_idx = 0U; + using Candidate = std::tuple; + std::vector candidates; auto bored = 0U; + Candidate over_rate = {0, {}, nullptr}; while (auto gw = api_->GetGateway(config_idx++)) { if (state_iter == state_.end() || state_iter->first > gw->prefix) { VLOG(2) << "A new gateway has entered the chat: " << gw->prefix << '=' @@ -55,9 +57,12 @@ bool Self::Process(RequestPtr const& req) { // VLOG(2) << "Not going to resend " << req->debug_string() << " to " // << gw->prefix << " as it has already failed us."; } else if (state_iter->second.over_rate(gw->rate)) { - // VLOG(2) << "Not considering " << gw->prefix - // << " at the moment as it's over its rate limit " << - // gw->rate; + auto score = state_iter->second.score(*req, gw->rate); + if (std::get<0>(over_rate) <= score) { + VLOG(2) << gw->prefix + << " may be over-rate, but keep it as a fallback."; + over_rate = std::make_tuple(score, gw->prefix, &(state_iter->second)); + } } else { candidates.push_back({state_iter->second.score(*req, gw->rate), gw->prefix, &(state_iter->second)}); @@ -67,13 +72,18 @@ bool Self::Process(RequestPtr const& req) { } std::advance(state_iter, 1); } + if (std::get<2>(over_rate)) { + if (candidates.empty()) { + LOG(INFO) << "Overburdened."; + } + candidates.push_back(over_rate); + } if (candidates.empty() && config_idx <= req->failures.size()) { - LOG(ERROR) << "Request has failed on every gateway I have:" - << req->debug_string(); + LOG(ERROR) << "Run out of gateways to send this to:" << req->debug_string(); forward(req); return false; } - auto to_send = std::max(bored / 2, 3U); + auto to_send = std::max(bored / 3UL, 2UL); std::sort(candidates.begin(), candidates.end(), std::greater{}); for (auto& [score, prefix, state] : candidates) { DoSend(req, prefix, *state); @@ -92,6 +102,9 @@ void Self::DoSend(RequestPtr req, std::string const& gw, GatewayState& state) { << req->debug_string(); return; } + if (state.extra_ms()) { + desc->timeout_seconds += state.extra_ms() / 1000L + 1L; + } auto timeout_threshold = ch::system_clock::now() + ch::seconds(desc->timeout_seconds ? desc->timeout_seconds : 300) - @@ -114,6 +127,7 @@ void Self::HandleResponse(HttpRequestDescription const& desc, bool timed_out) { if (req->type == Type::Zombie || (req->PartiallyRedundant() && req->type == Type::Block)) { + VLOG(1) << "Request has finished:" << req->debug_string(); return; } auto i = state_.find(gw); @@ -124,37 +138,50 @@ void Self::HandleResponse(HttpRequestDescription const& desc, LOG(ERROR) << "No content-type header?"; } else if (desc.accept.size() && ct.find(desc.accept) == std::string::npos) { - LOG(WARNING) << "Requested with Accept: " << desc.accept - << " but received response with content-type: " << ct; - } else if (!req->RespondSuccessfully(body, api_)) { - LOG(ERROR) << "Got an unuseful response from " << gw - << " forwarding request " << req->debug_string() - << " to next requestor."; + VLOG(2) << "Requested with Accept: " << desc.accept + << " but received response with content-type: " << ct; + return; + } + if (!req->RespondSuccessfully(body, api_)) { + LOG(ERROR) << "Got an unuseful response from " << gw << " for request " + << req->debug_string(); } else { + LOG(INFO) << "Response from " << gw << " to " << req->debug_string() + << " was successful & useful - progress made."; if (state_.end() != i) { i->second.hit(*req); } auto rpm = api_->GetGatewayRate(gw); + LOG(INFO) << "Rate for " << gw << " _WAS_ " << rpm + << " and is about to go up."; if (rpm < 15) { - api_->SetGatewayRate(gw, rpm * 2 + 1); - } else { - api_->SetGatewayRate(gw, rpm + 1); + rpm *= 2; } + rpm += 2; + api_->SetGatewayRate(gw, rpm); return; } } auto rpm = api_->GetGatewayRate(gw); if (status == 408 || status == 504 || status == 429 || status == 110 || timed_out) { - VLOG(1) << gw << " timed out."; - if (rpm > 9) { - api_->SetGatewayRate(gw, rpm - 4); - } else if (rpm) { - api_->SetGatewayRate(gw, 0U); + LOG(ERROR) << gw << " timed out on request " << req->debug_string(); + if (req->type == Type::Block) { + if (state_.end() != i) { + i->second.timed_out(); + } + if (rpm > 60U) { + api_->SetGatewayRate(gw, rpm - 9); + } else if (rpm) { + api_->SetGatewayRate(gw, rpm - 1); + } } + } else { + VLOG(1) << "Gateway " << gw << " failed request: " << req->debug_string(); + req->failures.insert(gw); } - req->failures.insert(gw); - if (state_.end() != i && i->second.miss(*req) && rpm) { + if (state_.end() != i && i->second.miss(*req) && rpm && + req->type == Type::Block) { api_->SetGatewayRate(gw, rpm - 1); } Process(req); diff --git a/library/src/ipfs_client/gw/providers_response.cc b/library/src/ipfs_client/gw/providers_response.cc new file mode 100644 index 00000000..26ba7b69 --- /dev/null +++ b/library/src/ipfs_client/gw/providers_response.cc @@ -0,0 +1,86 @@ +#include "providers_response.h" + +#include +#include + +#include + +#include "log_macros.h" + +using namespace std::literals; + +namespace prov = ipfs::providers; + +namespace { +std::string MultiaddrToGatewayPrefix(ipfs::SlashDelimited ma) { + auto addr_proto = ma.pop(); + VLOG(2) + << "Protocol expected to be one of ip4|ip6|dnsaddr|dns|dns4|dns6 , is:" + << addr_proto; + auto host = ma.pop(); + auto tcp = ma.pop(); + DCHECK_EQ(tcp, "tcp"); + auto port = ma.pop(); + auto app_proto = ma.pop(); + DCHECK_EQ(app_proto.substr(0, 4), "http"); + std::string rv{app_proto}; + rv.append("://").append(host).append(":").append(port).append("/").append( + ma.to_view()); + return rv; +} + +void ParseProvider(ipfs::DagJsonValue const& provider, ipfs::ContextApi& api) { + auto proto = provider["Protocol"sv]; + if (!proto) { + // Perhaps Scheme == peer. Not an error, but not used as of now. + return; + } + if (auto proto_name = proto->get_if_string()) { + if (proto_name.value() != "transport-ipfs-gateway-http") { + // Perhaps transport-bitswap or transport-graphsync-filecoinv1. + // Not an error, but not used here yet. + return; + } + } else { + LOG(ERROR) << ".Providers[x].Protocol was not a string?"; + return; + } + auto addrs = provider["Addrs"]; + if (!addrs) { + LOG(WARNING) << "Gateway with no addrs"; + return; + } + auto handle_addr = [&api](ipfs::DagJsonValue const& addr) { + if (auto s = addr.get_if_string()) { + auto gw_pre = MultiaddrToGatewayPrefix(ipfs::SlashDelimited{s.value()}); + LOG(INFO) << "'" << *s << "' -> '" << gw_pre << "'."; + api.AddGateway(gw_pre); + } else { + LOG(ERROR) << ".Providers[x].Addrs[x] is not a string"; + } + }; + if (!addrs->iterate_list(handle_addr)) { + LOG(ERROR) << ".Providers[x].Addrs is not a list"; + } +} +} // namespace + +void prov::ProcessResponse(std::string_view json_str, ContextApi& api) { + auto parsed = api.ParseJson(json_str); + if (!parsed) { + LOG(ERROR) + << "Response to routing/v1 providers request did not parse as JSON: " + << json_str; + return; + } + auto list = (*parsed)["Providers"]; + if (!list) { + LOG(ERROR) << "Response to routing/v1 providers request did not contain a " + "top-level key 'Providers': " + << json_str; + return; + } + if (!list->iterate_list([&api](auto& p) { ParseProvider(p, api); })) { + LOG(ERROR) << ".Providers was not a list."; + } +} diff --git a/library/src/ipfs_client/gw/providers_response.h b/library/src/ipfs_client/gw/providers_response.h new file mode 100644 index 00000000..5f261390 --- /dev/null +++ b/library/src/ipfs_client/gw/providers_response.h @@ -0,0 +1,14 @@ +#ifndef IPFS_CHROMIUM_PROVIDERS_RESPONSE_H +#define IPFS_CHROMIUM_PROVIDERS_RESPONSE_H + +#include + +namespace ipfs { +class ContextApi; +} + +namespace ipfs::providers { +void ProcessResponse(std::string_view json_str, ContextApi& api); +} + +#endif // IPFS_CHROMIUM_PROVIDERS_RESPONSE_H diff --git a/library/src/ipfs_client/gw/terminating_requestor.cc b/library/src/ipfs_client/gw/terminating_requestor.cc index 791ffd84..dd66c712 100644 --- a/library/src/ipfs_client/gw/terminating_requestor.cc +++ b/library/src/ipfs_client/gw/terminating_requestor.cc @@ -15,9 +15,10 @@ auto Self::handle(ipfs::gw::RequestPtr r) -> HandleOutcome { } else if (r->parallel) { return HandleOutcome::PENDING; } else { - VLOG(2) << "Out of options, giving up on gateway request " - << r->debug_string(); + LOG(ERROR) << "Out of options, giving up on gateway request " + << r->debug_string(); definitive_failure(r); + r->type = Type::Zombie; return HandleOutcome::DONE; } } diff --git a/library/src/ipfs_client/ipfs_request.cc b/library/src/ipfs_client/ipfs_request.cc index 90222a0b..a7367067 100644 --- a/library/src/ipfs_client/ipfs_request.cc +++ b/library/src/ipfs_client/ipfs_request.cc @@ -19,24 +19,13 @@ std::shared_ptr Self::fromUrl(std::string url, ipfs::IpfsRequest::Finisher return std::make_shared(std::move(url), std::move(f)); } -void Self::till_next(std::size_t w) { - waiting_ = w; -} void Self::finish(ipfs::Response& r) { - if (waiting_) { - if (--waiting_) { - return; - } - } callback_(*this, r); // TODO - cancel other gw req pointing into this callback_ = [](auto& q, auto&) { VLOG(2) << "IPFS request " << q.path().pop_all() << " satisfied multiply"; }; } -bool Self::ready_after() { - return waiting_ == 0 || 0 == --waiting_; -} void Self::new_path(std::string_view sv) { path_.assign(sv); } \ No newline at end of file diff --git a/library/src/ipfs_client/ipld/dag_node.cc b/library/src/ipfs_client/ipld/dag_node.cc index 0abe598b..d6ffd400 100644 --- a/library/src/ipfs_client/ipld/dag_node.cc +++ b/library/src/ipfs_client/ipld/dag_node.cc @@ -120,8 +120,8 @@ std::shared_ptr Node::fromBlock(ipfs::PbDag const& block) { result = std::make_shared(); break; case PbDag::Type::HAMTShard: - if (block.fsdata().has_fanout()) { - result = std::make_shared(block.fsdata().fanout()); + if (auto fanout = block.Fanout()) { + result = std::make_shared(fanout.value()); } else { result = std::make_shared(); } diff --git a/library/src/ipfs_client/ipns_record_unittest.cc b/library/src/ipfs_client/ipns_record_unittest.cc index 182d9803..f8135196 100644 --- a/library/src/ipfs_client/ipns_record_unittest.cc +++ b/library/src/ipfs_client/ipns_record_unittest.cc @@ -76,7 +76,8 @@ struct Api final : public i::ContextApi { std::optional GetGateway(std::size_t) const { return std::nullopt; } - + unsigned GetGatewayRate(std::string_view) { return 120U; } + void AddGateway(std::string_view) {} virtual ~Api() noexcept {} }; } // namespace diff --git a/library/src/ipfs_client/orchestrator.cc b/library/src/ipfs_client/orchestrator.cc index 45a4bec9..8af67bab 100644 --- a/library/src/ipfs_client/orchestrator.cc +++ b/library/src/ipfs_client/orchestrator.cc @@ -19,7 +19,8 @@ Self::Orchestrator(std::shared_ptr requestor, } void Self::build_response(std::shared_ptr req) { - if (!req || !req->ready_after()) { + if (!req) { + LOG(ERROR) << "Can't build a response to NULL."; return; } auto req_path = req->path(); @@ -31,8 +32,8 @@ void Self::build_response(std::shared_ptr req) { build_response(req); } } else { - VLOG(2) << "Requesting root " << affinity << " resolve path " - << req_path.to_string(); + LOG(INFO) << "Requesting root " << affinity << " resolve path " + << req_path.to_string(); auto root = it->second->rooted(); if (root != it->second) { it->second = root; @@ -91,7 +92,6 @@ void Self::from_tree(std::shared_ptr req, req->finish(Response::IMMUTABLY_GONE); } else { auto& mps = std::get(result).ipfs_abs_paths_; - req->till_next(mps.size()); for (auto& mp : mps) { VLOG(2) << "Attempt to resolve " << relative_path << " for " << req->path() << " leads to request for " << mp; diff --git a/library/src/ipfs_client/orchestrator_unittest.cc b/library/src/ipfs_client/orchestrator_unittest.cc index a0578c03..d2d87db8 100644 --- a/library/src/ipfs_client/orchestrator_unittest.cc +++ b/library/src/ipfs_client/orchestrator_unittest.cc @@ -75,6 +75,8 @@ struct MockApi final : public ipfs::ContextApi { cbors.erase(cbors.begin()); return std::move(r); } + unsigned GetGatewayRate(std::string_view) { return 120U; } + void AddGateway(std::string_view) {} }; struct TestRequestor final : public ig::Requestor { TestRequestor() { api_ = std::make_shared(); } diff --git a/library/src/ipfs_client/pb_dag.cc b/library/src/ipfs_client/pb_dag.cc index 86cbb42d..dcb859a9 100644 --- a/library/src/ipfs_client/pb_dag.cc +++ b/library/src/ipfs_client/pb_dag.cc @@ -6,7 +6,13 @@ #include -#include +#if __has_include() +#include +#include +#else +#include "ipfs_client/pb_dag.pb.h" +#include "ipfs_client/unix_fs.pb.h" +#endif namespace { std::string get_bytes(std::string const& s) { @@ -57,21 +63,28 @@ std::pair InitBlock(ipfs::MultiCodec c, } } // namespace -ipfs::PbDag::PbDag(Cid const& c, std::istream& s) : cid_(c) { - std::tie(valid_, fs_node_) = InitBlock(c.codec(), s, node_, fsdata_); +struct ipfs::PbDag::Data { + pb_dag::PBNode node_; + unix_fs::Data fsdata_; +}; + +ipfs::PbDag::PbDag(Cid const& c, std::istream& s) + : pimpl_{std::make_unique()}, cid_(c) { + std::tie(valid_, fs_node_) = + InitBlock(c.codec(), s, pimpl_->node_, pimpl_->fsdata_); } ipfs::PbDag::PbDag(Cid const& c, ByteView s) - : cid_(c), + : pimpl_{std::make_unique()}, + cid_(c), original_bytes_(reinterpret_cast(s.data()), s.size()) { std::tie(valid_, fs_node_) = - InitBlock(c.codec(), original_bytes_, node_, fsdata_); + InitBlock(c.codec(), original_bytes_, pimpl_->node_, pimpl_->fsdata_); } ipfs::PbDag::PbDag(Cid const& c, std::string_view s) : PbDag(c, as_bytes(s)) {} ipfs::PbDag::PbDag(PbDag const& rhs) - : node_(rhs.node_), - fsdata_(rhs.fsdata_), + : pimpl_(std::make_unique(*rhs.pimpl_)), valid_(rhs.valid_), fs_node_(rhs.fs_node_), mime_(rhs.mime_), @@ -94,28 +107,29 @@ auto ipfs::PbDag::type() const -> Type { return Type::NonFs; } if (is_file()) { - if (fsdata_.blocksizes_size() > 0) { + if (pimpl_->fsdata_.blocksizes_size() > 0) { return Type::File; } else { return Type::FileChunk; } } - if (fsdata_.type()) { - return static_cast(fsdata_.type()); + if (pimpl_->fsdata_.type()) { + return static_cast(pimpl_->fsdata_.type()); } return Type::Invalid; } bool ipfs::PbDag::is_file() const { - return valid() && fs_node_ && fsdata_.type() == unix_fs::Data_DataType_File; + return valid() && fs_node_ && + pimpl_->fsdata_.type() == unix_fs::Data_DataType_File; } std::string const& ipfs::PbDag::chunk_data() const { - return fsdata_.data(); + return pimpl_->fsdata_.data(); } std::string const& ipfs::PbDag::unparsed() const { - return node_.data(); + return pimpl_->node_.data(); } auto ipfs::PbDag::cid() const -> Cid const& { @@ -168,6 +182,23 @@ std::vector ipfs::PbDag::binary_hash(ContextApi& api, } } +void ipfs::PbDag::List( + std::function foo) const { + for (auto& link : pimpl_->node_.links()) { + // protobuf uses string for binary data, too + auto hash = as_bytes(link.hash()); + if (!foo(link.name(), LinkCid(hash))) { + break; + } + } +} +std::optional ipfs::PbDag::Fanout() const { + if (pimpl_ && fs_node_ && pimpl_->fsdata_.has_fanout()) { + return pimpl_->fsdata_.fanout(); + } + return std::nullopt; +} + std::ostream& operator<<(std::ostream& s, ipfs::PbDag::Type t) { switch (t) { case ipfs::PbDag::Type::Raw: diff --git a/library/src/ipfs_client/test_context.cc b/library/src/ipfs_client/test_context.cc index 56c885ea..2f0da360 100644 --- a/library/src/ipfs_client/test_context.cc +++ b/library/src/ipfs_client/test_context.cc @@ -110,30 +110,32 @@ void Self::CAresProcess() { io_.post([this]() { CAresProcess(); }); } } - +namespace http = boost::beast::http; class HttpSession : public std::enable_shared_from_this { using tcp = boost::asio::ip::tcp; + boost::asio::io_context& ioc_; + boost::asio::strand strand_; tcp::resolver resolver_; boost::asio::ssl::context& ssl_ctx_; boost::beast::ssl_stream stream_; boost::beast::flat_buffer buffer_; // (Must persist between reads) - boost::beast::http::request req_; + http::request req_; ipfs::ContextApi::HttpCompleteCallback cb_; int expiry_seconds_ = 91; std::string host_, port_, target_; ipfs::HttpRequestDescription desc_; static std::map resolutions_; std::string parsed_host_; - boost::beast::http::response_parser - response_parser_; - std::optional> - res_; + http::response_parser response_parser_; + std::optional> res_; + std::shared_ptr prev; void fail(boost::beast::error_code ec, char const* what) { - GOOGLE_LOG(WARNING) << what << ": " << ec.value() << ' ' << ec.message() - << " URL:" << desc_.url << " HOST:" << host_ - << " PORT:" << port_ << " TARGET:" << target_; - cb_(500, "", [](auto) { return std::string{}; }); + GOOGLE_LOG(INFO) << what << ": " << ec.value() << ' ' << ec.message() + << " URL:" << desc_.url << " HOST:" << host_ + << " PORT:" << port_ << " TARGET:" << target_; + auto status = ec.value() == 1 ? 408 : 500; + cb_(status, "", [](auto) { return std::string{}; }); } std::string parse_url() { ipfs::SlashDelimited ss{desc_.url}; @@ -163,9 +165,11 @@ class HttpSession : public std::enable_shared_from_this { boost::asio::ssl::context& ssc, ipfs::HttpRequestDescription& desc, ipfs::ContextApi::HttpCompleteCallback cb) - : resolver_(boost::asio::make_strand(ioc)), + : ioc_{ioc}, + strand_{boost::asio::make_strand(ioc)}, + resolver_(strand_), ssl_ctx_(ssc), - stream_(boost::asio::make_strand(ioc), ssc), + stream_(strand_, ssc), cb_{cb}, desc_{desc} { if (auto sz = desc_.max_response_size) { @@ -192,24 +196,29 @@ class HttpSession : public std::enable_shared_from_this { } req_.version(11); - req_.method(boost::beast::http::verb::get); + req_.method(http::verb::get); req_.target(target_); - req_.set(boost::beast::http::field::host, parsed_host_); + req_.set(http::field::host, parsed_host_); if (desc_.accept.size()) { // std::clog << "Setting Accept: " << desc_.accept << '\n'; req_.set("Accept", desc_.accept); } + extend_time(); + auto me = shared_from_this(); if (resolution().empty()) { GOOGLE_LOG(DEBUG) << "Starting " << desc_.url << " with a host resolution of " << host_ << ':' << port_; - extend_time(); resolver_.async_resolve( host_, port_, - boost::beast::bind_front_handler(&HttpSession::on_resolve, - shared_from_this())); + boost::beast::bind_front_handler(&HttpSession::on_resolve, me)); } else { - on_resolve({}, resolution()); + auto do_connect = [me]() { + boost::beast::get_lowest_layer(me->stream_) + .async_connect(me->resolution(), boost::beast::bind_front_handler( + &HttpSession::on_connect, me)); + }; + boost::asio::defer(strand_, do_connect); } } void on_resolve(boost::beast::error_code ec, @@ -219,10 +228,9 @@ class HttpSession : public std::enable_shared_from_this { resolution() = results; for (auto& ep : results) { GOOGLE_LOG(DEBUG) << desc_.url << " Resolved " << host_ - << ", now connecting to " - << req_[boost::beast::http::field::host] << " aka " - << ep.host_name() << ':' << ep.service_name() << " for " - << target_; + << ", now connecting to " << req_[http::field::host] + << " aka " << ep.host_name() << ':' << ep.service_name() + << " for " << target_; } extend_time(); boost::beast::get_lowest_layer(stream_).async_connect( @@ -244,10 +252,9 @@ class HttpSession : public std::enable_shared_from_this { shared_from_this())); } else { GOOGLE_LOG(DEBUG) << "Skipping the SSL handshake because port=" << port_; - boost::beast::http::async_write( - boost::beast::get_lowest_layer(stream_), req_, - boost::beast::bind_front_handler(&HttpSession::on_write, - shared_from_this())); + http::async_write(boost::beast::get_lowest_layer(stream_), req_, + boost::beast::bind_front_handler(&HttpSession::on_write, + shared_from_this())); } } bool use_ssl() const { return port_ == "443" || port_ == "https"; } @@ -261,10 +268,9 @@ class HttpSession : public std::enable_shared_from_this { if (ec) return fail(ec, "handshake"); extend_time(); - boost::beast::http::async_write( - stream_, req_, - boost::beast::bind_front_handler(&HttpSession::on_write, - shared_from_this())); + http::async_write(stream_, req_, + boost::beast::bind_front_handler(&HttpSession::on_write, + shared_from_this())); } void on_write(boost::beast::error_code ec, std::size_t) { @@ -273,15 +279,14 @@ class HttpSession : public std::enable_shared_from_this { GOOGLE_LOG(TRACE) << desc_.url << " request written."; extend_time(); if (use_ssl()) { - boost::beast::http::async_read( - stream_, buffer_, response_parser_, - boost::beast::bind_front_handler(&HttpSession::on_read, - shared_from_this())); + http::async_read(stream_, buffer_, response_parser_, + boost::beast::bind_front_handler(&HttpSession::on_read, + shared_from_this())); } else { - boost::beast::http::async_read( - boost::beast::get_lowest_layer(stream_), buffer_, response_parser_, - boost::beast::bind_front_handler(&HttpSession::on_read, - shared_from_this())); + http::async_read(boost::beast::get_lowest_layer(stream_), buffer_, + response_parser_, + boost::beast::bind_front_handler(&HttpSession::on_read, + shared_from_this())); } } @@ -310,29 +315,50 @@ class HttpSession : public std::enable_shared_from_this { } } if (res_->result_int() / 100 == 3) { - auto loc = (*res_)[boost::beast::http::field::location]; - if (loc.size()) { - desc_.url = loc; - close(); + auto loc = (*res_)[http::field::location]; + if (loc.empty()) { + LOG(ERROR) << "No Location header given for a redirect response."; + } else if (redirect_count(loc) >= 0xFF) { + LOG(ERROR) << "Too many redirects!! Giving up on " << loc << '\n'; + } else { GOOGLE_LOG(WARNING) << "Redirecting to " << loc << " aka " << desc_.url; - res_ = boost::beast::http::response{}; - req_.set(boost::beast::http::field::host, parse_url()); - req_.target(target_); - on_resolve({}, resolution()); - return; + auto desc = desc_; + desc.url = loc; + auto next = std::make_shared(ioc_, ssl_ctx_, desc, cb_); + next->prev = shared_from_this(); + next->run(); } + close(); + return; } - auto content_type = (*res_)[boost::beast::http::field::content_type]; + auto content_type = (*res_)[http::field::content_type]; + auto me = shared_from_this(); + auto respond = [me, get_hdr]() { + auto& r = *(me->res_); + me->cb_(r.result_int(), r.body(), get_hdr); + }; if (content_type.empty() || boost::algorithm::icontains(content_type, desc_.accept)) { - cb_(res_->result_int(), res_->body(), get_hdr); + LOG(TRACE) << "Got " << content_type; } else { - GOOGLE_LOG(INFO) << desc_.url - << " response incorrect content type: " << content_type - << " != " << desc_.accept; + LOG(INFO) << desc_.url + << " response incorrect content type: " << content_type + << " != " << desc_.accept; + res_->result(501); } + boost::asio::defer(strand_, respond); close(); } + int redirect_count(std::string_view comp) { + if (comp == desc_.url) { + LOG(ERROR) << "Redirect loop on " << comp; + return 0xFF; + } else if (!prev) { + return 1; + } else { + return 1 + prev->redirect_count(comp); + } + } void close() { if (use_ssl()) { stream_.async_shutdown(boost::beast::bind_front_handler( @@ -345,11 +371,12 @@ class HttpSession : public std::enable_shared_from_this { namespace E = boost::asio::error; switch (ec.value()) { case 0: + case 1: case 2: case ENOTCONN: return; default: - return fail(ec, "shutdown"); + fail(ec, "shutdown"); } } }; @@ -368,5 +395,37 @@ std::optional Self::GetGateway(std::size_t index) const { } return std::nullopt; } - +void Self::AddGateway(std::string_view prefix) { + auto it = FindGateway(prefix); + if (gateways_.end() != it && it->prefix == prefix) { + it->rate++; + LOG(INFO) << "Re-found existing gateway. Bumping it: " << prefix << '=' + << it->rate; + } else { + LOG(INFO) << "Adding new gateway:" << prefix; + gateways_.insert(it, GatewaySpec{std::string{prefix}, 60U}); + DCHECK( + std::is_sorted(gateways_.begin(), gateways_.end(), + [](auto& a, auto& b) { return a.prefix < b.prefix; })); + } +} +void Self::SetGatewayRate(std::string_view prefix, unsigned int rate) { + auto it = FindGateway(prefix); + if (gateways_.end() != it && it->prefix == prefix) { + LOG(INFO) << "Set gateway rate for " << prefix << " to " << rate; + it->rate = rate; + } else { + LOG(INFO) << "Attempted to set the rate of an unknown gateway " << prefix + << " to " << rate; + } +} +unsigned Self::GetGatewayRate(std::string_view prefix) { + auto i = FindGateway(prefix); + return gateways_.end() == i ? 60U : i->rate; +} +auto Self::FindGateway(std::string_view prefix) + -> std::vector::iterator { + auto cmp = [](auto& g, std::string_view p) { return g.prefix < p; }; + return std::lower_bound(gateways_.begin(), gateways_.end(), prefix, cmp); +} #endif diff --git a/library/src/vocab/html_escape_unittest.cc b/library/src/vocab/html_escape_unittest.cc index 151fb60c..5be98ccf 100644 --- a/library/src/vocab/html_escape_unittest.cc +++ b/library/src/vocab/html_escape_unittest.cc @@ -1,7 +1,7 @@ -#include - #include +#include + using namespace std::literals; TEST(HTMLEscapeTest, CommonForbiddenCharacters) { diff --git a/test_data/blocks/QmR6d4PC2FKNG46Gew68Try6cmXDkkxKHo21U64yUqZyKu b/test_data/blocks/QmR6d4PC2FKNG46Gew68Try6cmXDkkxKHo21U64yUqZyKu deleted file mode 100644 index c25b9e39..00000000 --- a/test_data/blocks/QmR6d4PC2FKNG46Gew68Try6cmXDkkxKHo21U64yUqZyKu +++ /dev/null @@ -1,2 +0,0 @@ - -Uf6 Uf7 Uf8 Uf9 Ug0 Ug1 Ug2 Ug3 Ug4 Ug5 Ug6 Ug7 Ug8 Ug9 Uh0 Uh1 Uh2 Uh3 Uh4 Uh5 Uh6 Uh7 Uh8 Uh9 Ui0 Ui1 Ui2 Ui3 Ui4 Ui5 Ui6 Ui7 Ui8 Ui9 Uj0 Uj1  \ No newline at end of file diff --git a/test_data/blocks/QmR9U6TzmfpRuvCHSwR1fbdjYXLAGmdTnTQYLYN5S1jKy9 b/test_data/blocks/QmR9U6TzmfpRuvCHSwR1fbdjYXLAGmdTnTQYLYN5S1jKy9 deleted file mode 100644 index 511da664..00000000 --- a/test_data/blocks/QmR9U6TzmfpRuvCHSwR1fbdjYXLAGmdTnTQYLYN5S1jKy9 +++ /dev/null @@ -1,2 +0,0 @@ - -Mb2 Mb3 Mb4 Mb5 Mb6 Mb7 Mb8 Mb9 Mc0 Mc1 Mc2 Mc3 Mc4 Mc5 Mc6 Mc7 Mc8 Mc9 Md0 Md1 Md2 Md3 Md4 Md5 Md6 Md7 Md8 Md9 Me0 Me1 Me2 Me3 Me4 Me5 Me6 Me7  \ No newline at end of file diff --git a/test_data/blocks/QmRJm8JS1FjtHvG4iaXKW14xuZUfoyAJLpuhGUK1VSS8Bt b/test_data/blocks/QmRJm8JS1FjtHvG4iaXKW14xuZUfoyAJLpuhGUK1VSS8Bt deleted file mode 100644 index df97d2af..00000000 --- a/test_data/blocks/QmRJm8JS1FjtHvG4iaXKW14xuZUfoyAJLpuhGUK1VSS8Bt +++ /dev/null @@ -1,2 +0,0 @@ - -Ok4 Ok5 Ok6 Ok7 Ok8 Ok9 Ol0 Ol1 Ol2 Ol3 Ol4 Ol5 Ol6 Ol7 Ol8 Ol9 Om0 Om1 Om2 Om3 Om4 Om5 Om6 Om7 Om8 Om9 On0 On1 On2 On3 On4 On5 On6 On7 On8 On9  \ No newline at end of file diff --git a/test_data/blocks/QmRRF8bLEMzjgnUZBRMAkyUyZUQax2PJBKgDuSzqW8CVT4 b/test_data/blocks/QmRRF8bLEMzjgnUZBRMAkyUyZUQax2PJBKgDuSzqW8CVT4 deleted file mode 100644 index 02d19da4..00000000 --- a/test_data/blocks/QmRRF8bLEMzjgnUZBRMAkyUyZUQax2PJBKgDuSzqW8CVT4 +++ /dev/null @@ -1,2 +0,0 @@ - -Db2 Db3 Db4 Db5 Db6 Db7 Db8 Db9 Dc0 Dc1 Dc2 Dc3 Dc4 Dc5 Dc6 Dc7 Dc8 Dc9 Dd0 Dd1 Dd2 Dd3 Dd4 Dd5 Dd6 Dd7 Dd8 Dd9 De0 De1 De2 De3 De4 De5 De6 De7  \ No newline at end of file diff --git a/test_data/blocks/QmRcxaA3HnB2SrGsrFdJyhXVGHLkXSXXyCc5iZCqYsWf6x b/test_data/blocks/QmRcxaA3HnB2SrGsrFdJyhXVGHLkXSXXyCc5iZCqYsWf6x deleted file mode 100644 index 2635702c..00000000 --- a/test_data/blocks/QmRcxaA3HnB2SrGsrFdJyhXVGHLkXSXXyCc5iZCqYsWf6x +++ /dev/null @@ -1,2 +0,0 @@ - -No8 No9 Np0 Np1 Np2 Np3 Np4 Np5 Np6 Np7 Np8 Np9 Nq0 Nq1 Nq2 Nq3 Nq4 Nq5 Nq6 Nq7 Nq8 Nq9 Nr0 Nr1 Nr2 Nr3 Nr4 Nr5 Nr6 Nr7 Nr8 Nr9 Ns0 Ns1 Ns2 Ns3  \ No newline at end of file diff --git a/test_data/blocks/QmRekb75JctBPndJ1vxwcmNMJP9WNq9MXUKkHWi1MpadAP b/test_data/blocks/QmRekb75JctBPndJ1vxwcmNMJP9WNq9MXUKkHWi1MpadAP deleted file mode 100644 index 06919de5..00000000 --- a/test_data/blocks/QmRekb75JctBPndJ1vxwcmNMJP9WNq9MXUKkHWi1MpadAP +++ /dev/null @@ -1,2 +0,0 @@ - -Sh2 Sh3 Sh4 Sh5 Sh6 Sh7 Sh8 Sh9 Si0 Si1 Si2 Si3 Si4 Si5 Si6 Si7 Si8 Si9 Sj0 Sj1 Sj2 Sj3 Sj4 Sj5 Sj6 Sj7 Sj8 Sj9 Sk0 Sk1 Sk2 Sk3 Sk4 Sk5 Sk6 Sk7  \ No newline at end of file diff --git a/test_data/blocks/QmRgUfZs5Gm8LjT4BxouNDEPY2eqYpqpLrZc7T2isPHdgi b/test_data/blocks/QmRgUfZs5Gm8LjT4BxouNDEPY2eqYpqpLrZc7T2isPHdgi deleted file mode 100644 index 9592ded0..00000000 --- a/test_data/blocks/QmRgUfZs5Gm8LjT4BxouNDEPY2eqYpqpLrZc7T2isPHdgi +++ /dev/null @@ -1,2 +0,0 @@ - -Yu4 Yu5 Yu6 Yu7 Yu8 Yu9 Yv0 Yv1 Yv2 Yv3 Yv4 Yv5 Yv6 Yv7 Yv8 Yv9 Yw0 Yw1 Yw2 Yw3 Yw4 Yw5 Yw6 Yw7 Yw8 Yw9 Yx0 Yx1 Yx2 Yx3 Yx4 Yx5 Yx6 Yx7 Yx8 Yx9  \ No newline at end of file diff --git a/test_data/blocks/QmRi4KVCJDJtkuNN8d2CBn6fHBfy7vkwytpVct5EEpyF8b b/test_data/blocks/QmRi4KVCJDJtkuNN8d2CBn6fHBfy7vkwytpVct5EEpyF8b deleted file mode 100644 index aef1f2d4..00000000 --- a/test_data/blocks/QmRi4KVCJDJtkuNN8d2CBn6fHBfy7vkwytpVct5EEpyF8b +++ /dev/null @@ -1,2 +0,0 @@ - -Ov2 Ov3 Ov4 Ov5 Ov6 Ov7 Ov8 Ov9 Ow0 Ow1 Ow2 Ow3 Ow4 Ow5 Ow6 Ow7 Ow8 Ow9 Ox0 Ox1 Ox2 Ox3 Ox4 Ox5 Ox6 Ox7 Ox8 Ox9 Oy0 Oy1 Oy2 Oy3 Oy4 Oy5 Oy6 Oy7  \ No newline at end of file diff --git a/test_data/blocks/QmS4HusdPJcQKn7M4VkkLCccm3robyr6hiJzywvZqyJPK8 b/test_data/blocks/QmS4HusdPJcQKn7M4VkkLCccm3robyr6hiJzywvZqyJPK8 deleted file mode 100644 index 45d444b4..00000000 --- a/test_data/blocks/QmS4HusdPJcQKn7M4VkkLCccm3robyr6hiJzywvZqyJPK8 +++ /dev/null @@ -1,2 +0,0 @@ - -Me8 Me9 Mf0 Mf1 Mf2 Mf3 Mf4 Mf5 Mf6 Mf7 Mf8 Mf9 Mg0 Mg1 Mg2 Mg3 Mg4 Mg5 Mg6 Mg7 Mg8 Mg9 Mh0 Mh1 Mh2 Mh3 Mh4 Mh5 Mh6 Mh7 Mh8 Mh9 Mi0 Mi1 Mi2 Mi3  \ No newline at end of file diff --git a/test_data/blocks/QmS6ZNKE9s8fsHoEnArsZXnzMWijKddhXXDsAev8LdTT5z b/test_data/blocks/QmS6ZNKE9s8fsHoEnArsZXnzMWijKddhXXDsAev8LdTT5z deleted file mode 100644 index b838bd73..00000000 --- a/test_data/blocks/QmS6ZNKE9s8fsHoEnArsZXnzMWijKddhXXDsAev8LdTT5z +++ /dev/null @@ -1,3 +0,0 @@ - - my index - \ No newline at end of file diff --git a/test_data/blocks/QmS8SG3PL3pmpwThwEQoaTtPD3tCYtomHrYJGmeZyhnHJP b/test_data/blocks/QmS8SG3PL3pmpwThwEQoaTtPD3tCYtomHrYJGmeZyhnHJP deleted file mode 100644 index e2fff992..00000000 --- a/test_data/blocks/QmS8SG3PL3pmpwThwEQoaTtPD3tCYtomHrYJGmeZyhnHJP +++ /dev/null @@ -1,2 +0,0 @@ - -Zb6 Zb7 Zb8 Zb9 Zc0 Zc1 Zc2 Zc3 Zc4 Zc5 Zc6 Zc7 Zc8 Zc9 Zd0 Zd1 Zd2 Zd3 Zd4 Zd5 Zd6 Zd7 Zd8 Zd9 Ze0 Ze1 Ze2 Ze3 Ze4 Ze5 Ze6 Ze7 Ze8 Ze9 Zf0 Zf1  \ No newline at end of file diff --git a/test_data/blocks/QmSGqw3vXsbtFcqKukUwL1L4gAEzuJxVtUiPhWndR92H1m b/test_data/blocks/QmSGqw3vXsbtFcqKukUwL1L4gAEzuJxVtUiPhWndR92H1m deleted file mode 100644 index f0af5948..00000000 --- a/test_data/blocks/QmSGqw3vXsbtFcqKukUwL1L4gAEzuJxVtUiPhWndR92H1m +++ /dev/null @@ -1,2 +0,0 @@ - -Yn2 Yn3 Yn4 Yn5 Yn6 Yn7 Yn8 Yn9 Yo0 Yo1 Yo2 Yo3 Yo4 Yo5 Yo6 Yo7 Yo8 Yo9 Yp0 Yp1 Yp2 Yp3 Yp4 Yp5 Yp6 Yp7 Yp8 Yp9 Yq0 Yq1 Yq2 Yq3 Yq4 Yq5 Yq6 Yq7  \ No newline at end of file diff --git a/test_data/blocks/QmSH5WEvzAQMnTX2VUmWf3yjjoXBtxNbLk1jaKSnSnxStL b/test_data/blocks/QmSH5WEvzAQMnTX2VUmWf3yjjoXBtxNbLk1jaKSnSnxStL deleted file mode 100644 index b6984642..00000000 --- a/test_data/blocks/QmSH5WEvzAQMnTX2VUmWf3yjjoXBtxNbLk1jaKSnSnxStL +++ /dev/null @@ -1,2 +0,0 @@ - -Ri0 Ri1 Ri2 Ri3 Ri4 Ri5 Ri6 Ri7 Ri8 Ri9 Rj0 Rj1 Rj2 Rj3 Rj4 Rj5 Rj6 Rj7 Rj8 Rj9 Rk0 Rk1 Rk2 Rk3 Rk4 Rk5 Rk6 Rk7 Rk8 Rk9 Rl0 Rl1 Rl2 Rl3 Rl4 Rl5  \ No newline at end of file diff --git a/test_data/blocks/QmSLYaLhN3Tanoa5AzxTYmMikiLFsTMdLBDRNLasZUAVhN b/test_data/blocks/QmSLYaLhN3Tanoa5AzxTYmMikiLFsTMdLBDRNLasZUAVhN deleted file mode 100644 index 78bff88f..00000000 --- a/test_data/blocks/QmSLYaLhN3Tanoa5AzxTYmMikiLFsTMdLBDRNLasZUAVhN +++ /dev/null @@ -1,2 +0,0 @@ - -Is8 Is9 It0 It1 It2 It3 It4 It5 It6 It7 It8 It9 Iu0 Iu1 Iu2 Iu3 Iu4 Iu5 Iu6 Iu7 Iu8 Iu9 Iv0 Iv1 Iv2 Iv3 Iv4 Iv5 Iv6 Iv7 Iv8 Iv9 Iw0 Iw1 Iw2 Iw3  \ No newline at end of file diff --git a/test_data/blocks/QmSRA6TUXLM2bbJNDvADRQJ7h7QQDWVi76SvDoHgCqDxPk b/test_data/blocks/QmSRA6TUXLM2bbJNDvADRQJ7h7QQDWVi76SvDoHgCqDxPk deleted file mode 100644 index 5938dac5..00000000 --- a/test_data/blocks/QmSRA6TUXLM2bbJNDvADRQJ7h7QQDWVi76SvDoHgCqDxPk +++ /dev/null @@ -1,2 +0,0 @@ - -Pj6 Pj7 Pj8 Pj9 Pk0 Pk1 Pk2 Pk3 Pk4 Pk5 Pk6 Pk7 Pk8 Pk9 Pl0 Pl1 Pl2 Pl3 Pl4 Pl5 Pl6 Pl7 Pl8 Pl9 Pm0 Pm1 Pm2 Pm3 Pm4 Pm5 Pm6 Pm7 Pm8 Pm9 Pn0 Pn1  \ No newline at end of file diff --git a/test_data/blocks/QmT4Jfjp858emhSLfzUvVT2xYANwtQ1rj61YKavYTEL3WC b/test_data/blocks/QmT4Jfjp858emhSLfzUvVT2xYANwtQ1rj61YKavYTEL3WC deleted file mode 100644 index 8dbacb0a..00000000 --- a/test_data/blocks/QmT4Jfjp858emhSLfzUvVT2xYANwtQ1rj61YKavYTEL3WC +++ /dev/null @@ -1,2 +0,0 @@ - -Ky4 Ky5 Ky6 Ky7 Ky8 Ky9 Kz0 Kz1 Kz2 Kz3 Kz4 Kz5 Kz6 Kz7 Kz8 Kz9 La0 La1 La2 La3 La4 La5 La6 La7 La8 La9 Lb0 Lb1 Lb2 Lb3 Lb4 Lb5 Lb6 Lb7 Lb8 Lb9  \ No newline at end of file diff --git a/test_data/blocks/QmUBxFhRagQGubsUWWV6Th3C8WRubdsG64FKA62ZpaFiV4 b/test_data/blocks/QmUBxFhRagQGubsUWWV6Th3C8WRubdsG64FKA62ZpaFiV4 deleted file mode 100644 index 55644faa..00000000 --- a/test_data/blocks/QmUBxFhRagQGubsUWWV6Th3C8WRubdsG64FKA62ZpaFiV4 +++ /dev/null @@ -1,94 +0,0 @@ - - - - - IPFS Multi-Gateway Experiment in Chromium | IPFS Blog & News - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

IPFS Multi-Gateway Experiment in Chromium

IPFS Multi-Gateway Experiment in Chromium

IPFS (opens new window) is a protocol suite for a content-addressed networking (opens new window). If you'd like to run a node (opens new window) and participate in the peer-to-peer network, by all means give it a try (opens new window)!

The most important thing to get: With IPFS you can fetch something by a Content ID (CID (opens new window)), which represents what it is, not where it's coming from.

The other way of fetching things from the IPFS ecosystem is through IPNS (opens new window), which allows someone to cryptographically sign a reference to a CID, then you can request whatever content that person/organization is currently pointing to as their site.

Essentially, http:// specifies "where" to find it, ipfs:// specifies "what" to find, and ipns:// specifies "whose" content to find.

What about people who don't know about IPFS, and just run across a link (opens new window)? What if they'd like to be able to use that link in their browser? This is where a "client" fits in - software that can talk to nodes to fetch the content they want, but without running one yourself.

# What is this all about?

Most IPFS clients talk to a particular HTTP gateway (opens new window). Multi-Gateway Clients proposed in IPIP-359 (opens new window) fulfill your requests using multiple Trustless Gateways (opens new window). This gives you more resilience, as you're not dependent on a single HTTP endpoint that can be censored or blocked by your ISP. It also can result in better performance, as you can multiplex requests that would typically run through a single server.

Here we're talking about a project to implement IPFS in Chromium (opens new window). The result is an experimental racing multi-gateway client built directly into the browser, which means the same request might get sent to multiple Trustless Gateways, and the first one to get the result verified wins. And it's built into a custom-patched build of Chromium.

# Why build this?

This is by no means the first time IPFS has been usable in a browser, or even Chromium-based browsers in particular. Javier Fernández at Igalia has written some good explanations of other approaches that have been taken over at his blog in his post Discovering Chrome’s pre-defined Custom Handlers (opens new window), and there's an overview on the IPFS blog (opens new window) as well.

Most of these approaches share in common the idea of translating IPFS and IPNS (opens new window) requests, 1:1, into HTTP requests. For example, if you have an HTTP gateway running locally on your machine, something like:

ipfs://bafybeihpy2n6vwt2jjq5gusv23ajtilzbao3ekfb2hiev2xvuxscdxqcp4

might become:

http://localhost:8080/ipfs/bafybeihpy2n6vwt2jjq5gusv23ajtilzbao3ekfb2hiev2xvuxscdxqcp4/

Or maybe it could become

https://ipfs.io/ipfs/bafybeihpy2n6vwt2jjq5gusv23ajtilzbao3ekfb2hiev2xvuxscdxqcp4/ (opens new window)

Or preferably (when Origin isolation (opens new window) matters):

https://bafybeihpy2n6vwt2jjq5gusv23ajtilzbao3ekfb2hiev2xvuxscdxqcp4.ipfs.dweb.link/ (opens new window)

In each case, you're delegating all the "IPFS stuff", including CID (hash) verification, to a particular node. This is effective for completing requests, but has some trade-offs, including the privacy and integrity risks when using a remote gateway provided by a third-party.

# Performance

If the gateway you're using happens to have the data you're seeking already on-hand, your performance will be great, since it can simply return to you what it already has. Perforance might even be better than the multi-gateway client, since no extraneous requests would be made. However, if you were unlucky, that gateway will have to spend more time querying the IPFS network to try to find the data you request before it gives up. The ideal gateway to use may very well depend on what you happen to be doing at the moment - and may differ from one of your tabs to another. A multi-gateway client will have the worst case performance more rarely.

And while we've been talking about "files" for the most part, IPFS breaks larger files down into "blocks". So you can apply these same techniques at the block level, and it's also conceivable that for a sufficiently large file which exists on multiple gateways you're talking to, a verifying multi-gateway client might be able to be faster than a single-gateway client, since you might be pulling down parts of the file from different sources concurrently. RAPIDE (opens new window) is a more advanced in-development client which also makes use of this principle (along with other things). And it's showing promising results - watch a recent talk from IPFS Thing by Jorropo (opens new window) on it!

# Installation (vs. local gateway)

If you're reading this, installing a local node might seem like no big deal to you. However, we want IPFS to be accessible to people who haven't heard of it, and make it easy for them to click a link without having to think about which protocol-handling software they have installed ahead of time.

One approach is to have the browser install and start its own IPFS node. This is a pretty reasonable approach, but it can raise questions about when to dedicate resources to installation or the node's daemon (opens new window). The most notable example of this approach is Brave (opens new window).

Brave IPFS Choice

However, regardless of whether the browser manages a Kubo (opens new window) node as Brave does or implements IPFS natively, the architecture of the application has changed in a significant way - from being strictly a client, to being a server.

Including HTTP-client-only IPFS capabilities in a Chromium-based browser doesn't change the installation experience in a noticeable way, nor require any major rethink of the browser security model.

# Security (vs. single public gateway)

Content-addressed networking involves a validation step to make sure that the data you received matches the hash (opens new window) requested (it's a part of the CID). When you're requesting a file from an HTTP gateway, by default, the verification of the content is delegated to the node running the gateway. Further, if you receive the file in its final deserialized form as a response to a single request, naively using just an HTTP client, it's no longer possible to verify locally.

This is probably fine if the gateway you're talking to is one you're running locally. Presumably you trust that software as much as you trust your own browser.

The public IPFS gateways today appear to be consistently and reliably returning the correct results. Nonetheless the possibility exists, and it would be preferable if we didn't have to trust. That's why this experimental Chromium implementation uses the Trustless Gateway (opens new window) API and verifies the retrieved content locally.

# Where is the code?

In the repo you'll see separation between component (opens new window) and library (opens new window), where the former contains Chromium-specific code, and the latter contains code that helps with IPFS implementation details that can build without Chromium.

This distinction disappears when you switch over to the Chromium build. Both sets of source are dumped into a component (basically a submodule) called ipfs, that implements the handling of ipfs:// and ipns:// URLs.

Those who embed Chromium into another application generally provide an implementation of a couple of interfaces, namely ContentClient and ContentBrowserClient. They would need to add a little code to their implementations to use the ipfs component. Our repo contains a patch file which alters Chrome's implementations of these two as a demonstration to show how usage might work. That patch file might be useful as-is to someone who uses a patching approach to make a Chromium-derived browser.

# How (in more detail)?

# Hooking into Chromium

# Issuing HTTP(S) requests to Trustless Gateways

The detailed steps of the algorithm are laid out in the design doc (opens new window), but here's the basic idea:

  • An IPFS link will have a CID in the URL. This is the root (opens new window) of its DAG (opens new window), which contains directly or indirectly all the info needed to get all the files related to the site, and will be the first block (opens new window) needed to access the file/resource.
  • For any given block that is known to be needed, but not present in-memory, send requests to several gateways which haven't responded with an error for this CID yet and don't currently have pending requests to them. These requests have ?format=raw so that we'll get just the one block (with Content-Type application/vnd.ipld.raw (opens new window)), not the whole file.
  • When a response comes from a gateway, hash it according to the algo specified in the CID's multihash (opens new window). Right now, that has to be sha-256, and luckily it generally is. If the hashes don't match, the gateway's response gets treated much like an error - the gateway gets reduced in priority, and a new request goes out to a gateway that hasn't yet received this request.
  • If the hashes are equal, store the block, process the block as described in Codecs (below). If the new node includes links to more blocks we also need, send requests for those blocks.
  • When the browser has all the blocks needed, piece together the full file/resource and create an HTTP response and return it, as if it had been a single HTTP request all along.

# Codecs

If a CID is V0, we assume the codec (opens new window) is dag-pb (opens new window) (see below). Other CIDs specify the codec, and right now we support these 2:

# raw (0x55)

A block of this type is a blob - a bunch of bytes. We'll populate the body of the response with it.

# dag-pb (0x70)

That's ProtoBuf (opens new window)-encoded Directed Acyclic Graph (opens new window). A block of this type is a node in a DAG, and contains some bytes to let you know what kind of node it is. There is one very special and important type of node ipfs-chromium deals with a lot:

# UnixFS Node

The payload of these nodes have another ProtoBuf layer, and the DAG functions in a conceptually similar way to a read-only file system.

Not all kinds of UnixFS nodes are fully handled yet, but we cover these:

# File (simple case)

These nodes each have a data byte array that is the contents of a file. We'll use those bytes as the body of a response.

# File (multi-node)

In UnixFS a node can represent a file as the concatenation of other file nodes, to which it has links. The decision to use this kind of node generally has to do with the size of the file. A single node can't be much more than a megabyte, so files larger than that get cut into chunks and handled as a tree of nodes. There are a couple of reasons for that:

  • Data deduplication (it's possible the same sequences of bytes, and thus same CID, appears in multiple files or even within the same file)
  • In the case that a gateway were malicious, we wouldn't want to wait until a file of potentially unbounded size finishes downloading before we verify that it's correct. "ipfs-chromium" enforces a limit of 2MB per block.
  • It enables the possibility that one could concurrently fetch different parts of the file from different gateways.

If we have all the nodes linked-to already, we can concatenate their data together and make a response body out of it. If we don't, we'll convert the missing links to CIDs and request them from gateways.

# Directory (normal)

In this case the data field isn't really important to us. The links, however, represent items in the directory.

  • If your URL has a path, find the link matching the first element in the path, and repeat the whole process with that link's CID and the remainder of the path.
  • If you don't have a path, we'll assume you want index.html
    • If there's no index.html we'll generate an directory listing HTML file for you.
# HAMT (opens new window) (sharded) Directory

This is for directories with just too many entries in them to fit in a single block. The links from this directory node might be entries in the directory or they might be other HAMT nodes referring to the same directory (basically, the directory itself is getting split up over a tree of nodes).

  • If you're coming in from another HAMT node, you might have some unused bits of the hash to select the next child.
  • If you have a path, hash the name of the item you're looking for, pop the correct number of bits off the hash, and use it to select which element you're going to next.
  • If you don't have a path, we'll assume you want index.html.
  • We don't generate listings of sharded directories today, and this isn't a high-priority as it's an unreasonable use case.

The first element after ipns:// is the "ipns-name (opens new window)".

  • If the name is formatted as a CIDv1, and has its codec set to libp2p-key (0x72), ipfs-chromium will retrieve a signed IPNS record (opens new window) of what it points at from a gateway, and then load that content. -
    • The cryptographic signature in the record is verified using the public key, which corresponds to the "ipns-name"
    • Note: only two multibase (opens new window) encodings are fully supported for now: base36 and base32. If your IPNS or DNSLink record points to something base58 that should work, but otherwise avoid it (don't use it in the address bar!).
  • If the name is not formatted as a CIDv1, a DNS request is created for the appropriate TXT record to resolve it as a DNSLink (opens new window).

IPNS names may point to other IPNS names, in which case this process recurses. More commonly they point at an IPFS DAG, in which case ipfs-chromium will then load that content as described above.

# Bottom Line

So, in the end, the user gets to treat ipfs:// links to snapshotted data like any other link, gets the result in a reasonable timeframe, and can rely on the data they get back being the correct data.

ipns:// URLs of the DNSLink variety rely only on DNS being accurate.
-Regular ipns:// URLs, however, are verified by the cryptographically signed record (opens new window).

# Trying it out

If you want to try this yourself today, you can build it (opens new window) from source, or you may install a pre-built binary from GitHub releases (opens new window) or an IPFS gateway (opens new window).

If you'd just like to see it in action, here are the links I use in the video below:

  • ipfs://bafybeigchjo5f3jyzfjwmbavhr27jwdhu6wwhsodxg4qq4x72aasxewp64/blog.html - a snapshot of this blog post
  • ipns://k51qzi5uqu5dkq4jxcqvujfm2woh4p9y6inrojofxflzdnfht168zf8ynfzuu1/blog.html - a mutable pointer to the current version of this blog
  • ipns://docs.ipfs.tech - The IPFS documentation.
  • ipns://en.wikipedia-on-ipfs.org/wiki/ - Wikipedia, as a big HAMT + DNSLink
  • ipns://ipfs.io - an unusual case: a DNSLink to another DNSLink
  • https://littlebearlabs.io - an HTTPs URL for comparison.

# When could this be widespread?

This is very experimental, and will not be in mainstream browsers tomorrow. Feel free to vote for the issue (opens new window) where we discuss its future.

# Who is doing this?

Little Bear Labs (opens new window) and Protocol Labs (opens new window)

- - - - \ No newline at end of file diff --git a/test_data/blocks/QmVKrEdppsp1HPGxE7x4w7gZpCmdmf8MyPo2w3kXXRSfdM b/test_data/blocks/QmVKrEdppsp1HPGxE7x4w7gZpCmdmf8MyPo2w3kXXRSfdM deleted file mode 100644 index 195eb694..00000000 --- a/test_data/blocks/QmVKrEdppsp1HPGxE7x4w7gZpCmdmf8MyPo2w3kXXRSfdM +++ /dev/null @@ -1,94 +0,0 @@ - - - - - IPFS Multi-Gateway Experiment in Chromium | IPFS Blog & News - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

IPFS Multi-Gateway Experiment in Chromium

IPFS Multi-Gateway Experiment in Chromium

IPFS (opens new window) is a protocol suite for a content-addressed networking (opens new window). If you'd like to run a node (opens new window) and participate in the peer-to-peer network, by all means give it a try (opens new window)!

The most important thing to get: With IPFS you can fetch something by a Content ID (CID (opens new window)), which represents what it is, not where it's coming from.

The other way of fetching things from the IPFS ecosystem is through IPNS (opens new window), which allows someone to cryptographically sign a reference to a CID, then you can request whatever content that person/organization is currently pointing to as their site.

Essentially, http:// specifies "where" to find it, ipfs:// specifies "what" to find, and ipns:// specifies "whose" content to find.

What about people who don't know about IPFS, and just run across a link (opens new window)? What if they'd like to be able to use that link in their browser? This is where a "client" fits in - software that can talk to nodes to fetch the content they want, but without running one yourself.

# What is this all about?

Most IPFS clients talk to a particular HTTP gateway (opens new window). Multi-Gateway Clients proposed in IPIP-359 (opens new window) fulfill your requests using multiple Trustless Gateways (opens new window). This gives you more resilience, as you're not dependent on a single HTTP endpoint that can be censored or blocked by your ISP. It also can result in better performance, as you can multiplex requests that would typically run through a single server.

Here we're talking about a project to implement IPFS in Chromium (opens new window). The result is an experimental racing multi-gateway client built directly into the browser, which means the same request might get sent to multiple Trustless Gateways, and the first one to get the result verified wins. And it's built into a custom-patched build of Chromium.

# Why build this?

This is by no means the first time IPFS has been usable in a browser, or even Chromium-based browsers in particular. Javier Fernández at Igalia has written some good explanations of other approaches that have been taken over at his blog in his post Discovering Chrome’s pre-defined Custom Handlers (opens new window), and there's an overview on the IPFS blog (opens new window) as well.

Most of these approaches share in common the idea of translating IPFS and IPNS (opens new window) requests, 1:1, into HTTP requests. For example, if you have an HTTP gateway running locally on your machine, something like:

ipfs://bafybeihpy2n6vwt2jjq5gusv23ajtilzbao3ekfb2hiev2xvuxscdxqcp4

might become:

http://localhost:8080/ipfs/bafybeihpy2n6vwt2jjq5gusv23ajtilzbao3ekfb2hiev2xvuxscdxqcp4/

Or maybe it could become

https://ipfs.io/ipfs/bafybeihpy2n6vwt2jjq5gusv23ajtilzbao3ekfb2hiev2xvuxscdxqcp4/ (opens new window)

Or preferably (when Origin isolation (opens new window) matters):

https://bafybeihpy2n6vwt2jjq5gusv23ajtilzbao3ekfb2hiev2xvuxscdxqcp4.ipfs.dweb.link/ (opens new window)

In each case, you're delegating all the "IPFS stuff", including CID (hash) verification, to a particular node. This is effective for completing requests, but has some trade-offs, including the privacy and integrity risks when using a remote gateway provided by a third-party.

# Performance

If the gateway you're using happens to have the data you're seeking already on-hand, your performance will be great, since it can simply return to you what it already has. Perforance might even be better than the multi-gateway client, since no extraneous requests would be made. However, if you were unlucky, that gateway will have to spend more time querying the IPFS network to try to find the data you request before it gives up. The ideal gateway to use may very well depend on what you happen to be doing at the moment - and may differ from one of your tabs to another. A multi-gateway client will have the worst case performance more rarely.

And while we've been talking about "files" for the most part, IPFS breaks larger files down into "blocks". So you can apply these same techniques at the block level, and it's also conceivable that for a sufficiently large file which exists on multiple gateways you're talking to, a verifying multi-gateway client might be able to be faster than a single-gateway client, since you might be pulling down parts of the file from different sources concurrently. RAPIDE (opens new window) is a more advanced in-development client which also makes use of this principle (along with other things). And it's showing promising results - watch a recent talk from IPFS Thing by Jorropo (opens new window) on it!

# Installation (vs. local gateway)

If you're reading this, installing a local node might seem like no big deal to you. However, we want IPFS to be accessible to people who haven't heard of it, and make it easy for them to click a link without having to think about which protocol-handling software they have installed ahead of time.

One approach is to have the browser install and start its own IPFS node. This is a pretty reasonable approach, but it can raise questions about when to dedicate resources to installation or the node's daemon (opens new window). The most notable example of this approach is Brave (opens new window).

Brave IPFS Choice

However, regardless of whether the browser manages a Kubo (opens new window) node as Brave does or implements IPFS natively, the architecture of the application has changed in a significant way - from being strictly a client, to being a server.

Including HTTP-client-only IPFS capabilities in a Chromium-based browser doesn't change the installation experience in a noticeable way, nor require any major rethink of the browser security model.

# Security (vs. single public gateway)

Content-addressed networking involves a validation step to make sure that the data you received matches the hash (opens new window) requested (it's a part of the CID). When you're requesting a file from an HTTP gateway, by default, the verification of the content is delegated to the node running the gateway. Further, if you receive the file in its final deserialized form as a response to a single request, naively using just an HTTP client, it's no longer possible to verify locally.

This is probably fine if the gateway you're talking to is one you're running locally. Presumably you trust that software as much as you trust your own browser.

The public IPFS gateways today appear to be consistently and reliably returning the correct results. Nonetheless the possibility exists, and it would be preferable if we didn't have to trust. That's why this experimental Chromium implementation uses the Trustless Gateway (opens new window) API and verifies the retrieved content locally.

# Where is the code?

In the repo you'll see separation between component (opens new window) and library (opens new window), where the former contains Chromium-specific code, and the latter contains code that helps with IPFS implementation details that can build without Chromium.

This distinction disappears when you switch over to the Chromium build. Both sets of source are dumped into a component (basically a submodule) called ipfs, that implements the handling of ipfs:// and ipns:// URLs.

Those who embed Chromium into another application generally provide an implementation of a couple of interfaces, namely ContentClient and ContentBrowserClient. They would need to add a little code to their implementations to use the ipfs component. Our repo contains a patch file which alters Chrome's implementations of these two as a demonstration to show how usage might work. That patch file might be useful as-is to someone who uses a patching approach to make a Chromium-derived browser.

# How (in more detail)?

# Hooking into Chromium

# Issuing HTTP(S) requests to Trustless Gateways

The detailed steps of the algorithm are laid out in the design doc (opens new window), but here's the basic idea:

  • An IPFS link will have a CID in the URL. This is the root (opens new window) of its DAG (opens new window), which contains directly or indirectly all the info needed to get all the files related to the site, and will be the first block (opens new window) needed to access the file/resource.
  • For any given block that is known to be needed, but not present in-memory, send requests to several gateways which haven't responded with an error for this CID yet and don't currently have pending requests to them. These requests have ?format=raw so that we'll get just the one block (with Content-Type application/vnd.ipld.raw (opens new window)), not the whole file.
  • When a response comes from a gateway, hash it according to the algo specified in the CID's multihash (opens new window). Right now, that has to be sha-256, and luckily it generally is. If the hashes don't match, the gateway's response gets treated much like an error - the gateway gets reduced in priority, and a new request goes out to a gateway that hasn't yet received this request.
  • If the hashes are equal, store the block, process the block as described in Codecs (below). If the new node includes links to more blocks we also need, send requests for those blocks.
  • When the browser has all the blocks needed, piece together the full file/resource and create an HTTP response and return it, as if it had been a single HTTP request all along.

# Codecs

If a CID is V0, we assume the codec (opens new window) is dag-pb (opens new window) (see below). Other CIDs specify the codec, and right now we support these 2:

# raw (0x55)

A block of this type is a blob - a bunch of bytes. We'll populate the body of the response with it.

# dag-pb (0x70)

That's ProtoBuf (opens new window)-encoded Directed Acyclic Graph (opens new window). A block of this type is a node in a DAG, and contains some bytes to let you know what kind of node it is. There is one very special and important type of node ipfs-chromium deals with a lot:

# UnixFS Node

The payload of these nodes have another ProtoBuf layer, and the DAG functions in a conceptually similar way to a read-only file system.

Not all kinds of UnixFS nodes are fully handled yet, but we cover these:

# File (simple case)

These nodes each have a data byte array that is the contents of a file. We'll use those bytes as the body of a response.

# File (multi-node)

In UnixFS a node can represent a file as the concatenation of other file nodes, to which it has links. The decision to use this kind of node generally has to do with the size of the file. A single node can't be much more than a megabyte, so files larger than that get cut into chunks and handled as a tree of nodes. There are a couple of reasons for that:

  • Data deduplication (it's possible the same sequences of bytes, and thus same CID, appears in multiple files or even within the same file)
  • In the case that a gateway were malicious, we wouldn't want to wait until a file of potentially unbounded size finishes downloading before we verify that it's correct. "ipfs-chromium" enforces a limit of 2MB per block.
  • It enables the possibility that one could concurrently fetch different parts of the file from different gateways.

If we have all the nodes linked-to already, we can concatenate their data together and make a response body out of it. If we don't, we'll convert the missing links to CIDs and request them from gateways.

# Directory (normal)

In this case the data field isn't really important to us. The links, however, represent items in the directory.

  • If your URL has a path, find the link matching the first element in the path, and repeat the whole process with that link's CID and the remainder of the path.
  • If you don't have a path, we'll assume you want index.html
    • If there's no index.html we'll generate an directory listing HTML file for you.
# HAMT (opens new window) (sharded) Directory

This is for directories with just too many entries in them to fit in a single block. The links from this directory node might be entries in the directory or they might be other HAMT nodes referring to the same directory (basically, the directory itself is getting split up over a tree of nodes).

  • If you're coming in from another HAMT node, you might have some unused bits of the hash to select the next child.
  • If you have a path, hash the name of the item you're looking for, pop the correct number of bits off the hash, and use it to select which element you're going to next.
  • If you don't have a path, we'll assume you want index.html.
  • We don't generate listings of sharded directories today, and this isn't a high-priority as it's an unreasonable use case.

The first element after ipns:// is the "ipns-name (opens new window)".

  • If the name is formatted as a CIDv1, and has its codec set to libp2p-key (0x72), ipfs-chromium will retrieve a signed IPNS record (opens new window) of what it points at from a gateway, and then load that content. -
    • The cryptographic signature in the record is verified using the public key, which corresponds to the "ipns-name"
    • Note: only two multibase (opens new window) encodings are fully supported for now: base36 and base32. If your IPNS or DNSLink record points to something base58 that should work, but otherwise avoid it (don't use it in the address bar!).
  • If the name is not formatted as a CIDv1, a DNS request is created for the appropriate TXT record to resolve it as a DNSLink (opens new window).

IPNS names may point to other IPNS names, in which case this process recurses. More commonly they point at an IPFS DAG, in which case ipfs-chromium will then load that content as described above.

# Bottom Line

So, in the end, the user gets to treat ipfs:// links to snapshotted data like any other link, gets the result in a reasonable timeframe, and can rely on the data they get back being the correct data.

ipns:// URLs of the DNSLink variety rely only on DNS being accurate.
-Regular ipns:// URLs, however, are verified by the cryptographically signed record (opens new window).

# Trying it out

If you want to try this yourself today, you can build it (opens new window) from source, or you may install a pre-built binary from GitHub releases (opens new window) or an IPFS gateway (opens new window).

If you'd just like to see it in action, here are the links I use in the video below:

  • ipfs://bafybeigchjo5f3jyzfjwmbavhr27jwdhu6wwhsodxg4qq4x72aasxewp64/blog.html - a snapshot of this blog post
  • ipns://k51qzi5uqu5dkq4jxcqvujfm2woh4p9y6inrojofxflzdnfht168zf8ynfzuu1/blog.html - a mutable pointer to the current version of this blog
  • ipns://docs.ipfs.tech - The IPFS documentation.
  • ipns://en.wikipedia-on-ipfs.org/wiki/ - Wikipedia, as a big HAMT + DNSLink
  • ipns://ipfs.io - an unusual case: a DNSLink to another DNSLink
  • https://littlebearlabs.io - an HTTPs URL for comparison.

# When could this be widespread?

This is very experimental, and will not be in mainstream browsers tomorrow. Feel free to vote for the issue (opens new window) where we discuss its future.

# Who is doing this?

Little Bear Labs (opens new window) and Protocol Labs (opens new window)

- - - - \ No newline at end of file diff --git a/test_data/blocks/QmaWDLb4gnJcJbT1Df5X3j91ysiwkkyxw6329NLiC1KMDR b/test_data/blocks/QmaWDLb4gnJcJbT1Df5X3j91ysiwkkyxw6329NLiC1KMDR deleted file mode 100644 index 67ce7197..00000000 --- a/test_data/blocks/QmaWDLb4gnJcJbT1Df5X3j91ysiwkkyxw6329NLiC1KMDR +++ /dev/null @@ -1,3 +0,0 @@ -- -" eGDT2KLW8c1ǘJuYP=2022 - \ No newline at end of file diff --git a/test_data/blocks/Qmb92YVGixJwh2HHRpy91H2x9KqpnzgyaqHxm3XAgzTWK5 b/test_data/blocks/Qmb92YVGixJwh2HHRpy91H2x9KqpnzgyaqHxm3XAgzTWK5 deleted file mode 100644 index 5529eabb..00000000 --- a/test_data/blocks/Qmb92YVGixJwh2HHRpy91H2x9KqpnzgyaqHxm3XAgzTWK5 +++ /dev/null @@ -1,195 +0,0 @@ - - - - - -An open system to manage data without a central server | IPFS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Discover What's
Out There with IPFS

IPFS is an open system to manage data without a central server

A Universe of Uses

IPFS's versatility shines across different industries – making it the multi-purpose tool for the decentralized age.

Why IPFS?

Our peer-to-peer content delivery network is built around the innovation of content addressing: store, retrieve, and locate data based on the fingerprint of its actual content rather than its name or location.

Open

The IPFS network is distributed and participatory, which reduces the problem of data silos that plague central servers.

Verifiable

The integrity of all data is verified using hash functions, so you can trust that you always get the data you’re looking for.

Resilient

Files and data can be stored across multiple nodes, which keeps content at the ready even during critical outages.

Get Started

Join thousands of developers who choose IPFS to build software that’s open, verifiable, and resilient.

Build peer-to-peer apps with IPFS

Our most popular implementations are written in Go, Rust, and JavaScript – and we also have support for desktop, browsers, mobile, embedded devices, and more.

Store and manage your data on IPFS

Use content addressing to give your data unique identifiers, and use IPFS for large-scale data storage and compute.

Use IPFS apps

Share files, stream music, publish your website, store NFTs, and much more through hundreds of applications built on IPFS.

Develop tooling for IPFS

Design storage, compute, encryption, and implementation layers to optimize the way developers use IPFS.

Connect Through Community

We've sparked the most robust open-source ecosystem on Earth, and together we're achieving interplanetary aspirations.

Our protocol's success hinges on participation from all of our neighbors: Each new node that joins IPFS makes the network stronger for everyone.

Join the Community

IPFS by the Numbers

2000+

active contributors

280,000+

unique nodes

1 Billion+

CIDs published to network

From The Community

- - - \ No newline at end of file diff --git a/test_data/blocks/QmdVDRcZcAQPBensuRn4edoJa7NK8qUfWBSJULnGo8aYZj b/test_data/blocks/QmdVDRcZcAQPBensuRn4edoJa7NK8qUfWBSJULnGo8aYZj deleted file mode 100644 index c051cfdb..00000000 Binary files a/test_data/blocks/QmdVDRcZcAQPBensuRn4edoJa7NK8qUfWBSJULnGo8aYZj and /dev/null differ diff --git a/test_data/blocks/QmdvFUG5JR8f37w7WqbaZfRFGM9wh33yWgMMncSHhAxnBW b/test_data/blocks/QmdvFUG5JR8f37w7WqbaZfRFGM9wh33yWgMMncSHhAxnBW deleted file mode 100644 index 164deb8b..00000000 --- a/test_data/blocks/QmdvFUG5JR8f37w7WqbaZfRFGM9wh33yWgMMncSHhAxnBW +++ /dev/null @@ -1,4 +0,0 @@ -4 -" Vk"(4N(@8+[>@=5? -index.html - \ No newline at end of file diff --git a/test_data/blocks/QmeMo2QuaXiXjwsQMVQwK5PHZwqoFv8NYqndJBGa5junwA b/test_data/blocks/QmeMo2QuaXiXjwsQMVQwK5PHZwqoFv8NYqndJBGa5junwA deleted file mode 100644 index 7e53e135..00000000 --- a/test_data/blocks/QmeMo2QuaXiXjwsQMVQwK5PHZwqoFv8NYqndJBGa5junwA +++ /dev/null @@ -1,2 +0,0 @@ - -Ty4 Ty5 Ty6 Ty7 Ty8 Ty9 Tz0 Tz1 Tz2 Tz3 Tz4 Tz5 Tz6 Tz7 Tz8 Tz9 Ua0 Ua1 Ua2 Ua3 Ua4 Ua5 Ua6 Ua7 Ua8 Ua9 Ub0 Ub1 Ub2 Ub3 Ub4 Ub5 Ub6 Ub7 Ub8 Ub9  \ No newline at end of file diff --git a/test_data/blocks/Qmew6Rpt8DVLp6DrPuq1yhVNcvCywWMEeaNW13sNkSoRtH b/test_data/blocks/Qmew6Rpt8DVLp6DrPuq1yhVNcvCywWMEeaNW13sNkSoRtH deleted file mode 100644 index 206aedfd..00000000 --- a/test_data/blocks/Qmew6Rpt8DVLp6DrPuq1yhVNcvCywWMEeaNW13sNkSoRtH +++ /dev/null @@ -1,2 +0,0 @@ - -Qm4 Qm5 Qm6 Qm7 Qm8 Qm9 Qn0 Qn1 Qn2 Qn3 Qn4 Qn5 Qn6 Qn7 Qn8 Qn9 Qo0 Qo1 Qo2 Qo3 Qo4 Qo5 Qo6 Qo7 Qo8 Qo9 Qp0 Qp1 Qp2 Qp3 Qp4 Qp5 Qp6 Qp7 Qp8 Qp9  \ No newline at end of file diff --git a/test_data/blocks/QmexustWHXRkcFFt1g37mxBmC1bSmZM5oXmDVDdT6tTELJ b/test_data/blocks/QmexustWHXRkcFFt1g37mxBmC1bSmZM5oXmDVDdT6tTELJ deleted file mode 100644 index 62b93b50..00000000 --- a/test_data/blocks/QmexustWHXRkcFFt1g37mxBmC1bSmZM5oXmDVDdT6tTELJ +++ /dev/null @@ -1,2 +0,0 @@ - -Og8 Og9 Oh0 Oh1 Oh2 Oh3 Oh4 Oh5 Oh6 Oh7 Oh8 Oh9 Oi0 Oi1 Oi2 Oi3 Oi4 Oi5 Oi6 Oi7 Oi8 Oi9 Oj0 Oj1 Oj2 Oj3 Oj4 Oj5 Oj6 Oj7 Oj8 Oj9 Ok0 Ok1 Ok2 Ok3  \ No newline at end of file diff --git a/test_data/blocks/Qmf34HtFeHfGeawV5jVUrssu2r7bLiEc4Xrf1digi2bAWD b/test_data/blocks/Qmf34HtFeHfGeawV5jVUrssu2r7bLiEc4Xrf1digi2bAWD deleted file mode 100644 index 160dc06a..00000000 --- a/test_data/blocks/Qmf34HtFeHfGeawV5jVUrssu2r7bLiEc4Xrf1digi2bAWD +++ /dev/null @@ -1,2 +0,0 @@ - -Nl2 Nl3 Nl4 Nl5 Nl6 Nl7 Nl8 Nl9 Nm0 Nm1 Nm2 Nm3 Nm4 Nm5 Nm6 Nm7 Nm8 Nm9 Nn0 Nn1 Nn2 Nn3 Nn4 Nn5 Nn6 Nn7 Nn8 Nn9 No0 No1 No2 No3 No4 No5 No6 No7  \ No newline at end of file diff --git a/test_data/blocks/QmfAvAZqcgbb4ijk8KqvUZ8hAAQ7rNToeU1Q8cEWb1czzY b/test_data/blocks/QmfAvAZqcgbb4ijk8KqvUZ8hAAQ7rNToeU1Q8cEWb1czzY deleted file mode 100644 index 85f19620..00000000 --- a/test_data/blocks/QmfAvAZqcgbb4ijk8KqvUZ8hAAQ7rNToeU1Q8cEWb1czzY +++ /dev/null @@ -1,4 +0,0 @@ -. -" 7Vnu -uٸa!吸lZIcdir - \ No newline at end of file diff --git a/test_data/blocks/QmfBsRowJSgkv1NCFCmXJ29wyYXAF2xU1jdCppwTazyQCx b/test_data/blocks/QmfBsRowJSgkv1NCFCmXJ29wyYXAF2xU1jdCppwTazyQCx deleted file mode 100644 index 09372359..00000000 --- a/test_data/blocks/QmfBsRowJSgkv1NCFCmXJ29wyYXAF2xU1jdCppwTazyQCx +++ /dev/null @@ -1,2 +0,0 @@ - -Cj2 Cj3 Cj4 Cj5 Cj6 Cj7 Cj8 Cj9 Ck0 Ck1 Ck2 Ck3 Ck4 Ck5 Ck6 Ck7 Ck8 Ck9 Cl0 Cl1 Cl2 Cl3 Cl4 Cl5 Cl6 Cl7 Cl8 Cl9 Cm0 Cm1 Cm2 Cm3 Cm4 Cm5 Cm6 Cm7  \ No newline at end of file diff --git a/test_data/blocks/bafkreiaehjyyo5gfok6yujnnx2y37tk4ajlk4eooz6pzyp4sluhffpvpre b/test_data/blocks/bafkreiaehjyyo5gfok6yujnnx2y37tk4ajlk4eooz6pzyp4sluhffpvpre deleted file mode 100644 index 2f259b79..00000000 --- a/test_data/blocks/bafkreiaehjyyo5gfok6yujnnx2y37tk4ajlk4eooz6pzyp4sluhffpvpre +++ /dev/null @@ -1 +0,0 @@ -s \ No newline at end of file diff --git a/test_data/blocks/bafkreiaurxu4lj5ejum6k3gzvynfks7wpbd27mgfr5xbf6rjvr657suzia b/test_data/blocks/bafkreiaurxu4lj5ejum6k3gzvynfks7wpbd27mgfr5xbf6rjvr657suzia deleted file mode 100644 index 4f6c4ee9..00000000 --- a/test_data/blocks/bafkreiaurxu4lj5ejum6k3gzvynfks7wpbd27mgfr5xbf6rjvr657suzia +++ /dev/null @@ -1 +0,0 @@ -p \ No newline at end of file diff --git a/test_data/blocks/bafybeid7szco6ai6bsicmwdtp35b6oxjzb4ppqzhuwn5byxhmlb6bbdmzu b/test_data/blocks/bafybeid7szco6ai6bsicmwdtp35b6oxjzb4ppqzhuwn5byxhmlb6bbdmzu new file mode 100644 index 00000000..dd97690e Binary files /dev/null and b/test_data/blocks/bafybeid7szco6ai6bsicmwdtp35b6oxjzb4ppqzhuwn5byxhmlb6bbdmzu differ diff --git a/test_data/blocks/bafybeidcikfflh4rydhubchh46lxjxlagnmfo64mges5zbddwmzaglnixi b/test_data/blocks/bafybeidcikfflh4rydhubchh46lxjxlagnmfo64mges5zbddwmzaglnixi new file mode 100644 index 00000000..f19d1d85 Binary files /dev/null and b/test_data/blocks/bafybeidcikfflh4rydhubchh46lxjxlagnmfo64mges5zbddwmzaglnixi differ diff --git a/test_data/blocks/bafybeiedwcidxo7uukqvjnyitmvwse6oarnbcqhfc5udwdv4ct7qkfboee b/test_data/blocks/bafybeiedwcidxo7uukqvjnyitmvwse6oarnbcqhfc5udwdv4ct7qkfboee new file mode 100644 index 00000000..08b5532d Binary files /dev/null and b/test_data/blocks/bafybeiedwcidxo7uukqvjnyitmvwse6oarnbcqhfc5udwdv4ct7qkfboee differ diff --git a/test_data/blocks/bafybeieymwabzccklmmqsz2in66tafzvrsv6dgydpcb2xgt2vcrzj6jzae b/test_data/blocks/bafybeieymwabzccklmmqsz2in66tafzvrsv6dgydpcb2xgt2vcrzj6jzae new file mode 100644 index 00000000..629431c2 Binary files /dev/null and b/test_data/blocks/bafybeieymwabzccklmmqsz2in66tafzvrsv6dgydpcb2xgt2vcrzj6jzae differ diff --git a/test_data/blocks/bafybeifbzvpq6r72dtpwkrbifvob3kli25ns4axif76m3hqcjsnjqllhhq b/test_data/blocks/bafybeifbzvpq6r72dtpwkrbifvob3kli25ns4axif76m3hqcjsnjqllhhq new file mode 100644 index 00000000..c5ca9842 Binary files /dev/null and b/test_data/blocks/bafybeifbzvpq6r72dtpwkrbifvob3kli25ns4axif76m3hqcjsnjqllhhq differ diff --git a/test_data/include/mock_api.h b/test_data/include/mock_api.h index 8bb992e4..53170d4d 100644 --- a/test_data/include/mock_api.h +++ b/test_data/include/mock_api.h @@ -70,6 +70,8 @@ struct MockApi final : public i::ContextApi { std::optional GetGateway(std::size_t) const { return std::nullopt; } + unsigned GetGatewayRate(std::string_view) { return 120U; } + void AddGateway(std::string_view) {} }; } // namespace