summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/local-def.h16
-rw-r--r--include/proto-def.h23
-rw-r--r--nbproject/Makefile-Debug.mk2
-rw-r--r--nbproject/configurations.xml1
-rw-r--r--src/afbs-api.c28
-rw-r--r--src/alsa-api.c18
-rw-r--r--src/config.c59
-rw-r--r--src/dbus-api.c15
-rw-r--r--src/http-svc.c197
-rw-r--r--src/main.c4
-rw-r--r--src/rest-api.c61
-rw-r--r--src/session.c2
12 files changed, 252 insertions, 174 deletions
diff --git a/include/local-def.h b/include/local-def.h
index f69ddcbe..3f0a26a0 100644
--- a/include/local-def.h
+++ b/include/local-def.h
@@ -17,7 +17,6 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- $Id: $
*/
#define _GNU_SOURCE
@@ -35,7 +34,7 @@
#include <time.h>
#include <json.h>
#include <microhttpd.h>
-
+#include <magic.h>
#define AJQ_VERSION "0.1"
@@ -54,7 +53,8 @@ typedef int BOOL;
#define FAILED -1
// prebuild json error are constructed in config.c
-typedef enum { AFB_FALSE, AFB_TRUE, AFB_FATAL, AFB_FAIL, AFB_WARNING, AFB_EMPTY, AFB_SUCCESS} AFB_ERROR;
+typedef enum { AFB_FALSE, AFB_TRUE, AFB_FATAL, AFB_FAIL, AFB_WARNING, AFB_EMPTY, AFB_SUCCESS} AFB_error;
+
extern char *ERROR_LABEL[];
#define ERROR_LABEL_DEF {"false", "true","fatal", "fail", "warning", "empty", "success"}
@@ -70,7 +70,7 @@ typedef struct {
int level;
char* label;
json_object *json;
-} AFB_ErrorT;
+} AFB_errorT;
// Post handler
typedef struct {
@@ -79,6 +79,11 @@ typedef struct {
int uid;
} AFB_HttpPost;
+typedef struct {
+ char path[512];
+ int fd;
+} AFB_staticfile;
+
// some usefull static object initialized when entering listen loop.
extern int verbose;
@@ -112,7 +117,6 @@ typedef struct {
char *configfile; // where to store configuration on gateway exit
uid_t setuid;
int cacheTimeout;
- AFB_redirect_msg html5; // html5 redirect message
} AFB_config;
// Command line structure hold cli --command + help text
@@ -137,6 +141,7 @@ typedef struct {
AFB_type type;
char *info;
char *prefix;
+ size_t prefixlen;
json_object *jtype;
AFB_restapi *apis;
} AFB_plugin;
@@ -154,6 +159,7 @@ typedef struct {
int fakemod; // respond to GET/POST request without interacting with sndboard
int forceexit; // when autoconfig from script force exit before starting server
AFB_plugin **plugins; // pointer to REST/API plugins
+ magic_t magic; // Mime type file magic lib
} AFB_session;
diff --git a/include/proto-def.h b/include/proto-def.h
index 934bf30f..d1b0c3fb 100644
--- a/include/proto-def.h
+++ b/include/proto-def.h
@@ -23,31 +23,38 @@
// Rest-api
PUBLIC json_object* pingSample (AFB_plugin *plugin, AFB_session *session, AFB_request *post);
PUBLIC const char* getQueryValue (AFB_request * request, char *name);
-PUBLIC AFB_plugin *afsvRegister (AFB_session *session);
PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, const char *method, const char* url);
+void initPlugins (AFB_session *session);
+
+typedef AFB_plugin* (*AFB_pluginCB)(AFB_session *session);
+PUBLIC AFB_plugin* afsvRegister (AFB_session *session);
+PUBLIC AFB_plugin* dbusRegister (AFB_session *session);
+PUBLIC AFB_plugin* alsaRegister (AFB_session *session);
+
+
// Session handling
-PUBLIC AFB_ERROR sessionCheckdir (AFB_session *session);
+PUBLIC AFB_error sessionCheckdir (AFB_session *session);
PUBLIC json_object *sessionList (AFB_session *session, AFB_request *request);
PUBLIC json_object *sessionToDisk (AFB_session *session, AFB_request *request, char *name,json_object *jsonSession);
PUBLIC json_object *sessionFromDisk (AFB_session *session, AFB_request *request, char *name);
// Httpd server
-PUBLIC AFB_ERROR httpdStart (AFB_session *session);
-PUBLIC AFB_ERROR httpdLoop (AFB_session *session);
+PUBLIC AFB_error httpdStart (AFB_session *session);
+PUBLIC AFB_error httpdLoop (AFB_session *session);
PUBLIC void httpdStop (AFB_session *session);
// config management
PUBLIC char *configTime (void);
PUBLIC AFB_session *configInit (void);
-PUBLIC json_object *jsonNewMessage (AFB_ERROR level, char* format, ...);
-PUBLIC json_object *jsonNewStatus (AFB_ERROR level);
+PUBLIC json_object *jsonNewMessage (AFB_error level, char* format, ...);
+PUBLIC json_object *jsonNewStatus (AFB_error level);
PUBLIC json_object *jsonNewjtype (void);
-PUBLIC json_object *jsonNewMessage (AFB_ERROR level, char* format, ...);
+PUBLIC json_object *jsonNewMessage (AFB_error level, char* format, ...);
PUBLIC void jsonDumpObject (json_object * jObject);
-PUBLIC AFB_ERROR configLoadFile (AFB_session * session, AFB_config *cliconfig);
+PUBLIC AFB_error configLoadFile (AFB_session * session, AFB_config *cliconfig);
PUBLIC void configStoreFile (AFB_session * session);
diff --git a/nbproject/Makefile-Debug.mk b/nbproject/Makefile-Debug.mk
index fb778c72..1866ff18 100644
--- a/nbproject/Makefile-Debug.mk
+++ b/nbproject/Makefile-Debug.mk
@@ -59,7 +59,7 @@ FFLAGS=
ASFLAGS=
# Link Libraries and Options
-LDLIBSOPTIONS=`pkg-config --libs libmicrohttpd` `pkg-config --libs json-c`
+LDLIBSOPTIONS=`pkg-config --libs libmicrohttpd` `pkg-config --libs json-c` -lmagic
# Build Targets
.build-conf: ${BUILD_SUBPROJECTS}
diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml
index f30d8c0b..c0793702 100644
--- a/nbproject/configurations.xml
+++ b/nbproject/configurations.xml
@@ -52,6 +52,7 @@
<linkerLibItems>
<linkerOptionItem>`pkg-config --libs libmicrohttpd`</linkerOptionItem>
<linkerOptionItem>`pkg-config --libs json-c`</linkerOptionItem>
+ <linkerLibLibItem>magic</linkerLibLibItem>
</linkerLibItems>
</linkerTool>
</compileType>
diff --git a/src/afbs-api.c b/src/afbs-api.c
index 5b7d4896..40e89d02 100644
--- a/src/afbs-api.c
+++ b/src/afbs-api.c
@@ -36,23 +36,23 @@ STATIC json_object* pingAfbs (AFB_session *session, AFB_request *request) {
STATIC AFB_restapi pluginApis[]= {
- {"/ping" , (AFB_apiCB)pingSample ,"Ping Service"},
- {"/get-all" , (AFB_apiCB)pingAfbs ,"Ping Application Framework"},
- {"/get-one" , (AFB_apiCB)pingSample ,"Verbose Mode"},
- {"/start-one", (AFB_apiCB)pingSample ,"Verbose Mode"},
- {"/stop-one" , (AFB_apiCB)pingSample ,"Verbose Mode"},
- {"/probe-one", (AFB_apiCB)pingSample ,"Verbose Mode"},
- {"/ctx-store", (AFB_apiCB)pingSample ,"Verbose Mode"},
- {"/ctx-load" , (AFB_apiCB)pingSample ,"Verbose Mode"},
+ {"ping" , (AFB_apiCB)pingSample ,"Ping Service"},
+ {"get-all" , (AFB_apiCB)pingAfbs ,"Ping Application Framework"},
+ {"get-one" , (AFB_apiCB)pingSample ,"Verbose Mode"},
+ {"start-one", (AFB_apiCB)pingSample ,"Verbose Mode"},
+ {"stop-one" , (AFB_apiCB)pingSample ,"Verbose Mode"},
+ {"probe-one", (AFB_apiCB)pingSample ,"Verbose Mode"},
+ {"ctx-store", (AFB_apiCB)pingSample ,"Verbose Mode"},
+ {"ctx-load" , (AFB_apiCB)pingSample ,"Verbose Mode"},
{0,0,0}
};
PUBLIC AFB_plugin *afsvRegister (AFB_session *session) {
- AFB_plugin plugin;
- plugin.type = AFB_PLUGIN;
- plugin.info = "Application Framework Binder Service";
- plugin.prefix= "afbs"; // url base
- plugin.apis = pluginApis;
+ AFB_plugin *plugin = malloc (sizeof (AFB_plugin));
+ plugin->type = AFB_PLUGIN;
+ plugin->info = "Application Framework Binder Service";
+ plugin->prefix= "afbs"; // url base
+ plugin->apis = pluginApis;
- return (&plugin);
+ return (plugin);
}; \ No newline at end of file
diff --git a/src/alsa-api.c b/src/alsa-api.c
index dca372ab..ab7dced3 100644
--- a/src/alsa-api.c
+++ b/src/alsa-api.c
@@ -29,20 +29,20 @@ STATIC json_object* pingAfbs (AFB_plugin *plugin, AFB_session *session, struct M
STATIC AFB_restapi pluginApis[]= {
- {"/ping" , (AFB_apiCB)pingSample ,"Ping Service"},
- {"/get-all" , (AFB_apiCB)pingAfbs ,"Ping Application Framework"},
- {"/get-one" , (AFB_apiCB)pingSample ,"Verbose Mode"},
- {"/start-one", (AFB_apiCB)pingSample ,"Verbose Mode"},
- {"/stop-one" , (AFB_apiCB)pingSample ,"Verbose Mode"},
- {"/probe-one", (AFB_apiCB)pingSample ,"Verbose Mode"},
- {"/ctx-store", (AFB_apiCB)pingSample ,"Verbose Mode"},
- {"/ctx-load" , (AFB_apiCB)pingSample ,"Verbose Mode"},
+ {"ping" , (AFB_apiCB)pingSample ,"Ping Service"},
+ {"get-all" , (AFB_apiCB)pingAfbs ,"Ping Application Framework"},
+ {"get-one" , (AFB_apiCB)pingSample ,"Verbose Mode"},
+ {"start-one", (AFB_apiCB)pingSample ,"Verbose Mode"},
+ {"stop-one" , (AFB_apiCB)pingSample ,"Verbose Mode"},
+ {"probe-one", (AFB_apiCB)pingSample ,"Verbose Mode"},
+ {"ctx-store", (AFB_apiCB)pingSample ,"Verbose Mode"},
+ {"ctx-load" , (AFB_apiCB)pingSample ,"Verbose Mode"},
{0,0,0}
};
PUBLIC AFB_plugin *alsaRegister (AFB_session *session) {
AFB_plugin *plugin = malloc (sizeof (AFB_plugin));
-
+ plugin->type = AFB_PLUGIN;
plugin->info = "Application Framework Binder Service";
plugin->prefix = "alsa";
plugin->apis = pluginApis;
diff --git a/src/config.c b/src/config.c
index 893b3a5f..6691ca03 100644
--- a/src/config.c
+++ b/src/config.c
@@ -33,8 +33,8 @@
PUBLIC char *ERROR_LABEL[]=ERROR_LABEL_DEF;
PUBLIC int verbose;
-STATIC AFB_ErrorT AFB_Error [AFB_SUCCESS+1];
-STATIC json_object *AFBJsonType;
+STATIC AFB_errorT AFBerr [AFB_SUCCESS+1];
+STATIC json_object *jTypeStatic;
/* ------------------------------------------------------------------------------
* Get localtime and return in a string
@@ -56,15 +56,11 @@ PUBLIC char * configTime (void) {
}
// load config from disk and merge with CLI option
-PUBLIC AFB_ERROR configLoadFile (AFB_session * session, AFB_config *cliconfig) {
+PUBLIC AFB_error configLoadFile (AFB_session * session, AFB_config *cliconfig) {
static char cacheTimeout [10];
int fd;
json_object * AFBConfig, *value;
- // fix config redirect message
- session->config->html5.msg = "Angular/HTML5 redirect";
- session->config->html5.len = strlen(session->config->html5.msg);
-
// default HTTP port
if (cliconfig->httpdPort == 0) session->config->httpdPort=1234;
else session->config->httpdPort=cliconfig->httpdPort;
@@ -88,16 +84,27 @@ PUBLIC AFB_ERROR configLoadFile (AFB_session * session, AFB_config *cliconfig) {
// if no Angular/HTML5 rootbase let's try '/' as default
if (cliconfig->rootbase == NULL) {
- session->config->rootbase = "/";
+ session->config->rootbase = "/opa";
} else {
- session->config->console= cliconfig->console;
+ session->config->rootbase= cliconfig->rootbase;
}
- // if no rootapi use '/api'
- if (cliconfig->rootbase == NULL) {
- session->config->rootbase = "/api";
+ if (cliconfig->rootapi == NULL) {
+ session->config->rootapi = "/api";
} else {
- session->config->console= cliconfig->console;
+ session->config->rootapi= cliconfig->rootapi;
+ }
+
+ if (cliconfig->smack == NULL) {
+ session->config->smack = "demo";
+ } else {
+ session->config->smack= cliconfig->smack;
+ }
+
+ if (cliconfig->smack == NULL) {
+ session->config->plugins = "all";
+ } else {
+ session->config->plugins= cliconfig->plugins;
}
@@ -204,6 +211,8 @@ PUBLIC AFB_ERROR configLoadFile (AFB_session * session, AFB_config *cliconfig) {
session->cacheTimeout = cacheTimeout; // httpd uses cacheTimeout string version
json_object_put (AFBConfig); // decrease reference count to free the json object
+
+
return AFB_SUCCESS;
}
@@ -258,29 +267,31 @@ PUBLIC AFB_session *configInit () {
// stack config handle into session
session->config = config;
- AFBJsonType = json_object_new_string ("AFB_message");
+ jTypeStatic = json_object_new_string ("AFB_message");
// initialise JSON constant messages and increase reference count to make them permanent
verbosesav = verbose;
verbose = 0; // run initialisation in silent mode
-
for (idx = 0; idx <= AFB_SUCCESS; idx++) {
- AFB_Error[idx].level = idx;
- AFB_Error[idx].label = ERROR_LABEL [idx];
- AFB_Error[idx].json = jsonNewMessage (idx, NULL);
+ AFBerr[idx].level = idx;
+ AFBerr[idx].label = ERROR_LABEL [idx];
+ AFBerr[idx].json = jsonNewMessage (idx, NULL);
}
verbose = verbosesav;
+ // Load Plugins
+ initPlugins (session);
+
return (session);
}
// get JSON object from error level and increase its reference count
-PUBLIC json_object *jsonNewStatus (AFB_ERROR level) {
+PUBLIC json_object *jsonNewStatus (AFB_error level) {
- json_object *target = AFB_Error[level].json;
+ json_object *target = AFBerr[level].json;
json_object_get (target);
return (target);
@@ -288,12 +299,12 @@ PUBLIC json_object *jsonNewStatus (AFB_ERROR level) {
// get AFB object type with adequate usage count
PUBLIC json_object *jsonNewjtype (void) {
- json_object_get (AFBJsonType); // increase reference count
- return (AFBJsonType);
+ json_object_get (jTypeStatic); // increase reference count
+ return (jTypeStatic);
}
// build an ERROR message and return it as a valid json object
-PUBLIC json_object *jsonNewMessage (AFB_ERROR level, char* format, ...) {
+PUBLIC json_object *jsonNewMessage (AFB_error level, char* format, ...) {
static int count = 0;
json_object * AFBResponse;
va_list args;
@@ -313,7 +324,7 @@ PUBLIC json_object *jsonNewMessage (AFB_ERROR level, char* format, ...) {
json_object_object_add (AFBResponse, "info" , json_object_new_string (message));
}
if (verbose) {
- fprintf (stderr, "AFB:%-6s [%3d]: ", AFB_Error [level].label, count++);
+ fprintf (stderr, "AFB:%-6s [%3d]: ", AFBerr [level].label, count++);
if (format != NULL) {
fprintf (stderr, "%s", message);
} else {
diff --git a/src/dbus-api.c b/src/dbus-api.c
index febe87d9..9d34e436 100644
--- a/src/dbus-api.c
+++ b/src/dbus-api.c
@@ -19,7 +19,7 @@
#include "local-def.h"
-STATIC json_object* pingAfbs (AFB_plugin *plugin, AFB_session *session, struct MHD_Connection *connection, AFB_request *request) {
+STATIC json_object* pingAfbs (AFB_plugin *plugin, AFB_session *session, AFB_request *request) {
static pingcount=0;
json_object *response;
response = jsonNewMessage(AFB_SUCCESS, "Ping Application Framework %d", pingcount++);
@@ -29,20 +29,15 @@ STATIC json_object* pingAfbs (AFB_plugin *plugin, AFB_session *session, struct M
STATIC AFB_restapi pluginApis[]= {
- {"/ping" , (AFB_apiCB)pingSample ,"Ping Service"},
- {"/get-all" , (AFB_apiCB)pingAfbs ,"Ping Application Framework"},
- {"/get-one" , (AFB_apiCB)pingSample ,"Verbose Mode"},
- {"/start-one", (AFB_apiCB)pingSample ,"Verbose Mode"},
- {"/stop-one" , (AFB_apiCB)pingSample ,"Verbose Mode"},
- {"/probe-one", (AFB_apiCB)pingSample ,"Verbose Mode"},
- {"/ctx-store", (AFB_apiCB)pingSample ,"Verbose Mode"},
- {"/ctx-load" , (AFB_apiCB)pingSample ,"Verbose Mode"},
+ {"ping" , (AFB_apiCB)pingAfbs ,"Ping Application Framework"},
+ {"ctx-store", (AFB_apiCB)pingSample ,"Verbose Mode"},
+ {"ctx-load" , (AFB_apiCB)pingSample ,"Verbose Mode"},
{0,0,0}
};
PUBLIC AFB_plugin *dbusRegister (AFB_session *session) {
AFB_plugin *plugin = malloc (sizeof (AFB_plugin));
-
+ plugin->type = AFB_PLUGIN;
plugin->info = "Application Framework Binder Service";
plugin->prefix= "dbus";
plugin->apis = pluginApis;
diff --git a/src/http-svc.c b/src/http-svc.c
index b29a4bb8..d638f6e9 100644
--- a/src/http-svc.c
+++ b/src/http-svc.c
@@ -31,16 +31,43 @@
#include <microhttpd.h>
+
#include <sys/stat.h>
#include "../include/local-def.h"
+// let's compute fixed URL length only once
+static apiUrlLen=0;
+static baseUrlLen=0;
+
// proto missing from GCC
char *strcasestr(const char *haystack, const char *needle);
static int rqtcount = 0; // dummy request rqtcount to make each message be different
static int postcount = 0;
-static int aipUrlLen=0; // do not compute apiurl for each call
-static int baseUrlLen=0; // do not compute baseurl for each call
+
+// try to open libmagic to handle mine types
+static AFB_error initLibMagic (AFB_session *session) {
+ const char *magic_full;
+
+ /*MAGIC_MIME tells magic to return a mime of the file, but you can specify different things*/
+ if (verbose) printf("Loading mimetype default magic database\n");
+
+ session->magic = magic_open(MAGIC_MIME);
+ if (session->magic == NULL) {
+ fprintf(stderr,"ERROR: unable to initialize magic library\n");
+ return AFB_FAIL;
+ }
+ if (magic_load(session->magic, NULL) != 0) {
+ fprintf(stderr,"cannot load magic database - %s\n", magic_error(session->magic));
+ magic_close(session->magic);
+ return AFB_FAIL;
+ }
+
+ //usage
+ //magic_full = magic_file(magic_cookie, actual_file);
+ //printf("%s\n", magic_full);
+ return AFB_SUCCESS;
+}
// Because of POST call multiple time requestApi we need to free POST handle here
static void endRequest (void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe) {
@@ -62,86 +89,93 @@ STATIC void computeEtag(char *etag, int maxlen, struct stat *sbuf) {
snprintf(etag, maxlen, "%d", time);
}
-STATIC int servFile (struct MHD_Connection *connection, AFB_session *session, const char *url, char *filepath, int fd) {
- const char *etagCache;
+STATIC int servFile (struct MHD_Connection *connection, AFB_session *session, const char *url, AFB_staticfile *staticfile) {
+ const char *etagCache, *mimetype;
char etagValue[15];
struct MHD_Response *response;
struct stat sbuf;
int ret;
- if (fstat (fd, &sbuf) != 0) {
- fprintf(stderr, "Fail to stat file: [%s] error:%s\n", filepath, strerror(errno));
+ if (fstat (staticfile->fd, &sbuf) != 0) {
+ fprintf(stderr, "Fail to stat file: [%s] error:%s\n", staticfile->path, strerror(errno));
return (FAILED);
}
// if url is a directory let's add index.html and redirect client
if (S_ISDIR (sbuf.st_mode)) {
- strncpy (filepath, url, sizeof (filepath));
-
- if (url [strlen (url) -1] != '/') strncat (filepath, "/", sizeof (filepath));
- strncat (filepath, "index.html", sizeof (filepath));
- close (fd);
- response = MHD_create_response_from_buffer (0,"", MHD_RESPMEM_PERSISTENT);
- MHD_add_response_header (response,MHD_HTTP_HEADER_LOCATION, filepath);
- ret = MHD_queue_response (connection, MHD_HTTP_MOVED_PERMANENTLY, response);
-
+ if (url [strlen (url) -1] != '/') strncat (staticfile->path, "/", sizeof (staticfile->path));
+ strncat (staticfile->path, "index.html", sizeof (staticfile->path));
+ close (staticfile->fd); // close directory try to open index.html
+ if (-1 == (staticfile->fd = open(staticfile->path, O_RDONLY)) || (fstat (staticfile->fd, &sbuf) != 0)) {
+ fprintf(stderr, "No Index.html in direcory [%s]\n", staticfile->path);
+ return (FAILED);
+ }
+
} else if (! S_ISREG (sbuf.st_mode)) { // only standard file any other one including symbolic links are refused.
- fprintf (stderr, "Fail file: [%s] is not a regular file\n", filepath);
+ fprintf (stderr, "Fail file: [%s] is not a regular file\n", staticfile->path);
const char *errorstr = "<html><body>Alsa-Json-Gateway Invalid file type</body></html>";
response = MHD_create_response_from_buffer (strlen (errorstr),
(void *) errorstr, MHD_RESPMEM_PERSISTENT);
- ret = MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
+ MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
+ goto finishJob;
- } else {
+ }
- // https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=fr
- // ftp://ftp.heanet.ie/disk1/www.gnu.org/software/libmicrohttpd/doxygen/dc/d0c/microhttpd_8h.html
-
- // Check etag value and load file only when modification date changes
- etagCache = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_NONE_MATCH);
- computeEtag(etagValue, sizeof (etagValue), &sbuf);
-
- if (etagCache != NULL && strcmp(etagValue, etagCache) == 0) {
- close(fd); // file did not change since last upload
- if (verbose) fprintf(stderr, "Not Modify: [%s]\n", filepath);
- response = MHD_create_response_from_buffer(0, "", MHD_RESPMEM_PERSISTENT);
- MHD_add_response_header(response, MHD_HTTP_HEADER_CACHE_CONTROL, session->cacheTimeout); // default one hour cache
- MHD_add_response_header(response, MHD_HTTP_HEADER_ETAG, etagValue);
- ret = MHD_queue_response(connection, MHD_HTTP_NOT_MODIFIED, response);
-
- } else { // it's a new file, we need to upload it to client
- if (verbose) fprintf(stderr, "Serving: [%s]\n", filepath);
- response = MHD_create_response_from_fd(sbuf.st_size, fd);
- MHD_add_response_header(response, MHD_HTTP_HEADER_CACHE_CONTROL, session->cacheTimeout); // default one hour cache
- MHD_add_response_header(response, MHD_HTTP_HEADER_ETAG, etagValue);
- ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
- }
+ // https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=fr
+ // ftp://ftp.heanet.ie/disk1/www.gnu.org/software/libmicrohttpd/doxygen/dc/d0c/microhttpd_8h.html
+
+ // Check etag value and load file only when modification date changes
+ etagCache = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_IF_NONE_MATCH);
+ computeEtag(etagValue, sizeof (etagValue), &sbuf);
+
+ if (etagCache != NULL && strcmp(etagValue, etagCache) == 0) {
+ close(staticfile->fd); // file did not change since last upload
+ if (verbose) fprintf(stderr, "Not Modify: [%s]\n", staticfile->path);
+ response = MHD_create_response_from_buffer(0, "", MHD_RESPMEM_PERSISTENT);
+ MHD_add_response_header(response, MHD_HTTP_HEADER_CACHE_CONTROL, session->cacheTimeout); // default one hour cache
+ MHD_add_response_header(response, MHD_HTTP_HEADER_ETAG, etagValue);
+ MHD_queue_response(connection, MHD_HTTP_NOT_MODIFIED, response);
+
+ } else { // it's a new file, we need to upload it to client
+ // if we have magic let's try to guest mime type
+ if (session->magic) {
+ mimetype="Unknown";
+ mimetype= magic_descriptor(session->magic, staticfile->fd);
+ if (mimetype != NULL) MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, mimetype);
+ };
+ if (verbose) fprintf(stderr, "Serving: [%s] mine=%s\n", staticfile->path, mimetype);
+ response = MHD_create_response_from_fd(sbuf.st_size, staticfile->fd);
+ MHD_add_response_header(response, MHD_HTTP_HEADER_CACHE_CONTROL, session->cacheTimeout); // default one hour cache
+ MHD_add_response_header(response, MHD_HTTP_HEADER_ETAG, etagValue);
+ MHD_queue_response(connection, MHD_HTTP_OK, response);
}
+
+finishJob:
MHD_destroy_response(response);
- return (ret);
-
+ return (MHD_YES);
}
// minimal httpd file server for static HTML,JS,CSS,etc...
STATIC int requestFile(struct MHD_Connection *connection, AFB_session *session, const char* url) {
int fd;
int ret;
-
- char filepath [512];
+ AFB_staticfile staticfile;
// build full path from rootdir + url
- strncpy(filepath, session->config->rootdir, sizeof (filepath));
- strncat(filepath, url, 511);
+
+
+ strncpy(staticfile.path, session->config->rootdir, sizeof (staticfile.path));
+ strncat(staticfile.path, url, sizeof (staticfile.path));
// try to open file and get its size
- if (-1 == (fd = open(filepath, O_RDONLY))) {
- fprintf(stderr, "Fail to open file: [%s] error:%s\n", filepath, strerror(errno));
+ if (-1 == (staticfile.fd = open(staticfile.path, O_RDONLY))) {
+ fprintf(stderr, "Fail to open file: [%s] error:%s\n", staticfile.path, strerror(errno));
return (FAILED);
}
// open file is OK let use it
- ret = servFile (connection, session, url, filepath, fd);
+ ret = servFile (connection, session, url, &staticfile);
return ret;
}
@@ -151,34 +185,35 @@ STATIC int checkHTML5(struct MHD_Connection *connection, AFB_session *session, c
int fd;
int ret;
struct MHD_Response *response;
- char filepath [512];
+ AFB_staticfile staticfile;
- // if requesting '/' serve index.html
- if (strlen (url) == 0) {
- strncpy(filepath, session->config->rootdir, sizeof (filepath));
- strncat(filepath, "/index.html", sizeof (filepath));
+ // Any URL prefixed with /rootbase is served with index.html ex: /opa,/opa/,/opa/#!xxxxxx
+ if ( url[0] == '\0' || url[1] == '\0' || url[1] == '#') {
+ strncpy(staticfile.path, session->config->rootdir, sizeof (staticfile.path));
+ strncat(staticfile.path, "/index.html", sizeof (staticfile.path));
// try to open file and get its size
- if (-1 == (fd = open(filepath, O_RDONLY))) {
- fprintf(stderr, "Fail to open file: [%s] error:%s\n", filepath, strerror(errno));
+ if (-1 == (staticfile.fd = open(staticfile.path, O_RDONLY))) {
+ fprintf(stderr, "Fail to open file: [%s] error:%s\n", staticfile.path, strerror(errno));
// Nothing respond to this request Files, API, Angular Base
- const char *errorstr = "<html><body>Alsa-Json-Gateway Unknown or Not readable file</body></html>";
+ const char *errorstr = "<html><body>Application Framework OPA/index.html Not found</body></html>";
response = MHD_create_response_from_buffer(strlen(errorstr),(void *)errorstr, MHD_RESPMEM_PERSISTENT);
- ret = MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
- ret = MHD_YES;
- return (FAILED);
+ MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response);
+ return (MHD_YES);
} else {
- ret = servFile (connection, session, url, filepath, fd);
+ ret = servFile (connection, session, url, &staticfile);
return ret;
}
}
- // we are facing a internal route within the HTML5 OnePageApp let's redirect ex: /myapp/#!user/login
- strncpy(filepath, session->config->rootbase, sizeof (filepath));
- strncat(filepath, "#!", sizeof (filepath));
- strncat(filepath, url, sizeof (filepath));
- response = MHD_create_response_from_buffer(session->config->html5.len,(void *)session->config->html5.msg, MHD_RESPMEM_PERSISTENT);
- MHD_add_response_header (response, "Location", "http://somesite.com/page.html");
- MHD_queue_response (connection, MHD_HTTP_OK, response);
+ // Url match /opa/xxxx but not /opa#!xxxx we redirect the URL to /opa#!/xxxx to force index.html reload
+ strncpy(staticfile.path, session->config->rootbase, sizeof (staticfile.path));
+ strncat(staticfile.path, "#!", sizeof (staticfile.path));
+ strncat(staticfile.path, url, sizeof (staticfile.path));
+ response = MHD_create_response_from_buffer(0,"", MHD_RESPMEM_PERSISTENT);
+ MHD_add_response_header (response, "Location", staticfile.path);
+ MHD_queue_response (connection, MHD_HTTP_MOVED_PERMANENTLY, response);
+ if (verbose) fprintf (stderr,"checkHTML5 redirect to [%s]\n",staticfile.path);
+ return (MHD_YES);
}
// Check and Dispatch HTTP request
@@ -193,9 +228,9 @@ STATIC int newRequest(void *cls,
struct MHD_Response *response;
int ret;
- // this is an Angular request we change URL /!#xxxxx
- if (0 == strncmp(url, session->config->rootapi, baseUrlLen)) {
- ret = doRestApi(connection, session, method, &url[baseUrlLen]);
+ // this is a REST API let's check for plugins
+ if (0 == strncmp(url, session->config->rootapi, apiUrlLen)) {
+ ret = doRestApi(connection, session, method, &url[apiUrlLen+1]);
return ret;
}
@@ -220,17 +255,21 @@ STATIC int newRequest(void *cls,
}
STATIC int newClient(void *cls, const struct sockaddr * addr, socklen_t addrlen) {
- // check if client is comming from an acceptable IP
+ // check if client is coming from an acceptable IP
return (MHD_YES); // MHD_NO
}
-PUBLIC AFB_ERROR httpdStart(AFB_session *session) {
-
- // do this only once
- aipUrlLen = strlen (session->config->rootapi);
- baseUrlLen = strlen (session->config->rootbase);
-
+PUBLIC AFB_error httpdStart(AFB_session *session) {
+
+ // compute fixed URL length at startup time
+ apiUrlLen = strlen (session->config->rootapi);
+ baseUrlLen= strlen (session->config->rootbase);
+
+ // open libmagic cache
+ initLibMagic (session);
+
+
if (verbose) {
printf("AFB:notice Waiting port=%d rootdir=%s\n", session->config->httpdPort, session->config->rootdir);
printf("AFB:notice Browser URL= http://localhost:%d\n", session->config->httpdPort);
@@ -253,7 +292,7 @@ PUBLIC AFB_ERROR httpdStart(AFB_session *session) {
}
// infinite loop
-PUBLIC AFB_ERROR httpdLoop(AFB_session *session) {
+PUBLIC AFB_error httpdLoop(AFB_session *session) {
static int count = 0;
if (verbose) fprintf(stderr, "AFB:notice entering httpd waiting loop\n");
diff --git a/src/main.c b/src/main.c
index 795c2a7b..a86de1c5 100644
--- a/src/main.c
+++ b/src/main.c
@@ -88,7 +88,7 @@ static AFB_options cliOptions [] = {
{SET_TCP_PORT ,1,"port" , "HTTP listening TCP port [default 1234]"},
{SET_ROOT_DIR ,1,"rootdir" , "HTTP Root Directory [default $HOME/.AFB"},
- {SET_ROOT_BASE ,1,"rootbase" , "Angular Base Root URL [default /"},
+ {SET_ROOT_BASE ,1,"rootbase" , "Angular Base Root URL [default /opa"},
{SET_ROOT_API ,1,"rootapi" , "HTML Root API URL [default /api"},
{SET_CACHE_TO ,1,"cache-eol" , "Client cache end of live [default 3600s]"},
@@ -227,7 +227,7 @@ static void closeSession (AFB_session *session) {
| Main listening HTTP loop
+--------------------------------------------------------- */
static void listenLoop (AFB_session *session) {
- AFB_ERROR err;
+ AFB_error err;
if (signal (SIGABRT, signalFail) == SIG_ERR) {
fprintf (stderr, "%s ERR: main fail to install Signal handler\n", configTime());
diff --git a/src/rest-api.c b/src/rest-api.c
index 3b5c53e1..74237adc 100644
--- a/src/rest-api.c
+++ b/src/rest-api.c
@@ -110,13 +110,15 @@ PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, co
for (idx=0; session->plugins[idx] != NULL; idx++) {
if (!strcmp (session->plugins[idx]->prefix, baseurl)) {
jsonResponse = callPluginApi (session->plugins[idx], session, &request );
- free (urlcpy);
+ // free (urlcpy);
break;
}
+ }
+ // No plugin was found
+ if (session->plugins[idx] == NULL) {
errMessage = jsonNewMessage(AFB_FATAL, "No Plugin for %s", baseurl);
free (urlcpy);
goto ExitOnError;
-
}
// plugin callback did not return a valid Json Object
@@ -150,26 +152,43 @@ PUBLIC const char* getQueryValue (AFB_request * request, char *name) {
return (value);
}
-
-void *initPlugins (AFB_session *session) {
- static AFB_plugin *plugins[10]; // no more than 10 plugins !!!
- AFB_plugin *plugin;
+// Loop on plugins. Check that they have the right type, prepare a JSON object with prefix
+STATIC AFB_plugin ** RegisterPlugins(AFB_plugin **plugins) {
int idx;
- // simulate dynamic library load for plugins
- // need to implement mods argument to activate only requested mods
- idx=0;
-
- // Minimal check before accepting a new plugin
- plugin = afsvRegister (session);
- if (plugin->type != AFB_PLUGIN) {
- fprintf (stderr, "ERROR: AFSV plugin invalid type=%d!=%d\n", plugin->type, AFB_PLUGIN);
- } else {
- // Prepare Plugin name to be added to API response
- plugin->jtype = json_object_new_string (plugin->prefix);
- json_object_get (plugin->jtype); // increase reference count to make it permanent
- plugins[idx++]= plugin;
+ for (idx=0; plugins[idx] != NULL; idx++) {
+ if (plugins[idx]->type != AFB_PLUGIN) {
+ fprintf (stderr, "ERROR: AFSV plugin[%d] invalid type=%d != %d\n", idx, AFB_PLUGIN, plugins[idx]->type);
+ } else {
+ // some sanity controls
+ if ((plugins[idx]->prefix == NULL) || (plugins[idx]->info == NULL) || (plugins[idx]->apis == NULL)){
+ if (plugins[idx]->prefix == NULL) plugins[idx]->prefix = "No URL prefix for APIs";
+ if (plugins[idx]->info == NULL) plugins[idx]->info = "No Info describing plugin APIs";
+ fprintf (stderr, "ERROR: plugin[%d] invalid prefix=%s info=%s", idx,plugins[idx]->prefix, plugins[idx]->info);
+ return NULL;
+ }
+
+ if (verbose) fprintf (stderr, "Loading plugin[%d] prefix=[%s] info=%s\n", idx, plugins[idx]->prefix, plugins[idx]->info);
+
+ // Prepare Plugin name to be added into each API response
+ plugins[idx]->jtype = json_object_new_string (plugins[idx]->prefix);
+ json_object_get (plugins[idx]->jtype); // increase reference count to make it permanent
+
+ // compute urlprefix lenght
+ plugins[idx]->prefixlen = strlen (plugins[idx]->prefix);
+ }
}
-
- session->plugins= plugins;
+ return (plugins);
+}
+
+void initPlugins (AFB_session *session) {
+ static AFB_plugin *plugins[10];
+
+ plugins[0]= afsvRegister (session),
+ plugins[1]= dbusRegister (session),
+ plugins[2]= alsaRegister (session),
+ plugins[3]= NULL;
+
+ // complete plugins and save them within current sessions
+ session->plugins= RegisterPlugins (plugins);
} \ No newline at end of file
diff --git a/src/session.c b/src/session.c
index 2bb5b442..aade418e 100644
--- a/src/session.c
+++ b/src/session.c
@@ -35,7 +35,7 @@
// verify we can read/write in session dir
-PUBLIC AFB_ERROR sessionCheckdir (AFB_session *session) {
+PUBLIC AFB_error sessionCheckdir (AFB_session *session) {
int err;