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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3061bc1 was b7fd2a0, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Use errno_t in all uspace and kernel code.

Change type of every variable, parameter and return value that holds an
<errno.h> constant to either errno_t (the usual case), or sys_errno_t
(some places in kernel). This is for the purpose of self-documentation,
as well as for type-checking with a bit of type definition hackery.

Although this is a massive commit, it is a simple text replacement, and thus
is very easy to verify. Simply do the following:

`
git checkout <this commit's hash>
git reset HEAD
git add .
tools/srepl '\berrno_t\b' int
git add .
tools/srepl '\bsys_errno_t\b' sysarg_t
git reset
git diff
`

While this doesn't ensure that the replacements are correct, it does ensure
that the commit doesn't do anything except those replacements. Since errno_t
is typedef'd to int in the usual case (and sys_errno_t to sysarg_t), even if
incorrect, this commit cannot change behavior.

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