2024-02-29 06:56:22 +00:00
|
|
|
#ifndef HTTP_SERV_H
|
|
|
|
#define HTTP_SERV_H
|
|
|
|
|
|
|
|
#include "lwip/pbuf.h"
|
|
|
|
#include "lwip/tcp.h"
|
2024-09-03 20:44:06 +00:00
|
|
|
#include "http_parser.h"
|
2024-02-29 06:56:22 +00:00
|
|
|
|
2024-04-30 18:05:21 +00:00
|
|
|
#define HTTP_PORT 80
|
2024-02-29 06:56:22 +00:00
|
|
|
#define POLL_TIME_S 5
|
|
|
|
|
|
|
|
extern CURRENT_STATE_T current_state;
|
|
|
|
|
|
|
|
typedef struct TCP_SERVER_T_ {
|
2024-03-05 05:07:18 +00:00
|
|
|
struct tcp_pcb * server_pcb;
|
|
|
|
bool complete;
|
|
|
|
ip_addr_t gw;
|
2024-02-29 06:56:22 +00:00
|
|
|
} TCP_SERVER_T;
|
|
|
|
|
|
|
|
typedef struct TCP_CONNECT_STATE_T_ {
|
2024-03-05 05:07:18 +00:00
|
|
|
struct tcp_pcb * pcb;
|
|
|
|
int sent_len;
|
2024-09-04 20:14:59 +00:00
|
|
|
HTTP_REQUEST_PARSER_T * request_parser;
|
|
|
|
HTTP_RESPONSE_COMPOSER_T * response_composer;
|
2024-03-05 05:07:18 +00:00
|
|
|
ip_addr_t * gw;
|
2024-02-29 06:56:22 +00:00
|
|
|
} TCP_CONNECT_STATE_T;
|
|
|
|
|
2024-02-29 22:21:31 +00:00
|
|
|
TCP_SERVER_T * http_serv_state = NULL;
|
2024-02-29 06:56:22 +00:00
|
|
|
|
|
|
|
static err_t tcp_close_client_connection(TCP_CONNECT_STATE_T * con_state, struct tcp_pcb * client_pcb, err_t close_err) {
|
2024-03-05 05:07:18 +00:00
|
|
|
if (client_pcb) {
|
|
|
|
assert(con_state && con_state->pcb == client_pcb);
|
|
|
|
tcp_arg(client_pcb, NULL);
|
|
|
|
tcp_poll(client_pcb, NULL, 0);
|
|
|
|
tcp_sent(client_pcb, NULL);
|
|
|
|
tcp_recv(client_pcb, NULL);
|
|
|
|
tcp_err(client_pcb, NULL);
|
|
|
|
err_t err = tcp_close(client_pcb);
|
|
|
|
if (err != ERR_OK) {
|
|
|
|
DEBUG_printf("[HTTP] [ERR] Close failed %d, calling abort\n", err);
|
|
|
|
tcp_abort(client_pcb);
|
|
|
|
close_err = ERR_ABRT;
|
|
|
|
}
|
|
|
|
if (con_state) {
|
2024-09-03 20:44:06 +00:00
|
|
|
delete_request_parser(con_state->request_parser);
|
2024-09-04 20:14:59 +00:00
|
|
|
delete_response_composer(con_state->response_composer);
|
2024-03-05 05:07:18 +00:00
|
|
|
free(con_state);
|
|
|
|
}
|
2024-09-03 20:44:06 +00:00
|
|
|
DEBUG_printf("[HTTP] [OK ] Finished and closed connection to client\n");
|
2024-03-05 05:07:18 +00:00
|
|
|
}
|
|
|
|
return close_err;
|
2024-02-29 06:56:22 +00:00
|
|
|
}
|
|
|
|
|
2024-02-29 22:21:31 +00:00
|
|
|
static void tcp_server_close(TCP_SERVER_T * state) {
|
2024-03-05 05:07:18 +00:00
|
|
|
if (state->server_pcb) {
|
|
|
|
tcp_arg(state->server_pcb, NULL);
|
|
|
|
tcp_close(state->server_pcb);
|
|
|
|
state->server_pcb = NULL;
|
|
|
|
}
|
2024-02-29 06:56:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static err_t tcp_server_sent(void * arg, struct tcp_pcb * pcb, u16_t len) {
|
2024-03-05 05:07:18 +00:00
|
|
|
TCP_CONNECT_STATE_T * con_state = (TCP_CONNECT_STATE_T *)arg;
|
|
|
|
DEBUG_printf("[HTTP] [OK ] Sent %u\n", len);
|
|
|
|
con_state->sent_len += len;
|
2024-09-04 20:14:59 +00:00
|
|
|
if (con_state->sent_len >= con_state->response_composer->header_length + con_state->response_composer->content_length) {
|
|
|
|
DEBUG_printf("[HTTP] [OK ] Sent done\n");
|
2024-03-05 05:07:18 +00:00
|
|
|
return tcp_close_client_connection(con_state, pcb, ERR_OK);
|
|
|
|
}
|
|
|
|
return ERR_OK;
|
2024-02-29 06:56:22 +00:00
|
|
|
}
|
|
|
|
|
2024-03-01 22:00:01 +00:00
|
|
|
int send_response (TCP_CONNECT_STATE_T * con_state, struct tcp_pcb * pcb) {
|
2024-03-05 05:07:18 +00:00
|
|
|
// Check result buffer size
|
2024-09-04 20:14:59 +00:00
|
|
|
if (con_state->response_composer->content_length > sizeof(con_state->response_composer->body) - 1) {
|
|
|
|
DEBUG_printf("[HTTP] [ERR] Too much result data %d\n", con_state->response_composer->content_length);
|
2024-03-05 05:07:18 +00:00
|
|
|
return tcp_close_client_connection(con_state, pcb, ERR_CLSD);
|
|
|
|
}
|
|
|
|
// Check header buffer size
|
2024-09-04 20:14:59 +00:00
|
|
|
if (con_state->response_composer->header_length > sizeof(con_state->response_composer->header) - 1) {
|
|
|
|
DEBUG_printf("[HTTP] [ERR] Too much header data %d\n", con_state->response_composer->header_length);
|
2024-03-05 05:07:18 +00:00
|
|
|
return tcp_close_client_connection(con_state, pcb, ERR_CLSD);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send the headers to the client
|
|
|
|
con_state->sent_len = 0;
|
2024-09-04 20:14:59 +00:00
|
|
|
err_t err = tcp_write(pcb, con_state->response_composer->header, con_state->response_composer->header_length, 0);
|
2024-03-05 05:07:18 +00:00
|
|
|
if (err != ERR_OK) {
|
|
|
|
DEBUG_printf("[HTTP] [ERR] Failed to write header data %d\n", err);
|
|
|
|
return tcp_close_client_connection(con_state, pcb, err);
|
|
|
|
}
|
|
|
|
// Send the body to the client
|
2024-09-04 20:14:59 +00:00
|
|
|
if (con_state->response_composer->content_length) {
|
|
|
|
err = tcp_write(pcb, con_state->response_composer->body, con_state->response_composer->content_length, 0);
|
2024-03-05 05:07:18 +00:00
|
|
|
if (err != ERR_OK) {
|
|
|
|
DEBUG_printf("[HTTP] [ERR] Failed to write result data %d\n", err);
|
|
|
|
return tcp_close_client_connection(con_state, pcb, err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
2024-03-01 22:00:01 +00:00
|
|
|
}
|
|
|
|
|
2024-09-04 20:14:59 +00:00
|
|
|
void handle_status_get (const char * request, const char * params, char * body, HTTP_RESPONSE_COMPOSER_T * response) {
|
|
|
|
set_status(response, 200);
|
|
|
|
set_type(response, "application/json");
|
|
|
|
char response_body[128];
|
|
|
|
snprintf(response_body, 128, "{volt: %f, temp: %f, power: %d}", current_state.voltage, current_state.tempC, current_state.power_state);
|
|
|
|
set_body(response, response_body);
|
2024-09-03 20:44:06 +00:00
|
|
|
}
|
|
|
|
|
2024-09-04 20:14:59 +00:00
|
|
|
void handle_power_post (const char * request, const char * params, char * body, HTTP_RESPONSE_COMPOSER_T * response) {
|
|
|
|
set_type(response, "application/json");
|
2024-09-03 20:44:06 +00:00
|
|
|
if (strstr(body, "requested_state")) {
|
|
|
|
int requested_power_state_int;
|
|
|
|
int led_param = sscanf(body, "requested_state=%d", &requested_power_state_int);
|
|
|
|
if (led_param) {
|
|
|
|
if (requested_power_state_int == 0 || requested_power_state_int == 1) {
|
2024-09-04 20:14:59 +00:00
|
|
|
set_status(response, 200);
|
2024-09-03 20:44:06 +00:00
|
|
|
bmc_power_handler((bool) requested_power_state_int);
|
2024-09-04 20:14:59 +00:00
|
|
|
set_body(response, "{}");
|
2024-09-03 20:44:06 +00:00
|
|
|
}
|
|
|
|
else {
|
2024-09-04 20:14:59 +00:00
|
|
|
set_status(response, 400);
|
|
|
|
set_body(response, "{error: true, description: \"invalid requested state, must be 0 or 1\"}");
|
2024-09-03 20:44:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2024-09-04 20:14:59 +00:00
|
|
|
set_status(response, 400);
|
|
|
|
set_body(response, "{error: true, description: \"invalid requested state, must be 0 or 1\"}");
|
2024-09-03 20:44:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2024-09-04 20:14:59 +00:00
|
|
|
set_status(response, 400);
|
|
|
|
set_body(response, "{error: true, description: \"missing required parameter requested_state\"}");
|
2024-09-03 20:44:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-04 20:14:59 +00:00
|
|
|
void handle_404_not_found (const char * request, const char * params, char * body, HTTP_RESPONSE_COMPOSER_T * response) {
|
|
|
|
set_status(response, 404);
|
|
|
|
set_type(response, "application/json");
|
|
|
|
set_body(response, "");
|
|
|
|
}
|
|
|
|
|
2024-02-29 06:56:22 +00:00
|
|
|
err_t tcp_server_recv(void * arg, struct tcp_pcb * pcb, struct pbuf * p, err_t err) {
|
2024-03-05 05:07:18 +00:00
|
|
|
TCP_CONNECT_STATE_T * con_state = (TCP_CONNECT_STATE_T *)arg;
|
|
|
|
if (!p) {
|
|
|
|
DEBUG_printf("[HTTP] [ERR] Client connection closed\n");
|
|
|
|
return tcp_close_client_connection(con_state, pcb, ERR_OK);
|
|
|
|
}
|
|
|
|
assert(con_state && con_state->pcb == pcb);
|
|
|
|
if (p->tot_len > 0) {
|
2024-09-04 20:14:59 +00:00
|
|
|
DEBUG_printf("[HTTP] [OK ] Recieved %d err: %d\n", p->tot_len, err);
|
2024-03-05 05:07:18 +00:00
|
|
|
|
2024-09-03 20:44:06 +00:00
|
|
|
char content[2048] = {0};
|
|
|
|
pbuf_copy_partial(p, &content, 2048 - 1, 0);
|
|
|
|
con_state->request_parser = new_request_parser();
|
|
|
|
parse_http_request(con_state->request_parser, content, strlen(content));
|
|
|
|
|
2024-09-04 20:14:59 +00:00
|
|
|
llhttp_method_t method = get_method(con_state->request_parser);
|
2024-09-03 20:44:06 +00:00
|
|
|
char * url = get_url(con_state->request_parser);
|
|
|
|
char * body = get_body(con_state->request_parser);
|
2024-03-05 05:07:18 +00:00
|
|
|
|
2024-09-04 20:14:59 +00:00
|
|
|
// debug print request
|
|
|
|
const char * method_name = llhttp_method_name(method);
|
|
|
|
DEBUG_printf("[HTTP] [OK ] Request: %s %s %s\n", method_name, url, body);
|
2024-03-05 05:07:18 +00:00
|
|
|
|
2024-09-04 20:14:59 +00:00
|
|
|
con_state->response_composer = new_response_composer();
|
2024-03-05 05:07:18 +00:00
|
|
|
|
|
|
|
// parse request depending on method and request
|
2024-09-04 20:14:59 +00:00
|
|
|
if (method == HTTP_GET && strncmp(url, "/status", sizeof("/status") - 1) == 0) {
|
|
|
|
handle_status_get(url, NULL, body, con_state->response_composer);
|
2024-03-05 05:07:18 +00:00
|
|
|
}
|
2024-09-04 20:14:59 +00:00
|
|
|
else if (method == HTTP_POST && strncmp(url, "/power", sizeof("/power") - 1) == 0) {
|
|
|
|
handle_power_post(url, NULL, body, con_state->response_composer);
|
2024-03-05 05:07:18 +00:00
|
|
|
}
|
2024-09-03 20:44:06 +00:00
|
|
|
else { // if not a registered path, return HTTP 404
|
2024-09-04 20:14:59 +00:00
|
|
|
handle_404_not_found(url, NULL, body, con_state->response_composer);
|
2024-03-05 05:07:18 +00:00
|
|
|
}
|
|
|
|
|
2024-09-04 20:14:59 +00:00
|
|
|
compose_http_response(con_state->response_composer);
|
|
|
|
|
2024-03-05 05:07:18 +00:00
|
|
|
// print result
|
2024-09-04 20:14:59 +00:00
|
|
|
DEBUG_printf("[HTTP] [OK ] Result: %s %s\n", con_state->response_composer->header, con_state->response_composer->body);
|
2024-03-05 05:07:18 +00:00
|
|
|
|
2024-09-03 20:44:06 +00:00
|
|
|
int err;
|
|
|
|
if (err = send_response(con_state, pcb)) {
|
|
|
|
DEBUG_printf("[HTTP] [ERR] Failure in send %d\n", err);
|
2024-03-05 05:07:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
tcp_recved(pcb, p->tot_len);
|
|
|
|
}
|
|
|
|
pbuf_free(p);
|
|
|
|
return ERR_OK;
|
2024-02-29 06:56:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static err_t tcp_server_poll(void * arg, struct tcp_pcb * pcb) {
|
2024-03-05 05:07:18 +00:00
|
|
|
TCP_CONNECT_STATE_T * con_state = (TCP_CONNECT_STATE_T *)arg;
|
|
|
|
return tcp_close_client_connection(con_state, pcb, ERR_OK); // Just disconnect clent?
|
2024-02-29 06:56:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void tcp_server_err(void * arg, err_t err) {
|
2024-03-05 05:07:18 +00:00
|
|
|
TCP_CONNECT_STATE_T * con_state = (TCP_CONNECT_STATE_T *)arg;
|
|
|
|
if (err != ERR_ABRT) {
|
|
|
|
DEBUG_printf("[HTTP] [ERR] Error %d\n", err);
|
|
|
|
tcp_close_client_connection(con_state, con_state->pcb, err);
|
|
|
|
}
|
2024-02-29 06:56:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static err_t tcp_server_accept(void * arg, struct tcp_pcb * client_pcb, err_t err) {
|
2024-03-05 05:07:18 +00:00
|
|
|
TCP_SERVER_T * state = (TCP_SERVER_T *)arg;
|
|
|
|
if (err != ERR_OK || client_pcb == NULL) {
|
|
|
|
DEBUG_printf("[HTTP] [ERR] Failure accepting client connection\n");
|
|
|
|
return ERR_VAL;
|
|
|
|
}
|
|
|
|
DEBUG_printf("[HTTP] [OK ] Client connected\n");
|
|
|
|
|
|
|
|
// Create the state for the connection
|
|
|
|
TCP_CONNECT_STATE_T * con_state = calloc(1, sizeof(TCP_CONNECT_STATE_T));
|
|
|
|
if (!con_state) {
|
|
|
|
DEBUG_printf("[HTTP] [ERR] Failed to allocate connect state\n");
|
|
|
|
return ERR_MEM;
|
|
|
|
}
|
|
|
|
con_state->pcb = client_pcb; // for checking
|
|
|
|
con_state->gw = &state->gw;
|
|
|
|
|
|
|
|
// setup connection to client
|
|
|
|
tcp_arg(client_pcb, con_state);
|
|
|
|
tcp_sent(client_pcb, tcp_server_sent);
|
|
|
|
tcp_recv(client_pcb, tcp_server_recv);
|
|
|
|
tcp_poll(client_pcb, tcp_server_poll, POLL_TIME_S * 2);
|
|
|
|
tcp_err(client_pcb, tcp_server_err);
|
|
|
|
|
|
|
|
return ERR_OK;
|
2024-02-29 06:56:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool tcp_server_open(void * arg) {
|
2024-03-05 05:07:18 +00:00
|
|
|
TCP_SERVER_T * state = (TCP_SERVER_T *)arg;
|
2024-04-30 18:05:21 +00:00
|
|
|
DEBUG_printf("[HTTP] [OK ] Starting server at %s on port %d\n", ip4addr_ntoa(netif_ip4_addr(netif_list)), HTTP_PORT);
|
2024-03-05 05:07:18 +00:00
|
|
|
|
|
|
|
struct tcp_pcb * pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
|
|
|
|
if (!pcb) {
|
|
|
|
DEBUG_printf("[HTTP] [ERR] Failed to create pcb\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-04-30 18:05:21 +00:00
|
|
|
err_t err = tcp_bind(pcb, IP_ANY_TYPE, HTTP_PORT);
|
2024-03-05 05:07:18 +00:00
|
|
|
if (err) {
|
2024-04-30 18:05:21 +00:00
|
|
|
DEBUG_printf("[HTTP] [ERR] Failed to bind to port %d\n", HTTP_PORT);
|
2024-03-05 05:07:18 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
state->server_pcb = tcp_listen_with_backlog(pcb, 1);
|
|
|
|
if (!state->server_pcb) {
|
2024-04-30 18:05:21 +00:00
|
|
|
DEBUG_printf("[HTTP] [ERR] Failed to listen on port %d\n", HTTP_PORT);
|
2024-03-05 05:07:18 +00:00
|
|
|
if (pcb) {
|
|
|
|
tcp_close(pcb);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
tcp_arg(state->server_pcb, state);
|
|
|
|
tcp_accept(state->server_pcb, tcp_server_accept);
|
|
|
|
|
|
|
|
return true;
|
2024-02-29 06:56:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int http_serv_init () {
|
2024-09-03 20:44:06 +00:00
|
|
|
// init http parser
|
|
|
|
if (http_parser_init()) {
|
|
|
|
DEBUG_printf("[HTTP] [ERR] Failed to initialize http parser\n");
|
|
|
|
}
|
|
|
|
|
2024-03-05 05:07:18 +00:00
|
|
|
http_serv_state = calloc(1, sizeof(TCP_SERVER_T));
|
|
|
|
if (!http_serv_state) {
|
|
|
|
DEBUG_printf("[HTTP] [ERR] Failed to allocate state\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2024-02-29 06:56:22 +00:00
|
|
|
|
2024-03-05 05:07:18 +00:00
|
|
|
if (!tcp_server_open(http_serv_state)) {
|
|
|
|
DEBUG_printf("[HTTP] [ERR] Failed to open server\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2024-02-29 06:56:22 +00:00
|
|
|
|
2024-03-05 05:07:18 +00:00
|
|
|
DEBUG_printf("[HTTP] [OK ] Sucessfully initialized http server\n");
|
2024-02-29 06:56:22 +00:00
|
|
|
|
2024-03-05 05:07:18 +00:00
|
|
|
return 0;
|
2024-02-29 06:56:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int http_serv_deinit () {
|
2024-09-03 20:44:06 +00:00
|
|
|
http_parser_deinit();
|
2024-03-05 05:07:18 +00:00
|
|
|
tcp_server_close(http_serv_state);
|
|
|
|
free(http_serv_state);
|
|
|
|
http_serv_state = NULL;
|
|
|
|
return 0;
|
2024-02-29 06:56:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|