Browse Source

demux: adaptive: add libvlchttp based connection

pull/128/head
Francois Cartegnie 5 years ago
committed by François Cartegnie
parent
commit
f5c3393339
  1. 4
      modules/demux/Makefile.am
  2. 5
      modules/demux/adaptive/http/AuthStorage.cpp
  3. 1
      modules/demux/adaptive/http/AuthStorage.hpp
  4. 363
      modules/demux/adaptive/http/HTTPConnection.cpp
  5. 34
      modules/demux/adaptive/http/HTTPConnection.hpp

4
modules/demux/Makefile.am

@ -491,6 +491,10 @@ libvlc_adaptive_la_SOURCES += \
libvlc_adaptive_la_CXXFLAGS = $(AM_CXXFLAGS) -I$(srcdir)/demux/adaptive
libvlc_adaptive_la_LIBADD = $(SOCKET_LIBS) $(LIBM)
libvlc_adaptive_la_LDFLAGS = -static
if !HAVE_WIN32
libvlc_adaptive_la_LDFLAGS += -lpthread
endif
libvlc_adaptive_la_LIBADD += libvlc_http.la
if HAVE_ZLIB
libvlc_adaptive_la_LIBADD += -lz
endif

5
modules/demux/adaptive/http/AuthStorage.cpp

@ -62,3 +62,8 @@ std::string AuthStorage::getCookie( const ConnectionParams &params, bool secure
}
return ret;
}
vlc_http_cookie_jar_t *AuthStorage::getJar() const
{
return p_cookies_jar;
}

1
modules/demux/adaptive/http/AuthStorage.hpp

@ -38,6 +38,7 @@ namespace adaptive
~AuthStorage();
void addCookie( const std::string &cookie, const ConnectionParams & );
std::string getCookie( const ConnectionParams &, bool secure );
vlc_http_cookie_jar_t *getJar() const;
private:
vlc_http_cookie_jar_t *p_cookies_jar;

363
modules/demux/adaptive/http/HTTPConnection.cpp

@ -26,11 +26,23 @@
#include "AuthStorage.hpp"
#include "Transport.hpp"
#include "../tools/Helper.h"
#include "../AbstractSource.hpp"
#include "../plumbing/SourceStream.hpp"
#include <cstdio>
#include <sstream>
#include <algorithm>
#include <vlc_stream.h>
#include <vlc_tls.h>
#include <vlc_keystore.h>
extern "C"
{
#include "../access/http/resource.h"
#include "../access/http/connmgr.h"
#include "../access/http/conn.h"
#include "../access/http/message.h"
}
using namespace adaptive::http;
@ -480,6 +492,342 @@ std::string HTTPConnection::extraRequestHeaders() const
return ss.str();
}
class adaptive::http::LibVLCHTTPSource : public adaptive::AbstractSource
{
friend class LibVLCHTTPConnection;
public:
LibVLCHTTPSource(vlc_object_t *p_object, struct vlc_http_cookie_jar_t *jar)
{
http_mgr = vlc_http_mgr_create(p_object, jar);
http_res = nullptr;
totalRead = 0;
}
virtual ~LibVLCHTTPSource()
{
if(http_mgr)
vlc_http_mgr_destroy(http_mgr);
}
virtual block_t *readNextBlock() override
{
if(http_res == nullptr)
return nullptr;
block_t *b = vlc_http_res_read(http_res);
if(b == vlc_http_error)
return nullptr;
if(b)
totalRead += b->i_buffer;
return b;
}
void reset()
{
if(http_res)
{
vlc_http_res_destroy(http_res);
http_res = nullptr;
totalRead = 0;
}
}
private:
struct restuple
{
struct vlc_http_resource resource;
LibVLCHTTPSource *source;
};
int formatRequest(const struct vlc_http_resource *,
struct vlc_http_msg *req)
{
vlc_http_msg_add_header(req, "Cache-Control", "no-cache");
if(range.isValid())
{
if(range.getEndByte() > 0)
{
if (vlc_http_msg_add_header(req, "Range", "bytes=%" PRIuMAX "-%" PRIuMAX,
range.getStartByte(), range.getEndByte()))
return -1;
}
else
{
if (vlc_http_msg_add_header(req, "Range", "bytes=%" PRIuMAX "-",
range.getStartByte()))
return -1;
}
}
return 0;
}
int validateResponse(const struct vlc_http_resource *, const struct vlc_http_msg *resp)
{
if (vlc_http_msg_get_status(resp) == 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. */
return -1;
uintmax_t start, end;
if (sscanf(str, "bytes %" SCNuMAX "-%" SCNuMAX, &start, &end) != 2
|| start != range.getStartByte() || start > end ||
(range.getEndByte() > range.getStartByte() && range.getEndByte() != end) )
/* A single range response is what we asked for, but not at that
* start offset. */
return -1;
}
return 0;
}
static int formatrequest_handler(const struct vlc_http_resource *res,
struct vlc_http_msg *req, void *opaque)
{
return (*static_cast<LibVLCHTTPSource **>(opaque))->formatRequest(res, req);
}
static int validateresponse_handler(const struct vlc_http_resource *res,
const struct vlc_http_msg *resp, void *opaque)
{
return (*static_cast<LibVLCHTTPSource **>(opaque))->validateResponse(res, resp);
}
static const struct vlc_http_resource_cbs callbacks;
size_t totalRead;
struct vlc_http_mgr *http_mgr;
BytesRange range;
public:
struct vlc_http_resource *http_res;
int create(const char *uri,const std::string &ua,
const std::string &ref, const BytesRange &range)
{
struct restuple *tpl = new struct restuple;
tpl->source = this;
this->range = range;
if (vlc_http_res_init(&tpl->resource, &this->callbacks, http_mgr, uri,
ua.empty() ? nullptr : ua.c_str(),
ref.empty() ? nullptr : ref.c_str()))
{
delete tpl;
return -1;
}
http_res = &tpl->resource;
return 0;
}
int abortandlogin(const char *user, const char *pass)
{
if(http_res == nullptr)
return -1;
free(http_res->username);
http_res->username = user ? strdup(user) : nullptr;
free(http_res->password);
http_res->password = pass ? strdup(pass) : nullptr;
struct vlc_http_msg *resp = vlc_http_res_open(http_res, &http_res[1]);
if (resp == nullptr)
return -1;
if (http_res->response != nullptr)
vlc_http_msg_destroy(http_res->response);
http_res->response = resp;
return 0;
}
};
const struct vlc_http_resource_cbs LibVLCHTTPSource::callbacks =
{
LibVLCHTTPSource::formatrequest_handler,
LibVLCHTTPSource::validateresponse_handler,
};
LibVLCHTTPConnection::LibVLCHTTPConnection(vlc_object_t *p_object_, AuthStorage *auth)
: AbstractConnection( p_object_ )
{
source = new adaptive::http::LibVLCHTTPSource(p_object_, auth->getJar());
sourceStream = new ChunksSourceStream(p_object, source);
stream = nullptr;
char *psz_useragent = var_InheritString(p_object_, "http-user-agent");
if(psz_useragent)
{
useragent = std::string(psz_useragent);
free(psz_useragent);
}
char *psz_referer = var_InheritString(p_object_, "http-referrer");
if(psz_referer)
{
referer = std::string(psz_referer);
free(psz_referer);
}
}
LibVLCHTTPConnection::~LibVLCHTTPConnection()
{
reset();
delete sourceStream;
delete source;
}
void LibVLCHTTPConnection::reset()
{
source->reset();
sourceStream->Reset();
if(stream)
{
vlc_stream_Delete(stream);
stream = nullptr;
}
bytesRange = BytesRange();
contentType = std::string();
bytesRead = 0;
contentLength = 0;
}
bool LibVLCHTTPConnection::canReuse(const ConnectionParams &params_) const
{
if(!available)
return false;
return (params.getHostname() == params_.getHostname() &&
params.getScheme() == params_.getScheme() &&
params.getPort() == params_.getPort());
}
RequestStatus LibVLCHTTPConnection::request(const std::string &path,
const BytesRange &range)
{
if(source->http_mgr == nullptr)
return RequestStatus::GenericError;
reset();
/* Set new path for this query */
params.setPath(path);
if(range.isValid())
msg_Dbg(p_object, "Retrieving %s @%zu-%zu", params.getUrl().c_str(),
range.getStartByte(), range.getEndByte());
else
msg_Dbg(p_object, "Retrieving %s", params.getUrl().c_str());
if(source->create(params.getUrl().c_str(), useragent,referer, range))
return RequestStatus::GenericError;
struct vlc_credential crd;
struct vlc_url_t crd_url;
vlc_UrlParse(&crd_url, params.getUrl().c_str());
vlc_credential_init(&crd, &crd_url);
if (vlc_credential_get(&crd, p_object, NULL, NULL, NULL, NULL))
{
vlc_http_res_set_login(source->http_res,
crd.psz_username, crd.psz_password);
}
int status = vlc_http_res_get_status(source->http_res);
if (status < 0)
{
vlc_credential_clean(&crd);
vlc_UrlClean(&crd_url);
return RequestStatus::GenericError;
}
char *psz_realm = nullptr;
if (status == 401) /* authentication */
{
psz_realm = vlc_http_res_get_basic_realm(source->http_res);
if (psz_realm)
{
vlc_credential_init(&crd, &crd_url);
crd.psz_authtype = "Basic";
crd.psz_realm = psz_realm;
if (vlc_credential_get(&crd, p_object, NULL, NULL,
_("HTTP authentication"),
_("Please enter a valid login name and a "
"password for realm %s."), psz_realm))
{
if(source->abortandlogin(crd.psz_username, crd.psz_password))
{
vlc_credential_clean(&crd);
vlc_UrlClean(&crd_url);
free(psz_realm);
return RequestStatus::Unauthorized;
}
status = vlc_http_res_get_status(source->http_res);
}
}
}
if(status > 0 && status < 400 && crd.psz_realm &&
crd.i_get_order > decltype(crd.i_get_order)::GET_FROM_MEMORY_KEYSTORE)
{
/* Force caching into memory keystore */
crd.b_from_keystore = false;
crd.b_store = false;
vlc_credential_store(&crd, p_object);
}
vlc_credential_clean(&crd);
vlc_UrlClean(&crd_url);
free(psz_realm);
if (status >= 400)
return RequestStatus::GenericError;
char *psz_redir = vlc_http_res_get_redirect(source->http_res);
if(psz_redir)
{
ConnectionParams loc = ConnectionParams(psz_redir);
free(psz_redir);
if(loc.getScheme().empty())
{
locationparams = params;
locationparams.setPath(loc.getPath());
}
else locationparams = loc;
return RequestStatus::Redirection;
}
sourceStream->Reset();
stream = sourceStream->makeStream();
if(stream == nullptr)
return RequestStatus::GenericError;
contentLength = vlc_http_msg_get_size(source->http_res->response);
const char *s = vlc_http_msg_get_header(source->http_res->response, "Content-Type");
if(s)
contentType = std::string(s);
s = vlc_http_msg_get_header(source->http_res->response, "Content-Encoding");
if(s && stream && (strstr(s, "deflate") || strstr(s, "gzip")))
{
stream_t *decomp = vlc_stream_FilterNew(stream, "inflate");
if(decomp)
{
stream = decomp;
contentLength = 0;
}
}
return RequestStatus::Success;
}
ssize_t LibVLCHTTPConnection::read(void *p_buffer, size_t len)
{
ssize_t read = vlc_stream_Read(stream, p_buffer, len);
bytesRead = source->totalRead;
return read;
}
void LibVLCHTTPConnection::setUsed( bool b )
{
available = !b;
if(available)
reset();
}
StreamUrlConnection::StreamUrlConnection(vlc_object_t *p_object)
: AbstractConnection(p_object)
{
@ -640,6 +988,21 @@ AbstractConnection * NativeConnectionFactory::createConnection(vlc_object_t *p_o
return conn;
}
LibVLCHTTPConnectionFactory::LibVLCHTTPConnectionFactory( AuthStorage *auth )
: AbstractConnectionFactory()
{
authStorage = auth;
}
AbstractConnection * LibVLCHTTPConnectionFactory::createConnection(vlc_object_t *p_object,
const ConnectionParams &params)
{
if((params.getScheme() != "http" && params.getScheme() != "https") ||
params.getHostname().empty())
return nullptr;
return new LibVLCHTTPConnection(p_object, authStorage);
}
StreamUrlConnectionFactory::StreamUrlConnectionFactory()
: AbstractConnectionFactory()
{

34
modules/demux/adaptive/http/HTTPConnection.hpp

@ -32,6 +32,8 @@
namespace adaptive
{
class ChunksSourceStream;
namespace http
{
class Transport;
@ -114,6 +116,28 @@ namespace adaptive
Transport *transport;
};
class LibVLCHTTPSource;
class LibVLCHTTPConnection : public AbstractConnection
{
public:
LibVLCHTTPConnection(vlc_object_t *, AuthStorage *);
virtual ~LibVLCHTTPConnection();
virtual bool canReuse (const ConnectionParams &) const override;
virtual RequestStatus request(const std::string& path,
const BytesRange & = BytesRange()) override;
virtual ssize_t read (void *p_buffer, size_t len) override;
virtual void setUsed ( bool ) override;
private:
void reset();
std::string useragent;
std::string referer;
LibVLCHTTPSource *source;
ChunksSourceStream *sourceStream;
stream_t *stream;
};
class StreamUrlConnection : public AbstractConnection
{
public:
@ -151,6 +175,16 @@ namespace adaptive
AuthStorage *authStorage;
};
class LibVLCHTTPConnectionFactory : public AbstractConnectionFactory
{
public:
LibVLCHTTPConnectionFactory( AuthStorage * );
virtual ~LibVLCHTTPConnectionFactory() = default;
virtual AbstractConnection * createConnection(vlc_object_t *, const ConnectionParams &) override;
private:
AuthStorage *authStorage;
};
class StreamUrlConnectionFactory : public AbstractConnectionFactory
{
public:

Loading…
Cancel
Save