Skip to content

Commit 9452cd1

Browse files
committed
WIP configurable ArNS 404 page support
1 parent 79792e1 commit 9452cd1

File tree

4 files changed

+87
-27
lines changed

4 files changed

+87
-27
lines changed

src/config.ts

+14
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,20 @@ if (APEX_ARNS_NAME !== undefined && ARNS_ROOT_HOST === undefined) {
515515
throw new Error('ARNS_ROOT_HOST must be defined when APEX_ARNS_NAME is used');
516516
}
517517

518+
export const ARNS_NOT_FOUND_TX_ID = env.varOrUndefined('ARNS_NOT_FOUND_TX_ID');
519+
520+
export const ARNS_NOT_FOUND_ARNS_NAME = env.varOrUndefined(
521+
'ARNS_NOT_FOUND_ARNS_NAME',
522+
);
523+
if (
524+
ARNS_NOT_FOUND_TX_ID !== undefined &&
525+
ARNS_NOT_FOUND_ARNS_NAME !== undefined
526+
) {
527+
throw new Error(
528+
'ARNS_NOT_FOUND_TX_ID and ARNS_NOT_FOUND_ARNS_NAME are mutually exclusive but both are set.',
529+
);
530+
}
531+
518532
//
519533
// Header caching
520534
//

src/middleware/arns.ts

+71-27
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,34 @@ export const createArnsMiddleware = ({
3737
nameResolver: NameResolver;
3838
}): Handler =>
3939
asyncMiddleware(async (req, res, next) => {
40+
// Skip all ArNS processing if the root ArNS host is not set.
41+
if (config.ARNS_ROOT_HOST !== undefined && config.ARNS_ROOT_HOST !== '') {
42+
next();
43+
return;
44+
}
45+
46+
const hostNameIsArNSRoot = req.hostname === config.ARNS_ROOT_HOST;
47+
48+
if (
49+
// Use apex ID as ArNS root data if it's set.
50+
config.APEX_TX_ID !== undefined &&
51+
hostNameIsArNSRoot
52+
) {
53+
res.header(headerNames.arnsResolvedId, config.APEX_TX_ID);
54+
dataHandler(req, res, next);
55+
}
56+
57+
let arnsSubdomain: string | undefined;
4058
if (
41-
// Ignore requests that do end with the ArNS root hostname.
42-
(config.ARNS_ROOT_HOST !== undefined &&
43-
config.ARNS_ROOT_HOST !== '' &&
44-
!req.hostname.endsWith('.' + config.ARNS_ROOT_HOST)) ||
59+
// If apex ArNS name is set and hostname matches root use the apex ArNS
60+
// name as the ArNS subdomain.
61+
config.APEX_ARNS_NAME !== undefined &&
62+
hostNameIsArNSRoot
63+
) {
64+
arnsSubdomain = config.APEX_ARNS_NAME;
65+
} else if (
66+
// Ignore requests that do not end with the ArNS root hostname.
67+
!req.hostname.endsWith('.' + config.ARNS_ROOT_HOST) ||
4568
// Ignore requests that do not include subdomains since ArNS always
4669
// requires a subdomain.
4770
!Array.isArray(req.subdomains) ||
@@ -51,22 +74,22 @@ export const createArnsMiddleware = ({
5174
) {
5275
next();
5376
return;
77+
} else {
78+
arnsSubdomain = req.subdomains[req.subdomains.length - 1];
5479
}
5580

56-
const arnsSubdomain = req.subdomains[req.subdomains.length - 1];
57-
5881
if (
5982
EXCLUDED_SUBDOMAINS.has(arnsSubdomain) ||
60-
// Avoid collisions with sandbox URLs by ensuring the subdomain length
61-
// is below the minimum length of a sandbox subdomain. Undernames are
62-
// are an exception because they can be longer and '_' cannot appear in
63-
// base32.
83+
// Avoid collisions with sandbox URLs by ensuring the subdomain length is
84+
// below the minimum length of a sandbox subdomain. Undernames are are an
85+
// exception because they can be longer and '_' cannot appear in base32.
6486
(arnsSubdomain.length > MAX_ARNS_NAME_LENGTH && !arnsSubdomain.match(/_/))
6587
) {
6688
next();
6789
return;
6890
}
6991

92+
// TODO: add comment explaining this behavior
7093
if (DATA_PATH_REGEX.test(req.path)) {
7194
next();
7295
return;
@@ -77,34 +100,55 @@ export const createArnsMiddleware = ({
77100
return;
78101
}
79102

80-
// NOTE: Errors and request deduplication are expected to be handled by the
81-
// resolver
103+
// NOTE: Errors and in-flight resolution deduplication are expected to be
104+
// handled by the resolver.
82105
const end = metrics.arnsResolutionTime.startTimer();
83-
const { resolvedId, ttl, processId, resolvedAt, limit, index } =
106+
let { resolvedId, ttl, processId, resolvedAt, limit, index } =
84107
await nameResolver.resolve({
85108
name: arnsSubdomain,
86109
});
87110
end();
88111
if (resolvedId === undefined) {
89-
sendNotFound(res);
90-
return;
112+
res.status(404);
113+
if (
114+
// ArNS undername should not use custom 404 pages.
115+
// TODO: expand this explanation
116+
arnsSubdomain.match(/_/) ||
117+
// Custom 404s should not be used for Apex ArNS.
118+
hostNameIsArNSRoot
119+
) {
120+
sendNotFound(res);
121+
} else if (config.ARNS_NOT_FOUND_TX_ID !== undefined) {
122+
resolvedId = config.ARNS_NOT_FOUND_TX_ID;
123+
} else if (config.ARNS_NOT_FOUND_ARNS_NAME !== undefined) {
124+
({ resolvedId, ttl, processId, resolvedAt, limit, index } =
125+
await nameResolver.resolve({
126+
name: arnsSubdomain,
127+
}));
128+
} else {
129+
sendNotFound(res);
130+
return;
131+
}
91132
}
92133

93134
res.header(headerNames.arnsResolvedId, resolvedId);
94-
res.header(headerNames.arnsTtlSeconds, ttl.toString());
135+
res.header(headerNames.arnsTtlSeconds, ttl?.toString());
95136
res.header(headerNames.arnsProcessId, processId);
96-
res.header(headerNames.arnsResolvedAt, resolvedAt.toString());
97-
// limit and index can be undefined if they come from the cache
98-
res.header(headerNames.arnsLimit, limit?.toString());
99-
res.header(headerNames.arnsIndex, index?.toString());
137+
res.header(headerNames.arnsResolvedAt, resolvedAt?.toString());
138+
// Limit and index can be undefined if they come from a cache that existed
139+
// before they were added.
140+
if (limit !== undefined && index !== undefined) {
141+
res.header(headerNames.arnsLimit, limit.toString());
142+
res.header(headerNames.arnsIndex, index.toString());
100143

101-
// handle undername limit exceeded
102-
if (config.ARNS_RESOLVER_ENFORCE_UNDERNAME_LIMIT && index > limit) {
103-
sendPaymentRequired(
104-
res,
105-
'ArNS undername limit exceeded. Purchase additional undernames to continue.',
106-
);
107-
return;
144+
// handle undername limit exceeded
145+
if (config.ARNS_RESOLVER_ENFORCE_UNDERNAME_LIMIT && index > limit) {
146+
sendPaymentRequired(
147+
res,
148+
'ArNS undername limit exceeded. Purchase additional undernames to continue.',
149+
);
150+
return;
151+
}
108152
}
109153

110154
// TODO: add a header for arns cache status

src/routes/arns.ts

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ if (config.ARNS_ROOT_HOST !== undefined) {
4343
);
4444
}
4545

46+
// TODO: consider moving this into ar-io router
4647
arnsRouter.get('/ar-io/resolver/:name', async (req, res) => {
4748
const { name } = req.params;
4849
// NOTE: Errors and request deduplication are expected to be handled by the

src/routes/default.ts

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ defaultRouter.get('*', async (req, res, next) => {
5252
return dataHandler(modifiedReq, res, next);
5353
}
5454

55+
// TODO: APEX_ARNS_NAME handling into arnsMiddleware
5556
if (APEX_ARNS_NAME !== undefined) {
5657
// Modify the request to add APEX_ARNS_NAME as a subdomain
5758
const modifiedReq = new Proxy(req, {

0 commit comments

Comments
 (0)