bool vlc_http_file_can_seek(struct vlc_http_file *file) { /* See IETF RFC7233 */ int status = vlc_http_file_get_status(file); if (status < 0) return false; if (status == 206 || status == 416) return true; /* Partial Content */ return vlc_http_msg_get_token(file->resp, "Accept-Ranges", "bytes") != NULL; }
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); }
block_t *vlc_http_file_read(struct vlc_http_file *file) { if (vlc_http_file_get_status(file) < 0) return NULL; block_t *block = vlc_http_res_read(file->resp); if (block == NULL) { /* Automatically reconnect if server supports seek */ if (vlc_http_file_can_seek(file) && vlc_http_file_seek(file, file->offset) == 0) block = vlc_http_res_read(file->resp); if (block == NULL) return NULL; } file->offset += block->i_buffer; return block; }
char *vlc_http_file_get_type(struct vlc_http_file *file) { if (vlc_http_file_get_status(file) < 0) return NULL; return vlc_http_res_get_type(file->resp); }
char *vlc_http_file_get_redirect(struct vlc_http_file *file) { if (vlc_http_file_get_status(file) < 0) return NULL; return vlc_http_res_get_redirect(&file->resource, file->resp); }
static int Open(vlc_object_t *obj) { access_t *access = (access_t *)obj; access_sys_t *sys = malloc(sizeof (*sys)); int ret = VLC_ENOMEM; if (unlikely(sys == NULL)) return VLC_ENOMEM; sys->manager = NULL; sys->file = NULL; sys->manager = vlc_http_mgr_create(obj); if (sys->manager == NULL) goto error; char *ua = var_InheritString(obj, "http-user-agent"); char *ref = var_InheritString(obj, "http-referrer"); sys->file = vlc_http_file_create(sys->manager, access->psz_url, ua, ref); free(ref); free(ua); if (sys->file == NULL) goto error; char *redir = vlc_http_file_get_redirect(sys->file); if (redir != NULL) { access->psz_url = redir; ret = VLC_ACCESS_REDIRECT; goto error; } ret = VLC_EGENERIC; int status = vlc_http_file_get_status(sys->file); if (status < 0) { msg_Err(access, "HTTP connection failure"); goto error; } if (status >= 300) { msg_Err(access, "HTTP %d error", status); goto error; } access->info.b_eof = false; access->pf_read = NULL; access->pf_block = Read; access->pf_seek = Seek; access->pf_control = Control; access->p_sys = sys; return VLC_SUCCESS; error: if (sys->file != NULL) vlc_http_file_destroy(sys->file); if (sys->manager != NULL) vlc_http_mgr_destroy(sys->manager); free(sys); return ret; }
int main(void) { struct vlc_http_file *f; char *str; /* Request failure test */ f = vlc_http_file_create(NULL, url, ua, NULL); assert(f != NULL); vlc_http_file_seek(f, 0); assert(vlc_http_file_get_status(f) < 0); assert(vlc_http_file_get_redirect(f) == NULL); assert(vlc_http_file_get_size(f) == (uintmax_t)-1); assert(!vlc_http_file_can_seek(f)); assert(vlc_http_file_get_type(f) == NULL); assert(vlc_http_file_read(f) == NULL); vlc_http_file_destroy(f); /* Non-seekable stream test */ replies[0] = "HTTP/1.1 200 OK\r\n" "ETag: \"foobar42\"\r\n" "Content-Type: video/mpeg\r\n" "\r\n"; offset = 0; f = vlc_http_file_create(NULL, url, ua, NULL); assert(f != NULL); assert(vlc_http_file_get_status(f) == 200); assert(!vlc_http_file_can_seek(f)); assert(vlc_http_file_get_size(f) == (uintmax_t)-1); str = vlc_http_file_get_type(f); assert(str != NULL && !strcmp(str, "video/mpeg")); free(str); /* Seek failure */ replies[0] = "HTTP/1.1 200 OK\r\nETag: \"foobar42\"\r\n\r\n"; assert(vlc_http_file_seek(f, offset = 1234) < 0); vlc_http_file_destroy(f); /* Seekable file test */ replies[0] = "HTTP/1.1 206 Partial Content\r\n" "Content-Range: bytes 0-2344/2345\r\n" "ETag: W/\"foobar42\"\r\n" "\r\n"; offset = 0; f = vlc_http_file_create(NULL, url, ua, NULL); assert(f != NULL); assert(vlc_http_file_can_seek(f)); assert(vlc_http_file_get_size(f) == 2345); /* Seek success */ replies[0] = "HTTP/1.1 206 Partial Content\r\n" "Content-Range: bytes 1234-3455/3456\r\n" "ETag: W/\"foobar42\"\r\n" "\r\n"; assert(vlc_http_file_seek(f, offset = 1234) == 0); assert(vlc_http_file_can_seek(f)); assert(vlc_http_file_get_size(f) == 3456); /* Seek too far */ replies[0] = "HTTP/1.1 416 Range Not Satisfiable\r\n" "Content-Range: bytes */4567\r\n" "ETag: W/\"foobar42\"\r\n" "\r\n"; vlc_http_file_seek(f, offset = 5678); assert(vlc_http_file_can_seek(f)); assert(vlc_http_file_get_size(f) == 4567); assert(vlc_http_file_read(f) == NULL); vlc_http_file_destroy(f); /* Redirect */ replies[0] = "HTTP/1.1 301 Permanent Redirect\r\n" "Location: /somewhere/else/#here\r\n" "\r\n"; offset = 0; f = vlc_http_file_create(NULL, url, ua, NULL); assert(f != NULL); assert(!vlc_http_file_can_seek(f)); assert(vlc_http_file_get_size(f) == (uintmax_t)-1); str = vlc_http_file_get_redirect(f); assert(str != NULL && !strcmp(str, "https://www.example.com:8443/somewhere/else/")); free(str); vlc_http_file_destroy(f); /* Continuation */ replies[0] = "HTTP/1.1 100 Standby\r\n" "\r\n"; replies[1] = "HTTP/1.1 200 OK\r\n" "Content-Length: 9999\r\n" "\r\n"; offset = 0; f = vlc_http_file_create(NULL, url, ua, NULL); assert(f != NULL); assert(vlc_http_file_get_size(f) == 9999); assert(vlc_http_file_get_redirect(f) == NULL); vlc_http_file_destroy(f); /* Dummy API calls */ f = vlc_http_file_create(NULL, "ftp://localhost/foo", NULL, NULL); assert(f == NULL); return 0; }