diff --git a/data/www/aseprite.css b/data/www/aseprite.css new file mode 100644 index 000000000..f8845f18c --- /dev/null +++ b/data/www/aseprite.css @@ -0,0 +1,17 @@ +body, h2 { + font-family: Segoe UI, Arial; +} + +h2 { + font-size: 14pt; + font-weight: normal; + color: #669; +} + +#console { + border:8px solid #eee; + background-color:#eee; + width:80%; + height:128px; + overflow:auto; +} diff --git a/data/www/aseprite.js b/data/www/aseprite.js new file mode 100644 index 000000000..029d16d10 --- /dev/null +++ b/data/www/aseprite.js @@ -0,0 +1,22 @@ +window.Aseprite = window.Aseprite || {}; +(function(Aseprite) { + 'use strict'; + + Aseprite.host = 'http://127.0.0.1:10453'; + + Aseprite.get = function(uri, callback) { + $.ajax({ + type: 'GET', + url: Aseprite.host + uri, + dataType: 'json', + cache: false, + success: function(data) { + callback(data); + } + }); + } + + Aseprite.get_version = function(callback) { + Aseprite.get('/version', callback); + } +})(window.Aseprite); diff --git a/data/www/aseprite.png b/data/www/aseprite.png new file mode 100644 index 000000000..740a6f826 Binary files /dev/null and b/data/www/aseprite.png differ diff --git a/data/www/index.html b/data/www/index.html new file mode 100644 index 000000000..6fb915033 --- /dev/null +++ b/data/www/index.html @@ -0,0 +1,31 @@ + + + + Aseprite + + + + + + + + +

Console:

+

+
+  

Test your code:

+
+ + + + diff --git a/src/app/webserver.cpp b/src/app/webserver.cpp index eb80efbd1..d8920f1bc 100644 --- a/src/app/webserver.cpp +++ b/src/app/webserver.cpp @@ -22,13 +22,29 @@ #include "app/webserver.h" +#include "base/fs.h" +#include "base/path.h" +#include "resource_finder.h" #include "webserver/webserver.h" +#include + +#define API_VERSION 1 + namespace app { WebServer::WebServer() : m_webServer(NULL) { + ResourceFinder rf; + rf.findInDataDir("www"); + + while (const char* path = rf.next()) { + if (base::directory_exists(path)) { + m_wwwpath = path; + break; + } + } } WebServer::~WebServer() @@ -38,7 +54,38 @@ WebServer::~WebServer() void WebServer::start() { - m_webServer = new webserver::WebServer; + m_webServer = new webserver::WebServer(this); +} + +void WebServer::onProcessRequest(webserver::IRequest* request, + webserver::IResponse* response) +{ + std::string uri = request->getUri(); + if (!uri.empty() && uri[uri.size()-1] == '/') + uri.erase(uri.size()-1); + + if (uri == "/version") { + response->setContentType("text/plain"); + response->getStream() << "{\"package\":\"" << PACKAGE "\"," + << "\"version\":\"" << VERSION << "\"," + << "\"webserver\":\"" << m_webServer->getName() << "\"," + << "\"api\":\"" << API_VERSION << "\"}"; + } + else { + if (uri == "/" || uri.empty()) + uri = "/index.html"; + + std::string fn = base::join_path(m_wwwpath, uri); + if (base::file_exists(fn)) { + response->sendFile(fn.c_str()); + } + else { + response->setStatusCode(404); + response->getStream() << "Not found\n" + << "URI = " << uri << "\n" + << "Local file = " << fn; + } + } } } diff --git a/src/app/webserver.h b/src/app/webserver.h index e71acc08f..00c205590 100644 --- a/src/app/webserver.h +++ b/src/app/webserver.h @@ -21,11 +21,12 @@ #ifdef ENABLE_WEBSERVER +#include "base/compiler_specific.h" #include "webserver/webserver.h" namespace app { - class WebServer + class WebServer : public webserver::IDelegate { public: WebServer(); @@ -33,8 +34,13 @@ namespace app { void start(); + // webserver::IDelegate implementation + virtual void onProcessRequest(webserver::IRequest* request, + webserver::IResponse* response) OVERRIDE; + private: webserver::WebServer* m_webServer; + std::string m_wwwpath; }; } diff --git a/src/webserver/webserver.cpp b/src/webserver/webserver.cpp index fbd8a8188..87ca5e5b5 100644 --- a/src/webserver/webserver.cpp +++ b/src/webserver/webserver.cpp @@ -21,6 +21,7 @@ #include "webserver/webserver.h" #include "base/bind.h" +#include "base/compiler_specific.h" #include "mongoose.h" #include @@ -29,11 +30,87 @@ namespace webserver { static int begin_request_handler(mg_connection* conn); +class RequestResponseImpl : public IRequest + , public IResponse +{ +public: + RequestResponseImpl(mg_connection* conn, std::ostream& stream) + : m_conn(conn) + , m_requestInfo(mg_get_request_info(conn)) + , m_stream(stream) + , m_code(200) + , m_done(false) + , m_contentType("text/plain") + { + } + + // IRequest implementation + + virtual const char* getRequestMethod() OVERRIDE { + return m_requestInfo->request_method; + } + + virtual const char* getUri() OVERRIDE { + return m_requestInfo->uri; + } + + virtual const char* getHttpVersion() OVERRIDE { + return m_requestInfo->http_version; + } + + virtual const char* getQueryString() OVERRIDE { + return m_requestInfo->query_string; + } + + // IResponse implementation + + virtual void setStatusCode(int code) OVERRIDE { + m_code = code; + } + + virtual void setContentType(const char* contentType) OVERRIDE { + m_contentType = contentType; + } + + virtual std::ostream& getStream() OVERRIDE { + return m_stream; + } + + virtual void sendFile(const char* path) OVERRIDE { + mg_send_file(m_conn, path); + m_done = true; + } + + int getStatusCode() const { + return m_code; + } + + const char* getContentType() const { + return m_contentType.c_str(); + } + + bool done() { + return m_done; + } + +private: + mg_connection* m_conn; + const mg_request_info* m_requestInfo; + std::ostream& m_stream; + int m_code; + bool m_done; + std::string m_contentType; +}; + class WebServer::WebServerImpl { public: - WebServerImpl() { - const char* options[] = { "listening_ports", "10453", NULL}; + WebServerImpl(IDelegate* delegate) + : m_delegate(delegate) { + const char* options[] = { + "listening_ports", "10453", + NULL + }; memset(&m_callbacks, 0, sizeof(m_callbacks)); m_callbacks.begin_request = &begin_request_handler; @@ -45,22 +122,38 @@ public: mg_stop(m_context); } - int onBeginRequest(mg_connection* conn) { - const mg_request_info* request_info = mg_get_request_info(conn); - std::stringstream content; + std::string getName() const { + std::string name; + name = "mongoose "; + name += mg_version(); + return name; + } - // Prepare the message we're going to send - content << PACKAGE << " v" << VERSION; + int onBeginRequest(mg_connection* conn) { + std::stringstream body; + RequestResponseImpl rr(conn, body); + m_delegate->onProcessRequest(&rr, &rr); + + if (rr.done()) + return 1; // Send HTTP reply to the client - std::string contentStr = content.str(); - mg_printf(conn, - "HTTP/1.1 200 OK\r\n" - "Content-Type: text/plain\r\n" - "Content-Length: %d\r\n" - "\r\n" - "%s", - contentStr.size(), contentStr.c_str()); + std::string bodyStr = body.str(); + std::stringstream headers; + + headers << "HTTP/1.1 " + << rr.getStatusCode() << " " + << getStatusCodeString(rr.getStatusCode()) << "\r\n" + << "Server: mongoose/" << mg_version() << "\r\n" + << "Content-Type: " << rr.getContentType() << "\r\n" + << "Content-Length: " << bodyStr.size() << "\r\n" + << "Access-Control-Allow-Origin: *\r\n" + << "\r\n"; + + std::string headersStr = headers.str(); + + mg_write(conn, (const void*)headersStr.c_str(), headersStr.size()); + mg_write(conn, (const void*)bodyStr.c_str(), bodyStr.size()); // Returning non-zero tells mongoose that our function has replied to // the client, and mongoose should not send client any more data. @@ -68,6 +161,71 @@ public: } private: + const char* getStatusCodeString(int code) { + switch (code) { + case 100: return "Continue"; + case 101: return "Switching Protocols"; + case 102: return "Processing"; + case 200: return "OK"; + case 201: return "Created"; + case 202: return "Accepted"; + case 203: return "Non-Authoritative Information"; + case 204: return "No Content"; + case 205: return "Reset Content"; + case 206: return "Partial Content"; + case 207: return "Multi-Status"; + case 208: return "Already Reported"; + case 226: return "IM Used"; + case 300: return "Multiple Choices"; + case 301: return "Moved Permanently"; + case 302: return "Found"; + case 303: return "See Other"; + case 304: return "Not Modified"; + case 305: return "Use Proxy"; + case 306: return "Reserved"; + case 307: return "Temporary Redirect"; + case 308: return "Permanent Redirect"; + case 400: return "Bad Request"; + case 401: return "Unauthorized"; + case 402: return "Payment Required"; + case 403: return "Forbidden"; + case 404: return "Not Found"; + case 405: return "Method Not Allowed"; + case 406: return "Not Acceptable"; + case 407: return "Proxy Authentication Required"; + case 408: return "Request Timeout"; + case 409: return "Conflict"; + case 410: return "Gone"; + case 411: return "Length Required"; + case 412: return "Precondition Failed"; + case 413: return "Request Entity Too Large"; + case 414: return "Request-URI Too Long"; + case 415: return "Unsupported Media Type"; + case 416: return "Requested Range Not Satisfiable"; + case 417: return "Expectation Failed"; + case 422: return "Unprocessable Entity"; + case 423: return "Locked"; + case 424: return "Failed Dependency"; + case 426: return "Upgrade Required"; + case 428: return "Precondition Required"; + case 429: return "Too Many Requests"; + case 431: return "Request Header Fields Too Large"; + case 500: return "Internal Server Error"; + case 501: return "Not Implemented"; + case 502: return "Bad Gateway"; + case 503: return "Service Unavailable"; + case 504: return "Gateway Timeout"; + case 505: return "HTTP Version Not Supported"; + case 506: return "Variant Also Negotiates (Experimental)"; + case 507: return "Insufficient Storage"; + case 508: return "Loop Detected"; + case 510: return "Not Extended"; + case 511: return "Network Authentication Required"; + default: return "Unassigned"; + } + } + + IDelegate* m_delegate; mg_context* m_context; mg_callbacks m_callbacks; }; @@ -75,12 +233,14 @@ private: static int begin_request_handler(mg_connection* conn) { const mg_request_info* request_info = mg_get_request_info(conn); - WebServer::WebServerImpl* webServer = reinterpret_cast(request_info->user_data); + WebServer::WebServerImpl* webServer = + reinterpret_cast(request_info->user_data); + return webServer->onBeginRequest(conn); } -WebServer::WebServer() - : m_impl(new WebServerImpl) +WebServer::WebServer(IDelegate* delegate) + : m_impl(new WebServerImpl(delegate)) { } @@ -89,4 +249,9 @@ WebServer::~WebServer() delete m_impl; } +std::string WebServer::getName() const +{ + return m_impl->getName(); +} + } diff --git a/src/webserver/webserver.h b/src/webserver/webserver.h index 80f84745a..4b6f53de3 100644 --- a/src/webserver/webserver.h +++ b/src/webserver/webserver.h @@ -20,17 +20,44 @@ #define WEBSERVER_WEBSERVER_H_INCLUDED #include "base/disable_copying.h" +#include +#include namespace webserver { - class WebServer - { + class IRequest { + public: + virtual ~IRequest() { } + virtual const char* getRequestMethod() = 0; + virtual const char* getUri() = 0; + virtual const char* getHttpVersion() = 0; + virtual const char* getQueryString() = 0; + }; + + class IResponse { + public: + virtual ~IResponse() { } + virtual void setStatusCode(int code) = 0; + virtual void setContentType(const char* contentType) = 0; + virtual std::ostream& getStream() = 0; + virtual void sendFile(const char* path) = 0; + }; + + class IDelegate { + public: + virtual ~IDelegate() { } + virtual void onProcessRequest(IRequest* request, IResponse* response) = 0; + }; + + class WebServer { public: class WebServerImpl; - WebServer(); + WebServer(IDelegate* delegate); ~WebServer(); + std::string getName() const; + private: WebServerImpl* m_impl;