(name, url)
| 1137 | // ─── Server Capability Check ───────────────────────────────────────────────── |
| 1138 | |
| 1139 | async function checkServerCapabilities(name, url) { |
| 1140 | showToast('Checking server capabilities...', 'info'); |
| 1141 | |
| 1142 | const testHostname = 'example.com'; |
| 1143 | const dnsQuery = buildDNSQuery(testHostname); |
| 1144 | const usesJsonApi = (() => { |
| 1145 | try { return new URL(url).pathname.includes('/resolve'); } |
| 1146 | catch { return false; } |
| 1147 | })(); |
| 1148 | |
| 1149 | const wireGetUrl = (() => { |
| 1150 | const u = new URL(url); |
| 1151 | u.searchParams.set('dns', encodeDnsQueryBase64Url(dnsQuery)); |
| 1152 | return u; |
| 1153 | })(); |
| 1154 | |
| 1155 | const jsonGetUrl = (() => { |
| 1156 | const u = new URL(url); |
| 1157 | u.searchParams.set('name', testHostname); |
| 1158 | u.searchParams.set('type', 'A'); |
| 1159 | u.searchParams.set('nocache', Date.now()); |
| 1160 | return u; |
| 1161 | })(); |
| 1162 | |
| 1163 | const withTimeout = async (input, options = {}) => { |
| 1164 | const controller = new AbortController(); |
| 1165 | const timer = setTimeout(() => controller.abort(), 4000); |
| 1166 | try { return await fetch(input, { ...options, signal: controller.signal }); } |
| 1167 | catch (error) { console.error('Capability test error', { input, options, error }); return null; } |
| 1168 | finally { clearTimeout(timer); } |
| 1169 | }; |
| 1170 | |
| 1171 | const testGet = async (mode) => { |
| 1172 | const urlToUse = usesJsonApi ? jsonGetUrl : wireGetUrl; |
| 1173 | const headers = usesJsonApi ? DNS_JSON_HEADERS : DNS_MESSAGE_HEADERS; |
| 1174 | const response = await withTimeout(urlToUse, { method: 'GET', mode, headers }); |
| 1175 | if (!response) return { success: false, cors: false }; |
| 1176 | if (mode === 'cors') return { success: response.ok, cors: response.type === 'cors' }; |
| 1177 | return { success: true, cors: false }; |
| 1178 | }; |
| 1179 | |
| 1180 | const testPostCors = async () => { |
| 1181 | if (usesJsonApi) return { success: false, cors: false }; |
| 1182 | const response = await withTimeout(url, { |
| 1183 | method: 'POST', mode: 'cors', |
| 1184 | headers: { 'Content-Type': 'application/dns-message', ...DNS_MESSAGE_HEADERS }, |
| 1185 | body: dnsQuery |
| 1186 | }); |
| 1187 | if (!response) return { success: false, cors: false }; |
| 1188 | return { success: response.ok, cors: response.type === 'cors' }; |
| 1189 | }; |
| 1190 | |
| 1191 | const [getCors, postCors, getNoCors] = await Promise.all([ |
| 1192 | testGet('cors'), testPostCors(), testGet('no-cors') |
| 1193 | ]); |
| 1194 | |
| 1195 | let chosenType = null; |
| 1196 | let allowCors = false; |
no test coverage detected