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

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

libhttp: Add higher-level API for working with headers

  • Property mode set to 100644
File size: 8.1 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, char **out_name)
97{
98 receive_buffer_mark_t name_start;
99 receive_buffer_mark_t name_end;
100
101 recv_mark(rb, &name_start);
102 recv_mark(rb, &name_end);
103
104 char c = 0;
105 do {
106 recv_mark_update(rb, &name_end);
107
108 int rc = recv_char(rb, &c, true);
109 if (rc != EOK) {
110 recv_unmark(rb, &name_start);
111 recv_unmark(rb, &name_end);
112 return rc;
113 }
114 } while (is_token(c));
115
116 if (c != ':') {
117 recv_unmark(rb, &name_start);
118 recv_unmark(rb, &name_end);
119 return EINVAL;
120 }
121
122 char *name = NULL;
123 int rc = recv_cut_str(rb, &name_start, &name_end, &name);
124 recv_unmark(rb, &name_start);
125 recv_unmark(rb, &name_end);
126 if (rc != EOK)
127 return rc;
128
129 *out_name = name;
130 return EOK;
131}
132
133int http_header_receive_value(receive_buffer_t *rb, char **out_value)
134{
135 int rc = EOK;
136 char c = 0;
137
138 receive_buffer_mark_t value_start;
139 recv_mark(rb, &value_start);
140
141 /* Ignore any inline LWS */
142 while (true) {
143 recv_mark_update(rb, &value_start);
144 rc = recv_char(rb, &c, false);
145 if (rc != EOK)
146 goto error;
147
148 if (c != ' ' && c != '\t')
149 break;
150
151 rc = recv_char(rb, &c, true);
152 if (rc != EOK)
153 goto error;
154 }
155
156 receive_buffer_mark_t value_end;
157 recv_mark(rb, &value_end);
158
159 while (true) {
160 recv_mark_update(rb, &value_end);
161
162 rc = recv_char(rb, &c, true);
163 if (rc != EOK)
164 goto error_end;
165
166 if (c != '\r' && c != '\n')
167 continue;
168
169 rc = recv_discard(rb, (c == '\r' ? '\n' : '\r'));
170 if (rc < 0)
171 goto error_end;
172
173 rc = recv_char(rb, &c, false);
174 if (rc != EOK)
175 goto error_end;
176
177 if (c != ' ' && c != '\t')
178 break;
179
180 /* Ignore the char */
181 rc = recv_char(rb, &c, true);
182 if (rc != EOK)
183 goto error_end;
184 }
185
186 char *value = NULL;
187 rc = recv_cut_str(rb, &value_start, &value_end, &value);
188 recv_unmark(rb, &value_start);
189 recv_unmark(rb, &value_end);
190 if (rc != EOK)
191 return rc;
192
193 *out_value = value;
194 return EOK;
195error_end:
196 recv_unmark(rb, &value_end);
197error:
198 recv_unmark(rb, &value_start);
199 return rc;
200}
201
202int http_header_receive(receive_buffer_t *rb, http_header_t *header)
203{
204 char *name = NULL;
205 int rc = http_header_receive_name(rb, &name);
206 if (rc != EOK) {
207 return rc;
208 }
209
210 char *value = NULL;
211 rc = http_header_receive_value(rb, &value);
212 if (rc != EOK) {
213 free(name);
214 return rc;
215 }
216
217 header->name = name;
218 header->value = value;
219 return EOK;
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 stricmp(name_a, name_b) == 0;
255}
256
257void http_headers_init(http_headers_t *headers) {
258 list_initialize(&headers->list);
259}
260
261int 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
284int 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
295int http_headers_set(http_headers_t *headers, const char *name,
296 const char *value)
297{
298 http_header_t *header = NULL;
299 int 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
315int http_headers_get(http_headers_t *headers, const char *name, char **value)
316{
317 http_header_t *header = NULL;
318 int 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
326int http_headers_receive(receive_buffer_t *rb, http_headers_t *headers)
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 http_header_t *header = malloc(sizeof(http_header_t));
341 if (header == NULL) {
342 rc = ENOMEM;
343 goto error;
344 }
345 http_header_init(header);
346
347 rc = http_header_receive(rb, header);
348 if (rc != EOK) {
349 free(header);
350 goto error;
351 }
352
353 http_headers_append_header(headers, header);
354 added++;
355 }
356
357 return EOK;
358error:
359 while (added-- > 0) {
360 link_t *link = list_last(&headers->list);
361 http_header_t *header = list_get_instance(link, http_header_t, link);
362 http_headers_remove(headers, header);
363 http_header_destroy(header);
364 }
365 return rc;
366}
367
368void http_headers_clear(http_headers_t *headers)
369{
370 link_t *link = list_first(&headers->list);
371 while (link != NULL) {
372 link_t *next = list_next(link, &headers->list);
373 http_header_t *header = list_get_instance(link, http_header_t, link);
374 list_remove(link);
375 http_header_destroy(header);
376 link = next;
377 }
378}
379
380/** @}
381 */
Note: See TracBrowser for help on using the repository browser.