/** Process an incoming GUI request @returns non-zero on success, 0 on failure (errno set) **/ int http_gui_request(HTTP *http,char *uri) { gui_set_html_stream((void*)http,http_format); if (gui_html_output_page(uri)>=0) { http_type(http,"text/html"); return 1; } else return 0; }
/** Process an incoming action request @returns non-zero on success, 0 on failure (errno set) **/ int http_action_request(HTTP *http,char *action) { if (gui_post_action(action)==-1) { http_status(http,HTTP_ACCEPTED); http_type(http,"text/plain"); http_format(http,"Goodbye"); http_send(http); http_close(http); shutdown_now(); return 1; } else return 0; }
/* * Handles an HTTP error. */ void http_error(socket_t s, unsigned http_code, const struct http_request_s *request) { const char *content_filename; switch (http_code) { case 404: content_filename = "404.html"; break; case 500: content_filename = "500.html"; break; default: panic("unsupported HTTP code %u", http_code); } if (http_type(request->name) == HTTP_TYPE_HTML) { http_buffer_t content = http_lookup_content(content_filename); if (content != NULL) { if (http_send_response(s, http_code, content, request)) { http_buffer_close(content); return; } http_buffer_close(content); } } // Emergency fall back: static const char http_header[] = "HTTP/1.1 500 Internal Server Error\r\n" "Connection: close\r\n" "\r\n"; size_t http_header_length = sizeof(http_header)-1; if (send(s, http_header, http_header_length, 0) != http_header_length) { warning("unable to send HTTP 500 response of size " SIZE_T_FMT " bytes", http_header_length); } }
/** Process an incoming XML data request @returns non-zero on success, 0 on failure (errno set) **/ int http_xml_request(HTTP *http,char *uri) { char arg1[1024]="", arg2[1024]=""; int nargs = sscanf(uri,"%1023[^/=\r\n]/%1023[^\r\n=]",arg1,arg2); char *value = strchr(uri,'='); char buffer[1024]=""; OBJECT *obj=NULL; char *id; /* value */ if (value) *value++; /* decode %.. */ http_decode(arg1); http_decode(arg2); if (value) http_decode(value); /* process request */ switch (nargs) { /* get global variable */ case 1: /* find the variable */ if (global_getvar(arg1,buffer,sizeof(buffer))==NULL) { output_error("global variable '%s' not found", arg1); return 0; } /* assignment, if any */ if (value) global_setvar(arg1,value); /* post the response */ http_format(http,"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); http_format(http,"<globalvar>\n\t<name>%s</name>\n\t<value>%s</value>\n</globalvar>\n", arg1, http_unquote(buffer)); http_type(http,"text/xml"); return 1; /* get object property */ case 2: /* find the object */ id = strchr(arg1,':'); if ( id==NULL ) obj = object_find_name(arg1); else obj = object_find_by_id(atoi(id+1)); if ( obj==NULL ) { output_error("object '%s' not found", arg1); return 0; } /* post the current value */ if ( !object_get_value_by_name(obj,arg2,buffer,sizeof(buffer)) ) { output_error("object '%s' property '%s' not found", arg1, arg2); return 0; } /* assignment, if any */ if ( value && !object_set_value_by_name(obj,arg2,value) ) { output_error("cannot set object '%s' property '%s' to '%s'", arg1, arg2, value); return 0; } /* post the response */ http_format(http,"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<property>\n"); http_format(http,"\t<object>%s</object>\n", arg1); http_format(http,"\t<name>%s</name>\n", arg2); http_format(http,"\t<value>%s</value>\n", http_unquote(buffer)); /* TODO add property type info */ http_format(http,"</property>\n"); http_type(http,"text/xml"); return 1; default: return 0; } return 0; }
/** Process an incoming request @returns nothing **/ void http_response(SOCKET fd) { HTTP *http = http_create(fd); size_t len; int content_length = 0; char *user_agent = NULL; char *host = NULL; int keep_alive = 0; char *connection = NULL; char *accept = NULL; struct s_map { char *name; enum {INTEGER,STRING} type; void *value; size_t sz; } map[] = { {"Content-Length", INTEGER, (void*)&content_length, 0}, {"Host", STRING, (void*)&host, 0}, {"Keep-Alive", INTEGER, (void*)&keep_alive, 0}, {"Connection", STRING, (void*)&connection, 0}, {"Accept", STRING, (void*)&accept, 0}, }; while ( (int)(len=recv_data(fd,http->query,sizeof(http->query)))>0 ) { /* first term is always the request */ char *request = http->query; char method[32]; char uri[1024]; char version[32]; char *p = strchr(http->query,'\r'); int v; /* initialize the response */ http_reset(http); /* read the request string */ if (sscanf(request,"%s %s %s",method,uri,version)!=3) { http_status(http,HTTP_BADREQUEST); http_format(http,HTTP_BADREQUEST); http_type(http,"text/html"); http_send(http); break; } /* read the rest of the header */ while (p!=NULL && (p=strchr(p,'\r'))!=NULL) { *p = '\0'; p+=2; for ( v=0 ; v<sizeof(map)/sizeof(map[0]) ; v++ ) { if (map[v].sz==0) map[v].sz = strlen(map[v].name); if (strnicmp(map[v].name,p,map[v].sz)==0 && strncmp(p+map[v].sz,": ",2)==0) { if (map[v].type==INTEGER) { *(int*)(map[v].value) = atoi(p+map[v].sz+2); break; } else if (map[v].type==STRING) { *(char**)map[v].value = p+map[v].sz+2; break; } } } } output_verbose("%s (host='%s', len=%d)",http->query,host?host:"???",content_length); /* reject anything but a GET */ if (stricmp(method,"GET")!=0) { http_status(http,HTTP_METHODNOTALLOWED); http_format(http,HTTP_METHODNOTALLOWED); http_type(http,"text/html"); /* technically, we should add an Allow entry to the response header */ http_send(http); break; } /* handle request */ if ( strcmp(uri,"/favicon.ico")==0 ) { if ( http_favicon(http) ) http_status(http,HTTP_OK); else http_status(http,HTTP_NOTFOUND); http_send(http); } else { static struct s_map { char *path; int (*request)(HTTP*,char*); char *success; char *failure; } map[] = { /* this is the map of recognize request types */ {"/xml/", http_xml_request, HTTP_OK, HTTP_NOTFOUND}, {"/gui/", http_gui_request, HTTP_OK, HTTP_NOTFOUND}, {"/output/", http_output_request, HTTP_OK, HTTP_NOTFOUND}, {"/action/", http_action_request, HTTP_ACCEPTED,HTTP_NOTFOUND}, {"/rt/", http_get_rt, HTTP_OK, HTTP_NOTFOUND}, {"/perl/", http_run_perl, HTTP_OK, HTTP_NOTFOUND}, {"/gnuplot/", http_run_gnuplot, HTTP_OK, HTTP_NOTFOUND}, {"/java/", http_run_java, HTTP_OK, HTTP_NOTFOUND}, {"/python/", http_run_python, HTTP_OK, HTTP_NOTFOUND}, {"/r/", http_run_r, HTTP_OK, HTTP_NOTFOUND}, {"/scilab/", http_run_scilab, HTTP_OK, HTTP_NOTFOUND}, {"/octave/", http_run_octave, HTTP_OK, HTTP_NOTFOUND}, }; int n; for ( n=0 ; n<sizeof(map)/sizeof(map[0]) ; n++ ) { size_t len = strlen(map[n].path); if (strncmp(uri,map[n].path,len)==0) { if ( map[n].request(http,uri+len) ) http_status(http,map[n].success); else http_status(http,map[n].failure); http_send(http); goto Next; } } } /* deprecated XML usage */ if (strncmp(uri,"/",1)==0 ) { if ( http_xml_request(http,uri+1) ) { output_warning("deprecate XML usage in request '%s'", uri); http_status(http,HTTP_OK); } else http_status(http,HTTP_NOTFOUND); http_send(http); } else { http_status(http,HTTP_NOTFOUND); http_format(http,HTTP_NOTFOUND); http_type(http,"text/html"); http_send(http); } /* keep-alive not desired*/ Next: if (connection && stricmp(connection,"close")==0) break; } http_close(http); output_verbose("socket %d closed",http->s); }
/* * Send a HTTP response. */ bool http_send_response(socket_t s, unsigned http_code, http_buffer_t content, const struct http_request_s *request) { const char *http_response_header; switch (http_code) { case 200: http_response_header = "HTTP/1.1 200 OK"; break; case 404: http_response_header = "HTTP/1.1 404 Not Found"; break; case 500: http_response_header = "HTTP/1.1 500 Internal Server Error"; break; default: return false; } const char *http_content_type; int type = http_type(request->name); switch (type) { default: case HTTP_TYPE_HTML: http_content_type = "text/html"; break; case HTTP_TYPE_CSS: http_content_type = "text/css"; break; case HTTP_TYPE_JAVASCRIPT: http_content_type = "text/javascript"; break; case HTTP_TYPE_TEXT: http_content_type = "text/plain"; break; case HTTP_TYPE_SVG: http_content_type = "image/svg+xml"; break; } http_buffer_t buff; if (type == HTTP_TYPE_HTML || type == HTTP_TYPE_JAVASCRIPT) { buff = http_buffer_open(); http_expand_content(content, request, buff); } else { buff = content; } const char *http_header_format = "%s\r\n" "Content-Length: %d\r\n" "Connection: close\r\n" "Content-Type: %s\r\n" "Cache-Control: no-cache, must-revalidate\r\n" "Server: " PROGRAM_NAME_LONG "\r\n" "\r\n"; size_t http_body_length = buff->put_pos; size_t http_header_length = snprintf(NULL, 0, http_header_format, http_response_header, http_body_length, http_content_type); size_t http_response_length = http_header_length + http_body_length; char *http_response = (char *)malloc(http_response_length); if (http_response == NULL) { error("unable to allocate " SIZE_T_FMT " bytes for HTTP response " "buffer", http_response_length); } snprintf(http_response, http_response_length, http_header_format, http_response_header, http_body_length, http_content_type); memmove(http_response + http_header_length, buff->buff, http_body_length); if (buff != content) { http_buffer_close(buff); } bool ret_val = true; if (send(s, http_response, http_response_length, 0) != http_response_length) { warning("unable to send HTTP %u response of size " SIZE_T_FMT " bytes", http_code, http_response_length); ret_val = false; } free(http_response); return ret_val; }
void http_response(SOCKET fd) { HTTP *http = http_create(fd); size_t len; int content_length = 0; char *user_agent = NULL; char *host = NULL; int keep_alive = 0; char *connection = NULL; char *accept = NULL; struct s_map { char *name; enum {INTEGER,STRING} type; void *value; int sz; } map[] = { {"Content-Length", INTEGER, (void*)&content_length, 0}, {"Host", STRING, (void*)&host, 0}, {"Keep-Alive", INTEGER, (void*)&keep_alive, 0}, {"Connection", STRING, (void*)&connection, 0}, {"Accept", STRING, (void*)&accept, 0}, }; while ( (int)(len=recv_data(fd,http->query,sizeof(http->query)))>0 ) { /* first term is always the request */ char *request = http->query; char method[32]; char uri[1024]; char version[32]; char *p = strchr(http->query,'\r'); int v; /* read the request string */ if (sscanf(request,"%s %s %s",method,uri,version)!=3) { http_status(http,HTTP_BADREQUEST); http_format(http,HTTP_BADREQUEST); http_type(http,"text/html"); http_send(http); break; } /* read the rest of the header */ while (p!=NULL && (p=strchr(p,'\r'))!=NULL) { *p = '\0'; p+=2; for ( v=0 ; v<sizeof(map)/sizeof(map[0]) ; v++ ) { if (map[v].sz==0) map[v].sz = strlen(map[v].name); if (strnicmp(map[v].name,p,map[v].sz)==0 && strncmp(p+map[v].sz,": ",2)==0) { if (map[v].type==INTEGER) { *(int*)(map[v].value) = atoi(p+map[v].sz+2); break; } else if (map[v].type==STRING) { *(char**)map[v].value = p+map[v].sz+2; break; } } } } output_verbose("%s (host='%s', len=%d)",http->query,host?host:"???",content_length); /* reject anything but a GET */ if (stricmp(method,"GET")!=0) { http_status(http,HTTP_METHODNOTALLOWED); http_format(http,HTTP_METHODNOTALLOWED); http_type(http,"text/html"); /* technically, we should add an Allow entry to the response header */ http_send(http); break; } /* handle request */ if (strncmp(uri,"/gui/",5)==0 ) { if ( http_gui_request(http,uri+5) ) http_status(http,HTTP_OK); else http_status(http,HTTP_NOTFOUND); http_send(http); } else if (strncmp(uri,"/output/",8)==0 ) { if ( http_output_request(http,uri+8) ) http_status(http,HTTP_OK); else http_status(http,HTTP_NOTFOUND); http_send(http); } else if (strncmp(uri,"/action/",8)==0) { if ( http_action_request(http,uri+8) ) http_status(http,HTTP_ACCEPTED); else http_status(http,HTTP_NOTFOUND); http_send(http); } else if ( strcmp(uri,"/favicon.ico")==0 ) { if ( http_favicon(http) ) http_status(http,HTTP_OK); else http_status(http,HTTP_NOTFOUND); http_send(http); } else if (strncmp(uri,"/",1)==0 ) { if ( http_xml_request(http,uri+1) ) http_status(http,HTTP_OK); else http_status(http,HTTP_NOTFOUND); http_send(http); } else { http_status(http,HTTP_NOTFOUND); http_format(http,HTTP_NOTFOUND); http_type(http,"text/html"); http_send(http); } /* keep-alive not desired*/ if (connection && stricmp(connection,"close")==0) break; } http_close(http); output_verbose("socket %d closed",http->s); }