Разбираем HTTP Range по стандарту
В одном из проектов мне понадобилось разобрать HTTP Range запрос, чтобы добавить поддержку загрузки файлов по частям. В сети полно различных примеров, но я так и не нашел ни одной полной реализации RFC 2616. Один код не учитывал, что диапазонов может быть несколько, другой, что стандарт допускает запросы больше размера документа, третий не различает синтаксически правильный и недостижимый запрос, как рекомендует стандарт. Поэтому я решил написать свою реализацию и поделиться со всеми. Подробности и пример реализации на PHP под катом.
Как гласит стандарт, запрос диапазона состоит из двух частей: размерность диапазона и список правил выборки. Единственная размерность диапазонов определенная в RFC 2616 – байты. Также, необходимо учесть, что в одном и том же заголовке Range может быть сразу несколько диапазонов, указанных через запятую.
Существует два варианта выборки диапазона HTTP клиентом.
Первый — указание начальной и конечной позиции в теле документа. Первая позиция начинается с нуля. Последняя позиция ОБЯЗАНА быть больше или равна первой позиции в запросе. В противном случае, согласно стандарту, этот заголовок реализация ОБЯЗАНА игнорировать. Если последняя позиция отсутствует или ее значение больше или равно размеру документа, то последней позицией считается текущий размер документа в байтах, уменьшенный на 1. По стандарту, это не является ошибкой, так как позволяет клиенту запрашивать часть документа, не зная его размер заранее.
Например, для документа размером 10 байт, bytes=1-9 запрашивает 9 байт, начиная со второго и заканчивая последним байтом тела документа.
Второй — выборка последних N байт тела документа. Если размер документа меньше, чем указанный в запросе, то будет выбран весь документ. Например, bytes=-2 запрашивает последние 2 байта.
После завершения обработки всех диапазонов серверу СЛЕДУЕТ определить, есть ли хоть один диапазон, который содержит не нулевое количество байт. Если таких нет, то серверу СЛЕДУЕТ ответить клиенту 416 (Requested range not satisfiable), иначе 206 (Partial Content).
Реализация считается «условно совместимой», если она не выполняет условия СЛЕДУЕТ (SHOULD). Таким образом, полная обработка запроса Range может быть достигнута при выполнении всех условий стандарта.
Источник
HTTP-заголовок Range
например, предположим, что файл имеет длину 100 байт и у меня есть все 100 байт. Однако я не знаю, каким должен быть ожидаемый размер файла, поэтому я прошу файл и указываю заголовок диапазона, который выглядит следующим образом:
это допустимый запрос диапазона?
3 ответов:
Это синтаксически допустимый запрос, но не выполнимый запрос. Если смотреть дальше в этом разделе вы увидите:
Если синтаксически допустимый набор байтовых диапазонов включает в себя по крайней мере одну спецификацию байтового диапазона, первый байт-pos которой меньше текущей длины тела сущности, или по крайней мере одну спецификацию суффикса байтового диапазона с ненулевой длиной суффикса, то набор байтовых диапазонов удовлетворителен. В противном случае набор байтовых диапазонов не может быть удовлетворен. если байт-диапазон-комплект неудовлетворительно, сервер должен вернуть ответ со статусом 416 (запрошенный диапазон не удовлетворен). В противном случае сервер должен вернуть ответ со статусом 206 (частичное содержимое), содержащий удовлетворяемые диапазоны сущности-тела.
поэтому я думаю, что в вашем примере, сервер должен возвращать 416, так как это не допустимый диапазон байтов для этого файла.
как Wrikken предложил, это действительный запрос. Это также довольно часто, когда клиент запрашивает носитель или возобновляет загрузку.
клиент часто проверяет, обрабатывает ли сервер ранжированные запросы, кроме как просто ищет Accept-Ranges ответ. Хром всегда передает Range: bytes=0- С его первым получить запрос на видео, так что это то, что вы не можете уволить.
всякий раз, когда клиент включает в себя Range: в своем запросе, даже если он деформирован, он ожидает частичного ответа на контент (206). При поиске вперед во время воспроизведения видео HTML5 браузер запрашивает только начальную точку. Например:
таким образом, чтобы клиент мог правильно воспроизводить видео, ваш сервер должен иметь возможность обрабатывать эти неполные запросы диапазона.
вы можете обрабатывать тип «диапазона», указанный в вашем вопросе, двумя способами:
во-первых, вы смогли ответить с спрошенным начальная точка задается в ответе, затем общая длина файла минус один (запрошенный диапазон байтов индексируется нулем). Например:
во-вторых, вы можете ответить с начальной точкой, указанной в запросе, и открытой длиной файла (размером). Это для веб-трансляций или других средств массовой информации, где общая длина неизвестна. Например:
советы:
вы всегда должны отвечать длиной содержимого, включенной в диапазон. Если диапазон завершен, с начала до конца, то длина содержимого-это просто разница:
запрос: Диапазон: число байт=500-1000
ответ: Контент-диапазон: 500-1000 байт/123456
помните, что диапазон имеет нулевую индексацию, поэтому Range: bytes=0-999 на самом деле запрашивает 1000 байт, а не 999, поэтому ответьте чем-то вроде:
но, избегайте последнего метода, если это возможно, потому что некоторые медиа-плееры пытаются выяснить продолжительность от размера файла. Если ваш запрос касается медиа-контента, что является моей догадкой, то вы должны включить его продолжительность в ответ. Это делается в следующем формате:
это должно быть с плавающей точкой. В отличие от Content-Length , это значение не обязательно должно быть точным. Он используется, чтобы помочь игроку искать вокруг видео. Если вы потокового вещания и иметь только общее представление о том, как долго это будет, лучше указать свой предполагаемый срок, а не игнорировать его. Итак, для двухчасовой веб-трансляции вы можете включить что-то вроде:
для некоторых типов носителей, таких как webm, необходимо также включить тип содержимого, например:
все это необходимо для медиа, чтобы играть правильно, особенно в HTML5. Если вы не даете продолжительность, игрок может попытаться выяснить продолжительность (чтобы разрешить поиск) из своего размера файла, но это не будет точным. Это нормально, и необходимо для трансляции или в прямом эфире, но не подходит для воспроизведения видео файлов. Вы можете извлечь продолжительность с помощью программного обеспечения, как FFMPEG и сохранить его в базе данных или даже имя файла.
X-Content-Duration постепенно сворачивается в пользу Content-Duration , так что я бы включил это тоже. Один основной, ответ на запрос » 0-» будет включать, по крайней мере, следующее:
еще один момент: Chrome всегда начинает свой первый запрос видео со следующего:
некоторые серверы будут отправлять обычный ответ 200 в качестве ответа, который он принимает (но с ограниченными возможностями воспроизведения), но попробуйте отправить 206 вместо того, чтобы показать, чем ваш сервер обрабатывает диапазоны. RFC 2616 говорит, что допустимо игнорировать заголовки диапазона.
В отличие от ответа Марка Новаковского, который по какой-то причине был поддержан многими, да, это действительный и выполнимый запрос.
на самом деле стандарт, как указал Wrikken, делает именно такой пример. На практике Firefox отвечает на такие запросы, как ожидалось (с кодом 206), и это именно то, что я использую для реализации прогрессивной загрузки, то есть только получает хвост длинного файла журнала, который растет в реальном времени с опросом.
Источник
Используемые по умолчанию значения заголовка Accept
В этой статье описывается, какие значения используются в HTTP-заголовке Accept по умолчанию в зависимости от конкретного запроса и версии браузера.
Значения по умолчанию
Здесь приведены значения, которые отправляются, когда нет никакой уточняющей информации. Обратите внимание, что все браузеры добавляют MIME-тип */* , чтобы были охвачены все возможные варианты. Обычно значения имеют такой вид, когда запросы выполняются через адресную строку или через HTML-элемент .
| Агент пользователя | Значение | Комментарий |
|---|---|---|
| Firefox | В Firefox до версии 65 включительно значение можно изменить с помощью параметра network.http.accept.default (см. исходный код). | |
| Safari, Chrome | Значение улучшено по сравнению с прежними вариантами заголовка Accept : MIME-тип image/png уже не указывается как более приоритетный, чем text/html . | |
| Internet Explorer 8 | image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/x-shockwave-flash, application/msword, */* | См. запись IE and the Accept Header в блоге MSDN под названием IEInternals. |
| Edge | text/html, application/xhtml+xml, image/jxr, */* | |
| Opera | text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1 |
Значения для изображений
Если запрашивается изображение, например через HTML-элемент , агент пользователя часто задаёт уточнённый список подходящих MIME-типов.
| Агент пользователя | Значение | Комментарий |
|---|---|---|
| Firefox | Значение можно изменить с помощью параметра image.http.accept . исходный код | |
| Safari | */* | |
| Chrome | image/webp,image/apng,image/*,*/*;q=0.8 | исходный код |
| Internet Explorer до версии 8 включительно | */* | См. запись IE and the Accept Header в блоге MSDN под названием IEInternals. |
| Internet Explorer 9 | image/png,image/svg+xml,image/*;q=0.8, */*;q=0.5 | См. запись Fiddler is better with Internet Explorer 9 в блоге MSDN под названием IEInternals. |
Значения для видео
Если запрашивается видео через HTML-элемент , в большинстве браузеров используется уточнённое значение.
| Агент пользователя | Значение | Комментарий |
|---|---|---|
| Firefox до версии 3.6 | Не поддерживается для элемента . | |
| Firefox начиная с версии 3.6 | video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5 | См. страницу ошибки 489071. исходный код |
| Chrome | */* | исходный код |
| Internet Explorer до версии 8 включительно | Не поддерживается для элемента . |
Значения для аудиофайлов
| Агент пользователя | Значение | Комментарий |
|---|---|---|
| Firefox начиная с версии 3.6 | audio/webm,audio/ogg,audio/wav,audio/*;q=0.9,application/ogg;q=0.7,video/*;q=0.6,*/*;q=0.5 | См. страницу ошибки 489071. исходный код |
| Safari, Chrome | */* | исходный код |
| Internet Explorer до версии 8 включительно | Не поддерживается для элемента . | |
| Internet Explorer 9 | ? |
Значения для скриптов
Если запрашивается скрипт, например через HTML-элемент
Источник
HTTP Range header
I was reading http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35 and trying to figure out how to continue a file download.
For example, suppose a file is of length 100 bytes and I have all the 100 bytes. However, I don’t know what the expected file size should be, so I ask for the file and specify a Range header that looks like this:
Is this a valid Range request?
5 Answers 5
As Wrikken suggested, it’s a valid request. It’s also quite common when the client is requesting media or resuming a download.
A client will often test to see if the server handles ranged requests other than just looking for an Accept-Ranges response. Chrome always sends a Range: bytes=0- with its first GET request for a video, so it’s something you can’t dismiss.
Whenever a client includes Range: in its request, even if it’s malformed, it’s expecting a partial content (206) response. When you seek forward during HTML5 video playback, the browser only requests the starting point. For example:
So, in order for the client to play video properly, your server must be able to handle these incomplete range requests.
You can handle the type of ‘range’ you specified in your question in two ways:
First, You could reply with the requested starting point given in the response, then the total length of the file minus one (the requested byte range is zero-indexed). For example:
Second, you could reply with the starting point given in the request and an open-ended file length (size). This is for webcasts or other media where the total length is unknown. For example:
Tips:
You must always respond with the content length included with the range. If the range is complete, with start to end, then the content length is simply the difference:
Request: Range: bytes=500-1000
Response: Content-Range: bytes 500-1000/123456
Remember that the range is zero-indexed, so Range: bytes=0-999 is actually requesting 1000 bytes, not 999, so respond with something like:
But, avoid the latter method if possible because some media players try to figure out the duration from the file size. If your request is for media content, which is my hunch, then you should include its duration in the response. This is done with the following format:
This must be a floating point. Unlike Content-Length , this value doesn’t have to be accurate. It’s used to help the player seek around the video. If you are streaming a webcast and only have a general idea of how long it will be, it’s better to include your estimated duration rather than ignore it altogether. So, for a two-hour webcast, you could include something like:
With some media types, such as webm, you must also include the content-type, such as:
All of these are necessary for the media to play properly, especially in HTML5. If you don’t give a duration, the player may try to figure out the duration (to allow for seeking) from its file size, but this won’t be accurate. This is fine, and necessary for webcasts or live streaming, but not ideal for playback of video files. You can extract the duration using software like FFMPEG and save it in a database or even the filename.
X-Content-Duration is being phased out in favor of Content-Duration , so I’d include that too. A basic, response to a «0-» request would include at least the following:
One more point: Chrome always starts its first video request with the following:
Some servers will send a regular 200 response as a reply, which it accepts (but with limited playback options), but try to send a 206 instead to show than your server handles ranges. RFC 2616 says it’s acceptable to ignore range headers.
Источник