Add experimental webserver API and home page

Added webserver::IRequest/IResponse/IDelegate interfaces.
This commit is contained in:
David Capello 2013-04-01 19:37:02 -03:00
parent 2364776c44
commit d259b5c394
8 changed files with 338 additions and 23 deletions

17
data/www/aseprite.css Normal file
View File

@ -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;
}

22
data/www/aseprite.js Normal file
View File

@ -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);

BIN
data/www/aseprite.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

31
data/www/index.html Normal file
View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<title>Aseprite</title>
<link href="aseprite.css" rel="stylesheet">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="aseprite.js"></script>
<script type="text/javascript">
$(function() {
$('#run').click(function() {
eval($('#code').text());
});
});
</script>
</head>
<body>
<img src="aseprite.png">
<h2>Console:</h2>
<pre id="console"></pre>
<h2>Test your code:</h2>
<textarea id="code" cols="80" rows="10">
Aseprite.get_version(function(version){
alert(version.package + "/" + version.version + " APIv" + version.api +
" (" + version.webserver + " webserver)");
});</textarea><br>
<input id="run" type="button" value="Run" style="width:200px">
</body>
</html>

View File

@ -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 <fstream>
#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;
}
}
}
}

View File

@ -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;
};
}

View File

@ -21,6 +21,7 @@
#include "webserver/webserver.h"
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "mongoose.h"
#include <sstream>
@ -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<WebServer::WebServerImpl*>(request_info->user_data);
WebServer::WebServerImpl* webServer =
reinterpret_cast<WebServer::WebServerImpl*>(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();
}
}

View File

@ -20,17 +20,44 @@
#define WEBSERVER_WEBSERVER_H_INCLUDED
#include "base/disable_copying.h"
#include <string>
#include <iosfwd>
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;