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