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