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