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;
}