//***************************************************************************** /* Copyright © 2010 Jim Schimpf. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //***************************************************************************** /* Module Name: HTTP interface for Firenet Description: This uses libmicrohttpd to build a RESTful interface to Firenet. Library Extensions rest_start() - Start HTTP interface rest_stop() - Stop HTTP interface rest_close() - Close current connection & return data rest_url() - Extract URL from current connection rest_data() - Get data from current connection rest_client_lock() - Lock server to current client Revision History: 24 Oct 2010 Initial version 20 Nov 2010 UC both http & rest base 16 Dec 2010 Add client lock to lock the webserver to one client 7-Mar-2011 Dropping POST data reader, as post data does not work... 23-Dec-2011 [7b5a95ed75] Update to get rest base size in structure 29-Jan-2012 [28bcb55998] Change Minnow so that it can work as a regular web server */ //**************************************************************************** #include #include #include "lauxlib.h" #include "lua.h" #include "lualib.h" #include "lstate.h" #include "lapi.h" #include "qdj.h" #include "rest.h" #include "restclient.h" #if 0 #pragma mark - #pragma mark DATA #endif #define RESTFUL_HANDLE_VALID 'rest' typedef struct rest_dummy { unsigned long valid; RESTQEL *rdata; } RESTFUL_HANDLE; static int rest_start( lua_State *L ); static int rest_stop( lua_State *L ); static int rest_open(lua_State *L); static int rest_close(lua_State *L); static int rest_url(lua_State *L); static int rest_http_data(lua_State *L); static int rest_client_lock(lua_State *L); // Support static int get_http_base( RSERV_DATA *rsrv ); static RESTFUL_HANDLE *checkRESTFUL_HANDLE( lua_State *L ); static void uppercase_it(char *ptr ); #if 0 #pragma mark - #pragma mark Lua API #endif static struct luaL_reg httplib_f[] = { {"start",rest_start}, {"stop",rest_stop}, {"open",rest_open}, {NULL,NULL} }; static struct luaL_reg httplib_m[] = { {"close",rest_close}, {"url",rest_url}, // {"data",rest_http_data}, // TBD {"lock",rest_client_lock}, {NULL,NULL} }; #if 0 #pragma mark -- #pragma mark -- LUA SUPPORT -- #endif /******************************************************************************* * * luaopen_parselib - Attach HTTP class to LUA * * INPUT: L - Lua state * * OUTPUT: 1 - Stacked HTTP library table * *********************************************************************************/ LUALIB_API int luaopen_httplib (lua_State *L) { // (1) Create the class Metatable luaL_newmetatable(L,"PP.http"); // (2) Put the __index element with the metatable in the // metatable lua_pushstring(L,"__index"); lua_pushvalue(L,-2); // Put the meta table (1) on stack lua_settable(L,-3); // Add it to the metatable // (3) Add the method table to the stack luaL_openlib(L,NULL,httplib_m,0); // (3) Add the class table to the system & return that it's on the // stack luaL_openlib(L, "http", httplib_f, 0); return 1; } /******************************************************************************* * * RESTFUL_HANDLE *checkRESTFUL_HANDLE( lua_State *L ) - Check TOS for RESTFUL handle * * INPUT: NONE * Examine TOS * * OUTPUT: Pointer to RESTFUL_HANDLE object if it was TOS * NULL if not * *********************************************************************************/ static RESTFUL_HANDLE *checkRESTFUL_HANDLE( lua_State *L ) { RESTFUL_HANDLE *h; // (1) Check the TOS to see if we have TG.limo metatable h = (RESTFUL_HANDLE *)luaL_checkudata(L,1,"PP.http"); luaL_argcheck( L,(h != NULL),1,"Restful object expected"); return( h ); } #if 0 #pragma mark - #pragma mark CREATE/DELETE API #endif //*************************************************************************** /* * rest_start( http_port,http_base,http_site,rest_base) * * INPUT: http_port - # Value, port used for HTTP transactions * http_base - ABSOLUTE loccation of site files (HTML file root) * rest_base - RESTful tree root * * OUTPUT: 1 if started nil if not * */ //***************************************************************************/ static int rest_start( lua_State *L ) { size_t llen; double dval; char *ptr; RSERV_DATA *rsrv = rest_data(); int rtnval = 0; // (1) Pull in the values 1 at a time // PORT, BASE then Restful base if( lua_isnumber(L,1)) { dval = lua_tonumber(L, 1); rsrv->http_port = (int)dval; // (2) Get the path to the files if( lua_isstring(L,2 ) ) { ptr = (char *)luaL_checklstring(L,2,&llen); rsrv->http_site = (char *)MEMALLOC(llen+1); if( rsrv->http_site != NULL ) { // 3 Copy and set up http_base value // if OK continue memcpy(rsrv->http_site,ptr,llen); if( get_http_base(rsrv) ) { uppercase_it(rsrv->http_base); // (3) Get the path to the RESTful location // if present if( lua_isstring(L,3 ) ) { ptr = (char *)luaL_checklstring(L,3,&llen); rsrv->rest_base = (char *)MEMALLOC(llen+1); rsrv->len_rest_base = llen; if( rsrv->rest_base != NULL ) { memcpy(rsrv->rest_base,ptr,llen); uppercase_it(rsrv->rest_base); } else rsrv->len_rest_base = 0; // No rest base } else { // (3a) There is no REST input so handle that rsrv->len_rest_base = 0; rsrv->rest_base = NULL; } // (5) Start server if( rest_server() == 0 ) { // (6) Start client if( rest_support_open() == 0 ) { dval = 1.0; lua_pushnumber( L,dval ); // Return true rtnval = 1; } } } } } } // (7) If rtnval == 0 call close to shut things down if( rtnval == 0 ) rest_stop(L); return( rtnval ); } //*************************************************************************** /* * rest_stop( ) * * INPUT: NONE * * OUTPUT: Stop HTTP server */ //***************************************************************************/ static int rest_stop( lua_State *L ) { RSERV_DATA *rsrv = rest_data(); // (1) Close com channel and stop server rest_support_close(); rsrv->run_flag = 0; rest_server_close(); usleep( 5000000 ); // Wait 5 seconds // (2) Delete data if( rsrv->http_base != NULL ) { MEMFREE( rsrv->http_base ); rsrv->http_base = NULL; } if( rsrv->http_site != NULL ) { MEMFREE( rsrv->http_site ); rsrv->http_site = NULL; } if( rsrv->rest_base != NULL ) { MEMFREE( rsrv->rest_base ); rsrv->rest_base = NULL; } return( 0 ); } #if 0 #pragma mark - #pragma mark I/O API #endif //*************************************************************************** /* * http.open() - NON-Blocking receive * * INPUT: NONE * * OUTPUT: NIL if no data, REST structure if receive * */ //***************************************************************************/ static int rest_open(lua_State *L) { int rtnval = 0; RESTQEL *packet; RESTFUL_HANDLE *h; // (1) Did we get anything ? packet = rest_support_receive(); if( packet != NULL ) { // (2) Build a lua user structure and return this to // the user h = (RESTFUL_HANDLE *)lua_newuserdata(L,sizeof(RESTFUL_HANDLE)); if( h != NULL ) { h->valid = RESTFUL_HANDLE_VALID; h->rdata = packet; // (2a) Return handle to user, attach metatable to mark this // as part of http class luaL_getmetatable(L,"PP.http"); lua_setmetatable(L, -2); rtnval = 1; } } return rtnval; } //*************************************************************************** /* * http.close(h,data) - Close http transaction and return data * * INPUT: data - Returned data for http I/O * * OUTPUT: NONE * */ //***************************************************************************/ static int rest_close(lua_State *L) { RESTFUL_HANDLE *h = checkRESTFUL_HANDLE(L); size_t llen = 0; char *ptr = NULL; char *send_data; if( h != NULL ) { // (2) Check for data if( lua_isstring(L,2) ) { ptr = (char *)luaL_checklstring(L, 2, &llen); } else { llen = 0; ptr = ""; } // (3) We must locally allocate the data // to make MHD happy as it will deallocate // this copy of the data send_data = (char *)MEMALLOC( llen ); if( send_data != NULL ) { memcpy(send_data,ptr,llen); // (4) Now send the data off to the server rest_support_send((int)llen,send_data,h->rdata); } } return(0); } #if 0 #pragma mark - #pragma mark HTTP DATA API #endif //*************************************************************************** /* * h:url() - Return requested URL from restful handle * * INPUT: h - Restful handle * * OUTPUT: Lua numeric list with URL elements starting at REST start = [1] * Followed by a number for the HTTP type of request * See HTTP_TYPE in rest.h * * Uppercase all URL elements * */ //***************************************************************************/ static int rest_url(lua_State *L) { int rtnval = 0; RESTFUL_HANDLE *h = checkRESTFUL_HANDLE(L); int i,n; int count = 1; double dval; char *ptr; char *url; int list; // (1) If we have a handle then proceed if( h != NULL ) { // (2) Create a table on the stack and set for return lua_newtable(L); // (3) Start the work on the URL, n = strlen(h->rdata->url); url = (char *) MEMALLOC(n+1); if( url != NULL ) { memcpy(url,h->rdata->url,n); ptr = url; for( i=0; i 0 ) { list = lua_gettop(L); dval = (double)count++; // Index lua_pushnumber(L, dval); uppercase_it( ptr ); // Upper case name lua_pushstring(L, (const char *)ptr); // String value lua_rawset(L,list); // Add to table } ptr = (char *)&url[i+1]; } } // Add in last path element if present if( ptr != NULL && strlen(ptr) != 0 ) { list = lua_gettop(L); dval = (double)count++; // Index lua_pushnumber(L, dval); uppercase_it( ptr ); // Upper case name lua_pushstring(L, (const char *)ptr); // String value lua_rawset(L,list); } MEMFREE(url); } // Add n=count-1 to table list = lua_gettop(L); lua_pushstring(L,"n"); dval = (double)(count - 1); lua_pushnumber(L, dval); lua_rawset(L,list); // Add to table // Put on HTTP type return as part of the result dval = (double)h->rdata->type; lua_pushnumber(L, dval); rtnval = 2; } return rtnval; } // ********** POST DATA READS DON'T WORK ********** #if 0 //*************************************************************************** /* * h:data() - Return data from restful handle * * INPUT: h - Restful handle * * OUTPUT: Lua data as a string * */ //***************************************************************************/ static int rest_http_data(lua_State *L) { int rtnval = 0; RESTFUL_HANDLE *h = checkRESTFUL_HANDLE(L); // (1) Do we have a handle ? if( h != NULL ) { // (2) Ok then push the data as a string lua_pushlstring(L, (const char *)h->rdata->data,h->rdata->size); rtnval = 1; } return rtnval; } #endif #if 0 #pragma mark - #pragma mark HTTP LOGIN API #endif //*************************************************************************** /* * h:rest_client_lock(flag) - Lock connection to current client (by IP) * * INPUT: h - Restful handle * flag - If <> nil then lock client, if nil unlock * * OUTPUT: NONE * */ //***************************************************************************/ static int rest_client_lock(lua_State *L) { int rtnval = 0; RESTFUL_HANDLE *h = checkRESTFUL_HANDLE(L); RSERV_DATA *rsrv = rest_data(); // (1) Do we have a handle ? if( h != NULL ) { // (2) Is this lock or unlock if( lua_isnil(L,2) ) { // DELETE the lock rsrv->lock_addr_flag = 0; // Clear flag } else { // Lock requested, copy the current request IP // to checker location and set the flag strcpy(rsrv->client_ip_addr,rsrv->request_ip_addr); rsrv->lock_addr_flag = 1; // Set flag } } return rtnval; } #if 0 #pragma mark - #pragma mark Internal API #endif //*************************************************************************** /* * int get_http_site( RSERV_DATA *rsrv ) - Get HTTP base name from http_base * * INPUT: rsrv - Struct with filled out http_base * * OUTPUT: 1 if site name set up 0 if not * */ //***************************************************************************/ static int get_http_base( RSERV_DATA *rsrv ) { int rtnval = 0; int n,i; char *ptr; // (1) Scan backward from the end of the http_site and find first "/" n = strlen(rsrv->http_site); for( i=n-1; i>=0; i-- ) { if( rsrv->http_site[i] == '/' ) break; } // (2) At this point i -> '/' so move fwd 1 and set string pointer there ptr = (char *)&(rsrv->http_site[i+1]); // (3) Move this string into structure n = strlen(ptr); rsrv->http_base = (char *)MEMALLOC(n+1); rsrv->len_http_base = n; if( rsrv->http_base != NULL ) { memcpy(rsrv->http_base,ptr,n); rtnval = 1; } return rtnval; } //*************************************************************************** /* * void uppercase_it(char *ptr ) - Uppercase the URL parts * * INPUT: ptr - Part of URL * * OUTPUT: NAME url part converted to uppercase * */ //***************************************************************************/ static void uppercase_it(char *ptr ) { int n = strlen(ptr); int i; for( i=0; i