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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ab6edb6 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
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 if (buf == NULL) {
88 return printf_size(HTTP_HEADER_LINE, header->name, header->value);
89 } else {
90 return snprintf(buf, buf_size,
91 HTTP_HEADER_LINE, header->name, header->value);
92 }
93}
94
95errno_t http_header_receive_name(receive_buffer_t *rb,
96 receive_buffer_mark_t *name_end)
97{
98 char c = 0;
99 do {
100 if (name_end)
101 recv_mark_update(rb, name_end);
102
103 errno_t rc = recv_char(rb, &c, true);
104 if (rc != EOK)
105 return rc;
106 } while (is_token(c));
107
108 if (c != ':')
109 return EINVAL;
110
111 return EOK;
112}
113
114errno_t http_header_receive_value(receive_buffer_t *rb,
115 receive_buffer_mark_t *value_start, receive_buffer_mark_t *value_end)
116{
117 errno_t rc = EOK;
118 char c = 0;
119
120 /* Ignore any inline LWS */
121 while (true) {
122 if (value_start)
123 recv_mark_update(rb, value_start);
124
125 rc = recv_char(rb, &c, false);
126 if (rc != EOK)
127 return rc;
128
129 if (c != ' ' && c != '\t')
130 break;
131
132 rc = recv_char(rb, &c, true);
133 if (rc != EOK)
134 return rc;
135 }
136
137 while (true) {
138 recv_mark_update(rb, value_end);
139
140 rc = recv_char(rb, &c, true);
141 if (rc != EOK)
142 return rc;
143
144 if (c != '\r' && c != '\n')
145 continue;
146
147 size_t nrecv;
148 rc = recv_discard(rb, (c == '\r' ? '\n' : '\r'), &nrecv);
149 if (rc != EOK)
150 return rc;
151
152 rc = recv_char(rb, &c, false);
153 if (rc != EOK)
154 return rc;
155
156 if (c != ' ' && c != '\t')
157 break;
158
159 /* Ignore the char */
160 rc = recv_char(rb, &c, true);
161 if (rc != EOK)
162 return rc;
163 }
164
165 return EOK;
166}
167
168errno_t http_header_receive(receive_buffer_t *rb, http_header_t *header,
169 size_t size_limit, size_t *out_bytes_used)
170{
171 receive_buffer_mark_t mark_start;
172 receive_buffer_mark_t mark_end;
173
174 recv_mark(rb, &mark_start);
175 recv_mark(rb, &mark_end);
176
177 errno_t rc = http_header_receive_name(rb, &mark_end);
178 if (rc != EOK)
179 goto end;
180
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 }
186
187 char *name = NULL;
188 rc = recv_cut_str(rb, &mark_start, &mark_end, &name);
189 if (rc != EOK)
190 goto end;
191
192 rc = http_header_receive_value(rb, &mark_start, &mark_end);
193 if (rc != EOK)
194 goto end_with_name;
195
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;
200 }
201
202 char *value = NULL;
203 rc = recv_cut_str(rb, &mark_start, &mark_end, &value);
204 if (rc != EOK)
205 goto end_with_name;
206
207 if (out_bytes_used)
208 *out_bytes_used = name_size + value_size;
209
210 header->name = name;
211 header->value = value;
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;
219}
220
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;
229
230 while (is_lws(value[read_index]))
231 read_index++;
232
233 while (value[read_index] != 0) {
234 if (is_lws(value[read_index])) {
235 while (is_lws(value[read_index]))
236 read_index++;
237
238 if (value[read_index] != 0)
239 value[write_index++] = ' ';
240
241 continue;
242 }
243
244 value[write_index++] = value[read_index++];
245 }
246
247 value[write_index] = 0;
248}
249
250/** Test if two header names are equivalent
251 *
252 */
253bool http_header_name_match(const char *name_a, const char *name_b)
254{
255 return str_casecmp(name_a, name_b) == 0;
256}
257
258void http_headers_init(http_headers_t *headers)
259{
260 list_initialize(&headers->list);
261}
262
263errno_t http_headers_find_single(http_headers_t *headers, const char *name,
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;
270
271 if (found == NULL) {
272 found = header;
273 } else {
274 return HTTP_EMULTIPLE_HEADERS;
275 }
276 }
277
278 if (found == NULL)
279 return HTTP_EMISSING_HEADER;
280
281 *out_header = found;
282 return EOK;
283}
284
285errno_t http_headers_append(http_headers_t *headers, const char *name,
286 const char *value)
287{
288 http_header_t *header = http_header_create(name, value);
289 if (header == NULL)
290 return ENOMEM;
291
292 http_headers_append_header(headers, header);
293 return EOK;
294}
295
296errno_t http_headers_set(http_headers_t *headers, const char *name,
297 const char *value)
298{
299 http_header_t *header = NULL;
300 errno_t rc = http_headers_find_single(headers, name, &header);
301 if (rc != EOK && rc != HTTP_EMISSING_HEADER)
302 return rc;
303
304 if (rc == HTTP_EMISSING_HEADER)
305 return http_headers_append(headers, name, value);
306
307 char *new_value = str_dup(value);
308 if (new_value == NULL)
309 return ENOMEM;
310
311 free(header->value);
312 header->value = new_value;
313 return EOK;
314}
315
316errno_t http_headers_get(http_headers_t *headers, const char *name, char **value)
317{
318 http_header_t *header = NULL;
319 errno_t rc = http_headers_find_single(headers, name, &header);
320 if (rc != EOK)
321 return rc;
322
323 *value = header->value;
324 return EOK;
325}
326
327errno_t http_headers_receive(receive_buffer_t *rb, http_headers_t *headers,
328 size_t limit_alloc, unsigned limit_count)
329{
330 errno_t rc = EOK;
331 unsigned added = 0;
332
333 while (true) {
334 char c = 0;
335 rc = recv_char(rb, &c, false);
336 if (rc != EOK)
337 goto error;
338
339 if (c == '\n' || c == '\r')
340 break;
341
342 if (limit_count > 0 && added >= limit_count) {
343 rc = ELIMIT;
344 goto error;
345 }
346
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);
353
354 size_t header_size;
355 rc = http_header_receive(rb, header, limit_alloc, &header_size);
356 if (rc != EOK) {
357 free(header);
358 goto error;
359 }
360 limit_alloc -= header_size;
361
362 http_headers_append_header(headers, header);
363 added++;
364 }
365
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
389/** @}
390 */
Note: See TracBrowser for help on using the repository browser.