Fixes for http

This commit is contained in:
Raven Scott
2026-04-22 03:55:14 -04:00
parent 7b8fb056c0
commit 4b87f2e2d8
4 changed files with 113 additions and 13 deletions
@@ -48,8 +48,10 @@ export async function ensureBareOsWwwHolesailTunnel(ctx, env, port) {
if (!ctx.b4a || typeof ctx.b4a.from !== 'function') return
const { path, state } = await bareHolesailManagedReadState(ctx, env)
/** Holesail server mode requires an explicit loopback host. */
const wantHost = '127.0.0.1'
/** @type {Record<string, unknown>} */
const want = { server: true, port: p, enabled: true }
const want = { server: true, port: p, host: wantHost, enabled: true }
const existing = state.connections[id]
if (existing && typeof existing === 'object') {
const ex = /** @type {Record<string, unknown>} */ (existing)
@@ -59,7 +61,9 @@ export async function ensureBareOsWwwHolesailTunnel(ctx, env, port) {
ex.enabled === undefined || ex.enabled === null || String(ex.enabled) === ''
? true
: bareHolesailEnvTruthy(ex.enabled)
if (server && curPort === p && enabled) {
const rawHost = String(ex.host ?? '').trim()
const hostOk = rawHost === wantHost
if (server && curPort === p && enabled && hostOk) {
if (bareHolesailManagedServiceIsRunning()) {
try {
await bareHolesailManagedStartOne(ctx, env, id)
@@ -221,17 +221,27 @@ function wwwLog(ctx, line) {
}
/**
* Ensure `~/.www/` exists and contains default `index.html` when missing (idempotent).
* Called from identity unlock/register and from the `bare-os-www` initd unit.
* @param {Record<string, unknown>} ctx
*/
async function ensureDefaultIndex(ctx) {
export async function ensureBareOsWwwHomeDefaults(ctx) {
const vfs = ctx.vfs
if (!vfs || typeof vfs.resolveLogical !== 'function') return
const idx = vfs.resolveLogical('~/.www/index.html')
try {
await vfs.readFile(idx)
return
const cur = await vfs.readFile(idx)
if (cur != null) {
const u8 =
cur instanceof Uint8Array
? cur
: ctx.b4a && typeof ctx.b4a.from === 'function'
? ctx.b4a.from(cur)
: null
if (u8 && u8.byteLength > 0) return
}
} catch {
/* missing */
/* missing or unreadable */
}
try {
await vfs.mkdir(vfs.resolveLogical('~/.www'), { recursive: true })
@@ -253,6 +263,13 @@ async function ensureDefaultIndex(ctx) {
}
}
/**
* @param {Record<string, unknown>} ctx
*/
async function ensureDefaultIndex(ctx) {
await ensureBareOsWwwHomeDefaults(ctx)
}
/**
* @param {import('node:http').IncomingMessage} req
* @param {import('node:http').ServerResponse} res
@@ -275,13 +292,44 @@ async function handleWwwRequest(req, res, ctx, docrootNorm) {
return
}
const vfs = ctx.vfs
if (!vfs || typeof vfs.readFile !== 'function') {
if (
!vfs ||
typeof vfs.readFile !== 'function' ||
typeof vfs.stat !== 'function'
) {
res.statusCode = 500
res.end('VFS unavailable\n')
return
}
try {
if (req.method === 'HEAD') {
const st = await vfs.stat(abs)
if (!st || st.type === 'directory') {
res.statusCode = 404
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end(
'<!DOCTYPE html><html><head><meta charset="utf-8"/><title>404</title></head><body><h1>404</h1></body></html>\n'
)
return
}
const len = Number(st.size) || 0
res.statusCode = 200
res.setHeader('Content-Type', mimeForPath(abs))
res.setHeader('Cache-Control', 'no-cache')
res.setHeader('Content-Length', String(len))
res.end()
return
}
const buf = await vfs.readFile(abs)
if (buf == null) {
res.statusCode = 404
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end(
'<!DOCTYPE html><html><head><meta charset="utf-8"/><title>404</title></head><body><h1>404</h1></body></html>\n'
)
return
}
const u8 =
buf instanceof Uint8Array
? buf
@@ -291,18 +339,16 @@ async function handleWwwRequest(req, res, ctx, docrootNorm) {
res.statusCode = 200
res.setHeader('Content-Type', mimeForPath(abs))
res.setHeader('Cache-Control', 'no-cache')
if (req.method === 'HEAD') {
res.setHeader('Content-Length', String(u8.byteLength))
res.end()
return
}
res.setHeader('Content-Length', String(u8.byteLength))
res.end(u8)
} catch (e) {
const code = /** @type {{ code?: string }} */ (e)?.code
if (code === 'ENOENT') {
res.statusCode = 404
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end('<!DOCTYPE html><html><head><meta charset="utf-8"/><title>404</title></head><body><h1>404</h1></body></html>\n')
res.end(
'<!DOCTYPE html><html><head><meta charset="utf-8"/><title>404</title></head><body><h1>404</h1></body></html>\n'
)
return
}
res.statusCode = 500
@@ -27,6 +27,7 @@ import {
SSHD_DEFAULT_AUTHORIZED_KEYS_FILE
} from './sshd-config-parse.js'
import { ensureBareOsVarLogTree } from './bare-os-var-log.js'
import { ensureBareOsWwwHomeDefaults } from './bare-os-www-initd.js'
const LEGACY_ROOT_MIGRATION_STATE = '/.bare-os/migration/legacy-root-v1.json'
@@ -373,6 +374,11 @@ export async function applyLoginKeys(ctx, keys) {
wipeSecret(ctx)
await applyUnlockedEnv(ctx, keys.publicKey, keys.secretKey)
await ensureBareDir(ctx)
try {
await ensureBareOsWwwHomeDefaults(ctx)
} catch (e) {
ctx.console?.error?.('[bare-os] ensureBareOsWwwHomeDefaults: ' + (e?.message || e))
}
maybeAppendVaultMultisigContinuityAudit(ctx, 'applyLoginKeys')
}
@@ -451,6 +457,11 @@ export async function registerIdentity(ctx, passphrase) {
await applyUnlockedEnv(ctx, publicKey, secretKey)
secureZero(secretKey)
await ensureBareDir(ctx)
try {
await ensureBareOsWwwHomeDefaults(ctx)
} catch (e) {
ctx.console?.error?.('[bare-os] ensureBareOsWwwHomeDefaults: ' + (e?.message || e))
}
try {
await bareOsAppendVaultRotationCheckpoint(ctx, {
kind: 'identity_register',
@@ -494,6 +505,11 @@ export async function unlockIdentity(ctx, passphrase) {
wipeSecret(ctx)
await applyUnlockedEnv(ctx, publicKey, secretKey)
await ensureBareDir(ctx)
try {
await ensureBareOsWwwHomeDefaults(ctx)
} catch (e) {
ctx.console?.error?.('[bare-os] ensureBareOsWwwHomeDefaults: ' + (e?.message || e))
}
try {
await bareOsAppendVaultRotationCheckpoint(ctx, {
kind: 'identity_unlock',
@@ -12,6 +12,7 @@ import {
bareOsWwwInitdEnabled,
bareOsWwwResolveStaticPath,
BARE_OS_WWW_DEFAULT_INDEX,
ensureBareOsWwwHomeDefaults,
maybeRegisterBareOsWwwInitd
} from './lib/bare-os-www-initd.js'
import { findBareServiceDefinition } from './lib/bare-initd.js'
@@ -133,6 +134,21 @@ test('bare-os-www serves default index on ephemeral port', async (t) => {
})
t.ok(String(body).includes('Bare OS'))
t.ok(String(body).includes('~/.www'))
const headLen = await new Promise((resolve, reject) => {
const r = http.request(
{ hostname: '127.0.0.1', port, path: '/', method: 'HEAD' },
(res) => {
t.is(res.statusCode, 200)
const cl = res.headers['content-length']
t.ok(cl && Number(cl) > 100, 'HEAD Content-Length reflects body size')
res.resume()
resolve(Number(cl))
}
)
r.on('error', reject)
r.end()
})
t.ok(headLen > 100)
await /** @type {{ stop: (ctx: unknown) => Promise<void> }} */ (svc).stop(ctx)
await store.close()
rmSync(dir, { recursive: true, force: true })
@@ -156,6 +172,7 @@ test('ensureBareOsWwwHolesailTunnel persists managed server entry', async (t) =>
const row = state.connections[id]
t.ok(row && /** @type {{ server?: boolean }} */ (row).server === true)
t.is(/** @type {{ port?: number }} */ (row).port, p)
t.is(/** @type {{ host?: string }} */ (row).host, '127.0.0.1')
await store.close()
rmSync(dir, { recursive: true, force: true })
})
@@ -164,3 +181,20 @@ test('BARE_OS_WWW_DEFAULT_INDEX is non-trivial HTML', (t) => {
t.ok(BARE_OS_WWW_DEFAULT_INDEX.includes('<!DOCTYPE html>'))
t.ok(BARE_OS_WWW_DEFAULT_INDEX.includes('Bare OS'))
})
test('ensureBareOsWwwHomeDefaults creates ~/.www/index.html', async (t) => {
const dir = testCorestoreDir('wwwdefaults')
const store = new Corestore(dir)
const sys = new Hyperdrive(store)
const personal = new Hyperdrive(store.namespace('pdef'))
await sys.ready()
await personal.ready()
const ctx = testCtx(sys, personal)
await ensureBareOsWwwHomeDefaults(ctx)
const idx = ctx.vfs.resolveLogical('~/.www/index.html')
const buf = await ctx.vfs.readFile(idx)
t.ok(buf && buf.byteLength > 50)
t.ok(b4a.toString(buf, 'utf8').includes('Bare OS'))
await store.close()
rmSync(dir, { recursive: true, force: true })
})