LCOV - code coverage report
Current view: top level - msus/webserver - cache_msu.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 119 136 87.5 %
Date: 2018-01-11 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /*
       2             : START OF LICENSE STUB
       3             :     DeDOS: Declarative Dispersion-Oriented Software
       4             :     Copyright (C) 2017 University of Pennsylvania, Georgetown University
       5             : 
       6             :     This program is free software: you can redistribute it and/or modify
       7             :     it under the terms of the GNU General Public License as published by
       8             :     the Free Software Foundation, either version 3 of the License, or
       9             :     (at your option) any later version.
      10             : 
      11             :     This program is distributed in the hope that it will be useful,
      12             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :     GNU General Public License for more details.
      15             : 
      16             :     You should have received a copy of the GNU General Public License
      17             :     along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             : END OF LICENSE STUB
      19             : */
      20             : #include "connection-handler.h"
      21             : #include "dfg.h"
      22             : #include "local_msu.h"
      23             : #include "logging.h"
      24             : #include "msu_calls.h"
      25             : #include "msu_message.h"
      26             : #include "msu_type.h"
      27             : #include "routing_strategies.h"
      28             : #include "rt_stats.h"
      29             : #include "local_files.h"
      30             : 
      31             : #include "webserver/uthash.h"
      32             : #include "webserver/write_msu.h"
      33             : #include "webserver/cache_msu.h"
      34             : #include "webserver/fileio_msu.h"
      35             : #include "webserver/connection-handler.h"
      36             : #include "webserver/httpops.h"
      37             : 
      38             : #include <string.h>
      39             : 
      40             : #define DEFAULT_WWW_DIR "www/"
      41             : #define DEFAULT_OCCUPANCY_RATE 0.2
      42             : #define DEFAULT_MAX_KB_SIZE UINT_MAX
      43             : #define DEFAULT_MAX_FILES UINT_MAX
      44             : #define CACHE_INIT_SYNTAX "<www_dir>, <max_cache_size_in_kb>, <max_cached_files>, " \
      45             :                           "<max_cache_occupancy_rate>"
      46             : 
      47             : #define MONITOR_CACHE_STATS
      48             : #define CACHE_HIT_STAT MSU_STAT1
      49             : #define CACHE_MISS_STAT MSU_STAT2
      50             : #define CACHE_EVICT_STAT MSU_STAT3
      51             : 
      52             : 
      53             : static struct local_msu *cache_instance;
      54             : 
      55             : struct cached_file {
      56             :     long byte_size;
      57             :     char *path;
      58             :     char *contents;
      59             :     struct cached_file *lru_prev;
      60             :     struct cached_file *lru_next;
      61             :     UT_hash_handle hh;
      62             : };
      63             : 
      64             : struct ws_cache_state {
      65             :     unsigned int max_files;
      66             :     unsigned int max_kb_size;
      67             :     float max_occupancy_rate;
      68             :     unsigned long byte_size;
      69             :     unsigned int file_count;
      70             :     char *www_dir;
      71             :     struct cached_file *cache;
      72             :     struct cached_file *lru_head; // Contains the least recently used item
      73             :     struct cached_file *lru_tail; // Contains the last item used
      74             : };
      75             : 
      76         138 : struct cached_file *check_cache(struct ws_cache_state *fc, char *path) {
      77         138 :     struct cached_file *cached = NULL;
      78         138 :     HASH_FIND_STR(fc->cache, path, cached);
      79         138 :     if (cached != NULL) {
      80         109 :         log_info("File %s retrieved from cache", path);
      81             : 
      82             :         // Update LRU if not already the tail
      83         109 :         if (fc->lru_tail != cached) {
      84           6 :             if (fc->lru_head == cached) {
      85           6 :                 fc->lru_head = cached->lru_next;
      86             :             } else {
      87           0 :                 cached->lru_prev->lru_next = cached->lru_next;
      88             :             }
      89           6 :             cached->lru_next->lru_prev = cached->lru_prev;
      90           6 :             cached->lru_prev = fc->lru_tail;
      91           6 :             fc->lru_tail->lru_next = cached;
      92           6 :             cached->lru_next = NULL;
      93           6 :             fc->lru_tail = cached;
      94             :         }
      95             : 
      96         109 :         return cached;
      97             :     }
      98             : 
      99          29 :     return NULL;
     100             : }
     101             : 
     102          27 : static int parse_init_cache_payload(char *to_parse, struct ws_cache_state *cache_state) {
     103             : 
     104          27 :     cache_state->www_dir = DEFAULT_WWW_DIR;
     105          27 :     cache_state->max_kb_size = DEFAULT_MAX_KB_SIZE;
     106          27 :     cache_state->max_files = DEFAULT_MAX_FILES;
     107          27 :     cache_state->max_occupancy_rate = DEFAULT_OCCUPANCY_RATE;
     108             : 
     109          27 :     if (to_parse == NULL) {
     110           0 :         log_warn("Initializing cache MSU with default parameters. "
     111             :                          "(FYI: init syntax is [" CACHE_INIT_SYNTAX "])");
     112             :     } else {
     113             :         char *saveptr, *tok;
     114          27 :         if ( (tok = strtok_r(to_parse, " ,", &saveptr)) == NULL) {
     115           0 :             log_warn("Couldn't get wwW_dir from initialization string");
     116           0 :             return 0;
     117             :         }
     118          27 :         cache_state->www_dir = malloc(32 + strlen(tok));
     119          27 :         get_local_file(cache_state->www_dir, tok);
     120             : 
     121          27 :         if ( (tok = strtok_r(NULL, " ,", &saveptr)) == NULL) {
     122           0 :             log_warn("Couldn't get max cache size in kb from initialization string");
     123           0 :             return 0;
     124             :         }
     125          27 :         int max_kb_size = atoi(tok);
     126          27 :         if (max_kb_size >= 0)
     127          27 :             cache_state->max_kb_size = (unsigned int) max_kb_size;
     128             : 
     129          27 :         if ( (tok = strtok_r(NULL, " ,", &saveptr)) == NULL) {
     130           0 :             log_warn("Couldn't get max cached files from initialization string");
     131           0 :             return 0;
     132             :         }
     133          27 :         int max_files = atoi(tok);
     134          27 :         if (max_files >= 0)
     135          27 :             cache_state->max_files = (unsigned int) max_files;
     136             : 
     137          27 :         if ( (tok = strtok_r(NULL, " ,", &saveptr)) == NULL) {
     138           0 :             log_warn("Couldn't get max occupancy rate in kb from initialization string");
     139           0 :             return 0;
     140             :         }
     141          27 :         float max_occupancy_rate = atof(tok);
     142          27 :         if (max_occupancy_rate >= 0.0)
     143          27 :             cache_state->max_occupancy_rate = max_occupancy_rate;
     144             : 
     145          27 :         if ( (tok = strtok_r(NULL, " ,", &saveptr)) != NULL) {
     146           0 :             log_warn("Discarding extra tokens from cache initialization: %s", tok);
     147             :         }
     148             :     }
     149          27 :     return 0;
     150             : }
     151             : 
     152          29 : static int cache_file(struct ws_cache_state *fc, char *path, char *contents, long length) {
     153             :     // Only cache the file if it isn't too large
     154          29 :     float kbytes = (float) length / 1024;
     155          29 :     if (kbytes > fc->max_kb_size || kbytes / fc->max_kb_size > fc->max_occupancy_rate) {
     156           4 :         log_info("File at %s is too large for caching (%ld bytes)", path, length);
     157           4 :         return -1;
     158             :     }
     159             : 
     160             :     // Evict files if necessar11y
     161          87 :     while (((float) fc->byte_size + length) / 1024 > fc->max_kb_size ||
     162          29 :            fc->max_files == fc->file_count) {
     163           8 :         struct cached_file *cached = fc->lru_head;
     164           8 :         if (cached == NULL) {
     165           0 :             log_error("Trying to evict lru head that is NULL!");
     166           0 :             return -2;
     167             :         }
     168             : 
     169           8 :         log_info("Evicting %s from cache", cached->path);
     170             : #ifdef MONITOR_CACHE_STATS
     171           8 :         increment_stat(CACHE_EVICT_STAT, cache_instance->id, 1);
     172             : #endif
     173             : 
     174           8 :         HASH_DEL(fc->cache, cached);
     175           8 :         fc->lru_head = cached->lru_next;
     176           8 :         if (fc->lru_head != NULL) {
     177           4 :             fc->lru_head->lru_prev = NULL;
     178             :         }
     179           8 :         if (fc->lru_tail == cached) {
     180           4 :             fc->lru_tail = NULL;
     181             :         }
     182           8 :         fc->file_count--;
     183           8 :         fc->byte_size -= cached->byte_size;
     184           8 :         free(cached->path);
     185           8 :         free(cached->contents);
     186           8 :         free(cached);
     187             :     }
     188             : 
     189             :     // Now add the file to the cache
     190          25 :     struct cached_file *cached = (struct cached_file *) malloc(
     191             :             sizeof(struct cached_file));
     192          25 :     if (cached == NULL) {
     193           0 :         log_error("Failed to allocate space for cached_file struct for file %s", path);
     194           0 :         return -2;
     195             :     }
     196             : 
     197          25 :     log_info("Adding file %s to cache", path);
     198             : 
     199             :     // Add to cache
     200          25 :     cached->byte_size = length;
     201             :     // Copy path
     202          25 :     int path_len = strlen(path);
     203          25 :     cached->path = (char *) malloc(path_len + 1);
     204          25 :     strncpy(cached->path, path, path_len);
     205          25 :     cached->path[path_len] = '\0';
     206             :     // Copy contents
     207          25 :     if (length > 0) {
     208          25 :         cached->contents = (char *) malloc(length);
     209          25 :         memcpy(cached->contents, contents, length);
     210             :     } else {
     211           0 :         cached->contents = NULL;
     212             :     }
     213          25 :     fc->file_count++;
     214          25 :     HASH_ADD_STR(fc->cache, path, cached);
     215             : 
     216             :     // Update LRU linked list
     217          25 :     cached->lru_prev = fc->lru_tail;
     218          25 :     if (fc->lru_tail != NULL) {
     219           8 :         fc->lru_tail->lru_next = cached;
     220             :     }
     221          25 :     cached->lru_next = NULL;
     222          25 :     if (fc->lru_head == NULL) {
     223          17 :         fc->lru_head = cached;
     224             :     }
     225          25 :     fc->lru_tail = cached;
     226          25 :     fc->byte_size += length;
     227             : 
     228          25 :     return length;
     229             : }
     230             : 
     231         167 : static int ws_cache_lookup(struct local_msu *self,
     232             :                            struct msu_msg *msg) {
     233         167 :     struct response_state *resp = msg->data;
     234         167 :     struct ws_cache_state *fc = self->msu_state;
     235             : 
     236         167 :     url_to_path(resp->url, fc->www_dir, resp->path, MAX_FILEPATH_LEN);
     237             : 
     238             :     // TODO: Check message type properly to determine if this is a response to cache, or request
     239         305 :     if (resp->body[0] == '\0' && resp->body_len == 0) {
     240         138 :         struct cached_file *file = check_cache(fc, resp->path);
     241         138 :         if (file == NULL) {
     242             :             // File not cached, send to file IO msu
     243             : #ifdef MONITOR_CACHE_STATS
     244          29 :             increment_stat(CACHE_MISS_STAT, cache_instance->id, 1);
     245             : #endif
     246          29 :             call_msu_type(self, &WEBSERVER_FILEIO_MSU_TYPE, &msg->hdr, sizeof(*resp), resp);
     247             :         } else {
     248             :             // File cached, generate response including http headers and send to write msu
     249         109 :             int code = 404;
     250         109 :             char *mime_type = NULL;
     251         109 :             if (file->contents != NULL && file->byte_size > 0) {
     252         109 :                 code = 200;
     253         109 :                 resp->body_len = file->byte_size;
     254         109 :                 mime_type = path_to_mimetype(resp->path);
     255         109 :                 memcpy(resp->body, file->contents, resp->body_len);
     256             :             }
     257         109 :             resp->header_len = generate_header(resp->header, code, MAX_HEADER_LEN, resp->body_len,
     258             :                                                mime_type);
     259         109 :             if (resp->header_len > MAX_HEADER_LEN) {
     260           0 :                 resp->header_len = MAX_HEADER_LEN;
     261             :             }
     262             : #ifdef MONITOR_CACHE_STATS
     263         109 :             increment_stat(CACHE_HIT_STAT, cache_instance->id, 1);
     264             : #endif
     265         109 :             call_msu_type(self, &WEBSERVER_WRITE_MSU_TYPE, &msg->hdr, sizeof(*resp), resp);
     266             :         }
     267             :     } else {
     268             :         // Received a response to save to the cache
     269          29 :         cache_file((struct ws_cache_state *)self->msu_state, resp->path, resp->body,
     270          29 :                    resp->body_len);
     271             :     }
     272             : 
     273         167 :     return 0;
     274             : }
     275             : 
     276          27 : static int ws_cache_init(struct local_msu *self, struct msu_init_data *init_data) {
     277          27 :     struct ws_cache_state *cache_state = malloc(sizeof(*cache_state));
     278             : 
     279          27 :     parse_init_cache_payload(init_data->init_data, cache_state);
     280             : 
     281          27 :     cache_state->byte_size = 0;
     282          27 :     cache_state->file_count = 0;
     283          27 :     cache_state->cache = NULL;
     284          27 :     cache_state->lru_head = NULL;
     285          27 :     cache_state->lru_tail = NULL;
     286             : 
     287          27 :     self->msu_state = (void*)cache_state;
     288             : 
     289          27 :     cache_instance = self;
     290             : 
     291             : #ifdef MONITOR_CACHE_STATS
     292          27 :     init_stat_item(CACHE_HIT_STAT, self->id);
     293          27 :     init_stat_item(CACHE_MISS_STAT, self->id);
     294          27 :     init_stat_item(CACHE_EVICT_STAT, self->id);
     295             : #endif
     296             : 
     297          27 :     return 0;
     298             : }
     299             : 
     300             : struct msu_type WEBSERVER_CACHE_MSU_TYPE = {
     301             :         .name = "Webserver_cache_msu",
     302             :         .id = WEBSERVER_CACHE_MSU_TYPE_ID,
     303             :         .receive = ws_cache_lookup,
     304             :         .init = ws_cache_init,
     305             :         .route = shortest_queue_route
     306             : };

Generated by: LCOV version 1.10