request.c (4856B)
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdint.h> 4 #include <stdlib.h> 5 6 #include "../utils/log.h" 7 #include "request.h" 8 9 const char* method_names[] = 10 {"GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE"}; 11 12 const char* method_name(enum http_method m) { 13 return method_names[m]; 14 } 15 16 const enum http_method method_codes[] = 17 {DELETE, CONNECT, PUT, POST, TRACE, HEAD, OPTIONS, GET}; 18 enum http_method method_enum(char* p) { 19 return method_codes[((*(uint64_t*)p*0x1b8b6e6d) % 0x100000000) >> 28]; 20 } 21 22 // Be careful about passing by value! if the internal hashmap resizes 23 // a req_free() call on the original (the one copied *from*) will be a 24 // double free() error 25 request_t req_new(char* reqbuf, size_t bufsize) { 26 request_t req; 27 req.buf = reqbuf; 28 req.bufsize = bufsize; 29 req.headers = hashmap_new(MAX_HEADER_NAME, MAX_HEADER_VALUE); 30 req.headers.vark = true; 31 return req; 32 } 33 34 int cmp_r_mimetype(const void* a, const void* b) { 35 struct req_mimetype* aa = (struct req_mimetype*)a; 36 struct req_mimetype* bb = (struct req_mimetype*)b; 37 if (aa->q > bb->q) return -1; 38 else if (aa->q == bb->q) return 0; 39 else if (aa->q < bb->q) return 1; 40 return 0; // unreachable? 41 } 42 43 // TODO handle parse errors 44 vec_t hdr_parse_accept(char* val) { 45 vec_t mimetypes = vec_new(sizeof(struct req_mimetype)); 46 char* start = strtok(val, ","); 47 char* next; 48 do { 49 next = strtok(NULL, ","); 50 float q = 1; 51 char* qptr = NULL; 52 53 char* end = next != NULL ? next : start+strlen(start); 54 if ((qptr = (char*)memchr(start, ';', end-start))) { 55 sscanf(qptr, ";q=%f", &q); 56 } 57 struct req_mimetype mtype; 58 mtype.q = q; 59 memset(mtype.item, 0, MAX_MIMETYPE_LEN); 60 if (qptr != NULL) strncpy(mtype.item, start, qptr-start); 61 else if (next != NULL) strncpy(mtype.item, start, next-start); 62 else strcpy(mtype.item, start); 63 vec_push(&mimetypes, &mtype); 64 } while ((start = next)); 65 vec_sort(&mimetypes, cmp_r_mimetype); 66 return mimetypes; 67 } 68 69 struct mime_type parse_mimetype(char* str) { 70 char* slash = strchr(str, '/'); 71 // if (slash == NULL) handle_error(); 72 char* subtype = slash + 1; 73 } 74 75 76 77 int req_parse(request_t* req) { 78 char method[8] = {0}; 79 int matched = sscanf(req->buf, "%s %s HTTP/%f\r\n", 80 (char*)method, (char*)req->path, &req->ver); 81 if (matched < 3 || matched == EOF) return -1; 82 req->method = method_enum((char*)method); 83 84 // FIXME if start is null this causes problems 85 char* start = memchr(req->buf, '\n', MAX_HEADER_NAME+MAX_HEADER_VALUE)+1; 86 while (start+MAX_HEADER_NAME+MAX_HEADER_VALUE < req->buf+req->bufsize) { 87 char name[MAX_HEADER_NAME] = {0}; 88 char value[MAX_HEADER_VALUE] = {0}; 89 int matched = sscanf(start, "%32[^:]: %s", name, value); 90 if (matched == 0) return 0; // No more headers; 91 else if (matched == 1) { 92 if (name[0] == '\r') return 0; // TODO sketch 93 // Uhh... half a header. NOTE doesn't even seem to work. fun. 94 return -1; 95 } 96 hashmap_set(&req->headers, name, value); 97 start = memchr(start, '\n', MAX_HEADER_NAME+MAX_HEADER_VALUE)+1; 98 } 99 return 0; 100 } 101 102 void req_free(request_t* req) { 103 hashmap_free(&req->headers); 104 } 105 106 #ifdef TEST 107 #include <stdlib.h> 108 #include "../test.h" 109 110 static void assert_method_eq(enum http_method a, enum http_method b) { 111 if (a != b) { 112 printf("(%s != %s)", method_name(a), method_name(b)); 113 exit(1); 114 } 115 } 116 117 void test_method_str_to_enum() { 118 assert_method_eq(method_enum("GET"), GET); 119 assert_method_eq(method_enum("HEAD"), HEAD); 120 assert_method_eq(method_enum("POST"), POST); 121 assert_method_eq(method_enum("PUT"), PUT); 122 assert_method_eq(method_enum("DELETE"), DELETE); 123 assert_method_eq(method_enum("CONNECT"), CONNECT); 124 assert_method_eq(method_enum("OPTIONS"), OPTIONS); 125 assert_method_eq(method_enum("TRACE"), TRACE); 126 }; 127 128 void test_hdr_parse_accept() { 129 char hdr[MAX_HEADER_VALUE] = 130 "text/html,application/xml;q=0.9,image/webp,*/*;q=0.8"; 131 vec_t mtypes = hdr_parse_accept(hdr); 132 assert_size_eq(mtypes.vec_sz, 4); 133 134 struct req_mimetype* mtype = vec_get(&mtypes, 0); 135 assert_str_eq("text/html", mtype->item); 136 assert_float_eq(1.0, mtype->q); 137 138 mtype = vec_get(&mtypes, 1); 139 assert_str_eq("image/webp", mtype->item); 140 assert_float_eq(1.0, mtype->q); 141 142 mtype = vec_get(&mtypes, 2); 143 assert_str_eq("application/xml", mtype->item); 144 assert_float_eq(0.9, mtype->q); 145 146 mtype = vec_get(&mtypes, 3); 147 assert_str_eq("*/*", mtype->item); 148 assert_float_eq(0.8, mtype->q); 149 } 150 151 void test_bad_req_total_garbage() { 152 request_t r = req_new("this is not a request.", 23); 153 assert_int_eq(-1, req_parse(&r)); 154 } 155 156 #endif