source: mainline/uspace/lib/http/http.c@ ef2ecec

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ef2ecec was 26de91a, checked in by Jiri Svoboda <jiri@…>, 12 years ago

IPv4 and v6 should not need separate handling by a simple client that is just connecting to a host/address. Add IPv6/DNS support in applications where missing.

  • Property mode set to 100644
File size: 11.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 <net/socket.h>
42#include <inet/dnsr.h>
43
44#include "http.h"
45
46#define HTTP_METHOD_LINE "%s %s HTTP/1.1\r\n"
47#define HTTP_HEADER_LINE "%s: %s\r\n"
48#define HTTP_REQUEST_LINE "\r\n"
49
50static char *cut_str(const char *start, const char *end)
51{
52 size_t size = end - start;
53 return str_ndup(start, size);
54}
55
56static void recv_reset(http_t *http)
57{
58 http->recv_buffer_in = 0;
59 http->recv_buffer_out = 0;
60}
61
62/** Receive one character (with buffering) */
63static int recv_char(http_t *http, char *c, bool consume)
64{
65 if (http->recv_buffer_out == http->recv_buffer_in) {
66 recv_reset(http);
67
68 ssize_t rc = recv(http->conn_sd, http->recv_buffer, http->buffer_size, 0);
69 if (rc <= 0)
70 return rc;
71
72 http->recv_buffer_in = rc;
73 }
74
75 *c = http->recv_buffer[http->recv_buffer_out];
76 if (consume)
77 http->recv_buffer_out++;
78 return EOK;
79}
80
81static ssize_t recv_buffer(http_t *http, char *buf, size_t buf_size)
82{
83 /* Flush any buffered data*/
84 if (http->recv_buffer_out != http->recv_buffer_in) {
85 size_t size = min(http->recv_buffer_in - http->recv_buffer_out, buf_size);
86 memcpy(buf, http->recv_buffer + http->recv_buffer_out, size);
87 http->recv_buffer_out += size;
88 return size;
89 }
90
91 return recv(http->conn_sd, buf, buf_size, 0);
92}
93
94/** Receive a character and if it is c, discard it from input buffer */
95static int recv_discard(http_t *http, char discard)
96{
97 char c = 0;
98 int rc = recv_char(http, &c, false);
99 if (rc != EOK)
100 return rc;
101 if (c != discard)
102 return EOK;
103 return recv_char(http, &c, true);
104}
105
106/* Receive a single line */
107static ssize_t recv_line(http_t *http, char *line, size_t size)
108{
109 size_t written = 0;
110
111 while (written < size) {
112 char c = 0;
113 int rc = recv_char(http, &c, true);
114 if (rc != EOK)
115 return rc;
116 if (c == '\n') {
117 recv_discard(http, '\r');
118 line[written++] = 0;
119 return written;
120 }
121 else if (c == '\r') {
122 recv_discard(http, '\n');
123 line[written++] = 0;
124 return written;
125 }
126 line[written++] = c;
127 }
128
129 return ELIMIT;
130}
131
132http_t *http_create(const char *host, uint16_t port)
133{
134 http_t *http = malloc(sizeof(http_t));
135 if (http == NULL)
136 return NULL;
137
138 http->host = str_dup(host);
139 if (http->host == NULL) {
140 free(http);
141 return NULL;
142 }
143 http->port = port;
144
145 http->buffer_size = 4096;
146 http->recv_buffer = malloc(http->buffer_size);
147 if (http->recv_buffer == NULL) {
148 free(http->host);
149 free(http);
150 return NULL;
151 }
152
153 return http;
154}
155
156int http_connect(http_t *http)
157{
158 if (http->connected)
159 return EBUSY;
160
161 /* Interpret as address */
162 int rc = inet_addr_parse(http->host, &http->addr);
163
164 if (rc != EOK) {
165 /* Interpret as a host name */
166 dnsr_hostinfo_t *hinfo = NULL;
167 rc = dnsr_name2host(http->host, &hinfo, ip_any);
168
169 if (rc != EOK)
170 return rc;
171
172 http->addr = hinfo->addr;
173 dnsr_hostinfo_destroy(hinfo);
174 }
175
176 struct sockaddr *saddr;
177 socklen_t saddrlen;
178
179 rc = inet_addr_sockaddr(&http->addr, http->port, &saddr, &saddrlen);
180 if (rc != EOK) {
181 assert(rc == ENOMEM);
182 return ENOMEM;
183 }
184
185 http->conn_sd = socket(saddr->sa_family, SOCK_STREAM, 0);
186 if (http->conn_sd < 0)
187 return http->conn_sd;
188
189 rc = connect(http->conn_sd, saddr, saddrlen);
190 free(saddr);
191
192 return rc;
193}
194
195http_header_t *http_header_create(const char *name, const char *value)
196{
197 char *dname = str_dup(name);
198 if (dname == NULL)
199 return NULL;
200
201 char *dvalue = str_dup(value);
202 if (dvalue == NULL) {
203 free(dname);
204 return NULL;
205 }
206
207 return http_header_create_no_copy(dname, dvalue);
208}
209
210http_header_t *http_header_create_no_copy(char *name, char *value)
211{
212 http_header_t *header = malloc(sizeof(http_header_t));
213 if (header == NULL)
214 return NULL;
215
216 link_initialize(&header->link);
217 header->name = name;
218 header->value = value;
219
220 return header;
221}
222
223void http_header_destroy(http_header_t *header)
224{
225 free(header->name);
226 free(header->value);
227 free(header);
228}
229
230http_request_t *http_request_create(const char *method, const char *path)
231{
232 http_request_t *req = malloc(sizeof(http_request_t));
233 if (req == NULL)
234 return NULL;
235
236 req->method = str_dup(method);
237 if (req->method == NULL) {
238 free(req);
239 return NULL;
240 }
241
242 req->path = str_dup(path);
243 if (req->path == NULL) {
244 free(req->method);
245 free(req);
246 return NULL;
247 }
248
249 list_initialize(&req->headers);
250
251 return req;
252}
253
254void http_request_destroy(http_request_t *req)
255{
256 free(req->method);
257 free(req->path);
258 link_t *link = req->headers.head.next;
259 while (link != &req->headers.head) {
260 link_t *next = link->next;
261 http_header_t *header = list_get_instance(link, http_header_t, link);
262 http_header_destroy(header);
263 link = next;
264 }
265 free(req);
266}
267
268static ssize_t http_encode_method(char *buf, size_t buf_size,
269 const char *method, const char *path)
270{
271 if (buf == NULL) {
272 return printf_size(HTTP_METHOD_LINE, method, path);
273 }
274 else {
275 return snprintf(buf, buf_size, HTTP_METHOD_LINE, method, path);
276 }
277}
278
279static ssize_t http_encode_header(char *buf, size_t buf_size,
280 http_header_t *header)
281{
282 /* TODO properly split long header values */
283 if (buf == NULL) {
284 return printf_size(HTTP_HEADER_LINE, header->name, header->value);
285 }
286 else {
287 return snprintf(buf, buf_size,
288 HTTP_HEADER_LINE, header->name, header->value);
289 }
290}
291
292int http_request_format(http_request_t *req, char **out_buf,
293 size_t *out_buf_size)
294{
295 /* Compute the size of the request */
296 ssize_t meth_size = http_encode_method(NULL, 0, req->method, req->path);
297 if (meth_size < 0)
298 return meth_size;
299 size_t size = meth_size;
300
301 list_foreach(req->headers, link, http_header_t, header) {
302 ssize_t header_size = http_encode_header(NULL, 0, header);
303 if (header_size < 0)
304 return header_size;
305 size += header_size;
306 }
307 size += str_length(HTTP_REQUEST_LINE);
308
309 char *buf = malloc(size);
310 if (buf == NULL)
311 return ENOMEM;
312
313 char *pos = buf;
314 size_t pos_size = size;
315 ssize_t written = http_encode_method(pos, pos_size, req->method, req->path);
316 if (written < 0) {
317 free(buf);
318 return written;
319 }
320 pos += written;
321 pos_size -= written;
322
323 list_foreach(req->headers, link, http_header_t, header) {
324 written = http_encode_header(pos, pos_size, header);
325 if (written < 0) {
326 free(buf);
327 return written;
328 }
329 pos += written;
330 pos_size -= written;
331 }
332
333 size_t rlsize = str_size(HTTP_REQUEST_LINE);
334 memcpy(pos, HTTP_REQUEST_LINE, rlsize);
335 pos_size -= rlsize;
336 assert(pos_size == 0);
337
338 *out_buf = buf;
339 *out_buf_size = size;
340 return EOK;
341}
342
343int http_send_request(http_t *http, http_request_t *req)
344{
345 char *buf;
346 size_t buf_size;
347
348 int rc = http_request_format(req, &buf, &buf_size);
349 if (rc != EOK)
350 return rc;
351
352 rc = send(http->conn_sd, buf, buf_size, 0);
353 free(buf);
354
355 return rc;
356}
357
358int http_parse_status(const char *line, http_version_t *out_version,
359 uint16_t *out_status, char **out_message)
360{
361 http_version_t version;
362 uint16_t status;
363 char *message = NULL;
364
365 if (!str_test_prefix(line, "HTTP/"))
366 return EINVAL;
367
368 const char *pos_version = line + 5;
369 const char *pos = pos_version;
370
371 int rc = str_uint8_t(pos_version, &pos, 10, false, &version.major);
372 if (rc != EOK)
373 return rc;
374 if (*pos != '.')
375 return EINVAL;
376 pos++;
377
378 pos_version = pos;
379 rc = str_uint8_t(pos_version, &pos, 10, false, &version.minor);
380 if (rc != EOK)
381 return rc;
382 if (*pos != ' ')
383 return EINVAL;
384 pos++;
385
386 const char *pos_status = pos;
387 rc = str_uint16_t(pos_status, &pos, 10, false, &status);
388 if (rc != EOK)
389 return rc;
390 if (*pos != ' ')
391 return EINVAL;
392 pos++;
393
394 if (out_message) {
395 message = str_dup(pos);
396 if (message == NULL)
397 return ENOMEM;
398 }
399
400 if (out_version)
401 *out_version = version;
402 if (out_status)
403 *out_status = status;
404 if (out_message)
405 *out_message = message;
406 return EOK;
407}
408
409int http_parse_header(const char *line, char **out_name, char **out_value)
410{
411 const char *pos = line;
412 while (*pos != 0 && *pos != ':') pos++;
413 if (*pos != ':')
414 return EINVAL;
415
416 char *name = cut_str(line, pos);
417 if (name == NULL)
418 return ENOMEM;
419
420 pos++;
421
422 while (*pos == ' ') pos++;
423
424 char *value = str_dup(pos);
425 if (value == NULL) {
426 free(name);
427 return ENOMEM;
428 }
429
430 *out_name = name;
431 *out_value = value;
432
433 return EOK;
434}
435
436int http_receive_response(http_t *http, http_response_t **out_response)
437{
438 http_response_t *resp = malloc(sizeof(http_response_t));
439 if (resp == NULL)
440 return ENOMEM;
441 memset(resp, 0, sizeof(http_response_t));
442 list_initialize(&resp->headers);
443
444 char *line = malloc(http->buffer_size);
445 if (line == NULL) {
446 free(resp);
447 return ENOMEM;
448 }
449
450 int rc = recv_line(http, line, http->buffer_size);
451 if (rc < 0)
452 goto error;
453
454 rc = http_parse_status(line, &resp->version, &resp->status,
455 &resp->message);
456 if (rc != EOK)
457 goto error;
458
459 while (true) {
460 rc = recv_line(http, line, http->buffer_size);
461 if (rc < 0)
462 goto error;
463 if (*line == 0)
464 break;
465
466 char *name = NULL;
467 char *value = NULL;
468 rc = http_parse_header(line, &name, &value);
469 if (rc != EOK)
470 goto error;
471
472 http_header_t *header = http_header_create_no_copy(name, value);
473 if (header == NULL) {
474 free(name);
475 free(value);
476 rc = ENOMEM;
477 goto error;
478 }
479
480 list_append(&header->link, &resp->headers);
481 }
482
483 *out_response = resp;
484
485 return EOK;
486error:
487 free(line);
488 http_response_destroy(resp);
489 return rc;
490}
491
492int http_receive_body(http_t *http, void *buf, size_t buf_size)
493{
494 return recv_buffer(http, buf, buf_size);
495}
496
497void http_response_destroy(http_response_t *resp)
498{
499 free(resp->message);
500 link_t *link = resp->headers.head.next;
501 while (link != &resp->headers.head) {
502 link_t *next = link->next;
503 http_header_t *header = list_get_instance(link, http_header_t, link);
504 http_header_destroy(header);
505 link = next;
506 }
507 free(resp);
508}
509
510int http_close(http_t *http)
511{
512 if (!http->connected)
513 return EINVAL;
514
515 return closesocket(http->conn_sd);
516}
517
518void http_destroy(http_t *http)
519{
520 free(http->recv_buffer);
521 free(http);
522}
523
524/** @}
525 */
Note: See TracBrowser for help on using the repository browser.