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