17
17
*/
18
18
import { Handler } from 'express' ;
19
19
import { asyncMiddleware } from 'middleware-async' ;
20
+ import { URL } from 'node:url' ;
20
21
21
22
import * as config from '../config.js' ;
22
23
import { headerNames } from '../constants.js' ;
@@ -37,11 +38,35 @@ export const createArnsMiddleware = ({
37
38
nameResolver : NameResolver ;
38
39
} ) : Handler =>
39
40
asyncMiddleware ( async ( req , res , next ) => {
41
+ // Skip all ArNS processing if the root ArNS host is not set.
42
+ if ( config . ARNS_ROOT_HOST === undefined || config . ARNS_ROOT_HOST === '' ) {
43
+ next ( ) ;
44
+ return ;
45
+ }
46
+
47
+ const hostNameIsArNSRoot = req . hostname === config . ARNS_ROOT_HOST ;
48
+
40
49
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 ) ) ||
50
+ // Use apex ID as ArNS root data if it's set.
51
+ config . APEX_TX_ID !== undefined &&
52
+ hostNameIsArNSRoot
53
+ ) {
54
+ res . header ( headerNames . arnsResolvedId , config . APEX_TX_ID ) ;
55
+ dataHandler ( req , res , next ) ;
56
+ return ;
57
+ }
58
+
59
+ let arnsSubdomain : string | undefined ;
60
+ if (
61
+ // If apex ArNS name is set and hostname matches root use the apex ArNS
62
+ // name as the ArNS subdomain.
63
+ config . APEX_ARNS_NAME !== undefined &&
64
+ hostNameIsArNSRoot
65
+ ) {
66
+ arnsSubdomain = config . APEX_ARNS_NAME ;
67
+ } else if (
68
+ // Ignore requests that do not end with the ArNS root hostname.
69
+ ! req . hostname . endsWith ( '.' + config . ARNS_ROOT_HOST ) ||
45
70
// Ignore requests that do not include subdomains since ArNS always
46
71
// requires a subdomain.
47
72
! Array . isArray ( req . subdomains ) ||
@@ -51,22 +76,22 @@ export const createArnsMiddleware = ({
51
76
) {
52
77
next ( ) ;
53
78
return ;
79
+ } else {
80
+ arnsSubdomain = req . subdomains [ req . subdomains . length - 1 ] ;
54
81
}
55
82
56
- const arnsSubdomain = req . subdomains [ req . subdomains . length - 1 ] ;
57
-
58
83
if (
59
84
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.
85
+ // Avoid collisions with sandbox URLs by ensuring the subdomain length is
86
+ // below the minimum length of a sandbox subdomain. Undernames are are an
87
+ // exception because they can be longer and '_' cannot appear in base32.
64
88
( arnsSubdomain . length > MAX_ARNS_NAME_LENGTH && ! arnsSubdomain . match ( / _ / ) )
65
89
) {
66
90
next ( ) ;
67
91
return ;
68
92
}
69
93
94
+ // TODO: add comment explaining this behavior
70
95
if ( DATA_PATH_REGEX . test ( req . path ) ) {
71
96
next ( ) ;
72
97
return ;
@@ -77,34 +102,71 @@ export const createArnsMiddleware = ({
77
102
return ;
78
103
}
79
104
80
- // NOTE: Errors and request deduplication are expected to be handled by the
81
- // resolver
105
+ // NOTE: Errors and in-flight resolution deduplication are expected to be
106
+ // handled by the resolver.
82
107
const end = metrics . arnsResolutionTime . startTimer ( ) ;
83
- const { resolvedId, ttl, processId, resolvedAt, limit, index } =
108
+ let { resolvedId, ttl, processId, resolvedAt, limit, index } =
84
109
await nameResolver . resolve ( {
85
110
name : arnsSubdomain ,
86
111
} ) ;
87
112
end ( ) ;
88
113
if ( resolvedId === undefined ) {
89
- sendNotFound ( res ) ;
90
- return ;
114
+ // Extract host from referer if available
115
+ let refererHost ;
116
+ if ( req . headers . referer !== undefined ) {
117
+ try {
118
+ const refererUrl = new URL ( req . headers . referer ) ;
119
+ refererHost = refererUrl . host ;
120
+ } catch ( e ) {
121
+ // Invalid URL, ignore
122
+ }
123
+ }
124
+ if ( req . path === '' || req . path === '/' ) {
125
+ res . status ( 404 ) ;
126
+ } else if ( req . headers . host !== refererHost ) {
127
+ res . redirect ( '/' ) ;
128
+ return ;
129
+ }
130
+ if (
131
+ // ArNS undername should not use custom 404 pages.
132
+ // TODO: expand this explanation
133
+ arnsSubdomain ?. match ( / _ / ) ||
134
+ // Custom 404s should not be used for Apex ArNS.
135
+ hostNameIsArNSRoot
136
+ ) {
137
+ sendNotFound ( res ) ;
138
+ return ;
139
+ } else if ( config . ARNS_NOT_FOUND_TX_ID !== undefined ) {
140
+ resolvedId = config . ARNS_NOT_FOUND_TX_ID ;
141
+ } else if ( config . ARNS_NOT_FOUND_ARNS_NAME !== undefined ) {
142
+ ( { resolvedId, ttl, processId, resolvedAt, limit, index } =
143
+ await nameResolver . resolve ( {
144
+ name : config . ARNS_NOT_FOUND_ARNS_NAME ,
145
+ } ) ) ;
146
+ } else {
147
+ sendNotFound ( res ) ;
148
+ return ;
149
+ }
91
150
}
92
151
93
152
res . header ( headerNames . arnsResolvedId , resolvedId ) ;
94
- res . header ( headerNames . arnsTtlSeconds , ttl . toString ( ) ) ;
153
+ res . header ( headerNames . arnsTtlSeconds , ttl ? .toString ( ) ) ;
95
154
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 ( ) ) ;
155
+ res . header ( headerNames . arnsResolvedAt , resolvedAt ?. toString ( ) ) ;
156
+ // Limit and index can be undefined if they come from a cache that existed
157
+ // before they were added.
158
+ if ( limit !== undefined && index !== undefined ) {
159
+ res . header ( headerNames . arnsLimit , limit . toString ( ) ) ;
160
+ res . header ( headerNames . arnsIndex , index . toString ( ) ) ;
100
161
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 ;
162
+ // handle undername limit exceeded
163
+ if ( config . ARNS_RESOLVER_ENFORCE_UNDERNAME_LIMIT && index > limit ) {
164
+ sendPaymentRequired (
165
+ res ,
166
+ 'ArNS undername limit exceeded. Purchase additional undernames to continue.' ,
167
+ ) ;
168
+ return ;
169
+ }
108
170
}
109
171
110
172
// TODO: add a header for arns cache status
0 commit comments