|
1 #include <sys/types.h> |
|
2 #include <sys/stat.h> |
|
3 #include <dirent.h> |
|
4 #include <fcntl.h> |
|
5 #include <unistd.h> |
|
6 #include <sys/mman.h> |
|
7 #include <stdio.h> |
|
8 #include <string.h> |
|
9 |
|
10 #include <event2/http.h> |
|
11 #include <event2/buffer.h> |
|
12 |
|
13 #include "static.h" |
|
14 #include "common.h" |
|
15 |
|
16 static void _static_hit (struct evhttp_request *request, void *arg) { |
|
17 struct static_file *ctx = arg; |
|
18 struct evbuffer *buf = NULL; |
|
19 int err = -1; |
|
20 |
|
21 // create the new buffer |
|
22 if ((buf = evbuffer_new()) == NULL) |
|
23 ERROR("evbuffer_new"); |
|
24 |
|
25 // XXX: this is so very, very wasteful... |
|
26 if (evbuffer_add(buf, ctx->mmap_addr, ctx->size)) |
|
27 ERROR("evbuffer_add %zu", ctx->size); |
|
28 |
|
29 // send the reply... |
|
30 evhttp_send_reply(request, HTTP_OK, "OK", buf); |
|
31 |
|
32 err = 0; |
|
33 |
|
34 error : |
|
35 // free the buffer |
|
36 if (buf) |
|
37 evbuffer_free(buf); |
|
38 |
|
39 if (err) |
|
40 evhttp_send_error(request, 505, "Internal Server Error"); |
|
41 } |
|
42 |
|
43 int static_init (struct static_file *ctx, struct evhttp *http_server, const char *url, const char *path) { |
|
44 struct stat stat_buf; |
|
45 |
|
46 // clear the ctx |
|
47 memset(ctx, 0, sizeof(*ctx)); |
|
48 |
|
49 // open the file... |
|
50 if ((ctx->fh = open(path, O_RDONLY)) == -1) |
|
51 PERROR("open(O_RDONLY): %s", path); |
|
52 |
|
53 // stat the file to get its size |
|
54 if (fstat(ctx->fh, &stat_buf)) |
|
55 PERROR("fstat: %s", path); |
|
56 |
|
57 ctx->size = stat_buf.st_size; |
|
58 |
|
59 // then mmap the file. MAP_POPULATE so that we block now, not later... |
|
60 if ((ctx->mmap_addr = mmap(NULL, ctx->size, PROT_READ, MAP_SHARED | MAP_POPULATE, ctx->fh, 0)) == MAP_FAILED) |
|
61 PERROR("mmap: %s 0 -> %zu", path, ctx->size); |
|
62 |
|
63 // finally, add the HTTP request handler |
|
64 evhttp_set_cb(http_server, url, &_static_hit, ctx); |
|
65 |
|
66 INFO("%s -> %s", url, path); |
|
67 |
|
68 // return |
|
69 return 0; |
|
70 |
|
71 error : |
|
72 static_deinit(ctx); |
|
73 |
|
74 return -1; |
|
75 } |
|
76 |
|
77 void static_deinit (struct static_file *ctx) { |
|
78 if (ctx->fh) |
|
79 if (close(ctx->fh)) |
|
80 PWARNING("close"); |
|
81 |
|
82 if (ctx->mmap_addr && ctx->mmap_addr != MAP_FAILED) |
|
83 if (munmap(ctx->mmap_addr, ctx->size)) |
|
84 PWARNING("munmap"); |
|
85 } |
|
86 |
|
87 int static_dir_init (struct static_dir *ctx, struct evhttp *http_server, const char *url_prefix, const char *dir_path) { |
|
88 DIR *dir = NULL; |
|
89 struct dirent *ent; |
|
90 int i = 0; |
|
91 char url_buf[STATIC_PATH_MAX], path_buf[STATIC_PATH_MAX]; |
|
92 |
|
93 // init the ctx |
|
94 ctx->file_count = 0; |
|
95 |
|
96 // open the dir |
|
97 if ((dir = opendir(dir_path)) == NULL) |
|
98 PERROR("opendir: %s", dir_path); |
|
99 |
|
100 // scan the files |
|
101 while ((ent = readdir(dir)) != NULL) { |
|
102 // skip dotfiles |
|
103 if (ent->d_name[0] == '.') |
|
104 continue; |
|
105 |
|
106 // check we still have space |
|
107 if (i >= STATIC_DIR_MAX) { |
|
108 PWARNING("static_dir is full: %d", i); |
|
109 break; |
|
110 } |
|
111 |
|
112 // format the url/path bufs |
|
113 snprintf(url_buf, STATIC_PATH_MAX, "%s/%s", url_prefix, ent->d_name); |
|
114 snprintf(path_buf, STATIC_PATH_MAX, "%s/%s", dir_path, ent->d_name); |
|
115 |
|
116 // init the static_file |
|
117 static_init(&ctx->files[i], http_server, url_buf, path_buf); |
|
118 |
|
119 // remember how many files are in use and move on to the next one |
|
120 ctx->file_count = ++i; |
|
121 } |
|
122 |
|
123 |
|
124 // ok |
|
125 return 0; |
|
126 |
|
127 error: |
|
128 static_dir_deinit(ctx); |
|
129 |
|
130 return -1; |
|
131 } |
|
132 |
|
133 void static_dir_deinit (struct static_dir *ctx) { |
|
134 for (int i = 0; i < ctx->file_count; i++) { |
|
135 static_deinit(&ctx->files[i]); |
|
136 } |
|
137 } |