Commit b561929d authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

http: factor more common between file and live streams

parent f9c09233
......@@ -33,24 +33,21 @@
#include <vlc_network.h> /* FIXME: only for vlc_getProxyUrl() */
#include "connmgr.h"
#include "resource.h"
#include "file.h"
#include "live.h"
struct access_sys_t
{
struct vlc_http_mgr *manager;
union
{
struct vlc_http_file *file;
struct vlc_http_live *live;
};
struct vlc_http_resource *resource;
};
static block_t *FileRead(access_t *access)
{
access_sys_t *sys = access->p_sys;
block_t *b = vlc_http_file_read(sys->file);
block_t *b = vlc_http_file_read(sys->resource);
if (b == NULL)
access->info.b_eof = true;
return b;
......@@ -61,7 +58,7 @@ static int FileSeek(access_t *access, uint64_t pos)
access_sys_t *sys = access->p_sys;
access->info.b_eof = false;
if (vlc_http_file_seek(sys->file, pos))
if (vlc_http_file_seek(sys->resource, pos))
return VLC_EGENERIC;
return VLC_SUCCESS;
}
......@@ -73,7 +70,7 @@ static int FileControl(access_t *access, int query, va_list args)
switch (query)
{
case ACCESS_CAN_SEEK:
*va_arg(args, bool *) = vlc_http_file_can_seek(sys->file);
*va_arg(args, bool *) = vlc_http_file_can_seek(sys->resource);
break;
case ACCESS_CAN_FASTSEEK:
......@@ -87,7 +84,7 @@ static int FileControl(access_t *access, int query, va_list args)
case ACCESS_GET_SIZE:
{
uintmax_t val = vlc_http_file_get_size(sys->file);
uintmax_t val = vlc_http_file_get_size(sys->resource);
if (val >= UINT64_MAX)
return VLC_EGENERIC;
......@@ -101,7 +98,7 @@ static int FileControl(access_t *access, int query, va_list args)
break;
case ACCESS_GET_CONTENT_TYPE:
*va_arg(args, char **) = vlc_http_file_get_type(sys->file);
*va_arg(args, char **) = vlc_http_file_get_type(sys->resource);
break;
case ACCESS_SET_PAUSE_STATE:
......@@ -117,7 +114,7 @@ static block_t *LiveRead(access_t *access)
{
access_sys_t *sys = access->p_sys;
block_t *b = vlc_http_live_read(sys->live);
block_t *b = vlc_http_live_read(sys->resource);
if (b == NULL) /* TODO: loop instead of EOF, see vlc_http_live_read() */
access->info.b_eof = true;
return b;
......@@ -149,7 +146,7 @@ static int LiveControl(access_t *access, int query, va_list args)
break;
case ACCESS_GET_CONTENT_TYPE:
*va_arg(args, char **) = vlc_http_live_get_type(sys->live);
*va_arg(args, char **) = vlc_http_live_get_type(sys->resource);
break;
default:
......@@ -177,7 +174,7 @@ static int Open(vlc_object_t *obj)
return VLC_ENOMEM;
sys->manager = NULL;
sys->file = NULL;
sys->resource = NULL;
void *jar = NULL;
if (var_InheritBool(obj, "http-forward-cookies"))
......@@ -194,31 +191,18 @@ static int Open(vlc_object_t *obj)
bool live = var_InheritBool(obj, "http-continuous");
if (live)
sys->live = vlc_http_live_create(sys->manager, access->psz_url, ua,
referer);
sys->resource = vlc_http_live_create(sys->manager, access->psz_url, ua,
referer);
else
sys->file = vlc_http_file_create(sys->manager, access->psz_url, ua,
referer);
sys->resource = vlc_http_file_create(sys->manager, access->psz_url, ua,
referer);
free(referer);
free(ua);
char *redir;
if (live)
{
if (sys->live == NULL)
goto error;
redir = vlc_http_live_get_redirect(sys->live);
}
else
{
if (sys->file == NULL)
goto error;
redir = vlc_http_file_get_redirect(sys->file);
}
if (sys->resource == NULL)
goto error;
char *redir = vlc_http_res_get_redirect(sys->resource);
if (redir != NULL)
{
access->psz_url = redir;
......@@ -228,8 +212,7 @@ static int Open(vlc_object_t *obj)
ret = VLC_EGENERIC;
int status = live ? vlc_http_live_get_status(sys->live)
: vlc_http_file_get_status(sys->file);
int status = vlc_http_res_get_status(sys->resource);
if (status < 0)
{
msg_Err(access, "HTTP connection failure");
......@@ -261,8 +244,8 @@ static int Open(vlc_object_t *obj)
return VLC_SUCCESS;
error:
if (sys->file != NULL)
vlc_http_file_destroy(sys->file);
if (sys->resource != NULL)
vlc_http_res_destroy(sys->resource);
if (sys->manager != NULL)
vlc_http_mgr_destroy(sys->manager);
free(sys);
......@@ -274,10 +257,7 @@ static void Close(vlc_object_t *obj)
access_t *access = (access_t *)obj;
access_sys_t *sys = access->p_sys;
if (access->pf_block == LiveRead)
vlc_http_live_destroy(sys->live);
else
vlc_http_file_destroy(sys->file);
vlc_http_res_destroy(sys->resource);
vlc_http_mgr_destroy(sys->manager);
free(sys);
}
......
......@@ -42,20 +42,19 @@
struct vlc_http_file
{
struct vlc_http_resource resource;
struct vlc_http_msg *resp;
uintmax_t offset;
bool failed;
};
static int vlc_http_file_req(struct vlc_http_msg *req,
const struct vlc_http_resource *res, void *opaque)
static int vlc_http_file_req(const struct vlc_http_resource *res,
struct vlc_http_msg *req, void *opaque)
{
struct vlc_http_file *file = (struct vlc_http_file *)res;
const uintmax_t *offset = opaque;
if (file->resp != NULL)
if (file->resource.response != NULL)
{
const char *str = vlc_http_msg_get_header(file->resp, "ETag");
const char *str = vlc_http_msg_get_header(file->resource.response,
"ETag");
if (str != NULL)
{
if (!memcmp(str, "W/", 2))
......@@ -64,7 +63,7 @@ static int vlc_http_file_req(struct vlc_http_msg *req,
}
else
{
time_t mtime = vlc_http_msg_get_mtime(file->resp);
time_t mtime = vlc_http_msg_get_mtime(file->resource.response);
if (mtime != -1)
vlc_http_msg_add_time(req, "If-Unmodified-Since", &mtime);
}
......@@ -76,17 +75,12 @@ static int vlc_http_file_req(struct vlc_http_msg *req,
return 0;
}
static struct vlc_http_msg *vlc_http_file_open(struct vlc_http_file *file,
uintmax_t offset)
static int vlc_http_file_resp(const struct vlc_http_resource *res,
const struct vlc_http_msg *resp, void *opaque)
{
struct vlc_http_msg *resp;
resp = vlc_http_res_open(&file->resource, vlc_http_file_req, &offset);
if (resp == NULL)
return NULL;
const uintmax_t *offset = opaque;
int status = vlc_http_msg_get_status(resp);
if (status == 206)
if (vlc_http_msg_get_status(resp) == 206)
{
const char *str = vlc_http_msg_get_header(resp, "Content-Range");
if (str == NULL)
......@@ -96,81 +90,57 @@ static struct vlc_http_msg *vlc_http_file_open(struct vlc_http_file *file,
uintmax_t start, end;
if (sscanf(str, "bytes %ju-%ju", &start, &end) != 2
|| start != offset || start > end)
|| start != *offset || start > end)
/* A single range response is what we asked for, but not at that
* start offset. */
goto fail;
}
return resp;
(void) res;
return 0;
fail:
vlc_http_msg_destroy(resp);
errno = EIO;
return NULL;
return -1;
}
void vlc_http_file_destroy(struct vlc_http_file *file)
static const struct vlc_http_resource_cbs vlc_http_file_callbacks =
{
if (file->resp != NULL)
vlc_http_msg_destroy(file->resp);
vlc_http_res_deinit(&file->resource);
free(file);
}
vlc_http_file_req,
vlc_http_file_resp,
};
struct vlc_http_file *vlc_http_file_create(struct vlc_http_mgr *mgr,
const char *uri, const char *ua,
const char *ref)
struct vlc_http_resource *vlc_http_file_create(struct vlc_http_mgr *mgr,
const char *uri, const char *ua,
const char *ref)
{
struct vlc_http_file *file = malloc(sizeof (*file));
if (unlikely(file == NULL))
return NULL;
if (vlc_http_res_init(&file->resource, mgr, uri, ua, ref))
if (vlc_http_res_init(&file->resource, &vlc_http_file_callbacks, mgr,
uri, ua, ref))
{
free(file);
return NULL;
}
file->resp = NULL;
file->offset = 0;
file->failed = false;
return file;
return &file->resource;
}
int vlc_http_file_get_status(struct vlc_http_file *file)
uintmax_t vlc_http_file_get_size(struct vlc_http_resource *res)
{
if (file->resp == NULL)
{
if (file->failed)
return -1;
file->resp = vlc_http_file_open(file, file->offset);
if (file->resp == NULL)
{
file->failed = true;
return -1;
}
}
return vlc_http_msg_get_status(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);
}
uintmax_t vlc_http_file_get_size(struct vlc_http_file *file)
{
int status = vlc_http_file_get_status(file);
int status = vlc_http_res_get_status(res);
if (status < 0)
return -1;
const char *range = vlc_http_msg_get_header(file->resp, "Content-Range");
const char *range = vlc_http_msg_get_header(res->response,
"Content-Range");
if (status == 206 /* Partial Content */)
{ /* IETF RFC7233 §4.1 */
assert(range != NULL); /* checked by vlc_http_file_open() */
assert(range != NULL); /* checked by vlc_http_file_resp() */
uintmax_t end, total;
......@@ -183,7 +153,7 @@ uintmax_t vlc_http_file_get_size(struct vlc_http_file *file)
case 2:
return total;
}
vlc_assert_unreachable(); /* checked by vlc_http_file_open() */
vlc_assert_unreachable(); /* checked by vlc_http_file_resp() */
}
if (status == 416 /* Range Not Satisfiable */)
......@@ -202,36 +172,31 @@ uintmax_t vlc_http_file_get_size(struct vlc_http_file *file)
/* 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);
return vlc_http_msg_get_size(res->response);
}
bool vlc_http_file_can_seek(struct vlc_http_file *file)
bool vlc_http_file_can_seek(struct vlc_http_resource *res)
{ /* See IETF RFC7233 */
int status = vlc_http_file_get_status(file);
int status = vlc_http_res_get_status(res);
if (status < 0)
return false;
if (status == 206 || status == 416)
return true; /* Partial Content */
return vlc_http_msg_get_token(file->resp, "Accept-Ranges",
return vlc_http_msg_get_token(res->response, "Accept-Ranges",
"bytes") != NULL;
}
char *vlc_http_file_get_type(struct vlc_http_file *file)
int vlc_http_file_seek(struct vlc_http_resource *res, uintmax_t offset)
{
if (vlc_http_file_get_status(file) < 0)
return NULL;
return vlc_http_res_get_type(file->resp);
}
int vlc_http_file_seek(struct vlc_http_file *file, uintmax_t offset)
{
struct vlc_http_msg *resp = vlc_http_file_open(file, offset);
struct vlc_http_msg *resp = vlc_http_res_open(res, &offset);
if (resp == NULL)
return -1;
struct vlc_http_file *file = (struct vlc_http_file *)res;
int status = vlc_http_msg_get_status(resp);
if (file->resp != NULL)
if (res->response != NULL)
{ /* Accept the new and ditch the old one if:
* - requested succeeded and range was accepted (206),
* - requested failed due to out-of-range (416),
......@@ -242,26 +207,24 @@ int vlc_http_file_seek(struct vlc_http_file *file, uintmax_t offset)
vlc_http_msg_destroy(resp);
return -1;
}
vlc_http_msg_destroy(file->resp);
vlc_http_msg_destroy(res->response);
}
file->resp = resp;
res->response = resp;
file->offset = offset;
return 0;
}
block_t *vlc_http_file_read(struct vlc_http_file *file)
block_t *vlc_http_file_read(struct vlc_http_resource *res)
{
if (vlc_http_file_get_status(file) < 0)
return NULL;
block_t *block = vlc_http_res_read(file->resp);
struct vlc_http_file *file = (struct vlc_http_file *)res;
block_t *block = vlc_http_res_read(res);
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 (vlc_http_file_can_seek(res)
&& vlc_http_file_seek(res, file->offset) == 0)
block = vlc_http_res_read(res);
if (block == NULL)
return NULL;
......
......@@ -27,8 +27,8 @@
* @{
*/
struct vlc_http_file;
struct vlc_http_mgr;
struct vlc_http_resource;
struct block_t;
/**
......@@ -39,29 +39,12 @@ struct block_t;
* @param url URL of the file to read
* @param ua user agent string (or NULL to ignore)
* @param ref referral URL (or NULL to ignore)
*/
struct vlc_http_file *vlc_http_file_create(struct vlc_http_mgr *mgr,
const char *url, const char *ua,
const char *ref);
/**
* Destroys an HTTP file.
*
* Releases all resources allocated or held by the HTTP file object.
* @return an HTTP resource object pointer, or NULL on error
*/
void vlc_http_file_destroy(struct vlc_http_file *);
int vlc_http_file_get_status(struct vlc_http_file *);
/**
* Gets redirection URL.
*
* Checks if the file URL lead to a redirection. If so, return the redirect
* location.
*
* @return Heap-allocated URL or NULL if no redirection.
*/
char *vlc_http_file_get_redirect(struct vlc_http_file *);
struct vlc_http_resource *vlc_http_file_create(struct vlc_http_mgr *mgr,
const char *url, const char *ua,
const char *ref);
/**
* Gets file size.
......@@ -70,7 +53,7 @@ char *vlc_http_file_get_redirect(struct vlc_http_file *);
*
* @return Bytes count or (uintmax_t)-1 if unknown.
*/
uintmax_t vlc_http_file_get_size(struct vlc_http_file *);
uintmax_t vlc_http_file_get_size(struct vlc_http_resource *);
/**
* Checks seeking support.
......@@ -78,14 +61,7 @@ uintmax_t vlc_http_file_get_size(struct vlc_http_file *);
* @retval true if file supports seeking
* @retval false if file does not support seeking
*/
bool vlc_http_file_can_seek(struct vlc_http_file *);
/**
* Gets MIME type.
*
* @return Heap-allocated MIME type string, or NULL if unknown.
*/
char *vlc_http_file_get_type(struct vlc_http_file *);
bool vlc_http_file_can_seek(struct vlc_http_resource *);
/**
* Sets the read offset.
......@@ -94,11 +70,18 @@ char *vlc_http_file_get_type(struct vlc_http_file *);
* @retval 0 if seek succeeded
* @retval -1 if seek failed
*/
int vlc_http_file_seek(struct vlc_http_file *, uintmax_t offset);
int vlc_http_file_seek(struct vlc_http_resource *, uintmax_t offset);
/**
* Reads data.
*
* Reads data from a file and update the file offset.
*/
struct block_t *vlc_http_file_read(struct vlc_http_file *);
struct block_t *vlc_http_file_read(struct vlc_http_resource *);
#define vlc_http_file_get_status vlc_http_res_get_status
#define vlc_http_file_get_redirect vlc_http_res_get_redirect
#define vlc_http_file_get_type vlc_http_res_get_type
#define vlc_http_file_destroy vlc_http_res_destroy
/** @} */
......@@ -31,6 +31,7 @@
#include <vlc_common.h>
#include <vlc_http.h>
#include "resource.h"
#include "file.h"
#include "message.h"
......@@ -50,7 +51,7 @@ static vlc_http_cookie_jar_t *jar;
int main(void)
{
struct vlc_http_file *f;
struct vlc_http_resource *f;
char *str;
jar = vlc_http_cookies_new();
......@@ -65,7 +66,7 @@ int main(void)
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);
vlc_http_res_destroy(f);
/* Non-seekable stream test */
replies[0] = "HTTP/1.1 200 OK\r\n"
......
......@@ -31,15 +31,8 @@
#pragma GCC visibility push(default)
struct vlc_http_live
{
struct vlc_http_resource resource;
struct vlc_http_msg *resp;
bool failed;
};
static int vlc_http_live_req(struct vlc_http_msg *req,
const struct vlc_http_resource *res, void *opaque)
static int vlc_http_live_req(const struct vlc_http_resource *res,
struct vlc_http_msg *req, void *opaque)
{
vlc_http_msg_add_header(req, "Accept-Encoding", "gzip, deflate");
(void) res;
......@@ -47,82 +40,47 @@ static int vlc_http_live_req(struct vlc_http_msg *req,
return 0;
}
static struct vlc_http_msg *vlc_http_live_open(struct vlc_http_live *live)
static int vlc_http_live_resp(const struct vlc_http_resource *res,
const struct vlc_http_msg *resp, void *opaque)
{
return vlc_http_res_open(&live->resource, vlc_http_live_req, NULL);
(void) res;
(void) resp;
(void) opaque;
return 0;
}
void vlc_http_live_destroy(struct vlc_http_live *live)
static const struct vlc_http_resource_cbs vlc_http_live_callbacks =
{
if (live->resp != NULL)
vlc_http_msg_destroy(live->resp);
vlc_http_res_deinit(&live->resource);
free(live);
}
vlc_http_live_req,
vlc_http_live_resp,
};
struct vlc_http_live *vlc_http_live_create(struct vlc_http_mgr *mgr,
const char *uri, const char *ua,
const char *ref)
struct vlc_http_resource *vlc_http_live_create(struct vlc_http_mgr *mgr,
const char *uri, const char *ua,
const char *ref)
{
struct vlc_http_live *live = malloc(sizeof (*live));
if (unlikely(live == NULL))
struct vlc_http_resource *res = malloc(sizeof (*res));
if (unlikely(res == NULL))
return NULL;
if (vlc_http_res_init(&live->resource, mgr, uri, ua, ref))
if (vlc_http_res_init(res, &vlc_http_live_callbacks, mgr, uri, ua, ref))
{
free(live);
return NULL;
free(res);
res = NULL;
}
live->resp = NULL;
live->failed = false;
return live;
return res;
}
int vlc_http_live_get_status(struct vlc_http_live *live)
block_t *vlc_http_live_read(struct vlc_http_resource *res)
{
if (live->resp == NULL)
{
if (live->failed)
return -1;
live->resp = vlc_http_live_open(live);
if (live->resp == NULL)
{
live->failed = true;
return -1;
}
}
return vlc_http_msg_get_status(live->resp);
}
char *vlc_http_live_get_redirect(struct vlc_http_live *live)
{
if (vlc_http_live_get_status(live) < 0)
return NULL;
return vlc_http_res_get_redirect(&live->resource, live->resp);
}
char *vlc_http_live_get_type(struct vlc_http_live *live)
{
if (vlc_http_live_get_status(live) < 0)
return NULL;
return vlc_http_res_get_type(live->resp);
}
block_t *vlc_http_live_read(struct vlc_http_live *live)
{
if (vlc_http_live_get_status(live) < 0)
return NULL;
struct block_t *block = vlc_http_res_read(live->resp);
struct block_t *block = vlc_http_res_read(res);
if (block != NULL)
return block;
/* Automatically try to reconnect */
/* TODO: Retry-After parsing, loop and pacing timer */
vlc_http_msg_destroy(live->resp);
live->resp = NULL;
if (vlc_http_live_get_status(live) < 0)
return NULL;
return vlc_http_res_read(live->resp);
vlc_http_msg_destroy(res->response);
res->response = NULL;
return vlc_http_res_read(res);
}
......@@ -25,17 +25,17 @@
* @{
*/
struct vlc_http_live;
struct vlc_http_resource;
struct block_t;
struct vlc_http_live *vlc_http_live_create(struct vlc_http_mgr *mgr,
const char *uri, const char *ua,
const char *ref);
void vlc_http_live_destroy(struct vlc_http_live *live);
struct vlc_http_resource *vlc_http_live_create(struct vlc_http_mgr *mgr,
const char *uri, const char *ua,
const char *ref);
struct block_t *vlc_http_live_read(struct vlc_http_resource *);
int vlc_http_live_get_status(struct vlc_http_live *live);
char *vlc_http_live_get_redirect(struct vlc_http_live *live);
char *vlc_http_live_get_type(struct vlc_http_live *live);
block_t *vlc_http_live_read(struct vlc_http_live *live);
#define vlc_http_live_get_status vlc_http_res_get_status
#define vlc_http_live_get_redirect vlc_http_res_get_redirect
#define vlc_http_live_get_type vlc_http_res_get_type
#define vlc_http_live_destroye vlc_http_res_destroy
/** @} */
......@@ -37,7 +37,7 @@
#include "resource.h"
static struct vlc_http_msg *
vlc_http_res_req(const struct vlc_http_resource *res)
vlc_http_res_req(const struct vlc_http_resource *res, void *opaque)
{
struct vlc_http_msg *req;
......@@ -71,25 +71,24 @@ vlc_http_res_req(const struct vlc_http_resource *res)
/* TODO: vlc_http_msg_add_header(req, "TE", "gzip, deflate"); */
if (res->cbs->request_format(res, req, opaque))
{
vlc_http_msg_destroy(req);
return NULL;
}
return req;
}
struct vlc_http_msg *vlc_http_res_open(struct vlc_http_resource *res,
int (*request_cb)(struct vlc_http_msg *, const struct vlc_http_resource *,
void *), void *opaque)
void *opaque)
{
struct vlc_http_msg *req;
retry:
req = vlc_http_res_req(res);
req = vlc_http_res_req(res, opaque);
if (unlikely(req == NULL))
return NULL;
if (request_cb(req, res, opaque))
{
vlc_http_msg_destroy(req);
return NULL;
}
struct vlc_http_msg *resp = vlc_http_mgr_request(res->manager, res->secure,
res->host, res->port, req);
vlc_http_msg_destroy(req);
......@@ -119,19 +118,48 @@ retry:
goto retry;
}
if (res->cbs->response_validate(res, resp, opaque))
goto fail;
return resp;
fail:
vlc_http_msg_destroy(resp);
return NULL;
}
void vlc_http_res_deinit(struct vlc_http_resource *res)
int vlc_http_res_get_status(struct vlc_http_resource *res)
{
if (res->response == NULL)
{
if (res->failure)
return -1;
res->response = vlc_http_res_open(res, res + 1);
if (res->response == NULL)