bunkum

an old and silly c99 web server with some fun features
Log | Files | Refs | README

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