Comprensión de los algoritmos de selección de bloques de ubicación y servidores de Nginx

Introducción

Índice
  1. Introducción
  • Configuraciones de bloques de Nginx
  • Cómo decide Nginx qué bloque de servidor manejará una solicitud
    1. Analizar la listendirectiva para encontrar posibles coincidencias
    2. Analizando la server_namedirectiva para elegir una coincidencia
    3. Ejemplos
  • Bloques de ubicación coincidentes
    1. Sintaxis del bloque de ubicación
    2. Ejemplos que demuestran la sintaxis del bloque de ubicación
    3. Cómo elige Nginx qué ubicación utilizar para gestionar las solicitudes
    4. ¿Cuándo la evaluación del bloque de ubicación salta a otras ubicaciones?
  • Conclusión
  • Nginx es uno de los servidores web más populares del mundo. Puede gestionar con éxito cargas elevadas con muchas conexiones de clientes simultáneas y puede funcionar como servidor web, servidor de correo o servidor proxy inverso.

    En esta guía, analizaremos algunos de los detalles que determinan cómo Nginx procesa las solicitudes de los clientes. Comprender estas ideas puede ayudar a eliminar las conjeturas a la hora de diseñar bloques de ubicación y servidor, y puede hacer que el manejo de las solicitudes parezca menos impredecible.

    Configuraciones de bloques de Nginx

    Nginx divide de forma lógica las configuraciones destinadas a ofrecer distintos contenidos en bloques, que se encuentran en una estructura jerárquica. Cada vez que se realiza una solicitud de un cliente, Nginx inicia un proceso para determinar qué bloques de configuración se deben utilizar para gestionar la solicitud. Este proceso de decisión es lo que analizaremos en esta guía.

    Los bloques principales que discutiremos son el bloque de servidor y el bloque de ubicación .

    Un bloque de servidor es un subconjunto de la configuración de Nginx que define un servidor virtual que se utiliza para gestionar solicitudes de un tipo determinado. Los administradores suelen configurar varios bloques de servidor y decidir qué bloque debe gestionar qué conexión en función del nombre de dominio, el puerto y la dirección IP solicitados.

    Un bloque de ubicación se encuentra dentro de un bloque de servidor y se utiliza para definir cómo Nginx debe manejar las solicitudes de diferentes recursos y URI para el servidor principal. El espacio de URI se puede subdividir de la forma que desee el administrador utilizando estos bloques. Es un modelo extremadamente flexible.

    Cómo decide Nginx qué bloque de servidor manejará una solicitud

    Dado que Nginx permite al administrador definir múltiples bloques de servidor que funcionan como instancias de servidor web virtual independientes, necesita un procedimiento para determinar cuál de estos bloques de servidor se utilizará para satisfacer una solicitud.

    Esto se logra mediante un sistema definido de comprobaciones que se utilizan para encontrar la mejor coincidencia posible. Las principales directivas de bloque de servidor que Nginx tiene en cuenta durante este proceso son la listendirectiva y la server_namedirectiva.

    Analizar la listendirectiva para encontrar posibles coincidencias

    En primer lugar, Nginx analiza la dirección IP y el puerto de la solicitud. Los compara con la listendirectiva de cada servidor para crear una lista de los bloques de servidor que posiblemente puedan resolver la solicitud.

    La listendirectiva generalmente define a qué dirección IP y puerto responderá el bloque de servidor. De manera predeterminada, a cualquier bloque de servidor que no incluya una listendirectiva se le asignan los parámetros de escucha de 0.0.0.0:80(o 0.0.0.0:8080si Nginx está siendo ejecutado por un usuario normal que no sea root ). Esto permite que estos bloques respondan a las solicitudes en cualquier interfaz en el puerto 80, pero este valor predeterminado no tiene mucho peso dentro del proceso de selección del servidor.

    La listendirectiva se puede configurar para:

    • Una combinación de dirección IP y puerto.
    • Una dirección IP única que luego escuchará en el puerto predeterminado 80.
    • Un puerto solitario que escuchará todas las interfaces en ese puerto.
    • La ruta a un socket Unix.

    La última opción generalmente sólo tendrá implicaciones al pasar solicitudes entre diferentes servidores.

    Al intentar determinar a qué bloque de servidor enviar una solicitud, Nginx primero intentará decidir según la especificidad de la listendirectiva utilizando las siguientes reglas:

    • Nginx traduce todas las directivas “incompletas” listensustituyendo los valores faltantes por sus valores predeterminados para que cada bloque pueda evaluarse por su dirección IP y puerto. Algunos ejemplos de estas traducciones son:
      • Un bloque sin listendirectiva utiliza el valor 0.0.0.0:80.
      • Un bloque establecido en una dirección IP 111.111.111.111sin puerto se convierte en111.111.111.111:80
      • Un bloque configurado en un puerto 8888sin dirección IP se convierte en0.0.0.0:8888
    • Luego, Nginx intenta recopilar una lista de los bloques de servidor que coinciden con la solicitud de manera más específica en función de la dirección IP y el puerto. Esto significa que cualquier bloque que esté utilizando funcionalmente 0.0.0.0su dirección IP (para que coincida con cualquier interfaz) no se seleccionará si hay bloques coincidentes que incluyan una dirección IP específica. En cualquier caso, el puerto debe coincidir exactamente.
    • Si solo hay una coincidencia más específica, se utilizará ese bloque de servidor para atender la solicitud. Si hay varios bloques de servidor con el mismo nivel de coincidencia de especificidad, Nginx comienza a evaluar la server_namedirectiva de cada bloque de servidor.

    Es importante entender que Nginx solo evaluará la server_namedirectiva cuando necesite distinguir entre bloques de servidor que coincidan con el mismo nivel de especificidad en la listendirectiva. Por ejemplo, si example.comestá alojado en el puerto 80de 192.168.1.10, una solicitud de example.comsiempre será atendida por el primer bloque en este ejemplo, a pesar de la server_namedirectiva en el segundo bloque.

    server {    listen 192.168.1.10;    . . .}server {    listen 80;    server_name example.com;    . . .}

    En el caso de que más de un bloque de servidor coincida con la misma especificidad, el siguiente paso es verificar la server_namedirectiva.

    Analizando la server_namedirectiva para elegir una coincidencia

    A continuación, para evaluar más a fondo las solicitudes que tienen listendirectivas igualmente específicas, Nginx verifica el Hostencabezado de la solicitud. Este valor contiene el dominio o la dirección IP a la que el cliente realmente intentaba acceder.

    Nginx intenta encontrar la mejor coincidencia para el valor que encuentra al observar la server_namedirectiva dentro de cada uno de los bloques de servidor que aún son candidatos para la selección. Nginx los evalúa utilizando la siguiente fórmula:

    • Nginx primero intentará encontrar un bloque de servidor con un valor que coincida exactamenteserver_name con el del Hostencabezado de la solicitud . Si lo encuentra, se utilizará el bloque asociado para atender la solicitud. Si se encuentran varias coincidencias exactas, se utilizará la primera .
    • Si no se encuentra una coincidencia exacta, Nginx intentará encontrar un bloque de servidor con un server_nameque coincida utilizando un comodín inicial (indicado por un *al comienzo del nombre en la configuración). Si se encuentra uno, ese bloque se utilizará para atender la solicitud. Si se encuentran múltiples coincidencias, se utilizará la coincidencia más larga para atender la solicitud.
    • Si no se encuentra ninguna coincidencia con un comodín inicial, Nginx busca un bloque de servidor con un server_nameque coincida con un comodín final (indicado por un nombre de servidor que termina con un *en la configuración). Si se encuentra uno, ese bloque se utiliza para atender la solicitud. Si se encuentran varias coincidencias, se utilizará la coincidencia más larga para atender la solicitud.
    • Si no se encuentra ninguna coincidencia utilizando un comodín final, Nginx evalúa los bloques de servidor que definen el server_nameuso de expresiones regulares (indicadas por un ~antes del nombre). El primero server_name con una expresión regular que coincida con el encabezado “Host” se utilizará para atender la solicitud.
    • Si no se encuentra ninguna coincidencia de expresión regular, Nginx selecciona el bloque de servidor predeterminado para esa dirección IP y puerto.

    Cada combinación de dirección IP/puerto tiene un bloque de servidor predeterminado que se utilizará cuando no se pueda determinar un curso de acción con los métodos anteriores. Para una combinación de dirección IP/puerto, este será el primer bloque en la configuración o el bloque que contiene la default_serveropción como parte de la listendirectiva (que anularía el algoritmo de primer hallazgo). Solo puede haber una default_serverdeclaración por cada combinación de dirección IP/puerto.

    Ejemplos

    Si hay un server_namedefinido que coincide exactamente con el Hostvalor del encabezado, se selecciona ese bloque de servidor para procesar la solicitud.

    En este ejemplo, si el Hostencabezado de la solicitud se estableciera en host1.example.com, se seleccionaría el segundo servidor:

    server {    listen 80;    server_name *.example.com;    . . .}server {    listen 80;    server_name host1.example.com;    . . .}

    Si no se encuentra una coincidencia exacta, Nginx verifica si hay una server_namecon un comodín inicial que encaje. Se seleccionará la coincidencia más larga que comience con un comodín para cumplir con la solicitud.

    En este ejemplo, si la solicitud tuviera un Hostencabezado de www.example.org, se seleccionaría el segundo bloque de servidor:

    server {    listen 80;    server_name www.example.*;    . . .}server {    listen 80;    server_name *.example.org;    . . .}server {    listen 80;    server_name *.org;    . . .}

    Si no se encuentra ninguna coincidencia con un comodín inicial, Nginx comprobará si existe una coincidencia utilizando un comodín al final de la expresión. En este punto, se seleccionará la coincidencia más larga que termine con un comodín para atender la solicitud.

    Por ejemplo, si la solicitud tiene un Hostencabezado establecido en www.example.com, se seleccionará el tercer bloque de servidor:

    server {    listen 80;    server_name host1.example.com;    . . .}server {    listen 80;    server_name example.com;    . . .}server {    listen 80;    server_name www.example.*;    . . .}

    Si no se encuentran coincidencias con comodines, Nginx intentará buscar server_namedirectivas que utilicen expresiones regulares. Se seleccionará la primera expresión regular coincidente para responder a la solicitud.

    Por ejemplo, si el Hostencabezado de la solicitud se establece en www.example.com, se seleccionará el segundo bloque de servidor para satisfacer la solicitud:

    server {    listen 80;    server_name example.com;    . . .}server {    listen 80;    server_name ~^(www|host1).*.example.com$;    . . .}server {    listen 80;    server_name ~^(subdomain|set|www|host1).*.example.com$;    . . .}

    Si ninguno de los pasos anteriores puede satisfacer la solicitud, esta se pasará al servidor predeterminado para la dirección IP y el puerto correspondientes.

    Bloques de ubicación coincidentes

    Similar al proceso que Nginx utiliza para seleccionar el bloque de servidor que procesará una solicitud, Nginx también tiene un algoritmo establecido para decidir qué bloque de ubicación dentro del servidor utilizar para manejar las solicitudes.

    Sintaxis del bloque de ubicación

    Antes de explicar cómo Nginx decide qué bloque de ubicación utilizar para gestionar las solicitudes, repasemos parte de la sintaxis que puede ver en las definiciones de bloques de ubicación. Los bloques de ubicación se encuentran dentro de los bloques de servidor (u otros bloques de ubicación) y se utilizan para decidir cómo procesar la URI de la solicitud (la parte de la solicitud que viene después del nombre de dominio o la dirección IP/puerto).

    Los bloques de ubicación generalmente toman la siguiente forma:

    location optional_modifier location_match {    . . .}

    En location_matchel ejemplo anterior se define qué debe comprobar Nginx en la URI de la solicitud. La existencia o no del modificador en el ejemplo anterior afecta la forma en que Nginx intenta hacer coincidir el bloque de ubicación. Los modificadores a continuación harán que el bloque de ubicación asociado se interprete de la siguiente manera:

    • (ninguno) : si no hay modificadores, la ubicación se interpreta como una coincidencia de prefijo . Esto significa que la ubicación proporcionada se comparará con el comienzo de la URI de la solicitud para determinar una coincidencia.
    • =:Si se utiliza un signo igual, este bloque se considerará una coincidencia si el URI de la solicitud coincide exactamente con la ubicación indicada.
    • ~:Si hay un modificador tilde, esta ubicación se interpretará como una coincidencia de expresión regular que distingue entre mayúsculas y minúsculas.
    • ~*:Si se utiliza un modificador de tilde y asterisco, el bloque de ubicación se interpretará como una coincidencia de expresión regular que no distingue entre mayúsculas y minúsculas.
    • ^~:Si hay un modificador de tilde y quilate, y si se selecciona este bloque como la mejor coincidencia de expresión no regular, no se realizará la coincidencia de expresión regular.

    Ejemplos que demuestran la sintaxis del bloque de ubicación

    Como ejemplo de coincidencia de prefijo, se puede seleccionar el siguiente bloque de ubicación para responder a las URI de solicitud que se parecen a /site, /site/page1/index.htmlo /site/index.html:

    location /site {    . . .}

    Para demostrar la coincidencia exacta de la URL de solicitud, este bloque siempre se utilizará para responder a una URL de solicitud que parezca /page1. No se utilizará para responder a una /page1/index.htmlURL de solicitud. Tenga en cuenta que si se selecciona este bloque y la solicitud se completa mediante una página de índice, se realizará una redirección interna a otra ubicación que será el controlador real de la solicitud:

    location = /page1 {    . . .}

    Como ejemplo de una ubicación que debe interpretarse como una expresión regular que distingue entre mayúsculas y minúsculas, este bloque podría usarse para manejar solicitudes de /tortoise.jpg, pero no de /FLOWER.PNG:

    location ~ .(jpe?g|png|gif|ico)$ {    . . .}

    A continuación se muestra un bloque que permitiría una coincidencia sin distinción entre mayúsculas y minúsculas similar al anterior. Aquí, este bloque podría manejar tanto /tortoise.jpg y como: /FLOWER.PNG

    location ~* .(jpe?g|png|gif|ico)$ {    . . .}

    Por último, este bloque evitaría que se produzcan coincidencias de expresiones regulares si se determina que son las mejores coincidencias de expresiones no regulares. Podría gestionar solicitudes de /costumes/ninja.html:

    location ^~ /costumes {    . . .}

    Como puede ver, los modificadores indican cómo se debe interpretar el bloque de ubicación. Sin embargo, esto no nos indica el algoritmo que Nginx utiliza para decidir a qué bloque de ubicación enviar la solicitud. Lo repasaremos a continuación.

    Cómo elige Nginx qué ubicación utilizar para gestionar las solicitudes

    Nginx elige la ubicación que se utilizará para atender una solicitud de forma similar a como selecciona un bloque de servidor. Ejecuta un proceso que determina el mejor bloque de ubicación para cualquier solicitud determinada. Comprender este proceso es un requisito fundamental para poder configurar Nginx de forma fiable y precisa.

    Teniendo en cuenta los tipos de declaraciones de ubicación que describimos anteriormente, Nginx evalúa los posibles contextos de ubicación comparando la URI de la solicitud con cada una de las ubicaciones. Para ello, utiliza el siguiente algoritmo:

    • Nginx comienza por verificar todas las coincidencias de ubicación basadas en prefijos (todos los tipos de ubicación que no involucran una expresión regular). Verifica cada ubicación con la URL de solicitud completa.
    • En primer lugar, Nginx busca una coincidencia exacta. Si =se encuentra un bloque de ubicación que utiliza el modificador que coincide exactamente con la URL de la solicitud, este bloque de ubicación se selecciona inmediatamente para atender la solicitud.
    • Si no se encuentran coincidencias de bloques de ubicación exactas (con el =modificador), Nginx pasa a evaluar prefijos no exactos. Descubre la ubicación de prefijo coincidente más larga para la URI de solicitud dada, que luego evalúa de la siguiente manera:
      • Si la ubicación del prefijo coincidente más largo tiene el ^~modificador, entonces Nginx finalizará inmediatamente su búsqueda y seleccionará esta ubicación para atender la solicitud.
      • Si la ubicación del prefijo coincidente más largo no utiliza el ^~modificador, Nginx almacena la coincidencia por el momento para poder cambiar el foco de la búsqueda.
    • Una vez que se determina y almacena la ubicación del prefijo coincidente más largo, Nginx pasa a evaluar las ubicaciones de las expresiones regulares (tanto si distinguen entre mayúsculas y minúsculas). Si hay alguna ubicación de expresión regular dentro de la ubicación del prefijo coincidente más largo, Nginx la moverá a la parte superior de su lista de ubicaciones de expresiones regulares para verificar. Luego, Nginx intenta hacer coincidir las ubicaciones de las expresiones regulares de manera secuencial. La primera ubicación de expresión regular que coincide con la URI de la solicitud se selecciona inmediatamente para atender la solicitud.
    • Si no se encuentran ubicaciones de expresiones regulares que coincidan con el URI de la solicitud, se selecciona la ubicación del prefijo almacenado previamente para atender la solicitud.

    Es importante entender que, de forma predeterminada, Nginx ofrecerá coincidencias de expresiones regulares en lugar de coincidencias de prefijos. Sin embargo, evalúa primero las ubicaciones de los prefijos, lo que permite al administrador anular esta tendencia especificando las ubicaciones mediante los modificadores =y ^~.

    También es importante tener en cuenta que, si bien las ubicaciones de prefijo generalmente se seleccionan en función de la coincidencia más larga y específica, la evaluación de expresiones regulares se detiene cuando se encuentra la primera ubicación coincidente. Esto significa que el posicionamiento dentro de la configuración tiene amplias implicaciones para las ubicaciones de expresiones regulares.

    Por último, es importante entender que las coincidencias de expresiones regulares dentro de la coincidencia de prefijo más larga “saltarán la línea” cuando Nginx evalúe las ubicaciones de expresiones regulares. Estas se evaluarán, en orden, antes de que se considere cualquiera de las otras coincidencias de expresiones regulares. Maxim Dounin, un desarrollador de Nginx increíblemente útil, explica en esta publicación esta parte del algoritmo de selección.

    ¿Cuándo la evaluación del bloque de ubicación salta a otras ubicaciones?

    En términos generales, cuando se selecciona un bloque de ubicación para atender una solicitud, la solicitud se procesa completamente dentro de ese contexto a partir de ese momento. Solo la ubicación seleccionada y las directivas heredadas determinan cómo se procesa la solicitud, sin interferencias de los bloques de ubicación hermanos.

    Aunque esta es una regla general que le permitirá diseñar sus bloques de ubicación de una manera predecible, es importante tener en cuenta que hay ocasiones en las que se activa una nueva búsqueda de ubicación mediante ciertas directivas dentro de la ubicación seleccionada. Las excepciones a la regla de “un solo bloque de ubicación” pueden tener implicaciones en la forma en que se atiende realmente la solicitud y pueden no estar alineadas con las expectativas que tenía al diseñar sus bloques de ubicación.

    Algunas directivas que pueden llevar a este tipo de redirección interna son:

    • índice
    • archivos de prueba
    • volver a escribir
    • error_pagina

    Repasemos esto brevemente.

    La indexdirectiva siempre conduce a una redirección interna si se utiliza para gestionar la solicitud. Las coincidencias de ubicación exacta se utilizan a menudo para acelerar el proceso de selección finalizando inmediatamente la ejecución del algoritmo. Sin embargo, si realiza una coincidencia de ubicación exacta que sea un directorio , existe una buena posibilidad de que la solicitud se redirija a una ubicación diferente para su procesamiento real.

    En este ejemplo, la primera ubicación coincide con una URI de solicitud de /exact, pero para manejar la solicitud, la indexdirectiva heredada por el bloque inicia una redirección interna al segundo bloque:

    index index.html;location = /exact {    . . .}location / {    . . .}

    En el caso anterior, si realmente necesita que la ejecución permanezca en el primer bloque, tendrá que idear un método diferente para satisfacer la solicitud al directorio. Por ejemplo, podría establecer un valor inválido indexpara ese bloque y activar autoindex:

    location = /exact {    index nothing_will_match;    autoindex on;}location  / {    . . .}

    Esta es una forma de evitar indexque un usuario cambie de contexto, pero probablemente no sea útil para la mayoría de las configuraciones. En general, una coincidencia exacta de directorios puede ser útil para cosas como reescribir la solicitud (que también da como resultado una nueva búsqueda de ubicación).

    Otro caso en el que se puede reevaluar la ubicación del procesamiento es con la try_filesdirectiva. Esta directiva le indica a Nginx que verifique la existencia de un conjunto de archivos o directorios con nombre. El último parámetro puede ser una URI a la que Nginx realizará una redirección interna.

    Considere la siguiente configuración:

    root /var/www/main;location / {    try_files $uri $uri.html $uri/ /fallback/index.html;}location /fallback {    root /var/www/another;}

    En el ejemplo anterior, si se realiza una solicitud para /blahblah, la primera ubicación recibirá inicialmente la solicitud. Intentará encontrar un archivo llamado blahblahen /var/www/mainel directorio. Si no puede encontrar uno, buscará un archivo llamado blahblah.html. Luego intentará ver si hay un directorio llamado blahblah/dentro del /var/www/maindirectorio. Si fallan todos estos intentos, redireccionará a /fallback/index.html. Esto activará otra búsqueda de ubicación que será capturada por el segundo bloque de ubicación. Esto servirá el archivo /var/www/another/fallback/index.html.

    Otra directiva que puede provocar la pérdida de un bloque de ubicación es la rewritedirectiva . Cuando se utiliza el lastparámetro con la rewritedirectiva, o cuando no se utiliza ningún parámetro, Nginx buscará una nueva ubicación coincidente en función de los resultados de la reescritura.

    Por ejemplo, si modificamos el último ejemplo para incluir una reescritura, podemos ver que la solicitud a veces se pasa directamente a la segunda ubicación sin depender de la try_filesdirectiva:

    root /var/www/main;location / {    rewrite ^/rewriteme/(.*)$ /$1 last;    try_files $uri $uri.html $uri/ /fallback/index.html;}location /fallback {    root /var/www/another;}

    En el ejemplo anterior, una solicitud de /rewriteme/helloserá manejada inicialmente por el primer bloque de ubicación. Se reescribirá en /helloy se buscará una ubicación. En este caso, coincidirá nuevamente con la primera ubicación y será procesada por el try_filescomo de costumbre, tal vez retrocediendo a /fallback/index.htmlsi no se encuentra nada (usando la try_filesredirección interna que analizamos anteriormente).

    Sin embargo, si se realiza una solicitud para /rewriteme/fallback/hello, el primer bloque coincidirá nuevamente. La reescritura se aplicará nuevamente, esta vez con un resultado de /fallback/hello. La solicitud se procesará a partir del segundo bloque de ubicación.

    Una situación similar ocurre con la returndirectiva cuando se envían los códigos de estado 301o 302. La diferencia en este caso es que da como resultado una solicitud completamente nueva en forma de una redirección visible externamente. Esta misma situación puede ocurrir con la rewritedirectiva cuando se utilizan los indicadores redirecto permanent. Sin embargo, estas búsquedas de ubicación no deberían ser inesperadas, ya que las redirecciones visibles externamente siempre dan como resultado una nueva solicitud.

    La error_pagedirectiva puede generar una redirección interna similar a la creada por try_files. Esta directiva se utiliza para definir lo que debe suceder cuando se encuentran determinados códigos de estado. Es probable que esto nunca se ejecute si try_filesse establece , ya que esa directiva maneja todo el ciclo de vida de una solicitud.

    Considere este ejemplo:

    root /var/www/main;location / {    error_page 404 /another/whoops.html;}location /another {    root /var/www;}

    Todas las solicitudes (excepto las que comienzan con /another) serán manejadas por el primer bloque, que servirá los archivos desde /var/www/main. Sin embargo, si no se encuentra un archivo (estado 404), /another/whoops.htmlse producirá una redirección interna a , lo que dará lugar a una nueva búsqueda de ubicación que finalmente aterrizará en el segundo bloque. Este archivo se servirá desde /var/www/another/whoops.html.

    Como puede ver, comprender las circunstancias en las que Nginx activa una nueva búsqueda de ubicación puede ayudar a predecir el comportamiento que verá al realizar solicitudes.

    Conclusión

    Comprender las formas en que Nginx procesa las solicitudes de los clientes puede facilitar mucho su trabajo como administrador. Podrá saber qué bloque de servidor seleccionará Nginx en función de cada solicitud de cliente. También podrá saber cómo se seleccionará el bloque de ubicación en función de la URI de la solicitud. En general, conocer la forma en que Nginx selecciona los diferentes bloques le permitirá rastrear los contextos que aplicará Nginx para atender cada solicitud.

    SUSCRÍBETE A NUESTRO BOLETÍN 
    No te pierdas de nuestro contenido ni de ninguna de nuestras guías para que puedas avanzar en los juegos que más te gustan.

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

    Subir

    Este sitio web utiliza cookies para mejorar tu experiencia mientras navegas por él. Este sitio web utiliza cookies para mejorar tu experiencia de usuario. Al continuar navegando, aceptas su uso. Mas informacion