1 /* 2 * http_kv - HTTP version, method, status key-value string mapping 3 * 4 * Fully-rewritten from original 5 * Copyright(c) 2018 Glenn Strauss gstrauss()gluelogic.com All rights reserved 6 * License: BSD 3-clause (same as lighttpd) 7 */ 8 #include "first.h" 9 10 #include "http_kv.h" 11 #include "buffer.h" 12 13 #include <string.h> 14 15 typedef struct { 16 int key; 17 unsigned int vlen; 18 const char *value; 19 } keyvalue; 20 21 static const keyvalue http_versions[] = { 22 { HTTP_VERSION_2, CONST_LEN_STR("HTTP/2.0") }, /* SERVER_PROTOCOL */ 23 { HTTP_VERSION_1_1, CONST_LEN_STR("HTTP/1.1") }, 24 { HTTP_VERSION_1_0, CONST_LEN_STR("HTTP/1.0") }, 25 { HTTP_VERSION_UNSET, 0, NULL } 26 }; 27 28 static const buffer http_methods[] = { 29 { CONST_STR_LEN("GET")+1, 0 }, 30 { CONST_STR_LEN("HEAD")+1, 0 }, 31 { CONST_STR_LEN("QUERY")+1, 0 }, 32 { CONST_STR_LEN("POST")+1, 0 }, 33 { CONST_STR_LEN("PUT")+1, 0 }, 34 { CONST_STR_LEN("DELETE")+1, 0 }, 35 { CONST_STR_LEN("CONNECT")+1, 0 }, 36 { CONST_STR_LEN("OPTIONS")+1, 0 }, 37 { CONST_STR_LEN("TRACE")+1, 0 }, 38 { CONST_STR_LEN("ACL")+1, 0 }, 39 { CONST_STR_LEN("BASELINE-CONTROL")+1, 0 }, 40 { CONST_STR_LEN("BIND")+1, 0 }, 41 { CONST_STR_LEN("CHECKIN")+1, 0 }, 42 { CONST_STR_LEN("CHECKOUT")+1, 0 }, 43 { CONST_STR_LEN("COPY")+1, 0 }, 44 { CONST_STR_LEN("LABEL")+1, 0 }, 45 { CONST_STR_LEN("LINK")+1, 0 }, 46 { CONST_STR_LEN("LOCK")+1, 0 }, 47 { CONST_STR_LEN("MERGE")+1, 0 }, 48 { CONST_STR_LEN("MKACTIVITY")+1, 0 }, 49 { CONST_STR_LEN("MKCALENDAR")+1, 0 }, 50 { CONST_STR_LEN("MKCOL")+1, 0 }, 51 { CONST_STR_LEN("MKREDIRECTREF")+1, 0 }, 52 { CONST_STR_LEN("MKWORKSPACE")+1, 0 }, 53 { CONST_STR_LEN("MOVE")+1, 0 }, 54 { CONST_STR_LEN("ORDERPATCH")+1, 0 }, 55 { CONST_STR_LEN("PATCH")+1, 0 }, 56 { CONST_STR_LEN("PROPFIND")+1, 0 }, 57 { CONST_STR_LEN("PROPPATCH")+1, 0 }, 58 { CONST_STR_LEN("REBIND")+1, 0 }, 59 { CONST_STR_LEN("REPORT")+1, 0 }, 60 { CONST_STR_LEN("SEARCH")+1, 0 }, 61 { CONST_STR_LEN("UNBIND")+1, 0 }, 62 { CONST_STR_LEN("UNCHECKOUT")+1, 0 }, 63 { CONST_STR_LEN("UNLINK")+1, 0 }, 64 { CONST_STR_LEN("UNLOCK")+1, 0 }, 65 { CONST_STR_LEN("UPDATE")+1, 0 }, 66 { CONST_STR_LEN("UPDATEREDIRECTREF")+1, 0 }, 67 { CONST_STR_LEN("VERSION-CONTROL")+1, 0 }, 68 69 { CONST_STR_LEN("PRI")+1, 0 }, 70 { "", 0, 0 } 71 }; 72 73 /* https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml */ 74 static const keyvalue http_status[] = { 75 { 100, CONST_LEN_STR("100 Continue") }, 76 { 101, CONST_LEN_STR("101 Switching Protocols") }, 77 { 102, CONST_LEN_STR("102 Processing") }, /* WebDAV */ 78 { 103, CONST_LEN_STR("103 Early Hints") }, 79 { 200, CONST_LEN_STR("200 OK") }, 80 { 201, CONST_LEN_STR("201 Created") }, 81 { 202, CONST_LEN_STR("202 Accepted") }, 82 { 203, CONST_LEN_STR("203 Non-Authoritative Information") }, 83 { 204, CONST_LEN_STR("204 No Content") }, 84 { 205, CONST_LEN_STR("205 Reset Content") }, 85 { 206, CONST_LEN_STR("206 Partial Content") }, 86 { 207, CONST_LEN_STR("207 Multi-status") }, /* WebDAV */ 87 { 208, CONST_LEN_STR("208 Already Reported") }, 88 { 226, CONST_LEN_STR("226 IM Used") }, 89 { 300, CONST_LEN_STR("300 Multiple Choices") }, 90 { 301, CONST_LEN_STR("301 Moved Permanently") }, 91 { 302, CONST_LEN_STR("302 Found") }, 92 { 303, CONST_LEN_STR("303 See Other") }, 93 { 304, CONST_LEN_STR("304 Not Modified") }, 94 { 305, CONST_LEN_STR("305 Use Proxy") }, 95 { 306, CONST_LEN_STR("306 (Unused)") }, 96 { 307, CONST_LEN_STR("307 Temporary Redirect") }, 97 { 308, CONST_LEN_STR("308 Permanent Redirect") }, 98 { 400, CONST_LEN_STR("400 Bad Request") }, 99 { 401, CONST_LEN_STR("401 Unauthorized") }, 100 { 402, CONST_LEN_STR("402 Payment Required") }, 101 { 403, CONST_LEN_STR("403 Forbidden") }, 102 { 404, CONST_LEN_STR("404 Not Found") }, 103 { 405, CONST_LEN_STR("405 Method Not Allowed") }, 104 { 406, CONST_LEN_STR("406 Not Acceptable") }, 105 { 407, CONST_LEN_STR("407 Proxy Authentication Required") }, 106 { 408, CONST_LEN_STR("408 Request Timeout") }, 107 { 409, CONST_LEN_STR("409 Conflict") }, 108 { 410, CONST_LEN_STR("410 Gone") }, 109 { 411, CONST_LEN_STR("411 Length Required") }, 110 { 412, CONST_LEN_STR("412 Precondition Failed") }, 111 { 413, CONST_LEN_STR("413 Payload Too Large") }, 112 { 414, CONST_LEN_STR("414 URI Too Long") }, 113 { 415, CONST_LEN_STR("415 Unsupported Media Type") }, 114 { 416, CONST_LEN_STR("416 Range Not Satisfiable") }, 115 { 417, CONST_LEN_STR("417 Expectation Failed") }, 116 { 421, CONST_LEN_STR("421 Misdirected Request") }, /* RFC 7540 */ 117 { 422, CONST_LEN_STR("422 Unprocessable Entity") }, /* WebDAV */ 118 { 423, CONST_LEN_STR("423 Locked") }, /* WebDAV */ 119 { 424, CONST_LEN_STR("424 Failed Dependency") }, /* WebDAV */ 120 { 426, CONST_LEN_STR("426 Upgrade Required") }, /* TLS */ 121 { 428, CONST_LEN_STR("428 Precondition Required") }, 122 { 429, CONST_LEN_STR("429 Too Many Requests") }, 123 { 431, CONST_LEN_STR("431 Request Header Fields Too Large") }, 124 { 451, CONST_LEN_STR("451 Unavailable For Legal Reasons") }, 125 { 500, CONST_LEN_STR("500 Internal Server Error") }, 126 { 501, CONST_LEN_STR("501 Not Implemented") }, 127 { 502, CONST_LEN_STR("502 Bad Gateway") }, 128 { 503, CONST_LEN_STR("503 Service Unavailable") }, 129 { 504, CONST_LEN_STR("504 Gateway Timeout") }, 130 { 505, CONST_LEN_STR("505 HTTP Version Not Supported") }, 131 { 506, CONST_LEN_STR("506 Variant Also Negotiates") }, 132 { 507, CONST_LEN_STR("507 Insufficient Storage") }, /* WebDAV */ 133 { 508, CONST_LEN_STR("508 Loop Detected") }, 134 { 510, CONST_LEN_STR("510 Not Extended") }, 135 { 511, CONST_LEN_STR("511 Network Authentication Required") }, 136 137 { -1, 0, NULL } 138 }; 139 140 141 const buffer *http_method_buf (http_method_t i) 142 { 143 return ((unsigned int)i < sizeof(http_methods)/sizeof(*http_methods)-2) 144 ? http_methods+i 145 : http_methods+i+sizeof(http_methods)/sizeof(*http_methods); 146 /* HTTP_METHOD_PRI is -2, HTTP_METHOD_UNSET is -1 */ 147 } 148 149 150 __attribute_noinline__ 151 __attribute_pure__ 152 static const keyvalue * keyvalue_from_key (const keyvalue *kv, const int k) 153 { 154 /*(expects sentinel to have key == -1 and value == NULL)*/ 155 while (kv->key != k && kv->key != -1) ++kv; 156 return kv; 157 } 158 159 160 #if 0 /*(unused)*/ 161 __attribute_pure__ 162 static int keyvalue_get_key(const keyvalue *kv, const char * const s, const unsigned int slen) 163 { 164 /*(expects sentinel to have key == -1 and vlen == 0)*/ 165 while (kv->vlen && (kv->vlen != slen || 0 != memcmp(kv->value, s, slen))) 166 ++kv; 167 return kv->key; 168 } 169 #endif 170 171 172 const char *get_http_version_name(int i) { 173 return keyvalue_from_key(http_versions, i)->value; 174 } 175 176 #if 0 /*(unused)*/ 177 const char *get_http_status_name(int i) { 178 return keyvalue_from_key(http_status, i)->value; 179 } 180 #endif 181 182 #if 0 /*(unused)*/ 183 int get_http_version_key(const char *s, size_t slen) { 184 return keyvalue_get_key(http_versions, s, (unsigned int)slen); 185 } 186 #endif 187 188 http_method_t get_http_method_key(const char *s, const size_t slen) { 189 if (slen == 3 && s[0] == 'G' && s[1] == 'E' && s[2] == 'T') 190 return HTTP_METHOD_GET; 191 const buffer *kv = http_methods+1; /*(step over http_methods[0] ("GET"))*/ 192 while (kv->used && (kv->used-1 != slen || 0 != memcmp(kv->ptr, s, slen))) 193 ++kv; 194 const uint_fast32_t i = kv - http_methods; 195 /*(not done: could overload kv->size and store enum in kv->size)*/ 196 return (i < sizeof(http_methods)/sizeof(*http_methods)-2) 197 ? (http_method_t)i 198 : i == sizeof(http_methods)/sizeof(*http_methods)-2 199 ? HTTP_METHOD_PRI 200 : HTTP_METHOD_UNSET; 201 } 202 203 204 void http_status_append(buffer * const b, const int status) { 205 if (200 == status) { /*(short-circuit common case)*/ 206 buffer_append_string_len(b, CONST_STR_LEN("200 OK")); 207 return; 208 } 209 210 const keyvalue * const kv = keyvalue_from_key(http_status, status); 211 if (__builtin_expect( (0 != kv->vlen), 1)) 212 buffer_append_string_len(b, kv->value, kv->vlen); 213 else { 214 buffer_append_int(b, status); 215 buffer_append_char(b, ' '); 216 } 217 } 218 219 void http_version_append(buffer * const b, const http_version_t version) { 220 const keyvalue * const kv = keyvalue_from_key(http_versions, version); 221 if (__builtin_expect( (0 != kv->vlen), 1)) 222 buffer_append_string_len(b, kv->value, kv->vlen); 223 } 224