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;