source: mainline/uspace/lib/http/src/headers.c

Last change on this file was 4f4018b, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 6 years ago

Do not misuse printf_size() to estimate byte size of output

The incorrectly named function actually counts unicode code
points.

  • Property mode set to 100644
File size: 8.3 KB
Line 
1/*
2 * Copyright (c) 2013 Martin Sucha
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup http
30 * @{
31 */
32/**
33 * @file
34 */
35
36#include <errno.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <str.h>
40#include <macros.h>
41
42#include <http/http.h>
43#include <http/ctype.h>
44
45#define HTTP_HEADER_LINE "%s: %s\r\n"
46
47void http_header_init(http_header_t *header)
48{
49 link_initialize(&header->link);
50 header->name = NULL;
51 header->value = NULL;
52}
53
54http_header_t *http_header_create(const char *name, const char *value)
55{
56 http_header_t *header = malloc(sizeof(http_header_t));
57 if (header == NULL)
58 return NULL;
59 http_header_init(header);
60
61 header->name = str_dup(name);
62 if (header->name == NULL) {
63 free(header);
64 return NULL;
65 }
66
67 header->value = str_dup(value);
68 if (header->value == NULL) {
69 free(header->name);
70 free(header);
71 return NULL;
72 }
73
74 return header;
75}
76
77void http_header_destroy(http_header_t *header)
78{
79 free(header->name);
80 free(header->value);
81 free(header);
82}
83
84ssize_t http_header_encode(http_header_t *header, char *buf, size_t buf_size)
85{
86 /* TODO properly split long header values */
87 return snprintf(buf, buf_size,
88 HTTP_HEADER_LINE, header->name, header->value);
89}
90
91errno_t http_header_receive_name(receive_buffer_t *rb,
92 receive_buffer_mark_t *name_end)
93{
94 char c = 0;
95 do {
96 if (name_end)
97 recv_mark_update(rb, name_end);
98
99 errno_t rc = recv_char(rb, &c, true);
100 if (rc != EOK)
101 return rc;
102 } while (is_token(c));
103
104 if (c != ':')
105 return EINVAL;
106
107 return EOK;
108}
109
110errno_t http_header_receive_value(receive_buffer_t *rb,
111 receive_buffer_mark_t *value_start, receive_buffer_mark_t *value_end)
112{
113 errno_t rc = EOK;
114 char c = 0;
115
116 /* Ignore any inline LWS */
117 while (true) {
118 if (value_start)
119 recv_mark_update(rb, value_start);
120
121 rc = recv_char(rb, &c, false);
122 if (rc != EOK)
123 return rc;
124
125 if (c != ' ' && c != '\t')
126 break;
127
128 rc = recv_char(rb, &c, true);
129 if (rc != EOK)
130 return rc;
131 }
132
133 while (true) {
134 recv_mark_update(rb, value_end);
135
136 rc = recv_char(rb, &c, true);
137 if (rc != EOK)
138 return rc;
139
140 if (c != '\r' && c != '\n')
141 continue;
142
143 size_t nrecv;
144 rc = recv_discard(rb, (c == '\r' ? '\n' : '\r'), &nrecv);
145 if (rc != EOK)
146 return rc;
147
148 rc = recv_char(rb, &c, false);
149 if (rc != EOK)
150 return rc;
151
152 if (c != ' ' && c != '\t')
153 break;
154
155 /* Ignore the char */
156 rc = recv_char(rb, &c, true);
157 if (rc != EOK)
158 return rc;
159 }
160
161 return EOK;
162}
163
164errno_t http_header_receive(receive_buffer_t *rb, http_header_t *header,
165 size_t size_limit, size_t *out_bytes_used)
166{
167 receive_buffer_mark_t mark_start;
168 receive_buffer_mark_t mark_end;
169
170 recv_mark(rb, &mark_start);
171 recv_mark(rb, &mark_end);
172
173 errno_t rc = http_header_receive_name(rb, &mark_end);
174 if (rc != EOK)
175 goto end;
176
177 size_t name_size = mark_end.offset - mark_start.offset;
178 if (size_limit > 0 && name_size > size_limit) {
179 rc = ELIMIT;
180 goto end;
181 }
182
183 char *name = NULL;
184 rc = recv_cut_str(rb, &mark_start, &mark_end, &name);
185 if (rc != EOK)
186 goto end;
187
188 rc = http_header_receive_value(rb, &mark_start, &mark_end);
189 if (rc != EOK)
190 goto end_with_name;
191
192 size_t value_size = mark_end.offset - mark_start.offset;
193 if (size_limit > 0 && (name_size + value_size) > size_limit) {
194 rc = ELIMIT;
195 goto end_with_name;
196 }
197
198 char *value = NULL;
199 rc = recv_cut_str(rb, &mark_start, &mark_end, &value);
200 if (rc != EOK)
201 goto end_with_name;
202
203 if (out_bytes_used)
204 *out_bytes_used = name_size + value_size;
205
206 header->name = name;
207 header->value = value;
208 goto end;
209end_with_name:
210 free(name);
211end:
212 recv_unmark(rb, &mark_start);
213 recv_unmark(rb, &mark_end);
214 return rc;
215}
216
217/** Normalize HTTP header value
218 *
219 * @see RFC2616 section 4.2
220 */
221void http_header_normalize_value(char *value)
222{
223 size_t read_index = 0;
224 size_t write_index = 0;
225
226 while (is_lws(value[read_index]))
227 read_index++;
228
229 while (value[read_index] != 0) {
230 if (is_lws(value[read_index])) {
231 while (is_lws(value[read_index]))
232 read_index++;
233
234 if (value[read_index] != 0)
235 value[write_index++] = ' ';
236
237 continue;
238 }
239
240 value[write_index++] = value[read_index++];
241 }
242
243 value[write_index] = 0;
244}
245
246/** Test if two header names are equivalent
247 *
248 */
249bool http_header_name_match(const char *name_a, const char *name_b)
250{
251 return str_casecmp(name_a, name_b) == 0;
252}
253
254void http_headers_init(http_headers_t *headers)
255{
256 list_initialize(&headers->list);
257}
258
259errno_t http_headers_find_single(http_headers_t *headers, const char *name,
260 http_header_t **out_header)
261{
262 http_header_t *found = NULL;
263 http_headers_foreach(*headers, header) {
264 if (!http_header_name_match(header->name, name))
265 continue;
266
267 if (found == NULL) {
268 found = header;
269 } else {
270 return HTTP_EMULTIPLE_HEADERS;
271 }
272 }
273
274 if (found == NULL)
275 return HTTP_EMISSING_HEADER;
276
277 *out_header = found;
278 return EOK;
279}
280
281errno_t http_headers_append(http_headers_t *headers, const char *name,
282 const char *value)
283{
284 http_header_t *header = http_header_create(name, value);
285 if (header == NULL)
286 return ENOMEM;
287
288 http_headers_append_header(headers, header);
289 return EOK;
290}
291
292errno_t http_headers_set(http_headers_t *headers, const char *name,
293 const char *value)
294{
295 http_header_t *header = NULL;
296 errno_t rc = http_headers_find_single(headers, name, &header);
297 if (rc != EOK && rc != HTTP_EMISSING_HEADER)
298 return rc;
299
300 if (rc == HTTP_EMISSING_HEADER)
301 return http_headers_append(headers, name, value);
302
303 char *new_value = str_dup(value);
304 if (new_value == NULL)
305 return ENOMEM;
306
307 free(header->value);
308 header->value = new_value;
309 return EOK;
310}
311
312errno_t http_headers_get(http_headers_t *headers, const char *name, char **value)
313{
314 http_header_t *header = NULL;
315 errno_t rc = http_headers_find_single(headers, name, &header);
316 if (rc != EOK)
317 return rc;
318
319 *value = header->value;
320 return EOK;
321}
322
323errno_t http_headers_receive(receive_buffer_t *rb, http_headers_t *headers,
324 size_t limit_alloc, unsigned limit_count)
325{
326 errno_t rc = EOK;
327 unsigned added = 0;
328
329 while (true) {
330 char c = 0;
331 rc = recv_char(rb, &c, false);
332 if (rc != EOK)
333 goto error;
334
335 if (c == '\n' || c == '\r')
336 break;
337
338 if (limit_count > 0 && added >= limit_count) {
339 rc = ELIMIT;
340 goto error;
341 }
342
343 http_header_t *header = malloc(sizeof(http_header_t));
344 if (header == NULL) {
345 rc = ENOMEM;
346 goto error;
347 }
348 http_header_init(header);
349
350 size_t header_size;
351 rc = http_header_receive(rb, header, limit_alloc, &header_size);
352 if (rc != EOK) {
353 free(header);
354 goto error;
355 }
356 limit_alloc -= header_size;
357
358 http_headers_append_header(headers, header);
359 added++;
360 }
361
362 return EOK;
363error:
364 while (added-- > 0) {
365 link_t *link = list_last(&headers->list);
366 http_header_t *header = list_get_instance(link, http_header_t, link);
367 http_headers_remove(headers, header);
368 http_header_destroy(header);
369 }
370 return rc;
371}
372
373void http_headers_clear(http_headers_t *headers)
374{
375 link_t *link = list_first(&headers->list);
376 while (link != NULL) {
377 link_t *next = list_next(link, &headers->list);
378 http_header_t *header = list_get_instance(link, http_header_t, link);
379 list_remove(link);
380 http_header_destroy(header);
381 link = next;
382 }
383}
384
385/** @}
386 */
Note: See TracBrowser for help on using the repository browser.