libflute
Classes | Public Types | Public Member Functions | List of all members
LibFlute::FileDeliveryTable Class Reference

A class for parsing and creating FLUTE FDTs (File Delivery Tables). More...

#include <FileDeliveryTable.h>

Classes

struct  FileEntry
 An entry for a file in the FDT. More...
 

Public Types

enum  FdtNamespace { FDT_NS_NONE = 0 , FDT_NS_RFC3926 , FDT_NS_DRAFT_2005 , FDT_NS_3GPP_CONSOLIDATED_V2 }
 FDT namespace enumeration. More...
 

Public Member Functions

 FileDeliveryTable (uint32_t instance_id, FecOti fec_oti, FdtNamespace fdt_namespace=FDT_NS_NONE)
 Create an empty FDT. More...
 
 FileDeliveryTable (uint32_t instance_id, char *buffer, size_t len)
 Parse an XML string and create a FDT class from it. More...
 
virtual ~FileDeliveryTable ()
 Default destructor. More...
 
uint32_t instance_id ()
 Get the FDT instance ID. More...
 
void set_expires (uint64_t exp)
 Set the expiry value. More...
 
void add (const FileEntry &entry)
 Add a file entry. More...
 
void remove (uint32_t toi)
 Remove a file entry. More...
 
std::string to_string () const
 Serialize the FDT to an XML string. More...
 
const std::vector< FileEntry > & file_entries () const
 Get all current file entries. More...
 
void sent ()
 Mark instance sent. More...
 

Detailed Description

A class for parsing and creating FLUTE FDTs (File Delivery Tables).

Definition at line 29 of file FileDeliveryTable.h.

Member Enumeration Documentation

◆ FdtNamespace

FDT namespace enumeration.

Enumerator
FDT_NS_NONE 
FDT_NS_RFC3926 
FDT_NS_DRAFT_2005 
FDT_NS_3GPP_CONSOLIDATED_V2 

Definition at line 34 of file FileDeliveryTable.h.

34  {
35  FDT_NS_NONE = 0,
38 // FDT_NS_RFC6726, // FLUTE v2 - will need other things implementing to use this correctly
40  };

Constructor & Destructor Documentation

◆ FileDeliveryTable() [1/2]

LibFlute::FileDeliveryTable::FileDeliveryTable ( uint32_t  instance_id,
FecOti  fec_oti,
FdtNamespace  fdt_namespace = FDT_NS_NONE 
)

Create an empty FDT.

Parameters
instance_idFDT instance ID to set
fec_otiGlobal FEC OTI parameters
fdt_namespaceThe XML namespace to use for FDT

Definition at line 135 of file FileDeliveryTable.cpp.

136  : _instance_id( instance_id )
137  , _instance_id_sent( instance_id - 1 )
138  , _global_fec_oti( fec_oti )
139  , _fdt_namespace( fdt_namespace )
140 {
141 }
uint32_t instance_id()
Get the FDT instance ID.

◆ FileDeliveryTable() [2/2]

LibFlute::FileDeliveryTable::FileDeliveryTable ( uint32_t  instance_id,
char *  buffer,
size_t  len 
)

Parse an XML string and create a FDT class from it.

Parameters
instance_idFDT instance ID (from ALC headers)
bufferString containing the FDT XML
lenLength of the buffer

Definition at line 143 of file FileDeliveryTable.cpp.

144  : _instance_id( instance_id )
145  , _instance_id_sent( instance_id - 1 )
146  , _global_fec_oti()
147 {
148  static const std::string mbms2007_ns("urn:3GPP:metadata:2007:MBMS:FLUTE:FDT"); // 3GPP TS 26.346 Clause 7.2.10.2
149  static const std::string mbms2012_ns("urn:3GPP:metadata:2012:MBMS:FLUTE:FDT"); // 3GPP TS 26.346 Clause 7.2.10.2
150  tinyxml2::XMLDocument doc(true, tinyxml2::COLLAPSE_WHITESPACE);
151  doc.Parse(buffer, len);
152  auto fdt_instance = doc.RootElement();
153  // A malformed / truncated FDT buffer leaves no root element; every access
154  // below (elementNamespace, Name(), ...) would then dereference null and crash
155  // the receiver. Throw instead so Receiver::handle_receive_from's
156  // catch(std::exception&) drops the bad FDT and keeps the client alive.
157  if (doc.Error() || fdt_instance == nullptr) {
158  spdlog::warn("FDT parse failed ({}) at line {}, len={}",
159  doc.ErrorName() ? doc.ErrorName() : "?", doc.ErrorLineNum(), len);
160  throw std::runtime_error(std::string("FDT XML parse failed: ") +
161  (doc.ErrorName() ? doc.ErrorName() : "no root element"));
162  }
163  XMLNamespaces root_ns(fdt_instance);
164  auto fdt_ns = root_ns.elementNamespace(fdt_instance);
165  if (!root_ns.matches(fdt_instance->Name(), "FDT-Instance", fdt_ns)) {
166  throw std::runtime_error("Root element is not FDT-Instance");
167  }
168 
169  if (fdt_ns == "") {
170  _fdt_namespace = FDT_NS_NONE;
171  } else if (fdt_ns == "http://www.example.com/flute") { // RFC 3926 Section 3.4.2
172  _fdt_namespace = FDT_NS_RFC3926;
173  } else if (fdt_ns == "urn:IETF:metadata:2005:FLUTE:FDT") { // 3GPP TS 26.346 Clause 7.2.10.1
174  _fdt_namespace = FDT_NS_DRAFT_2005;
175 // } else if (fdt_ns == "urn:ietf:params:xml:ns:fdt") { // RFC 6726 - FLUTEv2 - needs more work
176 // _fdt_namespace = FDT_NS_RFC6726;
177  } else if (fdt_ns == "urn:3GPP:metadata:2022:FLUTE:FDT") { // 3GPP TS 26.346 Clause L.6.1
178  _fdt_namespace = FDT_NS_3GPP_CONSOLIDATED_V2;
179  } else {
180  throw std::runtime_error("FDT namespace not recognised");
181  }
182 
183  _expires = std::stoull(root_ns.findAttribute(fdt_instance, "Expires", fdt_ns)->Value());
184 
185  spdlog::debug("Received new FDT with instance ID {}: {}", instance_id, buffer);
186 
187  auto val = root_ns.findAttribute(fdt_instance, "FEC-OTI-FEC-Encoding-ID", fdt_ns);
188  if (val != nullptr) {
189  _global_fec_oti.encoding_id = static_cast<FecScheme>(strtoul(val->Value(), nullptr, 0));
190  }
191 
192  val = root_ns.findAttribute(fdt_instance, "FEC-OTI-FEC-Instance-ID", fdt_ns);
193  if (val != nullptr) {
194  _global_fec_oti.instance_id = strtoul(val->Value(), nullptr, 0);
195  }
196 
197  val = root_ns.findAttribute(fdt_instance, "FEC-OTI-Maximum-Source-Block-Length", fdt_ns);
198  if (val != nullptr) {
199  _global_fec_oti.max_source_block_length = strtoul(val->Value(), nullptr, 0);
200  }
201 
202  val = root_ns.findAttribute(fdt_instance, "FEC-OTI-Encoding-Symbol-Length", fdt_ns);
203  if (val != nullptr) {
204  _global_fec_oti.encoding_symbol_length = strtoul(val->Value(), nullptr, 0);
205  }
206 
207  val = root_ns.findAttribute(fdt_instance, "FEC-OTI-Max-Number-of-Encoding-Symbols", fdt_ns);
208  if (val != nullptr) {
209  _global_fec_oti.max_number_of_encoding_symbols = strtoul(val->Value(), nullptr, 0);
210  }
211 
212  for (auto file = root_ns.findChildElement(fdt_instance, "File", fdt_ns);
213  file != nullptr; file = root_ns.findSiblingElement(file, "File", fdt_ns)) {
214 
215  XMLNamespaces file_ns(file, root_ns);
216 
217  // File required attributes
218  auto toi_str = file_ns.findAttribute(file, "TOI", fdt_ns);
219  if (toi_str == nullptr) {
220  throw std::runtime_error("Missing TOI attribute on File element");
221  }
222  uint32_t toi = strtoull(toi_str->Value(), nullptr, 0);
223 
224  auto content_location = file_ns.findAttribute(file, "Content-Location", fdt_ns);
225  if (content_location == nullptr) {
226  throw std::runtime_error("Missing Content-Location attribute on File element");
227  }
228 
229  // File optional attributes
230  uint32_t content_length = 0;
231  val = file_ns.findAttribute(file, "Content-Length", fdt_ns);
232  if (val != nullptr) {
233  content_length = strtoull(val->Value(), nullptr, 0);
234  }
235 
236  uint32_t transfer_length = 0;
237  val = file_ns.findAttribute(file, "Transfer-Length", fdt_ns);
238  if (val != nullptr) {
239  transfer_length = strtoull(val->Value(), nullptr, 0);
240  } else {
241  transfer_length = content_length;
242  }
243 
244  auto content_md5 = std::string();
245  val = file_ns.findAttribute(file, "Content-MD5", fdt_ns);
246  if (val != nullptr) {
247  content_md5 = val->Value();
248  }
249 
250  auto content_encoding = std::string();
251  val = file_ns.findAttribute(file, "Content-Encoding", fdt_ns);
252  if (val != nullptr) {
253  content_encoding = val->Value();
254  }
255 
256  auto content_type = std::string();
257  val = file_ns.findAttribute(file, "Content-Type", fdt_ns);
258  if (val != nullptr) {
259  content_type = val->Value();
260  }
261 
262  auto encoding_id = _global_fec_oti.encoding_id;
263  val = file_ns.findAttribute(file, "FEC-OTI-FEC-Encoding-ID", fdt_ns);
264  if (val != nullptr) {
265  encoding_id = static_cast<FecScheme>(strtoul(val->Value(), nullptr, 0));
266  }
267 
268  auto fec_instance_id = _global_fec_oti.instance_id;
269  val = file_ns.findAttribute(file, "FEC-OTI-FEC-Instance-ID", fdt_ns);
270  if (val != nullptr) {
271  fec_instance_id = strtoul(val->Value(), nullptr, 0);
272  }
273 
274  auto max_source_block_length = _global_fec_oti.max_source_block_length;
275  val = file_ns.findAttribute(file, "FEC-OTI-Maximum-Source-Block-Length", fdt_ns);
276  if (val != nullptr) {
277  max_source_block_length = strtoul(val->Value(), nullptr, 0);
278  }
279 
280  auto encoding_symbol_length = _global_fec_oti.encoding_symbol_length;
281  val = file_ns.findAttribute(file, "FEC-OTI-Encoding-Symbol-Length", fdt_ns);
282  if (val != nullptr) {
283  encoding_symbol_length = strtoul(val->Value(), nullptr, 0);
284  }
285 
286  auto max_number_of_encoding_symbols = _global_fec_oti.max_number_of_encoding_symbols;
287  val = file_ns.findAttribute(file, "FEC-OTI-Max-Number-of-Encoding-Symbols", fdt_ns);
288  if (val != nullptr) {
289  max_number_of_encoding_symbols = strtoul(val->Value(), nullptr, 0);
290  }
291 
292  auto mbms2012_file_etag = "";
293  val = file_ns.findAttribute(file, "File-ETag", mbms2012_ns);
294  if (val != nullptr) {
295  mbms2012_file_etag = val->Value();
296  }
297 
298  // File optional elements
299 
300  bool no_cache = false;
301  //bool max_stale = false;
302  std::optional<uint64_t> cache_expires = std::nullopt;
303  auto cc = file_ns.findChildElement(file, "Cache-Control", mbms2007_ns);
304  if (cc) {
305  XMLNamespaces cc_ns(cc, file_ns);
306 
307  // mbms2007:Cache-Control optional elements
308 
309  auto no_cache_elem = cc_ns.findChildElement(cc, "no-cache", mbms2007_ns);
310  if (no_cache_elem) {
311  no_cache = true;
312  }
313 
314  //auto max_stale_elem = cc_ns.findChildElement(cc, "max-stale", mbms2007_ns);
315  //if (max_stale_elem) {
316  // max_stale = true;
317  //}
318 
319  auto expires_elem = cc_ns.findChildElement(cc, "Expires", mbms2007_ns);
320  if (expires_elem) {
321  cache_expires = strtoul(expires_elem->GetText(), nullptr, 0);
322  }
323  }
324 
325  FecOti fec_oti{
326  .encoding_id = (FecScheme)encoding_id,
327  .instance_id = fec_instance_id,
328  .transfer_length = transfer_length,
329  .encoding_symbol_length = encoding_symbol_length,
330  .max_source_block_length = max_source_block_length,
331  .max_number_of_encoding_symbols = max_number_of_encoding_symbols
332  };
333 
334  FileEntry fe{
335  toi,
336  std::string(content_location->Value()),
337  content_length,
338  content_md5,
339  content_type,
340  (cache_expires)?(cache_expires.value()):0,
341  fec_oti,
342  {
343  no_cache,
344  cache_expires
345  },
346  content_encoding,
347  mbms2012_file_etag
348  };
349  _file_entries.push_back(fe);
350  }
351 }
static char doc[]
FecScheme
Error correction schemes.
Definition: flute_types.h:44
uint32_t instance_id
Definition: flute_types.h:53
FecScheme encoding_id
Definition: flute_types.h:52
uint32_t max_source_block_length
Definition: flute_types.h:56
uint32_t max_number_of_encoding_symbols
Definition: flute_types.h:57
uint32_t encoding_symbol_length
Definition: flute_types.h:55

◆ ~FileDeliveryTable()

virtual LibFlute::FileDeliveryTable::~FileDeliveryTable ( )
inlinevirtual

Default destructor.

Definition at line 63 of file FileDeliveryTable.h.

63 {};

Member Function Documentation

◆ add()

auto LibFlute::FileDeliveryTable::add ( const FileEntry entry)

Add a file entry.

Definition at line 353 of file FileDeliveryTable.cpp.

354 {
355  if (_instance_id == _instance_id_sent) _instance_id++;
356  _file_entries.push_back(fe);
357 }

◆ file_entries()

const std::vector<FileEntry>& LibFlute::FileDeliveryTable::file_entries ( ) const
inline

Get all current file entries.

Definition at line 115 of file FileDeliveryTable.h.

115  {
116  return _file_entries;
117  };

◆ instance_id()

uint32_t LibFlute::FileDeliveryTable::instance_id ( )
inline

Get the FDT instance ID.

Definition at line 68 of file FileDeliveryTable.h.

68 { return _instance_id; };

◆ remove()

auto LibFlute::FileDeliveryTable::remove ( uint32_t  toi)

Remove a file entry.

Definition at line 359 of file FileDeliveryTable.cpp.

360 {
361  for (auto it = _file_entries.cbegin(); it != _file_entries.cend();) {
362  if (it->toi == toi) {
363  it = _file_entries.erase(it);
364  } else {
365  ++it;
366  }
367  }
368  if (_instance_id == _instance_id_sent) _instance_id++;
369 }

◆ sent()

void LibFlute::FileDeliveryTable::sent ( )
inline

Mark instance sent.

Definition at line 122 of file FileDeliveryTable.h.

122 { _instance_id_sent = _instance_id; };

◆ set_expires()

void LibFlute::FileDeliveryTable::set_expires ( uint64_t  exp)
inline

Set the expiry value.

Definition at line 95 of file FileDeliveryTable.h.

95 { _expires = exp; };

◆ to_string()

auto LibFlute::FileDeliveryTable::to_string ( ) const

Serialize the FDT to an XML string.

Definition at line 371 of file FileDeliveryTable.cpp.

371  {
372  tinyxml2::XMLDocument doc;
373  doc.InsertFirstChild( doc.NewDeclaration() );
374  auto root = doc.NewElement("FDT-Instance");
375  switch (_fdt_namespace) {
376  case FDT_NS_RFC3926:
377  // RFC 3926 Section 3.4.2
378  root->SetAttribute("xmlns", "http://www.example.com/flute");
379  break;
380  case FDT_NS_DRAFT_2005:
381  // 3GPP TS 26.346 Clause 7.2.10.1
382  root->SetAttribute("xmlns", "urn:IETF:metadata:2005:FLUTE:FDT");
383  break;
384 // case FDT_NS_RFC6726: // FLUTE v2 - Will need other things implementing to use this
385 // // RFC 6726
386 // root->SetAttribute("xmlns", "urn:ietf:params:xml:ns:fdt");
387 // break;
389  // 3GPP TS 26.346 Clause L.6.1
390  root->SetAttribute("xmlns", "urn:3GPP:metadata:2022:FLUTE:FDT");
391  break;
392  default:
393  break;
394  }
395  root->SetAttribute("Expires", std::to_string(_expires).c_str());
396  root->SetAttribute("FEC-OTI-FEC-Encoding-ID", (unsigned)_global_fec_oti.encoding_id);
397  if (_global_fec_oti.instance_id) root->SetAttribute("FEC-OTI-FEC-Instance-ID", (unsigned)_global_fec_oti.instance_id);
398  root->SetAttribute("FEC-OTI-Maximum-Source-Block-Length", (unsigned)_global_fec_oti.max_source_block_length);
399  root->SetAttribute("FEC-OTI-Encoding-Symbol-Length", (unsigned)_global_fec_oti.encoding_symbol_length);
400  root->SetAttribute("xmlns:mbms2007", "urn:3GPP:metadata:2007:MBMS:FLUTE:FDT"); // 3GPP TS 26.346 Clause 7.2.10.2
401  root->SetAttribute("xmlns:mbms2012", "urn:3GPP:metadata:2012:MBMS:FLUTE:FDT"); // 3GPP TS 26.346 Clause 7.2.10.2
402  doc.InsertEndChild(root);
403 
404  for (const auto& file : _file_entries) {
405  auto f = doc.NewElement("File");
406  f->SetAttribute("TOI", file.toi);
407  f->SetAttribute("Content-Location", file.content_location.c_str());
408  f->SetAttribute("Content-Length", file.content_length);
409  if (file.fec_oti.transfer_length) f->SetAttribute("Transfer-Length", file.fec_oti.transfer_length);
410  if (!file.content_md5.empty()) f->SetAttribute("Content-MD5", file.content_md5.c_str());
411  if (!file.content_encoding.empty()) f->SetAttribute("Content-Encoding", file.content_encoding.c_str());
412  if (!file.content_type.empty()) f->SetAttribute("Content-Type", file.content_type.c_str());
413  if (file.fec_oti.encoding_id != _global_fec_oti.encoding_id)
414  f->SetAttribute("FEC-OTI-FEC-Encoding-ID", (unsigned)file.fec_oti.encoding_id);
415  if (file.fec_oti.instance_id != 0 && file.fec_oti.instance_id != _global_fec_oti.instance_id)
416  f->SetAttribute("FEC-OTI-FEC-Instance-ID", (unsigned)file.fec_oti.instance_id);
417  if (file.fec_oti.max_source_block_length != 0 &&
418  file.fec_oti.max_source_block_length != _global_fec_oti.max_source_block_length)
419  f->SetAttribute("FEC-OTI-Maximum-Source-Block-Length", (unsigned)file.fec_oti.max_source_block_length);
420  if (file.fec_oti.encoding_symbol_length != 0 &&
421  file.fec_oti.encoding_symbol_length != _global_fec_oti.encoding_symbol_length)
422  f->SetAttribute("FEC-OTI-Encoding-Symbol-Length", (unsigned)file.fec_oti.encoding_symbol_length);
423  if (!file.etag.empty()) f->SetAttribute("mbms2012:File-ETag", file.etag.c_str());
424  if (file.cache_control.no_cache || file.cache_control.cache_expires) {
425  auto cc = doc.NewElement("mbms2007:Cache-Control");
426  if (file.cache_control.no_cache) {
427  auto noc = doc.NewElement("mbms2007:no-cache");
428  noc->SetText("true");
429  cc->InsertEndChild(noc);
430  } else {
431  auto exp = doc.NewElement("mbms2007:Expires");
432  exp->SetText(std::to_string(file.expires).c_str());
433  cc->InsertEndChild(exp);
434  }
435  f->InsertEndChild(cc);
436  }
437  root->InsertEndChild(f);
438  }
439 
440 
441  tinyxml2::XMLPrinter printer;
442  doc.Print(&printer);
443  return std::string(printer.CStr());
444 }

The documentation for this class was generated from the following files: