xref: /lighttpd1.4/src/http_kv.c (revision 70b1af06)
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