My Project
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros
cache_msu.c
Go to the documentation of this file.
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"
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;
62 };
63 
65  unsigned int max_files;
66  unsigned int max_kb_size;
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 struct cached_file *check_cache(struct ws_cache_state *fc, char *path) {
77  struct cached_file *cached = NULL;
78  HASH_FIND_STR(fc->cache, path, cached);
79  if (cached != NULL) {
80  log_info("File %s retrieved from cache", path);
81 
82  // Update LRU if not already the tail
83  if (fc->lru_tail != cached) {
84  if (fc->lru_head == cached) {
85  fc->lru_head = cached->lru_next;
86  } else {
87  cached->lru_prev->lru_next = cached->lru_next;
88  }
89  cached->lru_next->lru_prev = cached->lru_prev;
90  cached->lru_prev = fc->lru_tail;
91  fc->lru_tail->lru_next = cached;
92  cached->lru_next = NULL;
93  fc->lru_tail = cached;
94  }
95 
96  return cached;
97  }
98 
99  return NULL;
100 }
101 
102 static int parse_init_cache_payload(char *to_parse, struct ws_cache_state *cache_state) {
103 
104  cache_state->www_dir = DEFAULT_WWW_DIR;
105  cache_state->max_kb_size = DEFAULT_MAX_KB_SIZE;
106  cache_state->max_files = DEFAULT_MAX_FILES;
108 
109  if (to_parse == NULL) {
110  log_warn("Initializing cache MSU with default parameters. "
111  "(FYI: init syntax is [" CACHE_INIT_SYNTAX "])");
112  } else {
113  char *saveptr, *tok;
114  if ( (tok = strtok_r(to_parse, " ,", &saveptr)) == NULL) {
115  log_warn("Couldn't get wwW_dir from initialization string");
116  return 0;
117  }
118  cache_state->www_dir = malloc(32 + strlen(tok));
119  get_local_file(cache_state->www_dir, tok);
120 
121  if ( (tok = strtok_r(NULL, " ,", &saveptr)) == NULL) {
122  log_warn("Couldn't get max cache size in kb from initialization string");
123  return 0;
124  }
125  int max_kb_size = atoi(tok);
126  if (max_kb_size >= 0)
127  cache_state->max_kb_size = (unsigned int) max_kb_size;
128 
129  if ( (tok = strtok_r(NULL, " ,", &saveptr)) == NULL) {
130  log_warn("Couldn't get max cached files from initialization string");
131  return 0;
132  }
133  int max_files = atoi(tok);
134  if (max_files >= 0)
135  cache_state->max_files = (unsigned int) max_files;
136 
137  if ( (tok = strtok_r(NULL, " ,", &saveptr)) == NULL) {
138  log_warn("Couldn't get max occupancy rate in kb from initialization string");
139  return 0;
140  }
141  float max_occupancy_rate = atof(tok);
142  if (max_occupancy_rate >= 0.0)
143  cache_state->max_occupancy_rate = max_occupancy_rate;
144 
145  if ( (tok = strtok_r(NULL, " ,", &saveptr)) != NULL) {
146  log_warn("Discarding extra tokens from cache initialization: %s", tok);
147  }
148  }
149  return 0;
150 }
151 
152 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  float kbytes = (float) length / 1024;
155  if (kbytes > fc->max_kb_size || kbytes / fc->max_kb_size > fc->max_occupancy_rate) {
156  log_info("File at %s is too large for caching (%ld bytes)", path, length);
157  return -1;
158  }
159 
160  // Evict files if necessar11y
161  while (((float) fc->byte_size + length) / 1024 > fc->max_kb_size ||
162  fc->max_files == fc->file_count) {
163  struct cached_file *cached = fc->lru_head;
164  if (cached == NULL) {
165  log_error("Trying to evict lru head that is NULL!");
166  return -2;
167  }
168 
169  log_info("Evicting %s from cache", cached->path);
170 #ifdef MONITOR_CACHE_STATS
171  increment_stat(CACHE_EVICT_STAT, cache_instance->id, 1);
172 #endif
173 
174  HASH_DEL(fc->cache, cached);
175  fc->lru_head = cached->lru_next;
176  if (fc->lru_head != NULL) {
177  fc->lru_head->lru_prev = NULL;
178  }
179  if (fc->lru_tail == cached) {
180  fc->lru_tail = NULL;
181  }
182  fc->file_count--;
183  fc->byte_size -= cached->byte_size;
184  free(cached->path);
185  free(cached->contents);
186  free(cached);
187  }
188 
189  // Now add the file to the cache
190  struct cached_file *cached = (struct cached_file *) malloc(
191  sizeof(struct cached_file));
192  if (cached == NULL) {
193  log_error("Failed to allocate space for cached_file struct for file %s", path);
194  return -2;
195  }
196 
197  log_info("Adding file %s to cache", path);
198 
199  // Add to cache
200  cached->byte_size = length;
201  // Copy path
202  int path_len = strlen(path);
203  cached->path = (char *) malloc(path_len + 1);
204  strncpy(cached->path, path, path_len);
205  cached->path[path_len] = '\0';
206  // Copy contents
207  if (length > 0) {
208  cached->contents = (char *) malloc(length);
209  memcpy(cached->contents, contents, length);
210  } else {
211  cached->contents = NULL;
212  }
213  fc->file_count++;
214  HASH_ADD_STR(fc->cache, path, cached);
215 
216  // Update LRU linked list
217  cached->lru_prev = fc->lru_tail;
218  if (fc->lru_tail != NULL) {
219  fc->lru_tail->lru_next = cached;
220  }
221  cached->lru_next = NULL;
222  if (fc->lru_head == NULL) {
223  fc->lru_head = cached;
224  }
225  fc->lru_tail = cached;
226  fc->byte_size += length;
227 
228  return length;
229 }
230 
231 static int ws_cache_lookup(struct local_msu *self,
232  struct msu_msg *msg) {
233  struct response_state *resp = msg->data;
234  struct ws_cache_state *fc = self->msu_state;
235 
236  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  if (resp->body[0] == '\0' && resp->body_len == 0) {
240  struct cached_file *file = check_cache(fc, resp->path);
241  if (file == NULL) {
242  // File not cached, send to file IO msu
243 #ifdef MONITOR_CACHE_STATS
244  increment_stat(CACHE_MISS_STAT, cache_instance->id, 1);
245 #endif
246  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  int code = 404;
250  char *mime_type = NULL;
251  if (file->contents != NULL && file->byte_size > 0) {
252  code = 200;
253  resp->body_len = file->byte_size;
254  mime_type = path_to_mimetype(resp->path);
255  memcpy(resp->body, file->contents, resp->body_len);
256  }
257  resp->header_len = generate_header(resp->header, code, MAX_HEADER_LEN, resp->body_len,
258  mime_type);
259  if (resp->header_len > MAX_HEADER_LEN) {
260  resp->header_len = MAX_HEADER_LEN;
261  }
262 #ifdef MONITOR_CACHE_STATS
263  increment_stat(CACHE_HIT_STAT, cache_instance->id, 1);
264 #endif
265  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  cache_file((struct ws_cache_state *)self->msu_state, resp->path, resp->body,
270  resp->body_len);
271  }
272 
273  return 0;
274 }
275 
276 static int ws_cache_init(struct local_msu *self, struct msu_init_data *init_data) {
277  struct ws_cache_state *cache_state = malloc(sizeof(*cache_state));
278 
279  parse_init_cache_payload(init_data->init_data, cache_state);
280 
281  cache_state->byte_size = 0;
282  cache_state->file_count = 0;
283  cache_state->cache = NULL;
284  cache_state->lru_head = NULL;
285  cache_state->lru_tail = NULL;
286 
287  self->msu_state = (void*)cache_state;
288 
289  cache_instance = self;
290 
291 #ifdef MONITOR_CACHE_STATS
292  init_stat_item(CACHE_HIT_STAT, self->id);
293  init_stat_item(CACHE_MISS_STAT, self->id);
294  init_stat_item(CACHE_EVICT_STAT, self->id);
295 #endif
296 
297  return 0;
298 }
299 
301  .name = "Webserver_cache_msu",
303  .receive = ws_cache_lookup,
304  .init = ws_cache_init,
305  .route = shortest_queue_route
306 };
unsigned long byte_size
Definition: cache_msu.c:68
#define HASH_ADD_STR(head, strfield, add)
Definition: uthash.h:395
Collecting statistics within the runtime.
unsigned int max_kb_size
Definition: cache_msu.c:66
struct cached_file * cache
Definition: cache_msu.c:71
int shortest_queue_route(struct msu_type *type, struct local_msu *sender, struct msu_msg *msg, struct msu_endpoint *output)
Chooses the local MSU with the shortest queue.
struct msu_type WEBSERVER_FILEIO_MSU_TYPE
Definition: fileio_msu.c:76
#define log_info(fmt,...)
Definition: logging.h:88
#define DEFAULT_OCCUPANCY_RATE
Definition: cache_msu.c:41
#define WEBSERVER_CACHE_MSU_TYPE_ID
Definition: cache_msu.h:23
#define CACHE_EVICT_STAT
Definition: cache_msu.c:50
Defines a type of MSU, including callback and accessor functions.
#define DEFAULT_MAX_KB_SIZE
Definition: cache_msu.c:42
unsigned int max_files
Definition: cache_msu.c:65
#define CACHE_INIT_SYNTAX
Definition: cache_msu.c:44
struct msu_type WEBSERVER_CACHE_MSU_TYPE
Definition: cache_msu.c:300
void * data
Payload.
Definition: msu_message.h:104
#define CACHE_MISS_STAT
Definition: cache_msu.c:49
#define MAX_FILEPATH_LEN
Logging of status messages to the terminal.
Access local files within the repo.
char * path_to_mimetype(char *path)
Definition: httpops.c:36
unsigned int id
Unique ID for a local MSU.
Definition: local_msu.h:54
struct msu_type WEBSERVER_WRITE_MSU_TYPE
Definition: write_msu.c:69
struct cached_file * lru_head
Definition: cache_msu.c:72
char * path
Definition: cache_msu.c:57
struct cached_file * lru_next
Definition: cache_msu.c:60
Declares the methods available for calling an MSU from another MSU.
#define log_error(fmt,...)
Definition: logging.h:101
Declares the structures and functions applicable to MSUs on the local machine.
int init_stat_item(enum stat_id stat_id, unsigned int item_id)
Initializes a stat item so that statistics can be logged to it.
Definition: rt_stats.c:719
UT_hash_handle hh
Definition: cache_msu.c:61
static int ws_cache_init(struct local_msu *self, struct msu_init_data *init_data)
Definition: cache_msu.c:276
float max_occupancy_rate
Definition: cache_msu.c:67
#define CACHE_HIT_STAT
Definition: cache_msu.c:48
unsigned int file_count
Definition: cache_msu.c:69
struct cached_file * lru_prev
Definition: cache_msu.c:59
#define HASH_FIND_STR(head, findstr, out)
Definition: uthash.h:393
#define DEFAULT_WWW_DIR
Definition: cache_msu.c:40
char * www_dir
Definition: cache_msu.c:70
The structure that represents an MSU located on the local machine.
Definition: local_msu.h:38
struct cached_file * lru_tail
Definition: cache_msu.c:73
Data with which an MSU is initialized, and the payload for messages of type CTRL_CREATE_MSU.
Definition: dfg.h:66
static struct local_msu * cache_instance
Definition: cache_msu.c:53
static int cache_file(struct ws_cache_state *fc, char *path, char *contents, long length)
Definition: cache_msu.c:152
long byte_size
Definition: cache_msu.c:56
static int ws_cache_lookup(struct local_msu *self, struct msu_msg *msg)
Definition: cache_msu.c:231
int get_local_file(char *out, char *file)
Gets a file relative to the path of the executable.
Definition: local_files.c:38
int call_msu_type(struct local_msu *sender, struct msu_type *dst_type, struct msu_msg_hdr *hdr, size_t data_size, void *data)
Sends an MSU message to a destination of the given type, utilizing the sending MSU's routing function...
Definition: msu_calls.c:146
Defines a type of MSU.
Definition: msu_type.h:46
Interfaces for the creation and modification of the data-flow-graph and and general description of th...
struct msu_msg_hdr hdr
Definition: msu_message.h:102
static int parse_init_cache_payload(char *to_parse, struct ws_cache_state *cache_state)
Definition: cache_msu.c:102
int increment_stat(enum stat_id stat_id, unsigned int item_id, double value)
Increments the given statistic by the provided value.
Definition: rt_stats.c:389
char init_data[64]
Definition: dfg.h:67
int generate_header(char *dest, int code, int capacity, int body_len, char *mime_type)
Definition: httpops.c:88
A message that is to be delivered to an instance of an MSU.
Definition: msu_message.h:101
struct cached_file * check_cache(struct ws_cache_state *fc, char *path)
Definition: cache_msu.c:76
Messages passed to MSUs.
#define MAX_HEADER_LEN
char * name
Name for the msu type.
Definition: msu_type.h:48
#define HASH_DEL(head, delptr)
Definition: uthash.h:411
int url_to_path(char *url, char *dir, char *path, int capacity)
Definition: httpops.c:62
#define log_warn(fmt,...)
Definition: logging.h:113
char * contents
Definition: cache_msu.c:58
#define DEFAULT_MAX_FILES
Definition: cache_msu.c:43
Declares strategies that MSUs can use for routing to endpoints.