Обслуживать запрос диапазона HTTP с помощью phoenix?

В настоящее время я разрабатываю веб-сайт с Phoenix, и у меня есть раздел видео, который должен воспроизводиться в фоновом режиме.

Хотя он отлично работает в Chrome и Firefox, он не работает в Safari.

Я подозреваю, что это потому, что ковбой неправильно обслуживает HTTP-запрос диапазона.

Есть ли способ включить (если отключено по умолчанию)?

$ curl -H Range:bytes=16- -I http://localhost:4000/videos/vid_home_1.mp4
HTTP/1.1 200 OK
server: Cowboy
date: Tue, 12 Apr 2016 14:41:20 GMT
content-length: 633787
cache-control: public
etag: 480A03F
content-type: video/mp4

Когда это должно быть 206, как показано на сервере nginx:

$ curl -H Range:bytes=16- -I http://localhost/videos/vid_home_1.mp4
HTTP/1.1 206 Partial Content
Server: nginx/1.8.0
Date: Tue, 12 Apr 2016 14:46:17 GMT
Content-Type: video/mp4
Content-Length: 633771
Last-Modified: Mon, 11 Apr 2016 12:26:26 GMT
Connection: keep-alive
ETag: "570b97f2-9abbb"
Content-Range: bytes 16-633786/633787

person TheSquad    schedule 12.04.2016    source источник
comment
зачем обслуживать статический контент через cowboy?   -  person ardhitama    schedule 12.04.2016
comment
@ardhitama: ну, в основном потому, что это то, что использует phoenix, а я все еще нахожусь на ранней стадии разработки ... а также, насколько мне известно, cowboy довольно хорошо обслуживает статический контент. Однако, если я не могу обслужить запрос диапазона, я могу пройти через nginx и обратный прокси-сервер к моему серверу phoenix, но я бы предпочел исправить это.   -  person TheSquad    schedule 12.04.2016


Ответы (2)


Я нашел способ сделать это сам с помощью Plugs... Итак, если кто-то хочет обслуживать Range Request с помощью Phoenix / Elixir, вот что вам нужно сделать (это довольно просто и не принимает во внимание rfc)

defmodule Plug.Range do                                                                                       
  @behaviour Plug                                                                                             
  @allowed_methods ~w(GET HEAD)                                                                               
  import Plug.Conn                                                                                            

  def init(options) do                                                                                        
    options                                                                                                   
  end                                                                                                         

  def call(conn, _opts) do                                                                                    
    if (Enum.empty?(Plug.Conn.get_req_header(conn, "range"))) do                                              
      conn                                                                                                    
    else                                                                                                      
      file_path = "priv/static" <> conn.request_path                                                          
      if File.exists? file_path do                                                                            

        stats = File.stat! file_path                                                                          
        filesize = stats.size                                                                                 

        req = Regex.run(~r/bytes=([0-9]+)-([0-9]+)?/, conn |> Plug.Conn.get_req_header("range") |> List.first)

        {req_start, _} = req |> Enum.at(1) |> Integer.parse                                                   
        {req_end, _} = req |> Enum.at(2, filesize |> to_string) |> Integer.parse                              

        file_end = ( filesize - 2) |> to_string                                                               

        length = req_end - req_start + 1                                                                      

        conn                                                                                                  
        |> Plug.Conn.put_resp_header("Content-Type", "video/mp4")                                             
        |> Plug.Conn.put_resp_header("Accept-Ranges", "bytes")                                                
        |> Plug.Conn.put_resp_header("Content-Range", "bytes #{req_start}-#{req_end}/#{filesize}")            
        |> Plug.Conn.send_file(206, file_path, req_start, length)                                             
        |> Plug.Conn.halt                                                                                     
      else                                                                                                    
        conn                                                                                                  
      end                                                                                                     
    end                                                                                                       
  end                                                                                                         
end           

Как вы можете видеть, прямо сейчас он будет отправлять только тип содержимого "video/mp4", но вы можете легко заставить что-то работать для всего...

Наконец, чтобы Plug заработал, вам нужно поместить его непосредственно перед Plug.static в файле конечной точки проекта.

Надеюсь, это поможет кому-то...

РЕДАКТИРОВАТЬ: Для тех, кто заинтересован, я создал для этого пакет github/hex.pm: Hex link
ссылка на github

person TheSquad    schedule 13.04.2016

Похоже, что Cowboy (пока) не поддерживает заголовок Range, поэтому для этого вам нужно будет использовать другой веб-сервер.

Источник: https://github.com/ninenines/cowboy/issues/306

person Patrick Oscity    schedule 13.04.2016
comment
Вы правы, Range еще не поддерживается, однако я нашел способ сделать это с помощью плагинов, см. мой ответ ниже... - person TheSquad; 13.04.2016
comment
Лично я предпочел бы реализацию, совместимую с RFC. Но если это работает для вас, отлично! Ваше здоровье ;-) - person Patrick Oscity; 13.04.2016
comment
Это только для целей разработки... К сожалению, сейчас у меня нет времени на создание реализации, совместимой с RFC... Я, вероятно, создам github через месяц с подключаемым модулем, совместимым с RFC, но пока он будет работать для меня ;-) - person TheSquad; 13.04.2016
comment
Эй, я не хотел бросать вам вызов сделать реализацию, совместимую с RFC ;-) Просто хочу сказать, что вам, вероятно, не следует запускать службу потокового видео с высоким трафиком в производстве на основе этого ;-P - person Patrick Oscity; 13.04.2016