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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ee1c2d9 was cbfc8b7, checked in by Martin Sucha <sucha14@…>, 12 years ago

libhttp: Allow to specify limits when receiving HTTP response

  • 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 <stdio.h>
37#include <stdlib.h>
38#include <str.h>
39#include <macros.h>
40
41#include <http/http.h>
42#include <http/ctype.h>
43#include <http/errno.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
96int 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 int 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
115int http_header_receive_value(receive_buffer_t *rb,
116 receive_buffer_mark_t *value_start, receive_buffer_mark_t *value_end)
117{
118 int 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 rc = recv_discard(rb, (c == '\r' ? '\n' : '\r'));
149 if (rc < 0)
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
168int 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 int 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])) read_index++;
231
232 while (value[read_index] != 0) {
233 if (is_lws(value[read_index])) {
234 while (is_lws(value[read_index])) read_index++;
235
236 if (value[read_index] != 0)
237 value[write_index++] = ' ';
238
239 continue;
240 }
241
242 value[write_index++] = value[read_index++];
243 }
244
245 value[write_index] = 0;
246}
247
248/** Test if two header names are equivalent
249 *
250 */
251bool http_header_name_match(const char *name_a, const char *name_b)
252{
253 return stricmp(name_a, name_b) == 0;
254}
255
256void http_headers_init(http_headers_t *headers) {
257 list_initialize(&headers->list);
258}
259
260int http_headers_find_single(http_headers_t *headers, const char *name,
261 http_header_t **out_header)
262{
263 http_header_t *found = NULL;
264 http_headers_foreach(*headers, header) {
265 if (!http_header_name_match(header->name, name))
266 continue;
267
268 if (found == NULL) {
269 found = header;
270 }
271 else {
272 return HTTP_EMULTIPLE_HEADERS;
273 }
274 }
275
276 if (found == NULL)
277 return HTTP_EMISSING_HEADER;
278
279 *out_header = found;
280 return EOK;
281}
282
283int http_headers_append(http_headers_t *headers, const char *name,
284 const char *value)
285{
286 http_header_t *header = http_header_create(name, value);
287 if (header == NULL)
288 return ENOMEM;
289
290 http_headers_append_header(headers, header);
291 return EOK;
292}
293
294int http_headers_set(http_headers_t *headers, const char *name,
295 const char *value)
296{
297 http_header_t *header = NULL;
298 int rc = http_headers_find_single(headers, name, &header);
299 if (rc != EOK && rc != HTTP_EMISSING_HEADER)
300 return rc;
301
302 if (rc == HTTP_EMISSING_HEADER)
303 return http_headers_append(headers, name, value);
304
305 char *new_value = str_dup(value);
306 if (new_value == NULL)
307 return ENOMEM;
308
309 free(header->value);
310 header->value = new_value;
311 return EOK;
312}
313
314int http_headers_get(http_headers_t *headers, const char *name, char **value)
315{
316 http_header_t *header = NULL;
317 int rc = http_headers_find_single(headers, name, &header);
318 if (rc != EOK)
319 return rc;
320
321 *value = header->value;
322 return EOK;
323}
324
325int http_headers_receive(receive_buffer_t *rb, http_headers_t *headers,
326 size_t limit_alloc, unsigned limit_count)
327{
328 int rc = EOK;
329 unsigned added = 0;
330
331 while (true) {
332 char c = 0;
333 rc = recv_char(rb, &c, false);
334 if (rc != EOK)
335 goto error;
336
337 if (c == '\n' || c == '\r')
338 break;
339
340 if (limit_count > 0 && added >= limit_count) {
341 rc = ELIMIT;
342 goto error;
343 }
344
345 http_header_t *header = malloc(sizeof(http_header_t));
346 if (header == NULL) {
347 rc = ENOMEM;
348 goto error;
349 }
350 http_header_init(header);
351
352 size_t header_size;
353 rc = http_header_receive(rb, header, limit_alloc, &header_size);
354 if (rc != EOK) {
355 free(header);
356 goto error;
357 }
358 limit_alloc -= header_size;
359
360 http_headers_append_header(headers, header);
361 added++;
362 }
363
364 return EOK;
365error:
366 while (added-- > 0) {
367 link_t *link = list_last(&headers->list);
368 http_header_t *header = list_get_instance(link, http_header_t, link);
369 http_headers_remove(headers, header);
370 http_header_destroy(header);
371 }
372 return rc;
373}
374
375void http_headers_clear(http_headers_t *headers)
376{
377 link_t *link = list_first(&headers->list);
378 while (link != NULL) {
379 link_t *next = list_next(link, &headers->list);
380 http_header_t *header = list_get_instance(link, http_header_t, link);
381 list_remove(link);
382 http_header_destroy(header);
383 link = next;
384 }
385}
386
387/** @}
388 */
Note: See TracBrowser for help on using the repository browser.