mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
http: Move web server to core and use flags.
This way we can independently control the debugger and disc sharing, since debugger could have security impact.
This commit is contained in:
parent
042256337a
commit
3a8a2ff933
10 changed files with 329 additions and 204 deletions
|
@ -1393,6 +1393,8 @@ add_library(${CoreLibName} ${CoreLinkType}
|
||||||
Core/HDRemaster.cpp
|
Core/HDRemaster.cpp
|
||||||
Core/HDRemaster.h
|
Core/HDRemaster.h
|
||||||
Core/ThreadEventQueue.h
|
Core/ThreadEventQueue.h
|
||||||
|
Core/WebServer.cpp
|
||||||
|
Core/WebServer.h
|
||||||
Core/Debugger/Breakpoints.cpp
|
Core/Debugger/Breakpoints.cpp
|
||||||
Core/Debugger/Breakpoints.h
|
Core/Debugger/Breakpoints.h
|
||||||
Core/Debugger/DebugInterface.h
|
Core/Debugger/DebugInterface.h
|
||||||
|
|
|
@ -511,6 +511,7 @@
|
||||||
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AnySuitable</InlineFunctionExpansion>
|
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AnySuitable</InlineFunctionExpansion>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="WaveFile.cpp" />
|
<ClCompile Include="WaveFile.cpp" />
|
||||||
|
<ClCompile Include="WebServer.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\ext\disarm.h" />
|
<ClInclude Include="..\ext\disarm.h" />
|
||||||
|
@ -746,6 +747,7 @@
|
||||||
<ClInclude Include="Util\ppge_atlas.h" />
|
<ClInclude Include="Util\ppge_atlas.h" />
|
||||||
<ClInclude Include="..\ext\xxhash.h" />
|
<ClInclude Include="..\ext\xxhash.h" />
|
||||||
<ClInclude Include="WaveFile.h" />
|
<ClInclude Include="WaveFile.h" />
|
||||||
|
<ClInclude Include="WebServer.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\android\jni\Android.mk" />
|
<None Include="..\android\jni\Android.mk" />
|
||||||
|
|
|
@ -689,6 +689,9 @@
|
||||||
<ClCompile Include="Replay.cpp">
|
<ClCompile Include="Replay.cpp">
|
||||||
<Filter>Core</Filter>
|
<Filter>Core</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="WebServer.cpp">
|
||||||
|
<Filter>Core</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="ELF\ElfReader.h">
|
<ClInclude Include="ELF\ElfReader.h">
|
||||||
|
@ -1268,6 +1271,9 @@
|
||||||
<ClInclude Include="Replay.h">
|
<ClInclude Include="Replay.h">
|
||||||
<Filter>Core</Filter>
|
<Filter>Core</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="WebServer.h">
|
||||||
|
<Filter>Core</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="CMakeLists.txt" />
|
<None Include="CMakeLists.txt" />
|
||||||
|
|
278
Core/WebServer.cpp
Normal file
278
Core/WebServer.cpp
Normal file
|
@ -0,0 +1,278 @@
|
||||||
|
// Copyright (c) 2014- PPSSPP Project.
|
||||||
|
|
||||||
|
// 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, version 2.0 or later versions.
|
||||||
|
|
||||||
|
// 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 2.0 for more details.
|
||||||
|
|
||||||
|
// A copy of the GPL 2.0 should have been included with the program.
|
||||||
|
// If not, see http://www.gnu.org/licenses/
|
||||||
|
|
||||||
|
// Official git repository and contact information can be found at
|
||||||
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include "base/stringutil.h"
|
||||||
|
#include "base/timeutil.h"
|
||||||
|
#include "file/fd_util.h"
|
||||||
|
#include "net/http_client.h"
|
||||||
|
#include "net/http_server.h"
|
||||||
|
#include "net/sinks.h"
|
||||||
|
#include "thread/threadutil.h"
|
||||||
|
#include "Common/FileUtil.h"
|
||||||
|
#include "Common/Log.h"
|
||||||
|
#include "Core/Config.h"
|
||||||
|
#include "Core/WebServer.h"
|
||||||
|
|
||||||
|
enum class ServerStatus {
|
||||||
|
STOPPED,
|
||||||
|
STARTING,
|
||||||
|
RUNNING,
|
||||||
|
STOPPING,
|
||||||
|
RESTARTING,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *REPORT_HOSTNAME = "report.ppsspp.org";
|
||||||
|
static const int REPORT_PORT = 80;
|
||||||
|
|
||||||
|
static std::thread serverThread;
|
||||||
|
static ServerStatus serverStatus;
|
||||||
|
static std::mutex serverStatusLock;
|
||||||
|
static int serverFlags;
|
||||||
|
|
||||||
|
static void UpdateStatus(ServerStatus s) {
|
||||||
|
std::lock_guard<std::mutex> guard(serverStatusLock);
|
||||||
|
serverStatus = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool UpdateStatus(ServerStatus s, ServerStatus old) {
|
||||||
|
std::lock_guard<std::mutex> guard(serverStatusLock);
|
||||||
|
if (serverStatus == old) {
|
||||||
|
serverStatus = s;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ServerStatus RetrieveStatus() {
|
||||||
|
std::lock_guard<std::mutex> guard(serverStatusLock);
|
||||||
|
return serverStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This reports the local IP address to report.ppsspp.org, which can then
|
||||||
|
// relay that address to a mobile device searching for the server.
|
||||||
|
static void RegisterServer(int port) {
|
||||||
|
http::Client http;
|
||||||
|
Buffer theVoid;
|
||||||
|
|
||||||
|
char resource4[1024] = {};
|
||||||
|
if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT, net::DNSType::IPV4)) {
|
||||||
|
if (http.Connect()) {
|
||||||
|
std::string ip = fd_util::GetLocalIP(http.sock());
|
||||||
|
snprintf(resource4, sizeof(resource4) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port);
|
||||||
|
|
||||||
|
http.GET(resource4, &theVoid);
|
||||||
|
theVoid.Skip(theVoid.size());
|
||||||
|
http.Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT, net::DNSType::IPV6)) {
|
||||||
|
// We register both IPv4 and IPv6 in case the other client is using a different one.
|
||||||
|
if (resource4[0] != 0 && http.Connect()) {
|
||||||
|
http.GET(resource4, &theVoid);
|
||||||
|
theVoid.Skip(theVoid.size());
|
||||||
|
http.Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently, we're not using keepalive, so gotta reconnect...
|
||||||
|
if (http.Connect()) {
|
||||||
|
char resource6[1024] = {};
|
||||||
|
std::string ip = fd_util::GetLocalIP(http.sock());
|
||||||
|
snprintf(resource6, sizeof(resource6) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port);
|
||||||
|
|
||||||
|
http.GET(resource6, &theVoid);
|
||||||
|
theVoid.Skip(theVoid.size());
|
||||||
|
http.Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RegisterDiscHandlers(http::Server *http, std::unordered_map<std::string, std::string> *paths) {
|
||||||
|
for (std::string filename : g_Config.recentIsos) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
static const std::string sep = "\\/";
|
||||||
|
#else
|
||||||
|
static const std::string sep = "/";
|
||||||
|
#endif
|
||||||
|
size_t basepos = filename.find_last_of(sep);
|
||||||
|
std::string basename = "/" + (basepos == filename.npos ? filename : filename.substr(basepos + 1));
|
||||||
|
|
||||||
|
// Let's not serve directories, since they won't work. Only single files.
|
||||||
|
// Maybe can do PBPs and other files later. Would be neat to stream virtual disc filesystems.
|
||||||
|
if (endsWithNoCase(basename, ".cso") || endsWithNoCase(basename, ".iso")) {
|
||||||
|
(*paths)[ReplaceAll(basename, " ", "%20")] = filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto handler = [paths](const http::Request &request) {
|
||||||
|
std::string filename = (*paths)[request.resource()];
|
||||||
|
s64 sz = File::GetFileSize(filename);
|
||||||
|
|
||||||
|
std::string range;
|
||||||
|
if (request.Method() == http::RequestHeader::HEAD) {
|
||||||
|
request.WriteHttpResponseHeader(200, sz, "application/octet-stream", "Accept-Ranges: bytes\r\n");
|
||||||
|
} else if (request.GetHeader("range", &range)) {
|
||||||
|
s64 begin = 0, last = 0;
|
||||||
|
if (sscanf(range.c_str(), "bytes=%lld-%lld", &begin, &last) != 2) {
|
||||||
|
request.WriteHttpResponseHeader(400, -1, "text/plain");
|
||||||
|
request.Out()->Push("Could not understand range request.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (begin < 0 || begin > last || last >= sz) {
|
||||||
|
request.WriteHttpResponseHeader(416, -1, "text/plain");
|
||||||
|
request.Out()->Push("Range goes outside of file.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fp = File::OpenCFile(filename, "rb");
|
||||||
|
if (!fp || fseek(fp, begin, SEEK_SET) != 0) {
|
||||||
|
request.WriteHttpResponseHeader(500, -1, "text/plain");
|
||||||
|
request.Out()->Push("File access failed.");
|
||||||
|
if (fp) {
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s64 len = last - begin + 1;
|
||||||
|
char contentRange[1024];
|
||||||
|
sprintf(contentRange, "Content-Range: bytes %lld-%lld/%lld\r\n", begin, last, sz);
|
||||||
|
request.WriteHttpResponseHeader(206, len, "application/octet-stream", contentRange);
|
||||||
|
|
||||||
|
const size_t CHUNK_SIZE = 16 * 1024;
|
||||||
|
char *buf = new char[CHUNK_SIZE];
|
||||||
|
for (s64 pos = 0; pos < len; pos += CHUNK_SIZE) {
|
||||||
|
s64 chunklen = std::min(len - pos, (s64)CHUNK_SIZE);
|
||||||
|
fread(buf, chunklen, 1, fp);
|
||||||
|
request.Out()->Push(buf, chunklen);
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
delete [] buf;
|
||||||
|
request.Out()->Flush();
|
||||||
|
} else {
|
||||||
|
request.WriteHttpResponseHeader(418, -1, "text/plain");
|
||||||
|
request.Out()->Push("This server only supports range requests.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto pair : *paths) {
|
||||||
|
http->RegisterHandler(pair.first.c_str(), handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ExecuteWebServer() {
|
||||||
|
setCurrentThreadName("HTTPServer");
|
||||||
|
|
||||||
|
auto http = new http::Server(new threading::NewThreadExecutor());
|
||||||
|
std::unordered_map<std::string, std::string> discPaths;
|
||||||
|
|
||||||
|
if (serverFlags & (int)WebServerFlags::DISCS) {
|
||||||
|
RegisterDiscHandlers(http, &discPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!http->Listen(g_Config.iRemoteISOPort)) {
|
||||||
|
if (!http->Listen(0)) {
|
||||||
|
ERROR_LOG(FILESYS, "Unable to listen on any port");
|
||||||
|
UpdateStatus(ServerStatus::STOPPED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UpdateStatus(ServerStatus::RUNNING);
|
||||||
|
|
||||||
|
g_Config.iRemoteISOPort = http->Port();
|
||||||
|
RegisterServer(http->Port());
|
||||||
|
double lastRegister = real_time_now();
|
||||||
|
while (RetrieveStatus() == ServerStatus::RUNNING) {
|
||||||
|
http->RunSlice(1.0);
|
||||||
|
|
||||||
|
double now = real_time_now();
|
||||||
|
if (now > lastRegister + 540.0) {
|
||||||
|
RegisterServer(http->Port());
|
||||||
|
lastRegister = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
http->Stop();
|
||||||
|
|
||||||
|
// Move to STARTING to lock flags/STOPPING.
|
||||||
|
if (UpdateStatus(ServerStatus::STARTING, ServerStatus::RESTARTING)) {
|
||||||
|
ExecuteWebServer();
|
||||||
|
} else {
|
||||||
|
UpdateStatus(ServerStatus::STOPPED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StartWebServer(WebServerFlags flags) {
|
||||||
|
std::lock_guard<std::mutex> guard(serverStatusLock);
|
||||||
|
switch (serverStatus) {
|
||||||
|
case ServerStatus::RUNNING:
|
||||||
|
case ServerStatus::RESTARTING:
|
||||||
|
if ((serverFlags & (int)flags) == (int)flags) {
|
||||||
|
// Already running those flags.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
serverStatus = ServerStatus::RESTARTING;
|
||||||
|
serverFlags |= (int)flags;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case ServerStatus::STOPPED:
|
||||||
|
serverStatus = ServerStatus::STARTING;
|
||||||
|
serverFlags = (int)flags;
|
||||||
|
serverThread = std::thread(&ExecuteWebServer);
|
||||||
|
serverThread.detach();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StopWebServer(WebServerFlags flags) {
|
||||||
|
std::lock_guard<std::mutex> guard(serverStatusLock);
|
||||||
|
if (serverStatus != ServerStatus::RUNNING && serverStatus != ServerStatus::RESTARTING) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
serverFlags &= ~(int)flags;
|
||||||
|
if (serverFlags == 0) {
|
||||||
|
serverStatus = ServerStatus::STOPPING;
|
||||||
|
} else {
|
||||||
|
serverStatus = ServerStatus::RESTARTING;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebServerStopping(WebServerFlags flags) {
|
||||||
|
std::lock_guard<std::mutex> guard(serverStatusLock);
|
||||||
|
if (serverStatus == ServerStatus::RESTARTING) {
|
||||||
|
return (serverFlags & (int)flags) == 0;
|
||||||
|
}
|
||||||
|
return serverStatus == ServerStatus::STOPPING;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebServerStopped(WebServerFlags flags) {
|
||||||
|
std::lock_guard<std::mutex> guard(serverStatusLock);
|
||||||
|
if (serverStatus == ServerStatus::RUNNING) {
|
||||||
|
return (serverFlags & (int)flags) == 0;
|
||||||
|
}
|
||||||
|
return serverStatus == ServerStatus::STOPPED;
|
||||||
|
}
|
27
Core/WebServer.h
Normal file
27
Core/WebServer.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright (c) 2014- PPSSPP Project.
|
||||||
|
|
||||||
|
// 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, version 2.0 or later versions.
|
||||||
|
|
||||||
|
// 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 2.0 for more details.
|
||||||
|
|
||||||
|
// A copy of the GPL 2.0 should have been included with the program.
|
||||||
|
// If not, see http://www.gnu.org/licenses/
|
||||||
|
|
||||||
|
// Official git repository and contact information can be found at
|
||||||
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||||
|
|
||||||
|
enum class WebServerFlags {
|
||||||
|
DISCS = 1,
|
||||||
|
|
||||||
|
ALL = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool StartWebServer(WebServerFlags flags);
|
||||||
|
bool StopWebServer(WebServerFlags flags);
|
||||||
|
bool WebServerStopping(WebServerFlags flags);
|
||||||
|
bool WebServerStopped(WebServerFlags flags);
|
|
@ -84,6 +84,7 @@
|
||||||
#include "Core/HLE/sceUsbGps.h"
|
#include "Core/HLE/sceUsbGps.h"
|
||||||
#include "Core/Util/GameManager.h"
|
#include "Core/Util/GameManager.h"
|
||||||
#include "Core/Util/AudioFormat.h"
|
#include "Core/Util/AudioFormat.h"
|
||||||
|
#include "Core/WebServer.h"
|
||||||
#include "GPU/GPUInterface.h"
|
#include "GPU/GPUInterface.h"
|
||||||
|
|
||||||
#include "ui_atlas.h"
|
#include "ui_atlas.h"
|
||||||
|
@ -587,7 +588,8 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_Config.bRemoteShareOnStartup) {
|
if (g_Config.bRemoteShareOnStartup) {
|
||||||
StartRemoteISOSharing();
|
// TODO: Separate config options.
|
||||||
|
StartWebServer(WebServerFlags::ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string sysName = System_GetProperty(SYSPROP_NAME);
|
std::string sysName = System_GetProperty(SYSPROP_NAME);
|
||||||
|
|
|
@ -18,20 +18,15 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <condition_variable>
|
|
||||||
|
|
||||||
#include "base/timeutil.h"
|
#include "base/timeutil.h"
|
||||||
#include "file/fd_util.h"
|
|
||||||
#include "i18n/i18n.h"
|
#include "i18n/i18n.h"
|
||||||
#include "json/json_reader.h"
|
#include "json/json_reader.h"
|
||||||
#include "net/http_client.h"
|
#include "net/http_client.h"
|
||||||
#include "net/http_server.h"
|
|
||||||
#include "net/resolve.h"
|
#include "net/resolve.h"
|
||||||
#include "net/sinks.h"
|
|
||||||
#include "thread/threadutil.h"
|
|
||||||
#include "Common/Common.h"
|
#include "Common/Common.h"
|
||||||
#include "Common/FileUtil.h"
|
|
||||||
#include "Core/Config.h"
|
#include "Core/Config.h"
|
||||||
|
#include "Core/WebServer.h"
|
||||||
#include "UI/RemoteISOScreen.h"
|
#include "UI/RemoteISOScreen.h"
|
||||||
|
|
||||||
using namespace UI;
|
using namespace UI;
|
||||||
|
@ -39,190 +34,9 @@ using namespace UI;
|
||||||
static const char *REPORT_HOSTNAME = "report.ppsspp.org";
|
static const char *REPORT_HOSTNAME = "report.ppsspp.org";
|
||||||
static const int REPORT_PORT = 80;
|
static const int REPORT_PORT = 80;
|
||||||
|
|
||||||
enum class ServerStatus {
|
|
||||||
STOPPED,
|
|
||||||
STARTING,
|
|
||||||
RUNNING,
|
|
||||||
STOPPING,
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::thread *serverThread = nullptr;
|
|
||||||
static ServerStatus serverStatus;
|
|
||||||
static std::mutex serverStatusLock;
|
|
||||||
static std::condition_variable serverStatusCond;
|
|
||||||
|
|
||||||
static bool scanCancelled = false;
|
static bool scanCancelled = false;
|
||||||
static bool scanAborted = false;
|
static bool scanAborted = false;
|
||||||
|
|
||||||
static void UpdateStatus(ServerStatus s) {
|
|
||||||
std::lock_guard<std::mutex> guard(serverStatusLock);
|
|
||||||
serverStatus = s;
|
|
||||||
serverStatusCond.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
static ServerStatus RetrieveStatus() {
|
|
||||||
std::lock_guard<std::mutex> guard(serverStatusLock);
|
|
||||||
return serverStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This reports the local IP address to report.ppsspp.org, which can then
|
|
||||||
// relay that address to a mobile device searching for the server.
|
|
||||||
static void RegisterServer(int port) {
|
|
||||||
http::Client http;
|
|
||||||
Buffer theVoid;
|
|
||||||
|
|
||||||
char resource4[1024] = {};
|
|
||||||
if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT, net::DNSType::IPV4)) {
|
|
||||||
if (http.Connect(2, 20.0, &scanCancelled)) {
|
|
||||||
std::string ip = fd_util::GetLocalIP(http.sock());
|
|
||||||
snprintf(resource4, sizeof(resource4) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port);
|
|
||||||
|
|
||||||
http.GET(resource4, &theVoid);
|
|
||||||
theVoid.Skip(theVoid.size());
|
|
||||||
http.Disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT, net::DNSType::IPV6)) {
|
|
||||||
// We register both IPv4 and IPv6 in case the other client is using a different one.
|
|
||||||
if (resource4[0] != 0 && http.Connect()) {
|
|
||||||
http.GET(resource4, &theVoid);
|
|
||||||
theVoid.Skip(theVoid.size());
|
|
||||||
http.Disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Currently, we're not using keepalive, so gotta reconnect...
|
|
||||||
if (http.Connect()) {
|
|
||||||
char resource6[1024] = {};
|
|
||||||
std::string ip = fd_util::GetLocalIP(http.sock());
|
|
||||||
snprintf(resource6, sizeof(resource6) - 1, "/match/update?local=%s&port=%d", ip.c_str(), port);
|
|
||||||
|
|
||||||
http.GET(resource6, &theVoid);
|
|
||||||
theVoid.Skip(theVoid.size());
|
|
||||||
http.Disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ExecuteServer() {
|
|
||||||
setCurrentThreadName("HTTPServer");
|
|
||||||
|
|
||||||
auto http = new http::Server(new threading::SameThreadExecutor());
|
|
||||||
|
|
||||||
std::map<std::string, std::string> paths;
|
|
||||||
for (std::string filename : g_Config.recentIsos) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
static const std::string sep = "\\/";
|
|
||||||
#else
|
|
||||||
static const std::string sep = "/";
|
|
||||||
#endif
|
|
||||||
size_t basepos = filename.find_last_of(sep);
|
|
||||||
std::string basename = "/" + (basepos == filename.npos ? filename : filename.substr(basepos + 1));
|
|
||||||
|
|
||||||
// Let's not serve directories, since they won't work. Only single files.
|
|
||||||
// Maybe can do PBPs and other files later. Would be neat to stream virtual disc filesystems.
|
|
||||||
if (endsWithNoCase(basename, ".cso") || endsWithNoCase(basename, ".iso")) {
|
|
||||||
paths[ReplaceAll(basename, " ", "%20")] = filename;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto handler = [&](const http::Request &request) {
|
|
||||||
std::string filename = paths[request.resource()];
|
|
||||||
s64 sz = File::GetFileSize(filename);
|
|
||||||
|
|
||||||
std::string range;
|
|
||||||
if (request.Method() == http::RequestHeader::HEAD) {
|
|
||||||
request.WriteHttpResponseHeader(200, sz, "application/octet-stream", "Accept-Ranges: bytes\r\n");
|
|
||||||
} else if (request.GetHeader("range", &range)) {
|
|
||||||
s64 begin = 0, last = 0;
|
|
||||||
if (sscanf(range.c_str(), "bytes=%lld-%lld", &begin, &last) != 2) {
|
|
||||||
request.WriteHttpResponseHeader(400, -1, "text/plain");
|
|
||||||
request.Out()->Push("Could not understand range request.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (begin < 0 || begin > last || last >= sz) {
|
|
||||||
request.WriteHttpResponseHeader(416, -1, "text/plain");
|
|
||||||
request.Out()->Push("Range goes outside of file.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *fp = File::OpenCFile(filename, "rb");
|
|
||||||
if (!fp || fseek(fp, begin, SEEK_SET) != 0) {
|
|
||||||
request.WriteHttpResponseHeader(500, -1, "text/plain");
|
|
||||||
request.Out()->Push("File access failed.");
|
|
||||||
if (fp) {
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
s64 len = last - begin + 1;
|
|
||||||
char contentRange[1024];
|
|
||||||
sprintf(contentRange, "Content-Range: bytes %lld-%lld/%lld\r\n", begin, last, sz);
|
|
||||||
request.WriteHttpResponseHeader(206, len, "application/octet-stream", contentRange);
|
|
||||||
|
|
||||||
const size_t CHUNK_SIZE = 16 * 1024;
|
|
||||||
char *buf = new char[CHUNK_SIZE];
|
|
||||||
for (s64 pos = 0; pos < len; pos += CHUNK_SIZE) {
|
|
||||||
s64 chunklen = std::min(len - pos, (s64)CHUNK_SIZE);
|
|
||||||
fread(buf, chunklen, 1, fp);
|
|
||||||
request.Out()->Push(buf, chunklen);
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
delete [] buf;
|
|
||||||
request.Out()->Flush();
|
|
||||||
} else {
|
|
||||||
request.WriteHttpResponseHeader(418, -1, "text/plain");
|
|
||||||
request.Out()->Push("This server only supports range requests.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (auto pair : paths) {
|
|
||||||
http->RegisterHandler(pair.first.c_str(), handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!http->Listen(g_Config.iRemoteISOPort)) {
|
|
||||||
if (!http->Listen(0)) {
|
|
||||||
ERROR_LOG(FILESYS, "Unable to listen on any port");
|
|
||||||
UpdateStatus(ServerStatus::STOPPED);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UpdateStatus(ServerStatus::RUNNING);
|
|
||||||
|
|
||||||
g_Config.iRemoteISOPort = http->Port();
|
|
||||||
RegisterServer(http->Port());
|
|
||||||
double lastRegister = real_time_now();
|
|
||||||
while (RetrieveStatus() == ServerStatus::RUNNING) {
|
|
||||||
http->RunSlice(5.0);
|
|
||||||
|
|
||||||
double now = real_time_now();
|
|
||||||
if (now > lastRegister + 540.0) {
|
|
||||||
RegisterServer(http->Port());
|
|
||||||
lastRegister = now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
http->Stop();
|
|
||||||
|
|
||||||
UpdateStatus(ServerStatus::STOPPED);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool StartRemoteISOSharing() {
|
|
||||||
std::lock_guard<std::mutex> guard(serverStatusLock);
|
|
||||||
|
|
||||||
if (serverStatus != ServerStatus::STOPPED) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
serverStatus = ServerStatus::STARTING;
|
|
||||||
serverThread = new std::thread(&ExecuteServer);
|
|
||||||
serverThread->detach();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool FindServer(std::string &resultHost, int &resultPort) {
|
static bool FindServer(std::string &resultHost, int &resultPort) {
|
||||||
http::Client http;
|
http::Client http;
|
||||||
Buffer result;
|
Buffer result;
|
||||||
|
@ -393,11 +207,8 @@ RemoteISOScreen::RemoteISOScreen() : serverRunning_(false), serverStopping_(fals
|
||||||
void RemoteISOScreen::update() {
|
void RemoteISOScreen::update() {
|
||||||
UIScreenWithBackground::update();
|
UIScreenWithBackground::update();
|
||||||
|
|
||||||
bool nowRunning = RetrieveStatus() != ServerStatus::STOPPED;
|
bool nowRunning = !WebServerStopped(WebServerFlags::DISCS);
|
||||||
if (serverStopping_ && !nowRunning) {
|
if (serverStopping_ && !nowRunning) {
|
||||||
// Server stopped, delete the thread.
|
|
||||||
delete serverThread;
|
|
||||||
serverThread = nullptr;
|
|
||||||
serverStopping_ = false;
|
serverStopping_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,11 +235,10 @@ void RemoteISOScreen::CreateViews() {
|
||||||
rightColumnItems->SetSpacing(0.0f);
|
rightColumnItems->SetSpacing(0.0f);
|
||||||
Choice *browseChoice = new Choice(ri->T("Browse Games"));
|
Choice *browseChoice = new Choice(ri->T("Browse Games"));
|
||||||
rightColumnItems->Add(browseChoice)->OnClick.Handle(this, &RemoteISOScreen::HandleBrowse);
|
rightColumnItems->Add(browseChoice)->OnClick.Handle(this, &RemoteISOScreen::HandleBrowse);
|
||||||
ServerStatus status = RetrieveStatus();
|
if (WebServerStopping(WebServerFlags::DISCS)) {
|
||||||
if (status == ServerStatus::STOPPING) {
|
|
||||||
rightColumnItems->Add(new Choice(ri->T("Stopping..")))->SetDisabledPtr(&serverStopping_);
|
rightColumnItems->Add(new Choice(ri->T("Stopping..")))->SetDisabledPtr(&serverStopping_);
|
||||||
browseChoice->SetEnabled(false);
|
browseChoice->SetEnabled(false);
|
||||||
} else if (status != ServerStatus::STOPPED) {
|
} else if (!WebServerStopped(WebServerFlags::DISCS)) {
|
||||||
rightColumnItems->Add(new Choice(ri->T("Stop Sharing")))->OnClick.Handle(this, &RemoteISOScreen::HandleStopServer);
|
rightColumnItems->Add(new Choice(ri->T("Stop Sharing")))->OnClick.Handle(this, &RemoteISOScreen::HandleStopServer);
|
||||||
browseChoice->SetEnabled(false);
|
browseChoice->SetEnabled(false);
|
||||||
} else {
|
} else {
|
||||||
|
@ -450,7 +260,7 @@ void RemoteISOScreen::CreateViews() {
|
||||||
}
|
}
|
||||||
|
|
||||||
UI::EventReturn RemoteISOScreen::HandleStartServer(UI::EventParams &e) {
|
UI::EventReturn RemoteISOScreen::HandleStartServer(UI::EventParams &e) {
|
||||||
if (!StartRemoteISOSharing()) {
|
if (!StartWebServer(WebServerFlags::DISCS)) {
|
||||||
return EVENT_SKIPPED;
|
return EVENT_SKIPPED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,13 +268,10 @@ UI::EventReturn RemoteISOScreen::HandleStartServer(UI::EventParams &e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
UI::EventReturn RemoteISOScreen::HandleStopServer(UI::EventParams &e) {
|
UI::EventReturn RemoteISOScreen::HandleStopServer(UI::EventParams &e) {
|
||||||
std::lock_guard<std::mutex> guard(serverStatusLock);
|
if (!StopWebServer(WebServerFlags::DISCS)) {
|
||||||
|
|
||||||
if (serverStatus != ServerStatus::RUNNING) {
|
|
||||||
return EVENT_SKIPPED;
|
return EVENT_SKIPPED;
|
||||||
}
|
}
|
||||||
|
|
||||||
serverStatus = ServerStatus::STOPPING;
|
|
||||||
serverStopping_ = true;
|
serverStopping_ = true;
|
||||||
RecreateViews();
|
RecreateViews();
|
||||||
|
|
||||||
|
@ -683,13 +490,13 @@ void RemoteISOBrowseScreen::CreateViews() {
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoteISOSettingsScreen::RemoteISOSettingsScreen() {
|
RemoteISOSettingsScreen::RemoteISOSettingsScreen() {
|
||||||
serverRunning_ = RetrieveStatus() != ServerStatus::STOPPED;
|
serverRunning_ = !WebServerStopped(WebServerFlags::DISCS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteISOSettingsScreen::update() {
|
void RemoteISOSettingsScreen::update() {
|
||||||
UIDialogScreenWithBackground::update();
|
UIDialogScreenWithBackground::update();
|
||||||
|
|
||||||
bool nowRunning = RetrieveStatus() != ServerStatus::STOPPED;
|
bool nowRunning = !WebServerStopped(WebServerFlags::DISCS);
|
||||||
if (serverRunning_ != nowRunning) {
|
if (serverRunning_ != nowRunning) {
|
||||||
RecreateViews();
|
RecreateViews();
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,6 @@
|
||||||
#include "UI/MiscScreens.h"
|
#include "UI/MiscScreens.h"
|
||||||
#include "UI/MainScreen.h"
|
#include "UI/MainScreen.h"
|
||||||
|
|
||||||
bool StartRemoteISOSharing();
|
|
||||||
|
|
||||||
class RemoteISOScreen : public UIScreenWithBackground {
|
class RemoteISOScreen : public UIScreenWithBackground {
|
||||||
public:
|
public:
|
||||||
RemoteISOScreen();
|
RemoteISOScreen();
|
||||||
|
|
|
@ -463,6 +463,7 @@
|
||||||
<ClInclude Include="..\..\Core\System.h" />
|
<ClInclude Include="..\..\Core\System.h" />
|
||||||
<ClInclude Include="..\..\Core\TextureReplacer.h" />
|
<ClInclude Include="..\..\Core\TextureReplacer.h" />
|
||||||
<ClInclude Include="..\..\Core\ThreadEventQueue.h" />
|
<ClInclude Include="..\..\Core\ThreadEventQueue.h" />
|
||||||
|
<ClInclude Include="..\..\Core\WebServer.h" />
|
||||||
<ClInclude Include="..\..\Core\Util\AudioFormat.h" />
|
<ClInclude Include="..\..\Core\Util\AudioFormat.h" />
|
||||||
<ClInclude Include="..\..\Core\Util\AudioFormatNEON.h" />
|
<ClInclude Include="..\..\Core\Util\AudioFormatNEON.h" />
|
||||||
<ClInclude Include="..\..\Core\Util\BlockAllocator.h" />
|
<ClInclude Include="..\..\Core\Util\BlockAllocator.h" />
|
||||||
|
@ -673,6 +674,7 @@
|
||||||
<ClCompile Include="..\..\Core\Screenshot.cpp" />
|
<ClCompile Include="..\..\Core\Screenshot.cpp" />
|
||||||
<ClCompile Include="..\..\Core\System.cpp" />
|
<ClCompile Include="..\..\Core\System.cpp" />
|
||||||
<ClCompile Include="..\..\Core\TextureReplacer.cpp" />
|
<ClCompile Include="..\..\Core\TextureReplacer.cpp" />
|
||||||
|
<ClCompile Include="..\..\Core\WebServer.cpp" />
|
||||||
<ClCompile Include="..\..\Core\Util\AudioFormat.cpp" />
|
<ClCompile Include="..\..\Core\Util\AudioFormat.cpp" />
|
||||||
<ClCompile Include="..\..\Core\Util\AudioFormatNEON.cpp" />
|
<ClCompile Include="..\..\Core\Util\AudioFormatNEON.cpp" />
|
||||||
<ClCompile Include="..\..\Core\Util\BlockAllocator.cpp" />
|
<ClCompile Include="..\..\Core\Util\BlockAllocator.cpp" />
|
||||||
|
|
|
@ -297,6 +297,7 @@ EXEC_AND_LIB_FILES := \
|
||||||
$(SRC)/Core/Screenshot.cpp \
|
$(SRC)/Core/Screenshot.cpp \
|
||||||
$(SRC)/Core/System.cpp \
|
$(SRC)/Core/System.cpp \
|
||||||
$(SRC)/Core/TextureReplacer.cpp \
|
$(SRC)/Core/TextureReplacer.cpp \
|
||||||
|
$(SRC)/Core/WebServer.cpp \
|
||||||
$(SRC)/Core/Debugger/Breakpoints.cpp \
|
$(SRC)/Core/Debugger/Breakpoints.cpp \
|
||||||
$(SRC)/Core/Debugger/SymbolMap.cpp \
|
$(SRC)/Core/Debugger/SymbolMap.cpp \
|
||||||
$(SRC)/Core/Dialog/PSPDialog.cpp \
|
$(SRC)/Core/Dialog/PSPDialog.cpp \
|
||||||
|
|
Loading…
Add table
Reference in a new issue