https_request.cpp 5.89 KB
#include <errno.h>

#include "esp_crt_bundle.h"
#include "esp_http_client.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "freertos/semphr.h"
#include <sys/param.h>

#include "https_request.h"

static SemaphoreHandle_t mutex = NULL;
static char** output_buffer;
static unsigned int output_len;
static char*** output_headers;
static unsigned int output_header_len;
static int http_code;
static EventGroupHandle_t https_event_group;
esp_http_client_handle_t client;

esp_err_t http_event_handler(esp_http_client_event_t *evt) {
  const char* TAG = "http_event_handler";
  size_t copy_len;
  size_t content_len;
  size_t header_len;
  void* newptr;

  switch (evt->event_id) {
  case HTTP_EVENT_ERROR:
    ESP_LOGE(TAG, "HTTP_EVENT_ERROR, data=%s", (char*)evt->data);
    xEventGroupSetBits(https_event_group, HTTPS_ERROR_BIT);
    break;
  case HTTP_EVENT_ON_CONNECTED:
    ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
    break;
  case HTTP_EVENT_HEADER_SENT:
    ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
    break;
  case HTTP_EVENT_ON_HEADER:
    ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, %s=%s", evt->header_key, evt->header_value);
    header_len = strlen(evt->header_key) + strlen(evt->header_value) + 1;
    newptr = realloc(*output_headers, (output_header_len + 1) * sizeof(char*));
    if(newptr == NULL) {
      ESP_LOGE(TAG, "Failed to realloc memory for output headers");
      xEventGroupSetBits(https_event_group, HTTPS_ERROR_BIT);
      return ESP_FAIL;
    }
    *output_headers = (char**)newptr;
    newptr = malloc(header_len + 1);
    if(newptr == NULL) {
      ESP_LOGE(TAG, "Failed to alloc memory for output header");
      xEventGroupSetBits(https_event_group, HTTPS_ERROR_BIT);
      return ESP_FAIL;
    }
    (*output_headers)[output_header_len] = (char*)newptr;
    snprintf((*output_headers)[output_header_len++], header_len + 1, "%s=%s", evt->header_key, evt->header_value);
    break;
  case HTTP_EVENT_ON_DATA:
    ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
    content_len = esp_http_client_get_content_length(evt->client);
    if (*output_buffer == NULL) {
      *output_buffer = (char*)malloc(content_len + 1);
      output_len = 0;
      if (*output_buffer == NULL) {
        ESP_LOGE(TAG, "Failed to allocate memory for output buffer");
        xEventGroupSetBits(https_event_group, HTTPS_ERROR_BIT);
        return ESP_FAIL;
      }
    }
    copy_len = MIN(evt->data_len, (content_len - output_len));
    if (copy_len) {
      memcpy((*output_buffer) + output_len, evt->data, copy_len);
    }
    output_len += copy_len;
    break;
  case HTTP_EVENT_ON_FINISH:
    ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
    (*output_buffer)[output_len] = '\0';
    http_code = esp_http_client_get_status_code(evt->client);
    newptr = realloc(*output_headers, (output_header_len + 1) * sizeof(char*));
    if(newptr == NULL) {
      ESP_LOGE(TAG, "Failed to realloc memory for output headers");
      xEventGroupSetBits(https_event_group, HTTPS_ERROR_BIT);
      return ESP_FAIL;
    }
    *output_headers = (char**)newptr;
    (*output_headers)[output_header_len++] = NULL;
    xEventGroupSetBits(https_event_group, HTTPS_FINISHED_BIT);
    break;
  case HTTP_EVENT_DISCONNECTED:
    ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
    break;
  case HTTP_EVENT_REDIRECT:
    ESP_LOGD(TAG, "HTTP_EVENT_REDIRECT");
    break;
  }
  return ESP_OK;
}

esp_err_t init_https() {
  https_event_group = xEventGroupCreate();
  mutex = xSemaphoreCreateMutex();

  esp_http_client_config_t config = {
    .url = "https://ima.lol",
    .event_handler = http_event_handler,
    .crt_bundle_attach = esp_crt_bundle_attach,
  };
  client = esp_http_client_init(&config);

  return ESP_OK;
}

void free_https_response(char*** response_headers, char** response) {
  unsigned int i;
  if(*response_headers != NULL) {
    for(i = 0; (*response_headers)[i] != NULL; ++i) {
      if((*response_headers)[i] != NULL) {
        free((*response_headers)[i]);
        (*response_headers)[i] = NULL;
      }
    }
    free(*response_headers);
    *response_headers = NULL;
  }

  if(*response != NULL) {
    free(*response);
    *response = NULL;
  }
}

esp_err_t do_https_request(const char* url, esp_http_client_method_t method, const char** headers, char*** response_headers, char** response, int* code) {
  const char* TAG = "do_https_request";

  ESP_LOGI(TAG, "https request: method=%d url=%s", method, url);

  if(xSemaphoreTake(mutex, 5000 / portTICK_PERIOD_MS ) == pdTRUE) {
    ESP_LOGD(TAG, "obtained https mutex");

    output_buffer = response;
    output_headers = response_headers;
    *output_buffer = NULL;
    *output_headers = NULL;
    output_len = 0;
    output_header_len = 0;

    esp_err_t err = esp_http_client_set_url(client, url);
    if(err != ESP_OK) {
      ESP_LOGE(TAG, "Unable to set HTTP client url");
      return err;
    }

    err = esp_http_client_set_method(client, method);
    if(err != ESP_OK) {
      ESP_LOGE(TAG, "Unable to set HTTP client method");
      return err;
    }

    for(unsigned int i = 0; headers[i] != NULL; ++i) {
      unsigned int j;
      char* header = strdup(headers[i]);
      for(j = 0; header[j] != '='; ++j) {
        // skip
      }
      header[j] = '\0';
      ESP_LOGD(TAG, "setting header: key=%s, val=%s", header, header + j + 1);
      esp_http_client_set_header(client, header, header + j + 1);
      free(header);
    }

    esp_http_client_perform(client);

    EventBits_t bits = xEventGroupWaitBits(
      https_event_group,
      HTTPS_FINISHED_BIT | HTTPS_ERROR_BIT,
      pdFALSE,
      pdFALSE,
      portMAX_DELAY
    );

    if (bits & HTTPS_ERROR_BIT) {
      ESP_LOGE(TAG, "Failed to complete https request: url=%s", url);
      //esp_http_client_cleanup(client);
      xSemaphoreGive(mutex);
      return ESP_FAIL;
    }

    *code = http_code;
    //esp_http_client_cleanup(client);

    xSemaphoreGive(mutex);
  } else {
    ESP_LOGE(TAG, "Failed to acquire https request mutex");
    return ESP_ERR_NOT_FINISHED;
  }
  return ESP_OK;
}