diff --git a/Makefile b/Makefile index 3a60486..bbb9af4 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,8 @@ LIBOBJS = libs/ini.o \ libs/utils.o \ libs/ws.o \ libs/sha1.o \ - libs/list.o + libs/list.o \ + libs/scheduler.o PLUGINSDEP = libs/plugin.o @@ -30,6 +31,9 @@ httpd: lib $(SERVER_O) $(CC) $(CFLAGS) $(SERVER_O) -o $(BUILDIRD)/httpd httpd.c $(SERVERLIB) cp antd $(BUILDIRD) +relay: lib $(SERVER_O) + $(CC) $(CFLAGS) $(SERVER_O) -o $(BUILDIRD)/relay relay.c $(SERVERLIB) + cp forward $(BUILDIRD) lib: $(LIBOBJS) $(CC) $(CFLAGS) $(DB_LIB) $(SSL_LIB) -shared -o $(LIB_NAME).$(EXT) $(LIBOBJS) cp $(LIB_NAME).$(EXT) $(LIB_PATH$)/ diff --git a/forward b/forward new file mode 100644 index 0000000..dd2f1cc --- /dev/null +++ b/forward @@ -0,0 +1,8 @@ +#!/bin/sh +UNAME=`uname -s` + +if [ "$UNAME" = "Darwin" ]; then + DYLD_LIBRARY_PATH=$(dirname "$0")/plugins/ $(dirname "$0")/relay +else + LD_LIBRARY_PATH=$(dirname "$0")/plugins/ $(dirname "$0")/relay +fi diff --git a/http_server.c b/http_server.c index 928b46e..e768dbb 100644 --- a/http_server.c +++ b/http_server.c @@ -1,120 +1,299 @@ #include "http_server.h" static pthread_mutex_t server_mux = PTHREAD_MUTEX_INITIALIZER; -/**********************************************************************/ -/* A request has caused a call to accept() on the server port to -* return. Process the request appropriately. -* Parameters: the socket connected to the client */ -/**********************************************************************/ -void accept_request(void* client) +config_t server_config; +config_t* config() { - char buf[1024]; - int numchars; - char method[255]; - char url[4096]; - char path[1024]; - char* token; - char *line; - char* oldurl = NULL; - char* tmp = NULL; - dictionary rq = NULL; - size_t i, j; - struct stat st; - - //char *query_string = NULL; - //LOG("SOCK IS %d\n", ((antd_client_t*)client)->sock); - numchars = read_buf(client, buf, sizeof(buf)); - if(numchars <= 0) - { - unknow(client); - goto end; - } - i = 0; j = 0; - while (j < numchars && !ISspace(buf[j]) && (i < sizeof(method) - 1)) - { - method[i] = buf[j]; - i++; j++; - } - method[i] = '\0'; - if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) - { - LOG("METHOD NOT FOUND %s\n", method); - // unimplemented - //while(get_line(client, buf, sizeof(buf)) > 0) printf("%s\n",buf ); - unimplemented(client); - //antd_close(client); - goto end; - } - - - i = 0; - while (ISspace(buf[j]) && (j < sizeof(buf))) - j++; - while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf))) - { - url[i] = buf[j]; - i++; j++; - } - url[i] = '\0'; - + return &server_config; +} +void destroy_config() +{ + list_free(&(server_config.rules)); + freedict(server_config.handlers); + if(server_config.plugins_dir) free(server_config.plugins_dir); + if(server_config.plugins_ext) free(server_config.plugins_ext); + if(server_config.db_path) free(server_config.db_path); + if(server_config.htdocs) free(server_config.htdocs); + if(server_config.tmpdir) free(server_config.tmpdir); - oldurl = strdup(url); - tmp = strchr(oldurl,'?'); - if(tmp) - *tmp = '\0'; + LOG("Unclosed connection: %d\n", server_config.connection); +} - rq = decode_request(client, method, url); - if(rq == NULL) +static int config_handler(void* conf, const char* section, const char* name, + const char* value) +{ + config_t* pconfig = (config_t*)conf; + //char * ppath = NULL; + if (MATCH("SERVER", "port")) { + pconfig->port = atoi(value); + } else if (MATCH("SERVER", "plugins")) { + pconfig->plugins_dir = strdup(value); + } else if (MATCH("SERVER", "plugins_ext")) { + pconfig->plugins_ext = strdup(value); + } else if(MATCH("SERVER", "database")) { + pconfig->db_path = strdup(value); + } else if(MATCH("SERVER", "htdocs")) { + pconfig->htdocs = strdup(value); + } else if(MATCH("SERVER", "tmpdir")) { + pconfig->tmpdir = strdup(value); + } + else if(MATCH("SERVER", "maxcon")) { + pconfig->maxcon = atoi(value); + } + else if(MATCH("SERVER", "backlog")) { + pconfig->backlog = atoi(value); + } + else if(MATCH("SERVER", "workers")) { + pconfig->n_workers = atoi(value); + } +#ifdef USE_OPENSSL + else if(MATCH("SERVER", "ssl.enable")) { + pconfig->usessl = atoi(value); + } + else if(MATCH("SERVER", "ssl.cert")) { + pconfig->sslcert = strdup(value); + } + else if(MATCH("SERVER", "ssl.key")) { + pconfig->sslkey = strdup(value); + } +#endif + else if (strcmp(section, "RULES") == 0) { - badrequest(client); - goto end; - } + list_put_s(&pconfig->rules, name); + list_put_s(&pconfig->rules, value); + } + else if (strcmp(section, "FILEHANDLER") == 0) + { + dput( pconfig->handlers, name ,strdup(value)); + } + else if(strcmp(section,"AUTOSTART")==0){ + // The server section must be added before the autostart section + // auto start plugin + plugin_load(value); + } else { + return 0; /* unknown section/name, error */ + } + return 1; +} +void init_file_system() +{ + struct stat st; + if (stat(server_config.plugins_dir, &st) == -1) + mkdir(server_config.plugins_dir, 0755); + if (stat(server_config.db_path, &st) == -1) + mkdir(server_config.db_path, 0755); + if (stat(server_config.htdocs, &st) == -1) + mkdir(server_config.htdocs, 0755); + if (stat(server_config.tmpdir, &st) == -1) + mkdir(server_config.tmpdir, 0755); + else + { + removeAll(server_config.tmpdir,0); + } + +} +void load_config(const char* file) +{ + server_config.port = 8888; + server_config.plugins_dir = "plugins/"; + server_config.plugins_ext = ".dylib"; + server_config.db_path = "databases/"; + server_config.htdocs = "htdocs/"; + server_config.tmpdir = "tmp/"; + server_config.n_workers = 4; + server_config.backlog = 100; + server_config.rules = list_init(); + server_config.handlers = dict(); + server_config.maxcon = 1000; + server_config.connection = 0; +#ifdef USE_OPENSSL + server_config.usessl = 0; + server_config.sslcert = "cert.pem"; + server_config.sslkey = "key.pem"; +#endif + if (ini_parse(file, config_handler, &server_config) < 0) { + LOG("Can't load '%s'\n. Used defaut configuration", file); + } + else + { + LOG("Using configuration : %s\n", file); +#ifdef USE_OPENSSL + LOG("SSL enable %d\n", server_config.usessl); + LOG("SSL cert %s\n", server_config.sslcert); + LOG("SSL key %s\n", server_config.sslkey); +#endif + } + init_file_system(); +} + + +void* accept_request(void* data) +{ + int count; + char buf[BUFFLEN]; + char* token = NULL; + char* line = NULL; + antd_task_t* task; + antd_request_t* rq = (antd_request_t*) data; + + task = antd_create_task(NULL,(void*)rq,NULL); + task->priority++; + fd_set read_flags, write_flags; + // first verify if the socket is ready + antd_client_t* client = (antd_client_t*) rq->client; + FD_ZERO(&read_flags); + FD_SET(rq->client->sock, &read_flags); + FD_ZERO(&write_flags); + FD_SET(rq->client->sock, &write_flags); + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 500; + // select + int sel = select(client->sock+1, &read_flags, &write_flags, (fd_set*)0, &timeout); + if(sel == -1) + { + unknow(rq->client); + return task; + } + if(sel == 0 || (!FD_ISSET(client->sock, &read_flags) && !FD_ISSET(client->sock, &write_flags))) + { + // retry it later + task->handle = accept_request; + return task; + } + // perform the ssl handshake if enabled +#ifdef USE_OPENSSL + int ret,stat; + if(server_config.usessl == 1 && client->status == 0) + { + if (SSL_accept((SSL*)client->ssl) == -1) { + stat = SSL_get_error((SSL*)client->ssl, ret); + switch(stat) + { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_NONE: + //LOG("RECALL %d\n", stat); + task->handle = accept_request; + task->priority = HIGH_PRIORITY; + return task; + default: + LOG("ERRRRRRRRROR accept %d %d %d\n", stat, ret, ERR_get_error()); + ERR_print_errors_fp(stderr); + return task; + } + } + client->status = 1; + task->handle = accept_request; + return task; + } + else + { + if(!FD_ISSET(client->sock, &read_flags)) + { + task->handle = accept_request; + return task; + } + } +#endif + server_config.connection++; + count = read_buf(rq->client, buf, sizeof(buf)); + //LOG("count is %d\n", count); + line = buf; + // get the method string + token = strsep(&line," "); + if(!line) + { + LOG("No method found\n"); + unknow(rq->client); + return task; + } + trim(token,' '); + trim(line,' '); + dput(rq->request, "METHOD", strdup(token)); + // get the request + token = strsep(&line, " "); + if(!line) + { + LOG("No request found\n"); + unknow(rq->client); + return task; + } + trim(token,' '); + trim(line,' '); + trim(line, '\n'); + trim(line, '\r'); + dput(rq->request, "PROTOCOL", strdup(line)); + dput(rq->request, "REQUEST_QUERY", strdup(token)); + line = token; + token = strsep(&line, "?"); + dput(rq->request, "REQUEST_PATH", strdup(token)); + // decode request + // now return the task + task->handle = decode_request_header; + return task; +} + +void* resolve_request(void* data) +{ + struct stat st; + char path[2*BUFFLEN]; + antd_request_t* rq = (antd_request_t*) data; + antd_task_t* task = antd_create_task(NULL,(void*)rq,NULL); + task->priority++; + char* url = (char*)dvalue(rq->request, "RESOURCE_PATH"); + char* newurl = NULL; + char* rqp = (char*)dvalue(rq->request, "REQUEST_PATH"); sprintf(path, server_config.htdocs); strcat(path, url); LOG("Path is : %s \n", path); //if (path[strlen(path) - 1] == '/') // strcat(path, "index.html"); if (stat(path, &st) == -1) { - if(execute_plugin(client,oldurl,method,rq) < 0) - not_found(client); + free(task); + return execute_plugin(rq, rqp); } else { if (S_ISDIR(st.st_mode)) { - int l = strlen(path); - int ul = strlen(url); strcat(path, "/index.html"); if(stat(path, &st) == -1) { association it; for_each_assoc(it, server_config.handlers) { - path[l] = '\0'; - url[ul] = '\0'; - strcat(url,"/index."); - strcat(path, "/index."); - strcat(url,it->key); - strcat(path, it->key); - if(stat(path, &st) == 0) + newurl = __s("%s/index.%s", url, it->key); + memset(path, 0, sizeof(path)); + strcat(path, server_config.htdocs); + strcat(path, newurl); + if(stat(path, &st) != 0) + { + free(newurl); + newurl = NULL; + } + else { - l = -1; - i = HASHSIZE; break; } } - if(l!= -1) + if(!newurl) { - not_found(client); - goto end; + notfound(rq->client); + return task; } + //if(url) free(url); this is freed in the dput function + url = newurl; + dput(rq->request, "RESOURCE_PATH", url); } } + dput(rq->request, "ABS_RESOURCE_PATH", strdup(path)); // check if the mime is supported // if the mime is not supported // find an handler plugin to process it // if the plugin is not found, forbidden access to the file should be sent char* mime_type = mime(path); + dput(rq->request, "RESOURCE_MIME", strdup(mime_type)); if(strcmp(mime_type,"application/octet-stream") == 0) { char * ex = ext(path); @@ -122,44 +301,50 @@ void accept_request(void* client) if(ex) free(ex); if(h) { - sprintf(buf,"/%s%s",h,url); - LOG("WARNING::::Access octetstream via handler %s\n", buf); - if(execute_plugin(client,buf,method,rq) < 0) - cannot_execute(client); + sprintf(path,"/%s%s",h,url); + LOG("WARNING::::Access octetstream via handler %s\n", path); + //if(execute_plugin(client,buf,method,rq) < 0) + // cannot_execute(client); + free(task); + return execute_plugin(rq, path); } else - unknow(client); + unknow(rq->client); } else { - ctype(client,mime_type); - // if the mime is supported, send the file - serve_file(client, path); - //response(client,"this is the file"); - } + task->type = HEAVY; + task->handle = serve_file; + } + return task; } -end: - if(oldurl) free(oldurl); - if(rq) - { - dictionary subdict; - subdict = (dictionary)dvalue(rq, "__xheader__"); - if(subdict) - { - freedict(subdict); - dput(rq, "__xheader__", NULL); - } +} - subdict = (dictionary)dvalue(rq, "cookie"); - if(subdict) - { - freedict(subdict); - dput(rq, "cookie", NULL); - } - freedict(rq); +void* finish_request(void* data) +{ + if(!data) return NULL; + LOG("Close request\n"); + antd_request_t* rq = (antd_request_t*)data; + // free all other thing + if(rq->request) + { + dictionary tmp = dvalue(rq->request, "COOKIE"); + if(tmp) freedict(tmp); + tmp = dvalue(rq->request, "REQUEST_HEADER"); + if(tmp) freedict(tmp); + tmp = dvalue(rq->request, "REQUEST_DATA"); + if(tmp) freedict(tmp); + dput(rq->request, "REQUEST_HEADER", NULL); + dput(rq->request, "REQUEST_DATA", NULL); + dput(rq->request, "COOKIE", NULL); + freedict(rq->request); } - antd_close(client); + antd_close(rq->client); + free(rq); + server_config.connection--; + LOG("Remaining connection %d\n", server_config.connection); + return NULL; } int rule_check(const char*k, const char* v, const char* host, const char* _url, const char* _query, char* buf) @@ -232,164 +417,28 @@ int rule_check(const char*k, const char* v, const char* host, const char* _url, free(query); return 1; } -/**********************************************************************/ -/* Put the entire contents of a file out on a socket. This function -* is named after the UNIX "cat" command, because it might have been -* easier just to do something like pipe, fork, and exec("cat"). -* Parameters: the client socket descriptor -* FILE pointer for the file to cat */ -/**********************************************************************/ -void catb(void* client, FILE* ptr) -{ - unsigned char buffer[BUFFLEN]; - size_t size; - while(!feof(ptr)) - { - size = fread(buffer,1,BUFFLEN,ptr); - __b(client,buffer,size); - //if(!__b(client,buffer,size)) return; - } - //fclose(ptr); -} -void cat(void* client, FILE *resource) -{ - char buf[1024]; - //fgets(buf, sizeof(buf), resource); - while (fgets(buf, sizeof(buf), resource) != NULL) - { - antd_send(client, buf, strlen(buf)); - //fgets(buf, sizeof(buf), resource); - } - -} - -/**********************************************************************/ -/* Inform the client that a CGI script could not be executed. -* Parameter: the client socket descriptor. */ -/**********************************************************************/ -void cannot_execute(void* client) -{ - set_status(client,500,"Internal Server Error"); - __t(client,SERVER_STRING); - __t(client,"Content-Type: text/html"); - response(client,""); - __t(client, "

Error prohibited CGI execution."); -} - -/**********************************************************************/ -/* Print out an error message with perror() (for system errors; based -* on value of errno, which indicates system call errors) and exit the -* program indicating an error. */ -/**********************************************************************/ -void error_die(const char *sc) +static void error_die(const char *sc) { perror(sc); exit(1); } - -/**********************************************************************/ -/* Get a line from a socket, whether the line ends in a newline, -* carriage return, or a CRLF combination. Terminates the string read -* with a null character. If no newline indicator is found before the -* end of the buffer, the string is terminated with a null. If any of -* the above three line terminators is read, the last character of the -* string will be a linefeed and the string will be terminated with a -* null character. -* Parameters: the socket descriptor -* the buffer to save the data in -* the size of the buffer -* Returns: the number of bytes stored (excluding null) */ -/**********************************************************************/ -//This function is deprecate -/*int get_line(int sock, char *buf, int size) +void* serve_file(void* data) { - int i = 0; - char c = '\0'; - int n; - - while ((i < size - 1) && (c != '\n')) - { - n = recv(sock, &c, 1, 0); - - if (n > 0) - { - if (c == '\r') - { - n = recv(sock, &c, 1, MSG_PEEK); - - if ((n > 0) && (c == '\n')) - recv(sock, &c, 1, 0); - else - c = '\n'; - } - buf[i] = c; - i++; - } - else - c = '\n'; - } - buf[i] = '\0'; - - return(i); -}*/ - - -/**********************************************************************/ -/* Give a client a 404 not found status message. */ -/**********************************************************************/ -void not_found(void* client) -{ - set_status(client,404,"NOT FOUND"); - __t(client,SERVER_STRING); - __t(client,"Content-Type: text/html"); - response(client,""); - __t(client, "Not Found"); - __t(client, "

The server could not fulfill"); - __t(client, "your request because the resource specified"); - __t(client, "is unavailable or nonexistent."); - __t(client, ""); -} - -/**********************************************************************/ -/* Send a regular file to the client. Use headers, and report -* errors to client if they occur. -* Parameters: a pointer to a file structure produced from the socket -* file descriptor -* the name of the file to serve */ -/**********************************************************************/ -void serve_file(void* client, const char *filename) -{ - LOG("Serve file: %s\n", filename); - FILE *resource = NULL; - int numchars = 1; - //char buf[1024]; - - //buf[0] = 'A'; buf[1] = '\0'; - //while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ - // numchars = get_line(client, buf, sizeof(buf)); - - resource = fopen(filename, "rb"); - if (resource == NULL) - not_found(client); + antd_request_t* rq = (antd_request_t*) data; + antd_task_t* task = antd_create_task(NULL,(void*)rq,NULL); + task->priority++; + char* path = (char*)dvalue(rq->request, "ABS_RESOURCE_PATH"); + char* newurl = NULL; + char* mime_type = (char*)dvalue(rq->request, "RESOURCE_MIME"); + ctype(rq->client,mime_type); + if(is_bin(path)) + __fb(rq->client, path); else - { - if(is_bin(filename)) - catb(client,resource); - else - cat(client, resource); - } - fclose(resource); + __f(rq->client, path); + return task; } -/**********************************************************************/ -/* This function starts the process of listening for web connections -* on a specified port. If the port is 0, then dynamically allocate a -* port and modify the original port variable to reflect the actual -* port. -* Parameters: pointer to variable containing the port to connect on -* Returns: the socket */ -/**********************************************************************/ int startup(unsigned *port) { int httpd = 0; @@ -417,32 +466,6 @@ int startup(unsigned *port) return(httpd); } -/**********************************************************************/ -/* Inform the client that the requested web method has not been -* implemented. -* Parameter: the client socket */ -/**********************************************************************/ -void unimplemented(void* client) -{ - set_status(client,501,"Method Not Implemented"); - __t(client,SERVER_STRING); - __t(client,"Content-Type: text/html"); - response(client,""); - __t(client, "Method Not Implemented"); - __t(client, ""); - __t(client, "

HTTP request method not supported."); - __t(client, ""); -} - -void badrequest(void* client) -{ - set_status(client,400,"Bad Request"); - __t(client,SERVER_STRING); - __t(client,"Content-Type: text/html"); - response(client,""); - __t(client,"The request could not be understood by the server due to malformed syntax."); -} - char* apply_rules(const char* host, char*url) { association it; @@ -479,39 +502,29 @@ char* apply_rules(const char* host, char*url) return strdup(query_string); } /** - * Decode the HTTP request - * Get the cookie values - * if it is the GET request, decode the query string into a dictionary - * if it is a POST, check the content type of the request - * - if it is a POST request with URL encoded : decode the url encode - * - if it is a POST request with multipart form data: de code the multipart - * - if other - UNIMPLEMENTED - * @param client socket client - * @param method HTTP method - * @param query query string in case of GET - * @return a dictionary of key- value + * Decode the HTTP request header */ -dictionary decode_request(void* client,const char* method, char* url) + +void* decode_request_header(void* data) { - dictionary request = dict(); + antd_request_t* rq = (antd_request_t*) data; dictionary cookie = NULL; - dictionary xheader = dict(); char* line; char * token; char* query = NULL; - char* ctype = NULL; char* host = NULL; - int clen = -1; - + char buf[2*BUFFLEN]; + char* url = (char*)dvalue(rq->request, "REQUEST_QUERY"); + dictionary xheader = dict(); + dictionary request = dict(); + dput(rq->request,"REQUEST_HEADER",xheader); + dput(rq->request,"REQUEST_DATA",request); // first real all header // this for check if web socket is enabled - int ws= 0; - char* ws_key = NULL; - char buf[BUFFLEN]; // ip address - dput(xheader,"REMOTE_ADDR", (void*)strdup(((antd_client_t*)client)->ip )); + dput(xheader,"REMOTE_ADDR", (void*)strdup(((antd_client_t*)rq->client)->ip )); //while((line = read_line(client)) && strcmp("\r\n",line)) - while((read_buf(client,buf,sizeof(buf))) && strcmp("\r\n",buf)) + while((read_buf(rq->client,buf,sizeof(buf))) && strcmp("\r\n",buf)) { line = buf; trim(line, '\n'); @@ -525,109 +538,125 @@ dictionary decode_request(void* client,const char* method, char* url) { if(!cookie) cookie = decode_cookie(line); } - else if(token != NULL &&strcasecmp(token,"Content-Type") == 0) - { - ctype = strdup(line); //strsep(&line,":"); - trim(ctype,' '); - } else if(token != NULL &&strcasecmp(token,"Content-Length") == 0) - { - token = line; //strsep(&line,":"); - trim(token,' '); - clen = atoi(token); - } - else if(token != NULL && strcasecmp(token,"Upgrade") == 0) - { - // verify that the connection is upgrade to websocket - trim(line, ' '); - if(line != NULL && strcasecmp(line,"websocket") == 0) - ws = 1; - }else if(token != NULL && strcasecmp(token,"Host") == 0) + else if(token != NULL && strcasecmp(token,"Host") == 0) { host = strdup(line); } - else if(token != NULL && strcasecmp(token,"Sec-WebSocket-Key") == 0) - { - // get the key from the client - trim(line, ' '); - ws_key = strdup(line); - } } //if(line) free(line); - query = apply_rules(host, url); + memset(buf, 0, sizeof(buf)); + strcat(buf,url); + query = apply_rules(host, buf); + dput(rq->request,"RESOURCE_PATH",strdup(buf)); if(query) { LOG("Query: %s\n", query); decode_url_request(query, request); free(query); } + if(cookie) + dput(rq->request,"COOKIE",cookie); if(host) free(host); - if(strcmp(method,"GET") == 0) + // header ok, now checkmethod + antd_task_t* task = antd_create_task(decode_request,(void*)rq, NULL); + task->priority++; + return task; +} + +void* decode_request(void* data) +{ + antd_request_t* rq = (antd_request_t*) data; + dictionary request = dvalue(rq->request, "REQUEST_DATA"); + dictionary headers = dvalue(rq->request, "REQUEST_HEADER"); + int ws = 0; + char*ws_key = NULL; + char* method = NULL; + char* tmp; + antd_task_t* task = NULL; + ws_key = (char*) dvalue(headers, "Sec-WebSocket-Key"); + tmp = (char*)dvalue(headers, "Upgrade"); + if(tmp && strcasecmp(tmp, "websocket") == 0) ws = 1; + method = (char*) dvalue(rq->request, "METHOD"); + task = antd_create_task(NULL,(void*)rq, NULL); + task->priority++; + if(strcmp(method,"GET") == 0 || strcmp(method,"PUT") == 0) { //if(ctype) free(ctype); if(ws && ws_key != NULL) { - ws_confirm_request(client, ws_key); - free(ws_key); + ws_confirm_request(rq->client, ws_key); // insert wsocket flag to request // plugin should handle this ugraded connection // not the server - //if(!request) request = dict(); - dput(request,"__web_socket__",strdup("1")); + dput(rq->request,"__web_socket__",strdup("1")); } + // resolve task + task->handle = resolve_request; + return task; + } + else if(strcmp(method,"POST") == 0) + { + task->handle = decode_post_request; + task->type = HEAVY; + return task; } else { - if(ws_key) - free(ws_key); - if(ctype == NULL || clen == -1) - { - LOG("Bad request\n"); - if(ctype) free(ctype); - if(cookie) freedict(cookie); - freedict(request); - freedict(xheader); - return NULL; - } - LOG("ContentType %s\n", ctype); - // decide what to do with the data - if(strstr(ctype,FORM_URL_ENCODE) > 0) - { - char* pquery = post_data_decode(client,clen); - decode_url_request(pquery, request); - free(pquery); - } else if(strstr(ctype,FORM_MULTI_PART)> 0) - { - //printf("Multi part form : %s\n", ctype); - decode_multi_part_request(client,ctype,request); - } - else - { - char* pquery = post_data_decode(client,clen); - char* key = strstr(ctype,"/"); - if(key) - key++; - else - key = ctype; - dput(request,key, strdup(pquery)); - free(pquery); - } + unimplemented(rq->client); + return task; } - if(ctype) free(ctype); - //if(cookie->key == NULL) {free(cookie);cookie= NULL;} - //if(!request) - // request = dict(); - if(cookie) - dput(request,"cookie",cookie); - dput(request,"__xheader__",xheader); - return request; } -void __px(const char* data,int size) + +void* decode_post_request(void* data) { - for (int i = 0; i < size; ++i) - printf(" %02x", data[i]); - - printf("\n"); + antd_request_t* rq = (antd_request_t*) data; + dictionary request = dvalue(rq->request, "REQUEST_DATA"); + dictionary headers = dvalue(rq->request, "REQUEST_HEADER"); + char* ctype = NULL; + int clen = -1; + char* tmp; + antd_task_t* task = NULL; + ctype = (char*) dvalue(headers, "Content-Type"); + tmp = (char*)dvalue(headers, "Content-Length"); + if(tmp) + clen = atoi(tmp); + task = antd_create_task(NULL,(void*)rq, NULL); + task->priority++; + if(ctype == NULL || clen == -1) + { + LOG("Bad request\n"); + badrequest(rq->client); + return task; + } + LOG("ContentType %s\n", ctype); + // decide what to do with the data + if(strstr(ctype,FORM_URL_ENCODE) > 0) + { + char* pquery = post_data_decode(rq->client,clen); + decode_url_request(pquery, request); + free(pquery); + } else if(strstr(ctype,FORM_MULTI_PART)> 0) + { + //printf("Multi part form : %s\n", ctype); + // TODO: split this to multiple task + free(task); + return decode_multi_part_request(rq,ctype,request); + } + else + { + char* pquery = post_data_decode(rq->client,clen); + char* key = strstr(ctype,"/"); + if(key) + key++; + else + key = ctype; + dput(request,key, strdup(pquery)); + free(pquery); + } + task->handle = resolve_request; + return task; } + /** * Send header to the client to confirm * that the websocket is accepted by @@ -688,195 +717,208 @@ dictionary decode_cookie(const char* line) token1 = strsep(&token,"="); if(token1 && token && strlen(token) > 0) { - if(dic == NULL) - dic = dict(); + if(dic == NULL) dic = dict(); dput(dic,token1,strdup(token)); } } - //} free(orgcpy); return dic; } /** * Decode the multi-part form data from the POST request * If it is a file upload, copy the file to tmp dir - * and generate the metadata for the server-side - * @param client the socket client - * @param ctype Content-Type of the request - * @param clen Content length, but not used here - * @return a dictionary of key - value */ -void decode_multi_part_request(void* client,const char* ctype, dictionary dic) +void* decode_multi_part_request(void* data,const char* ctype, dictionary dic) { char * boundary; - char * boundend; char * line; - char * orgline; char * str_copy = strdup(ctype); char* orgcpy = str_copy; - char* token; - char* keytoken ; - char* valtoken ; - char* part_name; - char* part_file; + antd_request_t* rq = (antd_request_t*) data; + antd_task_t* task = antd_create_task(NULL, (void*)rq, NULL); + task->priority++; + //dictionary dic = NULL; + FILE *fp = NULL; + boundary = strsep(&str_copy,"="); //discard first part + boundary = str_copy; + if(boundary && strlen(boundary)>0) + { + //dic = dict(); + trim(boundary,' '); + dput(rq->request, "MULTI_PART_BOUNDARY", strdup(boundary)); + //find first boundary + while((line = read_line(rq->client))&&strstr(line,boundary) <= 0) + { + if(line) free(line); + } + if(line) + { + task->handle = decode_multi_part_request_data; + task->type = HEAVY; + free(line); + } + } + free(orgcpy); + return task; +} +void* decode_multi_part_request_data(void* data) +{ + // loop through each part separated by the boundary + char* line; + char* orgline; + char* part_name = NULL; + char* part_file = NULL; char* file_path; char buf[BUFFLEN]; char* field; //dictionary dic = NULL; FILE *fp = NULL; - boundary = strsep(&str_copy,"="); //discard first part - boundary = strsep(&str_copy,"="); - if(boundary && strlen(boundary)>0) + char* token, *keytoken, *valtoken; + antd_request_t* rq = (antd_request_t*) data; + antd_task_t* task = antd_create_task(NULL, (void*)rq, NULL); + task->priority++; + char* boundary = (char*)dvalue(rq->request, "MULTI_PART_BOUNDARY"); + dictionary dic = (dictionary)dvalue(rq->request, "REQUEST_DATA"); + char* boundend = __s("%s--",boundary); + // search for content disposition: + while((line = read_line(rq->client)) && + strstr(line,"Content-Disposition:") <= 0) { - //dic = dict(); - trim(boundary,' '); - boundend = __s("%s--",boundary); - //find first boundary - while((line = read_line(client))&&strstr(line,boundary) <= 0) - { - if(line) free(line); - } - // loop through each part separated by the boundary - while(line && strstr(line,boundary) > 0){ - if(line) - { - free(line); - line = NULL; - } - // search for content disposition: - while((line = read_line(client)) && - strstr(line,"Content-Disposition:") <= 0) - { - free(line); - line = NULL; - } - if(!line || strstr(line,"Content-Disposition:") <= 0) - { - if(line) - free(line); - free(orgcpy); - free(boundend); - return; - } - orgline = line; - // extract parameters from header - part_name = NULL; - part_file = NULL; - while((token = strsep(&line,";"))) - { - keytoken = strsep(&token,"="); - if(keytoken && strlen(keytoken)>0) - { - trim(keytoken,' '); - valtoken = strsep(&token,"="); - if(valtoken) - { - trim(valtoken,' '); - trim(valtoken,'\n'); - trim(valtoken,'\r'); - trim(valtoken,'\"'); - if(strcmp(keytoken,"name") == 0) - { - part_name = strdup(valtoken); - } else if(strcmp(keytoken,"filename") == 0) - { - part_file = strdup(valtoken); - } - } - } - } - free(orgline); - line = NULL; - // get the binary data - if(part_name != NULL) - { - // go to the beginer of data bock - while((line = read_line(client)) && strcmp(line,"\r\n") != 0) - { - free(line); - line = NULL; - } - if(line) - { - free(line); - line = NULL; - } - if(part_file == NULL) - { - /** - * This allow only 1024 bytes of data (max), - * out of this range, the data is cut out. - * Need an efficient way to handle this - */ - line = read_line(client); - trim(line,'\n'); - trim(line,'\r'); - trim(line,' '); - dput(dic,part_name,line); - // find the next boundary - while((line = read_line(client)) && strstr(line,boundary) <= 0) - { - free(line); - line = NULL; - } - } - else - { - file_path = __s("%s%s.%u",server_config.tmpdir,part_file,(unsigned)time(NULL)); - fp=fopen(file_path, "wb"); - if(fp) - { - int totalsize=0,len=0; - //read until the next boundary - while((len = read_buf(client,buf,sizeof(buf))) > 0 && strstr(buf,boundary) <= 0) - { - fwrite(buf, len, 1, fp); - totalsize += len; - } - //remove \r\n at the end - fseek(fp, 0, SEEK_SET); - //fseek(fp,-2, SEEK_CUR); - totalsize -= 2; - ftruncate(fileno(fp),totalsize); - fclose(fp); - line = strdup(buf); - - field = __s("%s.file",part_name); - dput(dic,field, strdup(part_file)); - free(field); - field = __s("%s.tmp",part_name); - dput(dic,field,strdup(file_path)); - free(field); - field = __s("%s.size",part_name); - dput(dic,field,__s("%d",totalsize)); - free(field); - field = __s("%s.ext",part_name); - dput(dic,field,ext(part_file)); - free(field); - - } - else - { - LOG("Cannot wirte file to :%s\n", file_path ); - } - free(file_path); - free(part_file); - } - free(part_name); - } - //printf("[Lines]:%s\n",line); - // check if end of request - if(line&&strstr(line,boundend)>0) - { - LOG("End request %s\n", boundend); - free(line); - break; - } - } - free(boundend); + free(line); + line = NULL; } - free(orgcpy); - //return dic; + if(!line || strstr(line,"Content-Disposition:") <= 0) + { + if(line) + free(line); + free(boundend); + return task; + } + orgline = line; + // extract parameters from header + while((token = strsep(&line,";"))) + { + keytoken = strsep(&token,"="); + if(keytoken && strlen(keytoken)>0) + { + trim(keytoken,' '); + valtoken = strsep(&token,"="); + if(valtoken) + { + trim(valtoken,' '); + trim(valtoken,'\n'); + trim(valtoken,'\r'); + trim(valtoken,'\"'); + if(strcmp(keytoken,"name") == 0) + { + part_name = strdup(valtoken); + } else if(strcmp(keytoken,"filename") == 0) + { + part_file = strdup(valtoken); + } + } + } + } + free(orgline); + line = NULL; + // get the binary data + if(part_name != NULL) + { + // go to the beginer of data bock + while((line = read_line(rq->client)) && strcmp(line,"\r\n") != 0) + { + free(line); + line = NULL; + } + if(line) + { + free(line); + line = NULL; + } + if(part_file == NULL) + { + /** + * This allow only 1024 bytes of data (max), + * out of this range, the data is cut out. + * Need an efficient way to handle this + */ + line = read_line(rq->client); + trim(line,'\n'); + trim(line,'\r'); + trim(line,' '); + dput(dic,part_name,line); + // find the next boundary + while((line = read_line(rq->client)) && strstr(line,boundary) <= 0) + { + free(line); + line = NULL; + } + } + else + { + file_path = __s("%s%s.%u",server_config.tmpdir,part_file,(unsigned)time(NULL)); + fp=fopen(file_path, "wb"); + if(fp) + { + int totalsize=0,len=0; + //read until the next boundary + // TODO: this is not efficient for big file + // need a solution + while((len = read_buf(rq->client,buf,sizeof(buf))) > 0 && strstr(buf,boundary) <= 0) + { + fwrite(buf, len, 1, fp); + totalsize += len; + } + //remove \r\n at the end + fseek(fp, 0, SEEK_SET); + //fseek(fp,-2, SEEK_CUR); + totalsize -= 2; + ftruncate(fileno(fp),totalsize); + fclose(fp); + line = strdup(buf); + + field = __s("%s.file",part_name); + dput(dic,field, strdup(part_file)); + free(field); + field = __s("%s.tmp",part_name); + dput(dic,field,strdup(file_path)); + free(field); + field = __s("%s.size",part_name); + dput(dic,field,__s("%d",totalsize)); + free(field); + field = __s("%s.ext",part_name); + dput(dic,field,ext(part_file)); + free(field); + + } + else + { + LOG("Cannot wirte file to :%s\n", file_path ); + } + free(file_path); + free(part_file); + } + free(part_name); + } + //printf("[Lines]:%s\n",line); + // check if end of request + if(line&&strstr(line,boundend)>0) + { + LOG("End request %s\n", boundend); + task->handle = resolve_request; + free(line); + free(boundend); + return task; + } + if(line && strstr(line,boundary) > 0) + { + // continue upload + task->type = HEAVY; + task->handle = decode_multi_part_request_data; + } + free(line); + free(boundend); + return task; } /** * Decode a query string (GET request or POST URL encoded) to @@ -918,12 +960,26 @@ void decode_url_request(const char* query, dictionary dic) char* post_data_decode(void* client,int len) { char *query = (char*) malloc((len+1)*sizeof(char)); - for (int i = 0; i < len; i++) { - antd_recv(client, (query+i), 1); - } - query[len]='\0'; - //query = url_decode(query); - //LOG("JSON Query %s\n", query); + char* ptr = query; + int readlen = len > BUFFLEN?BUFFLEN:len; + int read = 0, stat = 1; + while(readlen > 0 && stat > 0) + { + stat = antd_recv(client, ptr+read, readlen); + if(stat > 0) + { + read += stat; + readlen = (len - read) > BUFFLEN?BUFFLEN:(len-read); + } + } + + if(read > 0) + query[read]='\0'; + else + { + free(query); + query = NULL; + } return query; } @@ -941,11 +997,11 @@ char* post_data_decode(void* client,int len) * @return -1 if failure * 1 if sucess */ -int execute_plugin(void* client, const char *path, const char *method, dictionary dic) +void* execute_plugin(void* data, const char *path) { char pname[255]; char pfunc[255]; - void (*fn)(void*, const char*,const char*, dictionary); + void* (*fn)(void*); struct plugin_entry *plugin ; int plen = strlen(path); char * rpath = (char*) malloc((plen+1)*sizeof(char)); @@ -955,6 +1011,9 @@ int execute_plugin(void* client, const char *path, const char *method, dictionar rpath[plen] = '\0'; trim(rpath,'/'); char * delim = strchr(rpath,'/'); + antd_request_t* rq = (antd_request_t*) data; + antd_task_t* task = antd_create_task(NULL, (void*)rq, NULL); + task->priority++; if(delim == NULL) { strcpy(pname,rpath); @@ -970,9 +1029,8 @@ int execute_plugin(void* client, const char *path, const char *method, dictionar memcpy(pfunc,rpath+npos+1,fpos); pfunc[fpos-1]='\0'; } - LOG("Client %d\n",((antd_client_t*)client)->sock ); + LOG("Client %d\n",((antd_client_t*)rq->client)->sock ); LOG("Path : '%s'\n", rpath); - LOG("Method:%s\n", method); LOG("Plugin name '%s'\n",pname); LOG("Query path. '%s'\n", pfunc); //LOG("query :%s\n", query_string); @@ -986,22 +1044,23 @@ int execute_plugin(void* client, const char *path, const char *method, dictionar if( plugin == NULL) { if(orgs) free(orgs); - return -1; + unknow(rq->client); + return task; } } // load the function - fn = (void (*)(void*, const char *, const char*, dictionary))dlsym(plugin->handle, PLUGIN_HANDLER); + fn = (void* (*)(void*))dlsym(plugin->handle, PLUGIN_HANDLER); if ((error = dlerror()) != NULL) { if(orgs) free(orgs); LOG("Problem when finding %s method from %s : %s \n", PLUGIN_HANDLER, pname,error); - return -1; + unknow(rq->client); + return task; } - //dictionary dic = decode_request(client,method,query_string); - (*fn)(client,method,pfunc,dic); - //free(dic); + task->type = HEAVY; + task->handle = fn; free(orgs); - return 1; + return task; } #ifdef USE_OPENSSL diff --git a/http_server.h b/http_server.h index 3636fd9..ed54a86 100644 --- a/http_server.h +++ b/http_server.h @@ -7,29 +7,28 @@ #include #include #include +#include #include "libs/handle.h" +#include "libs/scheduler.h" #include "plugin_manager.h" #define FORM_URL_ENCODE "application/x-www-form-urlencoded" #define FORM_MULTI_PART "multipart/form-data" #define PLUGIN_HANDLER "handle" #define WS_MAGIC_STRING "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" - - -#define ISspace(x) isspace((int)(x)) - -#define SERVER_STRING "Server: ant-httpd" +#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 #define CONFIG "config.ini" -extern config_t server_config; -void accept_request(void*); +config_t* config(); +void destroy_config(); +void* accept_request(void*); +void* finish_request(void*); void cat(void*, FILE *); void cannot_execute(void*); -void error_die(const char *); //int get_line(int, char *, int); void not_found(void*); -void serve_file(void*, const char *); +void* serve_file(void*); int startup(unsigned *); void unimplemented(void*); void badrequest(void*); @@ -37,12 +36,15 @@ int rule_check(const char*, const char*, const char* , const char* , const char* void ws_confirm_request(void*, const char*); char* post_url_decode(void* client,int len); void decode_url_request(const char* query, dictionary); -dictionary decode_request(void* client,const char* method, char* url); -void decode_multi_part_request(void*,const char*, dictionary); +void* decode_request_header(void* data); +void* decode_request(void* data); +void* decode_post_request(void* data); +void* resolve_request(void* data); +void* decode_multi_part_request(void*,const char*, dictionary); +void* decode_multi_part_request_data(void* data); dictionary decode_cookie(const char*); char* post_data_decode(void*,int); - -int execute_plugin(void* client, const char *path, - const char *method, dictionary rq); +void set_nonblock(int); +void* execute_plugin(void* data, const char *path); #endif \ No newline at end of file diff --git a/httpd.c b/httpd.c index 7a5bc15..54200ca 100644 --- a/httpd.c +++ b/httpd.c @@ -2,9 +2,9 @@ #include #include "http_server.h" #include "libs/ini.h" +static antd_scheduler_t scheduler; +static int server_sock = -1; -#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 -int server_sock = -1; #ifdef USE_OPENSSL static int ssl_session_ctx_id = 1; SSL_CTX *ctx; @@ -53,13 +53,13 @@ void configure_context(SSL_CTX *ctx) SSL_CTX_set_session_id_context(ctx, (void *)&ssl_session_ctx_id, sizeof(ssl_session_ctx_id)); /* Set the key and cert */ /* use the full chain bundle of certificate */ - //if (SSL_CTX_use_certificate_file(ctx, server_config.sslcert, SSL_FILETYPE_PEM) <= 0) { - if (SSL_CTX_use_certificate_chain_file(ctx, server_config.sslcert) <= 0) { + //if (SSL_CTX_use_certificate_file(ctx, server_config->sslcert, SSL_FILETYPE_PEM) <= 0) { + if (SSL_CTX_use_certificate_chain_file(ctx, config()->sslcert) <= 0) { ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); } - if (SSL_CTX_use_PrivateKey_file(ctx, server_config.sslkey, SSL_FILETYPE_PEM) <= 0 ) { + if (SSL_CTX_use_PrivateKey_file(ctx, config()->sslkey, SSL_FILETYPE_PEM) <= 0 ) { ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); } @@ -72,117 +72,23 @@ void configure_context(SSL_CTX *ctx) #endif -static int config_handler(void* conf, const char* section, const char* name, - const char* value) -{ - config_t* pconfig = (config_t*)conf; - //char * ppath = NULL; - if (MATCH("SERVER", "port")) { - pconfig->port = atoi(value); - } else if (MATCH("SERVER", "plugins")) { - pconfig->plugins_dir = strdup(value); - } else if (MATCH("SERVER", "plugins_ext")) { - pconfig->plugins_ext = strdup(value); - } else if(MATCH("SERVER", "database")) { - pconfig->db_path = strdup(value); - } else if(MATCH("SERVER", "htdocs")) { - pconfig->htdocs = strdup(value); - } else if(MATCH("SERVER", "tmpdir")) { - pconfig->tmpdir = strdup(value); - } - else if(MATCH("SERVER", "maxcon")) { - pconfig->maxcon = atoi(value); - } - else if(MATCH("SERVER", "backlog")) { - pconfig->backlog = atoi(value); - } -#ifdef USE_OPENSSL - else if(MATCH("SERVER", "ssl.enable")) { - pconfig->usessl = atoi(value); - } - else if(MATCH("SERVER", "ssl.cert")) { - pconfig->sslcert = strdup(value); - } - else if(MATCH("SERVER", "ssl.key")) { - pconfig->sslkey = strdup(value); - } -#endif - else if (strcmp(section, "RULES") == 0) - { - list_put_s(&pconfig->rules, name); - list_put_s(&pconfig->rules, value); - } - else if (strcmp(section, "FILEHANDLER") == 0) - { - dput( pconfig->handlers, name ,strdup(value)); - } - else if(strcmp(section,"AUTOSTART")==0){ - // The server section must be added before the autostart section - // auto start plugin - plugin_load(value); - } else { - return 0; /* unknown section/name, error */ - } - return 1; -} -void init_file_system() -{ - struct stat st; - if (stat(server_config.plugins_dir, &st) == -1) - mkdir(server_config.plugins_dir, 0755); - if (stat(server_config.db_path, &st) == -1) - mkdir(server_config.db_path, 0755); - if (stat(server_config.htdocs, &st) == -1) - mkdir(server_config.htdocs, 0755); - if (stat(server_config.tmpdir, &st) == -1) - mkdir(server_config.tmpdir, 0755); - else - { - removeAll(server_config.tmpdir,0); - } - -} -void load_config(const char* file) -{ - server_config.port = 8888; - server_config.plugins_dir = "plugins/"; - server_config.plugins_ext = ".dylib"; - server_config.db_path = "databases/"; - server_config.htdocs = "htdocs"; - server_config.tmpdir = "tmp"; - server_config.backlog = 100; - server_config.rules = list_init(); - server_config.handlers = dict(); - server_config.maxcon = 1000; - server_config.connection = 0; -#ifdef USE_OPENSSL - server_config.usessl = 0; - server_config.sslcert = "cert.pem"; - server_config.sslkey = "key.pem"; -#endif - if (ini_parse(file, config_handler, &server_config) < 0) { - LOG("Can't load '%s'\n. Used defaut configuration", file); - } - else - { - LOG("Using configuration : %s\n", file); -#ifdef USE_OPENSSL - LOG("SSL enable %d\n", server_config.usessl); - LOG("SSL cert %s\n", server_config.sslcert); - LOG("SSL key %s\n", server_config.sslkey); -#endif - } - init_file_system(); -} void stop_serve(int dummy) { - list_free(&(server_config.rules)); - freedict(server_config.handlers); - LOG("Unclosed connection: %d\n", server_config.connection); - unload_all_plugin(); + UNUSED(dummy); + sigset_t mask; + sigemptyset(&mask); + //Blocks the SIG_IGN signal (by adding SIG_IGN to newMask) + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGPIPE); + sigaddset(&mask, SIGABRT); + sigprocmask(SIG_BLOCK, &mask, NULL); + antd_scheduler_destroy(&scheduler); + unload_all_plugin(); + destroy_config(); #ifdef USE_OPENSSL SSL_CTX_free(ctx); #endif close(server_sock); + sigprocmask(SIG_UNBLOCK, &mask, NULL); } int main(int argc, char* argv[]) { @@ -191,11 +97,10 @@ int main(int argc, char* argv[]) load_config(CONFIG); else load_config(argv[1]); - unsigned port = server_config.port; + unsigned port = config()->port; int client_sock = -1; struct sockaddr_in client_name; socklen_t client_name_len = sizeof(client_name); - pthread_t newthread; char* client_ip = NULL; // ignore the broken PIPE error when writing //or reading to/from a closed socked connection @@ -204,7 +109,7 @@ int main(int argc, char* argv[]) signal(SIGINT, stop_serve); #ifdef USE_OPENSSL - if( server_config.usessl == 1 ) + if( config()->usessl == 1 ) { init_openssl(); ctx = create_context(); @@ -213,24 +118,24 @@ int main(int argc, char* argv[]) } #endif - server_sock = startup(&port); LOG("httpd running on port %d\n", port); - - while (1) + // default to 4 workers + antd_scheduler_init(&scheduler, config()->n_workers); + set_nonblock(server_sock); + while (scheduler.status) { - if( server_config.connection >= server_config.maxcon ) - { - LOG("Too many unclosed connection (%d). Wait for it\n", server_config.connection); - continue; - } - antd_client_t* client = (antd_client_t*)malloc(sizeof(antd_client_t)); + antd_task_schedule(&scheduler); client_sock = accept(server_sock,(struct sockaddr *)&client_name,&client_name_len); if (client_sock == -1) { - perror("Cannot accept client request\n"); + //perror("Cannot accept client request\n"); continue; } + antd_client_t* client = (antd_client_t*)malloc(sizeof(antd_client_t)); + antd_request_t* request = (antd_request_t*)malloc(sizeof(*request)); + request->client = client; + request->request = dict(); /* get the remote IP */ @@ -240,40 +145,46 @@ int main(int argc, char* argv[]) client_ip = inet_ntoa(client_name.sin_addr); client->ip = strdup(client_ip); LOG("Client IP: %s\n", client_ip); + LOG("socket: %d\n", client_sock); } //return &(((struct sockaddr_in6*)sa)->sin6_addr); /* accept_request(client_sock); */ // set timeout to socket - struct timeval timeout; - timeout.tv_sec = 20; - timeout.tv_usec = 0; + set_nonblock(client_sock); + /*struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 5000; if (setsockopt (client_sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(timeout)) < 0) perror("setsockopt failed\n"); if (setsockopt (client_sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,sizeof(timeout)) < 0) perror("setsockopt failed\n"); - + */ client->sock = client_sock; - server_config.connection++; - //LOG("Unclosed connection: %d\n", server_config.connection); + // 100 times retry connection before abort + //LOG("Unclosed connection: %d\n", server_config->connection); #ifdef USE_OPENSSL client->ssl = NULL; - if(server_config.usessl == 1) + client->status = 0; + if(config()->usessl == 1) { client->ssl = (void*)SSL_new(ctx); if(!client->ssl) continue; - SSL_set_fd((SSL*)client->ssl, client_sock); + SSL_set_fd((SSL*)client->ssl, client->sock); - if (SSL_accept((SSL*)client->ssl) <= 0) { + /*if (SSL_accept((SSL*)client->ssl) <= 0) { + LOG("EROOR accept\n"); ERR_print_errors_fp(stderr); antd_close(client); continue; - } + }*/ } #endif - if (pthread_create(&newthread , NULL,(void *(*)(void *))accept_request, (void *)client) != 0) + // create callback for the server + antd_add_task(&scheduler, antd_create_task(accept_request,(void*)request, finish_request )); + /*if (pthread_create(&newthread , NULL,(void *(*)(void *))accept_request, (void *)client) != 0) { perror("pthread_create"); antd_close(client); @@ -282,7 +193,7 @@ int main(int argc, char* argv[]) { //reclaim the stack data when thread finish pthread_detach(newthread) ; - } + }*/ //accept_request(&client); } diff --git a/libs/dictionary.c b/libs/dictionary.c index a77dcb1..89e54c0 100644 --- a/libs/dictionary.c +++ b/libs/dictionary.c @@ -52,18 +52,25 @@ association __put_el_with_key(dictionary dic, const char* key) if(dic == NULL) return NULL; if ((np = dlookup(dic,key)) == NULL) { /* not found */ np = (association) malloc(sizeof(*np)); + np->value = NULL; if (np == NULL || (np->key = strdup(key)) == NULL) return NULL; hashval = hash(key, DHASHSIZE); np->next = dic[hashval]; dic[hashval] = np; } + // found return np; } association dput(dictionary dic,const char* key, void* value) { association np = __put_el_with_key(dic,key); - if(np == NULL) return NULL; + if(np == NULL) + { + if(value) free(value); + return NULL; + } + if(np->value && value) free(np->value); np->value = value; return np; } diff --git a/libs/dictionary.h b/libs/dictionary.h index 1675df4..752001a 100644 --- a/libs/dictionary.h +++ b/libs/dictionary.h @@ -46,6 +46,5 @@ void* dvalue(dictionary, const char*); association dput(dictionary,const char*, void*); int dremove(dictionary, const char*); void freedict(dictionary); -void stest(const char* ); #endif \ No newline at end of file diff --git a/libs/handle.c b/libs/handle.c index e4becdd..0a08f6f 100644 --- a/libs/handle.c +++ b/libs/handle.c @@ -1,5 +1,4 @@ -#include "handle.h" -config_t server_config; +#include "handle.h" #ifdef USE_OPENSSL int usessl() { @@ -96,18 +95,112 @@ int antd_send(void *src, const void* data, int len) int antd_recv(void *src, void* data, int len) { if(!src) return -1; - int ret; + int read; antd_client_t * source = (antd_client_t *) src; #ifdef USE_OPENSSL if(usessl()) { - //LOG("SSL READ\n"); - ret = SSL_read((SSL*) source->ssl, data, len); + int received; + char* ptr = (char* )data; + int readlen = len > BUFFLEN?BUFFLEN:len; + read = 0; + fd_set fds; + struct timeval timeout; + while (readlen > 0) + { + received = SSL_read (source->ssl, ptr+read, readlen); + if (received > 0) + { + read += received; + readlen = (len - read) > BUFFLEN?BUFFLEN:(len-read); + } + else + { + //printf(" received equal to or less than 0\n") + int err = SSL_get_error(source->ssl, received); + switch (err) + { + case SSL_ERROR_NONE: + { + // no real error, just try again... + //LOG("SSL_ERROR_NONE \n"); + continue; + } + + case SSL_ERROR_ZERO_RETURN: + { + // peer disconnected... + //printf("SSL_ERROR_ZERO_RETURN \n"); + break; + } + + case SSL_ERROR_WANT_READ: + { + // no data available right now, wait a few seconds in case new data arrives... + //printf("SSL_ERROR_WANT_READ\n"); + + int sock = SSL_get_rfd(source->ssl); + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeout.tv_sec = 0; + timeout.tv_usec = 500; + err = select(sock+1, &fds, NULL, NULL, &timeout); + if (err == 0 || (err > 0 && FD_ISSET(sock, &fds))) + continue; // more data to read... + break; + } + + case SSL_ERROR_WANT_WRITE: + { + // socket not writable right now, wait a few seconds and try again... + //printf("SSL_ERROR_WANT_WRITE \n"); + int sock = SSL_get_wfd(source->ssl); + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeout.tv_sec = 0; + timeout.tv_usec = 500; + + err = select(sock+1, NULL, &fds, NULL, &timeout); + if (err == 0 || (err > 0 && FD_ISSET(sock, &fds))) + continue; // can write more data now... + break; + } + + default: + { + // other error + break; + } + } + + break; + } + } + /* + int stat, r, st; + do{ + ret = SSL_read((SSL*) source->ssl, data, len); + stat = SSL_get_error((SSL*)source->ssl, r); + } while(ret == -1 && + ( + stat == SSL_ERROR_WANT_READ || + stat == SSL_ERROR_WANT_WRITE || + stat == SSL_ERROR_NONE || + (stat == SSL_ERROR_SYSCALL && r== 0 && !ERR_get_error()) + )); + if(ret == -1) + { + LOG("Problem reading %d %d %d\n", ret, stat, r); + } + //set_nonblock(source->sock); + */ } else { #endif - ret = recv(((int) source->sock), data, len, 0); + read = recv(((int) source->sock), data, len, 0); #ifdef USE_OPENSSL } #endif @@ -115,7 +208,20 @@ int antd_recv(void *src, void* data, int len) { antd_close(src); }*/ - return ret; + return read; +} +void set_nonblock(int socket) { + int flags; + flags = fcntl(socket,F_GETFL,0); + //assert(flags != -1); + fcntl(socket, F_SETFL, flags | O_NONBLOCK); +} +void set_block() +{ + int flags; + flags = fcntl(socket,F_GETFL,0); + //assert(flags != -1); + fcntl(socket, F_SETFL, flags & (~O_NONBLOCK)); } int antd_close(void* src) { @@ -133,9 +239,7 @@ int antd_close(void* src) #endif //printf("Close sock %d\n", source->sock); int ret = close(source->sock); - if(source->ip) free(source->ip); - server_config.connection--; - LOG("Remaining connection %d\n", server_config.connection); + if(source->ip) free(source->ip); free(src); src = NULL; return ret; @@ -299,8 +403,41 @@ void clear_cookie(void* client, dictionary dic) } void unknow(void* client) { - html(client); - __t(client,"404 API not found"); + set_status(client,520,"Unknown Error"); + __t(client,"Content-Type: text/html; charset=utf-8"); + response(client,""); + __t(client,"520 Unknow request"); +} +void notfound(void* client) +{ + set_status(client,404,"Not found"); + __t(client,"Content-Type: text/html; charset=utf-8"); + response(client,""); + __t(client,"Resource not found"); +} +void badrequest(void* client) +{ + set_status(client,400,"Bad request"); + __t(client,"Content-Type: text/html; charset=utf-8"); + response(client,""); + __t(client,"400 Bad request"); +} +void unimplemented(void* client) +{ + set_status(client,501,"Method Not Implemented"); + __t(client,"Content-Type: text/html"); + response(client,""); + __t(client, "Method Not Implemented"); + __t(client, ""); + __t(client, "

HTTP request method not supported."); + __t(client, ""); +} +void cannot_execute(void* client) +{ + set_status(client,500,"Internal Server Error"); + __t(client,"Content-Type: text/html"); + response(client,""); + __t(client, "

Error prohibited CGI execution."); } int ws_enable(dictionary dic) { diff --git a/libs/handle.h b/libs/handle.h index beea497..9aa1738 100644 --- a/libs/handle.h +++ b/libs/handle.h @@ -11,11 +11,12 @@ #ifdef USE_DB #include "dbhelper.h" #endif +#include #include "dictionary.h" #include "list.h" #include "ini.h" -#define SERVER_NAME "antd" +#define SERVER_NAME "Antd" #define IS_POST(method) (strcmp(method,"POST")== 0) #define IS_GET(method) (strcmp(method,"GET")== 0) #define R_STR(d,k) ((char*)dvalue(d,k)) @@ -28,6 +29,21 @@ #ifdef USE_OPENSSL int __attribute__((weak)) usessl(); #endif +//extern config_t server_config; +typedef struct{ + int sock; + void* ssl; + char* ip; +#ifdef USE_OPENSSL + int status; +#endif +} antd_client_t; + +typedef struct { + antd_client_t* client; + dictionary request; +} antd_request_t; + typedef struct { int port; @@ -41,19 +57,15 @@ typedef struct { int backlog; int maxcon; int connection; + int n_workers; #ifdef USE_OPENSSL int usessl; char* sslcert; char* sslkey; #endif }config_t; -//extern config_t server_config; -typedef struct{ - int sock; - void* ssl; - char* ip; -} antd_client_t; - +void set_nonblock(int socket); +void set_block(int socket); int response(void*, const char*); void ctype(void*,const char*); void redirect(void*,const char*); @@ -75,6 +87,9 @@ void set_status(void*,int,const char*); void clear_cookie(void*, dictionary); /*Default function for plugin*/ void unknow(void*); +void badrequest(void* client); +void unimplemented(void* client); +void notfound(void* client); int ws_enable(dictionary); char* read_line(void* sock); int read_buf(void* sock,char* buf,int i); diff --git a/libs/plugin.c b/libs/plugin.c index 24b38dc..46a47ed 100644 --- a/libs/plugin.c +++ b/libs/plugin.c @@ -1,8 +1,7 @@ #include "plugin.h" -plugin_header __plugin__; +plugin_header_t __plugin__; // private function -call __init__; void __init_plugin__(const char* pl,config_t* conf){ __plugin__.name = strdup(pl); __plugin__.dbpath= strdup(conf->db_path); @@ -12,7 +11,7 @@ void __init_plugin__(const char* pl,config_t* conf){ #ifdef USE_OPENSSL __plugin__.usessl = conf->usessl; #endif - if(__init__ != NULL) __init__(); + init(); }; #ifdef USE_DB @@ -41,7 +40,10 @@ int usessl() return __plugin__.usessl; } #endif*/ - +plugin_header_t* meta() +{ + return &__plugin__; +} char* route(const char* repath) { int len = strlen(__plugin__.name) + 2; @@ -75,9 +77,10 @@ char* config_dir() return path; } -void __release() +void __release__() { - printf("Releasing plugin\n"); + destroy(); + LOG("Releasing plugin\n"); if(__plugin__.name) free(__plugin__.name); if(__plugin__.dbpath) free(__plugin__.dbpath); if(__plugin__.htdocs) free(__plugin__.htdocs); diff --git a/libs/plugin.h b/libs/plugin.h index a51dbf0..468dad0 100644 --- a/libs/plugin.h +++ b/libs/plugin.h @@ -5,6 +5,7 @@ #include "dbhelper.h" #endif #include "ws.h" +#include "scheduler.h" typedef struct { char *name; @@ -15,16 +16,14 @@ typedef struct { #ifdef USE_OPENSSL int usessl; #endif -} plugin_header; +} plugin_header_t; -typedef void(*call)(); +//typedef void(*call)(); #ifdef USE_DB typedef sqlite3* sqldb; #endif -extern plugin_header __plugin__; -extern call __init__; #ifdef USE_DB @@ -36,6 +35,9 @@ char* route(const char*); char* htdocs(const char*); char* config_dir(); /*Default function for plugin*/ -void handle(void*, const char*,const char*,dictionary); -void __release(); +// init the plugin +void init(); +void destroy(); +void* handle(void*); +plugin_header_t* meta(); #endif diff --git a/libs/scheduler.c b/libs/scheduler.c new file mode 100644 index 0000000..7d59ee8 --- /dev/null +++ b/libs/scheduler.c @@ -0,0 +1,292 @@ +#include "scheduler.h" + +static void enqueue(antd_task_queue_t* q, antd_task_t* task) +{ + antd_task_item_t it = *q; + while(it && it->next != NULL) + it = it->next; + antd_task_item_t taski = (antd_task_item_t)malloc(sizeof *taski); + taski->task = task; + taski->next = NULL; + if(!it) // first task + { + *q = taski; + } + else + { + it->next = taski; + } +} + + +static void stop(antd_scheduler_t* scheduler) +{ + scheduler->status = 0; + for (int i = 0; i < scheduler->n_workers; i++) + pthread_join(scheduler->workers[i], NULL); + if(scheduler->workers) free(scheduler->workers); + // destroy all the mutex + pthread_mutex_destroy(&scheduler->scheduler_lock); + pthread_mutex_destroy(&scheduler->worker_lock); + pthread_mutex_destroy(&scheduler->pending_lock); +} + +static antd_task_item_t dequeue(antd_task_queue_t* q) +{ + antd_task_item_t it = *q; + if(it) + { + *q = it->next; + it->next = NULL; + } + return it; +} + + +static antd_callback_t* callback_of( void* (*callback)(void*) ) +{ + antd_callback_t* cb = NULL; + if(callback) + { + cb = (antd_callback_t*)malloc(sizeof *cb); + cb->handle = callback; + cb->next = NULL; + } + return cb; +} + +static void free_callback(antd_callback_t* cb) +{ + antd_callback_t* it = cb; + antd_callback_t* curr; + while(it) + { + curr = it; + it = it->next; + free(curr); + } +} + +static void enqueue_callback(antd_callback_t* cb, antd_callback_t* el) +{ + antd_callback_t* it = cb; + while(it && it->next != NULL) + it = it->next; + if(!it) return; // this should not happend + it->next = el; +} + +static void execute_callback(antd_scheduler_t* scheduler, antd_task_t* task) +{ + antd_callback_t* cb = task->callback; + if(cb) + { + // call the first come call back + task->handle = cb->handle; + task->callback = task->callback->next; + free(cb); + antd_add_task(scheduler, task); + } + else + { + free(task); + } +} + +static void destroy_queue(antd_task_queue_t q) +{ + antd_task_item_t it, curr; + it = q; + while(it) + { + // first free the task + if(it->task && it->task->callback) free_callback(it->task->callback); + if(it->task) free(it->task); + // then free the placeholder + curr = it; + it = it->next; + free(curr); + } +} +static void work(antd_scheduler_t* scheduler) +{ + while(scheduler->status) + { + antd_task_item_t it; + pthread_mutex_lock(&scheduler->worker_lock); + it = dequeue(&scheduler->workers_queue); + pthread_mutex_unlock(&scheduler->worker_lock); + // execute the task + //LOG("task executed by worker %d\n", worker->pid); + antd_execute_task(scheduler, it); + } +} + +/* + Main API methods + init the main scheduler +*/ + +void antd_scheduler_init(antd_scheduler_t* scheduler, int n) +{ + scheduler->n_workers = n; + scheduler->status = 1; + scheduler->workers_queue = NULL; + scheduler->pending_task = 0 ; + // init lock + pthread_mutex_init(&scheduler->scheduler_lock,NULL); + pthread_mutex_init(&scheduler->worker_lock, NULL); + pthread_mutex_init(&scheduler->pending_lock, NULL); + for(int i = 0; i < N_PRIORITY; i++) scheduler->task_queue[i] = NULL; + // create scheduler.workers + if(n > 0) + { + scheduler->workers = (pthread_t*)malloc(n*(sizeof(pthread_t))); + if(!scheduler->workers) + { + LOG("Cannot allocate memory for worker\n"); + exit(-1); + } + for(int i = 0; i < scheduler->n_workers;i++) + { + if (pthread_create(&scheduler->workers[i], NULL,(void *(*)(void *))work, (void*)scheduler) != 0) + { + perror("pthread_create: cannot create worker\n"); + } + } + } + LOG("Antd scheduler initialized with %d worker\n", scheduler->n_workers); +} +/* + destroy all pending task + pthread_mutex_lock(&scheduler.queue_lock); +*/ +void antd_scheduler_destroy(antd_scheduler_t* scheduler) +{ + // free all the chains + stop(scheduler); + LOG("Destroy remaining queue\n"); + for(int i=0; i < N_PRIORITY; i++) + { + destroy_queue(scheduler->task_queue[i]); + } + destroy_queue(scheduler->workers_queue); +} + +/* + create a task +*/ +antd_task_t* antd_create_task(void* (*handle)(void*), void *data, void* (*callback)(void*)) +{ + antd_task_t* task = (antd_task_t*)malloc(sizeof *task); + task->stamp = (unsigned long)time(NULL); + task->data = data; + task->handle = handle; + task->callback = callback_of(callback); + task->priority = NORMAL_PRIORITY; + task->type = LIGHT; + return task; +} + +/* + scheduling a task +*/ +void antd_add_task(antd_scheduler_t* scheduler, antd_task_t* task) +{ + // check if task is exist + int prio = task->priority>N_PRIORITY-1?N_PRIORITY-1:task->priority; + //LOG("Prio is %d\n", prio); + pthread_mutex_lock(&scheduler->scheduler_lock); + enqueue(&scheduler->task_queue[prio], task); + pthread_mutex_unlock(&scheduler->scheduler_lock); + pthread_mutex_lock(&scheduler->pending_lock); + scheduler->pending_task++; + pthread_mutex_unlock(&scheduler->pending_lock); +} + + +void antd_execute_task(antd_scheduler_t* scheduler, antd_task_item_t taski) +{ + if(!taski) + return; + // execute the task + void *ret = (*(taski->task->handle))(taski->task->data); + // check the return data if it is a new task + if(!ret) + { + // call the first callback + execute_callback(scheduler, taski->task); + free(taski); + } + else + { + antd_task_t* rtask = (antd_task_t*) ret; + if(taski->task->callback) + { + if(rtask->callback) + { + enqueue_callback(rtask->callback, taski->task->callback); + } + else + { + rtask->callback = taski->task->callback; + } + } + if(!rtask->handle) + { + // call the first callback + execute_callback(scheduler, rtask); + free(taski->task); + free(taski); + } + else + { + antd_add_task(scheduler, rtask); + free(taski->task); + free(taski); + } + } + pthread_mutex_lock(&scheduler->pending_lock); + scheduler->pending_task--; + pthread_mutex_unlock(&scheduler->pending_lock); +} + +int antd_scheduler_busy(antd_scheduler_t* scheduler) +{ + return scheduler->pending_task != 0; +} + +void antd_task_schedule(antd_scheduler_t* scheduler) +{ + // fetch next task from the task_queue + antd_task_item_t it = NULL; + pthread_mutex_lock(&scheduler->scheduler_lock); + for(int i = 0; i< N_PRIORITY; i++) + { + + it = dequeue(&scheduler->task_queue[i]); + if(it) + break; + } + pthread_mutex_unlock(&scheduler->scheduler_lock); + if(!it) + { + return; + } + // has the task now + // check the type of tas + if(it->task->type == LIGHT || scheduler->n_workers <= 0) + { + // do it by myself + antd_execute_task( scheduler, it); + } + else + { + // delegate to other workers by + //pushing to the worker queue + pthread_mutex_lock(&scheduler->worker_lock); + enqueue(&scheduler->workers_queue, it->task); + pthread_mutex_unlock(&scheduler->worker_lock); + free(it); + } +} \ No newline at end of file diff --git a/libs/scheduler.h b/libs/scheduler.h new file mode 100644 index 0000000..4c9747d --- /dev/null +++ b/libs/scheduler.h @@ -0,0 +1,96 @@ +#ifndef ANT_SCHEDULER +#define ANT_SCHEDULER + +#include "utils.h" +#include + +#define N_PRIORITY 10 +#define NORMAL_PRIORITY ((int)((N_PRIORITY - 1) / 2)) +#define LOW_PRIORITY (N_PRIORITY - 1) +#define HIGH_PRIORITY 0 + +typedef enum { LIGHT, HEAVY } antd_task_type_t; +// callback definition +typedef struct __callback_t{ + void* (*handle)(void*); + struct __callback_t * next; +} antd_callback_t; +// task definition +typedef struct { + /* + creation time of a task + */ + unsigned long stamp; + /* + priority from 0 to N_PRIORITY - 1 + higher value is lower priority + */ + uint8_t priority; + /* + the callback + */ + void* (*handle)(void*); + antd_callback_t* callback; + /* + user data if any + */ + void * data; + /* + type of a task + light tasks are executed directly + heavy tasks are delegated to workers + */ + antd_task_type_t type; +} antd_task_t; + + +typedef struct __task_item_t{ + antd_task_t* task; + struct __task_item_t* next; +}* antd_task_item_t; + +typedef antd_task_item_t antd_task_queue_t; + +typedef struct { + pthread_mutex_t scheduler_lock; + pthread_mutex_t worker_lock; + pthread_mutex_t pending_lock; + antd_task_queue_t task_queue[N_PRIORITY]; + antd_task_queue_t workers_queue; + uint8_t status; // 0 stop, 1 working + pthread_t* workers; + int n_workers; + int pending_task; +} antd_scheduler_t; + +/* + init the main scheduler +*/ +void antd_scheduler_init(antd_scheduler_t*, int); +/* + destroy all pending task +*/ +void antd_scheduler_destroy(antd_scheduler_t*); + +/* + create a task +*/ +antd_task_t* antd_create_task(void* (*handle)(void*), void *data, void* (*callback)(void*)); + +/* + add a task +*/ +void antd_add_task(antd_scheduler_t*, antd_task_t*); +/* + execute and free a task a task +*/ +void antd_execute_task(antd_scheduler_t*, antd_task_item_t); +/* + scheduler status +*/ +int antd_scheduler_busy(antd_scheduler_t*); +/* + schedule a task +*/ +void antd_task_schedule(antd_scheduler_t*); +#endif \ No newline at end of file diff --git a/libs/utils.c b/libs/utils.c index 7ecef2b..aa34951 100644 --- a/libs/utils.c +++ b/libs/utils.c @@ -145,7 +145,11 @@ char* ext(const char* file) if(file == NULL) return NULL; char* str_cpy = strdup(file); char* str_org = str_cpy; - if(strstr(str_cpy,".")<= 0) return strdup(""); + if(strstr(str_cpy,".")<= 0) + { + free(str_org); + return NULL; + } if(*file == '.') trim(str_cpy,'.'); @@ -184,6 +188,7 @@ mime_t mime_from_type(const char* type) char* mime(const char* file) { char * ex = ext(file); + if(!ex) return "application/octet-stream"; mime_t m = mime_from_ext(ex); if(ex) free(ex); diff --git a/libs/utils.h b/libs/utils.h index 139df74..c9b8a3d 100644 --- a/libs/utils.h +++ b/libs/utils.h @@ -58,6 +58,9 @@ THE SOFTWARE. #else #define LOG(a,...) do{}while(0) #endif +// add this to the utils +#define UNUSED(x) (void)(x) + #define BUFFLEN 1024 #define HASHSIZE 1024 #define DHASHSIZE 50 diff --git a/plugin_manager.c b/plugin_manager.c index 1c959b9..a6e2b57 100644 --- a/plugin_manager.c +++ b/plugin_manager.c @@ -31,10 +31,16 @@ struct plugin_entry *plugin_load(char *name) if ((np = plugin_lookup(name)) == NULL) { /* not found */ np = (struct plugin_entry *) malloc(sizeof(*np)); if (np == NULL || (np->pname = strdup(name)) == NULL) - return NULL; + { + if(np) free(np); + return NULL; + } if ((np->handle = plugin_from_file(name)) == NULL) + { + if(np) free(np); return NULL; - hashval = hash(name,HASHSIZE); + } + hashval = hash(name,HASHSIZE); np->next = plugin_table[hashval]; plugin_table[hashval] = np; } else /* already there */ @@ -53,7 +59,7 @@ void * plugin_from_file(char* name) { void *lib_handle; char* error; - char* path = __s("%s%s%s",server_config.plugins_dir,name,server_config.plugins_ext); + char* path = __s("%s%s%s",config()->plugins_dir,name,config()->plugins_ext); void (*fn)(const char*, config_t*); lib_handle = dlopen(path, RTLD_LAZY); if (!lib_handle) @@ -68,7 +74,7 @@ void * plugin_from_file(char* name) if ((error = dlerror()) != NULL) LOG("Problem when setting data path for %s : %s \n", name,error); else - (*fn)(name,&server_config); + (*fn)(name,config()); if(path) free(path); return lib_handle; @@ -79,17 +85,7 @@ void unload_plugin(struct plugin_entry* np) char* error; void (*fn)() = NULL; // find and execute the exit function - fn = (void (*)())dlsym(np->handle, "pexit"); - if ((error = dlerror()) != NULL) - { - LOG("Cant not find exit method from %s : %s \n", np->pname,error); - } - else - { - // execute it - (*fn)(); - } - fn = (void(*)()) dlsym(np->handle, "__release"); + fn = (void(*)()) dlsym(np->handle, "__release__"); if ((error = dlerror()) != NULL) { LOG("Cant not release plugin %s : %s \n", np->pname,error); diff --git a/plugin_manager.h b/plugin_manager.h index 466464f..cd88530 100644 --- a/plugin_manager.h +++ b/plugin_manager.h @@ -3,13 +3,12 @@ #include #include "libs/utils.h" #include "libs/handle.h" - +#include "http_server.h" struct plugin_entry { struct plugin_entry *next; char *pname; void *handle; }; -extern config_t server_config; /* lookup: look for s in hashtab */ struct plugin_entry *plugin_lookup(char *s); /* install: put (name, defn) in hashtab */ diff --git a/relay.c b/relay.c new file mode 100644 index 0000000..0fe8327 --- /dev/null +++ b/relay.c @@ -0,0 +1,128 @@ +#include "http_server.h" +#include "libs/scheduler.h" +#include +static antd_scheduler_t scheduler; +/* +this node is a relay from the http +to https +*/ +#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 +int server_sock = -1; +void stop_serve(int dummy) { + UNUSED(dummy); + antd_scheduler_destroy(&scheduler); + close(server_sock); +} +/* +HTTP/1.1 301 Moved Permanently +Location: http://www.example.org/ +Content-Type: text/html +Content-Length: 174 +*/ +void* antd_redirect(void* user_data) +{ + void** data = (void**)user_data; + void* client = data[0]; + char* host = (char*)data[1]; + __t(client,"%s", "HTTP/1.1 301 Moved Permanently"); + __t(client, "Location: https://%s", host); + __t(client, "%s", "Content-Type: text/html"); + __t(client, ""); + __t(client, "This page has moved to https://%s", host); + free(host); + free(user_data); + return antd_create_task(NULL,client, NULL); +} + +void* antd_free_client(void* client) +{ + antd_client_t * source = (antd_client_t *) client; + if(source->ip) free(source->ip); + close(source->sock); + free(client); + return NULL; +} + +void* antd_get_host(void * client) +{ + char buff[1024]; + char* line, *token; + char* host = NULL; + while((read_buf(client,buff,sizeof(buff))) && strcmp("\r\n",buff)) + { + line = buff; + trim(line, '\n'); + trim(line, '\r'); + token = strsep(&line, ":"); + trim(token,' '); + trim(line,' '); + if(token && strcasecmp(token,"Host")==0) + if(line) + { + host = strdup(line); + break; + } + } + if(!host) host = strdup("lxsang.me"); + void** data = (void**)malloc(2*(sizeof *data)); + data[0] = client; + data[1] = (void*)host; + LOG("[%s] Request for: %s --> https://%s\n", ((antd_client_t*)client)->ip, host, host); + return antd_create_task(antd_redirect,data, NULL); +} + +int main(int argc, char* argv[]) +{ + UNUSED(argc); + UNUSED(argv); +// load the config first + unsigned port = 80; + int client_sock = -1; + struct sockaddr_in client_name; + socklen_t client_name_len = sizeof(client_name); + // ignore the broken PIPE error when writing + //or reading to/from a closed socked connection + signal(SIGPIPE, SIG_IGN); + signal(SIGABRT, SIG_IGN); + signal(SIGINT, stop_serve); + server_sock = startup(&port); + //struct timeval timeout; + //timeout.tv_sec = 0; + //timeout.tv_usec = 500; + // 0 worker + antd_scheduler_init(&scheduler, 0); + // set server socket to non blocking + fcntl(server_sock, F_SETFL, O_NONBLOCK); /* Change the socket into non-blocking state */ + LOG("relayd running on port %d\n", port); + + while (scheduler.status) + { + // execute task + antd_task_schedule(&scheduler); + client_sock = accept(server_sock,(struct sockaddr *)&client_name,&client_name_len); + if (client_sock == -1) + { + continue; + } + antd_client_t* client = (antd_client_t*)malloc(sizeof(antd_client_t)); + // set timeout to socket + + //if (setsockopt (client_sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(timeout)) < 0) + // perror("setsockopt failed\n"); + + //if (setsockopt (client_sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,sizeof(timeout)) < 0) + // perror("setsockopt failed\n"); + + /* + get the remote IP + */ + client->ip = NULL; + if (client_name.sin_family == AF_INET) + client->ip = strdup(inet_ntoa(client_name.sin_addr)); + client->sock = client_sock; + //accept_request(&client); + antd_add_task(&scheduler, antd_create_task(antd_get_host,(void*)client, antd_free_client )); + } + + return(0); +} \ No newline at end of file