feat(editor): F4.6d the web browser and web server on a computer's desktop
The oracle BrowserWindowRun uses Java's browser and web server windows as a person does; the room keeps a browser window per computer and sends a program window's view, the start names the person's language. The editor shows the page in a sandboxed frame, links and forms go to the room, the web server has its button, virtual hosts and lines. Better than Java: relative images and links count from the page's folder, a mark stays in the page, the error page from Java's template, the virtual hosts' file with complete rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,8 @@ import {
|
||||
formatHttpResponse,
|
||||
imageSources,
|
||||
javaBase64,
|
||||
linkTarget,
|
||||
pageReference,
|
||||
javaBase64Decode,
|
||||
parseHttpRequest,
|
||||
parseHttpResponse,
|
||||
@@ -80,6 +82,29 @@ describe("the browser's address line (erzeugeURL)", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("a page's references", () => {
|
||||
it('count from the page\'s folder, written as Java writes them for a page in the root (Java counts from the root)', () => {
|
||||
expect(pageReference('/', 'bild.png')).toBe('bild.png');
|
||||
expect(pageReference('/seite.html?x=1/2', 'unter/bild.png')).toBe('unter/bild.png');
|
||||
expect(pageReference('/ordner/seite.html', 'bild.png')).toBe('ordner/bild.png');
|
||||
expect(pageReference('/vhost1/', '../bild.png')).toBe('vhost1/../bild.png');
|
||||
expect(pageReference('ohne/schraegstrich.html', 'a.png')).toBe('ohne/a.png');
|
||||
expect(pageReference('/ordner/seite.html', '/bild.png')).toBe('/bild.png');
|
||||
expect(pageReference('/ordner/seite.html', 'http://h/x.png')).toBe('http://h/x.png');
|
||||
});
|
||||
|
||||
it('lead a link to the page\'s host, an http address to its own, and nowhere for a mark or another scheme', () => {
|
||||
const page = { host: 'www.filius.de', path: '/ordner/seite.html' };
|
||||
expect(linkTarget(page, 'vier.html')).toEqual({ host: 'www.filius.de', path: '/ordner/vier.html' });
|
||||
expect(linkTarget(page, ' /text.txt ')).toEqual({ host: 'www.filius.de', path: '/text.txt' });
|
||||
expect(linkTarget(page, 'ziel.html?a=1#oben')).toEqual({ host: 'www.filius.de', path: '/ordner/ziel.html?a=1' });
|
||||
expect(linkTarget({ host: 'h', path: '/' }, 'a.html')).toEqual({ host: 'h', path: '/a.html' });
|
||||
expect(linkTarget(page, 'HTTP://192.168.0.30/x/y#z')).toEqual({ host: '192.168.0.30', path: '/x/y' });
|
||||
expect(linkTarget(page, 'http://kein host/')).toBeUndefined();
|
||||
for (const href of ['', ' ', '#oben', 'mailto:anna@filius.de', 'javascript:alert(1)', 'ftp://h/']) expect(linkTarget(page, href), href).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('the images of a page', () => {
|
||||
it('finds each img tag, in any case, its src in any quotes', () => {
|
||||
expect(imageSources('<img src="a.png"><IMG SRC=\'b.png\'> <img alt="x" src=c/d.png><img\nsrc = "e.png" /><imgx src="no.png"><img alt="no src">')).toEqual([
|
||||
@@ -181,6 +206,42 @@ describe('the web server', () => {
|
||||
expect(ask(plain, 'GET / HTTP/1.1\nHost: h0.de')[0]).toBe('HTTP/1.1 200 OK\nContent-type: text/html\n\nStart');
|
||||
});
|
||||
|
||||
it('switches its virtual hosts and writes their table with the complete rows, which it reads again as written', () => {
|
||||
const fileSystem = files({ '/webserver/index.html': 'Start', '/webserver/v/index.html': 'V' });
|
||||
const engine = startedEngine(network(fileSystem));
|
||||
const server = engine.webServer(SERVER)!;
|
||||
expect([server.useVirtualHosts, server.virtualHostTable]).toEqual([false, [['', ''], ['', ''], ['', ''], ['', ''], ['', '']]]);
|
||||
server.setVirtualHost(0, 0, 'h.de');
|
||||
// An incomplete row is not written: the folder is made, the file empty.
|
||||
expect(fileSystem.textFile('/www.conf/vhosts')).toBe('');
|
||||
server.setVirtualHost(0, 1, 'v');
|
||||
server.setVirtualHost(2, 1, 'nur');
|
||||
server.setVirtualHost(4, 0, 'z.de');
|
||||
server.setVirtualHost(4, 1, 'w');
|
||||
server.setVirtualHost(5, 0, 'nein.de');
|
||||
expect(server.virtualHostTable).toEqual([['h.de', 'v'], ['', ''], ['', 'nur'], ['', ''], ['z.de', 'w']]);
|
||||
expect(fileSystem.textFile('/www.conf/vhosts')).toBe('h.de\nv\nz.de\nw\n');
|
||||
// Off, the table counts for nothing; on, from the next request.
|
||||
expect(ask(engine, 'GET / HTTP/1.1\nHost: h.de')[0]!.endsWith('\n\nStart')).toBe(true);
|
||||
server.setUseVirtualHosts(true);
|
||||
expect(ask(engine, 'GET / HTTP/1.1\nHost: H.DE', 'GET / HTTP/1.1\nHost: x.de')[0]!.endsWith('\n\nV')).toBe(true);
|
||||
const again = startedEngine(network(fileSystem)).webServer(SERVER)!;
|
||||
expect([again.useVirtualHosts, again.virtualHostTable]).toEqual([true, [['h.de', 'v'], ['z.de', 'w'], ['', ''], ['', ''], ['', '']]]);
|
||||
});
|
||||
|
||||
it('keeps its table where /www.conf is a file and the vhosts cannot be written', () => {
|
||||
const fileSystem = files({ '/www.conf': 'kein Ordner' });
|
||||
const server = startedEngine(network(fileSystem)).webServer(SERVER)!;
|
||||
server.setVirtualHost(0, 0, 'h.de');
|
||||
server.setVirtualHost(0, 1, 'v');
|
||||
expect(server.virtualHostTable[0]).toEqual(['h.de', 'v']);
|
||||
expect(fileSystem.textFile('/www.conf')).toBe('kein Ordner');
|
||||
// Without files at all, the table holds too.
|
||||
const bare = startedEngine({ ...network(new VirtualFileSystem()), fileSystemsByNodeId: new Map() }).webServer(SERVER)!;
|
||||
bare.setVirtualHost(1, 1, 'x');
|
||||
expect(bare.virtualHostTable[1]).toEqual(['', 'x']);
|
||||
});
|
||||
|
||||
it('serves nothing without files', () => {
|
||||
const engine = startedEngine({ ...network(new VirtualFileSystem()), fileSystemsByNodeId: new Map() });
|
||||
expect(ask(engine, 'GET / HTTP/1.1')).toEqual(['HTTP/1.1 404 Not Found\n']);
|
||||
@@ -206,6 +267,24 @@ describe('the web browser', () => {
|
||||
expect(browser.page!.images.size).toBe(0);
|
||||
});
|
||||
|
||||
it("asks for a page's images from the page's folder and keeps them by their src, with the page's host and path", () => {
|
||||
const fileSystem = files({ '/webserver/ordner/seite.html': '<img src="a.png"><img src="/b.png">' });
|
||||
fileSystem.writeImageFile('/webserver/ordner/a.png', new Uint8Array([1]), 'image/png');
|
||||
fileSystem.writeImageFile('/webserver/b.png', new Uint8Array([2]), 'image/png');
|
||||
const engine = startedEngine(network(fileSystem));
|
||||
const browser = engine.webBrowser(CLIENT)!;
|
||||
browser.open('10.0.0.1/ordner/seite.html?x=1');
|
||||
engine.handle({ type: 'advance', toMilliseconds: 10_000 });
|
||||
expect(browser.page).toMatchObject({ host: '10.0.0.1', path: '/ordner/seite.html?x=1' });
|
||||
expect([...browser.page!.images]).toEqual([['a.png', new Uint8Array([1])], ['/b.png', new Uint8Array([2])]]);
|
||||
const lines = browser.log.all.map(({ timeMilliseconds: _, ...line }) => line);
|
||||
expect(lines).toEqual([
|
||||
{ kind: 'page', host: '10.0.0.1', path: '/ordner/seite.html?x=1', statusCode: 200, contentType: 'text/html', body: '<img src="a.png"><img src="/b.png">' },
|
||||
{ kind: 'image', source: 'a.png', statusCode: 200, contentType: 'image/png', data: new Uint8Array([1]) },
|
||||
{ kind: 'image', source: '/b.png', statusCode: 200, contentType: 'image/png', data: new Uint8Array([2]) },
|
||||
]);
|
||||
});
|
||||
|
||||
it('fetches images only for an HTML page', () => {
|
||||
const engine = startedEngine(network(files({ '/webserver/text.txt': '<img src="a.png">' })));
|
||||
const browser = engine.webBrowser(CLIENT)!;
|
||||
|
||||
Reference in New Issue
Block a user