src/worker/wkr_static.c in webroar-0.4.0 vs src/worker/wkr_static.c in webroar-0.5.0

- old
+ new

@@ -32,23 +32,35 @@ #endif extern config_t *Config; #define DEFAULT_EXPIRES "Headers/expires" -#define EXPIRES_BY_TYPE "Headers/expires_by_type" -#define EXPIRES_BY_TYPE_EXT "expires_by_type/ext" -#define EXPIRES_BY_TYPE_EXPIRES "expires_by_type/expires" +#define EXPIRES_BY_TYPE "Headers/expires_by_type/*" +#define EXPIRES_BY_TYPE_EXT "ext" +#define EXPIRES_BY_TYPE_EXPIRES "expires" #define HTTP_HEADER_IF_MODIFIED_SINCE "HTTP_IF_MODIFIED_SINCE" #define HTTP_HEADER_CONNECTION "HTTP_CONNECTION" +#define WR_NO_ENCODING 0 +#define WR_DEFLATE_ENCODING 1 +#define WR_GZIP_ENCODING 2 #define CONNECTION_CLOSE "Close" #define CONNECTION_KEEP_ALIVE "Keep-Alive" #define WR_MSG_QUEUE_SERVER_HOST "starling/host" #define WR_MSG_QUEUE_SERVER_PORT "starling/port" #define WR_PID_MSG_QUEUE_NAME "starling/pid_queue_name" +/* from zutil.h */ +#ifndef DEF_MEM_LEVEL +#if MAX_MEM_LEVEL >= 8 +#define DEF_MEM_LEVEL 8 +#else +#define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +#endif + typedef enum { HTTP_STATUS_200 = 0, HTTP_STATUS_304, HTTP_STATUS_403, HTTP_STATUS_404 @@ -211,11 +223,11 @@ } return HTTP_STATUS_200; } long int get_default_expires(node_t *root) { - char *node_value = get_node_value(root, DEFAULT_EXPIRES); + char *node_value = yaml_get_value(root, DEFAULT_EXPIRES); if (node_value == NULL) { //return EXPIRES_DURATION; return 0; }else if (strcmp(node_value, "off") == 0) { @@ -239,22 +251,22 @@ if (root == NULL) { LOG_ERROR(SEVERE, "Could not read the file %s", mapping_file); return -1; } - node = get_nodes(root, "File Extensions"); + node = yaml_get_node(root, "File Extensions"); if (root == NULL || node->child == NULL) { LOG_ERROR(SEVERE, "Could not read 'File Extensions' from the file %s", mapping_file); return -1; } node = node->child; while (node) { ext = wr_malloc(static_file_t); - strcpy(ext->ext, node->name); + strcpy(ext->ext, node->key); strcpy(ext->mime_type, node->value); ext->expires = expires; int index; if(ext->ext[0] >= '0' && ext->ext[0] <= '9'){ index = ext->ext[0] - '0'; @@ -271,11 +283,11 @@ LOG_ERROR(WARN, "Mapping index out of bound for extension = %s", ext->ext); free(ext); } node = node->next; } - node_free(root); + yaml_node_free(root); // Set default mime type ext = wr_malloc(static_file_t); strcpy(ext->ext, "txt"); strcpy(ext->mime_type, "text/plain"); @@ -285,42 +297,150 @@ return 0; } void set_expires_by_type(static_server_t *s, node_t *root) { - node_t *node = get_nodes(root, EXPIRES_BY_TYPE); + node_t *node = yaml_get_node(root, EXPIRES_BY_TYPE); long int expires; char *types, *expires_str, *type; while (node) { - types = get_node_value(node, EXPIRES_BY_TYPE_EXT); - expires_str = get_node_value(node, EXPIRES_BY_TYPE_EXPIRES); + types = yaml_get_value(node->child, EXPIRES_BY_TYPE_EXT); + expires_str = yaml_get_value(node->child, EXPIRES_BY_TYPE_EXPIRES); expires = atol(expires_str); type = strtok(types, " ,"); while (type != NULL) { set_expires_time(s, type, expires); type = strtok(NULL, " ,"); } - node = NODE_NEXT(node); + node = node->next; } } #ifdef W_ZLIB +int deflate_file_compression(http_t *h) { + if(compress2((Bytef*)h->resp->resp_body->str, (uLongf*)&h->resp->resp_body->size, + (const Bytef*)h->stat->buffer->str, (uLongf)h->stat->buffer->len, Z_DEFAULT_COMPRESSION) != Z_OK){ + wr_buffer_null(h->stat->buffer); + wr_buffer_null(h->resp->resp_body); + return FALSE; + } else { + h->resp->resp_body->len = h->resp->resp_body->size; + return TRUE; + } +} + +int gzip_file_compression(http_t *h) { + z_stream c_stream; + int err; + + c_stream.zalloc = NULL; + c_stream.zfree = NULL; + c_stream.opaque = NULL; + c_stream.total_out = 0; + + err = deflateInit2(&c_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, + 15+16, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + + if (err != Z_OK) { + LOG_ERROR(WARN,"Deflate Init error"); + return FALSE; + } + + c_stream.next_in = (Bytef*) h->stat->buffer->str; + c_stream.avail_in = h->stat->buffer->len; + c_stream.next_out = (Bytef*) h->resp->resp_body->str; + c_stream.avail_out = h->resp->resp_body->size; + + do { + c_stream.next_out = (Bytef*) (h->resp->resp_body->str + c_stream.total_out); + + // Calculate the amount of remaining free space in the output buffer + // by subtracting the number of bytes that have been written so far + // from the buffer's total capacity + c_stream.avail_out = h->resp->resp_body->size - c_stream.total_out; + err = deflate(&c_stream, Z_FINISH); + } while (err == Z_OK); + + if (err = Z_STREAM_END) { + err = deflateEnd(&c_stream); + if ( err != Z_OK) { + LOG_ERROR(WARN, "Deflate End error"); + return FALSE; + } else { + h->resp->resp_body->len = c_stream.total_out; + return TRUE; + } + } else { + switch (err) { + case Z_ERRNO: + LOG_ERROR(WARN, "Error occured while reading file."); + break; + case Z_STREAM_ERROR: + LOG_ERROR(WARN, "The stream state was inconsistent (e.g., next_in or next_out was NULL)."); + break; + case Z_DATA_ERROR: + LOG_ERROR(WARN, "The deflate data was invalid or incomplete."); + break; + case Z_MEM_ERROR: + LOG_ERROR(WARN, "Memory could not be allocated for processing."); + break; + case Z_BUF_ERROR: + LOG_ERROR(WARN, "Ran out of output buffer for writing compressed bytes."); + break; + case Z_VERSION_ERROR: + LOG_ERROR(WARN, "The version of zlib.h and the version of the library linked do not match."); + break; + default: + LOG_ERROR(WARN, "Unknown error code."); + break; + } + return FALSE; + } +} /** Compress file */ /* Compress file if its size is >10kb and < 1mb and its mime-type has either * text or xml */ -int file_compress(http_t *h, static_file_t *ext){ - LOG_FUNCTION +int file_compress(http_t *h, static_file_t *ext) { + LOG_FUNCTION + int rv=0; + char *cache_control=NULL; + + cache_control = scgi_header_value_get(h->req->scgi, "Cache-Control"); + if(cache_control && strstr(cache_control, "no-transform")) { + return FALSE; + } + h->stat->encoding = scgi_header_value_get(h->req->scgi, "HTTP_ACCEPT_ENCODING"); h->stat->user_agent = scgi_header_value_get(h->req->scgi, "HTTP_USER_AGENT"); - - if(h->stat->buf.st_size >= Config->Worker.Compress.lower_limit - && h->stat->buf.st_size <= Config->Worker.Compress.upper_limit - && h->stat->encoding && strstr(h->stat->encoding,"deflate")){ + /* Skip compressing entity body if user agent is IE6 + refer http://schroepl.net/projekte/mod_gzip/browser.htm and + http://support.microsoft.com/default.aspx?scid=kb;en-us;Q313712 + for problem details + */ + if(h->stat->user_agent && strstr(h->stat->user_agent, "MSIE 6.0")) { + return FALSE; + } + if(h->stat->buf.st_size >= Config->Worker.Compress.lower_limit + && h->stat->buf.st_size <= Config->Worker.Compress.upper_limit + && h->stat->encoding) { + //TODO: look into Cache-Control request header and q value of Accept-Encoding request header + // to decide correct encoding + if (strstr(h->stat->encoding, "gzip")) { + h->resp->content_encoding = WR_GZIP_ENCODING; + } + else if (strstr(h->stat->encoding, "deflate")) { + h->resp->content_encoding = WR_DEFLATE_ENCODING; + } + else { + h->resp->content_encoding = WR_NO_ENCODING; + return FALSE; + } + #ifdef W_REGEX if(h->stat->r_content_type){ if(regexec(h->stat->r_content_type, ext->mime_type, 0, NULL, 0) !=0 ) return FALSE; } @@ -336,10 +456,15 @@ #endif wr_u_int read; FILE *file; wr_buffer_create(h->stat->buffer, h->stat->buf.st_size); + + if(h->stat->buffer->str == NULL) { + return FALSE; + } + file = fopen(h->stat->path, "r"); if(file == NULL) return FALSE; while(h->stat->buffer->len < h->stat->buf.st_size){ @@ -355,33 +480,41 @@ fclose(file); //zlib states that the source buffer must be at least 0.1 times larger than //the source buffer plus 12 bytes to cope with the overhead of zlib data streams - wr_buffer_create(h->resp->resp_body, h->stat->buf.st_size + h->stat->buf.st_size*0.1 + 12); - h->resp->resp_body->len = h->resp->resp_body->size; - //now compress the data - if(compress2((Bytef*)h->resp->resp_body->str, (uLongf*)&h->resp->resp_body->len, - (const Bytef*)h->stat->buffer->str, (uLongf)h->stat->buffer->len, Z_DEFAULT_COMPRESSION) != Z_OK){ - wr_buffer_null(h->stat->buffer); - wr_buffer_null(h->resp->resp_body); - return FALSE; + wr_buffer_create(h->resp->resp_body, h->stat->buf.st_size * 1.01 + 12); + + switch(h->resp->content_encoding) { + case WR_GZIP_ENCODING: + rv = gzip_file_compression(h); + LOG_DEBUG(DEBUG, "Done gzip encoding rv = %d", rv); + break; + case WR_DEFLATE_ENCODING: + rv = deflate_file_compression(h); + LOG_DEBUG(DEBUG, "Done deflate encoding rv = %d", rv); + break; } + wr_buffer_null(h->stat->buffer); - - return TRUE; + + if(rv == FALSE) { + wr_buffer_null(h->resp->resp_body); + } + + return rv; } return FALSE; } #endif void http_resp_200(http_t *h) { LOG_FUNCTION char str[STR_SIZE512], expire_date[STR_SIZE64] = "", current_date[STR_SIZE64] = "", modify_date[STR_SIZE64] = ""; int len; - int ret_val; + int ret_val = FALSE; time_t t; static_file_t *ext = get_mime_type(h->stat); LOG_DEBUG(DEBUG,"File extension = %s, mimetype = %s, expires = %d ", ext->ext, ext->mime_type, ext->expires); const char *conn_header = scgi_header_value_get(h->req->scgi, HTTP_HEADER_CONNECTION); @@ -405,20 +538,20 @@ if (ext->expires > 0) { t += ext->expires; time_to_httpdate(t, expire_date, STR_SIZE64); len = sprintf(str, "HTTP/1.1 200 OK\r\nDate: %s\r\nServer: %s-%s\r\nLast-Modified: %s\r\nExpires: %s\r\nConnection: %s\r\n%sContent-Type: %s\r\nContent-Length: %d\r\n\r\n", - current_date, Config->Worker.Server.name.str, Config->Worker.Server.version.str, modify_date, expire_date, - (conn_header ? conn_header : CONNECTION_CLOSE), - (ret_val == TRUE ? "Content-Encoding: deflate\r\n" : ""), - ext->mime_type, h->resp->resp_body->len); - }else { + current_date, Config->Worker.Server.name.str, Config->Worker.Server.version.str, modify_date, expire_date, + (conn_header ? conn_header : CONNECTION_CLOSE), + (ret_val == TRUE ? (h->resp->content_encoding == WR_GZIP_ENCODING ? "Content-Encoding: gzip\r\n" : "Content-Encoding: deflate\r\n") : ""), + ext->mime_type, h->resp->resp_body->len); + } else { len = sprintf(str, "HTTP/1.1 200 OK\r\nDate: %s\r\nServer: %s-%s\r\nLast-Modified: %s\r\nConnection: %s\r\n%sContent-Type: %s\r\nContent-Length: %d\r\n\r\n", - current_date, Config->Worker.Server.name.str, Config->Worker.Server.version.str, modify_date, - (conn_header ? conn_header : CONNECTION_CLOSE), - (ret_val == TRUE ? "Content-Encoding: deflate\r\n" : ""), - ext->mime_type, h->resp->resp_body->len); + current_date, Config->Worker.Server.name.str, Config->Worker.Server.version.str, modify_date, + (conn_header ? conn_header : CONNECTION_CLOSE), + (ret_val == TRUE ? (h->resp->content_encoding == WR_GZIP_ENCODING ? "Content-Encoding: gzip\r\n" : "Content-Encoding: deflate\r\n") : ""), + ext->mime_type, h->resp->resp_body->len); } wr_string_new(h->resp->header, str, len); h->resp->resp_code = http_status[HTTP_STATUS_200].code; } @@ -505,13 +638,13 @@ wr_msg_queue_server_t *msg_queue_server = NULL; wr_msg_queue_conn_t *msg_queue_conn = NULL; char msg_value[STR_SIZE32]; int rv; - host = get_node_value(root, WR_MSG_QUEUE_SERVER_HOST); - port = get_node_value(root, WR_MSG_QUEUE_SERVER_PORT); - queue_name = get_node_value(root, WR_PID_MSG_QUEUE_NAME); + host = yaml_get_value(root, WR_MSG_QUEUE_SERVER_HOST); + port = yaml_get_value(root, WR_MSG_QUEUE_SERVER_PORT); + queue_name = yaml_get_value(root, WR_PID_MSG_QUEUE_NAME); if (!host || !port || !queue_name) { LOG_ERROR(SEVERE, "Error getting message queue configuration"); goto err; } msg_queue_server = wr_msg_queue_server_new(host, atoi(port)); @@ -535,11 +668,11 @@ LOG_ERROR(SEVERE, "Failed to send PID to message queue"); } else { LOG_INFO("PID sent to queue successfully."); } err: - node_free(root); + yaml_node_free(root); wr_msg_queue_conn_free(msg_queue_conn); wr_msg_queue_server_free(msg_queue_server); return; } } @@ -557,16 +690,16 @@ return FALSE; } expires = get_default_expires(root); if (create_dictionary(s, Config->Worker.File.mime_type.str, expires) != 0) { - node_free(root); + yaml_node_free(root); return FALSE; } set_expires_by_type(s, root); - node_free(root); + yaml_node_free(root); send_static_worker_pid(); return TRUE; }