openvibers/openvibe.games Commits

The server's 404 page names the client's icon (<link rel="icon" type="image/svg+xml" href="/favicon.svg">, as the client pages do since 0f29769). Without it the browser asked for /favicon.ico, a second 404 and a console error on every not-found page (browser check on /__ovcheck-404, Host scripts/browser-check.js, WS-Q task 3). Test: apps/server/src/net/httpServer.test.ts

OP OpenVibers committed 26 Sept 2026 · 74d039d8d8 · 2 files, +16 −2


Changes

ddiff --git a/apps/server/src/net/httpServer.test.ts b/apps/server/src/net/httpServer.test.ts
iindex b11e3c9..07d547c 100644
---- a/apps/server/src/net/httpServer.test.ts
++++ b/apps/server/src/net/httpServer.test.ts
@@@ -1,8 +1,9 @@
--import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
++import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { request, type IncomingHttpHeaders, type OutgoingHttpHeaders, type Server } from 'node:http'
import type { AddressInfo } from 'node:net'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
++import { fileURLToPath } from 'node:url'
import { createConsoleLogger } from '@openvibe/shared'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import { ServerMetrics } from '../observability/metrics.js'
@@@ -203,6 +204,8 @@ describe('unknown paths answer 404', () => {
expect(r.body).toContain('<a href="/play">Play Scraplandia</a>')
expect(r.body).toContain('<a href="/editor">Map editor</a>')
expect(r.body).not.toMatch(/<script\b/)
++ // Names the client's icon, so the browser does not ask /favicon.ico (a second 404).
++ expect(r.body).toContain('<link rel="icon" type="image/svg+xml" href="/favicon.svg">')
},
)
@@@ -300,4 +303,11 @@ describe('404 page', () => {
expect(page).toContain(`/${'a'.repeat(199)}…</code>`)
expect(page).not.toContain('a'.repeat(201))
})
++
++ it('names the icon the client pages use, and that file ships with the client', () => {
++ const page = notFoundPage('/nope', [])
++ expect(page).toContain('<link rel="icon" type="image/svg+xml" href="/favicon.svg">')
++ const icon = fileURLToPath(new URL('../../../client/public/favicon.svg', import.meta.url))
++ expect(existsSync(icon)).toBe(true)
++ })
})
ddiff --git a/apps/server/src/net/notFound.ts b/apps/server/src/net/notFound.ts
iindex a9489f5..17ec52a 100644
---- a/apps/server/src/net/notFound.ts
++++ b/apps/server/src/net/notFound.ts
@@@ -7,7 +7,10 @@ import { extname } from 'node:path'
* portal and monitors read as up).
*
* Page requests get a small self-contained HTML page that names the path and
-- * links to the host's real entry points. API paths and clients asking for
++ * links to the host's real entry points. It names the client's icon
++ * (/favicon.svg, as the client pages do): without an icon link the browser
++ * asks for /favicon.ico, which is itself a 404 and a console error (browser
++ * check, OpenVibe.Host scripts/browser-check.js). API paths and clients asking for
* JSON get `{"error":"not_found"}` (the shape /auth/* already answers). A
* missing asset (any extension but .html) gets plain text.
*/
@@@ -55,6 +58,7 @@ export function notFoundPage(path: string, links: readonly NotFoundLink[]): stri
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex">
++<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<title>Page not found · OpenVibe.Games</title>
<style>
:root { color-scheme: dark; }