//************************************************************************* /* Copyright (c) 2010. Pandora Products All Rights Reserved. */ //************************************************************************* /* * Module Name: REST - Application Web server * * Description: This server is used to serve web pages and * do RESTful web operations * * Author: Jim Schimpf * * Revision History: 26 Sep 2010 Initial version * 10 Oct 2010 Add restcom comunications * * SYNTAX: httptest * -http_port Server port for WWW * -http_base Base file location for Web page service * -rest_port Connection port to RESTful app * -rest_base RESTful base address * -v Show version and information * * This program uses libmicrohttpd from Christian Grothoff as the HTTP * protocol manager */ //************************************************************************ #include #include #include #include "platform.h" #include "microhttpd.h" #include "internal.h" #include "rest.h" #include "restcom.h" #if 0 #pragma mark - #pragma mark -- Data -- #endif typedef enum { NO_SITE, HTTP_SITE, REST_SITE, } URL_TYPE; typedef struct { const char *method_name; HTTP_TYPE type; } HTTP_METHOD_LIST; static HTTP_METHOD_LIST methods[] = { MHD_HTTP_METHOD_CONNECT, HTTP_CONNECT, MHD_HTTP_METHOD_DELETE, HTTP_DELETE, MHD_HTTP_METHOD_GET, HTTP_GET, MHD_HTTP_METHOD_HEAD, HTTP_HEAD, MHD_HTTP_METHOD_OPTIONS, HTTP_OPTIONS, MHD_HTTP_METHOD_POST, HTTP_POST, MHD_HTTP_METHOD_PUT, HTTP_PUT, MHD_HTTP_METHOD_TRACE, HTTP_TRACE, }; static int distributor (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr); static HTTP_TYPE get_method( const char *method ); static URL_TYPE url_type( char *url,RSERV_DATA *rdata ); static int http (void *cls, struct MHD_Connection *connection, const char *url, HTTP_TYPE method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr); static char *get_file( char *path, size_t *size ); static int rest (void *cls, struct MHD_Connection *connection, const char *url, HTTP_TYPE method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr); #define MSG_404 "404 PAGE

404 Page NOT FOUND

" #define DEF_HTTP_PORT 8080 static RSERV_DATA local_data; #if 0 #pragma mark - #pragma mark -- Server External API -- #endif /*********************************************************************** * * int rest_server( NONE ) - Run server * * INPUT: NONE * * OUTPUT: Return 0 if run ok (when server shutdown) * < 0 if server start/run problem * **********************************************************************/ int rest_server( void ) { struct MHD_Daemon *d = NULL; int rtnval = -1; // Set for failure RSERV_DATA *rdata = rest_data(); // (1) Set up RESTCOM channel if( restcom_server_open() == 0 ) { // (2) Start HTTP server rdata->run_flag = 1; // Set for RUN d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG, rdata->http_port, NULL, NULL, &distributor, rdata , MHD_OPTION_END); // (3) If it started then keep running till user // calls quits if( d != NULL ) { rtnval = 0; // Successful run rdata->d = (void *)d; } } return rtnval; } /*********************************************************************** * * int rest_server_close( NONE ) - Close server * * INPUT: NONE * * OUTPUT: NONE * **********************************************************************/ void rest_server_close( void ) { RSERV_DATA *rdata = rest_data(); struct MHD_Daemon *d; // (1) Shutdown HTTP d = (struct MHD_Daemon *)rdata->d; MHD_stop_daemon (d); // (2) Shutdown Server comm restcom_server_close(); } /*********************************************************************** * * RSERV_DATA *rest_data(void) - Return pointer to server data struct * * INPUT: NONE * * OUTPUT: Pointer to local server data struct * **********************************************************************/ RSERV_DATA *rest_data(void) { return( &local_data ); } #if 0 #pragma mark - #pragma mark -- MHD Support --- #endif /*********************************************************************** * * static int distributor () - Do HTTP Request * * INPUT: cls - Registration option * connection - Context * url - URL requested * method - Type of action needed * version - HTTP version string * upload_data - Passed in data on POST * upload_data_size - Size in bytes of post * * OUTPUT: Return MHD_YES if successful, MHD_NO if not * **********************************************************************/ static int distributor (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) { RSERV_DATA *data = (RSERV_DATA *)cls; char *site; int ret = MHD_NO; HTTP_TYPE call_method; struct MHD_Response *response = NULL; URL_TYPE type; // (1) Classify the requested method call_method = get_method(method); // (1) Where is this going ? // See if it's to the HTTP or RESTful site // Compare the URL (less the first character) with the // HTTP base site = (char *)&(url[1]); type = url_type(site,data); switch(type) { case HTTP_SITE: // Serve pages ret = http( cls, connection, url, call_method, version, upload_data, upload_data_size, ptr); break; case REST_SITE: // Do RESTful stuff ret = rest( cls, connection, url, call_method, version, upload_data, upload_data_size, ptr); break; case NO_SITE: // 404 response = MHD_create_response_from_data (strlen (MSG_404), (void *) MSG_404, MHD_NO, MHD_NO); if( response != NULL ) { ret = MHD_queue_response (connection, MHD_HTTP_OK, response); MHD_destroy_response (response); } break; } return ret; } /*********************************************************************** * * static HTTP_TYPE get_method( char *method ) * * INPUT: method - HTTP method string * * OUTPUT: Return method type or HTTP_NONE if not found * **********************************************************************/ static HTTP_TYPE get_method( const char *method ) { int i; int n = sizeof(methods)/sizeof(HTTP_METHOD_LIST); HTTP_TYPE rtn = HTTP_NONE; // Find the matching method for( i=0; ihttp_base,strlen(rdata->http_base)) == 0 ) { type = HTTP_SITE; } else { if( strncmp(base,rdata->rest_base,strlen(rdata->rest_base)) == 0 ) { type = REST_SITE; } } return type; } #if 0 #pragma mark - #pragma mark -- HTTP support --- #endif /*********************************************************************** * * static int http () - Do HTTP Request * * INPUT: cls - Registration option * connection - Context * url - URL requested * method - Type of action needed * version - HTTP version string * upload_data - Passed in data on POST * upload_data_size - Size in bytes of post * * OUTPUT: Return MHD_YES if successful, MHD_NO if not * **********************************************************************/ static int http (void *cls, struct MHD_Connection *connection, const char *url, HTTP_TYPE method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) { RSERV_DATA *data = (RSERV_DATA *)cls; struct MHD_Response *response = NULL; int ret = MHD_NO; char fname[256]; char *path; char *fdata; size_t fsize; // (1) Switch on requested method switch( method ) { case HTTP_GET: // GET METHOD // Build the file location, first strip off the // http_base and the leading / from the URL path = (char *)&url[(int)(strlen(data->http_base)+1)]; strcpy(fname,data->http_site); strcat(fname,path); // Pointer to actual file inside http_base fdata = get_file(fname,&fsize); if( fdata != NULL) { // If we have file data then build a response from it response = MHD_create_response_from_data (fsize,(void *)fdata, MHD_YES, MHD_NO); } else { // Otherwise return a 404 response = MHD_create_response_from_data (strlen (MSG_404), (void *) MSG_404, MHD_NO, MHD_NO); } break; default: // Unknown method break; } // Return response and status if( response != NULL ) { ret = MHD_queue_response (connection, MHD_HTTP_OK, response); MHD_destroy_response (response); } return ret; } /*********************************************************************** * * char *get_file( char *path, size_t *size ) - Read in a file * * INPUT: path - Path to file * size - Size in bytes if read * * OUTPUT: Pointer to allocated buffer with file data * **********************************************************************/ static char *get_file( char *path, size_t *size ) { char *data = NULL; int fd; struct stat stats; int sz; // (1) Open and get the file size fd = open(path,O_RDONLY); if( fd >= 0 ) { // (2) Get the size if( fstat(fd,&stats) == 0 ) { // (3) Build the buffer data = MEMALLOC( stats.st_size ); if( data != NULL ) { // (4) Read the file & close it sz = read(fd, (void *)data, stats.st_size); if( sz <= 0 ) { printf("Read [%s] Bad FILE\n",path); MEMFREE(data); data = NULL; } else { *size = (size_t)sz; printf("Read: [%s] %d bytes\n",path,(int)(*size)); } } } close(fd); } return data; } #if 0 #pragma mark - #pragma mark -- RESTful support --- #endif /*********************************************************************** * * static int rest () - Do RESTful Request * * INPUT: cls - Registration option * connection - Context * url - URL requested * method - Type of action needed * version - HTTP version string * upload_data - Passed in data on POST * upload_data_size - Size in bytes of post * * OUTPUT: Return MHD_YES if successful, MHD_NO if not * **********************************************************************/ static int rest (void *cls, struct MHD_Connection *connection, const char *url, HTTP_TYPE method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) { struct MHD_Response *response = NULL; int ret = MHD_NO; RESTQEL *el; int count = 0; int fail = 0; int size = 0; char *data = NULL; // (1) Send data to client // NOTE: On post send read buffer with POST data if( method == HTTP_POST ) { size = strlen(connection->read_buffer); data = connection->read_buffer; } if( restcom_master_send( (void *)connection, method, (char *)url, size, (char *)data) == 0 ) { // (2) Wait for the result while( (el = restcom_master_receive((void *)connection)) == NULL ) { usleep( 100000 ); // Sleep for 100 ms count = count + 1; if( count > 200 ) // Wait 20 seconds { fail = 1; break; } } // (3) If OK, build the response if( fail == 0 ) { response = MHD_create_response_from_data ((size_t)el->size,(void *)el->data, MHD_YES, MHD_NO); } // Return response and status if( response != NULL ) { ret = MHD_queue_response (connection, MHD_HTTP_OK, response); MHD_destroy_response (response); } // (4) Remove received element restcom_delete_packet( el ); } return ret; }