Servidor simple HTTP 1.1 en lenguaje C

Servidor simple HTTP 1.1 en lenguaje C

Esta publicación es simplemente un código de prueba para crear un servidor http rest en el lenguaje C, extremadamente sencillo version 1.1. Ya que este protocolo se basa en modo texto y es fácil de implementar sin usar dependencias extras.

Diferencias entre http 1.1 y http 2

Formato de transmisión:

HTTP/1.1 utiliza un formato basado en texto, lo que facilita la lectura y depuración pero introduce cierta ineficiencia en el procesamiento.

HTTP/2 emplea un formato binario, lo que reduce la sobrecarga de análisis y permite una transmisión de datos más rápida y compacta.

Multiplexación de conexiones:

HTTP/1.1 procesa las solicitudes de forma secuencial o mediante técnicas de pipelining que, en algunos casos, pueden provocar bloqueos (head-of-line blocking).

HTTP/2 permite la multiplexación, enviando múltiples streams de datos de forma simultánea sobre una única conexión, eliminando así los bloqueos y optimizando el uso del canal.

Compresión de cabeceras:

HTTP/1.1 envía las cabeceras de cada solicitud y respuesta sin compresión, lo que puede resultar redundante y aumentar el tamaño de los datos transmitidos.

HTTP/2 incorpora la compresión de cabeceras mediante el algoritmo HPACK, reduciendo el volumen de información transferida, especialmente en comunicaciones repetitivas.

Control de flujo y prioridad:

HTTP/1.1 carece de mecanismos avanzados para gestionar la prioridad de las solicitudes y el control del flujo de datos.

HTTP/2 introduce sistemas para asignar prioridades a los streams y gestionar de manera dinámica el flujo de información, lo que mejora la eficiencia en la entrega de recursos según las necesidades del cliente.

Estas mejoras técnicas en HTTP/2 permiten una comunicación más eficiente y rápida entre cliente y servidor, aunque su complejidad aumenta en comparación con la implementación más sencilla de HTTP/1.1.

Código

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PUERTO 8080
#define TAM_BUFFER 4096

int main(void) {
    int fd_servidor, fd_cliente;
    struct sockaddr_in direccion;
    socklen_t longitud = sizeof(direccion);
    char buffer[TAM_BUFFER] = {0};

    /* Respuesta HTTP fija: status, cabeceras y cuerpo */
    const char *respuesta_http =
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: text/plain\r\n"
        "Content-Length: 13\r\n"
        "\r\n"
        "Hello, world!";

    /* Creación del socket */
    fd_servidor = socket(AF_INET, SOCK_STREAM, 0);
    if (fd_servidor == -1) {
        perror("Error al crear el socket");
        exit(EXIT_FAILURE);
    }

    /* Permitir reutilizar la dirección y puerto */
    int opcion = 1;
    if (setsockopt(fd_servidor, SOL_SOCKET, SO_REUSEADDR, &opcion, sizeof(opcion)) < 0) {
        perror("Error en setsockopt");
        close(fd_servidor);
        exit(EXIT_FAILURE);
    }

    /* Configuración de la dirección del servidor */
    direccion.sin_family = AF_INET;
    direccion.sin_addr.s_addr = INADDR_ANY;
    direccion.sin_port = htons(PUERTO);

    /* Enlazar el socket a la dirección y puerto especificados */
    if (bind(fd_servidor, (struct sockaddr *)&direccion, sizeof(direccion)) < 0) {
        perror("Error al hacer bind");
        close(fd_servidor);
        exit(EXIT_FAILURE);
    }

    /* Poner el socket en modo escucha */
    if (listen(fd_servidor, 3) < 0) {
        perror("Error en listen");
        close(fd_servidor);
        exit(EXIT_FAILURE);
    }

    printf("Servidor HTTP escuchando en el puerto %d\n", PUERTO);

    /* Bucle principal: aceptar y responder conexiones */
    while (1) {
        fd_cliente = accept(fd_servidor, (struct sockaddr *)&direccion, &longitud);
        if (fd_cliente < 0) {
            perror("Error en accept");
            continue;  // Si falla una conexión, intenta con la siguiente
        }

        /* Leer la solicitud HTTP (no se procesa realmente en este ejemplo) */
        int bytes_leidos = read(fd_cliente, buffer, TAM_BUFFER - 1);
        if (bytes_leidos > 0) {
            buffer[bytes_leidos] = '\0';
            printf("Solicitud recibida:\n%s\n", buffer);
        }

        /* Enviar la respuesta HTTP */
        write(fd_cliente, respuesta_http, strlen(respuesta_http));

        /* Cerrar la conexión con el cliente */
        close(fd_cliente);
    }

    /* Cerrar el socket del servidor (nunca se alcanza en este ejemplo) */
    close(fd_servidor);
    return 0;
}

Compilar

gcc httpapi.c -o httpapi.exe

Como mencione el código es extremadamente simple y solo sirve para ejemplificar el funcionamiento del protocolo 1.1