Validación de la respuesta en el servidor
Clase 5 de 12 • Curso de Gestión de Servidores con Rust
Contenido del curso
Clase 5 de 12 • Curso de Gestión de Servidores con Rust
Contenido del curso
¿Qué hemos realizado hasta el momento?
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.
Mitchell Sierralta Valdivia
Aitor Zaldua
Osvaldo Frias
Gerardo Alberto Soto Alvarez del Castillo
Osvaldo Frias
Mario Andrés Castro Martínez
Pablo Aquino
Javier Guevara
Roberto Antonio Berrospe Machin
Gerardo Alberto Soto Alvarez del Castillo
Juan Fernando Montufar Juárez
Juan Sebastian Torchia
Osvaldo Frias
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. }
El código completo va quedando así...
Ya muestra el index.html para el localhost:7373 y el 404.html para cualquier petición al localhost:7373/<loquesea>
Refactor :3
Hola @Osvaldo, una pregunta, ¿Porqué muestras la longitud del contenido? ¿tiene algún propósito, o es por protocolo? Gracias.
Sí, es parte de la respuesta HTTP. En el RFC 2616 sección 14.13 se especifica el uso del atributo Content-Length.
Hola!! El refactor:
El error 404:
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(); } }
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. }
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(); }
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.
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(); }
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(); } }
Mensaje de error :p