1 // Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15
16 #include <stdlib.h>
17 #include <string.h>
18 #include <ctype.h>
19 #include <stdio.h>
20 #include <stdarg.h>
21 #include "esp_log.h"
22 #include "http_header.h"
23 #include "http_utils.h"
24
25 static const char *TAG = "HTTP_HEADER";
26 #define HEADER_BUFFER (1024)
27
28 /**
29 * dictionary item struct, with key-value pair
30 */
31 typedef struct http_header_item {
32 char *key; /*!< key */
33 char *value; /*!< value */
34 STAILQ_ENTRY(http_header_item) next; /*!< Point to next entry */
35 } http_header_item_t;
36
37 STAILQ_HEAD(http_header, http_header_item);
38
39
http_header_init(void)40 http_header_handle_t http_header_init(void)
41 {
42 http_header_handle_t header = calloc(1, sizeof(struct http_header));
43 HTTP_MEM_CHECK(TAG, header, return NULL);
44 STAILQ_INIT(header);
45 return header;
46 }
47
http_header_destroy(http_header_handle_t header)48 esp_err_t http_header_destroy(http_header_handle_t header)
49 {
50 esp_err_t err = http_header_clean(header);
51 free(header);
52 return err;
53 }
54
http_header_get_item(http_header_handle_t header,const char * key)55 http_header_item_handle_t http_header_get_item(http_header_handle_t header, const char *key)
56 {
57 http_header_item_handle_t item;
58 if (header == NULL || key == NULL) {
59 return NULL;
60 }
61 STAILQ_FOREACH(item, header, next) {
62 if (strcasecmp(item->key, key) == 0) {
63 return item;
64 }
65 }
66 return NULL;
67 }
68
http_header_get(http_header_handle_t header,const char * key,char ** value)69 esp_err_t http_header_get(http_header_handle_t header, const char *key, char **value)
70 {
71 http_header_item_handle_t item;
72
73 item = http_header_get_item(header, key);
74 if (item) {
75 *value = item->value;
76 } else {
77 *value = NULL;
78 }
79
80 return ESP_OK;
81 }
82
http_header_new_item(http_header_handle_t header,const char * key,const char * value)83 static esp_err_t http_header_new_item(http_header_handle_t header, const char *key, const char *value)
84 {
85 http_header_item_handle_t item;
86
87 item = calloc(1, sizeof(http_header_item_t));
88 HTTP_MEM_CHECK(TAG, item, return ESP_ERR_NO_MEM);
89 http_utils_assign_string(&item->key, key, -1);
90 HTTP_MEM_CHECK(TAG, item->key, goto _header_new_item_exit);
91 http_utils_trim_whitespace(&item->key);
92 http_utils_assign_string(&item->value, value, -1);
93 HTTP_MEM_CHECK(TAG, item->value, goto _header_new_item_exit);
94 http_utils_trim_whitespace(&item->value);
95 STAILQ_INSERT_TAIL(header, item, next);
96 return ESP_OK;
97 _header_new_item_exit:
98 free(item->key);
99 free(item->value);
100 free(item);
101 return ESP_ERR_NO_MEM;
102 }
103
http_header_set(http_header_handle_t header,const char * key,const char * value)104 esp_err_t http_header_set(http_header_handle_t header, const char *key, const char *value)
105 {
106 http_header_item_handle_t item;
107
108 if (value == NULL) {
109 return http_header_delete(header, key);
110 }
111
112 item = http_header_get_item(header, key);
113
114 if (item) {
115 free(item->value);
116 item->value = strdup(value);
117 http_utils_trim_whitespace(&item->value);
118 return ESP_OK;
119 }
120 return http_header_new_item(header, key, value);
121 }
122
http_header_set_from_string(http_header_handle_t header,const char * key_value_data)123 esp_err_t http_header_set_from_string(http_header_handle_t header, const char *key_value_data)
124 {
125 char *eq_ch;
126 char *p_str;
127
128 p_str = strdup(key_value_data);
129 HTTP_MEM_CHECK(TAG, p_str, return ESP_ERR_NO_MEM);
130 eq_ch = strchr(p_str, ':');
131 if (eq_ch == NULL) {
132 free(p_str);
133 return ESP_ERR_INVALID_ARG;
134 }
135 *eq_ch = 0;
136
137 http_header_set(header, p_str, eq_ch + 1);
138 free(p_str);
139 return ESP_OK;
140 }
141
142
http_header_delete(http_header_handle_t header,const char * key)143 esp_err_t http_header_delete(http_header_handle_t header, const char *key)
144 {
145 http_header_item_handle_t item = http_header_get_item(header, key);
146 if (item) {
147 STAILQ_REMOVE(header, item, http_header_item, next);
148 free(item->key);
149 free(item->value);
150 free(item);
151 } else {
152 return ESP_ERR_NOT_FOUND;
153 }
154 return ESP_OK;
155 }
156
157
http_header_set_format(http_header_handle_t header,const char * key,const char * format,...)158 int http_header_set_format(http_header_handle_t header, const char *key, const char *format, ...)
159 {
160 va_list argptr;
161 int len = 0;
162 char *buf = NULL;
163 va_start(argptr, format);
164 len = vasprintf(&buf, format, argptr);
165 HTTP_MEM_CHECK(TAG, buf, return 0);
166 va_end(argptr);
167 if (buf == NULL) {
168 return 0;
169 }
170 http_header_set(header, key, buf);
171 free(buf);
172 return len;
173 }
174
http_header_generate_string(http_header_handle_t header,int index,char * buffer,int * buffer_len)175 int http_header_generate_string(http_header_handle_t header, int index, char *buffer, int *buffer_len)
176 {
177 http_header_item_handle_t item;
178 int siz = 0;
179 int idx = 0;
180 int ret_idx = -1;
181 bool is_end = false;
182
183 // iterate over the header entries to calculate buffer size and determine last item
184 STAILQ_FOREACH(item, header, next) {
185 if (item->value && idx >= index) {
186 siz += strlen(item->key);
187 siz += strlen(item->value);
188 siz += 4; //': ' and '\r\n'
189 }
190 idx ++;
191
192 if (siz + 1 > *buffer_len - 2) {
193 // if this item would not fit to the buffer, return the index of the last fitting one
194 ret_idx = idx - 1;
195 ESP_LOGE(TAG, "Buffer length is small to fit all the headers");
196 break;
197 }
198 }
199
200 if (siz == 0) {
201 return 0;
202 }
203 if (ret_idx < 0) {
204 // all items would fit, mark this as the end of http header string
205 ret_idx = idx;
206 is_end = true;
207 }
208
209 // iterate again over the header entries to write only the fitting indeces
210 int str_len = 0;
211 idx = 0;
212 STAILQ_FOREACH(item, header, next) {
213 if (item->value && idx >= index && idx < ret_idx) {
214 str_len += snprintf(buffer + str_len, *buffer_len - str_len, "%s: %s\r\n", item->key, item->value);
215 }
216 idx ++;
217 }
218 if (is_end) {
219 // write the http header terminator if all header entries have been written in this function call
220 str_len += snprintf(buffer + str_len, *buffer_len - str_len, "\r\n");
221 }
222 *buffer_len = str_len;
223 return ret_idx;
224 }
225
http_header_clean(http_header_handle_t header)226 esp_err_t http_header_clean(http_header_handle_t header)
227 {
228 http_header_item_handle_t item = STAILQ_FIRST(header), tmp;
229 while (item != NULL) {
230 tmp = STAILQ_NEXT(item, next);
231 free(item->key);
232 free(item->value);
233 free(item);
234 item = tmp;
235 }
236 STAILQ_INIT(header);
237 return ESP_OK;
238 }
239
http_header_count(http_header_handle_t header)240 int http_header_count(http_header_handle_t header)
241 {
242 http_header_item_handle_t item;
243 int count = 0;
244 STAILQ_FOREACH(item, header, next) {
245 count ++;
246 }
247 return count;
248 }
249