Nagłówek zakresu HTTP

81

Czytałem http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35 i próbowałem dowiedzieć się, jak kontynuować pobieranie pliku.

Na przykład załóżmy, że plik ma długość 100 bajtów, a ja mam wszystkie 100 bajtów. Jednak nie wiem, jaki powinien być oczekiwany rozmiar pliku, więc pytam o plik i określam nagłówek Range, który wygląda następująco:

Range: bytes=100-

Czy to prawidłowe żądanie dotyczące zakresu?

dhruvbird
źródło
5
Erm, przykład pod nim cytuje 'bajty = 9500-' jako poprawne, więc ....
Wrikken
1
Najbardziej aktualnym numerem referencyjnym jest RFC7233 - httpwg.github.io/specs/rfc7233.html
Mark Nottingham
2
Możesz najpierw wysłać żądanie HEAD i sprawdzić długość pliku.
Matheus Rocha

Odpowiedzi:

55

Jest to żądanie poprawne składniowo, ale nie spełniające wymagań. Jeśli spojrzysz dalej w tej sekcji, zobaczysz:

Jeśli składniowo poprawny zestaw zakresu bajtów zawiera co najmniej jedną specyfikację zakresu bajtów, której pozycja pierwszego bajtu jest mniejsza niż bieżąca długość ciała jednostki, lub co najmniej jedną specyfikację zakresu bajtów sufiksu z wartością inną niż - zerowa długość sufiksu, wtedy zestaw zakresu bajtów jest zadowalający. W przeciwnym razie zestaw zakresu bajtów jest niezadowalający. Jeśli zestaw zakresu bajtów jest niezadowalający, serwer POWINIEN zwrócić odpowiedź o statusie 416 (Żądany zakres nie jest spełniony) . W przeciwnym razie serwer POWINIEN zwrócić odpowiedź o statusie 206 (zawartość częściowa), zawierającą dopuszczalne zakresy treści jednostki.

Więc myślę, że w twoim przykładzie serwer powinien zwrócić 416, ponieważ nie jest to prawidłowy zakres bajtów dla tego pliku.

Marc Novakowski
źródło
Czy jest więc jakiś sposób, aby klient wznowił pobieranie bez wywołania HEAD, aby najpierw ustalić długość treści, a następnie wykonać obliczenia i pobrać rzeczywistą zawartość? Mam na myśli jakieś otwarte adresowanie, takie jak „podaj mi wszystkie bajty po takim a takim bajcie…”
dhruvbird
5
Klient będzie już wiedział, czy ma wszystkie dane z pierwotnego żądania - powinien albo odebrać nagłówek Content-Length w oryginalnej odpowiedzi, albo jeśli był kodowany w kawałkach, otrzyma fragment o zerowej długości, aby wskazać odpowiedź była kompletna. Jeśli nie zapisałeś tego stanu i masz po prostu kawałek bajtów na dysku, to tak, będziesz musiał albo wykonać żądanie HEAD, albo użyć nagłówka Range, aby zapytać o zakres bajtów, a jeśli odzyskasz 416 odpowiedź wiesz, że masz wszystkie bajty.
Marc Novakowski
Myślę, że Expect-Continue pozwala na przesyłanie strumieniowe fragmentów mniej więcej tak, jak chcesz?
MJB
@MarcNovakowski Właściwie rozważmy przypadek wget i użycie flagi -c. Ponieważ wget nie przechowuje żadnych metadanych dotyczących kompletności pliku, przypuśćmy, że rozmiar pliku na dysku wynosi 99 bajtów. wget zażąda zakresu bajtów „100-” i uważam, że serwer powinien odpowiedzieć odpowiedzią o długości 0, ponieważ żądanie jest tylko 1 za końcem pliku.
dhruvbird
148

Jak zasugerował Wrikken , jest to poprawne żądanie. Jest to również dość powszechne, gdy klient żąda nośnika lub wznawia pobieranie.

Klient często sprawdza, czy serwer obsługuje żądania z zakresu innych niż tylko szukanie Accept-Rangesodpowiedzi. Chrome zawsze wysyła Range: bytes=0-z pierwszym żądaniem GET filmu, więc nie można tego odrzucić.

Za każdym razem, gdy klient dołącza Range:do swojego żądania, nawet jeśli jest źle sformułowany, oczekuje odpowiedzi częściowej zawartości (206). Kiedy szukasz do przodu podczas odtwarzania wideo HTML5, przeglądarka żąda tylko punktu początkowego. Na przykład:

Range: bytes=3744-

Tak więc, aby klient mógł prawidłowo odtwarzać wideo, Twój serwer musi być w stanie obsłużyć te niekompletne żądania zakresu.

Typ „zakresu” określony w pytaniu możesz obsłużyć na dwa sposoby:

Najpierw możesz odpowiedzieć z żądanym punktem początkowym podanym w odpowiedzi, a następnie całkowitą długością pliku minus jeden (żądany zakres bajtów jest indeksowany przez zero). Na przykład:

Żądanie:

GET /BigBuckBunny_320x180.mp4 
Range: bytes=100-

Odpowiedź:

206 Partial Content
Content-Type: video/mp4
Content-Length: 64656927
Accept-Ranges: bytes
Content-Range: bytes 100-64656926/64656927

Po drugie, możesz odpowiedzieć, podając punkt początkowy podany w żądaniu i otwartą długość (rozmiar) pliku. Dotyczy to transmisji internetowych lub innych mediów, w przypadku których całkowita długość nie jest znana. Na przykład:

Żądanie:

GET /BigBuckBunny_320x180.mp4
Range: bytes=100-

Odpowiedź:

206 Partial Content
Content-Type: video/mp4
Content-Length: 64656927
Accept-Ranges: bytes
Content-Range: bytes 100-64656926/*

Porady:

Musisz zawsze odpowiadać, podając długość treści zawartą w zakresie. Jeśli zakres jest kompletny, od początku do końca, długość treści jest po prostu różnicą:

Żądanie: zakres: bajty = 500-1000

Odpowiedź: Zakres zawartości: bajty 500-1000 / 123456

Pamiętaj, że zakres jest indeksowany przez zero, więc w Range: bytes=0-999rzeczywistości żąda 1000 bajtów, a nie 999, więc odpowiedz na przykład:

Content-Length: 1000
Content-Range: bytes 0-999/123456

Lub:

Content-Length: 1000
Content-Range: bytes 0-999/*

Ale jeśli to możliwe, unikaj tej drugiej metody, ponieważ niektóre odtwarzacze multimedialne próbują obliczyć czas trwania na podstawie rozmiaru pliku. Jeśli Twoje żądanie dotyczy treści multimedialnych, co jest moim przeczuciem, w odpowiedzi podaj czas jego trwania. Odbywa się to w następującym formacie:

X-Content-Duration: 63.23 

To musi być zmiennoprzecinkowa. W przeciwieństwie do Content-Lengthtego ta wartość nie musi być dokładna. Służy do pomocy graczowi w wyszukiwaniu filmów. Jeśli transmitujesz transmisję internetową i masz tylko ogólne pojęcie o tym, jak długo to potrwa, lepiej jest uwzględnić szacowany czas trwania, a nie całkowicie go zignorować. Tak więc w przypadku dwugodzinnej transmisji internetowej możesz dołączyć coś takiego:

X-Content-Duration: 7200.00 

W przypadku niektórych typów multimediów, takich jak webm, musisz również uwzględnić typ zawartości, na przykład:

Content-Type: video/webm 

Wszystko to jest niezbędne do prawidłowego odtwarzania multimediów, zwłaszcza w HTML5. Jeśli nie podasz czasu trwania, gracz może spróbować obliczyć czas trwania (aby umożliwić wyszukiwanie) na podstawie rozmiaru pliku, ale nie będzie to dokładne. Jest to w porządku i konieczne do transmisji internetowych lub transmisji na żywo, ale nie jest idealne do odtwarzania plików wideo. Możesz wyodrębnić czas trwania za pomocą oprogramowania takiego jak FFMPEG i zapisać go w bazie danych lub nawet w nazwie pliku.

X-Content-Durationjest wycofywany na korzyść Content-Duration, więc to też bym uwzględnił. Podstawowa odpowiedź na żądanie „0-” zawierałaby co najmniej następujące elementy:

HTTP/1.1 206 Partial Content
Date: Sun, 08 May 2013 06:37:54 GMT
Server: Apache/2.0.52 (Red Hat)
Accept-Ranges: bytes
Content-Length: 3980
Content-Range: bytes 0-3979/3980
Content-Type: video/webm
X-Content-Duration: 2054.53
Content-Duration: 2054.53

Jeszcze jedna kwestia: Chrome zawsze rozpoczyna pierwsze żądanie wideo od następujących:

Range: bytes=0-

Niektóre serwery wyślą zwykłą odpowiedź 200 jako odpowiedź, którą zaakceptuje (ale z ograniczonymi opcjami odtwarzania), ale zamiast tego spróbuj wysłać 206, aby pokazać, że twój serwer obsługuje zakresy. RFC 2616 mówi, że ignorowanie nagłówków zakresów jest dopuszczalne.

Victor Stoddard
źródło
Co zrobić, jeśli treść jest strumieniem wideo na żywo, który nie ma określonego czasu trwania?
Joel Barsotti
@Joel, musisz odpowiedzieć z czasem trwania, nawet jeśli go nie znasz. W takim przypadku po prostu wypróbuj 0,0. Dla klienta czas trwania i tak nie ma znaczenia, ponieważ zwykle nie można przeskanować transmisji na żywo. Jeśli 0,0 nie działa, po prostu spróbuj czegoś naprawdę wysokiego, na przykład 1000000,00.
Victor Stoddard,
@VictorStoddard Czy fragmentaryczne przesyłanie strumieniowe można zastosować do zwykłego pobierania plików, gdy w żądaniu klienta nie ma nagłówka Range? Jak serwer powinien odpowiedzieć w takim przypadku?
gkiko,
@gkiko Nie ma większej różnicy niż użycie nagłówka Transfer-Encoding zamiast Content-Length w Chunked Transfer Encoding. Porcje mogą pochodzić z pojedynczego pliku, a serwer może ustawić rozmiar porcji. Klient powinien zbuforować i poskładać fragmenty w miarę ich otrzymywania. Alternatywnie, HTTP Streaming wykorzystuje wstępnie nagrane segmenty pliku multimedialnego, gdzie są one zapisywane na serwerze jako pojedyncze części (pliki ts). Te segmenty są obsługiwane za pomocą zwykłych żądań GET plików HTTP uzyskanych z pliku indeksu. Uważam, że segmentacja jest trudna, ale to było lata temu.
Victor Stoddard
Content-Length: 64656927 Accept-Ranges: bajty Content-Range: bajty 100-64656926 Dlaczego Content-Length ma wartość inną niż „64656827”?
iwind
7

W przeciwieństwie do odpowiedzi Marka Novakowskiego, która z jakiegoś powodu spotkała się z poparciem wielu, tak, jest to uzasadniona i satysfakcjonująca prośba.

W rzeczywistości standard, jak wskazał Wrikken, jest właśnie takim przykładem. W praktyce Firefox odpowiada na takie żądania zgodnie z oczekiwaniami (kodem 206) i właśnie tego używam do implementacji pobierania progresywnego, to znaczy otrzymuję tylko koniec długiego pliku dziennika, który rośnie w czasie rzeczywistym wraz z odpytywaniem.

Francesco Potortì
źródło
2
Przeczytaj ponownie odpowiedź Marca Novakowkiego. „zadowalający” ma szczególne znaczenie w cytowanym przez niego dokumencie RFC. To żądanie nie jest możliwe do spełnienia, ponieważ żądane bajty są poza długością pliku.
Scott Lamb
1
Firefox nie jest elementem oprogramowania, który odpowiada na żądanie, jest to serwer http
Colin D
Tak, przepraszam, miałem na myśli Apache
Francesco Potortì
5

Dla osób, które natkną się na powyższą odpowiedź Victora Stoddarda w 2019 r. I nabierają nadziei i łzawych oczu, zauważ, że:

a) Wsparcie dla X-Content-Duration zostało usunięte w Firefoksie 41: https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/41#HTTP

b) Myślę, że był obsługiwany tylko w Firefoksie dla audio i wideo .ogg, a nie dla innych typów.

c) Nie widzę, żeby kiedykolwiek było obsługiwane w Chrome, ale może to być po prostu brak badań z mojej strony. Jednak jego obecność lub brak wydaje się nie mieć żadnego wpływu w taki czy inny sposób na filmy webm lub ogv od dzisiaj w Chrome 71.

d) Nie mogę znaleźć miejsca, w którym „Content-Duration” zastąpiło „X-Content-Duration”, nie sądzę, że „X-Content-Duration” istniało wystarczająco długo, aby następna nazwa nagłówka była.

Myślę, że oznacza to, że od dzisiaj, jeśli chcesz udostępniać kontenery webm lub ogv, które zawierają strumienie, które nie znają czasu ich trwania (np. Wyjście potoku ffpeg), do Chrome lub FF i chcesz, aby można je było czyścić w element wideo HTML 5, prawdopodobnie nie masz szczęścia. Firefox 64.0 podejmuje połowiczną próbę uczynienia tych rzeczy łatwymi do szorowania, niezależnie od tego, czy obsługujesz żądania dotyczące zakresu, ale jest zdezorientowany i rzuca kołowrotkiem, dopóki strumień nie zostanie całkowicie pobrany, jeśli szukasz kilka razy więcej, niż uważa za stosowne. Chrome nawet nie próbuje, to po prostu nopes się i nie pozwoli Ci szorować wcale aż cały strumień jest gotowy do gry .

Chris McDonough
źródło
Oto długi wątek autorstwa programistów FF, który mówi o obsłudze tego typu plików. bugzilla.mozilla.org/show_bug.cgi?id=657791
Chris McDonough,