source: mainline/uspace/lib/http/src/headers.c@ 4b9213d

Last change on this file since 4b9213d 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
RevLine 
[f9a2831]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
[10de842]36#include <errno.h>
[f9a2831]37#include <stdio.h>
38#include <stdlib.h>
39#include <str.h>
40#include <macros.h>
41
[b623b68]42#include <http/http.h>
43#include <http/ctype.h>
[f9a2831]44
45#define HTTP_HEADER_LINE "%s: %s\r\n"
46
[0005b63]47void http_header_init(http_header_t *header)
[f9a2831]48{
[0005b63]49 link_initialize(&header->link);
50 header->name = NULL;
51 header->value = NULL;
[f9a2831]52}
53
[0005b63]54http_header_t *http_header_create(const char *name, const char *value)
[f9a2831]55{
56 http_header_t *header = malloc(sizeof(http_header_t));
57 if (header == NULL)
58 return NULL;
[0005b63]59 http_header_init(header);
60
61 header->name = str_dup(name);
62 if (header->name == NULL) {
63 free(header);
64 return NULL;
65 }
[a35b458]66
[0005b63]67 header->value = str_dup(value);
68 if (header->value == NULL) {
69 free(header->name);
70 free(header);
71 return NULL;
72 }
73
[f9a2831]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
[0005b63]84ssize_t http_header_encode(http_header_t *header, char *buf, size_t buf_size)
[f9a2831]85{
86 /* TODO properly split long header values */
[4f4018b]87 return snprintf(buf, buf_size,
88 HTTP_HEADER_LINE, header->name, header->value);
[f9a2831]89}
90
[b7fd2a0]91errno_t http_header_receive_name(receive_buffer_t *rb,
[cbfc8b7]92 receive_buffer_mark_t *name_end)
[f9a2831]93{
[3ce68b7]94 char c = 0;
95 do {
[cbfc8b7]96 if (name_end)
97 recv_mark_update(rb, name_end);
[a35b458]98
[b7fd2a0]99 errno_t rc = recv_char(rb, &c, true);
[cbfc8b7]100 if (rc != EOK)
[3ce68b7]101 return rc;
102 } while (is_token(c));
[a35b458]103
[cbfc8b7]104 if (c != ':')
[f9a2831]105 return EINVAL;
[a35b458]106
[3ce68b7]107 return EOK;
108}
109
[b7fd2a0]110errno_t http_header_receive_value(receive_buffer_t *rb,
[cbfc8b7]111 receive_buffer_mark_t *value_start, receive_buffer_mark_t *value_end)
[3ce68b7]112{
[b7fd2a0]113 errno_t rc = EOK;
[3ce68b7]114 char c = 0;
[a35b458]115
[3ce68b7]116 /* Ignore any inline LWS */
117 while (true) {
[cbfc8b7]118 if (value_start)
119 recv_mark_update(rb, value_start);
[a35b458]120
[3ce68b7]121 rc = recv_char(rb, &c, false);
122 if (rc != EOK)
[cbfc8b7]123 return rc;
[a35b458]124
[3ce68b7]125 if (c != ' ' && c != '\t')
126 break;
[a35b458]127
[3ce68b7]128 rc = recv_char(rb, &c, true);
129 if (rc != EOK)
[cbfc8b7]130 return rc;
[3ce68b7]131 }
[a35b458]132
[3ce68b7]133 while (true) {
[cbfc8b7]134 recv_mark_update(rb, value_end);
[a35b458]135
[3ce68b7]136 rc = recv_char(rb, &c, true);
137 if (rc != EOK)
[cbfc8b7]138 return rc;
[a35b458]139
[3ce68b7]140 if (c != '\r' && c != '\n')
141 continue;
[a35b458]142
[9a09212]143 size_t nrecv;
144 rc = recv_discard(rb, (c == '\r' ? '\n' : '\r'), &nrecv);
145 if (rc != EOK)
[cbfc8b7]146 return rc;
[a35b458]147
[3ce68b7]148 rc = recv_char(rb, &c, false);
149 if (rc != EOK)
[cbfc8b7]150 return rc;
[a35b458]151
[3ce68b7]152 if (c != ' ' && c != '\t')
153 break;
[a35b458]154
[3ce68b7]155 /* Ignore the char */
156 rc = recv_char(rb, &c, true);
157 if (rc != EOK)
[cbfc8b7]158 return rc;
[3ce68b7]159 }
[a35b458]160
[3ce68b7]161 return EOK;
162}
163
[b7fd2a0]164errno_t http_header_receive(receive_buffer_t *rb, http_header_t *header,
[cbfc8b7]165 size_t size_limit, size_t *out_bytes_used)
[3ce68b7]166{
[cbfc8b7]167 receive_buffer_mark_t mark_start;
168 receive_buffer_mark_t mark_end;
[a35b458]169
[cbfc8b7]170 recv_mark(rb, &mark_start);
171 recv_mark(rb, &mark_end);
[a35b458]172
[b7fd2a0]173 errno_t rc = http_header_receive_name(rb, &mark_end);
[cbfc8b7]174 if (rc != EOK)
175 goto end;
[a35b458]176
[cbfc8b7]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 }
[a35b458]182
[3ce68b7]183 char *name = NULL;
[cbfc8b7]184 rc = recv_cut_str(rb, &mark_start, &mark_end, &name);
185 if (rc != EOK)
186 goto end;
[a35b458]187
[cbfc8b7]188 rc = http_header_receive_value(rb, &mark_start, &mark_end);
189 if (rc != EOK)
190 goto end_with_name;
[a35b458]191
[cbfc8b7]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;
[3ce68b7]196 }
[a35b458]197
[3ce68b7]198 char *value = NULL;
[cbfc8b7]199 rc = recv_cut_str(rb, &mark_start, &mark_end, &value);
200 if (rc != EOK)
201 goto end_with_name;
[a35b458]202
[cbfc8b7]203 if (out_bytes_used)
204 *out_bytes_used = name_size + value_size;
[a35b458]205
[0005b63]206 header->name = name;
207 header->value = value;
[cbfc8b7]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;
[f9a2831]215}
216
[3ce68b7]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;
[a35b458]225
[1433ecda]226 while (is_lws(value[read_index]))
227 read_index++;
[a35b458]228
[3ce68b7]229 while (value[read_index] != 0) {
230 if (is_lws(value[read_index])) {
[1433ecda]231 while (is_lws(value[read_index]))
232 read_index++;
[a35b458]233
[3ce68b7]234 if (value[read_index] != 0)
235 value[write_index++] = ' ';
[a35b458]236
[3ce68b7]237 continue;
238 }
[a35b458]239
[3ce68b7]240 value[write_index++] = value[read_index++];
241 }
[a35b458]242
[3ce68b7]243 value[write_index] = 0;
244}
245
[4d4f656]246/** Test if two header names are equivalent
247 *
248 */
249bool http_header_name_match(const char *name_a, const char *name_b)
250{
[0e7c3d9]251 return str_casecmp(name_a, name_b) == 0;
[4d4f656]252}
253
[1433ecda]254void http_headers_init(http_headers_t *headers)
255{
[4d4f656]256 list_initialize(&headers->list);
257}
258
[b7fd2a0]259errno_t http_headers_find_single(http_headers_t *headers, const char *name,
[4d4f656]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;
[a35b458]266
[4d4f656]267 if (found == NULL) {
268 found = header;
[1433ecda]269 } else {
[4d4f656]270 return HTTP_EMULTIPLE_HEADERS;
271 }
272 }
[a35b458]273
[4d4f656]274 if (found == NULL)
275 return HTTP_EMISSING_HEADER;
[a35b458]276
[4d4f656]277 *out_header = found;
278 return EOK;
279}
280
[b7fd2a0]281errno_t http_headers_append(http_headers_t *headers, const char *name,
[4d4f656]282 const char *value)
283{
284 http_header_t *header = http_header_create(name, value);
285 if (header == NULL)
286 return ENOMEM;
[a35b458]287
[4d4f656]288 http_headers_append_header(headers, header);
289 return EOK;
290}
291
[b7fd2a0]292errno_t http_headers_set(http_headers_t *headers, const char *name,
[4d4f656]293 const char *value)
294{
295 http_header_t *header = NULL;
[b7fd2a0]296 errno_t rc = http_headers_find_single(headers, name, &header);
[4d4f656]297 if (rc != EOK && rc != HTTP_EMISSING_HEADER)
298 return rc;
[a35b458]299
[4d4f656]300 if (rc == HTTP_EMISSING_HEADER)
301 return http_headers_append(headers, name, value);
[a35b458]302
[4d4f656]303 char *new_value = str_dup(value);
304 if (new_value == NULL)
305 return ENOMEM;
[a35b458]306
[4d4f656]307 free(header->value);
308 header->value = new_value;
309 return EOK;
310}
311
[b7fd2a0]312errno_t http_headers_get(http_headers_t *headers, const char *name, char **value)
[4d4f656]313{
314 http_header_t *header = NULL;
[b7fd2a0]315 errno_t rc = http_headers_find_single(headers, name, &header);
[4d4f656]316 if (rc != EOK)
317 return rc;
[a35b458]318
[4d4f656]319 *value = header->value;
320 return EOK;
321}
322
[b7fd2a0]323errno_t http_headers_receive(receive_buffer_t *rb, http_headers_t *headers,
[cbfc8b7]324 size_t limit_alloc, unsigned limit_count)
[4d4f656]325{
[b7fd2a0]326 errno_t rc = EOK;
[4d4f656]327 unsigned added = 0;
[a35b458]328
[4d4f656]329 while (true) {
330 char c = 0;
331 rc = recv_char(rb, &c, false);
332 if (rc != EOK)
333 goto error;
[a35b458]334
[4d4f656]335 if (c == '\n' || c == '\r')
336 break;
[a35b458]337
[cbfc8b7]338 if (limit_count > 0 && added >= limit_count) {
339 rc = ELIMIT;
340 goto error;
341 }
[a35b458]342
[4d4f656]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);
[a35b458]349
[cbfc8b7]350 size_t header_size;
351 rc = http_header_receive(rb, header, limit_alloc, &header_size);
[4d4f656]352 if (rc != EOK) {
353 free(header);
354 goto error;
355 }
[cbfc8b7]356 limit_alloc -= header_size;
[a35b458]357
[4d4f656]358 http_headers_append_header(headers, header);
359 added++;
360 }
[a35b458]361
[4d4f656]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
[f9a2831]385/** @}
386 */
Note: See TracBrowser for help on using the repository browser.