X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;ds=sidebyside;f=src%2Fhttp-svc.c;h=1eeb287870c046520bf6dd572034799bcc80d796;hb=9a4dbba322074fcdf9ab10fcda80805f6348344b;hp=b29a4bb859394779a8302d7ea44d52eaea3049d3;hpb=cd054544444e92e7695dd288f0c04b7af0f668e3;p=src%2Fapp-framework-binder.git diff --git a/src/http-svc.c b/src/http-svc.c index b29a4bb8..1eeb2878 100644 --- a/src/http-svc.c +++ b/src/http-svc.c @@ -31,27 +31,49 @@ #include + #include #include "../include/local-def.h" +// let's compute fixed URL length only once +static apiUrlLen=0; +static baseUrlLen=0; +static rootUrlLen=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 mime types +static AFB_error initLibMagic (AFB_session *session) { + + /*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_TYPE); + if (session->magic == NULL) { + fprintf(stderr,"ERROR: unable to initialize magic library\n"); + return AFB_FAIL; + } + + // Warning: should not use NULL for DB [libmagic bug wont pass efence check] + if (magic_load(session->magic, MAGIC_DB) != 0) { + fprintf(stderr,"cannot load magic database - %s\n", magic_error(session->magic)); + magic_close(session->magic); + return AFB_FAIL; + } + + 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) { - AFB_HttpPost *posthandle = *con_cls; + AFB_PostHandle *posthandle = *con_cls; // if post handle was used let's free everything - if (posthandle) { - if (verbose) fprintf (stderr, "End Post Request UID=%d\n", posthandle->uid); - free (posthandle->data); - free (posthandle); - } + if (posthandle != NULL) endPostRequest (posthandle); } @@ -62,125 +84,139 @@ 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)); - return (FAILED); + if (fstat (staticfile->fd, &sbuf) != 0) { + fprintf(stderr, "Fail to stat file: [%s] error:%s\n", staticfile->path, strerror(errno)); + goto abortRequest; } - + // 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); - - } 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); - const char *errorstr = "Alsa-Json-Gateway Invalid file type"; + close (staticfile->fd); // close directory check for Index + + // No trailing '/'. Let's add one and redirect for relative paths to work + if (url [strlen (url) -1] != '/') { + response = MHD_create_response_from_buffer(0,"", MHD_RESPMEM_PERSISTENT); + strncpy(staticfile->path, url, sizeof (staticfile->path)); + strncat(staticfile->path, "/", sizeof (staticfile->path)); + MHD_add_response_header (response, "Location", staticfile->path); + MHD_queue_response (connection, MHD_HTTP_MOVED_PERMANENTLY, response); + if (verbose) fprintf (stderr,"Adding trailing '/' [%s]\n",staticfile->path); + goto sendRequest; + } + + strncat (staticfile->path, OPA_INDEX, sizeof (staticfile->path)); + 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); + goto abortRequest; + } + } else if (! S_ISREG (sbuf.st_mode)) { // only standard file any other one including symbolic links are refused. + close (staticfile->fd); // nothing useful to do with this file + fprintf (stderr, "Fail file: [%s] is not a regular file\n", staticfile->path); + const char *errorstr = "Application Framework Binder Invalid file type"; response = MHD_create_response_from_buffer (strlen (errorstr), (void *) errorstr, MHD_RESPMEM_PERSISTENT); - ret = MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response); - - } else { + MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response); + goto sendRequest; + } - // 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= magic_descriptor(session->magic, staticfile->fd); + if (mimetype != NULL) MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, mimetype); + } else mimetype="application/unknown"; + + if (verbose) fprintf(stderr, "Serving: [%s] mime=%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); } + +sendRequest: MHD_destroy_response(response); - return (ret); + return (MHD_YES); +abortRequest: + return (FAILED); } -// minimal httpd file server for static HTML,JS,CSS,etc... -STATIC int requestFile(struct MHD_Connection *connection, AFB_session *session, const char* url) { + +// this function return either Index.htlm or a redirect to /#!route to make angular happy +STATIC int redirectHTML5(struct MHD_Connection *connection, AFB_session *session, const char* url) { + int fd; int ret; + struct MHD_Response *response; + AFB_staticfile staticfile; + + // Url match /opa/xxxx should redirect to "/opa/#!page" to force index.html reload + strncpy(staticfile.path, session->config->rootbase, sizeof (staticfile.path)); + strncat(staticfile.path, "/#!", sizeof (staticfile.path)); + strncat(staticfile.path, &url[1], 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); +} - char filepath [512]; +// 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, ret, idx; + AFB_staticfile staticfile; + char *requestdir, *requesturl; + + // default search for file is rootdir base + requestdir= session->config->rootdir; + requesturl=(char*)url; + + // Check for optional aliases + for (idx=0; session->config->aliasdir[idx].url != NULL; idx++) { + if (0 == strncmp(url, session->config->aliasdir[idx].url, session->config->aliasdir[idx].len)) { + requestdir = session->config->aliasdir[idx].path; + requesturl=(char*)&url[session->config->aliasdir[idx].len]; + break; + } + } + // build full path from rootdir + url - strncpy(filepath, session->config->rootdir, sizeof (filepath)); - strncat(filepath, url, 511); + strncpy(staticfile.path, requestdir, sizeof (staticfile.path)); + strncat(staticfile.path, requesturl, 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; } -// this function return either Index.htlm or a redirect to /#!route to make angular happy -STATIC int checkHTML5(struct MHD_Connection *connection, AFB_session *session, const char* url) { - - int fd; - int ret; - struct MHD_Response *response; - char filepath [512]; - - // if requesting '/' serve index.html - if (strlen (url) == 0) { - strncpy(filepath, session->config->rootdir, sizeof (filepath)); - strncat(filepath, "/index.html", sizeof (filepath)); - // 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)); - // Nothing respond to this request Files, API, Angular Base - const char *errorstr = "Alsa-Json-Gateway Unknown or Not readable file"; - 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); - } else { - ret = servFile (connection, session, url, filepath, fd); - 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); -} - // Check and Dispatch HTTP request STATIC int newRequest(void *cls, struct MHD_Connection *connection, @@ -193,9 +229,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, &url[apiUrlLen+1], method, upload_data, upload_data_size, con_cls); return ret; } @@ -206,9 +242,9 @@ STATIC int newRequest(void *cls, ret = requestFile(connection, session, url); if (ret != FAILED) return ret; - // no static was served let check for Angular redirect + // no static was served let's try HTML5 OPA redirect if (0 == strncmp(url, session->config->rootbase, baseUrlLen)) { - ret = checkHTML5(connection, session, &url[baseUrlLen]); + ret = redirectHTML5(connection, session, &url[baseUrlLen]); return ret; } @@ -220,17 +256,25 @@ 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); + rootUrlLen= strlen (session->config->rootdir); + + // Initialise Client Session Hash Table + ctxStoreInit (CTX_NBCLIENTS); + + //TBD open libmagic cache [fail to pass EFENCE check (allocating 0 bytes)] + //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,14 +297,14 @@ 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"); if (session->foreground) { while (TRUE) { - fprintf(stderr, "AFB:notice Use Ctrl-C to quit"); + fprintf(stderr, "AFB:notice Use Ctrl-C to quit\n"); (void) getc(stdin); } } else {