8#define LOC QString("HTTPRange: ")
10auto sumrange = [](uint64_t Cum,
HTTPRange Range) {
return ((Range.second + 1) - Range.first) + Cum; };
14 if (!Response ||
Request.isEmpty())
18 auto * data = std::get_if<HTTPData>(&(Response->
m_response));
22 size = (*data)->size();
24 size = (*file)->size();
29 HTTPRanges& ranges = data ? (*data)->m_ranges : (*file)->m_ranges;
30 int64_t& partialsize = data ? (*data)->m_partialSize : (*file)->m_partialSize;
41 auto * data = std::get_if<HTTPData>(&(Response->
m_response));
45 size = (*data)->size();
47 size = (*file)->size();
51 HTTPRanges& ranges = data ? (*data)->m_ranges : (*file)->m_ranges;
52 if (ranges.size() < 2)
55 auto & mime = data ? (*data)->m_mimeType : (*file)->m_mimeType;
57 for (
auto & range : ranges)
59 auto header = QString(
"\r\n--%1\r\nContent-Type: %2\r\nContent-Range: %3\r\n\r\n")
68 int64_t headersize = 0;
70 headersize += header->size();
73 (*data)->m_multipartHeaders =
headers;
74 (*data)->m_multipartHeaderSize = headersize;
78 (*file)->m_multipartHeaders =
headers;
79 (*file)->m_multipartHeaderSize = headersize;
85 return QString(
"bytes %1-%2/%3").arg(Range.first).arg(Range.second).arg(Size);
92 if (Ranges.size() == 1)
100 auto * data = std::get_if<HTTPData>(&Data);
101 auto *
file = std::get_if<HTTPFile>(&Data);
105 int64_t partialsize = data ? (*data)->m_partialSize : (*file)->m_partialSize;
106 auto written =
static_cast<uint64_t
>(data ? (*data)->m_written : (*file)->m_written);
107 HTTPRanges& ranges = data ? (*data)->m_ranges : (*file)->m_ranges;
111 for (
const auto& range : ranges)
113 uint64_t newsum =
sumrange(oldsum, range);
114 if (oldsum <= written && written < newsum)
117 if ((oldsum == written) && !
headers.empty())
124 ToWrite = std::min(Available,
static_cast<int64_t
>(newsum - written));
127 Offset =
static_cast<int64_t
>(range.first - oldsum);
133 if (((
static_cast<int64_t
>(written) + ToWrite) >= partialsize) && !
headers.empty())
135 result.second =
headers.back();
167 LOG(VB_HTTP, LOG_DEBUG,
LOC + QString(
"Parsing: '%1'").arg(
Request));
170 QStringList initial =
Request.toLower().split(
"=");
171 if (initial.size() != 2)
173 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Failed to parse ranges: '%1'").arg(
Request));
178 if (!initial.at(0).contains(
"bytes"))
180 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Unkown range units: '%1'").arg(initial.at(0)));
185 QStringList rangelist = initial.at(1).split(
",", Qt::SkipEmptyParts);
188 if (rangelist.isEmpty())
190 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Failed to find ranges: '%1'").arg(initial.at(1)));
196 for (
auto & range : rangelist)
198 QStringList parts = range.split(
"-");
199 if (parts.size() != 2)
201 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Failed to parse range: '%1'").arg(range));
205 bool validrange =
false;
206 bool startvalid =
false;
207 bool endvalid =
false;
208 bool startstr = !parts.at(0).isEmpty();
209 bool endstr = !parts.at(1).isEmpty();
210 uint64_t start = parts.at(0).toULongLong(&startvalid);
211 uint64_t end = parts.at(1).toULongLong(&endvalid);
214 if (startstr && endstr && startvalid && endvalid)
216 validrange = ((end < static_cast<uint64_t>(TotalSize)) && (start <= end));
219 else if (startstr && startvalid)
221 end =
static_cast<uint64_t
>(TotalSize - 1);
222 validrange = start <= end;
225 else if (endstr && endvalid)
228 end =
static_cast<uint64_t
>(TotalSize) - 1;
229 start =
static_cast<uint64_t
>(TotalSize) - size;
230 validrange = start <= end;
235 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Invalid HTTP range: '%1'").arg(range));
239 ranges.emplace_back(start, end);
246 static const int s_overhead = 90;
247 if (ranges.size() > 1)
250 {
return (First.first == Second.first) && (First.second == Second.second); };
252 {
return First.first < Second.first; };
255 std::sort(ranges.begin(), ranges.end(), lessthan);
260 for (
const auto & range : ranges)
261 debug.append(QString(
"%1:%2").arg(range.first).arg(range.second));
262 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Sorted ranges: %1").arg(
debug.join(
" ")));
266 bool finished =
false;
270 for (
uint i = 0; i < (ranges.size() - 1); ++i)
272 if ((ranges[i].second + s_overhead) >= ranges[i + 1].first)
275 ranges[i + 1].first = ranges[i].first;
277 uint64_t end = std::max(ranges[i].second, ranges[i + 1].second);
278 ranges[i].second = ranges[i + 1].second = end;
282 auto last = std::unique(ranges.begin(), ranges.end(), equals);
283 ranges.erase(last, ranges.end());
288 PartialSize = std::accumulate(ranges.cbegin(), ranges.cend(),
static_cast<uint64_t
>(0),
sumrange);
294 for (
const auto & range : ranges)
295 debug.append(QString(
"%1:%2").arg(range.first).arg(range.second));
296 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Final ranges : %1").arg(
debug.join(
" ")));
297 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Bytes to send: %1").arg(PartialSize));
static QString GetRangeHeader(HTTPRanges &Ranges, int64_t Size)
static MythHTTPStatus ParseRanges(const QString &Request, int64_t TotalSize, HTTPRanges &Ranges, int64_t &PartialSize)
Parse a range request header.
static void BuildMultipartHeaders(MythHTTPResponse *Response)
static HTTPMulti HandleRangeWrite(HTTPVariant Data, int64_t Available, int64_t &ToWrite, int64_t &Offset)
static void HandleRangeRequest(MythHTTPResponse *Response, const QString &Request)
static QString GetContentType(const MythMimeType &Mime)
std::vector< HTTPRange > HTTPRanges
std::pair< uint64_t, uint64_t > HTTPRange
@ HTTPRequestedRangeNotSatisfiable
std::variant< std::monostate, HTTPData, HTTPFile > HTTPVariant
std::vector< HTTPData > HTTPContents
std::pair< HTTPData, HTTPData > HTTPMulti
static QString s_multipartBoundary
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
VERBOSE_PREAMBLE Most debug(nodatabase, notimestamp, noextra)") VERBOSE_MAP(VB_GENERAL