[Scummvm-git-logs] scummvm master -> 03f1b5610d0a1293821afbc739154206b031e00b

chkuendig noreply at scummvm.org
Wed Aug 13 16:39:05 UTC 2025


This automated email contains information about 12 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .

Summary:
a7b33fbd9b CLOUD: Remove unneeded checks for USE_LIBCURL
ea3c595ce8 CLOUD: Rename 'backends/networking/curl' to 'backends/networking/http' for future alternative http clients
4c3a3bdaff CLOUD: Remove direct use of 'curl_slist' and instead use Common::Array<String>
629150b6e9 EMSCRIPTEN: CLOUD: Implement cloud support in Emscripten
f35ff12669 CLOUD: FS: EMSCRIPTEN: Implement cloud-backed filesystem so games can directly be run from cloud storage
8bb1ead700 TESTBED: Fix Cloud tests
1b99f57b72 CLOUD: Fallback to local date if not sent from backend.
ebfc9c8f55 CLOUD: Refactor ConnectionManager and NetworkReadStream
007efa7fba BACKENDS: NETWORKING: Fix new[]/free mismatch
57df0a6049 BACKENDS: Untangle networking USE flags
7289eac20b COMMON: Fix Common::percentEncodeString
03f1b5610d CLOUD: Remove ConnectionManager::urlEncode and use Common::percentEncodeString


Commit: a7b33fbd9b86ad534ba798e181bf09f5a89be4b5
    https://github.com/scummvm/scummvm/commit/a7b33fbd9b86ad534ba798e181bf09f5a89be4b5
Author: Christian Kündig (christian at kuendig.info)
Date: 2025-08-13T18:38:54+02:00

Commit Message:
CLOUD: Remove unneeded checks for USE_LIBCURL

USE_CLOUD is only set if libcurl (or another potential future http client) is enabled.

Changed paths:
    backends/module.mk
    backends/saves/default/default-saves.cpp
    backends/saves/default/default-saves.h
    backends/saves/savefile.cpp
    base/main.cpp
    engines/testbed/config-params.h
    engines/testbed/module.mk
    engines/testbed/testbed.cpp
    gui/dump-all-dialogs.cpp
    gui/editgamedialog.cpp
    gui/launcher.cpp
    gui/module.mk
    gui/options.cpp
    gui/options.h
    gui/saveload-dialog.cpp
    gui/saveload-dialog.h
    gui/saveload.cpp


diff --git a/backends/module.mk b/backends/module.mk
index c3585d624d4..a39b5dcc9a0 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -28,7 +28,6 @@ MODULE_OBJS := \
 	timer/default/default-timer.o
 
 ifdef USE_CLOUD
-ifdef USE_LIBCURL
 MODULE_OBJS += \
 	cloud/basestorage.o \
 	cloud/cloudicon.o \
@@ -65,7 +64,6 @@ MODULE_OBJS += \
 	cloud/onedrive/onedrivelistdirectoryrequest.o \
 	cloud/onedrive/onedriveuploadrequest.o
 endif
-endif
 
 ifdef USE_SCUMMVMDLC
 ifdef USE_LIBCURL
@@ -124,13 +122,11 @@ MODULE_OBJS += \
 endif
 
 ifdef USE_CLOUD
-ifdef USE_LIBCURL
 ifdef USE_SDL_NET
 MODULE_OBJS += \
 	networking/sdl_net/handlers/connectcloudhandler.o
 endif
 endif
-endif
 
 # ENet networking source files.
 ifdef USE_ENET
diff --git a/backends/saves/default/default-saves.cpp b/backends/saves/default/default-saves.cpp
index 6ebee53a17c..0acc05bfcbe 100644
--- a/backends/saves/default/default-saves.cpp
+++ b/backends/saves/default/default-saves.cpp
@@ -21,7 +21,7 @@
 
 #include "common/scummsys.h"
 
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 #include "backends/cloud/cloudmanager.h"
 #endif
 #include "common/file.h"
@@ -40,7 +40,7 @@
 
 #include <errno.h>	// for removeSavefile()
 
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 const char *const DefaultSaveFileManager::TIMESTAMPS_FILENAME = "timestamps";
 #endif
 
@@ -144,7 +144,7 @@ Common::OutSaveFile *DefaultSaveFileManager::openForSaving(const Common::String
 		}
 	}
 
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 	// Update file's timestamp
 	Common::HashMap<Common::String, uint32> timestamps = loadTimestamps();
 	timestamps[filename] = INVALID_TIMESTAMP;
@@ -181,7 +181,7 @@ bool DefaultSaveFileManager::removeSavefile(const Common::String &filename) {
 	if (getError().getCode() != Common::kNoError)
 		return false;
 
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 	// Update file's timestamp
 	Common::HashMap<Common::String, uint32> timestamps = loadTimestamps();
 	Common::HashMap<Common::String, uint32>::iterator it = timestamps.find(filename);
@@ -257,7 +257,7 @@ void DefaultSaveFileManager::assureCached(const Common::Path &savePathName) {
 	// Check that path exists and is usable.
 	checkPath(Common::FSNode(savePathName));
 
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 	Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing
 	if (!files.empty()) updateSavefilesList(files); //makes this cache invalid
 	else _lockedFiles = files;
@@ -298,7 +298,7 @@ void DefaultSaveFileManager::assureCached(const Common::Path &savePathName) {
 	_cachedDirectory = savePathName;
 }
 
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 
 Common::HashMap<Common::String, uint32> DefaultSaveFileManager::loadTimestamps() {
 	Common::HashMap<Common::String, uint32> timestamps;
@@ -384,7 +384,7 @@ void DefaultSaveFileManager::saveTimestamps(Common::HashMap<Common::String, uint
 	f.close();
 }
 
-#endif // ifdef USE_LIBCURL
+#endif // ifdef USE_CLOUD
 
 Common::Path DefaultSaveFileManager::concatWithSavesPath(Common::String name) {
 	DefaultSaveFileManager *manager = dynamic_cast<DefaultSaveFileManager *>(g_system->getSavefileManager());
diff --git a/backends/saves/default/default-saves.h b/backends/saves/default/default-saves.h
index ad4b6b1def3..f8f713679df 100644
--- a/backends/saves/default/default-saves.h
+++ b/backends/saves/default/default-saves.h
@@ -44,7 +44,7 @@ public:
 	bool removeSavefile(const Common::String &filename) override;
 	bool exists(const Common::String &filename) override;
 
-#ifdef USE_LIBCURL
+#ifdef USE_CLOUD
 
 	static const uint32 INVALID_TIMESTAMP = UINT_MAX;
 	static const char *const TIMESTAMPS_FILENAME;
diff --git a/backends/saves/savefile.cpp b/backends/saves/savefile.cpp
index a2ae858d056..2fe7c81e15e 100644
--- a/backends/saves/savefile.cpp
+++ b/backends/saves/savefile.cpp
@@ -22,7 +22,7 @@
 #include "common/util.h"
 #include "common/savefile.h"
 #include "common/str.h"
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 #include "backends/cloud/cloudmanager.h"
 #endif
 
@@ -32,7 +32,7 @@ OutSaveFile::OutSaveFile(WriteStream *w): _wrapped(w) {}
 
 OutSaveFile::~OutSaveFile() {
 	delete _wrapped;
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 	CloudMan.syncSaves();
 #endif
 }
diff --git a/base/main.cpp b/base/main.cpp
index 652b5881677..0203da3ad8a 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -73,10 +73,8 @@
 #include "backends/keymapper/keymapper.h"
 
 #ifdef USE_CLOUD
-#ifdef USE_LIBCURL
 #include "backends/cloud/cloudmanager.h"
 #include "backends/networking/curl/connectionmanager.h"
-#endif
 #ifdef USE_SDL_NET
 #include "backends/networking/sdl_net/localwebserver.h"
 #endif
@@ -696,7 +694,7 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 	}
 #endif
 
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 	CloudMan.init();
 	CloudMan.syncSaves();
 #endif
@@ -893,11 +891,9 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 #ifdef USE_SDL_NET
 	Networking::LocalWebserver::destroy();
 #endif
-#ifdef USE_LIBCURL
 	Networking::ConnectionManager::destroy();
 	//I think it's important to destroy it after ConnectionManager
 	Cloud::CloudManager::destroy();
-#endif
 #endif
 	PluginManager::destroy();
 	GUI::GuiManager::destroy();
diff --git a/engines/testbed/config-params.h b/engines/testbed/config-params.h
index 0bee488292e..373a7f9c202 100644
--- a/engines/testbed/config-params.h
+++ b/engines/testbed/config-params.h
@@ -53,7 +53,7 @@ private:
 	 */
 	bool _isInteractive;
 	bool _isGameDataFound;
-#ifdef USE_LIBCURL
+#ifdef USE_CLOUD
 	bool _isCloudTestCallbackCalled;
 	bool _isCloudTestErrorCallbackCalled;
 #endif
@@ -71,7 +71,7 @@ public:
 	bool isGameDataFound() { return _isGameDataFound; }
 	void setGameDataFound(bool status) { _isGameDataFound = status; }
 
-#ifdef USE_LIBCURL
+#ifdef USE_CLOUD
 	bool isCloudTestCallbackCalled() const { return _isCloudTestCallbackCalled; }
 	void setCloudTestCallbackCalled(bool status) { _isCloudTestCallbackCalled = status; }
 
diff --git a/engines/testbed/module.mk b/engines/testbed/module.mk
index f50c11ae372..76bbdcff655 100644
--- a/engines/testbed/module.mk
+++ b/engines/testbed/module.mk
@@ -18,11 +18,9 @@ MODULE_OBJS := \
 	video.o
 
 ifdef USE_CLOUD
-ifdef USE_LIBCURL
 MODULE_OBJS += \
 	cloud.o
 endif
-endif
 
 ifdef USE_SDL_NET
 MODULE_OBJS += \
diff --git a/engines/testbed/testbed.cpp b/engines/testbed/testbed.cpp
index b137d52f2a2..a149a120bd9 100644
--- a/engines/testbed/testbed.cpp
+++ b/engines/testbed/testbed.cpp
@@ -174,7 +174,7 @@ void TestbedEngine::pushTestsuites(Common::Array<Testsuite *> &testsuiteList) {
 	ts = new SpeechTestSuite();
 	testsuiteList.push_back(ts);
 #endif
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 	// Cloud
 	ts = new CloudTestSuite();
 	testsuiteList.push_back(ts);
diff --git a/gui/dump-all-dialogs.cpp b/gui/dump-all-dialogs.cpp
index 03007497b91..740363e631a 100644
--- a/gui/dump-all-dialogs.cpp
+++ b/gui/dump-all-dialogs.cpp
@@ -108,11 +108,12 @@ void dumpDialogs(const Common::String &message, const Common::String &lang) {
 	GUI::AboutDialog aboutDialog;
 	handleSimpleDialog(aboutDialog, "aboutDialog" + suffix, surf);
 
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 	// CloudConnectingWizard
 	GUI::CloudConnectionWizard cloudConnectingWizard;
 	handleSimpleDialog(cloudConnectingWizard, "cloudConnectingWizard" + suffix, surf);
-
+#endif
+#ifdef USE_LIBCURL
 	// RemoteBrowserDialog
 	GUI::RemoteBrowserDialog remoteBrowserDialog(_("Select directory with game data"));
 	handleSimpleDialog(remoteBrowserDialog, "remoteBrowserDialog" + suffix, surf);
diff --git a/gui/editgamedialog.cpp b/gui/editgamedialog.cpp
index 173ffc75f56..19b40f07f7c 100644
--- a/gui/editgamedialog.cpp
+++ b/gui/editgamedialog.cpp
@@ -42,7 +42,7 @@
 #include "gui/widgets/popup.h"
 #include "gui/widgets/scrollcontainer.h"
 
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 #include "backends/cloud/cloudmanager.h"
 #endif
 
diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 354d1a1fe5b..123c52eaac9 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -55,7 +55,7 @@
 #include "engines/advancedDetector.h"
 
 #include "graphics/cursorman.h"
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 #include "backends/cloud/cloudmanager.h"
 #endif
 #if defined(USE_DLC)
@@ -400,7 +400,7 @@ void LauncherDialog::addGame() {
 
 		if (_browser->runModal() > 0) {
 			// User made his choice...
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 			Common::Path selectedDirectory = _browser->getResult().getPath();
 			Common::Path bannedDirectory = CloudMan.getDownloadLocalDirectory();
 			selectedDirectory.removeTrailingSeparators();
diff --git a/gui/module.mk b/gui/module.mk
index a360a014724..c364bf3d84e 100644
--- a/gui/module.mk
+++ b/gui/module.mk
@@ -48,7 +48,6 @@ MODULE_OBJS := \
 	widgets/scrollcontainer.o \
 	widgets/tab.o
 
-ifdef USE_LIBCURL
 ifdef USE_CLOUD
 MODULE_OBJS += \
 	cloudconnectionwizard.o \
@@ -56,6 +55,7 @@ MODULE_OBJS += \
 	remotebrowser.o
 endif
 
+ifdef USE_LIBCURL
 MODULE_OBJS += \
 	downloadpacksdialog.o \
 	integrity-dialog.o
diff --git a/gui/options.cpp b/gui/options.cpp
index 203b74535a6..5aadb5f901a 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -57,12 +57,10 @@
 #include "widgets/edittext.h"
 
 #ifdef USE_CLOUD
-#ifdef USE_LIBCURL
 #include "backends/cloud/cloudmanager.h"
 #include "gui/cloudconnectionwizard.h"
 #include "gui/downloaddialog.h"
 #endif
-#endif
 
 #ifdef USE_LIBCURL
 #include "gui/downloadpacksdialog.h"
@@ -2194,7 +2192,6 @@ GlobalOptionsDialog::GlobalOptionsDialog(LauncherDialog *launcher)
 	_updatesPopUp = nullptr;
 #endif
 #ifdef USE_CLOUD
-#ifdef USE_LIBCURL
 	_selectedStorageIndex = CloudMan.getStorageIndex();
 	_storagePopUpDesc = nullptr;
 	_storagePopUp = nullptr;
@@ -2218,7 +2215,6 @@ GlobalOptionsDialog::GlobalOptionsDialog(LauncherDialog *launcher)
 	_storageWizardConnectButton = nullptr;
 	_redrawCloudTab = false;
 #endif
-#endif
 
 #ifdef USE_SDL_NET
 	_runServerButton = nullptr;
@@ -2375,7 +2371,6 @@ void GlobalOptionsDialog::build() {
 	addMiscControls(miscContainer, "GlobalOptions_Misc_Container.", g_gui.useLowResGUI());
 
 #ifdef USE_CLOUD
-#ifdef USE_LIBCURL
 	//
 	// 8) The Cloud tab (remote storages)
 	//
@@ -2390,7 +2385,6 @@ void GlobalOptionsDialog::build() {
 	setTarget(container);
 
 	addCloudControls(container, "GlobalOptions_Cloud_Container.", g_gui.useLowResGUI());
-#endif // USE_LIBCURL
 #endif // USE_CLOUD
 
 #ifdef USE_SDL_NET
@@ -2791,7 +2785,6 @@ void GlobalOptionsDialog::addMiscControls(GuiObject *boss, const Common::String
 }
 
 #ifdef USE_CLOUD
-#ifdef USE_LIBCURL
 void GlobalOptionsDialog::addCloudControls(GuiObject *boss, const Common::String &prefix, bool lowres) {
 	_storagePopUpDesc = new StaticTextWidget(boss, prefix + "StoragePopupDesc", _("Active storage:"), _("Active cloud storage"));
 	_storagePopUp = new PopUpWidget(boss, prefix + "StoragePopup", Common::U32String(), kStoragePopUpCmd);
@@ -2841,7 +2834,6 @@ void GlobalOptionsDialog::addCloudControls(GuiObject *boss, const Common::String
 
 	setupCloudTab();
 }
-#endif // USE_LIBCURL
 #endif // USE_CLOUD
 
 #ifdef USE_SDL_NET
@@ -3084,7 +3076,6 @@ void GlobalOptionsDialog::apply() {
 #endif // USE_UPDATES
 
 #ifdef USE_CLOUD
-#ifdef USE_LIBCURL
 	if (CloudMan.getStorageIndex() != _selectedStorageIndex) {
 		if (!CloudMan.switchStorage(_selectedStorageIndex)) {
 			bool anotherStorageIsWorking = CloudMan.isWorking();
@@ -3097,7 +3088,6 @@ void GlobalOptionsDialog::apply() {
 			dialog.runModal();
 		}
 	}
-#endif // USE_LIBCURL
 #endif
 
 #ifdef USE_SDL_NET
@@ -3457,7 +3447,6 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		break;
 	}
 #ifdef USE_CLOUD
-#ifdef USE_LIBCURL
 	case kCloudTabContainerReflowCmd: {
 		setupCloudTab();
 		break;
@@ -3522,7 +3511,6 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		sendCommand(kSetPositionCmd, 0);
 		break;
 	}
-#endif // USE_LIBCURL
 #ifdef USE_SDL_NET
 	case kRunServerCmd: {
 #ifdef NETWORKING_LOCALWEBSERVER_ENABLE_PORT_OVERRIDE
@@ -3572,12 +3560,10 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 void GlobalOptionsDialog::handleTickle() {
 	OptionsDialog::handleTickle();
 #ifdef USE_CLOUD
-#ifdef USE_LIBCURL
 	if (_redrawCloudTab) {
 		reflowLayout(); // recalculates scrollbar as well
 		_redrawCloudTab = false;
 	}
-#endif // USE_LIBCURL
 #endif // USE_CLOUD
 
 #ifdef USE_SDL_NET
@@ -3642,9 +3628,7 @@ void GlobalOptionsDialog::reflowLayout() {
 
 	OptionsDialog::reflowLayout();
 #ifdef USE_CLOUD
-#ifdef USE_LIBCURL
 	setupCloudTab();
-#endif // USE_LIBCURL
 #endif // USE_CLOUD
 
 #ifdef USE_SDL_NET
@@ -3653,7 +3637,6 @@ void GlobalOptionsDialog::reflowLayout() {
 }
 
 #ifdef USE_CLOUD
-#ifdef USE_LIBCURL
 void GlobalOptionsDialog::setupCloudTab() {
 	_selectedStorageIndex = (_storagePopUp ? _storagePopUp->getSelectedTag() : (uint32)Cloud::kStorageNoneId);
 
@@ -3785,7 +3768,6 @@ void GlobalOptionsDialog::shiftWidget(Widget *widget, const char *widgetName, in
 
 	widget->setPos(x + xOffset, y + yOffset);
 }
-#endif // USE_LIBCURL
 #endif // USE_CLOUD
 
 #ifdef USE_SDL_NET
@@ -3843,7 +3825,6 @@ void GlobalOptionsDialog::reflowNetworkTabLayout() {
 #endif // USE_SDL_NET
 
 #ifdef USE_CLOUD
-#ifdef USE_LIBCURL
 void GlobalOptionsDialog::storageSavesSyncedCallback(const Cloud::Storage::BoolResponse &response) {
 	_redrawCloudTab = true;
 }
@@ -3855,7 +3836,6 @@ void GlobalOptionsDialog::storageErrorCallback(const Networking::ErrorResponse &
 	if (!response.interrupted)
 		g_system->displayMessageOnOSD(_("Request failed.\nCheck your Internet connection."));
 }
-#endif // USE_LIBCURL
 #endif // USE_CLOUD
 
 bool OptionsDialog::testGraphicsSettings() {
diff --git a/gui/options.h b/gui/options.h
index f88638e0a70..37509a3d303 100644
--- a/gui/options.h
+++ b/gui/options.h
@@ -32,7 +32,7 @@
 #include "gui/fluidsynth-dialog.h"
 #endif
 
-#ifdef USE_LIBCURL
+#ifdef USE_CLOUD
 #include "backends/cloud/storage.h"
 #endif
 
@@ -339,7 +339,6 @@ protected:
 	void addMiscControls(GuiObject *boss, const Common::String &prefix, bool lowres);
 
 #ifdef USE_CLOUD
-#ifdef USE_LIBCURL
 	//
 	// Cloud controls
 	//
@@ -372,7 +371,6 @@ protected:
 
 	void storageSavesSyncedCallback(const Cloud::Storage::BoolResponse &response);
 	void storageErrorCallback(const Networking::ErrorResponse &response);
-#endif // USE_LIBCURL
 #endif // USE_CLOUD
 
 #ifdef USE_SDL_NET
diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index e111a8b2bfd..5612f6241db 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -21,7 +21,7 @@
 
 #include "gui/saveload-dialog.h"
 
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 #include "backends/cloud/cloudmanager.h"
 #include "backends/cloud/savessyncrequest.h"
 #include "backends/networking/curl/connectionmanager.h"
@@ -43,7 +43,7 @@ namespace GUI {
 
 #define SCALEVALUE(val) ((val) * g_gui.getScaleFactor())
 
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 
 enum {
 	kCancelSyncCmd = 'PDCS',
@@ -178,7 +178,7 @@ SaveLoadChooserDialog::SaveLoadChooserDialog(const Common::String &dialogName, c
 #ifndef DISABLE_SAVELOADCHOOSER_GRID
 	, _listButton(nullptr), _gridButton(nullptr)
 #endif // !DISABLE_SAVELOADCHOOSER_GRID
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 	, _pollFrame(0), _didUpdateAfterSync(true)
 #endif
 	{
@@ -194,7 +194,7 @@ SaveLoadChooserDialog::SaveLoadChooserDialog(int x, int y, int w, int h, const b
 #ifndef DISABLE_SAVELOADCHOOSER_GRID
 	, _listButton(nullptr), _gridButton(nullptr)
 #endif // !DISABLE_SAVELOADCHOOSER_GRID
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 	, _pollFrame(0), _didUpdateAfterSync(true)
 #endif
 	{
@@ -259,7 +259,7 @@ void SaveLoadChooserDialog::handleCommand(CommandSender *sender, uint32 cmd, uin
 	return Dialog::handleCommand(sender, cmd, data);
 }
 
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 void SaveLoadChooserDialog::runSaveSync(bool hasSavepathOverride) {
 	if (!CloudMan.isSyncing()) {
 		if (hasSavepathOverride) {
@@ -272,7 +272,7 @@ void SaveLoadChooserDialog::runSaveSync(bool hasSavepathOverride) {
 #endif
 
 void SaveLoadChooserDialog::handleTickle() {
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 	if (!_dialogWasShown && CloudMan.isSyncing()) {
 		Common::Array<Common::String> files = CloudMan.getSyncingFiles();
 		if (!files.empty()) {
@@ -317,7 +317,7 @@ void SaveLoadChooserDialog::reflowLayout() {
 }
 
 void SaveLoadChooserDialog::updateSaveList(bool external) {
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 	Common::Array<Common::String> files = CloudMan.getSyncingFiles(); //returns empty array if not syncing
 	g_system->getSavefileManager()->updateSavefilesList(files);
 #endif
@@ -328,7 +328,7 @@ void SaveLoadChooserDialog::listSaves() {
 	if (!_metaEngine) return; //very strange
 	_saveList = _metaEngine->listSaves(_target.c_str(), _saveMode);
 
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 	//if there is Cloud support, add currently synced files as "locked" saves in the list
 	if (_metaEngine->hasFeature(MetaEngine::kSimpleSavesNames)) {
 		Common::String pattern = _target + ".###";
@@ -401,7 +401,7 @@ ButtonWidget *SaveLoadChooserDialog::createSwitchButton(const Common::String &na
 }
 #endif // !DISABLE_SAVELOADCHOOSER_GRID
 
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 void SaveLoadChooserDialog::pollCloudMan() {
 	_pollFrame = (_pollFrame + 1) % 60;
 	if (_pollFrame != 1)
diff --git a/gui/saveload-dialog.h b/gui/saveload-dialog.h
index 96552a287e6..cf16bdeef4f 100644
--- a/gui/saveload-dialog.h
+++ b/gui/saveload-dialog.h
@@ -29,7 +29,7 @@
 
 namespace GUI {
 
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 class SaveLoadChooserDialog;
 
 class SaveLoadCloudSyncProgressDialog : public Dialog { //protected?
@@ -83,7 +83,7 @@ public:
 
 	void handleCommand(CommandSender *sender, uint32 cmd, uint32 data) override;
 
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 	virtual void runSaveSync(bool hasSavepathOverride);
 #endif
 
@@ -134,7 +134,7 @@ protected:
 	ButtonWidget *createSwitchButton(const Common::String &name, const Common::U32String &desc, const Common::U32String &tooltip, const char *image, uint32 cmd = 0);
 #endif // !DISABLE_SAVELOADCHOOSER_GRID
 
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 	int _pollFrame;
 	bool _didUpdateAfterSync;
 
diff --git a/gui/saveload.cpp b/gui/saveload.cpp
index dc84f898c02..630c37516c3 100644
--- a/gui/saveload.cpp
+++ b/gui/saveload.cpp
@@ -87,7 +87,7 @@ int SaveLoadChooser::runModalWithMetaEngineAndTarget(const MetaEngine *engine, c
 	if (!_impl)
 		return -1;
 
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#ifdef USE_CLOUD
 	_impl->runSaveSync(ConfMan.hasKey("savepath", target));
 #endif
 


Commit: ea3c595ce83fdc5b082372b5c29129caf077f25e
    https://github.com/scummvm/scummvm/commit/ea3c595ce83fdc5b082372b5c29129caf077f25e
Author: Christian Kündig (christian at kuendig.info)
Date: 2025-08-13T18:38:54+02:00

Commit Message:
CLOUD: Rename 'backends/networking/curl' to 'backends/networking/http' for future alternative http clients

Changed paths:
  A backends/networking/http/connectionmanager.cpp
  A backends/networking/http/connectionmanager.h
  A backends/networking/http/httpjsonrequest.cpp
  A backends/networking/http/httpjsonrequest.h
  A backends/networking/http/httprequest.cpp
  A backends/networking/http/httprequest.h
  A backends/networking/http/networkreadstream.cpp
  A backends/networking/http/networkreadstream.h
  A backends/networking/http/postrequest.cpp
  A backends/networking/http/postrequest.h
  A backends/networking/http/request.cpp
  A backends/networking/http/request.h
  A backends/networking/http/session.cpp
  A backends/networking/http/session.h
  A backends/networking/http/sessionrequest.cpp
  A backends/networking/http/sessionrequest.h
  A backends/networking/http/socket.cpp
  A backends/networking/http/socket.h
  A backends/networking/http/url.cpp
  A backends/networking/http/url.h
  R backends/networking/curl/connectionmanager.cpp
  R backends/networking/curl/connectionmanager.h
  R backends/networking/curl/curljsonrequest.cpp
  R backends/networking/curl/curljsonrequest.h
  R backends/networking/curl/curlrequest.cpp
  R backends/networking/curl/curlrequest.h
  R backends/networking/curl/networkreadstream.cpp
  R backends/networking/curl/networkreadstream.h
  R backends/networking/curl/postrequest.cpp
  R backends/networking/curl/postrequest.h
  R backends/networking/curl/request.cpp
  R backends/networking/curl/request.h
  R backends/networking/curl/session.cpp
  R backends/networking/curl/session.h
  R backends/networking/curl/sessionrequest.cpp
  R backends/networking/curl/sessionrequest.h
  R backends/networking/curl/socket.cpp
  R backends/networking/curl/socket.h
  R backends/networking/curl/url.cpp
  R backends/networking/curl/url.h
    backends/cloud/basestorage.cpp
    backends/cloud/basestorage.h
    backends/cloud/box/boxlistdirectorybyidrequest.cpp
    backends/cloud/box/boxlistdirectorybyidrequest.h
    backends/cloud/box/boxstorage.cpp
    backends/cloud/box/boxstorage.h
    backends/cloud/box/boxtokenrefresher.cpp
    backends/cloud/box/boxtokenrefresher.h
    backends/cloud/box/boxuploadrequest.cpp
    backends/cloud/box/boxuploadrequest.h
    backends/cloud/cloudicon.h
    backends/cloud/cloudmanager.h
    backends/cloud/downloadrequest.cpp
    backends/cloud/downloadrequest.h
    backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
    backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
    backends/cloud/dropbox/dropboxinforequest.cpp
    backends/cloud/dropbox/dropboxinforequest.h
    backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
    backends/cloud/dropbox/dropboxlistdirectoryrequest.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxstorage.h
    backends/cloud/dropbox/dropboxtokenrefresher.cpp
    backends/cloud/dropbox/dropboxtokenrefresher.h
    backends/cloud/dropbox/dropboxuploadrequest.cpp
    backends/cloud/dropbox/dropboxuploadrequest.h
    backends/cloud/folderdownloadrequest.cpp
    backends/cloud/folderdownloadrequest.h
    backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
    backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledrivestorage.h
    backends/cloud/googledrive/googledrivetokenrefresher.cpp
    backends/cloud/googledrive/googledrivetokenrefresher.h
    backends/cloud/googledrive/googledriveuploadrequest.cpp
    backends/cloud/googledrive/googledriveuploadrequest.h
    backends/cloud/id/idcreatedirectoryrequest.h
    backends/cloud/id/iddownloadrequest.h
    backends/cloud/id/idlistdirectoryrequest.h
    backends/cloud/id/idresolveidrequest.h
    backends/cloud/id/idstorage.h
    backends/cloud/id/idstreamfilerequest.h
    backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
    backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
    backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
    backends/cloud/onedrive/onedrivelistdirectoryrequest.h
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivestorage.h
    backends/cloud/onedrive/onedrivetokenrefresher.cpp
    backends/cloud/onedrive/onedrivetokenrefresher.h
    backends/cloud/onedrive/onedriveuploadrequest.cpp
    backends/cloud/onedrive/onedriveuploadrequest.h
    backends/cloud/savessyncrequest.cpp
    backends/cloud/savessyncrequest.h
    backends/cloud/storage.cpp
    backends/cloud/storage.h
    backends/dlc/dlcmanager.h
    backends/dlc/scummvmcloud.cpp
    backends/dlc/scummvmcloud.h
    backends/module.mk
    backends/networking/sdl_net/handlers/connectcloudhandler.cpp
    backends/networking/sdl_net/handlers/connectcloudhandler.h
    base/main.cpp
    engines/scumm/he/net/net_lobby.h
    gui/cloudconnectionwizard.h
    gui/downloadpacksdialog.cpp
    gui/integrity-dialog.h
    gui/remotebrowser.cpp
    gui/remotebrowser.h
    gui/saveload-dialog.cpp


diff --git a/backends/cloud/basestorage.cpp b/backends/cloud/basestorage.cpp
index a00e47a4656..69e106f56ff 100644
--- a/backends/cloud/basestorage.cpp
+++ b/backends/cloud/basestorage.cpp
@@ -21,8 +21,8 @@
 
 #include "backends/cloud/basestorage.h"
 #include "backends/cloud/cloudmanager.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/httpjsonrequest.h"
 #include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/formats/json.h"
@@ -43,7 +43,7 @@ void BaseStorage::getAccessToken(const Common::String &code, Networking::ErrorCa
 	Networking::ErrorCallback errorCallback = new Common::CallbackBridge<BaseStorage, const Networking::ErrorResponse &, const Networking::ErrorResponse &>(this, &BaseStorage::codeFlowFailed, callback);
 
 	Common::String url = Common::String::format("https://cloud.scummvm.org/%s/token/%s", cloudProvider().c_str(), code.c_str());
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, url);
+	Networking::HttpJsonRequest *request = new Networking::HttpJsonRequest(innerCallback, errorCallback, url);
 
 	addRequest(request);
 }
@@ -68,7 +68,7 @@ void BaseStorage::codeFlowComplete(Networking::ErrorCallback callback, const Net
 	Common::JSONObject result;
 	if (success) {
 		result = json->asObject();
-		if (!Networking::CurlJsonRequest::jsonContainsAttribute(result, "error", "BaseStorage::codeFlowComplete")) {
+		if (!Networking::HttpJsonRequest::jsonContainsAttribute(result, "error", "BaseStorage::codeFlowComplete")) {
 			warning("BaseStorage: bad response, no 'error' attribute passed");
 			debug(9, "%s", json->stringify(true).c_str());
 			success = false;
@@ -78,7 +78,7 @@ void BaseStorage::codeFlowComplete(Networking::ErrorCallback callback, const Net
 
 	if (success && result.getVal("error")->asBool()) {
 		Common::String errorMessage = "{error: true}, message is missing";
-		if (Networking::CurlJsonRequest::jsonContainsString(result, "message", "BaseStorage::codeFlowComplete")) {
+		if (Networking::HttpJsonRequest::jsonContainsString(result, "message", "BaseStorage::codeFlowComplete")) {
 			errorMessage = result.getVal("message")->asString();
 		}
 		warning("BaseStorage: response says error occurred: %s", errorMessage.c_str());
@@ -86,7 +86,7 @@ void BaseStorage::codeFlowComplete(Networking::ErrorCallback callback, const Net
 		callbackMessage = errorMessage;
 	}
 
-	if (success && !Networking::CurlJsonRequest::jsonContainsObject(result, "oauth", "BaseStorage::codeFlowComplete")) {
+	if (success && !Networking::HttpJsonRequest::jsonContainsObject(result, "oauth", "BaseStorage::codeFlowComplete")) {
 		warning("BaseStorage: bad response, no 'oauth' attribute passed");
 		debug(9, "%s", json->stringify(true).c_str());
 		success = false;
@@ -97,8 +97,8 @@ void BaseStorage::codeFlowComplete(Networking::ErrorCallback callback, const Net
 	bool requiresRefreshToken = needsRefreshToken();
 	if (success) {
 		oauth = result.getVal("oauth")->asObject();
-		if (!Networking::CurlJsonRequest::jsonContainsString(oauth, "access_token", "BaseStorage::codeFlowComplete") ||
-			!Networking::CurlJsonRequest::jsonContainsString(oauth, "refresh_token", "BaseStorage::codeFlowComplete", !requiresRefreshToken)) {
+		if (!Networking::HttpJsonRequest::jsonContainsString(oauth, "access_token", "BaseStorage::codeFlowComplete") ||
+			!Networking::HttpJsonRequest::jsonContainsString(oauth, "refresh_token", "BaseStorage::codeFlowComplete", !requiresRefreshToken)) {
 			warning("BaseStorage: bad response, no 'access_token' or 'refresh_token' attribute passed");
 			debug(9, "%s", json->stringify(true).c_str());
 			success = false;
@@ -145,7 +145,7 @@ void BaseStorage::refreshAccessToken(BoolCallback callback, Networking::ErrorCal
 		errorCallback = getErrorPrintingCallback();
 
 	Common::String url = Common::String::format("https://cloud.scummvm.org/%s/refresh", cloudProvider().c_str());
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorCallback, url);
+	Networking::HttpJsonRequest *request = new Networking::HttpJsonRequest(innerCallback, errorCallback, url);
 	request->addHeader("X-ScummVM-Refresh-Token: " + _refreshToken);
 	addRequest(request);
 }
@@ -167,7 +167,7 @@ void BaseStorage::tokenRefreshed(BoolCallback callback, const Networking::JsonRe
 	Common::JSONObject result;
 	if (success) {
 		result = json->asObject();
-		if (!Networking::CurlJsonRequest::jsonContainsAttribute(result, "error", "BaseStorage::tokenRefreshed")) {
+		if (!Networking::HttpJsonRequest::jsonContainsAttribute(result, "error", "BaseStorage::tokenRefreshed")) {
 			warning("BaseStorage: bad response, no 'error' attribute passed");
 			debug(9, "%s", json->stringify(true).c_str());
 			success = false;
@@ -176,14 +176,14 @@ void BaseStorage::tokenRefreshed(BoolCallback callback, const Networking::JsonRe
 
 	if (success && result.getVal("error")->asBool()) {
 		Common::String errorMessage = "{error: true}, message is missing";
-		if (Networking::CurlJsonRequest::jsonContainsString(result, "message", "BaseStorage::tokenRefreshed")) {
+		if (Networking::HttpJsonRequest::jsonContainsString(result, "message", "BaseStorage::tokenRefreshed")) {
 			errorMessage = result.getVal("message")->asString();
 		}
 		warning("BaseStorage: response says error occurred: %s", errorMessage.c_str());
 		success = false;
 	}
 
-	if (success && !Networking::CurlJsonRequest::jsonContainsObject(result, "oauth", "BaseStorage::tokenRefreshed")) {
+	if (success && !Networking::HttpJsonRequest::jsonContainsObject(result, "oauth", "BaseStorage::tokenRefreshed")) {
 		warning("BaseStorage: bad response, no 'oauth' attribute passed");
 		debug(9, "%s", json->stringify(true).c_str());
 		success = false;
@@ -193,8 +193,8 @@ void BaseStorage::tokenRefreshed(BoolCallback callback, const Networking::JsonRe
 	bool requiresRefreshToken = !canReuseRefreshToken();
 	if (success) {
 		oauth = result.getVal("oauth")->asObject();
-		if (!Networking::CurlJsonRequest::jsonContainsString(oauth, "access_token", "BaseStorage::tokenRefreshed") ||
-			!Networking::CurlJsonRequest::jsonContainsString(oauth, "refresh_token", "BaseStorage::tokenRefreshed", !requiresRefreshToken)) {
+		if (!Networking::HttpJsonRequest::jsonContainsString(oauth, "access_token", "BaseStorage::tokenRefreshed") ||
+			!Networking::HttpJsonRequest::jsonContainsString(oauth, "refresh_token", "BaseStorage::tokenRefreshed", !requiresRefreshToken)) {
 			warning("BaseStorage: bad response, no 'access_token' or 'refresh_token' attribute passed");
 			debug(9, "%s", json->stringify(true).c_str());
 			success = false;
diff --git a/backends/cloud/basestorage.h b/backends/cloud/basestorage.h
index 53124f03f24..1a1331cfcb1 100644
--- a/backends/cloud/basestorage.h
+++ b/backends/cloud/basestorage.h
@@ -23,7 +23,7 @@
 #define BACKENDS_CLOUD_BASE_STORAGE_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/httpjsonrequest.h"
 
 namespace Cloud {
 
diff --git a/backends/cloud/box/boxlistdirectorybyidrequest.cpp b/backends/cloud/box/boxlistdirectorybyidrequest.cpp
index 0fadd8e3b3f..7ff4f314c30 100644
--- a/backends/cloud/box/boxlistdirectorybyidrequest.cpp
+++ b/backends/cloud/box/boxlistdirectorybyidrequest.cpp
@@ -24,9 +24,9 @@
 #include "backends/cloud/box/boxtokenrefresher.h"
 #include "backends/cloud/iso8601.h"
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/formats/json.h"
 
 namespace Cloud {
@@ -67,7 +67,7 @@ void BoxListDirectoryByIdRequest::makeRequest(uint32 offset) {
 
 	Networking::JsonCallback callback = new Common::Callback<BoxListDirectoryByIdRequest, const Networking::JsonResponse &>(this, &BoxListDirectoryByIdRequest::responseCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<BoxListDirectoryByIdRequest, const Networking::ErrorResponse &>(this, &BoxListDirectoryByIdRequest::errorCallback);
-	Networking::CurlJsonRequest *request = new BoxTokenRefresher(_storage, callback, failureCallback, url.c_str());
+	Networking::HttpJsonRequest *request = new BoxTokenRefresher(_storage, callback, failureCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + _storage->accessToken());
 	_workingRequest = ConnMan.addRequest(request);
 }
@@ -83,7 +83,7 @@ void BoxListDirectoryByIdRequest::responseCallback(const Networking::JsonRespons
 		_date = response.request->date();
 
 	Networking::ErrorResponse error(this, "BoxListDirectoryByIdRequest::responseCallback: unknown error");
-	const Networking::CurlJsonRequest *rq = (const Networking::CurlJsonRequest *)response.request;
+	const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
@@ -130,15 +130,15 @@ void BoxListDirectoryByIdRequest::responseCallback(const Networking::JsonRespons
 
 		Common::JSONArray items = responseObject.getVal("entries")->asArray();
 		for (uint32 i = 0; i < items.size(); ++i) {
-			if (!Networking::CurlJsonRequest::jsonIsObject(items[i], "BoxListDirectoryByIdRequest")) continue;
+			if (!Networking::HttpJsonRequest::jsonIsObject(items[i], "BoxListDirectoryByIdRequest")) continue;
 
 			Common::JSONObject item = items[i]->asObject();
 
-			if (!Networking::CurlJsonRequest::jsonContainsString(item, "id", "BoxListDirectoryByIdRequest")) continue;
-			if (!Networking::CurlJsonRequest::jsonContainsString(item, "name", "BoxListDirectoryByIdRequest")) continue;
-			if (!Networking::CurlJsonRequest::jsonContainsString(item, "type", "BoxListDirectoryByIdRequest")) continue;
-			if (!Networking::CurlJsonRequest::jsonContainsString(item, "modified_at", "BoxListDirectoryByIdRequest")) continue;
-			if (!Networking::CurlJsonRequest::jsonContainsStringOrIntegerNumber(item, "size", "BoxListDirectoryByIdRequest")) continue;
+			if (!Networking::HttpJsonRequest::jsonContainsString(item, "id", "BoxListDirectoryByIdRequest")) continue;
+			if (!Networking::HttpJsonRequest::jsonContainsString(item, "name", "BoxListDirectoryByIdRequest")) continue;
+			if (!Networking::HttpJsonRequest::jsonContainsString(item, "type", "BoxListDirectoryByIdRequest")) continue;
+			if (!Networking::HttpJsonRequest::jsonContainsString(item, "modified_at", "BoxListDirectoryByIdRequest")) continue;
+			if (!Networking::HttpJsonRequest::jsonContainsStringOrIntegerNumber(item, "size", "BoxListDirectoryByIdRequest")) continue;
 
 			Common::String id = item.getVal("id")->asString();
 			Common::String name = item.getVal("name")->asString();
diff --git a/backends/cloud/box/boxlistdirectorybyidrequest.h b/backends/cloud/box/boxlistdirectorybyidrequest.h
index bb344a71221..228c75e35b5 100644
--- a/backends/cloud/box/boxlistdirectorybyidrequest.h
+++ b/backends/cloud/box/boxlistdirectorybyidrequest.h
@@ -23,8 +23,8 @@
 #define BACKENDS_CLOUD_BOX_BOXLISTDIRECTORYBYIDREQUEST_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/request.h"
 #include "common/callback.h"
 
 namespace Cloud {
diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index 4865a803cdf..d16dbf27648 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -27,9 +27,9 @@
 #include "backends/cloud/box/boxtokenrefresher.h"
 #include "backends/cloud/box/boxuploadrequest.h"
 #include "backends/cloud/cloudmanager.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/formats/json.h"
@@ -80,7 +80,7 @@ void BoxStorage::infoInnerCallback(StorageInfoCallback outerCallback, const Netw
 		return;
 	}
 
-	if (!Networking::CurlJsonRequest::jsonIsObject(json, "BoxStorage::infoInnerCallback")) {
+	if (!Networking::HttpJsonRequest::jsonIsObject(json, "BoxStorage::infoInnerCallback")) {
 		delete json;
 		delete outerCallback;
 		return;
@@ -94,19 +94,19 @@ void BoxStorage::infoInnerCallback(StorageInfoCallback outerCallback, const Netw
 	// can check that "type": "user"
 	// there is also "max_upload_size", "phone" and "avatar_url"
 
-	if (Networking::CurlJsonRequest::jsonContainsString(jsonInfo, "id", "BoxStorage::infoInnerCallback"))
+	if (Networking::HttpJsonRequest::jsonContainsString(jsonInfo, "id", "BoxStorage::infoInnerCallback"))
 		uid = jsonInfo.getVal("id")->asString();
 
-	if (Networking::CurlJsonRequest::jsonContainsString(jsonInfo, "name", "BoxStorage::infoInnerCallback"))
+	if (Networking::HttpJsonRequest::jsonContainsString(jsonInfo, "name", "BoxStorage::infoInnerCallback"))
 		displayName = jsonInfo.getVal("name")->asString();
 
-	if (Networking::CurlJsonRequest::jsonContainsString(jsonInfo, "login", "BoxStorage::infoInnerCallback"))
+	if (Networking::HttpJsonRequest::jsonContainsString(jsonInfo, "login", "BoxStorage::infoInnerCallback"))
 		email = jsonInfo.getVal("login")->asString();
 
-	if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(jsonInfo, "space_amount", "BoxStorage::infoInnerCallback"))
+	if (Networking::HttpJsonRequest::jsonContainsIntegerNumber(jsonInfo, "space_amount", "BoxStorage::infoInnerCallback"))
 		quotaAllocated = jsonInfo.getVal("space_amount")->asIntegerNumber();
 
-	if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(jsonInfo, "space_used", "BoxStorage::infoInnerCallback"))
+	if (Networking::HttpJsonRequest::jsonContainsIntegerNumber(jsonInfo, "space_used", "BoxStorage::infoInnerCallback"))
 		quotaUsed = jsonInfo.getVal("space_used")->asIntegerNumber();
 
 	Common::String username = email;
@@ -139,7 +139,7 @@ void BoxStorage::createDirectoryInnerCallback(BoolCallback outerCallback, const
 	}
 
 	if (outerCallback) {
-		if (Networking::CurlJsonRequest::jsonIsObject(json, "BoxStorage::createDirectoryInnerCallback")) {
+		if (Networking::HttpJsonRequest::jsonIsObject(json, "BoxStorage::createDirectoryInnerCallback")) {
 			Common::JSONObject jsonInfo = json->asObject();
 			(*outerCallback)(BoolResponse(nullptr, jsonInfo.contains("id")));
 		} else {
@@ -157,7 +157,7 @@ Networking::Request *BoxStorage::createDirectoryWithParentId(const Common::Strin
 
 	Common::String url = BOX_API_FOLDERS;
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, const BoolResponse &, const Networking::JsonResponse &>(this, &BoxStorage::createDirectoryInnerCallback, callback);
-	Networking::CurlJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, url.c_str());
+	Networking::HttpJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + accessToken());
 	request->addHeader("Content-Type: application/json");
 
@@ -208,7 +208,7 @@ Networking::Request *BoxStorage::streamFileById(const Common::String &id, Networ
 
 Networking::Request *BoxStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, const StorageInfoResponse &, const Networking::JsonResponse &>(this, &BoxStorage::infoInnerCallback, callback);
-	Networking::CurlJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, BOX_API_USERS_ME);
+	Networking::HttpJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, BOX_API_USERS_ME);
 	request->addHeader("Authorization: Bearer " + _token);
 	return addRequest(request);
 }
diff --git a/backends/cloud/box/boxstorage.h b/backends/cloud/box/boxstorage.h
index 27f2af06694..e1776d9c597 100644
--- a/backends/cloud/box/boxstorage.h
+++ b/backends/cloud/box/boxstorage.h
@@ -23,7 +23,7 @@
 #define BACKENDS_CLOUD_BOX_BOXSTORAGE_H
 
 #include "backends/cloud/id/idstorage.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/httpjsonrequest.h"
 
 namespace Cloud {
 namespace Box {
diff --git a/backends/cloud/box/boxtokenrefresher.cpp b/backends/cloud/box/boxtokenrefresher.cpp
index c69b63426eb..f3fcc9c5a71 100644
--- a/backends/cloud/box/boxtokenrefresher.cpp
+++ b/backends/cloud/box/boxtokenrefresher.cpp
@@ -24,7 +24,7 @@
 #include <curl/curl.h>
 #include "backends/cloud/box/boxtokenrefresher.h"
 #include "backends/cloud/box/boxstorage.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/debug.h"
 #include "common/formats/json.h"
 
@@ -32,7 +32,7 @@ namespace Cloud {
 namespace Box {
 
 BoxTokenRefresher::BoxTokenRefresher(BoxStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url):
-	CurlJsonRequest(callback, ecb, url), _parentStorage(parent) {}
+	HttpJsonRequest(callback, ecb, url), _parentStorage(parent) {}
 
 BoxTokenRefresher::~BoxTokenRefresher() {}
 
@@ -59,7 +59,7 @@ void BoxTokenRefresher::tokenRefreshed(const Storage::BoolResponse &response) {
 void BoxTokenRefresher::finishJson(const Common::JSONValue *json) {
 	if (!json) {
 		//that's probably not an error (200 OK)
-		CurlJsonRequest::finishJson(nullptr);
+		HttpJsonRequest::finishJson(nullptr);
 		return;
 	}
 
@@ -104,7 +104,7 @@ void BoxTokenRefresher::finishJson(const Common::JSONValue *json) {
 	}
 
 	//notify user of success
-	CurlJsonRequest::finishJson(json);
+	HttpJsonRequest::finishJson(json);
 }
 
 void BoxTokenRefresher::finishError(const Networking::ErrorResponse &error, Networking::RequestState state) {
@@ -125,12 +125,12 @@ void BoxTokenRefresher::setHeaders(const Common::Array<Common::String> &headers)
 	curl_slist_free_all(_headersList);
 	_headersList = nullptr;
 	for (uint32 i = 0; i < headers.size(); ++i)
-		CurlJsonRequest::addHeader(headers[i]);
+		HttpJsonRequest::addHeader(headers[i]);
 }
 
 void BoxTokenRefresher::addHeader(const Common::String  &header) {
 	_headers.push_back(header);
-	CurlJsonRequest::addHeader(header);
+	HttpJsonRequest::addHeader(header);
 }
 
 } // End of namespace Box
diff --git a/backends/cloud/box/boxtokenrefresher.h b/backends/cloud/box/boxtokenrefresher.h
index 6c311cadf48..be1f4929833 100644
--- a/backends/cloud/box/boxtokenrefresher.h
+++ b/backends/cloud/box/boxtokenrefresher.h
@@ -23,14 +23,14 @@
 #define BACKENDS_CLOUD_BOX_BOXTOKENREFRESHER_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/httpjsonrequest.h"
 
 namespace Cloud {
 namespace Box {
 
 class BoxStorage;
 
-class BoxTokenRefresher: public Networking::CurlJsonRequest {
+class BoxTokenRefresher: public Networking::HttpJsonRequest {
 	BoxStorage *_parentStorage;
 	Common::Array<Common::String> _headers;
 
diff --git a/backends/cloud/box/boxuploadrequest.cpp b/backends/cloud/box/boxuploadrequest.cpp
index 256db30cce7..e03c4acb30b 100644
--- a/backends/cloud/box/boxuploadrequest.cpp
+++ b/backends/cloud/box/boxuploadrequest.cpp
@@ -24,9 +24,9 @@
 #include "backends/cloud/box/boxtokenrefresher.h"
 #include "backends/cloud/iso8601.h"
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/formats/json.h"
 
 namespace Cloud {
@@ -109,7 +109,7 @@ void BoxUploadRequest::upload() {
 	url += "/content";
 	Networking::JsonCallback callback = new Common::Callback<BoxUploadRequest, const Networking::JsonResponse &>(this, &BoxUploadRequest::uploadedCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<BoxUploadRequest, const Networking::ErrorResponse &>(this, &BoxUploadRequest::notUploadedCallback);
-	Networking::CurlJsonRequest *request = new BoxTokenRefresher(_storage, callback, failureCallback, url.c_str());
+	Networking::HttpJsonRequest *request = new BoxTokenRefresher(_storage, callback, failureCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + _storage->accessToken());
 
 	Common::JSONObject jsonRequestParameters;
@@ -132,7 +132,7 @@ void BoxUploadRequest::uploadedCallback(const Networking::JsonResponse &response
 	if (_ignoreCallback) return;
 
 	Networking::ErrorResponse error(this, false, true, "", -1);
-	const Networking::CurlJsonRequest *rq = (const Networking::CurlJsonRequest *)response.request;
+	const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
 	if (rq) {
 		const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
 		if (stream) {
@@ -160,20 +160,20 @@ void BoxUploadRequest::uploadedCallback(const Networking::JsonResponse &response
 	}
 
 	Common::JSONObject object = json->asObject();
-	if (Networking::CurlJsonRequest::jsonContainsArray(object, "entries", "BoxUploadRequest")) {
+	if (Networking::HttpJsonRequest::jsonContainsArray(object, "entries", "BoxUploadRequest")) {
 		Common::JSONArray entries = object.getVal("entries")->asArray();
 		if (entries.size() == 0) {
 			warning("BoxUploadRequest: 'entries' found, but it's empty");
-		} else if (!Networking::CurlJsonRequest::jsonIsObject(entries[0], "BoxUploadRequest")) {
+		} else if (!Networking::HttpJsonRequest::jsonIsObject(entries[0], "BoxUploadRequest")) {
 			warning("BoxUploadRequest: 'entries' first item is not an object");
 		} else {
 			Common::JSONObject item = entries[0]->asObject();
 
-			if (Networking::CurlJsonRequest::jsonContainsString(item, "id", "BoxUploadRequest") &&
-				Networking::CurlJsonRequest::jsonContainsString(item, "name", "BoxUploadRequest") &&
-				Networking::CurlJsonRequest::jsonContainsString(item, "type", "BoxUploadRequest") &&
-				Networking::CurlJsonRequest::jsonContainsString(item, "modified_at", "BoxUploadRequest") &&
-				Networking::CurlJsonRequest::jsonContainsStringOrIntegerNumber(item, "size", "BoxUploadRequest")) {
+			if (Networking::HttpJsonRequest::jsonContainsString(item, "id", "BoxUploadRequest") &&
+				Networking::HttpJsonRequest::jsonContainsString(item, "name", "BoxUploadRequest") &&
+				Networking::HttpJsonRequest::jsonContainsString(item, "type", "BoxUploadRequest") &&
+				Networking::HttpJsonRequest::jsonContainsString(item, "modified_at", "BoxUploadRequest") &&
+				Networking::HttpJsonRequest::jsonContainsStringOrIntegerNumber(item, "size", "BoxUploadRequest")) {
 
 				//finished
 				Common::String id = item.getVal("id")->asString();
diff --git a/backends/cloud/box/boxuploadrequest.h b/backends/cloud/box/boxuploadrequest.h
index 64666513c79..71a4e1f6073 100644
--- a/backends/cloud/box/boxuploadrequest.h
+++ b/backends/cloud/box/boxuploadrequest.h
@@ -23,8 +23,8 @@
 #define BACKENDS_CLOUD_BOX_BOXUPLOADREQUEST_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/request.h"
 #include "common/callback.h"
 
 namespace Cloud {
diff --git a/backends/cloud/cloudicon.h b/backends/cloud/cloudicon.h
index 001ba809ef9..2cd6cb6f48c 100644
--- a/backends/cloud/cloudicon.h
+++ b/backends/cloud/cloudicon.h
@@ -19,8 +19,8 @@
  *
  */
 
-#ifndef BACKENDS_NETWORKING_CURL_CLOUDICON_H
-#define BACKENDS_NETWORKING_CURL_CLOUDICON_H
+#ifndef BACKENDS_CLOUD_CLOUDICON_H
+#define BACKENDS_CLOUD_CLOUDICON_H
 
 #include "graphics/surface.h"
 
diff --git a/backends/cloud/cloudmanager.h b/backends/cloud/cloudmanager.h
index 85574b5c283..af8759bb0da 100644
--- a/backends/cloud/cloudmanager.h
+++ b/backends/cloud/cloudmanager.h
@@ -24,7 +24,7 @@
 
 #include "backends/cloud/storage.h"
 #include "backends/cloud/cloudicon.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/httpjsonrequest.h"
 
 #include "common/array.h"
 #include "common/singleton.h"
diff --git a/backends/cloud/downloadrequest.cpp b/backends/cloud/downloadrequest.cpp
index e90a84308c6..2d5779a0b1c 100644
--- a/backends/cloud/downloadrequest.cpp
+++ b/backends/cloud/downloadrequest.cpp
@@ -20,7 +20,7 @@
  */
 
 #include "backends/cloud/downloadrequest.h"
-#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/http/connectionmanager.h"
 #include "common/textconsole.h"
 
 namespace Cloud {
diff --git a/backends/cloud/downloadrequest.h b/backends/cloud/downloadrequest.h
index e5a2050deaa..7649d5896ba 100644
--- a/backends/cloud/downloadrequest.h
+++ b/backends/cloud/downloadrequest.h
@@ -22,8 +22,8 @@
 #ifndef BACKENDS_CLOUD_DOWNLOADREQUEST_H
 #define BACKENDS_CLOUD_DOWNLOADREQUEST_H
 
-#include "backends/networking/curl/request.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/request.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "backends/cloud/storage.h"
 #include "common/file.h"
 
diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
index 81306578e39..4a8f7165327 100644
--- a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.cpp
@@ -23,9 +23,9 @@
 #include "backends/cloud/dropbox/dropboxstorage.h"
 #include "backends/cloud/dropbox/dropboxtokenrefresher.h"
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/formats/json.h"
 
 namespace Cloud {
@@ -54,7 +54,7 @@ void DropboxCreateDirectoryRequest::start() {
 
 	Networking::JsonCallback innerCallback = new Common::Callback<DropboxCreateDirectoryRequest, const Networking::JsonResponse &>(this, &DropboxCreateDirectoryRequest::responseCallback);
 	Networking::ErrorCallback errorResponseCallback = new Common::Callback<DropboxCreateDirectoryRequest, const Networking::ErrorResponse &>(this, &DropboxCreateDirectoryRequest::errorCallback);
-	Networking::CurlJsonRequest *request = new DropboxTokenRefresher(_storage, innerCallback, errorResponseCallback, DROPBOX_API_CREATE_FOLDER);
+	Networking::HttpJsonRequest *request = new DropboxTokenRefresher(_storage, innerCallback, errorResponseCallback, DROPBOX_API_CREATE_FOLDER);
 	request->addHeader("Authorization: Bearer " + _storage->accessToken());
 	request->addHeader("Content-Type: application/json");
 
@@ -76,7 +76,7 @@ void DropboxCreateDirectoryRequest::responseCallback(const Networking::JsonRespo
 	if (response.request) _date = response.request->date();
 
 	Networking::ErrorResponse error(this, "DropboxCreateDirectoryRequest::responseCallback: unknown error");
-	const Networking::CurlJsonRequest *rq = (const Networking::CurlJsonRequest *)response.request;
+	const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
@@ -97,7 +97,7 @@ void DropboxCreateDirectoryRequest::responseCallback(const Networking::JsonRespo
 	if (info.contains("id")) {
 		finishCreation(true);
 	} else {
-		if (Networking::CurlJsonRequest::jsonContainsString(info, "error_summary", "DropboxCreateDirectoryRequest")) {
+		if (Networking::HttpJsonRequest::jsonContainsString(info, "error_summary", "DropboxCreateDirectoryRequest")) {
 			Common::String summary = info.getVal("error_summary")->asString();
 			if (summary.contains("path") && summary.contains("conflict") && summary.contains("folder")) {
 				// existing directory - not an error for CreateDirectoryRequest
diff --git a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
index 0b0148c3bb2..23b7079f302 100644
--- a/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
+++ b/backends/cloud/dropbox/dropboxcreatedirectoryrequest.h
@@ -23,8 +23,8 @@
 #define BACKENDS_CLOUD_DROPBOX_DROPBOXCREATEDIRECTORYREQUEST_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/request.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/request.h"
+#include "backends/networking/http/httpjsonrequest.h"
 
 namespace Cloud {
 namespace Dropbox {
diff --git a/backends/cloud/dropbox/dropboxinforequest.cpp b/backends/cloud/dropbox/dropboxinforequest.cpp
index ff98b8b258d..8d1b43096f7 100644
--- a/backends/cloud/dropbox/dropboxinforequest.cpp
+++ b/backends/cloud/dropbox/dropboxinforequest.cpp
@@ -24,9 +24,9 @@
 #include "backends/cloud/dropbox/dropboxtokenrefresher.h"
 #include "backends/cloud/cloudmanager.h"
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/formats/json.h"
 
 namespace Cloud {
@@ -56,7 +56,7 @@ void DropboxInfoRequest::start() {
 
 	Networking::JsonCallback innerCallback = new Common::Callback<DropboxInfoRequest, const Networking::JsonResponse &>(this, &DropboxInfoRequest::userResponseCallback);
 	Networking::ErrorCallback errorResponseCallback = new Common::Callback<DropboxInfoRequest, const Networking::ErrorResponse &>(this, &DropboxInfoRequest::errorCallback);
-	Networking::CurlJsonRequest *request = new DropboxTokenRefresher(_storage, innerCallback, errorResponseCallback, DROPBOX_API_GET_CURRENT_ACCOUNT);
+	Networking::HttpJsonRequest *request = new DropboxTokenRefresher(_storage, innerCallback, errorResponseCallback, DROPBOX_API_GET_CURRENT_ACCOUNT);
 	request->addHeader("Authorization: Bearer " + _storage->accessToken());
 	request->addHeader("Content-Type: application/json");
 	request->addPostField("null"); //use POST
@@ -73,7 +73,7 @@ void DropboxInfoRequest::userResponseCallback(const Networking::JsonResponse &re
 	}
 
 	Networking::ErrorResponse error(this, "DropboxInfoRequest::userResponseCallback: unknown error");
-	const Networking::CurlJsonRequest *rq = (const Networking::CurlJsonRequest *)response.request;
+	const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
@@ -92,17 +92,17 @@ void DropboxInfoRequest::userResponseCallback(const Networking::JsonResponse &re
 
 	//Dropbox documentation states there are no errors for this API method
 	Common::JSONObject info = json->asObject();
-	if (Networking::CurlJsonRequest::jsonContainsAttribute(info, "name", "DropboxInfoRequest") &&
-		Networking::CurlJsonRequest::jsonIsObject(info.getVal("name"), "DropboxInfoRequest")) {
+	if (Networking::HttpJsonRequest::jsonContainsAttribute(info, "name", "DropboxInfoRequest") &&
+		Networking::HttpJsonRequest::jsonIsObject(info.getVal("name"), "DropboxInfoRequest")) {
 		Common::JSONObject nameInfo = info.getVal("name")->asObject();
-		if (Networking::CurlJsonRequest::jsonContainsString(nameInfo, "display_name", "DropboxInfoRequest")) {
+		if (Networking::HttpJsonRequest::jsonContainsString(nameInfo, "display_name", "DropboxInfoRequest")) {
 			_name = nameInfo.getVal("display_name")->asString();
 		}
 	}
-	if (Networking::CurlJsonRequest::jsonContainsString(info, "account_id", "DropboxInfoRequest")) {
+	if (Networking::HttpJsonRequest::jsonContainsString(info, "account_id", "DropboxInfoRequest")) {
 		_uid = info.getVal("account_id")->asString();
 	}
-	if (Networking::CurlJsonRequest::jsonContainsString(info, "email", "DropboxInfoRequest")) {
+	if (Networking::HttpJsonRequest::jsonContainsString(info, "email", "DropboxInfoRequest")) {
 		_email = info.getVal("email")->asString();
 	}
 	CloudMan.setStorageUsername(kStorageDropboxId, _email);
@@ -110,7 +110,7 @@ void DropboxInfoRequest::userResponseCallback(const Networking::JsonResponse &re
 
 	Networking::JsonCallback innerCallback = new Common::Callback<DropboxInfoRequest, const Networking::JsonResponse &>(this, &DropboxInfoRequest::quotaResponseCallback);
 	Networking::ErrorCallback errorResponseCallback = new Common::Callback<DropboxInfoRequest, const Networking::ErrorResponse &>(this, &DropboxInfoRequest::errorCallback);
-	Networking::CurlJsonRequest *request = new DropboxTokenRefresher(_storage, innerCallback, errorResponseCallback, DROPBOX_API_GET_SPACE_USAGE);
+	Networking::HttpJsonRequest *request = new DropboxTokenRefresher(_storage, innerCallback, errorResponseCallback, DROPBOX_API_GET_SPACE_USAGE);
 	request->addHeader("Authorization: Bearer " + _storage->accessToken());
 	request->addHeader("Content-Type: application/json");
 	request->addPostField("null"); //use POST
@@ -127,7 +127,7 @@ void DropboxInfoRequest::quotaResponseCallback(const Networking::JsonResponse &r
 	}
 
 	Networking::ErrorResponse error(this, "DropboxInfoRequest::quotaResponseCallback: unknown error");
-	const Networking::CurlJsonRequest *rq = (const Networking::CurlJsonRequest *)response.request;
+	const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
@@ -147,7 +147,7 @@ void DropboxInfoRequest::quotaResponseCallback(const Networking::JsonResponse &r
 	//Dropbox documentation states there are no errors for this API method
 	Common::JSONObject info = json->asObject();
 
-	if (!Networking::CurlJsonRequest::jsonContainsIntegerNumber(info, "used", "DropboxInfoRequest")) {
+	if (!Networking::HttpJsonRequest::jsonContainsIntegerNumber(info, "used", "DropboxInfoRequest")) {
 		error.response = "Passed JSON misses 'used' attribute!";
 		finishError(error);
 		delete json;
@@ -156,10 +156,10 @@ void DropboxInfoRequest::quotaResponseCallback(const Networking::JsonResponse &r
 
 	uint64 used = info.getVal("used")->asIntegerNumber(), allocated = 0;
 
-	if (Networking::CurlJsonRequest::jsonContainsAttribute(info, "allocation", "DropboxInfoRequest") &&
-		Networking::CurlJsonRequest::jsonIsObject(info.getVal("allocation"), "DropboxInfoRequest")) {
+	if (Networking::HttpJsonRequest::jsonContainsAttribute(info, "allocation", "DropboxInfoRequest") &&
+		Networking::HttpJsonRequest::jsonIsObject(info.getVal("allocation"), "DropboxInfoRequest")) {
 		Common::JSONObject allocation = info.getVal("allocation")->asObject();
-		if (!Networking::CurlJsonRequest::jsonContainsIntegerNumber(allocation, "allocated", "DropboxInfoRequest")) {
+		if (!Networking::HttpJsonRequest::jsonContainsIntegerNumber(allocation, "allocated", "DropboxInfoRequest")) {
 			error.response = "Passed JSON misses 'allocation/allocated' attribute!";
 			finishError(error);
 			delete json;
diff --git a/backends/cloud/dropbox/dropboxinforequest.h b/backends/cloud/dropbox/dropboxinforequest.h
index a7409a03921..659110815b0 100644
--- a/backends/cloud/dropbox/dropboxinforequest.h
+++ b/backends/cloud/dropbox/dropboxinforequest.h
@@ -23,8 +23,8 @@
 #define BACKENDS_CLOUD_DROPBOX_DROPBOXINFOREQUEST_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/request.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/request.h"
+#include "backends/networking/http/httpjsonrequest.h"
 
 namespace Cloud {
 namespace Dropbox {
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
index e937470f7f6..94d45bc2a3f 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.cpp
@@ -24,9 +24,9 @@
 #include "backends/cloud/dropbox/dropboxtokenrefresher.h"
 #include "backends/cloud/iso8601.h"
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/formats/json.h"
 #include "common/debug.h"
 
@@ -58,7 +58,7 @@ void DropboxListDirectoryRequest::start() {
 
 	Networking::JsonCallback callback = new Common::Callback<DropboxListDirectoryRequest, const Networking::JsonResponse &>(this, &DropboxListDirectoryRequest::responseCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<DropboxListDirectoryRequest, const Networking::ErrorResponse &>(this, &DropboxListDirectoryRequest::errorCallback);
-	Networking::CurlJsonRequest *request = new DropboxTokenRefresher(_storage, callback, failureCallback, DROPBOX_API_LIST_FOLDER);
+	Networking::HttpJsonRequest *request = new DropboxTokenRefresher(_storage, callback, failureCallback, DROPBOX_API_LIST_FOLDER);
 	request->addHeader("Authorization: Bearer " + _storage->accessToken());
 	request->addHeader("Content-Type: application/json");
 
@@ -86,7 +86,7 @@ void DropboxListDirectoryRequest::responseCallback(const Networking::JsonRespons
 		_date = response.request->date();
 
 	Networking::ErrorResponse error(this, "DropboxListDirectoryRequest::responseCallback: unknown error");
-	const Networking::CurlJsonRequest *rq = (const Networking::CurlJsonRequest *)response.request;
+	const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
@@ -131,23 +131,23 @@ void DropboxListDirectoryRequest::responseCallback(const Networking::JsonRespons
 
 		Common::JSONArray items = responseObject.getVal("entries")->asArray();
 		for (uint32 i = 0; i < items.size(); ++i) {
-			if (!Networking::CurlJsonRequest::jsonIsObject(items[i], "DropboxListDirectoryRequest"))
+			if (!Networking::HttpJsonRequest::jsonIsObject(items[i], "DropboxListDirectoryRequest"))
 				continue;
 
 			Common::JSONObject item = items[i]->asObject();
 
-			if (!Networking::CurlJsonRequest::jsonContainsString(item, "path_lower", "DropboxListDirectoryRequest"))
+			if (!Networking::HttpJsonRequest::jsonContainsString(item, "path_lower", "DropboxListDirectoryRequest"))
 				continue;
-			if (!Networking::CurlJsonRequest::jsonContainsString(item, ".tag", "DropboxListDirectoryRequest"))
+			if (!Networking::HttpJsonRequest::jsonContainsString(item, ".tag", "DropboxListDirectoryRequest"))
 				continue;
 
 			Common::String path = item.getVal("path_lower")->asString();
 			bool isDirectory = (item.getVal(".tag")->asString() == "folder");
 			uint32 size = 0, timestamp = 0;
 			if (!isDirectory) {
-				if (!Networking::CurlJsonRequest::jsonContainsString(item, "server_modified", "DropboxListDirectoryRequest"))
+				if (!Networking::HttpJsonRequest::jsonContainsString(item, "server_modified", "DropboxListDirectoryRequest"))
 					continue;
-				if (!Networking::CurlJsonRequest::jsonContainsIntegerNumber(item, "size", "DropboxListDirectoryRequest"))
+				if (!Networking::HttpJsonRequest::jsonContainsIntegerNumber(item, "size", "DropboxListDirectoryRequest"))
 					continue;
 
 				size = item.getVal("size")->asIntegerNumber();
@@ -172,7 +172,7 @@ void DropboxListDirectoryRequest::responseCallback(const Networking::JsonRespons
 	}
 
 	if (hasMore) {
-		if (!Networking::CurlJsonRequest::jsonContainsString(responseObject, "cursor", "DropboxListDirectoryRequest")) {
+		if (!Networking::HttpJsonRequest::jsonContainsString(responseObject, "cursor", "DropboxListDirectoryRequest")) {
 			error.response = "\"has_more\" found, but \"cursor\" is not (or it's not a string)!";
 			finishError(error);
 			delete json;
@@ -181,7 +181,7 @@ void DropboxListDirectoryRequest::responseCallback(const Networking::JsonRespons
 
 		Networking::JsonCallback callback = new Common::Callback<DropboxListDirectoryRequest, const Networking::JsonResponse &>(this, &DropboxListDirectoryRequest::responseCallback);
 		Networking::ErrorCallback failureCallback = new Common::Callback<DropboxListDirectoryRequest, const Networking::ErrorResponse &>(this, &DropboxListDirectoryRequest::errorCallback);
-		Networking::CurlJsonRequest *request = new DropboxTokenRefresher(_storage, callback, failureCallback, DROPBOX_API_LIST_FOLDER_CONTINUE);
+		Networking::HttpJsonRequest *request = new DropboxTokenRefresher(_storage, callback, failureCallback, DROPBOX_API_LIST_FOLDER_CONTINUE);
 		request->addHeader("Authorization: Bearer " + _storage->accessToken());
 		request->addHeader("Content-Type: application/json");
 
diff --git a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
index 754e34e2def..d6163a7ec8e 100644
--- a/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
+++ b/backends/cloud/dropbox/dropboxlistdirectoryrequest.h
@@ -23,9 +23,9 @@
 #define BACKENDS_CLOUD_DROPBOX_DROPBOXLISTDIRECTORYREQUEST_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/request.h"
 #include "common/callback.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/httpjsonrequest.h"
 
 namespace Cloud {
 namespace Dropbox {
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 3dde2158a1a..118449525e6 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -28,8 +28,8 @@
 #include "backends/cloud/dropbox/dropboxlistdirectoryrequest.h"
 #include "backends/cloud/dropbox/dropboxuploadrequest.h"
 #include "backends/cloud/cloudmanager.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/httpjsonrequest.h"
 #include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/formats/json.h"
@@ -83,7 +83,7 @@ Networking::Request *DropboxStorage::streamFileById(const Common::String &path,
 	jsonRequestParameters.setVal("path", new Common::JSONValue(path));
 	Common::JSONValue value(jsonRequestParameters);
 
-	Networking::CurlRequest *request = new Networking::CurlRequest(nullptr, nullptr, DROPBOX_API_FILES_DOWNLOAD); //TODO: is it OK to pass no callbacks?
+	Networking::HttpRequest *request = new Networking::HttpRequest(nullptr, nullptr, DROPBOX_API_FILES_DOWNLOAD); //TODO: is it OK to pass no callbacks?
 	request->addHeader("Authorization: Bearer " + _token);
 	request->addHeader("Dropbox-API-Arg: " + Common::JSON::stringify(&value));
 	request->addHeader("Content-Type: "); //required to be empty (as we do POST, it's usually app/form-url-encoded)
diff --git a/backends/cloud/dropbox/dropboxstorage.h b/backends/cloud/dropbox/dropboxstorage.h
index 929719ce66f..1c948755a31 100644
--- a/backends/cloud/dropbox/dropboxstorage.h
+++ b/backends/cloud/dropbox/dropboxstorage.h
@@ -24,7 +24,7 @@
 
 #include "backends/cloud/basestorage.h"
 #include "common/callback.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/httpjsonrequest.h"
 
 namespace Cloud {
 namespace Dropbox {
diff --git a/backends/cloud/dropbox/dropboxtokenrefresher.cpp b/backends/cloud/dropbox/dropboxtokenrefresher.cpp
index 1fac580e72a..bfa5a2f541b 100644
--- a/backends/cloud/dropbox/dropboxtokenrefresher.cpp
+++ b/backends/cloud/dropbox/dropboxtokenrefresher.cpp
@@ -24,7 +24,7 @@
 #include <curl/curl.h>
 #include "backends/cloud/dropbox/dropboxtokenrefresher.h"
 #include "backends/cloud/dropbox/dropboxstorage.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/debug.h"
 #include "common/formats/json.h"
 
@@ -32,7 +32,7 @@ namespace Cloud {
 namespace Dropbox {
 
 DropboxTokenRefresher::DropboxTokenRefresher(DropboxStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url):
-	CurlJsonRequest(callback, ecb, url), _parentStorage(parent) {}
+	HttpJsonRequest(callback, ecb, url), _parentStorage(parent) {}
 
 DropboxTokenRefresher::~DropboxTokenRefresher() {}
 
@@ -59,7 +59,7 @@ void DropboxTokenRefresher::tokenRefreshed(const Storage::BoolResponse &response
 void DropboxTokenRefresher::finishJson(const Common::JSONValue *json) {
 	if (!json) {
 		//that's probably not an error (200 OK)
-		CurlJsonRequest::finishJson(nullptr);
+		HttpJsonRequest::finishJson(nullptr);
 		return;
 	}
 
@@ -94,7 +94,7 @@ void DropboxTokenRefresher::finishJson(const Common::JSONValue *json) {
 	}
 
 	//notify user of success
-	CurlJsonRequest::finishJson(json);
+	HttpJsonRequest::finishJson(json);
 }
 
 void DropboxTokenRefresher::finishError(const Networking::ErrorResponse &error, Networking::RequestState state) {
@@ -112,12 +112,12 @@ void DropboxTokenRefresher::setHeaders(const Common::Array<Common::String> &head
 	curl_slist_free_all(_headersList);
 	_headersList = nullptr;
 	for (uint32 i = 0; i < headers.size(); ++i)
-		CurlJsonRequest::addHeader(headers[i]);
+		HttpJsonRequest::addHeader(headers[i]);
 }
 
 void DropboxTokenRefresher::addHeader(const Common::String &header) {
 	_headers.push_back(header);
-	CurlJsonRequest::addHeader(header);
+	HttpJsonRequest::addHeader(header);
 }
 
 } // End of namespace Dropbox
diff --git a/backends/cloud/dropbox/dropboxtokenrefresher.h b/backends/cloud/dropbox/dropboxtokenrefresher.h
index f65f051500f..9c3afa6ee4a 100644
--- a/backends/cloud/dropbox/dropboxtokenrefresher.h
+++ b/backends/cloud/dropbox/dropboxtokenrefresher.h
@@ -23,14 +23,14 @@
 #define BACKENDS_CLOUD_DROPBOX_DROPBOXTOKENREFRESHER_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/httpjsonrequest.h"
 
 namespace Cloud {
 namespace Dropbox {
 
 class DropboxStorage;
 
-class DropboxTokenRefresher: public Networking::CurlJsonRequest {
+class DropboxTokenRefresher: public Networking::HttpJsonRequest {
 	DropboxStorage *_parentStorage;
 	Common::Array<Common::String> _headers;
 
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.cpp b/backends/cloud/dropbox/dropboxuploadrequest.cpp
index ef639d69ccd..e52a7949a38 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.cpp
+++ b/backends/cloud/dropbox/dropboxuploadrequest.cpp
@@ -24,9 +24,9 @@
 #include "backends/cloud/dropbox/dropboxtokenrefresher.h"
 #include "backends/cloud/iso8601.h"
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/formats/json.h"
 
 namespace Cloud {
@@ -110,7 +110,7 @@ void DropboxUploadRequest::uploadNextPart() {
 	Common::JSONValue value(jsonRequestParameters);
 	Networking::JsonCallback callback = new Common::Callback<DropboxUploadRequest, const Networking::JsonResponse &>(this, &DropboxUploadRequest::partUploadedCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<DropboxUploadRequest, const Networking::ErrorResponse &>(this, &DropboxUploadRequest::partUploadedErrorCallback);
-	Networking::CurlJsonRequest *request = new DropboxTokenRefresher(_storage, callback, failureCallback, url.c_str());
+	Networking::HttpJsonRequest *request = new DropboxTokenRefresher(_storage, callback, failureCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + _storage->accessToken());
 	request->addHeader("Content-Type: application/octet-stream");
 	request->addHeader("Dropbox-API-Arg: " + Common::JSON::stringify(&value));
@@ -128,7 +128,7 @@ void DropboxUploadRequest::partUploadedCallback(const Networking::JsonResponse &
 		return;
 
 	Networking::ErrorResponse error(this, false, true, "", -1);
-	const Networking::CurlJsonRequest *rq = (const Networking::CurlJsonRequest *)response.request;
+	const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
@@ -147,7 +147,7 @@ void DropboxUploadRequest::partUploadedCallback(const Networking::JsonResponse &
 		//debug(9, "%s", json->stringify(true).c_str());
 
 		if (object.contains("error") || object.contains("error_summary")) {
-			if (Networking::CurlJsonRequest::jsonContainsString(object, "error_summary", "DropboxUploadRequest")) {
+			if (Networking::HttpJsonRequest::jsonContainsString(object, "error_summary", "DropboxUploadRequest")) {
 				warning("Dropbox returned error: %s", object.getVal("error_summary")->asString().c_str());
 			}
 			error.response = json->stringify(true);
@@ -156,9 +156,9 @@ void DropboxUploadRequest::partUploadedCallback(const Networking::JsonResponse &
 			return;
 		}
 
-		if (Networking::CurlJsonRequest::jsonContainsString(object, "path_lower", "DropboxUploadRequest") &&
-			Networking::CurlJsonRequest::jsonContainsString(object, "server_modified", "DropboxUploadRequest") &&
-			Networking::CurlJsonRequest::jsonContainsIntegerNumber(object, "size", "DropboxUploadRequest")) {
+		if (Networking::HttpJsonRequest::jsonContainsString(object, "path_lower", "DropboxUploadRequest") &&
+			Networking::HttpJsonRequest::jsonContainsString(object, "server_modified", "DropboxUploadRequest") &&
+			Networking::HttpJsonRequest::jsonContainsIntegerNumber(object, "size", "DropboxUploadRequest")) {
 			//finished
 			Common::String path = object.getVal("path_lower")->asString();
 			uint32 size = object.getVal("size")->asIntegerNumber();
@@ -168,7 +168,7 @@ void DropboxUploadRequest::partUploadedCallback(const Networking::JsonResponse &
 		}
 
 		if (_sessionId == "") {
-			if (Networking::CurlJsonRequest::jsonContainsString(object, "session_id", "DropboxUploadRequest"))
+			if (Networking::HttpJsonRequest::jsonContainsString(object, "session_id", "DropboxUploadRequest"))
 				_sessionId = object.getVal("session_id")->asString();
 			needsFinishRequest = true;
 		}
diff --git a/backends/cloud/dropbox/dropboxuploadrequest.h b/backends/cloud/dropbox/dropboxuploadrequest.h
index 9ea3b3af48d..b9ca85d0883 100644
--- a/backends/cloud/dropbox/dropboxuploadrequest.h
+++ b/backends/cloud/dropbox/dropboxuploadrequest.h
@@ -23,8 +23,8 @@
 #define BACKENDS_CLOUD_DROPBOX_DROPBOXUPLOADREQUEST_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/request.h"
 #include "common/callback.h"
 
 namespace Cloud {
diff --git a/backends/cloud/folderdownloadrequest.cpp b/backends/cloud/folderdownloadrequest.cpp
index 1cf034508e4..0148b667b23 100644
--- a/backends/cloud/folderdownloadrequest.cpp
+++ b/backends/cloud/folderdownloadrequest.cpp
@@ -24,7 +24,7 @@
 #include "backends/cloud/id/iddownloadrequest.h"
 #include "common/debug.h"
 #include "gui/downloaddialog.h"
-#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/http/connectionmanager.h"
 #include "cloudmanager.h"
 
 namespace Cloud {
diff --git a/backends/cloud/folderdownloadrequest.h b/backends/cloud/folderdownloadrequest.h
index d134f79bc97..ec551dcc9a9 100644
--- a/backends/cloud/folderdownloadrequest.h
+++ b/backends/cloud/folderdownloadrequest.h
@@ -22,7 +22,7 @@
 #ifndef BACKENDS_CLOUD_FOLDERDOWNLOADREQUEST_H
 #define BACKENDS_CLOUD_FOLDERDOWNLOADREQUEST_H
 
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/request.h"
 #include "backends/cloud/storage.h"
 #include "gui/object.h"
 
diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
index c1a596317a1..5e46712d316 100644
--- a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
+++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.cpp
@@ -23,9 +23,9 @@
 #include "backends/cloud/googledrive/googledrivestorage.h"
 #include "backends/cloud/iso8601.h"
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/formats/json.h"
 #include "googledrivetokenrefresher.h"
 
@@ -66,7 +66,7 @@ void GoogleDriveListDirectoryByIdRequest::makeRequest(const Common::String &page
 
 	Networking::JsonCallback callback = new Common::Callback<GoogleDriveListDirectoryByIdRequest, const Networking::JsonResponse &>(this, &GoogleDriveListDirectoryByIdRequest::responseCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveListDirectoryByIdRequest, const Networking::ErrorResponse &>(this, &GoogleDriveListDirectoryByIdRequest::errorCallback);
-	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
+	Networking::HttpJsonRequest *request = new GoogleDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + _storage->accessToken());
 	_workingRequest = ConnMan.addRequest(request);
 }
@@ -81,7 +81,7 @@ void GoogleDriveListDirectoryByIdRequest::responseCallback(const Networking::Jso
 		_date = response.request->date();
 
 	Networking::ErrorResponse error(this, "GoogleDriveListDirectoryByIdRequest::responseCallback");
-	const Networking::CurlJsonRequest *rq = (const Networking::CurlJsonRequest *)response.request;
+	const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
diff --git a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
index 1e6197d9d8b..42321492b6c 100644
--- a/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
+++ b/backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h
@@ -23,9 +23,9 @@
 #define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVELISTDIRECTORYBYIDREQUEST_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/request.h"
 #include "common/callback.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/httpjsonrequest.h"
 
 namespace Cloud {
 namespace GoogleDrive {
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 7e48c1af6c6..67ee1620675 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -27,9 +27,9 @@
 #include "backends/cloud/googledrive/googledrivetokenrefresher.h"
 #include "backends/cloud/googledrive/googledrivelistdirectorybyidrequest.h"
 #include "backends/cloud/googledrive/googledriveuploadrequest.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/formats/json.h"
@@ -81,7 +81,7 @@ void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, co
 		return;
 	}
 
-	if (!Networking::CurlJsonRequest::jsonIsObject(json, "GoogleDriveStorage::infoInnerCallback")) {
+	if (!Networking::HttpJsonRequest::jsonIsObject(json, "GoogleDriveStorage::infoInnerCallback")) {
 		delete json;
 		delete outerCallback;
 		return;
@@ -92,30 +92,30 @@ void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, co
 	Common::String uid, displayName, email;
 	uint64 quotaUsed = 0, quotaAllocated = 0;
 
-	if (Networking::CurlJsonRequest::jsonContainsAttribute(jsonInfo, "user", "GoogleDriveStorage::infoInnerCallback") &&
-		Networking::CurlJsonRequest::jsonIsObject(jsonInfo.getVal("user"), "GoogleDriveStorage::infoInnerCallback")) {
+	if (Networking::HttpJsonRequest::jsonContainsAttribute(jsonInfo, "user", "GoogleDriveStorage::infoInnerCallback") &&
+		Networking::HttpJsonRequest::jsonIsObject(jsonInfo.getVal("user"), "GoogleDriveStorage::infoInnerCallback")) {
 		//"me":true, "kind":"drive#user","photoLink": "",
 		//"displayName":"Alexander Tkachev","emailAddress":"alexander at tkachov.ru","permissionId":""
 		Common::JSONObject user = jsonInfo.getVal("user")->asObject();
-		if (Networking::CurlJsonRequest::jsonContainsString(user, "permissionId", "GoogleDriveStorage::infoInnerCallback"))
+		if (Networking::HttpJsonRequest::jsonContainsString(user, "permissionId", "GoogleDriveStorage::infoInnerCallback"))
 			uid = user.getVal("permissionId")->asString(); //not sure it's user's id, but who cares anyway?
-		if (Networking::CurlJsonRequest::jsonContainsString(user, "displayName", "GoogleDriveStorage::infoInnerCallback"))
+		if (Networking::HttpJsonRequest::jsonContainsString(user, "displayName", "GoogleDriveStorage::infoInnerCallback"))
 			displayName = user.getVal("displayName")->asString();
-		if (Networking::CurlJsonRequest::jsonContainsString(user, "emailAddress", "GoogleDriveStorage::infoInnerCallback"))
+		if (Networking::HttpJsonRequest::jsonContainsString(user, "emailAddress", "GoogleDriveStorage::infoInnerCallback"))
 			email = user.getVal("emailAddress")->asString();
 	}
 
-	if (Networking::CurlJsonRequest::jsonContainsAttribute(jsonInfo, "storageQuota", "GoogleDriveStorage::infoInnerCallback") &&
-		Networking::CurlJsonRequest::jsonIsObject(jsonInfo.getVal("storageQuota"), "GoogleDriveStorage::infoInnerCallback")) {
+	if (Networking::HttpJsonRequest::jsonContainsAttribute(jsonInfo, "storageQuota", "GoogleDriveStorage::infoInnerCallback") &&
+		Networking::HttpJsonRequest::jsonIsObject(jsonInfo.getVal("storageQuota"), "GoogleDriveStorage::infoInnerCallback")) {
 		//"usageInDrive":"6332462","limit":"18253611008","usage":"6332462","usageInDriveTrash":"0"
 		Common::JSONObject storageQuota = jsonInfo.getVal("storageQuota")->asObject();
 
-		if (Networking::CurlJsonRequest::jsonContainsString(storageQuota, "usage", "GoogleDriveStorage::infoInnerCallback")) {
+		if (Networking::HttpJsonRequest::jsonContainsString(storageQuota, "usage", "GoogleDriveStorage::infoInnerCallback")) {
 			Common::String usage = storageQuota.getVal("usage")->asString();
 			quotaUsed = usage.asUint64();
 		}
 
-		if (Networking::CurlJsonRequest::jsonContainsString(storageQuota, "limit", "GoogleDriveStorage::infoInnerCallback")) {
+		if (Networking::HttpJsonRequest::jsonContainsString(storageQuota, "limit", "GoogleDriveStorage::infoInnerCallback")) {
 			Common::String limit = storageQuota.getVal("limit")->asString();
 			quotaAllocated = limit.asUint64();
 		}
@@ -140,7 +140,7 @@ void GoogleDriveStorage::createDirectoryInnerCallback(BoolCallback outerCallback
 	}
 
 	if (outerCallback) {
-		if (Networking::CurlJsonRequest::jsonIsObject(json, "GoogleDriveStorage::createDirectoryInnerCallback")) {
+		if (Networking::HttpJsonRequest::jsonIsObject(json, "GoogleDriveStorage::createDirectoryInnerCallback")) {
 			Common::JSONObject jsonInfo = json->asObject();
 			(*outerCallback)(BoolResponse(nullptr, jsonInfo.contains("id")));
 		} else {
@@ -192,7 +192,7 @@ Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(const Commo
 
 	Common::String url = GOOGLEDRIVE_API_FILES;
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, const BoolResponse &, const Networking::JsonResponse &>(this, &GoogleDriveStorage::createDirectoryInnerCallback, callback);
-	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
+	Networking::HttpJsonRequest *request = new GoogleDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + accessToken());
 	request->addHeader("Content-Type: application/json");
 
@@ -214,7 +214,7 @@ Networking::Request *GoogleDriveStorage::info(StorageInfoCallback callback, Netw
 	if (!callback)
 		callback = new Common::Callback<GoogleDriveStorage, const StorageInfoResponse &>(this, &GoogleDriveStorage::printInfo);
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, const StorageInfoResponse &, const Networking::JsonResponse &>(this, &GoogleDriveStorage::infoInnerCallback, callback);
-	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(this, innerCallback, errorCallback, GOOGLEDRIVE_API_ABOUT);
+	Networking::HttpJsonRequest *request = new GoogleDriveTokenRefresher(this, innerCallback, errorCallback, GOOGLEDRIVE_API_ABOUT);
 	request->addHeader("Authorization: Bearer " + _token);
 	return addRequest(request);
 }
diff --git a/backends/cloud/googledrive/googledrivestorage.h b/backends/cloud/googledrive/googledrivestorage.h
index f897b36f8a5..5115f581bb3 100644
--- a/backends/cloud/googledrive/googledrivestorage.h
+++ b/backends/cloud/googledrive/googledrivestorage.h
@@ -23,7 +23,7 @@
 #define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVESTORAGE_H
 
 #include "backends/cloud/id/idstorage.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/httpjsonrequest.h"
 
 namespace Cloud {
 namespace GoogleDrive {
diff --git a/backends/cloud/googledrive/googledrivetokenrefresher.cpp b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
index a1b1f9ba9db..95e51b23143 100644
--- a/backends/cloud/googledrive/googledrivetokenrefresher.cpp
+++ b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
@@ -24,7 +24,7 @@
 #include <curl/curl.h>
 #include "backends/cloud/googledrive/googledrivetokenrefresher.h"
 #include "backends/cloud/googledrive/googledrivestorage.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/debug.h"
 #include "common/formats/json.h"
 
@@ -32,7 +32,7 @@ namespace Cloud {
 namespace GoogleDrive {
 
 GoogleDriveTokenRefresher::GoogleDriveTokenRefresher(GoogleDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url):
-	CurlJsonRequest(callback, ecb, url), _parentStorage(parent) {}
+	HttpJsonRequest(callback, ecb, url), _parentStorage(parent) {}
 
 GoogleDriveTokenRefresher::~GoogleDriveTokenRefresher() {}
 
@@ -59,7 +59,7 @@ void GoogleDriveTokenRefresher::tokenRefreshed(const Storage::BoolResponse &resp
 void GoogleDriveTokenRefresher::finishJson(const Common::JSONValue *json) {
 	if (!json) {
 		//that's probably not an error (200 OK)
-		CurlJsonRequest::finishJson(nullptr);
+		HttpJsonRequest::finishJson(nullptr);
 		return;
 	}
 
@@ -105,7 +105,7 @@ void GoogleDriveTokenRefresher::finishJson(const Common::JSONValue *json) {
 	}
 
 	//notify user of success
-	CurlJsonRequest::finishJson(json);
+	HttpJsonRequest::finishJson(json);
 }
 
 void GoogleDriveTokenRefresher::setHeaders(const Common::Array<Common::String> &headers) {
@@ -113,12 +113,12 @@ void GoogleDriveTokenRefresher::setHeaders(const Common::Array<Common::String> &
 	curl_slist_free_all(_headersList);
 	_headersList = nullptr;
 	for (uint32 i = 0; i < headers.size(); ++i)
-		CurlJsonRequest::addHeader(headers[i]);
+		HttpJsonRequest::addHeader(headers[i]);
 }
 
 void GoogleDriveTokenRefresher::addHeader(const Common::String &header) {
 	_headers.push_back(header);
-	CurlJsonRequest::addHeader(header);
+	HttpJsonRequest::addHeader(header);
 }
 
 } // End of namespace GoogleDrive
diff --git a/backends/cloud/googledrive/googledrivetokenrefresher.h b/backends/cloud/googledrive/googledrivetokenrefresher.h
index 2be4e0718ea..05f610638dd 100644
--- a/backends/cloud/googledrive/googledrivetokenrefresher.h
+++ b/backends/cloud/googledrive/googledrivetokenrefresher.h
@@ -23,14 +23,14 @@
 #define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVETOKENREFRESHER_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/httpjsonrequest.h"
 
 namespace Cloud {
 namespace GoogleDrive {
 
 class GoogleDriveStorage;
 
-class GoogleDriveTokenRefresher: public Networking::CurlJsonRequest {
+class GoogleDriveTokenRefresher: public Networking::HttpJsonRequest {
 	GoogleDriveStorage *_parentStorage;
 	Common::Array<Common::String> _headers;
 
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp
index 3d8808305d8..57a26b61e28 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.cpp
+++ b/backends/cloud/googledrive/googledriveuploadrequest.cpp
@@ -23,9 +23,9 @@
 #include "backends/cloud/googledrive/googledrivestorage.h"
 #include "backends/cloud/iso8601.h"
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/formats/json.h"
 #include "googledrivetokenrefresher.h"
 
@@ -118,7 +118,7 @@ void GoogleDriveUploadRequest::startUpload() {
 	url += "?uploadType=resumable&fields=id,mimeType,modifiedTime,name,size";
 	Networking::JsonCallback callback = new Common::Callback<GoogleDriveUploadRequest, const Networking::JsonResponse &>(this, &GoogleDriveUploadRequest::startUploadCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveUploadRequest, const Networking::ErrorResponse &>(this, &GoogleDriveUploadRequest::startUploadErrorCallback);
-	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
+	Networking::HttpJsonRequest *request = new GoogleDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + _storage->accessToken());
 	request->addHeader("Content-Type: application/json");
 	if (_resolvedId != "")
@@ -146,7 +146,7 @@ void GoogleDriveUploadRequest::startUploadCallback(const Networking::JsonRespons
 		return;
 
 	Networking::ErrorResponse error(this, false, true, "GoogleDriveUploadRequest::startUploadCallback", -1);
-	const Networking::CurlJsonRequest *rq = (const Networking::CurlJsonRequest *)response.request;
+	const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
 	if (rq) {
 		const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
 		if (stream) {
@@ -191,7 +191,7 @@ void GoogleDriveUploadRequest::uploadNextPart() {
 
 	Networking::JsonCallback callback = new Common::Callback<GoogleDriveUploadRequest, const Networking::JsonResponse &>(this, &GoogleDriveUploadRequest::partUploadedCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveUploadRequest, const Networking::ErrorResponse &>(this, &GoogleDriveUploadRequest::partUploadedErrorCallback);
-	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
+	Networking::HttpJsonRequest *request = new GoogleDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + _storage->accessToken());
 	request->usePut();
 
@@ -252,7 +252,7 @@ void GoogleDriveUploadRequest::partUploadedCallback(const Networking::JsonRespon
 		return;
 
 	Networking::ErrorResponse error(this, false, true, "", -1);
-	const Networking::CurlJsonRequest *rq = (const Networking::CurlJsonRequest *)response.request;
+	const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
 	if (rq) {
 		const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
 		if (stream) {
@@ -283,17 +283,17 @@ void GoogleDriveUploadRequest::partUploadedCallback(const Networking::JsonRespon
 			return;
 		}
 
-		if (Networking::CurlJsonRequest::jsonContainsString(object, "id", "GoogleDriveUploadRequest") &&
-			Networking::CurlJsonRequest::jsonContainsString(object, "name", "GoogleDriveUploadRequest") &&
-			Networking::CurlJsonRequest::jsonContainsString(object, "mimeType", "GoogleDriveUploadRequest")) {
+		if (Networking::HttpJsonRequest::jsonContainsString(object, "id", "GoogleDriveUploadRequest") &&
+			Networking::HttpJsonRequest::jsonContainsString(object, "name", "GoogleDriveUploadRequest") &&
+			Networking::HttpJsonRequest::jsonContainsString(object, "mimeType", "GoogleDriveUploadRequest")) {
 			//finished
 			Common::String id = object.getVal("id")->asString();
 			Common::String name = object.getVal("name")->asString();
 			bool isDirectory = (object.getVal("mimeType")->asString() == "application/vnd.google-apps.folder");
 			uint32 size = 0, timestamp = 0;
-			if (Networking::CurlJsonRequest::jsonContainsString(object, "size", "GoogleDriveUploadRequest", true))
+			if (Networking::HttpJsonRequest::jsonContainsString(object, "size", "GoogleDriveUploadRequest", true))
 				size = object.getVal("size")->asString().asUint64();
-			if (Networking::CurlJsonRequest::jsonContainsString(object, "modifiedTime", "GoogleDriveUploadRequest", true))
+			if (Networking::HttpJsonRequest::jsonContainsString(object, "modifiedTime", "GoogleDriveUploadRequest", true))
 				timestamp = ISO8601::convertToTimestamp(object.getVal("modifiedTime")->asString());
 
 			finishUpload(StorageFile(id, _savePath, name, size, timestamp, isDirectory));
@@ -316,7 +316,7 @@ void GoogleDriveUploadRequest::partUploadedErrorCallback(const Networking::Error
 	if (_ignoreCallback)
 		return;
 
-	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)error.request;
+	Networking::HttpJsonRequest *rq = (Networking::HttpJsonRequest *)error.request;
 	if (rq) {
 		const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
 		if (stream) {
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.h b/backends/cloud/googledrive/googledriveuploadrequest.h
index a48549552cf..3521e6ae953 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.h
+++ b/backends/cloud/googledrive/googledriveuploadrequest.h
@@ -23,8 +23,8 @@
 #define BACKENDS_CLOUD_GOOGLEDRIVE_GOOGLEDRIVEUPLOADREQUEST_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/request.h"
 #include "common/callback.h"
 
 namespace Cloud {
diff --git a/backends/cloud/id/idcreatedirectoryrequest.h b/backends/cloud/id/idcreatedirectoryrequest.h
index 92f3231a4f6..cf270f7b059 100644
--- a/backends/cloud/id/idcreatedirectoryrequest.h
+++ b/backends/cloud/id/idcreatedirectoryrequest.h
@@ -23,7 +23,7 @@
 #define BACKENDS_CLOUD_ID_IDCREATEDIRECTORYREQUEST_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/request.h"
 #include "common/callback.h"
 
 namespace Cloud {
diff --git a/backends/cloud/id/iddownloadrequest.h b/backends/cloud/id/iddownloadrequest.h
index f88fedbfe38..ed7d5b9269e 100644
--- a/backends/cloud/id/iddownloadrequest.h
+++ b/backends/cloud/id/iddownloadrequest.h
@@ -23,7 +23,7 @@
 #define BACKENDS_CLOUD_ID_IDDOWNLOADREQUEST_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/request.h"
 #include "common/callback.h"
 
 namespace Cloud {
diff --git a/backends/cloud/id/idlistdirectoryrequest.h b/backends/cloud/id/idlistdirectoryrequest.h
index 6124021e87e..d2926428c71 100644
--- a/backends/cloud/id/idlistdirectoryrequest.h
+++ b/backends/cloud/id/idlistdirectoryrequest.h
@@ -23,7 +23,7 @@
 #define BACKENDS_CLOUD_ID_IDLISTDIRECTORYREQUEST_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/request.h"
 #include "common/callback.h"
 
 namespace Cloud {
diff --git a/backends/cloud/id/idresolveidrequest.h b/backends/cloud/id/idresolveidrequest.h
index fb2f75d9fa0..b9b46b87f90 100644
--- a/backends/cloud/id/idresolveidrequest.h
+++ b/backends/cloud/id/idresolveidrequest.h
@@ -23,7 +23,7 @@
 #define BACKENDS_CLOUD_ID_IDRESOLVEIDREQUEST_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/request.h"
 #include "common/callback.h"
 
 namespace Cloud {
diff --git a/backends/cloud/id/idstorage.h b/backends/cloud/id/idstorage.h
index 90b1bc35af0..ad66f455ac7 100644
--- a/backends/cloud/id/idstorage.h
+++ b/backends/cloud/id/idstorage.h
@@ -23,7 +23,7 @@
 #define BACKENDS_CLOUD_ID_IDSTORAGE_H
 
 #include "backends/cloud/basestorage.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/httpjsonrequest.h"
 
 /*
  * Id::IdStorage is a special base class, which is created
diff --git a/backends/cloud/id/idstreamfilerequest.h b/backends/cloud/id/idstreamfilerequest.h
index a28a1581135..8ecbfe73abd 100644
--- a/backends/cloud/id/idstreamfilerequest.h
+++ b/backends/cloud/id/idstreamfilerequest.h
@@ -23,7 +23,7 @@
 #define BACKENDS_CLOUD_ID_IDSTREAMFILEREQUEST_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/request.h"
 #include "common/callback.h"
 
 namespace Cloud {
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
index 2ca0ff5fd25..767f55bad8a 100644
--- a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
@@ -22,9 +22,9 @@
 #include "backends/cloud/onedrive/onedrivecreatedirectoryrequest.h"
 #include "backends/cloud/onedrive/onedrivestorage.h"
 #include "backends/cloud/onedrive/onedrivetokenrefresher.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/formats/json.h"
 
 namespace Cloud {
@@ -72,7 +72,7 @@ void OneDriveCreateDirectoryRequest::start() {
 	url += "/children";
 	Networking::JsonCallback innerCallback = new Common::Callback<OneDriveCreateDirectoryRequest, const Networking::JsonResponse &>(this, &OneDriveCreateDirectoryRequest::responseCallback);
 	Networking::ErrorCallback errorResponseCallback = new Common::Callback<OneDriveCreateDirectoryRequest, const Networking::ErrorResponse &>(this, &OneDriveCreateDirectoryRequest::errorCallback);
-	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, innerCallback, errorResponseCallback, url.c_str());
+	Networking::HttpJsonRequest *request = new OneDriveTokenRefresher(_storage, innerCallback, errorResponseCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + _storage->accessToken());
 	request->addHeader("Content-Type: application/json");
 
@@ -96,7 +96,7 @@ void OneDriveCreateDirectoryRequest::responseCallback(const Networking::JsonResp
 		_date = response.request->date();
 
 	Networking::ErrorResponse error(this, "OneDriveCreateDirectoryRequest::responseCallback: unknown error");
-	const Networking::CurlJsonRequest *rq = (const Networking::CurlJsonRequest *)response.request;
+	const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
index 0be3cd9afe1..2a0c03678bd 100644
--- a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.h
@@ -23,8 +23,8 @@
 #define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVECREATEDIRECTORYREQUEST_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/request.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/request.h"
+#include "backends/networking/http/httpjsonrequest.h"
 
 namespace Cloud {
 namespace OneDrive {
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
index 46e5078d71f..da0c6ecbd7e 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
@@ -23,8 +23,8 @@
 #include "backends/cloud/onedrive/onedrivestorage.h"
 #include "backends/cloud/onedrive/onedrivetokenrefresher.h"
 #include "backends/cloud/iso8601.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/formats/json.h"
 
 namespace Cloud {
@@ -84,7 +84,7 @@ void OneDriveListDirectoryRequest::listNextDirectory() {
 void OneDriveListDirectoryRequest::makeRequest(const Common::String &url) {
 	Networking::JsonCallback callback = new Common::Callback<OneDriveListDirectoryRequest, const Networking::JsonResponse &>(this, &OneDriveListDirectoryRequest::listedDirectoryCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveListDirectoryRequest, const Networking::ErrorResponse &>(this, &OneDriveListDirectoryRequest::listedDirectoryErrorCallback);
-	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
+	Networking::HttpJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
 	request->addHeader("Authorization: bearer " + _storage->accessToken());
 	_workingRequest = ConnMan.addRequest(request);
 }
@@ -102,7 +102,7 @@ void OneDriveListDirectoryRequest::listedDirectoryCallback(const Networking::Jso
 		_date = response.request->date();
 
 	Networking::ErrorResponse error(this, "OneDriveListDirectoryRequest::listedDirectoryCallback: unknown error");
-	const Networking::CurlJsonRequest *rq = (const Networking::CurlJsonRequest *)response.request;
+	const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
@@ -122,7 +122,7 @@ void OneDriveListDirectoryRequest::listedDirectoryCallback(const Networking::Jso
 	Common::JSONObject object = json->asObject();
 
 	//check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults
-	if (!Networking::CurlJsonRequest::jsonContainsArray(object, "value", "OneDriveListDirectoryRequest")) {
+	if (!Networking::HttpJsonRequest::jsonContainsArray(object, "value", "OneDriveListDirectoryRequest")) {
 		error.response = "\"value\" not found or that's not an array!";
 		finishError(error);
 		delete json;
@@ -131,14 +131,14 @@ void OneDriveListDirectoryRequest::listedDirectoryCallback(const Networking::Jso
 
 	Common::JSONArray items = object.getVal("value")->asArray();
 	for (uint32 i = 0; i < items.size(); ++i) {
-		if (!Networking::CurlJsonRequest::jsonIsObject(items[i], "OneDriveListDirectoryRequest")) continue;
+		if (!Networking::HttpJsonRequest::jsonIsObject(items[i], "OneDriveListDirectoryRequest")) continue;
 
 		Common::JSONObject item = items[i]->asObject();
 
-		if (!Networking::CurlJsonRequest::jsonContainsAttribute(item, "folder", "OneDriveListDirectoryRequest", true)) continue;
-		if (!Networking::CurlJsonRequest::jsonContainsString(item, "name", "OneDriveListDirectoryRequest")) continue;
-		if (!Networking::CurlJsonRequest::jsonContainsIntegerNumber(item, "size", "OneDriveListDirectoryRequest")) continue;
-		if (!Networking::CurlJsonRequest::jsonContainsString(item, "lastModifiedDateTime", "OneDriveListDirectoryRequest")) continue;
+		if (!Networking::HttpJsonRequest::jsonContainsAttribute(item, "folder", "OneDriveListDirectoryRequest", true)) continue;
+		if (!Networking::HttpJsonRequest::jsonContainsString(item, "name", "OneDriveListDirectoryRequest")) continue;
+		if (!Networking::HttpJsonRequest::jsonContainsIntegerNumber(item, "size", "OneDriveListDirectoryRequest")) continue;
+		if (!Networking::HttpJsonRequest::jsonContainsString(item, "lastModifiedDateTime", "OneDriveListDirectoryRequest")) continue;
 
 		Common::String path = _currentDirectory + item.getVal("name")->asString();
 		bool isDirectory = item.contains("folder");
@@ -154,7 +154,7 @@ void OneDriveListDirectoryRequest::listedDirectoryCallback(const Networking::Jso
 
 	bool hasMore = object.contains("@odata.nextLink");
 	if (hasMore) {
-		if (!Networking::CurlJsonRequest::jsonContainsString(object, "@odata.nextLink", "OneDriveListDirectoryRequest")) {
+		if (!Networking::HttpJsonRequest::jsonContainsString(object, "@odata.nextLink", "OneDriveListDirectoryRequest")) {
 			error.response = "\"@odata.nextLink\" is not a string!";
 			finishError(error);
 			delete json;
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.h b/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
index 883e81ac045..07e412897f6 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.h
@@ -23,8 +23,8 @@
 #define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVELISTDIRECTORYREQUEST_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/request.h"
 #include "common/callback.h"
 
 namespace Cloud {
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index ed327265470..b7879299df0 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -28,9 +28,9 @@
 #include "backends/cloud/onedrive/onedrivetokenrefresher.h"
 #include "backends/cloud/onedrive/onedrivelistdirectoryrequest.h"
 #include "backends/cloud/onedrive/onedriveuploadrequest.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/config-manager.h"
 #include "common/debug.h"
 #include "common/formats/json.h"
@@ -80,7 +80,7 @@ void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, const
 		return;
 	}
 
-	if (!Networking::CurlJsonRequest::jsonIsObject(json, "OneDriveStorage::infoInnerCallback")) {
+	if (!Networking::HttpJsonRequest::jsonIsObject(json, "OneDriveStorage::infoInnerCallback")) {
 		delete json;
 		delete outerCallback;
 		return;
@@ -91,18 +91,18 @@ void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, const
 	Common::String uid, displayName, email;
 	uint64 quotaUsed = 0, quotaAllocated = 26843545600LL; // 25 GB, because I actually don't know any way to find out the real one
 
-	if (Networking::CurlJsonRequest::jsonContainsObject(jsonInfo, "createdBy", "OneDriveStorage::infoInnerCallback")) {
+	if (Networking::HttpJsonRequest::jsonContainsObject(jsonInfo, "createdBy", "OneDriveStorage::infoInnerCallback")) {
 		Common::JSONObject createdBy = jsonInfo.getVal("createdBy")->asObject();
-		if (Networking::CurlJsonRequest::jsonContainsObject(createdBy, "user", "OneDriveStorage::infoInnerCallback")) {
+		if (Networking::HttpJsonRequest::jsonContainsObject(createdBy, "user", "OneDriveStorage::infoInnerCallback")) {
 			Common::JSONObject user = createdBy.getVal("user")->asObject();
-			if (Networking::CurlJsonRequest::jsonContainsString(user, "id", "OneDriveStorage::infoInnerCallback"))
+			if (Networking::HttpJsonRequest::jsonContainsString(user, "id", "OneDriveStorage::infoInnerCallback"))
 				uid = user.getVal("id")->asString();
-			if (Networking::CurlJsonRequest::jsonContainsString(user, "displayName", "OneDriveStorage::infoInnerCallback"))
+			if (Networking::HttpJsonRequest::jsonContainsString(user, "displayName", "OneDriveStorage::infoInnerCallback"))
 				displayName = user.getVal("displayName")->asString();
 		}
 	}
 
-	if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(jsonInfo, "size", "OneDriveStorage::infoInnerCallback")) {
+	if (Networking::HttpJsonRequest::jsonContainsIntegerNumber(jsonInfo, "size", "OneDriveStorage::infoInnerCallback")) {
 		quotaUsed = jsonInfo.getVal("size")->asIntegerNumber();
 	}
 
@@ -131,7 +131,7 @@ void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback out
 		return;
 	}
 
-	if (!Networking::CurlJsonRequest::jsonIsObject(json, "OneDriveStorage::fileInfoCallback")) {
+	if (!Networking::HttpJsonRequest::jsonIsObject(json, "OneDriveStorage::fileInfoCallback")) {
 		if (outerCallback)
 			(*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
 		delete json;
@@ -140,7 +140,7 @@ void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback out
 	}
 
 	Common::JSONObject result = response.value->asObject();
-	if (!Networking::CurlJsonRequest::jsonContainsString(result, "@microsoft.graph.downloadUrl", "OneDriveStorage::fileInfoCallback")) {
+	if (!Networking::HttpJsonRequest::jsonContainsString(result, "@microsoft.graph.downloadUrl", "OneDriveStorage::fileInfoCallback")) {
 		warning("OneDriveStorage: downloadUrl not found in passed JSON");
 		debug(9, "%s", response.value->stringify().c_str());
 		if (outerCallback)
@@ -175,7 +175,7 @@ Networking::Request *OneDriveStorage::streamFileById(const Common::String &path,
 	debug(9, "OneDrive: `download \"%s\"`", path.c_str());
 	Common::String url = ONEDRIVE_API_SPECIAL_APPROOT_ID + ConnMan.urlEncode(path);
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, const Networking::NetworkReadStreamResponse &, const Networking::JsonResponse &>(this, &OneDriveStorage::fileInfoCallback, outerCallback);
-	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
+	Networking::HttpJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
 	request->addHeader("Authorization: bearer " + _token);
 	return addRequest(request);
 }
@@ -190,7 +190,7 @@ Networking::Request *OneDriveStorage::createDirectory(const Common::String &path
 Networking::Request *OneDriveStorage::info(StorageInfoCallback callback, Networking::ErrorCallback errorCallback) {
 	debug(9, "OneDrive: `info`");
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, const StorageInfoResponse &, const Networking::JsonResponse &>(this, &OneDriveStorage::infoInnerCallback, callback);
-	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, ONEDRIVE_API_SPECIAL_APPROOT);
+	Networking::HttpJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, ONEDRIVE_API_SPECIAL_APPROOT);
 	request->addHeader("Authorization: bearer " + _token);
 	return addRequest(request);
 }
diff --git a/backends/cloud/onedrive/onedrivestorage.h b/backends/cloud/onedrive/onedrivestorage.h
index 848cf928494..99778282f4e 100644
--- a/backends/cloud/onedrive/onedrivestorage.h
+++ b/backends/cloud/onedrive/onedrivestorage.h
@@ -23,7 +23,7 @@
 #define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVESTORAGE_H
 
 #include "backends/cloud/basestorage.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/httpjsonrequest.h"
 
 namespace Cloud {
 namespace OneDrive {
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index cc6da8ff3f1..b905769ec49 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -24,7 +24,7 @@
 #include <curl/curl.h>
 #include "backends/cloud/onedrive/onedrivetokenrefresher.h"
 #include "backends/cloud/onedrive/onedrivestorage.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/debug.h"
 #include "common/formats/json.h"
 
@@ -32,7 +32,7 @@ namespace Cloud {
 namespace OneDrive {
 
 OneDriveTokenRefresher::OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url):
-	CurlJsonRequest(callback, ecb, url), _parentStorage(parent) {}
+	HttpJsonRequest(callback, ecb, url), _parentStorage(parent) {}
 
 OneDriveTokenRefresher::~OneDriveTokenRefresher() {}
 
@@ -59,7 +59,7 @@ void OneDriveTokenRefresher::tokenRefreshed(const Storage::BoolResponse &respons
 void OneDriveTokenRefresher::finishJson(const Common::JSONValue *json) {
 	if (!json) {
 		//that's probably not an error (200 OK)
-		CurlJsonRequest::finishJson(nullptr);
+		HttpJsonRequest::finishJson(nullptr);
 		return;
 	}
 
@@ -111,7 +111,7 @@ void OneDriveTokenRefresher::finishJson(const Common::JSONValue *json) {
 	}
 
 	//notify user of success
-	CurlJsonRequest::finishJson(json);
+	HttpJsonRequest::finishJson(json);
 }
 
 void OneDriveTokenRefresher::finishError(const Networking::ErrorResponse &error, Networking::RequestState state) {
@@ -147,12 +147,12 @@ void OneDriveTokenRefresher::setHeaders(const Common::Array<Common::String> &hea
 	curl_slist_free_all(_headersList);
 	_headersList = nullptr;
 	for (uint32 i = 0; i < headers.size(); ++i)
-		CurlJsonRequest::addHeader(headers[i]);
+		HttpJsonRequest::addHeader(headers[i]);
 }
 
 void OneDriveTokenRefresher::addHeader(const Common::String &header) {
 	_headers.push_back(header);
-	CurlJsonRequest::addHeader(header);
+	HttpJsonRequest::addHeader(header);
 }
 
 } // End of namespace OneDrive
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.h b/backends/cloud/onedrive/onedrivetokenrefresher.h
index 781b283bff9..b32fd3f5582 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.h
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.h
@@ -23,14 +23,14 @@
 #define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVETOKENREFRESHER_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/httpjsonrequest.h"
 
 namespace Cloud {
 namespace OneDrive {
 
 class OneDriveStorage;
 
-class OneDriveTokenRefresher: public Networking::CurlJsonRequest {
+class OneDriveTokenRefresher: public Networking::HttpJsonRequest {
 	OneDriveStorage *_parentStorage;
 	Common::Array<Common::String> _headers;
 
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.cpp b/backends/cloud/onedrive/onedriveuploadrequest.cpp
index 5546a920f51..49cfc1221a0 100644
--- a/backends/cloud/onedrive/onedriveuploadrequest.cpp
+++ b/backends/cloud/onedrive/onedriveuploadrequest.cpp
@@ -23,9 +23,9 @@
 #include "backends/cloud/onedrive/onedrivestorage.h"
 #include "backends/cloud/iso8601.h"
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/formats/json.h"
 #include "onedrivetokenrefresher.h"
 
@@ -75,7 +75,7 @@ void OneDriveUploadRequest::uploadNextPart() {
 		Common::String url = Common::String::format(ONEDRIVE_API_SPECIAL_APPROOT_UPLOAD, ConnMan.urlEncode(_savePath).c_str()); //folder must exist
 		Networking::JsonCallback callback = new Common::Callback<OneDriveUploadRequest, const Networking::JsonResponse &>(this, &OneDriveUploadRequest::partUploadedCallback);
 		Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveUploadRequest, const Networking::ErrorResponse &>(this, &OneDriveUploadRequest::partUploadedErrorCallback);
-		Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
+		Networking::HttpJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
 		request->addHeader("Authorization: Bearer " + _storage->accessToken());
 		request->setBuffer(new byte[1], 0); //use POST
 		_workingRequest = ConnMan.addRequest(request);
@@ -91,7 +91,7 @@ void OneDriveUploadRequest::uploadNextPart() {
 
 	Networking::JsonCallback callback = new Common::Callback<OneDriveUploadRequest, const Networking::JsonResponse &>(this, &OneDriveUploadRequest::partUploadedCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveUploadRequest, const Networking::ErrorResponse &>(this, &OneDriveUploadRequest::partUploadedErrorCallback);
-	Networking::CurlJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
+	Networking::HttpJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
 	request->addHeader("Authorization: Bearer " + _storage->accessToken());
 	request->usePut();
 
@@ -121,7 +121,7 @@ void OneDriveUploadRequest::partUploadedCallback(const Networking::JsonResponse
 		return;
 
 	Networking::ErrorResponse error(this, false, true, "", -1);
-	const Networking::CurlJsonRequest *rq = (const Networking::CurlJsonRequest *)response.request;
+	const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
@@ -143,10 +143,10 @@ void OneDriveUploadRequest::partUploadedCallback(const Networking::JsonResponse
 			return;
 		}
 
-		if (Networking::CurlJsonRequest::jsonContainsString(object, "id", "OneDriveUploadRequest") &&
-			Networking::CurlJsonRequest::jsonContainsString(object, "name", "OneDriveUploadRequest") &&
-			Networking::CurlJsonRequest::jsonContainsIntegerNumber(object, "size", "OneDriveUploadRequest") &&
-			Networking::CurlJsonRequest::jsonContainsString(object, "lastModifiedDateTime", "OneDriveUploadRequest")) {
+		if (Networking::HttpJsonRequest::jsonContainsString(object, "id", "OneDriveUploadRequest") &&
+			Networking::HttpJsonRequest::jsonContainsString(object, "name", "OneDriveUploadRequest") &&
+			Networking::HttpJsonRequest::jsonContainsIntegerNumber(object, "size", "OneDriveUploadRequest") &&
+			Networking::HttpJsonRequest::jsonContainsString(object, "lastModifiedDateTime", "OneDriveUploadRequest")) {
 			//finished
 			Common::String path = _savePath;
 			uint32 size = object.getVal("size")->asIntegerNumber();
@@ -156,7 +156,7 @@ void OneDriveUploadRequest::partUploadedCallback(const Networking::JsonResponse
 		}
 
 		if (_uploadUrl == "") {
-			if (Networking::CurlJsonRequest::jsonContainsString(object, "uploadUrl", "OneDriveUploadRequest"))
+			if (Networking::HttpJsonRequest::jsonContainsString(object, "uploadUrl", "OneDriveUploadRequest"))
 				_uploadUrl = object.getVal("uploadUrl")->asString();
 		}
 	}
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.h b/backends/cloud/onedrive/onedriveuploadrequest.h
index 74f6e7d95e7..bd82750d4a8 100644
--- a/backends/cloud/onedrive/onedriveuploadrequest.h
+++ b/backends/cloud/onedrive/onedriveuploadrequest.h
@@ -23,8 +23,8 @@
 #define BACKENDS_CLOUD_ONEDRIVE_ONEDRIVEUPLOADREQUEST_H
 
 #include "backends/cloud/storage.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/request.h"
 #include "common/callback.h"
 
 namespace Cloud {
diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index 13fb7a2cc8b..b48354d0ce2 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -23,7 +23,7 @@
 #include "backends/cloud/cloudmanager.h"
 #include "backends/cloud/downloadrequest.h"
 #include "backends/cloud/id/iddownloadrequest.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/httpjsonrequest.h"
 #include "backends/saves/default/default-saves.h"
 #include "common/config-manager.h"
 #include "common/debug.h"
@@ -206,7 +206,7 @@ void SavesSyncRequest::directoryListedErrorCallback(const Networking::ErrorRespo
 				// OneDrive-related error:
 				if (object.contains("error") && object.getVal("error")->isObject()) {
 					Common::JSONObject errorNode = object.getVal("error")->asObject();
-					if (Networking::CurlJsonRequest::jsonContainsString(errorNode, "code", "SavesSyncRequest")) {
+					if (Networking::HttpJsonRequest::jsonContainsString(errorNode, "code", "SavesSyncRequest")) {
 						Common::String code = errorNode.getVal("code")->asString();
 						if (code == "itemNotFound") {
 							irrecoverable = false;
diff --git a/backends/cloud/savessyncrequest.h b/backends/cloud/savessyncrequest.h
index 9c85f11ff9f..575b5623c3a 100644
--- a/backends/cloud/savessyncrequest.h
+++ b/backends/cloud/savessyncrequest.h
@@ -22,7 +22,7 @@
 #ifndef BACKENDS_CLOUD_SAVESSYNCREQUEST_H
 #define BACKENDS_CLOUD_SAVESSYNCREQUEST_H
 
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/request.h"
 #include "backends/cloud/storage.h"
 #include "common/hashmap.h"
 #include "common/hash-str.h"
diff --git a/backends/cloud/storage.cpp b/backends/cloud/storage.cpp
index 50c0c86db37..b31c65cd220 100644
--- a/backends/cloud/storage.cpp
+++ b/backends/cloud/storage.cpp
@@ -23,7 +23,7 @@
 #include "backends/cloud/downloadrequest.h"
 #include "backends/cloud/folderdownloadrequest.h"
 #include "backends/cloud/savessyncrequest.h"
-#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/http/connectionmanager.h"
 #include "common/debug.h"
 #include "common/file.h"
 #include "common/translation.h"
diff --git a/backends/cloud/storage.h b/backends/cloud/storage.h
index 0b8e0d3102b..4c3ade4700e 100644
--- a/backends/cloud/storage.h
+++ b/backends/cloud/storage.h
@@ -24,8 +24,8 @@
 
 #include "backends/cloud/storagefile.h"
 #include "backends/cloud/storageinfo.h"
-#include "backends/networking/curl/request.h"
-#include "backends/networking/curl/curlrequest.h"
+#include "backends/networking/http/request.h"
+#include "backends/networking/http/httprequest.h"
 #include "common/array.h"
 #include "common/callback.h"
 #include "common/mutex.h"
diff --git a/backends/dlc/dlcmanager.h b/backends/dlc/dlcmanager.h
index e169b1c3c54..c52d6e21dff 100644
--- a/backends/dlc/dlcmanager.h
+++ b/backends/dlc/dlcmanager.h
@@ -29,7 +29,7 @@
 
 #include "gui/object.h"
 
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/request.h"
 
 namespace GUI {
 class LauncherDialog;
diff --git a/backends/dlc/scummvmcloud.cpp b/backends/dlc/scummvmcloud.cpp
index 312e638a83f..509d68ef1aa 100644
--- a/backends/dlc/scummvmcloud.cpp
+++ b/backends/dlc/scummvmcloud.cpp
@@ -34,7 +34,7 @@
 
 #include "gui/gui-manager.h"
 
-#include "backends/networking/curl/sessionrequest.h"
+#include "backends/networking/http/sessionrequest.h"
 #include "backends/dlc/scummvmcloud.h"
 #include "backends/dlc/dlcmanager.h"
 #include "backends/dlc/dlcdesc.h"
@@ -54,7 +54,7 @@ void ScummVMCloud::jsonCallbackGetAllDLCs(const Networking::JsonResponse &respon
 	if (result.contains("entries")) {
 		Common::JSONArray items = result.getVal("entries")->asArray();
 		for (uint32 i = 0; i < items.size(); ++i) {
-			if (!Networking::CurlJsonRequest::jsonIsObject(items[i], "ScummVMCloud")) continue;
+			if (!Networking::HttpJsonRequest::jsonIsObject(items[i], "ScummVMCloud")) continue;
 			Common::JSONObject item = items[i]->asObject();
 
 			DLC::DLCDesc *dlc = new DLC::DLCDesc();
@@ -89,7 +89,7 @@ void ScummVMCloud::getAllDLCs() {
 	Common::String url("https://scummvm-dlcs-default-rtdb.firebaseio.com/dlcs.json"); // temporary mock api
 	Networking::JsonCallback callback = new Common::Callback<ScummVMCloud, const Networking::JsonResponse &>(this, &ScummVMCloud::jsonCallbackGetAllDLCs);
 	Networking::ErrorCallback failureCallback = new Common::Callback<ScummVMCloud, const Networking::ErrorResponse &>(this, &ScummVMCloud::errorCallbackGetAllDLCs);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(
+	Networking::HttpJsonRequest *request = new Networking::HttpJsonRequest(
 		callback, failureCallback, url);
 
 	request->execute();
diff --git a/backends/dlc/scummvmcloud.h b/backends/dlc/scummvmcloud.h
index 6a123680900..be3a1dbdf80 100644
--- a/backends/dlc/scummvmcloud.h
+++ b/backends/dlc/scummvmcloud.h
@@ -26,8 +26,8 @@
 #include "common/queue.h"
 
 #include "backends/dlc/store.h"
-#include "backends/networking/curl/request.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/request.h"
+#include "backends/networking/http/httpjsonrequest.h"
 
 namespace Networking {
 class SessionRequest;
diff --git a/backends/module.mk b/backends/module.mk
index a39b5dcc9a0..2d7a3a6f0f6 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -79,16 +79,16 @@ endif
 
 ifdef USE_LIBCURL
 MODULE_OBJS += \
-	networking/curl/connectionmanager.o \
-	networking/curl/networkreadstream.o \
-	networking/curl/curlrequest.o \
-	networking/curl/curljsonrequest.o \
-	networking/curl/postrequest.o \
-	networking/curl/request.o \
-	networking/curl/session.o \
-	networking/curl/sessionrequest.o \
-	networking/curl/socket.o \
-	networking/curl/url.o
+	networking/http/connectionmanager.o \
+	networking/http/networkreadstream.o \
+	networking/http/httprequest.o \
+	networking/http/httpjsonrequest.o \
+	networking/http/postrequest.o \
+	networking/http/request.o \
+	networking/http/session.o \
+	networking/http/sessionrequest.o \
+	networking/http/socket.o \
+	networking/http/url.o
 endif
 
 ifdef EMSCRIPTEN
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/http/connectionmanager.cpp
similarity index 98%
rename from backends/networking/curl/connectionmanager.cpp
rename to backends/networking/http/connectionmanager.cpp
index 4d204cc1995..57169395a66 100644
--- a/backends/networking/curl/connectionmanager.cpp
+++ b/backends/networking/http/connectionmanager.cpp
@@ -22,8 +22,8 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include <curl/curl.h>
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/debug.h"
 #include "common/fs.h"
 #include "common/system.h"
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/http/connectionmanager.h
similarity index 96%
rename from backends/networking/curl/connectionmanager.h
rename to backends/networking/http/connectionmanager.h
index 546d14a0596..2585232411f 100644
--- a/backends/networking/curl/connectionmanager.h
+++ b/backends/networking/http/connectionmanager.h
@@ -19,10 +19,10 @@
  *
  */
 
-#ifndef BACKENDS_NETWORKING_CURL_CONNECTIONMANAGER_H
-#define BACKENDS_NETWORKING_CURL_CONNECTIONMANAGER_H
+#ifndef BACKENDS_NETWORKING_HTTP_CONNECTIONMANAGER_H
+#define BACKENDS_NETWORKING_HTTP_CONNECTIONMANAGER_H
 
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/request.h"
 #include "common/str.h"
 #include "common/singleton.h"
 #include "common/hashmap.h"
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/http/httpjsonrequest.cpp
similarity index 82%
rename from backends/networking/curl/curljsonrequest.cpp
rename to backends/networking/http/httpjsonrequest.cpp
index dad77c25617..e74da6c6a5e 100644
--- a/backends/networking/curl/curljsonrequest.cpp
+++ b/backends/networking/http/httpjsonrequest.cpp
@@ -22,31 +22,31 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include <curl/curl.h>
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/debug.h"
 #include "common/formats/json.h"
 
 namespace Networking {
 
-CurlJsonRequest::CurlJsonRequest(JsonCallback cb, ErrorCallback ecb, const Common::String &url) :
-	CurlRequest(nullptr, ecb, url), _jsonCallback(cb), _contentsStream(DisposeAfterUse::YES),
-	_buffer(new byte[CURL_JSON_REQUEST_BUFFER_SIZE]) {}
+HttpJsonRequest::HttpJsonRequest(JsonCallback cb, ErrorCallback ecb, const Common::String &url) :
+	HttpRequest(nullptr, ecb, url), _jsonCallback(cb), _contentsStream(DisposeAfterUse::YES),
+	_buffer(new byte[HTTP_JSON_REQUEST_BUFFER_SIZE]) {}
 
-CurlJsonRequest::~CurlJsonRequest() {
+HttpJsonRequest::~HttpJsonRequest() {
 	delete _jsonCallback;
 	delete[] _buffer;
 }
 
-void CurlJsonRequest::handle() {
+void HttpJsonRequest::handle() {
 	if (!_stream) _stream = makeStream();
 
 	if (_stream) {
-		uint32 readBytes = _stream->read(_buffer, CURL_JSON_REQUEST_BUFFER_SIZE);
+		uint32 readBytes = _stream->read(_buffer, HTTP_JSON_REQUEST_BUFFER_SIZE);
 		if (readBytes != 0)
 			if (_contentsStream.write(_buffer, readBytes) != readBytes)
-				warning("CurlJsonRequest: unable to write all the bytes into MemoryWriteStreamDynamic");
+				warning("HttpJsonRequest: unable to write all the bytes into MemoryWriteStreamDynamic");
 
 		if (_stream->eos()) {
 			char *contents = Common::JSON::zeroTerminateContents(_contentsStream);
@@ -63,7 +63,7 @@ void CurlJsonRequest::handle() {
 	}
 }
 
-void CurlJsonRequest::restart() {
+void HttpJsonRequest::restart() {
 	if (_stream)
 		delete _stream;
 	_stream = nullptr;
@@ -71,7 +71,7 @@ void CurlJsonRequest::restart() {
 	//with no stream available next handle() will create another one
 }
 
-void CurlJsonRequest::finishJson(const Common::JSONValue *json) {
+void HttpJsonRequest::finishJson(const Common::JSONValue *json) {
 	Request::finishSuccess();
 	if (_jsonCallback)
 		(*_jsonCallback)(JsonResponse(this, json)); //potential memory leak, free it in your callbacks!
@@ -79,7 +79,7 @@ void CurlJsonRequest::finishJson(const Common::JSONValue *json) {
 		delete json;
 }
 
-bool CurlJsonRequest::jsonIsObject(const Common::JSONValue *item, const char *warningPrefix) {
+bool HttpJsonRequest::jsonIsObject(const Common::JSONValue *item, const char *warningPrefix) {
 	if (item == nullptr) {
 		warning("%s: passed item is NULL", warningPrefix);
 		return false;
@@ -92,7 +92,7 @@ bool CurlJsonRequest::jsonIsObject(const Common::JSONValue *item, const char *wa
 	return false;
 }
 
-bool CurlJsonRequest::jsonContainsObject(const Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
+bool HttpJsonRequest::jsonContainsObject(const Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
 	if (!item.contains(key)) {
 		if (isOptional) {
 			return true;
@@ -109,7 +109,7 @@ bool CurlJsonRequest::jsonContainsObject(const Common::JSONObject &item, const c
 	return false;
 }
 
-bool CurlJsonRequest::jsonContainsString(const Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
+bool HttpJsonRequest::jsonContainsString(const Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
 	if (!item.contains(key)) {
 		if (isOptional) {
 			return true;
@@ -126,7 +126,7 @@ bool CurlJsonRequest::jsonContainsString(const Common::JSONObject &item, const c
 	return false;
 }
 
-bool CurlJsonRequest::jsonContainsIntegerNumber(const Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
+bool HttpJsonRequest::jsonContainsIntegerNumber(const Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
 	if (!item.contains(key)) {
 		if (isOptional) {
 			return true;
@@ -143,7 +143,7 @@ bool CurlJsonRequest::jsonContainsIntegerNumber(const Common::JSONObject &item,
 	return false;
 }
 
-bool CurlJsonRequest::jsonContainsArray(const Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
+bool HttpJsonRequest::jsonContainsArray(const Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
 	if (!item.contains(key)) {
 		if (isOptional) {
 			return true;
@@ -160,7 +160,7 @@ bool CurlJsonRequest::jsonContainsArray(const Common::JSONObject &item, const ch
 	return false;
 }
 
-bool CurlJsonRequest::jsonContainsStringOrIntegerNumber(const Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
+bool HttpJsonRequest::jsonContainsStringOrIntegerNumber(const Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
 	if (!item.contains(key)) {
 		if (isOptional) {
 			return true;
@@ -177,7 +177,7 @@ bool CurlJsonRequest::jsonContainsStringOrIntegerNumber(const Common::JSONObject
 	return false;
 }
 
-bool CurlJsonRequest::jsonContainsAttribute(const Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
+bool HttpJsonRequest::jsonContainsAttribute(const Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
 	if (!item.contains(key)) {
 		if (isOptional) {
 			return true;
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/http/httpjsonrequest.h
similarity index 87%
rename from backends/networking/curl/curljsonrequest.h
rename to backends/networking/http/httpjsonrequest.h
index bf9c3c1587e..b4ce4cdec41 100644
--- a/backends/networking/curl/curljsonrequest.h
+++ b/backends/networking/http/httpjsonrequest.h
@@ -19,10 +19,10 @@
  *
  */
 
-#ifndef BACKENDS_NETWORKING_CURL_CURLJSONREQUEST_H
-#define BACKENDS_NETWORKING_CURL_CURLJSONREQUEST_H
+#ifndef BACKENDS_NETWORKING_HTTP_HTTPJSONREQUEST_H
+#define BACKENDS_NETWORKING_HTTP_HTTPJSONREQUEST_H
 
-#include "backends/networking/curl/curlrequest.h"
+#include "backends/networking/http/httprequest.h"
 #include "common/memstream.h"
 #include "common/formats/json.h"
 
@@ -32,9 +32,9 @@ typedef Response<const Common::JSONValue *> JsonResponse;
 typedef Common::BaseCallback<const JsonResponse &> *JsonCallback;
 typedef Common::BaseCallback<const Common::JSONValue *> *JSONValueCallback;
 
-#define CURL_JSON_REQUEST_BUFFER_SIZE 512 * 1024
+#define HTTP_JSON_REQUEST_BUFFER_SIZE 512 * 1024
 
-class CurlJsonRequest: public CurlRequest {
+class HttpJsonRequest: public HttpRequest {
 protected:
 	JsonCallback _jsonCallback;
 	Common::MemoryWriteStreamDynamic _contentsStream;
@@ -44,8 +44,8 @@ protected:
 	virtual void finishJson(const Common::JSONValue *json);
 
 public:
-	CurlJsonRequest(JsonCallback cb, ErrorCallback ecb, const Common::String &url);
-	~CurlJsonRequest() override;
+	HttpJsonRequest(JsonCallback cb, ErrorCallback ecb, const Common::String &url);
+	~HttpJsonRequest() override;
 
 	void handle() override;
 	void restart() override;
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/http/httprequest.cpp
similarity index 70%
rename from backends/networking/curl/curlrequest.cpp
rename to backends/networking/http/httprequest.cpp
index 7c847aee827..4df4fd6e4e8 100644
--- a/backends/networking/curl/curlrequest.cpp
+++ b/backends/networking/http/httprequest.cpp
@@ -22,24 +22,24 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include <curl/curl.h>
-#include "backends/networking/curl/curlrequest.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/httprequest.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/textconsole.h"
 
 namespace Networking {
 
-CurlRequest::CurlRequest(DataCallback cb, ErrorCallback ecb, const Common::String &url):
+HttpRequest::HttpRequest(DataCallback cb, ErrorCallback ecb, const Common::String &url):
 	Request(cb, ecb), _url(url), _stream(nullptr), _headersList(nullptr), _bytesBuffer(nullptr),
 	_bytesBufferSize(0), _uploading(false), _usingPatch(false), _keepAlive(false), _keepAliveIdle(120), _keepAliveInterval(60) {}
 
-CurlRequest::~CurlRequest() {
+HttpRequest::~HttpRequest() {
 	curl_slist_free_all(_headersList);
 	delete _stream;
 	delete[] _bytesBuffer;
 }
 
-NetworkReadStream *CurlRequest::makeStream() {
+NetworkReadStream *HttpRequest::makeStream() {
 	if (_bytesBuffer)
 		return new NetworkReadStream(_url.c_str(), _headersList, _bytesBuffer, _bytesBufferSize, _uploading, _usingPatch, true, _keepAlive, _keepAliveIdle, _keepAliveInterval);
 	if (!_formFields.empty() || !_formFiles.empty())
@@ -47,12 +47,12 @@ NetworkReadStream *CurlRequest::makeStream() {
 	return new NetworkReadStream(_url.c_str(), _headersList, _postFields, _uploading, _usingPatch, _keepAlive, _keepAliveIdle, _keepAliveInterval);
 }
 
-void CurlRequest::handle() {
+void HttpRequest::handle() {
 	if (!_stream) _stream = makeStream();
 
 	if (_stream && _stream->eos()) {
 		if (_stream->httpResponseCode() != 200) {
-			warning("CurlRequest: HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());
+			warning("HttpRequest: HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());
 			ErrorResponse error(this, false, true, "HTTP response code is not 200 OK", _stream->httpResponseCode());
 			finishError(error);
 			return;
@@ -62,14 +62,14 @@ void CurlRequest::handle() {
 	}
 }
 
-void CurlRequest::restart() {
+void HttpRequest::restart() {
 	if (_stream)
 		delete _stream;
 	_stream = nullptr;
 	//with no stream available next handle() will create another one
 }
 
-Common::String CurlRequest::date() const {
+Common::String HttpRequest::date() const {
 	if (_stream) {
 		Common::HashMap<Common::String, Common::String> headers = _stream->responseHeadersMap();
 		if (headers.contains("date"))
@@ -78,23 +78,23 @@ Common::String CurlRequest::date() const {
 	return "";
 }
 
-void CurlRequest::setHeaders(const Common::Array<Common::String> &headers) {
+void HttpRequest::setHeaders(const Common::Array<Common::String> &headers) {
 	curl_slist_free_all(_headersList);
 	_headersList = nullptr;
 	for (uint32 i = 0; i < headers.size(); ++i)
 		addHeader(headers[i]);
 }
 
-void CurlRequest::addHeader(const Common::String &header) {
+void HttpRequest::addHeader(const Common::String &header) {
 	_headersList = curl_slist_append(_headersList, header.c_str());
 }
 
-void CurlRequest::addPostField(const Common::String &keyValuePair) {
+void HttpRequest::addPostField(const Common::String &keyValuePair) {
 	if (_bytesBuffer)
-		warning("CurlRequest: added POST fields would be ignored, because there is buffer present");
+		warning("HttpRequest: added POST fields would be ignored, because there is buffer present");
 
 	if (!_formFields.empty() || !_formFiles.empty())
-		warning("CurlRequest: added POST fields would be ignored, because there are form fields/files present");
+		warning("HttpRequest: added POST fields would be ignored, because there are form fields/files present");
 
 	if (_postFields == "")
 		_postFields = keyValuePair;
@@ -102,29 +102,29 @@ void CurlRequest::addPostField(const Common::String &keyValuePair) {
 		_postFields += "&" + keyValuePair;
 }
 
-void CurlRequest::addFormField(const Common::String &name, const Common::String &value) {
+void HttpRequest::addFormField(const Common::String &name, const Common::String &value) {
 	if (_bytesBuffer)
-		warning("CurlRequest: added POST form fields would be ignored, because there is buffer present");
+		warning("HttpRequest: added POST form fields would be ignored, because there is buffer present");
 
 	if (_formFields.contains(name))
-		warning("CurlRequest: form field '%s' already had a value", name.c_str());
+		warning("HttpRequest: form field '%s' already had a value", name.c_str());
 
 	_formFields[name] = value;
 }
 
-void CurlRequest::addFormFile(const Common::String &name, const Common::Path &filename) {
+void HttpRequest::addFormFile(const Common::String &name, const Common::Path &filename) {
 	if (_bytesBuffer)
-		warning("CurlRequest: added POST form files would be ignored, because there is buffer present");
+		warning("HttpRequest: added POST form files would be ignored, because there is buffer present");
 
 	if (_formFields.contains(name))
-		warning("CurlRequest: form file field '%s' already had a value", name.c_str());
+		warning("HttpRequest: form file field '%s' already had a value", name.c_str());
 
 	_formFiles[name] = filename;
 }
 
-void CurlRequest::setBuffer(byte *buffer, uint32 size) {
+void HttpRequest::setBuffer(byte *buffer, uint32 size) {
 	if (_postFields != "")
-		warning("CurlRequest: added POST fields would be ignored, because buffer added");
+		warning("HttpRequest: added POST fields would be ignored, because buffer added");
 
 	if (_bytesBuffer)
 		delete[] _bytesBuffer;
@@ -133,21 +133,21 @@ void CurlRequest::setBuffer(byte *buffer, uint32 size) {
 	_bytesBufferSize = size;
 }
 
-void CurlRequest::usePut() { _uploading = true; }
+void HttpRequest::usePut() { _uploading = true; }
 
-void CurlRequest::usePatch() { _usingPatch = true; }
+void HttpRequest::usePatch() { _usingPatch = true; }
 
-void CurlRequest::connectionKeepAlive(long idle, long interval) {
+void HttpRequest::connectionKeepAlive(long idle, long interval) {
 	_keepAlive = true;
 	_keepAliveIdle = idle;
 	_keepAliveInterval = interval;
 }
 
-void CurlRequest::connectionClose() {
+void HttpRequest::connectionClose() {
 	_keepAlive = false;
 }
 
-NetworkReadStreamResponse CurlRequest::execute() {
+NetworkReadStreamResponse HttpRequest::execute() {
 	if (!_stream) {
 		_stream = makeStream();
 		ConnMan.addRequest(this);
@@ -156,9 +156,9 @@ NetworkReadStreamResponse CurlRequest::execute() {
 	return NetworkReadStreamResponse(this, _stream);
 }
 
-const NetworkReadStream *CurlRequest::getNetworkReadStream() const { return _stream; }
+const NetworkReadStream *HttpRequest::getNetworkReadStream() const { return _stream; }
 
-void CurlRequest::wait(int spinlockDelay) {
+void HttpRequest::wait(int spinlockDelay) {
 	while (state() == Networking::PROCESSING) {
 		g_system->delayMillis(spinlockDelay);
 	}
diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/http/httprequest.h
similarity index 92%
rename from backends/networking/curl/curlrequest.h
rename to backends/networking/http/httprequest.h
index 577871d9400..c925a539662 100644
--- a/backends/networking/curl/curlrequest.h
+++ b/backends/networking/http/httprequest.h
@@ -19,10 +19,10 @@
  *
  */
 
-#ifndef BACKENDS_NETWORKING_CURL_CURLREQUEST_H
-#define BACKENDS_NETWORKING_CURL_CURLREQUEST_H
+#ifndef BACKENDS_NETWORKING_HTTP_HTTPREQUEST_H
+#define BACKENDS_NETWORKING_HTTP_HTTPREQUEST_H
 
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/request.h"
 #include "common/path.h"
 #include "common/str.h"
 #include "common/array.h"
@@ -38,7 +38,7 @@ class NetworkReadStream;
 typedef Response<NetworkReadStream *> NetworkReadStreamResponse;
 typedef Common::BaseCallback<const NetworkReadStreamResponse &> *NetworkReadStreamCallback;
 
-class CurlRequest: public Request {
+class HttpRequest: public Request {
 protected:
 	Common::String _url;
 	NetworkReadStream *_stream;
@@ -56,8 +56,8 @@ protected:
 	NetworkReadStream *makeStream();
 
 public:
-	CurlRequest(DataCallback cb, ErrorCallback ecb, const Common::String &url);
-	~CurlRequest() override;
+	HttpRequest(DataCallback cb, ErrorCallback ecb, const Common::String &url);
+	~HttpRequest() override;
 
 	void handle() override;
 	void restart() override;
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/http/networkreadstream.cpp
similarity index 99%
rename from backends/networking/curl/networkreadstream.cpp
rename to backends/networking/http/networkreadstream.cpp
index 1315d4addb6..b22aa2c3cad 100644
--- a/backends/networking/curl/networkreadstream.cpp
+++ b/backends/networking/http/networkreadstream.cpp
@@ -23,8 +23,8 @@
 #define CURL_DISABLE_DEPRECATION
 
 #include <curl/curl.h>
-#include "backends/networking/curl/networkreadstream.h"
-#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/http/networkreadstream.h"
+#include "backends/networking/http/connectionmanager.h"
 #include "base/version.h"
 #include "common/debug.h"
 
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/http/networkreadstream.h
similarity index 98%
rename from backends/networking/curl/networkreadstream.h
rename to backends/networking/http/networkreadstream.h
index 3e1d9fb39e5..43cf427210f 100644
--- a/backends/networking/curl/networkreadstream.h
+++ b/backends/networking/http/networkreadstream.h
@@ -19,8 +19,8 @@
  *
  */
 
-#ifndef BACKENDS_NETWORKING_CURL_NETWORKREADSTREAM_H
-#define BACKENDS_NETWORKING_CURL_NETWORKREADSTREAM_H
+#ifndef BACKENDS_NETWORKING_HTTP_NETWORKREADSTREAM_H
+#define BACKENDS_NETWORKING_HTTP_NETWORKREADSTREAM_H
 
 #include "common/memstream.h"
 #include "common/path.h"
diff --git a/backends/networking/curl/postrequest.cpp b/backends/networking/http/postrequest.cpp
similarity index 91%
rename from backends/networking/curl/postrequest.cpp
rename to backends/networking/http/postrequest.cpp
index eec0c276723..1f3ce408a99 100644
--- a/backends/networking/curl/postrequest.cpp
+++ b/backends/networking/http/postrequest.cpp
@@ -19,10 +19,10 @@
  *
  */
 
-#include "backends/networking/curl/postrequest.h"
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/curljsonrequest.h"
-#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/http/postrequest.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/networkreadstream.h"
 #include "common/formats/json.h"
 
 namespace Networking {
@@ -62,7 +62,7 @@ void PostRequest::start() {
 
 	Networking::JsonCallback innerCallback = new Common::Callback<PostRequest, const Networking::JsonResponse &>(this, &PostRequest::responseCallback);
 	Networking::ErrorCallback errorResponseCallback = new Common::Callback<PostRequest, const Networking::ErrorResponse &>(this, &PostRequest::errorCallback);
-	Networking::CurlJsonRequest *request = new Networking::CurlJsonRequest(innerCallback, errorResponseCallback, _url);
+	Networking::HttpJsonRequest *request = new Networking::HttpJsonRequest(innerCallback, errorResponseCallback, _url);
 
 	if (_postData && _jsonData) {
 		warning("Error, both data and JSON present while calling %s", _url.c_str());
@@ -92,7 +92,7 @@ void PostRequest::responseCallback(const Networking::JsonResponse &response) {
 	if (response.request) _date = response.request->date();
 
 	Networking::ErrorResponse error(this, "PostRequest::responseCallback: unknown error");
-	const Networking::CurlJsonRequest *rq = (const Networking::CurlJsonRequest *)response.request;
+	const Networking::HttpJsonRequest *rq = (const Networking::HttpJsonRequest *)response.request;
 	if (rq && rq->getNetworkReadStream())
 		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();
 
diff --git a/backends/networking/curl/postrequest.h b/backends/networking/http/postrequest.h
similarity index 90%
rename from backends/networking/curl/postrequest.h
rename to backends/networking/http/postrequest.h
index 9b97db45daa..1ad0ec179e6 100644
--- a/backends/networking/curl/postrequest.h
+++ b/backends/networking/http/postrequest.h
@@ -19,11 +19,11 @@
  *
  */
 
-#ifndef BACKENDS_NETWORKING_CURL_POSTREQUEST_H
-#define BACKENDS_NETWORKING_CURL_POSTREQUEST_H
+#ifndef BACKENDS_NETWORKING_HTTP_POSTREQUEST_H
+#define BACKENDS_NETWORKING_HTTP_POSTREQUEST_H
 
-#include "backends/networking/curl/request.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/request.h"
+#include "backends/networking/http/httpjsonrequest.h"
 
 namespace Networking {
 
diff --git a/backends/networking/curl/request.cpp b/backends/networking/http/request.cpp
similarity index 97%
rename from backends/networking/curl/request.cpp
rename to backends/networking/http/request.cpp
index ba92d42f8e2..241db60c396 100644
--- a/backends/networking/curl/request.cpp
+++ b/backends/networking/http/request.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/request.h"
 
 namespace Networking {
 
diff --git a/backends/networking/curl/request.h b/backends/networking/http/request.h
similarity index 98%
rename from backends/networking/curl/request.h
rename to backends/networking/http/request.h
index a0393784e48..331c167fff6 100644
--- a/backends/networking/curl/request.h
+++ b/backends/networking/http/request.h
@@ -19,8 +19,8 @@
  *
  */
 
-#ifndef BACKENDS_NETWORKING_CURL_REQUEST_H
-#define BACKENDS_NETWORKING_CURL_REQUEST_H
+#ifndef BACKENDS_NETWORKING_HTTP_REQUEST_H
+#define BACKENDS_NETWORKING_HTTP_REQUEST_H
 
 #include "common/callback.h"
 #include "common/scummsys.h"
diff --git a/backends/networking/curl/session.cpp b/backends/networking/http/session.cpp
similarity index 98%
rename from backends/networking/curl/session.cpp
rename to backends/networking/http/session.cpp
index 39421b6c041..50a1343fbe0 100644
--- a/backends/networking/curl/session.cpp
+++ b/backends/networking/http/session.cpp
@@ -21,7 +21,7 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
-#include "backends/networking/curl/session.h"
+#include "backends/networking/http/session.h"
 
 namespace Networking {
 
diff --git a/backends/networking/curl/session.h b/backends/networking/http/session.h
similarity index 91%
rename from backends/networking/curl/session.h
rename to backends/networking/http/session.h
index 5f5a36393f6..e37284594f7 100644
--- a/backends/networking/curl/session.h
+++ b/backends/networking/http/session.h
@@ -19,10 +19,10 @@
  *
  */
 
-#ifndef BACKENDS_NETWORKING_CURL_SESSION_H
-#define BACKENDS_NETWORKING_CURL_SESSION_H
+#ifndef BACKENDS_NETWORKING_HTTP_SESSION_H
+#define BACKENDS_NETWORKING_HTTP_SESSION_H
 
-#include "backends/networking/curl/sessionrequest.h"
+#include "backends/networking/http/sessionrequest.h"
 
 namespace Networking {
 
diff --git a/backends/networking/curl/sessionrequest.cpp b/backends/networking/http/sessionrequest.cpp
similarity index 94%
rename from backends/networking/curl/sessionrequest.cpp
rename to backends/networking/http/sessionrequest.cpp
index cd449a3da60..8c377cea545 100644
--- a/backends/networking/curl/sessionrequest.cpp
+++ b/backends/networking/http/sessionrequest.cpp
@@ -22,9 +22,9 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
 #include <curl/curl.h>
-#include "backends/networking/curl/connectionmanager.h"
-#include "backends/networking/curl/networkreadstream.h"
-#include "backends/networking/curl/sessionrequest.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/networkreadstream.h"
+#include "backends/networking/http/sessionrequest.h"
 #include "common/debug.h"
 #include "common/file.h"
 #include "common/formats/json.h"
@@ -32,8 +32,8 @@
 namespace Networking {
 
 SessionRequest::SessionRequest(const Common::String &url, const Common::Path &localFile, DataCallback cb, ErrorCallback ecb, bool binary):
-	CurlRequest(cb, ecb, url), _contentsStream(DisposeAfterUse::YES),
-	_buffer(new byte[CURL_SESSION_REQUEST_BUFFER_SIZE]), _text(nullptr), _localFile(nullptr),
+	HttpRequest(cb, ecb, url), _contentsStream(DisposeAfterUse::YES),
+	_buffer(new byte[HTTP_SESSION_REQUEST_BUFFER_SIZE]), _text(nullptr), _localFile(nullptr),
 	_started(false), _complete(false), _success(false), _binary(binary) {
 
 	openLocalFile(localFile);
@@ -99,7 +99,7 @@ char *SessionRequest::getPreparedContents() {
 void SessionRequest::finishError(const ErrorResponse &error, RequestState state) {
 	_complete = true;
 	_success = false;
-	CurlRequest::finishError(error, PAUSED);
+	HttpRequest::finishError(error, PAUSED);
 }
 
 void SessionRequest::finishSuccess() {
@@ -164,7 +164,7 @@ void SessionRequest::handle() {
 			finishError(error);
 			return;
 		}
-		uint32 readBytes = _stream->read(_buffer, CURL_SESSION_REQUEST_BUFFER_SIZE);
+		uint32 readBytes = _stream->read(_buffer, HTTP_SESSION_REQUEST_BUFFER_SIZE);
 		if (readBytes != 0) {
 			if (!_localFile) {
 				if (_contentsStream.write(_buffer, readBytes) != readBytes)
diff --git a/backends/networking/curl/sessionrequest.h b/backends/networking/http/sessionrequest.h
similarity index 91%
rename from backends/networking/curl/sessionrequest.h
rename to backends/networking/http/sessionrequest.h
index 8d0e52d3710..c9de3235643 100644
--- a/backends/networking/curl/sessionrequest.h
+++ b/backends/networking/http/sessionrequest.h
@@ -19,10 +19,10 @@
  *
  */
 
-#ifndef BACKENDS_NETWORKING_CURL_SESSIONREQUEST_H
-#define BACKENDS_NETWORKING_CURL_SESSIONREQUEST_H
+#ifndef BACKENDS_NETWORKING_HTTP_SESSIONREQUEST_H
+#define BACKENDS_NETWORKING_HTTP_SESSIONREQUEST_H
 
-#include "backends/networking/curl/curlrequest.h"
+#include "backends/networking/http/httprequest.h"
 #include "common/memstream.h"
 
 namespace Common {
@@ -33,7 +33,7 @@ class Path;
 
 namespace Networking {
 
-#define CURL_SESSION_REQUEST_BUFFER_SIZE 512 * 1024
+#define HTTP_SESSION_REQUEST_BUFFER_SIZE 512 * 1024
 
 struct SessionFileResponse {
 	byte *buffer;
@@ -47,7 +47,7 @@ struct SessionFileResponse {
  * @return Returns SessionFileResponse in the callback
  */
 
-class SessionRequest: public CurlRequest {
+class SessionRequest: public HttpRequest {
 protected:
 	Common::MemoryWriteStreamDynamic _contentsStream;
 	byte *_buffer;
diff --git a/backends/networking/curl/socket.cpp b/backends/networking/http/socket.cpp
similarity index 98%
rename from backends/networking/curl/socket.cpp
rename to backends/networking/http/socket.cpp
index ce5809af46f..67bb24bc69a 100644
--- a/backends/networking/curl/socket.cpp
+++ b/backends/networking/http/socket.cpp
@@ -21,8 +21,8 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 #include <curl/curl.h>
-#include "backends/networking/curl/socket.h"
-#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/http/socket.h"
+#include "backends/networking/http/connectionmanager.h"
 #include "common/debug.h"
 #include "common/system.h"
 
diff --git a/backends/networking/curl/socket.h b/backends/networking/http/socket.h
similarity index 94%
rename from backends/networking/curl/socket.h
rename to backends/networking/http/socket.h
index 3f355787430..8c50f23c8b5 100644
--- a/backends/networking/curl/socket.h
+++ b/backends/networking/http/socket.h
@@ -18,8 +18,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-#ifndef BACKENDS_NETWORKING_CURL_SOCKET_H
-#define BACKENDS_NETWORKING_CURL_SOCKET_H
+#ifndef BACKENDS_NETWORKING_HTTP_SOCKET_H
+#define BACKENDS_NETWORKING_HTTP_SOCKET_H
 
 typedef void CURL;
 #ifdef WIN32
diff --git a/backends/networking/curl/url.cpp b/backends/networking/http/url.cpp
similarity index 98%
rename from backends/networking/curl/url.cpp
rename to backends/networking/http/url.cpp
index 646fd709f40..b360f9f4a14 100644
--- a/backends/networking/curl/url.cpp
+++ b/backends/networking/http/url.cpp
@@ -21,7 +21,7 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 #include <curl/curl.h>
-#include "backends/networking/curl/url.h"
+#include "backends/networking/http/url.h"
 #include "common/debug.h"
 #include "common/textconsole.h"
 
diff --git a/backends/networking/curl/url.h b/backends/networking/http/url.h
similarity index 96%
rename from backends/networking/curl/url.h
rename to backends/networking/http/url.h
index abd2c6d56b3..b1af0fd56ac 100644
--- a/backends/networking/curl/url.h
+++ b/backends/networking/http/url.h
@@ -18,8 +18,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-#ifndef BACKENDS_NETWORKING_CURL_URL_H
-#define BACKENDS_NETWORKING_CURL_URL_H
+#ifndef BACKENDS_NETWORKING_HTTP_URL_H
+#define BACKENDS_NETWORKING_HTTP_URL_H
 
 typedef struct Curl_URL CURLU;
 
diff --git a/backends/networking/sdl_net/handlers/connectcloudhandler.cpp b/backends/networking/sdl_net/handlers/connectcloudhandler.cpp
index 1c161f5a515..35e797fc14e 100644
--- a/backends/networking/sdl_net/handlers/connectcloudhandler.cpp
+++ b/backends/networking/sdl_net/handlers/connectcloudhandler.cpp
@@ -22,7 +22,7 @@
 #include "backends/networking/sdl_net/handlers/connectcloudhandler.h"
 #include "backends/fs/fs-factory.h"
 #include "backends/cloud/cloudmanager.h"
-#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/http/httpjsonrequest.h"
 #include "backends/networking/sdl_net/getclienthandler.h"
 #include "backends/networking/sdl_net/handlerutils.h"
 #include "backends/networking/sdl_net/localwebserver.h"
diff --git a/backends/networking/sdl_net/handlers/connectcloudhandler.h b/backends/networking/sdl_net/handlers/connectcloudhandler.h
index 9e9aa55ac1c..84da9993703 100644
--- a/backends/networking/sdl_net/handlers/connectcloudhandler.h
+++ b/backends/networking/sdl_net/handlers/connectcloudhandler.h
@@ -24,7 +24,7 @@
 
 #include "backends/networking/sdl_net/handlers/basehandler.h"
 #include "backends/networking/sdl_net/client.h"
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/request.h"
 #include "common/memstream.h"
 
 namespace Networking {
diff --git a/base/main.cpp b/base/main.cpp
index 0203da3ad8a..62fc1848175 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -74,7 +74,7 @@
 
 #ifdef USE_CLOUD
 #include "backends/cloud/cloudmanager.h"
-#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/http/connectionmanager.h"
 #ifdef USE_SDL_NET
 #include "backends/networking/sdl_net/localwebserver.h"
 #endif
diff --git a/engines/scumm/he/net/net_lobby.h b/engines/scumm/he/net/net_lobby.h
index 81105f0893c..4748e7beac9 100644
--- a/engines/scumm/he/net/net_lobby.h
+++ b/engines/scumm/he/net/net_lobby.h
@@ -22,8 +22,8 @@
 #ifndef SCUMM_HE_NET_LOBBY_H
 #define SCUMM_HE_NET_LOBBY_H
 
-#include "backends/networking/curl/socket.h"
-#include "backends/networking/curl/url.h"
+#include "backends/networking/http/socket.h"
+#include "backends/networking/http/url.h"
 #include "common/formats/json.h"
 
 #include "scumm/he/net/net_main.h"
diff --git a/gui/cloudconnectionwizard.h b/gui/cloudconnectionwizard.h
index 8cef2be5994..06961f9a449 100644
--- a/gui/cloudconnectionwizard.h
+++ b/gui/cloudconnectionwizard.h
@@ -22,7 +22,7 @@
 #ifndef GUI_CLOUDCONNECTIONWIZARD_H
 #define GUI_CLOUDCONNECTIONWIZARD_H
 
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/request.h"
 #include "common/str.h"
 #include "common/ustr.h"
 #include "gui/dialog.h"
diff --git a/gui/downloadpacksdialog.cpp b/gui/downloadpacksdialog.cpp
index ed02282c5b6..de92782c2f3 100644
--- a/gui/downloadpacksdialog.cpp
+++ b/gui/downloadpacksdialog.cpp
@@ -19,10 +19,10 @@
  *
  */
 
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/request.h"
 #include "gui/downloadpacksdialog.h"
 #include "gui/downloaddialog.h"
-#include "backends/networking/curl/session.h"
+#include "backends/networking/http/session.h"
 #include "common/config-manager.h"
 #include "common/translation.h"
 #include "common/util.h"
diff --git a/gui/integrity-dialog.h b/gui/integrity-dialog.h
index c2b3b0987f5..ee40cc9e932 100644
--- a/gui/integrity-dialog.h
+++ b/gui/integrity-dialog.h
@@ -22,7 +22,7 @@
 #ifndef GUI_INTEGRITY_DIALOG_H
 #define GUI_INTEGRITY_DIALOG_H
 
-#include "backends/networking/curl/postrequest.h"
+#include "backends/networking/http/postrequest.h"
 
 #include "common/array.h"
 #include "common/formats/json.h"
diff --git a/gui/remotebrowser.cpp b/gui/remotebrowser.cpp
index c0221f01caf..4ebd52d4faa 100644
--- a/gui/remotebrowser.cpp
+++ b/gui/remotebrowser.cpp
@@ -28,7 +28,7 @@
 #include "common/algorithm.h"
 
 #include "common/translation.h"
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/request.h"
 #include "backends/cloud/storage.h"
 #include "backends/cloud/cloudmanager.h"
 #include "gui/message.h"
diff --git a/gui/remotebrowser.h b/gui/remotebrowser.h
index 10248d0f21a..841ccd4bc2f 100644
--- a/gui/remotebrowser.h
+++ b/gui/remotebrowser.h
@@ -25,7 +25,7 @@
 #include "gui/dialog.h"
 #include "common/fs.h"
 #include "backends/cloud/storagefile.h"
-#include "backends/networking/curl/request.h"
+#include "backends/networking/http/request.h"
 #include "backends/cloud/storage.h"
 
 namespace GUI {
diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp
index 5612f6241db..07060ee82eb 100644
--- a/gui/saveload-dialog.cpp
+++ b/gui/saveload-dialog.cpp
@@ -24,7 +24,7 @@
 #ifdef USE_CLOUD
 #include "backends/cloud/cloudmanager.h"
 #include "backends/cloud/savessyncrequest.h"
-#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/http/connectionmanager.h"
 #endif
 
 #include "common/translation.h"


Commit: 4c3a3bdaff8b024bea62b0842ba5d1a612c8540c
    https://github.com/scummvm/scummvm/commit/4c3a3bdaff8b024bea62b0842ba5d1a612c8540c
Author: Christian Kündig (christian at kuendig.info)
Date: 2025-08-13T18:38:54+02:00

Commit Message:
CLOUD: Remove direct use of 'curl_slist' and instead use Common::Array<String>

Changed paths:
    backends/cloud/box/boxstorage.cpp
    backends/cloud/box/boxtokenrefresher.cpp
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxtokenrefresher.cpp
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledrivetokenrefresher.cpp
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivetokenrefresher.cpp
    backends/networking/http/httpjsonrequest.cpp
    backends/networking/http/httprequest.cpp
    backends/networking/http/httprequest.h
    backends/networking/http/networkreadstream.cpp
    backends/networking/http/networkreadstream.h
    backends/networking/http/sessionrequest.cpp


diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index d16dbf27648..c826e0f8246 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -21,7 +21,6 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
-#include <curl/curl.h>
 #include "backends/cloud/box/boxstorage.h"
 #include "backends/cloud/box/boxlistdirectorybyidrequest.h"
 #include "backends/cloud/box/boxtokenrefresher.h"
@@ -197,7 +196,8 @@ Networking::Request *BoxStorage::streamFileById(const Common::String &id, Networ
 	if (callback) {
 		Common::String url = Common::String::format(BOX_API_FILES_CONTENT, id.c_str());
 		Common::String header = "Authorization: Bearer " + _token;
-		curl_slist *headersList = curl_slist_append(nullptr, header.c_str());
+		Networking::RequestHeaders *headersList = new Networking::RequestHeaders();
+		headersList->push_back(header);
 		Networking::NetworkReadStream *stream = new Networking::NetworkReadStream(url.c_str(), headersList, "");
 		(*callback)(Networking::NetworkReadStreamResponse(nullptr, stream));
 	}
diff --git a/backends/cloud/box/boxtokenrefresher.cpp b/backends/cloud/box/boxtokenrefresher.cpp
index f3fcc9c5a71..4c7a0e5ead5 100644
--- a/backends/cloud/box/boxtokenrefresher.cpp
+++ b/backends/cloud/box/boxtokenrefresher.cpp
@@ -21,7 +21,6 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
-#include <curl/curl.h>
 #include "backends/cloud/box/boxtokenrefresher.h"
 #include "backends/cloud/box/boxstorage.h"
 #include "backends/networking/http/networkreadstream.h"
@@ -122,10 +121,7 @@ void BoxTokenRefresher::finishError(const Networking::ErrorResponse &error, Netw
 
 void BoxTokenRefresher::setHeaders(const Common::Array<Common::String> &headers) {
 	_headers = headers;
-	curl_slist_free_all(_headersList);
-	_headersList = nullptr;
-	for (uint32 i = 0; i < headers.size(); ++i)
-		HttpJsonRequest::addHeader(headers[i]);
+	HttpJsonRequest::setHeaders(headers);
 }
 
 void BoxTokenRefresher::addHeader(const Common::String  &header) {
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index 118449525e6..f1837d4d37f 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -21,7 +21,6 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
-#include <curl/curl.h>
 #include "backends/cloud/dropbox/dropboxstorage.h"
 #include "backends/cloud/dropbox/dropboxcreatedirectoryrequest.h"
 #include "backends/cloud/dropbox/dropboxinforequest.h"
diff --git a/backends/cloud/dropbox/dropboxtokenrefresher.cpp b/backends/cloud/dropbox/dropboxtokenrefresher.cpp
index bfa5a2f541b..6475ba4cd6d 100644
--- a/backends/cloud/dropbox/dropboxtokenrefresher.cpp
+++ b/backends/cloud/dropbox/dropboxtokenrefresher.cpp
@@ -21,7 +21,6 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
-#include <curl/curl.h>
 #include "backends/cloud/dropbox/dropboxtokenrefresher.h"
 #include "backends/cloud/dropbox/dropboxstorage.h"
 #include "backends/networking/http/networkreadstream.h"
@@ -109,10 +108,8 @@ void DropboxTokenRefresher::finishError(const Networking::ErrorResponse &error,
 
 void DropboxTokenRefresher::setHeaders(const Common::Array<Common::String> &headers) {
 	_headers = headers;
-	curl_slist_free_all(_headersList);
-	_headersList = nullptr;
-	for (uint32 i = 0; i < headers.size(); ++i)
-		HttpJsonRequest::addHeader(headers[i]);
+	// Clear existing headers and add new ones
+	HttpRequest::setHeaders(headers);
 }
 
 void DropboxTokenRefresher::addHeader(const Common::String &header) {
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 67ee1620675..1437688859e 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -21,7 +21,6 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
-#include <curl/curl.h>
 #include "backends/cloud/googledrive/googledrivestorage.h"
 #include "backends/cloud/cloudmanager.h"
 #include "backends/cloud/googledrive/googledrivetokenrefresher.h"
@@ -168,7 +167,8 @@ Networking::Request *GoogleDriveStorage::streamFileById(const Common::String &id
 	if (callback) {
 		Common::String url = Common::String::format(GOOGLEDRIVE_API_FILES_ALT_MEDIA, ConnMan.urlEncode(id).c_str());
 		Common::String header = "Authorization: Bearer " + _token;
-		curl_slist *headersList = curl_slist_append(nullptr, header.c_str());
+		Networking::RequestHeaders *headersList = new Networking::RequestHeaders();
+		headersList->push_back(header);
 		Networking::NetworkReadStream *stream = new Networking::NetworkReadStream(url.c_str(), headersList, "");
 		(*callback)(Networking::NetworkReadStreamResponse(nullptr, stream));
 	}
diff --git a/backends/cloud/googledrive/googledrivetokenrefresher.cpp b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
index 95e51b23143..de3b6629844 100644
--- a/backends/cloud/googledrive/googledrivetokenrefresher.cpp
+++ b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
@@ -21,7 +21,6 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
-#include <curl/curl.h>
 #include "backends/cloud/googledrive/googledrivetokenrefresher.h"
 #include "backends/cloud/googledrive/googledrivestorage.h"
 #include "backends/networking/http/networkreadstream.h"
@@ -110,10 +109,7 @@ void GoogleDriveTokenRefresher::finishJson(const Common::JSONValue *json) {
 
 void GoogleDriveTokenRefresher::setHeaders(const Common::Array<Common::String> &headers) {
 	_headers = headers;
-	curl_slist_free_all(_headersList);
-	_headersList = nullptr;
-	for (uint32 i = 0; i < headers.size(); ++i)
-		HttpJsonRequest::addHeader(headers[i]);
+	HttpJsonRequest::setHeaders(headers);
 }
 
 void GoogleDriveTokenRefresher::addHeader(const Common::String &header) {
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index b7879299df0..5c6fe9da92b 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -21,7 +21,6 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
-#include <curl/curl.h>
 #include "backends/cloud/onedrive/onedrivestorage.h"
 #include "backends/cloud/cloudmanager.h"
 #include "backends/cloud/onedrive/onedrivecreatedirectoryrequest.h"
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index b905769ec49..95e8e6aaef4 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -21,7 +21,6 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
-#include <curl/curl.h>
 #include "backends/cloud/onedrive/onedrivetokenrefresher.h"
 #include "backends/cloud/onedrive/onedrivestorage.h"
 #include "backends/networking/http/networkreadstream.h"
@@ -144,10 +143,7 @@ void OneDriveTokenRefresher::finishErrorIrrecoverable(const Networking::ErrorRes
 
 void OneDriveTokenRefresher::setHeaders(const Common::Array<Common::String> &headers) {
 	_headers = headers;
-	curl_slist_free_all(_headersList);
-	_headersList = nullptr;
-	for (uint32 i = 0; i < headers.size(); ++i)
-		HttpJsonRequest::addHeader(headers[i]);
+	HttpJsonRequest::setHeaders(headers);
 }
 
 void OneDriveTokenRefresher::addHeader(const Common::String &header) {
diff --git a/backends/networking/http/httpjsonrequest.cpp b/backends/networking/http/httpjsonrequest.cpp
index e74da6c6a5e..35a5c483085 100644
--- a/backends/networking/http/httpjsonrequest.cpp
+++ b/backends/networking/http/httpjsonrequest.cpp
@@ -21,7 +21,6 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
-#include <curl/curl.h>
 #include "backends/networking/http/httpjsonrequest.h"
 #include "backends/networking/http/connectionmanager.h"
 #include "backends/networking/http/networkreadstream.h"
diff --git a/backends/networking/http/httprequest.cpp b/backends/networking/http/httprequest.cpp
index 4df4fd6e4e8..e3161822770 100644
--- a/backends/networking/http/httprequest.cpp
+++ b/backends/networking/http/httprequest.cpp
@@ -21,7 +21,6 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
-#include <curl/curl.h>
 #include "backends/networking/http/httprequest.h"
 #include "backends/networking/http/connectionmanager.h"
 #include "backends/networking/http/networkreadstream.h"
@@ -30,21 +29,20 @@
 namespace Networking {
 
 HttpRequest::HttpRequest(DataCallback cb, ErrorCallback ecb, const Common::String &url):
-	Request(cb, ecb), _url(url), _stream(nullptr), _headersList(nullptr), _bytesBuffer(nullptr),
+	Request(cb, ecb), _url(url), _stream(nullptr), _bytesBuffer(nullptr),
 	_bytesBufferSize(0), _uploading(false), _usingPatch(false), _keepAlive(false), _keepAliveIdle(120), _keepAliveInterval(60) {}
 
 HttpRequest::~HttpRequest() {
-	curl_slist_free_all(_headersList);
 	delete _stream;
 	delete[] _bytesBuffer;
 }
 
 NetworkReadStream *HttpRequest::makeStream() {
 	if (_bytesBuffer)
-		return new NetworkReadStream(_url.c_str(), _headersList, _bytesBuffer, _bytesBufferSize, _uploading, _usingPatch, true, _keepAlive, _keepAliveIdle, _keepAliveInterval);
+		return new NetworkReadStream(_url.c_str(), &_headersList, _bytesBuffer, _bytesBufferSize, _uploading, _usingPatch, true, _keepAlive, _keepAliveIdle, _keepAliveInterval);
 	if (!_formFields.empty() || !_formFiles.empty())
-		return new NetworkReadStream(_url.c_str(), _headersList, _formFields, _formFiles, _keepAlive, _keepAliveIdle, _keepAliveInterval);
-	return new NetworkReadStream(_url.c_str(), _headersList, _postFields, _uploading, _usingPatch, _keepAlive, _keepAliveIdle, _keepAliveInterval);
+		return new NetworkReadStream(_url.c_str(), &_headersList, _formFields, _formFiles, _keepAlive, _keepAliveIdle, _keepAliveInterval);
+	return new NetworkReadStream(_url.c_str(), &_headersList, _postFields, _uploading, _usingPatch, _keepAlive, _keepAliveIdle, _keepAliveInterval);
 }
 
 void HttpRequest::handle() {
@@ -79,14 +77,13 @@ Common::String HttpRequest::date() const {
 }
 
 void HttpRequest::setHeaders(const Common::Array<Common::String> &headers) {
-	curl_slist_free_all(_headersList);
-	_headersList = nullptr;
+	_headersList.clear();
 	for (uint32 i = 0; i < headers.size(); ++i)
 		addHeader(headers[i]);
 }
 
 void HttpRequest::addHeader(const Common::String &header) {
-	_headersList = curl_slist_append(_headersList, header.c_str());
+	_headersList.push_back(header);
 }
 
 void HttpRequest::addPostField(const Common::String &keyValuePair) {
diff --git a/backends/networking/http/httprequest.h b/backends/networking/http/httprequest.h
index c925a539662..aeb506e03a3 100644
--- a/backends/networking/http/httprequest.h
+++ b/backends/networking/http/httprequest.h
@@ -26,13 +26,14 @@
 #include "common/path.h"
 #include "common/str.h"
 #include "common/array.h"
+#include "common/list.h"
 #include "common/hashmap.h"
 #include "common/hash-str.h"
 
-struct curl_slist;
-
 namespace Networking {
 
+typedef Common::Array<Common::String> RequestHeaders;
+
 class NetworkReadStream;
 
 typedef Response<NetworkReadStream *> NetworkReadStreamResponse;
@@ -42,7 +43,7 @@ class HttpRequest: public Request {
 protected:
 	Common::String _url;
 	NetworkReadStream *_stream;
-	curl_slist *_headersList;
+	RequestHeaders _headersList;
 	Common::String _postFields;
 	Common::HashMap<Common::String, Common::String> _formFields;
 	Common::HashMap<Common::String, Common::Path> _formFiles;
diff --git a/backends/networking/http/networkreadstream.cpp b/backends/networking/http/networkreadstream.cpp
index b22aa2c3cad..61801c091ea 100644
--- a/backends/networking/http/networkreadstream.cpp
+++ b/backends/networking/http/networkreadstream.cpp
@@ -71,12 +71,27 @@ void NetworkReadStream::resetStream() {
 	_sendingContentsSize = _sendingContentsPos = 0;
 	_progressDownloaded = _progressTotal = 0;
 	_bufferCopy = nullptr;
+	if (_headersSlist) {
+		curl_slist_free_all(_headersSlist);
+		_headersSlist = nullptr;
+	}
+}
+
+curl_slist *NetworkReadStream::requestHeadersToSlist(const RequestHeaders *headersList) {
+	curl_slist *slist = nullptr;
+	if (headersList) {
+		for (const Common::String &header : *headersList) {
+			slist = curl_slist_append(slist, header.c_str());
+		}
+	}
+	return slist;
 }
 
-void NetworkReadStream::initCurl(const char *url, curl_slist *headersList) {
+void NetworkReadStream::initCurl(const char *url, RequestHeaders *headersList) {
 	resetStream();
 
 	_easy = curl_easy_init();
+	_headersSlist = requestHeadersToSlist(headersList);
 	curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
 	curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); //so callback can call us
 	curl_easy_setopt(_easy, CURLOPT_PRIVATE, this); //so ConnectionManager can call us when request is complete
@@ -87,7 +102,7 @@ void NetworkReadStream::initCurl(const char *url, curl_slist *headersList) {
 	curl_easy_setopt(_easy, CURLOPT_ERRORBUFFER, _errorBuffer);
 	curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
 	curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
-	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
+	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, _headersSlist);
 	curl_easy_setopt(_easy, CURLOPT_USERAGENT, gScummVMFullVersion);
 	curl_easy_setopt(_easy, CURLOPT_NOPROGRESS, 0L);
 	curl_easy_setopt(_easy, CURLOPT_PROGRESSFUNCTION, curlProgressCallbackOlder);
@@ -118,7 +133,7 @@ void NetworkReadStream::initCurl(const char *url, curl_slist *headersList) {
 #endif
 }
 
-bool NetworkReadStream::reuseCurl(const char *url, curl_slist *headersList) {
+bool NetworkReadStream::reuseCurl(const char *url, RequestHeaders *headersList) {
 	if (!_keepAlive) {
 		warning("NetworkReadStream: Can't reuse curl handle (was not setup as keep-alive)");
 		return false;
@@ -126,8 +141,9 @@ bool NetworkReadStream::reuseCurl(const char *url, curl_slist *headersList) {
 
 	resetStream();
 
+	_headersSlist = requestHeadersToSlist(headersList);
 	curl_easy_setopt(_easy, CURLOPT_URL, url);
-	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
+	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, _headersSlist);
 	curl_easy_setopt(_easy, CURLOPT_USERAGENT, gScummVMFullVersion); // in case headersList rewrites it
 
 	return true;
@@ -193,25 +209,31 @@ void NetworkReadStream::setupFormMultipart(const Common::HashMap<Common::String,
 	ConnMan.registerEasyHandle(_easy);
 }
 
-NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, const Common::String &postFields, bool uploading, bool usingPatch, bool keepAlive, long keepAliveIdle, long keepAliveInterval):
-		_backingStream(DisposeAfterUse::YES), _keepAlive(keepAlive), _keepAliveIdle(keepAliveIdle), _keepAliveInterval(keepAliveInterval), _errorBuffer(nullptr), _errorCode(CURLE_OK) {
+NetworkReadStream::NetworkReadStream(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading, bool usingPatch, bool keepAlive, long keepAliveIdle, long keepAliveInterval):
+		_backingStream(DisposeAfterUse::YES), _keepAlive(keepAlive), _keepAliveIdle(keepAliveIdle), _keepAliveInterval(keepAliveInterval), _errorBuffer(nullptr), _errorCode(CURLE_OK)
+		, _headersSlist(nullptr)
+	{
 	initCurl(url, headersList);
 	setupBufferContents((const byte *)postFields.c_str(), postFields.size(), uploading, usingPatch, false);
 }
 
-NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles, bool keepAlive, long keepAliveIdle, long keepAliveInterval):
-		_backingStream(DisposeAfterUse::YES), _keepAlive(keepAlive), _keepAliveIdle(keepAliveIdle), _keepAliveInterval(keepAliveInterval), _errorBuffer(nullptr), _errorCode(CURLE_OK) {
+NetworkReadStream::NetworkReadStream(const char *url, RequestHeaders *headersList, const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles, bool keepAlive, long keepAliveIdle, long keepAliveInterval):
+		_backingStream(DisposeAfterUse::YES), _keepAlive(keepAlive), _keepAliveIdle(keepAliveIdle), _keepAliveInterval(keepAliveInterval), _errorBuffer(nullptr), _errorCode(CURLE_OK)
+		, _headersSlist(nullptr)
+	{
 	initCurl(url, headersList);
 	setupFormMultipart(formFields, formFiles);
 }
 
-NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post, bool keepAlive, long keepAliveIdle, long keepAliveInterval):
-		_backingStream(DisposeAfterUse::YES), _keepAlive(keepAlive), _keepAliveIdle(keepAliveIdle), _keepAliveInterval(keepAliveInterval), _errorBuffer(nullptr), _errorCode(CURLE_OK) {
+NetworkReadStream::NetworkReadStream(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post, bool keepAlive, long keepAliveIdle, long keepAliveInterval):
+		_backingStream(DisposeAfterUse::YES), _keepAlive(keepAlive), _keepAliveIdle(keepAliveIdle), _keepAliveInterval(keepAliveInterval), _errorBuffer(nullptr), _errorCode(CURLE_OK)
+		, _headersSlist(nullptr)
+	{
 	initCurl(url, headersList);
 	setupBufferContents(buffer, bufferSize, uploading, usingPatch, post);
 }
 
-bool NetworkReadStream::reuse(const char *url, curl_slist *headersList, const Common::String &postFields, bool uploading, bool usingPatch) {
+bool NetworkReadStream::reuse(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading, bool usingPatch) {
 	if (!reuseCurl(url, headersList))
 		return false;
 
@@ -220,7 +242,7 @@ bool NetworkReadStream::reuse(const char *url, curl_slist *headersList, const Co
 	return true;
 }
 
-bool NetworkReadStream::reuse(const char *url, curl_slist *headersList, const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles) {
+bool NetworkReadStream::reuse(const char *url, RequestHeaders *headersList, const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles) {
 	if (!reuseCurl(url, headersList))
 		return false;
 
@@ -229,7 +251,7 @@ bool NetworkReadStream::reuse(const char *url, curl_slist *headersList, const Co
 	return true;
 }
 
-bool NetworkReadStream::reuse(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
+bool NetworkReadStream::reuse(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
 	if (!reuseCurl(url, headersList))
 		return false;
 
diff --git a/backends/networking/http/networkreadstream.h b/backends/networking/http/networkreadstream.h
index 43cf427210f..cd410d18add 100644
--- a/backends/networking/http/networkreadstream.h
+++ b/backends/networking/http/networkreadstream.h
@@ -30,12 +30,13 @@
 #include "common/hash-str.h"
 
 typedef void CURL;
-struct curl_slist;
 
 namespace Networking {
+typedef Common::Array<Common::String> RequestHeaders;
 
 class NetworkReadStream : public Common::ReadStream {
 	CURL *_easy;
+	struct curl_slist *_headersSlist;  // Track the curl headers list for cleanup
 	Common::MemoryReadWriteStream _backingStream;
 	bool _keepAlive;
 	long _keepAliveIdle, _keepAliveInterval;
@@ -50,8 +51,9 @@ class NetworkReadStream : public Common::ReadStream {
 	uint64 _progressDownloaded, _progressTotal;
 
 	void resetStream();
-	void initCurl(const char *url, curl_slist *headersList);
-	bool reuseCurl(const char *url, curl_slist *headersList);
+	static struct curl_slist *requestHeadersToSlist(const RequestHeaders *headersList);
+	void initCurl(const char *url, RequestHeaders *headersList);
+	bool reuseCurl(const char *url, RequestHeaders *headersList);
 	void setupBufferContents(const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post);
 	void setupFormMultipart(const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles);
 
@@ -77,26 +79,26 @@ class NetworkReadStream : public Common::ReadStream {
 	static int curlProgressCallbackOlder(void *p, double dltotal, double dlnow, double ultotal, double ulnow);
 public:
 	/** Send <postFields>, using POST by default. */
-	NetworkReadStream(const char *url, curl_slist *headersList, const Common::String &postFields, bool uploading = false, bool usingPatch = false, bool keepAlive = false, long keepAliveIdle = 120, long keepAliveInterval = 60);
+	NetworkReadStream(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading = false, bool usingPatch = false, bool keepAlive = false, long keepAliveIdle = 120, long keepAliveInterval = 60);
 	/** Send <formFields>, <formFiles>, using POST multipart/form. */
 	NetworkReadStream(
-	    const char *url, curl_slist *headersList,
+	    const char *url, RequestHeaders *headersList,
 	    const Common::HashMap<Common::String, Common::String> &formFields,
 	    const Common::HashMap<Common::String, Common::Path> &formFiles,
 		bool keepAlive = false, long keepAliveIdle = 120, long keepAliveInterval = 60);
 	/** Send <buffer>, using POST by default. */
-	NetworkReadStream(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading = false, bool usingPatch = false, bool post = true, bool keepAlive = false, long keepAliveIdle = 120, long keepAliveInterval = 60);
+	NetworkReadStream(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading = false, bool usingPatch = false, bool post = true, bool keepAlive = false, long keepAliveIdle = 120, long keepAliveInterval = 60);
 	~NetworkReadStream() override;
 
 	/** Send <postFields>, using POST by default. */
-	bool reuse(const char *url, curl_slist *headersList, const Common::String &postFields, bool uploading = false, bool usingPatch = false);
+	bool reuse(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading = false, bool usingPatch = false);
 	/** Send <formFields>, <formFiles>, using POST multipart/form. */
 	bool reuse(
-		const char *url, curl_slist *headersList,
+		const char *url, RequestHeaders *headersList,
 		const Common::HashMap<Common::String, Common::String> &formFields,
 		const Common::HashMap<Common::String, Common::Path> &formFiles);
 	/** Send <buffer>, using POST by default. */
-	bool reuse(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading = false, bool usingPatch = false, bool post = true);
+	bool reuse(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading = false, bool usingPatch = false, bool post = true);
 
 	/**
 	 * Returns true if a read failed because the stream end has been reached.
diff --git a/backends/networking/http/sessionrequest.cpp b/backends/networking/http/sessionrequest.cpp
index 8c377cea545..a259a7c3c04 100644
--- a/backends/networking/http/sessionrequest.cpp
+++ b/backends/networking/http/sessionrequest.cpp
@@ -21,7 +21,6 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
-#include <curl/curl.h>
 #include "backends/networking/http/connectionmanager.h"
 #include "backends/networking/http/networkreadstream.h"
 #include "backends/networking/http/sessionrequest.h"
@@ -73,12 +72,12 @@ bool SessionRequest::reuseStream() {
 	}
 
 	if (_bytesBuffer)
-		return _stream->reuse(_url.c_str(), _headersList, _bytesBuffer, _bytesBufferSize, _uploading, _usingPatch, true);
+		return _stream->reuse(_url.c_str(), &_headersList, _bytesBuffer, _bytesBufferSize, _uploading, _usingPatch, true);
 
 	if (!_formFields.empty() || !_formFiles.empty())
-		return _stream->reuse(_url.c_str(), _headersList, _formFields, _formFiles);
+		return _stream->reuse(_url.c_str(), &_headersList, _formFields, _formFiles);
 
-	return _stream->reuse(_url.c_str(), _headersList, _postFields, _uploading, _usingPatch);
+	return _stream->reuse(_url.c_str(), &_headersList, _postFields, _uploading, _usingPatch);
 }
 
 char *SessionRequest::getPreparedContents() {


Commit: 629150b6e9b9a569e5c36c809765bbd8a0cf90c5
    https://github.com/scummvm/scummvm/commit/629150b6e9b9a569e5c36c809765bbd8a0cf90c5
Author: Christian Kündig (christian at kuendig.info)
Date: 2025-08-13T18:38:54+02:00

Commit Message:
EMSCRIPTEN: CLOUD: Implement cloud support in Emscripten

Changed paths:
  A backends/networking/http/curl/networkreadstream-curl.cpp
  A backends/networking/http/curl/socket.cpp
  A backends/networking/http/curl/socket.h
  A backends/networking/http/curl/url.cpp
  A backends/networking/http/curl/url.h
  A backends/networking/http/emscripten/networkreadstream-emscripten.cpp
  R backends/networking/http/networkreadstream.cpp
  R backends/networking/http/socket.cpp
  R backends/networking/http/socket.h
  R backends/networking/http/url.cpp
  R backends/networking/http/url.h
    backends/module.mk
    backends/networking/http/connectionmanager.cpp
    backends/networking/http/connectionmanager.h
    backends/networking/http/networkreadstream.h
    backends/platform/sdl/emscripten/emscripten.cpp
    backends/platform/sdl/emscripten/emscripten.h
    base/version.cpp
    configure
    dists/emscripten/README.md
    engines/scumm/he/net/net_lobby.h
    gui/cloudconnectionwizard.cpp
    gui/cloudconnectionwizard.h


diff --git a/backends/module.mk b/backends/module.mk
index 2d7a3a6f0f6..bf21d2c6484 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -80,15 +80,15 @@ endif
 ifdef USE_LIBCURL
 MODULE_OBJS += \
 	networking/http/connectionmanager.o \
-	networking/http/networkreadstream.o \
-	networking/http/httprequest.o \
+	networking/http/curl/networkreadstream-curl.o \
+	networking/http/curl/socket.o \
+	networking/http/curl/url.o \
 	networking/http/httpjsonrequest.o \
+	networking/http/httprequest.o \
 	networking/http/postrequest.o \
 	networking/http/request.o \
 	networking/http/session.o \
-	networking/http/sessionrequest.o \
-	networking/http/socket.o \
-	networking/http/url.o
+	networking/http/sessionrequest.o 
 endif
 
 ifdef EMSCRIPTEN
@@ -97,6 +97,17 @@ MODULE_OBJS += \
 	fs/emscripten/emscripten-posix-fs.o \
 	fs/emscripten/http-fs.o \
 	midi/webmidi.o 
+ifdef USE_CLOUD
+MODULE_OBJS += \
+	networking/http/connectionmanager.o \
+	networking/http/httpjsonrequest.o \
+	networking/http/httprequest.o \
+	networking/http/postrequest.o \
+	networking/http/request.o \
+	networking/http/session.o \
+	networking/http/sessionrequest.o \
+	networking/http/emscripten/networkreadstream-emscripten.o 
+endif
 ifdef USE_TTS
 MODULE_OBJS += \
 	text-to-speech/emscripten/emscripten-text-to-speech.o
diff --git a/backends/networking/http/connectionmanager.cpp b/backends/networking/http/connectionmanager.cpp
index 57169395a66..a1b08bc2cb6 100644
--- a/backends/networking/http/connectionmanager.cpp
+++ b/backends/networking/http/connectionmanager.cpp
@@ -21,7 +21,6 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
-#include <curl/curl.h>
 #include "backends/networking/http/connectionmanager.h"
 #include "backends/networking/http/networkreadstream.h"
 #include "common/debug.h"
@@ -29,6 +28,12 @@
 #include "common/system.h"
 #include "common/timer.h"
 
+#if defined(USE_LIBCURL)
+#include <curl/curl.h>
+#endif
+#if defined(EMSCRIPTEN)
+#include <emscripten.h>
+#endif
 #if defined(ANDROID_BACKEND)
 #include "backends/platform/android/jni-android.h"
 #endif
@@ -42,8 +47,10 @@ DECLARE_SINGLETON(Networking::ConnectionManager);
 namespace Networking {
 
 ConnectionManager::ConnectionManager(): _multi(nullptr), _timerStarted(false), _frame(0) {
+#ifdef USE_LIBCURL
 	curl_global_init(CURL_GLOBAL_ALL);
 	_multi = curl_multi_init();
+#endif
 }
 
 ConnectionManager::~ConnectionManager() {
@@ -81,15 +88,19 @@ ConnectionManager::~ConnectionManager() {
 	_requests.clear();
 
 	//cleanup
+#ifdef USE_LIBCURL
 	curl_multi_cleanup(_multi);
 	curl_global_cleanup();
 	_multi = nullptr;
+#endif
 	_handleMutex.unlock();
 }
 
+#ifdef USE_LIBCURL
 void ConnectionManager::registerEasyHandle(CURL *easy) const {
 	curl_multi_add_handle(_multi, easy);
 }
+#endif
 
 Request *ConnectionManager::addRequest(Request *request, RequestCallback callback) {
 	_addedRequestsMutex.lock();
@@ -101,6 +112,9 @@ Request *ConnectionManager::addRequest(Request *request, RequestCallback callbac
 }
 
 Common::String ConnectionManager::urlEncode(const Common::String &s) const {
+	Common::String result = "";
+	debug(5, "ConnectionManager::urlEncode(%s)", s.c_str());
+#ifdef USE_LIBCURL
 	if (!_multi)
 		return "";
 #if LIBCURL_VERSION_NUM >= 0x070F04
@@ -109,11 +123,22 @@ Common::String ConnectionManager::urlEncode(const Common::String &s) const {
 	char *output = curl_escape(s.c_str(), s.size());
 #endif
 	if (output) {
-		Common::String result = output;
+		result = output;
 		curl_free(output);
-		return result;
 	}
-	return "";
+#elif defined(EMSCRIPTEN)
+	const char *input = s.c_str();
+	result = (char*) EM_ASM_PTR({
+		var jsString = encodeURIComponent(UTF8ToString($0));
+		var size = lengthBytesUTF8(jsString) + 1;
+		var ret = Module._malloc(size);
+		stringToUTF8Array(jsString, HEAP8, ret, size);
+		return ret;
+	}, input);
+#endif
+	debug(5, "ConnectionManager::urlEncode(%s) = %s", s.c_str(), result.c_str());
+		
+	return result;
 }
 
 uint32 ConnectionManager::getCloudRequestsPeriodInMicroseconds() {
@@ -227,6 +252,7 @@ void ConnectionManager::interateRequests() {
 }
 
 void ConnectionManager::processTransfers() {
+#ifdef USE_LIBCURL
 	if (!_multi) return;
 
 	//check libcurl's transfers and notify requests of messages from queue (transfer completion or failure)
@@ -250,6 +276,7 @@ void ConnectionManager::processTransfers() {
 			warning("Unknown libcurl message type %d", curlMsg->msg);
 		}
 	}
+#endif 
 }
 
 } // End of namespace Cloud
diff --git a/backends/networking/http/connectionmanager.h b/backends/networking/http/connectionmanager.h
index 2585232411f..ff99baed116 100644
--- a/backends/networking/http/connectionmanager.h
+++ b/backends/networking/http/connectionmanager.h
@@ -91,12 +91,14 @@ public:
 	ConnectionManager();
 	~ConnectionManager() override;
 
+#ifdef USE_LIBCURL
 	/**
 	 * All libcurl transfers are going through this ConnectionManager.
 	 * So, if you want to start any libcurl transfer, you must create
 	 * an easy handle and register it using this method.
 	 */
 	void registerEasyHandle(CURL *easy) const;
+#endif
 
 	/**
 	 * Use this method to add new Request into manager's queue.
diff --git a/backends/networking/http/networkreadstream.cpp b/backends/networking/http/curl/networkreadstream-curl.cpp
similarity index 100%
rename from backends/networking/http/networkreadstream.cpp
rename to backends/networking/http/curl/networkreadstream-curl.cpp
diff --git a/backends/networking/http/socket.cpp b/backends/networking/http/curl/socket.cpp
similarity index 99%
rename from backends/networking/http/socket.cpp
rename to backends/networking/http/curl/socket.cpp
index 67bb24bc69a..dc7f4cdfd72 100644
--- a/backends/networking/http/socket.cpp
+++ b/backends/networking/http/curl/socket.cpp
@@ -21,8 +21,8 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 #include <curl/curl.h>
-#include "backends/networking/http/socket.h"
 #include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/curl/socket.h"
 #include "common/debug.h"
 #include "common/system.h"
 
diff --git a/backends/networking/http/socket.h b/backends/networking/http/curl/socket.h
similarity index 94%
rename from backends/networking/http/socket.h
rename to backends/networking/http/curl/socket.h
index 8c50f23c8b5..3f355787430 100644
--- a/backends/networking/http/socket.h
+++ b/backends/networking/http/curl/socket.h
@@ -18,8 +18,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-#ifndef BACKENDS_NETWORKING_HTTP_SOCKET_H
-#define BACKENDS_NETWORKING_HTTP_SOCKET_H
+#ifndef BACKENDS_NETWORKING_CURL_SOCKET_H
+#define BACKENDS_NETWORKING_CURL_SOCKET_H
 
 typedef void CURL;
 #ifdef WIN32
diff --git a/backends/networking/http/url.cpp b/backends/networking/http/curl/url.cpp
similarity index 98%
rename from backends/networking/http/url.cpp
rename to backends/networking/http/curl/url.cpp
index b360f9f4a14..ab84381a91e 100644
--- a/backends/networking/http/url.cpp
+++ b/backends/networking/http/curl/url.cpp
@@ -21,7 +21,7 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 #include <curl/curl.h>
-#include "backends/networking/http/url.h"
+#include "backends/networking/http/curl/url.h"
 #include "common/debug.h"
 #include "common/textconsole.h"
 
diff --git a/backends/networking/http/url.h b/backends/networking/http/curl/url.h
similarity index 96%
rename from backends/networking/http/url.h
rename to backends/networking/http/curl/url.h
index b1af0fd56ac..abd2c6d56b3 100644
--- a/backends/networking/http/url.h
+++ b/backends/networking/http/curl/url.h
@@ -18,8 +18,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-#ifndef BACKENDS_NETWORKING_HTTP_URL_H
-#define BACKENDS_NETWORKING_HTTP_URL_H
+#ifndef BACKENDS_NETWORKING_CURL_URL_H
+#define BACKENDS_NETWORKING_CURL_URL_H
 
 typedef struct Curl_URL CURLU;
 
diff --git a/backends/networking/http/emscripten/networkreadstream-emscripten.cpp b/backends/networking/http/emscripten/networkreadstream-emscripten.cpp
new file mode 100644
index 00000000000..6d4f7216ced
--- /dev/null
+++ b/backends/networking/http/emscripten/networkreadstream-emscripten.cpp
@@ -0,0 +1,349 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#define FORBIDDEN_SYMBOL_EXCEPTION_asctime
+#define FORBIDDEN_SYMBOL_EXCEPTION_clock
+#define FORBIDDEN_SYMBOL_EXCEPTION_ctime
+#define FORBIDDEN_SYMBOL_EXCEPTION_difftime
+#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
+#define FORBIDDEN_SYMBOL_EXCEPTION_getdate
+#define FORBIDDEN_SYMBOL_EXCEPTION_gmtime
+#define FORBIDDEN_SYMBOL_EXCEPTION_localtime
+#define FORBIDDEN_SYMBOL_EXCEPTION_mktime
+#define FORBIDDEN_SYMBOL_EXCEPTION_strcpy
+#define FORBIDDEN_SYMBOL_EXCEPTION_strdup
+#define FORBIDDEN_SYMBOL_EXCEPTION_time
+#include <emscripten.h>
+#include <emscripten/fetch.h>
+
+#include "backends/networking/http/networkreadstream.h"
+#include "base/version.h"
+#include "common/debug.h"
+
+#define CURLE_OK 0x0  // the only CURLcode value used/checked in ScummVM
+
+namespace Networking {
+
+void NetworkReadStream::emscriptenOnReadyStateChange(emscripten_fetch_t *fetch) {
+	if (fetch->readyState != 2)
+		return;
+
+	size_t headersLengthBytes = emscripten_fetch_get_response_headers_length(fetch) + 1;
+	char *headerString = (char *)malloc(headersLengthBytes);
+
+	assert(headerString);
+	emscripten_fetch_get_response_headers(fetch, headerString, headersLengthBytes);
+	NetworkReadStream *stream = (NetworkReadStream *)fetch->userData;
+	stream->addResponseHeaders(headerString, headersLengthBytes);
+	free(headerString);
+}
+
+void NetworkReadStream::emscriptenOnProgress(emscripten_fetch_t *fetch) {
+	/*
+	if (fetch->totalBytes) {
+		debug(5,"Downloading %s.. %.2f percent complete.", fetch->url, fetch->dataOffset * 100.0 / fetch->totalBytes);
+	} else {
+		debug(5,"Downloading %s.. %lld bytes complete.", fetch->url, fetch->dataOffset + fetch->numBytes);
+	}
+	debug(5,"Downloading %s.. %.2f %s complete. HTTP readyState: %hu. HTTP status: %hu - "
+			"HTTP statusText: %s. Received chunk [%llu, %llu]",
+			fetch->url,
+			fetch->totalBytes > 0 ? (fetch->dataOffset + fetch->numBytes) * 100.0 / fetch->totalBytes : (fetch->dataOffset + fetch->numBytes),
+			fetch->totalBytes > 0 ? "percent" : " bytes",
+			fetch->readyState,
+			fetch->status,
+			fetch->statusText,
+			fetch->dataOffset,
+			fetch->dataOffset + fetch->numBytes);
+	*/
+	NetworkReadStream *stream = (NetworkReadStream *)fetch->userData;
+	if (stream) {
+		stream->setProgress(fetch->dataOffset, fetch->totalBytes);
+	}
+}
+
+void NetworkReadStream::emscriptenOnSuccess(emscripten_fetch_t *fetch) {
+	NetworkReadStream *stream = (NetworkReadStream *)fetch->userData;
+	stream->emscriptenDownloadFinished(true);
+}
+
+void NetworkReadStream::emscriptenOnError(emscripten_fetch_t *fetch) {
+	NetworkReadStream *stream = (NetworkReadStream *)fetch->userData;
+	stream->emscriptenDownloadFinished(false);
+}
+
+void NetworkReadStream::emscriptenDownloadFinished(bool success) {
+
+	if (success) {
+		debug(5, "NetworkReadStream::emscriptenHandleDownload Finished downloading %llu bytes from URL %s. HTTP status code: %d", _emscripten_fetch->numBytes, _emscripten_fetch->url, _emscripten_fetch->status);
+	} else {
+		debug(5, "NetworkReadStream::emscriptenHandleDownload Downloading %s failed, HTTP failure status code: %d, status text: %s", _emscripten_fetch->url, _emscripten_fetch->status, _emscripten_fetch->statusText);
+	}
+	if (_emscripten_fetch->numBytes > 0) {
+		// TODO: This could be done continuously during emscriptenOnProgress?
+		this->_backingStream.write(_emscripten_fetch->data, _emscripten_fetch->numBytes); 
+	}
+	this->setProgress(_emscripten_fetch->numBytes, _emscripten_fetch->numBytes);
+	if (success) {
+		this->finished(CURLE_OK); // TODO: actually pass the result code from emscripten_fetch
+
+	} else {
+		this->finished(-1);
+	}
+}
+
+void NetworkReadStream::resetStream() {
+	_eos = _requestComplete = false;
+	_sendingContentsSize = _sendingContentsPos = 0;
+	_progressDownloaded = _progressTotal = 0;
+	_bufferCopy = nullptr;
+	_emscripten_fetch = nullptr;
+	_emscripten_request_headers = nullptr;
+}
+
+void NetworkReadStream::initEmscripten(const char *url, RequestHeaders *headersList) {
+
+	resetStream();
+	emscripten_fetch_attr_init(_emscripten_fetch_attr);
+
+	// convert header list
+	// first get size of list
+	int size = 0;
+	if (headersList) {
+		size = headersList->size();
+		debug(5, "_emscripten_request_headers count: %d", size);
+	}
+	_emscripten_request_headers = new char *[size * 2 + 1];
+	_emscripten_request_headers[size * 2] = 0; // header array needs to be null-terminated.
+
+	int i = 0;
+	if (headersList) {
+		for (const Common::String &header : *headersList) {
+			// Find the colon separator
+			uint colonPos = header.findFirstOf(':');
+			if (colonPos == Common::String::npos) {
+				warning("NetworkReadStream: Malformed header (no colon): %s", header.c_str());
+				continue;
+			}
+			
+			// Split into key and value parts
+			Common::String key = header.substr(0, colonPos);
+			Common::String value = header.substr(colonPos + 1);
+			
+			// Trim whitespace from key and value
+			key.trim();
+			value.trim();
+						
+			// Store key and value as separate strings
+			_emscripten_request_headers[i++] = strdup(key.c_str());
+			_emscripten_request_headers[i++] = strdup(value.c_str());
+			debug(9, "_emscripten_request_headers key='%s' value='%s'", key.c_str(), value.c_str());
+		}
+	}
+
+	_emscripten_fetch_attr->requestHeaders = _emscripten_request_headers;
+	strcpy(_emscripten_fetch_attr->requestMethod, "GET"); // todo: move down to setup buffer contents
+	_emscripten_fetch_attr->attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
+	_emscripten_fetch_attr->onerror = emscriptenOnError;
+	_emscripten_fetch_attr->onprogress = emscriptenOnProgress;
+	_emscripten_fetch_attr->onreadystatechange = emscriptenOnReadyStateChange;
+	_emscripten_fetch_attr->onsuccess = emscriptenOnSuccess;
+	_emscripten_fetch_attr->userData = this;
+}
+void NetworkReadStream::setupBufferContents(const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
+	if (uploading) {
+		strcpy(_emscripten_fetch_attr->requestMethod, "PUT");
+		_emscripten_fetch_attr->requestDataSize = bufferSize;
+		_emscripten_fetch_attr->requestData = (const char *)buffer;
+	} else if (usingPatch) {
+		strcpy(_emscripten_fetch_attr->requestMethod, "PATCH");
+	} else {
+		if (post || bufferSize != 0) {
+			strcpy(_emscripten_fetch_attr->requestMethod, "POST");
+			_emscripten_fetch_attr->requestDataSize = bufferSize;
+			_emscripten_fetch_attr->requestData = (const char *)buffer;
+		}
+	}
+	debug(5, "NetworkReadStream::setupBufferContents uploading %s usingPatch %s post %s ->method %s", uploading ? "true" : "false", usingPatch ? "true" : "false", post ? "true" : "false", _emscripten_fetch_attr->requestMethod);
+	_emscripten_fetch = emscripten_fetch(_emscripten_fetch_attr, _emscripten_fetch_url);
+}
+
+void NetworkReadStream::setupFormMultipart(const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles) {
+	// set POST multipart upload form fields/files
+	error("NetworkReadStream::setupFormMultipart not implemented");
+}
+
+/** Send <postFields>, using POST by default. */
+NetworkReadStream::NetworkReadStream(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading, bool usingPatch, bool keepAlive, long keepAliveIdle, long keepAliveInterval) : _emscripten_fetch_attr(new emscripten_fetch_attr_t()), _emscripten_fetch_url(url), _backingStream(DisposeAfterUse::YES), _keepAlive(keepAlive), _errorBuffer(nullptr), _errorCode(CURLE_OK) {
+	initEmscripten(url, headersList);
+	setupBufferContents((const byte *)postFields.c_str(), postFields.size(), uploading, usingPatch, false);
+}
+/** Send <formFields>, <formFiles>, using POST multipart/form. */
+NetworkReadStream::NetworkReadStream(const char *url, RequestHeaders *headersList, const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles, bool keepAlive, long keepAliveIdle, long keepAliveInterval) : _emscripten_fetch_attr(new emscripten_fetch_attr_t()), _emscripten_fetch_url(url), _backingStream(DisposeAfterUse::YES), _keepAlive(keepAlive), _errorBuffer(nullptr), _errorCode(CURLE_OK) {
+	initEmscripten(url, headersList);
+	setupFormMultipart(formFields, formFiles);
+}
+
+/** Send <buffer>, using POST by default. */
+NetworkReadStream::NetworkReadStream(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post, bool keepAlive, long keepAliveIdle, long keepAliveInterval) : _emscripten_fetch_attr(new emscripten_fetch_attr_t()), _emscripten_fetch_url(url), _backingStream(DisposeAfterUse::YES), _keepAlive(keepAlive), _errorBuffer(nullptr), _errorCode(CURLE_OK) {
+	initEmscripten(url, headersList);
+	setupBufferContents(buffer, bufferSize, uploading, usingPatch, post);
+}
+
+bool NetworkReadStream::reuse(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading, bool usingPatch) {
+	return false; // Not implemented for Emscripten
+}
+
+bool NetworkReadStream::reuse(const char *url, RequestHeaders *headersList, const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles) {
+	return false; // Not implemented for Emscripten
+}
+
+bool NetworkReadStream::reuse(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
+	return false; // Not implemented for Emscripten
+}
+
+NetworkReadStream::~NetworkReadStream() {
+	if (_emscripten_fetch) {
+		debug(5, "~NetworkReadStream: emscripten_fetch_close");
+		emscripten_fetch_close(_emscripten_fetch);
+	}
+	
+	// Free the headers array and its contents
+	if (_emscripten_request_headers) {
+		for (int i = 0; _emscripten_request_headers[i] != nullptr; ++i) {
+			free(_emscripten_request_headers[i]);  // Free each strdup'd string
+		}
+		delete[] _emscripten_request_headers;
+	}
+	
+	free(_bufferCopy);
+	free(_errorBuffer);
+}
+
+bool NetworkReadStream::eos() const {
+	return _eos;
+}
+
+uint32 NetworkReadStream::read(void *dataPtr, uint32 dataSize) {
+	uint32 actuallyRead = _backingStream.read(dataPtr, dataSize);
+
+	// Only access _emscripten_fetch->url if _emscripten_fetch is valid
+	// debug(5,"NetworkReadStream::read %u %s %s %s", actuallyRead, _eos ? "_eos" : "not _eos", _requestComplete ? "_requestComplete" : "_request not Complete", _emscripten_fetch ? _emscripten_fetch->url : "no-url");
+	if (actuallyRead == 0) {
+		if (_requestComplete)
+			_eos = true;
+		return 0;
+	}
+
+	return actuallyRead;
+}
+
+void NetworkReadStream::finished(uint32 errorCode) {
+	_requestComplete = true;
+	_errorCode = errorCode;
+
+	if (_errorCode == CURLE_OK) {
+		debug(9, "NetworkReadStream::finished %s - Request succeeded", _emscripten_fetch_url);
+	} else {
+		// Make a copy of the error message since _emscripten_fetch might be cleaned up
+		if (_emscripten_fetch && _emscripten_fetch->statusText) {
+			_errorBuffer = strdup(_emscripten_fetch->statusText);
+		} else {
+			_errorBuffer = strdup("Unknown error");
+		}
+		warning("NetworkReadStream::finished %s - Request failed (%d - %s)", _emscripten_fetch_url, _errorCode, getError());
+	}
+}
+
+bool NetworkReadStream::hasError() const {
+	return _errorCode != CURLE_OK;
+}
+
+const char *NetworkReadStream::getError() const {
+	return _errorBuffer;
+}
+
+long NetworkReadStream::httpResponseCode() const {
+	// return 200;
+	unsigned short responseCode = 0;
+	if (_emscripten_fetch)
+		responseCode = _emscripten_fetch->status;
+	debug(5, "NetworkReadStream::httpResponseCode %hu %hu ", _emscripten_fetch->status, responseCode);
+	return responseCode;
+}
+
+Common::String NetworkReadStream::currentLocation() const {
+	debug(5, "NetworkReadStream::currentLocation %s", _emscripten_fetch_url);
+	return Common::String(_emscripten_fetch_url);
+}
+
+Common::String NetworkReadStream::responseHeaders() const {
+	return _responseHeaders;
+}
+
+Common::HashMap<Common::String, Common::String> NetworkReadStream::responseHeadersMap() const {
+
+	Common::HashMap<Common::String, Common::String> headers;
+
+	const char *headerString = responseHeaders().c_str();
+	char **responseHeaders = emscripten_fetch_unpack_response_headers(headerString);
+	assert(responseHeaders);
+
+	int numHeaders = 0;
+	for (; responseHeaders[numHeaders * 2]; ++numHeaders) {
+		// Check both the header and its value are present.
+		assert(responseHeaders[(numHeaders * 2) + 1]);
+		headers[responseHeaders[numHeaders * 2]] = responseHeaders[(numHeaders * 2) + 1];
+	}
+
+	emscripten_fetch_free_unpacked_response_headers(responseHeaders);
+
+	return headers;
+}
+
+uint32 NetworkReadStream::fillWithSendingContents(char *bufferToFill, uint32 maxSize) {
+	uint32 sendSize = _sendingContentsSize - _sendingContentsPos;
+	if (sendSize > maxSize)
+		sendSize = maxSize;
+	for (uint32 i = 0; i < sendSize; ++i) {
+		bufferToFill[i] = _sendingContentsBuffer[_sendingContentsPos + i];
+	}
+	_sendingContentsPos += sendSize;
+	return sendSize;
+}
+
+uint32 NetworkReadStream::addResponseHeaders(char *buffer, uint32 bufferSize) {
+	_responseHeaders += Common::String(buffer, bufferSize);
+	return bufferSize;
+}
+
+double NetworkReadStream::getProgress() const {
+	if (_progressTotal < 1)
+		return 0;
+	return (double)_progressDownloaded / (double)_progressTotal;
+}
+
+void NetworkReadStream::setProgress(uint64 downloaded, uint64 total) {
+	_progressDownloaded = downloaded;
+	_progressTotal = total;
+}
+
+} // namespace Networking
diff --git a/backends/networking/http/networkreadstream.h b/backends/networking/http/networkreadstream.h
index cd410d18add..e59edc633b7 100644
--- a/backends/networking/http/networkreadstream.h
+++ b/backends/networking/http/networkreadstream.h
@@ -27,16 +27,31 @@
 #include "common/stream.h"
 #include "common/str.h"
 #include "common/hashmap.h"
+#include "common/array.h"
 #include "common/hash-str.h"
 
+#ifdef USE_LIBCURL
 typedef void CURL;
+#endif
+#ifdef EMSCRIPTEN
+struct emscripten_fetch_attr_t;
+struct emscripten_fetch_t;
+#endif
 
 namespace Networking {
 typedef Common::Array<Common::String> RequestHeaders;
-
+	
 class NetworkReadStream : public Common::ReadStream {
+#ifdef USE_LIBCURL
 	CURL *_easy;
 	struct curl_slist *_headersSlist;  // Track the curl headers list for cleanup
+#endif
+#ifdef EMSCRIPTEN
+	emscripten_fetch_attr_t *_emscripten_fetch_attr;
+	emscripten_fetch_t *_emscripten_fetch;
+	const char *_emscripten_fetch_url = nullptr;
+	char **_emscripten_request_headers;
+#endif
 	Common::MemoryReadWriteStream _backingStream;
 	bool _keepAlive;
 	long _keepAliveIdle, _keepAliveInterval;
@@ -51,9 +66,14 @@ class NetworkReadStream : public Common::ReadStream {
 	uint64 _progressDownloaded, _progressTotal;
 
 	void resetStream();
+#ifdef USE_LIBCURL
 	static struct curl_slist *requestHeadersToSlist(const RequestHeaders *headersList);
 	void initCurl(const char *url, RequestHeaders *headersList);
 	bool reuseCurl(const char *url, RequestHeaders *headersList);
+#endif
+#ifdef EMSCRIPTEN
+	void initEmscripten(const char *url, RequestHeaders *headersList);
+#endif
 	void setupBufferContents(const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post);
 	void setupFormMultipart(const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles);
 
@@ -73,10 +93,19 @@ class NetworkReadStream : public Common::ReadStream {
 	*/
 	uint32 addResponseHeaders(char *buffer, uint32 bufferSize);
 
+#ifdef USE_LIBCURL
 	static size_t curlDataCallback(char *d, size_t n, size_t l, void *p);
 	static size_t curlReadDataCallback(char *d, size_t n, size_t l, void *p);
 	static size_t curlHeadersCallback(char *d, size_t n, size_t l, void *p);
 	static int curlProgressCallbackOlder(void *p, double dltotal, double dlnow, double ultotal, double ulnow);
+#endif 
+#ifdef EMSCRIPTEN
+	static void emscriptenOnSuccess(emscripten_fetch_t *fetch);
+	static void emscriptenOnError(emscripten_fetch_t *fetch);
+	static void emscriptenOnProgress(emscripten_fetch_t *fetch);
+	static void emscriptenOnReadyStateChange(emscripten_fetch_t *fetch);
+	void emscriptenDownloadFinished(bool success);
+#endif
 public:
 	/** Send <postFields>, using POST by default. */
 	NetworkReadStream(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading = false, bool usingPatch = false, bool keepAlive = false, long keepAliveIdle = 120, long keepAliveInterval = 60);
diff --git a/backends/platform/sdl/emscripten/emscripten.cpp b/backends/platform/sdl/emscripten/emscripten.cpp
index 7e590e9df39..03ecd52c3ab 100644
--- a/backends/platform/sdl/emscripten/emscripten.cpp
+++ b/backends/platform/sdl/emscripten/emscripten.cpp
@@ -71,6 +71,35 @@ EM_JS(void, downloadFile, (const char *filenamePtr, char *dataPtr, int dataSize)
 	}, 0);
 });
 
+#ifdef USE_CLOUD
+/* Listener to feed the activation JSON from the wizard at cloud.scummvm.org back 
+ * Usage: Run the following on the final page of the activation flow:
+ * 		  window.opener.postMessage(document.getElementById("json").value,"*")
+ */
+EM_JS(bool, cloud_connection_open_oauth_window, (char const *url), {
+	oauth_window = window.open(UTF8ToString(url));
+	window.addEventListener("message", (event) => {
+		Module._cloud_connection_json_callback(stringToNewUTF8( JSON.stringify(event.data)));
+		oauth_window.close()
+	}, {once : true});
+	return true;
+});
+#endif
+
+extern "C" {
+#ifdef USE_CLOUD
+void EMSCRIPTEN_KEEPALIVE cloud_connection_json_callback(char *str) {
+	warning("cloud_connection_callback: %s", str);
+	OSystem_Emscripten *emscripten_g_system = dynamic_cast<OSystem_Emscripten *>(g_system);
+	if (emscripten_g_system->_cloudConnectionCallback) {
+		(*emscripten_g_system->_cloudConnectionCallback)(new Common::String(str));
+	} else {
+		warning("No Storage Connection Callback Registered!");
+	}
+}
+#endif
+}
+
 // Overridden functions
 
 void OSystem_Emscripten::initBackend() {
@@ -219,4 +248,14 @@ void OSystem_Emscripten::delayMillis(uint msecs) {
 	} while (pause > 0);
 	lastThreshold = threshold;
 }
+
+#ifdef USE_CLOUD
+bool OSystem_Emscripten::openUrl(const Common::String &url) {
+	if(url.hasPrefix("https://cloud.scummvm.org/")){
+		return cloud_connection_open_oauth_window(url.c_str());
+	}
+	return	OSystem_SDL::openUrl(url);
+}
+#endif
+
 #endif
diff --git a/backends/platform/sdl/emscripten/emscripten.h b/backends/platform/sdl/emscripten/emscripten.h
index fd44427148e..ecd1c54107c 100644
--- a/backends/platform/sdl/emscripten/emscripten.h
+++ b/backends/platform/sdl/emscripten/emscripten.h
@@ -23,8 +23,24 @@
 #define PLATFORM_SDL_EMSCRIPTEN_H
 
 #include "backends/platform/sdl/posix/posix.h"
+#ifdef USE_CLOUD
+#include "backends/networking/http/request.h"
+#include "common/ustr.h"
+typedef Common::BaseCallback<const Common::String *> *CloudConnectionCallback;
+#endif
 
+extern "C" {
+void cloud_connection_json_callback(char *str);       // pass cloud storage activation data from JS to setup wizard
+}
 class OSystem_Emscripten : public OSystem_POSIX {
+#ifdef USE_CLOUD
+	friend void ::cloud_connection_json_callback(char *str);
+#endif
+protected:
+#ifdef USE_CLOUD
+	CloudConnectionCallback _cloudConnectionCallback;
+#endif
+
 public:
 	void initBackend() override;
 	bool hasFeature(Feature f) override;
@@ -42,6 +58,11 @@ public:
 	void init() override;
 	void addSysArchivesToSearchSet(Common::SearchSet &s, int priority) override;
 
+#ifdef USE_CLOUD
+	void setCloudConnectionCallback(CloudConnectionCallback cb) { _cloudConnectionCallback = cb; }
+	bool openUrl(const Common::String &url) override;
+#endif // USE_CLOUD
+    
 protected:
 	Common::Path getDefaultConfigFileName() override;
 	Common::Path getDefaultLogFileName() override;
diff --git a/base/version.cpp b/base/version.cpp
index 250043cb0fe..28ee3317a56 100644
--- a/base/version.cpp
+++ b/base/version.cpp
@@ -198,7 +198,7 @@ const char gScummVMFeatures[] = ""
 
 #ifdef USE_CLOUD
 	"cloud ("
-#  ifdef USE_LIBCURL
+#  if defined(USE_LIBCURL) || defined(EMSCRIPTEN) 
 	"servers"
 #    ifdef USE_SDL_NET
 	", local) "
@@ -210,6 +210,9 @@ const char gScummVMFeatures[] = ""
 #  ifdef USE_LIBCURL
 	"libcurl "
 #  endif
+#  ifdef EMSCRIPTEN
+	"emscripten_fetch "
+#  endif
 #  ifdef USE_SDL_NET
 	"SDL_net "
 #  endif
diff --git a/configure b/configure
index da5ac9d8c56..6f623d97c00 100755
--- a/configure
+++ b/configure
@@ -3646,12 +3646,15 @@ if test -n "$_host"; then
 			;;
 		wasm*-emscripten)
 			_backend="emscripten"
-			# Disable cloud and SDL_Net as this is handled in the browser
-			_cloud=no
-			_sdlnet=no
-			_libcurl=no
-			_curl=no
-			_enet=no
+			_enet=no    # Disable ENet (no sockets in the browser)
+			_sdlnet=no  # Disable SDL_Net (no sockets in the browser)
+			_libcurl=no # Emscripten backend uses emscripten_fetch not libcurl
+			_curl=no # Disable cloud unless it's manually enabled
+			if test "$_cloud" != yes; then
+				_cloud=no
+			else
+				append_var LDFLAGS "-sFETCH"
+			fi
 			_seq_midi=no
 			_timidity=no
 			_sndio=no
@@ -6097,6 +6100,10 @@ else
 		_cloud=yes
 		echo_n "servers"
 	fi
+	if test "$_host_os" = "emscripten"; then
+		_cloud=yes
+		echo_n "servers"
+	fi
 	if test "$_cloud" = "no"; then
 		echo_n "no"
 	fi
diff --git a/dists/emscripten/README.md b/dists/emscripten/README.md
index 4256e856382..9ea00c287a2 100644
--- a/dists/emscripten/README.md
+++ b/dists/emscripten/README.md
@@ -79,9 +79,11 @@ ScummVM relies heavily on Asyncify (see note above), and this comes with a quite
       * Persist last game and last plugin for offline use
       * Pre-load assets asynchronously (not blocking) - i.e. rest of the data of a game which has been launched
       * Loading indicators (doesn't work with the current synchronous/blocking filesystem)
-*   Add support for save games (and game data?) on personal cloud storage (Dropbox, Google Drive).
-
 Emscripten is currently re-doing their filesystem code, which could help address some of the above issues ( emscripten-core/emscripten#15041 ).
+* Locally persisted file system for saved games and settings using the Browser IndexedDB. (using [Emscripten IDBFS](https://emscripten.org/docs/api_reference/Filesystem-API.html#filesystem-api-idbfs))
+* Cloud storage (Dropbox, Google Drive etc.) is exposed as a special folder on the file system. Only read access is implemented, but saved games can be synchronized via the regular cloud sync feature
+* Screenshots and Logfiles are automatically downloaded after creation
+* All other data is stored in memory and removed on reload (incl. temporarily stored logfiles and screenshots)
 
 ### UI Integration
 *   Build a nice webpage around the canvas.
diff --git a/engines/scumm/he/net/net_lobby.h b/engines/scumm/he/net/net_lobby.h
index 4748e7beac9..a8984ea6791 100644
--- a/engines/scumm/he/net/net_lobby.h
+++ b/engines/scumm/he/net/net_lobby.h
@@ -22,8 +22,8 @@
 #ifndef SCUMM_HE_NET_LOBBY_H
 #define SCUMM_HE_NET_LOBBY_H
 
-#include "backends/networking/http/socket.h"
-#include "backends/networking/http/url.h"
+#include "backends/networking/http/curl/socket.h"
+#include "backends/networking/http/curl/url.h"
 #include "common/formats/json.h"
 
 #include "scumm/he/net/net_main.h"
diff --git a/gui/cloudconnectionwizard.cpp b/gui/cloudconnectionwizard.cpp
index 0034bdff05f..959e3a2871a 100644
--- a/gui/cloudconnectionwizard.cpp
+++ b/gui/cloudconnectionwizard.cpp
@@ -27,6 +27,9 @@
 #include "backends/networking/sdl_net/localwebserver.h"
 #endif // USE_SDL_NET
 
+#ifdef EMSCRIPTEN
+#include "backends/platform/sdl/emscripten/emscripten.h"
+#endif
 #include "common/formats/json.h"
 #include "common/file.h"
 #include "common/memstream.h"
@@ -94,6 +97,17 @@ CloudConnectionWizard::~CloudConnectionWizard() {
 	delete _callback;
 }
 
+#ifdef EMSCRIPTEN
+// allow browser to pass in JSON tokens
+void CloudConnectionWizard::emscriptenCloudConnectionCallback(const Common::String *message ){
+	showStep(Step::MANUAL_MODE_STEP_2);
+	if (!message->empty()) {
+		_codeBox->setEditString(*message);
+	}
+	manualModeConnect();
+}
+#endif 
+
 void CloudConnectionWizard::showStep(Step newStep) {
 	if (newStep == _currentStep)
 		return;
@@ -318,6 +332,10 @@ void CloudConnectionWizard::hideStepQuickModeSuccess() {
 // manual mode
 
 void CloudConnectionWizard::showStepManualMode1() {
+#ifdef EMSCRIPTEN
+	OSystem_Emscripten *emscripten_g_system = dynamic_cast<OSystem_Emscripten*>(g_system);
+	emscripten_g_system->setCloudConnectionCallback(new Common::Callback<CloudConnectionWizard, const Common::String *>(this, &CloudConnectionWizard::emscriptenCloudConnectionCallback));
+#endif
 	_headlineLabel->setLabel(_("Manual Mode: Step 1"));
 	showContainer("ConnectionWizard_ManualModeStep1");
 #ifdef USE_SDL_NET
@@ -364,12 +382,19 @@ void CloudConnectionWizard::showStepManualMode2() {
 
 	_label0 = new StaticTextWidget(_container, "ConnectionWizard_ManualModeStep2.Line1", _("Copy the JSON code from the browser here and press Next:"));
 	_codeBox = new EditTextWidget(_container, "ConnectionWizard_ManualModeStep2.CodeBox", Common::U32String(), Common::U32String(), 0, 0, ThemeEngine::kFontStyleConsole);
+#ifndef EMSCRIPTEN
 	_button0 = new ButtonWidget(_container, "ConnectionWizard_ManualModeStep2.PasteButton", _("Paste"), _("Paste code from clipboard"), kCloudConnectionWizardPasteCodeCmd);
 	_button1 = new ButtonWidget(_container, "ConnectionWizard_ManualModeStep2.LoadButton", _("Load"), _("Load code from file"), kCloudConnectionWizardLoadCodeCmd);
+#endif
 	_label1 = new StaticTextWidget(_container, "ConnectionWizard_ManualModeStep2.Line2", Common::U32String());
+
 }
 
 void CloudConnectionWizard::hideStepManualMode2() {
+#ifdef EMSCRIPTEN
+	OSystem_Emscripten *emscripten_g_system = dynamic_cast<OSystem_Emscripten*>(g_system);
+	emscripten_g_system->setCloudConnectionCallback(nullptr);
+#endif
 	hideContainer();
 	_closeButton->setEnabled(true);
 	hideBackButton();
diff --git a/gui/cloudconnectionwizard.h b/gui/cloudconnectionwizard.h
index 06961f9a449..d49ddcfcce7 100644
--- a/gui/cloudconnectionwizard.h
+++ b/gui/cloudconnectionwizard.h
@@ -124,6 +124,10 @@ class CloudConnectionWizard : public Dialog {
 	void manualModeConnect();
 	void manualModeStorageConnectionCallback(const Networking::ErrorResponse &response);
 
+#ifdef EMSCRIPTEN
+	void emscriptenCloudConnectionCallback(const Common::String *message);
+#endif
+
 public:
 	CloudConnectionWizard();
 	~CloudConnectionWizard() override;


Commit: f35ff12669e84f238d4d1a1a425d1667fb58d011
    https://github.com/scummvm/scummvm/commit/f35ff12669e84f238d4d1a1a425d1667fb58d011
Author: Christian Kündig (christian at kuendig.info)
Date: 2025-08-13T18:38:54+02:00

Commit Message:
CLOUD: FS: EMSCRIPTEN: Implement cloud-backed filesystem so games can directly be run from cloud storage

Changed paths:
  A backends/fs/emscripten/cloud-fs.cpp
  A backends/fs/emscripten/cloud-fs.h
    backends/fs/emscripten/emscripten-fs-factory.cpp
    backends/fs/emscripten/emscripten-posix-fs.cpp
    backends/module.mk


diff --git a/backends/fs/emscripten/cloud-fs.cpp b/backends/fs/emscripten/cloud-fs.cpp
new file mode 100644
index 00000000000..bf666515d27
--- /dev/null
+++ b/backends/fs/emscripten/cloud-fs.cpp
@@ -0,0 +1,205 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef EMSCRIPTEN
+
+#include "backends/fs/emscripten/cloud-fs.h"
+#include "backends/cloud/downloadrequest.h"
+#include "backends/fs/fs-factory.h"
+#include "backends/fs/posix/posix-fs.h"
+#include "backends/fs/posix/posix-iostream.h"
+
+#include "common/system.h"
+
+Common::HashMap<Common::String, AbstractFSList> CloudFilesystemNode::_cloudFolders = Common::HashMap<Common::String, AbstractFSList>();
+
+CloudFilesystemNode::CloudFilesystemNode(const Common::String &p) : _isDirectory(false), _isValid(false), _path(p), _storageFileId(nullptr) {
+	debug(5, "CloudFilesystemNode::CloudFilesystemNode(Common::String %s)", p.c_str());
+	assert(p.size() > 0);
+
+	// Normalize the path (that is, remove unneeded slashes etc.)
+	_path = Common::normalizePath(_path, '/');
+	_displayName = Common::lastPathComponent(_path, '/');
+	if (_path == CLOUD_FS_PATH) { // need special case for handling the root of the cloud-filesystem
+		_displayName = "[" + Common::lastPathComponent(_path, '/') + "]";
+		_isDirectory = true;
+		_isValid = true;
+		return;
+	} else { // we need to peek in the parent folder to see if file exists and if it's a directory
+		AbstractFSNode *parent = getParent();
+		AbstractFSList tmp = AbstractFSList();
+		parent->getChildren(tmp, Common::FSNode::kListAll, true);
+		for (AbstractFSList::iterator i = tmp.begin(); i != tmp.end(); ++i) {
+			CloudFilesystemNode *child = (CloudFilesystemNode *)*i;
+			if (child->getPath() == _path) {
+				_isDirectory = child->isDirectory();
+				_isValid = true;
+				_storageFileId = child->_storageFileId;
+				break;
+			}
+		}
+
+		return;
+	}
+}
+
+AbstractFSNode *CloudFilesystemNode::getChild(const Common::String &n) const {
+	assert(!_path.empty());
+	assert(_isDirectory);
+
+	// Make sure the string contains no slashes
+	assert(!n.contains('/'));
+
+	// We assume here that _path is already normalized (hence don't bother to call
+	//  Common::normalizePath on the final path).
+	Common::String newPath(_path);
+	if (_path.lastChar() != '/')
+		newPath += '/';
+	newPath += n;
+
+	return makeNode(newPath);
+}
+
+void CloudFilesystemNode::directoryListedCallback(const Cloud::Storage::ListDirectoryResponse &response) {
+	debug(5, "CloudFilesystemNode::directoryListedCallback %s", _path.c_str());
+	Common::Array<Cloud::StorageFile> storageFiles = response.value;
+	AbstractFSList *dirList = new AbstractFSList();
+
+	for (Common::Array<Cloud::StorageFile>::iterator i = storageFiles.begin(); i != storageFiles.end(); ++i) {
+		Cloud::StorageFile storageFile = *i;
+		CloudFilesystemNode *file_node = new CloudFilesystemNode();
+		file_node->_isDirectory = storageFile.isDirectory();
+		file_node->_path = _path + "/" + storageFile.name();
+		file_node->_isValid = true;
+		file_node->_displayName = "" + storageFile.name();
+		file_node->_storageFileId = storageFile.id();
+		dirList->push_back(file_node);
+	}
+	_cloudFolders[_path] = *dirList;
+}
+
+void CloudFilesystemNode::directoryListedErrorCallback(const Networking::ErrorResponse &_error) {
+	// _workingRequest = nullptr; // TODO: HANDLE THIS SOMEWHERE
+	error("Response %ld: %s", _error.httpResponseCode, _error.response.c_str());
+}
+
+void CloudFilesystemNode::fileDownloadedCallback(const Cloud::Storage::BoolResponse &response) {
+	// _workingRequest = nullptr; // TODO: HANDLE THIS SOMEWHERE
+	debug(5, "CloudFilesystemNode::fileDownloadedCallback %s", _path.c_str());
+}
+
+void CloudFilesystemNode::fileDownloadedErrorCallback(const Networking::ErrorResponse &_error) {
+	// _workingRequest = nullptr; // TODO: HANDLE THIS SOMEWHERE
+	error("Response %ld: %s", _error.httpResponseCode, _error.response.c_str());
+}
+
+bool CloudFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool hidden) const {
+	assert(_isDirectory);
+
+	if (!_cloudFolders.contains(_path)) {
+		debug(5, "CloudFilesystemNode::getChildren Fetching Children: %s", _path.c_str());
+		Common::String _cloud_path = _path.substr(sizeof(CLOUD_FS_PATH), _path.size() - sizeof(CLOUD_FS_PATH));
+
+		CloudMan.listDirectory(
+			_cloud_path,
+			new Common::Callback<CloudFilesystemNode, const Cloud::Storage::ListDirectoryResponse &>((CloudFilesystemNode *)this, &CloudFilesystemNode::directoryListedCallback),
+			new Common::Callback<CloudFilesystemNode, const Networking::ErrorResponse &>((CloudFilesystemNode *)this, &CloudFilesystemNode::directoryListedErrorCallback),
+			false);
+
+		while (!_cloudFolders.contains(_path)) {
+			g_system->delayMillis(10);
+		}
+		debug(5, "CloudFilesystemNode::getChildren %s size %u", _path.c_str(), _cloudFolders[_path].size());
+	}
+
+	for (AbstractFSList::iterator i = _cloudFolders[_path].begin(); i != _cloudFolders[_path].end(); ++i) {
+		// TODO: Respect mode and hidden getChildren parameters
+		CloudFilesystemNode *node = (CloudFilesystemNode *)*i;
+		// we need to copy node here as FSNode will take ownership of the pointer and destroy it after use
+		myList.push_back(new CloudFilesystemNode(*node));
+	}
+	return true;
+}
+
+AbstractFSNode *CloudFilesystemNode::getParent() const {
+	if (_path == "/")
+		return 0; // The filesystem root has no parent
+
+	const char *start = _path.c_str();
+	const char *end = start + _path.size();
+
+	// Strip of the last component. We make use of the fact that at this
+	// point, _path is guaranteed to be normalized
+	while (end > start && *(end - 1) != '/')
+		end--;
+
+	if (end == start) {
+		// This only happens if we were called with a relative path, for which
+		// there simply is no parent.
+		// TODO: We could also resolve this by assuming that the parent is the
+		//       current working directory, and returning a node referring to that.
+		return 0;
+	}
+
+	Common::String _parent_path = Common::normalizePath(Common::String(start, end), '/');
+	FilesystemFactory *factory = g_system->getFilesystemFactory();
+	return factory->makeFileNodePath(_parent_path);
+}
+
+Common::SeekableReadStream *CloudFilesystemNode::createReadStream() {
+	debug(5, "CloudFilesystemNode::createReadStream() %s", _path.c_str());
+	Common::String fsCachePath = Common::normalizePath("/.cache/" + _path, '/');
+	POSIXFilesystemNode *cacheFile = new POSIXFilesystemNode(fsCachePath);
+	if (!cacheFile->exists()) {
+		Cloud::Storage *_storage = CloudMan.getCurrentStorage();
+		Networking::Request *_workingRequest = _storage->downloadById(
+			_storageFileId,
+			Common::Path(fsCachePath),
+			new Common::Callback<CloudFilesystemNode, const Cloud::Storage::BoolResponse &>(this, &CloudFilesystemNode::fileDownloadedCallback),
+			new Common::Callback<CloudFilesystemNode, const Networking::ErrorResponse &>(this, &CloudFilesystemNode::fileDownloadedErrorCallback));
+		while (_workingRequest->state() != Networking::RequestState::FINISHED) {
+			g_system->delayMillis(10);
+		}
+		debug(5, "CloudFilesystemNode::createReadStream() file written %s", fsCachePath.c_str());
+	}
+	return PosixIoStream::makeFromPath(fsCachePath, StdioStream::WriteMode_Read);
+}
+
+Common::SeekableWriteStream *CloudFilesystemNode::createWriteStream(bool atomic) {
+	return 0;
+}
+
+bool CloudFilesystemNode::createDirectory() {
+	return false;
+}
+bool CloudFilesystemNode::exists() const {
+	return _isValid;
+}
+
+bool CloudFilesystemNode::isReadable() const {
+	return exists();
+}
+
+bool CloudFilesystemNode::isWritable() const {
+	return false;
+}
+
+#endif // #if defined(EMSCRIPTEN)
diff --git a/backends/fs/emscripten/cloud-fs.h b/backends/fs/emscripten/cloud-fs.h
new file mode 100644
index 00000000000..0c713777d38
--- /dev/null
+++ b/backends/fs/emscripten/cloud-fs.h
@@ -0,0 +1,95 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef CLOUD_FILESYSTEM_H
+#define CLOUD_FILESYSTEM_H
+
+#include "backends/fs/abstract-fs.h"
+
+#ifdef USE_CLOUD
+#include "backends/cloud/cloudmanager.h"
+#include "backends/cloud/storage.h"
+#include "backends/cloud/storagefile.h"
+#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/http/httpjsonrequest.h"
+#include "backends/networking/http/request.h"
+#endif
+
+#include "backends/fs/posix/posix-fs.h"
+
+/**
+ * Implementation of the ScummVM file system API based on POSIX.
+ *
+ * Parts of this class are documented in the base interface class, AbstractFSNode.
+ */
+class CloudFilesystemNode : public AbstractFSNode {
+#define CLOUD_FS_PATH "/cloud"
+protected:
+	static Common::HashMap<Common::String, AbstractFSList> _cloudFolders;
+	Common::String _displayName;
+	Common::String _path;
+	Common::String _storageFileId;
+	bool _isDirectory;
+	bool _isValid;
+
+	/**
+	 * Plain constructor, for internal use only (hence protected).
+	 */
+	CloudFilesystemNode() : _isDirectory(false), _isValid(false), _storageFileId(nullptr) {}
+
+	virtual AbstractFSNode *makeNode(const Common::String &path) const {
+		return new CloudFilesystemNode(path);
+	}
+
+	/**
+	 * Callbacks for network calls (file download and directory listing)
+	 */
+	void directoryListedCallback(const Cloud::Storage::ListDirectoryResponse &response);
+	void directoryListedErrorCallback(const Networking::ErrorResponse &error);
+	void fileDownloadedCallback(const Cloud::Storage::BoolResponse &response);
+	void fileDownloadedErrorCallback(const Networking::ErrorResponse &error);
+
+public:
+	/**
+	 * Creates a CloudFilesystemNode for a given path.
+	 *
+	 * @param path the path the new node should point to.
+	 */
+	CloudFilesystemNode(const Common::String &path);
+
+	bool exists() const override;
+	Common::U32String getDisplayName() const override { return _displayName; }
+	Common::String getName() const override { return _displayName; }
+	Common::String getPath() const override { return _path; }
+	bool isDirectory() const override { return _isDirectory; }
+	bool isReadable() const override;
+	bool isWritable() const override;
+
+	AbstractFSNode *getChild(const Common::String &n) const override;
+	bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const override;
+	AbstractFSNode *getParent() const override;
+
+	Common::SeekableReadStream *createReadStream() override;
+	Common::SeekableWriteStream *createWriteStream(bool atomic) override;
+	bool createDirectory() override;
+};
+
+#endif
diff --git a/backends/fs/emscripten/emscripten-fs-factory.cpp b/backends/fs/emscripten/emscripten-fs-factory.cpp
index e97bc3c7ffc..6047b195c15 100644
--- a/backends/fs/emscripten/emscripten-fs-factory.cpp
+++ b/backends/fs/emscripten/emscripten-fs-factory.cpp
@@ -27,6 +27,10 @@
 #include "backends/fs/emscripten/emscripten-posix-fs.h"
 #include "backends/fs/emscripten/http-fs.h"
 #include "common/debug.h"
+#ifdef USE_CLOUD
+#include "backends/fs/emscripten/cloud-fs.h"
+#endif
+
 #include <emscripten.h>
 
 EM_ASYNC_JS(void, _initSettings, (const char *pathPtr), {
@@ -80,6 +84,10 @@ AbstractFSNode *EmscriptenFilesystemFactory::makeFileNodePath(const Common::Stri
 			_httpNodes->setVal(path, new HTTPFilesystemNode(path));
 		}
 		return new HTTPFilesystemNode(*(_httpNodes->getVal(path)));
+#ifdef USE_CLOUD
+	} else if (path.hasPrefix(CLOUD_FS_PATH) && CloudMan.isStorageEnabled()) {
+		return new CloudFilesystemNode(path);
+#endif
 	} else {
 		return new EmscriptenPOSIXFilesystemNode(path);
 	}
diff --git a/backends/fs/emscripten/emscripten-posix-fs.cpp b/backends/fs/emscripten/emscripten-posix-fs.cpp
index 93127977c6e..bb2f115cf8b 100644
--- a/backends/fs/emscripten/emscripten-posix-fs.cpp
+++ b/backends/fs/emscripten/emscripten-posix-fs.cpp
@@ -29,6 +29,11 @@
 #include "backends/fs/posix/posix-iostream.h"
 #include "common/system.h"
 
+#ifdef USE_CLOUD
+#include "backends/cloud/cloudmanager.h"
+#include "backends/fs/emscripten/cloud-fs.h"
+#endif
+
 AbstractFSNode *EmscriptenPOSIXFilesystemNode::makeNode(const Common::String &path) const {
 	return g_system->getFilesystemFactory()->makeFileNodePath(path);
 }
@@ -42,6 +47,12 @@ bool EmscriptenPOSIXFilesystemNode::getChildren(AbstractFSList &myList, ListMode
 		HTTPFilesystemNode *data_entry = new HTTPFilesystemNode(DATA_PATH);
 		myList.push_back(data_entry);
 
+#ifdef USE_CLOUD
+		if (CloudMan.isStorageEnabled()) {
+			CloudFilesystemNode *cloud_entry = new CloudFilesystemNode(CLOUD_FS_PATH);
+			myList.push_back(cloud_entry);
+		}
+#endif
 	}
 	return POSIXFilesystemNode::getChildren(myList, mode, hidden);
 }
diff --git a/backends/module.mk b/backends/module.mk
index bf21d2c6484..bd5a78ad515 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -99,6 +99,7 @@ MODULE_OBJS += \
 	midi/webmidi.o 
 ifdef USE_CLOUD
 MODULE_OBJS += \
+	fs/emscripten/cloud-fs.o \
 	networking/http/connectionmanager.o \
 	networking/http/httpjsonrequest.o \
 	networking/http/httprequest.o \


Commit: 8bb1ead700ba3a490f380eaeaf9379a6fb3c44cf
    https://github.com/scummvm/scummvm/commit/8bb1ead700ba3a490f380eaeaf9379a6fb3c44cf
Author: Christian Kündig (christian at kuendig.info)
Date: 2025-08-13T18:38:54+02:00

Commit Message:
TESTBED: Fix Cloud tests

Changed paths:
    engines/testbed/cloud.cpp


diff --git a/engines/testbed/cloud.cpp b/engines/testbed/cloud.cpp
index f572b5e2fe4..f77454e38d2 100644
--- a/engines/testbed/cloud.cpp
+++ b/engines/testbed/cloud.cpp
@@ -441,9 +441,12 @@ TestExitStatus CloudTests::testDownloading() {
 		return kTestSkipped;
 	}
 
-	const Common::Path &path = ConfMan.getPath("path");
-	Common::FSDirectory gameRoot(path);
-	Common::FSNode node = gameRoot.getFSNode().getChild("downloaded_file.txt");
+	Common::FSNode testDirectory(ConfMan.getPath("path"));
+	if (!testDirectory.isWritable()) {
+		// redirect to savepath if game-data directory is not writable.
+		testDirectory = Common::FSNode(ConfMan.getPath("savepath"));
+	}
+	Common::FSNode node = testDirectory.getChild("downloaded_file.txt");
 	Common::Path filepath = node.getPath();
 	if (CloudMan.getCurrentStorage()->download(
 			Common::String(getRemoteTestPath()) + "/testfile.txt",
@@ -488,9 +491,12 @@ TestExitStatus CloudTests::testFolderDownloading() {
 		return kTestSkipped;
 	}
 
-	const Common::Path &path = ConfMan.getPath("path");
-	Common::FSDirectory gameRoot(path);
-	Common::FSNode node = gameRoot.getFSNode().getChild("downloaded_directory");
+	Common::FSNode testDirectory(ConfMan.getPath("path"));
+	if (!testDirectory.isWritable()) {
+		// redirect to savepath if game-data directory is not writable.
+		testDirectory = Common::FSNode(ConfMan.getPath("savepath"));
+	}
+	Common::FSNode node = testDirectory.getChild("downloaded_directory");
 	Common::Path filepath = node.getPath();
 	if (CloudMan.downloadFolder(
 			getRemoteTestPath(),
@@ -535,10 +541,6 @@ TestExitStatus CloudTests::testSavesSync() {
 		return kTestSkipped;
 	}
 
-	Common::Path path = ConfMan.getPath("path");
-	Common::FSDirectory gameRoot(path);
-	Common::FSNode node = gameRoot.getFSNode().getChild("downloaded_directory");
-	Common::Path filepath = node.getPath();
 	if (CloudMan.syncSaves(
 			new Common::GlobalFunctionCallback<const Cloud::Storage::BoolResponse &>(&savesSyncedCallback),
 			new Common::GlobalFunctionCallback<const Networking::ErrorResponse &>(&errorCallback)


Commit: 1b99f57b72ffccae66995960732f3ac9bb1e9fec
    https://github.com/scummvm/scummvm/commit/1b99f57b72ffccae66995960732f3ac9bb1e9fec
Author: Christian Kündig (christian at kuendig.info)
Date: 2025-08-13T18:38:54+02:00

Commit Message:
CLOUD: Fallback to local date if not sent from backend.

Note: HTTP Date header is local date at server time and has no special meaning. In Emscripten this header is inaccessible due to CORS restrictions.

Changed paths:
    backends/cloud/savessyncrequest.cpp


diff --git a/backends/cloud/savessyncrequest.cpp b/backends/cloud/savessyncrequest.cpp
index b48354d0ce2..2e6969d8285 100644
--- a/backends/cloud/savessyncrequest.cpp
+++ b/backends/cloud/savessyncrequest.cpp
@@ -82,6 +82,15 @@ void SavesSyncRequest::directoryListedCallback(const Storage::ListDirectoryRespo
 		return;
 
 	if (response.request) _date = response.request->date();
+	if (_date.empty()) {
+		// This is from SaveLoadChooser::createDefaultSaveDescription
+		TimeDate curTime;
+		g_system->getTimeAndDate(curTime);
+		curTime.tm_year += 1900; // fixup year
+		curTime.tm_mon++;        // fixup month
+		_date = Common::String::format("%04d-%02d-%02d / %02d:%02d:%02d", curTime.tm_year, curTime.tm_mon, curTime.tm_mday, curTime.tm_hour, curTime.tm_min, curTime.tm_sec);
+		debug(9, "SavesSyncRequest: using local time as fallback: %s", _date.c_str());
+	}
 
 	Common::HashMap<Common::String, bool> localFileNotAvailableInCloud;
 	for (auto &timestamp : _localFilesTimestamps) {
@@ -470,6 +479,7 @@ void SavesSyncRequest::finishSync(bool success) {
 	Request::finishSuccess();
 
 	//update last successful sync date
+	debug(9, "SavesSyncRequest: last successful sync date: %s", _date.c_str());
 	CloudMan.setStorageLastSync(CloudMan.getStorageIndex(), _date);
 
 	if (_boolCallback)


Commit: ebfc9c8f551751abf2d6cd34bba781c5ac3cb46d
    https://github.com/scummvm/scummvm/commit/ebfc9c8f551751abf2d6cd34bba781c5ac3cb46d
Author: Christian Kündig (christian at kuendig.info)
Date: 2025-08-13T18:38:54+02:00

Commit Message:
CLOUD: Refactor ConnectionManager and NetworkReadStream

Changed paths:
  A backends/networking/curl/cacert.cpp
  A backends/networking/curl/cacert.h
  A backends/networking/curl/socket.cpp
  A backends/networking/curl/socket.h
  A backends/networking/curl/url.cpp
  A backends/networking/curl/url.h
  A backends/networking/http/curl/connectionmanager-curl.cpp
  A backends/networking/http/curl/connectionmanager-curl.h
  A backends/networking/http/curl/networkreadstream-curl.h
  A backends/networking/http/emscripten/connectionmanager-emscripten.cpp
  A backends/networking/http/emscripten/connectionmanager-emscripten.h
  A backends/networking/http/emscripten/networkreadstream-emscripten.h
  A backends/networking/http/networkreadstream.cpp
  R backends/networking/http/curl/socket.cpp
  R backends/networking/http/curl/socket.h
  R backends/networking/http/curl/url.cpp
  R backends/networking/http/curl/url.h
    backends/cloud/box/boxstorage.cpp
    backends/cloud/box/boxtokenrefresher.cpp
    backends/cloud/box/boxtokenrefresher.h
    backends/cloud/dropbox/dropboxstorage.cpp
    backends/cloud/dropbox/dropboxtokenrefresher.cpp
    backends/cloud/dropbox/dropboxtokenrefresher.h
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledrivetokenrefresher.cpp
    backends/cloud/googledrive/googledrivetokenrefresher.h
    backends/cloud/id/idstorage.cpp
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedrivetokenrefresher.cpp
    backends/cloud/onedrive/onedrivetokenrefresher.h
    backends/module.mk
    backends/networking/http/connectionmanager.cpp
    backends/networking/http/connectionmanager.h
    backends/networking/http/curl/networkreadstream-curl.cpp
    backends/networking/http/emscripten/networkreadstream-emscripten.cpp
    backends/networking/http/httpjsonrequest.cpp
    backends/networking/http/httprequest.cpp
    backends/networking/http/networkreadstream.h
    backends/networking/http/session.cpp
    backends/networking/http/sessionrequest.cpp
    engines/scumm/he/net/net_lobby.h


diff --git a/backends/cloud/box/boxstorage.cpp b/backends/cloud/box/boxstorage.cpp
index c826e0f8246..a6d60fa3418 100644
--- a/backends/cloud/box/boxstorage.cpp
+++ b/backends/cloud/box/boxstorage.cpp
@@ -19,8 +19,6 @@
  *
  */
 
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
 #include "backends/cloud/box/boxstorage.h"
 #include "backends/cloud/box/boxlistdirectorybyidrequest.h"
 #include "backends/cloud/box/boxtokenrefresher.h"
@@ -198,7 +196,7 @@ Networking::Request *BoxStorage::streamFileById(const Common::String &id, Networ
 		Common::String header = "Authorization: Bearer " + _token;
 		Networking::RequestHeaders *headersList = new Networking::RequestHeaders();
 		headersList->push_back(header);
-		Networking::NetworkReadStream *stream = new Networking::NetworkReadStream(url.c_str(), headersList, "");
+		Networking::NetworkReadStream *stream = Networking::NetworkReadStream::make(url.c_str(), headersList, "");
 		(*callback)(Networking::NetworkReadStreamResponse(nullptr, stream));
 	}
 	delete callback;
diff --git a/backends/cloud/box/boxtokenrefresher.cpp b/backends/cloud/box/boxtokenrefresher.cpp
index 4c7a0e5ead5..d1c8f279c04 100644
--- a/backends/cloud/box/boxtokenrefresher.cpp
+++ b/backends/cloud/box/boxtokenrefresher.cpp
@@ -19,8 +19,6 @@
  *
  */
 
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
 #include "backends/cloud/box/boxtokenrefresher.h"
 #include "backends/cloud/box/boxstorage.h"
 #include "backends/networking/http/networkreadstream.h"
@@ -44,12 +42,11 @@ void BoxTokenRefresher::tokenRefreshed(const Storage::BoolResponse &response) {
 	}
 
 	//update headers: first change header with token, then pass those to request
-	for (uint32 i = 0; i < _headers.size(); ++i) {
-		if (_headers[i].contains("Authorization")) {
-			_headers[i] = "Authorization: Bearer " + _parentStorage->accessToken();
+	for (uint32 i = 0; i < _headersList.size(); ++i) {
+		if (_headersList[i].contains("Authorization")) {
+			_headersList[i] = "Authorization: Bearer " + _parentStorage->accessToken();
 		}
 	}
-	setHeaders(_headers);
 
 	//successfully received refreshed token, can restart the original request now
 	retry(0);
@@ -119,15 +116,5 @@ void BoxTokenRefresher::finishError(const Networking::ErrorResponse &error, Netw
 	Request::finishError(error);
 }
 
-void BoxTokenRefresher::setHeaders(const Common::Array<Common::String> &headers) {
-	_headers = headers;
-	HttpJsonRequest::setHeaders(headers);
-}
-
-void BoxTokenRefresher::addHeader(const Common::String  &header) {
-	_headers.push_back(header);
-	HttpJsonRequest::addHeader(header);
-}
-
 } // End of namespace Box
 } // End of namespace Cloud
diff --git a/backends/cloud/box/boxtokenrefresher.h b/backends/cloud/box/boxtokenrefresher.h
index be1f4929833..c43558c7bca 100644
--- a/backends/cloud/box/boxtokenrefresher.h
+++ b/backends/cloud/box/boxtokenrefresher.h
@@ -32,7 +32,6 @@ class BoxStorage;
 
 class BoxTokenRefresher: public Networking::HttpJsonRequest {
 	BoxStorage *_parentStorage;
-	Common::Array<Common::String> _headers;
 
 	void tokenRefreshed(const Storage::BoolResponse &response);
 
@@ -41,9 +40,6 @@ class BoxTokenRefresher: public Networking::HttpJsonRequest {
 public:
 	BoxTokenRefresher(BoxStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
 	~BoxTokenRefresher() override;
-
-	void setHeaders(const Common::Array<Common::String> &headers) override;
-	void addHeader(const Common::String &header) override;
 };
 
 } // End of namespace Box
diff --git a/backends/cloud/dropbox/dropboxstorage.cpp b/backends/cloud/dropbox/dropboxstorage.cpp
index f1837d4d37f..c2c2daa176f 100644
--- a/backends/cloud/dropbox/dropboxstorage.cpp
+++ b/backends/cloud/dropbox/dropboxstorage.cpp
@@ -19,8 +19,6 @@
  *
  */
 
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
 #include "backends/cloud/dropbox/dropboxstorage.h"
 #include "backends/cloud/dropbox/dropboxcreatedirectoryrequest.h"
 #include "backends/cloud/dropbox/dropboxinforequest.h"
diff --git a/backends/cloud/dropbox/dropboxtokenrefresher.cpp b/backends/cloud/dropbox/dropboxtokenrefresher.cpp
index 6475ba4cd6d..f39732c4646 100644
--- a/backends/cloud/dropbox/dropboxtokenrefresher.cpp
+++ b/backends/cloud/dropbox/dropboxtokenrefresher.cpp
@@ -19,8 +19,6 @@
  *
  */
 
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
 #include "backends/cloud/dropbox/dropboxtokenrefresher.h"
 #include "backends/cloud/dropbox/dropboxstorage.h"
 #include "backends/networking/http/networkreadstream.h"
@@ -44,12 +42,11 @@ void DropboxTokenRefresher::tokenRefreshed(const Storage::BoolResponse &response
 	}
 
 	//update headers: first change header with token, then pass those to request
-	for (uint32 i = 0; i < _headers.size(); ++i) {
-		if (_headers[i].contains("Authorization")) {
-			_headers[i] = "Authorization: Bearer " + _parentStorage->accessToken();
+	for (uint32 i = 0; i < _headersList.size(); ++i) {
+		if (_headersList[i].contains("Authorization")) {
+			_headersList[i] = "Authorization: Bearer " + _parentStorage->accessToken();
 		}
 	}
-	setHeaders(_headers);
 
 	//successfully received refreshed token, can restart the original request now
 	retry(0);
@@ -106,16 +103,5 @@ void DropboxTokenRefresher::finishError(const Networking::ErrorResponse &error,
 	Request::finishError(error);
 }
 
-void DropboxTokenRefresher::setHeaders(const Common::Array<Common::String> &headers) {
-	_headers = headers;
-	// Clear existing headers and add new ones
-	HttpRequest::setHeaders(headers);
-}
-
-void DropboxTokenRefresher::addHeader(const Common::String &header) {
-	_headers.push_back(header);
-	HttpJsonRequest::addHeader(header);
-}
-
 } // End of namespace Dropbox
 } // End of namespace Cloud
diff --git a/backends/cloud/dropbox/dropboxtokenrefresher.h b/backends/cloud/dropbox/dropboxtokenrefresher.h
index 9c3afa6ee4a..5915dc2f4b7 100644
--- a/backends/cloud/dropbox/dropboxtokenrefresher.h
+++ b/backends/cloud/dropbox/dropboxtokenrefresher.h
@@ -32,7 +32,6 @@ class DropboxStorage;
 
 class DropboxTokenRefresher: public Networking::HttpJsonRequest {
 	DropboxStorage *_parentStorage;
-	Common::Array<Common::String> _headers;
 
 	void tokenRefreshed(const Storage::BoolResponse &response);
 
@@ -41,9 +40,6 @@ class DropboxTokenRefresher: public Networking::HttpJsonRequest {
 public:
 	DropboxTokenRefresher(DropboxStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
 	~DropboxTokenRefresher() override;
-
-	void setHeaders(const Common::Array<Common::String> &headers) override;
-	void addHeader(const Common::String &header) override;
 };
 
 } // End of namespace Dropbox
diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index 1437688859e..d218608dbaa 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -19,8 +19,6 @@
  *
  */
 
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
 #include "backends/cloud/googledrive/googledrivestorage.h"
 #include "backends/cloud/cloudmanager.h"
 #include "backends/cloud/googledrive/googledrivetokenrefresher.h"
@@ -169,7 +167,7 @@ Networking::Request *GoogleDriveStorage::streamFileById(const Common::String &id
 		Common::String header = "Authorization: Bearer " + _token;
 		Networking::RequestHeaders *headersList = new Networking::RequestHeaders();
 		headersList->push_back(header);
-		Networking::NetworkReadStream *stream = new Networking::NetworkReadStream(url.c_str(), headersList, "");
+		Networking::NetworkReadStream *stream = Networking::NetworkReadStream::make(url.c_str(), headersList, "");
 		(*callback)(Networking::NetworkReadStreamResponse(nullptr, stream));
 	}
 	delete callback;
diff --git a/backends/cloud/googledrive/googledrivetokenrefresher.cpp b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
index de3b6629844..ba1f9273fbd 100644
--- a/backends/cloud/googledrive/googledrivetokenrefresher.cpp
+++ b/backends/cloud/googledrive/googledrivetokenrefresher.cpp
@@ -19,8 +19,6 @@
  *
  */
 
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
 #include "backends/cloud/googledrive/googledrivetokenrefresher.h"
 #include "backends/cloud/googledrive/googledrivestorage.h"
 #include "backends/networking/http/networkreadstream.h"
@@ -44,12 +42,11 @@ void GoogleDriveTokenRefresher::tokenRefreshed(const Storage::BoolResponse &resp
 	}
 
 	//update headers: first change header with token, then pass those to request
-	for (uint32 i = 0; i < _headers.size(); ++i) {
-		if (_headers[i].contains("Authorization")) {
-			_headers[i] = "Authorization: Bearer " + _parentStorage->accessToken();
+	for (uint32 i = 0; i < _headersList.size(); ++i) {
+		if (_headersList[i].contains("Authorization")) {
+			_headersList[i] = "Authorization: Bearer " + _parentStorage->accessToken();
 		}
 	}
-	setHeaders(_headers);
 
 	//successfully received refreshed token, can restart the original request now
 	retry(0);
@@ -107,15 +104,5 @@ void GoogleDriveTokenRefresher::finishJson(const Common::JSONValue *json) {
 	HttpJsonRequest::finishJson(json);
 }
 
-void GoogleDriveTokenRefresher::setHeaders(const Common::Array<Common::String> &headers) {
-	_headers = headers;
-	HttpJsonRequest::setHeaders(headers);
-}
-
-void GoogleDriveTokenRefresher::addHeader(const Common::String &header) {
-	_headers.push_back(header);
-	HttpJsonRequest::addHeader(header);
-}
-
 } // End of namespace GoogleDrive
 } // End of namespace Cloud
diff --git a/backends/cloud/googledrive/googledrivetokenrefresher.h b/backends/cloud/googledrive/googledrivetokenrefresher.h
index 05f610638dd..49a157369e1 100644
--- a/backends/cloud/googledrive/googledrivetokenrefresher.h
+++ b/backends/cloud/googledrive/googledrivetokenrefresher.h
@@ -32,7 +32,6 @@ class GoogleDriveStorage;
 
 class GoogleDriveTokenRefresher: public Networking::HttpJsonRequest {
 	GoogleDriveStorage *_parentStorage;
-	Common::Array<Common::String> _headers;
 
 	void tokenRefreshed(const Storage::BoolResponse &response);
 
@@ -40,9 +39,6 @@ class GoogleDriveTokenRefresher: public Networking::HttpJsonRequest {
 public:
 	GoogleDriveTokenRefresher(GoogleDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
 	~GoogleDriveTokenRefresher() override;
-
-	void setHeaders(const Common::Array<Common::String> &headers) override;
-	void addHeader(const Common::String &header) override;
 };
 
 } // End of namespace GoogleDrive
diff --git a/backends/cloud/id/idstorage.cpp b/backends/cloud/id/idstorage.cpp
index b24a87d7c98..e1afc0bca74 100644
--- a/backends/cloud/id/idstorage.cpp
+++ b/backends/cloud/id/idstorage.cpp
@@ -19,8 +19,6 @@
  *
  */
 
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
 #include "backends/cloud/id/idstorage.h"
 #include "backends/cloud/id/idcreatedirectoryrequest.h"
 #include "backends/cloud/id/iddownloadrequest.h"
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 5c6fe9da92b..1cd3134c7c2 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -19,8 +19,6 @@
  *
  */
 
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
 #include "backends/cloud/onedrive/onedrivestorage.h"
 #include "backends/cloud/cloudmanager.h"
 #include "backends/cloud/onedrive/onedrivecreatedirectoryrequest.h"
@@ -153,7 +151,7 @@ void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback out
 	if (outerCallback)
 		(*outerCallback)(Networking::NetworkReadStreamResponse(
 			response.request,
-			new Networking::NetworkReadStream(url, nullptr, "")
+			Networking::NetworkReadStream::make(url, nullptr, "")
 		));
 
 	delete json;
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.cpp b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
index 95e8e6aaef4..8f7d2ac7d04 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.cpp
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.cpp
@@ -19,8 +19,6 @@
  *
  */
 
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
 #include "backends/cloud/onedrive/onedrivetokenrefresher.h"
 #include "backends/cloud/onedrive/onedrivestorage.h"
 #include "backends/networking/http/networkreadstream.h"
@@ -44,12 +42,11 @@ void OneDriveTokenRefresher::tokenRefreshed(const Storage::BoolResponse &respons
 	}
 
 	//update headers: first change header with token, then pass those to request
-	for (uint32 i = 0; i < _headers.size(); ++i) {
-		if (_headers[i].contains("Authorization")) {
-			_headers[i] = "Authorization: bearer " + _parentStorage->accessToken();
+	for (uint32 i = 0; i < _headersList.size(); ++i) {
+		if (_headersList[i].contains("Authorization")) {
+			_headersList[i] = "Authorization: bearer " + _parentStorage->accessToken();
 		}
 	}
-	setHeaders(_headers);
 
 	//successfully received refreshed token, can restart the original request now
 	retry(0);
@@ -141,15 +138,5 @@ void OneDriveTokenRefresher::finishErrorIrrecoverable(const Networking::ErrorRes
 	Request::finishError(error, state); // call closest base class's method
 }
 
-void OneDriveTokenRefresher::setHeaders(const Common::Array<Common::String> &headers) {
-	_headers = headers;
-	HttpJsonRequest::setHeaders(headers);
-}
-
-void OneDriveTokenRefresher::addHeader(const Common::String &header) {
-	_headers.push_back(header);
-	HttpJsonRequest::addHeader(header);
-}
-
 } // End of namespace OneDrive
 } // End of namespace Cloud
diff --git a/backends/cloud/onedrive/onedrivetokenrefresher.h b/backends/cloud/onedrive/onedrivetokenrefresher.h
index b32fd3f5582..7a6d83146aa 100644
--- a/backends/cloud/onedrive/onedrivetokenrefresher.h
+++ b/backends/cloud/onedrive/onedrivetokenrefresher.h
@@ -32,7 +32,6 @@ class OneDriveStorage;
 
 class OneDriveTokenRefresher: public Networking::HttpJsonRequest {
 	OneDriveStorage *_parentStorage;
-	Common::Array<Common::String> _headers;
 
 	void tokenRefreshed(const Storage::BoolResponse &response);
 
@@ -43,9 +42,6 @@ class OneDriveTokenRefresher: public Networking::HttpJsonRequest {
 public:
 	OneDriveTokenRefresher(OneDriveStorage *parent, Networking::JsonCallback callback, Networking::ErrorCallback ecb, const char *url);
 	~OneDriveTokenRefresher() override;
-
-	void setHeaders(const Common::Array<Common::String> &headers) override;
-	void addHeader(const Common::String &header) override;
 };
 
 } // End of namespace OneDrive
diff --git a/backends/module.mk b/backends/module.mk
index bd5a78ad515..afa3c96bb9a 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -62,7 +62,15 @@ MODULE_OBJS += \
 	cloud/onedrive/onedrivecreatedirectoryrequest.o \
 	cloud/onedrive/onedrivetokenrefresher.o \
 	cloud/onedrive/onedrivelistdirectoryrequest.o \
-	cloud/onedrive/onedriveuploadrequest.o
+	cloud/onedrive/onedriveuploadrequest.o \
+	networking/http/connectionmanager.o \
+	networking/http/networkreadstream.o \
+	networking/http/httpjsonrequest.o \
+	networking/http/httprequest.o \
+	networking/http/postrequest.o \
+	networking/http/request.o \
+	networking/http/session.o \
+	networking/http/sessionrequest.o
 endif
 
 ifdef USE_SCUMMVMDLC
@@ -79,16 +87,14 @@ endif
 
 ifdef USE_LIBCURL
 MODULE_OBJS += \
-	networking/http/connectionmanager.o \
-	networking/http/curl/networkreadstream-curl.o \
-	networking/http/curl/socket.o \
-	networking/http/curl/url.o \
-	networking/http/httpjsonrequest.o \
-	networking/http/httprequest.o \
-	networking/http/postrequest.o \
-	networking/http/request.o \
-	networking/http/session.o \
-	networking/http/sessionrequest.o 
+	networking/curl/cacert.o \
+	networking/curl/socket.o \
+	networking/curl/url.o
+ifdef USE_CLOUD
+MODULE_OBJS += \
+	networking/http/curl/connectionmanager-curl.o \
+	networking/http/curl/networkreadstream-curl.o
+endif
 endif
 
 ifdef EMSCRIPTEN
@@ -100,20 +106,15 @@ MODULE_OBJS += \
 ifdef USE_CLOUD
 MODULE_OBJS += \
 	fs/emscripten/cloud-fs.o \
-	networking/http/connectionmanager.o \
-	networking/http/httpjsonrequest.o \
-	networking/http/httprequest.o \
-	networking/http/postrequest.o \
-	networking/http/request.o \
-	networking/http/session.o \
-	networking/http/sessionrequest.o \
-	networking/http/emscripten/networkreadstream-emscripten.o 
+	networking/http/emscripten/connectionmanager-emscripten.o \
+	networking/http/emscripten/networkreadstream-emscripten.o
 endif
 ifdef USE_TTS
 MODULE_OBJS += \
 	text-to-speech/emscripten/emscripten-text-to-speech.o
 endif
 endif
+
 ifdef USE_SDL_NET
 MODULE_OBJS += \
 	networking/sdl_net/client.o \
diff --git a/backends/networking/curl/cacert.cpp b/backends/networking/curl/cacert.cpp
new file mode 100644
index 00000000000..313cfb483c1
--- /dev/null
+++ b/backends/networking/curl/cacert.cpp
@@ -0,0 +1,61 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if defined(ANDROID_BACKEND)
+#include "backends/platform/android/jni-android.h"
+#endif
+
+#include "backends/networking/curl/cacert.h"
+
+#include "common/fs.h"
+
+namespace Networking {
+
+Common::String getCaCertPath() {
+#if defined(ANDROID_BACKEND)
+	// cacert path must exist on filesystem and be reachable by standard open syscall
+	// Lets use ScummVM internal directory
+	Common::String assetsPath = JNI::getScummVMAssetsPath();
+	return assetsPath + "/cacert.pem";
+#elif defined(DATA_PATH)
+	static enum {
+		kNotInitialized,
+		kFileNotFound,
+		kFileExists
+	} state = kNotInitialized;
+
+	if (state == kNotInitialized) {
+		Common::FSNode node(DATA_PATH "/cacert.pem");
+		state = node.exists() ? kFileExists : kFileNotFound;
+	}
+
+	if (state == kFileExists) {
+		return DATA_PATH "/cacert.pem";
+	} else {
+		return "";
+	}
+#else
+	return "";
+#endif
+}
+
+
+} // End of namespace Networking
diff --git a/backends/networking/curl/cacert.h b/backends/networking/curl/cacert.h
new file mode 100644
index 00000000000..6f264b9c989
--- /dev/null
+++ b/backends/networking/curl/cacert.h
@@ -0,0 +1,33 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef BACKENDS_NETWORKING_CURL_CACERT_H
+#define BACKENDS_NETWORKING_CURL_CACERT_H
+
+#include "common/str.h"
+
+namespace Networking {
+
+/** Return the path to the CA certificates bundle. */
+Common::String getCaCertPath();
+
+}
+
+#endif
diff --git a/backends/networking/http/curl/socket.cpp b/backends/networking/curl/socket.cpp
similarity index 96%
rename from backends/networking/http/curl/socket.cpp
rename to backends/networking/curl/socket.cpp
index dc7f4cdfd72..5856724e0ec 100644
--- a/backends/networking/http/curl/socket.cpp
+++ b/backends/networking/curl/socket.cpp
@@ -21,8 +21,10 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 #include <curl/curl.h>
-#include "backends/networking/http/connectionmanager.h"
-#include "backends/networking/http/curl/socket.h"
+
+#include "backends/networking/curl/socket.h"
+#include "backends/networking/curl/cacert.h"
+
 #include "common/debug.h"
 #include "common/system.h"
 
@@ -83,7 +85,8 @@ bool CurlSocket::connect(const Common::String &url) {
 #if defined NINTENDO_SWITCH || defined PSP2
 		curl_easy_setopt(_easy, CURLOPT_SSL_VERIFYPEER, 0);
 #endif
-		Common::String caCertPath = ConnMan.getCaCertPath();
+
+		Common::String caCertPath = getCaCertPath();
 		if (!caCertPath.empty()) {
 			curl_easy_setopt(_easy, CURLOPT_CAINFO, caCertPath.c_str());
 		}
diff --git a/backends/networking/http/curl/socket.h b/backends/networking/curl/socket.h
similarity index 100%
rename from backends/networking/http/curl/socket.h
rename to backends/networking/curl/socket.h
diff --git a/backends/networking/http/curl/url.cpp b/backends/networking/curl/url.cpp
similarity index 98%
rename from backends/networking/http/curl/url.cpp
rename to backends/networking/curl/url.cpp
index ab84381a91e..ee33de51964 100644
--- a/backends/networking/http/curl/url.cpp
+++ b/backends/networking/curl/url.cpp
@@ -21,7 +21,9 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 #include <curl/curl.h>
-#include "backends/networking/http/curl/url.h"
+
+#include "backends/networking/curl/url.h"
+
 #include "common/debug.h"
 #include "common/textconsole.h"
 
diff --git a/backends/networking/http/curl/url.h b/backends/networking/curl/url.h
similarity index 100%
rename from backends/networking/http/curl/url.h
rename to backends/networking/curl/url.h
diff --git a/backends/networking/http/connectionmanager.cpp b/backends/networking/http/connectionmanager.cpp
index a1b08bc2cb6..88a37926ce6 100644
--- a/backends/networking/http/connectionmanager.cpp
+++ b/backends/networking/http/connectionmanager.cpp
@@ -19,44 +19,27 @@
  *
  */
 
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
 #include "backends/networking/http/connectionmanager.h"
 #include "backends/networking/http/networkreadstream.h"
 #include "common/debug.h"
-#include "common/fs.h"
 #include "common/system.h"
 #include "common/timer.h"
 
-#if defined(USE_LIBCURL)
-#include <curl/curl.h>
-#endif
-#if defined(EMSCRIPTEN)
-#include <emscripten.h>
-#endif
-#if defined(ANDROID_BACKEND)
-#include "backends/platform/android/jni-android.h"
-#endif
-
 namespace Common {
 
 DECLARE_SINGLETON(Networking::ConnectionManager);
 
-}
+/* The makeInstance function is defined in the platform specific source file */
 
-namespace Networking {
+} // namespace Common
 
-ConnectionManager::ConnectionManager(): _multi(nullptr), _timerStarted(false), _frame(0) {
-#ifdef USE_LIBCURL
-	curl_global_init(CURL_GLOBAL_ALL);
-	_multi = curl_multi_init();
-#endif
-}
+namespace Networking {
+ConnectionManager::ConnectionManager() : _timerStarted(false), _frame(0) {}
 
 ConnectionManager::~ConnectionManager() {
 	stopTimer();
 
-	//terminate all added requests which haven't been processed yet
+	// terminate all added requests which haven't been processed yet
 	_addedRequestsMutex.lock();
 	for (auto &curRequest : _addedRequests) {
 		Request *request = curRequest.request;
@@ -72,7 +55,7 @@ ConnectionManager::~ConnectionManager() {
 	_addedRequests.clear();
 	_addedRequestsMutex.unlock();
 
-	//terminate all requests
+	// terminate all requests
 	_handleMutex.lock();
 	for (auto &curRequest : _requests) {
 		Request *request = curRequest.request;
@@ -87,21 +70,9 @@ ConnectionManager::~ConnectionManager() {
 	}
 	_requests.clear();
 
-	//cleanup
-#ifdef USE_LIBCURL
-	curl_multi_cleanup(_multi);
-	curl_global_cleanup();
-	_multi = nullptr;
-#endif
 	_handleMutex.unlock();
 }
 
-#ifdef USE_LIBCURL
-void ConnectionManager::registerEasyHandle(CURL *easy) const {
-	curl_multi_add_handle(_multi, easy);
-}
-#endif
-
 Request *ConnectionManager::addRequest(Request *request, RequestCallback callback) {
 	_addedRequestsMutex.lock();
 	_addedRequests.push_back(RequestWithCallback(request, callback));
@@ -111,69 +82,11 @@ Request *ConnectionManager::addRequest(Request *request, RequestCallback callbac
 	return request;
 }
 
-Common::String ConnectionManager::urlEncode(const Common::String &s) const {
-	Common::String result = "";
-	debug(5, "ConnectionManager::urlEncode(%s)", s.c_str());
-#ifdef USE_LIBCURL
-	if (!_multi)
-		return "";
-#if LIBCURL_VERSION_NUM >= 0x070F04
-	char *output = curl_easy_escape(_multi, s.c_str(), s.size());
-#else
-	char *output = curl_escape(s.c_str(), s.size());
-#endif
-	if (output) {
-		result = output;
-		curl_free(output);
-	}
-#elif defined(EMSCRIPTEN)
-	const char *input = s.c_str();
-	result = (char*) EM_ASM_PTR({
-		var jsString = encodeURIComponent(UTF8ToString($0));
-		var size = lengthBytesUTF8(jsString) + 1;
-		var ret = Module._malloc(size);
-		stringToUTF8Array(jsString, HEAP8, ret, size);
-		return ret;
-	}, input);
-#endif
-	debug(5, "ConnectionManager::urlEncode(%s) = %s", s.c_str(), result.c_str());
-		
-	return result;
-}
-
 uint32 ConnectionManager::getCloudRequestsPeriodInMicroseconds() {
-	return TIMER_INTERVAL * CLOUD_PERIOD;
+	return TIMER_INTERVAL * ITERATION_PERIOD;
 }
 
-Common::String ConnectionManager::getCaCertPath() {
-#if defined(ANDROID_BACKEND)
-	// cacert path must exist on filesystem and be reachable by standard open syscall
-	// Lets use ScummVM internal directory
-	Common::String assetsPath = JNI::getScummVMAssetsPath();
-	return assetsPath + "/cacert.pem";
-#elif defined(DATA_PATH)
-	static enum {
-		kNotInitialized,
-		kFileNotFound,
-		kFileExists
-	} state = kNotInitialized;
-
-	if (state == kNotInitialized) {
-		Common::FSNode node(DATA_PATH"/cacert.pem");
-		state = node.exists() ? kFileExists : kFileNotFound;
-	}
-
-	if (state == kFileExists) {
-		return DATA_PATH"/cacert.pem";
-	} else {
-		return "";
-	}
-#else
-	return "";
-#endif
-}
-
-//private goes here:
+// private goes here:
 
 void connectionsThread(void *ignored) {
 	ConnMan.handle();
@@ -203,12 +116,12 @@ bool ConnectionManager::hasAddedRequests() {
 }
 
 void ConnectionManager::handle() {
-	//lock mutex here (in case another handle() would be called before this one ends)
+	// lock mutex here (in case another handle() would be called before this one ends)
 	_handleMutex.lock();
 	++_frame;
-	if (_frame % CLOUD_PERIOD == 0)
-		interateRequests();
-	if (_frame % CURL_PERIOD == 0)
+	if (_frame % ITERATION_PERIOD == 0)
+		iterateRequests();
+	if (_frame % PROCESSING_PERIOD == 0)
 		processTransfers();
 
 	if (_requests.empty() && !hasAddedRequests())
@@ -216,8 +129,8 @@ void ConnectionManager::handle() {
 	_handleMutex.unlock();
 }
 
-void ConnectionManager::interateRequests() {
-	//add new requests
+void ConnectionManager::iterateRequests() {
+	// add new requests
 	_addedRequestsMutex.lock();
 	for (auto &addedRequest : _addedRequests) {
 		_requests.push_back(addedRequest);
@@ -225,7 +138,7 @@ void ConnectionManager::interateRequests() {
 	_addedRequests.clear();
 	_addedRequestsMutex.unlock();
 
-	//call handle() of all running requests (so they can do their work)
+	// call handle() of all running requests (so they can do their work)
 	if (_frame % DEBUG_PRINT_PERIOD == 0)
 		debug(9, "handling %d request(s)", _requests.size());
 	for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end();) {
@@ -240,7 +153,7 @@ void ConnectionManager::interateRequests() {
 		if (!request || request->state() == FINISHED) {
 			delete (i->request);
 			if (i->onDeleteCallback) {
-				(*i->onDeleteCallback)(i->request); //that's not a mistake (we're passing an address and that method knows there is no object anymore)
+				(*i->onDeleteCallback)(i->request); // that's not a mistake (we're passing an address and that method knows there is no object anymore)
 				delete i->onDeleteCallback;
 			}
 			_requests.erase(i);
@@ -251,32 +164,4 @@ void ConnectionManager::interateRequests() {
 	}
 }
 
-void ConnectionManager::processTransfers() {
-#ifdef USE_LIBCURL
-	if (!_multi) return;
-
-	//check libcurl's transfers and notify requests of messages from queue (transfer completion or failure)
-	int transfersRunning;
-	curl_multi_perform(_multi, &transfersRunning);
-
-	int messagesInQueue;
-	CURLMsg *curlMsg;
-	while ((curlMsg = curl_multi_info_read(_multi, &messagesInQueue))) {
-		if (curlMsg->msg == CURLMSG_DONE) {
-			CURL *easyHandle = curlMsg->easy_handle;
-
-			NetworkReadStream *stream = nullptr;
-			curl_easy_getinfo(easyHandle, CURLINFO_PRIVATE, &stream);
-
-			if (stream)
-				stream->finished(curlMsg->data.result);
-
-			curl_multi_remove_handle(_multi, easyHandle);
-		} else {
-			warning("Unknown libcurl message type %d", curlMsg->msg);
-		}
-	}
-#endif 
-}
-
-} // End of namespace Cloud
+} // End of namespace Networking
diff --git a/backends/networking/http/connectionmanager.h b/backends/networking/http/connectionmanager.h
index ff99baed116..908bd800c2a 100644
--- a/backends/networking/http/connectionmanager.h
+++ b/backends/networking/http/connectionmanager.h
@@ -23,27 +23,21 @@
 #define BACKENDS_NETWORKING_HTTP_CONNECTIONMANAGER_H
 
 #include "backends/networking/http/request.h"
-#include "common/str.h"
-#include "common/singleton.h"
 #include "common/hashmap.h"
 #include "common/mutex.h"
-
-typedef void CURL;
-typedef void CURLM;
-struct curl_slist;
+#include "common/singleton.h"
+#include "common/str.h"
 
 namespace Networking {
 
-class NetworkReadStream;
-
-class ConnectionManager : public Common::Singleton<ConnectionManager> {
+class ConnectionManager : public Common::Singleton<Networking::ConnectionManager> {
 	static const uint32 FRAMES_PER_SECOND = 100;
 	static const uint32 TIMER_INTERVAL = 1000000 / FRAMES_PER_SECOND;
-	static const uint32 CLOUD_PERIOD = 1; //every frame
-	static const uint32 CURL_PERIOD = 1; //every frame
+	static const uint32 ITERATION_PERIOD = 1;                       // every frame
+	static const uint32 PROCESSING_PERIOD = 1;                        // every frame
 	static const uint32 DEBUG_PRINT_PERIOD = FRAMES_PER_SECOND; // once per second
 
-	friend void connectionsThread(void *); //calls handle()
+	friend void connectionsThread(void *); // calls handle()
 
 	typedef Common::BaseCallback<Request *> *RequestCallback;
 
@@ -71,10 +65,9 @@ class ConnectionManager : public Common::Singleton<ConnectionManager> {
 		Request *request;
 		RequestCallback onDeleteCallback;
 
-		RequestWithCallback(Request *rq = nullptr, RequestCallback cb = nullptr): request(rq), onDeleteCallback(cb) {}
+		RequestWithCallback(Request *rq = nullptr, RequestCallback cb = nullptr) : request(rq), onDeleteCallback(cb) {}
 	};
 
-	CURLM *_multi;
 	bool _timerStarted;
 	Common::Array<RequestWithCallback> _requests, _addedRequests;
 	Common::Mutex _handleMutex, _addedRequestsMutex;
@@ -83,22 +76,13 @@ class ConnectionManager : public Common::Singleton<ConnectionManager> {
 	void startTimer(int interval = TIMER_INTERVAL);
 	void stopTimer();
 	void handle();
-	void interateRequests();
-	void processTransfers();
+	void iterateRequests();
+	virtual void processTransfers() = 0;
 	bool hasAddedRequests();
 
 public:
 	ConnectionManager();
-	~ConnectionManager() override;
-
-#ifdef USE_LIBCURL
-	/**
-	 * All libcurl transfers are going through this ConnectionManager.
-	 * So, if you want to start any libcurl transfer, you must create
-	 * an easy handle and register it using this method.
-	 */
-	void registerEasyHandle(CURL *easy) const;
-#endif
+	~ConnectionManager();
 
 	/**
 	 * Use this method to add new Request into manager's queue.
@@ -116,17 +100,19 @@ public:
 	Request *addRequest(Request *request, RequestCallback callback = nullptr);
 
 	/** Return URL-encoded version of given string. */
-	Common::String urlEncode(const Common::String &s) const;
+	virtual Common::String urlEncode(const Common::String &s) const = 0;
 
 	static uint32 getCloudRequestsPeriodInMicroseconds();
-
-	/** Return the path to the CA certificates bundle. */
-	static Common::String getCaCertPath();
 };
 
 /** Shortcut for accessing the connection manager. */
-#define ConnMan     Networking::ConnectionManager::instance()
+#define ConnMan Networking::ConnectionManager::instance()
 
 } // End of namespace Networking
 
+namespace Common {
+template<>
+Networking::ConnectionManager *Singleton<Networking::ConnectionManager>::makeInstance();
+} // End of namespace Common
+
 #endif
diff --git a/backends/networking/http/curl/connectionmanager-curl.cpp b/backends/networking/http/curl/connectionmanager-curl.cpp
new file mode 100644
index 00000000000..af12417a582
--- /dev/null
+++ b/backends/networking/http/curl/connectionmanager-curl.cpp
@@ -0,0 +1,108 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "backends/networking/http/curl/connectionmanager-curl.h"
+#include "backends/networking/http/curl/networkreadstream-curl.h"
+#include "common/debug.h"
+#include "common/system.h"
+#include "common/timer.h"
+
+namespace Common {
+
+template<>
+Networking::ConnectionManager *Singleton<Networking::ConnectionManager>::makeInstance() {
+	return new Networking::ConnectionManagerCurl();
+}
+
+} // namespace Common
+
+namespace Networking {
+/* Workaround a MSVC bug from MSVC 2015
+ * The compiler considers this template specialization as inline.
+ * If this TU doesn't use the function, it is then discarded.
+ */
+#if defined(_MSC_VER) && (_MSC_VER >= 1900)
+void dummyFunction() {
+	ConnMan;
+}
+#endif
+
+ConnectionManagerCurl::ConnectionManagerCurl() : ConnectionManager(), _multi(nullptr) {
+	curl_global_init(CURL_GLOBAL_ALL);
+	_multi = curl_multi_init();
+}
+
+ConnectionManagerCurl::~ConnectionManagerCurl() {
+	// cleanup
+	curl_multi_cleanup(_multi);
+	curl_global_cleanup();
+	_multi = nullptr;
+}
+
+void ConnectionManagerCurl::registerEasyHandle(CURL *easy) const {
+	curl_multi_add_handle(_multi, easy);
+}
+
+Common::String ConnectionManagerCurl::urlEncode(const Common::String &s) const {
+	if (!_multi)
+		return "";
+#if LIBCURL_VERSION_NUM >= 0x070F04
+	char *output = curl_easy_escape(_multi, s.c_str(), s.size());
+#else
+	char *output = curl_escape(s.c_str(), s.size());
+#endif
+	if (output) {
+		Common::String result = output;
+		curl_free(output);
+		return result;
+	}
+	return "";
+}
+
+// private goes here:
+void ConnectionManagerCurl::processTransfers() {
+	if (!_multi)
+		return;
+
+	// check libcurl's transfers and notify requests of messages from queue (transfer completion or failure)
+	int transfersRunning;
+	curl_multi_perform(_multi, &transfersRunning);
+
+	int messagesInQueue;
+	CURLMsg *curlMsg;
+	while ((curlMsg = curl_multi_info_read(_multi, &messagesInQueue))) {
+		if (curlMsg->msg == CURLMSG_DONE) {
+			CURL *easyHandle = curlMsg->easy_handle;
+
+			NetworkReadStreamCurl *stream = nullptr;
+			curl_easy_getinfo(easyHandle, CURLINFO_PRIVATE, &stream);
+
+			if (stream)
+				stream->finished(curlMsg->data.result);
+
+			curl_multi_remove_handle(_multi, easyHandle);
+		} else {
+			warning("Unknown libcurl message type %d", curlMsg->msg);
+		}
+	}
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/http/curl/connectionmanager-curl.h b/backends/networking/http/curl/connectionmanager-curl.h
new file mode 100644
index 00000000000..ad8ea827d4f
--- /dev/null
+++ b/backends/networking/http/curl/connectionmanager-curl.h
@@ -0,0 +1,57 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BACKENDS_NETWORKING_HTTP_CURL_CONNECTIONMANAGERCURL_H
+#define BACKENDS_NETWORKING_HTTP_CURL_CONNECTIONMANAGERCURL_H
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/networking/http/connectionmanager.h"
+
+#include <curl/curl.h>
+
+namespace Networking {
+
+class ConnectionManagerCurl : public ConnectionManager {
+private:
+	CURLM *_multi;
+
+	void processTransfers() override;
+
+public:
+	ConnectionManagerCurl();
+	~ConnectionManagerCurl() override;
+
+	/**
+	 * All libcurl transfers are going through this ConnectionManager.
+	 * So, if you want to start any libcurl transfer, you must create
+	 * an easy handle and register it using this method.
+	 */
+	void registerEasyHandle(CURL *easy) const;
+
+	/** Return URL-encoded version of given string. */
+	Common::String urlEncode(const Common::String &s) const override;
+
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/http/curl/networkreadstream-curl.cpp b/backends/networking/http/curl/networkreadstream-curl.cpp
index 61801c091ea..c1d4137b489 100644
--- a/backends/networking/http/curl/networkreadstream-curl.cpp
+++ b/backends/networking/http/curl/networkreadstream-curl.cpp
@@ -22,48 +22,60 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 #define CURL_DISABLE_DEPRECATION
 
-#include <curl/curl.h>
-#include "backends/networking/http/networkreadstream.h"
-#include "backends/networking/http/connectionmanager.h"
+#include "backends/networking/curl/cacert.h"
+#include "backends/networking/http/curl/networkreadstream-curl.h"
+#include "backends/networking/http/curl/connectionmanager-curl.h"
 #include "base/version.h"
 #include "common/debug.h"
 
 namespace Networking {
 
-size_t NetworkReadStream::curlDataCallback(char *d, size_t n, size_t l, void *p) {
-	NetworkReadStream *stream = (NetworkReadStream *)p;
+NetworkReadStream *NetworkReadStream::make(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading, bool usingPatch, bool keepAlive, long keepAliveIdle, long keepAliveInterval) {
+	return new NetworkReadStreamCurl(url, headersList, postFields, uploading, usingPatch, keepAlive, keepAliveIdle, keepAliveInterval);
+}
+
+NetworkReadStream *NetworkReadStream::make(const char *url, RequestHeaders *headersList, const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles, bool keepAlive, long keepAliveIdle, long keepAliveInterval) {
+	return new NetworkReadStreamCurl(url, headersList, formFields, formFiles, keepAlive, keepAliveIdle, keepAliveInterval);
+}
+
+NetworkReadStream *NetworkReadStream::make(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post, bool keepAlive, long keepAliveIdle, long keepAliveInterval) {
+	return new NetworkReadStreamCurl(url, headersList, buffer, bufferSize, uploading, usingPatch, post, keepAlive, keepAliveIdle, keepAliveInterval);
+}
+
+size_t NetworkReadStreamCurl::curlDataCallback(char *d, size_t n, size_t l, void *p) {
+	NetworkReadStreamCurl *stream = (NetworkReadStreamCurl *)p;
 	if (stream)
 		return stream->_backingStream.write(d, n * l);
 	return 0;
 }
 
-size_t NetworkReadStream::curlReadDataCallback(char *d, size_t n, size_t l, void *p) {
-	NetworkReadStream *stream = (NetworkReadStream *)p;
+size_t NetworkReadStreamCurl::curlReadDataCallback(char *d, size_t n, size_t l, void *p) {
+	NetworkReadStreamCurl *stream = (NetworkReadStreamCurl *)p;
 	if (stream)
 		return stream->fillWithSendingContents(d, n * l);
 	return 0;
 }
 
-size_t NetworkReadStream::curlHeadersCallback(char *d, size_t n, size_t l, void *p) {
-	NetworkReadStream *stream = (NetworkReadStream *)p;
+size_t NetworkReadStreamCurl::curlHeadersCallback(char *d, size_t n, size_t l, void *p) {
+	NetworkReadStreamCurl *stream = (NetworkReadStreamCurl *)p;
 	if (stream)
 		return stream->addResponseHeaders(d, n * l);
 	return 0;
 }
 
 static int curlProgressCallback(void *p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
-	NetworkReadStream *stream = (NetworkReadStream *)p;
+	NetworkReadStreamCurl *stream = (NetworkReadStreamCurl *)p;
 	if (stream)
 		stream->setProgress(dlnow, dltotal);
 	return 0;
 }
 
-int NetworkReadStream::curlProgressCallbackOlder(void *p, double dltotal, double dlnow, double ultotal, double ulnow) {
+int NetworkReadStreamCurl::curlProgressCallbackOlder(void *p, double dltotal, double dlnow, double ultotal, double ulnow) {
 	// for libcurl older than 7.32.0 (CURLOPT_PROGRESSFUNCTION)
 	return curlProgressCallback(p, (curl_off_t)dltotal, (curl_off_t)dlnow, (curl_off_t)ultotal, (curl_off_t)ulnow);
 }
 
-void NetworkReadStream::resetStream() {
+void NetworkReadStreamCurl::resetStream() {
 	_eos = _requestComplete = false;
 	if (!_errorBuffer)
 		_errorBuffer = (char *)calloc(CURL_ERROR_SIZE, 1);
@@ -77,41 +89,35 @@ void NetworkReadStream::resetStream() {
 	}
 }
 
-curl_slist *NetworkReadStream::requestHeadersToSlist(const RequestHeaders *headersList) {
-	curl_slist *slist = nullptr;
-	if (headersList) {
-		for (const Common::String &header : *headersList) {
-			slist = curl_slist_append(slist, header.c_str());
-		}
-	}
-	return slist;
-}
-
-void NetworkReadStream::initCurl(const char *url, RequestHeaders *headersList) {
+void NetworkReadStreamCurl::initCurl(const char *url, RequestHeaders *headersList) {
 	resetStream();
 
 	_easy = curl_easy_init();
-	_headersSlist = requestHeadersToSlist(headersList);
 	curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
-	curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); //so callback can call us
-	curl_easy_setopt(_easy, CURLOPT_PRIVATE, this); //so ConnectionManager can call us when request is complete
+	curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); // so callback can call us
+	curl_easy_setopt(_easy, CURLOPT_PRIVATE, this);   // so ConnectionManager can call us when request is complete
 	curl_easy_setopt(_easy, CURLOPT_HEADER, 0L);
 	curl_easy_setopt(_easy, CURLOPT_HEADERDATA, this);
 	curl_easy_setopt(_easy, CURLOPT_HEADERFUNCTION, curlHeadersCallback);
 	curl_easy_setopt(_easy, CURLOPT_URL, url);
 	curl_easy_setopt(_easy, CURLOPT_ERRORBUFFER, _errorBuffer);
 	curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
-	curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
+	curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); // probably it's OK to have it always on
+
+	// Convert headers to curl_slist format
+	_headersSlist = requestHeadersToSlist(headersList);
 	curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, _headersSlist);
+
 	curl_easy_setopt(_easy, CURLOPT_USERAGENT, gScummVMFullVersion);
 	curl_easy_setopt(_easy, CURLOPT_NOPROGRESS, 0L);
 	curl_easy_setopt(_easy, CURLOPT_PROGRESSFUNCTION, curlProgressCallbackOlder);
 	curl_easy_setopt(_easy, CURLOPT_PROGRESSDATA, this);
+
 #if defined NINTENDO_SWITCH || defined PSP2
 	curl_easy_setopt(_easy, CURLOPT_SSL_VERIFYPEER, 0);
 #endif
 
-	Common::String caCertPath = ConnMan.getCaCertPath();
+	Common::String caCertPath = getCaCertPath();
 	if (!caCertPath.empty()) {
 		curl_easy_setopt(_easy, CURLOPT_CAINFO, caCertPath.c_str());
 	}
@@ -133,7 +139,7 @@ void NetworkReadStream::initCurl(const char *url, RequestHeaders *headersList) {
 #endif
 }
 
-bool NetworkReadStream::reuseCurl(const char *url, RequestHeaders *headersList) {
+bool NetworkReadStreamCurl::reuseCurl(const char *url, RequestHeaders *headersList) {
 	if (!_keepAlive) {
 		warning("NetworkReadStream: Can't reuse curl handle (was not setup as keep-alive)");
 		return false;
@@ -148,8 +154,7 @@ bool NetworkReadStream::reuseCurl(const char *url, RequestHeaders *headersList)
 
 	return true;
 }
-
-void NetworkReadStream::setupBufferContents(const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
+void NetworkReadStreamCurl::setupBufferContents(const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
 	if (uploading) {
 		curl_easy_setopt(_easy, CURLOPT_UPLOAD, 1L);
 		curl_easy_setopt(_easy, CURLOPT_READDATA, this);
@@ -165,17 +170,16 @@ void NetworkReadStream::setupBufferContents(const byte *buffer, uint32 bufferSiz
 			// CURLOPT_COPYPOSTFIELDS available since curl 7.17.1
 			curl_easy_setopt(_easy, CURLOPT_COPYPOSTFIELDS, buffer);
 #else
-			_bufferCopy = (byte*)malloc(bufferSize);
+			_bufferCopy = (byte *)malloc(bufferSize);
 			memcpy(_bufferCopy, buffer, bufferSize);
 			curl_easy_setopt(_easy, CURLOPT_POSTFIELDS, _bufferCopy);
 #endif
 		}
 	}
-	ConnMan.registerEasyHandle(_easy);
+	dynamic_cast<ConnectionManagerCurl &>(ConnMan).registerEasyHandle(_easy);
 }
 
-void NetworkReadStream::setupFormMultipart(const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles) {
-	// set POST multipart upload form fields/files
+void NetworkReadStreamCurl::setupFormMultipart(const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles) {
 	struct curl_httppost *formpost = nullptr;
 	struct curl_httppost *lastptr = nullptr;
 
@@ -185,11 +189,10 @@ void NetworkReadStream::setupFormMultipart(const Common::HashMap<Common::String,
 			&lastptr,
 			CURLFORM_COPYNAME, i->_key.c_str(),
 			CURLFORM_COPYCONTENTS, i->_value.c_str(),
-			CURLFORM_END
-		);
+			CURLFORM_END);
 
 		if (code != CURL_FORMADD_OK)
-			warning("NetworkReadStream: field curl_formadd('%s') failed", i->_key.c_str());
+			warning("NetworkReadStreamCurl: field curl_formadd('%s') failed", i->_key.c_str());
 	}
 
 	for (Common::HashMap<Common::String, Common::Path>::iterator i = formFiles.begin(); i != formFiles.end(); ++i) {
@@ -198,42 +201,48 @@ void NetworkReadStream::setupFormMultipart(const Common::HashMap<Common::String,
 			&lastptr,
 			CURLFORM_COPYNAME, i->_key.c_str(),
 			CURLFORM_FILE, i->_value.toString(Common::Path::kNativeSeparator).c_str(),
-			CURLFORM_END
-		);
+			CURLFORM_END);
 
 		if (code != CURL_FORMADD_OK)
-			warning("NetworkReadStream: file curl_formadd('%s') failed", i->_key.c_str());
+			warning("NetworkReadStreamCurl: file curl_formadd('%s') failed", i->_key.c_str());
 	}
 
 	curl_easy_setopt(_easy, CURLOPT_HTTPPOST, formpost);
-	ConnMan.registerEasyHandle(_easy);
+	dynamic_cast<ConnectionManagerCurl &>(ConnMan).registerEasyHandle(_easy);
 }
 
-NetworkReadStream::NetworkReadStream(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading, bool usingPatch, bool keepAlive, long keepAliveIdle, long keepAliveInterval):
-		_backingStream(DisposeAfterUse::YES), _keepAlive(keepAlive), _keepAliveIdle(keepAliveIdle), _keepAliveInterval(keepAliveInterval), _errorBuffer(nullptr), _errorCode(CURLE_OK)
-		, _headersSlist(nullptr)
-	{
+NetworkReadStreamCurl::NetworkReadStreamCurl(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading, bool usingPatch, bool keepAlive, long keepAliveIdle, long keepAliveInterval)
+	: NetworkReadStream(keepAlive, keepAliveIdle, keepAliveInterval),
+	_errorBuffer(nullptr), _headersSlist(nullptr) {
 	initCurl(url, headersList);
 	setupBufferContents((const byte *)postFields.c_str(), postFields.size(), uploading, usingPatch, false);
 }
 
-NetworkReadStream::NetworkReadStream(const char *url, RequestHeaders *headersList, const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles, bool keepAlive, long keepAliveIdle, long keepAliveInterval):
-		_backingStream(DisposeAfterUse::YES), _keepAlive(keepAlive), _keepAliveIdle(keepAliveIdle), _keepAliveInterval(keepAliveInterval), _errorBuffer(nullptr), _errorCode(CURLE_OK)
-		, _headersSlist(nullptr)
-	{
+NetworkReadStreamCurl::NetworkReadStreamCurl(const char *url, RequestHeaders *headersList, const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles, bool keepAlive, long keepAliveIdle, long keepAliveInterval)
+	: NetworkReadStream(keepAlive, keepAliveIdle, keepAliveInterval),
+	_errorBuffer(nullptr), _headersSlist(nullptr) {
 	initCurl(url, headersList);
 	setupFormMultipart(formFields, formFiles);
 }
 
-NetworkReadStream::NetworkReadStream(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post, bool keepAlive, long keepAliveIdle, long keepAliveInterval):
-		_backingStream(DisposeAfterUse::YES), _keepAlive(keepAlive), _keepAliveIdle(keepAliveIdle), _keepAliveInterval(keepAliveInterval), _errorBuffer(nullptr), _errorCode(CURLE_OK)
-		, _headersSlist(nullptr)
-	{
+NetworkReadStreamCurl::NetworkReadStreamCurl(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post, bool keepAlive, long keepAliveIdle, long keepAliveInterval)
+	: NetworkReadStream(keepAlive, keepAliveIdle, keepAliveInterval),
+	_errorBuffer(nullptr), _headersSlist(nullptr) {
 	initCurl(url, headersList);
 	setupBufferContents(buffer, bufferSize, uploading, usingPatch, post);
 }
 
-bool NetworkReadStream::reuse(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading, bool usingPatch) {
+curl_slist *NetworkReadStreamCurl::requestHeadersToSlist(const RequestHeaders *headersList) {
+	curl_slist *slist = nullptr;
+	if (headersList) {
+		for (const Common::String &header : *headersList) {
+			slist = curl_slist_append(slist, header.c_str());
+		}
+	}
+	return slist;
+}
+
+bool NetworkReadStreamCurl::reuse(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading, bool usingPatch) {
 	if (!reuseCurl(url, headersList))
 		return false;
 
@@ -242,7 +251,7 @@ bool NetworkReadStream::reuse(const char *url, RequestHeaders *headersList, cons
 	return true;
 }
 
-bool NetworkReadStream::reuse(const char *url, RequestHeaders *headersList, const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles) {
+bool NetworkReadStreamCurl::reuse(const char *url, RequestHeaders *headersList, const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles) {
 	if (!reuseCurl(url, headersList))
 		return false;
 
@@ -251,7 +260,7 @@ bool NetworkReadStream::reuse(const char *url, RequestHeaders *headersList, cons
 	return true;
 }
 
-bool NetworkReadStream::reuse(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
+bool NetworkReadStreamCurl::reuse(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
 	if (!reuseCurl(url, headersList))
 		return false;
 
@@ -260,30 +269,17 @@ bool NetworkReadStream::reuse(const char *url, RequestHeaders *headersList, cons
 	return true;
 }
 
-NetworkReadStream::~NetworkReadStream() {
+NetworkReadStreamCurl::~NetworkReadStreamCurl() {
 	if (_easy)
 		curl_easy_cleanup(_easy);
 	free(_bufferCopy);
 	free(_errorBuffer);
-}
-
-bool NetworkReadStream::eos() const {
-	return _eos;
-}
-
-uint32 NetworkReadStream::read(void *dataPtr, uint32 dataSize) {
-	uint32 actuallyRead = _backingStream.read(dataPtr, dataSize);
-
-	if (actuallyRead == 0) {
-		if (_requestComplete)
-			_eos = true;
-		return 0;
+	if (_headersSlist) {
+		curl_slist_free_all(_headersSlist);
 	}
-
-	return actuallyRead;
 }
 
-void NetworkReadStream::finished(uint32 errorCode) {
+void NetworkReadStreamCurl::finished(CURLcode errorCode) {
 	_requestComplete = true;
 
 	char *url = nullptr;
@@ -292,28 +288,28 @@ void NetworkReadStream::finished(uint32 errorCode) {
 	_errorCode = errorCode;
 
 	if (_errorCode == CURLE_OK) {
-		debug(9, "NetworkReadStream: %s - Request succeeded", url);
+		debug(9, "NetworkReadStreamCurl: %s - Request succeeded", url);
 	} else {
-		warning("NetworkReadStream: %s - Request failed (%d - %s)", url, _errorCode, getError());
+		warning("NetworkReadStreamCurl: %s - Request failed (%d - %s)", url, _errorCode, getError());
 	}
 }
 
-bool NetworkReadStream::hasError() const {
+bool NetworkReadStreamCurl::hasError() const {
 	return _errorCode != CURLE_OK;
 }
 
-const char *NetworkReadStream::getError() const {
-	return strlen(_errorBuffer) ? _errorBuffer : curl_easy_strerror((CURLcode)_errorCode);
+const char *NetworkReadStreamCurl::getError() const {
+	return strlen(_errorBuffer) ? _errorBuffer : curl_easy_strerror(_errorCode);
 }
 
-long NetworkReadStream::httpResponseCode() const {
+long NetworkReadStreamCurl::httpResponseCode() const {
 	long responseCode = -1;
 	if (_easy)
 		curl_easy_getinfo(_easy, CURLINFO_RESPONSE_CODE, &responseCode);
 	return responseCode;
 }
 
-Common::String NetworkReadStream::currentLocation() const {
+Common::String NetworkReadStreamCurl::currentLocation() const {
 	Common::String result = "";
 	if (_easy) {
 		char *pointer;
@@ -323,11 +319,7 @@ Common::String NetworkReadStream::currentLocation() const {
 	return result;
 }
 
-Common::String NetworkReadStream::responseHeaders() const {
-	return _responseHeaders;
-}
-
-Common::HashMap<Common::String, Common::String> NetworkReadStream::responseHeadersMap() const {
+Common::HashMap<Common::String, Common::String> NetworkReadStreamCurl::responseHeadersMap() const {
 	// HTTP headers are described at RFC 2616: https://datatracker.ietf.org/doc/html/rfc2616#section-4.2
 	// this implementation tries to follow it, but for simplicity it does not support multi-line header values
 
@@ -399,31 +391,4 @@ Common::HashMap<Common::String, Common::String> NetworkReadStream::responseHeade
 	return headers;
 }
 
-uint32 NetworkReadStream::fillWithSendingContents(char *bufferToFill, uint32 maxSize) {
-	uint32 sendSize = _sendingContentsSize - _sendingContentsPos;
-	if (sendSize > maxSize)
-		sendSize = maxSize;
-	for (uint32 i = 0; i < sendSize; ++i) {
-		bufferToFill[i] = _sendingContentsBuffer[_sendingContentsPos + i];
-	}
-	_sendingContentsPos += sendSize;
-	return sendSize;
-}
-
-uint32 NetworkReadStream::addResponseHeaders(char *buffer, uint32 bufferSize) {
-	_responseHeaders += Common::String(buffer, bufferSize);
-	return bufferSize;
-}
-
-double NetworkReadStream::getProgress() const {
-	if (_progressTotal < 1)
-		return 0;
-	return (double)_progressDownloaded / (double)_progressTotal;
-}
-
-void NetworkReadStream::setProgress(uint64 downloaded, uint64 total) {
-	_progressDownloaded = downloaded;
-	_progressTotal = total;
-}
-
-} // End of namespace Cloud
+} // End of namespace Networking
diff --git a/backends/networking/http/curl/networkreadstream-curl.h b/backends/networking/http/curl/networkreadstream-curl.h
new file mode 100644
index 00000000000..6473b4699f4
--- /dev/null
+++ b/backends/networking/http/curl/networkreadstream-curl.h
@@ -0,0 +1,98 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BACKENDS_NETWORKING_HTTP_CURL_NETWORKREADSTREAMCURL_H
+#define BACKENDS_NETWORKING_HTTP_CURL_NETWORKREADSTREAMCURL_H
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/networking/http/networkreadstream.h"
+#include "common/hash-str.h"
+#include "common/hashmap.h"
+#include "common/memstream.h"
+#include "common/path.h"
+#include "common/str.h"
+#include "common/stream.h"
+
+#include <curl/curl.h>
+
+namespace Networking {
+
+class NetworkReadStreamCurl : public NetworkReadStream {
+private:
+	CURL *_easy;
+	struct curl_slist *_headersSlist;
+	char *_errorBuffer;
+	CURLcode _errorCode;
+	byte *_bufferCopy; // To use with old curl version where CURLOPT_COPYPOSTFIELDS is not available
+	void initCurl(const char *url, RequestHeaders *headersList);
+	bool reuseCurl(const char *url, RequestHeaders *headersList);
+	void setupBufferContents(const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) override;
+	void setupFormMultipart(const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles) override;
+	static struct curl_slist *requestHeadersToSlist(const RequestHeaders *headersList);
+
+	static size_t curlDataCallback(char *d, size_t n, size_t l, void *p);
+	static size_t curlReadDataCallback(char *d, size_t n, size_t l, void *p);
+	static size_t curlHeadersCallback(char *d, size_t n, size_t l, void *p);
+	static int curlProgressCallbackOlder(void *p, double dltotal, double dlnow, double ultotal, double ulnow);
+
+	// CURL-specific methods
+	CURL *getEasyHandle() const { return _easy; }
+
+public:
+	NetworkReadStreamCurl(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading, bool usingPatch, bool keepAlive, long keepAliveIdle, long keepAliveInterval);
+
+	NetworkReadStreamCurl(const char *url, RequestHeaders *headersList, const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles, bool keepAlive, long keepAliveIdle, long keepAliveInterval);
+
+	NetworkReadStreamCurl(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post, bool keepAlive, long keepAliveIdle, long keepAliveInterval);
+
+	~NetworkReadStreamCurl();
+	void finished(CURLcode errorCode);
+
+	/** Send <postFields>, using POST by default. */
+	bool reuse(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading = false, bool usingPatch = false) override;
+	/** Send <formFields>, <formFiles>, using POST multipart/form. */
+	bool reuse(
+		const char *url, RequestHeaders *headersList,
+		const Common::HashMap<Common::String, Common::String> &formFields,
+		const Common::HashMap<Common::String, Common::Path> &formFiles) override;
+	/** Send <buffer>, using POST by default. */
+	bool reuse(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading = false, bool usingPatch = false, bool post = true) override;
+
+	void resetStream() override;
+
+	long httpResponseCode() const override;
+	Common::String currentLocation() const override;
+	/**
+	 * Return response headers as HashMap. All header names in
+	 * it are lowercase.
+	 *
+	 * @note This method should be called when eos() == true.
+	 */
+	Common::HashMap<Common::String, Common::String> responseHeadersMap() const override;
+
+	bool hasError() const override;
+	const char *getError() const override;
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/http/emscripten/connectionmanager-emscripten.cpp b/backends/networking/http/emscripten/connectionmanager-emscripten.cpp
new file mode 100644
index 00000000000..b0c877729b7
--- /dev/null
+++ b/backends/networking/http/emscripten/connectionmanager-emscripten.cpp
@@ -0,0 +1,72 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef EMSCRIPTEN
+
+#define FORBIDDEN_SYMBOL_EXCEPTION_asctime
+#define FORBIDDEN_SYMBOL_EXCEPTION_clock
+#define FORBIDDEN_SYMBOL_EXCEPTION_ctime
+#define FORBIDDEN_SYMBOL_EXCEPTION_difftime
+#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
+#define FORBIDDEN_SYMBOL_EXCEPTION_getdate
+#define FORBIDDEN_SYMBOL_EXCEPTION_gmtime
+#define FORBIDDEN_SYMBOL_EXCEPTION_localtime
+#define FORBIDDEN_SYMBOL_EXCEPTION_mktime
+#define FORBIDDEN_SYMBOL_EXCEPTION_strcpy
+#define FORBIDDEN_SYMBOL_EXCEPTION_strdup
+#define FORBIDDEN_SYMBOL_EXCEPTION_time
+
+#include "backends/networking/http/emscripten/connectionmanager-emscripten.h"
+#include "common/debug.h"
+#include <emscripten.h>
+
+namespace Common {
+
+template<>
+Networking::ConnectionManager *Singleton<Networking::ConnectionManager>::makeInstance() {
+	return new Networking::ConnectionManagerEmscripten();
+}
+
+} // namespace Common
+
+namespace Networking {
+
+Common::String ConnectionManagerEmscripten::urlEncode(const Common::String &s) const {
+	const char *input = s.c_str();
+	char *result = (char *)EM_ASM_PTR({
+			var jsString = encodeURIComponent(UTF8ToString($0));
+			var size = lengthBytesUTF8(jsString) + 1;
+			var ret = Module._malloc(size);
+			stringToUTF8Array(jsString, HEAP8, ret, size);
+			return ret; }, input);
+
+	Common::String encodedResult = Common::String(result);
+	free(result); // Free the malloc'd memory from EM_ASM_PTR
+	return encodedResult;
+}
+
+void ConnectionManagerEmscripten::processTransfers() {
+	// Emscripten handles transfers asynchronously via callbacks
+	// No action needed here
+}
+} // End of namespace Networking
+
+#endif // EMSCRIPTEN
diff --git a/backends/networking/http/emscripten/connectionmanager-emscripten.h b/backends/networking/http/emscripten/connectionmanager-emscripten.h
new file mode 100644
index 00000000000..f4deb4fca55
--- /dev/null
+++ b/backends/networking/http/emscripten/connectionmanager-emscripten.h
@@ -0,0 +1,42 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BACKENDS_NETWORKING_HTTP_EMSCRIPTEN_CONNECTIONMANAGEREMSCRIPTEN_H
+#define BACKENDS_NETWORKING_HTTP_EMSCRIPTEN_CONNECTIONMANAGEREMSCRIPTEN_H
+
+#ifdef EMSCRIPTEN
+
+#include "backends/networking/http/connectionmanager.h"
+
+namespace Networking {
+
+class ConnectionManagerEmscripten : public ConnectionManager {
+
+public:
+	void processTransfers() override;
+	Common::String urlEncode(const Common::String &s) const override;
+};
+
+} // End of namespace Networking
+
+#endif // EMSCRIPTEN
+
+#endif
diff --git a/backends/networking/http/emscripten/networkreadstream-emscripten.cpp b/backends/networking/http/emscripten/networkreadstream-emscripten.cpp
index 6d4f7216ced..9dede63d44d 100644
--- a/backends/networking/http/emscripten/networkreadstream-emscripten.cpp
+++ b/backends/networking/http/emscripten/networkreadstream-emscripten.cpp
@@ -34,15 +34,26 @@
 #include <emscripten.h>
 #include <emscripten/fetch.h>
 
+#include "backends/networking/http/emscripten/networkreadstream-emscripten.h"
 #include "backends/networking/http/networkreadstream.h"
 #include "base/version.h"
 #include "common/debug.h"
 
-#define CURLE_OK 0x0  // the only CURLcode value used/checked in ScummVM
-
 namespace Networking {
 
-void NetworkReadStream::emscriptenOnReadyStateChange(emscripten_fetch_t *fetch) {
+NetworkReadStream *NetworkReadStream::make(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading, bool usingPatch, bool keepAlive, long keepAliveIdle, long keepAliveInterval) {
+	return new NetworkReadStreamEmscripten(url, headersList, postFields, uploading, usingPatch, keepAlive, keepAliveIdle, keepAliveInterval);
+}
+
+NetworkReadStream *NetworkReadStream::make(const char *url, RequestHeaders *headersList, const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles, bool keepAlive, long keepAliveIdle, long keepAliveInterval) {
+	return new NetworkReadStreamEmscripten(url, headersList, formFields, formFiles, keepAlive, keepAliveIdle, keepAliveInterval);
+}
+
+NetworkReadStream *NetworkReadStream::make(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post, bool keepAlive, long keepAliveIdle, long keepAliveInterval) {
+	return new NetworkReadStreamEmscripten(url, headersList, buffer, bufferSize, uploading, usingPatch, post, keepAlive, keepAliveIdle, keepAliveInterval);
+}
+
+void NetworkReadStreamEmscripten::emscriptenOnReadyStateChange(emscripten_fetch_t *fetch) {
 	if (fetch->readyState != 2)
 		return;
 
@@ -51,12 +62,12 @@ void NetworkReadStream::emscriptenOnReadyStateChange(emscripten_fetch_t *fetch)
 
 	assert(headerString);
 	emscripten_fetch_get_response_headers(fetch, headerString, headersLengthBytes);
-	NetworkReadStream *stream = (NetworkReadStream *)fetch->userData;
+	NetworkReadStreamEmscripten *stream = (NetworkReadStreamEmscripten *)fetch->userData;
 	stream->addResponseHeaders(headerString, headersLengthBytes);
 	free(headerString);
 }
 
-void NetworkReadStream::emscriptenOnProgress(emscripten_fetch_t *fetch) {
+void NetworkReadStreamEmscripten::emscriptenOnProgress(emscripten_fetch_t *fetch) {
 	/*
 	if (fetch->totalBytes) {
 		debug(5,"Downloading %s.. %.2f percent complete.", fetch->url, fetch->dataOffset * 100.0 / fetch->totalBytes);
@@ -74,52 +85,57 @@ void NetworkReadStream::emscriptenOnProgress(emscripten_fetch_t *fetch) {
 			fetch->dataOffset,
 			fetch->dataOffset + fetch->numBytes);
 	*/
-	NetworkReadStream *stream = (NetworkReadStream *)fetch->userData;
+	NetworkReadStreamEmscripten *stream = (NetworkReadStreamEmscripten *)fetch->userData;
 	if (stream) {
 		stream->setProgress(fetch->dataOffset, fetch->totalBytes);
 	}
 }
 
-void NetworkReadStream::emscriptenOnSuccess(emscripten_fetch_t *fetch) {
-	NetworkReadStream *stream = (NetworkReadStream *)fetch->userData;
+void NetworkReadStreamEmscripten::emscriptenOnSuccess(emscripten_fetch_t *fetch) {
+	NetworkReadStreamEmscripten *stream = (NetworkReadStreamEmscripten *)fetch->userData;
 	stream->emscriptenDownloadFinished(true);
 }
 
-void NetworkReadStream::emscriptenOnError(emscripten_fetch_t *fetch) {
-	NetworkReadStream *stream = (NetworkReadStream *)fetch->userData;
+void NetworkReadStreamEmscripten::emscriptenOnError(emscripten_fetch_t *fetch) {
+	NetworkReadStreamEmscripten *stream = (NetworkReadStreamEmscripten *)fetch->userData;
 	stream->emscriptenDownloadFinished(false);
 }
 
-void NetworkReadStream::emscriptenDownloadFinished(bool success) {
-
-	if (success) {
-		debug(5, "NetworkReadStream::emscriptenHandleDownload Finished downloading %llu bytes from URL %s. HTTP status code: %d", _emscripten_fetch->numBytes, _emscripten_fetch->url, _emscripten_fetch->status);
-	} else {
-		debug(5, "NetworkReadStream::emscriptenHandleDownload Downloading %s failed, HTTP failure status code: %d, status text: %s", _emscripten_fetch->url, _emscripten_fetch->status, _emscripten_fetch->statusText);
-	}
+void NetworkReadStreamEmscripten::emscriptenDownloadFinished(bool success) {
+	_requestComplete = true;
 	if (_emscripten_fetch->numBytes > 0) {
 		// TODO: This could be done continuously during emscriptenOnProgress?
-		this->_backingStream.write(_emscripten_fetch->data, _emscripten_fetch->numBytes); 
+		this->_backingStream.write(_emscripten_fetch->data, _emscripten_fetch->numBytes);
 	}
 	this->setProgress(_emscripten_fetch->numBytes, _emscripten_fetch->numBytes);
-	if (success) {
-		this->finished(CURLE_OK); // TODO: actually pass the result code from emscripten_fetch
 
+	if (success) {
+		debug(5, "NetworkReadStreamEmscripten::emscriptenHandleDownload Finished downloading %llu bytes from URL %s. HTTP status code: %d", _emscripten_fetch->numBytes, _emscripten_fetch->url, _emscripten_fetch->status);
+		_success = true; // TODO: actually pass the result code from emscripten_fetch
 	} else {
-		this->finished(-1);
+		debug(5, "NetworkReadStreamEmscripten::emscriptenHandleDownload Downloading %s failed, HTTP failure status code: %d, status text: %s", _emscripten_fetch->url, _emscripten_fetch->status, _emscripten_fetch->statusText);
+
+		// Make a copy of the error message since _emscripten_fetch might be cleaned up
+		if (_emscripten_fetch && _emscripten_fetch->statusText) {
+			_errorBuffer = strdup(_emscripten_fetch->statusText);
+		} else {
+			_errorBuffer = strdup("Unknown error");
+		}
+		warning("NetworkReadStreamEmscripten::finished %s - Request failed (%s)", _emscripten_fetch_url, getError());
 	}
 }
 
-void NetworkReadStream::resetStream() {
+void NetworkReadStreamEmscripten::resetStream() {
 	_eos = _requestComplete = false;
 	_sendingContentsSize = _sendingContentsPos = 0;
 	_progressDownloaded = _progressTotal = 0;
-	_bufferCopy = nullptr;
 	_emscripten_fetch = nullptr;
 	_emscripten_request_headers = nullptr;
+	free(_errorBuffer);
+	_errorBuffer = nullptr;
 }
 
-void NetworkReadStream::initEmscripten(const char *url, RequestHeaders *headersList) {
+void NetworkReadStreamEmscripten::initEmscripten(const char *url, RequestHeaders *headersList) {
 
 	resetStream();
 	emscripten_fetch_attr_init(_emscripten_fetch_attr);
@@ -140,18 +156,18 @@ void NetworkReadStream::initEmscripten(const char *url, RequestHeaders *headersL
 			// Find the colon separator
 			uint colonPos = header.findFirstOf(':');
 			if (colonPos == Common::String::npos) {
-				warning("NetworkReadStream: Malformed header (no colon): %s", header.c_str());
+				warning("NetworkReadStreamEmscripten: Malformed header (no colon): %s", header.c_str());
 				continue;
 			}
-			
+
 			// Split into key and value parts
 			Common::String key = header.substr(0, colonPos);
 			Common::String value = header.substr(colonPos + 1);
-			
+
 			// Trim whitespace from key and value
 			key.trim();
 			value.trim();
-						
+
 			// Store key and value as separate strings
 			_emscripten_request_headers[i++] = strdup(key.c_str());
 			_emscripten_request_headers[i++] = strdup(value.c_str());
@@ -168,7 +184,7 @@ void NetworkReadStream::initEmscripten(const char *url, RequestHeaders *headersL
 	_emscripten_fetch_attr->onsuccess = emscriptenOnSuccess;
 	_emscripten_fetch_attr->userData = this;
 }
-void NetworkReadStream::setupBufferContents(const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
+void NetworkReadStreamEmscripten::setupBufferContents(const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
 	if (uploading) {
 		strcpy(_emscripten_fetch_attr->requestMethod, "PUT");
 		_emscripten_fetch_attr->requestDataSize = bufferSize;
@@ -182,71 +198,61 @@ void NetworkReadStream::setupBufferContents(const byte *buffer, uint32 bufferSiz
 			_emscripten_fetch_attr->requestData = (const char *)buffer;
 		}
 	}
-	debug(5, "NetworkReadStream::setupBufferContents uploading %s usingPatch %s post %s ->method %s", uploading ? "true" : "false", usingPatch ? "true" : "false", post ? "true" : "false", _emscripten_fetch_attr->requestMethod);
+	debug(5, "NetworkReadStreamEmscripten::setupBufferContents uploading %s usingPatch %s post %s ->method %s", uploading ? "true" : "false", usingPatch ? "true" : "false", post ? "true" : "false", _emscripten_fetch_attr->requestMethod);
 	_emscripten_fetch = emscripten_fetch(_emscripten_fetch_attr, _emscripten_fetch_url);
 }
 
-void NetworkReadStream::setupFormMultipart(const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles) {
+void NetworkReadStreamEmscripten::setupFormMultipart(const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles) {
 	// set POST multipart upload form fields/files
-	error("NetworkReadStream::setupFormMultipart not implemented");
+	error("NetworkReadStreamEmscripten::setupFormMultipart not implemented");
 }
 
 /** Send <postFields>, using POST by default. */
-NetworkReadStream::NetworkReadStream(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading, bool usingPatch, bool keepAlive, long keepAliveIdle, long keepAliveInterval) : _emscripten_fetch_attr(new emscripten_fetch_attr_t()), _emscripten_fetch_url(url), _backingStream(DisposeAfterUse::YES), _keepAlive(keepAlive), _errorBuffer(nullptr), _errorCode(CURLE_OK) {
+NetworkReadStreamEmscripten::NetworkReadStreamEmscripten(const char *url, RequestHeaders *headersList, const Common::String &postFields,
+		bool uploading, bool usingPatch, bool keepAlive, long keepAliveIdle, long keepAliveInterval) :
+		_emscripten_fetch_attr(new emscripten_fetch_attr_t()), _emscripten_fetch_url(url), _errorBuffer(nullptr),
+		NetworkReadStream(keepAlive, keepAliveIdle, keepAliveInterval) {
 	initEmscripten(url, headersList);
 	setupBufferContents((const byte *)postFields.c_str(), postFields.size(), uploading, usingPatch, false);
 }
 /** Send <formFields>, <formFiles>, using POST multipart/form. */
-NetworkReadStream::NetworkReadStream(const char *url, RequestHeaders *headersList, const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles, bool keepAlive, long keepAliveIdle, long keepAliveInterval) : _emscripten_fetch_attr(new emscripten_fetch_attr_t()), _emscripten_fetch_url(url), _backingStream(DisposeAfterUse::YES), _keepAlive(keepAlive), _errorBuffer(nullptr), _errorCode(CURLE_OK) {
+NetworkReadStreamEmscripten::NetworkReadStreamEmscripten(const char *url, RequestHeaders *headersList, const Common::HashMap<Common::String,
+		Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles, bool keepAlive, long keepAliveIdle,
+		long keepAliveInterval) : _emscripten_fetch_attr(new emscripten_fetch_attr_t()), _emscripten_fetch_url(url), _errorBuffer(nullptr),
+		NetworkReadStream(keepAlive, keepAliveIdle, keepAliveInterval) {
 	initEmscripten(url, headersList);
 	setupFormMultipart(formFields, formFiles);
 }
 
 /** Send <buffer>, using POST by default. */
-NetworkReadStream::NetworkReadStream(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post, bool keepAlive, long keepAliveIdle, long keepAliveInterval) : _emscripten_fetch_attr(new emscripten_fetch_attr_t()), _emscripten_fetch_url(url), _backingStream(DisposeAfterUse::YES), _keepAlive(keepAlive), _errorBuffer(nullptr), _errorCode(CURLE_OK) {
+NetworkReadStreamEmscripten::NetworkReadStreamEmscripten(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize,
+		bool uploading, bool usingPatch, bool post, bool keepAlive, long keepAliveIdle, long keepAliveInterval) :
+		_emscripten_fetch_attr(new emscripten_fetch_attr_t()), _emscripten_fetch_url(url), _errorBuffer(nullptr),
+		NetworkReadStream(keepAlive, keepAliveIdle, keepAliveInterval) {
 	initEmscripten(url, headersList);
 	setupBufferContents(buffer, bufferSize, uploading, usingPatch, post);
 }
 
-bool NetworkReadStream::reuse(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading, bool usingPatch) {
-	return false; // Not implemented for Emscripten
-}
-
-bool NetworkReadStream::reuse(const char *url, RequestHeaders *headersList, const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles) {
-	return false; // Not implemented for Emscripten
-}
-
-bool NetworkReadStream::reuse(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
-	return false; // Not implemented for Emscripten
-}
-
-NetworkReadStream::~NetworkReadStream() {
+NetworkReadStreamEmscripten::~NetworkReadStreamEmscripten() {
 	if (_emscripten_fetch) {
-		debug(5, "~NetworkReadStream: emscripten_fetch_close");
+		debug(5, "~NetworkReadStreamEmscripten: emscripten_fetch_close");
 		emscripten_fetch_close(_emscripten_fetch);
 	}
-	
+
 	// Free the headers array and its contents
 	if (_emscripten_request_headers) {
 		for (int i = 0; _emscripten_request_headers[i] != nullptr; ++i) {
-			free(_emscripten_request_headers[i]);  // Free each strdup'd string
+			free(_emscripten_request_headers[i]); // Free each strdup'd string
 		}
 		delete[] _emscripten_request_headers;
 	}
-	
-	free(_bufferCopy);
-	free(_errorBuffer);
-}
-
-bool NetworkReadStream::eos() const {
-	return _eos;
 }
 
-uint32 NetworkReadStream::read(void *dataPtr, uint32 dataSize) {
+uint32 NetworkReadStreamEmscripten::read(void *dataPtr, uint32 dataSize) {
 	uint32 actuallyRead = _backingStream.read(dataPtr, dataSize);
 
 	// Only access _emscripten_fetch->url if _emscripten_fetch is valid
-	// debug(5,"NetworkReadStream::read %u %s %s %s", actuallyRead, _eos ? "_eos" : "not _eos", _requestComplete ? "_requestComplete" : "_request not Complete", _emscripten_fetch ? _emscripten_fetch->url : "no-url");
+	// debug(5,"NetworkReadStreamEmscripten::read %u %s %s %s", actuallyRead, _eos ? "_eos" : "not _eos", _requestComplete ? "_requestComplete" : "_request not Complete", _emscripten_fetch ? _emscripten_fetch->url : "no-url");
 	if (actuallyRead == 0) {
 		if (_requestComplete)
 			_eos = true;
@@ -256,50 +262,29 @@ uint32 NetworkReadStream::read(void *dataPtr, uint32 dataSize) {
 	return actuallyRead;
 }
 
-void NetworkReadStream::finished(uint32 errorCode) {
-	_requestComplete = true;
-	_errorCode = errorCode;
-
-	if (_errorCode == CURLE_OK) {
-		debug(9, "NetworkReadStream::finished %s - Request succeeded", _emscripten_fetch_url);
-	} else {
-		// Make a copy of the error message since _emscripten_fetch might be cleaned up
-		if (_emscripten_fetch && _emscripten_fetch->statusText) {
-			_errorBuffer = strdup(_emscripten_fetch->statusText);
-		} else {
-			_errorBuffer = strdup("Unknown error");
-		}
-		warning("NetworkReadStream::finished %s - Request failed (%d - %s)", _emscripten_fetch_url, _errorCode, getError());
-	}
+bool NetworkReadStreamEmscripten::hasError() const {
+	return !_success;
 }
 
-bool NetworkReadStream::hasError() const {
-	return _errorCode != CURLE_OK;
-}
-
-const char *NetworkReadStream::getError() const {
+const char *NetworkReadStreamEmscripten::getError() const {
 	return _errorBuffer;
 }
 
-long NetworkReadStream::httpResponseCode() const {
+long NetworkReadStreamEmscripten::httpResponseCode() const {
 	// return 200;
 	unsigned short responseCode = 0;
 	if (_emscripten_fetch)
 		responseCode = _emscripten_fetch->status;
-	debug(5, "NetworkReadStream::httpResponseCode %hu %hu ", _emscripten_fetch->status, responseCode);
+	debug(5, "NetworkReadStreamEmscripten::httpResponseCode %hu", responseCode);
 	return responseCode;
 }
 
-Common::String NetworkReadStream::currentLocation() const {
-	debug(5, "NetworkReadStream::currentLocation %s", _emscripten_fetch_url);
+Common::String NetworkReadStreamEmscripten::currentLocation() const {
+	debug(5, "NetworkReadStreamEmscripten::currentLocation %s", _emscripten_fetch_url);
 	return Common::String(_emscripten_fetch_url);
 }
 
-Common::String NetworkReadStream::responseHeaders() const {
-	return _responseHeaders;
-}
-
-Common::HashMap<Common::String, Common::String> NetworkReadStream::responseHeadersMap() const {
+Common::HashMap<Common::String, Common::String> NetworkReadStreamEmscripten::responseHeadersMap() const {
 
 	Common::HashMap<Common::String, Common::String> headers;
 
@@ -319,31 +304,4 @@ Common::HashMap<Common::String, Common::String> NetworkReadStream::responseHeade
 	return headers;
 }
 
-uint32 NetworkReadStream::fillWithSendingContents(char *bufferToFill, uint32 maxSize) {
-	uint32 sendSize = _sendingContentsSize - _sendingContentsPos;
-	if (sendSize > maxSize)
-		sendSize = maxSize;
-	for (uint32 i = 0; i < sendSize; ++i) {
-		bufferToFill[i] = _sendingContentsBuffer[_sendingContentsPos + i];
-	}
-	_sendingContentsPos += sendSize;
-	return sendSize;
-}
-
-uint32 NetworkReadStream::addResponseHeaders(char *buffer, uint32 bufferSize) {
-	_responseHeaders += Common::String(buffer, bufferSize);
-	return bufferSize;
-}
-
-double NetworkReadStream::getProgress() const {
-	if (_progressTotal < 1)
-		return 0;
-	return (double)_progressDownloaded / (double)_progressTotal;
-}
-
-void NetworkReadStream::setProgress(uint64 downloaded, uint64 total) {
-	_progressDownloaded = downloaded;
-	_progressTotal = total;
-}
-
 } // namespace Networking
diff --git a/backends/networking/http/emscripten/networkreadstream-emscripten.h b/backends/networking/http/emscripten/networkreadstream-emscripten.h
new file mode 100644
index 00000000000..61693a3cfe9
--- /dev/null
+++ b/backends/networking/http/emscripten/networkreadstream-emscripten.h
@@ -0,0 +1,82 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BACKENDS_NETWORKING_HTTP_EMSCRIPTEN_NETWORKREADSTREAMEMSCRIPTEN_H
+#define BACKENDS_NETWORKING_HTTP_EMSCRIPTEN_NETWORKREADSTREAMEMSCRIPTEN_H
+
+#ifdef EMSCRIPTEN
+
+#include "backends/networking/http/networkreadstream.h"
+#include <emscripten/fetch.h>
+
+namespace Networking {
+
+class NetworkReadStream; // Forward declaration
+
+class NetworkReadStreamEmscripten : public NetworkReadStream {
+private:
+	emscripten_fetch_attr_t *_emscripten_fetch_attr;
+	emscripten_fetch_t *_emscripten_fetch;
+	const char *_emscripten_fetch_url = nullptr;
+	char **_emscripten_request_headers;
+	bool _success;
+	char *_errorBuffer;
+
+public:
+	NetworkReadStreamEmscripten(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading, bool usingPatch, bool keepAlive, long keepAliveIdle, long keepAliveInterval);
+
+	NetworkReadStreamEmscripten(const char *url, RequestHeaders *headersList, const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles, bool keepAlive, long keepAliveIdle, long keepAliveInterval);
+
+	NetworkReadStreamEmscripten(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post, bool keepAlive, long keepAliveIdle, long keepAliveInterval);
+
+	~NetworkReadStreamEmscripten() override;
+	void initEmscripten(const char *url, RequestHeaders *headersList);
+
+	// NetworkReadStream interface
+	bool reuse(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading = false, bool usingPatch = false) override { return false; }                                                 // no reuse for Emscripten
+	bool reuse(const char *url, RequestHeaders *headersList, const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles) override { return false; } // no reuse for Emscripten
+	bool reuse(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading = false, bool usingPatch = false, bool post = false) override { return false; }                         // no reuse for Emscripten
+	void setupBufferContents(const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) override;
+	void setupFormMultipart(const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles) override;
+
+	bool hasError() const override;
+	const char *getError() const override;
+
+	long httpResponseCode() const override;
+	Common::String currentLocation() const override;
+	Common::HashMap<Common::String, Common::String> responseHeadersMap() const override;
+	void resetStream() override;
+
+	uint32 read(void *dataPtr, uint32 dataSize) override;
+
+	// Static callback functions
+	static void emscriptenOnSuccess(emscripten_fetch_t *fetch);
+	static void emscriptenOnError(emscripten_fetch_t *fetch);
+	static void emscriptenOnProgress(emscripten_fetch_t *fetch);
+	static void emscriptenOnReadyStateChange(emscripten_fetch_t *fetch);
+	void emscriptenDownloadFinished(bool success);
+};
+
+} // End of namespace Networking
+
+#endif // EMSCRIPTEN
+
+#endif
diff --git a/backends/networking/http/httpjsonrequest.cpp b/backends/networking/http/httpjsonrequest.cpp
index 35a5c483085..7187639f1ce 100644
--- a/backends/networking/http/httpjsonrequest.cpp
+++ b/backends/networking/http/httpjsonrequest.cpp
@@ -19,8 +19,6 @@
  *
  */
 
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
 #include "backends/networking/http/httpjsonrequest.h"
 #include "backends/networking/http/connectionmanager.h"
 #include "backends/networking/http/networkreadstream.h"
diff --git a/backends/networking/http/httprequest.cpp b/backends/networking/http/httprequest.cpp
index e3161822770..0f53f0fad72 100644
--- a/backends/networking/http/httprequest.cpp
+++ b/backends/networking/http/httprequest.cpp
@@ -19,8 +19,6 @@
  *
  */
 
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
 #include "backends/networking/http/httprequest.h"
 #include "backends/networking/http/connectionmanager.h"
 #include "backends/networking/http/networkreadstream.h"
@@ -39,10 +37,10 @@ HttpRequest::~HttpRequest() {
 
 NetworkReadStream *HttpRequest::makeStream() {
 	if (_bytesBuffer)
-		return new NetworkReadStream(_url.c_str(), &_headersList, _bytesBuffer, _bytesBufferSize, _uploading, _usingPatch, true, _keepAlive, _keepAliveIdle, _keepAliveInterval);
+		return NetworkReadStream::make(_url.c_str(), &_headersList, _bytesBuffer, _bytesBufferSize, _uploading, _usingPatch, true, _keepAlive, _keepAliveIdle, _keepAliveInterval);
 	if (!_formFields.empty() || !_formFiles.empty())
-		return new NetworkReadStream(_url.c_str(), &_headersList, _formFields, _formFiles, _keepAlive, _keepAliveIdle, _keepAliveInterval);
-	return new NetworkReadStream(_url.c_str(), &_headersList, _postFields, _uploading, _usingPatch, _keepAlive, _keepAliveIdle, _keepAliveInterval);
+		return NetworkReadStream::make(_url.c_str(), &_headersList, _formFields, _formFiles, _keepAlive, _keepAliveIdle, _keepAliveInterval);
+	return NetworkReadStream::make(_url.c_str(), &_headersList, _postFields, _uploading, _usingPatch, _keepAlive, _keepAliveIdle, _keepAliveInterval);
 }
 
 void HttpRequest::handle() {
@@ -77,9 +75,7 @@ Common::String HttpRequest::date() const {
 }
 
 void HttpRequest::setHeaders(const Common::Array<Common::String> &headers) {
-	_headersList.clear();
-	for (uint32 i = 0; i < headers.size(); ++i)
-		addHeader(headers[i]);
+	_headersList = headers;
 }
 
 void HttpRequest::addHeader(const Common::String &header) {
diff --git a/backends/networking/http/networkreadstream.cpp b/backends/networking/http/networkreadstream.cpp
new file mode 100644
index 00000000000..59497271a8a
--- /dev/null
+++ b/backends/networking/http/networkreadstream.cpp
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "networkreadstream.h"
+#include "common/tokenizer.h"
+
+namespace Networking {
+
+/*
+ * The make static functions are defined in the implementation-specific subclass
+ */
+
+uint32 NetworkReadStream::fillWithSendingContents(char *bufferToFill, uint32 maxSize) {
+	uint32 sendSize = _sendingContentsSize - _sendingContentsPos;
+	if (sendSize > maxSize)
+		sendSize = maxSize;
+	for (uint32 i = 0; i < sendSize; ++i) {
+		bufferToFill[i] = _sendingContentsBuffer[_sendingContentsPos + i];
+	}
+	_sendingContentsPos += sendSize;
+	return sendSize;
+}
+
+uint32 NetworkReadStream::addResponseHeaders(char *buffer, uint32 bufferSize) {
+	_responseHeaders += Common::String(buffer, bufferSize);
+	return bufferSize;
+}
+
+double NetworkReadStream::getProgress() const {
+	if (_progressTotal < 1)
+		return 0;
+	return (double)_progressDownloaded / (double)_progressTotal;
+}
+
+void NetworkReadStream::setProgress(uint64 downloaded, uint64 total) {
+	_progressDownloaded = downloaded;
+	_progressTotal = total;
+}
+
+uint32 NetworkReadStream::read(void *dataPtr, uint32 dataSize) {
+	uint32 actuallyRead = _backingStream.read(dataPtr, dataSize);
+
+	if (actuallyRead == 0) {
+		if (_requestComplete)
+			_eos = true;
+		return 0;
+	}
+
+	return actuallyRead;
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/http/networkreadstream.h b/backends/networking/http/networkreadstream.h
index e59edc633b7..54c15e28559 100644
--- a/backends/networking/http/networkreadstream.h
+++ b/backends/networking/http/networkreadstream.h
@@ -22,60 +22,40 @@
 #ifndef BACKENDS_NETWORKING_HTTP_NETWORKREADSTREAM_H
 #define BACKENDS_NETWORKING_HTTP_NETWORKREADSTREAM_H
 
+#include "common/array.h"
+#include "common/hash-str.h"
+#include "common/hashmap.h"
 #include "common/memstream.h"
 #include "common/path.h"
-#include "common/stream.h"
 #include "common/str.h"
-#include "common/hashmap.h"
-#include "common/array.h"
-#include "common/hash-str.h"
-
-#ifdef USE_LIBCURL
-typedef void CURL;
-#endif
-#ifdef EMSCRIPTEN
-struct emscripten_fetch_attr_t;
-struct emscripten_fetch_t;
-#endif
+#include "common/stream.h"
 
 namespace Networking {
 typedef Common::Array<Common::String> RequestHeaders;
-	
+
+// Simple interface for platform-specific NetworkReadStream implementations
 class NetworkReadStream : public Common::ReadStream {
-#ifdef USE_LIBCURL
-	CURL *_easy;
-	struct curl_slist *_headersSlist;  // Track the curl headers list for cleanup
-#endif
-#ifdef EMSCRIPTEN
-	emscripten_fetch_attr_t *_emscripten_fetch_attr;
-	emscripten_fetch_t *_emscripten_fetch;
-	const char *_emscripten_fetch_url = nullptr;
-	char **_emscripten_request_headers;
-#endif
+protected:
 	Common::MemoryReadWriteStream _backingStream;
 	bool _keepAlive;
 	long _keepAliveIdle, _keepAliveInterval;
 	bool _eos, _requestComplete;
-	char *_errorBuffer;
-	uint32 _errorCode;
 	const byte *_sendingContentsBuffer;
 	uint32 _sendingContentsSize;
 	uint32 _sendingContentsPos;
-	byte *_bufferCopy; // To use with old curl version where CURLOPT_COPYPOSTFIELDS is not available
 	Common::String _responseHeaders;
 	uint64 _progressDownloaded, _progressTotal;
 
-	void resetStream();
-#ifdef USE_LIBCURL
-	static struct curl_slist *requestHeadersToSlist(const RequestHeaders *headersList);
-	void initCurl(const char *url, RequestHeaders *headersList);
-	bool reuseCurl(const char *url, RequestHeaders *headersList);
-#endif
-#ifdef EMSCRIPTEN
-	void initEmscripten(const char *url, RequestHeaders *headersList);
-#endif
-	void setupBufferContents(const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post);
-	void setupFormMultipart(const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles);
+	/**
+	 * This method is called by ConnectionManager to indicate
+	 * that transfer is finished.
+	 *
+	 * @note It's called on failure too.
+	 */
+	virtual void resetStream() = 0;
+
+	virtual void setupBufferContents(const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) = 0;
+	virtual void setupFormMultipart(const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles) = 0;
 
 	/**
 	 * Fills the passed buffer with _sendingContentsBuffer contents.
@@ -87,48 +67,45 @@ class NetworkReadStream : public Common::ReadStream {
 	uint32 fillWithSendingContents(char *bufferToFill, uint32 maxSize);
 
 	/**
-	* Remembers headers returned to CURL in server's response.
-	*
-	* @returns how many bytes were actually read
-	*/
+	 * Remembers headers returned to CURL in server's response.
+	 *
+	 * @returns how many bytes were actually read
+	 */
 	uint32 addResponseHeaders(char *buffer, uint32 bufferSize);
 
-#ifdef USE_LIBCURL
-	static size_t curlDataCallback(char *d, size_t n, size_t l, void *p);
-	static size_t curlReadDataCallback(char *d, size_t n, size_t l, void *p);
-	static size_t curlHeadersCallback(char *d, size_t n, size_t l, void *p);
-	static int curlProgressCallbackOlder(void *p, double dltotal, double dlnow, double ultotal, double ulnow);
-#endif 
-#ifdef EMSCRIPTEN
-	static void emscriptenOnSuccess(emscripten_fetch_t *fetch);
-	static void emscriptenOnError(emscripten_fetch_t *fetch);
-	static void emscriptenOnProgress(emscripten_fetch_t *fetch);
-	static void emscriptenOnReadyStateChange(emscripten_fetch_t *fetch);
-	void emscriptenDownloadFinished(bool success);
-#endif
+	NetworkReadStream(bool keepAlive, long keepAliveIdle, long keepAliveInterval)
+		: _backingStream(DisposeAfterUse::YES), _eos(false), _requestComplete(false), _sendingContentsBuffer(nullptr),
+		  _sendingContentsSize(0), _sendingContentsPos(0), _progressDownloaded(0), _progressTotal(0),
+		  _keepAlive(keepAlive), _keepAliveIdle(keepAliveIdle), _keepAliveInterval(keepAliveInterval) {
+	}
+
 public:
+	/* Implementation-defined Constructors */
+
 	/** Send <postFields>, using POST by default. */
-	NetworkReadStream(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading = false, bool usingPatch = false, bool keepAlive = false, long keepAliveIdle = 120, long keepAliveInterval = 60);
+	static NetworkReadStream *make(const char *url, RequestHeaders *headersList, const Common::String &postFields,
+			bool uploading = false, bool usingPatch = false,
+			bool keepAlive = false, long keepAliveIdle = 120, long keepAliveInterval = 60);
+
 	/** Send <formFields>, <formFiles>, using POST multipart/form. */
-	NetworkReadStream(
-	    const char *url, RequestHeaders *headersList,
-	    const Common::HashMap<Common::String, Common::String> &formFields,
-	    const Common::HashMap<Common::String, Common::Path> &formFiles,
-		bool keepAlive = false, long keepAliveIdle = 120, long keepAliveInterval = 60);
+	static NetworkReadStream *make(const char *url, RequestHeaders *headersList,
+			const Common::HashMap<Common::String, Common::String> &formFields, const Common::HashMap<Common::String, Common::Path> &formFiles,
+			bool keepAlive = false, long keepAliveIdle = 120, long keepAliveInterval = 60);
+
 	/** Send <buffer>, using POST by default. */
-	NetworkReadStream(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading = false, bool usingPatch = false, bool post = true, bool keepAlive = false, long keepAliveIdle = 120, long keepAliveInterval = 60);
-	~NetworkReadStream() override;
+	static NetworkReadStream *make(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize,
+			bool uploading = false, bool usingPatch = false, bool post = true,
+			bool keepAlive = false, long keepAliveIdle = 120, long keepAliveInterval = 60);
 
 	/** Send <postFields>, using POST by default. */
-	bool reuse(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading = false, bool usingPatch = false);
+	virtual bool reuse(const char *url, RequestHeaders *headersList, const Common::String &postFields, bool uploading = false, bool usingPatch = false) = 0;
 	/** Send <formFields>, <formFiles>, using POST multipart/form. */
-	bool reuse(
+	virtual bool reuse(
 		const char *url, RequestHeaders *headersList,
 		const Common::HashMap<Common::String, Common::String> &formFields,
-		const Common::HashMap<Common::String, Common::Path> &formFiles);
+		const Common::HashMap<Common::String, Common::Path> &formFiles) = 0;
 	/** Send <buffer>, using POST by default. */
-	bool reuse(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading = false, bool usingPatch = false, bool post = true);
-
+	virtual bool reuse(const char *url, RequestHeaders *headersList, const byte *buffer, uint32 bufferSize, bool uploading = false, bool usingPatch = false, bool post = true) = 0;
 	/**
 	 * Returns true if a read failed because the stream end has been reached.
 	 * This flag is cleared by clearErr().
@@ -139,7 +116,7 @@ public:
 	 * with N bytes, reading exactly N bytes from the start should *not*
 	 * set eos; only reading *beyond* the available data should set it.
 	 */
-	bool eos() const override;
+	bool eos() const override { return _eos; }
 
 	/**
 	 * Read data from the stream. Subclasses must implement this
@@ -155,44 +132,38 @@ public:
 	 */
 	uint32 read(void *dataPtr, uint32 dataSize) override;
 
-	/**
-	 * This method is called by ConnectionManager to indicate
-	 * that transfer is finished.
-	 *
-	 * @note It's called on failure too.
-	 */
-	void finished(uint32 errorCode);
-
 	/**
 	 * Returns HTTP response code from inner CURL handle.
 	 * It returns -1 to indicate there is no inner handle.
 	 *
 	 * @note This method should be called when eos() == true.
 	 */
-	long httpResponseCode() const;
+	virtual long httpResponseCode() const = 0;
 
 	/**
-	* Return current location URL from inner CURL handle.
-	* "" is returned to indicate there is no inner handle.
-	*
-	* @note This method should be called when eos() == true.
-	*/
-	Common::String currentLocation() const;
+	 * Return current location URL from inner CURL handle.
+	 * "" is returned to indicate there is no inner handle.
+	 *
+	 * @note This method should be called when eos() == true.
+	 */
+	virtual Common::String currentLocation() const = 0;
 
 	/**
-	* Return response headers.
-	*
-	* @note This method should be called when eos() == true.
-	*/
-	Common::String responseHeaders() const;
+	 * Return response headers.
+	 *
+	 * @note This method should be called when eos() == true.
+	 */
+	Common::String responseHeaders() const {
+		return _responseHeaders;
+	}
 
 	/**
-	* Return response headers as HashMap. All header names in
-	* it are lowercase.
-	*
-	* @note This method should be called when eos() == true.
-	*/
-	Common::HashMap<Common::String, Common::String> responseHeadersMap() const;
+	 * Return response headers as HashMap. All header names in
+	 * it are lowercase.
+	 *
+	 * @note This method should be called when eos() == true.
+	 */
+	virtual Common::HashMap<Common::String, Common::String> responseHeadersMap() const = 0;
 
 	/** Returns a number in range [0, 1], where 1 is "complete". */
 	double getProgress() const;
@@ -202,10 +173,8 @@ public:
 
 	bool keepAlive() const { return _keepAlive; }
 
-	bool hasError() const;
-	uint32 getErrorCode() const { return _errorCode; }
-	const char *getError() const;
-
+	virtual bool hasError() const = 0;
+	virtual const char *getError() const = 0;
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/http/session.cpp b/backends/networking/http/session.cpp
index 50a1343fbe0..e1b67a01ac3 100644
--- a/backends/networking/http/session.cpp
+++ b/backends/networking/http/session.cpp
@@ -19,8 +19,6 @@
  *
  */
 
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
 #include "backends/networking/http/session.h"
 
 namespace Networking {
diff --git a/backends/networking/http/sessionrequest.cpp b/backends/networking/http/sessionrequest.cpp
index a259a7c3c04..e7dbab6e707 100644
--- a/backends/networking/http/sessionrequest.cpp
+++ b/backends/networking/http/sessionrequest.cpp
@@ -19,8 +19,6 @@
  *
  */
 
-#define FORBIDDEN_SYMBOL_ALLOW_ALL
-
 #include "backends/networking/http/connectionmanager.h"
 #include "backends/networking/http/networkreadstream.h"
 #include "backends/networking/http/sessionrequest.h"
@@ -186,7 +184,7 @@ void SessionRequest::handle() {
 
 		if (_stream->eos()) {
 			if (_stream->hasError()) {
-				ErrorResponse error(this, false, true, Common::String::format("TLS stream response code is not CURLE_OK OK: %s", _stream->getError()), _stream->getErrorCode());
+				ErrorResponse error(this, false, true, Common::String::format("Stream is in error: %s", _stream->getError()), -1);
 				finishError(error);
 				return;
 			}
diff --git a/engines/scumm/he/net/net_lobby.h b/engines/scumm/he/net/net_lobby.h
index a8984ea6791..81105f0893c 100644
--- a/engines/scumm/he/net/net_lobby.h
+++ b/engines/scumm/he/net/net_lobby.h
@@ -22,8 +22,8 @@
 #ifndef SCUMM_HE_NET_LOBBY_H
 #define SCUMM_HE_NET_LOBBY_H
 
-#include "backends/networking/http/curl/socket.h"
-#include "backends/networking/http/curl/url.h"
+#include "backends/networking/curl/socket.h"
+#include "backends/networking/curl/url.h"
 #include "common/formats/json.h"
 
 #include "scumm/he/net/net_main.h"


Commit: 007efa7fba41954bfb5ccea413f7aa15434aa7d3
    https://github.com/scummvm/scummvm/commit/007efa7fba41954bfb5ccea413f7aa15434aa7d3
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-13T18:38:54+02:00

Commit Message:
BACKENDS: NETWORKING: Fix new[]/free mismatch

Changed paths:
    backends/networking/sdl_net/handlerutils.cpp


diff --git a/backends/networking/sdl_net/handlerutils.cpp b/backends/networking/sdl_net/handlerutils.cpp
index c1ba080aa39..3387f45859b 100644
--- a/backends/networking/sdl_net/handlerutils.cpp
+++ b/backends/networking/sdl_net/handlerutils.cpp
@@ -98,7 +98,8 @@ Common::String HandlerUtils::readEverythingFromStream(Common::SeekableReadStream
 }
 
 Common::SeekableReadStream *HandlerUtils::makeResponseStreamFromString(const Common::String &response) {
-	byte *data = new byte[response.size()];
+	byte *data = (byte *)malloc(response.size());
+	assert(data);
 	memcpy(data, response.c_str(), response.size());
 	return new Common::MemoryReadStream(data, response.size(), DisposeAfterUse::YES);
 }


Commit: 57df0a6049d195dd512b2bd780d96ab9a2d19097
    https://github.com/scummvm/scummvm/commit/57df0a6049d195dd512b2bd780d96ab9a2d19097
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2025-08-13T18:38:54+02:00

Commit Message:
BACKENDS: Untangle networking USE flags

A new USE_HTTP define is created to indicate that an HTTP backend is
available (using either libcurl or emscripten platform).
USE_CLOUD now only builds cloud providers storage support, while
USE_SDL_NET determines if a local web server is built.
USE_LIBCURL is now only used when the feature really depends on libcurl
(that is lobby support for Scumm HE).

Changed paths:
    backends/module.mk
    backends/networking/sdl_net/handlers/filesbasehandler.cpp
    backends/networking/sdl_net/handlerutils.cpp
    backends/networking/sdl_net/localwebserver.cpp
    backends/networking/sdl_net/localwebserver.h
    backends/platform/libretro/dependencies.mk
    backends/platform/sdl/sdl.cpp
    base/main.cpp
    base/version.cpp
    configure
    devtools/create_project/create_project.cpp
    gui/dump-all-dialogs.cpp
    gui/editgamedialog.cpp
    gui/module.mk
    gui/options.cpp


diff --git a/backends/module.mk b/backends/module.mk
index afa3c96bb9a..af68814b790 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -27,6 +27,17 @@ MODULE_OBJS := \
 	saves/default/default-saves.o \
 	timer/default/default-timer.o
 
+ifdef USE_HTTP
+MODULE_OBJS += \
+	networking/http/connectionmanager.o \
+	networking/http/networkreadstream.o \
+	networking/http/httpjsonrequest.o \
+	networking/http/httprequest.o \
+	networking/http/postrequest.o \
+	networking/http/request.o \
+	networking/http/session.o \
+	networking/http/sessionrequest.o
+
 ifdef USE_CLOUD
 MODULE_OBJS += \
 	cloud/basestorage.o \
@@ -62,19 +73,10 @@ MODULE_OBJS += \
 	cloud/onedrive/onedrivecreatedirectoryrequest.o \
 	cloud/onedrive/onedrivetokenrefresher.o \
 	cloud/onedrive/onedrivelistdirectoryrequest.o \
-	cloud/onedrive/onedriveuploadrequest.o \
-	networking/http/connectionmanager.o \
-	networking/http/networkreadstream.o \
-	networking/http/httpjsonrequest.o \
-	networking/http/httprequest.o \
-	networking/http/postrequest.o \
-	networking/http/request.o \
-	networking/http/session.o \
-	networking/http/sessionrequest.o
+	cloud/onedrive/onedriveuploadrequest.o
 endif
 
 ifdef USE_SCUMMVMDLC
-ifdef USE_LIBCURL
 MODULE_OBJS += \
 	dlc/scummvmcloud.o
 endif
@@ -90,7 +92,7 @@ MODULE_OBJS += \
 	networking/curl/cacert.o \
 	networking/curl/socket.o \
 	networking/curl/url.o
-ifdef USE_CLOUD
+ifdef USE_HTTP
 MODULE_OBJS += \
 	networking/http/curl/connectionmanager-curl.o \
 	networking/http/curl/networkreadstream-curl.o
@@ -105,7 +107,10 @@ MODULE_OBJS += \
 	midi/webmidi.o 
 ifdef USE_CLOUD
 MODULE_OBJS += \
-	fs/emscripten/cloud-fs.o \
+	fs/emscripten/cloud-fs.o
+endif
+ifdef USE_HTTP
+MODULE_OBJS += \
 	networking/http/emscripten/connectionmanager-emscripten.o \
 	networking/http/emscripten/networkreadstream-emscripten.o
 endif
@@ -132,10 +137,8 @@ MODULE_OBJS += \
 	networking/sdl_net/localwebserver.o \
 	networking/sdl_net/reader.o \
 	networking/sdl_net/uploadfileclienthandler.o
-endif
 
 ifdef USE_CLOUD
-ifdef USE_SDL_NET
 MODULE_OBJS += \
 	networking/sdl_net/handlers/connectcloudhandler.o
 endif
diff --git a/backends/networking/sdl_net/handlers/filesbasehandler.cpp b/backends/networking/sdl_net/handlers/filesbasehandler.cpp
index 6750985c65f..a206fce4e44 100644
--- a/backends/networking/sdl_net/handlers/filesbasehandler.cpp
+++ b/backends/networking/sdl_net/handlers/filesbasehandler.cpp
@@ -64,7 +64,7 @@ bool FilesBaseHandler::urlToPath(Common::String &url, Common::Path &path, Common
 		baseUrl = "/saves/";
 
 		// determine savepath (prefix to remove)
-#ifdef USE_LIBCURL
+#ifdef USE_CLOUD
 		DefaultSaveFileManager *manager = dynamic_cast<DefaultSaveFileManager *>(g_system->getSavefileManager());
 		basePath = (manager ? manager->concatWithSavesPath("") : ConfMan.getPath("savepath"));
 #else
diff --git a/backends/networking/sdl_net/handlerutils.cpp b/backends/networking/sdl_net/handlerutils.cpp
index 3387f45859b..029f1634c56 100644
--- a/backends/networking/sdl_net/handlerutils.cpp
+++ b/backends/networking/sdl_net/handlerutils.cpp
@@ -164,7 +164,7 @@ bool HandlerUtils::hasPermittedPrefix(const Common::Path &path) {
 	}
 
 	// prefix for /saves/
-#ifdef USE_LIBCURL
+#ifdef USE_CLOUD
 	DefaultSaveFileManager *manager = dynamic_cast<DefaultSaveFileManager *>(g_system->getSavefileManager());
 	prefix = (manager ? manager->concatWithSavesPath("") : ConfMan.getPath("savepath"));
 #else
diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp
index 768179820ef..25272dd9da7 100644
--- a/backends/networking/sdl_net/localwebserver.cpp
+++ b/backends/networking/sdl_net/localwebserver.cpp
@@ -75,9 +75,7 @@ LocalWebserver::LocalWebserver(): _set(nullptr), _serverSocket(nullptr), _timerS
 	addPathHandler("/list", &_listAjaxHandler);
 	addPathHandler("/filesAJAX", &_filesAjaxPageHandler);
 #ifdef USE_CLOUD
-#ifdef USE_LIBCURL
 	addPathHandler("/connect_cloud", &_connectCloudHandler);
-#endif // USE_LIBCURL
 #endif // USE_CLOUD
 	_defaultHandler = &_resourceHandler;
 }
diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h
index b47ee9c4c50..ec2d42f278f 100644
--- a/backends/networking/sdl_net/localwebserver.h
+++ b/backends/networking/sdl_net/localwebserver.h
@@ -38,9 +38,7 @@
 #include "common/scummsys.h"
 
 #ifdef USE_CLOUD
-#ifdef USE_LIBCURL
 #include "backends/networking/sdl_net/handlers/connectcloudhandler.h"
-#endif // USE_LIBCURL
 #endif // USE_CLOUD
 
 namespace Common {
@@ -76,9 +74,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
 	ListAjaxHandler _listAjaxHandler;
 	FilesAjaxPageHandler _filesAjaxPageHandler;
 #ifdef USE_CLOUD
-#ifdef USE_LIBCURL
 	ConnectCloudHandler _connectCloudHandler;
-#endif // USE_LIBCURL
 #endif // USE_CLOUD
 	ResourceHandler _resourceHandler;
 	uint32 _idlingFrames;
@@ -110,9 +106,7 @@ public:
 	static uint32 getPort();
 
 #ifdef USE_CLOUD
-#ifdef USE_LIBCURL
 	void setStorageConnectionCallback(Networking::ErrorCallback cb) { _connectCloudHandler.setStorageConnectionCallback(cb); }
-#endif // USE_LIBCURL
 #endif // USE_CLOUD
 
 	static void setClientGetHandler(Client &client, const Common::String &response, long code = 200, const char *mimeType = nullptr);
diff --git a/backends/platform/libretro/dependencies.mk b/backends/platform/libretro/dependencies.mk
index 79e2067b9a3..616af1bbff6 100644
--- a/backends/platform/libretro/dependencies.mk
+++ b/backends/platform/libretro/dependencies.mk
@@ -598,7 +598,8 @@ ifeq ($(this_lib_available), yes)
 	LDFLAGS += $(this_lib_flags)
 	INCLUDES += $(sharedlibs_this_lib_includes)
 	USE_LIBCURL := 1
-	DEFINES += -DUSE_CLOUD -DUSE_LIBCURL
+	USE_HTTP := 1
+	DEFINES += -DUSE_CLOUD -DUSE_LIBCURL -DUSE_HTTP
 else
 $(info System libcurl not available, dropping cloud feature.)
 endif
diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp
index 726a1857961..a6f0d170522 100644
--- a/backends/platform/sdl/sdl.cpp
+++ b/backends/platform/sdl/sdl.cpp
@@ -54,7 +54,7 @@
 #if defined(USE_OPENGL_GAME) || defined(USE_OPENGL_SHADERS)
 #include "graphics/opengl/context.h"
 #endif
-#if defined(USE_SCUMMVMDLC) && defined(USE_LIBCURL)
+#if defined(USE_SCUMMVMDLC)
 #include "backends/dlc/scummvmcloud.h"
 #endif
 #include "graphics/cursorman.h"
@@ -105,7 +105,7 @@ OSystem_SDL::OSystem_SDL()
 	_eventSource(nullptr),
 	_eventSourceWrapper(nullptr),
 	_window(nullptr) {
-#if defined(USE_SCUMMVMDLC) && defined(USE_LIBCURL)
+#if defined(USE_SCUMMVMDLC)
 	_dlcStore = new DLC::ScummVMCloud::ScummVMCloud();
 #endif
 }
@@ -225,7 +225,7 @@ bool OSystem_SDL::hasFeature(Feature f) {
 	if (f == kFeatureOpenGLForGame) return _oglType != OpenGL::kContextNone && OpenGLContext.type != OpenGL::kContextGLES;
 	if (f == kFeatureShadersForGame) return _supportsShaders;
 #endif
-#if defined(USE_SCUMMVMDLC) && defined(USE_LIBCURL)
+#if defined(USE_SCUMMVMDLC)
 	if (f == kFeatureDLC) return true;
 #endif
 #if SDL_VERSION_ATLEAST(3, 0, 0)
diff --git a/base/main.cpp b/base/main.cpp
index 62fc1848175..89d098af947 100644
--- a/base/main.cpp
+++ b/base/main.cpp
@@ -75,10 +75,10 @@
 #ifdef USE_CLOUD
 #include "backends/cloud/cloudmanager.h"
 #include "backends/networking/http/connectionmanager.h"
+#endif
 #ifdef USE_SDL_NET
 #include "backends/networking/sdl_net/localwebserver.h"
 #endif
-#endif
 
 #if defined(__DC__)
 #include "backends/platform/dc/DCLauncherDialog.h"
@@ -887,10 +887,10 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) {
 			launcherDialog();
 		}
 	}
-#ifdef USE_CLOUD
 #ifdef USE_SDL_NET
 	Networking::LocalWebserver::destroy();
 #endif
+#ifdef USE_CLOUD
 	Networking::ConnectionManager::destroy();
 	//I think it's important to destroy it after ConnectionManager
 	Cloud::CloudManager::destroy();
diff --git a/base/version.cpp b/base/version.cpp
index 28ee3317a56..2d6aaaef7cf 100644
--- a/base/version.cpp
+++ b/base/version.cpp
@@ -197,25 +197,13 @@ const char gScummVMFeatures[] = ""
 #endif
 
 #ifdef USE_CLOUD
-	"cloud ("
-#  if defined(USE_LIBCURL) || defined(EMSCRIPTEN) 
-	"servers"
-#    ifdef USE_SDL_NET
-	", local) "
-#    else
-	") "
-#    endif
-#  endif
-#else
-#  ifdef USE_LIBCURL
+	"cloud "
+#endif
+#ifdef USE_LIBCURL
 	"libcurl "
-#  endif
-#  ifdef EMSCRIPTEN
-	"emscripten_fetch "
-#  endif
-#  ifdef USE_SDL_NET
+#endif
+#ifdef USE_SDL_NET
 	"SDL_net "
-#  endif
 #endif
 
 #ifdef USE_ENET
diff --git a/configure b/configure
index 6f623d97c00..2afaf38e9d0 100755
--- a/configure
+++ b/configure
@@ -3649,11 +3649,8 @@ if test -n "$_host"; then
 			_enet=no    # Disable ENet (no sockets in the browser)
 			_sdlnet=no  # Disable SDL_Net (no sockets in the browser)
 			_libcurl=no # Emscripten backend uses emscripten_fetch not libcurl
-			_curl=no # Disable cloud unless it's manually enabled
 			if test "$_cloud" != yes; then
 				_cloud=no
-			else
-				append_var LDFLAGS "-sFETCH"
 			fi
 			_seq_midi=no
 			_timidity=no
@@ -5942,6 +5939,20 @@ echo "$_libcurl"
 
 define_in_config_if_yes "$_libcurl" "USE_LIBCURL"
 
+echocheck "HTTP client"
+if test "$_host_os" = "emscripten" &&  test "$_cloud" = "yes"; then
+	append_var LDFLAGS "-sFETCH"
+	_http=yes
+	echo "Emscripten"
+elif test "$_libcurl" = "yes"; then
+	_http=yes
+	echo "libcurl"
+else
+	_http=no
+	echo "no"
+fi
+define_in_config_if_yes $_http 'USE_HTTP'
+
 #
 # Check for libopenmpt to be present
 #
@@ -6083,38 +6094,16 @@ fi
 
 echo "$_libmpcdec"
 
-#
-# Check whether to build cloud integration support
-#
-echo_n "Cloud integration... "
-if test "$_cloud" = "no"; then
-	echo "no"
-else
-	_cloud=no
-	if test "$_sdlnet" = "yes"; then
-		_cloud=yes
-		echo_n "local"
-	fi
-	if test "$_libcurl" = "yes"; then
-		if test "$_cloud" = "yes"; then echo_n ", "; fi
-		_cloud=yes
-		echo_n "servers"
-	fi
-	if test "$_host_os" = "emscripten"; then
-		_cloud=yes
-		echo_n "servers"
-	fi
-	if test "$_cloud" = "no"; then
-		echo_n "no"
+if test "$_scummvmdlc" = "yes"; then
+	if test "$_http" != "yes"; then
+		_scummvmdlc=no
 	fi
-	echo  # newline
+else
+	_scummvmdlc=no
 fi
-define_in_config_if_yes $_cloud 'USE_CLOUD'
 
 if test "$_scummvmdlc" = "yes"; then
-	if test "$_libcurl" = "yes"; then
-		_dlc=yes
-	fi
+	_dlc=yes
 fi
 
 define_in_config_if_yes $_dlc 'USE_DLC'
@@ -7190,6 +7179,24 @@ else
 fi
 define_in_config_if_yes $_updates 'USE_UPDATES'
 
+#
+# Check whether to build cloud integration support
+#
+echo_n "Building cloud integration... "
+if test "$_cloud" = "no"; then
+	echo "no"
+elif test "$_http" = "yes"; then
+	_cloud=yes
+	echo "yes"
+else
+	_cloud=no
+	echo "no"
+fi
+define_in_config_if_yes $_cloud 'USE_CLOUD'
+
+echo_n "Building local web server... "
+echo "$_sdlnet"
+
 #
 # Check whether to create a build with all resources files linked into the binary
 #
diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp
index e44de09948a..59b67505c53 100644
--- a/devtools/create_project/create_project.cpp
+++ b/devtools/create_project/create_project.cpp
@@ -1185,6 +1185,7 @@ const Feature s_features[] = {
 	{"opengl_game_classic",               "USE_OPENGL_GAME", false, true,  "OpenGL support (classic) in 3d games" },
 	{"opengl_game_shaders",            "USE_OPENGL_SHADERS", false, true,  "OpenGL support (shaders) in 3d games" },
 	{            "taskbar",                   "USE_TASKBAR", false, true,  "Taskbar integration support" },
+	{               "http",                      "USE_HTTP", false, true,  "HTTP client support" },
 	{              "cloud",                     "USE_CLOUD", false, true,  "Cloud integration support" },
 	{               "enet",                      "USE_ENET", false, true,  "ENet networking support" },
 	{        "translation",               "USE_TRANSLATION", false, true,  "Translation support" },
@@ -1297,6 +1298,17 @@ static void fixupFeatures(ProjectType projectType, BuildSetup &setup) {
 		setFeatureBuildState("mikmod", setup.features, false);
 	}
 
+	// Only libcurl provides an HTTP client for now
+	// (or Emscripten but it's not supported by create_project)
+	if (!getFeatureBuildState("libcurl", setup.features)) {
+		setFeatureBuildState("http", setup.features, false);
+	}
+
+	// Without an HTTP client there is no cloud feature
+	if (!getFeatureBuildState("http", setup.features)) {
+		setFeatureBuildState("cloud", setup.features, false);
+	}
+
 	// These features depend on OpenGL
 	if (!getFeatureBuildState("opengl", setup.features)) {
 		setFeatureBuildState("opengl_game_classic", setup.features, false);
diff --git a/gui/dump-all-dialogs.cpp b/gui/dump-all-dialogs.cpp
index 740363e631a..596c662b9e9 100644
--- a/gui/dump-all-dialogs.cpp
+++ b/gui/dump-all-dialogs.cpp
@@ -112,12 +112,13 @@ void dumpDialogs(const Common::String &message, const Common::String &lang) {
 	// CloudConnectingWizard
 	GUI::CloudConnectionWizard cloudConnectingWizard;
 	handleSimpleDialog(cloudConnectingWizard, "cloudConnectingWizard" + suffix, surf);
-#endif
-#ifdef USE_LIBCURL
+
 	// RemoteBrowserDialog
 	GUI::RemoteBrowserDialog remoteBrowserDialog(_("Select directory with game data"));
 	handleSimpleDialog(remoteBrowserDialog, "remoteBrowserDialog" + suffix, surf);
+#endif
 
+#ifdef USE_HTTP
 	// DownloadIconPacksDialog
 	GUI::DownloadPacksDialog downloadIconPacksDialog(_("icon packs"), "LIST", "gui-icons*.dat");
 	handleSimpleDialog(downloadIconPacksDialog, "downloadIconPacksDialog" + suffix, surf);
diff --git a/gui/editgamedialog.cpp b/gui/editgamedialog.cpp
index 19b40f07f7c..502a1b79735 100644
--- a/gui/editgamedialog.cpp
+++ b/gui/editgamedialog.cpp
@@ -285,7 +285,7 @@ EditGameDialog::EditGameDialog(const Common::String &domain)
 	// These buttons have to be extra wide, or the text will be truncated
 	// in the small version of the GUI.
 
-#ifdef USE_LIBCURL
+#ifdef USE_HTTP
 	// GUI: Check integrity button
 	if (ConfMan.hasKey("enable_integrity_checking", Common::ConfigManager::kApplicationDomain))
 		new ButtonWidget(tab, "GameOptions_Paths.Checkintegrity", _("Check Integrity"), _("Perform integrity check for all game files"), kCmdCheckIntegrity);
@@ -619,7 +619,7 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 				error.runModal();
 				return;
 			}
-#if defined(USE_CLOUD) && defined(USE_LIBCURL)
+#if defined(USE_CLOUD)
 			MessageDialog warningMessage(_("Saved games sync feature doesn't work with non-default directories. If you want your saved games to sync, use default directory."));
 			warningMessage.runModal();
 #endif
@@ -637,7 +637,7 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 		_savePathWidget->setLabel(Common::Path());
 		break;
 
-#ifdef USE_LIBCURL
+#ifdef USE_HTTP
 	case kCmdCheckIntegrity: {
 		IntegrityDialog wizard("http://gamesdb.sev.zone/validate", _domain);
 		wizard.runModal();
diff --git a/gui/module.mk b/gui/module.mk
index c364bf3d84e..b7e9ccd5860 100644
--- a/gui/module.mk
+++ b/gui/module.mk
@@ -55,7 +55,7 @@ MODULE_OBJS += \
 	remotebrowser.o
 endif
 
-ifdef USE_LIBCURL
+ifdef USE_HTTP
 MODULE_OBJS += \
 	downloadpacksdialog.o \
 	integrity-dialog.o
diff --git a/gui/options.cpp b/gui/options.cpp
index 5aadb5f901a..091fa973038 100644
--- a/gui/options.cpp
+++ b/gui/options.cpp
@@ -62,7 +62,7 @@
 #include "gui/downloaddialog.h"
 #endif
 
-#ifdef USE_LIBCURL
+#ifdef USE_HTTP
 #include "gui/downloadpacksdialog.h"
 #endif
 
@@ -1690,7 +1690,7 @@ void OptionsDialog::addGraphicControls(GuiObject *boss, const Common::String &pr
 
 	_shaderClearButton = addClearButton(boss, prefix + "grShaderClearButton", kClearShaderCmd);
 
-#ifdef USE_LIBCURL
+#ifdef USE_HTTP
 	_updateShadersButton = new ButtonWidget(boss, prefix + "UpdateShadersButton", _("Download Shaders"), _("Check on the scummvm.org website for updates of shader packs"), kUpdateShadersCmd);
 #endif
 
@@ -2715,7 +2715,7 @@ void GlobalOptionsDialog::addGUIControls(GuiObject *boss, const Common::String &
 		_useSystemDialogsCheckbox->setState(ConfMan.getBool("gui_browser_native", _domain));
 	}
 
-#ifdef USE_LIBCURL
+#ifdef USE_HTTP
 	new ButtonWidget(boss, prefix + "UpdateIconsButton", _("Download Icons"),  _("Check on the scummvm.org website for updates of icon packs"), kUpdateIconsCmd);
 #endif
 }
@@ -3248,7 +3248,7 @@ void GlobalOptionsDialog::apply() {
 }
 
 void GlobalOptionsDialog::close() {
-#if defined(USE_CLOUD) && defined(USE_SDL_NET)
+#if defined(USE_SDL_NET)
 	if (LocalServer.isRunning()) {
 		LocalServer.stop();
 	}
@@ -3329,7 +3329,6 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 	}
 #endif
 
-#ifdef USE_CLOUD
 #ifdef USE_SDL_NET
 	case kChooseRootDirCmd: {
 		BrowserDialog browser(_("Select directory for Files Manager /root/"), true);
@@ -3344,7 +3343,7 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 	}
 #endif
 
-#ifdef USE_LIBCURL
+#ifdef USE_HTTP
 	case kUpdateIconsCmd: {
 		DownloadPacksDialog dia(_("icon packs"), "LIST", "gui-icons*.dat");
 		dia.runModal();
@@ -3355,7 +3354,7 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 	}
 #endif
 
-#ifdef USE_LIBCURL
+#ifdef USE_HTTP
 	case kUpdateShadersCmd: {
 		DownloadPacksDialog dia(_("shader packs"), "LIST-SHADERS", "shaders*.dat");
 		dia.runModal();
@@ -3363,7 +3362,6 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 	}
 #endif
 
-#endif
 	case kThemePathClearCmd:
 		_themePath->setLabel(Common::Path());
 		break;
@@ -3511,6 +3509,7 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		sendCommand(kSetPositionCmd, 0);
 		break;
 	}
+#endif // USE_CLOUD
 #ifdef USE_SDL_NET
 	case kRunServerCmd: {
 #ifdef NETWORKING_LOCALWEBSERVER_ENABLE_PORT_OVERRIDE
@@ -3540,7 +3539,6 @@ void GlobalOptionsDialog::handleCommand(CommandSender *sender, uint32 cmd, uint3
 		break;
 	}
 #endif // USE_SDL_NET
-#endif // USE_CLOUD
 #ifdef USE_FLUIDSYNTH
 	case kFluidSynthSettingsCmd:
 		_fluidSynthSettingsDialog->runModal();


Commit: 7289eac20b3e102cb8dcdf4459d830bfd1cea821
    https://github.com/scummvm/scummvm/commit/7289eac20b3e102cb8dcdf4459d830bfd1cea821
Author: Christian Kündig (christian at kuendig.info)
Date: 2025-08-13T18:38:54+02:00

Commit Message:
COMMON: Fix Common::percentEncodeString

Changed paths:
    common/str.cpp


diff --git a/common/str.cpp b/common/str.cpp
index c63429a3122..67a1cefbc69 100644
--- a/common/str.cpp
+++ b/common/str.cpp
@@ -828,7 +828,7 @@ String percentEncodeString(const String &src) {
 			c == '~' || c == '-' || c == '.' || c == '_')
 			res += c;
 		else
-			res += Common::String::format("%%%02X", c);
+			res += Common::String::format("%%%02X", (unsigned char)c);
 	}
 
 	return res;


Commit: 03f1b5610d0a1293821afbc739154206b031e00b
    https://github.com/scummvm/scummvm/commit/03f1b5610d0a1293821afbc739154206b031e00b
Author: Christian Kündig (christian at kuendig.info)
Date: 2025-08-13T18:38:54+02:00

Commit Message:
CLOUD: Remove ConnectionManager::urlEncode and use Common::percentEncodeString

Changed paths:
    backends/cloud/googledrive/googledrivestorage.cpp
    backends/cloud/googledrive/googledriveuploadrequest.cpp
    backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
    backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
    backends/cloud/onedrive/onedrivestorage.cpp
    backends/cloud/onedrive/onedriveuploadrequest.cpp
    backends/networking/http/connectionmanager.h
    backends/networking/http/curl/connectionmanager-curl.cpp
    backends/networking/http/curl/connectionmanager-curl.h
    backends/networking/http/emscripten/connectionmanager-emscripten.cpp
    backends/networking/http/emscripten/connectionmanager-emscripten.h


diff --git a/backends/cloud/googledrive/googledrivestorage.cpp b/backends/cloud/googledrive/googledrivestorage.cpp
index d218608dbaa..0853a4ed929 100644
--- a/backends/cloud/googledrive/googledrivestorage.cpp
+++ b/backends/cloud/googledrive/googledrivestorage.cpp
@@ -163,7 +163,7 @@ Networking::Request *GoogleDriveStorage::upload(const Common::String &path, Comm
 
 Networking::Request *GoogleDriveStorage::streamFileById(const Common::String &id, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
 	if (callback) {
-		Common::String url = Common::String::format(GOOGLEDRIVE_API_FILES_ALT_MEDIA, ConnMan.urlEncode(id).c_str());
+		Common::String url = Common::String::format(GOOGLEDRIVE_API_FILES_ALT_MEDIA, Common::percentEncodeString(id).c_str());
 		Common::String header = "Authorization: Bearer " + _token;
 		Networking::RequestHeaders *headersList = new Networking::RequestHeaders();
 		headersList->push_back(header);
diff --git a/backends/cloud/googledrive/googledriveuploadrequest.cpp b/backends/cloud/googledrive/googledriveuploadrequest.cpp
index 57a26b61e28..2e6173512a5 100644
--- a/backends/cloud/googledrive/googledriveuploadrequest.cpp
+++ b/backends/cloud/googledrive/googledriveuploadrequest.cpp
@@ -114,7 +114,7 @@ void GoogleDriveUploadRequest::startUpload() {
 
 	Common::String url = GOOGLEDRIVE_API_FILES;
 	if (_resolvedId != "")
-		url += "/" + ConnMan.urlEncode(_resolvedId);
+		url += "/" + Common::percentEncodeString(_resolvedId);
 	url += "?uploadType=resumable&fields=id,mimeType,modifiedTime,name,size";
 	Networking::JsonCallback callback = new Common::Callback<GoogleDriveUploadRequest, const Networking::JsonResponse &>(this, &GoogleDriveUploadRequest::startUploadCallback);
 	Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveUploadRequest, const Networking::ErrorResponse &>(this, &GoogleDriveUploadRequest::startUploadErrorCallback);
diff --git a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
index 767f55bad8a..5d16529716c 100644
--- a/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivecreatedirectoryrequest.cpp
@@ -68,7 +68,7 @@ void OneDriveCreateDirectoryRequest::start() {
 
 	Common::String url = ONEDRIVE_API_SPECIAL_APPROOT;
 	if (parent != "")
-		url += ":/" + ConnMan.urlEncode(parent) + ":";
+		url += ":/" + Common::percentEncodeString(parent) + ":";
 	url += "/children";
 	Networking::JsonCallback innerCallback = new Common::Callback<OneDriveCreateDirectoryRequest, const Networking::JsonResponse &>(this, &OneDriveCreateDirectoryRequest::responseCallback);
 	Networking::ErrorCallback errorResponseCallback = new Common::Callback<OneDriveCreateDirectoryRequest, const Networking::ErrorResponse &>(this, &OneDriveCreateDirectoryRequest::errorCallback);
diff --git a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
index da0c6ecbd7e..8693423478f 100644
--- a/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
+++ b/backends/cloud/onedrive/onedrivelistdirectoryrequest.cpp
@@ -76,7 +76,7 @@ void OneDriveListDirectoryRequest::listNextDirectory() {
 
 	Common::String dir = _currentDirectory;
 	dir.deleteLastChar();
-	Common::String url = Common::String::format(ONEDRIVE_API_SPECIAL_APPROOT_CHILDREN, ConnMan.urlEncode(dir).c_str());
+	Common::String url = Common::String::format(ONEDRIVE_API_SPECIAL_APPROOT_CHILDREN, Common::percentEncodeString(dir).c_str());
 	if (dir == "") url = Common::String(ONEDRIVE_API_SPECIAL_APPROOT_CHILDREN_ROOT_ITSELF);
 	makeRequest(url);
 }
diff --git a/backends/cloud/onedrive/onedrivestorage.cpp b/backends/cloud/onedrive/onedrivestorage.cpp
index 1cd3134c7c2..94e0867eafd 100644
--- a/backends/cloud/onedrive/onedrivestorage.cpp
+++ b/backends/cloud/onedrive/onedrivestorage.cpp
@@ -170,7 +170,7 @@ Networking::Request *OneDriveStorage::upload(const Common::String &path, Common:
 
 Networking::Request *OneDriveStorage::streamFileById(const Common::String &path, Networking::NetworkReadStreamCallback outerCallback, Networking::ErrorCallback errorCallback) {
 	debug(9, "OneDrive: `download \"%s\"`", path.c_str());
-	Common::String url = ONEDRIVE_API_SPECIAL_APPROOT_ID + ConnMan.urlEncode(path);
+	Common::String url = ONEDRIVE_API_SPECIAL_APPROOT_ID + Common::percentEncodeString(path);
 	Networking::JsonCallback innerCallback = new Common::CallbackBridge<OneDriveStorage, const Networking::NetworkReadStreamResponse &, const Networking::JsonResponse &>(this, &OneDriveStorage::fileInfoCallback, outerCallback);
 	Networking::HttpJsonRequest *request = new OneDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
 	request->addHeader("Authorization: bearer " + _token);
diff --git a/backends/cloud/onedrive/onedriveuploadrequest.cpp b/backends/cloud/onedrive/onedriveuploadrequest.cpp
index 49cfc1221a0..589418795ef 100644
--- a/backends/cloud/onedrive/onedriveuploadrequest.cpp
+++ b/backends/cloud/onedrive/onedriveuploadrequest.cpp
@@ -72,7 +72,7 @@ void OneDriveUploadRequest::uploadNextPart() {
 	const uint32 UPLOAD_PER_ONE_REQUEST = 10 * 1024 * 1024;
 
 	if (_uploadUrl == "" && (uint32)_contentsStream->size() > UPLOAD_PER_ONE_REQUEST) {
-		Common::String url = Common::String::format(ONEDRIVE_API_SPECIAL_APPROOT_UPLOAD, ConnMan.urlEncode(_savePath).c_str()); //folder must exist
+		Common::String url = Common::String::format(ONEDRIVE_API_SPECIAL_APPROOT_UPLOAD, Common::percentEncodeString(_savePath).c_str()); //folder must exist
 		Networking::JsonCallback callback = new Common::Callback<OneDriveUploadRequest, const Networking::JsonResponse &>(this, &OneDriveUploadRequest::partUploadedCallback);
 		Networking::ErrorCallback failureCallback = new Common::Callback<OneDriveUploadRequest, const Networking::ErrorResponse &>(this, &OneDriveUploadRequest::partUploadedErrorCallback);
 		Networking::HttpJsonRequest *request = new OneDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
@@ -84,7 +84,7 @@ void OneDriveUploadRequest::uploadNextPart() {
 
 	Common::String url;
 	if (_uploadUrl == "") {
-		url = Common::String::format(ONEDRIVE_API_SPECIAL_APPROOT_CONTENT, ConnMan.urlEncode(_savePath).c_str());
+		url = Common::String::format(ONEDRIVE_API_SPECIAL_APPROOT_CONTENT, Common::percentEncodeString(_savePath).c_str());
 	} else {
 		url = _uploadUrl;
 	}
diff --git a/backends/networking/http/connectionmanager.h b/backends/networking/http/connectionmanager.h
index 908bd800c2a..70bbaf950ef 100644
--- a/backends/networking/http/connectionmanager.h
+++ b/backends/networking/http/connectionmanager.h
@@ -99,9 +99,6 @@ public:
 	 */
 	Request *addRequest(Request *request, RequestCallback callback = nullptr);
 
-	/** Return URL-encoded version of given string. */
-	virtual Common::String urlEncode(const Common::String &s) const = 0;
-
 	static uint32 getCloudRequestsPeriodInMicroseconds();
 };
 
diff --git a/backends/networking/http/curl/connectionmanager-curl.cpp b/backends/networking/http/curl/connectionmanager-curl.cpp
index af12417a582..6c2e3c294ba 100644
--- a/backends/networking/http/curl/connectionmanager-curl.cpp
+++ b/backends/networking/http/curl/connectionmanager-curl.cpp
@@ -61,22 +61,6 @@ void ConnectionManagerCurl::registerEasyHandle(CURL *easy) const {
 	curl_multi_add_handle(_multi, easy);
 }
 
-Common::String ConnectionManagerCurl::urlEncode(const Common::String &s) const {
-	if (!_multi)
-		return "";
-#if LIBCURL_VERSION_NUM >= 0x070F04
-	char *output = curl_easy_escape(_multi, s.c_str(), s.size());
-#else
-	char *output = curl_escape(s.c_str(), s.size());
-#endif
-	if (output) {
-		Common::String result = output;
-		curl_free(output);
-		return result;
-	}
-	return "";
-}
-
 // private goes here:
 void ConnectionManagerCurl::processTransfers() {
 	if (!_multi)
diff --git a/backends/networking/http/curl/connectionmanager-curl.h b/backends/networking/http/curl/connectionmanager-curl.h
index ad8ea827d4f..4452858819d 100644
--- a/backends/networking/http/curl/connectionmanager-curl.h
+++ b/backends/networking/http/curl/connectionmanager-curl.h
@@ -47,9 +47,6 @@ public:
 	 */
 	void registerEasyHandle(CURL *easy) const;
 
-	/** Return URL-encoded version of given string. */
-	Common::String urlEncode(const Common::String &s) const override;
-
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/http/emscripten/connectionmanager-emscripten.cpp b/backends/networking/http/emscripten/connectionmanager-emscripten.cpp
index b0c877729b7..a542ad6efef 100644
--- a/backends/networking/http/emscripten/connectionmanager-emscripten.cpp
+++ b/backends/networking/http/emscripten/connectionmanager-emscripten.cpp
@@ -49,20 +49,6 @@ Networking::ConnectionManager *Singleton<Networking::ConnectionManager>::makeIns
 
 namespace Networking {
 
-Common::String ConnectionManagerEmscripten::urlEncode(const Common::String &s) const {
-	const char *input = s.c_str();
-	char *result = (char *)EM_ASM_PTR({
-			var jsString = encodeURIComponent(UTF8ToString($0));
-			var size = lengthBytesUTF8(jsString) + 1;
-			var ret = Module._malloc(size);
-			stringToUTF8Array(jsString, HEAP8, ret, size);
-			return ret; }, input);
-
-	Common::String encodedResult = Common::String(result);
-	free(result); // Free the malloc'd memory from EM_ASM_PTR
-	return encodedResult;
-}
-
 void ConnectionManagerEmscripten::processTransfers() {
 	// Emscripten handles transfers asynchronously via callbacks
 	// No action needed here
diff --git a/backends/networking/http/emscripten/connectionmanager-emscripten.h b/backends/networking/http/emscripten/connectionmanager-emscripten.h
index f4deb4fca55..ec773eeb83b 100644
--- a/backends/networking/http/emscripten/connectionmanager-emscripten.h
+++ b/backends/networking/http/emscripten/connectionmanager-emscripten.h
@@ -32,7 +32,6 @@ class ConnectionManagerEmscripten : public ConnectionManager {
 
 public:
 	void processTransfers() override;
-	Common::String urlEncode(const Common::String &s) const override;
 };
 
 } // End of namespace Networking




More information about the Scummvm-git-logs mailing list