/** HTTP session for summon: redirects, cookies, caps. Uses ctx.httpFetch. */ var BARE_SUMMON_UA = 'Summon/0.1 (Bare OS; text)' var BARE_SUMMON_MAX_REDIRECTS = 10 var BARE_SUMMON_DEFAULT_TIMEOUT = 20000 var BARE_SUMMON_DEFAULT_MAX_BYTES = 4 * 1024 * 1024 function bareSummonResolveFetch(ctx) { if (ctx && typeof ctx.httpFetch === 'function') return ctx.httpFetch if (ctx && ctx.bare && typeof ctx.bare.fetch === 'function') return ctx.bare.fetch if (typeof fetch === 'function') return fetch return null } function bareSummonHeadersToObject(h) { var out = {} if (!h) return out if (typeof h.forEach === 'function') { h.forEach(function (v, k) { out[String(k).toLowerCase()] = String(v) }) return out } var k for (k in h) { if (Object.prototype.hasOwnProperty.call(h, k)) out[String(k).toLowerCase()] = String(h[k]) } return out } function bareSummonReadSetCookies(headers, res) { var out = [] if ( res && typeof res.headers !== 'undefined' && res.headers && typeof res.headers.getSetCookie === 'function' ) { try { var g = res.headers.getSetCookie() if (Array.isArray(g)) return g } catch (e) { /* ignore */ } } var raw = headers['set-cookie'] if (!raw) return out if (Array.isArray(raw)) return raw return [String(raw)] } async function bareSummonFetch(ctx, urlStr, opts) { opts = opts || {} var fn = bareSummonResolveFetch(ctx) if (!fn) { return { ok: false, error: 'summon: no HTTP client (ctx.httpFetch)' } } var jar = opts.jar || bareSummonCreateCookieJar() var method = String(opts.method || 'GET').toUpperCase() var body = opts.body var maxRedir = opts.maxRedirects != null ? opts.maxRedirects : BARE_SUMMON_MAX_REDIRECTS var timeout = opts.timeoutMs != null ? opts.timeoutMs : BARE_SUMMON_DEFAULT_TIMEOUT var maxBytes = opts.maxBytes != null ? opts.maxBytes : BARE_SUMMON_DEFAULT_MAX_BYTES var current = urlStr var hops = 0 var lastStatus = 0 var headersOut = {} var text = '' var finalUrl = urlStr while (hops <= maxRedir) { var parsed = bareSummonParseUrl(current) if (!parsed || !bareSummonIsHttp(parsed)) { return { ok: false, error: 'summon: only http(s) URLs', url: current } } var hdr = { 'user-agent': BARE_SUMMON_UA, accept: 'text/html,application/xhtml+xml;q=0.9,text/plain;q=0.8,*/*;q=0.1' } var cookie = jar.headerFor(current) if (cookie) hdr.cookie = cookie if (opts.headers) { var hk for (hk in opts.headers) { if (Object.prototype.hasOwnProperty.call(opts.headers, hk)) { hdr[hk] = opts.headers[hk] } } } var ac = null var timer = null if (typeof AbortController === 'function' && timeout > 0) { ac = new AbortController() timer = setTimeout(function () { try { ac.abort() } catch (e) { /* ignore */ } }, timeout) } var res try { res = await fn(current, { method: method, headers: hdr, body: method === 'GET' || method === 'HEAD' ? undefined : body, redirect: 'manual', signal: ac ? ac.signal : undefined }) } catch (err) { if (timer) clearTimeout(timer) return { ok: false, error: 'summon: request failed: ' + ((err && err.message) || String(err)), url: current } } if (timer) clearTimeout(timer) lastStatus = res && res.status ? res.status : 0 headersOut = bareSummonHeadersToObject(res && res.headers) var setc = bareSummonReadSetCookies(headersOut, res) for (var i = 0; i < setc.length; i++) jar.put(setc[i], current, opts) var loc = headersOut.location if (loc && lastStatus >= 300 && lastStatus < 400 && hops < maxRedir) { var next = bareSummonResolveUrl(loc, current) if (!next) break if ( lastStatus === 303 || ((lastStatus === 301 || lastStatus === 302) && method === 'POST') ) { method = 'GET' body = undefined } current = next.href hops++ continue } finalUrl = current if (typeof res.text === 'function') { text = await res.text() } else if (typeof res.arrayBuffer === 'function') { var ab = await res.arrayBuffer() text = new TextDecoder('utf-8', { fatal: false }).decode( new Uint8Array(ab) ) } if (text && text.length > maxBytes) text = text.slice(0, maxBytes) break } return { ok: lastStatus >= 200 && lastStatus < 400, status: lastStatus, url: finalUrl, headers: headersOut, body: text, jar: jar, redirects: hops } }