add support for reusing connections
[project/uclient.git] / uclient-http.c
1 #include <stdio.h>
2 #include <ctype.h>
3 #include <unistd.h>
4
5 #include <libubox/ustream.h>
6 #include <libubox/ustream-ssl.h>
7 #include <libubox/usock.h>
8 #include <libubox/blobmsg.h>
9
10 #include "uclient.h"
11 #include "uclient-utils.h"
12 #include "uclient-backend.h"
13
14 static struct ustream_ssl_ctx *ssl_ctx;
15
16 enum request_type {
17 REQ_GET,
18 REQ_HEAD,
19 REQ_POST,
20 __REQ_MAX
21 };
22
23 enum http_state {
24 HTTP_STATE_INIT,
25 HTTP_STATE_HEADERS_SENT,
26 HTTP_STATE_REQUEST_DONE,
27 HTTP_STATE_RECV_HEADERS,
28 HTTP_STATE_RECV_DATA,
29 HTTP_STATE_ERROR,
30 };
31
32 static const char * const request_types[__REQ_MAX] = {
33 [REQ_GET] = "GET",
34 [REQ_HEAD] = "HEAD",
35 [REQ_POST] = "POST",
36 };
37
38 struct uclient_http {
39 struct uclient uc;
40
41 struct ustream *us;
42
43 struct ustream_fd ufd;
44 struct ustream_ssl ussl;
45
46 bool ssl;
47 bool eof;
48 bool connection_close;
49 enum request_type req_type;
50 enum http_state state;
51
52 long read_chunked;
53
54 struct blob_buf headers;
55 struct blob_buf meta;
56 };
57
58 enum {
59 PREFIX_HTTP,
60 PREFIX_HTTPS,
61 __PREFIX_MAX,
62 };
63
64 static const char * const uclient_http_prefix[] = {
65 [PREFIX_HTTP] = "http://",
66 [PREFIX_HTTPS] = "https://",
67 [__PREFIX_MAX] = NULL
68 };
69
70 static int uclient_do_connect(struct uclient_http *uh, const char *port)
71 {
72 int fd;
73
74 if (uh->uc.url->port)
75 port = uh->uc.url->port;
76
77 fd = usock(USOCK_TCP | USOCK_NONBLOCK, uh->uc.url->host, port);
78 if (fd < 0)
79 return -1;
80
81 ustream_fd_init(&uh->ufd, fd);
82 return 0;
83 }
84
85 static void uclient_http_disconnect(struct uclient *cl)
86 {
87 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
88
89 if (!uh->us)
90 return;
91
92 if (uh->ssl)
93 ustream_free(&uh->ussl.stream);
94 ustream_free(&uh->ufd.stream);
95 close(uh->ufd.fd.fd);
96 uh->us = NULL;
97 }
98
99 static void uclient_notify_eof(struct uclient_http *uh)
100 {
101 struct ustream *us = uh->us;
102
103 if (!uh->eof) {
104 if (!us->eof && !us->write_error)
105 return;
106
107 if (ustream_pending_data(us, false))
108 return;
109 }
110
111 uclient_backend_set_eof(&uh->uc);
112
113 if (uh->connection_close)
114 uclient_http_disconnect(&uh->uc);
115 }
116
117 static void uclient_http_process_headers(struct uclient_http *uh)
118 {
119 enum {
120 HTTP_HDR_TRANSFER_ENCODING,
121 HTTP_HDR_CONNECTION,
122 __HTTP_HDR_MAX,
123 };
124 static const struct blobmsg_policy hdr_policy[__HTTP_HDR_MAX] = {
125 #define hdr(_name) { .name = _name, .type = BLOBMSG_TYPE_STRING }
126 [HTTP_HDR_TRANSFER_ENCODING] = hdr("transfer-encoding"),
127 [HTTP_HDR_CONNECTION] = hdr("connection"),
128 #undef hdr
129 };
130 struct blob_attr *tb[__HTTP_HDR_MAX];
131 struct blob_attr *cur;
132
133 blobmsg_parse(hdr_policy, __HTTP_HDR_MAX, tb, blob_data(uh->meta.head), blob_len(uh->meta.head));
134
135 cur = tb[HTTP_HDR_TRANSFER_ENCODING];
136 if (cur && strstr(blobmsg_data(cur), "chunked"))
137 uh->read_chunked = 0;
138
139 cur = tb[HTTP_HDR_CONNECTION];
140 if (cur && strstr(blobmsg_data(cur), "close"))
141 uh->connection_close = true;
142 }
143
144 static void uclient_parse_http_line(struct uclient_http *uh, char *data)
145 {
146 char *name;
147 char *sep;
148
149 if (uh->state == HTTP_STATE_REQUEST_DONE) {
150 uh->state = HTTP_STATE_RECV_HEADERS;
151 return;
152 }
153
154 if (!*data) {
155 uh->state = HTTP_STATE_RECV_DATA;
156 uh->uc.meta = uh->meta.head;
157 uclient_http_process_headers(uh);
158 if (uh->uc.cb->header_done)
159 uh->uc.cb->header_done(&uh->uc);
160
161 if (uh->req_type == REQ_HEAD) {
162 uh->eof = true;
163 uclient_notify_eof(uh);
164 }
165
166 return;
167 }
168
169 sep = strchr(data, ':');
170 if (!sep)
171 return;
172
173 *(sep++) = 0;
174
175 for (name = data; *name; name++)
176 *name = tolower(*name);
177
178 name = data;
179 while (isspace(*sep))
180 sep++;
181
182 blobmsg_add_string(&uh->meta, name, sep);
183 }
184
185 static void __uclient_notify_read(struct uclient_http *uh)
186 {
187 struct uclient *uc = &uh->uc;
188 char *data;
189 int len;
190
191 if (uh->state < HTTP_STATE_REQUEST_DONE)
192 return;
193
194 data = ustream_get_read_buf(uh->us, &len);
195 if (!data || !len)
196 return;
197
198 if (uh->state < HTTP_STATE_RECV_DATA) {
199 char *sep;
200 int cur_len;
201
202 do {
203 sep = strstr(data, "\r\n");
204 if (!sep)
205 break;
206
207 /* Check for multi-line HTTP headers */
208 if (sep > data) {
209 if (!sep[2])
210 return;
211
212 if (isspace(sep[2]) && sep[2] != '\r') {
213 sep[0] = ' ';
214 sep[1] = ' ';
215 continue;
216 }
217 }
218
219 *sep = 0;
220 cur_len = sep + 2 - data;
221 uclient_parse_http_line(uh, data);
222 ustream_consume(uh->us, cur_len);
223 len -= cur_len;
224
225 data = ustream_get_read_buf(uh->us, &len);
226 } while (uh->state < HTTP_STATE_RECV_DATA);
227
228 if (!len)
229 return;
230 }
231
232 if (uh->state == HTTP_STATE_RECV_DATA && uc->cb->data_read)
233 uc->cb->data_read(uc);
234 }
235
236 static void uclient_notify_read(struct ustream *us, int bytes)
237 {
238 struct uclient_http *uh = container_of(us, struct uclient_http, ufd.stream);
239
240 __uclient_notify_read(uh);
241 }
242
243 static void uclient_notify_state(struct ustream *us)
244 {
245 struct uclient_http *uh = container_of(us, struct uclient_http, ufd.stream);
246
247 uclient_notify_eof(uh);
248 }
249
250 static int uclient_setup_http(struct uclient_http *uh)
251 {
252 struct ustream *us = &uh->ufd.stream;
253 int ret;
254
255 uh->us = us;
256 us->string_data = true;
257 us->notify_state = uclient_notify_state;
258 us->notify_read = uclient_notify_read;
259
260 ret = uclient_do_connect(uh, "80");
261 if (ret)
262 return ret;
263
264 return 0;
265 }
266
267 static void uclient_ssl_notify_read(struct ustream *us, int bytes)
268 {
269 struct uclient_http *uh = container_of(us, struct uclient_http, ussl.stream);
270
271 __uclient_notify_read(uh);
272 }
273
274 static void uclient_ssl_notify_state(struct ustream *us)
275 {
276 struct uclient_http *uh = container_of(us, struct uclient_http, ussl.stream);
277
278 uclient_notify_eof(uh);
279 }
280
281 static int uclient_setup_https(struct uclient_http *uh)
282 {
283 struct ustream *us = &uh->ussl.stream;
284 int ret;
285
286 uh->ssl = true;
287 uh->us = us;
288
289 ret = uclient_do_connect(uh, "443");
290 if (ret)
291 return ret;
292
293 if (!ssl_ctx)
294 ssl_ctx = ustream_ssl_context_new(false);
295
296 us->string_data = true;
297 us->notify_state = uclient_ssl_notify_state;
298 us->notify_read = uclient_ssl_notify_read;
299 ustream_ssl_init(&uh->ussl, &uh->ufd.stream, ssl_ctx, false);
300
301 return 0;
302 }
303
304 static void uclient_http_reset_state(struct uclient_http *uh)
305 {
306 uclient_backend_reset_state(&uh->uc);
307 uh->read_chunked = -1;
308 uh->eof = false;
309 uh->connection_close = false;
310 uh->state = HTTP_STATE_INIT;
311 }
312
313 static int uclient_http_connect(struct uclient *cl)
314 {
315 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
316
317 uclient_http_reset_state(uh);
318 blob_buf_init(&uh->meta, 0);
319
320 if (uh->us)
321 return 0;
322
323 uh->ssl = cl->url->prefix == PREFIX_HTTPS;
324
325 if (uh->ssl)
326 return uclient_setup_https(uh);
327 else
328 return uclient_setup_http(uh);
329 }
330
331 static struct uclient *uclient_http_alloc(void)
332 {
333 struct uclient_http *uh;
334
335 uh = calloc_a(sizeof(*uh));
336 blob_buf_init(&uh->headers, 0);
337
338 return &uh->uc;
339 }
340
341 static void uclient_http_free(struct uclient *cl)
342 {
343 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
344
345 uclient_http_disconnect(cl);
346 blob_buf_free(&uh->headers);
347 blob_buf_free(&uh->meta);
348 free(uh);
349 }
350
351 int
352 uclient_http_set_request_type(struct uclient *cl, const char *type)
353 {
354 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
355 int i;
356
357 if (cl->backend != &uclient_backend_http)
358 return -1;
359
360 if (uh->state > HTTP_STATE_INIT)
361 return -1;
362
363 for (i = 0; i < ARRAY_SIZE(request_types); i++) {
364 if (strcmp(request_types[i], type) != 0)
365 continue;
366
367 uh->req_type = i;
368 return 0;
369 }
370
371 return -1;
372 }
373
374 int
375 uclient_http_reset_headers(struct uclient *cl, const char *name, const char *value)
376 {
377 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
378
379 blob_buf_init(&uh->headers, 0);
380
381 return 0;
382 }
383
384 int
385 uclient_http_set_header(struct uclient *cl, const char *name, const char *value)
386 {
387 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
388
389 if (cl->backend != &uclient_backend_http)
390 return -1;
391
392 if (uh->state > HTTP_STATE_INIT)
393 return -1;
394
395 blobmsg_add_string(&uh->headers, name, value);
396 return 0;
397 }
398
399 static void
400 uclient_http_send_headers(struct uclient_http *uh)
401 {
402 struct uclient_url *url = uh->uc.url;
403 struct blob_attr *cur;
404 int rem;
405
406 if (uh->state >= HTTP_STATE_HEADERS_SENT)
407 return;
408
409 ustream_printf(uh->us,
410 "%s /%s HTTP/1.1\r\n"
411 "Host: %s\r\n",
412 request_types[uh->req_type],
413 url->location, url->host);
414
415 blobmsg_for_each_attr(cur, uh->headers.head, rem)
416 ustream_printf(uh->us, "%s: %s\n", blobmsg_name(cur), (char *) blobmsg_data(cur));
417
418 if (url->auth) {
419 int auth_len = strlen(url->auth);
420 char *auth_buf;
421
422 if (auth_len > 512)
423 return;
424
425 auth_buf = alloca(base64_len(auth_len) + 1);
426 base64_encode(url->auth, auth_len, auth_buf);
427 ustream_printf(uh->us, "Authorization: Basic %s\r\n", auth_buf);
428 }
429
430 if (uh->req_type == REQ_POST)
431 ustream_printf(uh->us, "Transfer-Encoding: chunked\r\n");
432
433 ustream_printf(uh->us, "\r\n");
434 }
435
436 static int
437 uclient_http_send_data(struct uclient *cl, char *buf, unsigned int len)
438 {
439 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
440
441 if (uh->state >= HTTP_STATE_REQUEST_DONE)
442 return -1;
443
444 uclient_http_send_headers(uh);
445
446 ustream_printf(uh->us, "%X\r\n", len);
447 ustream_write(uh->us, buf, len, false);
448 ustream_printf(uh->us, "\r\n");
449
450 return len;
451 }
452
453 static int
454 uclient_http_request_done(struct uclient *cl)
455 {
456 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
457
458 if (uh->state >= HTTP_STATE_REQUEST_DONE)
459 return -1;
460
461 uclient_http_send_headers(uh);
462 uh->state = HTTP_STATE_REQUEST_DONE;
463
464 return 0;
465 }
466
467 static int
468 uclient_http_read(struct uclient *cl, char *buf, unsigned int len)
469 {
470 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
471 int read_len = 0;
472 char *data, *data_end;
473
474 if (uh->state < HTTP_STATE_RECV_DATA || !uh->us)
475 return 0;
476
477 data = ustream_get_read_buf(uh->us, &read_len);
478 if (!data || !read_len)
479 return 0;
480
481 data_end = data + read_len;
482 read_len = 0;
483
484 if (uh->read_chunked == 0) {
485 char *sep;
486
487 if (data[0] == '\r' && data[1] == '\n') {
488 data += 2;
489 read_len += 2;
490 }
491
492 sep = strstr(data, "\r\n");
493 if (!sep)
494 return 0;
495
496 *sep = 0;
497 uh->read_chunked = strtoul(data, NULL, 16);
498
499 read_len += sep + 2 - data;
500 data = sep + 2;
501
502 if (!uh->read_chunked)
503 uh->eof = true;
504 }
505
506 if (len > data_end - data)
507 len = data_end - data;
508
509 if (uh->read_chunked >= 0) {
510 if (len > uh->read_chunked)
511 len = uh->read_chunked;
512
513 uh->read_chunked -= len;
514 }
515
516 if (len > 0) {
517 read_len += len;
518 memcpy(buf, data, len);
519 }
520
521 if (read_len > 0)
522 ustream_consume(uh->us, read_len);
523
524 uclient_notify_eof(uh);
525
526 return len;
527 }
528
529 const struct uclient_backend uclient_backend_http __hidden = {
530 .prefix = uclient_http_prefix,
531
532 .alloc = uclient_http_alloc,
533 .free = uclient_http_free,
534 .connect = uclient_http_connect,
535 .update_url = uclient_http_disconnect,
536
537 .read = uclient_http_read,
538 .write = uclient_http_send_data,
539 .request = uclient_http_request_done,
540 };