Home

Building a Basic HTTP Server in C with Various Method Support

22 views

Creating a minimal HTTP server in C that supports GET, POST, DELETE, PUT, and PATCH methods involves using sockets and parsing HTTP requests. Below is a simplified example, focusing on the basic setup and handling of different methods.

Please note that a full-featured HTTP server would require a more sophisticated approach including proper request parsing, headers management, robust error handling, multi-threading for handling concurrent requests, and security considerations.

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

#define PORT 8080
#define BUFFER_SIZE 4096

void handle_request(int client_socket);
void send_response(int client_socket, const char* status, const char* body);

int main() {
    int server_socket, client_socket;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_len = sizeof(client_addr);

    // Create socket
    if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Socket creation failed");
        exit(EXIT_FAILURE);
    }

    // Set socket options
    int opt = 1;
    if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
        perror("Setsockopt failed");
        close(server_socket);
        exit(EXIT_FAILURE);
    }

    // Bind socket to port
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(PORT);
    if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("Bind failed");
        close(server_socket);
        exit(EXIT_FAILURE);
    }

    // Listen for connections
    if (listen(server_socket, 10) < 0) {
        perror("Listen failed");
        close(server_socket);
        exit(EXIT_FAILURE);
    }
    
    printf("HTTP server listening on port %d\n", PORT);

    // Main loop to accept and handle requests
    while (1) {
        // Accept client connection
        if ((client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_len)) < 0) {
            perror("Accept failed");
            continue;
        }

        // Handle request in a separate function
        handle_request(client_socket);

        // Close client connection
        close(client_socket);
    }

    // Close server socket
    close(server_socket);
    return 0;
}

void handle_request(int client_socket) {
    char buffer[BUFFER_SIZE];
    read(client_socket, buffer, BUFFER_SIZE);

    // Simple request method parsing
    if (strstr(buffer, "GET /")) {
        send_response(client_socket, "200 OK", "GET method response");
    } else if (strstr(buffer, "POST /")) {
        send_response(client_socket, "200 OK", "POST method response");
    } else if (strstr(buffer, "DELETE /")) {
        send_response(client_socket, "200 OK", "DELETE method response");
    } else if (strstr(buffer, "PUT /")) {
        send_response(client_socket, "200 OK", "PUT method response");
    } else if (strstr(buffer, "PATCH /")) {
        send_response(client_socket, "200 OK", "PATCH method response");
    } else {
        send_response(client_socket, "405 Method Not Allowed", "Method not allowed");
    }
}

void send_response(int client_socket, const char* status, const char* body) {
    char response[BUFFER_SIZE];
    snprintf(response, sizeof(response),
             "HTTP/1.1 %s\r\n"
             "Content-Type: text/plain\r\n"
             "Content-Length: %zu\r\n"
             "\r\n"
             "%s",
             status, strlen(body), body);
    write(client_socket, response, strlen(response));
}

Key Points:

  1. Creating Socket: The server creates a socket to listen for incoming connections.
  2. Binding Socket: Binds the socket to a specified port.
  3. Listening: The server listens for incoming connection requests.
  4. Accepting Connections: Accepts connections from clients.
  5. Handling Requests: A basic parsing to determine the HTTP method and responding accordingly.
  6. Sending Responses: Sends an appropriate HTTP response based on the request.

This example demonstrates a basic structure and should be expanded to handle more complex scenarios appropriately.