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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 4e2d387 was 1433ecda, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix cstyle: make ccheck-fix and commit only files where all the changes are good.

  • Property mode set to 100644
File size: 8.4 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 */
87 if (buf == NULL) {
88 return printf_size(HTTP_HEADER_LINE, header->name, header->value);
[1433ecda]89 } else {
[f9a2831]90 return snprintf(buf, buf_size,
91 HTTP_HEADER_LINE, header->name, header->value);
92 }
93}
94
[b7fd2a0]95errno_t http_header_receive_name(receive_buffer_t *rb,
[cbfc8b7]96 receive_buffer_mark_t *name_end)
[f9a2831]97{
[3ce68b7]98 char c = 0;
99 do {
[cbfc8b7]100 if (name_end)
101 recv_mark_update(rb, name_end);
[a35b458]102
[b7fd2a0]103 errno_t rc = recv_char(rb, &c, true);
[cbfc8b7]104 if (rc != EOK)
[3ce68b7]105 return rc;
106 } while (is_token(c));
[a35b458]107
[cbfc8b7]108 if (c != ':')
[f9a2831]109 return EINVAL;
[a35b458]110
[3ce68b7]111 return EOK;
112}
113
[b7fd2a0]114errno_t http_header_receive_value(receive_buffer_t *rb,
[cbfc8b7]115 receive_buffer_mark_t *value_start, receive_buffer_mark_t *value_end)
[3ce68b7]116{
[b7fd2a0]117 errno_t rc = EOK;
[3ce68b7]118 char c = 0;
[a35b458]119
[3ce68b7]120 /* Ignore any inline LWS */
121 while (true) {
[cbfc8b7]122 if (value_start)
123 recv_mark_update(rb, value_start);
[a35b458]124
[3ce68b7]125 rc = recv_char(rb, &c, false);
126 if (rc != EOK)
[cbfc8b7]127 return rc;
[a35b458]128
[3ce68b7]129 if (c != ' ' && c != '\t')
130 break;
[a35b458]131
[3ce68b7]132 rc = recv_char(rb, &c, true);
133 if (rc != EOK)
[cbfc8b7]134 return rc;
[3ce68b7]135 }
[a35b458]136
[3ce68b7]137 while (true) {
[cbfc8b7]138 recv_mark_update(rb, value_end);
[a35b458]139
[3ce68b7]140 rc = recv_char(rb, &c, true);
141 if (rc != EOK)
[cbfc8b7]142 return rc;
[a35b458]143
[3ce68b7]144 if (c != '\r' && c != '\n')
145 continue;
[a35b458]146
[9a09212]147 size_t nrecv;
148 rc = recv_discard(rb, (c == '\r' ? '\n' : '\r'), &nrecv);
149 if (rc != EOK)
[cbfc8b7]150 return rc;
[a35b458]151
[3ce68b7]152 rc = recv_char(rb, &c, false);
153 if (rc != EOK)
[cbfc8b7]154 return rc;
[a35b458]155
[3ce68b7]156 if (c != ' ' && c != '\t')
157 break;
[a35b458]158
[3ce68b7]159 /* Ignore the char */
160 rc = recv_char(rb, &c, true);
161 if (rc != EOK)
[cbfc8b7]162 return rc;
[3ce68b7]163 }
[a35b458]164
[3ce68b7]165 return EOK;
166}
167
[b7fd2a0]168errno_t http_header_receive(receive_buffer_t *rb, http_header_t *header,
[cbfc8b7]169 size_t size_limit, size_t *out_bytes_used)
[3ce68b7]170{
[cbfc8b7]171 receive_buffer_mark_t mark_start;
172 receive_buffer_mark_t mark_end;
[a35b458]173
[cbfc8b7]174 recv_mark(rb, &mark_start);
175 recv_mark(rb, &mark_end);
[a35b458]176
[b7fd2a0]177 errno_t rc = http_header_receive_name(rb, &mark_end);
[cbfc8b7]178 if (rc != EOK)
179 goto end;
[a35b458]180
[cbfc8b7]181 size_t name_size = mark_end.offset - mark_start.offset;
182 if (size_limit > 0 && name_size > size_limit) {
183 rc = ELIMIT;
184 goto end;
185 }
[a35b458]186
[3ce68b7]187 char *name = NULL;
[cbfc8b7]188 rc = recv_cut_str(rb, &mark_start, &mark_end, &name);
189 if (rc != EOK)
190 goto end;
[a35b458]191
[cbfc8b7]192 rc = http_header_receive_value(rb, &mark_start, &mark_end);
193 if (rc != EOK)
194 goto end_with_name;
[a35b458]195
[cbfc8b7]196 size_t value_size = mark_end.offset - mark_start.offset;
197 if (size_limit > 0 && (name_size + value_size) > size_limit) {
198 rc = ELIMIT;
199 goto end_with_name;
[3ce68b7]200 }
[a35b458]201
[3ce68b7]202 char *value = NULL;
[cbfc8b7]203 rc = recv_cut_str(rb, &mark_start, &mark_end, &value);
204 if (rc != EOK)
205 goto end_with_name;
[a35b458]206
[cbfc8b7]207 if (out_bytes_used)
208 *out_bytes_used = name_size + value_size;
[a35b458]209
[0005b63]210 header->name = name;
211 header->value = value;
[cbfc8b7]212 goto end;
213end_with_name:
214 free(name);
215end:
216 recv_unmark(rb, &mark_start);
217 recv_unmark(rb, &mark_end);
218 return rc;
[f9a2831]219}
220
[3ce68b7]221/** Normalize HTTP header value
222 *
223 * @see RFC2616 section 4.2
224 */
225void http_header_normalize_value(char *value)
226{
227 size_t read_index = 0;
228 size_t write_index = 0;
[a35b458]229
[1433ecda]230 while (is_lws(value[read_index]))
231 read_index++;
[a35b458]232
[3ce68b7]233 while (value[read_index] != 0) {
234 if (is_lws(value[read_index])) {
[1433ecda]235 while (is_lws(value[read_index]))
236 read_index++;
[a35b458]237
[3ce68b7]238 if (value[read_index] != 0)
239 value[write_index++] = ' ';
[a35b458]240
[3ce68b7]241 continue;
242 }
[a35b458]243
[3ce68b7]244 value[write_index++] = value[read_index++];
245 }
[a35b458]246
[3ce68b7]247 value[write_index] = 0;
248}
249
[4d4f656]250/** Test if two header names are equivalent
251 *
252 */
253bool http_header_name_match(const char *name_a, const char *name_b)
254{
[0e7c3d9]255 return str_casecmp(name_a, name_b) == 0;
[4d4f656]256}
257
[1433ecda]258void http_headers_init(http_headers_t *headers)
259{
[4d4f656]260 list_initialize(&headers->list);
261}
262
[b7fd2a0]263errno_t http_headers_find_single(http_headers_t *headers, const char *name,
[4d4f656]264 http_header_t **out_header)
265{
266 http_header_t *found = NULL;
267 http_headers_foreach(*headers, header) {
268 if (!http_header_name_match(header->name, name))
269 continue;
[a35b458]270
[4d4f656]271 if (found == NULL) {
272 found = header;
[1433ecda]273 } else {
[4d4f656]274 return HTTP_EMULTIPLE_HEADERS;
275 }
276 }
[a35b458]277
[4d4f656]278 if (found == NULL)
279 return HTTP_EMISSING_HEADER;
[a35b458]280
[4d4f656]281 *out_header = found;
282 return EOK;
283}
284
[b7fd2a0]285errno_t http_headers_append(http_headers_t *headers, const char *name,
[4d4f656]286 const char *value)
287{
288 http_header_t *header = http_header_create(name, value);
289 if (header == NULL)
290 return ENOMEM;
[a35b458]291
[4d4f656]292 http_headers_append_header(headers, header);
293 return EOK;
294}
295
[b7fd2a0]296errno_t http_headers_set(http_headers_t *headers, const char *name,
[4d4f656]297 const char *value)
298{
299 http_header_t *header = NULL;
[b7fd2a0]300 errno_t rc = http_headers_find_single(headers, name, &header);
[4d4f656]301 if (rc != EOK && rc != HTTP_EMISSING_HEADER)
302 return rc;
[a35b458]303
[4d4f656]304 if (rc == HTTP_EMISSING_HEADER)
305 return http_headers_append(headers, name, value);
[a35b458]306
[4d4f656]307 char *new_value = str_dup(value);
308 if (new_value == NULL)
309 return ENOMEM;
[a35b458]310
[4d4f656]311 free(header->value);
312 header->value = new_value;
313 return EOK;
314}
315
[b7fd2a0]316errno_t http_headers_get(http_headers_t *headers, const char *name, char **value)
[4d4f656]317{
318 http_header_t *header = NULL;
[b7fd2a0]319 errno_t rc = http_headers_find_single(headers, name, &header);
[4d4f656]320 if (rc != EOK)
321 return rc;
[a35b458]322
[4d4f656]323 *value = header->value;
324 return EOK;
325}
326
[b7fd2a0]327errno_t http_headers_receive(receive_buffer_t *rb, http_headers_t *headers,
[cbfc8b7]328 size_t limit_alloc, unsigned limit_count)
[4d4f656]329{
[b7fd2a0]330 errno_t rc = EOK;
[4d4f656]331 unsigned added = 0;
[a35b458]332
[4d4f656]333 while (true) {
334 char c = 0;
335 rc = recv_char(rb, &c, false);
336 if (rc != EOK)
337 goto error;
[a35b458]338
[4d4f656]339 if (c == '\n' || c == '\r')
340 break;
[a35b458]341
[cbfc8b7]342 if (limit_count > 0 && added >= limit_count) {
343 rc = ELIMIT;
344 goto error;
345 }
[a35b458]346
[4d4f656]347 http_header_t *header = malloc(sizeof(http_header_t));
348 if (header == NULL) {
349 rc = ENOMEM;
350 goto error;
351 }
352 http_header_init(header);
[a35b458]353
[cbfc8b7]354 size_t header_size;
355 rc = http_header_receive(rb, header, limit_alloc, &header_size);
[4d4f656]356 if (rc != EOK) {
357 free(header);
358 goto error;
359 }
[cbfc8b7]360 limit_alloc -= header_size;
[a35b458]361
[4d4f656]362 http_headers_append_header(headers, header);
363 added++;
364 }
[a35b458]365
[4d4f656]366 return EOK;
367error:
368 while (added-- > 0) {
369 link_t *link = list_last(&headers->list);
370 http_header_t *header = list_get_instance(link, http_header_t, link);
371 http_headers_remove(headers, header);
372 http_header_destroy(header);
373 }
374 return rc;
375}
376
377void http_headers_clear(http_headers_t *headers)
378{
379 link_t *link = list_first(&headers->list);
380 while (link != NULL) {
381 link_t *next = list_next(link, &headers->list);
382 http_header_t *header = list_get_instance(link, http_header_t, link);
383 list_remove(link);
384 http_header_destroy(header);
385 link = next;
386 }
387}
388
[f9a2831]389/** @}
390 */
Note: See TracBrowser for help on using the repository browser.