From 3dd2a634a0629eeede4d8c09653881f41569f1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Sun, 11 Apr 2021 14:56:03 +0200 Subject: [PATCH] Allowing loading HTTP local resources from a HTTPS endpoint. By default, maps are loaded in HTTPS if WorkAdventure is running in HTTPS, and in HTTP is WorkAdventure is running in HTTP. Also, if WorkAdventure is running in HTTP and map loading fails, we try map loading in HTTPS (useful when we are working on WorkAdventure locally and want to load a map on a secure domain). This commit adds the last combination: If WorkAdventure is running in HTTPS, and map loading fails in HTTPS **AND** if the map URL is targetting "localhost", "*.localhost" or "127.0.0.1", then we attempt to load the resource in HTTP. Why? "localhost" is considered secure context by modern browsers. So even if a page is loaded in HTTPS, it can load resources from any secure context (including localhost in HTTP). This means that from "https://play.workadventu.re", I can now test a map running locally on my machine (served by a classic webserver without any certificate). This change should make map testing easier, since map developers will not have to install the whole WorkAdventure project to test their map locally. --- front/src/Phaser/Game/GameScene.ts | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 9a165eb5..6c4c6e3e 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -161,6 +161,7 @@ export class GameScene extends ResizableScene implements CenterListener { private characterLayers!: string[]; private messageSubscription: Subscription|null = null; private popUpElements : Map = new Map(); + private originalMapUrl: string|undefined; constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) { super({ @@ -195,7 +196,8 @@ export class GameScene extends ResizableScene implements CenterListener { this.load.image(openChatIconName, 'resources/objects/talk.png'); this.load.on(FILE_LOAD_ERROR, (file: {src: string}) => { // If we happen to be in HTTP and we are trying to load a URL in HTTPS only... (this happens only in dev environments) - if (window.location.protocol === 'http:' && file.src === this.MapUrlFile && file.src.startsWith('http:')) { + if (window.location.protocol === 'http:' && file.src === this.MapUrlFile && file.src.startsWith('http:') && this.originalMapUrl === undefined) { + this.originalMapUrl = this.MapUrlFile; this.MapUrlFile = this.MapUrlFile.replace('http://', 'https://'); this.load.tilemapTiledJSON(this.MapUrlFile, this.MapUrlFile); this.load.on('filecomplete-tilemapJSON-'+this.MapUrlFile, (key: string, type: string, data: unknown) => { @@ -203,10 +205,25 @@ export class GameScene extends ResizableScene implements CenterListener { }); return; } + // 127.0.0.1, localhost and *.localhost are considered secure, even on HTTP. + // So if we are in https, we can still try to load a HTTP local resource (can be useful for testing purposes) + // See https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure + const url = new URL(file.src); + const host = url.host.split(':')[0]; + if (window.location.protocol === 'https:' && file.src === this.MapUrlFile && (host === '127.0.0.1' || host === 'localhost' || host.endsWith('.localhost')) && this.originalMapUrl === undefined) { + this.originalMapUrl = this.MapUrlFile; + this.MapUrlFile = this.MapUrlFile.replace('https://', 'http://'); + this.load.tilemapTiledJSON(this.MapUrlFile, this.MapUrlFile); + this.load.on('filecomplete-tilemapJSON-'+this.MapUrlFile, (key: string, type: string, data: unknown) => { + this.onMapLoad(data); + }); + return; + } + this.scene.start(ErrorSceneName, { title: 'Network error', subTitle: 'An error occurred while loading resource:', - message: file.src + message: this.originalMapUrl ?? file.src }); }); this.load.on('filecomplete-tilemapJSON-'+this.MapUrlFile, (key: string, type: string, data: unknown) => {