add support for passing in an external ustream_ssl context
[project/uclient.git] / uclient-http.c
index c344513..045a772 100644 (file)
@@ -12,9 +12,6 @@
 #include "uclient-utils.h"
 #include "uclient-backend.h"
 
-static struct ustream_ssl_ctx *ssl_ctx;
-static uint32_t nc;
-
 enum auth_type {
        AUTH_TYPE_UNKNOWN,
        AUTH_TYPE_NONE,
@@ -47,11 +44,13 @@ static const char * const request_types[__REQ_MAX] = {
 struct uclient_http {
        struct uclient uc;
 
+       struct ustream_ssl_ctx *ssl_ctx;
        struct ustream *us;
 
        struct ustream_fd ufd;
        struct ustream_ssl ussl;
 
+       bool ssl_ctx_ext;
        bool ssl;
        bool eof;
        bool connection_close;
@@ -64,6 +63,8 @@ struct uclient_http {
        long read_chunked;
        long content_length;
 
+       uint32_t nc;
+
        struct blob_buf headers;
        struct blob_buf meta;
 };
@@ -275,7 +276,7 @@ static bool strmatch(char **str, const char *prefix)
 static void
 get_cnonce(char *dest)
 {
-       uint32_t val = nc;
+       uint32_t val = 0;
        FILE *f;
 
        f = fopen("/dev/urandom", "r");
@@ -376,7 +377,7 @@ uclient_http_add_auth_digest(struct uclient_http *uh)
        if (!realm || !data.qop || !data.nonce)
                return;
 
-       sprintf(nc_str, "%08x", nc++);
+       sprintf(nc_str, "%08x", uh->nc++);
        get_cnonce(cnonce_str);
 
        data.qop = "auth";
@@ -501,6 +502,19 @@ static void uclient_parse_http_line(struct uclient_http *uh, char *data)
        char *sep;
 
        if (uh->state == HTTP_STATE_REQUEST_DONE) {
+               char *code;
+
+               /* HTTP/1.1 */
+               strsep(&data, " ");
+
+               code = strsep(&data, " ");
+               if (!code)
+                       goto error;
+
+               uh->uc.status_code = strtoul(code, &sep, 10);
+               if (sep && *sep)
+                       goto error;
+
                uh->state = HTTP_STATE_RECV_HEADERS;
                return;
        }
@@ -524,6 +538,12 @@ static void uclient_parse_http_line(struct uclient_http *uh, char *data)
                sep++;
 
        blobmsg_add_string(&uh->meta, name, sep);
+       return;
+
+error:
+       uh->uc.status_code = 400;
+       uh->eof = true;
+       uclient_notify_eof(uh);
 }
 
 static void __uclient_notify_read(struct uclient_http *uh)
@@ -634,13 +654,13 @@ static int uclient_setup_https(struct uclient_http *uh)
        if (ret)
                return ret;
 
-       if (!ssl_ctx)
-               ssl_ctx = ustream_ssl_context_new(false);
+       if (!uh->ssl_ctx)
+               uh->ssl_ctx = ustream_ssl_context_new(false);
 
        us->string_data = true;
        us->notify_state = uclient_ssl_notify_state;
        us->notify_read = uclient_ssl_notify_read;
-       ustream_ssl_init(&uh->ussl, &uh->ufd.stream, ssl_ctx, false);
+       ustream_ssl_init(&uh->ussl, &uh->ufd.stream, uh->ssl_ctx, false);
 
        return 0;
 }
@@ -672,10 +692,19 @@ static struct uclient *uclient_http_alloc(void)
        return &uh->uc;
 }
 
+static void uclient_http_free_ssl_ctx(struct uclient_http *uh)
+{
+       if (uh->ssl_ctx && !uh->ssl_ctx_ext)
+               ustream_ssl_context_free(uh->ssl_ctx);
+
+       uh->ssl_ctx_ext = false;
+}
+
 static void uclient_http_free(struct uclient *cl)
 {
        struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
 
+       uclient_http_free_ssl_ctx(uh);
        uclient_http_free_url_state(cl);
        blob_buf_free(&uh->headers);
        blob_buf_free(&uh->meta);
@@ -831,7 +860,58 @@ uclient_http_read(struct uclient *cl, char *buf, unsigned int len)
        return len;
 }
 
-const struct uclient_backend uclient_backend_http __hidden = {
+bool uclient_http_redirect(struct uclient *cl)
+{
+       struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
+       struct blobmsg_policy location = {
+               .name = "location",
+               .type = BLOBMSG_TYPE_STRING,
+       };
+       struct uclient_url *url = cl->url;
+       struct blob_attr *tb;
+
+       if (cl->backend != &uclient_backend_http)
+               return false;
+
+       switch (cl->status_code) {
+       case 301:
+       case 302:
+       case 307:
+               break;
+       default:
+               return false;
+       }
+
+       blobmsg_parse(&location, 1, &tb, blob_data(uh->meta.head), blob_len(uh->meta.head));
+       if (!tb)
+               return false;
+
+       url = uclient_get_url(blobmsg_data(tb), url->auth);
+       if (!url)
+               return false;
+
+       free(cl->url);
+       cl->url = url;
+       uclient_http_connect(cl);
+       uclient_http_request_done(cl);
+
+       return true;
+}
+
+int uclient_http_set_ssl_ctx(struct uclient *cl, struct ustream_ssl_ctx *ctx)
+{
+       struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
+
+       uclient_http_free_url_state(cl);
+
+       uclient_http_free_ssl_ctx(uh);
+       uh->ssl_ctx = ctx;
+       uh->ssl_ctx_ext = !!ctx;
+
+       return 0;
+}
+
+const struct uclient_backend uclient_backend_http = {
        .prefix = uclient_http_prefix,
 
        .alloc = uclient_http_alloc,