2018-07-30 16:06:48 +00:00
|
|
|
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
#include <freertos/FreeRTOS.h>
|
|
|
|
#include <freertos/task.h>
|
|
|
|
#include <esp_log.h>
|
|
|
|
#include <esp_err.h>
|
|
|
|
|
2018-10-24 10:09:43 +00:00
|
|
|
#include <esp_http_server.h>
|
2018-07-30 16:06:48 +00:00
|
|
|
|
|
|
|
#include <protocomm.h>
|
|
|
|
#include <protocomm_httpd.h>
|
|
|
|
|
|
|
|
#include "protocomm_priv.h"
|
|
|
|
|
|
|
|
static const char *TAG = "protocomm_httpd";
|
|
|
|
static protocomm_t *pc_httpd; /* The global protocomm instance for HTTPD */
|
2019-02-08 09:51:33 +00:00
|
|
|
static bool pc_ext_httpd_handle_provided = false;
|
2018-07-30 16:06:48 +00:00
|
|
|
static uint32_t session_id = PROTOCOMM_NO_SESSION_ID;
|
|
|
|
|
|
|
|
#define MAX_REQ_BODY_LEN 4096
|
|
|
|
|
|
|
|
static esp_err_t common_post_handler(httpd_req_t *req)
|
|
|
|
{
|
|
|
|
esp_err_t ret;
|
|
|
|
uint8_t *outbuf = NULL;
|
|
|
|
char *req_body = NULL;
|
|
|
|
const char *ep_name = NULL;
|
|
|
|
ssize_t outlen;
|
|
|
|
|
|
|
|
int cur_session_id = httpd_req_to_sockfd(req);
|
|
|
|
|
|
|
|
if (cur_session_id != session_id) {
|
2019-02-08 09:51:33 +00:00
|
|
|
/* Initialize new security session */
|
2018-07-30 16:06:48 +00:00
|
|
|
if (session_id != PROTOCOMM_NO_SESSION_ID) {
|
2019-02-08 09:51:33 +00:00
|
|
|
ESP_LOGD(TAG, "Closing session with ID: %d", session_id);
|
2018-07-30 16:06:48 +00:00
|
|
|
/* Presently HTTP server doesn't support callback on socket closure so
|
|
|
|
* previous session can only be closed when new session is requested */
|
|
|
|
if (pc_httpd->sec && pc_httpd->sec->close_transport_session) {
|
2019-04-12 12:02:38 +00:00
|
|
|
ret = pc_httpd->sec->close_transport_session(pc_httpd->sec_inst, session_id);
|
2018-07-30 16:06:48 +00:00
|
|
|
if (ret != ESP_OK) {
|
2019-05-28 09:11:49 +00:00
|
|
|
ESP_LOGW(TAG, "Error closing session with ID: %d", session_id);
|
2018-07-30 16:06:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
session_id = PROTOCOMM_NO_SESSION_ID;
|
|
|
|
}
|
|
|
|
if (pc_httpd->sec && pc_httpd->sec->new_transport_session) {
|
2019-04-12 12:02:38 +00:00
|
|
|
ret = pc_httpd->sec->new_transport_session(pc_httpd->sec_inst, cur_session_id);
|
2018-07-30 16:06:48 +00:00
|
|
|
if (ret != ESP_OK) {
|
|
|
|
ESP_LOGE(TAG, "Failed to launch new session with ID: %d", cur_session_id);
|
|
|
|
ret = ESP_FAIL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
session_id = cur_session_id;
|
2019-02-08 09:51:33 +00:00
|
|
|
ESP_LOGD(TAG, "New session with ID: %d", cur_session_id);
|
2018-07-30 16:06:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (req->content_len <= 0) {
|
|
|
|
ESP_LOGE(TAG, "Content length not found");
|
|
|
|
ret = ESP_FAIL;
|
|
|
|
goto out;
|
|
|
|
} else if (req->content_len > MAX_REQ_BODY_LEN) {
|
|
|
|
ESP_LOGE(TAG, "Request content length should be less than 4kb");
|
|
|
|
ret = ESP_FAIL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
req_body = (char *) malloc(req->content_len);
|
|
|
|
if (!req_body) {
|
|
|
|
ESP_LOGE(TAG, "Unable to allocate for request length %d", req->content_len);
|
|
|
|
ret = ESP_ERR_NO_MEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t recv_size = 0;
|
|
|
|
while (recv_size < req->content_len) {
|
|
|
|
ret = httpd_req_recv(req, req_body + recv_size, req->content_len - recv_size);
|
|
|
|
if (ret < 0) {
|
|
|
|
ret = ESP_FAIL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
recv_size += ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Extract the endpoint name from URI string of type "/ep_name" */
|
|
|
|
ep_name = req->uri + 1;
|
|
|
|
|
|
|
|
ret = protocomm_req_handle(pc_httpd, ep_name, session_id,
|
|
|
|
(uint8_t *)req_body, recv_size, &outbuf, &outlen);
|
|
|
|
|
|
|
|
if (ret != ESP_OK) {
|
|
|
|
ESP_LOGE(TAG, "Data handler failed");
|
|
|
|
ret = ESP_FAIL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = httpd_resp_send(req, (char *)outbuf, outlen);
|
|
|
|
if (ret != ESP_OK) {
|
|
|
|
ESP_LOGE(TAG, "HTTP send failed");
|
|
|
|
ret = ESP_FAIL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ret = ESP_OK;
|
|
|
|
out:
|
|
|
|
if (req_body) {
|
|
|
|
free(req_body);
|
|
|
|
}
|
|
|
|
if (outbuf) {
|
|
|
|
free(outbuf);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-02-01 12:50:37 +00:00
|
|
|
static esp_err_t protocomm_httpd_add_endpoint(const char *ep_name,
|
2018-07-30 16:06:48 +00:00
|
|
|
protocomm_req_handler_t req_handler,
|
|
|
|
void *priv_data)
|
|
|
|
{
|
|
|
|
if (pc_httpd == NULL) {
|
|
|
|
return ESP_ERR_INVALID_STATE;
|
|
|
|
}
|
|
|
|
|
2019-02-08 09:51:33 +00:00
|
|
|
ESP_LOGD(TAG, "Adding endpoint : %s", ep_name);
|
2018-07-30 16:06:48 +00:00
|
|
|
|
|
|
|
/* Construct URI name by prepending '/' to ep_name */
|
|
|
|
char* ep_uri = calloc(1, strlen(ep_name) + 2);
|
|
|
|
if (!ep_uri) {
|
|
|
|
ESP_LOGE(TAG, "Malloc failed for ep uri");
|
|
|
|
return ESP_ERR_NO_MEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create URI handler structure */
|
|
|
|
sprintf(ep_uri, "/%s", ep_name);
|
|
|
|
httpd_uri_t config_handler = {
|
|
|
|
.uri = ep_uri,
|
|
|
|
.method = HTTP_POST,
|
|
|
|
.handler = common_post_handler,
|
|
|
|
.user_ctx = NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Register URI handler */
|
|
|
|
esp_err_t err;
|
|
|
|
httpd_handle_t *server = (httpd_handle_t *) pc_httpd->priv;
|
|
|
|
if ((err = httpd_register_uri_handler(*server, &config_handler)) != ESP_OK) {
|
|
|
|
ESP_LOGE(TAG, "Uri handler register failed: %s", esp_err_to_name(err));
|
|
|
|
free(ep_uri);
|
|
|
|
return ESP_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(ep_uri);
|
|
|
|
return ESP_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static esp_err_t protocomm_httpd_remove_endpoint(const char *ep_name)
|
|
|
|
{
|
|
|
|
if (pc_httpd == NULL) {
|
|
|
|
return ESP_ERR_INVALID_STATE;
|
|
|
|
}
|
|
|
|
|
2019-02-08 09:51:33 +00:00
|
|
|
ESP_LOGD(TAG, "Removing endpoint : %s", ep_name);
|
2018-07-30 16:06:48 +00:00
|
|
|
|
|
|
|
/* Construct URI name by prepending '/' to ep_name */
|
|
|
|
char* ep_uri = calloc(1, strlen(ep_name) + 2);
|
|
|
|
if (!ep_uri) {
|
|
|
|
ESP_LOGE(TAG, "Malloc failed for ep uri");
|
|
|
|
return ESP_ERR_NO_MEM;
|
|
|
|
}
|
|
|
|
sprintf(ep_uri, "/%s", ep_name);
|
|
|
|
|
|
|
|
/* Unregister URI handler */
|
|
|
|
esp_err_t err;
|
|
|
|
httpd_handle_t *server = (httpd_handle_t *) pc_httpd->priv;
|
|
|
|
if ((err = httpd_unregister_uri_handler(*server, ep_uri, HTTP_POST)) != ESP_OK) {
|
|
|
|
ESP_LOGE(TAG, "Uri handler de-register failed: %s", esp_err_to_name(err));
|
|
|
|
free(ep_uri);
|
|
|
|
return ESP_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(ep_uri);
|
|
|
|
return ESP_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
esp_err_t protocomm_httpd_start(protocomm_t *pc, const protocomm_httpd_config_t *config)
|
|
|
|
{
|
|
|
|
if (!pc || !config) {
|
|
|
|
return ESP_ERR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pc_httpd) {
|
|
|
|
if (pc == pc_httpd) {
|
|
|
|
ESP_LOGE(TAG, "HTTP server already running for this protocomm instance");
|
|
|
|
return ESP_ERR_INVALID_STATE;
|
|
|
|
}
|
|
|
|
ESP_LOGE(TAG, "HTTP server started for another protocomm instance");
|
|
|
|
return ESP_ERR_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
2019-02-01 12:50:37 +00:00
|
|
|
if (config->ext_handle_provided) {
|
|
|
|
if (config->data.handle) {
|
|
|
|
pc->priv = config->data.handle;
|
|
|
|
pc_ext_httpd_handle_provided = true;
|
|
|
|
} else {
|
|
|
|
return ESP_ERR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Private data will point to the HTTP server handle */
|
|
|
|
pc->priv = calloc(1, sizeof(httpd_handle_t));
|
|
|
|
if (!pc->priv) {
|
|
|
|
ESP_LOGE(TAG, "Malloc failed for HTTP server handle");
|
|
|
|
return ESP_ERR_NO_MEM;
|
|
|
|
}
|
2018-07-30 16:06:48 +00:00
|
|
|
|
2019-02-01 12:50:37 +00:00
|
|
|
/* Configure the HTTP server */
|
|
|
|
httpd_config_t server_config = HTTPD_DEFAULT_CONFIG();
|
|
|
|
server_config.server_port = config->data.config.port;
|
|
|
|
server_config.stack_size = config->data.config.stack_size;
|
|
|
|
server_config.task_priority = config->data.config.task_priority;
|
|
|
|
server_config.lru_purge_enable = true;
|
|
|
|
server_config.max_open_sockets = 1;
|
|
|
|
|
|
|
|
esp_err_t err;
|
|
|
|
if ((err = httpd_start((httpd_handle_t *)pc->priv, &server_config)) != ESP_OK) {
|
|
|
|
ESP_LOGE(TAG, "Failed to start http server: %s", esp_err_to_name(err));
|
|
|
|
free(pc->priv);
|
|
|
|
return err;
|
|
|
|
}
|
2018-07-30 16:06:48 +00:00
|
|
|
}
|
|
|
|
pc->add_endpoint = protocomm_httpd_add_endpoint;
|
|
|
|
pc->remove_endpoint = protocomm_httpd_remove_endpoint;
|
|
|
|
pc_httpd = pc;
|
2019-05-28 09:11:49 +00:00
|
|
|
session_id = PROTOCOMM_NO_SESSION_ID;
|
2018-07-30 16:06:48 +00:00
|
|
|
return ESP_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
esp_err_t protocomm_httpd_stop(protocomm_t *pc)
|
|
|
|
{
|
|
|
|
if ((pc != NULL) && (pc == pc_httpd)) {
|
2019-02-01 12:50:37 +00:00
|
|
|
if (!pc_ext_httpd_handle_provided) {
|
|
|
|
httpd_handle_t *server_handle = (httpd_handle_t *) pc_httpd->priv;
|
|
|
|
httpd_stop(*server_handle);
|
|
|
|
free(server_handle);
|
|
|
|
} else {
|
|
|
|
pc_ext_httpd_handle_provided = false;
|
|
|
|
}
|
2018-07-30 16:06:48 +00:00
|
|
|
pc_httpd->priv = NULL;
|
|
|
|
pc_httpd = NULL;
|
2019-05-28 09:11:49 +00:00
|
|
|
session_id = PROTOCOMM_NO_SESSION_ID;
|
2018-07-30 16:06:48 +00:00
|
|
|
return ESP_OK;
|
|
|
|
}
|
|
|
|
return ESP_ERR_INVALID_ARG;
|
|
|
|
}
|