Skip to content

Commit

Permalink
Added initial HTTP/HTTPS webserver/websocket server support (nw)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmicko committed Jan 4, 2017
1 parent 47c4f47 commit 63e3f48
Show file tree
Hide file tree
Showing 24 changed files with 3,550 additions and 4 deletions.
1 change: 1 addition & 0 deletions scripts/src/emu.lua
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ includedirs {
files {
MAME_DIR .. "src/emu/emu.h",
MAME_DIR .. "src/emu/main.h",
MAME_DIR .. "src/emu/main.cpp",
MAME_DIR .. "src/emu/gamedrv.h",
MAME_DIR .. "src/emu/hashfile.cpp",
MAME_DIR .. "src/emu/hashfile.h",
Expand Down
13 changes: 13 additions & 0 deletions scripts/src/lib.lua
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ project "utils"
MAME_DIR .. "src/lib/util/avhuff.h",
MAME_DIR .. "src/lib/util/aviio.cpp",
MAME_DIR .. "src/lib/util/aviio.h",
MAME_DIR .. "src/lib/util/base64.hpp",
MAME_DIR .. "src/lib/util/bitmap.cpp",
MAME_DIR .. "src/lib/util/bitmap.h",
MAME_DIR .. "src/lib/util/cdrom.cpp",
Expand All @@ -41,13 +42,18 @@ project "utils"
MAME_DIR .. "src/lib/util/chdcd.h",
MAME_DIR .. "src/lib/util/chdcodec.cpp",
MAME_DIR .. "src/lib/util/chdcodec.h",
MAME_DIR .. "src/lib/util/client_http.hpp",
MAME_DIR .. "src/lib/util/client_https.hpp",
MAME_DIR .. "src/lib/util/client_ws.hpp",
MAME_DIR .. "src/lib/util/client_wss.hpp",
MAME_DIR .. "src/lib/util/corealloc.h",
MAME_DIR .. "src/lib/util/corefile.cpp",
MAME_DIR .. "src/lib/util/corefile.h",
MAME_DIR .. "src/lib/util/corestr.cpp",
MAME_DIR .. "src/lib/util/corestr.h",
MAME_DIR .. "src/lib/util/coreutil.cpp",
MAME_DIR .. "src/lib/util/coreutil.h",
MAME_DIR .. "src/lib/util/crypto.hpp",
MAME_DIR .. "src/lib/util/delegate.cpp",
MAME_DIR .. "src/lib/util/delegate.h",
MAME_DIR .. "src/lib/util/flac.cpp",
Expand All @@ -71,14 +77,21 @@ project "utils"
MAME_DIR .. "src/lib/util/options.h",
MAME_DIR .. "src/lib/util/palette.cpp",
MAME_DIR .. "src/lib/util/palette.h",
MAME_DIR .. "src/lib/util/path_to_regex.cpp",
MAME_DIR .. "src/lib/util/path_to_regex.h",
MAME_DIR .. "src/lib/util/plaparse.cpp",
MAME_DIR .. "src/lib/util/plaparse.h",
MAME_DIR .. "src/lib/util/png.cpp",
MAME_DIR .. "src/lib/util/png.h",
MAME_DIR .. "src/lib/util/pool.cpp",
MAME_DIR .. "src/lib/util/pool.h",
MAME_DIR .. "src/lib/util/server_http.hpp",
MAME_DIR .. "src/lib/util/server_https.hpp",
MAME_DIR .. "src/lib/util/server_ws.hpp",
MAME_DIR .. "src/lib/util/server_wss.hpp",
MAME_DIR .. "src/lib/util/sha1.cpp",
MAME_DIR .. "src/lib/util/sha1.h",
MAME_DIR .. "src/lib/util/sha1.hpp",
MAME_DIR .. "src/lib/util/strformat.cpp",
MAME_DIR .. "src/lib/util/strformat.h",
MAME_DIR .. "src/lib/util/timeconv.cpp",
Expand Down
6 changes: 6 additions & 0 deletions src/emu/emuopts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,12 @@ const options_entry emu_options::s_option_entries[] =
{ OPTION_PLUGIN, nullptr, OPTION_STRING, "list of plugins to enable" },
{ OPTION_NO_PLUGIN, nullptr, OPTION_STRING, "list of plugins to disable" },
{ OPTION_LANGUAGE ";lang", "English", OPTION_STRING, "display language" },

{ nullptr, nullptr, OPTION_HEADER, "HTTP SERVER OPTIONS" },
{ OPTION_HTTP, "0", OPTION_BOOLEAN, "HTTP server enable" },
{ OPTION_HTTP_PORT, "8080", OPTION_INTEGER, "HTTP server port" },
{ OPTION_HTTP_ROOT, "web", OPTION_STRING, "HTTP server document root" },

{ nullptr }
};

Expand Down
9 changes: 9 additions & 0 deletions src/emu/emuopts.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@

#define OPTION_LANGUAGE "language"

#define OPTION_HTTP "http"
#define OPTION_HTTP_PORT "http_port"
#define OPTION_HTTP_ROOT "http_root"

//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
Expand Down Expand Up @@ -381,6 +385,11 @@ class emu_options : public core_options

const char *language() const { return value(OPTION_LANGUAGE); }

// Web server specific optopns
bool http() const { return value(OPTION_HTTP); }
short http_port() const { return int_value(OPTION_HTTP_PORT); }
const char *http_root() const { return value(OPTION_HTTP_ROOT); }

// cache frequently used options in members
void update_cached_options();

Expand Down
30 changes: 29 additions & 1 deletion src/emu/machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@
#include "network.h"
#include "ui/uimain.h"
#include <time.h>
#include "server_http.hpp"
#include "rapidjson/include/rapidjson/writer.h"
#include "rapidjson/include/rapidjson/stringbuffer.h"

#if defined(EMSCRIPTEN)
#include <emscripten.h>
Expand Down Expand Up @@ -332,6 +335,8 @@ int running_machine::run(bool quiet)
if (m_saveload_schedule != SLS_NONE)
handle_saveload();

export_http_api();

// run the CPUs until a reset or exit
m_hard_reset_pending = false;
while ((!m_hard_reset_pending && !m_exit_pending) || m_saveload_schedule != SLS_NONE)
Expand Down Expand Up @@ -1176,7 +1181,30 @@ running_machine::logerror_callback_item::logerror_callback_item(logerror_callbac
{
}


void running_machine::export_http_api()
{
m_manager.http_server()->on_get("/api/machine", [this](auto response, auto request)
{
rapidjson::StringBuffer s;
rapidjson::Writer<rapidjson::StringBuffer> writer(s);
writer.StartObject();
writer.Key("name");
writer.String(m_basename.c_str());

writer.Key("devices");
writer.StartArray();

device_iterator iter(root_device());
for (device_t &device : iter)
writer.String(device.tag());

writer.EndArray();
writer.EndObject();

response->type("application/json");
response->status(200).send(s.GetString());
});
}

//**************************************************************************
// SYSTEM TIME
Expand Down
1 change: 1 addition & 0 deletions src/emu/machine.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ class running_machine
void add_logerror_callback(logerror_callback callback);
void set_ui_active(bool active) { m_ui_active = active; }
void debug_break();
void export_http_api();

// TODO: Do saves and loads still require scheduling?
void immediate_save(const char *filename);
Expand Down
186 changes: 186 additions & 0 deletions src/emu/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// license:BSD-3-Clause
// copyright-holders:Nicola Salmoria, Aaron Giles
/***************************************************************************
main.cpp
Controls execution of the core MAME system.
***************************************************************************/

#include "emu.h"
#include "emuopts.h"
#include "main.h"
#include "server_http.hpp"
#include <fstream>

const static struct mapping
{
const char* extension;
const char* mime_type;
} mappings[] =
{
{ "aac", "audio/aac" },
{ "aat", "application/font-sfnt" },
{ "aif", "audio/x-aif" },
{ "arj", "application/x-arj-compressed" },
{ "asf", "video/x-ms-asf" },
{ "avi", "video/x-msvideo" },
{ "bmp", "image/bmp" },
{ "cff", "application/font-sfnt" },
{ "css", "text/css" },
{ "csv", "text/csv" },
{ "doc", "application/msword" },
{ "eps", "application/postscript" },
{ "exe", "application/octet-stream" },
{ "gif", "image/gif" },
{ "gz", "application/x-gunzip" },
{ "htm", "text/html" },
{ "html", "text/html" },
{ "ico", "image/x-icon" },
{ "ief", "image/ief" },
{ "jpeg", "image/jpeg" },
{ "jpg", "image/jpeg" },
{ "jpm", "image/jpm" },
{ "jpx", "image/jpx" },
{ "js", "application/javascript" },
{ "json", "application/json" },
{ "m3u", "audio/x-mpegurl" },
{ "m4v", "video/x-m4v" },
{ "mid", "audio/x-midi" },
{ "mov", "video/quicktime" },
{ "mp3", "audio/mpeg" },
{ "mp4", "video/mp4" },
{ "mpeg", "video/mpeg" },
{ "mpg", "video/mpeg" },
{ "oga", "audio/ogg" },
{ "ogg", "audio/ogg" },
{ "ogv", "video/ogg" },
{ "otf", "application/font-sfnt" },
{ "pct", "image/x-pct" },
{ "pdf", "application/pdf" },
{ "pfr", "application/font-tdpfr" },
{ "pict", "image/pict" },
{ "png", "image/png" },
{ "ppt", "application/x-mspowerpoint" },
{ "ps", "application/postscript" },
{ "qt", "video/quicktime" },
{ "ra", "audio/x-pn-realaudio" },
{ "ram", "audio/x-pn-realaudio" },
{ "rar", "application/x-arj-compressed" },
{ "rgb", "image/x-rgb" },
{ "rtf", "application/rtf" },
{ "sgm", "text/sgml" },
{ "shtm", "text/html" },
{ "shtml", "text/html" },
{ "sil", "application/font-sfnt" },
{ "svg", "image/svg+xml" },
{ "swf", "application/x-shockwave-flash" },
{ "tar", "application/x-tar" },
{ "tgz", "application/x-tar-gz" },
{ "tif", "image/tiff" },
{ "tiff", "image/tiff" },
{ "torrent", "application/x-bittorrent" },
{ "ttf", "application/font-sfnt" },
{ "txt", "text/plain" },
{ "wav", "audio/x-wav" },
{ "webm", "video/webm" },
{ "woff", "application/font-woff" },
{ "wrl", "model/vrml" },
{ "xhtml", "application/xhtml+xml" },
{ "xls", "application/x-msexcel" },
{ "xml", "text/xml" },
{ "xsl", "application/xml" },
{ "xslt", "application/xml" },
{ "zip", "application/x-zip-compressed" }
};

static std::string extension_to_type(const std::string& extension)
{
for (mapping m : mappings)
{
if (m.extension == extension)
{
return m.mime_type;
}
}

return "text/plain";
}

machine_manager::machine_manager(emu_options& options, osd_interface& osd)
: m_osd(osd),
m_options(options),
m_machine(nullptr),
m_io_context(std::make_shared<asio::io_context>())
{
}

machine_manager::~machine_manager()
{
if (options().http())
{
m_server->stop();
}
m_server_thread.join();
}

void machine_manager::start_http_server()
{
if (options().http())
{
m_server = std::make_unique<webpp::http_server>();
m_server->m_config.port = options().http_port();
m_server->set_io_context(m_io_context);
std::string doc_root = options().http_root();

m_server->on_get([this, doc_root](auto response, auto request) {
std::string path = request->path;
// If path ends in slash (i.e. is a directory) then add "index.html".
if (path[path.size() - 1] == '/')
{
path += "index.html";
}

std::size_t last_qmark_pos = path.find_last_of("?");
if (last_qmark_pos != std::string::npos)
path = path.substr(0, last_qmark_pos - 1);

// Determine the file extension.
std::size_t last_slash_pos = path.find_last_of("/");
std::size_t last_dot_pos = path.find_last_of(".");
std::string extension;
if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos)
{
extension = path.substr(last_dot_pos + 1);
}

// Open the file to send back.
std::string full_path = doc_root + path;
std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary);
if (!is)
{
response->status(400).send("Error");
}

// Fill out the reply to be sent to the client.
std::string content;
char buf[512];
while (is.read(buf, sizeof(buf)).gcount() > 0)
content.append(buf, size_t(is.gcount()));

response->type(extension_to_type(extension));
response->status(200).send(content);

});
m_server->start();
}
}

void machine_manager::start_context()
{
m_server_thread = std::thread([this]() {
m_io_context->run();
});
}

19 changes: 17 additions & 2 deletions src/emu/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,23 @@ class emulator_info

// ======================> machine_manager
class ui_manager;
namespace asio
{
class io_context;
}
namespace webpp
{
class http_server;
}

class machine_manager
{
DISABLE_COPYING(machine_manager);
protected:
// construction/destruction
machine_manager(emu_options &options, osd_interface &osd) : m_osd(osd), m_options(options), m_machine(nullptr) { }
machine_manager(emu_options& options, osd_interface& osd);
public:
virtual ~machine_manager() { }
virtual ~machine_manager();

osd_interface &osd() const { return m_osd; }
emu_options &options() const { return m_options; }
Expand All @@ -87,10 +95,17 @@ class machine_manager
virtual void ui_initialize(running_machine& machine) { }

virtual void update_machine() { }

void start_http_server();
void start_context();
webpp::http_server* http_server() const { return m_server.get(); }
protected:
osd_interface & m_osd; // reference to OSD system
emu_options & m_options; // reference to options
running_machine * m_machine;
std::shared_ptr<asio::io_context> m_io_context;
std::unique_ptr<webpp::http_server> m_server;
std::thread m_server_thread;
};


Expand Down
6 changes: 5 additions & 1 deletion src/frontend/mame/clifront.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,13 @@ int cli_frontend::execute(int argc, char **argv)

load_translation(m_options);

manager->start_http_server();

manager->start_luaengine();

start_execution(manager, argc, argv, option_errors);
manager->start_context();

start_execution(manager, argc, argv, option_errors);
}
// handle exceptions of various types
catch (emu_fatalerror &fatal)
Expand Down
Loading

0 comments on commit 63e3f48

Please sign in to comment.