/*@@ @file Server.c @date Wed Sep 13 20:10:24 2000 @author Tom Goodale @desc Web serving and utility routines. @enddesc @version $Header$ @@*/ #include #include #include "cctk.h" #include "cctk_Constants.h" #include "cctk_Groups.h" #include "cctk_GroupsOnGH.h" #include "cctk_Parameter.h" #include "util_Hash.h" #include "util_String.h" #include "httpd.h" #include "http_Steer.h" #include "http_Expression.h" static char *rcsid = "$Header$"; CCTK_FILEVERSION(DevThorns_httpd_Server_c) /******************************************************************** ********************* Local Data Types *********************** ********************************************************************/ typedef struct { int (*function)(cGH *, httpRequest *, void *); void *data; } httpPage; /******************************************************************** ********************* Local Routine Prototypes ********************* ********************************************************************/ static httpPage *CreatePageData(int (*function)(cGH *,httpRequest *, void *), void *data); static httpPage *FindPage(char *path, char **residual); static int StatusUntilIt(cGH *cctkGH); static int StatusUntilTime(cGH *cctkGH); static int StatusUntilExpression(cGH *cctkGH); static double evaluator(const char *name, void *data); /******************************************************************** ********************* Other Routine Prototypes ********************* ********************************************************************/ /******************************************************************** ********************* Local Data ***************************** ********************************************************************/ static uHash *pages = NULL; static const char *notfound_page = "\nError 404: Not Found\ The URI you requested could not be found\n\n"; static const char *notimplemented_page = "\nError 501: Not Implemented\ The requested method is not implemented\n\n"; #define INITIAL_SIZE 16 /******************************************************************** ********************* External Routines ********************** ********************************************************************/ /*@@ @routine HTTP_RequestGet @date Wed Sep 13 20:10:24 2000 @author Tom Goodale @desc Routine to deal with a GET request. @enddesc @calls @calledby @history @endhistory @@*/ int HTTP_RequestGET(cGH *cctkGH, httpRequest *request) { httpPage *pagedata; int retval; char message[1024]; if((pagedata = FindPage(request->uri, &(request->residual)))) { retval = pagedata->function(cctkGH, request, pagedata->data); } else { retval = -1; } if(retval < 0) { strcpy(message,"HTTP/1.0 404 Not Found\r\n"); HTTP_Write(request, message, strlen(message)); strcpy(message,"Content-Type: text/html\r\n\r\n"); HTTP_Write(request, message, strlen(message)); HTTP_Write(request, notfound_page, strlen(notfound_page)); } return retval; } /*@@ @routine HTTP_RequestUnsupported @date Thu Sep 14 12:18:56 2000 @author Tom Goodale @desc @enddesc @calls @calledby @history @endhistory @@*/ int HTTP_RequestUnsupported(cGH *cctkGH, httpRequest *request) { char message[1024]; strcpy(message,"HTTP/1.0 501 Not Implemented\r\n"); HTTP_Write(request, message, strlen(message)); strcpy(message,"Content-Type: text/html\r\n\r\n"); HTTP_Write(request, message, strlen(message)); HTTP_Write(request, notimplemented_page, strlen(notimplemented_page)); return 0; } /*@@ @routine HTTP_RegisterPage @date Wed Sep 13 20:10:24 2000 @author Tom Goodale @desc Routine to register a web page. @enddesc @calls @calledby @history @endhistory @@*/ int HTTP_RegisterPage(const char *path, int (*function)(cGH *, httpRequest *, void *), void *data) { int retval; httpPage *pagedata; /* Create the hash table if it's not already been created */ if(! pages) { pages = Util_HashCreate(INITIAL_SIZE); } if(Util_HashData(pages, strlen(path), path, 0)) { fprintf(stderr, "Page %s already exists\n", path); retval = -1; } else { pagedata = CreatePageData(function, data); if(pagedata) { retval = Util_HashStore(pages, strlen(path), path, 0, (void *)pagedata); } else { retval = -2; } } return 0; } /*@@ @routine HTTP_UpdateState @date Sat Sep 16 21:12:23 2000 @author Tom Goodale @desc Updates the state of the server from the latest parameter values. @enddesc @calls @calledby @history @endhistory @@*/ int HTTP_UpdateState(cGH *cctkGH, httpState *state) { int type; CCTK_INT *value; int pause_selected; int until_it_selected; int until_time_selected; int until_expression_selected; int stepping; static int steering = 1; static int last_steered = -1; value = (CCTK_INT *)CCTK_ParameterGet("single_step",CCTK_THORNSTRING, &type); stepping = *value; value = (CCTK_INT *)CCTK_ParameterGet("pause",CCTK_THORNSTRING, &type); pause_selected = *value; value = (CCTK_INT *)CCTK_ParameterGet("until_it_active",CCTK_THORNSTRING, &type); until_it_selected = *value; value = (CCTK_INT *)CCTK_ParameterGet("until_time_active",CCTK_THORNSTRING, &type); until_time_selected = *value; value = (CCTK_INT *)CCTK_ParameterGet("until_expression_active",CCTK_THORNSTRING, &type); until_expression_selected = *value; state->paused = (! stepping) && (pause_selected || (cctkGH && ((until_it_selected && StatusUntilIt(cctkGH)) || (until_time_selected && StatusUntilTime(cctkGH)) || (until_expression_selected && StatusUntilExpression(cctkGH))))); /* If single step is selected, need to reset the parameter to force a pause * next time. */ if(stepping) { HTTP_SteerQueue(CCTK_THORNSTRING, "single_step", "no"); } if(cctkGH && ! state->paused) { if(steering) { value = (CCTK_INT *)CCTK_ParameterGet("steering_frequency",CCTK_THORNSTRING, &type); if(*value > 0 && *value+last_steered <= cctkGH->cctk_iteration) { /* Time to steer */ state->steer = 1; last_steered = cctkGH->cctk_iteration; } else { state->steer = 0; } if(*value <= 0) { /* Once steering is switched off, can't restart it ! */ steering = 0; } } } else { state->steer = 1; } value = (CCTK_INT *)CCTK_ParameterGet("terminate",CCTK_THORNSTRING, &type); state->terminate = *value; if(!state->paused) { value = (CCTK_INT *)CCTK_ParameterGet("timeout_seconds",CCTK_THORNSTRING, &type); state->timeout_seconds = *value; value = (CCTK_INT *)CCTK_ParameterGet("timeout_useconds",CCTK_THORNSTRING, &type); state->timeout_useconds = *value; } else { state->timeout_seconds = -1; state->timeout_useconds = -1; } return 0; } /*@@ @routine HTTP_Terminate @date Sat Sep 16 21:40:27 2000 @author Tom Goodale @desc Terminate the simulation. @enddesc @calls @calledby @history @endhistory @@*/ int HTTP_Terminate(cGH *cctkGH) { CCTK_Exit(cctkGH, 0); return 0; } /******************************************************************** ********************* Local Routines ************************* ********************************************************************/ /*@@ @routine CreatePageData @date Wed Sep 13 20:10:24 2000 @author Tom Goodale @desc Creates a httpPage structure. @enddesc @calls @calledby @history @endhistory @@*/ static httpPage *CreatePageData(int (*function)(cGH *, httpRequest *, void *), void *data) { httpPage *pagedata; pagedata = (httpPage *)malloc(sizeof(httpPage)); if(pagedata) { pagedata->function = function; pagedata->data = data; } return pagedata; } /*@@ @routine FindPage @date Wed Sep 13 20:10:24 2000 @author Tom Goodale @desc Finds a page, if it exists. @enddesc @calls @calledby @history @endhistory @@*/ static httpPage *FindPage(char *path, char **residual) { httpPage *pagedata; char *position; char *temp; pagedata = NULL; if(pages) { /* Check for index.html */ if(path[strlen(path)-1] == '/') { #ifdef HTTP_DEBUG printf("Looking for '%sindex.html'\n", path); #endif temp = (char *)malloc(strlen(path)+11); sprintf(temp,"%sindex.html", path); pagedata = Util_HashData(pages, strlen(temp), temp, 0); *residual = NULL; free(temp); } else if((pagedata = Util_HashData(pages, strlen(path), path, 0))) { /* Or exact path */ *residual = NULL; } else { #ifdef HTTP_DEBUG printf("Looking for '%s/index.html'\n", path); #endif temp = (char *)malloc(strlen(path)+12); sprintf(temp,"%s/index.html", path); pagedata = Util_HashData(pages, strlen(temp), temp, 0); *residual = NULL; free(temp); } if(!pagedata) { /* Ok, now cycle through. Know it doesn't end with a slash */ for(position = path+strlen(path)-1; position >= path; position--) { if(*position == '/') { #ifdef HTTP_DEBUG printf("Looking for '%s' less '%s' \n", path, position); #endif if((pagedata = Util_HashData(pages, position-path, path, 0))) { *residual = position+1; break; } } } } } else { pagedata = NULL; } return pagedata; } /*@@ @routine StatusUntilIt @date Tue Sep 19 22:59:34 2000 @author Tom Goodale @desc Is the iteration greater than the desired stop value ? @enddesc @calls @calledby @history @endhistory @@*/ static int StatusUntilIt(cGH *cctkGH) { int retval; CCTK_INT *value; int type; value = (CCTK_INT *)CCTK_ParameterGet("until_it",CCTK_THORNSTRING, &type); if(cctkGH && *value <= cctkGH->cctk_iteration) { retval = 1; } else { retval = 0; } return retval; } /*@@ @routine StatusUntilTime @date Tue Sep 19 23:00:16 2000 @author Tom Goodale @desc Is the simulation time greater than the stop value ? @enddesc @calls @calledby @history @endhistory @@*/ static int StatusUntilTime(cGH *cctkGH) { int retval; CCTK_REAL *value; int type; value = (CCTK_REAL *)CCTK_ParameterGet("until_time",CCTK_THORNSTRING, &type); if(cctkGH && *value <= cctkGH->cctk_time) { retval = 1; } else { retval = 0; } return retval; } /*@@ @routine StatusUntilExpression @date Tue Sep 19 23:00:47 2000 @author Tom Goodale @desc Is the stop expression true ? @enddesc @calls @calledby @history @endhistory @@*/ static int StatusUntilExpression(cGH *cctkGH) { static char *parsed_expression = NULL; static int times_set = -1; int new_times_set; int retval; const char **value; int type; char *copy; /* See if we need to parse the expression again. */ new_times_set = CCTK_ParameterQueryTimesSet("until_expression", CCTK_THORNSTRING); if(new_times_set > times_set) { times_set = new_times_set; value = (const char **)CCTK_ParameterGet("until_expression",CCTK_THORNSTRING, &type); if(parsed_expression) { free(parsed_expression); } parsed_expression = HTTP_ExpressionParse(*value); } if(parsed_expression && strlen(parsed_expression) > 0 && cctkGH) { /* Make a copy */ copy = Util_Strdup(parsed_expression); /* Evaluate the expression */ retval = HTTP_ExpressionEvaluate(copy, evaluator, (void *)cctkGH); /* Free the copy */ free(copy); } else { retval = 0; } return retval; } /*@@ @routine evaluator @date Tue Sep 19 23:07:20 2000 @author Tom Goodale @desc Takes the name of a gridscalar and returns its value. @enddesc @calls @calledby @history @endhistory @@*/ static double evaluator(const char *expression, void *data) { double retval; cGH *cctkGH; void *pointer; int varindex; int vartype; cctkGH = (cGH *)data; varindex = CCTK_VarIndex(expression); if(varindex > -1) { vartype = CCTK_VarTypeI(varindex); pointer = CCTK_VarDataPtrI(cctkGH, 0, varindex); switch(vartype) { case CCTK_VARIABLE_CHAR : retval = *((CCTK_CHAR *)pointer); break; case CCTK_VARIABLE_INT : retval = *((CCTK_INT *)pointer); break; case CCTK_VARIABLE_REAL : retval = *((CCTK_REAL *)pointer); break; default : fprintf(stderr, "Unsupported variable type %d\n", vartype); retval = 0; } } else if(CCTK_Equals(expression,"time")) { retval = cctkGH->cctk_time; } else if(CCTK_Equals(expression,"iteration")) { retval = cctkGH->cctk_iteration; } else { retval = strtod(expression,NULL); } return retval; }