xref: /lighttpd1.4/src/http_header.c (revision 15cdc313)
1 #include "first.h"
2 
3 #include "http_header.h"
4 #include "base.h"
5 #include "array.h"
6 #include "buffer.h"
7 
8 
9 typedef struct keyvlenvalue {
10     const int key;
11     const unsigned int vlen;
12     const char * const value;
13 } keyvlenvalue;
14 
15 /* Note: must be sorted by length */
16 /* Note: must be kept in sync with http_header.h enum http_header_e */
17 #define CONST_LEN_STR(x) (unsigned int)(sizeof(x)-1), (x)
18 static const keyvlenvalue http_headers[] = {
19   { HTTP_HEADER_HOST,                 CONST_LEN_STR("Host") }
20  ,{ HTTP_HEADER_DATE,                 CONST_LEN_STR("Date") }
21  ,{ HTTP_HEADER_ETAG,                 CONST_LEN_STR("ETag") }
22  ,{ HTTP_HEADER_VARY,                 CONST_LEN_STR("Vary") }
23  ,{ HTTP_HEADER_RANGE,                CONST_LEN_STR("Range") }
24  ,{ HTTP_HEADER_COOKIE,               CONST_LEN_STR("Cookie") }
25  ,{ HTTP_HEADER_EXPECT,               CONST_LEN_STR("Expect") }
26  ,{ HTTP_HEADER_STATUS,               CONST_LEN_STR("Status") }
27  ,{ HTTP_HEADER_SERVER,               CONST_LEN_STR("Server") }
28  ,{ HTTP_HEADER_UPGRADE,              CONST_LEN_STR("Upgrade") }
29  ,{ HTTP_HEADER_LOCATION,             CONST_LEN_STR("Location") }
30  ,{ HTTP_HEADER_FORWARDED,            CONST_LEN_STR("Forwarded") }
31  ,{ HTTP_HEADER_CONNECTION,           CONST_LEN_STR("Connection") }
32  ,{ HTTP_HEADER_SET_COOKIE,           CONST_LEN_STR("Set-Cookie") }
33  ,{ HTTP_HEADER_CONTENT_TYPE,         CONST_LEN_STR("Content-Type") }
34  ,{ HTTP_HEADER_LAST_MODIFIED,        CONST_LEN_STR("Last-Modified") }
35  ,{ HTTP_HEADER_AUTHORIZATION,        CONST_LEN_STR("Authorization") }
36  ,{ HTTP_HEADER_IF_NONE_MATCH,        CONST_LEN_STR("If-None-Match") }
37  ,{ HTTP_HEADER_CACHE_CONTROL,        CONST_LEN_STR("Cache-Control") }
38  ,{ HTTP_HEADER_CONTENT_LENGTH,       CONST_LEN_STR("Content-Length") }
39  ,{ HTTP_HEADER_ACCEPT_ENCODING,      CONST_LEN_STR("Accept-Encoding") }
40  ,{ HTTP_HEADER_X_FORWARDED_FOR,      CONST_LEN_STR("X-Forwarded-For") }
41  ,{ HTTP_HEADER_CONTENT_ENCODING,     CONST_LEN_STR("Content-Encoding") }
42  ,{ HTTP_HEADER_CONTENT_LOCATION,     CONST_LEN_STR("Content-Location") }
43  ,{ HTTP_HEADER_IF_MODIFIED_SINCE,    CONST_LEN_STR("If-Modified-Since") }
44  ,{ HTTP_HEADER_TRANSFER_ENCODING,    CONST_LEN_STR("Transfer-Encoding") }
45  ,{ HTTP_HEADER_X_FORWARDED_PROTO,    CONST_LEN_STR("X-Forwarded-Proto") }
46  ,{ HTTP_HEADER_OTHER, 0, NULL }
47 };
48 
49 enum http_header_e http_header_hkey_get(const char *s, size_t slen) {
50     const struct keyvlenvalue * const kv = http_headers;
51     for (int i = 0; kv[i].vlen && slen >= kv[i].vlen; ++i) {
52         if (slen == kv[i].vlen
53             && buffer_eq_icase_ssn(s, kv[i].value, slen))
54             return (enum http_header_e)kv[i].key;
55     }
56     return HTTP_HEADER_OTHER;
57 }
58 
59 
60 buffer * http_header_response_get(connection *con, enum http_header_e id, const char *k, size_t klen) {
61     data_string * const ds =
62       (id <= HTTP_HEADER_OTHER || (con->response.htags & id))
63       ? (data_string *)array_get_element_klen(con->response.headers, k, klen)
64       : NULL;
65     return ds && !buffer_string_is_empty(ds->value) ? ds->value : NULL;
66 }
67 
68 void http_header_response_unset(connection *con, enum http_header_e id, const char *k, size_t klen) {
69     if (id <= HTTP_HEADER_OTHER || (con->response.htags & id)) {
70         if (id > HTTP_HEADER_OTHER) con->response.htags &= ~id;
71         array_set_key_value(con->response.headers, k, klen, CONST_STR_LEN(""));
72     }
73 }
74 
75 void http_header_response_set(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen) {
76     /* set value, including setting blank value if 0 == vlen
77      * (note: if 0 == vlen, header is still inserted with blank value,
78      *  which is used to indicate a "removed" header)
79      */
80     if (id > HTTP_HEADER_OTHER)
81         (vlen) ? (con->response.htags |= id) : (con->response.htags &= ~id);
82     array_set_key_value(con->response.headers, k, klen, v, vlen);
83 }
84 
85 void http_header_response_append(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen) {
86     if (vlen) {
87         data_string *ds= (id <= HTTP_HEADER_OTHER || (con->response.htags & id))
88           ? (data_string *)array_get_element_klen(con->response.headers,k,klen)
89           : NULL;
90         if (id > HTTP_HEADER_OTHER) con->response.htags |= id;
91         if (NULL == ds) {
92             array_insert_key_value(con->response.headers, k, klen, v, vlen);
93         }
94         else { /* append value */
95             buffer *vb = ds->value;
96             if (!buffer_string_is_empty(vb))
97                 buffer_append_string_len(vb, CONST_STR_LEN(", "));
98             buffer_append_string_len(vb, v, vlen);
99         }
100     }
101 }
102 
103 void http_header_response_insert(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen) {
104     if (vlen) {
105         data_string *ds= (id <= HTTP_HEADER_OTHER || (con->response.htags & id))
106           ? (data_string *)array_get_element_klen(con->response.headers,k,klen)
107           : NULL;
108         if (id > HTTP_HEADER_OTHER) con->response.htags |= id;
109         if (NULL == ds) {
110             array_insert_key_value(con->response.headers, k, klen, v, vlen);
111         }
112         else { /* append value */
113             buffer *vb = ds->value;
114             if (!buffer_string_is_empty(vb)) {
115                 buffer_append_string_len(vb, CONST_STR_LEN("\r\n"));
116                 buffer_append_string_len(vb, k, klen);
117                 buffer_append_string_len(vb, CONST_STR_LEN(": "));
118             }
119             buffer_append_string_len(vb, v, vlen);
120         }
121     }
122 }
123 
124 
125 buffer * http_header_request_get(connection *con, enum http_header_e id, const char *k, size_t klen) {
126     data_string * const ds =
127       (id <= HTTP_HEADER_OTHER || (con->request.htags & id))
128       ? (data_string *)array_get_element_klen(con->request.headers, k, klen)
129       : NULL;
130     return ds && !buffer_string_is_empty(ds->value) ? ds->value : NULL;
131 }
132 
133 void http_header_request_unset(connection *con, enum http_header_e id, const char *k, size_t klen) {
134     if (id <= HTTP_HEADER_OTHER || (con->request.htags & id)) {
135         if (id > HTTP_HEADER_OTHER) con->request.htags &= ~id;
136         array_set_key_value(con->request.headers, k, klen, CONST_STR_LEN(""));
137     }
138 }
139 
140 void http_header_request_set(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen) {
141     /* set value, including setting blank value if 0 == vlen
142      * (note: if 0 == vlen, header is still inserted with blank value,
143      *  which is used to indicate a "removed" header)
144      */
145     if (id > HTTP_HEADER_OTHER)
146         (vlen) ? (con->request.htags |= id) : (con->request.htags &= ~id);
147     array_set_key_value(con->request.headers, k, klen, v, vlen);
148 }
149 
150 void http_header_request_append(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen) {
151     if (vlen) {
152         data_string *ds = (id <= HTTP_HEADER_OTHER || (con->request.htags & id))
153           ? (data_string *)array_get_element_klen(con->request.headers, k, klen)
154           : NULL;
155         if (id > HTTP_HEADER_OTHER) con->request.htags |= id;
156         if (NULL == ds) {
157             array_insert_key_value(con->request.headers, k, klen, v, vlen);
158         }
159         else { /* append value */
160             buffer *vb = ds->value;
161             if (!buffer_string_is_empty(vb))
162                 buffer_append_string_len(vb, CONST_STR_LEN(", "));
163             buffer_append_string_len(vb, v, vlen);
164         }
165     }
166 }
167 
168 
169 buffer * http_header_env_get(connection *con, const char *k, size_t klen) {
170     data_string * const ds =
171       (data_string *)array_get_element_klen(con->environment, k, klen);
172     return ds && !buffer_string_is_empty(ds->value) ? ds->value : NULL;
173 }
174 
175 void http_header_env_set(connection *con, const char *k, size_t klen, const char *v, size_t vlen) {
176     array_set_key_value(con->environment, k, klen, v, vlen);
177 }
178 
179 void http_header_env_append(connection *con, const char *k, size_t klen, const char *v, size_t vlen) {
180     /*if (vlen)*/ /* skip check; permit env var w/ blank value to be appended */
181     {
182         buffer * const vb = http_header_env_get(con, k, klen);
183         if (NULL == vb) {
184             array_insert_key_value(con->environment, k, klen, v, vlen);
185         }
186         else if (vlen) { /* append value */
187             buffer_append_string_len(vb, CONST_STR_LEN(", "));
188             buffer_append_string_len(vb, v, vlen);
189         }
190     }
191 }
192