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