static struct vlc_http_msg *vlc_http_file_open(struct vlc_http_file *file, uintmax_t offset) { struct vlc_http_msg *resp; resp = vlc_http_res_open(&file->resource, vlc_http_file_req, &offset); if (resp == NULL) return NULL; int status = vlc_http_msg_get_status(resp); if (status == 206) { const char *str = vlc_http_msg_get_header(resp, "Content-Range"); if (str == NULL) /* A multipart/byteranges response. This is not what we asked for * and we do not support it. */ goto fail; uintmax_t start, end; if (sscanf(str, "bytes %ju-%ju", &start, &end) != 2 || start != offset || start > end) /* A single range response is what we asked for, but not at that * start offset. */ goto fail; } return resp; fail: vlc_http_msg_destroy(resp); errno = EIO; return NULL; }
static int vlc_http_file_req(struct vlc_http_msg *req, const struct vlc_http_resource *res, void *opaque) { struct vlc_http_file *file = (struct vlc_http_file *)res; const uintmax_t *offset = opaque; if (file->resp != NULL) { const char *str = vlc_http_msg_get_header(file->resp, "ETag"); if (str != NULL) { if (!memcmp(str, "W/", 2)) str += 2; /* skip weak mark */ vlc_http_msg_add_header(req, "If-Match", "%s", str); } else { time_t mtime = vlc_http_msg_get_mtime(file->resp); if (mtime != -1) vlc_http_msg_add_time(req, "If-Unmodified-Since", &mtime); } } if (vlc_http_msg_add_header(req, "Range", "bytes=%ju-", *offset) && *offset != 0) return -1; return 0; }
struct vlc_http_msg *vlc_https_request(struct vlc_http_mgr *mgr, const char *host, unsigned port, const struct vlc_http_msg *req) { const char *str; char *end; assert(mgr == NULL); assert(!strcmp(host, "www.example.com")); assert(port == 8443); str = vlc_http_msg_get_method(req); assert(!strcmp(str, "GET")); str = vlc_http_msg_get_scheme(req); assert(!strcmp(str, "https")); str = vlc_http_msg_get_authority(req); assert(!strcmp(str, "www.example.com:8443")); str = vlc_http_msg_get_path(req); assert(!strcmp(str, "/dir/file.ext?a=b")); str = vlc_http_msg_get_agent(req); assert(!strcmp(str, ua)); str = vlc_http_msg_get_header(req, "Referer"); assert(str == NULL); str = vlc_http_msg_get_header(req, "Accept"); assert(str == NULL || strstr(str, "*/*") != NULL); str = vlc_http_msg_get_header(req, "Accept-Language"); assert(str == NULL || strstr(str, "*") != NULL); str = vlc_http_msg_get_header(req, "Range"); assert(str != NULL && !strncmp(str, "bytes=", 6) && strtoul(str + 6, &end, 10) == offset && *end == '-'); str = vlc_http_msg_get_header(req, "If-Match"); if (offset != 0) assert(str != NULL && !strcmp(str, "\"foobar42\"")); else assert(str == NULL || strcmp(str, "*") || strcmp(str, "\"foobar42\"")); return vlc_http_stream_read_headers(&stream); }
uintmax_t vlc_http_file_get_size(struct vlc_http_file *file) { int status = vlc_http_file_get_status(file); if (status < 0) return -1; const char *range = vlc_http_msg_get_header(file->resp, "Content-Range"); if (status == 206 /* Partial Content */) { /* IETF RFC7233 §4.1 */ assert(range != NULL); /* checked by vlc_http_file_open() */ uintmax_t end, total; switch (sscanf(range, "bytes %*u-%ju/%ju", &end, &total)) { case 1: if (unlikely(end == UINTMAX_MAX)) return -1; /* avoid wrapping to zero */ return end + 1; case 2: return total; } vlc_assert_unreachable(); /* checked by vlc_http_file_open() */ } if (status == 416 /* Range Not Satisfiable */) { /* IETF RFC7233 §4.4 */ uintmax_t total; if (range == NULL) return -1; /* valid but helpless response */ if (sscanf(range, "bytes */%ju", &total) == 1) return total; /* this occurs when seeking beyond EOF */ } if (status >= 300 || status == 201) return -1; /* Error or redirection, size is unknown/irrelevant. */ /* Content-Range is meaningless here (see RFC7233 B), so check if the size * of the response entity body is known. */ return vlc_http_msg_get_size(file->resp); }