Skip to content

Commit

Permalink
Redirect gateway-like urls to ipfs://
Browse files Browse the repository at this point in the history
Resolves brave/brave-browser#21454
Urls in format of https://bafy.ipfs.gateway.io or https://gateway.io/ipfs/bafy
are now redirected to ipfs:// scheme if x-ipfs-path header is received
  • Loading branch information
cypt4 committed Oct 31, 2022
1 parent 23c1718 commit c9db9dc
Show file tree
Hide file tree
Showing 7 changed files with 394 additions and 38 deletions.
284 changes: 266 additions & 18 deletions browser/ipfs/test/ipfs_service_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,20 @@ class IpfsServiceBrowserTest : public InProcessBrowserTest {
return http_response;
}

std::unique_ptr<net::test_server::HttpResponse> HandlePublicGatewayRequest(
const net::test_server::HttpRequest& request) {
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_content_type("text/html");

// IPFS gateways set this
http_response->AddCustomHeader("access-control-allow-origin", "*");
http_response->AddCustomHeader("x-ipfs-path", "/ipfs/Qmm");
http_response->set_code(net::HTTP_OK);

return http_response;
}

std::unique_ptr<net::test_server::HttpResponse> HandleEmbeddedSrvrRequest(
const net::test_server::HttpRequest& request) {
auto http_response =
Expand Down Expand Up @@ -1053,57 +1067,291 @@ IN_PROC_BROWSER_TEST_F(IpfsServiceBrowserTest, CannotLoadIPFSImageFromHTTP) {
EXPECT_EQ(base::Value(true), loaded.value);
}

IN_PROC_BROWSER_TEST_F(IpfsServiceBrowserTest, TopLevelAutoRedirectsOn) {
IN_PROC_BROWSER_TEST_F(IpfsServiceBrowserTest,
TopLevelAutoRedirectsOff_DoNotTranslateToIPFS) {
ResetTestServer(
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
base::Unretained(this)));
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, false);
browser()->profile()->GetPrefs()->SetInteger(
kIPFSResolveMethod,
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_GATEWAY));
GURL gateway = GetURL("b.com", "/");
SetIPFSDefaultGatewayForTest(gateway);

auto tab_url = GetURL(
"bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq.ipfs.a.com",
"/simple.html?a=b");

ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(contents->GetURL(), tab_url);
}

IN_PROC_BROWSER_TEST_F(
IpfsServiceBrowserTest,
TopLevelAutoRedirectsOn_DoNotRedirectFromGatewayLikeUrl_IpfsDisabled) {
ResetTestServer(
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
base::Unretained(this)));
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
browser()->profile()->GetPrefs()->SetInteger(
kIPFSResolveMethod,
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_DISABLED));
GURL gateway = GetURL("b.com", "/");
SetIPFSDefaultGatewayForTest(gateway);

auto tab_url = GetURL(
"bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq.ipfs.a.com",
"/simple.html?a=b");

ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(contents->GetURL(), tab_url);
}

IN_PROC_BROWSER_TEST_F(
IpfsServiceBrowserTest,
TopLevelAutoRedirectsOn_Gateway_RedirectFromGatewayLikeUrl_IpfsSubDomain) {
ResetTestServer(
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
base::Unretained(this)));
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
browser()->profile()->GetPrefs()->SetInteger(
kIPFSResolveMethod,
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_GATEWAY));
GURL gateway = GetURL("b.com", "/");
SetIPFSDefaultGatewayForTest(gateway);

auto tab_url = GetURL(
"bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq.ipfs.a.com",
"/simple.html?a=b");

ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(
contents->GetURL(),
GetURL(
"b.com",
"/ipfs/bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/"
"simple.html?a=b"));
}

IN_PROC_BROWSER_TEST_F(
IpfsServiceBrowserTest,
TopLevelAutoRedirectsOn_DoNotRedirectFromGatewayLikeUrl_IfGatewayUrl) {
ResetTestServer(
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
base::Unretained(this)));
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
browser()->profile()->GetPrefs()->SetInteger(
kIPFSResolveMethod,
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_GATEWAY));
GURL gateway = GetURL("b.com", "/");
SetIPFSDefaultGatewayForTest(gateway);

auto tab_url = GetURL(
"bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq.ipfs.b.com",
"/simple.html?a=b");

ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(contents->GetURL(), tab_url);
}

IN_PROC_BROWSER_TEST_F(
IpfsServiceBrowserTest,
TopLevelAutoRedirectsOn_DoNotRedirectFromGatewayLikeUrl_IfLocalhostUrl) {
ResetTestServer(
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
base::Unretained(this)));
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
browser()->profile()->GetPrefs()->SetInteger(
kIPFSResolveMethod,
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_GATEWAY));
GURL gateway = GetURL("b.com", "/");
SetIPFSDefaultGatewayForTest(gateway);

auto tab_url = GetURL(
"bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq.ipfs."
"localhost",
"/simple.html?a=b");

ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(contents->GetURL(), tab_url);
}

IN_PROC_BROWSER_TEST_F(
IpfsServiceBrowserTest,
TopLevelAutoRedirectsOn_DoNotRedirectFromGatewayLikeUrl_NoXIpfsPathHeader) {
ResetTestServer(
base::BindRepeating(&IpfsServiceBrowserTest::HandleEmbeddedSrvrRequest,
base::Unretained(this)));
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
browser()->profile()->GetPrefs()->SetInteger(
kIPFSResolveMethod,
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_GATEWAY));
GURL gateway = GetURL("b.com", "/");
SetIPFSDefaultGatewayForTest(gateway);
auto tab_url = GetURL("a.com", "/simple.html");

auto tab_url =
GetURL("a.com",
"/ipfs/"
"bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq");

ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(contents->GetURL().host(), tab_url.host());
EXPECT_EQ(contents->GetURL(), tab_url);
}

IN_PROC_BROWSER_TEST_F(
IpfsServiceBrowserTest,
TopLevelAutoRedirectsOn_Gateway_RedirectFromGatewayLikeUrl_IpfsPath) {
ResetTestServer(
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
base::Unretained(this)));
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
browser()->profile()->GetPrefs()->SetInteger(
kIPFSResolveMethod,
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_GATEWAY));
tab_url = GURL("ipfs://Qmc2JTQo4iXf24g98otZmGFQq176eQ2Cdbb88qA5ToMEvC/2");
GURL gateway = GetURL("b.com", "/");
SetIPFSDefaultGatewayForTest(gateway);

auto tab_url = GetURL(
"a.com",
"/ipfs/bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/"
"simple.html?a=b");

ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(
contents->GetURL(),
GetURL(
"b.com",
"/ipfs/bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/"
"simple.html?a=b"));
}

IN_PROC_BROWSER_TEST_F(
IpfsServiceBrowserTest,
TopLevelAutoRedirectsOn_ASK_RedirectFromGatewayLikeUrl_IpfsPath) {
ResetTestServer(
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
base::Unretained(this)));
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
browser()->profile()->GetPrefs()->SetInteger(
kIPFSResolveMethod,
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_ASK));
GURL gateway = GetURL("b.com", "/");
SetIPFSDefaultGatewayForTest(gateway);

auto tab_url = GetURL(
"a.com",
"/ipfs/bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/"
"simple.html?a=b");

ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(
contents->GetURL(),
GURL("ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/"
"simple.html?a=b"));
}

IN_PROC_BROWSER_TEST_F(
IpfsServiceBrowserTest,
TopLevelAutoRedirectsOn_ASK_RedirectFromGatewayLikeUrl_IpfsSubDomain) {
ResetTestServer(
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
base::Unretained(this)));
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
browser()->profile()->GetPrefs()->SetInteger(
kIPFSResolveMethod,
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_ASK));
GURL gateway = GetURL("b.com", "/");
SetIPFSDefaultGatewayForTest(gateway);

auto tab_url = GetURL(
"bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq.ipfs.a.com",
"/simple.html?a=b");

ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
auto domain = GetDomainAndRegistry(
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(
contents->GetURL(),
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
GURL("ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/"
"simple.html?a=b"));
}

IN_PROC_BROWSER_TEST_F(IpfsServiceBrowserTest,
TopLevelAutoRedirectsOn_DoNotTranslateSimpleUrls) {
ResetTestServer(
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
base::Unretained(this)));
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
browser()->profile()->GetPrefs()->SetInteger(
kIPFSResolveMethod,
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_ASK));
GURL gateway = GetURL("b.com", "/");
SetIPFSDefaultGatewayForTest(gateway);

EXPECT_EQ(domain, gateway.host());
auto tab_url = GetURL("a.com", "/simple.html?a=b");

ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(contents->GetURL(), tab_url);
}

IN_PROC_BROWSER_TEST_F(IpfsServiceBrowserTest,
TopLevelAutoRedirectsOnWithQuery) {
TopLevelAutoRedirectsOn_DoNotTranslateIncompleteUrls) {
ResetTestServer(
base::BindRepeating(&IpfsServiceBrowserTest::HandleEmbeddedSrvrRequest,
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
base::Unretained(this)));
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, true);
browser()->profile()->GetPrefs()->SetInteger(
kIPFSResolveMethod,
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_ASK));
GURL gateway = GetURL("b.com", "/");
SetIPFSDefaultGatewayForTest(gateway);
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), GetURL("a.com", "/simple.html?abc=123xyz&other=qwerty")));

auto tab_url = GetURL("ipfs.a.com", "/simple.html?a=b");

ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(contents->GetURL().query(), "abc=123xyz&other=qwerty");
EXPECT_EQ(contents->GetURL(), tab_url);
}

IN_PROC_BROWSER_TEST_F(IpfsServiceBrowserTest, TopLevelAutoRedirectsOff) {
ResetTestServer(
base::BindRepeating(&IpfsServiceBrowserTest::HandleEmbeddedSrvrRequest,
base::BindRepeating(&IpfsServiceBrowserTest::HandlePublicGatewayRequest,
base::Unretained(this)));
SetIPFSDefaultGatewayForTest(GetURL("b.com", "/"));
GURL other_gateway = GetURL("a.com", "/simple.html");
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GetURL("a.com", "/simple.html")));
browser()->profile()->GetPrefs()->SetBoolean(kIPFSAutoRedirectGateway, false);
browser()->profile()->GetPrefs()->SetInteger(
kIPFSResolveMethod,
static_cast<int>(ipfs::IPFSResolveMethodTypes::IPFS_ASK));
GURL gateway = GetURL("b.com", "/");
SetIPFSDefaultGatewayForTest(gateway);

auto tab_url = GetURL(
"bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq.ipfs.a.com",
"/simple.html?a=b");

ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(contents->GetURL().host(), other_gateway.host());
EXPECT_EQ(contents->GetURL(), tab_url);
}

IN_PROC_BROWSER_TEST_F(IpfsServiceBrowserTest, ImportTextToIpfs) {
Expand Down
25 changes: 15 additions & 10 deletions browser/net/ipfs_redirect_network_delegate_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "components/user_prefs/user_prefs.h"
#include "content/public/browser/browser_context.h"
#include "net/base/net_errors.h"
#include "net/base/url_util.h"

namespace ipfs {

Expand Down Expand Up @@ -94,6 +95,13 @@ int OnHeadersReceived_IPFSRedirectWork(
std::shared_ptr<brave::BraveRequestInfo> ctx) {
if (!ctx->browser_context)
return net::OK;

// Auto-redirect gateway-like urls is enabled only for top-level frames
// to avoid mixed content corner cases.
if (ctx->resource_type == blink::mojom::ResourceType::kSubFrame) {
return net::OK;
}

auto* prefs = user_prefs::UserPrefs::Get(ctx->browser_context);
if (IsIpfsResolveMethodDisabled(prefs)) {
return net::OK;
Expand All @@ -105,24 +113,21 @@ int OnHeadersReceived_IPFSRedirectWork(
response_headers->GetNormalizedHeader("x-ipfs-path", &ipfs_path) &&
// Make sure we don't infinite redirect
!ctx->request_url.DomainIs(ctx->ipfs_gateway_url.host()) &&
// Do not redirect if the frame is not ipfs/ipns
IsIPFSScheme(ctx->initiator_url)) {
GURL::Replacements replacements;
replacements.SetPathStr(ipfs_path);
!net::IsLocalhost(ctx->request_url)) {
auto translated_url = ipfs::TranslateToCurrentGatewayUrl(ctx->request_url);

if (ctx->request_url.has_query()) {
replacements.SetQueryStr(ctx->request_url.query_piece());
if (!translated_url) {
return net::OK;
}

GURL new_url = ctx->ipfs_gateway_url.ReplaceComponents(replacements);

*override_response_headers =
new net::HttpResponseHeaders(response_headers->raw_headers());
(*override_response_headers)
->ReplaceStatusLine("HTTP/1.1 307 Temporary Redirect");
(*override_response_headers)->RemoveHeader("Location");
(*override_response_headers)->AddHeader("Location", new_url.spec());
*allowed_unsafe_redirect_url = new_url;
(*override_response_headers)
->AddHeader("Location", translated_url.value().spec());
*allowed_unsafe_redirect_url = translated_url.value();
}

return net::OK;
Expand Down
Loading

0 comments on commit c9db9dc

Please sign in to comment.