Introducción
En el desarrollo de software, a menudo nos encontramos con la necesidad de manejar las solicitudes de una red de manera más controlada y segura, especialmente cuando se trata de aplicaciones web. Es aquí donde los Proxies Reversos entran en juego.
Hoy, discutiremos cómo puedes construir tu propio Proxy Reverso utilizando el lenguaje de programación Go. Pero antes de sumergirnos en el código, comprendamos primero qué es un Proxy Reverso y su importancia.
¿Qué es un Proxy Reverso y Para Qué Sirve?
Un servidor proxy, en términos generales, es una estación intermedia que se encuentra entre los usuarios y los recursos que desean acceder, ya sean páginas web, servicios o cualquier otro tipo de datos. Un Proxy Reverso, sin embargo, funciona un poco diferente: sirve como intermediario para las solicitudes provenientes de Internet que buscan acceder a un servidor ubicado detrás de él.
¿Cómo funciona exactamente un Proxy Reverso?
Cuando un Cliente A solicita algo al Servidor B, en realidad no lo hace directamente. En lugar de eso, la solicitud va al Proxy Reverso, que luego contacta al Servidor B en nombre del Cliente A. Así, desde el punto de vista del cliente, parece que todo el trabajo lo está haciendo el Proxy Reverso, cuando en realidad este último simplemente está transmitiendo los mensajes de ida y vuelta entre el cliente y el servidor.
Esto tiene varias funciones que van más allá de simplemente agregar un paso extra en una transacción.
Funciones Principales
El Proxy Reverso generalmente realiza varias funciones:
1. Balanceo de carga
Un Proxy Reverso puede distribuir la carga de varias formas a varios servidores. Esto significa que se pueden establecer algoritmos de distribución de solicitudes entrantes a diferentes servidores para distribuir la carga de trabajo, evitando que un solo servidor se sature con todas las solicitudes.
2. Seguridad
Los Proxy Reversos ocultan la información de los servidores reales y su rendimiento, lo que es especialmente útil para proteger servidores individuales o para mantener la confidencialidad de la red interna.
3. Caché de contenido
Los Proxies Reversos pueden almacenar contenido estático en su caché. Cuando un usuario solicita este contenido, el Proxy Reverso puede entregarlo directamente sin tener que reenviar la solicitud al servidor original. Esto mejora la eficiencia y la velocidad de la red.
4. Compresión de contenido
Algunos Proxies Reversos pueden comprimir las respuestas del servidor antes de enviarlas al cliente. Esto reduce la cantidad de datos que se transfieren entre el servidor y el cliente, mejorando la velocidad y eficiencia.
Implementación
¡Ahora que sabemos qué es un Proxy Reverso y para qué se utiliza, vamos a empezar con la diversión! Primero, vamos a construir un servidor web simple utilizando Go: Este servidor sencillo escucha en el puerto 9001 y maneja las solicitudes HTTP entrantes a través de la función handleRequest. Para cada solicitud, registra los detalles de la misma, añade una respuesta en la cabecera personalizada, y responde con el estado HTTP 202 Accepted y un mensaje de confirmación.
Nuestro Proxy Reverso Ahora, vamos a implementar el Proxy Reverso que redirige todas las solicitudes al punto final especificado. Vamos a analizar algunas partes clave de este código:
package main
import (
"flag"
"io"
"log"
"net/http"
"net/http/httputil"
"time"
)
const ServerTimeout = 5 * time.Second
const ServerPort = ":9000"
func main() {
// Get the endpoint as a flag
endpoint := processFlags()
// Start the server
startServer(endpoint)
}
// processFlags captures endpoint from command line flags
func processFlags() *string {
endpoint := flag.String("e", "", "")
flag.Parse()
if *endpoint == "" {
log.Fatal("endpoint is required")
}
return endpoint
}
En el método principal, llamamos a processFlags para capturar argumentos desde la línea de comando. Aquí, buscamos un argumento -e que representa el punto final al que queremos redirigir nuestras solicitudes. Cada vez que comencemos nuestro Proxy Reverso, necesitaremos proporcionar este punto final. El método startServer inicia el servidor, y pasamos la función forwardRequest como manejador de solicitudes. Por lo tanto, cada vez que se recibe una nueva solicitud, forwardRequest es quién la procesará. Ahora, veamos cómo forwardRequest gestiona la solicitud:
// forwardRequest forwards requests to the specified endpoint
func forwardRequest(endpoint *string, rw http.ResponseWriter, r *http.Request) {
// Create a new request
req, err := http.NewRequest(r.Method, *endpoint, r.Body)
if err != nil {
processErr(rw, err)
return
}
prepareRequest(r, req)
// Actually forward the request to our endpoint
resp := doRequest(req, rw)
if resp == nil {
return
}
defer resp.Body.Close()
processResponse(resp, rw)
}
El método forwardRequest crea una nueva solicitud HTTP con la URL de nuestro punto final, el método de la solicitud original y el cuerpo de la solicitud original. Si hay algún error al crear la nueva solicitud, se llama a processErr (un método simple que maneja cualquier error que pueda ocurrir durante este proceso) y la ejecución se detiene. Posteriormente, se llama al método doRequest, para hacer efectivamente la redirección a nuestro punto final, mediante un cliente HTTP.
Por último podemos visualizar su comportamiento en la siguiente imagen:
Si deseas ver el código completo del ‘reverse proxy’, se encuentra aquí.
Conclusión
Los Proxies Reversos son herramientas esenciales en el mundo moderno de las aplicaciones web, ya sea para balancear carga, garantizar la seguridad, o para cachear contenido. Go nos ofrece una manera simple pero robusta de implementar un Proxy Reverso, permitiéndonos redirigir y manejar las solicitudes de la manera que queramos. Ya sea que estés construyendo una gran aplicación de microservicios o estés simplemente buscando aprender más sobre redes y solicitudes HTTP, crear tu propio Proxy Reverso es una excelente manera de profundizar en el desarrollo de aplicaciones web.