http-server.c (6427B)
1 #if defined(_WIN32) || defined(_WIN64) 2 #include <windows.h> 3 #else 4 #include <time.h> 5 #include <unistd.h> 6 #endif 7 8 #include <stdbool.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 13 #include "finwo/http-parser.h" 14 #include "finwo/fnet.h" 15 16 #include "http-server.h" 17 18 static void sleep_ms(long ms) { 19 #if defined(__APPLE__) 20 usleep(ms * 1000); 21 #elif defined(_WIN32) || defined(_WIN64) 22 Sleep(ms); 23 #else 24 time_t sec = (int)(ms / 1000); 25 const long t = ms -(sec * 1000); 26 struct timespec req; 27 req.tv_sec = sec; 28 req.tv_nsec = t * 1000000L; 29 while(-1 == nanosleep(&req, &req)); 30 #endif 31 } 32 33 struct fnet_udata { 34 struct http_server_opts *opts; 35 struct fnet_options_t *fnet_opts; 36 }; 37 38 struct hs_route { 39 void *next; 40 const char *method; 41 char **path; 42 void (*fn)(struct http_server_reqdata*); 43 }; 44 45 struct hs_route *registered_routes = NULL; 46 47 char ** _hs_pathTokens(const char *path) { 48 char **output = calloc(strlen(path), sizeof(char*)); 49 50 int token_count = 0; 51 char *dupped = strdup(path); 52 char *token = strtok(dupped, "/"); 53 while(token != NULL) { 54 output[token_count++] = strdup(token); 55 token = strtok(NULL, "/"); 56 } 57 free(dupped); 58 59 return output; 60 } 61 62 void _hs_onServing(struct fnet_ev *ev) { 63 struct fnet_udata *ludata = ev->udata; 64 65 if (ludata->opts->evs && ludata->opts->evs->serving) { 66 ludata->opts->evs->serving(ludata->opts->addr, ludata->opts->port, ludata->opts->udata); 67 } 68 } 69 70 void _hs_onTick(struct fnet_ev *ev) { 71 struct fnet_udata *ludata = ev->udata; 72 73 if (ludata->opts->evs && ludata->opts->evs->tick) { 74 ludata->opts->evs->tick(ludata->opts->udata); 75 } 76 } 77 78 static void _hs_onRequest(struct http_parser_event *ev) { 79 struct http_server_reqdata *reqdata = ev->udata; 80 struct hs_route *route = registered_routes; 81 struct hs_route *selected_route = NULL; 82 83 // Tokenize the given path only once 84 char **pathTokens = _hs_pathTokens(ev->request->path); 85 char **routeTokens; 86 char *meta = calloc(strlen(ev->request->path), sizeof(char)); 87 int i; 88 89 // Method/path matching 90 while(route) { 91 92 // Skip route if the method doesn't match 93 if (strcmp(ev->request->method, route->method)) { 94 route = route->next; 95 continue; // Continues while(route) 96 } 97 98 // Checking if the path matches 99 routeTokens = route->path; 100 i = 0; 101 102 while((pathTokens[i] && routeTokens[i])) { 103 if (routeTokens[i][0] == ':') { i++; continue; } 104 if (strcmp(pathTokens[i], routeTokens[i])) { 105 i = -1; 106 break; // Breaks token-checking 107 } 108 i++; 109 } 110 111 // Content mismatch 112 if (i == -1) { 113 route = route->next; 114 continue; // Continues while(route) 115 } 116 117 // Length mismatch 118 if (pathTokens[i] || routeTokens[i]) { 119 route = route->next; 120 continue; 121 } 122 123 // Here = route match 124 125 // Store url params as meta 'param:<name>' = 'path[i] 126 i = 0; 127 while((pathTokens[i] && routeTokens[i])) { 128 if (routeTokens[i][0] != ':') { i++; continue; } 129 meta[0] = '\0'; 130 strcat(meta, "param:"); 131 strcat(meta, routeTokens[i]+1); 132 http_parser_meta_set(ev->request, meta, pathTokens[i]); 133 i++; 134 } 135 136 selected_route = route; 137 break; 138 } 139 140 if (!selected_route) { 141 if (reqdata->evs && reqdata->evs->notFound) { 142 reqdata->evs->notFound(reqdata); 143 return; 144 } else { 145 fnet_close(reqdata->connection); 146 return; 147 } 148 } 149 150 // Call the route handler 151 selected_route->fn(reqdata); 152 } 153 154 void _hs_onData(struct fnet_ev *ev) { 155 struct http_server_reqdata *reqdata = ev->udata; 156 http_parser_pair_request_data(reqdata->reqres, ev->buffer); 157 } 158 159 void _hs_onClose(struct fnet_ev *ev) { 160 struct http_server_reqdata *reqdata = ev->udata; 161 162 if (reqdata->evs && reqdata->evs->close) { 163 reqdata->evs->close(reqdata); 164 } 165 166 http_parser_pair_free(reqdata->reqres); 167 free(reqdata); 168 } 169 170 void _hs_onConnect(struct fnet_ev *ev) { 171 struct fnet_t *conn = ev->connection; 172 struct fnet_udata *ludata = ev->udata; 173 174 // Setup new request/response pair 175 struct http_server_reqdata *reqdata = malloc(sizeof(struct http_server_reqdata)); 176 reqdata->connection = conn; 177 reqdata->reqres = http_parser_pair_init(reqdata); 178 reqdata->reqres->onRequest = _hs_onRequest; 179 reqdata->evs = ludata->opts->evs; 180 reqdata->udata = ludata->opts->udata; 181 ev->connection->udata = reqdata; 182 183 // Setup data flowing from connection into reqres 184 ev->connection->onData = _hs_onData; 185 ev->connection->onClose = _hs_onClose; 186 } 187 188 void http_server_response_send(struct http_server_reqdata *reqdata, bool close) { 189 struct buf *response_buffer = http_parser_sprint_response(reqdata->reqres->response); 190 fnet_write(reqdata->connection, response_buffer); 191 buf_clear(response_buffer); 192 free(response_buffer); 193 if (close) { 194 fnet_close(reqdata->connection); 195 } 196 } 197 198 void http_server_route(const char *method, const char *path, void (*fn)(struct http_server_reqdata*)) { 199 struct hs_route *route = malloc(sizeof(struct hs_route)); 200 route->next = registered_routes; 201 route->method = method; 202 route->fn = fn; 203 route->path = _hs_pathTokens(path); 204 registered_routes = route; 205 } 206 207 void _hs_onListenClose(struct fnet_ev *ev) { 208 struct fnet_udata *ludata = ev->udata; 209 if (!ludata->opts->shutdown) { 210 ludata->opts->listen_connection = fnet_listen(ludata->opts->addr, ludata->opts->port, ludata->fnet_opts); 211 } 212 } 213 214 void http_server_main(struct http_server_opts *opts) { 215 int ret; 216 if (!opts) exit(1); 217 opts->shutdown = false; 218 219 // Prepare http context 220 struct fnet_udata *ludata = calloc(1, sizeof(struct fnet_udata)); 221 ludata->opts = opts; 222 223 // Prepare network options 224 struct fnet_options_t fnet_opts = { 225 .proto = FNET_PROTO_TCP, 226 .flags = 0, 227 .onListen = _hs_onServing, 228 .onConnect = _hs_onConnect, 229 .onData = NULL, 230 .onTick = _hs_onTick, 231 .onClose = _hs_onListenClose, 232 .udata = ludata, 233 }; 234 235 // Track network options in http context 236 ludata->fnet_opts = &fnet_opts; 237 238 // Signal that we want our port 239 ludata->opts->listen_connection = fnet_listen(ludata->opts->addr, ludata->opts->port, ludata->fnet_opts); 240 if (!(ludata->opts->listen_connection)) { 241 exit(1); 242 } 243 244 // This is a forever function, controlled by network thread 245 while(!opts->shutdown) { 246 ret = fnet_main(); 247 if (ret) exit(ret); 248 sleep_ms(100); 249 } 250 }