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