Skip to content
Snippets Groups Projects
Commit 3304149f authored by aticu's avatar aticu
Browse files

Implement a basic web server

parent e661b909
No related branches found
No related tags found
No related merge requests found
#include "esp32_network_implementation.h"
#include "wifi_data.h"
#include "esp32.h"
int esp32_network_implementation_init(void *custom_data) {
if(esp32_init() != ESP32_OK) {
printf("Could not initialize ESP32.\r\n");
return 1;
}
if(esp32_connect_to_ap(WIFI_SSID, WIFI_PASSWORD) != ESP32_OK) {
printf("Could not connect to the access point.\r\n");
return 2;
}
if(esp32_open_server(80) != ESP32_OK) {
printf("Could not open server.\r\n");
esp32_disconnect_from_ap();
return 3;
}
return 0;
}
enum network_event esp32_network_implementation_recv_data_or_connection(void *custom_data, int *connection_id, uint32_t *len, char **data) {
// Loop until one of the event types supported by the server occurs.
while(1) {
enum esp32_event event_type = esp32_wait_for_event((uint32_t *)connection_id, len, data);
switch(event_type) {
case ESP32_NEW_CONNECTION:
return NEW_CONNECTION;
case ESP32_CLOSED_CONNECTION:
return CLOSED_CONNECTION;
case ESP32_MSG_RECEIVED:
return DATA_RECEIVED;
case ESP32_UNKNOWN_EVENT:
break;
}
}
}
int esp32_network_implementation_close_connection(void *custom_data, int connection_id) {
if(esp32_close_connection(connection_id) != ESP32_OK) {
return 1;
}
return 0;
}
int esp32_network_implementation_send_data(void *custom_data, int connection_id, uint32_t len, char *data) {
if(esp32_send(connection_id, len, data) != ESP32_OK) {
return 1;
}
return 0;
}
struct network_implementation ESP32_NETWORK_IMPLEMENTATION = {
.init = esp32_network_implementation_init,
.recv_data_or_connection = esp32_network_implementation_recv_data_or_connection,
.close_connection = esp32_network_implementation_close_connection,
.send_data = esp32_network_implementation_send_data,
.custom_data = NULL,
};
#ifndef ESP32_NETWORK_IMPLEMENTATION_H_
#define ESP32_NETWORK_IMPLEMENTATION_H_
#include "webserver.h"
struct network_implementation ESP32_NETWORK_IMPLEMENTATION;
#endif /* ESP32_NETWORK_IMPLEMENTATION_H_ */
#include "webserver.h"
static struct network_implementation NETWORK;
int webserver_init(struct network_implementation network) {
NETWORK = network;
return NETWORK.init(NETWORK.custom_data);
}
enum network_event webserver_recv_data_or_connection(int *connection_id, uint32_t *len, char **data) {
return NETWORK.recv_data_or_connection(NETWORK.custom_data, connection_id, len, data);
}
int webserver_close_connection(int connection_id) {
return NETWORK.close_connection(NETWORK.custom_data, connection_id);
}
int webserver_send_data(int connection_id, uint32_t len, char *data) {
return NETWORK.send_data(NETWORK.custom_data, connection_id, len, data);
}
void webserver_handle_request(request_handler handler, int id, uint32_t len, char *data) {
char *end_ptr = &data[len];
char *current_position = data;
enum request_type type;
if(len > 4 && strncmp(data, "GET ", 4) == 0) {
// We have what seems like a GET request.
type = GET_REQUEST;
current_position = &data[4];
} else if(len > 5 && strncmp(data, "POST ", 5) == 0) {
// We have what seems like a POST request.
type = POST_REQUEST;
current_position = &data[5];
} else {
// We have an unknown or invalid request. Just drop it.
return;
}
char *location = current_position;
while(current_position < end_ptr && *current_position != ' ') {
++current_position;
}
if(current_position == end_ptr) {
// We have an invalid request, drop it.
return;
}
// Zero terminate the location for the request handler.
*current_position = '\0';
++current_position;
// Go to the end of the first line.
while(current_position < end_ptr && strncmp(current_position - 1, "\r\n", 2) != 0) {
++current_position;
}
if(current_position == end_ptr) {
// We have an invalid request, drop it.
return;
}
++current_position;
uint32_t length = 0;
// Parse all headers.
while(current_position < end_ptr && strncmp(current_position - 4, "\r\n\r\n", 4) != 0) {
char *header_start = current_position;
while(current_position < end_ptr && strncmp(current_position - 1, "\r\n", 2) != 0) {
++current_position;
}
if(header_start + 16 < end_ptr && strncmp(header_start, "Content-Length: ", 16) == 0) {
length = atoi(header_start + 16);
}
++current_position;
}
if(strncmp(current_position - 4, "\r\n\r\n", 4) != 0) {
// We have an invalid request, drop it.
return;
}
// Now that we're done parsing, call the handler and send the response.
if(handler != NULL) {
bool free_response;
uint32_t response_len;
char *response = handler(location, type, current_position, length, &response_len, &free_response);
if(response != NULL) {
if(webserver_send_data(id, response_len, response) != 0) {
// Just ignore errors for now since we have no better way of handling them.
printf("These was an error sending the response: %s\r\n", response);
}
if(free_response) {
free(response);
}
}
}
}
void webserver_run(request_handler handler) {
// We assume here for now that each request fully arrives in one packet.
// Because of the limited amount of memory, it would not generally be possible to save intermediate packets
// and for our use case this should suffice
while(1) {
int id;
uint32_t len;
char *data;
switch(webserver_recv_data_or_connection(&id, &len, &data)) {
case NEW_CONNECTION:
// Just accept the connection and wait for data to arrive.
// A denial of service attack is not really anything we need to worry about for this project.
printf("(%d) The web server accepted a new connection.\r\n", id);
break;
case CLOSED_CONNECTION:
// Since we just accept connections without specially handling them, there is nothing to do here.
printf("(%d) Connection closed.\r\n", id);
break;
case DATA_RECEIVED:
webserver_handle_request(handler, id, len, data);
webserver_close_connection(id);
break;
case ERROR:
// Close the connection on errors, as there is likely little we can do to fix them.
webserver_close_connection(id);
printf("(%d) An error occurred while waiting for data to arrive.\r\n", id);
break;
}
free(data);
}
}
#ifndef WEBSERVER_H_
#define WEBSERVER_H_
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct network_implementation {
// Returns non-zero value if the network implementations could not be initialized successfully.
int (*init)(void *custom_data);
// Note that the webserver will call free on the data pointer after calling this function.
enum network_event (*recv_data_or_connection)(void *custom_data, int *connection_id, uint32_t *len, char **data);
// Returns non-zero value if the data could not be sent successfully.
int (*send_data)(void *custom_data, int connection_id, uint32_t len, char *data);
// Returns non-zero value if the connection could not be closed successfully.
int (*close_connection)(void *custom_data, int connection_id);
// Allows implementations to maintain associated data.
void *custom_data;
};
enum network_event {
NEW_CONNECTION,
CLOSED_CONNECTION,
DATA_RECEIVED,
ERROR,
};
enum request_type {
GET_REQUEST,
POST_REQUEST
};
// A function prototype to handle a request.
// When implementing handlers, please note that the data pointer is not guaranteed to be zero-terminated.
typedef char *(*request_handler)(char *location, enum request_type type, char *data, uint32_t data_len, uint32_t *out_len, bool *free_result);
// Returns non-zero value if the server could not be properly initialized.
int webserver_init(struct network_implementation network);
// Runs the webserver with the given request handling function.
void webserver_run(request_handler handler);
#endif /* WEBSERVER_H_ */
...@@ -13,13 +13,28 @@ ...@@ -13,13 +13,28 @@
#include "led.h" #include "led.h"
#include "esp32.h" #include "esp32.h"
#include "wifi_data.h" #include "wifi_data.h"
#include "webserver.h"
#include "esp32_network_implementation.h"
#define DELAY 20000000 #define DELAY 20000000
#define BAUDRATE_115200 115200 #define BAUDRATE_115200 115200
#define SPICLOCK_80KHZ 80000 #define SPICLOCK_80KHZ 80000
int main(void) char *server_request_handler(char *location, enum request_type type, char *data, uint32_t data_len, uint32_t *out_len, bool *free_result) {
{ *free_result = false;
if(type == GET_REQUEST && strncmp(location, "/", 2) == 0) {
char *result = "HTTP/1.1 200 OK\r\n"
"Content-Length: 13\r\n"
"Connection: Close\r\n"
"\r\n"
"Hello, World!";
*out_len = strlen(result);
return result;
}
return NULL;
}
int main(void) {
LED_off(LED_ALL); LED_off(LED_ALL);
LED_on(LED_RED); LED_on(LED_RED);
...@@ -30,80 +45,17 @@ int main(void) ...@@ -30,80 +45,17 @@ int main(void)
spi_init(SPICLOCK_80KHZ); spi_init(SPICLOCK_80KHZ);
if(esp32_init() != ESP32_OK) { if(webserver_init(ESP32_NETWORK_IMPLEMENTATION) != 0) {
printf("Could not initialize ESP32.\r\n");
return 1;
}
printf("Initialized ESP32.\r\n");
printf("Connecting to the access point.\r\n");
if(esp32_connect_to_ap(WIFI_SSID, WIFI_PASSWORD) != ESP32_OK) {
printf("Could not connect to the access point.\r\n");
return 1; return 1;
} }
printf("Connected to the access point.\r\n");
LED_off(LED_RED); LED_off(LED_RED);
LED_on(LED_BLUE); LED_on(LED_BLUE);
printf("Opening a server at port 80.\r\n"); webserver_run(server_request_handler);
if(esp32_open_server(80) != ESP32_OK) {
printf("Could not open server.\r\n");
esp32_disconnect_from_ap();
return 1;
}
printf("Server is now open at port 80.\r\n");
LED_off(LED_BLUE); LED_off(LED_BLUE);
LED_on(LED_GREEN); LED_on(LED_GREEN);
uint32_t running = 1;
while(running) {
uint32_t id = 0;
uint32_t len = 0;
char *data = NULL;
enum esp32_event event_type = esp32_wait_for_event(&id, &len, &data);
switch(event_type) {
case ESP32_NEW_CONNECTION:
printf("connection %d was opened\r\n", id);
if(id == 2) {
esp32_send(id, 5, "nope\n");
esp32_close_connection(id);
}
break;
case ESP32_CLOSED_CONNECTION:
printf("connection %d was closed\r\n", id);
break;
case ESP32_MSG_RECEIVED:
printf("received message of length %d from connection %d:\r\n", len, id);
printf("%s\r\n", data);
if(len >= 5 && strncmp(data, "close", 5) == 0) {
running = 0;
} else {
esp32_send(id, len, data);
}
break;
case ESP32_UNKNOWN_EVENT:
printf("unknown event occurred.\r\n");
break;
}
free(data);
}
if(esp32_close_server()) {
printf("Failed to close the server.\r\n");
return 1;
}
printf("Disconnecting from the access point.\r\n");
if(esp32_disconnect_from_ap()) {
printf("Failed to disconnect from the access point.\r\n");
return 1;
}
printf("Disconnected from the access point.\r\n");
LED_off(LED_GREEN);
return 0; return 0;
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment