HTTP with Sockets - mriksman/esp-idf-homekit GitHub Wiki

The 1esp_http_server1 library is about 54KB. When trying to implement a really slim server, you can use sockets to listen on port 80, and manage client requests manually.

ESP-IDF supports the following lwIP TCP/IP stack functions:

  • BSD Sockets API
  • Netconn API is enabled but not officially supported for ESP-IDF applications

In the code below, I’ve removed all the error checking for brevity.

static fd_set master_set;
static void ota_server_task(void * param)
{
    int server_socket = 0;
    server_socket = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(OTA_LISTEN_PORT);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr));
    listen(server_socket, 5);

    struct sockaddr_in client_addr;
    size_t client_addrlen = sizeof(client_addr);

    fd_set working_set;
    // initialize the set of active sockets (i.e. the server socket)
    FD_ZERO (&master_set);
    FD_SET (server_socket, &master_set);

    while (1) {
        memcpy(&working_set, &master_set, sizeof(master_set));
        // block until input arrives on one or more active sockets
        select(FD_SETSIZE, &working_set, NULL, NULL, NULL);

        // service all the sockets with input pending
        for (int fd = 0; fd < FD_SETSIZE; ++fd) {
            if (FD_ISSET (fd, &working_set)) {
                if (fd == server_socket) {
                    int client_fd;
           client_fd = accept(server_socket, (struct sockaddr *)&client_addr, &client_addrlen);
                    FD_SET (client_fd, &master_set);                        
                }
                else {
                    if (process_client_request(fd) < 0) {
                        close(fd);
                        FD_CLR (fd, &master_set);
                    }
                }
            }
        } 
    } 
}

Then, in process_client_request(fd) you check the HTTP headers.

esp_err_t process_client_request(int client_fd) {
    char buffer[OTA_BUFF_SIZE] = {0};
    int len;

    esp_err_t err = ESP_OK;

    len = read(client_fd, buffer, OTA_BUFF_SIZE);
    if (len < 0) {
        // Read error
        return -1; 
    }
    else if (len == 0) {
        // Normal connection close from client
        return -1;
    }
    else {
        // Data read
        const char *header_end = "\r\n\r\n";
        char *body_start_p = strstr(buffer, header_end) + strlen(header_end);
        int body_part_len = len - (body_start_p - buffer);

        const char *method_start_p = buffer;
        const char *method_end_p = strstr(method_start_p, " ");
        const char *uri_start_p = method_end_p + 1;
        const char *uri_end_p = strstr(uri_start_p, " ");
        const char *content_length_start = "Content-Length: ";
        char *content_length_start_p = strstr(buffer, content_length_start);
        if (content_length_start_p != NULL) {
            int content_length = -1;
            sscanf(content_length_start_p + strlen(content_length_start), "%d", &content_length);
            ESP_LOGI(TAG, "Detected content length: %d", content_length);   
        }
        if (strncmp(uri_start_p, "/event", strlen("/event")) == 0) {
            len = sprintf(buffer, "HTTP/1.1 200 OK\r\n"
                                  "Connection: Keep-Alive\r\n"
                                  "Content-Type: text/event-stream\r\n"
                                  "Cache-Control: no-cache'\r\n\r\n");
            send(client_fd, buffer, len, 0);
        }
        else if (strncmp(uri_start_p, "/ ", strlen("/ ")) == 0) {
            char content_stream[] = "<!DOCTYPE html>" 
                                    "...";
            len = sprintf(buffer, "HTTP/1.1 200 OK\r\n"
                                  "Content-Length: %d\r\n\r\n", sizeof(content_stream)-1);
            send(client_fd, buffer, len, 0);
            send(client_fd, content_stream, sizeof(content_stream)-1, 0);
        }
        else {
            len = sprintf(buffer, "HTTP/1.1 404 Fuck Off\r\n"
                                  "Content-Length: 0\r\n\r\n");
            send(client_fd, buffer, len, 0);
        }
    }
    return 0;
}