uclient-fetch: use package name pattern in message for missing SSL library
[project/uclient.git] / uclient-http.c
index 195fa1c..ef8de98 100644 (file)
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
+#include <sys/socket.h>
 #include <stdio.h>
 #include <ctype.h>
 #include <unistd.h>
 #include <stdint.h>
+#include <string.h>
 #include <fcntl.h>
 
 #include <libubox/ustream.h>
@@ -90,6 +92,8 @@ struct uclient_http {
        long read_chunked;
        long content_length;
 
+       int usock_flags;
+
        uint32_t nc;
 
        struct blob_buf headers;
@@ -120,7 +124,9 @@ static int uclient_do_connect(struct uclient_http *uh, const char *port)
 
        memset(&uh->uc.remote_addr, 0, sizeof(uh->uc.remote_addr));
 
-       fd = usock_inet(USOCK_TCP, uh->uc.url->host, port, &uh->uc.remote_addr);
+       fd = usock_inet_timeout(USOCK_TCP | USOCK_NONBLOCK | uh->usock_flags,
+                               uh->uc.url->host, port, &uh->uc.remote_addr,
+                               uh->uc.timeout_msecs);
        if (fd < 0)
                return -1;
 
@@ -192,6 +198,9 @@ static void uclient_notify_eof(struct uclient_http *uh)
                        return;
        }
 
+       if (uh->content_length < 0 && uh->read_chunked >= 0)
+               uh->uc.data_eof = true;
+
        uclient_backend_set_eof(&uh->uc);
 
        if (uh->connection_close)
@@ -278,6 +287,18 @@ static void uclient_http_process_headers(struct uclient_http *uh)
        uh->auth_type = uclient_http_update_auth_type(uh);
 }
 
+static bool uclient_request_supports_body(enum request_type req_type)
+{
+       switch (req_type) {
+       case REQ_POST:
+       case REQ_PUT:
+       case REQ_DELETE:
+               return true;
+       default:
+               return false;
+       }
+}
+
 static void
 uclient_http_add_auth_basic(struct uclient_http *uh)
 {
@@ -359,11 +380,14 @@ get_cnonce(char *dest)
 {
        uint32_t val = 0;
        FILE *f;
+       size_t n;
 
        f = fopen("/dev/urandom", "r");
        if (f) {
-               fread(&val, sizeof(val), 1, f);
+               n = fread(&val, sizeof(val), 1, f);
                fclose(f);
+               if (n != 1)
+                       return;
        }
 
        bin_to_hex(dest, &val, sizeof(val));
@@ -539,6 +563,7 @@ uclient_http_send_headers(struct uclient_http *uh)
        struct uclient_url *url = uh->uc.url;
        struct blob_attr *cur;
        enum request_type req_type = uh->req_type;
+       bool literal_ipv6;
        int rem;
 
        if (uh->state >= HTTP_STATE_HEADERS_SENT)
@@ -547,16 +572,22 @@ uclient_http_send_headers(struct uclient_http *uh)
        if (uh->uc.proxy_url)
                url = uh->uc.proxy_url;
 
+       literal_ipv6 = strchr(url->host, ':');
+
        ustream_printf(uh->us,
                "%s %s HTTP/1.1\r\n"
-               "Host: %s\r\n",
-               request_types[req_type],
-               url->location, url->host);
+               "Host: %s%s%s%s%s\r\n",
+               request_types[req_type], url->location,
+               literal_ipv6 ? "[" : "",
+               url->host,
+               literal_ipv6 ? "]" : "",
+               url->port ? ":" : "",
+               url->port ? url->port : "");
 
        blobmsg_for_each_attr(cur, uh->headers.head, rem)
                ustream_printf(uh->us, "%s: %s\r\n", blobmsg_name(cur), (char *) blobmsg_data(cur));
 
-       if (uh->req_type == REQ_POST || uh->req_type == REQ_PUT)
+       if (uclient_request_supports_body(uh->req_type))
                ustream_printf(uh->us, "Transfer-Encoding: chunked\r\n");
 
        uclient_http_add_auth_header(uh);
@@ -663,28 +694,33 @@ static void __uclient_notify_read(struct uclient_http *uh)
                return;
 
        if (uh->state < HTTP_STATE_RECV_DATA) {
-               char *sep;
+               char *sep, *next;
                int cur_len;
 
                do {
-                       sep = strstr(data, "\r\n");
+                       sep = strchr(data, '\n');
                        if (!sep)
                                break;
 
+                       next = sep + 1;
+                       if (sep > data && sep[-1] == '\r')
+                               sep--;
+
                        /* Check for multi-line HTTP headers */
                        if (sep > data) {
-                               if (!sep[2])
+                               if (!*next)
                                        return;
 
-                               if (isspace(sep[2]) && sep[2] != '\r') {
+                               if (isspace(*next) && *next != '\r' && *next != '\n') {
                                        sep[0] = ' ';
-                                       sep[1] = ' ';
+                                       if (sep + 1 < next)
+                                               sep[1] = ' ';
                                        continue;
                                }
                        }
 
                        *sep = 0;
-                       cur_len = sep + 2 - data;
+                       cur_len = next - data;
                        uclient_parse_http_line(uh, data);
                        if (seq != uh->seq)
                                return;
@@ -979,7 +1015,7 @@ uclient_http_request_done(struct uclient *cl)
                return -1;
 
        uclient_http_send_headers(uh);
-       if (uh->req_type == REQ_POST || uh->req_type == REQ_PUT)
+       if (uclient_request_supports_body(uh->req_type))
                ustream_printf(uh->us, "0\r\n\r\n");
        uh->state = HTTP_STATE_REQUEST_DONE;
 
@@ -1063,7 +1099,7 @@ uclient_http_read(struct uclient *cl, char *buf, unsigned int len)
        return len;
 }
 
-bool uclient_http_redirect(struct uclient *cl)
+int uclient_http_redirect(struct uclient *cl)
 {
        struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
        struct blobmsg_policy location = {
@@ -1089,13 +1125,15 @@ bool uclient_http_redirect(struct uclient *cl)
        if (!tb)
                return false;
 
-       url = uclient_get_url(blobmsg_data(tb), url->auth);
+       url = uclient_get_url_location(url, blobmsg_data(tb));
        if (!url)
                return false;
 
        free(cl->url);
        cl->url = url;
-       uclient_http_connect(cl);
+       if (uclient_http_connect(cl))
+               return -1;
+
        uclient_http_request_done(cl);
 
        return true;
@@ -1119,6 +1157,28 @@ int uclient_http_set_ssl_ctx(struct uclient *cl, const struct ustream_ssl_ops *o
        return 0;
 }
 
+int uclient_http_set_address_family(struct uclient *cl, int af)
+{
+       struct uclient_http *uh = container_of(cl, struct uclient_http, uc);
+
+       if (cl->backend != &uclient_backend_http)
+               return -1;
+
+       switch (af) {
+       case AF_INET:
+               uh->usock_flags = USOCK_IPV4ONLY;
+               break;
+       case AF_INET6:
+               uh->usock_flags = USOCK_IPV6ONLY;
+               break;
+       default:
+               uh->usock_flags = 0;
+               break;
+       }
+
+       return 0;
+}
+
 const struct uclient_backend uclient_backend_http = {
        .prefix = uclient_http_prefix,