CursosEmpresasBlogLiveConfPrecios

Validación de la respuesta en el servidor

Clase 5 de 12 • Curso de Gestión de Servidores con Rust

Contenido del curso

Introducción

  • 1
    Qué aprenderemos

    Qué aprenderemos

    01:39 min
  • 2
    Ambiente de trabajo

    Ambiente de trabajo

    01:03 min

Protocolos TCP y HTTP

  • 3
    Conexión TCP

    Conexión TCP

    03:24 min
  • 4
    Conexión HTTP

    Conexión HTTP

    02:38 min
  • 5
    Validación de la respuesta en el servidor

    Validación de la respuesta en el servidor

    Viendo ahora

Concurrencia

  • 6
    Uso de concurrencia en Rust

    Uso de concurrencia en Rust

    01:34 min
  • 7
    Creación de hilos con Rust

    Creación de hilos con Rust

    02:19 min
  • 8
    Traits, Metodos y Mutex para concurrencia

    Traits, Metodos y Mutex para concurrencia

    03:21 min

Creación de un Servidor web multiprocesos

  • 9
    Mejora de nuestro servidor web con multitareas: ThreadPool

    Mejora de nuestro servidor web con multitareas: ThreadPool

    02:46 min
  • 10
    Configurando nuestro Threadpool

    Configurando nuestro Threadpool

    03:12 min

Limpieza del servidor

  • 11
    Uso de Drop

    Uso de Drop

    01:18 min

Hacerlo de la manera fácil

  • 12
    Uso de frameworks y crates

    Uso de frameworks y crates

    02:02 min
Tomar examen

Escuelas

  • Desarrollo Web
    • Fundamentos del Desarrollo Web Profesional
    • Diseño y Desarrollo Frontend
    • Desarrollo Frontend con JavaScript
    • Desarrollo Frontend con Vue.js
    • Desarrollo Frontend con Angular
    • Desarrollo Frontend con React.js
    • Desarrollo Backend con Node.js
    • Desarrollo Backend con Python
    • Desarrollo Backend con Java
    • Desarrollo Backend con PHP
    • Desarrollo Backend con Ruby
    • Bases de Datos para Web
    • Seguridad Web & API
    • Testing Automatizado y QA para Web
    • Arquitecturas Web Modernas y Escalabilidad
    • DevOps y Cloud para Desarrolladores Web
  • English Academy
    • Inglés Básico A1
    • Inglés Básico A2
    • Inglés Intermedio B1
    • Inglés Intermedio Alto B2
    • Inglés Avanzado C1
    • Inglés para Propósitos Específicos
    • Inglés de Negocios
  • Marketing Digital
    • Fundamentos de Marketing Digital
    • Marketing de Contenidos y Redacción Persuasiva
    • SEO y Posicionamiento Web
    • Social Media Marketing y Community Management
    • Publicidad Digital y Paid Media
    • Analítica Digital y Optimización (CRO)
    • Estrategia de Marketing y Growth
    • Marketing de Marca y Comunicación Estratégica
    • Marketing para E-commerce
    • Marketing B2B
    • Inteligencia Artificial Aplicada al Marketing
    • Automatización del Marketing
    • Marca Personal y Marketing Freelance
    • Ventas y Experiencia del Cliente
    • Creación de Contenido para Redes Sociales
  • Inteligencia Artificial y Data Science
    • Fundamentos de Data Science y AI
    • Análisis y Visualización de Datos
    • Machine Learning y Deep Learning
    • Data Engineer
    • Inteligencia Artificial para la Productividad
    • Desarrollo de Aplicaciones con IA
    • AI Software Engineer
  • Ciberseguridad
    • Fundamentos de Ciberseguridad
    • Hacking Ético y Pentesting (Red Team)
    • Análisis de Malware e Ingeniería Forense
    • Seguridad Defensiva y Cumplimiento (Blue Team)
    • Ciberseguridad Estratégica
  • Liderazgo y Habilidades Blandas
    • Fundamentos de Habilidades Profesionales
    • Liderazgo y Gestión de Equipos
    • Comunicación Avanzada y Oratoria
    • Negociación y Resolución de Conflictos
    • Inteligencia Emocional y Autogestión
    • Productividad y Herramientas Digitales
    • Gestión de Proyectos y Metodologías Ágiles
    • Desarrollo de Carrera y Marca Personal
    • Diversidad, Inclusión y Entorno Laboral Saludable
    • Filosofía y Estrategia para Líderes
  • Diseño de Producto y UX
    • Fundamentos de Diseño UX/UI
    • Investigación de Usuarios (UX Research)
    • Arquitectura de Información y Usabilidad
    • Diseño de Interfaces y Prototipado (UI Design)
    • Sistemas de Diseño y DesignOps
    • Redacción UX (UX Writing)
    • Creatividad e Innovación en Diseño
    • Diseño Accesible e Inclusivo
    • Diseño Asistido por Inteligencia Artificial
    • Gestión de Producto y Liderazgo en Diseño
    • Diseño de Interacciones Emergentes (VUI/VR)
    • Desarrollo Web para Diseñadores
    • Diseño y Prototipado No-Code
  • Contenido Audiovisual
    • Fundamentos de Producción Audiovisual
    • Producción de Video para Plataformas Digitales
    • Producción de Audio y Podcast
    • Fotografía y Diseño Gráfico para Contenido Digital
    • Motion Graphics y Animación
    • Contenido Interactivo y Realidad Aumentada
    • Estrategia, Marketing y Monetización de Contenidos
  • Desarrollo Móvil
    • Fundamentos de Desarrollo Móvil
    • Desarrollo Nativo Android con Kotlin
    • Desarrollo Nativo iOS con Swift
    • Desarrollo Multiplataforma con React Native
    • Desarrollo Multiplataforma con Flutter
    • Arquitectura y Patrones de Diseño Móvil
    • Integración de APIs y Persistencia Móvil
    • Testing y Despliegue en Móvil
    • Diseño UX/UI para Móviles
  • Diseño Gráfico y Arte Digital
    • Fundamentos del Diseño Gráfico y Digital
    • Diseño de Identidad Visual y Branding
    • Ilustración Digital y Arte Conceptual
    • Diseño Editorial y de Empaques
    • Motion Graphics y Animación 3D
    • Diseño Gráfico Asistido por Inteligencia Artificial
    • Creatividad e Innovación en Diseño
  • Programación
    • Fundamentos de Programación e Ingeniería de Software
    • Herramientas de IA para el trabajo
    • Matemáticas para Programación
    • Programación con Python
    • Programación con JavaScript
    • Programación con TypeScript
    • Programación Orientada a Objetos con Java
    • Desarrollo con C# y .NET
    • Programación con PHP
    • Programación con Go y Rust
    • Programación Móvil con Swift y Kotlin
    • Programación con C y C++
    • Administración Básica de Servidores Linux
  • Negocios
    • Fundamentos de Negocios y Emprendimiento
    • Estrategia y Crecimiento Empresarial
    • Finanzas Personales y Corporativas
    • Inversión en Mercados Financieros
    • Ventas, CRM y Experiencia del Cliente
    • Operaciones, Logística y E-commerce
    • Gestión de Proyectos y Metodologías Ágiles
    • Aspectos Legales y Cumplimiento
    • Habilidades Directivas y Crecimiento Profesional
    • Diversidad e Inclusión en el Entorno Laboral
    • Herramientas Digitales y Automatización para Negocios
  • Blockchain y Web3
    • Fundamentos de Blockchain y Web3
    • Desarrollo de Smart Contracts y dApps
    • Finanzas Descentralizadas (DeFi)
    • NFTs y Economía de Creadores
    • Seguridad Blockchain
    • Ecosistemas Blockchain Alternativos (No-EVM)
    • Producto, Marketing y Legal en Web3
  • Recursos Humanos
    • Fundamentos y Cultura Organizacional en RRHH
    • Atracción y Selección de Talento
    • Cultura y Employee Experience
    • Gestión y Desarrollo de Talento
    • Desarrollo y Evaluación de Liderazgo
    • Diversidad, Equidad e Inclusión
    • AI y Automatización en Recursos Humanos
    • Tecnología y Automatización en RRHH
  • Finanzas e Inversiones
    • Fundamentos de Finanzas Personales y Corporativas
    • Análisis y Valoración Financiera
    • Inversión y Mercados de Capitales
    • Finanzas Descentralizadas (DeFi) y Criptoactivos
    • Finanzas y Estrategia para Startups
    • Inteligencia Artificial Aplicada a Finanzas
    • Domina Excel
    • Financial Analyst
    • Conseguir trabajo en Finanzas e Inversiones
  • Startups
    • Fundamentos y Validación de Ideas
    • Estrategia de Negocio y Product-Market Fit
    • Desarrollo de Producto y Operaciones Lean
    • Finanzas, Legal y Fundraising
    • Marketing, Ventas y Growth para Startups
    • Cultura, Talento y Liderazgo
    • Finanzas y Operaciones en Ecommerce
    • Startups Web3 y Blockchain
    • Startups con Impacto Social
    • Expansión y Ecosistema Startup
  • Cloud Computing y DevOps
    • Fundamentos de Cloud y DevOps
    • Administración de Servidores Linux
    • Contenerización y Orquestación
    • Infraestructura como Código (IaC) y CI/CD
    • Amazon Web Services
    • Microsoft Azure
    • Serverless y Observabilidad
    • Certificaciones Cloud (Preparación)
    • Plataforma Cloud GCP

Platzi y comunidad

  • Platzi Business
  • Live Classes
  • Lanzamientos
  • Executive Program
  • Trabaja con nosotros
  • Podcast

Recursos

  • Manual de Marca

Soporte

  • Preguntas Frecuentes
  • Contáctanos

Legal

  • Términos y Condiciones
  • Privacidad
  • Tyc promociones
Reconocimientos
Reconocimientos
Logo reconocimientoTop 40 Mejores EdTech del mundo · 2024
Logo reconocimientoPrimera Startup Latina admitida en YC · 2014
Logo reconocimientoPrimera Startup EdTech · 2018
Logo reconocimientoCEO Ganador Medalla por la Educación T4 & HP · 2024
Logo reconocimientoCEO Mejor Emprendedor del año · 2024
De LATAM conpara el mundo
YoutubeInstagramLinkedInTikTokFacebookX (Twitter)Threads

      ¿Qué hemos realizado hasta el momento?

      1. Hemos aprendido a crear una conexión TCP.
      2. Logramos enviar y leer una solicitud HTTP.
      3. Logramos tener una vista al momento que nuestro servidor está activo.
      4. Hemos aprendido a enviar status 200 por el navegador.

      Para tener nuestro servidor completo, necesitamos otro status que podría ayudarnos a validar si no obtenemos respuesta a nuestra solicitud. El tan temido 404 Not Found. Para esto ya sabemos como leer archivos html dentro de nuestra función principal, ahora debemos crear una nueva interfaz mostrando mensaje de error.

      Haremos una modificación a nuestro código, esta vez usaremos ìf y else para manejar nuestras dos peticiones. Al código que ya hemos venido trabajando, nos posicionamos en nuestra función handle_connection, ahí es donde crearemos nuestras condiciones.

      Agregaremos la siguiente línea a nuestro código:

      let get = b"GET / HTTP/1.1\r\n";

      Primero, agregamos la respuesta de nuestra solicitud en la una variable llamada get. Debido a que estamos leyendo bytes sin procesar en el buffer, transformamos get en una cadena de bytes agregando la sintaxis de cadena de bytes "b" al comienzo de la respuesta.

      Ahora sí, comenzamos con nuestra condición:

      if buffer.starts_with(get) {}

      Esto significa que si buffer recibe una respuesta en get y nuestra condición se cumple, quiere decir que tendremos que arrojar nuestro mensaje con el status 200 de petición exitosa.

      let contents = fs::read_to_string("hello.html").unwrap(); let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", contents); stream.write(response.as_bytes()).unwrap(); stream.flush().unwrap();

      Pero en caso de que esta condición no se llegara a cumplir, es decir, que se está enviando otro tipo de petición y no nuestra dirección, por ejemplo: `127.0.0.1:7373/algo´ esto debería arrojarnos un mensaje de error en nuestro navegador.

      Para esto, primero crearemos un archivo html que nos muestre un mensaje de error en mi caso es 404.html.

      <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Hola</title> </head> <body> <h1 style="text-align:center;">¡Oops!</h1> <h1 style="text-align:center;">Lamento el inconveniente, pero no encuentro la página que solicitas</h1> <img src="https://www.rust-lang.org/logos/error.png" style="display:block; margin:auto;" width="600" height="500" /> </body> </html>

      Ya que tenemos nuestro mensaje de error para mostrar en el navegador, creamos nuestra respuesta en nuestra condicionante:

      } else { let status_line = "HTTP/1.1 404 NOT FOUND\r\n\r\n"; let contents = fs::read_to_string("404.html").unwrap(); let response = format!("{}{}", status_line, contents); stream.write(response.as_bytes()).unwrap(); stream.flush().unwrap(); }

      Si revisas, lo único que llega a cambiar es nuestra variable response que ahora es status_line donde recibe mensaje de error a la petición, y ahora leemos nuestro archivo 404.htmlen lugar de nuestro archivo de bienvenida.

      Es hora de que ejecutes cargo run y veas los resultados.

      Ya tenemos un servidor que puede enviar y recibir una sola petición, eso es un gran avance para ser nuestra 4ta clase. En las siguientes clases aumentaremos la dificultad creando un servidor que reciba varias peticiones a la vez.

      Es hora de que juegues con tu código, de qué manera crees que pueda ser refactorizado, también puedes agregar control de errores para que te sea más fácil identificarlos.

      Por el momento, deja en los comentarios el mensaje de error 404 que diseñaste.

      Comentarios

        Mitchell Sierralta Valdivia

        Mitchell Sierralta Valdivia

        student•
        hace 5 años

        no es mucho pero es trabajo honesto

        use std::net::{TcpListener, TcpStream}; use std::io::prelude::*; use std::fs; fn main() { let listener = TcpListener::bind("127.0.0.1:7373").unwrap(); for stream in listener.incoming(){ let stream2 = stream.unwrap(); handle_connection(stream2) } } fn handle_connection(mut stream: TcpStream) { let mut buffer = [0; 1024]; let get = b"GET / HTTP/1.1\r\n"; stream.read(&mut buffer).unwrap(); if buffer.starts_with(get){ let status_line = "HTTP/1.1 200 OK\r\n\r\n"; web_response(stream, "html/index.html".to_string(), status_line.to_string()); } else{ let status_line = "HTTP/1.1 404 NOT FOUND\r\n\r\n"; web_response(stream, "html/404.html".to_string(), status_line.to_string()); } } fn web_response(mut stream : TcpStream, content : String, status_line : String){ let contents = fs::read_to_string(content).unwrap(); let response = format!("{}\r\n\r\n{}", status_line, contents); stream.write(response.as_bytes()).unwrap(); stream.flush().unwrap(); // espera a que se escriban todos los datos. }
        Aitor Zaldua

        Aitor Zaldua

        student•
        hace 5 años

        El código completo va quedando así...

        Untitled.png

        Ya muestra el index.html para el localhost:7373 y el 404.html para cualquier petición al localhost:7373/<loquesea>

        Osvaldo Frias

        Osvaldo Frias

        student•
        hace 5 años

        Refactor :3

        Screenshot_2.png
          Gerardo Alberto Soto Alvarez del Castillo

          Gerardo Alberto Soto Alvarez del Castillo

          student•
          hace 5 años

          Hola @Osvaldo, una pregunta, ¿Porqué muestras la longitud del contenido? ¿tiene algún propósito, o es por protocolo? Gracias.

          Osvaldo Frias

          Osvaldo Frias

          student•
          hace 5 años

          Sí, es parte de la respuesta HTTP. En el RFC 2616 sección 14.13 se especifica el uso del atributo Content-Length.

        Mario Andrés Castro Martínez

        Mario Andrés Castro Martínez

        student•
        hace 5 años

        Hola!! El refactor:

        codeRust.png

        El error 404:

        404.png

        Pablo Aquino

        Pablo Aquino

        student•
        hace 5 años

        New Code

        use std::net::{TcpListener, TcpStream}; use std::io::prelude::*; use std::fs; fn main() { let listener=TcpListener::bind("127.0.0.1:7373").unwrap(); for stream in listener.incoming(){ let stream=stream.unwrap(); handle_connection(stream); } } fn handle_connection(mut stream: TcpStream) { let mut buffer=[0;256]; let get=b"GET / HTTP/1.1\r\n"; stream.read(&mut buffer).unwrap(); //println!("Request: {}", String::from_utf8_lossy(&buffer[..])); if buffer.starts_with(get) { let contents=fs::read_to_string("index.html").unwrap(); let response=format!("HTTP/1.1 200 OK\r\n\r\n{}", contents); stream.write(response.as_bytes()).unwrap(); //Nos ayuda a leer la cadena bytes que estamos recibiendo. stream.flush().unwrap(); //Esperará e impedirá que el programa continúe hasta que se escriban todos los bytes en la conexión. } else { let status_line="HTTP/1.1 400 NOT FOUND\r\n\r\n"; let contents=fs::read_to_string("404.html").unwrap(); let response=format!("{}{}",status_line,contents); stream.write(response.as_bytes()).unwrap(); stream.flush().unwrap(); } }
        Javier Guevara

        Javier Guevara

        student•
        hace 5 años

        Mi código

        use std::net::{TcpListener, TcpStream}; use std::io::prelude::*; use std::fs; fn main() { let listener = TcpListener::bind("127.0.0.1:7373").unwrap(); for stream in listener.incoming() { let stream = stream.unwrap(); handle_connection(stream); } } fn handle_connection(mut stream: TcpStream) { let get = b"GET / HTTP/1.1 200\r\n"; let mut buffer = [0; 1024]; stream.read(&mut buffer).unwrap(); if buffer.starts_with(get) { let status_line = "HTTP/1.1 200 OK\r\n\r\n"; let content = fs::read_to_string("index.html").unwrap(); web_response(stream, content, status_line.to_string()); } else { let status_line = "HTTP/1.1 404 NOT FOUND\r\n\r\n"; let content = fs::read_to_string("404.html").unwrap(); web_response(stream, content, status_line.to_string()); } } fn web_response(mut stream : TcpStream, content : String, status_line : String){ let response = format!("{}\r\n\r\n{}", status_line, content); stream.write(response.as_bytes()).unwrap(); stream.flush().unwrap(); // espera a que se escriban todos los datos. }
        Roberto Antonio Berrospe Machin

        Roberto Antonio Berrospe Machin

        student•
        hace 5 años

        Código...

        use std::fs; use std::io::Read; use std::io::Write; use std::net::{TcpListener, TcpStream}; fn main() { let listener = TcpListener::bind("127.0.0.1:7373").unwrap(); for stream in listener.incoming() { let stream = stream.unwrap(); handle_connection(stream); } } fn handle_connection(mut stream: TcpStream) { let mut buffer = [0; 256]; stream.read(&mut buffer).unwrap(); println!("Request: {}", String::from_utf8_lossy(&buffer[..])); let get_root = b"GET / HTTP/1.1\r\n"; let get_indx = b"GET /index.html HTTP/1.1\r\n"; if buffer.starts_with(get_root) || buffer.starts_with(get_indx) { let contents = fs::read_to_string("index.html").unwrap(); let response = format!( "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}", contents.len(), contents ); stream.write(response.as_bytes()).unwrap(); } else { let contents = fs::read_to_string("404.html").unwrap(); let response = format!( "HTTP/1.1 404 NOT FOUND\r\nContent-Length: {}\r\n\r\n{}", contents.len(), contents ); stream.write(response.as_bytes()).unwrap(); } stream.flush().unwrap(); }
        Gerardo Alberto Soto Alvarez del Castillo

        Gerardo Alberto Soto Alvarez del Castillo

        student•
        hace 5 años

        Les comparto una lista de los códigos más comunes del protocolo HTTP: Fuente: https://es.wikipedia.org/wiki/Anexo:C%C3%B3digos_de_estado_HTTP#1xx:_Respuestas_informativas

        1xx Respuestas informativas 100 Continue El navegador puede continuar realizando su petición (se utiliza para indicar que la primera parte de la petición del navegador se ha recibido correctamente).2​ 101 Switching Protocols El servidor acepta el cambio de protocolo propuesto por el navegador (puede ser por ejemplo un cambio de HTTP 1.0 a HTTP 1.1).

        2xx Peticiones correctas 200 OK Respuesta estándar para peticiones correctas. 201 Created La petición ha sido completada y ha resultado en la creación de un nuevo recurso.

        3xx Redirecciones 300 Multiple Choices Indica opciones múltiples para el URI que el cliente podría seguir. Esto podría ser utilizado, por ejemplo, para presentar distintas opciones de formato para video, listar archivos con distintas extensiones o word sense disambiguation.

        4xx Errores del cliente :( 400 Bad Request El servidor no procesará la solicitud, porque no puede, o no debe, debido a algo que es percibido como un error del cliente (ej: solicitud malformada, sintaxis errónea, etc). La solicitud contiene sintaxis errónea y no debería repetirse. 401 Unauthorized4​ Similar al 403 Forbidden, pero específicamente para su uso cuando la autentificación es posible pero ha fallado o aún no ha sido provista. Vea autenticación HTTP básica y Digest access authentication. 402 Payment Required La intención original era que este código pudiese ser usado como parte de alguna forma o esquema de Dinero electrónico o micropagos, pero eso no sucedió, y este código nunca se utilizó. 403 Forbidden La solicitud fue legal, pero el servidor rehúsa responderla dado que el cliente no tiene los privilegios para realizarla. En contraste a una respuesta 401 No autorizado, autenticarse previamente no va a cambiar la respuesta. 404 Not Found Recurso no encontrado. Se utiliza cuando el servidor web no encuentra la página o recurso solicitado.

        5xx Errores del servidor :[ 500 Internal Server Error Es un código comúnmente emitido por aplicaciones empotradas en servidores web, mismas que generan contenido dinámicamente, por ejemplo aplicaciones montadas en IIS o Tomcat, cuando se encuentran con situaciones de error ajenas a la naturaleza del servidor web. 501 Not Implemented El servidor no soporta alguna funcionalidad necesaria para responder a la solicitud del navegador (como por ejemplo el método utilizado para la petición). 502 Bad Gateway El servidor está actuando de proxy o gateway y ha recibido una respuesta inválida del otro servidor, por lo que no puede responder adecuadamente a la petición del navegador.

        Juan Fernando Montufar Juárez

        Juan Fernando Montufar Juárez

        student•
        hace 5 años

        Pues lo hice de esta forma:

        use std::net::{TcpListener, TcpStream}; use std::io::prelude::*; use std::fs; fn main() { let listener = TcpListener::bind("127.0.0.1:7373").unwrap(); for stream in listener.incoming() { let stream = stream.unwrap(); handle_connection(stream); } } fn handle_connection(mut stream: TcpStream) { let mut buffer = [0; 1024]; stream.read(&mut buffer).unwrap(); let get = b"GET / HTTP/1.1\r\n"; let filename; let status; if buffer.starts_with(get){ filename = "index.html"; status = "200 OK"; } else { filename = "404.html"; status = "404 NOT FOUND"; } let contents = fs::read_to_string(filename).unwrap(); let response = format!("HTTP/1.1 {}\r\nContent-Length: {}\r\n\r\n{}", status, contents.len(), contents); stream.write(response.as_bytes()).unwrap(); stream.flush().unwrap(); }
        Juan Sebastian Torchia

        Juan Sebastian Torchia

        student•
        hace 5 años

        El codigo hasta el momento con el 404

        use std::net::{TcpListener, TcpStream}; use std::io::prelude::*; use std::fs; fn main() { let listener = TcpListener::bind("lolcahost:7373").unwrap(); for stream in listener.incoming() { let stream = stream.unwrap(); handle_connection(stream); } } fn handle_connection(mut stream: TcpStream) { let mut buffer = [0; 1024]; stream.read(&mut buffer).unwrap(); let get = b"GET / HTTP/1.1\r\n"; if buffer.starts_with(get) { let contents = fs::read_to_string("index.html").unwrap(); let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", contents); stream.write(response.as_bytes()).unwrap(); //nos ayuda a leer la cadena de bytes que estamos recibiendo. stream.flush().unwrap(); //Esperará e impedirá que el programa continúe hasta que se escriban todos los bytes en la conexión. } else { let contents = fs::read_to_string("404.html").unwrap(); let status_line = "HTTP/1.1 404 NOT FOUND\r\n\r\n"; let response = format!("{}{}", status_line, contents); stream.write(response.as_bytes()).unwrap(); stream.flush().unwrap(); } }
        Osvaldo Frias

        Osvaldo Frias

        student•
        hace 5 años

        Mensaje de error :p

        Screenshot_1.png