diff --git a/antd-config.ini b/antd-config.ini index 575894b..588b4e5 100644 --- a/antd-config.ini +++ b/antd-config.ini @@ -30,6 +30,10 @@ ssl.cert=/opt/www/server.crt ssl.key=/opt/www/server.key ssl.cipher=ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256 +; enable compression +gzip_enable = 1 +gzip_types = text\/.*,.*\/css,.*\/json,.*\/javascript + ; a configuration each port [PORT:443] diff --git a/configure.ac b/configure.ac index e467166..dc7c3c9 100644 --- a/configure.ac +++ b/configure.ac @@ -50,6 +50,22 @@ AC_CHECK_LIB([dl], [dlopen], [], [ AC_MSG_ERROR([unable to find dlopen()]) ]) + +# check for zlib +use_zlib=no +# check if libssl header exists +AC_CHECK_HEADER([zlib.h],[ + # check if the library exists + AC_DEFINE([USE_ZLIB], [1],[Use zlib]) + use_zlib=yes +], []) +AC_CHECK_LIB([z],[deflate],[], [ + if test "$use_zlib" = "yes"; then + AC_MSG_ERROR([Unable to find zlib shared library]) + fi +]) + + AC_DEFINE([_GNU_SOURCE], [1],[Use GNU source]) # AC_CANONICAL_HOST is needed to access the 'host_os' variable diff --git a/http_server.c b/http_server.c index 1782dc4..1236649 100644 --- a/http_server.c +++ b/http_server.c @@ -85,6 +85,8 @@ void destroy_config() free(server_config.tmpdir); if(server_config.ssl_cipher) free(server_config.ssl_cipher); + if(server_config.gzip_types) + list_free(&server_config.gzip_types); if(server_config.errorfp) { fclose(server_config.errorfp); @@ -162,6 +164,16 @@ static int config_handler(void *conf, const char *section, const char *name, { pconfig->errorfp = fopen(value, "w"); } +#ifdef USE_ZLIB + else if (MATCH("SERVER", "gzip_enable")) + { + pconfig->gzip_enable = atoi(value); + } + else if (MATCH("SERVER", "gzip_types")) + { + pconfig->gzip_types = split(value,","); + } +#endif #ifdef DEBUG else if (MATCH("SERVER", "server_log")) { @@ -279,6 +291,8 @@ void load_config(const char *file) server_config.sslcert = "cert.pem"; server_config.sslkey = "key.pem"; server_config.ssl_cipher = NULL; + server_config.gzip_enable = 0; + server_config.gzip_types = NULL; // put it default mimes for(int i = 0; _mimes[i].type != NULL; i++) { @@ -349,7 +363,7 @@ void *accept_request(void *data) // perform the ssl handshake if enabled #ifdef USE_OPENSSL int ret = -1, stat; - if (client->port_config->usessl == 1 && client->status == 0) + if (client->ssl && client->status == 0) { //LOG("Atttempt %d\n", client->attempt); if (SSL_accept((SSL *)client->ssl) == -1) @@ -451,7 +465,7 @@ void *resolve_request(void *data) char *newurl = NULL; char *rqp = NULL; char *oldrqp = NULL; - strcpy(path, rq->client->port_config->htdocs); + htdocs(rq, path); strcat(path, url); //LOG("Path is : %s", path); //if (path[strlen(path) - 1] == '/') @@ -484,7 +498,7 @@ void *resolve_request(void *data) { newurl = __s("%s/index.%s", url, it->key); memset(path, 0, sizeof(path)); - strcat(path, rq->client->port_config->htdocs); + htdocs(rq,path); strcat(path, newurl); if (stat(path, &st) != 0) { @@ -775,15 +789,12 @@ void *decode_request_header(void *data) char *host = NULL; char buf[2 * BUFFLEN]; char *url = (char *)dvalue(rq->request, "REQUEST_QUERY"); - dictionary_t xheader = dict(); - dictionary_t request = dict(); - dput(rq->request, "REQUEST_HEADER", xheader); - dput(rq->request, "REQUEST_DATA", request); + dictionary_t xheader = dvalue(rq->request, "REQUEST_HEADER"); + dictionary_t request = dvalue(rq->request, "REQUEST_DATA"); + char* port_s = (char*) dvalue(xheader, "SERVER_PORT"); + port_config_t* pcnf = (port_config_t*)dvalue(server_config.ports, port_s); // first real all header // this for check if web socket is enabled - // ip address - dput(xheader, "REMOTE_ADDR", (void *)strdup(((antd_client_t *)rq->client)->ip)); - dput(xheader, "SERVER_PORT", (void *)__s("%d", ((antd_client_t *)rq->client)->port_config->port)); while ((read_buf(rq->client, buf, sizeof(buf))) && strcmp("\r\n", buf)) { line = buf; @@ -796,8 +807,11 @@ void *decode_request_header(void *data) dput(xheader, token, strdup(line)); if (token != NULL && strcasecmp(token, "Cookie") == 0) { - if (!cookie) - cookie = decode_cookie(line); + if(!cookie) + { + cookie = dict(); + } + decode_cookie(line, cookie); } else if (token != NULL && strcasecmp(token, "Host") == 0) { @@ -818,11 +832,35 @@ void *decode_request_header(void *data) return antd_create_task(NULL, (void *)rq, NULL,rq->client->last_io);; } } + +#ifdef USE_ZLIB + // check for gzip + line = (char *)dvalue(xheader, "Accept-Encoding"); + if(line) + { + if(regex_match("gzip",line,0, NULL)) + { + rq->client->z_level = ANTD_CGZ; + } + else if(regex_match("deflate", line, 0, NULL)) + { + rq->client->z_level = ANTD_CDEFL; + } + else + { + rq->client->z_level = ANTD_CNONE; + } + } + else + { + rq->client->z_level = ANTD_CNONE; + } +#endif //if(line) free(line); memset(buf, 0, sizeof(buf)); strcat(buf, url); LOG("Original query: %s", url); - query = apply_rules(rq->client->port_config->rules, host, buf); + query = apply_rules(pcnf->rules, host, buf); LOG("Processed query: %s", query); dput(rq->request, "RESOURCE_PATH", url_decode(buf)); if (query) @@ -982,7 +1020,7 @@ void ws_confirm_request(void *client, const char *key) * @param client The client socket * @return The Dictionary socket or NULL */ -dictionary_t decode_cookie(const char *line) +void decode_cookie(const char *line, dictionary_t dic) { char *token, *token1; char *cpstr = strdup(line); @@ -991,20 +1029,16 @@ dictionary_t decode_cookie(const char *line) trim(cpstr, '\n'); trim(cpstr, '\r'); - dictionary_t dic = NULL; while ((token = strsep(&cpstr, ";"))) { trim(token, ' '); token1 = strsep(&token, "="); if (token1 && token && strlen(token) > 0) { - if (dic == NULL) - dic = dict(); dput(dic, token1, strdup(token)); } } free(orgcpy); - return dic; } /** * Decode the multi-part form data from the POST request @@ -1318,4 +1352,35 @@ void *execute_plugin(void *data, const char *pname) dictionary_t mimes_list() { return server_config.mimes; -} \ No newline at end of file +} + + +void dbdir(char* dest) +{ + strcpy(dest,server_config.db_path); +} +void tmpdir(char* dest) +{ + strcpy(dest, server_config.tmpdir); +} +void plugindir(char* dest) +{ + strcpy(dest, server_config.plugins_dir); +} + +#ifdef USE_ZLIB +int compressable(char* ctype) +{ + if(!server_config.gzip_enable || server_config.gzip_types == NULL) + return ANTD_CNONE; + item_t it; + list_for_each(it, server_config.gzip_types) + { + if(it->type == LIST_TYPE_POINTER && it->value.ptr && regex_match((const char*)it->value.ptr, ctype, 0, NULL)) + { + return 1; + } + } + return 0; +} +#endif \ No newline at end of file diff --git a/http_server.h b/http_server.h index 29c91cc..c5c97a4 100644 --- a/http_server.h +++ b/http_server.h @@ -39,7 +39,7 @@ void* decode_post_request(void* data); void* resolve_request(void* data); void* decode_multi_part_request(void*,const char*); void* decode_multi_part_request_data(void* data); -dictionary_t decode_cookie(const char*); +void decode_cookie(const char*, dictionary_t d); char* post_data_decode(void*,int); void set_nonblock(int); void* execute_plugin(void* data, const char *path); diff --git a/httpd.c b/httpd.c index 6c945ff..a18c07b 100644 --- a/httpd.c +++ b/httpd.c @@ -234,16 +234,23 @@ int main(int argc, char* argv[]) antd_request_t* request = (antd_request_t*)malloc(sizeof(*request)); request->client = client; request->request = dict(); - client->port_config = pcnf; + client->zstream = NULL; + client->z_level = ANTD_CNONE; + + dictionary_t xheader = dict(); + dput(request->request, "REQUEST_HEADER", xheader); + dput(request->request, "REQUEST_DATA", dict()); + dput(xheader, "SERVER_PORT", (void *)__s("%d", pcnf->port)); + dput(xheader, "SERVER_WWW_ROOT", (void*)strdup(pcnf->htdocs)); /* get the remote IP */ - client->ip = NULL; if (client_name.sin_family == AF_INET) { client_ip = inet_ntoa(client_name.sin_addr); - client->ip = strdup(client_ip); LOG("Connect to client IP: %s on port:%d", client_ip, pcnf->port); + // ip address + dput(xheader, "REMOTE_ADDR", (void *)strdup(client_ip)); //LOG("socket: %d\n", client_sock); } @@ -261,8 +268,8 @@ int main(int argc, char* argv[]) */ client->sock = client_sock; time(&client->last_io); - #ifdef USE_OPENSSL client->ssl = NULL; + #ifdef USE_OPENSSL client->status = 0; if(pcnf->usessl == 1) { diff --git a/lib/handle.c b/lib/handle.c index fe12b3c..602224c 100644 --- a/lib/handle.c +++ b/lib/handle.c @@ -76,6 +76,34 @@ int require_plugin(const char* name) return 0; } +int compressable(char* ctype) +{ + UNUSED(ctype); + return 0; +} + +void htdocs(antd_request_t* rq, char* dest) +{ + dictionary_t xheader = (dictionary_t)dvalue(rq->request, "REQUEST_HEADER"); + char* www = (char*)dvalue(xheader, "SERVER_WWW_ROOT"); + if(www) + { + strcpy(dest,www); + } +} +void dbdir(char* dest) +{ + UNUSED(dest); +} +void tmpdir(char* dest) +{ + UNUSED(dest); +} +void plugindir(char* dest) +{ + UNUSED(dest); +} + const char* get_status_str(int stat) { switch(stat) @@ -150,10 +178,72 @@ const char* get_status_str(int stat) } } -void antd_send_header(void* client, antd_response_header_t* res) +void antd_send_header(void* cl, antd_response_header_t* res) { if(!res->header) res->header = dict(); + antd_client_t* client = (antd_client_t*) cl; +#ifdef USE_ZLIB + char* str = dvalue(res->header,"Content-Encoding"); + if(!str) + { + // check for compress + str = dvalue(res->header,"Content-Type"); + if(str) + { + if(compressable(str)) + { + switch (client->z_level) + { + case ANTD_CGZ: + client->zstream = (z_stream *) malloc(sizeof(z_stream)); + if(client->zstream) + { + ((z_stream*)client->zstream)->zalloc = Z_NULL; + ((z_stream*)client->zstream)->zfree = Z_NULL; + ((z_stream*)client->zstream)->opaque = Z_NULL; + if(deflateInit2(client->zstream,Z_BEST_COMPRESSION,Z_DEFLATED,15 | 16, 8,Z_DEFAULT_STRATEGY) != Z_OK) + { + ERROR("Cannot init gzip stream"); + free(client->zstream); + client->zstream = NULL; + } + else + { + client->status = Z_NO_FLUSH; + dput(res->header,"Content-Encoding", strdup("gzip")); + } + } + break; + + case ANTD_CDEFL: + client->zstream = (z_stream *) malloc(sizeof(z_stream)); + if(client->zstream) + { + ((z_stream*)client->zstream)->zalloc = Z_NULL; + ((z_stream*)client->zstream)->zfree = Z_NULL; + ((z_stream*)client->zstream)->opaque = Z_NULL; + if(deflateInit(client->zstream, Z_BEST_COMPRESSION) != Z_OK) + { + ERROR("Cannot init deflate stream"); + free(client->zstream); + client->zstream = NULL; + } + else + { + client->status = Z_NO_FLUSH; + dput(res->header,"Content-Encoding", strdup("deflate")); + } + } + break; + + default: + break; + } + } + } + } +#endif dput(res->header,"Server", strdup(SERVER_NAME)); const char* stat_str = get_status_str(res->status); __t(client, "HTTP/1.1 %d %s", res->status, stat_str); @@ -191,16 +281,57 @@ void octstream(void* client, char* name) //Content-Disposition: attachment; filename="fname.ext" }*/ -int antd_send(void *src, const void* data, int len) +#ifdef USE_ZLIB +int zcompress(antd_client_t * cl, uint8_t* data, int len, uint8_t* dest) { - if(!src || !data) return -1; - int written; + z_stream* zstream = (z_stream*) cl->zstream; + zstream->avail_in = (uInt)len; + zstream->next_in = (Bytef *)data; + zstream->avail_out = len; + zstream->next_out = dest; + if(deflate(zstream, cl->status) == Z_STREAM_ERROR) + { + free(dest); + return -1; + } + return zstream->total_out; +} +#endif + + +int antd_send(void *src, const void* data_in, int len_in) +{ + uint8_t* data = (uint8_t*)data_in; + int len = len_in; antd_client_t * source = (antd_client_t *) src; + +#ifdef USE_ZLIB + + if(source->zstream && source->z_level != ANTD_CNONE) + { + data = (uint8_t*) malloc(len); + if(data) + { + len = zcompress(source,data_in, len, data); + } + } +#endif + + if(!src || !data) + { +#ifdef USE_ZLIB + if(source->zstream && source->z_level != ANTD_CNONE && data) + free(data); +#endif + return -1; + } + int written; char* ptr; int writelen = 0; int count; + #ifdef USE_OPENSSL - if(source->port_config->usessl) + if(source->ssl) { //LOG("SSL WRITE\n"); //ret = SSL_write((SSL*) source->ssl, data, len); @@ -334,6 +465,10 @@ int antd_send(void *src, const void* data, int len) { antd_close(src); }*/ +#ifdef USE_ZLIB + if(source->zstream && source->z_level != ANTD_CNONE && data) + free(data); +#endif return written; } int antd_recv(void *src, void* data, int len) @@ -345,7 +480,7 @@ int antd_recv(void *src, void* data, int len) int readlen=0; antd_client_t * source = (antd_client_t *) src; #ifdef USE_OPENSSL - if(source->port_config->usessl) + if(source->ssl) { ptr = (char* )data; readlen = len > BUFFLEN?BUFFLEN:len; @@ -517,8 +652,32 @@ int antd_close(void* src) { if(!src) return -1; antd_client_t * source = (antd_client_t *) src; +#ifdef USE_ZLIB + //TODO: send finish data to the socket before quit + if(source->zstream) + { + printf("Close the stream now\n"); + if(source->status == Z_NO_FLUSH) + { + uint8_t buf[512]; + // send finish data + z_stream* zstream = (z_stream*) cl->zstream; + source->status = Z_FINISH; + + zstream->avail_in = 0; + zstream->next_in = ""; + zstream->avail_out = 512; + zstream->next_out = buf; + + antd_send(source,"", 0); + deflateEnd(source->zstream); + } + deflateEnd(source->zstream); + free(source->zstream); + } +#endif #ifdef USE_OPENSSL - if(source->port_config->usessl && source->ssl){ + if(source->ssl){ //printf("SSL:Shutdown ssl\n"); //SSL_shutdown((SSL*) source->ssl); SSL_set_shutdown((SSL*) source->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); @@ -536,7 +695,6 @@ int antd_close(void* src) #endif //printf("Close sock %d\n", source->sock); int ret = close(source->sock); - if(source->ip) free(source->ip); free(src); src = NULL; return ret; diff --git a/lib/handle.h b/lib/handle.h index 9137934..98e0dec 100644 --- a/lib/handle.h +++ b/lib/handle.h @@ -9,9 +9,13 @@ #include #include #endif +#ifdef USE_ZLIB +#include +#endif #ifdef USE_DB #include "dbhelper.h" #endif + #include #include #include "dictionary.h" @@ -30,6 +34,9 @@ #define FORM_MULTI_PART "multipart/form-data" #define MAX_IO_WAIT_TIME 5 // second + +typedef enum {ANTD_CGZ, ANTD_CDEFL, ANTD_CNONE} antd_compress_t; + //extern config_t server_config; typedef struct { @@ -43,12 +50,11 @@ typedef struct { typedef struct{ int sock; void* ssl; - char* ip; -//#ifdef USE_OPENSSL int status; -//#endif time_t last_io; - port_config_t* port_config; + // compress + antd_compress_t z_level; + void* zstream; } antd_client_t; typedef struct { @@ -79,31 +85,37 @@ typedef struct { int connection; int n_workers; int max_upload_size; + // log FILE* errorfp; -// #ifdef DEBUG FILE* logfp; -// #endif -// #ifdef USE_OPENSSL + // ssl int enable_ssl; char* sslcert; char* sslkey; char* ssl_cipher; + int gzip_enable; + list_t gzip_types; dictionary_t mimes; dictionary_t ports; // #endif }config_t; typedef struct { - char *name; - char *dbpath; - char *tmpdir; - char*pdir; + char name[128]; + char dbpath[512]; + char tmpdir[512]; + char pdir[512]; int raw_body; } plugin_header_t; int __attribute__((weak)) require_plugin(const char*); +void __attribute__((weak)) htdocs(antd_request_t* rq, char* dest); +void __attribute__((weak)) dbdir(char* dest); +void __attribute__((weak)) tmpdir(char* dest); +void __attribute__((weak)) plugindir(char* dest); +int __attribute__((weak)) compressable(char* ctype); void set_nonblock(int socket); //void set_block(int socket); diff --git a/lib/list.c b/lib/list.c index 5141bbe..b64d0c1 100644 --- a/lib/list.c +++ b/lib/list.c @@ -136,9 +136,10 @@ list_t split(const char* str, const char* delim) list_t l = list_init(); while((token = strsep(&str_cpy,delim))) { + trim(token, ' '); if(strlen(token) > 0) { - list_put_special(&l,token); + list_put_special(&l, token); } } if(l->type== LIST_TYPE_NIL) diff --git a/lib/plugin.h b/lib/plugin.h index 888720a..c0ea10c 100644 --- a/lib/plugin.h +++ b/lib/plugin.h @@ -20,8 +20,6 @@ sqldb getdb(); sqldb __getdb(char *name); #endif -char* route(const char*); -char* htdocs(const char*); char* config_dir(); /*Default function for plugin*/ // init the plugin @@ -37,11 +35,11 @@ STATIC PART, should be included in any plugin #ifdef PLUGIN_IMPLEMENT static plugin_header_t __plugin__; // private function -void __init_plugin__(const char* pl,config_t* conf){ - __plugin__.name = strdup(pl); - __plugin__.dbpath= conf->db_path; - __plugin__.pdir = conf->plugins_dir; - __plugin__.tmpdir = conf->tmpdir; +void __init_plugin__(const char* pl){ + strcpy(__plugin__.name,pl); + dbdir(__plugin__.dbpath); + plugindir(__plugin__.pdir); + tmpdir(__plugin__.tmpdir); __plugin__.raw_body = 0; init(); }; @@ -72,26 +70,6 @@ plugin_header_t* meta() { return &__plugin__; } -char* route(const char* repath) -{ - int len = strlen(__plugin__.name) + 2; - if(repath != NULL) - len += strlen(repath)+1; - char * path = (char*) malloc(len*sizeof(char)); - strcpy(path,"/"); - strcat(path,__plugin__.name); - if(repath != NULL) - { - strcat(path,"/"); - strcat(path,repath); - } - return path; -} - -const char* tmpdir() -{ - return (const char*) __plugin__.tmpdir; -} char* config_dir() { @@ -105,11 +83,6 @@ char* config_dir() void __release__() { destroy(); - LOG("Releasing plugin\n"); - if(__plugin__.name) free(__plugin__.name); - //if(__plugin__.dbpath) free(__plugin__.dbpath); - //if(__plugin__.htdocs) free(__plugin__.htdocs); - //if(__plugin__.pdir) free(__plugin__.pdir); } #endif #endif diff --git a/lib/ws.c b/lib/ws.c index 830a806..9198686 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -369,14 +369,10 @@ int request_socket(const char* ip, int port) void ws_client_close(ws_client_t* wsclient) { - int usessl = wsclient->antdsock->port_config->usessl; - port_config_t *ptr = wsclient->antdsock->port_config; antd_close(wsclient->antdsock); - if(ptr) - free(ptr); #ifdef USE_OPENSSL - if(usessl) + if(wsclient->ssl_ctx) { if(wsclient->ssl_ctx) SSL_CTX_free(wsclient->ssl_ctx); @@ -389,34 +385,29 @@ void ws_client_close(ws_client_t* wsclient) // DEPRECATED: ERR_remove_state(0); ERR_free_strings(); } -#else - UNUSED(usessl); #endif } //this is for the client side, not use for now -int ws_client_connect(ws_client_t* wsclient) +int ws_client_connect(ws_client_t* wsclient, port_config_t pcnf) { char ip[100]; int stat = ip_from_hostname(wsclient->host,ip); if(stat == -1) return -1; - int sock = request_socket(ip, wsclient->antdsock->port_config->port); + int sock = request_socket(ip, pcnf.port); if(sock <= 0) { ERROR("Cannot request socket"); return -1; } // will be free - wsclient->antdsock->ip = strdup(ip); wsclient->antdsock->sock = sock; wsclient->antdsock->status = 0; wsclient->antdsock->last_io = time(NULL); - wsclient->antdsock->port_config->sock = -1; - wsclient->antdsock->port_config->rules = NULL; - wsclient->antdsock->port_config->htdocs = NULL; + wsclient->antdsock->zstream = NULL; #ifdef USE_OPENSSL - if(wsclient->antdsock->port_config->usessl) + if(pcnf.usessl) { SSL_library_init(); SSL_load_error_strings(); diff --git a/lib/ws.h b/lib/ws.h index 579d868..5675ed8 100644 --- a/lib/ws.h +++ b/lib/ws.h @@ -73,6 +73,6 @@ char* get_ip_address(); // client void ws_client_close(ws_client_t* wsclient); -int ws_client_connect(ws_client_t* wsclient); +int ws_client_connect(ws_client_t* wsclient, port_config_t pcnf); int ws_open_handshake(ws_client_t* client); #endif \ No newline at end of file diff --git a/plugin_manager.c b/plugin_manager.c index 141dda2..77020f2 100644 --- a/plugin_manager.c +++ b/plugin_manager.c @@ -66,7 +66,7 @@ void * plugin_from_file(char* name) void *lib_handle; char* error; char* path = __s("%s%s%s",config()->plugins_dir,name,config()->plugins_ext); - void (*fn)(const char*, config_t*); + void (*fn)(const char*); lib_handle = dlopen(path, RTLD_LAZY); if (!lib_handle) { @@ -76,11 +76,11 @@ void * plugin_from_file(char* name) return NULL; } // set database path - fn = (void (*)(const char *, config_t*))dlsym(lib_handle, "__init_plugin__"); + fn = (void (*)(const char *))dlsym(lib_handle, "__init_plugin__"); if ((error = dlerror()) != NULL) ERROR("Problem when finding plugin init function for %s : %s", name,error); else - (*fn)(name,config()); + (*fn)(name); if(path) free(path); return lib_handle;