Apache2 com mod_mono não responde com conteúdo parcial quando um intervalo é solicitado

1

Eu tenho um servidor Apache2 com o mod_mono em uma máquina virtual que tem o Ubuntu 16.04. Eu observei que o servidor não está respondendo às solicitações com o cabeçalho Range corretamente. Inicialmente, não era possível nem mesmo procurar um horário específico nos arquivos de vídeo, mas usar mod_headers e adicionar Header set Accept-Ranges bytes ao arquivo do VirtualHost corrigiu isso.

No entanto, ele nunca responde com 206 Partial Content , mesmo quando Range é solicitado. O problema que isso causa é que, quando o usuário opta por continuar o download, o download do arquivo é iniciado novamente desde o início. Em dispositivos móveis, o conteúdo é impossível de assistir porque a resposta 200 OK envia o vídeo inteiro, ao qual os dispositivos simplesmente não têm memória suficiente.

A imagem abaixo mostra o problema. Eu estava assistindo a um vídeo e fiz uma pausa. Quando selecionado para reproduzir o vídeo novamente, todo o vídeo é baixado novamente, o que usa mais largura de banda e memória do dispositivo. Procurar também causa isso.

Que configuração devo fazer para que a resposta ao conteúdo parcial funcione?

Aconfiguraçãoatual:

link

ServerNameexemploUserweb_serverGroupweb_serverServerRoot/home/web_server/serverIncludeOptionalmods-enabled/*.loadIncludeOptionalmods-enabled/*.confIncludeOptionalconf-enabled/*.confIncludeOptionalsites-enabled/*.confListen80<IfModulessl_module>Listen443</IfModule><IfModulemod_gnutls.c>Listen443</IfModule>PidFileapache2log/httpd.pidErrorLogapache2log/error.logHostnameLookupsOffLogLevelwarn<Directory"/">
  Require all denied
  Options -Indexes
  AllowOverride None
</Directory>

<FilesMatch "^\.ht">
    Require all denied
</FilesMatch>

LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent

CheckSpelling Off
CheckCaseOnly On

mono_conf.conf (está habilitado para sites):

<VirtualHost *:80>

  ServerName local-server-1
  ServerAdmin web-admin@local-server-1
  DocumentRoot /home/web_server/server/mono

  MonoServerPath local-server-1 "/usr/bin/mod-mono-server"

  MonoSetEnv local-server-1 MONO_IOMAP=all;MONO_OLD_RX=1

  MonoApplications local-server-1 "/:/home/web_server/server/mono"
  <Location "/">
    MonoSetServerAlias local-server-1
  </Location>

  <Location /mono>
    SetHandler mono-ctrl
    Order deny,allow
    Deny from all
    Allow from 127.0.0.1
  </Location>

  <IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/javascript
  </IfModule>

  <Directory "/home/web_server/server/mono">
    SetHandler mono
    Header set Accept-Ranges bytes
    Allow from all
    Require all granted
  </Directory>

</VirtualHost>

<VirtualHost *:443>
  SSLEngine On
  SSLCertificateFile    "/tmp/server.crt"
  SSLCertificateKeyFile "/tmp/server.key"

  ServerName local-server-1
  ServerAdmin web-admin@local-server-1
  DocumentRoot /home/web_server/server/mono

  MonoServerPath local-server-1 "/usr/bin/mod-mono-server"

  MonoSetEnv local-server-1 MONO_IOMAP=all;MONO_OLD_RX=1

  MonoApplications local-server-1 "/:/home/web_server/server/mono"
  <Location "/">
    MonoSetServerAlias local-server-1
  </Location>

  <Location /mono>
    SetHandler mono-ctrl
    Order deny,allow
    Deny from all
    Allow from 127.0.0.1
  </Location>

  <IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/javascript
  </IfModule>

  <Directory "/home/web_server/server/mono">
    SetHandler mono
    Header set Accept-Ranges bytes
    Allow from all
    Require all granted
  </Directory>

</VirtualHost>

Solicitação do navegador:

GET http://localhost/arquivos/video.webm
Host: localhost
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0
Accept: video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5
Accept-Language: pt-BR,pt;q=0.8,en-US;q=0.5,en;q=0.3
Referer: http://localhost/Default.aspx
Range: bytes=18120704-
Cookie: ASP.NET_SessionId=46ED58B6CD7745987060CDF5
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache

Resposta do servidor:

Date: Thu, 18 Jan 2018 04:45:55 GMT
Server: Apache/2.4.18 (Ubuntu)
Last-Modified: Thu, 18 Jan 2018 02:16:55 GMT
X-AspNet-Version: 4.0.30319
Content-Length: 26728741
Cache-Control: private
Accept-Ranges: bytes
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: video/webm
    
por FurretUber 18.01.2018 / 05:56

1 resposta

0

Consegui resolver esse problema criando um manipulador especificamente para verificar se o pedido tem um intervalo e depois dar uma resposta com o intervalo e os cabeçalhos necessários.

No HTML, foi necessário adicionar o seguinte em cada endereço dos arquivos que eu quero permitir solicitações de intervalo:

<source  src="PartialHandler.ashx?arquivos/video.webm"  />

O PartialHandler.ashx contém o seguinte ProcessRequest:

public void ProcessRequest(HttpContext context)
        {
            string a = Uri.UnescapeDataString(context.Request.Url.Query);
            if (a.StartsWith("?", StringComparison.InvariantCultureIgnoreCase))
            {
                a = a.Substring(1);
            }

            Console.WriteLine(a);

            if (a == null || a == "")
            {
                context.Response.StatusCode = 403;
                context.Response.End();
                return;
            }

            Console.WriteLine(a);

            FileInfo aberto;
            long tam_tot;
            long tam_tot_ran;
            try
            {
                aberto = new FileInfo(HttpRuntime.AppDomainAppPath + a);
                tam_tot = aberto.Length;
                tam_tot_ran = tam_tot - 1;
                context.Response.AppendHeader("Accept-Ranges", "0-" + tam_tot_ran);
                context.Response.AppendHeader("Content-Type", MimeMapping.GetMimeMapping(a));
            }
            catch (FileNotFoundException)
            {
                context.Response.StatusCode = 404;
                context.Response.End();
                return;
            }

            string allhead = context.Request.Headers.ToString();
            if (allhead.Contains("Range=bytes"))
            {
                var pedido = context.Request.Headers.Get("Range");

                if (pedido.Contains(","))
                {
                    context.Response.StatusCode = 416;
                    context.Response.End();
                    return;
                }

                Console.WriteLine(pedido); //bytes=5-15
                long end_igual = pedido.IndexOf("=", StringComparison.InvariantCultureIgnoreCase);
                long end_traco = pedido.IndexOf("-", StringComparison.InvariantCultureIgnoreCase);
                string tam_ini_str = pedido.Substring((int)end_igual + 1, (int)end_traco - 1 - (int)end_igual);
                string tam_fin_str = pedido.Substring((int)end_traco + 1);
                long.TryParse(tam_ini_str, out long tam_ini);
                long.TryParse(tam_fin_str, out long tam_fin);

                if (tam_fin > tam_tot_ran)
                {
                    context.Response.StatusCode = 416;
                    context.Response.End();
                    return;
                }

                context.Response.StatusCode = 206;

                if (tam_fin == 0)
                {
                    context.Response.AppendHeader("Content-Length", (tam_tot - tam_ini).ToString());
                    context.Response.AppendHeader("Content-Range", "bytes " + tam_ini + "-" + tam_tot_ran + "/" + tam_tot.ToString());
                    context.Response.TransmitFile(aberto.FullName, tam_ini, tam_tot_ran);
                    context.Response.End();
                }
                else
                {
                    context.Response.AppendHeader("Content-Length", (tam_fin - tam_ini + 1).ToString());
                    context.Response.AppendHeader("Content-Range", "bytes " + tam_ini + "-" + tam_fin + "/" + tam_tot.ToString());
                    context.Response.TransmitFile(aberto.FullName, tam_ini, tam_fin);
                    context.Response.End();
                }

            }
            else
            {
                context.Response.AppendHeader("Content-Length", aberto.Length.ToString());
                context.Response.TransmitFile(aberto.FullName);
                context.Response.End();
            }
        }

Com isso, todos os vídeos funcionaram em todos os dispositivos que testei, todo o re-download de todos os arquivos de vídeo toda vez que ele foi pausado ou em uso não está mais acontecendo e os dispositivos móveis podem reproduzi-los.

    
por 20.01.2018 / 02:03