Files
bare-operating-system/packages/bare-os-coreutils/lib/summon/summon-http.js
T
2026-08-18 18:11:28 -04:00

168 lines
4.6 KiB
JavaScript

/** 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
}
}