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 keyvalue http_methods[] = { 29 { HTTP_METHOD_GET, CONST_LEN_STR("GET") }, 30 { HTTP_METHOD_HEAD, CONST_LEN_STR("HEAD") }, 31 { HTTP_METHOD_POST, CONST_LEN_STR("POST") }, 32 { HTTP_METHOD_PUT, CONST_LEN_STR("PUT") }, 33 { HTTP_METHOD_DELETE, CONST_LEN_STR("DELETE") }, 34 { HTTP_METHOD_CONNECT, CONST_LEN_STR("CONNECT") }, 35 { HTTP_METHOD_OPTIONS, CONST_LEN_STR("OPTIONS") }, 36 { HTTP_METHOD_TRACE, CONST_LEN_STR("TRACE") }, 37 { HTTP_METHOD_ACL, CONST_LEN_STR("ACL") }, 38 { HTTP_METHOD_BASELINE_CONTROL, CONST_LEN_STR("BASELINE-CONTROL") }, 39 { HTTP_METHOD_BIND, CONST_LEN_STR("BIND") }, 40 { HTTP_METHOD_CHECKIN, CONST_LEN_STR("CHECKIN") }, 41 { HTTP_METHOD_CHECKOUT, CONST_LEN_STR("CHECKOUT") }, 42 { HTTP_METHOD_COPY, CONST_LEN_STR("COPY") }, 43 { HTTP_METHOD_LABEL, CONST_LEN_STR("LABEL") }, 44 { HTTP_METHOD_LINK, CONST_LEN_STR("LINK") }, 45 { HTTP_METHOD_LOCK, CONST_LEN_STR("LOCK") }, 46 { HTTP_METHOD_MERGE, CONST_LEN_STR("MERGE") }, 47 { HTTP_METHOD_MKACTIVITY, CONST_LEN_STR("MKACTIVITY") }, 48 { HTTP_METHOD_MKCALENDAR, CONST_LEN_STR("MKCALENDAR") }, 49 { HTTP_METHOD_MKCOL, CONST_LEN_STR("MKCOL") }, 50 { HTTP_METHOD_MKREDIRECTREF, CONST_LEN_STR("MKREDIRECTREF") }, 51 { HTTP_METHOD_MKWORKSPACE, CONST_LEN_STR("MKWORKSPACE") }, 52 { HTTP_METHOD_MOVE, CONST_LEN_STR("MOVE") }, 53 { HTTP_METHOD_ORDERPATCH, CONST_LEN_STR("ORDERPATCH") }, 54 { HTTP_METHOD_PATCH, CONST_LEN_STR("PATCH") }, 55 { HTTP_METHOD_PROPFIND, CONST_LEN_STR("PROPFIND") }, 56 { HTTP_METHOD_PROPPATCH, CONST_LEN_STR("PROPPATCH") }, 57 { HTTP_METHOD_REBIND, CONST_LEN_STR("REBIND") }, 58 { HTTP_METHOD_REPORT, CONST_LEN_STR("REPORT") }, 59 { HTTP_METHOD_SEARCH, CONST_LEN_STR("SEARCH") }, 60 { HTTP_METHOD_UNBIND, CONST_LEN_STR("UNBIND") }, 61 { HTTP_METHOD_UNCHECKOUT, CONST_LEN_STR("UNCHECKOUT") }, 62 { HTTP_METHOD_UNLINK, CONST_LEN_STR("UNLINK") }, 63 { HTTP_METHOD_UNLOCK, CONST_LEN_STR("UNLOCK") }, 64 { HTTP_METHOD_UPDATE, CONST_LEN_STR("UPDATE") }, 65 { HTTP_METHOD_UPDATEREDIRECTREF, CONST_LEN_STR("UPDATEREDIRECTREF") }, 66 { HTTP_METHOD_VERSION_CONTROL, CONST_LEN_STR("VERSION-CONTROL") }, 67 68 { HTTP_METHOD_PRI, CONST_LEN_STR("PRI") }, 69 { HTTP_METHOD_UNSET, 0, NULL } 70 }; 71 72 static const keyvalue http_status[] = { 73 { 100, CONST_LEN_STR("100 Continue") }, 74 { 101, CONST_LEN_STR("101 Switching Protocols") }, 75 { 102, CONST_LEN_STR("102 Processing") }, /* WebDAV */ 76 { 200, CONST_LEN_STR("200 OK") }, 77 { 201, CONST_LEN_STR("201 Created") }, 78 { 202, CONST_LEN_STR("202 Accepted") }, 79 { 203, CONST_LEN_STR("203 Non-Authoritative Information") }, 80 { 204, CONST_LEN_STR("204 No Content") }, 81 { 205, CONST_LEN_STR("205 Reset Content") }, 82 { 206, CONST_LEN_STR("206 Partial Content") }, 83 { 207, CONST_LEN_STR("207 Multi-status") }, /* WebDAV */ 84 { 300, CONST_LEN_STR("300 Multiple Choices") }, 85 { 301, CONST_LEN_STR("301 Moved Permanently") }, 86 { 302, CONST_LEN_STR("302 Found") }, 87 { 303, CONST_LEN_STR("303 See Other") }, 88 { 304, CONST_LEN_STR("304 Not Modified") }, 89 { 305, CONST_LEN_STR("305 Use Proxy") }, 90 { 306, CONST_LEN_STR("306 (Unused)") }, 91 { 307, CONST_LEN_STR("307 Temporary Redirect") }, 92 { 308, CONST_LEN_STR("308 Permanent Redirect") }, 93 { 400, CONST_LEN_STR("400 Bad Request") }, 94 { 401, CONST_LEN_STR("401 Unauthorized") }, 95 { 402, CONST_LEN_STR("402 Payment Required") }, 96 { 403, CONST_LEN_STR("403 Forbidden") }, 97 { 404, CONST_LEN_STR("404 Not Found") }, 98 { 405, CONST_LEN_STR("405 Method Not Allowed") }, 99 { 406, CONST_LEN_STR("406 Not Acceptable") }, 100 { 407, CONST_LEN_STR("407 Proxy Authentication Required") }, 101 { 408, CONST_LEN_STR("408 Request Timeout") }, 102 { 409, CONST_LEN_STR("409 Conflict") }, 103 { 410, CONST_LEN_STR("410 Gone") }, 104 { 411, CONST_LEN_STR("411 Length Required") }, 105 { 412, CONST_LEN_STR("412 Precondition Failed") }, 106 { 413, CONST_LEN_STR("413 Request Entity Too Large") }, 107 { 414, CONST_LEN_STR("414 Request-URI Too Long") }, 108 { 415, CONST_LEN_STR("415 Unsupported Media Type") }, 109 { 416, CONST_LEN_STR("416 Requested Range Not Satisfiable") }, 110 { 417, CONST_LEN_STR("417 Expectation Failed") }, 111 { 421, CONST_LEN_STR("421 Misdirected Request") }, /* RFC 7540 */ 112 { 422, CONST_LEN_STR("422 Unprocessable Entity") }, /* WebDAV */ 113 { 423, CONST_LEN_STR("423 Locked") }, /* WebDAV */ 114 { 424, CONST_LEN_STR("424 Failed Dependency") }, /* WebDAV */ 115 { 426, CONST_LEN_STR("426 Upgrade Required") }, /* TLS */ 116 { 500, CONST_LEN_STR("500 Internal Server Error") }, 117 { 501, CONST_LEN_STR("501 Not Implemented") }, 118 { 502, CONST_LEN_STR("502 Bad Gateway") }, 119 { 503, CONST_LEN_STR("503 Service Not Available") }, 120 { 504, CONST_LEN_STR("504 Gateway Timeout") }, 121 { 505, CONST_LEN_STR("505 HTTP Version Not Supported") }, 122 { 507, CONST_LEN_STR("507 Insufficient Storage") }, /* WebDAV */ 123 124 { -1, 0, NULL } 125 }; 126 127 128 static const char *keyvalue_get_value(const keyvalue *kv, int k) { 129 int i; 130 for (i = 0; kv[i].value; i++) { 131 if (kv[i].key == k) return kv[i].value; 132 } 133 return NULL; 134 } 135 136 static int keyvalue_get_key(const keyvalue *kv, const char *s, unsigned int slen) { 137 for (int i = 0; kv[i].vlen; ++i) { 138 if (kv[i].vlen == slen && 0 == memcmp(kv[i].value, s, slen)) 139 return kv[i].key; 140 } 141 return -1; 142 } 143 144 145 const char *get_http_version_name(int i) { 146 return keyvalue_get_value(http_versions, i); 147 } 148 149 const char *get_http_status_name(int i) { 150 return keyvalue_get_value(http_status, i); 151 } 152 153 const char *get_http_method_name(http_method_t i) { 154 return keyvalue_get_value(http_methods, i); 155 } 156 157 int get_http_version_key(const char *s, size_t slen) { 158 return keyvalue_get_key(http_versions, s, (unsigned int)slen); 159 } 160 161 http_method_t get_http_method_key(const char *s, size_t slen) { 162 return (http_method_t)keyvalue_get_key(http_methods, s, (unsigned int)slen); 163 } 164 165 166 void http_status_append(buffer * const b, const int status) { 167 if (200 == status) { /*(short-circuit common case)*/ 168 buffer_append_string_len(b, CONST_STR_LEN("200 OK")); 169 return; 170 } 171 172 const keyvalue * const kv = http_status; 173 int i; 174 for (i = 0; kv[i].key != status && kv[i].vlen; ++i) ; 175 if (kv[i].vlen) { 176 buffer_append_string_len(b, kv[i].value, kv[i].vlen); 177 } 178 else { 179 buffer_append_int(b, status); 180 buffer_append_string_len(b, CONST_STR_LEN(" ")); 181 } 182 } 183 184 void http_method_append(buffer * const b, const http_method_t method) { 185 const keyvalue * const kv = http_methods; 186 int i; 187 for (i = 0; kv[i].key != method && kv[i].vlen; ++i) ; 188 if (kv[i].vlen) { 189 buffer_append_string_len(b, kv[i].value, kv[i].vlen); 190 } 191 } 192 193 void http_version_append(buffer * const b, const http_version_t version) { 194 const keyvalue * const kv = http_versions; 195 int i; 196 for (i = 0; kv[i].key != version && kv[i].vlen; ++i) ; 197 if (kv[i].vlen) { 198 buffer_append_string_len(b, kv[i].value, kv[i].vlen); 199 } 200 } 201