Запрос URL-адреса HLS/DASH для размещенного на Reddit видео через XHR приводит к ошибке «Отсутствует заголовок CORS ‘Access-Control-Allow-Origin’»

Я пытаюсь использовать библиотеку hls.js для отображения размещенных на Reddit видео из плейлиста HLS/DASH. Однако при доступе к любому URL-адресу Reddit HLS/DASH, например этому, через XHR произойдет сбой. из-за того, что ошибка говорит о нарушении политики того же происхождения:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://v.redd.it/5r0nz8sywgl41/DASHPlaylist.mpd. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).

Странно то, что запрос, предшествующий GET, является OPTIONS, который возвращает заголовок Access-Control-Allow-Origin с правильным URL-адресом источника. Я вижу ответ в консоли разработчика, но запрос по-прежнему «не выполняется». Если я использую расширение «Allow CORS: Access-Control-Allow-Origin», все работает нормально. Что я делаю неправильно?

Заголовки запроса OPTIONS:

Host: v.redd.it
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0
Accept: */*
Accept-Language: lt,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization
Referer: http://localhost:3000/
Origin: http://localhost:3000
DNT: 1
Connection: keep-alive
Save-Data: on
Pragma: no-cache
Cache-Control: no-cache
TE: Trailers

ВАРИАНТЫ заголовков ответов:

HTTP/2 200 OK
retry-after: 0
access-control-allow-origin: http://localhost:3000
access-control-allow-headers: authorization
access-control-allow-methods: GET
access-control-max-age: 3000
date: Sun, 08 Mar 2020 18:40:29 GMT
via: 1.1 varnish
x-served-by: cache-hhn4029-HHN
x-cache: HIT
x-cache-hits: 0
x-timer: S1583692829.044409,VS0,VE0
server: snooserv
accept-ranges: bytes
x-cdn-server-region: EU-East
x-cdn-client-region: EU
x-cdn-name: fastly
access-control-expose-headers: x-cdn-server-region, x-cdn-client-region, x-cdn-name
cache-control: public, max-age=604800, s-maxage=86400, must-revalidate
vary: Access-Control-Request-Headers, Access-Control-Request-Method,Origin
content-length: 0
X-Firefox-Spdy: h2

Получить заголовки запроса:

Host: v.redd.it
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0
Accept: */*
Accept-Language: lt,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br
Referer: http://localhost:3000/
Authorization: Bearer --------------------------- (actual token here)
Origin: http://localhost:3000
DNT: 1
Connection: keep-alive
Save-Data: on
Pragma: no-cache
Cache-Control: no-cache
TE: Trailers

ПОЛУЧИТЬ заголовки ответа:

HTTP/2 200 OK
last-modified: Sun, 08 Mar 2020 15:34:11 GMT
etag: "0abfb243cb8d03188bf34ff29d6d4af8"
content-type: application/dash+xml
via: 1.1 varnish
date: Sun, 08 Mar 2020 18:40:29 GMT
via: 1.1 varnish
x-served-by: cache-bwi5138-BWI, cache-hhn4029-HHN
x-cache: HIT, HIT
x-cache-hits: 2, 34
x-timer: S1583692829.127808,VS0,VE0
server: snooserv
accept-ranges: bytes
x-cdn-server-region: EU-East
x-cdn-client-region: EU
x-cdn-name: fastly
access-control-expose-headers: x-cdn-server-region, x-cdn-client-region, x-cdn-name
cache-control: public, max-age=604800, s-maxage=86400, must-revalidate
vary: Origin
content-length: 1976
X-Firefox-Spdy: h2

ПОЛУЧИТЬ ответ:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<MPD mediaPresentationDuration="PT15.034S" minBufferTime="PT1.500S" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" type="static" xmlns="urn:mpeg:dash:schema:mpd:2011">
    <Period duration="PT15.034S">
        <AdaptationSet segmentAlignment="true" subsegmentAlignment="true" subsegmentStartsWithSAP="1">
            <Representation bandwidth="1172841" codecs="avc1.4d401f" frameRate="30" height="480" id="VIDEO-1" mimeType="video/mp4" startWithSAP="1" width="720">
                <BaseURL>DASH_480</BaseURL>
                <SegmentBase indexRange="913-992" indexRangeExact="true">
                    <Initialization range="0-912"/>
                </SegmentBase>
            </Representation>
            <Representation bandwidth="784684" codecs="avc1.4d401e" frameRate="30" height="360" id="VIDEO-2" mimeType="video/mp4" startWithSAP="1" width="540">
                <BaseURL>DASH_360</BaseURL>
                <SegmentBase indexRange="915-994" indexRangeExact="true">
                    <Initialization range="0-914"/>
                </SegmentBase>
            </Representation>
            <Representation bandwidth="592784" codecs="avc1.4d401e" frameRate="30" height="240" id="VIDEO-3" mimeType="video/mp4" startWithSAP="1" width="360">
                <BaseURL>DASH_240</BaseURL>
                <SegmentBase indexRange="915-994" indexRangeExact="true">
                    <Initialization range="0-914"/>
                </SegmentBase>
            </Representation>
            <Representation bandwidth="91690" codecs="avc1.4d400a" frameRate="30" height="96" id="VIDEO-4" mimeType="video/mp4" startWithSAP="1" width="144">
                <BaseURL>DASH_96</BaseURL>
                <SegmentBase indexRange="912-991" indexRangeExact="true">
                    <Initialization range="0-911"/>
                </SegmentBase>
            </Representation>
        </AdaptationSet>
    </Period>
</MPD>

Большое спасибо за твою помощь.


person Emilis Kiškis    schedule 08.03.2020    source источник


Ответы (1)


Проблема только в том, что ответ от https://v.redd.it/5r0nz8sywgl41/DASHPlaylist.mpd на запрос GET из вашего кода не включает заголовок ответа Access-Control-Allow-Origin. Как ни странно, он включает заголовок ответа Access-Control-Expose-Headers. Итак, причина только в том, что сервер неправильно настроен; на самом деле он не поддерживает CORS должным образом.

В частности: даже несмотря на то, что сервер отправляет заголовок Access-Control-Allow-Origin в ответ на предварительную проверку OPTIONS, этого самого по себе недостаточно, чтобы браузеры разрешили вашему интерфейсному коду получить доступ к ответу на фактический запрос GET в вашем коде. Чтобы ваш код работал, сервер также должен отправить заголовок Access-Control-Allow-Origin в ответ на этот запрос GET.

Но на самом деле вы можете обойти https://v.redd.it неправильную конфигурацию и получить доступ к URL-адресам Reddit HLS/DASH из кода внешнего интерфейса без расширения браузера — отправив запрос через прокси-сервер CORS, как в следующем примере.

const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url = "https://v.redd.it/5r0nz8sywgl41/DASHPlaylist.mpd";
fetch(proxyurl + url)
.then(response => response.text())
.then(contents => console.log(contents))

Объяснение см. в ответе на странице https://stackoverflow.com/a/43881141/441757.

Вы можете легко запустить свой собственный прокси, используя код из https://github.com/Rob--W/cors-anywhere/, и вы сможете быстро развернуть собственный прокси на Heroku буквально всего за 2-3 минуты, с помощью 5 команд:

git clone https://github.com/Rob--W/cors-anywhere.git
cd cors-anywhere/
npm install
heroku create
git push heroku master

После выполнения этих команд вы получите собственный сервер CORS Anywhere, работающий, например, по адресу https://cryptic-headland-94862.herokuapp.com/. Итак, вместо префикса URL-адреса вашего запроса с https://cors-anywhere.herokuapp.com, добавьте к нему префикс URL-адреса для вашего собственного экземпляра; например, https://cryptic-headland-94862.herokuapp.com/https://example.com.

person sideshowbarker    schedule 09.03.2020
comment
Спасибо за дополнительную информацию. Однако не нарушает ли это политику доступа к контенту Reddit или что-то в этом роде? - person Emilis Kiškis; 12.03.2020
comment
Я не знаю, какая политика может быть у Reddit, которая запрещала бы вам использовать прокси (свой или чужой) для запросов к их сайту. Вы уже можете делать запросы к серверу Reddit с помощью curl или wget, любого HTTP-клиента или любой среды выполнения на стороне сервера, не сталкиваясь с какими-либо проблемами. Единственное место, где налагаются какие-либо ограничения на доступ к URL-адресам Reddit, — это браузеры. - person sideshowbarker; 12.03.2020
comment
Или, если сформулировать проблему политики другими словами: наличие заголовка ответа Access-Control-Allow-Origin — это просто явный индикатор для браузеров, чтобы снять ограничения, которые браузеры уже налагают по умолчанию. Но отсутствие заголовка ответа Access-Control-Allow-Origin не является явным признаком того, что сайт намеренно намеревается запретить внешнему коду JavaScript доступ к ответам. Вместо этого просто владельцы сайтов не уделили время включению CORS для своего сайта, или что они пытались включить CORS для сайта, но вместо этого неправильно настроили его. - person sideshowbarker; 12.03.2020
comment
Значит, CORS больше похож на инструмент для клиентов, позволяющий сделать просмотр неизвестных источников более безопасным? - person Emilis Kiškis; 12.03.2020
comment
CORS был разработан почти исключительно для интрасетей и, в частности, для атак с повышением привилегий с запутанным заместителем внутри интрасетей. Проблемный случай, для решения которого был разработан CORS, — это случай, когда интрасети ошибочно полагают, что любой запрос, исходящий из источника внутри интрасети, за брандмауэром, является «безопасным» клиентом, и поэтому наивно/небезопасно выполняет аутентификацию только на основе IP. адрес. Но вредоносное стороннее веб-приложение может запускаться с компьютера пользователя внутри интрасети, с IP-адреса этого пользователя с правами этого пользователя. - person sideshowbarker; 12.03.2020