Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/dweb-api-resolver/src/resolver/arweave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,9 @@ export const arweaveUrlToSandboxSubdomain = async (
return arweave_gateway;
}

// Modify hostname to add subdomain
const url = new URL(arweave_gateway.toString());
url.host = subdomain + "." + url.host;
url.hostname = subdomain + "." + url.hostname;

return url;
};
40 changes: 31 additions & 9 deletions packages/dweb-api-resolver/src/resolver/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,39 @@ export const recordToProxyRecord = async (
overrideCodecHeader,
};
} else if (record.codec === "arweave-ns") {
const backend = new URL(arweaveConfig.getBackend());
const backendString = arweaveConfig.getBackend();
const backend = new URL(backendString);

// Extract explicit port from original backend string
// Use lookahead to match port followed by /, ?, #, or end of string
let explicitPort: string | null = null;
const portMatch = backendString.match(/:(\d+)(?=\/|\?|#|$)/);
if (portMatch) {
explicitPort = portMatch[1];
}

const resultUrl = await arweaveUrlToSandboxSubdomain(
request,
logger,
record.DoHContentIdentifier,
backend,
);

// Manually construct XContentLocation to preserve explicit port
// Note: We cannot use url.port = explicitPort because the URL API
// automatically normalizes default ports (443 for https, 80 for http)
// The URL components (pathname, search, hash) are already properly encoded by the URL object
let xContentLocation: string;
if (explicitPort) {
// Preserve explicit port even if it's a default port
xContentLocation = `${resultUrl.protocol}//${resultUrl.hostname}:${explicitPort}${resultUrl.pathname}${resultUrl.search}${resultUrl.hash}`;
} else {
xContentLocation = resultUrl.toString();
}

return {
...record,
XContentLocation: (
await arweaveUrlToSandboxSubdomain(
request,
logger,
record.DoHContentIdentifier,
backend,
)
).toString(),
XContentLocation: xContentLocation,
XContentPath: ensureTrailingSlash("/" + record.DoHContentIdentifier),
};
} else if (record.codec === "swarm") {
Expand Down
64 changes: 64 additions & 0 deletions packages/dweb-api-server/src/test/integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,70 @@ describe("Proxy API Integration Tests", function () {
expect(res.header("x-content-path")).to.equal("/");
expect(res.header("x-content-storage-type")).to.equal("ipns-ns");
});

it("should preserve port in X-Content-Location for arweave with explicit port", async () => {
// Set arweave backend with explicit non-default port
harnessInput.configurationService.set((conf) => {
conf.arweave.backend = "https://arweave.net:8443";
});

const { content_location, content_path, content_storage_type, res } =
await commonSetup({
name: "makesy.eth",
type: "arweave",
contentHash: "arweave://Gum-G8CFTCIJIeDVJSxAzB9qNy2zv7SC4Cv_bgw7I3g",
additionalInfo: {
arweave: {
result: "Gum-G8CFTCIJIeDVJSxAzB9qNy2zv7SC4Cv_bgw7I3g",
query: "Gum-G8CFTCIJIeDVJSxAzB9qNy2zv7SC4Cv_bgw7I3g",
subdomain_sandbox_id:
"dlu34g6aqvgcecjb4dksklcazqpwunznwo73jaxafp7w4db3en4a",
},
},
options: populateDefaultOptions({}),
});

expect(res.statusCode).to.be.equal(200);
expect(content_location).to.be.equal(
"dlu34g6aqvgcecjb4dksklcazqpwunznwo73jaxafp7w4db3en4a.arweave.net:8443",
);
expect(content_path).to.be.equal(
"/Gum-G8CFTCIJIeDVJSxAzB9qNy2zv7SC4Cv_bgw7I3g/",
);
expect(content_storage_type).to.be.equal("arweave-ns");
});

it("should preserve explicit default port 443 in X-Content-Location for arweave", async () => {
// Set arweave backend with explicit default port 443
harnessInput.configurationService.set((conf) => {
conf.arweave.backend = "https://arweave.net:443";
});

const { content_location, content_path, content_storage_type, res } =
await commonSetup({
name: "makesy.eth",
type: "arweave",
contentHash: "arweave://Gum-G8CFTCIJIeDVJSxAzB9qNy2zv7SC4Cv_bgw7I3g",
additionalInfo: {
arweave: {
result: "Gum-G8CFTCIJIeDVJSxAzB9qNy2zv7SC4Cv_bgw7I3g",
query: "Gum-G8CFTCIJIeDVJSxAzB9qNy2zv7SC4Cv_bgw7I3g",
subdomain_sandbox_id:
"dlu34g6aqvgcecjb4dksklcazqpwunznwo73jaxafp7w4db3en4a",
},
},
options: populateDefaultOptions({}),
});

expect(res.statusCode).to.be.equal(200);
expect(content_location).to.be.equal(
"dlu34g6aqvgcecjb4dksklcazqpwunznwo73jaxafp7w4db3en4a.arweave.net:443",
);
expect(content_path).to.be.equal(
"/Gum-G8CFTCIJIeDVJSxAzB9qNy2zv7SC4Cv_bgw7I3g/",
);
expect(content_storage_type).to.be.equal("arweave-ns");
});
});

describe("Caddy API Integration Tests", function () {
Expand Down