add a utility function to detect redirects and handle them
[project/uclient.git] / uclient-http.c
1 #include <stdio.h>
2 #include <ctype.h>
3 #include <unistd.h>
4 #include <stdint.h>
5
6 #include <libubox/ustream.h>
7 #include <libubox/ustream-ssl.h>
8 #include <libubox/usock.h>
9 #include <libubox/blobmsg.h>
10
11 #include "uclient.h"
12 #include "uclient-utils.h"
13 #include "uclient-backend.h"
14
15 static struct ustream_ssl_ctx *ssl_ctx;
16 static uint32_t nc;
17
18 enum auth_type {
19 AUTH_TYPE_UNKNOWN,
20 AUTH_TYPE_NONE,
21 AUTH_TYPE_BASIC,
22 AUTH_TYPE_DIGEST,
23 };
24
25 enum request_type {
26 REQ_GET,
27 REQ_HEAD,
28 REQ_POST,
29 __REQ_MAX
30 };
31
32 enum http_state {
33 HTTP_STATE_INIT,
34 HTTP_STATE_HEADERS_SENT,
35 HTTP_STATE_REQUEST_DONE,
36 HTTP_STATE_RECV_HEADERS,
37 HTTP_STATE_RECV_DATA,
38 HTTP_STATE_ERROR,
39 };
40
41 static const char * const request_types[__REQ_MAX] = {
42 [REQ_GET] = "GET",
43 [REQ_HEAD] = "HEAD",
44 [REQ_POST] = "POST",
45 };
46
47 struct uclient_http {
48 struct uclient uc;
49
50 struct ustream *us;
51
52 struct ustream_fd ufd;
53 struct ustream_ssl ussl;
54
55 bool ssl;
56 bool eof;
57 bool connection_close;
58 enum request_type req_type;
59 enum http_state state;
60
61 enum auth_type auth_type;
62 char *auth_str;
63
64 long read_chunked;
65 long content_length;
66
67 struct blob_buf headers;
68 struct blob_buf meta;
69 };
70
71 enum {
72 PREFIX_HTTP,
73 PREFIX_HTTPS,
74 __PREFIX_MAX,
75 };
76
77 static const char * const uclient_http_prefix[] = {
78 [PREFIX_HTTP] = "http://",
79 [PREFIX_HTTPS] = "https://",
80 [__PREFIX_MAX] = NULL
81 };
82
83 static int uclient_do_connect(struct uclient_http *uh, const char *port)
84 {
85 int fd;
86
87 if (uh->uc.url->port)
88 port = uh->uc.url->port;
89
90 fd = usock(USOCK_TCP | USOCK_NONBLOCK, uh->uc.url->host, port);
91 if (fd < 0)
92 return -1;
93
94 ustream_fd_init(&uh->ufd, fd);
95 return 0;
96 }
97
98 static void uclient_http_disconnect(struct uclient_http *uh)
99 {
100 if (!uh->us)
101 return;
102
103 if (uh->ssl)
104 ustream_free(&uh->ussl.stream);
105 ustream_free(&uh->ufd.stream);
106 close(uh->ufd.fd.fd);
107 uh->us = NULL;
108 }
109
110 static void uclient_http_free_url_state(struct uclient *cl)
111 {
112 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
113
114 uh->auth_type = AUTH_TYPE_UNKNOWN;
115 free(uh->auth_str);
116 uh->auth_str = NULL;
117 uclient_http_disconnect(uh);
118 }
119
120 static void uclient_notify_eof(struct uclient_http *uh)
121 {
122 struct ustream *us = uh->us;
123
124 if (!uh->eof) {
125 if (!us->eof && !us->write_error)
126 return;
127
128 if (ustream_pending_data(us, false))
129 return;
130 }
131
132 uclient_backend_set_eof(&uh->uc);
133
134 if (uh->connection_close)
135 uclient_http_disconnect(uh);
136 }
137
138 static void uclient_http_reset_state(struct uclient_http *uh)
139 {
140 uclient_backend_reset_state(&uh->uc);
141 uh->read_chunked = -1;
142 uh->content_length = -1;
143 uh->eof = false;
144 uh->connection_close = false;
145 uh->state = HTTP_STATE_INIT;
146
147 if (uh->auth_type == AUTH_TYPE_UNKNOWN && !uh->uc.url->auth)
148 uh->auth_type = AUTH_TYPE_NONE;
149 }
150
151 static void uclient_http_init_request(struct uclient_http *uh)
152 {
153 uclient_http_reset_state(uh);
154 blob_buf_init(&uh->meta, 0);
155 }
156
157 static enum auth_type
158 uclient_http_update_auth_type(struct uclient_http *uh)
159 {
160 if (!uh->auth_str)
161 return AUTH_TYPE_NONE;
162
163 if (!strncasecmp(uh->auth_str, "basic", 5))
164 return AUTH_TYPE_BASIC;
165
166 if (!strncasecmp(uh->auth_str, "digest", 6))
167 return AUTH_TYPE_DIGEST;
168
169 return AUTH_TYPE_NONE;
170 }
171
172 static void uclient_http_process_headers(struct uclient_http *uh)
173 {
174 enum {
175 HTTP_HDR_TRANSFER_ENCODING,
176 HTTP_HDR_CONNECTION,
177 HTTP_HDR_CONTENT_LENGTH,
178 HTTP_HDR_AUTH,
179 __HTTP_HDR_MAX,
180 };
181 static const struct blobmsg_policy hdr_policy[__HTTP_HDR_MAX] = {
182 #define hdr(_name) { .name = _name, .type = BLOBMSG_TYPE_STRING }
183 [HTTP_HDR_TRANSFER_ENCODING] = hdr("transfer-encoding"),
184 [HTTP_HDR_CONNECTION] = hdr("connection"),
185 [HTTP_HDR_CONTENT_LENGTH] = hdr("content-length"),
186 [HTTP_HDR_AUTH] = hdr("www-authenticate"),
187 #undef hdr
188 };
189 struct blob_attr *tb[__HTTP_HDR_MAX];
190 struct blob_attr *cur;
191
192 blobmsg_parse(hdr_policy, __HTTP_HDR_MAX, tb, blob_data(uh->meta.head), blob_len(uh->meta.head));
193
194 cur = tb[HTTP_HDR_TRANSFER_ENCODING];
195 if (cur && strstr(blobmsg_data(cur), "chunked"))
196 uh->read_chunked = 0;
197
198 cur = tb[HTTP_HDR_CONNECTION];
199 if (cur && strstr(blobmsg_data(cur), "close"))
200 uh->connection_close = true;
201
202 cur = tb[HTTP_HDR_CONTENT_LENGTH];
203 if (cur)
204 uh->content_length = strtoul(blobmsg_data(cur), NULL, 10);
205
206 cur = tb[HTTP_HDR_AUTH];
207 if (cur) {
208 free(uh->auth_str);
209 uh->auth_str = strdup(blobmsg_data(cur));
210 }
211
212 uh->auth_type = uclient_http_update_auth_type(uh);
213 }
214
215 static void
216 uclient_http_add_auth_basic(struct uclient_http *uh)
217 {
218 struct uclient_url *url = uh->uc.url;
219 int auth_len = strlen(url->auth);
220 char *auth_buf;
221
222 if (auth_len > 512)
223 return;
224
225 auth_buf = alloca(base64_len(auth_len) + 1);
226 base64_encode(url->auth, auth_len, auth_buf);
227 ustream_printf(uh->us, "Authorization: Basic %s\r\n", auth_buf);
228 }
229
230 static char *digest_unquote_sep(char **str)
231 {
232 char *cur = *str + 1;
233 char *start = cur;
234 char *out;
235
236 if (**str != '"')
237 return NULL;
238
239 out = cur;
240 while (1) {
241 if (!*cur)
242 return NULL;
243
244 if (*cur == '"') {
245 cur++;
246 break;
247 }
248
249 if (*cur == '\\')
250 cur++;
251
252 *(out++) = *(cur++);
253 }
254
255 if (*cur == ',')
256 cur++;
257
258 *out = 0;
259 *str = cur;
260
261 return start;
262 }
263
264 static bool strmatch(char **str, const char *prefix)
265 {
266 int len = strlen(prefix);
267
268 if (strncmp(*str, prefix, len) != 0 || (*str)[len] != '=')
269 return false;
270
271 *str += len + 1;
272 return true;
273 }
274
275 static void
276 get_cnonce(char *dest)
277 {
278 uint32_t val = nc;
279 FILE *f;
280
281 f = fopen("/dev/urandom", "r");
282 if (f) {
283 fread(&val, sizeof(val), 1, f);
284 fclose(f);
285 }
286
287 bin_to_hex(dest, &val, sizeof(val));
288 }
289
290 static void add_field(char **buf, int *ofs, int *len, const char *name, const char *val)
291 {
292 int available = *len - *ofs;
293 int required;
294 const char *next;
295 char *cur;
296
297 if (*len && !*buf)
298 return;
299
300 required = strlen(name) + 4 + strlen(val) * 2;
301 if (required > available)
302 *len += required - available + 64;
303
304 *buf = realloc(*buf, *len);
305 if (!*buf)
306 return;
307
308 cur = *buf + *ofs;
309 cur += sprintf(cur, ", %s=\"", name);
310
311 while ((next = strchr(val, '"'))) {
312 if (next > val) {
313 memcpy(cur, val, next - val);
314 cur += next - val;
315 }
316
317 cur += sprintf(cur, "\\\"");
318 val = next + 1;
319 }
320
321 cur += sprintf(cur, "%s\"", val);
322 *ofs = cur - *buf;
323 }
324
325 static void
326 uclient_http_add_auth_digest(struct uclient_http *uh)
327 {
328 struct uclient_url *url = uh->uc.url;
329 const char *realm = NULL, *opaque = NULL;
330 const char *user, *password;
331 char *buf, *next;
332 int len, ofs;
333
334 char cnonce_str[9];
335 char nc_str[9];
336 char ahash[33];
337 char hash[33];
338
339 struct http_digest_data data = {
340 .nc = nc_str,
341 .cnonce = cnonce_str,
342 .auth_hash = ahash,
343 };
344
345 len = strlen(uh->auth_str) + 1;
346 if (len > 512)
347 return;
348
349 buf = alloca(len);
350 strcpy(buf, uh->auth_str);
351
352 /* skip auth type */
353 strsep(&buf, " ");
354
355 next = buf;
356 while (*next) {
357 const char **dest = NULL;
358
359 while (isspace(*next))
360 next++;
361
362 if (strmatch(&next, "realm"))
363 dest = &realm;
364 else if (strmatch(&next, "qop"))
365 dest = &data.qop;
366 else if (strmatch(&next, "nonce"))
367 dest = &data.nonce;
368 else if (strmatch(&next, "opaque"))
369 dest = &opaque;
370 else
371 return;
372
373 *dest = digest_unquote_sep(&next);
374 }
375
376 if (!realm || !data.qop || !data.nonce)
377 return;
378
379 sprintf(nc_str, "%08x", nc++);
380 get_cnonce(cnonce_str);
381
382 data.qop = "auth";
383 data.uri = url->location;
384 data.method = request_types[uh->req_type];
385
386 password = strchr(url->auth, ':');
387 if (password) {
388 char *user_buf;
389
390 len = password - url->auth;
391 if (len > 256)
392 return;
393
394 user_buf = alloca(len + 1);
395 strncpy(user_buf, url->auth, len);
396 user_buf[len] = 0;
397 user = user_buf;
398 password++;
399 } else {
400 user = url->auth;
401 password = "";
402 }
403
404 http_digest_calculate_auth_hash(ahash, user, realm, password);
405 http_digest_calculate_response(hash, &data);
406
407 buf = NULL;
408 len = 0;
409 ofs = 0;
410
411 add_field(&buf, &ofs, &len, "username", user);
412 add_field(&buf, &ofs, &len, "realm", realm);
413 add_field(&buf, &ofs, &len, "nonce", data.nonce);
414 add_field(&buf, &ofs, &len, "uri", data.uri);
415 add_field(&buf, &ofs, &len, "cnonce", data.cnonce);
416 add_field(&buf, &ofs, &len, "response", hash);
417 if (opaque)
418 add_field(&buf, &ofs, &len, "opaque", opaque);
419
420 ustream_printf(uh->us, "Authorization: Digest nc=%s, qop=%s%s\r\n", data.nc, data.qop, buf);
421 free(buf);
422 }
423
424 static void
425 uclient_http_add_auth_header(struct uclient_http *uh)
426 {
427 if (!uh->uc.url->auth)
428 return;
429
430 switch (uh->auth_type) {
431 case AUTH_TYPE_UNKNOWN:
432 case AUTH_TYPE_NONE:
433 break;
434 case AUTH_TYPE_BASIC:
435 uclient_http_add_auth_basic(uh);
436 break;
437 case AUTH_TYPE_DIGEST:
438 uclient_http_add_auth_digest(uh);
439 break;
440 }
441 }
442
443 static void
444 uclient_http_send_headers(struct uclient_http *uh)
445 {
446 struct uclient_url *url = uh->uc.url;
447 struct blob_attr *cur;
448 enum request_type req_type = uh->req_type;
449 int rem;
450
451 if (uh->state >= HTTP_STATE_HEADERS_SENT)
452 return;
453
454 if (uh->auth_type == AUTH_TYPE_UNKNOWN)
455 req_type = REQ_HEAD;
456
457 ustream_printf(uh->us,
458 "%s %s HTTP/1.1\r\n"
459 "Host: %s\r\n",
460 request_types[req_type],
461 url->location, url->host);
462
463 blobmsg_for_each_attr(cur, uh->headers.head, rem)
464 ustream_printf(uh->us, "%s: %s\n", blobmsg_name(cur), (char *) blobmsg_data(cur));
465
466 if (uh->req_type == REQ_POST)
467 ustream_printf(uh->us, "Transfer-Encoding: chunked\r\n");
468
469 uclient_http_add_auth_header(uh);
470
471 ustream_printf(uh->us, "\r\n");
472 }
473
474 static void uclient_http_headers_complete(struct uclient_http *uh)
475 {
476 enum auth_type auth_type = uh->auth_type;
477
478 uh->state = HTTP_STATE_RECV_DATA;
479 uh->uc.meta = uh->meta.head;
480 uclient_http_process_headers(uh);
481
482 if (auth_type == AUTH_TYPE_UNKNOWN) {
483 uclient_http_init_request(uh);
484 uclient_http_send_headers(uh);
485 uh->state = HTTP_STATE_REQUEST_DONE;
486 return;
487 }
488
489 if (uh->uc.cb->header_done)
490 uh->uc.cb->header_done(&uh->uc);
491
492 if (uh->req_type == REQ_HEAD) {
493 uh->eof = true;
494 uclient_notify_eof(uh);
495 }
496 }
497
498 static void uclient_parse_http_line(struct uclient_http *uh, char *data)
499 {
500 char *name;
501 char *sep;
502
503 if (uh->state == HTTP_STATE_REQUEST_DONE) {
504 char *code;
505
506 /* HTTP/1.1 */
507 strsep(&data, " ");
508
509 code = strsep(&data, " ");
510 if (!code)
511 goto error;
512
513 uh->uc.status_code = strtoul(code, &sep, 10);
514 if (sep && *sep)
515 goto error;
516
517 uh->state = HTTP_STATE_RECV_HEADERS;
518 return;
519 }
520
521 if (!*data) {
522 uclient_http_headers_complete(uh);
523 return;
524 }
525
526 sep = strchr(data, ':');
527 if (!sep)
528 return;
529
530 *(sep++) = 0;
531
532 for (name = data; *name; name++)
533 *name = tolower(*name);
534
535 name = data;
536 while (isspace(*sep))
537 sep++;
538
539 blobmsg_add_string(&uh->meta, name, sep);
540 return;
541
542 error:
543 uh->uc.status_code = 400;
544 uh->eof = true;
545 uclient_notify_eof(uh);
546 }
547
548 static void __uclient_notify_read(struct uclient_http *uh)
549 {
550 struct uclient *uc = &uh->uc;
551 char *data;
552 int len;
553
554 if (uh->state < HTTP_STATE_REQUEST_DONE)
555 return;
556
557 data = ustream_get_read_buf(uh->us, &len);
558 if (!data || !len)
559 return;
560
561 if (uh->state < HTTP_STATE_RECV_DATA) {
562 char *sep;
563 int cur_len;
564
565 do {
566 sep = strstr(data, "\r\n");
567 if (!sep)
568 break;
569
570 /* Check for multi-line HTTP headers */
571 if (sep > data) {
572 if (!sep[2])
573 return;
574
575 if (isspace(sep[2]) && sep[2] != '\r') {
576 sep[0] = ' ';
577 sep[1] = ' ';
578 continue;
579 }
580 }
581
582 *sep = 0;
583 cur_len = sep + 2 - data;
584 uclient_parse_http_line(uh, data);
585 ustream_consume(uh->us, cur_len);
586 len -= cur_len;
587
588 data = ustream_get_read_buf(uh->us, &len);
589 } while (data && uh->state < HTTP_STATE_RECV_DATA);
590
591 if (!len)
592 return;
593 }
594
595 if (uh->state == HTTP_STATE_RECV_DATA && uc->cb->data_read)
596 uc->cb->data_read(uc);
597 }
598
599 static void uclient_notify_read(struct ustream *us, int bytes)
600 {
601 struct uclient_http *uh = container_of(us, struct uclient_http, ufd.stream);
602
603 __uclient_notify_read(uh);
604 }
605
606 static void uclient_notify_state(struct ustream *us)
607 {
608 struct uclient_http *uh = container_of(us, struct uclient_http, ufd.stream);
609
610 uclient_notify_eof(uh);
611 }
612
613 static int uclient_setup_http(struct uclient_http *uh)
614 {
615 struct ustream *us = &uh->ufd.stream;
616 int ret;
617
618 uh->us = us;
619 us->string_data = true;
620 us->notify_state = uclient_notify_state;
621 us->notify_read = uclient_notify_read;
622
623 ret = uclient_do_connect(uh, "80");
624 if (ret)
625 return ret;
626
627 return 0;
628 }
629
630 static void uclient_ssl_notify_read(struct ustream *us, int bytes)
631 {
632 struct uclient_http *uh = container_of(us, struct uclient_http, ussl.stream);
633
634 __uclient_notify_read(uh);
635 }
636
637 static void uclient_ssl_notify_state(struct ustream *us)
638 {
639 struct uclient_http *uh = container_of(us, struct uclient_http, ussl.stream);
640
641 uclient_notify_eof(uh);
642 }
643
644 static int uclient_setup_https(struct uclient_http *uh)
645 {
646 struct ustream *us = &uh->ussl.stream;
647 int ret;
648
649 uh->ssl = true;
650 uh->us = us;
651
652 ret = uclient_do_connect(uh, "443");
653 if (ret)
654 return ret;
655
656 if (!ssl_ctx)
657 ssl_ctx = ustream_ssl_context_new(false);
658
659 us->string_data = true;
660 us->notify_state = uclient_ssl_notify_state;
661 us->notify_read = uclient_ssl_notify_read;
662 ustream_ssl_init(&uh->ussl, &uh->ufd.stream, ssl_ctx, false);
663
664 return 0;
665 }
666
667 static int uclient_http_connect(struct uclient *cl)
668 {
669 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
670
671 uclient_http_init_request(uh);
672
673 if (uh->us)
674 return 0;
675
676 uh->ssl = cl->url->prefix == PREFIX_HTTPS;
677
678 if (uh->ssl)
679 return uclient_setup_https(uh);
680 else
681 return uclient_setup_http(uh);
682 }
683
684 static struct uclient *uclient_http_alloc(void)
685 {
686 struct uclient_http *uh;
687
688 uh = calloc_a(sizeof(*uh));
689 blob_buf_init(&uh->headers, 0);
690
691 return &uh->uc;
692 }
693
694 static void uclient_http_free(struct uclient *cl)
695 {
696 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
697
698 uclient_http_free_url_state(cl);
699 blob_buf_free(&uh->headers);
700 blob_buf_free(&uh->meta);
701 free(uh);
702 }
703
704 int
705 uclient_http_set_request_type(struct uclient *cl, const char *type)
706 {
707 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
708 int i;
709
710 if (cl->backend != &uclient_backend_http)
711 return -1;
712
713 if (uh->state > HTTP_STATE_INIT)
714 return -1;
715
716 for (i = 0; i < ARRAY_SIZE(request_types); i++) {
717 if (strcmp(request_types[i], type) != 0)
718 continue;
719
720 uh->req_type = i;
721 return 0;
722 }
723
724 return -1;
725 }
726
727 int
728 uclient_http_reset_headers(struct uclient *cl, const char *name, const char *value)
729 {
730 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
731
732 blob_buf_init(&uh->headers, 0);
733
734 return 0;
735 }
736
737 int
738 uclient_http_set_header(struct uclient *cl, const char *name, const char *value)
739 {
740 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
741
742 if (cl->backend != &uclient_backend_http)
743 return -1;
744
745 if (uh->state > HTTP_STATE_INIT)
746 return -1;
747
748 blobmsg_add_string(&uh->headers, name, value);
749 return 0;
750 }
751
752 static int
753 uclient_http_send_data(struct uclient *cl, char *buf, unsigned int len)
754 {
755 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
756
757 if (uh->state >= HTTP_STATE_REQUEST_DONE)
758 return -1;
759
760 uclient_http_send_headers(uh);
761
762 ustream_printf(uh->us, "%X\r\n", len);
763 if (len > 0)
764 ustream_write(uh->us, buf, len, false);
765 ustream_printf(uh->us, "\r\n");
766
767 return len;
768 }
769
770 static int
771 uclient_http_request_done(struct uclient *cl)
772 {
773 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
774
775 if (uh->state >= HTTP_STATE_REQUEST_DONE)
776 return -1;
777
778 uclient_http_send_headers(uh);
779 uh->state = HTTP_STATE_REQUEST_DONE;
780
781 return 0;
782 }
783
784 static int
785 uclient_http_read(struct uclient *cl, char *buf, unsigned int len)
786 {
787 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
788 int read_len = 0;
789 char *data, *data_end;
790
791 if (uh->state < HTTP_STATE_RECV_DATA || !uh->us)
792 return 0;
793
794 data = ustream_get_read_buf(uh->us, &read_len);
795 if (!data || !read_len)
796 return 0;
797
798 data_end = data + read_len;
799 read_len = 0;
800
801 if (uh->read_chunked == 0) {
802 char *sep;
803
804 if (data[0] == '\r' && data[1] == '\n') {
805 data += 2;
806 read_len += 2;
807 }
808
809 sep = strstr(data, "\r\n");
810 if (!sep)
811 return 0;
812
813 *sep = 0;
814 uh->read_chunked = strtoul(data, NULL, 16);
815
816 read_len += sep + 2 - data;
817 data = sep + 2;
818
819 if (!uh->read_chunked)
820 uh->eof = true;
821 }
822
823 if (len > data_end - data)
824 len = data_end - data;
825
826 if (uh->read_chunked >= 0) {
827 if (len > uh->read_chunked)
828 len = uh->read_chunked;
829
830 uh->read_chunked -= len;
831 } else if (uh->content_length >= 0) {
832 if (len > uh->content_length)
833 len = uh->content_length;
834
835 uh->content_length -= len;
836 if (!uh->content_length)
837 uh->eof = true;
838 }
839
840 if (len > 0) {
841 read_len += len;
842 memcpy(buf, data, len);
843 }
844
845 if (read_len > 0)
846 ustream_consume(uh->us, read_len);
847
848 uclient_notify_eof(uh);
849
850 return len;
851 }
852
853 bool uclient_http_redirect(struct uclient *cl)
854 {
855 struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
856 struct blobmsg_policy location = {
857 .name = "location",
858 .type = BLOBMSG_TYPE_STRING,
859 };
860 struct uclient_url *url = cl->url;
861 struct blob_attr *tb;
862
863 if (cl->backend != &uclient_backend_http)
864 return false;
865
866 switch (cl->status_code) {
867 case 301:
868 case 302:
869 case 307:
870 break;
871 default:
872 return false;
873 }
874
875 blobmsg_parse(&location, 1, &tb, blob_data(uh->meta.head), blob_len(uh->meta.head));
876 if (!tb)
877 return false;
878
879 url = uclient_get_url(blobmsg_data(tb), url->auth);
880 if (!url)
881 return false;
882
883 free(cl->url);
884 cl->url = url;
885 uclient_http_connect(cl);
886 uclient_http_request_done(cl);
887
888 return true;
889 }
890
891 const struct uclient_backend uclient_backend_http = {
892 .prefix = uclient_http_prefix,
893
894 .alloc = uclient_http_alloc,
895 .free = uclient_http_free,
896 .connect = uclient_http_connect,
897 .update_url = uclient_http_free_url_state,
898
899 .read = uclient_http_read,
900 .write = uclient_http_send_data,
901 .request = uclient_http_request_done,
902 };