Commit f99ffc21 authored by Jacob Priddy's avatar Jacob Priddy 👌

Merge branch '9-use-post-request-for-door-access' into 'master'

Resolve "Use post request for door access"

Closes #9

See merge request !9
parents a253b75a ec01b892
Pipeline #12489 passed with stages
in 1 minute and 11 seconds
FROM espressif/idf:latest
FROM espressif/idf:release-v4.2
RUN apt-get update && apt-get install -y \
build-essential \
......
......@@ -11,10 +11,16 @@ extern "C" {
#define HTTP_UNAUTHORIZED 403
// Requires door_wifi
typedef enum {
DOOR_API_REQUEST_TYPE_GET,
DOOR_API_REQUEST_TYPE_POST,
} door_api_request_type;
typedef struct
{
char* path;
char* data;
door_api_request_type type;
door_http_callback_t handle_data_cb;
door_http_request_finished_cb custom_on_finish;
} door_api_request_s;
......
......@@ -49,6 +49,23 @@ void door_http_destroy_client_handle(door_http_client_handle_t client);
*/
int door_http_get(door_http_client_handle_t client, const char* path, door_http_callback_t* callbacks);
/**
* @brief Performs a post request to the door code server. door_http_free_buffer must be called
* when the callee is done with the data before the next request
* @param[in] path the path of the url to make the request to
* @param[in] data the http post data to send. Should be a JSON string
* @param[in] callbacks http request callbacks to handle getting data etc...
* @return
* - status code on success
* - < 0 on fail
*/
int door_http_post(
door_http_client_handle_t client,
const char* path,
const char* data,
door_http_callback_t* callbacks
);
/**
* @brief Downloads and updates the next OTA partition from the server.
* Will return false if not connected to WiFi
......
......@@ -17,6 +17,8 @@
#define ACCESS_ENDPOINT API_BASE_PATH "/access/"
#define STATUS_ENDPOINT API_BASE_PATH "/status/"
#define ACCESS_ENDPOINT_DOORCODE_DATA_START "{\"doorcode\":\""
#define ACCESS_ENDPOINT_DOORCODE_DATA_END "\"}"
#define MAX_CODE_COUNT 500
static queue_handle_t queue;
......@@ -91,19 +93,36 @@ static void status_on_finish(int32_t status_code)
void door_api_verify_code(const char* code, door_http_request_finished_cb on_finish)
{
char* endpoint = NULL, * data = NULL;
if (!client || !queue)
goto error;
if (strlen(code) > MAX_CODE_COUNT)
goto error;
// sizeof for ACCESS_ENDPOINT includes the null terminator
char* endpoint = malloc(strlen(code) + sizeof(ACCESS_ENDPOINT));
// Copy endpoint string.
endpoint = malloc(sizeof(ACCESS_ENDPOINT));
if (!endpoint)
goto error;
*endpoint = 0x00;
strcat(endpoint, ACCESS_ENDPOINT);
strcat(endpoint, code);
strcpy(endpoint, ACCESS_ENDPOINT);
// sizeof includes the null terminator
// Setup post data buffer
data = malloc(sizeof(ACCESS_ENDPOINT_DOORCODE_DATA_START) + sizeof(ACCESS_ENDPOINT_DOORCODE_DATA_END) +
strlen(code));
if (!data)
goto error;
*data = 0x00;
// Append the json string into the post data buffer
strcat(data, ACCESS_ENDPOINT_DOORCODE_DATA_START);
strcat(data, code);
strcat(data, ACCESS_ENDPOINT_DOORCODE_DATA_END);
door_api_request_s request = {
.path = endpoint,
.type = DOOR_API_REQUEST_TYPE_POST,
.data = data,
.handle_data_cb = {
.on_data = status_gatherer,
.on_finish = status_on_finish,
......@@ -113,6 +132,8 @@ void door_api_verify_code(const char* code, door_http_request_finished_cb on_fin
door_rtos_queue_send(queue, &request, QUEUE_FULL_GRAB_WAIT_PERIOD_MS);
return;
error:
free(endpoint);
free(data);
if (on_finish)
on_finish(DOOR_HTTP_ERR_FAIL);
}
......@@ -122,11 +143,16 @@ void door_api_get_status(void)
if (!client || !queue)
return;
char* endpoint = malloc(strlen(STATUS_ENDPOINT) + 1);
char* endpoint = malloc(sizeof(STATUS_ENDPOINT));
// No free memory chunk big enough for the status endpoint
if (!endpoint)
return;
strcpy(endpoint, STATUS_ENDPOINT);
door_api_request_s request = {
.path = endpoint,
.type = DOOR_API_REQUEST_TYPE_GET,
.data = NULL,
.handle_data_cb = {
.on_data = status_gatherer,
.on_finish = status_on_finish,
......@@ -169,8 +195,12 @@ void door_api_handle_requests_task(void* parameters)
// New request to process.
status_state = (const status_state_t){ 0 };
status_state.on_finish = request.custom_on_finish;
door_http_get(client, request.path, &request.handle_data_cb);
if (request.type == DOOR_API_REQUEST_TYPE_GET)
door_http_get(client, request.path, &request.handle_data_cb);
else if (request.type == DOOR_API_REQUEST_TYPE_POST)
door_http_post(client, request.path, request.data, &request.handle_data_cb);
free(request.path);
free(request.data);
} while (!done);
door_http_destroy_client_handle(client);
......
......@@ -100,7 +100,7 @@ static void door_http_fill_config(esp_http_client_config_t* config)
door_http_client_handle_t door_http_create_client(const char* token)
{
char* token_string;
const esp_partition_t *running;
const esp_partition_t* running;
esp_app_desc_t running_app_info;
door_http_client_handle_t client;
......@@ -117,7 +117,8 @@ door_http_client_handle_t door_http_create_client(const char* token)
// sizeof includes a null index so I don't need to + 1
token_string = malloc(strlen(token) + sizeof(HTTP_TOKEN_BEARER_STRING));
if (!token_string) {
if (!token_string)
{
ESP_LOGE(TAG, "Could not allocate memory for token_string");
return NULL;
}
......@@ -131,10 +132,14 @@ door_http_client_handle_t door_http_create_client(const char* token)
// set version header
running = esp_ota_get_running_partition();
if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) {
if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK)
{
esp_http_client_set_header(client, HTTP_HEADER_VERSION_KEY, running_app_info.version);
}
// Set header for the requested content type
esp_http_client_set_header(client, "Content-Type", "application/json");
return client;
}
......@@ -160,29 +165,66 @@ int door_http_get(door_http_client_handle_t client, const char* path, door_http_
current_request_callbacks = callbacks;
esp_http_client_set_url(handle, path);
esp_http_client_set_method(handle, HTTP_METHOD_GET);
if (esp_http_client_perform(handle) != ESP_OK)
esp_err_t err = esp_http_client_perform(handle);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
return DOOR_HTTP_ERR_FAIL;
}
int status_code = esp_http_client_get_status_code(handle);
ESP_LOGI(TAG, "Status code from get call: %d", status_code);
ESP_LOGI(TAG, "Status code from GET call: %d", status_code);
return status_code;
}
static esp_err_t door_http_ota_update_validate_image_header(esp_app_desc_t *new_app_info)
int door_http_post(
door_http_client_handle_t client,
const char* path,
const char* data,
door_http_callback_t* callbacks
)
{
if (new_app_info == NULL) {
esp_http_client_handle_t handle = (esp_http_client_handle_t)client;
ESP_LOGI(TAG, "DOOR_HTTP_SET: sending POST request to: %s", path);
if (!door_rtos_event_get(WIFI_CONNECTED_EVENT) || !handle)
{
ESP_LOGI(TAG, "Not connected to wifi or not initialized");
return DOOR_HTTP_ERR_FAIL;
}
current_request_callbacks = callbacks;
esp_http_client_set_url(handle, path);
esp_http_client_set_method(handle, HTTP_METHOD_POST);
esp_http_client_set_post_field(client, data, strlen(data));
esp_err_t err = esp_http_client_perform(handle);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
return DOOR_HTTP_ERR_FAIL;
}
int status_code = esp_http_client_get_status_code(handle);
ESP_LOGI(TAG, "Status code from POST call: %d", status_code);
return status_code;
}
static esp_err_t door_http_ota_update_validate_image_header(esp_app_desc_t* new_app_info)
{
if (new_app_info == NULL)
{
return ESP_ERR_INVALID_ARG;
}
const esp_partition_t *running = esp_ota_get_running_partition();
const esp_partition_t* running = esp_ota_get_running_partition();
esp_app_desc_t running_app_info;
if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) {
if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK)
{
ESP_LOGI(UTAG, "Running firmware version: %s", running_app_info.version);
}
#ifndef UPDATE_SKIP_VERSION_CHECK
if (memcmp(new_app_info->version, running_app_info.version, sizeof(new_app_info->version)) == 0) {
if (memcmp(new_app_info->version, running_app_info.version, sizeof(new_app_info->version)) == 0)
{
ESP_LOGW(UTAG, "Current running version is the same as a new. We will not continue the update.");
return ESP_FAIL;
}
......@@ -195,7 +237,7 @@ bool door_http_ota_update(const char* api_token)
{
char* token_get;
esp_err_t ota_finish_err;
esp_http_client_config_t config = {0};
esp_http_client_config_t config = { 0 };
ESP_LOGI(UTAG, "Beginning OTA update");
if (!door_rtos_event_get(WIFI_CONNECTED_EVENT))
{
......@@ -225,26 +267,31 @@ bool door_http_ota_update(const char* api_token)
esp_https_ota_handle_t https_ota_handle = NULL;
esp_err_t err = esp_https_ota_begin(&ota_config, &https_ota_handle);
free(token_get);
if (err != ESP_OK) {
if (err != ESP_OK)
{
ESP_LOGE(UTAG, "ESP HTTPS OTA Begin failed");
return false;
}
esp_app_desc_t app_desc;
err = esp_https_ota_get_img_desc(https_ota_handle, &app_desc);
if (err != ESP_OK) {
if (err != ESP_OK)
{
ESP_LOGE(UTAG, "esp_https_ota_read_img_desc failed");
goto ota_end;
}
err = door_http_ota_update_validate_image_header(&app_desc);
if (err != ESP_OK) {
if (err != ESP_OK)
{
ESP_LOGE(UTAG, "image header verification failed");
goto ota_end;
}
while (1) {
while (1)
{
err = esp_https_ota_perform(https_ota_handle);
if (err != ESP_ERR_HTTPS_OTA_IN_PROGRESS) {
if (err != ESP_ERR_HTTPS_OTA_IN_PROGRESS)
{
break;
}
// esp_https_ota_perform returns after every read operation which gives user the ability to
......@@ -253,18 +300,23 @@ bool door_http_ota_update(const char* api_token)
ESP_LOGD(UTAG, "Image bytes read: %d", esp_https_ota_get_image_len_read(https_ota_handle));
}
if (esp_https_ota_is_complete_data_received(https_ota_handle) != true) {
if (esp_https_ota_is_complete_data_received(https_ota_handle) != true)
{
// the OTA image was not completely received and user can customise the response to this situation.
ESP_LOGE(UTAG, "Complete data was not received.");
}
ota_end:
ota_finish_err = esp_https_ota_finish(https_ota_handle);
if ((err == ESP_OK) && (ota_finish_err == ESP_OK)) {
if ((err == ESP_OK) && (ota_finish_err == ESP_OK))
{
ESP_LOGI(UTAG, "ESP_HTTPS_OTA upgrade successful.");
return true;
} else {
if (ota_finish_err == ESP_ERR_OTA_VALIDATE_FAILED) {
}
else
{
if (ota_finish_err == ESP_ERR_OTA_VALIDATE_FAILED)
{
ESP_LOGE(UTAG, "Image validation failed, image is corrupted");
}
ESP_LOGE(UTAG, "ESP_HTTPS_OTA upgrade failed %d", ota_finish_err);
......
......@@ -57,6 +57,18 @@ int door_http_get(door_http_client_handle_t client, const char* path, door_http_
return -1;
}
int door_http_post(
door_http_client_handle_t client,
const char* path,
const char* data,
door_http_callback_t* callbacks
)
{
if (Hooks::http && Hooks::http->post_request_hook)
return Hooks::http->post_request_hook(client, path, data, callbacks);
return -1;
}
bool door_http_ota_update(const char* api_token)
{
if (Hooks::http && Hooks::http->update_request_hook)
......
......@@ -23,13 +23,16 @@ TEST_CASE("api makes request to correct end point with correct parameters", "[ap
Hooks::rtos = std::make_unique<Hooks::RTOS>();
int http_status_code = 200;
std::string requested_endpoint;
std::string post_data;
Hooks::http->get_request_hook = [&](
Hooks::http->post_request_hook = [&](
door_http_client_handle_t handle,
const char* endpoint,
const char* data,
door_http_callback_t* cb)
{
requested_endpoint = endpoint;
post_data = data;
return Hooks::http->get_request_hook_default(handle, endpoint, cb, http_status_code);
};
......@@ -58,7 +61,15 @@ TEST_CASE("api makes request to correct end point with correct parameters", "[ap
http_status_code = -1;
door_api_verify_code("code", cb);
door_api_handle_requests_task(nullptr);
REQUIRE(requested_endpoint == API_BASE_PATH "/access/code");
REQUIRE(requested_endpoint == API_BASE_PATH "/access/");
}
SECTION("testing post data gets assembled correctly")
{
http_status_code = -1;
door_api_verify_code("code", cb);
door_api_handle_requests_task(nullptr);
REQUIRE(post_data == "{\"doorcode\":\"code\"}");
}
SECTION("Testing up to max length codes")
......
......@@ -32,6 +32,12 @@ namespace Hooks
door_http_callback_t*,
int status_code = 200);
std::function<int(door_http_client_handle_t, const char*, door_http_callback_t*)> get_request_hook;
std::function<int(
door_http_client_handle_t,
const char*,
const char*,
door_http_callback_t*
)> post_request_hook;
std::function<bool(const char*)> update_request_hook;
};
......
0.0.0.1
\ No newline at end of file
1.0.0.0
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment